diff -Nru libgit2-0.20.0/appveyor.yml libgit2-0.22.2/appveyor.yml --- libgit2-0.20.0/appveyor.yml 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/appveyor.yml 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,19 @@ +version: '{build}' +branches: + only: + - master +build_script: +- ps: >- + choco install cmake + + choco install python2 + + mkdir build + + cd build + + cmake -D ENABLE_TRACE=ON -D BUILD_CLAR=ON -D STDCALL=ON .. -G"Visual Studio 11" + + cmake --build . --config RelWithDebInfo +test_script: +- ps: ctest -V . diff -Nru libgit2-0.20.0/AUTHORS libgit2-0.22.2/AUTHORS --- libgit2-0.20.0/AUTHORS 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/AUTHORS 2015-03-24 16:10:45.000000000 +0000 @@ -6,6 +6,7 @@ Andreas Ericsson Anton "antong" Gyllenberg Ankur Sethi +Arthur Schreiber Ben Noordhuis Ben Straub Benjamin C Meyer @@ -25,6 +26,7 @@ Holger Weiss Ingmar Vanhassel J. David Ibáñez +Jacques Germishuys Jakob Pfender Jason Penny Jason R. McNeil diff -Nru libgit2-0.20.0/CHANGELOG.md libgit2-0.22.2/CHANGELOG.md --- libgit2-0.20.0/CHANGELOG.md 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/CHANGELOG.md 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,232 @@ +v0.22 + 1 +------ + +v0.22 +------ + +### Changes or improvements + +* `git_signature_new()` now requires a non-empty email address. + +* Use CommonCrypto libraries for SHA-1 calculation on Mac OS X. + +* Disable SSL compression and SSLv2 and SSLv3 ciphers in favor of TLSv1 + in OpenSSL. + +* The fetch behavior of remotes with autotag set to `GIT_REMOTE_DOWNLOAD_TAGS_ALL` + has been changed to match git 1.9.0 and later. In this mode, libgit2 now + fetches all tags in addition to whatever else needs to be fetched. + +* `git_checkout()` now handles case-changing renames correctly on + case-insensitive filesystems; for example renaming "readme" to "README". + +* The search for libssh2 is now done via pkg-config instead of a + custom search of a few directories. + +* Add support for core.protectHFS and core.protectNTFS. Add more + validation for filenames which we write such as references. + +* The local transport now generates textual progress output like + git-upload-pack does ("counting objects"). + +* `git_checkout_index()` can now check out an in-memory index that is not + necessarily the repository's index, so you may check out an index + that was produced by git_merge and friends while retaining the cached + information. + +* Remove the default timeout for receiving / sending data over HTTP using + the WinHTTP transport layer. + +* Add SPNEGO (Kerberos) authentication using GSSAPI on Unix systems. + +* Provide built-in objects for the empty blob (e69de29) and empty + tree (4b825dc) objects. + +* The index' tree cache is now filled upon read-tree and write-tree + and the cache is written to disk. + +* LF -> CRLF filter refuses to handle mixed-EOL files + +* LF -> CRLF filter now runs when * text = auto (with Git for Windows 1.9.4) + +* File unlocks are atomic again via rename. Read-only files on Windows are + made read-write if necessary. + +* Share open packfiles across repositories to share descriptors and mmaps. + +* Use a map for the treebuilder, making insertion O(1) + +* The build system now accepts an option EMBED_SSH_PATH which when set + tells it to include a copy of libssh2 at the given location. This is + enabled for MSVC. + +* Add support for refspecs with the asterisk in the middle of a + pattern. + +* Fetching now performs opportunistic updates. To achieve this, we + introduce a difference between active and passive refspecs, which + make `git_remote_download()` and `git_remote_fetch()` to take a list of + resfpecs to be the active list, similarly to how git fetch accepts a + list on the command-line. + +* The THREADSAFE option to build libgit2 with threading support has + been flipped to be on by default. + +* The remote object has learnt to prune remote-tracking branches. If + the remote is configured to do so, this will happen via + `git_remote_fetch()`. You can also call `git_remote_prune()` after + connecting or fetching to perform the prune. + + +### API additions + +* Introduce `git_buf_text_is_binary()` and `git_buf_text_contains_nul()` for + consumers to perform binary detection on a git_buf. + +* `git_branch_upstream_remote()` has been introduced to provide the + branch..remote configuration value. + +* Introduce `git_describe_commit()` and `git_describe_workdir()` to provide + a description of the current commit (and working tree, respectively) + based on the nearest tag or reference + +* Introduce `git_merge_bases()` and the `git_oidarray` type to expose all + merge bases between two commits. + +* Introduce `git_merge_bases_many()` to expose all merge bases between + multiple commits. + +* Introduce rebase functionality (using the merge algorithm only). + Introduce `git_rebase_init()` to begin a new rebase session, + `git_rebase_open()` to open an in-progress rebase session, + `git_rebase_commit()` to commit the current rebase operation, + `git_rebase_next()` to apply the next rebase operation, + `git_rebase_abort()` to abort an in-progress rebase and `git_rebase_finish()` + to complete a rebase operation. + +* Introduce `git_note_author()` and `git_note_committer()` to get the author + and committer information on a `git_note`, respectively. + +* A factory function for ssh has been added which allows to change the + path of the programs to execute for receive-pack and upload-pack on + the server, `git_transport_ssh_with_paths()`. + +* The ssh transport supports asking the remote host for accepted + credential types as well as multiple challeges using a single + connection. This requires to know which username you want to connect + as, so this introduces the USERNAME credential type which the ssh + transport will use to ask for the username. + +* The `GIT_EPEEL` error code has been introduced when we cannot peel a tag + to the requested object type; if the given object otherwise cannot be + peeled, `GIT_EINVALIDSPEC` is returned. + +* Introduce `GIT_REPOSITORY_INIT_RELATIVE_GITLINK` to use relative paths + when writing gitlinks, as is used by git core for submodules. + +* `git_remote_prune()` has been added. See above for description. + + +* Introduce reference transactions, which allow multiple references to + be locked at the same time and updates be queued. This also allows + us to safely update a reflog with arbitrary contents, as we need to + do for stash. + +### API removals + +* `git_remote_supported_url()` and `git_remote_is_valid_url()` have been + removed as they have become essentially useless with rsync-style ssh paths. + +* `git_clone_into()` and `git_clone_local_into()` have been removed from the + public API in favour of `git_clone callbacks`. + +* The option to ignore certificate errors via `git_remote_cert_check()` + is no longer present. Instead, `git_remote_callbacks` has gained a new + entry which lets the user perform their own certificate checks. + +### Breaking API changes + +* `git_cherry_pick()` is now `git_cherrypick()`. + +* The `git_submodule_update()` function was renamed to + `git_submodule_update_strategy()`. `git_submodule_update()` is now used to + provide functionalty similar to "git submodule update". + +* `git_treebuilder_create()` was renamed to `git_treebuilder_new()` to better + reflect it being a constructor rather than something which writes to + disk. + +* `git_treebuilder_new()` (was `git_treebuilder_create()`) now takes a + repository so that it can query repository configuration. + Subsequently, `git_treebuilder_write()` no longer takes a repository. + +* `git_threads_init()` and `git_threads_shutdown()` have been renamed to + `git_libgit2_init()` and `git_libgit2_shutdown()` to better explain what + their purpose is, as it's grown to be more than just about threads. + +* `git_libgit2_init()` and `git_libgit2_shutdown()` now return the number of + initializations of the library, so consumers may schedule work on the + first initialization. + +* The `git_transport_register()` function no longer takes a priority and takes + a URL scheme name (eg "http") instead of a prefix like "http://" + +* `git_index_name_entrycount()` and `git_index_reuc_entrycount()` now + return size_t instead of unsigned int. + +* The `context_lines` and `interhunk_lines` fields in `git_diff`_options are + now `uint32_t` instead of `uint16_t`. This allows to set them to `UINT_MAX`, + in effect asking for "infinite" context e.g. to iterate over all the + unmodified lines of a diff. + +* `git_status_file()` now takes an exact path. Use `git_status_list_new()` if + pathspec searching is needed. + +* `git_note_create()` has changed the position of the notes reference + name to match `git_note_remove()`. + +* Rename `git_remote_load()` to `git_remote_lookup()` to bring it in line + with the rest of the lookup functions. + +* `git_remote_rename()` now takes the repository and the remote's + current name. Accepting a remote indicates we want to change it, + which we only did partially. It is much clearer if we accept a name + and no loaded objects are changed. + +* `git_remote_delete()` now accepts the repository and the remote's name + instead of a loaded remote. + +* `git_merge_head` is now `git_annotated_commit`, to better reflect its usage + for multiple functions (including rebase) + +* The `git_clone_options` struct no longer provides the `ignore_cert_errors` or + `remote_name` members for remote customization. + + Instead, the `git_clone_options` struct has two new members, `remote_cb` and + `remote_cb_payload`, which allow the caller to completely override the remote + creation process. If needed, the caller can use this callback to give their + remote a name other than the default (origin) or disable cert checking. + + The `remote_callbacks` member has been preserved for convenience, although it + is not used when a remote creation callback is supplied. + +* The `git_clone`_options struct now provides `repository_cb` and + `repository_cb_payload` to allow the user to create a repository with + custom options. + +* The `git_push` struct to perform a push has been replaced with + `git_remote_upload()`. The refspecs and options are passed as a + function argument. `git_push_update_tips()` is now also + `git_remote_update_tips()` and the callbacks are in the same struct as + the rest. + +* The `git_remote_set_transport()` function now sets a transport factory function, + rather than a pre-existing transport instance. + +* The `git_transport` structure definition has moved into the sys/transport.h + file. + +* libgit2 no longer automatically sets the OpenSSL locking + functions. This is not something which we can know to do. A + last-resort convenience function is provided in sys/openssl.h, + `git_openssl_set_locking()` which can be used to set the locking. diff -Nru libgit2-0.20.0/cmake/Modules/AddCFlagIfSupported.cmake libgit2-0.22.2/cmake/Modules/AddCFlagIfSupported.cmake --- libgit2-0.20.0/cmake/Modules/AddCFlagIfSupported.cmake 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/cmake/Modules/AddCFlagIfSupported.cmake 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,16 @@ +# - Append compiler flag to CMAKE_C_FLAGS if compiler supports it +# ADD_C_FLAG_IF_SUPPORTED() +# - the compiler flag to test +# This internally calls the CHECK_C_COMPILER_FLAG macro. + +INCLUDE(CheckCCompilerFlag) + +MACRO(ADD_C_FLAG_IF_SUPPORTED _FLAG) + STRING(TOUPPER ${_FLAG} UPCASE) + STRING(REGEX REPLACE "^-" "" UPCASE_PRETTY ${UPCASE}) + CHECK_C_COMPILER_FLAG(${_FLAG} IS_${UPCASE_PRETTY}_SUPPORTED) + + IF(IS_${UPCASE_PRETTY}_SUPPORTED) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_FLAG}") + ENDIF() +ENDMACRO() diff -Nru libgit2-0.20.0/cmake/Modules/FindGSSAPI.cmake libgit2-0.22.2/cmake/Modules/FindGSSAPI.cmake --- libgit2-0.20.0/cmake/Modules/FindGSSAPI.cmake 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/cmake/Modules/FindGSSAPI.cmake 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,324 @@ +# - Try to find GSSAPI +# Once done this will define +# +# KRB5_CONFIG - Path to krb5-config +# GSSAPI_ROOT_DIR - Set this variable to the root installation of GSSAPI +# +# Read-Only variables: +# GSSAPI_FLAVOR_MIT - set to TURE if MIT Kerberos has been found +# GSSAPI_FLAVOR_HEIMDAL - set to TRUE if Heimdal Keberos has been found +# GSSAPI_FOUND - system has GSSAPI +# GSSAPI_INCLUDE_DIR - the GSSAPI include directory +# GSSAPI_LIBRARIES - Link these to use GSSAPI +# GSSAPI_DEFINITIONS - Compiler switches required for using GSSAPI +# +#============================================================================= +# Copyright (c) 2013 Andreas Schneider +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# + +find_path(GSSAPI_ROOT_DIR + NAMES + include/gssapi.h + include/gssapi/gssapi.h + HINTS + ${_GSSAPI_ROOT_HINTS} + PATHS + ${_GSSAPI_ROOT_PATHS} +) +mark_as_advanced(GSSAPI_ROOT_DIR) + +if (UNIX) + find_program(KRB5_CONFIG + NAMES + krb5-config + PATHS + ${GSSAPI_ROOT_DIR}/bin + /opt/local/bin) + mark_as_advanced(KRB5_CONFIG) + + if (KRB5_CONFIG) + # Check if we have MIT KRB5 + execute_process( + COMMAND + ${KRB5_CONFIG} --vendor + RESULT_VARIABLE + _GSSAPI_VENDOR_RESULT + OUTPUT_VARIABLE + _GSSAPI_VENDOR_STRING) + + if (_GSSAPI_VENDOR_STRING MATCHES ".*Massachusetts.*") + set(GSSAPI_FLAVOR_MIT TRUE) + else() + execute_process( + COMMAND + ${KRB5_CONFIG} --libs gssapi + RESULT_VARIABLE + _GSSAPI_LIBS_RESULT + OUTPUT_VARIABLE + _GSSAPI_LIBS_STRING) + + if (_GSSAPI_LIBS_STRING MATCHES ".*roken.*") + set(GSSAPI_FLAVOR_HEIMDAL TRUE) + endif() + endif() + + # Get the include dir + execute_process( + COMMAND + ${KRB5_CONFIG} --cflags gssapi + RESULT_VARIABLE + _GSSAPI_INCLUDE_RESULT + OUTPUT_VARIABLE + _GSSAPI_INCLUDE_STRING) + string(REGEX REPLACE "(\r?\n)+$" "" _GSSAPI_INCLUDE_STRING "${_GSSAPI_INCLUDE_STRING}") + string(REGEX REPLACE " *-I" "" _GSSAPI_INCLUDEDIR "${_GSSAPI_INCLUDE_STRING}") + endif() + + if (NOT GSSAPI_FLAVOR_MIT AND NOT GSSAPI_FLAVOR_HEIMDAL) + # Check for HEIMDAL + find_package(PkgConfig) + if (PKG_CONFIG_FOUND) + pkg_check_modules(_GSSAPI heimdal-gssapi) + endif (PKG_CONFIG_FOUND) + + if (_GSSAPI_FOUND) + set(GSSAPI_FLAVOR_HEIMDAL TRUE) + else() + find_path(_GSSAPI_ROKEN + NAMES + roken.h + PATHS + ${GSSAPI_ROOT_DIR}/include + ${_GSSAPI_INCLUDEDIR}) + if (_GSSAPI_ROKEN) + set(GSSAPI_FLAVOR_HEIMDAL TRUE) + endif() + endif () + endif() +endif (UNIX) + +find_path(GSSAPI_INCLUDE_DIR + NAMES + gssapi.h + gssapi/gssapi.h + PATHS + ${GSSAPI_ROOT_DIR}/include + ${_GSSAPI_INCLUDEDIR} +) + +if (GSSAPI_FLAVOR_MIT) + find_library(GSSAPI_LIBRARY + NAMES + gssapi_krb5 + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(KRB5_LIBRARY + NAMES + krb5 + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(K5CRYPTO_LIBRARY + NAMES + k5crypto + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(COM_ERR_LIBRARY + NAMES + com_err + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + if (GSSAPI_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${GSSAPI_LIBRARY} + ) + endif (GSSAPI_LIBRARY) + + if (KRB5_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${KRB5_LIBRARY} + ) + endif (KRB5_LIBRARY) + + if (K5CRYPTO_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${K5CRYPTO_LIBRARY} + ) + endif (K5CRYPTO_LIBRARY) + + if (COM_ERR_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${COM_ERR_LIBRARY} + ) + endif (COM_ERR_LIBRARY) +endif (GSSAPI_FLAVOR_MIT) + +if (GSSAPI_FLAVOR_HEIMDAL) + find_library(GSSAPI_LIBRARY + NAMES + gssapi + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(KRB5_LIBRARY + NAMES + krb5 + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(HCRYPTO_LIBRARY + NAMES + hcrypto + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(COM_ERR_LIBRARY + NAMES + com_err + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(HEIMNTLM_LIBRARY + NAMES + heimntlm + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(HX509_LIBRARY + NAMES + hx509 + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(ASN1_LIBRARY + NAMES + asn1 + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(WIND_LIBRARY + NAMES + wind + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + find_library(ROKEN_LIBRARY + NAMES + roken + PATHS + ${GSSAPI_ROOT_DIR}/lib + ${_GSSAPI_LIBDIR} + ) + + if (GSSAPI_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${GSSAPI_LIBRARY} + ) + endif (GSSAPI_LIBRARY) + + if (KRB5_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${KRB5_LIBRARY} + ) + endif (KRB5_LIBRARY) + + if (HCRYPTO_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${HCRYPTO_LIBRARY} + ) + endif (HCRYPTO_LIBRARY) + + if (COM_ERR_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${COM_ERR_LIBRARY} + ) + endif (COM_ERR_LIBRARY) + + if (HEIMNTLM_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${HEIMNTLM_LIBRARY} + ) + endif (HEIMNTLM_LIBRARY) + + if (HX509_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${HX509_LIBRARY} + ) + endif (HX509_LIBRARY) + + if (ASN1_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${ASN1_LIBRARY} + ) + endif (ASN1_LIBRARY) + + if (WIND_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${WIND_LIBRARY} + ) + endif (WIND_LIBRARY) + + if (ROKEN_LIBRARY) + set(GSSAPI_LIBRARIES + ${GSSAPI_LIBRARIES} + ${WIND_LIBRARY} + ) + endif (ROKEN_LIBRARY) +endif (GSSAPI_FLAVOR_HEIMDAL) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GSSAPI DEFAULT_MSG GSSAPI_LIBRARIES GSSAPI_INCLUDE_DIR) + +if (GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES) + set(GSSAPI_FOUND TRUE) +endif (GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES) + +# show the GSSAPI_INCLUDE_DIRS and GSSAPI_LIBRARIES variables only in the advanced view +mark_as_advanced(GSSAPI_INCLUDE_DIRS GSSAPI_LIBRARIES) diff -Nru libgit2-0.20.0/cmake/Modules/FindIconv.cmake libgit2-0.22.2/cmake/Modules/FindIconv.cmake --- libgit2-0.20.0/cmake/Modules/FindIconv.cmake 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/cmake/Modules/FindIconv.cmake 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,43 @@ +# - Try to find Iconv +# Once done this will define +# +# ICONV_FOUND - system has Iconv +# ICONV_INCLUDE_DIR - the Iconv include directory +# ICONV_LIBRARIES - Link these to use Iconv +# + +IF(ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) + # Already in cache, be silent + SET(ICONV_FIND_QUIETLY TRUE) +ENDIF() + +FIND_PATH(ICONV_INCLUDE_DIR iconv.h PATHS /opt/local/include NO_DEFAULT_PATH) +FIND_PATH(ICONV_INCLUDE_DIR iconv.h) + +FIND_LIBRARY(iconv_lib NAMES iconv libiconv libiconv-2 c NO_DEFAULT_PATH PATHS /opt/local/lib) +FIND_LIBRARY(iconv_lib NAMES iconv libiconv libiconv-2 c) + +IF(ICONV_INCLUDE_DIR AND iconv_lib) + SET(ICONV_FOUND TRUE) +ENDIF() + +IF(ICONV_FOUND) + # split iconv into -L and -l linker options, so we can set them for pkg-config + GET_FILENAME_COMPONENT(iconv_path ${iconv_lib} PATH) + GET_FILENAME_COMPONENT(iconv_name ${iconv_lib} NAME_WE) + STRING(REGEX REPLACE "^lib" "" iconv_name ${iconv_name}) + SET(ICONV_LIBRARIES "-L${iconv_path} -l${iconv_name}") + + IF(NOT ICONV_FIND_QUIETLY) + MESSAGE(STATUS "Found Iconv: ${ICONV_LIBRARIES}") + ENDIF(NOT ICONV_FIND_QUIETLY) +ELSE() + IF(Iconv_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find Iconv") + ENDIF(Iconv_FIND_REQUIRED) +ENDIF() + +MARK_AS_ADVANCED( + ICONV_INCLUDE_DIR + ICONV_LIBRARIES +) diff -Nru libgit2-0.20.0/cmake/Modules/FindLIBSSH2.cmake libgit2-0.22.2/cmake/Modules/FindLIBSSH2.cmake --- libgit2-0.20.0/cmake/Modules/FindLIBSSH2.cmake 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/cmake/Modules/FindLIBSSH2.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -if (LIBSSH2_LIBRARIES AND LIBSSH2_INCLUDE_DIRS) - set(LIBSSH2_FOUND TRUE) -else (LIBSSH2_LIBRARIES AND LIBSSH2_INCLUDE_DIRS) - find_path(LIBSSH2_INCLUDE_DIR - NAMES - libssh2.h - PATHS - /usr/include - /usr/local/include - /opt/local/include - /sw/include - ${CMAKE_INCLUDE_PATH} - ${CMAKE_INSTALL_PREFIX}/include - ) - - find_library(LIBSSH2_LIBRARY - NAMES - ssh2 - libssh2 - PATHS - /usr/lib - /usr/local/lib - /opt/local/lib - /sw/lib - ${CMAKE_LIBRARY_PATH} - ${CMAKE_INSTALL_PREFIX}/lib - ) - - if (LIBSSH2_INCLUDE_DIR AND LIBSSH2_LIBRARY) - set(LIBSSH2_FOUND TRUE) - endif (LIBSSH2_INCLUDE_DIR AND LIBSSH2_LIBRARY) - - if (LIBSSH2_FOUND) - set(LIBSSH2_INCLUDE_DIRS - ${LIBSSH2_INCLUDE_DIR} - ) - - set(LIBSSH2_LIBRARIES - ${LIBSSH2_LIBRARIES} - ${LIBSSH2_LIBRARY} - ) - endif (LIBSSH2_FOUND) -endif (LIBSSH2_LIBRARIES AND LIBSSH2_INCLUDE_DIRS) - diff -Nru libgit2-0.20.0/CMakeLists.txt libgit2-0.22.2/CMakeLists.txt --- libgit2-0.20.0/CMakeLists.txt 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/CMakeLists.txt 2015-03-24 16:10:45.000000000 +0000 @@ -15,13 +15,17 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.6) # Add find modules to the path -SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") +SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/") + +INCLUDE(CheckLibraryExists) +INCLUDE(AddCFlagIfSupported) +INCLUDE(FindPkgConfig) # Build options # OPTION( SONAME "Set the (SO)VERSION of the target" ON ) OPTION( BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" ON ) -OPTION( THREADSAFE "Build libgit2 as threadsafe" OFF ) +OPTION( THREADSAFE "Build libgit2 as threadsafe" ON ) OPTION( BUILD_CLAR "Build Tests using the Clar suite" ON ) OPTION( BUILD_EXAMPLES "Build library usage example apps" OFF ) OPTION( TAGS "Generate tags" OFF ) @@ -33,8 +37,10 @@ OPTION( USE_ICONV "Link with and use iconv library" OFF ) OPTION( USE_SSH "Link with libssh to enable SSH support" ON ) +OPTION( USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF ) +OPTION( VALGRIND "Configure build for valgrind" OFF ) -IF(APPLE) +IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") SET( USE_ICONV ON ) ENDIF() @@ -54,6 +60,14 @@ # By default, libgit2 is built with WinHTTP. To use the built-in # HTTP transport, invoke CMake with the "-DWINHTTP=OFF" argument. OPTION( WINHTTP "Use Win32 WinHTTP routines" ON ) + + # If you want to embed a copy of libssh2 into libgit2, pass a + # path to libssh2 + OPTION( EMBED_SSH_PATH "Path to libssh2 to embed (Windows)" OFF ) + + ADD_DEFINITIONS(-D_SCL_SECURE_NO_WARNINGS) + ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE) + ADD_DEFINITIONS(-D_CRT_NONSTDC_NO_DEPRECATE) ENDIF() # This variable will contain the libraries we need to put into @@ -77,17 +91,13 @@ ELSEIF(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") TARGET_LINK_LIBRARIES(${target} socket nsl) SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lsocket -lnsl" PARENT_SCOPE) - ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Linux") + ENDIF() + CHECK_LIBRARY_EXISTS(rt clock_gettime "time.h" NEED_LIBRT) + IF(NEED_LIBRT) TARGET_LINK_LIBRARIES(${target} rt) SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lrt" PARENT_SCOPE) ENDIF() - IF(USE_ICONV) - TARGET_LINK_LIBRARIES(${target} iconv) - ADD_DEFINITIONS(-DGIT_USE_ICONV) - SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -liconv" PARENT_SCOPE) - ENDIF() - IF(THREADSAFE) TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT}) ENDIF() @@ -123,9 +133,19 @@ 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]+$") +STRING(REGEX REPLACE "^.*LIBGIT2_SOVERSION ([0-9]+)$" "\\1" LIBGIT2_SOVERSION "${GIT2_HEADER_SOVERSION}") + # Find required dependencies INCLUDE_DIRECTORIES(src include) +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 AND NOT MINGW) ADD_DEFINITIONS(-DGIT_WINHTTP) INCLUDE_DIRECTORIES(deps/http-parser) @@ -135,13 +155,13 @@ FIND_PACKAGE(OpenSSL) ENDIF () - FIND_PACKAGE(HTTP_Parser QUIET) + 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}) SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lhttp_parser") ELSE() - MESSAGE("http-parser was not found or is too old; using bundled 3rd-party sources.") + MESSAGE(STATUS "http-parser was not found or is too old; using bundled 3rd-party sources.") INCLUDE_DIRECTORIES(deps/http-parser) FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h) ENDIF() @@ -151,9 +171,15 @@ IF (WIN32 AND NOT MINGW AND NOT SHA1_TYPE STREQUAL "builtin") ADD_DEFINITIONS(-DWIN32_SHA1) FILE(GLOB SRC_SHA1 src/hash/hash_win32.c) +ELSEIF (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + ADD_DEFINITIONS(-DGIT_COMMON_CRYPTO) ELSEIF (OPENSSL_FOUND AND NOT SHA1_TYPE STREQUAL "builtin") ADD_DEFINITIONS(-DOPENSSL_SHA1) - SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} openssl") + IF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + SET(LIBGIT2_PC_LIBS "${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() @@ -164,42 +190,57 @@ ENDIF() # Include POSIX regex when it is required -IF(WIN32 OR AMIGA OR ANDROID) +IF(WIN32 OR AMIGA OR ANDROID OR CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)") INCLUDE_DIRECTORIES(deps/regex) SET(SRC_REGEX deps/regex/regex.c) ENDIF() # Optional external dependency: zlib -# It's optional, but FIND_PACKAGE gives a warning that looks more like an -# error. -FIND_PACKAGE(ZLIB QUIET) +FIND_PACKAGE(ZLIB) IF (ZLIB_FOUND) INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS}) LINK_LIBRARIES(${ZLIB_LIBRARIES}) - IF(APPLE) + IF(APPLE OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD") SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lz") ELSE() SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} zlib") ENDIF() - # Fake the message CMake would have shown - MESSAGE("-- Found zlib: ${ZLIB_LIBRARY}") ELSE() - MESSAGE( "zlib was not found; using bundled 3rd-party sources." ) + 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() -IF (USE_SSH AND NOT MINGW) - FIND_PACKAGE(LIBSSH2 QUIET) +# Optional external dependency: libssh2 +IF (USE_SSH) + PKG_CHECK_MODULES(LIBSSH2 libssh2) ENDIF() IF (LIBSSH2_FOUND) ADD_DEFINITIONS(-DGIT_SSH) - INCLUDE_DIRECTORIES(${LIBSSH2_INCLUDE_DIR}) + INCLUDE_DIRECTORIES(${LIBSSH2_INCLUDE_DIRS}) + LINK_DIRECTORIES(${LIBSSH2_LIBRARY_DIRS}) SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} libssh2") SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES}) 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}) + SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} ${ICONV_LIBRARIES}") +ENDIF() # Platform specific compilation flags IF (MSVC) @@ -274,7 +315,11 @@ # Precompiled headers ELSE () - SET(CMAKE_C_FLAGS "-D_GNU_SOURCE -Wall -Wextra -Wno-missing-field-initializers -Wstrict-aliasing=2 -Wstrict-prototypes ${CMAKE_C_FLAGS}") + SET(CMAKE_C_FLAGS "-D_GNU_SOURCE -Wall -Wextra ${CMAKE_C_FLAGS}") + + 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}") + ENDIF() IF (WIN32 AND NOT CYGWIN) SET(CMAKE_C_FLAGS_DEBUG "-D_DEBUG") @@ -288,11 +333,22 @@ ADD_DEFINITIONS(-D__USE_MINGW_ANSI_STDIO=1) ELSEIF (BUILD_SHARED_LIBS) - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -fPIC") + ADD_C_FLAG_IF_SUPPORTED(-fvisibility=hidden) + + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") ENDIF () + + 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) + IF (APPLE) # Apple deprecated OpenSSL - SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations") - ENDIF () + ADD_C_FLAG_IF_SUPPORTED(-Wno-deprecated-declarations) + ENDIF() + IF (PROFILE) SET(CMAKE_C_FLAGS "-pg ${CMAKE_C_FLAGS}") SET(CMAKE_EXE_LINKER_FLAGS "-pg ${CMAKE_EXE_LINKER_FLAGS}") @@ -317,7 +373,7 @@ IF (THREADSAFE) IF (NOT WIN32) - find_package(Threads REQUIRED) + FIND_PACKAGE(Threads REQUIRED) ENDIF() ADD_DEFINITIONS(-DGIT_THREADS) @@ -333,9 +389,11 @@ 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) - FILE(GLOB SRC_OS src/amiga/*.c src/amiga/*.h) + 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) @@ -346,13 +404,15 @@ ELSEIF (CMAKE_SIZEOF_VOID_P EQUAL 4) ADD_DEFINITIONS(-DGIT_ARCH_32) ELSE() - message(FATAL_ERROR "Unsupported architecture") + MESSAGE(FATAL_ERROR "Unsupported architecture") ENDIF() # Compile and link libgit2 -ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC}) +ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SSH} ${SRC_SHA1} ${WIN_RC}) TARGET_LINK_LIBRARIES(git2 ${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) @@ -365,7 +425,7 @@ IF (SONAME) SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING}) - SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_VERSION_MAJOR}) + 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}) @@ -398,6 +458,7 @@ 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) @@ -414,10 +475,12 @@ ${CLAR_PATH}/clar.c PROPERTIES OBJECT_DEPENDS ${CLAR_PATH}/clar.suite) - ADD_EXECUTABLE(libgit2_clar ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1}) + ADD_EXECUTABLE(libgit2_clar ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SSH} ${SRC_SHA1}) TARGET_LINK_LIBRARIES(libgit2_clar ${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) MSVC_SPLIT_SOURCES(libgit2_clar) @@ -433,7 +496,7 @@ IF (TAGS) FIND_PROGRAM(CTAGS ctags) IF (NOT CTAGS) - message(FATAL_ERROR "Could not find ctags command") + MESSAGE(FATAL_ERROR "Could not find ctags command") ENDIF () FILE(GLOB_RECURSE SRC_ALL *.[ch]) diff -Nru libgit2-0.20.0/CONTRIBUTING.md libgit2-0.22.2/CONTRIBUTING.md --- libgit2-0.20.0/CONTRIBUTING.md 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/CONTRIBUTING.md 2015-03-24 16:10:45.000000000 +0000 @@ -5,30 +5,44 @@ ## Licensing -By contributing to libgit2, you agree to release your contribution under the terms of the license. -For code under `examples`, this is governed by the [CC0 Public Domain Dedication](examples/COPYING). -All other code is released under the [GPL v2 with linking exception](COPYING). +By contributing to libgit2, you agree to release your contribution under +the terms of the license. Except for the `examples` directory, all code +is released under the [GPL v2 with linking exception](COPYING). + +The `examples` code is governed by the +[CC0 Public Domain Dedication](examples/COPYING), so that you may copy +from them into your own application. ## Discussion & Chat -We hang out in the #libgit2 channel on irc.freenode.net. +We hang out in the +[`#libgit2`](http://webchat.freenode.net/?channels=#libgit2)) channel on +irc.freenode.net. Also, feel free to open an [Issue](https://github.com/libgit2/libgit2/issues/new) to start a discussion about any concerns you have. We like to use Issues for that so there is an easily accessible permanent record of the conversation. +## Libgit2 Versions + +The `master` branch is the main branch where development happens. +Releases are tagged +(e.g. [v0.21.0](https://github.com/libgit2/libgit2/releases/tag/v0.21.0) ) +and when a critical bug fix needs to be backported, it will be done on a +`-maint` maintenance branch. + ## Reporting Bugs First, know which version of libgit2 your problem is in and include it in your bug report. This can either be a tag (e.g. -[v0.17.0](https://github.com/libgit2/libgit2/tree/v0.17.0) ) or a commit -SHA (e.g. -[01be7863](https://github.com/libgit2/libgit2/commit/01be786319238fd6507a08316d1c265c1a89407f) -). Using [`git describe`](http://git-scm.com/docs/git-describe) is a great -way to tell us what version you're working with. +[v0.17.0](https://github.com/libgit2/libgit2/releases/tag/v0.17.0)) or a +commit SHA +(e.g. [01be7863](https://github.com/libgit2/libgit2/commit/01be7863)). +Using [`git describe`](http://git-scm.com/docs/git-describe) is a +great way to tell us what version you're working with. -If you're not running against the latest `development` branch version, +If you're not running against the latest `master` branch version, please compile and test against that to avoid re-reporting an issue that's already been fixed. @@ -40,25 +54,56 @@ ## Pull Requests -Our work flow is a typical GitHub flow, where contributors fork the -[libgit2 repository](https://github.com/libgit2/libgit2), make their changes -on branch, and submit a +Our work flow is a [typical GitHub +flow](https://guides.github.com/introduction/flow/index.html), where +contributors fork the [libgit2 repository](https://github.com/libgit2/libgit2), +make their changes on branch, and submit a [Pull Request](https://help.github.com/articles/using-pull-requests) -(a.k.a. "PR"). +(a.k.a. "PR"). Pull requests should usually be targeted at the `master` +branch. Life will be a lot easier for you (and us) if you follow this pattern -(i.e. fork, named branch, submit PR). If you use your fork's `development` -branch, things can get messy. +(i.e. fork, named branch, submit PR). If you use your fork's `master` +branch directly, things can get messy. + +Please include a nice description of your changes when you submit your PR; +if we have to read the whole diff to figure out why you're contributing +in the first place, you're less likely to get feedback and have your change +merged in. + +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 +visibility for your fix, allow others to comment early on the changes and +also let others know that you are currently working on something. + +Before wrapping up a PR, you should be sure to: + +* Write tests to cover any functional changes +* Update documentation for any changed public APIs +* Add to the [`CHANGELOG.md`](CHANGELOG.md) file describing any major changes + +## Unit Tests + +We believe that our unit tests allow us to keep the quality of libgit2 +high: any new changes must not cause unit test failures, and new changes +should include unit tests that cover the bug fixes or new features. +For bug fixes, we prefer unit tests that illustrate the failure before +the change, but pass with your changes. + +In addition to new tests, please ensure that your changes do not cause +any other test failures. Running the entire test suite is helpful +before you submit a pull request. When you build libgit2, the test +suite will also be built. You can run all tests by simply running +the resultant `libgit2_clar` binary. If you want to run a specific +unit test, you can name it with the `-s` option. For example: -Please include a nice description of your changes with your PR; if we have -to read the whole diff to figure out why you're contributing in the first -place, you're less likely to get feedback and have your change merged in. - -If you are working on a particular area then feel free to submit a PR that -highlights your work in progress (and flag in the PR title that it's not -ready to merge). This will help in getting visibility for your fix, allow -others to comment early on the changes and also let others know that you -are currently working on something. + libgit2_clar -sstatus::worktree::long_filenames + +Or you can run an entire class of tests. For example, to run all the +worktree status tests: + + libgit2_clar -sstatus::worktree ## Porting Code From Other Open-Source Projects @@ -76,39 +121,26 @@ MIT and BSD (3-clause) licenses are typically no problem. Apache 2.0 license typically doesn't work due to GPL incompatibility. -If you are pulling in code from core Git, another project or code you've pulled from -a forum / Stack Overflow then please flag this in your PR and also make sure you've -given proper credit to the original author in the code snippet. +If your pull request uses code from core Git, another project, or code +from a forum / Stack Overflow, then *please* flag this in your PR and make +sure you've given proper credit to the original author in the code +snippet. ## Style Guide -`libgit2` is written in [ANSI C](http://en.wikipedia.org/wiki/ANSI_C) -(a.k.a. C89) with some specific conventions for function and type naming, -code formatting, and testing. +The public API of `libgit2` is [ANSI C](http://en.wikipedia.org/wiki/ANSI_C) +(a.k.a. C89) compatible. Internally, `libgit2` is written using a portable +subset of C99 - in order to compile with GCC, Clang, MSVC, etc., we keep +local variable declarations at the tops of blocks only and avoid `//` style +comments. Additionally, `libgit2` follows some extra conventions for +function and type naming, code formatting, and testing. We like to keep the source code consistent and easy to read. Maintaining this takes some discipline, but it's been more than worth it. Take a look -at the -[conventions file](https://github.com/libgit2/libgit2/blob/development/CONVENTIONS.md). +at the [conventions +file](https://github.com/libgit2/libgit2/blob/development/CONVENTIONS.md). ## Starter Projects -So, you want to start helping out with `libgit2`? That's fantastic? We -welcome contributions and we promise we'll try to be nice. - -If you want to jump in, you can look at our issues list to see if there -are any unresolved issues to jump in on. Also, here is a list of some -smaller project ideas that could help you become familiar with the code -base and make a nice first step: - -* Convert a `git_*modulename*_foreach()` callback-based iteration API - into a `git_*modulename*_iterator` object with a create/advance style - of API. This helps folks writing language bindings and usually isn't - too complicated. -* Write a new `examples/` program that mirrors a particular core git - command. (See `examples/diff.c` for example.) This lets you (and us) - easily exercise a particular facet of the API and measure compatability - and feature parity with core git. -* Submit a PR to clarify documentation! While we do try to document all of - the APIs, your fresh eyes on the documentation will find areas that are - confusing much more easily. +See our [projects +list](https://github.com/libgit2/libgit2/blob/development/PROJECTS.md). diff -Nru libgit2-0.20.0/CONVENTIONS.md libgit2-0.22.2/CONVENTIONS.md --- libgit2-0.20.0/CONVENTIONS.md 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/CONVENTIONS.md 2015-03-24 16:10:45.000000000 +0000 @@ -6,14 +6,18 @@ ## Compatibility `libgit2` runs on many different platforms with many different compilers. -It is written in [ANSI C](http://en.wikipedia.org/wiki/ANSI_C) (a.k.a. C89) -with some specific standards for function and type naming, code formatting, -and testing. - -We try to avoid more recent extensions to maximize portability. We also, to -the greatest extent possible, try to avoid lots of `#ifdef`s inside the core -code base. This is somewhat unavoidable, but since it can really hamper -maintainability, we keep it to a minimum. + +The public API of `libgit2` is [ANSI C](http://en.wikipedia.org/wiki/ANSI_C) +(a.k.a. C89) compatible. + +Internally, `libgit2` is written using a portable subset of C99 - in order +to maximize compatibility (e.g. with MSVC) we avoid certain C99 +extensions. Specifically, we keep local variable declarations at the tops +of blocks only and we avoid `//` style comments. + +Also, to the greatest extent possible, we try to avoid lots of `#ifdef`s +inside the core code base. This is somewhat unavoidable, but since it can +really hamper maintainability, we keep it to a minimum. ## Match Surrounding Code @@ -209,6 +213,9 @@ GIT_INLINE(result_type) git_modulename_functionname(arg_list); ``` +`GIT_INLINE` (or `inline`) should not be used in public headers in order +to preserve ANSI C compatibility. + ## Tests `libgit2` uses the [clar](https://github.com/vmg/clar) testing framework. diff -Nru libgit2-0.20.0/COPYING libgit2-0.22.2/COPYING --- libgit2-0.20.0/COPYING 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/COPYING 2015-03-24 16:10:45.000000000 +0000 @@ -388,19 +388,7 @@ ---------------------------------------------------------------------- -The priority queue implementation is based on code licensed under the -Apache 2.0 license: - - Copyright 2010 Volkan Yazıcı - Copyright 2006-2010 The Apache Software Foundation - -The full text of the Apache 2.0 license is available at: - - http://www.apache.org/licenses/LICENSE-2.0 - ----------------------------------------------------------------------- - -The Clay framework is licensed under the MIT license: +The Clar framework is licensed under the MIT license: Copyright (C) 2011 by Vicent Marti @@ -930,64 +918,3 @@ That's all there is to it! ---------------------------------------------------------------------- - -Portions of src/win32/posix_w32.c are derrived from link_win32.c in PHP: - --------------------------------------------------------------------- - The PHP License, version 3.01 -Copyright (c) 1999 - 2012 The PHP Group. All rights reserved. --------------------------------------------------------------------- - -Redistribution and use in source and binary forms, with or without -modification, is permitted provided that the following conditions -are met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. - - 3. The name "PHP" must not be used to endorse or promote products - derived from this software without prior written permission. For - written permission, please contact group@php.net. - - 4. Products derived from this software may not be called "PHP", nor - may "PHP" appear in their name, without prior written permission - from group@php.net. You may indicate that your software works in - conjunction with PHP by saying "Foo for PHP" instead of calling - it "PHP Foo" or "phpfoo" - - 5. The PHP Group may publish revised and/or new versions of the - license from time to time. Each version will be given a - distinguishing version number. - Once covered code has been published under a particular version - of the license, you may always continue to use it under the terms - of that version. You may also choose to use such covered code - under the terms of any subsequent version of the license - published by the PHP Group. No one other than the PHP Group has - the right to modify the terms applicable to covered code created - under this License. - - 6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes PHP software, freely available from - ". - -THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND -ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP -DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, -INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, -STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED -OF THE POSSIBILITY OF SUCH DAMAGE. - --------------------------------------------------------------------- - diff -Nru libgit2-0.20.0/debian/changelog libgit2-0.22.2/debian/changelog --- libgit2-0.20.0/debian/changelog 2014-03-24 20:47:35.000000000 +0000 +++ libgit2-0.22.2/debian/changelog 2015-07-19 13:53:06.000000000 +0000 @@ -1,3 +1,68 @@ +libgit2 (0.22.2-2~utopic) utopic; urgency=medium + + * Backport to utopic + + -- Chow Loong Jin Sun, 19 Jul 2015 21:53:06 +0800 + +libgit2 (0.22.2-2) unstable; urgency=medium + + * debian/control: Added dependency on pkg-config (Closes: #786494) + * debian/rules: Manually cleanup clar files (Closes: #786491) + + -- Russell Sim Sat, 23 May 2015 22:42:04 -0700 + +libgit2 (0.22.2-1) unstable; urgency=medium + + * New upstream releaase 0.22.2 (Closes: #780495). + + -- Russell Sim Wed, 06 May 2015 21:54:25 +1000 + +libgit2 (0.21.3-1.1) unstable; urgency=medium + + * Non-maintainer upload. + * Use CURDIR instead of PWD to fix the previous upload (Closes: #761539). + + -- Ivo De Decker Thu, 08 Jan 2015 23:04:40 +0100 + +libgit2 (0.21.3-1) unstable; urgency=medium + + * New upstream release 0.21.3 (CVE-2014-9390) (Closes: #774048). + * debian/rules: Run tests in a local tmpdir to fix FTBFS (Closes: #761539). + + -- Russell Sim Sun, 28 Dec 2014 18:52:18 +1100 + +libgit2 (0.21.2-2) unstable; urgency=medium + + * debian/control: Added missing dependencies. (Closes: #768482) + + -- Russell Sim Sun, 16 Nov 2014 10:33:23 +0100 + +libgit2 (0.21.2-1) unstable; urgency=medium + + * New upstream release 0.21.2. + * debian/control: Added libssh2-1-dev requirement. (Closes: #761042) + * debian/control: Added libhttp-parser-dev requirement. + * debian/copyright: Fixed MIT NGINX license format. + * debian/copyright: Reordered file sections. + * debian/copyright: Fixed incorrect clar licensing. + * Bumped standards version to 3.9.6 (no change necessary). + + -- Russell Sim Tue, 28 Oct 2014 20:34:58 +1100 + +libgit2 (0.21.1-1) unstable; urgency=medium + + * New upstream release 0.21.1. + + -- Russell Sim Wed, 03 Sep 2014 12:41:44 +1000 + +libgit2 (0.21.0-1) UNRELEASED; urgency=medium + + * New upstream release 0.21.0. + * debian/copyright: Added new licenses: ICS, CC0. + * Library name now matches so name version. + + -- Russell Sim Sat, 21 Jun 2014 11:19:23 +1000 + libgit2 (0.20.0-1) experimental; urgency=medium * New upstream release 0.20.0. diff -Nru libgit2-0.20.0/debian/clean libgit2-0.22.2/debian/clean --- libgit2-0.20.0/debian/clean 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/debian/clean 2015-07-19 13:53:06.000000000 +0000 @@ -0,0 +1,2 @@ +tests/.clarcache +tests/clar.suite diff -Nru libgit2-0.20.0/debian/control libgit2-0.22.2/debian/control --- libgit2-0.20.0/debian/control 2014-03-24 20:47:35.000000000 +0000 +++ libgit2-0.22.2/debian/control 2015-07-19 13:53:06.000000000 +0000 @@ -2,9 +2,10 @@ Section: libs Priority: extra Maintainer: Russell Sim -Build-Depends: debhelper (>= 9.20120417), python-minimal (>= 2.4.0), - cmake, libz-dev, libssl-dev, ca-certificates -Standards-Version: 3.9.5 +Build-Depends: debhelper (>= 9.20120417), python-minimal (>= 2.4.0), pkg-config, + cmake, libz-dev, libssl-dev, libssh2-1-dev, libhttp-parser-dev, + ca-certificates +Standards-Version: 3.9.6 Homepage: http://libgit2.github.com/ Vcs-Git: git://anonscm.debian.org/users/arrsim-guest/libgit2.git Vcs-Browser: http://anonscm.debian.org/gitweb/?p=users/arrsim-guest/libgit2.git;a=summary @@ -14,7 +15,8 @@ Architecture: any Section: libdevel Multi-Arch: same -Depends: libgit2-0 (= ${binary:Version}), libz-dev, libssl-dev, +Depends: libgit2-22 (= ${binary:Version}), libz-dev, + libssl-dev, libssh2-1-dev, libhttp-parser-dev, ${shlibs:Depends}, ${misc:Depends} Description: low-level Git library (development files) libgit2 is a portable, pure C implementation of the Git @@ -23,7 +25,7 @@ . This package contains the development files for libgit2. -Package: libgit2-0 +Package: libgit2-22 Architecture: any Multi-Arch: same Pre-Depends: ${misc:Pre-Depends} @@ -37,7 +39,7 @@ Section: debug Architecture: any Multi-Arch: same -Depends: libgit2-0 (= ${binary:Version}), +Depends: libgit2-22 (= ${binary:Version}), ${misc:Depends} Description: libgit2 library and debugging symbols libgit2 is a portable, pure C implementation of the Git diff -Nru libgit2-0.20.0/debian/copyright libgit2-0.22.2/debian/copyright --- libgit2-0.20.0/debian/copyright 2014-03-24 20:47:35.000000000 +0000 +++ libgit2-0.22.2/debian/copyright 2015-07-19 13:53:06.000000000 +0000 @@ -3,8 +3,116 @@ Source: http://libgit2.github.com/ Files: * -Copyright: Copyright (C) 2009-2012 the libgit2 contributors +Copyright: 2009-2012, the libgit2 contributors +License: GPL-2 with linking exception, origin admission + +Files: cmake/Modules/FindGSSAPI.cmake +Copyright: 2013, Andreas Schneider +License: BSD-2-clause + +Files: debian/* +Copyright: 2011-2014, Russell Sim +License: GPL-2+ + +Files: deps/regex/* +Copyright: 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006,2008 Free Software Foundation, Inc. +License: LGPL-2.1+ + +Files: deps/zlib/* +Copyright: 1995-2010, Jean-loup Gailly + 1995-2010, Mark Adler +License: Zlib + +Files: deps/http-parser/* +Copyright: Igor Sysoev, Joyent, Inc. and other Node contributors +License: MIT+NGINX + +Files: examples/* +Copyright: Public Domain +License: CC0 + +Files: include/git2/inttypes.h include/git2/stdint.h +Copyright: 2006, Alexander Chemeris +License: BSD-3-clause-modified + +Files: src/khash.h +Copyright: 2008, 2009, 2011, Attractive Chaos +License: MIT + +Files: src/tsort.c +Copyright: 2010, Christopher Swenson + 2011, Vicent Marti +License: MIT + +Files: src/path.c +Copyright: 2008, The Android Open Source Project +License: BSD-2-clause + +Files: src/util.c src/fnmatch.h +Copyright: 1990, Regents of the University of California. +License: BSD-3-clause + +Files: src/xdiff/* +Copyright: 2003, Davide Libenzi +License: LGPL-2.1+ + +Files: src/xdiff/xmerge.c +Copyright: 2003-2006, Davide Libenzi + 2003-2006, Johannes E. Schindelin +License: LGPL-2.1+ + +Files: src/xdiff/xpatience.c +Copyright: 2003-2009, Davide Libenzi + 2003-2009, Johannes E. Schindelin +License: LGPL-2.1+ + +Files: src/xdiff/xhistogram.c +Copyright: 2010, Google Inc and others from JGit's IP log. +License: EDL-1.0 + +Files: src/win32/posix_w32.c +Copyright: 1999 - 2012, The PHP Group +License: PHP-3.01 + +Files: src/fnmatch.c +Copyright: 1989, 1993, 1994, The Regents of the University of California. +License: BSD-3-clause + +Files: tests/clar/* +Copyright: 2011, Vicent Marti +License: MIT + +Files: tests/clar.h tests/clar.c tests/generate.py +Copyright: Vicent Marti +License: ISC + +Files: tests/refs/normalize.c +Copyright: 2009, Google Inc. +License: BSD-3-clause-jgit + +Files: src/date.c +Copyright: 2005, Linus Torvalds License: GPL-2 with linking exception + . + Note that the only valid version of the GPL as far as this project + is concerned is _this_ particular version of the license (ie v2, not + v2.2 or v3.x or whatever), unless explicitly otherwise stated. + . + LINKING EXCEPTION + . + In addition to the permissions in the GNU General Public License, + the authors give you unlimited permission to link the compiled + version of this library into combinations with other programs, + and to distribute those combinations without any restriction + coming from the use of this file. (The General Public License + restrictions do apply in other respects; for example, they cover + modification of the file, and distribution when not linked into + a combined executable.) + . + On Debian systems, the complete text of the GNU General + Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". + +License: GPL-2 with linking exception, origin admission libgit2 is Copyright (C) 2009-2011 the libgit2 contributors, unless otherwise stated. See the AUTHORS file for details. . @@ -39,58 +147,7 @@ http://www.apache.org/licenses/LICENSE-2.0 -Files: src/khash.h -Copyright: Copyright (c) 2008, 2009, 2011 by Attractive Chaos -License: MIT - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - . - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - . - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - SOFTWARE. - - -Files: src/tsort.c -Copyright: Copyright (c) 2010 Christopher Swenson - Copyright (c) 2011 Vicent Marti -License: MIT - Permission is hereby granted, free of charge, to any person obtaining - a copy of this software and associated documentation files (the - "Software"), to deal in the Software without restriction, including - without limitation the rights to use, copy, modify, merge, publish, - distribute, sublicense, and/or sell copies of the Software, and to - permit persons to whom the Software is furnished to do so, subject to - the following conditions: - . - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - . - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - -Files: src/path.c -Copyright: Copyright (C) 2008 The Android Open Source Project -License: BSD - Copyright (C) 2008 The Android Open Source Project +License: BSD-2-clause All rights reserved. . Redistribution and use in source and binary forms, with or without @@ -117,68 +174,191 @@ SUCH DAMAGE. -Files: src/win32/posix_w32.c -Copyright: Copyright (c) 1999 - 2012 The PHP Group -License: PHP 3.01 +License: BSD-3-clause + All rights reserved. Redistribution and use in source and binary forms, with or without - modification, is permitted provided that the following conditions + modification, are permitted provided that the following conditions are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. [rescinded 22 July 1999] + 4. Neither the name of the University nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. + +License: BSD-3-clause-modified + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: . - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. . - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the - distribution. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + . + 3. The name of the author may be used to endorse or promote products + derived from this software without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +License: BSD-3-clause-jgit + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: . - 3. The name "PHP" must not be used to endorse or promote products - derived from this software without prior written permission. For - written permission, please contact group@php.net. + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. . - 4. Products derived from this software may not be called "PHP", nor - may "PHP" appear in their name, without prior written permission - from group@php.net. You may indicate that your software works in - conjunction with PHP by saying "Foo for PHP" instead of calling - it "PHP Foo" or "phpfoo" + - Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + . + - Neither the name of the Git Development Community nor the + names of its contributors may be used to endorse or promote + products derived from this software without specific prior + written permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +License: CC0 + 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. . - 5. The PHP Group may publish revised and/or new versions of the - license from time to time. Each version will be given a - distinguishing version number. - Once covered code has been published under a particular version - of the license, you may always continue to use it under the terms - of that version. You may also choose to use such covered code - under the terms of any subsequent version of the license - published by the PHP Group. No one other than the PHP Group has - the right to modify the terms applicable to covered code created - under this License. + You should have received a copy of the CC0 Public Domain Dedication along + with this software. If not, see + . + + +License: ISC + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. . - 6. Redistributions of any form whatsoever must retain the following - acknowledgment: - "This product includes PHP software, freely available from - ". + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + + +License: LGPL-2.1+ + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. . - THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND - ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP - DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + . + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA. + + +License: EDL-1.0 + This program and the accompanying materials are made available + under the terms of the Eclipse Distribution License v1.0 which + accompanies this distribution, is reproduced below, and is + available at http://www.eclipse.org/org/documents/edl-v10.php + . + All rights reserved. + . + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + . + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + . + - Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + . + - Neither the name of the Eclipse Foundation, Inc. nor the + names of its contributors may be used to endorse or promote + products derived from this software without specific prior + written permission. + . + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - OF THE POSSIBILITY OF SUCH DAMAGE. + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -Files: tests-clar/clar -Copyright: Copyright (C) 2011 by Vicent Marti -License: MIT - The Clar framework is licensed under the MIT license: +License: GPL-2+ + This package is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + . + This package is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. . - Copyright (C) 2011 by Vicent Marti + You should have received a copy of the GNU General Public License + along with this program. If not, see . + On Debian systems, the complete text of the GNU General + Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". + + +License: MIT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights @@ -198,9 +378,7 @@ THE SOFTWARE. -Files: deps/http-parser/* -Copyright: Igor Sysoev, Joyent, Inc. and other Node contributors -License: MIT +License: MIT+NGINX http_parser.c is based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev and licensed under the MIT license. . @@ -226,8 +404,59 @@ IN THE SOFTWARE. -Files: deps/zlib/* -Copyright: Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler +License: PHP-3.01 + Redistribution and use in source and binary forms, with or without + modification, is permitted provided that the following conditions + are met: + . + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + . + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + . + 3. The name "PHP" must not be used to endorse or promote products + derived from this software without prior written permission. For + written permission, please contact group@php.net. + . + 4. Products derived from this software may not be called "PHP", nor + may "PHP" appear in their name, without prior written permission + from group@php.net. You may indicate that your software works in + conjunction with PHP by saying "Foo for PHP" instead of calling + it "PHP Foo" or "phpfoo" + . + 5. The PHP Group may publish revised and/or new versions of the + license from time to time. Each version will be given a + distinguishing version number. + Once covered code has been published under a particular version + of the license, you may always continue to use it under the terms + of that version. You may also choose to use such covered code + under the terms of any subsequent version of the license + published by the PHP Group. No one other than the PHP Group has + the right to modify the terms applicable to covered code created + under this License. + . + 6. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes PHP software, freely available from + ". + . + THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND + ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP + DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + OF THE POSSIBILITY OF SUCH DAMAGE. + + License: Zlib This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -247,45 +476,3 @@ . Jean-loup Gailly Mark Adler jloup@gzip.org madler@alumni.caltech.edu - - -Files: deps/regex/* -Copyright: Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006,2008 Free Software Foundation, Inc. -License: LGPL-2.1 - Free Software Foundation, Inc. - This file is part of the GNU C Library. - . - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - . - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - . - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301 USA. - - -Files: debian/* -Copyright: 2011-2014 Russell Sim -License: GPL-2+ - This package is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - . - This package is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - . - You should have received a copy of the GNU General Public License - along with this program. If not, see - . - On Debian systems, the complete text of the GNU General - Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". diff -Nru libgit2-0.20.0/debian/libgit2-0.install libgit2-0.22.2/debian/libgit2-0.install --- libgit2-0.20.0/debian/libgit2-0.install 2014-03-21 12:21:36.000000000 +0000 +++ libgit2-0.22.2/debian/libgit2-0.install 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -/usr/lib/*/lib*so.* diff -Nru libgit2-0.20.0/debian/libgit2-0.lintian-overrides libgit2-0.22.2/debian/libgit2-0.lintian-overrides --- libgit2-0.20.0/debian/libgit2-0.lintian-overrides 2014-03-21 12:21:36.000000000 +0000 +++ libgit2-0.22.2/debian/libgit2-0.lintian-overrides 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -libgit2-0: no-upstream-changelog diff -Nru libgit2-0.20.0/debian/libgit2-0.symbols libgit2-0.22.2/debian/libgit2-0.symbols --- libgit2-0.20.0/debian/libgit2-0.symbols 2014-03-24 20:47:35.000000000 +0000 +++ libgit2-0.22.2/debian/libgit2-0.symbols 1970-01-01 00:00:00.000000000 +0000 @@ -1,646 +0,0 @@ -libgit2.so.0 libgit2-0 #MINVER# - git_attr_add_macro@Base 0.17.0 - git_attr_cache_flush@Base 0.17.0 - git_attr_foreach@Base 0.17.0 - git_attr_get@Base 0.17.0 - git_attr_get_many@Base 0.17.0 - git_attr_value@Base 0.18.0 - git_blame_buffer@Base 0.20.0 - git_blame_file@Base 0.20.0 - git_blame_free@Base 0.20.0 - git_blame_get_hunk_byindex@Base 0.20.0 - git_blame_get_hunk_byline@Base 0.20.0 - git_blame_get_hunk_count@Base 0.20.0 - git_blob_create_frombuffer@Base 0.17.0 - git_blob_create_fromchunks@Base 0.18.0 - git_blob_create_fromdisk@Base 0.17.0 - git_blob_create_fromworkdir@Base 0.18.0 - git_blob_filtered_content@Base 0.20.0 - git_blob_free@Base 0.19.0 - git_blob_id@Base 0.19.0 - git_blob_is_binary@Base 0.18.0 - git_blob_lookup@Base 0.19.0 - git_blob_lookup_prefix@Base 0.19.0 - git_blob_owner@Base 0.19.0 - git_blob_rawcontent@Base 0.17.0 - git_blob_rawsize@Base 0.17.0 - git_branch_create@Base 0.17.0 - git_branch_delete@Base 0.17.0 -#MISSING: 0.20.0# git_branch_foreach@Base 0.18.0 - git_branch_is_head@Base 0.18.0 - git_branch_iterator_free@Base 0.20.0 - git_branch_iterator_new@Base 0.20.0 - git_branch_lookup@Base 0.18.0 - git_branch_move@Base 0.17.0 - git_branch_name@Base 0.18.0 - git_branch_next@Base 0.20.0 - git_branch_remote_name@Base 0.18.0 - git_branch_set_upstream@Base 0.18.0 - git_branch_upstream@Base 0.18.0 - git_branch_upstream_name@Base 0.18.0 - git_buf_free@Base 0.20.0 - git_buf_grow@Base 0.20.0 - git_buf_set@Base 0.20.0 - git_checkout_head@Base 0.18.0 - git_checkout_index@Base 0.18.0 - git_checkout_tree@Base 0.18.0 - git_clone@Base 0.18.0 - git_clone_into@Base 0.20.0 - git_commit_author@Base 0.17.0 - git_commit_committer@Base 0.17.0 - git_commit_create@Base 0.17.0 - git_commit_create_from_oids@Base 0.19.0 - git_commit_create_v@Base 0.17.0 - git_commit_free@Base 0.19.0 - git_commit_id@Base 0.17.0 - git_commit_lookup@Base 0.19.0 - git_commit_lookup_prefix@Base 0.19.0 - git_commit_message@Base 0.17.0 - git_commit_message_encoding@Base 0.17.0 - git_commit_message_raw@Base 0.20.0 - git_commit_nth_gen_ancestor@Base 0.18.0 - git_commit_owner@Base 0.19.0 - git_commit_parent@Base 0.17.0 - git_commit_parent_id@Base 0.18.0 - git_commit_parentcount@Base 0.17.0 - git_commit_raw_header@Base 0.20.0 - git_commit_time@Base 0.17.0 - git_commit_time_offset@Base 0.17.0 - git_commit_tree@Base 0.17.0 - git_commit_tree_id@Base 0.18.0 - git_config_add_backend@Base 0.18.0 - git_config_add_file_ondisk@Base 0.17.0 - git_config_backend_foreach_match@Base 0.20.0 - git_config_delete_entry@Base 0.18.0 - git_config_delete_multivar@Base 0.20.0 - git_config_find_global@Base 0.17.0 - git_config_find_system@Base 0.17.0 - git_config_find_xdg@Base 0.18.0 - git_config_foreach@Base 0.17.0 - git_config_foreach_match@Base 0.18.0 - git_config_free@Base 0.17.0 - git_config_get_bool@Base 0.17.0 - git_config_get_entry@Base 0.18.0 - git_config_get_int32@Base 0.17.0 - git_config_get_int64@Base 0.17.0 - git_config_get_mapped@Base 0.17.0 -#MISSING: 0.20.0# git_config_get_multivar@Base 0.17.0 - git_config_get_multivar_foreach@Base 0.20.0 - git_config_get_string@Base 0.17.0 - git_config_iterator_free@Base 0.20.0 - git_config_iterator_glob_new@Base 0.20.0 - git_config_iterator_new@Base 0.20.0 - git_config_lookup_map_value@Base 0.18.0 - git_config_multivar_iterator_new@Base 0.20.0 - git_config_new@Base 0.17.0 - git_config_next@Base 0.20.0 - git_config_open_default@Base 0.18.0 - git_config_open_global@Base 0.19.0 - git_config_open_level@Base 0.18.0 - git_config_open_ondisk@Base 0.17.0 - git_config_parse_bool@Base 0.18.0 - git_config_parse_int32@Base 0.18.0 - git_config_parse_int64@Base 0.18.0 - git_config_refresh@Base 0.18.0 - git_config_set_bool@Base 0.17.0 - git_config_set_int32@Base 0.17.0 - git_config_set_int64@Base 0.17.0 - git_config_set_multivar@Base 0.17.0 - git_config_set_string@Base 0.17.0 - git_cred_default_new@Base 0.20.0 - git_cred_has_username@Base 0.20.0 - git_cred_ssh_custom_new@Base 0.20.0 - git_cred_ssh_key_new@Base 0.20.0 - git_cred_userpass@Base 0.18.0 - git_cred_userpass_plaintext_new@Base 0.18.0 - git_diff_blob_to_buffer@Base 0.18.0 - git_diff_blobs@Base 0.17.0 - git_diff_find_similar@Base 0.18.0 - git_diff_foreach@Base 0.17.0 - git_diff_free@Base 0.20.0 - git_diff_get_delta@Base 0.20.0 -#MISSING: 0.20.0# git_diff_get_patch@Base 0.18.0 - git_diff_index_to_workdir@Base 0.18.0 - git_diff_is_sorted_icase@Base 0.20.0 -#MISSING: 0.20.0# git_diff_list_free@Base 0.17.0 - git_diff_merge@Base 0.17.0 - git_diff_num_deltas@Base 0.18.0 - git_diff_num_deltas_of_type@Base 0.18.0 - git_diff_options_init@Base 0.20.0 -#MISSING: 0.20.0# git_diff_patch_delta@Base 0.18.0 -#MISSING: 0.20.0# git_diff_patch_free@Base 0.18.0 -#MISSING: 0.20.0# git_diff_patch_from_blob_and_buffer@Base 0.19.0 -#MISSING: 0.20.0# git_diff_patch_from_blobs@Base 0.19.0 -#MISSING: 0.20.0# git_diff_patch_get_hunk@Base 0.18.0 -#MISSING: 0.20.0# git_diff_patch_get_line_in_hunk@Base 0.18.0 -#MISSING: 0.20.0# git_diff_patch_line_stats@Base 0.18.0 -#MISSING: 0.20.0# git_diff_patch_num_hunks@Base 0.18.0 -#MISSING: 0.20.0# git_diff_patch_num_lines_in_hunk@Base 0.18.0 -#MISSING: 0.20.0# git_diff_patch_print@Base 0.18.0 -#MISSING: 0.20.0# git_diff_patch_to_str@Base 0.18.0 - git_diff_print@Base 0.20.0 -#MISSING: 0.20.0# git_diff_print_compact@Base 0.17.0 -#MISSING: 0.20.0# git_diff_print_patch@Base 0.17.0 -#MISSING: 0.20.0# git_diff_print_raw@Base 0.19.0 - git_diff_status_char@Base 0.18.0 - git_diff_tree_to_index@Base 0.18.0 - git_diff_tree_to_tree@Base 0.17.0 - git_diff_tree_to_workdir@Base 0.18.0 - git_diff_tree_to_workdir_with_index@Base 0.20.0 - git_filter_list_apply_to_blob@Base 0.20.0 - git_filter_list_apply_to_data@Base 0.20.0 - git_filter_list_apply_to_file@Base 0.20.0 - git_filter_list_free@Base 0.20.0 - git_filter_list_length@Base 0.20.0 - git_filter_list_load@Base 0.20.0 - git_filter_list_new@Base 0.20.0 - git_filter_list_push@Base 0.20.0 - git_filter_lookup@Base 0.20.0 - git_filter_register@Base 0.20.0 - git_filter_source_filemode@Base 0.20.0 - git_filter_source_id@Base 0.20.0 - git_filter_source_mode@Base 0.20.0 - git_filter_source_path@Base 0.20.0 - git_filter_source_repo@Base 0.20.0 - git_filter_unregister@Base 0.20.0 - git_graph_ahead_behind@Base 0.18.0 - git_ignore_add_rule@Base 0.18.0 - git_ignore_clear_internal_rules@Base 0.18.0 - git_ignore_path_is_ignored@Base 0.18.0 - git_index_add@Base 0.17.0 - git_index_add_all@Base 0.19.0 - git_index_add_bypath@Base 0.18.0 - git_index_caps@Base 0.18.0 - git_index_clear@Base 0.17.0 - git_index_conflict_add@Base 0.18.0 - git_index_conflict_cleanup@Base 0.18.0 - git_index_conflict_get@Base 0.18.0 - git_index_conflict_iterator_free@Base 0.19.0 - git_index_conflict_iterator_new@Base 0.19.0 - git_index_conflict_next@Base 0.19.0 - git_index_conflict_remove@Base 0.18.0 - git_index_entry_stage@Base 0.17.0 - git_index_entrycount@Base 0.17.0 - git_index_find@Base 0.17.0 - git_index_free@Base 0.17.0 - git_index_get_byindex@Base 0.18.0 - git_index_get_bypath@Base 0.18.0 - git_index_has_conflicts@Base 0.18.0 - git_index_name_add@Base 0.19.0 - git_index_name_clear@Base 0.19.0 - git_index_name_entrycount@Base 0.19.0 - git_index_name_get_byindex@Base 0.19.0 - git_index_new@Base 0.18.0 - git_index_open@Base 0.17.0 - git_index_owner@Base 0.18.0 - git_index_path@Base 0.20.0 - git_index_read@Base 0.17.0 - git_index_read_tree@Base 0.17.0 - git_index_remove@Base 0.17.0 - git_index_remove_all@Base 0.19.0 - git_index_remove_bypath@Base 0.18.0 - git_index_remove_directory@Base 0.18.0 - git_index_reuc_add@Base 0.18.0 - git_index_reuc_clear@Base 0.18.0 - git_index_reuc_entrycount@Base 0.18.0 - git_index_reuc_find@Base 0.18.0 - git_index_reuc_get_byindex@Base 0.18.0 - git_index_reuc_get_bypath@Base 0.18.0 - git_index_reuc_remove@Base 0.18.0 - git_index_set_caps@Base 0.18.0 - git_index_update_all@Base 0.19.0 - git_index_write@Base 0.17.0 - git_index_write_tree@Base 0.18.0 - git_index_write_tree_to@Base 0.18.0 - git_indexer_append@Base 0.20.0 - git_indexer_commit@Base 0.20.0 - git_indexer_free@Base 0.20.0 - git_indexer_hash@Base 0.20.0 - git_indexer_new@Base 0.20.0 -#MISSING: 0.20.0# git_indexer_stream_add@Base 0.17.0 -#MISSING: 0.20.0# git_indexer_stream_finalize@Base 0.17.0 -#MISSING: 0.20.0# git_indexer_stream_free@Base 0.17.0 -#MISSING: 0.20.0# git_indexer_stream_hash@Base 0.17.0 -#MISSING: 0.20.0# git_indexer_stream_new@Base 0.17.0 - git_libgit2_capabilities@Base 0.18.0 - git_libgit2_opts@Base 0.18.0 - git_libgit2_version@Base 0.17.0 - git_merge@Base 0.20.0 - git_merge_base@Base 0.17.0 - git_merge_base_many@Base 0.18.0 - git_merge_head_free@Base 0.19.0 - git_merge_head_from_fetchhead@Base 0.19.0 - git_merge_head_from_oid@Base 0.19.0 - git_merge_head_from_ref@Base 0.19.0 - git_merge_result_fastforward_oid@Base 0.20.0 - git_merge_result_free@Base 0.20.0 - git_merge_result_is_fastforward@Base 0.20.0 - git_merge_result_is_uptodate@Base 0.20.0 - git_merge_trees@Base 0.19.0 - git_message_prettify@Base 0.18.0 - git_note_create@Base 0.17.0 - git_note_default_ref@Base 0.17.0 - git_note_foreach@Base 0.17.0 - git_note_free@Base 0.17.0 - git_note_iterator_free@Base 0.18.0 - git_note_iterator_new@Base 0.18.0 - git_note_message@Base 0.17.0 - git_note_next@Base 0.18.0 - git_note_oid@Base 0.17.0 - git_note_read@Base 0.17.0 - git_note_remove@Base 0.17.0 - git_object__size@Base 0.17.0 - git_object_dup@Base 0.18.0 - git_object_free@Base 0.17.0 - git_object_id@Base 0.17.0 - git_object_lookup@Base 0.17.0 - git_object_lookup_bypath@Base 0.20.0 - git_object_lookup_prefix@Base 0.17.0 - git_object_owner@Base 0.17.0 - git_object_peel@Base 0.18.0 - git_object_string2type@Base 0.17.0 - git_object_type2string@Base 0.17.0 - git_object_type@Base 0.17.0 - git_object_typeisloose@Base 0.17.0 - git_odb_add_alternate@Base 0.17.0 - git_odb_add_backend@Base 0.17.0 - git_odb_add_disk_alternate@Base 0.18.0 - git_odb_backend_loose@Base 0.17.0 - git_odb_backend_malloc@Base 0.18.0 - git_odb_backend_one_pack@Base 0.18.0 - git_odb_backend_pack@Base 0.17.0 - git_odb_exists@Base 0.17.0 - git_odb_foreach@Base 0.18.0 - git_odb_free@Base 0.17.0 - git_odb_get_backend@Base 0.19.0 - git_odb_hash@Base 0.17.0 - git_odb_hashfile@Base 0.17.0 - git_odb_new@Base 0.17.0 - git_odb_num_backends@Base 0.19.0 - git_odb_object_data@Base 0.17.0 - git_odb_object_dup@Base 0.20.0 - git_odb_object_free@Base 0.17.0 - git_odb_object_id@Base 0.17.0 - git_odb_object_size@Base 0.17.0 - git_odb_object_type@Base 0.17.0 - git_odb_open@Base 0.17.0 - git_odb_open_rstream@Base 0.17.0 - git_odb_open_wstream@Base 0.17.0 - git_odb_read@Base 0.17.0 - git_odb_read_header@Base 0.17.0 - git_odb_read_prefix@Base 0.17.0 - git_odb_refresh@Base 0.18.0 - git_odb_stream_finalize_write@Base 0.20.0 - git_odb_stream_free@Base 0.20.0 - git_odb_stream_read@Base 0.20.0 - git_odb_stream_write@Base 0.20.0 - git_odb_write@Base 0.17.0 - git_odb_write_pack@Base 0.18.0 - git_oid_allocfmt@Base 0.17.0 - git_oid_cmp@Base 0.19.0 - git_oid_cpy@Base 0.17.0 - git_oid_fmt@Base 0.17.0 - git_oid_fromraw@Base 0.17.0 - git_oid_fromstr@Base 0.17.0 - git_oid_fromstrn@Base 0.17.0 - git_oid_fromstrp@Base 0.18.0 - git_oid_iszero@Base 0.17.0 - git_oid_ncmp@Base 0.17.0 - git_oid_nfmt@Base 0.19.0 - git_oid_pathfmt@Base 0.17.0 - git_oid_shorten_add@Base 0.17.0 - git_oid_shorten_free@Base 0.17.0 - git_oid_shorten_new@Base 0.17.0 - git_oid_strcmp@Base 0.19.0 - git_oid_streq@Base 0.17.0 - git_oid_tostr@Base 0.17.0 - git_packbuilder_foreach@Base 0.18.0 - git_packbuilder_free@Base 0.18.0 - git_packbuilder_hash@Base 0.20.0 - git_packbuilder_insert@Base 0.18.0 - git_packbuilder_insert_commit@Base 0.19.0 - git_packbuilder_insert_tree@Base 0.18.0 - git_packbuilder_new@Base 0.18.0 - git_packbuilder_object_count@Base 0.18.0 - git_packbuilder_set_callbacks@Base 0.20.0 - git_packbuilder_set_threads@Base 0.18.0 - git_packbuilder_write@Base 0.18.0 - git_packbuilder_written@Base 0.18.0 - git_patch_free@Base 0.20.0 - git_patch_from_blob_and_buffer@Base 0.20.0 - git_patch_from_blobs@Base 0.20.0 - git_patch_from_diff@Base 0.20.0 - git_patch_get_delta@Base 0.20.0 - git_patch_get_hunk@Base 0.20.0 - git_patch_get_line_in_hunk@Base 0.20.0 - git_patch_line_stats@Base 0.20.0 - git_patch_num_hunks@Base 0.20.0 - git_patch_num_lines_in_hunk@Base 0.20.0 - git_patch_print@Base 0.20.0 - git_patch_size@Base 0.20.0 - git_patch_to_str@Base 0.20.0 - git_pathspec_free@Base 0.20.0 - git_pathspec_match_diff@Base 0.20.0 - git_pathspec_match_index@Base 0.20.0 - git_pathspec_match_list_diff_entry@Base 0.20.0 - git_pathspec_match_list_entry@Base 0.20.0 - git_pathspec_match_list_entrycount@Base 0.20.0 - git_pathspec_match_list_failed_entry@Base 0.20.0 - git_pathspec_match_list_failed_entrycount@Base 0.20.0 - git_pathspec_match_list_free@Base 0.20.0 - git_pathspec_match_tree@Base 0.20.0 - git_pathspec_match_workdir@Base 0.20.0 - git_pathspec_matches_path@Base 0.20.0 - git_pathspec_new@Base 0.20.0 - git_push_add_refspec@Base 0.18.0 - git_push_finish@Base 0.18.0 - git_push_free@Base 0.18.0 - git_push_new@Base 0.18.0 - git_push_set_callbacks@Base 0.20.0 - git_push_set_options@Base 0.18.0 - git_push_status_foreach@Base 0.18.0 - git_push_unpack_ok@Base 0.18.0 - git_push_update_tips@Base 0.18.0 - git_refdb_backend_fs@Base 0.18.0 - git_refdb_compress@Base 0.18.0 - git_refdb_free@Base 0.18.0 - git_refdb_new@Base 0.18.0 - git_refdb_open@Base 0.18.0 - git_refdb_set_backend@Base 0.18.0 - git_reference__alloc@Base 0.18.0 - git_reference__alloc_symbolic@Base 0.19.0 - git_reference_cmp@Base 0.17.0 - git_reference_create@Base 0.18.0 - git_reference_delete@Base 0.17.0 - git_reference_dwim@Base 0.19.0 - git_reference_foreach@Base 0.17.0 - git_reference_foreach_glob@Base 0.18.0 - git_reference_foreach_name@Base 0.19.0 - git_reference_free@Base 0.17.0 - git_reference_has_log@Base 0.18.0 - git_reference_is_branch@Base 0.18.0 - git_reference_is_remote@Base 0.18.0 - git_reference_is_tag@Base 0.20.0 - git_reference_is_valid_name@Base 0.18.0 - git_reference_iterator_free@Base 0.19.0 - git_reference_iterator_glob_new@Base 0.19.0 - git_reference_iterator_new@Base 0.19.0 - git_reference_list@Base 0.17.0 - git_reference_lookup@Base 0.17.0 - git_reference_name@Base 0.17.0 - git_reference_name_to_id@Base 0.18.0 - git_reference_next@Base 0.19.0 - git_reference_next_name@Base 0.19.0 - git_reference_normalize_name@Base 0.18.0 - git_reference_owner@Base 0.17.0 - git_reference_peel@Base 0.18.0 - git_reference_rename@Base 0.17.0 - git_reference_resolve@Base 0.17.0 - git_reference_set_target@Base 0.17.0 - git_reference_shorthand@Base 0.19.0 - git_reference_symbolic_create@Base 0.18.0 - git_reference_symbolic_set_target@Base 0.18.0 - git_reference_symbolic_target@Base 0.18.0 - git_reference_target@Base 0.17.0 - git_reference_target_peel@Base 0.19.0 - git_reference_type@Base 0.17.0 - git_reflog_append@Base 0.18.0 - git_reflog_append_to@Base 0.20.0 - git_reflog_delete@Base 0.17.0 - git_reflog_drop@Base 0.18.0 - git_reflog_entry_byindex@Base 0.17.0 - git_reflog_entry_committer@Base 0.17.0 - git_reflog_entry_id_new@Base 0.18.0 - git_reflog_entry_id_old@Base 0.18.0 - git_reflog_entry_message@Base 0.18.0 - git_reflog_entrycount@Base 0.17.0 - git_reflog_free@Base 0.17.0 - git_reflog_read@Base 0.17.0 - git_reflog_rename@Base 0.17.0 - git_reflog_write@Base 0.17.0 - git_refspec_direction@Base 0.19.0 - git_refspec_dst@Base 0.17.0 - git_refspec_dst_matches@Base 0.18.0 - git_refspec_force@Base 0.18.0 - git_refspec_rtransform@Base 0.18.0 - git_refspec_src@Base 0.17.0 - git_refspec_src_matches@Base 0.17.0 - git_refspec_string@Base 0.19.0 - git_refspec_transform@Base 0.17.0 - git_remote_add_fetch@Base 0.19.0 - git_remote_add_push@Base 0.19.0 - git_remote_autotag@Base 0.18.0 - git_remote_check_cert@Base 0.18.0 - git_remote_clear_refspecs@Base 0.19.0 - git_remote_connect@Base 0.17.0 - git_remote_connected@Base 0.17.0 - git_remote_create@Base 0.18.0 - git_remote_create_inmemory@Base 0.18.0 - git_remote_create_with_fetchspec@Base 0.20.0 - git_remote_disconnect@Base 0.17.0 - git_remote_download@Base 0.17.0 - git_remote_fetch@Base 0.20.0 - git_remote_free@Base 0.17.0 - git_remote_get_fetch_refspecs@Base 0.19.0 - git_remote_get_push_refspecs@Base 0.19.0 - git_remote_get_refspec@Base 0.19.0 - git_remote_is_valid_name@Base 0.18.0 - git_remote_list@Base 0.17.0 - git_remote_load@Base 0.17.0 - git_remote_ls@Base 0.17.0 - git_remote_name@Base 0.17.0 - git_remote_owner@Base 0.20.0 - git_remote_pushurl@Base 0.18.0 - git_remote_refspec_count@Base 0.19.0 -#MISSING: 0.20.0# git_remote_remove_refspec@Base 0.19.0 - git_remote_rename@Base 0.18.0 - git_remote_save@Base 0.17.0 - git_remote_set_autotag@Base 0.18.0 - git_remote_set_callbacks@Base 0.18.0 -#MISSING: 0.20.0# git_remote_set_cred_acquire_cb@Base 0.18.0 - git_remote_set_fetch_refspecs@Base 0.20.0 - git_remote_set_push_refspecs@Base 0.20.0 - git_remote_set_pushurl@Base 0.18.0 - git_remote_set_transport@Base 0.18.0 - git_remote_set_update_fetchhead@Base 0.18.0 - git_remote_set_url@Base 0.18.0 - git_remote_stats@Base 0.18.0 - git_remote_stop@Base 0.18.0 - git_remote_supported_url@Base 0.17.0 - git_remote_update_fetchhead@Base 0.18.0 - git_remote_update_tips@Base 0.17.0 - git_remote_url@Base 0.17.0 - git_remote_valid_url@Base 0.17.0 - git_repository__cleanup@Base 0.19.0 - git_repository_config@Base 0.17.0 - git_repository_detach_head@Base 0.18.0 - git_repository_discover@Base 0.17.0 - git_repository_fetchhead_foreach@Base 0.18.0 - git_repository_free@Base 0.17.0 - git_repository_get_namespace@Base 0.19.0 - git_repository_hashfile@Base 0.18.0 - git_repository_head@Base 0.17.0 - git_repository_head_detached@Base 0.17.0 -#MISSING: 0.20.0# git_repository_head_orphan@Base 0.17.0 - git_repository_head_unborn@Base 0.20.0 - git_repository_index@Base 0.17.0 - git_repository_init@Base 0.17.0 - git_repository_init_ext@Base 0.18.0 - git_repository_is_bare@Base 0.17.0 - git_repository_is_empty@Base 0.17.0 - git_repository_is_shallow@Base 0.19.0 - git_repository_merge_cleanup@Base 0.18.0 - git_repository_mergehead_foreach@Base 0.18.0 - git_repository_message@Base 0.18.0 - git_repository_message_remove@Base 0.18.0 - git_repository_new@Base 0.19.0 - git_repository_odb@Base 0.17.0 - git_repository_open@Base 0.17.0 - git_repository_open_bare@Base 0.19.0 - git_repository_open_ext@Base 0.17.0 - git_repository_path@Base 0.17.0 - git_repository_refdb@Base 0.18.0 - git_repository_reinit_filesystem@Base 0.20.0 - git_repository_set_config@Base 0.17.0 - git_repository_set_head@Base 0.18.0 - git_repository_set_head_detached@Base 0.18.0 - git_repository_set_index@Base 0.17.0 - git_repository_set_namespace@Base 0.19.0 - git_repository_set_odb@Base 0.17.0 - git_repository_set_refdb@Base 0.18.0 - git_repository_set_workdir@Base 0.17.0 - git_repository_state@Base 0.18.0 - git_repository_workdir@Base 0.17.0 - git_repository_wrap_odb@Base 0.18.0 - git_reset@Base 0.18.0 - git_reset_default@Base 0.18.0 - git_revparse@Base 0.18.0 - git_revparse_ext@Base 0.19.0 - git_revparse_single@Base 0.18.0 - git_revwalk_free@Base 0.17.0 - git_revwalk_hide@Base 0.17.0 - git_revwalk_hide_glob@Base 0.17.0 - git_revwalk_hide_head@Base 0.17.0 - git_revwalk_hide_ref@Base 0.17.0 - git_revwalk_new@Base 0.17.0 - git_revwalk_next@Base 0.17.0 - git_revwalk_push@Base 0.17.0 - git_revwalk_push_glob@Base 0.17.0 - git_revwalk_push_head@Base 0.17.0 - git_revwalk_push_range@Base 0.18.0 - git_revwalk_push_ref@Base 0.17.0 - git_revwalk_repository@Base 0.17.0 - git_revwalk_reset@Base 0.17.0 - git_revwalk_simplify_first_parent@Base 0.20.0 - git_revwalk_sorting@Base 0.17.0 - git_signature_default@Base 0.20.0 - git_signature_dup@Base 0.17.0 - git_signature_free@Base 0.17.0 - git_signature_new@Base 0.17.0 - git_signature_now@Base 0.17.0 - git_smart_subtransport_git@Base 0.18.0 - git_smart_subtransport_http@Base 0.18.0 - git_smart_subtransport_ssh@Base 0.20.0 - git_stash_drop@Base 0.18.0 - git_stash_foreach@Base 0.18.0 - git_stash_save@Base 0.18.0 - git_status_byindex@Base 0.19.0 - git_status_file@Base 0.17.0 - git_status_foreach@Base 0.17.0 - git_status_foreach_ext@Base 0.17.0 - git_status_list_entrycount@Base 0.19.0 - git_status_list_free@Base 0.19.0 - git_status_list_new@Base 0.19.0 - git_status_should_ignore@Base 0.17.0 - git_strarray_copy@Base 0.17.0 - git_strarray_free@Base 0.17.0 - git_submodule_add_finalize@Base 0.18.0 - git_submodule_add_setup@Base 0.18.0 - git_submodule_add_to_index@Base 0.18.0 - git_submodule_fetch_recurse_submodules@Base 0.18.0 - git_submodule_foreach@Base 0.17.0 - git_submodule_head_id@Base 0.18.0 - git_submodule_ignore@Base 0.18.0 - git_submodule_index_id@Base 0.18.0 - git_submodule_init@Base 0.18.0 - git_submodule_location@Base 0.18.0 - git_submodule_lookup@Base 0.17.0 - git_submodule_name@Base 0.18.0 - git_submodule_open@Base 0.18.0 - git_submodule_owner@Base 0.18.0 - git_submodule_path@Base 0.18.0 - git_submodule_reload@Base 0.18.0 - git_submodule_reload_all@Base 0.18.0 - git_submodule_save@Base 0.18.0 - git_submodule_set_fetch_recurse_submodules@Base 0.18.0 - git_submodule_set_ignore@Base 0.18.0 - git_submodule_set_update@Base 0.18.0 - git_submodule_set_url@Base 0.18.0 - git_submodule_status@Base 0.18.0 - git_submodule_sync@Base 0.18.0 - git_submodule_update@Base 0.18.0 - git_submodule_url@Base 0.18.0 - git_submodule_wd_id@Base 0.18.0 - git_tag_annotation_create@Base 0.19.0 - git_tag_create@Base 0.17.0 - git_tag_create_frombuffer@Base 0.17.0 - git_tag_create_lightweight@Base 0.17.0 - git_tag_delete@Base 0.17.0 - git_tag_foreach@Base 0.18.0 - git_tag_free@Base 0.19.0 - git_tag_id@Base 0.17.0 - git_tag_list@Base 0.17.0 - git_tag_list_match@Base 0.17.0 - git_tag_lookup@Base 0.19.0 - git_tag_lookup_prefix@Base 0.19.0 - git_tag_message@Base 0.17.0 - git_tag_name@Base 0.17.0 - git_tag_owner@Base 0.19.0 - git_tag_peel@Base 0.17.0 - git_tag_tagger@Base 0.17.0 - git_tag_target@Base 0.17.0 - git_tag_target_id@Base 0.18.0 - git_tag_target_type@Base 0.18.0 - git_threads_init@Base 0.17.0 - git_threads_shutdown@Base 0.17.0 - git_trace_set@Base 0.18.0 - git_transport_dummy@Base 0.18.0 - git_transport_local@Base 0.18.0 - git_transport_new@Base 0.18.0 - git_transport_register@Base 0.20.0 - git_transport_smart@Base 0.18.0 - git_transport_unregister@Base 0.20.0 - git_tree_entry_byindex@Base 0.17.0 - git_tree_entry_byname@Base 0.17.0 - git_tree_entry_byoid@Base 0.18.0 - git_tree_entry_bypath@Base 0.18.0 - git_tree_entry_cmp@Base 0.18.0 - git_tree_entry_dup@Base 0.18.0 - git_tree_entry_filemode@Base 0.18.0 - git_tree_entry_filemode_raw@Base 0.20.0 - git_tree_entry_free@Base 0.18.0 - git_tree_entry_id@Base 0.17.0 - git_tree_entry_name@Base 0.17.0 - git_tree_entry_to_object@Base 0.17.0 - git_tree_entry_type@Base 0.17.0 - git_tree_entrycount@Base 0.17.0 - git_tree_free@Base 0.19.0 - git_tree_id@Base 0.17.0 - git_tree_lookup@Base 0.19.0 - git_tree_lookup_prefix@Base 0.19.0 - git_tree_owner@Base 0.18.0 - git_tree_walk@Base 0.17.0 - git_treebuilder_clear@Base 0.17.0 - git_treebuilder_create@Base 0.17.0 - git_treebuilder_entrycount@Base 0.18.0 - git_treebuilder_filter@Base 0.17.0 - git_treebuilder_free@Base 0.17.0 - git_treebuilder_get@Base 0.17.0 - git_treebuilder_insert@Base 0.17.0 - git_treebuilder_remove@Base 0.17.0 - git_treebuilder_write@Base 0.17.0 - giterr_clear@Base 0.17.0 - giterr_detach@Base 0.20.0 - giterr_last@Base 0.17.0 - giterr_set_oom@Base 0.18.0 - giterr_set_str@Base 0.18.0 diff -Nru libgit2-0.20.0/debian/libgit2-22.install libgit2-0.22.2/debian/libgit2-22.install --- libgit2-0.20.0/debian/libgit2-22.install 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/debian/libgit2-22.install 2015-07-19 13:53:06.000000000 +0000 @@ -0,0 +1 @@ +/usr/lib/*/lib*so.* diff -Nru libgit2-0.20.0/debian/libgit2-22.lintian-overrides libgit2-0.22.2/debian/libgit2-22.lintian-overrides --- libgit2-0.20.0/debian/libgit2-22.lintian-overrides 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/debian/libgit2-22.lintian-overrides 2015-07-19 13:53:06.000000000 +0000 @@ -0,0 +1 @@ +libgit2-22: spelling-error-in-binary diff -Nru libgit2-0.20.0/debian/libgit2-22.symbols libgit2-0.22.2/debian/libgit2-22.symbols --- libgit2-0.20.0/debian/libgit2-22.symbols 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/debian/libgit2-22.symbols 2015-07-19 13:53:06.000000000 +0000 @@ -0,0 +1,709 @@ +libgit2.so.22 libgit2-22 #MINVER# + git_annotated_commit_free@Base 0.22.0 + git_annotated_commit_from_fetchhead@Base 0.22.0 + git_annotated_commit_from_ref@Base 0.22.0 + git_annotated_commit_id@Base 0.22.0 + git_annotated_commit_lookup@Base 0.22.0 + git_attr_add_macro@Base 0.17.0 + git_attr_cache_flush@Base 0.17.0 + git_attr_foreach@Base 0.17.0 + git_attr_get@Base 0.17.0 + git_attr_get_many@Base 0.17.0 + git_attr_value@Base 0.18.0 + git_blame_buffer@Base 0.20.0 + git_blame_file@Base 0.20.0 + git_blame_free@Base 0.20.0 + git_blame_get_hunk_byindex@Base 0.20.0 + git_blame_get_hunk_byline@Base 0.20.0 + git_blame_get_hunk_count@Base 0.20.0 + git_blame_init_options@Base 0.21.0 + git_blob_create_frombuffer@Base 0.17.0 + git_blob_create_fromchunks@Base 0.18.0 + git_blob_create_fromdisk@Base 0.17.0 + git_blob_create_fromworkdir@Base 0.18.0 + git_blob_filtered_content@Base 0.20.0 + git_blob_free@Base 0.19.0 + git_blob_id@Base 0.19.0 + git_blob_is_binary@Base 0.18.0 + git_blob_lookup@Base 0.19.0 + git_blob_lookup_prefix@Base 0.19.0 + git_blob_owner@Base 0.19.0 + git_blob_rawcontent@Base 0.17.0 + git_blob_rawsize@Base 0.17.0 + git_branch_create@Base 0.17.0 + git_branch_delete@Base 0.17.0 + git_branch_is_head@Base 0.18.0 + git_branch_iterator_free@Base 0.20.0 + git_branch_iterator_new@Base 0.20.0 + git_branch_lookup@Base 0.18.0 + git_branch_move@Base 0.17.0 + git_branch_name@Base 0.18.0 + git_branch_next@Base 0.20.0 + git_branch_remote_name@Base 0.18.0 + git_branch_set_upstream@Base 0.18.0 + git_branch_upstream@Base 0.18.0 + git_branch_upstream_name@Base 0.18.0 + git_branch_upstream_remote@Base 0.22.0 + git_buf_contains_nul@Base 0.22.0 + git_buf_free@Base 0.20.0 + git_buf_grow@Base 0.20.0 + git_buf_is_binary@Base 0.22.0 + git_buf_set@Base 0.20.0 + git_checkout_head@Base 0.18.0 + git_checkout_index@Base 0.18.0 + git_checkout_init_options@Base 0.21.0 + git_checkout_tree@Base 0.18.0 + git_cherrypick@Base 0.22.0 + git_cherrypick_commit@Base 0.22.0 + git_cherrypick_init_options@Base 0.22.0 + git_clone@Base 0.18.0 + git_clone_init_options@Base 0.21.0 + git_commit_amend@Base 0.21.0 + git_commit_author@Base 0.17.0 + git_commit_committer@Base 0.17.0 + git_commit_create@Base 0.17.0 + git_commit_create_from_callback@Base 0.21.0 + git_commit_create_from_ids@Base 0.21.0 + git_commit_create_v@Base 0.17.0 + git_commit_free@Base 0.19.0 + git_commit_id@Base 0.17.0 + git_commit_lookup@Base 0.19.0 + git_commit_lookup_prefix@Base 0.19.0 + git_commit_message@Base 0.17.0 + git_commit_message_encoding@Base 0.17.0 + git_commit_message_raw@Base 0.20.0 + git_commit_nth_gen_ancestor@Base 0.18.0 + git_commit_owner@Base 0.19.0 + git_commit_parent@Base 0.17.0 + git_commit_parent_id@Base 0.18.0 + git_commit_parentcount@Base 0.17.0 + git_commit_raw_header@Base 0.20.0 + git_commit_summary@Base 0.21.0 + git_commit_time@Base 0.17.0 + git_commit_time_offset@Base 0.17.0 + git_commit_tree@Base 0.17.0 + git_commit_tree_id@Base 0.18.0 + git_config_add_backend@Base 0.18.0 + git_config_add_file_ondisk@Base 0.17.0 + git_config_backend_foreach_match@Base 0.20.0 + git_config_delete_entry@Base 0.18.0 + git_config_delete_multivar@Base 0.20.0 + git_config_find_global@Base 0.17.0 + git_config_find_system@Base 0.17.0 + git_config_find_xdg@Base 0.18.0 + git_config_foreach@Base 0.17.0 + git_config_foreach_match@Base 0.18.0 + git_config_free@Base 0.17.0 + git_config_get_bool@Base 0.17.0 + git_config_get_entry@Base 0.18.0 + git_config_get_int32@Base 0.17.0 + git_config_get_int64@Base 0.17.0 + git_config_get_mapped@Base 0.17.0 + git_config_get_multivar_foreach@Base 0.20.0 + git_config_get_string@Base 0.17.0 + git_config_init_backend@Base 0.21.0 + git_config_iterator_free@Base 0.20.0 + git_config_iterator_glob_new@Base 0.20.0 + git_config_iterator_new@Base 0.20.0 + git_config_lookup_map_value@Base 0.18.0 + git_config_multivar_iterator_new@Base 0.20.0 + git_config_new@Base 0.17.0 + git_config_next@Base 0.20.0 + git_config_open_default@Base 0.18.0 + git_config_open_global@Base 0.19.0 + git_config_open_level@Base 0.18.0 + git_config_open_ondisk@Base 0.17.0 + git_config_parse_bool@Base 0.18.0 + git_config_parse_int32@Base 0.18.0 + git_config_parse_int64@Base 0.18.0 + git_config_set_bool@Base 0.17.0 + git_config_set_int32@Base 0.17.0 + git_config_set_int64@Base 0.17.0 + git_config_set_multivar@Base 0.17.0 + git_config_set_string@Base 0.17.0 + git_config_snapshot@Base 0.21.0 + git_cred_default_new@Base 0.20.0 + git_cred_has_username@Base 0.20.0 + git_cred_ssh_custom_new@Base 0.20.0 + git_cred_ssh_interactive_new@Base 0.21.0 + git_cred_ssh_key_from_agent@Base 0.21.0 + git_cred_ssh_key_new@Base 0.20.0 + git_cred_username_new@Base 0.22.0 + git_cred_userpass@Base 0.18.0 + git_cred_userpass_plaintext_new@Base 0.18.0 + git_describe_commit@Base 0.22.0 + git_describe_format@Base 0.22.0 + git_describe_init_format_options@Base 0.22.0 + git_describe_init_options@Base 0.22.0 + git_describe_result_free@Base 0.22.0 + git_describe_workdir@Base 0.22.0 + git_diff_blob_to_buffer@Base 0.18.0 + git_diff_blobs@Base 0.17.0 + git_diff_buffers@Base 0.21.0 + git_diff_commit_as_email@Base 0.21.0 + git_diff_find_init_options@Base 0.21.0 + git_diff_find_similar@Base 0.18.0 + git_diff_foreach@Base 0.17.0 + git_diff_format_email@Base 0.21.0 + git_diff_format_email_init_options@Base 0.21.0 + git_diff_free@Base 0.20.0 + git_diff_get_delta@Base 0.20.0 + git_diff_get_perfdata@Base 0.21.0 + git_diff_get_stats@Base 0.21.0 + git_diff_index_to_workdir@Base 0.18.0 + git_diff_init_options@Base 0.21.0 + git_diff_is_sorted_icase@Base 0.20.0 + git_diff_merge@Base 0.17.0 + git_diff_num_deltas@Base 0.18.0 + git_diff_num_deltas_of_type@Base 0.18.0 + git_diff_print@Base 0.20.0 + git_diff_print_callback__to_buf@Base 0.21.0 + git_diff_print_callback__to_file_handle@Base 0.21.0 + git_diff_stats_deletions@Base 0.21.0 + git_diff_stats_files_changed@Base 0.21.0 + git_diff_stats_free@Base 0.21.0 + git_diff_stats_insertions@Base 0.21.0 + git_diff_stats_to_buf@Base 0.21.0 + git_diff_status_char@Base 0.18.0 + git_diff_tree_to_index@Base 0.18.0 + git_diff_tree_to_tree@Base 0.17.0 + git_diff_tree_to_workdir@Base 0.18.0 + git_diff_tree_to_workdir_with_index@Base 0.20.0 + git_filter_list_apply_to_blob@Base 0.20.0 + git_filter_list_apply_to_data@Base 0.20.0 + git_filter_list_apply_to_file@Base 0.20.0 + git_filter_list_free@Base 0.20.0 + git_filter_list_length@Base 0.20.0 + git_filter_list_load@Base 0.20.0 + git_filter_list_new@Base 0.20.0 + git_filter_list_push@Base 0.20.0 + git_filter_lookup@Base 0.20.0 + git_filter_register@Base 0.20.0 + git_filter_source_filemode@Base 0.20.0 + git_filter_source_id@Base 0.20.0 + git_filter_source_mode@Base 0.20.0 + git_filter_source_options@Base 0.21.0 + git_filter_source_path@Base 0.20.0 + git_filter_source_repo@Base 0.20.0 + git_filter_unregister@Base 0.20.0 + git_graph_ahead_behind@Base 0.18.0 + git_graph_descendant_of@Base 0.21.0 + git_hashsig_compare@Base 0.22.0 + git_hashsig_create@Base 0.22.0 + git_hashsig_create_fromfile@Base 0.22.0 + git_hashsig_free@Base 0.22.0 + git_ignore_add_rule@Base 0.18.0 + git_ignore_clear_internal_rules@Base 0.18.0 + git_ignore_path_is_ignored@Base 0.18.0 + git_index_add@Base 0.17.0 + git_index_add_all@Base 0.19.0 + git_index_add_bypath@Base 0.18.0 + git_index_caps@Base 0.18.0 + git_index_clear@Base 0.17.0 + git_index_conflict_add@Base 0.18.0 + git_index_conflict_cleanup@Base 0.18.0 + git_index_conflict_get@Base 0.18.0 + git_index_conflict_iterator_free@Base 0.19.0 + git_index_conflict_iterator_new@Base 0.19.0 + git_index_conflict_next@Base 0.19.0 + git_index_conflict_remove@Base 0.18.0 + git_index_entry_stage@Base 0.17.0 + git_index_entrycount@Base 0.17.0 + git_index_find@Base 0.17.0 + git_index_free@Base 0.17.0 + git_index_get_byindex@Base 0.18.0 + git_index_get_bypath@Base 0.18.0 + git_index_has_conflicts@Base 0.18.0 + git_index_name_add@Base 0.19.0 + git_index_name_clear@Base 0.19.0 + git_index_name_entrycount@Base 0.19.0 + git_index_name_get_byindex@Base 0.19.0 + git_index_new@Base 0.18.0 + git_index_open@Base 0.17.0 + git_index_owner@Base 0.18.0 + git_index_path@Base 0.20.0 + git_index_read@Base 0.17.0 + git_index_read_tree@Base 0.17.0 + git_index_remove@Base 0.17.0 + git_index_remove_all@Base 0.19.0 + git_index_remove_bypath@Base 0.18.0 + git_index_remove_directory@Base 0.18.0 + git_index_reuc_add@Base 0.18.0 + git_index_reuc_clear@Base 0.18.0 + git_index_reuc_entrycount@Base 0.18.0 + git_index_reuc_find@Base 0.18.0 + git_index_reuc_get_byindex@Base 0.18.0 + git_index_reuc_get_bypath@Base 0.18.0 + git_index_reuc_remove@Base 0.18.0 + git_index_set_caps@Base 0.18.0 + git_index_update_all@Base 0.19.0 + git_index_write@Base 0.17.0 + git_index_write_tree@Base 0.18.0 + git_index_write_tree_to@Base 0.18.0 + git_indexer_append@Base 0.20.0 + git_indexer_commit@Base 0.20.0 + git_indexer_free@Base 0.20.0 + git_indexer_hash@Base 0.20.0 + git_indexer_new@Base 0.20.0 + git_libgit2_features@Base 0.21.0 + git_libgit2_init@Base 0.22.0 + git_libgit2_opts@Base 0.18.0 + git_libgit2_shutdown@Base 0.22.0 + git_libgit2_version@Base 0.17.0 + git_merge@Base 0.20.0 + git_merge_analysis@Base 0.21.0 + git_merge_base@Base 0.17.0 + git_merge_base_many@Base 0.18.0 + git_merge_base_octopus@Base 0.21.0 + git_merge_bases@Base 0.22.0 + git_merge_bases_many@Base 0.22.0 + git_merge_commits@Base 0.21.0 + git_merge_file@Base 0.21.0 + git_merge_file_from_index@Base 0.21.0 + git_merge_file_init_input@Base 0.21.0 + git_merge_file_init_options@Base 0.21.0 + git_merge_file_result_free@Base 0.21.0 + git_merge_init_options@Base 0.21.0 + git_merge_trees@Base 0.19.0 + git_message_prettify@Base 0.18.0 + git_note_author@Base 0.22.0 + git_note_committer@Base 0.22.0 + git_note_create@Base 0.17.0 + git_note_default_ref@Base 0.17.0 + git_note_foreach@Base 0.17.0 + git_note_free@Base 0.17.0 + git_note_id@Base 0.21.0 + git_note_iterator_free@Base 0.18.0 + git_note_iterator_new@Base 0.18.0 + git_note_message@Base 0.17.0 + git_note_next@Base 0.18.0 + git_note_read@Base 0.17.0 + git_note_remove@Base 0.17.0 + git_object__size@Base 0.17.0 + git_object_dup@Base 0.18.0 + git_object_free@Base 0.17.0 + git_object_id@Base 0.17.0 + git_object_lookup@Base 0.17.0 + git_object_lookup_bypath@Base 0.20.0 + git_object_lookup_prefix@Base 0.17.0 + git_object_owner@Base 0.17.0 + git_object_peel@Base 0.18.0 + git_object_short_id@Base 0.21.0 + git_object_string2type@Base 0.17.0 + git_object_type2string@Base 0.17.0 + git_object_type@Base 0.17.0 + git_object_typeisloose@Base 0.17.0 + git_odb_add_alternate@Base 0.17.0 + git_odb_add_backend@Base 0.17.0 + git_odb_add_disk_alternate@Base 0.18.0 + git_odb_backend_loose@Base 0.17.0 + git_odb_backend_malloc@Base 0.18.0 + git_odb_backend_one_pack@Base 0.18.0 + git_odb_backend_pack@Base 0.17.0 + git_odb_exists@Base 0.17.0 + git_odb_exists_prefix@Base 0.21.0 + git_odb_foreach@Base 0.18.0 + git_odb_free@Base 0.17.0 + git_odb_get_backend@Base 0.19.0 + git_odb_hash@Base 0.17.0 + git_odb_hashfile@Base 0.17.0 + git_odb_init_backend@Base 0.21.0 + git_odb_new@Base 0.17.0 + git_odb_num_backends@Base 0.19.0 + git_odb_object_data@Base 0.17.0 + git_odb_object_dup@Base 0.20.0 + git_odb_object_free@Base 0.17.0 + git_odb_object_id@Base 0.17.0 + git_odb_object_size@Base 0.17.0 + git_odb_object_type@Base 0.17.0 + git_odb_open@Base 0.17.0 + git_odb_open_rstream@Base 0.17.0 + git_odb_open_wstream@Base 0.17.0 + git_odb_read@Base 0.17.0 + git_odb_read_header@Base 0.17.0 + git_odb_read_prefix@Base 0.17.0 + git_odb_refresh@Base 0.18.0 + git_odb_stream_finalize_write@Base 0.20.0 + git_odb_stream_free@Base 0.20.0 + git_odb_stream_read@Base 0.20.0 + git_odb_stream_write@Base 0.20.0 + git_odb_write@Base 0.17.0 + git_odb_write_pack@Base 0.18.0 + git_oid_cmp@Base 0.19.0 + git_oid_cpy@Base 0.17.0 + git_oid_equal@Base 0.21.0 + git_oid_fmt@Base 0.17.0 + git_oid_fromraw@Base 0.17.0 + git_oid_fromstr@Base 0.17.0 + git_oid_fromstrn@Base 0.17.0 + git_oid_fromstrp@Base 0.18.0 + git_oid_iszero@Base 0.17.0 + git_oid_ncmp@Base 0.17.0 + git_oid_nfmt@Base 0.19.0 + git_oid_pathfmt@Base 0.17.0 + git_oid_shorten_add@Base 0.17.0 + git_oid_shorten_free@Base 0.17.0 + git_oid_shorten_new@Base 0.17.0 + git_oid_strcmp@Base 0.19.0 + git_oid_streq@Base 0.17.0 + git_oid_tostr@Base 0.17.0 + git_oid_tostr_s@Base 0.22.0 + git_oidarray_free@Base 0.22.0 + git_openssl_set_locking@Base 0.22.0 + git_packbuilder_foreach@Base 0.18.0 + git_packbuilder_free@Base 0.18.0 + git_packbuilder_hash@Base 0.20.0 + git_packbuilder_insert@Base 0.18.0 + git_packbuilder_insert_commit@Base 0.19.0 + git_packbuilder_insert_tree@Base 0.18.0 + git_packbuilder_new@Base 0.18.0 + git_packbuilder_object_count@Base 0.18.0 + git_packbuilder_set_callbacks@Base 0.20.0 + git_packbuilder_set_threads@Base 0.18.0 + git_packbuilder_write@Base 0.18.0 + git_packbuilder_write_buf@Base 0.21.0 + git_packbuilder_written@Base 0.18.0 + git_patch_free@Base 0.20.0 + git_patch_from_blob_and_buffer@Base 0.20.0 + git_patch_from_blobs@Base 0.20.0 + git_patch_from_buffers@Base 0.21.0 + git_patch_from_diff@Base 0.20.0 + git_patch_get_delta@Base 0.20.0 + git_patch_get_hunk@Base 0.20.0 + git_patch_get_line_in_hunk@Base 0.20.0 + git_patch_line_stats@Base 0.20.0 + git_patch_num_hunks@Base 0.20.0 + git_patch_num_lines_in_hunk@Base 0.20.0 + git_patch_print@Base 0.20.0 + git_patch_size@Base 0.20.0 + git_patch_to_buf@Base 0.21.0 + git_pathspec_free@Base 0.20.0 + git_pathspec_match_diff@Base 0.20.0 + git_pathspec_match_index@Base 0.20.0 + git_pathspec_match_list_diff_entry@Base 0.20.0 + git_pathspec_match_list_entry@Base 0.20.0 + git_pathspec_match_list_entrycount@Base 0.20.0 + git_pathspec_match_list_failed_entry@Base 0.20.0 + git_pathspec_match_list_failed_entrycount@Base 0.20.0 + git_pathspec_match_list_free@Base 0.20.0 + git_pathspec_match_tree@Base 0.20.0 + git_pathspec_match_workdir@Base 0.20.0 + git_pathspec_matches_path@Base 0.20.0 + git_pathspec_new@Base 0.20.0 + git_push_init_options@Base 0.21.0 + git_rebase_abort@Base 0.22.0 + git_rebase_commit@Base 0.22.0 + git_rebase_finish@Base 0.22.0 + git_rebase_free@Base 0.22.0 + git_rebase_init@Base 0.22.0 + git_rebase_init_options@Base 0.22.0 + git_rebase_next@Base 0.22.0 + git_rebase_open@Base 0.22.0 + git_rebase_operation_byindex@Base 0.22.0 + git_rebase_operation_current@Base 0.22.0 + git_rebase_operation_entrycount@Base 0.22.0 + git_refdb_backend_fs@Base 0.18.0 + git_refdb_compress@Base 0.18.0 + git_refdb_free@Base 0.18.0 + git_refdb_init_backend@Base 0.21.0 + git_refdb_new@Base 0.18.0 + git_refdb_open@Base 0.18.0 + git_refdb_set_backend@Base 0.18.0 + git_reference__alloc@Base 0.18.0 + git_reference__alloc_symbolic@Base 0.19.0 + git_reference_cmp@Base 0.17.0 + git_reference_create@Base 0.18.0 + git_reference_create_matching@Base 0.21.0 + git_reference_delete@Base 0.17.0 + git_reference_dwim@Base 0.19.0 + git_reference_ensure_log@Base 0.21.0 + git_reference_foreach@Base 0.17.0 + git_reference_foreach_glob@Base 0.18.0 + git_reference_foreach_name@Base 0.19.0 + git_reference_free@Base 0.17.0 + git_reference_has_log@Base 0.18.0 + git_reference_is_branch@Base 0.18.0 + git_reference_is_note@Base 0.21.0 + git_reference_is_remote@Base 0.18.0 + git_reference_is_tag@Base 0.20.0 + git_reference_is_valid_name@Base 0.18.0 + git_reference_iterator_free@Base 0.19.0 + git_reference_iterator_glob_new@Base 0.19.0 + git_reference_iterator_new@Base 0.19.0 + git_reference_list@Base 0.17.0 + git_reference_lookup@Base 0.17.0 + git_reference_name@Base 0.17.0 + git_reference_name_to_id@Base 0.18.0 + git_reference_next@Base 0.19.0 + git_reference_next_name@Base 0.19.0 + git_reference_normalize_name@Base 0.18.0 + git_reference_owner@Base 0.17.0 + git_reference_peel@Base 0.18.0 + git_reference_remove@Base 0.21.0 + git_reference_rename@Base 0.17.0 + git_reference_resolve@Base 0.17.0 + git_reference_set_target@Base 0.17.0 + git_reference_shorthand@Base 0.19.0 + git_reference_symbolic_create@Base 0.18.0 + git_reference_symbolic_create_matching@Base 0.21.0 + git_reference_symbolic_set_target@Base 0.18.0 + git_reference_symbolic_target@Base 0.18.0 + git_reference_target@Base 0.17.0 + git_reference_target_peel@Base 0.19.0 + git_reference_type@Base 0.17.0 + git_reflog_append@Base 0.18.0 + git_reflog_delete@Base 0.17.0 + git_reflog_drop@Base 0.18.0 + git_reflog_entry_byindex@Base 0.17.0 + git_reflog_entry_committer@Base 0.17.0 + git_reflog_entry_id_new@Base 0.18.0 + git_reflog_entry_id_old@Base 0.18.0 + git_reflog_entry_message@Base 0.18.0 + git_reflog_entrycount@Base 0.17.0 + git_reflog_free@Base 0.17.0 + git_reflog_read@Base 0.17.0 + git_reflog_rename@Base 0.17.0 + git_reflog_write@Base 0.17.0 + git_refspec_direction@Base 0.19.0 + git_refspec_dst@Base 0.17.0 + git_refspec_dst_matches@Base 0.18.0 + git_refspec_force@Base 0.18.0 + git_refspec_rtransform@Base 0.18.0 + git_refspec_src@Base 0.17.0 + git_refspec_src_matches@Base 0.17.0 + git_refspec_string@Base 0.19.0 + git_refspec_transform@Base 0.17.0 + git_remote_add_fetch@Base 0.19.0 + git_remote_add_push@Base 0.19.0 + git_remote_autotag@Base 0.18.0 + git_remote_clear_refspecs@Base 0.19.0 + git_remote_connect@Base 0.17.0 + git_remote_connected@Base 0.17.0 + git_remote_create@Base 0.18.0 + git_remote_create_anonymous@Base 0.21.0 + git_remote_create_with_fetchspec@Base 0.20.0 + git_remote_default_branch@Base 0.21.0 + git_remote_delete@Base 0.21.0 + git_remote_disconnect@Base 0.17.0 + git_remote_download@Base 0.17.0 + git_remote_dup@Base 0.21.0 + git_remote_fetch@Base 0.20.0 + git_remote_free@Base 0.17.0 + git_remote_get_callbacks@Base 0.21.0 + git_remote_get_fetch_refspecs@Base 0.19.0 + git_remote_get_push_refspecs@Base 0.19.0 + git_remote_get_refspec@Base 0.19.0 + git_remote_init_callbacks@Base 0.21.0 + git_remote_is_valid_name@Base 0.18.0 + git_remote_list@Base 0.17.0 + git_remote_lookup@Base 0.22.0 + git_remote_ls@Base 0.17.0 + git_remote_name@Base 0.17.0 + git_remote_owner@Base 0.20.0 + git_remote_prune@Base 0.22.0 + git_remote_prune_refs@Base 0.22.0 + git_remote_push@Base 0.22.0 + git_remote_pushurl@Base 0.18.0 + git_remote_refspec_count@Base 0.19.0 + git_remote_rename@Base 0.18.0 + git_remote_save@Base 0.17.0 + git_remote_set_autotag@Base 0.18.0 + git_remote_set_callbacks@Base 0.18.0 + git_remote_set_fetch_refspecs@Base 0.20.0 + git_remote_set_push_refspecs@Base 0.20.0 + git_remote_set_pushurl@Base 0.18.0 + git_remote_set_transport@Base 0.18.0 + git_remote_set_update_fetchhead@Base 0.18.0 + git_remote_set_url@Base 0.18.0 + git_remote_stats@Base 0.18.0 + git_remote_stop@Base 0.18.0 + git_remote_update_fetchhead@Base 0.18.0 + git_remote_update_tips@Base 0.17.0 + git_remote_upload@Base 0.22.0 + git_remote_url@Base 0.17.0 + git_repository__cleanup@Base 0.19.0 + git_repository_config@Base 0.17.0 + git_repository_config_snapshot@Base 0.21.0 + git_repository_detach_head@Base 0.18.0 + git_repository_discover@Base 0.17.0 + git_repository_fetchhead_foreach@Base 0.18.0 + git_repository_free@Base 0.17.0 + git_repository_get_namespace@Base 0.19.0 + git_repository_hashfile@Base 0.18.0 + git_repository_head@Base 0.17.0 + git_repository_head_detached@Base 0.17.0 + git_repository_head_unborn@Base 0.20.0 + git_repository_index@Base 0.17.0 + git_repository_init@Base 0.17.0 + git_repository_init_ext@Base 0.18.0 + git_repository_init_init_options@Base 0.21.0 + git_repository_is_bare@Base 0.17.0 + git_repository_is_empty@Base 0.17.0 + git_repository_is_shallow@Base 0.19.0 + git_repository_mergehead_foreach@Base 0.18.0 + git_repository_message@Base 0.18.0 + git_repository_message_remove@Base 0.18.0 + git_repository_new@Base 0.19.0 + git_repository_odb@Base 0.17.0 + git_repository_open@Base 0.17.0 + git_repository_open_bare@Base 0.19.0 + git_repository_open_ext@Base 0.17.0 + git_repository_path@Base 0.17.0 + git_repository_refdb@Base 0.18.0 + git_repository_reinit_filesystem@Base 0.20.0 + git_repository_set_bare@Base 0.22.0 + git_repository_set_config@Base 0.17.0 + git_repository_set_head@Base 0.18.0 + git_repository_set_head_detached@Base 0.18.0 + git_repository_set_index@Base 0.17.0 + git_repository_set_namespace@Base 0.19.0 + git_repository_set_odb@Base 0.17.0 + git_repository_set_refdb@Base 0.18.0 + git_repository_set_workdir@Base 0.17.0 + git_repository_state@Base 0.18.0 + git_repository_state_cleanup@Base 0.21.0 + git_repository_workdir@Base 0.17.0 + git_repository_wrap_odb@Base 0.18.0 + git_reset@Base 0.18.0 + git_reset_default@Base 0.18.0 + git_revert@Base 0.21.0 + git_revert_commit@Base 0.22.0 + git_revert_init_options@Base 0.21.0 + git_revparse@Base 0.18.0 + git_revparse_ext@Base 0.19.0 + git_revparse_single@Base 0.18.0 + git_revwalk_add_hide_cb@Base 0.21.0 + git_revwalk_free@Base 0.17.0 + git_revwalk_hide@Base 0.17.0 + git_revwalk_hide_glob@Base 0.17.0 + git_revwalk_hide_head@Base 0.17.0 + git_revwalk_hide_ref@Base 0.17.0 + git_revwalk_new@Base 0.17.0 + git_revwalk_next@Base 0.17.0 + git_revwalk_push@Base 0.17.0 + git_revwalk_push_glob@Base 0.17.0 + git_revwalk_push_head@Base 0.17.0 + git_revwalk_push_range@Base 0.18.0 + git_revwalk_push_ref@Base 0.17.0 + git_revwalk_repository@Base 0.17.0 + git_revwalk_reset@Base 0.17.0 + git_revwalk_simplify_first_parent@Base 0.20.0 + git_revwalk_sorting@Base 0.17.0 + git_signature_default@Base 0.20.0 + git_signature_dup@Base 0.17.0 + git_signature_free@Base 0.17.0 + git_signature_new@Base 0.17.0 + git_signature_now@Base 0.17.0 + git_smart_subtransport_git@Base 0.18.0 + git_smart_subtransport_http@Base 0.18.0 + git_smart_subtransport_ssh@Base 0.20.0 + git_stash_drop@Base 0.18.0 + git_stash_foreach@Base 0.18.0 + git_stash_save@Base 0.18.0 + git_status_byindex@Base 0.19.0 + git_status_file@Base 0.17.0 + git_status_foreach@Base 0.17.0 + git_status_foreach_ext@Base 0.17.0 + git_status_init_options@Base 0.21.0 + git_status_list_entrycount@Base 0.19.0 + git_status_list_free@Base 0.19.0 + git_status_list_get_perfdata@Base 0.21.0 + git_status_list_new@Base 0.19.0 + git_status_should_ignore@Base 0.17.0 + git_strarray_copy@Base 0.17.0 + git_strarray_free@Base 0.17.0 + git_submodule_add_finalize@Base 0.18.0 + git_submodule_add_setup@Base 0.18.0 + git_submodule_add_to_index@Base 0.18.0 + git_submodule_branch@Base 0.21.0 + git_submodule_fetch_recurse_submodules@Base 0.18.0 + git_submodule_foreach@Base 0.17.0 + git_submodule_free@Base 0.21.0 + git_submodule_head_id@Base 0.18.0 + git_submodule_ignore@Base 0.18.0 + git_submodule_index_id@Base 0.18.0 + git_submodule_init@Base 0.18.0 + git_submodule_location@Base 0.18.0 + git_submodule_lookup@Base 0.17.0 + git_submodule_name@Base 0.18.0 + git_submodule_open@Base 0.18.0 + git_submodule_owner@Base 0.18.0 + git_submodule_path@Base 0.18.0 + git_submodule_reload@Base 0.18.0 + git_submodule_reload_all@Base 0.18.0 + git_submodule_repo_init@Base 0.22.0 + git_submodule_resolve_url@Base 0.21.0 + git_submodule_save@Base 0.18.0 + git_submodule_set_fetch_recurse_submodules@Base 0.18.0 + git_submodule_set_ignore@Base 0.18.0 + git_submodule_set_update@Base 0.18.0 + git_submodule_set_url@Base 0.18.0 + git_submodule_status@Base 0.18.0 + git_submodule_sync@Base 0.18.0 + git_submodule_update@Base 0.18.0 + git_submodule_update_init_options@Base 0.22.0 + git_submodule_update_strategy@Base 0.22.0 + git_submodule_url@Base 0.18.0 + git_submodule_wd_id@Base 0.18.0 + git_tag_annotation_create@Base 0.19.0 + git_tag_create@Base 0.17.0 + git_tag_create_frombuffer@Base 0.17.0 + git_tag_create_lightweight@Base 0.17.0 + git_tag_delete@Base 0.17.0 + git_tag_foreach@Base 0.18.0 + git_tag_free@Base 0.19.0 + git_tag_id@Base 0.17.0 + git_tag_list@Base 0.17.0 + git_tag_list_match@Base 0.17.0 + git_tag_lookup@Base 0.19.0 + git_tag_lookup_prefix@Base 0.19.0 + git_tag_message@Base 0.17.0 + git_tag_name@Base 0.17.0 + git_tag_owner@Base 0.19.0 + git_tag_peel@Base 0.17.0 + git_tag_tagger@Base 0.17.0 + git_tag_target@Base 0.17.0 + git_tag_target_id@Base 0.18.0 + git_tag_target_type@Base 0.18.0 + git_trace_set@Base 0.18.0 + git_transport_init@Base 0.21.0 + git_transport_local@Base 0.18.0 + git_transport_new@Base 0.18.0 + git_transport_register@Base 0.20.0 + git_transport_smart@Base 0.18.0 + git_transport_ssh_with_paths@Base 0.22.0 + git_transport_unregister@Base 0.20.0 + git_tree_entry_byid@Base 0.21.0 + git_tree_entry_byindex@Base 0.17.0 + git_tree_entry_byname@Base 0.17.0 + git_tree_entry_bypath@Base 0.18.0 + git_tree_entry_cmp@Base 0.18.0 + git_tree_entry_dup@Base 0.18.0 + git_tree_entry_filemode@Base 0.18.0 + git_tree_entry_filemode_raw@Base 0.20.0 + git_tree_entry_free@Base 0.18.0 + git_tree_entry_id@Base 0.17.0 + git_tree_entry_name@Base 0.17.0 + git_tree_entry_to_object@Base 0.17.0 + git_tree_entry_type@Base 0.17.0 + git_tree_entrycount@Base 0.17.0 + git_tree_free@Base 0.19.0 + git_tree_id@Base 0.17.0 + git_tree_lookup@Base 0.19.0 + git_tree_lookup_prefix@Base 0.19.0 + git_tree_owner@Base 0.18.0 + git_tree_walk@Base 0.17.0 + git_treebuilder_clear@Base 0.17.0 + git_treebuilder_entrycount@Base 0.18.0 + git_treebuilder_filter@Base 0.17.0 + git_treebuilder_free@Base 0.17.0 + git_treebuilder_get@Base 0.17.0 + git_treebuilder_insert@Base 0.17.0 + git_treebuilder_new@Base 0.22.0 + git_treebuilder_remove@Base 0.17.0 + git_treebuilder_write@Base 0.17.0 + giterr_clear@Base 0.17.0 + giterr_detach@Base 0.20.0 + giterr_last@Base 0.17.0 + giterr_set_oom@Base 0.18.0 + giterr_set_str@Base 0.18.0 diff -Nru libgit2-0.20.0/debian/libgit2-dbg.lintian-overrides libgit2-0.22.2/debian/libgit2-dbg.lintian-overrides --- libgit2-0.20.0/debian/libgit2-dbg.lintian-overrides 2014-03-21 12:21:36.000000000 +0000 +++ libgit2-0.22.2/debian/libgit2-dbg.lintian-overrides 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -libgit2-dbg: no-upstream-changelog diff -Nru libgit2-0.20.0/debian/libgit2-dev.lintian-overrides libgit2-0.22.2/debian/libgit2-dev.lintian-overrides --- libgit2-0.20.0/debian/libgit2-dev.lintian-overrides 2014-03-21 12:21:36.000000000 +0000 +++ libgit2-0.22.2/debian/libgit2-dev.lintian-overrides 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -libgit2-dev: no-upstream-changelog diff -Nru libgit2-0.20.0/debian/patches/disable_tests.patch libgit2-0.22.2/debian/patches/disable_tests.patch --- libgit2-0.20.0/debian/patches/disable_tests.patch 2014-03-24 20:47:35.000000000 +0000 +++ libgit2-0.22.2/debian/patches/disable_tests.patch 2015-07-19 13:53:06.000000000 +0000 @@ -14,7 +14,7 @@ ENABLE_TESTING() - ADD_TEST(libgit2_clar libgit2_clar -ionline) -+ ADD_TEST(libgit2_clar libgit2_clar -xonline -xblame::simple::trivial_libgit2) ++ ADD_TEST(libgit2_clar libgit2_clar -xonline -xblame::simple::trivial_libgit2 -xnetwork::remote::remotes::single_branch -xnetwork::remote::remotes::restricted_refspecs) ENDIF () IF (TAGS) diff -Nru libgit2-0.20.0/debian/rules libgit2-0.22.2/debian/rules --- libgit2-0.20.0/debian/rules 2014-03-24 20:47:35.000000000 +0000 +++ libgit2-0.22.2/debian/rules 2015-07-19 13:53:06.000000000 +0000 @@ -9,6 +9,7 @@ #export DH_VERBOSE=1 DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) +TEST_TMPDIR := $(CURDIR)/tmp-test override_dh_auto_configure: dh_auto_configure --builddirectory=build-debian-release -- \ @@ -28,14 +29,20 @@ override_dh_makeshlibs: dh_makeshlibs -V -override_dh_auto_build : +override_dh_auto_build: dh_auto_build --builddirectory=build-debian-release dh_auto_build --builddirectory=build-debian-devel -override_dh_auto_install : +override_dh_auto_install: dh_auto_install --builddirectory=build-debian-release dh_auto_install --builddirectory=build-debian-devel +override_dh_auto_test: + mkdir -p build-debian-release/tmp + TMPDIR=$(CURDIR)/build-debian-release/tmp dh_auto_test --builddirectory=build-debian-release + mkdir -p build-debian-devel/tmp + TMPDIR=$(CURDIR)/build-debian-devel/tmp dh_auto_test --builddirectory=build-debian-devel + override_dh_strip: dh_strip --dbg-package=libgit2-dbg diff -Nru libgit2-0.20.0/deps/http-parser/http_parser.h libgit2-0.22.2/deps/http-parser/http_parser.h --- libgit2-0.20.0/deps/http-parser/http_parser.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/deps/http-parser/http_parser.h 2015-03-24 16:10:45.000000000 +0000 @@ -40,6 +40,8 @@ typedef unsigned __int64 uint64_t; typedef SIZE_T size_t; typedef SSIZE_T ssize_t; +#elif defined(__sun) || defined(__sun__) +#include #else #include #endif diff -Nru libgit2-0.20.0/deps/regex/regex.c libgit2-0.22.2/deps/regex/regex.c --- libgit2-0.20.0/deps/regex/regex.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/deps/regex/regex.c 2015-03-24 16:10:45.000000000 +0000 @@ -67,10 +67,17 @@ #include "regex_internal.h" #include "regex_internal.c" + #ifdef GAWK -#define bool int -#define true (1) -#define false (0) +# define bool int + +# ifndef true +# define true (1) +# endif + +# ifndef false +# define false (0) +# endif #endif #include "regcomp.c" #include "regexec.c" diff -Nru libgit2-0.20.0/deps/zlib/adler32.c libgit2-0.22.2/deps/zlib/adler32.c --- libgit2-0.20.0/deps/zlib/adler32.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/deps/zlib/adler32.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,5 +1,5 @@ /* adler32.c -- compute the Adler-32 checksum of a data stream - * Copyright (C) 1995-2007 Mark Adler + * Copyright (C) 1995-2011 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -9,9 +9,9 @@ #define local static -local uLong adler32_combine_(uLong adler1, uLong adler2, z_off64_t len2); +local uLong adler32_combine_ OF((uLong adler1, uLong adler2, z_off64_t len2)); -#define BASE 65521UL /* largest prime smaller than 65536 */ +#define BASE 65521 /* largest prime smaller than 65536 */ #define NMAX 5552 /* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */ @@ -21,39 +21,44 @@ #define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); #define DO16(buf) DO8(buf,0); DO8(buf,8); -/* use NO_DIVIDE if your processor does not do division in hardware */ +/* use NO_DIVIDE if your processor does not do division in hardware -- + try it both ways to see which is faster */ #ifdef NO_DIVIDE -# define MOD(a) \ +/* note that this assumes BASE is 65521, where 65536 % 65521 == 15 + (thank you to John Reiser for pointing this out) */ +# define CHOP(a) \ + do { \ + unsigned long tmp = a >> 16; \ + a &= 0xffffUL; \ + a += (tmp << 4) - tmp; \ + } while (0) +# define MOD28(a) \ do { \ - if (a >= (BASE << 16)) a -= (BASE << 16); \ - if (a >= (BASE << 15)) a -= (BASE << 15); \ - if (a >= (BASE << 14)) a -= (BASE << 14); \ - if (a >= (BASE << 13)) a -= (BASE << 13); \ - if (a >= (BASE << 12)) a -= (BASE << 12); \ - if (a >= (BASE << 11)) a -= (BASE << 11); \ - if (a >= (BASE << 10)) a -= (BASE << 10); \ - if (a >= (BASE << 9)) a -= (BASE << 9); \ - if (a >= (BASE << 8)) a -= (BASE << 8); \ - if (a >= (BASE << 7)) a -= (BASE << 7); \ - if (a >= (BASE << 6)) a -= (BASE << 6); \ - if (a >= (BASE << 5)) a -= (BASE << 5); \ - if (a >= (BASE << 4)) a -= (BASE << 4); \ - if (a >= (BASE << 3)) a -= (BASE << 3); \ - if (a >= (BASE << 2)) a -= (BASE << 2); \ - if (a >= (BASE << 1)) a -= (BASE << 1); \ + CHOP(a); \ if (a >= BASE) a -= BASE; \ } while (0) -# define MOD4(a) \ +# define MOD(a) \ do { \ - if (a >= (BASE << 4)) a -= (BASE << 4); \ - if (a >= (BASE << 3)) a -= (BASE << 3); \ - if (a >= (BASE << 2)) a -= (BASE << 2); \ - if (a >= (BASE << 1)) a -= (BASE << 1); \ + CHOP(a); \ + MOD28(a); \ + } while (0) +# define MOD63(a) \ + do { /* this assumes a is not negative */ \ + z_off64_t tmp = a >> 32; \ + a &= 0xffffffffL; \ + a += (tmp << 8) - (tmp << 5) + tmp; \ + tmp = a >> 16; \ + a &= 0xffffL; \ + a += (tmp << 4) - tmp; \ + tmp = a >> 16; \ + a &= 0xffffL; \ + a += (tmp << 4) - tmp; \ if (a >= BASE) a -= BASE; \ } while (0) #else # define MOD(a) a %= BASE -# define MOD4(a) a %= BASE +# define MOD28(a) a %= BASE +# define MOD63(a) a %= BASE #endif /* ========================================================================= */ @@ -92,7 +97,7 @@ } if (adler >= BASE) adler -= BASE; - MOD4(sum2); /* only added so many BASE's */ + MOD28(sum2); /* only added so many BASE's */ return adler | (sum2 << 16); } @@ -137,8 +142,13 @@ unsigned long sum2; unsigned rem; + /* for negative len, return invalid adler32 as a clue for debugging */ + if (len2 < 0) + return 0xffffffffUL; + /* the derivation of this formula is left as an exercise for the reader */ - rem = (unsigned)(len2 % BASE); + MOD63(len2); /* assumes len2 >= 0 */ + rem = (unsigned)len2; sum1 = adler1 & 0xffff; sum2 = rem * sum1; MOD(sum2); diff -Nru libgit2-0.20.0/deps/zlib/crc32.c libgit2-0.22.2/deps/zlib/crc32.c --- libgit2-0.20.0/deps/zlib/crc32.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/deps/zlib/crc32.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,5 +1,5 @@ /* crc32.c -- compute the CRC-32 of a data stream - * Copyright (C) 1995-2006, 2010 Mark Adler + * Copyright (C) 1995-2006, 2010, 2011, 2012 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h * * Thanks to Rodney Brown for his contribution of faster @@ -17,6 +17,8 @@ of the crc tables. Therefore, if you #define DYNAMIC_CRC_TABLE, you should first call get_crc_table() to initialize the tables before allowing more than one thread to use crc32(). + + DYNAMIC_CRC_TABLE and MAKECRCH can be #defined to write out crc32.h. */ #ifdef MAKECRCH @@ -30,31 +32,11 @@ #define local static -/* Find a four-byte integer type for crc32_little() and crc32_big(). */ -#ifndef NOBYFOUR -# ifdef STDC /* need ANSI C limits.h to determine sizes */ -# include -# define BYFOUR -# if (UINT_MAX == 0xffffffffUL) - typedef unsigned int u4; -# else -# if (ULONG_MAX == 0xffffffffUL) - typedef unsigned long u4; -# else -# if (USHRT_MAX == 0xffffffffUL) - typedef unsigned short u4; -# else -# undef BYFOUR /* can't find a four-byte integer type! */ -# endif -# endif -# endif -# endif /* STDC */ -#endif /* !NOBYFOUR */ - /* Definitions for doing the crc four data bytes at a time. */ +#if !defined(NOBYFOUR) && defined(Z_U4) +# define BYFOUR +#endif #ifdef BYFOUR -# define REV(w) ((((w)>>24)&0xff)+(((w)>>8)&0xff00)+ \ - (((w)&0xff00)<<8)+(((w)&0xff)<<24)) local unsigned long crc32_little OF((unsigned long, const unsigned char FAR *, unsigned)); local unsigned long crc32_big OF((unsigned long, @@ -68,16 +50,16 @@ local unsigned long gf2_matrix_times OF((unsigned long *mat, unsigned long vec)); local void gf2_matrix_square OF((unsigned long *square, unsigned long *mat)); -local uLong crc32_combine_(uLong crc1, uLong crc2, z_off64_t len2); +local uLong crc32_combine_ OF((uLong crc1, uLong crc2, z_off64_t len2)); #ifdef DYNAMIC_CRC_TABLE local volatile int crc_table_empty = 1; -local unsigned long FAR crc_table[TBLS][256]; +local z_crc_t FAR crc_table[TBLS][256]; local void make_crc_table OF((void)); #ifdef MAKECRCH - local void write_table OF((FILE *, const unsigned long FAR *)); + local void write_table OF((FILE *, const z_crc_t FAR *)); #endif /* MAKECRCH */ /* Generate tables for a byte-wise 32-bit CRC calculation on the polynomial: @@ -107,9 +89,9 @@ */ local void make_crc_table() { - unsigned long c; + z_crc_t c; int n, k; - unsigned long poly; /* polynomial exclusive-or pattern */ + z_crc_t poly; /* polynomial exclusive-or pattern */ /* terms of polynomial defining this crc (except x^32): */ static volatile int first = 1; /* flag to limit concurrent making */ static const unsigned char p[] = {0,1,2,4,5,7,8,10,11,12,16,22,23,26}; @@ -121,13 +103,13 @@ first = 0; /* make exclusive-or pattern from polynomial (0xedb88320UL) */ - poly = 0UL; - for (n = 0; n < sizeof(p)/sizeof(unsigned char); n++) - poly |= 1UL << (31 - p[n]); + poly = 0; + for (n = 0; n < (int)(sizeof(p)/sizeof(unsigned char)); n++) + poly |= (z_crc_t)1 << (31 - p[n]); /* generate a crc for every 8-bit value */ for (n = 0; n < 256; n++) { - c = (unsigned long)n; + c = (z_crc_t)n; for (k = 0; k < 8; k++) c = c & 1 ? poly ^ (c >> 1) : c >> 1; crc_table[0][n] = c; @@ -138,11 +120,11 @@ and then the byte reversal of those as well as the first table */ for (n = 0; n < 256; n++) { c = crc_table[0][n]; - crc_table[4][n] = REV(c); + crc_table[4][n] = ZSWAP32(c); for (k = 1; k < 4; k++) { c = crc_table[0][c & 0xff] ^ (c >> 8); crc_table[k][n] = c; - crc_table[k + 4][n] = REV(c); + crc_table[k + 4][n] = ZSWAP32(c); } } #endif /* BYFOUR */ @@ -164,7 +146,7 @@ if (out == NULL) return; fprintf(out, "/* crc32.h -- tables for rapid CRC calculation\n"); fprintf(out, " * Generated automatically by crc32.c\n */\n\n"); - fprintf(out, "local const unsigned long FAR "); + fprintf(out, "local const z_crc_t FAR "); fprintf(out, "crc_table[TBLS][256] =\n{\n {\n"); write_table(out, crc_table[0]); # ifdef BYFOUR @@ -184,12 +166,13 @@ #ifdef MAKECRCH local void write_table(out, table) FILE *out; - const unsigned long FAR *table; + const z_crc_t FAR *table; { int n; for (n = 0; n < 256; n++) - fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", table[n], + fprintf(out, "%s0x%08lxUL%s", n % 5 ? "" : " ", + (unsigned long)(table[n]), n == 255 ? "\n" : (n % 5 == 4 ? ",\n" : ", ")); } #endif /* MAKECRCH */ @@ -204,13 +187,13 @@ /* ========================================================================= * This function can be used by asm versions of crc32() */ -const unsigned long FAR * ZEXPORT get_crc_table() +const z_crc_t FAR * ZEXPORT get_crc_table() { #ifdef DYNAMIC_CRC_TABLE if (crc_table_empty) make_crc_table(); #endif /* DYNAMIC_CRC_TABLE */ - return (const unsigned long FAR *)crc_table; + return (const z_crc_t FAR *)crc_table; } /* ========================================================================= */ @@ -232,7 +215,7 @@ #ifdef BYFOUR if (sizeof(void *) == sizeof(ptrdiff_t)) { - u4 endian; + z_crc_t endian; endian = 1; if (*((unsigned char *)(&endian))) @@ -266,17 +249,17 @@ const unsigned char FAR *buf; unsigned len; { - register u4 c; - register const u4 FAR *buf4; + register z_crc_t c; + register const z_crc_t FAR *buf4; - c = (u4)crc; + c = (z_crc_t)crc; c = ~c; while (len && ((ptrdiff_t)buf & 3)) { c = crc_table[0][(c ^ *buf++) & 0xff] ^ (c >> 8); len--; } - buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4 = (const z_crc_t FAR *)(const void FAR *)buf; while (len >= 32) { DOLIT32; len -= 32; @@ -306,17 +289,17 @@ const unsigned char FAR *buf; unsigned len; { - register u4 c; - register const u4 FAR *buf4; + register z_crc_t c; + register const z_crc_t FAR *buf4; - c = REV((u4)crc); + c = ZSWAP32((z_crc_t)crc); c = ~c; while (len && ((ptrdiff_t)buf & 3)) { c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); len--; } - buf4 = (const u4 FAR *)(const void FAR *)buf; + buf4 = (const z_crc_t FAR *)(const void FAR *)buf; buf4--; while (len >= 32) { DOBIG32; @@ -333,7 +316,7 @@ c = crc_table[4][(c >> 24) ^ *buf++] ^ (c << 8); } while (--len); c = ~c; - return (unsigned long)(REV(c)); + return (unsigned long)(ZSWAP32(c)); } #endif /* BYFOUR */ diff -Nru libgit2-0.20.0/deps/zlib/crc32.h libgit2-0.22.2/deps/zlib/crc32.h --- libgit2-0.20.0/deps/zlib/crc32.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/deps/zlib/crc32.h 2015-03-24 16:10:45.000000000 +0000 @@ -2,7 +2,7 @@ * Generated automatically by crc32.c */ -local const unsigned long FAR crc_table[TBLS][256] = +local const z_crc_t FAR crc_table[TBLS][256] = { { 0x00000000UL, 0x77073096UL, 0xee0e612cUL, 0x990951baUL, 0x076dc419UL, diff -Nru libgit2-0.20.0/deps/zlib/deflate.c libgit2-0.22.2/deps/zlib/deflate.c --- libgit2-0.20.0/deps/zlib/deflate.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/deps/zlib/deflate.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,5 +1,5 @@ /* deflate.c -- compress data using the deflation algorithm - * Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler + * Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -37,7 +37,7 @@ * REFERENCES * * Deutsch, L.P.,"DEFLATE Compressed Data Format Specification". - * Available in http://www.ietf.org/rfc/rfc1951.txt + * Available in http://tools.ietf.org/html/rfc1951 * * A description of the Rabin and Karp algorithm is given in the book * "Algorithms" by R. Sedgewick, Addison-Wesley, p252. @@ -52,7 +52,7 @@ #include "deflate.h" const char deflate_copyright[] = - " deflate 1.2.5 Copyright 1995-2010 Jean-loup Gailly and Mark Adler "; + " deflate 1.2.8 Copyright 1995-2013 Jean-loup Gailly and Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -155,6 +155,9 @@ struct static_tree_desc_s {int dummy;}; /* for buggy compilers */ #endif +/* rank Z_BLOCK between Z_NO_FLUSH and Z_PARTIAL_FLUSH */ +#define RANK(f) (((f) << 1) - ((f) > 4 ? 9 : 0)) + /* =========================================================================== * Update a hash value with the given input byte * IN assertion: all calls to to UPDATE_HASH are made with consecutive @@ -235,10 +238,19 @@ strm->msg = Z_NULL; if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else strm->zalloc = zcalloc; strm->opaque = (voidpf)0; +#endif } - if (strm->zfree == (free_func)0) strm->zfree = zcfree; + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif #ifdef FASTEST if (level != 0) level = 1; @@ -293,7 +305,7 @@ if (s->window == Z_NULL || s->prev == Z_NULL || s->head == Z_NULL || s->pending_buf == Z_NULL) { s->status = FINISH_STATE; - strm->msg = (char*)ERR_MSG(Z_MEM_ERROR); + strm->msg = ERR_MSG(Z_MEM_ERROR); deflateEnd (strm); return Z_MEM_ERROR; } @@ -314,43 +326,70 @@ uInt dictLength; { deflate_state *s; - uInt length = dictLength; - uInt n; - IPos hash_head = 0; - - if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL || - strm->state->wrap == 2 || - (strm->state->wrap == 1 && strm->state->status != INIT_STATE)) - return Z_STREAM_ERROR; + uInt str, n; + int wrap; + unsigned avail; + z_const unsigned char *next; + if (strm == Z_NULL || strm->state == Z_NULL || dictionary == Z_NULL) + return Z_STREAM_ERROR; s = strm->state; - if (s->wrap) + wrap = s->wrap; + if (wrap == 2 || (wrap == 1 && s->status != INIT_STATE) || s->lookahead) + return Z_STREAM_ERROR; + + /* when using zlib wrappers, compute Adler-32 for provided dictionary */ + if (wrap == 1) strm->adler = adler32(strm->adler, dictionary, dictLength); + s->wrap = 0; /* avoid computing Adler-32 in read_buf */ - if (length < MIN_MATCH) return Z_OK; - if (length > s->w_size) { - length = s->w_size; - dictionary += dictLength - length; /* use the tail of the dictionary */ - } - zmemcpy(s->window, dictionary, length); - s->strstart = length; - s->block_start = (long)length; - - /* Insert all strings in the hash table (except for the last two bytes). - * s->lookahead stays null, so s->ins_h will be recomputed at the next - * call of fill_window. - */ - s->ins_h = s->window[0]; - UPDATE_HASH(s, s->ins_h, s->window[1]); - for (n = 0; n <= length - MIN_MATCH; n++) { - INSERT_STRING(s, n, hash_head); - } - if (hash_head) hash_head = 0; /* to make compiler happy */ + /* if dictionary would fill window, just replace the history */ + if (dictLength >= s->w_size) { + if (wrap == 0) { /* already empty otherwise */ + CLEAR_HASH(s); + s->strstart = 0; + s->block_start = 0L; + s->insert = 0; + } + dictionary += dictLength - s->w_size; /* use the tail */ + dictLength = s->w_size; + } + + /* insert dictionary into window and hash */ + avail = strm->avail_in; + next = strm->next_in; + strm->avail_in = dictLength; + strm->next_in = (z_const Bytef *)dictionary; + fill_window(s); + while (s->lookahead >= MIN_MATCH) { + str = s->strstart; + n = s->lookahead - (MIN_MATCH-1); + do { + UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); +#ifndef FASTEST + s->prev[str & s->w_mask] = s->head[s->ins_h]; +#endif + s->head[s->ins_h] = (Pos)str; + str++; + } while (--n); + s->strstart = str; + s->lookahead = MIN_MATCH-1; + fill_window(s); + } + s->strstart += s->lookahead; + s->block_start = (long)s->strstart; + s->insert = s->lookahead; + s->lookahead = 0; + s->match_length = s->prev_length = MIN_MATCH-1; + s->match_available = 0; + strm->next_in = next; + strm->avail_in = avail; + s->wrap = wrap; return Z_OK; } /* ========================================================================= */ -int ZEXPORT deflateReset (strm) +int ZEXPORT deflateResetKeep (strm) z_streamp strm; { deflate_state *s; @@ -380,12 +419,23 @@ s->last_flush = Z_NO_FLUSH; _tr_init(s); - lm_init(s); return Z_OK; } /* ========================================================================= */ +int ZEXPORT deflateReset (strm) + z_streamp strm; +{ + int ret; + + ret = deflateResetKeep(strm); + if (ret == Z_OK) + lm_init(strm->state); + return ret; +} + +/* ========================================================================= */ int ZEXPORT deflateSetHeader (strm, head) z_streamp strm; gz_headerp head; @@ -397,14 +447,42 @@ } /* ========================================================================= */ +int ZEXPORT deflatePending (strm, pending, bits) + unsigned *pending; + int *bits; + z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + if (pending != Z_NULL) + *pending = strm->state->pending; + if (bits != Z_NULL) + *bits = strm->state->bi_valid; + return Z_OK; +} + +/* ========================================================================= */ int ZEXPORT deflatePrime (strm, bits, value) z_streamp strm; int bits; int value; { + deflate_state *s; + int put; + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; - strm->state->bi_valid = bits; - strm->state->bi_buf = (ush)(value & ((1 << bits) - 1)); + s = strm->state; + if ((Bytef *)(s->d_buf) < s->pending_out + ((Buf_size + 7) >> 3)) + return Z_BUF_ERROR; + do { + put = Buf_size - s->bi_valid; + if (put > bits) + put = bits; + s->bi_buf |= (ush)((value & ((1 << put) - 1)) << s->bi_valid); + s->bi_valid += put; + _tr_flush_bits(s); + value >>= put; + bits -= put; + } while (bits); return Z_OK; } @@ -435,6 +513,8 @@ strm->total_in != 0) { /* Flush the last buffer: */ err = deflate(strm, Z_BLOCK); + if (err == Z_BUF_ERROR && s->pending == 0) + err = Z_OK; } if (s->level != level) { s->level = level; @@ -562,19 +642,22 @@ local void flush_pending(strm) z_streamp strm; { - unsigned len = strm->state->pending; + unsigned len; + deflate_state *s = strm->state; + _tr_flush_bits(s); + len = s->pending; if (len > strm->avail_out) len = strm->avail_out; if (len == 0) return; - zmemcpy(strm->next_out, strm->state->pending_out, len); + zmemcpy(strm->next_out, s->pending_out, len); strm->next_out += len; - strm->state->pending_out += len; + s->pending_out += len; strm->total_out += len; strm->avail_out -= len; - strm->state->pending -= len; - if (strm->state->pending == 0) { - strm->state->pending_out = strm->state->pending_buf; + s->pending -= len; + if (s->pending == 0) { + s->pending_out = s->pending_buf; } } @@ -801,7 +884,7 @@ * flushes. For repeated and useless calls with Z_FINISH, we keep * returning Z_STREAM_END instead of Z_BUF_ERROR. */ - } else if (strm->avail_in == 0 && flush <= old_flush && + } else if (strm->avail_in == 0 && RANK(flush) <= RANK(old_flush) && flush != Z_FINISH) { ERR_RETURN(strm, Z_BUF_ERROR); } @@ -850,6 +933,7 @@ if (s->lookahead == 0) { s->strstart = 0; s->block_start = 0L; + s->insert = 0; } } } @@ -945,12 +1029,12 @@ ss = source->state; - zmemcpy(dest, source, sizeof(z_stream)); + zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); ds = (deflate_state *) ZALLOC(dest, 1, sizeof(deflate_state)); if (ds == Z_NULL) return Z_MEM_ERROR; dest->state = (struct internal_state FAR *) ds; - zmemcpy(ds, ss, sizeof(deflate_state)); + zmemcpy((voidpf)ds, (voidpf)ss, sizeof(deflate_state)); ds->strm = dest; ds->window = (Bytef *) ZALLOC(dest, ds->w_size, 2*sizeof(Byte)); @@ -966,8 +1050,8 @@ } /* following zmemcpy do not work for 16-bit MSDOS */ zmemcpy(ds->window, ss->window, ds->w_size * 2 * sizeof(Byte)); - zmemcpy(ds->prev, ss->prev, ds->w_size * sizeof(Pos)); - zmemcpy(ds->head, ss->head, ds->hash_size * sizeof(Pos)); + zmemcpy((voidpf)ds->prev, (voidpf)ss->prev, ds->w_size * sizeof(Pos)); + zmemcpy((voidpf)ds->head, (voidpf)ss->head, ds->hash_size * sizeof(Pos)); zmemcpy(ds->pending_buf, ss->pending_buf, (uInt)ds->pending_buf_size); ds->pending_out = ds->pending_buf + (ss->pending_out - ss->pending_buf); @@ -1001,15 +1085,15 @@ strm->avail_in -= len; + zmemcpy(buf, strm->next_in, len); if (strm->state->wrap == 1) { - strm->adler = adler32(strm->adler, strm->next_in, len); + strm->adler = adler32(strm->adler, buf, len); } #ifdef GZIP else if (strm->state->wrap == 2) { - strm->adler = crc32(strm->adler, strm->next_in, len); + strm->adler = crc32(strm->adler, buf, len); } #endif - zmemcpy(buf, strm->next_in, len); strm->next_in += len; strm->total_in += len; @@ -1036,6 +1120,7 @@ s->strstart = 0; s->block_start = 0L; s->lookahead = 0; + s->insert = 0; s->match_length = s->prev_length = MIN_MATCH-1; s->match_available = 0; s->ins_h = 0; @@ -1310,6 +1395,8 @@ unsigned more; /* Amount of free space at the end of the window. */ uInt wsize = s->w_size; + Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); + do { more = (unsigned)(s->window_size -(ulg)s->lookahead -(ulg)s->strstart); @@ -1362,7 +1449,7 @@ #endif more += wsize; } - if (s->strm->avail_in == 0) return; + if (s->strm->avail_in == 0) break; /* If there was no sliding: * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && @@ -1381,12 +1468,24 @@ s->lookahead += n; /* Initialize the hash value now that we have some input: */ - if (s->lookahead >= MIN_MATCH) { - s->ins_h = s->window[s->strstart]; - UPDATE_HASH(s, s->ins_h, s->window[s->strstart+1]); + if (s->lookahead + s->insert >= MIN_MATCH) { + uInt str = s->strstart - s->insert; + s->ins_h = s->window[str]; + UPDATE_HASH(s, s->ins_h, s->window[str + 1]); #if MIN_MATCH != 3 Call UPDATE_HASH() MIN_MATCH-3 more times #endif + while (s->insert) { + UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); +#ifndef FASTEST + s->prev[str & s->w_mask] = s->head[s->ins_h]; +#endif + s->head[s->ins_h] = (Pos)str; + str++; + s->insert--; + if (s->lookahead + s->insert < MIN_MATCH) + break; + } } /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, * but this is not important since only literal bytes will be emitted. @@ -1427,6 +1526,9 @@ s->high_water += init; } } + + Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, + "not enough room for search"); } /* =========================================================================== @@ -1506,8 +1608,14 @@ FLUSH_BLOCK(s, 0); } } - FLUSH_BLOCK(s, flush == Z_FINISH); - return flush == Z_FINISH ? finish_done : block_done; + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if ((long)s->strstart > s->block_start) + FLUSH_BLOCK(s, 0); + return block_done; } /* =========================================================================== @@ -1603,8 +1711,14 @@ } if (bflush) FLUSH_BLOCK(s, 0); } - FLUSH_BLOCK(s, flush == Z_FINISH); - return flush == Z_FINISH ? finish_done : block_done; + s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; } #ifndef FASTEST @@ -1728,8 +1842,14 @@ _tr_tally_lit(s, s->window[s->strstart-1], bflush); s->match_available = 0; } - FLUSH_BLOCK(s, flush == Z_FINISH); - return flush == Z_FINISH ? finish_done : block_done; + s->insert = s->strstart < MIN_MATCH-1 ? s->strstart : MIN_MATCH-1; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; } #endif /* FASTEST */ @@ -1749,11 +1869,11 @@ for (;;) { /* Make sure that we always have enough lookahead, except * at the end of the input file. We need MAX_MATCH bytes - * for the longest encodable run. + * for the longest run, plus one for the unrolled loop. */ - if (s->lookahead < MAX_MATCH) { + if (s->lookahead <= MAX_MATCH) { fill_window(s); - if (s->lookahead < MAX_MATCH && flush == Z_NO_FLUSH) { + if (s->lookahead <= MAX_MATCH && flush == Z_NO_FLUSH) { return need_more; } if (s->lookahead == 0) break; /* flush the current block */ @@ -1776,6 +1896,7 @@ if (s->match_length > s->lookahead) s->match_length = s->lookahead; } + Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); } /* Emit match if have run of MIN_MATCH or longer, else emit literal */ @@ -1796,8 +1917,14 @@ } if (bflush) FLUSH_BLOCK(s, 0); } - FLUSH_BLOCK(s, flush == Z_FINISH); - return flush == Z_FINISH ? finish_done : block_done; + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; } /* =========================================================================== @@ -1829,6 +1956,12 @@ s->strstart++; if (bflush) FLUSH_BLOCK(s, 0); } - FLUSH_BLOCK(s, flush == Z_FINISH); - return flush == Z_FINISH ? finish_done : block_done; + s->insert = 0; + if (flush == Z_FINISH) { + FLUSH_BLOCK(s, 1); + return finish_done; + } + if (s->last_lit) + FLUSH_BLOCK(s, 0); + return block_done; } diff -Nru libgit2-0.20.0/deps/zlib/deflate.h libgit2-0.22.2/deps/zlib/deflate.h --- libgit2-0.20.0/deps/zlib/deflate.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/deps/zlib/deflate.h 2015-03-24 16:10:45.000000000 +0000 @@ -1,5 +1,5 @@ /* deflate.h -- internal compression state - * Copyright (C) 1995-2010 Jean-loup Gailly + * Copyright (C) 1995-2012 Jean-loup Gailly * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -48,6 +48,9 @@ #define MAX_BITS 15 /* All codes must not exceed MAX_BITS bits */ +#define Buf_size 16 +/* size of bit buffer in bi_buf */ + #define INIT_STATE 42 #define EXTRA_STATE 69 #define NAME_STATE 73 @@ -101,7 +104,7 @@ int wrap; /* bit 0 true for zlib, bit 1 true for gzip */ gz_headerp gzhead; /* gzip header information to write */ uInt gzindex; /* where in extra, name, or comment */ - Byte method; /* STORED (for zip only) or DEFLATED */ + Byte method; /* can only be DEFLATED */ int last_flush; /* value of flush param for previous deflate call */ /* used by deflate.c: */ @@ -188,7 +191,7 @@ int nice_match; /* Stop searching when current match exceeds this */ /* used by trees.c: */ - /* Didn't use ct_data typedef below to supress compiler warning */ + /* Didn't use ct_data typedef below to suppress compiler warning */ struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ @@ -244,7 +247,7 @@ ulg opt_len; /* bit length of current block with optimal trees */ ulg static_len; /* bit length of current block with static trees */ uInt matches; /* number of string matches in current block */ - int last_eob_len; /* bit length of EOB code for last block */ + uInt insert; /* bytes at end of window left to insert */ #ifdef DEBUG ulg compressed_len; /* total bit length of compressed file mod 2^32 */ @@ -294,6 +297,7 @@ int ZLIB_INTERNAL _tr_tally OF((deflate_state *s, unsigned dist, unsigned lc)); void ZLIB_INTERNAL _tr_flush_block OF((deflate_state *s, charf *buf, ulg stored_len, int last)); +void ZLIB_INTERNAL _tr_flush_bits OF((deflate_state *s)); void ZLIB_INTERNAL _tr_align OF((deflate_state *s)); void ZLIB_INTERNAL _tr_stored_block OF((deflate_state *s, charf *buf, ulg stored_len, int last)); diff -Nru libgit2-0.20.0/deps/zlib/infback.c libgit2-0.22.2/deps/zlib/infback.c --- libgit2-0.20.0/deps/zlib/infback.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/deps/zlib/infback.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,640 @@ +/* infback.c -- inflate using a call-back interface + * Copyright (C) 1995-2011 Mark Adler + * For conditions of distribution and use, see copyright notice in zlib.h + */ + +/* + This code is largely copied from inflate.c. Normally either infback.o or + inflate.o would be linked into an application--not both. The interface + with inffast.c is retained so that optimized assembler-coded versions of + inflate_fast() can be used with either inflate.c or infback.c. + */ + +#include "zutil.h" +#include "inftrees.h" +#include "inflate.h" +#include "inffast.h" + +/* function prototypes */ +local void fixedtables OF((struct inflate_state FAR *state)); + +/* + strm provides memory allocation functions in zalloc and zfree, or + Z_NULL to use the library memory allocation functions. + + windowBits is in the range 8..15, and window is a user-supplied + window and output buffer that is 2**windowBits bytes. + */ +int ZEXPORT inflateBackInit_(strm, windowBits, window, version, stream_size) +z_streamp strm; +int windowBits; +unsigned char FAR *window; +const char *version; +int stream_size; +{ + struct inflate_state FAR *state; + + if (version == Z_NULL || version[0] != ZLIB_VERSION[0] || + stream_size != (int)(sizeof(z_stream))) + return Z_VERSION_ERROR; + if (strm == Z_NULL || window == Z_NULL || + windowBits < 8 || windowBits > 15) + return Z_STREAM_ERROR; + strm->msg = Z_NULL; /* in case we return an error */ + if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zalloc = zcalloc; + strm->opaque = (voidpf)0; +#endif + } + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif + state = (struct inflate_state FAR *)ZALLOC(strm, 1, + sizeof(struct inflate_state)); + if (state == Z_NULL) return Z_MEM_ERROR; + Tracev((stderr, "inflate: allocated\n")); + strm->state = (struct internal_state FAR *)state; + state->dmax = 32768U; + state->wbits = windowBits; + state->wsize = 1U << windowBits; + state->window = window; + state->wnext = 0; + state->whave = 0; + return Z_OK; +} + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +local void fixedtables(state) +struct inflate_state FAR *state; +{ +#ifdef BUILDFIXED + static int virgin = 1; + static code *lenfix, *distfix; + static code fixed[544]; + + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + unsigned sym, bits; + static code *next; + + /* literal/length table */ + sym = 0; + while (sym < 144) state->lens[sym++] = 8; + while (sym < 256) state->lens[sym++] = 9; + while (sym < 280) state->lens[sym++] = 7; + while (sym < 288) state->lens[sym++] = 8; + next = fixed; + lenfix = next; + bits = 9; + inflate_table(LENS, state->lens, 288, &(next), &(bits), state->work); + + /* distance table */ + sym = 0; + while (sym < 32) state->lens[sym++] = 5; + distfix = next; + bits = 5; + inflate_table(DISTS, state->lens, 32, &(next), &(bits), state->work); + + /* do this just once */ + virgin = 0; + } +#else /* !BUILDFIXED */ +# include "inffixed.h" +#endif /* BUILDFIXED */ + state->lencode = lenfix; + state->lenbits = 9; + state->distcode = distfix; + state->distbits = 5; +} + +/* Macros for inflateBack(): */ + +/* Load returned state from inflate_fast() */ +#define LOAD() \ + do { \ + put = strm->next_out; \ + left = strm->avail_out; \ + next = strm->next_in; \ + have = strm->avail_in; \ + hold = state->hold; \ + bits = state->bits; \ + } while (0) + +/* Set state from registers for inflate_fast() */ +#define RESTORE() \ + do { \ + strm->next_out = put; \ + strm->avail_out = left; \ + strm->next_in = next; \ + strm->avail_in = have; \ + state->hold = hold; \ + state->bits = bits; \ + } while (0) + +/* Clear the input bit accumulator */ +#define INITBITS() \ + do { \ + hold = 0; \ + bits = 0; \ + } while (0) + +/* Assure that some input is available. If input is requested, but denied, + then return a Z_BUF_ERROR from inflateBack(). */ +#define PULL() \ + do { \ + if (have == 0) { \ + have = in(in_desc, &next); \ + if (have == 0) { \ + next = Z_NULL; \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* Get a byte of input into the bit accumulator, or return from inflateBack() + with an error if there is no input available. */ +#define PULLBYTE() \ + do { \ + PULL(); \ + have--; \ + hold += (unsigned long)(*next++) << bits; \ + bits += 8; \ + } while (0) + +/* Assure that there are at least n bits in the bit accumulator. If there is + not enough available input to do that, then return from inflateBack() with + an error. */ +#define NEEDBITS(n) \ + do { \ + while (bits < (unsigned)(n)) \ + PULLBYTE(); \ + } while (0) + +/* Return the low n bits of the bit accumulator (n < 16) */ +#define BITS(n) \ + ((unsigned)hold & ((1U << (n)) - 1)) + +/* Remove n bits from the bit accumulator */ +#define DROPBITS(n) \ + do { \ + hold >>= (n); \ + bits -= (unsigned)(n); \ + } while (0) + +/* Remove zero to seven bits as needed to go to a byte boundary */ +#define BYTEBITS() \ + do { \ + hold >>= bits & 7; \ + bits -= bits & 7; \ + } while (0) + +/* Assure that some output space is available, by writing out the window + if it's full. If the write fails, return from inflateBack() with a + Z_BUF_ERROR. */ +#define ROOM() \ + do { \ + if (left == 0) { \ + put = state->window; \ + left = state->wsize; \ + state->whave = left; \ + if (out(out_desc, put, left)) { \ + ret = Z_BUF_ERROR; \ + goto inf_leave; \ + } \ + } \ + } while (0) + +/* + strm provides the memory allocation functions and window buffer on input, + and provides information on the unused input on return. For Z_DATA_ERROR + returns, strm will also provide an error message. + + in() and out() are the call-back input and output functions. When + inflateBack() needs more input, it calls in(). When inflateBack() has + filled the window with output, or when it completes with data in the + window, it calls out() to write out the data. The application must not + change the provided input until in() is called again or inflateBack() + returns. The application must not change the window/output buffer until + inflateBack() returns. + + in() and out() are called with a descriptor parameter provided in the + inflateBack() call. This parameter can be a structure that provides the + information required to do the read or write, as well as accumulated + information on the input and output such as totals and check values. + + in() should return zero on failure. out() should return non-zero on + failure. If either in() or out() fails, than inflateBack() returns a + Z_BUF_ERROR. strm->next_in can be checked for Z_NULL to see whether it + was in() or out() that caused in the error. Otherwise, inflateBack() + returns Z_STREAM_END on success, Z_DATA_ERROR for an deflate format + error, or Z_MEM_ERROR if it could not allocate memory for the state. + inflateBack() can also return Z_STREAM_ERROR if the input parameters + are not correct, i.e. strm is Z_NULL or the state was not initialized. + */ +int ZEXPORT inflateBack(strm, in, in_desc, out, out_desc) +z_streamp strm; +in_func in; +void FAR *in_desc; +out_func out; +void FAR *out_desc; +{ + struct inflate_state FAR *state; + z_const unsigned char FAR *next; /* next input */ + unsigned char FAR *put; /* next output */ + unsigned have, left; /* available input and output */ + unsigned long hold; /* bit buffer */ + unsigned bits; /* bits in bit buffer */ + unsigned copy; /* number of stored or match bytes to copy */ + unsigned char FAR *from; /* where to copy match bytes from */ + code here; /* current decoding table entry */ + code last; /* parent table entry */ + unsigned len; /* length to copy for repeats, bits to drop */ + int ret; /* return code */ + static const unsigned short order[19] = /* permutation of code lengths */ + {16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + + /* Check that the strm exists and that the state was initialized */ + if (strm == Z_NULL || strm->state == Z_NULL) + return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* Reset the state */ + strm->msg = Z_NULL; + state->mode = TYPE; + state->last = 0; + state->whave = 0; + next = strm->next_in; + have = next != Z_NULL ? strm->avail_in : 0; + hold = 0; + bits = 0; + put = state->window; + left = state->wsize; + + /* Inflate until end of block marked as last */ + for (;;) + switch (state->mode) { + case TYPE: + /* determine and dispatch block type */ + if (state->last) { + BYTEBITS(); + state->mode = DONE; + break; + } + NEEDBITS(3); + state->last = BITS(1); + DROPBITS(1); + switch (BITS(2)) { + case 0: /* stored block */ + Tracev((stderr, "inflate: stored block%s\n", + state->last ? " (last)" : "")); + state->mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + Tracev((stderr, "inflate: fixed codes block%s\n", + state->last ? " (last)" : "")); + state->mode = LEN; /* decode codes */ + break; + case 2: /* dynamic block */ + Tracev((stderr, "inflate: dynamic codes block%s\n", + state->last ? " (last)" : "")); + state->mode = TABLE; + break; + case 3: + strm->msg = (char *)"invalid block type"; + state->mode = BAD; + } + DROPBITS(2); + break; + + case STORED: + /* get and verify stored block length */ + BYTEBITS(); /* go to byte boundary */ + NEEDBITS(32); + if ((hold & 0xffff) != ((hold >> 16) ^ 0xffff)) { + strm->msg = (char *)"invalid stored block lengths"; + state->mode = BAD; + break; + } + state->length = (unsigned)hold & 0xffff; + Tracev((stderr, "inflate: stored length %u\n", + state->length)); + INITBITS(); + + /* copy stored block from input to output */ + while (state->length != 0) { + copy = state->length; + PULL(); + ROOM(); + if (copy > have) copy = have; + if (copy > left) copy = left; + zmemcpy(put, next, copy); + have -= copy; + next += copy; + left -= copy; + put += copy; + state->length -= copy; + } + Tracev((stderr, "inflate: stored end\n")); + state->mode = TYPE; + break; + + case TABLE: + /* get dynamic table entries descriptor */ + NEEDBITS(14); + state->nlen = BITS(5) + 257; + DROPBITS(5); + state->ndist = BITS(5) + 1; + DROPBITS(5); + state->ncode = BITS(4) + 4; + DROPBITS(4); +#ifndef PKZIP_BUG_WORKAROUND + if (state->nlen > 286 || state->ndist > 30) { + strm->msg = (char *)"too many length or distance symbols"; + state->mode = BAD; + break; + } +#endif + Tracev((stderr, "inflate: table sizes ok\n")); + + /* get code length code lengths (not a typo) */ + state->have = 0; + while (state->have < state->ncode) { + NEEDBITS(3); + state->lens[order[state->have++]] = (unsigned short)BITS(3); + DROPBITS(3); + } + while (state->have < 19) + state->lens[order[state->have++]] = 0; + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 7; + ret = inflate_table(CODES, state->lens, 19, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid code lengths set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: code lengths ok\n")); + + /* get length and distance code code lengths */ + state->have = 0; + while (state->have < state->nlen + state->ndist) { + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.val < 16) { + DROPBITS(here.bits); + state->lens[state->have++] = here.val; + } + else { + if (here.val == 16) { + NEEDBITS(here.bits + 2); + DROPBITS(here.bits); + if (state->have == 0) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + len = (unsigned)(state->lens[state->have - 1]); + copy = 3 + BITS(2); + DROPBITS(2); + } + else if (here.val == 17) { + NEEDBITS(here.bits + 3); + DROPBITS(here.bits); + len = 0; + copy = 3 + BITS(3); + DROPBITS(3); + } + else { + NEEDBITS(here.bits + 7); + DROPBITS(here.bits); + len = 0; + copy = 11 + BITS(7); + DROPBITS(7); + } + if (state->have + copy > state->nlen + state->ndist) { + strm->msg = (char *)"invalid bit length repeat"; + state->mode = BAD; + break; + } + while (copy--) + state->lens[state->have++] = (unsigned short)len; + } + } + + /* handle error breaks in while */ + if (state->mode == BAD) break; + + /* check for end-of-block code (better have one) */ + if (state->lens[256] == 0) { + strm->msg = (char *)"invalid code -- missing end-of-block"; + state->mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state->next = state->codes; + state->lencode = (code const FAR *)(state->next); + state->lenbits = 9; + ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), + &(state->lenbits), state->work); + if (ret) { + strm->msg = (char *)"invalid literal/lengths set"; + state->mode = BAD; + break; + } + state->distcode = (code const FAR *)(state->next); + state->distbits = 6; + ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, + &(state->next), &(state->distbits), state->work); + if (ret) { + strm->msg = (char *)"invalid distances set"; + state->mode = BAD; + break; + } + Tracev((stderr, "inflate: codes ok\n")); + state->mode = LEN; + + case LEN: + /* use inflate_fast() if we have enough input and output */ + if (have >= 6 && left >= 258) { + RESTORE(); + if (state->whave < state->wsize) + state->whave = state->wsize - left; + inflate_fast(strm, state->wsize); + LOAD(); + break; + } + + /* get a literal, length, or end-of-block code */ + for (;;) { + here = state->lencode[BITS(state->lenbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if (here.op && (here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->lencode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(here.bits); + state->length = (unsigned)here.val; + + /* process literal */ + if (here.op == 0) { + Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + "inflate: literal '%c'\n" : + "inflate: literal 0x%02x\n", here.val)); + ROOM(); + *put++ = (unsigned char)(state->length); + left--; + state->mode = LEN; + break; + } + + /* process end of block */ + if (here.op & 32) { + Tracevv((stderr, "inflate: end of block\n")); + state->mode = TYPE; + break; + } + + /* invalid code */ + if (here.op & 64) { + strm->msg = (char *)"invalid literal/length code"; + state->mode = BAD; + break; + } + + /* length code -- get extra bits, if any */ + state->extra = (unsigned)(here.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->length += BITS(state->extra); + DROPBITS(state->extra); + } + Tracevv((stderr, "inflate: length %u\n", state->length)); + + /* get distance code */ + for (;;) { + here = state->distcode[BITS(state->distbits)]; + if ((unsigned)(here.bits) <= bits) break; + PULLBYTE(); + } + if ((here.op & 0xf0) == 0) { + last = here; + for (;;) { + here = state->distcode[last.val + + (BITS(last.bits + last.op) >> last.bits)]; + if ((unsigned)(last.bits + here.bits) <= bits) break; + PULLBYTE(); + } + DROPBITS(last.bits); + } + DROPBITS(here.bits); + if (here.op & 64) { + strm->msg = (char *)"invalid distance code"; + state->mode = BAD; + break; + } + state->offset = (unsigned)here.val; + + /* get distance extra bits, if any */ + state->extra = (unsigned)(here.op) & 15; + if (state->extra != 0) { + NEEDBITS(state->extra); + state->offset += BITS(state->extra); + DROPBITS(state->extra); + } + if (state->offset > state->wsize - (state->whave < state->wsize ? + left : 0)) { + strm->msg = (char *)"invalid distance too far back"; + state->mode = BAD; + break; + } + Tracevv((stderr, "inflate: distance %u\n", state->offset)); + + /* copy match from window to output */ + do { + ROOM(); + copy = state->wsize - state->offset; + if (copy < left) { + from = put + copy; + copy = left - copy; + } + else { + from = put - state->offset; + copy = left; + } + if (copy > state->length) copy = state->length; + state->length -= copy; + left -= copy; + do { + *put++ = *from++; + } while (--copy); + } while (state->length != 0); + break; + + case DONE: + /* inflate stream terminated properly -- write leftover output */ + ret = Z_STREAM_END; + if (left < state->wsize) { + if (out(out_desc, state->window, state->wsize - left)) + ret = Z_BUF_ERROR; + } + goto inf_leave; + + case BAD: + ret = Z_DATA_ERROR; + goto inf_leave; + + default: /* can't happen, but makes compilers happy */ + ret = Z_STREAM_ERROR; + goto inf_leave; + } + + /* Return unused input */ + inf_leave: + strm->next_in = next; + strm->avail_in = have; + return ret; +} + +int ZEXPORT inflateBackEnd(strm) +z_streamp strm; +{ + if (strm == Z_NULL || strm->state == Z_NULL || strm->zfree == (free_func)0) + return Z_STREAM_ERROR; + ZFREE(strm, strm->state); + strm->state = Z_NULL; + Tracev((stderr, "inflate: end\n")); + return Z_OK; +} diff -Nru libgit2-0.20.0/deps/zlib/inffast.c libgit2-0.22.2/deps/zlib/inffast.c --- libgit2-0.20.0/deps/zlib/inffast.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/deps/zlib/inffast.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,5 +1,5 @@ /* inffast.c -- fast decoding - * Copyright (C) 1995-2008, 2010 Mark Adler + * Copyright (C) 1995-2008, 2010, 2013 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -69,8 +69,8 @@ unsigned start; /* inflate()'s starting value for strm->avail_out */ { struct inflate_state FAR *state; - unsigned char FAR *in; /* local strm->next_in */ - unsigned char FAR *last; /* while in < last, enough input available */ + z_const unsigned char FAR *in; /* local strm->next_in */ + z_const unsigned char FAR *last; /* have enough input while in < last */ unsigned char FAR *out; /* local strm->next_out */ unsigned char FAR *beg; /* inflate()'s initial strm->next_out */ unsigned char FAR *end; /* while out < end, enough space available */ diff -Nru libgit2-0.20.0/deps/zlib/inffixed.h libgit2-0.22.2/deps/zlib/inffixed.h --- libgit2-0.20.0/deps/zlib/inffixed.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/deps/zlib/inffixed.h 2015-03-24 16:10:45.000000000 +0000 @@ -2,9 +2,9 @@ * Generated automatically by makefixed(). */ - /* WARNING: this file should *not* be used by applications. It - is part of the implementation of the compression library and - is subject to change. Applications should only use zlib.h. + /* WARNING: this file should *not* be used by applications. + It is part of the implementation of this library and is + subject to change. Applications should only use zlib.h. */ static const code lenfix[512] = { diff -Nru libgit2-0.20.0/deps/zlib/inflate.c libgit2-0.22.2/deps/zlib/inflate.c --- libgit2-0.20.0/deps/zlib/inflate.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/deps/zlib/inflate.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,5 +1,5 @@ /* inflate.c -- zlib decompression - * Copyright (C) 1995-2010 Mark Adler + * Copyright (C) 1995-2012 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -93,14 +93,15 @@ /* function prototypes */ local void fixedtables OF((struct inflate_state FAR *state)); -local int updatewindow OF((z_streamp strm, unsigned out)); +local int updatewindow OF((z_streamp strm, const unsigned char FAR *end, + unsigned copy)); #ifdef BUILDFIXED void makefixed OF((void)); #endif -local unsigned syncsearch OF((unsigned FAR *have, unsigned char FAR *buf, +local unsigned syncsearch OF((unsigned FAR *have, const unsigned char FAR *buf, unsigned len)); -int ZEXPORT inflateReset(strm) +int ZEXPORT inflateResetKeep(strm) z_streamp strm; { struct inflate_state FAR *state; @@ -109,15 +110,13 @@ state = (struct inflate_state FAR *)strm->state; strm->total_in = strm->total_out = state->total = 0; strm->msg = Z_NULL; - strm->adler = 1; /* to support ill-conceived Java test suite */ + if (state->wrap) /* to support ill-conceived Java test suite */ + strm->adler = state->wrap & 1; state->mode = HEAD; state->last = 0; state->havedict = 0; state->dmax = 32768U; state->head = Z_NULL; - state->wsize = 0; - state->whave = 0; - state->wnext = 0; state->hold = 0; state->bits = 0; state->lencode = state->distcode = state->next = state->codes; @@ -127,6 +126,19 @@ return Z_OK; } +int ZEXPORT inflateReset(strm) +z_streamp strm; +{ + struct inflate_state FAR *state; + + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + state->wsize = 0; + state->whave = 0; + state->wnext = 0; + return inflateResetKeep(strm); +} + int ZEXPORT inflateReset2(strm, windowBits) z_streamp strm; int windowBits; @@ -180,10 +192,19 @@ if (strm == Z_NULL) return Z_STREAM_ERROR; strm->msg = Z_NULL; /* in case we return an error */ if (strm->zalloc == (alloc_func)0) { +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else strm->zalloc = zcalloc; strm->opaque = (voidpf)0; +#endif } - if (strm->zfree == (free_func)0) strm->zfree = zcfree; + if (strm->zfree == (free_func)0) +#ifdef Z_SOLO + return Z_STREAM_ERROR; +#else + strm->zfree = zcfree; +#endif state = (struct inflate_state FAR *) ZALLOC(strm, 1, sizeof(struct inflate_state)); if (state == Z_NULL) return Z_MEM_ERROR; @@ -321,8 +342,8 @@ low = 0; for (;;) { if ((low % 7) == 0) printf("\n "); - printf("{%u,%u,%d}", state.lencode[low].op, state.lencode[low].bits, - state.lencode[low].val); + printf("{%u,%u,%d}", (low & 127) == 99 ? 64 : state.lencode[low].op, + state.lencode[low].bits, state.lencode[low].val); if (++low == size) break; putchar(','); } @@ -355,12 +376,13 @@ output will fall in the output data, making match copies simpler and faster. The advantage may be dependent on the size of the processor's data caches. */ -local int updatewindow(strm, out) +local int updatewindow(strm, end, copy) z_streamp strm; -unsigned out; +const Bytef *end; +unsigned copy; { struct inflate_state FAR *state; - unsigned copy, dist; + unsigned dist; state = (struct inflate_state FAR *)strm->state; @@ -380,19 +402,18 @@ } /* copy state->wsize or less output bytes into the circular window */ - copy = out - strm->avail_out; if (copy >= state->wsize) { - zmemcpy(state->window, strm->next_out - state->wsize, state->wsize); + zmemcpy(state->window, end - state->wsize, state->wsize); state->wnext = 0; state->whave = state->wsize; } else { dist = state->wsize - state->wnext; if (dist > copy) dist = copy; - zmemcpy(state->window + state->wnext, strm->next_out - copy, dist); + zmemcpy(state->window + state->wnext, end - copy, dist); copy -= dist; if (copy) { - zmemcpy(state->window, strm->next_out - copy, copy); + zmemcpy(state->window, end - copy, copy); state->wnext = copy; state->whave = state->wsize; } @@ -499,11 +520,6 @@ bits -= bits & 7; \ } while (0) -/* Reverse the bytes in a 32-bit value */ -#define REVERSE(q) \ - ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ - (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) - /* inflate() uses a state machine to process as much input data and generate as much output data as possible before returning. The state machine is @@ -591,7 +607,7 @@ int flush; { struct inflate_state FAR *state; - unsigned char FAR *next; /* next input */ + z_const unsigned char FAR *next; /* next input */ unsigned char FAR *put; /* next output */ unsigned have, left; /* available input and output */ unsigned long hold; /* bit buffer */ @@ -797,7 +813,7 @@ #endif case DICTID: NEEDBITS(32); - strm->adler = state->check = REVERSE(hold); + strm->adler = state->check = ZSWAP32(hold); INITBITS(); state->mode = DICT; case DICT: @@ -905,7 +921,7 @@ while (state->have < 19) state->lens[order[state->have++]] = 0; state->next = state->codes; - state->lencode = (code const FAR *)(state->next); + state->lencode = (const code FAR *)(state->next); state->lenbits = 7; ret = inflate_table(CODES, state->lens, 19, &(state->next), &(state->lenbits), state->work); @@ -925,7 +941,6 @@ PULLBYTE(); } if (here.val < 16) { - NEEDBITS(here.bits); DROPBITS(here.bits); state->lens[state->have++] = here.val; } @@ -980,7 +995,7 @@ values here (9 and 6) without reading the comments in inftrees.h concerning the ENOUGH constants, which depend on those values */ state->next = state->codes; - state->lencode = (code const FAR *)(state->next); + state->lencode = (const code FAR *)(state->next); state->lenbits = 9; ret = inflate_table(LENS, state->lens, state->nlen, &(state->next), &(state->lenbits), state->work); @@ -989,7 +1004,7 @@ state->mode = BAD; break; } - state->distcode = (code const FAR *)(state->next); + state->distcode = (const code FAR *)(state->next); state->distbits = 6; ret = inflate_table(DISTS, state->lens + state->nlen, state->ndist, &(state->next), &(state->distbits), state->work); @@ -1170,7 +1185,7 @@ #ifdef GUNZIP state->flags ? hold : #endif - REVERSE(hold)) != state->check) { + ZSWAP32(hold)) != state->check) { strm->msg = (char *)"incorrect data check"; state->mode = BAD; break; @@ -1214,8 +1229,9 @@ */ inf_leave: RESTORE(); - if (state->wsize || (state->mode < CHECK && out != strm->avail_out)) - if (updatewindow(strm, out)) { + if (state->wsize || (out != strm->avail_out && state->mode < BAD && + (state->mode < CHECK || flush != Z_FINISH))) + if (updatewindow(strm, strm->next_out, out - strm->avail_out)) { state->mode = MEM; return Z_MEM_ERROR; } @@ -1249,13 +1265,37 @@ return Z_OK; } +int ZEXPORT inflateGetDictionary(strm, dictionary, dictLength) +z_streamp strm; +Bytef *dictionary; +uInt *dictLength; +{ + struct inflate_state FAR *state; + + /* check state */ + if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; + state = (struct inflate_state FAR *)strm->state; + + /* copy dictionary */ + if (state->whave && dictionary != Z_NULL) { + zmemcpy(dictionary, state->window + state->wnext, + state->whave - state->wnext); + zmemcpy(dictionary + state->whave - state->wnext, + state->window, state->wnext); + } + if (dictLength != Z_NULL) + *dictLength = state->whave; + return Z_OK; +} + int ZEXPORT inflateSetDictionary(strm, dictionary, dictLength) z_streamp strm; const Bytef *dictionary; uInt dictLength; { struct inflate_state FAR *state; - unsigned long id; + unsigned long dictid; + int ret; /* check state */ if (strm == Z_NULL || strm->state == Z_NULL) return Z_STREAM_ERROR; @@ -1263,29 +1303,21 @@ if (state->wrap != 0 && state->mode != DICT) return Z_STREAM_ERROR; - /* check for correct dictionary id */ + /* check for correct dictionary identifier */ if (state->mode == DICT) { - id = adler32(0L, Z_NULL, 0); - id = adler32(id, dictionary, dictLength); - if (id != state->check) + dictid = adler32(0L, Z_NULL, 0); + dictid = adler32(dictid, dictionary, dictLength); + if (dictid != state->check) return Z_DATA_ERROR; } - /* copy dictionary to window */ - if (updatewindow(strm, strm->avail_out)) { + /* copy dictionary to window using updatewindow(), which will amend the + existing dictionary if appropriate */ + ret = updatewindow(strm, dictionary + dictLength, dictLength); + if (ret) { state->mode = MEM; return Z_MEM_ERROR; } - if (dictLength > state->wsize) { - zmemcpy(state->window, dictionary + dictLength - state->wsize, - state->wsize); - state->whave = state->wsize; - } - else { - zmemcpy(state->window + state->wsize - dictLength, dictionary, - dictLength); - state->whave = dictLength; - } state->havedict = 1; Tracev((stderr, "inflate: dictionary set\n")); return Z_OK; @@ -1321,7 +1353,7 @@ */ local unsigned syncsearch(have, buf, len) unsigned FAR *have; -unsigned char FAR *buf; +const unsigned char FAR *buf; unsigned len; { unsigned got; @@ -1433,8 +1465,8 @@ } /* copy state */ - zmemcpy(dest, source, sizeof(z_stream)); - zmemcpy(copy, state, sizeof(struct inflate_state)); + zmemcpy((voidpf)dest, (voidpf)source, sizeof(z_stream)); + zmemcpy((voidpf)copy, (voidpf)state, sizeof(struct inflate_state)); if (state->lencode >= state->codes && state->lencode <= state->codes + ENOUGH - 1) { copy->lencode = copy->codes + (state->lencode - state->codes); diff -Nru libgit2-0.20.0/deps/zlib/inftrees.c libgit2-0.22.2/deps/zlib/inftrees.c --- libgit2-0.20.0/deps/zlib/inftrees.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/deps/zlib/inftrees.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,5 +1,5 @@ /* inftrees.c -- generate Huffman trees for efficient decoding - * Copyright (C) 1995-2010 Mark Adler + * Copyright (C) 1995-2013 Mark Adler * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -9,7 +9,7 @@ #define MAXBITS 15 const char inflate_copyright[] = - " inflate 1.2.5 Copyright 1995-2010 Mark Adler "; + " inflate 1.2.8 Copyright 1995-2013 Mark Adler "; /* If you use the zlib library in a product, an acknowledgment is welcome in the documentation of your product. If for some reason you cannot @@ -62,7 +62,7 @@ 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; static const unsigned short lext[31] = { /* Length codes 257..285 extra */ 16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18, - 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 73, 195}; + 19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78}; static const unsigned short dbase[32] = { /* Distance codes 0..29 base */ 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, @@ -208,8 +208,8 @@ mask = used - 1; /* mask for comparing low */ /* check available table space */ - if ((type == LENS && used >= ENOUGH_LENS) || - (type == DISTS && used >= ENOUGH_DISTS)) + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) return 1; /* process all codes and make table entries */ @@ -277,8 +277,8 @@ /* check for enough space */ used += 1U << curr; - if ((type == LENS && used >= ENOUGH_LENS) || - (type == DISTS && used >= ENOUGH_DISTS)) + if ((type == LENS && used > ENOUGH_LENS) || + (type == DISTS && used > ENOUGH_DISTS)) return 1; /* point entry in root table to sub-table */ @@ -289,38 +289,14 @@ } } - /* - Fill in rest of table for incomplete codes. This loop is similar to the - loop above in incrementing huff for table indices. It is assumed that - len is equal to curr + drop, so there is no loop needed to increment - through high index bits. When the current sub-table is filled, the loop - drops back to the root table to fill in any remaining entries there. - */ - here.op = (unsigned char)64; /* invalid code marker */ - here.bits = (unsigned char)(len - drop); - here.val = (unsigned short)0; - while (huff != 0) { - /* when done with sub-table, drop back to root table */ - if (drop != 0 && (huff & mask) != low) { - drop = 0; - len = root; - next = *table; - here.bits = (unsigned char)len; - } - - /* put invalid code marker in table */ - next[huff >> drop] = here; - - /* backwards increment the len-bit code huff */ - incr = 1U << (len - 1); - while (huff & incr) - incr >>= 1; - if (incr != 0) { - huff &= incr - 1; - huff += incr; - } - else - huff = 0; + /* fill in remaining table entry if code is incomplete (guaranteed to have + at most one remaining entry, since if the code is incomplete, the + maximum code length that was allowed to get this far is one bit) */ + if (huff != 0) { + here.op = (unsigned char)64; /* invalid code marker */ + here.bits = (unsigned char)(len - drop); + here.val = (unsigned short)0; + next[huff] = here; } /* set return parameters */ diff -Nru libgit2-0.20.0/deps/zlib/trees.c libgit2-0.22.2/deps/zlib/trees.c --- libgit2-0.20.0/deps/zlib/trees.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/deps/zlib/trees.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,5 +1,5 @@ /* trees.c -- output deflated data using Huffman coding - * Copyright (C) 1995-2010 Jean-loup Gailly + * Copyright (C) 1995-2012 Jean-loup Gailly * detect_data_type() function provided freely by Cosmin Truta, 2006 * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -74,11 +74,6 @@ * probability, to avoid transmitting the lengths for unused bit length codes. */ -#define Buf_size (8 * 2*sizeof(char)) -/* Number of bits used within bi_buf. (bi_buf might be implemented on - * more than 16 bits on some systems.) - */ - /* =========================================================================== * Local data. These are initialized only once. */ @@ -151,8 +146,8 @@ local int build_bl_tree OF((deflate_state *s)); local void send_all_trees OF((deflate_state *s, int lcodes, int dcodes, int blcodes)); -local void compress_block OF((deflate_state *s, ct_data *ltree, - ct_data *dtree)); +local void compress_block OF((deflate_state *s, const ct_data *ltree, + const ct_data *dtree)); local int detect_data_type OF((deflate_state *s)); local unsigned bi_reverse OF((unsigned value, int length)); local void bi_windup OF((deflate_state *s)); @@ -399,7 +394,6 @@ s->bi_buf = 0; s->bi_valid = 0; - s->last_eob_len = 8; /* enough lookahead for inflate */ #ifdef DEBUG s->compressed_len = 0L; s->bits_sent = 0L; @@ -883,15 +877,17 @@ } /* =========================================================================== + * Flush the bits in the bit buffer to pending output (leaves at most 7 bits) + */ +void ZLIB_INTERNAL _tr_flush_bits(s) + deflate_state *s; +{ + bi_flush(s); +} + +/* =========================================================================== * Send one empty static block to give enough lookahead for inflate. * This takes 10 bits, of which 7 may remain in the bit buffer. - * The current inflate code requires 9 bits of lookahead. If the - * last two codes for the previous block (real code plus EOB) were coded - * on 5 bits or less, inflate may have only 5+3 bits of lookahead to decode - * the last real code. In this case we send two empty static blocks instead - * of one. (There are no problems if the previous block is stored or fixed.) - * To simplify the code, we assume the worst case of last real code encoded - * on one bit only. */ void ZLIB_INTERNAL _tr_align(s) deflate_state *s; @@ -902,20 +898,6 @@ s->compressed_len += 10L; /* 3 for block type, 7 for EOB */ #endif bi_flush(s); - /* Of the 10 bits for the empty block, we have already sent - * (10 - bi_valid) bits. The lookahead for the last real code (before - * the EOB of the previous block) was thus at least one plus the length - * of the EOB plus what we have just sent of the empty static block. - */ - if (1 + s->last_eob_len + 10 - s->bi_valid < 9) { - send_bits(s, STATIC_TREES<<1, 3); - send_code(s, END_BLOCK, static_ltree); -#ifdef DEBUG - s->compressed_len += 10L; -#endif - bi_flush(s); - } - s->last_eob_len = 7; } /* =========================================================================== @@ -990,7 +972,8 @@ } else if (s->strategy == Z_FIXED || static_lenb == opt_lenb) { #endif send_bits(s, (STATIC_TREES<<1)+last, 3); - compress_block(s, (ct_data *)static_ltree, (ct_data *)static_dtree); + compress_block(s, (const ct_data *)static_ltree, + (const ct_data *)static_dtree); #ifdef DEBUG s->compressed_len += 3 + s->static_len; #endif @@ -998,7 +981,8 @@ send_bits(s, (DYN_TREES<<1)+last, 3); send_all_trees(s, s->l_desc.max_code+1, s->d_desc.max_code+1, max_blindex+1); - compress_block(s, (ct_data *)s->dyn_ltree, (ct_data *)s->dyn_dtree); + compress_block(s, (const ct_data *)s->dyn_ltree, + (const ct_data *)s->dyn_dtree); #ifdef DEBUG s->compressed_len += 3 + s->opt_len; #endif @@ -1075,8 +1059,8 @@ */ local void compress_block(s, ltree, dtree) deflate_state *s; - ct_data *ltree; /* literal tree */ - ct_data *dtree; /* distance tree */ + const ct_data *ltree; /* literal tree */ + const ct_data *dtree; /* distance tree */ { unsigned dist; /* distance of matched string */ int lc; /* match length or unmatched char (if dist == 0) */ @@ -1118,7 +1102,6 @@ } while (lx < s->last_lit); send_code(s, END_BLOCK, ltree); - s->last_eob_len = ltree[END_BLOCK].Len; } /* =========================================================================== @@ -1226,7 +1209,6 @@ int header; /* true if block header must be written */ { bi_windup(s); /* align on byte boundary */ - s->last_eob_len = 8; /* enough lookahead for inflate */ if (header) { put_short(s, (ush)len); diff -Nru libgit2-0.20.0/deps/zlib/zconf.h libgit2-0.22.2/deps/zlib/zconf.h --- libgit2-0.20.0/deps/zlib/zconf.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/deps/zlib/zconf.h 2015-03-24 16:10:45.000000000 +0000 @@ -14,6 +14,7 @@ * forms, we didn't write zlib */ #if defined(_MSC_VER) # pragma warning( disable : 4131 ) +# pragma warning( disable : 4142 ) /* benign redefinition of type */ #endif /* Maximum value for memLevel in deflateInit2 */ @@ -33,10 +34,12 @@ # define FAR #endif #define OF(args) args +#define Z_ARG(args) args typedef unsigned char Byte; /* 8 bits */ typedef unsigned int uInt; /* 16 bits or more */ typedef unsigned long uLong; /* 32 bits or more */ +typedef unsigned long z_crc_t; typedef Byte FAR Bytef; typedef char FAR charf; @@ -50,5 +53,6 @@ #define z_off_t git_off_t #define z_off64_t z_off_t +#define z_const const #endif /* ZCONF_H */ diff -Nru libgit2-0.20.0/deps/zlib/zlib.h libgit2-0.22.2/deps/zlib/zlib.h --- libgit2-0.20.0/deps/zlib/zlib.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/deps/zlib/zlib.h 2015-03-24 16:10:45.000000000 +0000 @@ -1,7 +1,7 @@ /* zlib.h -- interface of the 'zlib' general purpose compression library - version 1.2.5, April 19th, 2010 + version 1.2.8, April 28th, 2013 - Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler + Copyright (C) 1995-2013 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -24,8 +24,8 @@ The data format used by the zlib library is described by RFCs (Request for - Comments) 1950 to 1952 in the files http://www.ietf.org/rfc/rfc1950.txt - (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). + Comments) 1950 to 1952 in the files http://tools.ietf.org/html/rfc1950 + (zlib format), rfc1951 (deflate format) and rfc1952 (gzip format). */ #ifndef ZLIB_H @@ -37,11 +37,11 @@ extern "C" { #endif -#define ZLIB_VERSION "1.2.5" -#define ZLIB_VERNUM 0x1250 +#define ZLIB_VERSION "1.2.8" +#define ZLIB_VERNUM 0x1280 #define ZLIB_VER_MAJOR 1 #define ZLIB_VER_MINOR 2 -#define ZLIB_VER_REVISION 5 +#define ZLIB_VER_REVISION 8 #define ZLIB_VER_SUBREVISION 0 /* @@ -83,15 +83,15 @@ struct internal_state; typedef struct z_stream_s { - Bytef *next_in; /* next input byte */ + z_const Bytef *next_in; /* next input byte */ uInt avail_in; /* number of bytes available at next_in */ - uLong total_in; /* total nb of input bytes read so far */ + uLong total_in; /* total number of input bytes read so far */ Bytef *next_out; /* next output byte should be put there */ uInt avail_out; /* remaining free space at next_out */ - uLong total_out; /* total nb of bytes output so far */ + uLong total_out; /* total number of bytes output so far */ - char *msg; /* last error message, NULL if no error */ + z_const char *msg; /* last error message, NULL if no error */ struct internal_state FAR *state; /* not visible by applications */ alloc_func zalloc; /* used to allocate the internal state */ @@ -327,8 +327,9 @@ Z_FINISH can be used immediately after deflateInit if all the compression is to be done in a single step. In this case, avail_out must be at least the - value returned by deflateBound (see below). If deflate does not return - Z_STREAM_END, then it must be called again as described above. + value returned by deflateBound (see below). Then deflate is guaranteed to + return Z_STREAM_END. If not enough output space is provided, deflate will + not return Z_STREAM_END, and it must be called again as described above. deflate() sets strm->adler to the adler32 checksum of all input read so far (that is, total_in bytes). @@ -451,23 +452,29 @@ error. However if all decompression is to be performed in a single step (a single call of inflate), the parameter flush should be set to Z_FINISH. In this case all pending input is processed and all pending output is flushed; - avail_out must be large enough to hold all the uncompressed data. (The size - of the uncompressed data may have been saved by the compressor for this - purpose.) The next operation on this stream must be inflateEnd to deallocate - the decompression state. The use of Z_FINISH is never required, but can be - used to inform inflate that a faster approach may be used for the single - inflate() call. + avail_out must be large enough to hold all of the uncompressed data for the + operation to complete. (The size of the uncompressed data may have been + saved by the compressor for this purpose.) The use of Z_FINISH is not + required to perform an inflation in one step. However it may be used to + inform inflate that a faster approach can be used for the single inflate() + call. Z_FINISH also informs inflate to not maintain a sliding window if the + stream completes, which reduces inflate's memory footprint. If the stream + does not complete, either because not all of the stream is provided or not + enough output space is provided, then a sliding window will be allocated and + inflate() can be called again to continue the operation as if Z_NO_FLUSH had + been used. In this implementation, inflate() always flushes as much output as possible to the output buffer, and always uses the faster approach on the - first call. So the only effect of the flush parameter in this implementation - is on the return value of inflate(), as noted below, or when it returns early - because Z_BLOCK or Z_TREES is used. + first call. So the effects of the flush parameter in this implementation are + on the return value of inflate() as noted below, when inflate() returns early + when Z_BLOCK or Z_TREES is used, and when inflate() avoids the allocation of + memory for a sliding window when Z_FINISH is used. If a preset dictionary is needed after this call (see inflateSetDictionary - below), inflate sets strm->adler to the adler32 checksum of the dictionary + below), inflate sets strm->adler to the Adler-32 checksum of the dictionary chosen by the compressor and returns Z_NEED_DICT; otherwise it sets - strm->adler to the adler32 checksum of all output produced so far (that is, + strm->adler to the Adler-32 checksum of all output produced so far (that is, total_out bytes) and returns Z_OK, Z_STREAM_END or an error code as described below. At the end of the stream, inflate() checks that its computed adler32 checksum is equal to that saved by the compressor and returns Z_STREAM_END @@ -478,7 +485,9 @@ initializing with inflateInit2(). Any information contained in the gzip header is not retained, so applications that need that information should instead use raw inflate, see inflateInit2() below, or inflateBack() and - perform their own processing of the gzip header and trailer. + perform their own processing of the gzip header and trailer. When processing + gzip-wrapped deflate data, strm->adler32 is set to the CRC-32 of the output + producted so far. The CRC-32 is checked against the gzip trailer. inflate() returns Z_OK if some progress has been made (more input processed or more output produced), Z_STREAM_END if the end of the compressed data has @@ -580,10 +589,15 @@ uInt dictLength)); /* Initializes the compression dictionary from the given byte sequence - without producing any compressed output. This function must be called - immediately after deflateInit, deflateInit2 or deflateReset, before any call - of deflate. The compressor and decompressor must use exactly the same - dictionary (see inflateSetDictionary). + without producing any compressed output. When using the zlib format, this + function must be called immediately after deflateInit, deflateInit2 or + deflateReset, and before any call of deflate. When doing raw deflate, this + function must be called either before any call of deflate, or immediately + after the completion of a deflate block, i.e. after all input has been + consumed and all output has been delivered when using any of the flush + options Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, or Z_FULL_FLUSH. The + compressor and decompressor must use exactly the same dictionary (see + inflateSetDictionary). The dictionary should consist of strings (byte sequences) that are likely to be encountered later in the data to be compressed, with the most commonly @@ -610,8 +624,8 @@ deflateSetDictionary returns Z_OK if success, or Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is inconsistent (for example if deflate has already been called for this stream - or if the compression method is bsort). deflateSetDictionary does not - perform any compression: this will be done by deflate(). + or if not at a block boundary for raw deflate). deflateSetDictionary does + not perform any compression: this will be done by deflate(). */ ZEXTERN int ZEXPORT deflateCopy OF((z_streamp dest, @@ -688,8 +702,28 @@ deflation of sourceLen bytes. It must be called after deflateInit() or deflateInit2(), and after deflateSetHeader(), if used. This would be used to allocate an output buffer for deflation in a single pass, and so would be - called before deflate(). -*/ + called before deflate(). If that first deflate() call is provided the + sourceLen input bytes, an output buffer allocated to the size returned by + deflateBound(), and the flush value Z_FINISH, then deflate() is guaranteed + to return Z_STREAM_END. Note that it is possible for the compressed size to + be larger than the value returned by deflateBound() if flush options other + than Z_FINISH or Z_NO_FLUSH are used. +*/ + +ZEXTERN int ZEXPORT deflatePending OF((z_streamp strm, + unsigned *pending, + int *bits)); +/* + deflatePending() returns the number of bytes and bits of output that have + been generated, but not yet provided in the available output. The bytes not + provided would be due to the available output space having being consumed. + The number of bits of output not provided are between 0 and 7, where they + await more bits to join them in order to fill out a full byte. If pending + or bits are Z_NULL, then those values are not set. + + deflatePending returns Z_OK if success, or Z_STREAM_ERROR if the source + stream state was inconsistent. + */ ZEXTERN int ZEXPORT deflatePrime OF((z_streamp strm, int bits, @@ -703,8 +737,9 @@ than or equal to 16, and that many of the least significant bits of value will be inserted in the output. - deflatePrime returns Z_OK if success, or Z_STREAM_ERROR if the source - stream state was inconsistent. + deflatePrime returns Z_OK if success, Z_BUF_ERROR if there was not enough + room in the internal buffer to insert the bits, or Z_STREAM_ERROR if the + source stream state was inconsistent. */ ZEXTERN int ZEXPORT deflateSetHeader OF((z_streamp strm, @@ -790,10 +825,11 @@ if that call returned Z_NEED_DICT. The dictionary chosen by the compressor can be determined from the adler32 value returned by that call of inflate. The compressor and decompressor must use exactly the same dictionary (see - deflateSetDictionary). For raw inflate, this function can be called - immediately after inflateInit2() or inflateReset() and before any call of - inflate() to set the dictionary. The application must insure that the - dictionary that was used for compression is provided. + deflateSetDictionary). For raw inflate, this function can be called at any + time to set the dictionary. If the provided dictionary is smaller than the + window and there is already data in the window, then the provided dictionary + will amend what's there. The application must insure that the dictionary + that was used for compression is provided. inflateSetDictionary returns Z_OK if success, Z_STREAM_ERROR if a parameter is invalid (e.g. dictionary being Z_NULL) or the stream state is @@ -803,19 +839,38 @@ inflate(). */ +ZEXTERN int ZEXPORT inflateGetDictionary OF((z_streamp strm, + Bytef *dictionary, + uInt *dictLength)); +/* + Returns the sliding dictionary being maintained by inflate. dictLength is + set to the number of bytes in the dictionary, and that many bytes are copied + to dictionary. dictionary must have enough space, where 32768 bytes is + always enough. If inflateGetDictionary() is called with dictionary equal to + Z_NULL, then only the dictionary length is returned, and nothing is copied. + Similary, if dictLength is Z_NULL, then it is not set. + + inflateGetDictionary returns Z_OK on success, or Z_STREAM_ERROR if the + stream state is inconsistent. +*/ + ZEXTERN int ZEXPORT inflateSync OF((z_streamp strm)); /* - Skips invalid compressed data until a full flush point (see above the - description of deflate with Z_FULL_FLUSH) can be found, or until all + Skips invalid compressed data until a possible full flush point (see above + for the description of deflate with Z_FULL_FLUSH) can be found, or until all available input is skipped. No output is provided. - inflateSync returns Z_OK if a full flush point has been found, Z_BUF_ERROR - if no more input was provided, Z_DATA_ERROR if no flush point has been - found, or Z_STREAM_ERROR if the stream structure was inconsistent. In the - success case, the application may save the current current value of total_in - which indicates where valid compressed data was found. In the error case, - the application may repeatedly call inflateSync, providing more input each - time, until success or end of the input data. + inflateSync searches for a 00 00 FF FF pattern in the compressed data. + All full flush points have this pattern, but not all occurrences of this + pattern are full flush points. + + inflateSync returns Z_OK if a possible full flush point has been found, + Z_BUF_ERROR if no more input was provided, Z_DATA_ERROR if no flush point + has been found, or Z_STREAM_ERROR if the stream structure was inconsistent. + In the success case, the application may save the current current value of + total_in which indicates where valid compressed data was found. In the + error case, the application may repeatedly call inflateSync, providing more + input each time, until success or end of the input data. */ ZEXTERN int ZEXPORT inflateCopy OF((z_streamp dest, @@ -962,12 +1017,13 @@ See inflateBack() for the usage of these routines. inflateBackInit will return Z_OK on success, Z_STREAM_ERROR if any of - the paramaters are invalid, Z_MEM_ERROR if the internal state could not be + the parameters are invalid, Z_MEM_ERROR if the internal state could not be allocated, or Z_VERSION_ERROR if the version of the library does not match the version of the header file. */ -typedef unsigned (*in_func) OF((void FAR *, unsigned char FAR * FAR *)); +typedef unsigned (*in_func) OF((void FAR *, + z_const unsigned char FAR * FAR *)); typedef int (*out_func) OF((void FAR *, unsigned char FAR *, unsigned)); ZEXTERN int ZEXPORT inflateBack OF((z_streamp strm, @@ -975,11 +1031,12 @@ out_func out, void FAR *out_desc)); /* inflateBack() does a raw inflate with a single call using a call-back - interface for input and output. This is more efficient than inflate() for - file i/o applications in that it avoids copying between the output and the - sliding window by simply making the window itself the output buffer. This - function trusts the application to not change the output buffer passed by - the output function, at least until inflateBack() returns. + interface for input and output. This is potentially more efficient than + inflate() for file i/o applications, in that it avoids copying between the + output and the sliding window by simply making the window itself the output + buffer. inflate() can be faster on modern CPUs when used with large + buffers. inflateBack() trusts the application to not change the output + buffer passed by the output function, at least until inflateBack() returns. inflateBackInit() must be called first to allocate the internal state and to initialize the state with the user-provided window buffer. @@ -1088,6 +1145,7 @@ 27-31: 0 (reserved) */ +#ifndef Z_SOLO /* utility functions */ @@ -1149,10 +1207,11 @@ uncompress returns Z_OK if success, Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if there was not enough room in the output - buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. + buffer, or Z_DATA_ERROR if the input data was corrupted or incomplete. In + the case where there is not enough room, uncompress() will fill the output + buffer with the uncompressed data up to that point. */ - /* gzip file access functions */ /* @@ -1162,7 +1221,7 @@ wrapper, documented in RFC 1952, wrapped around a deflate stream. */ -typedef voidp gzFile; /* opaque gzip file descriptor */ +typedef struct gzFile_s *gzFile; /* semi-opaque gzip file descriptor */ /* ZEXTERN gzFile ZEXPORT gzopen OF((const char *path, const char *mode)); @@ -1172,13 +1231,28 @@ a strategy: 'f' for filtered data as in "wb6f", 'h' for Huffman-only compression as in "wb1h", 'R' for run-length encoding as in "wb1R", or 'F' for fixed code compression as in "wb9F". (See the description of - deflateInit2 for more information about the strategy parameter.) Also "a" - can be used instead of "w" to request that the gzip stream that will be - written be appended to the file. "+" will result in an error, since reading - and writing to the same gzip file is not supported. + deflateInit2 for more information about the strategy parameter.) 'T' will + request transparent writing or appending with no compression and not using + the gzip format. + + "a" can be used instead of "w" to request that the gzip stream that will + be written be appended to the file. "+" will result in an error, since + reading and writing to the same gzip file is not supported. The addition of + "x" when writing will create the file exclusively, which fails if the file + already exists. On systems that support it, the addition of "e" when + reading or writing will set the flag to close the file on an execve() call. + + These functions, as well as gzip, will read and decode a sequence of gzip + streams in a file. The append function of gzopen() can be used to create + such a file. (Also see gzflush() for another way to do this.) When + appending, gzopen does not test whether the file begins with a gzip stream, + nor does it look for the end of the gzip streams to begin appending. gzopen + will simply append a gzip stream to the existing file. gzopen can be used to read a file which is not in gzip format; in this - case gzread will directly read from the file without decompression. + case gzread will directly read from the file without decompression. When + reading, this will be detected automatically by looking for the magic two- + byte gzip header. gzopen returns NULL if the file could not be opened, if there was insufficient memory to allocate the gzFile state, or if an invalid mode was @@ -1197,7 +1271,11 @@ descriptor fd, just like fclose(fdopen(fd, mode)) closes the file descriptor fd. If you want to keep fd open, use fd = dup(fd_keep); gz = gzdopen(fd, mode);. The duplicated descriptor should be saved to avoid a leak, since - gzdopen does not close fd if it fails. + gzdopen does not close fd if it fails. If you are using fileno() to get the + file descriptor from a FILE *, then you will have to use dup() to avoid + double-close()ing the file descriptor. Both gzclose() and fclose() will + close the associated file descriptor, so they need to have different file + descriptors. gzdopen returns NULL if there was insufficient memory to allocate the gzFile state, if an invalid mode was specified (an 'r', 'w', or 'a' was not @@ -1235,14 +1313,26 @@ ZEXTERN int ZEXPORT gzread OF((gzFile file, voidp buf, unsigned len)); /* Reads the given number of uncompressed bytes from the compressed file. If - the input file was not in gzip format, gzread copies the given number of - bytes into the buffer. + the input file is not in gzip format, gzread copies the given number of + bytes into the buffer directly from the file. After reaching the end of a gzip stream in the input, gzread will continue - to read, looking for another gzip stream, or failing that, reading the rest - of the input file directly without decompression. The entire input file - will be read if gzread is called until it returns less than the requested - len. + to read, looking for another gzip stream. Any number of gzip streams may be + concatenated in the input file, and will all be decompressed by gzread(). + If something other than a gzip stream is encountered after a gzip stream, + that remaining trailing garbage is ignored (and no error is returned). + + gzread can be used to read a gzip file that is being concurrently written. + Upon reaching the end of the input, gzread will return with the available + data. If the error code returned by gzerror is Z_OK or Z_BUF_ERROR, then + gzclearerr can be used to clear the end of file indicator in order to permit + gzread to be tried again. Z_OK indicates that a gzip stream was completed + on the last gzread. Z_BUF_ERROR indicates that the input file ended in the + middle of a gzip stream. Note that gzread does not return -1 in the event + of an incomplete gzip stream. This error is deferred until gzclose(), which + will return Z_BUF_ERROR if the last gzread ended in the middle of a gzip + stream. Alternatively, gzerror can be used before gzclose to detect this + case. gzread returns the number of uncompressed bytes actually read, less than len for end of file, or -1 for error. @@ -1256,7 +1346,7 @@ error. */ -ZEXTERN int ZEXPORTVA gzprintf OF((gzFile file, const char *format, ...)); +ZEXTERN int ZEXPORTVA gzprintf Z_ARG((gzFile file, const char *format, ...)); /* Converts, formats, and writes the arguments to the compressed file under control of the format string, as in fprintf. gzprintf returns the number of @@ -1301,7 +1391,10 @@ ZEXTERN int ZEXPORT gzgetc OF((gzFile file)); /* Reads one byte from the compressed file. gzgetc returns this byte or -1 - in case of end of file or error. + in case of end of file or error. This is implemented as a macro for speed. + As such, it does not do all of the checking the other functions do. I.e. + it does not check to see if file is NULL, nor whether the structure file + points to has been clobbered or not. */ ZEXTERN int ZEXPORT gzungetc OF((int c, gzFile file)); @@ -1397,9 +1490,7 @@ ZEXTERN int ZEXPORT gzdirect OF((gzFile file)); /* Returns true (1) if file is being copied directly while reading, or false - (0) if file is a gzip stream being decompressed. This state can change from - false to true while reading the input file if the end of a gzip stream is - reached, but is followed by data that is not another gzip stream. + (0) if file is a gzip stream being decompressed. If the input file is empty, gzdirect() will return true, since the input does not contain a gzip stream. @@ -1408,6 +1499,13 @@ cause buffers to be allocated to allow reading the file to determine if it is a gzip file. Therefore if gzbuffer() is used, it should be called before gzdirect(). + + When writing, gzdirect() returns true (1) if transparent writing was + requested ("wT" for the gzopen() mode), or false (0) otherwise. (Note: + gzdirect() is not needed when writing. Transparent writing must be + explicitly requested, so the application already knows the answer. When + linking statically, using gzdirect() will include all of the zlib code for + gzip file reading and decompression, which may not be desired.) */ ZEXTERN int ZEXPORT gzclose OF((gzFile file)); @@ -1419,7 +1517,8 @@ must not be called more than once on the same allocation. gzclose will return Z_STREAM_ERROR if file is not valid, Z_ERRNO on a - file operation error, or Z_OK on success. + file operation error, Z_MEM_ERROR if out of memory, Z_BUF_ERROR if the + last read ended in the middle of a gzip stream, or Z_OK on success. */ ZEXTERN int ZEXPORT gzclose_r OF((gzFile file)); @@ -1457,6 +1556,7 @@ file that is being written concurrently. */ +#endif /* !Z_SOLO */ /* checksum functions */ @@ -1492,16 +1592,17 @@ Combine two Adler-32 checksums into one. For two sequences of bytes, seq1 and seq2 with lengths len1 and len2, Adler-32 checksums were calculated for each, adler1 and adler2. adler32_combine() returns the Adler-32 checksum of - seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. + seq1 and seq2 concatenated, requiring only adler1, adler2, and len2. Note + that the z_off_t type (like off_t) is a signed integer. If len2 is + negative, the result has no meaning or utility. */ ZEXTERN uLong ZEXPORT crc32 OF((uLong crc, const Bytef *buf, uInt len)); /* Update a running CRC-32 with the bytes buf[0..len-1] and return the updated CRC-32. If buf is Z_NULL, this function returns the required - initial value for the for the crc. Pre- and post-conditioning (one's - complement) is performed within this function so it shouldn't be done by the - application. + initial value for the crc. Pre- and post-conditioning (one's complement) is + performed within this function so it shouldn't be done by the application. Usage example: @@ -1544,17 +1645,42 @@ const char *version, int stream_size)); #define deflateInit(strm, level) \ - deflateInit_((strm), (level), ZLIB_VERSION, sizeof(z_stream)) + deflateInit_((strm), (level), ZLIB_VERSION, (int)sizeof(z_stream)) #define inflateInit(strm) \ - inflateInit_((strm), ZLIB_VERSION, sizeof(z_stream)) + inflateInit_((strm), ZLIB_VERSION, (int)sizeof(z_stream)) #define deflateInit2(strm, level, method, windowBits, memLevel, strategy) \ deflateInit2_((strm),(level),(method),(windowBits),(memLevel),\ - (strategy), ZLIB_VERSION, sizeof(z_stream)) + (strategy), ZLIB_VERSION, (int)sizeof(z_stream)) #define inflateInit2(strm, windowBits) \ - inflateInit2_((strm), (windowBits), ZLIB_VERSION, sizeof(z_stream)) + inflateInit2_((strm), (windowBits), ZLIB_VERSION, \ + (int)sizeof(z_stream)) #define inflateBackInit(strm, windowBits, window) \ inflateBackInit_((strm), (windowBits), (window), \ - ZLIB_VERSION, sizeof(z_stream)) + ZLIB_VERSION, (int)sizeof(z_stream)) + +#ifndef Z_SOLO + +/* gzgetc() macro and its supporting function and exposed data structure. Note + * that the real internal state is much larger than the exposed structure. + * This abbreviated structure exposes just enough for the gzgetc() macro. The + * user should not mess with these exposed elements, since their names or + * behavior could change in the future, perhaps even capriciously. They can + * only be used by the gzgetc() macro. You have been warned. + */ +struct gzFile_s { + unsigned have; + unsigned char *next; + z_off64_t pos; +}; +ZEXTERN int ZEXPORT gzgetc_ OF((gzFile file)); /* backward compatibility */ +#ifdef Z_PREFIX_SET +# undef z_gzgetc +# define z_gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) +#else +# define gzgetc(g) \ + ((g)->have ? ((g)->have--, (g)->pos++, *((g)->next)++) : gzgetc(g)) +#endif /* provide 64-bit offset functions if _LARGEFILE64_SOURCE defined, and/or * change the regular functions to 64 bits if _FILE_OFFSET_BITS is 64 (if @@ -1562,7 +1688,7 @@ * functions are changed to 64 bits) -- in case these are set on systems * without large file support, _LFS64_LARGEFILE must also be true */ -#if defined(_LARGEFILE64_SOURCE) && _LFS64_LARGEFILE-0 +#ifdef Z_LARGE64 ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); ZEXTERN z_off64_t ZEXPORT gzseek64 OF((gzFile, z_off64_t, int)); ZEXTERN z_off64_t ZEXPORT gztell64 OF((gzFile)); @@ -1571,14 +1697,23 @@ ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off64_t)); #endif -#if !defined(ZLIB_INTERNAL) && _FILE_OFFSET_BITS-0 == 64 && _LFS64_LARGEFILE-0 -# define gzopen gzopen64 -# define gzseek gzseek64 -# define gztell gztell64 -# define gzoffset gzoffset64 -# define adler32_combine adler32_combine64 -# define crc32_combine crc32_combine64 -# ifdef _LARGEFILE64_SOURCE +#if !defined(ZLIB_INTERNAL) && defined(Z_WANT64) +# ifdef Z_PREFIX_SET +# define z_gzopen z_gzopen64 +# define z_gzseek z_gzseek64 +# define z_gztell z_gztell64 +# define z_gzoffset z_gzoffset64 +# define z_adler32_combine z_adler32_combine64 +# define z_crc32_combine z_crc32_combine64 +# else +# define gzopen gzopen64 +# define gzseek gzseek64 +# define gztell gztell64 +# define gzoffset gzoffset64 +# define adler32_combine adler32_combine64 +# define crc32_combine crc32_combine64 +# endif +# ifndef Z_LARGE64 ZEXTERN gzFile ZEXPORT gzopen64 OF((const char *, const char *)); ZEXTERN z_off_t ZEXPORT gzseek64 OF((gzFile, z_off_t, int)); ZEXTERN z_off_t ZEXPORT gztell64 OF((gzFile)); @@ -1595,6 +1730,13 @@ ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); #endif +#else /* Z_SOLO */ + + ZEXTERN uLong ZEXPORT adler32_combine OF((uLong, uLong, z_off_t)); + ZEXTERN uLong ZEXPORT crc32_combine OF((uLong, uLong, z_off_t)); + +#endif /* !Z_SOLO */ + /* hack for buggy compilers */ #if !defined(ZUTIL_H) && !defined(NO_DUMMY_DECL) struct internal_state {int dummy;}; @@ -1603,8 +1745,21 @@ /* undocumented functions */ ZEXTERN const char * ZEXPORT zError OF((int)); ZEXTERN int ZEXPORT inflateSyncPoint OF((z_streamp)); -ZEXTERN const uLongf * ZEXPORT get_crc_table OF((void)); +ZEXTERN const z_crc_t FAR * ZEXPORT get_crc_table OF((void)); ZEXTERN int ZEXPORT inflateUndermine OF((z_streamp, int)); +ZEXTERN int ZEXPORT inflateResetKeep OF((z_streamp)); +ZEXTERN int ZEXPORT deflateResetKeep OF((z_streamp)); +#if defined(_WIN32) && !defined(Z_SOLO) +ZEXTERN gzFile ZEXPORT gzopen_w OF((const wchar_t *path, + const char *mode)); +#endif +#if defined(STDC) || defined(Z_HAVE_STDARG_H) +# ifndef Z_SOLO +ZEXTERN int ZEXPORTVA gzvprintf Z_ARG((gzFile file, + const char *format, + va_list va)); +# endif +#endif #ifdef __cplusplus } diff -Nru libgit2-0.20.0/deps/zlib/zutil.c libgit2-0.22.2/deps/zlib/zutil.c --- libgit2-0.20.0/deps/zlib/zutil.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/deps/zlib/zutil.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,5 +1,5 @@ /* zutil.c -- target dependent utility functions for the compression library - * Copyright (C) 1995-2005, 2010 Jean-loup Gailly. + * Copyright (C) 1995-2005, 2010, 2011, 2012 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -11,7 +11,7 @@ struct internal_state {int dummy;}; /* for buggy compilers */ #endif -const char * const z_errmsg[10] = { +z_const char * const z_errmsg[10] = { "need dictionary", /* Z_NEED_DICT 2 */ "stream end", /* Z_STREAM_END 1 */ "", /* Z_OK 0 */ @@ -85,27 +85,27 @@ #ifdef FASTEST flags += 1L << 21; #endif -#ifdef STDC +#if defined(STDC) || defined(Z_HAVE_STDARG_H) # ifdef NO_vsnprintf - flags += 1L << 25; + flags += 1L << 25; # ifdef HAS_vsprintf_void - flags += 1L << 26; + flags += 1L << 26; # endif # else # ifdef HAS_vsnprintf_void - flags += 1L << 26; + flags += 1L << 26; # endif # endif #else - flags += 1L << 24; + flags += 1L << 24; # ifdef NO_snprintf - flags += 1L << 25; + flags += 1L << 25; # ifdef HAS_sprintf_void - flags += 1L << 26; + flags += 1L << 26; # endif # else # ifdef HAS_snprintf_void - flags += 1L << 26; + flags += 1L << 26; # endif # endif #endif @@ -181,6 +181,7 @@ } #endif +#ifndef Z_SOLO #ifdef SYS16BIT @@ -316,3 +317,5 @@ } #endif /* MY_ZCALLOC */ + +#endif /* !Z_SOLO */ diff -Nru libgit2-0.20.0/deps/zlib/zutil.h libgit2-0.22.2/deps/zlib/zutil.h --- libgit2-0.20.0/deps/zlib/zutil.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/deps/zlib/zutil.h 2015-03-24 16:10:45.000000000 +0000 @@ -1,5 +1,5 @@ /* zutil.h -- internal interface and configuration of the compression library - * Copyright (C) 1995-2010 Jean-loup Gailly. + * Copyright (C) 1995-2013 Jean-loup Gailly. * For conditions of distribution and use, see copyright notice in zlib.h */ @@ -13,7 +13,7 @@ #ifndef ZUTIL_H #define ZUTIL_H -#if ((__GNUC__-0) * 10 + __GNUC_MINOR__-0 >= 33) && !defined(NO_VIZ) +#ifdef HAVE_HIDDEN # define ZLIB_INTERNAL __attribute__((visibility ("hidden"))) #else # define ZLIB_INTERNAL @@ -21,7 +21,7 @@ #include "zlib.h" -#ifdef STDC +#if defined(STDC) && !defined(Z_SOLO) # if !(defined(_WIN32_WCE) && defined(_MSC_VER)) # include # endif @@ -29,6 +29,10 @@ # include #endif +#ifdef Z_SOLO + typedef long ptrdiff_t; /* guess -- will be caught if guess is wrong */ +#endif + #ifndef local # define local static #endif @@ -40,13 +44,13 @@ typedef ush FAR ushf; typedef unsigned long ulg; -extern const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ +extern z_const char * const z_errmsg[10]; /* indexed by 2-zlib_error */ /* (size given to avoid silly warnings with Visual C++) */ #define ERR_MSG(err) z_errmsg[Z_NEED_DICT-(err)] #define ERR_RETURN(strm,err) \ - return (strm->msg = (char*)ERR_MSG(err), (err)) + return (strm->msg = ERR_MSG(err), (err)) /* To be used only when the state is known to be valid */ /* common constants */ @@ -78,16 +82,18 @@ #if defined(MSDOS) || (defined(WINDOWS) && !defined(WIN32)) # define OS_CODE 0x00 -# if defined(__TURBOC__) || defined(__BORLANDC__) -# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) - /* Allow compilation with ANSI keywords only enabled */ - void _Cdecl farfree( void *block ); - void *_Cdecl farmalloc( unsigned long nbytes ); -# else -# include +# ifndef Z_SOLO +# if defined(__TURBOC__) || defined(__BORLANDC__) +# if (__STDC__ == 1) && (defined(__LARGE__) || defined(__COMPACT__)) + /* Allow compilation with ANSI keywords only enabled */ + void _Cdecl farfree( void *block ); + void *_Cdecl farmalloc( unsigned long nbytes ); +# else +# include +# endif +# else /* MSC or DJGPP */ +# include # endif -# else /* MSC or DJGPP */ -# include # endif #endif @@ -107,18 +113,20 @@ #ifdef OS2 # define OS_CODE 0x06 -# ifdef M_I86 +# if defined(M_I86) && !defined(Z_SOLO) # include # endif #endif #if defined(MACOS) || defined(TARGET_OS_MAC) # define OS_CODE 0x07 -# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os -# include /* for fdopen */ -# else -# ifndef fdopen -# define fdopen(fd,mode) NULL /* No fdopen() */ +# ifndef Z_SOLO +# if defined(__MWERKS__) && __dest_os != __be_os && __dest_os != __win32_os +# include /* for fdopen */ +# else +# ifndef fdopen +# define fdopen(fd,mode) NULL /* No fdopen() */ +# endif # endif # endif #endif @@ -153,14 +161,15 @@ # endif #endif -#if defined(__BORLANDC__) +#if defined(__BORLANDC__) && !defined(MSDOS) #pragma warn -8004 #pragma warn -8008 #pragma warn -8066 #endif /* provide prototypes for these when building zlib without LFS */ -#if !defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0 +#if !defined(_WIN32) && \ + (!defined(_LARGEFILE64_SOURCE) || _LFS64_LARGEFILE-0 == 0) ZEXTERN uLong ZEXPORT adler32_combine64 OF((uLong, uLong, z_off_t)); ZEXTERN uLong ZEXPORT crc32_combine64 OF((uLong, uLong, z_off_t)); #endif @@ -177,42 +186,7 @@ /* functions */ -#if defined(STDC99) || (defined(__TURBOC__) && __TURBOC__ >= 0x550) -# ifndef HAVE_VSNPRINTF -# define HAVE_VSNPRINTF -# endif -#endif -#if defined(__CYGWIN__) -# ifndef HAVE_VSNPRINTF -# define HAVE_VSNPRINTF -# endif -#endif -#ifndef HAVE_VSNPRINTF -# ifdef MSDOS - /* vsnprintf may exist on some MS-DOS compilers (DJGPP?), - but for now we just assume it doesn't. */ -# define NO_vsnprintf -# endif -# ifdef __TURBOC__ -# define NO_vsnprintf -# endif -# ifdef WIN32 - /* In Win32, vsnprintf is available as the "non-ANSI" _vsnprintf. */ -# if !defined(vsnprintf) && !defined(NO_vsnprintf) -# if !defined(_MSC_VER) || ( defined(_MSC_VER) && _MSC_VER < 1500 ) -# define vsnprintf _vsnprintf -# endif -# endif -# endif -# ifdef __SASC -# define NO_vsnprintf -# endif -#endif -#ifdef VMS -# define NO_vsnprintf -#endif - -#if defined(pyr) +#if defined(pyr) || defined(Z_SOLO) # define NO_MEMCPY #endif #if defined(SMALL_MEDIUM) && !defined(_MSC_VER) && !defined(__SC__) @@ -261,14 +235,19 @@ # define Tracecv(c,x) #endif - -voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, - unsigned size)); -void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); +#ifndef Z_SOLO + voidpf ZLIB_INTERNAL zcalloc OF((voidpf opaque, unsigned items, + unsigned size)); + void ZLIB_INTERNAL zcfree OF((voidpf opaque, voidpf ptr)); +#endif #define ZALLOC(strm, items, size) \ (*((strm)->zalloc))((strm)->opaque, (items), (size)) #define ZFREE(strm, addr) (*((strm)->zfree))((strm)->opaque, (voidpf)(addr)) #define TRY_FREE(s, p) {if (p) ZFREE(s, p);} +/* Reverse the bytes in a 32-bit value */ +#define ZSWAP32(q) ((((q) >> 24) & 0xff) + (((q) >> 8) & 0xff00) + \ + (((q) & 0xff00) << 8) + (((q) & 0xff) << 24)) + #endif /* ZUTIL_H */ diff -Nru libgit2-0.20.0/docs/diff-internals.md libgit2-0.22.2/docs/diff-internals.md --- libgit2-0.20.0/docs/diff-internals.md 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/docs/diff-internals.md 2015-03-24 16:10:45.000000000 +0000 @@ -21,7 +21,7 @@ External Objects ---------------- -* `git_diff_options` repesents user choices about how a diff should be +* `git_diff_options` represents user choices about how a diff should be performed and is passed to most diff generating functions. * `git_diff_file` represents an item on one side of a possible delta * `git_diff_delta` represents a pair of items that have changed in some @@ -37,7 +37,7 @@ header that compactly represents that information, and it will have a number of lines of context surrounding added and deleted lines. * A `line` is simple a line of data along with a `git_diff_line_t` value - that tells how the data should be interpretted (e.g. context or added). + that tells how the data should be interpreted (e.g. context or added). Internal Objects ---------------- diff -Nru libgit2-0.20.0/.editorconfig libgit2-0.22.2/.editorconfig --- libgit2-0.20.0/.editorconfig 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/.editorconfig 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,14 @@ +; Check http://editorconfig.org/ for more informations +; Top-most EditorConfig file +root = true + +; tab indentation +[*] +indent_style = tab +trim_trailing_whitespace = true +insert_final_newline = true + +; 4-column space indentation +[*.md] +indent_style = space +indent_size = 4 diff -Nru libgit2-0.20.0/examples/add.c libgit2-0.22.2/examples/add.c --- libgit2-0.20.0/examples/add.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/add.c 2015-03-24 16:10:45.000000000 +0000 @@ -40,7 +40,7 @@ int options = 0, count = 0; struct print_payload payload = {0}; - git_threads_init(); + git_libgit2_init(); parse_opts(&options, &count, argc, argv); @@ -66,7 +66,7 @@ git_index_free(index); git_repository_free(repo); - git_threads_shutdown(); + git_libgit2_shutdown(); return 0; } @@ -78,7 +78,7 @@ git_status_t status; (void)matched_pathspec; - if (git_status_file(&status, p.repo, path)) { + if (git_status_file((unsigned int*)(&status), p.repo, path)) { return -1; //abort } diff -Nru libgit2-0.20.0/examples/blame.c libgit2-0.22.2/examples/blame.c --- libgit2-0.20.0/examples/blame.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/blame.c 2015-03-24 16:10:45.000000000 +0000 @@ -14,6 +14,11 @@ #include "common.h" +#ifdef _MSC_VER +#define snprintf sprintf_s +#define strcasecmp strcmpi +#endif + /** * This example demonstrates how to invoke the libgit2 blame API to roughly * simulate the output of `git blame` and a few of its command line arguments. @@ -26,12 +31,14 @@ int M; int start_line; int end_line; + int F; }; static void parse_opts(struct opts *o, int argc, char *argv[]); int main(int argc, char *argv[]) { int i, line, break_on_null_hunk; + size_t rawsize; char spec[1024] = {0}; struct opts o = {0}; const char *rawdata; @@ -42,11 +49,12 @@ git_blob *blob; git_object *obj; - git_threads_init(); + git_libgit2_init(); parse_opts(&o, argc, argv); if (o.M) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES; if (o.C) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES; + if (o.F) blameopts.flags |= GIT_BLAME_FIRST_PARENT; /** Open the repository. */ check_lg2(git_repository_open_ext(&repo, ".", 0, NULL), "Couldn't open repository", NULL); @@ -87,22 +95,24 @@ git_object_free(obj); rawdata = git_blob_rawcontent(blob); + rawsize = git_blob_rawsize(blob); /** Produce the output. */ line = 1; i = 0; break_on_null_hunk = 0; - while (i < git_blob_rawsize(blob)) { - const char *eol = strchr(rawdata+i, '\n'); + while (i < rawsize) { + const char *eol = memchr(rawdata + i, '\n', rawsize - i); char oid[10] = {0}; const git_blame_hunk *hunk = git_blame_get_hunk_byline(blame, line); - if (break_on_null_hunk && !hunk) break; + if (break_on_null_hunk && !hunk) + break; if (hunk) { - break_on_null_hunk = 1; char sig[128] = {0}; - + break_on_null_hunk = 1; + git_oid_tostr(oid, 10, &hunk->final_commit_id); snprintf(sig, 30, "%s <%s>", hunk->final_signature->name, hunk->final_signature->email); @@ -110,8 +120,8 @@ oid, sig, line, - (int)(eol-rawdata-i), - rawdata+i); + (int)(eol - rawdata - i), + rawdata + i); } i = (int)(eol - rawdata + 1); @@ -123,7 +133,7 @@ git_blame_free(blame); git_repository_free(repo); - git_threads_shutdown(); + git_libgit2_shutdown(); return 0; } @@ -141,6 +151,7 @@ fprintf(stderr, " -L process only line range n-m, counting from 1\n"); fprintf(stderr, " -M find line moves within and across files\n"); fprintf(stderr, " -C find line copies within and across files\n"); + fprintf(stderr, " -F follow only the first parent commits\n"); fprintf(stderr, "\n"); exit(1); } @@ -169,6 +180,8 @@ o->M = 1; else if (!strcasecmp(a, "-C")) o->C = 1; + else if (!strcasecmp(a, "-F")) + o->F = 1; else if (!strcasecmp(a, "-L")) { i++; a = argv[i]; if (i >= argc) fatal("Not enough arguments to -L", NULL); diff -Nru libgit2-0.20.0/examples/cat-file.c libgit2-0.22.2/examples/cat-file.c --- libgit2-0.20.0/examples/cat-file.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/cat-file.c 2015-03-24 16:10:45.000000000 +0000 @@ -42,7 +42,7 @@ static void show_blob(const git_blob *blob) { /* ? Does this need crlf filtering? */ - fwrite(git_blob_rawcontent(blob), git_blob_rawsize(blob), 1, stdout); + fwrite(git_blob_rawcontent(blob), (size_t)git_blob_rawsize(blob), 1, stdout); } /** Show each entry with its type, id and attributes */ @@ -127,7 +127,7 @@ git_object *obj = NULL; char oidstr[GIT_OID_HEXSZ + 1]; - git_threads_init(); + git_libgit2_init(); parse_opts(&o, argc, argv); @@ -190,7 +190,7 @@ git_object_free(obj); git_repository_free(repo); - git_threads_shutdown(); + git_libgit2_shutdown(); return 0; } diff -Nru libgit2-0.20.0/examples/common.c libgit2-0.22.2/examples/common.c --- libgit2-0.20.0/examples/common.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/common.c 2015-03-24 16:10:45.000000000 +0000 @@ -53,6 +53,33 @@ return strncmp(str, pfx, len) ? 0 : len; } +int optional_str_arg( + const char **out, struct args_info *args, const char *opt, const char *def) +{ + const char *found = args->argv[args->pos]; + size_t len = is_prefixed(found, opt); + + if (!len) + return 0; + + if (!found[len]) { + if (args->pos + 1 == args->argc) { + *out = def; + return 1; + } + args->pos += 1; + *out = args->argv[args->pos]; + return 1; + } + + if (found[len] == '=') { + *out = found + len + 1; + return 1; + } + + return 0; +} + int match_str_arg( const char **out, struct args_info *args, const char *opt) { @@ -156,7 +183,7 @@ const git_diff_line *l, void *p) { - FILE *fp = p; + FILE *fp = (FILE*)p; (void)d; (void)h; diff -Nru libgit2-0.20.0/examples/common.h libgit2-0.22.2/examples/common.h --- libgit2-0.20.0/examples/common.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/common.h 2015-03-24 16:10:45.000000000 +0000 @@ -49,6 +49,15 @@ /** * Check current `args` entry against `opt` string. If it matches * exactly, take the next arg as a string; if it matches as a prefix with + * an equal sign, take the remainder as a string; if value not supplied, + * default value `def` will be given. otherwise return 0. + */ +extern int optional_str_arg( + const char **out, struct args_info *args, const char *opt, const char *def); + +/** + * Check current `args` entry against `opt` string. If it matches + * exactly, take the next arg as a string; if it matches as a prefix with * an equal sign, take the remainder as a string; otherwise return 0. */ extern int match_str_arg( diff -Nru libgit2-0.20.0/examples/describe.c libgit2-0.22.2/examples/describe.c --- libgit2-0.20.0/examples/describe.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/examples/describe.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,162 @@ +/* + * libgit2 "describe" example - shows how to describe commits + * + * 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 + * . + */ + +#include "common.h" + +/** + * The following example partially reimplements the `git describe` command + * and some of its options. + * + * These commands should work: + + * - Describe HEAD with default options (`describe`) + * - Describe specified revision (`describe master~2`) + * - Describe specified revisions (`describe master~2 HEAD~3`) + * - Describe HEAD with dirty state suffix (`describe --dirty=*`) + * - Describe consider all refs (`describe --all master`) + * - Describe consider lightweight tags (`describe --tags temp-tag`) + * - Describe show non-default abbreviated size (`describe --abbrev=10`) + * - Describe always output the long format if matches a tag (`describe --long v1.0`) + * - Describe consider only tags of specified pattern (`describe --match v*-release`) + * - Describe show the fallback result (`describe --always`) + * - Describe follow only the first parent commit (`describe --first-parent`) + * + * The command line parsing logic is simplified and doesn't handle + * all of the use cases. + */ + +/** describe_options represents the parsed command line options */ +typedef struct { + const char **commits; + size_t commit_count; + git_describe_options describe_options; + git_describe_format_options format_options; +} describe_options; + +typedef struct args_info args_info; + +static void do_describe_single(git_repository *repo, describe_options *opts, const char *rev) +{ + git_object *commit; + git_describe_result *describe_result; + git_buf buf = { 0 }; + + if (rev) { + check_lg2(git_revparse_single(&commit, repo, rev), + "Failed to lookup rev", rev); + + check_lg2(git_describe_commit(&describe_result, commit, &opts->describe_options), + "Failed to describe rev", rev); + } + else + check_lg2(git_describe_workdir(&describe_result, repo, &opts->describe_options), + "Failed to describe workdir", NULL); + + check_lg2(git_describe_format(&buf, describe_result, &opts->format_options), + "Failed to format describe rev", rev); + + printf("%s\n", buf.ptr); +} + +static void do_describe(git_repository *repo, describe_options *opts) +{ + if (opts->commit_count == 0) + do_describe_single(repo, opts, NULL); + else + { + size_t i; + for (i = 0; i < opts->commit_count; i++) + do_describe_single(repo, opts, opts->commits[i]); + } +} + +static void print_usage(void) +{ + fprintf(stderr, "usage: see `git help describe`\n"); + exit(1); +} + +/** Parse command line arguments */ +static void parse_options(describe_options *opts, int argc, char **argv) +{ + args_info args = ARGS_INFO_INIT; + + for (args.pos = 1; args.pos < argc; ++args.pos) { + const char *curr = argv[args.pos]; + + if (curr[0] != '-') { + opts->commits = (const char **)realloc((void *)opts->commits, ++opts->commit_count); + opts->commits[opts->commit_count - 1] = curr; + } else if (!strcmp(curr, "--all")) { + opts->describe_options.describe_strategy = GIT_DESCRIBE_ALL; + } else if (!strcmp(curr, "--tags")) { + opts->describe_options.describe_strategy = GIT_DESCRIBE_TAGS; + } else if (!strcmp(curr, "--exact-match")) { + opts->describe_options.max_candidates_tags = 0; + } else if (!strcmp(curr, "--long")) { + opts->format_options.always_use_long_format = 1; + } else if (!strcmp(curr, "--always")) { + opts->describe_options.show_commit_oid_as_fallback = 1; + } else if (!strcmp(curr, "--first-parent")) { + opts->describe_options.only_follow_first_parent = 1; + } else if (optional_str_arg(&opts->format_options.dirty_suffix, &args, "--dirty", "-dirty")) { + } else if (match_int_arg((int *)&opts->format_options.abbreviated_size, &args, "--abbrev", 0)) { + } else if (match_int_arg((int *)&opts->describe_options.max_candidates_tags, &args, "--candidates", 0)) { + } else if (match_str_arg(&opts->describe_options.pattern, &args, "--match")) { + } + } + + if (opts->commit_count > 0) { + if (opts->format_options.dirty_suffix) + fatal("--dirty is incompatible with commit-ishes", NULL); + } + else { + if (!opts->format_options.dirty_suffix || !opts->format_options.dirty_suffix[0]) { + opts->commits = (const char **)malloc(++opts->commit_count); + opts->commits[0] = "HEAD"; + } + } +} + +/** Initialize describe_options struct */ +static void describe_options_init(describe_options *opts) +{ + memset(opts, 0, sizeof(*opts)); + + opts->commits = NULL; + opts->commit_count = 0; + git_describe_init_options(&opts->describe_options, GIT_DESCRIBE_OPTIONS_VERSION); + git_describe_init_format_options(&opts->format_options, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION); +} + +int main(int argc, char **argv) +{ + git_repository *repo; + describe_options opts; + + git_libgit2_init(); + + check_lg2(git_repository_open_ext(&repo, ".", 0, NULL), + "Could not open repository", NULL); + + describe_options_init(&opts); + parse_options(&opts, argc, argv); + + do_describe(repo, &opts); + + git_repository_free(repo); + git_libgit2_shutdown(); + + return 0; +} diff -Nru libgit2-0.20.0/examples/diff.c libgit2-0.22.2/examples/diff.c --- libgit2-0.20.0/examples/diff.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/diff.c 2015-03-24 16:10:45.000000000 +0000 @@ -33,12 +33,27 @@ "\033[36m" /* cyan */ }; +enum { + OUTPUT_DIFF = (1 << 0), + OUTPUT_STAT = (1 << 1), + OUTPUT_SHORTSTAT = (1 << 2), + OUTPUT_NUMSTAT = (1 << 3), + OUTPUT_SUMMARY = (1 << 4) +}; + +enum { + CACHE_NORMAL = 0, + CACHE_ONLY = 1, + CACHE_NONE = 2 +}; + /** The 'opts' struct captures all the various parsed command line options. */ struct opts { git_diff_options diffopts; git_diff_find_options findopts; int color; - int cached; + int cache; + int output; git_diff_format_t format; const char *treeish1; const char *treeish2; @@ -46,9 +61,11 @@ }; /** These functions are implemented at the end */ +static void usage(const char *message, const char *arg); static void parse_opts(struct opts *o, int argc, char *argv[]); static int color_printer( const git_diff_delta*, const git_diff_hunk*, const git_diff_line*, void*); +static void diff_print_stats(git_diff *diff, struct opts *o); int main(int argc, char *argv[]) { @@ -57,10 +74,10 @@ git_diff *diff; struct opts o = { GIT_DIFF_OPTIONS_INIT, GIT_DIFF_FIND_OPTIONS_INIT, - -1, 0, GIT_DIFF_FORMAT_PATCH, NULL, NULL, "." + -1, 0, 0, GIT_DIFF_FORMAT_PATCH, NULL, NULL, "." }; - git_threads_init(); + git_libgit2_init(); parse_opts(&o, argc, argv); @@ -74,6 +91,7 @@ * * <sha1> --cached * * <sha1> * * --cached + * * --nocache (don't use index data in diff at all) * * nothing * * Currently ranged arguments like <sha1>..<sha2> and <sha1>...<sha2> @@ -89,20 +107,23 @@ check_lg2( git_diff_tree_to_tree(&diff, repo, t1, t2, &o.diffopts), "diff trees", NULL); - else if (t1 && o.cached) - check_lg2( - git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts), - "diff tree to index", NULL); + else if (o.cache != CACHE_NORMAL) { + if (!t1) + treeish_to_tree(&t1, repo, "HEAD"); + + if (o.cache == CACHE_NONE) + check_lg2( + git_diff_tree_to_workdir(&diff, repo, t1, &o.diffopts), + "diff tree to working directory", NULL); + else + check_lg2( + git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts), + "diff tree to index", NULL); + } else if (t1) check_lg2( git_diff_tree_to_workdir_with_index(&diff, repo, t1, &o.diffopts), "diff tree to working directory", NULL); - else if (o.cached) { - treeish_to_tree(&t1, repo, "HEAD"); - check_lg2( - git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts), - "diff tree to index", NULL); - } else check_lg2( git_diff_index_to_workdir(&diff, repo, NULL, &o.diffopts), @@ -117,15 +138,23 @@ /** Generate simple output using libgit2 display helper. */ - if (o.color >= 0) - fputs(colors[0], stdout); + if (!o.output) + o.output = OUTPUT_DIFF; - check_lg2( - git_diff_print(diff, o.format, color_printer, &o.color), - "displaying diff", NULL); + if (o.output != OUTPUT_DIFF) + diff_print_stats(diff, &o); - if (o.color >= 0) - fputs(colors[0], stdout); + if ((o.output & OUTPUT_DIFF) != 0) { + if (o.color >= 0) + fputs(colors[0], stdout); + + check_lg2( + git_diff_print(diff, o.format, color_printer, &o.color), + "displaying diff", NULL); + + if (o.color >= 0) + fputs(colors[0], stdout); + } /** Cleanup before exiting. */ @@ -134,7 +163,7 @@ git_tree_free(t2); git_repository_free(repo); - git_threads_shutdown(); + git_libgit2_shutdown(); return 0; } @@ -200,16 +229,25 @@ usage("Only one or two tree identifiers can be provided", NULL); } else if (!strcmp(a, "-p") || !strcmp(a, "-u") || - !strcmp(a, "--patch")) + !strcmp(a, "--patch")) { + o->output |= OUTPUT_DIFF; o->format = GIT_DIFF_FORMAT_PATCH; + } else if (!strcmp(a, "--cached")) - o->cached = 1; - else if (!strcmp(a, "--name-only")) + o->cache = CACHE_ONLY; + else if (!strcmp(a, "--nocache")) + o->cache = CACHE_NONE; + else if (!strcmp(a, "--name-only") || !strcmp(a, "--format=name")) o->format = GIT_DIFF_FORMAT_NAME_ONLY; - else if (!strcmp(a, "--name-status")) + else if (!strcmp(a, "--name-status") || + !strcmp(a, "--format=name-status")) o->format = GIT_DIFF_FORMAT_NAME_STATUS; - else if (!strcmp(a, "--raw")) + else if (!strcmp(a, "--raw") || !strcmp(a, "--format=raw")) o->format = GIT_DIFF_FORMAT_RAW; + else if (!strcmp(a, "--format=diff-index")) { + o->format = GIT_DIFF_FORMAT_RAW; + o->diffopts.id_abbrev = 40; + } else if (!strcmp(a, "--color")) o->color = 0; else if (!strcmp(a, "--no-color")) @@ -228,6 +266,18 @@ o->diffopts.flags |= GIT_DIFF_INCLUDE_IGNORED; else if (!strcmp(a, "--untracked")) o->diffopts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; + else if (!strcmp(a, "--patience")) + o->diffopts.flags |= GIT_DIFF_PATIENCE; + else if (!strcmp(a, "--minimal")) + o->diffopts.flags |= GIT_DIFF_MINIMAL; + else if (!strcmp(a, "--stat")) + o->output |= OUTPUT_STAT; + else if (!strcmp(a, "--numstat")) + o->output |= OUTPUT_NUMSTAT; + else if (!strcmp(a, "--shortstat")) + o->output |= OUTPUT_SHORTSTAT; + else if (!strcmp(a, "--summary")) + o->output |= OUTPUT_SUMMARY; else if (match_uint16_arg( &o->findopts.rename_threshold, &args, "-M") || match_uint16_arg( @@ -249,9 +299,39 @@ &o->diffopts.context_lines, &args, "--unified") && !match_uint16_arg( &o->diffopts.interhunk_lines, &args, "--inter-hunk-context") && + !match_uint16_arg( + &o->diffopts.id_abbrev, &args, "--abbrev") && !match_str_arg(&o->diffopts.old_prefix, &args, "--src-prefix") && !match_str_arg(&o->diffopts.new_prefix, &args, "--dst-prefix") && !match_str_arg(&o->dir, &args, "--git-dir")) usage("Unknown command line argument", a); } } + +/** Display diff output with "--stat", "--numstat", or "--shortstat" */ +static void diff_print_stats(git_diff *diff, struct opts *o) +{ + git_diff_stats *stats; + git_buf b = GIT_BUF_INIT_CONST(NULL, 0); + git_diff_stats_format_t format = 0; + + check_lg2( + git_diff_get_stats(&stats, diff), "generating stats for diff", NULL); + + if (o->output & OUTPUT_STAT) + format |= GIT_DIFF_STATS_FULL; + if (o->output & OUTPUT_SHORTSTAT) + format |= GIT_DIFF_STATS_SHORT; + if (o->output & OUTPUT_NUMSTAT) + format |= GIT_DIFF_STATS_NUMBER; + if (o->output & OUTPUT_SUMMARY) + format |= GIT_DIFF_STATS_INCLUDE_SUMMARY; + + check_lg2( + git_diff_stats_to_buf(&b, stats, format, 80), "formatting stats", NULL); + + fputs(b.ptr, stdout); + + git_buf_free(&b); + git_diff_stats_free(stats); +} diff -Nru libgit2-0.20.0/examples/for-each-ref.c libgit2-0.22.2/examples/for-each-ref.c --- libgit2-0.20.0/examples/for-each-ref.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/examples/for-each-ref.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,46 @@ +#include +#include +#include "common.h" + +static int show_ref(git_reference *ref, void *data) +{ + git_repository *repo = data; + git_reference *resolved = NULL; + char hex[GIT_OID_HEXSZ+1]; + const git_oid *oid; + git_object *obj; + + if (git_reference_type(ref) == GIT_REF_SYMBOLIC) + check_lg2(git_reference_resolve(&resolved, ref), + "Unable to resolve symbolic reference", + git_reference_name(ref)); + + oid = git_reference_target(resolved ? resolved : ref); + git_oid_fmt(hex, oid); + hex[GIT_OID_HEXSZ] = 0; + check_lg2(git_object_lookup(&obj, repo, oid, GIT_OBJ_ANY), + "Unable to lookup object", hex); + + printf("%s %-6s\t%s\n", + hex, + git_object_type2string(git_object_type(obj)), + git_reference_name(ref)); + + if (resolved) + git_reference_free(resolved); + return 0; +} + +int main(int argc, char **argv) +{ + git_repository *repo; + + if (argc != 1 || argv[1] /* silence -Wunused-parameter */) + fatal("Sorry, no for-each-ref options supported yet", NULL); + + check_lg2(git_repository_open(&repo, "."), + "Could not open repository", NULL); + check_lg2(git_reference_foreach(repo, show_ref, repo), + "Could not iterate over references", NULL); + return 0; +} diff -Nru libgit2-0.20.0/examples/general.c libgit2-0.22.2/examples/general.c --- libgit2-0.20.0/examples/general.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/general.c 2015-03-24 16:10:45.000000000 +0000 @@ -47,11 +47,10 @@ // as an example. static void check_error(int error_code, const char *action) { + const git_error *error = giterr_last(); if (!error_code) return; - const git_error *error = giterr_last(); - printf("Error %d %s - %s\n", error_code, action, (error && error->message) ? error->message : "???"); @@ -60,6 +59,10 @@ int main (int argc, char** argv) { + // Initialize the library, this will set up any global state which libgit2 needs + // including threading and crypto + git_libgit2_init(); + // ### Opening the Repository // There are a couple of methods for opening a repository, this being the @@ -95,8 +98,8 @@ // Next we will convert the 20 byte raw SHA1 value to a human readable 40 // char hex value. printf("\n*Raw to Hex*\n"); - char out[41]; - out[40] = '\0'; + char out[GIT_OID_HEXSZ+1]; + out[GIT_OID_HEXSZ] = '\0'; // If you have a oid, you can easily get the hex value of the SHA as well. git_oid_fmt(out, &oid); @@ -123,7 +126,7 @@ // We can read raw objects directly from the object database if we have // the oid (SHA) of the object. This allows us to access objects without - // knowing thier type and inspect the raw bytes unparsed. + // knowing their type and inspect the raw bytes unparsed. error = git_odb_read(&obj, odb, &oid); check_error(error, "finding object in repository"); @@ -399,7 +402,7 @@ // Now that we have the starting point pushed onto the walker, we start // asking for ancestors. It will return them in the sorting order we asked - // for as commit oids. We can then lookup and parse the commited pointed + // for as commit oids. We can then lookup and parse the committed pointed // at by the returned OID; note that this operation is specially fast // since the raw contents of the commit object will be cached in memory while ((git_revwalk_next(&oid, walk)) == 0) { diff -Nru libgit2-0.20.0/examples/.gitignore libgit2-0.22.2/examples/.gitignore --- libgit2-0.20.0/examples/.gitignore 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/.gitignore 2015-03-24 16:10:45.000000000 +0000 @@ -8,4 +8,6 @@ log rev-parse status +tag +for-each-ref *.dSYM diff -Nru libgit2-0.20.0/examples/init.c libgit2-0.22.2/examples/init.c --- libgit2-0.20.0/examples/init.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/init.c 2015-03-24 16:10:45.000000000 +0000 @@ -46,7 +46,7 @@ git_repository *repo = NULL; struct opts o = { 1, 0, 0, 0, GIT_REPOSITORY_INIT_SHARED_UMASK, 0, 0, 0 }; - git_threads_init(); + git_libgit2_init(); parse_opts(&o, argc, argv); @@ -116,7 +116,7 @@ } git_repository_free(repo); - git_threads_shutdown(); + git_libgit2_shutdown(); return 0; } diff -Nru libgit2-0.20.0/examples/log.c libgit2-0.22.2/examples/log.c --- libgit2-0.20.0/examples/log.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/log.c 2015-03-24 16:10:45.000000000 +0000 @@ -54,8 +54,9 @@ int min_parents, max_parents; git_time_t before; git_time_t after; - char *author; - char *committer; + const char *author; + const char *committer; + const char *grep; }; /** utility functions that parse options and help with log output */ @@ -65,6 +66,9 @@ static void print_commit(git_commit *commit); static int match_with_parent(git_commit *commit, int i, git_diff_options *); +/** utility functions for filtering */ +static int signature_matches(const git_signature *sig, const char *filter); +static int log_message_matches(const git_commit *commit, const char *filter); int main(int argc, char *argv[]) { @@ -76,7 +80,7 @@ git_commit *commit = NULL; git_pathspec *ps = NULL; - git_threads_init(); + git_libgit2_init(); /** Parse arguments and set up revwalker. */ @@ -128,6 +132,15 @@ continue; } + if (!signature_matches(git_commit_author(commit), opt.author)) + continue; + + if (!signature_matches(git_commit_committer(commit), opt.committer)) + continue; + + if (!log_message_matches(commit, opt.grep)) + continue; + if (count++ < opt.skip) continue; if (opt.limit != -1 && printed++ >= opt.limit) { @@ -167,7 +180,33 @@ git_pathspec_free(ps); git_revwalk_free(s.walker); git_repository_free(s.repo); - git_threads_shutdown(); + git_libgit2_shutdown(); + + return 0; +} + +/** Determine if the given git_signature does not contain the filter text. */ +static int signature_matches(const git_signature *sig, const char *filter) { + if (filter == NULL) + return 1; + + if (sig != NULL && + (strstr(sig->name, filter) != NULL || + strstr(sig->email, filter) != NULL)) + return 1; + + return 0; +} + +static int log_message_matches(const git_commit *commit, const char *filter) { + const char *message = NULL; + + if (filter == NULL) + return 1; + + if ((message = git_commit_message(commit)) != NULL && + strstr(message, filter) != NULL) + return 1; return 0; } @@ -401,6 +440,12 @@ set_sorting(s, GIT_SORT_TOPOLOGICAL); else if (!strcmp(a, "--reverse")) set_sorting(s, GIT_SORT_REVERSE); + else if (match_str_arg(&opt->author, &args, "--author")) + /** Found valid --author */; + else if (match_str_arg(&opt->committer, &args, "--committer")) + /** Found valid --committer */; + else if (match_str_arg(&opt->grep, &args, "--grep")) + /** Found valid --grep */; else if (match_str_arg(&s->repodir, &args, "--git-dir")) /** Found git-dir. */; else if (match_int_arg(&opt->skip, &args, "--skip", 0)) diff -Nru libgit2-0.20.0/examples/Makefile libgit2-0.22.2/examples/Makefile --- libgit2-0.20.0/examples/Makefile 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/Makefile 2015-03-24 16:10:45.000000000 +0000 @@ -3,7 +3,8 @@ 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 +APPS = general showindex diff rev-list cat-file status log rev-parse init blame tag +APPS += for-each-ref all: $(APPS) diff -Nru libgit2-0.20.0/examples/network/clone.c libgit2-0.22.2/examples/network/clone.c --- libgit2-0.20.0/examples/network/clone.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/network/clone.c 2015-03-24 16:10:45.000000000 +0000 @@ -18,14 +18,20 @@ static void print_progress(const progress_data *pd) { - int network_percent = (100*pd->fetch_progress.received_objects) / pd->fetch_progress.total_objects; - int index_percent = (100*pd->fetch_progress.indexed_objects) / pd->fetch_progress.total_objects; + int network_percent = pd->fetch_progress.total_objects > 0 ? + (100*pd->fetch_progress.received_objects) / pd->fetch_progress.total_objects : + 0; + int index_percent = pd->fetch_progress.total_objects > 0 ? + (100*pd->fetch_progress.indexed_objects) / pd->fetch_progress.total_objects : + 0; + int checkout_percent = pd->total_steps > 0 ? (100 * pd->completed_steps) / pd->total_steps - : 0.f; + : 0; int kbytes = pd->fetch_progress.received_bytes / 1024; - if (pd->fetch_progress.received_objects == pd->fetch_progress.total_objects) { + if (pd->fetch_progress.total_objects && + pd->fetch_progress.received_objects == pd->fetch_progress.total_objects) { printf("Resolving deltas %d/%d\r", pd->fetch_progress.indexed_deltas, pd->fetch_progress.total_deltas); @@ -62,7 +68,7 @@ progress_data pd = {{0}}; git_repository *cloned_repo = NULL; git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT; - git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; const char *url = argv[1]; const char *path = argv[2]; int error; diff -Nru libgit2-0.20.0/examples/network/fetch.c libgit2-0.22.2/examples/network/fetch.c --- libgit2-0.20.0/examples/network/fetch.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/network/fetch.c 2015-03-24 16:10:45.000000000 +0000 @@ -36,7 +36,7 @@ // Download the packfile and index it. This function updates the // amount of received data and the indexer stats which lets you // inform the user about progress. - if (git_remote_download(data->remote) < 0) { + if (git_remote_download(data->remote, NULL) < 0) { data->ret = -1; goto exit; } @@ -90,14 +90,14 @@ // Figure out whether it's a named remote or a URL printf("Fetching %s for repo %p\n", argv[1], repo); - if (git_remote_load(&remote, repo, argv[1]) < 0) { - if (git_remote_create_inmemory(&remote, repo, NULL, argv[1]) < 0) + if (git_remote_lookup(&remote, repo, argv[1]) < 0) { + if (git_remote_create_anonymous(&remote, repo, argv[1], NULL) < 0) return -1; } // Set up the callbacks (only update_tips for now) callbacks.update_tips = &update_cb; - callbacks.progress = &progress_cb; + callbacks.sideband_progress = &progress_cb; callbacks.credentials = cred_acquire_cb; git_remote_set_callbacks(remote, &callbacks); @@ -155,8 +155,8 @@ // Update the references in the remote's namespace to point to the // right commits. This may be needed even if there was no packfile // to download, which can happen e.g. when the branches have been - // changed but all the neede objects are available locally. - if (git_remote_update_tips(remote) < 0) + // changed but all the needed objects are available locally. + if (git_remote_update_tips(remote, NULL, NULL) < 0) return -1; git_remote_free(remote); diff -Nru libgit2-0.20.0/examples/network/git2.c libgit2-0.22.2/examples/network/git2.c --- libgit2-0.20.0/examples/network/git2.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/network/git2.c 2015-03-24 16:10:45.000000000 +0000 @@ -54,7 +54,7 @@ exit(EXIT_FAILURE); } - git_threads_init(); + git_libgit2_init(); for (i = 0; commands[i].name != NULL; ++i) { if (!strcmp(argv[1], commands[i].name)) diff -Nru libgit2-0.20.0/examples/network/ls-remote.c libgit2-0.22.2/examples/network/ls-remote.c --- libgit2-0.20.0/examples/network/ls-remote.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/network/ls-remote.c 2015-03-24 16:10:45.000000000 +0000 @@ -13,9 +13,9 @@ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; // Find the remote by name - error = git_remote_load(&remote, repo, name); + error = git_remote_lookup(&remote, repo, name); if (error < 0) { - error = git_remote_create_inmemory(&remote, repo, NULL, name); + error = git_remote_create_anonymous(&remote, repo, name, NULL); if (error < 0) goto cleanup; } diff -Nru libgit2-0.20.0/examples/rev-list.c libgit2-0.22.2/examples/rev-list.c --- libgit2-0.20.0/examples/rev-list.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/rev-list.c 2015-03-24 16:10:45.000000000 +0000 @@ -22,9 +22,9 @@ git_repository *repo; git_revwalk *walk; git_oid oid; - char buf[41]; + char buf[GIT_OID_HEXSZ+1]; - git_threads_init(); + git_libgit2_init(); check_lg2(git_repository_open_ext(&repo, ".", 0, NULL), "opening repository", NULL); check_lg2(git_revwalk_new(&walk, repo), "allocating revwalk", NULL); @@ -32,11 +32,11 @@ while (!git_revwalk_next(&oid, walk)) { git_oid_fmt(buf, &oid); - buf[40] = '\0'; + buf[GIT_OID_HEXSZ] = '\0'; printf("%s\n", buf); } - git_threads_shutdown(); + git_libgit2_shutdown(); return 0; } diff -Nru libgit2-0.20.0/examples/rev-parse.c libgit2-0.22.2/examples/rev-parse.c --- libgit2-0.20.0/examples/rev-parse.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/rev-parse.c 2015-03-24 16:10:45.000000000 +0000 @@ -29,13 +29,13 @@ { struct parse_state ps = {0}; - git_threads_init(); + git_libgit2_init(); parse_opts(&ps, argc, argv); check_lg2(parse_revision(&ps), "Parsing", NULL); git_repository_free(ps.repo); - git_threads_shutdown(); + git_libgit2_shutdown(); return 0; } diff -Nru libgit2-0.20.0/examples/showindex.c libgit2-0.22.2/examples/showindex.c --- libgit2-0.20.0/examples/showindex.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/showindex.c 2015-03-24 16:10:45.000000000 +0000 @@ -20,10 +20,10 @@ unsigned int i, ecount; char *dir = "."; size_t dirlen; - char out[41]; - out[40] = '\0'; + char out[GIT_OID_HEXSZ+1]; + out[GIT_OID_HEXSZ] = '\0'; - git_threads_init(); + git_libgit2_init(); if (argc > 2) fatal("usage: showindex []", NULL); @@ -49,7 +49,7 @@ for (i = 0; i < ecount; ++i) { const git_index_entry *e = git_index_get_byindex(index, i); - git_oid_fmt(out, &e->oid); + git_oid_fmt(out, &e->id); printf("File Path: %s\n", e->path); printf(" Stage: %d\n", git_index_entry_stage(e)); @@ -64,7 +64,7 @@ } git_index_free(index); - git_threads_shutdown(); + git_libgit2_shutdown(); return 0; } diff -Nru libgit2-0.20.0/examples/status.c libgit2-0.22.2/examples/status.c --- libgit2-0.20.0/examples/status.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/examples/status.c 2015-03-24 16:10:45.000000000 +0000 @@ -13,6 +13,12 @@ */ #include "common.h" +#ifdef _WIN32 +# include +# define sleep(a) Sleep(a * 1000) +#else +# include +#endif /** * This example demonstrates the use of the libgit2 status APIs, @@ -44,19 +50,22 @@ #define MAX_PATHSPEC 8 struct opts { - git_status_options statusopt; - char *repodir; - char *pathspec[MAX_PATHSPEC]; - int npaths; - int format; - int zterm; - int showbranch; + git_status_options statusopt; + char *repodir; + char *pathspec[MAX_PATHSPEC]; + int npaths; + int format; + int zterm; + int showbranch; + int showsubmod; + int repeat; }; static void parse_opts(struct opts *o, int argc, char *argv[]); static void show_branch(git_repository *repo, int format); static void print_long(git_status_list *status); static void print_short(git_repository *repo, git_status_list *status); +static int print_submod(git_submodule *sm, const char *name, void *payload); int main(int argc, char *argv[]) { @@ -64,7 +73,7 @@ git_status_list *status; struct opts o = { GIT_STATUS_OPTIONS_INIT, "." }; - git_threads_init(); + git_libgit2_init(); o.statusopt.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; o.statusopt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | @@ -84,6 +93,10 @@ fatal("Cannot report status on bare repository", git_repository_path(repo)); +show_status: + if (o.repeat) + printf("\033[H\033[2J"); + /** * Run status on the repository * @@ -98,19 +111,31 @@ * about what results are presented. */ check_lg2(git_status_list_new(&status, repo, &o.statusopt), - "Could not get status", NULL); + "Could not get status", NULL); if (o.showbranch) show_branch(repo, o.format); + if (o.showsubmod) { + int submod_count = 0; + check_lg2(git_submodule_foreach(repo, print_submod, &submod_count), + "Cannot iterate submodules", o.repodir); + } + if (o.format == FORMAT_LONG) print_long(status); else print_short(repo, status); git_status_list_free(status); + + if (o.repeat) { + sleep(o.repeat); + goto show_status; + } + git_repository_free(repo); - git_threads_shutdown(); + git_libgit2_shutdown(); return 0; } @@ -363,22 +388,25 @@ unsigned int smstatus = 0; if (!git_submodule_lookup( - &sm, repo, s->index_to_workdir->new_file.path) && - !git_submodule_status(&smstatus, sm)) - { - if (smstatus & GIT_SUBMODULE_STATUS_WD_MODIFIED) - extra = " (new commits)"; - else if (smstatus & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) - extra = " (modified content)"; - else if (smstatus & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) - extra = " (modified content)"; - else if (smstatus & GIT_SUBMODULE_STATUS_WD_UNTRACKED) - extra = " (untracked content)"; + &sm, repo, s->index_to_workdir->new_file.path)) { + + if (!git_submodule_status(&smstatus, sm)) { + if (smstatus & GIT_SUBMODULE_STATUS_WD_MODIFIED) + extra = " (new commits)"; + else if (smstatus & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) + extra = " (modified content)"; + else if (smstatus & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) + extra = " (modified content)"; + else if (smstatus & GIT_SUBMODULE_STATUS_WD_UNTRACKED) + extra = " (untracked content)"; + } } + + git_submodule_free(sm); } /** - * Now that we have all the information, it's time to format the output. + * Now that we have all the information, format the output. */ if (s->head_to_index) { @@ -414,6 +442,21 @@ } } +static int print_submod(git_submodule *sm, const char *name, void *payload) +{ + int *count = payload; + (void)name; + + if (*count == 0) + printf("# Submodules\n"); + (*count)++; + + printf("# - submodule '%s' at %s\n", + git_submodule_name(sm), git_submodule_path(sm)); + + return 0; +} + /** * Parse options that git's status command supports. */ @@ -459,6 +502,12 @@ o->statusopt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES; else if (!strncmp(a, "--git-dir=", strlen("--git-dir="))) o->repodir = a + strlen("--git-dir="); + else if (!strcmp(a, "--repeat")) + o->repeat = 10; + else if (match_int_arg(&o->repeat, &args, "--repeat", 0)) + /* okay */; + else if (!strcmp(a, "--list-submodules")) + o->showsubmod = 1; else check_lg2(-1, "Unsupported option", a); } diff -Nru libgit2-0.20.0/examples/tag.c libgit2-0.22.2/examples/tag.c --- libgit2-0.20.0/examples/tag.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/examples/tag.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,319 @@ +/* + * libgit2 "tag" example - shows how to list, create and delete tags + * + * 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 + * . + */ + +#include "common.h" + +/** + * The following example partially reimplements the `git tag` command + * and some of its options. + * + * These commands should work: + + * - Tag name listing (`tag`) + * - Filtered tag listing with messages (`tag -n3 -l "v0.1*"`) + * - Lightweight tag creation (`tag test v0.18.0`) + * - Tag creation (`tag -a -m "Test message" test v0.18.0`) + * - Tag deletion (`tag -d test`) + * + * The command line parsing logic is simplified and doesn't handle + * all of the use cases. + */ + +/** tag_options represents the parsed command line options */ +typedef struct { + const char *message; + const char *pattern; + const char *tag_name; + const char *target; + int num_lines; + int force; +} tag_options; + +/** tag_state represents the current program state for dragging around */ +typedef struct { + git_repository *repo; + tag_options *opts; +} tag_state; + +/** An action to execute based on the command line arguments */ +typedef void (*tag_action)(tag_state *state); +typedef struct args_info args_info; + +static void check(int result, const char *message) +{ + if (result) fatal(message, NULL); +} + +/** Tag listing: Print individual message lines */ +static void print_list_lines(const char *message, const tag_state *state) +{ + const char *msg = message; + int num = state->opts->num_lines - 1; + + if (!msg) return; + + /** first line - headline */ + while(*msg && *msg != '\n') printf("%c", *msg++); + + /** skip over new lines */ + while(*msg && *msg == '\n') msg++; + + printf("\n"); + + /** print just headline? */ + if (num == 0) return; + if (*msg && msg[1]) printf("\n"); + + /** print individual commit/tag lines */ + while (*msg && num-- >= 2) { + printf(" "); + + while (*msg && *msg != '\n') printf("%c", *msg++); + + /** handle consecutive new lines */ + if (*msg && *msg == '\n' && msg[1] == '\n') { + num--; + printf("\n"); + } + while(*msg && *msg == '\n') msg++; + + printf("\n"); + } +} + +/** Tag listing: Print an actual tag object */ +static void print_tag(git_tag *tag, const tag_state *state) +{ + printf("%-16s", git_tag_name(tag)); + + if (state->opts->num_lines) { + const char *msg = git_tag_message(tag); + print_list_lines(msg, state); + } else { + printf("\n"); + } +} + +/** Tag listing: Print a commit (target of a lightweight tag) */ +static void print_commit(git_commit *commit, const char *name, + const tag_state *state) +{ + printf("%-16s", name); + + if (state->opts->num_lines) { + const char *msg = git_commit_message(commit); + print_list_lines(msg, state); + } else { + printf("\n"); + } +} + +/** Tag listing: Fallback, should not happen */ +static void print_name(const char *name) +{ + printf("%s\n", name); +} + +/** Tag listing: Lookup tags based on ref name and dispatch to print */ +static int each_tag(const char *name, tag_state *state) +{ + git_repository *repo = state->repo; + git_object *obj; + + check_lg2(git_revparse_single(&obj, repo, name), + "Failed to lookup rev", name); + + switch (git_object_type(obj)) { + case GIT_OBJ_TAG: + print_tag((git_tag *) obj, state); + break; + case GIT_OBJ_COMMIT: + print_commit((git_commit *) obj, name, state); + break; + default: + print_name(name); + } + + git_object_free(obj); + return 0; +} + +static void action_list_tags(tag_state *state) +{ + const char *pattern = state->opts->pattern; + git_strarray tag_names = {0}; + size_t i; + + check_lg2(git_tag_list_match(&tag_names, pattern ? pattern : "*", state->repo), + "Unable to get list of tags", NULL); + + for(i = 0; i < tag_names.count; i++) { + each_tag(tag_names.strings[i], state); + } + + git_strarray_free(&tag_names); +} + +static void action_delete_tag(tag_state *state) +{ + tag_options *opts = state->opts; + git_object *obj; + git_buf abbrev_oid = {0}; + + check(!opts->tag_name, "Name required"); + + check_lg2(git_revparse_single(&obj, state->repo, opts->tag_name), + "Failed to lookup rev", opts->tag_name); + + check_lg2(git_object_short_id(&abbrev_oid, obj), + "Unable to get abbreviated OID", opts->tag_name); + + check_lg2(git_tag_delete(state->repo, opts->tag_name), + "Unable to delete tag", opts->tag_name); + + printf("Deleted tag '%s' (was %s)\n", opts->tag_name, abbrev_oid.ptr); + + git_buf_free(&abbrev_oid); + git_object_free(obj); +} + +static void action_create_lighweight_tag(tag_state *state) +{ + git_repository *repo = state->repo; + tag_options *opts = state->opts; + git_oid oid; + git_object *target; + + check(!opts->tag_name, "Name required"); + + if (!opts->target) opts->target = "HEAD"; + + check(!opts->target, "Target required"); + + check_lg2(git_revparse_single(&target, repo, opts->target), + "Unable to resolve spec", opts->target); + + check_lg2(git_tag_create_lightweight(&oid, repo, opts->tag_name, + target, opts->force), "Unable to create tag", NULL); + + git_object_free(target); +} + +static void action_create_tag(tag_state *state) +{ + git_repository *repo = state->repo; + tag_options *opts = state->opts; + git_signature *tagger; + git_oid oid; + git_object *target; + + check(!opts->tag_name, "Name required"); + check(!opts->message, "Message required"); + + if (!opts->target) opts->target = "HEAD"; + + check_lg2(git_revparse_single(&target, repo, opts->target), + "Unable to resolve spec", opts->target); + + check_lg2(git_signature_default(&tagger, repo), + "Unable to create signature", NULL); + + check_lg2(git_tag_create(&oid, repo, opts->tag_name, + target, tagger, opts->message, opts->force), "Unable to create tag", NULL); + + git_object_free(target); + git_signature_free(tagger); +} + +static void print_usage(void) +{ + fprintf(stderr, "usage: see `git help tag`\n"); + exit(1); +} + +/** Parse command line arguments and choose action to run when done */ +static void parse_options(tag_action *action, tag_options *opts, int argc, char **argv) +{ + args_info args = ARGS_INFO_INIT; + *action = &action_list_tags; + + for (args.pos = 1; args.pos < argc; ++args.pos) { + const char *curr = argv[args.pos]; + + if (curr[0] != '-') { + if (!opts->tag_name) + opts->tag_name = curr; + else if (!opts->target) + opts->target = curr; + else + print_usage(); + + if (*action != &action_create_tag) + *action = &action_create_lighweight_tag; + } else if (!strcmp(curr, "-n")) { + opts->num_lines = 1; + *action = &action_list_tags; + } else if (!strcmp(curr, "-a")) { + *action = &action_create_tag; + } else if (!strcmp(curr, "-f")) { + opts->force = 1; + } else if (match_int_arg(&opts->num_lines, &args, "-n", 0)) { + *action = &action_list_tags; + } else if (match_str_arg(&opts->pattern, &args, "-l")) { + *action = &action_list_tags; + } else if (match_str_arg(&opts->tag_name, &args, "-d")) { + *action = &action_delete_tag; + } else if (match_str_arg(&opts->message, &args, "-m")) { + *action = &action_create_tag; + } + } +} + +/** Initialize tag_options struct */ +static void tag_options_init(tag_options *opts) +{ + memset(opts, 0, sizeof(*opts)); + + opts->message = NULL; + opts->pattern = NULL; + opts->tag_name = NULL; + opts->target = NULL; + opts->num_lines = 0; + opts->force = 0; +} + +int main(int argc, char **argv) +{ + git_repository *repo; + tag_options opts; + tag_action action; + tag_state state; + + git_libgit2_init(); + + check_lg2(git_repository_open_ext(&repo, ".", 0, NULL), + "Could not open repository", NULL); + + tag_options_init(&opts); + parse_options(&action, &opts, argc, argv); + + state.repo = repo; + state.opts = &opts; + action(&state); + + git_repository_free(repo); + git_libgit2_shutdown(); + + return 0; +} diff -Nru libgit2-0.20.0/git.git-authors libgit2-0.22.2/git.git-authors --- libgit2-0.20.0/git.git-authors 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/git.git-authors 2015-03-24 16:10:45.000000000 +0000 @@ -28,9 +28,6 @@ # his/her consent on a patch-by-patch basis. # "???" means the person is a prominent contributor who has # not yet made his/her standpoint clear. -# "ign" means the authors consent is ignored for the purpose -# of libification. This is because the author has contributed -# to areas that aren't interesting for the library. # # Please try to keep the list alphabetically ordered. It will # help in case we get all 600-ish git.git authors on it. @@ -39,33 +36,39 @@ # but has otherwise not contributed to git.) # ok Adam Simpkins (http transport) +ok Adrian Johnson +ok Alexey Shumkin ok Andreas Ericsson ok Boyd Lynn Gerber +ok Brandon Casey ok Brian Downing ok Brian Gernhardt ok Christian Couder ok Daniel Barkalow ok Florian Forster +ok Gustaf Hendeby ok Holger Weiss ok Jeff King ok Johannes Schindelin ok Johannes Sixt +ask Jonathan Nieder ok Junio C Hamano ok Kristian Høgsberg ok Linus Torvalds ok Lukas Sandström ok Matthieu Moy -ign Mike McCormack (imap-send) +ok Michael Haggerty ok Nicolas Pitre ok Paolo Bonzini ok Paul Kocher ok Peter Hagervall +ok Petr Onderka ok Pierre Habouzit ok Pieter de Bie ok René Scharfe -ign Robert Shearman (imap-send) ok Sebastian Schuberth ok Shawn O. Pearce ok Steffen Prohaska ok Sven Verdoolaege +ask Thomas Rast (ok before 6-Oct-2013) ok Torsten Bögershausen diff -Nru libgit2-0.20.0/include/git2/annotated_commit.h libgit2-0.22.2/include/git2/annotated_commit.h --- libgit2-0.20.0/include/git2/annotated_commit.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/include/git2/annotated_commit.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,99 @@ +/* + * 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_git_annotated_commit_h__ +#define INCLUDE_git_annotated_commit_h__ + +#include "common.h" +#include "repository.h" +#include "types.h" + +/** + * @file git2/annotated_commit.h + * @brief Git annotated commit routines + * @defgroup git_annotated_commit Git annotated commit routines + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Creates a `git_annotated_commit` from the given reference. + * The resulting git_annotated_commit must be freed with + * `git_annotated_commit_free`. + * + * @param out pointer to store the git_annotated_commit result in + * @param repo repository that contains the given reference + * @param ref reference to use to lookup the git_annotated_commit + * @return 0 on success or error code + */ +GIT_EXTERN(int) git_annotated_commit_from_ref( + git_annotated_commit **out, + git_repository *repo, + const git_reference *ref); + +/** + * Creates a `git_annotated_commit` from the given fetch head data. + * The resulting git_annotated_commit must be freed with + * `git_annotated_commit_free`. + * + * @param out pointer to store the git_annotated_commit result in + * @param repo repository that contains the given commit + * @param branch_name name of the (remote) branch + * @param remote_url url of the remote + * @param oid the commit object id of the remote branch + * @return 0 on success or error code + */ +GIT_EXTERN(int) git_annotated_commit_from_fetchhead( + git_annotated_commit **out, + git_repository *repo, + const char *branch_name, + const char *remote_url, + const git_oid *id); + +/** + * Creates a `git_annotated_commit` from the given commit id. + * The resulting git_annotated_commit must be freed with + * `git_annotated_commit_free`. + * + * An annotated commit contains information about how it was + * looked up, which may be useful for functions like merge or + * rebase to provide context to the operation. For example, + * conflict files will include the name of the source or target + * branches being merged. It is therefore preferable to use the + * most specific function (eg `git_annotated_commit_from_ref`) + * instead of this one when that data is known. + * + * @param out pointer to store the git_annotated_commit result in + * @param repo repository that contains the given commit + * @param id the commit object id to lookup + * @return 0 on success or error code + */ +GIT_EXTERN(int) git_annotated_commit_lookup( + git_annotated_commit **out, + git_repository *repo, + const git_oid *id); + +/** + * Gets the commit ID that the given `git_annotated_commit` refers to. + * + * @param head the given annotated commit + * @return commit id + */ +GIT_EXTERN(const git_oid *) git_annotated_commit_id( + const git_annotated_commit *commit); + +/** + * Frees a `git_annotated_commit`. + * + * @param annotated_commit annotated commit to free + */ +GIT_EXTERN(void) git_annotated_commit_free( + git_annotated_commit *commit); + +/** @} */ +GIT_END_DECL +#endif diff -Nru libgit2-0.20.0/include/git2/attr.h libgit2-0.22.2/include/git2/attr.h --- libgit2-0.20.0/include/git2/attr.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/attr.h 2015-03-24 16:10:45.000000000 +0000 @@ -76,25 +76,28 @@ */ #define GIT_ATTR_HAS_VALUE(attr) (git_attr_value(attr) == GIT_ATTR_VALUE_T) +/** + * Possible states for an attribute + */ typedef enum { - GIT_ATTR_UNSPECIFIED_T = 0, - GIT_ATTR_TRUE_T, - GIT_ATTR_FALSE_T, - GIT_ATTR_VALUE_T, + GIT_ATTR_UNSPECIFIED_T = 0, /**< The attribute has been left unspecified */ + GIT_ATTR_TRUE_T, /**< The attribute has been set */ + GIT_ATTR_FALSE_T, /**< The attribute has been unset */ + GIT_ATTR_VALUE_T, /**< This attribute has a value */ } git_attr_t; -/* - * Return the value type for a given attribute. +/** + * Return the value type for a given attribute. * - * This can be either `TRUE`, `FALSE`, `UNSPECIFIED` (if the attribute - * was not set at all), or `VALUE`, if the attribute was set to - * an actual string. + * This can be either `TRUE`, `FALSE`, `UNSPECIFIED` (if the attribute + * was not set at all), or `VALUE`, if the attribute was set to an + * actual string. * - * If the attribute has a `VALUE` string, it can be accessed normally - * as a NULL-terminated C string. + * If the attribute has a `VALUE` string, it can be accessed normally + * as a NULL-terminated C string. * - * @param attr The attribute - * @return the value type for the attribute + * @param attr The attribute + * @return the value type for the attribute */ GIT_EXTERN(git_attr_t) git_attr_value(const char *attr); @@ -199,8 +202,9 @@ * only once per attribute name, even if there are multiple * rules for a given file. The highest priority rule will be * used. Return a non-zero value from this to stop looping. + * The value will be returned from `git_attr_foreach`. * @param payload Passed on as extra parameter to callback function. - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_attr_foreach( git_repository *repo, diff -Nru libgit2-0.20.0/include/git2/blame.h libgit2-0.22.2/include/git2/blame.h --- libgit2-0.20.0/include/git2/blame.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/blame.h 2015-03-24 16:10:45.000000000 +0000 @@ -40,6 +40,9 @@ * commit (like `git blame -CCC`). Implies SAME_COMMIT_COPIES. * NOT IMPLEMENTED. */ GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = (1<<3), + /** Restrict the search of commits to those reachable following only the + * first parents. */ + GIT_BLAME_FIRST_PARENT = (1<<4), } git_blame_flag_t; /** @@ -64,7 +67,6 @@ * - `max_line` is the last line in the file to blame. The default is the last * line of the file. */ - typedef struct git_blame_options { unsigned int version; @@ -80,6 +82,18 @@ #define GIT_BLAME_OPTIONS_INIT {GIT_BLAME_OPTIONS_VERSION} /** + * Initializes a `git_blame_options` with default values. Equivalent to + * creating an instance with GIT_BLAME_OPTIONS_INIT. + * + * @param opts The `git_blame_options` struct to initialize + * @param version Version of struct; pass `GIT_BLAME_OPTIONS_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_blame_init_options( + git_blame_options *opts, + unsigned int version); + +/** * Structure that represents a blame hunk. * * - `lines_in_hunk` is the number of lines in this hunk @@ -183,7 +197,7 @@ git_blame **out, git_blame *reference, const char *buffer, - uint32_t buffer_len); + size_t buffer_len); /** * Free memory allocated by git_blame_file or git_blame_buffer. diff -Nru libgit2-0.20.0/include/git2/blob.h libgit2-0.22.2/include/git2/blob.h --- libgit2-0.20.0/include/git2/blob.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/blob.h 2015-03-24 16:10:45.000000000 +0000 @@ -84,7 +84,7 @@ * time. * * @param blob pointer to the blob - * @return the pointer; NULL if the blob has no contents + * @return the pointer */ GIT_EXTERN(const void *) git_blob_rawcontent(const git_blob *blob); @@ -159,37 +159,32 @@ * Write a loose blob to the Object Database from a * provider of chunks of data. * - * Provided the `hintpath` parameter is filled, its value - * will help to determine what git filters should be applied - * to the object before it can be placed to the object database. + * If the `hintpath` parameter is filled, it will be used to determine + * what git filters should be applied to the object before it is written + * to the object database. * + * The implementation of the callback MUST respect the following rules: * - * The implementation of the callback has to respect the - * following rules: + * - `content` must be filled by the callback. The maximum number of + * bytes that the buffer can accept per call is defined by the + * `max_length` parameter. Allocation and freeing of the buffer will + * be taken care of by libgit2. * - * - `content` will have to be filled by the consumer. The maximum number - * of bytes that the buffer can accept per call is defined by the - * `max_length` parameter. Allocation and freeing of the buffer will be taken - * care of by the function. + * - The `callback` must return the number of bytes that have been + * written to the `content` buffer. * - * - The callback is expected to return the number of bytes - * that `content` have been filled with. - * - * - When there is no more data to stream, the callback should - * return 0. This will prevent it from being invoked anymore. - * - * - When an error occurs, the callback should return -1. + * - When there is no more data to stream, `callback` should return + * 0. This will prevent it from being invoked anymore. * + * - If an error occurs, the callback should return a negative value. + * This value will be returned to the caller. * * @param id Return the id of the written blob - * - * @param repo repository where the blob will be written. - * This repository can be bare or not. - * - * @param hintpath if not NULL, will help selecting the filters - * to apply onto the content of the blob to be created. - * - * @return 0 or an error code + * @param repo Repository where the blob will be written. + * This repository can be bare or not. + * @param hintpath If not NULL, will be used to select data filters + * to apply onto the content of the blob to be created. + * @return 0 or error code (from either libgit2 or callback function) */ GIT_EXTERN(int) git_blob_create_fromchunks( git_oid *id, @@ -201,26 +196,27 @@ /** * Write an in-memory buffer to the ODB as a blob * - * @param oid return the oid of the written blob + * @param id return the id of the written blob * @param repo repository where to blob will be written * @param buffer data to be written into the blob * @param len length of the data * @return 0 or an error code */ -GIT_EXTERN(int) git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len); +GIT_EXTERN(int) git_blob_create_frombuffer( + git_oid *id, git_repository *repo, const void *buffer, size_t len); /** * Determine if the blob content is most certainly binary or not. * * The heuristic used to guess if a file is binary is taken from core git: * Searching for NUL bytes and looking for a reasonable ratio of printable - * to non-printable characters among the first 4000 bytes. + * to non-printable characters among the first 8000 bytes. * * @param blob The blob which content should be analyzed * @return 1 if the content of the blob is detected * as binary; 0 otherwise. */ -GIT_EXTERN(int) git_blob_is_binary(git_blob *blob); +GIT_EXTERN(int) git_blob_is_binary(const git_blob *blob); /** @} */ GIT_END_DECL diff -Nru libgit2-0.20.0/include/git2/branch.h libgit2-0.22.2/include/git2/branch.h --- libgit2-0.20.0/include/git2/branch.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/branch.h 2015-03-24 16:10:45.000000000 +0000 @@ -43,6 +43,12 @@ * * @param force Overwrite existing branch. * + * @param signature The identity that will used to populate the reflog entry + * + * @param log_message The one line long message to be appended to the reflog. + * If NULL, the default is "Branch: created"; if you want something more + * useful, provide a message. + * * @return 0, GIT_EINVALIDSPEC or an error code. * A proper reference is written in the refs/heads namespace * pointing to the provided target commit. @@ -52,7 +58,9 @@ git_repository *repo, const char *branch_name, const git_commit *target, - int force); + int force, + const git_signature *signature, + const char *log_message); /** * Delete an existing branch reference. @@ -76,7 +84,7 @@ * @param repo Repository where to find the branches. * @param list_flags Filtering flags for the branch * listing. Valid values are GIT_BRANCH_LOCAL, GIT_BRANCH_REMOTE - * or a combination of the two. + * or GIT_BRANCH_ALL. * * @return 0 on success or an error code */ @@ -115,13 +123,19 @@ * * @param force Overwrite existing branch. * + * @param signature The identity that will used to populate the reflog entry + * + * @param log_message The one line long message to be appended to the reflog + * * @return 0 on success, GIT_EINVALIDSPEC or an error code. */ GIT_EXTERN(int) git_branch_move( git_reference **out, git_reference *branch, const char *new_branch_name, - int force); + int force, + const git_signature *signature, + const char *log_message); /** * Lookup a branch by its name in a repository. @@ -165,8 +179,9 @@ * @return 0 on success; otherwise an error code (e.g., if the * ref is no local or remote branch). */ -GIT_EXTERN(int) git_branch_name(const char **out, - git_reference *ref); +GIT_EXTERN(int) git_branch_name( + const char **out, + const git_reference *ref); /** * Return the reference supporting the remote tracking branch, @@ -182,7 +197,7 @@ */ GIT_EXTERN(int) git_branch_upstream( git_reference **out, - git_reference *branch); + const git_reference *branch); /** * Set the upstream configuration for a given local branch @@ -200,25 +215,20 @@ * Return the name of the reference supporting the remote tracking branch, * given the name of a local branch reference. * - * @param tracking_branch_name_out The user-allocated buffer which will be - * filled with the name of the reference. Pass NULL if you just want to - * get the needed size of the name of the reference as the output value. - * - * @param buffer_size Size of the `out` buffer in bytes. + * @param out Pointer to the user-allocated git_buf which will be + * filled with the name of the reference. * * @param repo the repository where the branches live * - * @param canonical_branch_name name of the local branch. + * @param refname reference name of the local branch. * - * @return number of characters in the reference name - * including the trailing NUL byte; GIT_ENOTFOUND when no remote tracking - * reference exists, otherwise an error code. + * @return 0, GIT_ENOTFOUND when no remote tracking reference exists, + * otherwise an error code. */ GIT_EXTERN(int) git_branch_upstream_name( - char *tracking_branch_name_out, - size_t buffer_size, + git_buf *out, git_repository *repo, - const char *canonical_branch_name); + const char *refname); /** * Determine if the current local branch is pointed at by HEAD. @@ -229,33 +239,38 @@ * error code otherwise. */ GIT_EXTERN(int) git_branch_is_head( - git_reference *branch); + const git_reference *branch); /** * Return the name of remote that the remote tracking branch belongs to. * - * @param remote_name_out The user-allocated buffer which will be - * filled with the name of the remote. Pass NULL if you just want to - * get the needed size of the name of the remote as the output value. - * - * @param buffer_size Size of the `out` buffer in bytes. + * @param out Pointer to the user-allocated git_buf which will be filled with the name of the remote. * * @param repo The repository where the branch lives. * * @param canonical_branch_name name of the remote tracking branch. * - * @return Number of characters in the reference name - * including the trailing NUL byte; GIT_ENOTFOUND + * @return 0, GIT_ENOTFOUND * when no remote matching remote was found, * GIT_EAMBIGUOUS when the branch maps to several remotes, * otherwise an error code. */ GIT_EXTERN(int) git_branch_remote_name( - char *remote_name_out, - size_t buffer_size, + git_buf *out, git_repository *repo, const char *canonical_branch_name); + +/** + * Retrieve the name fo the upstream remote of a local branch + * + * @param buf the buffer into which to write the name + * @param repo the repository in which to look + * @param refname the full name of the branch + * @return 0 or an error code + */ + GIT_EXTERN(int) git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname); + /** @} */ GIT_END_DECL #endif diff -Nru libgit2-0.20.0/include/git2/buffer.h libgit2-0.22.2/include/git2/buffer.h --- libgit2-0.20.0/include/git2/buffer.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/buffer.h 2015-03-24 16:10:45.000000000 +0000 @@ -74,7 +74,7 @@ /** * Resize the buffer allocation to make more space. * - * This will attempt to grow the buffer to accomodate the target size. + * This will attempt to grow the buffer to accommodate the target size. * * If the buffer refers to memory that was not allocated by libgit2 (i.e. * the `asize` field is zero), then `ptr` will be replaced with a newly @@ -105,6 +105,22 @@ GIT_EXTERN(int) git_buf_set( git_buf *buffer, const void *data, size_t datalen); +/** +* Check quickly if buffer looks like it contains binary data +* +* @param buf Buffer to check +* @return 1 if buffer looks like non-text data +*/ +GIT_EXTERN(int) git_buf_is_binary(const git_buf *buf); + +/** +* Check quickly if buffer contains a NUL byte +* +* @param buf Buffer to check +* @return 1 if buffer contains a NUL byte +*/ +GIT_EXTERN(int) git_buf_contains_nul(const git_buf *buf); + GIT_END_DECL /** @} */ diff -Nru libgit2-0.20.0/include/git2/checkout.h libgit2-0.22.2/include/git2/checkout.h --- libgit2-0.20.0/include/git2/checkout.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/checkout.h 2015-03-24 16:10:45.000000000 +0000 @@ -43,17 +43,17 @@ * In between those are `GIT_CHECKOUT_SAFE` and `GIT_CHECKOUT_SAFE_CREATE` * both of which only make modifications that will not lose changes. * - * | target == baseline | target != baseline | - * ---------------------|-----------------------|----------------------| - * workdir == baseline | no action | create, update, or | - * | | delete file | - * ---------------------|-----------------------|----------------------| - * workdir exists and | no action | conflict (notify | - * is != baseline | notify dirty MODIFIED | and cancel checkout) | - * ---------------------|-----------------------|----------------------| - * workdir missing, | create if SAFE_CREATE | create file | - * baseline present | notify dirty DELETED | | - * ---------------------|-----------------------|----------------------| + * | target == baseline | target != baseline | + * ---------------------|-----------------------|----------------------| + * workdir == baseline | no action | create, update, or | + * | | delete file | + * ---------------------|-----------------------|----------------------| + * workdir exists and | no action | conflict (notify | + * is != baseline | notify dirty MODIFIED | and cancel checkout) | + * ---------------------|-----------------------|----------------------| + * workdir missing, | create if SAFE_CREATE | create file | + * baseline present | notify dirty DELETED | | + * ---------------------|-----------------------|----------------------| * * The only difference between SAFE and SAFE_CREATE is that SAFE_CREATE * will cause a file to be checked out if it is missing from the working @@ -99,9 +99,19 @@ * files with unmerged index entries instead. GIT_CHECKOUT_USE_OURS and * GIT_CHECKOUT_USE_THEIRS to proceed with the checkout using either the * stage 2 ("ours") or stage 3 ("theirs") version of files in the index. + * + * - GIT_CHECKOUT_DONT_OVERWRITE_IGNORED prevents ignored files from being + * overwritten. Normally, files that are ignored in the working directory + * are not considered "precious" and may be overwritten if the checkout + * target contains that file. + * + * - GIT_CHECKOUT_DONT_REMOVE_EXISTING prevents checkout from removing + * files or folders that fold to the same name on case insensitive + * filesystems. This can cause files to retain their existing names + * and write through existing symbolic links. */ typedef enum { - GIT_CHECKOUT_NONE = 0, /** default is a dry run, no actual updates */ + GIT_CHECKOUT_NONE = 0, /**< default is a dry run, no actual updates */ /** Allow safe updates that cannot overwrite uncommitted data */ GIT_CHECKOUT_SAFE = (1u << 0), @@ -144,6 +154,18 @@ /** Ignore directories in use, they will be left empty */ GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES = (1u << 18), + /** Don't overwrite ignored files that exist in the checkout target */ + GIT_CHECKOUT_DONT_OVERWRITE_IGNORED = (1u << 19), + + /** Write normal merge files for conflicts */ + GIT_CHECKOUT_CONFLICT_STYLE_MERGE = (1u << 20), + + /** Include common ancestor data in diff3 format files for conflicts */ + GIT_CHECKOUT_CONFLICT_STYLE_DIFF3 = (1u << 21), + + /** Don't overwrite existing files or folders */ + GIT_CHECKOUT_DONT_REMOVE_EXISTING = (1u << 22), + /** * THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED */ @@ -174,7 +196,12 @@ * - GIT_CHECKOUT_NOTIFY_IGNORED notifies about ignored files. * * Returning a non-zero value from this callback will cancel the checkout. - * Notification callbacks are made prior to modifying any files on disk. + * The non-zero return value will be propagated back and returned by the + * git_checkout_... call. + * + * Notification callbacks are made prior to modifying any files on disk, + * so canceling on any notification will still happen prior to any files + * being modified. */ typedef enum { GIT_CHECKOUT_NOTIFY_NONE = 0, @@ -206,26 +233,26 @@ /** * Checkout options structure * - * Zero out for defaults. Initialize with `GIT_CHECKOUT_OPTS_INIT` macro to + * Zero out for defaults. Initialize with `GIT_CHECKOUT_OPTIONS_INIT` macro to * correctly set the `version` field. E.g. * - * git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + * git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; */ -typedef struct git_checkout_opts { +typedef struct git_checkout_options { unsigned int version; - unsigned int checkout_strategy; /** default will be a dry run */ + unsigned int checkout_strategy; /**< default will be a dry run */ - int disable_filters; /** don't apply filters like CRLF conversion */ - unsigned int dir_mode; /** default is 0755 */ - unsigned int file_mode; /** default is 0644 or 0755 as dictated by blob */ - int file_open_flags; /** default is O_CREAT | O_TRUNC | O_WRONLY */ + int disable_filters; /**< don't apply filters like CRLF conversion */ + unsigned int dir_mode; /**< default is 0755 */ + unsigned int file_mode; /**< default is 0644 or 0755 as dictated by blob */ + int file_open_flags; /**< default is O_CREAT | O_TRUNC | O_WRONLY */ - unsigned int notify_flags; /** see `git_checkout_notify_t` above */ + unsigned int notify_flags; /**< see `git_checkout_notify_t` above */ git_checkout_notify_cb notify_cb; void *notify_payload; - /* Optional callback to notify the consumer of checkout progress. */ + /** Optional callback to notify the consumer of checkout progress. */ git_checkout_progress_cb progress_cb; void *progress_payload; @@ -235,16 +262,29 @@ */ git_strarray paths; - git_tree *baseline; /** expected content of workdir, defaults to HEAD */ + git_tree *baseline; /**< expected content of workdir, defaults to HEAD */ - const char *target_directory; /** alternative checkout path to workdir */ + const char *target_directory; /**< alternative checkout path to workdir */ - const char *our_label; /** the name of the "our" side of conflicts */ - const char *their_label; /** the name of the "their" side of conflicts */ -} git_checkout_opts; + const char *ancestor_label; /**< the name of the common ancestor side of conflicts */ + const char *our_label; /**< the name of the "our" side of conflicts */ + const char *their_label; /**< the name of the "their" side of conflicts */ +} git_checkout_options; -#define GIT_CHECKOUT_OPTS_VERSION 1 -#define GIT_CHECKOUT_OPTS_INIT {GIT_CHECKOUT_OPTS_VERSION} +#define GIT_CHECKOUT_OPTIONS_VERSION 1 +#define GIT_CHECKOUT_OPTIONS_INIT {GIT_CHECKOUT_OPTIONS_VERSION} + +/** +* Initializes a `git_checkout_options` with default values. Equivalent to +* creating an instance with GIT_CHECKOUT_OPTIONS_INIT. +* +* @param opts the `git_checkout_options` struct to initialize. +* @param version Version of struct; pass `GIT_CHECKOUT_OPTIONS_VERSION` +* @return Zero on success; -1 on failure. +*/ +GIT_EXTERN(int) git_checkout_init_options( + git_checkout_options *opts, + unsigned int version); /** * Updates files in the index and the working tree to match the content of @@ -252,13 +292,13 @@ * * @param repo repository to check out (must be non-bare) * @param opts specifies checkout options (may be NULL) - * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing - * branch, GIT_ERROR otherwise (use giterr_last for information - * about the error) + * @return 0 on success, GIT_EUNBORNBRANCH if HEAD points to a non + * existing branch, non-zero value returned by `notify_cb`, or + * other error code < 0 (use giterr_last for error details) */ GIT_EXTERN(int) git_checkout_head( git_repository *repo, - const git_checkout_opts *opts); + const git_checkout_options *opts); /** * Updates files in the working tree to match the content of the index. @@ -266,13 +306,13 @@ * @param repo repository into which to check out (must be non-bare) * @param index index to be checked out (or NULL to use repository index) * @param opts specifies checkout options (may be NULL) - * @return 0 on success, GIT_ERROR otherwise (use giterr_last for information - * about the error) + * @return 0 on success, non-zero return value from `notify_cb`, or error + * code < 0 (use giterr_last for error details) */ GIT_EXTERN(int) git_checkout_index( git_repository *repo, git_index *index, - const git_checkout_opts *opts); + const git_checkout_options *opts); /** * Updates files in the index and working tree to match the content of the @@ -282,13 +322,13 @@ * @param treeish a commit, tag or tree which content will be used to update * the working directory (or NULL to use HEAD) * @param opts specifies checkout options (may be NULL) - * @return 0 on success, GIT_ERROR otherwise (use giterr_last for information - * about the error) + * @return 0 on success, non-zero return value from `notify_cb`, or error + * code < 0 (use giterr_last for error details) */ GIT_EXTERN(int) git_checkout_tree( git_repository *repo, const git_object *treeish, - const git_checkout_opts *opts); + const git_checkout_options *opts); /** @} */ GIT_END_DECL diff -Nru libgit2-0.20.0/include/git2/cherrypick.h libgit2-0.22.2/include/git2/cherrypick.h --- libgit2-0.20.0/include/git2/cherrypick.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/include/git2/cherrypick.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,90 @@ +/* + * 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_git_cherrypick_h__ +#define INCLUDE_git_cherrypick_h__ + +#include "common.h" +#include "types.h" +#include "merge.h" + +/** + * @file git2/cherrypick.h + * @brief Git cherry-pick routines + * @defgroup git_cherrypick Git cherry-pick routines + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Cherry-pick options + */ +typedef struct { + unsigned int version; + + /** For merge commits, the "mainline" is treated as the parent. */ + unsigned int mainline; + + git_merge_options merge_opts; /*< Options for the merging */ + git_checkout_options checkout_opts; /*< Options for the checkout */ +} git_cherrypick_options; + +#define GIT_CHERRYPICK_OPTIONS_VERSION 1 +#define GIT_CHERRYPICK_OPTIONS_INIT {GIT_CHERRYPICK_OPTIONS_VERSION, 0, GIT_MERGE_OPTIONS_INIT, GIT_CHECKOUT_OPTIONS_INIT} + +/** + * Initializes a `git_cherrypick_options` with default values. Equivalent to + * creating an instance with GIT_CHERRYPICK_OPTIONS_INIT. + * + * @param opts the `git_cherrypick_options` struct to initialize + * @param version Version of struct; pass `GIT_CHERRYPICK_OPTIONS_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_cherrypick_init_options( + git_cherrypick_options *opts, + unsigned int version); + +/** + * Cherry-picks the given commit against the given "our" commit, producing an + * index that reflects the result of the cherry-pick. + * + * The returned index must be freed explicitly with `git_index_free`. + * + * @param out pointer to store the index result in + * @param repo the repository that contains the given commits + * @param cherrypick_commit the commit to cherry-pick + * @param our_commit the commit to revert against (eg, HEAD) + * @param mainline the parent of the revert commit, if it is a merge + * @param merge_options the merge options (or null for defaults) + * @return zero on success, -1 on failure. + */ +GIT_EXTERN(int) git_cherrypick_commit( + git_index **out, + git_repository *repo, + git_commit *cherrypick_commit, + git_commit *our_commit, + unsigned int mainline, + const git_merge_options *merge_options); + +/** + * Cherry-pick the given commit, producing changes in the index and working directory. + * + * @param repo the repository to cherry-pick + * @param commit the commit to cherry-pick + * @param cherrypick_options the cherry-pick options (or null for defaults) + * @return zero on success, -1 on failure. + */ +GIT_EXTERN(int) git_cherrypick( + git_repository *repo, + git_commit *commit, + const git_cherrypick_options *cherrypick_options); + +/** @} */ +GIT_END_DECL + +#endif + diff -Nru libgit2-0.20.0/include/git2/clone.h libgit2-0.22.2/include/git2/clone.h --- libgit2-0.20.0/include/git2/clone.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/clone.h 2015-03-24 16:10:45.000000000 +0000 @@ -12,6 +12,7 @@ #include "indexer.h" #include "checkout.h" #include "remote.h" +#include "transport.h" /** @@ -24,78 +25,189 @@ GIT_BEGIN_DECL /** - * Clone options structure + * Options for bypassing the git-aware transport on clone. Bypassing + * it means that instead of a fetch, libgit2 will copy the object + * database directory instead of figuring out what it needs, which is + * faster. If possible, it will hardlink the files to save space. + */ +typedef enum { + /** + * Auto-detect (default), libgit2 will bypass the git-aware + * transport for local paths, but use a normal fetch for + * `file://` urls. + */ + GIT_CLONE_LOCAL_AUTO, + /** + * Bypass the git-aware transport even for a `file://` url. + */ + GIT_CLONE_LOCAL, + /** + * Do no bypass the git-aware transport + */ + GIT_CLONE_NO_LOCAL, + /** + * Bypass the git-aware transport, but do not try to use + * hardlinks. + */ + GIT_CLONE_LOCAL_NO_LINKS, +} git_clone_local_t; + +/** + * The signature of a function matching git_remote_create, with an additional + * void* as a callback payload. * - * Use zeros to indicate default settings. It's easiest to use the - * `GIT_CLONE_OPTIONS_INIT` macro: + * Callers of git_clone may provide a function matching this signature to override + * the remote creation and customization process during a clone operation. * - * git_clone_options opts = GIT_CLONE_OPTIONS_INIT; + * @param out the resulting remote + * @param repo the repository in which to create the remote + * @param name the remote's name + * @param url the remote's url + * @param payload an opaque payload + * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code + */ +typedef int (*git_remote_create_cb)( + git_remote **out, + git_repository *repo, + const char *name, + const char *url, + void *payload); + +/** + * The signature of a function matchin git_repository_init, with an + * aditional void * as callback payload. * - * - `checkout_opts` is options for the checkout step. To disable checkout, - * set the `checkout_strategy` to GIT_CHECKOUT_DEFAULT. - * - `bare` should be set to zero to create a standard repo, non-zero for - * a bare repo - * - `ignore_cert_errors` should be set to 1 if errors validating the remote host's - * certificate should be ignored. - * - * ** "origin" remote options: ** - * - `remote_name` is the name given to the "origin" remote. The default is - * "origin". - * - `checkout_branch` gives the name of the branch to checkout. NULL means - * use the remote's HEAD. + * Callers of git_clone my provide a function matching this signature + * to override the repository creation and customization process + * during a clone operation. + * + * @param out the resulting repository + * @param path path in which to create the repository + * @param bare whether the repository is bare. This is the value from the clone options + * @param payload payload specified by the options + * @return 0, or a negative value to indicate error */ +typedef int (*git_repository_create_cb)( + git_repository **out, + const char *path, + int bare, + void *payload); +/** + * Clone options structure + * + * Use the GIT_CLONE_OPTIONS_INIT to get the default settings, like this: + * + * git_clone_options opts = GIT_CLONE_OPTIONS_INIT; + */ typedef struct git_clone_options { unsigned int version; - git_checkout_opts checkout_opts; + /** + * These options are passed to the checkout step. To disable + * checkout, set the `checkout_strategy` to + * `GIT_CHECKOUT_NONE`. Generally you will want the use + * GIT_CHECKOUT_SAFE_CREATE to create all files in the working + * directory for the newly cloned repository. + */ + git_checkout_options checkout_opts; + + /** + * Callbacks to use for reporting fetch progress, and for acquiring + * credentials in the event they are needed. This parameter is ignored if + * the remote_cb parameter is set; if you provide a remote creation + * callback, then you have the opportunity to configure remote callbacks in + * provided function. + */ git_remote_callbacks remote_callbacks; + /** + * Set to zero (false) to create a standard repo, or non-zero + * for a bare repo + */ int bare; - int ignore_cert_errors; - const char *remote_name; + + /** + * Whether to use a fetch or copy the object database. + */ + git_clone_local_t local; + + /** + * The name of the branch to checkout. NULL means use the + * remote's default branch. + */ const char* checkout_branch; + + /** + * The identity used when updating the reflog. NULL means to + * use the default signature using the config. + */ + git_signature *signature; + + /** + * A callback used to create the new repository into which to + * clone. If NULL, the 'bare' field will be used to determine + * whether to create a bare repository. + */ + git_repository_create_cb repository_cb; + + /** + * An opaque payload to pass to the git_repository creation callback. + * This parameter is ignored unless repository_cb is non-NULL. + */ + void *repository_cb_payload; + + /** + * A callback used to create the git_remote, prior to its being + * used to perform the clone operation. See the documentation for + * git_remote_create_cb for details. This parameter may be NULL, + * indicating that git_clone should provide default behavior. + */ + git_remote_create_cb remote_cb; + + /** + * An opaque payload to pass to the git_remote creation callback. + * This parameter is ignored unless remote_cb is non-NULL. + */ + void *remote_cb_payload; } git_clone_options; #define GIT_CLONE_OPTIONS_VERSION 1 -#define GIT_CLONE_OPTIONS_INIT {GIT_CLONE_OPTIONS_VERSION, {GIT_CHECKOUT_OPTS_VERSION, GIT_CHECKOUT_SAFE_CREATE}, GIT_REMOTE_CALLBACKS_INIT} +#define GIT_CLONE_OPTIONS_INIT {GIT_CLONE_OPTIONS_VERSION, {GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE_CREATE}, GIT_REMOTE_CALLBACKS_INIT} + +/** + * Initializes a `git_clone_options` with default values. Equivalent to + * creating an instance with GIT_CLONE_OPTIONS_INIT. + * + * @param opts The `git_clone_options` struct to initialize + * @param version Version of struct; pass `GIT_CLONE_OPTIONS_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_clone_init_options( + git_clone_options *opts, + unsigned int version); /** * Clone a remote repository. * - * This version handles the simple case. If you'd like to create the - * repository or remote with non-default settings, you can create and - * configure them and then use `git_clone_into()`. + * By default this creates its repository and initial remote to match + * git's defaults. You can use the options in the callback to + * customize how these are created. * * @param out pointer that will receive the resulting repository object * @param url the remote repository to clone * @param local_path local directory to clone to - * @param options configuration options for the clone. If NULL, the function - * works as though GIT_OPTIONS_INIT were passed. - * @return 0 on success, GIT_ERROR otherwise (use giterr_last for information - * about the error) + * @param options configuration options for the clone. If NULL, the + * function works as though GIT_OPTIONS_INIT were passed. + * @return 0 on success, any non-zero return value from a callback + * function, or a negative value to indicate an error (use + * `giterr_last` for a detailed error message) */ GIT_EXTERN(int) git_clone( - git_repository **out, - const char *url, - const char *local_path, - const git_clone_options *options); - -/** - * Clone into a repository - * - * After creating the repository and remote and configuring them for - * paths and callbacks respectively, you can call this function to - * perform the clone operation and optionally checkout files. - * - * @param repo the repository to use - * @param remote the remote repository to clone from - * @param co_opts options to use during checkout - * @param branch the branch to checkout after the clone, pass NULL for the remote's - * default branch - * @return 0 on success or an error code - */ -GIT_EXTERN(int) git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch); + git_repository **out, + const char *url, + const char *local_path, + const git_clone_options *options); /** @} */ GIT_END_DECL diff -Nru libgit2-0.20.0/include/git2/commit.h libgit2-0.22.2/include/git2/commit.h --- libgit2-0.20.0/include/git2/commit.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/commit.h 2015-03-24 16:10:45.000000000 +0000 @@ -117,6 +117,17 @@ GIT_EXTERN(const char *) git_commit_message_raw(const git_commit *commit); /** + * Get the short "summary" of the git commit message. + * + * The returned message is the summary of the commit, comprising the + * first paragraph of the message with whitespace trimmed and squashed. + * + * @param commit a previously loaded commit. + * @return the summary of a commit or NULL on error + */ +GIT_EXTERN(const char *) git_commit_summary(git_commit *commit); + +/** * Get the commit time (i.e. committer time) of a commit. * * @param commit a previously loaded commit. @@ -231,8 +242,8 @@ /** * Create new commit in the repository from a list of `git_object` pointers * - * The message will not be cleaned up automatically. You can do that with - * the `git_message_prettify()` function. + * The message will **not** be cleaned up automatically. You can do that + * with the `git_message_prettify()` function. * * @param id Pointer in which to store the OID of the newly created commit * @@ -243,7 +254,8 @@ * is not direct, it will be resolved to a direct reference. * Use "HEAD" to update the HEAD of the current branch and * make it point to this commit. If the reference doesn't - * exist yet, it will be created. + * exist yet, it will be created. If it does exist, the first + * parent must be the tip of this branch. * * @param author Signature with author and author time of commit * @@ -280,20 +292,20 @@ const char *message_encoding, const char *message, const git_tree *tree, - int parent_count, + size_t parent_count, const git_commit *parents[]); /** * Create new commit in the repository using a variable argument list. * - * The message will be cleaned up from excess whitespace and it will be made - * sure that the last line ends with a '\n'. + * The message will **not** be cleaned up automatically. You can do that + * with the `git_message_prettify()` function. * * The parents for the commit are specified as a variable list of pointers * to `const git_commit *`. Note that this is a convenience method which may * not be safe to export for certain languages or compilers * - * All other parameters remain the same at `git_commit_create()`. + * All other parameters remain the same as `git_commit_create()`. * * @see git_commit_create */ @@ -306,9 +318,40 @@ const char *message_encoding, const char *message, const git_tree *tree, - int parent_count, + size_t parent_count, ...); +/** + * Amend an existing commit by replacing only non-NULL values. + * + * This creates a new commit that is exactly the same as the old commit, + * except that any non-NULL values will be updated. The new commit has + * the same parents as the old commit. + * + * The `update_ref` value works as in the regular `git_commit_create()`, + * updating the ref to point to the newly rewritten commit. If you want + * to amend a commit that is not currently the tip of the branch and then + * rewrite the following commits to reach a ref, pass this as NULL and + * update the rest of the commit chain and ref separately. + * + * Unlike `git_commit_create()`, the `author`, `committer`, `message`, + * `message_encoding`, and `tree` parameters can be NULL in which case this + * will use the values from the original `commit_to_amend`. + * + * All parameters have the same meanings as in `git_commit_create()`. + * + * @see git_commit_create + */ +GIT_EXTERN(int) git_commit_amend( + git_oid *id, + const git_commit *commit_to_amend, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_tree *tree); + /** @} */ GIT_END_DECL #endif diff -Nru libgit2-0.20.0/include/git2/common.h libgit2-0.22.2/include/git2/common.h --- libgit2-0.20.0/include/git2/common.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/common.h 2015-03-24 16:10:45.000000000 +0000 @@ -37,13 +37,6 @@ # define GIT_EXTERN(type) extern type #endif -/** Declare a function as always inlined. */ -#if defined(_MSC_VER) -# define GIT_INLINE(type) static __inline type -#else -# define GIT_INLINE(type) static inline type -#endif - /** Declare a function's takes printf style arguments. */ #ifdef __GNUC__ # define GIT_FORMAT_PRINTF(a,b) __attribute__((format (printf, a, b))) @@ -101,31 +94,41 @@ GIT_EXTERN(void) git_libgit2_version(int *major, int *minor, int *rev); /** - * Combinations of these values describe the capabilities of libgit2. + * Combinations of these values describe the features with which libgit2 + * was compiled */ typedef enum { - GIT_CAP_THREADS = ( 1 << 0 ), - GIT_CAP_HTTPS = ( 1 << 1 ), - GIT_CAP_SSH = ( 1 << 2 ), -} git_cap_t; + GIT_FEATURE_THREADS = (1 << 0), + GIT_FEATURE_HTTPS = (1 << 1), + GIT_FEATURE_SSH = (1 << 2), +} git_feature_t; /** * Query compile time options for libgit2. * - * @return A combination of GIT_CAP_* values. + * @return A combination of GIT_FEATURE_* values. * - * - GIT_CAP_THREADS + * - GIT_FEATURE_THREADS * Libgit2 was compiled with thread support. Note that thread support is * still to be seen as a 'work in progress' - basic object lookups are * believed to be threadsafe, but other operations may not be. * - * - GIT_CAP_HTTPS + * - GIT_FEATURE_HTTPS * Libgit2 supports the https:// protocol. This requires the openssl * library to be found when compiling libgit2. + * + * - GIT_FEATURE_SSH + * Libgit2 supports the SSH protocol for network operations. This requires + * the libssh2 library to be found when compiling libgit2 */ -GIT_EXTERN(int) git_libgit2_capabilities(void); - +GIT_EXTERN(int) git_libgit2_features(void); +/** + * Global library options + * + * These are used to select which global option to set or get and are + * used in `git_libgit2_opts()`. + */ typedef enum { GIT_OPT_GET_MWINDOW_SIZE, GIT_OPT_SET_MWINDOW_SIZE, @@ -138,7 +141,8 @@ GIT_OPT_ENABLE_CACHING, GIT_OPT_GET_CACHED_MEMORY, GIT_OPT_GET_TEMPLATE_PATH, - GIT_OPT_SET_TEMPLATE_PATH + GIT_OPT_SET_TEMPLATE_PATH, + GIT_OPT_SET_SSL_CERT_LOCATIONS, } git_libgit2_opt_t; /** @@ -163,12 +167,12 @@ * >Set the maximum amount of memory that can be mapped at any time * by the library * - * * opts(GIT_OPT_GET_SEARCH_PATH, int level, char *out, size_t len) + * * opts(GIT_OPT_GET_SEARCH_PATH, int level, git_buf *buf) * * > Get the search path for a given level of config data. "level" must * > be one of `GIT_CONFIG_LEVEL_SYSTEM`, `GIT_CONFIG_LEVEL_GLOBAL`, or * > `GIT_CONFIG_LEVEL_XDG`. The search path is written to the `out` - * > buffer up to size `len`. Returns GIT_EBUFS if buffer is too small. + * > buffer. * * * opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path) * @@ -197,7 +201,7 @@ * > across all repositories before libgit2 starts evicting objects * > from the cache. This is a soft limit, in that the library might * > briefly exceed it, but will start aggressively evicting objects - * > from cache when that happens. The default cache size is 256Mb. + * > from cache when that happens. The default cache size is 256MB. * * * opts(GIT_OPT_ENABLE_CACHING, int enabled) * @@ -212,11 +216,10 @@ * > Get the current bytes in cache and the maximum that would be * > allowed in the cache. * - * * opts(GIT_OPT_GET_TEMPLATE_PATH, char *out, size_t len) + * * opts(GIT_OPT_GET_TEMPLATE_PATH, git_buf *out) * * > Get the default template path. - * > The path is written to the `out` - * > buffer up to size `len`. Returns GIT_EBUFS if buffer is too small. + * > The path is written to the `out` buffer. * * * opts(GIT_OPT_SET_TEMPLATE_PATH, const char *path) * @@ -224,6 +227,17 @@ * > * > - `path` directory of template. * + * * opts(GIT_OPT_SET_SSL_CERT_LOCATIONS, const char *file, const char *path) + * + * > Set the SSL certificate-authority locations. + * > + * > - `file` is the location of a file containing several + * > certificates concatenated together. + * > - `path` is the location of a directory holding several + * > certificates, one per file. + * > + * > Either parameter may be `NULL`, but not both. + * * @param option Option key * @param ... value to set the option * @return 0 on success, <0 on failure diff -Nru libgit2-0.20.0/include/git2/config.h libgit2-0.22.2/include/git2/config.h --- libgit2-0.20.0/include/git2/config.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/config.h 2015-03-24 16:10:45.000000000 +0000 @@ -9,6 +9,7 @@ #include "common.h" #include "types.h" +#include "buffer.h" /** * @file git2/config.h @@ -54,15 +55,21 @@ GIT_CONFIG_HIGHEST_LEVEL = -1, } git_config_level_t; +/** + * An entry in a configuration file + */ typedef struct { - const char *name; - const char *value; - git_config_level_t level; + const char *name; /*< Name of the entry (normalised) */ + const char *value; /*< String value of the entry */ + git_config_level_t level; /*< Which config file this was found in */ } git_config_entry; typedef int (*git_config_foreach_cb)(const git_config_entry *, void *); typedef struct git_config_iterator git_config_iterator; +/** + * Config var type + */ typedef enum { GIT_CVAR_FALSE = 0, GIT_CVAR_TRUE = 1, @@ -70,6 +77,9 @@ GIT_CVAR_STRING } git_cvar_t; +/** + * Mapping from config variables to values. + */ typedef struct { git_cvar_t cvar_type; const char *str_match; @@ -90,11 +100,10 @@ * This method will not guess the path to the xdg compatible * config file (.config/git/config). * - * @param out Buffer to store the path in - * @param length size of the buffer in bytes - * @return 0 if a global configuration file has been found. Its path will be stored in `buffer`. + * @param out Pointer to a user-allocated git_buf in which to store the path + * @return 0 if a global configuration file has been found. Its path will be stored in `out`. */ -GIT_EXTERN(int) git_config_find_global(char *out, size_t length); +GIT_EXTERN(int) git_config_find_global(git_buf *out); /** * Locate the path to the global xdg compatible configuration file @@ -107,25 +116,23 @@ * may be used on any `git_config` call to load the * xdg compatible configuration file. * - * @param out Buffer to store the path in - * @param length size of the buffer in bytes + * @param out Pointer to a user-allocated git_buf in which to store the path * @return 0 if a xdg compatible configuration file has been - * found. Its path will be stored in `buffer`. + * found. Its path will be stored in `out`. */ -GIT_EXTERN(int) git_config_find_xdg(char *out, size_t length); +GIT_EXTERN(int) git_config_find_xdg(git_buf *out); /** * Locate the path to the system configuration file * * If /etc/gitconfig doesn't exist, it will look for * %PROGRAMFILES%\Git\etc\gitconfig. - - * @param out Buffer to store the path in - * @param length size of the buffer in bytes + * + * @param out Pointer to a user-allocated git_buf in which to store the path * @return 0 if a system configuration file has been - * found. Its path will be stored in `buffer`. + * found. Its path will be stored in `out`. */ -GIT_EXTERN(int) git_config_find_system(char *out, size_t length); +GIT_EXTERN(int) git_config_find_system(git_buf *out); /** * Open the global, XDG and system configuration files @@ -228,19 +235,21 @@ */ GIT_EXTERN(int) git_config_open_global(git_config **out, git_config *config); - /** - * Reload changed config files + * Create a snapshot of the configuration * - * A config file may be changed on disk out from under the in-memory - * config object. This function causes us to look for files that have - * been modified since we last loaded them and refresh the config with - * the latest information. + * Create a snapshot of the current state of a configuration, which + * allows you to look into a consistent view of the configuration for + * looking up complex values (e.g. a remote, submodule). * - * @param cfg The configuration to refresh + * The string returned when querying such a config object is valid + * until it is freed. + * + * @param out pointer in which to store the snapshot config object + * @param config configuration to snapshot * @return 0 or an error code */ -GIT_EXTERN(int) git_config_refresh(git_config *cfg); +GIT_EXTERN(int) git_config_snapshot(git_config **out, git_config *config); /** * Free the configuration and its associated memory and files @@ -270,7 +279,7 @@ * * All config files will be looked into, in the order of their * defined level. A higher level means a higher priority. The - * first occurence of the variable will be returned here. + * first occurrence of the variable will be returned here. * * @param out pointer to the variable where the value should be stored * @param cfg where to look for the variable @@ -314,7 +323,8 @@ * Get the value of a string config variable. * * The string is owned by the variable and should not be freed by the - * user. + * user. The pointer will be valid until the next operation on this + * config object. * * All config files will be looked into, in the order of their * defined level. A higher level means a higher priority. The @@ -355,6 +365,9 @@ /** * Return the current entry and advance the iterator * + * The pointers returned by this function are valid until the iterator + * is freed. + * * @param entry pointer to store the entry * @param iter the iterator * @return 0 or an error code. GIT_ITEROVER if the iteration has completed @@ -450,13 +463,16 @@ * * The callback receives the normalized name and value of each variable * in the config backend, and the data pointer passed to this function. - * As soon as one of the callback functions returns something other than 0, - * this function stops iterating and returns `GIT_EUSER`. + * If the callback returns a non-zero value, the function stops iterating + * and returns that value to the caller. + * + * The pointers passed to the callback are only valid as long as the + * iteration is ongoing. * * @param cfg where to get the variables from * @param callback the function to call on each variable * @param payload the data to pass to the callback - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_config_foreach( const git_config *cfg, @@ -493,6 +509,9 @@ * 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. + * * @param cfg where to get the variables from * @param regexp regular expression to match against config names * @param callback the function to call on each variable @@ -612,8 +631,8 @@ GIT_EXTERN(int) git_config_backend_foreach_match( git_config_backend *backend, const char *regexp, - int (*fn)(const git_config_entry *, void *), - void *data); + git_config_foreach_cb callback, + void *payload); /** @} */ diff -Nru libgit2-0.20.0/include/git2/describe.h libgit2-0.22.2/include/git2/describe.h --- libgit2-0.20.0/include/git2/describe.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/include/git2/describe.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,162 @@ +/* + * 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_git_describe_h__ +#define INCLUDE_git_describe_h__ + +#include "common.h" +#include "types.h" +#include "buffer.h" + +/** + * @file git2/describe.h + * @brief Git describing routines + * @defgroup git_describe Git describing routines + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Reference lookup strategy + * + * These behave like the --tags and --all optios to git-describe, + * namely they say to look for any reference in either refs/tags/ or + * refs/ respectively. + */ +typedef enum { + GIT_DESCRIBE_DEFAULT, + GIT_DESCRIBE_TAGS, + GIT_DESCRIBE_ALL, +} git_describe_strategy_t; + +/** + * Describe options structure + * + * Initialize with `GIT_DESCRIBE_OPTIONS_INIT` macro to correctly set + * the `version` field. E.g. + * + * git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; + */ +typedef struct git_describe_options { + unsigned int version; + + unsigned int max_candidates_tags; /** default: 10 */ + unsigned int describe_strategy; /** default: GIT_DESCRIBE_DEFAULT */ + const char *pattern; + /** + * When calculating the distance from the matching tag or + * reference, only walk down the first-parent ancestry. + */ + int only_follow_first_parent; + /** + * If no matching tag or reference is found, the describe + * operation would normally fail. If this option is set, it + * will instead fall back to showing the full id of the + * commit. + */ + int show_commit_oid_as_fallback; +} git_describe_options; + +#define GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS 10 +#define GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE 7 + +#define GIT_DESCRIBE_OPTIONS_VERSION 1 +#define GIT_DESCRIBE_OPTIONS_INIT { \ + GIT_DESCRIBE_OPTIONS_VERSION, \ + GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS, \ +} + +GIT_EXTERN(int) git_describe_init_options(git_describe_options *opts, unsigned int version); + +/** + * Options for formatting the describe string + */ +typedef struct { + unsigned int version; + + /** + * Size of the abbreviated commit id to use. This value is the + * lower bound for the length of the abbreviated string. The + * default is 7. + */ + unsigned int abbreviated_size; + + /** + * Set to use the long format even when a shorter name could be used. + */ + int always_use_long_format; + + /** + * If the workdir is dirty and this is set, this string will + * be appended to the description string. + */ + char *dirty_suffix; +} git_describe_format_options; + +#define GIT_DESCRIBE_FORMAT_OPTIONS_VERSION 1 +#define GIT_DESCRIBE_FORMAT_OPTIONS_INIT { \ + GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, \ + GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE, \ + } + +GIT_EXTERN(int) git_describe_init_format_options(git_describe_format_options *opts, unsigned int version); + +typedef struct git_describe_result git_describe_result; + +/** + * Describe a commit + * + * Perform the describe operation on the given committish object. + * + * @param result pointer to store the result. You must free this once + * you're done with it. + * @param committish a committish to describe + * @param opts the lookup options + */ +GIT_EXTERN(int) git_describe_commit( + git_describe_result **result, + git_object *committish, + git_describe_options *opts); + +/** + * Describe a commit + * + * Perform the describe operation on the current commit and the + * worktree. After peforming describe on HEAD, a status is run and the + * description is considered to be dirty if there are. + * + * @param result pointer to store the result. You must free this once + * you're done with it. + * @param repo the repository in which to perform the describe + * @param opts the lookup options + */ +GIT_EXTERN(int) git_describe_workdir( + git_describe_result **out, + git_repository *repo, + git_describe_options *opts); + +/** + * Print the describe result to a buffer + * + * @param result the result from `git_describe_commit()` or + * `git_describe_workdir()`. + * @param opt the formatting options + */ +GIT_EXTERN(int) git_describe_format( + git_buf *out, + const git_describe_result *result, + const git_describe_format_options *opts); + +/** + * Free the describe result. + */ +GIT_EXTERN(void) git_describe_result_free(git_describe_result *result); + +/** @} */ +GIT_END_DECL + +#endif diff -Nru libgit2-0.20.0/include/git2/diff.h libgit2-0.22.2/include/git2/diff.h --- libgit2-0.20.0/include/git2/diff.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/diff.h 2015-03-24 16:10:45.000000000 +0000 @@ -145,6 +145,19 @@ */ GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS = (1u << 14), + /** When diff finds a file in the working directory with stat + * information different from the index, but the OID ends up being the + * same, write the correct stat information into the index. Note: + * without this flag, diff will always leave the index untouched. + */ + GIT_DIFF_UPDATE_INDEX = (1u << 15), + + /** Include unreadable files in the diff */ + GIT_DIFF_INCLUDE_UNREADABLE = (1u << 16), + + /** Include unreadable files in the diff */ + GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED = (1u << 17), + /* * Options controlling how output will be generated */ @@ -180,6 +193,10 @@ /** Take extra time to find minimal diff */ GIT_DIFF_MINIMAL = (1 << 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_option_t; /** @@ -201,9 +218,9 @@ * considered reserved for internal or future use. */ typedef enum { - GIT_DIFF_FLAG_BINARY = (1u << 0), /** file(s) treated as binary data */ - GIT_DIFF_FLAG_NOT_BINARY = (1u << 1), /** file(s) treated as text data */ - GIT_DIFF_FLAG_VALID_OID = (1u << 2), /** `oid` value is known correct */ + GIT_DIFF_FLAG_BINARY = (1u << 0), /**< file(s) treated as binary data */ + GIT_DIFF_FLAG_NOT_BINARY = (1u << 1), /**< file(s) treated as text data */ + GIT_DIFF_FLAG_VALID_ID = (1u << 2), /**< `id` value is known correct */ } git_diff_flag_t; /** @@ -217,15 +234,16 @@ * DELETED pairs). */ typedef enum { - GIT_DELTA_UNMODIFIED = 0, /** no changes */ - GIT_DELTA_ADDED = 1, /** entry does not exist in old version */ - GIT_DELTA_DELETED = 2, /** entry does not exist in new version */ - GIT_DELTA_MODIFIED = 3, /** entry content changed between old and new */ - GIT_DELTA_RENAMED = 4, /** entry was renamed between old and new */ - GIT_DELTA_COPIED = 5, /** entry was copied from another old entry */ - GIT_DELTA_IGNORED = 6, /** entry is ignored item in workdir */ - GIT_DELTA_UNTRACKED = 7, /** entry is untracked item in workdir */ - GIT_DELTA_TYPECHANGE = 8, /** type of entry changed between old and new */ + GIT_DELTA_UNMODIFIED = 0, /**< no changes */ + GIT_DELTA_ADDED = 1, /**< entry does not exist in old version */ + GIT_DELTA_DELETED = 2, /**< entry does not exist in new version */ + GIT_DELTA_MODIFIED = 3, /**< entry content changed between old and new */ + GIT_DELTA_RENAMED = 4, /**< entry was renamed between old and new */ + GIT_DELTA_COPIED = 5, /**< entry was copied from another old entry */ + GIT_DELTA_IGNORED = 6, /**< entry is ignored item in workdir */ + GIT_DELTA_UNTRACKED = 7, /**< entry is untracked item in workdir */ + GIT_DELTA_TYPECHANGE = 8, /**< type of entry changed between old and new */ + GIT_DELTA_UNREADABLE = 9, /**< entry is unreadable */ } git_delta_t; /** @@ -250,7 +268,7 @@ * be restricted to one of the `git_filemode_t` values. */ typedef struct { - git_oid oid; + git_oid id; const char *path; git_off_t size; uint32_t flags; @@ -359,9 +377,9 @@ /* options controlling how to diff text is generated */ - uint16_t context_lines; /**< defaults to 3 */ - uint16_t interhunk_lines; /**< defaults to 0 */ - uint16_t oid_abbrev; /**< default 'core.abbrev' or 7 if unset */ + uint32_t context_lines; /**< defaults to 3 */ + uint32_t interhunk_lines; /**< defaults to 0 */ + uint16_t id_abbrev; /**< default 'core.abbrev' or 7 if unset */ git_off_t max_size; /**< defaults to 512MB */ const char *old_prefix; /**< defaults to "a" */ const char *new_prefix; /**< defaults to "b" */ @@ -377,6 +395,18 @@ {GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_DEFAULT, {NULL,0}, NULL, NULL, 3} /** + * Initializes a `git_diff_options` with default values. Equivalent to + * creating an instance with GIT_DIFF_OPTIONS_INIT. + * + * @param opts The `git_diff_options` struct to initialize + * @param version Version of struct; pass `GIT_DIFF_OPTIONS_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_diff_init_options( + git_diff_options *opts, + unsigned int version); + +/** * When iterating over a diff, callback that will be made per file. * * @param delta A pointer to the delta data for the file @@ -391,15 +421,14 @@ /** * Structure describing a hunk of a diff. */ -typedef struct git_diff_hunk git_diff_hunk; -struct git_diff_hunk { - 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[128]; /** Header text, NUL-byte terminated */ -}; +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[128]; /**< Header text, NUL-byte terminated */ +} git_diff_hunk; /** * When iterating over a diff, callback that will be made per hunk. @@ -439,16 +468,15 @@ /** * Structure describing a line (or data span) of a diff. */ -typedef struct git_diff_line git_diff_line; -struct git_diff_line { - char origin; /** A git_diff_line_t value */ - int old_lineno; /** Line number in old file or -1 for added line */ - int new_lineno; /** Line number in new file or -1 for deleted line */ - int num_lines; /** Number of newline characters in content */ - size_t content_len; /** Number of bytes of data */ - git_off_t content_offset; /** Offset in the original file to the content */ - const char *content; /** Pointer to diff text, not NUL-byte terminated */ -}; +typedef struct { + char origin; /**< A git_diff_line_t value */ + int old_lineno; /**< Line number in old file or -1 for added line */ + int new_lineno; /**< Line number in new file or -1 for deleted line */ + int num_lines; /**< Number of newline characters in content */ + size_t content_len; /**< Number of bytes of data */ + git_off_t content_offset; /**< Offset in the original file to the content */ + const char *content; /**< Pointer to diff text, not NUL-byte terminated */ +} git_diff_line; /** * When iterating over a diff, callback that will be made per text diff @@ -459,50 +487,83 @@ * of lines of file and hunk headers. */ typedef int (*git_diff_line_cb)( - const git_diff_delta *delta, /** delta that contains this data */ - const git_diff_hunk *hunk, /** hunk containing this data */ - const git_diff_line *line, /** line data */ - void *payload); /** user reference data */ + const git_diff_delta *delta, /**< delta that contains this data */ + const git_diff_hunk *hunk, /**< hunk containing this data */ + const git_diff_line *line, /**< line data */ + void *payload); /**< user reference data */ /** * Flags to control the behavior of diff rename/copy detection. */ typedef enum { - /** look for renames? (`--find-renames`) */ + /** Obey `diff.renames`. Overridden by any other GIT_DIFF_FIND_... flag. */ + GIT_DIFF_FIND_BY_CONFIG = 0, + + /** Look for renames? (`--find-renames`) */ GIT_DIFF_FIND_RENAMES = (1u << 0), - /** consider old side of modified for renames? (`--break-rewrites=N`) */ + + /** Consider old side of MODIFIED for renames? (`--break-rewrites=N`) */ GIT_DIFF_FIND_RENAMES_FROM_REWRITES = (1u << 1), - /** look for copies? (a la `--find-copies`) */ + /** Look for copies? (a la `--find-copies`). */ GIT_DIFF_FIND_COPIES = (1u << 2), - /** consider unmodified as copy sources? (`--find-copies-harder`) */ + + /** Consider UNMODIFIED as copy sources? (`--find-copies-harder`). + * + * For this to work correctly, use GIT_DIFF_INCLUDE_UNMODIFIED when + * the initial `git_diff` is being generated. + */ GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED = (1u << 3), - /** mark large rewrites for split (`--break-rewrites=/M`) */ + /** Mark significant rewrites for split (`--break-rewrites=/M`) */ GIT_DIFF_FIND_REWRITES = (1u << 4), - /** actually split large rewrites into delete/add pairs */ + /** Actually split large rewrites into delete/add pairs */ GIT_DIFF_BREAK_REWRITES = (1u << 5), - /** mark rewrites for split and break into delete/add pairs */ + /** Mark rewrites for split and break into delete/add pairs */ GIT_DIFF_FIND_AND_BREAK_REWRITES = (GIT_DIFF_FIND_REWRITES | GIT_DIFF_BREAK_REWRITES), - /** find renames/copies for untracked items in working directory */ + /** Find renames/copies for UNTRACKED items in working directory. + * + * For this to work correctly, use GIT_DIFF_INCLUDE_UNTRACKED when the + * initial `git_diff` is being generated (and obviously the diff must + * be against the working directory for this to make sense). + */ GIT_DIFF_FIND_FOR_UNTRACKED = (1u << 6), - /** turn on all finding features */ + /** Turn on all finding features. */ GIT_DIFF_FIND_ALL = (0x0ff), - /** measure similarity ignoring leading whitespace (default) */ + /** Measure similarity ignoring leading whitespace (default) */ GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE = 0, - /** measure similarity ignoring all whitespace */ + /** Measure similarity ignoring all whitespace */ GIT_DIFF_FIND_IGNORE_WHITESPACE = (1u << 12), - /** measure similarity including all data */ + /** Measure similarity including all data */ GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE = (1u << 13), - /** measure similarity only by comparing SHAs (fast and cheap) */ + /** Measure similarity only by comparing SHAs (fast and cheap) */ GIT_DIFF_FIND_EXACT_MATCH_ONLY = (1u << 14), - /** do not break rewrites unless they contribute to a rename */ + /** Do not break rewrites unless they contribute to a rename. + * + * Normally, GIT_DIFF_FIND_AND_BREAK_REWRITES will measure the self- + * similarity of modified files and split the ones that have changed a + * lot into a DELETE / ADD pair. Then the sides of that pair will be + * considered candidates for rename and copy detection. + * + * If you add this flag in and the split pair is *not* used for an + * actual rename or copy, then the modified record will be restored to + * a regular MODIFIED record instead of being split. + */ GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY = (1u << 15), + + /** Remove any UNMODIFIED deltas after find_similar is done. + * + * Using GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED to emulate the + * --find-copies-harder behavior requires building a diff with the + * GIT_DIFF_INCLUDE_UNMODIFIED flag. If you do not want UNMODIFIED + * records in the final result, pass this flag to have them removed. + */ + GIT_DIFF_FIND_REMOVE_UNMODIFIED = (1u << 16), } git_diff_find_t; /** @@ -543,7 +604,11 @@ typedef struct { unsigned int version; - /** Combination of git_diff_find_t values (default FIND_RENAMES) */ + /** + * Combination of git_diff_find_t values (default GIT_DIFF_FIND_BY_CONFIG). + * NOTE: if you don't explicitly set this, `diff.renames` could be set + * to false, resulting in `git_diff_find_similar` doing nothing. + */ uint32_t flags; /** Similarity to consider a file renamed (default 50) */ @@ -567,6 +632,18 @@ #define GIT_DIFF_FIND_OPTIONS_VERSION 1 #define GIT_DIFF_FIND_OPTIONS_INIT {GIT_DIFF_FIND_OPTIONS_VERSION} +/** + * Initializes a `git_diff_find_options` with default values. Equivalent to + * creating an instance with GIT_DIFF_FIND_OPTIONS_INIT. + * + * @param opts The `git_diff_find_options` struct to initialize + * @param version Version of struct; pass `GIT_DIFF_FIND_OPTIONS_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_diff_find_init_options( + git_diff_find_options *opts, + unsigned int version); + /** @name Diff Generator Functions * * These are the functions you would use to create (or destroy) a @@ -662,24 +739,17 @@ * The tree you provide will be used for the "old_file" side of the delta, * and the working directory will be used for the "new_file" side. * - * Please note: this is *NOT* the same as `git diff `. Running - * `git diff HEAD` or the like actually uses information from the index, - * along with the tree and working directory info. - * - * This function returns strictly the differences between the tree and the - * files contained in the working directory, regardless of the state of - * files in the index. It may come as a surprise, but there is no direct - * equivalent in core git. - * - * To emulate `git diff `, use `git_diff_tree_to_workdir_with_index` - * (or `git_diff_tree_to_index` and `git_diff_index_to_workdir`, then call - * `git_diff_merge` on the results). That will yield a `git_diff` that - * matches the git output. - * - * If this seems confusing, take the case of a file with a staged deletion - * where the file has then been put back into the working dir and modified. - * The tree-to-workdir diff for that file is 'modified', but core git would - * show status 'deleted' since there is a pending deletion in the index. + * This is not the same as `git diff ` or `git diff-index + * `. Those commands use information from the index, whereas this + * function strictly returns the differences between the tree and the files + * in the working directory, regardless of the state of the index. Use + * `git_diff_tree_to_workdir_with_index` to emulate those commands. + * + * To see difference between this and `git_diff_tree_to_workdir_with_index`, + * consider the example of a staged file deletion where the file has then + * been put back into the working dir and further modified. The + * tree-to-workdir diff for that file is 'modified', but `git diff` would + * show status 'deleted' since there is a staged delete. * * @param diff A pointer to a git_diff pointer that will be allocated. * @param repo The repository containing the tree. @@ -744,23 +814,6 @@ git_diff *diff, const git_diff_find_options *options); -/** - * Initialize diff options structure - * - * In most cases, you can probably just use `GIT_DIFF_OPTIONS_INIT` to - * initialize the diff options structure, but in some cases that is not - * going to work. You can call this function instead. Note that you - * must pass both a pointer to the structure to be initialized and the - * `GIT_DIFF_OPTIONS_VERSION` value from the header you compiled with. - * - * @param options Pointer to git_diff_options memory to be initialized - * @param version Should be `GIT_DIFF_OPTIONS_VERSION` - * @return 0 on success, negative on failure (such as unsupported version) - */ -GIT_EXTERN(int) git_diff_options_init( - git_diff_options *options, - unsigned int version); - /**@}*/ @@ -833,7 +886,7 @@ * files whose only changed is a file mode change. * * Returning a non-zero value from any of the callbacks will terminate - * the iteration and cause this return `GIT_EUSER`. + * the iteration and return the value to the user. * * @param diff A git_diff generated by one of the above functions. * @param file_cb Callback function to make per file in the diff. @@ -844,7 +897,7 @@ * same callback will be made for context lines, added, and * removed lines, and even for a deleted trailing newline. * @param payload Reference pointer that will be passed to your callbacks. - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_diff_foreach( git_diff *diff, @@ -881,13 +934,13 @@ * Iterate over a diff generating formatted text output. * * Returning a non-zero value from the callbacks will terminate the - * iteration and cause this return `GIT_EUSER`. + * iteration and return the non-zero value to the caller. * * @param diff A git_diff generated by one of the above functions. * @param format A git_diff_format_t value to pick the text format. * @param print_cb Callback to make per line of diff text. * @param payload Reference pointer that will be passed to your callback. - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_diff_print( git_diff *diff, @@ -927,7 +980,7 @@ * @param hunk_cb Callback for each hunk in diff; can be NULL * @param line_cb Callback for each line in diff; can be NULL * @param payload Payload passed to each callback function - * @return 0 on success, GIT_EUSER on non-zero callback return, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_diff_blobs( const git_blob *old_blob, @@ -962,7 +1015,7 @@ * @param hunk_cb Callback for each hunk in diff; can be NULL * @param line_cb Callback for each line in diff; can be NULL * @param payload Payload passed to each callback function - * @return 0 on success, GIT_EUSER on non-zero callback return, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_diff_blob_to_buffer( const git_blob *old_blob, @@ -976,6 +1029,214 @@ git_diff_line_cb line_cb, void *payload); +/** + * Directly run a diff between two buffers. + * + * Even more than with `git_diff_blobs`, comparing two buffer lacks + * context, so the `git_diff_file` parameters to the callbacks will be + * faked a la the rules for `git_diff_blobs()`. + * + * @param old_buffer Raw data for old side of diff, or NULL for empty + * @param old_len Length of the raw data for old side of the diff + * @param old_as_path Treat old buffer as if it had this filename; can be NULL + * @param new_buffer Raw data for new side of diff, or NULL for empty + * @param new_len Length of raw data for new side of diff + * @param new_as_path Treat buffer as if it had this filename; can be NULL + * @param options Options for diff, or NULL for default options + * @param file_cb Callback for "file"; made once if there is a diff; can be NULL + * @param hunk_cb Callback for each hunk in diff; can be NULL + * @param line_cb Callback for each line in diff; can be NULL + * @param payload Payload passed to each callback function + * @return 0 on success, non-zero callback return value, or error code + */ +GIT_EXTERN(int) git_diff_buffers( + const void *old_buffer, + size_t old_len, + const char *old_as_path, + const void *new_buffer, + size_t new_len, + const char *new_as_path, + const git_diff_options *options, + git_diff_file_cb file_cb, + git_diff_hunk_cb hunk_cb, + git_diff_line_cb line_cb, + void *payload); + +/** + * This is an opaque structure which is allocated by `git_diff_get_stats`. + * You are responsible for releasing the object memory when done, using the + * `git_diff_stats_free()` function. + */ +typedef struct git_diff_stats git_diff_stats; + +/** + * Formatting options for diff stats + */ +typedef enum { + /** No stats*/ + GIT_DIFF_STATS_NONE = 0, + + /** Full statistics, equivalent of `--stat` */ + GIT_DIFF_STATS_FULL = (1u << 0), + + /** Short statistics, equivalent of `--shortstat` */ + GIT_DIFF_STATS_SHORT = (1u << 1), + + /** Number statistics, equivalent of `--numstat` */ + GIT_DIFF_STATS_NUMBER = (1u << 2), + + /** Extended header information such as creations, renames and mode changes, equivalent of `--summary` */ + GIT_DIFF_STATS_INCLUDE_SUMMARY = (1u << 3), +} git_diff_stats_format_t; + +/** + * Accumlate diff statistics for all patches. + * + * @param out Structure containg the diff statistics. + * @param diff A git_diff generated by one of the above functions. + * @return 0 on success; non-zero on error + */ +GIT_EXTERN(int) git_diff_get_stats( + git_diff_stats **out, + git_diff *diff); + +/** + * Get the total number of files changed in a diff + * + * @param stats A `git_diff_stats` generated by one of the above functions. + * @return total number of files changed in the diff + */ +GIT_EXTERN(size_t) git_diff_stats_files_changed( + const git_diff_stats *stats); + +/** + * Get the total number of insertions in a diff + * + * @param stats A `git_diff_stats` generated by one of the above functions. + * @return total number of insertions in the diff + */ +GIT_EXTERN(size_t) git_diff_stats_insertions( + const git_diff_stats *stats); + +/** + * Get the total number of deletions in a diff + * + * @param stats A `git_diff_stats` generated by one of the above functions. + * @return total number of deletions in the diff + */ +GIT_EXTERN(size_t) git_diff_stats_deletions( + const git_diff_stats *stats); + +/** + * Print diff statistics to a `git_buf`. + * + * @param out buffer to store the formatted diff statistics in. + * @param stats A `git_diff_stats` generated by one of the above functions. + * @param format Formatting option. + * @param width Target width for output (only affects GIT_DIFF_STATS_FULL) + * @return 0 on success; non-zero on error + */ +GIT_EXTERN(int) git_diff_stats_to_buf( + git_buf *out, + const git_diff_stats *stats, + git_diff_stats_format_t format, + size_t width); + +/** + * Deallocate a `git_diff_stats`. + * + * @param stats The previously created statistics object; + * cannot be used after free. + */ +GIT_EXTERN(void) git_diff_stats_free(git_diff_stats *stats); + +/** + * Formatting options for diff e-mail generation + */ +typedef enum { + /** Normal patch, the default */ + GIT_DIFF_FORMAT_EMAIL_NONE = 0, + + /** Don't insert "[PATCH]" in the subject header*/ + GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER = (1 << 0), + +} git_diff_format_email_flags_t; + +/** + * Options for controlling the formatting of the generated e-mail. + */ +typedef struct { + unsigned int version; + + git_diff_format_email_flags_t flags; + + /** This patch number */ + size_t patch_no; + + /** Total number of patches in this series */ + size_t total_patches; + + /** id to use for the commit */ + const git_oid *id; + + /** Summary of the change */ + const char *summary; + + /** Author of the change */ + const git_signature *author; +} git_diff_format_email_options; + +#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION 1 +#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT {GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, 0, 1, 1, NULL, NULL, NULL} + +/** + * Create an e-mail ready patch from a diff. + * + * @param out buffer to store the e-mail patch in + * @param diff containing the commit + * @param opts structure with options to influence content and formatting. + * @return 0 or an error code + */ +GIT_EXTERN(int) git_diff_format_email( + git_buf *out, + git_diff *diff, + const git_diff_format_email_options *opts); + +/** + * Create an e-mail ready patch for a commit. + * + * Does not support creating patches for merge commits (yet). + * + * @param out buffer to store the e-mail patch in + * @param repo containing the commit + * @param commit pointer to up commit + * @param patch_no patch number of the commit + * @param total_patches total number of patches in the patch set + * @param flags determines the formatting of the e-mail + * @param diff_opts structure with options to influence diff or NULL for defaults. + * @return 0 or an error code + */ +GIT_EXTERN(int) git_diff_commit_as_email( + git_buf *out, + git_repository *repo, + git_commit *commit, + size_t patch_no, + size_t total_patches, + git_diff_format_email_flags_t flags, + const git_diff_options *diff_opts); + +/** + * Initializes a `git_diff_format_email_options` with default values. + * + * Equivalent to creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT. + * + * @param opts The `git_diff_format_email_options` struct to initialize + * @param version Version of struct; pass `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_diff_format_email_init_options( + git_diff_format_email_options *opts, + unsigned int version); GIT_END_DECL diff -Nru libgit2-0.20.0/include/git2/errors.h libgit2-0.22.2/include/git2/errors.h --- libgit2-0.20.0/include/git2/errors.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/errors.h 2015-03-24 16:10:45.000000000 +0000 @@ -8,7 +8,6 @@ #define INCLUDE_git_errors_h__ #include "common.h" -#include "buffer.h" /** * @file git2/errors.h @@ -20,25 +19,43 @@ /** Generic return codes */ typedef enum { - GIT_OK = 0, - GIT_ERROR = -1, - GIT_ENOTFOUND = -3, - GIT_EEXISTS = -4, - GIT_EAMBIGUOUS = -5, - GIT_EBUFS = -6, - GIT_EUSER = -7, - GIT_EBAREREPO = -8, - GIT_EUNBORNBRANCH = -9, - GIT_EUNMERGED = -10, - GIT_ENONFASTFORWARD = -11, - GIT_EINVALIDSPEC = -12, - GIT_EMERGECONFLICT = -13, - GIT_ELOCKED = -14, + GIT_OK = 0, /**< No error */ - GIT_PASSTHROUGH = -30, - GIT_ITEROVER = -31, + GIT_ERROR = -1, /**< Generic error */ + GIT_ENOTFOUND = -3, /**< Requested object could not be found */ + GIT_EEXISTS = -4, /**< Object exists preventing operation */ + GIT_EAMBIGUOUS = -5, /**< More than one object matches */ + GIT_EBUFS = -6, /**< Output buffer too short to hold data */ + + /* GIT_EUSER is a special error that is never generated by libgit2 + * code. You can return it from a callback (e.g to stop an iteration) + * to know that it was generated by the callback and not by libgit2. + */ + GIT_EUSER = -7, + + GIT_EBAREREPO = -8, /**< Operation not allowed on bare repository */ + GIT_EUNBORNBRANCH = -9, /**< HEAD refers to branch with no commits */ + GIT_EUNMERGED = -10, /**< Merge in progress prevented operation */ + GIT_ENONFASTFORWARD = -11, /**< Reference was not fast-forwardable */ + GIT_EINVALIDSPEC = -12, /**< Name/ref spec was not in a valid format */ + GIT_EMERGECONFLICT = -13, /**< Merge conflicts prevented operation */ + GIT_ELOCKED = -14, /**< Lock file prevented operation */ + GIT_EMODIFIED = -15, /**< Reference value does not match expected */ + GIT_EAUTH = -16, /**< Authentication error */ + GIT_ECERTIFICATE = -17, /**< Server certificate is invalid */ + GIT_EAPPLIED = -18, /**< Patch/merge has already been applied */ + GIT_EPEEL = -19, /**< The requested peel operation is not possible */ + + GIT_PASSTHROUGH = -30, /**< Internal only */ + GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */ } git_error_code; +/** + * Structure to store extra details of the last error that occurred. + * + * This is kept on a per-thread basis if GIT_THREADS was defined when the + * library was build, otherwise one is kept globally for the library + */ typedef struct { char *message; int klass; @@ -71,6 +88,11 @@ GITERR_MERGE, GITERR_SSH, GITERR_FILTER, + GITERR_REVERT, + GITERR_CALLBACK, + GITERR_CHERRYPICK, + GITERR_DESCRIBE, + GITERR_REBASE, } git_error_t; /** @@ -90,7 +112,7 @@ * Get the last error data and clear it. * * This copies the last error into the given `git_error` struct - * and returns 0 if the copy was successful, leaving the error + * and returns 0 if the copy was successful, leaving the error * cleared as if `giterr_clear` had been called. * * If there was no existing error in the library, -1 will be returned diff -Nru libgit2-0.20.0/include/git2/filter.h libgit2-0.22.2/include/git2/filter.h --- libgit2-0.20.0/include/git2/filter.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/filter.h 2015-03-24 16:10:45.000000000 +0000 @@ -36,6 +36,14 @@ } git_filter_mode_t; /** + * Filter option flags. + */ +typedef enum { + GIT_FILTER_OPT_DEFAULT = 0u, + GIT_FILTER_OPT_ALLOW_UNSAFE = (1u << 0), +} git_filter_opt_t; + +/** * A filter that can transform file data * * This represents a filter that can be used to transform or even replace @@ -75,6 +83,7 @@ * @param blob The blob to which the filter will be applied (if known) * @param path Relative path of the file to be filtered * @param mode Filtering direction (WT->ODB or ODB->WT) + * @param options Combination of `git_filter_opt_t` flags * @return 0 on success (which could still return NULL if no filters are * needed for the requested file), <0 on error */ @@ -83,7 +92,8 @@ git_repository *repo, git_blob *blob, /* can be NULL */ const char *path, - git_filter_mode_t mode); + git_filter_mode_t mode, + uint32_t options); /** * Apply filter list to a data buffer. diff -Nru libgit2-0.20.0/include/git2/global.h libgit2-0.22.2/include/git2/global.h --- libgit2-0.20.0/include/git2/global.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/include/git2/global.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,44 @@ +/* + * 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_git_global_h__ +#define INCLUDE_git_global_h__ + +#include "common.h" + +GIT_BEGIN_DECL + +/** + * Init the global state + * + * This function must the called before any other libgit2 function in + * order to set up global state and threading. + * + * This function may be called multiple times - it will return the number + * of times the initialization has been called (including this one) that have + * not subsequently been shutdown. + * + * @return the number of initializations of the library, or an error code. + */ +GIT_EXTERN(int) git_libgit2_init(void); + +/** + * Shutdown the global state + * + * Clean up the global state and threading context after calling it as + * many times as `git_libgit2_init()` was called - it will return the + * number of remainining initializations that have not been shutdown + * (after this one). + * + * @return the number of remaining initializations of the library, or an + * error code. + */ +GIT_EXTERN(int) git_libgit2_shutdown(void); + +/** @} */ +GIT_END_DECL +#endif + diff -Nru libgit2-0.20.0/include/git2/graph.h libgit2-0.22.2/include/git2/graph.h --- libgit2-0.20.0/include/git2/graph.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/graph.h 2015-03-24 16:10:45.000000000 +0000 @@ -36,6 +36,20 @@ */ GIT_EXTERN(int) git_graph_ahead_behind(size_t *ahead, size_t *behind, git_repository *repo, const git_oid *local, const git_oid *upstream); + +/** + * Determine if a commit is the descendant of another commit. + * + * @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, + * 0 if not, error code otherwise. + */ +GIT_EXTERN(int) git_graph_descendant_of( + git_repository *repo, + const git_oid *commit, + const git_oid *ancestor); + /** @} */ GIT_END_DECL #endif diff -Nru libgit2-0.20.0/include/git2/indexer.h libgit2-0.22.2/include/git2/indexer.h --- libgit2-0.20.0/include/git2/indexer.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/indexer.h 2015-03-24 16:10:45.000000000 +0000 @@ -32,7 +32,7 @@ const char *path, unsigned int mode, git_odb *odb, - git_transfer_progress_callback progress_cb, + git_transfer_progress_cb progress_cb, void *progress_cb_payload); /** diff -Nru libgit2-0.20.0/include/git2/index.h libgit2-0.22.2/include/git2/index.h --- libgit2-0.20.0/include/git2/index.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/index.h 2015-03-24 16:10:45.000000000 +0000 @@ -56,12 +56,12 @@ unsigned int gid; git_off_t file_size; - git_oid oid; + git_oid id; unsigned short flags; unsigned short flags_extended; - char *path; + const char *path; } git_index_entry; /** @@ -73,11 +73,22 @@ */ #define GIT_IDXENTRY_NAMEMASK (0x0fff) #define GIT_IDXENTRY_STAGEMASK (0x3000) -#define GIT_IDXENTRY_EXTENDED (0x4000) -#define GIT_IDXENTRY_VALID (0x8000) #define GIT_IDXENTRY_STAGESHIFT 12 -#define GIT_IDXENTRY_STAGE(E) (((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT) +/** + * Flags for index entries + */ +typedef enum { + GIT_IDXENTRY_EXTENDED = (0x4000), + GIT_IDXENTRY_VALID = (0x8000), +} git_indxentry_flag_t; + +#define GIT_IDXENTRY_STAGE(E) \ + (((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT) + +#define GIT_IDXENTRY_STAGE_SET(E,S) do { \ + (E)->flags = ((E)->flags & ~GIT_IDXENTRY_STAGEMASK) | \ + (((S) & 0x03) << GIT_IDXENTRY_STAGESHIFT); } while (0) /** * Bitmasks for on-disk fields of `git_index_entry`'s `flags_extended` @@ -87,43 +98,43 @@ * in-memory only and used by libgit2. Only the flags in * `GIT_IDXENTRY_EXTENDED_FLAGS` will get saved on-disk. * - * These bitmasks match the three fields in the `git_index_entry` - * `flags_extended` value that belong on disk. You can use them to - * interpret the data in the `flags_extended`. - */ -#define GIT_IDXENTRY_INTENT_TO_ADD (1 << 13) -#define GIT_IDXENTRY_SKIP_WORKTREE (1 << 14) -/* GIT_IDXENTRY_EXTENDED2 is reserved for future extension */ -#define GIT_IDXENTRY_EXTENDED2 (1 << 15) - -#define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE) - -/** - * Bitmasks for in-memory only fields of `git_index_entry`'s `flags_extended` - * - * These bitmasks match the other fields in the `git_index_entry` - * `flags_extended` value that are only used in-memory by libgit2. You + * Thee first three bitmasks match the three fields in the + * `git_index_entry` `flags_extended` value that belong on disk. You * can use them to interpret the data in the `flags_extended`. + * + * The rest of the bitmasks match the other fields in the `git_index_entry` + * `flags_extended` value that are only used in-memory by libgit2. + * You can use them to interpret the data in the `flags_extended`. + * */ -#define GIT_IDXENTRY_UPDATE (1 << 0) -#define GIT_IDXENTRY_REMOVE (1 << 1) -#define GIT_IDXENTRY_UPTODATE (1 << 2) -#define GIT_IDXENTRY_ADDED (1 << 3) - -#define GIT_IDXENTRY_HASHED (1 << 4) -#define GIT_IDXENTRY_UNHASHED (1 << 5) -#define GIT_IDXENTRY_WT_REMOVE (1 << 6) /* remove in work directory */ -#define GIT_IDXENTRY_CONFLICTED (1 << 7) +typedef enum { -#define GIT_IDXENTRY_UNPACKED (1 << 8) -#define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9) + GIT_IDXENTRY_INTENT_TO_ADD = (1 << 13), + GIT_IDXENTRY_SKIP_WORKTREE = (1 << 14), + /** Reserved for future extension */ + GIT_IDXENTRY_EXTENDED2 = (1 << 15), + + GIT_IDXENTRY_EXTENDED_FLAGS = (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE), + GIT_IDXENTRY_UPDATE = (1 << 0), + GIT_IDXENTRY_REMOVE = (1 << 1), + GIT_IDXENTRY_UPTODATE = (1 << 2), + GIT_IDXENTRY_ADDED = (1 << 3), + + GIT_IDXENTRY_HASHED = (1 << 4), + GIT_IDXENTRY_UNHASHED = (1 << 5), + GIT_IDXENTRY_WT_REMOVE = (1 << 6), /**< remove in work directory */ + GIT_IDXENTRY_CONFLICTED = (1 << 7), + + GIT_IDXENTRY_UNPACKED = (1 << 8), + GIT_IDXENTRY_NEW_SKIP_WORKTREE = (1 << 9), +} git_idxentry_extended_flag_t; /** Capabilities of system that affect index actions. */ typedef enum { - GIT_INDEXCAP_IGNORE_CASE = 1u, - GIT_INDEXCAP_NO_FILEMODE = 2u, - GIT_INDEXCAP_NO_SYMLINKS = 4u, - GIT_INDEXCAP_FROM_OWNER = ~0u + GIT_INDEXCAP_IGNORE_CASE = 1, + GIT_INDEXCAP_NO_FILEMODE = 2, + GIT_INDEXCAP_NO_SYMLINKS = 4, + GIT_INDEXCAP_FROM_OWNER = -1, } git_indexcap_t; /** Callback for APIs that add/remove/update files matching pathspec */ @@ -158,8 +169,8 @@ * to back it. * * Since there is no ODB or working directory behind this index, - * any Index methods which rely on these (e.g. index_add) will - * fail with the GIT_EBAREINDEX error code. + * any Index methods which rely on these (e.g. index_add_bypath) + * will fail with the GIT_ERROR error code. * * If you need to access the index of an actual repository, * use the `git_repository_index` wrapper. @@ -206,7 +217,7 @@ * @param index An existing index object * @return A combination of GIT_INDEXCAP values */ -GIT_EXTERN(unsigned int) git_index_caps(const git_index *index); +GIT_EXTERN(int) git_index_caps(const git_index *index); /** * Set index capabilities flags. @@ -219,7 +230,7 @@ * @param caps A combination of GIT_INDEXCAP values * @return 0 on success, -1 on failure */ -GIT_EXTERN(int) git_index_set_caps(git_index *index, unsigned int caps); +GIT_EXTERN(int) git_index_set_caps(git_index *index, int caps); /** * Update the contents of an existing index object in memory by reading @@ -255,7 +266,7 @@ * @param index an existing index object * @return path to index file or NULL for in-memory index */ -GIT_EXTERN(const char *) git_index_path(git_index *index); +GIT_EXTERN(const char *) git_index_path(const git_index *index); /** * Read a tree into the index file with stats @@ -327,12 +338,14 @@ /** * Clear the contents (all the entries) of an index object. - * This clears the index object in memory; changes must be manually - * written to disk for them to take effect. + * + * This clears the index object in memory; changes must be explicitly + * written to disk for them to take effect persistently. * * @param index an existing index object + * @return 0 on success, error code < 0 on failure */ -GIT_EXTERN(void) git_index_clear(git_index *index); +GIT_EXTERN(int) git_index_clear(git_index *index); /** * Get a pointer to one of the entries in the index @@ -405,10 +418,10 @@ * * This entry is calculated from the entry's flag attribute like this: * - * (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT + * (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT * * @param entry The entry - * @returns the stage number + * @return the stage number */ GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry); @@ -493,7 +506,7 @@ * item in the working directory immediately *before* it is added to / * updated in the index. Returning zero will add the item to the index, * greater than zero will skip the item, and less than zero will abort the - * scan and cause GIT_EUSER to be returned. + * scan and return that value to the caller. * * @param index an existing index object * @param pathspec array of path patterns @@ -502,7 +515,7 @@ * gets index of matching pathspec entry); can be NULL; * return 0 to add, >0 to skip, <0 to abort scan. * @param payload payload passed through to callback function - * @return 0 or an error code + * @return 0 on success, negative callback return value, or error code */ GIT_EXTERN(int) git_index_add_all( git_index *index, @@ -524,7 +537,7 @@ * gets index of matching pathspec entry); can be NULL; * return 0 to add, >0 to skip, <0 to abort scan. * @param payload payload passed through to callback function - * @return 0 or an error code + * @return 0 on success, negative callback return value, or error code */ GIT_EXTERN(int) git_index_remove_all( git_index *index, @@ -553,7 +566,7 @@ * gets index of matching pathspec entry); can be NULL; * return 0 to add, >0 to skip, <0 to abort scan. * @param payload payload passed through to callback function - * @return 0 or an error code + * @return 0 on success, negative callback return value, or error code */ GIT_EXTERN(int) git_index_update_all( git_index *index, @@ -568,8 +581,7 @@ * @param at_pos the address to which the position of the index entry is written (optional) * @param index an existing index object * @param path path to search - * @return a zero-based position in the index if found; - * GIT_ENOTFOUND otherwise + * @return a zero-based position in the index if found; GIT_ENOTFOUND otherwise */ GIT_EXTERN(int) git_index_find(size_t *at_pos, git_index *index, const char *path); @@ -613,6 +625,7 @@ * @param their_out Pointer to store the their entry * @param index an existing index object * @param path path to search + * @return 0 or an error code */ GIT_EXTERN(int) git_index_conflict_get( const git_index_entry **ancestor_out, @@ -625,16 +638,18 @@ * Removes the index entries that represent a conflict of a single file. * * @param index an existing index object - * @param path to search + * @param path path to remove conflicts for + * @return 0 or an error code */ GIT_EXTERN(int) git_index_conflict_remove(git_index *index, const char *path); /** - * Remove all conflicts in the index (entries with a stage greater than 0.) + * Remove all conflicts in the index (entries with a stage greater than 0). * * @param index an existing index object + * @return 0 or an error code */ -GIT_EXTERN(void) git_index_conflict_cleanup(git_index *index); +GIT_EXTERN(int) git_index_conflict_cleanup(git_index *index); /** * Determine if the index contains entries representing file conflicts. @@ -644,9 +659,12 @@ GIT_EXTERN(int) git_index_has_conflicts(const git_index *index); /** - * Create an iterator for the conflicts in the index. You may not modify the - * index while iterating, the results are undefined. + * Create an iterator for the conflicts in the index. + * + * The index must not be modified while iterating; the results are undefined. * + * @param iterator_out The newly created conflict iterator + * @param index The index to scan * @return 0 or an error code */ GIT_EXTERN(int) git_index_conflict_iterator_new( diff -Nru libgit2-0.20.0/include/git2/merge.h libgit2-0.22.2/include/git2/merge.h --- libgit2-0.20.0/include/git2/merge.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/merge.h 2015-03-24 16:10:45.000000000 +0000 @@ -10,8 +10,10 @@ #include "common.h" #include "types.h" #include "oid.h" +#include "oidarray.h" #include "checkout.h" #include "index.h" +#include "annotated_commit.h" /** * @file git2/merge.h @@ -23,70 +25,302 @@ GIT_BEGIN_DECL /** + * The file inputs to `git_merge_file`. Callers should populate the + * `git_merge_file_input` structure with descriptions of the files in + * each side of the conflict for use in producing the merge file. + */ +typedef struct { + unsigned int version; + + /** Pointer to the contents of the file. */ + const char *ptr; + + /** Size of the contents pointed to in `ptr`. */ + size_t size; + + /** File name of the conflicted file, or `NULL` to not merge the path. */ + const char *path; + + /** File mode of the conflicted file, or `0` to not merge the mode. */ + unsigned int mode; +} git_merge_file_input; + +#define GIT_MERGE_FILE_INPUT_VERSION 1 +#define GIT_MERGE_FILE_INPUT_INIT {GIT_MERGE_FILE_INPUT_VERSION} + +/** + * Initializes a `git_merge_file_input` with default values. Equivalent to + * creating an instance with GIT_MERGE_FILE_INPUT_INIT. + * + * @param opts the `git_merge_file_input` instance to initialize. + * @param version the version of the struct; you should pass + * `GIT_MERGE_FILE_INPUT_VERSION` here. + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_merge_file_init_input( + git_merge_file_input *opts, + unsigned int version); + +/** * Flags for `git_merge_tree` options. A combination of these flags can be - * passed in via the `flags` value in the `git_merge_tree_opts`. + * passed in via the `flags` value in the `git_merge_options`. */ typedef enum { - /** Detect renames */ + /** + * Detect renames that occur between the common ancestor and the "ours" + * side or the common ancestor and the "theirs" side. This will enable + * the ability to merge between a modified and renamed file. + */ GIT_MERGE_TREE_FIND_RENAMES = (1 << 0), } git_merge_tree_flag_t; /** - * Automerge options for `git_merge_trees_opts`. + * Merge file favor options for `git_merge_options` instruct the file-level + * merging functionality how to deal with conflicting regions of the files. */ typedef enum { - GIT_MERGE_AUTOMERGE_NORMAL = 0, - GIT_MERGE_AUTOMERGE_NONE = 1, - GIT_MERGE_AUTOMERGE_FAVOR_OURS = 2, - GIT_MERGE_AUTOMERGE_FAVOR_THEIRS = 3, -} git_merge_automerge_flags; + /** + * When a region of a file is changed in both branches, a conflict + * will be recorded in the index so that `git_checkout` can produce + * a merge file with conflict markers in the working directory. + * This is the default. + */ + GIT_MERGE_FILE_FAVOR_NORMAL = 0, + /** + * When a region of a file is changed in both branches, the file + * created in the index will contain the "ours" side of any conflicting + * region. The index will not record a conflict. + */ + GIT_MERGE_FILE_FAVOR_OURS = 1, + /** + * When a region of a file is changed in both branches, the file + * created in the index will contain the "theirs" side of any conflicting + * region. The index will not record a conflict. + */ + GIT_MERGE_FILE_FAVOR_THEIRS = 2, + + /** + * When a region of a file is changed in both branches, the file + * created in the index will contain each unique line from each side, + * which has the result of combining both files. The index will not + * record a conflict. + */ + GIT_MERGE_FILE_FAVOR_UNION = 3, +} git_merge_file_favor_t; + +/** + * File merging flags + */ +typedef enum { + /** Defaults */ + GIT_MERGE_FILE_DEFAULT = 0, + + /** Create standard conflicted merge files */ + GIT_MERGE_FILE_STYLE_MERGE = (1 << 0), + + /** Create diff3-style files */ + GIT_MERGE_FILE_STYLE_DIFF3 = (1 << 1), + + /** Condense non-alphanumeric regions for simplified diff file */ + GIT_MERGE_FILE_SIMPLIFY_ALNUM = (1 << 2), +} git_merge_file_flags_t; + +/** + * Options for merging a file + */ +typedef struct { + unsigned int version; + + /** + * Label for the ancestor file side of the conflict which will be prepended + * to labels in diff3-format merge files. + */ + const char *ancestor_label; + + /** + * Label for our file side of the conflict which will be prepended + * to labels in merge files. + */ + const char *our_label; + + /** + * Label for their file side of the conflict which will be prepended + * to labels in merge files. + */ + const char *their_label; + + /** The file to favor in region conflicts. */ + git_merge_file_favor_t favor; + + /** Merge file flags. */ + git_merge_file_flags_t flags; +} git_merge_file_options; + +#define GIT_MERGE_FILE_OPTIONS_VERSION 1 +#define GIT_MERGE_FILE_OPTIONS_INIT {GIT_MERGE_FILE_OPTIONS_VERSION} + +/** + * Initializes a `git_merge_file_options` with default values. Equivalent to + * creating an instance with GIT_MERGE_FILE_OPTIONS_INIT. + * + * @param opts the `git_merge_file_options` instance to initialize. + * @param version the version of the struct; you should pass + * `GIT_MERGE_FILE_OPTIONS_VERSION` here. + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_merge_file_init_options( + git_merge_file_options *opts, + unsigned int version); + +/** + * Information about file-level merging + */ +typedef struct { + /** + * True if the output was automerged, false if the output contains + * conflict markers. + */ + unsigned int automergeable; + + /** + * The path that the resultant merge file should use, or NULL if a + * filename conflict would occur. + */ + const char *path; + + /** The mode that the resultant merge file should use. */ + unsigned int mode; + + /** The contents of the merge. */ + const char *ptr; + + /** The length of the merge contents. */ + size_t len; +} git_merge_file_result; + +/** + * Merging options + */ typedef struct { unsigned int version; git_merge_tree_flag_t flags; - /** Similarity to consider a file renamed (default 50) */ + /** + * Similarity to consider a file renamed (default 50). If + * `GIT_MERGE_TREE_FIND_RENAMES` is enabled, added files will be compared + * with deleted files to determine their similarity. Files that are + * more similar than the rename threshold (percentage-wise) will be + * treated as a rename. + */ unsigned int rename_threshold; - /** Maximum similarity sources to examine (overrides the - * `merge.renameLimit` config) (default 200) + /** + * Maximum similarity sources to examine for renames (default 200). + * If the number of rename candidates (add / delete pairs) is greater + * than this value, inexact rename detection is aborted. + * + * This setting overrides the `merge.renameLimit` configuration value. */ unsigned int target_limit; /** Pluggable similarity metric; pass NULL to use internal metric */ git_diff_similarity_metric *metric; - /** Flags for automerging content. */ - git_merge_automerge_flags automerge_flags; -} git_merge_tree_opts; - -#define GIT_MERGE_TREE_OPTS_VERSION 1 -#define GIT_MERGE_TREE_OPTS_INIT {GIT_MERGE_TREE_OPTS_VERSION} + /** Flags for handling conflicting content. */ + git_merge_file_favor_t file_favor; +} git_merge_options; +#define GIT_MERGE_OPTIONS_VERSION 1 +#define GIT_MERGE_OPTIONS_INIT {GIT_MERGE_OPTIONS_VERSION} /** - * Option flags for `git_merge`. + * Initializes a `git_merge_options` with default values. Equivalent to + * creating an instance with GIT_MERGE_OPTIONS_INIT. * - * GIT_MERGE_NO_FASTFORWARD - Do not fast-forward. + * @param opts the `git_merge_options` instance to initialize. + * @param version the version of the struct; you should pass + * `GIT_MERGE_OPTIONS_VERSION` here. + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_merge_init_options( + git_merge_options *opts, + unsigned int version); + +/** + * The results of `git_merge_analysis` indicate the merge opportunities. */ typedef enum { - GIT_MERGE_NO_FASTFORWARD = 1, - GIT_MERGE_FASTFORWARD_ONLY = 2, -} git_merge_flags_t; + /** No merge is possible. (Unused.) */ + GIT_MERGE_ANALYSIS_NONE = 0, -typedef struct { - unsigned int version; + /** + * A "normal" merge; both HEAD and the given merge input have diverged + * from their common ancestor. The divergent commits must be merged. + */ + GIT_MERGE_ANALYSIS_NORMAL = (1 << 0), + + /** + * All given merge inputs are reachable from HEAD, meaning the + * repository is up-to-date and no merge needs to be performed. + */ + GIT_MERGE_ANALYSIS_UP_TO_DATE = (1 << 1), + + /** + * The given merge input is a fast-forward from HEAD and no merge + * needs to be performed. Instead, the client can check out the + * given merge input. + */ + GIT_MERGE_ANALYSIS_FASTFORWARD = (1 << 2), + + /** + * The HEAD of the current repository is "unborn" and does not point to + * a valid commit. No merge can be performed, but the caller may wish + * to simply set HEAD to the target commit(s). + */ + GIT_MERGE_ANALYSIS_UNBORN = (1 << 3), +} git_merge_analysis_t; - git_merge_flags_t merge_flags; - git_merge_tree_opts merge_tree_opts; +/** + * The user's stated preference for merges. + */ +typedef enum { + /** + * No configuration was found that suggests a preferred behavior for + * merge. + */ + GIT_MERGE_PREFERENCE_NONE = 0, - git_checkout_opts checkout_opts; -} git_merge_opts; + /** + * There is a `merge.ff=false` configuration setting, suggesting that + * the user does not want to allow a fast-forward merge. + */ + GIT_MERGE_PREFERENCE_NO_FASTFORWARD = (1 << 0), -#define GIT_MERGE_OPTS_VERSION 1 -#define GIT_MERGE_OPTS_INIT {GIT_MERGE_OPTS_VERSION, 0, GIT_MERGE_TREE_OPTS_INIT, GIT_CHECKOUT_OPTS_INIT} + /** + * There is a `merge.ff=only` configuration setting, suggesting that + * the user only wants fast-forward merges. + */ + GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY = (1 << 1), +} git_merge_preference_t; +/** + * Analyzes the given branch(es) and determines the opportunities for + * merging them into the HEAD of the repository. + * + * @param analysis_out analysis enumeration that the result is written into + * @param repo the repository to merge + * @param their_heads the heads to merge into + * @param their_heads_len the number of heads to merge + * @return 0 on success or error code + */ +GIT_EXTERN(int) git_merge_analysis( + git_merge_analysis_t *analysis_out, + git_merge_preference_t *preference_out, + git_repository *repo, + const git_annotated_commit **their_heads, + size_t their_heads_len); /** * Find a merge base between two commits @@ -95,7 +329,7 @@ * @param repo the repository where the commits exist * @param one one of the commits * @param two the other commit - * @return Zero on success; GIT_ENOTFOUND or -1 on failure. + * @return 0 on success, GIT_ENOTFOUND if not found or error code */ GIT_EXTERN(int) git_merge_base( git_oid *out, @@ -104,6 +338,21 @@ const git_oid *two); /** + * Find merge bases between two commits + * + * @param out array in which to store the resulting ids + * @param repo the repository where the commits exist + * @param one one of the commits + * @param two the other commit + * @return 0 on success, GIT_ENOTFOUND if not found or error code + */ +GIT_EXTERN(int) git_merge_bases( + git_oidarray *out, + git_repository *repo, + const git_oid *one, + const git_oid *two); + +/** * Find a merge base given a list of commits * * @param out the OID of a merge base considering all the commits @@ -119,59 +368,92 @@ const git_oid input_array[]); /** - * Creates a `git_merge_head` from the given reference + * Find all merge bases given a list of commits * - * @param out pointer to store the git_merge_head result in - * @param repo repository that contains the given reference - * @param ref reference to use as a merge input - * @return zero on success, -1 on failure. + * @param out array in which to store the resulting ids + * @param repo the repository where the commits exist + * @param length The number of commits in the provided `input_array` + * @param input_array oids of the commits + * @return Zero on success; GIT_ENOTFOUND or -1 on failure. */ -GIT_EXTERN(int) git_merge_head_from_ref( - git_merge_head **out, +GIT_EXTERN(int) git_merge_bases_many( + git_oidarray *out, git_repository *repo, - git_reference *ref); + size_t length, + const git_oid input_array[]); /** - * Creates a `git_merge_head` from the given fetch head data + * Find a merge base in preparation for an octopus merge * - * @param out pointer to store the git_merge_head result in - * @param repo repository that contains the given commit - * @param branch_name name of the (remote) branch - * @param remote_url url of the remote - * @param oid the commit object id to use as a merge input - * @return zero on success, -1 on failure. + * @param out the OID of a merge base considering all the commits + * @param repo the repository where the commits exist + * @param length The number of commits in the provided `input_array` + * @param input_array oids of the commits + * @return Zero on success; GIT_ENOTFOUND or -1 on failure. */ -GIT_EXTERN(int) git_merge_head_from_fetchhead( - git_merge_head **out, +GIT_EXTERN(int) git_merge_base_octopus( + git_oid *out, git_repository *repo, - const char *branch_name, - const char *remote_url, - const git_oid *oid); + size_t length, + const git_oid input_array[]); /** - * Creates a `git_merge_head` from the given commit id - * - * @param out pointer to store the git_merge_head result in - * @param repo repository that contains the given commit - * @param oid the commit object id to use as a merge input - * @return zero on success, -1 on failure. + * Merge two files as they exist in the in-memory data structures, using + * the given common ancestor as the baseline, producing a + * `git_merge_file_result` that reflects the merge result. The + * `git_merge_file_result` must be freed with `git_merge_file_result_free`. + * + * Note that this function does not reference a repository and any + * configuration must be passed as `git_merge_file_options`. + * + * @param out The git_merge_file_result to be filled in + * @param ancestor The contents of the ancestor file + * @param ours The contents of the file in "our" side + * @param theirs The contents of the file in "their" side + * @param opts The merge file options or `NULL` for defaults + * @return 0 on success or error code + */ +GIT_EXTERN(int) git_merge_file( + git_merge_file_result *out, + const git_merge_file_input *ancestor, + const git_merge_file_input *ours, + const git_merge_file_input *theirs, + const git_merge_file_options *opts); + +/** + * Merge two files as they exist in the index, using the given common + * ancestor as the baseline, producing a `git_merge_file_result` that + * reflects the merge result. The `git_merge_file_result` must be freed with + * `git_merge_file_result_free`. + * + * @param out The git_merge_file_result to be filled in + * @param repo The repository + * @param ancestor The index entry for the ancestor file (stage level 1) + * @param ours The index entry for our file (stage level 2) + * @param theirs The index entry for their file (stage level 3) + * @param opts The merge file options or NULL + * @return 0 on success or error code */ -GIT_EXTERN(int) git_merge_head_from_oid( - git_merge_head **out, +GIT_EXTERN(int) git_merge_file_from_index( + git_merge_file_result *out, git_repository *repo, - const git_oid *oid); + const git_index_entry *ancestor, + const git_index_entry *ours, + const git_index_entry *theirs, + const git_merge_file_options *opts); /** - * Frees a `git_merge_head` + * Frees a `git_merge_file_result`. * - * @param head merge head to free + * @param result The result to free or `NULL` */ -GIT_EXTERN(void) git_merge_head_free( - git_merge_head *head); +GIT_EXTERN(void) git_merge_file_result_free(git_merge_file_result *result); /** * Merge two trees, producing a `git_index` that reflects the result of - * the merge. + * the merge. The index may be written as-is to the working directory + * or checked out. If the index is to be converted to a tree, the caller + * should resolve any conflicts that arose as part of the merge. * * The returned index must be freed explicitly with `git_index_free`. * @@ -181,7 +463,7 @@ * @param our_tree the tree that reflects the destination tree * @param their_tree the tree to merge in to `our_tree` * @param opts the merge tree options (or null for defaults) - * @return zero on success, -1 on failure. + * @return 0 on success or error code */ GIT_EXTERN(int) git_merge_trees( git_index **out, @@ -189,44 +471,62 @@ const git_tree *ancestor_tree, const git_tree *our_tree, const git_tree *their_tree, - const git_merge_tree_opts *opts); + const git_merge_options *opts); /** - * Merges the given commits into HEAD, producing a new commit. + * Merge two commits, producing a `git_index` that reflects the result of + * the merge. The index may be written as-is to the working directory + * or checked out. If the index is to be converted to a tree, the caller + * should resolve any conflicts that arose as part of the merge. + * + * The merge performed uses the first common ancestor, unlike the + * `git-merge-recursive` strategy, which may produce an artificial common + * ancestor tree when there are multiple ancestors. + * + * The returned index must be freed explicitly with `git_index_free`. + * + * @param out pointer to store the index result in + * @param repo repository that contains the given trees + * @param our_commit the commit that reflects the destination tree + * @param their_commit the commit to merge in to `our_commit` + * @param opts the merge tree options (or null for defaults) + * @return 0 on success or error code + */ +GIT_EXTERN(int) git_merge_commits( + git_index **out, + git_repository *repo, + const git_commit *our_commit, + const git_commit *their_commit, + const git_merge_options *opts); + +/** + * Merges the given commit(s) into HEAD, writing the results into the working + * directory. Any changes are staged for commit and any conflicts are written + * to the index. Callers should inspect the repository's index after this + * completes, resolve any conflicts and prepare a commit. + * + * The merge performed uses the first common ancestor, unlike the + * `git-merge-recursive` strategy, which may produce an artificial common + * ancestor tree when there are multiple ancestors. + * + * For compatibility with git, the repository is put into a merging + * state. Once the commit is done (or if the uses wishes to abort), + * you should clear this state by calling + * `git_repository_state_cleanup()`. * - * @param out the results of the merge * @param repo the repository to merge - * @param merge_heads the heads to merge into - * @param merge_heads_len the number of heads to merge - * @param flags merge flags + * @param their_heads the heads to merge into + * @param their_heads_len the number of heads to merge + * @param merge_opts merge options + * @param checkout_opts checkout options + * @return 0 on success or error code */ GIT_EXTERN(int) git_merge( - git_merge_result **out, git_repository *repo, - const git_merge_head **their_heads, + const git_annotated_commit **their_heads, size_t their_heads_len, - const git_merge_opts *opts); - -/** - * Returns true if a merge is up-to-date (we were asked to merge the target - * into itself.) - */ -GIT_EXTERN(int) git_merge_result_is_uptodate(git_merge_result *merge_result); - -/** - * Returns true if a merge is eligible for fastforward - */ -GIT_EXTERN(int) git_merge_result_is_fastforward(git_merge_result *merge_result); - -/** - * Gets the fast-forward OID if the merge was a fastforward. - * - * @param out the OID of the fast-forward - * @param merge_result the results of the merge - */ -GIT_EXTERN(int) git_merge_result_fastforward_oid(git_oid *out, git_merge_result *merge_result); - -GIT_EXTERN(void) git_merge_result_free(git_merge_result *merge_result); + const git_merge_options *merge_opts, + const git_checkout_options *checkout_opts); /** @} */ GIT_END_DECL diff -Nru libgit2-0.20.0/include/git2/message.h libgit2-0.22.2/include/git2/message.h --- libgit2-0.20.0/include/git2/message.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/message.h 2015-03-24 16:10:45.000000000 +0000 @@ -8,6 +8,7 @@ #define INCLUDE_git_message_h__ #include "common.h" +#include "buffer.h" /** * @file git2/message.h @@ -23,25 +24,19 @@ * * Optionally, can remove lines starting with a "#". * - * @param out The user-allocated buffer which will be filled with the - * cleaned up message. Pass NULL if you just want to get the needed - * size of the prettified message as the output value. - * - * @param out_size Size of the `out` buffer in bytes. + * @param out The user-allocated git_buf which will be filled with the + * cleaned up message. * * @param message The message to be prettified. * - * @param strip_comments Non-zero to remove lines starting with "#", 0 to - * leave them in. + * @param strip_comments Non-zero to remove comment lines, 0 to leave them in. + * + * @param comment_char Comment character. Lines starting with this character + * are considered to be comments and removed if `strip_comments` is non-zero. * - * @return -1 on error, else number of characters in prettified message - * including the trailing NUL byte + * @return 0 or an error code. */ -GIT_EXTERN(int) git_message_prettify( - char *out, - size_t out_size, - const char *message, - int strip_comments); +GIT_EXTERN(int) git_message_prettify(git_buf *out, const char *message, int strip_comments, char comment_char); /** @} */ GIT_END_DECL diff -Nru libgit2-0.20.0/include/git2/net.h libgit2-0.22.2/include/git2/net.h --- libgit2-0.20.0/include/git2/net.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/net.h 2015-03-24 16:10:45.000000000 +0000 @@ -21,26 +21,32 @@ #define GIT_DEFAULT_PORT "9418" -/* +/** + * Direction of the connection. + * * We need this because we need to know whether we should call * git-upload-pack or git-receive-pack on the remote end when get_refs * gets called. */ - typedef enum { GIT_DIRECTION_FETCH = 0, GIT_DIRECTION_PUSH = 1 } git_direction; - /** - * Remote head description, given out on `ls` calls. + * Description of a reference advertised by a remote server, given out + * on `ls` calls. */ struct git_remote_head { int local; /* available locally */ git_oid oid; git_oid loid; char *name; + /** + * If the server send a symref mapping for this ref, this will + * point to the target. + */ + char *symref_target; }; /** diff -Nru libgit2-0.20.0/include/git2/notes.h libgit2-0.22.2/include/git2/notes.h --- libgit2-0.20.0/include/git2/notes.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/notes.h 2015-03-24 16:10:45.000000000 +0000 @@ -59,14 +59,12 @@ GIT_EXTERN(void) git_note_iterator_free(git_note_iterator *it); /** - * Returns the current item (note_id and annotated_id) and advance the iterator + * Return the current item (note_id and annotated_id) and advance the iterator * internally to the next value * - * The notes must not be freed manually by the user. - * - * @param it pointer to the iterator * @param note_id id of blob containing the message * @param annotated_id id of the git object being annotated + * @param it pointer to the iterator * * @return 0 (no error), GIT_ITEROVER (iteration is done) or an error code * (negative value) @@ -97,6 +95,23 @@ const git_oid *oid); /** + * Get the note author + * + * @param note the note + * @return the author + */ +GIT_EXTERN(const git_signature *) git_note_author(const git_note *note); + +/** + * Get the note committer + * + * @param note the note + * @return the committer + */ +GIT_EXTERN(const git_signature *) git_note_committer(const git_note *note); + + +/** * Get the note message * * @param note the note @@ -106,22 +121,22 @@ /** - * Get the note object OID + * Get the note object's id * * @param note the note - * @return the note object OID + * @return the note object's id */ -GIT_EXTERN(const git_oid *) git_note_oid(const git_note *note); +GIT_EXTERN(const git_oid *) git_note_id(const git_note *note); /** * Add a note for an object * * @param out pointer to store the OID (optional); NULL in case of error * @param repo repository where to store the note - * @param author signature of the notes commit author - * @param committer signature of the notes commit committer * @param notes_ref canonical name of the reference to use (optional); * defaults to "refs/notes/commits" + * @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 force Overwrite existing note @@ -131,9 +146,9 @@ GIT_EXTERN(int) git_note_create( git_oid *out, git_repository *repo, + const char *notes_ref, const git_signature *author, const git_signature *committer, - const char *notes_ref, const git_oid *oid, const char *note, int force); @@ -189,7 +204,7 @@ * * @param payload Extra parameter to callback function. * - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_note_foreach( git_repository *repo, diff -Nru libgit2-0.20.0/include/git2/object.h libgit2-0.22.2/include/git2/object.h --- libgit2-0.20.0/include/git2/object.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/object.h 2015-03-24 16:10:45.000000000 +0000 @@ -10,6 +10,7 @@ #include "common.h" #include "types.h" #include "oid.h" +#include "buffer.h" /** * @file git2/object.h @@ -104,6 +105,20 @@ GIT_EXTERN(const git_oid *) git_object_id(const git_object *obj); /** + * Get a short abbreviated OID string for the object + * + * This starts at the "core.abbrev" length (default 7 characters) and + * iteratively extends to a longer string if that length is ambiguous. + * The result will be unambiguous (at least until new objects are added to + * the repository). + * + * @param out Buffer to write string into + * @param obj The object to get an ID for + * @return 0 on success, <0 for error + */ +GIT_EXTERN(int) git_object_short_id(git_buf *out, const git_object *obj); + +/** * Get the object type of an object * * @param obj the repository object @@ -143,7 +158,7 @@ GIT_EXTERN(void) git_object_free(git_object *object); /** - * Convert an object type to it's string representation. + * Convert an object type to its string representation. * * The result is a pointer to a string in static memory and * should not be free()'ed. @@ -187,18 +202,25 @@ /** * Recursively peel an object until an object of the specified type is met. * - * The retrieved `peeled` object is owned by the repository and should be - * closed with the `git_object_free` method. + * If the query cannot be satisfied due to the object model, + * GIT_EINVALIDSPEC will be returned (e.g. trying to peel a blob to a + * tree). + * + * If you pass `GIT_OBJ_ANY` as the target type, then the object will + * be peeled until the type changes. A tag will be peeled until the + * referenced object is no longer a tag, and a commit will be peeled + * to a tree. Any other object type will return GIT_EINVALIDSPEC. + * + * If peeling a tag we discover an object which cannot be peeled to + * the target type due to the object model, GIT_EPEEL will be + * returned. * - * If you pass `GIT_OBJ_ANY` as the target type, then the object will be - * peeled until the type changes (e.g. a tag will be chased until the - * referenced object is no longer a tag). + * You must free the returned object. * * @param peeled Pointer to the peeled git_object * @param object The object to be processed - * @param target_type The type of the requested object (GIT_OBJ_COMMIT, - * GIT_OBJ_TAG, GIT_OBJ_TREE, GIT_OBJ_BLOB or GIT_OBJ_ANY). - * @return 0 on success, GIT_EAMBIGUOUS, GIT_ENOTFOUND or an error code + * @param target_type The type of the requested object (a GIT_OBJ_ value) + * @return 0 on success, GIT_EINVALIDSPEC, GIT_EPEEL, or an error code */ GIT_EXTERN(int) git_object_peel( git_object **peeled, diff -Nru libgit2-0.20.0/include/git2/odb.h libgit2-0.22.2/include/git2/odb.h --- libgit2-0.20.0/include/git2/odb.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/odb.h 2015-03-24 16:10:45.000000000 +0000 @@ -159,6 +159,19 @@ GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id); /** + * Determine if objects can be found in the object database from a short OID. + * + * @param out The full OID of the found object if just one is found. + * @param db The database to be searched for the given object. + * @param short_id A prefix of the id of the object to read. + * @param len The length of the prefix. + * @return 0 if found, GIT_ENOTFOUND if not found, GIT_EAMBIGUOUS if multiple + * matches were found, other value < 0 if there was a read error. + */ +GIT_EXTERN(int) git_odb_exists_prefix( + git_oid *out, git_odb *db, const git_oid *short_id, size_t len); + +/** * Refresh the object database to load newly added files. * * If the object databases have changed on disk while the library @@ -189,7 +202,7 @@ * @param db database to use * @param cb the callback to call for each object * @param payload data to pass to the callback - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload); @@ -325,7 +338,7 @@ GIT_EXTERN(int) git_odb_write_pack( git_odb_writepack **out, git_odb *db, - git_transfer_progress_callback progress_cb, + git_transfer_progress_cb progress_cb, void *progress_payload); /** diff -Nru libgit2-0.20.0/include/git2/oidarray.h libgit2-0.22.2/include/git2/oidarray.h --- libgit2-0.20.0/include/git2/oidarray.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/include/git2/oidarray.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * 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_git_oidarray_h__ +#define INCLUDE_git_oidarray_h__ + +#include "common.h" +#include "oid.h" + +GIT_BEGIN_DECL + +/** Array of object ids */ +typedef struct git_oidarray { + git_oid *ids; + size_t count; +} git_oidarray; + +/** + * Free the OID array + * + * This method must (and must only) be called on `git_oidarray` + * objects where the array is allocated by the library. Not doing so, + * will result in a memory leak. + * + * This does not free the `git_oidarray` itself, since the library will + * never allocate that object directly itself (it is more commonly embedded + * inside another struct or created on the stack). + * + * @param array git_oidarray from which to free oid data + */ +GIT_EXTERN(void) git_oidarray_free(git_oidarray *array); + +/** @} */ +GIT_END_DECL + +#endif + diff -Nru libgit2-0.20.0/include/git2/oid.h libgit2-0.22.2/include/git2/oid.h --- libgit2-0.20.0/include/git2/oid.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/oid.h 2015-03-24 16:10:45.000000000 +0000 @@ -116,13 +116,17 @@ GIT_EXTERN(void) git_oid_pathfmt(char *out, const git_oid *id); /** - * Format a git_oid into a newly allocated c-string. + * Format a git_oid into a statically allocated c-string. + * + * The c-string is owned by the library and should not be freed + * by the user. If libgit2 is built with thread support, the string + * will be stored in TLS (i.e. one buffer per thread) to allow for + * concurrent calls of the function. * * @param id the oid structure to format - * @return the c-string; NULL if memory is exhausted. Caller must - * deallocate the string with git__free(). + * @return the c-string */ -GIT_EXTERN(char *) git_oid_allocfmt(const git_oid *id); +GIT_EXTERN(char *) git_oid_tostr_s(const git_oid *oid); /** * Format a git_oid into a buffer as a hex format c-string. @@ -167,10 +171,7 @@ * @param b second oid structure. * @return true if equal, false otherwise */ -GIT_INLINE(int) git_oid_equal(const git_oid *a, const git_oid *b) -{ - return !git_oid_cmp(a, b); -} +GIT_EXTERN(int) git_oid_equal(const git_oid *a, const git_oid *b); /** * Compare the first 'len' hexadecimal characters (packets of 4 bits) diff -Nru libgit2-0.20.0/include/git2/pack.h libgit2-0.22.2/include/git2/pack.h --- libgit2-0.20.0/include/git2/pack.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/pack.h 2015-03-24 16:10:45.000000000 +0000 @@ -52,7 +52,7 @@ GIT_PACKBUILDER_ADDING_OBJECTS = 0, GIT_PACKBUILDER_DELTAFICATION = 1, } git_packbuilder_stage_t; - + /** * Initialize a new packbuilder * @@ -115,6 +115,17 @@ GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *id); /** + * Write the contents of the packfile to an in-memory buffer + * + * The contents of the buffer will become a valid packfile, even though there + * will be no attached index + * + * @param buf Buffer where to write the packfile + * @param pb The packbuilder + */ +GIT_EXTERN(int) git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb); + +/** * Write the new pack and corresponding index file to path. * * @param pb The packbuilder @@ -129,7 +140,7 @@ git_packbuilder *pb, const char *path, unsigned int mode, - git_transfer_progress_callback progress_cb, + git_transfer_progress_cb progress_cb, void *progress_cb_payload); /** @@ -143,6 +154,7 @@ GIT_EXTERN(const git_oid *) git_packbuilder_hash(git_packbuilder *pb); typedef int (*git_packbuilder_foreach_cb)(void *buf, size_t size, void *payload); + /** * Create the new pack and pass each object to the callback * diff -Nru libgit2-0.20.0/include/git2/patch.h libgit2-0.22.2/include/git2/patch.h --- libgit2-0.20.0/include/git2/patch.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/patch.h 2015-03-24 16:10:45.000000000 +0000 @@ -106,6 +106,34 @@ const git_diff_options *opts); /** + * Directly generate a patch from the difference between two buffers. + * + * This is just like `git_diff_buffers()` except it generates a patch + * object for the difference instead of directly making callbacks. You can + * use the standard `git_patch` accessor functions to read the patch + * data, and you must call `git_patch_free()` on the patch when done. + * + * @param out The generated patch; NULL on error + * @param old_buffer Raw data for old side of diff, or NULL for empty + * @param old_len Length of the raw data for old side of the diff + * @param old_as_path Treat old buffer as if it had this filename; can be NULL + * @param new_buffer Raw data for new side of diff, or NULL for empty + * @param new_len Length of raw data for new side of diff + * @param new_as_path Treat buffer as if it had this filename; can be NULL + * @param opts Options for diff, or NULL for default options + * @return 0 on success or error code < 0 + */ +GIT_EXTERN(int) git_patch_from_buffers( + git_patch **out, + const void *old_buffer, + size_t old_len, + const char *old_as_path, + const char *new_buffer, + size_t new_len, + const char *new_as_path, + const git_diff_options *opts); + +/** * Free a git_patch object. */ GIT_EXTERN(void) git_patch_free(git_patch *patch); @@ -113,12 +141,12 @@ /** * Get the delta associated with a patch */ -GIT_EXTERN(const git_diff_delta *) git_patch_get_delta(git_patch *patch); +GIT_EXTERN(const git_diff_delta *) git_patch_get_delta(const git_patch *patch); /** * Get the number of hunks in a patch */ -GIT_EXTERN(size_t) git_patch_num_hunks(git_patch *patch); +GIT_EXTERN(size_t) git_patch_num_hunks(const git_patch *patch); /** * Get line counts of each type in a patch. @@ -169,7 +197,7 @@ * @return Number of lines in hunk or -1 if invalid hunk index */ GIT_EXTERN(int) git_patch_num_lines_in_hunk( - git_patch *patch, + const git_patch *patch, size_t hunk_idx); /** @@ -218,13 +246,13 @@ * Serialize the patch to text via callback. * * Returning a non-zero value from the callback will terminate the iteration - * and cause this return `GIT_EUSER`. + * and return that value to the caller. * * @param patch A git_patch representing changes to one file * @param print_cb Callback function to output lines of the patch. Will be * called for file headers, hunk headers, and diff lines. * @param payload Reference pointer that will be passed to your callbacks. - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_patch_print( git_patch *patch, @@ -234,15 +262,14 @@ /** * Get the content of a patch as a single diff text. * - * @param string Allocated string; caller must free. + * @param out The git_buf to be filled in * @param patch A git_patch representing changes to one file * @return 0 on success, <0 on failure. */ -GIT_EXTERN(int) git_patch_to_str( - char **string, +GIT_EXTERN(int) git_patch_to_buf( + git_buf *out, git_patch *patch); - GIT_END_DECL /**@}*/ diff -Nru libgit2-0.20.0/include/git2/pathspec.h libgit2-0.22.2/include/git2/pathspec.h --- libgit2-0.20.0/include/git2/pathspec.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/pathspec.h 2015-03-24 16:10:45.000000000 +0000 @@ -12,6 +12,8 @@ #include "strarray.h" #include "diff.h" +GIT_BEGIN_DECL + /** * Compiled pathspec */ @@ -257,4 +259,5 @@ GIT_EXTERN(const char *) git_pathspec_match_list_failed_entry( const git_pathspec_match_list *m, size_t pos); +GIT_END_DECL #endif diff -Nru libgit2-0.20.0/include/git2/push.h libgit2-0.22.2/include/git2/push.h --- libgit2-0.20.0/include/git2/push.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/push.h 2015-03-24 16:10:45.000000000 +0000 @@ -39,6 +39,19 @@ #define GIT_PUSH_OPTIONS_VERSION 1 #define GIT_PUSH_OPTIONS_INIT { GIT_PUSH_OPTIONS_VERSION } +/** + * Initializes a `git_push_options` with default values. Equivalent to + * creating an instance with GIT_PUSH_OPTIONS_INIT. + * + * @param opts the `git_push_options` instance to initialize. + * @param version the version of the struct; you should pass + * `GIT_PUSH_OPTIONS_VERSION` here. + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_push_init_options( + git_push_options *opts, + unsigned int version); + /** Push network progress notification function */ typedef int (*git_push_transfer_progress)( unsigned int current, @@ -46,115 +59,6 @@ size_t bytes, void* payload); -/** - * Create a new push object - * - * @param out New push object - * @param remote Remote instance - * - * @return 0 or an error code - */ -GIT_EXTERN(int) git_push_new(git_push **out, git_remote *remote); - -/** - * Set options on a push object - * - * @param push The push object - * @param opts The options to set on the push object - * - * @return 0 or an error code - */ -GIT_EXTERN(int) git_push_set_options( - git_push *push, - const git_push_options *opts); - -/** - * Set the callbacks for a push - * - * @param push The push object - * @param pack_progress_cb Function to call with progress information during - * pack building. Be aware that this is called inline with pack building - * operations, so performance may be affected. - * @param pack_progress_cb_payload Payload for the pack progress callback. - * @param transfer_progress_cb Function to call with progress information during - * the upload portion of a push. Be aware that this is called inline with - * pack building operations, so performance may be affected. - * @param transfer_progress_cb_payload Payload for the network progress callback. - * @return 0 or an error code - */ -GIT_EXTERN(int) git_push_set_callbacks( - git_push *push, - git_packbuilder_progress pack_progress_cb, - void *pack_progress_cb_payload, - git_push_transfer_progress transfer_progress_cb, - void *transfer_progress_cb_payload); - -/** - * Add a refspec to be pushed - * - * @param push The push object - * @param refspec Refspec string - * - * @return 0 or an error code - */ -GIT_EXTERN(int) git_push_add_refspec(git_push *push, const char *refspec); - -/** - * Update remote tips after a push - * - * @param push The push object - * - * @return 0 or an error code - */ -GIT_EXTERN(int) git_push_update_tips(git_push *push); - -/** - * Actually push all given refspecs - * - * Note: To check if the push was successful (i.e. all remote references - * have been updated as requested), you need to call both - * `git_push_unpack_ok` and `git_push_status_foreach`. The remote - * repository might have refused to update some or all of the references. - * - * @param push The push object - * - * @return 0 or an error code - */ -GIT_EXTERN(int) git_push_finish(git_push *push); - -/** - * Check if remote side successfully unpacked - * - * @param push The push object - * - * @return true if remote side successfully unpacked, false otherwise - */ -GIT_EXTERN(int) git_push_unpack_ok(git_push *push); - -/** - * Call callback `cb' on each status - * - * For each of the updated references, we receive a status report in the - * form of `ok refs/heads/master` or `ng refs/heads/master `. - * `msg != NULL` means the reference has not been updated for the given - * reason. - * - * @param push The push object - * @param cb The callback to call on each object - * - * @return 0 on success, GIT_EUSER on non-zero callback, or error code - */ -GIT_EXTERN(int) git_push_status_foreach(git_push *push, - int (*cb)(const char *ref, const char *msg, void *data), - void *data); - -/** - * Free the given push object - * - * @param push The push object - */ -GIT_EXTERN(void) git_push_free(git_push *push); - /** @} */ GIT_END_DECL #endif diff -Nru libgit2-0.20.0/include/git2/rebase.h libgit2-0.22.2/include/git2/rebase.h --- libgit2-0.20.0/include/git2/rebase.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/include/git2/rebase.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,274 @@ +/* + * 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_git_rebase_h__ +#define INCLUDE_git_rebase_h__ + +#include "common.h" +#include "types.h" +#include "oid.h" +#include "annotated_commit.h" + +/** + * @file git2/rebase.h + * @brief Git rebase routines + * @defgroup git_rebase Git merge routines + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Rebase options + * + * Use to tell the rebase machinery how to operate. + */ +typedef struct { + unsigned int version; + + /** + * Provide a quiet rebase experience; unused by libgit2 but provided for + * interoperability with other clients. + */ + int quiet; + + /** + * Canonical name of the notes reference used to rewrite notes for + * rebased commits when finishing the rebase; if NULL, the contents of + * the coniguration option `notes.rewriteRef` is examined, unless the + * configuration option `notes.rewrite.rebase` is set to false. If + * `notes.rewriteRef` is NULL, notes will not be rewritten. + */ + const char *rewrite_notes_ref; +} git_rebase_options; + +/** + * Type of rebase operation in-progress after calling `git_rebase_next`. + */ +typedef enum { + /** + * The given commit is to be cherry-picked. The client should commit + * the changes and continue if there are no conflicts. + */ + GIT_REBASE_OPERATION_PICK = 0, + + /** + * The given commit is to be cherry-picked, but the client should prompt + * the user to provide an updated commit message. + */ + GIT_REBASE_OPERATION_REWORD, + + /** + * The given commit is to be cherry-picked, but the client should stop + * to allow the user to edit the changes before committing them. + */ + GIT_REBASE_OPERATION_EDIT, + + /** + * The given commit is to be squashed into the previous commit. The + * commit message will be merged with the previous message. + */ + GIT_REBASE_OPERATION_SQUASH, + + /** + * The given commit is to be squashed into the previous commit. The + * commit message from this commit will be discarded. + */ + GIT_REBASE_OPERATION_FIXUP, + + /** + * No commit will be cherry-picked. The client should run the given + * command and (if successful) continue. + */ + GIT_REBASE_OPERATION_EXEC, +} git_rebase_operation_t; + +#define GIT_REBASE_OPTIONS_VERSION 1 +#define GIT_REBASE_OPTIONS_INIT {GIT_REBASE_OPTIONS_VERSION} + +/** + * A rebase operation + * + * Describes a single instruction/operation to be performed during the + * rebase. + */ +typedef struct { + /** The type of rebase operation. */ + git_rebase_operation_t type; + + /** + * The commit ID being cherry-picked. This will be populated for + * all operations except those of type `GIT_REBASE_OPERATION_EXEC`. + */ + const git_oid id; + + /** + * The executable the user has requested be run. This will only + * be populated for operations of type `GIT_REBASE_OPERATION_EXEC`. + */ + const char *exec; +} git_rebase_operation; + +/** + * Initializes a `git_rebase_options` with default values. Equivalent to + * creating an instance with GIT_REBASE_OPTIONS_INIT. + * + * @param opts the `git_rebase_options` instance to initialize. + * @param version the version of the struct; you should pass + * `GIT_REBASE_OPTIONS_VERSION` here. + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_rebase_init_options( + git_rebase_options *opts, + unsigned int version); + +/** + * Initializes a rebase operation to rebase the changes in `branch` + * relative to `upstream` onto another branch. To begin the rebase + * process, call `git_rebase_next`. When you have finished with this + * object, call `git_rebase_free`. + * + * @param out Pointer to store the rebase object + * @param repo The repository to perform the rebase + * @param branch The terminal commit to rebase + * @param upstream The commit to begin rebasing from, or NULL to rebase all + * reachable commits + * @param onto The branch to rebase onto, or NULL to rebase onto the given + * upstream + * @param signature The signature of the rebaser (optional) + * @param opts Options to specify how rebase is performed + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_rebase_init( + git_rebase **out, + git_repository *repo, + const git_annotated_commit *branch, + const git_annotated_commit *upstream, + const git_annotated_commit *onto, + const git_signature *signature, + const git_rebase_options *opts); + +/** + * Opens an existing rebase that was previously started by either an + * invocation of `git_rebase_init` or by another client. + * + * @param out Pointer to store the rebase object + * @param reop The repository that has a rebase in-progress + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_rebase_open(git_rebase **out, git_repository *repo); + +/** + * Gets the count of rebase operations that are to be applied. + * + * @param rebase The in-progress rebase + * @return The number of rebase operations in total + */ +GIT_EXTERN(size_t) git_rebase_operation_entrycount(git_rebase *rebase); + +/** + * Gets the index of the rebase operation that is currently being applied. + * + * @param rebase The in-progress rebase + * @return The index of the rebase operation currently being applied. + */ +GIT_EXTERN(size_t) git_rebase_operation_current(git_rebase *rebase); + +/** + * Gets the rebase operation specified by the given index. + * + * @param rebase The in-progress rebase + * @param idx The index of the rebase operation to retrieve + * @return The rebase operation or NULL if `idx` was out of bounds + */ +GIT_EXTERN(git_rebase_operation *) git_rebase_operation_byindex( + git_rebase *rebase, + size_t idx); + +/** + * Performs the next rebase operation and returns the information about it. + * If the operation is one that applies a patch (which is any operation except + * GIT_REBASE_OPERATION_EXEC) then the patch will be applied and the index and + * working directory will be updated with the changes. If there are conflicts, + * you will need to address those before committing the changes. + * + * @param out Pointer to store the rebase operation that is to be performed next + * @param repo The rebase in progress + * @param checkout_opts Options to specify how the patch should be checked out + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_rebase_next( + git_rebase_operation **operation, + git_rebase *rebase, + git_checkout_options *checkout_opts); + +/** + * Commits the current patch. You must have resolved any conflicts that + * were introduced during the patch application from the `git_rebase_next` + * invocation. + * + * @param id Pointer in which to store the OID of the newly created commit + * @param repo The rebase that is in-progress + * @param author The author of the updated commit, or NULL to keep the + * author from the original commit + * @param committer The committer of the rebase + * @param message_encoding The encoding for the message in the commit, + * represented with a standard encoding name. If message is NULL, + * this should also be NULL, and the encoding from the original + * commit will be maintained. If message is specified, this may be + * NULL to indicate that "UTF-8" is to be used. + * @param message The message for this commit, or NULL to use the message + * from the original commit. + * @return Zero on success, GIT_EUNMERGED if there are unmerged changes in + * the index, GIT_EAPPLIED if the current commit has already + * been applied to the upstream and there is nothing to commit, + * -1 on failure. + */ +GIT_EXTERN(int) git_rebase_commit( + git_oid *id, + git_rebase *rebase, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message); + +/** + * Aborts a rebase that is currently in progress, resetting the repository + * and working directory to their state before rebase began. + * + * @param rebase The rebase that is in-progress + * @param signature The identity that is aborting the rebase + * @return Zero on success; GIT_ENOTFOUND if a rebase is not in progress, + * -1 on other errors. + */ +GIT_EXTERN(int) git_rebase_abort( + git_rebase *rebase, + const git_signature *signature); + +/** + * Finishes a rebase that is currently in progress once all patches have + * been applied. + * + * @param rebase The rebase that is in-progress + * @param signature The identity that is finishing the rebase (optional) + * @param opts Options to specify how rebase is finished + * @param Zero on success; -1 on error + */ +GIT_EXTERN(int) git_rebase_finish( + git_rebase *rebase, + const git_signature *signature, + const git_rebase_options *opts); + +/** + * Frees the `git_rebase` object. + * + * @param rebase The rebase object + */ +GIT_EXTERN(void) git_rebase_free(git_rebase *rebase); + +/** @} */ +GIT_END_DECL +#endif diff -Nru libgit2-0.20.0/include/git2/reflog.h libgit2-0.22.2/include/git2/reflog.h --- libgit2-0.20.0/include/git2/reflog.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/reflog.h 2015-03-24 16:10:45.000000000 +0000 @@ -47,7 +47,7 @@ GIT_EXTERN(int) git_reflog_write(git_reflog *reflog); /** - * Add a new entry to the reflog. + * Add a new entry to the in-memory reflog. * * `msg` is optional and can be NULL. * @@ -60,23 +60,6 @@ GIT_EXTERN(int) git_reflog_append(git_reflog *reflog, const git_oid *id, const git_signature *committer, const char *msg); /** - * Add a new entry to the named reflog. - * - * This utility function loads the named reflog, appends to it and - * writes it back out to the backend. - * - * `msg` is optional and can be NULL. - * - * @param repo the repository to act on - * @param name the reflog's name - * @param id the OID the reference is now pointing to - * @param committer the signature of the committer - * @param msg the reflog message - * @return 0 or an error code - */ -GIT_EXTERN(int) git_reflog_append_to(git_repository *repo, const char *name, const git_oid *id, const git_signature *committer, const char *msg); - -/** * Rename a reflog * * The reflog to be renamed is expected to already exist @@ -86,7 +69,7 @@ * * @param repo the repository * @param old_name the old name of the reference - * @param new_name the new name of the reference + * @param name the new name of the reference * @return 0 on success, GIT_EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_reflog_rename(git_repository *repo, const char *old_name, const char *name); @@ -119,7 +102,7 @@ * equal to 0 (zero) and less than `git_reflog_entrycount()`. * @return the entry; NULL if not found */ -GIT_EXTERN(const git_reflog_entry *) git_reflog_entry_byindex(git_reflog *reflog, size_t idx); +GIT_EXTERN(const git_reflog_entry *) git_reflog_entry_byindex(const git_reflog *reflog, size_t idx); /** * Remove an entry from the reflog by its index diff -Nru libgit2-0.20.0/include/git2/refs.h libgit2-0.22.2/include/git2/refs.h --- libgit2-0.20.0/include/git2/refs.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/refs.h 2015-03-24 16:10:45.000000000 +0000 @@ -27,7 +27,7 @@ * The returned reference must be freed by the user. * * The name will be checked for validity. - * See `git_reference_create_symbolic()` for rules about valid names. + * See `git_reference_symbolic_create()` for rules about valid names. * * @param out pointer to the looked-up reference * @param repo the repository to look up the reference @@ -58,7 +58,7 @@ * Lookup a reference by DWIMing its short name * * Apply the git precendence rules to the given shorthand to determine - * which reference the user is refering to. + * which reference the user is referring to. * * @param out pointer in which to store the reference * @param repo the repository in which to look @@ -68,6 +68,48 @@ GIT_EXTERN(int) git_reference_dwim(git_reference **out, git_repository *repo, const char *shorthand); /** + * Conditionally create a new symbolic reference. + * + * A symbolic reference is a reference name that refers to another + * reference name. If the other name moves, the symbolic name will move, + * too. As a simple example, the "HEAD" reference might refer to + * "refs/heads/master" while on the "master" branch of a repository. + * + * The symbolic reference will be created in the repository and written to + * the disk. The generated reference object must be freed by the user. + * + * Valid reference names must follow one of two patterns: + * + * 1. Top-level names must contain only capital letters and underscores, + * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD"). + * 2. Names prefixed with "refs/" can be almost anything. You must avoid + * the characters '~', '^', ':', '\\', '?', '[', and '*', and the + * sequences ".." and "@{" which have special meaning to revparse. + * + * This function will return an error if a reference already exists with the + * given name unless `force` is true, in which case it will be overwritten. + * + * The signature and message for the reflog will be ignored if the + * reference does not belong in the standard set (HEAD, branches and + * remote-tracking branches) and it does not have a reflog. + * + * It will return GIT_EMODIFIED if the reference's value at the time + * of updating does not match the one passed through `current_value` + * (i.e. if the ref has changed since the user read it). + * + * @param out Pointer to the newly created reference + * @param repo Repository where that reference will live + * @param name The name of the reference + * @param target The target of the reference + * @param force Overwrite existing references + * @param current_value The expected value of the reference when updating + * @param signature The identity that will used to populate the reflog entry + * @param log_message The one line long message to be appended to the reflog + * @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC, GIT_EMODIFIED or an error code + */ +GIT_EXTERN(int) git_reference_symbolic_create_matching(git_reference **out, git_repository *repo, const char *name, const char *target, int force, const char *current_value, const git_signature *signature, const char *log_message); + +/** * Create a new symbolic reference. * * A symbolic reference is a reference name that refers to another @@ -89,14 +131,20 @@ * This function will return an error if a reference already exists with the * given name unless `force` is true, in which case it will be overwritten. * + * The signature and message for the reflog will be ignored if the + * reference does not belong in the standard set (HEAD, branches and + * remote-tracking branches) and it does not have a reflog. + * * @param out Pointer to the newly created reference * @param repo Repository where that reference will live * @param name The name of the reference * @param target The target of the reference * @param force Overwrite existing references + * @param signature The identity that will used to populate the reflog entry + * @param log_message The one line long message to be appended to the reflog * @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code */ -GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repository *repo, const char *name, const char *target, int force); +GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repository *repo, const char *name, const char *target, int force, const git_signature *signature, const char *log_message); /** * Create a new direct reference. @@ -121,14 +169,64 @@ * This function will return an error if a reference already exists with the * given name unless `force` is true, in which case it will be overwritten. * + * The signature and message for the reflog will be ignored if the + * reference does not belong in the standard set (HEAD, branches and + * remote-tracking branches) and and it does not have a reflog. + * * @param out Pointer to the newly created reference * @param repo Repository where that reference will live * @param name The name of the reference * @param id The object id pointed to by the reference. * @param force Overwrite existing references + * @param signature The identity that will used to populate the reflog entry + * @param log_message The one line long message to be appended to the reflog * @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code */ -GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force); +GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force, const git_signature *signature, const char *log_message); + +/** + * Conditionally create new direct reference + * + * A direct reference (also called an object id reference) refers directly + * to a specific object id (a.k.a. OID or SHA) in the repository. The id + * permanently refers to the object (although the reference itself can be + * moved). For example, in libgit2 the direct ref "refs/tags/v0.17.0" + * refers to OID 5b9fac39d8a76b9139667c26a63e6b3f204b3977. + * + * The direct reference will be created in the repository and written to + * the disk. The generated reference object must be freed by the user. + * + * Valid reference names must follow one of two patterns: + * + * 1. Top-level names must contain only capital letters and underscores, + * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD"). + * 2. Names prefixed with "refs/" can be almost anything. You must avoid + * the characters '~', '^', ':', '\\', '?', '[', and '*', and the + * sequences ".." and "@{" which have special meaning to revparse. + * + * This function will return an error if a reference already exists with the + * given name unless `force` is true, in which case it will be overwritten. + * + * The signature and message for the reflog will be ignored if the + * reference does not belong in the standard set (HEAD, branches and + * remote-tracking branches) and and it does not have a reflog. + * + * It will return GIT_EMODIFIED if the reference's value at the time + * of updating does not match the one passed through `current_id` + * (i.e. if the ref has changed since the user read it). + * + * @param out Pointer to the newly created reference + * @param repo Repository where that reference will live + * @param name The name of the reference + * @param id The object id pointed to by the reference. + * @param force Overwrite existing references + * @param current_id The expected value of the reference at the time of update + * @param signature The identity that will used to populate the reflog entry + * @param log_message The one line long message to be appended to the reflog + * @return 0 on success, GIT_EMODIFIED if the value of the reference + * has changed, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code + */ +GIT_EXTERN(int) git_reference_create_matching(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force, const git_oid *current_id, const git_signature *signature, const char *log_message); /** * Get the OID pointed to by a direct reference. @@ -179,7 +277,7 @@ /** * Get the full name of a reference. * - * See `git_reference_create_symbolic()` for rules about valid names. + * See `git_reference_symbolic_create()` for rules about valid names. * * @param ref The reference * @return the full name for the ref @@ -220,20 +318,28 @@ * The new reference will be written to disk, overwriting the given reference. * * The target name will be checked for validity. - * See `git_reference_create_symbolic()` for rules about valid names. + * See `git_reference_symbolic_create()` for rules about valid names. + * + * The signature and message for the reflog will be ignored if the + * reference does not belong in the standard set (HEAD, branches and + * remote-tracking branches) and and it does not have a reflog. * * @param out Pointer to the newly created reference * @param ref The reference * @param target The new target for the reference + * @param signature The identity that will used to populate the reflog entry + * @param log_message The one line long message to be appended to the reflog * @return 0 on success, GIT_EINVALIDSPEC or an error code */ GIT_EXTERN(int) git_reference_symbolic_set_target( git_reference **out, git_reference *ref, - const char *target); + const char *target, + const git_signature *signature, + const char *log_message); /** - * Create a new reference with the same name as the given reference but a + * Conditionally create a new reference with the same name as the given reference but a * different OID target. The reference must be a direct reference, otherwise * this will fail. * @@ -242,12 +348,17 @@ * @param out Pointer to the newly created reference * @param ref The reference * @param id The new target OID for the reference - * @return 0 or an error code + * @param signature The identity that will used to populate the reflog entry + * @param log_message The one line long message to be appended to the reflog + * @return 0 on success, GIT_EMODIFIED if the value of the reference + * has changed since it was read, or an error code */ GIT_EXTERN(int) git_reference_set_target( git_reference **out, git_reference *ref, - const git_oid *id); + const git_oid *id, + const git_signature *signature, + const char *log_message); /** * Rename an existing reference. @@ -255,7 +366,7 @@ * This method works for both direct and symbolic references. * * The new name will be checked for validity. - * See `git_reference_create_symbolic()` for rules about valid names. + * See `git_reference_symbolic_create()` for rules about valid names. * * If the `force` flag is not enabled, and there's already * a reference with the given name, the renaming will fail. @@ -268,6 +379,8 @@ * @param ref The reference to rename * @param new_name The new name for the reference * @param force Overwrite an existing reference + * @param signature The identity that will used to populate the reflog entry + * @param log_message The one line long message to be appended to the reflog * @return 0 on success, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code * */ @@ -275,7 +388,9 @@ git_reference **new_ref, git_reference *ref, const char *new_name, - int force); + int force, + const git_signature *signature, + const char *log_message); /** * Delete an existing reference. @@ -284,12 +399,26 @@ * will be immediately removed on disk but the memory will not be freed. * Callers must call `git_reference_free`. * + * This function will return an error if the reference has changed + * from the time it was looked up. + * * @param ref The reference to remove - * @return 0 or an error code + * @return 0, GIT_EMODIFIED or an error code */ GIT_EXTERN(int) git_reference_delete(git_reference *ref); /** + * Delete an existing reference by name + * + * This method removes the named reference from the repository without + * looking at its old value. + * + * @param name The reference to remove + * @return 0 or an error code + */ +GIT_EXTERN(int) git_reference_remove(git_repository *repo, const char *name); + +/** * Fill a list with all the references that can be found in a repository. * * The string array will be filled with the names of all references; these @@ -310,20 +439,33 @@ * Perform a callback on each reference in the repository. * * The `callback` function will be called for each reference in the - * repository, receiving the name of the reference and the `payload` value + * repository, receiving the reference object and the `payload` value * passed to this method. Returning a non-zero value from the callback * will terminate the iteration. * * @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 - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_reference_foreach( git_repository *repo, git_reference_foreach_cb callback, void *payload); +/** + * Perform a callback on the fully-qualified name of each reference. + * + * The `callback` function will be called for each reference in the + * repository, receiving the name of the reference and the `payload` value + * passed to this method. Returning a non-zero value from the callback + * will terminate the iteration. + * + * @param repo Repository where to find the refs + * @param callback Function which will be called for every listed ref name + * @param payload Additional data to pass to the callback + * @return 0 on success, non-zero callback return value, or error code + */ GIT_EXTERN(int) git_reference_foreach_name( git_repository *repo, git_reference_foreach_name_cb callback, @@ -343,7 +485,9 @@ * @param ref2 The second git_reference * @return 0 if the same, else a stable but meaningless ordering. */ -GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2); +GIT_EXTERN(int) git_reference_cmp( + const git_reference *ref1, + const git_reference *ref2); /** * Create an iterator for the repo's references @@ -379,6 +523,17 @@ */ GIT_EXTERN(int) git_reference_next(git_reference **out, git_reference_iterator *iter); +/** + * Get the next reference's name + * + * This function is provided for convenience in case only the names + * are interesting as it avoids the allocation of the `git_reference` + * object which `git_reference_next()` needs. + * + * @param out pointer in which to store the string + * @param iter the iterator + * @return 0, GIT_ITEROVER if there are no more; or an error code + */ GIT_EXTERN(int) git_reference_next_name(const char **out, git_reference_iterator *iter); /** @@ -415,12 +570,24 @@ /** * Check if a reflog exists for the specified reference. * - * @param ref A git reference - * + * @param repo the repository + * @param refname the reference's name * @return 0 when no reflog can be found, 1 when it exists; * otherwise an error code. */ -GIT_EXTERN(int) git_reference_has_log(git_reference *ref); +GIT_EXTERN(int) git_reference_has_log(git_repository *repo, const char *refname); + +/** + * Ensure there is a reflog for a particular reference. + * + * Make sure that successive updates to the reference will append to + * its log. + * + * @param repo the repository + * @param refname the reference's name + * @return 0 or an error code. + */ +GIT_EXTERN(int) git_reference_ensure_log(git_repository *repo, const char *refname); /** * Check if a reference is a local branch. @@ -430,7 +597,7 @@ * @return 1 when the reference lives in the refs/heads * namespace; 0 otherwise. */ -GIT_EXTERN(int) git_reference_is_branch(git_reference *ref); +GIT_EXTERN(int) git_reference_is_branch(const git_reference *ref); /** * Check if a reference is a remote tracking branch @@ -440,7 +607,7 @@ * @return 1 when the reference lives in the refs/remotes * namespace; 0 otherwise. */ -GIT_EXTERN(int) git_reference_is_remote(git_reference *ref); +GIT_EXTERN(int) git_reference_is_remote(const git_reference *ref); /** * Check if a reference is a tag @@ -450,9 +617,25 @@ * @return 1 when the reference lives in the refs/tags * namespace; 0 otherwise. */ -GIT_EXTERN(int) git_reference_is_tag(git_reference *ref); +GIT_EXTERN(int) git_reference_is_tag(const git_reference *ref); + +/** + * Check if a reference is a note + * + * @param ref A git reference + * + * @return 1 when the reference lives in the refs/notes + * namespace; 0 otherwise. + */ +GIT_EXTERN(int) git_reference_is_note(const git_reference *ref); +/** + * Normalization options for reference lookup + */ typedef enum { + /** + * No particular normalization. + */ GIT_REF_FORMAT_NORMAL = 0u, /** @@ -490,7 +673,7 @@ * Once normalized, if the reference name is valid, it will be returned in * the user allocated buffer. * - * See `git_reference_create_symbolic()` for rules about valid names. + * See `git_reference_symbolic_create()` for rules about valid names. * * @param buffer_out User allocated buffer to store normalized name * @param buffer_size Size of buffer_out @@ -554,7 +737,7 @@ * @param ref a reference * @return the human-readable version of the name */ -GIT_EXTERN(const char *) git_reference_shorthand(git_reference *ref); +GIT_EXTERN(const char *) git_reference_shorthand(const git_reference *ref); /** @} */ diff -Nru libgit2-0.20.0/include/git2/refspec.h libgit2-0.22.2/include/git2/refspec.h --- libgit2-0.20.0/include/git2/refspec.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/refspec.h 2015-03-24 16:10:45.000000000 +0000 @@ -10,6 +10,7 @@ #include "common.h" #include "types.h" #include "net.h" +#include "buffer.h" /** * @file git2/refspec.h @@ -82,23 +83,21 @@ * Transform a reference to its target following the refspec's rules * * @param out where to store the target name - * @param outlen the size of the `out` buffer * @param spec the refspec * @param name the name of the reference to transform * @return 0, GIT_EBUFS or another error */ -GIT_EXTERN(int) git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name); +GIT_EXTERN(int) git_refspec_transform(git_buf *out, const git_refspec *spec, const char *name); /** * Transform a target reference to its source reference following the refspec's rules * * @param out where to store the source reference name - * @param outlen the size of the `out` buffer * @param spec the refspec * @param name the name of the reference to transform * @return 0, GIT_EBUFS or another error */ -GIT_EXTERN(int) git_refspec_rtransform(char *out, size_t outlen, const git_refspec *spec, const char *name); +GIT_EXTERN(int) git_refspec_rtransform(git_buf *out, const git_refspec *spec, const char *name); GIT_END_DECL diff -Nru libgit2-0.20.0/include/git2/remote.h libgit2-0.22.2/include/git2/remote.h --- libgit2-0.20.0/include/git2/remote.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/remote.h 2015-03-24 16:10:45.000000000 +0000 @@ -14,6 +14,7 @@ #include "indexer.h" #include "strarray.h" #include "transport.h" +#include "push.h" /** * @file git2/remote.h @@ -62,10 +63,10 @@ const char *fetch); /** - * Create a remote in memory + * Create an anonymous remote * - * Create a remote with the given refspec in memory. You can use - * this when you have a URL instead of a remote's name. Note that in-memory + * Create a remote with the given url and refspec in memory. You can use + * this when you have a URL instead of a remote's name. Note that anonymous * remotes cannot be converted to persisted remotes. * * The name, when provided, will be checked for validity. @@ -73,15 +74,15 @@ * * @param out pointer to the new remote object * @param repo the associated repository - * @param fetch the fetch refspec to use for this remote. * @param url the remote repository's URL + * @param fetch the fetch refspec to use for this remote. * @return 0 or an error code */ -GIT_EXTERN(int) git_remote_create_inmemory( +GIT_EXTERN(int) git_remote_create_anonymous( git_remote **out, git_repository *repo, - const char *fetch, - const char *url); + const char *url, + const char *fetch); /** * Get the information for a particular remote @@ -94,7 +95,7 @@ * @param name the remote's name * @return 0, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code */ -GIT_EXTERN(int) git_remote_load(git_remote **out, git_repository *repo, const char *name); +GIT_EXTERN(int) git_remote_lookup(git_remote **out, git_repository *repo, const char *name); /** * Save a remote to its repository's configuration @@ -108,6 +109,18 @@ GIT_EXTERN(int) git_remote_save(const git_remote *remote); /** + * Create a copy of an existing remote. All internal strings are also + * duplicated. Callbacks are not duplicated. + * + * Call `git_remote_free` to free the data. + * + * @param dest pointer where to store the copy + * @param source object to copy + * @return 0 or an error code + */ +GIT_EXTERN(int) git_remote_dup(git_remote **dest, git_remote *source); + +/** * Get the remote's repository * * @param remote the remote @@ -182,7 +195,7 @@ * @param array pointer to the array in which to store the strings * @param remote the remote to query */ -GIT_EXTERN(int) git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote); +GIT_EXTERN(int) git_remote_get_fetch_refspecs(git_strarray *array, const git_remote *remote); /** * Set the remote's list of fetch refspecs @@ -215,7 +228,7 @@ * @param array pointer to the array in which to store the strings * @param remote the remote to query */ -GIT_EXTERN(int) git_remote_get_push_refspecs(git_strarray *array, git_remote *remote); +GIT_EXTERN(int) git_remote_get_push_refspecs(git_strarray *array, const git_remote *remote); /** * Set the remote's list of push refspecs @@ -242,7 +255,7 @@ * @param remote the remote * @return the amount of refspecs configured in this remote */ -GIT_EXTERN(size_t) git_remote_refspec_count(git_remote *remote); +GIT_EXTERN(size_t) git_remote_refspec_count(const git_remote *remote); /** * Get a refspec from the remote @@ -251,7 +264,7 @@ * @param n the refspec to get * @return the nth refspec */ -GIT_EXTERN(const git_refspec *)git_remote_get_refspec(git_remote *remote, size_t n); +GIT_EXTERN(const git_refspec *)git_remote_get_refspec(const git_remote *remote, size_t n); /** * Open a connection to a remote @@ -268,14 +281,19 @@ GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction); /** - * Get a list of refs at the remote + * Get the remote repository's reference advertisement list * - * The remote (or more exactly its transport) must be connected. The - * memory belongs to the remote. + * Get the list of references with which the server responds to a new + * connection. * - * The array will stay valid as long as the remote object exists and - * its transport isn't changed, but a copy is recommended for usage of - * the data. + * The remote (or more exactly its transport) must have connected to + * the remote repository. This list is available as soon as the + * connection to the remote is initiated and it remains available + * after disconnecting. + * + * The memory belongs to the remote. The pointer will be valid as long + * as a new connection is not initiated, but it is recommended that + * you make a copy in order to make use of the data. * * @param out pointer to the array * @param size the number of remote heads @@ -294,9 +312,25 @@ * The .idx file will be created and both it and the packfile with be * renamed to their final name. * + * @param remote the remote + * @param refspecs the refspecs to use for this negotiation and + * download. Use NULL or an empty array to use the base refspecs * @return 0 or an error code */ -GIT_EXTERN(int) git_remote_download(git_remote *remote); +GIT_EXTERN(int) git_remote_download(git_remote *remote, const git_strarray *refspecs); + +/** + * Create a packfile and send it to the server + * + * Connect to the remote if it hasn't been done yet, negotiate with + * the remote git which objects are missing, create a packfile with the missing objects and send it. + * + * @param remote the remote + * @param refspecs the refspecs to use for this negotiation and + * upload. Use NULL or an empty array to use the base refspecs + * @return 0 or an error code + */ +GIT_EXTERN(int) git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts); /** * Check whether the remote is connected @@ -307,7 +341,7 @@ * @param remote the remote * @return 1 if it's connected, 0 otherwise. */ -GIT_EXTERN(int) git_remote_connected(git_remote *remote); +GIT_EXTERN(int) git_remote_connected(const git_remote *remote); /** * Cancel the operation @@ -322,8 +356,7 @@ /** * Disconnect from the remote * - * Close the connection to the remote and free the underlying - * transport. + * Close the connection to the remote. * * @param remote the remote to disconnect from */ @@ -343,9 +376,24 @@ * Update the tips to the new state * * @param remote the remote to update + * @param signature The identity to use when updating reflogs + * @param reflog_message The message to insert into the reflogs. If NULL, the + * default is "fetch ", where is the name of + * the remote (or its url, for in-memory remotes). * @return 0 or an error code */ -GIT_EXTERN(int) git_remote_update_tips(git_remote *remote); +GIT_EXTERN(int) git_remote_update_tips( + git_remote *remote, + const git_signature *signature, + const char *reflog_message); + +/** + * Prune tracking refs that are no longer present on remote + * + * @param remote the remote to prune + * @return 0 or an error code + */ +GIT_EXTERN(int) git_remote_prune(git_remote *remote); /** * Download new data and update tips @@ -354,25 +402,35 @@ * disconnect and update the remote-tracking branches. * * @param remote the remote to fetch from + * @param refspecs the refspecs to use for this fetch. Pass NULL or an + * empty array to use the base refspecs. + * @param signature The identity to use when updating reflogs + * @param reflog_message The message to insert into the reflogs. If NULL, the + * default is "fetch" * @return 0 or an error code */ -GIT_EXTERN(int) git_remote_fetch(git_remote *remote); - -/** - * Return whether a string is a valid remote URL - * - * @param url the url to check - * @return 1 if the url is valid, 0 otherwise - */ -GIT_EXTERN(int) git_remote_valid_url(const char *url); - -/** - * Return whether the passed URL is supported by this version of the library. - * - * @param url the url to check - * @return 1 if the url is supported, 0 otherwise -*/ -GIT_EXTERN(int) git_remote_supported_url(const char* url); +GIT_EXTERN(int) git_remote_fetch( + git_remote *remote, + const git_strarray *refspecs, + const git_signature *signature, + const char *reflog_message); + +/** + * Perform a push + * + * Peform all the steps from a push. + * + * @param remote the remote to push to + * @param refspecs the refspecs to use for pushing. If none are + * passed, the configured refspecs will be used + * @param opts the options + * @param signature signature to use for the reflog of updated references + * @param reflog_message message to use for the reflog of upated references + */ +GIT_EXTERN(int) git_remote_push(git_remote *remote, + const git_strarray *refspecs, + const git_push_options *opts, + const git_signature *signature, const char *reflog_message); /** * Get a list of the configured remotes for a repo @@ -386,30 +444,6 @@ GIT_EXTERN(int) git_remote_list(git_strarray *out, git_repository *repo); /** - * Choose whether to check the server's certificate (applies to HTTPS only) - * - * @param remote the remote to configure - * @param check whether to check the server's certificate (defaults to yes) - */ -GIT_EXTERN(void) git_remote_check_cert(git_remote *remote, int check); - -/** - * Sets a custom transport for the remote. The caller can use this function - * to bypass the automatic discovery of a transport by URL scheme (i.e. - * http://, https://, git://) and supply their own transport to be used - * instead. After providing the transport to a remote using this function, - * the transport object belongs exclusively to that remote, and the remote will - * free it when it is freed with git_remote_free. - * - * @param remote the remote to configure - * @param transport the transport object for the remote to use - * @return 0 or an error code - */ -GIT_EXTERN(int) git_remote_set_transport( - git_remote *remote, - git_transport *transport); - -/** * Argument to the completion callback which tells it which operation * finished. */ @@ -432,7 +466,7 @@ * progress side-band will be passed to this function (this is * the 'counting objects' output. */ - int (*progress)(const char *str, int len, void *data); + git_transport_message_cb sideband_progress; /** * Completion is called when different parts of the download @@ -443,15 +477,26 @@ /** * This will be called if the remote host requires * authentication in order to connect to it. + * + * Returning GIT_PASSTHROUGH will make libgit2 behave as + * though this field isn't set. + */ + git_cred_acquire_cb credentials; + + /** + * If cert verification fails, this will be called to let the + * user make the final decision of whether to allow the + * connection to proceed. Returns 1 to allow the connection, 0 + * to disallow it or a negative value to indicate an error. */ - int (*credentials)(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *data); + git_transport_certificate_check_cb certificate_check; /** * During the download of new data, this will be regularly * called with the current count of progress done by the * indexer. */ - int (*transfer_progress)(const git_transfer_progress *stats, void *data); + git_transfer_progress_cb transfer_progress; /** * Each time a reference is updated locally, this function @@ -460,6 +505,28 @@ int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data); /** + * Function to call with progress information during pack + * building. Be aware that this is called inline with pack + * building operations, so performance may be affected. + */ + git_packbuilder_progress pack_progress; + + /** + * Function to call with progress information during the + * upload portion of a push. Be aware that this is called + * inline with pack building operations, so performance may be + * affected. + */ + 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. + */ + int (*push_update_reference)(const char *refname, const char *status, void *data); + + /** * This will be passed to each of the callbacks in this struct * as the last parameter. */ @@ -470,6 +537,18 @@ #define GIT_REMOTE_CALLBACKS_INIT {GIT_REMOTE_CALLBACKS_VERSION} /** + * Initializes a `git_remote_callbacks` with default values. Equivalent to + * creating an instance with GIT_REMOTE_CALLBACKS_INIT. + * + * @param opts the `git_remote_callbacks` struct to initialize + * @param version Version of struct; pass `GIT_REMOTE_CALLBACKS_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_remote_init_callbacks( + git_remote_callbacks *opts, + unsigned int version); + +/** * Set the callbacks for a remote * * Note that the remote keeps its own copy of the data and you need to @@ -482,10 +561,26 @@ GIT_EXTERN(int) git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks); /** + * Retrieve the current callback structure + * + * This provides read access to the callbacks structure as the remote + * sees it. + * + * @param remote the remote to query + * @return a pointer to the callbacks structure + */ +GIT_EXTERN(const git_remote_callbacks *) git_remote_get_callbacks(git_remote *remote); + +/** * Get the statistics structure that is filled in by the fetch operation. */ GIT_EXTERN(const git_transfer_progress *) git_remote_stats(git_remote *remote); +/** + * Automatic tag following option + * + * Lets us select the --tags option to use. + */ typedef enum { GIT_REMOTE_DOWNLOAD_TAGS_AUTO = 0, GIT_REMOTE_DOWNLOAD_TAGS_NONE = 1, @@ -498,7 +593,7 @@ * @param remote the remote to query * @return the auto-follow setting */ -GIT_EXTERN(git_remote_autotag_option_t) git_remote_autotag(git_remote *remote); +GIT_EXTERN(git_remote_autotag_option_t) git_remote_autotag(const git_remote *remote); /** * Set the tag auto-follow setting @@ -511,6 +606,14 @@ git_remote_autotag_option_t value); /** + * Retrieve the ref-prune setting + * + * @param remote the remote to query + * @return the ref-prune setting + */ +GIT_EXTERN(int) git_remote_prune_refs(const git_remote *remote); + +/** * Give the remote a new name * * All remote-tracking branches and configuration settings @@ -519,20 +622,22 @@ * The new name will be checked for validity. * See `git_tag_create()` for rules about valid names. * - * A temporary in-memory remote cannot be given a name with this method. + * No loaded instances of a the remote with the old name will change + * their name or their list of refspecs. * - * @param remote the remote to rename + * @param problems non-default refspecs cannot be renamed and will be + * stored here for further processing by the caller. Always free this + * strarray on successful return. + * @param repo the repository in which to rename + * @param name the current name of the reamote * @param new_name the new name the remote should bear - * @param callback Optional callback to notify the consumer of fetch refspecs - * that haven't been automatically updated and need potential manual tweaking. - * @param payload Additional data to pass to the callback * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code */ GIT_EXTERN(int) git_remote_rename( - git_remote *remote, - const char *new_name, - git_remote_rename_problem_cb callback, - void *payload); + git_strarray *problems, + git_repository *repo, + const char *name, + const char *new_name); /** * Retrieve the update FETCH_HEAD setting. @@ -559,6 +664,36 @@ */ GIT_EXTERN(int) git_remote_is_valid_name(const char *remote_name); +/** +* Delete an existing persisted remote. +* +* All remote-tracking branches and configuration settings +* for the remote will be removed. +* +* @param repo the repository in which to act +* @param name the name of the remove to delete +* @return 0 on success, or an error code. +*/ +GIT_EXTERN(int) git_remote_delete(git_repository *repo, const char *name); + +/** + * Retrieve the name of the remote's default branch + * + * The default branch of a repository is the branch which HEAD points + * to. If the remote does not support reporting this information + * directly, it performs the guess as git does; that is, if there are + * multiple branches which point to the same commit, the first one is + * chosen. If the master branch is a candidate, it wins. + * + * This function must only be called after connecting. + * + * @param out the buffern in which to store the reference name + * @param remote the remote + * @return 0, GIT_ENOTFOUND if the remote does not have any references + * or none of them point to HEAD's commit, or an error message. + */ +GIT_EXTERN(int) git_remote_default_branch(git_buf *out, git_remote *remote); + /** @} */ GIT_END_DECL #endif diff -Nru libgit2-0.20.0/include/git2/repository.h libgit2-0.22.2/include/git2/repository.h --- libgit2-0.20.0/include/git2/repository.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/repository.h 2015-03-24 16:10:45.000000000 +0000 @@ -10,6 +10,7 @@ #include "common.h" #include "types.h" #include "oid.h" +#include "buffer.h" /** * @file git2/repository.h @@ -58,10 +59,8 @@ * The method will automatically detect if the repository is bare * (if there is a repository). * - * @param path_out The user allocated buffer which will - * contain the found path. - * - * @param path_size repository_path size + * @param out A pointer to a user-allocated git_buf which will contain + * the found path. * * @param start_path The base path where the lookup starts. * @@ -77,8 +76,7 @@ * @return 0 or an error code */ GIT_EXTERN(int) git_repository_discover( - char *path_out, - size_t path_size, + git_buf *out, const char *start_path, int across_fs, const char *ceiling_dirs); @@ -198,6 +196,8 @@ * looking the "template_path" from the options if set, or the * `init.templatedir` global config if not, or falling back on * "/usr/share/git-core/templates" if it exists. + * * GIT_REPOSITORY_INIT_RELATIVE_GITLINK - If an alternate workdir is + * specified, use relative paths for the gitdir and core.worktree. */ typedef enum { GIT_REPOSITORY_INIT_BARE = (1u << 0), @@ -206,6 +206,7 @@ GIT_REPOSITORY_INIT_MKDIR = (1u << 3), GIT_REPOSITORY_INIT_MKPATH = (1u << 4), GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE = (1u << 5), + GIT_REPOSITORY_INIT_RELATIVE_GITLINK = (1u << 6), } git_repository_init_flag_t; /** @@ -270,6 +271,18 @@ #define GIT_REPOSITORY_INIT_OPTIONS_INIT {GIT_REPOSITORY_INIT_OPTIONS_VERSION} /** + * Initializes a `git_repository_init_options` with default values. Equivalent + * to creating an instance with GIT_REPOSITORY_INIT_OPTIONS_INIT. + * + * @param opts the `git_repository_init_options` struct to initialize + * @param version Version of struct; pass `GIT_REPOSITORY_INIT_OPTIONS_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_repository_init_init_options( + git_repository_init_options *opts, + unsigned int version); + +/** * Create a new Git repository in the given folder with extended controls. * * This will initialize a new git repository (creating the repo_path @@ -329,8 +342,8 @@ /** * Check if a repository is empty * - * An empty repository has just been initialized and contains - * no references. + * An empty repository has just been initialized and contains no references + * apart from HEAD, which must be pointing to the unborn master branch. * * @param repo Repo to test * @return 1 if the repository is empty, 0 if it isn't, error code @@ -398,13 +411,29 @@ * The configuration file must be freed once it's no longer * being used by the user. * - * @param out Pointer to store the loaded config file + * @param out Pointer to store the loaded configuration * @param repo A repository object * @return 0, or an error code */ GIT_EXTERN(int) git_repository_config(git_config **out, git_repository *repo); /** + * Get a snapshot of the repository's configuration + * + * Convenience function to take a snapshot from the repository's + * configuration. The contents of this snapshot will not change, + * even if the underlying config files are modified. + * + * The configuration file must be freed once it's no longer + * being used by the user. + * + * @param out Pointer to store the loaded configuration + * @param repo the repository + * @return 0, or an error code + */ +GIT_EXTERN(int) git_repository_config_snapshot(git_config **out, git_repository *repo); + +/** * Get the Object Database for this repository. * * If a custom ODB has not been set, the default @@ -464,21 +493,11 @@ * Use this function to get the contents of this file. Don't forget to * remove the file after you create the commit. * - * If the repository message exists and there are no errors reading it, this - * returns the bytes needed to store the message in memory (i.e. message - * file size plus one terminating NUL byte). That value is returned even if - * `out` is NULL or `len` is shorter than the necessary size. - * - * The `out` buffer will *always* be NUL terminated, even if truncation - * occurs. - * - * @param out Buffer to write data into or NULL to just read required size - * @param len Length of `out` buffer in bytes + * @param out git_buf to write data into * @param repo Repository to read prepared message from - * @return GIT_ENOTFOUND if no message exists, other value < 0 for other - * errors, or total bytes in message (may be > `len`) on success + * @return 0, GIT_ENOTFOUND if no message exists or an error code */ -GIT_EXTERN(int) git_repository_message(char *out, size_t len, git_repository *repo); +GIT_EXTERN(int) git_repository_message(git_buf *out, git_repository *repo); /** * Remove git's prepared message. @@ -488,13 +507,13 @@ GIT_EXTERN(int) git_repository_message_remove(git_repository *repo); /** - * Remove all the metadata associated with an ongoing git merge, including - * MERGE_HEAD, MERGE_MSG, etc. + * Remove all the metadata associated with an ongoing command like merge, + * revert, cherry-pick, etc. For example: MERGE_HEAD, MERGE_MSG, etc. * * @param repo A repository object * @return 0 on success, or error */ -GIT_EXTERN(int) git_repository_merge_cleanup(git_repository *repo); +GIT_EXTERN(int) git_repository_state_cleanup(git_repository *repo); typedef int (*git_repository_fetchhead_foreach_cb)(const char *ref_name, const char *remote_url, @@ -503,14 +522,18 @@ void *payload); /** - * Call callback 'callback' for each entry in the given FETCH_HEAD file. + * Invoke 'callback' for each entry in the given FETCH_HEAD file. + * + * Return a non-zero value from the callback to stop the loop. * * @param repo A repository object * @param callback Callback function * @param payload Pointer to callback data (optional) - * @return 0 on success, GIT_ENOTFOUND, GIT_EUSER or error + * @return 0 on success, non-zero callback return value, GIT_ENOTFOUND if + * there is no FETCH_HEAD file, or other error code. */ -GIT_EXTERN(int) git_repository_fetchhead_foreach(git_repository *repo, +GIT_EXTERN(int) git_repository_fetchhead_foreach( + git_repository *repo, git_repository_fetchhead_foreach_cb callback, void *payload); @@ -518,15 +541,19 @@ void *payload); /** - * If a merge is in progress, call callback 'cb' for each commit ID in the + * If a merge is in progress, invoke 'callback' for each commit ID in the * MERGE_HEAD file. * + * Return a non-zero value from the callback to stop the loop. + * * @param repo A repository object * @param callback Callback function * @param payload Pointer to callback data (optional) - * @return 0 on success, GIT_ENOTFOUND, GIT_EUSER or error + * @return 0 on success, non-zero callback return value, GIT_ENOTFOUND if + * there is no MERGE_HEAD file, or other error code. */ -GIT_EXTERN(int) git_repository_mergehead_foreach(git_repository *repo, +GIT_EXTERN(int) git_repository_mergehead_foreach( + git_repository *repo, git_repository_mergehead_foreach_cb callback, void *payload); @@ -538,6 +565,10 @@ * hash a file in the repository and you want to apply filtering rules (e.g. * crlf filters) before generating the SHA, then use this function. * + * Note: if the repository has `core.safecrlf` set to fail and the + * filtering triggers that failure, then this function will return an + * error and not calculate the hash of the file. + * * @param out Output value of calculated SHA * @param repo Repository pointer * @param path Path to file on disk whose contents should be hashed. If the @@ -547,6 +578,7 @@ * NULL, then the `path` parameter will be used instead. If * this is passed as the empty string, then no filters will be * applied when calculating the hash. + * @return 0 on success, or an error code */ GIT_EXTERN(int) git_repository_hashfile( git_oid *out, @@ -571,11 +603,15 @@ * * @param repo Repository pointer * @param refname Canonical name of the reference the HEAD should point at + * @param signature The identity that will used to populate the reflog entry + * @param log_message The one line long message to be appended to the reflog * @return 0 on success, or an error code */ GIT_EXTERN(int) git_repository_set_head( git_repository* repo, - const char* refname); + const char* refname, + const git_signature *signature, + const char *log_message); /** * Make the repository HEAD directly point to the Commit. @@ -591,11 +627,15 @@ * * @param repo Repository pointer * @param commitish Object id of the Commit the HEAD should point to + * @param signature The identity that will used to populate the reflog entry + * @param log_message The one line long message to be appended to the reflog * @return 0 on success, or an error code */ GIT_EXTERN(int) git_repository_set_head_detached( git_repository* repo, - const git_oid* commitish); + const git_oid* commitish, + const git_signature *signature, + const char *log_message); /** * Detach the HEAD. @@ -611,17 +651,27 @@ * Otherwise, the HEAD will be detached and point to the peeled Commit. * * @param repo Repository pointer + * @param signature The identity that will used to populate the reflog entry + * @param reflog_message The one line long message to be appended to the reflog * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing * branch or an error code */ GIT_EXTERN(int) git_repository_detach_head( - git_repository* repo); + git_repository* repo, + const git_signature *signature, + const char *reflog_message); +/** + * Repository state + * + * These values represent possible states for the repository to be in, + * based on the current operation which is ongoing. + */ typedef enum { GIT_REPOSITORY_STATE_NONE, GIT_REPOSITORY_STATE_MERGE, GIT_REPOSITORY_STATE_REVERT, - GIT_REPOSITORY_STATE_CHERRY_PICK, + GIT_REPOSITORY_STATE_CHERRYPICK, GIT_REPOSITORY_STATE_BISECT, GIT_REPOSITORY_STATE_REBASE, GIT_REPOSITORY_STATE_REBASE_INTERACTIVE, diff -Nru libgit2-0.20.0/include/git2/reset.h libgit2-0.22.2/include/git2/reset.h --- libgit2-0.20.0/include/git2/reset.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/reset.h 2015-03-24 16:10:45.000000000 +0000 @@ -7,6 +7,10 @@ #ifndef INCLUDE_git_reset_h__ #define INCLUDE_git_reset_h__ +#include "common.h" +#include "types.h" +#include "strarray.h" + /** * @file git2/reset.h * @brief Git reset management routines @@ -19,9 +23,9 @@ * Kinds of reset operation */ typedef enum { - GIT_RESET_SOFT = 1, /** Move the head to the given commit */ - GIT_RESET_MIXED = 2, /** SOFT plus reset index to the commit */ - GIT_RESET_HARD = 3, /** MIXED plus changes in working tree discarded */ + GIT_RESET_SOFT = 1, /**< Move the head to the given commit */ + GIT_RESET_MIXED = 2, /**< SOFT plus reset index to the commit */ + GIT_RESET_HARD = 3, /**< MIXED plus changes in working tree discarded */ } git_reset_t; /** @@ -48,10 +52,26 @@ * * @param reset_type Kind of reset operation to perform. * + * @param checkout_opts Checkout options to be used for a HARD reset. + * The checkout_strategy field will be overridden (based on reset_type). + * This parameter can be used to propagate notify and progress callbacks. + * + * @param signature The identity that will used to populate the reflog entry + * + * @param log_message The one line long message to be appended to the reflog. + * The reflog is only updated if the affected direct reference is actually + * changing. If NULL, the default is "reset: moving"; if you want something more + * useful, provide a message. + * * @return 0 on success or an error code */ GIT_EXTERN(int) git_reset( - git_repository *repo, git_object *target, git_reset_t reset_type); + git_repository *repo, + git_object *target, + git_reset_t reset_type, + git_checkout_options *checkout_opts, + const git_signature *signature, + const char *log_message); /** * Updates some entries in the index from the target commit tree. diff -Nru libgit2-0.20.0/include/git2/revert.h libgit2-0.22.2/include/git2/revert.h --- libgit2-0.20.0/include/git2/revert.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/include/git2/revert.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,89 @@ +/* + * 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_git_revert_h__ +#define INCLUDE_git_revert_h__ + +#include "common.h" +#include "types.h" +#include "merge.h" + +/** + * @file git2/revert.h + * @brief Git revert routines + * @defgroup git_revert Git revert routines + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Options for revert + */ +typedef struct { + unsigned int version; + + /** For merge commits, the "mainline" is treated as the parent. */ + unsigned int mainline; + + git_merge_options merge_opts; /*< Options for the merging */ + git_checkout_options checkout_opts; /*< Options for the checkout */ +} git_revert_options; + +#define GIT_REVERT_OPTIONS_VERSION 1 +#define GIT_REVERT_OPTIONS_INIT {GIT_REVERT_OPTIONS_VERSION, 0, GIT_MERGE_OPTIONS_INIT, GIT_CHECKOUT_OPTIONS_INIT} + +/** + * Initializes a `git_revert_options` with default values. Equivalent to + * creating an instance with GIT_REVERT_OPTIONS_INIT. + * + * @param opts the `git_revert_options` struct to initialize + * @param version Version of struct; pass `GIT_REVERT_OPTIONS_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_revert_init_options( + git_revert_options *opts, + unsigned int version); + +/** + * Reverts the given commit against the given "our" commit, producing an + * index that reflects the result of the revert. + * + * The returned index must be freed explicitly with `git_index_free`. + * + * @param out pointer to store the index result in + * @param repo the repository that contains the given commits + * @param revert_commit the commit to revert + * @param our_commit the commit to revert against (eg, HEAD) + * @param mainline the parent of the revert commit, if it is a merge + * @param merge_options the merge options (or null for defaults) + * @return zero on success, -1 on failure. + */ +GIT_EXTERN(int) git_revert_commit( + git_index **out, + git_repository *repo, + git_commit *revert_commit, + git_commit *our_commit, + unsigned int mainline, + const git_merge_options *merge_options); + +/** + * Reverts the given commit, producing changes in the index and working directory. + * + * @param repo the repository to revert + * @param commit the commit to revert + * @param given_opts merge flags + * @return zero on success, -1 on failure. + */ +GIT_EXTERN(int) git_revert( + git_repository *repo, + git_commit *commit, + const git_revert_options *given_opts); + +/** @} */ +GIT_END_DECL +#endif + diff -Nru libgit2-0.20.0/include/git2/revwalk.h libgit2-0.22.2/include/git2/revwalk.h --- libgit2-0.20.0/include/git2/revwalk.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/revwalk.h 2015-03-24 16:10:45.000000000 +0000 @@ -21,33 +21,38 @@ GIT_BEGIN_DECL /** - * Sort the repository contents in no particular ordering; - * this sorting is arbitrary, implementation-specific - * and subject to change at any time. - * This is the default sorting for new walkers. + * Flags to specify the sorting which a revwalk should perform. */ -#define GIT_SORT_NONE (0) - -/** - * Sort the repository contents in topological order - * (parents before children); this sorting mode - * can be combined with time sorting. - */ -#define GIT_SORT_TOPOLOGICAL (1 << 0) - -/** - * Sort the repository contents by commit time; - * this sorting mode can be combined with - * topological sorting. - */ -#define GIT_SORT_TIME (1 << 1) - -/** - * Iterate through the repository contents in reverse - * order; this sorting mode can be combined with - * any of the above. - */ -#define GIT_SORT_REVERSE (1 << 2) +typedef enum { + /** + * Sort the repository contents in no particular ordering; + * this sorting is arbitrary, implementation-specific + * and subject to change at any time. + * This is the default sorting for new walkers. + */ + GIT_SORT_NONE = 0, + + /** + * Sort the repository contents in topological order + * (parents before children); this sorting mode + * can be combined with time sorting. + */ + GIT_SORT_TOPOLOGICAL = 1 << 0, + + /** + * Sort the repository contents by commit time; + * this sorting mode can be combined with + * topological sorting. + */ + GIT_SORT_TIME = 1 << 1, + + /** + * Iterate through the repository contents in reverse + * order; this sorting mode can be combined with + * any of the above. + */ + GIT_SORT_REVERSE = 1 << 2, +} git_sort_t; /** * Allocate a new revision walker to iterate through a repo. @@ -87,7 +92,7 @@ /** * Mark a commit to start traversal from. * - * The given OID must belong to a commit on the walked + * The given OID must belong to a committish on the walked * repository. * * The given commit will be used as one of the roots @@ -108,7 +113,10 @@ * pattern will be pushed to the revision walker. * * A leading 'refs/' is implied if not present as well as a trailing - * '/ *' if the glob lacks '?', '*' or '['. + * '/\*' if the glob lacks '?', '\*' or '['. + * + * Any references matching this glob which do not point to a + * committish will be ignored. * * @param walk the walker being used for the traversal * @param glob the glob pattern references should match @@ -127,7 +135,7 @@ /** * Mark a commit (and its ancestors) uninteresting for the output. * - * The given OID must belong to a commit on the walked + * The given OID must belong to a committish on the walked * repository. * * The resolved commit and all its parents will be hidden from the @@ -147,7 +155,10 @@ * revision walk. * * A leading 'refs/' is implied if not present as well as a trailing - * '/ *' if the glob lacks '?', '*' or '['. + * '/\*' if the glob lacks '?', '\*' or '['. + * + * Any references matching this glob which do not point to a + * committish will be ignored. * * @param walk the walker being used for the traversal * @param glob the glob pattern references should match @@ -166,7 +177,7 @@ /** * Push the OID pointed to by a reference * - * The reference must point to a commit. + * The reference must point to a committish. * * @param walk the walker being used for the traversal * @param refname the reference to push @@ -177,7 +188,7 @@ /** * Hide the OID pointed to by a reference * - * The reference must point to a commit. + * The reference must point to a committish. * * @param walk the walker being used for the traversal * @param refname the reference to hide @@ -255,6 +266,30 @@ */ GIT_EXTERN(git_repository *) git_revwalk_repository(git_revwalk *walk); +/** + * This is a callback function that user can provide to hide a + * commit and its parents. If the callback function returns non-zero value, + * then this commit and its parents will be hidden. + * + * @param commit_id oid of Commit + * @param payload User-specified pointer to data to be passed as data payload + */ +typedef int(*git_revwalk_hide_cb)( + const git_oid *commit_id, + void *payload); + +/** + * Adds a callback function to hide a commit and its parents + * + * @param walk the revision walker + * @param hide_cb callback function to hide a commit and its parents + * @param payload data payload to be passed to callback function + */ +GIT_EXTERN(int) git_revwalk_add_hide_cb( + git_revwalk *walk, + git_revwalk_hide_cb hide_cb, + void *payload); + /** @} */ GIT_END_DECL #endif diff -Nru libgit2-0.20.0/include/git2/signature.h libgit2-0.22.2/include/git2/signature.h --- libgit2-0.20.0/include/git2/signature.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/signature.h 2015-03-24 16:10:45.000000000 +0000 @@ -68,10 +68,11 @@ * * Call `git_signature_free()` to free the data. * - * @param sig signature to duplicated - * @return a copy of sig, NULL on out of memory + * @param dest pointer where to store the copy + * @param sig signature to duplicate + * @return 0 or an error code */ -GIT_EXTERN(git_signature *) git_signature_dup(const git_signature *sig); +GIT_EXTERN(int) git_signature_dup(git_signature **dest, const git_signature *sig); /** * Free an existing signature. diff -Nru libgit2-0.20.0/include/git2/stash.h libgit2-0.22.2/include/git2/stash.h --- libgit2-0.20.0/include/git2/stash.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/stash.h 2015-03-24 16:10:45.000000000 +0000 @@ -18,21 +18,30 @@ */ GIT_BEGIN_DECL +/** + * Stash flags + */ typedef enum { + /** + * No option, default + */ GIT_STASH_DEFAULT = 0, - /* All changes already added to the index - * are left intact in the working directory + /** + * All changes already added to the index are left intact in + * the working directory */ GIT_STASH_KEEP_INDEX = (1 << 0), - /* All untracked files are also stashed and then - * cleaned up from the working directory + /** + * All untracked files are also stashed and then cleaned up + * from the working directory */ GIT_STASH_INCLUDE_UNTRACKED = (1 << 1), - /* All ignored files are also stashed and then - * cleaned up from the working directory + /** + * All ignored files are also stashed and then cleaned up from + * the working directory */ GIT_STASH_INCLUDE_IGNORED = (1 << 2), } git_stash_flags; @@ -62,19 +71,15 @@ unsigned int flags); /** - * When iterating over all the stashed states, callback that will be - * issued per entry. + * This is a callback function you can provide to iterate over all the + * stashed states that will be invoked per entry. * * @param index The position within the stash list. 0 points to the - * most recent stashed state. - * + * most recent stashed state. * @param message The stash message. - * * @param stash_id The commit oid of the stashed state. - * * @param payload Extra parameter to callback function. - * - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 to continue iterating or non-zero to stop */ typedef int (*git_stash_cb)( size_t index, @@ -89,12 +94,12 @@ * * @param repo Repository where to find the stash. * - * @param callback Callback to invoke per found stashed state. The most recent - * stash state will be enumerated first. + * @param callback Callback to invoke per found stashed state. The most + * recent stash state will be enumerated first. * * @param payload Extra parameter to callback function. * - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_stash_foreach( git_repository *repo, diff -Nru libgit2-0.20.0/include/git2/status.h libgit2-0.22.2/include/git2/status.h --- libgit2-0.20.0/include/git2/status.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/status.h 2015-03-24 16:10:45.000000000 +0000 @@ -43,6 +43,7 @@ GIT_STATUS_WT_DELETED = (1u << 9), GIT_STATUS_WT_TYPECHANGE = (1u << 10), GIT_STATUS_WT_RENAMED = (1u << 11), + GIT_STATUS_WT_UNREADABLE = (1u << 12), GIT_STATUS_IGNORED = (1u << 14), } git_status_t; @@ -121,6 +122,11 @@ * - GIT_STATUS_OPT_NO_REFRESH bypasses the default status behavior of * doing a "soft" index reload (i.e. reloading the index data if the * file on disk has been modified outside libgit2). + * - GIT_STATUS_OPT_UPDATE_INDEX tells libgit2 to refresh the stat cache + * in the index for files that are unchanged but have out of date stat + * information in the index. It will result in less work being done on + * subsequent calls to get status. This is mutually exclusive with the + * NO_REFRESH option. * * Calling `git_status_foreach()` is like calling the extended version * with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED, @@ -128,19 +134,22 @@ * together as `GIT_STATUS_OPT_DEFAULTS` if you want them as a baseline. */ typedef enum { - GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0), - GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1), - GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2), - GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3), - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4), - GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5), - GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6), - GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1u << 7), - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8), - GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1u << 9), - GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10), - GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11), - GIT_STATUS_OPT_NO_REFRESH = (1u << 12), + GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0), + GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1), + GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2), + GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3), + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4), + GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5), + GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6), + GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1u << 7), + GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8), + GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1u << 9), + GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10), + GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11), + GIT_STATUS_OPT_NO_REFRESH = (1u << 12), + GIT_STATUS_OPT_UPDATE_INDEX = (1u << 13), + GIT_STATUS_OPT_INCLUDE_UNREADABLE = (1u << 14), + GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED = (1u << 15), } git_status_opt_t; #define GIT_STATUS_OPT_DEFAULTS \ @@ -175,6 +184,18 @@ #define GIT_STATUS_OPTIONS_INIT {GIT_STATUS_OPTIONS_VERSION} /** + * Initializes a `git_status_options` with default values. Equivalent to + * creating an instance with GIT_STATUS_OPTIONS_INIT. + * + * @param opts The `git_status_options` instance to initialize. + * @param version Version of struct; pass `GIT_STATUS_OPTIONS_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_status_init_options( + git_status_options *opts, + unsigned int version); + +/** * A status entry, providing the differences between the file as it exists * in HEAD and the index, and providing the differences between the index * and the working directory. @@ -203,12 +224,12 @@ * into this function. * * If the callback returns a non-zero value, this function will stop looping - * and return GIT_EUSER. + * and return that value to caller. * * @param repo A repository object * @param callback The function to call on each file * @param payload Pointer to pass through to callback function - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_status_foreach( git_repository *repo, @@ -223,11 +244,16 @@ * in what order. See the `git_status_options` structure for details * about the additional controls that this makes available. * + * Note that if a `pathspec` is given in the `git_status_options` to filter + * the status, then the results from rename detection (if you enable it) may + * not be accurate. To do rename detection properly, this must be called + * with no `pathspec` so that all files can be considered. + * * @param repo Repository object * @param opts Status options structure * @param callback The function to call on each file * @param payload Pointer to pass through to callback function - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, non-zero callback return value, or error code */ GIT_EXTERN(int) git_status_foreach_ext( git_repository *repo, @@ -238,12 +264,25 @@ /** * Get file status for a single file. * - * This is not quite the same as calling `git_status_foreach_ext()` with - * the pathspec set to the specified path. + * This tries to get status for the filename that you give. If no files + * match that name (in either the HEAD, index, or working directory), this + * returns GIT_ENOTFOUND. + * + * If the name matches multiple files (for example, if the `path` names a + * directory or if running on a case- insensitive filesystem and yet the + * HEAD has two entries that both match the path), then this returns + * GIT_EAMBIGUOUS because it cannot give correct results. + * + * This does not do any sort of rename detection. Renames require a set of + * targets and because of the path filtering, there is not enough + * information to check renames correctly. To check file status with rename + * detection, there is no choice but to do a full `git_status_list_new` and + * scan through looking for the path that you are interested in. * * @param status_flags Output combination of git_status_t values for file * @param repo A repository object - * @param path The file to retrieve status for relative to the repo workdir + * @param path The exact path to retrieve status for relative to the + * repository working directory * @return 0 on success, GIT_ENOTFOUND if the file is not found in the HEAD, * index, and work tree, GIT_EAMBIGUOUS if `path` matches multiple files * or if it refers to a folder, and -1 on other errors. @@ -256,6 +295,11 @@ /** * Gather file status information and populate the `git_status_list`. * + * Note that if a `pathspec` is given in the `git_status_options` to filter + * the status, then the results from rename detection (if you enable it) may + * not be accurate. To do rename detection properly, this must be called + * with no `pathspec` so that all files can be considered. + * * @param out Pointer to store the status results in * @param repo Repository object * @param opts Status options structure @@ -269,6 +313,9 @@ /** * Gets the count of status entries in this list. * + * If there are no changes in status (at least according the options given + * when the status list was created), this can return 0. + * * @param statuslist Existing status list object * @return the number of status entries */ diff -Nru libgit2-0.20.0/include/git2/submodule.h libgit2-0.22.2/include/git2/submodule.h --- libgit2-0.20.0/include/git2/submodule.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/submodule.h 2015-03-24 16:10:45.000000000 +0000 @@ -10,6 +10,8 @@ #include "common.h" #include "types.h" #include "oid.h" +#include "remote.h" +#include "checkout.h" /** * @file git2/submodule.h @@ -97,7 +99,8 @@ (((S) & GIT_SUBMODULE_STATUS__INDEX_FLAGS) == 0) #define GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(S) \ - (((S) & GIT_SUBMODULE_STATUS__WD_FLAGS) == 0) + (((S) & (GIT_SUBMODULE_STATUS__WD_FLAGS & \ + ~GIT_SUBMODULE_STATUS_WD_UNINITIALIZED)) == 0) #define GIT_SUBMODULE_STATUS_IS_WD_DIRTY(S) \ (((S) & (GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED | \ @@ -105,6 +108,83 @@ GIT_SUBMODULE_STATUS_WD_UNTRACKED)) != 0) /** + * Submodule update options structure + * + * Use the GIT_SUBMODULE_UPDATE_OPTIONS_INIT to get the default settings, + * like this: + * + * git_submodule_update_options opts = GIT_SUBMODULE_UPDATE_OPTIONS_INIT; + */ +typedef struct git_submodule_update_options { + unsigned int version; + + /** + * These options are passed to the checkout step. To disable + * checkout, set the `checkout_strategy` to + * `GIT_CHECKOUT_NONE`. Generally you will want the use + * GIT_CHECKOUT_SAFE to update files in the working + * directory. Use the `clone_checkout_strategy` field + * to set the checkout strategy that will be used in + * the case where update needs to clone the repository. + */ + git_checkout_options checkout_opts; + + /** + * Callbacks to use for reporting fetch progress, and for acquiring + * credentials in the event they are needed. + */ + git_remote_callbacks remote_callbacks; + + /** + * The checkout strategy to use when the sub repository needs to + * be cloned. Use GIT_CHECKOUT_SAFE_CREATE to create all files + * in the working directory for the newly cloned repository. + */ + unsigned int clone_checkout_strategy; + + /** + * The identity used when updating the reflog. NULL means to + * use the default signature using the config. + */ + git_signature *signature; +} git_submodule_update_options; + +#define GIT_SUBMODULE_UPDATE_OPTIONS_VERSION 1 +#define GIT_SUBMODULE_UPDATE_OPTIONS_INIT \ + { GIT_CHECKOUT_OPTIONS_VERSION, \ + { GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE}, \ + GIT_REMOTE_CALLBACKS_INIT, GIT_CHECKOUT_SAFE_CREATE } + +/** + * Initializes a `git_submodule_update_options` with default values. + * Equivalent to creating an instance with GIT_SUBMODULE_UPDATE_OPTIONS_INIT. + * + * @param opts The `git_submodule_update_options` instance to initialize. + * @param version Version of struct; pass `GIT_SUBMODULE_UPDATE_OPTIONS_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_submodule_update_init_options( + git_submodule_update_options *opts, unsigned int version); + +/** + * Update a submodule. This will clone a missing submodule and + * checkout the subrepository to the commit specified in the index of + * containing repository. + * + * @param submodule Submodule object + * @param init If the submodule is not initialized, setting this flag to true + * will initialize the submodule before updating. Otherwise, this will + * return an error if attempting to update an uninitialzed repository. + * but setting this to true forces them to be updated. + * @param options configuration options for the update. If NULL, the + * function works as though GIT_SUBMODULE_UPDATE_OPTIONS_INIT was passed. + * @return 0 on success, any non-zero return value from a callback + * function, or a negative value to indicate an error (use + * `giterr_last` for a detailed error message). + */ +GIT_EXTERN(int) git_submodule_update(git_submodule *submodule, int init, git_submodule_update_options *options); + +/** * Lookup submodule information by name or path. * * Given either the submodule name or path (they are usually the same), this @@ -114,34 +194,40 @@ * * - The submodule is not mentioned in the HEAD, the index, and the config, * but does "exist" in the working directory (i.e. there is a subdirectory - * that is a valid self-contained git repo). In this case, this function - * returns GIT_EEXISTS to indicate the the submodule exists but not in a + * that appears to be a Git repository). In this case, this function + * returns GIT_EEXISTS to indicate a sub-repository exists but not in a * state where a git_submodule can be instantiated. * - The submodule is not mentioned in the HEAD, index, or config and the * working directory doesn't contain a value git repo at that path. * There may or may not be anything else at that path, but nothing that * looks like a submodule. In this case, this returns GIT_ENOTFOUND. * - * The submodule object is owned by the containing repo and will be freed - * when the repo is freed. The caller need not free the submodule. + * You must call `git_submodule_free` when done with the submodule. * - * @param submodule Pointer to submodule description object pointer.. - * @param repo The repository. - * @param name The name of the submodule. Trailing slashes will be ignored. + * @param out Output ptr to submodule; pass NULL to just get return code + * @param repo The parent repository + * @param name The name of or path to the submodule; trailing slashes okay * @return 0 on success, GIT_ENOTFOUND if submodule does not exist, - * GIT_EEXISTS if submodule exists in working directory only, -1 on - * other errors. + * GIT_EEXISTS if a repository is found in working directory only, + * -1 on other errors. */ GIT_EXTERN(int) git_submodule_lookup( - git_submodule **submodule, + git_submodule **out, git_repository *repo, const char *name); /** + * Release a submodule + * + * @param submodule Submodule object + */ +GIT_EXTERN(void) git_submodule_free(git_submodule *submodule); + +/** * Iterate over all tracked submodules of a repository. * * See the note on `git_submodule` above. This iterates over the tracked - * submodules as decribed therein. + * submodules as described therein. * * If you are concerned about items in the working directory that look like * submodules but are not tracked, the diff API will generate a diff record @@ -174,9 +260,11 @@ * `git_submodule_add_finalize()` to wrap up adding the new submodule and * .gitmodules to the index to be ready to commit. * - * @param submodule The newly created submodule ready to open for clone - * @param repo Superproject repository to contain the new submodule - * @param url URL for the submodules remote + * You must call `git_submodule_free` on the submodule object when done. + * + * @param out The newly created submodule ready to open for clone + * @param repo The repository in which you want to create the submodule + * @param url URL for the submodule's remote * @param path Path at which the submodule should be created * @param use_gitlink Should workdir contain a gitlink to the repo in * .git/modules vs. repo directly in workdir. @@ -184,7 +272,7 @@ * -1 on other errors. */ GIT_EXTERN(int) git_submodule_add_setup( - git_submodule **submodule, + git_submodule **out, git_repository *repo, const char *url, const char *path, @@ -271,6 +359,24 @@ GIT_EXTERN(const char *) git_submodule_url(git_submodule *submodule); /** + * Resolve a submodule url relative to the given repository. + * + * @param out buffer to store the absolute submodule url in + * @param repo Pointer to repository object + * @param url Relative url + * @return 0 or an error code + */ +GIT_EXTERN(int) git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *url); + +/** +* Get the branch for the submodule. +* +* @param submodule Pointer to submodule object +* @return Pointer to the submodule branch +*/ +GIT_EXTERN(const char *) git_submodule_branch(git_submodule *submodule); + +/** * Set the URL for the submodule. * * This sets the URL in memory for the submodule. This will be used for @@ -376,7 +482,7 @@ * @return The current git_submodule_update_t value that will be used * for this submodule. */ -GIT_EXTERN(git_submodule_update_t) git_submodule_update( +GIT_EXTERN(git_submodule_update_t) git_submodule_update_strategy( git_submodule *submodule); /** @@ -410,7 +516,7 @@ * * @return 0 if fetchRecurseSubmodules is false, 1 if true */ -GIT_EXTERN(int) git_submodule_fetch_recurse_submodules( +GIT_EXTERN(git_submodule_recurse_t) git_submodule_fetch_recurse_submodules( git_submodule *submodule); /** @@ -424,9 +530,9 @@ * @param fetch_recurse_submodules Boolean value * @return old value for fetchRecurseSubmodules */ -GIT_EXTERN(int) git_submodule_set_fetch_recurse_submodules( +GIT_EXTERN(git_submodule_recurse_t) git_submodule_set_fetch_recurse_submodules( git_submodule *submodule, - int fetch_recurse_submodules); + git_submodule_recurse_t fetch_recurse_submodules); /** * Copy submodule info into ".git/config" file. @@ -444,6 +550,24 @@ GIT_EXTERN(int) git_submodule_init(git_submodule *submodule, int overwrite); /** + * Set up the subrepository for a submodule in preparation for clone. + * + * This function can be called to init and set up a submodule + * repository from a submodule in preparation to clone it from + * its remote. + * + * @param out Output pointer to the created git repository. + * @param sm The submodule to create a new subrepository from. + * @param use_gitlink Should the workdir contain a gitlink to + * the repo in .git/modules vs. repo directly in workdir. + * @return 0 on success, <0 on failure. + */ +GIT_EXTERN(int) git_submodule_repo_init( + git_repository **out, + const git_submodule *sm, + int use_gitlink); + +/** * Copy submodule remote info into submodule repo. * * This copies the information about the submodules URL into the checked out @@ -474,15 +598,23 @@ * * Call this to reread cached submodule information for this submodule if * you have reason to believe that it has changed. + * + * @param submodule The submodule to reload + * @param force Force reload even if the data doesn't seem out of date + * @return 0 on success, <0 on error */ -GIT_EXTERN(int) git_submodule_reload(git_submodule *submodule); +GIT_EXTERN(int) git_submodule_reload(git_submodule *submodule, int force); /** * Reread all submodule info. * * Call this to reload all cached submodule information for the repo. + * + * @param repo The repository to reload submodule data for + * @param force Force full reload even if the data doesn't seem out of date + * @return 0 on success, <0 on error */ -GIT_EXTERN(int) git_submodule_reload_all(git_repository *repo); +GIT_EXTERN(int) git_submodule_reload_all(git_repository *repo, int force); /** * Get the status for a submodule. diff -Nru libgit2-0.20.0/include/git2/sys/commit.h libgit2-0.22.2/include/git2/sys/commit.h --- libgit2-0.20.0/include/git2/sys/commit.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/sys/commit.h 2015-03-24 16:10:45.000000000 +0000 @@ -21,16 +21,18 @@ GIT_BEGIN_DECL /** - * Create new commit in the repository from a list of `git_oid` values + * Create new commit in the repository from a list of `git_oid` values. * * See documentation for `git_commit_create()` for information about the * parameters, as the meaning is identical excepting that `tree` and * `parents` now take `git_oid`. This is a dangerous API in that nor * the `tree`, neither the `parents` list of `git_oid`s are checked for * validity. + * + * @see git_commit_create */ -GIT_EXTERN(int) git_commit_create_from_oids( - git_oid *oid, +GIT_EXTERN(int) git_commit_create_from_ids( + git_oid *id, git_repository *repo, const char *update_ref, const git_signature *author, @@ -38,9 +40,41 @@ const char *message_encoding, const char *message, const git_oid *tree, - int parent_count, + size_t parent_count, const git_oid *parents[]); +/** + * Callback function to return parents for commit. + * + * This is invoked with the count of the number of parents processed so far + * along with the user supplied payload. This should return a git_oid of + * the next parent or NULL if all parents have been provided. + */ +typedef const git_oid *(*git_commit_parent_callback)(size_t idx, void *payload); + +/** + * Create a new commit in the repository with an callback to supply parents. + * + * See documentation for `git_commit_create()` for information about the + * parameters, as the meaning is identical excepting that `tree` takes a + * `git_oid` and doesn't check for validity, and `parent_cb` is invoked + * with `parent_payload` and should return `git_oid` values or NULL to + * indicate that all parents are accounted for. + * + * @see git_commit_create + */ +GIT_EXTERN(int) git_commit_create_from_callback( + git_oid *id, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_oid *tree, + git_commit_parent_callback parent_cb, + void *parent_payload); + /** @} */ GIT_END_DECL #endif diff -Nru libgit2-0.20.0/include/git2/sys/config.h libgit2-0.22.2/include/git2/sys/config.h --- libgit2-0.20.0/include/git2/sys/config.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/sys/config.h 2015-03-24 16:10:45.000000000 +0000 @@ -57,19 +57,32 @@ /* Open means open the file/database and parse if necessary */ int (*open)(struct git_config_backend *, git_config_level_t level); - int (*get)(const struct git_config_backend *, const char *key, const git_config_entry **entry); + int (*get)(struct git_config_backend *, const char *key, const 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); int (*del)(struct git_config_backend *, const char *key); int (*del_multivar)(struct git_config_backend *, const char *key, const char *regexp); int (*iterator)(git_config_iterator **, struct git_config_backend *); - int (*refresh)(struct git_config_backend *); + /** Produce a read-only version of this backend */ + int (*snapshot)(struct git_config_backend **, struct git_config_backend *); void (*free)(struct git_config_backend *); }; #define GIT_CONFIG_BACKEND_VERSION 1 #define GIT_CONFIG_BACKEND_INIT {GIT_CONFIG_BACKEND_VERSION} /** + * Initializes a `git_config_backend` with default values. Equivalent to + * creating an instance with GIT_CONFIG_BACKEND_INIT. + * + * @param opts the `git_config_backend` struct to initialize. + * @param version Version of struct; pass `GIT_CONFIG_BACKEND_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_config_init_backend( + git_config_backend *backend, + unsigned int version); + +/** * Add a generic config file instance to an existing config * * Note that the configuration object will free the file diff -Nru libgit2-0.20.0/include/git2/sys/diff.h libgit2-0.22.2/include/git2/sys/diff.h --- libgit2-0.20.0/include/git2/sys/diff.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/include/git2/sys/diff.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,94 @@ +/* + * 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_diff_h__ +#define INCLUDE_sys_git_diff_h__ + +#include "git2/common.h" +#include "git2/types.h" +#include "git2/oid.h" +#include "git2/diff.h" +#include "git2/status.h" + +/** + * @file git2/sys/diff.h + * @brief Low-level Git diff utilities + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Diff print callback that writes to a git_buf. + * + * This function is provided not for you to call it directly, but instead + * so you can use it as a function pointer to the `git_diff_print` or + * `git_patch_print` APIs. When using those APIs, you specify a callback + * to actually handle the diff and/or patch data. + * + * Use this callback to easily write that data to a `git_buf` buffer. You + * must pass a `git_buf *` value as the payload to the `git_diff_print` + * and/or `git_patch_print` function. The data will be appended to the + * buffer (after any existing content). + */ +GIT_EXTERN(int) git_diff_print_callback__to_buf( + const git_diff_delta *delta, + const git_diff_hunk *hunk, + const git_diff_line *line, + void *payload); /*< payload must be a `git_buf *` */ + +/** + * Diff print callback that writes to stdio FILE handle. + * + * This function is provided not for you to call it directly, but instead + * so you can use it as a function pointer to the `git_diff_print` or + * `git_patch_print` APIs. When using those APIs, you specify a callback + * to actually handle the diff and/or patch data. + * + * Use this callback to easily write that data to a stdio FILE handle. You + * must pass a `FILE *` value (such as `stdout` or `stderr` or the return + * value from `fopen()`) as the payload to the `git_diff_print` + * and/or `git_patch_print` function. If you pass NULL, this will write + * data to `stdout`. + */ +GIT_EXTERN(int) git_diff_print_callback__to_file_handle( + const git_diff_delta *delta, + const git_diff_hunk *hunk, + const git_diff_line *line, + void *payload); /*< payload must be a `FILE *` */ + + +/** + * Performance data from diffing + */ +typedef struct { + unsigned int version; + size_t stat_calls; /*< Number of stat() calls performed */ + size_t oid_calculations; /*< Number of ID calculations */ +} git_diff_perfdata; + +#define GIT_DIFF_PERFDATA_VERSION 1 +#define GIT_DIFF_PERFDATA_INIT {GIT_DIFF_PERFDATA_VERSION,0,0} + +/** + * Get performance data for a diff object. + * + * @param out Structure to be filled with diff performance data + * @param diff Diff to read performance data from + * @return 0 for success, <0 for error + */ +GIT_EXTERN(int) git_diff_get_perfdata( + git_diff_perfdata *out, const git_diff *diff); + +/** + * Get performance data for diffs from a git_status_list + */ +GIT_EXTERN(int) git_status_list_get_perfdata( + git_diff_perfdata *out, const git_status_list *status); + +/** @} */ +GIT_END_DECL +#endif diff -Nru libgit2-0.20.0/include/git2/sys/filter.h libgit2-0.22.2/include/git2/sys/filter.h --- libgit2-0.20.0/include/git2/sys/filter.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/sys/filter.h 2015-03-24 16:10:45.000000000 +0000 @@ -55,7 +55,10 @@ * your own chains of filters. */ GIT_EXTERN(int) git_filter_list_new( - git_filter_list **out, git_repository *repo, git_filter_mode_t mode); + git_filter_list **out, + git_repository *repo, + git_filter_mode_t mode, + uint32_t options); /** * Add a filter to a filter list with the given payload. @@ -115,10 +118,15 @@ GIT_EXTERN(const git_oid *) git_filter_source_id(const git_filter_source *src); /** - * Get the git_filter_mode_t to be applied + * Get the git_filter_mode_t to be used */ GIT_EXTERN(git_filter_mode_t) git_filter_source_mode(const git_filter_source *src); +/** + * Get the combination git_filter_opt_t options to be applied + */ +GIT_EXTERN(uint32_t) git_filter_source_options(const git_filter_source *src); + /* * struct git_filter * @@ -149,6 +157,7 @@ * Specified as `filter.shutdown`, this is an optional callback invoked * when the filter is unregistered or when libgit2 is shutting down. It * will be called once at most and should release resources as needed. + * This may be called even if the `initialize` callback was not made. * * Typically this function will free the `git_filter` object itself. */ diff -Nru libgit2-0.20.0/include/git2/sys/hashsig.h libgit2-0.22.2/include/git2/sys/hashsig.h --- libgit2-0.20.0/include/git2/sys/hashsig.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/include/git2/sys/hashsig.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,79 @@ +/* + * 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_hashsig_h__ +#define INCLUDE_sys_hashsig_h__ + +#include "git2/common.h" + +GIT_BEGIN_DECL + +/** + * Similarity signature of line hashes for a buffer + */ +typedef struct git_hashsig git_hashsig; + +/** + * Options for hashsig calculation + */ +typedef enum { + GIT_HASHSIG_NORMAL = 0, /* use all data */ + GIT_HASHSIG_IGNORE_WHITESPACE = 1, /* ignore whitespace */ + GIT_HASHSIG_SMART_WHITESPACE = 2, /* ignore \r and all space after \n */ +} git_hashsig_option_t; + +/** + * Build a similarity signature for a buffer + * + * If you have passed a whitespace-ignoring buffer, then the whitespace + * will be removed from the buffer while it is being processed, modifying + * the buffer in place. Sorry about that! + * + * This will return an error if the buffer doesn't contain enough data to + * compute a valid signature. + * + * @param out The array of hashed runs representing the file content + * @param buf The contents of the file to hash + * @param buflen The length of the data at `buf` + * @param generate_pairwise_hashes Should pairwise runs be hashed + */ +GIT_EXTERN(int) git_hashsig_create( + git_hashsig **out, + const char *buf, + size_t buflen, + git_hashsig_option_t opts); + +/** + * Build a similarity signature from a file + * + * This walks through the file, only loading a maximum of 4K of file data at + * a time. Otherwise, it acts just like `git_hashsig_create`. + * + * This will return an error if the file doesn't contain enough data to + * compute a valid signature. + */ +GIT_EXTERN(int) git_hashsig_create_fromfile( + git_hashsig **out, + const char *path, + git_hashsig_option_t opts); + +/** + * Release memory for a content similarity signature + */ +GIT_EXTERN(void) git_hashsig_free(git_hashsig *sig); + +/** + * Measure similarity between two files + * + * @return <0 for error, [0 to 100] as similarity score + */ +GIT_EXTERN(int) git_hashsig_compare( + const git_hashsig *a, + const git_hashsig *b); + +GIT_END_DECL + +#endif diff -Nru libgit2-0.20.0/include/git2/sys/index.h libgit2-0.22.2/include/git2/sys/index.h --- libgit2-0.20.0/include/git2/sys/index.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/sys/index.h 2015-03-24 16:10:45.000000000 +0000 @@ -42,7 +42,7 @@ * @param index an existing index object * @return integer of count of current filename conflict entries */ -GIT_EXTERN(unsigned int) git_index_name_entrycount(git_index *index); +GIT_EXTERN(size_t) git_index_name_entrycount(git_index *index); /** * Get a filename conflict entry from the index. @@ -90,7 +90,7 @@ * @param index an existing index object * @return integer of count of current resolve undo entries */ -GIT_EXTERN(unsigned int) git_index_reuc_entrycount(git_index *index); +GIT_EXTERN(size_t) git_index_reuc_entrycount(git_index *index); /** * Finds the resolve undo entry that points to the given path in the Git diff -Nru libgit2-0.20.0/include/git2/sys/mempack.h libgit2-0.22.2/include/git2/sys/mempack.h --- libgit2-0.20.0/include/git2/sys/mempack.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/include/git2/sys/mempack.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,85 @@ +/* + * 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_odb_mempack_h__ +#define INCLUDE_sys_git_odb_mempack_h__ + +#include "git2/common.h" +#include "git2/types.h" +#include "git2/oid.h" +#include "git2/odb.h" + +/** + * @file git2/sys/mempack.h + * @brief Custom ODB backend that permits packing objects in-memory + * @defgroup git_backend Git custom backend APIs + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** + * Instantiate a new mempack backend. + * + * The backend must be added to an existing ODB with the highest + * priority. + * + * git_mempack_new(&mempacker); + * git_repository_odb(&odb, repository); + * git_odb_add_backend(odb, mempacker, 999); + * + * Once the backend has been loaded, all writes to the ODB will + * instead be queued in memory, and can be finalized with + * `git_mempack_dump`. + * + * 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 + * @return 0 on success; error code otherwise + */ +int git_mempack_new(git_odb_backend **out); + +/** + * Dump all the queued in-memory writes to a packfile. + * + * The contents of the packfile will be stored in the given buffer. + * It is the caller's responsibility to ensure that the generated + * packfile is available to the repository (e.g. by writing it + * to disk, or doing something crazy like distributing it across + * several copies of the repository over a network). + * + * Once the generated packfile is available to the repository, + * call `git_mempack_reset` to cleanup the memory store. + * + * Calling `git_mempack_reset` before the packfile has been + * written to disk will result in an inconsistent repository + * (the objects in the memory store won't be accessible). + * + * @param pack Buffer where to store the raw packfile + * @param repo The active repository where the backend is loaded + * @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); + +/** + * Reset the memory packer by clearing all the queued objects. + * + * This assumes that `git_mempack_dump` has been called before to + * store all the queued objects into a single packfile. + * + * Alternatively, call `reset` without a previous dump to "undo" + * all the recently written objects, giving transaction-like + * semantics to the Git repository. + * + * @param backend The mempack backend + */ +void git_mempack_reset(git_odb_backend *backend); + +GIT_END_DECL + +#endif diff -Nru libgit2-0.20.0/include/git2/sys/odb_backend.h libgit2-0.22.2/include/git2/sys/odb_backend.h --- libgit2-0.20.0/include/git2/sys/odb_backend.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/sys/odb_backend.h 2015-03-24 16:10:45.000000000 +0000 @@ -35,11 +35,8 @@ int (* read)( void **, size_t *, git_otype *, git_odb_backend *, const git_oid *); - /* To find a unique object given a prefix - * of its oid. - * The oid given must be so that the - * remaining (GIT_OID_HEXSZ - len)*4 bits - * are 0s. + /* To find a unique object given a prefix of its oid. The oid given + * must be so that the remaining (GIT_OID_HEXSZ - len)*4 bits are 0s. */ int (* read_prefix)( git_oid *, void **, size_t *, git_otype *, @@ -64,6 +61,9 @@ int (* exists)( git_odb_backend *, const git_oid *); + int (* exists_prefix)( + git_oid *, git_odb_backend *, const git_oid *, size_t); + /** * If the backend implements a refreshing mechanism, it should be exposed * through this endpoint. Each call to `git_odb_refresh()` will invoke it. @@ -81,7 +81,7 @@ int (* writepack)( git_odb_writepack **, git_odb_backend *, git_odb *odb, - git_transfer_progress_callback progress_cb, void *progress_payload); + git_transfer_progress_cb progress_cb, void *progress_payload); void (* free)(git_odb_backend *); }; @@ -89,6 +89,18 @@ #define GIT_ODB_BACKEND_VERSION 1 #define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION} +/** + * Initializes a `git_odb_backend` with default values. Equivalent to + * creating an instance with GIT_ODB_BACKEND_INIT. + * + * @param opts the `git_odb_backend` struct to initialize. + * @param version Version the struct; pass `GIT_ODB_BACKEND_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_odb_init_backend( + git_odb_backend *backend, + unsigned int version); + GIT_EXTERN(void *) git_odb_backend_malloc(git_odb_backend *backend, size_t len); GIT_END_DECL diff -Nru libgit2-0.20.0/include/git2/sys/openssl.h libgit2-0.22.2/include/git2/sys/openssl.h --- libgit2-0.20.0/include/git2/sys/openssl.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/include/git2/sys/openssl.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,38 @@ +/* + * 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_git_openssl_h__ +#define INCLUDE_git_openssl_h__ + +#include "git2/common.h" + +GIT_BEGIN_DECL + +/** + * Initialize the OpenSSL locks + * + * OpenSSL requires the application to determine how it performs + * locking. + * + * This is a last-resort convenience function which libgit2 provides for + * allocating and initializing the locks as well as setting the + * locking function to use the system's native locking functions. + * + * The locking function will be cleared and the memory will be freed + * when you call git_threads_sutdown(). + * + * If your programming language has an OpenSSL package/bindings, it + * likely sets up locking. You should very strongly prefer that over + * this function. + * + * @return 0 on success, -1 if there are errors or if libgit2 was not + * built with OpenSSL and threading support. + */ +GIT_EXTERN(int) git_openssl_set_locking(void); + +GIT_END_DECL +#endif + diff -Nru libgit2-0.20.0/include/git2/sys/refdb_backend.h libgit2-0.22.2/include/git2/sys/refdb_backend.h --- libgit2-0.20.0/include/git2/sys/refdb_backend.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/sys/refdb_backend.h 2015-03-24 16:10:45.000000000 +0000 @@ -93,17 +93,20 @@ * must provide this function. */ int (*write)(git_refdb_backend *backend, - const git_reference *ref, int force); + const git_reference *ref, int force, + const git_signature *who, const char *message, + const git_oid *old, const char *old_target); int (*rename)( git_reference **out, git_refdb_backend *backend, - const char *old_name, const char *new_name, int force); + const char *old_name, const char *new_name, int force, + const git_signature *who, const char *message); /** * Deletes the given reference from the refdb. A refdb implementation * must provide this function. */ - int (*del)(git_refdb_backend *backend, const char *ref_name); + int (*del)(git_refdb_backend *backend, const char *ref_name, const git_oid *old_id, const char *old_target); /** * Suggests that the given refdb compress or optimize its references. @@ -115,6 +118,17 @@ int (*compress)(git_refdb_backend *backend); /** + * Query whether a particular reference has a log (may be empty) + */ + int (*has_log)(git_refdb_backend *backend, const char *refname); + + /** + * Make sure a particular reference will have a reflog which + * will be appended to on writes. + */ + int (*ensure_log)(git_refdb_backend *backend, const char *refname); + + /** * Frees any resources held by the refdb. A refdb implementation may * provide this function; if it is not provided, nothing will be done. */ @@ -139,12 +153,37 @@ * Remove a reflog. */ int (*reflog_delete)(git_refdb_backend *backend, const char *name); + + /** + * Lock a reference. The opaque parameter will be passed to the unlock function + */ + int (*lock)(void **payload_out, git_refdb_backend *backend, const char *refname); + + /** + * Unlock a reference. Only one of target or symbolic_target + * will be set. success indicates whether to update the + * reference or discard the lock (if it's false) + */ + int (*unlock)(git_refdb_backend *backend, void *payload, int success, int update_reflog, + const git_reference *ref, const git_signature *sig, const char *message); }; #define GIT_REFDB_BACKEND_VERSION 1 #define GIT_REFDB_BACKEND_INIT {GIT_REFDB_BACKEND_VERSION} /** + * Initializes a `git_refdb_backend` with default values. Equivalent to + * creating an instance with GIT_REFDB_BACKEND_INIT. + * + * @param opts the `git_refdb_backend` struct to initialize + * @param version Version of struct; pass `GIT_REFDB_BACKEND_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_refdb_init_backend( + git_refdb_backend *backend, + unsigned int version); + +/** * Constructors for default filesystem-based refdb backend * * Under normal usage, this is called for you when the repository is diff -Nru libgit2-0.20.0/include/git2/sys/refs.h libgit2-0.22.2/include/git2/sys/refs.h --- libgit2-0.20.0/include/git2/sys/refs.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/sys/refs.h 2015-03-24 16:10:45.000000000 +0000 @@ -12,6 +12,15 @@ #include "git2/oid.h" /** + * @file git2/sys/refs.h + * @brief Low-level Git ref creation + * @defgroup git_backend Git custom backend APIs + * @ingroup Git + * @{ + */ +GIT_BEGIN_DECL + +/** * Create a new direct reference from an OID. * * @param name the reference name @@ -35,4 +44,6 @@ const char *name, const char *target); +/** @} */ +GIT_END_DECL #endif diff -Nru libgit2-0.20.0/include/git2/sys/repository.h libgit2-0.22.2/include/git2/sys/repository.h --- libgit2-0.20.0/include/git2/sys/repository.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/sys/repository.h 2015-03-24 16:10:45.000000000 +0000 @@ -7,6 +7,9 @@ #ifndef INCLUDE_sys_git_repository_h__ #define INCLUDE_sys_git_repository_h__ +#include "git2/common.h" +#include "git2/types.h" + /** * @file git2/sys/repository.h * @brief Git repository custom implementation routines @@ -53,7 +56,7 @@ * * @param repo A repository object * @param recurse_submodules Should submodules be updated recursively - * @returrn 0 on success, < 0 on error + * @return 0 on success, < 0 on error */ GIT_EXTERN(int) git_repository_reinit_filesystem( git_repository *repo, @@ -119,6 +122,19 @@ */ GIT_EXTERN(void) git_repository_set_index(git_repository *repo, git_index *index); +/** + * Set a repository to be bare. + * + * Clear the working directory and set core.bare to true. You may also + * want to call `git_repository_set_index(repo, NULL)` since a bare repo + * typically does not have an index, but this function will not do that + * for you. + * + * @param repo Repo to make bare + * @return 0 on success, <0 on failure + */ +GIT_EXTERN(int) git_repository_set_bare(git_repository *repo); + /** @} */ GIT_END_DECL #endif diff -Nru libgit2-0.20.0/include/git2/sys/stream.h libgit2-0.22.2/include/git2/sys/stream.h --- libgit2-0.20.0/include/git2/sys/stream.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/include/git2/sys/stream.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,42 @@ +/* + * 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_stream_h__ +#define INCLUDE_sys_git_stream_h__ + +#include "git2/common.h" +#include "git2/types.h" + +GIT_BEGIN_DECL + +#define GIT_STREAM_VERSION 1 + +/** + * Every stream must have this struct as its first element, so the + * API can talk to it. You'd define your stream as + * + * struct my_stream { + * git_stream parent; + * ... + * } + * + * and fill the functions + */ +typedef struct git_stream { + int version; + + int encrypted; + int (*connect)(struct git_stream *); + int (*certificate)(git_cert **, struct git_stream *); + ssize_t (*read)(struct git_stream *, void *, size_t); + ssize_t (*write)(struct git_stream *, const char *, size_t, int); + int (*close)(struct git_stream *); + void (*free)(struct git_stream *); +} git_stream; + +GIT_END_DECL + +#endif diff -Nru libgit2-0.20.0/include/git2/sys/transport.h libgit2-0.22.2/include/git2/sys/transport.h --- libgit2-0.20.0/include/git2/sys/transport.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/include/git2/sys/transport.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,365 @@ +/* + * 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" + +/** + * @file git2/sys/transport.h + * @brief Git custom transport registration interfaces and functions + * @defgroup git_transport Git custom transport registration + * @ingroup Git + * @{ + */ + +GIT_BEGIN_DECL + +/** + * Flags to pass to transport + * + * Currently unused. + */ +typedef enum { + GIT_TRANSPORTFLAGS_NONE = 0, +} git_transport_flags_t; + +typedef struct git_transport git_transport; + +struct git_transport { + unsigned int version; + /* Set progress and error callbacks */ + int (*set_callbacks)( + git_transport *transport, + git_transport_message_cb progress_cb, + git_transport_message_cb error_cb, + git_transport_certificate_check_cb certificate_check_cb, + void *payload); + + /* Connect the transport to the remote repository, using the given + * direction. */ + int (*connect)( + git_transport *transport, + const char *url, + git_cred_acquire_cb cred_acquire_cb, + void *cred_acquire_payload, + int direction, + int flags); + + /* This function may be called after a successful call to + * connect(). The array returned is owned by the transport and + * is guaranteed until the next call of a transport function. */ + int (*ls)( + const git_remote_head ***out, + size_t *size, + git_transport *transport); + + /* Executes the push whose context is in the git_push object. */ + int (*push)(git_transport *transport, git_push *push); + + /* This function may be called after a successful call to connect(), when + * the direction is FETCH. The function performs a negotiation to calculate + * the wants list for the fetch. */ + int (*negotiate_fetch)( + git_transport *transport, + git_repository *repo, + const git_remote_head * const *refs, + size_t count); + + /* This function may be called after a successful call to negotiate_fetch(), + * when the direction is FETCH. This function retrieves the pack file for + * the fetch from the remote end. */ + int (*download_pack)( + git_transport *transport, + git_repository *repo, + git_transfer_progress *stats, + git_transfer_progress_cb progress_cb, + void *progress_payload); + + /* Checks to see if the transport is connected */ + int (*is_connected)(git_transport *transport); + + /* Reads the flags value previously passed into connect() */ + int (*read_flags)(git_transport *transport, int *flags); + + /* Cancels any outstanding transport operation */ + void (*cancel)(git_transport *transport); + + /* This function is the reverse of connect() -- it terminates the + * connection to the remote end. */ + int (*close)(git_transport *transport); + + /* Frees/destructs the git_transport object. */ + void (*free)(git_transport *transport); +}; + +#define GIT_TRANSPORT_VERSION 1 +#define GIT_TRANSPORT_INIT {GIT_TRANSPORT_VERSION} + +/** + * Initializes a `git_transport` with default values. Equivalent to + * creating an instance with GIT_TRANSPORT_INIT. + * + * @param opts the `git_transport` struct to initialize + * @param version Version of struct; pass `GIT_TRANSPORT_VERSION` + * @return Zero on success; -1 on failure. + */ +GIT_EXTERN(int) git_transport_init( + git_transport *opts, + unsigned int version); + +/** + * Function to use to create a transport from a URL. The transport database + * is scanned to find a transport that implements the scheme of the URI (i.e. + * git:// or http://) and a transport object is returned to the caller. + * + * @param out The newly created transport (out) + * @param owner The git_remote which will own this transport + * @param url The URL to connect to + * @return 0 or an error code + */ +GIT_EXTERN(int) git_transport_new(git_transport **out, git_remote *owner, const char *url); + +/** + * Create an ssh transport with custom git command paths + * + * This is a factory function suitable for setting as the transport + * callback in a remote (or for a clone in the options). + * + * The payload argument must be a strarray pointer with the paths for + * the `git-upload-pack` and `git-receive-pack` at index 0 and 1. + * + * @param out the resulting transport + * @param owner the owning remote + * @param payload a strarray with the paths + * @return 0 or an error code + */ +GIT_EXTERN(int) git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *payload); + +/* Signature of a function which creates a transport */ +typedef int (*git_transport_cb)(git_transport **out, git_remote *owner, void *param); + +/** + * Add a custom transport definition, to be used in addition to the built-in + * set of transports that come with libgit2. + * + * The caller is responsible for synchronizing calls to git_transport_register + * and git_transport_unregister with other calls to the library that + * instantiate transports. + * + * @param prefix The scheme (ending in "://") to match, i.e. "git://" + * @param cb The callback used to create an instance of the transport + * @param param A fixed parameter to pass to cb at creation time + * @return 0 or an error code + */ +GIT_EXTERN(int) git_transport_register( + const char *prefix, + git_transport_cb cb, + void *param); + +/** + * + * Unregister a custom transport definition which was previously registered + * with git_transport_register. + * + * @param prefix From the previous call to git_transport_register + * @return 0 or an error code + */ +GIT_EXTERN(int) git_transport_unregister( + const char *prefix); + +/* Transports which come with libgit2 (match git_transport_cb). The expected + * value for "param" is listed in-line below. */ + +/** + * Create an instance of the dummy transport. + * + * @param out The newly created transport (out) + * @param owner The git_remote which will own this transport + * @param payload You must pass NULL for this parameter. + * @return 0 or an error code + */ +GIT_EXTERN(int) git_transport_dummy( + git_transport **out, + git_remote *owner, + /* NULL */ void *payload); + +/** + * Create an instance of the local transport. + * + * @param out The newly created transport (out) + * @param owner The git_remote which will own this transport + * @param payload You must pass NULL for this parameter. + * @return 0 or an error code + */ +GIT_EXTERN(int) git_transport_local( + git_transport **out, + git_remote *owner, + /* NULL */ void *payload); + +/** + * Create an instance of the smart transport. + * + * @param out The newly created transport (out) + * @param owner The git_remote which will own this transport + * @param payload A pointer to a git_smart_subtransport_definition + * @return 0 or an error code + */ +GIT_EXTERN(int) git_transport_smart( + git_transport **out, + git_remote *owner, + /* (git_smart_subtransport_definition *) */ void *payload); + +/* + *** End of base transport interface *** + *** Begin interface for subtransports for the smart transport *** + */ + +/* The smart transport knows how to speak the git protocol, but it has no + * knowledge of how to establish a connection between it and another endpoint, + * or how to move data back and forth. For this, a subtransport interface is + * declared, and the smart transport delegates this work to the subtransports. + * Three subtransports are implemented: git, http, and winhttp. (The http and + * winhttp transports each implement both http and https.) */ + +/* Subtransports can either be RPC = 0 (persistent connection) or RPC = 1 + * (request/response). The smart transport handles the differences in its own + * logic. The git subtransport is RPC = 0, while http and winhttp are both + * RPC = 1. */ + +/* Actions that the smart transport can ask + * a subtransport to perform */ +typedef enum { + GIT_SERVICE_UPLOADPACK_LS = 1, + GIT_SERVICE_UPLOADPACK = 2, + GIT_SERVICE_RECEIVEPACK_LS = 3, + GIT_SERVICE_RECEIVEPACK = 4, +} git_smart_service_t; + +typedef struct git_smart_subtransport git_smart_subtransport; +typedef struct git_smart_subtransport_stream git_smart_subtransport_stream; + +/* A stream used by the smart transport to read and write data + * from a subtransport */ +struct git_smart_subtransport_stream { + /* The owning subtransport */ + git_smart_subtransport *subtransport; + + int (*read)( + git_smart_subtransport_stream *stream, + char *buffer, + size_t buf_size, + size_t *bytes_read); + + int (*write)( + git_smart_subtransport_stream *stream, + const char *buffer, + size_t len); + + void (*free)( + git_smart_subtransport_stream *stream); +}; + +/* An implementation of a subtransport which carries data for the + * smart transport */ +struct git_smart_subtransport { + int (* action)( + git_smart_subtransport_stream **out, + git_smart_subtransport *transport, + const char *url, + git_smart_service_t action); + + /* Subtransports are guaranteed a call to close() between + * calls to action(), except for the following two "natural" progressions + * of actions against a constant URL. + * + * 1. UPLOADPACK_LS -> UPLOADPACK + * 2. RECEIVEPACK_LS -> RECEIVEPACK */ + int (*close)(git_smart_subtransport *transport); + + void (*free)(git_smart_subtransport *transport); +}; + +/* A function which creates a new subtransport for the smart transport */ +typedef int (*git_smart_subtransport_cb)( + git_smart_subtransport **out, + git_transport* owner); + +/** + * Definition for a "subtransport" + * + * This is used to let the smart protocol code know about the protocol + * which you are implementing. + */ +typedef struct git_smart_subtransport_definition { + /** The function to use to create the git_smart_subtransport */ + git_smart_subtransport_cb callback; + + /** + * True if the protocol is stateless; false otherwise. For example, + * http:// is stateless, but git:// is not. + */ + unsigned rpc; +} git_smart_subtransport_definition; + +/* Smart transport subtransports that come with libgit2 */ + +/** + * Create an instance of the http subtransport. This subtransport + * also supports https. On Win32, this subtransport may be implemented + * using the WinHTTP library. + * + * @param out The newly created subtransport + * @param owner The smart transport to own this subtransport + * @return 0 or an error code + */ +GIT_EXTERN(int) git_smart_subtransport_http( + git_smart_subtransport **out, + git_transport* owner); + +/** + * Create an instance of the git subtransport. + * + * @param out The newly created subtransport + * @param owner The smart transport to own this subtransport + * @return 0 or an error code + */ +GIT_EXTERN(int) git_smart_subtransport_git( + git_smart_subtransport **out, + git_transport* owner); + +/** + * Create an instance of the ssh subtransport. + * + * @param out The newly created subtransport + * @param owner The smart transport to own this subtransport + * @return 0 or an error code + */ +GIT_EXTERN(int) git_smart_subtransport_ssh( + git_smart_subtransport **out, + git_transport* owner); + +/** + * Sets a custom transport factory for the remote. The caller can use this + * function to override the transport used for this remote when performing + * network operations. + * + * @param remote the remote to configure + * @param transport_cb the function to use to create a transport + * @param payload opaque parameter passed to transport_cb + * @return 0 or an error code + */ +GIT_EXTERN(int) git_remote_set_transport( + git_remote *remote, + git_transport_cb transport_cb, + void *payload); + +/** @} */ +GIT_END_DECL +#endif diff -Nru libgit2-0.20.0/include/git2/threads.h libgit2-0.22.2/include/git2/threads.h --- libgit2-0.20.0/include/git2/threads.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/threads.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * 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_git_threads_h__ -#define INCLUDE_git_threads_h__ - -#include "common.h" - -/** - * @file git2/threads.h - * @brief Library level thread functions - * @defgroup git_thread Threading functions - * @ingroup Git - * @{ - */ -GIT_BEGIN_DECL - -/** - * Init the threading system. - * - * If libgit2 has been built with GIT_THREADS - * on, this function must be called once before - * any other library functions. - * - * If libgit2 has been built without GIT_THREADS - * support, this function is a no-op. - * - * @return 0 or an error code - */ -GIT_EXTERN(int) git_threads_init(void); - -/** - * Shutdown the threading system. - * - * If libgit2 has been built with GIT_THREADS - * on, this function must be called before shutting - * down the library. - * - * If libgit2 has been built without GIT_THREADS - * support, this function is a no-op. - */ -GIT_EXTERN(void) git_threads_shutdown(void); - -/** @} */ -GIT_END_DECL -#endif - diff -Nru libgit2-0.20.0/include/git2/transaction.h libgit2-0.22.2/include/git2/transaction.h --- libgit2-0.20.0/include/git2/transaction.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/include/git2/transaction.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,111 @@ +/* + * 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_git_transaction_h__ +#define INCLUDE_git_transaction_h__ + +#include "common.h" +GIT_BEGIN_DECL + +/** + * Create a new transaction object + * + * This does not lock anything, but sets up the transaction object to + * know from which repository to lock. + * + * @param out the resulting transaction + * @param repo the repository in which to lock + * @return 0 or an error code + */ +GIT_EXTERN(int) git_transaction_new(git_transaction **out, git_repository *repo); + +/** + * Lock a reference + * + * Lock the specified reference. This is the first step to updating a + * reference. + * + * @param tx the transaction + * @param refname the reference to lock + * @return 0 or an error message + */ +GIT_EXTERN(int) git_transaction_lock_ref(git_transaction *tx, const char *refname); + +/** + * Set the target of a reference + * + * Set the target of the specified reference. This reference must be + * locked. + * + * @param tx the transaction + * @param refname reference to update + * @param target target to set the reference to + * @param sig signature to use in the reflog; pass NULL to read the identity from the config + * @param msg message to use in the reflog + * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code + */ +GIT_EXTERN(int) git_transaction_set_target(git_transaction *tx, const char *refname, const git_oid *target, const git_signature *sig, const char *msg); + +/** + * Set the target of a reference + * + * Set the target of the specified reference. This reference must be + * locked. + * + * @param tx the transaction + * @param refname reference to update + * @param target target to set the reference to + * @param sig signature to use in the reflog; pass NULL to read the identity from the config + * @param msg message to use in the reflog + * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code + */ +GIT_EXTERN(int) git_transaction_set_symbolic_target(git_transaction *tx, const char *refname, const char *target, const git_signature *sig, const char *msg); + +/** + * Set the reflog of a reference + * + * Set the specified reference's reflog. If this is combined with + * setting the target, that update won't be written to the reflog. + * + * @param tx the transaction + * @param refname the reference whose reflog to set + * @param reflog the reflog as it should be written out + * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code + */ +GIT_EXTERN(int) git_transaction_set_reflog(git_transaction *tx, const char *refname, const git_reflog *reflog); + +/** + * Remove a reference + * + * @param tx the transaction + * @param refname the reference to remove + * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code + */ +GIT_EXTERN(int) git_transaction_remove(git_transaction *tx, const char *refname); + +/** + * Commit the changes from the transaction + * + * Perform the changes that have been queued. The updates will be made + * one by one, and the first failure will stop the processing. + * + * @param tx the transaction + * @return 0 or an error code + */ +GIT_EXTERN(int) git_transaction_commit(git_transaction *tx); + +/** + * Free the resources allocated by this transaction + * + * If any references remain locked, they will be unlocked without any + * changes made to them. + * + * @param tx the transaction + */ +GIT_EXTERN(void) git_transaction_free(git_transaction *tx); + +GIT_END_DECL +#endif diff -Nru libgit2-0.20.0/include/git2/transport.h libgit2-0.22.2/include/git2/transport.h --- libgit2-0.20.0/include/git2/transport.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/transport.h 2015-03-24 16:10:45.000000000 +0000 @@ -11,10 +11,6 @@ #include "net.h" #include "types.h" -#ifdef GIT_SSH -#include -#endif - /** * @file git2/transport.h * @brief Git transport interfaces and functions @@ -24,6 +20,63 @@ */ GIT_BEGIN_DECL +/** + * Type of SSH host fingerprint + */ +typedef enum { + /** MD5 is available */ + GIT_CERT_SSH_MD5 = (1 << 0), + /** SHA-1 is available */ + GIT_CERT_SSH_SHA1 = (1 << 1), +} git_cert_ssh_t; + +/** + * Hostkey information taken from libssh2 + */ +typedef struct { + /** + * Type of certificate. Here to share the header with + * `git_cert`. + */ + git_cert_t cert_type; + /** + * A hostkey type from libssh2, either + * `GIT_CERT_SSH_MD5` or `GIT_CERT_SSH_SHA1` + */ + git_cert_ssh_t type; + + /** + * Hostkey hash. If type has `GIT_CERT_SSH_MD5` set, this will + * have the MD5 hash of the hostkey. + */ + unsigned char hash_md5[16]; + + /** + * Hostkey hash. If type has `GIT_CERT_SSH_SHA1` set, this will + * have the SHA-1 hash of the hostkey. + */ + unsigned char hash_sha1[20]; +} git_cert_hostkey; + +/** + * X.509 certificate information + */ +typedef struct { + /** + * Type of certificate. Here to share the header with + * `git_cert`. + */ + git_cert_t cert_type; + /** + * Pointer to the X.509 certificate data + */ + void *data; + /** + * Length of the memory block pointed to by `data`. + */ + size_t len; +} git_cert_x509; + /* *** Begin interface for credentials acquisition *** */ @@ -41,6 +94,17 @@ /* git_cred_default */ GIT_CREDTYPE_DEFAULT = (1u << 3), + + /* git_cred_ssh_interactive */ + GIT_CREDTYPE_SSH_INTERACTIVE = (1u << 4), + + /** + * Username-only information + * + * If the SSH transport does not know which username to use, + * it will ask via this credential type. + */ + GIT_CREDTYPE_USERNAME = (1u << 5), } git_credtype_t; /* The base structure for all credential types */ @@ -58,12 +122,20 @@ char *password; } git_cred_userpass_plaintext; -#ifdef GIT_SSH -typedef LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*git_cred_sign_callback)); -#else -typedef int (*git_cred_sign_callback)(void *, ...); + +/* + * If the user hasn't included libssh2.h before git2.h, we need to + * define a few types for the callback signatures. + */ +#ifndef LIBSSH2_VERSION +typedef struct _LIBSSH2_SESSION LIBSSH2_SESSION; +typedef struct _LIBSSH2_USERAUTH_KBDINT_PROMPT LIBSSH2_USERAUTH_KBDINT_PROMPT; +typedef struct _LIBSSH2_USERAUTH_KBDINT_RESPONSE LIBSSH2_USERAUTH_KBDINT_RESPONSE; #endif +typedef int (*git_cred_sign_callback)(LIBSSH2_SESSION *session, unsigned char **sig, size_t *sig_len, const unsigned char *data, size_t data_len, void **abstract); +typedef void (*git_cred_ssh_interactive_callback)(const char* name, int name_len, const char* instruction, int instruction_len, int num_prompts, const LIBSSH2_USERAUTH_KBDINT_PROMPT* prompts, LIBSSH2_USERAUTH_KBDINT_RESPONSE* responses, void **abstract); + /** * A ssh key from disk */ @@ -76,6 +148,16 @@ } git_cred_ssh_key; /** + * Keyboard-interactive based ssh authentication + */ +typedef struct git_cred_ssh_interactive { + git_cred parent; + char *username; + git_cred_ssh_interactive_callback prompt_callback; + void *payload; +} git_cred_ssh_interactive; + +/** * A key with a custom signature function */ typedef struct git_cred_ssh_custom { @@ -83,13 +165,19 @@ char *username; char *publickey; size_t publickey_len; - void *sign_callback; - void *sign_data; + git_cred_sign_callback sign_callback; + void *payload; } git_cred_ssh_custom; /** A key for NTLM/Kerberos "default" credentials */ typedef struct git_cred git_cred_default; +/** Username-only credential information */ +typedef struct git_cred_username { + git_cred parent; + char username[1]; +} git_cred_username; + /** * Check whether a credential object contains username information. * @@ -131,6 +219,33 @@ const char *passphrase); /** + * Create a new ssh keyboard-interactive based credential object. + * The supplied credential parameter will be internally duplicated. + * + * @param username Username to use to authenticate. + * @param prompt_callback The callback method used for prompts. + * @param payload Additional data to pass to the callback. + * @return 0 for success or an error code for failure. + */ +GIT_EXTERN(int) git_cred_ssh_interactive_new( + git_cred **out, + const char *username, + git_cred_ssh_interactive_callback prompt_callback, + void *payload); + +/** + * Create a new ssh key credential object used for querying an ssh-agent. + * The supplied credential parameter will be internally duplicated. + * + * @param out The newly created credential object. + * @param username username to use to authenticate + * @return 0 for success or an error code for failure + */ +GIT_EXTERN(int) git_cred_ssh_key_from_agent( + git_cred **out, + const char *username); + +/** * Create an ssh key credential with a custom signing function. * * This lets you use your own function to sign the challenge. @@ -144,8 +259,8 @@ * @param username username to use to authenticate * @param publickey The bytes of the public key. * @param publickey_len The length of the public key in bytes. - * @param sign_fn The callback method to sign the data during the challenge. - * @param sign_data The data to pass to the sign function. + * @param sign_callback The callback method to sign the data during the challenge. + * @param payload Additional data to pass to the callback. * @return 0 for success or an error code for failure */ GIT_EXTERN(int) git_cred_ssh_custom_new( @@ -153,8 +268,8 @@ const char *username, const char *publickey, size_t publickey_len, - git_cred_sign_callback sign_fn, - void *sign_data); + git_cred_sign_callback sign_callback, + void *payload); /** * Create a "default" credential usable for Negotiate mechanisms like NTLM @@ -165,6 +280,14 @@ GIT_EXTERN(int) git_cred_default_new(git_cred **out); /** + * Create a credential to specify a username. + * + * This is used with ssh authentication to query for the username if + * none is specified in the url. + */ +GIT_EXTERN(int) git_cred_username_new(git_cred **cred, const char *username); + +/** * Signature of a function which acquires a credential object. * * - cred: The newly created credential object. @@ -173,7 +296,8 @@ * remote url, or NULL if not included. * - allowed_types: A bitmask stating which cred types are OK to return. * - payload: The payload provided when specifying this callback. - * - returns 0 for success or non-zero to indicate an error + * - returns 0 for success, < 0 to indicate an error, > 0 to indicate + * no credential was acquired */ typedef int (*git_cred_acquire_cb)( git_cred **cred, @@ -182,307 +306,6 @@ unsigned int allowed_types, void *payload); -/* - *** End interface for credentials acquisition *** - *** Begin base transport interface *** - */ - -typedef enum { - GIT_TRANSPORTFLAGS_NONE = 0, - /* If the connection is secured with SSL/TLS, the authenticity - * of the server certificate should not be verified. */ - GIT_TRANSPORTFLAGS_NO_CHECK_CERT = 1 -} git_transport_flags_t; - -typedef int (*git_transport_message_cb)(const char *str, int len, void *data); - -typedef struct git_transport git_transport; - -struct git_transport { - unsigned int version; - /* Set progress and error callbacks */ - int (*set_callbacks)( - git_transport *transport, - git_transport_message_cb progress_cb, - git_transport_message_cb error_cb, - void *payload); - - /* Connect the transport to the remote repository, using the given - * direction. */ - int (*connect)( - git_transport *transport, - const char *url, - git_cred_acquire_cb cred_acquire_cb, - void *cred_acquire_payload, - int direction, - int flags); - - /* This function may be called after a successful call to - * connect(). The array returned is owned by the transport and - * is guranteed until the next call of a transport function. */ - int (*ls)( - const git_remote_head ***out, - size_t *size, - git_transport *transport); - - /* Executes the push whose context is in the git_push object. */ - int (*push)(git_transport *transport, git_push *push); - - /* This function may be called after a successful call to connect(), when - * the direction is FETCH. The function performs a negotiation to calculate - * the wants list for the fetch. */ - int (*negotiate_fetch)( - git_transport *transport, - git_repository *repo, - const git_remote_head * const *refs, - size_t count); - - /* This function may be called after a successful call to negotiate_fetch(), - * when the direction is FETCH. This function retrieves the pack file for - * the fetch from the remote end. */ - int (*download_pack)( - git_transport *transport, - git_repository *repo, - git_transfer_progress *stats, - git_transfer_progress_callback progress_cb, - void *progress_payload); - - /* Checks to see if the transport is connected */ - int (*is_connected)(git_transport *transport); - - /* Reads the flags value previously passed into connect() */ - int (*read_flags)(git_transport *transport, int *flags); - - /* Cancels any outstanding transport operation */ - void (*cancel)(git_transport *transport); - - /* This function is the reverse of connect() -- it terminates the - * connection to the remote end. */ - int (*close)(git_transport *transport); - - /* Frees/destructs the git_transport object. */ - void (*free)(git_transport *transport); -}; - -#define GIT_TRANSPORT_VERSION 1 -#define GIT_TRANSPORT_INIT {GIT_TRANSPORT_VERSION} - -/** - * Function to use to create a transport from a URL. The transport database - * is scanned to find a transport that implements the scheme of the URI (i.e. - * git:// or http://) and a transport object is returned to the caller. - * - * @param out The newly created transport (out) - * @param owner The git_remote which will own this transport - * @param url The URL to connect to - * @return 0 or an error code - */ -GIT_EXTERN(int) git_transport_new(git_transport **out, git_remote *owner, const char *url); - -/* Signature of a function which creates a transport */ -typedef int (*git_transport_cb)(git_transport **out, git_remote *owner, void *param); - -/** - * Add a custom transport definition, to be used in addition to the built-in - * set of transports that come with libgit2. - * - * The caller is responsible for synchronizing calls to git_transport_register - * and git_transport_unregister with other calls to the library that - * instantiate transports. - * - * @param prefix The scheme (ending in "://") to match, i.e. "git://" - * @param priority The priority of this transport relative to others with - * the same prefix. Built-in transports have a priority of 1. - * @param cb The callback used to create an instance of the transport - * @param param A fixed parameter to pass to cb at creation time - * @return 0 or an error code - */ -GIT_EXTERN(int) git_transport_register( - const char *prefix, - unsigned priority, - git_transport_cb cb, - void *param); - -/** - * - * Unregister a custom transport definition which was previously registered - * with git_transport_register. - * - * @param prefix From the previous call to git_transport_register - * @param priority From the previous call to git_transport_register - * @return 0 or an error code - */ -GIT_EXTERN(int) git_transport_unregister( - const char *prefix, - unsigned priority); - -/* Transports which come with libgit2 (match git_transport_cb). The expected - * value for "param" is listed in-line below. */ - -/** - * Create an instance of the dummy transport. - * - * @param out The newly created transport (out) - * @param owner The git_remote which will own this transport - * @param payload You must pass NULL for this parameter. - * @return 0 or an error code - */ -GIT_EXTERN(int) git_transport_dummy( - git_transport **out, - git_remote *owner, - /* NULL */ void *payload); - -/** - * Create an instance of the local transport. - * - * @param out The newly created transport (out) - * @param owner The git_remote which will own this transport - * @param payload You must pass NULL for this parameter. - * @return 0 or an error code - */ -GIT_EXTERN(int) git_transport_local( - git_transport **out, - git_remote *owner, - /* NULL */ void *payload); - -/** - * Create an instance of the smart transport. - * - * @param out The newly created transport (out) - * @param owner The git_remote which will own this transport - * @param payload A pointer to a git_smart_subtransport_definition - * @return 0 or an error code - */ -GIT_EXTERN(int) git_transport_smart( - git_transport **out, - git_remote *owner, - /* (git_smart_subtransport_definition *) */ void *payload); - -/* - *** End of base transport interface *** - *** Begin interface for subtransports for the smart transport *** - */ - -/* The smart transport knows how to speak the git protocol, but it has no - * knowledge of how to establish a connection between it and another endpoint, - * or how to move data back and forth. For this, a subtransport interface is - * declared, and the smart transport delegates this work to the subtransports. - * Three subtransports are implemented: git, http, and winhttp. (The http and - * winhttp transports each implement both http and https.) */ - -/* Subtransports can either be RPC = 0 (persistent connection) or RPC = 1 - * (request/response). The smart transport handles the differences in its own - * logic. The git subtransport is RPC = 0, while http and winhttp are both - * RPC = 1. */ - -/* Actions that the smart transport can ask - * a subtransport to perform */ -typedef enum { - GIT_SERVICE_UPLOADPACK_LS = 1, - GIT_SERVICE_UPLOADPACK = 2, - GIT_SERVICE_RECEIVEPACK_LS = 3, - GIT_SERVICE_RECEIVEPACK = 4, -} git_smart_service_t; - -typedef struct git_smart_subtransport git_smart_subtransport; -typedef struct git_smart_subtransport_stream git_smart_subtransport_stream; - -/* A stream used by the smart transport to read and write data - * from a subtransport */ -struct git_smart_subtransport_stream { - /* The owning subtransport */ - git_smart_subtransport *subtransport; - - int (*read)( - git_smart_subtransport_stream *stream, - char *buffer, - size_t buf_size, - size_t *bytes_read); - - int (*write)( - git_smart_subtransport_stream *stream, - const char *buffer, - size_t len); - - void (*free)( - git_smart_subtransport_stream *stream); -}; - -/* An implementation of a subtransport which carries data for the - * smart transport */ -struct git_smart_subtransport { - int (* action)( - git_smart_subtransport_stream **out, - git_smart_subtransport *transport, - const char *url, - git_smart_service_t action); - - /* Subtransports are guaranteed a call to close() between - * calls to action(), except for the following two "natural" progressions - * of actions against a constant URL. - * - * 1. UPLOADPACK_LS -> UPLOADPACK - * 2. RECEIVEPACK_LS -> RECEIVEPACK */ - int (*close)(git_smart_subtransport *transport); - - void (*free)(git_smart_subtransport *transport); -}; - -/* A function which creates a new subtransport for the smart transport */ -typedef int (*git_smart_subtransport_cb)( - git_smart_subtransport **out, - git_transport* owner); - -typedef struct git_smart_subtransport_definition { - /* The function to use to create the git_smart_subtransport */ - git_smart_subtransport_cb callback; - - /* True if the protocol is stateless; false otherwise. For example, - * http:// is stateless, but git:// is not. */ - unsigned rpc; -} git_smart_subtransport_definition; - -/* Smart transport subtransports that come with libgit2 */ - -/** - * Create an instance of the http subtransport. This subtransport - * also supports https. On Win32, this subtransport may be implemented - * using the WinHTTP library. - * - * @param out The newly created subtransport - * @param owner The smart transport to own this subtransport - * @return 0 or an error code - */ -GIT_EXTERN(int) git_smart_subtransport_http( - git_smart_subtransport **out, - git_transport* owner); - -/** - * Create an instance of the git subtransport. - * - * @param out The newly created subtransport - * @param owner The smart transport to own this subtransport - * @return 0 or an error code - */ -GIT_EXTERN(int) git_smart_subtransport_git( - git_smart_subtransport **out, - git_transport* owner); - -/** - * Create an instance of the ssh subtransport. - * - * @param out The newly created subtransport - * @param owner The smart transport to own this subtransport - * @return 0 or an error code - */ -GIT_EXTERN(int) git_smart_subtransport_ssh( - git_smart_subtransport **out, - git_transport* owner); - -/* - *** End interface for subtransports for the smart transport *** - */ - /** @} */ GIT_END_DECL #endif diff -Nru libgit2-0.20.0/include/git2/tree.h libgit2-0.22.2/include/git2/tree.h --- libgit2-0.20.0/include/git2/tree.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/tree.h 2015-03-24 16:10:45.000000000 +0000 @@ -121,11 +121,11 @@ * Warning: this must examine every entry in the tree, so it is not fast. * * @param tree a previously loaded tree. - * @param oid the sha being looked for + * @param id the sha being looked for * @return the tree entry; NULL if not found */ -GIT_EXTERN(const git_tree_entry *) git_tree_entry_byoid( - const git_tree *tree, const git_oid *oid); +GIT_EXTERN(const git_tree_entry *) git_tree_entry_byid( + const git_tree *tree, const git_oid *id); /** * Retrieve a tree entry contained in a tree or in any of its subtrees, @@ -150,10 +150,11 @@ * Create a copy of a tree entry. The returned copy is owned by the user, * and must be freed explicitly with `git_tree_entry_free()`. * - * @param entry A tree entry to duplicate - * @return a copy of the original entry or NULL on error (alloc failure) + * @param dest pointer where to store the copy + * @param source tree entry to duplicate + * @return 0 or an error code */ -GIT_EXTERN(git_tree_entry *) git_tree_entry_dup(const git_tree_entry *entry); +GIT_EXTERN(int) git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source); /** * Free a user-owned tree entry @@ -246,11 +247,12 @@ * entries and will have to be filled manually. * * @param out Pointer where to store the tree builder + * @param repo Repository in which to store the object * @param source Source tree to initialize the builder (optional) * @return 0 on success; error code otherwise */ -GIT_EXTERN(int) git_treebuilder_create( - git_treebuilder **out, const git_tree *source); +GIT_EXTERN(int) git_treebuilder_new( + git_treebuilder **out, git_repository *repo, const git_tree *source); /** * Clear all the entires in the builder @@ -300,8 +302,10 @@ * If an entry named `filename` already exists, its attributes * will be updated with the given ones. * - * The optional pointer `out` can be used to retrieve a pointer to - * the newly created/updated entry. Pass NULL if you do not need it. + * The optional pointer `out` can be used to retrieve a pointer to the + * newly created/updated entry. Pass NULL if you do not need it. The + * 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 @@ -332,11 +336,18 @@ GIT_EXTERN(int) git_treebuilder_remove( git_treebuilder *bld, const char *filename); +/** + * Callback for git_treebuilder_filter + * + * The return value is treated as a boolean, with zero indicating that the + * entry should be left alone and any non-zero value meaning that the + * entry should be removed from the treebuilder list (i.e. filtered out). + */ typedef int (*git_treebuilder_filter_cb)( const git_tree_entry *entry, void *payload); /** - * Filter the entries in the tree + * Selectively remove entries in the tree * * The `filter` callback will be called for each entry in the tree with a * pointer to the entry and the provided `payload`; if the callback returns @@ -344,7 +355,7 @@ * * @param bld Tree builder * @param filter Callback to filter entries - * @param payload Extra data to pass to filter + * @param payload Extra data to pass to filter callback */ GIT_EXTERN(void) git_treebuilder_filter( git_treebuilder *bld, @@ -358,12 +369,11 @@ * identifying SHA1 hash will be stored in the `id` pointer. * * @param id Pointer to store the OID of the newly written tree - * @param repo Repository in which to store the object * @param bld Tree builder to write * @return 0 or an error code */ GIT_EXTERN(int) git_treebuilder_write( - git_oid *id, git_repository *repo, git_treebuilder *bld); + git_oid *id, git_treebuilder *bld); /** Callback for the tree traversal method */ diff -Nru libgit2-0.20.0/include/git2/types.h libgit2-0.22.2/include/git2/types.h --- libgit2-0.20.0/include/git2/types.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/types.h 2015-03-24 16:10:45.000000000 +0000 @@ -131,7 +131,7 @@ /** Memory representation of an index file. */ typedef struct git_index git_index; -/** An interator for conflicts in the index. */ +/** An iterator for conflicts in the index. */ typedef struct git_index_conflict_iterator git_index_conflict_iterator; /** Memory representation of a set of config files */ @@ -154,15 +154,15 @@ /** Time in a signature */ typedef struct git_time { - git_time_t time; /** time in seconds from epoch */ - int offset; /** timezone offset, in minutes */ + git_time_t time; /**< time in seconds from epoch */ + int offset; /**< timezone offset, in minutes */ } git_time; /** An action signature (e.g. for committers, taggers, etc) */ typedef struct git_signature { - char *name; /** full name of the author */ - char *email; /** email of the author */ - git_time when; /** time when the action happened */ + char *name; /**< full name of the author */ + char *email; /**< email of the author */ + git_time when; /**< time when the action happened */ } git_signature; /** In-memory representation of a reference. */ @@ -171,8 +171,11 @@ /** Iterator for references */ typedef struct git_reference_iterator git_reference_iterator; -/** Merge heads, the input to merge */ -typedef struct git_merge_head git_merge_head; +/** Transactional interface to references */ +typedef struct git_transaction git_transaction; + +/** Annotated commits, the input to merge and rebase. */ +typedef struct git_annotated_commit git_annotated_commit; /** Merge result */ typedef struct git_merge_result git_merge_result; @@ -180,12 +183,14 @@ /** Representation of a status collection */ typedef struct git_status_list git_status_list; +/** Representation of a rebase */ +typedef struct git_rebase git_rebase; /** Basic type of any Git reference. */ typedef enum { - GIT_REF_INVALID = 0, /** Invalid reference */ - GIT_REF_OID = 1, /** A reference which points at an object id */ - GIT_REF_SYMBOLIC = 2, /** A reference which points at another reference */ + GIT_REF_INVALID = 0, /**< Invalid reference */ + GIT_REF_OID = 1, /**< A reference which points at an object id */ + GIT_REF_SYMBOLIC = 2, /**< A reference which points at another reference */ GIT_REF_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC, } git_ref_t; @@ -193,22 +198,38 @@ typedef enum { GIT_BRANCH_LOCAL = 1, GIT_BRANCH_REMOTE = 2, + GIT_BRANCH_ALL = GIT_BRANCH_LOCAL|GIT_BRANCH_REMOTE, } git_branch_t; /** Valid modes for index and tree entries. */ typedef enum { - GIT_FILEMODE_NEW = 0000000, - GIT_FILEMODE_TREE = 0040000, - GIT_FILEMODE_BLOB = 0100644, - GIT_FILEMODE_BLOB_EXECUTABLE = 0100755, - GIT_FILEMODE_LINK = 0120000, - GIT_FILEMODE_COMMIT = 0160000, + GIT_FILEMODE_UNREADABLE = 0000000, + GIT_FILEMODE_TREE = 0040000, + GIT_FILEMODE_BLOB = 0100644, + GIT_FILEMODE_BLOB_EXECUTABLE = 0100755, + GIT_FILEMODE_LINK = 0120000, + GIT_FILEMODE_COMMIT = 0160000, } git_filemode_t; +/* + * A refspec specifies the mapping between remote and local reference + * names when fetch or pushing. + */ typedef struct git_refspec git_refspec; + +/** + * Git's idea of a remote repository. A remote can be anonymous (in + * which case it does not have backing configuration entires). + */ typedef struct git_remote git_remote; + +/** + * Preparation for a push operation. Can be used to configure what to + * push and the level of parallelism of the packfile builder. + */ typedef struct git_push git_push; +/* documentation in the definition */ typedef struct git_remote_head git_remote_head; typedef struct git_remote_callbacks git_remote_callbacks; @@ -240,7 +261,56 @@ * @param stats Structure containing information about the state of the transfer * @param payload Payload provided by caller */ -typedef int (*git_transfer_progress_callback)(const git_transfer_progress *stats, void *payload); +typedef int (*git_transfer_progress_cb)(const git_transfer_progress *stats, void *payload); + +/** + * Type for messages delivered by the transport. Return a negative value + * to cancel the network operation. + * + * @param str The message from the transport + * @param len The length of the message + * @param payload Payload provided by the caller + */ +typedef int (*git_transport_message_cb)(const char *str, int len, void *payload); + +/** + * Type of host certificate structure that is passed to the check callback + */ +typedef enum git_cert_t { + /** + * The `data` argument to the callback will be a pointer to + * the DER-encoded data. + */ + GIT_CERT_X509, + /** + * The `data` argument to the callback will be a pointer to a + * `git_cert_hostkey` structure. + */ + GIT_CERT_HOSTKEY_LIBSSH2, +} git_cert_t; + +/** + * Parent type for `git_cert_hostkey` and `git_cert_x509`. + */ +typedef struct { + /** + * Type of certificate. A `GIT_CERT_` value. + */ + git_cert_t cert_type; +} git_cert; + +/** + * Callback for the user's custom certificate checks. + * + * @param type The type of certificate or host info, SSH or X.509 + * @param data The data for the certificate or host info + * @param len The size of the certificate or host info + * @param valid Whether the libgit2 checks (OpenSSL or WinHTTP) think + * this certificate is valid + * @param host Hostname of the host libgit2 connected to + * @param payload Payload provided by the caller + */ +typedef int (*git_transport_certificate_check_cb)(git_cert *cert, int valid, const char *host, void *payload); /** * Opaque structure representing a submodule. @@ -313,16 +383,35 @@ * when we don't want any particular ignore rule to be specified. */ typedef enum { - GIT_SUBMODULE_IGNORE_RESET = -1, /* reset to on-disk value */ + GIT_SUBMODULE_IGNORE_RESET = -1, /**< reset to on-disk value */ - GIT_SUBMODULE_IGNORE_NONE = 1, /* any change or untracked == dirty */ - GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /* dirty if tracked files change */ - GIT_SUBMODULE_IGNORE_DIRTY = 3, /* only dirty if HEAD moved */ - GIT_SUBMODULE_IGNORE_ALL = 4, /* never dirty */ + GIT_SUBMODULE_IGNORE_NONE = 1, /**< any change or untracked == dirty */ + GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /**< dirty if tracked files change */ + GIT_SUBMODULE_IGNORE_DIRTY = 3, /**< only dirty if HEAD moved */ + GIT_SUBMODULE_IGNORE_ALL = 4, /**< never dirty */ GIT_SUBMODULE_IGNORE_DEFAULT = 0 } git_submodule_ignore_t; +/** + * Options for submodule recurse. + * + * Represent the value of `submodule.$name.fetchRecurseSubmodules` + * + * * GIT_SUBMODULE_RECURSE_RESET - reset to the on-disk value + * * GIT_SUBMODULE_RECURSE_NO - do no recurse into submodules + * * GIT_SUBMODULE_RECURSE_YES - recurse into submodules + * * GIT_SUBMODULE_RECURSE_ONDEMAND - recurse into submodules only when + * commit not already in local clone + */ +typedef enum { + GIT_SUBMODULE_RECURSE_RESET = -1, + + GIT_SUBMODULE_RECURSE_NO = 0, + GIT_SUBMODULE_RECURSE_YES = 1, + GIT_SUBMODULE_RECURSE_ONDEMAND = 2, +} git_submodule_recurse_t; + /** @} */ GIT_END_DECL diff -Nru libgit2-0.20.0/include/git2/version.h libgit2-0.22.2/include/git2/version.h --- libgit2-0.20.0/include/git2/version.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2/version.h 2015-03-24 16:10:45.000000000 +0000 @@ -7,9 +7,11 @@ #ifndef INCLUDE_git_version_h__ #define INCLUDE_git_version_h__ -#define LIBGIT2_VERSION "0.20.0" +#define LIBGIT2_VERSION "0.22.2" #define LIBGIT2_VER_MAJOR 0 -#define LIBGIT2_VER_MINOR 20 -#define LIBGIT2_VER_REVISION 0 +#define LIBGIT2_VER_MINOR 22 +#define LIBGIT2_VER_REVISION 2 + +#define LIBGIT2_SOVERSION 22 #endif diff -Nru libgit2-0.20.0/include/git2.h libgit2-0.22.2/include/git2.h --- libgit2-0.20.0/include/git2.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/include/git2.h 2015-03-24 16:10:45.000000000 +0000 @@ -8,19 +8,23 @@ #ifndef INCLUDE_git_git_h__ #define INCLUDE_git_git_h__ +#include "git2/annotated_commit.h" #include "git2/attr.h" #include "git2/blob.h" #include "git2/blame.h" #include "git2/branch.h" #include "git2/buffer.h" #include "git2/checkout.h" +#include "git2/cherrypick.h" #include "git2/clone.h" #include "git2/commit.h" #include "git2/common.h" #include "git2/config.h" +#include "git2/describe.h" #include "git2/diff.h" #include "git2/errors.h" #include "git2/filter.h" +#include "git2/global.h" #include "git2/graph.h" #include "git2/ignore.h" #include "git2/index.h" @@ -31,11 +35,13 @@ #include "git2/notes.h" #include "git2/object.h" #include "git2/odb.h" +#include "git2/odb_backend.h" #include "git2/oid.h" #include "git2/pack.h" #include "git2/patch.h" #include "git2/pathspec.h" #include "git2/push.h" +#include "git2/rebase.h" #include "git2/refdb.h" #include "git2/reflog.h" #include "git2/refs.h" @@ -43,6 +49,7 @@ #include "git2/remote.h" #include "git2/repository.h" #include "git2/reset.h" +#include "git2/revert.h" #include "git2/revparse.h" #include "git2/revwalk.h" #include "git2/signature.h" @@ -50,7 +57,6 @@ #include "git2/status.h" #include "git2/submodule.h" #include "git2/tag.h" -#include "git2/threads.h" #include "git2/transport.h" #include "git2/tree.h" #include "git2/types.h" diff -Nru libgit2-0.20.0/PROJECTS.md libgit2-0.22.2/PROJECTS.md --- libgit2-0.20.0/PROJECTS.md 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/PROJECTS.md 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,101 @@ +Projects For LibGit2 +==================== + +So, you want to start helping out with `libgit2`? That's fantastic! We +welcome contributions and we promise we'll try to be nice. + +This is a list of libgit2 related projects that new contributors can take +on. It includes a number of good starter projects and well as some larger +ideas that no one is actively working on. + +## Before You Start + +Please start by reading the [README.md](README.md), +[CONTRIBUTING.md](CONTRIBUTING.md), and [CONVENTIONS.md](CONVENTIONS.md) +files before diving into one of these projects. Those explain our work +flow and coding conventions to help ensure that your work will be easily +integrated into libgit2. + +Next, work through the build instructions and make sure you can clone the +repository, compile it, and run the tests successfully. That will make +sure that your development environment is set up correctly and you are +ready to start on libgit2 development. + +## Starter Projects + +These are good small projects to get started with libgit2. + +* Look at the `examples/` programs, find an existing one that mirrors a + core Git command and add a missing command-line option. There are many + gaps right now and this helps demonstrate how to use the library. Here + are some specific ideas (though there are many more): + * Fix the `examples/diff.c` implementation of the `-B` + (a.k.a. `--break-rewrites`) command line option to actually look for + the optional `[][/]` configuration values. There is an + existing comment that reads `/* TODO: parse thresholds */`. The + 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 + libgit2 supports it in the revwalk API. +* Pick a Git command that is not already emulated in `examples/` and write + a new example that mirrors the behavior. Examples don't have to be + perfect emulations, but should demonstrate how to use the libgit2 APIs + to get results that are similar to Git commands. This lets you (and us) + easily exercise a particular facet of the API and measure compatability + and feature parity with core git. +* Submit a PR to clarify documentation! While we do try to document all of + the APIs, your fresh eyes on the documentation will find areas that are + confusing much more easily. + +If none of these appeal to you, take a look at our issues list to see if +there are any unresolved issues you'd like to jump in on. + +## Larger Projects + +These are ideas for larger projects mostly taken from our backlog of +[Issues](https://github.com/libgit2/libgit2/issues). Please don't dive +into one of these as a first project for libgit2 - we'd rather get to +know you first by successfully shipping your work on one of the smaller +projects above. + +Some of these projects are broken down into subprojects and/or have +some incremental steps listed towards the larger goal. Those steps +might make good smaller projects by themselves. + +* Port part of the Git test suite to run against the command line emulation + in examples/ + * Pick a Git command that is emulated in our examples/ area + * Extract the Git tests that exercise that command + * Convert the tests to call our emulation + * These tests could go in examples/tests/... +* Fix symlink support for files in the .git directory (i.e. don't overwrite + the symlinks when writing the file contents back out) +* Add hooks API to enumerate and manage hooks (not run them at this point) + * Enumeration of available hooks + * Lookup API to see which hooks have a script and get the script + * Read/write API to load a hook script and write a hook script + * Eventually, callback API to invoke a hook callback when libgit2 + executes the action in question +* Isolate logic of ignore evaluation into a standalone API +* Upgrade internal libxdiff code to latest from core Git +* Improve index internals with hashtable lookup for files instead of + using binary search every time +* Tree builder improvements: + * Extend to allow building a tree hierarchy +* Apply-patch API +* Add a patch editing API to enable "git add -p" type operations +* Textconv API to filter binary data before generating diffs (something + like the current Filter API, probably). +* Performance profiling and improvement +* Support "git replace" ref replacements +* Include conflicts in diff results and in status + * GIT_DELTA_CONFLICT for items in conflict (with multiple files) + * Appropriate flags for status +* Support sparse checkout (i.e. "core.sparsecheckout" and ".git/info/sparse-checkout") diff -Nru libgit2-0.20.0/README.md libgit2-0.22.2/README.md --- libgit2-0.20.0/README.md 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/README.md 2015-03-24 16:10:45.000000000 +0000 @@ -1,11 +1,13 @@ libgit2 - the Git linkable library ================================== -[![Build Status](https://secure.travis-ci.org/libgit2/libgit2.png?branch=development)](http://travis-ci.org/libgit2/libgit2) - -`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. +[![Travis Build Status](https://secure.travis-ci.org/libgit2/libgit2.png?branch=master)](http://travis-ci.org/libgit2/libgit2) +[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/gnjsdi9r48cfoveg/branch/master?svg=true)](https://ci.appveyor.com/project/nulltoken/libgit2/branch/master) +[![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. `libgit2` is licensed under a **very permissive license** (GPLv2 with a special Linking Exception). This basically means that you can link it (unmodified) @@ -19,18 +21,19 @@ * API documentation: * IRC: [#libgit2](irc://irc.freenode.net/libgit2) on irc.freenode.net. * Mailing list: The libgit2 mailing list was - traditionally hosted in Librelist but has been deprecated. We encourage you to + traditionally hosted in Librelist but has been deprecated. We encourage you to [use StackOverflow](http://stackoverflow.com/questions/tagged/libgit2) instead for any questions regarding - the library, or [open an issue](https://github.com/libgit2/libgit2/issues) - on GitHub for bug reports. The mailing list archives are still available at + the library, or [open an issue](https://github.com/libgit2/libgit2/issues) + on GitHub for bug reports. The mailing list archives are still available at . What It Can Do ============== -`libgit2` is already very usable and is being used in production for many applications including the GitHub.com site, in Plastic SCM -and also powering Microsoft's Visual Studio tools for Git. The library provides: +`libgit2` is already very usable and is being used in production for many +applications including the GitHub.com site, in Plastic SCM and also powering +Microsoft's Visual Studio tools for Git. The library provides: * SHA conversions, formatting and shortening * abstracted ODB backend system @@ -53,9 +56,27 @@ - pthreads (non-Windows) to enable threadsafe access as well as multi-threaded pack generation - OpenSSL (non-Windows) to talk over HTTPS and provide the SHA-1 functions -- LibSSH2 to enable the ssh transport +- LibSSH2 to enable the SSH transport - iconv (OSX) to handle the HFS+ path encoding peculiarities +Initialization +=============== + +The library needs to keep track of some global state. Call + + git_libgit2_init(); + +before calling any other libgit2 functions. You can call this function many times. A matching number of calls to + + git_libgit2_shutdown(); + +will free the resources. + +Threading +========= + +See [THREADING](THREADING.md) for information + Building libgit2 - Using CMake ============================== @@ -64,7 +85,7 @@ they should be installed by default on all systems. Under Windows, libgit2 uses the native Windows API for threading. -The `libgit2` library is built using `CMake 2.6+` () on all platforms. +The `libgit2` library is built using [CMake]() (version 2.6 or newer) on all platforms. On most systems you can build the library using the following commands @@ -88,7 +109,7 @@ - `INCLUDE_INSTALL_DIR`: Where to install headers to. - `BUILD_SHARED_LIBS`: Build libgit2 as a Shared Library (defaults to ON) - `BUILD_CLAR`: Build [Clar](https://github.com/vmg/clar)-based test suite (defaults to ON) -- `THREADSAFE`: Build libgit2 with threading support (defaults to OFF) +- `THREADSAFE`: Build libgit2 with threading support (defaults to ON) - `STDCALL`: Build libgit2 as `stdcall`. Turn off for `cdecl` (Windows; defaults to ON) Compiler and linker options @@ -117,8 +138,7 @@ prompt, not the regular or Windows SDK one. Select the right generator for your version with the `-G "Visual Studio X" option. -See [the wiki] -(https://github.com/libgit2/libgit2/wiki/Building-libgit2-on-Windows) +See [the website](https://libgit2.github.com/docs/guides/build-and-link) for more detailed instructions. Android @@ -131,11 +151,11 @@ SET(CMAKE_SYSTEM_NAME Linux) SET(CMAKE_SYSTEM_VERSION Android) - + SET(CMAKE_C_COMPILER {PATH}/bin/arm-linux-androideabi-gcc) SET(CMAKE_CXX_COMPILER {PATH}/bin/arm-linux-androideabi-g++) SET(CMAKE_FIND_ROOT_PATH {PATH}/sysroot/) - + SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) @@ -164,11 +184,14 @@ * libgit2-glib * Haskell * hgit2 +* Java + * Jagged +* Julia + * LibGit2.jl * Lua * luagit2 * .NET * libgit2sharp - * libgit2net, low level bindings superseded by libgit2sharp * Node.js * node-gitteh * nodegit @@ -179,13 +202,19 @@ * Parrot Virtual Machine * parrot-libgit2 * Perl - * Git-Raw + * Git-Raw * PHP * php-git +* PowerShell + * GitPowerShell * Python * pygit2 +* R + * git2r * Ruby * Rugged +* Rust + * git2-rs * Vala * libgit2.vapi @@ -195,14 +224,16 @@ How Can I Contribute? ================================== -Check the [contribution guidelines](CONTRIBUTING.md). - +Check the [contribution guidelines](CONTRIBUTING.md) to understand our +workflow, the libgit2 [coding conventions](CONVENTIONS.md), and out list of +[good starting projects](PROJECTS.md). License ================================== -`libgit2` is under GPL2 **with linking exemption**. This means you -can link to and use the library from any program, proprietary or open source; paid -or gratis. However, you cannot modify libgit2 and distribute it without + +`libgit2` is under GPL2 **with linking exception**. This means you can link to +and use the library from any program, proprietary or open source; paid or +gratis. However, you cannot modify libgit2 and distribute it without supplying the source. -See the COPYING file for the full license text. +See the [COPYING file](COPYING) for the full license text. diff -Nru libgit2-0.20.0/script/cibuild.sh libgit2-0.22.2/script/cibuild.sh --- libgit2-0.20.0/script/cibuild.sh 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/script/cibuild.sh 2015-03-24 16:10:45.000000000 +0000 @@ -1,26 +1,42 @@ #!/bin/sh +if [ -n "$COVERITY" ]; +then + ./script/coverity.sh; + exit $?; +fi + # Create a test repo which we can use for the online::push tests -mkdir $HOME/_temp -git init --bare $HOME/_temp/test.git -git daemon --listen=localhost --export-all --enable=receive-pack --base-path=$HOME/_temp $HOME/_temp 2>/dev/null & +mkdir "$HOME"/_temp +git init --bare "$HOME"/_temp/test.git +git daemon --listen=localhost --export-all --enable=receive-pack --base-path="$HOME"/_temp "$HOME"/_temp 2>/dev/null & export GITTEST_REMOTE_URL="git://localhost/test.git" mkdir _build cd _build +# shellcheck disable=SC2086 cmake .. -DCMAKE_INSTALL_PREFIX=../_install $OPTIONS || exit $? -cmake --build . --target install || exit $? +make -j2 install || exit $? ctest -V . || exit $? # Now that we've tested the raw git protocol, let's set up ssh to we # can do the push tests over it killall git-daemon -sudo start ssh + +if [ "$TRAVIS_OS_NAME" = "osx" ]; then + echo 'PasswordAuthentication yes' | sudo tee -a /etc/sshd_config +else + sudo start ssh +fi + 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 +# Get the fingerprint for localhost and remove the colons so we can parse it as a hex number +export GITTEST_REMOTE_SSH_FINGERPRINT=$(ssh-keygen -F localhost -l | tail -n 1 | cut -d ' ' -f 2 | tr -d ':') + export GITTEST_REMOTE_URL="ssh://localhost/$HOME/_temp/test.git" export GITTEST_REMOTE_USER=$USER export GITTEST_REMOTE_SSH_KEY="$HOME/.ssh/id_rsa" @@ -28,5 +44,9 @@ export GITTEST_REMOTE_SSH_PASSPHRASE="" if [ -e ./libgit2_clar ]; then - ./libgit2_clar -sonline::push + ./libgit2_clar -sonline::push -sonline::clone::ssh_cert && + ./libgit2_clar -sonline::clone::ssh_with_paths + if [ "$TRAVIS_OS_NAME" = "linux" ]; then + ./libgit2_clar -sonline::clone::cred_callback + fi fi diff -Nru libgit2-0.20.0/script/coverity.sh libgit2-0.22.2/script/coverity.sh --- libgit2-0.20.0/script/coverity.sh 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/script/coverity.sh 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,57 @@ +#!/bin/bash +set -e + +# Environment check +[ -z "$COVERITY_TOKEN" ] && echo "Need to set a coverity token" && exit 1 + +# Only run this on our branches +echo "Pull request: $TRAVIS_PULL_REQUEST | Slug: $TRAVIS_REPO_SLUG" +if [ "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_REPO_SLUG" != "libgit2/libgit2" ]; +then + echo "Only analyzing 'development' on the main repo." + exit 0 +fi + +COV_VERSION=6.6.1 +case $(uname -m) in + i?86) BITS=32 ;; + amd64|x86_64) BITS=64 ;; +esac +SCAN_TOOL=https://scan.coverity.com/download/linux-${BITS} +TOOL_BASE=$(pwd)/_coverity-scan + +# Install coverity tools +if [ ! -d "$TOOL_BASE" ]; then + echo "Downloading coverity..." + mkdir -p "$TOOL_BASE" + pushd "$TOOL_BASE" + wget -O coverity_tool.tgz $SCAN_TOOL \ + --post-data "project=libgit2&token=$COVERITY_TOKEN" + tar xzf coverity_tool.tgz + popd + TOOL_DIR=$(find "$TOOL_BASE" -type d -name 'cov-analysis*') + ln -s "$TOOL_DIR" "$TOOL_BASE"/cov-analysis +fi + +COV_BUILD="$TOOL_BASE/cov-analysis/bin/cov-build" + +# Configure and build +rm -rf _build +mkdir _build +cd _build +cmake .. -DTHREADSAFE=ON +COVERITY_UNSUPPORTED=1 \ + $COV_BUILD --dir cov-int \ + cmake --build . + +# Upload results +tar czf libgit2.tgz cov-int +SHA=$(git rev-parse --short HEAD) +curl \ + --form project=libgit2 \ + --form token="$COVERITY_TOKEN" \ + --form email=bs@github.com \ + --form file=@libgit2.tgz \ + --form version="$SHA" \ + --form description="Travis build" \ + http://scan5.coverity.com/cgi-bin/upload.py diff -Nru libgit2-0.20.0/script/install-deps-linux.sh libgit2-0.22.2/script/install-deps-linux.sh --- libgit2-0.20.0/script/install-deps-linux.sh 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/script/install-deps-linux.sh 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,6 @@ +#!/bin/sh + +set -x + +sudo apt-get -qq update && +sudo apt-get -qq install cmake libssh2-1-dev openssh-client openssh-server diff -Nru libgit2-0.20.0/script/install-deps-osx.sh libgit2-0.22.2/script/install-deps-osx.sh --- libgit2-0.20.0/script/install-deps-osx.sh 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/script/install-deps-osx.sh 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,5 @@ +#!/bin/sh + +set -x + +brew install libssh2 cmake diff -Nru libgit2-0.20.0/src/amiga/map.c libgit2-0.22.2/src/amiga/map.c --- libgit2-0.20.0/src/amiga/map.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/amiga/map.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -/* - * 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 - -#ifndef GIT_WIN32 - -#include "posix.h" -#include "map.h" -#include - -int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset) -{ - GIT_MMAP_VALIDATE(out, len, prot, flags); - - out->data = NULL; - out->len = 0; - - if ((prot & GIT_PROT_WRITE) && ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)) { - giterr_set(GITERR_OS, "Trying to map shared-writeable"); - return -1; - } - - out->data = malloc(len); - GITERR_CHECK_ALLOC(out->data); - - if ((p_lseek(fd, offset, SEEK_SET) < 0) || ((size_t)p_read(fd, out->data, len) != len)) { - giterr_set(GITERR_OS, "mmap emulation failed"); - return -1; - } - - out->len = len; - return 0; -} - -int p_munmap(git_map *map) -{ - assert(map != NULL); - free(map->data); - - return 0; -} - -#endif - diff -Nru libgit2-0.20.0/src/annotated_commit.c libgit2-0.22.2/src/annotated_commit.c --- libgit2-0.20.0/src/annotated_commit.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/annotated_commit.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,121 @@ +/* + * 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 "annotated_commit.h" + +#include "git2/commit.h" +#include "git2/refs.h" +#include "git2/repository.h" +#include "git2/annotated_commit.h" + +static int annotated_commit_init( + git_annotated_commit **out, + git_repository *repo, + const git_oid *id, + const char *ref_name, + const char *remote_url) +{ + git_annotated_commit *annotated_commit; + int error = 0; + + assert(out && id); + + *out = NULL; + + annotated_commit = git__calloc(1, sizeof(git_annotated_commit)); + GITERR_CHECK_ALLOC(annotated_commit); + + if (ref_name) { + annotated_commit->ref_name = git__strdup(ref_name); + GITERR_CHECK_ALLOC(annotated_commit->ref_name); + } + + if (remote_url) { + annotated_commit->remote_url = git__strdup(remote_url); + GITERR_CHECK_ALLOC(annotated_commit->remote_url); + } + + git_oid_fmt(annotated_commit->id_str, id); + annotated_commit->id_str[GIT_OID_HEXSZ] = '\0'; + + if ((error = git_commit_lookup(&annotated_commit->commit, repo, id)) < 0) { + git_annotated_commit_free(annotated_commit); + return error; + } + + *out = annotated_commit; + return error; +} + +int git_annotated_commit_from_ref( + git_annotated_commit **out, + git_repository *repo, + const git_reference *ref) +{ + git_reference *resolved; + int error = 0; + + assert(out && repo && ref); + + *out = NULL; + + if ((error = git_reference_resolve(&resolved, ref)) < 0) + return error; + + error = annotated_commit_init(out, repo, git_reference_target(resolved), + git_reference_name(ref), NULL); + + git_reference_free(resolved); + return error; +} + +int git_annotated_commit_lookup( + git_annotated_commit **out, + git_repository *repo, + const git_oid *id) +{ + assert(out && repo && id); + + return annotated_commit_init(out, repo, id, NULL, NULL); +} + +int git_annotated_commit_from_fetchhead( + git_annotated_commit **out, + git_repository *repo, + const char *branch_name, + const char *remote_url, + const git_oid *id) +{ + assert(repo && id && branch_name && remote_url); + + return annotated_commit_init(out, repo, id, branch_name, remote_url); +} + +const git_oid *git_annotated_commit_id( + const git_annotated_commit *annotated_commit) +{ + assert(annotated_commit); + return git_commit_id(annotated_commit->commit); +} + +void git_annotated_commit_free(git_annotated_commit *annotated_commit) +{ + if (annotated_commit == NULL) + return; + + if (annotated_commit->commit != NULL) + git_commit_free(annotated_commit->commit); + + if (annotated_commit->ref_name != NULL) + git__free(annotated_commit->ref_name); + + if (annotated_commit->remote_url != NULL) + git__free(annotated_commit->remote_url); + + git__free(annotated_commit); +} diff -Nru libgit2-0.20.0/src/annotated_commit.h libgit2-0.22.2/src/annotated_commit.h --- libgit2-0.20.0/src/annotated_commit.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/annotated_commit.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,22 @@ +/* + * 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_annotated_commit_h__ +#define INCLUDE_annotated_commit_h__ + +#include "git2/oid.h" + +/** Internal structure for merge inputs */ +struct git_annotated_commit { + git_commit *commit; + + char *ref_name; + char *remote_url; + + char id_str[GIT_OID_HEXSZ+1]; +}; + +#endif diff -Nru libgit2-0.20.0/src/array.h libgit2-0.22.2/src/array.h --- libgit2-0.20.0/src/array.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/array.h 2015-03-24 16:10:45.000000000 +0000 @@ -7,7 +7,7 @@ #ifndef INCLUDE_array_h__ #define INCLUDE_array_h__ -#include "util.h" +#include "common.h" /* * Use this to declare a typesafe resizable array of items, a la: @@ -44,7 +44,7 @@ /* use a generic array for growth so this can return the new item */ GIT_INLINE(void *) git_array_grow(void *_a, size_t item_size) { - git_array_generic_t *a = _a; + volatile git_array_generic_t *a = _a; uint32_t new_size = (a->size < 8) ? 8 : a->asize * 3 / 2; char *new_array = git__realloc(a->ptr, new_size * item_size); if (!new_array) { diff -Nru libgit2-0.20.0/src/attr.c libgit2-0.22.2/src/attr.c --- libgit2-0.20.0/src/attr.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/attr.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,8 +1,8 @@ #include "common.h" #include "repository.h" -#include "fileops.h" +#include "sysdir.h" #include "config.h" -#include "attr.h" +#include "attr_file.h" #include "ignore.h" #include "git2/oid.h" #include @@ -33,6 +33,7 @@ const char *path, git_vector *files); +static void release_attr_files(git_vector *files); int git_attr_get( const char **value, @@ -49,6 +50,8 @@ git_attr_name attr; git_attr_rule *rule; + assert(value && repo && name); + *value = NULL; if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) @@ -57,6 +60,7 @@ if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0) goto cleanup; + memset(&attr, 0, sizeof(attr)); attr.name = name; attr.name_hash = git_attr_file__name_hash(name); @@ -74,7 +78,7 @@ } cleanup: - git_vector_free(&files); + release_attr_files(&files); git_attr_path__free(&path); return error; @@ -103,6 +107,11 @@ attr_get_many_info *info = NULL; size_t num_found = 0; + if (!num_attr) + return 0; + + assert(values && repo && names); + if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) return -1; @@ -145,7 +154,7 @@ } cleanup: - git_vector_free(&files); + release_attr_files(&files); git_attr_path__free(&path); git__free(info); @@ -169,15 +178,15 @@ git_attr_assignment *assign; git_strmap *seen = NULL; + assert(repo && callback); + if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0) return -1; - if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0) + if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0 || + (error = git_strmap_alloc(&seen)) < 0) goto cleanup; - seen = git_strmap_alloc(); - GITERR_CHECK_ALLOC(seen); - git_vector_foreach(&files, i, file) { git_attr_file__foreach_matching_rule(file, &path, j, rule) { @@ -193,8 +202,7 @@ error = callback(assign->name, assign->value, payload); if (error) { - giterr_clear(); - error = GIT_EUSER; + giterr_set_after_callback(error); goto cleanup; } } @@ -203,278 +211,114 @@ cleanup: git_strmap_free(seen); - git_vector_free(&files); + release_attr_files(&files); git_attr_path__free(&path); return error; } - -int git_attr_add_macro( +static int preload_attr_file( git_repository *repo, - const char *name, - const char *values) + git_attr_file_source source, + const char *base, + const char *file) { int error; - git_attr_rule *macro = NULL; - git_pool *pool; - - if (git_attr_cache__init(repo) < 0) - return -1; - - macro = git__calloc(1, sizeof(git_attr_rule)); - GITERR_CHECK_ALLOC(macro); - - pool = &git_repository_attr_cache(repo)->pool; - - macro->match.pattern = git_pool_strdup(pool, name); - GITERR_CHECK_ALLOC(macro->match.pattern); - - macro->match.length = strlen(macro->match.pattern); - macro->match.flags = GIT_ATTR_FNMATCH_MACRO; + git_attr_file *preload = NULL; - error = git_attr_assignment__parse(repo, pool, ¯o->assigns, &values); - - if (!error) - error = git_attr_cache__insert_macro(repo, macro); - - if (error < 0) - git_attr_rule__free(macro); + if (!file) + return 0; + if (!(error = git_attr_cache__get( + &preload, repo, source, base, file, git_attr_file__parse_buffer))) + git_attr_file__free(preload); return error; } -bool git_attr_cache__is_cached( - git_repository *repo, git_attr_file_source source, const char *path) +static int attr_setup(git_repository *repo) { - git_buf cache_key = GIT_BUF_INIT; - git_strmap *files = git_repository_attr_cache(repo)->files; + int error = 0; const char *workdir = git_repository_workdir(repo); - bool rval; - - if (workdir && git__prefixcmp(path, workdir) == 0) - path += strlen(workdir); - if (git_buf_printf(&cache_key, "%d#%s", (int)source, path) < 0) - return false; - - rval = git_strmap_exists(files, git_buf_cstr(&cache_key)); - - git_buf_free(&cache_key); - - return rval; -} + git_index *idx = NULL; + git_buf sys = GIT_BUF_INIT; -static int load_attr_file( - const char **data, - git_futils_filestamp *stamp, - const char *filename) -{ - int error; - git_buf content = GIT_BUF_INIT; - - error = git_futils_filestamp_check(stamp, filename); - if (error < 0) + if ((error = git_attr_cache__init(repo)) < 0) return error; - /* if error == 0, then file is up to date. By returning GIT_ENOTFOUND, - * we tell the caller not to reparse this file... + /* preload attribute files that could contain macros so the + * definitions will be available for later file parsing */ - if (!error) - return GIT_ENOTFOUND; - error = git_futils_readbuffer(&content, filename); + if (!(error = git_sysdir_find_system_file(&sys, GIT_ATTR_FILE_SYSTEM))) { + error = preload_attr_file( + repo, GIT_ATTR_FILE__FROM_FILE, NULL, sys.ptr); + git_buf_free(&sys); + } if (error < 0) { - /* convert error into ENOTFOUND so failed permissions / invalid - * file type don't actually stop the operation in progress. - */ - return GIT_ENOTFOUND; - - /* TODO: once warnings are available, issue a warning callback */ + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = 0; + } else + return error; } - *data = git_buf_detach(&content); - - return 0; -} - -static int load_attr_blob_from_index( - const char **content, - git_blob **blob, - git_repository *repo, - const git_oid *old_oid, - const char *relfile) -{ - int error; - size_t pos; - git_index *index; - const git_index_entry *entry; - - if ((error = git_repository_index__weakptr(&index, repo)) < 0 || - (error = git_index_find(&pos, index, relfile)) < 0) + if ((error = preload_attr_file( + repo, GIT_ATTR_FILE__FROM_FILE, + NULL, git_repository_attr_cache(repo)->cfg_attr_file)) < 0) return error; - entry = git_index_get_byindex(index, pos); - - if (old_oid && git_oid__cmp(old_oid, &entry->oid) == 0) - return GIT_ENOTFOUND; - - if ((error = git_blob_lookup(blob, repo, &entry->oid)) < 0) + if ((error = preload_attr_file( + repo, GIT_ATTR_FILE__FROM_FILE, + git_repository_path(repo), GIT_ATTR_FILE_INREPO)) < 0) return error; - *content = git_blob_rawcontent(*blob); - return 0; -} - -static int load_attr_from_cache( - git_attr_file **file, - git_attr_cache *cache, - git_attr_file_source source, - const char *relative_path) -{ - git_buf cache_key = GIT_BUF_INIT; - khiter_t cache_pos; - - *file = NULL; - - if (!cache || !cache->files) - return 0; - - if (git_buf_printf(&cache_key, "%d#%s", (int)source, relative_path) < 0) - return -1; - - cache_pos = git_strmap_lookup_index(cache->files, cache_key.ptr); - - git_buf_free(&cache_key); - - if (git_strmap_valid_index(cache->files, cache_pos)) - *file = git_strmap_value_at(cache->files, cache_pos); - - return 0; -} - -int git_attr_cache__internal_file( - git_repository *repo, - const char *filename, - git_attr_file **file) -{ - int error = 0; - git_attr_cache *cache = git_repository_attr_cache(repo); - khiter_t cache_pos = git_strmap_lookup_index(cache->files, filename); - - if (git_strmap_valid_index(cache->files, cache_pos)) { - *file = git_strmap_value_at(cache->files, cache_pos); - return 0; - } - - if (git_attr_file__new(file, 0, filename, &cache->pool) < 0) - return -1; + if (workdir != NULL && + (error = preload_attr_file( + repo, GIT_ATTR_FILE__FROM_FILE, workdir, GIT_ATTR_FILE)) < 0) + return error; - git_strmap_insert(cache->files, (*file)->key + 2, *file, error); - if (error > 0) - error = 0; + if ((error = git_repository_index__weakptr(&idx, repo)) < 0 || + (error = preload_attr_file( + repo, GIT_ATTR_FILE__FROM_INDEX, NULL, GIT_ATTR_FILE)) < 0) + return error; return error; } -int git_attr_cache__push_file( +int git_attr_add_macro( git_repository *repo, - const char *base, - const char *filename, - git_attr_file_source source, - git_attr_file_parser parse, - void* parsedata, - git_vector *stack) + const char *name, + const char *values) { - int error = 0; - git_buf path = GIT_BUF_INIT; - const char *workdir = git_repository_workdir(repo); - const char *relfile, *content = NULL; - git_attr_cache *cache = git_repository_attr_cache(repo); - git_attr_file *file = NULL; - git_blob *blob = NULL; - git_futils_filestamp stamp; - - assert(filename && stack); - - /* join base and path as needed */ - if (base != NULL && git_path_root(filename) < 0) { - if (git_buf_joinpath(&path, base, filename) < 0) - return -1; - filename = path.ptr; - } - - relfile = filename; - if (workdir && git__prefixcmp(relfile, workdir) == 0) - relfile += strlen(workdir); - - /* check cache */ - if (load_attr_from_cache(&file, cache, source, relfile) < 0) - return -1; - - /* if not in cache, load data, parse, and cache */ - - if (source == GIT_ATTR_FILE_FROM_FILE) { - git_futils_filestamp_set( - &stamp, file ? &file->cache_data.stamp : NULL); - - error = load_attr_file(&content, &stamp, filename); - } else { - error = load_attr_blob_from_index(&content, &blob, - repo, file ? &file->cache_data.oid : NULL, relfile); - } - - if (error) { - /* not finding a file is not an error for this function */ - if (error == GIT_ENOTFOUND) { - giterr_clear(); - error = 0; - } - goto finish; - } + int error; + git_attr_rule *macro = NULL; + git_pool *pool; - /* if we got here, we have to parse and/or reparse the file */ - if (file) - git_attr_file__clear_rules(file); - else { - error = git_attr_file__new(&file, source, relfile, &cache->pool); - if (error < 0) - goto finish; - } + if ((error = git_attr_cache__init(repo)) < 0) + return error; - if (parse && (error = parse(repo, parsedata, content, file)) < 0) - goto finish; + macro = git__calloc(1, sizeof(git_attr_rule)); + GITERR_CHECK_ALLOC(macro); - git_strmap_insert(cache->files, file->key, file, error); //-V595 - if (error > 0) - error = 0; + pool = &git_repository_attr_cache(repo)->pool; - /* remember "cache buster" file signature */ - if (blob) - git_oid_cpy(&file->cache_data.oid, git_object_id((git_object *)blob)); - else - git_futils_filestamp_set(&file->cache_data.stamp, &stamp); + macro->match.pattern = git_pool_strdup(pool, name); + GITERR_CHECK_ALLOC(macro->match.pattern); -finish: - /* push file onto vector if we found one*/ - if (!error && file != NULL) - error = git_vector_insert(stack, file); + macro->match.length = strlen(macro->match.pattern); + macro->match.flags = GIT_ATTR_FNMATCH_MACRO; - if (error != 0) - git_attr_file__free(file); + error = git_attr_assignment__parse(repo, pool, ¯o->assigns, &values); - if (blob) - git_blob_free(blob); - else - git__free((void *)content); + if (!error) + error = git_attr_cache__insert_macro(repo, macro); - git_buf_free(&path); + if (error < 0) + git_attr_rule__free(macro); return error; } -#define push_attr_file(R,S,B,F) \ - git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,git_attr_file__parse_buffer,NULL,(S)) - typedef struct { git_repository *repo; uint32_t flags; @@ -483,7 +327,7 @@ git_vector *files; } attr_walk_up_info; -int git_attr_cache__decide_sources( +static int attr_decide_sources( uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs) { int count = 0; @@ -491,56 +335,89 @@ switch (flags & 0x03) { case GIT_ATTR_CHECK_FILE_THEN_INDEX: if (has_wd) - srcs[count++] = GIT_ATTR_FILE_FROM_FILE; + srcs[count++] = GIT_ATTR_FILE__FROM_FILE; if (has_index) - srcs[count++] = GIT_ATTR_FILE_FROM_INDEX; + srcs[count++] = GIT_ATTR_FILE__FROM_INDEX; break; case GIT_ATTR_CHECK_INDEX_THEN_FILE: if (has_index) - srcs[count++] = GIT_ATTR_FILE_FROM_INDEX; + srcs[count++] = GIT_ATTR_FILE__FROM_INDEX; if (has_wd) - srcs[count++] = GIT_ATTR_FILE_FROM_FILE; + srcs[count++] = GIT_ATTR_FILE__FROM_FILE; break; case GIT_ATTR_CHECK_INDEX_ONLY: if (has_index) - srcs[count++] = GIT_ATTR_FILE_FROM_INDEX; + srcs[count++] = GIT_ATTR_FILE__FROM_INDEX; break; } return count; } -static int push_one_attr(void *ref, git_buf *path) +static int push_attr_file( + git_repository *repo, + git_vector *list, + git_attr_file_source source, + const char *base, + const char *filename) +{ + int error = 0; + git_attr_file *file = NULL; + + error = git_attr_cache__get( + &file, repo, source, base, filename, git_attr_file__parse_buffer); + if (error < 0) + return error; + + if (file != NULL) { + if ((error = git_vector_insert(list, file)) < 0) + git_attr_file__free(file); + } + + return error; +} + +static int push_one_attr(void *ref, const char *path) { int error = 0, n_src, i; attr_walk_up_info *info = (attr_walk_up_info *)ref; git_attr_file_source src[2]; - n_src = git_attr_cache__decide_sources( + n_src = attr_decide_sources( info->flags, info->workdir != NULL, info->index != NULL, src); for (i = 0; !error && i < n_src; ++i) - error = git_attr_cache__push_file( - info->repo, path->ptr, GIT_ATTR_FILE, src[i], - git_attr_file__parse_buffer, NULL, info->files); + error = push_attr_file( + info->repo, info->files, src[i], path, GIT_ATTR_FILE); return error; } +static void release_attr_files(git_vector *files) +{ + size_t i; + git_attr_file *file; + + git_vector_foreach(files, i, file) { + git_attr_file__free(file); + files->contents[i] = NULL; + } + git_vector_free(files); +} + static int collect_attr_files( git_repository *repo, uint32_t flags, const char *path, git_vector *files) { - int error; + int error = 0; git_buf dir = GIT_BUF_INIT; const char *workdir = git_repository_workdir(repo); - attr_walk_up_info info; + attr_walk_up_info info = { NULL }; - if (git_attr_cache__init(repo) < 0 || - git_vector_init(files, 4, NULL) < 0) - return -1; + if ((error = attr_setup(repo)) < 0) + return error; /* Resolve path in a non-bare repo */ if (workdir != NULL) @@ -558,7 +435,8 @@ */ error = push_attr_file( - repo, files, git_repository_path(repo), GIT_ATTR_FILE_INREPO); + repo, files, GIT_ATTR_FILE__FROM_FILE, + git_repository_path(repo), GIT_ATTR_FILE_INREPO); if (error < 0) goto cleanup; @@ -569,21 +447,27 @@ giterr_clear(); /* no error even if there is no index */ info.files = files; - error = git_path_walk_up(&dir, workdir, push_one_attr, &info); + if (!strcmp(dir.ptr, ".")) + error = push_one_attr(&info, ""); + else + error = git_path_walk_up(&dir, workdir, push_one_attr, &info); + if (error < 0) goto cleanup; if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) { error = push_attr_file( - repo, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file); + repo, files, GIT_ATTR_FILE__FROM_FILE, + NULL, git_repository_attr_cache(repo)->cfg_attr_file); if (error < 0) goto cleanup; } if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) { - error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM); + error = git_sysdir_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM); if (!error) - error = push_attr_file(repo, files, NULL, dir.ptr); + error = push_attr_file( + repo, files, GIT_ATTR_FILE__FROM_FILE, NULL, dir.ptr); else if (error == GIT_ENOTFOUND) { giterr_clear(); error = 0; @@ -592,153 +476,8 @@ cleanup: if (error < 0) - git_vector_free(files); + release_attr_files(files); git_buf_free(&dir); return error; } - -static int attr_cache__lookup_path( - char **out, git_config *cfg, const char *key, const char *fallback) -{ - git_buf buf = GIT_BUF_INIT; - int error; - const char *cfgval = NULL; - - *out = NULL; - - if (!(error = git_config_get_string(&cfgval, cfg, key))) { - - /* expand leading ~/ as needed */ - if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' && - !git_futils_find_global_file(&buf, &cfgval[2])) - *out = git_buf_detach(&buf); - else if (cfgval) - *out = git__strdup(cfgval); - - } else if (error == GIT_ENOTFOUND) { - giterr_clear(); - error = 0; - - if (!git_futils_find_xdg_file(&buf, fallback)) - *out = git_buf_detach(&buf); - } - - git_buf_free(&buf); - - return error; -} - -int git_attr_cache__init(git_repository *repo) -{ - int ret; - git_attr_cache *cache = git_repository_attr_cache(repo); - git_config *cfg; - - if (cache->initialized) - return 0; - - /* cache config settings for attributes and ignores */ - if (git_repository_config__weakptr(&cfg, repo) < 0) - return -1; - - ret = attr_cache__lookup_path( - &cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG, GIT_ATTR_FILE_XDG); - if (ret < 0) - return ret; - - ret = attr_cache__lookup_path( - &cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG, GIT_IGNORE_FILE_XDG); - if (ret < 0) - return ret; - - /* allocate hashtable for attribute and ignore file contents */ - if (cache->files == NULL) { - cache->files = git_strmap_alloc(); - GITERR_CHECK_ALLOC(cache->files); - } - - /* allocate hashtable for attribute macros */ - if (cache->macros == NULL) { - cache->macros = git_strmap_alloc(); - GITERR_CHECK_ALLOC(cache->macros); - } - - /* allocate string pool */ - if (git_pool_init(&cache->pool, 1, 0) < 0) - return -1; - - cache->initialized = 1; - - /* insert default macros */ - return git_attr_add_macro(repo, "binary", "-diff -crlf -text"); -} - -void git_attr_cache_flush( - git_repository *repo) -{ - git_attr_cache *cache; - - if (!repo) - return; - - cache = git_repository_attr_cache(repo); - - if (cache->files != NULL) { - git_attr_file *file; - - git_strmap_foreach_value(cache->files, file, { - git_attr_file__free(file); - }); - - git_strmap_free(cache->files); - } - - if (cache->macros != NULL) { - git_attr_rule *rule; - - git_strmap_foreach_value(cache->macros, rule, { - git_attr_rule__free(rule); - }); - - git_strmap_free(cache->macros); - } - - git_pool_clear(&cache->pool); - - git__free(cache->cfg_attr_file); - cache->cfg_attr_file = NULL; - - git__free(cache->cfg_excl_file); - cache->cfg_excl_file = NULL; - - cache->initialized = 0; -} - -int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro) -{ - git_strmap *macros = git_repository_attr_cache(repo)->macros; - int error; - - /* TODO: generate warning log if (macro->assigns.length == 0) */ - if (macro->assigns.length == 0) - return 0; - - git_strmap_insert(macros, macro->match.pattern, macro, error); - return (error < 0) ? -1 : 0; -} - -git_attr_rule *git_attr_cache__lookup_macro( - git_repository *repo, const char *name) -{ - git_strmap *macros = git_repository_attr_cache(repo)->macros; - khiter_t pos; - - pos = git_strmap_lookup_index(macros, name); - - if (!git_strmap_valid_index(macros, pos)) - return NULL; - - return (git_attr_rule *)git_strmap_value_at(macros, pos); -} - diff -Nru libgit2-0.20.0/src/attrcache.c libgit2-0.22.2/src/attrcache.c --- libgit2-0.20.0/src/attrcache.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/attrcache.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,449 @@ +#include "common.h" +#include "repository.h" +#include "attr_file.h" +#include "config.h" +#include "sysdir.h" +#include "ignore.h" + +GIT__USE_STRMAP; + +GIT_INLINE(int) attr_cache_lock(git_attr_cache *cache) +{ + GIT_UNUSED(cache); /* avoid warning if threading is off */ + + if (git_mutex_lock(&cache->lock) < 0) { + giterr_set(GITERR_OS, "Unable to get attr cache lock"); + return -1; + } + return 0; +} + +GIT_INLINE(void) attr_cache_unlock(git_attr_cache *cache) +{ + GIT_UNUSED(cache); /* avoid warning if threading is off */ + git_mutex_unlock(&cache->lock); +} + +GIT_INLINE(git_attr_file_entry *) attr_cache_lookup_entry( + git_attr_cache *cache, const char *path) +{ + khiter_t pos = git_strmap_lookup_index(cache->files, path); + + if (git_strmap_valid_index(cache->files, pos)) + return git_strmap_value_at(cache->files, pos); + else + return NULL; +} + +int git_attr_cache__alloc_file_entry( + git_attr_file_entry **out, + const char *base, + const char *path, + git_pool *pool) +{ + size_t baselen = 0, pathlen = strlen(path); + size_t cachesize = sizeof(git_attr_file_entry) + pathlen + 1; + git_attr_file_entry *ce; + + if (base != NULL && git_path_root(path) < 0) { + baselen = strlen(base); + cachesize += baselen; + + if (baselen && base[baselen - 1] != '/') + cachesize++; + } + + ce = git_pool_mallocz(pool, (uint32_t)cachesize); + GITERR_CHECK_ALLOC(ce); + + if (baselen) { + memcpy(ce->fullpath, base, baselen); + + if (base[baselen - 1] != '/') + ce->fullpath[baselen++] = '/'; + } + memcpy(&ce->fullpath[baselen], path, pathlen); + + ce->path = &ce->fullpath[baselen]; + *out = ce; + + return 0; +} + +/* call with attrcache locked */ +static int attr_cache_make_entry( + git_attr_file_entry **out, git_repository *repo, const char *path) +{ + int error = 0; + git_attr_cache *cache = git_repository_attr_cache(repo); + git_attr_file_entry *entry = NULL; + + error = git_attr_cache__alloc_file_entry( + &entry, git_repository_workdir(repo), path, &cache->pool); + + if (!error) { + git_strmap_insert(cache->files, entry->path, entry, error); + if (error > 0) + error = 0; + } + + *out = entry; + return error; +} + +/* insert entry or replace existing if we raced with another thread */ +static int attr_cache_upsert(git_attr_cache *cache, git_attr_file *file) +{ + git_attr_file_entry *entry; + git_attr_file *old; + + if (attr_cache_lock(cache) < 0) + return -1; + + entry = attr_cache_lookup_entry(cache, file->entry->path); + + GIT_REFCOUNT_OWN(file, entry); + GIT_REFCOUNT_INC(file); + + old = git__compare_and_swap( + &entry->file[file->source], entry->file[file->source], file); + + if (old) { + GIT_REFCOUNT_OWN(old, NULL); + git_attr_file__free(old); + } + + attr_cache_unlock(cache); + return 0; +} + +static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file) +{ + int error = 0; + git_attr_file_entry *entry; + + if (!file) + return 0; + if ((error = attr_cache_lock(cache)) < 0) + return error; + + if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL) + file = git__compare_and_swap(&entry->file[file->source], file, NULL); + + attr_cache_unlock(cache); + + if (file) { + GIT_REFCOUNT_OWN(file, NULL); + git_attr_file__free(file); + } + + return error; +} + +/* Look up cache entry and file. + * - If entry is not present, create it while the cache is locked. + * - If file is present, increment refcount before returning it, so the + * cache can be unlocked and it won't go away. + */ +static int attr_cache_lookup( + git_attr_file **out_file, + git_attr_file_entry **out_entry, + git_repository *repo, + git_attr_file_source source, + const char *base, + const char *filename) +{ + int error = 0; + git_buf path = GIT_BUF_INIT; + const char *wd = git_repository_workdir(repo), *relfile; + git_attr_cache *cache = git_repository_attr_cache(repo); + git_attr_file_entry *entry = NULL; + git_attr_file *file = NULL; + + /* join base and path as needed */ + if (base != NULL && git_path_root(filename) < 0) { + if (git_buf_joinpath(&path, base, filename) < 0) + return -1; + filename = path.ptr; + } + + relfile = filename; + if (wd && !git__prefixcmp(relfile, wd)) + relfile += strlen(wd); + + /* check cache for existing entry */ + if ((error = attr_cache_lock(cache)) < 0) + goto cleanup; + + entry = attr_cache_lookup_entry(cache, relfile); + if (!entry) + error = attr_cache_make_entry(&entry, repo, relfile); + else if (entry->file[source] != NULL) { + file = entry->file[source]; + GIT_REFCOUNT_INC(file); + } + + attr_cache_unlock(cache); + +cleanup: + *out_file = file; + *out_entry = entry; + + git_buf_free(&path); + return error; +} + +int git_attr_cache__get( + git_attr_file **out, + git_repository *repo, + git_attr_file_source source, + const char *base, + const char *filename, + git_attr_file_parser parser) +{ + int error = 0; + git_attr_cache *cache = git_repository_attr_cache(repo); + git_attr_file_entry *entry = NULL; + git_attr_file *file = NULL, *updated = NULL; + + if ((error = attr_cache_lookup( + &file, &entry, repo, source, base, filename)) < 0) + return error; + + /* load file if we don't have one or if existing one is out of date */ + if (!file || (error = git_attr_file__out_of_date(repo, file)) > 0) + error = git_attr_file__load(&updated, repo, entry, source, parser); + + /* if we loaded the file, insert into and/or update cache */ + if (updated) { + if ((error = attr_cache_upsert(cache, updated)) < 0) + git_attr_file__free(updated); + else { + git_attr_file__free(file); /* offset incref from lookup */ + file = updated; + } + } + + /* if file could not be loaded */ + if (error < 0) { + /* remove existing entry */ + if (file) { + attr_cache_remove(cache, file); + git_attr_file__free(file); /* offset incref from lookup */ + file = NULL; + } + /* no error if file simply doesn't exist */ + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = 0; + } + } + + *out = file; + return error; +} + +bool git_attr_cache__is_cached( + git_repository *repo, + git_attr_file_source source, + const char *filename) +{ + git_attr_cache *cache = git_repository_attr_cache(repo); + git_strmap *files; + khiter_t pos; + git_attr_file_entry *entry; + + if (!cache || !(files = cache->files)) + return false; + + pos = git_strmap_lookup_index(files, filename); + if (!git_strmap_valid_index(files, pos)) + return false; + + entry = git_strmap_value_at(files, pos); + + return entry && (entry->file[source] != NULL); +} + + +static int attr_cache__lookup_path( + char **out, git_config *cfg, const char *key, const char *fallback) +{ + git_buf buf = GIT_BUF_INIT; + int error; + const git_config_entry *entry = NULL; + + *out = NULL; + + if ((error = git_config__lookup_entry(&entry, cfg, key, false)) < 0) + return error; + + if (entry) { + const char *cfgval = entry->value; + + /* expand leading ~/ as needed */ + if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' && + !git_sysdir_find_global_file(&buf, &cfgval[2])) + *out = git_buf_detach(&buf); + else if (cfgval) + *out = git__strdup(cfgval); + } + else if (!git_sysdir_find_xdg_file(&buf, fallback)) + *out = git_buf_detach(&buf); + + git_buf_free(&buf); + + return error; +} + +static void attr_cache__free(git_attr_cache *cache) +{ + bool unlock; + + if (!cache) + return; + + unlock = (git_mutex_lock(&cache->lock) == 0); + + if (cache->files != NULL) { + git_attr_file_entry *entry; + git_attr_file *file; + int i; + + git_strmap_foreach_value(cache->files, entry, { + for (i = 0; i < GIT_ATTR_FILE_NUM_SOURCES; ++i) { + if ((file = git__swap(entry->file[i], NULL)) != NULL) { + GIT_REFCOUNT_OWN(file, NULL); + git_attr_file__free(file); + } + } + }); + git_strmap_free(cache->files); + } + + if (cache->macros != NULL) { + git_attr_rule *rule; + + git_strmap_foreach_value(cache->macros, rule, { + git_attr_rule__free(rule); + }); + git_strmap_free(cache->macros); + } + + git_pool_clear(&cache->pool); + + git__free(cache->cfg_attr_file); + cache->cfg_attr_file = NULL; + + git__free(cache->cfg_excl_file); + cache->cfg_excl_file = NULL; + + if (unlock) + git_mutex_unlock(&cache->lock); + git_mutex_free(&cache->lock); + + git__free(cache); +} + +int git_attr_cache__do_init(git_repository *repo) +{ + int ret = 0; + git_attr_cache *cache = git_repository_attr_cache(repo); + git_config *cfg = NULL; + + if (cache) + return 0; + + cache = git__calloc(1, sizeof(git_attr_cache)); + GITERR_CHECK_ALLOC(cache); + + /* set up lock */ + if (git_mutex_init(&cache->lock) < 0) { + giterr_set(GITERR_OS, "Unable to initialize lock for attr cache"); + git__free(cache); + return -1; + } + + if ((ret = git_repository_config_snapshot(&cfg, repo)) < 0) + goto cancel; + + /* cache config settings for attributes and ignores */ + ret = attr_cache__lookup_path( + &cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG, GIT_ATTR_FILE_XDG); + if (ret < 0) + goto cancel; + + ret = attr_cache__lookup_path( + &cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG, GIT_IGNORE_FILE_XDG); + if (ret < 0) + goto cancel; + + /* allocate hashtable for attribute and ignore file contents, + * hashtable for attribute macros, and string pool + */ + if ((ret = git_strmap_alloc(&cache->files)) < 0 || + (ret = git_strmap_alloc(&cache->macros)) < 0 || + (ret = git_pool_init(&cache->pool, 1, 0)) < 0) + goto cancel; + + cache = git__compare_and_swap(&repo->attrcache, NULL, cache); + if (cache) + goto cancel; /* raced with another thread, free this but no error */ + + git_config_free(cfg); + + /* insert default macros */ + return git_attr_add_macro(repo, "binary", "-diff -crlf -text"); + +cancel: + attr_cache__free(cache); + git_config_free(cfg); + return ret; +} + +void git_attr_cache_flush(git_repository *repo) +{ + git_attr_cache *cache; + + /* this could be done less expensively, but for now, we'll just free + * the entire attrcache and let the next use reinitialize it... + */ + if (repo && (cache = git__swap(repo->attrcache, NULL)) != NULL) + attr_cache__free(cache); +} + +int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro) +{ + git_attr_cache *cache = git_repository_attr_cache(repo); + git_strmap *macros = cache->macros; + int error; + + /* TODO: generate warning log if (macro->assigns.length == 0) */ + if (macro->assigns.length == 0) + return 0; + + if (git_mutex_lock(&cache->lock) < 0) { + giterr_set(GITERR_OS, "Unable to get attr cache lock"); + error = -1; + } else { + git_strmap_insert(macros, macro->match.pattern, macro, error); + git_mutex_unlock(&cache->lock); + } + + return (error < 0) ? -1 : 0; +} + +git_attr_rule *git_attr_cache__lookup_macro( + git_repository *repo, const char *name) +{ + git_strmap *macros = git_repository_attr_cache(repo)->macros; + khiter_t pos; + + pos = git_strmap_lookup_index(macros, name); + + if (!git_strmap_valid_index(macros, pos)) + return NULL; + + return (git_attr_rule *)git_strmap_value_at(macros, pos); +} + diff -Nru libgit2-0.20.0/src/attrcache.h libgit2-0.22.2/src/attrcache.h --- libgit2-0.20.0/src/attrcache.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/attrcache.h 2015-03-24 16:10:45.000000000 +0000 @@ -7,18 +7,50 @@ #ifndef INCLUDE_attrcache_h__ #define INCLUDE_attrcache_h__ -#include "pool.h" +#include "attr_file.h" #include "strmap.h" +#define GIT_ATTR_CONFIG "core.attributesfile" +#define GIT_IGNORE_CONFIG "core.excludesfile" + typedef struct { - int initialized; - git_pool pool; - git_strmap *files; /* hash path to git_attr_file of rules */ - git_strmap *macros; /* hash name to vector */ char *cfg_attr_file; /* cached value of core.attributesfile */ char *cfg_excl_file; /* cached value of core.excludesfile */ + git_strmap *files; /* hash path to git_attr_cache_entry records */ + git_strmap *macros; /* hash name to vector */ + git_mutex lock; + git_pool pool; } git_attr_cache; -extern int git_attr_cache__init(git_repository *repo); +extern int git_attr_cache__do_init(git_repository *repo); + +#define git_attr_cache__init(REPO) \ + (git_repository_attr_cache(REPO) ? 0 : git_attr_cache__do_init(REPO)) + +/* get file - loading and reload as needed */ +extern int git_attr_cache__get( + git_attr_file **file, + git_repository *repo, + git_attr_file_source source, + const char *base, + const char *filename, + git_attr_file_parser parser); + +extern bool git_attr_cache__is_cached( + git_repository *repo, + git_attr_file_source source, + const char *path); + +extern int git_attr_cache__alloc_file_entry( + git_attr_file_entry **out, + const char *base, + const char *path, + git_pool *pool); + +extern int git_attr_cache__insert_macro( + git_repository *repo, git_attr_rule *macro); + +extern git_attr_rule *git_attr_cache__lookup_macro( + git_repository *repo, const char *name); #endif diff -Nru libgit2-0.20.0/src/attr_file.c libgit2-0.22.2/src/attr_file.c --- libgit2-0.20.0/src/attr_file.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/attr_file.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,103 +1,253 @@ #include "common.h" #include "repository.h" #include "filebuf.h" -#include "attr.h" +#include "attr_file.h" +#include "attrcache.h" #include "git2/blob.h" #include "git2/tree.h" +#include "index.h" #include -static int sort_by_hash_and_name(const void *a_raw, const void *b_raw); -static void git_attr_rule__clear(git_attr_rule *rule); -static bool parse_optimized_patterns( - git_attr_fnmatch *spec, - git_pool *pool, - const char *pattern); +static void attr_file_free(git_attr_file *file) +{ + bool unlock = !git_mutex_lock(&file->lock); + git_attr_file__clear_rules(file, false); + git_pool_clear(&file->pool); + if (unlock) + git_mutex_unlock(&file->lock); + git_mutex_free(&file->lock); + + git__memzero(file, sizeof(*file)); + git__free(file); +} int git_attr_file__new( - git_attr_file **attrs_ptr, - git_attr_file_source from, - const char *path, - git_pool *pool) + git_attr_file **out, + git_attr_file_entry *entry, + git_attr_file_source source) { - git_attr_file *attrs = NULL; - - attrs = git__calloc(1, sizeof(git_attr_file)); + git_attr_file *attrs = git__calloc(1, sizeof(git_attr_file)); GITERR_CHECK_ALLOC(attrs); - if (pool) - attrs->pool = pool; - else { - attrs->pool = git__calloc(1, sizeof(git_pool)); - if (!attrs->pool || git_pool_init(attrs->pool, 1, 0) < 0) - goto fail; - attrs->pool_is_allocated = true; + if (git_mutex_init(&attrs->lock) < 0) { + giterr_set(GITERR_OS, "Failed to initialize lock"); + git__free(attrs); + return -1; } - if (path) { - size_t len = strlen(path); + if (git_pool_init(&attrs->pool, 1, 0) < 0) { + attr_file_free(attrs); + return -1; + } - attrs->key = git_pool_malloc(attrs->pool, (uint32_t)len + 3); - GITERR_CHECK_ALLOC(attrs->key); + GIT_REFCOUNT_INC(attrs); + attrs->entry = entry; + attrs->source = source; + *out = attrs; + return 0; +} - attrs->key[0] = '0' + (char)from; - attrs->key[1] = '#'; - memcpy(&attrs->key[2], path, len); - attrs->key[len + 2] = '\0'; +int git_attr_file__clear_rules(git_attr_file *file, bool need_lock) +{ + unsigned int i; + git_attr_rule *rule; + + if (need_lock && git_mutex_lock(&file->lock) < 0) { + giterr_set(GITERR_OS, "Failed to lock attribute file"); + return -1; } - if (git_vector_init(&attrs->rules, 4, NULL) < 0) - goto fail; + git_vector_foreach(&file->rules, i, rule) + git_attr_rule__free(rule); + git_vector_free(&file->rules); + + if (need_lock) + git_mutex_unlock(&file->lock); - *attrs_ptr = attrs; return 0; +} -fail: - git_attr_file__free(attrs); - attrs_ptr = NULL; - return -1; +void git_attr_file__free(git_attr_file *file) +{ + if (!file) + return; + GIT_REFCOUNT_DEC(file, attr_file_free); } -int git_attr_file__parse_buffer( - git_repository *repo, void *parsedata, const char *buffer, git_attr_file *attrs) +static int attr_file_oid_from_index( + git_oid *oid, git_repository *repo, const char *path) +{ + int error; + git_index *idx; + size_t pos; + const git_index_entry *entry; + + if ((error = git_repository_index__weakptr(&idx, repo)) < 0 || + (error = git_index__find_pos(&pos, idx, path, 0, 0)) < 0) + return error; + + if (!(entry = git_index_get_byindex(idx, pos))) + return GIT_ENOTFOUND; + + *oid = entry->id; + return 0; +} + +int git_attr_file__load( + git_attr_file **out, + git_repository *repo, + git_attr_file_entry *entry, + git_attr_file_source source, + git_attr_file_parser parser) { int error = 0; - const char *scan = NULL; - char *context = NULL; - git_attr_rule *rule = NULL; + git_blob *blob = NULL; + git_buf content = GIT_BUF_INIT; + git_attr_file *file; + struct stat st; + + *out = NULL; + + switch (source) { + case GIT_ATTR_FILE__IN_MEMORY: + /* in-memory attribute file doesn't need data */ + break; + case GIT_ATTR_FILE__FROM_INDEX: { + git_oid id; + + if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 || + (error = git_blob_lookup(&blob, repo, &id)) < 0) + return error; + + /* Do not assume that data straight from the ODB is NULL-terminated; + * copy the contents of a file to a buffer to work on */ + git_buf_put(&content, git_blob_rawcontent(blob), git_blob_rawsize(blob)); + break; + } + case GIT_ATTR_FILE__FROM_FILE: { + int fd; + + if (p_stat(entry->fullpath, &st) < 0) + return git_path_set_error(errno, entry->fullpath, "stat"); + if (S_ISDIR(st.st_mode)) + return GIT_ENOTFOUND; + + /* For open or read errors, return ENOTFOUND to skip item */ + /* TODO: issue warning when warning API is available */ + + if ((fd = git_futils_open_ro(entry->fullpath)) < 0) + return GIT_ENOTFOUND; + + error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size); + p_close(fd); + + if (error < 0) + return GIT_ENOTFOUND; - GIT_UNUSED(parsedata); + break; + } + default: + giterr_set(GITERR_INVALID, "Unknown file source %d", source); + return -1; + } + + if ((error = git_attr_file__new(&file, entry, source)) < 0) + goto cleanup; + + if (parser && (error = parser(repo, file, git_buf_cstr(&content))) < 0) { + git_attr_file__free(file); + goto cleanup; + } - assert(buffer && attrs); + /* write cache breaker */ + if (source == GIT_ATTR_FILE__FROM_INDEX) + git_oid_cpy(&file->cache_data.oid, git_blob_id(blob)); + else if (source == GIT_ATTR_FILE__FROM_FILE) + git_futils_filestamp_set_from_stat(&file->cache_data.stamp, &st); + /* else always cacheable */ + + *out = file; + +cleanup: + git_blob_free(blob); + git_buf_free(&content); + + return error; +} + +int git_attr_file__out_of_date(git_repository *repo, git_attr_file *file) +{ + if (!file) + return 1; + + switch (file->source) { + case GIT_ATTR_FILE__IN_MEMORY: + return 0; + + case GIT_ATTR_FILE__FROM_FILE: + return git_futils_filestamp_check( + &file->cache_data.stamp, file->entry->fullpath); + + case GIT_ATTR_FILE__FROM_INDEX: { + int error; + git_oid id; + + if ((error = attr_file_oid_from_index( + &id, repo, file->entry->path)) < 0) + return error; + + return (git_oid__cmp(&file->cache_data.oid, &id) != 0); + } + + default: + giterr_set(GITERR_INVALID, "Invalid file type %d", file->source); + return -1; + } +} + +static int sort_by_hash_and_name(const void *a_raw, const void *b_raw); +static void git_attr_rule__clear(git_attr_rule *rule); +static bool parse_optimized_patterns( + git_attr_fnmatch *spec, + git_pool *pool, + const char *pattern); - scan = buffer; +int git_attr_file__parse_buffer( + git_repository *repo, git_attr_file *attrs, const char *data) +{ + int error = 0; + const char *scan = data, *context = NULL; + git_attr_rule *rule = NULL; /* if subdir file path, convert context for file paths */ - if (attrs->key && git__suffixcmp(attrs->key, "/" GIT_ATTR_FILE) == 0) { - context = attrs->key + 2; - context[strlen(context) - strlen(GIT_ATTR_FILE)] = '\0'; + if (attrs->entry && + git_path_root(attrs->entry->path) < 0 && + !git__suffixcmp(attrs->entry->path, "/" GIT_ATTR_FILE)) + context = attrs->entry->path; + + if (git_mutex_lock(&attrs->lock) < 0) { + giterr_set(GITERR_OS, "Failed to lock attribute file"); + return -1; } while (!error && *scan) { /* allocate rule if needed */ - if (!rule) { - if (!(rule = git__calloc(1, sizeof(git_attr_rule)))) { - error = -1; - break; - } - rule->match.flags = GIT_ATTR_FNMATCH_ALLOWNEG | - GIT_ATTR_FNMATCH_ALLOWMACRO; + if (!rule && !(rule = git__calloc(1, sizeof(*rule)))) { + error = -1; + break; } + rule->match.flags = + GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO; + /* parse the next "pattern attr attr attr" line */ if (!(error = git_attr_fnmatch__parse( - &rule->match, attrs->pool, context, &scan)) && + &rule->match, &attrs->pool, context, &scan)) && !(error = git_attr_assignment__parse( - repo, attrs->pool, &rule->assigns, &scan))) + repo, &attrs->pool, &rule->assigns, &scan))) { if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO) - /* should generate error/warning if this is coming from any - * file other than .gitattributes at repo root. - */ + /* TODO: warning if macro found in file below repo root */ error = git_attr_cache__insert_macro(repo, rule); else error = git_vector_insert(&attrs->rules, rule); @@ -113,66 +263,12 @@ } } + git_mutex_unlock(&attrs->lock); git_attr_rule__free(rule); - /* restore file path used for context */ - if (context) - context[strlen(context)] = '.'; /* first char of GIT_ATTR_FILE */ - - return error; -} - -int git_attr_file__new_and_load( - git_attr_file **attrs_ptr, - const char *path) -{ - int error; - git_buf content = GIT_BUF_INIT; - - if ((error = git_attr_file__new(attrs_ptr, 0, path, NULL)) < 0) - return error; - - if (!(error = git_futils_readbuffer(&content, path))) - error = git_attr_file__parse_buffer( - NULL, NULL, git_buf_cstr(&content), *attrs_ptr); - - git_buf_free(&content); - - if (error) { - git_attr_file__free(*attrs_ptr); - *attrs_ptr = NULL; - } - return error; } -void git_attr_file__clear_rules(git_attr_file *file) -{ - unsigned int i; - git_attr_rule *rule; - - git_vector_foreach(&file->rules, i, rule) - git_attr_rule__free(rule); - - git_vector_free(&file->rules); -} - -void git_attr_file__free(git_attr_file *file) -{ - if (!file) - return; - - git_attr_file__clear_rules(file); - - if (file->pool_is_allocated) { - git_pool_clear(file->pool); - git__free(file->pool); - } - file->pool = NULL; - - git__free(file); -} - uint32_t git_attr_file__name_hash(const char *name) { uint32_t h = 5381; @@ -183,10 +279,9 @@ return h; } - int git_attr_file__lookup_one( git_attr_file *file, - const git_attr_path *path, + git_attr_path *path, const char *attr, const char **value) { @@ -212,30 +307,110 @@ return 0; } +int git_attr_file__load_standalone(git_attr_file **out, const char *path) +{ + int error; + git_attr_file *file; + git_buf content = GIT_BUF_INIT; + + error = git_attr_file__new(&file, NULL, GIT_ATTR_FILE__FROM_FILE); + if (error < 0) + return error; + + error = git_attr_cache__alloc_file_entry( + &file->entry, NULL, path, &file->pool); + if (error < 0) { + git_attr_file__free(file); + return error; + } + /* because the cache entry is allocated from the file's own pool, we + * don't have to free it - freeing file+pool will free cache entry, too. + */ + + if (!(error = git_futils_readbuffer(&content, path))) { + error = git_attr_file__parse_buffer(NULL, file, content.ptr); + git_buf_free(&content); + } + + if (error < 0) + git_attr_file__free(file); + else + *out = file; + + return error; +} bool git_attr_fnmatch__match( git_attr_fnmatch *match, - const git_attr_path *path) + git_attr_path *path) { - int fnm; - int icase_flags = (match->flags & GIT_ATTR_FNMATCH_ICASE) ? FNM_CASEFOLD : 0; + const char *filename; + int flags = 0; - if (match->flags & GIT_ATTR_FNMATCH_DIRECTORY && !path->is_dir) - return false; + /* + * If the rule was generated in a subdirectory, we must only + * use it for paths inside that directory. We can thus return + * a non-match if the prefixes don't match. + */ + if (match->containing_dir) { + if (match->flags & GIT_ATTR_FNMATCH_ICASE) { + if (git__strncasecmp(path->path, match->containing_dir, match->containing_dir_length)) + return 0; + } else { + if (git__prefixcmp(path->path, match->containing_dir)) + return 0; + } + } - if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) - fnm = p_fnmatch(match->pattern, path->path, FNM_PATHNAME | icase_flags); - else if (path->is_dir) - fnm = p_fnmatch(match->pattern, path->basename, FNM_LEADING_DIR | icase_flags); - else - fnm = p_fnmatch(match->pattern, path->basename, icase_flags); + if (match->flags & GIT_ATTR_FNMATCH_ICASE) + flags |= FNM_CASEFOLD; + if (match->flags & GIT_ATTR_FNMATCH_LEADINGDIR) + flags |= FNM_LEADING_DIR; + + if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) { + filename = path->path; + flags |= FNM_PATHNAME; + } else { + filename = path->basename; - return (fnm == FNM_NOMATCH) ? false : true; + if (path->is_dir) + flags |= FNM_LEADING_DIR; + } + + if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir) { + int matchval; + + /* for attribute checks or root ignore checks, fail match */ + if (!(match->flags & GIT_ATTR_FNMATCH_IGNORE) || + path->basename == path->path) + return false; + + /* for ignore checks, use container of current item for check */ + path->basename[-1] = '\0'; + flags |= FNM_LEADING_DIR; + matchval = p_fnmatch(match->pattern, path->path, flags); + path->basename[-1] = '/'; + return (matchval != FNM_NOMATCH); + } + + /* if path is a directory prefix of a negated pattern, then match */ + if ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) && path->is_dir) { + size_t pathlen = strlen(path->path); + bool prefixed = (pathlen <= match->length) && + ((match->flags & GIT_ATTR_FNMATCH_ICASE) ? + !strncasecmp(match->pattern, path->path, pathlen) : + !strncmp(match->pattern, path->path, pathlen)); + + if (prefixed && git_path_at_end_of_segment(&match->pattern[pathlen])) + return true; + } + + return (p_fnmatch(match->pattern, filename, flags) != FNM_NOMATCH); } bool git_attr_rule__match( git_attr_rule *rule, - const git_attr_path *path) + git_attr_path *path) { bool matched = git_attr_fnmatch__match(&rule->match, path); @@ -245,7 +420,6 @@ return matched; } - git_attr_assignment *git_attr_rule__lookup_assignment( git_attr_rule *rule, const char *name) { @@ -344,7 +518,7 @@ int git_attr_fnmatch__parse( git_attr_fnmatch *spec, git_pool *pool, - const char *source, + const char *context, const char **base) { const char *pattern, *scan; @@ -375,7 +549,8 @@ } if (*pattern == '!' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWNEG) != 0) { - spec->flags = spec->flags | GIT_ATTR_FNMATCH_NEGATIVE; + spec->flags = spec->flags | + GIT_ATTR_FNMATCH_NEGATIVE | GIT_ATTR_FNMATCH_LEADINGDIR; pattern++; } @@ -383,7 +558,7 @@ for (scan = pattern; *scan != '\0'; ++scan) { /* scan until (non-escaped) white space */ if (git__isspace(*scan) && *(scan - 1) != '\\') { - if (!allow_space || (*scan != ' ' && *scan != '\t')) + if (!allow_space || (*scan != ' ' && *scan != '\t' && *scan != '\r')) break; } @@ -404,26 +579,57 @@ if ((spec->length = scan - pattern) == 0) return GIT_ENOTFOUND; + /* + * Remove one trailing \r in case this is a CRLF delimited + * file, in the case of Icon\r\r\n, we still leave the first + * \r there to match against. + */ + if (pattern[spec->length - 1] == '\r') + if (--spec->length == 0) + return GIT_ENOTFOUND; + if (pattern[spec->length - 1] == '/') { spec->length--; spec->flags = spec->flags | GIT_ATTR_FNMATCH_DIRECTORY; if (--slash_count <= 0) spec->flags = spec->flags & ~GIT_ATTR_FNMATCH_FULLPATH; } + if ((spec->flags & GIT_ATTR_FNMATCH_NOLEADINGDIR) == 0 && + spec->length >= 2 && + pattern[spec->length - 1] == '*' && + pattern[spec->length - 2] == '/') { + spec->length -= 2; + spec->flags = spec->flags | GIT_ATTR_FNMATCH_LEADINGDIR; + /* leave FULLPATH match on, however */ + } + + if (context) { + char *slash = strchr(context, '/'); + size_t len; + if (slash) { + /* include the slash for easier matching */ + len = slash - context + 1; + spec->containing_dir = git_pool_strndup(pool, context, len); + spec->containing_dir_length = len; + } + } if ((spec->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0 && - source != NULL && git_path_root(pattern) < 0) + context != NULL && git_path_root(pattern) < 0) { - size_t sourcelen = strlen(source); + /* use context path minus the trailing filename */ + char *slash = strrchr(context, '/'); + size_t contextlen = slash ? slash - context + 1 : 0; + /* given an unrooted fullpath match from a file inside a repo, * prefix the pattern with the relative directory of the source file */ spec->pattern = git_pool_malloc( - pool, (uint32_t)(sourcelen + spec->length + 1)); + pool, (uint32_t)(contextlen + spec->length + 1)); if (spec->pattern) { - memcpy(spec->pattern, source, sourcelen); - memcpy(spec->pattern + sourcelen, pattern, spec->length); - spec->length += sourcelen; + memcpy(spec->pattern, context, contextlen); + memcpy(spec->pattern + contextlen, pattern, spec->length); + spec->length += contextlen; spec->pattern[spec->length] = '\0'; } } else { @@ -436,6 +642,7 @@ } else { /* strip '\' that might have be used for internal whitespace */ spec->length = git__unescape(spec->pattern); + /* TODO: convert remaining '\' into '/' for POSIX ??? */ } return 0; @@ -574,8 +781,10 @@ error = git_vector_insert_sorted( assigns, massign, &merge_assignments); - if (error < 0 && error != GIT_EEXISTS) + if (error < 0 && error != GIT_EEXISTS) { + git_attr_assignment__free(assign); return error; + } } } } diff -Nru libgit2-0.20.0/src/attr_file.h libgit2-0.22.2/src/attr_file.h --- libgit2-0.20.0/src/attr_file.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/attr_file.h 2015-03-24 16:10:45.000000000 +0000 @@ -30,10 +30,20 @@ #define GIT_ATTR_FNMATCH_MATCH_ALL (1U << 8) #define GIT_ATTR_FNMATCH_ALLOWNEG (1U << 9) #define GIT_ATTR_FNMATCH_ALLOWMACRO (1U << 10) +#define GIT_ATTR_FNMATCH_LEADINGDIR (1U << 11) +#define GIT_ATTR_FNMATCH_NOLEADINGDIR (1U << 12) #define GIT_ATTR_FNMATCH__INCOMING \ - (GIT_ATTR_FNMATCH_ALLOWSPACE | \ - GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO) + (GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG | \ + GIT_ATTR_FNMATCH_ALLOWMACRO | GIT_ATTR_FNMATCH_NOLEADINGDIR) + +typedef enum { + GIT_ATTR_FILE__IN_MEMORY = 0, + GIT_ATTR_FILE__FROM_FILE = 1, + GIT_ATTR_FILE__FROM_INDEX = 2, + + GIT_ATTR_FILE_NUM_SOURCES = 3 +} git_attr_file_source; extern const char *git_attr__true; extern const char *git_attr__false; @@ -42,6 +52,8 @@ typedef struct { char *pattern; size_t length; + char *containing_dir; + size_t containing_dir_length; unsigned int flags; } git_attr_fnmatch; @@ -63,17 +75,32 @@ const char *value; } git_attr_assignment; +typedef struct git_attr_file_entry git_attr_file_entry; + typedef struct { - char *key; /* cache "source#path" this was loaded from */ - git_vector rules; /* vector of or */ - git_pool *pool; - bool pool_is_allocated; + git_refcount rc; + git_mutex lock; + git_attr_file_entry *entry; + git_attr_file_source source; + git_vector rules; /* vector of or */ + git_pool pool; union { git_oid oid; git_futils_filestamp stamp; } cache_data; } git_attr_file; +struct git_attr_file_entry { + git_attr_file *file[GIT_ATTR_FILE_NUM_SOURCES]; + const char *path; /* points into fullpath */ + char fullpath[GIT_FLEX_ARRAY]; +}; + +typedef int (*git_attr_file_parser)( + git_repository *repo, + git_attr_file *file, + const char *data); + typedef struct { git_buf full; char *path; @@ -81,31 +108,39 @@ int is_dir; } git_attr_path; -typedef enum { - GIT_ATTR_FILE_FROM_FILE = 0, - GIT_ATTR_FILE_FROM_INDEX = 1 -} git_attr_file_source; - /* * git_attr_file API */ -extern int git_attr_file__new( - git_attr_file **attrs_ptr, git_attr_file_source src, const char *path, git_pool *pool); +int git_attr_file__new( + git_attr_file **out, + git_attr_file_entry *entry, + git_attr_file_source source); + +void git_attr_file__free(git_attr_file *file); + +int git_attr_file__load( + git_attr_file **out, + git_repository *repo, + git_attr_file_entry *ce, + git_attr_file_source source, + git_attr_file_parser parser); -extern int git_attr_file__new_and_load( - git_attr_file **attrs_ptr, const char *path); +int git_attr_file__load_standalone( + git_attr_file **out, const char *path); -extern void git_attr_file__free(git_attr_file *file); +int git_attr_file__out_of_date( + git_repository *repo, git_attr_file *file); -extern void git_attr_file__clear_rules(git_attr_file *file); +int git_attr_file__parse_buffer( + git_repository *repo, git_attr_file *attrs, const char *data); -extern int git_attr_file__parse_buffer( - git_repository *repo, void *parsedata, const char *buf, git_attr_file *file); +int git_attr_file__clear_rules( + git_attr_file *file, bool need_lock); -extern int git_attr_file__lookup_one( +int git_attr_file__lookup_one( git_attr_file *file, - const git_attr_path *path, + git_attr_path *path, const char *attr, const char **value); @@ -114,7 +149,7 @@ git_vector_rforeach(&(file)->rules, (iter), (rule)) \ if (git_attr_rule__match((rule), (path))) -extern uint32_t git_attr_file__name_hash(const char *name); +uint32_t git_attr_file__name_hash(const char *name); /* @@ -129,13 +164,13 @@ extern bool git_attr_fnmatch__match( git_attr_fnmatch *rule, - const git_attr_path *path); + git_attr_path *path); extern void git_attr_rule__free(git_attr_rule *rule); extern bool git_attr_rule__match( git_attr_rule *rule, - const git_attr_path *path); + git_attr_path *path); extern git_attr_assignment *git_attr_rule__lookup_assignment( git_attr_rule *rule, const char *name); diff -Nru libgit2-0.20.0/src/attr.h libgit2-0.22.2/src/attr.h --- libgit2-0.20.0/src/attr.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/attr.h 2015-03-24 16:10:45.000000000 +0000 @@ -8,38 +8,6 @@ #define INCLUDE_attr_h__ #include "attr_file.h" - -#define GIT_ATTR_CONFIG "core.attributesfile" -#define GIT_IGNORE_CONFIG "core.excludesfile" - -typedef int (*git_attr_file_parser)( - git_repository *, void *, const char *, git_attr_file *); - -extern int git_attr_cache__insert_macro( - git_repository *repo, git_attr_rule *macro); - -extern git_attr_rule *git_attr_cache__lookup_macro( - git_repository *repo, const char *name); - -extern int git_attr_cache__push_file( - git_repository *repo, - const char *base, - const char *filename, - git_attr_file_source source, - git_attr_file_parser parse, - void *parsedata, /* passed through to parse function */ - git_vector *stack); - -extern int git_attr_cache__internal_file( - git_repository *repo, - const char *key, - git_attr_file **file_ptr); - -/* returns true if path is in cache */ -extern bool git_attr_cache__is_cached( - git_repository *repo, git_attr_file_source source, const char *path); - -extern int git_attr_cache__decide_sources( - uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs); +#include "attrcache.h" #endif diff -Nru libgit2-0.20.0/src/bitvec.h libgit2-0.22.2/src/bitvec.h --- libgit2-0.20.0/src/bitvec.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/bitvec.h 2015-03-24 16:10:45.000000000 +0000 @@ -7,7 +7,7 @@ #ifndef INCLUDE_bitvec_h__ #define INCLUDE_bitvec_h__ -#include "util.h" +#include "common.h" /* * This is a silly little fixed length bit vector type that will store diff -Nru libgit2-0.20.0/src/blame.c libgit2-0.22.2/src/blame.c --- libgit2-0.20.0/src/blame.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/blame.c 2015-03-24 16:10:45.000000000 +0000 @@ -20,12 +20,15 @@ static int hunk_byfinalline_search_cmp(const void *key, const void *entry) { - uint16_t lineno = (uint16_t)*(size_t*)key; git_blame_hunk *hunk = (git_blame_hunk*)entry; - if (lineno < hunk->final_start_line_number) + size_t lineno = *(size_t*)key; + size_t lines_in_hunk = (size_t)hunk->lines_in_hunk; + size_t final_start_line_number = (size_t)hunk->final_start_line_number; + + if (lineno < final_start_line_number) return -1; - if (lineno >= hunk->final_start_line_number + hunk->lines_in_hunk) + if (lineno >= final_start_line_number + lines_in_hunk) return 1; return 0; } @@ -76,8 +79,8 @@ git_oid_cpy(&newhunk->orig_commit_id, &hunk->orig_commit_id); git_oid_cpy(&newhunk->final_commit_id, &hunk->final_commit_id); newhunk->boundary = hunk->boundary; - newhunk->final_signature = git_signature_dup(hunk->final_signature); - newhunk->orig_signature = git_signature_dup(hunk->orig_signature); + git_signature_dup(&newhunk->final_signature, hunk->final_signature); + git_signature_dup(&newhunk->orig_signature, hunk->orig_signature); return newhunk; } @@ -95,7 +98,7 @@ { size_t i; - if (!git_vector_bsearch2( &i, v, hunk_byfinalline_search_cmp, &start_line)) { + if (!git_vector_bsearch2(&i, v, hunk_byfinalline_search_cmp, &start_line)) { for (; i < v->length; i++) { git_blame_hunk *hunk = (git_blame_hunk*)v->contents[i]; hunk->final_start_line_number += shift_by; @@ -108,17 +111,22 @@ git_blame_options opts, const char *path) { - git_blame *gbr = (git_blame*)git__calloc(1, sizeof(git_blame)); - if (!gbr) { - giterr_set_oom(); + git_blame *gbr = git__calloc(1, sizeof(git_blame)); + if (!gbr) return NULL; - } - git_vector_init(&gbr->hunks, 8, hunk_cmp); - git_vector_init(&gbr->paths, 8, paths_cmp); + gbr->repository = repo; gbr->options = opts; - gbr->path = git__strdup(path); - git_vector_insert(&gbr->paths, git__strdup(path)); + + if (git_vector_init(&gbr->hunks, 8, hunk_cmp) < 0 || + git_vector_init(&gbr->paths, 8, paths_cmp) < 0 || + (gbr->path = git__strdup(path)) == NULL || + git_vector_insert(&gbr->paths, git__strdup(path)) < 0) + { + git_blame_free(gbr); + return NULL; + } + return gbr; } @@ -126,7 +134,6 @@ { size_t i; git_blame_hunk *hunk; - char *path; if (!blame) return; @@ -134,13 +141,11 @@ free_hunk(hunk); git_vector_free(&blame->hunks); - git_vector_foreach(&blame->paths, i, path) - git__free(path); - git_vector_free(&blame->paths); + git_vector_free_deep(&blame->paths); git_array_clear(blame->line_index); - git__free((void*)blame->path); + git__free(blame->path); git_blob_free(blame->final_blob); git__free(blame); } @@ -159,10 +164,10 @@ const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, uint32_t lineno) { - size_t i; + size_t i, new_lineno = (size_t)lineno; assert(blame); - if (!git_vector_bsearch2( &i, &blame->hunks, hunk_byfinalline_search_cmp, &lineno)) { + if (!git_vector_bsearch2(&i, &blame->hunks, hunk_byfinalline_search_cmp, &new_lineno)) { return git_blame_get_hunk_byindex(blame, (uint32_t)i); } @@ -239,7 +244,7 @@ git_off_t len = blame->final_buf_size; int num = 0, incomplete = 0, bol = 1; size_t *i; - + if (len && buf[len-1] != '\n') incomplete++; /* incomplete line at the end */ while (len--) { @@ -260,13 +265,15 @@ blame->num_lines = num + incomplete; return blame->num_lines; } - + static git_blame_hunk* hunk_from_entry(git_blame__entry *e) { git_blame_hunk *h = new_hunk( e->lno+1, e->num_lines, e->s_lno+1, e->suspect->path); git_oid_cpy(&h->final_commit_id, git_commit_id(e->suspect->commit)); - h->final_signature = git_signature_dup(git_commit_author(e->suspect->commit)); + git_oid_cpy(&h->orig_commit_id, git_commit_id(e->suspect->commit)); + git_signature_dup(&h->final_signature, git_commit_author(e->suspect->commit)); + git_signature_dup(&h->orig_signature, git_commit_author(e->suspect->commit)); h->boundary = e->is_boundary ? 1 : 0; return h; } @@ -282,8 +289,6 @@ goto cleanup; error = git_object_lookup_bypath((git_object**)&blame->final_blob, (git_object*)blame->final, blame->path, GIT_OBJ_BLOB); - if (error < 0) - goto cleanup; cleanup: return error; @@ -311,7 +316,6 @@ ent->suspect = o; blame->ent = ent; - blame->path = blame->path; git_blame__like_git(blame, blame->options.flags); @@ -448,7 +452,7 @@ git_blame **out, git_blame *reference, const char *buffer, - uint32_t buffer_len) + size_t buffer_len) { git_blame *blame; git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; @@ -474,3 +478,10 @@ *out = blame; return 0; } + +int git_blame_init_options(git_blame_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_blame_options, GIT_BLAME_OPTIONS_INIT); + return 0; +} diff -Nru libgit2-0.20.0/src/blame_git.c libgit2-0.22.2/src/blame_git.c --- libgit2-0.20.0/src/blame_git.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/blame_git.c 2015-03-24 16:10:45.000000000 +0000 @@ -485,12 +485,14 @@ git_blame__origin *sg_buf[16]; git_blame__origin *porigin, **sg_origin = sg_buf; - GIT_UNUSED(opt); - num_parents = git_commit_parentcount(commit); if (!git_oid_cmp(git_commit_id(commit), &blame->options.oldest_commit)) /* Stop at oldest specified commit */ num_parents = 0; + else if (opt & GIT_BLAME_FIRST_PARENT && num_parents > 1) + /* Limit search to the first parent */ + num_parents = 1; + if (!num_parents) { git_oid_cpy(&blame->options.oldest_commit, git_commit_id(commit)); goto finish; diff -Nru libgit2-0.20.0/src/blame.h libgit2-0.22.2/src/blame.h --- libgit2-0.20.0/src/blame.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/blame.h 2015-03-24 16:10:45.000000000 +0000 @@ -64,7 +64,7 @@ } git_blame__entry; struct git_blame { - const char *path; + char *path; git_repository *repository; git_blame_options options; diff -Nru libgit2-0.20.0/src/blob.c libgit2-0.22.2/src/blob.c --- libgit2-0.20.0/src/blob.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/blob.c 2015-03-24 16:10:45.000000000 +0000 @@ -50,25 +50,28 @@ return 0; } -int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len) +int git_blob_create_frombuffer( + git_oid *id, git_repository *repo, const void *buffer, size_t len) { int error; git_odb *odb; git_odb_stream *stream; + assert(id && repo); + if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 || (error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < 0) return error; if ((error = git_odb_stream_write(stream, buffer, len)) == 0) - error = git_odb_stream_finalize_write(oid, stream); + error = git_odb_stream_finalize_write(id, stream); git_odb_stream_free(stream); return error; } static int write_file_stream( - git_oid *oid, git_odb *odb, const char *path, git_off_t file_size) + git_oid *id, git_odb *odb, const char *path, git_off_t file_size) { int fd, error; char buffer[4096]; @@ -97,14 +100,14 @@ } if (!error) - error = git_odb_stream_finalize_write(oid, stream); + error = git_odb_stream_finalize_write(id, stream); git_odb_stream_free(stream); return error; } static int write_file_filtered( - git_oid *oid, + git_oid *id, git_off_t *size, git_odb *odb, const char *full_path, @@ -119,7 +122,7 @@ if (!error) { *size = tgt.size; - error = git_odb_write(oid, odb, tgt.ptr, tgt.size, GIT_OBJ_BLOB); + error = git_odb_write(id, odb, tgt.ptr, tgt.size, GIT_OBJ_BLOB); } git_buf_free(&tgt); @@ -127,7 +130,7 @@ } static int write_symlink( - git_oid *oid, git_odb *odb, const char *path, size_t link_size) + git_oid *id, git_odb *odb, const char *path, size_t link_size) { char *link_data; ssize_t read_len; @@ -143,13 +146,13 @@ return -1; } - error = git_odb_write(oid, odb, (void *)link_data, link_size, GIT_OBJ_BLOB); + error = git_odb_write(id, odb, (void *)link_data, link_size, GIT_OBJ_BLOB); git__free(link_data); return error; } int git_blob__create_from_paths( - git_oid *oid, + git_oid *id, struct stat *out_st, git_repository *repo, const char *content_path, @@ -188,24 +191,25 @@ mode = hint_mode ? hint_mode : st.st_mode; if (S_ISLNK(mode)) { - error = write_symlink(oid, odb, content_path, (size_t)size); + error = write_symlink(id, odb, content_path, (size_t)size); } else { git_filter_list *fl = NULL; if (try_load_filters) /* Load the filters for writing this file to the ODB */ error = git_filter_list_load( - &fl, repo, NULL, hint_path, GIT_FILTER_TO_ODB); + &fl, repo, NULL, hint_path, + GIT_FILTER_TO_ODB, GIT_FILTER_OPT_DEFAULT); if (error < 0) /* well, that didn't work */; else if (fl == NULL) /* No filters need to be applied to the document: we can stream * directly from disk */ - error = write_file_stream(oid, odb, content_path, size); + error = write_file_stream(id, odb, content_path, size); else { /* We need to apply one or more filters */ - error = write_file_filtered(oid, &size, odb, content_path, fl); + error = write_file_filtered(id, &size, odb, content_path, fl); git_filter_list_free(fl); } @@ -233,13 +237,13 @@ } int git_blob_create_fromworkdir( - git_oid *oid, git_repository *repo, const char *path) + git_oid *id, git_repository *repo, const char *path) { - return git_blob__create_from_paths(oid, NULL, repo, NULL, path, 0, true); + return git_blob__create_from_paths(id, NULL, repo, NULL, path, 0, true); } int git_blob_create_fromdisk( - git_oid *oid, git_repository *repo, const char *path) + git_oid *id, git_repository *repo, const char *path) { int error; git_buf full_path = GIT_BUF_INIT; @@ -257,7 +261,7 @@ hintpath += strlen(workdir); error = git_blob__create_from_paths( - oid, NULL, repo, git_buf_cstr(&full_path), hintpath, 0, true); + id, NULL, repo, git_buf_cstr(&full_path), hintpath, 0, true); git_buf_free(&full_path); return error; @@ -266,63 +270,72 @@ #define BUFFER_SIZE 4096 int git_blob_create_fromchunks( - git_oid *oid, + git_oid *id, git_repository *repo, const char *hintpath, int (*source_cb)(char *content, size_t max_length, void *payload), void *payload) { - int error = -1, read_bytes; + int error; char *content = NULL; git_filebuf file = GIT_FILEBUF_INIT; git_buf path = GIT_BUF_INIT; - if (git_buf_joinpath( - &path, git_repository_path(repo), GIT_OBJECTS_DIR "streamed") < 0) + assert(id && repo && source_cb); + + if ((error = git_buf_joinpath( + &path, git_repository_path(repo), GIT_OBJECTS_DIR "streamed")) < 0) goto cleanup; content = git__malloc(BUFFER_SIZE); GITERR_CHECK_ALLOC(content); - if (git_filebuf_open(&file, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY, 0666) < 0) + if ((error = git_filebuf_open( + &file, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY, 0666)) < 0) goto cleanup; while (1) { - read_bytes = source_cb(content, BUFFER_SIZE, payload); + int read_bytes = source_cb(content, BUFFER_SIZE, payload); - assert(read_bytes <= BUFFER_SIZE); - - if (read_bytes <= 0) + if (!read_bytes) break; - if (git_filebuf_write(&file, content, read_bytes) < 0) + if (read_bytes > BUFFER_SIZE) { + giterr_set(GITERR_OBJECT, "Invalid chunk size while creating blob"); + error = GIT_EBUFS; + } else if (read_bytes < 0) { + error = giterr_set_after_callback(read_bytes); + } else { + error = git_filebuf_write(&file, content, read_bytes); + } + + if (error < 0) goto cleanup; } - if (read_bytes < 0) - goto cleanup; - - if (git_filebuf_flush(&file) < 0) + if ((error = git_filebuf_flush(&file)) < 0) goto cleanup; error = git_blob__create_from_paths( - oid, NULL, repo, file.path_lock, hintpath, 0, hintpath != NULL); + id, NULL, repo, file.path_lock, hintpath, 0, hintpath != NULL); cleanup: git_buf_free(&path); git_filebuf_cleanup(&file); git__free(content); + return error; } -int git_blob_is_binary(git_blob *blob) +int git_blob_is_binary(const git_blob *blob) { git_buf content; assert(blob); content.ptr = blob->odb_object->buffer; - content.size = min(blob->odb_object->cached.size, 4000); + content.size = + min(blob->odb_object->cached.size, GIT_FILTER_BYTES_TO_CHECK_NUL); content.asize = 0; return git_buf_text_is_binary(&content); @@ -339,11 +352,14 @@ assert(blob && path && out); + git_buf_sanitize(out); + if (check_for_binary_data && git_blob_is_binary(blob)) return 0; if (!(error = git_filter_list_load( - &fl, git_blob_owner(blob), blob, path, GIT_FILTER_TO_WORKTREE))) { + &fl, git_blob_owner(blob), blob, path, + GIT_FILTER_TO_WORKTREE, GIT_FILTER_OPT_DEFAULT))) { error = git_filter_list_apply_to_blob(out, fl, blob); diff -Nru libgit2-0.20.0/src/branch.c libgit2-0.22.2/src/branch.c --- libgit2-0.20.0/src/branch.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/branch.c 2015-03-24 16:10:45.000000000 +0000 @@ -21,27 +21,22 @@ const char *branch_name, int is_remote) { - git_reference *branch; - int error = -1; + git_reference *branch = NULL; + int error = 0; char *prefix; git_buf ref_name = GIT_BUF_INIT; - *branch_reference_out = NULL; - prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR; - if (git_buf_joinpath(&ref_name, prefix, branch_name) < 0) - goto cleanup; - - if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0) { - giterr_set(GITERR_REFERENCE, - "Cannot locate %s branch '%s'.", is_remote ? "remote-tracking" : "local", branch_name); - goto cleanup; - } + if ((error = git_buf_joinpath(&ref_name, prefix, branch_name)) < 0) + /* OOM */; + else if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0) + giterr_set( + GITERR_REFERENCE, "Cannot locate %s branch '%s'", + is_remote ? "remote-tracking" : "local", branch_name); - *branch_reference_out = branch; + *branch_reference_out = branch; /* will be NULL on error */ -cleanup: git_buf_free(&ref_name); return error; } @@ -59,26 +54,53 @@ git_repository *repository, const char *branch_name, const git_commit *commit, - int force) + int force, + const git_signature *signature, + const char *log_message) { + int is_head = 0; git_reference *branch = NULL; - git_buf canonical_branch_name = GIT_BUF_INIT; + git_buf canonical_branch_name = GIT_BUF_INIT, + log_message_buf = GIT_BUF_INIT; int error = -1; assert(branch_name && commit && ref_out); assert(git_object_owner((const git_object *)commit) == repository); + if (force && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) { + error = git_branch_is_head(branch); + git_reference_free(branch); + branch = NULL; + + if (error < 0) + goto cleanup; + + is_head = error; + } + + if (is_head && force) { + giterr_set(GITERR_REFERENCE, "Cannot force update branch '%s' as it is " + "the current HEAD of the repository.", branch_name); + error = -1; + goto cleanup; + } + if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0) goto cleanup; + if (git_buf_sets(&log_message_buf, log_message ? log_message : "Branch: created") < 0) + goto cleanup; + error = git_reference_create(&branch, repository, - git_buf_cstr(&canonical_branch_name), git_commit_id(commit), force); + git_buf_cstr(&canonical_branch_name), git_commit_id(commit), force, signature, + git_buf_cstr(&log_message_buf)); if (!error) *ref_out = branch; cleanup: git_buf_free(&canonical_branch_name); + git_buf_free(&log_message_buf); return error; } @@ -90,33 +112,40 @@ assert(branch); - if (!git_reference_is_branch(branch) && - !git_reference_is_remote(branch)) { - giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.", git_reference_name(branch)); - return -1; + if (!git_reference_is_branch(branch) && !git_reference_is_remote(branch)) { + giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.", + git_reference_name(branch)); + return GIT_ENOTFOUND; } if ((is_head = git_branch_is_head(branch)) < 0) return is_head; if (is_head) { - giterr_set(GITERR_REFERENCE, - "Cannot delete branch '%s' as it is the current HEAD of the repository.", git_reference_name(branch)); + giterr_set(GITERR_REFERENCE, "Cannot delete branch '%s' as it is " + "the current HEAD of the repository.", git_reference_name(branch)); return -1; } - if (git_buf_printf(&config_section, "branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0) + if (git_buf_join(&config_section, '.', "branch", + git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0) goto on_error; if (git_config_rename_section( - git_reference_owner(branch), - git_buf_cstr(&config_section), - NULL) < 0) - goto on_error; + git_reference_owner(branch), git_buf_cstr(&config_section), NULL) < 0) + goto on_error; if (git_reference_delete(branch) < 0) goto on_error; + if ((error = git_reflog_delete(git_reference_owner(branch), git_reference_name(branch))) < 0) { + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = 0; + } + goto on_error; + } + error = 0; on_error: @@ -182,6 +211,9 @@ { branch_iter *iter = (branch_iter *) _iter; + if (iter == NULL) + return; + git_reference_iterator_free(iter->iter); git__free(iter); } @@ -190,11 +222,14 @@ git_reference **out, git_reference *branch, const char *new_branch_name, - int force) + int force, + const git_signature *signature, + const char *log_message) { git_buf new_reference_name = GIT_BUF_INIT, - old_config_section = GIT_BUF_INIT, - new_config_section = GIT_BUF_INIT; + old_config_section = GIT_BUF_INIT, + new_config_section = GIT_BUF_INIT, + log_message_buf = GIT_BUF_INIT; int error; assert(branch && new_branch_name); @@ -202,26 +237,40 @@ if (!git_reference_is_branch(branch)) return not_a_local_branch(git_reference_name(branch)); - error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name); - if (error < 0) + if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0) goto done; - git_buf_printf(&old_config_section, - "branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)); + if (log_message) { + if ((error = git_buf_sets(&log_message_buf, log_message)) < 0) + goto done; + } else { + if ((error = git_buf_printf(&log_message_buf, "Branch: renamed %s to %s", + git_reference_name(branch), git_buf_cstr(&new_reference_name))) < 0) + goto done; + } - git_buf_printf(&new_config_section, "branch.%s", new_branch_name); + /* first update ref then config so failure won't trash config */ - if ((error = git_config_rename_section(git_reference_owner(branch), - git_buf_cstr(&old_config_section), - git_buf_cstr(&new_config_section))) < 0) + error = git_reference_rename( + out, branch, git_buf_cstr(&new_reference_name), force, + signature, git_buf_cstr(&log_message_buf)); + if (error < 0) goto done; - error = git_reference_rename(out, branch, git_buf_cstr(&new_reference_name), force); + git_buf_join(&old_config_section, '.', "branch", + git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)); + git_buf_join(&new_config_section, '.', "branch", new_branch_name); + + error = git_config_rename_section( + git_reference_owner(branch), + git_buf_cstr(&old_config_section), + git_buf_cstr(&new_config_section)); done: git_buf_free(&new_reference_name); git_buf_free(&old_config_section); git_buf_free(&new_config_section); + git_buf_free(&log_message_buf); return error; } @@ -237,7 +286,9 @@ return retrieve_branch_reference(ref_out, repo, branch_name, branch_type == GIT_BRANCH_REMOTE); } -int git_branch_name(const char **out, git_reference *ref) +int git_branch_name( + const char **out, + const git_reference *ref) { const char *branch_name; @@ -260,17 +311,13 @@ static int retrieve_upstream_configuration( const char **out, - git_repository *repo, + const git_config *config, const char *canonical_branch_name, const char *format) { - git_config *config; git_buf buf = GIT_BUF_INIT; int error; - if (git_repository_config__weakptr(&config, repo) < 0) - return -1; - if (git_buf_printf(&buf, format, canonical_branch_name + strlen(GIT_REFS_HEADS_DIR)) < 0) return -1; @@ -280,39 +327,45 @@ return error; } -int git_branch_upstream__name( - git_buf *tracking_name, +int git_branch_upstream_name( + git_buf *out, git_repository *repo, - const char *canonical_branch_name) + const char *refname) { const char *remote_name, *merge_name; git_buf buf = GIT_BUF_INIT; int error = -1; git_remote *remote = NULL; const git_refspec *refspec; + git_config *config; - assert(tracking_name && canonical_branch_name); + assert(out && refname); - if (!git_reference__is_branch(canonical_branch_name)) - return not_a_local_branch(canonical_branch_name); + git_buf_sanitize(out); + + if (!git_reference__is_branch(refname)) + return not_a_local_branch(refname); + + if ((error = git_repository_config_snapshot(&config, repo)) < 0) + return error; if ((error = retrieve_upstream_configuration( - &remote_name, repo, canonical_branch_name, "branch.%s.remote")) < 0) + &remote_name, config, refname, "branch.%s.remote")) < 0) goto cleanup; if ((error = retrieve_upstream_configuration( - &merge_name, repo, canonical_branch_name, "branch.%s.merge")) < 0) + &merge_name, config, refname, "branch.%s.merge")) < 0) goto cleanup; if (!*remote_name || !*merge_name) { giterr_set(GITERR_REFERENCE, - "branch '%s' does not have an upstream", canonical_branch_name); + "branch '%s' does not have an upstream", refname); error = GIT_ENOTFOUND; goto cleanup; } if (strcmp(".", remote_name) != 0) { - if ((error = git_remote_load(&remote, repo, remote_name)) < 0) + if ((error = git_remote_lookup(&remote, repo, remote_name)) < 0) goto cleanup; refspec = git_remote__matching_refspec(remote, merge_name); @@ -321,21 +374,51 @@ goto cleanup; } - if (git_refspec_transform_r(&buf, refspec, merge_name) < 0) + if (git_refspec_transform(&buf, refspec, merge_name) < 0) goto cleanup; } else if (git_buf_sets(&buf, merge_name) < 0) goto cleanup; - error = git_buf_set(tracking_name, git_buf_cstr(&buf), git_buf_len(&buf)); + error = git_buf_set(out, git_buf_cstr(&buf), git_buf_len(&buf)); cleanup: + git_config_free(config); git_remote_free(remote); git_buf_free(&buf); return error; } -static int remote_name(git_buf *buf, git_repository *repo, const char *canonical_branch_name) +int git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname) +{ + int error; + const char *str; + git_config *cfg; + + if (!git_reference__is_branch(refname)) + return not_a_local_branch(refname); + + git_buf_sanitize(buf); + if ((error = git_repository_config_snapshot(&cfg, repo)) < 0) + return error; + + if ((error = retrieve_upstream_configuration(&str, cfg, refname, "branch.%s.remote")) < 0) + goto cleanup; + + if (!*str) { + giterr_set(GITERR_REFERENCE, "branch '%s' does not have an upstream remote", refname); + error = GIT_ENOTFOUND; + goto cleanup; + } + + error = git_buf_puts(buf, str); + +cleanup: + git_config_free(cfg); + return error; +} + +int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refname) { git_strarray remote_list = {0}; size_t i; @@ -344,12 +427,14 @@ int error = 0; char *remote_name = NULL; - assert(buf && repo && canonical_branch_name); + assert(buf && repo && refname); + + git_buf_sanitize(buf); /* Verify that this is a remote branch */ - if (!git_reference__is_remote(canonical_branch_name)) { + if (!git_reference__is_remote(refname)) { giterr_set(GITERR_INVALID, "Reference '%s' is not a remote branch.", - canonical_branch_name); + refname); error = GIT_ERROR; goto cleanup; } @@ -360,10 +445,10 @@ /* Find matching remotes */ for (i = 0; i < remote_list.count; i++) { - if ((error = git_remote_load(&remote, repo, remote_list.strings[i])) < 0) + if ((error = git_remote_lookup(&remote, repo, remote_list.strings[i])) < 0) continue; - fetchspec = git_remote__matching_dst_refspec(remote, canonical_branch_name); + fetchspec = git_remote__matching_dst_refspec(remote, refname); if (fetchspec) { /* If we have not already set out yet, then set * it to the matching remote name. Otherwise @@ -375,7 +460,7 @@ git_remote_free(remote); giterr_set(GITERR_REFERENCE, - "Reference '%s' is ambiguous", canonical_branch_name); + "Reference '%s' is ambiguous", refname); error = GIT_EAMBIGUOUS; goto cleanup; } @@ -389,76 +474,26 @@ error = git_buf_puts(buf, remote_name); } else { giterr_set(GITERR_REFERENCE, - "Could not determine remote for '%s'", canonical_branch_name); + "Could not determine remote for '%s'", refname); error = GIT_ENOTFOUND; } cleanup: + if (error < 0) + git_buf_free(buf); + git_strarray_free(&remote_list); return error; } -int git_branch_remote_name(char *buffer, size_t buffer_len, git_repository *repo, const char *refname) -{ - int ret; - git_buf buf = GIT_BUF_INIT; - - if ((ret = remote_name(&buf, repo, refname)) < 0) - return ret; - - if (buffer) - git_buf_copy_cstr(buffer, buffer_len, &buf); - - ret = (int)git_buf_len(&buf) + 1; - git_buf_free(&buf); - - return ret; -} - -int git_branch_upstream_name( - char *tracking_branch_name_out, - size_t buffer_size, - git_repository *repo, - const char *canonical_branch_name) -{ - git_buf buf = GIT_BUF_INIT; - int error; - - assert(canonical_branch_name); - - if (tracking_branch_name_out && buffer_size) - *tracking_branch_name_out = '\0'; - - if ((error = git_branch_upstream__name( - &buf, repo, canonical_branch_name)) < 0) - goto cleanup; - - if (tracking_branch_name_out && buf.size + 1 > buffer_size) { /* +1 for NUL byte */ - giterr_set( - GITERR_INVALID, - "Buffer too short to hold the tracked reference name."); - error = -1; - goto cleanup; - } - - if (tracking_branch_name_out) - git_buf_copy_cstr(tracking_branch_name_out, buffer_size, &buf); - - error = (int)buf.size + 1; - -cleanup: - git_buf_free(&buf); - return (int)error; -} - int git_branch_upstream( - git_reference **tracking_out, - git_reference *branch) + git_reference **tracking_out, + const git_reference *branch) { int error; git_buf tracking_name = GIT_BUF_INIT; - if ((error = git_branch_upstream__name(&tracking_name, + if ((error = git_branch_upstream_name(&tracking_name, git_reference_owner(branch), git_reference_name(branch))) < 0) return error; @@ -541,7 +576,7 @@ if (local) git_buf_puts(&value, "."); else - remote_name(&value, repo, git_reference_name(upstream)); + git_branch_remote_name(&value, repo, git_reference_name(upstream)); if (git_buf_printf(&key, "branch.%s.remote", shortname) < 0) goto on_error; @@ -555,12 +590,12 @@ goto on_error; } else { /* Get the remoe-tracking branch's refname in its repo */ - if (git_remote_load(&remote, repo, git_buf_cstr(&value)) < 0) + if (git_remote_lookup(&remote, repo, git_buf_cstr(&value)) < 0) goto on_error; fetchspec = git_remote__matching_dst_refspec(remote, git_reference_name(upstream)); git_buf_clear(&value); - if (!fetchspec || git_refspec_transform_l(&value, fetchspec, git_reference_name(upstream)) < 0) + if (!fetchspec || git_refspec_rtransform(&value, fetchspec, git_reference_name(upstream)) < 0) goto on_error; git_remote_free(remote); @@ -590,7 +625,7 @@ } int git_branch_is_head( - git_reference *branch) + const git_reference *branch) { git_reference *head; bool is_same = false; diff -Nru libgit2-0.20.0/src/buffer.c libgit2-0.22.2/src/buffer.c --- libgit2-0.20.0/src/buffer.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/buffer.c 2015-03-24 16:10:45.000000000 +0000 @@ -7,7 +7,7 @@ #include "buffer.h" #include "posix.h" #include "git2/buffer.h" -#include +#include "buf_text.h" #include /* Used as default value for git_buf->ptr so that people can always @@ -66,8 +66,11 @@ new_ptr = git__realloc(new_ptr, new_size); if (!new_ptr) { - if (mark_oom) + if (mark_oom) { + if (buf->ptr && (buf->ptr != git_buf__initbuf)) + git__free(buf->ptr); buf->ptr = git_buf__oom; + } return -1; } @@ -100,12 +103,23 @@ git_buf_init(buf, 0); } +void git_buf_sanitize(git_buf *buf) +{ + if (buf->ptr == NULL) { + assert(buf->size == 0 && buf->asize == 0); + buf->ptr = git_buf__initbuf; + } else if (buf->asize > buf->size) + buf->ptr[buf->size] = '\0'; +} + void git_buf_clear(git_buf *buf) { buf->size = 0; - if (!buf->ptr) + if (!buf->ptr) { buf->ptr = git_buf__initbuf; + buf->asize = 0; + } if (buf->asize > 0) buf->ptr[0] = '\0'; @@ -120,12 +134,25 @@ ENSURE_SIZE(buf, len + 1); memmove(buf->ptr, data, len); } + buf->size = len; - buf->ptr[buf->size] = '\0'; + if (buf->asize > buf->size) + buf->ptr[buf->size] = '\0'; + } return 0; } +int git_buf_is_binary(const git_buf *buf) +{ + return git_buf_text_is_binary(buf); +} + +int git_buf_contains_nul(const git_buf *buf) +{ + return git_buf_text_contains_nul(buf); +} + int git_buf_sets(git_buf *buf, const char *string) { return git_buf_set(buf, string, string ? strlen(string) : 0); @@ -139,25 +166,37 @@ return 0; } -int git_buf_put(git_buf *buf, const char *data, size_t len) +int git_buf_putcn(git_buf *buf, char c, size_t len) { ENSURE_SIZE(buf, buf->size + len + 1); - memmove(buf->ptr + buf->size, data, len); + memset(buf->ptr + buf->size, c, len); buf->size += len; buf->ptr[buf->size] = '\0'; return 0; } +int git_buf_put(git_buf *buf, const char *data, size_t len) +{ + if (len) { + assert(data); + ENSURE_SIZE(buf, buf->size + len + 1); + memmove(buf->ptr + buf->size, data, len); + buf->size += len; + buf->ptr[buf->size] = '\0'; + } + return 0; +} + int git_buf_puts(git_buf *buf, const char *string) { assert(string); return git_buf_put(buf, string, strlen(string)); } -static const char b64str[] = +static const char base64_encode[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -int git_buf_put_base64(git_buf *buf, const char *data, size_t len) +int git_buf_encode_base64(git_buf *buf, const char *data, size_t len) { size_t extra = len % 3; uint8_t *write, a, b, c; @@ -172,19 +211,19 @@ b = *read++; c = *read++; - *write++ = b64str[a >> 2]; - *write++ = b64str[(a & 0x03) << 4 | b >> 4]; - *write++ = b64str[(b & 0x0f) << 2 | c >> 6]; - *write++ = b64str[c & 0x3f]; + *write++ = base64_encode[a >> 2]; + *write++ = base64_encode[(a & 0x03) << 4 | b >> 4]; + *write++ = base64_encode[(b & 0x0f) << 2 | c >> 6]; + *write++ = base64_encode[c & 0x3f]; } if (extra > 0) { a = *read++; b = (extra > 1) ? *read++ : 0; - *write++ = b64str[a >> 2]; - *write++ = b64str[(a & 0x03) << 4 | b >> 4]; - *write++ = (extra > 1) ? b64str[(b & 0x0f) << 2] : '='; + *write++ = base64_encode[a >> 2]; + *write++ = base64_encode[(a & 0x03) << 4 | b >> 4]; + *write++ = (extra > 1) ? base64_encode[(b & 0x0f) << 2] : '='; *write++ = '='; } @@ -194,6 +233,88 @@ return 0; } +/* The inverse of base64_encode, offset by '+' == 43. */ +static const int8_t base64_decode[] = { + 62, + -1, -1, -1, + 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + -1, -1, -1, 0, -1, -1, -1, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + -1, -1, -1, -1, -1, -1, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 +}; + +#define BASE64_DECODE_VALUE(c) (((c) < 43 || (c) > 122) ? -1 : base64_decode[c - 43]) + +int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len) +{ + size_t i; + int8_t a, b, c, d; + size_t orig_size = buf->size; + + assert(len % 4 == 0); + ENSURE_SIZE(buf, buf->size + (len / 4 * 3) + 1); + + for (i = 0; i < len; i += 4) { + if ((a = BASE64_DECODE_VALUE(base64[i])) < 0 || + (b = BASE64_DECODE_VALUE(base64[i+1])) < 0 || + (c = BASE64_DECODE_VALUE(base64[i+2])) < 0 || + (d = BASE64_DECODE_VALUE(base64[i+3])) < 0) { + buf->size = orig_size; + buf->ptr[buf->size] = '\0'; + + giterr_set(GITERR_INVALID, "Invalid base64 input"); + return -1; + } + + buf->ptr[buf->size++] = ((a << 2) | (b & 0x30) >> 4); + buf->ptr[buf->size++] = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2); + buf->ptr[buf->size++] = (c & 0x03) << 6 | (d & 0x3f); + } + + buf->ptr[buf->size] = '\0'; + return 0; +} + +static const char b85str[] = + "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"; + +int git_buf_encode_base85(git_buf *buf, const char *data, size_t len) +{ + ENSURE_SIZE(buf, buf->size + (5 * ((len / 4) + !!(len % 4))) + 1); + + while (len) { + uint32_t acc = 0; + char b85[5]; + int i; + + for (i = 24; i >= 0; i -= 8) { + uint8_t ch = *data++; + acc |= ch << i; + + if (--len == 0) + break; + } + + for (i = 4; i >= 0; i--) { + int val = acc % 85; + acc /= 85; + + b85[i] = b85str[val]; + } + + for (i = 0; i < 5; i++) + buf->ptr[buf->size++] = b85[i]; + } + + buf->ptr[buf->size] = '\0'; + + return 0; +} + int git_buf_vprintf(git_buf *buf, const char *format, va_list ap) { int len; @@ -272,19 +393,20 @@ void git_buf_truncate(git_buf *buf, size_t len) { - if (len < buf->size) { - buf->size = len; + if (len >= buf->size) + return; + + buf->size = len; + if (buf->size < buf->asize) buf->ptr[buf->size] = '\0'; - } } void git_buf_shorten(git_buf *buf, size_t amount) { - if (amount > buf->size) - amount = buf->size; - - buf->size = buf->size - amount; - buf->ptr[buf->size] = '\0'; + if (buf->size > amount) + git_buf_truncate(buf, buf->size - amount); + else + git_buf_clear(buf); } void git_buf_rtruncate_at_char(git_buf *buf, char separator) @@ -424,7 +546,7 @@ ssize_t offset_a = -1; /* not safe to have str_b point internally to the buffer */ - assert(str_b < buf->ptr || str_b > buf->ptr + buf->size); + assert(str_b < buf->ptr || str_b >= buf->ptr + buf->size); /* figure out if we need to insert a separator */ if (separator && strlen_a) { @@ -439,13 +561,14 @@ if (git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1) < 0) return -1; + assert(buf->ptr); /* fix up internal pointers */ if (offset_a >= 0) str_a = buf->ptr + offset_a; /* do the actual copying */ - if (offset_a != 0) + if (offset_a != 0 && str_a) memmove(buf->ptr, str_a, strlen_a); if (need_sep) buf->ptr[strlen_a] = separator; @@ -457,6 +580,59 @@ return 0; } +int git_buf_join3( + git_buf *buf, + char separator, + const char *str_a, + const char *str_b, + const char *str_c) +{ + size_t len_a = strlen(str_a), len_b = strlen(str_b), len_c = strlen(str_c); + int sep_a = 0, sep_b = 0; + char *tgt; + + /* for this function, disallow pointers into the existing buffer */ + assert(str_a < buf->ptr || str_a >= buf->ptr + buf->size); + assert(str_b < buf->ptr || str_b >= buf->ptr + buf->size); + assert(str_c < buf->ptr || str_c >= buf->ptr + buf->size); + + if (separator) { + if (len_a > 0) { + while (*str_b == separator) { str_b++; len_b--; } + sep_a = (str_a[len_a - 1] != separator); + } + if (len_a > 0 || len_b > 0) + while (*str_c == separator) { str_c++; len_c--; } + if (len_b > 0) + sep_b = (str_b[len_b - 1] != separator); + } + + if (git_buf_grow(buf, len_a + sep_a + len_b + sep_b + len_c + 1) < 0) + return -1; + + tgt = buf->ptr; + + if (len_a) { + memcpy(tgt, str_a, len_a); + tgt += len_a; + } + if (sep_a) + *tgt++ = separator; + if (len_b) { + memcpy(tgt, str_b, len_b); + tgt += len_b; + } + if (sep_b) + *tgt++ = separator; + if (len_c) + memcpy(tgt, str_c, len_c); + + buf->size = len_a + sep_a + len_b + sep_b + len_c; + buf->ptr[buf->size] = '\0'; + + return 0; +} + void git_buf_rtrim(git_buf *buf) { while (buf->size > 0) { @@ -466,7 +642,8 @@ buf->size--; } - buf->ptr[buf->size] = '\0'; + if (buf->asize > buf->size) + buf->ptr[buf->size] = '\0'; } int git_buf_cmp(const git_buf *a, const git_buf *b) @@ -490,8 +667,7 @@ /* Ported from git.git * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176 */ - if (git_buf_grow(buf, git_buf_len(buf) + nb_to_insert - nb_to_remove) < 0) - return -1; + ENSURE_SIZE(buf, buf->size + nb_to_insert - nb_to_insert + 1); memmove(buf->ptr + where + nb_to_insert, buf->ptr + where + nb_to_remove, diff -Nru libgit2-0.20.0/src/buffer.h libgit2-0.22.2/src/buffer.h --- libgit2-0.20.0/src/buffer.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/buffer.h 2015-03-24 16:10:45.000000000 +0000 @@ -10,7 +10,6 @@ #include "common.h" #include "git2/strarray.h" #include "git2/buffer.h" -#include /* typedef struct { * char *ptr; @@ -50,6 +49,15 @@ extern int git_buf_try_grow( git_buf *buf, size_t target_size, bool mark_oom, bool preserve_external); +/** + * Sanitizes git_buf structures provided from user input. Users of the + * library, when providing git_buf's, may wish to provide a NULL ptr for + * ease of handling. The buffer routines, however, expect a non-NULL ptr + * always. This helper method simply handles NULL input, converting to a + * git_buf__initbuf. + */ +extern void git_buf_sanitize(git_buf *buf); + extern void git_buf_swap(git_buf *buf_a, git_buf *buf_b); extern char *git_buf_detach(git_buf *buf); extern void git_buf_attach(git_buf *buf, char *ptr, size_t asize); @@ -80,6 +88,7 @@ */ int git_buf_sets(git_buf *buf, const char *string); int git_buf_putc(git_buf *buf, char c); +int git_buf_putcn(git_buf *buf, char c, size_t len); int git_buf_put(git_buf *buf, const char *data, size_t len); int git_buf_puts(git_buf *buf, const char *string); int git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3); @@ -90,8 +99,12 @@ void git_buf_shorten(git_buf *buf, size_t amount); void git_buf_rtruncate_at_char(git_buf *path, char separator); +/** General join with separator */ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...); +/** Fast join of two strings - first may legally point into `buf` data */ int git_buf_join(git_buf *buf, char separator, const char *str_a, const char *str_b); +/** Fast join of three strings - cannot reference `buf` data */ +int git_buf_join3(git_buf *buf, char separator, const char *str_a, const char *str_b, const char *str_c); /** * Join two strings as paths, inserting a slash between as needed. @@ -143,7 +156,12 @@ int git_buf_cmp(const git_buf *a, const git_buf *b); /* Write data as base64 encoded in buffer */ -int git_buf_put_base64(git_buf *buf, const char *data, size_t len); +int git_buf_encode_base64(git_buf *buf, const char *data, size_t len); +/* Decode the given bas64 and write the result to the buffer */ +int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len); + +/* Write data as "base85" encoded in buffer */ +int git_buf_encode_base85(git_buf *buf, const char *data, size_t len); /* * Insert, remove or replace a portion of the buffer. diff -Nru libgit2-0.20.0/src/buf_text.c libgit2-0.22.2/src/buf_text.c --- libgit2-0.20.0/src/buf_text.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/buf_text.c 2015-03-24 16:10:45.000000000 +0000 @@ -123,9 +123,13 @@ for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) { size_t copylen = next - scan; - /* don't convert existing \r\n to \r\r\n */ - size_t extralen = (next > start && next[-1] == '\r') ? 1 : 2; - size_t needsize = tgt->size + copylen + extralen + 1; + size_t needsize = tgt->size + copylen + 2 + 1; + + /* if we find mixed line endings, bail */ + if (next > start && next[-1] == '\r') { + git_buf_free(tgt); + return GIT_PASSTHROUGH; + } if (tgt->asize < needsize && git_buf_grow(tgt, needsize) < 0) return -1; @@ -134,11 +138,12 @@ memcpy(tgt->ptr + tgt->size, scan, copylen); tgt->size += copylen; } - if (extralen == 2) - tgt->ptr[tgt->size++] = '\r'; + + tgt->ptr[tgt->size++] = '\r'; tgt->ptr[tgt->size++] = '\n'; } + tgt->ptr[tgt->size] = '\0'; return git_buf_put(tgt, scan, end - scan); } @@ -186,7 +191,10 @@ while (scan < end) { unsigned char c = *scan++; - if (c > 0x1F && c < 0x7F) + /* Printable characters are those above SPACE (0x1F) excluding DEL, + * and including BS, ESC and FF. + */ + if ((c > 0x1F && c != 127) || c == '\b' || c == '\033' || c == '\014') printable++; else if (c == '\0') return true; diff -Nru libgit2-0.20.0/src/buf_text.h libgit2-0.22.2/src/buf_text.h --- libgit2-0.20.0/src/buf_text.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/buf_text.h 2015-03-24 16:10:45.000000000 +0000 @@ -56,9 +56,10 @@ extern void git_buf_text_unescape(git_buf *buf); /** - * Replace all \r\n with \n. Does not modify \r without trailing \n. + * Replace all \r\n with \n. * - * @return 0 on success, -1 on memory error + * @return 0 on success, -1 on memory error, GIT_PASSTHROUGH if the + * source buffer has mixed line endings. */ extern int git_buf_text_crlf_to_lf(git_buf *tgt, const git_buf *src); diff -Nru libgit2-0.20.0/src/cache.c libgit2-0.22.2/src/cache.c --- libgit2-0.20.0/src/cache.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/cache.c 2015-03-24 16:10:45.000000000 +0000 @@ -68,8 +68,8 @@ { memset(cache, 0, sizeof(*cache)); cache->map = git_oidmap_alloc(); - if (git_mutex_init(&cache->lock)) { - giterr_set(GITERR_OS, "Failed to initialize cache mutex"); + if (git_rwlock_init(&cache->lock)) { + giterr_set(GITERR_OS, "Failed to initialize cache rwlock"); return -1; } return 0; @@ -94,19 +94,19 @@ void git_cache_clear(git_cache *cache) { - if (git_mutex_lock(&cache->lock) < 0) + if (git_rwlock_wrlock(&cache->lock) < 0) return; clear_cache(cache); - git_mutex_unlock(&cache->lock); + git_rwlock_wrunlock(&cache->lock); } void git_cache_free(git_cache *cache) { git_cache_clear(cache); git_oidmap_free(cache->map); - git_mutex_free(&cache->lock); + git_rwlock_free(&cache->lock); git__memzero(cache, sizeof(*cache)); } @@ -152,7 +152,7 @@ khiter_t pos; git_cached_obj *entry = NULL; - if (!git_cache__enabled || git_mutex_lock(&cache->lock) < 0) + if (!git_cache__enabled || git_rwlock_rdlock(&cache->lock) < 0) return NULL; pos = kh_get(oid, cache->map, oid); @@ -166,7 +166,7 @@ } } - git_mutex_unlock(&cache->lock); + git_rwlock_rdunlock(&cache->lock); return entry; } @@ -185,7 +185,7 @@ if (!cache_should_store(entry->type, entry->size)) return entry; - if (git_mutex_lock(&cache->lock) < 0) + if (git_rwlock_wrlock(&cache->lock) < 0) return entry; /* soften the load on the cache */ @@ -227,7 +227,7 @@ } } - git_mutex_unlock(&cache->lock); + git_rwlock_wrunlock(&cache->lock); return entry; } diff -Nru libgit2-0.20.0/src/cache.h libgit2-0.22.2/src/cache.h --- libgit2-0.20.0/src/cache.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/cache.h 2015-03-24 16:10:45.000000000 +0000 @@ -30,7 +30,7 @@ typedef struct { git_oidmap *map; - git_mutex lock; + git_rwlock lock; ssize_t used_memory; } git_cache; diff -Nru libgit2-0.20.0/src/cc-compat.h libgit2-0.22.2/src/cc-compat.h --- libgit2-0.20.0/src/cc-compat.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/cc-compat.h 2015-03-24 16:10:45.000000000 +0000 @@ -7,6 +7,8 @@ #ifndef INCLUDE_compat_h__ #define INCLUDE_compat_h__ +#include + /* * See if our compiler is known to support flexible array members. */ @@ -33,15 +35,25 @@ # define GIT_TYPEOF(x) #endif +#if defined(__GNUC__) +# define GIT_ALIGN(x,size) x __attribute__ ((aligned(size))) +#elif defined(_MSC_VER) +# define GIT_ALIGN(x,size) __declspec(align(size)) x +#else +# define GIT_ALIGN(x,size) x +#endif + #define GIT_UNUSED(x) ((void)(x)) /* Define the printf format specifer to use for size_t output */ #if defined(_MSC_VER) || defined(__MINGW32__) # define PRIuZ "Iu" # define PRIxZ "Ix" +# define PRIdZ "Id" #else # define PRIuZ "zu" # define PRIxZ "zx" +# define PRIdZ "zd" #endif /* Micosoft Visual C/C++ */ diff -Nru libgit2-0.20.0/src/checkout.c libgit2-0.22.2/src/checkout.c --- libgit2-0.20.0/src/checkout.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/checkout.c 2015-03-24 16:10:45.000000000 +0000 @@ -37,30 +37,38 @@ CHECKOUT_ACTION__UPDATE_BLOB = 2, CHECKOUT_ACTION__UPDATE_SUBMODULE = 4, CHECKOUT_ACTION__CONFLICT = 8, - CHECKOUT_ACTION__UPDATE_CONFLICT = 16, - CHECKOUT_ACTION__MAX = 16, - CHECKOUT_ACTION__DEFER_REMOVE = 32, + CHECKOUT_ACTION__REMOVE_CONFLICT = 16, + CHECKOUT_ACTION__UPDATE_CONFLICT = 32, + CHECKOUT_ACTION__MAX = 32, + CHECKOUT_ACTION__DEFER_REMOVE = 64, CHECKOUT_ACTION__REMOVE_AND_UPDATE = (CHECKOUT_ACTION__UPDATE_BLOB | CHECKOUT_ACTION__REMOVE), }; typedef struct { git_repository *repo; + git_iterator *target; git_diff *diff; - git_checkout_opts opts; + git_checkout_options opts; bool opts_free_baseline; char *pfx; git_index *index; git_pool pool; git_vector removes; - git_vector conflicts; + git_vector remove_conflicts; + git_vector update_conflicts; + git_vector *update_reuc; + git_vector *update_names; git_buf path; size_t workdir_len; + git_buf tmp; unsigned int strategy; int can_symlink; bool reload_submodules; size_t total_steps; size_t completed_steps; + git_checkout_perfdata perfdata; + git_buf last_mkdir; } checkout_data; typedef struct { @@ -70,7 +78,9 @@ int name_collision:1, directoryfile:1, - one_to_two:1; + one_to_two:1, + binary:1, + submodule:1; } checkout_conflictdata; static int checkout_notify( @@ -83,19 +93,17 @@ const git_diff_file *baseline = NULL, *target = NULL, *workdir = NULL; const char *path = NULL; - if (!data->opts.notify_cb) - return 0; - - if ((why & data->opts.notify_flags) == 0) + if (!data->opts.notify_cb || + (why & data->opts.notify_flags) == 0) return 0; if (wditem) { memset(&wdfile, 0, sizeof(wdfile)); - git_oid_cpy(&wdfile.oid, &wditem->oid); + git_oid_cpy(&wdfile.id, &wditem->id); wdfile.path = wditem->path; wdfile.size = wditem->file_size; - wdfile.flags = GIT_DIFF_FLAG_VALID_OID; + wdfile.flags = GIT_DIFF_FLAG_VALID_ID; wdfile.mode = wditem->mode; workdir = &wdfile; @@ -115,6 +123,7 @@ case GIT_DELTA_ADDED: case GIT_DELTA_IGNORED: case GIT_DELTA_UNTRACKED: + case GIT_DELTA_UNREADABLE: target = &delta->new_file; break; case GIT_DELTA_DELETED: @@ -125,13 +134,19 @@ path = delta->old_file.path; } - return data->opts.notify_cb( - why, path, baseline, target, workdir, data->opts.notify_payload); + { + int error = data->opts.notify_cb( + why, path, baseline, target, workdir, data->opts.notify_payload); + + return giterr_set_after_callback_function( + error, "git_checkout notification"); + } } static bool checkout_is_workdir_modified( checkout_data *data, const git_diff_file *baseitem, + const git_diff_file *newitem, const git_index_entry *wditem) { git_oid oid; @@ -142,30 +157,37 @@ git_submodule *sm; unsigned int sm_status = 0; const git_oid *sm_oid = NULL; + bool rval = false; - if (git_submodule_lookup(&sm, data->repo, wditem->path) < 0 || - git_submodule_status(&sm_status, sm) < 0) - return true; - - if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) + if (git_submodule_lookup(&sm, data->repo, wditem->path) < 0) { + giterr_clear(); return true; + } - sm_oid = git_submodule_wd_id(sm); - if (!sm_oid) - return false; + if (git_submodule_status(&sm_status, sm) < 0 || + GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) + rval = true; + else if ((sm_oid = git_submodule_wd_id(sm)) == NULL) + rval = false; + else + rval = (git_oid__cmp(&baseitem->id, sm_oid) != 0); - return (git_oid__cmp(&baseitem->oid, sm_oid) != 0); + git_submodule_free(sm); + 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. + * 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.) */ if ((ie = git_index_get_bypath(data->index, wditem->path, 0)) != NULL) { if (wditem->mtime.seconds == ie->mtime.seconds && wditem->mtime.nanoseconds == ie->mtime.nanoseconds && wditem->file_size == ie->file_size) - return (git_oid__cmp(&baseitem->oid, &ie->oid) != 0); + return (git_oid__cmp(&baseitem->id, &ie->id) != 0 && + git_oid_cmp(&newitem->id, &ie->id) != 0); } /* depending on where base is coming from, we may or may not know @@ -174,113 +196,135 @@ if (baseitem->size && wditem->file_size != baseitem->size) return true; - if (git_diff__oid_for_file( - data->repo, wditem->path, wditem->mode, - wditem->file_size, &oid) < 0) + if (git_diff__oid_for_entry(&oid, data->diff, wditem, NULL) < 0) return false; - return (git_oid__cmp(&baseitem->oid, &oid) != 0); + return (git_oid__cmp(&baseitem->id, &oid) != 0); } #define CHECKOUT_ACTION_IF(FLAG,YES,NO) \ ((data->strategy & GIT_CHECKOUT_##FLAG) ? CHECKOUT_ACTION__##YES : CHECKOUT_ACTION__##NO) static int checkout_action_common( + int *action, checkout_data *data, - int action, const git_diff_delta *delta, const git_index_entry *wd) { git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE; - if (action <= 0) - return action; - if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) - action = (action & ~CHECKOUT_ACTION__REMOVE); + *action = (*action & ~CHECKOUT_ACTION__REMOVE); - if ((action & CHECKOUT_ACTION__UPDATE_BLOB) != 0) { + if ((*action & CHECKOUT_ACTION__UPDATE_BLOB) != 0) { if (S_ISGITLINK(delta->new_file.mode)) - action = (action & ~CHECKOUT_ACTION__UPDATE_BLOB) | + *action = (*action & ~CHECKOUT_ACTION__UPDATE_BLOB) | CHECKOUT_ACTION__UPDATE_SUBMODULE; /* to "update" a symlink, we must remove the old one first */ if (delta->new_file.mode == GIT_FILEMODE_LINK && wd != NULL) - action |= CHECKOUT_ACTION__REMOVE; + *action |= CHECKOUT_ACTION__REMOVE; notify = GIT_CHECKOUT_NOTIFY_UPDATED; } - if ((action & CHECKOUT_ACTION__CONFLICT) != 0) + if ((*action & CHECKOUT_ACTION__CONFLICT) != 0) notify = GIT_CHECKOUT_NOTIFY_CONFLICT; - if (notify != GIT_CHECKOUT_NOTIFY_NONE && - checkout_notify(data, notify, delta, wd) != 0) - return GIT_EUSER; - - return action; + return checkout_notify(data, notify, delta, wd); } static int checkout_action_no_wd( + int *action, checkout_data *data, const git_diff_delta *delta) { - int action = CHECKOUT_ACTION__NONE; + int error = 0; + + *action = CHECKOUT_ACTION__NONE; switch (delta->status) { case GIT_DELTA_UNMODIFIED: /* case 12 */ - if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL)) - return GIT_EUSER; - action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, NONE); + error = checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL); + if (error) + return error; + *action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, NONE); break; case GIT_DELTA_ADDED: /* case 2 or 28 (and 5 but not really) */ - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); break; case GIT_DELTA_MODIFIED: /* case 13 (and 35 but not really) */ - action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, CONFLICT); + *action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, CONFLICT); break; case GIT_DELTA_TYPECHANGE: /* case 21 (B->T) and 28 (T->B)*/ if (delta->new_file.mode == GIT_FILEMODE_TREE) - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); break; case GIT_DELTA_DELETED: /* case 8 or 25 */ + *action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE); + break; default: /* impossible */ break; } - return checkout_action_common(data, action, delta, NULL); + return checkout_action_common(action, data, delta, NULL); +} + +static bool wd_item_is_removable(git_iterator *iter, const git_index_entry *wd) +{ + git_buf *full = NULL; + + if (wd->mode != GIT_FILEMODE_TREE) + return true; + if (git_iterator_current_workdir_path(&full, iter) < 0) + return true; + return !full || !git_path_contains(full, DOT_GIT); +} + +static int checkout_queue_remove(checkout_data *data, const char *path) +{ + char *copy = git_pool_strdup(&data->pool, path); + GITERR_CHECK_ALLOC(copy); + return git_vector_insert(&data->removes, copy); } +/* note that this advances the iterator over the wd item */ static int checkout_action_wd_only( checkout_data *data, git_iterator *workdir, - const git_index_entry *wd, + const git_index_entry **wditem, git_vector *pathspec) { + int error = 0; bool remove = false; git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE; + const git_index_entry *wd = *wditem; if (!git_pathspec__match( pathspec, wd->path, (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0, git_iterator_ignore_case(workdir), NULL, NULL)) - return 0; + return git_iterator_advance(wditem, workdir); /* check if item is tracked in the index but not in the checkout diff */ if (data->index != NULL) { - if (wd->mode != GIT_FILEMODE_TREE) { - int error; + size_t pos; + + error = git_index__find_pos( + &pos, data->index, wd->path, 0, GIT_INDEX_STAGE_ANY); - if ((error = git_index_find(NULL, data->index, wd->path)) == 0) { + if (wd->mode != GIT_FILEMODE_TREE) { + if (!error) { /* found by git_index__find_pos call */ notify = GIT_CHECKOUT_NOTIFY_DIRTY; remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0); } else if (error != GIT_ENOTFOUND) return error; + else + error = 0; /* git_index__find_pos does not set error msg */ } else { /* for tree entries, we have to see if there are any index * entries that are contained inside that tree */ - size_t pos = git_index__prefix_position(data->index, wd->path); const git_index_entry *e = git_index_get_byindex(data->index, pos); if (e != NULL && data->diff->pfxcomp(e->path, wd->path) == 0) { @@ -290,29 +334,52 @@ } } - if (notify != GIT_CHECKOUT_NOTIFY_NONE) - /* found in index */; - else if (git_iterator_current_is_ignored(workdir)) { - notify = GIT_CHECKOUT_NOTIFY_IGNORED; - remove = ((data->strategy & GIT_CHECKOUT_REMOVE_IGNORED) != 0); - } - else { - notify = GIT_CHECKOUT_NOTIFY_UNTRACKED; - remove = ((data->strategy & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0); - } + if (notify != GIT_CHECKOUT_NOTIFY_NONE) { + /* if we found something in the index, notify and advance */ + if ((error = checkout_notify(data, notify, NULL, wd)) != 0) + return error; - if (checkout_notify(data, notify, NULL, wd)) - return GIT_EUSER; + if (remove && wd_item_is_removable(workdir, wd)) + error = checkout_queue_remove(data, wd->path); - if (remove) { - char *path = git_pool_strdup(&data->pool, wd->path); - GITERR_CHECK_ALLOC(path); + if (!error) + error = git_iterator_advance(wditem, workdir); + } else { + /* untracked or ignored - can't know which until we advance through */ + bool over = false, removable = wd_item_is_removable(workdir, wd); + git_iterator_status_t untracked_state; + + /* copy the entry for issuing notification callback later */ + git_index_entry saved_wd = *wd; + git_buf_sets(&data->tmp, wd->path); + saved_wd.path = data->tmp.ptr; + + error = git_iterator_advance_over_with_status( + wditem, &untracked_state, workdir); + if (error == GIT_ITEROVER) + over = true; + else if (error < 0) + return error; - if (git_vector_insert(&data->removes, path) < 0) - return -1; + if (untracked_state == GIT_ITERATOR_STATUS_IGNORED) { + notify = GIT_CHECKOUT_NOTIFY_IGNORED; + remove = ((data->strategy & GIT_CHECKOUT_REMOVE_IGNORED) != 0); + } else { + notify = GIT_CHECKOUT_NOTIFY_UNTRACKED; + remove = ((data->strategy & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0); + } + + if ((error = checkout_notify(data, notify, NULL, &saved_wd)) != 0) + return error; + + if (remove && removable) + error = checkout_queue_remove(data, saved_wd.path); + + if (!error && over) /* restore ITEROVER if needed */ + error = GIT_ITEROVER; } - return 0; + return error; } static bool submodule_is_config_only( @@ -321,45 +388,54 @@ { git_submodule *sm = NULL; unsigned int sm_loc = 0; + bool rval = false; - if (git_submodule_lookup(&sm, data->repo, path) < 0 || - git_submodule_location(&sm_loc, sm) < 0 || - sm_loc == GIT_SUBMODULE_STATUS_IN_CONFIG) + if (git_submodule_lookup(&sm, data->repo, path) < 0) return true; - return false; + if (git_submodule_location(&sm_loc, sm) < 0 || + sm_loc == GIT_SUBMODULE_STATUS_IN_CONFIG) + rval = true; + + git_submodule_free(sm); + + return rval; } static int checkout_action_with_wd( + int *action, checkout_data *data, const git_diff_delta *delta, + git_iterator *workdir, const git_index_entry *wd) { - int action = CHECKOUT_ACTION__NONE; + *action = CHECKOUT_ACTION__NONE; switch (delta->status) { case GIT_DELTA_UNMODIFIED: /* case 14/15 or 33 */ - if (checkout_is_workdir_modified(data, &delta->old_file, wd)) { - if (checkout_notify( - data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd)) - return GIT_EUSER; - action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, NONE); + if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd)) { + GITERR_CHECK_ERROR( + checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd) ); + *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, NONE); } break; case GIT_DELTA_ADDED: /* case 3, 4 or 6 */ - action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT); + if (git_iterator_current_is_ignored(workdir)) + *action = CHECKOUT_ACTION_IF(DONT_OVERWRITE_IGNORED, CONFLICT, UPDATE_BLOB); + else + *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT); break; case GIT_DELTA_DELETED: /* case 9 or 10 (or 26 but not really) */ - if (checkout_is_workdir_modified(data, &delta->old_file, wd)) - action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); + if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd)) + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); else - action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE); break; case GIT_DELTA_MODIFIED: /* case 16, 17, 18 (or 36 but not really) */ - if (checkout_is_workdir_modified(data, &delta->old_file, wd)) - action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT); + if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd)) + *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT); else - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); break; case GIT_DELTA_TYPECHANGE: /* case 22, 23, 29, 30 */ if (delta->old_file.mode == GIT_FILEMODE_TREE) { @@ -367,92 +443,96 @@ /* either deleting items in old tree will delete the wd dir, * or we'll get a conflict when we attempt blob update... */ - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); else if (wd->mode == GIT_FILEMODE_COMMIT) { /* workdir is possibly a "phantom" submodule - treat as a * tree if the only submodule info came from the config */ if (submodule_is_config_only(data, wd->path)) - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); else - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); } else - action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); } - else if (checkout_is_workdir_modified(data, &delta->old_file, wd)) - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + else if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd)) + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); else - action = CHECKOUT_ACTION_IF(SAFE, REMOVE_AND_UPDATE, NONE); + *action = CHECKOUT_ACTION_IF(SAFE, REMOVE_AND_UPDATE, NONE); /* don't update if the typechange is to a tree */ if (delta->new_file.mode == GIT_FILEMODE_TREE) - action = (action & ~CHECKOUT_ACTION__UPDATE_BLOB); + *action = (*action & ~CHECKOUT_ACTION__UPDATE_BLOB); break; default: /* impossible */ break; } - return checkout_action_common(data, action, delta, wd); + return checkout_action_common(action, data, delta, wd); } static int checkout_action_with_wd_blocker( + int *action, checkout_data *data, const git_diff_delta *delta, const git_index_entry *wd) { - int action = CHECKOUT_ACTION__NONE; + *action = CHECKOUT_ACTION__NONE; switch (delta->status) { case GIT_DELTA_UNMODIFIED: /* should show delta as dirty / deleted */ - if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd)) - return GIT_EUSER; - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, NONE); + GITERR_CHECK_ERROR( + checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd) ); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, NONE); break; case GIT_DELTA_ADDED: case GIT_DELTA_MODIFIED: - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); break; case GIT_DELTA_DELETED: - action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT); break; case GIT_DELTA_TYPECHANGE: /* not 100% certain about this... */ - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); break; default: /* impossible */ break; } - return checkout_action_common(data, action, delta, wd); + return checkout_action_common(action, data, delta, wd); } static int checkout_action_with_wd_dir( + int *action, checkout_data *data, const git_diff_delta *delta, + git_iterator *workdir, const git_index_entry *wd) { - int action = CHECKOUT_ACTION__NONE; + *action = CHECKOUT_ACTION__NONE; switch (delta->status) { case GIT_DELTA_UNMODIFIED: /* case 19 or 24 (or 34 but not really) */ - if (checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL) || - checkout_notify( - data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd)) - return GIT_EUSER; + GITERR_CHECK_ERROR( + checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL)); + GITERR_CHECK_ERROR( + checkout_notify(data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd)); break; case GIT_DELTA_ADDED:/* case 4 (and 7 for dir) */ case GIT_DELTA_MODIFIED: /* case 20 (or 37 but not really) */ if (delta->old_file.mode == GIT_FILEMODE_COMMIT) /* expected submodule (and maybe found one) */; else if (delta->new_file.mode != GIT_FILEMODE_TREE) - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + *action = git_iterator_current_is_ignored(workdir) ? + CHECKOUT_ACTION_IF(DONT_OVERWRITE_IGNORED, CONFLICT, REMOVE_AND_UPDATE) : + CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); break; case GIT_DELTA_DELETED: /* case 11 (and 27 for dir) */ - if (delta->old_file.mode != GIT_FILEMODE_TREE && - checkout_notify( - data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd)) - return GIT_EUSER; + if (delta->old_file.mode != GIT_FILEMODE_TREE) + GITERR_CHECK_ERROR( + checkout_notify(data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd)); break; case GIT_DELTA_TYPECHANGE: /* case 24 or 31 */ if (delta->old_file.mode == GIT_FILEMODE_TREE) { @@ -462,39 +542,41 @@ * directory if is it left empty, so we can defer removing the * dir and it will succeed if no children are left. */ - action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); - if (action != CHECKOUT_ACTION__NONE) - action |= CHECKOUT_ACTION__DEFER_REMOVE; + *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE); + if (*action != CHECKOUT_ACTION__NONE) + *action |= CHECKOUT_ACTION__DEFER_REMOVE; } else if (delta->new_file.mode != GIT_FILEMODE_TREE) /* For typechange to dir, dir is already created so no action */ - action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); + *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT); break; default: /* impossible */ break; } - return checkout_action_common(data, action, delta, wd); + return checkout_action_common(action, data, delta, wd); } static int checkout_action( + int *action, checkout_data *data, git_diff_delta *delta, git_iterator *workdir, - const git_index_entry **wditem_ptr, + const git_index_entry **wditem, git_vector *pathspec) { - const git_index_entry *wd = *wditem_ptr; - int cmp = -1, act; + int cmp = -1, error; int (*strcomp)(const char *, const char *) = data->diff->strcomp; int (*pfxcomp)(const char *str, const char *pfx) = data->diff->pfxcomp; - int error; + int (*advance)(const git_index_entry **, git_iterator *) = NULL; /* move workdir iterator to follow along with deltas */ while (1) { + const git_index_entry *wd = *wditem; + if (!wd) - return checkout_action_no_wd(data, delta); + return checkout_action_no_wd(action, data, delta); cmp = strcomp(wd->path, delta->old_file.path); @@ -512,79 +594,74 @@ if (cmp == 0) { if (wd->mode == GIT_FILEMODE_TREE) { /* case 2 - entry prefixed by workdir tree */ - error = git_iterator_advance_into_or_over(&wd, workdir); - if (error && error != GIT_ITEROVER) - goto fail; - *wditem_ptr = wd; + error = git_iterator_advance_into_or_over(wditem, workdir); + if (error < 0 && error != GIT_ITEROVER) + goto done; continue; } /* case 3 maybe - wd contains non-dir where dir expected */ if (delta->old_file.path[strlen(wd->path)] == '/') { - act = checkout_action_with_wd_blocker(data, delta, wd); - *wditem_ptr = - git_iterator_advance(&wd, workdir) ? NULL : wd; - return act; + error = checkout_action_with_wd_blocker( + action, data, delta, wd); + advance = git_iterator_advance; + goto done; } } /* case 1 - handle wd item (if it matches pathspec) */ - if (checkout_action_wd_only(data, workdir, wd, pathspec) < 0) - goto fail; - if ((error = git_iterator_advance(&wd, workdir)) < 0 && - error != GIT_ITEROVER) - goto fail; - - *wditem_ptr = wd; + error = checkout_action_wd_only(data, workdir, wditem, pathspec); + if (error && error != GIT_ITEROVER) + goto done; continue; } if (cmp == 0) { /* case 4 */ - act = checkout_action_with_wd(data, delta, wd); - *wditem_ptr = git_iterator_advance(&wd, workdir) ? NULL : wd; - return act; + error = checkout_action_with_wd(action, data, delta, workdir, wd); + advance = git_iterator_advance; + goto done; } cmp = pfxcomp(wd->path, delta->old_file.path); if (cmp == 0) { /* case 5 */ if (wd->path[strlen(delta->old_file.path)] != '/') - return checkout_action_no_wd(data, delta); + return checkout_action_no_wd(action, data, delta); if (delta->status == GIT_DELTA_TYPECHANGE) { if (delta->old_file.mode == GIT_FILEMODE_TREE) { - act = checkout_action_with_wd(data, delta, wd); - if ((error = git_iterator_advance_into(&wd, workdir)) < 0 && - error != GIT_ENOTFOUND) - goto fail; - *wditem_ptr = wd; - return act; + error = checkout_action_with_wd(action, data, delta, workdir, wd); + advance = git_iterator_advance_into; + goto done; } if (delta->new_file.mode == GIT_FILEMODE_TREE || delta->new_file.mode == GIT_FILEMODE_COMMIT || delta->old_file.mode == GIT_FILEMODE_COMMIT) { - act = checkout_action_with_wd(data, delta, wd); - if ((error = git_iterator_advance(&wd, workdir)) < 0 && - error != GIT_ITEROVER) - goto fail; - *wditem_ptr = wd; - return act; + error = checkout_action_with_wd(action, data, delta, workdir, wd); + advance = git_iterator_advance; + goto done; } } - return checkout_action_with_wd_dir(data, delta, wd); + return checkout_action_with_wd_dir(action, data, delta, workdir, wd); } /* case 6 - wd is after delta */ - return checkout_action_no_wd(data, delta); + return checkout_action_no_wd(action, data, delta); } -fail: - *wditem_ptr = NULL; - return -1; +done: + if (!error && advance != NULL && + (error = advance(wditem, workdir)) < 0) { + *wditem = NULL; + if (error == GIT_ITEROVER) + error = 0; + } + + return error; } static int checkout_remaining_wd_items( @@ -595,10 +672,8 @@ { int error = 0; - while (wd && !error) { - if (!(error = checkout_action_wd_only(data, workdir, wd, spec))) - error = git_iterator_advance(&wd, workdir); - } + while (wd && !error) + error = checkout_action_wd_only(data, workdir, &wd, spec); if (error == GIT_ITEROVER) error = 0; @@ -633,10 +708,13 @@ return diff; } -int checkout_conflictdata_empty(const git_vector *conflicts, size_t idx) +int checkout_conflictdata_empty( + const git_vector *conflicts, size_t idx, void *payload) { checkout_conflictdata *conflict; + GIT_UNUSED(payload); + if ((conflict = git_vector_get(conflicts, idx)) == NULL) return -1; @@ -674,31 +752,103 @@ return false; } -static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, git_vector *pathspec) +GIT_INLINE(int) checkout_conflict_detect_submodule(checkout_conflictdata *conflict) +{ + conflict->submodule = ((conflict->ancestor && S_ISGITLINK(conflict->ancestor->mode)) || + (conflict->ours && S_ISGITLINK(conflict->ours->mode)) || + (conflict->theirs && S_ISGITLINK(conflict->theirs->mode))); + return 0; +} + +GIT_INLINE(int) checkout_conflict_detect_binary(git_repository *repo, checkout_conflictdata *conflict) +{ + git_blob *ancestor_blob = NULL, *our_blob = NULL, *their_blob = NULL; + int error = 0; + + if (conflict->submodule) + return 0; + + if (conflict->ancestor) { + if ((error = git_blob_lookup(&ancestor_blob, repo, &conflict->ancestor->id)) < 0) + goto done; + + conflict->binary = git_blob_is_binary(ancestor_blob); + } + + if (!conflict->binary && conflict->ours) { + if ((error = git_blob_lookup(&our_blob, repo, &conflict->ours->id)) < 0) + goto done; + + conflict->binary = git_blob_is_binary(our_blob); + } + + if (!conflict->binary && conflict->theirs) { + if ((error = git_blob_lookup(&their_blob, repo, &conflict->theirs->id)) < 0) + goto done; + + conflict->binary = git_blob_is_binary(their_blob); + } + +done: + git_blob_free(ancestor_blob); + git_blob_free(our_blob); + git_blob_free(their_blob); + + return error; +} + +static int checkout_conflict_append_update( + const git_index_entry *ancestor, + const git_index_entry *ours, + const git_index_entry *theirs, + void *payload) +{ + checkout_data *data = payload; + checkout_conflictdata *conflict; + int error; + + conflict = git__calloc(1, sizeof(checkout_conflictdata)); + GITERR_CHECK_ALLOC(conflict); + + conflict->ancestor = ancestor; + conflict->ours = ours; + conflict->theirs = theirs; + + if ((error = checkout_conflict_detect_submodule(conflict)) < 0 || + (error = checkout_conflict_detect_binary(data->repo, conflict)) < 0) + { + git__free(conflict); + return error; + } + + if (git_vector_insert(&data->update_conflicts, conflict)) + return -1; + + return 0; +} + +static int checkout_conflicts_foreach( + checkout_data *data, + git_index *index, + git_iterator *workdir, + git_vector *pathspec, + int (*cb)(const git_index_entry *, const git_index_entry *, const git_index_entry *, void *), + void *payload) { git_index_conflict_iterator *iterator = NULL; const git_index_entry *ancestor, *ours, *theirs; - checkout_conflictdata *conflict; int error = 0; - if ((error = git_index_conflict_iterator_new(&iterator, data->index)) < 0) + if ((error = git_index_conflict_iterator_new(&iterator, index)) < 0) goto done; - data->conflicts._cmp = checkout_conflictdata_cmp; - /* Collect the conflicts */ while ((error = git_index_conflict_next(&ancestor, &ours, &theirs, iterator)) == 0) { if (!conflict_pathspec_match(data, workdir, pathspec, ancestor, ours, theirs)) continue; - conflict = git__calloc(1, sizeof(checkout_conflictdata)); - GITERR_CHECK_ALLOC(conflict); - - conflict->ancestor = ancestor; - conflict->ours = ours; - conflict->theirs = theirs; - - git_vector_insert(&data->conflicts, conflict); + if ((error = cb(ancestor, ours, theirs, payload)) < 0) + goto done; } if (error == GIT_ITEROVER) @@ -710,6 +860,26 @@ return error; } +static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, git_vector *pathspec) +{ + git_index *index; + + /* Only write conficts from sources that have them: indexes. */ + if ((index = git_iterator_get_index(data->target)) == NULL) + return 0; + + data->update_conflicts._cmp = checkout_conflictdata_cmp; + + if (checkout_conflicts_foreach(data, index, workdir, pathspec, checkout_conflict_append_update, data) < 0) + return -1; + + /* Collect the REUC and NAME entries */ + data->update_reuc = &index->reuc; + data->update_names = &index->names; + + return 0; +} + GIT_INLINE(int) checkout_conflicts_cmp_entry( const char *path, const git_index_entry *entry) @@ -734,10 +904,10 @@ { size_t pos; - if (git_vector_bsearch2(&pos, &data->conflicts, checkout_conflicts_cmp_ancestor, path) < 0) + if (git_vector_bsearch2(&pos, &data->update_conflicts, checkout_conflicts_cmp_ancestor, path) < 0) return NULL; - return git_vector_get(&data->conflicts, pos); + return git_vector_get(&data->update_conflicts, pos); } static checkout_conflictdata *checkout_conflicts_search_branch( @@ -747,7 +917,7 @@ checkout_conflictdata *conflict; size_t i; - git_vector_foreach(&data->conflicts, i, conflict) { + git_vector_foreach(&data->update_conflicts, i, conflict) { int cmp = -1; if (conflict->ancestor) @@ -839,16 +1009,20 @@ static int checkout_conflicts_coalesce_renames( checkout_data *data) { + git_index *index; const git_index_name_entry *name_entry; checkout_conflictdata *ancestor_conflict, *our_conflict, *their_conflict; size_t i, names; int error = 0; + if ((index = git_iterator_get_index(data->target)) == NULL) + return 0; + /* Juggle entries based on renames */ - names = git_index_name_entrycount(data->index); - + names = git_index_name_entrycount(index); + for (i = 0; i < names; i++) { - name_entry = git_index_name_get_byindex(data->index, i); + name_entry = git_index_name_get_byindex(index, i); if ((error = checkout_conflicts_load_byname_entry( &ancestor_conflict, &our_conflict, &their_conflict, @@ -882,7 +1056,8 @@ ancestor_conflict->one_to_two = 1; } - git_vector_remove_matching(&data->conflicts, checkout_conflictdata_empty); + git_vector_remove_matching( + &data->update_conflicts, checkout_conflictdata_empty, NULL); done: return error; @@ -891,16 +1066,20 @@ static int checkout_conflicts_mark_directoryfile( checkout_data *data) { + git_index *index; checkout_conflictdata *conflict; const git_index_entry *entry; size_t i, j, len; const char *path; int prefixed, error = 0; - len = git_index_entrycount(data->index); + if ((index = git_iterator_get_index(data->target)) == NULL) + return 0; + + len = git_index_entrycount(index); /* Find d/f conflicts */ - git_vector_foreach(&data->conflicts, i, conflict) { + git_vector_foreach(&data->update_conflicts, i, conflict) { if ((conflict->ours && conflict->theirs) || (!conflict->ours && !conflict->theirs)) continue; @@ -908,7 +1087,7 @@ path = conflict->ours ? conflict->ours->path : conflict->theirs->path; - if ((error = git_index_find(&j, data->index, path)) < 0) { + if ((error = git_index_find(&j, index, path)) < 0) { if (error == GIT_ENOTFOUND) giterr_set(GITERR_INDEX, "Index inconsistency, could not find entry for expected conflict '%s'", path); @@ -917,14 +1096,14 @@ } for (; j < len; j++) { - if ((entry = git_index_get_byindex(data->index, j)) == NULL) { + if ((entry = git_index_get_byindex(index, j)) == NULL) { giterr_set(GITERR_INDEX, "Index inconsistency, truncated index while loading expected conflict '%s'", path); error = -1; goto done; } - prefixed = git_path_equal_or_prefixed(path, entry->path); + prefixed = git_path_equal_or_prefixed(path, entry->path, NULL); if (prefixed == GIT_PATH_EQUAL) continue; @@ -940,7 +1119,7 @@ return error; } -static int checkout_get_conflicts( +static int checkout_get_update_conflicts( checkout_data *data, git_iterator *workdir, git_vector *pathspec) @@ -959,13 +1138,73 @@ return error; } +static int checkout_conflict_append_remove( + const git_index_entry *ancestor, + const git_index_entry *ours, + const git_index_entry *theirs, + void *payload) +{ + checkout_data *data = payload; + const char *name; + + assert(ancestor || ours || theirs); + + if (ancestor) + name = git__strdup(ancestor->path); + else if (ours) + name = git__strdup(ours->path); + else if (theirs) + name = git__strdup(theirs->path); + else + abort(); + + GITERR_CHECK_ALLOC(name); + + return git_vector_insert(&data->remove_conflicts, (char *)name); +} + +static int checkout_get_remove_conflicts( + checkout_data *data, + git_iterator *workdir, + git_vector *pathspec) +{ + if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) != 0) + return 0; + + return checkout_conflicts_foreach(data, data->index, workdir, pathspec, checkout_conflict_append_remove, data); +} + +static int checkout_verify_paths( + git_repository *repo, + int action, + git_diff_delta *delta) +{ + unsigned int flags = GIT_PATH_REJECT_DEFAULTS | GIT_PATH_REJECT_DOT_GIT; + + if (action & CHECKOUT_ACTION__REMOVE) { + if (!git_path_isvalid(repo, delta->old_file.path, flags)) { + giterr_set(GITERR_CHECKOUT, "Cannot remove invalid path '%s'", delta->old_file.path); + return -1; + } + } + + if (action & ~CHECKOUT_ACTION__REMOVE) { + if (!git_path_isvalid(repo, delta->new_file.path, flags)) { + giterr_set(GITERR_CHECKOUT, "Cannot checkout to invalid path '%s'", delta->new_file.path); + return -1; + } + } + + return 0; +} + static int checkout_get_actions( uint32_t **actions_ptr, size_t **counts_ptr, checkout_data *data, git_iterator *workdir) { - int error = 0; + int error = 0, act; const git_index_entry *wditem; git_vector pathspec = GIT_VECTOR_INIT, *deltas; git_pool pathpool = GIT_POOL_INIT_STRINGPOOL; @@ -992,12 +1231,11 @@ } git_vector_foreach(deltas, i, delta) { - int act = checkout_action(data, delta, workdir, &wditem, &pathspec); + if ((error = checkout_action(&act, data, delta, workdir, &wditem, &pathspec)) == 0) + error = checkout_verify_paths(data->repo, act, delta); - if (act < 0) { - error = act; + if (error != 0) goto fail; - } actions[i] = act; @@ -1012,7 +1250,7 @@ } error = checkout_remaining_wd_items(data, workdir, wditem, &pathspec); - if (error < 0) + if (error) goto fail; counts[CHECKOUT_ACTION__REMOVE] += data->removes.length; @@ -1020,17 +1258,21 @@ if (counts[CHECKOUT_ACTION__CONFLICT] > 0 && (data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) == 0) { - giterr_set(GITERR_CHECKOUT, "%d conflicts prevent checkout", - (int)counts[CHECKOUT_ACTION__CONFLICT]); + giterr_set(GITERR_CHECKOUT, "%d %s checkout", + (int)counts[CHECKOUT_ACTION__CONFLICT], + counts[CHECKOUT_ACTION__CONFLICT] == 1 ? + "conflict prevents" : "conflicts prevent"); error = GIT_EMERGECONFLICT; goto fail; } - if ((error = checkout_get_conflicts(data, workdir, &pathspec)) < 0) + if ((error = checkout_get_remove_conflicts(data, workdir, &pathspec)) < 0 || + (error = checkout_get_update_conflicts(data, workdir, &pathspec)) < 0) goto fail; - counts[CHECKOUT_ACTION__UPDATE_CONFLICT] = git_vector_length(&data->conflicts); + counts[CHECKOUT_ACTION__REMOVE_CONFLICT] = git_vector_length(&data->remove_conflicts); + counts[CHECKOUT_ACTION__UPDATE_CONFLICT] = git_vector_length(&data->update_conflicts); git_pathspec__vfree(&pathspec); git_pool_clear(&pathpool); @@ -1049,52 +1291,143 @@ return error; } +static int checkout_mkdir( + checkout_data *data, + const char *path, + const char *base, + mode_t mode, + unsigned int flags) +{ + struct git_futils_mkdir_perfdata mkdir_perfdata = {0}; + + int error = git_futils_mkdir_withperf( + path, base, mode, flags, &mkdir_perfdata); + + data->perfdata.mkdir_calls += mkdir_perfdata.mkdir_calls; + data->perfdata.stat_calls += mkdir_perfdata.stat_calls; + data->perfdata.chmod_calls += mkdir_perfdata.chmod_calls; + + return error; +} + +static bool should_remove_existing(checkout_data *data) +{ + int ignorecase = 0; + + git_repository__cvar(&ignorecase, data->repo, GIT_CVAR_IGNORECASE); + + return (ignorecase && + (data->strategy & GIT_CHECKOUT_DONT_REMOVE_EXISTING) == 0); +} + +#define MKDIR_NORMAL \ + GIT_MKDIR_PATH | GIT_MKDIR_VERIFY_DIR +#define MKDIR_REMOVE_EXISTING \ + MKDIR_NORMAL | GIT_MKDIR_REMOVE_FILES | GIT_MKDIR_REMOVE_SYMLINKS + +static int mkpath2file( + checkout_data *data, const char *path, unsigned int mode) +{ + git_buf *mkdir_path = &data->tmp; + struct stat st; + bool remove_existing = should_remove_existing(data); + int error; + + if ((error = git_buf_sets(mkdir_path, path)) < 0) + return error; + + git_buf_rtruncate_at_char(mkdir_path, '/'); + + if (!data->last_mkdir.size || + data->last_mkdir.size != mkdir_path->size || + memcmp(mkdir_path->ptr, data->last_mkdir.ptr, mkdir_path->size) != 0) { + + if ((error = checkout_mkdir( + data, mkdir_path->ptr, data->opts.target_directory, mode, + remove_existing ? MKDIR_REMOVE_EXISTING : MKDIR_NORMAL)) < 0) + return error; + + git_buf_swap(&data->last_mkdir, mkdir_path); + } + + if (remove_existing) { + data->perfdata.stat_calls++; + + if (p_lstat(path, &st) == 0) { + + /* Some file, symlink or folder already exists at this name. + * We would have removed it in remove_the_old unless we're on + * a case inensitive filesystem (or the user has asked us not + * to). Remove the similarly named file to write the new. + */ + error = git_futils_rmdir_r(path, NULL, GIT_RMDIR_REMOVE_FILES); + } else if (errno != ENOENT) { + giterr_set(GITERR_OS, "Failed to stat file '%s'", path); + return GIT_EEXISTS; + } else { + giterr_clear(); + } + } + + return error; +} + static int buffer_to_file( + checkout_data *data, struct stat *st, git_buf *buf, const char *path, - mode_t dir_mode, - int file_open_flags, mode_t file_mode) { int error; - if ((error = git_futils_mkpath2file(path, dir_mode)) < 0) + if ((error = mkpath2file(data, path, data->opts.dir_mode)) < 0) return error; if ((error = git_futils_writebuffer( - buf, path, file_open_flags, file_mode)) < 0) + buf, path, data->opts.file_open_flags, file_mode)) < 0) return error; - if (st != NULL && (error = p_stat(path, st)) < 0) - giterr_set(GITERR_OS, "Error statting '%s'", path); + if (st) { + data->perfdata.stat_calls++; - else if (GIT_PERMS_IS_EXEC(file_mode) && - (error = p_chmod(path, file_mode)) < 0) - giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path); + if ((error = p_stat(path, st)) < 0) { + giterr_set(GITERR_OS, "Error statting '%s'", path); + return error; + } + } + + if (GIT_PERMS_IS_EXEC(file_mode)) { + data->perfdata.chmod_calls++; + + if ((error = p_chmod(path, file_mode)) < 0) + giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path); + } return error; } static int blob_content_to_file( + checkout_data *data, struct stat *st, git_blob *blob, const char *path, const char * hint_path, - mode_t entry_filemode, - git_checkout_opts *opts) + mode_t entry_filemode) { - int error = 0; - mode_t file_mode = opts->file_mode ? opts->file_mode : entry_filemode; + mode_t file_mode = data->opts.file_mode ? + data->opts.file_mode : entry_filemode; git_buf out = GIT_BUF_INIT; git_filter_list *fl = NULL; + int error = 0; if (hint_path == NULL) hint_path = path; - if (!opts->disable_filters) + if (!data->opts.disable_filters) error = git_filter_list_load( - &fl, git_blob_owner(blob), blob, hint_path, GIT_FILTER_TO_WORKTREE); + &fl, git_blob_owner(blob), blob, hint_path, + GIT_FILTER_TO_WORKTREE, GIT_FILTER_OPT_DEFAULT); if (!error) error = git_filter_list_apply_to_blob(&out, fl, blob); @@ -1102,9 +1435,7 @@ git_filter_list_free(fl); if (!error) { - error = buffer_to_file( - st, &out, path, opts->dir_mode, opts->file_open_flags, file_mode); - + error = buffer_to_file(data, st, &out, path, file_mode); st->st_mode = entry_filemode; git_buf_free(&out); @@ -1114,29 +1445,30 @@ } static int blob_content_to_link( + checkout_data *data, struct stat *st, git_blob *blob, - const char *path, - mode_t dir_mode, - int can_symlink) + const char *path) { git_buf linktarget = GIT_BUF_INIT; int error; - if ((error = git_futils_mkpath2file(path, dir_mode)) < 0) + if ((error = mkpath2file(data, path, data->opts.dir_mode)) < 0) return error; if ((error = git_blob__getbuf(&linktarget, blob)) < 0) return error; - if (can_symlink) { + if (data->can_symlink) { if ((error = p_symlink(git_buf_cstr(&linktarget), path)) < 0) - giterr_set(GITERR_OS, "Could not create symlink %s\n", path); + giterr_set(GITERR_OS, "Could not create symlink %s", path); } else { error = git_futils_fake_symlink(git_buf_cstr(&linktarget), path); } if (!error) { + data->perfdata.stat_calls++; + if ((error = p_lstat(path, st)) < 0) giterr_set(GITERR_CHECKOUT, "Could not stat symlink %s", path); @@ -1160,9 +1492,8 @@ memset(&entry, 0, sizeof(entry)); entry.path = (char *)file->path; /* cast to prevent warning */ - git_index_entry__init_from_stat( - &entry, st, !(git_index_caps(data->index) & GIT_INDEXCAP_NO_FILEMODE)); - git_oid_cpy(&entry.oid, &file->oid); + git_index_entry__init_from_stat(&entry, st, true); + git_oid_cpy(&entry.id, &file->id); return git_index_add(data->index, &entry); } @@ -1181,6 +1512,7 @@ if (git_buf_puts(&data->path, file->path) < 0) return -1; + data->perfdata.stat_calls++; if (p_stat(git_buf_cstr(&data->path), &st) < 0) { giterr_set( GITERR_CHECKOUT, "Could not stat submodule %s\n", file->path); @@ -1196,19 +1528,20 @@ checkout_data *data, const git_diff_file *file) { + bool remove_existing = should_remove_existing(data); int error = 0; - git_submodule *sm; /* Until submodules are supported, UPDATE_ONLY means do nothing here */ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) return 0; - if ((error = git_futils_mkdir( - file->path, data->opts.target_directory, - data->opts.dir_mode, GIT_MKDIR_PATH)) < 0) + if ((error = checkout_mkdir( + data, + file->path, data->opts.target_directory, data->opts.dir_mode, + remove_existing ? MKDIR_REMOVE_EXISTING : MKDIR_NORMAL)) < 0) return error; - if ((error = git_submodule_lookup(&sm, data->repo, file->path)) < 0) { + if ((error = git_submodule_lookup(NULL, data->repo, file->path)) < 0) { /* I've observed repos with submodules in the tree that do not * have a .gitmodules - core Git just makes an empty directory */ @@ -1242,10 +1575,13 @@ data->opts.progress_payload); } -static int checkout_safe_for_update_only(const char *path, mode_t expected_mode) +static int checkout_safe_for_update_only( + checkout_data *data, const char *path, mode_t expected_mode) { struct stat st; + data->perfdata.stat_calls++; + if (p_lstat(path, &st) < 0) { /* if doesn't exist, then no error and no update */ if (errno == ENOENT || errno == ENOTDIR) @@ -1278,11 +1614,9 @@ return error; if (S_ISLNK(mode)) - error = blob_content_to_link( - st, blob, full_path, data->opts.dir_mode, data->can_symlink); + error = blob_content_to_link(data, st, blob, full_path); else - error = blob_content_to_file( - st, blob, full_path, hint_path, mode, &data->opts); + error = blob_content_to_file(data, st, blob, full_path, hint_path, mode); git_blob_free(blob); @@ -1313,13 +1647,13 @@ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) { int rval = checkout_safe_for_update_only( - git_buf_cstr(&data->path), file->mode); + data, git_buf_cstr(&data->path), file->mode); if (rval <= 0) return rval; } error = checkout_write_content( - data, &file->oid, git_buf_cstr(&data->path), NULL, file->mode, &st); + data, &file->id, git_buf_cstr(&data->path), NULL, file->mode, &st); /* update the index unless prevented */ if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0) @@ -1449,7 +1783,7 @@ /* initial reload of submodules if .gitmodules was changed */ if (data->reload_submodules && - (error = git_submodule_reload_all(data->repo)) < 0) + (error = git_submodule_reload_all(data->repo, 1)) < 0) return error; git_vector_foreach(&data->diff->deltas, i, delta) { @@ -1473,7 +1807,7 @@ } /* final reload once submodules have been updated */ - return git_submodule_reload_all(data->repo); + return git_submodule_reload_all(data->repo, 1); } static int checkout_lookup_head_tree(git_tree **out, git_repository *repo) @@ -1568,11 +1902,11 @@ } if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 && - (error = checkout_safe_for_update_only(git_buf_cstr(&data->path), side->mode)) <= 0) + (error = checkout_safe_for_update_only(data, git_buf_cstr(&data->path), side->mode)) <= 0) return error; return checkout_write_content(data, - &side->oid, git_buf_cstr(&data->path), hint_path, side->mode, &st); + &side->id, git_buf_cstr(&data->path), hint_path, side->mode, &st); } static int checkout_write_entries( @@ -1619,26 +1953,23 @@ checkout_conflictdata *conflict) { git_buf our_label = GIT_BUF_INIT, their_label = GIT_BUF_INIT, - path_suffixed = GIT_BUF_INIT, path_workdir = GIT_BUF_INIT; - 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_result result = GIT_MERGE_FILE_RESULT_INIT; + path_suffixed = GIT_BUF_INIT, path_workdir = GIT_BUF_INIT, + in_data = GIT_BUF_INIT, out_data = GIT_BUF_INIT; + git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT; + git_merge_file_result result = {0}; git_filebuf output = GIT_FILEBUF_INIT; + git_filter_list *fl = NULL; int error = 0; - if ((conflict->ancestor && - (error = git_merge_file_input_from_index_entry( - &ancestor, data->repo, conflict->ancestor)) < 0) || - (error = git_merge_file_input_from_index_entry( - &ours, data->repo, conflict->ours)) < 0 || - (error = git_merge_file_input_from_index_entry( - &theirs, data->repo, conflict->theirs)) < 0) - goto done; + if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_DIFF3) + opts.flags |= GIT_MERGE_FILE_STYLE_DIFF3; - ancestor.label = NULL; - ours.label = data->opts.our_label ? data->opts.our_label : "ours"; - theirs.label = data->opts.their_label ? data->opts.their_label : "theirs"; + opts.ancestor_label = data->opts.ancestor_label ? + data->opts.ancestor_label : "ancestor"; + opts.our_label = data->opts.our_label ? + data->opts.our_label : "ours"; + opts.their_label = data->opts.their_label ? + data->opts.their_label : "theirs"; /* If all the paths are identical, decorate the diff3 file with the branch * names. Otherwise, append branch_name:path. @@ -1647,16 +1978,17 @@ strcmp(conflict->ours->path, conflict->theirs->path) != 0) { if ((error = conflict_entry_name( - &our_label, ours.label, conflict->ours->path)) < 0 || + &our_label, opts.our_label, conflict->ours->path)) < 0 || (error = conflict_entry_name( - &their_label, theirs.label, conflict->theirs->path)) < 0) + &their_label, opts.their_label, conflict->theirs->path)) < 0) goto done; - ours.label = git_buf_cstr(&our_label); - theirs.label = git_buf_cstr(&their_label); + opts.our_label = git_buf_cstr(&our_label); + opts.their_label = git_buf_cstr(&their_label); } - if ((error = git_merge_files(&result, &ancestor, &ours, &theirs, 0)) < 0) + if ((error = git_merge_file_from_index(&result, data->repo, + conflict->ancestor, conflict->ours, conflict->theirs, &opts)) < 0) goto done; if (result.path == NULL || result.mode == 0) { @@ -1669,22 +2001,35 @@ goto done; if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 && - (error = checkout_safe_for_update_only(git_buf_cstr(&path_workdir), result.mode)) <= 0) + (error = checkout_safe_for_update_only(data, git_buf_cstr(&path_workdir), result.mode)) <= 0) goto done; - if ((error = git_futils_mkpath2file(path_workdir.ptr, 0755)) < 0 || - (error = git_filebuf_open(&output, path_workdir.ptr, GIT_FILEBUF_DO_NOT_BUFFER, result.mode)) < 0 || - (error = git_filebuf_write(&output, result.data, result.len)) < 0 || + if (!data->opts.disable_filters) { + in_data.ptr = (char *)result.ptr; + in_data.size = result.len; + + if ((error = git_filter_list_load(&fl, data->repo, NULL, git_buf_cstr(&path_workdir), + GIT_FILTER_TO_WORKTREE, GIT_FILTER_OPT_DEFAULT)) < 0 || + (error = git_filter_list_apply_to_data(&out_data, fl, &in_data)) < 0) + goto done; + } else { + out_data.ptr = (char *)result.ptr; + out_data.size = result.len; + } + + if ((error = mkpath2file(data, path_workdir.ptr, data->opts.dir_mode)) < 0 || + (error = git_filebuf_open(&output, git_buf_cstr(&path_workdir), GIT_FILEBUF_DO_NOT_BUFFER, result.mode)) < 0 || + (error = git_filebuf_write(&output, out_data.ptr, out_data.size)) < 0 || (error = git_filebuf_commit(&output)) < 0) goto done; done: + git_filter_list_free(fl); + + git_buf_free(&out_data); git_buf_free(&our_label); git_buf_free(&their_label); - git_merge_file_input_free(&ancestor); - git_merge_file_input_free(&ours); - git_merge_file_input_free(&theirs); git_merge_file_result_free(&result); git_buf_free(&path_workdir); git_buf_free(&path_suffixed); @@ -1692,13 +2037,46 @@ return error; } +static int checkout_conflict_add( + checkout_data *data, + const git_index_entry *conflict) +{ + int error = git_index_remove(data->index, conflict->path, 0); + + if (error == GIT_ENOTFOUND) + giterr_clear(); + else if (error < 0) + return error; + + return git_index_add(data->index, conflict); +} + +static int checkout_conflict_update_index( + checkout_data *data, + checkout_conflictdata *conflict) +{ + int error = 0; + + if (conflict->ancestor) + error = checkout_conflict_add(data, conflict->ancestor); + + if (!error && conflict->ours) + error = checkout_conflict_add(data, conflict->ours); + + if (!error && conflict->theirs) + error = checkout_conflict_add(data, conflict->theirs); + + return error; +} + static int checkout_create_conflicts(checkout_data *data) { checkout_conflictdata *conflict; size_t i; int error = 0; - git_vector_foreach(&data->conflicts, i, conflict) { + git_vector_foreach(&data->update_conflicts, i, conflict) { + /* Both deleted: nothing to do */ if (conflict->ours == NULL && conflict->theirs == NULL) error = 0; @@ -1742,9 +2120,23 @@ else if (S_ISLNK(conflict->theirs->mode)) error = checkout_write_entry(data, conflict, conflict->ours); - else + /* If any side is a gitlink, do nothing. */ + else if (conflict->submodule) + error = 0; + + /* If any side is binary, write the ours side */ + else if (conflict->binary) + error = checkout_write_entry(data, conflict, conflict->ours); + + else if (!error) error = checkout_write_merge(data, conflict); + /* Update the index extensions (REUC and NAME) if we're checking + * out a different index. (Otherwise just leave them there.) + */ + if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0) + error = checkout_conflict_update_index(data, conflict); + if (error) break; @@ -1757,12 +2149,55 @@ return error; } +static int checkout_remove_conflicts(checkout_data *data) +{ + const char *conflict; + size_t i; + + git_vector_foreach(&data->remove_conflicts, i, conflict) { + if (git_index_conflict_remove(data->index, conflict) < 0) + return -1; -static void checkout_data_clear(checkout_data *data) + data->completed_steps++; + } + + return 0; +} + +static int checkout_extensions_update_index(checkout_data *data) { - checkout_conflictdata *conflict; + const git_index_reuc_entry *reuc_entry; + const git_index_name_entry *name_entry; size_t i; + int error = 0; + + if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) + return 0; + if (data->update_reuc) { + git_vector_foreach(data->update_reuc, i, reuc_entry) { + if ((error = git_index_reuc_add(data->index, reuc_entry->path, + reuc_entry->mode[0], &reuc_entry->oid[0], + reuc_entry->mode[1], &reuc_entry->oid[1], + reuc_entry->mode[2], &reuc_entry->oid[2])) < 0) + goto done; + } + } + + if (data->update_names) { + git_vector_foreach(data->update_names, i, name_entry) { + if ((error = git_index_name_add(data->index, name_entry->ancestor, + name_entry->ours, name_entry->theirs)) < 0) + goto done; + } + } + +done: + return error; +} + +static void checkout_data_clear(checkout_data *data) +{ if (data->opts_free_baseline) { git_tree_free(data->opts.baseline); data->opts.baseline = NULL; @@ -1771,15 +2206,15 @@ git_vector_free(&data->removes); git_pool_clear(&data->pool); - git_vector_foreach(&data->conflicts, i, conflict) - git__free(conflict); - - git_vector_free(&data->conflicts); + git_vector_free_deep(&data->remove_conflicts); + git_vector_free_deep(&data->update_conflicts); git__free(data->pfx); data->pfx = NULL; + git_buf_free(&data->last_mkdir); git_buf_free(&data->path); + git_buf_free(&data->tmp); git_index_free(data->index); data->index = NULL; @@ -1788,7 +2223,7 @@ static int checkout_data_init( checkout_data *data, git_iterator *target, - const git_checkout_opts *proposed) + const git_checkout_options *proposed) { int error = 0; git_repository *repo = git_iterator_owner(target); @@ -1805,39 +2240,40 @@ return error; data->repo = repo; + data->target = target; GITERR_CHECK_VERSION( - proposed, GIT_CHECKOUT_OPTS_VERSION, "git_checkout_opts"); + proposed, GIT_CHECKOUT_OPTIONS_VERSION, "git_checkout_options"); if (!proposed) - GIT_INIT_STRUCTURE(&data->opts, GIT_CHECKOUT_OPTS_VERSION); + GIT_INIT_STRUCTURE(&data->opts, GIT_CHECKOUT_OPTIONS_VERSION); else - memmove(&data->opts, proposed, sizeof(git_checkout_opts)); + memmove(&data->opts, proposed, sizeof(git_checkout_options)); if (!data->opts.target_directory) data->opts.target_directory = git_repository_workdir(repo); else if (!git_path_isdir(data->opts.target_directory) && - (error = git_futils_mkdir(data->opts.target_directory, NULL, - GIT_DIR_MODE, GIT_MKDIR_VERIFY_DIR)) < 0) + (error = checkout_mkdir(data, + data->opts.target_directory, NULL, + GIT_DIR_MODE, GIT_MKDIR_VERIFY_DIR)) < 0) goto cleanup; /* refresh config and index content unless NO_REFRESH is given */ if ((data->opts.checkout_strategy & GIT_CHECKOUT_NO_REFRESH) == 0) { git_config *cfg; - if ((error = git_repository_config__weakptr(&cfg, repo)) < 0 || - (error = git_config_refresh(cfg)) < 0) + if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) goto cleanup; - /* if we are checking out the index, don't reload, - * otherwise get index and force reload + /* Get the repository index and reload it (unless we're checking + * out the index; then it has the changes we're trying to check + * out and those should not be overwritten.) */ - if ((data->index = git_iterator_get_index(target)) != NULL) { - GIT_REFCOUNT_INC(data->index); - } else { - /* otherwise, grab and reload the index */ - if ((error = git_repository_index(&data->index, data->repo)) < 0 || - (error = git_index_read(data->index, true)) < 0) + if ((error = git_repository_index(&data->index, data->repo)) < 0) + goto cleanup; + + if (data->index != git_iterator_get_index(target)) { + if ((error = git_index_read(data->index, true)) < 0) goto cleanup; /* cannot checkout if unresolved conflicts exist */ @@ -1849,7 +2285,7 @@ goto cleanup; } - /* clean conflict data when doing a tree or commit checkout */ + /* clean conflict data in the current index */ git_index_name_clear(data->index); git_index_reuc_clear(data->index); } @@ -1891,8 +2327,32 @@ goto cleanup; } + if ((data->opts.checkout_strategy & + (GIT_CHECKOUT_CONFLICT_STYLE_MERGE | GIT_CHECKOUT_CONFLICT_STYLE_DIFF3)) == 0) { + const char *conflict_style; + git_config *cfg = NULL; + + if ((error = git_repository_config__weakptr(&cfg, repo)) < 0 || + (error = git_config_get_string(&conflict_style, cfg, "merge.conflictstyle")) < 0 || + error == GIT_ENOTFOUND) + ; + else if (error) + goto cleanup; + else if (strcmp(conflict_style, "merge") == 0) + data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_MERGE; + else if (strcmp(conflict_style, "diff3") == 0) + data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_DIFF3; + else { + giterr_set(GITERR_CHECKOUT, "unknown style '%s' given for 'merge.conflictstyle'", + conflict_style); + error = -1; + goto cleanup; + } + } + if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 || - (error = git_vector_init(&data->conflicts, 0, NULL)) < 0 || + (error = git_vector_init(&data->remove_conflicts, 0, NULL)) < 0 || + (error = git_vector_init(&data->update_conflicts, 0, NULL)) < 0 || (error = git_pool_init(&data->pool, 1, 0)) < 0 || (error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 || (error = git_path_to_dir(&data->path)) < 0) @@ -1909,7 +2369,8 @@ int git_checkout_iterator( git_iterator *target, - const git_checkout_opts *opts) + git_index *index, + const git_checkout_options *opts) { int error = 0; git_iterator *baseline = NULL, *workdir = NULL; @@ -1926,6 +2387,7 @@ diff_opts.flags = GIT_DIFF_INCLUDE_UNMODIFIED | + GIT_DIFF_INCLUDE_UNREADABLE | GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_RECURSE_UNTRACKED_DIRS | /* needed to match baseline */ GIT_DIFF_INCLUDE_IGNORED | @@ -1944,7 +2406,7 @@ if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 || (error = git_iterator_for_workdir_ext( - &workdir, data.repo, data.opts.target_directory, + &workdir, data.repo, data.opts.target_directory, index, NULL, iterflags | GIT_ITERATOR_DONT_AUTOEXPAND, data.pfx, data.pfx)) < 0 || (error = git_iterator_for_tree( @@ -1966,10 +2428,11 @@ * actions to be taken, plus look for conflicts and send notifications, * then loop through conflicts. */ - if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) < 0) + if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) != 0) goto cleanup; data.total_steps = counts[CHECKOUT_ACTION__REMOVE] + + counts[CHECKOUT_ACTION__REMOVE_CONFLICT] + counts[CHECKOUT_ACTION__UPDATE_BLOB] + counts[CHECKOUT_ACTION__UPDATE_SUBMODULE] + counts[CHECKOUT_ACTION__UPDATE_CONFLICT]; @@ -1983,6 +2446,10 @@ (error = checkout_remove_the_old(actions, &data)) < 0) goto cleanup; + if (counts[CHECKOUT_ACTION__REMOVE_CONFLICT] > 0 && + (error = checkout_remove_conflicts(&data)) < 0) + goto cleanup; + if (counts[CHECKOUT_ACTION__UPDATE_BLOB] > 0 && (error = checkout_create_the_new(actions, &data)) < 0) goto cleanup; @@ -1995,12 +2462,13 @@ (error = checkout_create_conflicts(&data)) < 0) goto cleanup; + if (data.index != git_iterator_get_index(target) && + (error = checkout_extensions_update_index(&data)) < 0) + goto cleanup; + assert(data.completed_steps == data.total_steps); cleanup: - if (error == GIT_EUSER) - giterr_clear(); - if (!error && data.index != NULL && (data.strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0) error = git_index_write(data.index); @@ -2018,9 +2486,9 @@ int git_checkout_index( git_repository *repo, git_index *index, - const git_checkout_opts *opts) + const git_checkout_options *opts) { - int error; + int error, owned = 0; git_iterator *index_i; if (!index && !repo) { @@ -2028,10 +2496,16 @@ "Must provide either repository or index to checkout"); return -1; } - if (index && repo && git_index_owner(index) != repo) { + + if (index && repo && + git_index_owner(index) && + git_index_owner(index) != repo) { giterr_set(GITERR_CHECKOUT, "Index to checkout does not match repository"); return -1; + } else if(index && repo && !git_index_owner(index)) { + GIT_REFCOUNT_OWN(index, repo); + owned = 1; } if (!repo) @@ -2042,7 +2516,10 @@ GIT_REFCOUNT_INC(index); if (!(error = git_iterator_for_index(&index_i, index, 0, NULL, NULL))) - error = git_checkout_iterator(index_i, opts); + error = git_checkout_iterator(index_i, index, opts); + + if (owned) + GIT_REFCOUNT_OWN(index, NULL); git_iterator_free(index_i); git_index_free(index); @@ -2053,9 +2530,10 @@ int git_checkout_tree( git_repository *repo, const git_object *treeish, - const git_checkout_opts *opts) + const git_checkout_options *opts) { int error; + git_index *index; git_tree *tree = NULL; git_iterator *tree_i = NULL; @@ -2090,10 +2568,14 @@ } } - if (!(error = git_iterator_for_tree(&tree_i, tree, 0, NULL, NULL))) - error = git_checkout_iterator(tree_i, opts); + if ((error = git_repository_index(&index, repo)) < 0) + return error; + + if (!(error = git_iterator_for_tree(&tree_i, tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL))) + error = git_checkout_iterator(tree_i, index, opts); git_iterator_free(tree_i); + git_index_free(index); git_tree_free(tree); return error; @@ -2101,8 +2583,15 @@ int git_checkout_head( git_repository *repo, - const git_checkout_opts *opts) + const git_checkout_options *opts) { assert(repo); return git_checkout_tree(repo, NULL, opts); } + +int git_checkout_init_options(git_checkout_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_checkout_options, GIT_CHECKOUT_OPTIONS_INIT); + return 0; +} diff -Nru libgit2-0.20.0/src/checkout.h libgit2-0.22.2/src/checkout.h --- libgit2-0.20.0/src/checkout.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/checkout.h 2015-03-24 16:10:45.000000000 +0000 @@ -12,6 +12,13 @@ #define GIT_CHECKOUT__NOTIFY_CONFLICT_TREE (1u << 12) +/** Internal structure; this is exposed in future versions. */ +typedef struct { + size_t mkdir_calls; + size_t stat_calls; + size_t chmod_calls; +} git_checkout_perfdata; + /** * Update the working directory to match the target iterator. The * expected baseline value can be passed in via the checkout options @@ -19,6 +26,7 @@ */ extern int git_checkout_iterator( git_iterator *target, - const git_checkout_opts *opts); + git_index *index, + const git_checkout_options *opts); #endif diff -Nru libgit2-0.20.0/src/cherrypick.c libgit2-0.22.2/src/cherrypick.c --- libgit2-0.20.0/src/cherrypick.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/cherrypick.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,223 @@ +/* +* 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 "repository.h" +#include "filebuf.h" +#include "merge.h" +#include "vector.h" + +#include "git2/types.h" +#include "git2/merge.h" +#include "git2/cherrypick.h" +#include "git2/commit.h" +#include "git2/sys/commit.h" + +#define GIT_CHERRYPICK_FILE_MODE 0666 + +static int write_cherrypick_head( + git_repository *repo, + const char *commit_oidstr) +{ + git_filebuf file = GIT_FILEBUF_INIT; + git_buf file_path = GIT_BUF_INIT; + int error = 0; + + if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_CHERRYPICK_HEAD_FILE)) >= 0 && + (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRYPICK_FILE_MODE)) >= 0 && + (error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0) + error = git_filebuf_commit(&file); + + if (error < 0) + git_filebuf_cleanup(&file); + + git_buf_free(&file_path); + + return error; +} + +static int write_merge_msg( + git_repository *repo, + const char *commit_msg) +{ + git_filebuf file = GIT_FILEBUF_INIT; + git_buf file_path = GIT_BUF_INIT; + int error = 0; + + if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 || + (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_CHERRYPICK_FILE_MODE)) < 0 || + (error = git_filebuf_printf(&file, "%s", commit_msg)) < 0) + goto cleanup; + + error = git_filebuf_commit(&file); + +cleanup: + if (error < 0) + git_filebuf_cleanup(&file); + + git_buf_free(&file_path); + + return error; +} + +static int cherrypick_normalize_opts( + git_repository *repo, + git_cherrypick_options *opts, + const git_cherrypick_options *given, + const char *their_label) +{ + int error = 0; + unsigned int default_checkout_strategy = GIT_CHECKOUT_SAFE_CREATE | + GIT_CHECKOUT_ALLOW_CONFLICTS; + + GIT_UNUSED(repo); + + if (given != NULL) + memcpy(opts, given, sizeof(git_cherrypick_options)); + else { + git_cherrypick_options default_opts = GIT_CHERRYPICK_OPTIONS_INIT; + memcpy(opts, &default_opts, sizeof(git_cherrypick_options)); + } + + if (!opts->checkout_opts.checkout_strategy) + opts->checkout_opts.checkout_strategy = default_checkout_strategy; + + if (!opts->checkout_opts.our_label) + opts->checkout_opts.our_label = "HEAD"; + + if (!opts->checkout_opts.their_label) + opts->checkout_opts.their_label = their_label; + + return error; +} + +static int cherrypick_state_cleanup(git_repository *repo) +{ + const char *state_files[] = { GIT_CHERRYPICK_HEAD_FILE, GIT_MERGE_MSG_FILE }; + + return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files)); +} + +static int cherrypick_seterr(git_commit *commit, const char *fmt) +{ + char commit_oidstr[GIT_OID_HEXSZ + 1]; + + giterr_set(GITERR_CHERRYPICK, fmt, + git_oid_tostr(commit_oidstr, GIT_OID_HEXSZ + 1, git_commit_id(commit))); + + return -1; +} + +int git_cherrypick_commit( + git_index **out, + git_repository *repo, + git_commit *cherrypick_commit, + git_commit *our_commit, + unsigned int mainline, + const git_merge_options *merge_opts) +{ + git_commit *parent_commit = NULL; + git_tree *parent_tree = NULL, *our_tree = NULL, *cherrypick_tree = NULL; + int parent = 0, error = 0; + + assert(out && repo && cherrypick_commit && our_commit); + + if (git_commit_parentcount(cherrypick_commit) > 1) { + if (!mainline) + return cherrypick_seterr(cherrypick_commit, + "Mainline branch is not specified but %s is a merge commit"); + + parent = mainline; + } else { + if (mainline) + return cherrypick_seterr(cherrypick_commit, + "Mainline branch specified but %s is not a merge commit"); + + parent = git_commit_parentcount(cherrypick_commit); + } + + if (parent && + ((error = git_commit_parent(&parent_commit, cherrypick_commit, (parent - 1))) < 0 || + (error = git_commit_tree(&parent_tree, parent_commit)) < 0)) + goto done; + + if ((error = git_commit_tree(&cherrypick_tree, cherrypick_commit)) < 0 || + (error = git_commit_tree(&our_tree, our_commit)) < 0) + goto done; + + error = git_merge_trees(out, repo, parent_tree, our_tree, cherrypick_tree, merge_opts); + +done: + git_tree_free(parent_tree); + git_tree_free(our_tree); + git_tree_free(cherrypick_tree); + git_commit_free(parent_commit); + + return error; +} + +int git_cherrypick( + git_repository *repo, + git_commit *commit, + const git_cherrypick_options *given_opts) +{ + git_cherrypick_options opts; + git_reference *our_ref = NULL; + git_commit *our_commit = NULL; + char commit_oidstr[GIT_OID_HEXSZ + 1]; + const char *commit_msg, *commit_summary; + git_buf their_label = GIT_BUF_INIT; + git_index *index_new = NULL; + int error = 0; + + assert(repo && commit); + + GITERR_CHECK_VERSION(given_opts, GIT_CHERRYPICK_OPTIONS_VERSION, "git_cherrypick_options"); + + if ((error = git_repository__ensure_not_bare(repo, "cherry-pick")) < 0) + return error; + + if ((commit_msg = git_commit_message(commit)) == NULL || + (commit_summary = git_commit_summary(commit)) == NULL) { + error = -1; + goto on_error; + } + + git_oid_nfmt(commit_oidstr, sizeof(commit_oidstr), git_commit_id(commit)); + + if ((error = write_merge_msg(repo, commit_msg)) < 0 || + (error = git_buf_printf(&their_label, "%.7s... %s", commit_oidstr, commit_summary)) < 0 || + (error = cherrypick_normalize_opts(repo, &opts, given_opts, git_buf_cstr(&their_label))) < 0 || + (error = write_cherrypick_head(repo, commit_oidstr)) < 0 || + (error = git_repository_head(&our_ref, repo)) < 0 || + (error = git_reference_peel((git_object **)&our_commit, our_ref, GIT_OBJ_COMMIT)) < 0 || + (error = git_cherrypick_commit(&index_new, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 || + (error = git_merge__check_result(repo, index_new)) < 0 || + (error = git_merge__append_conflicts_to_merge_msg(repo, index_new)) < 0 || + (error = git_checkout_index(repo, index_new, &opts.checkout_opts)) < 0) + goto on_error; + goto done; + +on_error: + cherrypick_state_cleanup(repo); + +done: + git_index_free(index_new); + git_commit_free(our_commit); + git_reference_free(our_ref); + git_buf_free(&their_label); + + return error; +} + +int git_cherrypick_init_options( + git_cherrypick_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_cherrypick_options, GIT_CHERRYPICK_OPTIONS_INIT); + return 0; +} diff -Nru libgit2-0.20.0/src/clone.c libgit2-0.22.2/src/clone.c --- libgit2-0.20.0/src/clone.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/clone.c 2015-03-24 16:10:45.000000000 +0000 @@ -22,12 +22,17 @@ #include "refs.h" #include "path.h" #include "repository.h" +#include "odb.h" + +static int clone_local_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, int link, const git_signature *signature); static int create_branch( git_reference **branch, git_repository *repo, const git_oid *target, - const char *name) + const char *name, + const git_signature *signature, + const char *log_message) { git_commit *head_obj = NULL; git_reference *branch_ref = NULL; @@ -38,7 +43,7 @@ return error; /* Create the new branch */ - error = git_branch_create(&branch_ref, repo, name, head_obj, 0); + error = git_branch_create(&branch_ref, repo, name, head_obj, 0, signature, log_message); git_commit_free(head_obj); @@ -87,11 +92,13 @@ git_reference **branch, git_repository *repo, const git_oid *target, - const char *branch_name) + const char *branch_name, + const git_signature *signature, + const char *log_message) { int error; - if ((error = create_branch(branch, repo, target, branch_name)) < 0) + if ((error = create_branch(branch, repo, target, branch_name, signature, log_message)) < 0) return error; return setup_tracking_config( @@ -101,168 +108,105 @@ git_reference_name(*branch)); } -struct head_info { - git_repository *repo; - git_oid remote_head_oid; - git_buf branchname; - const git_refspec *refspec; - bool found; -}; - -static int reference_matches_remote_head( - const char *reference_name, - void *payload) -{ - struct head_info *head_info = (struct head_info *)payload; - git_oid oid; - - /* TODO: Should we guard against references - * which name doesn't start with refs/heads/ ? - */ - - /* Stop looking if we've already found a match */ - if (head_info->found) - return 0; - - if (git_reference_name_to_id( - &oid, - head_info->repo, - reference_name) < 0) { - /* If the reference doesn't exists, it obviously cannot match the expected oid. */ - giterr_clear(); - return 0; - } - - if (git_oid__cmp(&head_info->remote_head_oid, &oid) == 0) { - /* Determine the local reference name from the remote tracking one */ - if (git_refspec_transform_l( - &head_info->branchname, - head_info->refspec, - reference_name) < 0) - return -1; - - if (git_buf_len(&head_info->branchname) > 0) { - if (git_buf_sets( - &head_info->branchname, - git_buf_cstr(&head_info->branchname) + strlen(GIT_REFS_HEADS_DIR)) < 0) - return -1; - - head_info->found = 1; - } - } - - return 0; -} - static int update_head_to_new_branch( git_repository *repo, const git_oid *target, - const char *name) + const char *name, + const git_signature *signature, + const char *reflog_message) { git_reference *tracking_branch = NULL; int error; - if ((error = create_tracking_branch( - &tracking_branch, - repo, - target, - name)) < 0) - return error; + if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR)) + name += strlen(GIT_REFS_HEADS_DIR); + + error = create_tracking_branch(&tracking_branch, repo, target, name, + signature, reflog_message); - error = git_repository_set_head(repo, git_reference_name(tracking_branch)); + if (!error) + error = git_repository_set_head( + repo, git_reference_name(tracking_branch), + signature, reflog_message); git_reference_free(tracking_branch); + /* if it already existed, then the user's refspec created it for us, ignore it' */ + if (error == GIT_EEXISTS) + error = 0; + return error; } -static int update_head_to_remote(git_repository *repo, git_remote *remote) +static int update_head_to_remote( + git_repository *repo, + git_remote *remote, + const git_signature *signature, + const char *reflog_message) { - int retcode = -1; + int error = 0; size_t refs_len; - git_refspec dummy_spec; + git_refspec *refspec; const git_remote_head *remote_head, **refs; - struct head_info head_info; + const git_oid *remote_head_id; git_buf remote_master_name = GIT_BUF_INIT; + git_buf branch = GIT_BUF_INIT; - if (git_remote_ls(&refs, &refs_len, remote) < 0) - return -1; + if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0) + return error; - /* Did we just clone an empty repository? */ - if (refs_len == 0) { + /* We cloned an empty repository or one with an unborn HEAD */ + if (refs_len == 0 || strcmp(refs[0]->name, GIT_HEAD_FILE)) return setup_tracking_config( - repo, - "master", - GIT_REMOTE_ORIGIN, - GIT_REFS_HEADS_MASTER_FILE); - } + repo, "master", GIT_REMOTE_ORIGIN, GIT_REFS_HEADS_MASTER_FILE); - /* Get the remote's HEAD. This is always the first ref in the list. */ + /* We know we have HEAD, let's see where it points */ remote_head = refs[0]; assert(remote_head); - git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid); - git_buf_init(&head_info.branchname, 16); - head_info.repo = repo; - head_info.refspec = git_remote__matching_refspec(remote, GIT_REFS_HEADS_MASTER_FILE); - head_info.found = 0; - - if (head_info.refspec == NULL) { - memset(&dummy_spec, 0, sizeof(git_refspec)); - head_info.refspec = &dummy_spec; - } - - /* Determine the remote tracking reference name from the local master */ - if (git_refspec_transform_r( - &remote_master_name, - head_info.refspec, - GIT_REFS_HEADS_MASTER_FILE) < 0) - return -1; + remote_head_id = &remote_head->oid; - /* Check to see if the remote HEAD points to the remote master */ - if (reference_matches_remote_head(git_buf_cstr(&remote_master_name), &head_info) < 0) + error = git_remote_default_branch(&branch, remote); + if (error == GIT_ENOTFOUND) { + error = git_repository_set_head_detached( + repo, remote_head_id, signature, reflog_message); goto cleanup; + } - if (head_info.found) { - retcode = update_head_to_new_branch( - repo, - &head_info.remote_head_oid, - git_buf_cstr(&head_info.branchname)); + refspec = git_remote__matching_refspec(remote, git_buf_cstr(&branch)); + if (refspec == NULL) { + giterr_set(GITERR_NET, "the remote's default branch does not fit the refspec configuration"); + error = GIT_EINVALIDSPEC; goto cleanup; } - /* Not master. Check all the other refs. */ - if (git_reference_foreach_name( - repo, - reference_matches_remote_head, - &head_info) < 0) - goto cleanup; - - if (head_info.found) { - retcode = update_head_to_new_branch( - repo, - &head_info.remote_head_oid, - git_buf_cstr(&head_info.branchname)); - - goto cleanup; - } else { - retcode = git_repository_set_head_detached( - repo, - &head_info.remote_head_oid); + /* Determine the remote tracking reference name from the local master */ + if ((error = git_refspec_transform( + &remote_master_name, + refspec, + git_buf_cstr(&branch))) < 0) goto cleanup; - } + + error = update_head_to_new_branch( + repo, + remote_head_id, + git_buf_cstr(&branch), + signature, reflog_message); cleanup: git_buf_free(&remote_master_name); - git_buf_free(&head_info.branchname); - return retcode; + git_buf_free(&branch); + + return error; } static int update_head_to_branch( git_repository *repo, const char *remote_name, - const char *branch) + const char *branch, + const git_signature *signature, + const char *reflog_message) { int retcode; git_buf remote_branch_name = GIT_BUF_INIT; @@ -277,7 +221,8 @@ if ((retcode = git_reference_lookup(&remote_ref, repo, git_buf_cstr(&remote_branch_name))) < 0) goto cleanup; - retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch); + retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch, + signature, reflog_message); cleanup: git_reference_free(remote_ref); @@ -285,6 +230,29 @@ return retcode; } +static int default_repository_create(git_repository **out, const char *path, int bare, void *payload) +{ + GIT_UNUSED(payload); + + return git_repository_init(out, path, bare); +} + +static int default_remote_create( + git_remote **out, + git_repository *repo, + const char *name, + const char *url, + void *payload) +{ + int error; + git_remote_callbacks *callbacks = payload; + + if ((error = git_remote_create(out, repo, name, url)) < 0) + return error; + + return git_remote_set_callbacks(*out, callbacks); +} + /* * submodules? */ @@ -297,16 +265,24 @@ { int error; git_remote *origin = NULL; - const char *name; + char buf[GIT_PATH_MAX]; + git_remote_create_cb remote_create = options->remote_cb; + void *payload = options->remote_cb_payload; + + /* If the path exists and is a dir, the url should be the absolute path */ + if (git_path_root(url) < 0 && git_path_exists(url) && git_path_isdir(url)) { + if (p_realpath(url, buf) == NULL) + return -1; - name = options->remote_name ? options->remote_name : "origin"; - if ((error = git_remote_create(&origin, repo, name, url)) < 0) - goto on_error; + url = buf; + } - if (options->ignore_cert_errors) - git_remote_check_cert(origin, 0); + if (!remote_create) { + remote_create = default_remote_create; + payload = (void *)&options->remote_callbacks; + } - if ((error = git_remote_set_callbacks(origin, &options->remote_callbacks)) < 0) + if ((error = remote_create(&origin, repo, "origin", url, payload)) < 0) goto on_error; if ((error = git_remote_save(origin)) < 0) @@ -323,7 +299,7 @@ static bool should_checkout( git_repository *repo, bool is_bare, - const git_checkout_opts *opts) + const git_checkout_options *opts) { if (is_bare) return false; @@ -337,51 +313,87 @@ return !git_repository_head_unborn(repo); } -int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch) +static int checkout_branch(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature, const char *reflog_message) { - int error = 0, old_fetchhead; - git_strarray refspecs; + int error; - assert(repo && remote); + if (branch) + error = update_head_to_branch(repo, git_remote_name(remote), branch, + signature, reflog_message); + /* Point HEAD to the same ref as the remote's head */ + else + error = update_head_to_remote(repo, remote, signature, reflog_message); + + if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts)) + error = git_checkout_head(repo, co_opts); + + return error; +} + +static int clone_into(git_repository *repo, git_remote *_remote, const git_checkout_options *co_opts, const char *branch, const git_signature *signature) +{ + int error; + git_buf reflog_message = GIT_BUF_INIT; + git_remote *remote; + const git_remote_callbacks *callbacks; + + assert(repo && _remote); if (!git_repository_is_empty(repo)) { giterr_set(GITERR_INVALID, "the repository is not empty"); return -1; } - - if ((error = git_remote_get_fetch_refspecs(&refspecs, remote)) < 0) + if ((error = git_remote_dup(&remote, _remote)) < 0) return error; + callbacks = git_remote_get_callbacks(_remote); + if (!giterr__check_version(callbacks, 1, "git_remote_callbacks") && + (error = git_remote_set_callbacks(remote, callbacks)) < 0) + goto cleanup; + if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0) - return error; + goto cleanup; - old_fetchhead = git_remote_update_fetchhead(remote); git_remote_set_update_fetchhead(remote, 0); + git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote)); - if ((error = git_remote_fetch(remote)) < 0) + if ((error = git_remote_fetch(remote, NULL, signature, git_buf_cstr(&reflog_message))) != 0) goto cleanup; - if (branch) - error = update_head_to_branch(repo, git_remote_name(remote), branch); - /* Point HEAD to the same ref as the remote's head */ - else - error = update_head_to_remote(repo, remote); - - if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts)) - error = git_checkout_head(repo, co_opts); + error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message)); cleanup: - git_remote_set_update_fetchhead(remote, old_fetchhead); - /* Go back to the original refspecs */ - if (git_remote_set_fetch_refspecs(remote, &refspecs) < 0) { - git_strarray_free(&refspecs); - return -1; + git_remote_free(remote); + git_buf_free(&reflog_message); + + return error; +} + +int git_clone__should_clone_local(const char *url_or_path, git_clone_local_t local) +{ + git_buf fromurl = GIT_BUF_INIT; + const char *path = url_or_path; + bool is_url, is_local; + + if (local == GIT_CLONE_NO_LOCAL) + return 0; + + if ((is_url = git_path_is_local_file_url(url_or_path)) != 0) { + if (git_path_fromurl(&fromurl, url_or_path) < 0) { + is_local = -1; + goto done; + } + + path = fromurl.ptr; } - git_strarray_free(&refspecs); + is_local = (!is_url || local != GIT_CLONE_LOCAL_AUTO) && + git_path_isdir(path); - return error; +done: + git_buf_free(&fromurl); + return is_local; } int git_clone( @@ -395,6 +407,7 @@ git_remote *origin; git_clone_options options = GIT_CLONE_OPTIONS_INIT; uint32_t rmdir_flags = GIT_RMDIR_REMOVE_FILES; + git_repository_create_cb repository_cb; assert(out && url && local_path); @@ -414,22 +427,142 @@ if (git_path_exists(local_path)) rmdir_flags |= GIT_RMDIR_SKIP_ROOT; - if ((error = git_repository_init(&repo, local_path, options.bare)) < 0) + if (options.repository_cb) + repository_cb = options.repository_cb; + else + repository_cb = default_repository_create; + + if ((error = repository_cb(&repo, local_path, options.bare, options.repository_cb_payload)) < 0) return error; if (!(error = create_and_configure_origin(&origin, repo, url, &options))) { - error = git_clone_into( - repo, origin, &options.checkout_opts, options.checkout_branch); + int clone_local = git_clone__should_clone_local(url, options.local); + int link = options.local != GIT_CLONE_LOCAL_NO_LINKS; + + if (clone_local == 1) + error = clone_local_into( + repo, origin, &options.checkout_opts, + options.checkout_branch, link, options.signature); + else if (clone_local == 0) + error = clone_into( + repo, origin, &options.checkout_opts, + options.checkout_branch, options.signature); + else + error = -1; git_remote_free(origin); } - if (error < 0) { + if (error != 0) { + git_error_state last_error = {0}; + giterr_capture(&last_error, error); + git_repository_free(repo); repo = NULL; + (void)git_futils_rmdir_r(local_path, NULL, rmdir_flags); + + giterr_restore(&last_error); } *out = repo; return error; } + +int git_clone_init_options(git_clone_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_clone_options, GIT_CLONE_OPTIONS_INIT); + return 0; +} + +static const char *repository_base(git_repository *repo) +{ + if (git_repository_is_bare(repo)) + return git_repository_path(repo); + + return git_repository_workdir(repo); +} + +static bool can_link(const char *src, const char *dst, int link) +{ +#ifdef GIT_WIN32 + GIT_UNUSED(src); + GIT_UNUSED(dst); + GIT_UNUSED(link); + return false; +#else + + struct stat st_src, st_dst; + + if (!link) + return false; + + if (p_stat(src, &st_src) < 0) + return false; + + if (p_stat(dst, &st_dst) < 0) + return false; + + return st_src.st_dev == st_dst.st_dev; +#endif +} + +static int clone_local_into(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, int link, const git_signature *signature) +{ + int error, flags; + git_repository *src; + git_buf src_odb = GIT_BUF_INIT, dst_odb = GIT_BUF_INIT, src_path = GIT_BUF_INIT; + git_buf reflog_message = GIT_BUF_INIT; + + assert(repo && remote); + + if (!git_repository_is_empty(repo)) { + giterr_set(GITERR_INVALID, "the repository is not empty"); + return -1; + } + + /* + * Let's figure out what path we should use for the source + * repo, if it's not rooted, the path should be relative to + * the repository's worktree/gitdir. + */ + if ((error = git_path_from_url_or_path(&src_path, git_remote_url(remote))) < 0) + return error; + + /* Copy .git/objects/ from the source to the target */ + if ((error = git_repository_open(&src, git_buf_cstr(&src_path))) < 0) { + git_buf_free(&src_path); + return error; + } + + git_buf_joinpath(&src_odb, git_repository_path(src), GIT_OBJECTS_DIR); + git_buf_joinpath(&dst_odb, git_repository_path(repo), GIT_OBJECTS_DIR); + if (git_buf_oom(&src_odb) || git_buf_oom(&dst_odb)) { + error = -1; + goto cleanup; + } + + flags = 0; + if (can_link(git_repository_path(src), git_repository_path(repo), link)) + flags |= GIT_CPDIR_LINK_FILES; + + if ((error = git_futils_cp_r(git_buf_cstr(&src_odb), git_buf_cstr(&dst_odb), + flags, GIT_OBJECT_DIR_MODE)) < 0) + goto cleanup; + + git_buf_printf(&reflog_message, "clone: from %s", git_remote_url(remote)); + + if ((error = git_remote_fetch(remote, NULL, signature, git_buf_cstr(&reflog_message))) != 0) + goto cleanup; + + error = checkout_branch(repo, remote, co_opts, branch, signature, git_buf_cstr(&reflog_message)); + +cleanup: + git_buf_free(&reflog_message); + git_buf_free(&src_path); + git_buf_free(&src_odb); + git_buf_free(&dst_odb); + git_repository_free(src); + return error; +} diff -Nru libgit2-0.20.0/src/clone.h libgit2-0.22.2/src/clone.h --- libgit2-0.20.0/src/clone.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/clone.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,12 @@ +/* + * 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_clone_h__ +#define INCLUDE_clone_h__ + +extern int git_clone__should_clone_local(const char *url, git_clone_local_t local); + +#endif diff -Nru libgit2-0.20.0/src/commit.c libgit2-0.22.2/src/commit.c --- libgit2-0.20.0/src/commit.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/commit.c 2015-03-24 16:10:45.000000000 +0000 @@ -16,8 +16,7 @@ #include "commit.h" #include "signature.h" #include "message.h" - -#include +#include "refs.h" void git_commit__free(void *_commit) { @@ -31,45 +30,13 @@ git__free(commit->raw_header); git__free(commit->raw_message); git__free(commit->message_encoding); + git__free(commit->summary); git__free(commit); } -int git_commit_create_v( - git_oid *oid, - git_repository *repo, - const char *update_ref, - const git_signature *author, - const git_signature *committer, - const char *message_encoding, - const char *message, - const git_tree *tree, - int parent_count, - ...) -{ - va_list ap; - int i, res; - const git_commit **parents; - - parents = git__malloc(parent_count * sizeof(git_commit *)); - GITERR_CHECK_ALLOC(parents); - - va_start(ap, parent_count); - for (i = 0; i < parent_count; ++i) - parents[i] = va_arg(ap, const git_commit *); - va_end(ap); - - res = git_commit_create( - oid, repo, update_ref, author, committer, - message_encoding, message, - tree, parent_count, parents); - - git__free((void *)parents); - return res; -} - -int git_commit_create_from_oids( - git_oid *oid, +int git_commit_create_from_callback( + git_oid *id, git_repository *repo, const char *update_ref, const git_signature *author, @@ -77,19 +44,44 @@ const char *message_encoding, const char *message, const git_oid *tree, - int parent_count, - const git_oid *parents[]) + git_commit_parent_callback parent_cb, + void *parent_payload) { + git_reference *ref = NULL; + int error = 0, matched_parent = 0; + const git_oid *current_id = NULL; git_buf commit = GIT_BUF_INIT; - int i; + size_t i = 0; git_odb *odb; + const git_oid *parent; + + assert(id && repo && tree && parent_cb); + + if (update_ref) { + error = git_reference_lookup_resolved(&ref, repo, update_ref, 10); + if (error < 0 && error != GIT_ENOTFOUND) + return error; + } + giterr_clear(); - assert(oid && repo && tree && parent_count >= 0); + if (ref) + current_id = git_reference_target(ref); git_oid__writebuf(&commit, "tree ", tree); - for (i = 0; i < parent_count; ++i) - git_oid__writebuf(&commit, "parent ", parents[i]); + while ((parent = parent_cb(i, parent_payload)) != NULL) { + git_oid__writebuf(&commit, "parent ", parent); + if (i == 0 && current_id && git_oid_equal(current_id, parent)) + matched_parent = 1; + i++; + } + + if (ref && !matched_parent) { + git_reference_free(ref); + git_buf_free(&commit); + giterr_set(GITERR_OBJECT, "failed to create commit: current tip is not the first parent"); + return GIT_EMODIFIED; + } git_signature__writebuf(&commit, "author ", author); git_signature__writebuf(&commit, "committer ", committer); @@ -105,13 +97,17 @@ if (git_repository_odb__weakptr(&odb, repo) < 0) goto on_error; - if (git_odb_write(oid, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT) < 0) + if (git_odb_write(id, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT) < 0) goto on_error; git_buf_free(&commit); - if (update_ref != NULL) - return git_reference__update_terminal(repo, update_ref, oid); + if (update_ref != NULL) { + error = git_reference__update_for_commit( + repo, ref, update_ref, id, committer, "commit"); + git_reference_free(ref); + return error; + } return 0; @@ -121,8 +117,101 @@ return -1; } +typedef struct { + size_t total; + va_list args; +} commit_parent_varargs; + +static const git_oid *commit_parent_from_varargs(size_t curr, void *payload) +{ + commit_parent_varargs *data = payload; + const git_commit *commit; + if (curr >= data->total) + return NULL; + commit = va_arg(data->args, const git_commit *); + return commit ? git_commit_id(commit) : NULL; +} + +int git_commit_create_v( + git_oid *id, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_tree *tree, + size_t parent_count, + ...) +{ + int error = 0; + commit_parent_varargs data; + + assert(tree && git_tree_owner(tree) == repo); + + data.total = parent_count; + va_start(data.args, parent_count); + + error = git_commit_create_from_callback( + id, repo, update_ref, author, committer, + message_encoding, message, git_tree_id(tree), + commit_parent_from_varargs, &data); + + va_end(data.args); + return error; +} + +typedef struct { + size_t total; + const git_oid **parents; +} commit_parent_oids; + +static const git_oid *commit_parent_from_ids(size_t curr, void *payload) +{ + commit_parent_oids *data = payload; + return (curr < data->total) ? data->parents[curr] : NULL; +} + +int git_commit_create_from_ids( + git_oid *id, + git_repository *repo, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_oid *tree, + size_t parent_count, + const git_oid *parents[]) +{ + commit_parent_oids data = { parent_count, parents }; + + return git_commit_create_from_callback( + id, repo, update_ref, author, committer, + message_encoding, message, tree, + commit_parent_from_ids, &data); +} + +typedef struct { + size_t total; + const git_commit **parents; + git_repository *repo; +} commit_parent_data; + +static const git_oid *commit_parent_from_array(size_t curr, void *payload) +{ + commit_parent_data *data = payload; + const git_commit *commit; + if (curr >= data->total) + return NULL; + commit = data->parents[curr]; + if (git_commit_owner(commit) != data->repo) + return NULL; + return git_commit_id(commit); +} + int git_commit_create( - git_oid *oid, + git_oid *id, git_repository *repo, const char *update_ref, const git_signature *author, @@ -130,31 +219,87 @@ const char *message_encoding, const char *message, const git_tree *tree, - int parent_count, + size_t parent_count, const git_commit *parents[]) { - int retval, i; - const git_oid **parent_oids; + commit_parent_data data = { parent_count, parents, repo }; - assert(parent_count >= 0); - assert(git_object_owner((const git_object *)tree) == repo); + assert(tree && git_tree_owner(tree) == repo); - parent_oids = git__malloc(parent_count * sizeof(git_oid *)); - GITERR_CHECK_ALLOC(parent_oids); + return git_commit_create_from_callback( + id, repo, update_ref, author, committer, + message_encoding, message, git_tree_id(tree), + commit_parent_from_array, &data); +} - for (i = 0; i < parent_count; ++i) { - assert(git_object_owner((const git_object *)parents[i]) == repo); - parent_oids[i] = git_object_id((const git_object *)parents[i]); +static const git_oid *commit_parent_for_amend(size_t curr, void *payload) +{ + const git_commit *commit_to_amend = payload; + if (curr >= git_array_size(commit_to_amend->parent_ids)) + return NULL; + return git_array_get(commit_to_amend->parent_ids, curr); +} + +int git_commit_amend( + git_oid *id, + const git_commit *commit_to_amend, + const char *update_ref, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message, + const git_tree *tree) +{ + git_repository *repo; + git_oid tree_id; + git_reference *ref; + int error; + + assert(id && commit_to_amend); + + repo = git_commit_owner(commit_to_amend); + + if (!author) + author = git_commit_author(commit_to_amend); + if (!committer) + committer = git_commit_committer(commit_to_amend); + if (!message_encoding) + message_encoding = git_commit_message_encoding(commit_to_amend); + if (!message) + message = git_commit_message(commit_to_amend); + + if (!tree) { + git_tree *old_tree; + GITERR_CHECK_ERROR( git_commit_tree(&old_tree, commit_to_amend) ); + git_oid_cpy(&tree_id, git_tree_id(old_tree)); + git_tree_free(old_tree); + } else { + assert(git_tree_owner(tree) == repo); + git_oid_cpy(&tree_id, git_tree_id(tree)); } - retval = git_commit_create_from_oids( - oid, repo, update_ref, author, committer, - message_encoding, message, - git_object_id((const git_object *)tree), parent_count, parent_oids); + if (update_ref) { + if ((error = git_reference_lookup_resolved(&ref, repo, update_ref, 5)) < 0) + return error; - git__free((void *)parent_oids); + if (git_oid_cmp(git_commit_id(commit_to_amend), git_reference_target(ref))) { + git_reference_free(ref); + giterr_set(GITERR_REFERENCE, "commit to amend is not the tip of the given branch"); + return -1; + } + } + + error = git_commit_create_from_callback( + id, repo, NULL, author, committer, message_encoding, message, + &tree_id, commit_parent_for_amend, (void *)commit_to_amend); + + if (!error && update_ref) { + error = git_reference__update_for_commit( + repo, ref, NULL, id, committer, "commit"); + git_reference_free(ref); + } - return retval; + return error; } int git_commit__parse(void *_commit, git_odb_object *odb_obj) @@ -163,33 +308,15 @@ const char *buffer_start = git_odb_object_data(odb_obj), *buffer; const char *buffer_end = buffer_start + git_odb_object_size(odb_obj); git_oid parent_id; - uint32_t parent_count = 0; size_t header_len; - /* find end-of-header (counting parents as we go) */ - for (buffer = buffer_start; buffer < buffer_end; ++buffer) { - if (!strncmp("\n\n", buffer, 2)) { - ++buffer; - break; - } - if (!strncmp("\nparent ", buffer, strlen("\nparent "))) - ++parent_count; - } - - header_len = buffer - buffer_start; - commit->raw_header = git__strndup(buffer_start, header_len); - GITERR_CHECK_ALLOC(commit->raw_header); + buffer = buffer_start; - /* point "buffer" to header data */ - buffer = commit->raw_header; - buffer_end = commit->raw_header + header_len; - - if (parent_count < 1) - parent_count = 1; - - git_array_init_to_size(commit->parent_ids, parent_count); + /* Allocate for one, which will allow not to realloc 90% of the time */ + git_array_init_to_size(commit->parent_ids, 1); GITERR_CHECK_ARRAY(commit->parent_ids); + /* The tree is always the first field */ if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0) goto bad_buffer; @@ -220,6 +347,9 @@ /* Parse add'l header entries */ while (buffer < buffer_end) { const char *eoln = buffer; + if (buffer[-1] == '\n' && buffer[0] == '\n') + break; + while (eoln < buffer_end && *eoln != '\n') ++eoln; @@ -235,13 +365,12 @@ buffer = eoln; } - /* point "buffer" to data after header */ - buffer = git_odb_object_data(odb_obj); - buffer_end = buffer + git_odb_object_size(odb_obj); - - buffer += header_len; - if (*buffer == '\n') - ++buffer; + header_len = buffer - buffer_start; + commit->raw_header = git__strndup(buffer_start, header_len); + GITERR_CHECK_ALLOC(commit->raw_header); + + /* point "buffer" to data after header, +1 for the final LF */ + buffer = buffer_start + header_len + 1; /* extract commit message */ if (buffer <= buffer_end) { @@ -275,10 +404,12 @@ const char *git_commit_message(const git_commit *commit) { - const char *message = commit->raw_message; + const char *message; assert(commit); + message = commit->raw_message; + /* trim leading newlines from raw message */ while (*message && *message == '\n') ++message; @@ -286,6 +417,36 @@ return message; } +const char *git_commit_summary(git_commit *commit) +{ + git_buf summary = GIT_BUF_INIT; + const char *msg, *space; + + assert(commit); + + if (!commit->summary) { + for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) { + if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n')) + break; + else if (msg[0] == '\n') + git_buf_putc(&summary, ' '); + else if (git__isspace(msg[0])) + space = space ? space : msg; + else if (space) { + git_buf_put(&summary, space, (msg - space) + 1); + space = NULL; + } else + git_buf_putc(&summary, *msg); + } + + commit->summary = git_buf_detach(&summary); + if (!commit->summary) + commit->summary = git__strdup(""); + } + + return commit->summary; +} + int git_commit_tree(git_tree **tree_out, const git_commit *commit) { assert(commit); @@ -325,19 +486,18 @@ assert(ancestor && commit); - current = (git_commit *)commit; + if (git_object_dup((git_object **) ¤t, (git_object *) commit) < 0) + return -1; - if (n == 0) - return git_commit_lookup( - ancestor, - commit->object.repo, - git_object_id((const git_object *)commit)); + if (n == 0) { + *ancestor = current; + return 0; + } while (n--) { - error = git_commit_parent(&parent, (git_commit *)current, 0); + error = git_commit_parent(&parent, current, 0); - if (current != commit) - git_commit_free(current); + git_commit_free(current); if (error < 0) return error; diff -Nru libgit2-0.20.0/src/commit.h libgit2-0.22.2/src/commit.h --- libgit2-0.20.0/src/commit.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/commit.h 2015-03-24 16:10:45.000000000 +0000 @@ -26,6 +26,8 @@ char *message_encoding; char *raw_message; char *raw_header; + + char *summary; }; void git_commit__free(void *commit); diff -Nru libgit2-0.20.0/src/commit_list.c libgit2-0.22.2/src/commit_list.c --- libgit2-0.20.0/src/commit_list.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/commit_list.c 2015-03-24 16:10:45.000000000 +0000 @@ -11,10 +11,10 @@ #include "pool.h" #include "odb.h" -int git_commit_list_time_cmp(void *a, void *b) +int git_commit_list_time_cmp(const void *a, const void *b) { - git_commit_list_node *commit_a = (git_commit_list_node *)a; - git_commit_list_node *commit_b = (git_commit_list_node *)b; + const git_commit_list_node *commit_a = a; + const git_commit_list_node *commit_b = b; return (commit_a->time < commit_b->time); } @@ -151,7 +151,7 @@ while (buffer > committer_start && git__isspace(*buffer)) buffer--; - /* Seek for the begining of the pack of digits */ + /* Seek for the beginning of the pack of digits */ while (buffer > committer_start && git__isdigit(*buffer)) buffer--; diff -Nru libgit2-0.20.0/src/commit_list.h libgit2-0.22.2/src/commit_list.h --- libgit2-0.20.0/src/commit_list.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/commit_list.h 2015-03-24 16:10:45.000000000 +0000 @@ -18,6 +18,8 @@ #define COMMIT_ALLOC \ (sizeof(git_commit_list_node) + PARENTS_PER_COMMIT * sizeof(git_commit_list_node *)) +#define FLAG_BITS 4 + typedef struct git_commit_list_node { git_oid oid; uint32_t time; @@ -25,7 +27,7 @@ uninteresting:1, topo_delay:1, parsed:1, - flags : 4; + flags : FLAG_BITS; unsigned short in_degree; unsigned short out_degree; @@ -39,7 +41,7 @@ } git_commit_list; git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk); -int git_commit_list_time_cmp(void *a, void *b); +int git_commit_list_time_cmp(const void *a, const void *b); void git_commit_list_free(git_commit_list **list_p); git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p); git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_commit_list **list_p); diff -Nru libgit2-0.20.0/src/common.h libgit2-0.22.2/src/common.h --- libgit2-0.20.0/src/common.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/common.h 2015-03-24 16:10:45.000000000 +0000 @@ -10,6 +10,13 @@ #include "git2/common.h" #include "cc-compat.h" +/** Declare a function as always inlined. */ +#if defined(_MSC_VER) +# define GIT_INLINE(type) static __inline type +#else +# define GIT_INLINE(type) static inline type +#endif + #include #include #include @@ -26,6 +33,7 @@ # include # include # include +# include # include "win32/msvc-compat.h" # include "win32/mingw-compat.h" # include "win32/error.h" @@ -37,11 +45,15 @@ #else # include +# include # ifdef GIT_THREADS # include +# include # endif #define GIT_STDLIB_CALL +# include + #endif #include "git2/types.h" @@ -57,10 +69,10 @@ #define GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { return -1; } /** - * Check a return value and propogate result if non-zero. + * Check a return value and propagate result if non-zero. */ #define GITERR_CHECK_ERROR(code) \ - do { int _err = (code); if (_err < 0) return _err; } while (0) + do { int _err = (code); if (_err) return _err; } while (0) /** * Set the error message for this thread, formatting as needed. @@ -74,28 +86,61 @@ int giterr_set_regex(const regex_t *regex, int error_code); /** - * Gets the system error code for this thread. + * Set error message for user callback if needed. + * + * If the error code in non-zero and no error message is set, this + * sets a generic error message. + * + * @return This always returns the `error_code` parameter. */ -GIT_INLINE(int) giterr_system_last(void) +GIT_INLINE(int) giterr_set_after_callback_function( + int error_code, const char *action) { + if (error_code) { + const git_error *e = giterr_last(); + if (!e || !e->message) + giterr_set(e ? e->klass : GITERR_CALLBACK, + "%s callback returned %d", action, error_code); + } + return error_code; +} + #ifdef GIT_WIN32 - return GetLastError(); +#define giterr_set_after_callback(code) \ + giterr_set_after_callback_function((code), __FUNCTION__) #else - return errno; +#define giterr_set_after_callback(code) \ + giterr_set_after_callback_function((code), __func__) #endif -} + +/** + * Gets the system error code for this thread. + */ +int giterr_system_last(void); /** * Sets the system error code for this thread. */ -GIT_INLINE(void) giterr_system_set(int code) -{ -#ifdef GIT_WIN32 - SetLastError(code); -#else - errno = code; -#endif -} +void giterr_system_set(int code); + +/** + * Structure to preserve libgit2 error state + */ +typedef struct { + int error_code; + git_error error_msg; +} git_error_state; + +/** + * Capture current error state to restore later, returning error code. + * If `error_code` is zero, this does nothing and returns zero. + */ +int giterr_capture(git_error_state *state, int error_code); + +/** + * Restore error state to a previous value, returning saved error code. + */ +int giterr_restore(git_error_state *state); /** * Check a versioned structure for validity @@ -126,6 +171,11 @@ } #define GIT_INIT_STRUCTURE(S,V) git__init_structure(S, sizeof(*S), V) +#define GIT_INIT_STRUCTURE_FROM_TEMPLATE(PTR,VERSION,TYPE,TPL) do { \ + TYPE _tmpl = TPL; \ + GITERR_CHECK_VERSION(&(VERSION), _tmpl.version, #TYPE); \ + memcpy((PTR), &_tmpl, sizeof(_tmpl)); } while (0) + /* NOTE: other giterr functions are in the public errors.h header file */ #include "util.h" diff -Nru libgit2-0.20.0/src/compress.c libgit2-0.22.2/src/compress.c --- libgit2-0.20.0/src/compress.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/compress.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -/* - * 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 "compress.h" - -#include - -#define BUFFER_SIZE (1024 * 1024) - -int git__compress(git_buf *buf, const void *buff, size_t len) -{ - z_stream zs; - char *zb; - size_t have; - - memset(&zs, 0, sizeof(zs)); - if (deflateInit(&zs, Z_DEFAULT_COMPRESSION) != Z_OK) - return -1; - - zb = git__malloc(BUFFER_SIZE); - GITERR_CHECK_ALLOC(zb); - - zs.next_in = (void *)buff; - zs.avail_in = (uInt)len; - - do { - zs.next_out = (unsigned char *)zb; - zs.avail_out = BUFFER_SIZE; - - if (deflate(&zs, Z_FINISH) == Z_STREAM_ERROR) { - git__free(zb); - return -1; - } - - have = BUFFER_SIZE - (size_t)zs.avail_out; - - if (git_buf_put(buf, zb, have) < 0) { - git__free(zb); - return -1; - } - - } while (zs.avail_out == 0); - - assert(zs.avail_in == 0); - - deflateEnd(&zs); - git__free(zb); - return 0; -} diff -Nru libgit2-0.20.0/src/compress.h libgit2-0.22.2/src/compress.h --- libgit2-0.20.0/src/compress.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/compress.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -/* - * 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_compress_h__ -#define INCLUDE_compress_h__ - -#include "common.h" - -#include "buffer.h" - -int git__compress(git_buf *buf, const void *buff, size_t len); - -#endif /* INCLUDE_compress_h__ */ diff -Nru libgit2-0.20.0/src/config.c libgit2-0.22.2/src/config.c --- libgit2-0.20.0/src/config.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/config.c 2015-03-24 16:10:45.000000000 +0000 @@ -6,7 +6,7 @@ */ #include "common.h" -#include "fileops.h" +#include "sysdir.h" #include "config.h" #include "git2/config.h" #include "git2/sys/config.h" @@ -137,6 +137,38 @@ return error; } +int git_config_snapshot(git_config **out, git_config *in) +{ + int error = 0; + size_t i; + file_internal *internal; + git_config *config; + + *out = NULL; + + if (git_config_new(&config) < 0) + return -1; + + git_vector_foreach(&in->files, i, internal) { + git_config_backend *b; + + if ((error = internal->file->snapshot(&b, internal->file)) < 0) + break; + + if ((error = git_config_add_backend(config, b, internal->level, 0)) < 0) { + b->free(b); + break; + } + } + + if (error < 0) + git_config_free(config); + else + *out = config; + + return error; +} + static int find_internal_file_by_level( file_internal **internal_out, const git_config *cfg, @@ -294,23 +326,6 @@ return 0; } -int git_config_refresh(git_config *cfg) -{ - int error = 0; - size_t i; - - for (i = 0; i < cfg->files.length && !error; ++i) { - file_internal *internal = git_vector_get(&cfg->files, i); - git_config_backend *file = internal->file; - error = file->refresh(file); - } - - if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL) - git_repository__cvar_cache_clear(GIT_REFCOUNT_OWNER(cfg)); - - return error; -} - /* * Loop over all the variables */ @@ -458,6 +473,7 @@ if ((result = regcomp(&iter->regex, regexp, REG_EXTENDED)) < 0) { giterr_set_regex(&iter->regex, result); regfree(&iter->regex); + git__free(iter); return -1; } @@ -480,47 +496,45 @@ int git_config_backend_foreach_match( git_config_backend *backend, const char *regexp, - int (*fn)(const git_config_entry *, void *), - void *data) + git_config_foreach_cb cb, + void *payload) { git_config_entry *entry; git_config_iterator* iter; regex_t regex; - int result = 0; + int error = 0; if (regexp != NULL) { - if ((result = regcomp(®ex, regexp, REG_EXTENDED)) < 0) { - giterr_set_regex(®ex, result); + if ((error = regcomp(®ex, regexp, REG_EXTENDED)) < 0) { + giterr_set_regex(®ex, error); regfree(®ex); return -1; } } - if ((result = backend->iterator(&iter, backend)) < 0) { + if ((error = backend->iterator(&iter, backend)) < 0) { iter = NULL; return -1; } - while(!(iter->next(&entry, iter) < 0)) { + while (!(iter->next(&entry, iter) < 0)) { /* skip non-matching keys if regexp was provided */ if (regexp && regexec(®ex, entry->name, 0, NULL, 0) != 0) continue; /* abort iterator on non-zero return value */ - if (fn(entry, data)) { - giterr_clear(); - result = GIT_EUSER; - goto cleanup; + if ((error = cb(entry, payload)) != 0) { + giterr_set_after_callback(error); + break; } } -cleanup: if (regexp != NULL) regfree(®ex); iter->free(iter); - return result; + return error; } int git_config_foreach_match( @@ -536,10 +550,9 @@ if ((error = git_config_iterator_glob_new(&iter, cfg, regexp)) < 0) return error; - while ((error = git_config_next(&entry, iter)) == 0) { - if(cb(entry, payload)) { - giterr_clear(); - error = GIT_EUSER; + while (!(error = git_config_next(&entry, iter))) { + if ((error = cb(entry, payload)) != 0) { + giterr_set_after_callback(error); break; } } @@ -617,9 +630,112 @@ return error; } +int git_config__update_entry( + git_config *config, + const char *key, + const char *value, + bool overwrite_existing, + bool only_if_existing) +{ + int error = 0; + const git_config_entry *ce = NULL; + + if ((error = git_config__lookup_entry(&ce, config, key, false)) < 0) + return error; + + if (!ce && only_if_existing) /* entry doesn't exist */ + return 0; + if (ce && !overwrite_existing) /* entry would be overwritten */ + return 0; + if (value && ce && ce->value && !strcmp(ce->value, value)) /* no change */ + return 0; + if (!value && (!ce || !ce->value)) /* asked to delete absent entry */ + return 0; + + if (!value) + error = git_config_delete_entry(config, key); + else + error = git_config_set_string(config, key, value); + + return error; +} + /*********** * Getters ***********/ + +static int config_error_notfound(const char *name) +{ + giterr_set(GITERR_CONFIG, "Config value '%s' was not found", name); + return GIT_ENOTFOUND; +} + +enum { + GET_ALL_ERRORS = 0, + GET_NO_MISSING = 1, + GET_NO_ERRORS = 2 +}; + +static int get_entry( + const git_config_entry **out, + const git_config *cfg, + const char *name, + bool normalize_name, + int want_errors) +{ + int res = GIT_ENOTFOUND; + const char *key = name; + char *normalized = NULL; + size_t i; + file_internal *internal; + + *out = NULL; + + if (normalize_name) { + if ((res = git_config__normalize_name(name, &normalized)) < 0) + goto cleanup; + key = normalized; + } + + res = GIT_ENOTFOUND; + git_vector_foreach(&cfg->files, i, internal) { + if (!internal || !internal->file) + continue; + + res = internal->file->get(internal->file, key, out); + if (res != GIT_ENOTFOUND) + break; + } + + git__free(normalized); + +cleanup: + if (res == GIT_ENOTFOUND) + res = (want_errors > GET_ALL_ERRORS) ? 0 : config_error_notfound(name); + else if (res && (want_errors == GET_NO_ERRORS)) { + giterr_clear(); + res = 0; + } + + return res; +} + +int git_config_get_entry( + const git_config_entry **out, const git_config *cfg, const char *name) +{ + return get_entry(out, cfg, name, true, GET_ALL_ERRORS); +} + +int git_config__lookup_entry( + const git_config_entry **out, + const git_config *cfg, + const char *key, + bool no_errors) +{ + return get_entry( + out, cfg, key, false, no_errors ? GET_NO_ERRORS : GET_NO_MISSING); +} + int git_config_get_mapped( int *out, const git_config *cfg, @@ -627,116 +743,91 @@ const git_cvar_map *maps, size_t map_n) { - const char *value; + const git_config_entry *entry; int ret; - if ((ret = git_config_get_string(&value, cfg, name)) < 0) + if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0) return ret; - return git_config_lookup_map_value(out, maps, map_n, value); + return git_config_lookup_map_value(out, maps, map_n, entry->value); } int git_config_get_int64(int64_t *out, const git_config *cfg, const char *name) { - const char *value; + const git_config_entry *entry; int ret; - if ((ret = git_config_get_string(&value, cfg, name)) < 0) + if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0) return ret; - return git_config_parse_int64(out, value); + return git_config_parse_int64(out, entry->value); } int git_config_get_int32(int32_t *out, const git_config *cfg, const char *name) { - const char *value; + const git_config_entry *entry; int ret; - if ((ret = git_config_get_string(&value, cfg, name)) < 0) + if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0) return ret; - return git_config_parse_int32(out, value); + return git_config_parse_int32(out, entry->value); } -static int get_string_at_file(const char **out, const git_config_backend *file, const char *name) +int git_config_get_bool(int *out, const git_config *cfg, const char *name) { const git_config_entry *entry; - int res; + int ret; - res = file->get(file, name, &entry); - if (!res) - *out = entry->value; + if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0) + return ret; - return res; + return git_config_parse_bool(out, entry->value); } -static int config_error_notfound(const char *name) +int git_config_get_string( + const char **out, const git_config *cfg, const char *name) { - giterr_set(GITERR_CONFIG, "Config value '%s' was not found", name); - return GIT_ENOTFOUND; + const git_config_entry *entry; + int ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS); + *out = !ret ? (entry->value ? entry->value : "") : NULL; + return ret; } -static int get_string(const char **out, const git_config *cfg, const char *name) +const char *git_config__get_string_force( + const git_config *cfg, const char *key, const char *fallback_value) { - file_internal *internal; - unsigned int i; - int res; - - git_vector_foreach(&cfg->files, i, internal) { - if (!internal || !internal->file) - continue; - - res = get_string_at_file(out, internal->file, name); - if (res != GIT_ENOTFOUND) - return res; - } - - return config_error_notfound(name); + const git_config_entry *entry; + get_entry(&entry, cfg, key, false, GET_NO_ERRORS); + return (entry && entry->value) ? entry->value : fallback_value; } -int git_config_get_bool(int *out, const git_config *cfg, const char *name) +int git_config__get_bool_force( + const git_config *cfg, const char *key, int fallback_value) { - const char *value = NULL; - int ret; - - if ((ret = get_string(&value, cfg, name)) < 0) - return ret; - - return git_config_parse_bool(out, value); -} + int val = fallback_value; + const git_config_entry *entry; -int git_config_get_string(const char **out, const git_config *cfg, const char *name) -{ - int ret; - const char *str = NULL; + get_entry(&entry, cfg, key, false, GET_NO_ERRORS); - if ((ret = get_string(&str, cfg, name)) < 0) - return ret; + if (entry && git_config_parse_bool(&val, entry->value) < 0) + giterr_clear(); - *out = str == NULL ? "" : str; - return 0; + return val; } -int git_config_get_entry(const git_config_entry **out, const git_config *cfg, const char *name) +int git_config__get_int_force( + const git_config *cfg, const char *key, int fallback_value) { - file_internal *internal; - unsigned int i; - git_config_backend *file; - int ret; - - *out = NULL; + int32_t val = (int32_t)fallback_value; + const git_config_entry *entry; - git_vector_foreach(&cfg->files, i, internal) { - if (!internal || !internal->file) - continue; - file = internal->file; + get_entry(&entry, cfg, key, false, GET_NO_ERRORS); - ret = file->get(file, name, out); - if (ret != GIT_ENOTFOUND) - return ret; - } + if (entry && git_config_parse_int32(&val, entry->value) < 0) + giterr_clear(); - return config_error_notfound(name); + return (int)val; } int git_config_get_multivar_foreach( @@ -753,9 +844,10 @@ found = 0; while ((err = iter->next(&entry, iter)) == 0) { found = 1; - if(cb(entry, payload)) { - iter->free(iter); - return GIT_EUSER; + + if ((err = cb(entry, payload)) != 0) { + giterr_set_after_callback(err); + break; } } @@ -882,86 +974,50 @@ void git_config_iterator_free(git_config_iterator *iter) { - iter->free(iter); -} - -static int git_config__find_file_to_path( - char *out, size_t outlen, int (*find)(git_buf *buf)) -{ - int error = 0; - git_buf path = GIT_BUF_INIT; - - if ((error = find(&path)) < 0) - goto done; - - if (path.size >= outlen) { - giterr_set(GITERR_NOMEMORY, "Buffer is too short for the path"); - error = GIT_EBUFS; - goto done; - } - - git_buf_copy_cstr(out, outlen, &path); - -done: - git_buf_free(&path); - return error; -} - -int git_config_find_global_r(git_buf *path) -{ - return git_futils_find_global_file(path, GIT_CONFIG_FILENAME_GLOBAL); -} - -int git_config_find_global(char *global_config_path, size_t length) -{ - return git_config__find_file_to_path( - global_config_path, length, git_config_find_global_r); -} + if (iter == NULL) + return; -int git_config_find_xdg_r(git_buf *path) -{ - return git_futils_find_xdg_file(path, GIT_CONFIG_FILENAME_XDG); + iter->free(iter); } -int git_config_find_xdg(char *xdg_config_path, size_t length) +int git_config_find_global(git_buf *path) { - return git_config__find_file_to_path( - xdg_config_path, length, git_config_find_xdg_r); + git_buf_sanitize(path); + return git_sysdir_find_global_file(path, GIT_CONFIG_FILENAME_GLOBAL); } -int git_config_find_system_r(git_buf *path) +int git_config_find_xdg(git_buf *path) { - return git_futils_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM); + git_buf_sanitize(path); + return git_sysdir_find_xdg_file(path, GIT_CONFIG_FILENAME_XDG); } -int git_config_find_system(char *system_config_path, size_t length) +int git_config_find_system(git_buf *path) { - return git_config__find_file_to_path( - system_config_path, length, git_config_find_system_r); + git_buf_sanitize(path); + return git_sysdir_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM); } int git_config__global_location(git_buf *buf) { const git_buf *paths; const char *sep, *start; - size_t len; - if (git_futils_dirs_get(&paths, GIT_FUTILS_DIR_GLOBAL) < 0) + if (git_sysdir_get(&paths, GIT_SYSDIR_GLOBAL) < 0) return -1; /* no paths, so give up */ - if (git_buf_len(paths) == 0) + if (!paths || !git_buf_len(paths)) return -1; - start = git_buf_cstr(paths); - sep = strchr(start, GIT_PATH_LIST_SEPARATOR); - - if (sep) - len = sep - start; - else - len = paths->size; + /* find unescaped separator or end of string */ + for (sep = start = git_buf_cstr(paths); *sep; ++sep) { + if (*sep == GIT_PATH_LIST_SEPARATOR && + (sep <= start || sep[-1] != '\\')) + break; + } - if (git_buf_set(buf, start, len) < 0) + if (git_buf_set(buf, start, (size_t)(sep - start)) < 0) return -1; return git_buf_joinpath(buf, buf->ptr, GIT_CONFIG_FILENAME_GLOBAL); @@ -976,16 +1032,16 @@ if ((error = git_config_new(&cfg)) < 0) return error; - if (!git_config_find_global_r(&buf) || !git_config__global_location(&buf)) { + 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); } - if (!error && !git_config_find_xdg_r(&buf)) + if (!error && !git_config_find_xdg(&buf)) error = git_config_add_file_ondisk(cfg, buf.ptr, GIT_CONFIG_LEVEL_XDG, 0); - if (!error && !git_config_find_system_r(&buf)) + if (!error && !git_config_find_system(&buf)) error = git_config_add_file_ondisk(cfg, buf.ptr, GIT_CONFIG_LEVEL_SYSTEM, 0); @@ -1070,7 +1126,7 @@ const char *num_end; int64_t num; - if (git__strtol64(&num, value, &num_end, 0) < 0) + if (!value || git__strtol64(&num, value, &num_end, 0) < 0) goto fail_parse; switch (*num_end) { @@ -1104,7 +1160,7 @@ } fail_parse: - giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value); + giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value ? value : "(null)"); return -1; } @@ -1124,7 +1180,7 @@ return 0; fail_parse: - giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value); + giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value ? value : "(null)"); return -1; } @@ -1167,7 +1223,6 @@ git_config *config; git_buf *name; size_t old_len; - int actual_error; }; static int rename_config_entries_cb( @@ -1190,8 +1245,6 @@ if (!error) error = git_config_delete_entry(data->config, entry->name); - data->actual_error = error; /* preserve actual error code */ - return error; } @@ -1216,7 +1269,6 @@ data.config = config; data.name = &replace; data.old_len = strlen(old_section_name) + 1; - data.actual_error = 0; if ((error = git_buf_join(&replace, '.', new_section_name, "")) < 0) goto cleanup; @@ -1233,12 +1285,16 @@ error = git_config_foreach_match( config, git_buf_cstr(&pattern), rename_config_entries_cb, &data); - if (error == GIT_EUSER) - error = data.actual_error; - cleanup: git_buf_free(&pattern); git_buf_free(&replace); return error; } + +int git_config_init_backend(git_config_backend *backend, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + backend, version, git_config_backend, GIT_CONFIG_BACKEND_INIT); + return 0; +} diff -Nru libgit2-0.20.0/src/config_cache.c libgit2-0.22.2/src/config_cache.c --- libgit2-0.20.0/src/config_cache.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/config_cache.c 2015-03-24 16:10:45.000000000 +0000 @@ -7,11 +7,11 @@ #include "common.h" #include "fileops.h" +#include "repository.h" #include "config.h" #include "git2/config.h" #include "vector.h" #include "filter.h" -#include "repository.h" struct map_data { const char *cvar_name; @@ -51,6 +51,12 @@ {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT} }; +static git_cvar_map _cvar_map_safecrlf[] = { + {GIT_CVAR_FALSE, NULL, GIT_SAFE_CRLF_FALSE}, + {GIT_CVAR_TRUE, NULL, GIT_SAFE_CRLF_FAIL}, + {GIT_CVAR_STRING, "warn", GIT_SAFE_CRLF_WARN} +}; + /* * Generic map for integer values */ @@ -68,32 +74,41 @@ {"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT }, {"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_DEFAULT }, {"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT }, + {"core.safecrlf", _cvar_map_safecrlf, ARRAY_SIZE(_cvar_map_safecrlf), GIT_SAFE_CRLF_DEFAULT}, + {"core.logallrefupdates", NULL, 0, GIT_LOGALLREFUPDATES_DEFAULT }, + {"core.protecthfs", NULL, 0, GIT_PROTECTHFS_DEFAULT }, + {"core.protectntfs", NULL, 0, GIT_PROTECTNTFS_DEFAULT }, }; +int git_config__cvar(int *out, git_config *config, git_cvar_cached cvar) +{ + int error = 0; + struct map_data *data = &_cvar_maps[(int)cvar]; + const git_config_entry *entry; + + git_config__lookup_entry(&entry, config, data->cvar_name, false); + + if (!entry) + *out = data->default_value; + else if (data->maps) + error = git_config_lookup_map_value( + out, data->maps, data->map_count, entry->value); + else + error = git_config_parse_bool(out, entry->value); + + return error; +} + int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar) { *out = repo->cvar_cache[(int)cvar]; if (*out == GIT_CVAR_NOT_CACHED) { - struct map_data *data = &_cvar_maps[(int)cvar]; - git_config *config; int error; + git_config *config; - error = git_repository_config__weakptr(&config, repo); - if (error < 0) - return error; - - if (data->maps) - error = git_config_get_mapped( - out, config, data->cvar_name, data->maps, data->map_count); - else - error = git_config_get_bool(out, config, data->cvar_name); - - if (error == GIT_ENOTFOUND) { - giterr_clear(); - *out = data->default_value; - } - else if (error < 0) + if ((error = git_repository_config__weakptr(&config, repo)) < 0 || + (error = git_config__cvar(out, config, cvar)) < 0) return error; repo->cvar_cache[(int)cvar] = *out; diff -Nru libgit2-0.20.0/src/config_file.c libgit2-0.22.2/src/config_file.c --- libgit2-0.20.0/src/config_file.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/config_file.c 2015-03-24 16:10:45.000000000 +0000 @@ -7,8 +7,8 @@ #include "common.h" #include "config.h" -#include "fileops.h" #include "filebuf.h" +#include "sysdir.h" #include "buffer.h" #include "buf_text.h" #include "git2/config.h" @@ -26,7 +26,7 @@ typedef struct cvar_t { struct cvar_t *next; git_config_entry *entry; - int included; /* whether this is part of [include] */ + bool included; /* whether this is part of [include] */ } cvar_t; typedef struct git_config_file_iter { @@ -87,28 +87,54 @@ }; typedef struct { + git_atomic refcount; + git_strmap *values; +} refcounted_strmap; + +typedef struct { git_config_backend parent; + /* mutex to coordinate accessing the values */ + git_mutex values_mutex; + refcounted_strmap *values; + int readonly; +} diskfile_header; - git_strmap *values; +typedef struct { + diskfile_header header; + + git_config_level_t level; git_array_t(struct reader) readers; char *file_path; - - git_config_level_t level; } diskfile_backend; -static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth); +typedef struct { + diskfile_header header; + + diskfile_backend *snapshot_from; +} diskfile_readonly_backend; + +static int config_parse(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth); static int parse_variable(struct reader *reader, char **var_name, char **var_value); static int config_write(diskfile_backend *cfg, 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 -1; +} + static void cvar_free(cvar_t *var) { if (var == NULL) @@ -120,18 +146,6 @@ git__free(var); } -static int cvar_length(cvar_t *var) -{ - int length = 0; - - while (var) { - length++; - var = var->next; - } - - return length; -} - int git_config_file_normalize_section(char *start, char *end) { char *scan; @@ -155,6 +169,30 @@ return 0; } +/* Add or append the new config option */ +static int append_entry(git_strmap *values, cvar_t *var) +{ + git_strmap_iter pos; + cvar_t *existing; + int error = 0; + + pos = git_strmap_lookup_index(values, var->entry->name); + if (!git_strmap_valid_index(values, pos)) { + git_strmap_insert(values, var->entry->name, var, error); + } else { + existing = git_strmap_value_at(values, pos); + while (existing->next != NULL) { + existing = existing->next; + } + existing->next = var; + } + + if (error > 0) + error = 0; + + return error; +} + static void free_vars(git_strmap *values) { cvar_t *var = NULL; @@ -172,6 +210,55 @@ git_strmap_free(values); } +static void refcounted_strmap_free(refcounted_strmap *map) +{ + if (!map) + return; + + if (git_atomic_dec(&map->refcount) != 0) + return; + + free_vars(map->values); + git__free(map); +} + +/** + * Take the current values map from the backend and increase its + * refcount. This is its own function to make sure we use the mutex to + * avoid the map pointer from changing under us. + */ +static refcounted_strmap *refcounted_strmap_take(diskfile_header *h) +{ + refcounted_strmap *map; + + git_mutex_lock(&h->values_mutex); + + map = h->values; + git_atomic_inc(&map->refcount); + + git_mutex_unlock(&h->values_mutex); + + return map; +} + +static int refcounted_strmap_alloc(refcounted_strmap **out) +{ + refcounted_strmap *map; + int error; + + map = git__calloc(1, sizeof(refcounted_strmap)); + GITERR_CHECK_ALLOC(map); + + git_atomic_set(&map->refcount, 1); + + if ((error = git_strmap_alloc(&map->values)) < 0) + git__free(map); + else + *out = map; + + return error; +} + static int config_open(git_config_backend *cfg, git_config_level_t level) { int res; @@ -180,11 +267,15 @@ b->level = level; - b->values = git_strmap_alloc(); - GITERR_CHECK_ALLOC(b->values); + 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); @@ -198,53 +289,73 @@ if (res == GIT_ENOTFOUND) return 0; - if (res < 0 || (res = config_parse(b, reader, level, 0)) < 0) { - free_vars(b->values); - b->values = NULL; + if (res < 0 || (res = config_parse(b->header.values->values, b, reader, 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) +{ + refcounted_strmap *values = NULL, *tmp; + diskfile_backend *b = (diskfile_backend *)cfg; + struct reader *reader = NULL; + int error = 0; + + if ((error = refcounted_strmap_alloc(&values)) < 0) + goto out; + + reader = git_array_get(b->readers, git_array_size(b->readers) - 1); + GITERR_CHECK_ALLOC(reader); + + if ((error = config_parse(values->values, b, reader, b->level, 0)) < 0) + goto out; + + git_mutex_lock(&b->header.values_mutex); + + tmp = b->header.values; + b->header.values = values; + values = tmp; + + git_mutex_unlock(&b->header.values_mutex); + +out: + refcounted_strmap_free(values); + if (reader) + git_buf_free(&reader->buffer); + return error; +} + static int config_refresh(git_config_backend *cfg) { - int res = 0, updated = 0, any_updated = 0; + int error = 0, updated = 0, any_updated = 0; diskfile_backend *b = (diskfile_backend *)cfg; - git_strmap *old_values; - struct reader *reader; + struct reader *reader = NULL; uint32_t i; for (i = 0; i < git_array_size(b->readers); i++) { reader = git_array_get(b->readers, i); - res = git_futils_readbuffer_updated( - &reader->buffer, reader->file_path, &reader->file_mtime, &reader->file_size, &updated); + error = git_futils_readbuffer_updated( + &reader->buffer, reader->file_path, + &reader->file_mtime, &reader->file_size, &updated); - if (res < 0) - return (res == GIT_ENOTFOUND) ? 0 : res; + if (error < 0 && error != GIT_ENOTFOUND) + return error; if (updated) any_updated = 1; } if (!any_updated) - return (res == GIT_ENOTFOUND) ? 0 : res; + return (error == GIT_ENOTFOUND) ? 0 : error; - /* need to reload - store old values and prep for reload */ - old_values = b->values; - b->values = git_strmap_alloc(); - GITERR_CHECK_ALLOC(b->values); - - if ((res = config_parse(b, reader, b->level, 0)) < 0) { - free_vars(b->values); - b->values = old_values; - } else { - free_vars(old_values); - } - - git_buf_free(&reader->buffer); - return res; + return config__refresh(cfg); } static void backend_free(git_config_backend *_backend) @@ -262,13 +373,15 @@ git_array_clear(backend->readers); git__free(backend->file_path); - free_vars(backend->values); + refcounted_strmap_free(backend->header.values); + git_mutex_free(&backend->header.values_mutex); git__free(backend); } static void config_iterator_free( git_config_iterator* iter) { + iter->backend->free(iter->backend); git__free(iter); } @@ -277,12 +390,13 @@ git_config_iterator *iter) { git_config_file_iter *it = (git_config_file_iter *) iter; - diskfile_backend *b = (diskfile_backend *) it->parent.backend; + diskfile_header *h = (diskfile_header *) it->parent.backend; + git_strmap *values = h->values->values; int err = 0; cvar_t * var; if (it->next_var == NULL) { - err = git_strmap_next((void**) &var, &(it->iter), b->values); + err = git_strmap_next((void**) &var, &(it->iter), values); } else { var = it->next_var; } @@ -302,15 +416,28 @@ git_config_iterator **iter, struct git_config_backend* backend) { - diskfile_backend *b = (diskfile_backend *)backend; - git_config_file_iter *it = git__calloc(1, sizeof(git_config_file_iter)); + diskfile_header *h; + git_config_file_iter *it; + git_config_backend *snapshot; + diskfile_backend *b = (diskfile_backend *) backend; + int error; - GIT_UNUSED(b); + if ((error = config_snapshot(&snapshot, backend)) < 0) + return error; + + if ((error = snapshot->open(snapshot, b->level)) < 0) + return error; + it = git__calloc(1, sizeof(git_config_file_iter)); GITERR_CHECK_ALLOC(it); - it->parent.backend = backend; - it->iter = git_strmap_begin(b->values); + h = (diskfile_header *)snapshot; + + /* strmap_begin() is currently a macro returning 0 */ + GIT_UNUSED(h); + + it->parent.backend = snapshot; + it->iter = git_strmap_begin(h->values); it->next_var = NULL; it->parent.next = config_iterator_next; @@ -322,8 +449,9 @@ static int config_set(git_config_backend *cfg, const char *name, const char *value) { - cvar_t *var = NULL, *old_var = NULL; diskfile_backend *b = (diskfile_backend *)cfg; + refcounted_strmap *map; + git_strmap *values; char *key, *esc_value = NULL; khiter_t pos; int rval, ret; @@ -331,112 +459,92 @@ if ((rval = git_config__normalize_name(name, &key)) < 0) return rval; + map = refcounted_strmap_take(&b->header); + values = map->values; + /* * Try to find it in the existing values and update it if it * only has one value. */ - pos = git_strmap_lookup_index(b->values, key); - if (git_strmap_valid_index(b->values, pos)) { - cvar_t *existing = git_strmap_value_at(b->values, pos); - char *tmp = NULL; - - git__free(key); + pos = git_strmap_lookup_index(values, key); + if (git_strmap_valid_index(values, pos)) { + cvar_t *existing = git_strmap_value_at(values, pos); if (existing->next != NULL) { giterr_set(GITERR_CONFIG, "Multivar incompatible with simple set"); - return -1; + ret = -1; + goto out; } /* don't update if old and new values already match */ if ((!existing->entry->value && !value) || - (existing->entry->value && value && !strcmp(existing->entry->value, value))) - return 0; - - if (value) { - tmp = git__strdup(value); - GITERR_CHECK_ALLOC(tmp); - esc_value = escape_value(value); - GITERR_CHECK_ALLOC(esc_value); + (existing->entry->value && value && + !strcmp(existing->entry->value, value))) { + ret = 0; + goto out; } - - git__free((void *)existing->entry->value); - existing->entry->value = tmp; - - ret = config_write(b, existing->entry->name, NULL, esc_value); - - git__free(esc_value); - return ret; } - var = git__malloc(sizeof(cvar_t)); - GITERR_CHECK_ALLOC(var); - memset(var, 0x0, sizeof(cvar_t)); - var->entry = git__malloc(sizeof(git_config_entry)); - GITERR_CHECK_ALLOC(var->entry); - memset(var->entry, 0x0, sizeof(git_config_entry)); - - var->entry->name = key; - var->entry->value = NULL; + /* No early returns due to sanity checks, let's write it out and refresh */ if (value) { - var->entry->value = git__strdup(value); - GITERR_CHECK_ALLOC(var->entry->value); esc_value = escape_value(value); GITERR_CHECK_ALLOC(esc_value); } - if ((ret = config_write(b, key, NULL, esc_value)) < 0) { - git__free(esc_value); - cvar_free(var); - return ret; - } + if ((ret = config_write(b, key, NULL, esc_value)) < 0) + goto out; - git__free(esc_value); - git_strmap_insert2(b->values, key, var, old_var, rval); - if (rval < 0) - return -1; - if (old_var != NULL) - cvar_free(old_var); + ret = config_refresh(cfg); - return 0; +out: + refcounted_strmap_free(map); + git__free(esc_value); + git__free(key); + return ret; } /* * Internal function that actually gets the value in string form */ -static int config_get(const git_config_backend *cfg, const char *name, const git_config_entry **out) +static int config_get(git_config_backend *cfg, const char *key, const git_config_entry **out) { - diskfile_backend *b = (diskfile_backend *)cfg; - char *key; + diskfile_header *h = (diskfile_header *)cfg; + refcounted_strmap *map; + git_strmap *values; khiter_t pos; - int error; cvar_t *var; + int error; - if ((error = git_config__normalize_name(name, &key)) < 0) + if (!h->readonly && ((error = config_refresh(cfg)) < 0)) return error; - pos = git_strmap_lookup_index(b->values, key); - git__free(key); + map = refcounted_strmap_take(h); + values = map->values; + + pos = git_strmap_lookup_index(values, key); /* no error message; the config system will write one */ - if (!git_strmap_valid_index(b->values, pos)) + if (!git_strmap_valid_index(values, pos)) { + refcounted_strmap_free(map); return GIT_ENOTFOUND; + } - var = git_strmap_value_at(b->values, pos); + var = git_strmap_value_at(values, pos); while (var->next) var = var->next; + refcounted_strmap_free(map); *out = var->entry; - return 0; } static int config_set_multivar( git_config_backend *cfg, const char *name, const char *regexp, const char *value) { - int replaced = 0; - cvar_t *var, *newvar; diskfile_backend *b = (diskfile_backend *)cfg; + refcounted_strmap *map; + git_strmap *values; char *key; regex_t preg; int result; @@ -447,62 +555,33 @@ if ((result = git_config__normalize_name(name, &key)) < 0) return result; - pos = git_strmap_lookup_index(b->values, key); - if (!git_strmap_valid_index(b->values, pos)) { + map = refcounted_strmap_take(&b->header); + values = b->header.values->values; + + pos = git_strmap_lookup_index(values, key); + if (!git_strmap_valid_index(values, pos)) { /* If we don't have it, behave like a normal set */ result = config_set(cfg, name, value); + refcounted_strmap_free(map); git__free(key); return result; } - var = git_strmap_value_at(b->values, pos); - result = regcomp(&preg, regexp, REG_EXTENDED); if (result < 0) { - git__free(key); giterr_set_regex(&preg, result); - regfree(&preg); - return -1; - } - - for (;;) { - if (regexec(&preg, var->entry->value, 0, NULL, 0) == 0) { - char *tmp = git__strdup(value); - GITERR_CHECK_ALLOC(tmp); - - git__free((void *)var->entry->value); - var->entry->value = tmp; - replaced = 1; - } - - if (var->next == NULL) - break; - - var = var->next; + result = -1; + goto out; } - /* If we've reached the end of the variables and we haven't found it yet, we need to append it */ - if (!replaced) { - newvar = git__malloc(sizeof(cvar_t)); - GITERR_CHECK_ALLOC(newvar); - memset(newvar, 0x0, sizeof(cvar_t)); - newvar->entry = git__malloc(sizeof(git_config_entry)); - GITERR_CHECK_ALLOC(newvar->entry); - memset(newvar->entry, 0x0, sizeof(git_config_entry)); - - newvar->entry->name = git__strdup(var->entry->name); - GITERR_CHECK_ALLOC(newvar->entry->name); - - newvar->entry->value = git__strdup(value); - GITERR_CHECK_ALLOC(newvar->entry->value); + /* If we do have it, set call config_write() and reload */ + if ((result = config_write(b, key, &preg, value)) < 0) + goto out; - newvar->entry->level = var->entry->level; - - var->next = newvar; - } - - result = config_write(b, key, &preg, value); + result = config_refresh(cfg); +out: + refcounted_strmap_free(map); git__free(key); regfree(&preg); @@ -513,6 +592,7 @@ { cvar_t *var; diskfile_backend *b = (diskfile_backend *)cfg; + refcounted_strmap *map; git_strmap *values; char *key; int result; khiter_t pos; @@ -520,35 +600,37 @@ if ((result = git_config__normalize_name(name, &key)) < 0) return result; - pos = git_strmap_lookup_index(b->values, key); + map = refcounted_strmap_take(&b->header); + values = b->header.values->values; + + pos = git_strmap_lookup_index(values, key); git__free(key); - if (!git_strmap_valid_index(b->values, pos)) { + if (!git_strmap_valid_index(values, pos)) { + refcounted_strmap_free(map); giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name); return GIT_ENOTFOUND; } - var = git_strmap_value_at(b->values, pos); + var = git_strmap_value_at(values, pos); + refcounted_strmap_free(map); if (var->next != NULL) { giterr_set(GITERR_CONFIG, "Cannot delete multivar with a single delete"); return -1; } - git_strmap_delete_at(b->values, pos); - - result = config_write(b, var->entry->name, NULL, NULL); + if ((result = config_write(b, var->entry->name, NULL, NULL)) < 0) + return result; - cvar_free(var); - return result; + return config_refresh(cfg); } static int config_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp) { - cvar_t *var, *prev = NULL, *new_head = NULL; - cvar_t **to_delete; - int to_delete_idx; diskfile_backend *b = (diskfile_backend *)cfg; + refcounted_strmap *map; + git_strmap *values; char *key; regex_t preg; int result; @@ -557,66 +639,45 @@ if ((result = git_config__normalize_name(name, &key)) < 0) return result; - pos = git_strmap_lookup_index(b->values, key); + map = refcounted_strmap_take(&b->header); + values = b->header.values->values; - if (!git_strmap_valid_index(b->values, pos)) { - giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name); + pos = git_strmap_lookup_index(values, key); + + if (!git_strmap_valid_index(values, pos)) { + refcounted_strmap_free(map); git__free(key); + giterr_set(GITERR_CONFIG, "Could not find key '%s' to delete", name); return GIT_ENOTFOUND; } - var = git_strmap_value_at(b->values, pos); + refcounted_strmap_free(map); result = regcomp(&preg, regexp, REG_EXTENDED); if (result < 0) { - git__free(key); giterr_set_regex(&preg, result); - regfree(&preg); - return -1; - } - - to_delete = git__calloc(cvar_length(var), sizeof(cvar_t *)); - GITERR_CHECK_ALLOC(to_delete); - to_delete_idx = 0; - - while (var != NULL) { - cvar_t *next = var->next; - - if (regexec(&preg, var->entry->value, 0, NULL, 0) == 0) { - // If we are past the head, reattach previous node to next one, - // otherwise set the new head for the strmap. - if (prev != NULL) { - prev->next = next; - } else { - new_head = next; - } - - to_delete[to_delete_idx++] = var; - } else { - prev = var; - } - - var = next; - } - - if (new_head != NULL) { - git_strmap_set_value_at(b->values, pos, new_head); - } else { - git_strmap_delete_at(b->values, pos); + result = -1; + goto out; } - if (to_delete_idx > 0) - result = config_write(b, key, &preg, NULL); + if ((result = config_write(b, key, &preg, NULL)) < 0) + goto out; - while (to_delete_idx-- > 0) - cvar_free(to_delete[to_delete_idx]); + result = config_refresh(cfg); +out: git__free(key); - git__free(to_delete); regfree(&preg); return result; } +static int config_snapshot(git_config_backend **out, git_config_backend *in) +{ + diskfile_backend *b = (diskfile_backend *) in; + + return git_config_file__snapshot(out, b); +} + int git_config_file__ondisk(git_config_backend **out, const char *path) { diskfile_backend *backend; @@ -624,20 +685,118 @@ backend = git__calloc(1, sizeof(diskfile_backend)); GITERR_CHECK_ALLOC(backend); - backend->parent.version = GIT_CONFIG_BACKEND_VERSION; + 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->parent.open = config_open; - backend->parent.get = config_get; - backend->parent.set = config_set; - backend->parent.set_multivar = config_set_multivar; - backend->parent.del = config_delete; - backend->parent.del_multivar = config_delete_multivar; - backend->parent.iterator = config_iterator_new; - backend->parent.refresh = config_refresh; - backend->parent.free = backend_free; + backend->header.parent.open = config_open; + backend->header.parent.get = config_get; + backend->header.parent.set = config_set; + backend->header.parent.set_multivar = config_set_multivar; + backend->header.parent.del = config_delete; + backend->header.parent.del_multivar = config_delete_multivar; + backend->header.parent.iterator = config_iterator_new; + backend->header.parent.snapshot = config_snapshot; + backend->header.parent.free = backend_free; + + *out = (git_config_backend *)backend; + + return 0; +} + +static int config_set_readonly(git_config_backend *cfg, const char *name, const char *value) +{ + GIT_UNUSED(cfg); + GIT_UNUSED(name); + GIT_UNUSED(value); + + return config_error_readonly(); +} + +static int config_set_multivar_readonly( + git_config_backend *cfg, const char *name, const char *regexp, const char *value) +{ + GIT_UNUSED(cfg); + GIT_UNUSED(name); + GIT_UNUSED(regexp); + GIT_UNUSED(value); + + return config_error_readonly(); +} + +static int config_delete_multivar_readonly(git_config_backend *cfg, const char *name, const char *regexp) +{ + GIT_UNUSED(cfg); + GIT_UNUSED(name); + GIT_UNUSED(regexp); + + return config_error_readonly(); +} + +static int config_delete_readonly(git_config_backend *cfg, const char *name) +{ + GIT_UNUSED(cfg); + GIT_UNUSED(name); + + return config_error_readonly(); +} + +static void backend_readonly_free(git_config_backend *_backend) +{ + diskfile_backend *backend = (diskfile_backend *)_backend; + + if (backend == NULL) + return; + + refcounted_strmap_free(backend->header.values); + git_mutex_free(&backend->header.values_mutex); + git__free(backend); +} + +static int config_readonly_open(git_config_backend *cfg, git_config_level_t level) +{ + diskfile_readonly_backend *b = (diskfile_readonly_backend *) cfg; + diskfile_backend *src = b->snapshot_from; + diskfile_header *src_header = &src->header; + refcounted_strmap *src_map; + int error; + + if (!src_header->readonly && (error = config_refresh(&src_header->parent)) < 0) + return error; + + /* We're just copying data, don't care about the level */ + GIT_UNUSED(level); + + src_map = refcounted_strmap_take(src_header); + b->header.values = src_map; + + return 0; +} + +int git_config_file__snapshot(git_config_backend **out, diskfile_backend *in) +{ + diskfile_readonly_backend *backend; + + backend = git__calloc(1, sizeof(diskfile_readonly_backend)); + GITERR_CHECK_ALLOC(backend); + + backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION; + git_mutex_init(&backend->header.values_mutex); + + backend->snapshot_from = in; + + backend->header.readonly = 1; + backend->header.parent.version = GIT_CONFIG_BACKEND_VERSION; + backend->header.parent.open = config_readonly_open; + backend->header.parent.get = config_get; + backend->header.parent.set = config_set_readonly; + backend->header.parent.set_multivar = config_set_multivar_readonly; + backend->header.parent.del = config_delete_readonly; + backend->header.parent.del_multivar = config_delete_multivar_readonly; + backend->header.parent.iterator = config_iterator_new; + backend->header.parent.free = backend_readonly_free; *out = (git_config_backend *)backend; @@ -1000,7 +1159,7 @@ } /* skip any space at the end */ - if (ptr > line && git__isspace(ptr[-1])) { + while (ptr > line && git__isspace(ptr[-1])) { ptr--; } ptr[0] = '\0'; @@ -1012,21 +1171,20 @@ { /* From the user's home */ if (path[0] == '~' && path[1] == '/') - return git_futils_find_global_file(out, &path[1]); + return git_sysdir_find_global_file(out, &path[1]); return git_path_join_unrooted(out, path, dir, NULL); } -static int config_parse(diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth) +static int config_parse(git_strmap *values, diskfile_backend *cfg_file, struct reader *reader, git_config_level_t level, int depth) { int c; char *current_section = NULL; char *var_name; char *var_value; - cvar_t *var, *existing; + cvar_t *var; git_buf buf = GIT_BUF_INIT; int result = 0; - khiter_t pos; uint32_t reader_idx; if (depth >= MAX_INCLUDE_DEPTH) { @@ -1070,40 +1228,32 @@ if (result < 0) break; - var = git__malloc(sizeof(cvar_t)); - GITERR_CHECK_ALLOC(var); - memset(var, 0x0, sizeof(cvar_t)); - var->entry = git__malloc(sizeof(git_config_entry)); - GITERR_CHECK_ALLOC(var->entry); - memset(var->entry, 0x0, sizeof(git_config_entry)); - git__strtolower(var_name); git_buf_printf(&buf, "%s.%s", current_section, var_name); git__free(var_name); - if (git_buf_oom(&buf)) + if (git_buf_oom(&buf)) { + git__free(var_value); return -1; + } + + var = git__calloc(1, sizeof(cvar_t)); + GITERR_CHECK_ALLOC(var); + var->entry = git__calloc(1, sizeof(git_config_entry)); + GITERR_CHECK_ALLOC(var->entry); var->entry->name = git_buf_detach(&buf); var->entry->value = var_value; var->entry->level = level; var->included = !!depth; - /* Add or append the new config option */ - pos = git_strmap_lookup_index(cfg_file->values, var->entry->name); - if (!git_strmap_valid_index(cfg_file->values, pos)) { - git_strmap_insert(cfg_file->values, var->entry->name, var, result); - if (result < 0) - break; + + if ((result = append_entry(values, var)) < 0) + break; + else result = 0; - } else { - existing = git_strmap_value_at(cfg_file->values, pos); - while (existing->next != NULL) { - existing = existing->next; - } - existing->next = var; - } + /* Add or append the new config option */ if (!git__strcmp(var->entry->name, "include.path")) { struct reader *r; git_buf path = GIT_BUF_INIT; @@ -1117,7 +1267,7 @@ if ((result = git_path_dirname_r(&path, reader->file_path)) < 0) break; - /* We need to know out index in the array, as the next config_parse call may realloc */ + /* We need to know our index in the array, as the next config_parse call may realloc */ index = git_array_size(cfg_file->readers) - 1; dir = git_buf_detach(&path); result = included_path(&path, dir, var->entry->value); @@ -1128,12 +1278,19 @@ r->file_path = git_buf_detach(&path); git_buf_init(&r->buffer, 0); - if ((result = git_futils_readbuffer_updated(&r->buffer, r->file_path, &r->file_mtime, - &r->file_size, NULL)) < 0) - break; + result = git_futils_readbuffer_updated(&r->buffer, r->file_path, &r->file_mtime, + &r->file_size, NULL); + + if (result == 0) { + result = config_parse(values, cfg_file, r, level, depth+1); + r = git_array_get(cfg_file->readers, index); + reader = git_array_get(cfg_file->readers, reader_idx); + } + else if (result == GIT_ENOTFOUND) { + giterr_clear(); + result = 0; + } - result = config_parse(cfg_file, r, level, depth+1); - r = git_array_get(cfg_file->readers, index); git_buf_free(&r->buffer); if (result < 0) @@ -1366,23 +1523,23 @@ git_filebuf_write(&file, reader->buffer.ptr, reader->buffer.size); + if (reader->buffer.size > 0 && *(reader->buffer.ptr + reader->buffer.size - 1) != '\n') + git_filebuf_write(&file, "\n", 1); + /* And now if we just need to add a variable */ if (!section_matches && write_section(&file, section) < 0) goto rewrite_fail; /* Sanity check: if we are here, and value is NULL, that means that somebody - * touched the config file after our intial read. We should probably assert() + * touched the config file after our initial read. We should probably assert() * this, but instead we'll handle it gracefully with an error. */ if (value == NULL) { giterr_set(GITERR_CONFIG, - "Race condition when writing a config file (a cvar has been removed)"); + "race condition when writing a config file (a cvar has been removed)"); goto rewrite_fail; } /* If we are here, there is at least a section line */ - if (reader->buffer.size > 0 && *(reader->buffer.ptr + reader->buffer.size - 1) != '\n') - git_filebuf_write(&file, "\n", 1); - q = quotes_for_value(value); git_filebuf_printf(&file, "\t%s = %s%s%s\n", name, q, value, q); } @@ -1493,7 +1650,7 @@ } /* An odd number means last backslash wasn't escaped, so it's multiline */ - return (end > str) && (count & 1); + return count & 1; } static int parse_multiline_variable(struct reader *reader, git_buf *value, int in_quotes) diff -Nru libgit2-0.20.0/src/config_file.h libgit2-0.22.2/src/config_file.h --- libgit2-0.20.0/src/config_file.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/config_file.h 2015-03-24 16:10:45.000000000 +0000 @@ -16,7 +16,8 @@ GIT_INLINE(void) git_config_file_free(git_config_backend *cfg) { - cfg->free(cfg); + if (cfg) + cfg->free(cfg); } GIT_INLINE(int) git_config_file_get_string( diff -Nru libgit2-0.20.0/src/config.h libgit2-0.22.2/src/config.h --- libgit2-0.20.0/src/config.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/config.h 2015-03-24 16:10:45.000000000 +0000 @@ -24,11 +24,6 @@ git_vector files; }; -extern int git_config_find_global_r(git_buf *global_config_path); -extern int git_config_find_xdg_r(git_buf *system_config_path); -extern int git_config_find_system_r(git_buf *system_config_path); - - extern int git_config__global_location(git_buf *buf); extern int git_config_rename_section( @@ -51,5 +46,40 @@ extern int git_config__normalize_name(const char *in, char **out); +/* internal only: does not normalize key and sets out to NULL if not found */ +extern int git_config__lookup_entry( + const git_config_entry **out, + const git_config *cfg, + const char *key, + bool no_errors); + +/* internal only: update and/or delete entry string with constraints */ +extern int git_config__update_entry( + git_config *cfg, + const char *key, + const char *value, + bool overwrite_existing, + bool only_if_existing); + +/* + * Lookup functions that cannot fail. These functions look up a config + * value and return a fallback value if the value is missing or if any + * failures occur while trying to access the value. + */ + +extern const char *git_config__get_string_force( + const git_config *cfg, const char *key, const char *fallback_value); + +extern int git_config__get_bool_force( + const git_config *cfg, const char *key, int fallback_value); + +extern int git_config__get_int_force( + const git_config *cfg, const char *key, int fallback_value); + +/* API for repository cvar-style lookups from config - not cached, but + * uses cvar value maps and fallbacks + */ +extern int git_config__cvar( + int *out, git_config *config, git_cvar_cached cvar); #endif diff -Nru libgit2-0.20.0/src/crlf.c libgit2-0.22.2/src/crlf.c --- libgit2-0.20.0/src/crlf.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/crlf.c 2015-03-24 16:10:45.000000000 +0000 @@ -21,6 +21,7 @@ int crlf_action; int eol; int auto_crlf; + int safe_crlf; }; struct crlf_filter { @@ -101,7 +102,7 @@ if (!S_ISREG(entry->mode)) /* don't crlf filter non-blobs */ return true; - if (git_blob_lookup(&blob, repo, &entry->oid) < 0) + if (git_blob_lookup(&blob, repo, &entry->id) < 0) return false; blobcontent = git_blob_rawcontent(blob); @@ -137,6 +138,26 @@ if (git_buf_text_gather_stats(&stats, from, false)) return GIT_PASSTHROUGH; + /* If there are no CR characters to filter out, then just pass */ + if (!stats.cr) + return GIT_PASSTHROUGH; + + /* If safecrlf is enabled, sanity-check the result. */ + if (stats.cr != stats.crlf || stats.lf != stats.crlf) { + switch (ca->safe_crlf) { + case GIT_SAFE_CRLF_FAIL: + giterr_set( + GITERR_FILTER, "LF would be replaced by CRLF in '%s'", + git_filter_source_path(src)); + return -1; + case GIT_SAFE_CRLF_WARN: + /* TODO: issue warning when warning API is available */; + break; + default: + break; + } + } + /* * We're currently not going to even try to convert stuff * that has bare CR characters. Does anybody do that crazy @@ -218,24 +239,11 @@ if (!workdir_ending) return -1; - if (!strcmp("\n", workdir_ending)) { - if (ca->crlf_action == GIT_CRLF_GUESS && ca->auto_crlf) - return GIT_PASSTHROUGH; - - if (git_buf_find(from, '\r') < 0) - return GIT_PASSTHROUGH; - - if (git_buf_text_crlf_to_lf(to, from) < 0) - return -1; - } else { - /* only other supported option is lf->crlf conversion */ - assert(!strcmp("\r\n", workdir_ending)); - - if (git_buf_text_lf_to_crlf(to, from) < 0) - return -1; - } + /* only LF->CRLF conversion is supported, do nothing on LF platforms */ + if (strcmp(workdir_ending, "\r\n") != 0) + return GIT_PASSTHROUGH; - return 0; + return git_buf_text_lf_to_crlf(to, from); } static int crlf_check( @@ -269,14 +277,34 @@ if (ca.crlf_action == GIT_CRLF_BINARY) return GIT_PASSTHROUGH; - if (ca.crlf_action == GIT_CRLF_GUESS) { + if (ca.crlf_action == GIT_CRLF_GUESS || + (ca.crlf_action == GIT_CRLF_AUTO && + git_filter_source_mode(src) == GIT_FILTER_SMUDGE)) { + error = git_repository__cvar( &ca.auto_crlf, git_filter_source_repo(src), GIT_CVAR_AUTO_CRLF); if (error < 0) return error; - if (ca.auto_crlf == GIT_AUTO_CRLF_FALSE) + if (ca.crlf_action == GIT_CRLF_GUESS && + ca.auto_crlf == GIT_AUTO_CRLF_FALSE) return GIT_PASSTHROUGH; + + if (ca.auto_crlf == GIT_AUTO_CRLF_INPUT && + git_filter_source_mode(src) == GIT_FILTER_SMUDGE) + return GIT_PASSTHROUGH; + } + + if (git_filter_source_mode(src) == GIT_FILTER_CLEAN) { + error = git_repository__cvar( + &ca.safe_crlf, git_filter_source_repo(src), GIT_CVAR_SAFE_CRLF); + if (error < 0) + return error; + + /* downgrade FAIL to WARN if ALLOW_UNSAFE option is used */ + if ((git_filter_source_options(src) & GIT_FILTER_OPT_ALLOW_UNSAFE) && + ca.safe_crlf == GIT_SAFE_CRLF_FAIL) + ca.safe_crlf = GIT_SAFE_CRLF_WARN; } *payload = git__malloc(sizeof(ca)); @@ -317,6 +345,8 @@ git_filter *git_crlf_filter_new(void) { struct crlf_filter *f = git__calloc(1, sizeof(struct crlf_filter)); + if (f == NULL) + return NULL; f->f.version = GIT_FILTER_VERSION; f->f.attributes = "crlf eol text"; diff -Nru libgit2-0.20.0/src/date.c libgit2-0.22.2/src/date.c --- libgit2-0.20.0/src/date.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/date.c 2015-03-24 16:10:45.000000000 +0000 @@ -874,3 +874,31 @@ *out = approxidate_str(date, time_sec, &error_ret); return error_ret; } + +int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date) +{ + int written; + struct tm gmt; + time_t t; + + assert(out && date); + + t = (time_t) (date->time + date->offset * 60); + + if (p_gmtime_r (&t, &gmt) == NULL) + return -1; + + written = p_snprintf(out, len, "%.3s, %u %.3s %.4u %02u:%02u:%02u %+03d%02d", + weekday_names[gmt.tm_wday], + gmt.tm_mday, + month_names[gmt.tm_mon], + gmt.tm_year + 1900, + gmt.tm_hour, gmt.tm_min, gmt.tm_sec, + date->offset / 60, date->offset % 60); + + if (written < 0 || (written > (int) len - 1)) + return -1; + + return 0; +} + diff -Nru libgit2-0.20.0/src/delta.c libgit2-0.22.2/src/delta.c --- libgit2-0.20.0/src/delta.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/delta.c 2015-03-24 16:10:45.000000000 +0000 @@ -144,7 +144,7 @@ entries = 0xfffffffeU / RABIN_WINDOW; } hsize = entries / 4; - for (i = 4; (1u << i) < hsize && i < 31; i++); + for (i = 4; i < 31 && (1u << i) < hsize; i++); hsize = 1 << i; hmask = hsize - 1; diff -Nru libgit2-0.20.0/src/describe.c libgit2-0.22.2/src/describe.c --- libgit2-0.20.0/src/describe.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/describe.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,890 @@ +/* + * 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/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" +#include "refs.h" +#include "revwalk.h" +#include "tag.h" +#include "vector.h" +#include "repository.h" + +/* Ported from https://github.com/git/git/blob/89dde7882f71f846ccd0359756d27bebc31108de/builtin/describe.c */ + +struct commit_name { + git_tag *tag; + unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */ + unsigned name_checked:1; + git_oid sha1; + char *path; + + /* Khash workaround. They original key has to still be reachable */ + git_oid peeled; +}; + +static void *oidmap_value_bykey(git_oidmap *map, const git_oid *key) +{ + khint_t pos = git_oidmap_lookup_index(map, key); + + if (!git_oidmap_valid_index(map, pos)) + return NULL; + + return git_oidmap_value_at(map, pos); +} + +static struct commit_name *find_commit_name( + git_oidmap *names, + const git_oid *peeled) +{ + return (struct commit_name *)(oidmap_value_bykey(names, peeled)); +} + +static int replace_name( + git_tag **tag, + git_repository *repo, + struct commit_name *e, + unsigned int prio, + const git_oid *sha1) +{ + git_time_t e_time = 0, t_time = 0; + + if (!e || e->prio < prio) + return 1; + + if (e->prio == 2 && prio == 2) { + /* Multiple annotated tags point to the same commit. + * Select one to keep based upon their tagger date. + */ + git_tag *t = NULL; + + if (!e->tag) { + if (git_tag_lookup(&t, repo, &e->sha1) < 0) + return 1; + e->tag = t; + } + + if (git_tag_lookup(&t, repo, sha1) < 0) + return 0; + + *tag = t; + + if (e->tag->tagger) + e_time = e->tag->tagger->when.time; + + if (t->tagger) + t_time = t->tagger->when.time; + + if (e_time < t_time) + return 1; + } + + return 0; +} + +static int add_to_known_names( + git_repository *repo, + git_oidmap *names, + const char *path, + const git_oid *peeled, + unsigned int prio, + const git_oid *sha1) +{ + struct commit_name *e = find_commit_name(names, peeled); + bool found = (e != NULL); + + git_tag *tag = NULL; + if (replace_name(&tag, repo, e, prio, sha1)) { + if (!found) { + e = git__malloc(sizeof(struct commit_name)); + GITERR_CHECK_ALLOC(e); + + e->path = NULL; + e->tag = NULL; + } + + if (e->tag) + git_tag_free(e->tag); + e->tag = tag; + e->prio = prio; + e->name_checked = 0; + git_oid_cpy(&e->sha1, sha1); + git__free(e->path); + e->path = git__strdup(path); + git_oid_cpy(&e->peeled, peeled); + + if (!found) { + int ret; + + git_oidmap_insert(names, &e->peeled, e, ret); + if (ret < 0) + return -1; + } + } + else + git_tag_free(tag); + + return 0; +} + +static int retrieve_peeled_tag_or_object_oid( + git_oid *peeled_out, + git_oid *ref_target_out, + git_repository *repo, + const char *refname) +{ + git_reference *ref; + git_object *peeled = NULL; + int error; + + if ((error = git_reference_lookup_resolved(&ref, repo, refname, -1)) < 0) + return error; + + if ((error = git_reference_peel(&peeled, ref, GIT_OBJ_ANY)) < 0) + goto cleanup; + + git_oid_cpy(ref_target_out, git_reference_target(ref)); + git_oid_cpy(peeled_out, git_object_id(peeled)); + + if (git_oid_cmp(ref_target_out, peeled_out) != 0) + error = 1; /* The reference was pointing to a annotated tag */ + else + error = 0; /* Any other object */ + +cleanup: + git_reference_free(ref); + git_object_free(peeled); + return error; +} + +struct git_describe_result { + int dirty; + int exact_match; + int fallback_to_id; + git_oid commit_id; + git_repository *repo; + struct commit_name *name; + struct possible_tag *tag; +}; + +struct get_name_data +{ + git_describe_options *opts; + git_repository *repo; + git_oidmap *names; + git_describe_result *result; +}; + +static int commit_name_dup(struct commit_name **out, struct commit_name *in) +{ + struct commit_name *name; + + name = git__malloc(sizeof(struct commit_name)); + GITERR_CHECK_ALLOC(name); + + memcpy(name, in, sizeof(struct commit_name)); + name->tag = NULL; + name->path = NULL; + + if (in->tag && git_object_dup((git_object **) &name->tag, (git_object *) in->tag) < 0) + return -1; + + name->path = git__strdup(in->path); + GITERR_CHECK_ALLOC(name->path); + + *out = name; + return 0; +} + +static int get_name(const char *refname, void *payload) +{ + struct get_name_data *data; + bool is_tag, is_annotated, all; + git_oid peeled, sha1; + unsigned int prio; + int error = 0; + + data = (struct get_name_data *)payload; + is_tag = !git__prefixcmp(refname, GIT_REFS_TAGS_DIR); + all = data->opts->describe_strategy == GIT_DESCRIBE_ALL; + + /* Reject anything outside refs/tags/ unless --all */ + if (!all && !is_tag) + return 0; + + /* Accept only tags that match the pattern, if given */ + if (data->opts->pattern && (!is_tag || p_fnmatch(data->opts->pattern, + refname + strlen(GIT_REFS_TAGS_DIR), 0))) + return 0; + + /* Is it annotated? */ + if ((error = retrieve_peeled_tag_or_object_oid( + &peeled, &sha1, data->repo, refname)) < 0) + return error; + + is_annotated = error; + + /* + * By default, we only use annotated tags, but with --tags + * we fall back to lightweight ones (even without --tags, + * we still remember lightweight ones, only to give hints + * in an error message). --all allows any refs to be used. + */ + if (is_annotated) + prio = 2; + else if (is_tag) + prio = 1; + else + prio = 0; + + add_to_known_names(data->repo, data->names, + all ? refname + strlen(GIT_REFS_DIR) : refname + strlen(GIT_REFS_TAGS_DIR), + &peeled, prio, &sha1); + return 0; +} + +struct possible_tag { + struct commit_name *name; + int depth; + int found_order; + unsigned flag_within; +}; + +static int possible_tag_dup(struct possible_tag **out, struct possible_tag *in) +{ + struct possible_tag *tag; + int error; + + tag = git__malloc(sizeof(struct possible_tag)); + GITERR_CHECK_ALLOC(tag); + + memcpy(tag, in, sizeof(struct possible_tag)); + tag->name = NULL; + + if ((error = commit_name_dup(&tag->name, in->name)) < 0) { + git__free(tag); + *out = NULL; + return error; + } + + *out = tag; + return 0; +} + +static int compare_pt(const void *a_, const void *b_) +{ + struct possible_tag *a = (struct possible_tag *)a_; + struct possible_tag *b = (struct possible_tag *)b_; + if (a->depth != b->depth) + return a->depth - b->depth; + if (a->found_order != b->found_order) + return a->found_order - b->found_order; + return 0; +} + +#define SEEN (1u << 0) + +static unsigned long finish_depth_computation( + git_pqueue *list, + git_revwalk *walk, + struct possible_tag *best) +{ + unsigned long seen_commits = 0; + int error, i; + + while (git_pqueue_size(list) > 0) { + git_commit_list_node *c = git_pqueue_pop(list); + seen_commits++; + if (c->flags & best->flag_within) { + size_t index = 0; + while (git_pqueue_size(list) > index) { + git_commit_list_node *i = git_pqueue_get(list, index); + if (!(i->flags & best->flag_within)) + break; + index++; + } + if (index > git_pqueue_size(list)) + break; + } else + best->depth++; + for (i = 0; i < c->out_degree; i++) { + git_commit_list_node *p = c->parents[i]; + if ((error = git_commit_list_parse(walk, p)) < 0) + return error; + if (!(p->flags & SEEN)) + if ((error = git_pqueue_insert(list, p)) < 0) + return error; + p->flags |= c->flags; + } + } + return seen_commits; +} + +static int display_name(git_buf *buf, git_repository *repo, struct commit_name *n) +{ + if (n->prio == 2 && !n->tag) { + if (git_tag_lookup(&n->tag, repo, &n->sha1) < 0) { + giterr_set(GITERR_TAG, "Annotated tag '%s' not available", n->path); + return -1; + } + } + + if (n->tag && !n->name_checked) { + if (!git_tag_name(n->tag)) { + giterr_set(GITERR_TAG, "Annotated tag '%s' has no embedded name", n->path); + return -1; + } + + /* TODO: Cope with warnings + if (strcmp(n->tag->tag, all ? n->path + 5 : n->path)) + warning(_("tag '%s' is really '%s' here"), n->tag->tag, n->path); + */ + + n->name_checked = 1; + } + + if (n->tag) + git_buf_printf(buf, "%s", git_tag_name(n->tag)); + else + git_buf_printf(buf, "%s", n->path); + + return 0; +} + +static int find_unique_abbrev_size( + int *out, + git_repository *repo, + const git_oid *oid_in, + int abbreviated_size) +{ + size_t size = abbreviated_size; + git_odb *odb; + git_oid dummy; + int error; + + if ((error = git_repository_odb__weakptr(&odb, repo)) < 0) + return error; + + while (size < GIT_OID_HEXSZ) { + if ((error = git_odb_exists_prefix(&dummy, odb, oid_in, size)) == 0) { + *out = (int) size; + return 0; + } + + /* If the error wasn't that it's not unique, then it's a proper error */ + if (error != GIT_EAMBIGUOUS) + return error; + + /* Try again with a larger size */ + size++; + } + + /* If we didn't find any shorter prefix, we have to do the whole thing */ + *out = GIT_OID_HEXSZ; + + return 0; +} + +static int show_suffix( + git_buf *buf, + int depth, + git_repository *repo, + const git_oid* id, + size_t abbrev_size) +{ + int error, size = 0; + + char hex_oid[GIT_OID_HEXSZ]; + + if ((error = find_unique_abbrev_size(&size, repo, id, abbrev_size)) < 0) + return error; + + git_oid_fmt(hex_oid, id); + + git_buf_printf(buf, "-%d-g", depth); + + git_buf_put(buf, hex_oid, size); + + return git_buf_oom(buf) ? -1 : 0; +} + +#define MAX_CANDIDATES_TAGS FLAG_BITS - 1 + +static int describe_not_found(const git_oid *oid, const char *message_format) { + char oid_str[GIT_OID_HEXSZ + 1]; + git_oid_tostr(oid_str, sizeof(oid_str), oid); + + giterr_set(GITERR_DESCRIBE, message_format, oid_str); + return GIT_ENOTFOUND; +} + +static int describe( + struct get_name_data *data, + git_commit *commit) +{ + struct commit_name *n; + struct possible_tag *best; + bool all, tags; + git_revwalk *walk = NULL; + git_pqueue list; + git_commit_list_node *cmit, *gave_up_on = NULL; + git_vector all_matches = GIT_VECTOR_INIT; + unsigned int match_cnt = 0, annotated_cnt = 0, cur_match; + unsigned long seen_commits = 0; /* TODO: Check long */ + unsigned int unannotated_cnt = 0; + int error; + + if (git_vector_init(&all_matches, MAX_CANDIDATES_TAGS, compare_pt) < 0) + return -1; + + if ((error = git_pqueue_init(&list, 0, 2, git_commit_list_time_cmp)) < 0) + goto cleanup; + + all = data->opts->describe_strategy == GIT_DESCRIBE_ALL; + tags = data->opts->describe_strategy == GIT_DESCRIBE_TAGS; + + git_oid_cpy(&data->result->commit_id, git_commit_id(commit)); + + n = find_commit_name(data->names, git_commit_id(commit)); + if (n && (tags || all || n->prio == 2)) { + /* + * Exact match to an existing ref. + */ + data->result->exact_match = 1; + if ((error = commit_name_dup(&data->result->name, n)) < 0) + goto cleanup; + + goto cleanup; + } + + if (!data->opts->max_candidates_tags) { + error = describe_not_found( + git_commit_id(commit), + "Cannot describe - no tag exactly matches '%s'"); + + goto cleanup; + } + + if ((error = git_revwalk_new(&walk, git_commit_owner(commit))) < 0) + goto cleanup; + + if ((cmit = git_revwalk__commit_lookup(walk, git_commit_id(commit))) == NULL) + goto cleanup; + + if ((error = git_commit_list_parse(walk, cmit)) < 0) + goto cleanup; + + cmit->flags = SEEN; + + if ((error = git_pqueue_insert(&list, cmit)) < 0) + goto cleanup; + + while (git_pqueue_size(&list) > 0) + { + int i; + + git_commit_list_node *c = (git_commit_list_node *)git_pqueue_pop(&list); + seen_commits++; + + n = find_commit_name(data->names, &c->oid); + + if (n) { + if (!tags && !all && n->prio < 2) { + unannotated_cnt++; + } else if (match_cnt < data->opts->max_candidates_tags) { + struct possible_tag *t = git__malloc(sizeof(struct commit_name)); + GITERR_CHECK_ALLOC(t); + if ((error = git_vector_insert(&all_matches, t)) < 0) + goto cleanup; + + match_cnt++; + + t->name = n; + t->depth = seen_commits - 1; + t->flag_within = 1u << match_cnt; + t->found_order = match_cnt; + c->flags |= t->flag_within; + if (n->prio == 2) + annotated_cnt++; + } + else { + gave_up_on = c; + break; + } + } + + for (cur_match = 0; cur_match < match_cnt; cur_match++) { + struct possible_tag *t = git_vector_get(&all_matches, cur_match); + if (!(c->flags & t->flag_within)) + t->depth++; + } + + if (annotated_cnt && (git_pqueue_size(&list) == 0)) { + /* + if (debug) { + char oid_str[GIT_OID_HEXSZ + 1]; + git_oid_tostr(oid_str, sizeof(oid_str), &c->oid); + + fprintf(stderr, "finished search at %s\n", oid_str); + } + */ + break; + } + for (i = 0; i < c->out_degree; i++) { + git_commit_list_node *p = c->parents[i]; + if ((error = git_commit_list_parse(walk, p)) < 0) + goto cleanup; + if (!(p->flags & SEEN)) + if ((error = git_pqueue_insert(&list, p)) < 0) + goto cleanup; + p->flags |= c->flags; + + if (data->opts->only_follow_first_parent) + break; + } + } + + if (!match_cnt) { + if (data->opts->show_commit_oid_as_fallback) { + data->result->fallback_to_id = 1; + git_oid_cpy(&data->result->commit_id, &cmit->oid); + + goto cleanup; + } + if (unannotated_cnt) { + error = describe_not_found(git_commit_id(commit), + "Cannot describe - " + "No annotated tags can describe '%s'." + "However, there were unannotated tags."); + goto cleanup; + } + else { + error = describe_not_found(git_commit_id(commit), + "Cannot describe - " + "No tags can describe '%s'."); + goto cleanup; + } + } + + git_vector_sort(&all_matches); + + best = (struct possible_tag *)git_vector_get(&all_matches, 0); + + if (gave_up_on) { + git_pqueue_insert(&list, gave_up_on); + seen_commits--; + } + if ((error = finish_depth_computation( + &list, walk, best)) < 0) + goto cleanup; + + seen_commits += error; + if ((error = possible_tag_dup(&data->result->tag, best)) < 0) + goto cleanup; + + /* + { + static const char *prio_names[] = { + "head", "lightweight", "annotated", + }; + + char oid_str[GIT_OID_HEXSZ + 1]; + + if (debug) { + for (cur_match = 0; cur_match < match_cnt; cur_match++) { + struct possible_tag *t = (struct possible_tag *)git_vector_get(&all_matches, cur_match); + fprintf(stderr, " %-11s %8d %s\n", + prio_names[t->name->prio], + t->depth, t->name->path); + } + fprintf(stderr, "traversed %lu commits\n", seen_commits); + if (gave_up_on) { + git_oid_tostr(oid_str, sizeof(oid_str), &gave_up_on->oid); + fprintf(stderr, + "more than %i tags found; listed %i most recent\n" + "gave up search at %s\n", + data->opts->max_candidates_tags, data->opts->max_candidates_tags, + oid_str); + } + } + } + */ + + git_oid_cpy(&data->result->commit_id, &cmit->oid); + +cleanup: + { + size_t i; + struct possible_tag *match; + git_vector_foreach(&all_matches, i, match) { + git__free(match); + } + } + git_vector_free(&all_matches); + git_pqueue_free(&list); + git_revwalk_free(walk); + return error; +} + +static int normalize_options( + git_describe_options *dst, + const git_describe_options *src) +{ + git_describe_options default_options = GIT_DESCRIBE_OPTIONS_INIT; + if (!src) src = &default_options; + + *dst = *src; + + if (dst->max_candidates_tags > GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS) + dst->max_candidates_tags = GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS; + + return 0; +} + +int git_describe_commit( + git_describe_result **result, + git_object *committish, + git_describe_options *opts) +{ + struct get_name_data data; + struct commit_name *name; + git_commit *commit; + int error = -1; + git_describe_options normalized; + + assert(committish); + + data.result = git__calloc(1, sizeof(git_describe_result)); + GITERR_CHECK_ALLOC(data.result); + data.result->repo = git_object_owner(committish); + + data.repo = git_object_owner(committish); + + if ((error = normalize_options(&normalized, opts)) < 0) + return error; + + GITERR_CHECK_VERSION( + &normalized, + GIT_DESCRIBE_OPTIONS_VERSION, + "git_describe_options"); + data.opts = &normalized; + + data.names = git_oidmap_alloc(); + GITERR_CHECK_ALLOC(data.names); + + /** TODO: contains to be implemented */ + + if ((error = git_object_peel((git_object **)(&commit), committish, GIT_OBJ_COMMIT)) < 0) + goto cleanup; + + if ((error = git_reference_foreach_name( + git_object_owner(committish), + get_name, &data)) < 0) + goto cleanup; + + if (git_oidmap_size(data.names) == 0) { + giterr_set(GITERR_DESCRIBE, "Cannot describe - " + "No reference found, cannot describe anything."); + error = -1; + goto cleanup; + } + + if ((error = describe(&data, commit)) < 0) + goto cleanup; + +cleanup: + git_commit_free(commit); + + git_oidmap_foreach_value(data.names, name, { + git_tag_free(name->tag); + git__free(name->path); + git__free(name); + }); + + git_oidmap_free(data.names); + + if (error < 0) + git_describe_result_free(data.result); + else + *result = data.result; + + return error; +} + +int git_describe_workdir( + git_describe_result **out, + git_repository *repo, + git_describe_options *opts) +{ + int error; + git_oid current_id; + git_status_list *status = NULL; + git_status_options status_opts = GIT_STATUS_OPTIONS_INIT; + git_describe_result *result = NULL; + git_object *commit; + + if ((error = git_reference_name_to_id(¤t_id, repo, GIT_HEAD_FILE)) < 0) + return error; + + if ((error = git_object_lookup(&commit, repo, ¤t_id, GIT_OBJ_COMMIT)) < 0) + return error; + + /* The first step is to perform a describe of HEAD, so we can leverage this */ + if ((error = git_describe_commit(&result, commit, opts)) < 0) + goto out; + + if ((error = git_status_list_new(&status, repo, &status_opts)) < 0) + goto out; + + + if (git_status_list_entrycount(status) > 0) + result->dirty = 1; + +out: + git_object_free(commit); + git_status_list_free(status); + + if (error < 0) + git_describe_result_free(result); + else + *out = result; + + return error; +} + +static int normalize_format_options( + git_describe_format_options *dst, + const git_describe_format_options *src) +{ + if (!src) { + git_describe_init_format_options(dst, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION); + return 0; + } + + memcpy(dst, src, sizeof(git_describe_format_options)); + return 0; +} + +int git_describe_format(git_buf *out, const git_describe_result *result, const git_describe_format_options *given) +{ + int error; + git_repository *repo; + struct commit_name *name; + git_describe_format_options opts; + + assert(out && result); + + GITERR_CHECK_VERSION(given, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, "git_describe_format_options"); + normalize_format_options(&opts, given); + + git_buf_sanitize(out); + + + if (opts.always_use_long_format && opts.abbreviated_size == 0) { + giterr_set(GITERR_DESCRIBE, "Cannot describe - " + "'always_use_long_format' is incompatible with a zero" + "'abbreviated_size'"); + return -1; + } + + + repo = result->repo; + + /* If we did find an exact match, then it's the easier method */ + if (result->exact_match) { + name = result->name; + if ((error = display_name(out, repo, name)) < 0) + return error; + + if (opts.always_use_long_format) { + const git_oid *id = name->tag ? git_tag_target_id(name->tag) : &result->commit_id; + if ((error = show_suffix(out, 0, repo, id, opts.abbreviated_size)) < 0) + return error; + } + + if (result->dirty && opts.dirty_suffix) + git_buf_puts(out, opts.dirty_suffix); + + return git_buf_oom(out) ? -1 : 0; + } + + /* If we didn't find *any* tags, we fall back to the commit's id */ + if (result->fallback_to_id) { + char hex_oid[GIT_OID_HEXSZ + 1] = {0}; + int size = 0; + + if ((error = find_unique_abbrev_size( + &size, repo, &result->commit_id, opts.abbreviated_size)) < 0) + return -1; + + git_oid_fmt(hex_oid, &result->commit_id); + git_buf_put(out, hex_oid, size); + + if (result->dirty && opts.dirty_suffix) + git_buf_puts(out, opts.dirty_suffix); + + return git_buf_oom(out) ? -1 : 0; + } + + /* Lastly, if we found a matching tag, we show that */ + name = result->tag->name; + + if ((error = display_name(out, repo, name)) < 0) + return error; + + if (opts.abbreviated_size) { + if ((error = show_suffix(out, result->tag->depth, repo, + &result->commit_id, opts.abbreviated_size)) < 0) + return error; + } + + if (result->dirty && opts.dirty_suffix) { + git_buf_puts(out, opts.dirty_suffix); + } + + return git_buf_oom(out) ? -1 : 0; +} + +void git_describe_result_free(git_describe_result *result) +{ + if (result == NULL) + return; + + if (result->name) { + git_tag_free(result->name->tag); + git__free(result->name->path); + git__free(result->name); + } + + if (result->tag) { + git_tag_free(result->tag->name->tag); + git__free(result->tag->name->path); + git__free(result->tag->name); + git__free(result->tag); + } + + git__free(result); +} + +int git_describe_init_options(git_describe_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_describe_options, GIT_DESCRIBE_OPTIONS_INIT); + return 0; +} + +int git_describe_init_format_options(git_describe_format_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_describe_format_options, GIT_DESCRIBE_FORMAT_OPTIONS_INIT); + return 0; +} diff -Nru libgit2-0.20.0/src/diff.c libgit2-0.22.2/src/diff.c --- libgit2-0.20.0/src/diff.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/diff.c 2015-03-24 16:10:45.000000000 +0000 @@ -49,16 +49,29 @@ return delta; } -static int diff_notify( - const git_diff *diff, - const git_diff_delta *delta, - const char *matched_pathspec) +static int diff_insert_delta( + git_diff *diff, git_diff_delta *delta, const char *matched_pathspec) { - if (!diff->opts.notify_cb) - return 0; + int error = 0; + + if (diff->opts.notify_cb) { + error = diff->opts.notify_cb( + diff, delta, matched_pathspec, diff->opts.notify_payload); + + if (error) { + git__free(delta); + + if (error > 0) /* positive value means to skip this delta */ + return 0; + else /* negative value means to cancel diff */ + return giterr_set_after_callback_function(error, "git_diff"); + } + } + + if ((error = git_vector_insert(&diff->deltas, delta)) < 0) + git__free(delta); - return diff->opts.notify_cb( - diff, delta, matched_pathspec, diff->opts.notify_payload); + return error; } static int diff_delta__from_one( @@ -68,7 +81,6 @@ { git_diff_delta *delta; const char *matched_pathspec; - int notify_res; if ((entry->flags & GIT_IDXENTRY_VALID) != 0) return 0; @@ -80,6 +92,10 @@ if (status == GIT_DELTA_UNTRACKED && DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED)) return 0; + + if (status == GIT_DELTA_UNREADABLE && + DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE)) + return 0; if (!git_pathspec__match( &diff->pathspec, entry->path, @@ -98,34 +114,25 @@ if (delta->status == GIT_DELTA_DELETED) { delta->old_file.mode = entry->mode; delta->old_file.size = entry->file_size; - git_oid_cpy(&delta->old_file.oid, &entry->oid); + git_oid_cpy(&delta->old_file.id, &entry->id); } else /* ADDED, IGNORED, UNTRACKED */ { delta->new_file.mode = entry->mode; delta->new_file.size = entry->file_size; - git_oid_cpy(&delta->new_file.oid, &entry->oid); + git_oid_cpy(&delta->new_file.id, &entry->id); } - delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID; + delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID; if (delta->status == GIT_DELTA_DELETED || - !git_oid_iszero(&delta->new_file.oid)) - delta->new_file.flags |= GIT_DIFF_FLAG_VALID_OID; + !git_oid_iszero(&delta->new_file.id)) + delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID; - notify_res = diff_notify(diff, delta, matched_pathspec); - - if (notify_res) - git__free(delta); - else if (git_vector_insert(&diff->deltas, delta) < 0) { - git__free(delta); - return -1; - } - - return notify_res < 0 ? GIT_EUSER : 0; + return diff_insert_delta(diff, delta, matched_pathspec); } static int diff_delta__from_two( git_diff *diff, - git_delta_t status, + git_delta_t status, const git_index_entry *old_entry, uint32_t old_mode, const git_index_entry *new_entry, @@ -134,7 +141,6 @@ const char *matched_pathspec) { git_diff_delta *delta; - int notify_res; const char *canonical_path = old_entry->path; if (status == GIT_DELTA_UNMODIFIED && @@ -154,35 +160,26 @@ GITERR_CHECK_ALLOC(delta); delta->nfiles = 2; - git_oid_cpy(&delta->old_file.oid, &old_entry->oid); + git_oid_cpy(&delta->old_file.id, &old_entry->id); delta->old_file.size = old_entry->file_size; delta->old_file.mode = old_mode; - delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID; + delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID; - git_oid_cpy(&delta->new_file.oid, &new_entry->oid); + git_oid_cpy(&delta->new_file.id, &new_entry->id); delta->new_file.size = new_entry->file_size; delta->new_file.mode = new_mode; if (new_oid) { if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) - git_oid_cpy(&delta->old_file.oid, new_oid); + git_oid_cpy(&delta->old_file.id, new_oid); else - git_oid_cpy(&delta->new_file.oid, new_oid); + git_oid_cpy(&delta->new_file.id, new_oid); } - if (new_oid || !git_oid_iszero(&new_entry->oid)) - delta->new_file.flags |= GIT_DIFF_FLAG_VALID_OID; + if (new_oid || !git_oid_iszero(&new_entry->id)) + delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID; - notify_res = diff_notify(diff, delta, matched_pathspec); - - if (notify_res) - git__free(delta); - else if (git_vector_insert(&diff->deltas, delta) < 0) { - git__free(delta); - return -1; - } - - return notify_res < 0 ? GIT_EUSER : 0; + return diff_insert_delta(diff, delta, matched_pathspec); } static git_diff_delta *diff_delta__last_for_item( @@ -196,21 +193,22 @@ switch (delta->status) { case GIT_DELTA_UNMODIFIED: case GIT_DELTA_DELETED: - if (git_oid__cmp(&delta->old_file.oid, &item->oid) == 0) + if (git_oid__cmp(&delta->old_file.id, &item->id) == 0) return delta; break; case GIT_DELTA_ADDED: - if (git_oid__cmp(&delta->new_file.oid, &item->oid) == 0) + if (git_oid__cmp(&delta->new_file.id, &item->id) == 0) return delta; break; + case GIT_DELTA_UNREADABLE: case GIT_DELTA_UNTRACKED: if (diff->strcomp(delta->new_file.path, item->path) == 0 && - git_oid__cmp(&delta->new_file.oid, &item->oid) == 0) + git_oid__cmp(&delta->new_file.id, &item->id) == 0) return delta; break; case GIT_DELTA_MODIFIED: - if (git_oid__cmp(&delta->old_file.oid, &item->oid) == 0 || - git_oid__cmp(&delta->new_file.oid, &item->oid) == 0) + if (git_oid__cmp(&delta->old_file.id, &item->id) == 0 || + git_oid__cmp(&delta->new_file.id, &item->id) == 0) return delta; break; default: @@ -300,29 +298,13 @@ (flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0) return true; - return false; -} - - -static int config_bool(git_config *cfg, const char *name, int defvalue) -{ - int val = defvalue; - - if (git_config_get_bool(&val, cfg, name) < 0) - giterr_clear(); + if (delta->status == GIT_DELTA_UNREADABLE && + (flags & GIT_DIFF_INCLUDE_UNREADABLE) == 0) + return true; - return val; + return false; } -static int config_int(git_config *cfg, const char *name, int defvalue) -{ - int val = defvalue; - - if (git_config_get_int32(&val, cfg, name) < 0) - giterr_clear(); - - return val; -} static const char *diff_mnemonic_prefix( git_iterator_type_t type, bool left_side) @@ -345,6 +327,31 @@ return pfx; } +static void diff_set_ignore_case(git_diff *diff, bool ignore_case) +{ + if (!ignore_case) { + diff->opts.flags &= ~GIT_DIFF_IGNORE_CASE; + + diff->strcomp = git__strcmp; + diff->strncomp = git__strncmp; + diff->pfxcomp = git__prefixcmp; + diff->entrycomp = git_index_entry_cmp; + + git_vector_set_cmp(&diff->deltas, git_diff_delta__cmp); + } else { + diff->opts.flags |= GIT_DIFF_IGNORE_CASE; + + diff->strcomp = git__strcasecmp; + diff->strncomp = git__strncasecmp; + diff->pfxcomp = git__prefixcmp_icase; + diff->entrycomp = git_index_entry_icmp; + + git_vector_set_cmp(&diff->deltas, git_diff_delta__casecmp); + } + + git_vector_sort(&diff->deltas); +} + static git_diff *diff_list_alloc( git_repository *repo, git_iterator *old_iter, @@ -371,24 +378,10 @@ /* Use case-insensitive compare if either iterator has * the ignore_case bit set */ - if (!git_iterator_ignore_case(old_iter) && - !git_iterator_ignore_case(new_iter)) { - diff->opts.flags &= ~GIT_DIFF_IGNORE_CASE; - - diff->strcomp = git__strcmp; - diff->strncomp = git__strncmp; - diff->pfxcomp = git__prefixcmp; - diff->entrycomp = git_index_entry__cmp; - } else { - diff->opts.flags |= GIT_DIFF_IGNORE_CASE; - - diff->strcomp = git__strcasecmp; - diff->strncomp = git__strncasecmp; - diff->pfxcomp = git__prefixcmp_icase; - diff->entrycomp = git_index_entry__cmp_icase; - - git_vector_set_cmp(&diff->deltas, git_diff_delta__casecmp); - } + diff_set_ignore_case( + diff, + git_iterator_ignore_case(old_iter) || + git_iterator_ignore_case(new_iter)); return diff; } @@ -397,7 +390,7 @@ git_diff *diff, const git_diff_options *opts) { - git_config *cfg; + git_config *cfg = NULL; git_repository *repo = diff->repo; git_pool *pool = &diff->pool; int val; @@ -422,20 +415,20 @@ diff->opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; /* load config values that affect diff behavior */ - if (git_repository_config__weakptr(&cfg, repo) < 0) - return -1; + if ((val = git_repository_config_snapshot(&cfg, repo)) < 0) + return val; - if (!git_repository__cvar(&val, repo, GIT_CVAR_SYMLINKS) && val) + if (!git_config__cvar(&val, cfg, GIT_CVAR_SYMLINKS) && val) diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS; - if (!git_repository__cvar(&val, repo, GIT_CVAR_IGNORESTAT) && val) + if (!git_config__cvar(&val, cfg, GIT_CVAR_IGNORESTAT) && val) diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_IGNORE_STAT; if ((diff->opts.flags & GIT_DIFF_IGNORE_FILEMODE) == 0 && - !git_repository__cvar(&val, repo, GIT_CVAR_FILEMODE) && val) + !git_config__cvar(&val, cfg, GIT_CVAR_FILEMODE) && val) diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_MODE_BITS; - if (!git_repository__cvar(&val, repo, GIT_CVAR_TRUSTCTIME) && val) + if (!git_config__cvar(&val, cfg, GIT_CVAR_TRUSTCTIME) && val) diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME; /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */ @@ -445,8 +438,8 @@ /* If not given explicit `opts`, check `diff.xyz` configs */ if (!opts) { - int context = config_int(cfg, "diff.context", 3); - diff->opts.context_lines = context >= 0 ? (uint16_t)context : 3; + int context = git_config__get_int_force(cfg, "diff.context", 3); + diff->opts.context_lines = context >= 0 ? (uint32_t)context : 3; /* add other defaults here */ } @@ -458,14 +451,21 @@ diff->new_src = tmp_src; } + /* Unset UPDATE_INDEX unless diffing workdir and index */ + if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) && + (!(diff->old_src == GIT_ITERATOR_TYPE_WORKDIR || + diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) || + !(diff->old_src == GIT_ITERATOR_TYPE_INDEX || + diff->new_src == GIT_ITERATOR_TYPE_INDEX))) + diff->opts.flags &= ~GIT_DIFF_UPDATE_INDEX; + /* if ignore_submodules not explicitly set, check diff config */ if (diff->opts.ignore_submodules <= 0) { - const char *str; + const git_config_entry *entry; + git_config__lookup_entry(&entry, cfg, "diff.ignoresubmodules", true); - if (git_config_get_string(&str , cfg, "diff.ignoreSubmodules") < 0) - giterr_clear(); - else if (str != NULL && - git_submodule_parse_ignore(&diff->opts.ignore_submodules, str) < 0) + if (entry && git_submodule_parse_ignore( + &diff->opts.ignore_submodules, entry->value) < 0) giterr_clear(); } @@ -474,9 +474,9 @@ const char *use_old = DIFF_OLD_PREFIX_DEFAULT; const char *use_new = DIFF_NEW_PREFIX_DEFAULT; - if (config_bool(cfg, "diff.noprefix", 0)) { + if (git_config__get_bool_force(cfg, "diff.noprefix", 0)) use_old = use_new = ""; - } else if (config_bool(cfg, "diff.mnemonicprefix", 0)) { + else if (git_config__get_bool_force(cfg, "diff.mnemonicprefix", 0)) { use_old = diff_mnemonic_prefix(diff->old_src, true); use_new = diff_mnemonic_prefix(diff->new_src, false); } @@ -490,8 +490,6 @@ /* strdup prefix from pool so we're not dependent on external data */ diff->opts.old_prefix = diff_strdup_prefix(pool, diff->opts.old_prefix); diff->opts.new_prefix = diff_strdup_prefix(pool, diff->opts.new_prefix); - if (!diff->opts.old_prefix || !diff->opts.new_prefix) - return -1; if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) { const char *tmp_prefix = diff->opts.old_prefix; @@ -499,19 +497,15 @@ diff->opts.new_prefix = tmp_prefix; } - return 0; + git_config_free(cfg); + + /* check strdup results for error */ + return (!diff->opts.old_prefix || !diff->opts.new_prefix) ? -1 : 0; } static void diff_list_free(git_diff *diff) { - git_diff_delta *delta; - unsigned int i; - - git_vector_foreach(&diff->deltas, i, delta) { - git__free(delta); - diff->deltas.contents[i] = NULL; - } - git_vector_free(&diff->deltas); + git_vector_free_deep(&diff->deltas); git_pathspec__vfree(&diff->pathspec); git_pool_clear(&diff->pool); @@ -534,73 +528,106 @@ } int git_diff__oid_for_file( - git_repository *repo, + git_oid *out, + git_diff *diff, const char *path, uint16_t mode, - git_off_t size, - git_oid *oid) + git_off_t size) { - int result = 0; + git_index_entry entry; + + memset(&entry, 0, sizeof(entry)); + entry.mode = mode; + entry.file_size = size; + entry.path = (char *)path; + + return git_diff__oid_for_entry(out, diff, &entry, NULL); +} + +int git_diff__oid_for_entry( + git_oid *out, + git_diff *diff, + const git_index_entry *src, + const git_oid *update_match) +{ + int error = 0; git_buf full_path = GIT_BUF_INIT; + git_index_entry entry = *src; + git_filter_list *fl = NULL; + + memset(out, 0, sizeof(*out)); if (git_buf_joinpath( - &full_path, git_repository_workdir(repo), path) < 0) + &full_path, git_repository_workdir(diff->repo), entry.path) < 0) return -1; - if (!mode) { + if (!entry.mode) { struct stat st; - if (p_stat(path, &st) < 0) { - giterr_set(GITERR_OS, "Could not stat '%s'", path); - result = -1; - goto cleanup; + diff->perf.stat_calls++; + + if (p_stat(full_path.ptr, &st) < 0) { + error = git_path_set_error(errno, entry.path, "stat"); + git_buf_free(&full_path); + return error; } - mode = st.st_mode; - size = st.st_size; + git_index_entry__init_from_stat( + &entry, &st, (diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) != 0); } /* calculate OID for file if possible */ - if (S_ISGITLINK(mode)) { + if (S_ISGITLINK(entry.mode)) { git_submodule *sm; - const git_oid *sm_oid; - if (!git_submodule_lookup(&sm, repo, path) && - (sm_oid = git_submodule_wd_id(sm)) != NULL) - git_oid_cpy(oid, sm_oid); - else { + if (!git_submodule_lookup(&sm, diff->repo, entry.path)) { + const git_oid *sm_oid = git_submodule_wd_id(sm); + if (sm_oid) + git_oid_cpy(out, sm_oid); + git_submodule_free(sm); + } else { /* if submodule lookup failed probably just in an intermediate * state where some init hasn't happened, so ignore the error */ giterr_clear(); - memset(oid, 0, sizeof(*oid)); } - } else if (S_ISLNK(mode)) { - result = git_odb__hashlink(oid, full_path.ptr); - } else if (!git__is_sizet(size)) { - giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'", path); - result = -1; - } else { - git_filter_list *fl = NULL; + } else if (S_ISLNK(entry.mode)) { + error = git_odb__hashlink(out, full_path.ptr); + diff->perf.oid_calculations++; + } else if (!git__is_sizet(entry.file_size)) { + giterr_set(GITERR_OS, "File size overflow (for 32-bits) on '%s'", + entry.path); + error = -1; + } else if (!(error = git_filter_list_load( + &fl, diff->repo, NULL, entry.path, + GIT_FILTER_TO_ODB, GIT_FILTER_OPT_ALLOW_UNSAFE))) + { + int fd = git_futils_open_ro(full_path.ptr); + if (fd < 0) + error = fd; + else { + error = git_odb__hashfd_filtered( + out, fd, (size_t)entry.file_size, GIT_OBJ_BLOB, fl); + p_close(fd); + diff->perf.oid_calculations++; + } - result = git_filter_list_load(&fl, repo, NULL, path, GIT_FILTER_TO_ODB); - if (!result) { - int fd = git_futils_open_ro(full_path.ptr); - if (fd < 0) - result = fd; - else { - result = git_odb__hashfd_filtered( - oid, fd, (size_t)size, GIT_OBJ_BLOB, fl); - p_close(fd); - } + git_filter_list_free(fl); + } + + /* update index for entry if requested */ + if (!error && update_match && git_oid_equal(out, update_match)) { + git_index *idx; - git_filter_list_free(fl); + if (!(error = git_repository_index(&idx, diff->repo))) { + memcpy(&entry.id, out, sizeof(entry.id)); + error = git_index_add(idx, &entry); + git_index_free(idx); } - } + } -cleanup: git_buf_free(&full_path); - return result; + return error; } static bool diff_time_eq( @@ -616,7 +643,6 @@ git_iterator *new_iter; const git_index_entry *oitem; const git_index_entry *nitem; - git_buf ignore_prefix; } diff_in_progress; #define MODE_BITS_MASK 0000777 @@ -650,24 +676,24 @@ } if (ign <= 0 && git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL) - return 0; - - if ((error = git_submodule__status( + /* ignore it */; + else if ((error = git_submodule__status( &sm_status, NULL, NULL, found_oid, sub, ign)) < 0) - return error; + /* return error below */; /* check IS_WD_UNMODIFIED because this case is only used * when the new side of the diff is the working directory */ - if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status)) + else if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status)) *status = GIT_DELTA_MODIFIED; /* now that we have a HEAD OID, check if HEAD moved */ - if ((sm_status & GIT_SUBMODULE_STATUS_IN_WD) != 0 && - !git_oid_equal(&info->oitem->oid, found_oid)) + else if ((sm_status & GIT_SUBMODULE_STATUS_IN_WD) != 0 && + !git_oid_equal(&info->oitem->id, found_oid)) *status = GIT_DELTA_MODIFIED; - return 0; + git_submodule_free(sub); + return error; } static int maybe_modified( @@ -681,7 +707,9 @@ unsigned int omode = oitem->mode; unsigned int nmode = nitem->mode; bool new_is_workdir = (info->new_iter->type == GIT_ITERATOR_TYPE_WORKDIR); + bool modified_uncertain = false; const char *matched_pathspec; + int error = 0; if (!git_pathspec__match( &diff->pathspec, oitem->path, @@ -715,24 +743,28 @@ else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) { if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE)) status = GIT_DELTA_TYPECHANGE; + else if (nmode == GIT_FILEMODE_UNREADABLE) { + if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem))) + error = diff_delta__from_one(diff, GIT_DELTA_UNREADABLE, nitem); + return error; + } else { - if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 || - diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0) - return -1; - return 0; + if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem))) + error = diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem); + return error; } } /* if oids and modes match (and are valid), then file is unmodified */ - else if (git_oid_equal(&oitem->oid, &nitem->oid) && + else if (git_oid_equal(&oitem->id, &nitem->id) && omode == nmode && - !git_oid_iszero(&oitem->oid)) + !git_oid_iszero(&oitem->id)) status = GIT_DELTA_UNMODIFIED; /* if we have an unknown OID and a workdir iterator, then check some * circumstances that can accelerate things or need special handling */ - else if (git_oid_iszero(&nitem->oid) && new_is_workdir) { + else if (git_oid_iszero(&nitem->id) && new_is_workdir) { bool use_ctime = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) != 0); bool use_nanos = ((diff->diffcaps & GIT_DIFFCAPS_TRUST_NANOSECS) != 0); @@ -741,22 +773,28 @@ /* TODO: add check against index file st_mtime to avoid racy-git */ if (S_ISGITLINK(nmode)) { - if (maybe_modified_submodule(&status, &noid, diff, info) < 0) - return -1; + if ((error = maybe_modified_submodule(&status, &noid, diff, info)) < 0) + return error; } /* if the stat data looks different, then mark modified - this just * means that the OID will be recalculated below to confirm change */ - else if (omode != nmode || - oitem->file_size != nitem->file_size || - !diff_time_eq(&oitem->mtime, &nitem->mtime, use_nanos) || + else if (omode != nmode || oitem->file_size != nitem->file_size) { + status = GIT_DELTA_MODIFIED; + modified_uncertain = + (oitem->file_size <= 0 && nitem->file_size > 0); + } + else if (!diff_time_eq(&oitem->mtime, &nitem->mtime, use_nanos) || (use_ctime && !diff_time_eq(&oitem->ctime, &nitem->ctime, use_nanos)) || oitem->ino != nitem->ino || oitem->uid != nitem->uid || oitem->gid != nitem->gid) + { status = GIT_DELTA_MODIFIED; + modified_uncertain = true; + } } /* if mode is GITLINK and submodules are ignored, then skip */ @@ -767,11 +805,15 @@ /* if we got here and decided that the files are modified, but we * haven't calculated the OID of the new item, then calculate it now */ - if (status == GIT_DELTA_MODIFIED && git_oid_iszero(&nitem->oid)) { + if (modified_uncertain && git_oid_iszero(&nitem->id)) { if (git_oid_iszero(&noid)) { - if (git_diff__oid_for_file(diff->repo, - nitem->path, nitem->mode, nitem->file_size, &noid) < 0) - return -1; + const git_oid *update_check = + DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) ? + &oitem->id : NULL; + + if ((error = git_diff__oid_for_entry( + &noid, diff, nitem, update_check)) < 0) + return error; } /* if oid matches, then mark unmodified (except submodules, where @@ -779,7 +821,7 @@ * matches between the index and the workdir HEAD) */ if (omode == nmode && !S_ISGITLINK(omode) && - git_oid_equal(&oitem->oid, &noid)) + git_oid_equal(&oitem->id, &noid)) status = GIT_DELTA_UNMODIFIED; } @@ -805,72 +847,6 @@ item->path[pathlen] == '/'); } -static int diff_scan_inside_untracked_dir( - git_diff *diff, diff_in_progress *info, git_delta_t *delta_type) -{ - int error = 0; - git_buf base = GIT_BUF_INIT; - bool is_ignored; - - *delta_type = GIT_DELTA_IGNORED; - git_buf_sets(&base, info->nitem->path); - - /* advance into untracked directory */ - if ((error = git_iterator_advance_into(&info->nitem, info->new_iter)) < 0) { - - /* skip ahead if empty */ - if (error == GIT_ENOTFOUND) { - giterr_clear(); - error = git_iterator_advance(&info->nitem, info->new_iter); - } - - goto done; - } - - /* look for actual untracked file */ - while (info->nitem != NULL && - !diff->pfxcomp(info->nitem->path, git_buf_cstr(&base))) { - is_ignored = git_iterator_current_is_ignored(info->new_iter); - - /* need to recurse into non-ignored directories */ - if (!is_ignored && S_ISDIR(info->nitem->mode)) { - error = git_iterator_advance_into(&info->nitem, info->new_iter); - - if (!error) - continue; - else if (error == GIT_ENOTFOUND) { - error = 0; - is_ignored = true; /* treat empty as ignored */ - } else - break; /* real error, must stop */ - } - - /* found a non-ignored item - treat parent dir as untracked */ - if (!is_ignored) { - *delta_type = GIT_DELTA_UNTRACKED; - break; - } - - if ((error = git_iterator_advance(&info->nitem, info->new_iter)) < 0) - break; - } - - /* finish off scan */ - while (info->nitem != NULL && - !diff->pfxcomp(info->nitem->path, git_buf_cstr(&base))) { - if ((error = git_iterator_advance(&info->nitem, info->new_iter)) < 0) - break; - } - -done: - git_buf_free(&base); - - if (error == GIT_ITEROVER) - error = 0; - - return error; -} - static int handle_unmatched_new_item( git_diff *diff, diff_in_progress *info) { @@ -882,24 +858,13 @@ /* check if this is a prefix of the other side */ contains_oitem = entry_is_prefixed(diff, info->oitem, nitem); - /* check if this is contained in an ignored parent directory */ - if (git_buf_len(&info->ignore_prefix)) { - if (diff->pfxcomp(nitem->path, git_buf_cstr(&info->ignore_prefix)) == 0) - delta_type = GIT_DELTA_IGNORED; - else - git_buf_clear(&info->ignore_prefix); - } + /* update delta_type if this item is ignored */ + if (git_iterator_current_is_ignored(info->new_iter)) + delta_type = GIT_DELTA_IGNORED; if (nitem->mode == GIT_FILEMODE_TREE) { bool recurse_into_dir = contains_oitem; - /* if not already inside an ignored dir, check if this is ignored */ - if (delta_type != GIT_DELTA_IGNORED && - git_iterator_current_is_ignored(info->new_iter)) { - delta_type = GIT_DELTA_IGNORED; - git_buf_sets(&info->ignore_prefix, nitem->path); - } - /* check if user requests recursion into this type of dir */ recurse_into_dir = contains_oitem || (delta_type == GIT_DELTA_UNTRACKED && @@ -908,12 +873,14 @@ DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)); /* do not advance into directories that contain a .git file */ - if (recurse_into_dir) { + if (recurse_into_dir && !contains_oitem) { git_buf *full = NULL; if (git_iterator_current_workdir_path(&full, info->new_iter) < 0) return -1; - if (full && git_path_contains_dir(full, DOT_GIT)) + if (full && git_path_contains(full, DOT_GIT)) { + /* TODO: warning if not a valid git repository */ recurse_into_dir = false; + } } /* still have to look into untracked directories to match core git - @@ -924,9 +891,10 @@ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS)) { git_diff_delta *last; + git_iterator_status_t untracked_state; /* attempt to insert record for this directory */ - if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0) + if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0) return error; /* if delta wasn't created (because of rules), just skip ahead */ @@ -935,11 +903,14 @@ return git_iterator_advance(&info->nitem, info->new_iter); /* iterate into dir looking for an actual untracked file */ - if (diff_scan_inside_untracked_dir(diff, info, &delta_type) < 0) - return -1; + if ((error = git_iterator_advance_over_with_status( + &info->nitem, &untracked_state, info->new_iter)) < 0 && + error != GIT_ITEROVER) + return error; - /* it iteration changed delta type, the update the record */ - if (delta_type == GIT_DELTA_IGNORED) { + /* if we found nothing or just ignored items, update the record */ + if (untracked_state == GIT_ITERATOR_STATUS_IGNORED || + untracked_state == GIT_ITERATOR_STATUS_EMPTY) { last->status = GIT_DELTA_IGNORED; /* remove the record if we don't want ignored records */ @@ -970,42 +941,42 @@ } } - /* In core git, the next two checks are effectively reversed -- - * i.e. when an file contained in an ignored directory is explicitly - * ignored, it shows up as an ignored file in the diff list, even though - * other untracked files in the same directory are skipped completely. - * - * To me, this seems odd. If the directory is ignored and the file is - * untracked, we should skip it consistently, regardless of whether it - * happens to match a pattern in the ignore file. - * - * To match the core git behavior, reverse the following two if checks - * so that individual file ignores are checked before container - * directory exclusions are used to skip the file. - */ else if (delta_type == GIT_DELTA_IGNORED && - DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS)) + DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS) && + git_iterator_current_tree_is_ignored(info->new_iter)) /* item contained in ignored directory, so skip over it */ return git_iterator_advance(&info->nitem, info->new_iter); - else if (git_iterator_current_is_ignored(info->new_iter)) - delta_type = GIT_DELTA_IGNORED; - else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR) delta_type = GIT_DELTA_ADDED; else if (nitem->mode == GIT_FILEMODE_COMMIT) { - git_submodule *sm; - /* ignore things that are not actual submodules */ - if (git_submodule_lookup(&sm, info->repo, nitem->path) != 0) { + if (git_submodule_lookup(NULL, info->repo, nitem->path) != 0) { giterr_clear(); delta_type = GIT_DELTA_IGNORED; + + /* if this contains a tracked item, treat as normal TREE */ + if (contains_oitem) { + error = git_iterator_advance_into(&info->nitem, info->new_iter); + if (error != GIT_ENOTFOUND) + return error; + + giterr_clear(); + return git_iterator_advance(&info->nitem, info->new_iter); + } } } + else if (nitem->mode == GIT_FILEMODE_UNREADABLE) { + if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED)) + delta_type = GIT_DELTA_UNTRACKED; + else + delta_type = GIT_DELTA_UNREADABLE; + } + /* Actually create the record for this item if necessary */ - if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0) + if ((error = diff_delta__from_one(diff, delta_type, nitem)) != 0) return error; /* If user requested TYPECHANGE records, then check for that instead of @@ -1030,7 +1001,7 @@ git_diff *diff, diff_in_progress *info) { int error = diff_delta__from_one(diff, GIT_DELTA_DELETED, info->oitem); - if (error < 0) + if (error != 0) return error; /* if we are generating TYPECHANGE records then check for that @@ -1092,7 +1063,6 @@ info.repo = repo; info.old_iter = old_iter; info.new_iter = new_iter; - git_buf_init(&info.ignore_prefix, 0); /* make iterators have matching icase behavior */ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE)) { @@ -1139,14 +1109,14 @@ error = 0; } + diff->perf.stat_calls += old_iter->stat_calls + new_iter->stat_calls; + cleanup: if (!error) *diff_ptr = diff; else git_diff_free(diff); - git_buf_free(&info.ignore_prefix); - return error; } @@ -1205,39 +1175,25 @@ const git_diff_options *opts) { int error = 0; - bool reset_index_ignore_case = false; + bool index_ignore_case = false; assert(diff && repo); if (!index && (error = diff_load_index(&index, repo)) < 0) return error; - if (index->ignore_case) { - git_index__set_ignore_case(index, false); - reset_index_ignore_case = true; - } + index_ignore_case = index->ignore_case; DIFF_FROM_ITERATORS( - git_iterator_for_tree(&a, old_tree, 0, pfx, pfx), - git_iterator_for_index(&b, index, 0, pfx, pfx) + git_iterator_for_tree( + &a, old_tree, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx), + git_iterator_for_index( + &b, index, GIT_ITERATOR_DONT_IGNORE_CASE, pfx, pfx) ); - if (reset_index_ignore_case) { - git_index__set_ignore_case(index, true); - - if (!error) { - git_diff *d = *diff; - - d->opts.flags |= GIT_DIFF_IGNORE_CASE; - d->strcomp = git__strcasecmp; - d->strncomp = git__strncasecmp; - d->pfxcomp = git__prefixcmp_icase; - d->entrycomp = git_index_entry__cmp_icase; - - git_vector_set_cmp(&d->deltas, git_diff_delta__casecmp); - git_vector_sort(&d->deltas); - } - } + /* if index is in case-insensitive order, re-sort deltas to match */ + if (!error && index_ignore_case) + diff_set_ignore_case(*diff, true); return error; } @@ -1258,9 +1214,12 @@ DIFF_FROM_ITERATORS( git_iterator_for_index(&a, index, 0, pfx, pfx), git_iterator_for_workdir( - &b, repo, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx) + &b, repo, index, NULL, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx) ); + if (!error && DIFF_FLAG_IS_SET(*diff, GIT_DIFF_UPDATE_INDEX)) + error = git_index_write(index); + return error; } @@ -1271,13 +1230,17 @@ const git_diff_options *opts) { int error = 0; + git_index *index; assert(diff && repo); + if ((error = git_repository_index__weakptr(&index, repo))) + return error; + DIFF_FROM_ITERATORS( git_iterator_for_tree(&a, old_tree, 0, pfx, pfx), git_iterator_for_workdir( - &b, repo, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx) + &b, repo, index, old_tree, GIT_ITERATOR_DONT_AUTOEXPAND, pfx, pfx) ); return error; @@ -1313,20 +1276,6 @@ return error; } -int git_diff_options_init(git_diff_options *options, unsigned int version) -{ - git_diff_options template = GIT_DIFF_OPTIONS_INIT; - - if (version != template.version) { - giterr_set(GITERR_INVALID, - "Invalid version %d for git_diff_options", (int)version); - return -1; - } - - memcpy(options, &template, sizeof(*options)); - return 0; -} - size_t git_diff_num_deltas(const git_diff *diff) { assert(diff); @@ -1358,13 +1307,22 @@ return (diff->opts.flags & GIT_DIFF_IGNORE_CASE) != 0; } +int git_diff_get_perfdata(git_diff_perfdata *out, const git_diff *diff) +{ + assert(out); + GITERR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata"); + out->stat_calls = diff->perf.stat_calls; + out->oid_calculations = diff->perf.oid_calculations; + return 0; +} + int git_diff__paired_foreach( git_diff *head2idx, git_diff *idx2wd, int (*cb)(git_diff_delta *h2i, git_diff_delta *i2w, void *payload), void *payload) { - int cmp; + int cmp, error = 0; git_diff_delta *h2i, *i2w; size_t i, j, i_max, j_max; int (*strcomp)(const char *, const char *) = git__strcmp; @@ -1420,18 +1378,17 @@ strcomp(h2i->new_file.path, i2w->old_file.path); if (cmp < 0) { - if (cb(h2i, NULL, payload)) - return GIT_EUSER; - i++; + i++; i2w = NULL; } else if (cmp > 0) { - if (cb(NULL, i2w, payload)) - return GIT_EUSER; - j++; + j++; h2i = NULL; } else { - if (cb(h2i, i2w, payload)) - return GIT_EUSER; i++; j++; } + + if ((error = cb(h2i, i2w, payload)) != 0) { + giterr_set_after_callback(error); + break; + } } /* restore case-insensitive delta sort */ @@ -1447,5 +1404,241 @@ git_vector_sort(&idx2wd->deltas); } + return error; +} + +int git_diff__commit( + git_diff **diff, + git_repository *repo, + const git_commit *commit, + const git_diff_options *opts) +{ + git_commit *parent = NULL; + git_diff *commit_diff = NULL; + git_tree *old_tree = NULL, *new_tree = NULL; + size_t parents; + int error = 0; + + if ((parents = git_commit_parentcount(commit)) > 1) { + char commit_oidstr[GIT_OID_HEXSZ + 1]; + + error = -1; + giterr_set(GITERR_INVALID, "Commit %s is a merge commit", + git_oid_tostr(commit_oidstr, GIT_OID_HEXSZ + 1, git_commit_id(commit))); + goto on_error; + } + + if (parents > 0) + if ((error = git_commit_parent(&parent, commit, 0)) < 0 || + (error = git_commit_tree(&old_tree, parent)) < 0) + goto on_error; + + if ((error = git_commit_tree(&new_tree, commit)) < 0 || + (error = git_diff_tree_to_tree(&commit_diff, repo, old_tree, new_tree, opts)) < 0) + goto on_error; + + *diff = commit_diff; + +on_error: + git_tree_free(new_tree); + git_tree_free(old_tree); + git_commit_free(parent); + + return error; +} + +int git_diff_format_email__append_header_tobuf( + git_buf *out, + const git_oid *id, + const git_signature *author, + const char *summary, + size_t patch_no, + size_t total_patches, + bool exclude_patchno_marker) +{ + char idstr[GIT_OID_HEXSZ + 1]; + char date_str[GIT_DATE_RFC2822_SZ]; + int error = 0; + + git_oid_fmt(idstr, id); + idstr[GIT_OID_HEXSZ] = '\0'; + + if ((error = git__date_rfc2822_fmt(date_str, sizeof(date_str), &author->when)) < 0) + return error; + + error = git_buf_printf(out, + "From %s Mon Sep 17 00:00:00 2001\n" \ + "From: %s <%s>\n" \ + "Date: %s\n" \ + "Subject: ", + idstr, + author->name, author->email, + date_str); + + if (error < 0) + return error; + + if (!exclude_patchno_marker) { + if (total_patches == 1) { + error = git_buf_puts(out, "[PATCH] "); + } else { + error = git_buf_printf(out, "[PATCH %"PRIuZ"/%"PRIuZ"] ", patch_no, total_patches); + } + + if (error < 0) + return error; + } + + error = git_buf_printf(out, "%s\n\n", summary); + + return error; +} + +int git_diff_format_email__append_patches_tobuf( + git_buf *out, + git_diff *diff) +{ + size_t i, deltas; + int error = 0; + + deltas = git_diff_num_deltas(diff); + + for (i = 0; i < deltas; ++i) { + git_patch *patch = NULL; + + if ((error = git_patch_from_diff(&patch, diff, i)) >= 0) + error = git_patch_to_buf(out, patch); + + git_patch_free(patch); + + if (error < 0) + break; + } + + return error; +} + +int git_diff_format_email( + git_buf *out, + git_diff *diff, + const git_diff_format_email_options *opts) +{ + git_diff_stats *stats = NULL; + char *summary = NULL, *loc = NULL; + bool ignore_marker; + unsigned int format_flags = 0; + int error; + + assert(out && diff && opts); + assert(opts->summary && opts->id && opts->author); + + GITERR_CHECK_VERSION(opts, GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, "git_format_email_options"); + + if ((ignore_marker = opts->flags & GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) == false) { + if (opts->patch_no > opts->total_patches) { + giterr_set(GITERR_INVALID, "patch %"PRIuZ" out of range. max %"PRIuZ, opts->patch_no, opts->total_patches); + return -1; + } + + if (opts->patch_no == 0) { + giterr_set(GITERR_INVALID, "invalid patch no %"PRIuZ". should be >0", opts->patch_no); + return -1; + } + } + + /* the summary we receive may not be clean. + * it could potentially contain new line characters + * or not be set, sanitize, */ + if ((loc = strpbrk(opts->summary, "\r\n")) != NULL) { + size_t offset = 0; + + if ((offset = (loc - opts->summary)) == 0) { + giterr_set(GITERR_INVALID, "summary is empty"); + error = -1; + goto on_error; + } + + summary = git__calloc(offset + 1, sizeof(char)); + GITERR_CHECK_ALLOC(summary); + strncpy(summary, opts->summary, offset); + } + + error = git_diff_format_email__append_header_tobuf(out, + opts->id, opts->author, summary == NULL ? opts->summary : summary, + opts->patch_no, opts->total_patches, ignore_marker); + + if (error < 0) + goto on_error; + + format_flags = GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY; + + if ((error = git_buf_puts(out, "---\n")) < 0 || + (error = git_diff_get_stats(&stats, diff)) < 0 || + (error = git_diff_stats_to_buf(out, stats, format_flags, 0)) < 0 || + (error = git_buf_putc(out, '\n')) < 0 || + (error = git_diff_format_email__append_patches_tobuf(out, diff)) < 0) + goto on_error; + + error = git_buf_puts(out, "--\nlibgit2 " LIBGIT2_VERSION "\n\n"); + +on_error: + git__free(summary); + git_diff_stats_free(stats); + + return error; +} + +int git_diff_commit_as_email( + git_buf *out, + git_repository *repo, + git_commit *commit, + size_t patch_no, + size_t total_patches, + git_diff_format_email_flags_t flags, + const git_diff_options *diff_opts) +{ + git_diff *diff = NULL; + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + int error; + + assert (out && repo && commit); + + opts.flags = flags; + opts.patch_no = patch_no; + opts.total_patches = total_patches; + opts.id = git_commit_id(commit); + opts.summary = git_commit_summary(commit); + opts.author = git_commit_author(commit); + + if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0) + return error; + + error = git_diff_format_email(out, diff, &opts); + + git_diff_free(diff); + return error; +} + +int git_diff_init_options(git_diff_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_diff_options, GIT_DIFF_OPTIONS_INIT); + return 0; +} + +int git_diff_find_init_options( + git_diff_find_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_diff_find_options, GIT_DIFF_FIND_OPTIONS_INIT); + return 0; +} + +int git_diff_format_email_init_options( + git_diff_format_email_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_diff_format_email_options, + GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT); return 0; } diff -Nru libgit2-0.20.0/src/diff_driver.c libgit2-0.22.2/src/diff_driver.c --- libgit2-0.20.0/src/diff_driver.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/diff_driver.c 2015-03-24 16:10:45.000000000 +0000 @@ -14,6 +14,7 @@ #include "strmap.h" #include "map.h" #include "buf_text.h" +#include "config.h" #include "repository.h" GIT__USE_STRMAP; @@ -25,10 +26,13 @@ DIFF_DRIVER_PATTERNLIST = 3, } git_diff_driver_t; +typedef struct { + regex_t re; + int flags; +} git_diff_driver_pattern; + enum { - DIFF_CONTEXT_FIND_NORMAL = 0, - DIFF_CONTEXT_FIND_ICASE = (1 << 0), - DIFF_CONTEXT_FIND_EXT = (1 << 1), + REG_NEGATE = (1 << 15) /* get out of the way of existing flags */ }; /* data for finding function context for a given file type */ @@ -36,11 +40,13 @@ git_diff_driver_t type; uint32_t binary_flags; uint32_t other_flags; - git_array_t(regex_t) fn_patterns; + git_array_t(git_diff_driver_pattern) fn_patterns; regex_t word_pattern; char name[GIT_FLEX_ARRAY]; }; +#include "userdiff.h" + struct git_diff_driver_registry { git_strmap *drivers; }; @@ -60,7 +66,7 @@ if (!reg) return NULL; - if ((reg->drivers = git_strmap_alloc()) == NULL) { + if (git_strmap_alloc(®->drivers) < 0) { git_diff_driver_registry_free(reg); return NULL; } @@ -80,34 +86,59 @@ git__free(reg); } -static int diff_driver_add_funcname( - git_diff_driver *drv, const char *name, int regex_flags) +static int diff_driver_add_patterns( + git_diff_driver *drv, const char *regex_str, int regex_flags) { - int error; - regex_t re, *re_ptr; + int error = 0; + const char *scan, *end; + git_diff_driver_pattern *pat = NULL; + git_buf buf = GIT_BUF_INIT; + + for (scan = regex_str; scan; scan = end) { + /* get pattern to fill in */ + if ((pat = git_array_alloc(drv->fn_patterns)) == NULL) { + error = -1; + break; + } + + pat->flags = regex_flags; + if (*scan == '!') { + pat->flags |= REG_NEGATE; + ++scan; + } + + if ((end = strchr(scan, '\n')) != NULL) { + error = git_buf_set(&buf, scan, end - scan); + end++; + } else { + error = git_buf_sets(&buf, scan); + } + if (error < 0) + break; - if ((error = regcomp(&re, name, regex_flags)) != 0) { - /* TODO: warning about bad regex instead of failure */ - error = giterr_set_regex(&re, error); - regfree(&re); - return error; + if ((error = regcomp(&pat->re, buf.ptr, regex_flags)) < 0) { + /* if regex fails to compile, warn? fail? */ + error = giterr_set_regex(&pat->re, error); + regfree(&pat->re); + break; + } } - re_ptr = git_array_alloc(drv->fn_patterns); - GITERR_CHECK_ALLOC(re_ptr); + if (error && pat != NULL) + (void)git_array_pop(drv->fn_patterns); /* release last item */ + git_buf_free(&buf); - memcpy(re_ptr, &re, sizeof(re)); - return 0; + return error; } static int diff_driver_xfuncname(const git_config_entry *entry, void *payload) { - return diff_driver_add_funcname(payload, entry->value, REG_EXTENDED); + return diff_driver_add_patterns(payload, entry->value, REG_EXTENDED); } static int diff_driver_funcname(const git_config_entry *entry, void *payload) { - return diff_driver_add_funcname(payload, entry->value, 0); + return diff_driver_add_patterns(payload, entry->value, 0); } static git_diff_driver_registry *git_repository_driver_registry( @@ -127,34 +158,79 @@ return repo->diff_drivers; } +static int git_diff_driver_builtin( + git_diff_driver **out, + git_diff_driver_registry *reg, + const char *driver_name) +{ + int error = 0; + git_diff_driver_definition *ddef = NULL; + git_diff_driver *drv = NULL; + size_t namelen, idx; + + for (idx = 0; idx < ARRAY_SIZE(builtin_defs); ++idx) { + if (!strcasecmp(driver_name, builtin_defs[idx].name)) { + ddef = &builtin_defs[idx]; + break; + } + } + if (!ddef) + goto done; + + namelen = strlen(ddef->name); + + drv = git__calloc(1, sizeof(git_diff_driver) + namelen + 1); + GITERR_CHECK_ALLOC(drv); + + drv->type = DIFF_DRIVER_PATTERNLIST; + memcpy(drv->name, ddef->name, namelen); + + if (ddef->fns && + (error = diff_driver_add_patterns( + drv, ddef->fns, ddef->flags | REG_EXTENDED)) < 0) + goto done; + + if (ddef->words && + (error = regcomp( + &drv->word_pattern, ddef->words, ddef->flags | REG_EXTENDED))) + { + error = giterr_set_regex(&drv->word_pattern, error); + goto done; + } + + git_strmap_insert(reg->drivers, drv->name, drv, error); + if (error > 0) + error = 0; + +done: + if (error && drv) + git_diff_driver_free(drv); + else + *out = drv; + + return error; +} + static int git_diff_driver_load( git_diff_driver **out, git_repository *repo, const char *driver_name) { - int error = 0, bval; + int error = 0; git_diff_driver_registry *reg; - git_diff_driver *drv; + git_diff_driver *drv = NULL; size_t namelen = strlen(driver_name); khiter_t pos; git_config *cfg; git_buf name = GIT_BUF_INIT; - const char *val; + const git_config_entry *ce; bool found_driver = false; - reg = git_repository_driver_registry(repo); - if (!reg) + if ((reg = git_repository_driver_registry(repo)) == NULL) return -1; - else { - pos = git_strmap_lookup_index(reg->drivers, driver_name); - if (git_strmap_valid_index(reg->drivers, pos)) { - *out = git_strmap_value_at(reg->drivers, pos); - return 0; - } - } - /* if you can't read config for repo, just use default driver */ - if (git_repository_config__weakptr(&cfg, repo) < 0) { - giterr_clear(); - return GIT_ENOTFOUND; + pos = git_strmap_lookup_index(reg->drivers, driver_name); + if (git_strmap_valid_index(reg->drivers, pos)) { + *out = git_strmap_value_at(reg->drivers, pos); + return 0; } drv = git__calloc(1, sizeof(git_diff_driver) + namelen + 1); @@ -162,25 +238,29 @@ drv->type = DIFF_DRIVER_AUTO; memcpy(drv->name, driver_name, namelen); + /* if you can't read config for repo, just use default driver */ + if (git_repository_config_snapshot(&cfg, repo) < 0) { + giterr_clear(); + goto done; + } + if ((error = git_buf_printf(&name, "diff.%s.binary", driver_name)) < 0) goto done; - if ((error = git_config_get_string(&val, cfg, name.ptr)) < 0) { - if (error != GIT_ENOTFOUND) - goto done; - /* diff..binary unspecified, so just continue */ - giterr_clear(); - } else if (git_config_parse_bool(&bval, val) < 0) { - /* TODO: warn that diff..binary has invalid value */ - giterr_clear(); - } else if (bval) { + + switch (git_config__get_bool_force(cfg, name.ptr, -1)) { + case true: /* if diff..binary is true, just return the binary driver */ *out = &global_drivers[DIFF_DRIVER_BINARY]; goto done; - } else { + case false: /* if diff..binary is false, force binary checks off */ /* but still may have custom function context patterns, etc. */ drv->binary_flags = GIT_DIFF_FORCE_TEXT; found_driver = true; + break; + default: + /* diff..binary unspecified or "auto", so just continue */ + break; } /* TODO: warn if diff..command or diff..textconv are set */ @@ -211,16 +291,16 @@ git_buf_truncate(&name, namelen + strlen("diff..")); git_buf_put(&name, "wordregex", strlen("wordregex")); - if ((error = git_config_get_string(&val, cfg, name.ptr)) < 0) { - if (error != GIT_ENOTFOUND) - goto done; - giterr_clear(); /* no diff..wordregex, so just continue */ - } else if ((error = regcomp(&drv->word_pattern, val, REG_EXTENDED)) != 0) { - /* TODO: warning about bad regex instead of failure */ - error = giterr_set_regex(&drv->word_pattern, error); + if ((error = git_config__lookup_entry(&ce, cfg, name.ptr, false)) < 0) goto done; - } else { + if (!ce || !ce->value) + /* no diff..wordregex, so just continue */; + else if (!(error = regcomp(&drv->word_pattern, ce->value, REG_EXTENDED))) found_driver = true; + else { + /* TODO: warn about bad regex instead of failure */ + error = giterr_set_regex(&drv->word_pattern, error); + goto done; } /* TODO: look up diff..algorithm to turn on minimal / patience @@ -235,14 +315,19 @@ git_strmap_insert(reg->drivers, drv->name, drv, error); if (error < 0) goto done; + error = 0; *out = drv; done: git_buf_free(&name); + git_config_free(cfg); - if (!*out) - *out = &global_drivers[DIFF_DRIVER_AUTO]; + if (!*out) { + int error2 = git_diff_driver_builtin(out, reg, driver_name); + if (!error) + error = error2; + } if (drv && drv != *out) git_diff_driver_free(drv); @@ -257,14 +342,13 @@ const char *value; assert(out); + *out = NULL; if (!repo || !path || !strlen(path)) - goto use_auto; - - if ((error = git_attr_get(&value, repo, 0, path, "diff")) < 0) - return error; - - if (GIT_ATTR_UNSPECIFIED(value)) + /* just use the auto value */; + else if ((error = git_attr_get(&value, repo, 0, path, "diff")) < 0) + /* return error below */; + else if (GIT_ATTR_UNSPECIFIED(value)) /* just use the auto value */; else if (GIT_ATTR_FALSE(value)) *out = &global_drivers[DIFF_DRIVER_BINARY]; @@ -273,17 +357,16 @@ /* otherwise look for driver information in config and build driver */ else if ((error = git_diff_driver_load(out, repo, value)) < 0) { - if (error != GIT_ENOTFOUND) - return error; - else + if (error == GIT_ENOTFOUND) { + error = 0; giterr_clear(); + } } -use_auto: if (!*out) *out = &global_drivers[DIFF_DRIVER_AUTO]; - return 0; + return error; } void git_diff_driver_free(git_diff_driver *driver) @@ -294,7 +377,7 @@ return; for (i = 0; i < git_array_size(driver->fn_patterns); ++i) - regfree(git_array_get(driver->fn_patterns, i)); + regfree(& git_array_get(driver->fn_patterns, i)->re); git_array_clear(driver->fn_patterns); regfree(&driver->word_pattern); @@ -314,7 +397,11 @@ int git_diff_driver_content_is_binary( git_diff_driver *driver, const char *content, size_t content_len) { - const git_buf search = { (char *)content, 0, min(content_len, 4000) }; + git_buf search; + + search.ptr = (char *)content; + search.size = min(content_len, GIT_FILTER_BYTES_TO_CHECK_NUL); + search.asize = 0; GIT_UNUSED(driver); @@ -331,23 +418,34 @@ } static int diff_context_line__simple( - git_diff_driver *driver, const char *line, size_t line_len) + git_diff_driver *driver, git_buf *line) { + char firstch = line->ptr[0]; GIT_UNUSED(driver); - GIT_UNUSED(line_len); - return (git__isalpha(*line) || *line == '_' || *line == '$'); + return (git__isalpha(firstch) || firstch == '_' || firstch == '$'); } static int diff_context_line__pattern_match( - git_diff_driver *driver, const char *line, size_t line_len) + git_diff_driver *driver, git_buf *line) { - size_t i; + size_t i, maxi = git_array_size(driver->fn_patterns); + regmatch_t pmatch[2]; - GIT_UNUSED(line_len); + for (i = 0; i < maxi; ++i) { + git_diff_driver_pattern *pat = git_array_get(driver->fn_patterns, i); + + if (!regexec(&pat->re, line->ptr, 2, pmatch, 0)) { + if (pat->flags & REG_NEGATE) + return false; + + /* use pmatch data to trim line data */ + i = (pmatch[1].rm_so >= 0) ? 1 : 0; + git_buf_consume(line, git_buf_cstr(line) + pmatch[i].rm_so); + git_buf_truncate(line, pmatch[i].rm_eo - pmatch[i].rm_so); + git_buf_rtrim(line); - for (i = 0; i < git_array_size(driver->fn_patterns); ++i) { - if (!regexec(git_array_get(driver->fn_patterns, i), line, 0, NULL, 0)) return true; + } } return false; @@ -369,8 +467,7 @@ if (!ctxt->line.size) return -1; - if (!ctxt->match_line || - !ctxt->match_line(ctxt->driver, ctxt->line.ptr, ctxt->line.size)) + if (!ctxt->match_line || !ctxt->match_line(ctxt->driver, &ctxt->line)) return -1; if (out_size > (long)ctxt->line.size) diff -Nru libgit2-0.20.0/src/diff_driver.h libgit2-0.22.2/src/diff_driver.h --- libgit2-0.20.0/src/diff_driver.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/diff_driver.h 2015-03-24 16:10:45.000000000 +0000 @@ -31,7 +31,7 @@ const char *, long, char *, long, void *); typedef int (*git_diff_find_context_line)( - git_diff_driver *, const char *, size_t); + git_diff_driver *, git_buf *); typedef struct { git_diff_driver *driver; diff -Nru libgit2-0.20.0/src/diff_file.c libgit2-0.22.2/src/diff_file.c --- libgit2-0.20.0/src/diff_file.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/diff_file.c 2015-03-24 16:10:45.000000000 +0000 @@ -112,6 +112,7 @@ has_data = !use_old && (diff->opts.flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) != 0; break; + case GIT_DELTA_UNREADABLE: case GIT_DELTA_MODIFIED: case GIT_DELTA_COPIED: case GIT_DELTA_RENAMED: @@ -127,57 +128,38 @@ return diff_file_content_init_common(fc, &diff->opts); } -int git_diff_file_content__init_from_blob( +int git_diff_file_content__init_from_src( git_diff_file_content *fc, git_repository *repo, const git_diff_options *opts, - const git_blob *blob, + const git_diff_file_content_src *src, git_diff_file *as_file) { memset(fc, 0, sizeof(*fc)); fc->repo = repo; fc->file = as_file; - fc->blob = blob; + fc->blob = src->blob; - if (!blob) { + if (!src->blob && !src->buf) { fc->flags |= GIT_DIFF_FLAG__NO_DATA; } else { fc->flags |= GIT_DIFF_FLAG__LOADED; - fc->file->flags |= GIT_DIFF_FLAG_VALID_OID; - fc->file->size = git_blob_rawsize(blob); + fc->file->flags |= GIT_DIFF_FLAG_VALID_ID; fc->file->mode = GIT_FILEMODE_BLOB; - git_oid_cpy(&fc->file->oid, git_blob_id(blob)); - fc->map.len = (size_t)fc->file->size; - fc->map.data = (char *)git_blob_rawcontent(blob); - } - - return diff_file_content_init_common(fc, opts); -} + if (src->blob) { + fc->file->size = git_blob_rawsize(src->blob); + git_oid_cpy(&fc->file->id, git_blob_id(src->blob)); + + fc->map.len = (size_t)fc->file->size; + fc->map.data = (char *)git_blob_rawcontent(src->blob); + } else { + fc->file->size = src->buflen; + git_odb_hash(&fc->file->id, src->buf, src->buflen, GIT_OBJ_BLOB); -int git_diff_file_content__init_from_raw( - git_diff_file_content *fc, - git_repository *repo, - const git_diff_options *opts, - const char *buf, - size_t buflen, - git_diff_file *as_file) -{ - memset(fc, 0, sizeof(*fc)); - fc->repo = repo; - fc->file = as_file; - - if (!buf) { - fc->flags |= GIT_DIFF_FLAG__NO_DATA; - } else { - fc->flags |= GIT_DIFF_FLAG__LOADED; - fc->file->flags |= GIT_DIFF_FLAG_VALID_OID; - fc->file->size = buflen; - fc->file->mode = GIT_FILEMODE_BLOB; - git_odb_hash(&fc->file->oid, buf, buflen, GIT_OBJ_BLOB); - - fc->map.len = buflen; - fc->map.data = (char *)buf; + fc->map.len = src->buflen; + fc->map.data = (char *)src->buf; + } } return diff_file_content_init_common(fc, opts); @@ -196,28 +178,36 @@ unsigned int sm_status = 0; const git_oid *sm_head; - if ((error = git_submodule_lookup(&sm, fc->repo, fc->file->path)) < 0 || - (error = git_submodule_status(&sm_status, sm)) < 0) { + if ((error = git_submodule_lookup(&sm, fc->repo, fc->file->path)) < 0) { /* GIT_EEXISTS means a "submodule" that has not been git added */ - if (error == GIT_EEXISTS) + if (error == GIT_EEXISTS) { + giterr_clear(); error = 0; + } + return error; + } + + if ((error = git_submodule_status(&sm_status, sm)) < 0) { + git_submodule_free(sm); return error; } /* update OID if we didn't have it previously */ - if ((fc->file->flags & GIT_DIFF_FLAG_VALID_OID) == 0 && + if ((fc->file->flags & GIT_DIFF_FLAG_VALID_ID) == 0 && ((sm_head = git_submodule_wd_id(sm)) != NULL || (sm_head = git_submodule_head_id(sm)) != NULL)) { - git_oid_cpy(&fc->file->oid, sm_head); - fc->file->flags |= GIT_DIFF_FLAG_VALID_OID; + git_oid_cpy(&fc->file->id, sm_head); + fc->file->flags |= GIT_DIFF_FLAG_VALID_ID; } if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) status = "-dirty"; + + git_submodule_free(sm); } - git_oid_tostr(oid, sizeof(oid), &fc->file->oid); + git_oid_tostr(oid, sizeof(oid), &fc->file->id); if (git_buf_printf(&content, "Subproject commit %s%s\n", oid, status) < 0) return -1; @@ -233,7 +223,7 @@ int error = 0; git_odb_object *odb_obj = NULL; - if (git_oid_iszero(&fc->file->oid)) + if (git_oid_iszero(&fc->file->id)) return 0; if (fc->file->mode == GIT_FILEMODE_COMMIT) @@ -255,7 +245,7 @@ git_odb_object_free(odb_obj); } else { error = git_blob_lookup( - (git_blob **)&fc->blob, fc->repo, &fc->file->oid); + (git_blob **)&fc->blob, fc->repo, &fc->file->id); } if (!error) { @@ -311,7 +301,8 @@ goto cleanup; if ((error = git_filter_list_load( - &fl, fc->repo, NULL, fc->file->path, GIT_FILTER_TO_ODB)) < 0) + &fl, fc->repo, NULL, fc->file->path, + GIT_FILTER_TO_ODB, GIT_FILTER_OPT_ALLOW_UNSAFE)) < 0) goto cleanup; /* if there are no filters, try to mmap the file */ @@ -331,7 +322,8 @@ error = git_filter_list_apply_to_data(&out, fl, &raw); - git_buf_free(&raw); + if (out.ptr != raw.ptr) + git_buf_free(&raw); if (!error) { fc->map.len = out.size; @@ -368,10 +360,10 @@ error = diff_file_content_load_workdir_file(fc, &path); /* once data is loaded, update OID if we didn't have it previously */ - if (!error && (fc->file->flags & GIT_DIFF_FLAG_VALID_OID) == 0) { + if (!error && (fc->file->flags & GIT_DIFF_FLAG_VALID_ID) == 0) { error = git_odb_hash( - &fc->file->oid, fc->map.data, fc->map.len, GIT_OBJ_BLOB); - fc->file->flags |= GIT_DIFF_FLAG_VALID_OID; + &fc->file->id, fc->map.data, fc->map.len, GIT_OBJ_BLOB); + fc->file->flags |= GIT_DIFF_FLAG_VALID_ID; } git_buf_free(&path); diff -Nru libgit2-0.20.0/src/diff_file.h libgit2-0.22.2/src/diff_file.h --- libgit2-0.20.0/src/diff_file.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/diff_file.h 2015-03-24 16:10:45.000000000 +0000 @@ -31,19 +31,21 @@ size_t delta_index, bool use_old); -extern int git_diff_file_content__init_from_blob( - git_diff_file_content *fc, - git_repository *repo, - const git_diff_options *opts, - const git_blob *blob, - git_diff_file *as_file); +typedef struct { + const git_blob *blob; + const void *buf; + size_t buflen; + const char *as_path; +} git_diff_file_content_src; + +#define GIT_DIFF_FILE_CONTENT_SRC__BLOB(BLOB,PATH) { (BLOB),NULL,0,(PATH) } +#define GIT_DIFF_FILE_CONTENT_SRC__BUF(BUF,LEN,PATH) { NULL,(BUF),(LEN),(PATH) } -extern int git_diff_file_content__init_from_raw( +extern int git_diff_file_content__init_from_src( git_diff_file_content *fc, git_repository *repo, const git_diff_options *opts, - const char *buf, - size_t buflen, + const git_diff_file_content_src *src, git_diff_file *as_file); /* this loads the blob/file-on-disk as needed */ diff -Nru libgit2-0.20.0/src/diff.h libgit2-0.22.2/src/diff.h --- libgit2-0.20.0/src/diff.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/diff.h 2015-03-24 16:10:45.000000000 +0000 @@ -8,6 +8,7 @@ #define INCLUDE_diff_h__ #include "git2/diff.h" +#include "git2/sys/diff.h" #include "git2/oid.h" #include @@ -62,6 +63,7 @@ git_iterator_type_t old_src; git_iterator_type_t new_src; uint32_t diffcaps; + git_diff_perfdata perf; int (*strcomp)(const char *, const char *); int (*strncomp)(const char *, const char *, size_t); @@ -90,7 +92,9 @@ int oid_strlen); extern int git_diff__oid_for_file( - git_repository *, const char *, uint16_t, git_off_t, git_oid *); + git_oid *out, git_diff *, const char *, uint16_t, git_off_t); +extern int git_diff__oid_for_entry( + git_oid *out, git_diff *, const git_index_entry *, const git_oid *update); extern int git_diff__from_iterators( git_diff **diff_ptr, @@ -116,6 +120,9 @@ extern int git_diff_find_similar__calc_similarity( int *score, void *siga, void *sigb, void *payload); +extern int git_diff__commit( + git_diff **diff, git_repository *repo, const git_commit *commit, const git_diff_options *opts); + /* * Sometimes a git_diff_file will have a zero size; this attempts to * fill in the size without loading the blob if possible. If that is @@ -134,7 +141,7 @@ return error; error = git_odb__read_header_or_object( - odb_obj, &len, &type, odb, &file->oid); + odb_obj, &len, &type, odb, &file->id); git_odb_free(odb); diff -Nru libgit2-0.20.0/src/diff_patch.c libgit2-0.22.2/src/diff_patch.c --- libgit2-0.20.0/src/diff_patch.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/diff_patch.c 2015-03-24 16:10:45.000000000 +0000 @@ -5,6 +5,7 @@ * a Linking Exception. For full terms see the included COPYING file. */ #include "common.h" +#include "git2/blob.h" #include "diff.h" #include "diff_file.h" #include "diff_driver.h" @@ -13,12 +14,11 @@ #include "fileops.h" /* cached information about a hunk in a diff */ -typedef struct diff_patch_hunk diff_patch_hunk; -struct diff_patch_hunk { +typedef struct diff_patch_hunk { git_diff_hunk hunk; size_t line_start; size_t line_count; -}; +} diff_patch_hunk; struct git_patch { git_refcount rc; @@ -133,9 +133,9 @@ incomplete_data = (((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 || - (patch->ofile.file->flags & GIT_DIFF_FLAG_VALID_OID) != 0) && + (patch->ofile.file->flags & GIT_DIFF_FLAG_VALID_ID) != 0) && ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 || - (patch->nfile.file->flags & GIT_DIFF_FLAG_VALID_OID) != 0)); + (patch->nfile.file->flags & GIT_DIFF_FLAG_VALID_ID) != 0)); /* always try to load workdir content first because filtering may * need 2x data size and this minimizes peak memory footprint @@ -169,7 +169,7 @@ if (incomplete_data && patch->ofile.file->mode == patch->nfile.file->mode && patch->ofile.file->mode != GIT_FILEMODE_COMMIT && - git_oid_equal(&patch->ofile.file->oid, &patch->nfile.file->oid) && + git_oid_equal(&patch->ofile.file->id, &patch->nfile.file->id) && patch->delta->status == GIT_DELTA_MODIFIED) /* not RENAMED/COPIED! */ patch->delta->status = GIT_DELTA_UNMODIFIED; @@ -184,7 +184,7 @@ patch->delta->status != GIT_DELTA_UNMODIFIED && (patch->ofile.map.len || patch->nfile.map.len) && (patch->ofile.map.len != patch->nfile.map.len || - !git_oid_equal(&patch->ofile.file->oid, &patch->nfile.file->oid))) + !git_oid_equal(&patch->ofile.file->id, &patch->nfile.file->id))) patch->flags |= GIT_DIFF_PATCH_DIFFABLE; patch->flags |= GIT_DIFF_PATCH_LOADED; @@ -193,21 +193,18 @@ return error; } -static int diff_patch_file_callback( +static int diff_patch_invoke_file_callback( git_patch *patch, git_diff_output *output) { - float progress; + float progress = patch->diff ? + ((float)patch->delta_index / patch->diff->deltas.length) : 1.0f; if (!output->file_cb) return 0; - progress = patch->diff ? - ((float)patch->delta_index / patch->diff->deltas.length) : 1.0f; - - if (output->file_cb(patch->delta, progress, output->payload) != 0) - output->error = GIT_EUSER; - - return output->error; + return giterr_set_after_callback_function( + output->file_cb(patch->delta, progress, output->payload), + "git_patch"); } static int diff_patch_generate(git_patch *patch, git_diff_output *output) @@ -229,7 +226,7 @@ return 0; if (output->diff_cb != NULL && - !(error = output->diff_cb(output, patch))) + (error = output->diff_cb(output, patch)) < 0) patch->flags |= GIT_DIFF_PATCH_DIFFED; return error; @@ -272,9 +269,11 @@ size_t idx; git_patch patch; - if (diff_required(diff, "git_diff_foreach") < 0) - return -1; + if ((error = diff_required(diff, "git_diff_foreach")) < 0) + return error; + memset(&xo, 0, sizeof(xo)); + memset(&patch, 0, sizeof(patch)); diff_output_init( &xo.output, &diff->opts, file_cb, hunk_cb, data_cb, payload); git_xdiff_init(&xo, &diff->opts); @@ -285,22 +284,18 @@ if (git_diff_delta__should_skip(&diff->opts, patch.delta)) continue; - if (!(error = diff_patch_init_from_diff(&patch, diff, idx))) { - - error = diff_patch_file_callback(&patch, &xo.output); + if ((error = diff_patch_init_from_diff(&patch, diff, idx)) < 0) + break; - if (!error) - error = diff_patch_generate(&patch, &xo.output); + if (!(error = diff_patch_invoke_file_callback(&patch, &xo.output))) + error = diff_patch_generate(&patch, &xo.output); - git_patch_free(&patch); - } + git_patch_free(&patch); - if (error < 0) + if (error) break; } - if (error == GIT_EUSER) - giterr_clear(); /* don't leave error message set invalidly */ return error; } @@ -321,7 +316,7 @@ (has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) : (has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED); - if (git_oid_equal(&patch->nfile.file->oid, &patch->ofile.file->oid)) + if (git_oid_equal(&patch->nfile.file->id, &patch->ofile.file->id)) pd->delta.status = GIT_DELTA_UNMODIFIED; patch->delta = &pd->delta; @@ -332,49 +327,53 @@ !(patch->ofile.opts_flags & GIT_DIFF_INCLUDE_UNMODIFIED)) return error; - error = diff_patch_file_callback(patch, (git_diff_output *)xo); + error = diff_patch_invoke_file_callback(patch, (git_diff_output *)xo); if (!error) error = diff_patch_generate(patch, (git_diff_output *)xo); - if (error == GIT_EUSER) - giterr_clear(); /* don't leave error message set invalidly */ - return error; } -static int diff_patch_from_blobs( +static int diff_patch_from_sources( diff_patch_with_delta *pd, git_xdiff_output *xo, - const git_blob *old_blob, - const char *old_path, - const git_blob *new_blob, - const char *new_path, + git_diff_file_content_src *oldsrc, + git_diff_file_content_src *newsrc, const git_diff_options *opts) { int error = 0; git_repository *repo = - new_blob ? git_object_owner((const git_object *)new_blob) : - old_blob ? git_object_owner((const git_object *)old_blob) : NULL; + oldsrc->blob ? git_blob_owner(oldsrc->blob) : + newsrc->blob ? git_blob_owner(newsrc->blob) : NULL; + git_diff_file *lfile = &pd->delta.old_file, *rfile = &pd->delta.new_file; + git_diff_file_content *ldata = &pd->patch.ofile, *rdata = &pd->patch.nfile; GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) { - const git_blob *tmp_blob; - const char *tmp_path; - tmp_blob = old_blob; old_blob = new_blob; new_blob = tmp_blob; - tmp_path = old_path; old_path = new_path; new_path = tmp_path; + void *tmp = lfile; lfile = rfile; rfile = tmp; + tmp = ldata; ldata = rdata; rdata = tmp; } pd->patch.delta = &pd->delta; - pd->delta.old_file.path = old_path; - pd->delta.new_file.path = new_path; + if (!oldsrc->as_path) { + if (newsrc->as_path) + oldsrc->as_path = newsrc->as_path; + else + oldsrc->as_path = newsrc->as_path = "file"; + } + else if (!newsrc->as_path) + newsrc->as_path = oldsrc->as_path; - if ((error = git_diff_file_content__init_from_blob( - &pd->patch.ofile, repo, opts, old_blob, &pd->delta.old_file)) < 0 || - (error = git_diff_file_content__init_from_blob( - &pd->patch.nfile, repo, opts, new_blob, &pd->delta.new_file)) < 0) + lfile->path = oldsrc->as_path; + rfile->path = newsrc->as_path; + + if ((error = git_diff_file_content__init_from_src( + ldata, repo, opts, oldsrc, lfile)) < 0 || + (error = git_diff_file_content__init_from_src( + rdata, repo, opts, newsrc, rfile)) < 0) return error; return diff_single_generate(pd, xo); @@ -409,11 +408,9 @@ return 0; } -int git_diff_blobs( - const git_blob *old_blob, - const char *old_path, - const git_blob *new_blob, - const char *new_path, +static int diff_from_sources( + git_diff_file_content_src *oldsrc, + git_diff_file_content_src *newsrc, const git_diff_options *opts, git_diff_file_cb file_cb, git_diff_hunk_cb hunk_cb, @@ -424,32 +421,24 @@ diff_patch_with_delta pd; git_xdiff_output xo; - memset(&pd, 0, sizeof(pd)); memset(&xo, 0, sizeof(xo)); - diff_output_init( &xo.output, opts, file_cb, hunk_cb, data_cb, payload); git_xdiff_init(&xo, opts); - if (!old_path && new_path) - old_path = new_path; - else if (!new_path && old_path) - new_path = old_path; + memset(&pd, 0, sizeof(pd)); - error = diff_patch_from_blobs( - &pd, &xo, old_blob, old_path, new_blob, new_path, opts); + error = diff_patch_from_sources(&pd, &xo, oldsrc, newsrc, opts); git_patch_free(&pd.patch); return error; } -int git_patch_from_blobs( +static int patch_from_sources( git_patch **out, - const git_blob *old_blob, - const char *old_path, - const git_blob *new_blob, - const char *new_path, + git_diff_file_content_src *oldsrc, + git_diff_file_content_src *newsrc, const git_diff_options *opts) { int error = 0; @@ -459,18 +448,15 @@ assert(out); *out = NULL; - if (diff_patch_with_delta_alloc(&pd, &old_path, &new_path) < 0) - return -1; + if ((error = diff_patch_with_delta_alloc( + &pd, &oldsrc->as_path, &newsrc->as_path)) < 0) + return error; memset(&xo, 0, sizeof(xo)); - diff_output_to_patch(&xo.output, &pd->patch); git_xdiff_init(&xo, opts); - error = diff_patch_from_blobs( - pd, &xo, old_blob, old_path, new_blob, new_path, opts); - - if (!error) + if (!(error = diff_patch_from_sources(pd, &xo, oldsrc, newsrc, opts))) *out = (git_patch *)pd; else git_patch_free((git_patch *)pd); @@ -478,46 +464,38 @@ return error; } -static int diff_patch_from_blob_and_buffer( - diff_patch_with_delta *pd, - git_xdiff_output *xo, +int git_diff_blobs( const git_blob *old_blob, const char *old_path, - const char *buf, - size_t buflen, - const char *buf_path, - const git_diff_options *opts) + const git_blob *new_blob, + const char *new_path, + const git_diff_options *opts, + git_diff_file_cb file_cb, + git_diff_hunk_cb hunk_cb, + git_diff_line_cb data_cb, + void *payload) { - int error = 0; - git_repository *repo = - old_blob ? git_object_owner((const git_object *)old_blob) : NULL; - - GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); - - pd->patch.delta = &pd->delta; - - if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) { - pd->delta.old_file.path = buf_path; - pd->delta.new_file.path = old_path; - - if (!(error = git_diff_file_content__init_from_raw( - &pd->patch.ofile, repo, opts, buf, buflen, &pd->delta.old_file))) - error = git_diff_file_content__init_from_blob( - &pd->patch.nfile, repo, opts, old_blob, &pd->delta.new_file); - } else { - pd->delta.old_file.path = old_path; - pd->delta.new_file.path = buf_path; - - if (!(error = git_diff_file_content__init_from_blob( - &pd->patch.ofile, repo, opts, old_blob, &pd->delta.old_file))) - error = git_diff_file_content__init_from_raw( - &pd->patch.nfile, repo, opts, buf, buflen, &pd->delta.new_file); - } - - if (error < 0) - return error; + git_diff_file_content_src osrc = + GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path); + git_diff_file_content_src nsrc = + GIT_DIFF_FILE_CONTENT_SRC__BLOB(new_blob, new_path); + return diff_from_sources( + &osrc, &nsrc, opts, file_cb, hunk_cb, data_cb, payload); +} - return diff_single_generate(pd, xo); +int git_patch_from_blobs( + git_patch **out, + const git_blob *old_blob, + const char *old_path, + const git_blob *new_blob, + const char *new_path, + const git_diff_options *opts) +{ + git_diff_file_content_src osrc = + GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path); + git_diff_file_content_src nsrc = + GIT_DIFF_FILE_CONTENT_SRC__BLOB(new_blob, new_path); + return patch_from_sources(out, &osrc, &nsrc, opts); } int git_diff_blob_to_buffer( @@ -532,28 +510,12 @@ git_diff_line_cb data_cb, void *payload) { - int error = 0; - diff_patch_with_delta pd; - git_xdiff_output xo; - - memset(&pd, 0, sizeof(pd)); - memset(&xo, 0, sizeof(xo)); - - diff_output_init( - &xo.output, opts, file_cb, hunk_cb, data_cb, payload); - git_xdiff_init(&xo, opts); - - if (!old_path && buf_path) - old_path = buf_path; - else if (!buf_path && old_path) - buf_path = old_path; - - error = diff_patch_from_blob_and_buffer( - &pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts); - - git_patch_free(&pd.patch); - - return error; + git_diff_file_content_src osrc = + GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path); + git_diff_file_content_src nsrc = + GIT_DIFF_FILE_CONTENT_SRC__BUF(buf, buflen, buf_path); + return diff_from_sources( + &osrc, &nsrc, opts, file_cb, hunk_cb, data_cb, payload); } int git_patch_from_blob_and_buffer( @@ -565,30 +527,49 @@ const char *buf_path, const git_diff_options *opts) { - int error = 0; - diff_patch_with_delta *pd; - git_xdiff_output xo; - - assert(out); - *out = NULL; - - if (diff_patch_with_delta_alloc(&pd, &old_path, &buf_path) < 0) - return -1; - - memset(&xo, 0, sizeof(xo)); - - diff_output_to_patch(&xo.output, &pd->patch); - git_xdiff_init(&xo, opts); - - error = diff_patch_from_blob_and_buffer( - pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts); + git_diff_file_content_src osrc = + GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path); + git_diff_file_content_src nsrc = + GIT_DIFF_FILE_CONTENT_SRC__BUF(buf, buflen, buf_path); + return patch_from_sources(out, &osrc, &nsrc, opts); +} - if (!error) - *out = (git_patch *)pd; - else - git_patch_free((git_patch *)pd); +int git_diff_buffers( + const void *old_buf, + size_t old_len, + const char *old_path, + const void *new_buf, + size_t new_len, + const char *new_path, + const git_diff_options *opts, + git_diff_file_cb file_cb, + git_diff_hunk_cb hunk_cb, + git_diff_line_cb data_cb, + void *payload) +{ + git_diff_file_content_src osrc = + GIT_DIFF_FILE_CONTENT_SRC__BUF(old_buf, old_len, old_path); + git_diff_file_content_src nsrc = + GIT_DIFF_FILE_CONTENT_SRC__BUF(new_buf, new_len, new_path); + return diff_from_sources( + &osrc, &nsrc, opts, file_cb, hunk_cb, data_cb, payload); +} - return error; +int git_patch_from_buffers( + git_patch **out, + const void *old_buf, + size_t old_len, + const char *old_path, + const char *new_buf, + size_t new_len, + const char *new_path, + const git_diff_options *opts) +{ + git_diff_file_content_src osrc = + GIT_DIFF_FILE_CONTENT_SRC__BUF(old_buf, old_len, old_path); + git_diff_file_content_src nsrc = + GIT_DIFF_FILE_CONTENT_SRC__BUF(new_buf, new_len, new_path); + return patch_from_sources(out, &osrc, &nsrc, opts); } int git_patch_from_diff( @@ -622,17 +603,18 @@ if ((error = diff_patch_alloc_from_diff(&patch, diff, idx)) < 0) return error; + memset(&xo, 0, sizeof(xo)); diff_output_to_patch(&xo.output, patch); git_xdiff_init(&xo, &diff->opts); - error = diff_patch_file_callback(patch, &xo.output); + error = diff_patch_invoke_file_callback(patch, &xo.output); if (!error) error = diff_patch_generate(patch, &xo.output); if (!error) { - /* if cumulative diff size is < 0.5 total size, flatten the patch */ - /* unload the file content */ + /* TODO: if cumulative diff size is < 0.5 total size, flatten patch */ + /* TODO: and unload the file content */ } if (error || !patch_ptr) @@ -640,8 +622,6 @@ else *patch_ptr = patch; - if (error == GIT_EUSER) - giterr_clear(); /* don't leave error message set invalidly */ return error; } @@ -651,13 +631,13 @@ GIT_REFCOUNT_DEC(patch, diff_patch_free); } -const git_diff_delta *git_patch_get_delta(git_patch *patch) +const git_diff_delta *git_patch_get_delta(const git_patch *patch) { assert(patch); return patch->delta; } -size_t git_patch_num_hunks(git_patch *patch) +size_t git_patch_num_hunks(const git_patch *patch) { assert(patch); return git_array_size(patch->hunks); @@ -728,7 +708,7 @@ return 0; } -int git_patch_num_lines_in_hunk(git_patch *patch, size_t hunk_idx) +int git_patch_num_lines_in_hunk(const git_patch *patch, size_t hunk_idx) { diff_patch_hunk *hunk; assert(patch); @@ -842,7 +822,8 @@ for (i = 0; !error && i < git_array_size(patch->hunks); ++i) { diff_patch_hunk *h = git_array_get(patch->hunks, i); - error = hunk_cb(patch->delta, &h->hunk, payload); + if (hunk_cb) + error = hunk_cb(patch->delta, &h->hunk, payload); if (!line_cb) continue; @@ -905,7 +886,7 @@ GIT_UNUSED(hunk_); hunk = git_array_last(patch->hunks); - GITERR_CHECK_ALLOC(hunk); + assert(hunk); /* programmer error if no hunk is available */ line = git_array_alloc(patch->lines); GITERR_CHECK_ALLOC(line); diff -Nru libgit2-0.20.0/src/diff_print.c libgit2-0.22.2/src/diff_print.c --- libgit2-0.20.0/src/diff_print.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/diff_print.c 2015-03-24 16:10:45.000000000 +0000 @@ -8,6 +8,10 @@ #include "diff.h" #include "diff_patch.h" #include "fileops.h" +#include "zstream.h" +#include "blob.h" +#include "delta.h" +#include "git2/sys/diff.h" typedef struct { git_diff *diff; @@ -36,9 +40,11 @@ if (diff) pi->flags = diff->opts.flags; + else + pi->flags = 0; - if (diff && diff->opts.oid_abbrev != 0) - pi->oid_strlen = diff->opts.oid_abbrev; + if (diff && diff->opts.id_abbrev != 0) + pi->oid_strlen = diff->opts.id_abbrev; else if (!diff || !diff->repo) pi->oid_strlen = GIT_ABBREV_DEFAULT; else if (git_repository__cvar( @@ -76,25 +82,20 @@ char code; switch (status) { - case GIT_DELTA_ADDED: code = 'A'; break; - case GIT_DELTA_DELETED: code = 'D'; break; - case GIT_DELTA_MODIFIED: code = 'M'; break; - case GIT_DELTA_RENAMED: code = 'R'; break; - case GIT_DELTA_COPIED: code = 'C'; break; - case GIT_DELTA_IGNORED: code = 'I'; break; - case GIT_DELTA_UNTRACKED: code = '?'; break; - default: code = ' '; break; + case GIT_DELTA_ADDED: code = 'A'; break; + case GIT_DELTA_DELETED: code = 'D'; break; + case GIT_DELTA_MODIFIED: code = 'M'; break; + case GIT_DELTA_RENAMED: code = 'R'; break; + case GIT_DELTA_COPIED: code = 'C'; break; + case GIT_DELTA_IGNORED: code = 'I'; break; + case GIT_DELTA_UNTRACKED: code = '?'; break; + case GIT_DELTA_UNREADABLE: code = 'X'; break; + default: code = ' '; break; } return code; } -static int callback_error(void) -{ - giterr_clear(); - return GIT_EUSER; -} - static int diff_print_one_name_only( const git_diff_delta *delta, float progress, void *data) { @@ -108,19 +109,16 @@ return 0; git_buf_clear(out); - - if (git_buf_puts(out, delta->new_file.path) < 0 || - git_buf_putc(out, '\n')) + git_buf_puts(out, delta->new_file.path); + git_buf_putc(out, '\n'); + if (git_buf_oom(out)) return -1; pi->line.origin = GIT_DIFF_LINE_FILE_HDR; pi->line.content = git_buf_cstr(out); pi->line.content_len = git_buf_len(out); - if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return callback_error(); - - return 0; + return pi->print_cb(delta, NULL, &pi->line, pi->payload); } static int diff_print_one_name_status( @@ -154,7 +152,6 @@ git_buf_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix); else git_buf_printf(out, "%c\t%s\n", code, delta->old_file.path); - if (git_buf_oom(out)) return -1; @@ -162,10 +159,7 @@ pi->line.content = git_buf_cstr(out); pi->line.content_len = git_buf_len(out); - if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return callback_error(); - - return 0; + return pi->print_cb(delta, NULL, &pi->line, pi->payload); } static int diff_print_one_raw( @@ -183,11 +177,12 @@ git_buf_clear(out); - git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid); - git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid); + git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.id); + git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.id); git_buf_printf( - out, ":%06o %06o %s... %s... %c", + out, (pi->oid_strlen <= GIT_OID_HEXSZ) ? + ":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c", delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code); if (delta->similarity > 0) @@ -208,10 +203,7 @@ pi->line.content = git_buf_cstr(out); pi->line.content_len = git_buf_len(out); - if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return callback_error(); - - return 0; + return pi->print_cb(delta, NULL, &pi->line, pi->payload); } static int diff_print_oid_range( @@ -219,8 +211,8 @@ { char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1]; - git_oid_tostr(start_oid, oid_strlen, &delta->old_file.oid); - git_oid_tostr(end_oid, oid_strlen, &delta->new_file.oid); + git_oid_tostr(start_oid, oid_strlen, &delta->old_file.id); + git_oid_tostr(end_oid, oid_strlen, &delta->new_file.id); /* TODO: Match git diff more closely */ if (delta->old_file.mode == delta->new_file.mode) { @@ -238,10 +230,7 @@ git_buf_printf(out, "index %s..%s\n", start_oid, end_oid); } - if (git_buf_oom(out)) - return -1; - - return 0; + return git_buf_oom(out) ? -1 : 0; } static int diff_delta_format_with_paths( @@ -254,11 +243,11 @@ const char *oldpath = delta->old_file.path; const char *newpath = delta->new_file.path; - if (git_oid_iszero(&delta->old_file.oid)) { + if (git_oid_iszero(&delta->old_file.id)) { oldpfx = ""; oldpath = "/dev/null"; } - if (git_oid_iszero(&delta->new_file.oid)) { + if (git_oid_iszero(&delta->new_file.id)) { newpfx = ""; newpath = "/dev/null"; } @@ -285,8 +274,7 @@ git_buf_printf(out, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path); - if (diff_print_oid_range(out, delta, oid_strlen) < 0) - return -1; + GITERR_CHECK_ERROR(diff_print_oid_range(out, delta, oid_strlen)); if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) diff_delta_format_with_paths( @@ -295,54 +283,194 @@ return git_buf_oom(out) ? -1 : 0; } +static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new) +{ + git_buf deflate = GIT_BUF_INIT, delta = GIT_BUF_INIT, *out = NULL; + const void *old_data, *new_data; + git_off_t old_data_len, new_data_len; + unsigned long delta_data_len, inflated_len; + const char *out_type = "literal"; + char *scan, *end; + int error; + + old_data = old ? git_blob_rawcontent(old) : NULL; + new_data = new ? git_blob_rawcontent(new) : NULL; + + old_data_len = old ? git_blob_rawsize(old) : 0; + new_data_len = new ? git_blob_rawsize(new) : 0; + + /* The git_delta function accepts unsigned long only */ + if (!git__is_ulong(old_data_len) || !git__is_ulong(new_data_len)) + return GIT_EBUFS; + + out = &deflate; + inflated_len = (unsigned long)new_data_len; + + if ((error = git_zstream_deflatebuf( + out, new_data, (size_t)new_data_len)) < 0) + goto done; + + /* The git_delta function accepts unsigned long only */ + if (!git__is_ulong((git_off_t)deflate.size)) { + error = GIT_EBUFS; + goto done; + } + + if (old && new) { + void *delta_data = git_delta( + old_data, (unsigned long)old_data_len, + new_data, (unsigned long)new_data_len, + &delta_data_len, (unsigned long)deflate.size); + + if (delta_data) { + error = git_zstream_deflatebuf( + &delta, delta_data, (size_t)delta_data_len); + + git__free(delta_data); + + if (error < 0) + goto done; + + if (delta.size < deflate.size) { + out = δ + out_type = "delta"; + inflated_len = delta_data_len; + } + } + } + + git_buf_printf(pi->buf, "%s %lu\n", out_type, inflated_len); + pi->line.num_lines++; + + for (scan = out->ptr, end = out->ptr + out->size; scan < end; ) { + size_t chunk_len = end - scan; + if (chunk_len > 52) + chunk_len = 52; + + if (chunk_len <= 26) + git_buf_putc(pi->buf, (char)chunk_len + 'A' - 1); + else + git_buf_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1); + + git_buf_encode_base85(pi->buf, scan, chunk_len); + git_buf_putc(pi->buf, '\n'); + + if (git_buf_oom(pi->buf)) { + error = -1; + goto done; + } + + scan += chunk_len; + pi->line.num_lines++; + } + +done: + git_buf_free(&deflate); + git_buf_free(&delta); + + return error; +} + +/* git diff --binary 8d7523f~2 8d7523f~1 */ +static int diff_print_patch_file_binary( + diff_print_info *pi, const git_diff_delta *delta, + const char *oldpfx, const char *newpfx) +{ + git_blob *old = NULL, *new = NULL; + const git_oid *old_id, *new_id; + int error; + size_t pre_binary_size; + + if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0) + goto noshow; + + pre_binary_size = pi->buf->size; + git_buf_printf(pi->buf, "GIT binary patch\n"); + pi->line.num_lines++; + + old_id = (delta->status != GIT_DELTA_ADDED) ? &delta->old_file.id : NULL; + new_id = (delta->status != GIT_DELTA_DELETED) ? &delta->new_file.id : NULL; + + if (old_id && (error = git_blob_lookup(&old, pi->diff->repo, old_id)) < 0) + goto done; + if (new_id && (error = git_blob_lookup(&new, pi->diff->repo,new_id)) < 0) + goto done; + + if ((error = print_binary_hunk(pi, old, new)) < 0 || + (error = git_buf_putc(pi->buf, '\n')) < 0 || + (error = print_binary_hunk(pi, new, old)) < 0) + { + if (error == GIT_EBUFS) { + giterr_clear(); + git_buf_truncate(pi->buf, pre_binary_size); + goto noshow; + } + } + + pi->line.num_lines++; + +done: + git_blob_free(old); + git_blob_free(new); + + return error; + +noshow: + pi->line.num_lines = 1; + return diff_delta_format_with_paths( + pi->buf, delta, oldpfx, newpfx, + "Binary files %s%s and %s%s differ\n"); +} + static int diff_print_patch_file( const git_diff_delta *delta, float progress, void *data) { + int error; diff_print_info *pi = data; const char *oldpfx = pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT; const char *newpfx = pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT; + bool binary = !!(delta->flags & GIT_DIFF_FLAG_BINARY); + bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY); + int oid_strlen = binary && show_binary ? + GIT_OID_HEXSZ + 1 : pi->oid_strlen; + GIT_UNUSED(progress); if (S_ISDIR(delta->new_file.mode) || delta->status == GIT_DELTA_UNMODIFIED || delta->status == GIT_DELTA_IGNORED || + delta->status == GIT_DELTA_UNREADABLE || (delta->status == GIT_DELTA_UNTRACKED && (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0)) return 0; - if (git_diff_delta__format_file_header( - pi->buf, delta, oldpfx, newpfx, pi->oid_strlen) < 0) - return -1; + if ((error = git_diff_delta__format_file_header( + pi->buf, delta, oldpfx, newpfx, oid_strlen)) < 0) + return error; pi->line.origin = GIT_DIFF_LINE_FILE_HDR; pi->line.content = git_buf_cstr(pi->buf); pi->line.content_len = git_buf_len(pi->buf); - if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return callback_error(); + if ((error = pi->print_cb(delta, NULL, &pi->line, pi->payload)) != 0) + return error; - if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) + if (!binary) return 0; git_buf_clear(pi->buf); - if (diff_delta_format_with_paths( - pi->buf, delta, oldpfx, newpfx, - "Binary files %s%s and %s%s differ\n") < 0) - return -1; + if ((error = diff_print_patch_file_binary(pi, delta, oldpfx, newpfx)) < 0) + return error; pi->line.origin = GIT_DIFF_LINE_BINARY; pi->line.content = git_buf_cstr(pi->buf); pi->line.content_len = git_buf_len(pi->buf); - pi->line.num_lines = 1; - - if (pi->print_cb(delta, NULL, &pi->line, pi->payload)) - return callback_error(); - return 0; + return pi->print_cb(delta, NULL, &pi->line, pi->payload); } static int diff_print_patch_hunk( @@ -359,10 +487,7 @@ pi->line.content = h->header; pi->line.content_len = h->header_len; - if (pi->print_cb(d, h, &pi->line, pi->payload)) - return callback_error(); - - return 0; + return pi->print_cb(d, h, &pi->line, pi->payload); } static int diff_print_patch_line( @@ -376,10 +501,7 @@ if (S_ISDIR(delta->new_file.mode)) return 0; - if (pi->print_cb(delta, hunk, line, pi->payload)) - return callback_error(); - - return 0; + return pi->print_cb(delta, hunk, line, pi->payload); } /* print a git_diff to an output callback */ @@ -421,9 +543,14 @@ if (!(error = diff_print_info_init( &pi, &buf, diff, format, print_cb, payload))) + { error = git_diff_foreach( diff, print_file, print_hunk, print_line, &pi); + if (error) /* make sure error message is set */ + giterr_set_after_callback_function(error, "git_diff_print"); + } + git_buf_free(&buf); return error; @@ -444,16 +571,21 @@ if (!(error = diff_print_info_init( &pi, &temp, git_patch__diff(patch), GIT_DIFF_FORMAT_PATCH, print_cb, payload))) + { error = git_patch__invoke_callbacks( patch, diff_print_patch_file, diff_print_patch_hunk, diff_print_patch_line, &pi); + if (error) /* make sure error message is set */ + giterr_set_after_callback_function(error, "git_patch_print"); + } + git_buf_free(&temp); return error; } -static int diff_print_to_buffer_cb( +int git_diff_print_callback__to_buf( const git_diff_delta *delta, const git_diff_hunk *hunk, const git_diff_line *line, @@ -462,6 +594,11 @@ git_buf *output = payload; GIT_UNUSED(delta); GIT_UNUSED(hunk); + if (!output) { + giterr_set(GITERR_INVALID, "Buffer pointer must be provided"); + return -1; + } + if (line->origin == GIT_DIFF_LINE_ADDITION || line->origin == GIT_DIFF_LINE_DELETION || line->origin == GIT_DIFF_LINE_CONTEXT) @@ -470,23 +607,28 @@ return git_buf_put(output, line->content, line->content_len); } -/* print a git_patch to a string buffer */ -int git_patch_to_str( - char **string, - git_patch *patch) +int git_diff_print_callback__to_file_handle( + const git_diff_delta *delta, + const git_diff_hunk *hunk, + const git_diff_line *line, + void *payload) { - int error; - git_buf output = GIT_BUF_INIT; - - error = git_patch_print(patch, diff_print_to_buffer_cb, &output); + FILE *fp = payload ? payload : stdout; - /* GIT_EUSER means git_buf_put in print_to_buffer_cb returned -1, - * meaning a memory allocation failure, so just map to -1... - */ - if (error == GIT_EUSER) - error = -1; + GIT_UNUSED(delta); GIT_UNUSED(hunk); - *string = git_buf_detach(&output); + if (line->origin == GIT_DIFF_LINE_CONTEXT || + line->origin == GIT_DIFF_LINE_ADDITION || + line->origin == GIT_DIFF_LINE_DELETION) + fputc(line->origin, fp); + fwrite(line->content, 1, line->content_len, fp); + return 0; +} - return error; +/* print a git_patch to a git_buf */ +int git_patch_to_buf(git_buf *out, git_patch *patch) +{ + assert(out && patch); + git_buf_sanitize(out); + return git_patch_print(patch, git_diff_print_callback__to_buf, out); } diff -Nru libgit2-0.20.0/src/diff_stats.c libgit2-0.22.2/src/diff_stats.c --- libgit2-0.20.0/src/diff_stats.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/diff_stats.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,336 @@ +/* + * 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 "vector.h" +#include "diff.h" +#include "diff_patch.h" + +#define DIFF_RENAME_FILE_SEPARATOR " => " +#define STATS_FULL_MIN_SCALE 7 + +typedef struct { + size_t insertions; + size_t deletions; +} diff_file_stats; + +struct git_diff_stats { + git_diff *diff; + diff_file_stats *filestats; + + size_t files_changed; + size_t insertions; + size_t deletions; + size_t renames; + + size_t max_name; + size_t max_filestat; + int max_digits; +}; + +static int digits_for_value(size_t val) +{ + int count = 1; + size_t placevalue = 10; + + while (val >= placevalue) { + ++count; + placevalue *= 10; + } + + return count; +} + +int git_diff_file_stats__full_to_buf( + git_buf *out, + const git_diff_delta *delta, + const diff_file_stats *filestat, + const git_diff_stats *stats, + size_t width) +{ + const char *old_path = NULL, *new_path = NULL; + size_t padding, old_size, new_size; + + old_path = delta->old_file.path; + new_path = delta->new_file.path; + old_size = delta->old_file.size; + new_size = delta->new_file.size; + + if (git_buf_printf(out, " %s", old_path) < 0) + goto on_error; + + if (strcmp(old_path, new_path) != 0) { + padding = stats->max_name - strlen(old_path) - strlen(new_path); + + if (git_buf_printf(out, DIFF_RENAME_FILE_SEPARATOR "%s", new_path) < 0) + goto on_error; + } else { + padding = stats->max_name - strlen(old_path); + + if (stats->renames > 0) + padding += strlen(DIFF_RENAME_FILE_SEPARATOR); + } + + if (git_buf_putcn(out, ' ', padding) < 0 || + git_buf_puts(out, " | ") < 0) + goto on_error; + + if (delta->flags & GIT_DIFF_FLAG_BINARY) { + if (git_buf_printf(out, + "Bin %" PRIuZ " -> %" PRIuZ " bytes", old_size, new_size) < 0) + goto on_error; + } + else { + if (git_buf_printf(out, + "%*" PRIuZ, stats->max_digits, + filestat->insertions + filestat->deletions) < 0) + goto on_error; + + if (filestat->insertions || filestat->deletions) { + if (git_buf_putc(out, ' ') < 0) + goto on_error; + + if (!width) { + if (git_buf_putcn(out, '+', filestat->insertions) < 0 || + git_buf_putcn(out, '-', filestat->deletions) < 0) + goto on_error; + } else { + size_t total = filestat->insertions + filestat->deletions; + size_t full = (total * width + stats->max_filestat / 2) / + stats->max_filestat; + size_t plus = full * filestat->insertions / total; + size_t minus = full - plus; + + if (git_buf_putcn(out, '+', max(plus, 1)) < 0 || + git_buf_putcn(out, '-', max(minus, 1)) < 0) + goto on_error; + } + } + } + + git_buf_putc(out, '\n'); + +on_error: + return (git_buf_oom(out) ? -1 : 0); +} + +int git_diff_file_stats__number_to_buf( + git_buf *out, + const git_diff_delta *delta, + const diff_file_stats *filestats) +{ + int error; + const char *path = delta->new_file.path; + + if (delta->flags & GIT_DIFF_FLAG_BINARY) + error = git_buf_printf(out, "%-8c" "%-8c" "%s\n", '-', '-', path); + else + error = git_buf_printf(out, "%-8" PRIuZ "%-8" PRIuZ "%s\n", + filestats->insertions, filestats->deletions, path); + + return error; +} + +int git_diff_file_stats__summary_to_buf( + git_buf *out, + const git_diff_delta *delta) +{ + if (delta->old_file.mode != delta->new_file.mode) { + if (delta->old_file.mode == 0) { + git_buf_printf(out, " create mode %06o %s\n", + delta->new_file.mode, delta->new_file.path); + } + else if (delta->new_file.mode == 0) { + git_buf_printf(out, " delete mode %06o %s\n", + delta->old_file.mode, delta->old_file.path); + } + else { + git_buf_printf(out, " mode change %06o => %06o %s\n", + delta->old_file.mode, delta->new_file.mode, delta->new_file.path); + } + } + + return 0; +} + +int git_diff_get_stats( + git_diff_stats **out, + git_diff *diff) +{ + size_t i, deltas; + size_t total_insertions = 0, total_deletions = 0; + git_diff_stats *stats = NULL; + int error = 0; + + assert(out && diff); + + stats = git__calloc(1, sizeof(git_diff_stats)); + GITERR_CHECK_ALLOC(stats); + + deltas = git_diff_num_deltas(diff); + + stats->filestats = git__calloc(deltas, sizeof(diff_file_stats)); + if (!stats->filestats) { + git__free(stats); + return -1; + } + + stats->diff = diff; + GIT_REFCOUNT_INC(diff); + + for (i = 0; i < deltas && !error; ++i) { + git_patch *patch = NULL; + size_t add = 0, remove = 0, namelen; + const git_diff_delta *delta; + + if ((error = git_patch_from_diff(&patch, diff, i)) < 0) + break; + + /* keep a count of renames because it will affect formatting */ + delta = git_patch_get_delta(patch); + + namelen = strlen(delta->new_file.path); + if (strcmp(delta->old_file.path, delta->new_file.path) != 0) { + namelen += strlen(delta->old_file.path); + stats->renames++; + } + + /* and, of course, count the line stats */ + error = git_patch_line_stats(NULL, &add, &remove, patch); + + git_patch_free(patch); + + stats->filestats[i].insertions = add; + stats->filestats[i].deletions = remove; + + total_insertions += add; + total_deletions += remove; + + if (stats->max_name < namelen) + stats->max_name = namelen; + if (stats->max_filestat < add + remove) + stats->max_filestat = add + remove; + } + + stats->files_changed = deltas; + stats->insertions = total_insertions; + stats->deletions = total_deletions; + stats->max_digits = digits_for_value(stats->max_filestat + 1); + + if (error < 0) { + git_diff_stats_free(stats); + stats = NULL; + } + + *out = stats; + return error; +} + +size_t git_diff_stats_files_changed( + const git_diff_stats *stats) +{ + assert(stats); + + return stats->files_changed; +} + +size_t git_diff_stats_insertions( + const git_diff_stats *stats) +{ + assert(stats); + + return stats->insertions; +} + +size_t git_diff_stats_deletions( + const git_diff_stats *stats) +{ + assert(stats); + + return stats->deletions; +} + +int git_diff_stats_to_buf( + git_buf *out, + const git_diff_stats *stats, + git_diff_stats_format_t format, + size_t width) +{ + int error = 0; + size_t i; + const git_diff_delta *delta; + + assert(out && stats); + + if (format & GIT_DIFF_STATS_NUMBER) { + for (i = 0; i < stats->files_changed; ++i) { + if ((delta = git_diff_get_delta(stats->diff, i)) == NULL) + continue; + + error = git_diff_file_stats__number_to_buf( + out, delta, &stats->filestats[i]); + if (error < 0) + return error; + } + } + + if (format & GIT_DIFF_STATS_FULL) { + if (width > 0) { + if (width > stats->max_name + stats->max_digits + 5) + width -= (stats->max_name + stats->max_digits + 5); + if (width < STATS_FULL_MIN_SCALE) + width = STATS_FULL_MIN_SCALE; + } + if (width > stats->max_filestat) + width = 0; + + for (i = 0; i < stats->files_changed; ++i) { + if ((delta = git_diff_get_delta(stats->diff, i)) == NULL) + continue; + + error = git_diff_file_stats__full_to_buf( + out, delta, &stats->filestats[i], stats, width); + if (error < 0) + return error; + } + } + + if (format & GIT_DIFF_STATS_FULL || format & GIT_DIFF_STATS_SHORT) { + error = git_buf_printf( + out, " %" PRIuZ " file%s changed, %" PRIuZ + " insertion%s(+), %" PRIuZ " deletion%s(-)\n", + stats->files_changed, stats->files_changed != 1 ? "s" : "", + stats->insertions, stats->insertions != 1 ? "s" : "", + stats->deletions, stats->deletions != 1 ? "s" : ""); + + if (error < 0) + return error; + } + + if (format & GIT_DIFF_STATS_INCLUDE_SUMMARY) { + for (i = 0; i < stats->files_changed; ++i) { + if ((delta = git_diff_get_delta(stats->diff, i)) == NULL) + continue; + + error = git_diff_file_stats__summary_to_buf(out, delta); + if (error < 0) + return error; + } + } + + return error; +} + +void git_diff_stats_free(git_diff_stats *stats) +{ + if (stats == NULL) + return; + + git_diff_free(stats->diff); /* bumped refcount in constructor */ + git__free(stats->filestats); + git__free(stats); +} + diff -Nru libgit2-0.20.0/src/diff_tform.c libgit2-0.22.2/src/diff_tform.c --- libgit2-0.20.0/src/diff_tform.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/diff_tform.c 2015-03-24 16:10:45.000000000 +0000 @@ -8,11 +8,12 @@ #include "git2/config.h" #include "git2/blob.h" +#include "git2/sys/hashsig.h" #include "diff.h" -#include "hashsig.h" #include "path.h" #include "fileops.h" +#include "config.h" static git_diff_delta *diff_delta__dup( const git_diff_delta *d, git_pool *pool) @@ -90,7 +91,7 @@ dup->status = a->status; } - git_oid_cpy(&dup->old_file.oid, &a->old_file.oid); + git_oid_cpy(&dup->old_file.id, &a->old_file.id); dup->old_file.mode = a->old_file.mode; dup->old_file.size = a->old_file.size; dup->old_file.flags = a->old_file.flags; @@ -113,7 +114,7 @@ if ((dup = diff_delta__dup(a, pool)) == NULL) return NULL; - if (b->status == GIT_DELTA_UNMODIFIED || b->status == GIT_DELTA_UNTRACKED) + if (b->status == GIT_DELTA_UNMODIFIED || b->status == GIT_DELTA_UNTRACKED || b->status == GIT_DELTA_UNREADABLE) return dup; if (dup->status == GIT_DELTA_DELETED) { @@ -123,7 +124,7 @@ dup->status = b->status; } - git_oid_cpy(&dup->old_file.oid, &b->old_file.oid); + git_oid_cpy(&dup->old_file.id, &b->old_file.id); dup->old_file.mode = b->old_file.mode; dup->old_file.size = b->old_file.size; dup->old_file.flags = b->old_file.flags; @@ -208,9 +209,7 @@ git_pool_strdup_safe(&onto->pool, onto->opts.new_prefix); } - git_vector_foreach(&onto_new, i, delta) - git__free(delta); - git_vector_free(&onto_new); + git_vector_free_deep(&onto_new); git_pool_clear(&onto_pool); return error; @@ -219,7 +218,7 @@ int git_diff_find_similar__hashsig_for_file( void **out, const git_diff_file *f, const char *path, void *p) { - git_hashsig_option_t opt = (git_hashsig_option_t)p; + git_hashsig_option_t opt = (git_hashsig_option_t)(intptr_t)p; int error = 0; GIT_UNUSED(f); @@ -236,7 +235,7 @@ int git_diff_find_similar__hashsig_for_buf( void **out, const git_diff_file *f, const char *buf, size_t len, void *p) { - git_hashsig_option_t opt = (git_hashsig_option_t)p; + git_hashsig_option_t opt = (git_hashsig_option_t)(intptr_t)p; int error = 0; GIT_UNUSED(f); @@ -275,28 +274,30 @@ { git_config *cfg = NULL; + GITERR_CHECK_VERSION(given, GIT_DIFF_FIND_OPTIONS_VERSION, "git_diff_find_options"); + if (diff->repo != NULL && git_repository_config__weakptr(&cfg, diff->repo) < 0) return -1; - if (given != NULL) + if (given) memcpy(opts, given, sizeof(*opts)); - else { - const char *val = NULL; - - GIT_INIT_STRUCTURE(opts, GIT_DIFF_FIND_OPTIONS_VERSION); - opts->flags = GIT_DIFF_FIND_RENAMES; - - if (git_config_get_string(&val, cfg, "diff.renames") < 0) - giterr_clear(); - else if (val && - (!strcasecmp(val, "copies") || !strcasecmp(val, "copy"))) - opts->flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES; + if (!given || + (given->flags & GIT_DIFF_FIND_ALL) == GIT_DIFF_FIND_BY_CONFIG) + { + const char *rule = + git_config__get_string_force(cfg, "diff.renames", "true"); + int boolval; + + if (!git__parse_bool(&boolval, rule) && !boolval) + /* don't set FIND_RENAMES if bool value is false */; + else if (!strcasecmp(rule, "copies") || !strcasecmp(rule, "copy")) + opts->flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES; + else + opts->flags |= GIT_DIFF_FIND_RENAMES; } - GITERR_CHECK_VERSION(opts, GIT_DIFF_FIND_OPTIONS_VERSION, "git_diff_find_options"); - /* some flags imply others */ if (opts->flags & GIT_DIFF_FIND_EXACT_MATCH_ONLY) { @@ -335,14 +336,11 @@ #undef USE_DEFAULT if (!opts->rename_limit) { - int32_t limit = 0; + opts->rename_limit = git_config__get_int_force( + cfg, "diff.renamelimit", DEFAULT_RENAME_LIMIT); - opts->rename_limit = DEFAULT_RENAME_LIMIT; - - if (git_config_get_int32(&limit, cfg, "diff.renameLimit") < 0) - giterr_clear(); - else if (limit > 0) - opts->rename_limit = limit; + if (opts->rename_limit <= 0) + opts->rename_limit = DEFAULT_RENAME_LIMIT; } /* assign the internal metric with whitespace flag as payload */ @@ -366,12 +364,28 @@ return 0; } +static int insert_delete_side_of_split( + git_diff *diff, git_vector *onto, const git_diff_delta *delta) +{ + /* make new record for DELETED side of split */ + git_diff_delta *deleted = diff_delta__dup(delta, &diff->pool); + GITERR_CHECK_ALLOC(deleted); + + deleted->status = GIT_DELTA_DELETED; + deleted->nfiles = 1; + memset(&deleted->new_file, 0, sizeof(deleted->new_file)); + deleted->new_file.path = deleted->old_file.path; + deleted->new_file.flags |= GIT_DIFF_FLAG_VALID_ID; + + return git_vector_insert(onto, deleted); +} + static int apply_splits_and_deletes( git_diff *diff, size_t expected_size, bool actually_split) { git_vector onto = GIT_VECTOR_INIT; size_t i; - git_diff_delta *delta, *deleted; + git_diff_delta *delta; if (git_vector_init(&onto, expected_size, git_diff_delta__cmp) < 0) return -1; @@ -384,17 +398,7 @@ if ((delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0 && actually_split) { delta->similarity = 0; - /* make new record for DELETED side of split */ - if (!(deleted = diff_delta__dup(delta, &diff->pool))) - goto on_error; - - deleted->status = GIT_DELTA_DELETED; - deleted->nfiles = 1; - memset(&deleted->new_file, 0, sizeof(deleted->new_file)); - deleted->new_file.path = deleted->old_file.path; - deleted->new_file.flags |= GIT_DIFF_FLAG_VALID_OID; - - if (git_vector_insert(&onto, deleted) < 0) + if (insert_delete_side_of_split(diff, &onto, delta) < 0) goto on_error; if (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) @@ -404,7 +408,7 @@ delta->nfiles = 1; memset(&delta->old_file, 0, sizeof(delta->old_file)); delta->old_file.path = delta->new_file.path; - delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID; + delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID; } /* clean up delta before inserting into new list */ @@ -436,9 +440,7 @@ return 0; on_error: - git_vector_foreach(&onto, i, delta) - git__free(delta); - git_vector_free(&onto); + git_vector_free_deep(&onto); return -1; } @@ -470,7 +472,7 @@ info->blob = NULL; git_buf_init(&info->data, 0); - if (info->file->size > 0) + if (info->file->size > 0 || info->src == GIT_ITERATOR_TYPE_WORKDIR) return 0; return git_diff_file__resolve_zero_size( @@ -508,7 +510,7 @@ (git_object **)&info->blob, info->repo, info->odb_obj, GIT_OBJ_BLOB); else - error = git_blob_lookup(&info->blob, info->repo, &file->oid); + error = git_blob_lookup(&info->blob, info->repo, &file->id); if (error < 0) { /* if lookup fails, just skip this item in similarity calc */ @@ -570,21 +572,21 @@ /* if exact match is requested, force calculation of missing OIDs now */ if (exact_match) { - if (git_oid_iszero(&a_file->oid) && + if (git_oid_iszero(&a_file->id) && diff->old_src == GIT_ITERATOR_TYPE_WORKDIR && - !git_diff__oid_for_file(diff->repo, a_file->path, - a_file->mode, a_file->size, &a_file->oid)) - a_file->flags |= GIT_DIFF_FLAG_VALID_OID; + !git_diff__oid_for_file(&a_file->id, + diff, a_file->path, a_file->mode, a_file->size)) + a_file->flags |= GIT_DIFF_FLAG_VALID_ID; - if (git_oid_iszero(&b_file->oid) && + if (git_oid_iszero(&b_file->id) && diff->new_src == GIT_ITERATOR_TYPE_WORKDIR && - !git_diff__oid_for_file(diff->repo, b_file->path, - b_file->mode, b_file->size, &b_file->oid)) - b_file->flags |= GIT_DIFF_FLAG_VALID_OID; + !git_diff__oid_for_file(&b_file->id, + diff, b_file->path, b_file->mode, b_file->size)) + b_file->flags |= GIT_DIFF_FLAG_VALID_ID; } /* check OID match as a quick test */ - if (git_oid__cmp(&a_file->oid, &b_file->oid) == 0) { + if (git_oid__cmp(&a_file->id, &b_file->id) == 0) { *score = 100; return 0; } @@ -730,6 +732,7 @@ switch (delta->status) { case GIT_DELTA_ADDED: case GIT_DELTA_UNTRACKED: + case GIT_DELTA_UNREADABLE: case GIT_DELTA_IGNORED: return false; @@ -740,6 +743,8 @@ case GIT_DELTA_UNMODIFIED: if (!FLAG_SET(opts, GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED)) return false; + if (FLAG_SET(opts, GIT_DIFF_FIND_REMOVE_UNMODIFIED)) + delta->flags |= GIT_DIFF_FLAG__TO_DELETE; break; default: /* MODIFIED, RENAMED, COPIED */ @@ -782,6 +787,7 @@ { return (delta->status == GIT_DELTA_ADDED || delta->status == GIT_DELTA_UNTRACKED || + delta->status == GIT_DELTA_UNREADABLE || delta->status == GIT_DELTA_IGNORED); } @@ -808,11 +814,11 @@ int error = 0, result; uint16_t similarity; git_diff_delta *src, *tgt; - git_diff_find_options opts; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; size_t num_deltas, num_srcs = 0, num_tgts = 0; size_t tried_srcs = 0, tried_tgts = 0; size_t num_rewrites = 0, num_updates = 0, num_bumped = 0; - void **sigcache; /* cache of similarity metric file signatures */ + void **sigcache = NULL; /* cache of similarity metric file signatures */ diff_find_match *tgt2src = NULL; diff_find_match *src2tgt = NULL; diff_find_match *tgt2src_copy = NULL; @@ -826,7 +832,11 @@ /* TODO: maybe abort if deltas.length > rename_limit ??? */ if (!git__is_uint32(num_deltas)) - return 0; + goto cleanup; + + /* No flags set; nothing to do */ + if ((opts.flags & GIT_DIFF_FIND_ALL) == 0) + goto cleanup; sigcache = git__calloc(num_deltas * 2, sizeof(void *)); GITERR_CHECK_ALLOC(sigcache); @@ -939,8 +949,6 @@ * Rewrite the diffs with renames / copies */ - tried_tgts = 0; - git_vector_foreach(&diff->deltas, t, tgt) { /* skip things that are not rename targets */ if ((tgt->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0) @@ -991,7 +999,7 @@ memcpy(&src->old_file, &swap, sizeof(src->old_file)); memset(&src->new_file, 0, sizeof(src->new_file)); src->new_file.path = src->old_file.path; - src->new_file.flags |= GIT_DIFF_FLAG_VALID_OID; + src->new_file.flags |= GIT_DIFF_FLAG_VALID_ID; num_updates++; @@ -1016,7 +1024,7 @@ src->nfiles = 1; memset(&src->old_file, 0, sizeof(src->old_file)); src->old_file.path = src->new_file.path; - src->old_file.flags |= GIT_DIFF_FLAG_VALID_OID; + src->old_file.flags |= GIT_DIFF_FLAG_VALID_ID; src->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; num_rewrites--; @@ -1058,10 +1066,7 @@ } } - else if (delta_is_new_only(tgt)) { - if (!FLAG_SET(&opts, GIT_DIFF_FIND_COPIES)) - continue; - + else if (FLAG_SET(&opts, GIT_DIFF_FIND_COPIES)) { if (tgt2src_copy[t].similarity < opts.copy_threshold) continue; @@ -1069,10 +1074,21 @@ best_match = &tgt2src_copy[t]; src = GIT_VECTOR_GET(&diff->deltas, best_match->idx); + if (delta_is_split(tgt)) { + error = insert_delete_side_of_split(diff, &diff->deltas, tgt); + if (error < 0) + goto cleanup; + num_rewrites--; + } + + if (!delta_is_split(tgt) && !delta_is_new_only(tgt)) + continue; + tgt->status = GIT_DELTA_COPIED; tgt->similarity = best_match->similarity; tgt->nfiles = 2; memcpy(&tgt->old_file, &src->old_file, sizeof(tgt->old_file)); + tgt->flags &= ~GIT_DIFF_FLAG__TO_SPLIT; num_updates++; } @@ -1093,11 +1109,13 @@ git__free(src2tgt); git__free(tgt2src_copy); - for (t = 0; t < num_deltas * 2; ++t) { - if (sigcache[t] != NULL) - opts.metric->free_signature(sigcache[t], opts.metric->payload); + if (sigcache) { + for (t = 0; t < num_deltas * 2; ++t) { + if (sigcache[t] != NULL) + opts.metric->free_signature(sigcache[t], opts.metric->payload); + } + git__free(sigcache); } - git__free(sigcache); if (!given_opts || !given_opts->metric) git__free(opts.metric); diff -Nru libgit2-0.20.0/src/diff_xdiff.c libgit2-0.22.2/src/diff_xdiff.c --- libgit2-0.20.0/src/diff_xdiff.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/diff_xdiff.c 2015-03-24 16:10:45.000000000 +0000 @@ -28,25 +28,29 @@ { /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */ if (*header != '@') - return -1; + goto fail; if (git_xdiff_scan_int(&header, &hunk->old_start) < 0) - return -1; + goto fail; if (*header == ',') { if (git_xdiff_scan_int(&header, &hunk->old_lines) < 0) - return -1; + goto fail; } else hunk->old_lines = 1; if (git_xdiff_scan_int(&header, &hunk->new_start) < 0) - return -1; + goto fail; if (*header == ',') { if (git_xdiff_scan_int(&header, &hunk->new_lines) < 0) - return -1; + goto fail; } else hunk->new_lines = 1; if (hunk->old_start < 0 || hunk->new_start < 0) - return -1; + goto fail; return 0; + +fail: + giterr_set(GITERR_INVALID, "Malformed hunk header from xdiff"); + return -1; } typedef struct { @@ -122,8 +126,9 @@ info->hunk.header[info->hunk.header_len] = '\0'; if (output->hunk_cb != NULL && - output->hunk_cb(delta, &info->hunk, output->payload)) - output->error = GIT_EUSER; + (output->error = output->hunk_cb( + delta, &info->hunk, output->payload))) + return output->error; info->old_lineno = info->hunk.old_start; info->new_lineno = info->hunk.new_start; @@ -146,10 +151,9 @@ output->error = diff_update_lines( info, &line, bufs[1].ptr, bufs[1].size); - if (!output->error && - output->data_cb != NULL && - output->data_cb(delta, &info->hunk, &line, output->payload)) - output->error = GIT_EUSER; + if (!output->error && output->data_cb != NULL) + output->error = output->data_cb( + delta, &info->hunk, &line, output->payload); } if (len == 3 && !output->error) { @@ -168,10 +172,9 @@ output->error = diff_update_lines( info, &line, bufs[2].ptr, bufs[2].size); - if (!output->error && - output->data_cb != NULL && - output->data_cb(delta, &info->hunk, &line, output->payload)) - output->error = GIT_EUSER; + if (!output->error && output->data_cb != NULL) + output->error = output->data_cb( + delta, &info->hunk, &line, output->payload); } return output->error; @@ -219,11 +222,9 @@ xo->output.diff_cb = git_xdiff; - memset(&xo->config, 0, sizeof(xo->config)); xo->config.ctxlen = opts ? opts->context_lines : 3; xo->config.interhunkctxlen = opts ? opts->interhunk_lines : 0; - memset(&xo->params, 0, sizeof(xo->params)); if (flags & GIT_DIFF_IGNORE_WHITESPACE) xo->params.flags |= XDF_WHITESPACE_FLAGS; if (flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE) @@ -236,6 +237,5 @@ if (flags & GIT_DIFF_MINIMAL) xo->params.flags |= XDF_NEED_MINIMAL; - memset(&xo->callback, 0, sizeof(xo->callback)); xo->callback.outf = git_xdiff_cb; } diff -Nru libgit2-0.20.0/src/errors.c libgit2-0.22.2/src/errors.c --- libgit2-0.20.0/src/errors.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/errors.c 2015-03-24 16:10:45.000000000 +0000 @@ -8,7 +8,6 @@ #include "global.h" #include "posix.h" #include "buffer.h" -#include /******************************************** * New error handling @@ -23,7 +22,8 @@ { git_error *error = &GIT_GLOBAL->error_t; - git__free(error->message); + if (error->message != string) + git__free(error->message); error->message = string; error->klass = error_class; @@ -45,15 +45,19 @@ #endif int error_code = (error_class == GITERR_OS) ? errno : 0; - va_start(arglist, string); - git_buf_vprintf(&buf, string, arglist); - va_end(arglist); + if (string) { + va_start(arglist, string); + git_buf_vprintf(&buf, string, arglist); + va_end(arglist); + + if (error_class == GITERR_OS) + git_buf_PUTS(&buf, ": "); + } if (error_class == GITERR_OS) { #ifdef GIT_WIN32 char * win32_error = git_win32_get_error_message(win32_error_code); if (win32_error) { - git_buf_PUTS(&buf, ": "); git_buf_puts(&buf, win32_error); git__free(win32_error); @@ -61,10 +65,8 @@ } else #endif - if (error_code) { - git_buf_PUTS(&buf, ": "); + if (error_code) git_buf_puts(&buf, strerror(error_code)); - } if (error_code) errno = 0; @@ -103,8 +105,10 @@ void giterr_clear(void) { - set_error(0, NULL); - GIT_GLOBAL->last_error = NULL; + if (GIT_GLOBAL->last_error != NULL) { + set_error(0, NULL); + GIT_GLOBAL->last_error = NULL; + } errno = 0; #ifdef GIT_WIN32 @@ -134,3 +138,39 @@ { return GIT_GLOBAL->last_error; } + +int giterr_capture(git_error_state *state, int error_code) +{ + state->error_code = error_code; + if (error_code) + giterr_detach(&state->error_msg); + return error_code; +} + +int giterr_restore(git_error_state *state) +{ + if (state && state->error_code && state->error_msg.message) + set_error(state->error_msg.klass, state->error_msg.message); + else + giterr_clear(); + + return state ? state->error_code : 0; +} + +int giterr_system_last(void) +{ +#ifdef GIT_WIN32 + return GetLastError(); +#else + return errno; +#endif +} + +void giterr_system_set(int code) +{ +#ifdef GIT_WIN32 + SetLastError(code); +#else + errno = code; +#endif +} diff -Nru libgit2-0.20.0/src/fetch.c libgit2-0.22.2/src/fetch.c --- libgit2-0.20.0/src/fetch.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/fetch.c 2015-03-24 16:10:45.000000000 +0000 @@ -28,22 +28,23 @@ if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { /* - * If tagopt is --tags, then we only use the default - * tags refspec and ignore the remote's + * If tagopt is --tags, always request tags + * in addition to the remote's refspecs */ if (git_refspec_src_matches(tagspec, head->name)) match = 1; - else - return 0; - } else if (git_remote__matching_refspec(remote, head->name)) - match = 1; + } + + if (!match && git_remote__matching_refspec(remote, head->name)) + match = 1; if (!match) return 0; /* If we have the object, mark it so we don't ask for it */ - if (git_odb_exists(odb, &head->oid)) + if (git_odb_exists(odb, &head->oid)) { head->local = 1; + } else remote->need_pack = 1; @@ -104,7 +105,9 @@ int git_fetch_negotiate(git_remote *remote) { git_transport *t = remote->transport; - + + remote->need_pack = 0; + if (filter_wants(remote) < 0) { giterr_set(GITERR_NET, "Failed to filter the reference list for wants"); return -1; @@ -128,9 +131,9 @@ { git_transport *t = remote->transport; - if(!remote->need_pack) + if (!remote->need_pack) return 0; return t->download_pack(t, remote->repo, &remote->stats, - remote->callbacks.transfer_progress, remote->callbacks.payload); + remote->callbacks.transfer_progress, remote->callbacks.payload); } diff -Nru libgit2-0.20.0/src/fetch.h libgit2-0.22.2/src/fetch.h --- libgit2-0.20.0/src/fetch.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/fetch.h 2015-03-24 16:10:45.000000000 +0000 @@ -17,7 +17,7 @@ git_transport *t, git_repository *repo, git_transfer_progress *stats, - git_transfer_progress_callback progress_cb, + git_transfer_progress_cb progress_cb, void *progress_payload); int git_fetch_setup_walk(git_revwalk **out, git_repository *repo); diff -Nru libgit2-0.20.0/src/fetchhead.c libgit2-0.22.2/src/fetchhead.c --- libgit2-0.20.0/src/fetchhead.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/fetchhead.c 2015-03-24 16:10:45.000000000 +0000 @@ -210,7 +210,7 @@ name = desc + 1; if (name) { - if ((desc = strchr(name, '\'')) == NULL || + if ((desc = strstr(name, "' ")) == NULL || git__prefixcmp(desc, "' of ") != 0) { giterr_set(GITERR_FETCHHEAD, "Invalid description in FETCH_HEAD line %d", line_num); @@ -260,8 +260,8 @@ while ((line = git__strsep(&buffer, "\n")) != NULL) { ++line_num; - if ((error = fetchhead_ref_parse(&oid, &is_merge, &name, &remote_url, - line, line_num)) < 0) + if ((error = fetchhead_ref_parse( + &oid, &is_merge, &name, &remote_url, line, line_num)) < 0) goto done; if (git_buf_len(&name) > 0) @@ -269,8 +269,9 @@ else ref_name = NULL; - if ((cb(ref_name, remote_url, &oid, is_merge, payload)) != 0) { - error = GIT_EUSER; + error = cb(ref_name, remote_url, &oid, is_merge, payload); + if (error) { + giterr_set_after_callback(error); goto done; } } diff -Nru libgit2-0.20.0/src/fetchhead.h libgit2-0.22.2/src/fetchhead.h --- libgit2-0.20.0/src/fetchhead.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/fetchhead.h 2015-03-24 16:10:45.000000000 +0000 @@ -9,14 +9,12 @@ #include "vector.h" -struct git_fetchhead_ref { +typedef struct git_fetchhead_ref { git_oid oid; unsigned int is_merge; char *ref_name; char *remote_url; -}; - -typedef struct git_fetchhead_ref git_fetchhead_ref; +} git_fetchhead_ref; int git_fetchhead_ref_create( git_fetchhead_ref **fetchhead_ref_out, diff -Nru libgit2-0.20.0/src/filebuf.c libgit2-0.22.2/src/filebuf.c --- libgit2-0.20.0/src/filebuf.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/filebuf.c 2015-03-24 16:10:45.000000000 +0000 @@ -4,8 +4,6 @@ * 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 - #include "common.h" #include "filebuf.h" #include "fileops.h" @@ -336,8 +334,6 @@ file->fd = -1; - p_unlink(file->path_original); - if (p_rename(file->path_lock, file->path_original) < 0) { giterr_set(GITERR_OS, "Failed to rename lockfile to '%s'", file->path_original); goto on_error; diff -Nru libgit2-0.20.0/src/filebuf.h libgit2-0.22.2/src/filebuf.h --- libgit2-0.20.0/src/filebuf.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/filebuf.h 2015-03-24 16:10:45.000000000 +0000 @@ -25,11 +25,12 @@ #define GIT_FILELOCK_EXTENSION ".lock\0" #define GIT_FILELOCK_EXTLENGTH 6 +typedef struct git_filebuf git_filebuf; struct git_filebuf { char *path_original; char *path_lock; - int (*write)(struct git_filebuf *file, void *source, size_t len); + int (*write)(git_filebuf *file, void *source, size_t len); bool compute_digest; git_hash_ctx digest; @@ -47,8 +48,6 @@ int last_error; }; -typedef struct git_filebuf git_filebuf; - #define GIT_FILEBUF_INIT {0} /* diff -Nru libgit2-0.20.0/src/fileops.c libgit2-0.22.2/src/fileops.c --- libgit2-0.20.0/src/fileops.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/fileops.c 2015-03-24 16:10:45.000000000 +0000 @@ -132,6 +132,7 @@ if (read_size != (ssize_t)len) { giterr_set(GITERR_OS, "Failed to read descriptor"); + git_buf_free(buf); return -1; } @@ -278,15 +279,58 @@ p_munmap(out); } -int git_futils_mkdir( +GIT_INLINE(int) validate_existing( + const char *make_path, + struct stat *st, + mode_t mode, + uint32_t flags, + struct git_futils_mkdir_perfdata *perfdata) +{ + if ((S_ISREG(st->st_mode) && (flags & GIT_MKDIR_REMOVE_FILES)) || + (S_ISLNK(st->st_mode) && (flags & GIT_MKDIR_REMOVE_SYMLINKS))) { + if (p_unlink(make_path) < 0) { + giterr_set(GITERR_OS, "Failed to remove %s '%s'", + S_ISLNK(st->st_mode) ? "symlink" : "file", make_path); + return GIT_EEXISTS; + } + + perfdata->mkdir_calls++; + + if (p_mkdir(make_path, mode) < 0) { + giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path); + return GIT_EEXISTS; + } + } + + else if (S_ISLNK(st->st_mode)) { + /* Re-stat the target, make sure it's a directory */ + perfdata->stat_calls++; + + if (p_stat(make_path, st) < 0) { + giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path); + return GIT_EEXISTS; + } + } + + else if (!S_ISDIR(st->st_mode)) { + giterr_set(GITERR_INVALID, + "Failed to make directory '%s': directory exists", make_path); + return GIT_EEXISTS; + } + + return 0; +} + +int git_futils_mkdir_withperf( const char *path, const char *base, mode_t mode, - uint32_t flags) + uint32_t flags, + struct git_futils_mkdir_perfdata *perfdata) { int error = -1; git_buf make_path = GIT_BUF_INIT; - ssize_t root = 0, min_root_len; + ssize_t root = 0, min_root_len, root_len; char lastch = '/', *tail; struct stat st; @@ -299,22 +343,29 @@ goto done; } - /* remove trailing slashes on path */ - while (make_path.ptr[make_path.size - 1] == '/') { - make_path.size--; - make_path.ptr[make_path.size] = '\0'; - } + /* Trim trailing slashes (except the root) */ + if ((root_len = git_path_root(make_path.ptr)) < 0) + root_len = 0; + else + root_len++; + + while (make_path.size > (size_t)root_len && + make_path.ptr[make_path.size - 1] == '/') + make_path.ptr[--make_path.size] = '\0'; /* if we are not supposed to made the last element, truncate it */ if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) { - git_buf_rtruncate_at_char(&make_path, '/'); + git_path_dirname_r(&make_path, make_path.ptr); flags |= GIT_MKDIR_SKIP_LAST; } - if ((flags & GIT_MKDIR_SKIP_LAST) != 0) - git_buf_rtruncate_at_char(&make_path, '/'); + if ((flags & GIT_MKDIR_SKIP_LAST) != 0) { + git_path_dirname_r(&make_path, make_path.ptr); + } - /* if nothing left after truncation, then we're done! */ - if (!make_path.size) { + /* We were either given the root path (or trimmed it to + * the root), we don't have anything to do. + */ + if (make_path.size <= (size_t)root_len) { error = 0; goto done; } @@ -350,32 +401,45 @@ *tail = '\0'; st.st_mode = 0; - /* make directory */ - if (p_mkdir(make_path.ptr, mode) < 0) { - int tmp_errno = giterr_system_last(); - - /* ignore error if directory already exists */ - if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) { - giterr_system_set(tmp_errno); + /* See what's going on with this path component */ + perfdata->stat_calls++; + + if (p_lstat(make_path.ptr, &st) < 0) { + perfdata->mkdir_calls++; + + if (errno != ENOENT || p_mkdir(make_path.ptr, mode) < 0) { giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr); + error = GIT_EEXISTS; goto done; } + giterr_clear(); + } else { /* with exclusive create, existing dir is an error */ if ((flags & GIT_MKDIR_EXCL) != 0) { - giterr_set(GITERR_OS, "Directory already exists '%s'", make_path.ptr); + giterr_set(GITERR_INVALID, "Failed to make directory '%s': directory exists", make_path.ptr); error = GIT_EEXISTS; goto done; } + + if ((error = validate_existing( + make_path.ptr, &st, mode, flags, perfdata)) < 0) + goto done; } /* chmod if requested and necessary */ if (((flags & GIT_MKDIR_CHMOD_PATH) != 0 || (lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) && - st.st_mode != mode && - (error = p_chmod(make_path.ptr, mode)) < 0) { - giterr_set(GITERR_OS, "Failed to set permissions on '%s'", make_path.ptr); - goto done; + st.st_mode != mode) { + + perfdata->chmod_calls++; + + if ((error = p_chmod(make_path.ptr, mode)) < 0 && + lastch == '\0') { + giterr_set(GITERR_OS, "Failed to set permissions on '%s'", + make_path.ptr); + goto done; + } } } @@ -383,10 +447,14 @@ /* check that full path really is a directory if requested & needed */ if ((flags & GIT_MKDIR_VERIFY_DIR) != 0 && - lastch != '\0' && - (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode))) { - giterr_set(GITERR_OS, "Path is not a directory '%s'", make_path.ptr); - error = GIT_ENOTFOUND; + lastch != '\0') { + perfdata->stat_calls++; + + if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) { + giterr_set(GITERR_OS, "Path is not a directory '%s'", + make_path.ptr); + error = GIT_ENOTFOUND; + } } done: @@ -394,6 +462,16 @@ return error; } +int git_futils_mkdir( + const char *path, + const char *base, + mode_t mode, + uint32_t flags) +{ + struct git_futils_mkdir_perfdata perfdata = {0}; + return git_futils_mkdir_withperf(path, base, mode, flags, &perfdata); +} + int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode) { return git_futils_mkdir(path, base, mode, GIT_MKDIR_PATH); @@ -403,7 +481,6 @@ const char *base; size_t baselen; uint32_t flags; - int error; int depth; } futils__rmdir_data; @@ -447,8 +524,8 @@ static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path) { + int error = 0; futils__rmdir_data *data = opaque; - int error = data->error; struct stat st; if (data->depth > FUTILS_MAX_DEPTH) @@ -474,13 +551,14 @@ data->depth++; error = git_path_direach(path, 0, futils__rmdir_recurs_foreach, data); - if (error < 0) - return (error == GIT_EUSER) ? data->error : error; data->depth--; + if (error < 0) + return error; + if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0) - return data->error; + return error; if ((error = p_rmdir(path->ptr)) < 0) { if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 && @@ -499,31 +577,26 @@ else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0) error = futils__error_cannot_rmdir(path->ptr, "still present"); - data->error = error; return error; } -static int futils__rmdir_empty_parent(void *opaque, git_buf *path) +static int futils__rmdir_empty_parent(void *opaque, const char *path) { futils__rmdir_data *data = opaque; - int error; - - if (git_buf_len(path) <= data->baselen) - return GIT_ITEROVER; + int error = 0; - error = p_rmdir(git_buf_cstr(path)); + if (strlen(path) <= data->baselen) + error = GIT_ITEROVER; - if (error) { + else if (p_rmdir(path) < 0) { int en = errno; if (en == ENOENT || en == ENOTDIR) { - giterr_clear(); - error = 0; + /* do nothing */ } else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) { - giterr_clear(); error = GIT_ITEROVER; } else { - error = git_path_set_error(errno, git_buf_cstr(path), "rmdir"); + error = git_path_set_error(errno, path, "rmdir"); } } @@ -535,12 +608,13 @@ { int error; git_buf fullpath = GIT_BUF_INIT; - futils__rmdir_data data = { 0 }; + futils__rmdir_data data; /* build path and find "root" where we should start calling mkdir */ if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0) return -1; + memset(&data, 0, sizeof(data)); data.base = base ? base : ""; data.baselen = base ? strlen(base) : 0; data.flags = flags; @@ -548,12 +622,13 @@ error = futils__rmdir_recurs_foreach(&data, &fullpath); /* remove now-empty parents if requested */ - if (!error && (flags & GIT_RMDIR_EMPTY_PARENTS) != 0) { + if (!error && (flags & GIT_RMDIR_EMPTY_PARENTS) != 0) error = git_path_walk_up( &fullpath, base, futils__rmdir_empty_parent, &data); - if (error == GIT_ITEROVER) - error = 0; + if (error == GIT_ITEROVER) { + giterr_clear(); + error = 0; } git_buf_free(&fullpath); @@ -561,219 +636,6 @@ return error; } - -static int git_futils_guess_system_dirs(git_buf *out) -{ -#ifdef GIT_WIN32 - return git_win32__find_system_dirs(out, L"etc\\"); -#else - return git_buf_sets(out, "/etc"); -#endif -} - -static int git_futils_guess_global_dirs(git_buf *out) -{ -#ifdef GIT_WIN32 - return git_win32__find_global_dirs(out); -#else - return git_buf_sets(out, getenv("HOME")); -#endif -} - -static int git_futils_guess_xdg_dirs(git_buf *out) -{ -#ifdef GIT_WIN32 - return git_win32__find_xdg_dirs(out); -#else - const char *env = NULL; - - if ((env = getenv("XDG_CONFIG_HOME")) != NULL) - return git_buf_joinpath(out, env, "git"); - else if ((env = getenv("HOME")) != NULL) - return git_buf_joinpath(out, env, ".config/git"); - - git_buf_clear(out); - return 0; -#endif -} - -static int git_futils_guess_template_dirs(git_buf *out) -{ -#ifdef GIT_WIN32 - return git_win32__find_system_dirs(out, L"share\\git-core\\templates"); -#else - return git_buf_sets(out, "/usr/share/git-core/templates"); -#endif -} - -typedef int (*git_futils_dirs_guess_cb)(git_buf *out); - -static git_buf git_futils__dirs[GIT_FUTILS_DIR__MAX] = - { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT }; - -static git_futils_dirs_guess_cb git_futils__dir_guess[GIT_FUTILS_DIR__MAX] = { - git_futils_guess_system_dirs, - git_futils_guess_global_dirs, - git_futils_guess_xdg_dirs, - git_futils_guess_template_dirs, -}; - -void git_futils_dirs_global_shutdown(void) -{ - int i; - for (i = 0; i < GIT_FUTILS_DIR__MAX; ++i) - git_buf_free(&git_futils__dirs[i]); -} - -int git_futils_dirs_global_init(void) -{ - git_futils_dir_t i; - const git_buf *path; - int error = 0; - - for (i = 0; !error && i < GIT_FUTILS_DIR__MAX; i++) - error = git_futils_dirs_get(&path, i); - - git__on_shutdown(git_futils_dirs_global_shutdown); - - return error; -} - -static int git_futils_check_selector(git_futils_dir_t which) -{ - if (which < GIT_FUTILS_DIR__MAX) - return 0; - giterr_set(GITERR_INVALID, "config directory selector out of range"); - return -1; -} - -int git_futils_dirs_get(const git_buf **out, git_futils_dir_t which) -{ - assert(out); - - *out = NULL; - - GITERR_CHECK_ERROR(git_futils_check_selector(which)); - - if (!git_buf_len(&git_futils__dirs[which])) - GITERR_CHECK_ERROR( - git_futils__dir_guess[which](&git_futils__dirs[which])); - - *out = &git_futils__dirs[which]; - return 0; -} - -int git_futils_dirs_get_str(char *out, size_t outlen, git_futils_dir_t which) -{ - const git_buf *path = NULL; - - GITERR_CHECK_ERROR(git_futils_check_selector(which)); - GITERR_CHECK_ERROR(git_futils_dirs_get(&path, which)); - - if (!out || path->size >= outlen) { - giterr_set(GITERR_NOMEMORY, "Buffer is too short for the path"); - return GIT_EBUFS; - } - - git_buf_copy_cstr(out, outlen, path); - return 0; -} - -#define PATH_MAGIC "$PATH" - -int git_futils_dirs_set(git_futils_dir_t which, const char *search_path) -{ - const char *expand_path = NULL; - git_buf merge = GIT_BUF_INIT; - - GITERR_CHECK_ERROR(git_futils_check_selector(which)); - - if (search_path != NULL) - expand_path = strstr(search_path, PATH_MAGIC); - - /* init with default if not yet done and needed (ignoring error) */ - if ((!search_path || expand_path) && - !git_buf_len(&git_futils__dirs[which])) - git_futils__dir_guess[which](&git_futils__dirs[which]); - - /* if $PATH is not referenced, then just set the path */ - if (!expand_path) - return git_buf_sets(&git_futils__dirs[which], search_path); - - /* otherwise set to join(before $PATH, old value, after $PATH) */ - if (expand_path > search_path) - git_buf_set(&merge, search_path, expand_path - search_path); - - if (git_buf_len(&git_futils__dirs[which])) - git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, - merge.ptr, git_futils__dirs[which].ptr); - - expand_path += strlen(PATH_MAGIC); - if (*expand_path) - git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, merge.ptr, expand_path); - - git_buf_swap(&git_futils__dirs[which], &merge); - git_buf_free(&merge); - - return git_buf_oom(&git_futils__dirs[which]) ? -1 : 0; -} - -static int git_futils_find_in_dirlist( - git_buf *path, const char *name, git_futils_dir_t which, const char *label) -{ - size_t len; - const char *scan, *next = NULL; - const git_buf *syspath; - - GITERR_CHECK_ERROR(git_futils_dirs_get(&syspath, which)); - - for (scan = git_buf_cstr(syspath); scan; scan = next) { - for (next = strchr(scan, GIT_PATH_LIST_SEPARATOR); - next && next > scan && next[-1] == '\\'; - next = strchr(next + 1, GIT_PATH_LIST_SEPARATOR)) - /* find unescaped separator or end of string */; - - len = next ? (size_t)(next++ - scan) : strlen(scan); - if (!len) - continue; - - GITERR_CHECK_ERROR(git_buf_set(path, scan, len)); - if (name) - GITERR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name)); - - if (git_path_exists(path->ptr)) - return 0; - } - - git_buf_clear(path); - giterr_set(GITERR_OS, "The %s file '%s' doesn't exist", label, name); - return GIT_ENOTFOUND; -} - -int git_futils_find_system_file(git_buf *path, const char *filename) -{ - return git_futils_find_in_dirlist( - path, filename, GIT_FUTILS_DIR_SYSTEM, "system"); -} - -int git_futils_find_global_file(git_buf *path, const char *filename) -{ - return git_futils_find_in_dirlist( - path, filename, GIT_FUTILS_DIR_GLOBAL, "global"); -} - -int git_futils_find_xdg_file(git_buf *path, const char *filename) -{ - return git_futils_find_in_dirlist( - path, filename, GIT_FUTILS_DIR_XDG, "global/xdg"); -} - -int git_futils_find_template_dir(git_buf *path) -{ - return git_futils_find_in_dirlist( - path, NULL, GIT_FUTILS_DIR_TEMPLATE, "template"); -} - int git_futils_fake_symlink(const char *old, const char *new) { int retcode = GIT_ERROR; @@ -858,7 +720,6 @@ uint32_t flags; uint32_t mkdir_flags; mode_t dirmode; - int error; } cp_r_info; #define GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT (1u << 10) @@ -896,23 +757,21 @@ from->ptr[git_path_basename_offset(from)] == '.') return 0; - if (git_buf_joinpath( - &info->to, info->to_root, from->ptr + info->from_prefix) < 0) { - error = -1; - goto exit; - } + if ((error = git_buf_joinpath( + &info->to, info->to_root, from->ptr + info->from_prefix)) < 0) + return error; if (!(error = git_path_lstat(info->to.ptr, &to_st))) exists = true; else if (error != GIT_ENOTFOUND) - goto exit; + return error; else { giterr_clear(); error = 0; } if ((error = git_path_lstat(from->ptr, &from_st)) < 0) - goto exit; + return error; if (S_ISDIR(from_st.st_mode)) { mode_t oldmode = info->dirmode; @@ -926,17 +785,13 @@ error = _cp_r_mkdir(info, from); /* recurse onto target directory */ - if (!error && (!exists || S_ISDIR(to_st.st_mode))) { + if (!error && (!exists || S_ISDIR(to_st.st_mode))) error = git_path_direach(from, 0, _cp_r_callback, info); - if (error == GIT_EUSER) - error = info->error; - } - if (oldmode != 0) info->dirmode = oldmode; - goto exit; + return error; } if (exists) { @@ -946,8 +801,7 @@ if (p_unlink(info->to.ptr) < 0) { giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'", info->to.ptr); - error = -1; - goto exit; + return GIT_EEXISTS; } } @@ -960,12 +814,14 @@ /* Make container directory on demand if needed */ if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 && (error = _cp_r_mkdir(info, from)) < 0) - goto exit; + return error; /* make symlink or regular file */ - if (S_ISLNK(from_st.st_mode)) + if (info->flags & GIT_CPDIR_LINK_FILES) { + error = p_link(from->ptr, info->to.ptr); + } else if (S_ISLNK(from_st.st_mode)) { error = cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size); - else { + } else { mode_t usemode = from_st.st_mode; if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0) @@ -974,8 +830,6 @@ error = git_futils_cp(from->ptr, info->to.ptr, usemode); } -exit: - info->error = error; return error; } @@ -992,11 +846,11 @@ if (git_buf_joinpath(&path, from, "") < 0) /* ensure trailing slash */ return -1; + memset(&info, 0, sizeof(info)); info.to_root = to; info.flags = flags; info.dirmode = dirmode; info.from_prefix = path.size; - info.error = 0; git_buf_init(&info.to, 0); /* precalculate mkdir flags */ @@ -1018,9 +872,6 @@ git_buf_free(&path); git_buf_free(&info.to); - if (error == GIT_EUSER) - error = info.error; - return error; } @@ -1033,10 +884,8 @@ if (stamp == NULL) return 1; - if (p_stat(path, &st) < 0) { - giterr_set(GITERR_OS, "Could not stat '%s'", path); + if (p_stat(path, &st) < 0) return GIT_ENOTFOUND; - } if (stamp->mtime == (git_time_t)st.st_mtime && stamp->size == (git_off_t)st.st_size && @@ -1060,3 +909,16 @@ else memset(target, 0, sizeof(*target)); } + + +void git_futils_filestamp_set_from_stat( + git_futils_filestamp *stamp, struct stat *st) +{ + if (st) { + stamp->mtime = (git_time_t)st->st_mtime; + stamp->size = (git_off_t)st->st_size; + stamp->ino = (unsigned int)st->st_ino; + } else { + memset(stamp, 0, sizeof(*stamp)); + } +} diff -Nru libgit2-0.20.0/src/fileops.h libgit2-0.22.2/src/fileops.h --- libgit2-0.20.0/src/fileops.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/fileops.h 2015-03-24 16:10:45.000000000 +0000 @@ -70,6 +70,8 @@ * * GIT_MKDIR_SKIP_LAST says to leave off the last element of the path * * GIT_MKDIR_SKIP_LAST2 says to leave off the last 2 elements of the path * * GIT_MKDIR_VERIFY_DIR says confirm final item is a dir, not just EEXIST + * * GIT_MKDIR_REMOVE_FILES says to remove files and recreate dirs + * * GIT_MKDIR_REMOVE_SYMLINKS says to remove symlinks and recreate dirs * * Note that the chmod options will be executed even if the directory already * exists, unless GIT_MKDIR_EXCL is given. @@ -82,8 +84,17 @@ GIT_MKDIR_SKIP_LAST = 16, GIT_MKDIR_SKIP_LAST2 = 32, GIT_MKDIR_VERIFY_DIR = 64, + GIT_MKDIR_REMOVE_FILES = 128, + GIT_MKDIR_REMOVE_SYMLINKS = 256, } git_futils_mkdir_flags; +struct git_futils_mkdir_perfdata +{ + size_t stat_calls; + size_t mkdir_calls; + size_t chmod_calls; +}; + /** * Create a directory or entire path. * @@ -95,8 +106,15 @@ * @param base Root for relative path. These directories will never be made. * @param mode The mode to use for created directories. * @param flags Combination of the mkdir flags above. + * @param perfdata Performance data, use `git_futils_mkdir` if you don't want this data. * @return 0 on success, else error code */ +extern int git_futils_mkdir_withperf(const char *path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_perfdata *perfdata); + +/** + * Create a directory or entire path. Similar to `git_futils_mkdir_withperf` + * without performance data. + */ extern int git_futils_mkdir(const char *path, const char *base, mode_t mode, uint32_t flags); /** @@ -173,6 +191,7 @@ * - GIT_CPDIR_SIMPLE_TO_MODE: default tries to replicate the mode of the * source file to the target; with this flag, always use 0666 (or 0777 if * source has exec bits set) for target. + * - GIT_CPDIR_LINK_FILES will try to use hardlinks for the files */ typedef enum { GIT_CPDIR_CREATE_EMPTY_DIRS = (1u << 0), @@ -181,6 +200,7 @@ GIT_CPDIR_OVERWRITE = (1u << 3), GIT_CPDIR_CHMOD_DIRS = (1u << 4), GIT_CPDIR_SIMPLE_TO_MODE = (1u << 5), + GIT_CPDIR_LINK_FILES = (1u << 6), } git_futils_cpdir_flags; /** @@ -268,89 +288,6 @@ extern void git_futils_mmap_free(git_map *map); /** - * Find a "global" file (i.e. one in a user's home directory). - * - * @param path buffer to write the full path into - * @param filename name of file to find in the home directory - * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error - */ -extern int git_futils_find_global_file(git_buf *path, const char *filename); - -/** - * Find an "XDG" file (i.e. one in user's XDG config path). - * - * @param path buffer to write the full path into - * @param filename name of file to find in the home directory - * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error - */ -extern int git_futils_find_xdg_file(git_buf *path, const char *filename); - -/** - * Find a "system" file (i.e. one shared for all users of the system). - * - * @param path buffer to write the full path into - * @param filename name of file to find in the home directory - * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error - */ -extern int git_futils_find_system_file(git_buf *path, const char *filename); - -/** - * Find template directory. - * - * @param path buffer to write the full path into - * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error - */ -extern int git_futils_find_template_dir(git_buf *path); - -typedef enum { - GIT_FUTILS_DIR_SYSTEM = 0, - GIT_FUTILS_DIR_GLOBAL = 1, - GIT_FUTILS_DIR_XDG = 2, - GIT_FUTILS_DIR_TEMPLATE = 3, - GIT_FUTILS_DIR__MAX = 4, -} git_futils_dir_t; - -/** - * Configures global data for configuration file search paths. - * - * @return 0 on success, <0 on failure - */ -extern int git_futils_dirs_global_init(void); - -/** - * Get the search path for global/system/xdg files - * - * @param out pointer to git_buf containing search path - * @param which which list of paths to return - * @return 0 on success, <0 on failure - */ -extern int git_futils_dirs_get(const git_buf **out, git_futils_dir_t which); - -/** - * Get search path into a preallocated buffer - * - * @param out String buffer to write into - * @param outlen Size of string buffer - * @param which Which search path to return - * @return 0 on success, GIT_EBUFS if out is too small, <0 on other failure - */ - -extern int git_futils_dirs_get_str( - char *out, size_t outlen, git_futils_dir_t which); - -/** - * Set search paths for global/system/xdg files - * - * The first occurrence of the magic string "$PATH" in the new value will - * be replaced with the old value of the search path. - * - * @param which Which search path to modify - * @param paths New search path (separated by GIT_PATH_LIST_SEPARATOR) - * @return 0 on success, <0 on failure (allocation error) - */ -extern int git_futils_dirs_set(git_futils_dir_t which, const char *paths); - -/** * Create a "fake" symlink (text file containing the target path). * * @param new symlink file to be created @@ -375,13 +312,14 @@ * Compare stat information for file with reference info. * * This function updates the file stamp to current data for the given path - * and returns 0 if the file is up-to-date relative to the prior setting or - * 1 if the file has been changed. (This also may return GIT_ENOTFOUND if - * the file doesn't exist.) + * and returns 0 if the file is up-to-date relative to the prior setting, + * 1 if the file has been changed, or GIT_ENOTFOUND if the file doesn't + * exist. This will not call giterr_set, so you must set the error if you + * plan to return an error. * * @param stamp File stamp to be checked * @param path Path to stat and check if changed - * @return 0 if up-to-date, 1 if out-of-date, <0 on error + * @return 0 if up-to-date, 1 if out-of-date, GIT_ENOTFOUND if cannot stat */ extern int git_futils_filestamp_check( git_futils_filestamp *stamp, const char *path); @@ -400,8 +338,9 @@ git_futils_filestamp *tgt, const git_futils_filestamp *src); /** - * Free the configuration file search paths. + * Set file stamp data from stat structure */ -extern void git_futils_dirs_global_shutdown(void); +extern void git_futils_filestamp_set_from_stat( + git_futils_filestamp *stamp, struct stat *st); #endif /* INCLUDE_fileops_h__ */ diff -Nru libgit2-0.20.0/src/filter.c libgit2-0.22.2/src/filter.c --- libgit2-0.20.0/src/filter.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/filter.c 2015-03-24 16:10:45.000000000 +0000 @@ -23,6 +23,7 @@ git_oid oid; /* zero if unknown (which is likely) */ uint16_t filemode; /* zero if unknown */ git_filter_mode_t mode; + uint32_t options; }; typedef struct { @@ -37,7 +38,7 @@ }; typedef struct { - const char *filter_name; + char *filter_name; git_filter *filter; int priority; int initialized; @@ -69,11 +70,12 @@ return; git_vector_foreach(®->filters, pos, fdef) { - if (fdef->initialized && fdef->filter && fdef->filter->shutdown) { + if (fdef->filter && fdef->filter->shutdown) { fdef->filter->shutdown(fdef->filter); fdef->initialized = false; } + git__free(fdef->filter_name); git__free(fdef->attrdata); git__free(fdef); } @@ -229,6 +231,8 @@ size_t nattr = 0, nmatch = 0; git_buf attrs = GIT_BUF_INIT; + assert(name && filter); + if (filter_registry_initialize() < 0) return -1; @@ -245,7 +249,9 @@ sizeof(git_filter_def) + 2 * nattr * sizeof(char *), 1); GITERR_CHECK_ALLOC(fdef); - fdef->filter_name = name; + fdef->filter_name = git__strdup(name); + GITERR_CHECK_ALLOC(fdef->filter_name); + fdef->filter = filter; fdef->priority = priority; fdef->nattrs = nattr; @@ -255,6 +261,7 @@ filter_def_set_attrs(fdef); if (git_vector_insert(&git__filter_registry->filters, fdef) < 0) { + git__free(fdef->filter_name); git__free(fdef->attrdata); git__free(fdef); return -1; @@ -269,6 +276,8 @@ size_t pos; git_filter_def *fdef; + assert(name); + /* cannot unregister default filters */ if (!strcmp(GIT_FILTER_CRLF, name) || !strcmp(GIT_FILTER_IDENT, name)) { giterr_set(GITERR_FILTER, "Cannot unregister filter '%s'", name); @@ -287,6 +296,7 @@ fdef->initialized = false; } + git__free(fdef->filter_name); git__free(fdef->attrdata); git__free(fdef); @@ -358,6 +368,11 @@ return src->mode; } +uint32_t git_filter_source_options(const git_filter_source *src) +{ + return src->options; +} + static int filter_list_new( git_filter_list **out, const git_filter_source *src) { @@ -372,6 +387,7 @@ fl->source.repo = src->repo; fl->source.path = fl->path; fl->source.mode = src->mode; + fl->source.options = src->options; *out = fl; return 0; @@ -419,12 +435,16 @@ } int git_filter_list_new( - git_filter_list **out, git_repository *repo, git_filter_mode_t mode) + git_filter_list **out, + git_repository *repo, + git_filter_mode_t mode, + uint32_t options) { git_filter_source src = { 0 }; src.repo = repo; src.path = NULL; src.mode = mode; + src.options = options; return filter_list_new(out, &src); } @@ -433,7 +453,8 @@ git_repository *repo, git_blob *blob, /* can be NULL */ const char *path, - git_filter_mode_t mode) + git_filter_mode_t mode, + uint32_t options) { int error = 0; git_filter_list *fl = NULL; @@ -448,6 +469,7 @@ src.repo = repo; src.path = path; src.mode = mode; + src.options = options; if (blob) git_oid_cpy(&src.oid, git_blob_id(blob)); @@ -578,6 +600,9 @@ git_buf *dbuffer[2], local = GIT_BUF_INIT; unsigned int si = 0; + git_buf_sanitize(tgt); + git_buf_sanitize(src); + if (!fl) return filter_list_out_buffer_from_raw(tgt, src->ptr, src->size); @@ -613,11 +638,11 @@ /* PASSTHROUGH means filter decided not to process the buffer */ error = 0; } else if (!error) { - git_buf_shorten(dbuffer[di], 0); /* force NUL termination */ + git_buf_sanitize(dbuffer[di]); /* force NUL termination */ si = di; /* swap buffers */ } else { tgt->size = 0; - return error; + goto cleanup; } } @@ -625,9 +650,10 @@ if (si != 1) git_buf_swap(dbuffer[0], dbuffer[1]); +cleanup: git_buf_free(&local); /* don't leak if we allocated locally */ - return 0; + return error; } int git_filter_list_apply_to_file( diff -Nru libgit2-0.20.0/src/filter.h libgit2-0.22.2/src/filter.h --- libgit2-0.20.0/src/filter.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/filter.h 2015-03-24 16:10:45.000000000 +0000 @@ -10,6 +10,10 @@ #include "common.h" #include "git2/filter.h" +/* Amount of file to examine for NUL byte when checking binary-ness */ +#define GIT_FILTER_BYTES_TO_CHECK_NUL 8000 + +/* Possible CRLF values */ typedef enum { GIT_CRLF_GUESS = -1, GIT_CRLF_BINARY = 0, diff -Nru libgit2-0.20.0/src/fnmatch.c libgit2-0.22.2/src/fnmatch.c --- libgit2-0.20.0/src/fnmatch.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/fnmatch.c 2015-03-24 16:10:45.000000000 +0000 @@ -6,6 +6,40 @@ */ /* + * This file contains code originally derrived from OpenBSD fnmatch.c + * + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Guido van Rossum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. * Compares a filename or pathname to a pattern. */ @@ -30,6 +64,7 @@ const char *stringstart; char *newp; char c, test; + int recurs_flags = flags & ~FNM_PERIOD; if (recurs-- == 0) return FNM_NORES; @@ -53,9 +88,17 @@ break; case '*': c = *pattern; - /* Collapse multiple stars. */ - while (c == '*') + + /* Let '**' override PATHNAME match for this segment. + * It will be restored if/when we recurse below. + */ + if (c == '*') { + flags &= ~FNM_PATHNAME; + while (c == '*') + c = *++pattern; + if (c == '/') c = *++pattern; + } if (*string == '.' && (flags & FNM_PERIOD) && (string == stringstart || @@ -80,7 +123,7 @@ while ((test = *string) != EOS) { int e; - e = p_fnmatchx(pattern, string, flags & ~FNM_PERIOD, recurs); + e = p_fnmatchx(pattern, string, recurs_flags, recurs); if (e != FNM_NOMATCH) return e; if (test == '/' && (flags & FNM_PATHNAME)) diff -Nru libgit2-0.20.0/src/fnmatch.h libgit2-0.22.2/src/fnmatch.h --- libgit2-0.20.0/src/fnmatch.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/fnmatch.h 2015-03-24 16:10:45.000000000 +0000 @@ -1,8 +1,29 @@ /* - * Copyright (C) the libgit2 contributors. All rights reserved. + * Copyright (C) 2008 The Android Open Source Project + * 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. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * 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__ diff -Nru libgit2-0.20.0/src/global.c libgit2-0.22.2/src/global.c --- libgit2-0.20.0/src/global.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/global.c 2015-03-24 16:10:45.000000000 +0000 @@ -7,8 +7,9 @@ #include "common.h" #include "global.h" #include "hash.h" -#include "fileops.h" -#include "git2/threads.h" +#include "sysdir.h" +#include "git2/global.h" +#include "git2/sys/openssl.h" #include "thread-utils.h" @@ -16,13 +17,22 @@ #define MAX_SHUTDOWN_CB 8 -git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB]; -git_atomic git__n_shutdown_callbacks; +#ifdef GIT_SSL +# include +SSL_CTX *git__ssl_ctx; +# ifdef GIT_THREADS +static git_mutex *openssl_locks; +# endif +#endif + +static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB]; +static git_atomic git__n_shutdown_callbacks; +static git_atomic git__n_inits; void git__on_shutdown(git_global_shutdown_fn callback) { int count = git_atomic_inc(&git__n_shutdown_callbacks); - assert(count <= MAX_SHUTDOWN_CB); + assert(count <= MAX_SHUTDOWN_CB && count > 0); git__shutdown_callbacks[count - 1] = callback; } @@ -30,17 +40,108 @@ { int pos; - while ((pos = git_atomic_dec(&git__n_shutdown_callbacks)) >= 0) { - if (git__shutdown_callbacks[pos]) - git__shutdown_callbacks[pos](); + for (pos = git_atomic_get(&git__n_shutdown_callbacks); pos > 0; pos = git_atomic_dec(&git__n_shutdown_callbacks)) { + git_global_shutdown_fn cb = git__swap(git__shutdown_callbacks[pos - 1], NULL); + if (cb != NULL) + cb(); + } + +} + +#if defined(GIT_THREADS) && defined(GIT_SSL) +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); + git__free(openssl_locks); +} +#endif + +static void init_ssl(void) +{ +#ifdef GIT_SSL + long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; + + /* Older OpenSSL and MacOS OpenSSL doesn't have this */ +#ifdef SSL_OP_NO_COMPRESSION + ssl_opts |= SSL_OP_NO_COMPRESSION; +#endif + + SSL_load_error_strings(); + OpenSSL_add_ssl_algorithms(); + /* + * Load SSLv{2,3} and TLSv1 so that we can talk with servers + * which use the SSL hellos, which are often used for + * compatibility. We then disable SSL so we only allow OpenSSL + * to speak TLSv1 to perform the encryption itself. + */ + git__ssl_ctx = SSL_CTX_new(SSLv23_method()); + SSL_CTX_set_options(git__ssl_ctx, ssl_opts); + SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL); + if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx)) { + SSL_CTX_free(git__ssl_ctx); + git__ssl_ctx = NULL; } +#endif +} + +int git_openssl_set_locking(void) +{ +#ifdef GIT_SSL +# ifdef GIT_THREADS + int num_locks, i; + + num_locks = CRYPTO_num_locks(); + openssl_locks = git__calloc(num_locks, sizeof(git_mutex)); + GITERR_CHECK_ALLOC(openssl_locks); + + for (i = 0; i < num_locks; i++) { + if (git_mutex_init(&openssl_locks[i]) != 0) { + giterr_set(GITERR_SSL, "failed to initialize openssl locks"); + return -1; + } + } + + CRYPTO_set_locking_callback(openssl_locking_function); + git__on_shutdown(shutdown_ssl_locking); + return 0; +# else + giterr_set(GITERR_THREAD, "libgit2 as not built with threads"); + return -1; +# endif +#else + giterr_set(GITERR_SSL, "libgit2 was not built with OpenSSL support"); + return -1; +#endif } /** * Handle the global state with TLS * * If libgit2 is built with GIT_THREADS enabled, - * the `git_threads_init()` function must be called + * the `git_libgit2_init()` function must be called * before calling any other function of the library. * * This function allocates a TLS index (using pthreads @@ -53,7 +154,7 @@ * allocated on each thread. * * Before shutting down the library, the - * `git_threads_shutdown` method must be called to free + * `git_libgit2_shutdown` method must be called to free * the previously reserved TLS index. * * If libgit2 is built without threading support, the @@ -63,9 +164,9 @@ */ /* - * `git_threads_init()` allows subsystems to perform global setup, + * `git_libgit2_init()` allows subsystems to perform global setup, * which may take place in the global scope. An explicit memory - * fence exists at the exit of `git_threads_init()`. Without this, + * fence exists at the exit of `git_libgit2_init()`. Without this, * CPU cores are free to reorder cache invalidation of `_tls_init` * before cache invalidation of the subsystems' newly written global * state. @@ -73,10 +174,9 @@ #if defined(GIT_THREADS) && defined(GIT_WIN32) static DWORD _tls_index; -static DWORD _mutex = 0; -static DWORD _n_inits = 0; +static volatile LONG _mutex = 0; -static int synchronized_threads_init() +static int synchronized_threads_init(void) { int error; @@ -86,31 +186,33 @@ /* Initialize any other subsystems that have global state */ if ((error = git_hash_global_init()) >= 0) - error = git_futils_dirs_global_init(); + error = git_sysdir_global_init(); win32_pthread_initialize(); return error; } -int git_threads_init(void) +int git_libgit2_init(void) { - int error = 0; + int ret; /* Enter the lock */ while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); } /* Only do work on a 0 -> 1 transition of the refcount */ - if (1 == ++_n_inits) - error = synchronized_threads_init(); + if ((ret = git_atomic_inc(&git__n_inits)) == 1) { + if (synchronized_threads_init() < 0) + ret = -1; + } /* Exit the lock */ InterlockedExchange(&_mutex, 0); - return error; + return ret; } -static void synchronized_threads_shutdown() +static void synchronized_threads_shutdown(void) { /* Shut down any subsystems that have global state */ git__shutdown(); @@ -118,24 +220,28 @@ git_mutex_free(&git__mwindow_mutex); } -void git_threads_shutdown(void) +int git_libgit2_shutdown(void) { + int ret; + /* Enter the lock */ while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); } /* Only do work on a 1 -> 0 transition of the refcount */ - if (0 == --_n_inits) + if ((ret = git_atomic_dec(&git__n_inits)) == 0) synchronized_threads_shutdown(); /* Exit the lock */ InterlockedExchange(&_mutex, 0); + + return ret; } git_global_st *git__global_state(void) { void *ptr; - assert(_n_inits); + assert(git_atomic_get(&git__n_inits) > 0); if ((ptr = TlsGetValue(_tls_index)) != NULL) return ptr; @@ -153,11 +259,13 @@ static pthread_key_t _tls_key; static pthread_once_t _once_init = PTHREAD_ONCE_INIT; -static git_atomic git__n_inits; int init_error = 0; static void cb__free_status(void *st) { + git_global_st *state = (git_global_st *) st; + git__free(state->error_t.message); + git__free(st); } @@ -167,43 +275,55 @@ return; pthread_key_create(&_tls_key, &cb__free_status); + /* Initialize any other subsystems that have global state */ if ((init_error = git_hash_global_init()) >= 0) - init_error = git_futils_dirs_global_init(); + init_error = git_sysdir_global_init(); + + /* OpenSSL needs to be initialized from the main thread */ + init_ssl(); GIT_MEMORY_BARRIER; } -int git_threads_init(void) +int git_libgit2_init(void) { + int ret; + pthread_once(&_once_init, init_once); - git_atomic_inc(&git__n_inits); - return init_error; + ret = git_atomic_inc(&git__n_inits); + + return init_error ? init_error : ret; } -void git_threads_shutdown(void) +int git_libgit2_shutdown(void) { + void *ptr = NULL; pthread_once_t new_once = PTHREAD_ONCE_INIT; + int ret; - if (git_atomic_dec(&git__n_inits) > 0) return; + if ((ret = git_atomic_dec(&git__n_inits)) > 0) + return ret; /* Shut down any subsystems that have global state */ git__shutdown(); - void *ptr = pthread_getspecific(_tls_key); + ptr = pthread_getspecific(_tls_key); pthread_setspecific(_tls_key, NULL); git__free(ptr); pthread_key_delete(_tls_key); git_mutex_free(&git__mwindow_mutex); _once_init = new_once; + + return ret; } git_global_st *git__global_state(void) { void *ptr; - assert(git__n_inits.val); + assert(git_atomic_get(&git__n_inits) > 0); if ((ptr = pthread_getspecific(_tls_key)) != NULL) return ptr; @@ -221,16 +341,27 @@ static git_global_st __state; -int git_threads_init(void) +int git_libgit2_init(void) { - /* noop */ - return 0; + static int ssl_inited = 0; + + if (!ssl_inited) { + init_ssl(); + ssl_inited = 1; + } + + return git_atomic_inc(&git__n_inits); } -void git_threads_shutdown(void) +int git_libgit2_shutdown(void) { + int ret; + /* Shut down any subsystems that have global state */ - git__shutdown(); + if (ret = git_atomic_dec(&git__n_inits)) + git__shutdown(); + + return ret; } git_global_st *git__global_state(void) diff -Nru libgit2-0.20.0/src/global.h libgit2-0.22.2/src/global.h --- libgit2-0.20.0/src/global.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/global.h 2015-03-24 16:10:45.000000000 +0000 @@ -7,14 +7,21 @@ #ifndef INCLUDE_global_h__ #define INCLUDE_global_h__ +#include "common.h" #include "mwindow.h" #include "hash.h" typedef struct { git_error *last_error; git_error error_t; + char oid_fmt[GIT_OID_HEXSZ+1]; } git_global_st; +#ifdef GIT_SSL +# include +extern SSL_CTX *git__ssl_ctx; +#endif + git_global_st *git__global_state(void); extern git_mutex git__mwindow_mutex; diff -Nru libgit2-0.20.0/src/graph.c libgit2-0.22.2/src/graph.c --- libgit2-0.20.0/src/graph.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/graph.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,4 +1,3 @@ - /* * Copyright (C) the libgit2 contributors. All rights reserved. * @@ -13,9 +12,9 @@ static int interesting(git_pqueue *list, git_commit_list *roots) { unsigned int i; - /* element 0 isn't used - we need to start at 1 */ - for (i = 1; i < list->size; i++) { - git_commit_list_node *commit = list->d[i]; + + for (i = 0; i < git_pqueue_size(list); i++) { + git_commit_list_node *commit = git_pqueue_get(list, i); if ((commit->flags & STALE) == 0) return 1; } @@ -42,7 +41,7 @@ return 0; } - if (git_pqueue_init(&list, 2, git_commit_list_time_cmp) < 0) + if (git_pqueue_init(&list, 0, 2, git_commit_list_time_cmp) < 0) return -1; if (git_commit_list_parse(walk, one) < 0) @@ -59,10 +58,9 @@ /* as long as there are non-STALE commits */ while (interesting(&list, roots)) { - git_commit_list_node *commit; + git_commit_list_node *commit = git_pqueue_pop(&list); int flags; - commit = git_pqueue_pop(&list); if (commit == NULL) break; @@ -110,40 +108,37 @@ { git_commit_list_node *commit; git_pqueue pq; - int i; + int error = 0, i; *ahead = 0; *behind = 0; - if (git_pqueue_init(&pq, 2, git_commit_list_time_cmp) < 0) + if (git_pqueue_init(&pq, 0, 2, git_commit_list_time_cmp) < 0) return -1; - if (git_pqueue_insert(&pq, one) < 0) - goto on_error; - if (git_pqueue_insert(&pq, two) < 0) - goto on_error; + + if ((error = git_pqueue_insert(&pq, one)) < 0 || + (error = git_pqueue_insert(&pq, two)) < 0) + goto done; while ((commit = git_pqueue_pop(&pq)) != NULL) { if (commit->flags & RESULT || (commit->flags & (PARENT1 | PARENT2)) == (PARENT1 | PARENT2)) continue; else if (commit->flags & PARENT1) - (*behind)++; - else if (commit->flags & PARENT2) (*ahead)++; + else if (commit->flags & PARENT2) + (*behind)++; for (i = 0; i < commit->out_degree; i++) { git_commit_list_node *p = commit->parents[i]; - if (git_pqueue_insert(&pq, p) < 0) - return -1; + if ((error = git_pqueue_insert(&pq, p)) < 0) + goto done; } commit->flags |= RESULT; } +done: git_pqueue_free(&pq); - return 0; - -on_error: - git_pqueue_free(&pq); - return -1; + return error; } int git_graph_ahead_behind(size_t *ahead, size_t *behind, git_repository *repo, @@ -176,3 +171,22 @@ git_revwalk_free(walk); return -1; } + +int git_graph_descendant_of(git_repository *repo, const git_oid *commit, const git_oid *ancestor) +{ + git_oid merge_base; + int error; + + if (git_oid_equal(commit, ancestor)) + return 0; + + error = git_merge_base(&merge_base, repo, commit, ancestor); + /* No merge-base found, it's not a descendant */ + if (error == GIT_ENOTFOUND) + return 0; + + if (error < 0) + return error; + + return git_oid_equal(&merge_base, ancestor); +} diff -Nru libgit2-0.20.0/src/hash/hash_common_crypto.h libgit2-0.22.2/src/hash/hash_common_crypto.h --- libgit2-0.20.0/src/hash/hash_common_crypto.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/hash/hash_common_crypto.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,44 @@ +/* + * 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_hash_common_crypto_h__ +#define INCLUDE_hash_common_crypto_h__ + +#include "hash.h" + +#include + +struct git_hash_ctx { + CC_SHA1_CTX c; +}; + +#define git_hash_global_init() 0 +#define git_hash_ctx_init(ctx) git_hash_init(ctx) +#define git_hash_ctx_cleanup(ctx) + +GIT_INLINE(int) git_hash_init(git_hash_ctx *ctx) +{ + assert(ctx); + CC_SHA1_Init(&ctx->c); + return 0; +} + +GIT_INLINE(int) git_hash_update(git_hash_ctx *ctx, const void *data, size_t len) +{ + assert(ctx); + CC_SHA1_Update(&ctx->c, data, len); + return 0; +} + +GIT_INLINE(int) git_hash_final(git_oid *out, git_hash_ctx *ctx) +{ + assert(ctx); + CC_SHA1_Final(out->id, &ctx->c); + return 0; +} + +#endif /* INCLUDE_hash_common_crypto_h__ */ diff -Nru libgit2-0.20.0/src/hash/hash_win32.c libgit2-0.22.2/src/hash/hash_win32.c --- libgit2-0.20.0/src/hash/hash_win32.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/hash/hash_win32.c 2015-03-24 16:10:45.000000000 +0000 @@ -236,7 +236,7 @@ /* * When compiled with GIT_THREADS, the global hash_prov data is - * initialized with git_threads_init. Otherwise, it must be initialized + * initialized with git_libgit2_init. Otherwise, it must be initialized * at first use. */ if (hash_prov.type == INVALID && (error = git_hash_global_init()) < 0) diff -Nru libgit2-0.20.0/src/hash.h libgit2-0.22.2/src/hash.h --- libgit2-0.20.0/src/hash.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/hash.h 2015-03-24 16:10:45.000000000 +0000 @@ -16,7 +16,9 @@ int git_hash_ctx_init(git_hash_ctx *ctx); void git_hash_ctx_cleanup(git_hash_ctx *ctx); -#if defined(OPENSSL_SHA1) +#if defined(GIT_COMMON_CRYPTO) +# include "hash/hash_common_crypto.h" +#elif defined(OPENSSL_SHA1) # include "hash/hash_openssl.h" #elif defined(WIN32_SHA1) # include "hash/hash_win32.h" diff -Nru libgit2-0.20.0/src/hashsig.c libgit2-0.22.2/src/hashsig.c --- libgit2-0.20.0/src/hashsig.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/hashsig.c 2015-03-24 16:10:45.000000000 +0000 @@ -4,7 +4,7 @@ * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ -#include "hashsig.h" +#include "git2/sys/hashsig.h" #include "fileops.h" #include "util.h" diff -Nru libgit2-0.20.0/src/hashsig.h libgit2-0.22.2/src/hashsig.h --- libgit2-0.20.0/src/hashsig.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/hashsig.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -/* - * 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_hashsig_h__ -#define INCLUDE_hashsig_h__ - -#include "common.h" - -/** - * Similarity signature of line hashes for a buffer - */ -typedef struct git_hashsig git_hashsig; - -typedef enum { - GIT_HASHSIG_NORMAL = 0, /* use all data */ - GIT_HASHSIG_IGNORE_WHITESPACE = 1, /* ignore whitespace */ - GIT_HASHSIG_SMART_WHITESPACE = 2, /* ignore \r and all space after \n */ -} git_hashsig_option_t; - -/** - * Build a similarity signature for a buffer - * - * If you have passed a whitespace-ignoring buffer, then the whitespace - * will be removed from the buffer while it is being processed, modifying - * the buffer in place. Sorry about that! - * - * This will return an error if the buffer doesn't contain enough data to - * compute a valid signature. - * - * @param out The array of hashed runs representing the file content - * @param buf The contents of the file to hash - * @param buflen The length of the data at `buf` - * @param generate_pairwise_hashes Should pairwise runs be hashed - */ -extern int git_hashsig_create( - git_hashsig **out, - const char *buf, - size_t buflen, - git_hashsig_option_t opts); - -/** - * Build a similarity signature from a file - * - * This walks through the file, only loading a maximum of 4K of file data at - * a time. Otherwise, it acts just like `git_hashsig_create`. - * - * This will return an error if the file doesn't contain enough data to - * compute a valid signature. - */ -extern int git_hashsig_create_fromfile( - git_hashsig **out, - const char *path, - git_hashsig_option_t opts); - -/** - * Release memory for a content similarity signature - */ -extern void git_hashsig_free(git_hashsig *sig); - -/** - * Measure similarity between two files - * - * @return <0 for error, [0 to 100] as similarity score - */ -extern int git_hashsig_compare( - const git_hashsig *a, - const git_hashsig *b); - -#endif diff -Nru libgit2-0.20.0/src/ident.c libgit2-0.22.2/src/ident.c --- libgit2-0.20.0/src/ident.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/ident.c 2015-03-24 16:10:45.000000000 +0000 @@ -115,6 +115,8 @@ git_filter *git_ident_filter_new(void) { git_filter *f = git__calloc(1, sizeof(git_filter)); + if (f == NULL) + return NULL; f->version = GIT_FILTER_VERSION; f->attributes = "+ident"; /* apply to files with ident attribute set */ diff -Nru libgit2-0.20.0/src/ignore.c libgit2-0.22.2/src/ignore.c --- libgit2-0.20.0/src/ignore.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/ignore.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,47 +1,124 @@ #include "git2/ignore.h" #include "common.h" #include "ignore.h" -#include "attr.h" +#include "attrcache.h" #include "path.h" #include "config.h" +#include "fnmatch.h" #define GIT_IGNORE_INTERNAL "[internal]exclude" #define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n" +/** + * A negative ignore can only unignore a file which is given explicitly before, thus + * + * foo + * !foo/bar + * + * does not unignore 'foo/bar' as it's not in the list. However + * + * foo/ + * !foo/bar + * + * does unignore 'foo/bar', as it is contained within the 'foo/' rule. + */ +static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match) +{ + int error = 0; + size_t i; + git_attr_fnmatch *rule; + char *path; + git_buf buf = GIT_BUF_INIT; + + /* 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); + } + if (git_buf_puts(&buf, match->pattern) < 0) + return -1; + + path = git_buf_detach(&buf); + + git_vector_foreach(rules, i, rule) { + /* no chance of matching w/o a wilcard */ + if (!(rule->flags & GIT_ATTR_FNMATCH_HASWILD)) + continue; + + /* + * If we're dealing with a directory (which we know via the + * strchr() check) we want to use 'dirname/' as the + * pattern so p_fnmatch() honours FNM_PATHNAME + */ + git_buf_clear(&buf); + if (rule->containing_dir) { + git_buf_puts(&buf, rule->containing_dir); + } + if (!strchr(rule->pattern, '*')) + error = git_buf_printf(&buf, "%s/*", rule->pattern); + else + error = git_buf_puts(&buf, rule->pattern); + + if (error < 0) + goto out; + + + if ((error = p_fnmatch(git_buf_cstr(&buf), path, FNM_PATHNAME)) < 0) { + giterr_set(GITERR_INVALID, "error matching pattern"); + goto out; + } + + /* if we found a match, we want to keep this rule */ + if (error != FNM_NOMATCH) { + *out = 1; + error = 0; + goto out; + } + } + + *out = 0; + error = 0; + +out: + git__free(path); + git_buf_free(&buf); + return error; +} + static int parse_ignore_file( - git_repository *repo, void *parsedata, const char *buffer, git_attr_file *ignores) + git_repository *repo, git_attr_file *attrs, const char *data) { int error = 0; - git_attr_fnmatch *match = NULL; - const char *scan = NULL; - char *context = NULL; int ignore_case = false; + const char *scan = data, *context = NULL; + git_attr_fnmatch *match = NULL; - /* Prefer to have the caller pass in a git_ignores as the parsedata - * object. If they did not, then look up the value of ignore_case */ - if (parsedata != NULL) - ignore_case = ((git_ignores *)parsedata)->ignore_case; - else if (git_repository__cvar(&ignore_case, repo, GIT_CVAR_IGNORECASE) < 0) - return error; + if (git_repository__cvar(&ignore_case, repo, GIT_CVAR_IGNORECASE) < 0) + giterr_clear(); - if (ignores->key && git__suffixcmp(ignores->key, "/" GIT_IGNORE_FILE) == 0) { - context = ignores->key + 2; - context[strlen(context) - strlen(GIT_IGNORE_FILE)] = '\0'; - } + /* if subdir file path, convert context for file paths */ + if (attrs->entry && + git_path_root(attrs->entry->path) < 0 && + !git__suffixcmp(attrs->entry->path, "/" GIT_IGNORE_FILE)) + context = attrs->entry->path; - scan = buffer; + if (git_mutex_lock(&attrs->lock) < 0) { + giterr_set(GITERR_OS, "Failed to lock ignore file"); + return -1; + } while (!error && *scan) { - if (!match) { - match = git__calloc(1, sizeof(*match)); - GITERR_CHECK_ALLOC(match); + int valid_rule = 1; + + if (!match && !(match = git__calloc(1, sizeof(*match)))) { + error = -1; + break; } match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG; if (!(error = git_attr_fnmatch__parse( - match, ignores->pool, context, &scan))) + match, &attrs->pool, context, &scan))) { match->flags |= GIT_ATTR_FNMATCH_IGNORE; @@ -49,11 +126,16 @@ match->flags |= GIT_ATTR_FNMATCH_ICASE; scan = git__next_line(scan); - error = git_vector_insert(&ignores->rules, match); + + /* if a negative match doesn't actually do anything, throw it away */ + if (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) + error = does_negate_rule(&valid_rule, &attrs->rules, match); + + if (!error && valid_rule) + error = git_vector_insert(&attrs->rules, match); } - if (error != 0) { - git__free(match->pattern); + if (error != 0 || !valid_rule) { match->pattern = NULL; if (error == GIT_ENOTFOUND) @@ -63,32 +145,55 @@ } } + git_mutex_unlock(&attrs->lock); git__free(match); - /* restore file path used for context */ - if (context) - context[strlen(context)] = '.'; /* first char of GIT_IGNORE_FILE */ return error; } -#define push_ignore_file(R,IGN,S,B,F) \ - git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,parse_ignore_file,(IGN),(S)) +static int push_ignore_file( + git_ignores *ignores, + git_vector *which_list, + const char *base, + const char *filename) +{ + int error = 0; + git_attr_file *file = NULL; + + error = git_attr_cache__get( + &file, ignores->repo, GIT_ATTR_FILE__FROM_FILE, + base, filename, parse_ignore_file); + if (error < 0) + return error; + + if (file != NULL) { + if ((error = git_vector_insert(which_list, file)) < 0) + git_attr_file__free(file); + } + + return error; +} -static int push_one_ignore(void *ref, git_buf *path) +static int push_one_ignore(void *payload, const char *path) { - git_ignores *ign = (git_ignores *)ref; - return push_ignore_file(ign->repo, ign, &ign->ign_path, path->ptr, GIT_IGNORE_FILE); + git_ignores *ign = payload; + ign->depth++; + return push_ignore_file(ign, &ign->ign_path, path, GIT_IGNORE_FILE); } -static int get_internal_ignores(git_attr_file **ign, git_repository *repo) +static int get_internal_ignores(git_attr_file **out, git_repository *repo) { int error; - if (!(error = git_attr_cache__init(repo))) - error = git_attr_cache__internal_file(repo, GIT_IGNORE_INTERNAL, ign); + if ((error = git_attr_cache__init(repo)) < 0) + return error; - if (!error && !(*ign)->rules.length) - error = parse_ignore_file(repo, NULL, GIT_IGNORE_DEFAULT_RULES, *ign); + error = git_attr_cache__get( + out, repo, GIT_ATTR_FILE__IN_MEMORY, NULL, GIT_IGNORE_INTERNAL, NULL); + + /* if internal rules list is empty, insert default rules */ + if (!error && !(*out)->rules.length) + error = parse_ignore_file(repo, *out, GIT_IGNORE_DEFAULT_RULES); return error; } @@ -101,33 +206,32 @@ int error = 0; const char *workdir = git_repository_workdir(repo); - assert(ignores); + assert(ignores && path); + memset(ignores, 0, sizeof(*ignores)); ignores->repo = repo; - git_buf_init(&ignores->dir, 0); - ignores->ign_internal = NULL; /* Read the ignore_case flag */ if ((error = git_repository__cvar( &ignores->ignore_case, repo, GIT_CVAR_IGNORECASE)) < 0) goto cleanup; - if ((error = git_vector_init(&ignores->ign_path, 8, NULL)) < 0 || - (error = git_vector_init(&ignores->ign_global, 2, NULL)) < 0 || - (error = git_attr_cache__init(repo)) < 0) + if ((error = git_attr_cache__init(repo)) < 0) goto cleanup; /* given a unrooted path in a non-bare repo, resolve it */ if (workdir && git_path_root(path) < 0) error = git_path_find_dir(&ignores->dir, path, workdir); else - error = git_buf_sets(&ignores->dir, path); + error = git_buf_joinpath(&ignores->dir, path, ""); if (error < 0) goto cleanup; + if (workdir && !git__prefixcmp(ignores->dir.ptr, workdir)) + ignores->dir_root = strlen(workdir); + /* set up internals */ - error = get_internal_ignores(&ignores->ign_internal, repo); - if (error < 0) + if ((error = get_internal_ignores(&ignores->ign_internal, repo)) < 0) goto cleanup; /* load .gitignore up the path */ @@ -139,14 +243,16 @@ } /* load .git/info/exclude */ - error = push_ignore_file(repo, ignores, &ignores->ign_global, + error = push_ignore_file( + ignores, &ignores->ign_global, git_repository_path(repo), GIT_IGNORE_FILE_INREPO); if (error < 0) goto cleanup; /* load core.excludesfile */ if (git_repository_attr_cache(repo)->cfg_excl_file != NULL) - error = push_ignore_file(repo, ignores, &ignores->ign_global, NULL, + error = push_ignore_file( + ignores, &ignores->ign_global, NULL, git_repository_attr_cache(repo)->cfg_excl_file); cleanup: @@ -161,57 +267,79 @@ if (git_buf_joinpath(&ign->dir, ign->dir.ptr, dir) < 0) return -1; + ign->depth++; + return push_ignore_file( - ign->repo, ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE); + ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE); } int git_ignore__pop_dir(git_ignores *ign) { if (ign->ign_path.length > 0) { git_attr_file *file = git_vector_last(&ign->ign_path); - const char *start, *end, *scan; - size_t keylen; + const char *start = file->entry->path, *end; - /* - ign->dir looks something like "a/b" (or "a/b/c/d") - * - file->key looks something like "0#a/b/.gitignore + /* - ign->dir looks something like "/home/user/a/b/" (or "a/b/c/d/") + * - file->path looks something like "a/b/.gitignore * - * We are popping the last directory off ign->dir. We also want to - * remove the file from the vector if the directory part of the key - * matches the ign->dir path. We need to test if the "a/b" part of + * We are popping the last directory off ign->dir. We also want + * to remove the file from the vector if the popped directory + * matches the ignore path. We need to test if the "a/b" part of * the file key matches the path we are about to pop. */ - for (start = end = scan = &file->key[2]; *scan; ++scan) - if (*scan == '/') - end = scan; /* point 'end' to last '/' in key */ - keylen = (end - start) + 1; - - if (ign->dir.size >= keylen && - !memcmp(ign->dir.ptr + ign->dir.size - keylen, start, keylen)) - git_vector_pop(&ign->ign_path); + if ((end = strrchr(start, '/')) != NULL) { + size_t dirlen = (end - start) + 1; + const char *relpath = ign->dir.ptr + ign->dir_root; + size_t pathlen = ign->dir.size - ign->dir_root; + + if (pathlen == dirlen && !memcmp(relpath, start, dirlen)) { + git_vector_pop(&ign->ign_path); + git_attr_file__free(file); + } + } + } + if (--ign->depth > 0) { git_buf_rtruncate_at_char(&ign->dir, '/'); + git_path_to_dir(&ign->dir); } + return 0; } void git_ignore__free(git_ignores *ignores) { - /* don't need to free ignores->ign_internal since it is in cache */ + unsigned int i; + git_attr_file *file; + + git_attr_file__free(ignores->ign_internal); + + git_vector_foreach(&ignores->ign_path, i, file) { + git_attr_file__free(file); + ignores->ign_path.contents[i] = NULL; + } git_vector_free(&ignores->ign_path); + + git_vector_foreach(&ignores->ign_global, i, file) { + git_attr_file__free(file); + ignores->ign_global.contents[i] = NULL; + } git_vector_free(&ignores->ign_global); + git_buf_free(&ignores->dir); } static bool ignore_lookup_in_rules( - git_vector *rules, git_attr_path *path, int *ignored) + int *ignored, git_attr_file *file, git_attr_path *path) { size_t j; git_attr_fnmatch *match; - git_vector_rforeach(rules, j, match) { + git_vector_rforeach(&file->rules, j, match) { if (git_attr_fnmatch__match(match, path)) { - *ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0); + *ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0) ? + GIT_IGNORE_TRUE : GIT_IGNORE_FALSE; return true; } } @@ -220,66 +348,66 @@ } int git_ignore__lookup( - git_ignores *ignores, const char *pathname, int *ignored) + int *out, git_ignores *ignores, const char *pathname) { unsigned int i; git_attr_file *file; git_attr_path path; + *out = GIT_IGNORE_NOTFOUND; + if (git_attr_path__init( &path, pathname, git_repository_workdir(ignores->repo)) < 0) return -1; /* first process builtins - success means path was found */ - if (ignore_lookup_in_rules( - &ignores->ign_internal->rules, &path, ignored)) + if (ignore_lookup_in_rules(out, ignores->ign_internal, &path)) goto cleanup; /* next process files in the path */ git_vector_foreach(&ignores->ign_path, i, file) { - if (ignore_lookup_in_rules(&file->rules, &path, ignored)) + if (ignore_lookup_in_rules(out, file, &path)) goto cleanup; } /* last process global ignores */ git_vector_foreach(&ignores->ign_global, i, file) { - if (ignore_lookup_in_rules(&file->rules, &path, ignored)) + if (ignore_lookup_in_rules(out, file, &path)) goto cleanup; } - *ignored = 0; - cleanup: git_attr_path__free(&path); return 0; } -int git_ignore_add_rule( - git_repository *repo, - const char *rules) +int git_ignore_add_rule(git_repository *repo, const char *rules) { int error; - git_attr_file *ign_internal; + git_attr_file *ign_internal = NULL; + + if ((error = get_internal_ignores(&ign_internal, repo)) < 0) + return error; - if (!(error = get_internal_ignores(&ign_internal, repo))) - error = parse_ignore_file(repo, NULL, rules, ign_internal); + error = parse_ignore_file(repo, ign_internal, rules); + git_attr_file__free(ign_internal); return error; } -int git_ignore_clear_internal_rules( - git_repository *repo) +int git_ignore_clear_internal_rules(git_repository *repo) { int error; git_attr_file *ign_internal; - if (!(error = get_internal_ignores(&ign_internal, repo))) { - git_attr_file__clear_rules(ign_internal); + if ((error = get_internal_ignores(&ign_internal, repo)) < 0) + return error; - return parse_ignore_file( - repo, NULL, GIT_IGNORE_DEFAULT_RULES, ign_internal); - } + if (!(error = git_attr_file__clear_rules(ign_internal, true))) + error = parse_ignore_file( + repo, ign_internal, GIT_IGNORE_DEFAULT_RULES); + git_attr_file__free(ign_internal); return error; } @@ -291,8 +419,6 @@ int error; const char *workdir; git_attr_path path; - char *tail, *end; - bool full_is_dir; git_ignores ignores; unsigned int i; git_attr_file *file; @@ -301,56 +427,42 @@ workdir = repo ? git_repository_workdir(repo) : NULL; - if ((error = git_attr_path__init(&path, pathname, workdir)) < 0) - return error; + memset(&path, 0, sizeof(path)); + memset(&ignores, 0, sizeof(ignores)); - tail = path.path; - end = &path.full.ptr[path.full.size]; - full_is_dir = path.is_dir; + if ((error = git_attr_path__init(&path, pathname, workdir)) < 0 || + (error = git_ignore__for_path(repo, path.path, &ignores)) < 0) + goto cleanup; while (1) { - /* advance to next component of path */ - path.basename = tail; - - while (tail < end && *tail != '/') tail++; - *tail = '\0'; - - path.full.size = (tail - path.full.ptr); - path.is_dir = (tail == end) ? full_is_dir : true; - - /* initialize ignores the first time through */ - if (path.basename == path.path && - (error = git_ignore__for_path(repo, path.path, &ignores)) < 0) - break; - /* first process builtins - success means path was found */ - if (ignore_lookup_in_rules( - &ignores.ign_internal->rules, &path, ignored)) + if (ignore_lookup_in_rules(ignored, ignores.ign_internal, &path)) goto cleanup; /* next process files in the path */ git_vector_foreach(&ignores.ign_path, i, file) { - if (ignore_lookup_in_rules(&file->rules, &path, ignored)) + if (ignore_lookup_in_rules(ignored, file, &path)) goto cleanup; } /* last process global ignores */ git_vector_foreach(&ignores.ign_global, i, file) { - if (ignore_lookup_in_rules(&file->rules, &path, ignored)) + if (ignore_lookup_in_rules(ignored, file, &path)) goto cleanup; } - /* if we found no rules before reaching the end, we're done */ - if (tail == end) + /* move up one directory */ + if (path.basename == path.path) break; + path.basename[-1] = '\0'; + while (path.basename > path.path && *path.basename != '/') + path.basename--; + if (path.basename > path.path) + path.basename++; + path.is_dir = 1; - /* now add this directory to list of ignores */ - if ((error = git_ignore__push_dir(&ignores, path.path)) < 0) + if ((error = git_ignore__pop_dir(&ignores)) < 0) break; - - /* reinstate divider in path */ - *tail = '/'; - while (*tail == '/') tail++; } *ignored = 0; @@ -361,7 +473,6 @@ return error; } - int git_ignore__check_pathspec_for_exact_ignores( git_repository *repo, git_vector *vspec, diff -Nru libgit2-0.20.0/src/ignore.h libgit2-0.22.2/src/ignore.h --- libgit2-0.20.0/src/ignore.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/ignore.h 2015-03-24 16:10:45.000000000 +0000 @@ -28,7 +28,9 @@ git_attr_file *ign_internal; git_vector ign_path; git_vector ign_global; + size_t dir_root; /* offset in dir to repo root */ int ignore_case; + int depth; } git_ignores; extern int git_ignore__for_path( @@ -40,7 +42,14 @@ extern void git_ignore__free(git_ignores *ign); -extern int git_ignore__lookup(git_ignores *ign, const char *path, int *ignored); +enum { + GIT_IGNORE_UNCHECKED = -2, + GIT_IGNORE_NOTFOUND = -1, + GIT_IGNORE_FALSE = 0, + GIT_IGNORE_TRUE = 1, +}; + +extern int git_ignore__lookup(int *out, git_ignores *ign, const char *path); /* command line Git sometimes generates an error message if given a * pathspec that contains an exact match to an ignored file (provided diff -Nru libgit2-0.20.0/src/index.c libgit2-0.22.2/src/index.c --- libgit2-0.20.0/src/index.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/index.c 2015-03-24 16:10:45.000000000 +0000 @@ -90,12 +90,24 @@ struct entry_srch_key { const char *path; + size_t pathlen; int stage; }; +struct entry_internal { + git_index_entry entry; + size_t pathlen; + char path[GIT_FLEX_ARRAY]; +}; + +struct reuc_entry_internal { + git_index_reuc_entry entry; + size_t pathlen; + char path[GIT_FLEX_ARRAY]; +}; + /* local declarations */ static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size); -static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size); static int read_header(struct index_header *dest, const void *buffer); static int parse_index(git_index *index, const char *buffer, size_t buffer_size); @@ -105,59 +117,72 @@ static void index_entry_free(git_index_entry *entry); static void index_entry_reuc_free(git_index_reuc_entry *reuc); -static int index_srch(const void *key, const void *array_member) +int git_index_entry_srch(const void *key, const void *array_member) { const struct entry_srch_key *srch_key = key; - const git_index_entry *entry = array_member; - int ret; - - ret = strcmp(srch_key->path, entry->path); + const struct entry_internal *entry = array_member; + int cmp; + size_t len1, len2, len; + + len1 = srch_key->pathlen; + len2 = entry->pathlen; + len = len1 < len2 ? len1 : len2; + + cmp = memcmp(srch_key->path, entry->path, len); + if (cmp) + return cmp; + if (len1 < len2) + return -1; + if (len1 > len2) + return 1; - if (ret == 0 && srch_key->stage != GIT_INDEX_STAGE_ANY) - ret = srch_key->stage - GIT_IDXENTRY_STAGE(entry); + if (srch_key->stage != GIT_INDEX_STAGE_ANY) + return srch_key->stage - GIT_IDXENTRY_STAGE(&entry->entry); - return ret; + return 0; } -static int index_isrch(const void *key, const void *array_member) +int git_index_entry_isrch(const void *key, const void *array_member) { const struct entry_srch_key *srch_key = key; - const git_index_entry *entry = array_member; - int ret; - - ret = strcasecmp(srch_key->path, entry->path); - - if (ret == 0 && srch_key->stage != GIT_INDEX_STAGE_ANY) - ret = srch_key->stage - GIT_IDXENTRY_STAGE(entry); - - return ret; -} + const struct entry_internal *entry = array_member; + int cmp; + size_t len1, len2, len; + + len1 = srch_key->pathlen; + len2 = entry->pathlen; + len = len1 < len2 ? len1 : len2; + + cmp = strncasecmp(srch_key->path, entry->path, len); + + if (cmp) + return cmp; + if (len1 < len2) + return -1; + if (len1 > len2) + return 1; -static int index_cmp_path(const void *a, const void *b) -{ - return strcmp((const char *)a, (const char *)b); -} + if (srch_key->stage != GIT_INDEX_STAGE_ANY) + return srch_key->stage - GIT_IDXENTRY_STAGE(&entry->entry); -static int index_icmp_path(const void *a, const void *b) -{ - return strcasecmp((const char *)a, (const char *)b); + return 0; } -static int index_srch_path(const void *path, const void *array_member) +static int index_entry_srch_path(const void *path, const void *array_member) { const git_index_entry *entry = array_member; return strcmp((const char *)path, entry->path); } -static int index_isrch_path(const void *path, const void *array_member) +static int index_entry_isrch_path(const void *path, const void *array_member) { const git_index_entry *entry = array_member; return strcasecmp((const char *)path, entry->path); } -static int index_cmp(const void *a, const void *b) +int git_index_entry_cmp(const void *a, const void *b) { int diff; const git_index_entry *entry_a = a; @@ -171,7 +196,7 @@ return diff; } -static int index_icmp(const void *a, const void *b) +int git_index_entry_icmp(const void *a, const void *b) { int diff; const git_index_entry *entry_a = a; @@ -262,9 +287,6 @@ static void index_entry_reuc_free(git_index_reuc_entry *reuc) { - if (!reuc) - return; - git__free(reuc->path); git__free(reuc); } @@ -272,11 +294,12 @@ { if (!entry) return; - git__free(entry->path); + + memset(&entry->id, 0, sizeof(entry->id)); git__free(entry); } -static unsigned int index_create_mode(unsigned int mode) +unsigned int git_index__create_mode(unsigned int mode) { if (S_ISLNK(mode)) return S_IFLNK; @@ -296,23 +319,74 @@ if (index->distrust_filemode && S_ISREG(mode)) return (existing && S_ISREG(existing->mode)) ? - existing->mode : index_create_mode(0666); + existing->mode : git_index__create_mode(0666); - return index_create_mode(mode); + return git_index__create_mode(mode); } -void git_index__set_ignore_case(git_index *index, bool ignore_case) +static int index_sort_if_needed(git_index *index, bool need_lock) { - index->ignore_case = ignore_case; + /* not truly threadsafe because between when this checks and/or + * sorts the array another thread could come in and unsort it + */ - index->entries_cmp_path = ignore_case ? index_icmp_path : index_cmp_path; - index->entries_search = ignore_case ? index_isrch : index_srch; - index->entries_search_path = ignore_case ? index_isrch_path : index_srch_path; + if (git_vector_is_sorted(&index->entries)) + return 0; + + if (need_lock && git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Unable to lock index"); + return -1; + } - git_vector_set_cmp(&index->entries, ignore_case ? index_icmp : index_cmp); git_vector_sort(&index->entries); - index->reuc_search = ignore_case ? reuc_isrch : reuc_srch; + if (need_lock) + git_mutex_unlock(&index->lock); + + return 0; +} + +GIT_INLINE(int) index_find_in_entries( + size_t *out, git_vector *entries, git_vector_cmp entry_srch, + const char *path, size_t path_len, int stage) +{ + struct entry_srch_key srch_key; + srch_key.path = path; + srch_key.pathlen = !path_len ? strlen(path) : path_len; + srch_key.stage = stage; + return git_vector_bsearch2(out, entries, entry_srch, &srch_key); +} + +GIT_INLINE(int) index_find( + size_t *out, git_index *index, + const char *path, size_t path_len, int stage, bool need_lock) +{ + if (index_sort_if_needed(index, need_lock) < 0) + return -1; + + return index_find_in_entries( + out, &index->entries, index->entries_search, path, path_len, stage); +} + +void git_index__set_ignore_case(git_index *index, bool ignore_case) +{ + index->ignore_case = ignore_case; + + if (ignore_case) { + index->entries_cmp_path = git__strcasecmp_cb; + index->entries_search = git_index_entry_isrch; + index->entries_search_path = index_entry_isrch_path; + index->reuc_search = reuc_isrch; + } else { + index->entries_cmp_path = git__strcmp_cb; + index->entries_search = git_index_entry_srch; + index->entries_search_path = index_entry_srch_path; + index->reuc_search = reuc_srch; + } + + git_vector_set_cmp(&index->entries, + ignore_case ? git_index_entry_icmp : git_index_entry_cmp); + index_sort_if_needed(index, true); git_vector_set_cmp(&index->reuc, ignore_case ? reuc_icmp : reuc_cmp); git_vector_sort(&index->reuc); @@ -321,41 +395,54 @@ int git_index_open(git_index **index_out, const char *index_path) { git_index *index; - int error; + int error = -1; assert(index_out); index = git__calloc(1, sizeof(git_index)); GITERR_CHECK_ALLOC(index); + if (git_mutex_init(&index->lock)) { + giterr_set(GITERR_OS, "Failed to initialize lock"); + git__free(index); + return -1; + } + + git_pool_init(&index->tree_pool, 1, 0); + if (index_path != NULL) { index->index_file_path = git__strdup(index_path); - GITERR_CHECK_ALLOC(index->index_file_path); + if (!index->index_file_path) + goto fail; /* Check if index file is stored on disk already */ if (git_path_exists(index->index_file_path) == true) index->on_disk = 1; } - if (git_vector_init(&index->entries, 32, index_cmp) < 0 || - git_vector_init(&index->names, 32, conflict_name_cmp) < 0 || - git_vector_init(&index->reuc, 32, reuc_cmp) < 0) - return -1; - - index->entries_cmp_path = index_cmp_path; - index->entries_search = index_srch; - index->entries_search_path = index_srch_path; + if (git_vector_init(&index->entries, 32, git_index_entry_cmp) < 0 || + git_vector_init(&index->names, 8, conflict_name_cmp) < 0 || + git_vector_init(&index->reuc, 8, reuc_cmp) < 0 || + git_vector_init(&index->deleted, 8, git_index_entry_cmp) < 0) + goto fail; + + index->entries_cmp_path = git__strcmp_cb; + index->entries_search = git_index_entry_srch; + index->entries_search_path = index_entry_srch_path; index->reuc_search = reuc_srch; - if ((index_path != NULL) && ((error = git_index_read(index, true)) < 0)) { - git_index_free(index); - return error; - } + if (index_path != NULL && (error = git_index_read(index, true)) < 0) + goto fail; *index_out = index; GIT_REFCOUNT_INC(index); return 0; + +fail: + git_pool_clear(&index->tree_pool); + git_index_free(index); + return error; } int git_index_new(git_index **out) @@ -365,12 +452,19 @@ static void index_free(git_index *index) { + /* index iterators increment the refcount of the index, so if we + * get here then there should be no outstanding iterators. + */ + assert(!git_atomic_get(&index->readers)); + git_index_clear(index); git_vector_free(&index->entries); git_vector_free(&index->names); git_vector_free(&index->reuc); + git_vector_free(&index->deleted); git__free(index->index_file_path); + git_mutex_free(&index->lock); git__memzero(index, sizeof(*index)); git__free(index); @@ -384,28 +478,71 @@ GIT_REFCOUNT_DEC(index, index_free); } -static void index_entries_free(git_vector *entries) +/* call with locked index */ +static void index_free_deleted(git_index *index) { + int readers = (int)git_atomic_get(&index->readers); size_t i; - for (i = 0; i < entries->length; ++i) - index_entry_free(git__swap(entries->contents[i], NULL)); + if (readers > 0 || !index->deleted.length) + return; + + for (i = 0; i < index->deleted.length; ++i) { + git_index_entry *ie = git__swap(index->deleted.contents[i], NULL); + index_entry_free(ie); + } - git_vector_clear(entries); + git_vector_clear(&index->deleted); } -void git_index_clear(git_index *index) +/* call with locked index */ +static int index_remove_entry(git_index *index, size_t pos) { + int error = 0; + git_index_entry *entry = git_vector_get(&index->entries, pos); + + if (entry != NULL) + git_tree_cache_invalidate_path(index->tree, entry->path); + + error = git_vector_remove(&index->entries, pos); + + if (!error) { + if (git_atomic_get(&index->readers) > 0) { + error = git_vector_insert(&index->deleted, entry); + } else { + index_entry_free(entry); + } + } + + return error; +} + +int git_index_clear(git_index *index) +{ + int error = 0; + assert(index); - index_entries_free(&index->entries); + index->tree = NULL; + git_pool_clear(&index->tree_pool); + + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Failed to lock index"); + return -1; + } + + while (!error && index->entries.length > 0) + error = index_remove_entry(index, index->entries.length - 1); + index_free_deleted(index); + git_index_reuc_clear(index); git_index_name_clear(index); git_futils_filestamp_set(&index->stamp, NULL); - git_tree_cache_free(index->tree); - index->tree = NULL; + git_mutex_unlock(&index->lock); + + return error; } static int create_index_error(int error, const char *msg) @@ -414,7 +551,7 @@ return error; } -int git_index_set_caps(git_index *index, unsigned int caps) +int git_index_set_caps(git_index *index, int caps) { unsigned int old_ignore_case; @@ -450,7 +587,7 @@ return 0; } -unsigned int git_index_caps(const git_index *index) +int git_index_caps(const git_index *index) { return ((index->ignore_case ? GIT_INDEXCAP_IGNORE_CASE : 0) | (index->distrust_filemode ? GIT_INDEXCAP_NO_FILEMODE : 0) | @@ -471,20 +608,32 @@ if (!index->on_disk) { if (force) - git_index_clear(index); + return git_index_clear(index); return 0; } updated = git_futils_filestamp_check(&stamp, index->index_file_path); - if (updated < 0 || (!updated && !force)) + if (updated < 0) { + giterr_set( + GITERR_INDEX, + "Failed to read index: '%s' no longer exists", + index->index_file_path); return updated; + } + if (!updated && !force) + return 0; error = git_futils_readbuffer(&buffer, index->index_file_path); if (error < 0) return error; - git_index_clear(index); - error = parse_index(index, buffer.ptr, buffer.size); + index->tree = NULL; + git_pool_clear(&index->tree_pool); + + error = git_index_clear(index); + + if (!error) + error = parse_index(index, buffer.ptr, buffer.size); if (!error) git_futils_filestamp_set(&index->stamp, &stamp); @@ -493,6 +642,18 @@ return error; } +int git_index__changed_relative_to( + git_index *index, const git_futils_filestamp *fs) +{ + /* attempt to update index (ignoring errors) */ + if (git_index_read(index, false) < 0) + giterr_clear(); + + return (index->stamp.mtime != fs->mtime || + index->stamp.size != fs->size || + index->stamp.ino != fs->ino); +} + int git_index_write(git_index *index) { git_filebuf file = GIT_FILEBUF_INIT; @@ -502,13 +663,14 @@ return create_index_error(-1, "Failed to read index: The index is in-memory only"); - git_vector_sort(&index->entries); + if (index_sort_if_needed(index, true) < 0) + return -1; git_vector_sort(&index->reuc); if ((error = git_filebuf_open( &file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_INDEX_FILE_MODE)) < 0) { if (error == GIT_ELOCKED) - giterr_set(GITERR_INDEX, "The index is locked. This might be due to a concurrrent or crashed process"); + giterr_set(GITERR_INDEX, "The index is locked. This might be due to a concurrent or crashed process"); return error; } @@ -521,15 +683,15 @@ if ((error = git_filebuf_commit(&file)) < 0) return error; - error = git_futils_filestamp_check(&index->stamp, index->index_file_path); - if (error < 0) - return error; + if (git_futils_filestamp_check(&index->stamp, index->index_file_path) < 0) + /* index could not be read from disk! */; + else + index->on_disk = 1; - index->on_disk = 1; return 0; } -const char * git_index_path(git_index *index) +const char * git_index_path(const git_index *index) { assert(index); return index->index_file_path; @@ -550,7 +712,8 @@ return git_tree__write_index(oid, index, repo); } -int git_index_write_tree_to(git_oid *oid, git_index *index, git_repository *repo) +int git_index_write_tree_to( + git_oid *oid, git_index *index, git_repository *repo) { assert(oid && index && repo); return git_tree__write_index(oid, index, repo); @@ -566,7 +729,8 @@ git_index *index, size_t n) { assert(index); - git_vector_sort(&index->entries); + if (index_sort_if_needed(index, true) < 0) + return NULL; return git_vector_get(&index->entries, n); } @@ -577,9 +741,7 @@ assert(index); - git_vector_sort(&index->entries); - - if (git_index__find(&pos, index, path, stage) < 0) { + if (index_find(&pos, index, path, 0, stage, true) < 0) { giterr_set(GITERR_INDEX, "Index does not contain %s", path); return NULL; } @@ -597,30 +759,41 @@ entry->dev = st->st_rdev; entry->ino = st->st_ino; entry->mode = (!trust_mode && S_ISREG(st->st_mode)) ? - index_create_mode(0666) : index_create_mode(st->st_mode); + git_index__create_mode(0666) : git_index__create_mode(st->st_mode); entry->uid = st->st_uid; entry->gid = st->st_gid; entry->file_size = st->st_size; } -int git_index_entry__cmp(const void *a, const void *b) +static int index_entry_create( + git_index_entry **out, + git_repository *repo, + const char *path) { - const git_index_entry *entry_a = a; - const git_index_entry *entry_b = b; + size_t pathlen = strlen(path); + struct entry_internal *entry; - return strcmp(entry_a->path, entry_b->path); -} + if (!git_path_isvalid(repo, path, + GIT_PATH_REJECT_DEFAULTS | GIT_PATH_REJECT_DOT_GIT)) { + giterr_set(GITERR_INDEX, "Invalid path: '%s'", path); + return -1; + } -int git_index_entry__cmp_icase(const void *a, const void *b) -{ - const git_index_entry *entry_a = a; - const git_index_entry *entry_b = b; + entry = git__calloc(sizeof(struct entry_internal) + pathlen + 1, 1); + GITERR_CHECK_ALLOC(entry); - return strcasecmp(entry_a->path, entry_b->path); + entry->pathlen = pathlen; + memcpy(entry->path, path, pathlen); + entry->entry.path = entry->path; + + *out = (git_index_entry *)entry; + return 0; } static int index_entry_init( - git_index_entry **entry_out, git_index *index, const char *rel_path) + git_index_entry **entry_out, + git_index *index, + const char *rel_path) { int error = 0; git_index_entry *entry = NULL; @@ -632,25 +805,40 @@ "Could not initialize index entry. " "Index is not backed up by an existing repository."); + if (index_entry_create(&entry, INDEX_OWNER(index), rel_path) < 0) + return -1; + /* write the blob to disk and get the oid and stat info */ error = git_blob__create_from_paths( &oid, &st, INDEX_OWNER(index), NULL, rel_path, 0, true); - if (error < 0) - return error; - entry = git__calloc(1, sizeof(git_index_entry)); - GITERR_CHECK_ALLOC(entry); + if (error < 0) { + index_entry_free(entry); + return error; + } + entry->id = oid; git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode); - entry->oid = oid; - entry->path = git__strdup(rel_path); - GITERR_CHECK_ALLOC(entry->path); - - *entry_out = entry; + *entry_out = (git_index_entry *)entry; return 0; } +static git_index_reuc_entry *reuc_entry_alloc(const char *path) +{ + size_t pathlen = strlen(path); + struct reuc_entry_internal *entry = + git__calloc(sizeof(struct reuc_entry_internal) + pathlen + 1, 1); + if (!entry) + return NULL; + + entry->pathlen = pathlen; + memcpy(entry->path, path, pathlen); + entry->entry.path = entry->path; + + return (git_index_reuc_entry *)entry; +} + static int index_entry_reuc_init(git_index_reuc_entry **reuc_out, const char *path, int ancestor_mode, const git_oid *ancestor_oid, @@ -661,15 +849,9 @@ assert(reuc_out && path); - *reuc_out = NULL; - - reuc = git__calloc(1, sizeof(git_index_reuc_entry)); + *reuc_out = reuc = reuc_entry_alloc(path); GITERR_CHECK_ALLOC(reuc); - reuc->path = git__strdup(path); - if (reuc->path == NULL) - return -1; - if ((reuc->mode[0] = ancestor_mode) > 0) git_oid_cpy(&reuc->oid[0], ancestor_oid); @@ -679,37 +861,161 @@ if ((reuc->mode[2] = their_mode) > 0) git_oid_cpy(&reuc->oid[2], their_oid); - *reuc_out = reuc; return 0; } -static git_index_entry *index_entry_dup(const git_index_entry *source_entry) +static void index_entry_cpy(git_index_entry *tgt, const git_index_entry *src) +{ + const char *tgt_path = tgt->path; + memcpy(tgt, src, sizeof(*tgt)); + tgt->path = tgt_path; /* reset to existing path data */ +} + +static int index_entry_dup( + git_index_entry **out, + git_repository *repo, + const git_index_entry *src) { git_index_entry *entry; - entry = git__malloc(sizeof(git_index_entry)); - if (!entry) - return NULL; + if (!src) { + *out = NULL; + return 0; + } - memcpy(entry, source_entry, sizeof(git_index_entry)); + if (index_entry_create(&entry, repo, src->path) < 0) + return -1; - /* duplicate the path string so we own it */ - entry->path = git__strdup(entry->path); - if (!entry->path) - return NULL; + index_entry_cpy(entry, src); + *out = entry; + return 0; +} - return entry; +static int has_file_name(git_index *index, + const git_index_entry *entry, size_t pos, int ok_to_replace) +{ + int retval = 0; + size_t len = strlen(entry->path); + int stage = GIT_IDXENTRY_STAGE(entry); + const char *name = entry->path; + + while (pos < index->entries.length) { + struct entry_internal *p = index->entries.contents[pos++]; + + if (len >= p->pathlen) + break; + if (memcmp(name, p->path, len)) + break; + if (GIT_IDXENTRY_STAGE(&p->entry) != stage) + continue; + if (p->path[len] != '/') + continue; + retval = -1; + if (!ok_to_replace) + break; + + if (index_remove_entry(index, --pos) < 0) + break; + } + return retval; +} + +/* + * Do we have another file with a pathname that is a proper + * subset of the name we're trying to add? + */ +static int has_dir_name(git_index *index, + const git_index_entry *entry, int ok_to_replace) +{ + int retval = 0; + int stage = GIT_IDXENTRY_STAGE(entry); + const char *name = entry->path; + const char *slash = name + strlen(name); + + for (;;) { + size_t len, pos; + + for (;;) { + if (*--slash == '/') + break; + if (slash <= entry->path) + return retval; + } + len = slash - name; + + if (!index_find(&pos, index, name, len, stage, false)) { + retval = -1; + if (!ok_to_replace) + break; + + if (index_remove_entry(index, pos) < 0) + break; + continue; + } + + /* + * Trivial optimization: if we find an entry that + * already matches the sub-directory, then we know + * we're ok, and we can exit. + */ + for (; pos < index->entries.length; ++pos) { + struct entry_internal *p = index->entries.contents[pos]; + + if (p->pathlen <= len || + p->path[len] != '/' || + memcmp(p->path, name, len)) + break; /* not our subdirectory */ + + if (GIT_IDXENTRY_STAGE(&p->entry) == stage) + return retval; + } + } + + return retval; +} + +static int check_file_directory_collision(git_index *index, + git_index_entry *entry, size_t pos, int ok_to_replace) +{ + int retval = has_file_name(index, entry, pos, ok_to_replace); + retval = retval + has_dir_name(index, entry, ok_to_replace); + + if (retval) { + giterr_set(GITERR_INDEX, + "'%s' appears as both a file and a directory", entry->path); + return -1; + } + + return 0; } -static int index_insert(git_index *index, git_index_entry *entry, int replace) +static int index_no_dups(void **old, void *new) { + const git_index_entry *entry = new; + GIT_UNUSED(old); + giterr_set(GITERR_INDEX, "'%s' appears multiple times at stage %d", + entry->path, GIT_IDXENTRY_STAGE(entry)); + return GIT_EEXISTS; +} + +/* index_insert takes ownership of the new entry - if it can't insert + * it, then it will return an error **and also free the entry**. When + * it replaces an existing entry, it will update the entry_ptr with the + * actual entry in the index (and free the passed in one). + */ +static int index_insert( + git_index *index, git_index_entry **entry_ptr, int replace) +{ + int error = 0; size_t path_length, position; - git_index_entry **existing = NULL; + git_index_entry *existing = NULL, *entry; + + assert(index && entry_ptr); - assert(index && entry && entry->path != NULL); + entry = *entry_ptr; /* make sure that the path length flag is correct */ - path_length = strlen(entry->path); + path_length = ((struct entry_internal *)entry)->pathlen; entry->flags &= ~GIT_IDXENTRY_NAMEMASK; @@ -718,28 +1024,51 @@ else entry->flags |= GIT_IDXENTRY_NAMEMASK; - /* look if an entry with this path already exists */ - if (!git_index__find( - &position, index, entry->path, GIT_IDXENTRY_STAGE(entry))) { - existing = (git_index_entry **)&index->entries.contents[position]; + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Unable to acquire index lock"); + return -1; + } + + git_vector_sort(&index->entries); + /* look if an entry with this path already exists */ + if (!index_find( + &position, index, entry->path, 0, GIT_IDXENTRY_STAGE(entry), false)) { + existing = index->entries.contents[position]; /* update filemode to existing values if stat is not trusted */ - entry->mode = index_merge_mode(index, *existing, entry->mode); + entry->mode = index_merge_mode(index, existing, entry->mode); } - /* if replacing is not requested or no existing entry exists, just - * insert entry at the end; the index is no longer sorted + /* look for tree / blob name collisions, removing conflicts if requested */ + error = check_file_directory_collision(index, entry, position, replace); + if (error < 0) + /* skip changes */; + + /* if we are replacing an existing item, overwrite the existing entry + * and return it in place of the passed in one. */ - if (!replace || !existing) - return git_vector_insert(&index->entries, entry); + else if (existing) { + if (replace) + index_entry_cpy(existing, entry); + index_entry_free(entry); + *entry_ptr = entry = existing; + } + else { + /* if replace is not requested or no existing entry exists, insert + * at the sorted position. (Since we re-sort after each insert to + * check for dups, this is actually cheaper in the long run.) + */ + error = git_vector_insert_sorted(&index->entries, entry, index_no_dups); + } - /* exists, replace it (preserving name from existing entry) */ - git__free(entry->path); - entry->path = (*existing)->path; - git__free(*existing); - *existing = entry; + if (error < 0) { + index_entry_free(*entry_ptr); + *entry_ptr = NULL; + } - return 0; + git_mutex_unlock(&index->lock); + + return error; } static int index_conflict_to_reuc(git_index *index, const char *path) @@ -757,9 +1086,9 @@ our_mode = conflict_entries[1] == NULL ? 0 : conflict_entries[1]->mode; their_mode = conflict_entries[2] == NULL ? 0 : conflict_entries[2]->mode; - ancestor_oid = conflict_entries[0] == NULL ? NULL : &conflict_entries[0]->oid; - our_oid = conflict_entries[1] == NULL ? NULL : &conflict_entries[1]->oid; - their_oid = conflict_entries[2] == NULL ? NULL : &conflict_entries[2]->oid; + ancestor_oid = conflict_entries[0] == NULL ? NULL : &conflict_entries[0]->id; + our_oid = conflict_entries[1] == NULL ? NULL : &conflict_entries[1]->id; + their_oid = conflict_entries[2] == NULL ? NULL : &conflict_entries[2]->id; if ((ret = git_index_reuc_add(index, path, ancestor_mode, ancestor_oid, our_mode, our_oid, their_mode, their_oid)) >= 0) @@ -776,19 +1105,15 @@ assert(index && path); if ((ret = index_entry_init(&entry, index, path)) < 0 || - (ret = index_insert(index, entry, 1)) < 0) - goto on_error; + (ret = index_insert(index, &entry, 1)) < 0) + return ret; /* Adding implies conflict was resolved, move conflict entries to REUC */ if ((ret = index_conflict_to_reuc(index, path)) < 0 && ret != GIT_ENOTFOUND) - goto on_error; + return ret; git_tree_cache_invalidate_path(index->tree, entry->path); return 0; - -on_error: - index_entry_free(entry); - return ret; } int git_index_remove_bypath(git_index *index, const char *path) @@ -806,19 +1131,30 @@ return 0; } +static bool valid_filemode(const int filemode) +{ + return (filemode == GIT_FILEMODE_BLOB || + filemode == GIT_FILEMODE_BLOB_EXECUTABLE || + filemode == GIT_FILEMODE_LINK || + filemode == GIT_FILEMODE_COMMIT); +} + + int git_index_add(git_index *index, const git_index_entry *source_entry) { git_index_entry *entry = NULL; int ret; - entry = index_entry_dup(source_entry); - if (entry == NULL) + assert(index && source_entry && source_entry->path); + + if (!valid_filemode(source_entry->mode)) { + giterr_set(GITERR_INDEX, "invalid filemode"); return -1; + } - if ((ret = index_insert(index, entry, 1)) < 0) { - index_entry_free(entry); + if ((ret = index_entry_dup(&entry, INDEX_OWNER(index), source_entry)) < 0 || + (ret = index_insert(index, &entry, 1)) < 0) return ret; - } git_tree_cache_invalidate_path(index->tree, entry->path); return 0; @@ -826,27 +1162,23 @@ int git_index_remove(git_index *index, const char *path, int stage) { - size_t position; int error; - git_index_entry *entry; - - git_vector_sort(&index->entries); + size_t position; - if (git_index__find(&position, index, path, stage) < 0) { - giterr_set(GITERR_INDEX, "Index does not contain %s at stage %d", - path, stage); - return GIT_ENOTFOUND; + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Failed to lock index"); + return -1; } - entry = git_vector_get(&index->entries, position); - if (entry != NULL) - git_tree_cache_invalidate_path(index->tree, entry->path); - - error = git_vector_remove(&index->entries, position); - - if (!error) - index_entry_free(entry); + if (index_find(&position, index, path, 0, stage, false) < 0) { + giterr_set( + GITERR_INDEX, "Index does not contain %s at stage %d", path, stage); + error = GIT_ENOTFOUND; + } else { + error = index_remove_entry(index, position); + } + git_mutex_unlock(&index->lock); return error; } @@ -857,14 +1189,16 @@ size_t pos; git_index_entry *entry; - if (git_buf_sets(&pfx, dir) < 0 || git_path_to_dir(&pfx) < 0) + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Failed to lock index"); return -1; + } - git_vector_sort(&index->entries); - - pos = git_index__prefix_position(index, pfx.ptr); + if (!(error = git_buf_sets(&pfx, dir)) && + !(error = git_path_to_dir(&pfx))) + index_find(&pos, index, pfx.ptr, pfx.size, GIT_INDEX_STAGE_ANY, false); - while (1) { + while (!error) { entry = git_vector_get(&index->entries, pos); if (!entry || git__prefixcmp(entry->path, pfx.ptr) != 0) break; @@ -874,32 +1208,22 @@ continue; } - git_tree_cache_invalidate_path(index->tree, entry->path); - - if ((error = git_vector_remove(&index->entries, pos)) < 0) - break; - index_entry_free(entry); + error = index_remove_entry(index, pos); - /* removed entry at 'pos' so we don't need to increment it */ + /* removed entry at 'pos' so we don't need to increment */ } + git_mutex_unlock(&index->lock); git_buf_free(&pfx); return error; } -int git_index__find( - size_t *at_pos, git_index *index, const char *path, int stage) +int git_index__find_pos( + size_t *out, git_index *index, const char *path, size_t path_len, int stage) { - struct entry_srch_key srch_key; - - assert(path); - - srch_key.path = path; - srch_key.stage = stage; - - return git_vector_bsearch2( - at_pos, &index->entries, index->entries_search, &srch_key); + assert(index && path); + return index_find(out, index, path, path_len, stage, true); } int git_index_find(size_t *at_pos, git_index *index, const char *path) @@ -908,7 +1232,14 @@ assert(index && path); - if (git_vector_bsearch2(&pos, &index->entries, index->entries_search_path, path) < 0) { + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Failed to lock index"); + return -1; + } + + if (git_vector_bsearch2( + &pos, &index->entries, index->entries_search_path, path) < 0) { + git_mutex_unlock(&index->lock); giterr_set(GITERR_INDEX, "Index does not contain %s", path); return GIT_ENOTFOUND; } @@ -916,36 +1247,20 @@ /* Since our binary search only looked at path, we may be in the * middle of a list of stages. */ - while (pos > 0) { - const git_index_entry *prev = git_vector_get(&index->entries, pos-1); + for (; pos > 0; --pos) { + const git_index_entry *prev = git_vector_get(&index->entries, pos - 1); if (index->entries_cmp_path(prev->path, path) != 0) break; - - --pos; } if (at_pos) *at_pos = pos; + git_mutex_unlock(&index->lock); return 0; } -size_t git_index__prefix_position(git_index *index, const char *path) -{ - struct entry_srch_key srch_key; - size_t pos; - - srch_key.path = path; - srch_key.stage = 0; - - git_vector_sort(&index->entries); - git_vector_bsearch2( - &pos, &index->entries, index->entries_search, &srch_key); - - return pos; -} - int git_index_conflict_add(git_index *index, const git_index_entry *ancestor_entry, const git_index_entry *our_entry, @@ -957,21 +1272,22 @@ assert (index); - if ((ancestor_entry != NULL && (entries[0] = index_entry_dup(ancestor_entry)) == NULL) || - (our_entry != NULL && (entries[1] = index_entry_dup(our_entry)) == NULL) || - (their_entry != NULL && (entries[2] = index_entry_dup(their_entry)) == NULL)) - return -1; + if ((ret = index_entry_dup(&entries[0], INDEX_OWNER(index), ancestor_entry)) < 0 || + (ret = index_entry_dup(&entries[1], INDEX_OWNER(index), our_entry)) < 0 || + (ret = index_entry_dup(&entries[2], INDEX_OWNER(index), their_entry)) < 0) + goto on_error; for (i = 0; i < 3; i++) { if (entries[i] == NULL) continue; /* Make sure stage is correct */ - entries[i]->flags = (entries[i]->flags & ~GIT_IDXENTRY_STAGEMASK) | - ((i+1) << GIT_IDXENTRY_STAGESHIFT); + GIT_IDXENTRY_STAGE_SET(entries[i], i + 1); - if ((ret = index_insert(index, entries[i], 1)) < 0) + if ((ret = index_insert(index, &entries[i], 1)) < 0) goto on_error; + + entries[i] = NULL; /* don't free if later entry fails */ } return 0; @@ -1061,23 +1377,24 @@ return 0; } -int git_index_conflict_remove(git_index *index, const char *path) +static int index_conflict_remove(git_index *index, const char *path) { - size_t pos, posmax; + size_t pos = 0; git_index_entry *conflict_entry; int error = 0; - assert(index && path); - - if (git_index_find(&pos, index, path) < 0) + if (path != NULL && git_index_find(&pos, index, path) < 0) return GIT_ENOTFOUND; - posmax = git_index_entrycount(index); + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Unable to lock index"); + return -1; + } - while (pos < posmax) { - conflict_entry = git_vector_get(&index->entries, pos); + while ((conflict_entry = git_vector_get(&index->entries, pos)) != NULL) { - if (index->entries_cmp_path(conflict_entry->path, path) != 0) + if (path != NULL && + index->entries_cmp_path(conflict_entry->path, path) != 0) break; if (GIT_IDXENTRY_STAGE(conflict_entry) == 0) { @@ -1085,32 +1402,25 @@ continue; } - if ((error = git_vector_remove(&index->entries, pos)) < 0) - return error; - - index_entry_free(conflict_entry); - posmax--; + if ((error = index_remove_entry(index, pos)) < 0) + break; } - return 0; + git_mutex_unlock(&index->lock); + + return error; } -static int index_conflicts_match(const git_vector *v, size_t idx) +int git_index_conflict_remove(git_index *index, const char *path) { - git_index_entry *entry = git_vector_get(v, idx); - - if (GIT_IDXENTRY_STAGE(entry) > 0) { - index_entry_free(entry); - return 1; - } - - return 0; + assert(index && path); + return index_conflict_remove(index, path); } -void git_index_conflict_cleanup(git_index *index) +int git_index_conflict_cleanup(git_index *index) { assert(index); - git_vector_remove_matching(&index->entries, index_conflicts_match); + return index_conflict_remove(index, NULL); } int git_index_has_conflicts(const git_index *index) @@ -1190,10 +1500,10 @@ git__free(iterator); } -unsigned int git_index_name_entrycount(git_index *index) +size_t git_index_name_entrycount(git_index *index) { assert(index); - return (unsigned int)index->names.length; + return index->names.length; } const git_index_name_entry *git_index_name_get_byindex( @@ -1205,32 +1515,36 @@ return git_vector_get(&index->names, n); } +static void index_name_entry_free(git_index_name_entry *ne) +{ + if (!ne) + return; + git__free(ne->ancestor); + git__free(ne->ours); + git__free(ne->theirs); + git__free(ne); +} + int git_index_name_add(git_index *index, const char *ancestor, const char *ours, const char *theirs) { git_index_name_entry *conflict_name; - assert ((ancestor && ours) || (ancestor && theirs) || (ours && theirs)); + assert((ancestor && ours) || (ancestor && theirs) || (ours && theirs)); conflict_name = git__calloc(1, sizeof(git_index_name_entry)); GITERR_CHECK_ALLOC(conflict_name); - if (ancestor) { - conflict_name->ancestor = git__strdup(ancestor); - GITERR_CHECK_ALLOC(conflict_name->ancestor); - } - - if (ours) { - conflict_name->ours = git__strdup(ours); - GITERR_CHECK_ALLOC(conflict_name->ours); - } - - if (theirs) { - conflict_name->theirs = git__strdup(theirs); - GITERR_CHECK_ALLOC(conflict_name->theirs); + if ((ancestor && !(conflict_name->ancestor = git__strdup(ancestor))) || + (ours && !(conflict_name->ours = git__strdup(ours))) || + (theirs && !(conflict_name->theirs = git__strdup(theirs))) || + git_vector_insert(&index->names, conflict_name) < 0) + { + index_name_entry_free(conflict_name); + return -1; } - return git_vector_insert(&index->names, conflict_name); + return 0; } void git_index_name_clear(git_index *index) @@ -1240,26 +1554,16 @@ assert(index); - git_vector_foreach(&index->names, i, conflict_name) { - if (conflict_name->ancestor) - git__free(conflict_name->ancestor); - - if (conflict_name->ours) - git__free(conflict_name->ours); - - if (conflict_name->theirs) - git__free(conflict_name->theirs); - - git__free(conflict_name); - } + git_vector_foreach(&index->names, i, conflict_name) + index_name_entry_free(conflict_name); git_vector_clear(&index->names); } -unsigned int git_index_reuc_entrycount(git_index *index) +size_t git_index_reuc_entrycount(git_index *index) { assert(index); - return (unsigned int)index->reuc.length; + return index->reuc.length; } static int index_reuc_insert( @@ -1279,7 +1583,6 @@ return git_vector_insert(&index->reuc, reuc); /* exists, replace it */ - git__free((*existing)->path); git__free(*existing); *existing = reuc; @@ -1296,15 +1599,13 @@ assert(index && path); - if ((error = index_entry_reuc_init(&reuc, path, ancestor_mode, ancestor_oid, our_mode, our_oid, their_mode, their_oid)) < 0 || + if ((error = index_entry_reuc_init(&reuc, path, ancestor_mode, + ancestor_oid, our_mode, our_oid, their_mode, their_oid)) < 0 || (error = index_reuc_insert(index, reuc, 1)) < 0) - { index_entry_reuc_free(reuc); - return error; - } return error; -} +} int git_index_reuc_find(size_t *at_pos, git_index *index, const char *path) { @@ -1389,13 +1690,9 @@ if (size <= len) return index_error_invalid("reading reuc entries"); - lost = git__calloc(1, sizeof(git_index_reuc_entry)); + lost = reuc_entry_alloc(buffer); GITERR_CHECK_ALLOC(lost); - /* read NUL-terminated pathname for entry */ - lost->path = git__strdup(buffer); - GITERR_CHECK_ALLOC(lost->path); - size -= len; buffer += len; @@ -1442,7 +1739,7 @@ } /* entries are guaranteed to be sorted on-disk */ - index->reuc.sorted = 1; + git_vector_set_sorted(&index->reuc, true); return 0; } @@ -1488,46 +1785,56 @@ #undef read_conflict_name /* entries are guaranteed to be sorted on-disk */ - index->names.sorted = 1; + git_vector_set_sorted(&index->names, true); return 0; } -static size_t read_entry(git_index_entry *dest, const void *buffer, size_t buffer_size) +static size_t read_entry( + git_index_entry **out, + git_index *index, + const void *buffer, + size_t buffer_size) { size_t path_length, entry_size; - uint16_t flags_raw; const char *path_ptr; - const struct entry_short *source = buffer; + struct entry_short source; + git_index_entry entry = {{0}}; if (INDEX_FOOTER_SIZE + minimal_entry_size > buffer_size) return 0; - memset(dest, 0x0, sizeof(git_index_entry)); + /* buffer is not guaranteed to be aligned */ + memcpy(&source, buffer, sizeof(struct entry_short)); - dest->ctime.seconds = (git_time_t)ntohl(source->ctime.seconds); - dest->ctime.nanoseconds = ntohl(source->ctime.nanoseconds); - dest->mtime.seconds = (git_time_t)ntohl(source->mtime.seconds); - dest->mtime.nanoseconds = ntohl(source->mtime.nanoseconds); - dest->dev = ntohl(source->dev); - dest->ino = ntohl(source->ino); - dest->mode = ntohl(source->mode); - dest->uid = ntohl(source->uid); - dest->gid = ntohl(source->gid); - dest->file_size = ntohl(source->file_size); - git_oid_cpy(&dest->oid, &source->oid); - dest->flags = ntohs(source->flags); - - if (dest->flags & GIT_IDXENTRY_EXTENDED) { - const struct entry_long *source_l = (const struct entry_long *)source; - path_ptr = source_l->path; + entry.ctime.seconds = (git_time_t)ntohl(source.ctime.seconds); + entry.ctime.nanoseconds = ntohl(source.ctime.nanoseconds); + entry.mtime.seconds = (git_time_t)ntohl(source.mtime.seconds); + entry.mtime.nanoseconds = ntohl(source.mtime.nanoseconds); + entry.dev = ntohl(source.dev); + entry.ino = ntohl(source.ino); + entry.mode = ntohl(source.mode); + entry.uid = ntohl(source.uid); + entry.gid = ntohl(source.gid); + entry.file_size = ntohl(source.file_size); + git_oid_cpy(&entry.id, &source.oid); + entry.flags = ntohs(source.flags); + + if (entry.flags & GIT_IDXENTRY_EXTENDED) { + uint16_t flags_raw; + size_t flags_offset; + + flags_offset = offsetof(struct entry_long, flags_extended); + memcpy(&flags_raw, (const char *) buffer + flags_offset, + sizeof(flags_raw)); + flags_raw = ntohs(flags_raw); - flags_raw = ntohs(source_l->flags_extended); - memcpy(&dest->flags_extended, &flags_raw, 2); + memcpy(&entry.flags_extended, &flags_raw, sizeof(flags_raw)); + path_ptr = (const char *) buffer + offsetof(struct entry_long, path); } else - path_ptr = source->path; + path_ptr = (const char *) buffer + offsetof(struct entry_short, path); - path_length = dest->flags & GIT_IDXENTRY_NAMEMASK; + path_length = entry.flags & GIT_IDXENTRY_NAMEMASK; /* if this is a very long string, we must find its * real length without overflowing */ @@ -1541,7 +1848,7 @@ path_length = path_end - path_ptr; } - if (dest->flags & GIT_IDXENTRY_EXTENDED) + if (entry.flags & GIT_IDXENTRY_EXTENDED) entry_size = long_entry_size(path_length); else entry_size = short_entry_size(path_length); @@ -1549,8 +1856,10 @@ if (INDEX_FOOTER_SIZE + entry_size > buffer_size) return 0; - dest->path = git__strdup(path_ptr); - assert(dest->path); + entry.path = (char *)path_ptr; + + if (index_entry_dup(out, INDEX_OWNER(index), &entry) < 0) + return 0; return entry_size; } @@ -1574,14 +1883,12 @@ static size_t read_extension(git_index *index, const char *buffer, size_t buffer_size) { - const struct index_extension *source; struct index_extension dest; size_t total_size; - source = (const struct index_extension *)(buffer); - - memcpy(dest.signature, source->signature, 4); - dest.extension_size = ntohl(source->extension_size); + /* buffer is not guaranteed to be aligned */ + memcpy(&dest, buffer, sizeof(struct index_extension)); + dest.extension_size = ntohl(dest.extension_size); total_size = dest.extension_size + sizeof(struct index_extension); @@ -1594,7 +1901,7 @@ if (dest.signature[0] >= 'A' && dest.signature[0] <= 'Z') { /* tree cache */ if (memcmp(dest.signature, INDEX_EXT_TREECACHE_SIG, 4) == 0) { - if (git_tree_cache_read(&index->tree, buffer + 8, dest.extension_size) < 0) + if (git_tree_cache_read(&index->tree, buffer + 8, dest.extension_size, &index->tree_pool) < 0) return 0; } else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) { if (read_reuc(index, buffer + 8, dest.extension_size) < 0) @@ -1616,13 +1923,15 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size) { + int error = 0; unsigned int i; struct index_header header = { 0 }; git_oid checksum_calculated, checksum_expected; #define seek_forward(_increase) { \ - if (_increase >= buffer_size) \ - return index_error_invalid("ran out of data while parsing"); \ + if (_increase >= buffer_size) { \ + error = index_error_invalid("ran out of data while parsing"); \ + goto done; } \ buffer += _increase; \ buffer_size -= _increase;\ } @@ -1635,35 +1944,41 @@ git_hash_buf(&checksum_calculated, buffer, buffer_size - INDEX_FOOTER_SIZE); /* Parse header */ - if (read_header(&header, buffer) < 0) - return -1; + if ((error = read_header(&header, buffer)) < 0) + return error; seek_forward(INDEX_HEADER_SIZE); - git_vector_clear(&index->entries); + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Unable to acquire index lock"); + return -1; + } + + assert(!index->entries.length); /* Parse all the entries */ for (i = 0; i < header.entry_count && buffer_size > INDEX_FOOTER_SIZE; ++i) { - size_t entry_size; git_index_entry *entry; - - entry = git__malloc(sizeof(git_index_entry)); - GITERR_CHECK_ALLOC(entry); - - entry_size = read_entry(entry, buffer, buffer_size); + size_t entry_size = read_entry(&entry, index, buffer, buffer_size); /* 0 bytes read means an object corruption */ - if (entry_size == 0) - return index_error_invalid("invalid entry"); + if (entry_size == 0) { + error = index_error_invalid("invalid entry"); + goto done; + } - if (git_vector_insert(&index->entries, entry) < 0) - return -1; + if ((error = git_vector_insert(&index->entries, entry)) < 0) { + index_entry_free(entry); + goto done; + } seek_forward(entry_size); } - if (i != header.entry_count) - return index_error_invalid("header entries changed while parsing"); + if (i != header.entry_count) { + error = index_error_invalid("header entries changed while parsing"); + goto done; + } /* There's still space for some extensions! */ while (buffer_size > INDEX_FOOTER_SIZE) { @@ -1672,28 +1987,40 @@ extension_size = read_extension(index, buffer, buffer_size); /* see if we have read any bytes from the extension */ - if (extension_size == 0) - return index_error_invalid("extension is truncated"); + if (extension_size == 0) { + error = index_error_invalid("extension is truncated"); + goto done; + } seek_forward(extension_size); } - if (buffer_size != INDEX_FOOTER_SIZE) - return index_error_invalid("buffer size does not match index footer size"); + if (buffer_size != INDEX_FOOTER_SIZE) { + error = index_error_invalid( + "buffer size does not match index footer size"); + goto done; + } /* 160-bit SHA-1 over the content of the index file before this checksum. */ git_oid_fromraw(&checksum_expected, (const unsigned char *)buffer); - if (git_oid__cmp(&checksum_calculated, &checksum_expected) != 0) - return index_error_invalid("calculated checksum does not match expected"); + if (git_oid__cmp(&checksum_calculated, &checksum_expected) != 0) { + error = index_error_invalid( + "calculated checksum does not match expected"); + goto done; + } #undef seek_forward - /* Entries are stored case-sensitively on disk. */ - index->entries.sorted = !index->ignore_case; - git_vector_sort(&index->entries); + /* Entries are stored case-sensitively on disk, so re-sort now if + * in-memory index is supposed to be case-insensitive + */ + git_vector_set_sorted(&index->entries, !index->ignore_case); + error = index_sort_if_needed(index, false); - return 0; +done: + git_mutex_unlock(&index->lock); + return error; } static bool is_index_extended(git_index *index) @@ -1721,7 +2048,7 @@ size_t path_len, disk_size; char *path; - path_len = strlen(entry->path); + path_len = ((struct entry_internal *)entry)->pathlen; if (entry->flags & GIT_IDXENTRY_EXTENDED) disk_size = long_entry_size(path_len); @@ -1756,7 +2083,7 @@ ondisk->gid = htonl(entry->gid); ondisk->file_size = htonl((uint32_t)entry->file_size); - git_oid_cpy(&ondisk->oid, &entry->oid); + git_oid_cpy(&ondisk->oid, &entry->id); ondisk->flags = htons(entry->flags); @@ -1778,22 +2105,30 @@ { int error = 0; size_t i; - git_vector case_sorted; + git_vector case_sorted, *entries; git_index_entry *entry; - git_vector *out = &index->entries; + + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Failed to lock index"); + return -1; + } /* If index->entries is sorted case-insensitively, then we need * to re-sort it case-sensitively before writing */ if (index->ignore_case) { - git_vector_dup(&case_sorted, &index->entries, index_cmp); + git_vector_dup(&case_sorted, &index->entries, git_index_entry_cmp); git_vector_sort(&case_sorted); - out = &case_sorted; + entries = &case_sorted; + } else { + entries = &index->entries; } - git_vector_foreach(out, i, entry) + git_vector_foreach(entries, i, entry) if ((error = write_disk_entry(file, entry)) < 0) break; + git_mutex_unlock(&index->lock); + if (index->ignore_case) git_vector_free(&case_sorted); @@ -1803,16 +2138,13 @@ static int write_extension(git_filebuf *file, struct index_extension *header, git_buf *data) { struct index_extension ondisk; - int error = 0; memset(&ondisk, 0x0, sizeof(struct index_extension)); memcpy(&ondisk, header, 4); ondisk.extension_size = htonl(header->extension_size); - if ((error = git_filebuf_write(file, &ondisk, sizeof(struct index_extension))) == 0) - error = git_filebuf_write(file, data->ptr, data->size); - - return error; + git_filebuf_write(file, &ondisk, sizeof(struct index_extension)); + return git_filebuf_write(file, data->ptr, data->size); } static int create_name_extension_data(git_buf *name_buf, git_index_name_entry *conflict_name) @@ -1918,18 +2250,43 @@ return error; } +static int write_tree_extension(git_index *index, git_filebuf *file) +{ + struct index_extension extension; + git_buf buf = GIT_BUF_INIT; + int error; + + if (index->tree == NULL) + return 0; + + if ((error = git_tree_cache_write(&buf, index->tree)) < 0) + return error; + + memset(&extension, 0x0, sizeof(struct index_extension)); + memcpy(&extension.signature, INDEX_EXT_TREECACHE_SIG, 4); + extension.extension_size = (uint32_t)buf.size; + + error = write_extension(file, &extension, &buf); + + git_buf_free(&buf); + + return error; +} + static int write_index(git_index *index, git_filebuf *file) { git_oid hash_final; struct index_header header; bool is_extended; + uint32_t index_version_number; assert(index && file); is_extended = is_index_extended(index); + index_version_number = is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER; header.signature = htonl(INDEX_HEADER_SIG); - header.version = htonl(is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER); + header.version = htonl(index_version_number); header.entry_count = htonl((uint32_t)index->entries.length); if (git_filebuf_write(file, &header, sizeof(struct index_header)) < 0) @@ -1938,7 +2295,9 @@ if (write_entries(index, file) < 0) return -1; - /* TODO: write tree cache extension */ + /* write the tree cache extension */ + if (index->tree != NULL && write_tree_extension(index, file) < 0) + return -1; /* write the rename conflict extension */ if (index->names.length > 0 && write_name_extension(index, file) < 0) @@ -1961,9 +2320,11 @@ } typedef struct read_tree_data { + git_index *index; git_vector *old_entries; git_vector *new_entries; - git_vector_cmp entries_search; + git_vector_cmp entry_cmp; + git_tree_cache *tree; } read_tree_data; static int read_tree_cb( @@ -1972,6 +2333,7 @@ read_tree_data *data = payload; git_index_entry *entry = NULL, *old_entry; git_buf path = GIT_BUF_INIT; + size_t pos; if (git_tree_entry__is_tree(tentry)) return 0; @@ -1979,29 +2341,22 @@ if (git_buf_joinpath(&path, root, tentry->filename) < 0) return -1; - entry = git__calloc(1, sizeof(git_index_entry)); - GITERR_CHECK_ALLOC(entry); + if (index_entry_create(&entry, INDEX_OWNER(data->index), path.ptr) < 0) + return -1; entry->mode = tentry->attr; - entry->oid = tentry->oid; + entry->id = tentry->oid; /* look for corresponding old entry and copy data to new entry */ - if (data->old_entries) { - size_t pos; - struct entry_srch_key skey; - - skey.path = path.ptr; - skey.stage = 0; - - if (!git_vector_bsearch2( - &pos, data->old_entries, data->entries_search, &skey) && - (old_entry = git_vector_get(data->old_entries, pos)) != NULL && - entry->mode == old_entry->mode && - git_oid_equal(&entry->oid, &old_entry->oid)) - { - memcpy(entry, old_entry, sizeof(*entry)); - entry->flags_extended = 0; - } + if (data->old_entries != NULL && + !index_find_in_entries( + &pos, data->old_entries, data->entry_cmp, path.ptr, 0, 0) && + (old_entry = git_vector_get(data->old_entries, pos)) != NULL && + entry->mode == old_entry->mode && + git_oid_equal(&entry->id, &old_entry->id)) + { + index_entry_cpy(entry, old_entry); + entry->flags_extended = 0; } if (path.size < GIT_IDXENTRY_NAMEMASK) @@ -2009,7 +2364,6 @@ else entry->flags = GIT_IDXENTRY_NAMEMASK; - entry->path = git_buf_detach(&path); git_buf_free(&path); if (git_vector_insert(data->new_entries, entry) < 0) { @@ -2028,20 +2382,38 @@ git_vector_set_cmp(&entries, index->entries._cmp); /* match sort */ + data.index = index; data.old_entries = &index->entries; data.new_entries = &entries; - data.entries_search = index->entries_search; + data.entry_cmp = index->entries_search; - git_vector_sort(&index->entries); + index->tree = NULL; + git_pool_clear(&index->tree_pool); + + if (index_sort_if_needed(index, true) < 0) + return -1; error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data); - git_vector_sort(&entries); + if (!error) { + git_vector_sort(&entries); - git_index_clear(index); + if ((error = git_index_clear(index)) < 0) + /* well, this isn't good */; + else if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Unable to acquire index lock"); + error = -1; + } else { + git_vector_swap(&entries, &index->entries); + git_mutex_unlock(&index->lock); + } + } - git_vector_swap(&entries, &index->entries); git_vector_free(&entries); + if (error < 0) + return error; + + error = git_tree_cache_read_tree(&index->tree, tree, &index->tree_pool); return error; } @@ -2095,7 +2467,7 @@ goto cleanup; if ((error = git_iterator_for_workdir( - &wditer, repo, 0, ps.prefix, ps.prefix)) < 0) + &wditer, repo, NULL, NULL, 0, ps.prefix, ps.prefix)) < 0) goto cleanup; while (!(error = git_iterator_advance(&wd, wditer))) { @@ -2108,7 +2480,7 @@ /* skip ignored items that are not already in the index */ if ((flags & GIT_INDEX_ADD_FORCE) == 0 && git_iterator_current_is_ignored(wditer) && - git_index__find(&existing, index, wd->path, 0) < 0) + index_find(&existing, index, wd->path, 0, 0, true) < 0) continue; /* issue notification callback if requested */ @@ -2116,8 +2488,7 @@ if (error > 0) /* return > 0 means skip this one */ continue; if (error < 0) { /* return < 0 means abort */ - giterr_clear(); - error = GIT_EUSER; + giterr_set_after_callback(error); break; } } @@ -2131,17 +2502,14 @@ break; /* make the new entry to insert */ - if ((entry = index_entry_dup(wd)) == NULL) { - error = -1; + if ((error = index_entry_dup(&entry, INDEX_OWNER(index), wd)) < 0) break; - } - entry->oid = blobid; + + entry->id = blobid; /* add working directory item to index */ - if ((error = index_insert(index, entry, 1)) < 0) { - index_entry_free(entry); + if ((error = index_insert(index, &entry, 1)) < 0) break; - } git_tree_cache_invalidate_path(index->tree, wd->path); @@ -2204,11 +2572,8 @@ error = 0; continue; } - if (error < 0) { /* return < 0 means abort */ - giterr_clear(); - error = GIT_EUSER; + if (error < 0) /* return < 0 means abort */ break; - } } /* index manipulation may alter entry, so don't depend on it */ @@ -2253,8 +2618,13 @@ git_index_matched_path_cb cb, void *payload) { - return index_apply_to_all( + int error = index_apply_to_all( index, INDEX_ACTION_REMOVE, pathspec, cb, payload); + + if (error) /* make sure error is set if callback stopped iteration */ + giterr_set_after_callback(error); + + return error; } int git_index_update_all( @@ -2263,6 +2633,56 @@ git_index_matched_path_cb cb, void *payload) { - return index_apply_to_all( + int error = index_apply_to_all( index, INDEX_ACTION_UPDATE, pathspec, cb, payload); + + if (error) /* make sure error is set if callback stopped iteration */ + giterr_set_after_callback(error); + + return error; +} + +int git_index_snapshot_new(git_vector *snap, git_index *index) +{ + int error; + + GIT_REFCOUNT_INC(index); + + if (git_mutex_lock(&index->lock) < 0) { + giterr_set(GITERR_OS, "Failed to lock index"); + return -1; + } + + git_atomic_inc(&index->readers); + git_vector_sort(&index->entries); + + error = git_vector_dup(snap, &index->entries, index->entries._cmp); + + git_mutex_unlock(&index->lock); + + if (error < 0) + git_index_free(index); + + return error; +} + +void git_index_snapshot_release(git_vector *snap, git_index *index) +{ + git_vector_free(snap); + + git_atomic_dec(&index->readers); + + if (!git_mutex_lock(&index->lock)) { + index_free_deleted(index); /* try to free pending deleted items */ + git_mutex_unlock(&index->lock); + } + + git_index_free(index); +} + +int git_index_snapshot_find( + size_t *out, git_vector *entries, git_vector_cmp entry_srch, + const char *path, size_t path_len, int stage) +{ + return index_find_in_entries(out, entries, entry_srch, path, path_len, stage); } diff -Nru libgit2-0.20.0/src/indexer.c libgit2-0.22.2/src/indexer.c --- libgit2-0.20.0/src/indexer.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/indexer.c 2015-03-24 16:10:45.000000000 +0000 @@ -5,8 +5,6 @@ * a Linking Exception. For full terms see the included COPYING file. */ -#include - #include "git2/indexer.h" #include "git2/object.h" @@ -18,7 +16,9 @@ #include "filebuf.h" #include "oid.h" #include "oidmap.h" -#include "compress.h" +#include "zstream.h" + +extern git_mutex git__mwindow_mutex; #define UINT31_MAX (0x7FFFFFFF) @@ -36,7 +36,6 @@ have_delta :1; struct git_pack_header hdr; struct git_pack_file *pack; - git_filebuf pack_file; unsigned int mode; git_off_t off; git_off_t entry_start; @@ -47,7 +46,7 @@ unsigned int fanout[256]; git_hash_ctx hash_ctx; git_oid hash; - git_transfer_progress_callback progress_cb; + git_transfer_progress_cb progress_cb; void *progress_payload; char objbuf[8*1024]; @@ -69,33 +68,18 @@ return &idx->hash; } -static int open_pack(struct git_pack_file **out, const char *filename) -{ - struct git_pack_file *pack; - - if (git_packfile_alloc(&pack, filename) < 0) - return -1; - - if ((pack->mwf.fd = p_open(pack->pack_name, O_RDONLY)) < 0) { - giterr_set(GITERR_OS, "Failed to open packfile."); - git_packfile_free(pack); - return -1; - } - - *out = pack; - return 0; -} - static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack) { int error; + git_map map; - /* Verify we recognize this pack file format. */ - if ((error = p_read(pack->mwf.fd, hdr, sizeof(*hdr))) < 0) { - giterr_set(GITERR_OS, "Failed to read in pack header"); + if ((error = p_mmap(&map, sizeof(*hdr), GIT_PROT_READ, GIT_MAP_SHARED, pack->mwf.fd, 0)) < 0) return error; - } + memcpy(hdr, map.data, sizeof(*hdr)); + p_munmap(&map); + + /* Verify we recognize this pack file format. */ if (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) { giterr_set(GITERR_INDEXER, "Wrong pack signature"); return -1; @@ -122,13 +106,13 @@ const char *prefix, unsigned int mode, git_odb *odb, - git_transfer_progress_callback progress_cb, + git_transfer_progress_cb progress_cb, void *progress_payload) { git_indexer *idx; - git_buf path = GIT_BUF_INIT; + git_buf path = GIT_BUF_INIT, tmp_path = GIT_BUF_INIT; static const char suff[] = "/pack"; - int error; + int error, fd = -1; idx = git__calloc(1, sizeof(git_indexer)); GITERR_CHECK_ALLOC(idx); @@ -136,25 +120,37 @@ idx->progress_cb = progress_cb; idx->progress_payload = progress_payload; idx->mode = mode ? mode : GIT_PACK_FILE_MODE; + git_hash_ctx_init(&idx->hash_ctx); git_hash_ctx_init(&idx->trailer); error = git_buf_joinpath(&path, prefix, suff); if (error < 0) goto cleanup; - error = git_filebuf_open(&idx->pack_file, path.ptr, - GIT_FILEBUF_TEMPORARY | GIT_FILEBUF_DO_NOT_BUFFER, - idx->mode); + fd = git_futils_mktmp(&tmp_path, git_buf_cstr(&path), idx->mode); git_buf_free(&path); + if (fd < 0) + goto cleanup; + + error = git_packfile_alloc(&idx->pack, git_buf_cstr(&tmp_path)); + git_buf_free(&tmp_path); + if (error < 0) goto cleanup; + idx->pack->mwf.fd = fd; + if ((error = git_mwindow_file_register(&idx->pack->mwf)) < 0) + goto cleanup; + *out = idx; return 0; cleanup: + if (fd != -1) + p_close(fd); + git_buf_free(&path); - git_filebuf_cleanup(&idx->pack_file); + git_buf_free(&tmp_path); git__free(idx); return -1; } @@ -270,7 +266,6 @@ struct entry *entry; git_off_t entry_size; struct git_pack_entry *pentry; - git_hash_ctx *ctx = &idx->hash_ctx; git_off_t entry_start = idx->entry_start; entry = git__calloc(1, sizeof(*entry)); @@ -279,7 +274,7 @@ pentry = git__calloc(1, sizeof(struct git_pack_entry)); GITERR_CHECK_ALLOC(pentry); - git_hash_final(&oid, ctx); + git_hash_final(&oid, &idx->hash_ctx); entry_size = idx->off - entry_start; if (entry_start > UINT31_MAX) { entry->offset = UINT32_MAX; @@ -294,6 +289,7 @@ k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error); if (!error) { git__free(pentry); + giterr_set(GITERR_INDEXER, "cannot handle duplicate objects in pack"); goto on_error; } @@ -355,7 +351,7 @@ git_oid oid; size_t entry_size; struct entry *entry; - struct git_pack_entry *pentry; + struct git_pack_entry *pentry = NULL; entry = git__calloc(1, sizeof(*entry)); GITERR_CHECK_ALLOC(entry); @@ -379,6 +375,7 @@ return save_entry(idx, entry, pentry, entry_start); on_error: + git__free(pentry); git__free(entry); git__free(obj->data); return -1; @@ -386,8 +383,11 @@ static int do_progress_callback(git_indexer *idx, git_transfer_progress *stats) { - if (!idx->progress_cb) return 0; - return idx->progress_cb(stats, idx->progress_payload); + if (idx->progress_cb) + return giterr_set_after_callback_function( + idx->progress_cb(stats, idx->progress_payload), + "indexer progress"); + return 0; } /* Hash everything but the last 20B of input */ @@ -427,6 +427,51 @@ idx->inbuf_len += size - to_expell; } +static int write_at(git_indexer *idx, const void *data, git_off_t offset, size_t size) +{ + git_file fd = idx->pack->mwf.fd; + size_t page_size; + size_t page_offset; + git_off_t page_start; + unsigned char *map_data; + git_map map; + int error; + + assert(data && size); + + if ((error = git__page_size(&page_size)) < 0) + return error; + + /* the offset needs to be at the beginning of the a page boundary */ + page_offset = offset % page_size; + page_start = offset - page_offset; + + if ((error = p_mmap(&map, page_offset + size, GIT_PROT_WRITE, GIT_MAP_SHARED, fd, page_start)) < 0) + return error; + + map_data = (unsigned char *)map.data; + memcpy(map_data + page_offset, data, size); + p_munmap(&map); + + return 0; +} + +static int append_to_pack(git_indexer *idx, const void *data, size_t size) +{ + git_off_t current_size = idx->pack->mwf.size; + + if (!size) + return 0; + + /* add the extra space we need at the end */ + if (p_ftruncate(idx->pack->mwf.fd, current_size + size) < 0) { + giterr_set(GITERR_OS, "Failed to increase size of pack file '%s'", idx->pack->pack_name); + return -1; + } + + return write_at(idx, data, idx->pack->mwf.size, size); +} + int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_transfer_progress *stats) { int error = -1; @@ -438,22 +483,13 @@ processed = stats->indexed_objects; - if (git_filebuf_write(&idx->pack_file, data, size) < 0) - return -1; + if ((error = append_to_pack(idx, data, size)) < 0) + return error; hash_partially(idx, data, (int)size); /* Make sure we set the new size of the pack */ - if (idx->opened_pack) { - idx->pack->mwf.size += size; - } else { - if (open_pack(&idx->pack, idx->pack_file.path_lock) < 0) - return -1; - idx->opened_pack = 1; - mwf = &idx->pack->mwf; - if (git_mwindow_file_register(&idx->pack->mwf) < 0) - return -1; - } + idx->pack->mwf.size += size; if (!idx->parsed_header) { unsigned int total_objects; @@ -461,8 +497,8 @@ if ((unsigned)idx->pack->mwf.size < sizeof(struct git_pack_header)) return 0; - if (parse_header(&idx->hdr, idx->pack) < 0) - return -1; + if ((error = parse_header(&idx->hdr, idx->pack)) < 0) + return error; idx->parsed_header = 1; idx->nr_objects = ntohl(hdr->hdr_entries); @@ -491,13 +527,16 @@ stats->indexed_deltas = 0; processed = stats->indexed_objects = 0; stats->total_objects = total_objects; - do_progress_callback(idx, stats); + + if ((error = do_progress_callback(idx, stats)) != 0) + return error; } /* Now that we have data in the pack, let's try to parse it */ /* As the file grows any windows we try to use will be out of date */ git_mwindow_free_all(mwf); + while (processed < idx->nr_objects) { git_packfile_stream *stream = &idx->stream; git_off_t entry_start = idx->off; @@ -515,11 +554,11 @@ return 0; } if (error < 0) - return -1; + goto on_error; git_mwindow_close(&w); idx->entry_start = entry_start; - git_hash_ctx_init(&idx->hash_ctx); + git_hash_init(&idx->hash_ctx); if (type == GIT_OBJ_REF_DELTA || type == GIT_OBJ_OFS_DELTA) { error = advance_delta_offset(idx, type); @@ -528,7 +567,7 @@ return 0; } if (error < 0) - return -1; + goto on_error; idx->have_delta = 1; } else { @@ -537,9 +576,10 @@ } idx->have_stream = 1; - if (git_packfile_stream_open(stream, idx->pack, idx->off) < 0) - goto on_error; + error = git_packfile_stream_open(stream, idx->pack, idx->off); + if (error < 0) + goto on_error; } if (idx->have_delta) { @@ -573,11 +613,8 @@ } stats->received_objects++; - if (do_progress_callback(idx, stats) != 0) { - giterr_clear(); - error = GIT_EUSER; + if ((error = do_progress_callback(idx, stats)) != 0) goto on_error; - } } return 0; @@ -613,24 +650,17 @@ * Rewind the packfile by the trailer, as we might need to fix the * packfile by injecting objects at the tail and must overwrite it. */ -static git_off_t seek_back_trailer(git_indexer *idx) +static void seek_back_trailer(git_indexer *idx) { - git_off_t off; - - if ((off = p_lseek(idx->pack_file.fd, -GIT_OID_RAWSZ, SEEK_CUR)) < 0) - return -1; - idx->pack->mwf.size -= GIT_OID_RAWSZ; git_mwindow_free_all(&idx->pack->mwf); - - return off; } static int inject_object(git_indexer *idx, git_oid *id) { git_odb_object *obj; struct entry *entry; - struct git_pack_entry *pentry; + struct git_pack_entry *pentry = NULL; git_oid foo = {{0}}; unsigned char hdr[64]; git_buf buf = GIT_BUF_INIT; @@ -639,36 +669,44 @@ size_t len, hdr_len; int error; - entry = git__calloc(1, sizeof(*entry)); - GITERR_CHECK_ALLOC(entry); - - entry_start = seek_back_trailer(idx); + seek_back_trailer(idx); + entry_start = idx->pack->mwf.size; - if (git_odb_read(&obj, idx->odb, id) < 0) + if (git_odb_read(&obj, idx->odb, id) < 0) { + giterr_set(GITERR_INDEXER, "missing delta bases"); return -1; + } data = git_odb_object_data(obj); len = git_odb_object_size(obj); + entry = git__calloc(1, sizeof(*entry)); + GITERR_CHECK_ALLOC(entry); + entry->crc = crc32(0L, Z_NULL, 0); /* Write out the object header */ hdr_len = git_packfile__object_header(hdr, len, git_odb_object_type(obj)); - git_filebuf_write(&idx->pack_file, hdr, hdr_len); + if ((error = append_to_pack(idx, hdr, hdr_len)) < 0) + goto cleanup; + idx->pack->mwf.size += hdr_len; - entry->crc = crc32(entry->crc, hdr, hdr_len); + entry->crc = crc32(entry->crc, hdr, (uInt)hdr_len); - if ((error = git__compress(&buf, data, len)) < 0) + if ((error = git_zstream_deflatebuf(&buf, data, len)) < 0) goto cleanup; /* And then the compressed object */ - git_filebuf_write(&idx->pack_file, buf.ptr, buf.size); + if ((error = append_to_pack(idx, buf.ptr, buf.size)) < 0) + goto cleanup; + idx->pack->mwf.size += buf.size; entry->crc = htonl(crc32(entry->crc, (unsigned char *)buf.ptr, (uInt)buf.size)); git_buf_free(&buf); /* Write a fake trailer so the pack functions play ball */ - if ((error = git_filebuf_write(&idx->pack_file, &foo, GIT_OID_RAWSZ)) < 0) + + if ((error = append_to_pack(idx, &foo, GIT_OID_RAWSZ)) < 0) goto cleanup; idx->pack->mwf.size += GIT_OID_RAWSZ; @@ -680,10 +718,14 @@ git_oid_cpy(&entry->oid, id); idx->off = entry_start + hdr_len + len; - if ((error = save_entry(idx, entry, pentry, entry_start)) < 0) - git__free(pentry); + error = save_entry(idx, entry, pentry, entry_start); cleanup: + if (error) { + git__free(entry); + git__free(pentry); + } + git_odb_object_free(obj); return error; } @@ -696,7 +738,7 @@ size_t size; git_otype type; git_mwindow *w = NULL; - git_off_t curpos; + git_off_t curpos = 0; unsigned char *base_info; unsigned int left = 0; git_oid base; @@ -710,6 +752,9 @@ /* Loop until we find the first REF delta */ git_vector_foreach(&idx->deltas, i, delta) { + if (!delta) + continue; + curpos = delta->delta_off; error = git_packfile_unpack_header(&size, &type, &idx->pack->mwf, &w, &curpos); git_mwindow_close(&w); @@ -749,13 +794,18 @@ { unsigned int i; struct delta_info *delta; - int progressed = 0; + int progressed = 0, non_null = 0, progress_cb_result; while (idx->deltas.length > 0) { progressed = 0; + non_null = 0; git_vector_foreach(&idx->deltas, i, delta) { git_rawobj obj; + if (!delta) + continue; + + non_null = 1; idx->off = delta->delta_off; if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0) continue; @@ -767,20 +817,19 @@ stats->indexed_objects++; stats->indexed_deltas++; progressed = 1; - do_progress_callback(idx, stats); + if ((progress_cb_result = do_progress_callback(idx, stats)) < 0) + return progress_cb_result; - /* - * Remove this delta from the list and - * decrease i so we don't skip over the next - * delta. - */ - git_vector_remove(&idx->deltas, i); + /* remove from the list */ + git_vector_set(NULL, &idx->deltas, i, NULL); git__free(delta); - i--; } + /* if none were actually set, we're done */ + if (!non_null) + break; + if (!progressed && (fix_thin_pack(idx, stats) < 0)) { - giterr_set(GITERR_INDEXER, "missing delta bases"); return -1; } } @@ -796,25 +845,16 @@ git_mwindow *w = NULL; git_mwindow_file *mwf; unsigned int left; - git_hash_ctx *ctx; mwf = &idx->pack->mwf; - ctx = &idx->trailer; - git_hash_ctx_init(ctx); - git_mwindow_free_all(mwf); + git_hash_init(&idx->trailer); + /* Update the header to include the numer of local objects we injected */ idx->hdr.hdr_entries = htonl(stats->total_objects + stats->local_objects); - if (p_lseek(idx->pack_file.fd, 0, SEEK_SET) < 0) { - giterr_set(GITERR_OS, "failed to seek to the beginning of the pack"); - return -1; - } - - if (p_write(idx->pack_file.fd, &idx->hdr, sizeof(struct git_pack_header)) < 0) { - giterr_set(GITERR_OS, "failed to update the pack header"); + if (write_at(idx, &idx->hdr, 0, sizeof(struct git_pack_header)) < 0) return -1; - } /* * We now use the same technique as before to determine the @@ -822,6 +862,7 @@ * hash_partially() keep the existing trailer out of the * calculation. */ + git_mwindow_free_all(mwf); idx->inbuf_len = 0; while (hashed < mwf->size) { ptr = git_mwindow_open(mwf, &w, hashed, chunk, &left); @@ -841,6 +882,7 @@ { git_mwindow *w = NULL; unsigned int i, long_offsets = 0, left; + int error; struct git_pack_idx_header hdr; git_buf filename = GIT_BUF_INIT; struct entry *entry; @@ -854,7 +896,7 @@ /* Test for this before resolve_deltas(), as it plays with idx->off */ if (idx->off < idx->pack->mwf.size - 20) { - giterr_set(GITERR_INDEXER, "unexpected data at the end of the pack"); + giterr_set(GITERR_INDEXER, "Unexpected data at the end of the pack"); return -1; } @@ -877,8 +919,8 @@ /* Freeze the number of deltas */ stats->total_deltas = stats->total_objects - stats->indexed_objects; - if (resolve_deltas(idx, stats) < 0) - return -1; + if ((error = resolve_deltas(idx, stats)) < 0) + return error; if (stats->indexed_objects != stats->total_objects) { giterr_set(GITERR_INDEXER, "early EOF"); @@ -890,13 +932,7 @@ return -1; git_hash_final(&trailer_hash, &idx->trailer); - if (p_lseek(idx->pack_file.fd, -GIT_OID_RAWSZ, SEEK_END) < 0) - return -1; - - if (p_write(idx->pack_file.fd, &trailer_hash, GIT_OID_RAWSZ) < 0) { - giterr_set(GITERR_OS, "failed to update pack trailer"); - return -1; - } + write_at(idx, &trailer_hash, idx->pack->mwf.size - GIT_OID_RAWSZ, GIT_OID_RAWSZ); } git_vector_sort(&idx->objects); @@ -979,16 +1015,21 @@ git_mwindow_free_all(&idx->pack->mwf); /* We need to close the descriptor here so Windows doesn't choke on commit_at */ - p_close(idx->pack->mwf.fd); + if (p_close(idx->pack->mwf.fd) < 0) { + giterr_set(GITERR_OS, "failed to close packfile"); + goto on_error; + } + idx->pack->mwf.fd = -1; if (index_path(&filename, idx, ".pack") < 0) goto on_error; + /* And don't forget to rename the packfile to its new place. */ - if (git_filebuf_commit_at(&idx->pack_file, filename.ptr) < 0) - return -1; + p_rename(idx->pack->pack_name, git_buf_cstr(&filename)); git_buf_free(&filename); + git_hash_ctx_cleanup(&ctx); return 0; on_error: @@ -1001,31 +1042,27 @@ void git_indexer_free(git_indexer *idx) { - khiter_t k; - unsigned int i; - struct entry *e; - struct delta_info *delta; - if (idx == NULL) return; - git_vector_foreach(&idx->objects, i, e) - git__free(e); - git_vector_free(&idx->objects); - - if (idx->pack) { - for (k = kh_begin(idx->pack->idx_cache); k != kh_end(idx->pack->idx_cache); k++) { - if (kh_exist(idx->pack->idx_cache, k)) - git__free(kh_value(idx->pack->idx_cache, k)); - } + git_vector_free_deep(&idx->objects); + + if (idx->pack && idx->pack->idx_cache) { + struct git_pack_entry *pentry; + kh_foreach_value( + idx->pack->idx_cache, pentry, { git__free(pentry); }); git_oidmap_free(idx->pack->idx_cache); } - git_vector_foreach(&idx->deltas, i, delta) - git__free(delta); - git_vector_free(&idx->deltas); - git_packfile_free(idx->pack); - git_filebuf_cleanup(&idx->pack_file); + git_vector_free_deep(&idx->deltas); + + if (!git_mutex_lock(&git__mwindow_mutex)) { + git_packfile_free(idx->pack); + git_mutex_unlock(&git__mwindow_mutex); + } + + git_hash_ctx_cleanup(&idx->trailer); + git_hash_ctx_cleanup(&idx->hash_ctx); git__free(idx); } diff -Nru libgit2-0.20.0/src/index.h libgit2-0.22.2/src/index.h --- libgit2-0.20.0/src/index.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/index.h 2015-03-24 16:10:45.000000000 +0000 @@ -21,17 +21,21 @@ git_refcount rc; char *index_file_path; - git_futils_filestamp stamp; + git_vector entries; - unsigned int on_disk:1; + git_mutex lock; /* lock held while entries is being changed */ + git_vector deleted; /* deleted entries if readers > 0 */ + git_atomic readers; /* number of active iterators */ + unsigned int on_disk:1; unsigned int ignore_case:1; unsigned int distrust_filemode:1; unsigned int no_symlinks:1; git_tree_cache *tree; + git_pool tree_pool; git_vector names; git_vector reuc; @@ -50,14 +54,44 @@ extern void git_index_entry__init_from_stat( git_index_entry *entry, struct stat *st, bool trust_mode); -extern size_t git_index__prefix_position(git_index *index, const char *path); +/* Index entry comparison functions for array sorting */ +extern int git_index_entry_cmp(const void *a, const void *b); +extern int git_index_entry_icmp(const void *a, const void *b); + +/* Index entry search functions for search using a search spec */ +extern int git_index_entry_srch(const void *a, const void *b); +extern int git_index_entry_isrch(const void *a, const void *b); + +/* Search index for `path`, returning GIT_ENOTFOUND if it does not exist + * (but not setting an error message). + * + * `at_pos` is set to the position where it is or would be inserted. + * Pass `path_len` as strlen of path or 0 to call strlen internally. + */ +extern int git_index__find_pos( + size_t *at_pos, git_index *index, const char *path, size_t path_len, int stage); -extern int git_index_entry__cmp(const void *a, const void *b); -extern int git_index_entry__cmp_icase(const void *a, const void *b); +extern void git_index__set_ignore_case(git_index *index, bool ignore_case); -extern int git_index__find( - size_t *at_pos, git_index *index, const char *path, int stage); +extern unsigned int git_index__create_mode(unsigned int mode); + +GIT_INLINE(const git_futils_filestamp *) git_index__filestamp(git_index *index) +{ + return &index->stamp; +} + +extern int git_index__changed_relative_to(git_index *index, const git_futils_filestamp *fs); + +/* Copy the current entries vector *and* increment the index refcount. + * Call `git_index__release_snapshot` when done. + */ +extern int git_index_snapshot_new(git_vector *snap, git_index *index); +extern void git_index_snapshot_release(git_vector *snap, git_index *index); + +/* Allow searching in a snapshot; entries must already be sorted! */ +extern int git_index_snapshot_find( + size_t *at_pos, git_vector *snap, git_vector_cmp entry_srch, + const char *path, size_t path_len, int stage); -extern void git_index__set_ignore_case(git_index *index, bool ignore_case); #endif diff -Nru libgit2-0.20.0/src/iterator.c libgit2-0.22.2/src/iterator.c --- libgit2-0.20.0/src/iterator.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/iterator.c 2015-03-24 16:10:45.000000000 +0000 @@ -10,7 +10,7 @@ #include "index.h" #include "ignore.h" #include "buffer.h" -#include "git2/submodule.h" +#include "submodule.h" #include #define ITERATOR_SET_CB(P,NAME_LC) do { \ @@ -447,7 +447,7 @@ te = tf->entries[tf->current]->te; ti->entry.mode = te->attr; - git_oid_cpy(&ti->entry.oid, &te->oid); + git_oid_cpy(&ti->entry.id, &te->oid); ti->entry.path = tree_iterator__current_filename(ti, te); GITERR_CHECK_ALLOC(ti->entry.path); @@ -644,6 +644,8 @@ git_iterator base; git_iterator_callbacks cb; git_index *index; + git_vector entries; + git_vector_cmp entry_srch; size_t current; /* when not in autoexpand mode, use these to represent "tree" state */ git_buf partial; @@ -654,10 +656,10 @@ static const git_index_entry *index_iterator__index_entry(index_iterator *ii) { - const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current); + const git_index_entry *ie = git_vector_get(&ii->entries, ii->current); if (ie != NULL && iterator__past_end(ii, ie->path)) { - ii->current = git_index_entrycount(ii->index); + ii->current = git_vector_length(&ii->entries); ie = NULL; } @@ -726,7 +728,7 @@ const git_index_entry **entry, git_iterator *self) { index_iterator *ii = (index_iterator *)self; - const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current); + const git_index_entry *ie = git_vector_get(&ii->entries, ii->current); if (ie != NULL && index_iterator__at_tree(ii)) { ii->tree_entry.path = ii->partial.ptr; @@ -744,14 +746,14 @@ static int index_iterator__at_end(git_iterator *self) { index_iterator *ii = (index_iterator *)self; - return (ii->current >= git_index_entrycount(ii->index)); + return (ii->current >= git_vector_length(&ii->entries)); } static int index_iterator__advance( const git_index_entry **entry, git_iterator *self) { index_iterator *ii = (index_iterator *)self; - size_t entrycount = git_index_entrycount(ii->index); + size_t entrycount = git_vector_length(&ii->entries); const git_index_entry *ie; if (!iterator__has_been_accessed(ii)) @@ -766,7 +768,7 @@ while (ii->current < entrycount) { ii->current++; - if (!(ie = git_index_get_byindex(ii->index, ii->current)) || + if (!(ie = git_vector_get(&ii->entries, ii->current)) || ii->base.prefixcomp(ie->path, ii->partial.ptr) != 0) break; } @@ -789,7 +791,7 @@ const git_index_entry **entry, git_iterator *self) { index_iterator *ii = (index_iterator *)self; - const git_index_entry *ie = git_index_get_byindex(ii->index, ii->current); + const git_index_entry *ie = git_vector_get(&ii->entries, ii->current); if (ie != NULL && index_iterator__at_tree(ii)) { if (ii->restore_terminator) @@ -815,8 +817,11 @@ if (iterator__reset_range(self, start, end) < 0) return -1; - ii->current = ii->base.start ? - git_index__prefix_position(ii->index, ii->base.start) : 0; + ii->current = 0; + + if (ii->base.start) + git_index_snapshot_find( + &ii->current, &ii->entries, ii->entry_srch, ii->base.start, 0, 0); if ((ie = index_iterator__skip_conflicts(ii)) == NULL) return 0; @@ -841,9 +846,8 @@ static void index_iterator__free(git_iterator *self) { index_iterator *ii = (index_iterator *)self; - git_index_free(ii->index); + git_index_snapshot_release(&ii->entries, ii->index); ii->index = NULL; - git_buf_free(&ii->partial); } @@ -854,18 +858,29 @@ const char *start, const char *end) { + int error = 0; index_iterator *ii = git__calloc(1, sizeof(index_iterator)); GITERR_CHECK_ALLOC(ii); + if ((error = git_index_snapshot_new(&ii->entries, index)) < 0) { + git__free(ii); + return error; + } + ii->index = index; + ITERATOR_BASE_INIT(ii, index, INDEX, git_index_owner(index)); - if (index->ignore_case) { - ii->base.flags |= GIT_ITERATOR_IGNORE_CASE; - ii->base.prefixcomp = git__prefixcmp_icase; + if ((error = iterator__update_ignore_case((git_iterator *)ii, flags)) < 0) { + git_iterator_free((git_iterator *)ii); + return error; } - ii->index = index; - GIT_REFCOUNT_INC(index); + ii->entry_srch = iterator__ignore_case(ii) ? + git_index_entry_isrch : git_index_entry_srch; + + git_vector_set_cmp(&ii->entries, iterator__ignore_case(ii) ? + git_index_entry_icmp : git_index_entry_cmp); + git_vector_sort(&ii->entries); git_buf_init(&ii->partial, 0); ii->tree_entry.mode = GIT_FILEMODE_TREE; @@ -873,7 +888,6 @@ index_iterator__reset((git_iterator *)ii, NULL, NULL); *iter = (git_iterator *)ii; - return 0; } @@ -883,6 +897,7 @@ fs_iterator_frame *next; git_vector entries; size_t index; + int is_ignored; }; typedef struct fs_iterator fs_iterator; @@ -920,12 +935,7 @@ static void fs_iterator__free_frame(fs_iterator_frame *ff) { - size_t i; - git_path_with_stat *path; - - git_vector_foreach(&ff->entries, i, path) - git__free(path); - git_vector_free(&ff->entries); + git_vector_free_deep(&ff->entries); git__free(ff); } @@ -991,9 +1001,8 @@ fi->base.start, fi->base.end, &ff->entries); if (error < 0) { - git_error last_error = {0}; - - giterr_detach(&last_error); + git_error_state last_error = { 0 }; + giterr_capture(&last_error, error); /* these callbacks may clear the error message */ fs_iterator__free_frame(ff); @@ -1001,18 +1010,14 @@ /* next time return value we skipped to */ fi->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS; - if (last_error.message) { - giterr_set_str(last_error.klass, last_error.message); - free(last_error.message); - } - - return error; + return giterr_restore(&last_error); } if (ff->entries.length == 0) { fs_iterator__free_frame(ff); return GIT_ENOTFOUND; } + fi->base.stat_calls += ff->entries.length; fs_iterator__seek_frame_start(fi, ff); @@ -1263,6 +1268,16 @@ fs_iterator fi; git_ignores ignores; int is_ignored; + + /* + * We may have a tree or the index+snapshot to compare against + * when checking for submodules. + */ + git_tree *tree; + git_index *index; + git_vector index_snapshot; + git_vector_cmp entry_srch; + } workdir_iterator; GIT_INLINE(bool) workdir_path_is_dotgit(const git_buf *path) @@ -1284,16 +1299,96 @@ return (len == 4 || path->ptr[len - 5] == '/'); } +/** + * Figure out if an entry is a submodule. + * + * We consider it a submodule if the path is listed as a submodule in + * either the tree or the index. + */ +static int is_submodule(workdir_iterator *wi, git_path_with_stat *ie) +{ + int error, is_submodule = 0; + + if (wi->tree) { + git_tree_entry *e; + + /* remove the trailing slash for finding */ + ie->path[ie->path_len-1] = '\0'; + error = git_tree_entry_bypath(&e, wi->tree, ie->path); + ie->path[ie->path_len-1] = '/'; + if (error < 0 && error != GIT_ENOTFOUND) + return 0; + if (!error) { + is_submodule = e->attr == GIT_FILEMODE_COMMIT; + git_tree_entry_free(e); + } + } + + if (!is_submodule && wi->index) { + git_index_entry *e; + size_t pos; + + error = git_index_snapshot_find(&pos, &wi->index_snapshot, wi->entry_srch, ie->path, ie->path_len-1, 0); + if (error < 0 && error != GIT_ENOTFOUND) + return 0; + + if (!error) { + e = git_vector_get(&wi->index_snapshot, pos); + + is_submodule = e->mode == GIT_FILEMODE_COMMIT; + } + } + + return is_submodule; +} + static int workdir_iterator__enter_dir(fs_iterator *fi) { - /* only push new ignores if this is not top level directory */ - if (fi->stack->next != NULL) { - workdir_iterator *wi = (workdir_iterator *)fi; + workdir_iterator *wi = (workdir_iterator *)fi; + fs_iterator_frame *ff = fi->stack; + size_t pos; + git_path_with_stat *entry; + bool found_submodules = false; + + /* check if this directory is ignored */ + if (git_ignore__lookup( + &ff->is_ignored, &wi->ignores, fi->path.ptr + fi->root_len) < 0) { + giterr_clear(); + ff->is_ignored = GIT_IGNORE_NOTFOUND; + } + + /* if this is not the top level directory... */ + if (ff->next != NULL) { ssize_t slash_pos = git_buf_rfind_next(&fi->path, '/'); + /* inherit ignored from parent if no rule specified */ + if (ff->is_ignored <= GIT_IGNORE_NOTFOUND) + ff->is_ignored = ff->next->is_ignored; + + /* push new ignores for files in this directory */ (void)git_ignore__push_dir(&wi->ignores, &fi->path.ptr[slash_pos + 1]); } + /* convert submodules to GITLINK and remove trailing slashes */ + git_vector_foreach(&ff->entries, pos, entry) { + if (!S_ISDIR(entry->st.st_mode) || !strcmp(GIT_DIR, entry->path)) + continue; + + if (is_submodule(wi, entry)) { + entry->st.st_mode = GIT_FILEMODE_COMMIT; + entry->path_len--; + entry->path[entry->path_len] = '\0'; + found_submodules = true; + } + } + + /* if we renamed submodules, re-sort and re-seek to start */ + if (found_submodules) { + git_vector_set_sorted(&ff->entries, 0); + git_vector_sort(&ff->entries); + fs_iterator__seek_frame_start(fi, ff); + } + return 0; } @@ -1306,7 +1401,6 @@ static int workdir_iterator__update_entry(fs_iterator *fi) { - int error = 0; workdir_iterator *wi = (workdir_iterator *)fi; /* skip over .git entries */ @@ -1314,21 +1408,7 @@ return GIT_ENOTFOUND; /* reset is_ignored since we haven't checked yet */ - wi->is_ignored = -1; - - /* check if apparent tree entries are actually submodules */ - if (fi->entry.mode != GIT_FILEMODE_TREE) - return 0; - - error = git_submodule_lookup(NULL, fi->base.repo, fi->entry.path); - if (error < 0) - giterr_clear(); - - /* mark submodule (or any dir with .git) as GITLINK and remove slash */ - if (!error || error == GIT_EEXISTS) { - fi->entry.mode = S_IFGITLINK; - fi->entry.path[strlen(fi->entry.path) - 1] = '\0'; - } + wi->is_ignored = GIT_IGNORE_UNCHECKED; return 0; } @@ -1336,6 +1416,9 @@ static void workdir_iterator__free(git_iterator *self) { workdir_iterator *wi = (workdir_iterator *)self; + if (wi->index) + git_index_snapshot_release(&wi->index_snapshot, wi->index); + git_tree_free(wi->tree); fs_iterator__free(self); git_ignore__free(&wi->ignores); } @@ -1344,6 +1427,8 @@ git_iterator **out, git_repository *repo, const char *repo_workdir, + git_index *index, + git_tree *tree, git_iterator_flag_t flags, const char *start, const char *end) @@ -1375,6 +1460,18 @@ return error; } + if (tree && (error = git_object_dup((git_object **)&wi->tree, (git_object *)tree)) < 0) + return error; + + wi->index = index; + if (index && (error = git_index_snapshot_new(&wi->index_snapshot, index)) < 0) { + git_iterator_free((git_iterator *)wi); + return error; + } + wi->entry_srch = iterator__ignore_case(wi) ? + git_index_entry_isrch : git_index_entry_srch; + + /* try to look up precompose and set flag if appropriate */ if (git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) < 0) giterr_clear(); @@ -1473,6 +1570,19 @@ return 0; } +static void workdir_iterator_update_is_ignored(workdir_iterator *wi) +{ + if (git_ignore__lookup( + &wi->is_ignored, &wi->ignores, wi->fi.entry.path) < 0) { + giterr_clear(); + wi->is_ignored = GIT_IGNORE_NOTFOUND; + } + + /* use ignore from containing frame stack */ + if (wi->is_ignored <= GIT_IGNORE_NOTFOUND) + wi->is_ignored = wi->fi.stack->is_ignored; +} + bool git_iterator_current_is_ignored(git_iterator *iter) { workdir_iterator *wi = (workdir_iterator *)iter; @@ -1480,14 +1590,22 @@ if (iter->type != GIT_ITERATOR_TYPE_WORKDIR) return false; - if (wi->is_ignored != -1) - return (bool)(wi->is_ignored != 0); + if (wi->is_ignored != GIT_IGNORE_UNCHECKED) + return (bool)(wi->is_ignored == GIT_IGNORE_TRUE); - if (git_ignore__lookup( - &wi->ignores, wi->fi.entry.path, &wi->is_ignored) < 0) - wi->is_ignored = true; + workdir_iterator_update_is_ignored(wi); - return (bool)wi->is_ignored; + return (bool)(wi->is_ignored == GIT_IGNORE_TRUE); +} + +bool git_iterator_current_tree_is_ignored(git_iterator *iter) +{ + workdir_iterator *wi = (workdir_iterator *)iter; + + if (iter->type != GIT_ITERATOR_TYPE_WORKDIR) + return false; + + return (bool)(wi->fi.stack->is_ignored == GIT_IGNORE_TRUE); } int git_iterator_cmp(git_iterator *iter, const char *path_prefix) @@ -1516,3 +1634,73 @@ return 0; } + +int git_iterator_advance_over_with_status( + const git_index_entry **entryptr, + git_iterator_status_t *status, + git_iterator *iter) +{ + int error = 0; + workdir_iterator *wi = (workdir_iterator *)iter; + char *base = NULL; + const git_index_entry *entry; + + *status = GIT_ITERATOR_STATUS_NORMAL; + + if (iter->type != GIT_ITERATOR_TYPE_WORKDIR) + return git_iterator_advance(entryptr, iter); + if ((error = git_iterator_current(&entry, iter)) < 0) + return error; + + if (!S_ISDIR(entry->mode)) { + workdir_iterator_update_is_ignored(wi); + if (wi->is_ignored == GIT_IGNORE_TRUE) + *status = GIT_ITERATOR_STATUS_IGNORED; + return git_iterator_advance(entryptr, iter); + } + + *status = GIT_ITERATOR_STATUS_EMPTY; + + base = git__strdup(entry->path); + GITERR_CHECK_ALLOC(base); + + /* scan inside directory looking for a non-ignored item */ + while (entry && !iter->prefixcomp(entry->path, base)) { + workdir_iterator_update_is_ignored(wi); + + /* if we found an explicitly ignored item, then update from + * EMPTY to IGNORED + */ + if (wi->is_ignored == GIT_IGNORE_TRUE) + *status = GIT_ITERATOR_STATUS_IGNORED; + else if (S_ISDIR(entry->mode)) { + error = git_iterator_advance_into(&entry, iter); + + if (!error) + continue; + else if (error == GIT_ENOTFOUND) { + error = 0; + wi->is_ignored = GIT_IGNORE_TRUE; /* mark empty dirs ignored */ + } else + break; /* real error, stop here */ + } else { + /* we found a non-ignored item, treat parent as untracked */ + *status = GIT_ITERATOR_STATUS_NORMAL; + break; + } + + if ((error = git_iterator_advance(&entry, iter)) < 0) + break; + } + + /* wrap up scan back to base directory */ + while (entry && !iter->prefixcomp(entry->path, base)) + if ((error = git_iterator_advance(&entry, iter)) < 0) + break; + + *entryptr = entry; + git__free(base); + + return error; +} + diff -Nru libgit2-0.20.0/src/iterator.h libgit2-0.22.2/src/iterator.h --- libgit2-0.20.0/src/iterator.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/iterator.h 2015-03-24 16:10:45.000000000 +0000 @@ -52,6 +52,7 @@ char *start; char *end; int (*prefixcomp)(const char *str, const char *prefix); + size_t stat_calls; unsigned int flags; }; @@ -85,6 +86,8 @@ git_iterator **out, git_repository *repo, const char *repo_workdir, + git_index *index, + git_tree *tree, git_iterator_flag_t flags, const char *start, const char *end); @@ -95,11 +98,13 @@ GIT_INLINE(int) git_iterator_for_workdir( git_iterator **out, git_repository *repo, + git_index *index, + git_tree *tree, git_iterator_flag_t flags, const char *start, const char *end) { - return git_iterator_for_workdir_ext(out, repo, NULL, flags, start, end); + return git_iterator_for_workdir_ext(out, repo, NULL, index, tree, flags, start, end); } /* for filesystem iterators, you have to explicitly pass in the ignore_case @@ -244,6 +249,8 @@ extern bool git_iterator_current_is_ignored(git_iterator *iter); +extern bool git_iterator_current_tree_is_ignored(git_iterator *iter); + extern int git_iterator_cmp( git_iterator *iter, const char *path_prefix); @@ -258,4 +265,23 @@ /* Return index pointer if index iterator, else NULL */ extern git_index *git_iterator_get_index(git_iterator *iter); +typedef enum { + GIT_ITERATOR_STATUS_NORMAL = 0, + GIT_ITERATOR_STATUS_IGNORED = 1, + GIT_ITERATOR_STATUS_EMPTY = 2 +} git_iterator_status_t; + +/* Advance over a directory and check if it contains no files or just + * ignored files. + * + * In a tree or the index, all directories will contain files, but in the + * working directory it is possible to have an empty directory tree or a + * tree that only contains ignored files. Many Git operations treat these + * cases specially. This advances over a directory (presumably an + * untracked directory) but checks during the scan if there are any files + * and any non-ignored files. + */ +extern int git_iterator_advance_over_with_status( + const git_index_entry **entry, git_iterator_status_t *status, git_iterator *iter); + #endif diff -Nru libgit2-0.20.0/src/merge.c libgit2-0.22.2/src/merge.c --- libgit2-0.20.0/src/merge.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/merge.c 2015-03-24 16:10:45.000000000 +0000 @@ -22,10 +22,12 @@ #include "tree.h" #include "merge_file.h" #include "blob.h" -#include "hashsig.h" #include "oid.h" #include "index.h" #include "filebuf.h" +#include "config.h" +#include "oidarray.h" +#include "annotated_commit.h" #include "git2/types.h" #include "git2/repository.h" @@ -38,9 +40,13 @@ #include "git2/signature.h" #include "git2/config.h" #include "git2/tree.h" +#include "git2/oidarray.h" +#include "git2/annotated_commit.h" #include "git2/sys/index.h" +#include "git2/sys/hashsig.h" #define GIT_MERGE_INDEX_ENTRY_EXISTS(X) ((X).mode != 0) +#define GIT_MERGE_INDEX_ENTRY_ISFILE(X) S_ISREG((X).mode) typedef enum { TREE_IDX_ANCESTOR = 0, @@ -58,16 +64,14 @@ /* Merge base computation */ -int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[]) +int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, size_t length, const git_oid input_array[]) { - git_revwalk *walk; + git_revwalk *walk = NULL; git_vector list; git_commit_list *result = NULL; + git_commit_list_node *commit; int error = -1; unsigned int i; - git_commit_list_node *commit; - - assert(out && repo && input_array); if (length < 2) { giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %u.", length); @@ -78,41 +82,121 @@ return -1; if (git_revwalk_new(&walk, repo) < 0) - goto cleanup; + goto on_error; for (i = 1; i < length; i++) { commit = git_revwalk__commit_lookup(walk, &input_array[i]); if (commit == NULL) - goto cleanup; + goto on_error; git_vector_insert(&list, commit); } commit = git_revwalk__commit_lookup(walk, &input_array[0]); if (commit == NULL) - goto cleanup; + goto on_error; if (git_merge__bases_many(&result, walk, commit, &list) < 0) - goto cleanup; + goto on_error; if (!result) { giterr_set(GITERR_MERGE, "No merge base found"); error = GIT_ENOTFOUND; - goto cleanup; + goto on_error; } + *out = result; + *walk_out = walk; + + git_vector_free(&list); + return 0; + +on_error: + git_vector_free(&list); + git_revwalk_free(walk); + return error; +} + +int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[]) +{ + git_revwalk *walk; + git_commit_list *result = NULL; + int error = 0; + + assert(out && repo && input_array); + + if ((error = merge_bases_many(&result, &walk, repo, length, input_array)) < 0) + return error; + git_oid_cpy(out, &result->item->oid); - error = 0; + git_commit_list_free(&result); + git_revwalk_free(walk); + + return 0; +} + +int git_merge_bases_many(git_oidarray *out, git_repository *repo, size_t length, const git_oid input_array[]) +{ + git_revwalk *walk; + git_commit_list *list, *result = NULL; + int error = 0; + git_array_oid_t array; + + assert(out && repo && input_array); + + if ((error = merge_bases_many(&result, &walk, repo, length, input_array)) < 0) + return error; + + git_array_init(array); + + list = result; + while (list) { + git_oid *id = git_array_alloc(array); + if (id == NULL) { + error = -1; + goto cleanup; + } + + git_oid_cpy(id, &list->item->oid); + list = list->next; + } + + git_oidarray__from_array(out, &array); cleanup: git_commit_list_free(&result); git_revwalk_free(walk); - git_vector_free(&list); + return error; } -int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const git_oid *two) +int git_merge_base_octopus(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[]) +{ + git_oid result; + unsigned int i; + int error = -1; + + assert(out && repo && input_array); + + if (length < 2) { + giterr_set(GITERR_INVALID, "At least two commits are required to find an ancestor. Provided 'length' was %u.", length); + return -1; + } + + result = input_array[0]; + for (i = 1; i < length; i++) { + error = git_merge_base(&result, repo, &result, &input_array[i]); + if (error < 0) + return error; + } + + *out = result; + + return 0; +} + +static int merge_bases(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, const git_oid *one, const git_oid *two) { git_revwalk *walk; git_vector list; @@ -146,23 +230,73 @@ return GIT_ENOTFOUND; } + *out = result; + *walk_out = walk; + + return 0; + +on_error: + git_revwalk_free(walk); + return -1; + +} + +int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const git_oid *two) +{ + int error; + git_revwalk *walk; + git_commit_list *result; + + if ((error = merge_bases(&result, &walk, repo, one, two)) < 0) + return error; + git_oid_cpy(out, &result->item->oid); git_commit_list_free(&result); git_revwalk_free(walk); return 0; +} + +int git_merge_bases(git_oidarray *out, git_repository *repo, const git_oid *one, const git_oid *two) +{ + int error; + git_revwalk *walk; + git_commit_list *result, *list; + git_array_oid_t array; + + git_array_init(array); + + if ((error = merge_bases(&result, &walk, repo, one, two)) < 0) + return error; + + list = result; + while (list) { + git_oid *id = git_array_alloc(array); + if (id == NULL) + goto on_error; + + git_oid_cpy(id, &list->item->oid); + list = list->next; + } + + git_oidarray__from_array(out, &array); + git_commit_list_free(&result); + git_revwalk_free(walk); + + return 0; on_error: + git_commit_list_free(&result); git_revwalk_free(walk); return -1; } static int interesting(git_pqueue *list) { - unsigned int i; - /* element 0 isn't used - we need to start at 1 */ - for (i = 1; i < list->size; i++) { - git_commit_list_node *commit = list->d[i]; + size_t i; + + for (i = 0; i < git_pqueue_size(list); i++) { + git_commit_list_node *commit = git_pqueue_get(list, i); if ((commit->flags & STALE) == 0) return 1; } @@ -178,13 +312,19 @@ git_commit_list *result = NULL, *tmp = NULL; git_pqueue list; + /* If there's only the one commit, there can be no merge bases */ + if (twos->length == 0) { + *out = NULL; + return 0; + } + /* if the commit is repeated, we have a our merge base already */ git_vector_foreach(twos, i, two) { if (one == two) return git_commit_list_insert(one, out) ? 0 : -1; } - if (git_pqueue_init(&list, twos->length * 2, git_commit_list_time_cmp) < 0) + if (git_pqueue_init(&list, 0, twos->length * 2, git_commit_list_time_cmp) < 0) return -1; if (git_commit_list_parse(walk, one) < 0) @@ -195,18 +335,22 @@ return -1; git_vector_foreach(twos, i, two) { - git_commit_list_parse(walk, two); + if (git_commit_list_parse(walk, two) < 0) + return -1; + two->flags |= PARENT2; + if (git_pqueue_insert(&list, two) < 0) return -1; } /* as long as there are non-STALE commits */ while (interesting(&list)) { - git_commit_list_node *commit; + git_commit_list_node *commit = git_pqueue_pop(&list); int flags; - commit = git_pqueue_pop(&list); + if (commit == NULL) + break; flags = commit->flags & (PARENT1 | PARENT2 | STALE); if (flags == (PARENT1 | PARENT2)) { @@ -253,7 +397,8 @@ return 0; } -int git_repository_mergehead_foreach(git_repository *repo, +int git_repository_mergehead_foreach( + git_repository *repo, git_repository_mergehead_foreach_cb cb, void *payload) { @@ -285,8 +430,8 @@ if ((error = git_oid_fromstr(&oid, line)) < 0) goto cleanup; - if (cb(&oid, payload) != 0) { - error = GIT_EUSER; + if ((error = cb(&oid, payload)) != 0) { + giterr_set_after_callback(error); goto cleanup; } @@ -314,7 +459,7 @@ return (b->path == NULL) ? 0 : 1; if ((value = a->mode - b->mode) == 0 && - (value = git_oid__cmp(&a->oid, &b->oid)) == 0) + (value = git_oid__cmp(&a->id, &b->id)) == 0) value = strcmp(a->path, b->path); return value; @@ -445,7 +590,6 @@ return error; } - static int merge_conflict_resolve_one_renamed( int *resolved, git_merge_diff_list *diff_list, @@ -476,12 +620,12 @@ conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED) return 0; - ours_changed = (git_oid__cmp(&conflict->ancestor_entry.oid, &conflict->our_entry.oid) != 0); - theirs_changed = (git_oid__cmp(&conflict->ancestor_entry.oid, &conflict->their_entry.oid) != 0); + ours_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->our_entry.id) != 0); + theirs_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->their_entry.id) != 0); /* if both are modified (and not to a common target) require a merge */ if (ours_changed && theirs_changed && - git_oid__cmp(&conflict->our_entry.oid, &conflict->their_entry.oid) != 0) + git_oid__cmp(&conflict->our_entry.id, &conflict->their_entry.id) != 0) return 0; if ((merged = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL) @@ -509,12 +653,11 @@ int *resolved, git_merge_diff_list *diff_list, const git_merge_diff *conflict, - unsigned int automerge_flags) + unsigned int merge_file_favor) { - 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_result result = GIT_MERGE_FILE_RESULT_INIT; + const git_index_entry *ancestor = NULL, *ours = NULL, *theirs = NULL; + git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT; + git_merge_file_result result = {0}; git_index_entry *index_entry; git_odb *odb = NULL; git_oid automerge_oid; @@ -524,13 +667,20 @@ *resolved = 0; - if (automerge_flags == GIT_MERGE_AUTOMERGE_NONE) + if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) || + !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry)) return 0; /* Reject D/F conflicts */ if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE) return 0; + /* Reject submodules. */ + if (S_ISGITLINK(conflict->ancestor_entry.mode) || + S_ISGITLINK(conflict->our_entry.mode) || + S_ISGITLINK(conflict->their_entry.mode)) + return 0; + /* Reject link/file conflicts. */ if ((S_ISLNK(conflict->ancestor_entry.mode) ^ S_ISLNK(conflict->our_entry.mode)) || (S_ISLNK(conflict->ancestor_entry.mode) ^ S_ISLNK(conflict->their_entry.mode))) @@ -546,13 +696,23 @@ strcmp(conflict->ancestor_entry.path, conflict->their_entry.path) != 0) return 0; + /* Reject binary conflicts */ + if (conflict->binary) + return 0; + + ancestor = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ? + &conflict->ancestor_entry : NULL; + ours = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ? + &conflict->our_entry : NULL; + theirs = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ? + &conflict->their_entry : NULL; + + opts.favor = merge_file_favor; + if ((error = git_repository_odb(&odb, diff_list->repo)) < 0 || - (error = git_merge_file_input_from_index_entry(&ancestor, diff_list->repo, &conflict->ancestor_entry)) < 0 || - (error = git_merge_file_input_from_index_entry(&ours, diff_list->repo, &conflict->our_entry)) < 0 || - (error = git_merge_file_input_from_index_entry(&theirs, diff_list->repo, &conflict->their_entry)) < 0 || - (error = git_merge_files(&result, &ancestor, &ours, &theirs, automerge_flags)) < 0 || + (error = git_merge_file_from_index(&result, diff_list->repo, ancestor, ours, theirs, &opts)) < 0 || !result.automergeable || - (error = git_odb_write(&automerge_oid, odb, result.data, result.len, GIT_OBJ_BLOB)) < 0) + (error = git_odb_write(&automerge_oid, odb, result.ptr, result.len, GIT_OBJ_BLOB)) < 0) goto done; if ((index_entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL) @@ -563,7 +723,7 @@ index_entry->file_size = result.len; index_entry->mode = result.mode; - git_oid_cpy(&index_entry->oid, &automerge_oid); + git_oid_cpy(&index_entry->id, &automerge_oid); git_vector_insert(&diff_list->staged, index_entry); git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict); @@ -571,9 +731,6 @@ *resolved = 1; done: - git_merge_file_input_free(&ancestor); - git_merge_file_input_free(&ours); - git_merge_file_input_free(&theirs); git_merge_file_result_free(&result); git_odb_free(odb); @@ -584,7 +741,7 @@ int *out, git_merge_diff_list *diff_list, const git_merge_diff *conflict, - unsigned int automerge_flags) + unsigned int merge_file_favor) { int resolved = 0; int error = 0; @@ -594,16 +751,14 @@ if ((error = merge_conflict_resolve_trivial(&resolved, diff_list, conflict)) < 0) goto done; - if (automerge_flags != GIT_MERGE_AUTOMERGE_NONE) { - if (!resolved && (error = merge_conflict_resolve_one_removed(&resolved, diff_list, conflict)) < 0) - goto done; + if (!resolved && (error = merge_conflict_resolve_one_removed(&resolved, diff_list, conflict)) < 0) + goto done; - if (!resolved && (error = merge_conflict_resolve_one_renamed(&resolved, diff_list, conflict)) < 0) - goto done; + if (!resolved && (error = merge_conflict_resolve_one_renamed(&resolved, diff_list, conflict)) < 0) + goto done; - if (!resolved && (error = merge_conflict_resolve_automerge(&resolved, diff_list, conflict, automerge_flags)) < 0) - goto done; - } + if (!resolved && (error = merge_conflict_resolve_automerge(&resolved, diff_list, conflict, merge_file_favor)) < 0) + goto done; *out = resolved; @@ -625,7 +780,7 @@ git_index_entry *b, size_t b_idx, void **cache, - const git_merge_tree_opts *opts) + const git_merge_options *opts) { GIT_UNUSED(repo); GIT_UNUSED(a_idx); @@ -633,7 +788,7 @@ GIT_UNUSED(cache); GIT_UNUSED(opts); - if (git_oid__cmp(&a->oid, &b->oid) == 0) + if (git_oid__cmp(&a->id, &b->id) == 0) return 100; return 0; @@ -643,7 +798,7 @@ void **out, git_repository *repo, git_index_entry *entry, - const git_merge_tree_opts *opts) + const git_merge_options *opts) { git_blob *blob; git_diff_file diff_file = {{{0}}}; @@ -652,10 +807,10 @@ *out = NULL; - if ((error = git_blob_lookup(&blob, repo, &entry->oid)) < 0) + if ((error = git_blob_lookup(&blob, repo, &entry->id)) < 0) return error; - git_oid_cpy(&diff_file.oid, &entry->oid); + git_oid_cpy(&diff_file.id, &entry->id); diff_file.path = entry->path; diff_file.size = entry->file_size; diff_file.mode = entry->mode; @@ -683,7 +838,7 @@ git_index_entry *b, size_t b_idx, void **cache, - const git_merge_tree_opts *opts) + const git_merge_options *opts) { int score = 0; int error = 0; @@ -720,9 +875,9 @@ 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_tree_opts *), + 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_tree_opts *opts) + const git_merge_options *opts) { size_t i, j; git_merge_diff *conflict_src, *conflict_tgt; @@ -823,7 +978,7 @@ bool theirs_renamed, size_t theirs_source_idx, git_merge_diff *target, - const git_merge_tree_opts *opts) + const git_merge_options *opts) { git_merge_diff *ours_source = NULL, *theirs_source = NULL; @@ -893,7 +1048,7 @@ git_merge_diff_list *diff_list, struct merge_diff_similarity *similarity_ours, struct merge_diff_similarity *similarity_theirs, - const git_merge_tree_opts *opts) + const git_merge_options *opts) { size_t i; bool ours_renamed = 0, theirs_renamed = 0; @@ -950,10 +1105,12 @@ } } -static int merge_diff_empty(const git_vector *conflicts, size_t idx) +static int merge_diff_empty(const git_vector *conflicts, size_t idx, void *p) { git_merge_diff *conflict = conflicts->contents[idx]; + GIT_UNUSED(p); + return (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) && !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) && !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry)); @@ -983,7 +1140,7 @@ int git_merge_diff_list__find_renames( git_repository *repo, git_merge_diff_list *diff_list, - const git_merge_tree_opts *opts) + const git_merge_options *opts) { struct merge_diff_similarity *similarity_ours, *similarity_theirs; void **cache = NULL; @@ -1034,7 +1191,7 @@ merge_diff_list_coalesce_renames(diff_list, similarity_ours, similarity_theirs, opts); /* And remove any entries that were merged and are now empty. */ - git_vector_remove_matching(&diff_list->conflicts, merge_diff_empty); + git_vector_remove_matching(&diff_list->conflicts, merge_diff_empty, NULL); done: if (cache != NULL) { @@ -1145,7 +1302,45 @@ return 0; } -GIT_INLINE(int) index_entry_dup( +GIT_INLINE(int) merge_diff_detect_binary( + git_repository *repo, + git_merge_diff *conflict) +{ + git_blob *ancestor_blob = NULL, *our_blob = NULL, *their_blob = NULL; + int error = 0; + + if (GIT_MERGE_INDEX_ENTRY_ISFILE(conflict->ancestor_entry)) { + if ((error = git_blob_lookup(&ancestor_blob, repo, &conflict->ancestor_entry.id)) < 0) + goto done; + + conflict->binary = git_blob_is_binary(ancestor_blob); + } + + if (!conflict->binary && + GIT_MERGE_INDEX_ENTRY_ISFILE(conflict->our_entry)) { + if ((error = git_blob_lookup(&our_blob, repo, &conflict->our_entry.id)) < 0) + goto done; + + conflict->binary = git_blob_is_binary(our_blob); + } + + if (!conflict->binary && + GIT_MERGE_INDEX_ENTRY_ISFILE(conflict->their_entry)) { + if ((error = git_blob_lookup(&their_blob, repo, &conflict->their_entry.id)) < 0) + goto done; + + conflict->binary = git_blob_is_binary(their_blob); + } + +done: + git_blob_free(ancestor_blob); + git_blob_free(our_blob); + git_blob_free(their_blob); + + return error; +} + +GIT_INLINE(int) index_entry_dup_pool( git_index_entry *out, git_pool *pool, const git_index_entry *src) @@ -1174,7 +1369,7 @@ return GIT_DELTA_TYPECHANGE; else if(S_ISLNK(ancestor->mode) ^ S_ISLNK(other->mode)) return GIT_DELTA_TYPECHANGE; - else if (git_oid__cmp(&ancestor->oid, &other->oid) || + else if (git_oid__cmp(&ancestor->id, &other->id) || ancestor->mode != other->mode) return GIT_DELTA_MODIFIED; @@ -1191,9 +1386,9 @@ if ((conflict = git_pool_malloc(pool, sizeof(git_merge_diff))) == NULL) return NULL; - if (index_entry_dup(&conflict->ancestor_entry, pool, entries[TREE_IDX_ANCESTOR]) < 0 || - index_entry_dup(&conflict->our_entry, pool, entries[TREE_IDX_OURS]) < 0 || - index_entry_dup(&conflict->their_entry, pool, entries[TREE_IDX_THEIRS]) < 0) + if (index_entry_dup_pool(&conflict->ancestor_entry, pool, entries[TREE_IDX_ANCESTOR]) < 0 || + index_entry_dup_pool(&conflict->our_entry, pool, entries[TREE_IDX_OURS]) < 0 || + index_entry_dup_pool(&conflict->their_entry, pool, entries[TREE_IDX_THEIRS]) < 0) return NULL; conflict->our_status = merge_delta_type_from_index_entries( @@ -1206,7 +1401,7 @@ /* Merge trees */ -static int merge_index_insert_conflict( +static int merge_diff_list_insert_conflict( git_merge_diff_list *diff_list, struct merge_diff_df_data *merge_df_data, const git_index_entry *tree_items[3]) @@ -1216,13 +1411,14 @@ if ((conflict = merge_diff_from_index_entries(diff_list, tree_items)) == NULL || merge_diff_detect_type(conflict) < 0 || merge_diff_detect_df_conflict(merge_df_data, conflict) < 0 || + merge_diff_detect_binary(diff_list->repo, conflict) < 0 || git_vector_insert(&diff_list->conflicts, conflict) < 0) return -1; return 0; } -static int merge_index_insert_unmodified( +static int merge_diff_list_insert_unmodified( git_merge_diff_list *diff_list, const git_index_entry *tree_items[3]) { @@ -1232,7 +1428,7 @@ entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry)); GITERR_CHECK_ALLOC(entry); - if ((error = index_entry_dup(entry, &diff_list->pool, tree_items[0])) >= 0) + if ((error = index_entry_dup_pool(entry, &diff_list->pool, tree_items[0])) >= 0) error = git_vector_insert(&diff_list->staged, entry); return error; @@ -1246,13 +1442,13 @@ { git_iterator *iterators[3] = {0}; const git_index_entry *items[3] = {0}, *best_cur_item, *cur_items[3]; - git_vector_cmp entry_compare = git_index_entry__cmp; + git_vector_cmp entry_compare = git_index_entry_cmp; struct merge_diff_df_data df_data = {0}; int cur_item_modified; size_t i, j; int error = 0; - assert(diff_list && our_tree && their_tree); + assert(diff_list && (our_tree || their_tree)); if ((error = git_iterator_for_tree(&iterators[TREE_IDX_ANCESTOR], (git_tree *)ancestor_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || (error = git_iterator_for_tree(&iterators[TREE_IDX_OURS], (git_tree *)our_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || @@ -1262,6 +1458,7 @@ /* Set up the iterators */ for (i = 0; i < 3; i++) { error = git_iterator_current(&items[i], iterators[i]); + if (error < 0 && error != GIT_ITEROVER) goto done; } @@ -1313,9 +1510,9 @@ break; if (cur_item_modified) - error = merge_index_insert_conflict(diff_list, &df_data, cur_items); + error = merge_diff_list_insert_conflict(diff_list, &df_data, cur_items); else - error = merge_index_insert_unmodified(diff_list, cur_items); + error = merge_diff_list_insert_unmodified(diff_list, cur_items); if (error < 0) goto done; @@ -1325,6 +1522,7 @@ continue; error = git_iterator_advance(&items[i], iterators[i]); + if (error < 0 && error != GIT_ITEROVER) goto done; } @@ -1352,8 +1550,10 @@ if (git_vector_init(&diff_list->staged, 0, NULL) < 0 || git_vector_init(&diff_list->conflicts, 0, NULL) < 0 || git_vector_init(&diff_list->resolved, 0, NULL) < 0 || - git_pool_init(&diff_list->pool, 1, 0) < 0) + git_pool_init(&diff_list->pool, 1, 0) < 0) { + git_merge_diff_list__free(diff_list); return NULL; + } return diff_list; } @@ -1370,10 +1570,10 @@ git__free(diff_list); } -static int merge_tree_normalize_opts( +static int merge_normalize_opts( git_repository *repo, - git_merge_tree_opts *opts, - const git_merge_tree_opts *given) + git_merge_options *opts, + const git_merge_options *given) { git_config *cfg = NULL; int error = 0; @@ -1384,9 +1584,9 @@ return error; if (given != NULL) - memcpy(opts, given, sizeof(git_merge_tree_opts)); + memcpy(opts, given, sizeof(git_merge_options)); else { - git_merge_tree_opts init = GIT_MERGE_TREE_OPTS_INIT; + git_merge_options init = GIT_MERGE_OPTIONS_INIT; memcpy(opts, &init, sizeof(init)); opts->flags = GIT_MERGE_TREE_FIND_RENAMES; @@ -1394,19 +1594,13 @@ } if (!opts->target_limit) { - int32_t limit = 0; - - opts->target_limit = GIT_MERGE_TREE_TARGET_LIMIT; - - if (git_config_get_int32(&limit, cfg, "merge.renameLimit") < 0) { - giterr_clear(); + int limit = git_config__get_int_force(cfg, "merge.renamelimit", 0); - if (git_config_get_int32(&limit, cfg, "diff.renameLimit") < 0) - giterr_clear(); - } + if (!limit) + limit = git_config__get_int_force(cfg, "diff.renamelimit", 0); - if (limit > 0) - opts->target_limit = limit; + opts->target_limit = (limit <= 0) ? + GIT_MERGE_TREE_TARGET_LIMIT : (unsigned int)limit; } /* assign the internal metric with whitespace flag as payload */ @@ -1452,7 +1646,7 @@ } mode[idx] = entry->mode; - oid[idx] = &entry->oid; + oid[idx] = &entry->id; return git_index_reuc_add(index, entry->path, mode[0], oid[0], mode[1], oid[1], mode[2], oid[2]); @@ -1560,20 +1754,22 @@ const git_tree *ancestor_tree, const git_tree *our_tree, const git_tree *their_tree, - const git_merge_tree_opts *given_opts) + const git_merge_options *given_opts) { git_merge_diff_list *diff_list; - git_merge_tree_opts opts; + git_merge_options opts; git_merge_diff *conflict; git_vector changes; size_t i; int error = 0; - assert(out && repo && our_tree && their_tree); + assert(out && repo && (our_tree || their_tree)); *out = NULL; - if ((error = merge_tree_normalize_opts(repo, &opts, given_opts)) < 0) + GITERR_CHECK_VERSION(given_opts, GIT_MERGE_OPTIONS_VERSION, "git_merge_options"); + + if ((error = merge_normalize_opts(repo, &opts, given_opts)) < 0) return error; diff_list = git_merge_diff_list__alloc(repo); @@ -1589,7 +1785,7 @@ git_vector_foreach(&changes, i, conflict) { int resolved = 0; - if ((error = merge_conflict_resolve(&resolved, diff_list, conflict, opts.automerge_flags)) < 0) + if ((error = merge_conflict_resolve(&resolved, diff_list, conflict, opts.file_favor)) < 0) goto done; if (!resolved) @@ -1607,34 +1803,45 @@ return error; } -/* Merge setup / cleanup */ - -static int write_orig_head( +int git_merge_commits( + git_index **out, git_repository *repo, - const git_merge_head *our_head) -{ - git_filebuf file = GIT_FILEBUF_INIT; - git_buf file_path = GIT_BUF_INIT; + const git_commit *our_commit, + const git_commit *their_commit, + const git_merge_options *opts) +{ + git_oid ancestor_oid; + git_commit *ancestor_commit = NULL; + git_tree *our_tree = NULL, *their_tree = NULL, *ancestor_tree = NULL; int error = 0; - assert(repo && our_head); - - if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_ORIG_HEAD_FILE)) == 0 && - (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) == 0 && - (error = git_filebuf_printf(&file, "%s\n", our_head->oid_str)) == 0) - error = git_filebuf_commit(&file); + if ((error = git_merge_base(&ancestor_oid, repo, git_commit_id(our_commit), git_commit_id(their_commit))) < 0 && + error == GIT_ENOTFOUND) + giterr_clear(); + else if (error < 0 || + (error = git_commit_lookup(&ancestor_commit, repo, &ancestor_oid)) < 0 || + (error = git_commit_tree(&ancestor_tree, ancestor_commit)) < 0) + goto done; - if (error < 0) - git_filebuf_cleanup(&file); + if ((error = git_commit_tree(&our_tree, our_commit)) < 0 || + (error = git_commit_tree(&their_tree, their_commit)) < 0 || + (error = git_merge_trees(out, repo, ancestor_tree, our_tree, their_tree, opts)) < 0) + goto done; - git_buf_free(&file_path); +done: + git_commit_free(ancestor_commit); + git_tree_free(our_tree); + git_tree_free(their_tree); + git_tree_free(ancestor_tree); return error; } +/* Merge setup / cleanup */ + static int write_merge_head( git_repository *repo, - const git_merge_head *heads[], + const git_annotated_commit *heads[], size_t heads_len) { git_filebuf file = GIT_FILEBUF_INIT; @@ -1649,7 +1856,7 @@ goto cleanup; for (i = 0; i < heads_len; i++) { - if ((error = git_filebuf_printf(&file, "%s\n", heads[i]->oid_str)) < 0) + if ((error = git_filebuf_printf(&file, "%s\n", heads[i]->id_str)) < 0) goto cleanup; } @@ -1664,31 +1871,20 @@ return error; } -static int write_merge_mode(git_repository *repo, unsigned int flags) +static int write_merge_mode(git_repository *repo) { git_filebuf file = GIT_FILEBUF_INIT; git_buf file_path = GIT_BUF_INIT; int error = 0; - /* For future expansion */ - GIT_UNUSED(flags); - assert(repo); if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MODE_FILE)) < 0 || (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) < 0) goto cleanup; - /* - * no-ff is the only thing allowed here at present. One would - * presume they would be space-delimited when there are more, but - * this needs to be revisited. - */ - - if (flags & GIT_MERGE_NO_FASTFORWARD) { - if ((error = git_filebuf_write(&file, "no-ff", 5)) < 0) - goto cleanup; - } + if ((error = git_filebuf_write(&file, "no-ff", 5)) < 0) + goto cleanup; error = git_filebuf_commit(&file); @@ -1702,7 +1898,7 @@ } struct merge_msg_entry { - const git_merge_head *merge_head; + const git_annotated_commit *merge_head; bool written; }; @@ -1890,7 +2086,7 @@ static int write_merge_msg( git_repository *repo, - const git_merge_head *heads[], + const git_annotated_commit *heads[], size_t heads_len) { git_filebuf file = GIT_FILEBUF_INIT; @@ -1936,7 +2132,7 @@ if ((error = git_filebuf_printf(&file, "%scommit '%s'", (i > 0) ? "; " : "", - entries[i].merge_head->oid_str)) < 0) + entries[i].merge_head->id_str)) < 0) goto cleanup; entries[i].written = 1; @@ -1984,7 +2180,7 @@ continue; if ((error = git_filebuf_printf(&file, "; commit '%s'", - entries[i].merge_head->oid_str)) < 0) + entries[i].merge_head->id_str)) < 0) goto cleanup; } @@ -2004,13 +2200,32 @@ return error; } +int git_merge__setup( + git_repository *repo, + const git_annotated_commit *our_head, + const git_annotated_commit *heads[], + size_t heads_len) +{ + int error = 0; + + assert (repo && our_head && heads); + + if ((error = git_repository__set_orig_head(repo, git_annotated_commit_id(our_head))) == 0 && + (error = write_merge_head(repo, heads, heads_len)) == 0 && + (error = write_merge_mode(repo)) == 0) { + error = write_merge_msg(repo, heads, heads_len); + } + + return error; +} + /* Merge branches */ static int merge_ancestor_head( - git_merge_head **ancestor_head, + git_annotated_commit **ancestor_head, git_repository *repo, - const git_merge_head *our_head, - const git_merge_head **their_heads, + const git_annotated_commit *our_head, + const git_annotated_commit **their_heads, size_t their_heads_len) { git_oid *oids, ancestor_oid; @@ -2025,49 +2240,18 @@ git_oid_cpy(&oids[0], git_commit_id(our_head->commit)); for (i = 0; i < their_heads_len; i++) - git_oid_cpy(&oids[i + 1], &their_heads[i]->oid); + git_oid_cpy(&oids[i + 1], git_annotated_commit_id(their_heads[i])); if ((error = git_merge_base_many(&ancestor_oid, repo, their_heads_len + 1, oids)) < 0) goto on_error; - error = git_merge_head_from_oid(ancestor_head, repo, &ancestor_oid); + error = git_annotated_commit_lookup(ancestor_head, repo, &ancestor_oid); on_error: git__free(oids); return error; } -GIT_INLINE(bool) merge_check_uptodate( - git_merge_result *result, - const git_merge_head *ancestor_head, - const git_merge_head *their_head) -{ - if (git_oid_cmp(&ancestor_head->oid, &their_head->oid) == 0) { - result->is_uptodate = 1; - return true; - } - - return false; -} - -GIT_INLINE(bool) merge_check_fastforward( - git_merge_result *result, - const git_merge_head *ancestor_head, - const git_merge_head *our_head, - const git_merge_head *their_head, - unsigned int flags) -{ - if ((flags & GIT_MERGE_NO_FASTFORWARD) == 0 && - git_oid_cmp(&ancestor_head->oid, &our_head->oid) == 0) { - result->is_fastforward = 1; - git_oid_cpy(&result->fastforward_oid, &their_head->oid); - - return true; - } - - return false; -} - const char *merge_their_label(const char *branchname) { const char *slash; @@ -2081,117 +2265,70 @@ return slash+1; } -static int merge_normalize_opts( +static int merge_normalize_checkout_opts( git_repository *repo, - git_merge_opts *opts, - const git_merge_opts *given, + git_checkout_options *checkout_opts, + const git_checkout_options *given_checkout_opts, + const git_annotated_commit *ancestor_head, + const git_annotated_commit *our_head, size_t their_heads_len, - const git_merge_head **their_heads) + const git_annotated_commit **their_heads) { int error = 0; - unsigned int default_checkout_strategy = GIT_CHECKOUT_SAFE_CREATE | - GIT_CHECKOUT_ALLOW_CONFLICTS; GIT_UNUSED(repo); - if (given != NULL) - memcpy(opts, given, sizeof(git_merge_opts)); + if (given_checkout_opts != NULL) + memcpy(checkout_opts, given_checkout_opts, sizeof(git_checkout_options)); else { - git_merge_opts default_opts = GIT_MERGE_OPTS_INIT; - memcpy(opts, &default_opts, sizeof(git_merge_opts)); + git_checkout_options default_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + default_checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + + memcpy(checkout_opts, &default_checkout_opts, sizeof(git_checkout_options)); } - if (!opts->checkout_opts.checkout_strategy) - opts->checkout_opts.checkout_strategy = default_checkout_strategy; + /* TODO: for multiple ancestors in merge-recursive, this is "merged common ancestors" */ + if (!checkout_opts->ancestor_label) { + if (ancestor_head && ancestor_head->commit) + checkout_opts->ancestor_label = git_commit_summary(ancestor_head->commit); + else + checkout_opts->ancestor_label = "ancestor"; + } - if (!opts->checkout_opts.our_label) - opts->checkout_opts.our_label = "HEAD"; + if (!checkout_opts->our_label) { + if (our_head && our_head->ref_name) + checkout_opts->our_label = our_head->ref_name; + else + checkout_opts->our_label = "ours"; + } - if (!opts->checkout_opts.their_label) { + if (!checkout_opts->their_label) { if (their_heads_len == 1 && their_heads[0]->ref_name) - opts->checkout_opts.their_label = merge_their_label(their_heads[0]->ref_name); + checkout_opts->their_label = merge_their_label(their_heads[0]->ref_name); else if (their_heads_len == 1) - opts->checkout_opts.their_label = their_heads[0]->oid_str; + checkout_opts->their_label = their_heads[0]->id_str; else - opts->checkout_opts.their_label = "theirs"; + checkout_opts->their_label = "theirs"; } return error; } -static int merge_affected_paths(git_vector *paths, git_repository *repo, git_index *index_new) +static int merge_check_index(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths) { git_tree *head_tree = NULL; - git_iterator *iter_head = NULL, *iter_new = NULL; - git_diff *merged_list = NULL; - git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_index *index_repo = NULL; + git_iterator *iter_repo = NULL, *iter_new = NULL; + git_diff *staged_diff_list = NULL, *index_diff_list = NULL; git_diff_delta *delta; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_vector staged_paths = GIT_VECTOR_INIT; size_t i; - const git_index_entry *e; - char *path; int error = 0; - if ((error = git_repository_head_tree(&head_tree, repo)) < 0 || - (error = git_iterator_for_tree(&iter_head, head_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || - (error = git_iterator_for_index(&iter_new, index_new, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || - (error = git_diff__from_iterators(&merged_list, repo, iter_head, iter_new, &opts)) < 0) - goto done; - - git_vector_foreach(&merged_list->deltas, i, delta) { - path = git__strdup(delta->new_file.path); - GITERR_CHECK_ALLOC(path); + GIT_UNUSED(merged_paths); - if ((error = git_vector_insert(paths, path)) < 0) - goto on_error; - } - - for (i = 0; i < git_index_entrycount(index_new); i++) { - e = git_index_get_byindex(index_new, i); - - if (git_index_entry_stage(e) != 0 && - (git_vector_last(paths) == NULL || - strcmp(git_vector_last(paths), e->path) != 0)) { - - path = git__strdup(e->path); - GITERR_CHECK_ALLOC(path); - - if ((error = git_vector_insert(paths, path)) < 0) - goto on_error; - } - } - - goto done; - -on_error: - git_vector_foreach(paths, i, path) - git__free(path); - - git_vector_clear(paths); - -done: - git_tree_free(head_tree); - git_iterator_free(iter_head); - git_iterator_free(iter_new); - git_diff_free(merged_list); - - return error; -} - -static int merge_check_index(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths) -{ - git_tree *head_tree = NULL; - git_index *index_repo = NULL; - git_iterator *iter_repo = NULL, *iter_new = NULL; - git_diff *staged_diff_list = NULL, *index_diff_list = NULL; - git_diff_delta *delta; - git_diff_options opts = GIT_DIFF_OPTIONS_INIT; - git_vector staged_paths = GIT_VECTOR_INIT; - size_t i; - int error = 0; - - GIT_UNUSED(merged_paths); - - *conflicts = 0; + *conflicts = 0; /* No staged changes may exist unless the change staged is identical to * the result of the merge. This allows one to apply to merge manually, @@ -2235,7 +2372,6 @@ static int merge_check_workdir(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths) { - git_tree *head_tree = NULL; git_diff *wd_diff_list = NULL; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; int error = 0; @@ -2244,10 +2380,17 @@ *conflicts = 0; - opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; + /* We need to have merged at least 1 file for the possibility to exist to + * have conflicts with the workdir. Passing 0 as the pathspec count paramter + * will consider all files in the working directory, that is, we may detect + * a conflict if there were untracked files in the workdir prior to starting + * the merge. This typically happens when cherry-picking a commmit whose + * changes have already been applied. + */ + if (merged_paths->length == 0) + return 0; - if ((error = git_repository_head_tree(&head_tree, repo)) < 0) - goto done; + opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; /* Workdir changes may exist iff they do not conflict with changes that * will be applied by the merge (including conflicts). Ensure that there @@ -2256,42 +2399,54 @@ opts.pathspec.count = merged_paths->length; opts.pathspec.strings = (char **)merged_paths->contents; - if ((error = git_diff_tree_to_workdir(&wd_diff_list, repo, head_tree, &opts)) < 0) + if ((error = git_diff_index_to_workdir(&wd_diff_list, repo, NULL, &opts)) < 0) goto done; *conflicts = wd_diff_list->deltas.length; done: - git_tree_free(head_tree); git_diff_free(wd_diff_list); return error; } -static int merge_indexes(git_repository *repo, git_index *index_new) +int git_merge__check_result(git_repository *repo, git_index *index_new) { - git_index *index_repo; - unsigned int index_repo_caps; + git_tree *head_tree = NULL; + git_iterator *iter_head = NULL, *iter_new = NULL; + git_diff *merged_list = NULL; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff_delta *delta; git_vector paths = GIT_VECTOR_INIT; - size_t index_conflicts = 0, wd_conflicts = 0, conflicts, i; - char *path; + size_t i, index_conflicts = 0, wd_conflicts = 0, conflicts; const git_index_entry *e; - const git_index_name_entry *name; - const git_index_reuc_entry *reuc; int error = 0; - if ((error = git_repository_index(&index_repo, repo)) < 0) + if ((error = git_repository_head_tree(&head_tree, repo)) < 0 || + (error = git_iterator_for_tree(&iter_head, head_tree, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || + (error = git_iterator_for_index(&iter_new, index_new, GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 || + (error = git_diff__from_iterators(&merged_list, repo, iter_head, iter_new, &opts)) < 0) goto done; - /* Set the index to case sensitive to handle the merge */ - index_repo_caps = git_index_caps(index_repo); + git_vector_foreach(&merged_list->deltas, i, delta) { + if ((error = git_vector_insert(&paths, (char *)delta->new_file.path)) < 0) + goto done; + } - if ((error = git_index_set_caps(index_repo, (index_repo_caps & ~GIT_INDEXCAP_IGNORE_CASE))) < 0) - goto done; + for (i = 0; i < git_index_entrycount(index_new); i++) { + e = git_index_get_byindex(index_new, i); + + if (git_index_entry_stage(e) != 0 && + (git_vector_last(&paths) == NULL || + strcmp(git_vector_last(&paths), e->path) != 0)) { + + if ((error = git_vector_insert(&paths, (char *)e->path)) < 0) + goto done; + } + } /* Make sure the index and workdir state do not prevent merging */ - if ((error = merge_affected_paths(&paths, repo, index_new)) < 0 || - (error = merge_check_index(&index_conflicts, repo, index_new, &paths)) < 0 || + if ((error = merge_check_index(&index_conflicts, repo, index_new, &paths)) < 0 || (error = merge_check_workdir(&wd_conflicts, repo, index_new, &paths)) < 0) goto done; @@ -2299,370 +2454,296 @@ giterr_set(GITERR_MERGE, "%d uncommitted change%s would be overwritten by merge", conflicts, (conflicts != 1) ? "s" : ""); error = GIT_EMERGECONFLICT; - - goto done; } - /* Update the new index */ - git_vector_foreach(&paths, i, path) { - if ((e = git_index_get_bypath(index_new, path, 0)) != NULL) - error = git_index_add(index_repo, e); - else - error = git_index_remove(index_repo, path, 0); - } +done: + git_vector_free(&paths); + git_tree_free(head_tree); + git_iterator_free(iter_head); + git_iterator_free(iter_new); + git_diff_free(merged_list); - /* Add conflicts */ - git_index_conflict_cleanup(index_repo); + return error; +} - for (i = 0; i < git_index_entrycount(index_new); i++) { - e = git_index_get_byindex(index_new, i); +int git_merge__append_conflicts_to_merge_msg( + git_repository *repo, + git_index *index) +{ + git_filebuf file = GIT_FILEBUF_INIT; + git_buf file_path = GIT_BUF_INIT; + const char *last = NULL; + size_t i; + int error; - if (git_index_entry_stage(e) != 0 && - (error = git_index_add(index_repo, e)) < 0) - goto done; - } + if (!git_index_has_conflicts(index)) + return 0; - /* Add name entries */ - git_index_name_clear(index_repo); + if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 || + (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_APPEND, GIT_MERGE_FILE_MODE)) < 0) + goto cleanup; - for (i = 0; i < git_index_name_entrycount(index_new); i++) { - name = git_index_name_get_byindex(index_new, i); + git_filebuf_printf(&file, "\nConflicts:\n"); - if ((error = git_index_name_add(index_repo, - name->ancestor, name->ours, name->theirs)) < 0) - goto done; - } + for (i = 0; i < git_index_entrycount(index); i++) { + const git_index_entry *e = git_index_get_byindex(index, i); - /* Add the reuc */ - git_index_reuc_clear(index_repo); + if (git_index_entry_stage(e) == 0) + continue; - for (i = 0; i < git_index_reuc_entrycount(index_new); i++) { - reuc = (git_index_reuc_entry *)git_index_reuc_get_byindex(index_new, i); + if (last == NULL || strcmp(e->path, last) != 0) + git_filebuf_printf(&file, "\t%s\n", e->path); - if ((error = git_index_reuc_add(index_repo, reuc->path, - reuc->mode[0], &reuc->oid[0], - reuc->mode[1], &reuc->oid[1], - reuc->mode[2], &reuc->oid[2])) < 0) - goto done; + last = e->path; } -done: - if (index_repo != NULL) - git_index_set_caps(index_repo, index_repo_caps); - - git_index_free(index_repo); + error = git_filebuf_commit(&file); - git_vector_foreach(&paths, i, path) - git__free(path); +cleanup: + if (error < 0) + git_filebuf_cleanup(&file); - git_vector_free(&paths); + git_buf_free(&file_path); return error; } -int git_merge( - git_merge_result **out, +static int merge_state_cleanup(git_repository *repo) +{ + const char *state_files[] = { + GIT_MERGE_HEAD_FILE, + GIT_MERGE_MODE_FILE, + GIT_MERGE_MSG_FILE, + }; + + return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files)); +} + +static int merge_heads( + git_annotated_commit **ancestor_head_out, + git_annotated_commit **our_head_out, git_repository *repo, - const git_merge_head **their_heads, - size_t their_heads_len, - const git_merge_opts *given_opts) + const git_annotated_commit **their_heads, + size_t their_heads_len) { - git_merge_result *result; - git_merge_opts opts; + git_annotated_commit *ancestor_head = NULL, *our_head = NULL; git_reference *our_ref = NULL; - git_merge_head *ancestor_head = NULL, *our_head = NULL; - git_tree *ancestor_tree = NULL, *our_tree = NULL, **their_trees = NULL; - git_index *index_new = NULL, *index_repo = NULL; - size_t i; int error = 0; - assert(out && repo && their_heads); - - *out = NULL; - - if (their_heads_len != 1) { - giterr_set(GITERR_MERGE, "Can only merge a single branch"); - return -1; - } - - result = git__calloc(1, sizeof(git_merge_result)); - GITERR_CHECK_ALLOC(result); - - their_trees = git__calloc(their_heads_len, sizeof(git_tree *)); - GITERR_CHECK_ALLOC(their_trees); - - if ((error = merge_normalize_opts(repo, &opts, given_opts, their_heads_len, their_heads)) < 0) - goto on_error; + *ancestor_head_out = NULL; + *our_head_out = NULL; if ((error = git_repository__ensure_not_bare(repo, "merge")) < 0) - goto on_error; + goto done; if ((error = git_reference_lookup(&our_ref, repo, GIT_HEAD_FILE)) < 0 || - (error = git_merge_head_from_ref(&our_head, repo, our_ref)) < 0) - goto on_error; - - if ((error = merge_ancestor_head(&ancestor_head, repo, our_head, their_heads, their_heads_len)) < 0 && - error != GIT_ENOTFOUND) - goto on_error; - - if (their_heads_len == 1 && - ancestor_head != NULL && - (merge_check_uptodate(result, ancestor_head, their_heads[0]) || - merge_check_fastforward(result, ancestor_head, our_head, their_heads[0], opts.merge_flags))) { - *out = result; + (error = git_annotated_commit_from_ref(&our_head, repo, our_ref)) < 0) goto done; - } - /* If FASTFORWARD_ONLY is specified, fail. */ - if ((opts.merge_flags & GIT_MERGE_FASTFORWARD_ONLY) == - GIT_MERGE_FASTFORWARD_ONLY) { - giterr_set(GITERR_MERGE, "Not a fast-forward."); - error = GIT_ENONFASTFORWARD; - goto on_error; - } - - /* Write the merge files to the repository. */ - if ((error = git_merge__setup(repo, our_head, their_heads, their_heads_len, opts.merge_flags)) < 0) - goto on_error; - - if (ancestor_head != NULL && - (error = git_commit_tree(&ancestor_tree, ancestor_head->commit)) < 0) - goto on_error; - - if ((error = git_commit_tree(&our_tree, our_head->commit)) < 0) - goto on_error; + if ((error = merge_ancestor_head(&ancestor_head, repo, our_head, their_heads, their_heads_len)) < 0) { + if (error != GIT_ENOTFOUND) + goto done; - for (i = 0; i < their_heads_len; i++) { - if ((error = git_commit_tree(&their_trees[i], their_heads[i]->commit)) < 0) - goto on_error; + giterr_clear(); + error = 0; } - /* TODO: recursive, octopus, etc... */ - - if ((error = git_merge_trees(&index_new, repo, ancestor_tree, our_tree, their_trees[0], &opts.merge_tree_opts)) < 0 || - (error = merge_indexes(repo, index_new)) < 0 || - (error = git_repository_index(&index_repo, repo)) < 0 || - (error = git_checkout_index(repo, index_repo, &opts.checkout_opts)) < 0) - goto on_error; - - result->index = index_new; - - *out = result; - goto done; - -on_error: - git_repository_merge_cleanup(repo); - - git_index_free(index_new); - git__free(result); + *ancestor_head_out = ancestor_head; + *our_head_out = our_head; done: - git_index_free(index_repo); - - git_tree_free(ancestor_tree); - git_tree_free(our_tree); - - for (i = 0; i < their_heads_len; i++) - git_tree_free(their_trees[i]); - - git__free(their_trees); - - git_merge_head_free(our_head); - git_merge_head_free(ancestor_head); + if (error < 0) { + git_annotated_commit_free(ancestor_head); + git_annotated_commit_free(our_head); + } git_reference_free(our_ref); return error; } -int git_merge__setup( - git_repository *repo, - const git_merge_head *our_head, - const git_merge_head *heads[], - size_t heads_len, - unsigned int flags) +static int merge_preference(git_merge_preference_t *out, git_repository *repo) { - int error = 0; + git_config *config; + const char *value; + int bool_value, error = 0; - assert (repo && our_head && heads); + *out = GIT_MERGE_PREFERENCE_NONE; - if ((error = write_orig_head(repo, our_head)) == 0 && - (error = write_merge_head(repo, heads, heads_len)) == 0 && - (error = write_merge_mode(repo, flags)) == 0) { - error = write_merge_msg(repo, heads, heads_len); + if ((error = git_repository_config_snapshot(&config, repo)) < 0) + goto done; + + if ((error = git_config_get_string(&value, config, "merge.ff")) < 0) { + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = 0; + } + + goto done; + } + + if (git_config_parse_bool(&bool_value, value) == 0) { + if (!bool_value) + *out |= GIT_MERGE_PREFERENCE_NO_FASTFORWARD; + } else { + if (strcasecmp(value, "only") == 0) + *out |= GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY; } +done: + git_config_free(config); return error; } -int git_repository_merge_cleanup(git_repository *repo) +int git_merge_analysis( + git_merge_analysis_t *analysis_out, + git_merge_preference_t *preference_out, + git_repository *repo, + const git_annotated_commit **their_heads, + size_t their_heads_len) { + git_annotated_commit *ancestor_head = NULL, *our_head = NULL; int error = 0; - git_buf merge_head_path = GIT_BUF_INIT, - merge_mode_path = GIT_BUF_INIT, - merge_msg_path = GIT_BUF_INIT; - assert(repo); + assert(analysis_out && preference_out && repo && their_heads); - if (git_buf_joinpath(&merge_head_path, repo->path_repository, GIT_MERGE_HEAD_FILE) < 0 || - git_buf_joinpath(&merge_mode_path, repo->path_repository, GIT_MERGE_MODE_FILE) < 0 || - git_buf_joinpath(&merge_msg_path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0) - return -1; - - if (git_path_isfile(merge_head_path.ptr)) { - if ((error = p_unlink(merge_head_path.ptr)) < 0) - goto cleanup; + if (their_heads_len != 1) { + giterr_set(GITERR_MERGE, "Can only merge a single branch"); + error = -1; + goto done; } - if (git_path_isfile(merge_mode_path.ptr)) - (void)p_unlink(merge_mode_path.ptr); + *analysis_out = GIT_MERGE_ANALYSIS_NONE; - if (git_path_isfile(merge_msg_path.ptr)) - (void)p_unlink(merge_msg_path.ptr); - -cleanup: - git_buf_free(&merge_msg_path); - git_buf_free(&merge_mode_path); - git_buf_free(&merge_head_path); - - return error; -} + if ((error = merge_preference(preference_out, repo)) < 0) + goto done; -/* Merge result data */ + if (git_repository_head_unborn(repo)) { + *analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_UNBORN; + goto done; + } -int git_merge_result_is_uptodate(git_merge_result *merge_result) -{ - assert(merge_result); + if ((error = merge_heads(&ancestor_head, &our_head, repo, their_heads, their_heads_len)) < 0) + goto done; - return merge_result->is_uptodate; -} + /* We're up-to-date if we're trying to merge our own common ancestor. */ + if (ancestor_head && git_oid_equal( + git_annotated_commit_id(ancestor_head), git_annotated_commit_id(their_heads[0]))) + *analysis_out |= GIT_MERGE_ANALYSIS_UP_TO_DATE; + + /* We're fastforwardable if we're our own common ancestor. */ + else if (ancestor_head && git_oid_equal( + git_annotated_commit_id(ancestor_head), git_annotated_commit_id(our_head))) + *analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_NORMAL; -int git_merge_result_is_fastforward(git_merge_result *merge_result) -{ - assert(merge_result); + /* Otherwise, just a normal merge is possible. */ + else + *analysis_out |= GIT_MERGE_ANALYSIS_NORMAL; - return merge_result->is_fastforward; +done: + git_annotated_commit_free(ancestor_head); + git_annotated_commit_free(our_head); + return error; } -int git_merge_result_fastforward_oid(git_oid *out, git_merge_result *merge_result) +int git_merge( + git_repository *repo, + const git_annotated_commit **their_heads, + size_t their_heads_len, + const git_merge_options *merge_opts, + const git_checkout_options *given_checkout_opts) { - assert(out && merge_result); - - git_oid_cpy(out, &merge_result->fastforward_oid); - return 0; -} + git_reference *our_ref = NULL; + git_checkout_options checkout_opts; + git_annotated_commit *ancestor_head = NULL, *our_head = NULL; + git_tree *ancestor_tree = NULL, *our_tree = NULL, **their_trees = NULL; + git_index *index_new = NULL; + size_t i; + int error = 0; -void git_merge_result_free(git_merge_result *merge_result) -{ - if (merge_result == NULL) - return; + assert(repo && their_heads); - git_index_free(merge_result->index); - merge_result->index = NULL; + if (their_heads_len != 1) { + giterr_set(GITERR_MERGE, "Can only merge a single branch"); + return -1; + } - git__free(merge_result); -} + their_trees = git__calloc(their_heads_len, sizeof(git_tree *)); + GITERR_CHECK_ALLOC(their_trees); -/* Merge heads are the input to merge */ + if ((error = merge_heads(&ancestor_head, &our_head, repo, their_heads, their_heads_len)) < 0) + goto on_error; -static int merge_head_init( - git_merge_head **out, - git_repository *repo, - const char *ref_name, - const char *remote_url, - const git_oid *oid) -{ - git_merge_head *head; - int error = 0; + if ((error = merge_normalize_checkout_opts(repo, &checkout_opts, given_checkout_opts, + ancestor_head, our_head, their_heads_len, their_heads)) < 0) + goto on_error; - assert(out && oid); + /* Write the merge files to the repository. */ + if ((error = git_merge__setup(repo, our_head, their_heads, their_heads_len)) < 0) + goto on_error; - *out = NULL; + if (ancestor_head != NULL && + (error = git_commit_tree(&ancestor_tree, ancestor_head->commit)) < 0) + goto on_error; - head = git__calloc(1, sizeof(git_merge_head)); - GITERR_CHECK_ALLOC(head); + if ((error = git_commit_tree(&our_tree, our_head->commit)) < 0) + goto on_error; - if (ref_name) { - head->ref_name = git__strdup(ref_name); - GITERR_CHECK_ALLOC(head->ref_name); + for (i = 0; i < their_heads_len; i++) { + if ((error = git_commit_tree(&their_trees[i], their_heads[i]->commit)) < 0) + goto on_error; } - if (remote_url) { - head->remote_url = git__strdup(remote_url); - GITERR_CHECK_ALLOC(head->remote_url); - } + /* TODO: recursive, octopus, etc... */ - git_oid_cpy(&head->oid, oid); + if ((error = git_merge_trees(&index_new, repo, ancestor_tree, our_tree, their_trees[0], merge_opts)) < 0 || + (error = git_merge__check_result(repo, index_new)) < 0 || + (error = git_merge__append_conflicts_to_merge_msg(repo, index_new)) < 0 || + (error = git_checkout_index(repo, index_new, &checkout_opts)) < 0) + goto on_error; - git_oid_fmt(head->oid_str, oid); - head->oid_str[GIT_OID_HEXSZ] = '\0'; + goto done; - if ((error = git_commit_lookup(&head->commit, repo, &head->oid)) < 0) { - git_merge_head_free(head); - return error; - } +on_error: + merge_state_cleanup(repo); - *out = head; - return error; -} +done: + git_index_free(index_new); -int git_merge_head_from_ref( - git_merge_head **out, - git_repository *repo, - git_reference *ref) -{ - git_reference *resolved; - int error = 0; + git_tree_free(ancestor_tree); + git_tree_free(our_tree); + + for (i = 0; i < their_heads_len; i++) + git_tree_free(their_trees[i]); - assert(out && repo && ref); + git__free(their_trees); - *out = NULL; + git_annotated_commit_free(our_head); + git_annotated_commit_free(ancestor_head); - if ((error = git_reference_resolve(&resolved, ref)) < 0) - return error; - - error = merge_head_init(out, repo, git_reference_name(ref), NULL, - git_reference_target(resolved)); + git_reference_free(our_ref); - git_reference_free(resolved); return error; } -int git_merge_head_from_oid( - git_merge_head **out, - git_repository *repo, - const git_oid *oid) +int git_merge_init_options(git_merge_options *opts, unsigned int version) { - assert(out && repo && oid); - - return merge_head_init(out, repo, NULL, NULL, oid); + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_merge_options, GIT_MERGE_OPTIONS_INIT); + return 0; } -int git_merge_head_from_fetchhead( - git_merge_head **out, - git_repository *repo, - const char *branch_name, - const char *remote_url, - const git_oid *oid) +int git_merge_file_init_input(git_merge_file_input *input, unsigned int version) { - assert(repo && branch_name && remote_url && oid); - - return merge_head_init(out, repo, branch_name, remote_url, oid); + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + input, version, git_merge_file_input, GIT_MERGE_FILE_INPUT_INIT); + return 0; } -void git_merge_head_free(git_merge_head *head) +int git_merge_file_init_options( + git_merge_file_options *opts, unsigned int version) { - if (head == NULL) - return; - - if (head->commit != NULL) - git_object_free((git_object *)head->commit); - - if (head->ref_name != NULL) - git__free(head->ref_name); - - if (head->remote_url != NULL) - git__free(head->remote_url); - - git__free(head); + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_merge_file_options, GIT_MERGE_FILE_OPTIONS_INIT); + return 0; } diff -Nru libgit2-0.20.0/src/merge_file.c libgit2-0.22.2/src/merge_file.c --- libgit2-0.20.0/src/merge_file.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/merge_file.c 2015-03-24 16:10:45.000000000 +0000 @@ -8,6 +8,9 @@ #include "common.h" #include "repository.h" #include "merge_file.h" +#include "posix.h" +#include "fileops.h" +#include "index.h" #include "git2/repository.h" #include "git2/object.h" @@ -22,17 +25,17 @@ const git_merge_file_input *ours, const git_merge_file_input *theirs) { - if (!GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) { - if (strcmp(ours->path, theirs->path) == 0) + if (!ancestor) { + if (ours && theirs && strcmp(ours->path, theirs->path) == 0) return ours->path; return NULL; } - if (strcmp(ancestor->path, ours->path) == 0) - return theirs->path; - else if(strcmp(ancestor->path, theirs->path) == 0) - return ours->path; + if (ours && strcmp(ancestor->path, ours->path) == 0) + return theirs ? theirs->path : NULL; + else if(theirs && strcmp(ancestor->path, theirs->path) == 0) + return ours ? ours->path : NULL; return NULL; } @@ -47,128 +50,230 @@ * assume executable. Otherwise, if any mode changed from the ancestor, * use that one. */ - if (!GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) { - if (ours->mode == GIT_FILEMODE_BLOB_EXECUTABLE || - theirs->mode == GIT_FILEMODE_BLOB_EXECUTABLE) + if (!ancestor) { + if ((ours && ours->mode == GIT_FILEMODE_BLOB_EXECUTABLE) || + (theirs && theirs->mode == GIT_FILEMODE_BLOB_EXECUTABLE)) return GIT_FILEMODE_BLOB_EXECUTABLE; return GIT_FILEMODE_BLOB; - } + } else if (ours && theirs) { + if (ancestor->mode == ours->mode) + return theirs->mode; - if (ancestor->mode == ours->mode) - return theirs->mode; - else if(ancestor->mode == theirs->mode) return ours->mode; + } return 0; } -int git_merge_file_input_from_index_entry( - git_merge_file_input *input, - git_repository *repo, +int git_merge_file__input_from_index( + git_merge_file_input *input_out, + git_odb_object **odb_object_out, + git_odb *odb, const git_index_entry *entry) { - git_odb *odb = NULL; int error = 0; - assert(input && repo && entry); - - if (entry->mode == 0) - return 0; + assert(input_out && odb_object_out && odb && entry); - if ((error = git_repository_odb(&odb, repo)) < 0 || - (error = git_odb_read(&input->odb_object, odb, &entry->oid)) < 0) + if ((error = git_odb_read(odb_object_out, odb, &entry->id)) < 0) goto done; - input->mode = entry->mode; - input->path = git__strdup(entry->path); - input->mmfile.size = git_odb_object_size(input->odb_object); - input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object); - - if (input->label == NULL) - input->label = entry->path; + input_out->path = entry->path; + input_out->mode = entry->mode; + input_out->ptr = (char *)git_odb_object_data(*odb_object_out); + input_out->size = git_odb_object_size(*odb_object_out); done: - git_odb_free(odb); - return error; } -int git_merge_file_input_from_diff_file( - git_merge_file_input *input, - git_repository *repo, - const git_diff_file *file) +static void merge_file_normalize_opts( + git_merge_file_options *out, + const git_merge_file_options *given_opts) { - git_odb *odb = NULL; + if (given_opts) + memcpy(out, given_opts, sizeof(git_merge_file_options)); + else { + git_merge_file_options default_opts = GIT_MERGE_FILE_OPTIONS_INIT; + memcpy(out, &default_opts, sizeof(git_merge_file_options)); + } +} + +static int git_merge_file__from_inputs( + git_merge_file_result *out, + const git_merge_file_input *ancestor, + const git_merge_file_input *ours, + const git_merge_file_input *theirs, + const git_merge_file_options *given_opts) +{ + xmparam_t xmparam; + mmfile_t ancestor_mmfile = {0}, our_mmfile = {0}, their_mmfile = {0}; + mmbuffer_t mmbuffer; + git_merge_file_options options = GIT_MERGE_FILE_OPTIONS_INIT; + const char *path; + int xdl_result; int error = 0; - assert(input && repo && file); + memset(out, 0x0, sizeof(git_merge_file_result)); - if (file->mode == 0) - return 0; + merge_file_normalize_opts(&options, given_opts); - if ((error = git_repository_odb(&odb, repo)) < 0 || - (error = git_odb_read(&input->odb_object, odb, &file->oid)) < 0) + memset(&xmparam, 0x0, sizeof(xmparam_t)); + + if (ancestor) { + xmparam.ancestor = (options.ancestor_label) ? + options.ancestor_label : ancestor->path; + ancestor_mmfile.ptr = (char *)ancestor->ptr; + ancestor_mmfile.size = ancestor->size; + } + + xmparam.file1 = (options.our_label) ? + options.our_label : ours->path; + our_mmfile.ptr = (char *)ours->ptr; + our_mmfile.size = ours->size; + + xmparam.file2 = (options.their_label) ? + options.their_label : theirs->path; + their_mmfile.ptr = (char *)theirs->ptr; + their_mmfile.size = theirs->size; + + if (options.favor == GIT_MERGE_FILE_FAVOR_OURS) + xmparam.favor = XDL_MERGE_FAVOR_OURS; + else if (options.favor == GIT_MERGE_FILE_FAVOR_THEIRS) + xmparam.favor = XDL_MERGE_FAVOR_THEIRS; + else if (options.favor == GIT_MERGE_FILE_FAVOR_UNION) + xmparam.favor = XDL_MERGE_FAVOR_UNION; + + xmparam.level = (options.flags & GIT_MERGE_FILE_SIMPLIFY_ALNUM) ? + XDL_MERGE_ZEALOUS_ALNUM : XDL_MERGE_ZEALOUS; + + if (options.flags & GIT_MERGE_FILE_STYLE_DIFF3) + xmparam.style = XDL_MERGE_DIFF3; + + if ((xdl_result = xdl_merge(&ancestor_mmfile, &our_mmfile, + &their_mmfile, &xmparam, &mmbuffer)) < 0) { + giterr_set(GITERR_MERGE, "Failed to merge files."); + error = -1; goto done; + } - input->mode = file->mode; - input->path = git__strdup(file->path); - input->mmfile.size = git_odb_object_size(input->odb_object); - input->mmfile.ptr = (char *)git_odb_object_data(input->odb_object); + if ((path = merge_file_best_path(ancestor, ours, theirs)) != NULL && + (out->path = strdup(path)) == NULL) { + error = -1; + goto done; + } - if (input->label == NULL) - input->label = file->path; + out->automergeable = (xdl_result == 0); + out->ptr = (const char *)mmbuffer.ptr; + out->len = mmbuffer.size; + out->mode = merge_file_best_mode(ancestor, ours, theirs); done: - git_odb_free(odb); + if (error < 0) + git_merge_file_result_free(out); return error; } -int git_merge_files( +static git_merge_file_input *git_merge_file__normalize_inputs( + git_merge_file_input *out, + const git_merge_file_input *given) +{ + memcpy(out, given, sizeof(git_merge_file_input)); + + if (!out->path) + out->path = "file.txt"; + + if (!out->mode) + out->mode = 0100644; + + return out; +} + +int git_merge_file( git_merge_file_result *out, - git_merge_file_input *ancestor, - git_merge_file_input *ours, - git_merge_file_input *theirs, - git_merge_automerge_flags flags) + const git_merge_file_input *ancestor, + const git_merge_file_input *ours, + const git_merge_file_input *theirs, + const git_merge_file_options *options) { - xmparam_t xmparam; - mmbuffer_t mmbuffer; - int xdl_result; - int error = 0; + git_merge_file_input inputs[3] = { {0} }; - assert(out && ancestor && ours && theirs); + assert(out && ours && theirs); memset(out, 0x0, sizeof(git_merge_file_result)); - if (!GIT_MERGE_FILE_SIDE_EXISTS(ours) || !GIT_MERGE_FILE_SIDE_EXISTS(theirs)) - return 0; + if (ancestor) + ancestor = git_merge_file__normalize_inputs(&inputs[0], ancestor); - memset(&xmparam, 0x0, sizeof(xmparam_t)); - xmparam.ancestor = ancestor->label; - xmparam.file1 = ours->label; - xmparam.file2 = theirs->label; + ours = git_merge_file__normalize_inputs(&inputs[1], ours); + theirs = git_merge_file__normalize_inputs(&inputs[2], theirs); - out->path = merge_file_best_path(ancestor, ours, theirs); - out->mode = merge_file_best_mode(ancestor, ours, theirs); + return git_merge_file__from_inputs(out, ancestor, ours, theirs, options); +} - if (flags == GIT_MERGE_AUTOMERGE_FAVOR_OURS) - xmparam.favor = XDL_MERGE_FAVOR_OURS; +int git_merge_file_from_index( + git_merge_file_result *out, + git_repository *repo, + const git_index_entry *ancestor, + const git_index_entry *ours, + const git_index_entry *theirs, + const git_merge_file_options *options) +{ + git_merge_file_input inputs[3] = { {0} }, + *ancestor_input = NULL, *our_input = NULL, *their_input = NULL; + git_odb *odb = NULL; + git_odb_object *odb_object[3] = { 0 }; + int error = 0; - if (flags == GIT_MERGE_AUTOMERGE_FAVOR_THEIRS) - xmparam.favor = XDL_MERGE_FAVOR_THEIRS; + assert(out && repo && ours && theirs); - if ((xdl_result = xdl_merge(&ancestor->mmfile, &ours->mmfile, - &theirs->mmfile, &xmparam, &mmbuffer)) < 0) { - giterr_set(GITERR_MERGE, "Failed to merge files."); - error = -1; + memset(out, 0x0, sizeof(git_merge_file_result)); + + if ((error = git_repository_odb(&odb, repo)) < 0) goto done; + + if (ancestor) { + if ((error = git_merge_file__input_from_index( + &inputs[0], &odb_object[0], odb, ancestor)) < 0) + goto done; + + ancestor_input = &inputs[0]; } - out->automergeable = (xdl_result == 0); - out->data = (unsigned char *)mmbuffer.ptr; - out->len = mmbuffer.size; + if ((error = git_merge_file__input_from_index( + &inputs[1], &odb_object[1], odb, ours)) < 0) + goto done; + + our_input = &inputs[1]; + + if ((error = git_merge_file__input_from_index( + &inputs[2], &odb_object[2], odb, theirs)) < 0) + goto done; + + their_input = &inputs[2]; + + if ((error = git_merge_file__from_inputs(out, + ancestor_input, our_input, their_input, options)) < 0) + goto done; done: + git_odb_object_free(odb_object[0]); + git_odb_object_free(odb_object[1]); + git_odb_object_free(odb_object[2]); + git_odb_free(odb); + return error; } + +void git_merge_file_result_free(git_merge_file_result *result) +{ + if (result == NULL) + return; + + git__free((char *)result->path); + + /* xdiff uses malloc() not git_malloc, so we use free(), not git_free() */ + free((char *)result->ptr); +} diff -Nru libgit2-0.20.0/src/merge_file.h libgit2-0.22.2/src/merge_file.h --- libgit2-0.20.0/src/merge_file.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/merge_file.h 2015-03-24 16:10:45.000000000 +0000 @@ -11,61 +11,4 @@ #include "git2/merge.h" -typedef struct { - const char *label; - char *path; - unsigned int mode; - mmfile_t mmfile; - - git_odb_object *odb_object; -} git_merge_file_input; - -#define GIT_MERGE_FILE_INPUT_INIT {0} - -typedef struct { - bool automergeable; - - const char *path; - int mode; - - unsigned char *data; - size_t len; -} git_merge_file_result; - -#define GIT_MERGE_FILE_RESULT_INIT {0} - -int git_merge_file_input_from_index_entry( - git_merge_file_input *input, - git_repository *repo, - const git_index_entry *entry); - -int git_merge_file_input_from_diff_file( - git_merge_file_input *input, - git_repository *repo, - const git_diff_file *file); - -int git_merge_files( - git_merge_file_result *out, - git_merge_file_input *ancestor, - git_merge_file_input *ours, - git_merge_file_input *theirs, - git_merge_automerge_flags flags); - -GIT_INLINE(void) git_merge_file_input_free(git_merge_file_input *input) -{ - assert(input); - git__free(input->path); - git_odb_object_free(input->odb_object); -} - -GIT_INLINE(void) git_merge_file_result_free(git_merge_file_result *filediff) -{ - if (filediff == NULL) - return; - - /* xdiff uses malloc() not git_malloc, so we use free(), not git_free() */ - if (filediff->data != NULL) - free(filediff->data); -} - #endif diff -Nru libgit2-0.20.0/src/merge.h libgit2-0.22.2/src/merge.h --- libgit2-0.20.0/src/merge.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/merge.h 2015-03-24 16:10:45.000000000 +0000 @@ -106,27 +106,9 @@ git_index_entry their_entry; git_delta_t their_status; -} git_merge_diff; - -/** Internal structure for merge inputs */ -struct git_merge_head { - char *ref_name; - char *remote_url; - - git_oid oid; - char oid_str[GIT_OID_HEXSZ+1]; - git_commit *commit; -}; - -/** Internal structure for merge results */ -struct git_merge_result { - bool is_uptodate; - - bool is_fastforward; - git_oid fastforward_oid; - git_index *index; -}; + int binary:1; +} git_merge_diff; int git_merge__bases_many( git_commit_list **out, @@ -145,7 +127,7 @@ const git_tree *ours_tree, const git_tree *theirs_tree); -int git_merge_diff_list__find_renames(git_repository *repo, git_merge_diff_list *merge_diff_list, const git_merge_tree_opts *opts); +int git_merge_diff_list__find_renames(git_repository *repo, git_merge_diff_list *merge_diff_list, const git_merge_options *opts); void git_merge_diff_list__free(git_merge_diff_list *diff_list); @@ -153,9 +135,12 @@ int git_merge__setup( git_repository *repo, - const git_merge_head *our_head, - const git_merge_head *their_heads[], - size_t their_heads_len, - unsigned int flags); + const git_annotated_commit *our_head, + const git_annotated_commit *heads[], + size_t heads_len); + +int git_merge__check_result(git_repository *repo, git_index *index_new); + +int git_merge__append_conflicts_to_merge_msg(git_repository *repo, git_index *index); #endif diff -Nru libgit2-0.20.0/src/message.c libgit2-0.22.2/src/message.c --- libgit2-0.20.0/src/message.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/message.c 2015-03-24 16:10:45.000000000 +0000 @@ -21,7 +21,7 @@ /* Greatly inspired from git.git "stripspace" */ /* see https://github.com/git/git/blob/497215d8811ac7b8955693ceaad0899ecd894ed2/builtin/stripspace.c#L4-67 */ -int git_message__prettify(git_buf *message_out, const char *message, int strip_comments) +int git_message_prettify(git_buf *message_out, const char *message, int strip_comments, char comment_char) { const size_t message_len = strlen(message); @@ -29,6 +29,8 @@ size_t i, line_length, rtrimmed_line_length; char *next_newline; + git_buf_sanitize(message_out); + for (i = 0; i < strlen(message); i += line_length) { next_newline = memchr(message + i, '\n', message_len - i); @@ -38,7 +40,7 @@ line_length = message_len - i; } - if (strip_comments && line_length && message[i] == '#') + if (strip_comments && line_length && message[i] == comment_char) continue; rtrimmed_line_length = line_length_without_trailing_spaces(message + i, line_length); @@ -58,29 +60,3 @@ return git_buf_oom(message_out) ? -1 : 0; } - -int git_message_prettify(char *message_out, size_t buffer_size, const char *message, int strip_comments) -{ - git_buf buf = GIT_BUF_INIT; - ssize_t out_size = -1; - - if (message_out && buffer_size) - *message_out = '\0'; - - if (git_message__prettify(&buf, message, strip_comments) < 0) - goto done; - - if (message_out && buf.size + 1 > buffer_size) { /* +1 for NUL byte */ - giterr_set(GITERR_INVALID, "Buffer too short to hold the cleaned message"); - goto done; - } - - if (message_out) - git_buf_copy_cstr(message_out, buffer_size, &buf); - - out_size = buf.size + 1; - -done: - git_buf_free(&buf); - return (int)out_size; -} diff -Nru libgit2-0.20.0/src/mwindow.c libgit2-0.22.2/src/mwindow.c --- libgit2-0.20.0/src/mwindow.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/mwindow.c 2015-03-24 16:10:45.000000000 +0000 @@ -11,6 +11,10 @@ #include "fileops.h" #include "map.h" #include "global.h" +#include "strmap.h" +#include "pack.h" + +GIT__USE_STRMAP; #define DEFAULT_WINDOW_SIZE \ (sizeof(void*) >= 8 \ @@ -26,20 +30,129 @@ /* Whenever you want to read or modify this, grab git__mwindow_mutex */ static git_mwindow_ctl mem_ctl; -/* - * Free all the windows in a sequence, typically because we're done - * with the file +/* Global list of mwindow files, to open packs once across repos */ +git_strmap *git__pack_cache = NULL; + +/** + * Run under mwindow lock */ -void git_mwindow_free_all(git_mwindow_file *mwf) +int git_mwindow_files_init(void) { - git_mwindow_ctl *ctl = &mem_ctl; - size_t i; + if (git__pack_cache) + return 0; + + git__on_shutdown(git_mwindow_files_free); + + return git_strmap_alloc(&git__pack_cache); +} + +void git_mwindow_files_free(void) +{ + git_strmap *tmp = git__pack_cache; + + git__pack_cache = NULL; + git_strmap_free(tmp); +} + +int git_mwindow_get_pack(struct git_pack_file **out, const char *path) +{ + int error; + char *packname; + git_strmap_iter pos; + struct git_pack_file *pack; + + if ((error = git_packfile__name(&packname, path)) < 0) + return error; + + if (git_mutex_lock(&git__mwindow_mutex) < 0) { + giterr_set(GITERR_OS, "failed to lock mwindow mutex"); + return -1; + } + + if (git_mwindow_files_init() < 0) { + git_mutex_unlock(&git__mwindow_mutex); + git__free(packname); + return -1; + } + + pos = git_strmap_lookup_index(git__pack_cache, packname); + git__free(packname); + + if (git_strmap_valid_index(git__pack_cache, pos)) { + pack = git_strmap_value_at(git__pack_cache, pos); + git_atomic_inc(&pack->refcount); + + git_mutex_unlock(&git__mwindow_mutex); + *out = pack; + return 0; + } + + /* If we didn't find it, we need to create it */ + if ((error = git_packfile_alloc(&pack, path)) < 0) { + git_mutex_unlock(&git__mwindow_mutex); + return error; + } + + git_atomic_inc(&pack->refcount); + + git_strmap_insert(git__pack_cache, pack->pack_name, pack, error); + git_mutex_unlock(&git__mwindow_mutex); + + if (error < 0) { + git_packfile_free(pack); + return -1; + } + + *out = pack; + return 0; +} + +void git_mwindow_put_pack(struct git_pack_file *pack) +{ + int count; + git_strmap_iter pos; + + if (git_mutex_lock(&git__mwindow_mutex) < 0) + return; + + /* put before get would be a corrupted state */ + assert(git__pack_cache); + pos = git_strmap_lookup_index(git__pack_cache, pack->pack_name); + /* if we cannot find it, the state is corrupted */ + assert(git_strmap_valid_index(git__pack_cache, pos)); + + count = git_atomic_dec(&pack->refcount); + if (count == 0) { + git_strmap_delete_at(git__pack_cache, pos); + git_packfile_free(pack); + } + + git_mutex_unlock(&git__mwindow_mutex); + return; +} + +void git_mwindow_free_all(git_mwindow_file *mwf) +{ if (git_mutex_lock(&git__mwindow_mutex)) { giterr_set(GITERR_THREAD, "unable to lock mwindow mutex"); return; } + git_mwindow_free_all_locked(mwf); + + git_mutex_unlock(&git__mwindow_mutex); +} + +/* + * Free all the windows in a sequence, typically because we're done + * with the file + */ +void git_mwindow_free_all_locked(git_mwindow_file *mwf) +{ + git_mwindow_ctl *ctl = &mem_ctl; + size_t i; + /* * Remove these windows from the global list */ @@ -67,8 +180,6 @@ mwf->windows = w->next; git__free(w); } - - git_mutex_unlock(&git__mwindow_mutex); } /* diff -Nru libgit2-0.20.0/src/mwindow.h libgit2-0.22.2/src/mwindow.h --- libgit2-0.20.0/src/mwindow.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/mwindow.h 2015-03-24 16:10:45.000000000 +0000 @@ -36,10 +36,18 @@ } git_mwindow_ctl; int git_mwindow_contains(git_mwindow *win, git_off_t offset); -void git_mwindow_free_all(git_mwindow_file *mwf); +void git_mwindow_free_all(git_mwindow_file *mwf); /* locks */ +void git_mwindow_free_all_locked(git_mwindow_file *mwf); /* run under lock */ unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, git_off_t offset, size_t extra, unsigned int *left); int git_mwindow_file_register(git_mwindow_file *mwf); void git_mwindow_file_deregister(git_mwindow_file *mwf); void git_mwindow_close(git_mwindow **w_cursor); +int git_mwindow_files_init(void); +void git_mwindow_files_free(void); + +struct git_pack_file; /* just declaration to avoid cyclical includes */ +int git_mwindow_get_pack(struct git_pack_file **out, const char *path); +void git_mwindow_put_pack(struct git_pack_file *pack); + #endif diff -Nru libgit2-0.20.0/src/netops.c libgit2-0.22.2/src/netops.c --- libgit2-0.20.0/src/netops.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/netops.c 2015-03-24 16:10:45.000000000 +0000 @@ -4,26 +4,6 @@ * 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 _WIN32 -# include -# include -# include -# include -# include -# include -# include -#else -# include -# ifdef _MSC_VER -# pragma comment(lib, "ws2_32") -# endif -#endif - -#ifdef GIT_SSL -# include -# include -# include -#endif #include #include "git2/errors.h" @@ -33,137 +13,48 @@ #include "posix.h" #include "buffer.h" #include "http_parser.h" - -#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 - -#ifdef GIT_SSL -static int ssl_set_error(gitno_ssl *ssl, int error) -{ - int err; - unsigned long e; - - err = SSL_get_error(ssl->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\n"); - break; - case SSL_ERROR_WANT_X509_LOOKUP: - giterr_set(GITERR_NET, "SSL error: x509 error\n"); - 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"); - 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; -} -#endif +#include "global.h" int gitno_recv(gitno_buffer *buf) { return buf->recv(buf); } -#ifdef GIT_SSL -static int gitno__recv_ssl(gitno_buffer *buf) +void gitno_buffer_setup_callback( + gitno_buffer *buf, + char *data, + size_t len, + int (*recv)(gitno_buffer *buf), void *cb_data) { - int ret; - - do { - ret = SSL_read(buf->socket->ssl.ssl, buf->data + buf->offset, buf->len - buf->offset); - } while (SSL_get_error(buf->socket->ssl.ssl, ret) == SSL_ERROR_WANT_READ); - - if (ret < 0) { - net_set_error("Error receiving socket data"); - return -1; - } - - buf->offset += ret; - return ret; + memset(data, 0x0, len); + buf->data = data; + buf->len = len; + buf->offset = 0; + buf->recv = recv; + buf->cb_data = cb_data; } -#endif -static int gitno__recv(gitno_buffer *buf) +static int recv_stream(gitno_buffer *buf) { + git_stream *io = (git_stream *) buf->cb_data; int ret; - ret = p_recv(buf->socket->socket, buf->data + buf->offset, buf->len - buf->offset, 0); - if (ret < 0) { - net_set_error("Error receiving socket data"); + ret = git_stream_read(io, buf->data + buf->offset, buf->len - buf->offset); + if (ret < 0) return -1; - } buf->offset += ret; return ret; } -void gitno_buffer_setup_callback( - gitno_socket *socket, - gitno_buffer *buf, - char *data, - size_t len, - int (*recv)(gitno_buffer *buf), void *cb_data) +void gitno_buffer_setup_fromstream(git_stream *st, gitno_buffer *buf, char *data, size_t len) { memset(data, 0x0, len); buf->data = data; buf->len = len; buf->offset = 0; - buf->socket = socket; - buf->recv = recv; - buf->cb_data = cb_data; -} - -void gitno_buffer_setup(gitno_socket *socket, gitno_buffer *buf, char *data, size_t len) -{ -#ifdef GIT_SSL - if (socket->ssl.ctx) { - gitno_buffer_setup_callback(socket, buf, data, len, gitno__recv_ssl, NULL); - return; - } -#endif - - gitno_buffer_setup_callback(socket, buf, data, len, gitno__recv, NULL); + buf->recv = recv_stream; + buf->cb_data = st; } /* Consume up to ptr and move the rest of the buffer to the beginning */ @@ -189,25 +80,8 @@ buf->offset -= cons; } -#ifdef GIT_SSL - -static int gitno_ssl_teardown(gitno_ssl *ssl) -{ - int ret; - - ret = SSL_shutdown(ssl->ssl); - if (ret < 0) - ret = ssl_set_error(ssl, ret); - else - ret = 0; - - SSL_free(ssl->ssl); - SSL_CTX_free(ssl->ctx); - return ret; -} - /* Match host names according to RFC 2818 rules */ -static int match_host(const char *pattern, const char *host) +int gitno__match_host(const char *pattern, const char *host) { for (;;) { char c = tolower(*pattern++); @@ -230,9 +104,9 @@ while(*host) { char h = tolower(*host); if (c == h) - return match_host(pattern, host++); + return gitno__match_host(pattern, host++); if (h == '.') - return match_host(pattern, host); + return gitno__match_host(pattern, host); host++; } return -1; @@ -245,335 +119,6 @@ return -1; } -static int check_host_name(const char *name, const char *host) -{ - if (!strcasecmp(name, host)) - return 0; - - if (match_host(name, host) < 0) - return -1; - - return 0; -} - -static int verify_server_cert(gitno_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->ssl) != X509_V_OK) { - giterr_set(GITERR_SSL, "The SSL certificate is invalid"); - return -1; - } - - /* 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->ssl); - - /* 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_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; - - 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_data(str), size); - peer_cn[size] = '\0'; - } - } else { - int size = ASN1_STRING_to_UTF8(&peer_cn, str); - GITERR_CHECK_ALLOC(peer_cn); - if (memchr(peer_cn, '\0', size)) - goto cert_fail; - } - - if (check_host_name((char *)peer_cn, host) < 0) - goto cert_fail; - - OPENSSL_free(peer_cn); - - return 0; - -on_error: - OPENSSL_free(peer_cn); - return ssl_set_error(ssl, 0); - -cert_fail: - OPENSSL_free(peer_cn); - giterr_set(GITERR_SSL, "Certificate host name check failed"); - return -1; -} - -static int ssl_setup(gitno_socket *socket, const char *host, int flags) -{ - int ret; - - SSL_library_init(); - SSL_load_error_strings(); - socket->ssl.ctx = SSL_CTX_new(SSLv23_method()); - if (socket->ssl.ctx == NULL) - return ssl_set_error(&socket->ssl, 0); - - SSL_CTX_set_mode(socket->ssl.ctx, SSL_MODE_AUTO_RETRY); - SSL_CTX_set_verify(socket->ssl.ctx, SSL_VERIFY_NONE, NULL); - if (!SSL_CTX_set_default_verify_paths(socket->ssl.ctx)) - return ssl_set_error(&socket->ssl, 0); - - socket->ssl.ssl = SSL_new(socket->ssl.ctx); - if (socket->ssl.ssl == NULL) - return ssl_set_error(&socket->ssl, 0); - - if((ret = SSL_set_fd(socket->ssl.ssl, socket->socket)) == 0) - return ssl_set_error(&socket->ssl, ret); - - if ((ret = SSL_connect(socket->ssl.ssl)) <= 0) - return ssl_set_error(&socket->ssl, ret); - - if (GITNO_CONNECT_SSL_NO_CHECK_CERT & flags) - return 0; - - return verify_server_cert(&socket->ssl, host); -} -#endif - -static int gitno__close(GIT_SOCKET s) -{ -#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 gitno_connect(gitno_socket *s_out, const char *host, const char *port, int flags) -{ - struct addrinfo *info = NULL, *p; - struct addrinfo hints; - 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 - - /* Zero the socket structure provided */ - memset(s_out, 0x0, sizeof(gitno_socket)); - - memset(&hints, 0x0, sizeof(struct addrinfo)); - hints.ai_socktype = SOCK_STREAM; - hints.ai_family = AF_UNSPEC; - - if ((ret = p_getaddrinfo(host, port, &hints, &info)) < 0) { - giterr_set(GITERR_NET, - "Failed to resolve address for %s: %s", 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) { - net_set_error("error creating socket"); - break; - } - - if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0) - break; - - /* If we can't connect, try the next one */ - gitno__close(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", host); - p_freeaddrinfo(info); - return -1; - } - - s_out->socket = s; - p_freeaddrinfo(info); - -#ifdef GIT_SSL - if ((flags & GITNO_CONNECT_SSL) && ssl_setup(s_out, host, flags) < 0) - return -1; -#else - /* SSL is not supported */ - if (flags & GITNO_CONNECT_SSL) { - giterr_set(GITERR_OS, "SSL is not supported by this copy of libgit2."); - return -1; - } -#endif - - return 0; -} - -#ifdef GIT_SSL -static int gitno_send_ssl(gitno_ssl *ssl, const char *msg, size_t len, int flags) -{ - int ret; - size_t off = 0; - - GIT_UNUSED(flags); - - while (off < len) { - ret = SSL_write(ssl->ssl, msg + off, len - off); - if (ret <= 0 && ret != SSL_ERROR_WANT_WRITE) - return ssl_set_error(ssl, ret); - - off += ret; - } - - return off; -} -#endif - -int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags) -{ - int ret; - size_t off = 0; - -#ifdef GIT_SSL - if (socket->ssl.ctx) - return gitno_send_ssl(&socket->ssl, msg, len, flags); -#endif - - while (off < len) { - errno = 0; - ret = p_send(socket->socket, msg + off, len - off, flags); - if (ret < 0) { - net_set_error("Error sending data"); - return -1; - } - - off += ret; - } - - return (int)off; -} - -int gitno_close(gitno_socket *s) -{ -#ifdef GIT_SSL - if (s->ssl.ctx && - gitno_ssl_teardown(&s->ssl) < 0) - return -1; -#endif - - return gitno__close(s->socket); -} - -int gitno_select_in(gitno_buffer *buf, long int sec, long int usec) -{ - fd_set fds; - struct timeval tv; - - tv.tv_sec = sec; - tv.tv_usec = usec; - - FD_ZERO(&fds); - FD_SET(buf->socket->socket, &fds); - - /* The select(2) interface is silly */ - return select((int)buf->socket->socket + 1, &fds, NULL, NULL, &tv); -} - static const char *prefix_http = "http://"; static const char *prefix_https = "https://"; @@ -715,6 +260,9 @@ if (u.field_set & (1 << UF_PATH)) { *path = git__substrdup(_path, u.field_data[UF_PATH].len); GITERR_CHECK_ALLOC(*path); + } else { + giterr_set(GITERR_NET, "invalid url, missing path"); + return GIT_EINVALIDSPEC; } if (u.field_set & (1 << UF_USERINFO)) { diff -Nru libgit2-0.20.0/src/netops.h libgit2-0.22.2/src/netops.h --- libgit2-0.20.0/src/netops.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/netops.h 2015-03-24 16:10:45.000000000 +0000 @@ -9,63 +9,60 @@ #include "posix.h" #include "common.h" +#include "stream.h" #ifdef GIT_SSL # include #endif -struct gitno_ssl { +typedef struct gitno_ssl { #ifdef GIT_SSL - SSL_CTX *ctx; SSL *ssl; #else size_t dummy; #endif -}; - -typedef struct gitno_ssl gitno_ssl; +} gitno_ssl; /* Represents a socket that may or may not be using SSL */ -struct gitno_socket { +typedef struct gitno_socket { GIT_SOCKET socket; gitno_ssl ssl; -}; +} gitno_socket; -typedef struct gitno_socket gitno_socket; - -struct gitno_buffer { +typedef struct gitno_buffer { char *data; size_t len; size_t offset; - gitno_socket *socket; int (*recv)(struct gitno_buffer *buffer); void *cb_data; -}; - -typedef struct gitno_buffer gitno_buffer; +} gitno_buffer; /* Flags to gitno_connect */ enum { /* Attempt to create an SSL connection. */ GITNO_CONNECT_SSL = 1, - - /* Valid only when GITNO_CONNECT_SSL is also specified. - * Indicates that the server certificate should not be validated. */ - GITNO_CONNECT_SSL_NO_CHECK_CERT = 2, }; -void gitno_buffer_setup(gitno_socket *t, gitno_buffer *buf, char *data, size_t len); -void gitno_buffer_setup_callback(gitno_socket *t, gitno_buffer *buf, char *data, size_t len, int (*recv)(gitno_buffer *buf), void *cb_data); +/** + * Check if the name in a cert matches the wanted hostname + * + * Check if a pattern from a certificate matches the hostname we + * wanted to connect to according to RFC2818 rules (which specifies + * HTTP over TLS). Mainly, an asterisk matches anything, but is + * limited to a single url component. + * + * Note that this does not set an error message. It expects the user + * to provide the message for the user. + */ +int gitno__match_host(const char *pattern, const char *host); + +void gitno_buffer_setup_fromstream(git_stream *st, gitno_buffer *buf, char *data, size_t len); +void gitno_buffer_setup_callback(gitno_buffer *buf, char *data, size_t len, int (*recv)(gitno_buffer *buf), void *cb_data); int gitno_recv(gitno_buffer *buf); void gitno_consume(gitno_buffer *buf, const char *ptr); void gitno_consume_n(gitno_buffer *buf, size_t cons); -int gitno_connect(gitno_socket *socket, const char *host, const char *port, int flags); -int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags); -int gitno_close(gitno_socket *s); -int gitno_select_in(gitno_buffer *buf, long int sec, long int usec); - typedef struct gitno_connection_data { char *host; char *port; diff -Nru libgit2-0.20.0/src/notes.c libgit2-0.22.2/src/notes.c --- libgit2-0.20.0/src/notes.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/notes.c 2015-03-24 16:10:45.000000000 +0000 @@ -107,7 +107,7 @@ const git_tree_entry *entry; git_oid tree_oid; - if ((error = git_treebuilder_create(&tb, source_tree)) < 0) + if ((error = git_treebuilder_new(&tb, repo, source_tree)) < 0) goto cleanup; if (object_oid) { @@ -119,7 +119,7 @@ goto cleanup; } - if ((error = git_treebuilder_write(&tree_oid, repo, tb)) < 0) + if ((error = git_treebuilder_write(&tree_oid, tb)) < 0) goto cleanup; error = git_tree_lookup(out, repo, &tree_oid); @@ -306,24 +306,36 @@ return error; } -static int note_new(git_note **out, git_oid *note_oid, git_blob *blob) +static int note_new( + git_note **out, + git_oid *note_oid, + git_commit *commit, + git_blob *blob) { git_note *note = NULL; note = (git_note *)git__malloc(sizeof(git_note)); GITERR_CHECK_ALLOC(note); - git_oid_cpy(¬e->oid, note_oid); - note->message = git__strdup((char *)git_blob_rawcontent(blob)); + git_oid_cpy(¬e->id, note_oid); + + if (git_signature_dup(¬e->author, git_commit_author(commit)) < 0 || + git_signature_dup(¬e->committer, git_commit_committer(commit)) < 0) + return -1; + + note->message = git__strndup(git_blob_rawcontent(blob), git_blob_rawsize(blob)); GITERR_CHECK_ALLOC(note->message); *out = note; - return 0; } static int note_lookup( - git_note **out, git_repository *repo, git_tree *tree, const char *target) + git_note **out, + git_repository *repo, + git_commit *commit, + git_tree *tree, + const char *target) { int error, fanout = 0; git_oid oid; @@ -340,7 +352,7 @@ if ((error = git_blob_lookup(&blob, repo, &oid)) < 0) goto cleanup; - if ((error = note_new(¬e, &oid, blob)) < 0) + if ((error = note_new(¬e, &oid, commit, blob)) < 0) goto cleanup; *out = note; @@ -378,20 +390,11 @@ static int note_get_default_ref(const char **out, git_repository *repo) { - int ret; git_config *cfg; + int ret = git_repository_config__weakptr(&cfg, repo); - *out = NULL; - - if (git_repository_config__weakptr(&cfg, repo) < 0) - return -1; - - ret = git_config_get_string(out, cfg, "core.notesRef"); - if (ret == GIT_ENOTFOUND) { - giterr_clear(); - *out = GIT_NOTES_DEFAULT_REF; - return 0; - } + *out = (ret != 0) ? NULL : git_config__get_string_force( + cfg, "core.notesref", GIT_NOTES_DEFAULT_REF); return ret; } @@ -441,7 +444,7 @@ if (!(error = retrieve_note_tree_and_commit( &tree, &commit, repo, ¬es_ref))) - error = note_lookup(out, repo, tree, target); + error = note_lookup(out, repo, commit, tree, target); git__free(target); git_tree_free(tree); @@ -452,9 +455,9 @@ int git_note_create( git_oid *out, git_repository *repo, + const char *notes_ref, const git_signature *author, const git_signature *committer, - const char *notes_ref, const git_oid *oid, const char *note, int allow_note_overwrite) @@ -511,16 +514,28 @@ return note_get_default_ref(out, repo); } +const git_signature *git_note_committer(const git_note *note) +{ + assert(note); + return note->committer; +} + +const git_signature *git_note_author(const git_note *note) +{ + assert(note); + return note->author; +} + const char * git_note_message(const git_note *note) { assert(note); return note->message; } -const git_oid * git_note_oid(const git_note *note) +const git_oid * git_note_id(const git_note *note) { assert(note); - return ¬e->oid; + return ¬e->id; } void git_note_free(git_note *note) @@ -528,6 +543,8 @@ if (note == NULL) return; + git_signature_free(note->committer); + git_signature_free(note->author); git__free(note->message); git__free(note); } @@ -592,8 +609,8 @@ return error; while (!(error = git_note_next(¬e_id, &annotated_id, iter))) { - if (note_cb(¬e_id, &annotated_id, payload)) { - error = GIT_EUSER; + if ((error = note_cb(¬e_id, &annotated_id, payload)) != 0) { + giterr_set_after_callback(error); break; } } @@ -649,7 +666,7 @@ if ((error = git_iterator_current(&item, it)) < 0) return error; - git_oid_cpy(note_id, &item->oid); + git_oid_cpy(note_id, &item->id); if (!(error = process_entry_path(item->path, annotated_id))) git_iterator_advance(NULL, it); diff -Nru libgit2-0.20.0/src/notes.h libgit2-0.22.2/src/notes.h --- libgit2-0.20.0/src/notes.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/notes.h 2015-03-24 16:10:45.000000000 +0000 @@ -21,7 +21,10 @@ "Notes removed by 'git_note_remove' from libgit2" struct git_note { - git_oid oid; + git_oid id; + + git_signature *author; + git_signature *committer; char *message; }; diff -Nru libgit2-0.20.0/src/object.c libgit2-0.22.2/src/object.c --- libgit2-0.20.0/src/object.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/object.c 2015-03-24 16:10:45.000000000 +0000 @@ -4,8 +4,6 @@ * 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 - #include "git2/object.h" #include "common.h" @@ -279,10 +277,8 @@ return git_tag_target(dereferenced, (git_tag*)obj); case GIT_OBJ_BLOB: - return GIT_ENOTFOUND; - case GIT_OBJ_TREE: - return GIT_EAMBIGUOUS; + return GIT_EPEEL; default: return GIT_EINVALIDSPEC; @@ -305,6 +301,32 @@ return error; } +static int check_type_combination(git_otype type, git_otype target) +{ + if (type == target) + return 0; + + switch (type) { + case GIT_OBJ_BLOB: + case GIT_OBJ_TREE: + /* a blob or tree can never be peeled to anything but themselves */ + return GIT_EINVALIDSPEC; + break; + case GIT_OBJ_COMMIT: + /* a commit can only be peeled to a tree */ + if (target != GIT_OBJ_TREE && target != GIT_OBJ_ANY) + return GIT_EINVALIDSPEC; + break; + case GIT_OBJ_TAG: + /* a tag may point to anything, so we let anything through */ + break; + default: + return GIT_EINVALIDSPEC; + } + + return 0; +} + int git_object_peel( git_object **peeled, const git_object *object, @@ -315,15 +337,18 @@ assert(object && peeled); - if (git_object_type(object) == target_type) - return git_object_dup(peeled, (git_object *)object); - assert(target_type == GIT_OBJ_TAG || target_type == GIT_OBJ_COMMIT || target_type == GIT_OBJ_TREE || target_type == GIT_OBJ_BLOB || target_type == GIT_OBJ_ANY); + if ((error = check_type_combination(git_object_type(object), target_type)) < 0) + return peel_error(error, git_object_id(object), target_type); + + if (git_object_type(object) == target_type) + return git_object_dup(peeled, (git_object *)object); + source = (git_object *)object; while (!(error = dereference_object(&deref, source))) { @@ -377,7 +402,7 @@ assert(out && treeish && path); - if ((error = git_object_peel((git_object**)&tree, treeish, GIT_OBJ_TREE) < 0) || + if ((error = git_object_peel((git_object**)&tree, treeish, GIT_OBJ_TREE)) < 0 || (error = git_tree_entry_bypath(&entry, tree, path)) < 0) { goto cleanup; @@ -399,3 +424,46 @@ git_tree_free(tree); return error; } + +int git_object_short_id(git_buf *out, const git_object *obj) +{ + git_repository *repo; + int len = GIT_ABBREV_DEFAULT, error; + git_oid id = {{0}}; + git_odb *odb; + + assert(out && obj); + + git_buf_sanitize(out); + repo = git_object_owner(obj); + + if ((error = git_repository__cvar(&len, repo, GIT_CVAR_ABBREV)) < 0) + return error; + + if ((error = git_repository_odb(&odb, repo)) < 0) + return error; + + while (len < GIT_OID_HEXSZ) { + /* set up short oid */ + memcpy(&id.id, &obj->cached.oid.id, (len + 1) / 2); + if (len & 1) + id.id[len / 2] &= 0xf0; + + error = git_odb_exists_prefix(NULL, odb, &id, len); + if (error != GIT_EAMBIGUOUS) + break; + + giterr_clear(); + len++; + } + + if (!error && !(error = git_buf_grow(out, len + 1))) { + git_oid_tostr(out->ptr, len + 1, &id); + out->size = len; + } + + git_odb_free(odb); + + return error; +} + diff -Nru libgit2-0.20.0/src/odb.c libgit2-0.22.2/src/odb.c --- libgit2-0.20.0/src/odb.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/odb.c 2015-03-24 16:10:45.000000000 +0000 @@ -445,7 +445,7 @@ { backend_internal *internal; - assert(odb && odb); + assert(out && odb); internal = git_vector_get(&odb->backends, pos); if (internal && internal->backend) { @@ -635,6 +635,66 @@ return (int)found; } +int git_odb_exists_prefix( + git_oid *out, git_odb *db, const git_oid *short_id, size_t len) +{ + int error = GIT_ENOTFOUND, num_found = 0; + size_t i; + git_oid key = {{0}}, last_found = {{0}}, found; + + assert(db && short_id); + + if (len < GIT_OID_MINPREFIXLEN) + return git_odb__error_ambiguous("prefix length too short"); + if (len > GIT_OID_HEXSZ) + len = GIT_OID_HEXSZ; + + if (len == GIT_OID_HEXSZ) { + if (git_odb_exists(db, short_id)) { + if (out) + git_oid_cpy(out, short_id); + return 0; + } else { + return git_odb__error_notfound("no match for id prefix", short_id); + } + } + + /* just copy valid part of short_id */ + memcpy(&key.id, short_id->id, (len + 1) / 2); + if (len & 1) + key.id[len / 2] &= 0xF0; + + for (i = 0; i < db->backends.length; ++i) { + backend_internal *internal = git_vector_get(&db->backends, i); + git_odb_backend *b = internal->backend; + + if (!b->exists_prefix) + continue; + + error = b->exists_prefix(&found, b, &key, len); + if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) + continue; + if (error) + return error; + + /* make sure found item doesn't introduce ambiguity */ + if (num_found) { + if (git_oid__cmp(&last_found, &found)) + return git_odb__error_ambiguous("multiple matches for prefix"); + } else { + git_oid_cpy(&last_found, &found); + num_found++; + } + } + + if (!num_found) + return git_odb__error_notfound("no match for id prefix", &key); + if (out) + git_oid_cpy(out, &last_found); + + return 0; +} + int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id) { int error; @@ -692,6 +752,28 @@ return 0; } +static git_oid empty_blob = {{ 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1, 0xd6, 0x43, 0x4b, 0x8b, + 0x29, 0xae, 0x77, 0x5a, 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91 }}; +static git_oid empty_tree = {{ 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60, + 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04 }}; + +static int hardcoded_objects(git_rawobj *raw, const git_oid *id) +{ + if (!git_oid_cmp(id, &empty_blob)) { + raw->type = GIT_OBJ_BLOB; + raw->len = 0; + raw->data = git__calloc(1, sizeof(uint8_t)); + return 0; + } else if (!git_oid_cmp(id, &empty_tree)) { + raw->type = GIT_OBJ_TREE; + raw->len = 0; + raw->data = git__calloc(1, sizeof(uint8_t)); + return 0; + } else { + return GIT_ENOTFOUND; + } +} + int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id) { size_t i, reads = 0; @@ -705,7 +787,7 @@ if (*out != NULL) return 0; - error = GIT_ENOTFOUND; + error = hardcoded_objects(&raw, id); for (i = 0; i < db->backends.length && error < 0; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); @@ -723,6 +805,7 @@ return error; } + giterr_clear(); if ((object = odb_object__alloc(id, &raw)) == NULL) return -1; @@ -735,7 +818,7 @@ { size_t i; int error = GIT_ENOTFOUND; - git_oid found_full_oid = {{0}}; + git_oid key = {{0}}, found_full_oid = {{0}}; git_rawobj raw; void *data = NULL; bool found = false; @@ -745,7 +828,6 @@ if (len < GIT_OID_MINPREFIXLEN) return git_odb__error_ambiguous("prefix length too short"); - if (len > GIT_OID_HEXSZ) len = GIT_OID_HEXSZ; @@ -755,13 +837,18 @@ return 0; } + /* just copy valid part of short_id */ + memcpy(&key.id, short_id->id, (len + 1) / 2); + if (len & 1) + key.id[len / 2] &= 0xF0; + for (i = 0; i < db->backends.length; ++i) { backend_internal *internal = git_vector_get(&db->backends, i); git_odb_backend *b = internal->backend; if (b->read_prefix != NULL) { git_oid full_oid; - error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, short_id, len); + error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, &key, len); if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) continue; @@ -782,7 +869,7 @@ } if (!found) - return git_odb__error_notfound("no match for prefix", short_id); + return git_odb__error_notfound("no match for prefix", &key); if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) return -1; @@ -862,7 +949,7 @@ { size_t i, writes = 0; int error = GIT_ERROR; - git_hash_ctx *ctx; + git_hash_ctx *ctx = NULL; assert(stream && db); @@ -883,22 +970,28 @@ } } - if (error == GIT_PASSTHROUGH) - error = 0; - if (error < 0 && !writes) - error = git_odb__error_unsupported_in_backend("write object"); + if (error < 0) { + if (error == GIT_PASSTHROUGH) + error = 0; + else if (!writes) + error = git_odb__error_unsupported_in_backend("write object"); + + goto done; + } ctx = git__malloc(sizeof(git_hash_ctx)); GITERR_CHECK_ALLOC(ctx); + if ((error = git_hash_ctx_init(ctx)) < 0) + goto done; - git_hash_ctx_init(ctx); hash_header(ctx, size, type); (*stream)->hash_ctx = ctx; (*stream)->declared_size = size; (*stream)->received_bytes = 0; +done: return error; } @@ -949,6 +1042,10 @@ void git_odb_stream_free(git_odb_stream *stream) { + if (stream == NULL) + return; + + git_hash_ctx_cleanup(stream->hash_ctx); git__free(stream->hash_ctx); stream->free(stream); } @@ -978,7 +1075,7 @@ return error; } -int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_transfer_progress_callback progress_cb, void *progress_payload) +int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_transfer_progress_cb progress_cb, void *progress_payload) { size_t i, writes = 0; int error = GIT_ERROR; @@ -1050,3 +1147,9 @@ return GIT_EAMBIGUOUS; } +int git_odb_init_backend(git_odb_backend *backend, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + backend, version, git_odb_backend, GIT_ODB_BACKEND_INIT); + return 0; +} diff -Nru libgit2-0.20.0/src/odb_loose.c libgit2-0.22.2/src/odb_loose.c --- libgit2-0.20.0/src/odb_loose.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/odb_loose.c 2015-03-24 16:10:45.000000000 +0000 @@ -56,7 +56,7 @@ /*********************************************************** * - * MISCELANEOUS HELPER FUNCTIONS + * MISCELLANEOUS HELPER FUNCTIONS * ***********************************************************/ @@ -519,11 +519,11 @@ loose_locate_object_state state; int error; - /* prealloc memory for OBJ_DIR/xx/ */ - if (git_buf_grow(object_location, dir_len + 5) < 0) + /* prealloc memory for OBJ_DIR/xx/xx..38x..xx */ + if (git_buf_grow(object_location, dir_len + 3 + GIT_OID_HEXSZ) < 0) return -1; - git_buf_sets(object_location, objects_dir); + git_buf_set(object_location, objects_dir, dir_len); git_path_to_dir(object_location); /* save adjusted position at end of dir so it can be restored later */ @@ -533,8 +533,9 @@ git_oid_fmt((char *)state.short_oid, short_oid); /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */ - if (git_buf_printf(object_location, "%.2s/", state.short_oid) < 0) + if (git_buf_put(object_location, (char *)state.short_oid, 3) < 0) return -1; + object_location->ptr[object_location->size - 1] = '/'; /* Check that directory exists */ if (git_path_isdir(object_location->ptr) == false) @@ -547,8 +548,7 @@ /* Explore directory to find a unique object matching short_oid */ error = git_path_direach( object_location, 0, fn_locate_object_short_oid, &state); - - if (error && error != GIT_EUSER) + if (error < 0 && error != GIT_EAMBIGUOUS) return error; if (!state.found) @@ -647,12 +647,9 @@ { int error = 0; - assert(len <= GIT_OID_HEXSZ); - - if (len < GIT_OID_MINPREFIXLEN) - error = git_odb__error_ambiguous("prefix length too short"); + assert(len >= GIT_OID_MINPREFIXLEN && len <= GIT_OID_HEXSZ); - else if (len == GIT_OID_HEXSZ) { + if (len == GIT_OID_HEXSZ) { /* We can fall back to regular read method */ error = loose_backend__read(buffer_p, len_p, type_p, backend, short_oid); if (!error) @@ -692,17 +689,32 @@ return !error; } +static int loose_backend__exists_prefix( + git_oid *out, git_odb_backend *backend, const git_oid *short_id, size_t len) +{ + git_buf object_path = GIT_BUF_INIT; + int error; + + assert(backend && out && short_id && len >= GIT_OID_MINPREFIXLEN); + + error = locate_object_short_oid( + &object_path, out, (loose_backend *)backend, short_id, len); + + git_buf_free(&object_path); + + return error; +} + struct foreach_state { size_t dir_len; git_odb_foreach_cb cb; void *data; - int cb_error; }; GIT_INLINE(int) filename_to_oid(git_oid *oid, const char *ptr) { int v, i = 0; - if (strlen(ptr) != 41) + if (strlen(ptr) != GIT_OID_HEXSZ+1) return -1; if (ptr[2] != '/') { @@ -735,18 +747,18 @@ if (filename_to_oid(&oid, path->ptr + state->dir_len) < 0) return 0; - if (state->cb(&oid, state->data)) { - state->cb_error = GIT_EUSER; - return -1; - } - - return 0; + return giterr_set_after_callback_function( + state->cb(&oid, state->data), "git_odb_foreach"); } static int foreach_cb(void *_state, git_buf *path) { struct foreach_state *state = (struct foreach_state *) _state; + /* non-dir is some stray file, ignore it */ + if (!git_path_isdir(git_buf_cstr(path))) + return 0; + return git_path_direach(path, 0, foreach_object_dir_cb, state); } @@ -764,6 +776,8 @@ git_buf_sets(&buf, objects_dir); git_path_to_dir(&buf); + if (git_buf_oom(&buf)) + return -1; memset(&state, 0, sizeof(state)); state.cb = cb; @@ -774,7 +788,7 @@ git_buf_free(&buf); - return state.cb_error ? state.cb_error : error; + return error; } static int loose_backend__stream_fwrite(git_odb_stream *_stream, const git_oid *oid) @@ -943,6 +957,7 @@ backend->parent.read_header = &loose_backend__read_header; backend->parent.writestream = &loose_backend__stream; backend->parent.exists = &loose_backend__exists; + backend->parent.exists_prefix = &loose_backend__exists_prefix; backend->parent.foreach = &loose_backend__foreach; backend->parent.free = &loose_backend__free; diff -Nru libgit2-0.20.0/src/odb_mempack.c libgit2-0.22.2/src/odb_mempack.c --- libgit2-0.20.0/src/odb_mempack.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/odb_mempack.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,182 @@ +/* + * 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 "git2/object.h" +#include "git2/sys/odb_backend.h" +#include "fileops.h" +#include "hash.h" +#include "odb.h" +#include "array.h" +#include "oidmap.h" + +#include "git2/odb_backend.h" +#include "git2/types.h" +#include "git2/pack.h" + +GIT__USE_OIDMAP; + +struct memobject { + git_oid oid; + size_t len; + git_otype type; + char data[]; +}; + +struct memory_packer_db { + git_odb_backend parent; + git_oidmap *objects; + git_array_t(struct memobject *) commits; +}; + +static int impl__write(git_odb_backend *_backend, const git_oid *oid, const void *data, size_t len, git_otype type) +{ + struct memory_packer_db *db = (struct memory_packer_db *)_backend; + struct memobject *obj = NULL; + khiter_t pos; + int rval; + + pos = kh_put(oid, db->objects, oid, &rval); + if (rval < 0) + return -1; + + if (rval == 0) + return 0; + + obj = git__malloc(sizeof(struct memobject) + len); + GITERR_CHECK_ALLOC(obj); + + memcpy(obj->data, data, len); + git_oid_cpy(&obj->oid, oid); + obj->len = len; + obj->type = type; + + kh_key(db->objects, pos) = &obj->oid; + kh_val(db->objects, pos) = obj; + + if (type == GIT_OBJ_COMMIT) { + struct memobject **store = git_array_alloc(db->commits); + GITERR_CHECK_ALLOC(store); + *store = obj; + } + + return 0; +} + +static int impl__exists(git_odb_backend *backend, const git_oid *oid) +{ + struct memory_packer_db *db = (struct memory_packer_db *)backend; + khiter_t pos; + + pos = kh_get(oid, db->objects, oid); + if (pos != kh_end(db->objects)) + return 1; + + return 0; +} + +static int impl__read(void **buffer_p, size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) +{ + struct memory_packer_db *db = (struct memory_packer_db *)backend; + struct memobject *obj = NULL; + khiter_t pos; + + pos = kh_get(oid, db->objects, oid); + if (pos == kh_end(db->objects)) + return GIT_ENOTFOUND; + + obj = kh_val(db->objects, pos); + + *len_p = obj->len; + *type_p = obj->type; + *buffer_p = git__malloc(obj->len); + GITERR_CHECK_ALLOC(*buffer_p); + + memcpy(*buffer_p, obj->data, obj->len); + return 0; +} + +static int impl__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *backend, const git_oid *oid) +{ + struct memory_packer_db *db = (struct memory_packer_db *)backend; + struct memobject *obj = NULL; + khiter_t pos; + + pos = kh_get(oid, db->objects, oid); + if (pos == kh_end(db->objects)) + return GIT_ENOTFOUND; + + obj = kh_val(db->objects, pos); + + *len_p = obj->len; + *type_p = obj->type; + return 0; +} + +int git_mempack_dump(git_buf *pack, git_repository *repo, git_odb_backend *_backend) +{ + struct memory_packer_db *db = (struct memory_packer_db *)_backend; + git_packbuilder *packbuilder; + uint32_t i; + int err = -1; + + if (git_packbuilder_new(&packbuilder, repo) < 0) + return -1; + + for (i = 0; i < db->commits.size; ++i) { + struct memobject *commit = db->commits.ptr[i]; + + err = git_packbuilder_insert_commit(packbuilder, &commit->oid); + if (err < 0) + goto cleanup; + } + + err = git_packbuilder_write_buf(pack, packbuilder); + +cleanup: + git_packbuilder_free(packbuilder); + return err; +} + +void git_mempack_reset(git_odb_backend *_backend) +{ + struct memory_packer_db *db = (struct memory_packer_db *)_backend; + struct memobject *object = NULL; + + kh_foreach_value(db->objects, object, { + git__free(object); + }); + + git_array_clear(db->commits); +} + +static void impl__free(git_odb_backend *_backend) +{ + git_mempack_reset(_backend); + git__free(_backend); +} + +int git_mempack_new(git_odb_backend **out) +{ + struct memory_packer_db *db; + + assert(out); + + db = git__calloc(1, sizeof(struct memory_packer_db)); + GITERR_CHECK_ALLOC(db); + + db->objects = git_oidmap_alloc(); + + db->parent.read = &impl__read; + db->parent.write = &impl__write; + db->parent.read_header = &impl__read_header; + db->parent.exists = &impl__exists; + db->parent.free = &impl__free; + + *out = (git_odb_backend *)db; + return 0; +} diff -Nru libgit2-0.20.0/src/odb_pack.c libgit2-0.22.2/src/odb_pack.c --- libgit2-0.20.0/src/odb_pack.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/odb_pack.c 2015-03-24 16:10:45.000000000 +0000 @@ -190,31 +190,39 @@ } - -static int packfile_load__cb(void *_data, git_buf *path) +static int packfile_load__cb(void *data, git_buf *path) { - struct pack_backend *backend = (struct pack_backend *)_data; + struct pack_backend *backend = data; struct git_pack_file *pack; + const char *path_str = git_buf_cstr(path); + size_t i, cmp_len = git_buf_len(path); int error; - size_t i; - if (git__suffixcmp(path->ptr, ".idx") != 0) + if (cmp_len <= strlen(".idx") || git__suffixcmp(path_str, ".idx") != 0) return 0; /* not an index */ + cmp_len -= strlen(".idx"); + for (i = 0; i < backend->packs.length; ++i) { struct git_pack_file *p = git_vector_get(&backend->packs, i); - if (memcmp(p->pack_name, git_buf_cstr(path), git_buf_len(path) - strlen(".idx")) == 0) + + if (memcmp(p->pack_name, path_str, cmp_len) == 0) return 0; } - error = git_packfile_alloc(&pack, path->ptr); - if (error == GIT_ENOTFOUND) - /* ignore missing .pack file as git does */ + error = git_mwindow_get_pack(&pack, path->ptr); + + /* ignore missing .pack file as git does */ + if (error == GIT_ENOTFOUND) { + giterr_clear(); return 0; - else if (error < 0) - return error; + } + + if (!error) + error = git_vector_insert(&backend->packs, pack); + + return error; - return git_vector_insert(&backend->packs, pack); } static int pack_entry_find_inner( @@ -314,13 +322,12 @@ * Implement the git_odb_backend API calls * ***********************************************************/ -static int pack_backend__refresh(git_odb_backend *_backend) +static int pack_backend__refresh(git_odb_backend *backend_) { - struct pack_backend *backend = (struct pack_backend *)_backend; - int error; struct stat st; git_buf path = GIT_BUF_INIT; + struct pack_backend *backend = (struct pack_backend *)backend_; if (backend->pack_folder == NULL) return 0; @@ -334,12 +341,9 @@ error = git_path_direach(&path, 0, packfile_load__cb, backend); git_buf_free(&path); - - if (error < 0) - return -1; - git_vector_sort(&backend->packs); - return 0; + + return error; } static int pack_backend__read_header_internal( @@ -489,6 +493,23 @@ return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0; } +static int pack_backend__exists_prefix( + git_oid *out, git_odb_backend *backend, const git_oid *short_id, size_t len) +{ + int error; + struct pack_backend *pb = (struct pack_backend *)backend; + struct git_pack_entry e = {0}; + + error = pack_entry_find_prefix(&e, pb, short_id, len); + + if (error == GIT_ENOTFOUND && !(error = pack_backend__refresh(backend))) + error = pack_entry_find_prefix(&e, pb, short_id, len); + + git_oid_cpy(out, &e.sha1); + + return error; +} + static int pack_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data) { int error; @@ -542,7 +563,7 @@ static int pack_backend__writepack(struct git_odb_writepack **out, git_odb_backend *_backend, git_odb *odb, - git_transfer_progress_callback progress_cb, + git_transfer_progress_cb progress_cb, void *progress_payload) { struct pack_backend *backend; @@ -584,7 +605,7 @@ for (i = 0; i < backend->packs.length; ++i) { struct git_pack_file *p = git_vector_get(&backend->packs, i); - git_packfile_free(p); + git_mwindow_put_pack(p); } git_vector_free(&backend->packs); @@ -608,6 +629,7 @@ backend->parent.read_prefix = &pack_backend__read_prefix; backend->parent.read_header = &pack_backend__read_header; backend->parent.exists = &pack_backend__exists; + backend->parent.exists_prefix = &pack_backend__exists_prefix; backend->parent.refresh = &pack_backend__refresh; backend->parent.foreach = &pack_backend__foreach; backend->parent.writepack = &pack_backend__writepack; @@ -625,7 +647,7 @@ if (pack_backend__alloc(&backend, 1) < 0) return -1; - if (git_packfile_alloc(&packfile, idx) < 0 || + if (git_mwindow_get_pack(&packfile, idx) < 0 || git_vector_insert(&backend->packs, packfile) < 0) { pack_backend__free((git_odb_backend *)backend); @@ -642,6 +664,9 @@ struct pack_backend *backend = NULL; git_buf path = GIT_BUF_INIT; + if (git_mwindow_files_init() < 0) + return -1; + if (pack_backend__alloc(&backend, 8) < 0) return -1; diff -Nru libgit2-0.20.0/src/oidarray.c libgit2-0.22.2/src/oidarray.c --- libgit2-0.20.0/src/oidarray.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/oidarray.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,21 @@ +/* + * 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/oidarray.h" +#include "oidarray.h" +#include "array.h" + +void git_oidarray_free(git_oidarray *arr) +{ + git__free(arr->ids); +} + +void git_oidarray__from_array(git_oidarray *arr, git_array_oid_t *array) +{ + arr->count = array->size; + arr->ids = array->ptr; +} diff -Nru libgit2-0.20.0/src/oidarray.h libgit2-0.22.2/src/oidarray.h --- libgit2-0.20.0/src/oidarray.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/oidarray.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,18 @@ +/* + * 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_oidarray_h__ +#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__from_array(git_oidarray *arr, git_array_oid_t *array); + +#endif diff -Nru libgit2-0.20.0/src/oid.c libgit2-0.22.2/src/oid.c --- libgit2-0.20.0/src/oid.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/oid.c 2015-03-24 16:10:45.000000000 +0000 @@ -8,6 +8,7 @@ #include "common.h" #include "git2/oid.h" #include "repository.h" +#include "global.h" #include #include @@ -24,30 +25,24 @@ size_t p; int v; - if (length > GIT_OID_HEXSZ) - return oid_error_invalid("too long"); + assert(out && str); - for (p = 0; p < length - 1; p += 2) { - v = (git__fromhex(str[p + 0]) << 4) - | git__fromhex(str[p + 1]); + if (!length) + return oid_error_invalid("too short"); - if (v < 0) - return oid_error_invalid("contains invalid characters"); + if (length > GIT_OID_HEXSZ) + return oid_error_invalid("too long"); - out->id[p / 2] = (unsigned char)v; - } + memset(out->id, 0, GIT_OID_RAWSZ); - if (length % 2) { - v = (git__fromhex(str[p + 0]) << 4); + for (p = 0; p < length; p++) { + v = git__fromhex(str[p]); if (v < 0) return oid_error_invalid("contains invalid characters"); - out->id[p / 2] = (unsigned char)v; - p += 2; + out->id[p / 2] |= (unsigned char)(v << (p % 2 ? 0 : 4)); } - memset(out->id + p / 2, 0, (GIT_OID_HEXSZ - p) / 2); - return 0; } @@ -105,6 +100,13 @@ str = fmt_one(str, oid->id[i]); } +char *git_oid_tostr_s(const git_oid *oid) +{ + char *str = GIT_GLOBAL->oid_fmt; + git_oid_nfmt(str, GIT_OID_HEXSZ + 1, oid); + return str; +} + char *git_oid_allocfmt(const git_oid *oid) { char *str = git__malloc(GIT_OID_HEXSZ + 1); @@ -179,6 +181,11 @@ return git_oid__cmp(a, b); } +int git_oid_equal(const git_oid *a, const git_oid *b) +{ + return (git_oid__cmp(a, b) == 0); +} + int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, size_t len) { const unsigned char *a = oid_a->id; @@ -204,7 +211,7 @@ int git_oid_strcmp(const git_oid *oid_a, const char *str) { - const unsigned char *a = oid_a->id; + const unsigned char *a; unsigned char strval; int hexval; @@ -314,6 +321,9 @@ void git_oid_shorten_free(git_oid_shorten *os) { + if (os == NULL) + return; + git__free(os->nodes); git__free(os); } diff -Nru libgit2-0.20.0/src/oid.h libgit2-0.22.2/src/oid.h --- libgit2-0.20.0/src/oid.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/oid.h 2015-03-24 16:10:45.000000000 +0000 @@ -9,6 +9,17 @@ #include "git2/oid.h" +/** + * Format a git_oid into a newly allocated c-string. + * + * The c-string is owned by the caller and needs to be manually freed. + * + * @param id the oid structure to format + * @return the c-string; NULL if memory is exhausted. Caller must + * deallocate the string with git__free(). + */ +char *git_oid_allocfmt(const git_oid *id); + GIT_INLINE(int) git_oid__hashcmp(const unsigned char *sha1, const unsigned char *sha2) { int i; diff -Nru libgit2-0.20.0/src/oidmap.h libgit2-0.22.2/src/oidmap.h --- libgit2-0.20.0/src/oidmap.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/oidmap.h 2015-03-24 16:10:45.000000000 +0000 @@ -32,4 +32,20 @@ #define git_oidmap_alloc() kh_init(oid) #define git_oidmap_free(h) kh_destroy(oid,h), h = NULL +#define git_oidmap_lookup_index(h, k) kh_get(oid, h, k) +#define git_oidmap_valid_index(h, idx) (idx != kh_end(h)) + +#define git_oidmap_value_at(h, idx) kh_val(h, idx) + +#define git_oidmap_insert(h, key, val, rval) do { \ + khiter_t __pos = kh_put(oid, h, key, &rval); \ + if (rval >= 0) { \ + if (rval == 0) kh_key(h, __pos) = key; \ + kh_val(h, __pos) = val; \ + } } while (0) + +#define git_oidmap_foreach_value kh_foreach_value + +#define git_oidmap_size(h) kh_size(h) + #endif diff -Nru libgit2-0.20.0/src/openssl_stream.c libgit2-0.22.2/src/openssl_stream.c --- libgit2-0.20.0/src/openssl_stream.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/openssl_stream.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,378 @@ +/* + * 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_SSL + +#include +#include +#include +#include + +#include "global.h" +#include "posix.h" +#include "stream.h" +#include "socket_stream.h" +#include "netops.h" +#include "git2/transport.h" + +#include +#include +#include + +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\n"); + break; + case SSL_ERROR_WANT_X509_LOOKUP: + giterr_set(GITERR_NET, "SSL error: x509 error\n"); + 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"); + 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; + + SSL_free(ssl); + 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_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_data(str), size); + peer_cn[size] = '\0'; + } + } 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_socket_stream *socket; + SSL *ssl; + git_cert_x509 cert_info; +} openssl_stream; + +int openssl_close(git_stream *stream); + +int openssl_connect(git_stream *stream) +{ + int ret; + openssl_stream *st = (openssl_stream *) stream; + + if ((ret = git_stream_connect((git_stream *)st->socket)) < 0) + return ret; + + if ((ret = SSL_set_fd(st->ssl, st->socket->s)) <= 0) { + openssl_close((git_stream *) st); + return ssl_set_error(st->ssl, ret); + } + + if ((ret = SSL_connect(st->ssl)) <= 0) + return ssl_set_error(st->ssl, ret); + + return verify_server_cert(st->ssl, st->socket->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.cert_type = GIT_CERT_X509; + st->cert_info.data = encoded_cert; + st->cert_info.len = len; + + *out = (git_cert *)&st->cert_info; + return 0; +} + +ssize_t openssl_write(git_stream *stream, const char *data, size_t len, int flags) +{ + openssl_stream *st = (openssl_stream *) stream; + int ret; + size_t off = 0; + + GIT_UNUSED(flags); + + while (off < len) { + ret = SSL_write(st->ssl, data + off, len - off); + if (ret <= 0 && ret != SSL_ERROR_WANT_WRITE) + return ssl_set_error(st->ssl, ret); + + off += ret; + } + + return off; +} + +ssize_t openssl_read(git_stream *stream, void *data, size_t len) +{ + openssl_stream *st = (openssl_stream *) stream; + int ret; + + do { + ret = SSL_read(st->ssl, data, len); + } while (SSL_get_error(st->ssl, ret) == SSL_ERROR_WANT_READ); + + if (ret < 0) { + ssl_set_error(st->ssl, ret); + return -1; + } + + return ret; +} + +int openssl_close(git_stream *stream) +{ + openssl_stream *st = (openssl_stream *) stream; + int ret; + + if ((ret = ssl_teardown(st->ssl)) < 0) + return -1; + + return git_stream_close((git_stream *)st->socket); +} + +void openssl_free(git_stream *stream) +{ + openssl_stream *st = (openssl_stream *) stream; + + git__free(st->cert_info.data); + git_stream_free((git_stream *) st->socket); + git__free(st); +} + +int git_openssl_stream_new(git_stream **out, const char *host, const char *port) +{ + openssl_stream *st; + + st = git__calloc(1, sizeof(openssl_stream)); + GITERR_CHECK_ALLOC(st); + + if (git_socket_stream_new((git_stream **) &st->socket, host, port)) + return -1; + + st->ssl = SSL_new(git__ssl_ctx); + if (st->ssl == NULL) { + giterr_set(GITERR_SSL, "failed to create ssl object"); + return -1; + } + + st->parent.version = GIT_STREAM_VERSION; + st->parent.encrypted = 1; + st->parent.connect = openssl_connect; + st->parent.certificate = openssl_certificate; + 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; +} + +#else + +#include "stream.h" + +int git_openssl_stream_new(git_stream **out, const char *host, const char *port) +{ + giterr_set(GITERR_SSL, "openssl is not supported in this version"); + return -1; +} + +#endif diff -Nru libgit2-0.20.0/src/openssl_stream.h libgit2-0.22.2/src/openssl_stream.h --- libgit2-0.20.0/src/openssl_stream.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/openssl_stream.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,14 @@ +/* + * 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_new(git_stream **out, const char *host, const char *port); + +#endif diff -Nru libgit2-0.20.0/src/pack.c libgit2-0.22.2/src/pack.c --- libgit2-0.20.0/src/pack.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/pack.c 2015-03-24 16:10:45.000000000 +0000 @@ -56,6 +56,7 @@ if (!e) return NULL; + git_atomic_inc(&e->refcount); memcpy(&e->raw, source, sizeof(git_rawobj)); return e; @@ -83,16 +84,12 @@ } git_offmap_free(cache->entries); - git_mutex_free(&cache->lock); + cache->entries = NULL; } - - memset(cache, 0, sizeof(*cache)); } static int cache_init(git_pack_cache *cache) { - memset(cache, 0, sizeof(*cache)); - cache->entries = git_offmap_alloc(); GITERR_CHECK_ALLOC(cache->entries); @@ -149,7 +146,11 @@ } } -static int cache_add(git_pack_cache *cache, git_rawobj *base, git_off_t offset) +static int cache_add( + git_pack_cache_entry **cached_out, + git_pack_cache *cache, + git_rawobj *base, + git_off_t offset) { git_pack_cache_entry *entry; int error, exists = 0; @@ -162,6 +163,7 @@ if (entry) { if (git_mutex_lock(&cache->lock) < 0) { giterr_set(GITERR_OS, "failed to lock cache"); + git__free(entry); return -1; } /* Add it to the cache if nobody else has */ @@ -174,6 +176,8 @@ assert(error != 0); kh_value(cache->entries, k) = entry; cache->memory_used += entry->raw.len; + + *cached_out = entry; } git_mutex_unlock(&cache->lock); /* Somebody beat us to adding it into the cache */ @@ -412,11 +416,14 @@ size = c & 15; shift = 4; while (c & 0x80) { - if (len <= used) + if (len <= used) { + giterr_set(GITERR_ODB, "buffer too small"); return GIT_EBUFS; + } if (bitsizeof(long) <= shift) { *usedp = 0; + giterr_set(GITERR_ODB, "packfile corrupted"); return -1; } @@ -514,72 +521,102 @@ return error; } -static int packfile_unpack_delta( - git_rawobj *obj, - struct git_pack_file *p, - git_mwindow **w_curs, - git_off_t *curpos, - size_t delta_size, - git_otype delta_type, - git_off_t obj_offset) +#define SMALL_STACK_SIZE 64 + +/** + * Generate the chain of dependencies which we need to get to the + * object at `off`. `chain` is used a stack, popping gives the right + * order to apply deltas on. If an object is found in the pack's base + * cache, we stop calculating there. + */ +static int pack_dependency_chain(git_dependency_chain *chain_out, + git_pack_cache_entry **cached_out, git_off_t *cached_off, + struct pack_chain_elem *small_stack, size_t *stack_sz, + struct git_pack_file *p, git_off_t obj_offset) { - git_off_t base_offset, base_key; - git_rawobj base, delta; - git_pack_cache_entry *cached = NULL; - int error, found_base = 0; + git_dependency_chain chain = GIT_ARRAY_INIT; + git_mwindow *w_curs = NULL; + git_off_t curpos = obj_offset, base_offset; + int error = 0, use_heap = 0; + size_t size, elem_pos; + git_otype type; - base_offset = get_delta_base(p, w_curs, curpos, delta_type, obj_offset); - git_mwindow_close(w_curs); - if (base_offset == 0) - return packfile_error("delta offset is zero"); - if (base_offset < 0) /* must actually be an error code */ - return (int)base_offset; + elem_pos = 0; + while (true) { + struct pack_chain_elem *elem; + git_pack_cache_entry *cached = NULL; + + /* if we have a base cached, we can stop here instead */ + if ((cached = cache_get(&p->bases, obj_offset)) != NULL) { + *cached_out = cached; + *cached_off = obj_offset; + break; + } - if (!p->bases.entries && (cache_init(&p->bases) < 0)) - return -1; + /* if we run out of space on the small stack, use the array */ + if (elem_pos == SMALL_STACK_SIZE) { + git_array_init_to_size(chain, elem_pos); + GITERR_CHECK_ARRAY(chain); + memcpy(chain.ptr, small_stack, elem_pos * sizeof(struct pack_chain_elem)); + chain.size = elem_pos; + use_heap = 1; + } - base_key = base_offset; /* git_packfile_unpack modifies base_offset */ - if ((cached = cache_get(&p->bases, base_offset)) != NULL) { - memcpy(&base, &cached->raw, sizeof(git_rawobj)); - found_base = 1; - } + curpos = obj_offset; + if (!use_heap) { + elem = &small_stack[elem_pos]; + } else { + elem = git_array_alloc(chain); + if (!elem) { + error = -1; + goto on_error; + } + } - if (!cached) { /* have to inflate it */ - error = git_packfile_unpack(&base, p, &base_offset); + elem->base_key = obj_offset; + + error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos); + git_mwindow_close(&w_curs); - /* - * TODO: git.git tries to load the base from other packfiles - * or loose objects. - * - * We'll need to do this in order to support thin packs. - */ if (error < 0) - return error; - } + goto on_error; - error = packfile_unpack_compressed(&delta, p, w_curs, curpos, delta_size, delta_type); - git_mwindow_close(w_curs); + elem->offset = curpos; + elem->size = size; + elem->type = type; + elem->base_key = obj_offset; - if (error < 0) { - if (!found_base) - git__free(base.data); - return error; - } + if (type != GIT_OBJ_OFS_DELTA && type != GIT_OBJ_REF_DELTA) + break; - obj->type = base.type; - error = git__delta_apply(obj, base.data, base.len, delta.data, delta.len); - if (error < 0) - goto on_error; + base_offset = get_delta_base(p, &w_curs, &curpos, type, obj_offset); + git_mwindow_close(&w_curs); - if (found_base) - git_atomic_dec(&cached->refcount); - else if (cache_add(&p->bases, &base, base_key) < 0) - git__free(base.data); + if (base_offset == 0) { + error = packfile_error("delta offset is zero"); + goto on_error; + } + if (base_offset < 0) { /* must actually be an error code */ + error = (int)base_offset; + goto on_error; + } -on_error: - git__free(delta.data); + /* we need to pass the pos *after* the delta-base bit */ + elem->offset = curpos; + + /* go through the loop again, but with the new object */ + obj_offset = base_offset; + elem_pos++; + } + + + *stack_sz = elem_pos + 1; + *chain_out = chain; + return error; - return error; /* error set by git__delta_apply */ +on_error: + git_array_clear(chain); + return error; } int git_packfile_unpack( @@ -589,48 +626,135 @@ { git_mwindow *w_curs = NULL; git_off_t curpos = *obj_offset; - int error; - - size_t size = 0; - git_otype type; + int error, free_base = 0; + git_dependency_chain chain = GIT_ARRAY_INIT; + struct pack_chain_elem *elem = NULL, *stack; + git_pack_cache_entry *cached = NULL; + struct pack_chain_elem small_stack[SMALL_STACK_SIZE]; + size_t stack_size = 0, elem_pos; + git_otype base_type; /* * TODO: optionally check the CRC on the packfile */ + error = pack_dependency_chain(&chain, &cached, obj_offset, small_stack, &stack_size, p, *obj_offset); + if (error < 0) + return error; + obj->data = NULL; obj->len = 0; obj->type = GIT_OBJ_BAD; - error = git_packfile_unpack_header(&size, &type, &p->mwf, &w_curs, &curpos); - git_mwindow_close(&w_curs); - - if (error < 0) - return error; + /* let's point to the right stack */ + stack = chain.ptr ? chain.ptr : small_stack; - switch (type) { - case GIT_OBJ_OFS_DELTA: - case GIT_OBJ_REF_DELTA: - error = packfile_unpack_delta( - obj, p, &w_curs, &curpos, - size, type, *obj_offset); - break; + elem_pos = stack_size; + if (cached) { + memcpy(obj, &cached->raw, sizeof(git_rawobj)); + base_type = obj->type; + elem_pos--; /* stack_size includes the base, which isn't actually there */ + } else { + elem = &stack[--elem_pos]; + base_type = elem->type; + } + switch (base_type) { case GIT_OBJ_COMMIT: case GIT_OBJ_TREE: case GIT_OBJ_BLOB: case GIT_OBJ_TAG: - error = packfile_unpack_compressed( - obj, p, &w_curs, &curpos, - size, type); + if (!cached) { + curpos = elem->offset; + error = packfile_unpack_compressed(obj, p, &w_curs, &curpos, elem->size, elem->type); + git_mwindow_close(&w_curs); + base_type = elem->type; + } + if (error < 0) + goto cleanup; break; - + case GIT_OBJ_OFS_DELTA: + case GIT_OBJ_REF_DELTA: + error = packfile_error("dependency chain ends in a delta"); + goto cleanup; default: - error = packfile_error("invalid packfile type in header");; - break; + error = packfile_error("invalid packfile type in header"); + goto cleanup; } - *obj_offset = curpos; + /* + * Finding the object we want a cached base element is + * problematic, as we need to make sure we don't accidentally + * give the caller the cached object, which it would then feel + * free to free, so we need to copy the data. + */ + if (cached && stack_size == 1) { + void *data = obj->data; + obj->data = git__malloc(obj->len + 1); + GITERR_CHECK_ALLOC(obj->data); + memcpy(obj->data, data, obj->len + 1); + git_atomic_dec(&cached->refcount); + goto cleanup; + } + + /* we now apply each consecutive delta until we run out */ + while (elem_pos > 0 && !error) { + git_rawobj base, delta; + + /* + * We can now try to add the base to the cache, as + * long as it's not already the cached one. + */ + if (!cached) + free_base = !!cache_add(&cached, &p->bases, obj, elem->base_key); + + elem = &stack[elem_pos - 1]; + curpos = elem->offset; + error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, elem->size, elem->type); + git_mwindow_close(&w_curs); + + if (error < 0) + break; + + /* the current object becomes the new base, on which we apply the delta */ + base = *obj; + obj->data = NULL; + obj->len = 0; + obj->type = GIT_OBJ_BAD; + + error = git__delta_apply(obj, base.data, base.len, delta.data, delta.len); + obj->type = base_type; + /* + * We usually don't want to free the base at this + * point, as we put it into the cache in the previous + * iteration. free_base lets us know that we got the + * base object directly from the packfile, so we can free it. + */ + git__free(delta.data); + if (free_base) { + free_base = 0; + git__free(base.data); + } + + if (cached) { + git_atomic_dec(&cached->refcount); + cached = NULL; + } + + if (error < 0) + break; + + elem_pos--; + } + +cleanup: + if (error < 0) + git__free(obj->data); + + if (elem) + *obj_offset = curpos; + + git_array_clear(chain); return error; } @@ -660,7 +784,7 @@ st = inflateInit(&obj->zstream); if (st != Z_OK) { git__free(obj); - giterr_set(GITERR_ZLIB, "Failed to inflate packfile"); + giterr_set(GITERR_ZLIB, "failed to init packfile stream"); return -1; } @@ -691,7 +815,7 @@ written = len - obj->zstream.avail_out; if (st != Z_OK && st != Z_STREAM_END) { - giterr_set(GITERR_ZLIB, "Failed to inflate packfile"); + giterr_set(GITERR_ZLIB, "error reading from the zlib stream"); return -1; } @@ -736,7 +860,7 @@ st = inflateInit(&stream); if (st != Z_OK) { git__free(buffer); - giterr_set(GITERR_ZLIB, "Failed to inflate packfile"); + giterr_set(GITERR_ZLIB, "failed to init zlib stream on unpack"); return -1; } @@ -763,7 +887,7 @@ if ((st != Z_STREAM_END) || stream.total_out != size) { git__free(buffer); - giterr_set(GITERR_ZLIB, "Failed to inflate packfile"); + giterr_set(GITERR_ZLIB, "error inflating zlib stream"); return -1; } @@ -852,16 +976,17 @@ cache_free(&p->bases); - git_mwindow_free_all(&p->mwf); - - if (p->mwf.fd >= 0) + if (p->mwf.fd >= 0) { + git_mwindow_free_all_locked(&p->mwf); p_close(p->mwf.fd); + } pack_index_free(p); git__free(p->bad_object_sha1); git_mutex_free(&p->lock); + git_mutex_free(&p->bases.lock); git__free(p); } @@ -946,6 +1071,23 @@ return -1; } +int git_packfile__name(char **out, const char *path) +{ + size_t path_len; + git_buf buf = GIT_BUF_INIT; + + path_len = strlen(path); + + if (path_len < strlen(".idx")) + return git_odb__error_notfound("invalid packfile path", NULL); + + if (git_buf_printf(&buf, "%.*s.pack", (int)(path_len - strlen(".idx")), path) < 0) + return -1; + + *out = git_buf_detach(&buf); + return 0; +} + int git_packfile_alloc(struct git_pack_file **pack_out, const char *path) { struct stat st; @@ -974,7 +1116,6 @@ p->pack_keep = 1; memcpy(p->pack_name + root_len, ".pack", sizeof(".pack")); - path_len = path_len - strlen(".idx") + strlen(".pack"); } if (p_stat(p->pack_name, &st) < 0 || !S_ISREG(st.st_mode)) { @@ -997,10 +1138,10 @@ return -1; } - /* see if we can parse the sha1 oid in the packfile name */ - if (path_len < 40 || - git_oid_fromstr(&p->sha1, path + path_len - GIT_OID_HEXSZ) < 0) - memset(&p->sha1, 0x0, GIT_OID_RAWSZ); + if (cache_init(&p->bases) < 0) { + git__free(p); + return -1; + } *pack_out = p; @@ -1042,10 +1183,9 @@ { const unsigned char *index = p->index_map.data, *current; uint32_t i; + int error = 0; if (index == NULL) { - int error; - if ((error = pack_index_open(p)) < 0) return error; @@ -1062,7 +1202,6 @@ if (p->oids == NULL) { git_vector offsets, oids; - int error; if ((error = git_vector_init(&oids, p->num_objects, NULL))) return error; @@ -1084,15 +1223,16 @@ git_vector_foreach(&offsets, i, current) git_vector_insert(&oids, (void*)¤t[4]); } + git_vector_free(&offsets); - p->oids = (git_oid **)oids.contents; + p->oids = (git_oid **)git_vector_detach(NULL, NULL, &oids); } for (i = 0; i < p->num_objects; i++) - if (cb(p->oids[i], data)) - return GIT_EUSER; + if ((error = cb(p->oids[i], data)) != 0) + return giterr_set_after_callback(error); - return 0; + return error; } static int pack_entry_find_offset( diff -Nru libgit2-0.20.0/src/pack.h libgit2-0.22.2/src/pack.h --- libgit2-0.20.0/src/pack.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/pack.h 2015-03-24 16:10:45.000000000 +0000 @@ -17,6 +17,7 @@ #include "mwindow.h" #include "odb.h" #include "oidmap.h" +#include "array.h" #define GIT_PACK_FILE_MODE 0444 @@ -60,6 +61,15 @@ git_rawobj raw; } git_pack_cache_entry; +struct pack_chain_elem { + git_off_t base_key; + git_off_t offset; + size_t size; + git_otype type; +}; + +typedef git_array_t(struct pack_chain_elem) git_dependency_chain; + #include "offmap.h" GIT__USE_OFFMAP; @@ -80,6 +90,7 @@ git_mwindow_file mwf; git_map index_map; git_mutex lock; /* protect updates to mwf and index_map */ + git_atomic refcount; uint32_t num_objects; uint32_t num_bad_objects; @@ -88,7 +99,6 @@ int index_version; git_time_t mtime; unsigned pack_local:1, pack_keep:1, has_cache:1; - git_oid sha1; git_oidmap *idx_cache; git_oid **oids; @@ -114,6 +124,8 @@ size_t git_packfile__object_header(unsigned char *hdr, size_t size, git_otype type); +int git_packfile__name(char **out, const char *path); + int git_packfile_unpack_header( size_t *size_p, git_otype *type_p, diff -Nru libgit2-0.20.0/src/pack-objects.c libgit2-0.22.2/src/pack-objects.c --- libgit2-0.20.0/src/pack-objects.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/pack-objects.c 2015-03-24 16:10:45.000000000 +0000 @@ -7,7 +7,7 @@ #include "pack-objects.h" -#include "compress.h" +#include "zstream.h" #include "delta.h" #include "iterator.h" #include "netops.h" @@ -61,6 +61,9 @@ /* The minimal interval between progress updates (in seconds). */ #define MIN_PROGRESS_UPDATE_INTERVAL 0.5 +/* Size of the buffer to feed to zlib */ +#define COMPRESS_BUFLEN (1024 * 1024) + static unsigned name_hash(const char *name) { unsigned c, hash = 0; @@ -87,8 +90,8 @@ int ret; int64_t val; - if (git_repository_config__weakptr(&config, pb->repo) < 0) - return -1; + if ((ret = git_repository_config_snapshot(&config, pb->repo)) < 0) + return ret; #define config_get(KEY,DST,DFLT) do { \ ret = git_config_get_int64(&val, config, KEY); \ @@ -106,6 +109,8 @@ #undef config_get + git_config_free(config); + return 0; } @@ -127,6 +132,7 @@ pb->nr_threads = 1; /* do not spawn any thread by default */ if (git_hash_ctx_init(&pb->ctx) < 0 || + git_zstream_init(&pb->zstream) < 0 || git_repository_odb(&pb->odb, repo) < 0 || packbuilder_config(pb) < 0) goto on_error; @@ -205,14 +211,18 @@ po = pb->object_list + pb->nr_objects; memset(po, 0x0, sizeof(*po)); - if (git_odb_read_header(&po->size, &po->type, pb->odb, oid) < 0) - return -1; + if ((ret = git_odb_read_header(&po->size, &po->type, pb->odb, oid)) < 0) + return ret; pb->nr_objects++; git_oid_cpy(&po->id, oid); po->hash = name_hash(name); pos = kh_put(oid, pb->object_ix, &po->id, &ret); + if (ret < 0) { + giterr_set_oom(); + return ret; + } assert(ret != 0); kh_value(pb->object_ix, pos) = po; @@ -220,12 +230,17 @@ if (pb->progress_cb) { double current_time = git__timer(); - if ((current_time - pb->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) { + double elapsed = current_time - pb->last_progress_report_time; + + if (elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) { pb->last_progress_report_time = current_time; - if (pb->progress_cb(GIT_PACKBUILDER_ADDING_OBJECTS, pb->nr_objects, 0, pb->progress_cb_payload)) { - giterr_clear(); - return GIT_EUSER; - } + + ret = pb->progress_cb( + GIT_PACKBUILDER_ADDING_OBJECTS, + pb->nr_objects, 0, pb->progress_cb_payload); + + if (ret) + return giterr_set_after_callback(ret); } } @@ -266,76 +281,96 @@ return -1; } -static int write_object(git_buf *buf, git_packbuilder *pb, git_pobject *po) +static int write_object( + git_packbuilder *pb, + git_pobject *po, + int (*write_cb)(void *buf, size_t size, void *cb_data), + void *cb_data) { git_odb_object *obj = NULL; - git_buf zbuf = GIT_BUF_INIT; git_otype type; - unsigned char hdr[10]; - size_t hdr_len; - unsigned long size; - void *data; + unsigned char hdr[10], *zbuf = NULL; + void *data = NULL; + size_t hdr_len, zbuf_len = COMPRESS_BUFLEN, data_len; + int error; + /* + * If we have a delta base, let's use the delta to save space. + * Otherwise load the whole object. 'data' ends up pointing to + * whatever data we want to put into the packfile. + */ if (po->delta) { if (po->delta_data) data = po->delta_data; - else if (get_delta(&data, pb->odb, po) < 0) - goto on_error; - size = po->delta_size; + else if ((error = get_delta(&data, pb->odb, po)) < 0) + goto done; + + data_len = po->delta_size; type = GIT_OBJ_REF_DELTA; } else { - if (git_odb_read(&obj, pb->odb, &po->id)) - goto on_error; + if ((error = git_odb_read(&obj, pb->odb, &po->id)) < 0) + goto done; data = (void *)git_odb_object_data(obj); - size = (unsigned long)git_odb_object_size(obj); + data_len = git_odb_object_size(obj); type = git_odb_object_type(obj); } /* Write header */ - hdr_len = git_packfile__object_header(hdr, size, type); + hdr_len = git_packfile__object_header(hdr, data_len, type); - if (git_buf_put(buf, (char *)hdr, hdr_len) < 0) - goto on_error; - - if (git_hash_update(&pb->ctx, hdr, hdr_len) < 0) - goto on_error; + if ((error = write_cb(hdr, hdr_len, cb_data)) < 0 || + (error = git_hash_update(&pb->ctx, hdr, hdr_len)) < 0) + goto done; if (type == GIT_OBJ_REF_DELTA) { - if (git_buf_put(buf, (char *)po->delta->id.id, GIT_OID_RAWSZ) < 0 || - git_hash_update(&pb->ctx, po->delta->id.id, GIT_OID_RAWSZ) < 0) - goto on_error; + if ((error = write_cb(po->delta->id.id, GIT_OID_RAWSZ, cb_data)) < 0 || + (error = git_hash_update(&pb->ctx, po->delta->id.id, GIT_OID_RAWSZ)) < 0) + goto done; } /* Write data */ - if (po->z_delta_size) - size = po->z_delta_size; - else if (git__compress(&zbuf, data, size) < 0) - goto on_error; - else { - if (po->delta) - git__free(data); - data = zbuf.ptr; - size = (unsigned long)zbuf.size; - } + if (po->z_delta_size) { + data_len = po->z_delta_size; - if (git_buf_put(buf, data, size) < 0 || - git_hash_update(&pb->ctx, data, size) < 0) - goto on_error; + if ((error = write_cb(data, data_len, cb_data)) < 0 || + (error = git_hash_update(&pb->ctx, data, data_len)) < 0) + goto done; + } else { + zbuf = git__malloc(zbuf_len); + GITERR_CHECK_ALLOC(zbuf); - if (po->delta_data) - git__free(po->delta_data); + git_zstream_reset(&pb->zstream); + git_zstream_set_input(&pb->zstream, data, data_len); - git_odb_object_free(obj); - git_buf_free(&zbuf); + while (!git_zstream_done(&pb->zstream)) { + if ((error = git_zstream_get_output(zbuf, &zbuf_len, &pb->zstream)) < 0 || + (error = write_cb(zbuf, zbuf_len, cb_data)) < 0 || + (error = git_hash_update(&pb->ctx, zbuf, zbuf_len)) < 0) + goto done; + + zbuf_len = COMPRESS_BUFLEN; /* reuse buffer */ + } + } + + /* + * If po->delta is true, data is a delta and it is our + * responsibility to free it (otherwise it's a git_object's + * data). We set po->delta_data to NULL in case we got the + * data from there instead of get_delta(). If we didn't, + * there's no harm. + */ + if (po->delta) { + git__free(data); + po->delta_data = NULL; + } pb->nr_written++; - return 0; -on_error: +done: + git__free(zbuf); git_odb_object_free(obj); - git_buf_free(&zbuf); - return -1; + return error; } enum write_one_status { @@ -345,9 +380,15 @@ WRITE_ONE_RECURSIVE = 2 /* already scheduled to be written */ }; -static int write_one(git_buf *buf, git_packbuilder *pb, git_pobject *po, - enum write_one_status *status) +static int write_one( + enum write_one_status *status, + git_packbuilder *pb, + git_pobject *po, + int (*write_cb)(void *buf, size_t size, void *cb_data), + void *cb_data) { + int error; + if (po->recursing) { *status = WRITE_ONE_RECURSIVE; return 0; @@ -358,21 +399,20 @@ if (po->delta) { po->recursing = 1; - if (write_one(buf, pb, po->delta, status) < 0) - return -1; - switch (*status) { - case WRITE_ONE_RECURSIVE: - /* we cannot depend on this one */ + + if ((error = write_one(status, pb, po->delta, write_cb, cb_data)) < 0) + return error; + + /* we cannot depend on this one */ + if (*status == WRITE_ONE_RECURSIVE) po->delta = NULL; - break; - default: - break; - } } + *status = WRITE_ONE_WRITTEN; po->written = 1; po->recursing = 0; - return write_object(buf, pb, po); + + return write_object(pb, po, write_cb, cb_data); } GIT_INLINE(void) add_to_write_order(git_pobject **wo, unsigned int *endp, @@ -552,12 +592,11 @@ } static int write_pack(git_packbuilder *pb, - int (*cb)(void *buf, size_t size, void *data), - void *data) + int (*write_cb)(void *buf, size_t size, void *cb_data), + void *cb_data) { git_pobject **write_order; git_pobject *po; - git_buf buf = GIT_BUF_INIT; enum write_one_status status; struct git_pack_header ph; git_oid entry_oid; @@ -575,10 +614,8 @@ ph.hdr_version = htonl(PACK_VERSION); ph.hdr_entries = htonl(pb->nr_objects); - if ((error = cb(&ph, sizeof(ph), data)) < 0) - goto done; - - if ((error = git_hash_update(&pb->ctx, &ph, sizeof(ph))) < 0) + if ((error = write_cb(&ph, sizeof(ph), cb_data)) < 0 || + (error = git_hash_update(&pb->ctx, &ph, sizeof(ph))) < 0) goto done; pb->nr_remaining = pb->nr_objects; @@ -586,25 +623,30 @@ pb->nr_written = 0; for ( ; i < pb->nr_objects; ++i) { po = write_order[i]; - if ((error = write_one(&buf, pb, po, &status)) < 0) - goto done; - if ((error = cb(buf.ptr, buf.size, data)) < 0) + + if ((error = write_one(&status, pb, po, write_cb, cb_data)) < 0) goto done; - git_buf_clear(&buf); } pb->nr_remaining -= pb->nr_written; } while (pb->nr_remaining && i < pb->nr_objects); - if ((error = git_hash_final(&entry_oid, &pb->ctx)) < 0) goto done; - error = cb(entry_oid.id, GIT_OID_RAWSZ, data); + error = write_cb(entry_oid.id, GIT_OID_RAWSZ, cb_data); done: + /* if callback cancelled writing, we must still free delta_data */ + for ( ; i < pb->nr_objects; ++i) { + po = write_order[i]; + if (po->delta_data) { + git__free(po->delta_data); + po->delta_data = NULL; + } + } + git__free(write_order); - git_buf_free(&buf); return error; } @@ -911,7 +953,7 @@ * between writes at that moment. */ if (po->delta_data) { - if (git__compress(&zbuf, po->delta_data, po->delta_size) < 0) + if (git_zstream_deflatebuf(&zbuf, po->delta_data, po->delta_size) < 0) goto on_error; git__free(po->delta_data); @@ -1167,7 +1209,7 @@ git_mutex_unlock(&target->mutex); if (!sub_size) { - git_thread_join(target->thread, NULL); + git_thread_join(&target->thread, NULL); git_cond_free(&target->cond); git_mutex_free(&target->mutex); active_threads--; @@ -1236,6 +1278,7 @@ int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb) { PREPARE_PACK; + git_buf_sanitize(buf); return write_pack(pb, &write_pack_buf, buf); } @@ -1249,7 +1292,7 @@ git_packbuilder *pb, const char *path, unsigned int mode, - git_transfer_progress_callback progress_cb, + git_transfer_progress_cb progress_cb, void *progress_cb_payload) { git_indexer *indexer; @@ -1284,21 +1327,22 @@ return &pb->pack_oid; } -static int cb_tree_walk(const char *root, const git_tree_entry *entry, void *payload) +static int cb_tree_walk( + const char *root, const git_tree_entry *entry, void *payload) { + int error; struct tree_walk_context *ctx = payload; /* A commit inside a tree represents a submodule commit and should be skipped. */ if (git_tree_entry_type(entry) == GIT_OBJ_COMMIT) return 0; - if (git_buf_sets(&ctx->buf, root) < 0 || - git_buf_puts(&ctx->buf, git_tree_entry_name(entry)) < 0) - return -1; + if (!(error = git_buf_sets(&ctx->buf, root)) && + !(error = git_buf_puts(&ctx->buf, git_tree_entry_name(entry)))) + error = git_packbuilder_insert( + ctx->pb, git_tree_entry_id(entry), git_buf_cstr(&ctx->buf)); - return git_packbuilder_insert(ctx->pb, - git_tree_entry_id(entry), - git_buf_cstr(&ctx->buf)); + return error; } int git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *oid) @@ -1318,22 +1362,17 @@ int git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *oid) { - git_tree *tree; + int error; + git_tree *tree = NULL; struct tree_walk_context context = { pb, GIT_BUF_INIT }; - if (git_tree_lookup(&tree, pb->repo, oid) < 0 || - git_packbuilder_insert(pb, oid, NULL) < 0) - return -1; - - if (git_tree_walk(tree, GIT_TREEWALK_PRE, cb_tree_walk, &context) < 0) { - git_tree_free(tree); - git_buf_free(&context.buf); - return -1; - } + if (!(error = git_tree_lookup(&tree, pb->repo, oid)) && + !(error = git_packbuilder_insert(pb, oid, NULL))) + error = git_tree_walk(tree, GIT_TREEWALK_PRE, cb_tree_walk, &context); git_tree_free(tree); git_buf_free(&context.buf); - return 0; + return error; } uint32_t git_packbuilder_object_count(git_packbuilder *pb) @@ -1380,6 +1419,7 @@ git__free(pb->object_list); git_hash_ctx_cleanup(&pb->ctx); + git_zstream_free(&pb->zstream); git__free(pb); } diff -Nru libgit2-0.20.0/src/pack-objects.h libgit2-0.22.2/src/pack-objects.h --- libgit2-0.20.0/src/pack-objects.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/pack-objects.h 2015-03-24 16:10:45.000000000 +0000 @@ -14,6 +14,7 @@ #include "hash.h" #include "oidmap.h" #include "netops.h" +#include "zstream.h" #include "git2/oid.h" #include "git2/pack.h" @@ -54,6 +55,7 @@ git_odb *odb; /* associated object database */ git_hash_ctx ctx; + git_zstream zstream; uint32_t nr_objects, nr_alloc, diff -Nru libgit2-0.20.0/src/path.c libgit2-0.22.2/src/path.c --- libgit2-0.20.0/src/path.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/path.c 2015-03-24 16:10:45.000000000 +0000 @@ -7,12 +7,13 @@ #include "common.h" #include "path.h" #include "posix.h" +#include "repository.h" #ifdef GIT_WIN32 #include "win32/posix.h" +#include "win32/w32_util.h" #else #include #endif -#include #include #include @@ -262,26 +263,31 @@ int git_path_join_unrooted( git_buf *path_out, const char *path, const char *base, ssize_t *root_at) { - int error, root; + ssize_t root; assert(path && path_out); - root = git_path_root(path); + root = (ssize_t)git_path_root(path); if (base != NULL && root < 0) { - error = git_buf_joinpath(path_out, base, path); + if (git_buf_joinpath(path_out, base, path) < 0) + return -1; - if (root_at) - *root_at = (ssize_t)strlen(base); - } - else { - error = git_buf_sets(path_out, path); + root = (ssize_t)strlen(base); + } else { + if (git_buf_sets(path_out, path) < 0) + return -1; - if (root_at) - *root_at = (root < 0) ? 0 : (ssize_t)root; + if (root < 0) + root = 0; + else if (base) + git_path_equal_or_prefixed(base, path, &root); } - return error; + if (root_at) + *root_at = root; + + return 0; } int git_path_prettify(git_buf *path_out, const char *path, const char *base) @@ -377,26 +383,33 @@ return -1; } -int git_path_fromurl(git_buf *local_path_out, const char *file_url) +static int local_file_url_prefixlen(const char *file_url) { - int offset = 0, len; + int len = -1; - assert(local_path_out && file_url); + if (git__prefixcmp(file_url, "file://") == 0) { + if (file_url[7] == '/') + len = 8; + else if (git__prefixcmp(file_url + 7, "localhost/") == 0) + len = 17; + } - if (git__prefixcmp(file_url, "file://") != 0) - return error_invalid_local_file_uri(file_url); + return len; +} - offset += 7; - len = (int)strlen(file_url); +bool git_path_is_local_file_url(const char *file_url) +{ + return (local_file_url_prefixlen(file_url) > 0); +} - if (offset < len && file_url[offset] == '/') - offset++; - else if (offset < len && git__prefixcmp(file_url + offset, "localhost/") == 0) - offset += 10; - else - return error_invalid_local_file_uri(file_url); +int git_path_fromurl(git_buf *local_path_out, const char *file_url) +{ + int offset; - if (offset >= len || file_url[offset] == '/') + assert(local_path_out && file_url); + + if ((offset = local_file_url_prefixlen(file_url)) < 0 || + file_url[offset] == '\0' || file_url[offset] == '/') return error_invalid_local_file_uri(file_url); #ifndef GIT_WIN32 @@ -404,14 +417,13 @@ #endif git_buf_clear(local_path_out); - return git__percent_decode(local_path_out, file_url + offset); } int git_path_walk_up( git_buf *path, const char *ceiling, - int (*cb)(void *data, git_buf *), + int (*cb)(void *data, const char *), void *data) { int error = 0; @@ -429,15 +441,27 @@ } scan = git_buf_len(path); + /* empty path: yield only once */ + if (!scan) { + error = cb(data, ""); + if (error) + giterr_set_after_callback(error); + return error; + } + iter.ptr = path->ptr; iter.size = git_buf_len(path); iter.asize = path->asize; while (scan >= stop) { - error = cb(data, &iter); + error = cb(data, iter.ptr); iter.ptr[scan] = oldc; - if (error < 0) + + if (error) { + giterr_set_after_callback(error); break; + } + scan = git_buf_rfind_next(&iter, '/'); if (scan >= 0) { scan++; @@ -450,6 +474,13 @@ if (scan >= 0) iter.ptr[scan] = oldc; + /* relative path: yield for the last component */ + if (!error && stop == 0 && iter.ptr[0] != '/') { + error = cb(data, ""); + if (error) + giterr_set_after_callback(error); + } + return error; } @@ -483,33 +514,43 @@ bool git_path_is_empty_dir(const char *path) { - HANDLE hFind = INVALID_HANDLE_VALUE; - git_win32_path wbuf; - int wbufsz; - WIN32_FIND_DATAW ffd; - bool retval = true; + git_win32_path filter_w; + bool empty = false; - if (!git_path_isdir(path)) - return false; + if (git_win32__findfirstfile_filter(filter_w, path)) { + WIN32_FIND_DATAW findData; + HANDLE hFind = FindFirstFileW(filter_w, &findData); + + /* FindFirstFile will fail if there are no children to the given + * path, which can happen if the given path is a file (and obviously + * has no children) or if the given path is an empty mount point. + * (Most directories have at least directory entries '.' and '..', + * but ridiculously another volume mounted in another drive letter's + * path space do not, and thus have nothing to enumerate.) If + * FindFirstFile fails, check if this is a directory-like thing + * (a mount point). + */ + if (hFind == INVALID_HANDLE_VALUE) + return git_path_isdir(path); - wbufsz = git_win32_path_from_c(wbuf, path); - if (!wbufsz || wbufsz + 2 > GIT_WIN_PATH_UTF16) - return false; - memcpy(&wbuf[wbufsz - 1], L"\\*", 3 * sizeof(wchar_t)); + /* If the find handle was created successfully, then it's a directory */ + empty = true; - hFind = FindFirstFileW(wbuf, &ffd); - if (INVALID_HANDLE_VALUE == hFind) - return false; + do { + /* Allow the enumeration to return . and .. and still be considered + * empty. In the special case of drive roots (i.e. C:\) where . and + * .. do not occur, we can still consider the path to be an empty + * directory if there's nothing there. */ + if (!git_path_is_dot_or_dotdotW(findData.cFileName)) { + empty = false; + break; + } + } while (FindNextFileW(hFind, &findData)); - do { - if (!git_path_is_dot_or_dotdotW(ffd.cFileName)) { - retval = false; - break; - } - } while (FindNextFileW(hFind, &ffd) != 0); + FindClose(hFind); + } - FindClose(hFind); - return retval; + return empty; } #else @@ -528,7 +569,9 @@ if (!git_path_isdir(path)) return false; - if (!(error = git_buf_sets(&dir, path))) + if ((error = git_buf_sets(&dir, path)) != 0) + giterr_clear(); + else error = git_path_direach(&dir, 0, path_found_entry, NULL); git_buf_free(&dir); @@ -619,7 +662,7 @@ /* call dirname if this is not a directory */ if (!error) /* && git_path_isdir(dir->ptr) == false) */ - error = git_path_dirname_r(dir, dir->ptr); + error = (git_path_dirname_r(dir, dir->ptr) < 0) ? -1 : 0; if (!error) error = git_path_to_dir(dir); @@ -738,6 +781,64 @@ return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0; } +int git_path_make_relative(git_buf *path, const char *parent) +{ + const char *p, *q, *p_dirsep, *q_dirsep; + size_t plen = path->size, newlen, depth = 1, i, offset; + + for (p_dirsep = p = path->ptr, q_dirsep = q = parent; *p && *q; p++, q++) { + if (*p == '/' && *q == '/') { + p_dirsep = p; + q_dirsep = q; + } + else if (*p != *q) + break; + } + + /* need at least 1 common path segment */ + if ((p_dirsep == path->ptr || q_dirsep == parent) && + (*p_dirsep != '/' || *q_dirsep != '/')) { + giterr_set(GITERR_INVALID, + "%s is not a parent of %s", parent, path->ptr); + return GIT_ENOTFOUND; + } + + if (*p == '/' && !*q) + p++; + else if (!*p && *q == '/') + q++; + else if (!*p && !*q) + return git_buf_clear(path), 0; + else { + p = p_dirsep + 1; + q = q_dirsep + 1; + } + + plen -= (p - path->ptr); + + if (!*q) + return git_buf_set(path, p, plen); + + for (; (q = strchr(q, '/')) && *(q + 1); q++) + depth++; + + newlen = (depth * 3) + plen; + + /* save the offset as we might realllocate the pointer */ + offset = p - path->ptr; + if (git_buf_try_grow(path, newlen + 1, 1, 0) < 0) + return -1; + p = path->ptr + offset; + + memmove(path->ptr + (depth * 3), p, plen + 1); + + for (i = 0; i < depth; i++) + memcpy(path->ptr + (i * 3), "../", 3); + + path->size = newlen; + return 0; +} + bool git_path_has_non_ascii(const char *path, size_t pathlen) { const uint8_t *scan = (const uint8_t *)path, *end; @@ -777,8 +878,10 @@ !git_path_has_non_ascii(*in, *inlen)) return 0; + git_buf_clear(&ic->buf); + while (1) { - if (git_buf_grow(&ic->buf, wantlen) < 0) + if (git_buf_grow(&ic->buf, wantlen + 1) < 0) return -1; nfc = ic->buf.ptr + ic->buf.size; @@ -791,8 +894,11 @@ if (rv != (size_t)-1) break; + /* if we cannot convert the data (probably because iconv thinks + * it is not valid UTF-8 source data), then use original data + */ if (errno != E2BIG) - goto fail; + return 0; /* make space for 2x the remaining data to be converted * (with per retry overhead to avoid infinite loops) @@ -815,6 +921,64 @@ return -1; } +static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX"; +static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX"; + +/* Check if the platform is decomposing unicode data for us. We will + * emulate core Git and prefer to use precomposed unicode data internally + * on these platforms, composing the decomposed unicode on the fly. + * + * This mainly happens on the Mac where HDFS stores filenames as + * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will + * return decomposed unicode from readdir() even when the actual + * filesystem is storing precomposed unicode. + */ +bool git_path_does_fs_decompose_unicode(const char *root) +{ + git_buf path = GIT_BUF_INIT; + int fd; + bool found_decomposed = false; + char tmp[6]; + + /* Create a file using a precomposed path and then try to find it + * using the decomposed name. If the lookup fails, then we will mark + * that we should precompose unicode for this repository. + */ + if (git_buf_joinpath(&path, root, nfc_file) < 0 || + (fd = p_mkstemp(path.ptr)) < 0) + goto done; + p_close(fd); + + /* record trailing digits generated by mkstemp */ + memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp)); + + /* try to look up as NFD path */ + if (git_buf_joinpath(&path, root, nfd_file) < 0) + goto done; + memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); + + found_decomposed = git_path_exists(path.ptr); + + /* remove temporary file (using original precomposed path) */ + if (git_buf_joinpath(&path, root, nfc_file) < 0) + goto done; + memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); + + (void)p_unlink(path.ptr); + +done: + git_buf_free(&path); + return found_decomposed; +} + +#else + +bool git_path_does_fs_decompose_unicode(const char *root) +{ + GIT_UNUSED(root); + return false; +} + #endif #if defined(__sun) || defined(__GNU__) @@ -835,7 +999,7 @@ path_dirent_data de_data; struct dirent *de, *de_buf = (struct dirent *)&de_data; - (void)flags; + GIT_UNUSED(flags); #ifdef GIT_USE_ICONV git_path_iconv_t ic = GIT_PATH_ICONV_INIT; @@ -848,6 +1012,9 @@ if ((dir = opendir(path->ptr)) == NULL) { giterr_set(GITERR_OS, "Failed to open directory '%s'", path->ptr); + if (errno == ENOENT) + return GIT_ENOTFOUND; + return -1; } @@ -867,7 +1034,7 @@ if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0) break; #endif - + if ((error = git_buf_put(path, de_path, de_len)) < 0) break; @@ -875,8 +1042,8 @@ git_buf_truncate(path, wd_len); /* restore path */ - if (error) { - error = GIT_EUSER; + if (error != 0) { + giterr_set_after_callback(error); break; } } @@ -903,7 +1070,7 @@ path_dirent_data de_data; struct dirent *de, *de_buf = (struct dirent *)&de_data; - (void)flags; + GIT_UNUSED(flags); #ifdef GIT_USE_ICONV git_path_iconv_t ic = GIT_PATH_ICONV_INIT; @@ -955,8 +1122,10 @@ entry_path[path_len] = '/'; memcpy(&entry_path[path_len + need_slash], de_path, de_len); - if ((error = git_vector_insert(contents, entry_path)) < 0) + if ((error = git_vector_insert(contents, entry_path)) < 0) { + git__free(entry_path); break; + } } closedir(dir); @@ -1032,26 +1201,31 @@ if ((error = git_buf_joinpath(&full, full.ptr, ps->path)) < 0 || (error = git_path_lstat(full.ptr, &ps->st)) < 0) { + if (error == GIT_ENOTFOUND) { - giterr_clear(); - error = 0; + /* file was removed between readdir and lstat */ + char *entry_path = git_vector_get(contents, i); git_vector_remove(contents, i--); - continue; + git__free(entry_path); + } else { + /* Treat the file as unreadable if we get any other error */ + memset(&ps->st, 0, sizeof(ps->st)); + ps->st.st_mode = GIT_FILEMODE_UNREADABLE; } - break; + giterr_clear(); + error = 0; + continue; } if (S_ISDIR(ps->st.st_mode)) { - if ((error = git_buf_joinpath(&full, full.ptr, ".git")) < 0) - break; - - if (p_access(full.ptr, F_OK) == 0) { - ps->st.st_mode = GIT_FILEMODE_COMMIT; - } else { - ps->path[ps->path_len++] = '/'; - ps->path[ps->path_len] = '\0'; - } + ps->path[ps->path_len++] = '/'; + ps->path[ps->path_len] = '\0'; + } + else if (!S_ISREG(ps->st.st_mode) && !S_ISLNK(ps->st.st_mode)) { + char *entry_path = git_vector_get(contents, i); + git_vector_remove(contents, i--); + git__free(entry_path); } } @@ -1062,3 +1236,266 @@ return error; } + +int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path) +{ + if (git_path_is_local_file_url(url_or_path)) + return git_path_fromurl(local_path_out, url_or_path); + else + return git_buf_sets(local_path_out, url_or_path); +} + +/* Reject paths like AUX or COM1, or those versions that end in a dot or + * colon. ("AUX." or "AUX:") + */ +GIT_INLINE(bool) verify_dospath( + const char *component, + size_t len, + const char dospath[3], + bool trailing_num) +{ + size_t last = trailing_num ? 4 : 3; + + if (len < last || git__strncasecmp(component, dospath, 3) != 0) + return true; + + if (trailing_num && (component[3] < '1' || component[3] > '9')) + return true; + + return (len > last && + component[last] != '.' && + component[last] != ':'); +} + +static int32_t next_hfs_char(const char **in, size_t *len) +{ + while (*len) { + int32_t codepoint; + int cp_len = git__utf8_iterate((const uint8_t *)(*in), (int)(*len), &codepoint); + if (cp_len < 0) + return -1; + + (*in) += cp_len; + (*len) -= cp_len; + + /* these code points are ignored completely */ + switch (codepoint) { + case 0x200c: /* ZERO WIDTH NON-JOINER */ + case 0x200d: /* ZERO WIDTH JOINER */ + case 0x200e: /* LEFT-TO-RIGHT MARK */ + case 0x200f: /* RIGHT-TO-LEFT MARK */ + case 0x202a: /* LEFT-TO-RIGHT EMBEDDING */ + case 0x202b: /* RIGHT-TO-LEFT EMBEDDING */ + case 0x202c: /* POP DIRECTIONAL FORMATTING */ + case 0x202d: /* LEFT-TO-RIGHT OVERRIDE */ + case 0x202e: /* RIGHT-TO-LEFT OVERRIDE */ + case 0x206a: /* INHIBIT SYMMETRIC SWAPPING */ + case 0x206b: /* ACTIVATE SYMMETRIC SWAPPING */ + case 0x206c: /* INHIBIT ARABIC FORM SHAPING */ + case 0x206d: /* ACTIVATE ARABIC FORM SHAPING */ + case 0x206e: /* NATIONAL DIGIT SHAPES */ + case 0x206f: /* NOMINAL DIGIT SHAPES */ + case 0xfeff: /* ZERO WIDTH NO-BREAK SPACE */ + continue; + } + + /* fold into lowercase -- this will only fold characters in + * the ASCII range, which is perfectly fine, because the + * git folder name can only be composed of ascii characters + */ + return tolower(codepoint); + } + return 0; /* NULL byte -- end of string */ +} + +static bool verify_dotgit_hfs(const char *path, size_t len) +{ + if (next_hfs_char(&path, &len) != '.' || + next_hfs_char(&path, &len) != 'g' || + next_hfs_char(&path, &len) != 'i' || + next_hfs_char(&path, &len) != 't' || + next_hfs_char(&path, &len) != 0) + return true; + + return false; +} + +GIT_INLINE(bool) verify_dotgit_ntfs(git_repository *repo, const char *path, size_t len) +{ + const char *shortname = NULL; + size_t i, start, shortname_len = 0; + + /* See if the repo has a custom shortname (not "GIT~1") */ + if (repo && + (shortname = git_repository__8dot3_name(repo)) && + shortname != git_repository__8dot3_default) + shortname_len = strlen(shortname); + + if (len >= 4 && strncasecmp(path, ".git", 4) == 0) + start = 4; + else if (len >= git_repository__8dot3_default_len && + strncasecmp(path, git_repository__8dot3_default, git_repository__8dot3_default_len) == 0) + start = git_repository__8dot3_default_len; + else if (shortname_len && len >= shortname_len && + strncasecmp(path, shortname, shortname_len) == 0) + start = shortname_len; + else + return true; + + /* Reject paths beginning with ".git\" */ + if (path[start] == '\\') + return false; + + for (i = start; i < len; i++) { + if (path[i] != ' ' && path[i] != '.') + return true; + } + + return false; +} + +GIT_INLINE(bool) verify_char(unsigned char c, unsigned int flags) +{ + if ((flags & GIT_PATH_REJECT_BACKSLASH) && c == '\\') + return false; + + if ((flags & GIT_PATH_REJECT_SLASH) && c == '/') + return false; + + if (flags & GIT_PATH_REJECT_NT_CHARS) { + if (c < 32) + return false; + + switch (c) { + case '<': + case '>': + case ':': + case '"': + case '|': + case '?': + case '*': + return false; + } + } + + return true; +} + +/* + * We fundamentally don't like some paths when dealing with user-inputted + * strings (in checkout or ref names): we don't want dot or dot-dot + * anywhere, we want to avoid writing weird paths on Windows that can't + * be handled by tools that use the non-\\?\ APIs, we don't want slashes + * or double slashes at the end of paths that can make them ambiguous. + * + * For checkout, we don't want to recurse into ".git" either. + */ +static bool verify_component( + git_repository *repo, + const char *component, + size_t len, + unsigned int flags) +{ + if (len == 0) + return false; + + if ((flags & GIT_PATH_REJECT_TRAVERSAL) && + len == 1 && component[0] == '.') + return false; + + if ((flags & GIT_PATH_REJECT_TRAVERSAL) && + len == 2 && component[0] == '.' && component[1] == '.') + return false; + + if ((flags & GIT_PATH_REJECT_TRAILING_DOT) && component[len-1] == '.') + return false; + + if ((flags & GIT_PATH_REJECT_TRAILING_SPACE) && component[len-1] == ' ') + return false; + + if ((flags & GIT_PATH_REJECT_TRAILING_COLON) && component[len-1] == ':') + return false; + + if (flags & GIT_PATH_REJECT_DOS_PATHS) { + if (!verify_dospath(component, len, "CON", false) || + !verify_dospath(component, len, "PRN", false) || + !verify_dospath(component, len, "AUX", false) || + !verify_dospath(component, len, "NUL", false) || + !verify_dospath(component, len, "COM", true) || + !verify_dospath(component, len, "LPT", true)) + return false; + } + + if (flags & GIT_PATH_REJECT_DOT_GIT_HFS && + !verify_dotgit_hfs(component, len)) + return false; + + if (flags & GIT_PATH_REJECT_DOT_GIT_NTFS && + !verify_dotgit_ntfs(repo, component, len)) + return false; + + if ((flags & GIT_PATH_REJECT_DOT_GIT_HFS) == 0 && + (flags & GIT_PATH_REJECT_DOT_GIT_NTFS) == 0 && + (flags & GIT_PATH_REJECT_DOT_GIT) && + len == 4 && + component[0] == '.' && + (component[1] == 'g' || component[1] == 'G') && + (component[2] == 'i' || component[2] == 'I') && + (component[3] == 't' || component[3] == 'T')) + return false; + + return true; +} + +GIT_INLINE(unsigned int) dotgit_flags( + git_repository *repo, + unsigned int flags) +{ + int protectHFS = 0, protectNTFS = 0; + +#ifdef __APPLE__ + protectHFS = 1; +#endif + +#ifdef GIT_WIN32 + protectNTFS = 1; +#endif + + if (repo && !protectHFS) + git_repository__cvar(&protectHFS, repo, GIT_CVAR_PROTECTHFS); + if (protectHFS) + flags |= GIT_PATH_REJECT_DOT_GIT_HFS; + + if (repo && !protectNTFS) + git_repository__cvar(&protectNTFS, repo, GIT_CVAR_PROTECTNTFS); + if (protectNTFS) + flags |= GIT_PATH_REJECT_DOT_GIT_NTFS; + + return flags; +} + +bool git_path_isvalid( + git_repository *repo, + const char *path, + unsigned int flags) +{ + const char *start, *c; + + /* Upgrade the ".git" checks based on platform */ + if ((flags & GIT_PATH_REJECT_DOT_GIT)) + flags = dotgit_flags(repo, flags); + + for (start = c = path; *c; c++) { + if (!verify_char(*c, flags)) + return false; + + if (*c == '/') { + if (!verify_component(repo, start, (c - start), flags)) + return false; + + start = c+1; + } + } + + return verify_component(repo, start, (c - start), flags); +} diff -Nru libgit2-0.20.0/src/path.h libgit2-0.22.2/src/path.h --- libgit2-0.20.0/src/path.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/path.h 2015-03-24 16:10:45.000000000 +0000 @@ -8,6 +8,7 @@ #define INCLUDE_path_h__ #include "common.h" +#include "posix.h" #include "buffer.h" #include "vector.h" @@ -119,6 +120,22 @@ # define git_path_mkposix(p) /* blank */ #endif +/** + * Check if string is a relative path (i.e. starts with "./" or "../") + */ +GIT_INLINE(int) git_path_is_relative(const char *p) +{ + return (p[0] == '.' && (p[1] == '/' || (p[1] == '.' && p[2] == '/'))); +} + +/** + * Check if string is at end of path segment (i.e. looking at '/' or '\0') + */ +GIT_INLINE(int) git_path_at_end_of_segment(const char *p) +{ + return !*p || *p == '/'; +} + extern int git__percent_decode(git_buf *decoded_out, const char *input); /** @@ -180,6 +197,17 @@ extern bool git_path_contains_dir(git_buf *parent, const char *subdir); /** + * Make the path relative to the given parent path. + * + * @param path The path to make relative + * @param parent The parent path to make path relative to + * @return 0 if path was made relative, GIT_ENOTFOUND + * if there was not common root between the paths, + * or <0. + */ +extern int git_path_make_relative(git_buf *path, const char *parent); + +/** * Check if the given path contains the given file. * * @param dir Directory path that might contain file @@ -255,9 +283,10 @@ * @param flags Combination of GIT_PATH_DIR flags. * @param callback Callback for each entry. Passed the `payload` and each * successive path inside the directory as a full path. This may - * safely append text to the pathbuf if needed. + * safely append text to the pathbuf if needed. Return non-zero to + * cancel iteration (and return value will be propagated back). * @param payload Passed to callback as first argument. - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success or error code from OS error or from callback */ extern int git_path_direach( git_buf *pathbuf, @@ -278,7 +307,7 @@ * reached (inclusive of a final call at the root_path). * * Returning anything other than 0 from the callback function - * will stop the iteration and propogate the error to the caller. + * will stop the iteration and propagate the error to the caller. * * @param pathbuf Buffer the function reads the directory from and * and updates with each successive name. @@ -288,13 +317,13 @@ * original input path. * @param callback Function to invoke on each path. Passed the `payload` * and the buffer containing the current path. The path should not - * be modified in any way. + * be modified in any way. Return non-zero to stop iteration. * @param state Passed to fn as the first ath. */ extern int git_path_walk_up( git_buf *pathbuf, const char *ceiling, - int (*callback)(void *payload, git_buf *path), + int (*callback)(void *payload, const char *path), void *payload); /** @@ -367,21 +396,35 @@ */ GIT_INLINE(int) git_path_equal_or_prefixed( const char *parent, - const char *child) + const char *child, + ssize_t *prefixlen) { const char *p = parent, *c = child; + int lastslash = 0; while (*p && *c) { + lastslash = (*p == '/'); + if (*p++ != *c++) return GIT_PATH_NOTEQUAL; } if (*p != '\0') return GIT_PATH_NOTEQUAL; - if (*c == '\0') + + if (*c == '\0') { + if (prefixlen) + *prefixlen = p - parent; + return GIT_PATH_EQUAL; - if (*c == '/') + } + + if (*c == '/' || lastslash) { + if (prefixlen) + *prefixlen = (p - parent) - lastslash; + return GIT_PATH_PREFIX; + } return GIT_PATH_NOTEQUAL; } @@ -427,4 +470,53 @@ #endif /* GIT_USE_ICONV */ +extern bool git_path_does_fs_decompose_unicode(const char *root); + +/* Used for paths to repositories on the filesystem */ +extern bool git_path_is_local_file_url(const char *file_url); +extern int git_path_from_url_or_path(git_buf *local_path_out, const char *url_or_path); + +/* Flags to determine path validity in `git_path_isvalid` */ +#define GIT_PATH_REJECT_TRAVERSAL (1 << 0) +#define GIT_PATH_REJECT_DOT_GIT (1 << 1) +#define GIT_PATH_REJECT_SLASH (1 << 2) +#define GIT_PATH_REJECT_BACKSLASH (1 << 3) +#define GIT_PATH_REJECT_TRAILING_DOT (1 << 4) +#define GIT_PATH_REJECT_TRAILING_SPACE (1 << 5) +#define GIT_PATH_REJECT_TRAILING_COLON (1 << 6) +#define GIT_PATH_REJECT_DOS_PATHS (1 << 7) +#define GIT_PATH_REJECT_NT_CHARS (1 << 8) +#define GIT_PATH_REJECT_DOT_GIT_HFS (1 << 9) +#define GIT_PATH_REJECT_DOT_GIT_NTFS (1 << 10) + +/* Default path safety for writing files to disk: since we use the + * Win32 "File Namespace" APIs ("\\?\") we need to protect from + * paths that the normal Win32 APIs would not write. + */ +#ifdef GIT_WIN32 +# define GIT_PATH_REJECT_DEFAULTS \ + GIT_PATH_REJECT_TRAVERSAL | \ + GIT_PATH_REJECT_BACKSLASH | \ + GIT_PATH_REJECT_TRAILING_DOT | \ + GIT_PATH_REJECT_TRAILING_SPACE | \ + GIT_PATH_REJECT_TRAILING_COLON | \ + GIT_PATH_REJECT_DOS_PATHS | \ + GIT_PATH_REJECT_NT_CHARS +#else +# define GIT_PATH_REJECT_DEFAULTS GIT_PATH_REJECT_TRAVERSAL +#endif + +/* + * Determine whether a path is a valid git path or not - this must not contain + * a '.' or '..' component, or a component that is ".git" (in any case). + * + * `repo` is optional. If specified, it will be used to determine the short + * path name to reject (if `GIT_PATH_REJECT_DOS_SHORTNAME` is specified), + * in addition to the default of "git~1". + */ +extern bool git_path_isvalid( + git_repository *repo, + const char *path, + unsigned int flags); + #endif diff -Nru libgit2-0.20.0/src/pathspec.c libgit2-0.22.2/src/pathspec.c --- libgit2-0.20.0/src/pathspec.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/pathspec.c 2015-03-24 16:10:45.000000000 +0000 @@ -83,14 +83,17 @@ if (!match) return -1; - match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG; + match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | + GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_NOLEADINGDIR; ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern); if (ret == GIT_ENOTFOUND) { git__free(match); continue; - } else if (ret < 0) + } else if (ret < 0) { + git__free(match); return ret; + } if (git_vector_insert(vspec, match) < 0) return -1; @@ -102,15 +105,7 @@ /* free data from the pathspec vector */ void git_pathspec__vfree(git_vector *vspec) { - git_attr_fnmatch *match; - unsigned int i; - - git_vector_foreach(vspec, i, match) { - git__free(match); - vspec->contents[i] = NULL; - } - - git_vector_free(vspec); + git_vector_free_deep(vspec); } struct pathspec_match_context { @@ -301,6 +296,9 @@ static void pathspec_match_free(git_pathspec_match_list *m) { + if (!m) + return; + git_pathspec_free(m->pathspec); m->pathspec = NULL; @@ -451,7 +449,7 @@ /* check if path is ignored and untracked */ if (index != NULL && git_iterator_current_is_ignored(iter) && - git_index__find(NULL, index, entry->path, GIT_INDEX_STAGE_ANY) < 0) + git_index__find_pos(NULL, index, entry->path, 0, GIT_INDEX_STAGE_ANY) < 0) continue; /* mark the matched pattern as used */ @@ -529,7 +527,7 @@ assert(repo); if (!(error = git_iterator_for_workdir( - &iter, repo, pathspec_match_iter_flags(flags), NULL, NULL))) { + &iter, repo, NULL, NULL, pathspec_match_iter_flags(flags), NULL, NULL))) { error = pathspec_match_from_iterator(out, iter, flags, ps); diff -Nru libgit2-0.20.0/src/pool.c libgit2-0.22.2/src/pool.c --- libgit2-0.20.0/src/pool.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/pool.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,4 +1,5 @@ #include "pool.h" +#include "posix.h" #ifndef GIT_WIN32 #include #endif @@ -7,7 +8,7 @@ git_pool_page *next; uint32_t size; uint32_t avail; - char data[GIT_FLEX_ARRAY]; + GIT_ALIGN(char data[GIT_FLEX_ARRAY], 8); }; struct pool_freelist { @@ -146,7 +147,7 @@ void *git_pool_malloc(git_pool *pool, uint32_t items) { git_pool_page *scan = pool->open, *prev; - uint32_t size = items * pool->item_size; + uint32_t size = ((items * pool->item_size) + 7) & ~7; void *ptr = NULL; pool->has_string_alloc = 0; @@ -158,7 +159,7 @@ return ptr; } - /* just add a block if there is no open one to accomodate this */ + /* just add a block if there is no open one to accommodate this */ if (size >= pool->page_size || !scan || scan->avail < size) return pool_alloc_page(pool, size); @@ -190,19 +191,18 @@ char *git_pool_strndup(git_pool *pool, const char *str, size_t n) { - void *ptr = NULL; + char *ptr = NULL; assert(pool && str && pool->item_size == sizeof(char)); - if (n + 1 == 0) { - giterr_set_oom(); + if ((uint32_t)(n + 1) < n) return NULL; - } if ((ptr = git_pool_malloc(pool, (uint32_t)(n + 1))) != NULL) { memcpy(ptr, str, n); - *(((char *)ptr) + n) = '\0'; + ptr[n] = '\0'; } + pool->has_string_alloc = 1; return ptr; @@ -306,17 +306,10 @@ static uint32_t size = 0; if (!size) { -#ifdef GIT_WIN32 - SYSTEM_INFO info; - GetSystemInfo(&info); - size = (uint32_t)info.dwPageSize; -#elif defined(__amigaos4__) - size = (uint32_t)4096; /* 4K as there is no global value we can query */ -#else - size = (uint32_t)sysconf(_SC_PAGE_SIZE); -#endif - - size -= 2 * sizeof(void *); /* allow space for malloc overhead */ + size_t page_size; + if (git__page_size(&page_size) < 0) + page_size = 4096; + size = page_size - 2 * sizeof(void *); /* allow space for malloc overhead */ } return size; diff -Nru libgit2-0.20.0/src/pool.h libgit2-0.22.2/src/pool.h --- libgit2-0.20.0/src/pool.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/pool.h 2015-03-24 16:10:45.000000000 +0000 @@ -143,8 +143,6 @@ extern bool git_pool__ptr_in_pool(git_pool *pool, void *ptr); -extern uint32_t git_pool__system_page_size(void); - extern uint32_t git_pool__suggest_items_per_page(uint32_t item_size); #endif diff -Nru libgit2-0.20.0/src/posix.c libgit2-0.22.2/src/posix.c --- libgit2-0.20.0/src/posix.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/posix.c 2015-03-24 16:10:45.000000000 +0000 @@ -99,7 +99,7 @@ #endif /* NO_ADDRINFO */ -int p_open(const char *path, int flags, ...) +int p_open(const char *path, volatile int flags, ...) { mode_t mode = 0; @@ -151,15 +151,14 @@ #endif /* GIT_WIN32 */ -int p_read(git_file fd, void *buf, size_t cnt) +ssize_t p_read(git_file fd, void *buf, size_t cnt) { char *b = buf; while (cnt) { ssize_t r; #ifdef GIT_WIN32 - assert((size_t)((unsigned int)cnt) == cnt); - r = read(fd, b, (unsigned int)cnt); + r = read(fd, b, cnt > INT_MAX ? INT_MAX : (unsigned int)cnt); #else r = read(fd, b, cnt); #endif @@ -173,7 +172,7 @@ cnt -= r; b += r; } - return (int)(b - (char *)buf); + return (b - (char *)buf); } int p_write(git_file fd, const void *buf, size_t cnt) @@ -189,7 +188,7 @@ r = write(fd, b, cnt); #endif if (r < 0) { - if (errno == EINTR || errno == EAGAIN) + if (errno == EINTR || GIT_ISBLOCKED(errno)) continue; return -1; } @@ -203,4 +202,48 @@ return 0; } +#ifdef NO_MMAP + +#include "map.h" + +int git__page_size(size_t *page_size) +{ + /* dummy; here we don't need any alignment anyway */ + *page_size = 4096; + return 0; +} + + +int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset) +{ + GIT_MMAP_VALIDATE(out, len, prot, flags); + + out->data = NULL; + out->len = 0; + + if ((prot & GIT_PROT_WRITE) && ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)) { + giterr_set(GITERR_OS, "Trying to map shared-writeable"); + return -1; + } + out->data = malloc(len); + GITERR_CHECK_ALLOC(out->data); + + if ((p_lseek(fd, offset, SEEK_SET) < 0) || ((size_t)p_read(fd, out->data, len) != len)) { + giterr_set(GITERR_OS, "mmap emulation failed"); + return -1; + } + + out->len = len; + return 0; +} + +int p_munmap(git_map *map) +{ + assert(map != NULL); + free(map->data); + + return 0; +} + +#endif diff -Nru libgit2-0.20.0/src/posix.h libgit2-0.22.2/src/posix.h --- libgit2-0.20.0/src/posix.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/posix.h 2015-03-24 16:10:45.000000000 +0000 @@ -12,23 +12,76 @@ #include #include "fnmatch.h" +/* stat: file mode type testing macros */ #ifndef S_IFGITLINK #define S_IFGITLINK 0160000 #define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK) #endif +#ifndef S_IFLNK +#define S_IFLNK 0120000 +#undef _S_IFLNK +#define _S_IFLNK S_IFLNK +#endif + +#ifndef S_IXUSR +#define S_IXUSR 00100 +#endif + +#ifndef S_ISLNK +#define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK) +#endif + +#ifndef S_ISDIR +#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) +#endif + +#ifndef S_ISREG +#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG) +#endif + +#ifndef S_ISFIFO +#define S_ISFIFO(m) (((m) & _S_IFMT) == _S_IFIFO) +#endif + /* if S_ISGID is not defined, then don't try to set it */ #ifndef S_ISGID #define S_ISGID 0 #endif -#if !defined(O_BINARY) +#ifndef O_BINARY #define O_BINARY 0 #endif -#if !defined(O_CLOEXEC) +#ifndef O_CLOEXEC #define O_CLOEXEC 0 #endif +/* access() mode parameter #defines */ +#ifndef F_OK +#define F_OK 0 /* existence check */ +#endif +#ifndef W_OK +#define W_OK 2 /* write mode check */ +#endif +#ifndef R_OK +#define R_OK 4 /* read mode check */ +#endif + +/* Determine whether an errno value indicates that a read or write failed + * because the descriptor is blocked. + */ +#if defined(EWOULDBLOCK) +#define GIT_ISBLOCKED(e) ((e) == EAGAIN || (e) == EWOULDBLOCK) +#else +#define GIT_ISBLOCKED(e) ((e) == EAGAIN) +#endif + +/* define some standard errnos that the runtime may be missing. for example, + * mingw lacks EAFNOSUPPORT. */ +#ifndef EAFNOSUPPORT +#define EAFNOSUPPORT (INT_MAX-1) +#endif + typedef int git_file; /** @@ -44,11 +97,9 @@ * Use your manpages to check the docs on these. */ -extern int p_read(git_file fd, void *buf, size_t cnt); +extern ssize_t p_read(git_file fd, void *buf, size_t cnt); extern int p_write(git_file fd, const void *buf, size_t cnt); -#define p_fstat(f,b) fstat(f, b) -#define p_lseek(f,n,w) lseek(f, n, w) #define p_close(fd) close(fd) #define p_umask(m) umask(m) @@ -57,28 +108,7 @@ extern int p_getcwd(char *buffer_out, size_t size); extern int p_rename(const char *from, const char *to); -#ifndef GIT_WIN32 - -#define p_stat(p,b) stat(p, b) -#define p_chdir(p) chdir(p) -#define p_rmdir(p) rmdir(p) -#define p_chmod(p,m) chmod(p, m) -#define p_access(p,m) access(p,m) -#define p_recv(s,b,l,f) recv(s,b,l,f) -#define p_send(s,b,l,f) send(s,b,l,f) -typedef int GIT_SOCKET; -#define INVALID_SOCKET -1 - -#define p_localtime_r localtime_r -#define p_gmtime_r gmtime_r - -#else - -typedef SOCKET GIT_SOCKET; -extern struct tm * p_localtime_r (const time_t *timer, struct tm *result); -extern struct tm * p_gmtime_r (const time_t *timer, struct tm *result); - -#endif +extern int git__page_size(size_t *page_size); /** * Platform-dependent methods @@ -89,9 +119,7 @@ # include "unix/posix.h" #endif -#ifndef __MINGW32__ -# define p_strnlen strnlen -#endif +#include "strnlen.h" #ifdef NO_READDIR_R # include diff -Nru libgit2-0.20.0/src/pqueue.c libgit2-0.22.2/src/pqueue.c --- libgit2-0.20.0/src/pqueue.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/pqueue.c 2015-03-24 16:10:45.000000000 +0000 @@ -3,161 +3,115 @@ * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. - * - * This file is based on a modified version of the priority queue found - * in the Apache project and libpqueue library. - * - * https://github.com/vy/libpqueue - * - * Original file notice: - * - * Copyright 2010 Volkan Yazici - * Copyright 2006-2010 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. */ -#include "common.h" #include "pqueue.h" +#include "util.h" -#define left(i) ((i) << 1) -#define right(i) (((i) << 1) + 1) -#define parent(i) ((i) >> 1) - -int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri) -{ - assert(q); - - /* Need to allocate n+1 elements since element 0 isn't used. */ - q->d = git__malloc((n + 1) * sizeof(void *)); - GITERR_CHECK_ALLOC(q->d); - - q->size = 1; - q->avail = q->step = (n + 1); /* see comment above about n+1 */ - q->cmppri = cmppri; +#define PQUEUE_LCHILD_OF(I) (((I)<<1)+1) +#define PQUEUE_RCHILD_OF(I) (((I)<<1)+2) +#define PQUEUE_PARENT_OF(I) (((I)-1)>>1) + +int git_pqueue_init( + git_pqueue *pq, + uint32_t flags, + size_t init_size, + git_vector_cmp cmp) +{ + int error = git_vector_init(pq, init_size, cmp); + + if (!error) { + /* mix in our flags */ + pq->flags |= flags; + + /* if fixed size heap, pretend vector is exactly init_size elements */ + if ((flags & GIT_PQUEUE_FIXED_SIZE) && init_size > 0) + pq->_alloc_size = init_size; + } - return 0; + return error; } - -void git_pqueue_free(git_pqueue *q) +static void pqueue_up(git_pqueue *pq, size_t el) { - git__free(q->d); - q->d = NULL; -} + size_t parent_el = PQUEUE_PARENT_OF(el); + void *kid = git_vector_get(pq, el); -void git_pqueue_clear(git_pqueue *q) -{ - q->size = 1; -} + while (el > 0) { + void *parent = pq->contents[parent_el]; -size_t git_pqueue_size(git_pqueue *q) -{ - /* queue element 0 exists but doesn't count since it isn't used. */ - return (q->size - 1); -} + if (pq->_cmp(parent, kid) <= 0) + break; + pq->contents[el] = parent; -static void bubble_up(git_pqueue *q, size_t i) -{ - size_t parent_node; - void *moving_node = q->d[i]; - - for (parent_node = parent(i); - ((i > 1) && q->cmppri(q->d[parent_node], moving_node)); - i = parent_node, parent_node = parent(i)) { - q->d[i] = q->d[parent_node]; + el = parent_el; + parent_el = PQUEUE_PARENT_OF(el); } - q->d[i] = moving_node; + pq->contents[el] = kid; } - -static size_t maxchild(git_pqueue *q, size_t i) +static void pqueue_down(git_pqueue *pq, size_t el) { - size_t child_node = left(i); + void *parent = git_vector_get(pq, el), *kid, *rkid; - if (child_node >= q->size) - return 0; + while (1) { + size_t kid_el = PQUEUE_LCHILD_OF(el); - if ((child_node + 1) < q->size && - q->cmppri(q->d[child_node], q->d[child_node + 1])) - child_node++; /* use right child instead of left */ + if ((kid = git_vector_get(pq, kid_el)) == NULL) + break; - return child_node; -} + if ((rkid = git_vector_get(pq, kid_el + 1)) != NULL && + pq->_cmp(kid, rkid) > 0) { + kid = rkid; + kid_el += 1; + } + if (pq->_cmp(parent, kid) <= 0) + break; -static void percolate_down(git_pqueue *q, size_t i) -{ - size_t child_node; - void *moving_node = q->d[i]; - - while ((child_node = maxchild(q, i)) != 0 && - q->cmppri(moving_node, q->d[child_node])) { - q->d[i] = q->d[child_node]; - i = child_node; + pq->contents[el] = kid; + el = kid_el; } - q->d[i] = moving_node; + pq->contents[el] = parent; } - -int git_pqueue_insert(git_pqueue *q, void *d) +int git_pqueue_insert(git_pqueue *pq, void *item) { - void *tmp; - size_t i; - size_t newsize; - - if (!q) return 1; - - /* allocate more memory if necessary */ - if (q->size >= q->avail) { - newsize = q->size + q->step; - tmp = git__realloc(q->d, sizeof(void *) * newsize); - GITERR_CHECK_ALLOC(tmp); + int error = 0; - q->d = tmp; - q->avail = newsize; + /* if heap is full, pop the top element if new one should replace it */ + if ((pq->flags & GIT_PQUEUE_FIXED_SIZE) != 0 && + pq->length >= pq->_alloc_size) + { + /* skip this item if below min item in heap */ + if (pq->_cmp(item, git_vector_get(pq, 0)) <= 0) + return 0; + /* otherwise remove the min item before inserting new */ + (void)git_pqueue_pop(pq); } - /* insert item */ - i = q->size++; - q->d[i] = d; - bubble_up(q, i); + if (!(error = git_vector_insert(pq, item))) + pqueue_up(pq, pq->length - 1); - return 0; + return error; } - -void *git_pqueue_pop(git_pqueue *q) +void *git_pqueue_pop(git_pqueue *pq) { - void *head; - - if (!q || q->size == 1) - return NULL; - - head = q->d[1]; - q->d[1] = q->d[--q->size]; - percolate_down(q, 1); - - return head; -} + void *rval = git_pqueue_get(pq, 0); + if (git_pqueue_size(pq) > 1) { + /* move last item to top of heap, shrink, and push item down */ + pq->contents[0] = git_vector_last(pq); + git_vector_pop(pq); + pqueue_down(pq, 0); + } else { + /* all we need to do is shrink the heap in this case */ + git_vector_pop(pq); + } -void *git_pqueue_peek(git_pqueue *q) -{ - if (!q || q->size == 1) - return NULL; - return q->d[1]; + return rval; } diff -Nru libgit2-0.20.0/src/pqueue.h libgit2-0.22.2/src/pqueue.h --- libgit2-0.20.0/src/pqueue.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/pqueue.h 2015-03-24 16:10:45.000000000 +0000 @@ -3,99 +3,54 @@ * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. - * - * This file is based on a modified version of the priority queue found - * in the Apache project and libpqueue library. - * - * https://github.com/vy/libpqueue - * - * Original file notice: - * - * Copyright 2010 Volkan Yazici - * Copyright 2006-2010 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. */ - #ifndef INCLUDE_pqueue_h__ #define INCLUDE_pqueue_h__ -/** callback functions to get/set/compare the priority of an element */ -typedef int (*git_pqueue_cmp)(void *a, void *b); +#include "vector.h" -/** the priority queue handle */ -typedef struct { - size_t size, avail, step; - git_pqueue_cmp cmppri; - void **d; -} git_pqueue; +typedef git_vector git_pqueue; +enum { + /* flag meaning: don't grow heap, keep highest values only */ + GIT_PQUEUE_FIXED_SIZE = (GIT_VECTOR_FLAG_MAX << 1), +}; /** - * initialize the queue - * - * @param n the initial estimate of the number of queue items for which memory - * should be preallocated - * @param cmppri the callback function to compare two nodes of the queue + * Initialize priority queue * - * @return the handle or NULL for insufficent memory - */ -int git_pqueue_init(git_pqueue *q, size_t n, git_pqueue_cmp cmppri); - - -/** - * free all memory used by the queue - * @param q the queue + * @param pq The priority queue struct to initialize + * @param flags Flags (see above) to control queue behavior + * @param init_size The initial queue size + * @param cmp The entry priority comparison function + * @return 0 on success, <0 on error */ -void git_pqueue_free(git_pqueue *q); +extern int git_pqueue_init( + git_pqueue *pq, + uint32_t flags, + size_t init_size, + git_vector_cmp cmp); -/** - * clear all the elements in the queue - * @param q the queue - */ -void git_pqueue_clear(git_pqueue *q); +#define git_pqueue_free git_vector_free +#define git_pqueue_clear git_vector_clear +#define git_pqueue_size git_vector_length +#define git_pqueue_get git_vector_get /** - * return the size of the queue. - * @param q the queue - */ -size_t git_pqueue_size(git_pqueue *q); - - -/** - * insert an item into the queue. - * @param q the queue - * @param d the item - * @return 0 on success - */ -int git_pqueue_insert(git_pqueue *q, void *d); - - -/** - * pop the highest-ranking item from the queue. - * @param q the queue - * @return NULL on error, otherwise the entry + * Insert a new item into the queue + * + * @param pq The priority queue + * @param item Pointer to the item data + * @return 0 on success, <0 on failure */ -void *git_pqueue_pop(git_pqueue *q); - +extern int git_pqueue_insert(git_pqueue *pq, void *item); /** - * access highest-ranking item without removing it. - * @param q the queue - * @return NULL on error, otherwise the entry + * Remove the top item in the priority queue + * + * @param pq The priority queue + * @return item from heap on success, NULL if queue is empty */ -void *git_pqueue_peek(git_pqueue *q); - -#endif /* PQUEUE_H */ -/** @} */ +extern void *git_pqueue_pop(git_pqueue *pq); +#endif diff -Nru libgit2-0.20.0/src/push.c libgit2-0.22.2/src/push.c --- libgit2-0.20.0/src/push.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/push.c 2015-03-24 16:10:45.000000000 +0000 @@ -19,7 +19,7 @@ { const push_spec *push_spec_a = a, *push_spec_b = b; - return strcmp(push_spec_a->rref, push_spec_b->rref); + return strcmp(push_spec_a->refspec.dst, push_spec_b->refspec.dst); } static int push_status_ref_cmp(const void *a, const void *b) @@ -94,12 +94,7 @@ if (spec == NULL) return; - if (spec->lref) - git__free(spec->lref); - - if (spec->rref) - git__free(spec->rref); - + git_refspec__free(&spec->refspec); git__free(spec); } @@ -134,47 +129,25 @@ static int parse_refspec(git_push *push, push_spec **spec, const char *str) { push_spec *s; - char *delim; *spec = NULL; s = git__calloc(1, sizeof(*s)); GITERR_CHECK_ALLOC(s); - if (str[0] == '+') { - s->force = true; - str++; + if (git_refspec__parse(&s->refspec, str, false) < 0) { + giterr_set(GITERR_INVALID, "invalid refspec %s", str); + goto on_error; } - delim = strchr(str, ':'); - if (delim == NULL) { - s->lref = git__strdup(str); - if (!s->lref || check_lref(push, s->lref) < 0) - goto on_error; - } else { - if (delim - str) { - s->lref = git__strndup(str, delim - str); - if (!s->lref || check_lref(push, s->lref) < 0) - goto on_error; - } - - if (strlen(delim + 1)) { - s->rref = git__strdup(delim + 1); - if (!s->rref || check_rref(s->rref) < 0) - goto on_error; - } + if (s->refspec.src && s->refspec.src[0] != '\0' && + check_lref(push, s->refspec.src) < 0) { + goto on_error; } - if (!s->lref && !s->rref) + if (check_rref(s->refspec.dst) < 0) goto on_error; - /* If rref is ommitted, use the same ref name as lref */ - if (!s->rref) { - s->rref = git__strdup(s->lref); - if (!s->rref || check_rref(s->rref) < 0) - goto on_error; - } - *spec = s; return 0; @@ -194,7 +167,10 @@ return 0; } -int git_push_update_tips(git_push *push) +int git_push_update_tips( + git_push *push, + const git_signature *signature, + const char *reflog_message) { git_buf remote_ref_name = GIT_BUF_INIT; size_t i, j; @@ -205,21 +181,19 @@ int error = 0; git_vector_foreach(&push->status, i, status) { - /* If this ref update was successful (ok, not ng), it will have an empty message */ - if (status->msg) - continue; + int fire_callback = 1; /* Find the corresponding remote ref */ fetch_spec = git_remote__matching_refspec(push->remote, status->ref); if (!fetch_spec) continue; - if ((error = git_refspec_transform_r(&remote_ref_name, fetch_spec, status->ref)) < 0) + if ((error = git_refspec_transform(&remote_ref_name, fetch_spec, status->ref)) < 0) goto on_error; /* Find matching push ref spec */ git_vector_foreach(&push->specs, j, push_spec) { - if (!strcmp(push_spec->rref, status->ref)) + if (!strcmp(push_spec->refspec.dst, status->ref)) break; } @@ -227,22 +201,38 @@ if (j == push->specs.length) continue; - /* Update the remote ref */ - if (git_oid_iszero(&push_spec->loid)) { - error = git_reference_lookup(&remote_ref, push->remote->repo, git_buf_cstr(&remote_ref_name)); + /* If this ref update was successful (ok, not ng), it will have an empty message */ + if (status->msg == NULL) { + /* Update the remote ref */ + if (git_oid_iszero(&push_spec->loid)) { + error = git_reference_lookup(&remote_ref, push->remote->repo, git_buf_cstr(&remote_ref_name)); - if (!error) { - if ((error = git_reference_delete(remote_ref)) < 0) { + if (error >= 0) { + error = git_reference_delete(remote_ref); git_reference_free(remote_ref); - goto on_error; } - git_reference_free(remote_ref); - } else if (error == GIT_ENOTFOUND) - giterr_clear(); - else + } else { + error = git_reference_create(NULL, push->remote->repo, + git_buf_cstr(&remote_ref_name), &push_spec->loid, 1, signature, + reflog_message ? reflog_message : "update by push"); + } + } + + if (error < 0) { + if (error != GIT_ENOTFOUND) goto on_error; - } else if ((error = git_reference_create(NULL, push->remote->repo, git_buf_cstr(&remote_ref_name), &push_spec->loid, 1)) < 0) - goto on_error; + + giterr_clear(); + fire_callback = 0; + } + + if (fire_callback && push->remote->callbacks.update_tips) { + error = push->remote->callbacks.update_tips(git_buf_cstr(&remote_ref_name), + &push_spec->roid, &push_spec->loid, push->remote->callbacks.payload); + + if (error < 0) + goto on_error; + } } error = 0; @@ -336,14 +326,15 @@ } else if (git_revwalk_push(rw, &spec->loid) < 0) goto on_error; - if (!spec->force) { + if (!spec->refspec.force) { git_oid base; if (git_oid_iszero(&spec->roid)) continue; if (!git_odb_exists(push->repo->_odb, &spec->roid)) { - giterr_set(GITERR_REFERENCE, "Cannot push missing reference"); + giterr_set(GITERR_REFERENCE, + "Cannot push because a reference that you are trying to update on the remote contains commits that are not present locally."); error = GIT_ENONFASTFORWARD; goto on_error; } @@ -541,10 +532,7 @@ error = 0; on_error: - git_vector_foreach(&commits, i, oid) - git__free(oid); - - git_vector_free(&commits); + git_vector_free_deep(&commits); return error; } @@ -557,22 +545,20 @@ /* Update local and remote oids*/ git_vector_foreach(&push->specs, i, spec) { - if (spec->lref) { + if (spec->refspec.src && spec->refspec.src[0]!= '\0') { /* This is a create or update. Local ref must exist. */ if (git_reference_name_to_id( - &spec->loid, push->repo, spec->lref) < 0) { - giterr_set(GITERR_REFERENCE, "No such reference '%s'", spec->lref); + &spec->loid, push->repo, spec->refspec.src) < 0) { + giterr_set(GITERR_REFERENCE, "No such reference '%s'", spec->refspec.src); return -1; } } - if (spec->rref) { - /* Remote ref may or may not (e.g. during create) already exist. */ - git_vector_foreach(&push->remote->refs, j, head) { - if (!strcmp(spec->rref, head->name)) { - git_oid_cpy(&spec->roid, &head->oid); - break; - } + /* Remote ref may or may not (e.g. during create) already exist. */ + git_vector_foreach(&push->remote->refs, j, head) { + if (!strcmp(spec->refspec.dst, head->name)) { + git_oid_cpy(&spec->roid, &head->oid); + break; } } } @@ -646,12 +632,12 @@ (error = do_push(push)) < 0) return error; - return 0; -} + if (!push->unpack_ok) { + error = -1; + giterr_set(GITERR_NET, "unpacking the sent packfile failed on the remote"); + } -int git_push_unpack_ok(git_push *push) -{ - return push->unpack_ok; + return error; } int git_push_status_foreach(git_push *push, @@ -662,8 +648,9 @@ unsigned int i; git_vector_foreach(&push->status, i, status) { - if (cb(status->ref, status->msg, data) < 0) - return GIT_EUSER; + int error = cb(status->ref, status->msg, data); + if (error) + return giterr_set_after_callback(error); } return 0; @@ -674,9 +661,7 @@ if (status == NULL) return; - if (status->msg) - git__free(status->msg); - + git__free(status->msg); git__free(status->ref); git__free(status); } @@ -702,3 +687,10 @@ git__free(push); } + +int git_push_init_options(git_push_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_push_options, GIT_PUSH_OPTIONS_INIT); + return 0; +} diff -Nru libgit2-0.20.0/src/push.h libgit2-0.22.2/src/push.h --- libgit2-0.20.0/src/push.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/push.h 2015-03-24 16:10:45.000000000 +0000 @@ -8,15 +8,13 @@ #define INCLUDE_push_h__ #include "git2.h" +#include "refspec.h" typedef struct push_spec { - char *lref; - char *rref; + struct git_refspec refspec; git_oid loid; git_oid roid; - - bool force; } push_spec; typedef struct push_status { @@ -53,4 +51,114 @@ */ void git_push_status_free(push_status *status); +/** + * Create a new push object + * + * @param out New push object + * @param remote Remote instance + * + * @return 0 or an error code + */ +int git_push_new(git_push **out, git_remote *remote); + +/** + * Set options on a push object + * + * @param push The push object + * @param opts The options to set on the push object + * + * @return 0 or an error code + */ +int git_push_set_options( + git_push *push, + const git_push_options *opts); + +/** + * Set the callbacks for a push + * + * @param push The push object + * @param pack_progress_cb Function to call with progress information during + * pack building. Be aware that this is called inline with pack building + * operations, so performance may be affected. + * @param pack_progress_cb_payload Payload for the pack progress callback. + * @param transfer_progress_cb Function to call with progress information during + * the upload portion of a push. Be aware that this is called inline with + * pack building operations, so performance may be affected. + * @param transfer_progress_cb_payload Payload for the network progress callback. + * @return 0 or an error code + */ +int git_push_set_callbacks( + git_push *push, + git_packbuilder_progress pack_progress_cb, + void *pack_progress_cb_payload, + git_push_transfer_progress transfer_progress_cb, + void *transfer_progress_cb_payload); + +/** + * Add a refspec to be pushed + * + * @param push The push object + * @param refspec Refspec string + * + * @return 0 or an error code + */ +int git_push_add_refspec(git_push *push, const char *refspec); + +/** + * Update remote tips after a push + * + * @param push The push object + * @param signature The identity to use when updating reflogs + * @param reflog_message The message to insert into the reflogs. If NULL, the + * default is "update by push". + * + * @return 0 or an error code + */ +int git_push_update_tips( + git_push *push, + const git_signature *signature, + const char *reflog_message); + +/** + * Perform the push + * + * This function will return an error in case of a protocol error or + * the server being unable to unpack the data we sent. + * + * The return value does not reflect whether the server accepted or + * refused any reference updates. Use `git_push_status_foreach()` in + * order to find out which updates were accepted or rejected. + * + * @param push The push object + * + * @return 0 or an error code + */ +int git_push_finish(git_push *push); + +/** + * Invoke callback `cb' on each status entry + * + * For each of the updated references, we receive a status report in the + * form of `ok refs/heads/master` or `ng refs/heads/master `. + * `msg != NULL` means the reference has not been updated for the given + * reason. + * + * Return a non-zero value from the callback to stop the loop. + * + * @param push The push object + * @param cb The callback to call on each object + * + * @return 0 on success, non-zero callback return value, or error code + */ +int git_push_status_foreach(git_push *push, + int (*cb)(const char *ref, const char *msg, void *data), + void *data); + +/** + * Free the given push object + * + * @param push The push object + */ +void git_push_free(git_push *push); + #endif diff -Nru libgit2-0.20.0/src/rebase.c libgit2-0.22.2/src/rebase.c --- libgit2-0.20.0/src/rebase.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/rebase.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,1129 @@ +/* + * 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 "buffer.h" +#include "repository.h" +#include "posix.h" +#include "filebuf.h" +#include "merge.h" +#include "array.h" +#include "config.h" +#include "annotated_commit.h" + +#include +#include +#include +#include +#include +#include +#include + +#define REBASE_APPLY_DIR "rebase-apply" +#define REBASE_MERGE_DIR "rebase-merge" + +#define HEAD_NAME_FILE "head-name" +#define ORIG_HEAD_FILE "orig-head" +#define HEAD_FILE "head" +#define ONTO_FILE "onto" +#define ONTO_NAME_FILE "onto_name" +#define QUIET_FILE "quiet" + +#define MSGNUM_FILE "msgnum" +#define END_FILE "end" +#define CMT_FILE_FMT "cmt.%" PRIuZ +#define CURRENT_FILE "current" +#define REWRITTEN_FILE "rewritten" + +#define ORIG_DETACHED_HEAD "detached HEAD" + +#define NOTES_DEFAULT_REF NULL + +#define REBASE_DIR_MODE 0777 +#define REBASE_FILE_MODE 0666 + +typedef enum { + GIT_REBASE_TYPE_NONE = 0, + GIT_REBASE_TYPE_APPLY = 1, + GIT_REBASE_TYPE_MERGE = 2, + GIT_REBASE_TYPE_INTERACTIVE = 3, +} git_rebase_type_t; + +struct git_rebase { + git_repository *repo; + + git_rebase_type_t type; + char *state_path; + + int head_detached : 1, + quiet : 1, + started : 1; + + char *orig_head_name; + git_oid orig_head_id; + + git_oid onto_id; + char *onto_name; + + git_array_t(git_rebase_operation) operations; + size_t current; +}; + +#define GIT_REBASE_STATE_INIT {0} + +static int rebase_state_type( + git_rebase_type_t *type_out, + char **path_out, + git_repository *repo) +{ + git_buf path = GIT_BUF_INIT; + git_rebase_type_t type = GIT_REBASE_TYPE_NONE; + + if (git_buf_joinpath(&path, repo->path_repository, REBASE_APPLY_DIR) < 0) + return -1; + + if (git_path_isdir(git_buf_cstr(&path))) { + type = GIT_REBASE_TYPE_APPLY; + goto done; + } + + git_buf_clear(&path); + if (git_buf_joinpath(&path, repo->path_repository, REBASE_MERGE_DIR) < 0) + return -1; + + if (git_path_isdir(git_buf_cstr(&path))) { + type = GIT_REBASE_TYPE_MERGE; + goto done; + } + +done: + *type_out = type; + + if (type != GIT_REBASE_TYPE_NONE && path_out) + *path_out = git_buf_detach(&path); + + git_buf_free(&path); + + return 0; +} + +GIT_INLINE(int) rebase_readfile( + git_buf *out, + git_buf *state_path, + const char *filename) +{ + size_t state_path_len = state_path->size; + int error; + + git_buf_clear(out); + + if ((error = git_buf_joinpath(state_path, state_path->ptr, filename)) < 0 || + (error = git_futils_readbuffer(out, state_path->ptr)) < 0) + goto done; + + git_buf_rtrim(out); + +done: + git_buf_truncate(state_path, state_path_len); + return error; +} + +GIT_INLINE(int) rebase_readint( + size_t *out, git_buf *asc_out, git_buf *state_path, const char *filename) +{ + int32_t num; + const char *eol; + int error = 0; + + if ((error = rebase_readfile(asc_out, state_path, filename)) < 0) + return error; + + if (git__strtol32(&num, asc_out->ptr, &eol, 10) < 0 || num < 0 || *eol) { + giterr_set(GITERR_REBASE, "The file '%s' contains an invalid numeric value", filename); + return -1; + } + + *out = (size_t) num; + + return 0; +} + +GIT_INLINE(int) rebase_readoid( + git_oid *out, git_buf *str_out, git_buf *state_path, const char *filename) +{ + int error; + + if ((error = rebase_readfile(str_out, state_path, filename)) < 0) + return error; + + if (str_out->size != GIT_OID_HEXSZ || git_oid_fromstr(out, str_out->ptr) < 0) { + giterr_set(GITERR_REBASE, "The file '%s' contains an invalid object ID", filename); + return -1; + } + + return 0; +} + +static int rebase_open_merge(git_rebase *rebase) +{ + git_buf state_path = GIT_BUF_INIT, buf = GIT_BUF_INIT, cmt = GIT_BUF_INIT; + git_oid current_id = {{0}}; + git_rebase_operation *operation; + size_t i, msgnum = 0, end; + int error; + + if ((error = git_buf_puts(&state_path, rebase->state_path)) < 0) + goto done; + + /* Read 'msgnum' if it exists (otherwise, let msgnum = 0) */ + if ((error = rebase_readint(&msgnum, &buf, &state_path, MSGNUM_FILE)) < 0 && + error != GIT_ENOTFOUND) + goto done; + + if (msgnum) { + rebase->started = 1; + rebase->current = msgnum - 1; + } + + /* Read 'end' */ + if ((error = rebase_readint(&end, &buf, &state_path, END_FILE)) < 0) + goto done; + + /* Read 'current' if it exists */ + if ((error = rebase_readoid(¤t_id, &buf, &state_path, CURRENT_FILE)) < 0 && + error != GIT_ENOTFOUND) + goto done; + + /* Read cmt.* */ + git_array_init_to_size(rebase->operations, end); + GITERR_CHECK_ARRAY(rebase->operations); + + for (i = 0; i < end; i++) { + operation = git_array_alloc(rebase->operations); + GITERR_CHECK_ALLOC(operation); + + git_buf_clear(&cmt); + + if ((error = git_buf_printf(&cmt, "cmt.%" PRIuZ, (i+1))) < 0 || + (error = rebase_readoid((git_oid *)&operation->id, &buf, &state_path, cmt.ptr)) < 0) + goto done; + } + + /* Read 'onto_name' */ + if ((error = rebase_readfile(&buf, &state_path, ONTO_NAME_FILE)) < 0) + goto done; + + rebase->onto_name = git_buf_detach(&buf); + +done: + git_buf_free(&cmt); + git_buf_free(&state_path); + git_buf_free(&buf); + + return error; +} + +int git_rebase_open(git_rebase **out, git_repository *repo) +{ + git_rebase *rebase; + git_buf path = GIT_BUF_INIT, orig_head_name = GIT_BUF_INIT, + orig_head_id = GIT_BUF_INIT, onto_id = GIT_BUF_INIT; + int state_path_len, error; + + assert(repo); + + rebase = git__calloc(1, sizeof(git_rebase)); + GITERR_CHECK_ALLOC(rebase); + + rebase->repo = repo; + + if ((error = rebase_state_type(&rebase->type, &rebase->state_path, repo)) < 0) + goto done; + + if (rebase->type == GIT_REBASE_TYPE_NONE) { + giterr_set(GITERR_REBASE, "There is no rebase in progress"); + error = GIT_ENOTFOUND; + goto done; + } + + if ((error = git_buf_puts(&path, rebase->state_path)) < 0) + goto done; + + state_path_len = git_buf_len(&path); + + if ((error = git_buf_joinpath(&path, path.ptr, HEAD_NAME_FILE)) < 0 || + (error = git_futils_readbuffer(&orig_head_name, path.ptr)) < 0) + goto done; + + git_buf_rtrim(&orig_head_name); + + if (strcmp(ORIG_DETACHED_HEAD, orig_head_name.ptr) == 0) + rebase->head_detached = 1; + + git_buf_truncate(&path, state_path_len); + + if ((error = git_buf_joinpath(&path, path.ptr, ORIG_HEAD_FILE)) < 0) + goto done; + + if (!git_path_isfile(path.ptr)) { + /* Previous versions of git.git used 'head' here; support that. */ + git_buf_truncate(&path, state_path_len); + + if ((error = git_buf_joinpath(&path, path.ptr, HEAD_FILE)) < 0) + goto done; + } + + if ((error = git_futils_readbuffer(&orig_head_id, path.ptr)) < 0) + goto done; + + git_buf_rtrim(&orig_head_id); + + if ((error = git_oid_fromstr(&rebase->orig_head_id, orig_head_id.ptr)) < 0) + goto done; + + git_buf_truncate(&path, state_path_len); + + if ((error = git_buf_joinpath(&path, path.ptr, ONTO_FILE)) < 0 || + (error = git_futils_readbuffer(&onto_id, path.ptr)) < 0) + goto done; + + git_buf_rtrim(&onto_id); + + if ((error = git_oid_fromstr(&rebase->onto_id, onto_id.ptr)) < 0) + goto done; + + if (!rebase->head_detached) + rebase->orig_head_name = git_buf_detach(&orig_head_name); + + switch (rebase->type) { + case GIT_REBASE_TYPE_INTERACTIVE: + giterr_set(GITERR_REBASE, "Interactive rebase is not supported"); + error = -1; + break; + case GIT_REBASE_TYPE_MERGE: + error = rebase_open_merge(rebase); + break; + case GIT_REBASE_TYPE_APPLY: + giterr_set(GITERR_REBASE, "Patch application rebase is not supported"); + error = -1; + break; + default: + abort(); + } + +done: + if (error == 0) + *out = rebase; + else + git_rebase_free(rebase); + + git_buf_free(&path); + git_buf_free(&orig_head_name); + git_buf_free(&orig_head_id); + git_buf_free(&onto_id); + return error; +} + +static int rebase_cleanup(git_rebase *rebase) +{ + return git_path_isdir(rebase->state_path) ? + git_futils_rmdir_r(rebase->state_path, NULL, GIT_RMDIR_REMOVE_FILES) : + 0; +} + +static int rebase_setupfile(git_rebase *rebase, const char *filename, int flags, const char *fmt, ...) +{ + git_buf path = GIT_BUF_INIT, + contents = GIT_BUF_INIT; + va_list ap; + int error; + + va_start(ap, fmt); + git_buf_vprintf(&contents, fmt, ap); + va_end(ap); + + if ((error = git_buf_joinpath(&path, rebase->state_path, filename)) == 0) + error = git_futils_writebuffer(&contents, path.ptr, flags, REBASE_FILE_MODE); + + git_buf_free(&path); + git_buf_free(&contents); + + return error; +} + +static const char *rebase_onto_name(const git_annotated_commit *onto) +{ + if (onto->ref_name && git__strncmp(onto->ref_name, "refs/heads/", 11) == 0) + return onto->ref_name + 11; + else if (onto->ref_name) + return onto->ref_name; + else + return onto->id_str; +} + +static int rebase_setupfiles_merge(git_rebase *rebase) +{ + git_buf commit_filename = GIT_BUF_INIT; + char id_str[GIT_OID_HEXSZ]; + git_rebase_operation *operation; + size_t i; + int error = 0; + + if ((error = rebase_setupfile(rebase, END_FILE, -1, "%d\n", git_array_size(rebase->operations))) < 0 || + (error = rebase_setupfile(rebase, ONTO_NAME_FILE, -1, "%s\n", rebase->onto_name)) < 0) + goto done; + + for (i = 0; i < git_array_size(rebase->operations); i++) { + operation = git_array_get(rebase->operations, i); + + git_buf_clear(&commit_filename); + git_buf_printf(&commit_filename, CMT_FILE_FMT, i+1); + + git_oid_fmt(id_str, &operation->id); + + if ((error = rebase_setupfile(rebase, commit_filename.ptr, -1, + "%.*s\n", GIT_OID_HEXSZ, id_str)) < 0) + goto done; + } + +done: + git_buf_free(&commit_filename); + return error; +} + +static int rebase_setupfiles(git_rebase *rebase) +{ + char onto[GIT_OID_HEXSZ], orig_head[GIT_OID_HEXSZ]; + + git_oid_fmt(onto, &rebase->onto_id); + git_oid_fmt(orig_head, &rebase->orig_head_id); + + if (p_mkdir(rebase->state_path, REBASE_DIR_MODE) < 0) { + giterr_set(GITERR_OS, "Failed to create rebase directory '%s'", rebase->state_path); + return -1; + } + + if (git_repository__set_orig_head(rebase->repo, &rebase->orig_head_id) < 0 || + rebase_setupfile(rebase, HEAD_NAME_FILE, -1, "%s\n", rebase->orig_head_name) < 0 || + rebase_setupfile(rebase, ONTO_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, onto) < 0 || + rebase_setupfile(rebase, ORIG_HEAD_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, orig_head) < 0 || + rebase_setupfile(rebase, QUIET_FILE, -1, rebase->quiet ? "t\n" : "\n") < 0) + return -1; + + return rebase_setupfiles_merge(rebase); +} + +int git_rebase_init_options(git_rebase_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_rebase_options, GIT_REBASE_OPTIONS_INIT); + return 0; +} + +static int rebase_normalize_opts( + git_repository *repo, + git_rebase_options *opts, + const git_rebase_options *given_opts) +{ + git_rebase_options default_opts = GIT_REBASE_OPTIONS_INIT; + git_config *config; + + if (given_opts) + memcpy(opts, given_opts, sizeof(git_rebase_options)); + else + memcpy(opts, &default_opts, sizeof(git_rebase_options)); + + if (git_repository_config(&config, repo) < 0) + return -1; + + if (given_opts && given_opts->rewrite_notes_ref) { + opts->rewrite_notes_ref = git__strdup(given_opts->rewrite_notes_ref); + GITERR_CHECK_ALLOC(opts->rewrite_notes_ref); + } else if (git_config__get_bool_force(config, "notes.rewrite.rebase", 1)) { + const char *rewrite_ref = git_config__get_string_force( + config, "notes.rewriteref", NOTES_DEFAULT_REF); + + if (rewrite_ref) { + opts->rewrite_notes_ref = git__strdup(rewrite_ref); + GITERR_CHECK_ALLOC(opts->rewrite_notes_ref); + } + } + + git_config_free(config); + + return 0; +} + +static void rebase_opts_free(git_rebase_options *opts) +{ + if (!opts) + return; + + git__free((char *)opts->rewrite_notes_ref); +} + +static int rebase_ensure_not_in_progress(git_repository *repo) +{ + int error; + git_rebase_type_t type; + + if ((error = rebase_state_type(&type, NULL, repo)) < 0) + return error; + + if (type != GIT_REBASE_TYPE_NONE) { + giterr_set(GITERR_REBASE, "There is an existing rebase in progress"); + return -1; + } + + return 0; +} + +static int rebase_ensure_not_dirty(git_repository *repo) +{ + git_tree *head = NULL; + git_index *index = NULL; + git_diff *diff = NULL; + int error; + + if ((error = git_repository_head_tree(&head, repo)) < 0 || + (error = git_repository_index(&index, repo)) < 0 || + (error = git_diff_tree_to_index(&diff, repo, head, index, NULL)) < 0) + goto done; + + if (git_diff_num_deltas(diff) > 0) { + giterr_set(GITERR_REBASE, "Uncommitted changes exist in index"); + error = -1; + goto done; + } + + git_diff_free(diff); + diff = NULL; + + if ((error = git_diff_index_to_workdir(&diff, repo, index, NULL)) < 0) + goto done; + + if (git_diff_num_deltas(diff) > 0) { + giterr_set(GITERR_REBASE, "Unstaged changes exist in workdir"); + error = -1; + } + +done: + git_diff_free(diff); + git_index_free(index); + git_tree_free(head); + + return error; +} + +static int rebase_init_operations( + git_rebase *rebase, + git_repository *repo, + const git_annotated_commit *branch, + const git_annotated_commit *upstream, + const git_annotated_commit *onto) +{ + git_revwalk *revwalk = NULL; + git_commit *commit; + git_oid id; + bool merge; + git_rebase_operation *operation; + int error; + + if (!upstream) + upstream = onto; + + if ((error = git_revwalk_new(&revwalk, rebase->repo)) < 0 || + (error = git_revwalk_push(revwalk, git_annotated_commit_id(branch))) < 0 || + (error = git_revwalk_hide(revwalk, git_annotated_commit_id(upstream))) < 0) + goto done; + + git_revwalk_sorting(revwalk, GIT_SORT_REVERSE | GIT_SORT_TIME); + + while ((error = git_revwalk_next(&id, revwalk)) == 0) { + if ((error = git_commit_lookup(&commit, repo, &id)) < 0) + goto done; + + merge = (git_commit_parentcount(commit) > 1); + git_commit_free(commit); + + if (merge) + continue; + + operation = git_array_alloc(rebase->operations); + operation->type = GIT_REBASE_OPERATION_PICK; + git_oid_cpy((git_oid *)&operation->id, &id); + } + + error = 0; + +done: + git_revwalk_free(revwalk); + return error; +} + +static int rebase_init_merge( + git_rebase *rebase, + git_repository *repo, + const git_annotated_commit *branch, + const git_annotated_commit *upstream, + const git_annotated_commit *onto) +{ + if (rebase_init_operations(rebase, repo, branch, upstream, onto) < 0) + return -1; + + rebase->onto_name = git__strdup(rebase_onto_name(onto)); + GITERR_CHECK_ALLOC(rebase->onto_name); + + return 0; +} + +static int rebase_init( + git_rebase *rebase, + git_repository *repo, + const git_annotated_commit *branch, + const git_annotated_commit *upstream, + const git_annotated_commit *onto, + const git_rebase_options *opts) +{ + git_buf state_path = GIT_BUF_INIT; + int error; + + if ((error = git_buf_joinpath(&state_path, repo->path_repository, REBASE_MERGE_DIR)) < 0) + return error; + + rebase->repo = repo; + rebase->type = GIT_REBASE_TYPE_MERGE; + rebase->state_path = git_buf_detach(&state_path); + rebase->orig_head_name = git__strdup(branch->ref_name ? branch->ref_name : ORIG_DETACHED_HEAD); + rebase->quiet = opts->quiet; + + git_oid_cpy(&rebase->orig_head_id, git_annotated_commit_id(branch)); + git_oid_cpy(&rebase->onto_id, git_annotated_commit_id(onto)); + + if (!rebase->orig_head_name || !rebase->state_path) + return -1; + + error = rebase_init_merge(rebase, repo, branch, upstream, onto); + + git_buf_free(&state_path); + + return error; +} + +int git_rebase_init( + git_rebase **out, + git_repository *repo, + const git_annotated_commit *branch, + const git_annotated_commit *upstream, + const git_annotated_commit *onto, + const git_signature *signature, + const git_rebase_options *given_opts) +{ + git_rebase *rebase = NULL; + git_rebase_options opts; + git_reference *head_ref = NULL; + git_buf reflog = GIT_BUF_INIT; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + int error; + + assert(repo && branch && (upstream || onto)); + + *out = NULL; + + GITERR_CHECK_VERSION(given_opts, GIT_REBASE_OPTIONS_VERSION, "git_rebase_options"); + + if (!onto) + onto = upstream; + + checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + if ((error = rebase_normalize_opts(repo, &opts, given_opts)) < 0 || + (error = git_repository__ensure_not_bare(repo, "rebase")) < 0 || + (error = rebase_ensure_not_in_progress(repo)) < 0 || + (error = rebase_ensure_not_dirty(repo)) < 0) + return error; + + rebase = git__calloc(1, sizeof(git_rebase)); + GITERR_CHECK_ALLOC(rebase); + + if ((error = rebase_init(rebase, repo, branch, upstream, onto, &opts)) < 0 || + (error = rebase_setupfiles(rebase)) < 0 || + (error = git_buf_printf(&reflog, + "rebase: checkout %s", rebase_onto_name(onto))) < 0 || + (error = git_reference_create(&head_ref, repo, GIT_HEAD_FILE, + git_annotated_commit_id(onto), 1, signature, reflog.ptr)) < 0 || + (error = git_checkout_head(repo, &checkout_opts)) < 0) + goto done; + + *out = rebase; + +done: + if (error < 0) { + rebase_cleanup(rebase); + git_rebase_free(rebase); + } + + git_reference_free(head_ref); + git_buf_free(&reflog); + rebase_opts_free(&opts); + + return error; +} + +static void normalize_checkout_opts( + git_rebase *rebase, + git_commit *current_commit, + git_checkout_options *checkout_opts, + const git_checkout_options *given_checkout_opts) +{ + if (given_checkout_opts != NULL) + memcpy(checkout_opts, given_checkout_opts, sizeof(git_checkout_options)); + else { + git_checkout_options default_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + default_checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + + memcpy(checkout_opts, &default_checkout_opts, sizeof(git_checkout_options)); + } + + if (!checkout_opts->ancestor_label) + checkout_opts->ancestor_label = "ancestor"; + + if (rebase->type == GIT_REBASE_TYPE_MERGE) { + if (!checkout_opts->our_label) + checkout_opts->our_label = rebase->onto_name; + + if (!checkout_opts->their_label) + checkout_opts->their_label = git_commit_summary(current_commit); + } else { + abort(); + } +} + +GIT_INLINE(int) rebase_movenext(git_rebase *rebase) +{ + size_t next = rebase->started ? rebase->current + 1 : 0; + + if (next == git_array_size(rebase->operations)) + return GIT_ITEROVER; + + rebase->started = 1; + rebase->current = next; + + return 0; +} + +static int rebase_next_merge( + git_rebase_operation **out, + git_rebase *rebase, + git_checkout_options *given_checkout_opts) +{ + git_buf path = GIT_BUF_INIT; + git_checkout_options checkout_opts = {0}; + git_commit *current_commit = NULL, *parent_commit = NULL; + git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL; + git_index *index = NULL; + git_rebase_operation *operation; + char current_idstr[GIT_OID_HEXSZ]; + unsigned int parent_count; + int error; + + *out = NULL; + + if ((error = rebase_movenext(rebase)) < 0) + goto done; + + operation = git_array_get(rebase->operations, rebase->current); + + if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || + (error = git_commit_tree(¤t_tree, current_commit)) < 0 || + (error = git_repository_head_tree(&head_tree, rebase->repo)) < 0) + goto done; + + if ((parent_count = git_commit_parentcount(current_commit)) > 1) { + giterr_set(GITERR_REBASE, "Cannot rebase a merge commit"); + error = -1; + goto done; + } else if (parent_count) { + if ((error = git_commit_parent(&parent_commit, current_commit, 0)) < 0 || + (error = git_commit_tree(&parent_tree, parent_commit)) < 0) + goto done; + } + + git_oid_fmt(current_idstr, &operation->id); + + if ((error = rebase_setupfile(rebase, MSGNUM_FILE, -1, "%d\n", rebase->current+1)) < 0 || + (error = rebase_setupfile(rebase, CURRENT_FILE, -1, "%.*s\n", GIT_OID_HEXSZ, current_idstr)) < 0) + goto done; + + normalize_checkout_opts(rebase, current_commit, &checkout_opts, given_checkout_opts); + + if ((error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, NULL)) < 0 || + (error = git_merge__check_result(rebase->repo, index)) < 0 || + (error = git_checkout_index(rebase->repo, index, &checkout_opts)) < 0) + goto done; + + *out = operation; + +done: + git_index_free(index); + git_tree_free(current_tree); + git_tree_free(head_tree); + git_tree_free(parent_tree); + git_commit_free(parent_commit); + git_commit_free(current_commit); + git_buf_free(&path); + + return error; +} + +int git_rebase_next( + git_rebase_operation **out, + git_rebase *rebase, + git_checkout_options *checkout_opts) +{ + int error; + + assert(out && rebase); + + switch (rebase->type) { + case GIT_REBASE_TYPE_MERGE: + error = rebase_next_merge(out, rebase, checkout_opts); + break; + default: + abort(); + } + + return error; +} + +static int rebase_commit_merge( + git_oid *commit_id, + git_rebase *rebase, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message) +{ + git_index *index = NULL; + git_reference *head = NULL; + git_commit *current_commit = NULL, *head_commit = NULL, *commit = NULL; + git_rebase_operation *operation; + git_tree *head_tree = NULL, *tree = NULL; + git_diff *diff = NULL; + git_oid tree_id; + git_buf reflog_msg = GIT_BUF_INIT; + char old_idstr[GIT_OID_HEXSZ], new_idstr[GIT_OID_HEXSZ]; + int error; + + operation = git_array_get(rebase->operations, rebase->current); + assert(operation); + + if ((error = git_repository_index(&index, rebase->repo)) < 0) + goto done; + + if (git_index_has_conflicts(index)) { + giterr_set(GITERR_REBASE, "Conflicts have not been resolved"); + error = GIT_EMERGECONFLICT; + goto done; + } + + if ((error = git_commit_lookup(¤t_commit, rebase->repo, &operation->id)) < 0 || + (error = git_repository_head(&head, rebase->repo)) < 0 || + (error = git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT)) < 0 || + (error = git_commit_tree(&head_tree, head_commit)) < 0 || + (error = git_diff_tree_to_index(&diff, rebase->repo, head_tree, index, NULL)) < 0) + goto done; + + if (git_diff_num_deltas(diff) == 0) { + giterr_set(GITERR_REBASE, "This patch has already been applied"); + error = GIT_EAPPLIED; + goto done; + } + + if ((error = git_index_write_tree(&tree_id, index)) < 0 || + (error = git_tree_lookup(&tree, rebase->repo, &tree_id)) < 0) + goto done; + + if (!author) + author = git_commit_author(current_commit); + + if (!message) { + message_encoding = git_commit_message_encoding(current_commit); + message = git_commit_message(current_commit); + } + + if ((error = git_commit_create(commit_id, rebase->repo, NULL, author, + committer, message_encoding, message, tree, 1, + (const git_commit **)&head_commit)) < 0 || + (error = git_commit_lookup(&commit, rebase->repo, commit_id)) < 0 || + (error = git_reference__update_for_commit( + rebase->repo, NULL, "HEAD", commit_id, committer, "rebase")) < 0) + goto done; + + git_oid_fmt(old_idstr, git_commit_id(current_commit)); + git_oid_fmt(new_idstr, commit_id); + + error = rebase_setupfile(rebase, REWRITTEN_FILE, O_CREAT|O_WRONLY|O_APPEND, + "%.*s %.*s\n", GIT_OID_HEXSZ, old_idstr, GIT_OID_HEXSZ, new_idstr); + +done: + git_buf_free(&reflog_msg); + git_commit_free(commit); + git_diff_free(diff); + git_tree_free(tree); + git_tree_free(head_tree); + git_commit_free(head_commit); + git_commit_free(current_commit); + git_reference_free(head); + git_index_free(index); + + return error; +} + +int git_rebase_commit( + git_oid *id, + git_rebase *rebase, + const git_signature *author, + const git_signature *committer, + const char *message_encoding, + const char *message) +{ + int error; + + assert(rebase && committer); + + switch (rebase->type) { + case GIT_REBASE_TYPE_MERGE: + error = rebase_commit_merge( + id, rebase, author, committer, message_encoding, message); + break; + default: + abort(); + } + + return error; +} + +int git_rebase_abort(git_rebase *rebase, const git_signature *signature) +{ + git_reference *orig_head_ref = NULL; + git_commit *orig_head_commit = NULL; + int error; + + assert(rebase && signature); + + error = rebase->head_detached ? + git_reference_create(&orig_head_ref, rebase->repo, GIT_HEAD_FILE, + &rebase->orig_head_id, 1, signature, "rebase: aborting") : + git_reference_symbolic_create( + &orig_head_ref, rebase->repo, GIT_HEAD_FILE, rebase->orig_head_name, 1, + signature, "rebase: aborting"); + + if (error < 0) + goto done; + + if ((error = git_commit_lookup( + &orig_head_commit, rebase->repo, &rebase->orig_head_id)) < 0 || + (error = git_reset(rebase->repo, (git_object *)orig_head_commit, + GIT_RESET_HARD, NULL, signature, NULL)) < 0) + goto done; + + error = rebase_cleanup(rebase); + +done: + git_commit_free(orig_head_commit); + git_reference_free(orig_head_ref); + + return error; +} + +static int rebase_copy_note( + git_rebase *rebase, + git_oid *from, + git_oid *to, + const git_signature *committer, + const git_rebase_options *opts) +{ + git_note *note = NULL; + git_oid note_id; + git_signature *who = NULL; + int error; + + if ((error = git_note_read(¬e, rebase->repo, opts->rewrite_notes_ref, from)) < 0) { + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = 0; + } + + goto done; + } + + if (!committer) { + if((error = git_signature_default(&who, rebase->repo)) < 0) { + if (error != GIT_ENOTFOUND || + (error = git_signature_now(&who, "unknown", "unknown")) < 0) + goto done; + + giterr_clear(); + } + + committer = who; + } + + error = git_note_create(¬e_id, rebase->repo, opts->rewrite_notes_ref, + git_note_author(note), committer, to, git_note_message(note), 0); + +done: + git_note_free(note); + git_signature_free(who); + + return error; +} + +static int rebase_copy_notes( + git_rebase *rebase, + const git_signature *committer, + const git_rebase_options *opts) +{ + git_buf path = GIT_BUF_INIT, rewritten = GIT_BUF_INIT; + char *pair_list, *fromstr, *tostr, *end; + git_oid from, to; + unsigned int linenum = 1; + int error = 0; + + if (!opts->rewrite_notes_ref) + goto done; + + if ((error = git_buf_joinpath(&path, rebase->state_path, REWRITTEN_FILE)) < 0 || + (error = git_futils_readbuffer(&rewritten, path.ptr)) < 0) + goto done; + + pair_list = rewritten.ptr; + + while (*pair_list) { + fromstr = pair_list; + + if ((end = strchr(fromstr, '\n')) == NULL) + goto on_error; + + pair_list = end+1; + *end = '\0'; + + if ((end = strchr(fromstr, ' ')) == NULL) + goto on_error; + + tostr = end+1; + *end = '\0'; + + if (strlen(fromstr) != GIT_OID_HEXSZ || + strlen(tostr) != GIT_OID_HEXSZ || + git_oid_fromstr(&from, fromstr) < 0 || + git_oid_fromstr(&to, tostr) < 0) + goto on_error; + + if ((error = rebase_copy_note(rebase, &from, &to, committer, opts)) < 0) + goto done; + + linenum++; + } + + goto done; + +on_error: + giterr_set(GITERR_REBASE, "Invalid rewritten file at line %d", linenum); + error = -1; + +done: + git_buf_free(&rewritten); + git_buf_free(&path); + + return error; +} + +int git_rebase_finish( + git_rebase *rebase, + const git_signature *signature, + const git_rebase_options *given_opts) +{ + git_rebase_options opts; + git_reference *terminal_ref = NULL, *branch_ref = NULL, *head_ref = NULL; + git_commit *terminal_commit = NULL; + git_buf branch_msg = GIT_BUF_INIT, head_msg = GIT_BUF_INIT; + char onto[GIT_OID_HEXSZ]; + int error; + + assert(rebase); + + GITERR_CHECK_VERSION(given_opts, GIT_REBASE_OPTIONS_VERSION, "git_rebase_options"); + + if ((error = rebase_normalize_opts(rebase->repo, &opts, given_opts)) < 0) + goto done; + + git_oid_fmt(onto, &rebase->onto_id); + + if ((error = git_buf_printf(&branch_msg, "rebase finished: %s onto %.*s", + rebase->orig_head_name, GIT_OID_HEXSZ, onto)) < 0 || + (error = git_buf_printf(&head_msg, "rebase finished: returning to %s", + rebase->orig_head_name)) < 0 || + (error = git_repository_head(&terminal_ref, rebase->repo)) < 0 || + (error = git_reference_peel((git_object **)&terminal_commit, + terminal_ref, GIT_OBJ_COMMIT)) < 0 || + (error = git_reference_create_matching(&branch_ref, + rebase->repo, rebase->orig_head_name, git_commit_id(terminal_commit), 1, + &rebase->orig_head_id, signature, branch_msg.ptr)) < 0 || + (error = git_reference_symbolic_create(&head_ref, + rebase->repo, GIT_HEAD_FILE, rebase->orig_head_name, 1, + signature, head_msg.ptr)) < 0 || + (error = rebase_copy_notes(rebase, signature, &opts)) < 0) + goto done; + + error = rebase_cleanup(rebase); + +done: + git_buf_free(&head_msg); + git_buf_free(&branch_msg); + git_commit_free(terminal_commit); + git_reference_free(head_ref); + git_reference_free(branch_ref); + git_reference_free(terminal_ref); + rebase_opts_free(&opts); + + return error; +} + +size_t git_rebase_operation_entrycount(git_rebase *rebase) +{ + assert(rebase); + + return git_array_size(rebase->operations); +} + +size_t git_rebase_operation_current(git_rebase *rebase) +{ + assert(rebase); + + return rebase->current; +} + +git_rebase_operation *git_rebase_operation_byindex(git_rebase *rebase, size_t idx) +{ + assert(rebase); + + return git_array_get(rebase->operations, idx); +} + +void git_rebase_free(git_rebase *rebase) +{ + if (rebase == NULL) + return; + + git__free(rebase->onto_name); + git__free(rebase->orig_head_name); + git__free(rebase->state_path); + git_array_clear(rebase->operations); + git__free(rebase); +} diff -Nru libgit2-0.20.0/src/refdb.c libgit2-0.22.2/src/refdb.c --- libgit2-0.20.0/src/refdb.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/refdb.c 2015-03-24 16:10:45.000000000 +0000 @@ -167,14 +167,14 @@ iter->free(iter); } -int git_refdb_write(git_refdb *db, git_reference *ref, int force) +int git_refdb_write(git_refdb *db, git_reference *ref, int force, const git_signature *who, const char *message, const git_oid *old_id, const char *old_target) { assert(db && db->backend); GIT_REFCOUNT_INC(db); ref->db = db; - return db->backend->write(db->backend, ref, force); + return db->backend->write(db->backend, ref, force, who, message, old_id, old_target); } int git_refdb_rename( @@ -182,12 +182,14 @@ git_refdb *db, const char *old_name, const char *new_name, - int force) + int force, + const git_signature *who, + const char *message) { int error; assert(db && db->backend); - error = db->backend->rename(out, db->backend, old_name, new_name, force); + error = db->backend->rename(out, db->backend, old_name, new_name, force, who, message); if (error < 0) return error; @@ -199,10 +201,10 @@ return 0; } -int git_refdb_delete(struct git_refdb *db, const char *ref_name) +int git_refdb_delete(struct git_refdb *db, const char *ref_name, const git_oid *old_id, const char *old_target) { assert(db && db->backend); - return db->backend->del(db->backend, ref_name); + return db->backend->del(db->backend, ref_name, old_id, old_target); } int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name) @@ -219,3 +221,43 @@ return 0; } + +int git_refdb_has_log(git_refdb *db, const char *refname) +{ + assert(db && refname); + + return db->backend->has_log(db->backend, refname); +} + +int git_refdb_ensure_log(git_refdb *db, const char *refname) +{ + assert(db && refname); + + return db->backend->ensure_log(db->backend, refname); +} + +int git_refdb_init_backend(git_refdb_backend *backend, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + backend, version, git_refdb_backend, GIT_REFDB_BACKEND_INIT); + return 0; +} + +int git_refdb_lock(void **payload, git_refdb *db, const char *refname) +{ + assert(payload && db && refname); + + if (!db->backend->lock) { + giterr_set(GITERR_REFERENCE, "backend does not support locking"); + return -1; + } + + return db->backend->lock(payload, db->backend, refname); +} + +int git_refdb_unlock(git_refdb *db, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message) +{ + assert(db); + + return db->backend->unlock(db->backend, payload, success, update_reflog, ref, sig, message); +} diff -Nru libgit2-0.20.0/src/refdb_fs.c libgit2-0.22.2/src/refdb_fs.c --- libgit2-0.20.0/src/refdb_fs.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/refdb_fs.c 2015-03-24 16:10:45.000000000 +0000 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -264,17 +265,25 @@ return error; } -static int _dirent_loose_load(void *data, git_buf *full_path) +static int _dirent_loose_load(void *payload, git_buf *full_path) { - refdb_fs_backend *backend = (refdb_fs_backend *)data; + refdb_fs_backend *backend = payload; const char *file_path; if (git__suffixcmp(full_path->ptr, ".lock") == 0) return 0; - if (git_path_isdir(full_path->ptr)) - return git_path_direach( + if (git_path_isdir(full_path->ptr)) { + int error = git_path_direach( full_path, backend->direach_flags, _dirent_loose_load, backend); + /* Race with the filesystem, ignore it */ + if (error == GIT_ENOTFOUND) { + giterr_clear(); + return 0; + } + + return error; + } file_path = full_path->ptr + strlen(backend->path); @@ -305,7 +314,7 @@ git_buf_free(&refs_path); - return (error == GIT_EUSER) ? -1 : error; + return error; } static int refdb_fs_backend__exists( @@ -449,6 +458,7 @@ git_pool pool; git_vector loose; + git_sortedcache *cache; size_t loose_pos; size_t packed_pos; } refdb_fs_iter; @@ -459,6 +469,7 @@ git_vector_free(&iter->loose); git_pool_clear(&iter->pool); + git_sortedcache_free(iter->cache); git__free(iter); } @@ -530,10 +541,14 @@ giterr_clear(); } - git_sortedcache_rlock(backend->refcache); + if (!iter->cache) { + if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0) + return error; + } - while (iter->packed_pos < git_sortedcache_entrycount(backend->refcache)) { - ref = git_sortedcache_entry(backend->refcache, iter->packed_pos++); + error = GIT_ITEROVER; + while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) { + ref = git_sortedcache_entry(iter->cache, iter->packed_pos++); if (!ref) /* stop now if another thread deleted refs and we past end */ break; @@ -547,7 +562,6 @@ break; } - git_sortedcache_runlock(backend->refcache); return error; } @@ -570,10 +584,14 @@ giterr_clear(); } - git_sortedcache_rlock(backend->refcache); + if (!iter->cache) { + if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0) + return error; + } - while (iter->packed_pos < git_sortedcache_entrycount(backend->refcache)) { - ref = git_sortedcache_entry(backend->refcache, iter->packed_pos++); + error = GIT_ITEROVER; + while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) { + ref = git_sortedcache_entry(iter->cache, iter->packed_pos++); if (!ref) /* stop now if another thread deleted refs and we past end */ break; @@ -587,7 +605,6 @@ break; } - git_sortedcache_runlock(backend->refcache); return error; } @@ -688,39 +705,100 @@ return 0; } -static int loose_write(refdb_fs_backend *backend, const git_reference *ref) +static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *name) { - git_filebuf file = GIT_FILEBUF_INIT; + int error; git_buf ref_path = GIT_BUF_INIT; + assert(file && backend && name); + + if (!git_path_isvalid(backend->repo, name, GIT_PATH_REJECT_DEFAULTS)) { + giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", name); + return GIT_EINVALIDSPEC; + } + /* Remove a possibly existing empty directory hierarchy * which name would collide with the reference name */ - if (git_futils_rmdir_r(ref->name, backend->path, GIT_RMDIR_SKIP_NONEMPTY) < 0) + if (git_futils_rmdir_r(name, backend->path, GIT_RMDIR_SKIP_NONEMPTY) < 0) return -1; - if (git_buf_joinpath(&ref_path, backend->path, ref->name) < 0) + if (git_buf_joinpath(&ref_path, backend->path, name) < 0) return -1; - if (git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE) < 0) { - git_buf_free(&ref_path); - return -1; - } + error = git_filebuf_open(file, ref_path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE); git_buf_free(&ref_path); + return error; +} + +static int loose_commit(git_filebuf *file, const git_reference *ref) +{ + assert(file && ref); if (ref->type == GIT_REF_OID) { char oid[GIT_OID_HEXSZ + 1]; git_oid_nfmt(oid, sizeof(oid), &ref->target.oid); - git_filebuf_printf(&file, "%s\n", oid); + git_filebuf_printf(file, "%s\n", oid); } else if (ref->type == GIT_REF_SYMBOLIC) { - git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref->target.symbolic); + git_filebuf_printf(file, GIT_SYMREF "%s\n", ref->target.symbolic); } else { assert(0); /* don't let this happen */ } - return git_filebuf_commit(&file); + return git_filebuf_commit(file); +} + +static int refdb_fs_backend__lock(void **out, git_refdb_backend *_backend, const char *refname) +{ + int error; + git_filebuf *lock; + refdb_fs_backend *backend = (refdb_fs_backend *) _backend; + + lock = git__calloc(1, sizeof(git_filebuf)); + GITERR_CHECK_ALLOC(lock); + + if ((error = loose_lock(lock, backend, refname)) < 0) { + git__free(lock); + return error; + } + + *out = lock; + return 0; +} + +static int refdb_fs_backend__write_tail( + git_refdb_backend *_backend, + const git_reference *ref, + git_filebuf *file, + int update_reflog, + const git_signature *who, + const char *message, + const git_oid *old_id, + const char *old_target); + +static int refdb_fs_backend__delete_tail( + git_refdb_backend *_backend, + git_filebuf *file, + const char *ref_name, + const git_oid *old_id, const char *old_target); + +static int refdb_fs_backend__unlock(git_refdb_backend *backend, void *payload, int success, int update_reflog, + const git_reference *ref, const git_signature *sig, const char *message) +{ + git_filebuf *lock = (git_filebuf *) payload; + int error = 0; + + if (success == 2) + error = refdb_fs_backend__delete_tail(backend, lock, ref->name, NULL, NULL); + else if (success) + error = refdb_fs_backend__write_tail(backend, ref, lock, update_reflog, sig, message, NULL, NULL); + else + git_filebuf_cleanup(lock); + + git__free(lock); + return error; } /* @@ -907,13 +985,152 @@ return -1; } +static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *author, const char *message); +static int has_reflog(git_repository *repo, const char *name); + +/* We only write if it's under heads/, remotes/ or notes/ or if it already has a log */ +static int should_write_reflog(int *write, git_repository *repo, const char *name) +{ + int error, logall; + + error = git_repository__cvar(&logall, repo, GIT_CVAR_LOGALLREFUPDATES); + if (error < 0) + return error; + + /* Defaults to the opposite of the repo being bare */ + if (logall == GIT_LOGALLREFUPDATES_UNSET) + logall = !git_repository_is_bare(repo); + + if (!logall) { + *write = 0; + } else if (has_reflog(repo, name)) { + *write = 1; + } else if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR) || + !git__strcmp(name, GIT_HEAD_FILE) || + !git__prefixcmp(name, GIT_REFS_REMOTES_DIR) || + !git__prefixcmp(name, GIT_REFS_NOTES_DIR)) { + *write = 1; + } else { + *write = 0; + } + + return 0; +} + +static int cmp_old_ref(int *cmp, git_refdb_backend *backend, const char *name, + const git_oid *old_id, const char *old_target) +{ + int error = 0; + git_reference *old_ref = NULL; + + *cmp = 0; + /* It "matches" if there is no old value to compare against */ + if (!old_id && !old_target) + return 0; + + if ((error = refdb_fs_backend__lookup(&old_ref, backend, name)) < 0) + goto out; + + /* If the types don't match, there's no way the values do */ + if (old_id && old_ref->type != GIT_REF_OID) { + *cmp = -1; + goto out; + } + if (old_target && old_ref->type != GIT_REF_SYMBOLIC) { + *cmp = 1; + goto out; + } + + if (old_id && old_ref->type == GIT_REF_OID) + *cmp = git_oid_cmp(old_id, &old_ref->target.oid); + + if (old_target && old_ref->type == GIT_REF_SYMBOLIC) + *cmp = git__strcmp(old_target, old_ref->target.symbolic); + +out: + git_reference_free(old_ref); + + return error; +} + +/* + * The git.git comment regarding this, for your viewing pleasure: + * + * Special hack: If a branch is updated directly and HEAD + * points to it (may happen on the remote side of a push + * for example) then logically the HEAD reflog should be + * updated too. + * A generic solution implies reverse symref information, + * but finding all symrefs pointing to the given branch + * would be rather costly for this rare event (the direct + * update of a branch) to be worth it. So let's cheat and + * check with HEAD only which should cover 99% of all usage + * scenarios (even 100% of the default ones). + */ +static int maybe_append_head(refdb_fs_backend *backend, const git_reference *ref, const git_signature *who, const char *message) +{ + int error; + git_oid old_id = {{0}}; + git_reference *tmp = NULL, *head = NULL, *peeled = NULL; + const char *name; + + if (ref->type == GIT_REF_SYMBOLIC) + return 0; + + /* if we can't resolve, we use {0}*40 as old id */ + git_reference_name_to_id(&old_id, backend->repo, ref->name); + + if ((error = git_reference_lookup(&head, backend->repo, GIT_HEAD_FILE)) < 0) + return error; + + if (git_reference_type(head) == GIT_REF_OID) + goto cleanup; + + if ((error = git_reference_lookup(&tmp, backend->repo, GIT_HEAD_FILE)) < 0) + goto cleanup; + + /* Go down the symref chain until we find the branch */ + while (git_reference_type(tmp) == GIT_REF_SYMBOLIC) { + error = git_reference_lookup(&peeled, backend->repo, git_reference_symbolic_target(tmp)); + if (error < 0) + break; + + git_reference_free(tmp); + tmp = peeled; + } + + if (error == GIT_ENOTFOUND) { + error = 0; + name = git_reference_symbolic_target(tmp); + } else if (error < 0) { + goto cleanup; + } else { + name = git_reference_name(tmp); + } + + if (strcmp(name, ref->name)) + goto cleanup; + + error = reflog_append(backend, head, &old_id, git_reference_target(ref), who, message); + +cleanup: + git_reference_free(tmp); + git_reference_free(head); + return error; +} + static int refdb_fs_backend__write( git_refdb_backend *_backend, const git_reference *ref, - int force) + int force, + const git_signature *who, + const char *message, + const git_oid *old_id, + const char *old_target) { refdb_fs_backend *backend = (refdb_fs_backend *)_backend; - int error; + git_filebuf file = GIT_FILEBUF_INIT; + int error = 0; assert(backend); @@ -921,20 +1138,109 @@ if (error < 0) return error; - return loose_write(backend, ref); + /* We need to perform the reflog append and old value check under the ref's lock */ + if ((error = loose_lock(&file, backend, ref->name)) < 0) + return error; + + return refdb_fs_backend__write_tail(_backend, ref, &file, true, who, message, old_id, old_target); +} + +static int refdb_fs_backend__write_tail( + git_refdb_backend *_backend, + const git_reference *ref, + git_filebuf *file, + int update_reflog, + const git_signature *who, + const char *message, + const git_oid *old_id, + const char *old_target) +{ + refdb_fs_backend *backend = (refdb_fs_backend *)_backend; + int error = 0, cmp = 0, should_write; + const char *new_target = NULL; + const git_oid *new_id = NULL; + + if ((error = cmp_old_ref(&cmp, _backend, ref->name, old_id, old_target)) < 0) + goto on_error; + + if (cmp) { + giterr_set(GITERR_REFERENCE, "old reference value does not match"); + error = GIT_EMODIFIED; + goto on_error; + } + + if (ref->type == GIT_REF_SYMBOLIC) + new_target = ref->target.symbolic; + else + new_id = &ref->target.oid; + + error = cmp_old_ref(&cmp, _backend, ref->name, new_id, new_target); + if (error < 0 && error != GIT_ENOTFOUND) + goto on_error; + + /* Don't update if we have the same value */ + if (!error && !cmp) { + error = 0; + goto on_error; /* not really error */ + } + + if (update_reflog) { + if ((error = should_write_reflog(&should_write, backend->repo, ref->name)) < 0) + goto on_error; + + if (should_write) { + if ((error = reflog_append(backend, ref, NULL, NULL, who, message)) < 0) + goto on_error; + if ((error = maybe_append_head(backend, ref, who, message)) < 0) + goto on_error; + } + } + + return loose_commit(file, ref); + +on_error: + git_filebuf_cleanup(file); + return error; } static int refdb_fs_backend__delete( git_refdb_backend *_backend, - const char *ref_name) + const char *ref_name, + const git_oid *old_id, const char *old_target) +{ + refdb_fs_backend *backend = (refdb_fs_backend *)_backend; + git_filebuf file = GIT_FILEBUF_INIT; + int error = 0; + + assert(backend && ref_name); + + if ((error = loose_lock(&file, backend, ref_name)) < 0) + return error; + + return refdb_fs_backend__delete_tail(_backend, &file, ref_name, old_id, old_target); +} + +static int refdb_fs_backend__delete_tail( + git_refdb_backend *_backend, + git_filebuf *file, + const char *ref_name, + const git_oid *old_id, const char *old_target) { refdb_fs_backend *backend = (refdb_fs_backend *)_backend; git_buf loose_path = GIT_BUF_INIT; size_t pack_pos; - int error = 0; + int error = 0, cmp = 0; bool loose_deleted = 0; - assert(backend && ref_name); + error = cmp_old_ref(&cmp, _backend, ref_name, old_id, old_target); + if (error < 0) + goto cleanup; + + if (cmp) { + giterr_set(GITERR_REFERENCE, "old reference value does not match"); + error = GIT_EMODIFIED; + goto cleanup; + } /* If a loose reference exists, remove it from the filesystem */ if (git_buf_joinpath(&loose_path, backend->path, ref_name) < 0) @@ -948,14 +1254,14 @@ git_buf_free(&loose_path); if (error != 0) - return error; + goto cleanup; - if (packed_reload(backend) < 0) - return -1; + if ((error = packed_reload(backend)) < 0) + goto cleanup; /* If a packed reference exists, remove it from the packfile and repack */ - if (git_sortedcache_wlock(backend->refcache) < 0) - return -1; + if ((error = git_sortedcache_wlock(backend->refcache)) < 0) + goto cleanup; if (!(error = git_sortedcache_lookup_index( &pack_pos, backend->refcache, ref_name))) @@ -963,21 +1269,33 @@ git_sortedcache_wunlock(backend->refcache); - if (error == GIT_ENOTFOUND) - return loose_deleted ? 0 : ref_error_notfound(ref_name); + if (error == GIT_ENOTFOUND) { + error = loose_deleted ? 0 : ref_error_notfound(ref_name); + goto cleanup; + } - return packed_write(backend); + error = packed_write(backend); + +cleanup: + git_filebuf_cleanup(file); + + return error; } +static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name); + static int refdb_fs_backend__rename( git_reference **out, git_refdb_backend *_backend, const char *old_name, const char *new_name, - int force) + int force, + const git_signature *who, + const char *message) { refdb_fs_backend *backend = (refdb_fs_backend *)_backend; git_reference *old, *new; + git_filebuf file = GIT_FILEBUF_INIT; int error; assert(backend); @@ -987,7 +1305,7 @@ (error = refdb_fs_backend__lookup(&old, _backend, old_name)) < 0) return error; - if ((error = refdb_fs_backend__delete(_backend, old_name)) < 0) { + if ((error = refdb_fs_backend__delete(_backend, old_name, NULL, NULL)) < 0) { git_reference_free(old); return error; } @@ -998,7 +1316,28 @@ return -1; } - if ((error = loose_write(backend, new)) < 0 || out == NULL) { + if ((error = loose_lock(&file, backend, new->name)) < 0) { + git_reference_free(new); + return error; + } + + /* Try to rename the refog; it's ok if the old doesn't exist */ + error = refdb_reflog_fs__rename(_backend, old_name, new_name); + if (((error == 0) || (error == GIT_ENOTFOUND)) && + ((error = reflog_append(backend, new, NULL, NULL, who, message)) < 0)) { + git_reference_free(new); + git_filebuf_cleanup(&file); + return error; + } + + if (error < 0) { + git_reference_free(new); + git_filebuf_cleanup(&file); + return error; + } + + + if ((error = loose_commit(&file, new)) < 0 || out == NULL) { git_reference_free(new); return error; } @@ -1173,7 +1512,7 @@ return error; if ((fd = p_open(filepath, - O_WRONLY | O_CREAT | O_TRUNC, + O_WRONLY | O_CREAT, GIT_REFLOG_FILE_MODE)) < 0) return -1; @@ -1182,7 +1521,54 @@ GIT_INLINE(int) retrieve_reflog_path(git_buf *path, git_repository *repo, const char *name) { - return git_buf_join_n(path, '/', 3, repo->path_repository, GIT_REFLOG_DIR, name); + return git_buf_join3(path, '/', repo->path_repository, GIT_REFLOG_DIR, name); +} + +static int refdb_reflog_fs__ensure_log(git_refdb_backend *_backend, const char *name) +{ + refdb_fs_backend *backend; + git_repository *repo; + git_buf path = GIT_BUF_INIT; + int error; + + assert(_backend && name); + + backend = (refdb_fs_backend *) _backend; + repo = backend->repo; + + if ((error = retrieve_reflog_path(&path, repo, name)) < 0) + return error; + + error = create_new_reflog_file(git_buf_cstr(&path)); + git_buf_free(&path); + + return error; +} + +static int has_reflog(git_repository *repo, const char *name) +{ + int ret = 0; + git_buf path = GIT_BUF_INIT; + + if (retrieve_reflog_path(&path, repo, name) < 0) + goto cleanup; + + ret = git_path_isfile(git_buf_cstr(&path)); + +cleanup: + git_buf_free(&path); + return ret; +} + +static int refdb_reflog_fs__has_log(git_refdb_backend *_backend, const char *name) +{ + refdb_fs_backend *backend; + + assert(_backend && name); + + backend = (refdb_fs_backend *) _backend; + + return has_reflog(backend->repo, name); } static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name) @@ -1264,34 +1650,53 @@ return git_buf_oom(buf); } +static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char *refname) +{ + git_repository *repo; + git_buf log_path = GIT_BUF_INIT; + int error; + + repo = backend->repo; + + if (!git_path_isvalid(backend->repo, refname, GIT_PATH_REJECT_DEFAULTS)) { + giterr_set(GITERR_INVALID, "Invalid reference name '%s'.", refname); + return GIT_EINVALIDSPEC; + } + + if (retrieve_reflog_path(&log_path, repo, refname) < 0) + return -1; + + if (!git_path_isfile(git_buf_cstr(&log_path))) { + giterr_set(GITERR_INVALID, + "Log file for reference '%s' doesn't exist.", refname); + error = -1; + goto cleanup; + } + + error = git_filebuf_open(file, git_buf_cstr(&log_path), 0, GIT_REFLOG_FILE_MODE); + +cleanup: + git_buf_free(&log_path); + + return error; +} + static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflog) { int error = -1; unsigned int i; git_reflog_entry *entry; - git_repository *repo; refdb_fs_backend *backend; - git_buf log_path = GIT_BUF_INIT; git_buf log = GIT_BUF_INIT; git_filebuf fbuf = GIT_FILEBUF_INIT; assert(_backend && reflog); backend = (refdb_fs_backend *) _backend; - repo = backend->repo; - if (retrieve_reflog_path(&log_path, repo, reflog->ref_name) < 0) + if ((error = lock_reflog(&fbuf, backend, reflog->ref_name)) < 0) return -1; - if (!git_path_isfile(git_buf_cstr(&log_path))) { - giterr_set(GITERR_INVALID, - "Log file for reference '%s' doesn't exist.", reflog->ref_name); - goto cleanup; - } - - if ((error = git_filebuf_open(&fbuf, git_buf_cstr(&log_path), 0, GIT_REFLOG_FILE_MODE)) < 0) - goto cleanup; - git_vector_foreach(&reflog->entries, i, entry) { if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0) goto cleanup; @@ -1308,7 +1713,79 @@ success: git_buf_free(&log); - git_buf_free(&log_path); + + return error; +} + +/* Append to the reflog, must be called under reference lock */ +static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *who, const char *message) +{ + int error, is_symbolic; + git_oid old_id = {{0}}, new_id = {{0}}; + git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT; + git_repository *repo = backend->repo; + + is_symbolic = ref->type == GIT_REF_SYMBOLIC; + + /* "normal" symbolic updates do not write */ + if (is_symbolic && + strcmp(ref->name, GIT_HEAD_FILE) && + !(old && new)) + return 0; + + /* From here on is_symoblic also means that it's HEAD */ + + if (old) { + git_oid_cpy(&old_id, old); + } else { + error = git_reference_name_to_id(&old_id, repo, ref->name); + if (error < 0 && error != GIT_ENOTFOUND) + return error; + } + + if (new) { + git_oid_cpy(&new_id, new); + } else { + if (!is_symbolic) { + git_oid_cpy(&new_id, git_reference_target(ref)); + } else { + error = git_reference_name_to_id(&new_id, repo, git_reference_symbolic_target(ref)); + if (error < 0 && error != GIT_ENOTFOUND) + return error; + /* detaching HEAD does not create an entry */ + if (error == GIT_ENOTFOUND) + return 0; + + giterr_clear(); + } + } + + if ((error = serialize_reflog_entry(&buf, &old_id, &new_id, who, message)) < 0) + goto cleanup; + + if ((error = retrieve_reflog_path(&path, repo, ref->name)) < 0) + goto cleanup; + + if (((error = git_futils_mkpath2file(git_buf_cstr(&path), 0777)) < 0) && + (error != GIT_EEXISTS)) { + goto cleanup; + } + + /* If the new branch matches part of the namespace of a previously deleted branch, + * there maybe an obsolete/unused directory (or directory hierarchy) in the way. + */ + if (git_path_isdir(git_buf_cstr(&path)) && + (git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) { + error = -1; + goto cleanup; + } + + error = git_futils_writebuffer(&buf, git_buf_cstr(&path), O_WRONLY|O_CREAT|O_APPEND, GIT_REFLOG_FILE_MODE); + +cleanup: + git_buf_free(&buf); + git_buf_free(&path); + return error; } @@ -1340,6 +1817,11 @@ if (git_buf_joinpath(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized)) < 0) return -1; + if (!git_path_exists(git_buf_cstr(&old_path))) { + error = GIT_ENOTFOUND; + goto cleanup; + } + /* * Move the reflog to a temporary place. This two-phase renaming is required * in order to cope with funny renaming use cases when one tries to move a reference @@ -1454,6 +1936,10 @@ backend->parent.del = &refdb_fs_backend__delete; backend->parent.rename = &refdb_fs_backend__rename; backend->parent.compress = &refdb_fs_backend__compress; + backend->parent.lock = &refdb_fs_backend__lock; + backend->parent.unlock = &refdb_fs_backend__unlock; + backend->parent.has_log = &refdb_reflog_fs__has_log; + backend->parent.ensure_log = &refdb_reflog_fs__ensure_log; backend->parent.free = &refdb_fs_backend__free; backend->parent.reflog_read = &refdb_reflog_fs__read; backend->parent.reflog_write = &refdb_reflog_fs__write; diff -Nru libgit2-0.20.0/src/refdb.h libgit2-0.22.2/src/refdb.h --- libgit2-0.20.0/src/refdb.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/refdb.h 2015-03-24 16:10:45.000000000 +0000 @@ -33,18 +33,25 @@ git_refdb *db, const char *old_name, const char *new_name, - int force); + int force, + const git_signature *who, + const char *message); int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob); int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter); int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter); void git_refdb_iterator_free(git_reference_iterator *iter); -int git_refdb_write(git_refdb *refdb, git_reference *ref, int force); -int git_refdb_delete(git_refdb *refdb, const char *ref_name); +int git_refdb_write(git_refdb *refdb, git_reference *ref, int force, const git_signature *who, const char *message, const git_oid *old_id, const char *old_target); +int git_refdb_delete(git_refdb *refdb, const char *ref_name, const git_oid *old_id, const char *old_target); int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name); int git_refdb_reflog_write(git_reflog *reflog); +int git_refdb_has_log(git_refdb *db, const char *refname); +int git_refdb_ensure_log(git_refdb *refdb, const char *refname); + +int git_refdb_lock(void **payload, git_refdb *db, const char *refname); +int git_refdb_unlock(git_refdb *db, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message); #endif diff -Nru libgit2-0.20.0/src/reflog.c libgit2-0.22.2/src/reflog.c --- libgit2-0.20.0/src/reflog.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/reflog.c 2015-03-24 16:10:45.000000000 +0000 @@ -82,7 +82,7 @@ entry = git__calloc(1, sizeof(git_reflog_entry)); GITERR_CHECK_ALLOC(entry); - if ((entry->committer = git_signature_dup(committer)) == NULL) + if ((git_signature_dup(&entry->committer, committer)) < 0) goto cleanup; if (msg != NULL) { @@ -148,7 +148,7 @@ return reflog->entries.length; } -const git_reflog_entry * git_reflog_entry_byindex(git_reflog *reflog, size_t idx) +const git_reflog_entry * git_reflog_entry_byindex(const git_reflog *reflog, size_t idx) { assert(reflog); @@ -230,22 +230,3 @@ return 0; } - -int git_reflog_append_to(git_repository *repo, const char *name, const git_oid *id, - const git_signature *committer, const char *msg) -{ - int error; - git_reflog *reflog; - - if ((error = git_reflog_read(&reflog, repo, name)) < 0) - return error; - - if ((error = git_reflog_append(reflog, id, committer, msg)) < 0) - goto cleanup; - - error = git_reflog_write(reflog); - -cleanup: - git_reflog_free(reflog); - return error; -} diff -Nru libgit2-0.20.0/src/refs.c libgit2-0.22.2/src/refs.c --- libgit2-0.20.0/src/refs.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/refs.c 2015-03-24 16:10:45.000000000 +0000 @@ -21,6 +21,8 @@ #include #include #include +#include +#include GIT__USE_STRMAP; @@ -115,7 +117,26 @@ int git_reference_delete(git_reference *ref) { - return git_refdb_delete(ref->db, ref->name); + const git_oid *old_id = NULL; + const char *old_target = NULL; + + if (ref->type == GIT_REF_OID) + old_id = &ref->target.oid; + else + old_target = ref->target.symbolic; + + return git_refdb_delete(ref->db, ref->name, old_id, old_target); +} + +int git_reference_remove(git_repository *repo, const char *name) +{ + git_refdb *db; + int error; + + if ((error = git_repository_refdb__weakptr(&db, repo)) < 0) + return error; + + return git_refdb_delete(db, name, NULL, NULL); } int git_reference_lookup(git_reference **ref_out, @@ -139,8 +160,7 @@ } static int reference_normalize_for_repo( - char *out, - size_t out_size, + git_refname_t out, git_repository *repo, const char *name) { @@ -151,7 +171,7 @@ precompose) flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE; - return git_reference_normalize_name(out, out_size, name, flags); + return git_reference_normalize_name(out, GIT_REFNAME_MAX, name, flags); } int git_reference_lookup_resolved( @@ -160,7 +180,7 @@ const char *name, int max_nesting) { - char scan_name[GIT_REFNAME_MAX]; + git_refname_t scan_name; git_ref_t scan_type; int error = 0, nesting; git_reference *ref = NULL; @@ -177,8 +197,7 @@ scan_type = GIT_REF_SYMBOLIC; - if ((error = reference_normalize_for_repo( - scan_name, sizeof(scan_name), repo, name)) < 0) + if ((error = reference_normalize_for_repo(scan_name, repo, name)) < 0) return error; if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) @@ -328,17 +347,24 @@ const char *name, const git_oid *oid, const char *symbolic, - int force) + int force, + const git_signature *signature, + const char *log_message, + const git_oid *old_id, + const char *old_target) { - char normalized[GIT_REFNAME_MAX]; + git_refname_t normalized; git_refdb *refdb; git_reference *ref = NULL; int error = 0; + assert(repo && name); + assert(symbolic || signature); + if (ref_out) *ref_out = NULL; - error = git_reference__normalize_name_lax(normalized, sizeof(normalized), name); + error = reference_normalize_for_repo(normalized, repo, name); if (error < 0) return error; @@ -347,15 +373,33 @@ return error; if (oid != NULL) { + git_odb *odb; + assert(symbolic == NULL); + + /* Sanity check the reference being created - target must exist. */ + if ((error = git_repository_odb__weakptr(&odb, repo)) < 0) + return error; + + if (!git_odb_exists(odb, oid)) { + giterr_set(GITERR_REFERENCE, + "Target OID for the reference doesn't exist on the repository"); + return -1; + } + ref = git_reference__alloc(normalized, oid, NULL); } else { - ref = git_reference__alloc_symbolic(normalized, symbolic); + git_refname_t normalized_target; + + if ((error = reference_normalize_for_repo(normalized_target, repo, symbolic)) < 0) + return error; + + ref = git_reference__alloc_symbolic(normalized, normalized_target); } GITERR_CHECK_ALLOC(ref); - if ((error = git_refdb_write(refdb, ref, force)) < 0) { + if ((error = git_refdb_write(refdb, ref, force, signature, log_message, old_id, old_target)) < 0) { git_reference_free(ref); return error; } @@ -368,29 +412,88 @@ return 0; } +int git_reference__log_signature(git_signature **out, git_repository *repo) +{ + int error; + git_signature *who; + + if(((error = git_signature_default(&who, repo)) < 0) && + ((error = git_signature_now(&who, "unknown", "unknown")) < 0)) + return error; + + *out = who; + return 0; +} + +int git_reference_create_matching( + git_reference **ref_out, + git_repository *repo, + const char *name, + const git_oid *id, + int force, + const git_oid *old_id, + const git_signature *signature, + const char *log_message) + +{ + int error; + git_signature *who = NULL; + + assert(id); + + if (!signature) { + if ((error = git_reference__log_signature(&who, repo)) < 0) + return error; + else + signature = who; + } + + error = reference__create( + ref_out, repo, name, id, NULL, force, signature, log_message, old_id, NULL); + + git_signature_free(who); + return error; +} + int git_reference_create( git_reference **ref_out, git_repository *repo, const char *name, - const git_oid *oid, - int force) + const git_oid *id, + int force, + const git_signature *signature, + const char *log_message) { - git_odb *odb; - int error = 0; + return git_reference_create_matching(ref_out, repo, name, id, force, NULL, signature, log_message); +} - assert(repo && name && oid); +int git_reference_symbolic_create_matching( + git_reference **ref_out, + git_repository *repo, + const char *name, + const char *target, + int force, + const char *old_target, + const git_signature *signature, + const char *log_message) +{ + int error; + git_signature *who = NULL; - /* Sanity check the reference being created - target must exist. */ - if ((error = git_repository_odb__weakptr(&odb, repo)) < 0) - return error; + assert(target); - if (!git_odb_exists(odb, oid)) { - giterr_set(GITERR_REFERENCE, - "Target OID for the reference doesn't exist on the repository"); - return -1; + if (!signature) { + if ((error = git_reference__log_signature(&who, repo)) < 0) + return error; + else + signature = who; } - return reference__create(ref_out, repo, name, oid, NULL, force); + error = reference__create( + ref_out, repo, name, NULL, target, force, signature, log_message, NULL, old_target); + + git_signature_free(who); + return error; } int git_reference_symbolic_create( @@ -398,95 +501,127 @@ git_repository *repo, const char *name, const char *target, - int force) + int force, + const git_signature *signature, + const char *log_message) { - char normalized[GIT_REFNAME_MAX]; - int error = 0; - - assert(repo && name && target); + return git_reference_symbolic_create_matching(ref_out, repo, name, target, force, NULL, signature, log_message); +} - if ((error = git_reference__normalize_name_lax( - normalized, sizeof(normalized), target)) < 0) - return error; +static int ensure_is_an_updatable_direct_reference(git_reference *ref) +{ + if (ref->type == GIT_REF_OID) + return 0; - return reference__create(ref_out, repo, name, NULL, normalized, force); + giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference"); + return -1; } int git_reference_set_target( git_reference **out, git_reference *ref, - const git_oid *id) + const git_oid *id, + const git_signature *signature, + const char *log_message) { + int error; + git_repository *repo; + assert(out && ref && id); - if (ref->type != GIT_REF_OID) { - giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference"); - return -1; - } + repo = ref->db->repo; + + if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0) + return error; - return git_reference_create(out, ref->db->repo, ref->name, id, 1); + return git_reference_create_matching(out, repo, ref->name, id, 1, &ref->target.oid, signature, log_message); +} + +static int ensure_is_an_updatable_symbolic_reference(git_reference *ref) +{ + if (ref->type == GIT_REF_SYMBOLIC) + return 0; + + giterr_set(GITERR_REFERENCE, "Cannot set symbolic target on a direct reference"); + return -1; } int git_reference_symbolic_set_target( git_reference **out, git_reference *ref, - const char *target) + const char *target, + const git_signature *signature, + const char *log_message) { + int error; + assert(out && ref && target); - if (ref->type != GIT_REF_SYMBOLIC) { - giterr_set(GITERR_REFERENCE, - "Cannot set symbolic target on a direct reference"); - return -1; - } + if ((error = ensure_is_an_updatable_symbolic_reference(ref)) < 0) + return error; - return git_reference_symbolic_create(out, ref->db->repo, ref->name, target, 1); + return git_reference_symbolic_create_matching( + out, ref->db->repo, ref->name, target, 1, ref->target.symbolic, signature, log_message); } -int git_reference_rename( - git_reference **out, - git_reference *ref, - const char *new_name, - int force) +static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force, + const git_signature *signature, const char *message) { - unsigned int normalization_flags; - char normalized[GIT_REFNAME_MAX]; + git_refname_t normalized; bool should_head_be_updated = false; int error = 0; - int reference_has_log; - normalization_flags = ref->type == GIT_REF_SYMBOLIC ? - GIT_REF_FORMAT_ALLOW_ONELEVEL : GIT_REF_FORMAT_NORMAL; + assert(ref && new_name && signature); - if ((error = git_reference_normalize_name( - normalized, sizeof(normalized), new_name, normalization_flags)) < 0) + if ((error = reference_normalize_for_repo( + normalized, git_reference_owner(ref), new_name)) < 0) return error; + /* Check if we have to update HEAD. */ if ((error = git_branch_is_head(ref)) < 0) return error; should_head_be_updated = (error > 0); - if ((error = git_refdb_rename(out, ref->db, ref->name, new_name, force)) < 0) + if ((error = git_refdb_rename(out, ref->db, ref->name, normalized, force, signature, message)) < 0) return error; - /* Update HEAD it was poiting to the reference being renamed. */ + /* Update HEAD it was pointing to the reference being renamed */ if (should_head_be_updated && - (error = git_repository_set_head(ref->db->repo, new_name)) < 0) { + (error = git_repository_set_head(ref->db->repo, normalized, signature, message)) < 0) { giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference"); return error; } - /* Rename the reflog file, if it exists. */ - reference_has_log = git_reference_has_log(ref); - if (reference_has_log < 0) - return reference_has_log; + return 0; +} + + +int git_reference_rename( + git_reference **out, + git_reference *ref, + const char *new_name, + int force, + const git_signature *signature, + const char *log_message) +{ + git_signature *who = (git_signature*)signature; + int error; - if (reference_has_log && (error = git_reflog_rename(git_reference_owner(ref), git_reference_name(ref), new_name)) < 0) + /* Should we return an error if there is no default? */ + if (!who && + ((error = git_signature_default(&who, ref->db->repo)) < 0) && + ((error = git_signature_now(&who, "unknown", "unknown")) < 0)) { return error; + } - return 0; + error = reference__rename(out, ref, new_name, force, who, log_message); + + if (!signature) + git_signature_free(who); + + return error; } int git_reference_resolve(git_reference **ref_out, const git_reference *ref) @@ -513,20 +648,19 @@ git_reference *ref; int error; - if (git_reference_iterator_new(&iter, repo) < 0) - return -1; + if ((error = git_reference_iterator_new(&iter, repo)) < 0) + return error; - while ((error = git_reference_next(&ref, iter)) == 0) { - if (callback(ref, payload)) { - error = GIT_EUSER; - goto out; + while (!(error = git_reference_next(&ref, iter))) { + if ((error = callback(ref, payload)) != 0) { + giterr_set_after_callback(error); + break; } } if (error == GIT_ITEROVER) error = 0; -out: git_reference_iterator_free(iter); return error; } @@ -540,20 +674,19 @@ const char *refname; int error; - if (git_reference_iterator_new(&iter, repo) < 0) - return -1; + if ((error = git_reference_iterator_new(&iter, repo)) < 0) + return error; - while ((error = git_reference_next_name(&refname, iter)) == 0) { - if (callback(refname, payload)) { - error = GIT_EUSER; - goto out; + while (!(error = git_reference_next_name(&refname, iter))) { + if ((error = callback(refname, payload)) != 0) { + giterr_set_after_callback(error); + break; } } if (error == GIT_ITEROVER) error = 0; -out: git_reference_iterator_free(iter); return error; } @@ -568,20 +701,19 @@ const char *refname; int error; - if (git_reference_iterator_glob_new(&iter, repo, glob) < 0) - return -1; + if ((error = git_reference_iterator_glob_new(&iter, repo, glob)) < 0) + return error; - while ((error = git_reference_next_name(&refname, iter)) == 0) { - if (callback(refname, payload)) { - error = GIT_EUSER; - goto out; + while (!(error = git_reference_next_name(&refname, iter))) { + if ((error = callback(refname, payload)) != 0) { + giterr_set_after_callback(error); + break; } } if (error == GIT_ITEROVER) error = 0; -out: git_reference_iterator_free(iter); return error; } @@ -619,12 +751,17 @@ void git_reference_iterator_free(git_reference_iterator *iter) { + if (iter == NULL) + return; + git_refdb_iterator_free(iter); } static int cb__reflist_add(const char *ref, void *data) { - return git_vector_insert((git_vector *)data, git__strdup(ref)); + char *name = git__strdup(ref); + GITERR_CHECK_ALLOC(name); + return git_vector_insert((git_vector *)data, name); } int git_reference_list( @@ -647,8 +784,8 @@ return -1; } - array->strings = (char **)ref_list.contents; - array->count = ref_list.length; + array->strings = (char **)git_vector_detach(&array->count, NULL, &ref_list); + return 0; } @@ -875,20 +1012,11 @@ return error; } -int git_reference__normalize_name_lax( - char *buffer_out, - size_t out_size, - const char *name) -{ - return git_reference_normalize_name( - buffer_out, - out_size, - name, - GIT_REF_FORMAT_ALLOW_ONELEVEL); -} #define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC) -int git_reference_cmp(git_reference *ref1, git_reference *ref2) +int git_reference_cmp( + const git_reference *ref1, + const git_reference *ref2) { git_ref_t type1, type2; assert(ref1 && ref2); @@ -910,7 +1038,9 @@ git_repository *repo, const char *ref_name, const git_oid *oid, - int nesting) + int nesting, + const git_signature *signature, + const char *log_message) { git_reference *ref; int error = 0; @@ -925,7 +1055,7 @@ /* If we haven't found the reference at all, create a new reference. */ if (error == GIT_ENOTFOUND) { giterr_clear(); - return git_reference_create(NULL, repo, ref_name, oid, 0); + return git_reference_create(NULL, repo, ref_name, oid, 0, signature, log_message); } if (error < 0) @@ -934,11 +1064,13 @@ /* If the ref is a symbolic reference, follow its target. */ if (git_reference_type(ref) == GIT_REF_SYMBOLIC) { error = reference__update_terminal(repo, git_reference_symbolic_target(ref), oid, - nesting+1); + nesting+1, signature, log_message); git_reference_free(ref); } else { + /* If we're not moving the target, don't recreate the ref */ + if (0 != git_oid_cmp(git_reference_target(ref), oid)) + error = git_reference_create(NULL, repo, ref_name, oid, 1, signature, log_message); git_reference_free(ref); - error = git_reference_create(NULL, repo, ref_name, oid, 1); } return error; @@ -952,27 +1084,71 @@ int git_reference__update_terminal( git_repository *repo, const char *ref_name, - const git_oid *oid) + const git_oid *oid, + const git_signature *signature, + const char *log_message) { - return reference__update_terminal(repo, ref_name, oid, 0); + return reference__update_terminal(repo, ref_name, oid, 0, signature, log_message); } -int git_reference_has_log( - git_reference *ref) +int git_reference__update_for_commit( + git_repository *repo, + git_reference *ref, + const char *ref_name, + const git_oid *id, + const git_signature *committer, + const char *operation) +{ + git_reference *ref_new = NULL; + git_commit *commit = NULL; + git_buf reflog_msg = GIT_BUF_INIT; + int error; + + if ((error = git_commit_lookup(&commit, repo, id)) < 0 || + (error = git_buf_printf(&reflog_msg, "%s%s: %s", + operation ? operation : "commit", + git_commit_parentcount(commit) == 0 ? " (initial)" : "", + git_commit_summary(commit))) < 0) + goto done; + + if (ref) + error = git_reference_set_target( + &ref_new, ref, id, committer, git_buf_cstr(&reflog_msg)); + else + error = git_reference__update_terminal( + repo, ref_name, id, committer, git_buf_cstr(&reflog_msg)); + +done: + git_reference_free(ref_new); + git_buf_free(&reflog_msg); + git_commit_free(commit); + return error; +} + +int git_reference_has_log(git_repository *repo, const char *refname) { - git_buf path = GIT_BUF_INIT; - int result; + int error; + git_refdb *refdb; - assert(ref); + assert(repo && refname); - if (git_buf_join_n(&path, '/', 3, ref->db->repo->path_repository, - GIT_REFLOG_DIR, ref->name) < 0) - return -1; + if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) + return error; + + return git_refdb_has_log(refdb, refname); +} - result = git_path_isfile(git_buf_cstr(&path)); - git_buf_free(&path); +int git_reference_ensure_log(git_repository *repo, const char *refname) +{ + int error; + git_refdb *refdb; - return result; + assert(repo && refname); + + if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0) + return error; + + return git_refdb_ensure_log(refdb, refname); } int git_reference__is_branch(const char *ref_name) @@ -980,7 +1156,7 @@ return git__prefixcmp(ref_name, GIT_REFS_HEADS_DIR) == 0; } -int git_reference_is_branch(git_reference *ref) +int git_reference_is_branch(const git_reference *ref) { assert(ref); return git_reference__is_branch(ref->name); @@ -991,7 +1167,7 @@ return git__prefixcmp(ref_name, GIT_REFS_REMOTES_DIR) == 0; } -int git_reference_is_remote(git_reference *ref) +int git_reference_is_remote(const git_reference *ref) { assert(ref); return git_reference__is_remote(ref->name); @@ -1002,12 +1178,23 @@ return git__prefixcmp(ref_name, GIT_REFS_TAGS_DIR) == 0; } -int git_reference_is_tag(git_reference *ref) +int git_reference_is_tag(const git_reference *ref) { assert(ref); return git_reference__is_tag(ref->name); } +int git_reference__is_note(const char *ref_name) +{ + return git__prefixcmp(ref_name, GIT_REFS_NOTES_DIR) == 0; +} + +int git_reference_is_note(const git_reference *ref) +{ + assert(ref); + return git_reference__is_note(ref->name); +} + static int peel_error(int error, git_reference *ref, const char* msg) { giterr_set( @@ -1076,7 +1263,7 @@ return git_reference__is_valid_name(refname, GIT_REF_FORMAT_ALLOW_ONELEVEL); } -const char *git_reference_shorthand(git_reference *ref) +const char *git_reference_shorthand(const git_reference *ref) { const char *name = ref->name; diff -Nru libgit2-0.20.0/src/refs.h libgit2-0.22.2/src/refs.h --- libgit2-0.20.0/src/refs.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/refs.h 2015-03-24 16:10:45.000000000 +0000 @@ -19,6 +19,7 @@ #define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/" #define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/" #define GIT_REFS_REMOTES_DIR GIT_REFS_DIR "remotes/" +#define GIT_REFS_NOTES_DIR GIT_REFS_DIR "notes/" #define GIT_REFS_DIR_MODE 0777 #define GIT_REFS_FILE_MODE 0666 @@ -34,7 +35,7 @@ #define GIT_FETCH_HEAD_FILE "FETCH_HEAD" #define GIT_MERGE_HEAD_FILE "MERGE_HEAD" #define GIT_REVERT_HEAD_FILE "REVERT_HEAD" -#define GIT_CHERRY_PICK_HEAD_FILE "CHERRY_PICK_HEAD" +#define GIT_CHERRYPICK_HEAD_FILE "CHERRY_PICK_HEAD" #define GIT_BISECT_LOG_FILE "BISECT_LOG" #define GIT_REBASE_MERGE_DIR "rebase-merge/" #define GIT_REBASE_MERGE_INTERACTIVE_FILE GIT_REBASE_MERGE_DIR "interactive" @@ -50,6 +51,8 @@ #define GIT_REFNAME_MAX 1024 +typedef char git_refname_t[GIT_REFNAME_MAX]; + struct git_reference { git_refdb *db; git_ref_t type; @@ -60,14 +63,13 @@ } target; git_oid peel; - char name[0]; + char name[GIT_FLEX_ARRAY]; }; git_reference *git_reference__set_name(git_reference *ref, const char *name); -int git_reference__normalize_name_lax(char *buffer_out, size_t out_size, const char *name); int git_reference__normalize_name(git_buf *buf, const char *name, unsigned int flags); -int git_reference__update_terminal(git_repository *repo, const char *ref_name, const git_oid *oid); +int git_reference__update_terminal(git_repository *repo, const char *ref_name, const git_oid *oid, const git_signature *signature, const char *log_message); int git_reference__is_valid_name(const char *refname, unsigned int flags); int git_reference__is_branch(const char *ref_name); int git_reference__is_remote(const char *ref_name); @@ -96,4 +98,15 @@ const char *name, int max_deref); +int git_reference__log_signature(git_signature **out, git_repository *repo); + +/** Update a reference after a commit. */ +int git_reference__update_for_commit( + git_repository *repo, + git_reference *ref, + const char *ref_name, + const git_oid *id, + const git_signature *committer, + const char *operation); + #endif diff -Nru libgit2-0.20.0/src/refspec.c libgit2-0.22.2/src/refspec.c --- libgit2-0.20.0/src/refspec.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/refspec.c 2015-03-24 16:10:45.000000000 +0000 @@ -119,6 +119,12 @@ if (!git_reference__is_valid_name(refspec->dst, flags)) goto invalid; } + + /* if the RHS is empty, then it's a copy of the LHS */ + if (!refspec->dst) { + refspec->dst = git__strdup(refspec->src); + GITERR_CHECK_ALLOC(refspec->dst); + } } refspec->string = git__strdup(input); @@ -178,88 +184,78 @@ return (p_fnmatch(refspec->dst, refname, 0) == 0); } -static int refspec_transform_internal(char *out, size_t outlen, const char *from, const char *to, const char *name) +static int refspec_transform( + git_buf *out, const char *from, const char *to, const char *name) { - size_t baselen, namelen; + const char *from_star, *to_star; + const char *name_slash, *from_slash; + size_t replacement_len, star_offset; - baselen = strlen(to); - if (outlen <= baselen) { - giterr_set(GITERR_INVALID, "Reference name too long"); - return GIT_EBUFS; - } + git_buf_sanitize(out); + git_buf_clear(out); /* - * No '*' at the end means that it's mapped to one specific local - * branch, so no actual transformation is needed. + * There are two parts to each side of a refspec, the bit + * before the star and the bit after it. The star can be in + * the middle of the pattern, so we need to look at each bit + * individually. */ - if (to[baselen - 1] != '*') { - memcpy(out, to, baselen + 1); /* include '\0' */ - return 0; - } + from_star = strchr(from, '*'); + to_star = strchr(to, '*'); - /* There's a '*' at the end, so remove its length */ - baselen--; + assert(from_star && to_star); - /* skip the prefix, -1 is for the '*' */ - name += strlen(from) - 1; + /* star offset, both in 'from' and in 'name' */ + star_offset = from_star - from; - namelen = strlen(name); + /* the first half is copied over */ + git_buf_put(out, to, to_star - to); - if (outlen <= baselen + namelen) { - giterr_set(GITERR_INVALID, "Reference name too long"); - return GIT_EBUFS; - } - - memcpy(out, to, baselen); - memcpy(out + baselen, name, namelen + 1); + /* then we copy over the replacement, from the star's offset to the next slash in 'name' */ + name_slash = strchr(name + star_offset, '/'); + if (!name_slash) + name_slash = strrchr(name, '\0'); - return 0; -} + /* if there is no slash after the star in 'from', we want to copy everything over */ + from_slash = strchr(from + star_offset, '/'); + if (!from_slash) + name_slash = strrchr(name, '\0'); -int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name) -{ - return refspec_transform_internal(out, outlen, spec->src, spec->dst, name); -} + replacement_len = (name_slash - name) - star_offset; + git_buf_put(out, name + star_offset, replacement_len); -int git_refspec_rtransform(char *out, size_t outlen, const git_refspec *spec, const char *name) -{ - return refspec_transform_internal(out, outlen, spec->dst, spec->src, name); + return git_buf_puts(out, to_star + 1); } -static int refspec_transform( - git_buf *out, const char *from, const char *to, const char *name) +int git_refspec_transform(git_buf *out, const git_refspec *spec, const char *name) { - size_t to_len = to ? strlen(to) : 0; - size_t from_len = from ? strlen(from) : 0; - size_t name_len = name ? strlen(name) : 0; + assert(out && spec && name); + git_buf_sanitize(out); - if (git_buf_set(out, to, to_len) < 0) + if (!git_refspec_src_matches(spec, name)) { + giterr_set(GITERR_INVALID, "ref '%s' doesn't match the source", name); return -1; - - if (to_len > 0) { - /* No '*' at the end of 'to' means that refspec is mapped to one - * specific branch, so no actual transformation is needed. - */ - if (out->ptr[to_len - 1] != '*') - return 0; - git_buf_shorten(out, 1); /* remove trailing '*' copied from 'to' */ } - if (from_len > 0) /* ignore trailing '*' from 'from' */ - from_len--; - if (from_len > name_len) - from_len = name_len; - - return git_buf_put(out, name + from_len, name_len - from_len); -} + if (!spec->pattern) + return git_buf_puts(out, spec->dst); -int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name) -{ return refspec_transform(out, spec->src, spec->dst, name); } -int git_refspec_transform_l(git_buf *out, const git_refspec *spec, const char *name) +int git_refspec_rtransform(git_buf *out, const git_refspec *spec, const char *name) { + assert(out && spec && name); + git_buf_sanitize(out); + + if (!git_refspec_dst_matches(spec, name)) { + giterr_set(GITERR_INVALID, "ref '%s' doesn't match the destination", name); + return -1; + } + + if (!spec->pattern) + return git_buf_puts(out, spec->src); + return refspec_transform(out, spec->dst, spec->src, name); } diff -Nru libgit2-0.20.0/src/refspec.h libgit2-0.22.2/src/refspec.h --- libgit2-0.20.0/src/refspec.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/refspec.h 2015-03-24 16:10:45.000000000 +0000 @@ -23,7 +23,6 @@ #define GIT_REFSPEC_TAGS "refs/tags/*:refs/tags/*" -int git_refspec_parse(struct git_refspec *refspec, const char *str); int git_refspec__parse( struct git_refspec *refspec, const char *str, @@ -31,28 +30,6 @@ void git_refspec__free(git_refspec *refspec); -/** - * Transform a reference to its target following the refspec's rules, - * and writes the results into a git_buf. - * - * @param out where to store the target name - * @param spec the refspec - * @param name the name of the reference to transform - * @return 0 or error if buffer allocation fails - */ -int git_refspec_transform_r(git_buf *out, const git_refspec *spec, const char *name); - -/** - * Transform a reference from its target following the refspec's rules, - * and writes the results into a git_buf. - * - * @param out where to store the source name - * @param spec the refspec - * @param name the name of the reference to transform - * @return 0 or error if buffer allocation fails - */ -int git_refspec_transform_l(git_buf *out, const git_refspec *spec, const char *name); - int git_refspec__serialize(git_buf *out, const git_refspec *refspec); /** diff -Nru libgit2-0.20.0/src/remote.c libgit2-0.22.2/src/remote.c --- libgit2-0.20.0/src/remote.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/remote.c 2015-03-24 16:10:45.000000000 +0000 @@ -18,10 +18,12 @@ #include "refs.h" #include "refspec.h" #include "fetchhead.h" +#include "push.h" static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs); +static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name); -static int add_refspec(git_remote *remote, const char *string, bool is_fetch) +static int add_refspec_to(git_vector *vector, const char *string, bool is_fetch) { git_refspec *spec; @@ -34,7 +36,7 @@ } spec->push = !is_fetch; - if (git_vector_insert(&remote->refspecs, spec) < 0) { + if (git_vector_insert(vector, spec) < 0) { git_refspec__free(spec); git__free(spec); return -1; @@ -43,9 +45,14 @@ return 0; } +static int add_refspec(git_remote *remote, const char *string, bool is_fetch) +{ + return add_refspec_to(&remote->refspecs, string, is_fetch); +} + static int download_tags_value(git_remote *remote, git_config *cfg) { - const char *val; + const git_config_entry *ce; git_buf buf = GIT_BUF_INIT; int error; @@ -53,16 +60,14 @@ if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0) return -1; - error = git_config_get_string(&val, cfg, git_buf_cstr(&buf)); + error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false); git_buf_free(&buf); - if (!error && !strcmp(val, "--no-tags")) - remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; - else if (!error && !strcmp(val, "--tags")) - remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL; - if (error == GIT_ENOTFOUND) { - giterr_clear(); - error = 0; + if (!error && ce && ce->value) { + if (!strcmp(ce->value, "--no-tags")) + remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; + else if (!strcmp(ce->value, "--tags")) + remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL; } return error; @@ -75,13 +80,15 @@ if (!git_remote_is_valid_name(name)) { giterr_set( GITERR_CONFIG, - "'%s' is not a valid remote name.", name); + "'%s' is not a valid remote name.", name ? name : "(null)"); error = GIT_EINVALIDSPEC; } return error; } +#if 0 +/* We could export this as a helper */ static int get_check_cert(int *out, git_repository *repo) { git_config *cfg; @@ -104,19 +111,36 @@ if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) return error; - if ((error = git_config_get_bool(out, cfg, "http.sslVerify")) == 0) - return 0; - else if (error != GIT_ENOTFOUND) - return error; - - giterr_clear(); + *out = git_config__get_bool_force(cfg, "http.sslverify", 1); return 0; } +#endif + +static int canonicalize_url(git_buf *out, const char *in) +{ +#ifdef GIT_WIN32 + const char *c; + + /* Given a UNC path like \\server\path, we need to convert this + * to //server/path for compatibility with core git. + */ + if (in[0] == '\\' && in[1] == '\\' && + (git__isalpha(in[2]) || git__isdigit(in[2]))) { + for (c = in; *c; c++) + git_buf_putc(out, *c == '\\' ? '/' : *c); + + return git_buf_oom(out) ? -1 : 0; + } +#endif + + return git_buf_puts(out, in); +} static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch) { git_remote *remote; - git_buf fetchbuf = GIT_BUF_INIT; + git_config *config = NULL; + git_buf canonical_url = GIT_BUF_INIT, fetchbuf = GIT_BUF_INIT; int error = -1; /* name is optional */ @@ -128,14 +152,11 @@ remote->repo = repo; remote->update_fetchhead = 1; - if (get_check_cert(&remote->check_cert, repo) < 0) - goto on_error; - - if (git_vector_init(&remote->refs, 32, NULL) < 0) + if (git_vector_init(&remote->refs, 32, NULL) < 0 || + canonicalize_url(&canonical_url, url) < 0) goto on_error; - remote->url = git__strdup(url); - GITERR_CHECK_ALLOC(remote->url); + remote->url = git_buf_detach(&canonical_url); if (name != NULL) { remote->name = git__strdup(name); @@ -145,6 +166,16 @@ if (fetch != NULL) { if (add_refspec(remote, fetch, true) < 0) goto on_error; + + if ((error = git_repository_config_snapshot(&config, repo)) < 0) + goto on_error; + + if (lookup_remote_prune_config(remote, config, name) < 0) + goto on_error; + + /* Move the data over to where the matching functions can find them */ + if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0) + goto on_error; } if (!name) @@ -152,12 +183,15 @@ remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE; *out = remote; - git_buf_free(&fetchbuf); - return 0; + error = 0; on_error: - git_remote_free(remote); + if (error) + git_remote_free(remote); + + git_config_free(config); git_buf_free(&fetchbuf); + git_buf_free(&canonical_url); return error; } @@ -166,7 +200,7 @@ int error; git_remote *remote; - error = git_remote_load(&remote, repo, name); + error = git_remote_lookup(&remote, repo, name); if (error == GIT_ENOTFOUND) return 0; @@ -187,34 +221,15 @@ int git_remote_create(git_remote **out, git_repository *repo, const char *name, const char *url) { git_buf buf = GIT_BUF_INIT; - git_remote *remote = NULL; int error; - if ((error = ensure_remote_name_is_valid(name)) < 0) - return error; - - if ((error = ensure_remote_doesnot_exist(repo, name)) < 0) - return error; - if (git_buf_printf(&buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0) return -1; - if (create_internal(&remote, repo, name, url, git_buf_cstr(&buf)) < 0) - goto on_error; - + error = git_remote_create_with_fetchspec(out, repo, name, url, git_buf_cstr(&buf)); git_buf_free(&buf); - if (git_remote_save(remote) < 0) - goto on_error; - - *out = remote; - - return 0; - -on_error: - git_buf_free(&buf); - git_remote_free(remote); - return -1; + return error; } int git_remote_create_with_fetchspec(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch) @@ -243,7 +258,7 @@ return -1; } -int git_remote_create_inmemory(git_remote **out, git_repository *repo, const char *fetch, const char *url) +int git_remote_create_anonymous(git_remote **out, git_repository *repo, const char *url, const char *fetch) { int error; git_remote *remote; @@ -255,6 +270,64 @@ return 0; } +int git_remote_dup(git_remote **dest, git_remote *source) +{ + int error = 0; + git_strarray refspecs = { 0 }; + git_remote *remote = git__calloc(1, sizeof(git_remote)); + GITERR_CHECK_ALLOC(remote); + + if (source->name != NULL) { + remote->name = git__strdup(source->name); + GITERR_CHECK_ALLOC(remote->name); + } + + if (source->url != NULL) { + remote->url = git__strdup(source->url); + GITERR_CHECK_ALLOC(remote->url); + } + + if (source->pushurl != NULL) { + remote->pushurl = git__strdup(source->pushurl); + GITERR_CHECK_ALLOC(remote->pushurl); + } + + remote->transport_cb = source->transport_cb; + remote->transport_cb_payload = source->transport_cb_payload; + remote->repo = source->repo; + remote->download_tags = source->download_tags; + remote->update_fetchhead = source->update_fetchhead; + remote->prune_refs = source->prune_refs; + + if (git_vector_init(&remote->refs, 32, NULL) < 0 || + git_vector_init(&remote->refspecs, 2, NULL) < 0 || + git_vector_init(&remote->active_refspecs, 2, NULL) < 0) { + error = -1; + goto cleanup; + } + + if ((error = git_remote_get_fetch_refspecs(&refspecs, source)) < 0 || + (error = git_remote_set_fetch_refspecs(remote, &refspecs)) < 0) + goto cleanup; + + git_strarray_free(&refspecs); + + if ((error = git_remote_get_push_refspecs(&refspecs, source)) < 0 || + (error = git_remote_set_push_refspecs(remote, &refspecs)) < 0) + goto cleanup; + + *dest = remote; + +cleanup: + + git_strarray_free(&refspecs); + + if (error < 0) + git__free(remote); + + return error; +} + struct refspec_cb_data { git_remote *remote; int fetch; @@ -262,8 +335,7 @@ static int refspec_cb(const git_config_entry *entry, void *payload) { - const struct refspec_cb_data *data = (struct refspec_cb_data *)payload; - + struct refspec_cb_data *data = (struct refspec_cb_data *)payload; return add_refspec(data->remote, entry->value, data->fetch); } @@ -290,20 +362,17 @@ error = 0; } - if (error < 0) - error = -1; - return error; } -int git_remote_load(git_remote **out, git_repository *repo, const char *name) +int git_remote_lookup(git_remote **out, git_repository *repo, const char *name) { git_remote *remote; git_buf buf = GIT_BUF_INIT; const char *val; int error = 0; git_config *config; - struct refspec_cb_data data; + struct refspec_cb_data data = { NULL }; bool optional_setting_found = false, found; assert(out && repo && name); @@ -311,8 +380,8 @@ if ((error = ensure_remote_name_is_valid(name)) < 0) return error; - if (git_repository_config__weakptr(&config, repo) < 0) - return -1; + if ((error = git_repository_config_snapshot(&config, repo)) < 0) + return error; remote = git__malloc(sizeof(git_remote)); GITERR_CHECK_ALLOC(remote); @@ -322,20 +391,16 @@ remote->name = git__strdup(name); GITERR_CHECK_ALLOC(remote->name); - if ((error = get_check_cert(&remote->check_cert, repo)) < 0) - goto cleanup; - - if ((git_vector_init(&remote->refs, 32, NULL) < 0) || - (git_vector_init(&remote->refspecs, 2, NULL) < 0) || - (git_vector_init(&remote->active_refspecs, 2, NULL) < 0)) { + if (git_vector_init(&remote->refs, 32, NULL) < 0 || + git_vector_init(&remote->refspecs, 2, NULL) < 0 || + git_vector_init(&remote->passive_refspecs, 2, NULL) < 0 || + git_vector_init(&remote->active_refspecs, 2, NULL) < 0) { error = -1; goto cleanup; } - if (git_buf_printf(&buf, "remote.%s.url", name) < 0) { - error = -1; + if ((error = git_buf_printf(&buf, "remote.%s.url", name)) < 0) goto cleanup; - } if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0) goto cleanup; @@ -360,6 +425,7 @@ if (!optional_setting_found) { error = GIT_ENOTFOUND; + giterr_set(GITERR_CONFIG, "Remote '%s' does not exist.", name); goto cleanup; } @@ -370,6 +436,7 @@ data.remote = remote; data.fetch = true; + git_buf_clear(&buf); git_buf_printf(&buf, "remote.%s.fetch", name); @@ -386,6 +453,9 @@ if (download_tags_value(remote, config) < 0) goto cleanup; + if ((error = lookup_remote_prune_config(remote, config, name)) < 0) + goto cleanup; + /* Move the data over to where the matching functions can find them */ if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0) goto cleanup; @@ -393,6 +463,7 @@ *out = remote; cleanup: + git_config_free(config); git_buf_free(&buf); if (error < 0) @@ -401,6 +472,30 @@ return error; } +static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name) +{ + git_buf buf = GIT_BUF_INIT; + int error = 0; + + git_buf_printf(&buf, "remote.%s.prune", name); + + if ((error = git_config_get_bool(&remote->prune_refs, config, git_buf_cstr(&buf))) < 0) { + if (error == GIT_ENOTFOUND) { + giterr_clear(); + + if ((error = git_config_get_bool(&remote->prune_refs, config, "fetch.prune")) < 0) { + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = 0; + } + } + } + } + + git_buf_free(&buf); + return error; +} + static int update_config_refspec(const git_remote *remote, git_config *config, int direction) { git_buf name = GIT_BUF_INIT; @@ -451,57 +546,46 @@ int git_remote_save(const git_remote *remote) { int error; - git_config *config; + git_config *cfg; const char *tagopt = NULL; git_buf buf = GIT_BUF_INIT; + const git_config_entry *existing; assert(remote); if (!remote->name) { - giterr_set(GITERR_INVALID, "Can't save an in-memory remote."); + giterr_set(GITERR_INVALID, "Can't save an anonymous remote."); return GIT_EINVALIDSPEC; } if ((error = ensure_remote_name_is_valid(remote->name)) < 0) return error; - if (git_repository_config__weakptr(&config, remote->repo) < 0) - return -1; + if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0) + return error; - if (git_buf_printf(&buf, "remote.%s.url", remote->name) < 0) - return -1; + if ((error = git_buf_printf(&buf, "remote.%s.url", remote->name)) < 0) + return error; - if (git_config_set_string(config, git_buf_cstr(&buf), remote->url) < 0) { - git_buf_free(&buf); - return -1; - } + /* after this point, buffer is allocated so end with cleanup */ + + if ((error = git_config_set_string( + cfg, git_buf_cstr(&buf), remote->url)) < 0) + goto cleanup; git_buf_clear(&buf); - if (git_buf_printf(&buf, "remote.%s.pushurl", remote->name) < 0) - return -1; + if ((error = git_buf_printf(&buf, "remote.%s.pushurl", remote->name)) < 0) + goto cleanup; - if (remote->pushurl) { - if (git_config_set_string(config, git_buf_cstr(&buf), remote->pushurl) < 0) { - git_buf_free(&buf); - return -1; - } - } else { - int error = git_config_delete_entry(config, git_buf_cstr(&buf)); - if (error == GIT_ENOTFOUND) { - error = 0; - giterr_clear(); - } - if (error < 0) { - git_buf_free(&buf); - return -1; - } - } + if ((error = git_config__update_entry( + cfg, git_buf_cstr(&buf), remote->pushurl, true, false)) < 0) + goto cleanup; - if (update_config_refspec(remote, config, GIT_DIRECTION_FETCH) < 0) - goto on_error; + if ((error = update_config_refspec(remote, cfg, GIT_DIRECTION_FETCH)) < 0) + goto cleanup; - if (update_config_refspec(remote, config, GIT_DIRECTION_PUSH) < 0) - goto on_error; + if ((error = update_config_refspec(remote, cfg, GIT_DIRECTION_PUSH)) < 0) + goto cleanup; /* * What action to take depends on the old and new values. This @@ -517,31 +601,26 @@ */ git_buf_clear(&buf); - if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0) - goto on_error; - - error = git_config_get_string(&tagopt, config, git_buf_cstr(&buf)); - if (error < 0 && error != GIT_ENOTFOUND) - goto on_error; + if ((error = git_buf_printf(&buf, "remote.%s.tagopt", remote->name)) < 0) + goto cleanup; - if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { - if (git_config_set_string(config, git_buf_cstr(&buf), "--tags") < 0) - goto on_error; - } else if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_NONE) { - if (git_config_set_string(config, git_buf_cstr(&buf), "--no-tags") < 0) - goto on_error; - } else if (tagopt) { - if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0) - goto on_error; - } + if ((error = git_config__lookup_entry( + &existing, cfg, git_buf_cstr(&buf), false)) < 0) + goto cleanup; - git_buf_free(&buf); + if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) + tagopt = "--tags"; + else if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_NONE) + tagopt = "--no-tags"; + else if (existing != NULL) + tagopt = NULL; - return 0; + error = git_config__update_entry( + cfg, git_buf_cstr(&buf), tagopt, true, false); -on_error: +cleanup: git_buf_free(&buf); - return -1; + return error; } const char *git_remote_name(const git_remote *remote) @@ -629,19 +708,22 @@ return -1; } - /* A transport could have been supplied in advance with - * git_remote_set_transport */ + /* If we don't have a transport object yet, and the caller specified a + * custom transport factory, use that */ + if (!t && remote->transport_cb && + (error = remote->transport_cb(&t, remote, remote->transport_cb_payload)) < 0) + return error; + + /* If we still don't have a transport, then use the global + * transport registrations which map URI schemes to transport factories */ if (!t && (error = git_transport_new(&t, remote, url)) < 0) return error; if (t->set_callbacks && - (error = t->set_callbacks(t, remote->callbacks.progress, NULL, remote->callbacks.payload)) < 0) + (error = t->set_callbacks(t, remote->callbacks.sideband_progress, NULL, remote->callbacks.certificate_check, remote->callbacks.payload)) < 0) goto on_error; - if (!remote->check_cert) - flags |= GIT_TRANSPORTFLAGS_NO_CHECK_CERT; - - if ((error = t->connect(t, url, remote->callbacks.credentials, remote->callbacks.payload, direction, flags)) < 0) + if ((error = t->connect(t, url, remote->callbacks.credentials, remote->callbacks.payload, direction, flags)) != 0) goto on_error; remote->transport = t; @@ -661,13 +743,19 @@ { assert(remote); + if (!remote->transport) { + giterr_set(GITERR_NET, "this remote has never connected"); + return -1; + } + return remote->transport->ls(out, size, remote->transport); } int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url) { git_config *cfg; - const char *val; + const git_config_entry *ce; + const char *val = NULL; int error; assert(remote); @@ -684,44 +772,39 @@ * to least specific. */ /* remote..proxy config setting */ - if (remote->name && 0 != *(remote->name)) { + if (remote->name && remote->name[0]) { git_buf buf = GIT_BUF_INIT; if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0) return error; - if ((error = git_config_get_string(&val, cfg, git_buf_cstr(&buf))) == 0 && - val && ('\0' != *val)) { - git_buf_free(&buf); + error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false); + git_buf_free(&buf); - *proxy_url = git__strdup(val); - GITERR_CHECK_ALLOC(*proxy_url); - return 0; - } else if (error != GIT_ENOTFOUND) + if (error < 0) return error; - giterr_clear(); - git_buf_free(&buf); + if (ce && ce->value) { + val = ce->value; + goto found; + } } /* http.proxy config setting */ - if ((error = git_config_get_string(&val, cfg, "http.proxy")) == 0 && - val && ('\0' != *val)) { - *proxy_url = git__strdup(val); - GITERR_CHECK_ALLOC(*proxy_url); - return 0; - } else if (error != GIT_ENOTFOUND) + if ((error = git_config__lookup_entry(&ce, cfg, "http.proxy", false)) < 0) return error; - - giterr_clear(); + if (ce && ce->value) { + val = ce->value; + goto found; + } /* HTTP_PROXY / HTTPS_PROXY environment variables */ val = use_ssl ? getenv("HTTPS_PROXY") : getenv("HTTP_PROXY"); - if (val && ('\0' != *val)) { +found: + if (val && val[0]) { *proxy_url = git__strdup(val); GITERR_CHECK_ALLOC(*proxy_url); - return 0; } return 0; @@ -781,46 +864,104 @@ return 0; } -int git_remote_download(git_remote *remote) +int git_remote_download(git_remote *remote, const git_strarray *refspecs) { - int error; - git_vector refs; + int error = -1; + size_t i; + git_vector refs, specs, *to_active; assert(remote); if (ls_to_vector(&refs, remote) < 0) return -1; + if ((git_vector_init(&specs, 0, NULL)) < 0) + goto on_error; + + remote->passed_refspecs = 0; + if (!refspecs || !refspecs->count) { + to_active = &remote->refspecs; + } else { + for (i = 0; i < refspecs->count; i++) { + if ((error = add_refspec_to(&specs, refspecs->strings[i], true)) < 0) + goto on_error; + } + + to_active = &specs; + remote->passed_refspecs = 1; + } + + free_refspecs(&remote->passive_refspecs); + if ((error = dwim_refspecs(&remote->passive_refspecs, &remote->refspecs, &refs)) < 0) + goto on_error; + free_refspecs(&remote->active_refspecs); + error = dwim_refspecs(&remote->active_refspecs, to_active, &refs); - error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &refs); git_vector_free(&refs); + free_refspecs(&specs); + git_vector_free(&specs); if (error < 0) - return -1; + return error; + + if (remote->push) { + git_push_free(remote->push); + remote->push = NULL; + } if ((error = git_fetch_negotiate(remote)) < 0) return error; return git_fetch_download_pack(remote); + +on_error: + git_vector_free(&refs); + free_refspecs(&specs); + git_vector_free(&specs); + return error; } -int git_remote_fetch(git_remote *remote) +int git_remote_fetch( + git_remote *remote, + const git_strarray *refspecs, + const git_signature *signature, + const char *reflog_message) { int error; + git_buf reflog_msg_buf = GIT_BUF_INIT; /* Connect and download everything */ - if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) < 0) + if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) != 0) return error; - if ((error = git_remote_download(remote)) < 0) - return error; + error = git_remote_download(remote, refspecs); /* We don't need to be connected anymore */ git_remote_disconnect(remote); + /* If the download failed, return the error */ + if (error != 0) + return error; + + /* Default reflog message */ + if (reflog_message) + git_buf_sets(&reflog_msg_buf, reflog_message); + else { + git_buf_printf(&reflog_msg_buf, "fetch %s", + remote->name ? remote->name : remote->url); + } + /* Create "remote/foo" branches for all remote branches */ - return git_remote_update_tips(remote); + error = git_remote_update_tips(remote, signature, git_buf_cstr(&reflog_msg_buf)); + git_buf_free(&reflog_msg_buf); + if (error < 0) + return error; + + if (remote->prune_refs) + error = git_remote_prune(remote); + + return error; } static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src) @@ -842,34 +983,70 @@ return 0; } -static int remote_head_for_ref(git_remote_head **out, git_refspec *spec, git_vector *update_heads, git_reference *ref) +static int ref_to_update(int *update, git_buf *remote_name, git_remote *remote, git_refspec *spec, const char *ref_name) +{ + int error = 0; + git_repository *repo; + git_buf upstream_remote = GIT_BUF_INIT; + git_buf upstream_name = GIT_BUF_INIT; + + repo = git_remote_owner(remote); + + if ((!git_reference__is_branch(ref_name)) || + !git_remote_name(remote) || + (error = git_branch_upstream_remote(&upstream_remote, repo, ref_name) < 0) || + git__strcmp(git_remote_name(remote), git_buf_cstr(&upstream_remote)) || + (error = git_branch_upstream_name(&upstream_name, repo, ref_name)) < 0 || + !git_refspec_dst_matches(spec, git_buf_cstr(&upstream_name)) || + (error = git_refspec_rtransform(remote_name, spec, upstream_name.ptr)) < 0) { + /* Not an error if there is no upstream */ + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = 0; + } + + *update = 0; + } else { + *update = 1; + } + + git_buf_free(&upstream_remote); + git_buf_free(&upstream_name); + return error; +} + +static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_refspec *spec, git_vector *update_heads, git_reference *ref) { git_reference *resolved_ref = NULL; - git_reference *tracking_ref = NULL; git_buf remote_name = GIT_BUF_INIT; - int error = 0; + git_config *config = NULL; + const char *ref_name; + int error = 0, update; assert(out && spec && ref); *out = NULL; - if ((error = git_reference_resolve(&resolved_ref, ref)) < 0 || - (!git_reference_is_branch(resolved_ref)) || - (error = git_branch_upstream(&tracking_ref, resolved_ref)) < 0 || - (error = git_refspec_transform_l(&remote_name, spec, git_reference_name(tracking_ref))) < 0) { - /* Not an error if HEAD is unborn or no tracking branch */ - if (error == GIT_ENOTFOUND) - error = 0; + error = git_reference_resolve(&resolved_ref, ref); - goto cleanup; + /* If we're in an unborn branch, let's pretend nothing happened */ + if (error == GIT_ENOTFOUND && git_reference_type(ref) == GIT_REF_SYMBOLIC) { + ref_name = git_reference_symbolic_target(ref); + error = 0; + } else { + ref_name = git_reference_name(resolved_ref); } - error = remote_head_for_fetchspec_src(out, update_heads, git_buf_cstr(&remote_name)); + if ((error = ref_to_update(&update, &remote_name, remote, spec, ref_name)) < 0) + goto cleanup; + + if (update) + error = remote_head_for_fetchspec_src(out, update_heads, git_buf_cstr(&remote_name)); cleanup: - git_reference_free(tracking_ref); - git_reference_free(resolved_ref); git_buf_free(&remote_name); + git_reference_free(resolved_ref); + git_config_free(config); return error; } @@ -898,7 +1075,7 @@ /* Determine what to merge: if refspec was a wildcard, just use HEAD */ if (git_refspec_is_wildcard(spec)) { if ((error = git_reference_lookup(&head_ref, remote->repo, GIT_HEAD_FILE)) < 0 || - (error = remote_head_for_ref(&merge_remote_ref, spec, update_heads, head_ref)) < 0) + (error = remote_head_for_ref(&merge_remote_ref, remote, spec, update_heads, head_ref)) < 0) goto cleanup; } else { /* If we're fetching a single refspec, that's the only thing that should be in FETCH_HEAD. */ @@ -938,24 +1115,168 @@ return error; } -static int update_tips_for_spec(git_remote *remote, git_refspec *spec, git_vector *refs) +/** + * Generate a list of candidates for pruning by getting a list of + * references which match the rhs of an active refspec. + */ +static int prune_candidates(git_vector *candidates, git_remote *remote) { - int error = 0, autotag; - unsigned int i = 0; - git_buf refname = GIT_BUF_INIT; - git_oid old; - git_odb *odb; - git_remote_head *head; - git_reference *ref; - git_refspec tagspec; - git_vector update_heads; + git_strarray arr = { 0 }; + size_t i; + int error; - assert(remote); + if ((error = git_reference_list(&arr, remote->repo)) < 0) + return error; - if (git_repository_odb__weakptr(&odb, remote->repo) < 0) - return -1; + for (i = 0; i < arr.count; i++) { + const char *refname = arr.strings[i]; + char *refname_dup; - if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) + if (!git_remote__matching_dst_refspec(remote, refname)) + continue; + + refname_dup = git__strdup(refname); + GITERR_CHECK_ALLOC(refname_dup); + + if ((error = git_vector_insert(candidates, refname_dup)) < 0) + goto out; + } + +out: + git_strarray_free(&arr); + return error; +} + +static int find_head(const void *_a, const void *_b) +{ + git_remote_head *a = (git_remote_head *) _a; + git_remote_head *b = (git_remote_head *) _b; + + return strcmp(a->name, b->name); +} + +int git_remote_prune(git_remote *remote) +{ + size_t i, j; + git_vector remote_refs = GIT_VECTOR_INIT; + git_vector candidates = GIT_VECTOR_INIT; + const git_refspec *spec; + const char *refname; + int error; + git_oid zero_id = {{ 0 }}; + + if ((error = ls_to_vector(&remote_refs, remote)) < 0) + goto cleanup; + + git_vector_set_cmp(&remote_refs, find_head); + + if ((error = prune_candidates(&candidates, remote)) < 0) + goto cleanup; + + /* + * Remove those entries from the candidate list for which we + * can find a remote reference in at least one refspec. + */ + git_vector_foreach(&candidates, i, refname) { + git_vector_foreach(&remote->active_refspecs, j, spec) { + git_buf buf = GIT_BUF_INIT; + size_t pos; + char *src_name; + git_remote_head key = {0}; + + if (!git_refspec_dst_matches(spec, refname)) + continue; + + if ((error = git_refspec_rtransform(&buf, spec, refname)) < 0) + goto cleanup; + + key.name = (char *) git_buf_cstr(&buf); + error = git_vector_search(&pos, &remote_refs, &key); + git_buf_free(&buf); + + if (error < 0 && error != GIT_ENOTFOUND) + goto cleanup; + + if (error == GIT_ENOTFOUND) + continue; + + /* if we did find a source, remove it from the candiates */ + if ((error = git_vector_set((void **) &src_name, &candidates, i, NULL)) < 0) + goto cleanup; + + git__free(src_name); + break; + } + } + + /* + * For those candidates still left in the list, we need to + * remove them. We do not remove symrefs, as those are for + * stuff like origin/HEAD which will never match, but we do + * not want to remove them. + */ + git_vector_foreach(&candidates, i, refname) { + git_reference *ref; + git_oid id; + + if (refname == NULL) + continue; + + error = git_reference_lookup(&ref, remote->repo, refname); + /* as we want it gone, let's not consider this an error */ + if (error == GIT_ENOTFOUND) + continue; + + if (error < 0) + goto cleanup; + + if (git_reference_type(ref) == GIT_REF_SYMBOLIC) { + git_reference_free(ref); + continue; + } + + git_oid_cpy(&id, git_reference_target(ref)); + error = git_reference_delete(ref); + git_reference_free(ref); + if (error < 0) + goto cleanup; + + if (remote->callbacks.update_tips) + error = remote->callbacks.update_tips(refname, &id, &zero_id, remote->callbacks.payload); + + if (error < 0) + goto cleanup; + } + +cleanup: + git_vector_free(&remote_refs); + git_vector_free_deep(&candidates); + return error; +} + +static int update_tips_for_spec( + git_remote *remote, + git_refspec *spec, + git_vector *refs, + const git_signature *signature, + const char *log_message) +{ + int error = 0, autotag; + unsigned int i = 0; + git_buf refname = GIT_BUF_INIT; + git_oid old; + git_odb *odb; + git_remote_head *head; + git_reference *ref; + git_refspec tagspec; + git_vector update_heads; + + assert(remote); + + if (git_repository_odb__weakptr(&odb, remote->repo) < 0) + return -1; + + if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) return -1; /* Make a copy of the transport's refs */ @@ -970,42 +1291,49 @@ if (!git_reference_is_valid_name(head->name)) continue; - if (git_refspec_src_matches(spec, head->name) && spec->dst) { - if (git_refspec_transform_r(&refname, spec, head->name) < 0) - goto on_error; - } else if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_NONE) { + if (git_refspec_src_matches(&tagspec, head->name)) { + if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_NONE) { - if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_ALL) - autotag = 1; + if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_AUTO) + autotag = 1; - if (!git_refspec_src_matches(&tagspec, head->name)) + git_buf_clear(&refname); + if (git_buf_puts(&refname, head->name) < 0) + goto on_error; + } else { continue; - - git_buf_clear(&refname); - if (git_buf_puts(&refname, head->name) < 0) + } + } else if (git_refspec_src_matches(spec, head->name) && spec->dst) { + if (git_refspec_transform(&refname, spec, head->name) < 0) goto on_error; } else { continue; } + /* In autotag mode, only create tags for objects already in db */ if (autotag && !git_odb_exists(odb, &head->oid)) continue; - if (git_vector_insert(&update_heads, head) < 0) + if (!autotag && git_vector_insert(&update_heads, head) < 0) goto on_error; error = git_reference_name_to_id(&old, remote->repo, refname.ptr); if (error < 0 && error != GIT_ENOTFOUND) goto on_error; - if (error == GIT_ENOTFOUND) + if (error == GIT_ENOTFOUND) { memset(&old, 0, GIT_OID_RAWSZ); + if (autotag && git_vector_insert(&update_heads, head) < 0) + goto on_error; + } + if (!git_oid__cmp(&old, &head->oid)) continue; /* In autotag mode, don't overwrite any locally-existing tags */ - error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag); + error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag, + signature, log_message); if (error < 0 && error != GIT_EEXISTS) goto on_error; @@ -1034,13 +1362,111 @@ } -int git_remote_update_tips(git_remote *remote) +/** + * Iteration over the three vectors, with a pause whenever we find a match + * + * On each stop, we store the iteration stat in the inout i,j,k + * parameters, and return the currently matching passive refspec as + * well as the head which we matched. + */ +static int next_head(const git_remote *remote, git_vector *refs, + git_refspec **out_spec, git_remote_head **out_head, + size_t *out_i, size_t *out_j, size_t *out_k) +{ + const git_vector *active, *passive; + git_remote_head *head; + git_refspec *spec, *passive_spec; + size_t i, j, k; + + active = &remote->active_refspecs; + passive = &remote->passive_refspecs; + + i = *out_i; + j = *out_j; + k = *out_k; + + for (; i < refs->length; i++) { + head = git_vector_get(refs, i); + + if (!git_reference_is_valid_name(head->name)) + continue; + + for (; j < active->length; j++) { + spec = git_vector_get(active, j); + + if (!git_refspec_src_matches(spec, head->name)) + continue; + + for (; k < passive->length; k++) { + passive_spec = git_vector_get(passive, k); + + if (!git_refspec_src_matches(passive_spec, head->name)) + continue; + + *out_spec = passive_spec; + *out_head = head; + *out_i = i; + *out_j = j; + *out_k = k + 1; + return 0; + + } + k = 0; + } + j = 0; + } + + return GIT_ITEROVER; +} + +static int opportunistic_updates(const git_remote *remote, git_vector *refs, const git_signature *sig, const char *msg) +{ + size_t i, j, k; + git_refspec *spec; + git_remote_head *head; + git_reference *ref; + git_buf refname = GIT_BUF_INIT; + int error; + + i = j = k = 0; + + while ((error = next_head(remote, refs, &spec, &head, &i, &j, &k)) == 0) { + /* + * If we got here, there is a refspec which was used + * for fetching which matches the source of one of the + * passive refspecs, so we should update that + * remote-tracking branch, but not add it to + * FETCH_HEAD + */ + + if ((error = git_refspec_transform(&refname, spec, head->name)) < 0) + return error; + + error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, true, sig, msg); + git_buf_free(&refname); + git_reference_free(ref); + + if (error < 0) + return error; + } + + return 0; +} + +int git_remote_update_tips( + git_remote *remote, + const git_signature *signature, + const char *reflog_message) { git_refspec *spec, tagspec; - git_vector refs; + git_vector refs = GIT_VECTOR_INIT; int error; size_t i; + /* push has its own logic hidden away in the push object */ + if (remote->push) { + return git_push_update_tips(remote->push, signature, reflog_message); + } if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) return -1; @@ -1050,25 +1476,29 @@ goto out; if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { - error = update_tips_for_spec(remote, &tagspec, &refs); - goto out; + if ((error = update_tips_for_spec(remote, &tagspec, &refs, signature, reflog_message)) < 0) + goto out; } git_vector_foreach(&remote->active_refspecs, i, spec) { if (spec->push) continue; - if ((error = update_tips_for_spec(remote, spec, &refs)) < 0) + if ((error = update_tips_for_spec(remote, spec, &refs, signature, reflog_message)) < 0) goto out; } + /* only try to do opportunisitic updates if the refpec lists differ */ + if (remote->passed_refspecs) + error = opportunistic_updates(remote, &refs, signature, reflog_message); + out: git_vector_free(&refs); git_refspec__free(&tagspec); return error; } -int git_remote_connected(git_remote *remote) +int git_remote_connected(const git_remote *remote) { assert(remote); @@ -1115,6 +1545,10 @@ free_refspecs(&remote->active_refspecs); git_vector_free(&remote->active_refspecs); + free_refspecs(&remote->passive_refspecs); + git_vector_free(&remote->passive_refspecs); + + git_push_free(remote->push); git__free(remote->url); git__free(remote->pushurl); git__free(remote->name); @@ -1141,51 +1575,32 @@ int git_remote_list(git_strarray *remotes_list, git_repository *repo) { - git_config *cfg; - git_vector list; int error; + git_config *cfg; + git_vector list = GIT_VECTOR_INIT; - if (git_repository_config__weakptr(&cfg, repo) < 0) - return -1; + if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) + return error; - if (git_vector_init(&list, 4, git__strcmp_cb) < 0) - return -1; + if ((error = git_vector_init(&list, 4, git__strcmp_cb)) < 0) + return error; error = git_config_foreach_match( cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list); if (error < 0) { - size_t i; - char *elem; - - git_vector_foreach(&list, i, elem) { - git__free(elem); - } - - git_vector_free(&list); - - /* cb error is converted to GIT_EUSER by git_config_foreach */ - if (error == GIT_EUSER) - error = -1; - + git_vector_free_deep(&list); return error; } git_vector_uniq(&list, git__free); - remotes_list->strings = (char **)list.contents; - remotes_list->count = list.length; + remotes_list->strings = + (char **)git_vector_detach(&remotes_list->count, NULL, &list); return 0; } -void git_remote_check_cert(git_remote *remote, int check) -{ - assert(remote); - - remote->check_cert = check; -} - int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks) { assert(remote && callbacks); @@ -1196,25 +1611,35 @@ if (remote->transport && remote->transport->set_callbacks) return remote->transport->set_callbacks(remote->transport, - remote->callbacks.progress, + remote->callbacks.sideband_progress, NULL, + remote->callbacks.certificate_check, remote->callbacks.payload); return 0; } -int git_remote_set_transport(git_remote *remote, git_transport *transport) +const git_remote_callbacks *git_remote_get_callbacks(git_remote *remote) { - assert(remote && transport); + assert(remote); - GITERR_CHECK_VERSION(transport, GIT_TRANSPORT_VERSION, "git_transport"); + return &remote->callbacks; +} + +int git_remote_set_transport( + git_remote *remote, + git_transport_cb transport_cb, + void *payload) +{ + assert(remote); if (remote->transport) { giterr_set(GITERR_NET, "A transport is already bound to this remote"); return -1; } - remote->transport = transport; + remote->transport_cb = transport_cb; + remote->transport_cb_payload = payload; return 0; } @@ -1224,7 +1649,7 @@ return &remote->stats; } -git_remote_autotag_option_t git_remote_autotag(git_remote *remote) +git_remote_autotag_option_t git_remote_autotag(const git_remote *remote) { return remote->download_tags; } @@ -1234,6 +1659,11 @@ remote->download_tags = value; } +int git_remote_prune_refs(const git_remote *remote) +{ + return remote->prune_refs; +} + static int rename_remote_config_section( git_repository *repo, const char *old_name, @@ -1246,13 +1676,14 @@ if (git_buf_printf(&old_section_name, "remote.%s", old_name) < 0) goto cleanup; - if (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0) - goto cleanup; + if (new_name && + (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0)) + goto cleanup; error = git_config_rename_section( repo, git_buf_cstr(&old_section_name), - git_buf_cstr(&new_section_name)); + new_name ? git_buf_cstr(&new_section_name) : NULL); cleanup: git_buf_free(&old_section_name); @@ -1261,8 +1692,7 @@ return error; } -struct update_data -{ +struct update_data { git_config *config; const char *old_remote_name; const char *new_remote_name; @@ -1278,9 +1708,7 @@ return 0; return git_config_set_string( - data->config, - entry->name, - data->new_remote_name); + data->config, entry->name, data->new_remote_name); } static int update_branch_remote_config_entry( @@ -1288,41 +1716,77 @@ const char *old_name, const char *new_name) { - git_config *config; - struct update_data data; + int error; + struct update_data data = { NULL }; - if (git_repository_config__weakptr(&config, repo) < 0) - return -1; + if ((error = git_repository_config__weakptr(&data.config, repo)) < 0) + return error; - data.config = config; data.old_remote_name = old_name; data.new_remote_name = new_name; return git_config_foreach_match( - config, - "branch\\..+\\.remote", - update_config_entries_cb, &data); + data.config, "branch\\..+\\.remote", update_config_entries_cb, &data); } static int rename_one_remote_reference( - git_reference *reference, + git_reference *reference_in, const char *old_remote_name, const char *new_remote_name) { - int error = -1; + int error; + git_reference *ref = NULL, *dummy = NULL; + git_buf namespace = GIT_BUF_INIT, old_namespace = GIT_BUF_INIT; git_buf new_name = GIT_BUF_INIT; + git_buf log_message = GIT_BUF_INIT; + size_t pfx_len; + const char *target; - if (git_buf_printf( - &new_name, - GIT_REFS_REMOTES_DIR "%s%s", - new_remote_name, - reference->name + strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name)) < 0) - return -1; + if ((error = git_buf_printf(&namespace, GIT_REFS_REMOTES_DIR "%s/", new_remote_name)) < 0) + return error; + + pfx_len = strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name) + 1; + git_buf_puts(&new_name, namespace.ptr); + if ((error = git_buf_puts(&new_name, git_reference_name(reference_in) + pfx_len)) < 0) + goto cleanup; + + if ((error = git_buf_printf(&log_message, + "renamed remote %s to %s", + old_remote_name, new_remote_name)) < 0) + goto cleanup; - error = git_reference_rename(NULL, reference, git_buf_cstr(&new_name), 0); - git_reference_free(reference); + if ((error = git_reference_rename(&ref, reference_in, git_buf_cstr(&new_name), 1, + NULL, git_buf_cstr(&log_message))) < 0) + goto cleanup; + + if (git_reference_type(ref) != GIT_REF_SYMBOLIC) + goto cleanup; + + /* Handle refs like origin/HEAD -> origin/master */ + target = git_reference_symbolic_target(ref); + if ((error = git_buf_printf(&old_namespace, GIT_REFS_REMOTES_DIR "%s/", old_remote_name)) < 0) + goto cleanup; + + if (git__prefixcmp(target, old_namespace.ptr)) + goto cleanup; + git_buf_clear(&new_name); + git_buf_puts(&new_name, namespace.ptr); + if ((error = git_buf_puts(&new_name, target + pfx_len)) < 0) + goto cleanup; + + error = git_reference_symbolic_set_target(&dummy, ref, git_buf_cstr(&new_name), + NULL, git_buf_cstr(&log_message)); + + git_reference_free(dummy); + +cleanup: + git_reference_free(reference_in); + git_reference_free(ref); + git_buf_free(&namespace); + git_buf_free(&old_namespace); git_buf_free(&new_name); + git_buf_free(&log_message); return error; } @@ -1331,161 +1795,136 @@ const char *old_name, const char *new_name) { - int error = -1; + int error; + git_buf buf = GIT_BUF_INIT; git_reference *ref; git_reference_iterator *iter; - if (git_reference_iterator_new(&iter, repo) < 0) - return -1; + if ((error = git_buf_printf(&buf, GIT_REFS_REMOTES_DIR "%s/*", old_name)) < 0) + return error; - while ((error = git_reference_next(&ref, iter)) == 0) { - if (git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) { - git_reference_free(ref); - continue; - } + error = git_reference_iterator_glob_new(&iter, repo, git_buf_cstr(&buf)); + git_buf_free(&buf); - if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0) { - git_reference_iterator_free(iter); - return error; - } + if (error < 0) + return error; + + while ((error = git_reference_next(&ref, iter)) == 0) { + if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0) + break; } git_reference_iterator_free(iter); - if (error == GIT_ITEROVER) - return 0; - - return error; + return (error == GIT_ITEROVER) ? 0 : error; } -static int rename_fetch_refspecs( - git_remote *remote, - const char *new_name, - int (*callback)(const char *problematic_refspec, void *payload), - void *payload) +static int rename_fetch_refspecs(git_vector *problems, git_remote *remote, const char *new_name) { git_config *config; git_buf base = GIT_BUF_INIT, var = GIT_BUF_INIT, val = GIT_BUF_INIT; const git_refspec *spec; size_t i; - int error = -1; + int error = 0; - if (git_buf_printf(&base, "+refs/heads/*:refs/remotes/%s/*", remote->name) < 0) - goto cleanup; + if ((error = git_repository_config__weakptr(&config, remote->repo)) < 0) + return error; + + if ((error = git_vector_init(problems, 1, NULL)) < 0) + return error; + + if ((error = git_buf_printf( + &base, "+refs/heads/*:refs/remotes/%s/*", remote->name)) < 0) + return error; git_vector_foreach(&remote->refspecs, i, spec) { if (spec->push) continue; - /* Every refspec is a problem refspec for an in-memory remote */ - if (!remote->name) { - if (callback(spec->string, payload) < 0) { - error = GIT_EUSER; - goto cleanup; - } + /* Does the dst part of the refspec follow the expected format? */ + if (strcmp(git_buf_cstr(&base), spec->string)) { + char *dup; - continue; - } + dup = git__strdup(spec->string); + GITERR_CHECK_ALLOC(dup); - /* Does the dst part of the refspec follow the extected standard format? */ - if (strcmp(git_buf_cstr(&base), spec->string)) { - if (callback(spec->string, payload) < 0) { - error = GIT_EUSER; - goto cleanup; - } + if ((error = git_vector_insert(problems, dup)) < 0) + break; continue; } /* If we do want to move it to the new section */ - if (git_buf_printf(&val, "+refs/heads/*:refs/remotes/%s/*", new_name) < 0) - goto cleanup; - if (git_buf_printf(&var, "remote.%s.fetch", new_name) < 0) - goto cleanup; + git_buf_clear(&val); + git_buf_clear(&var); - if (git_repository_config__weakptr(&config, remote->repo) < 0) - goto cleanup; + if (git_buf_printf( + &val, "+refs/heads/*:refs/remotes/%s/*", new_name) < 0 || + git_buf_printf(&var, "remote.%s.fetch", new_name) < 0) + { + error = -1; + break; + } - if (git_config_set_string(config, git_buf_cstr(&var), git_buf_cstr(&val)) < 0) - goto cleanup; + if ((error = git_config_set_string( + config, git_buf_cstr(&var), git_buf_cstr(&val))) < 0) + break; } - error = 0; - -cleanup: git_buf_free(&base); git_buf_free(&var); git_buf_free(&val); + + if (error < 0) { + char *str; + git_vector_foreach(problems, i, str) + git__free(str); + + git_vector_free(problems); + } + return error; } -int git_remote_rename( - git_remote *remote, - const char *new_name, - git_remote_rename_problem_cb callback, - void *payload) +int git_remote_rename(git_strarray *out, git_repository *repo, const char *name, const char *new_name) { int error; + git_vector problem_refspecs = GIT_VECTOR_INIT; + git_remote *remote = NULL; - assert(remote && new_name); + assert(out && repo && name && new_name); - if (!remote->name) { - giterr_set(GITERR_INVALID, "Can't rename an in-memory remote."); - return GIT_EINVALIDSPEC; - } + if ((error = git_remote_lookup(&remote, repo, name)) < 0) + return error; if ((error = ensure_remote_name_is_valid(new_name)) < 0) - return error; + goto cleanup; - if (remote->repo) { - if ((error = ensure_remote_doesnot_exist(remote->repo, new_name)) < 0) - return error; + if ((error = ensure_remote_doesnot_exist(repo, new_name)) < 0) + goto cleanup; + + if ((error = rename_remote_config_section(repo, name, new_name)) < 0) + goto cleanup; - if (!remote->name) { - if ((error = rename_fetch_refspecs( - remote, - new_name, - callback, - payload)) < 0) - return error; + if ((error = update_branch_remote_config_entry(repo, name, new_name)) < 0) + goto cleanup; - remote->name = git__strdup(new_name); + if ((error = rename_remote_references(repo, name, new_name)) < 0) + goto cleanup; - if (!remote->name) return 0; - return git_remote_save(remote); - } + if ((error = rename_fetch_refspecs(&problem_refspecs, remote, new_name)) < 0) + goto cleanup; - if ((error = rename_remote_config_section( - remote->repo, - remote->name, - new_name)) < 0) - return error; - - if ((error = update_branch_remote_config_entry( - remote->repo, - remote->name, - new_name)) < 0) - return error; - - if ((error = rename_remote_references( - remote->repo, - remote->name, - new_name)) < 0) - return error; - - if ((error = rename_fetch_refspecs( - remote, - new_name, - callback, - payload)) < 0) - return error; - } + out->count = problem_refspecs.length; + out->strings = (char **) problem_refspecs.contents; - git__free(remote->name); - remote->name = git__strdup(new_name); +cleanup: + if (error < 0) + git_vector_free(&problem_refspecs); - return 0; + git_remote_free(remote); + return error; } int git_remote_update_fetchhead(git_remote *remote) @@ -1562,27 +2001,14 @@ git_vector_clear(&remote->refspecs); } -static int add_and_dwim(git_remote *remote, const char *str, int push) -{ - git_refspec *spec; - git_vector *vec; - - if (add_refspec(remote, str, !push) < 0) - return -1; - - vec = &remote->refspecs; - spec = git_vector_get(vec, vec->length - 1); - return git_refspec__dwim_one(&remote->active_refspecs, spec, &remote->refs); -} - int git_remote_add_fetch(git_remote *remote, const char *refspec) { - return add_and_dwim(remote, refspec, false); + return add_refspec(remote, refspec, true); } int git_remote_add_push(git_remote *remote, const char *refspec) { - return add_and_dwim(remote, refspec, true); + return add_refspec(remote, refspec, false); } static int set_refspecs(git_remote *remote, git_strarray *array, int push) @@ -1610,10 +2036,7 @@ return -1; } - free_refspecs(&remote->active_refspecs); - git_vector_clear(&remote->active_refspecs); - - return dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs); + return 0; } int git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array) @@ -1626,7 +2049,7 @@ return set_refspecs(remote, array, true); } -static int copy_refspecs(git_strarray *array, git_remote *remote, unsigned int push) +static int copy_refspecs(git_strarray *array, const git_remote *remote, unsigned int push) { size_t i; git_vector refspecs; @@ -1655,29 +2078,321 @@ return 0; on_error: - git_vector_foreach(&refspecs, i, dup) - git__free(dup); - git_vector_free(&refspecs); + git_vector_free_deep(&refspecs); return -1; } -int git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote) +int git_remote_get_fetch_refspecs(git_strarray *array, const git_remote *remote) { return copy_refspecs(array, remote, false); } -int git_remote_get_push_refspecs(git_strarray *array, git_remote *remote) +int git_remote_get_push_refspecs(git_strarray *array, const git_remote *remote) { return copy_refspecs(array, remote, true); } -size_t git_remote_refspec_count(git_remote *remote) +size_t git_remote_refspec_count(const git_remote *remote) { return remote->refspecs.length; } -const git_refspec *git_remote_get_refspec(git_remote *remote, size_t n) +const git_refspec *git_remote_get_refspec(const git_remote *remote, size_t n) { return git_vector_get(&remote->refspecs, n); } + +int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_remote_callbacks, GIT_REMOTE_CALLBACKS_INIT); + return 0; +} + +/* asserts a branch..remote format */ +static const char *name_offset(size_t *len_out, const char *name) +{ + size_t prefix_len; + const char *dot; + + prefix_len = strlen("remote."); + dot = strchr(name + prefix_len, '.'); + + assert(dot); + + *len_out = dot - name - prefix_len; + return name + prefix_len; +} + +static int remove_branch_config_related_entries( + git_repository *repo, + const char *remote_name) +{ + int error; + git_config *config; + git_config_entry *entry; + git_config_iterator *iter; + git_buf buf = GIT_BUF_INIT; + + if ((error = git_repository_config__weakptr(&config, repo)) < 0) + return error; + + if ((error = git_config_iterator_glob_new(&iter, config, "branch\\..+\\.remote")) < 0) + return error; + + /* find any branches with us as upstream and remove that config */ + while ((error = git_config_next(&entry, iter)) == 0) { + const char *branch; + size_t branch_len; + + if (strcmp(remote_name, entry->value)) + continue; + + branch = name_offset(&branch_len, entry->name); + + git_buf_clear(&buf); + if (git_buf_printf(&buf, "branch.%.*s.merge", (int)branch_len, branch) < 0) + break; + + if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) + break; + + git_buf_clear(&buf); + if (git_buf_printf(&buf, "branch.%.*s.remote", (int)branch_len, branch) < 0) + break; + + if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0) + break; + } + + if (error == GIT_ITEROVER) + error = 0; + + git_buf_free(&buf); + git_config_iterator_free(iter); + return error; +} + +static int remove_refs(git_repository *repo, const git_refspec *spec) +{ + git_reference_iterator *iter = NULL; + git_vector refs; + const char *name; + char *dup; + int error; + size_t i; + + if ((error = git_vector_init(&refs, 8, NULL)) < 0) + return error; + + if ((error = git_reference_iterator_new(&iter, repo)) < 0) + goto cleanup; + + while ((error = git_reference_next_name(&name, iter)) == 0) { + if (!git_refspec_dst_matches(spec, name)) + continue; + + dup = git__strdup(name); + if (!dup) { + error = -1; + goto cleanup; + } + + if ((error = git_vector_insert(&refs, dup)) < 0) + goto cleanup; + } + if (error == GIT_ITEROVER) + error = 0; + if (error < 0) + goto cleanup; + + git_vector_foreach(&refs, i, name) { + if ((error = git_reference_remove(repo, name)) < 0) + break; + } + +cleanup: + git_reference_iterator_free(iter); + git_vector_foreach(&refs, i, dup) { + git__free(dup); + } + git_vector_free(&refs); + return error; +} + +static int remove_remote_tracking(git_repository *repo, const char *remote_name) +{ + git_remote *remote; + int error; + size_t i, count; + + /* we want to use what's on the config, regardless of changes to the instance in memory */ + if ((error = git_remote_lookup(&remote, repo, remote_name)) < 0) + return error; + + count = git_remote_refspec_count(remote); + for (i = 0; i < count; i++) { + const git_refspec *refspec = git_remote_get_refspec(remote, i); + + /* shouldn't ever actually happen */ + if (refspec == NULL) + continue; + + if ((error = remove_refs(repo, refspec)) < 0) + break; + } + + git_remote_free(remote); + return error; +} + +int git_remote_delete(git_repository *repo, const char *name) +{ + int error; + + assert(repo && name); + + if ((error = remove_branch_config_related_entries(repo, name)) < 0 || + (error = remove_remote_tracking(repo, name)) < 0 || + (error = rename_remote_config_section(repo, name, NULL)) < 0) + return error; + + return 0; +} + +int git_remote_default_branch(git_buf *out, git_remote *remote) +{ + const git_remote_head **heads; + const git_remote_head *guess = NULL; + const git_oid *head_id; + size_t heads_len, i; + int error; + + assert(out); + + if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0) + return error; + + if (heads_len == 0) + return GIT_ENOTFOUND; + + if (strcmp(heads[0]->name, GIT_HEAD_FILE)) + return GIT_ENOTFOUND; + + git_buf_sanitize(out); + /* the first one must be HEAD so if that has the symref info, we're done */ + if (heads[0]->symref_target) + return git_buf_puts(out, heads[0]->symref_target); + + /* + * If there's no symref information, we have to look over them + * and guess. We return the first match unless the master + * branch is a candidate. Then we return the master branch. + */ + head_id = &heads[0]->oid; + + for (i = 1; i < heads_len; i++) { + if (git_oid_cmp(head_id, &heads[i]->oid)) + continue; + + if (git__prefixcmp(heads[i]->name, GIT_REFS_HEADS_DIR)) + continue; + + if (!guess) { + guess = heads[i]; + continue; + } + + if (!git__strcmp(GIT_REFS_HEADS_MASTER_FILE, heads[i]->name)) { + guess = heads[i]; + break; + } + } + + if (!guess) + return GIT_ENOTFOUND; + + return git_buf_puts(out, guess->name); +} + +int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts) +{ + size_t i; + int error; + git_push *push; + git_refspec *spec; + git_remote_callbacks *cbs; + + assert(remote); + + if (!git_remote_connected(remote) && + (error = git_remote_connect(remote, GIT_DIRECTION_PUSH)) < 0) + goto cleanup; + + free_refspecs(&remote->active_refspecs); + if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0) + goto cleanup; + + if (remote->push) { + git_push_free(remote->push); + remote->push = NULL; + } + + if ((error = git_push_new(&remote->push, remote)) < 0) + return error; + + push = remote->push; + + if (opts && (error = git_push_set_options(push, opts)) < 0) + goto cleanup; + + if (refspecs && refspecs->count > 0) { + for (i = 0; i < refspecs->count; i++) { + if ((error = git_push_add_refspec(push, refspecs->strings[i])) < 0) + goto cleanup; + } + } else { + git_vector_foreach(&remote->refspecs, i, spec) { + if (!spec->push) + continue; + if ((error = git_push_add_refspec(push, spec->string)) < 0) + goto cleanup; + } + } + + cbs = &remote->callbacks; + if ((error = git_push_set_callbacks(push, + cbs->pack_progress, cbs->payload, + cbs->push_transfer_progress, cbs->payload)) < 0) + goto cleanup; + + if ((error = git_push_finish(push)) < 0) + goto cleanup; + + if (cbs->push_update_reference && + (error = git_push_status_foreach(push, cbs->push_update_reference, cbs->payload)) < 0) + goto cleanup; + +cleanup: + return error; +} + +int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts, + const git_signature *signature, const char *reflog_message) +{ + int error; + + assert(remote && refspecs); + + if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH)) < 0) + return error; + + if ((error = git_remote_upload(remote, refspecs, opts)) < 0) + return error; + + error = git_remote_update_tips(remote, signature, reflog_message); + + git_remote_disconnect(remote); + return error; +} diff -Nru libgit2-0.20.0/src/remote.h libgit2-0.22.2/src/remote.h --- libgit2-0.20.0/src/remote.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/remote.h 2015-03-24 16:10:45.000000000 +0000 @@ -9,6 +9,7 @@ #include "git2/remote.h" #include "git2/transport.h" +#include "git2/sys/transport.h" #include "refspec.h" #include "vector.h" @@ -22,14 +23,19 @@ git_vector refs; git_vector refspecs; git_vector active_refspecs; + git_vector passive_refspecs; + git_transport_cb transport_cb; + void *transport_cb_payload; git_transport *transport; git_repository *repo; + git_push *push; git_remote_callbacks callbacks; git_transfer_progress stats; unsigned int need_pack; git_remote_autotag_option_t download_tags; - int check_cert; int update_fetchhead; + int prune_refs; + int passed_refspecs; }; const char* git_remote__urlfordirection(struct git_remote *remote, int direction); diff -Nru libgit2-0.20.0/src/repository.c libgit2-0.22.2/src/repository.c --- libgit2-0.20.0/src/repository.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/repository.c 2015-03-24 16:10:45.000000000 +0000 @@ -4,7 +4,6 @@ * 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 #include #include "git2/object.h" @@ -17,6 +16,7 @@ #include "tag.h" #include "blob.h" #include "fileops.h" +#include "sysdir.h" #include "filebuf.h" #include "index.h" #include "config.h" @@ -27,12 +27,19 @@ #include "merge.h" #include "diff_driver.h" +#ifdef GIT_WIN32 +# include "win32/w32_util.h" +#endif + #define GIT_FILE_CONTENT_PREFIX "gitdir:" #define GIT_BRANCH_MASTER "master" #define GIT_REPO_VERSION 0 +const char *git_repository__8dot3_default = "GIT~1"; +size_t git_repository__8dot3_default_len = 5; + static void set_odb(git_repository *repo, git_odb *odb) { if (odb) { @@ -93,6 +100,7 @@ git_cache_clear(&repo->objects); git_attr_cache_flush(repo); + git_submodule_cache_free(repo); set_config(repo, NULL); set_index(repo, NULL); @@ -108,7 +116,6 @@ git_repository__cleanup(repo); git_cache_free(&repo->objects); - git_submodule_config_free(repo); git_diff_driver_registry_free(repo->diff_drivers); repo->diff_drivers = NULL; @@ -116,6 +123,7 @@ git__free(repo->path_repository); git__free(repo->workdir); git__free(repo->namespace); + git__free(repo->name_8dot3); git__memzero(repo, sizeof(*repo)); git__free(repo); @@ -165,13 +173,9 @@ return 0; } -static int load_config_data(git_repository *repo) +static int load_config_data(git_repository *repo, const git_config *config) { int is_bare; - git_config *config; - - if (git_repository_config__weakptr(&config, repo) < 0) - return -1; /* Try to figure out if it's bare, default to non-bare if it's not set */ if (git_config_get_bool(&is_bare, config, "core.bare") < 0) @@ -182,43 +186,37 @@ return 0; } -static int load_workdir(git_repository *repo, git_buf *parent_path) +static int load_workdir(git_repository *repo, git_config *config, git_buf *parent_path) { int error; - git_config *config; - const char *worktree; - git_buf worktree_buf = GIT_BUF_INIT; + const git_config_entry *ce; + git_buf worktree = GIT_BUF_INIT; if (repo->is_bare) return 0; - if (git_repository_config__weakptr(&config, repo) < 0) - return -1; + if ((error = git_config__lookup_entry( + &ce, config, "core.worktree", false)) < 0) + return error; - error = git_config_get_string(&worktree, config, "core.worktree"); - if (!error && worktree != NULL) { - error = git_path_prettify_dir( - &worktree_buf, worktree, repo->path_repository); - if (error < 0) + if (ce && ce->value) { + if ((error = git_path_prettify_dir( + &worktree, ce->value, repo->path_repository)) < 0) return error; - repo->workdir = git_buf_detach(&worktree_buf); + + repo->workdir = git_buf_detach(&worktree); } - else if (error != GIT_ENOTFOUND) - return error; + else if (parent_path && git_path_isdir(parent_path->ptr)) + repo->workdir = git_buf_detach(parent_path); else { - giterr_clear(); + if (git_path_dirname_r(&worktree, repo->path_repository) < 0 || + git_path_to_dir(&worktree) < 0) + return -1; - if (parent_path && git_path_isdir(parent_path->ptr)) - repo->workdir = git_buf_detach(parent_path); - else { - git_path_dirname_r(&worktree_buf, repo->path_repository); - git_path_to_dir(&worktree_buf); - repo->workdir = git_buf_detach(&worktree_buf); - } + repo->workdir = git_buf_detach(&worktree); } GITERR_CHECK_ALLOC(repo->workdir); - return 0; } @@ -291,16 +289,20 @@ return -1; git_buf_rtrim(&file); + /* apparently on Windows, some people use backslashes in paths */ + git_path_mkposix(file.ptr); if (git_buf_len(&file) <= prefix_len || memcmp(git_buf_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0) { - giterr_set(GITERR_REPOSITORY, "The `.git` file at '%s' is malformed", file_path); + giterr_set(GITERR_REPOSITORY, + "The `.git` file at '%s' is malformed", file_path); error = -1; } else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) { const char *gitlink = git_buf_cstr(&file) + prefix_len; while (*gitlink && git__isspace(*gitlink)) gitlink++; + error = git_path_prettify_dir( path_out, gitlink, git_buf_cstr(path_out)); } @@ -461,16 +463,22 @@ if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0) repo->is_bare = 1; - else if ((error = load_config_data(repo)) < 0 || - (error = load_workdir(repo, &parent)) < 0) - { - git_repository_free(repo); - return error; + else { + git_config *config = NULL; + + if ((error = git_repository_config_snapshot(&config, repo)) < 0 || + (error = load_config_data(repo, config)) < 0 || + (error = load_workdir(repo, config, &parent)) < 0) + git_repository_free(repo); + + git_config_free(config); } + if (!error) + *repo_ptr = repo; git_buf_free(&parent); - *repo_ptr = repo; - return 0; + + return error; } int git_repository_open(git_repository **repo_out, const char *path) @@ -493,34 +501,18 @@ } int git_repository_discover( - char *repository_path, - size_t size, + git_buf *out, const char *start_path, int across_fs, const char *ceiling_dirs) { - git_buf path = GIT_BUF_INIT; uint32_t flags = across_fs ? GIT_REPOSITORY_OPEN_CROSS_FS : 0; - int error; - - assert(start_path && repository_path && size > 0); - *repository_path = '\0'; + assert(start_path); - if ((error = find_repo(&path, NULL, start_path, flags, ceiling_dirs)) < 0) - return error != GIT_ENOTFOUND ? -1 : error; - - if (size < (size_t)(path.size + 1)) { - giterr_set(GITERR_REPOSITORY, - "The given buffer is too small to store the discovered path"); - git_buf_free(&path); - return -1; - } + git_buf_sanitize(out); - /* success: we discovered a repository */ - git_buf_copy_cstr(repository_path, size, &path); - git_buf_free(&path); - return 0; + return find_repo(out, NULL, start_path, flags, ceiling_dirs); } static int load_config( @@ -596,9 +588,9 @@ git_buf system_buf = GIT_BUF_INIT; git_config *config; - git_config_find_global_r(&global_buf); - git_config_find_xdg_r(&xdg_buf); - git_config_find_system_r(&system_buf); + git_config_find_global(&global_buf); + git_config_find_xdg(&xdg_buf); + git_config_find_system(&system_buf); /* If there is no global file, open a backend for it anyway */ if (git_buf_len(&global_buf) == 0) @@ -637,6 +629,17 @@ return 0; } +int git_repository_config_snapshot(git_config **out, git_repository *repo) +{ + int error; + git_config *weak; + + if ((error = git_repository_config__weakptr(&weak, repo)) < 0) + return error; + + return git_config_snapshot(out, weak); +} + void git_repository_set_config(git_repository *repo, git_config *config) { assert(repo && config); @@ -653,7 +656,8 @@ git_buf odb_path = GIT_BUF_INIT; git_odb *odb; - git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR); + if ((error = git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR)) < 0) + return error; error = git_odb_open(&odb, odb_path.ptr); if (!error) { @@ -738,7 +742,8 @@ git_buf index_path = GIT_BUF_INIT; git_index *index; - git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE); + if ((error = git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE)) < 0) + return error; error = git_index_open(&index, index_path.ptr); if (!error) { @@ -792,6 +797,28 @@ return repo->namespace; } +const char *git_repository__8dot3_name(git_repository *repo) +{ + if (!repo->has_8dot3) { + repo->has_8dot3 = 1; + +#ifdef GIT_WIN32 + if (!repo->is_bare) { + repo->name_8dot3 = git_win32_path_8dot3_name(repo->path_repository); + + /* We anticipate the 8.3 name is "GIT~1", so use a static for + * easy testing in the common case */ + if (repo->name_8dot3 && + strcasecmp(repo->name_8dot3, git_repository__8dot3_default) == 0) + repo->has_8dot3_default = 1; + } +#endif + } + + return repo->has_8dot3_default ? + git_repository__8dot3_default : repo->name_8dot3; +} + static int check_repositoryformatversion(git_config *config) { int version; @@ -890,60 +917,6 @@ return symlinks_supported; } -#ifdef GIT_USE_ICONV - -static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX"; -static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX"; - -/* Check if the platform is decomposing unicode data for us. We will - * emulate core Git and prefer to use precomposed unicode data internally - * on these platforms, composing the decomposed unicode on the fly. - * - * This mainly happens on the Mac where HDFS stores filenames as - * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will - * return decomposed unicode from readdir() even when the actual - * filesystem is storing precomposed unicode. - */ -static bool does_fs_decompose_unicode_paths(const char *wd_path) -{ - git_buf path = GIT_BUF_INIT; - int fd; - bool found_decomposed = false; - char tmp[6]; - - /* Create a file using a precomposed path and then try to find it - * using the decomposed name. If the lookup fails, then we will mark - * that we should precompose unicode for this repository. - */ - if (git_buf_joinpath(&path, wd_path, nfc_file) < 0 || - (fd = p_mkstemp(path.ptr)) < 0) - goto done; - p_close(fd); - - /* record trailing digits generated by mkstemp */ - memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp)); - - /* try to look up as NFD path */ - if (git_buf_joinpath(&path, wd_path, nfd_file) < 0) - goto done; - memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); - - found_decomposed = git_path_exists(path.ptr); - - /* remove temporary file (using original precomposed path) */ - if (git_buf_joinpath(&path, wd_path, nfc_file) < 0) - goto done; - memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); - - (void)p_unlink(path.ptr); - -done: - git_buf_free(&path); - return found_decomposed; -} - -#endif - static int create_empty_file(const char *path, mode_t mode) { int fd; @@ -1034,8 +1007,9 @@ #ifdef GIT_USE_ICONV if ((error = git_config_set_bool( cfg, "core.precomposeunicode", - does_fs_decompose_unicode_paths(work_dir))) < 0) + git_path_does_fs_decompose_unicode(work_dir))) < 0) return error; + /* on non-iconv platforms, don't even set core.precomposeunicode */ #endif return 0; @@ -1048,7 +1022,7 @@ uint32_t mode) { int error = 0; - git_buf cfg_path = GIT_BUF_INIT; + git_buf cfg_path = GIT_BUF_INIT, worktree_path = GIT_BUF_INIT; git_config *config = NULL; bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0); bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0); @@ -1073,9 +1047,16 @@ if (!is_bare) { SET_REPO_CONFIG(bool, "core.logallrefupdates", true); - if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD)) - SET_REPO_CONFIG(string, "core.worktree", work_dir); - else if (is_reinit) { + if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD)) { + if ((error = git_buf_sets(&worktree_path, work_dir)) < 0) + goto cleanup; + + if ((flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK)) + if ((error = git_path_make_relative(&worktree_path, repo_dir)) < 0) + goto cleanup; + + SET_REPO_CONFIG(string, "core.worktree", worktree_path.ptr); + } else if (is_reinit) { if (git_config_delete_entry(config, "core.worktree") < 0) giterr_clear(); } @@ -1092,6 +1073,7 @@ cleanup: git_buf_free(&cfg_path); + git_buf_free(&worktree_path); git_config_free(config); return error; @@ -1163,7 +1145,7 @@ #ifdef GIT_WIN32 if (!error && hidden) { - if (p_hide_directory__w32(path.ptr) < 0) + if (git_win32__sethidden(path.ptr) < 0) error = -1; } #else @@ -1180,10 +1162,11 @@ } static int repo_write_gitlink( - const char *in_dir, const char *to_repo) + const char *in_dir, const char *to_repo, bool use_relative_path) { int error; git_buf buf = GIT_BUF_INIT; + git_buf path_to_repo = GIT_BUF_INIT; struct stat st; git_path_dirname_r(&buf, to_repo); @@ -1211,13 +1194,20 @@ git_buf_clear(&buf); - error = git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo); + error = git_buf_sets(&path_to_repo, to_repo); + + if (!error && use_relative_path) + error = git_path_make_relative(&path_to_repo, in_dir); + + if (!error) + error = git_buf_join(&buf, ' ', GIT_FILE_CONTENT_PREFIX, path_to_repo.ptr); if (!error) error = repo_write_template(in_dir, true, DOT_GIT, 0666, true, buf.ptr); cleanup: git_buf_free(&buf); + git_buf_free(&path_to_repo); return error; } @@ -1244,12 +1234,13 @@ bool external_tpl = ((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0); mode_t dmode = pick_dir_mode(opts); + bool chmod = opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK; /* Hide the ".git" directory */ #ifdef GIT_WIN32 if ((opts->flags & GIT_REPOSITORY_INIT__HAS_DOTGIT) != 0) { - if (p_hide_directory__w32(repo_dir) < 0) { - giterr_set(GITERR_REPOSITORY, + if (git_win32__sethidden(repo_dir) < 0) { + giterr_set(GITERR_OS, "Failed to mark Git repository folder as hidden"); return -1; } @@ -1260,7 +1251,7 @@ if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 && (opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD) == 0) { - if (repo_write_gitlink(work_dir, repo_dir) < 0) + if (repo_write_gitlink(work_dir, repo_dir, opts->flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK) < 0) return -1; } @@ -1279,15 +1270,17 @@ } if (!tdir) { - if (!(error = git_futils_find_template_dir(&template_buf))) + if (!(error = git_sysdir_find_template_dir(&template_buf))) tdir = template_buf.ptr; default_template = true; } - if (tdir) - error = git_futils_cp_r(tdir, repo_dir, - GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS | - GIT_CPDIR_SIMPLE_TO_MODE, dmode); + if (tdir) { + uint32_t cpflags = GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_SIMPLE_TO_MODE; + if (opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK) + cpflags |= GIT_CPDIR_CHMOD_DIRS; + error = git_futils_cp_r(tdir, repo_dir, cpflags, dmode); + } git_buf_free(&template_buf); git_config_free(cfg); @@ -1308,9 +1301,14 @@ * - only create files if no external template was specified */ for (tpl = repo_template; !error && tpl->path; ++tpl) { - if (!tpl->content) + if (!tpl->content) { + uint32_t mkdir_flags = GIT_MKDIR_PATH; + if (chmod) + mkdir_flags |= GIT_MKDIR_CHMOD; + error = git_futils_mkdir( - tpl->path, repo_dir, dmode, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD); + tpl->path, repo_dir, dmode, mkdir_flags); + } else if (!external_tpl) { const char *content = tpl->content; @@ -1597,8 +1595,10 @@ error = git_repository_head(&ref, repo); git_reference_free(ref); - if (error == GIT_EUNBORNBRANCH) + if (error == GIT_EUNBORNBRANCH) { + giterr_clear(); return 1; + } if (error < 0) return -1; @@ -1610,15 +1610,14 @@ { GIT_UNUSED(refname); GIT_UNUSED(payload); - - return GIT_EUSER; + return GIT_PASSTHROUGH; } static int repo_contains_no_reference(git_repository *repo) { int error = git_reference_foreach_name(repo, &at_least_one_cb, NULL); - if (error == GIT_EUSER) + if (error == GIT_PASSTHROUGH) return 0; if (!error) @@ -1682,7 +1681,7 @@ if (git_repository_config__weakptr(&config, repo) < 0) return -1; - error = repo_write_gitlink(path.ptr, git_repository_path(repo)); + error = repo_write_gitlink(path.ptr, git_repository_path(repo), false); /* passthrough error means gitlink is unnecessary */ if (error == GIT_PASSTHROUGH) @@ -1712,6 +1711,32 @@ return repo->is_bare; } +int git_repository_set_bare(git_repository *repo) +{ + int error; + git_config *config; + + assert(repo); + + if (repo->is_bare) + return 0; + + if ((error = git_repository_config__weakptr(&config, repo)) < 0) + return error; + + if ((error = git_config_set_bool(config, "core.bare", true)) < 0) + return error; + + if ((error = git_config__update_entry(config, "core.worktree", NULL, true, true)) < 0) + return error; + + git__free(repo->workdir); + repo->workdir = NULL; + repo->is_bare = 1; + + return 0; +} + int git_repository_head_tree(git_tree **tree, git_repository *repo) { git_reference *head; @@ -1731,14 +1756,35 @@ return error; } -int git_repository_message(char *buffer, size_t len, git_repository *repo) +int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head) +{ + git_filebuf file = GIT_FILEBUF_INIT; + git_buf file_path = GIT_BUF_INIT; + char orig_head_str[GIT_OID_HEXSZ]; + int error = 0; + + git_oid_fmt(orig_head_str, orig_head); + + if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_ORIG_HEAD_FILE)) == 0 && + (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) == 0 && + (error = git_filebuf_printf(&file, "%.*s\n", GIT_OID_HEXSZ, orig_head_str)) == 0) + error = git_filebuf_commit(&file); + + if (error < 0) + git_filebuf_cleanup(&file); + + git_buf_free(&file_path); + + return error; +} + +int git_repository_message(git_buf *out, git_repository *repo) { - git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT; + git_buf path = GIT_BUF_INIT; struct stat st; int error; - if (buffer != NULL) - *buffer = '\0'; + git_buf_sanitize(out); if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0) return -1; @@ -1747,17 +1793,11 @@ if (errno == ENOENT) error = GIT_ENOTFOUND; giterr_set(GITERR_OS, "Could not access message file"); - } - else if (buffer != NULL) { - error = git_futils_readbuffer(&buf, git_buf_cstr(&path)); - git_buf_copy_cstr(buffer, len, &buf); + } else { + error = git_futils_readbuffer(out, git_buf_cstr(&path)); } git_buf_free(&path); - git_buf_free(&buf); - - if (!error) - error = (int)st.st_size + 1; /* add 1 for NUL byte */ return error; } @@ -1807,7 +1847,8 @@ /* passing empty string for "as_path" indicated --no-filters */ if (strlen(as_path) > 0) { error = git_filter_list_load( - &fl, repo, NULL, as_path, GIT_FILTER_TO_ODB); + &fl, repo, NULL, as_path, + GIT_FILTER_TO_ODB, GIT_FILTER_OPT_DEFAULT); if (error < 0) return error; } else { @@ -1852,7 +1893,9 @@ int git_repository_set_head( git_repository* repo, - const char* refname) + const char* refname, + const git_signature *signature, + const char *log_message) { git_reference *ref, *new_head = NULL; @@ -1865,12 +1908,17 @@ return error; if (!error) { - if (git_reference_is_branch(ref)) - error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, git_reference_name(ref), 1); - else - error = git_repository_set_head_detached(repo, git_reference_target(ref)); - } else if (looks_like_a_branch(refname)) - error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, refname, 1); + if (git_reference_is_branch(ref)) { + error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, + git_reference_name(ref), true, signature, log_message); + } else { + error = git_repository_set_head_detached(repo, git_reference_target(ref), + signature, log_message); + } + } else if (looks_like_a_branch(refname)) { + error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, refname, + true, signature, log_message); + } git_reference_free(ref); git_reference_free(new_head); @@ -1879,7 +1927,9 @@ int git_repository_set_head_detached( git_repository* repo, - const git_oid* commitish) + const git_oid* commitish, + const git_signature *signature, + const char *log_message) { int error; git_object *object, @@ -1894,7 +1944,7 @@ if ((error = git_object_peel(&peeled, object, GIT_OBJ_COMMIT)) < 0) goto cleanup; - error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), 1); + error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), true, signature, log_message); cleanup: git_object_free(object); @@ -1904,7 +1954,9 @@ } int git_repository_detach_head( - git_repository* repo) + git_repository* repo, + const git_signature *signature, + const char *reflog_message) { git_reference *old_head = NULL, *new_head = NULL; @@ -1919,7 +1971,8 @@ if ((error = git_object_lookup(&object, repo, git_reference_target(old_head), GIT_OBJ_COMMIT)) < 0) goto cleanup; - error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_reference_target(old_head), 1); + error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_reference_target(old_head), + 1, signature, reflog_message); cleanup: git_object_free(object); @@ -1956,8 +2009,8 @@ state = GIT_REPOSITORY_STATE_MERGE; else if(git_path_contains_file(&repo_path, GIT_REVERT_HEAD_FILE)) state = GIT_REPOSITORY_STATE_REVERT; - else if(git_path_contains_file(&repo_path, GIT_CHERRY_PICK_HEAD_FILE)) - state = GIT_REPOSITORY_STATE_CHERRY_PICK; + else if(git_path_contains_file(&repo_path, GIT_CHERRYPICK_HEAD_FILE)) + state = GIT_REPOSITORY_STATE_CHERRYPICK; else if(git_path_contains_file(&repo_path, GIT_BISECT_LOG_FILE)) state = GIT_REPOSITORY_STATE_BISECT; @@ -1965,19 +2018,80 @@ return state; } +int git_repository__cleanup_files( + git_repository *repo, const char *files[], size_t files_len) +{ + git_buf buf = GIT_BUF_INIT; + size_t i; + int error; + + for (error = 0, i = 0; !error && i < files_len; ++i) { + const char *path; + + if (git_buf_joinpath(&buf, repo->path_repository, files[i]) < 0) + return -1; + + path = git_buf_cstr(&buf); + + if (git_path_isfile(path)) { + error = p_unlink(path); + } else if (git_path_isdir(path)) { + error = git_futils_rmdir_r(path, NULL, + GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS); + } + + git_buf_clear(&buf); + } + + git_buf_free(&buf); + return error; +} + +static const char *state_files[] = { + GIT_MERGE_HEAD_FILE, + GIT_MERGE_MODE_FILE, + GIT_MERGE_MSG_FILE, + GIT_REVERT_HEAD_FILE, + GIT_CHERRYPICK_HEAD_FILE, + GIT_BISECT_LOG_FILE, + GIT_REBASE_MERGE_DIR, + GIT_REBASE_APPLY_DIR, +}; + +int git_repository_state_cleanup(git_repository *repo) +{ + assert(repo); + + return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files)); +} + int git_repository_is_shallow(git_repository *repo) { git_buf path = GIT_BUF_INIT; struct stat st; int error; - git_buf_joinpath(&path, repo->path_repository, "shallow"); + if ((error = git_buf_joinpath(&path, repo->path_repository, "shallow")) < 0) + return error; + error = git_path_lstat(path.ptr, &st); git_buf_free(&path); - if (error == GIT_ENOTFOUND) + if (error == GIT_ENOTFOUND) { + giterr_clear(); return 0; + } + if (error < 0) - return -1; + return error; return st.st_size == 0 ? 0 : 1; } + +int git_repository_init_init_options( + git_repository_init_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_repository_init_options, + GIT_REPOSITORY_INIT_OPTIONS_INIT); + return 0; +} diff -Nru libgit2-0.20.0/src/repository.h libgit2-0.22.2/src/repository.h --- libgit2-0.20.0/src/repository.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/repository.h 2015-03-24 16:10:45.000000000 +0000 @@ -19,7 +19,7 @@ #include "buffer.h" #include "object.h" #include "attrcache.h" -#include "strmap.h" +#include "submodule.h" #include "diff_driver.h" #define DOT_GIT ".git" @@ -38,6 +38,10 @@ GIT_CVAR_TRUSTCTIME, /* core.trustctime */ GIT_CVAR_ABBREV, /* core.abbrev */ GIT_CVAR_PRECOMPOSE, /* core.precomposeunicode */ + GIT_CVAR_SAFE_CRLF, /* core.safecrlf */ + GIT_CVAR_LOGALLREFUPDATES, /* core.logallrefupdates */ + GIT_CVAR_PROTECTHFS, /* core.protectHFS */ + GIT_CVAR_PROTECTNTFS, /* core.protectNTFS */ GIT_CVAR_CACHE_MAX } git_cvar_cached; @@ -89,7 +93,15 @@ GIT_ABBREV_DEFAULT = 7, /* core.precomposeunicode */ GIT_PRECOMPOSE_DEFAULT = GIT_CVAR_FALSE, - + /* core.safecrlf */ + GIT_SAFE_CRLF_DEFAULT = GIT_CVAR_FALSE, + /* core.logallrefupdates */ + GIT_LOGALLREFUPDATES_UNSET = 2, + GIT_LOGALLREFUPDATES_DEFAULT = GIT_LOGALLREFUPDATES_UNSET, + /* core.protectHFS */ + GIT_PROTECTHFS_DEFAULT = GIT_CVAR_FALSE, + /* core.protectNTFS */ + GIT_PROTECTNTFS_DEFAULT = GIT_CVAR_FALSE, } git_cvar_value; /* internal repository init flags */ @@ -105,17 +117,20 @@ git_refdb *_refdb; git_config *_config; git_index *_index; + git_submodule_cache *_submodules; git_cache objects; - git_attr_cache attrcache; - git_strmap *submodules; + git_attr_cache *attrcache; git_diff_driver_registry *diff_drivers; char *path_repository; char *workdir; char *namespace; + char *name_8dot3; - unsigned is_bare:1; + unsigned is_bare:1, + has_8dot3:1, + has_8dot3_default:1; unsigned int lru_counter; git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX]; @@ -123,7 +138,7 @@ GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo) { - return &repo->attrcache; + return repo->attrcache; } int git_repository_head_tree(git_tree **tree, git_repository *repo); @@ -144,16 +159,11 @@ * CVAR cache * * Efficient access to the most used config variables of a repository. - * The cache is cleared everytime the config backend is replaced. + * The cache is cleared every time the config backend is replaced. */ int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar); void git_repository__cvar_cache_clear(git_repository *repo); -/* - * Submodule cache - */ -extern void git_submodule_config_free(git_repository *repo); - GIT_INLINE(int) git_repository__ensure_not_bare( git_repository *repo, const char *operation_name) @@ -169,4 +179,23 @@ return GIT_EBAREREPO; } +int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head); + +int git_repository__cleanup_files(git_repository *repo, const char *files[], size_t files_len); + +/* + * Gets the DOS-compatible 8.3 "short name". This will return only the + * short name for the repository directory (ie, "git~1" for ".git"). This + * will always return a pointer to `git_repository__8dot3_default` when + * "GIT~1" is the short name. This will return NULL for bare repositories, + * and systems that do not have a short name. + */ +const char *git_repository__8dot3_name(git_repository *repo); + +/* The default DOS-compatible 8.3 "short name" for a git repository, + * "GIT~1". + */ +extern const char *git_repository__8dot3_default; +extern size_t git_repository__8dot3_default_len; + #endif diff -Nru libgit2-0.20.0/src/reset.c libgit2-0.22.2/src/reset.c --- libgit2-0.20.0/src/reset.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/reset.c 2015-03-24 16:10:45.000000000 +0000 @@ -60,19 +60,24 @@ for (i = 0, max_i = git_diff_num_deltas(diff); i < max_i; ++i) { const git_diff_delta *delta = git_diff_get_delta(diff, i); - if ((error = git_index_conflict_remove(index, delta->old_file.path)) < 0) - goto cleanup; - assert(delta->status == GIT_DELTA_ADDED || delta->status == GIT_DELTA_MODIFIED || delta->status == GIT_DELTA_DELETED); + error = git_index_conflict_remove(index, delta->old_file.path); + if (error < 0) { + if (delta->status == GIT_DELTA_ADDED && error == GIT_ENOTFOUND) + giterr_clear(); + else + goto cleanup; + } + if (delta->status == GIT_DELTA_DELETED) { if ((error = git_index_remove(index, delta->old_file.path, 0)) < 0) goto cleanup; } else { entry.mode = delta->new_file.mode; - git_oid_cpy(&entry.oid, &delta->new_file.oid); + git_oid_cpy(&entry.id, &delta->new_file.id); entry.path = (char *)delta->new_file.path; if ((error = git_index_add(index, &entry)) < 0) @@ -94,16 +99,23 @@ int git_reset( git_repository *repo, git_object *target, - git_reset_t reset_type) + git_reset_t reset_type, + git_checkout_options *checkout_opts, + const git_signature *signature, + const char *log_message) { git_object *commit = NULL; git_index *index = NULL; git_tree *tree = NULL; int error = 0; - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_buf log_message_buf = GIT_BUF_INIT; assert(repo && target); + if (checkout_opts) + opts = *checkout_opts; + if (git_object_owner(target) != repo) { giterr_set(GITERR_OBJECT, "%s - The given target does not belong to this repository.", ERROR_MSG); @@ -129,9 +141,14 @@ goto cleanup; } + if (log_message) + git_buf_sets(&log_message_buf, log_message); + else + git_buf_sets(&log_message_buf, "reset: moving"); + /* move HEAD to the new target */ if ((error = git_reference__update_terminal(repo, GIT_HEAD_FILE, - git_object_id(commit))) < 0) + git_object_id(commit), signature, git_buf_cstr(&log_message_buf))) < 0) goto cleanup; if (reset_type == GIT_RESET_HARD) { @@ -149,7 +166,7 @@ (error = git_index_write(index)) < 0) goto cleanup; - if ((error = git_repository_merge_cleanup(repo)) < 0) { + if ((error = git_repository_state_cleanup(repo)) < 0) { giterr_set(GITERR_INDEX, "%s - failed to clean up merge data", ERROR_MSG); goto cleanup; } @@ -159,6 +176,7 @@ git_object_free(commit); git_index_free(index); git_tree_free(tree); + git_buf_free(&log_message_buf); return error; } diff -Nru libgit2-0.20.0/src/revert.c libgit2-0.22.2/src/revert.c --- libgit2-0.20.0/src/revert.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/revert.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,226 @@ +/* +* 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 "repository.h" +#include "filebuf.h" +#include "merge.h" + +#include "git2/types.h" +#include "git2/merge.h" +#include "git2/revert.h" +#include "git2/commit.h" +#include "git2/sys/commit.h" + +#define GIT_REVERT_FILE_MODE 0666 + +static int write_revert_head( + git_repository *repo, + const char *commit_oidstr) +{ + git_filebuf file = GIT_FILEBUF_INIT; + git_buf file_path = GIT_BUF_INIT; + int error = 0; + + if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_REVERT_HEAD_FILE)) >= 0 && + (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_REVERT_FILE_MODE)) >= 0 && + (error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0) + error = git_filebuf_commit(&file); + + if (error < 0) + git_filebuf_cleanup(&file); + + git_buf_free(&file_path); + + return error; +} + +static int write_merge_msg( + git_repository *repo, + const char *commit_oidstr, + const char *commit_msgline) +{ + git_filebuf file = GIT_FILEBUF_INIT; + git_buf file_path = GIT_BUF_INIT; + int error = 0; + + if ((error = git_buf_joinpath(&file_path, repo->path_repository, GIT_MERGE_MSG_FILE)) < 0 || + (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_REVERT_FILE_MODE)) < 0 || + (error = git_filebuf_printf(&file, "Revert \"%s\"\n\nThis reverts commit %s.\n", + commit_msgline, commit_oidstr)) < 0) + goto cleanup; + + error = git_filebuf_commit(&file); + +cleanup: + if (error < 0) + git_filebuf_cleanup(&file); + + git_buf_free(&file_path); + + return error; +} + +static int revert_normalize_opts( + git_repository *repo, + git_revert_options *opts, + const git_revert_options *given, + const char *their_label) +{ + int error = 0; + unsigned int default_checkout_strategy = GIT_CHECKOUT_SAFE_CREATE | + GIT_CHECKOUT_ALLOW_CONFLICTS; + + GIT_UNUSED(repo); + + if (given != NULL) + memcpy(opts, given, sizeof(git_revert_options)); + else { + git_revert_options default_opts = GIT_REVERT_OPTIONS_INIT; + memcpy(opts, &default_opts, sizeof(git_revert_options)); + } + + if (!opts->checkout_opts.checkout_strategy) + opts->checkout_opts.checkout_strategy = default_checkout_strategy; + + if (!opts->checkout_opts.our_label) + opts->checkout_opts.our_label = "HEAD"; + + if (!opts->checkout_opts.their_label) + opts->checkout_opts.their_label = their_label; + + return error; +} + +static int revert_state_cleanup(git_repository *repo) +{ + const char *state_files[] = { GIT_REVERT_HEAD_FILE, GIT_MERGE_MSG_FILE }; + + return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files)); +} + +static int revert_seterr(git_commit *commit, const char *fmt) +{ + char commit_oidstr[GIT_OID_HEXSZ + 1]; + + git_oid_fmt(commit_oidstr, git_commit_id(commit)); + commit_oidstr[GIT_OID_HEXSZ] = '\0'; + + giterr_set(GITERR_REVERT, fmt, commit_oidstr); + + return -1; +} + +int git_revert_commit( + git_index **out, + git_repository *repo, + git_commit *revert_commit, + git_commit *our_commit, + unsigned int mainline, + const git_merge_options *merge_opts) +{ + git_commit *parent_commit = NULL; + git_tree *parent_tree = NULL, *our_tree = NULL, *revert_tree = NULL; + int parent = 0, error = 0; + + assert(out && repo && revert_commit && our_commit); + + if (git_commit_parentcount(revert_commit) > 1) { + if (!mainline) + return revert_seterr(revert_commit, + "Mainline branch is not specified but %s is a merge commit"); + + parent = mainline; + } else { + if (mainline) + return revert_seterr(revert_commit, + "Mainline branch specified but %s is not a merge commit"); + + parent = git_commit_parentcount(revert_commit); + } + + if (parent && + ((error = git_commit_parent(&parent_commit, revert_commit, (parent - 1))) < 0 || + (error = git_commit_tree(&parent_tree, parent_commit)) < 0)) + goto done; + + if ((error = git_commit_tree(&revert_tree, revert_commit)) < 0 || + (error = git_commit_tree(&our_tree, our_commit)) < 0) + goto done; + + error = git_merge_trees(out, repo, revert_tree, our_tree, parent_tree, merge_opts); + +done: + git_tree_free(parent_tree); + git_tree_free(our_tree); + git_tree_free(revert_tree); + git_commit_free(parent_commit); + + return error; +} + +int git_revert( + git_repository *repo, + git_commit *commit, + const git_revert_options *given_opts) +{ + git_revert_options opts; + git_reference *our_ref = NULL; + git_commit *our_commit = NULL; + char commit_oidstr[GIT_OID_HEXSZ + 1]; + const char *commit_msg; + git_buf their_label = GIT_BUF_INIT; + git_index *index_new = NULL; + int error; + + assert(repo && commit); + + GITERR_CHECK_VERSION(given_opts, GIT_REVERT_OPTIONS_VERSION, "git_revert_options"); + + if ((error = git_repository__ensure_not_bare(repo, "revert")) < 0) + return error; + + git_oid_fmt(commit_oidstr, git_commit_id(commit)); + commit_oidstr[GIT_OID_HEXSZ] = '\0'; + + if ((commit_msg = git_commit_summary(commit)) == NULL) { + error = -1; + goto on_error; + } + + if ((error = git_buf_printf(&their_label, "parent of %.7s... %s", commit_oidstr, commit_msg)) < 0 || + (error = revert_normalize_opts(repo, &opts, given_opts, git_buf_cstr(&their_label))) < 0 || + (error = write_revert_head(repo, commit_oidstr)) < 0 || + (error = write_merge_msg(repo, commit_oidstr, commit_msg)) < 0 || + (error = git_repository_head(&our_ref, repo)) < 0 || + (error = git_reference_peel((git_object **)&our_commit, our_ref, GIT_OBJ_COMMIT)) < 0 || + (error = git_revert_commit(&index_new, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 || + (error = git_merge__check_result(repo, index_new)) < 0 || + (error = git_merge__append_conflicts_to_merge_msg(repo, index_new)) < 0 || + (error = git_checkout_index(repo, index_new, &opts.checkout_opts)) < 0) + goto on_error; + + goto done; + +on_error: + revert_state_cleanup(repo); + +done: + git_index_free(index_new); + git_commit_free(our_commit); + git_reference_free(our_ref); + git_buf_free(&their_label); + + return error; +} + +int git_revert_init_options(git_revert_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_revert_options, GIT_REVERT_OPTIONS_INIT); + return 0; +} diff -Nru libgit2-0.20.0/src/revparse.c libgit2-0.22.2/src/revparse.c --- libgit2-0.20.0/src/revparse.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/revparse.c 2015-03-24 16:10:45.000000000 +0000 @@ -168,6 +168,8 @@ for (i = 0; i < numentries; i++) { entry = git_reflog_entry_byindex(reflog, i); msg = git_reflog_entry_message(entry); + if (!msg) + continue; if (regexec(&preg, msg, 2, regexmatches, 0)) continue; @@ -203,7 +205,6 @@ static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t identifier) { git_reflog *reflog; - int error = -1; size_t numentries; const git_reflog_entry *entry; bool search_by_pos = (identifier <= 100000000); @@ -214,21 +215,11 @@ numentries = git_reflog_entrycount(reflog); if (search_by_pos) { - if (numentries < identifier + 1) { - giterr_set( - GITERR_REFERENCE, - "Reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ, - git_reference_name(ref), numentries, identifier); - - error = GIT_ENOTFOUND; - goto cleanup; - } + if (numentries < identifier + 1) + goto notfound; entry = git_reflog_entry_byindex(reflog, identifier); git_oid_cpy(oid, git_reflog_entry_id_new(entry)); - error = 0; - goto cleanup; - } else { size_t i; git_time commit_time; @@ -241,16 +232,24 @@ continue; git_oid_cpy(oid, git_reflog_entry_id_new(entry)); - error = 0; - goto cleanup; + break; } - error = GIT_ENOTFOUND; + if (i == numentries) + goto notfound; } -cleanup: git_reflog_free(reflog); - return error; + return 0; + +notfound: + giterr_set( + GITERR_REFERENCE, + "Reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ, + git_reference_name(ref), numentries, identifier); + + git_reflog_free(reflog); + return GIT_ENOTFOUND; } static int retrieve_revobject_from_reflog(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, size_t position) @@ -488,8 +487,7 @@ git_revwalk_sorting(walk, GIT_SORT_TIME); if (spec_oid == NULL) { - // TODO: @carlosmn: The glob should be refs/* but this makes git_revwalk_next() fails - if ((error = git_revwalk_push_glob(walk, GIT_REFS_HEADS_DIR "*")) < 0) + if ((error = git_revwalk_push_glob(walk, "refs/*")) < 0) goto cleanup; } else if ((error = git_revwalk_push(walk, spec_oid)) < 0) goto cleanup; diff -Nru libgit2-0.20.0/src/revwalk.c libgit2-0.22.2/src/revwalk.c --- libgit2-0.20.0/src/revwalk.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/revwalk.c 2015-03-24 16:10:45.000000000 +0000 @@ -39,26 +39,20 @@ return commit; } -static int mark_uninteresting(git_commit_list_node *commit) +static int mark_uninteresting(git_revwalk *walk, git_commit_list_node *commit) { + int error; unsigned short i; git_array_t(git_commit_list_node *) pending = GIT_ARRAY_INIT; git_commit_list_node **tmp; assert(commit); - git_array_alloc(pending); - GITERR_CHECK_ARRAY(pending); - do { commit->uninteresting = 1; - /* This means we've reached a merge base, so there's no need to walk any more */ - if ((commit->flags & (RESULT | STALE)) == RESULT) { - tmp = git_array_pop(pending); - commit = tmp ? *tmp : NULL; - continue; - } + if ((error = git_commit_list_parse(walk, commit)) < 0) + return error; for (i = 0; i < commit->out_degree; ++i) if (!commit->parents[i]->uninteresting) { @@ -70,7 +64,7 @@ tmp = git_array_pop(pending); commit = tmp ? *tmp : NULL; - } while (git_array_size(pending) > 0); + } while (commit != NULL); git_array_clear(pending); @@ -81,7 +75,10 @@ { int error; - if (hide && mark_uninteresting(commit) < 0) + if (!hide && walk->hide_cb) + hide = walk->hide_cb(&commit->oid, walk->hide_cb_payload); + + if (hide && mark_uninteresting(walk, commit) < 0) return -1; if (commit->seen) @@ -92,7 +89,10 @@ if ((error = git_commit_list_parse(walk, commit)) < 0) return error; - return walk->enqueue(walk, commit); + if (!hide) + return walk->enqueue(walk, commit); + + return 0; } static int process_commit_parents(git_revwalk *walk, git_commit_list_node *commit) @@ -110,78 +110,84 @@ return error; } -static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting) +static int push_commit(git_revwalk *walk, const git_oid *oid, int uninteresting, int from_glob) { - git_object *obj; - git_otype type; + git_oid commit_id; + int error; + git_object *obj, *oobj; git_commit_list_node *commit; + git_commit_list *list; - if (git_object_lookup(&obj, walk->repo, oid, GIT_OBJ_ANY) < 0) - return -1; + if ((error = git_object_lookup(&oobj, walk->repo, oid, GIT_OBJ_ANY)) < 0) + return error; - type = git_object_type(obj); - git_object_free(obj); + error = git_object_peel(&obj, oobj, GIT_OBJ_COMMIT); + git_object_free(oobj); - if (type != GIT_OBJ_COMMIT) { - giterr_set(GITERR_INVALID, "Object is no commit object"); + if (error == GIT_ENOTFOUND || error == GIT_EINVALIDSPEC || error == GIT_EPEEL) { + /* If this comes from e.g. push_glob("tags"), ignore this */ + if (from_glob) + return 0; + + giterr_set(GITERR_INVALID, "Object is not a committish"); return -1; } + if (error < 0) + return error; + + git_oid_cpy(&commit_id, git_object_id(obj)); + git_object_free(obj); - commit = git_revwalk__commit_lookup(walk, oid); + commit = git_revwalk__commit_lookup(walk, &commit_id); if (commit == NULL) return -1; /* error already reported by failed lookup */ + if (uninteresting) + walk->did_hide = 1; + else + walk->did_push = 1; + commit->uninteresting = uninteresting; - if (walk->one == NULL && !uninteresting) { - walk->one = commit; - } else { - if (git_vector_insert(&walk->twos, commit) < 0) - return -1; + list = walk->user_input; + if (git_commit_list_insert(commit, &list) == NULL) { + giterr_set_oom(); + return -1; } + walk->user_input = list; + return 0; } int git_revwalk_push(git_revwalk *walk, const git_oid *oid) { assert(walk && oid); - return push_commit(walk, oid, 0); + return push_commit(walk, oid, 0, false); } int git_revwalk_hide(git_revwalk *walk, const git_oid *oid) { assert(walk && oid); - return push_commit(walk, oid, 1); + return push_commit(walk, oid, 1, false); } -static int push_ref(git_revwalk *walk, const char *refname, int hide) +static int push_ref(git_revwalk *walk, const char *refname, int hide, int from_glob) { git_oid oid; if (git_reference_name_to_id(&oid, walk->repo, refname) < 0) return -1; - return push_commit(walk, &oid, hide); -} - -struct push_cb_data { - git_revwalk *walk; - int hide; -}; - -static int push_glob_cb(const char *refname, void *data_) -{ - struct push_cb_data *data = (struct push_cb_data *)data_; - - return push_ref(data->walk, refname, data->hide); + return push_commit(walk, &oid, hide, from_glob); } static int push_glob(git_revwalk *walk, const char *glob, int hide) { int error = 0; git_buf buf = GIT_BUF_INIT; - struct push_cb_data data; + git_reference *ref; + git_reference_iterator *iter; size_t wildcard; assert(walk && glob); @@ -191,21 +197,28 @@ git_buf_joinpath(&buf, GIT_REFS_DIR, glob); else git_buf_puts(&buf, glob); + if (git_buf_oom(&buf)) + return -1; /* If no '?', '*' or '[' exist, we append '/ *' to the glob */ wildcard = strcspn(glob, "?*["); if (!glob[wildcard]) git_buf_put(&buf, "/*", 2); - data.walk = walk; - data.hide = hide; + if ((error = git_reference_iterator_glob_new(&iter, walk->repo, buf.ptr)) < 0) + goto out; - if (git_buf_oom(&buf)) - error = -1; - else - error = git_reference_foreach_glob( - walk->repo, git_buf_cstr(&buf), push_glob_cb, &data); + while ((error = git_reference_next(&ref, iter)) == 0) { + error = push_ref(walk, git_reference_name(ref), hide, true); + git_reference_free(ref); + if (error < 0) + break; + } + git_reference_iterator_free(iter); + if (error == GIT_ITEROVER) + error = 0; +out: git_buf_free(&buf); return error; } @@ -225,19 +238,19 @@ int git_revwalk_push_head(git_revwalk *walk) { assert(walk); - return push_ref(walk, GIT_HEAD_FILE, 0); + return push_ref(walk, GIT_HEAD_FILE, 0, false); } int git_revwalk_hide_head(git_revwalk *walk) { assert(walk); - return push_ref(walk, GIT_HEAD_FILE, 1); + return push_ref(walk, GIT_HEAD_FILE, 1, false); } int git_revwalk_push_ref(git_revwalk *walk, const char *refname) { assert(walk && refname); - return push_ref(walk, refname, 0); + return push_ref(walk, refname, 0, false); } int git_revwalk_push_range(git_revwalk *walk, const char *range) @@ -254,10 +267,10 @@ return GIT_EINVALIDSPEC; } - if ((error = push_commit(walk, git_object_id(revspec.from), 1))) + if ((error = push_commit(walk, git_object_id(revspec.from), 1, false))) goto out; - error = push_commit(walk, git_object_id(revspec.to), 0); + error = push_commit(walk, git_object_id(revspec.to), 0, false); out: git_object_free(revspec.from); @@ -268,7 +281,7 @@ int git_revwalk_hide_ref(git_revwalk *walk, const char *refname) { assert(walk && refname); - return push_ref(walk, refname, 1); + return push_ref(walk, refname, 1, false); } static int revwalk_enqueue_timesort(git_revwalk *walk, git_commit_list_node *commit) @@ -286,15 +299,14 @@ int error; git_commit_list_node *next; - while ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) { - if ((error = process_commit_parents(walk, next)) < 0) - return error; - + while ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) if (!next->uninteresting) { + if ((error = process_commit_parents(walk, next)) < 0) + return error; + *object_out = next; return 0; } - } giterr_clear(); return GIT_ITEROVER; @@ -305,15 +317,14 @@ int error; git_commit_list_node *next; - while ((next = git_commit_list_pop(&walk->iterator_rand)) != NULL) { - if ((error = process_commit_parents(walk, next)) < 0) - return error; - + while ((next = git_commit_list_pop(&walk->iterator_rand)) != NULL) if (!next->uninteresting) { + if ((error = process_commit_parents(walk, next)) < 0) + return error; + *object_out = next; return 0; } - } giterr_clear(); return GIT_ITEROVER; @@ -363,35 +374,97 @@ } +static int interesting(git_pqueue *list) +{ + size_t i; + + for (i = 0; i < git_pqueue_size(list); i++) { + git_commit_list_node *commit = git_pqueue_get(list, i); + if (!commit->uninteresting) + return 1; + } + + return 0; +} + +static int contains(git_pqueue *list, git_commit_list_node *node) +{ + size_t i; + + for (i = 0; i < git_pqueue_size(list); i++) { + git_commit_list_node *commit = git_pqueue_get(list, i); + if (commit == node) + return 1; + } + + return 0; +} + +static int premark_uninteresting(git_revwalk *walk) +{ + int error = 0; + unsigned short i; + git_pqueue q; + git_commit_list *list; + git_commit_list_node *commit, *parent; + + if ((error = git_pqueue_init(&q, 0, 8, git_commit_list_time_cmp)) < 0) + return error; + + for (list = walk->user_input; list; list = list->next) { + if ((error = git_commit_list_parse(walk, list->item)) < 0) + goto cleanup; + + if ((error = git_pqueue_insert(&q, list->item)) < 0) + goto cleanup; + } + + while (interesting(&q)) { + commit = git_pqueue_pop(&q); + + for (i = 0; i < commit->out_degree; i++) { + parent = commit->parents[i]; + + if ((error = git_commit_list_parse(walk, parent)) < 0) + goto cleanup; + + if (commit->uninteresting) + parent->uninteresting = 1; + + if (contains(&q, parent)) + continue; + + if ((error = git_pqueue_insert(&q, parent)) < 0) + goto cleanup; + } + } + +cleanup: + git_pqueue_free(&q); + return error; +} + static int prepare_walk(git_revwalk *walk) { int error; - unsigned int i; - git_commit_list_node *next, *two; - git_commit_list *bases = NULL; - - /* - * If walk->one is NULL, there were no positive references, - * so we know that the walk is already over. - */ - if (walk->one == NULL) { + git_commit_list *list; + git_commit_list_node *next; + + /* If there were no pushes, we know that the walk is already over */ + if (!walk->did_push) { giterr_clear(); return GIT_ITEROVER; } - /* first figure out what the merge bases are */ - if (git_merge__bases_many(&bases, walk, walk->one, &walk->twos) < 0) - return -1; - - git_commit_list_free(&bases); - if (process_commit(walk, walk->one, walk->one->uninteresting) < 0) - return -1; + if (walk->did_hide && (error = premark_uninteresting(walk)) < 0) + return error; - git_vector_foreach(&walk->twos, i, two) { - if (process_commit(walk, two, two->uninteresting) < 0) + for (list = walk->user_input; list; list = list->next) { + if (process_commit(walk, list->item, list->item->uninteresting) < 0) return -1; } + if (walk->sorting & GIT_SORT_TOPOLOGICAL) { unsigned short i; @@ -440,8 +513,8 @@ walk->commits = git_oidmap_alloc(); GITERR_CHECK_ALLOC(walk->commits); - if (git_pqueue_init(&walk->iterator_time, 8, git_commit_list_time_cmp) < 0 || - git_vector_init(&walk->twos, 4, NULL) < 0 || + if (git_pqueue_init( + &walk->iterator_time, 0, 8, git_commit_list_time_cmp) < 0 || git_pool_init(&walk->commit_pool, 1, git_pool__suggest_items_per_page(COMMIT_ALLOC) * COMMIT_ALLOC) < 0) return -1; @@ -471,7 +544,6 @@ git_oidmap_free(walk->commits); git_pool_clear(&walk->commit_pool); git_pqueue_free(&walk->iterator_time); - git_vector_free(&walk->twos); git__free(walk); } @@ -541,15 +613,38 @@ commit->in_degree = 0; commit->topo_delay = 0; commit->uninteresting = 0; + commit->flags = 0; }); git_pqueue_clear(&walk->iterator_time); git_commit_list_free(&walk->iterator_topo); git_commit_list_free(&walk->iterator_rand); git_commit_list_free(&walk->iterator_reverse); + git_commit_list_free(&walk->user_input); + walk->first_parent = 0; walk->walking = 0; + walk->did_push = walk->did_hide = 0; +} + +int git_revwalk_add_hide_cb( + git_revwalk *walk, + git_revwalk_hide_cb hide_cb, + void *payload) +{ + assert(walk); + + if (walk->walking) + git_revwalk_reset(walk); + + if (walk->hide_cb) { + /* There is already a callback added */ + giterr_set(GITERR_INVALID, "There is already a callback added to hide commits in revision walker."); + return -1; + } + + walk->hide_cb = hide_cb; + walk->hide_cb_payload = payload; - walk->one = NULL; - git_vector_clear(&walk->twos); + return 0; } diff -Nru libgit2-0.20.0/src/revwalk.h libgit2-0.22.2/src/revwalk.h --- libgit2-0.20.0/src/revwalk.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/revwalk.h 2015-03-24 16:10:45.000000000 +0000 @@ -32,12 +32,17 @@ int (*enqueue)(git_revwalk *, git_commit_list_node *); unsigned walking:1, - first_parent: 1; + first_parent: 1, + did_hide: 1, + did_push: 1; unsigned int sorting; - /* merge base calculation */ - git_commit_list_node *one; - git_vector twos; + /* the pushes and hides */ + git_commit_list *user_input; + + /* hide callback */ + git_revwalk_hide_cb hide_cb; + void *hide_cb_payload; }; git_commit_list_node *git_revwalk__commit_lookup(git_revwalk *walk, const git_oid *oid); diff -Nru libgit2-0.20.0/src/settings.c libgit2-0.22.2/src/settings.c --- libgit2-0.20.0/src/settings.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/settings.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,162 @@ +/* + * 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_SSL +# include +#endif + +#include +#include "common.h" +#include "sysdir.h" +#include "cache.h" +#include "global.h" + +void git_libgit2_version(int *major, int *minor, int *rev) +{ + *major = LIBGIT2_VER_MAJOR; + *minor = LIBGIT2_VER_MINOR; + *rev = LIBGIT2_VER_REVISION; +} + +int git_libgit2_features() +{ + return 0 +#ifdef GIT_THREADS + | GIT_FEATURE_THREADS +#endif +#if defined(GIT_SSL) || defined(GIT_WINHTTP) + | GIT_FEATURE_HTTPS +#endif +#if defined(GIT_SSH) + | GIT_FEATURE_SSH +#endif + ; +} + +/* Declarations for tuneable settings */ +extern size_t git_mwindow__window_size; +extern size_t git_mwindow__mapped_limit; + +static int config_level_to_sysdir(int config_level) +{ + int val = -1; + + switch (config_level) { + case GIT_CONFIG_LEVEL_SYSTEM: val = GIT_SYSDIR_SYSTEM; break; + case GIT_CONFIG_LEVEL_XDG: val = GIT_SYSDIR_XDG; break; + case GIT_CONFIG_LEVEL_GLOBAL: val = GIT_SYSDIR_GLOBAL; break; + default: + giterr_set( + GITERR_INVALID, "Invalid config path selector %d", config_level); + } + + return val; +} + +int git_libgit2_opts(int key, ...) +{ + int error = 0; + va_list ap; + + va_start(ap, key); + + switch (key) { + case GIT_OPT_SET_MWINDOW_SIZE: + git_mwindow__window_size = va_arg(ap, size_t); + break; + + case GIT_OPT_GET_MWINDOW_SIZE: + *(va_arg(ap, size_t *)) = git_mwindow__window_size; + break; + + case GIT_OPT_SET_MWINDOW_MAPPED_LIMIT: + git_mwindow__mapped_limit = va_arg(ap, size_t); + break; + + case GIT_OPT_GET_MWINDOW_MAPPED_LIMIT: + *(va_arg(ap, size_t *)) = git_mwindow__mapped_limit; + break; + + case GIT_OPT_GET_SEARCH_PATH: + if ((error = config_level_to_sysdir(va_arg(ap, int))) >= 0) { + git_buf *out = va_arg(ap, git_buf *); + const git_buf *tmp; + + git_buf_sanitize(out); + if ((error = git_sysdir_get(&tmp, error)) < 0) + break; + + error = git_buf_sets(out, tmp->ptr); + } + break; + + case GIT_OPT_SET_SEARCH_PATH: + if ((error = config_level_to_sysdir(va_arg(ap, int))) >= 0) + error = git_sysdir_set(error, va_arg(ap, const char *)); + break; + + case GIT_OPT_SET_CACHE_OBJECT_LIMIT: + { + git_otype type = (git_otype)va_arg(ap, int); + size_t size = va_arg(ap, size_t); + error = git_cache_set_max_object_size(type, size); + break; + } + + case GIT_OPT_SET_CACHE_MAX_SIZE: + git_cache__max_storage = va_arg(ap, ssize_t); + break; + + case GIT_OPT_ENABLE_CACHING: + git_cache__enabled = (va_arg(ap, int) != 0); + break; + + case GIT_OPT_GET_CACHED_MEMORY: + *(va_arg(ap, ssize_t *)) = git_cache__current_storage.val; + *(va_arg(ap, ssize_t *)) = git_cache__max_storage; + break; + + case GIT_OPT_GET_TEMPLATE_PATH: + { + git_buf *out = va_arg(ap, git_buf *); + const git_buf *tmp; + + git_buf_sanitize(out); + if ((error = git_sysdir_get(&tmp, GIT_SYSDIR_TEMPLATE)) < 0) + break; + + error = git_buf_sets(out, tmp->ptr); + } + break; + + case GIT_OPT_SET_TEMPLATE_PATH: + error = git_sysdir_set(GIT_SYSDIR_TEMPLATE, va_arg(ap, const char *)); + break; + + case GIT_OPT_SET_SSL_CERT_LOCATIONS: +#ifdef GIT_SSL + { + 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; + } + } +#else + giterr_set(GITERR_NET, "Cannot set certificate locations: OpenSSL is not enabled"); + error = -1; +#endif + break; + } + + va_end(ap); + + return error; +} + diff -Nru libgit2-0.20.0/src/signature.c libgit2-0.22.2/src/signature.c --- libgit2-0.20.0/src/signature.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/signature.c 2015-03-24 16:10:45.000000000 +0000 @@ -70,9 +70,9 @@ if (p->name == NULL || p->email == NULL) return -1; /* oom */ - if (p->name[0] == '\0') { + if (p->name[0] == '\0' || p->email[0] == '\0') { git_signature_free(p); - return signature_error("Signature cannot have an empty name"); + return signature_error("Signature cannot have an empty name or email"); } p->when.time = time; @@ -82,23 +82,52 @@ return 0; } -git_signature *git_signature_dup(const git_signature *sig) +int git_signature_dup(git_signature **dest, const git_signature *source) { - git_signature *new; + git_signature *signature; - if (sig == NULL) - return NULL; + if (source == NULL) + return 0; - new = git__calloc(1, sizeof(git_signature)); - if (new == NULL) - return NULL; + signature = git__calloc(1, sizeof(git_signature)); + GITERR_CHECK_ALLOC(signature); - new->name = git__strdup(sig->name); - new->email = git__strdup(sig->email); - new->when.time = sig->when.time; - new->when.offset = sig->when.offset; + signature->name = git__strdup(source->name); + GITERR_CHECK_ALLOC(signature->name); - return new; + signature->email = git__strdup(source->email); + GITERR_CHECK_ALLOC(signature->email); + + signature->when.time = source->when.time; + signature->when.offset = source->when.offset; + + *dest = signature; + + return 0; +} + +int git_signature__pdup(git_signature **dest, const git_signature *source, git_pool *pool) +{ + git_signature *signature; + + if (source == NULL) + return 0; + + signature = git_pool_mallocz(pool, sizeof(git_signature)); + GITERR_CHECK_ALLOC(signature); + + signature->name = git_pool_strdup(pool, source->name); + GITERR_CHECK_ALLOC(signature->name); + + signature->email = git_pool_strdup(pool, source->email); + GITERR_CHECK_ALLOC(signature->email); + + signature->when.time = source->when.time; + signature->when.offset = source->when.offset; + + *dest = signature; + + return 0; } int git_signature_now(git_signature **sig_out, const char *name, const char *email) @@ -139,7 +168,7 @@ git_config *cfg; const char *user_name, *user_email; - if ((error = git_repository_config(&cfg, repo)) < 0) + if ((error = git_repository_config_snapshot(&cfg, repo)) < 0) return error; if (!(error = git_config_get_string(&user_name, cfg, "user.name")) && @@ -225,6 +254,8 @@ int offset, hours, mins; char sign; + assert(buf && sig); + offset = sig->when.offset; sign = (sig->when.offset < 0) ? '-' : '+'; @@ -239,3 +270,14 @@ (unsigned)sig->when.time, sign, hours, mins); } +bool git_signature__equal(const git_signature *one, const git_signature *two) +{ + assert(one && two); + + return + 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; +} + diff -Nru libgit2-0.20.0/src/signature.h libgit2-0.22.2/src/signature.h --- libgit2-0.20.0/src/signature.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/signature.h 2015-03-24 16:10:45.000000000 +0000 @@ -14,5 +14,8 @@ int git_signature__parse(git_signature *sig, const char **buffer_out, const char *buffer_end, const char *header, char ender); void git_signature__writebuf(git_buf *buf, const char *header, const git_signature *sig); +bool git_signature__equal(const git_signature *one, const git_signature *two); + +int git_signature__pdup(git_signature **dest, const git_signature *source, git_pool *pool); #endif diff -Nru libgit2-0.20.0/src/socket_stream.c libgit2-0.22.2/src/socket_stream.c --- libgit2-0.20.0/src/socket_stream.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/socket_stream.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,212 @@ +/* + * 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 +# include +# include +# include +# include +# include +# include +#else +# include +# include +# 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) { + net_set_error("error creating socket"); + break; + } + + 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; +} diff -Nru libgit2-0.20.0/src/socket_stream.h libgit2-0.22.2/src/socket_stream.h --- libgit2-0.20.0/src/socket_stream.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/socket_stream.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,21 @@ +/* + * 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 diff -Nru libgit2-0.20.0/src/sortedcache.c libgit2-0.22.2/src/sortedcache.c --- libgit2-0.20.0/src/sortedcache.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/sortedcache.c 2015-03-24 16:10:45.000000000 +0000 @@ -20,7 +20,7 @@ if (git_pool_init(&sc->pool, 1, 0) < 0 || git_vector_init(&sc->items, 4, item_cmp) < 0 || - (sc->map = git_strmap_alloc()) == NULL) + git_strmap_alloc(&sc->map) < 0) goto fail; if (git_rwlock_init(&sc->lock)) { @@ -39,8 +39,7 @@ return 0; fail: - if (sc->map) - git_strmap_free(sc->map); + git_strmap_free(sc->map); git_vector_free(&sc->items); git_pool_clear(&sc->pool); git__free(sc); @@ -233,9 +232,8 @@ void git_sortedcache_updated(git_sortedcache *sc) { - /* update filestamp to latest value */ - if (git_futils_filestamp_check(&sc->stamp, sc->path) < 0) - giterr_clear(); + /* update filestamp to latest value */ + git_futils_filestamp_check(&sc->stamp, sc->path); } /* release all items in sorted cache */ @@ -321,7 +319,7 @@ void *git_sortedcache_entry(git_sortedcache *sc, size_t pos) { /* make sure the items are sorted so this gets the correct item */ - if (!sc->items.sorted) + if (!git_vector_is_sorted(&sc->items)) git_vector_sort(&sc->items); return git_vector_get(&sc->items, pos); diff -Nru libgit2-0.20.0/src/stash.c libgit2-0.22.2/src/stash.c --- libgit2-0.20.0/src/stash.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/stash.c 2015-03-24 16:10:45.000000000 +0000 @@ -15,6 +15,7 @@ #include "git2/status.h" #include "git2/checkout.h" #include "git2/index.h" +#include "git2/transaction.h" #include "signature.h" static int create_error(int error, const char *msg) @@ -178,7 +179,8 @@ break; case GIT_DELTA_UNTRACKED: - if (data->include_untracked) + if (data->include_untracked && + delta->new_file.mode != GIT_FILEMODE_TREE) add_path = delta->new_file.path; break; @@ -231,7 +233,8 @@ } if (flags & GIT_STASH_INCLUDE_IGNORED) { - opts.flags |= GIT_DIFF_INCLUDE_IGNORED; + opts.flags |= GIT_DIFF_INCLUDE_IGNORED | + GIT_DIFF_RECURSE_IGNORED_DIRS; data.include_ignored = true; } @@ -412,25 +415,15 @@ const char *message) { git_reference *stash; - git_reflog *reflog = NULL; int error; - if ((error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1)) < 0) - goto cleanup; - - git_reference_free(stash); - - if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE) < 0)) - goto cleanup; + if ((error = git_reference_ensure_log(repo, GIT_REFS_STASH_FILE)) < 0) + return error; - if ((error = git_reflog_append(reflog, w_commit_oid, stasher, message)) < 0) - goto cleanup; + error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1, stasher, message); - if ((error = git_reflog_write(reflog)) < 0) - goto cleanup; + git_reference_free(stash); -cleanup: - git_reflog_free(reflog); return error; } @@ -440,7 +433,7 @@ GIT_UNUSED(status); GIT_UNUSED(payload); - return 1; + return GIT_PASSTHROUGH; } static int ensure_there_are_changes_to_stash( @@ -456,14 +449,15 @@ if (include_untracked_files) opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED | - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; if (include_ignored_files) - opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED; + opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED | + GIT_STATUS_OPT_RECURSE_IGNORED_DIRS; error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL); - if (error == GIT_EUSER) + if (error == GIT_PASSTHROUGH) return 0; if (!error) @@ -475,15 +469,19 @@ static int reset_index_and_workdir( git_repository *repo, git_commit *commit, - bool remove_untracked) + bool remove_untracked, + bool remove_ignored) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_FORCE; if (remove_untracked) opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED; + if (remove_ignored) + opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_IGNORED; + return git_checkout_tree(repo, (git_object *)commit, &opts); } @@ -542,7 +540,8 @@ if ((error = reset_index_and_workdir( repo, ((flags & GIT_STASH_KEEP_INDEX) != 0) ? i_commit : b_commit, - (flags & GIT_STASH_INCLUDE_UNTRACKED) != 0)) < 0) + (flags & GIT_STASH_INCLUDE_UNTRACKED) != 0, + (flags & GIT_STASH_INCLUDE_IGNORED) != 0)) < 0) goto cleanup; cleanup: @@ -582,12 +581,14 @@ for (i = 0; i < max; i++) { entry = git_reflog_entry_byindex(reflog, i); - if (callback(i, + error = callback(i, git_reflog_entry_message(entry), git_reflog_entry_id_new(entry), - payload)) { - error = GIT_EUSER; - break; + payload); + + if (error) { + giterr_set_after_callback(error); + break; } } @@ -601,14 +602,21 @@ git_repository *repo, size_t index) { - git_reference *stash; + git_transaction *tx; + git_reference *stash = NULL; git_reflog *reflog = NULL; size_t max; int error; - if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0) + if ((error = git_transaction_new(&tx, repo)) < 0) return error; + if ((error = git_transaction_lock_ref(tx, GIT_REFS_STASH_FILE)) < 0) + goto cleanup; + + if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0) + goto cleanup; + if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0) goto cleanup; @@ -623,24 +631,25 @@ if ((error = git_reflog_drop(reflog, index, true)) < 0) goto cleanup; - if ((error = git_reflog_write(reflog)) < 0) + if ((error = git_transaction_set_reflog(tx, GIT_REFS_STASH_FILE, reflog)) < 0) goto cleanup; if (max == 1) { - error = git_reference_delete(stash); - git_reference_free(stash); - stash = NULL; + if ((error = git_transaction_remove(tx, GIT_REFS_STASH_FILE)) < 0) + goto cleanup; } else if (index == 0) { const git_reflog_entry *entry; entry = git_reflog_entry_byindex(reflog, 0); - - git_reference_free(stash); - error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1); + if ((error = git_transaction_set_target(tx, GIT_REFS_STASH_FILE, &entry->oid_cur, NULL, NULL)) < 0) + goto cleanup; } + error = git_transaction_commit(tx); + cleanup: git_reference_free(stash); + git_transaction_free(tx); git_reflog_free(reflog); return error; } diff -Nru libgit2-0.20.0/src/status.c libgit2-0.22.2/src/status.c --- libgit2-0.20.0/src/status.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/status.c 2015-03-24 16:10:45.000000000 +0000 @@ -38,7 +38,7 @@ case GIT_DELTA_RENAMED: st = GIT_STATUS_INDEX_RENAMED; - if (!git_oid_equal(&head2idx->old_file.oid, &head2idx->new_file.oid)) + if (!git_oid_equal(&head2idx->old_file.id, &head2idx->new_file.id)) st |= GIT_STATUS_INDEX_MODIFIED; break; case GIT_DELTA_TYPECHANGE: @@ -62,6 +62,9 @@ case GIT_DELTA_UNTRACKED: st = GIT_STATUS_WT_NEW; break; + case GIT_DELTA_UNREADABLE: + st = GIT_STATUS_WT_UNREADABLE; + break; case GIT_DELTA_DELETED: st = GIT_STATUS_WT_DELETED; break; @@ -74,25 +77,25 @@ case GIT_DELTA_RENAMED: st = GIT_STATUS_WT_RENAMED; - if (!git_oid_equal(&idx2wd->old_file.oid, &idx2wd->new_file.oid)) { + if (!git_oid_equal(&idx2wd->old_file.id, &idx2wd->new_file.id)) { /* if OIDs don't match, we might need to calculate them now to * discern between RENAMED vs RENAMED+MODIFED */ - if (git_oid_iszero(&idx2wd->old_file.oid) && + if (git_oid_iszero(&idx2wd->old_file.id) && diff->old_src == GIT_ITERATOR_TYPE_WORKDIR && !git_diff__oid_for_file( - diff->repo, idx2wd->old_file.path, idx2wd->old_file.mode, - idx2wd->old_file.size, &idx2wd->old_file.oid)) - idx2wd->old_file.flags |= GIT_DIFF_FLAG_VALID_OID; + &idx2wd->old_file.id, diff, idx2wd->old_file.path, + idx2wd->old_file.mode, idx2wd->old_file.size)) + idx2wd->old_file.flags |= GIT_DIFF_FLAG_VALID_ID; - if (git_oid_iszero(&idx2wd->new_file.oid) && + if (git_oid_iszero(&idx2wd->new_file.id) && diff->new_src == GIT_ITERATOR_TYPE_WORKDIR && !git_diff__oid_for_file( - diff->repo, idx2wd->new_file.path, idx2wd->new_file.mode, - idx2wd->new_file.size, &idx2wd->new_file.oid)) - idx2wd->new_file.flags |= GIT_DIFF_FLAG_VALID_OID; + &idx2wd->new_file.id, diff, idx2wd->new_file.path, + idx2wd->new_file.mode, idx2wd->new_file.size)) + idx2wd->new_file.flags |= GIT_DIFF_FLAG_VALID_ID; - if (!git_oid_equal(&idx2wd->old_file.oid, &idx2wd->new_file.oid)) + if (!git_oid_equal(&idx2wd->old_file.id, &idx2wd->new_file.id)) st |= GIT_STATUS_WT_MODIFIED; } break; @@ -225,6 +228,28 @@ return status; } +static int status_validate_options(const git_status_options *opts) +{ + if (!opts) + return 0; + + GITERR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options"); + + if (opts->show > GIT_STATUS_SHOW_WORKDIR_ONLY) { + giterr_set(GITERR_INVALID, "Unknown status 'show' option"); + return -1; + } + + if ((opts->flags & GIT_STATUS_OPT_NO_REFRESH) != 0 && + (opts->flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0) { + giterr_set(GITERR_INVALID, "Updating index from status " + "is not allowed when index refresh is disabled"); + return -1; + } + + return 0; +} + int git_status_list_new( git_status_list **out, git_repository *repo, @@ -240,11 +265,10 @@ int error = 0; unsigned int flags = opts ? opts->flags : GIT_STATUS_OPT_DEFAULTS; - assert(show <= GIT_STATUS_SHOW_WORKDIR_ONLY); - *out = NULL; - GITERR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options"); + if (status_validate_options(opts) < 0) + return -1; if ((error = git_repository__ensure_not_bare(repo, "status")) < 0 || (error = git_repository_index(&index, repo)) < 0) @@ -287,6 +311,12 @@ diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_IGNORED_DIRS; if ((flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0) diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES; + if ((flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0) + diffopt.flags = diffopt.flags | GIT_DIFF_UPDATE_INDEX; + if ((flags & GIT_STATUS_OPT_INCLUDE_UNREADABLE) != 0) + diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNREADABLE; + if ((flags & GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED) != 0) + diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED; if ((flags & GIT_STATUS_OPT_RENAMES_FROM_REWRITES) != 0) findopt.flags = findopt.flags | @@ -306,16 +336,18 @@ if (show != GIT_STATUS_SHOW_INDEX_ONLY) { if ((error = git_diff_index_to_workdir( - &status->idx2wd, repo, index, &diffopt)) < 0) + &status->idx2wd, repo, index, &diffopt)) < 0) { goto done; + } if ((flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 && (error = git_diff_find_similar(status->idx2wd, &findopt)) < 0) goto done; } - if ((error = git_diff__paired_foreach( - status->head2idx, status->idx2wd, status_collect, status)) < 0) + error = git_diff__paired_foreach( + status->head2idx, status->idx2wd, status_collect, status); + if (error < 0) goto done; if (flags & GIT_STATUS_OPT_SORT_CASE_SENSITIVELY) @@ -360,19 +392,13 @@ void git_status_list_free(git_status_list *status) { - git_status_entry *status_entry; - size_t i; - if (status == NULL) return; git_diff_free(status->head2idx); git_diff_free(status->idx2wd); - git_vector_foreach(&status->paired, i, status_entry) - git__free(status_entry); - - git_vector_free(&status->paired); + git_vector_free_deep(&status->paired); git__memzero(status, sizeof(*status)); git__free(status); @@ -389,17 +415,17 @@ size_t i; int error = 0; - if ((error = git_status_list_new(&status, repo, opts)) < 0) + if ((error = git_status_list_new(&status, repo, opts)) < 0) { return error; + } git_vector_foreach(&status->paired, i, status_entry) { const char *path = status_entry->head_to_index ? status_entry->head_to_index->old_file.path : status_entry->index_to_workdir->old_file.path; - if (cb(path, status_entry->status, payload) != 0) { - error = GIT_EUSER; - giterr_clear(); + if ((error = cb(path, status_entry->status, payload)) != 0) { + giterr_set_after_callback(error); break; } } @@ -468,7 +494,8 @@ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS | GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS | - GIT_STATUS_OPT_INCLUDE_UNMODIFIED; + GIT_STATUS_OPT_INCLUDE_UNMODIFIED | + GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH; opts.pathspec.count = 1; opts.pathspec.strings = &sfi.expected; @@ -501,3 +528,31 @@ return git_ignore_path_is_ignored(ignored, repo, path); } +int git_status_init_options(git_status_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_status_options, GIT_STATUS_OPTIONS_INIT); + return 0; +} + +int git_status_list_get_perfdata( + git_diff_perfdata *out, const git_status_list *status) +{ + assert(out); + GITERR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata"); + + out->stat_calls = 0; + out->oid_calculations = 0; + + if (status->head2idx) { + out->stat_calls += status->head2idx->perf.stat_calls; + out->oid_calculations += status->head2idx->perf.oid_calculations; + } + if (status->idx2wd) { + out->stat_calls += status->idx2wd->perf.stat_calls; + out->oid_calculations += status->idx2wd->perf.oid_calculations; + } + + return 0; +} + diff -Nru libgit2-0.20.0/src/stream.h libgit2-0.22.2/src/stream.h --- libgit2-0.20.0/src/stream.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/stream.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,53 @@ +/* + * 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_stream_h__ +#define INCLUDE_stream_h__ + +#include "common.h" +#include "git2/sys/stream.h" + +GIT_INLINE(int) git_stream_connect(git_stream *st) +{ + return st->connect(st); +} + +GIT_INLINE(int) git_stream_is_encrypted(git_stream *st) +{ + return st->encrypted; +} + +GIT_INLINE(int) git_stream_certificate(git_cert **out, git_stream *st) +{ + if (!st->encrypted) { + giterr_set(GITERR_INVALID, "an unencrypted stream does not have a certificate"); + return -1; + } + + return st->certificate(out, st); +} + +GIT_INLINE(ssize_t) git_stream_read(git_stream *st, void *data, size_t len) +{ + return st->read(st, data, len); +} + +GIT_INLINE(ssize_t) git_stream_write(git_stream *st, const char *data, size_t len, int flags) +{ + return st->write(st, data, len, flags); +} + +GIT_INLINE(int) git_stream_close(git_stream *st) +{ + return st->close(st); +} + +GIT_INLINE(void) git_stream_free(git_stream *st) +{ + st->free(st); +} + +#endif diff -Nru libgit2-0.20.0/src/strmap.h libgit2-0.22.2/src/strmap.h --- libgit2-0.20.0/src/strmap.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/strmap.h 2015-03-24 16:10:45.000000000 +0000 @@ -22,7 +22,9 @@ #define GIT__USE_STRMAP \ __KHASH_IMPL(str, static kh_inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal) -#define git_strmap_alloc() kh_init(str) +#define git_strmap_alloc(hp) \ + ((*(hp) = kh_init(str)) == NULL) ? giterr_set_oom(), -1 : 0 + #define git_strmap_free(h) kh_destroy(str, h), h = NULL #define git_strmap_clear(h) kh_clear(str, h) diff -Nru libgit2-0.20.0/src/strnlen.h libgit2-0.22.2/src/strnlen.h --- libgit2-0.20.0/src/strnlen.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/strnlen.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,24 @@ +/* + * 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_strlen_h__ +#define INCLUDE_strlen_h__ + +#if defined(__MINGW32__) || defined(__sun) || defined(__APPLE__) || defined(__MidnightBSD__) ||\ + (defined(_MSC_VER) && _MSC_VER < 1500) +# define NO_STRNLEN +#endif + +#ifdef NO_STRNLEN +GIT_INLINE(size_t) p_strnlen(const char *s, size_t maxlen) { + const char *end = memchr(s, 0, maxlen); + return end ? (size_t)(end - s) : maxlen; +} +#else +# define p_strnlen strnlen +#endif + +#endif diff -Nru libgit2-0.20.0/src/submodule.c libgit2-0.22.2/src/submodule.c --- libgit2-0.20.0/src/submodule.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/submodule.c 2015-03-24 16:10:45.000000000 +0000 @@ -43,6 +43,22 @@ {GIT_CVAR_TRUE, NULL, GIT_SUBMODULE_IGNORE_ALL}, }; +static git_cvar_map _sm_recurse_map[] = { + {GIT_CVAR_STRING, "on-demand", GIT_SUBMODULE_RECURSE_ONDEMAND}, + {GIT_CVAR_FALSE, NULL, GIT_SUBMODULE_RECURSE_NO}, + {GIT_CVAR_TRUE, NULL, GIT_SUBMODULE_RECURSE_YES}, +}; + +enum { + CACHE_OK = 0, + CACHE_REFRESH = 1, + CACHE_FLUSH = 2 +}; +enum { + GITMODULES_EXISTING = 0, + GITMODULES_CREATE = 1, +}; + static kh_inline khint_t str_hash_no_trailing_slash(const char *s) { khint_t h; @@ -71,13 +87,15 @@ str, static kh_inline, const char *, void *, 1, str_hash_no_trailing_slash, str_equal_no_trailing_slash); -static int load_submodule_config(git_repository *repo); -static git_config_backend *open_gitmodules(git_repository *, bool, const git_oid *); -static int lookup_head_remote(git_buf *url, git_repository *repo); -static int submodule_get(git_submodule **, git_repository *, const char *, const char *); +static int submodule_cache_init(git_repository *repo, int refresh); +static void submodule_cache_free(git_submodule_cache *cache); + +static git_config_backend *open_gitmodules(git_submodule_cache *, int gitmod); +static int get_url_base(git_buf *url, git_repository *repo); +static int lookup_head_remote_key(git_buf *remote_key, git_repository *repo); +static int submodule_get(git_submodule **, git_submodule_cache *, const char *, const char *); static int submodule_load_from_config(const git_config_entry *, void *); -static int submodule_load_from_wd_lite(git_submodule *, const char *, void *); -static int submodule_update_config(git_submodule *, const char *, const char *, bool, bool); +static int submodule_load_from_wd_lite(git_submodule *); static void submodule_get_index_status(unsigned int *, git_submodule *); static void submodule_get_wd_status(unsigned int *, git_submodule *, git_repository *, git_submodule_ignore_t); @@ -93,52 +111,134 @@ return git_buf_puts(key, suffix); } +/* lookup submodule or return ENOTFOUND if it doesn't exist */ +static int submodule_lookup( + git_submodule **out, + git_submodule_cache *cache, + const char *name, + const char *alternate) +{ + khiter_t pos; + + /* lock cache */ + + pos = git_strmap_lookup_index(cache->submodules, name); + + if (!git_strmap_valid_index(cache->submodules, pos) && alternate) + pos = git_strmap_lookup_index(cache->submodules, alternate); + + if (!git_strmap_valid_index(cache->submodules, pos)) { + /* unlock cache */ + return GIT_ENOTFOUND; /* don't set error - caller will cope */ + } + + if (out != NULL) { + git_submodule *sm = git_strmap_value_at(cache->submodules, pos); + GIT_REFCOUNT_INC(sm); + *out = sm; + } + + /* unlock cache */ + + return 0; +} + +/* clear a set of flags on all submodules */ +static void submodule_cache_clear_flags( + git_submodule_cache *cache, uint32_t mask) +{ + git_submodule *sm; + uint32_t inverted_mask = ~mask; + + git_strmap_foreach_value(cache->submodules, sm, { + sm->flags &= inverted_mask; + }); +} + /* * PUBLIC APIS */ -int git_submodule_lookup( - git_submodule **sm_ptr, /* NULL if user only wants to test existence */ +bool git_submodule__is_submodule(git_repository *repo, const char *name) +{ + git_strmap *map; + + if (submodule_cache_init(repo, CACHE_OK) < 0) { + giterr_clear(); + return false; + } + + if (!repo->_submodules || !(map = repo->_submodules->submodules)) + return false; + + return git_strmap_valid_index(map, git_strmap_lookup_index(map, name)); +} + +static void submodule_set_lookup_error(int error, const char *name) +{ + if (!error) + return; + + giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ? + "No submodule named '%s'" : + "Submodule '%s' has not been added yet", name); +} + +int git_submodule__lookup( + git_submodule **out, /* NULL if user only wants to test existence */ git_repository *repo, - const char *name) /* trailing slash is allowed */ + const char *name) /* trailing slash is allowed */ { int error; - khiter_t pos; assert(repo && name); - if ((error = load_submodule_config(repo)) < 0) + if ((error = submodule_cache_init(repo, CACHE_OK)) < 0) return error; - pos = git_strmap_lookup_index(repo->submodules, name); + if ((error = submodule_lookup(out, repo->_submodules, name, NULL)) < 0) + submodule_set_lookup_error(error, name); - if (!git_strmap_valid_index(repo->submodules, pos)) { - error = GIT_ENOTFOUND; + return error; +} + +int git_submodule_lookup( + git_submodule **out, /* NULL if user only wants to test existence */ + git_repository *repo, + const char *name) /* trailing slash is allowed */ +{ + int error; + + assert(repo && name); + + if ((error = submodule_cache_init(repo, CACHE_REFRESH)) < 0) + return error; + + if ((error = submodule_lookup(out, repo->_submodules, name, NULL)) < 0) { /* check if a plausible submodule exists at path */ if (git_repository_workdir(repo)) { git_buf path = GIT_BUF_INIT; - if (git_buf_joinpath(&path, git_repository_workdir(repo), name) < 0) + if (git_buf_join3(&path, + '/', git_repository_workdir(repo), name, DOT_GIT) < 0) return -1; - if (git_path_contains_dir(&path, DOT_GIT)) + if (git_path_exists(path.ptr)) error = GIT_EEXISTS; git_buf_free(&path); } - giterr_set(GITERR_SUBMODULE, (error == GIT_ENOTFOUND) ? - "No submodule named '%s'" : - "Submodule '%s' has not been added yet", name); - - return error; + submodule_set_lookup_error(error, name); } - if (sm_ptr) - *sm_ptr = git_strmap_value_at(repo->submodules, pos); + return error; +} - return 0; +static void submodule_free_dup(void *sm) +{ + git_submodule_free(sm); } int git_submodule_foreach( @@ -147,58 +247,117 @@ void *payload) { int error; + size_t i; git_submodule *sm; - git_vector seen = GIT_VECTOR_INIT; - git_vector_set_cmp(&seen, submodule_cmp); + git_submodule_cache *cache; + git_vector snapshot = GIT_VECTOR_INIT; assert(repo && callback); - if ((error = load_submodule_config(repo)) < 0) + if ((error = submodule_cache_init(repo, CACHE_REFRESH)) < 0) return error; - git_strmap_foreach_value(repo->submodules, sm, { - /* Usually the following will not come into play - it just prevents - * us from issuing a callback twice for a submodule where the name - * and path are not the same. - */ - if (GIT_REFCOUNT_VAL(sm) > 1) { - if (git_vector_bsearch(NULL, &seen, sm) != GIT_ENOTFOUND) - continue; - if ((error = git_vector_insert(&seen, sm)) < 0) + cache = repo->_submodules; + + if (git_mutex_lock(&cache->lock) < 0) { + giterr_set(GITERR_OS, "Unable to acquire lock on submodule cache"); + return -1; + } + + if (!(error = git_vector_init( + &snapshot, kh_size(cache->submodules), submodule_cmp))) { + + git_strmap_foreach_value(cache->submodules, sm, { + if ((error = git_vector_insert(&snapshot, sm)) < 0) break; - } + GIT_REFCOUNT_INC(sm); + }); + } - if (callback(sm, sm->name, payload)) { - giterr_clear(); - error = GIT_EUSER; + git_mutex_unlock(&cache->lock); + + if (error < 0) + goto done; + + git_vector_uniq(&snapshot, submodule_free_dup); + + git_vector_foreach(&snapshot, i, sm) { + if ((error = callback(sm, sm->name, payload)) != 0) { + giterr_set_after_callback(error); break; } - }); + } - git_vector_free(&seen); +done: + git_vector_foreach(&snapshot, i, sm) + git_submodule_free(sm); + git_vector_free(&snapshot); return error; } -void git_submodule_config_free(git_repository *repo) +void git_submodule_cache_free(git_repository *repo) { - git_strmap *smcfg; - git_submodule *sm; + git_submodule_cache *cache; assert(repo); - smcfg = repo->submodules; - repo->submodules = NULL; + if ((cache = git__swap(repo->_submodules, NULL)) != NULL) + submodule_cache_free(cache); +} - if (smcfg == NULL) - return; +static int submodule_repo_init( + git_repository **out, + git_repository *parent_repo, + const char *path, + const char *url, + bool use_gitlink) +{ + int error = 0; + git_buf workdir = GIT_BUF_INIT, repodir = GIT_BUF_INIT; + git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT; + git_repository *subrepo = NULL; + + error = git_buf_joinpath(&workdir, git_repository_workdir(parent_repo), path); + if (error < 0) + goto cleanup; + + initopt.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_NO_REINIT; + initopt.origin_url = url; + + /* init submodule repository and add origin remote as needed */ - git_strmap_foreach_value(smcfg, sm, { git_submodule_free(sm); }); - git_strmap_free(smcfg); + /* New style: sub-repo goes in /modules// with a + * gitlink in the sub-repo workdir directory to that repository + * + * Old style: sub-repo goes directly into repo//.git/ + */ + if (use_gitlink) { + error = git_buf_join3( + &repodir, '/', git_repository_path(parent_repo), "modules", path); + if (error < 0) + goto cleanup; + + initopt.workdir_path = workdir.ptr; + initopt.flags |= + GIT_REPOSITORY_INIT_NO_DOTGIT_DIR | + GIT_REPOSITORY_INIT_RELATIVE_GITLINK; + + error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt); + } else + error = git_repository_init_ext(&subrepo, workdir.ptr, &initopt); + +cleanup: + git_buf_free(&workdir); + git_buf_free(&repodir); + + *out = subrepo; + + return error; } int git_submodule_add_setup( - git_submodule **submodule, + git_submodule **out, git_repository *repo, const char *url, const char *path, @@ -206,37 +365,22 @@ { int error = 0; git_config_backend *mods = NULL; - git_submodule *sm; + git_submodule *sm = NULL; git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT; - git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT; git_repository *subrepo = NULL; assert(repo && url && path); /* see if there is already an entry for this submodule */ - if (git_submodule_lookup(&sm, repo, path) < 0) + if (git_submodule_lookup(NULL, repo, path) < 0) giterr_clear(); else { giterr_set(GITERR_SUBMODULE, - "Attempt to add a submodule that already exists"); + "Attempt to add submodule '%s' that already exists", path); return GIT_EEXISTS; } - /* resolve parameters */ - - if (url[0] == '.' && (url[1] == '/' || (url[1] == '.' && url[2] == '/'))) { - if (!(error = lookup_head_remote(&real_url, repo))) - error = git_path_apply_relative(&real_url, url); - } else if (strchr(url, ':') != NULL || url[0] == '/') { - error = git_buf_sets(&real_url, url); - } else { - giterr_set(GITERR_SUBMODULE, "Invalid format for submodule URL"); - error = -1; - } - if (error) - goto cleanup; - /* validate and normalize path */ if (git__prefixcmp(path, git_repository_workdir(repo)) == 0) @@ -250,9 +394,9 @@ /* update .gitmodules */ - if ((mods = open_gitmodules(repo, true, NULL)) == NULL) { + if (!(mods = open_gitmodules(repo->_submodules, GITMODULES_CREATE))) { giterr_set(GITERR_SUBMODULE, - "Adding submodules to a bare repository is not supported (for now)"); + "Adding submodules to a bare repository is not supported"); return -1; } @@ -261,7 +405,7 @@ goto cleanup; if ((error = submodule_config_key_trunc_puts(&name, "url")) < 0 || - (error = git_config_file_set_string(mods, name.ptr, real_url.ptr)) < 0) + (error = git_config_file_set_string(mods, name.ptr, url)) < 0) goto cleanup; git_buf_clear(&name); @@ -272,54 +416,43 @@ if (error < 0) goto cleanup; - /* New style: sub-repo goes in /modules// with a - * gitlink in the sub-repo workdir directory to that repository - * - * Old style: sub-repo goes directly into repo//.git/ + /* if the repo does not already exist, then init a new repo and add it. + * Otherwise, just add the existing repo. */ + if (!(git_path_exists(name.ptr) && + git_path_contains(&name, DOT_GIT))) { - initopt.flags = GIT_REPOSITORY_INIT_MKPATH | - GIT_REPOSITORY_INIT_NO_REINIT; - initopt.origin_url = real_url.ptr; - - if (git_path_exists(name.ptr) && - git_path_contains(&name, DOT_GIT)) - { - /* repo appears to already exist - reinit? */ - } - else if (use_gitlink) { - git_buf repodir = GIT_BUF_INIT; - - error = git_buf_join_n( - &repodir, '/', 3, git_repository_path(repo), "modules", path); - if (error < 0) + /* resolve the actual URL to use */ + if ((error = git_submodule_resolve_url(&real_url, repo, url)) < 0) goto cleanup; - initopt.workdir_path = name.ptr; - initopt.flags |= GIT_REPOSITORY_INIT_NO_DOTGIT_DIR; - - error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt); - - git_buf_free(&repodir); - } - else { - error = git_repository_init_ext(&subrepo, name.ptr, &initopt); + if ((error = submodule_repo_init(&subrepo, repo, path, real_url.ptr, use_gitlink)) < 0) + goto cleanup; } - if (error < 0) - goto cleanup; /* add submodule to hash and "reload" it */ - if (!(error = submodule_get(&sm, repo, path, NULL)) && - !(error = git_submodule_reload(sm))) + if (git_mutex_lock(&repo->_submodules->lock) < 0) { + giterr_set(GITERR_OS, "Unable to acquire lock on submodule cache"); + error = -1; + goto cleanup; + } + + if (!(error = submodule_get(&sm, repo->_submodules, path, NULL)) && + !(error = git_submodule_reload(sm, false))) error = git_submodule_init(sm, false); + git_mutex_unlock(&repo->_submodules->lock); + cleanup: - if (submodule != NULL) - *submodule = !error ? sm : NULL; + if (error && sm) { + git_submodule_free(sm); + sm = NULL; + } + if (out != NULL) + *out = sm; - if (mods != NULL) - git_config_file_free(mods); + git_config_file_free(mods); git_repository_free(subrepo); git_buf_free(&real_url); git_buf_free(&name); @@ -327,6 +460,34 @@ return error; } +int git_submodule_repo_init( + git_repository **out, + const git_submodule *sm, + int use_gitlink) +{ + int error; + git_repository *sub_repo = NULL; + const char *configured_url; + git_config *cfg = NULL; + git_buf buf = GIT_BUF_INIT; + + assert(out && sm); + + /* get the configured remote url of the submodule */ + if ((error = git_buf_printf(&buf, "submodule.%s.url", sm->name)) < 0 || + (error = git_repository_config_snapshot(&cfg, sm->repo)) < 0 || + (error = git_config_get_string(&configured_url, cfg, buf.ptr)) < 0 || + (error = submodule_repo_init(&sub_repo, sm->repo, sm->path, configured_url, use_gitlink)) < 0) + goto done; + + *out = sub_repo; + +done: + git_config_free(cfg); + git_buf_free(&buf); + return error; +} + int git_submodule_add_finalize(git_submodule *sm) { int error; @@ -382,7 +543,7 @@ error = -1; goto cleanup; } - git_oid_cpy(&entry.oid, &sm->wd_oid); + git_oid_cpy(&entry.id, &sm->wd_oid); if ((error = git_commit_lookup(&head, sm_repo, &sm->wd_oid)) < 0) goto cleanup; @@ -429,6 +590,15 @@ return NULL; } +const char *git_submodule_recurse_to_str(git_submodule_recurse_t recurse) +{ + int i; + for (i = 0; i < (int)ARRAY_SIZE(_sm_recurse_map); ++i) + if (_sm_recurse_map[i].map_value == recurse) + return _sm_recurse_map[i].str_match; + return NULL; +} + int git_submodule_save(git_submodule *submodule) { int error = 0; @@ -438,10 +608,10 @@ assert(submodule); - mods = open_gitmodules(submodule->repo, true, NULL); + mods = open_gitmodules(submodule->repo->_submodules, GITMODULES_CREATE); if (!mods) { giterr_set(GITERR_SUBMODULE, - "Adding submodules to a bare repository is not supported (for now)"); + "Adding submodules to a bare repository is not supported"); return -1; } @@ -458,6 +628,10 @@ (error = git_config_file_set_string(mods, key.ptr, submodule->url)) < 0) goto cleanup; + if ((error = submodule_config_key_trunc_puts(&key, "branch")) < 0 || + (error = git_config_file_set_string(mods, key.ptr, submodule->branch)) < 0) + goto cleanup; + if (!(error = submodule_config_key_trunc_puts(&key, "update")) && (val = git_submodule_update_to_str(submodule->update)) != NULL) error = git_config_file_set_string(mods, key.ptr, val); @@ -470,21 +644,21 @@ if (error < 0) goto cleanup; - if ((error = submodule_config_key_trunc_puts( - &key, "fetchRecurseSubmodules")) < 0 || - (error = git_config_file_set_string( - mods, key.ptr, submodule->fetch_recurse ? "true" : "false")) < 0) + if (!(error = submodule_config_key_trunc_puts(&key, "fetchRecurseSubmodules")) && + (val = git_submodule_recurse_to_str(submodule->fetch_recurse)) != NULL) + error = git_config_file_set_string(mods, key.ptr, val); + if (error < 0) goto cleanup; /* update internal defaults */ submodule->ignore_default = submodule->ignore; submodule->update_default = submodule->update; + submodule->fetch_recurse_default = submodule->fetch_recurse; submodule->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG; cleanup: - if (mods != NULL) - git_config_file_free(mods); + git_config_file_free(mods); git_buf_free(&key); return error; @@ -514,6 +688,33 @@ return submodule->url; } +int git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *url) +{ + int error = 0; + + assert(out && repo && url); + + git_buf_sanitize(out); + + if (git_path_is_relative(url)) { + if (!(error = get_url_base(out, repo))) + error = git_path_apply_relative(out, url); + } else if (strchr(url, ':') != NULL || url[0] == '/') { + error = git_buf_sets(out, url); + } else { + giterr_set(GITERR_SUBMODULE, "Invalid format for submodule URL"); + error = -1; + } + + return error; +} + +const char *git_submodule_branch(git_submodule *submodule) +{ + assert(submodule); + return submodule->branch; +} + int git_submodule_set_url(git_submodule *submodule, const char *url) { assert(submodule && url); @@ -589,7 +790,7 @@ return old; } -git_submodule_update_t git_submodule_update(git_submodule *submodule) +git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule) { assert(submodule); return (submodule->update < GIT_SUBMODULE_UPDATE_CHECKOUT) ? @@ -611,66 +812,302 @@ return old; } -int git_submodule_fetch_recurse_submodules( +git_submodule_recurse_t git_submodule_fetch_recurse_submodules( git_submodule *submodule) { assert(submodule); return submodule->fetch_recurse; } -int git_submodule_set_fetch_recurse_submodules( +git_submodule_recurse_t git_submodule_set_fetch_recurse_submodules( git_submodule *submodule, - int fetch_recurse_submodules) + git_submodule_recurse_t fetch_recurse_submodules) { - int old; + git_submodule_recurse_t old; assert(submodule); + if (fetch_recurse_submodules == GIT_SUBMODULE_RECURSE_RESET) + fetch_recurse_submodules = submodule->fetch_recurse_default; + old = submodule->fetch_recurse; - submodule->fetch_recurse = (fetch_recurse_submodules != 0); + submodule->fetch_recurse = fetch_recurse_submodules; return old; } -int git_submodule_init(git_submodule *submodule, int overwrite) +static int submodule_repo_create( + git_repository **out, + git_repository *parent_repo, + const char *path) +{ + int error = 0; + git_buf workdir = GIT_BUF_INIT, repodir = GIT_BUF_INIT; + git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT; + git_repository *subrepo = NULL; + + initopt.flags = + GIT_REPOSITORY_INIT_MKPATH | + GIT_REPOSITORY_INIT_NO_REINIT | + GIT_REPOSITORY_INIT_NO_DOTGIT_DIR | + GIT_REPOSITORY_INIT_RELATIVE_GITLINK; + + /* Workdir: path to sub-repo working directory */ + error = git_buf_joinpath(&workdir, git_repository_workdir(parent_repo), path); + if (error < 0) + goto cleanup; + + initopt.workdir_path = workdir.ptr; + + /** + * Repodir: path to the sub-repo. sub-repo goes in: + * /modules// with a gitlink in the + * sub-repo workdir directory to that repository. + */ + error = git_buf_join3( + &repodir, '/', git_repository_path(parent_repo), "modules", path); + if (error < 0) + goto cleanup; + + error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt); + +cleanup: + git_buf_free(&workdir); + git_buf_free(&repodir); + + *out = subrepo; + + return error; +} + +/** + * Callback to override sub-repository creation when + * cloning a sub-repository. + */ +static int git_submodule_update_repo_init_cb( + git_repository **out, + const char *path, + int bare, + void *payload) +{ + git_submodule *sm; + + GIT_UNUSED(bare); + + sm = payload; + + return submodule_repo_create(out, sm->repo, path); +} + +int git_submodule_update_init_options(git_submodule_update_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_submodule_update_options, GIT_SUBMODULE_UPDATE_OPTIONS_INIT); + return 0; +} + +int git_submodule_update(git_submodule *sm, int init, git_submodule_update_options *_update_options) { int error; - const char *val; + unsigned int submodule_status; + git_config *config = NULL; + const char *submodule_url; + git_repository *sub_repo = NULL; + git_remote *remote = NULL; + git_object *target_commit = NULL; + git_buf buf = GIT_BUF_INIT; + git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT; + git_clone_options clone_options = GIT_CLONE_OPTIONS_INIT; - /* write "submodule.NAME.url" */ + assert(sm); + + if (_update_options) + memcpy(&update_options, _update_options, sizeof(git_submodule_update_options)); + + GITERR_CHECK_VERSION(&update_options, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, "git_submodule_update_options"); + + /* Copy over the remote callbacks */ + clone_options.remote_callbacks = update_options.remote_callbacks; + clone_options.signature = update_options.signature; + + /* Get the status of the submodule to determine if it is already initialized */ + if ((error = git_submodule_status(&submodule_status, sm)) < 0) + goto done; + + /* + * If submodule work dir is not already initialized, check to see + * what we need to do (initialize, clone, return error...) + */ + if (submodule_status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) { + /* + * Work dir is not initialized, check to see if the submodule + * info has been copied into .git/config + */ + if ((error = git_repository_config_snapshot(&config, sm->repo)) < 0 || + (error = git_buf_printf(&buf, "submodule.%s.url", git_submodule_name(sm))) < 0) + goto done; + + if ((error = git_config_get_string(&submodule_url, config, git_buf_cstr(&buf))) < 0) { + /* + * If the error is not "not found" or if it is "not found" and we are not + * initializing the submodule, then return error. + */ + if (error != GIT_ENOTFOUND) + goto done; + + if (error == GIT_ENOTFOUND && !init) { + giterr_set(GITERR_SUBMODULE, "Submodule is not initialized."); + error = GIT_ERROR; + goto done; + } + + /* The submodule has not been initialized yet - initialize it now.*/ + if ((error = git_submodule_init(sm, 0)) < 0) + goto done; + + git_config_free(config); + config = NULL; + + if ((error = git_repository_config_snapshot(&config, sm->repo)) < 0 || + (error = git_config_get_string(&submodule_url, config, git_buf_cstr(&buf))) < 0) + goto done; + } + + /** submodule is initialized - now clone it **/ + /* override repo creation */ + clone_options.repository_cb = git_submodule_update_repo_init_cb; + clone_options.repository_cb_payload = sm; + + /* + * Do not perform checkout as part of clone, instead we + * will checkout the specific commit manually. + */ + clone_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE; + update_options.checkout_opts.checkout_strategy = update_options.clone_checkout_strategy; + + if ((error = git_clone(&sub_repo, submodule_url, sm->path, &clone_options)) < 0 || + (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm), update_options.signature, NULL)) < 0 || + (error = git_checkout_head(sub_repo, &update_options.checkout_opts)) != 0) + goto done; + } else { + /** + * Work dir is initialized - look up the commit in the parent repository's index, + * update the workdir contents of the subrepository, and set the subrepository's + * head to the new commit. + */ + if ((error = git_submodule_open(&sub_repo, sm)) < 0 || + (error = git_object_lookup(&target_commit, sub_repo, git_submodule_index_id(sm), GIT_OBJ_COMMIT)) < 0 || + (error = git_checkout_tree(sub_repo, target_commit, &update_options.checkout_opts)) != 0 || + (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm), update_options.signature, NULL)) < 0) + goto done; + + /* Invalidate the wd flags as the workdir has been updated. */ + sm->flags = sm->flags & + ~(GIT_SUBMODULE_STATUS_IN_WD | + GIT_SUBMODULE_STATUS__WD_OID_VALID | + GIT_SUBMODULE_STATUS__WD_SCANNED); + } + +done: + git_buf_free(&buf); + git_config_free(config); + git_object_free(target_commit); + git_remote_free(remote); + git_repository_free(sub_repo); + + return error; +} + +int git_submodule_init(git_submodule *sm, int overwrite) +{ + int error; + const char *val; + git_buf key = GIT_BUF_INIT, effective_submodule_url = GIT_BUF_INIT; + git_config *cfg = NULL; - if (!submodule->url) { + if (!sm->url) { giterr_set(GITERR_SUBMODULE, - "No URL configured for submodule '%s'", submodule->name); + "No URL configured for submodule '%s'", sm->name); return -1; } - error = submodule_update_config( - submodule, "url", submodule->url, overwrite != 0, false); - if (error < 0) + if ((error = git_repository_config(&cfg, sm->repo)) < 0) return error; + /* write "submodule.NAME.url" */ + + if ((error = git_submodule_resolve_url(&effective_submodule_url, sm->repo, sm->url)) < 0 || + (error = git_buf_printf(&key, "submodule.%s.url", sm->name)) < 0 || + (error = git_config__update_entry( + cfg, key.ptr, effective_submodule_url.ptr, overwrite != 0, false)) < 0) + goto cleanup; + /* write "submodule.NAME.update" if not default */ - val = (submodule->update == GIT_SUBMODULE_UPDATE_CHECKOUT) ? - NULL : git_submodule_update_to_str(submodule->update); - error = submodule_update_config( - submodule, "update", val, (overwrite != 0), false); + val = (sm->update == GIT_SUBMODULE_UPDATE_CHECKOUT) ? + NULL : git_submodule_update_to_str(sm->update); + + if ((error = git_buf_printf(&key, "submodule.%s.update", sm->name)) < 0 || + (error = git_config__update_entry( + cfg, key.ptr, val, overwrite != 0, false)) < 0) + goto cleanup; + + /* success */ + +cleanup: + git_config_free(cfg); + git_buf_free(&key); + git_buf_free(&effective_submodule_url); return error; } -int git_submodule_sync(git_submodule *submodule) +int git_submodule_sync(git_submodule *sm) { - if (!submodule->url) { + int error = 0; + git_config *cfg = NULL; + git_buf key = GIT_BUF_INIT; + git_repository *smrepo = NULL; + + if (!sm->url) { giterr_set(GITERR_SUBMODULE, - "No URL configured for submodule '%s'", submodule->name); + "No URL configured for submodule '%s'", sm->name); return -1; } /* copy URL over to config only if it already exists */ - return submodule_update_config( - submodule, "url", submodule->url, true, true); + if (!(error = git_repository_config__weakptr(&cfg, sm->repo)) && + !(error = git_buf_printf(&key, "submodule.%s.url", sm->name))) + error = git_config__update_entry(cfg, key.ptr, sm->url, true, true); + + /* if submodule exists in the working directory, update remote url */ + + if (!error && + (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) != 0 && + !(error = git_submodule_open(&smrepo, sm))) + { + git_buf remote_name = GIT_BUF_INIT; + + if ((error = git_repository_config__weakptr(&cfg, smrepo)) < 0) + /* return error from reading submodule config */; + else if ((error = lookup_head_remote_key(&remote_name, smrepo)) < 0) { + giterr_clear(); + error = git_buf_sets(&key, "branch.origin.remote"); + } else { + error = git_buf_join3( + &key, '.', "branch", remote_name.ptr, "remote"); + git_buf_free(&remote_name); + } + + if (!error) + error = git_config__update_entry(cfg, key.ptr, sm->url, true, false); + + git_repository_free(smrepo); + } + + git_buf_free(&key); + + return error; } static int git_submodule__open( @@ -737,11 +1174,9 @@ return git_submodule__open(subrepo, sm, false); } -int git_submodule_reload_all(git_repository *repo) +int git_submodule_reload_all(git_repository *repo, int force) { - assert(repo); - git_submodule_config_free(repo); - return load_submodule_config(repo); + return submodule_cache_init(repo, force ? CACHE_FLUSH : CACHE_REFRESH); } static void submodule_update_from_index_entry( @@ -756,7 +1191,7 @@ if (already_found) sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES; else - git_oid_cpy(&sm->index_oid, &ie->oid); + git_oid_cpy(&sm->index_oid, &ie->id); sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS__INDEX_OID_VALID; @@ -817,54 +1252,59 @@ return 0; } -int git_submodule_reload(git_submodule *submodule) + +int git_submodule_reload(git_submodule *sm, int force) { int error = 0; git_config_backend *mods; + git_submodule_cache *cache; - assert(submodule); + GIT_UNUSED(force); - /* refresh index data */ + assert(sm); - if (submodule_update_index(submodule) < 0) - return -1; + cache = sm->repo->_submodules; + + /* refresh index data */ + if ((error = submodule_update_index(sm)) < 0) + return error; /* refresh HEAD tree data */ + if ((error = submodule_update_head(sm)) < 0) + return error; - if (submodule_update_head(submodule) < 0) - return -1; + /* done if bare */ + if (git_repository_is_bare(sm->repo)) + return error; /* refresh config data */ - - mods = open_gitmodules(submodule->repo, false, NULL); + mods = open_gitmodules(cache, GITMODULES_EXISTING); if (mods != NULL) { git_buf path = GIT_BUF_INIT; git_buf_sets(&path, "submodule\\."); - git_buf_text_puts_escape_regex(&path, submodule->name); + git_buf_text_puts_escape_regex(&path, sm->name); git_buf_puts(&path, ".*"); if (git_buf_oom(&path)) error = -1; else error = git_config_file_foreach_match( - mods, path.ptr, submodule_load_from_config, submodule->repo); + mods, path.ptr, submodule_load_from_config, cache); git_buf_free(&path); git_config_file_free(mods); - } - if (error < 0) - return error; + if (error < 0) + return error; + } /* refresh wd data */ + sm->flags &= + ~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID | + GIT_SUBMODULE_STATUS__WD_FLAGS); - submodule->flags = submodule->flags & - ~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID); - - error = submodule_load_from_wd_lite(submodule, submodule->path, NULL); - - return error; + return submodule_load_from_wd_lite(sm); } static void submodule_copy_oid_maybe( @@ -958,32 +1398,69 @@ * INTERNAL FUNCTIONS */ -static git_submodule *submodule_alloc(git_repository *repo, const char *name) +static int submodule_alloc( + git_submodule **out, git_submodule_cache *cache, const char *name) { size_t namelen; git_submodule *sm; if (!name || !(namelen = strlen(name))) { giterr_set(GITERR_SUBMODULE, "Invalid submodule name"); - return NULL; + return -1; } sm = git__calloc(1, sizeof(git_submodule)); - if (sm == NULL) - return NULL; + GITERR_CHECK_ALLOC(sm); sm->name = sm->path = git__strdup(name); if (!sm->name) { git__free(sm); - return NULL; + return -1; } GIT_REFCOUNT_INC(sm); sm->ignore = sm->ignore_default = GIT_SUBMODULE_IGNORE_NONE; sm->update = sm->update_default = GIT_SUBMODULE_UPDATE_CHECKOUT; - sm->repo = repo; + sm->fetch_recurse = sm->fetch_recurse_default = GIT_SUBMODULE_RECURSE_NO; + sm->repo = cache->repo; + sm->branch = NULL; + + *out = sm; + return 0; +} + +static void submodule_cache_remove_item( + git_submodule_cache *cache, + git_submodule *item, + bool free_after_remove) +{ + git_strmap *map; + const char *name, *alt; + + if (!cache || !(map = cache->submodules) || !item) + return; - return sm; + name = item->name; + alt = (item->path != item->name) ? item->path : NULL; + + for (; name; name = alt, alt = NULL) { + khiter_t pos = git_strmap_lookup_index(map, name); + git_submodule *found; + + if (!git_strmap_valid_index(map, pos)) + continue; + + found = git_strmap_value_at(map, pos); + + if (found != item) + continue; + + git_strmap_set_value_at(map, pos, NULL); + git_strmap_delete_at(map, pos); + + if (free_after_remove) + git_submodule_free(found); + } } static void submodule_release(git_submodule *sm) @@ -991,10 +1468,17 @@ if (!sm) return; + if (sm->repo) { + git_submodule_cache *cache = sm->repo->_submodules; + sm->repo = NULL; + submodule_cache_remove_item(cache, sm, false); + } + if (sm->path != sm->name) git__free(sm->path); git__free(sm->name); git__free(sm->url); + git__free(sm->branch); git__memzero(sm, sizeof(*sm)); git__free(sm); } @@ -1007,48 +1491,51 @@ } static int submodule_get( - git_submodule **sm_ptr, - git_repository *repo, + git_submodule **out, + git_submodule_cache *cache, const char *name, const char *alternate) { - git_strmap *smcfg = repo->submodules; + int error = 0; khiter_t pos; git_submodule *sm; - int error; - - assert(repo && name); - pos = git_strmap_lookup_index(smcfg, name); + pos = git_strmap_lookup_index(cache->submodules, name); - if (!git_strmap_valid_index(smcfg, pos) && alternate) - pos = git_strmap_lookup_index(smcfg, alternate); + if (!git_strmap_valid_index(cache->submodules, pos) && alternate) + pos = git_strmap_lookup_index(cache->submodules, alternate); - if (!git_strmap_valid_index(smcfg, pos)) { - sm = submodule_alloc(repo, name); - GITERR_CHECK_ALLOC(sm); + if (!git_strmap_valid_index(cache->submodules, pos)) { + if ((error = submodule_alloc(&sm, cache, name)) < 0) + return error; /* insert value at name - if another thread beats us to it, then use * their record and release our own. */ - pos = kh_put(str, smcfg, sm->name, &error); + pos = kh_put(str, cache->submodules, sm->name, &error); - if (error < 0) { - git_submodule_free(sm); - sm = NULL; - } else if (error == 0) { + if (error < 0) + goto done; + else if (error == 0) { git_submodule_free(sm); - sm = git_strmap_value_at(smcfg, pos); + sm = git_strmap_value_at(cache->submodules, pos); } else { - git_strmap_set_value_at(smcfg, pos, sm); + error = 0; + git_strmap_set_value_at(cache->submodules, pos, sm); } } else { - sm = git_strmap_value_at(smcfg, pos); + sm = git_strmap_value_at(cache->submodules, pos); } - *sm_ptr = sm; +done: + if (error < 0) + git_submodule_free(sm); + else if (out) { + GIT_REFCOUNT_INC(sm); + *out = sm; + } - return (sm != NULL) ? 0 : -1; + return error; } static int submodule_config_error(const char *property, const char *value) @@ -1086,16 +1573,29 @@ return 0; } +int git_submodule_parse_recurse(git_submodule_recurse_t *out, const char *value) +{ + int val; + + if (git_config_lookup_map_value( + &val, _sm_recurse_map, ARRAY_SIZE(_sm_recurse_map), value) < 0) { + *out = GIT_SUBMODULE_RECURSE_YES; + return submodule_config_error("recurse", value); + } + + *out = (git_submodule_recurse_t)val; + return 0; +} + static int submodule_load_from_config( - const git_config_entry *entry, void *data) + const git_config_entry *entry, void *payload) { - git_repository *repo = data; - git_strmap *smcfg = repo->submodules; - const char *namestart, *property, *alternate = NULL; - const char *key = entry->name, *value = entry->value; + git_submodule_cache *cache = payload; + const char *namestart, *property; + const char *key = entry->name, *value = entry->value, *path; + char *alternate = NULL, *replaced = NULL; git_buf name = GIT_BUF_INIT; - git_submodule *sm; - bool is_path; + git_submodule *sm = NULL; int error = 0; if (git__prefixcmp(key, "submodule.") != 0) @@ -1108,15 +1608,11 @@ return 0; property++; - is_path = (strcasecmp(property, "path") == 0); + path = !strcasecmp(property, "path") ? value : NULL; - if (git_buf_set(&name, namestart, property - namestart - 1) < 0) - return -1; - - if (submodule_get(&sm, repo, name.ptr, is_path ? value : NULL) < 0) { - git_buf_free(&name); - return -1; - } + if ((error = git_buf_set(&name, namestart, property - namestart - 1)) < 0 || + (error = submodule_get(&sm, cache, name.ptr, path)) < 0) + goto done; sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG; @@ -1128,19 +1624,49 @@ * should be strcasecmp */ - if (strcmp(sm->name, name.ptr) != 0) { - alternate = sm->name = git_buf_detach(&name); - } else if (is_path && value && strcmp(sm->path, value) != 0) { - alternate = sm->path = git__strdup(value); - if (!sm->path) - error = -1; + if (strcmp(sm->name, name.ptr) != 0) { /* name changed */ + if (!strcmp(sm->path, name.ptr)) { /* already set as path */ + replaced = sm->name; + sm->name = sm->path; + } else { + if (sm->name != sm->path) + replaced = sm->name; + alternate = sm->name = git_buf_detach(&name); + } + } + else if (path && strcmp(path, sm->path) != 0) { /* path changed */ + if (!strcmp(sm->name, value)) { /* already set as name */ + replaced = sm->path; + sm->path = sm->name; + } else { + if (sm->path != sm->name) + replaced = sm->path; + if ((alternate = git__strdup(value)) == NULL) { + error = -1; + goto done; + } + sm->path = alternate; + } + } + + /* Deregister under name being replaced */ + if (replaced) { + git_strmap_delete(cache->submodules, replaced); + git_submodule_free(sm); + git__free(replaced); } + + /* Insert under alternate key */ if (alternate) { void *old_sm = NULL; - git_strmap_insert2(smcfg, alternate, sm, old_sm, error); + git_strmap_insert2(cache->submodules, alternate, sm, old_sm, error); + + if (error < 0) + goto done; + if (error > 0) + error = 0; - if (error >= 0) - GIT_REFCOUNT_INC(sm); /* inserted under a new key */ + GIT_REFCOUNT_INC(sm); /* increase refcount for new key */ /* if we replaced an old module under this key, release the old one */ if (old_sm && ((git_submodule *)old_sm) != sm) { @@ -1149,55 +1675,59 @@ } } - git_buf_free(&name); - if (error < 0) - return error; - /* TODO: Look up path in index and if it is present but not a GITLINK * then this should be deleted (at least to match git's behavior) */ - if (is_path) - return 0; + if (path) + goto done; /* copy other properties into submodule entry */ if (strcasecmp(property, "url") == 0) { git__free(sm->url); sm->url = NULL; - if (value != NULL && (sm->url = git__strdup(value)) == NULL) - return -1; + if (value != NULL && (sm->url = git__strdup(value)) == NULL) { + error = -1; + goto done; + } + } + else if (strcasecmp(property, "branch") == 0) { + git__free(sm->branch); + sm->branch = NULL; + + if (value != NULL && (sm->branch = git__strdup(value)) == NULL) { + error = -1; + goto done; + } } else if (strcasecmp(property, "update") == 0) { - if (git_submodule_parse_update(&sm->update, value) < 0) - return -1; + if ((error = git_submodule_parse_update(&sm->update, value)) < 0) + goto done; sm->update_default = sm->update; } else if (strcasecmp(property, "fetchRecurseSubmodules") == 0) { - if (git__parse_bool(&sm->fetch_recurse, value) < 0) - return submodule_config_error("fetchRecurseSubmodules", value); + if ((error = git_submodule_parse_recurse(&sm->fetch_recurse, value)) < 0) + goto done; + sm->fetch_recurse_default = sm->fetch_recurse; } else if (strcasecmp(property, "ignore") == 0) { - if (git_submodule_parse_ignore(&sm->ignore, value) < 0) - return -1; + if ((error = git_submodule_parse_ignore(&sm->ignore, value)) < 0) + goto done; sm->ignore_default = sm->ignore; } /* ignore other unknown submodule properties */ - return 0; +done: + git_submodule_free(sm); /* offset refcount inc from submodule_get() */ + git_buf_free(&name); + return error; } -static int submodule_load_from_wd_lite( - git_submodule *sm, const char *name, void *payload) +static int submodule_load_from_wd_lite(git_submodule *sm) { git_buf path = GIT_BUF_INIT; - GIT_UNUSED(name); - GIT_UNUSED(payload); - - if (git_repository_is_bare(sm->repo)) - return 0; - if (git_buf_joinpath(&path, git_repository_workdir(sm->repo), sm->path) < 0) return -1; @@ -1208,38 +1738,36 @@ sm->flags |= GIT_SUBMODULE_STATUS_IN_WD; git_buf_free(&path); - return 0; } -static int load_submodule_config_from_index( - git_repository *repo, git_oid *gitmodules_oid) +static int submodule_cache_refresh_from_index( + git_submodule_cache *cache, git_index *idx) { int error; - git_index *index; git_iterator *i; const git_index_entry *entry; - if ((error = git_repository_index__weakptr(&index, repo)) < 0 || - (error = git_iterator_for_index(&i, index, 0, NULL, NULL)) < 0) + if ((error = git_iterator_for_index(&i, idx, 0, NULL, NULL)) < 0) return error; while (!(error = git_iterator_advance(&entry, i))) { - khiter_t pos = git_strmap_lookup_index(repo->submodules, entry->path); + khiter_t pos = git_strmap_lookup_index(cache->submodules, entry->path); git_submodule *sm; - if (git_strmap_valid_index(repo->submodules, pos)) { - sm = git_strmap_value_at(repo->submodules, pos); + if (git_strmap_valid_index(cache->submodules, pos)) { + sm = git_strmap_value_at(cache->submodules, pos); if (S_ISGITLINK(entry->mode)) submodule_update_from_index_entry(sm, entry); else sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE; } else if (S_ISGITLINK(entry->mode)) { - if (!submodule_get(&sm, repo, entry->path, NULL)) + if (!submodule_get(&sm, cache, entry->path, NULL)) { submodule_update_from_index_entry(sm, entry); - } else if (strcmp(entry->path, GIT_MODULES_FILE) == 0) - git_oid_cpy(gitmodules_oid, &entry->oid); + git_submodule_free(sm); + } + } } if (error == GIT_ITEROVER) @@ -1250,44 +1778,33 @@ return error; } -static int load_submodule_config_from_head( - git_repository *repo, git_oid *gitmodules_oid) +static int submodule_cache_refresh_from_head( + git_submodule_cache *cache, git_tree *head) { int error; - git_tree *head; git_iterator *i; const git_index_entry *entry; - /* if we can't look up current head, then there's no submodule in it */ - if (git_repository_head_tree(&head, repo) < 0) { - giterr_clear(); - return 0; - } - - if ((error = git_iterator_for_tree(&i, head, 0, NULL, NULL)) < 0) { - git_tree_free(head); + if ((error = git_iterator_for_tree(&i, head, 0, NULL, NULL)) < 0) return error; - } while (!(error = git_iterator_advance(&entry, i))) { - khiter_t pos = git_strmap_lookup_index(repo->submodules, entry->path); + khiter_t pos = git_strmap_lookup_index(cache->submodules, entry->path); git_submodule *sm; - if (git_strmap_valid_index(repo->submodules, pos)) { - sm = git_strmap_value_at(repo->submodules, pos); + if (git_strmap_valid_index(cache->submodules, pos)) { + sm = git_strmap_value_at(cache->submodules, pos); if (S_ISGITLINK(entry->mode)) - submodule_update_from_head_data( - sm, entry->mode, &entry->oid); + submodule_update_from_head_data(sm, entry->mode, &entry->id); else sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE; } else if (S_ISGITLINK(entry->mode)) { - if (!submodule_get(&sm, repo, entry->path, NULL)) + if (!submodule_get(&sm, cache, entry->path, NULL)) { submodule_update_from_head_data( - sm, entry->mode, &entry->oid); - } else if (strcmp(entry->path, GIT_MODULES_FILE) == 0 && - git_oid_iszero(gitmodules_oid)) { - git_oid_cpy(gitmodules_oid, &entry->oid); + sm, entry->mode, &entry->id); + git_submodule_free(sm); + } } } @@ -1295,17 +1812,15 @@ error = 0; git_iterator_free(i); - git_tree_free(head); return error; } static git_config_backend *open_gitmodules( - git_repository *repo, - bool okay_to_create, - const git_oid *gitmodules_oid) + git_submodule_cache *cache, + int okay_to_create) { - const char *workdir = git_repository_workdir(repo); + const char *workdir = git_repository_workdir(cache->repo); git_buf path = GIT_BUF_INIT; git_config_backend *mods = NULL; @@ -1325,178 +1840,293 @@ } } - if (!mods && gitmodules_oid && !git_oid_iszero(gitmodules_oid)) { - /* TODO: Retrieve .gitmodules content from ODB */ + git_buf_free(&path); - /* Should we actually do this? Core git does not, but it means you - * can't really get much information about submodules on bare repos. - */ + return mods; +} + +static void submodule_cache_free(git_submodule_cache *cache) +{ + git_submodule *sm; + + if (!cache) + return; + + git_strmap_foreach_value(cache->submodules, sm, { + sm->repo = NULL; /* disconnect from repo */ + git_submodule_free(sm); + }); + git_strmap_free(cache->submodules); + + git_buf_free(&cache->gitmodules_path); + git_mutex_free(&cache->lock); + git__free(cache); +} + +static int submodule_cache_alloc( + git_submodule_cache **out, git_repository *repo) +{ + git_submodule_cache *cache = git__calloc(1, sizeof(git_submodule_cache)); + GITERR_CHECK_ALLOC(cache); + + if (git_mutex_init(&cache->lock) < 0) { + giterr_set(GITERR_OS, "Unable to initialize submodule cache lock"); + git__free(cache); + return -1; } - git_buf_free(&path); + if (git_strmap_alloc(&cache->submodules) < 0) { + submodule_cache_free(cache); + return -1; + } - return mods; + cache->repo = repo; + git_buf_init(&cache->gitmodules_path, 0); + + *out = cache; + return 0; } -static int load_submodule_config(git_repository *repo) +static int submodule_cache_refresh(git_submodule_cache *cache, int refresh) { - int error; - git_oid gitmodules_oid; + int error = 0, update_index, update_head, update_gitmod; + git_index *idx = NULL; + git_tree *head = NULL; + const char *wd = NULL; git_buf path = GIT_BUF_INIT; + git_submodule *sm; git_config_backend *mods = NULL; + uint32_t mask; - if (repo->submodules) + if (!cache || !cache->repo || !refresh) return 0; - memset(&gitmodules_oid, 0, sizeof(gitmodules_oid)); - - /* Submodule data is kept in a hashtable keyed by both name and path. - * These are usually the same, but that is not guaranteed. - */ - if (!repo->submodules) { - repo->submodules = git_strmap_alloc(); - GITERR_CHECK_ALLOC(repo->submodules); + if (git_mutex_lock(&cache->lock) < 0) { + giterr_set(GITERR_OS, "Unable to acquire lock on submodule cache"); + return -1; } - /* add submodule information from index */ + /* get sources that we will need to check */ - if ((error = load_submodule_config_from_index(repo, &gitmodules_oid)) < 0) + if (git_repository_index(&idx, cache->repo) < 0) + giterr_clear(); + if (git_repository_head_tree(&head, cache->repo) < 0) + giterr_clear(); + + wd = git_repository_workdir(cache->repo); + if (wd && (error = git_buf_joinpath(&path, wd, GIT_MODULES_FILE)) < 0) goto cleanup; + /* check for invalidation */ + + if (refresh == CACHE_FLUSH) + update_index = update_head = update_gitmod = true; + else { + update_index = + !idx || git_index__changed_relative_to(idx, &cache->index_stamp); + update_head = + !head || !git_oid_equal(&cache->head_id, git_tree_id(head)); + + update_gitmod = (wd != NULL) ? + git_futils_filestamp_check(&cache->gitmodules_stamp, path.ptr) : + (cache->gitmodules_stamp.mtime != 0); + } + + /* clear submodule flags that are to be refreshed */ + + mask = 0; + if (!idx || update_index) + mask |= GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS__INDEX_FLAGS | + GIT_SUBMODULE_STATUS__INDEX_OID_VALID | + GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES; + if (!head || update_head) + mask |= GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS__HEAD_OID_VALID; + if (update_gitmod) + mask |= GIT_SUBMODULE_STATUS_IN_CONFIG; + if (mask != 0) + mask |= GIT_SUBMODULE_STATUS_IN_WD | + GIT_SUBMODULE_STATUS__WD_SCANNED | + GIT_SUBMODULE_STATUS__WD_FLAGS | + GIT_SUBMODULE_STATUS__WD_OID_VALID; + else + goto cleanup; /* nothing to do */ + + submodule_cache_clear_flags(cache, mask); + + /* add back submodule information from index */ + + if (idx && update_index) { + if ((error = submodule_cache_refresh_from_index(cache, idx)) < 0) + goto cleanup; + + git_futils_filestamp_set( + &cache->index_stamp, git_index__filestamp(idx)); + } + /* add submodule information from HEAD */ - if ((error = load_submodule_config_from_head(repo, &gitmodules_oid)) < 0) - goto cleanup; + if (head && update_head) { + if ((error = submodule_cache_refresh_from_head(cache, head)) < 0) + goto cleanup; - /* add submodule information from .gitmodules */ + git_oid_cpy(&cache->head_id, git_tree_id(head)); + } - if ((mods = open_gitmodules(repo, false, &gitmodules_oid)) != NULL) - error = git_config_file_foreach(mods, submodule_load_from_config, repo); + /* add submodule information from .gitmodules */ - if (error != 0) - goto cleanup; + if (wd && update_gitmod > 0) { + if ((mods = open_gitmodules(cache, false)) != NULL && + (error = git_config_file_foreach( + mods, submodule_load_from_config, cache)) < 0) + goto cleanup; + } - /* shallow scan submodules in work tree */ + /* shallow scan submodules in work tree as needed */ - if (!git_repository_is_bare(repo)) - error = git_submodule_foreach(repo, submodule_load_from_wd_lite, NULL); + if (wd && mask != 0) { + git_strmap_foreach_value(cache->submodules, sm, { + submodule_load_from_wd_lite(sm); + }); + } + + /* remove submodules that no longer exist */ + + git_strmap_foreach_value(cache->submodules, sm, { + /* purge unless in HEAD, index, or .gitmodules; no sm for wd only */ + if (sm != NULL && + !(sm->flags & + (GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS_IN_CONFIG))) + submodule_cache_remove_item(cache, sm, true); + }); cleanup: + git_config_file_free(mods); + + /* TODO: if we got an error, mark submodule config as invalid? */ + + git_mutex_unlock(&cache->lock); + + git_index_free(idx); + git_tree_free(head); git_buf_free(&path); - if (mods != NULL) - git_config_file_free(mods); + return error; +} - if (error) - git_submodule_config_free(repo); +static int submodule_cache_init(git_repository *repo, int cache_refresh) +{ + int error = 0; + git_submodule_cache *cache = NULL; + + /* if submodules already exist, just refresh as requested */ + if (repo->_submodules) + return submodule_cache_refresh(repo->_submodules, cache_refresh); + + /* otherwise create a new cache, load it, and atomically swap it in */ + if (!(error = submodule_cache_alloc(&cache, repo)) && + !(error = submodule_cache_refresh(cache, CACHE_FLUSH))) + cache = git__compare_and_swap(&repo->_submodules, NULL, cache); + + /* might have raced with another thread to set cache, so free if needed */ + if (cache) + submodule_cache_free(cache); return error; } -static int lookup_head_remote(git_buf *url, git_repository *repo) +/* Lookup name of remote of the local tracking branch HEAD points to */ +static int lookup_head_remote_key(git_buf *remote_name, git_repository *repo) { int error; - git_config *cfg; - git_reference *head = NULL, *remote = NULL; - const char *tgt, *scan; - git_buf key = GIT_BUF_INIT; + git_reference *head = NULL; + git_buf upstream_name = GIT_BUF_INIT; - /* 1. resolve HEAD -> refs/heads/BRANCH - * 2. lookup config branch.BRANCH.remote -> ORIGIN - * 3. lookup remote.ORIGIN.url - */ - - if ((error = git_repository_config__weakptr(&cfg, repo)) < 0) + /* lookup and dereference HEAD */ + if ((error = git_repository_head(&head, repo)) < 0) return error; - if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) { - giterr_set(GITERR_SUBMODULE, - "Cannot resolve relative URL when HEAD cannot be resolved"); + /** + * If head does not refer to a branch, then return + * GIT_ENOTFOUND to indicate that we could not find + * a remote key for the local tracking branch HEAD points to. + **/ + if (!git_reference_is_branch(head)) { + giterr_set(GITERR_INVALID, + "HEAD does not refer to a branch."); error = GIT_ENOTFOUND; - goto cleanup; + goto done; } - if (git_reference_type(head) != GIT_REF_SYMBOLIC) { - giterr_set(GITERR_SUBMODULE, - "Cannot resolve relative URL when HEAD is not symbolic"); - error = GIT_ENOTFOUND; - goto cleanup; - } + /* lookup remote tracking branch of HEAD */ + if ((error = git_branch_upstream_name( + &upstream_name, + repo, + git_reference_name(head))) < 0) + goto done; + + /* lookup remote of remote tracking branch */ + if ((error = git_branch_remote_name(remote_name, repo, upstream_name.ptr)) < 0) + goto done; - if ((error = git_branch_upstream(&remote, head)) < 0) - goto cleanup; +done: + git_buf_free(&upstream_name); + git_reference_free(head); - /* remote should refer to something like refs/remotes/ORIGIN/BRANCH */ + return error; +} - if (git_reference_type(remote) != GIT_REF_SYMBOLIC || - git__prefixcmp(git_reference_symbolic_target(remote), GIT_REFS_REMOTES_DIR) != 0) - { - giterr_set(GITERR_SUBMODULE, - "Cannot resolve relative URL when HEAD is not symbolic"); - error = GIT_ENOTFOUND; - goto cleanup; - } +/* Lookup the remote of the local tracking branch HEAD points to */ +static int lookup_head_remote(git_remote **remote, git_repository *repo) +{ + int error; + git_buf remote_name = GIT_BUF_INIT; - scan = tgt = git_reference_symbolic_target(remote) + strlen(GIT_REFS_REMOTES_DIR); - while (*scan && (*scan != '/' || (scan > tgt && scan[-1] != '\\'))) - scan++; /* find non-escaped slash to end ORIGIN name */ + /* lookup remote of remote tracking branch name */ + if (!(error = lookup_head_remote_key(&remote_name, repo))) + error = git_remote_lookup(remote, repo, remote_name.ptr); - error = git_buf_printf(&key, "remote.%.*s.url", (int)(scan - tgt), tgt); - if (error < 0) - goto cleanup; + git_buf_free(&remote_name); - if ((error = git_config_get_string(&tgt, cfg, key.ptr)) < 0) - goto cleanup; + return error; +} - error = git_buf_sets(url, tgt); +/* Lookup remote, either from HEAD or fall back on origin */ +static int lookup_default_remote(git_remote **remote, git_repository *repo) +{ + int error = lookup_head_remote(remote, repo); -cleanup: - git_buf_free(&key); - git_reference_free(head); - git_reference_free(remote); + /* if that failed, use 'origin' instead */ + if (error == GIT_ENOTFOUND) + error = git_remote_lookup(remote, repo, "origin"); + + if (error == GIT_ENOTFOUND) + giterr_set( + GITERR_SUBMODULE, + "Cannot get default remote for submodule - no local tracking " + "branch for HEAD and origin does not exist"); return error; } -static int submodule_update_config( - git_submodule *submodule, - const char *attr, - const char *value, - bool overwrite, - bool only_existing) +static int get_url_base(git_buf *url, git_repository *repo) { int error; - git_config *config; - git_buf key = GIT_BUF_INIT; - const char *old = NULL; - - assert(submodule); - - error = git_repository_config__weakptr(&config, submodule->repo); - if (error < 0) - return error; - - error = git_buf_printf(&key, "submodule.%s.%s", submodule->name, attr); - if (error < 0) - goto cleanup; + git_remote *remote = NULL; - if (git_config_get_string(&old, config, key.ptr) < 0) + if (!(error = lookup_default_remote(&remote, repo))) { + error = git_buf_sets(url, git_remote_url(remote)); + git_remote_free(remote); + } + else if (error == GIT_ENOTFOUND) { + /* if repository does not have a default remote, use workdir instead */ giterr_clear(); + error = git_buf_sets(url, git_repository_workdir(repo)); + } - if (!old && only_existing) - goto cleanup; - if (old && !overwrite) - goto cleanup; - if ((!old && !value) || (old && value && strcmp(old, value) == 0)) - goto cleanup; - - if (!value) - error = git_config_delete_entry(config, key.ptr); - else - error = git_config_set_string(config, key.ptr, value); - -cleanup: - git_buf_free(&key); return error; } @@ -1517,6 +2147,7 @@ *status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED; } + static void submodule_get_wd_status( unsigned int *status, git_submodule *sm, diff -Nru libgit2-0.20.0/src/submodule.h libgit2-0.22.2/src/submodule.h --- libgit2-0.20.0/src/submodule.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/submodule.h 2015-03-24 16:10:45.000000000 +0000 @@ -60,7 +60,9 @@ * - `update_default` is the update value from the config * - `ignore` is a git_submodule_ignore_t value - see gitmodules(5) ignore. * - `ignore_default` is the ignore value from the config - * - `fetch_recurse` is 0 or 1 - see gitmodules(5) fetchRecurseSubmodules. + * - `fetch_recurse` is a git_submodule_recurse_t value - see gitmodules(5) + * fetchRecurseSubmodules. + * - `fetch_recurse_default` is the recurse value from the config * * - `repo` is the parent repository that contains this submodule. * - `flags` after for internal use, tracking where this submodule has been @@ -81,11 +83,13 @@ char *name; char *path; /* important: may just point to "name" string */ char *url; + char *branch; git_submodule_update_t update; git_submodule_update_t update_default; git_submodule_ignore_t ignore; git_submodule_ignore_t ignore_default; - int fetch_recurse; + git_submodule_recurse_t fetch_recurse; + git_submodule_recurse_t fetch_recurse_default; /* internal information */ git_repository *repo; @@ -95,6 +99,29 @@ git_oid wd_oid; }; +/** + * The git_submodule_cache stores known submodules along with timestamps, + * etc. about when they were loaded + */ +typedef struct { + git_repository *repo; + git_strmap *submodules; + git_mutex lock; + + /* cache invalidation data */ + git_oid head_id; + git_futils_filestamp index_stamp; + git_buf gitmodules_path; + git_futils_filestamp gitmodules_stamp; + git_futils_filestamp config_stamp; +} git_submodule_cache; + +/* Force revalidation of submodule data cache (alloc as needed) */ +extern int git_submodule_cache_refresh(git_repository *repo); + +/* Release all submodules */ +extern void git_submodule_cache_free(git_repository *repo); + /* Additional flags on top of public GIT_SUBMODULE_STATUS values */ enum { GIT_SUBMODULE_STATUS__WD_SCANNED = (1u << 20), @@ -110,6 +137,13 @@ #define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \ ((S) & ~(0xFFFFFFFFu << 20)) +/* Internal submodule check does not attempt to refresh cached data */ +extern bool git_submodule__is_submodule(git_repository *repo, const char *name); + +/* Internal lookup does not attempt to refresh cached data */ +extern int git_submodule__lookup( + git_submodule **out, git_repository *repo, const char *path); + /* Internal status fn returns status and optionally the various OIDs */ extern int git_submodule__status( unsigned int *out_status, @@ -124,9 +158,6 @@ git_repository **repo, git_submodule *submodule); -/* Release reference to submodule object - not currently for external use */ -extern void git_submodule_free(git_submodule *sm); - extern int git_submodule_parse_ignore( git_submodule_ignore_t *out, const char *value); extern int git_submodule_parse_update( @@ -134,5 +165,6 @@ extern const char *git_submodule_ignore_to_str(git_submodule_ignore_t); extern const char *git_submodule_update_to_str(git_submodule_update_t); +extern const char *git_submodule_recurse_to_str(git_submodule_recurse_t); #endif diff -Nru libgit2-0.20.0/src/sysdir.c libgit2-0.22.2/src/sysdir.c --- libgit2-0.20.0/src/sysdir.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/sysdir.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,252 @@ +/* + * 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 "sysdir.h" +#include "global.h" +#include "buffer.h" +#include "path.h" +#include +#if GIT_WIN32 +#include "win32/findfile.h" +#endif + +static int git_sysdir_guess_system_dirs(git_buf *out) +{ +#ifdef GIT_WIN32 + return git_win32__find_system_dirs(out, L"etc\\"); +#else + return git_buf_sets(out, "/etc"); +#endif +} + +static int git_sysdir_guess_global_dirs(git_buf *out) +{ +#ifdef GIT_WIN32 + return git_win32__find_global_dirs(out); +#else + return git_buf_sets(out, getenv("HOME")); +#endif +} + +static int git_sysdir_guess_xdg_dirs(git_buf *out) +{ +#ifdef GIT_WIN32 + return git_win32__find_xdg_dirs(out); +#else + const char *env = NULL; + + if ((env = getenv("XDG_CONFIG_HOME")) != NULL) + return git_buf_joinpath(out, env, "git"); + else if ((env = getenv("HOME")) != NULL) + return git_buf_joinpath(out, env, ".config/git"); + + git_buf_clear(out); + return 0; +#endif +} + +static int git_sysdir_guess_template_dirs(git_buf *out) +{ +#ifdef GIT_WIN32 + return git_win32__find_system_dirs(out, L"share\\git-core\\templates"); +#else + return git_buf_sets(out, "/usr/share/git-core/templates"); +#endif +} + +typedef int (*git_sysdir_guess_cb)(git_buf *out); + +static git_buf git_sysdir__dirs[GIT_SYSDIR__MAX] = + { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT }; + +static git_sysdir_guess_cb git_sysdir__dir_guess[GIT_SYSDIR__MAX] = { + git_sysdir_guess_system_dirs, + git_sysdir_guess_global_dirs, + git_sysdir_guess_xdg_dirs, + git_sysdir_guess_template_dirs, +}; + +static int git_sysdir__dirs_shutdown_set = 0; + +int git_sysdir_global_init(void) +{ + git_sysdir_t i; + const git_buf *path; + int error = 0; + + for (i = 0; !error && i < GIT_SYSDIR__MAX; i++) + error = git_sysdir_get(&path, i); + + return error; +} + +void git_sysdir_global_shutdown(void) +{ + int i; + for (i = 0; i < GIT_SYSDIR__MAX; ++i) + git_buf_free(&git_sysdir__dirs[i]); + + git_sysdir__dirs_shutdown_set = 0; +} + +static int git_sysdir_check_selector(git_sysdir_t which) +{ + if (which < GIT_SYSDIR__MAX) + return 0; + + giterr_set(GITERR_INVALID, "config directory selector out of range"); + return -1; +} + + +int git_sysdir_get(const git_buf **out, git_sysdir_t which) +{ + assert(out); + + *out = NULL; + + GITERR_CHECK_ERROR(git_sysdir_check_selector(which)); + + if (!git_buf_len(&git_sysdir__dirs[which])) { + /* prepare shutdown if we're going to need it */ + if (!git_sysdir__dirs_shutdown_set) { + git__on_shutdown(git_sysdir_global_shutdown); + git_sysdir__dirs_shutdown_set = 1; + } + + GITERR_CHECK_ERROR( + git_sysdir__dir_guess[which](&git_sysdir__dirs[which])); + } + + *out = &git_sysdir__dirs[which]; + return 0; +} + +int git_sysdir_get_str( + char *out, + size_t outlen, + git_sysdir_t which) +{ + const git_buf *path = NULL; + + GITERR_CHECK_ERROR(git_sysdir_check_selector(which)); + GITERR_CHECK_ERROR(git_sysdir_get(&path, which)); + + if (!out || path->size >= outlen) { + giterr_set(GITERR_NOMEMORY, "Buffer is too short for the path"); + return GIT_EBUFS; + } + + git_buf_copy_cstr(out, outlen, path); + return 0; +} + +#define PATH_MAGIC "$PATH" + +int git_sysdir_set(git_sysdir_t which, const char *search_path) +{ + const char *expand_path = NULL; + git_buf merge = GIT_BUF_INIT; + + GITERR_CHECK_ERROR(git_sysdir_check_selector(which)); + + if (search_path != NULL) + expand_path = strstr(search_path, PATH_MAGIC); + + /* init with default if not yet done and needed (ignoring error) */ + if ((!search_path || expand_path) && + !git_buf_len(&git_sysdir__dirs[which])) + git_sysdir__dir_guess[which](&git_sysdir__dirs[which]); + + /* if $PATH is not referenced, then just set the path */ + if (!expand_path) + return git_buf_sets(&git_sysdir__dirs[which], search_path); + + /* otherwise set to join(before $PATH, old value, after $PATH) */ + if (expand_path > search_path) + git_buf_set(&merge, search_path, expand_path - search_path); + + if (git_buf_len(&git_sysdir__dirs[which])) + git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, + merge.ptr, git_sysdir__dirs[which].ptr); + + expand_path += strlen(PATH_MAGIC); + if (*expand_path) + git_buf_join(&merge, GIT_PATH_LIST_SEPARATOR, merge.ptr, expand_path); + + git_buf_swap(&git_sysdir__dirs[which], &merge); + git_buf_free(&merge); + + return git_buf_oom(&git_sysdir__dirs[which]) ? -1 : 0; +} + +static int git_sysdir_find_in_dirlist( + git_buf *path, + const char *name, + git_sysdir_t which, + const char *label) +{ + size_t len; + const char *scan, *next = NULL; + const git_buf *syspath; + + GITERR_CHECK_ERROR(git_sysdir_get(&syspath, which)); + if (!syspath || !git_buf_len(syspath)) + goto done; + + for (scan = git_buf_cstr(syspath); scan; scan = next) { + /* find unescaped separator or end of string */ + for (next = scan; *next; ++next) { + if (*next == GIT_PATH_LIST_SEPARATOR && + (next <= scan || next[-1] != '\\')) + break; + } + + len = (size_t)(next - scan); + next = (*next ? next + 1 : NULL); + if (!len) + continue; + + GITERR_CHECK_ERROR(git_buf_set(path, scan, len)); + if (name) + GITERR_CHECK_ERROR(git_buf_joinpath(path, path->ptr, name)); + + if (git_path_exists(path->ptr)) + return 0; + } + +done: + git_buf_free(path); + giterr_set(GITERR_OS, "The %s file '%s' doesn't exist", label, name); + return GIT_ENOTFOUND; +} + +int git_sysdir_find_system_file(git_buf *path, const char *filename) +{ + return git_sysdir_find_in_dirlist( + path, filename, GIT_SYSDIR_SYSTEM, "system"); +} + +int git_sysdir_find_global_file(git_buf *path, const char *filename) +{ + return git_sysdir_find_in_dirlist( + path, filename, GIT_SYSDIR_GLOBAL, "global"); +} + +int git_sysdir_find_xdg_file(git_buf *path, const char *filename) +{ + return git_sysdir_find_in_dirlist( + path, filename, GIT_SYSDIR_XDG, "global/xdg"); +} + +int git_sysdir_find_template_dir(git_buf *path) +{ + return git_sysdir_find_in_dirlist( + path, NULL, GIT_SYSDIR_TEMPLATE, "template"); +} + diff -Nru libgit2-0.20.0/src/sysdir.h libgit2-0.22.2/src/sysdir.h --- libgit2-0.20.0/src/sysdir.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/sysdir.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,101 @@ +/* + * 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_sysdir_h__ +#define INCLUDE_sysdir_h__ + +#include "common.h" +#include "posix.h" +#include "buffer.h" + +/** + * Find a "global" file (i.e. one in a user's home directory). + * + * @param path buffer to write the full path into + * @param filename name of file to find in the home directory + * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error + */ +extern int git_sysdir_find_global_file(git_buf *path, const char *filename); + +/** + * Find an "XDG" file (i.e. one in user's XDG config path). + * + * @param path buffer to write the full path into + * @param filename name of file to find in the home directory + * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error + */ +extern int git_sysdir_find_xdg_file(git_buf *path, const char *filename); + +/** + * Find a "system" file (i.e. one shared for all users of the system). + * + * @param path buffer to write the full path into + * @param filename name of file to find in the home directory + * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error + */ +extern int git_sysdir_find_system_file(git_buf *path, const char *filename); + +/** + * Find template directory. + * + * @param path buffer to write the full path into + * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error + */ +extern int git_sysdir_find_template_dir(git_buf *path); + +typedef enum { + GIT_SYSDIR_SYSTEM = 0, + GIT_SYSDIR_GLOBAL = 1, + GIT_SYSDIR_XDG = 2, + GIT_SYSDIR_TEMPLATE = 3, + GIT_SYSDIR__MAX = 4, +} git_sysdir_t; + +/** + * Configures global data for configuration file search paths. + * + * @return 0 on success, <0 on failure + */ +extern int git_sysdir_global_init(void); + +/** + * Get the search path for global/system/xdg files + * + * @param out pointer to git_buf containing search path + * @param which which list of paths to return + * @return 0 on success, <0 on failure + */ +extern int git_sysdir_get(const git_buf **out, git_sysdir_t which); + +/** + * Get search path into a preallocated buffer + * + * @param out String buffer to write into + * @param outlen Size of string buffer + * @param which Which search path to return + * @return 0 on success, GIT_EBUFS if out is too small, <0 on other failure + */ + +extern int git_sysdir_get_str(char *out, size_t outlen, git_sysdir_t which); + +/** + * Set search paths for global/system/xdg files + * + * The first occurrence of the magic string "$PATH" in the new value will + * be replaced with the old value of the search path. + * + * @param which Which search path to modify + * @param paths New search path (separated by GIT_PATH_LIST_SEPARATOR) + * @return 0 on success, <0 on failure (allocation error) + */ +extern int git_sysdir_set(git_sysdir_t which, const char *paths); + +/** + * Free the configuration file search paths. + */ +extern void git_sysdir_global_shutdown(void); + +#endif /* INCLUDE_sysdir_h__ */ diff -Nru libgit2-0.20.0/src/tag.c libgit2-0.22.2/src/tag.c --- libgit2-0.20.0/src/tag.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/tag.c 2015-03-24 16:10:45.000000000 +0000 @@ -258,7 +258,7 @@ goto cleanup; /** Ensure the tag name doesn't conflict with an already existing - * reference unless overwriting has explictly been requested **/ + * reference unless overwriting has explicitly been requested **/ if (error == 0 && !allow_ref_overwrite) { git_buf_free(&ref_name); giterr_set(GITERR_TAG, "Tag already exists"); @@ -271,7 +271,7 @@ } else git_oid_cpy(oid, git_object_id(target)); - error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite); + error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL, NULL); cleanup: git_reference_free(new_ref); @@ -363,20 +363,22 @@ } /* write the buffer */ - if (git_odb_open_wstream(&stream, odb, strlen(buffer), GIT_OBJ_TAG) < 0) - return -1; + if ((error = git_odb_open_wstream( + &stream, odb, strlen(buffer), GIT_OBJ_TAG)) < 0) + return error; - git_odb_stream_write(stream, buffer, strlen(buffer)); + if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer)))) + error = git_odb_stream_finalize_write(oid, stream); - error = git_odb_stream_finalize_write(oid, stream); git_odb_stream_free(stream); if (error < 0) { git_buf_free(&ref_name); - return -1; + return error; } - error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite); + error = git_reference_create( + &new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL, NULL); git_reference_free(new_ref); git_buf_free(&ref_name); @@ -404,8 +406,9 @@ if (error < 0) return error; - if ((error = git_reference_delete(tag_ref)) == 0) - git_reference_free(tag_ref); + error = git_reference_delete(tag_ref); + + git_reference_free(tag_ref); return error; } @@ -418,16 +421,19 @@ static int tags_cb(const char *ref, void *data) { + int error; git_oid oid; tag_cb_data *d = (tag_cb_data *)data; if (git__prefixcmp(ref, GIT_REFS_TAGS_DIR) != 0) return 0; /* no tag */ - if (git_reference_name_to_id(&oid, d->repo, ref) < 0) - return -1; + if (!(error = git_reference_name_to_id(&oid, d->repo, ref))) { + if ((error = d->cb(ref, &oid, d->cb_data)) != 0) + giterr_set_after_callback_function(error, "git_tag_foreach"); + } - return d->cb(ref, &oid, d->cb_data); + return error; } int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data) @@ -455,8 +461,14 @@ tag_filter_data *filter = (tag_filter_data *)data; GIT_UNUSED(oid); - if (!*filter->pattern || p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0) - return git_vector_insert(filter->taglist, git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN)); + if (!*filter->pattern || + p_fnmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0) + { + char *matched = git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN); + GITERR_CHECK_ALLOC(matched); + + return git_vector_insert(filter->taglist, matched); + } return 0; } @@ -469,20 +481,20 @@ assert(tag_names && repo && pattern); - if (git_vector_init(&taglist, 8, NULL) < 0) - return -1; + if ((error = git_vector_init(&taglist, 8, NULL)) < 0) + return error; filter.taglist = &taglist; filter.pattern = pattern; error = git_tag_foreach(repo, &tag_list_cb, (void *)&filter); - if (error < 0) { + + if (error < 0) git_vector_free(&taglist); - return -1; - } - tag_names->strings = (char **)taglist.contents; - tag_names->count = taglist.length; + tag_names->strings = + (char **)git_vector_detach(&tag_names->count, NULL, &taglist); + return 0; } diff -Nru libgit2-0.20.0/src/thread-utils.h libgit2-0.22.2/src/thread-utils.h --- libgit2-0.20.0/src/thread-utils.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/thread-utils.h 2015-03-24 16:10:45.000000000 +0000 @@ -40,12 +40,18 @@ #ifdef GIT_THREADS -#define git_thread pthread_t -#define git_thread_create(thread, attr, start_routine, arg) \ - pthread_create(thread, attr, start_routine, arg) -#define git_thread_kill(thread) pthread_cancel(thread) -#define git_thread_exit(status) pthread_exit(status) -#define git_thread_join(id, status) pthread_join(id, status) +#if !defined(GIT_WIN32) + +typedef struct { + pthread_t thread; +} git_thread; + +#define git_thread_create(git_thread_ptr, attr, start_routine, arg) \ + pthread_create(&(git_thread_ptr)->thread, attr, start_routine, arg) +#define git_thread_join(git_thread_ptr, status) \ + pthread_join((git_thread_ptr)->thread, status) + +#endif /* Pthreads Mutex */ #define git_mutex pthread_mutex_t @@ -173,14 +179,14 @@ #define git_thread unsigned int #define git_thread_create(thread, attr, start_routine, arg) 0 -#define git_thread_kill(thread) (void)0 -#define git_thread_exit(status) (void)0 #define git_thread_join(id, status) (void)0 /* Pthreads Mutex */ #define git_mutex unsigned int -#define git_mutex_init(a) 0 -#define git_mutex_lock(a) 0 +GIT_INLINE(int) git_mutex_init(git_mutex *mutex) \ + { GIT_UNUSED(mutex); return 0; } +GIT_INLINE(int) git_mutex_lock(git_mutex *mutex) \ + { GIT_UNUSED(mutex); return 0; } #define git_mutex_unlock(a) (void)0 #define git_mutex_free(a) (void)0 diff -Nru libgit2-0.20.0/src/trace.h libgit2-0.22.2/src/trace.h --- libgit2-0.20.0/src/trace.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/trace.h 2015-03-24 16:10:45.000000000 +0000 @@ -7,8 +7,6 @@ #ifndef INCLUDE_trace_h__ #define INCLUDE_trace_h__ -#include - #include #include "buffer.h" @@ -48,8 +46,16 @@ #else +GIT_INLINE(void) git_trace__null( + git_trace_level_t level, + const char *fmt, ...) +{ + GIT_UNUSED(level); + GIT_UNUSED(fmt); +} + #define git_trace_level() ((void)0) -#define git_trace(lvl, ...) ((void)0) +#define git_trace git_trace__null #endif diff -Nru libgit2-0.20.0/src/transaction.c libgit2-0.22.2/src/transaction.c --- libgit2-0.20.0/src/transaction.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/transaction.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,352 @@ +/* + * 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 "repository.h" +#include "strmap.h" +#include "refdb.h" +#include "pool.h" +#include "reflog.h" +#include "signature.h" + +#include "git2/signature.h" +#include "git2/sys/refs.h" +#include "git2/sys/refdb_backend.h" + +GIT__USE_STRMAP; + +typedef struct { + const char *name; + void *payload; + + git_ref_t ref_type; + union { + git_oid id; + char *symbolic; + } target; + git_reflog *reflog; + + const char *message; + git_signature *sig; + + unsigned int committed :1, + remove :1; +} transaction_node; + +struct git_transaction { + git_repository *repo; + git_refdb *db; + + git_strmap *locks; + git_pool pool; +}; + +int git_transaction_new(git_transaction **out, git_repository *repo) +{ + int error; + git_pool pool; + git_transaction *tx = NULL; + + assert(out && repo); + + if ((error = git_pool_init(&pool, 1, 0)) < 0) + return error; + + tx = git_pool_mallocz(&pool, sizeof(git_transaction)); + if (!tx) { + error = -1; + goto on_error; + } + + if ((error = git_strmap_alloc(&tx->locks)) < 0) { + error = -1; + goto on_error; + } + + if ((error = git_repository_refdb(&tx->db, repo)) < 0) + goto on_error; + + memcpy(&tx->pool, &pool, sizeof(git_pool)); + tx->repo = repo; + *out = tx; + return 0; + +on_error: + git_pool_clear(&pool); + return error; +} + +int git_transaction_lock_ref(git_transaction *tx, const char *refname) +{ + int error; + transaction_node *node; + + assert(tx && refname); + + node = git_pool_mallocz(&tx->pool, sizeof(transaction_node)); + GITERR_CHECK_ALLOC(node); + + node->name = git_pool_strdup(&tx->pool, refname); + GITERR_CHECK_ALLOC(node->name); + + if ((error = git_refdb_lock(&node->payload, tx->db, refname)) < 0) + return error; + + git_strmap_insert(tx->locks, node->name, node, error); + if (error < 0) + goto cleanup; + + return 0; + +cleanup: + git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL); + + return error; +} + +static int find_locked(transaction_node **out, git_transaction *tx, const char *refname) +{ + git_strmap_iter pos; + transaction_node *node; + + pos = git_strmap_lookup_index(tx->locks, refname); + if (!git_strmap_valid_index(tx->locks, pos)) { + giterr_set(GITERR_REFERENCE, "the specified reference is not locked"); + return GIT_ENOTFOUND; + } + + node = git_strmap_value_at(tx->locks, pos); + + *out = node; + return 0; +} + +static int copy_common(transaction_node *node, git_transaction *tx, const git_signature *sig, const char *msg) +{ + if (sig && git_signature__pdup(&node->sig, sig, &tx->pool) < 0) + return -1; + + if (!node->sig) { + git_signature *tmp; + int error; + + if (git_reference__log_signature(&tmp, tx->repo) < 0) + return -1; + + /* make sure the sig we use is in our pool */ + error = git_signature__pdup(&node->sig, tmp, &tx->pool); + git_signature_free(tmp); + if (error < 0) + return error; + } + + if (msg) { + node->message = git_pool_strdup(&tx->pool, msg); + GITERR_CHECK_ALLOC(node->message); + } + + return 0; +} + +int git_transaction_set_target(git_transaction *tx, const char *refname, const git_oid *target, const git_signature *sig, const char *msg) +{ + int error; + transaction_node *node; + + assert(tx && refname && target); + + if ((error = find_locked(&node, tx, refname)) < 0) + return error; + + if ((error = copy_common(node, tx, sig, msg)) < 0) + return error; + + git_oid_cpy(&node->target.id, target); + node->ref_type = GIT_REF_OID; + + return 0; +} + +int git_transaction_set_symbolic_target(git_transaction *tx, const char *refname, const char *target, const git_signature *sig, const char *msg) +{ + int error; + transaction_node *node; + + assert(tx && refname && target); + + if ((error = find_locked(&node, tx, refname)) < 0) + return error; + + if ((error = copy_common(node, tx, sig, msg)) < 0) + return error; + + node->target.symbolic = git_pool_strdup(&tx->pool, target); + GITERR_CHECK_ALLOC(node->target.symbolic); + node->ref_type = GIT_REF_SYMBOLIC; + + return 0; +} + +int git_transaction_remove(git_transaction *tx, const char *refname) +{ + int error; + transaction_node *node; + + if ((error = find_locked(&node, tx, refname)) < 0) + return error; + + node->remove = true; + node->ref_type = GIT_REF_OID; /* the id will be ignored */ + + return 0; +} + +static int dup_reflog(git_reflog **out, const git_reflog *in, git_pool *pool) +{ + git_reflog *reflog; + git_reflog_entry *entries; + size_t len, i; + + reflog = git_pool_mallocz(pool, sizeof(git_reflog)); + GITERR_CHECK_ALLOC(reflog); + + reflog->ref_name = git_pool_strdup(pool, in->ref_name); + GITERR_CHECK_ALLOC(reflog->ref_name); + + len = in->entries.length; + reflog->entries.length = len; + reflog->entries.contents = git_pool_mallocz(pool, len * sizeof(void *)); + GITERR_CHECK_ALLOC(reflog->entries.contents); + + entries = git_pool_mallocz(pool, len * sizeof(git_reflog_entry)); + GITERR_CHECK_ALLOC(entries); + + for (i = 0; i < len; i++) { + const git_reflog_entry *src; + git_reflog_entry *tgt; + + tgt = &entries[i]; + reflog->entries.contents[i] = tgt; + + src = git_vector_get(&in->entries, i); + git_oid_cpy(&tgt->oid_old, &src->oid_old); + git_oid_cpy(&tgt->oid_cur, &src->oid_cur); + + tgt->msg = git_pool_strdup(pool, src->msg); + GITERR_CHECK_ALLOC(tgt->msg); + + if (git_signature__pdup(&tgt->committer, src->committer, pool) < 0) + return -1; + } + + + *out = reflog; + return 0; +} + +int git_transaction_set_reflog(git_transaction *tx, const char *refname, const git_reflog *reflog) +{ + int error; + transaction_node *node; + + assert(tx && refname && reflog); + + if ((error = find_locked(&node, tx, refname)) < 0) + return error; + + if ((error = dup_reflog(&node->reflog, reflog, &tx->pool)) < 0) + return error; + + return 0; +} + +static int update_target(git_refdb *db, transaction_node *node) +{ + git_reference *ref; + int error, update_reflog; + + if (node->ref_type == GIT_REF_OID) { + ref = git_reference__alloc(node->name, &node->target.id, NULL); + } else if (node->ref_type == GIT_REF_SYMBOLIC) { + ref = git_reference__alloc_symbolic(node->name, node->target.symbolic); + } else { + abort(); + } + + GITERR_CHECK_ALLOC(ref); + update_reflog = node->reflog == NULL; + + if (node->remove) { + error = git_refdb_unlock(db, node->payload, 2, false, ref, NULL, NULL); + } else if (node->ref_type == GIT_REF_OID) { + error = git_refdb_unlock(db, node->payload, true, update_reflog, ref, node->sig, node->message); + } else if (node->ref_type == GIT_REF_SYMBOLIC) { + error = git_refdb_unlock(db, node->payload, true, update_reflog, ref, node->sig, node->message); + } else { + abort(); + } + + git_reference_free(ref); + node->committed = true; + + return error; +} + +int git_transaction_commit(git_transaction *tx) +{ + transaction_node *node; + git_strmap_iter pos; + int error = 0; + + assert(tx); + + for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) { + if (!git_strmap_has_data(tx->locks, pos)) + continue; + + node = git_strmap_value_at(tx->locks, pos); + if (node->reflog) { + if ((error = tx->db->backend->reflog_write(tx->db->backend, node->reflog)) < 0) + return error; + } + + if (node->ref_type != GIT_REF_INVALID) { + if ((error = update_target(tx->db, node)) < 0) + return error; + } + } + + return 0; +} + +void git_transaction_free(git_transaction *tx) +{ + transaction_node *node; + git_pool pool; + git_strmap_iter pos; + + assert(tx); + + /* start by unlocking the ones we've left hanging, if any */ + for (pos = kh_begin(tx->locks); pos < kh_end(tx->locks); pos++) { + if (!git_strmap_has_data(tx->locks, pos)) + continue; + + node = git_strmap_value_at(tx->locks, pos); + if (node->committed) + continue; + + git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL); + } + + git_refdb_free(tx->db); + git_strmap_free(tx->locks); + + /* tx is inside the pool, so we need to extract the data */ + memcpy(&pool, &tx->pool, sizeof(git_pool)); + git_pool_clear(&pool); +} diff -Nru libgit2-0.20.0/src/transport.c libgit2-0.22.2/src/transport.c --- libgit2-0.20.0/src/transport.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/transport.c 2015-03-24 16:10:45.000000000 +0000 @@ -9,11 +9,11 @@ #include "git2/remote.h" #include "git2/net.h" #include "git2/transport.h" +#include "git2/sys/transport.h" #include "path.h" typedef struct transport_definition { char *prefix; - unsigned priority; git_transport_cb fn; void *param; } transport_definition; @@ -24,52 +24,55 @@ static git_smart_subtransport_definition ssh_subtransport_definition = { git_smart_subtransport_ssh, 0 }; #endif -static transport_definition local_transport_definition = { "file://", 1, git_transport_local, NULL }; -#ifdef GIT_SSH -static transport_definition ssh_transport_definition = { "ssh://", 1, git_transport_smart, &ssh_subtransport_definition }; -#else -static transport_definition dummy_transport_definition = { NULL, 1, git_transport_dummy, NULL }; -#endif +static transport_definition local_transport_definition = { "file://", git_transport_local, NULL }; static transport_definition transports[] = { - {"git://", 1, git_transport_smart, &git_subtransport_definition}, - {"http://", 1, git_transport_smart, &http_subtransport_definition}, - {"https://", 1, git_transport_smart, &http_subtransport_definition}, - {"file://", 1, git_transport_local, NULL}, + { "git://", git_transport_smart, &git_subtransport_definition }, + { "http://", git_transport_smart, &http_subtransport_definition }, +#if defined(GIT_SSL) || defined(GIT_WINHTTP) + { "https://", git_transport_smart, &http_subtransport_definition }, +#endif + { "file://", git_transport_local, NULL }, #ifdef GIT_SSH - {"ssh://", 1, git_transport_smart, &ssh_subtransport_definition}, + { "ssh://", git_transport_smart, &ssh_subtransport_definition }, #endif - {NULL, 0, 0} + { NULL, 0, 0 } }; -static git_vector additional_transports = GIT_VECTOR_INIT; +static git_vector custom_transports = GIT_VECTOR_INIT; #define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1 -static int transport_find_fn(const char *url, git_transport_cb *callback, void **param) +static transport_definition * transport_find_by_url(const char *url) { size_t i = 0; - unsigned priority = 0; - transport_definition *definition = NULL, *definition_iter; + transport_definition *d; - // First, check to see if it's an obvious URL, which a URL scheme - for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) { - definition_iter = &transports[i]; + /* Find a user transport who wants to deal with this URI */ + git_vector_foreach(&custom_transports, i, d) { + if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) { + return d; + } + } - if (strncasecmp(url, definition_iter->prefix, strlen(definition_iter->prefix))) - continue; + /* Find a system transport for this URI */ + for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) { + d = &transports[i]; - if (definition_iter->priority > priority) - definition = definition_iter; + if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) { + return d; + } } - git_vector_foreach(&additional_transports, i, definition_iter) { - if (strncasecmp(url, definition_iter->prefix, strlen(definition_iter->prefix))) - continue; + return NULL; +} - if (definition_iter->priority > priority) - definition = definition_iter; - } +static int transport_find_fn( + git_transport_cb *out, + const char *url, + void **param) +{ + transport_definition *definition = transport_find_by_url(url); #ifdef GIT_WIN32 /* On Windows, it might not be possible to discern between absolute local @@ -79,33 +82,28 @@ /* Check to see if the path points to a file on the local file system */ if (!definition && git_path_exists(url) && git_path_isdir(url)) definition = &local_transport_definition; +#endif - /* It could be a SSH remote path. Check to see if there's a : - * SSH is an unsupported transport mechanism in this version of libgit2 */ - if (!definition && strrchr(url, ':')) - definition = &dummy_transport_definition; -#else /* For other systems, perform the SSH check first, to avoid going to the * filesystem if it is not necessary */ /* It could be a SSH remote path. Check to see if there's a : * SSH is an unsupported transport mechanism in this version of libgit2 */ - if (!definition && strrchr(url, ':')) -#ifdef GIT_SSH - definition = &ssh_transport_definition; -#else - definition = &dummy_transport_definition; -#endif + if (!definition && strrchr(url, ':')) { + // re-search transports again with ssh:// as url so that we can find a third party ssh transport + definition = transport_find_by_url("ssh://"); + } +#ifndef GIT_WIN32 /* Check to see if the path points to a file on the local file system */ if (!definition && git_path_exists(url) && git_path_isdir(url)) definition = &local_transport_definition; #endif if (!definition) - return -1; + return GIT_ENOTFOUND; - *callback = definition->fn; + *out = definition->fn; *param = definition->param; return 0; @@ -115,15 +113,6 @@ * Public API * **************/ -int git_transport_dummy(git_transport **transport, git_remote *owner, void *param) -{ - GIT_UNUSED(transport); - GIT_UNUSED(owner); - GIT_UNUSED(param); - giterr_set(GITERR_NET, "This transport isn't implemented. Sorry"); - return -1; -} - int git_transport_new(git_transport **out, git_remote *owner, const char *url) { git_transport_cb fn; @@ -131,92 +120,101 @@ void *param; int error; - if (transport_find_fn(url, &fn, ¶m) < 0) { + if ((error = transport_find_fn(&fn, url, ¶m)) == GIT_ENOTFOUND) { giterr_set(GITERR_NET, "Unsupported URL protocol"); return -1; - } + } else if (error < 0) + return error; - error = fn(&transport, owner, param); - if (error < 0) + if ((error = fn(&transport, owner, param)) < 0) return error; + GITERR_CHECK_VERSION(transport, GIT_TRANSPORT_VERSION, "git_transport"); + *out = transport; return 0; } int git_transport_register( - const char *prefix, - unsigned priority, + const char *scheme, git_transport_cb cb, void *param) { - transport_definition *d; - - d = git__calloc(sizeof(transport_definition), 1); - GITERR_CHECK_ALLOC(d); + git_buf prefix = GIT_BUF_INIT; + transport_definition *d, *definition = NULL; + size_t i; + int error = 0; - d->prefix = git__strdup(prefix); + assert(scheme); + assert(cb); - if (!d->prefix) + if ((error = git_buf_printf(&prefix, "%s://", scheme)) < 0) goto on_error; - d->priority = priority; - d->fn = cb; - d->param = param; + git_vector_foreach(&custom_transports, i, d) { + if (strcasecmp(d->prefix, prefix.ptr) == 0) { + error = GIT_EEXISTS; + goto on_error; + } + } + + definition = git__calloc(1, sizeof(transport_definition)); + GITERR_CHECK_ALLOC(definition); + + definition->prefix = git_buf_detach(&prefix); + definition->fn = cb; + definition->param = param; - if (git_vector_insert(&additional_transports, d) < 0) + if (git_vector_insert(&custom_transports, definition) < 0) goto on_error; return 0; on_error: - git__free(d->prefix); - git__free(d); - return -1; + git_buf_free(&prefix); + git__free(definition); + return error; } -int git_transport_unregister( - const char *prefix, - unsigned priority) +int git_transport_unregister(const char *scheme) { + git_buf prefix = GIT_BUF_INIT; transport_definition *d; - unsigned i; + size_t i; + int error = 0; + + assert(scheme); + + if ((error = git_buf_printf(&prefix, "%s://", scheme)) < 0) + goto done; - git_vector_foreach(&additional_transports, i, d) { - if (d->priority == priority && !strcasecmp(d->prefix, prefix)) { - if (git_vector_remove(&additional_transports, i) < 0) - return -1; + git_vector_foreach(&custom_transports, i, d) { + if (strcasecmp(d->prefix, prefix.ptr) == 0) { + if ((error = git_vector_remove(&custom_transports, i)) < 0) + goto done; git__free(d->prefix); git__free(d); - if (!additional_transports.length) - git_vector_free(&additional_transports); + if (!custom_transports.length) + git_vector_free(&custom_transports); - return 0; + error = 0; + goto done; } } - return GIT_ENOTFOUND; -} - -/* from remote.h */ -int git_remote_valid_url(const char *url) -{ - git_transport_cb fn; - void *param; + error = GIT_ENOTFOUND; - return !transport_find_fn(url, &fn, ¶m); +done: + git_buf_free(&prefix); + return error; } -int git_remote_supported_url(const char* url) +int git_transport_init(git_transport *opts, unsigned int version) { - git_transport_cb fn; - void *param; - - if (transport_find_fn(url, &fn, ¶m) < 0) - return 0; - - return fn != &git_transport_dummy; + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_transport, GIT_TRANSPORT_INIT); + return 0; } diff -Nru libgit2-0.20.0/src/transports/auth.c libgit2-0.22.2/src/transports/auth.c --- libgit2-0.20.0/src/transports/auth.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/transports/auth.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,71 @@ +/* + * 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.h" +#include "buffer.h" +#include "auth.h" + +static int basic_next_token( + git_buf *out, git_http_auth_context *ctx, git_cred *c) +{ + git_cred_userpass_plaintext *cred; + git_buf raw = GIT_BUF_INIT; + int error = -1; + + GIT_UNUSED(ctx); + + if (c->credtype != GIT_CREDTYPE_USERPASS_PLAINTEXT) { + giterr_set(GITERR_INVALID, "invalid credential type for basic auth"); + goto on_error; + } + + cred = (git_cred_userpass_plaintext *)c; + + git_buf_printf(&raw, "%s:%s", cred->username, cred->password); + + if (git_buf_oom(&raw) || + git_buf_puts(out, "Authorization: Basic ") < 0 || + git_buf_encode_base64(out, git_buf_cstr(&raw), raw.size) < 0 || + git_buf_puts(out, "\r\n") < 0) + goto on_error; + + error = 0; + +on_error: + if (raw.size) + git__memzero(raw.ptr, raw.size); + + git_buf_free(&raw); + return error; +} + +static git_http_auth_context basic_context = { + GIT_AUTHTYPE_BASIC, + GIT_CREDTYPE_USERPASS_PLAINTEXT, + NULL, + basic_next_token, + NULL +}; + +int git_http_auth_basic( + git_http_auth_context **out, const gitno_connection_data *connection_data) +{ + GIT_UNUSED(connection_data); + + *out = &basic_context; + return 0; +} + +int git_http_auth_dummy( + git_http_auth_context **out, const gitno_connection_data *connection_data) +{ + GIT_UNUSED(connection_data); + + *out = NULL; + return 0; +} + diff -Nru libgit2-0.20.0/src/transports/auth.h libgit2-0.22.2/src/transports/auth.h --- libgit2-0.20.0/src/transports/auth.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/transports/auth.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,63 @@ +/* + * 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_http_auth_h__ +#define INCLUDE_http_auth_h__ + +#include "git2.h" +#include "netops.h" + +typedef enum { + GIT_AUTHTYPE_BASIC = 1, + GIT_AUTHTYPE_NEGOTIATE = 2, +} git_http_authtype_t; + +typedef struct git_http_auth_context git_http_auth_context; + +struct git_http_auth_context { + /** Type of scheme */ + git_http_authtype_t type; + + /** Supported credentials */ + git_credtype_t credtypes; + + /** Sets the challenge on the authentication context */ + int (*set_challenge)(git_http_auth_context *ctx, const char *challenge); + + /** Gets the next authentication token from the context */ + int (*next_token)(git_buf *out, git_http_auth_context *ctx, git_cred *cred); + + /** Frees the authentication context */ + void (*free)(git_http_auth_context *ctx); +}; + +typedef struct { + /** Type of scheme */ + git_http_authtype_t type; + + /** Name of the scheme (as used in the Authorization header) */ + const char *name; + + /** Credential types this scheme supports */ + git_credtype_t credtypes; + + /** Function to initialize an authentication context */ + int (*init_context)( + git_http_auth_context **out, + const gitno_connection_data *connection_data); +} git_http_auth_scheme; + +int git_http_auth_dummy( + git_http_auth_context **out, + const gitno_connection_data *connection_data); + +int git_http_auth_basic( + git_http_auth_context **out, + const gitno_connection_data *connection_data); + +#endif + diff -Nru libgit2-0.20.0/src/transports/auth_negotiate.c libgit2-0.22.2/src/transports/auth_negotiate.c --- libgit2-0.20.0/src/transports/auth_negotiate.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/transports/auth_negotiate.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,275 @@ +/* + * 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_GSSAPI + +#include "git2.h" +#include "common.h" +#include "buffer.h" +#include "auth.h" + +#include +#include + +static gss_OID_desc negotiate_oid_spnego = + { 6, (void *) "\x2b\x06\x01\x05\x05\x02" }; +static gss_OID_desc negotiate_oid_krb5 = + { 9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; + +static gss_OID negotiate_oids[] = + { &negotiate_oid_spnego, &negotiate_oid_krb5, NULL }; + +typedef struct { + git_http_auth_context parent; + unsigned configured : 1, + complete : 1; + git_buf target; + char *challenge; + gss_ctx_id_t gss_context; + gss_OID oid; +} http_auth_negotiate_context; + +static void negotiate_err_set( + OM_uint32 status_major, + OM_uint32 status_minor, + const char *message) +{ + gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER; + OM_uint32 status_display, context = 0; + + if (gss_display_status(&status_display, status_major, GSS_C_GSS_CODE, + GSS_C_NO_OID, &context, &buffer) == GSS_S_COMPLETE) { + giterr_set(GITERR_NET, "%s: %.*s (%d.%d)", + message, (int)buffer.length, (const char *)buffer.value, + status_major, status_minor); + gss_release_buffer(&status_minor, &buffer); + } else { + giterr_set(GITERR_NET, "%s: unknown negotiate error (%d.%d)", + message, status_major, status_minor); + } +} + +static int negotiate_set_challenge( + git_http_auth_context *c, + const char *challenge) +{ + http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c; + + assert(ctx && ctx->configured && challenge); + + git__free(ctx->challenge); + + ctx->challenge = git__strdup(challenge); + GITERR_CHECK_ALLOC(ctx->challenge); + + return 0; +} + +static int negotiate_next_token( + git_buf *buf, + git_http_auth_context *c, + git_cred *cred) +{ + http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c; + OM_uint32 status_major, status_minor; + gss_buffer_desc target_buffer = GSS_C_EMPTY_BUFFER, + input_token = GSS_C_EMPTY_BUFFER, + output_token = GSS_C_EMPTY_BUFFER; + gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER; + git_buf input_buf = GIT_BUF_INIT; + gss_name_t server = NULL; + gss_OID mech; + size_t challenge_len; + int error = 0; + + assert(buf && ctx && ctx->configured && cred && cred->credtype == GIT_CREDTYPE_DEFAULT); + + if (ctx->complete) + return 0; + + target_buffer.value = (void *)ctx->target.ptr; + target_buffer.length = ctx->target.size; + + status_major = gss_import_name(&status_minor, &target_buffer, + GSS_C_NT_HOSTBASED_SERVICE, &server); + + if (GSS_ERROR(status_major)) { + negotiate_err_set(status_major, status_minor, + "Could not parse principal"); + error = -1; + goto done; + } + + challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0; + + if (challenge_len < 9) { + giterr_set(GITERR_NET, "No negotiate challenge sent from server"); + error = -1; + goto done; + } else if (challenge_len > 9) { + if (git_buf_decode_base64(&input_buf, + ctx->challenge + 10, challenge_len - 10) < 0) { + giterr_set(GITERR_NET, "Invalid negotiate challenge from server"); + error = -1; + goto done; + } + + input_token.value = input_buf.ptr; + input_token.length = input_buf.size; + input_token_ptr = &input_token; + } else if (ctx->gss_context != GSS_C_NO_CONTEXT) { + giterr_set(GITERR_NET, "Could not restart authentication"); + error = -1; + goto done; + } + + mech = &negotiate_oid_spnego; + + if (GSS_ERROR(status_major = gss_init_sec_context( + &status_minor, + GSS_C_NO_CREDENTIAL, + &ctx->gss_context, + server, + mech, + GSS_C_DELEG_FLAG | GSS_C_MUTUAL_FLAG, + GSS_C_INDEFINITE, + GSS_C_NO_CHANNEL_BINDINGS, + input_token_ptr, + NULL, + &output_token, + NULL, + NULL))) { + negotiate_err_set(status_major, status_minor, "Negotiate failure"); + error = -1; + goto done; + } + + /* This message merely told us auth was complete; we do not respond. */ + if (status_major == GSS_S_COMPLETE) { + ctx->complete = 1; + goto done; + } + + git_buf_puts(buf, "Authorization: Negotiate "); + git_buf_encode_base64(buf, output_token.value, output_token.length); + git_buf_puts(buf, "\r\n"); + + if (git_buf_oom(buf)) + error = -1; + +done: + gss_release_name(&status_minor, &server); + gss_release_buffer(&status_minor, (gss_buffer_t) &output_token); + git_buf_free(&input_buf); + return error; +} + +static void negotiate_context_free(git_http_auth_context *c) +{ + http_auth_negotiate_context *ctx = (http_auth_negotiate_context *)c; + OM_uint32 status_minor; + + if (ctx->gss_context != GSS_C_NO_CONTEXT) { + gss_delete_sec_context( + &status_minor, &ctx->gss_context, GSS_C_NO_BUFFER); + ctx->gss_context = GSS_C_NO_CONTEXT; + } + + git_buf_free(&ctx->target); + + git__free(ctx->challenge); + + ctx->configured = 0; + ctx->complete = 0; + ctx->oid = NULL; + + git__free(ctx); +} + +static int negotiate_init_context( + http_auth_negotiate_context *ctx, + const gitno_connection_data *connection_data) +{ + OM_uint32 status_major, status_minor; + gss_OID item, *oid; + gss_OID_set mechanism_list; + size_t i; + + /* Query supported mechanisms looking for SPNEGO) */ + if (GSS_ERROR(status_major = + gss_indicate_mechs(&status_minor, &mechanism_list))) { + negotiate_err_set(status_major, status_minor, + "could not query mechanisms"); + return -1; + } + + if (mechanism_list) { + for (oid = negotiate_oids; *oid; oid++) { + for (i = 0; i < mechanism_list->count; i++) { + item = &mechanism_list->elements[i]; + + if (item->length == (*oid)->length && + memcmp(item->elements, (*oid)->elements, item->length) == 0) { + ctx->oid = *oid; + break; + } + + } + + if (ctx->oid) + break; + } + } + + gss_release_oid_set(&status_minor, &mechanism_list); + + if (!ctx->oid) { + giterr_set(GITERR_NET, "Negotiate authentication is not supported"); + return -1; + } + + git_buf_puts(&ctx->target, "HTTP@"); + git_buf_puts(&ctx->target, connection_data->host); + + if (git_buf_oom(&ctx->target)) + return -1; + + ctx->gss_context = GSS_C_NO_CONTEXT; + ctx->configured = 1; + + return 0; +} + +int git_http_auth_negotiate( + git_http_auth_context **out, + const gitno_connection_data *connection_data) +{ + http_auth_negotiate_context *ctx; + + *out = NULL; + + ctx = git__calloc(1, sizeof(http_auth_negotiate_context)); + GITERR_CHECK_ALLOC(ctx); + + if (negotiate_init_context(ctx, connection_data) < 0) { + git__free(ctx); + return -1; + } + + ctx->parent.type = GIT_AUTHTYPE_NEGOTIATE; + ctx->parent.credtypes = GIT_CREDTYPE_DEFAULT; + ctx->parent.set_challenge = negotiate_set_challenge; + ctx->parent.next_token = negotiate_next_token; + ctx->parent.free = negotiate_context_free; + + *out = (git_http_auth_context *)ctx; + + return 0; +} + +#endif /* GIT_GSSAPI */ + diff -Nru libgit2-0.20.0/src/transports/auth_negotiate.h libgit2-0.22.2/src/transports/auth_negotiate.h --- libgit2-0.20.0/src/transports/auth_negotiate.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/transports/auth_negotiate.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,27 @@ +/* + * 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_auth_negotiate_h__ +#define INCLUDE_auth_negotiate_h__ + +#include "git2.h" +#include "auth.h" + +#ifdef GIT_GSSAPI + +extern int git_http_auth_negotiate( + git_http_auth_context **out, + const gitno_connection_data *connection_data); + +#else + +#define git_http_auth_negotiate git_http_auth_dummy + +#endif /* GIT_GSSAPI */ + +#endif + diff -Nru libgit2-0.20.0/src/transports/cred.c libgit2-0.22.2/src/transports/cred.c --- libgit2-0.20.0/src/transports/cred.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/transports/cred.c 2015-03-24 16:10:45.000000000 +0000 @@ -11,31 +11,44 @@ int git_cred_has_username(git_cred *cred) { - int ret = 0; + if (cred->credtype == GIT_CREDTYPE_DEFAULT) + return 0; + return 1; +} + +const char *git_cred__username(git_cred *cred) +{ switch (cred->credtype) { - case GIT_CREDTYPE_USERPASS_PLAINTEXT: { - git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred; - ret = !!c->username; - break; - } - case GIT_CREDTYPE_SSH_KEY: { - git_cred_ssh_key *c = (git_cred_ssh_key *)cred; - ret = !!c->username; - break; - } - case GIT_CREDTYPE_SSH_CUSTOM: { - git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred; - ret = !!c->username; - break; - } - case GIT_CREDTYPE_DEFAULT: { - ret = 0; - break; - } + case GIT_CREDTYPE_USERNAME: + { + git_cred_username *c = (git_cred_username *) cred; + return c->username; + } + case GIT_CREDTYPE_USERPASS_PLAINTEXT: + { + git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *) cred; + return c->username; + } + case GIT_CREDTYPE_SSH_KEY: + { + git_cred_ssh_key *c = (git_cred_ssh_key *) cred; + return c->username; + } + case GIT_CREDTYPE_SSH_CUSTOM: + { + git_cred_ssh_custom *c = (git_cred_ssh_custom *) cred; + return c->username; + } + case GIT_CREDTYPE_SSH_INTERACTIVE: + { + git_cred_ssh_interactive *c = (git_cred_ssh_interactive *) cred; + return c->username; } - return ret; + default: + return NULL; + } } static void plaintext_free(struct git_cred *cred) @@ -51,7 +64,6 @@ git__free(c->password); } - git__memzero(c, sizeof(*c)); git__free(c); } @@ -94,8 +106,13 @@ (git_cred_ssh_key *)cred; git__free(c->username); - git__free(c->publickey); - git__free(c->privatekey); + + if (c->privatekey) { + /* Zero the memory which previously held the private key */ + size_t key_len = strlen(c->privatekey); + git__memzero(c->privatekey, key_len); + git__free(c->privatekey); + } if (c->passphrase) { /* Zero the memory which previously held the passphrase */ @@ -104,7 +121,22 @@ git__free(c->passphrase); } - git__memzero(c, sizeof(*c)); + if (c->publickey) { + /* Zero the memory which previously held the public key */ + size_t key_len = strlen(c->publickey); + git__memzero(c->publickey, key_len); + git__free(c->publickey); + } + + git__free(c); +} + +static void ssh_interactive_free(struct git_cred *cred) +{ + git_cred_ssh_interactive *c = (git_cred_ssh_interactive *)cred; + + git__free(c->username); + git__free(c); } @@ -113,9 +145,14 @@ git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred; git__free(c->username); - git__free(c->publickey); - git__memzero(c, sizeof(*c)); + if (c->publickey) { + /* Zero the memory which previously held the publickey */ + size_t key_len = strlen(c->publickey); + git__memzero(c->publickey, key_len); + git__free(c->publickey); + } + git__free(c); } @@ -126,6 +163,11 @@ git__free(c); } +static void username_free(struct git_cred *cred) +{ + git__free(cred); +} + int git_cred_ssh_key_new( git_cred **cred, const char *username, @@ -135,7 +177,7 @@ { git_cred_ssh_key *c; - assert(cred && privatekey); + assert(username && cred && privatekey); c = git__calloc(1, sizeof(git_cred_ssh_key)); GITERR_CHECK_ALLOC(c); @@ -143,10 +185,8 @@ c->parent.credtype = GIT_CREDTYPE_SSH_KEY; c->parent.free = ssh_key_free; - if (username) { - c->username = git__strdup(username); - GITERR_CHECK_ALLOC(c->username); - } + c->username = git__strdup(username); + GITERR_CHECK_ALLOC(c->username); c->privatekey = git__strdup(privatekey); GITERR_CHECK_ALLOC(c->privatekey); @@ -165,17 +205,63 @@ return 0; } +int git_cred_ssh_interactive_new( + git_cred **out, + const char *username, + git_cred_ssh_interactive_callback prompt_callback, + void *payload) +{ + git_cred_ssh_interactive *c; + + assert(out && username && prompt_callback); + + c = git__calloc(1, sizeof(git_cred_ssh_interactive)); + GITERR_CHECK_ALLOC(c); + + c->parent.credtype = GIT_CREDTYPE_SSH_INTERACTIVE; + c->parent.free = ssh_interactive_free; + + c->username = git__strdup(username); + GITERR_CHECK_ALLOC(c->username); + + c->prompt_callback = prompt_callback; + c->payload = payload; + + *out = &c->parent; + return 0; +} + +int git_cred_ssh_key_from_agent(git_cred **cred, const char *username) { + git_cred_ssh_key *c; + + assert(username && cred); + + c = git__calloc(1, sizeof(git_cred_ssh_key)); + GITERR_CHECK_ALLOC(c); + + c->parent.credtype = GIT_CREDTYPE_SSH_KEY; + c->parent.free = ssh_key_free; + + c->username = git__strdup(username); + GITERR_CHECK_ALLOC(c->username); + + c->privatekey = NULL; + + *cred = &c->parent; + return 0; +} + int git_cred_ssh_custom_new( git_cred **cred, const char *username, const char *publickey, size_t publickey_len, git_cred_sign_callback sign_callback, - void *sign_data) + void *payload) { git_cred_ssh_custom *c; - assert(cred); + assert(username && cred); c = git__calloc(1, sizeof(git_cred_ssh_custom)); GITERR_CHECK_ALLOC(c); @@ -183,10 +269,8 @@ c->parent.credtype = GIT_CREDTYPE_SSH_CUSTOM; c->parent.free = ssh_custom_free; - if (username) { - c->username = git__strdup(username); - GITERR_CHECK_ALLOC(c->username); - } + c->username = git__strdup(username); + GITERR_CHECK_ALLOC(c->username); if (publickey_len > 0) { c->publickey = git__malloc(publickey_len); @@ -197,7 +281,7 @@ c->publickey_len = publickey_len; c->sign_callback = sign_callback; - c->sign_data = sign_data; + c->payload = payload; *cred = &c->parent; return 0; @@ -218,3 +302,22 @@ *cred = c; return 0; } + +int git_cred_username_new(git_cred **cred, const char *username) +{ + git_cred_username *c; + size_t len; + + assert(cred); + + len = strlen(username); + c = git__malloc(sizeof(git_cred_username) + len + 1); + GITERR_CHECK_ALLOC(c); + + c->parent.credtype = GIT_CREDTYPE_USERNAME; + c->parent.free = username_free; + memcpy(c->username, username, len + 1); + + *cred = (git_cred *) c; + return 0; +} diff -Nru libgit2-0.20.0/src/transports/cred.h libgit2-0.22.2/src/transports/cred.h --- libgit2-0.20.0/src/transports/cred.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/transports/cred.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,14 @@ +/* + * 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_git_cred_h__ +#define INCLUDE_git_cred_h__ + +#include "git2/transport.h" + +const char *git_cred__username(git_cred *cred); + +#endif diff -Nru libgit2-0.20.0/src/transports/cred_helpers.c libgit2-0.22.2/src/transports/cred_helpers.c --- libgit2-0.20.0/src/transports/cred_helpers.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/transports/cred_helpers.c 2015-03-24 16:10:45.000000000 +0000 @@ -41,6 +41,9 @@ else return -1; + if (GIT_CREDTYPE_USERNAME & allowed_types) + return git_cred_username_new(cred, effective_username); + if ((GIT_CREDTYPE_USERPASS_PLAINTEXT & allowed_types) == 0 || git_cred_userpass_plaintext_new(cred, effective_username, userpass->password) < 0) return -1; diff -Nru libgit2-0.20.0/src/transports/git.c libgit2-0.22.2/src/transports/git.c --- libgit2-0.20.0/src/transports/git.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/transports/git.c 2015-03-24 16:10:45.000000000 +0000 @@ -8,6 +8,9 @@ #include "git2.h" #include "buffer.h" #include "netops.h" +#include "git2/sys/transport.h" +#include "stream.h" +#include "socket_stream.h" #define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport) @@ -17,16 +20,16 @@ typedef struct { git_smart_subtransport_stream parent; - gitno_socket socket; + git_stream *io; const char *cmd; char *url; unsigned sent_command : 1; -} git_stream; +} git_proto_stream; typedef struct { git_smart_subtransport parent; git_transport *owner; - git_stream *current_stream; + git_proto_stream *current_stream; } git_subtransport; /* @@ -66,7 +69,7 @@ return 0; } -static int send_command(git_stream *s) +static int send_command(git_proto_stream *s) { int error; git_buf request = GIT_BUF_INIT; @@ -75,10 +78,7 @@ if (error < 0) goto cleanup; - /* It looks like negative values are errors here, and positive values - * are the number of bytes sent. */ - error = gitno_send(&s->socket, request.ptr, request.size, 0); - + error = git_stream_write(s->io, request.ptr, request.size, 0); if (error >= 0) s->sent_command = 1; @@ -87,46 +87,48 @@ return error; } -static int git_stream_read( +static int git_proto_stream_read( git_smart_subtransport_stream *stream, char *buffer, size_t buf_size, size_t *bytes_read) { - git_stream *s = (git_stream *)stream; + int error; + git_proto_stream *s = (git_proto_stream *)stream; gitno_buffer buf; *bytes_read = 0; - if (!s->sent_command && send_command(s) < 0) - return -1; + if (!s->sent_command && (error = send_command(s)) < 0) + return error; - gitno_buffer_setup(&s->socket, &buf, buffer, buf_size); + gitno_buffer_setup_fromstream(s->io, &buf, buffer, buf_size); - if (gitno_recv(&buf) < 0) - return -1; + if ((error = gitno_recv(&buf)) < 0) + return error; *bytes_read = buf.offset; return 0; } -static int git_stream_write( +static int git_proto_stream_write( git_smart_subtransport_stream *stream, const char *buffer, size_t len) { - git_stream *s = (git_stream *)stream; + int error; + git_proto_stream *s = (git_proto_stream *)stream; - if (!s->sent_command && send_command(s) < 0) - return -1; + if (!s->sent_command && (error = send_command(s)) < 0) + return error; - return gitno_send(&s->socket, buffer, len, 0); + return git_stream_write(s->io, buffer, len, 0); } -static void git_stream_free(git_smart_subtransport_stream *stream) +static void git_proto_stream_free(git_smart_subtransport_stream *stream) { - git_stream *s = (git_stream *)stream; + git_proto_stream *s = (git_proto_stream *)stream; git_subtransport *t = OWNING_SUBTRANSPORT(s); int ret; @@ -134,33 +136,32 @@ t->current_stream = NULL; - if (s->socket.socket) { - ret = gitno_close(&s->socket); - assert(!ret); - } - + git_stream_close(s->io); + git_stream_free(s->io); git__free(s->url); - git__free(s); + git__free(s); } -static int git_stream_alloc( +static int git_proto_stream_alloc( git_subtransport *t, const char *url, const char *cmd, + const char *host, + const char *port, git_smart_subtransport_stream **stream) { - git_stream *s; + git_proto_stream *s; if (!stream) return -1; - s = git__calloc(sizeof(git_stream), 1); + s = git__calloc(sizeof(git_proto_stream), 1); GITERR_CHECK_ALLOC(s); s->parent.subtransport = &t->parent; - s->parent.read = git_stream_read; - s->parent.write = git_stream_write; - s->parent.free = git_stream_free; + s->parent.read = git_proto_stream_read; + s->parent.write = git_proto_stream_write; + s->parent.free = git_proto_stream_free; s->cmd = cmd; s->url = git__strdup(url); @@ -170,6 +171,11 @@ return -1; } + if ((git_socket_stream_new(&s->io, host, port)) < 0) + return -1; + + GITERR_CHECK_VERSION(s->io, GIT_STREAM_VERSION, "git_stream"); + *stream = &s->parent; return 0; } @@ -181,31 +187,40 @@ { char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; const char *stream_url = url; - git_stream *s; - int error = -1; + git_proto_stream *s; + int error; *stream = NULL; + if (!git__prefixcmp(url, prefix_git)) stream_url += strlen(prefix_git); - if (git_stream_alloc(t, stream_url, cmd_uploadpack, stream) < 0) - return -1; + if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT)) < 0) + return error; - s = (git_stream *)*stream; + error = git_proto_stream_alloc(t, stream_url, cmd_uploadpack, host, port, stream); - if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) { - if (!(error = gitno_connect(&s->socket, host, port, 0))) - t->current_stream = s; - - git__free(host); - git__free(port); - git__free(path); - git__free(user); - git__free(pass); - } else if (*stream) - git_stream_free(*stream); + git__free(host); + git__free(port); + git__free(path); + git__free(user); + git__free(pass); - return error; + + if (error < 0) { + git_proto_stream_free(*stream); + return error; + } + + s = (git_proto_stream *) *stream; + if ((error = git_stream_connect(s->io)) < 0) { + git_proto_stream_free(*stream); + return error; + } + + t->current_stream = s; + + return 0; } static int _git_uploadpack( @@ -231,31 +246,37 @@ { char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; const char *stream_url = url; - git_stream *s; + git_proto_stream *s; int error; *stream = NULL; if (!git__prefixcmp(url, prefix_git)) stream_url += strlen(prefix_git); - if (git_stream_alloc(t, stream_url, cmd_receivepack, stream) < 0) - return -1; + if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT)) < 0) + return error; + + error = git_proto_stream_alloc(t, stream_url, cmd_receivepack, host, port, stream); - s = (git_stream *)*stream; + git__free(host); + git__free(port); + git__free(path); + git__free(user); + git__free(pass); + + if (error < 0) { + git_proto_stream_free(*stream); + return error; + } - if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) { - if (!(error = gitno_connect(&s->socket, host, port, 0))) - t->current_stream = s; - - git__free(host); - git__free(port); - git__free(path); - git__free(user); - git__free(pass); - } else if (*stream) - git_stream_free(*stream); + s = (git_proto_stream *) *stream; - return error; + if ((error = git_stream_connect(s->io)) < 0) + return error; + + t->current_stream = s; + + return 0; } static int _git_receivepack( diff -Nru libgit2-0.20.0/src/transports/http.c libgit2-0.22.2/src/transports/http.c --- libgit2-0.20.0/src/transports/http.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/transports/http.c 2015-03-24 16:10:45.000000000 +0000 @@ -11,6 +11,15 @@ #include "buffer.h" #include "netops.h" #include "smart.h" +#include "auth.h" +#include "auth_negotiate.h" +#include "openssl_stream.h" +#include "socket_stream.h" + +git_http_auth_scheme auth_schemes[] = { + { GIT_AUTHTYPE_NEGOTIATE, "Negotiate", GIT_CREDTYPE_DEFAULT, git_http_auth_negotiate }, + { GIT_AUTHTYPE_BASIC, "Basic", GIT_CREDTYPE_USERPASS_PLAINTEXT, git_http_auth_basic }, +}; static const char *upload_pack_service = "upload-pack"; static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack"; @@ -20,7 +29,6 @@ static const char *receive_pack_service_url = "/git-receive-pack"; static const char *get_verb = "GET"; static const char *post_verb = "POST"; -static const char *basic_authtype = "Basic"; #define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport) @@ -35,10 +43,6 @@ VALUE }; -typedef enum { - GIT_HTTP_AUTH_BASIC = 1, -} http_authmechanism_t; - typedef struct { git_smart_subtransport_stream parent; const char *service; @@ -56,11 +60,8 @@ typedef struct { git_smart_subtransport parent; transport_smart *owner; - gitno_socket socket; + git_stream *io; gitno_connection_data connection_data; - git_cred *cred; - git_cred *url_cred; - http_authmechanism_t auth_mechanism; bool connected; /* Parser structures */ @@ -76,6 +77,11 @@ enum last_cb last_cb; int parse_error; unsigned parse_finished : 1; + + /* Authentication */ + git_cred *cred; + git_cred *url_cred; + git_vector auth_contexts; } http_subtransport; typedef struct { @@ -88,28 +94,91 @@ size_t *bytes_read; } parser_context; -static int apply_basic_credential(git_buf *buf, git_cred *cred) +static bool credtype_match(git_http_auth_scheme *scheme, void *data) { - git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred; - git_buf raw = GIT_BUF_INIT; - int error = -1; - - git_buf_printf(&raw, "%s:%s", c->username, c->password); - - if (git_buf_oom(&raw) || - git_buf_puts(buf, "Authorization: Basic ") < 0 || - git_buf_put_base64(buf, git_buf_cstr(&raw), raw.size) < 0 || - git_buf_puts(buf, "\r\n") < 0) - goto on_error; + unsigned int credtype = *(unsigned int *)data; - error = 0; + return !!(scheme->credtypes & credtype); +} -on_error: - if (raw.size) - memset(raw.ptr, 0x0, raw.size); +static bool challenge_match(git_http_auth_scheme *scheme, void *data) +{ + const char *scheme_name = scheme->name; + const char *challenge = (const char *)data; + size_t scheme_len; + + scheme_len = strlen(scheme_name); + return (strncmp(challenge, scheme_name, scheme_len) == 0 && + (challenge[scheme_len] == '\0' || challenge[scheme_len] == ' ')); +} + +static int auth_context_match( + git_http_auth_context **out, + http_subtransport *t, + bool (*scheme_match)(git_http_auth_scheme *scheme, void *data), + void *data) +{ + git_http_auth_scheme *scheme = NULL; + git_http_auth_context *context = NULL, *c; + size_t i; + + *out = NULL; + + for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) { + if (scheme_match(&auth_schemes[i], data)) { + scheme = &auth_schemes[i]; + break; + } + } + + if (!scheme) + return 0; - git_buf_free(&raw); - return error; + /* See if authentication has already started for this scheme */ + git_vector_foreach(&t->auth_contexts, i, c) { + if (c->type == scheme->type) { + context = c; + break; + } + } + + if (!context) { + if (scheme->init_context(&context, &t->connection_data) < 0) + return -1; + else if (!context) + return 0; + else if (git_vector_insert(&t->auth_contexts, context) < 0) + return -1; + } + + *out = context; + + return 0; +} + +static int apply_credentials(git_buf *buf, http_subtransport *t) +{ + git_cred *cred = t->cred; + git_http_auth_context *context; + + /* Apply the credentials given to us in the URL */ + if (!cred && t->connection_data.user && t->connection_data.pass) { + if (!t->url_cred && + git_cred_userpass_plaintext_new(&t->url_cred, + t->connection_data.user, t->connection_data.pass) < 0) + return -1; + + cred = t->url_cred; + } + + if (!cred) + return 0; + + /* Get or create a context for the best scheme for this cred type */ + if (auth_context_match(&context, t, credtype_match, &cred->credtype) < 0) + return -1; + + return context->next_token(buf, context, cred); } static int gen_request( @@ -137,19 +206,9 @@ git_buf_puts(buf, "Accept: */*\r\n"); /* Apply credentials to the request */ - if (t->cred && t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT && - t->auth_mechanism == GIT_HTTP_AUTH_BASIC && - apply_basic_credential(buf, t->cred) < 0) + if (apply_credentials(buf, t) < 0) return -1; - /* Use url-parsed basic auth if username and password are both provided */ - if (!t->cred && t->connection_data.user && t->connection_data.pass) { - if (!t->url_cred && git_cred_userpass_plaintext_new(&t->url_cred, - t->connection_data.user, t->connection_data.pass) < 0) - return -1; - if (apply_basic_credential(buf, t->url_cred) < 0) return -1; - } - git_buf_puts(buf, "\r\n"); if (git_buf_oom(buf)) @@ -158,20 +217,26 @@ return 0; } -static int parse_unauthorized_response( +static int parse_authenticate_response( git_vector *www_authenticate, - int *allowed_types, - http_authmechanism_t *auth_mechanism) + http_subtransport *t, + int *allowed_types) { - unsigned i; - char *entry; + git_http_auth_context *context; + char *challenge; + size_t i; - git_vector_foreach(www_authenticate, i, entry) { - if (!strncmp(entry, basic_authtype, 5) && - (entry[5] == '\0' || entry[5] == ' ')) { - *allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT; - *auth_mechanism = GIT_HTTP_AUTH_BASIC; - } + git_vector_foreach(www_authenticate, i, challenge) { + if (auth_context_match(&context, t, challenge_match, challenge) < 0) + return -1; + else if (!context) + continue; + + if (context->set_challenge && + context->set_challenge(context, challenge) < 0) + return -1; + + *allowed_types |= context->credtypes; } return 0; @@ -248,6 +313,7 @@ http_subtransport *t = ctx->t; http_stream *s = ctx->s; git_buf buf = GIT_BUF_INIT; + int error = 0, no_callback = 0, allowed_auth_types = 0; /* Both parse_header_name and parse_header_value are populated * and ready for consumption. */ @@ -255,30 +321,50 @@ if (on_header_ready(t) < 0) return t->parse_error = PARSE_ERROR_GENERIC; + /* Capture authentication headers which may be a 401 (authentication + * is not complete) or a 200 (simply informing us that auth *is* + * complete.) + */ + if (parse_authenticate_response(&t->www_authenticate, t, + &allowed_auth_types) < 0) + return t->parse_error = PARSE_ERROR_GENERIC; + /* Check for an authentication failure. */ - if (parser->status_code == 401 && - get_verb == s->verb && - t->owner->cred_acquire_cb) { - int allowed_types = 0; + if (parser->status_code == 401 && get_verb == s->verb) { + if (!t->owner->cred_acquire_cb) { + no_callback = 1; + } else { + if (allowed_auth_types && + (!t->cred || 0 == (t->cred->credtype & allowed_auth_types))) { + + error = t->owner->cred_acquire_cb(&t->cred, + t->owner->url, + t->connection_data.user, + allowed_auth_types, + t->owner->cred_acquire_payload); + + if (error == GIT_PASSTHROUGH) { + no_callback = 1; + } else if (error < 0) { + return PARSE_ERROR_GENERIC; + } else { + assert(t->cred); + + if (!(t->cred->credtype & allowed_auth_types)) { + giterr_set(GITERR_NET, "credentials callback returned an invalid cred type"); + return t->parse_error = PARSE_ERROR_GENERIC; + } + + /* Successfully acquired a credential. */ + t->parse_error = PARSE_ERROR_REPLAY; + return 0; + } + } + } - if (parse_unauthorized_response(&t->www_authenticate, - &allowed_types, &t->auth_mechanism) < 0) + if (no_callback) { + giterr_set(GITERR_NET, "authentication required but no callback set"); return t->parse_error = PARSE_ERROR_GENERIC; - - if (allowed_types && - (!t->cred || 0 == (t->cred->credtype & allowed_types))) { - - if (t->owner->cred_acquire_cb(&t->cred, - t->owner->url, - t->connection_data.user, - allowed_types, - t->owner->cred_acquire_payload) < 0) - return PARSE_ERROR_GENERIC; - - assert(t->cred); - - /* Successfully acquired a credential. */ - return t->parse_error = PARSE_ERROR_REPLAY; } } @@ -309,7 +395,8 @@ t->connected = 0; s->redirect_count++; - return t->parse_error = PARSE_ERROR_REPLAY; + t->parse_error = PARSE_ERROR_REPLAY; + return 0; } /* Check for a 200 HTTP status code. */ @@ -367,6 +454,13 @@ parser_context *ctx = (parser_context *) parser->data; http_subtransport *t = ctx->t; + /* If our goal is to replay the request (either an auth failure or + * a redirect) then don't bother buffering since we're ignoring the + * content anyway. + */ + if (t->parse_error == PARSE_ERROR_REPLAY) + return 0; + if (ctx->buf_size < len) { giterr_set(GITERR_NET, "Can't fit data in the buffer"); return t->parse_error = PARSE_ERROR_GENERIC; @@ -382,11 +476,8 @@ static void clear_parser_state(http_subtransport *t) { - unsigned i; - char *entry; - http_parser_init(&t->parser, HTTP_RESPONSE); - gitno_buffer_setup(&t->socket, + gitno_buffer_setup_fromstream(t->io, &t->parse_buffer, t->parse_buffer_data, sizeof(t->parse_buffer_data)); @@ -407,13 +498,10 @@ git__free(t->location); t->location = NULL; - git_vector_foreach(&t->www_authenticate, i, entry) - git__free(entry); - - git_vector_free(&t->www_authenticate); + git_vector_free_deep(&t->www_authenticate); } -static int write_chunk(gitno_socket *socket, const char *buffer, size_t len) +static int write_chunk(git_stream *io, const char *buffer, size_t len) { git_buf buf = GIT_BUF_INIT; @@ -423,7 +511,7 @@ if (git_buf_oom(&buf)) return -1; - if (gitno_send(socket, buf.ptr, buf.size, 0) < 0) { + if (git_stream_write(io, buf.ptr, buf.size, 0) < 0) { git_buf_free(&buf); return -1; } @@ -431,11 +519,11 @@ git_buf_free(&buf); /* Chunk body */ - if (len > 0 && gitno_send(socket, buffer, len, 0) < 0) + if (len > 0 && git_stream_write(io, buffer, len, 0) < 0) return -1; /* Chunk footer */ - if (gitno_send(socket, "\r\n", 2, 0) < 0) + if (git_stream_write(io, "\r\n", 2, 0) < 0) return -1; return 0; @@ -443,30 +531,55 @@ static int http_connect(http_subtransport *t) { - int flags = 0; + int error; if (t->connected && http_should_keep_alive(&t->parser) && - http_body_is_final(&t->parser)) + t->parse_finished) return 0; - if (t->socket.socket) - gitno_close(&t->socket); + if (t->io) { + git_stream_close(t->io); + git_stream_free(t->io); + t->io = NULL; + } if (t->connection_data.use_ssl) { - int tflags; + error = git_openssl_stream_new(&t->io, t->connection_data.host, t->connection_data.port); + } else { + error = git_socket_stream_new(&t->io, t->connection_data.host, t->connection_data.port); + } - if (t->owner->parent.read_flags(&t->owner->parent, &tflags) < 0) - return -1; + if (error < 0) + return error; - flags |= GITNO_CONNECT_SSL; + GITERR_CHECK_VERSION(t->io, GIT_STREAM_VERSION, "git_stream"); - if (GIT_TRANSPORTFLAGS_NO_CHECK_CERT & tflags) - flags |= GITNO_CONNECT_SSL_NO_CHECK_CERT; - } + error = git_stream_connect(t->io); - if (gitno_connect(&t->socket, t->connection_data.host, t->connection_data.port, flags) < 0) - return -1; +#ifdef GIT_SSL + if ((!error || error == GIT_ECERTIFICATE) && t->owner->certificate_check_cb != NULL && + git_stream_is_encrypted(t->io)) { + git_cert *cert; + int is_valid; + + if ((error = git_stream_certificate(&cert, t->io)) < 0) + return error; + + giterr_clear(); + is_valid = error != GIT_ECERTIFICATE; + error = t->owner->certificate_check_cb(cert, is_valid, t->connection_data.host, t->owner->message_cb_payload); + + if (error < 0) { + if (!giterr_last()) + giterr_set(GITERR_NET, "user cancelled certificate check"); + + return error; + } + } +#endif + if (error < 0) + return error; t->connected = 1; return 0; @@ -493,12 +606,10 @@ clear_parser_state(t); - if (gen_request(&request, s, 0) < 0) { - giterr_set(GITERR_NET, "Failed to generate request"); + if (gen_request(&request, s, 0) < 0) return -1; - } - if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) { + if (git_stream_write(t->io, request.ptr, request.size, 0) < 0) { git_buf_free(&request); return -1; } @@ -514,13 +625,13 @@ /* Flush, if necessary */ if (s->chunk_buffer_len > 0 && - write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0) + write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0) return -1; s->chunk_buffer_len = 0; /* Write the final chunk. */ - if (gitno_send(&t->socket, "0\r\n\r\n", 5, 0) < 0) + if (git_stream_write(t->io, "0\r\n\r\n", 5, 0) < 0) return -1; } @@ -528,7 +639,24 @@ } while (!*bytes_read && !t->parse_finished) { - t->parse_buffer.offset = 0; + size_t data_offset; + int error; + + /* + * Make the parse_buffer think it's as full of data as + * the buffer, so it won't try to recv more data than + * we can put into it. + * + * data_offset is the actual data offset from which we + * should tell the parser to start reading. + */ + if (buf_size >= t->parse_buffer.len) { + t->parse_buffer.offset = 0; + } else { + t->parse_buffer.offset = t->parse_buffer.len - buf_size; + } + + data_offset = t->parse_buffer.offset; if (gitno_recv(&t->parse_buffer) < 0) return -1; @@ -549,8 +677,8 @@ bytes_parsed = http_parser_execute(&t->parser, &t->settings, - t->parse_buffer.data, - t->parse_buffer.offset); + t->parse_buffer.data + data_offset, + t->parse_buffer.offset - data_offset); t->parser.data = NULL; @@ -559,8 +687,8 @@ if (PARSE_ERROR_REPLAY == t->parse_error) { s->sent_request = 0; - if (http_connect(t) < 0) - return -1; + if ((error = http_connect(t)) < 0) + return error; goto replay; } @@ -568,7 +696,7 @@ if (t->parse_error < 0) return -1; - if (bytes_parsed != t->parse_buffer.offset) { + if (bytes_parsed != t->parse_buffer.offset - data_offset) { giterr_set(GITERR_NET, "HTTP parser error: %s", http_errno_description((enum http_errno)t->parser.http_errno)); @@ -595,12 +723,10 @@ clear_parser_state(t); - if (gen_request(&request, s, 0) < 0) { - giterr_set(GITERR_NET, "Failed to generate request"); + if (gen_request(&request, s, 0) < 0) return -1; - } - if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) { + if (git_stream_write(t->io, request.ptr, request.size, 0) < 0) { git_buf_free(&request); return -1; } @@ -613,14 +739,14 @@ if (len > CHUNK_SIZE) { /* Flush, if necessary */ if (s->chunk_buffer_len > 0) { - if (write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0) + if (write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0) return -1; s->chunk_buffer_len = 0; } /* Write chunk directly */ - if (write_chunk(&t->socket, buffer, len) < 0) + if (write_chunk(t->io, buffer, len) < 0) return -1; } else { @@ -637,7 +763,7 @@ /* Is the buffer full? If so, then flush */ if (CHUNK_SIZE == s->chunk_buffer_len) { - if (write_chunk(&t->socket, s->chunk_buffer, s->chunk_buffer_len) < 0) + if (write_chunk(t->io, s->chunk_buffer, s->chunk_buffer_len) < 0) return -1; s->chunk_buffer_len = 0; @@ -670,15 +796,13 @@ clear_parser_state(t); - if (gen_request(&request, s, len) < 0) { - giterr_set(GITERR_NET, "Failed to generate request"); + if (gen_request(&request, s, len) < 0) return -1; - } - if (gitno_send(&t->socket, request.ptr, request.size, 0) < 0) + if (git_stream_write(t->io, request.ptr, request.size, 0) < 0) goto on_error; - if (len && gitno_send(&t->socket, buffer, len, 0) < 0) + if (len && git_stream_write(t->io, buffer, len, 0) < 0) goto on_error; git_buf_free(&request); @@ -816,8 +940,8 @@ (ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0) return ret; - if (http_connect(t) < 0) - return -1; + if ((ret = http_connect(t)) < 0) + return ret; switch (action) { case GIT_SERVICE_UPLOADPACK_LS: @@ -840,12 +964,15 @@ static int http_close(git_smart_subtransport *subtransport) { http_subtransport *t = (http_subtransport *) subtransport; + git_http_auth_context *context; + size_t i; clear_parser_state(t); - if (t->socket.socket) { - gitno_close(&t->socket); - memset(&t->socket, 0x0, sizeof(gitno_socket)); + if (t->io) { + git_stream_close(t->io); + git_stream_free(t->io); + t->io = NULL; } if (t->cred) { @@ -858,7 +985,15 @@ t->url_cred = NULL; } + git_vector_foreach(&t->auth_contexts, i, context) { + if (context->free) + context->free(context); + } + + git_vector_clear(&t->auth_contexts); + gitno_connection_data_free_ptrs(&t->connection_data); + memset(&t->connection_data, 0x0, sizeof(gitno_connection_data)); return 0; } @@ -869,6 +1004,7 @@ http_close(subtransport); + git_vector_free(&t->auth_contexts); git__free(t); } diff -Nru libgit2-0.20.0/src/transports/local.c libgit2-0.22.2/src/transports/local.c --- libgit2-0.20.0/src/transports/local.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/transports/local.c 2015-03-24 16:10:45.000000000 +0000 @@ -35,51 +35,85 @@ int flags; git_atomic cancelled; git_repository *repo; + git_transport_message_cb progress_cb; + git_transport_message_cb error_cb; + void *message_cb_payload; git_vector refs; unsigned connected : 1, have_refs : 1; } transport_local; +static void free_head(git_remote_head *head) +{ + git__free(head->name); + git__free(head->symref_target); + git__free(head); +} + +static void free_heads(git_vector *heads) +{ + git_remote_head *head; + size_t i; + + git_vector_foreach(heads, i, head) + free_head(head); + + git_vector_free(heads); +} + static int add_ref(transport_local *t, const char *name) { const char peeled[] = "^{}"; + git_reference *ref, *resolved; git_remote_head *head; + git_oid obj_id; git_object *obj = NULL, *target = NULL; git_buf buf = GIT_BUF_INIT; int error; - head = git__calloc(1, sizeof(git_remote_head)); - GITERR_CHECK_ALLOC(head); - - head->name = git__strdup(name); - GITERR_CHECK_ALLOC(head->name); + if ((error = git_reference_lookup(&ref, t->repo, name)) < 0) + return error; - error = git_reference_name_to_id(&head->oid, t->repo, name); + error = git_reference_resolve(&resolved, ref); if (error < 0) { - git__free(head->name); - git__free(head); + git_reference_free(ref); if (!strcmp(name, GIT_HEAD_FILE) && error == GIT_ENOTFOUND) { - /* This is actually okay. Empty repos often have a HEAD that points to - * a nonexistent "refs/heads/master". */ + /* This is actually okay. Empty repos often have a HEAD that + * points to a nonexistent "refs/heads/master". */ giterr_clear(); return 0; } return error; } - if (git_vector_insert(&t->refs, head) < 0) - { - git__free(head->name); - git__free(head); - return -1; + git_oid_cpy(&obj_id, git_reference_target(resolved)); + git_reference_free(resolved); + + head = git__calloc(1, sizeof(git_remote_head)); + GITERR_CHECK_ALLOC(head); + + head->name = git__strdup(name); + GITERR_CHECK_ALLOC(head->name); + + git_oid_cpy(&head->oid, &obj_id); + + if (git_reference_type(ref) == GIT_REF_SYMBOLIC) { + head->symref_target = git__strdup(git_reference_symbolic_target(ref)); + GITERR_CHECK_ALLOC(head->symref_target); + } + git_reference_free(ref); + + if ((error = git_vector_insert(&t->refs, head)) < 0) { + free_head(head); + return error; } /* If it's not a tag, we don't need to try to peel it */ if (git__prefixcmp(name, GIT_REFS_TAGS_DIR)) return 0; - if (git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY) < 0) - return -1; + if ((error = git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY)) < 0) + return error; head = NULL; @@ -94,27 +128,25 @@ /* And if it's a tag, peel it, and add it to the list */ head = git__calloc(1, sizeof(git_remote_head)); GITERR_CHECK_ALLOC(head); - if (git_buf_join(&buf, 0, name, peeled) < 0) - return -1; + if (git_buf_join(&buf, 0, name, peeled) < 0) { + free_head(head); + return -1; + } head->name = git_buf_detach(&buf); - if (git_tag_peel(&target, (git_tag *) obj) < 0) - goto on_error; + if (!(error = git_tag_peel(&target, (git_tag *)obj))) { + git_oid_cpy(&head->oid, git_object_id(target)); - git_oid_cpy(&head->oid, git_object_id(target)); - git_object_free(obj); - git_object_free(target); - - if (git_vector_insert(&t->refs, head) < 0) - return -1; - - return 0; + if ((error = git_vector_insert(&t->refs, head)) < 0) { + free_head(head); + } + } -on_error: git_object_free(obj); git_object_free(target); - return -1; + + return error; } static int store_refs(transport_local *t) @@ -161,7 +193,7 @@ /* * Try to open the url as a git directory. The direction doesn't - * matter in this case because we're calulating the heads ourselves. + * matter in this case because we're calculating the heads ourselves. */ static int local_connect( git_transport *transport, @@ -179,22 +211,22 @@ GIT_UNUSED(cred_acquire_cb); GIT_UNUSED(cred_acquire_payload); + if (t->connected) + return 0; + + free_heads(&t->refs); + t->url = git__strdup(url); GITERR_CHECK_ALLOC(t->url); t->direction = direction; t->flags = flags; - /* The repo layer doesn't want the prefix */ - if (!git__prefixcmp(t->url, "file://")) { - if (git_path_fromurl(&buf, t->url) < 0) { - git_buf_free(&buf); - return -1; - } - path = git_buf_cstr(&buf); - - } else { /* We assume transport->url is already a path */ - path = t->url; + /* 'url' may be a url or path; convert to a path */ + if ((error = git_path_from_url_or_path(&buf, url)) < 0) { + git_buf_free(&buf); + return error; } + path = git_buf_cstr(&buf); error = git_repository_open(&repo, path); @@ -222,7 +254,7 @@ return -1; } - *out = (const git_remote_head **) t->refs.contents; + *out = (const git_remote_head **)t->refs.contents; *size = t->refs.length; return 0; @@ -250,8 +282,9 @@ git_oid_cpy(&rhead->loid, git_object_id(obj)); else if (error != GIT_ENOTFOUND) return error; + else + giterr_clear(); git_object_free(obj); - giterr_clear(); } return 0; @@ -311,14 +344,11 @@ int error; git_reference *remote_ref = NULL; - /* rref will be NULL if it is implicit in the pushspec (e.g. 'b1:') */ - rref = rref ? rref : lref; - - if (lref) { + /* check for lhs, if it's empty it means to delete */ + if (lref[0] != '\0') { /* Create or update a ref */ - if ((error = git_reference_create(NULL, remote_repo, rref, loid, - !git_oid_iszero(roid))) < 0) - return error; + error = git_reference_create(NULL, remote_repo, rref, loid, + !git_oid_iszero(roid), NULL, NULL); } else { /* Delete a ref */ if ((error = git_reference_lookup(&remote_ref, remote_repo, rref)) < 0) { @@ -327,13 +357,11 @@ return error; } - if ((error = git_reference_delete(remote_ref)) < 0) - return error; - + error = git_reference_delete(remote_ref); git_reference_free(remote_ref); } - return 0; + return error; } static int local_push( @@ -346,11 +374,24 @@ git_repository *remote_repo = NULL; push_spec *spec; char *url = NULL; + const char *path; + git_buf buf = GIT_BUF_INIT; int error; unsigned int i; size_t j; - if ((error = git_repository_open(&remote_repo, push->remote->url)) < 0) + /* 'push->remote->url' may be a url or path; convert to a path */ + if ((error = git_path_from_url_or_path(&buf, push->remote->url)) < 0) { + git_buf_free(&buf); + return error; + } + path = git_buf_cstr(&buf); + + error = git_repository_open(&remote_repo, path); + + git_buf_free(&buf); + + if (error < 0) return error; /* We don't currently support pushing locally to non-bare repos. Proper @@ -377,7 +418,7 @@ git_vector_foreach(&push->specs, j, spec) { push_status *status; const git_error *last; - char *ref = spec->rref ? spec->rref : spec->lref; + char *ref = spec->refspec.dst; status = git__calloc(sizeof(push_status), 1); if (!status) @@ -389,7 +430,7 @@ goto on_error; } - error = local_push_update_remote_ref(remote_repo, spec->lref, spec->rref, + error = local_push_update_remote_ref(remote_repo, spec->refspec.src, spec->refspec.dst, &spec->loid, &spec->roid); switch (error) { @@ -445,7 +486,7 @@ typedef struct foreach_data { git_transfer_progress *stats; - git_transfer_progress_callback progress_cb; + git_transfer_progress_cb progress_cb; void *progress_payload; git_odb_writepack *writepack; } foreach_data; @@ -458,11 +499,13 @@ return data->writepack->append(data->writepack, buf, len, data->stats); } +static const char *counting_objects_fmt = "Counting objects %d\r"; + static int local_download_pack( git_transport *transport, git_repository *repo, git_transfer_progress *stats, - git_transfer_progress_callback progress_cb, + git_transfer_progress_cb progress_cb, void *progress_payload) { transport_local *t = (transport_local*)transport; @@ -474,6 +517,7 @@ git_packbuilder *pack = NULL; git_odb_writepack *writepack = NULL; git_odb *odb = NULL; + git_buf progress_info = GIT_BUF_INIT; if ((error = git_revwalk_new(&walk, t->repo)) < 0) goto cleanup; @@ -504,6 +548,13 @@ git_object_free(obj); } + if ((error = git_buf_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack))) < 0) + goto cleanup; + + if (t->progress_cb && + (error = t->progress_cb(git_buf_cstr(&progress_info), git_buf_len(&progress_info), t->message_cb_payload)) < 0) + goto cleanup; + /* Walk the objects, building a packfile */ if ((error = git_repository_odb__weakptr(&odb, repo)) < 0) goto cleanup; @@ -525,10 +576,29 @@ } git_commit_free(commit); + + git_buf_clear(&progress_info); + if ((error = git_buf_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack))) < 0) + goto cleanup; + + if (t->progress_cb && + (error = t->progress_cb(git_buf_cstr(&progress_info), git_buf_len(&progress_info), t->message_cb_payload)) < 0) + goto cleanup; + } } - if ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) < 0) + /* One last one with the newline */ + git_buf_clear(&progress_info); + git_buf_printf(&progress_info, counting_objects_fmt, git_packbuilder_object_count(pack)); + if ((error = git_buf_putc(&progress_info, '\n')) < 0) + goto cleanup; + + if (t->progress_cb && + (error = t->progress_cb(git_buf_cstr(&progress_info), git_buf_len(&progress_info), t->message_cb_payload)) < 0) + goto cleanup; + + if ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0) goto cleanup; /* Write the data to the ODB */ @@ -539,18 +609,37 @@ data.progress_payload = progress_payload; data.writepack = writepack; - if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) < 0) + if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) != 0) goto cleanup; } error = writepack->commit(writepack, stats); cleanup: if (writepack) writepack->free(writepack); + git_buf_free(&progress_info); git_packbuilder_free(pack); git_revwalk_free(walk); return error; } +static int local_set_callbacks( + git_transport *transport, + git_transport_message_cb progress_cb, + git_transport_message_cb error_cb, + git_transport_certificate_check_cb certificate_check_cb, + void *message_cb_payload) +{ + transport_local *t = (transport_local *)transport; + + GIT_UNUSED(certificate_check_cb); + + t->progress_cb = progress_cb; + t->error_cb = error_cb; + t->message_cb_payload = message_cb_payload; + + return 0; +} + static int local_is_connected(git_transport *transport) { transport_local *t = (transport_local *)transport; @@ -596,15 +685,8 @@ static void local_free(git_transport *transport) { transport_local *t = (transport_local *)transport; - size_t i; - git_remote_head *head; - git_vector_foreach(&t->refs, i, head) { - git__free(head->name); - git__free(head); - } - - git_vector_free(&t->refs); + free_heads(&t->refs); /* Close the transport, if it's still open. */ local_close(transport); @@ -619,6 +701,7 @@ int git_transport_local(git_transport **out, git_remote *owner, void *param) { + int error; transport_local *t; GIT_UNUSED(param); @@ -627,6 +710,7 @@ GITERR_CHECK_ALLOC(t); t->parent.version = GIT_TRANSPORT_VERSION; + t->parent.set_callbacks = local_set_callbacks; t->parent.connect = local_connect; t->parent.negotiate_fetch = local_negotiate_fetch; t->parent.download_pack = local_download_pack; @@ -638,7 +722,11 @@ t->parent.read_flags = local_read_flags; t->parent.cancel = local_cancel; - git_vector_init(&t->refs, 0, NULL); + if ((error = git_vector_init(&t->refs, 0, NULL)) < 0) { + git__free(t); + return error; + } + t->owner = owner; *out = (git_transport *) t; diff -Nru libgit2-0.20.0/src/transports/smart.c libgit2-0.22.2/src/transports/smart.c --- libgit2-0.20.0/src/transports/smart.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/transports/smart.c 2015-03-24 16:10:45.000000000 +0000 @@ -7,6 +7,7 @@ #include "git2.h" #include "smart.h" #include "refs.h" +#include "refspec.h" static int git_smart__recv_cb(gitno_buffer *buf) { @@ -23,13 +24,13 @@ buf->offset += bytes_read; - if (t->packetsize_cb && !t->cancelled.val) - if (t->packetsize_cb(bytes_read, t->packetsize_payload)) { + if (t->packetsize_cb && !t->cancelled.val) { + error = t->packetsize_cb(bytes_read, t->packetsize_payload); + if (error) { git_atomic_set(&t->cancelled, 1); - - giterr_clear(); return GIT_EUSER; } + } return (int)(buf->offset - old_len); } @@ -52,18 +53,20 @@ git_transport *transport, git_transport_message_cb progress_cb, git_transport_message_cb error_cb, + git_transport_certificate_check_cb certificate_check_cb, void *message_cb_payload) { transport_smart *t = (transport_smart *)transport; t->progress_cb = progress_cb; t->error_cb = error_cb; + t->certificate_check_cb = certificate_check_cb; t->message_cb_payload = message_cb_payload; return 0; } -int git_smart__update_heads(transport_smart *t) +int git_smart__update_heads(transport_smart *t, git_vector *symrefs) { size_t i; git_pkt *pkt; @@ -74,6 +77,25 @@ if (pkt->type != GIT_PKT_REF) continue; + if (symrefs) { + git_refspec *spec; + git_buf buf = GIT_BUF_INIT; + size_t j; + int error = 0; + + git_vector_foreach(symrefs, j, spec) { + git_buf_clear(&buf); + if (git_refspec_src_matches(spec, ref->head.name) && + !(error = git_refspec_transform(&buf, spec, ref->head.name))) + ref->head.symref_target = git_buf_detach(&buf); + } + + git_buf_free(&buf); + + if (error < 0) + return error; + } + if (git_vector_insert(&t->heads, &ref->head) < 0) return -1; } @@ -81,6 +103,19 @@ return 0; } +static void free_symrefs(git_vector *symrefs) +{ + git_refspec *spec; + size_t i; + + git_vector_foreach(symrefs, i, spec) { + git_refspec__free(spec); + git__free(spec); + } + + git_vector_free(symrefs); +} + static int git_smart__connect( git_transport *transport, const char *url, @@ -94,6 +129,7 @@ int error; git_pkt *pkt; git_pkt_ref *first; + git_vector symrefs; git_smart_service_t service; if (git_smart__reset_stream(t, true) < 0) @@ -122,7 +158,7 @@ /* Save off the current stream (i.e. socket) that we are working with */ t->current_stream = stream; - gitno_buffer_setup_callback(NULL, &t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); + gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); /* 2 flushes for RPC; 1 for stateful */ if ((error = git_smart__store_refs(t, t->rpc ? 2 : 1)) < 0) @@ -147,8 +183,11 @@ first = (git_pkt_ref *)git_vector_get(&t->refs, 0); + if ((error = git_vector_init(&symrefs, 1, NULL)) < 0) + return error; + /* Detect capabilities */ - if (git_smart__detect_caps(first, &t->caps) < 0) + if (git_smart__detect_caps(first, &t->caps, &symrefs) < 0) return -1; /* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */ @@ -159,7 +198,9 @@ } /* Keep a list of heads for _ls */ - git_smart__update_heads(t); + git_smart__update_heads(t, &symrefs); + + free_symrefs(&symrefs); if (t->rpc && git_smart__reset_stream(t, false) < 0) return -1; @@ -211,7 +252,7 @@ if ((error = stream->write(stream, (const char *)data, len)) < 0) return error; - gitno_buffer_setup_callback(NULL, &t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); + gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); return 0; } @@ -237,7 +278,7 @@ /* Save off the current stream (i.e. socket) that we are working with */ t->current_stream = *stream; - gitno_buffer_setup_callback(NULL, &t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); + gitno_buffer_setup_callback(&t->buffer, t->buffer_data, sizeof(t->buffer_data), git_smart__recv_cb, t); return 0; } @@ -272,6 +313,18 @@ unsigned int i; git_pkt *p; int ret; + git_smart_subtransport_stream *stream; + const char flush[] = "0000"; + + /* + * If we're still connected at this point and not using RPC, + * we should say goodbye by sending a flush, or git-daemon + * will complain that we disconnected unexpectedly. + */ + if (t->connected && !t->rpc && + !t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) { + t->current_stream->write(t->current_stream, flush, 4); + } ret = git_smart__reset_stream(t, true); @@ -342,7 +395,7 @@ t->parent.is_connected = git_smart__is_connected; t->parent.read_flags = git_smart__read_flags; t->parent.cancel = git_smart__cancel; - + t->owner = owner; t->rpc = definition->rpc; @@ -359,7 +412,7 @@ if (definition->callback(&t->wrapped, &t->parent) < 0) { git__free(t); return -1; - } + } *out = (git_transport *) t; return 0; diff -Nru libgit2-0.20.0/src/transports/smart.h libgit2-0.22.2/src/transports/smart.h --- libgit2-0.20.0/src/transports/smart.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/transports/smart.h 2015-03-24 16:10:45.000000000 +0000 @@ -9,6 +9,7 @@ #include "netops.h" #include "buffer.h" #include "push.h" +#include "git2/sys/transport.h" #define GIT_SIDE_BAND_DATA 1 #define GIT_SIDE_BAND_PROGRESS 2 @@ -23,6 +24,7 @@ #define GIT_CAP_DELETE_REFS "delete-refs" #define GIT_CAP_REPORT_STATUS "report-status" #define GIT_CAP_THIN_PACK "thin-pack" +#define GIT_CAP_SYMREF "symref" enum git_pkt_type { GIT_PKT_CMD, @@ -135,6 +137,7 @@ int flags; git_transport_message_cb progress_cb; git_transport_message_cb error_cb; + git_transport_certificate_check_cb certificate_check_cb; void *message_cb_payload; git_smart_subtransport *wrapped; git_smart_subtransport_stream *current_stream; @@ -154,7 +157,7 @@ /* smart_protocol.c */ int git_smart__store_refs(transport_smart *t, int flushes); -int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps); +int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs); int git_smart__push(git_transport *transport, git_push *push); int git_smart__negotiate_fetch( @@ -167,14 +170,14 @@ git_transport *transport, git_repository *repo, git_transfer_progress *stats, - git_transfer_progress_callback progress_cb, + git_transfer_progress_cb progress_cb, void *progress_payload); /* smart.c */ int git_smart__negotiation_step(git_transport *transport, void *data, size_t len); int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **out); -int git_smart__update_heads(transport_smart *t); +int git_smart__update_heads(transport_smart *t, git_vector *symrefs); /* smart_pkt.c */ int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len); diff -Nru libgit2-0.20.0/src/transports/smart_pkt.c libgit2-0.22.2/src/transports/smart_pkt.c --- libgit2-0.20.0/src/transports/smart_pkt.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/transports/smart_pkt.c 2015-03-24 16:10:45.000000000 +0000 @@ -153,7 +153,7 @@ return 0; } -static int progress_pkt(git_pkt **out, const char *line, size_t len) +static int sideband_progress_pkt(git_pkt **out, const char *line, size_t len) { git_pkt_progress *pkt; @@ -403,7 +403,7 @@ if (*line == GIT_SIDE_BAND_DATA) ret = data_pkt(head, line, len); else if (*line == GIT_SIDE_BAND_PROGRESS) - ret = progress_pkt(head, line, len); + ret = sideband_progress_pkt(head, line, len); else if (*line == GIT_SIDE_BAND_ERROR) ret = sideband_error_pkt(head, line, len); else if (!git__prefixcmp(line, "ACK")) @@ -433,6 +433,7 @@ if (pkt->type == GIT_PKT_REF) { git_pkt_ref *p = (git_pkt_ref *) pkt; git__free(p->head.name); + git__free(p->head.symref_target); } if (pkt->type == GIT_PKT_OK) { diff -Nru libgit2-0.20.0/src/transports/smart_protocol.c libgit2-0.22.2/src/transports/smart_protocol.c --- libgit2-0.20.0/src/transports/smart_protocol.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/transports/smart_protocol.c 2015-03-24 16:10:45.000000000 +0000 @@ -26,17 +26,16 @@ int error, flush = 0, recvd; const char *line_end = NULL; git_pkt *pkt = NULL; - git_pkt_ref *ref; size_t i; /* Clear existing refs in case git_remote_connect() is called again * after git_remote_disconnect(). */ - git_vector_foreach(refs, i, ref) { - git__free(ref->head.name); - git__free(ref); + git_vector_foreach(refs, i, pkt) { + git_pkt_free(pkt); } git_vector_clear(refs); + pkt = NULL; do { if (buf->offset > 0) @@ -45,7 +44,7 @@ error = GIT_EBUFS; if (error < 0 && error != GIT_EBUFS) - return -1; + return error; if (error == GIT_EBUFS) { if ((recvd = gitno_recv(buf)) < 0) @@ -78,7 +77,53 @@ return flush; } -int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps) +static int append_symref(const char **out, git_vector *symrefs, const char *ptr) +{ + int error; + const char *end; + git_buf buf = GIT_BUF_INIT; + git_refspec *mapping = NULL; + + ptr += strlen(GIT_CAP_SYMREF); + if (*ptr != '=') + goto on_invalid; + + ptr++; + if (!(end = strchr(ptr, ' ')) && + !(end = strchr(ptr, '\0'))) + goto on_invalid; + + if ((error = git_buf_put(&buf, ptr, end - ptr)) < 0) + return error; + + /* symref mapping has refspec format */ + mapping = git__calloc(1, sizeof(git_refspec)); + GITERR_CHECK_ALLOC(mapping); + + error = git_refspec__parse(mapping, git_buf_cstr(&buf), true); + git_buf_free(&buf); + + /* if the error isn't OOM, then it's a parse error; let's use a nicer message */ + if (error < 0) { + if (giterr_last()->klass != GITERR_NOMEMORY) + goto on_invalid; + + return error; + } + + if ((error = git_vector_insert(symrefs, mapping)) < 0) + return error; + + *out = end; + return 0; + +on_invalid: + giterr_set(GITERR_NET, "remote sent invalid symref"); + git_refspec__free(mapping); + return -1; +} + +int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs) { const char *ptr; @@ -141,6 +186,15 @@ continue; } + if (!git__prefixcmp(ptr, GIT_CAP_SYMREF)) { + int error; + + if ((error = append_symref(&ptr, symrefs, ptr)) < 0) + return error; + + continue; + } + /* We don't know this capability, so skip it */ ptr = strchr(ptr, ' '); } @@ -205,16 +259,17 @@ static int fetch_setup_walk(git_revwalk **out, git_repository *repo) { - git_revwalk *walk; + git_revwalk *walk = NULL; git_strarray refs; unsigned int i; git_reference *ref; + int error; - if (git_reference_list(&refs, repo) < 0) - return -1; + if ((error = git_reference_list(&refs, repo)) < 0) + return error; - if (git_revwalk_new(&walk, repo) < 0) - return -1; + if ((error = git_revwalk_new(&walk, repo)) < 0) + return error; git_revwalk_sorting(walk, GIT_SORT_TIME); @@ -223,13 +278,13 @@ if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR)) continue; - if (git_reference_lookup(&ref, repo, refs.strings[i]) < 0) + if ((error = git_reference_lookup(&ref, repo, refs.strings[i])) < 0) goto on_error; if (git_reference_type(ref) == GIT_REF_SYMBOLIC) continue; - if (git_revwalk_push(walk, git_reference_target(ref)) < 0) + if ((error = git_revwalk_push(walk, git_reference_target(ref))) < 0) goto on_error; git_reference_free(ref); @@ -240,9 +295,10 @@ return 0; on_error: + git_revwalk_free(walk); git_reference_free(ref); git_strarray_free(&refs); - return -1; + return error; } static int wait_while_ack(gitno_buffer *buf) @@ -260,7 +316,7 @@ break; if (pkt->type == GIT_PKT_ACK && - (pkt->status != GIT_ACK_CONTINUE || + (pkt->status != GIT_ACK_CONTINUE && pkt->status != GIT_ACK_COMMON)) { git__free(pkt); return 0; @@ -449,7 +505,7 @@ struct network_packetsize_payload { - git_transfer_progress_callback callback; + git_transfer_progress_cb callback; void *payload; git_transfer_progress *stats; size_t last_fired_bytes; @@ -477,7 +533,7 @@ git_transport *transport, git_repository *repo, git_transfer_progress *stats, - git_transfer_progress_callback progress_cb, + git_transfer_progress_cb transfer_progress_cb, void *progress_payload) { transport_smart *t = (transport_smart *)transport; @@ -489,8 +545,8 @@ memset(stats, 0, sizeof(git_transfer_progress)); - if (progress_cb) { - npp.callback = progress_cb; + if (transfer_progress_cb) { + npp.callback = transfer_progress_cb; npp.payload = progress_payload; npp.stats = stats; t->packetsize_cb = &network_packetsize; @@ -503,7 +559,7 @@ } if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 || - ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) < 0)) + ((error = git_odb_write_pack(&writepack, odb, transfer_progress_cb, progress_payload)) != 0)) goto done; /* @@ -517,57 +573,67 @@ } do { - git_pkt *pkt; + git_pkt *pkt = NULL; /* Check cancellation before network call */ if (t->cancelled.val) { - giterr_set(GITERR_NET, "The fetch was cancelled by the user"); + giterr_clear(); error = GIT_EUSER; goto done; } - if ((error = recv_pkt(&pkt, buf)) < 0) - goto done; + if ((error = recv_pkt(&pkt, buf)) >= 0) { + /* Check cancellation after network call */ + if (t->cancelled.val) { + giterr_clear(); + error = GIT_EUSER; + } else if (pkt->type == GIT_PKT_PROGRESS) { + if (t->progress_cb) { + git_pkt_progress *p = (git_pkt_progress *) pkt; + error = t->progress_cb(p->data, p->len, t->message_cb_payload); + } + } else if (pkt->type == GIT_PKT_DATA) { + git_pkt_data *p = (git_pkt_data *) pkt; - /* Check cancellation after network call */ - if (t->cancelled.val) { - giterr_set(GITERR_NET, "The fetch was cancelled by the user"); - error = GIT_EUSER; - goto done; + if (p->len) + error = writepack->append(writepack, p->data, p->len, stats); + } else if (pkt->type == GIT_PKT_FLUSH) { + /* A flush indicates the end of the packfile */ + git__free(pkt); + break; + } } - if (pkt->type == GIT_PKT_PROGRESS) { - if (t->progress_cb) { - git_pkt_progress *p = (git_pkt_progress *) pkt; - if (t->progress_cb(p->data, p->len, t->message_cb_payload)) { - giterr_set(GITERR_NET, "The fetch was cancelled by the user"); - return GIT_EUSER; - } - } - git__free(pkt); - } else if (pkt->type == GIT_PKT_DATA) { - git_pkt_data *p = (git_pkt_data *) pkt; - error = writepack->append(writepack, p->data, p->len, stats); + git__free(pkt); + if (error < 0) + goto done; - git__free(pkt); - if (error < 0) - goto done; - } else if (pkt->type == GIT_PKT_FLUSH) { - /* A flush indicates the end of the packfile */ - git__free(pkt); - break; - } } while (1); + /* + * Trailing execution of transfer_progress_cb, if necessary... + * Only the callback through the npp datastructure currently + * updates the last_fired_bytes value. It is possible that + * progress has already been reported with the correct + * "received_bytes" value, but until (if?) this is unified + * then we will report progress again to be sure that the + * correct last received_bytes value is reported. + */ + if (npp.callback && npp.stats->received_bytes > npp.last_fired_bytes) { + error = npp.callback(npp.stats, npp.payload); + if (error != 0) + goto done; + } + error = writepack->commit(writepack, stats); done: if (writepack) writepack->free(writepack); - - /* Trailing execution of progress_cb, if necessary */ - if (npp.callback && npp.stats->received_bytes > npp.last_fired_bytes) - npp.callback(npp.stats, npp.payload); + if (transfer_progress_cb) { + t->packetsize_cb = NULL; + t->packetsize_payload = NULL; + } return error; } @@ -576,12 +642,12 @@ { push_spec *spec; size_t i, len; - char old_id[41], new_id[41]; + char old_id[GIT_OID_HEXSZ+1], new_id[GIT_OID_HEXSZ+1]; - old_id[40] = '\0'; new_id[40] = '\0'; + old_id[GIT_OID_HEXSZ] = '\0'; new_id[GIT_OID_HEXSZ] = '\0'; git_vector_foreach(&push->specs, i, spec) { - len = 2*GIT_OID_HEXSZ + 7 + strlen(spec->rref); + len = 2*GIT_OID_HEXSZ + 7 + strlen(spec->refspec.dst); if (i == 0) { ++len; /* '\0' */ @@ -593,7 +659,7 @@ git_oid_fmt(old_id, &spec->roid); git_oid_fmt(new_id, &spec->loid); - git_buf_printf(buf, "%04"PRIxZ"%s %s %s", len, old_id, new_id, spec->rref); + git_buf_printf(buf, "%04"PRIxZ"%s %s %s", len, old_id, new_id, spec->refspec.dst); if (i == 0) { git_buf_putc(buf, '\0'); @@ -619,7 +685,7 @@ switch (pkt->type) { case GIT_PKT_OK: - status = git__calloc(1, sizeof(push_status)); + status = git__calloc(sizeof(push_status), 1); GITERR_CHECK_ALLOC(status); status->msg = NULL; status->ref = git__strdup(((git_pkt_ok *)pkt)->ref); @@ -681,10 +747,11 @@ return 0; } -static int parse_report(gitno_buffer *buf, git_push *push) +static int parse_report(transport_smart *transport, git_push *push) { git_pkt *pkt = NULL; const char *line_end = NULL; + gitno_buffer *buf = &transport->buffer; int error, recvd; for (;;) { @@ -723,6 +790,10 @@ error = -1; break; case GIT_PKT_PROGRESS: + if (transport->progress_cb) { + git_pkt_progress *p = (git_pkt_progress *) pkt; + error = transport->progress_cb(p->data, p->len, transport->message_cb_payload); + } break; default: error = add_push_report_pkt(push, pkt); @@ -747,7 +818,7 @@ added->type = GIT_PKT_REF; git_oid_cpy(&added->head.oid, &push_spec->loid); - added->head.name = git__strdup(push_spec->rref); + added->head.name = git__strdup(push_spec->refspec.dst); if (!added->head.name || git_vector_insert(refs, added) < 0) { @@ -786,7 +857,7 @@ /* For each push spec we sent to the server, we should have * gotten back a status packet in the push report which matches */ - if (strcmp(push_spec->rref, push_status->ref)) { + if (strcmp(push_spec->refspec.dst, push_status->ref)) { giterr_set(GITERR_NET, "report-status: protocol error"); return -1; } @@ -803,7 +874,7 @@ push_status = git_vector_get(push_report, i); ref = git_vector_get(refs, j); - cmp = strcmp(push_spec->rref, ref->head.name); + cmp = strcmp(push_spec->refspec.dst, ref->head.name); /* Iterate appropriately */ if (cmp <= 0) i++; @@ -868,10 +939,7 @@ if ((current_time - payload->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) { payload->last_progress_report_time = current_time; - if (payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload)) { - giterr_clear(); - error = GIT_EUSER; - } + error = payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload); } } @@ -897,7 +965,7 @@ #ifdef PUSH_DEBUG { git_remote_head *head; - char hex[41]; hex[40] = '\0'; + char hex[GIT_OID_HEXSZ+1]; hex[GIT_OID_HEXSZ] = '\0'; git_vector_foreach(&push->remote->refs, i, head) { git_oid_fmt(hex, &head->oid); @@ -919,7 +987,7 @@ * cases except when we only send delete commands */ git_vector_foreach(&push->specs, i, spec) { - if (spec->lref) { + if (spec->refspec.src && spec->refspec.src[0] != '\0') { need_pack = 1; break; } @@ -938,12 +1006,19 @@ * we consider the pack to have been unpacked successfully */ if (!push->specs.length || !push->report_status) push->unpack_ok = 1; - else if ((error = parse_report(&t->buffer, push)) < 0) + else if ((error = parse_report(t, push)) < 0) goto done; /* If progress is being reported write the final report */ if (push->transfer_progress_cb) { - push->transfer_progress_cb(push->pb->nr_written, push->pb->nr_objects, packbuilder_payload.last_bytes, push->transfer_progress_cb_payload); + error = push->transfer_progress_cb( + push->pb->nr_written, + push->pb->nr_objects, + packbuilder_payload.last_bytes, + push->transfer_progress_cb_payload); + + if (error < 0) + goto done; } if (push->status.length) { @@ -951,7 +1026,7 @@ if (error < 0) goto done; - error = git_smart__update_heads(t); + error = git_smart__update_heads(t, NULL); } done: diff -Nru libgit2-0.20.0/src/transports/ssh.c libgit2-0.22.2/src/transports/ssh.c --- libgit2-0.20.0/src/transports/ssh.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/transports/ssh.c 2015-03-24 16:10:45.000000000 +0000 @@ -5,15 +5,19 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#ifdef GIT_SSH +#include +#endif + #include "git2.h" #include "buffer.h" #include "netops.h" #include "smart.h" +#include "cred.h" +#include "socket_stream.h" #ifdef GIT_SSH -#include - #define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport) static const char prefix_ssh[] = "ssh://"; @@ -22,7 +26,7 @@ typedef struct { git_smart_subtransport_stream parent; - gitno_socket socket; + git_stream *io; LIBSSH2_SESSION *session; LIBSSH2_CHANNEL *channel; const char *cmd; @@ -35,8 +39,12 @@ transport_smart *owner; ssh_stream *current_stream; git_cred *cred; + char *cmd_uploadpack; + char *cmd_receivepack; } ssh_subtransport; +static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username); + static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg) { char *ssherr; @@ -53,6 +61,7 @@ static int gen_proto(git_buf *request, const char *cmd, const char *url) { char *repo; + int len; if (!git__prefixcmp(url, prefix_ssh)) { url = url + strlen(prefix_ssh); @@ -67,7 +76,7 @@ return -1; } - int len = strlen(cmd) + 1 /* Space */ + 1 /* Quote */ + strlen(repo) + 1 /* Quote */ + 1; + 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); @@ -131,11 +140,22 @@ size_t len) { ssh_stream *s = (ssh_stream *)stream; + size_t off = 0; + ssize_t ret = 0; if (!s->sent_command && send_command(s) < 0) return -1; - if (libssh2_channel_write(s->channel, buffer, len) < LIBSSH2_ERROR_NONE) { + do { + ret = libssh2_channel_write(s->channel, buffer + off, len - off); + if (ret < 0) + break; + + off += ret; + + } while (off < len); + + if (ret < 0) { ssh_error(s->session, "SSH could not write data"); return -1; } @@ -164,9 +184,10 @@ s->session = NULL; } - if (s->socket.socket) { - (void)gitno_close(&s->socket); - /* can't do anything here with error return value */ + if (s->io) { + git_stream_close(s->io); + git_stream_free(s->io); + s->io = NULL; } git__free(s->url); @@ -235,35 +256,107 @@ return 0; } +static int ssh_agent_auth(LIBSSH2_SESSION *session, git_cred_ssh_key *c) { + int rc = LIBSSH2_ERROR_NONE; + + struct libssh2_agent_publickey *curr, *prev = NULL; + + LIBSSH2_AGENT *agent = libssh2_agent_init(session); + + if (agent == NULL) + return -1; + + rc = libssh2_agent_connect(agent); + + if (rc != LIBSSH2_ERROR_NONE) + goto shutdown; + + rc = libssh2_agent_list_identities(agent); + + if (rc != LIBSSH2_ERROR_NONE) + goto shutdown; + + while (1) { + rc = libssh2_agent_get_identity(agent, &curr, prev); + + if (rc < 0) + goto shutdown; + + if (rc == 1) + goto shutdown; + + rc = libssh2_agent_userauth(agent, c->username, curr); + + if (rc == 0) + break; + + prev = curr; + } + +shutdown: + + if (rc != LIBSSH2_ERROR_NONE) + ssh_error(session, "error authenticating"); + + libssh2_agent_disconnect(agent); + libssh2_agent_free(agent); + + return rc; +} + static int _git_ssh_authenticate_session( LIBSSH2_SESSION* session, - const char *user, git_cred* cred) { int rc; do { + giterr_clear(); switch (cred->credtype) { case GIT_CREDTYPE_USERPASS_PLAINTEXT: { git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred; - user = c->username ? c->username : user; - rc = libssh2_userauth_password(session, user, c->password); + rc = libssh2_userauth_password(session, c->username, c->password); break; } case GIT_CREDTYPE_SSH_KEY: { git_cred_ssh_key *c = (git_cred_ssh_key *)cred; - user = c->username ? c->username : user; - rc = libssh2_userauth_publickey_fromfile( - session, c->username, c->publickey, c->privatekey, c->passphrase); + + if (c->privatekey) + rc = libssh2_userauth_publickey_fromfile( + session, c->username, c->publickey, + c->privatekey, c->passphrase); + else + rc = ssh_agent_auth(session, c); + break; } case GIT_CREDTYPE_SSH_CUSTOM: { git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred; - user = c->username ? c->username : user; rc = libssh2_userauth_publickey( session, c->username, (const unsigned char *)c->publickey, - c->publickey_len, c->sign_callback, &c->sign_data); + c->publickey_len, c->sign_callback, &c->payload); + break; + } + case GIT_CREDTYPE_SSH_INTERACTIVE: { + void **abstract = libssh2_session_abstract(session); + git_cred_ssh_interactive *c = (git_cred_ssh_interactive *)cred; + + /* ideally, we should be able to set this by calling + * libssh2_session_init_ex() instead of libssh2_session_init(). + * libssh2's API is inconsistent here i.e. libssh2_userauth_publickey() + * allows you to pass the `abstract` as part of the call, whereas + * libssh2_userauth_keyboard_interactive() does not! + * + * The only way to set the `abstract` pointer is by calling + * libssh2_session_abstract(), which will replace the existing + * pointer as is done below. This is safe for now (at time of writing), + * but may not be valid in future. + */ + *abstract = c->payload; + + rc = libssh2_userauth_keyboard_interactive( + session, c->username, c->prompt_callback); break; } default: @@ -271,20 +364,62 @@ } } 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_NONE) { - ssh_error(session, "Failed to authenticate SSH session"); + if (!giterr_last()) + ssh_error(session, "Failed to authenticate SSH session"); + return -1; + } + + return 0; +} + +static int request_creds(git_cred **out, ssh_subtransport *t, const char *user, int auth_methods) +{ + int error, no_callback = 0; + git_cred *cred = NULL; + + if (!t->owner->cred_acquire_cb) { + no_callback = 1; + } else { + error = t->owner->cred_acquire_cb(&cred, t->owner->url, user, auth_methods, + t->owner->cred_acquire_payload); + + if (error == GIT_PASSTHROUGH) + no_callback = 1; + else if (error < 0) + return error; + else if (!cred) { + giterr_set(GITERR_SSH, "Callback failed to initialize SSH credentials"); + return -1; + } + } + + if (no_callback) { + giterr_set(GITERR_SSH, "authentication required but no callback set"); + return -1; + } + + if (!(cred->credtype & auth_methods)) { + cred->free(cred); + giterr_set(GITERR_SSH, "callback returned unsupported credentials type"); return -1; } + *out = cred; + return 0; } static int _git_ssh_session_create( LIBSSH2_SESSION** session, - gitno_socket socket) + git_stream *io) { int rc = 0; LIBSSH2_SESSION* s; + git_socket_stream *socket = (git_socket_stream *) io; assert(session); @@ -295,7 +430,7 @@ } do { - rc = libssh2_session_startup(s, socket.socket); + rc = libssh2_session_startup(s, socket->s); } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc); if (rc != LIBSSH2_ERROR_NONE) { @@ -319,66 +454,126 @@ { char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; const char *default_port="22"; + int auth_methods, error = 0; ssh_stream *s; + git_cred *cred = NULL; LIBSSH2_SESSION* session=NULL; LIBSSH2_CHANNEL* channel=NULL; + t->current_stream = NULL; + *stream = NULL; if (ssh_stream_alloc(t, url, cmd, stream) < 0) return -1; s = (ssh_stream *)*stream; + s->session = NULL; + s->channel = NULL; if (!git__prefixcmp(url, prefix_ssh)) { - if (gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port) < 0) - goto on_error; + if ((error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port)) < 0) + goto done; } else { - if (git_ssh_extract_url_parts(&host, &user, url) < 0) - goto on_error; + if ((error = git_ssh_extract_url_parts(&host, &user, url)) < 0) + goto done; port = git__strdup(default_port); GITERR_CHECK_ALLOC(port); } - if (gitno_connect(&s->socket, host, port, 0) < 0) - goto on_error; + if ((error = git_socket_stream_new(&s->io, host, port)) < 0 || + (error = git_stream_connect(s->io)) < 0) + goto done; + + if ((error = _git_ssh_session_create(&session, s->io)) < 0) + goto done; + + if (t->owner->certificate_check_cb != NULL) { + git_cert_hostkey cert = { 0 }, *cert_ptr; + const char *key; + + cert.cert_type = GIT_CERT_HOSTKEY_LIBSSH2; + + key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1); + if (key != NULL) { + cert.type |= GIT_CERT_SSH_SHA1; + memcpy(&cert.hash_sha1, key, 20); + } - if (user && pass) { - if (git_cred_userpass_plaintext_new(&t->cred, user, pass) < 0) - goto on_error; - } else if (t->owner->cred_acquire_cb) { - if (t->owner->cred_acquire_cb( - &t->cred, t->owner->url, user, - GIT_CREDTYPE_USERPASS_PLAINTEXT | - GIT_CREDTYPE_SSH_KEY | - GIT_CREDTYPE_SSH_CUSTOM, - t->owner->cred_acquire_payload) < 0) - goto on_error; + key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5); + if (key != NULL) { + cert.type |= GIT_CERT_SSH_MD5; + memcpy(&cert.hash_md5, key, 16); + } - if (!t->cred) { - giterr_set(GITERR_SSH, "Callback failed to initialize SSH credentials"); - goto on_error; + if (cert.type == 0) { + giterr_set(GITERR_SSH, "unable to get the host key"); + error = -1; + goto done; } - } else { - giterr_set(GITERR_SSH, "Cannot set up SSH connection without credentials"); - goto on_error; - } - assert(t->cred); - if (!user && !git_cred_has_username(t->cred)) { - giterr_set_str(GITERR_NET, "Cannot authenticate without a username"); - goto on_error; + /* We don't currently trust any hostkeys */ + giterr_clear(); + + cert_ptr = &cert; + + error = t->owner->certificate_check_cb((git_cert *) cert_ptr, 0, host, t->owner->message_cb_payload); + if (error < 0) { + if (!giterr_last()) + giterr_set(GITERR_NET, "user cancelled hostkey check"); + + goto done; + } } - if (_git_ssh_session_create(&session, s->socket) < 0) - goto on_error; + /* we need the username to ask for auth methods */ + if (!user) { + if ((error = request_creds(&cred, t, NULL, GIT_CREDTYPE_USERNAME)) < 0) + goto done; + + user = git__strdup(((git_cred_username *) cred)->username); + cred->free(cred); + cred = NULL; + if (!user) + goto done; + } else if (user && pass) { + if ((error = git_cred_userpass_plaintext_new(&cred, user, pass)) < 0) + goto done; + } + + if ((error = list_auth_methods(&auth_methods, session, user)) < 0) + goto done; + + error = GIT_EAUTH; + /* if we already have something to try */ + if (cred && auth_methods & cred->credtype) + error = _git_ssh_authenticate_session(session, cred); + + while (error == GIT_EAUTH) { + if (cred) { + cred->free(cred); + cred = NULL; + } - if (_git_ssh_authenticate_session(session, user, t->cred) < 0) - goto on_error; + if ((error = request_creds(&cred, t, user, auth_methods)) < 0) + goto done; + + if (strcmp(user, git_cred__username(cred))) { + giterr_set(GITERR_SSH, "username does not match previous request"); + error = -1; + goto done; + } + + error = _git_ssh_authenticate_session(session, cred); + } + + if (error < 0) + goto done; channel = libssh2_channel_open_session(session); if (!channel) { + error = -1; ssh_error(session, "Failed to open SSH channel"); - goto on_error; + goto done; } libssh2_channel_set_blocking(channel, 1); @@ -387,31 +582,26 @@ s->channel = channel; t->current_stream = s; - git__free(host); - git__free(port); - git__free(path); - git__free(user); - git__free(pass); - return 0; +done: + if (error < 0) { + if (*stream) + ssh_stream_free(*stream); -on_error: - s->session = NULL; - s->channel = NULL; - t->current_stream = NULL; + if (session) + libssh2_session_free(session); + } - if (*stream) - ssh_stream_free(*stream); + if (cred) + cred->free(cred); git__free(host); git__free(port); + git__free(path); git__free(user); git__free(pass); - if (session) - libssh2_session_free(session); - - return -1; + return error; } static int ssh_uploadpack_ls( @@ -419,10 +609,9 @@ const char *url, git_smart_subtransport_stream **stream) { - if (_git_ssh_setup_conn(t, url, cmd_uploadpack, stream) < 0) - return -1; + const char *cmd = t->cmd_uploadpack ? t->cmd_uploadpack : cmd_uploadpack; - return 0; + return _git_ssh_setup_conn(t, url, cmd, stream); } static int ssh_uploadpack( @@ -446,10 +635,10 @@ const char *url, git_smart_subtransport_stream **stream) { - if (_git_ssh_setup_conn(t, url, cmd_receivepack, stream) < 0) - return -1; + const char *cmd = t->cmd_receivepack ? t->cmd_receivepack : cmd_receivepack; - return 0; + + return _git_ssh_setup_conn(t, url, cmd, stream); } static int ssh_receivepack( @@ -511,8 +700,57 @@ assert(!t->current_stream); + git__free(t->cmd_uploadpack); + git__free(t->cmd_receivepack); git__free(t); } + +#define SSH_AUTH_PUBLICKEY "publickey" +#define SSH_AUTH_PASSWORD "password" +#define SSH_AUTH_KEYBOARD_INTERACTIVE "keyboard-interactive" + +static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username) +{ + const char *list, *ptr; + + *out = 0; + + list = libssh2_userauth_list(session, username, strlen(username)); + + /* either error, or the remote accepts NONE auth, which is bizarre, let's punt */ + if (list == NULL && !libssh2_userauth_authenticated(session)) + return -1; + + ptr = list; + while (ptr) { + if (*ptr == ',') + ptr++; + + if (!git__prefixcmp(ptr, SSH_AUTH_PUBLICKEY)) { + *out |= GIT_CREDTYPE_SSH_KEY; + *out |= GIT_CREDTYPE_SSH_CUSTOM; + ptr += strlen(SSH_AUTH_PUBLICKEY); + continue; + } + + if (!git__prefixcmp(ptr, SSH_AUTH_PASSWORD)) { + *out |= GIT_CREDTYPE_USERPASS_PLAINTEXT; + ptr += strlen(SSH_AUTH_PASSWORD); + continue; + } + + if (!git__prefixcmp(ptr, SSH_AUTH_KEYBOARD_INTERACTIVE)) { + *out |= GIT_CREDTYPE_SSH_INTERACTIVE; + ptr += strlen(SSH_AUTH_KEYBOARD_INTERACTIVE); + continue; + } + + /* Skipt it if we don't know it */ + ptr = strchr(ptr, ','); + } + + return 0; +} #endif int git_smart_subtransport_ssh( @@ -538,6 +776,49 @@ assert(out); *out = NULL; + + giterr_set(GITERR_INVALID, "Cannot create SSH transport. Library was built without SSH support"); + return -1; +#endif +} + +int git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *payload) +{ +#ifdef GIT_SSH + git_strarray *paths = (git_strarray *) payload; + git_transport *transport; + transport_smart *smart; + ssh_subtransport *t; + int error; + git_smart_subtransport_definition ssh_definition = { + git_smart_subtransport_ssh, + 0, /* no RPC */ + }; + + if (paths->count != 2) { + giterr_set(GITERR_SSH, "invalid ssh paths, must be two strings"); + return GIT_EINVALIDSPEC; + } + + if ((error = git_transport_smart(&transport, owner, &ssh_definition)) < 0) + return error; + + smart = (transport_smart *) transport; + t = (ssh_subtransport *) smart->wrapped; + + t->cmd_uploadpack = git__strdup(paths->strings[0]); + GITERR_CHECK_ALLOC(t->cmd_uploadpack); + t->cmd_receivepack = git__strdup(paths->strings[1]); + GITERR_CHECK_ALLOC(t->cmd_receivepack); + + *out = transport; + return 0; +#else + GIT_UNUSED(owner); + GIT_UNUSED(payload); + + assert(out); + *out = NULL; giterr_set(GITERR_INVALID, "Cannot create SSH transport. Library was built without SSH support"); return -1; diff -Nru libgit2-0.20.0/src/transports/winhttp.c libgit2-0.22.2/src/transports/winhttp.c --- libgit2-0.20.0/src/transports/winhttp.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/transports/winhttp.c 2015-03-24 16:10:45.000000000 +0000 @@ -16,11 +16,17 @@ #include "remote.h" #include "repository.h" +#include +#pragma comment(lib, "crypt32") #include #pragma comment(lib, "winhttp") #include +/* For IInternetSecurityManager zone check */ +#include +#include + /* For UuidCreate */ #pragma comment(lib, "rpcrt4") @@ -31,6 +37,11 @@ #define WINHTTP_OPTION_PEERDIST_EXTENSION_STATE 109 #define CACHED_POST_BODY_BUF_SIZE 4096 #define UUID_LENGTH_CCH 32 +#define TIMEOUT_INFINITE -1 +#define DEFAULT_CONNECT_TIMEOUT 60000 +#ifndef WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH +#define WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH 0 +#endif static const char *prefix_http = "http://"; static const char *prefix_https = "https://"; @@ -87,30 +98,16 @@ git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred; git_buf buf = GIT_BUF_INIT, raw = GIT_BUF_INIT; wchar_t *wide = NULL; - int error = -1, wide_len = 0; + int error = -1, wide_len; git_buf_printf(&raw, "%s:%s", c->username, c->password); if (git_buf_oom(&raw) || git_buf_puts(&buf, "Authorization: Basic ") < 0 || - git_buf_put_base64(&buf, git_buf_cstr(&raw), raw.size) < 0) - goto on_error; - - wide_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, - git_buf_cstr(&buf), -1, NULL, 0); - - if (!wide_len) { - giterr_set(GITERR_OS, "Failed to measure string for wide conversion"); - goto on_error; - } - - wide = git__malloc(wide_len * sizeof(wchar_t)); - - if (!wide) + git_buf_encode_base64(&buf, git_buf_cstr(&raw), raw.size) < 0) goto on_error; - if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, - git_buf_cstr(&buf), -1, wide, wide_len)) { + if ((wide_len = git__utf8_to_16_alloc(&wide, git_buf_cstr(&buf))) < 0) { giterr_set(GITERR_OS, "Failed to convert string to wide form"); goto on_error; } @@ -141,12 +138,12 @@ static int apply_default_credentials(HINTERNET request) { - /* If we are explicitly asked to deliver default credentials, turn set - * the security level to low which will guarantee they are delivered. - * The default is "medium" which applies to the intranet and sounds - * like it would correspond to Internet Explorer security zones, but - * in fact does not. - */ + /* Either the caller explicitly requested that default credentials be passed, + * or our fallback credential callback was invoked and checked that the target + * URI was in the appropriate Internet Explorer security zone. By setting this + * flag, we guarantee that the credentials are delivered by WinHTTP. The default + * 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; if (!WinHttpSetOption(request, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(DWORD))) @@ -155,6 +152,120 @@ return 0; } +static int fallback_cred_acquire_cb( + git_cred **cred, + const char *url, + const char *username_from_url, + unsigned int allowed_types, + void *payload) +{ + int error = 1; + + GIT_UNUSED(username_from_url); + GIT_UNUSED(payload); + + /* If the target URI supports integrated Windows authentication + * as an authentication mechanism */ + if (GIT_CREDTYPE_DEFAULT & allowed_types) { + wchar_t *wide_url; + + /* Convert URL to wide characters */ + if (git__utf8_to_16_alloc(&wide_url, url) < 0) { + giterr_set(GITERR_OS, "Failed to convert string to wide form"); + return -1; + } + + if (SUCCEEDED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) { + IInternetSecurityManager* pISM; + + /* And if the target URI is in the My Computer, Intranet, or Trusted zones */ + if (SUCCEEDED(CoCreateInstance(&CLSID_InternetSecurityManager, NULL, + CLSCTX_ALL, &IID_IInternetSecurityManager, (void **)&pISM))) { + DWORD dwZone; + + if (SUCCEEDED(pISM->lpVtbl->MapUrlToZone(pISM, wide_url, &dwZone, 0)) && + (URLZONE_LOCAL_MACHINE == dwZone || + URLZONE_INTRANET == dwZone || + URLZONE_TRUSTED == dwZone)) { + git_cred *existing = *cred; + + if (existing) + existing->free(existing); + + /* Then use default Windows credentials to authenticate this request */ + error = git_cred_default_new(cred); + } + + pISM->lpVtbl->Release(pISM); + } + + CoUninitialize(); + } + + git__free(wide_url); + } + + return error; +} + +static int certificate_check(winhttp_stream *s, int valid) +{ + int error; + winhttp_subtransport *t = OWNING_SUBTRANSPORT(s); + PCERT_CONTEXT cert_ctx; + DWORD cert_ctx_size = sizeof(cert_ctx); + git_cert_x509 cert; + + /* If there is no override, we should fail if WinHTTP doesn't think it's fine */ + if (t->owner->certificate_check_cb == NULL && !valid) + return GIT_ECERTIFICATE; + + if (t->owner->certificate_check_cb == NULL || !t->connection_data.use_ssl) + return 0; + + if (!WinHttpQueryOption(s->request, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &cert_ctx, &cert_ctx_size)) { + giterr_set(GITERR_OS, "failed to get server certificate"); + return -1; + } + + giterr_clear(); + cert.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); + CertFreeCertificateContext(cert_ctx); + + if (error < 0 && !giterr_last()) + giterr_set(GITERR_NET, "user cancelled certificate check"); + + return error; +} + +static void winhttp_stream_close(winhttp_stream *s) +{ + if (s->chunk_buffer) { + git__free(s->chunk_buffer); + s->chunk_buffer = NULL; + } + + if (s->post_body) { + CloseHandle(s->post_body); + s->post_body = NULL; + } + + if (s->request_uri) { + git__free(s->request_uri); + s->request_uri = NULL; + } + + if (s->request) { + WinHttpCloseHandle(s->request); + s->request = NULL; + } + + s->sent_request = 0; +} + static int winhttp_stream_connect(winhttp_stream *s) { winhttp_subtransport *t = OWNING_SUBTRANSPORT(s); @@ -163,8 +274,10 @@ wchar_t ct[MAX_CONTENT_TYPE_LEN]; wchar_t *types[] = { L"*/*", NULL }; BOOL peerdist = FALSE; - int error = -1, wide_len; + int error = -1; unsigned long disable_redirects = WINHTTP_DISABLE_REDIRECTS; + int default_timeout = TIMEOUT_INFINITE; + int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT; /* Prepare URL */ git_buf_printf(&buf, "%s%s", t->connection_data.path, s->service_url); @@ -173,21 +286,7 @@ return -1; /* Convert URL to wide characters */ - wide_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, - git_buf_cstr(&buf), -1, NULL, 0); - - if (!wide_len) { - giterr_set(GITERR_OS, "Failed to measure string for wide conversion"); - goto on_error; - } - - s->request_uri = git__malloc(wide_len * sizeof(wchar_t)); - - if (!s->request_uri) - goto on_error; - - if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, - git_buf_cstr(&buf), -1, s->request_uri, wide_len)) { + if (git__utf8_to_16_alloc(&s->request_uri, git_buf_cstr(&buf)) < 0) { giterr_set(GITERR_OS, "Failed to convert string to wide form"); goto on_error; } @@ -207,6 +306,11 @@ goto on_error; } + if (!WinHttpSetTimeouts(s->request, default_timeout, default_connect_timeout, default_timeout, default_timeout)) { + giterr_set(GITERR_OS, "Failed to set timeouts for WinHTTP"); + goto on_error; + } + /* Set proxy if necessary */ if (git_remote__get_http_proxy(t->owner->owner, !!t->connection_data.use_ssl, &proxy_url) < 0) goto on_error; @@ -216,30 +320,17 @@ wchar_t *proxy_wide; /* Convert URL to wide characters */ - wide_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, - proxy_url, -1, NULL, 0); - - if (!wide_len) { - giterr_set(GITERR_OS, "Failed to measure string for wide conversion"); - goto on_error; - } + int proxy_wide_len = git__utf8_to_16_alloc(&proxy_wide, proxy_url); - proxy_wide = git__malloc(wide_len * sizeof(wchar_t)); - - if (!proxy_wide) - goto on_error; - - if (!MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, - proxy_url, -1, proxy_wide, wide_len)) { + if (proxy_wide_len < 0) { giterr_set(GITERR_OS, "Failed to convert string to wide form"); - git__free(proxy_wide); goto on_error; } /* Strip any trailing forward slash on the proxy URL; * WinHTTP doesn't like it if one is present */ - if (wide_len > 1 && L'/' == proxy_wide[wide_len - 2]) - proxy_wide[wide_len - 2] = L'\0'; + if (proxy_wide_len > 1 && L'/' == proxy_wide[proxy_wide_len - 2]) + proxy_wide[proxy_wide_len - 2] = L'\0'; proxy_info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY; proxy_info.lpszProxy = proxy_wide; @@ -290,7 +381,10 @@ s->service) < 0) goto on_error; - git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)); + if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) { + giterr_set(GITERR_OS, "Failed to convert content-type to wide characters"); + goto on_error; + } if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) { @@ -304,7 +398,10 @@ s->service) < 0) goto on_error; - git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)); + if (git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf)) < 0) { + giterr_set(GITERR_OS, "Failed to convert accept header to wide characters"); + goto on_error; + } if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L, WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) { @@ -319,13 +416,6 @@ if (t->owner->parent.read_flags(&t->owner->parent, &flags) < 0) goto on_error; - - if ((GIT_TRANSPORTFLAGS_NO_CHECK_CERT & flags) && - !WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS, - (LPVOID)&no_check_cert_flags, sizeof(no_check_cert_flags))) { - giterr_set(GITERR_OS, "Failed to set options to ignore cert errors"); - goto on_error; - } } /* If we have a credential on the subtransport, apply it to the request */ @@ -355,6 +445,9 @@ error = 0; on_error: + if (error < 0) + winhttp_stream_close(s); + git__free(proxy_url); git_buf_free(&buf); return error; @@ -432,21 +525,56 @@ return 0; } +static int winhttp_close_connection(winhttp_subtransport *t) +{ + int ret = 0; + + if (t->connection) { + if (!WinHttpCloseHandle(t->connection)) { + giterr_set(GITERR_OS, "Unable to close connection"); + ret = -1; + } + + t->connection = NULL; + } + + if (t->session) { + if (!WinHttpCloseHandle(t->session)) { + giterr_set(GITERR_OS, "Unable to close session"); + ret = -1; + } + + t->session = NULL; + } + + return ret; +} + static int winhttp_connect( winhttp_subtransport *t, const char *url) { wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")"; - git_win32_path host; + wchar_t *wide_host; int32_t port; - const char *default_port = "80"; + int error = -1; + int default_timeout = TIMEOUT_INFINITE; + int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT; + + GIT_UNUSED(url); + + t->session = NULL; + t->connection = NULL; /* Prepare port */ if (git__strtol32(&port, t->connection_data.port, NULL, 10) < 0) return -1; /* Prepare host */ - git_win32_path_from_c(host, t->connection_data.host); + if (git__utf8_to_16_alloc(&wide_host, t->connection_data.host) < 0) { + giterr_set(GITERR_OS, "Unable to convert host to wide characters"); + return -1; + } /* Establish session */ t->session = WinHttpOpen( @@ -458,24 +586,101 @@ if (!t->session) { giterr_set(GITERR_OS, "Failed to init WinHTTP"); - return -1; + goto on_error; + } + + 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; } + /* Establish connection */ t->connection = WinHttpConnect( t->session, - host, + wide_host, (INTERNET_PORT) port, 0); if (!t->connection) { giterr_set(GITERR_OS, "Failed to connect to host"); - return -1; + goto on_error; + } + + error = 0; + +on_error: + if (error < 0) + winhttp_close_connection(t); + + git__free(wide_host); + + return error; +} + +static int do_send_request(winhttp_stream *s, size_t len, int ignore_length) +{ + if (ignore_length) { + if (!WinHttpSendRequest(s->request, + WINHTTP_NO_ADDITIONAL_HEADERS, 0, + WINHTTP_NO_REQUEST_DATA, 0, + WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0)) { + return -1; + } + } else { + if (!WinHttpSendRequest(s->request, + WINHTTP_NO_ADDITIONAL_HEADERS, 0, + WINHTTP_NO_REQUEST_DATA, 0, + len, 0)) { + return -1; + } } return 0; } +static int send_request(winhttp_stream *s, size_t len, int ignore_length) +{ + int request_failed = 0, cert_valid = 1, error = 0; + DWORD ignore_flags; + + if ((error = do_send_request(s, len, ignore_length)) < 0) + request_failed = 1; + + if (request_failed) { + if (GetLastError() != ERROR_WINHTTP_SECURE_FAILURE) { + giterr_set(GITERR_OS, "failed to send request"); + return -1; + } else { + cert_valid = 0; + } + } + + giterr_clear(); + if ((error = certificate_check(s, cert_valid)) < 0) { + if (!giterr_last()) + giterr_set(GITERR_OS, "user cancelled certificate check"); + + return error; + } + + /* if neither the request nor the certificate check returned errors, we're done */ + if (!request_failed) + return 0; + + ignore_flags = no_check_cert_flags; + + if (!WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS, &ignore_flags, sizeof(ignore_flags))) { + giterr_set(GITERR_OS, "failed to set security options"); + return -1; + } + + if ((error = do_send_request(s, len, ignore_length)) < 0) + giterr_set(GITERR_OS, "failed to send request"); + + return error; +} + static int winhttp_stream_read( git_smart_subtransport_stream *stream, char *buffer, @@ -486,6 +691,7 @@ winhttp_subtransport *t = OWNING_SUBTRANSPORT(s); DWORD dw_bytes_read; char replay_count = 0; + int error; replay: /* Enforce a reasonable cap on the number of replays */ @@ -504,13 +710,9 @@ wchar_t expected_content_type[MAX_CONTENT_TYPE_LEN], content_type[MAX_CONTENT_TYPE_LEN]; if (!s->sent_request) { - if (!WinHttpSendRequest(s->request, - WINHTTP_NO_ADDITIONAL_HEADERS, 0, - WINHTTP_NO_REQUEST_DATA, 0, - s->post_body_len, 0)) { - giterr_set(GITERR_OS, "Failed to send request"); - return -1; - } + + if ((error = send_request(s, s->post_body_len, 0)) < 0) + return error; s->sent_request = 1; } @@ -624,7 +826,6 @@ } location = git__malloc(location_length); - location8 = git__malloc(location_length); GITERR_CHECK_ALLOC(location); if (!WinHttpQueryHeaders(s->request, @@ -637,19 +838,30 @@ git__free(location); return -1; } - git__utf16_to_8(location8, location_length, location); + + /* Convert the Location header to UTF-8 */ + if (git__utf16_to_8_alloc(&location8, location) < 0) { + giterr_set(GITERR_OS, "Failed to convert Location header to UTF-8"); + git__free(location); + return -1; + } + git__free(location); /* Replay the request */ - WinHttpCloseHandle(s->request); - s->request = NULL; - s->sent_request = 0; + winhttp_stream_close(s); if (!git__prefixcmp_icase(location8, prefix_https)) { /* Upgrade to secure connection; disconnect and start over */ - if (gitno_connection_data_from_url(&t->connection_data, location8, s->service_url) < 0) + if (gitno_connection_data_from_url(&t->connection_data, location8, s->service_url) < 0) { + git__free(location8); + return -1; + } + + winhttp_close_connection(t); + + if (winhttp_connect(t, location8) < 0) return -1; - winhttp_connect(t, location8); } git__free(location8); @@ -657,8 +869,7 @@ } /* Handle authentication failures */ - if (HTTP_STATUS_DENIED == status_code && - get_verb == s->verb && t->owner->cred_acquire_cb) { + if (HTTP_STATUS_DENIED == status_code && get_verb == s->verb) { int allowed_types; if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0) @@ -666,19 +877,34 @@ if (allowed_types && (!t->cred || 0 == (t->cred->credtype & allowed_types))) { + int cred_error = 1; + + /* Start with the user-supplied credential callback, if present */ + if (t->owner->cred_acquire_cb) { + cred_error = t->owner->cred_acquire_cb(&t->cred, t->owner->url, + t->connection_data.user, allowed_types, t->owner->cred_acquire_payload); - if (t->owner->cred_acquire_cb(&t->cred, t->owner->url, t->connection_data.user, allowed_types, - t->owner->cred_acquire_payload) < 0) - return GIT_EUSER; - - assert(t->cred); - - WinHttpCloseHandle(s->request); - s->request = NULL; - s->sent_request = 0; + if (cred_error < 0) + return cred_error; + } + + /* Invoke the fallback credentials acquisition callback if necessary */ + if (cred_error > 0) { + cred_error = fallback_cred_acquire_cb(&t->cred, t->owner->url, + t->connection_data.user, allowed_types, NULL); + + if (cred_error < 0) + return cred_error; + } - /* Successfully acquired a credential */ - goto replay; + if (!cred_error) { + assert(t->cred); + + winhttp_stream_close(s); + + /* Successfully acquired a credential */ + goto replay; + } } } @@ -689,11 +915,15 @@ /* Verify that we got the correct content-type back */ if (post_verb == s->verb) - snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-result", s->service); + p_snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-result", s->service); else - snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-advertisement", s->service); + p_snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-advertisement", s->service); + + if (git__utf8_to_16(expected_content_type, MAX_CONTENT_TYPE_LEN, expected_content_type_8) < 0) { + giterr_set(GITERR_OS, "Failed to convert expected content-type to wide characters"); + return -1; + } - git__utf8_to_16(expected_content_type, MAX_CONTENT_TYPE_LEN, expected_content_type_8); content_type_length = sizeof(content_type); if (!WinHttpQueryHeaders(s->request, @@ -735,6 +965,7 @@ winhttp_stream *s = (winhttp_stream *)stream; winhttp_subtransport *t = OWNING_SUBTRANSPORT(s); DWORD bytes_written; + int error; if (!s->request && winhttp_stream_connect(s) < 0) return -1; @@ -745,13 +976,8 @@ return -1; } - if (!WinHttpSendRequest(s->request, - WINHTTP_NO_ADDITIONAL_HEADERS, 0, - WINHTTP_NO_REQUEST_DATA, 0, - (DWORD)len, 0)) { - giterr_set(GITERR_OS, "Failed to send request"); - return -1; - } + if ((error = send_request(s, len, 0)) < 0) + return error; s->sent_request = 1; @@ -874,6 +1100,7 @@ { winhttp_stream *s = (winhttp_stream *)stream; winhttp_subtransport *t = OWNING_SUBTRANSPORT(s); + int error; if (!s->request && winhttp_stream_connect(s) < 0) return -1; @@ -887,13 +1114,8 @@ return -1; } - if (!WinHttpSendRequest(s->request, - WINHTTP_NO_ADDITIONAL_HEADERS, 0, - WINHTTP_NO_REQUEST_DATA, 0, - WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, 0)) { - giterr_set(GITERR_OS, "Failed to send request"); - return -1; - } + if ((error = send_request(s, 0, 1)) < 0) + return error; s->sent_request = 1; } @@ -945,26 +1167,7 @@ { winhttp_stream *s = (winhttp_stream *)stream; - if (s->chunk_buffer) { - git__free(s->chunk_buffer); - s->chunk_buffer = NULL; - } - - if (s->post_body) { - CloseHandle(s->post_body); - s->post_body = NULL; - } - - if (s->request_uri) { - git__free(s->request_uri); - s->request_uri = NULL; - } - - if (s->request) { - WinHttpCloseHandle(s->request); - s->request = NULL; - } - + winhttp_stream_close(s); git__free(s); } @@ -1052,9 +1255,9 @@ int ret = -1; if (!t->connection) - if (gitno_connection_data_from_url(&t->connection_data, url, NULL) < 0 || - winhttp_connect(t, url) < 0) - return -1; + if ((ret = gitno_connection_data_from_url(&t->connection_data, url, NULL)) < 0 || + (ret = winhttp_connect(t, url)) < 0) + return ret; if (winhttp_stream_alloc(t, &s) < 0) return -1; @@ -1093,9 +1296,9 @@ static int winhttp_close(git_smart_subtransport *subtransport) { winhttp_subtransport *t = (winhttp_subtransport *)subtransport; - int ret = 0; gitno_connection_data_free_ptrs(&t->connection_data); + memset(&t->connection_data, 0x0, sizeof(gitno_connection_data)); if (t->cred) { t->cred->free(t->cred); @@ -1107,25 +1310,7 @@ t->url_cred = NULL; } - if (t->connection) { - if (!WinHttpCloseHandle(t->connection)) { - giterr_set(GITERR_OS, "Unable to close connection"); - ret = -1; - } - - t->connection = NULL; - } - - if (t->session) { - if (!WinHttpCloseHandle(t->session)) { - giterr_set(GITERR_OS, "Unable to close session"); - ret = -1; - } - - t->session = NULL; - } - - return ret; + return winhttp_close_connection(t); } static void winhttp_free(git_smart_subtransport *subtransport) diff -Nru libgit2-0.20.0/src/tree.c libgit2-0.22.2/src/tree.c --- libgit2-0.20.0/src/tree.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/tree.c 2015-03-24 16:10:45.000000000 +0000 @@ -17,6 +17,8 @@ #define DEFAULT_TREE_SIZE 16 #define MAX_FILEMODE_BYTES 6 +GIT__USE_STRMAP; + static bool valid_filemode(const int filemode) { return (filemode == GIT_FILEMODE_TREE @@ -48,14 +50,11 @@ return GIT_FILEMODE_BLOB; } -static int valid_entry_name(const char *filename) +static int valid_entry_name(git_repository *repo, const char *filename) { return *filename != '\0' && - strchr(filename, '/') == NULL && - (*filename != '.' || - (strcmp(filename, ".") != 0 && - strcmp(filename, "..") != 0 && - strcmp(filename, DOT_GIT) != 0)); + git_path_isvalid(repo, filename, + GIT_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT | GIT_PATH_REJECT_SLASH); } static int entry_sort_cmp(const void *a, const void *b) @@ -204,22 +203,22 @@ git__free(entry); } -git_tree_entry *git_tree_entry_dup(const git_tree_entry *entry) +int git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source) { size_t total_size; git_tree_entry *copy; - assert(entry); + assert(source); - total_size = sizeof(git_tree_entry) + entry->filename_len + 1; + total_size = sizeof(git_tree_entry) + source->filename_len + 1; copy = git__malloc(total_size); - if (!copy) - return NULL; + GITERR_CHECK_ALLOC(copy); - memcpy(copy, entry, total_size); + memcpy(copy, source, total_size); - return copy; + *dest = copy; + return 0; } void git_tree__free(void *_tree) @@ -283,7 +282,8 @@ { size_t idx; - assert(tree->entries.sorted); /* be safe when we cast away constness */ + /* be safe when we cast away constness - i.e. don't trigger a sort */ + assert(git_vector_is_sorted(&tree->entries)); if (tree_key_search(&idx, (git_vector *)&tree->entries, name, name_len) < 0) return NULL; @@ -305,8 +305,8 @@ return git_vector_get(&tree->entries, idx); } -const git_tree_entry *git_tree_entry_byoid( - const git_tree *tree, const git_oid *oid) +const git_tree_entry *git_tree_entry_byid( + const git_tree *tree, const git_oid *id) { size_t i; const git_tree_entry *e; @@ -314,7 +314,7 @@ assert(tree); git_vector_foreach(&tree->entries, i, e) { - if (memcmp(&e->oid.id, &oid->id, sizeof(oid->id)) == 0) + if (memcmp(&e->oid.id, &id->id, sizeof(id->id)) == 0) return e; } @@ -333,7 +333,8 @@ ksearch.filename = path; ksearch.filename_len = strlen(path); - assert(tree->entries.sorted); /* be safe when we cast away constness */ + /* be safe when we cast away constness - i.e. don't trigger a sort */ + assert(git_vector_is_sorted(&tree->entries)); /* Find tree entry with appropriate prefix */ git_vector_bsearch2( @@ -363,7 +364,8 @@ unsigned int git_treebuilder_entrycount(git_treebuilder *bld) { assert(bld); - return (unsigned int)bld->entrycount; + + return git_strmap_num_entries(bld->map); } static int tree_error(const char *str, const char *path) @@ -448,8 +450,9 @@ git_filemode_t filemode) { git_tree_entry *entry; + int error = 0; - if (!valid_entry_name(filename)) + if (!valid_entry_name(bld->repo, filename)) return tree_error("Failed to insert entry. Invalid name for a tree entry", filename); entry = alloc_entry(filename); @@ -458,12 +461,13 @@ git_oid_cpy(&entry->oid, id); entry->attr = (uint16_t)filemode; - if (git_vector_insert(&bld->entries, entry) < 0) { - git__free(entry); + git_strmap_insert(bld->map, entry->filename, entry, error); + if (error < 0) { + git_tree_entry_free(entry); + giterr_set(GITERR_TREE, "failed to append entry %s to the tree builder", filename); return -1; } - bld->entrycount++; return 0; } @@ -481,12 +485,12 @@ const git_tree_cache *cache; cache = git_tree_cache_get(index->tree, dirname); - if (cache != NULL && cache->entries >= 0){ + if (cache != NULL && cache->entry_count >= 0){ git_oid_cpy(oid, &cache->oid); return (int)find_next_dir(dirname, index, start); } - if ((error = git_treebuilder_create(&bld, NULL)) < 0 || bld == NULL) + if ((error = git_treebuilder_new(&bld, repo, NULL)) < 0 || bld == NULL) return -1; /* @@ -551,13 +555,13 @@ if (error < 0) goto on_error; } else { - error = append_entry(bld, filename, &entry->oid, entry->mode); + error = append_entry(bld, filename, &entry->id, entry->mode); if (error < 0) goto on_error; } } - if (git_treebuilder_write(oid, repo, bld) < 0) + if (git_treebuilder_write(oid, bld) < 0) goto on_error; git_treebuilder_free(bld); @@ -572,6 +576,7 @@ git_oid *oid, git_index *index, git_repository *repo) { int ret; + git_tree *tree; bool old_ignore_case = false; assert(oid && index && repo); @@ -582,7 +587,7 @@ return GIT_EUNMERGED; } - if (index->tree != NULL && index->tree->entries >= 0) { + if (index->tree != NULL && index->tree->entry_count >= 0) { git_oid_cpy(oid, &index->tree->oid); return 0; } @@ -602,24 +607,42 @@ if (old_ignore_case) git_index__set_ignore_case(index, true); - return ret < 0 ? ret : 0; + index->tree = NULL; + + if (ret < 0) + return ret; + + git_pool_clear(&index->tree_pool); + + if ((ret = git_tree_lookup(&tree, repo, oid)) < 0) + return ret; + + /* Read the tree cache into the index */ + ret = git_tree_cache_read_tree(&index->tree, tree, &index->tree_pool); + git_tree_free(tree); + + return ret; } -int git_treebuilder_create(git_treebuilder **builder_p, const git_tree *source) +int git_treebuilder_new( + git_treebuilder **builder_p, + git_repository *repo, + const git_tree *source) { git_treebuilder *bld; - size_t i, source_entries = DEFAULT_TREE_SIZE; + size_t i; - assert(builder_p); + assert(builder_p && repo); bld = git__calloc(1, sizeof(git_treebuilder)); GITERR_CHECK_ALLOC(bld); - if (source != NULL) - source_entries = source->entries.length; + bld->repo = repo; - if (git_vector_init(&bld->entries, source_entries, entry_sort_cmp) < 0) - goto on_error; + if (git_strmap_alloc(&bld->map) < 0) { + git__free(bld); + return -1; + } if (source != NULL) { git_tree_entry *entry_src; @@ -649,32 +672,31 @@ git_filemode_t filemode) { git_tree_entry *entry; - size_t pos; + int error; + git_strmap_iter pos; assert(bld && id && filename); if (!valid_filemode(filemode)) return tree_error("Failed to insert entry. Invalid filemode for file", filename); - if (!valid_entry_name(filename)) + if (!valid_entry_name(bld->repo, filename)) return tree_error("Failed to insert entry. Invalid name for a tree entry", filename); - if (!tree_key_search(&pos, &bld->entries, filename, strlen(filename))) { - entry = git_vector_get(&bld->entries, pos); - if (entry->removed) { - entry->removed = 0; - bld->entrycount++; - } + pos = git_strmap_lookup_index(bld->map, filename); + if (git_strmap_valid_index(bld->map, pos)) { + entry = git_strmap_value_at(bld->map, pos); } else { entry = alloc_entry(filename); GITERR_CHECK_ALLOC(entry); - if (git_vector_insert(&bld->entries, entry) < 0) { - git__free(entry); + git_strmap_insert(bld->map, entry->filename, entry, error); + + if (error < 0) { + git_tree_entry_free(entry); + giterr_set(GITERR_TREE, "failed to insert %s", filename); return -1; } - - bld->entrycount++; } git_oid_cpy(&entry->oid, id); @@ -688,17 +710,14 @@ static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filename) { - size_t idx; - git_tree_entry *entry; + git_tree_entry *entry = NULL; + git_strmap_iter pos; assert(bld && filename); - if (tree_key_search(&idx, &bld->entries, filename, strlen(filename)) < 0) - return NULL; - - entry = git_vector_get(&bld->entries, idx); - if (entry->removed) - return NULL; + pos = git_strmap_lookup_index(bld->map, filename); + if (git_strmap_valid_index(bld->map, pos)) + entry = git_strmap_value_at(bld->map, pos); return entry; } @@ -710,35 +729,44 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename) { - git_tree_entry *remove_ptr = treebuilder_get(bld, filename); + git_tree_entry *entry = treebuilder_get(bld, filename); - if (remove_ptr == NULL || remove_ptr->removed) + if (entry == NULL) return tree_error("Failed to remove entry. File isn't in the tree", filename); - remove_ptr->removed = 1; - bld->entrycount--; + git_strmap_delete(bld->map, filename); + git_tree_entry_free(entry); + return 0; } -int git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld) +int git_treebuilder_write(git_oid *oid, git_treebuilder *bld) { int error = 0; - size_t i; + size_t i, entrycount; git_buf tree = GIT_BUF_INIT; git_odb *odb; + git_tree_entry *entry; + git_vector entries; assert(bld); - git_vector_sort(&bld->entries); + entrycount = git_strmap_num_entries(bld->map); + if (git_vector_init(&entries, entrycount, entry_sort_cmp) < 0) + return -1; - /* Grow the buffer beforehand to an estimated size */ - error = git_buf_grow(&tree, bld->entries.length * 72); + git_strmap_foreach_value(bld->map, entry, { + if (git_vector_insert(&entries, entry) < 0) + return -1; + }); + + git_vector_sort(&entries); - for (i = 0; i < bld->entries.length && !error; ++i) { - git_tree_entry *entry = git_vector_get(&bld->entries, i); + /* Grow the buffer beforehand to an estimated size */ + error = git_buf_grow(&tree, entrycount * 72); - if (entry->removed) - continue; + for (i = 0; i < entries.length && !error; ++i) { + git_tree_entry *entry = git_vector_get(&entries, i); git_buf_printf(&tree, "%o ", entry->attr); git_buf_put(&tree, entry->filename, entry->filename_len + 1); @@ -748,8 +776,10 @@ error = -1; } + git_vector_free(&entries); + if (!error && - !(error = git_repository_odb__weakptr(&odb, repo))) + !(error = git_repository_odb__weakptr(&odb, bld->repo))) error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE); git_buf_free(&tree); @@ -761,31 +791,27 @@ git_treebuilder_filter_cb filter, void *payload) { - size_t i; + const char *filename; git_tree_entry *entry; assert(bld && filter); - git_vector_foreach(&bld->entries, i, entry) { - if (!entry->removed && filter(entry, payload)) { - entry->removed = 1; - bld->entrycount--; - } - } + git_strmap_foreach(bld->map, filename, entry, { + if (filter(entry, payload)) { + git_strmap_delete(bld->map, filename); + git_tree_entry_free(entry); + } + }); } void git_treebuilder_clear(git_treebuilder *bld) { - size_t i; git_tree_entry *e; assert(bld); - git_vector_foreach(&bld->entries, i, e) - git_tree_entry_free(e); - - git_vector_clear(&bld->entries); - bld->entrycount = 0; + git_strmap_foreach_value(bld->map, e, git_tree_entry_free(e)); + git_strmap_clear(bld->map); } void git_treebuilder_free(git_treebuilder *bld) @@ -794,7 +820,7 @@ return; git_treebuilder_clear(bld); - git_vector_free(&bld->entries); + git_strmap_free(bld->map); git__free(bld); } @@ -853,8 +879,7 @@ case '\0': /* If there are no more components in the path, return * this entry */ - *entry_out = git_tree_entry_dup(entry); - return 0; + return git_tree_entry_dup(entry_out, entry); } if (git_tree_lookup(&subtree, root->object.repo, &entry->oid) < 0) @@ -884,22 +909,22 @@ git_vector_foreach(&tree->entries, i, entry) { if (preorder) { error = callback(path->ptr, entry, payload); - if (error > 0) { + if (error < 0) { /* negative value stops iteration */ + giterr_set_after_callback_function(error, "git_tree_walk"); + break; + } + if (error > 0) { /* positive value skips this entry */ error = 0; continue; } - if (error < 0) { - giterr_clear(); - return GIT_EUSER; - } } if (git_tree_entry__is_tree(entry)) { git_tree *subtree; size_t path_len = git_buf_len(path); - if ((error = git_tree_lookup( - &subtree, tree->object.repo, &entry->oid)) < 0) + error = git_tree_lookup(&subtree, tree->object.repo, &entry->oid); + if (error < 0) break; /* append the next entry to the path */ @@ -907,21 +932,24 @@ git_buf_putc(path, '/'); if (git_buf_oom(path)) - return -1; + error = -1; + else + error = tree_walk(subtree, callback, path, payload, preorder); - error = tree_walk(subtree, callback, path, payload, preorder); git_tree_free(subtree); - if (error != 0) break; git_buf_truncate(path, path_len); } - if (!preorder && callback(path->ptr, entry, payload) < 0) { - giterr_clear(); - error = GIT_EUSER; - break; + if (!preorder) { + error = callback(path->ptr, entry, payload); + if (error < 0) { /* negative value stops iteration */ + giterr_set_after_callback_function(error, "git_tree_walk"); + break; + } + error = 0; } } diff -Nru libgit2-0.20.0/src/tree-cache.c libgit2-0.22.2/src/tree-cache.c --- libgit2-0.20.0/src/tree-cache.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/tree-cache.c 2015-03-24 16:10:45.000000000 +0000 @@ -6,24 +6,19 @@ */ #include "tree-cache.h" +#include "pool.h" +#include "tree.h" -static git_tree_cache *find_child(const git_tree_cache *tree, const char *path) +static git_tree_cache *find_child( + const git_tree_cache *tree, const char *path, const char *end) { - size_t i, dirlen; - const char *end; - - end = strchr(path, '/'); - if (end == NULL) { - end = strrchr(path, '\0'); - } - - dirlen = end - path; + size_t i, dirlen = end ? (size_t)(end - path) : strlen(path); for (i = 0; i < tree->children_count; ++i) { - const char *childname = tree->children[i]->name; + git_tree_cache *child = tree->children[i]; - if (strlen(childname) == dirlen && !memcmp(path, childname, dirlen)) - return tree->children[i]; + if (child->namelen == dirlen && !memcmp(path, child->name, dirlen)) + return child; } return NULL; @@ -36,7 +31,7 @@ if (tree == NULL) return; - tree->entries = -1; + tree->entry_count = -1; while (ptr != NULL) { end = strchr(ptr, '/'); @@ -44,11 +39,11 @@ if (end == NULL) /* End of path */ break; - tree = find_child(tree, ptr); + tree = find_child(tree, ptr, end); if (tree == NULL) /* We don't have that tree */ return; - tree->entries = -1; + tree->entry_count = -1; ptr = end + 1; } } @@ -64,10 +59,9 @@ while (1) { end = strchr(ptr, '/'); - tree = find_child(tree, ptr); - if (tree == NULL) { /* Can't find it */ + tree = find_child(tree, ptr, end); + if (tree == NULL) /* Can't find it */ return NULL; - } if (end == NULL || *end + 1 == '\0') return tree; @@ -77,12 +71,12 @@ } static int read_tree_internal(git_tree_cache **out, - const char **buffer_in, const char *buffer_end, git_tree_cache *parent) + const char **buffer_in, const char *buffer_end, + git_pool *pool) { git_tree_cache *tree = NULL; const char *name_start, *buffer; int count; - size_t name_len; buffer = name_start = *buffer_in; @@ -92,22 +86,14 @@ if (++buffer >= buffer_end) goto corrupted; - name_len = strlen(name_start); - tree = git__malloc(sizeof(git_tree_cache) + name_len + 1); - GITERR_CHECK_ALLOC(tree); - - memset(tree, 0x0, sizeof(git_tree_cache)); - tree->parent = parent; - - /* NUL-terminated tree name */ - memcpy(tree->name, name_start, name_len); - tree->name[name_len] = '\0'; + if (git_tree_cache_new(&tree, name_start, pool) < 0) + return -1; /* Blank-terminated ASCII decimal number of entries in this tree */ if (git__strtol32(&count, buffer, &buffer, 10) < 0) goto corrupted; - tree->entries = count; + tree->entry_count = count; if (*buffer != ' ' || ++buffer >= buffer_end) goto corrupted; @@ -122,7 +108,7 @@ goto corrupted; /* The SHA1 is only there if it's not invalidated */ - if (tree->entries >= 0) { + if (tree->entry_count >= 0) { /* 160-bit SHA-1 for this tree and it's children */ if (buffer + GIT_OID_RAWSZ > buffer_end) goto corrupted; @@ -135,13 +121,13 @@ if (tree->children_count > 0) { unsigned int i; - tree->children = git__malloc(tree->children_count * sizeof(git_tree_cache *)); + tree->children = git_pool_malloc(pool, tree->children_count * sizeof(git_tree_cache *)); GITERR_CHECK_ALLOC(tree->children); memset(tree->children, 0x0, tree->children_count * sizeof(git_tree_cache *)); for (i = 0; i < tree->children_count; ++i) { - if (read_tree_internal(&tree->children[i], &buffer, buffer_end, tree) < 0) + if (read_tree_internal(&tree->children[i], &buffer, buffer_end, pool) < 0) goto corrupted; } } @@ -151,16 +137,15 @@ return 0; corrupted: - git_tree_cache_free(tree); giterr_set(GITERR_INDEX, "Corrupted TREE extension in index"); return -1; } -int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size) +int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size, git_pool *pool) { const char *buffer_end = buffer + buffer_size; - if (read_tree_internal(tree, &buffer, buffer_end, NULL) < 0) + if (read_tree_internal(tree, &buffer, buffer_end, pool) < 0) return -1; if (buffer < buffer_end) { @@ -171,19 +156,114 @@ return 0; } -void git_tree_cache_free(git_tree_cache *tree) +static int read_tree_recursive(git_tree_cache *cache, const git_tree *tree, git_pool *pool) { - unsigned int i; + git_repository *repo; + size_t i, j, nentries, ntrees; + int error; + + repo = git_tree_owner(tree); + + git_oid_cpy(&cache->oid, git_tree_id(tree)); + nentries = git_tree_entrycount(tree); + + /* + * We make sure we know how many trees we need to allocate for + * so we don't have to realloc and change the pointers for the + * parents. + */ + ntrees = 0; + for (i = 0; i < nentries; i++) { + const git_tree_entry *entry; + + entry = git_tree_entry_byindex(tree, i); + if (git_tree_entry_filemode(entry) == GIT_FILEMODE_TREE) + ntrees++; + } - if (tree == NULL) - return; + cache->children_count = ntrees; + cache->children = git_pool_mallocz(pool, ntrees * sizeof(git_tree_cache *)); + GITERR_CHECK_ALLOC(cache->children); + + j = 0; + for (i = 0; i < nentries; i++) { + const git_tree_entry *entry; + git_tree *subtree; + + entry = git_tree_entry_byindex(tree, i); + if (git_tree_entry_filemode(entry) != GIT_FILEMODE_TREE) { + cache->entry_count++; + continue; + } - if (tree->children != NULL) { - for (i = 0; i < tree->children_count; ++i) - git_tree_cache_free(tree->children[i]); + if ((error = git_tree_cache_new(&cache->children[j], git_tree_entry_name(entry), pool)) < 0) + return error; - git__free(tree->children); + if ((error = git_tree_lookup(&subtree, repo, git_tree_entry_id(entry))) < 0) + return error; + + error = read_tree_recursive(cache->children[j], subtree, pool); + git_tree_free(subtree); + cache->entry_count += cache->children[j]->entry_count; + j++; + + if (error < 0) + return error; } - git__free(tree); + return 0; +} + +int git_tree_cache_read_tree(git_tree_cache **out, const git_tree *tree, git_pool *pool) +{ + int error; + git_tree_cache *cache; + + if ((error = git_tree_cache_new(&cache, "", pool)) < 0) + return error; + + if ((error = read_tree_recursive(cache, tree, pool)) < 0) + return error; + + *out = cache; + return 0; +} + +int git_tree_cache_new(git_tree_cache **out, const char *name, git_pool *pool) +{ + size_t name_len; + git_tree_cache *tree; + + name_len = strlen(name); + tree = git_pool_malloc(pool, sizeof(git_tree_cache) + name_len + 1); + GITERR_CHECK_ALLOC(tree); + + memset(tree, 0x0, sizeof(git_tree_cache)); + /* NUL-terminated tree name */ + tree->namelen = name_len; + memcpy(tree->name, name, name_len); + tree->name[name_len] = '\0'; + + *out = tree; + return 0; +} + +static void write_tree(git_buf *out, git_tree_cache *tree) +{ + size_t i; + + git_buf_printf(out, "%s%c%"PRIdZ" %"PRIuZ"\n", tree->name, 0, tree->entry_count, tree->children_count); + + if (tree->entry_count != -1) + git_buf_put(out, (const char *) &tree->oid, GIT_OID_RAWSZ); + + for (i = 0; i < tree->children_count; i++) + write_tree(out, tree->children[i]); +} + +int git_tree_cache_write(git_buf *out, git_tree_cache *tree) +{ + write_tree(out, tree); + + return git_buf_oom(out) ? -1 : 0; } diff -Nru libgit2-0.20.0/src/tree-cache.h libgit2-0.22.2/src/tree-cache.h --- libgit2-0.20.0/src/tree-cache.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/tree-cache.h 2015-03-24 16:10:45.000000000 +0000 @@ -9,23 +9,29 @@ #define INCLUDE_tree_cache_h__ #include "common.h" +#include "pool.h" +#include "buffer.h" #include "git2/oid.h" -struct git_tree_cache { - struct git_tree_cache *parent; +typedef struct git_tree_cache { struct git_tree_cache **children; size_t children_count; - ssize_t entries; + ssize_t entry_count; git_oid oid; + size_t namelen; char name[GIT_FLEX_ARRAY]; -}; +} git_tree_cache; -typedef struct git_tree_cache git_tree_cache; - -int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size); +int git_tree_cache_write(git_buf *out, git_tree_cache *tree); +int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size, git_pool *pool); void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path); const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char *path); +int git_tree_cache_new(git_tree_cache **out, const char *name, git_pool *pool); +/** + * Read a tree as the root of the tree cache (like for `git read-tree`) + */ +int git_tree_cache_read_tree(git_tree_cache **out, const git_tree *tree, git_pool *pool); void git_tree_cache_free(git_tree_cache *tree); #endif diff -Nru libgit2-0.20.0/src/tree.h libgit2-0.22.2/src/tree.h --- libgit2-0.20.0/src/tree.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/tree.h 2015-03-24 16:10:45.000000000 +0000 @@ -11,9 +11,9 @@ #include "repository.h" #include "odb.h" #include "vector.h" +#include "strmap.h" struct git_tree_entry { - uint16_t removed; uint16_t attr; git_oid oid; size_t filename_len; @@ -26,8 +26,8 @@ }; struct git_treebuilder { - git_vector entries; - size_t entrycount; /* vector may contain "removed" entries */ + git_repository *repo; + git_strmap *map; }; GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e) diff -Nru libgit2-0.20.0/src/unix/map.c libgit2-0.22.2/src/unix/map.c --- libgit2-0.20.0/src/unix/map.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/unix/map.c 2015-03-24 16:10:45.000000000 +0000 @@ -6,15 +6,27 @@ */ #include -#ifndef GIT_WIN32 +#if !defined(GIT_WIN32) && !defined(NO_MMAP) #include "map.h" #include +#include #include +int git__page_size(size_t *page_size) +{ + long sc_page_size = sysconf(_SC_PAGE_SIZE); + if (sc_page_size < 0) { + giterr_set_str(GITERR_OS, "Can't determine system page size"); + return -1; + } + *page_size = (size_t) sc_page_size; + return 0; +} + int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset) { - int mprot = 0; + int mprot = PROT_READ; int mflag = 0; GIT_MMAP_VALIDATE(out, len, prot, flags); @@ -23,9 +35,7 @@ out->len = 0; if (prot & GIT_PROT_WRITE) - mprot = PROT_WRITE; - else if (prot & GIT_PROT_READ) - mprot = PROT_READ; + mprot |= PROT_WRITE; if ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED) mflag = MAP_SHARED; diff -Nru libgit2-0.20.0/src/unix/posix.h libgit2-0.22.2/src/unix/posix.h --- libgit2-0.20.0/src/unix/posix.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/unix/posix.h 2015-03-24 16:10:45.000000000 +0000 @@ -4,34 +4,47 @@ * 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_posix__unix_h__ +#define INCLUDE_posix__unix_h__ #include #include +typedef int GIT_SOCKET; +#define INVALID_SOCKET -1 + +#define p_lseek(f,n,w) lseek(f, n, w) +#define p_fstat(f,b) fstat(f, b) #define p_lstat(p,b) lstat(p,b) +#define p_stat(p,b) stat(p, b) + #define p_readlink(a, b, c) readlink(a, b, c) #define p_symlink(o,n) symlink(o, n) #define p_link(o,n) link(o, n) #define p_unlink(p) unlink(p) #define p_mkdir(p,m) mkdir(p, m) #define p_fsync(fd) fsync(fd) +extern char *p_realpath(const char *, char *); -/* The OpenBSD realpath function behaves differently */ -#if !defined(__OpenBSD__) -# define p_realpath(p, po) realpath(p, po) -#else -char *p_realpath(const char *, char *); -#endif +#define p_recv(s,b,l,f) recv(s,b,l,f) +#define p_send(s,b,l,f) send(s,b,l,f) +#define p_inet_pton(a, b, c) inet_pton(a, b, c) +#define p_strcasecmp(s1, s2) strcasecmp(s1, s2) +#define p_strncasecmp(s1, s2, c) strncasecmp(s1, s2, c) #define p_vsnprintf(b, c, f, a) vsnprintf(b, c, f, a) #define p_snprintf(b, c, f, ...) snprintf(b, c, f, __VA_ARGS__) #define p_mkstemp(p) mkstemp(p) -#define p_setenv(n,v,o) setenv(n,v,o) -#define p_inet_pton(a, b, c) inet_pton(a, b, c) +#define p_chdir(p) chdir(p) +#define p_chmod(p,m) chmod(p, m) +#define p_rmdir(p) rmdir(p) +#define p_access(p,m) access(p,m) +#define p_ftruncate(fd, sz) ftruncate(fd, sz) /* see win32/posix.h for explanation about why this exists */ #define p_lstat_posixly(p,b) lstat(p,b) +#define p_localtime_r(c, r) localtime_r(c, r) +#define p_gmtime_r(c, r) gmtime_r(c, r) + #endif diff -Nru libgit2-0.20.0/src/unix/realpath.c libgit2-0.22.2/src/unix/realpath.c --- libgit2-0.20.0/src/unix/realpath.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/unix/realpath.c 2015-03-24 16:10:45.000000000 +0000 @@ -6,7 +6,7 @@ */ #include -#ifdef __OpenBSD__ +#ifndef GIT_WIN32 #include #include @@ -16,15 +16,16 @@ char *p_realpath(const char *pathname, char *resolved) { char *ret; - if ((ret = realpath(pathname, resolved)) == NULL) return NULL; - /* Figure out if the file exists */ - if (!access(ret, F_OK)) - return ret; - - return NULL; +#ifdef __OpenBSD__ + /* The OpenBSD realpath function behaves differently, + * figure out if the file exists */ + if (access(ret, F_OK) < 0) + ret = NULL; +#endif + return ret; } #endif diff -Nru libgit2-0.20.0/src/userdiff.h libgit2-0.22.2/src/userdiff.h --- libgit2-0.20.0/src/userdiff.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/userdiff.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,208 @@ +/* + * 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_userdiff_h__ +#define INCLUDE_userdiff_h__ + +/* + * This file isolates the built in diff driver function name patterns. + * Most of these patterns are taken from Git (with permission from the + * original authors for relicensing to libgit2). + */ + +typedef struct { + const char *name; + const char *fns; + const char *words; + int flags; +} git_diff_driver_definition; + +#define WORD_DEFAULT "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+" + +/* + * These builtin driver definition macros have same signature as in core + * git userdiff.c so that the data can be extracted verbatim + */ +#define PATTERNS(NAME, FN_PATS, WORD_PAT) \ + { NAME, FN_PATS, WORD_PAT WORD_DEFAULT, 0 } +#define IPATTERN(NAME, FN_PATS, WORD_PAT) \ + { NAME, FN_PATS, WORD_PAT WORD_DEFAULT, REG_ICASE } + +/* + * The table of diff driver patterns + * + * Function name patterns are a list of newline separated patterns that + * match a function declaration (i.e. the line you want in the hunk header), + * or a negative pattern prefixed with a '!' to reject a pattern (such as + * rejecting goto labels in C code). + * + * Word boundary patterns are just a simple pattern that will be OR'ed with + * the default value above (i.e. whitespace or non-ASCII characters). + */ +static git_diff_driver_definition builtin_defs[] = { + +IPATTERN("ada", + "!^(.*[ \t])?(is[ \t]+new|renames|is[ \t]+separate)([ \t].*)?$\n" + "!^[ \t]*with[ \t].*$\n" + "^[ \t]*((procedure|function)[ \t]+.*)$\n" + "^[ \t]*((package|protected|task)[ \t]+.*)$", + /* -- */ + "[a-zA-Z][a-zA-Z0-9_]*" + "|[-+]?[0-9][0-9#_.aAbBcCdDeEfF]*([eE][+-]?[0-9_]+)?" + "|=>|\\.\\.|\\*\\*|:=|/=|>=|<=|<<|>>|<>"), + +IPATTERN("fortran", + "!^([C*]|[ \t]*!)\n" + "!^[ \t]*MODULE[ \t]+PROCEDURE[ \t]\n" + "^[ \t]*((END[ \t]+)?(PROGRAM|MODULE|BLOCK[ \t]+DATA" + "|([^'\" \t]+[ \t]+)*(SUBROUTINE|FUNCTION))[ \t]+[A-Z].*)$", + /* -- */ + "[a-zA-Z][a-zA-Z0-9_]*" + "|\\.([Ee][Qq]|[Nn][Ee]|[Gg][TtEe]|[Ll][TtEe]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|[Aa][Nn][Dd]|[Oo][Rr]|[Nn]?[Ee][Qq][Vv]|[Nn][Oo][Tt])\\." + /* numbers and format statements like 2E14.4, or ES12.6, 9X. + * Don't worry about format statements without leading digits since + * they would have been matched above as a variable anyway. */ + "|[-+]?[0-9.]+([AaIiDdEeFfLlTtXx][Ss]?[-+]?[0-9.]*)?(_[a-zA-Z0-9][a-zA-Z0-9_]*)?" + "|//|\\*\\*|::|[/<>=]="), + +PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$", + "[^<>= \t]+"), + +PATTERNS("java", + "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n" + "^[ \t]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?" + "|[-+*/<>%&^|=!]=" + "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"), + +PATTERNS("matlab", + "^[[:space:]]*((classdef|function)[[:space:]].*)$|^%%[[:space:]].*$", + "[a-zA-Z_][a-zA-Z0-9_]*|[-+0-9.e]+|[=~<>]=|\\.[*/\\^']|\\|\\||&&"), + +PATTERNS("objc", + /* Negate C statements that can look like functions */ + "!^[ \t]*(do|for|if|else|return|switch|while)\n" + /* Objective-C methods */ + "^[ \t]*([-+][ \t]*\\([ \t]*[A-Za-z_][A-Za-z_0-9* \t]*\\)[ \t]*[A-Za-z_].*)$\n" + /* C functions */ + "^[ \t]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$\n" + /* Objective-C class/protocol definitions */ + "^(@(implementation|interface|protocol)[ \t].*)$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?" + "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"), + +PATTERNS("pascal", + "^(((class[ \t]+)?(procedure|function)|constructor|destructor|interface|" + "implementation|initialization|finalization)[ \t]*.*)$" + "\n" + "^(.*=[ \t]*(class|record).*)$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+" + "|<>|<=|>=|:=|\\.\\."), + +PATTERNS("perl", + "^package .*\n" + "^sub [[:alnum:]_':]+[ \t]*" + "(\\([^)]*\\)[ \t]*)?" /* prototype */ + /* + * Attributes. A regex can't count nested parentheses, + * so just slurp up whatever we see, taking care not + * to accept lines like "sub foo; # defined elsewhere". + * + * An attribute could contain a semicolon, but at that + * point it seems reasonable enough to give up. + */ + "(:[^;#]*)?" + "(\\{[ \t]*)?" /* brace can come here or on the next line */ + "(#.*)?$\n" /* comment */ + "^(BEGIN|END|INIT|CHECK|UNITCHECK|AUTOLOAD|DESTROY)[ \t]*" + "(\\{[ \t]*)?" /* brace can come here or on the next line */ + "(#.*)?$\n" + "^=head[0-9] .*", /* POD */ + /* -- */ + "[[:alpha:]_'][[:alnum:]_']*" + "|0[xb]?[0-9a-fA-F_]*" + /* taking care not to interpret 3..5 as (3.)(.5) */ + "|[0-9a-fA-F_]+(\\.[0-9a-fA-F_]+)?([eE][-+]?[0-9_]+)?" + "|=>|-[rwxoRWXOezsfdlpSugkbctTBMAC>]|~~|::" + "|&&=|\\|\\|=|//=|\\*\\*=" + "|&&|\\|\\||//|\\+\\+|--|\\*\\*|\\.\\.\\.?" + "|[-+*/%.^&<>=!|]=" + "|=~|!~" + "|<<|<>|<=>|>>"), + +PATTERNS("python", "^[ \t]*((class|def)[ \t].*)$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?" + "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"), + +PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$", + /* -- */ + "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?." + "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~"), + +PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$", + "[={}\"]|[^={}\" \t]+"), + +PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$", + "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+"), + +PATTERNS("cpp", + /* Jump targets or access declarations */ + "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\n" + /* functions/methods, variables, and compounds at top level */ + "^((::[[:space:]]*)?[A-Za-z_].*)$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*" + "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*"), + +PATTERNS("csharp", + /* Keywords */ + "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n" + /* Methods and constructors */ + "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n" + /* Properties */ + "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n" + /* Type definitions */ + "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct)[ \t]+.*)$\n" + /* Namespace */ + "^[ \t]*(namespace[ \t]+.*)$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?" + "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"), + +PATTERNS("php", + "^[ \t]*(((public|private|protected|static|final)[ \t]+)*((class|function)[ \t].*))$", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[fFlL]?|0[xX]?[0-9a-fA-F]+[lL]?" + "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"), + +PATTERNS("javascript", + "([a-zA-Z_$][a-zA-Z0-9_$]*(\\.[a-zA-Z0-9_$]+)*[ \t]*=[ \t]*function([ \t][a-zA-Z_$][a-zA-Z0-9_$]*)?[^\\{]*)\n" + "([a-zA-Z_$][a-zA-Z0-9_$]*[ \t]*:[ \t]*function([ \t][a-zA-Z_$][a-zA-Z0-9_$]*)?[^\\{]*)\n" + "[^a-zA-Z0-9_\\$](function([ \t][a-zA-Z_$][a-zA-Z0-9_$]*)?[^\\{]*)", + /* -- */ + "[a-zA-Z_][a-zA-Z0-9_]*" + "|[-+0-9.e]+[fFlL]?|0[xX]?[0-9a-fA-F]+[lL]?" + "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"), +}; + +#undef IPATTERN +#undef PATTERNS +#undef WORD_DEFAULT + +#endif + diff -Nru libgit2-0.20.0/src/util.c libgit2-0.22.2/src/util.c --- libgit2-0.20.0/src/util.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/util.c 2015-03-24 16:10:45.000000000 +0000 @@ -6,140 +6,21 @@ */ #include #include "common.h" -#include #include #include #include "posix.h" -#include "fileops.h" -#include "cache.h" #ifdef _MSC_VER # include #endif -void git_libgit2_version(int *major, int *minor, int *rev) -{ - *major = LIBGIT2_VER_MAJOR; - *minor = LIBGIT2_VER_MINOR; - *rev = LIBGIT2_VER_REVISION; -} - -int git_libgit2_capabilities() -{ - return 0 -#ifdef GIT_THREADS - | GIT_CAP_THREADS -#endif -#if defined(GIT_SSL) || defined(GIT_WINHTTP) - | GIT_CAP_HTTPS -#endif -#if defined(GIT_SSH) - | GIT_CAP_SSH -#endif - ; -} - -/* Declarations for tuneable settings */ -extern size_t git_mwindow__window_size; -extern size_t git_mwindow__mapped_limit; - -static int config_level_to_futils_dir(int config_level) -{ - int val = -1; - - switch (config_level) { - case GIT_CONFIG_LEVEL_SYSTEM: val = GIT_FUTILS_DIR_SYSTEM; break; - case GIT_CONFIG_LEVEL_XDG: val = GIT_FUTILS_DIR_XDG; break; - case GIT_CONFIG_LEVEL_GLOBAL: val = GIT_FUTILS_DIR_GLOBAL; break; - default: - giterr_set( - GITERR_INVALID, "Invalid config path selector %d", config_level); - } - - return val; -} - -int git_libgit2_opts(int key, ...) -{ - int error = 0; - va_list ap; - - va_start(ap, key); - - switch (key) { - case GIT_OPT_SET_MWINDOW_SIZE: - git_mwindow__window_size = va_arg(ap, size_t); - break; - - case GIT_OPT_GET_MWINDOW_SIZE: - *(va_arg(ap, size_t *)) = git_mwindow__window_size; - break; - - case GIT_OPT_SET_MWINDOW_MAPPED_LIMIT: - git_mwindow__mapped_limit = va_arg(ap, size_t); - break; - - case GIT_OPT_GET_MWINDOW_MAPPED_LIMIT: - *(va_arg(ap, size_t *)) = git_mwindow__mapped_limit; - break; - - case GIT_OPT_GET_SEARCH_PATH: - if ((error = config_level_to_futils_dir(va_arg(ap, int))) >= 0) { - char *out = va_arg(ap, char *); - size_t outlen = va_arg(ap, size_t); - - error = git_futils_dirs_get_str(out, outlen, error); - } - break; - - case GIT_OPT_SET_SEARCH_PATH: - if ((error = config_level_to_futils_dir(va_arg(ap, int))) >= 0) - error = git_futils_dirs_set(error, va_arg(ap, const char *)); - break; - - case GIT_OPT_SET_CACHE_OBJECT_LIMIT: - { - git_otype type = (git_otype)va_arg(ap, int); - size_t size = va_arg(ap, size_t); - error = git_cache_set_max_object_size(type, size); - break; - } - - case GIT_OPT_SET_CACHE_MAX_SIZE: - git_cache__max_storage = va_arg(ap, ssize_t); - break; - - case GIT_OPT_ENABLE_CACHING: - git_cache__enabled = (va_arg(ap, int) != 0); - break; - - case GIT_OPT_GET_CACHED_MEMORY: - *(va_arg(ap, ssize_t *)) = git_cache__current_storage.val; - *(va_arg(ap, ssize_t *)) = git_cache__max_storage; - break; - - case GIT_OPT_GET_TEMPLATE_PATH: - { - char *out = va_arg(ap, char *); - size_t outlen = va_arg(ap, size_t); - - error = git_futils_dirs_get_str(out, outlen, GIT_FUTILS_DIR_TEMPLATE); - } - break; - - case GIT_OPT_SET_TEMPLATE_PATH: - error = git_futils_dirs_set(GIT_FUTILS_DIR_TEMPLATE, va_arg(ap, const char *)); - break; - } - - va_end(ap); - - return error; -} - void git_strarray_free(git_strarray *array) { size_t i; + + if (array == NULL) + return; + for (i = 0; i < array->count; ++i) git__free(array->strings[i]); @@ -369,6 +250,21 @@ return strncasecmp(str, prefix, strlen(prefix)); } +int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix) +{ + int s, p; + + while(str_n--) { + s = (unsigned char)tolower(*str++); + p = (unsigned char)tolower(*prefix++); + + if (s != p) + return s - p; + } + + return (0 - *prefix); +} + int git__suffixcmp(const char *str, const char *suffix) { size_t a = strlen(str); @@ -661,10 +557,12 @@ */ int git__strcmp_cb(const void *a, const void *b) { - const char *stra = (const char *)a; - const char *strb = (const char *)b; + return strcmp((const char *)a, (const char *)b); +} - return strcmp(stra, strb); +int git__strcasecmp_cb(const void *a, const void *b) +{ + return strcasecmp((const char *)a, (const char *)b); } int git__parse_bool(int *out, const char *value) @@ -729,7 +627,9 @@ #if defined(__MINGW32__) || defined(AMIGA) || \ defined(__OpenBSD__) || defined(__NetBSD__) || \ defined(__gnu_hurd__) || defined(__ANDROID_API__) || \ - (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8) + defined(__sun) || defined(__CYGWIN__) || \ + (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8) || \ + (defined(_MSC_VER) && _MSC_VER < 1500) git__insertsort_r(els, nel, elsize, NULL, cmp, payload); #elif defined(GIT_WIN32) git__qsort_r_glue glue = { cmp, payload }; @@ -764,3 +664,79 @@ if (freeswap) git__free(swapel); } + +static const int8_t utf8proc_utf8class[256] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +int git__utf8_charlen(const uint8_t *str, int str_len) +{ + int length, i; + + length = utf8proc_utf8class[str[0]]; + if (!length) + return -1; + + if (str_len >= 0 && length > str_len) + return -str_len; + + for (i = 1; i < length; i++) { + if ((str[i] & 0xC0) != 0x80) + return -i; + } + + return length; +} + +int git__utf8_iterate(const uint8_t *str, int str_len, int32_t *dst) +{ + int length; + int32_t uc = -1; + + *dst = -1; + length = git__utf8_charlen(str, str_len); + if (length < 0) + return -1; + + switch (length) { + case 1: + uc = str[0]; + break; + case 2: + uc = ((str[0] & 0x1F) << 6) + (str[1] & 0x3F); + if (uc < 0x80) uc = -1; + break; + case 3: + uc = ((str[0] & 0x0F) << 12) + ((str[1] & 0x3F) << 6) + + (str[2] & 0x3F); + if (uc < 0x800 || (uc >= 0xD800 && uc < 0xE000) || + (uc >= 0xFDD0 && uc < 0xFDF0)) uc = -1; + break; + case 4: + uc = ((str[0] & 0x07) << 18) + ((str[1] & 0x3F) << 12) + + ((str[2] & 0x3F) << 6) + (str[3] & 0x3F); + if (uc < 0x10000 || uc >= 0x110000) uc = -1; + break; + } + + if (uc < 0 || ((uc & 0xFFFF) >= 0xFFFE)) + return -1; + + *dst = uc; + return length; +} diff -Nru libgit2-0.20.0/src/util.h libgit2-0.22.2/src/util.h --- libgit2-0.20.0/src/util.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/util.h 2015-03-24 16:10:45.000000000 +0000 @@ -8,6 +8,7 @@ #define INCLUDE_util_h__ #include "common.h" +#include "strnlen.h" #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) #define bitsizeof(x) (CHAR_BIT * sizeof(x)) @@ -19,6 +20,17 @@ # define max(a,b) ((a) > (b) ? (a) : (b)) #endif +#define GIT_DATE_RFC2822_SZ 32 + +/** + * Return the length of a constant string. + * We are aware that `strlen` performs the same task and is usually + * optimized away by the compiler, whilst being safer because it returns + * valid values when passed a pointer instead of a constant string; however + * this macro will transparently work with wide-char and single-char strings. + */ +#define CONST_STRLEN(x) ((sizeof(x)/sizeof(x[0])) - 1) + /* * Custom memory allocation wrappers * that set error code and error message @@ -50,8 +62,7 @@ size_t length = 0; char *ptr; - while (length < n && str[length]) - ++length; + length = p_strnlen(str, n); ptr = (char*)git__malloc(length + 1); @@ -95,6 +106,7 @@ 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_icase(const char *str, size_t str_n, const char *prefix); extern int git__suffixcmp(const char *str, const char *suffix); GIT_INLINE(int) git__signum(int val) @@ -122,6 +134,13 @@ return p == (size_t)r; } +/** @return true if p fits into the range of an unsigned long */ +GIT_INLINE(int) git__is_ulong(git_off_t p) +{ + unsigned long r = (unsigned long)p; + return p == (git_off_t)r; +} + /* 32-bit cross-platform rotl */ #ifdef _MSC_VER /* use built-in method in MSVC */ # define git__rotl(v, s) (uint32_t)_rotl(v, s) @@ -194,6 +213,7 @@ size_t *position); extern int git__strcmp_cb(const void *a, const void *b); +extern int git__strcasecmp_cb(const void *a, const void *b); extern int git__strcmp(const char *a, const char *b); extern int git__strcasecmp(const char *a, const char *b); @@ -297,12 +317,12 @@ GIT_INLINE(bool) git__isspace(int c) { - return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v' || c == 0x85 /* Unicode CR+LF */); + return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v'); } GIT_INLINE(bool) git__isspace_nonlf(int c) { - return (c == ' ' || c == '\t' || c == '\f' || c == '\r' || c == '\v' || c == 0x85 /* Unicode CR+LF */); + return (c == ' ' || c == '\t' || c == '\f' || c == '\r' || c == '\v'); } GIT_INLINE(bool) git__iswildcard(int c) @@ -329,6 +349,16 @@ extern int git__date_parse(git_time_t *out, const char *date); /* + * Format a git_time as a RFC2822 string + * + * @param out buffer to store formatted date; a '\\0' terminator will automatically be added. + * @param len size of the buffer; should be atleast `GIT_DATE_RFC2822_SZ` in size; + * @param date the date to be formatted + * @return 0 if successful; -1 on error + */ +extern int git__date_rfc2822_fmt(char *out, size_t len, const git_time *date); + +/* * Unescapes a string in-place. * * Edge cases behavior: @@ -338,6 +368,17 @@ extern size_t git__unescape(char *str); /* + * Iterate through an UTF-8 string, yielding one + * codepoint at a time. + * + * @param str current position in the string + * @param str_len size left in the string; -1 if the string is NULL-terminated + * @param dst pointer where to store the current codepoint + * @return length in bytes of the read codepoint; -1 if the codepoint was invalid + */ +extern int git__utf8_iterate(const uint8_t *str, int str_len, int32_t *dst); + +/* * Safely zero-out memory, making sure that the compiler * doesn't optimize away the operation. */ @@ -390,7 +431,18 @@ scaling_factor = (double)info.numer / (double)info.denom; } - return (double)time * scaling_factor / 1.0E-9; + return (double)time * scaling_factor / 1.0E9; +} + +#elif defined(AMIGA) + +#include + +GIT_INLINE(double) git__timer(void) +{ + struct TimeVal tv; + ITimer->GetUpTime(&tv); + return (double)tv.Seconds + (double)tv.Microseconds / 1.0E6; } #else @@ -402,13 +454,13 @@ struct timespec tp; if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) { - return (double) tp.tv_sec + (double) tp.tv_nsec / 1E-9; + return (double) tp.tv_sec + (double) tp.tv_nsec / 1.0E9; } else { /* Fall back to using gettimeofday */ struct timeval tv; struct timezone tz; gettimeofday(&tv, &tz); - return (double)tv.tv_sec + (double)tv.tv_usec / 1E-6; + return (double)tv.tv_sec + (double)tv.tv_usec / 1.0E6; } } diff -Nru libgit2-0.20.0/src/vector.c libgit2-0.22.2/src/vector.c --- libgit2-0.20.0/src/vector.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/vector.c 2015-03-24 16:10:45.000000000 +0000 @@ -6,7 +6,6 @@ */ #include "common.h" -#include "repository.h" #include "vector.h" /* In elements, not bytes */ @@ -55,9 +54,11 @@ bytes = src->length * sizeof(void *); v->_alloc_size = src->length; - v->_cmp = cmp; + v->_cmp = cmp ? cmp : src->_cmp; v->length = src->length; - v->sorted = src->sorted && cmp == src->_cmp; + v->flags = src->flags; + if (cmp != src->_cmp) + git_vector_set_sorted(v, 0); v->contents = git__malloc(bytes); GITERR_CHECK_ALLOC(v->contents); @@ -77,6 +78,20 @@ v->_alloc_size = 0; } +void git_vector_free_deep(git_vector *v) +{ + size_t i; + + assert(v); + + for (i = 0; i < v->length; ++i) { + git__free(v->contents[i]); + v->contents[i] = NULL; + } + + git_vector_free(v); +} + int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp) { assert(v); @@ -84,12 +99,28 @@ v->_alloc_size = 0; v->_cmp = cmp; v->length = 0; - v->sorted = 1; + v->flags = GIT_VECTOR_SORTED; v->contents = NULL; return resize_vector(v, max(initial_size, MIN_ALLOCSIZE)); } +void **git_vector_detach(size_t *size, size_t *asize, git_vector *v) +{ + void **data = v->contents; + + if (size) + *size = v->length; + if (asize) + *asize = v->_alloc_size; + + v->_alloc_size = 0; + v->length = 0; + v->contents = NULL; + + return data; +} + int git_vector_insert(git_vector *v, void *element) { assert(v); @@ -99,7 +130,8 @@ return -1; v->contents[v->length++] = element; - v->sorted = 0; + + git_vector_set_sorted(v, v->length <= 1); return 0; } @@ -112,7 +144,7 @@ assert(v && v->_cmp); - if (!v->sorted) + if (!git_vector_is_sorted(v)) git_vector_sort(v); if (v->length >= v->_alloc_size && @@ -142,11 +174,12 @@ { assert(v); - if (v->sorted || !v->_cmp) + if (git_vector_is_sorted(v) || !v->_cmp) return; - git__tsort(v->contents, v->length, v->_cmp); - v->sorted = 1; + if (v->length > 1) + git__tsort(v->contents, v->length, v->_cmp); + git_vector_set_sorted(v, 1); } int git_vector_bsearch2( @@ -244,14 +277,16 @@ } void git_vector_remove_matching( - git_vector *v, int (*match)(const git_vector *v, size_t idx)) + git_vector *v, + int (*match)(const git_vector *v, size_t idx, void *payload), + void *payload) { size_t i, j; for (i = 0, j = 0; j < v->length; ++j) { v->contents[i] = v->contents[j]; - if (!match(v, i)) + if (!match(v, i, payload)) i++; } @@ -262,7 +297,7 @@ { assert(v); v->length = 0; - v->sorted = 1; + git_vector_set_sorted(v, 1); } void git_vector_swap(git_vector *a, git_vector *b) @@ -295,8 +330,10 @@ int git_vector_set(void **old, git_vector *v, size_t position, void *value) { - if (git_vector_resize_to(v, position + 1) < 0) - return -1; + if (position + 1 > v->length) { + if (git_vector_resize_to(v, position + 1) < 0) + return -1; + } if (old != NULL) *old = v->contents[position]; @@ -305,3 +342,18 @@ return 0; } + +int git_vector_verify_sorted(const git_vector *v) +{ + size_t i; + + if (!git_vector_is_sorted(v)) + return -1; + + for (i = 1; i < v->length; ++i) { + if (v->_cmp(v->contents[i - 1], v->contents[i]) > 0) + return -1; + } + + return 0; +} diff -Nru libgit2-0.20.0/src/vector.h libgit2-0.22.2/src/vector.h --- libgit2-0.20.0/src/vector.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/vector.h 2015-03-24 16:10:45.000000000 +0000 @@ -7,26 +7,34 @@ #ifndef INCLUDE_vector_h__ #define INCLUDE_vector_h__ -#include "git2/common.h" +#include "common.h" typedef int (*git_vector_cmp)(const void *, const void *); +enum { + GIT_VECTOR_SORTED = (1u << 0), + GIT_VECTOR_FLAG_MAX = (1u << 1), +}; + typedef struct git_vector { size_t _alloc_size; git_vector_cmp _cmp; void **contents; size_t length; - int sorted; + uint32_t flags; } git_vector; #define GIT_VECTOR_INIT {0} int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp); void git_vector_free(git_vector *v); +void git_vector_free_deep(git_vector *v); /* free each entry and self */ void git_vector_clear(git_vector *v); int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp); void git_vector_swap(git_vector *a, git_vector *b); +void **git_vector_detach(size_t *size, size_t *asize, git_vector *v); + void git_vector_sort(git_vector *v); /** Linear search for matching entry using internal comparison function */ @@ -77,19 +85,33 @@ int git_vector_remove(git_vector *v, size_t idx); void git_vector_pop(git_vector *v); void git_vector_uniq(git_vector *v, void (*git_free_cb)(void *)); + void git_vector_remove_matching( - git_vector *v, int (*match)(const git_vector *v, size_t idx)); + git_vector *v, + int (*match)(const git_vector *v, size_t idx, void *payload), + void *payload); int git_vector_resize_to(git_vector *v, size_t new_length); int git_vector_set(void **old, git_vector *v, size_t position, void *value); +/** Check if vector is sorted */ +#define git_vector_is_sorted(V) (((V)->flags & GIT_VECTOR_SORTED) != 0) + +/** Directly set sorted state of vector */ +#define git_vector_set_sorted(V,S) do { \ + (V)->flags = (S) ? ((V)->flags | GIT_VECTOR_SORTED) : \ + ((V)->flags & ~GIT_VECTOR_SORTED); } while (0) + /** Set the comparison function used for sorting the vector */ GIT_INLINE(void) git_vector_set_cmp(git_vector *v, git_vector_cmp cmp) { if (cmp != v->_cmp) { v->_cmp = cmp; - v->sorted = 0; + git_vector_set_sorted(v, 0); } } +/* Just use this in tests, not for realz. returns -1 if not sorted */ +int git_vector_verify_sorted(const git_vector *v); + #endif diff -Nru libgit2-0.20.0/src/win32/dir.c libgit2-0.22.2/src/win32/dir.c --- libgit2-0.20.0/src/win32/dir.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/win32/dir.c 2015-03-24 16:10:45.000000000 +0000 @@ -7,29 +7,13 @@ #define GIT__WIN32_NO_WRAP_DIR #include "posix.h" -static int init_filter(char *filter, size_t n, const char *dir) -{ - size_t len = strlen(dir); - - if (len+3 >= n) - return 0; - - strcpy(filter, dir); - if (len && dir[len-1] != '/') - strcat(filter, "/"); - strcat(filter, "*"); - - return 1; -} - git__DIR *git__opendir(const char *dir) { - git_win32_path_as_utf8 filter; git_win32_path filter_w; git__DIR *new = NULL; size_t dirlen; - if (!dir || !init_filter(filter, sizeof(filter), dir)) + if (!dir || !git_win32__findfirstfile_filter(filter_w, dir)) return NULL; dirlen = strlen(dir); @@ -39,7 +23,6 @@ return NULL; memcpy(new->dir, dir, dirlen); - git_win32_path_from_c(filter_w, filter); new->h = FindFirstFileW(filter_w, &new->f); if (new->h == INVALID_HANDLE_VALUE) { @@ -72,10 +55,10 @@ return -1; } - if (wcslen(d->f.cFileName) >= sizeof(entry->d_name)) + /* Convert the path to UTF-8 */ + if (git_win32_path_to_utf8(entry->d_name, d->f.cFileName) < 0) return -1; - git_win32_path_to_c(entry->d_name, d->f.cFileName); entry->d_ino = 0; *result = entry; @@ -96,7 +79,6 @@ void git__rewinddir(git__DIR *d) { - git_win32_path_as_utf8 filter; git_win32_path filter_w; if (!d) @@ -108,10 +90,9 @@ d->first = 0; } - if (!init_filter(filter, sizeof(filter), d->dir)) + if (!git_win32__findfirstfile_filter(filter_w, d->dir)) return; - git_win32_path_from_c(filter_w, filter); d->h = FindFirstFileW(filter_w, &d->f); if (d->h == INVALID_HANDLE_VALUE) diff -Nru libgit2-0.20.0/src/win32/dir.h libgit2-0.22.2/src/win32/dir.h --- libgit2-0.20.0/src/win32/dir.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/win32/dir.h 2015-03-24 16:10:45.000000000 +0000 @@ -8,10 +8,11 @@ #define INCLUDE_dir_h__ #include "common.h" +#include "w32_util.h" struct git__dirent { int d_ino; - git_win32_path_as_utf8 d_name; + git_win32_utf8_path d_name; }; typedef struct { diff -Nru libgit2-0.20.0/src/win32/error.c libgit2-0.22.2/src/win32/error.c --- libgit2-0.20.0/src/win32/error.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/win32/error.c 2015-03-24 16:10:45.000000000 +0000 @@ -7,21 +7,17 @@ #include "common.h" #include "error.h" +#include "utf-conv.h" #ifdef GIT_WINHTTP # include #endif -#ifndef WC_ERR_INVALID_CHARS -#define WC_ERR_INVALID_CHARS 0x80 -#endif - char *git_win32_get_error_message(DWORD error_code) { LPWSTR lpMsgBuf = NULL; HMODULE hModule = NULL; char *utf8_msg = NULL; - int utf8_size; DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS; @@ -45,33 +41,11 @@ if (FormatMessageW(dwFlags, hModule, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&lpMsgBuf, 0, NULL)) { + /* Convert the message to UTF-8. If this fails, we will + * return NULL, which is a condition expected by the caller */ + if (git__utf16_to_8_alloc(&utf8_msg, lpMsgBuf) < 0) + utf8_msg = NULL; - /* Invalid code point check supported on Vista+ only */ - if (git_has_win32_version(6, 0, 0)) - dwFlags = WC_ERR_INVALID_CHARS; - else - dwFlags = 0; - - utf8_size = WideCharToMultiByte(CP_UTF8, dwFlags, - lpMsgBuf, -1, NULL, 0, NULL, NULL); - - if (!utf8_size) { - assert(0); - goto on_error; - } - - utf8_msg = git__malloc(utf8_size); - - if (!utf8_msg) - goto on_error; - - if (!WideCharToMultiByte(CP_UTF8, dwFlags, - lpMsgBuf, -1, utf8_msg, utf8_size, NULL, NULL)) { - git__free(utf8_msg); - goto on_error; - } - -on_error: LocalFree(lpMsgBuf); } diff -Nru libgit2-0.20.0/src/win32/findfile.c libgit2-0.22.2/src/win32/findfile.c --- libgit2-0.20.0/src/win32/findfile.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/win32/findfile.c 2015-03-24 16:10:45.000000000 +0000 @@ -5,6 +5,7 @@ * a Linking Exception. For full terms see the included COPYING file. */ +#include "path_w32.h" #include "utf-conv.h" #include "path.h" #include "findfile.h" @@ -17,54 +18,34 @@ #define REG_MSYSGIT_INSTALL L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1" #endif -int git_win32__expand_path(struct git_win32__path *s_root, const wchar_t *templ) -{ - s_root->len = ExpandEnvironmentStringsW(templ, s_root->path, MAX_PATH); - return s_root->len ? 0 : -1; -} +typedef struct { + git_win32_path path; + DWORD len; +} _findfile_path; -static int win32_path_to_8(git_buf *path_utf8, const wchar_t *path) +static int git_win32__expand_path(_findfile_path *dest, const wchar_t *src) { - char temp_utf8[GIT_PATH_MAX]; + dest->len = ExpandEnvironmentStringsW(src, dest->path, ARRAY_SIZE(dest->path)); - git__utf16_to_8(temp_utf8, GIT_PATH_MAX, path); - git_path_mkposix(temp_utf8); + if (!dest->len || dest->len > ARRAY_SIZE(dest->path)) + return -1; - return git_buf_sets(path_utf8, temp_utf8); + return 0; } -int git_win32__find_file( - git_buf *path, const struct git_win32__path *root, const char *filename) +static int win32_path_to_8(git_buf *dest, const wchar_t *src) { - size_t len, alloc_len; - wchar_t *file_utf16 = NULL; - - if (!root || !filename || (len = strlen(filename)) == 0) - return GIT_ENOTFOUND; - - /* allocate space for wchar_t path to file */ - alloc_len = root->len + len + 2; - file_utf16 = git__calloc(alloc_len, sizeof(wchar_t)); - GITERR_CHECK_ALLOC(file_utf16); - - /* append root + '\\' + filename as wchar_t */ - memcpy(file_utf16, root->path, root->len * sizeof(wchar_t)); + git_win32_utf8_path utf8_path; - if (*filename == '/' || *filename == '\\') - filename++; - - git__utf8_to_16(file_utf16 + root->len - 1, alloc_len - root->len, filename); - - /* check access */ - if (_waccess(file_utf16, F_OK) < 0) { - git__free(file_utf16); - return GIT_ENOTFOUND; + if (git_win32_path_to_utf8(utf8_path, src) < 0) { + giterr_set(GITERR_OS, "Unable to convert path to UTF-8"); + return -1; } - win32_path_to_8(path, file_utf16); - git__free(file_utf16); + /* Convert backslashes to forward slashes */ + git_path_mkposix(utf8_path); - return 0; + return git_buf_sets(dest, utf8_path); } static wchar_t* win32_walkpath(wchar_t *path, wchar_t *buf, size_t buflen) @@ -89,7 +70,7 @@ static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe, const wchar_t *subdir) { wchar_t *env = _wgetenv(L"PATH"), lastch; - struct git_win32__path root; + _findfile_path root; size_t gitexe_len = wcslen(gitexe); if (!env) @@ -122,43 +103,44 @@ } static int win32_find_git_in_registry( - git_buf *buf, const HKEY hieve, const wchar_t *key, const wchar_t *subdir) + git_buf *buf, const HKEY hive, const wchar_t *key, const wchar_t *subdir) { HKEY hKey; - DWORD dwType = REG_SZ; - struct git_win32__path path16; + int error = GIT_ENOTFOUND; assert(buf); - path16.len = MAX_PATH; - - if (RegOpenKeyExW(hieve, key, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { - if (RegQueryValueExW(hKey, L"InstallLocation", NULL, &dwType, - (LPBYTE)&path16.path, &path16.len) == ERROR_SUCCESS) - { - /* InstallLocation points to the root of the git directory */ - - if (path16.len + 4 > MAX_PATH) { /* 4 = wcslen(L"etc\\") */ - giterr_set(GITERR_OS, "Cannot locate git - path too long"); - return -1; - } - - wcscat(path16.path, subdir); - path16.len += 4; - - win32_path_to_8(buf, path16.path); + if (!RegOpenKeyExW(hive, key, 0, KEY_READ, &hKey)) { + DWORD dwType, cbData; + git_win32_path path; + + /* Ensure that the buffer is big enough to have the suffix attached + * after we receive the result. */ + cbData = (DWORD)(sizeof(path) - wcslen(subdir) * sizeof(wchar_t)); + + /* InstallLocation points to the root of the git directory */ + if (!RegQueryValueExW(hKey, L"InstallLocation", NULL, &dwType, (LPBYTE)path, &cbData) && + dwType == REG_SZ) { + + /* Append the suffix */ + wcscat(path, subdir); + + /* Convert to UTF-8, with forward slashes, and output the path + * to the provided buffer */ + if (!win32_path_to_8(buf, path)) + error = 0; } RegCloseKey(hKey); } - return path16.len ? 0 : GIT_ENOTFOUND; + return error; } static int win32_find_existing_dirs( git_buf *out, const wchar_t *tmpl[]) { - struct git_win32__path path16; + _findfile_path path16; git_buf buf = GIT_BUF_INIT; git_buf_clear(out); diff -Nru libgit2-0.20.0/src/win32/findfile.h libgit2-0.22.2/src/win32/findfile.h --- libgit2-0.20.0/src/win32/findfile.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/win32/findfile.h 2015-03-24 16:10:45.000000000 +0000 @@ -8,17 +8,6 @@ #ifndef INCLUDE_git_findfile_h__ #define INCLUDE_git_findfile_h__ -struct git_win32__path { - wchar_t path[MAX_PATH]; - DWORD len; -}; - -extern int git_win32__expand_path( - struct git_win32__path *s_root, const wchar_t *templ); - -extern int git_win32__find_file( - git_buf *path, const struct git_win32__path *root, const char *filename); - extern int git_win32__find_system_dirs(git_buf *out, const wchar_t *subpath); extern int git_win32__find_global_dirs(git_buf *out); extern int git_win32__find_xdg_dirs(git_buf *out); diff -Nru libgit2-0.20.0/src/win32/map.c libgit2-0.22.2/src/win32/map.c --- libgit2-0.20.0/src/win32/map.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/win32/map.c 2015-03-24 16:10:45.000000000 +0000 @@ -8,6 +8,7 @@ #include "map.h" #include +#ifndef NO_MMAP static DWORD get_page_size(void) { @@ -22,6 +23,12 @@ return page_size; } +int git__page_size(size_t *page_size) +{ + *page_size = get_page_size(); + return 0; +} + int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset) { HANDLE fh = (HANDLE)_get_osfhandle(fd); @@ -112,4 +119,4 @@ return error; } - +#endif diff -Nru libgit2-0.20.0/src/win32/mingw-compat.h libgit2-0.22.2/src/win32/mingw-compat.h --- libgit2-0.20.0/src/win32/mingw-compat.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/win32/mingw-compat.h 2015-03-24 16:10:45.000000000 +0000 @@ -9,20 +9,13 @@ #if defined(__MINGW32__) -/* use a 64-bit file offset type */ -# define lseek _lseeki64 -# define stat _stati64 -# define fstat _fstati64 +#undef stat -/* stat: file mode type testing macros */ -# define _S_IFLNK 0120000 -# define S_IFLNK _S_IFLNK -# define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK) - -GIT_INLINE(size_t) p_strnlen(const char *s, size_t maxlen) { - const char *end = memchr(s, 0, maxlen); - return end ? (size_t)(end - s) : maxlen; -} +#if _WIN32_WINNT >= 0x0601 +#define stat __stat64 +#else +#define stat _stati64 +#endif #endif diff -Nru libgit2-0.20.0/src/win32/msvc-compat.h libgit2-0.22.2/src/win32/msvc-compat.h --- libgit2-0.20.0/src/win32/msvc-compat.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/win32/msvc-compat.h 2015-03-24 16:10:45.000000000 +0000 @@ -9,41 +9,12 @@ #if defined(_MSC_VER) -/* access() mode parameter #defines */ -# define F_OK 0 /* existence check */ -# define W_OK 2 /* write mode check */ -# define R_OK 4 /* read mode check */ +/* 64-bit stat information, regardless of USE_32BIT_TIME_T define */ +#define stat __stat64 -# define lseek _lseeki64 -# define stat _stat64 -# define fstat _fstat64 - -/* stat: file mode type testing macros */ -# define _S_IFLNK 0120000 -# define S_IFLNK _S_IFLNK -# define S_IXUSR 00100 - -# define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) -# define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG) -# define S_ISFIFO(m) (((m) & _S_IFMT) == _S_IFIFO) -# define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK) - -# define mode_t unsigned short - -/* case-insensitive string comparison */ -# define strcasecmp _stricmp -# define strncasecmp _strnicmp - -/* MSVC doesn't define ssize_t at all */ +typedef unsigned short mode_t; typedef SSIZE_T ssize_t; -/* define snprintf using variadic macro support if available */ -#if _MSC_VER >= 1400 -# define snprintf(BUF, SZ, FMT, ...) _snprintf_s(BUF, SZ, _TRUNCATE, FMT, __VA_ARGS__) -#else -# define snprintf _snprintf -#endif - #endif #define GIT_STDLIB_CALL __cdecl diff -Nru libgit2-0.20.0/src/win32/path_w32.c libgit2-0.22.2/src/win32/path_w32.c --- libgit2-0.20.0/src/win32/path_w32.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/win32/path_w32.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,305 @@ +/* + * 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 "path.h" +#include "path_w32.h" +#include "utf-conv.h" + +#define PATH__NT_NAMESPACE L"\\\\?\\" +#define PATH__NT_NAMESPACE_LEN 4 + +#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] == '/')) + +#define path__is_unc(p) \ + (((p)[0] == '\\' && (p)[1] == '\\') || ((p)[0] == '/' && (p)[1] == '/')) + +GIT_INLINE(int) path__cwd(wchar_t *path, int size) +{ + int len; + + if ((len = GetCurrentDirectoryW(size, path)) == 0) { + errno = GetLastError() == ERROR_ACCESS_DENIED ? EACCES : ENOENT; + return -1; + } else if (len > size) { + errno = ENAMETOOLONG; + return -1; + } + + /* The Win32 APIs may return "\\?\" once you've used it first. + * But it may not. What a gloriously predictible API! + */ + if (wcsncmp(path, PATH__NT_NAMESPACE, PATH__NT_NAMESPACE_LEN)) + return len; + + len -= PATH__NT_NAMESPACE_LEN; + + memmove(path, path + PATH__NT_NAMESPACE_LEN, sizeof(wchar_t) * len); + return len; +} + +static wchar_t *path__skip_server(wchar_t *path) +{ + wchar_t *c; + + for (c = path; *c; c++) { + if (path__is_dirsep(*c)) + return c + 1; + } + + return c; +} + +static wchar_t *path__skip_prefix(wchar_t *path) +{ + if (path__is_nt_namespace(path)) { + path += PATH__NT_NAMESPACE_LEN; + + if (wcsncmp(path, L"UNC\\", 4) == 0) + path = path__skip_server(path + 4); + else if (path__is_absolute(path)) + path += PATH__ABSOLUTE_LEN; + } else if (path__is_absolute(path)) { + path += PATH__ABSOLUTE_LEN; + } else if (path__is_unc(path)) { + path = path__skip_server(path + 2); + } + + return path; +} + +int git_win32_path_canonicalize(git_win32_path path) +{ + wchar_t *base, *from, *to, *next; + size_t len; + + base = to = path__skip_prefix(path); + + /* Unposixify if the prefix */ + for (from = path; from < to; from++) { + if (*from == L'/') + *from = L'\\'; + } + + while (*from) { + for (next = from; *next; ++next) { + if (*next == L'/') { + *next = L'\\'; + break; + } + + if (*next == L'\\') + break; + } + + len = next - from; + + if (len == 1 && from[0] == L'.') + /* do nothing with singleton dot */; + + else if (len == 2 && from[0] == L'.' && from[1] == L'.') { + if (to == base) { + /* no more path segments to strip, eat the "../" */ + if (*next == L'\\') + len++; + + base = to; + } else { + /* back up a path segment */ + while (to > base && to[-1] == L'\\') to--; + while (to > base && to[-1] != L'\\') to--; + } + } else { + if (*next == L'\\' && *from != L'\\') + len++; + + if (to != from) + memmove(to, from, sizeof(wchar_t) * len); + + to += len; + } + + from += len; + + while (*from == L'\\') from++; + } + + /* Strip trailing backslashes */ + while (to > base && to[-1] == L'\\') to--; + + *to = L'\0'; + + return (to - path); +} + +int git_win32_path__cwd(wchar_t *out, size_t len) +{ + int cwd_len; + + if ((cwd_len = path__cwd(out, len)) < 0) + return -1; + + /* UNC paths */ + if (wcsncmp(L"\\\\", out, 2) == 0) { + /* Our buffer must be at least 5 characters larger than the + * current working directory: we swallow one of the leading + * '\'s, but we we add a 'UNC' specifier to the path, plus + * a trailing directory separator, plus a NUL. + */ + if (cwd_len > MAX_PATH - 4) { + errno = ENAMETOOLONG; + return -1; + } + + memmove(out+2, out, sizeof(wchar_t) * cwd_len); + out[0] = L'U'; + out[1] = L'N'; + out[2] = L'C'; + + cwd_len += 2; + } + + /* Our buffer must be at least 2 characters larger than the current + * working directory. (One character for the directory separator, + * one for the null. + */ + else if (cwd_len > MAX_PATH - 2) { + errno = ENAMETOOLONG; + return -1; + } + + return cwd_len; +} + +int git_win32_path_from_utf8(git_win32_path out, const char *src) +{ + wchar_t *dest = out; + + /* All win32 paths are in NT-prefixed format, beginning with "\\?\". */ + memcpy(dest, PATH__NT_NAMESPACE, sizeof(wchar_t) * PATH__NT_NAMESPACE_LEN); + dest += PATH__NT_NAMESPACE_LEN; + + /* See if this is an absolute path (beginning with a drive letter) */ + if (path__is_absolute(src)) { + if (git__utf8_to_16(dest, MAX_PATH, src) < 0) + return -1; + } + /* File-prefixed NT-style paths beginning with \\?\ */ + else if (path__is_nt_namespace(src)) { + /* Skip the NT prefix, the destination already contains it */ + if (git__utf8_to_16(dest, MAX_PATH, src + PATH__NT_NAMESPACE_LEN) < 0) + return -1; + } + /* UNC paths */ + else if (path__is_unc(src)) { + memcpy(dest, L"UNC\\", sizeof(wchar_t) * 4); + dest += 4; + + /* Skip the leading "\\" */ + if (git__utf8_to_16(dest, MAX_PATH - 2, src + 2) < 0) + return -1; + } + /* Absolute paths omitting the drive letter */ + else if (src[0] == '\\' || src[0] == '/') { + if (path__cwd(dest, MAX_PATH) < 0) + return -1; + + if (!path__is_absolute(dest)) { + errno = ENOENT; + return -1; + } + + /* Skip the drive letter specification ("C:") */ + if (git__utf8_to_16(dest + 2, MAX_PATH - 2, src) < 0) + return -1; + } + /* Relative paths */ + else { + int cwd_len; + + if ((cwd_len = git_win32_path__cwd(dest, MAX_PATH)) < 0) + return -1; + + dest[cwd_len++] = L'\\'; + + if (git__utf8_to_16(dest + cwd_len, MAX_PATH - cwd_len, src) < 0) + return -1; + } + + return git_win32_path_canonicalize(out); +} + +int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src) +{ + char *out = dest; + int len; + + /* Strip NT namespacing "\\?\" */ + if (path__is_nt_namespace(src)) { + src += 4; + + /* "\\?\UNC\server\share" -> "\\server\share" */ + if (wcsncmp(src, L"UNC\\", 4) == 0) { + src += 4; + + memcpy(dest, "\\\\", 2); + out = dest + 2; + } + } + + if ((len = git__utf16_to_8(out, GIT_WIN_PATH_UTF8, src)) < 0) + return len; + + git_path_mkposix(dest); + + return len; +} + +char *git_win32_path_8dot3_name(const char *path) +{ + git_win32_path longpath, shortpath; + wchar_t *start; + char *shortname; + int len, namelen = 1; + + if (git_win32_path_from_utf8(longpath, path) < 0) + return NULL; + + len = GetShortPathNameW(longpath, shortpath, GIT_WIN_PATH_UTF16); + + while (len && shortpath[len-1] == L'\\') + shortpath[--len] = L'\0'; + + if (len == 0 || len >= GIT_WIN_PATH_UTF16) + return NULL; + + for (start = shortpath + (len - 1); + start > shortpath && *(start-1) != '/' && *(start-1) != '\\'; + start--) + namelen++; + + /* We may not have actually been given a short name. But if we have, + * it will be in the ASCII byte range, so we don't need to worry about + * multi-byte sequences and can allocate naively. + */ + if (namelen > 12 || (shortname = git__malloc(namelen + 1)) == NULL) + return NULL; + + if ((len = git__utf16_to_8(shortname, namelen + 1, start)) < 0) + return NULL; + + return shortname; +} diff -Nru libgit2-0.20.0/src/win32/path_w32.h libgit2-0.22.2/src/win32/path_w32.h --- libgit2-0.20.0/src/win32/path_w32.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/win32/path_w32.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,82 @@ +/* + * 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_git_path_w32_h__ +#define INCLUDE_git_path_w32_h__ + +#include "common.h" + +/* + * Provides a large enough buffer to support Windows paths: MAX_PATH is + * 260, corresponding to a maximum path length of 259 characters plus a + * NULL terminator. Prefixing with "\\?\" adds 4 characters, but if the + * original was a UNC path, then we turn "\\server\share" into + * "\\?\UNC\server\share". So we replace the first two characters with + * 8 characters, a net gain of 6, so the maximum length is MAX_PATH+6. + */ +#define GIT_WIN_PATH_UTF16 MAX_PATH+6 + +/* Maximum size of a UTF-8 Win32 path. We remove the "\\?\" or "\\?\UNC\" + * prefixes for presentation, bringing us back to 259 (non-NULL) + * characters. UTF-8 does have 4-byte sequences, but they are encoded in + * UTF-16 using surrogate pairs, which takes up the space of two characters. + * Two characters in the range U+0800 -> U+FFFF take up more space in UTF-8 + * (6 bytes) than one surrogate pair (4 bytes). + */ +#define GIT_WIN_PATH_UTF8 (259 * 3 + 1) + +/* + * The length of a Windows "shortname", for 8.3 compatibility. + */ +#define GIT_WIN_PATH_SHORTNAME 13 + +/* Win32 path types */ +typedef wchar_t git_win32_path[GIT_WIN_PATH_UTF16]; +typedef char git_win32_utf8_path[GIT_WIN_PATH_UTF8]; + +/** + * Create a Win32 path (in UCS-2 format) from a UTF-8 string. + * + * @param dest The buffer to receive the wide string. + * @param src The UTF-8 string to convert. + * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure + */ +extern int git_win32_path_from_utf8(git_win32_path dest, const char *src); + +/** + * Canonicalize a Win32 UCS-2 path so that it is suitable for delivery to the + * Win32 APIs: remove multiple directory separators, squashing to a single one, + * strip trailing directory separators, ensure directory separators are all + * canonical (always backslashes, never forward slashes) and process any + * directory entries of '.' or '..'. + * + * This processes the buffer in place. + * + * @param path The buffer to process + * @return The new length of the buffer, in wchar_t's (not counting the NULL terminator) + */ +extern int git_win32_path_canonicalize(git_win32_path path); + +/** + * Create an internal format (posix-style) UTF-8 path from a Win32 UCS-2 path. + * + * @param dest The buffer to receive the UTF-8 string. + * @param src The wide string to convert. + * @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure + */ +extern int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src); + +/** + * Get the short name for the terminal path component in the given path. + * For example, given "C:\Foo\Bar\Asdf.txt", this will return the short name + * for the file "Asdf.txt". + * + * @param path The given path in UTF-8 + * @return The name of the shortname for the given path + */ +extern char *git_win32_path_8dot3_name(const char *path); + +#endif diff -Nru libgit2-0.20.0/src/win32/posix.h libgit2-0.22.2/src/win32/posix.h --- libgit2-0.20.0/src/win32/posix.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/win32/posix.h 2015-03-24 16:10:45.000000000 +0000 @@ -9,55 +9,39 @@ #include "common.h" #include "../posix.h" +#include "path_w32.h" #include "utf-conv.h" #include "dir.h" -/* define some standard errnos that the runtime may be missing. for example, - * mingw lacks EAFNOSUPPORT. */ +typedef SOCKET GIT_SOCKET; -#ifndef EAFNOSUPPORT -# define EAFNOSUPPORT (INT_MAX-1) -#endif - -GIT_INLINE(int) p_link(const char *old, const char *new) -{ - GIT_UNUSED(old); - GIT_UNUSED(new); - errno = ENOSYS; - return -1; -} - -GIT_INLINE(int) p_mkdir(const char *path, mode_t mode) -{ - git_win32_path buf; - GIT_UNUSED(mode); - git_win32_path_from_c(buf, path); - return _wmkdir(buf); -} - -extern int p_unlink(const char *path); +#define p_lseek(f,n,w) _lseeki64(f, n, w) +#define p_fstat(f,b) _fstat64(f, b) extern int p_lstat(const char *file_name, struct stat *buf); -extern int p_readlink(const char *link, char *target, size_t target_len); +extern int p_stat(const char* path, struct stat* buf); + +extern int p_readlink(const char *path, char *buf, size_t bufsiz); extern int p_symlink(const char *old, const char *new); -extern int p_hide_directory__w32(const char *path); +extern int p_link(const char *old, const char *new); +extern int p_unlink(const char *path); +extern int p_mkdir(const char *path, mode_t mode); +extern int p_fsync(int fd); extern char *p_realpath(const char *orig_path, char *buffer); + +extern int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags); +extern int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags); +extern int p_inet_pton(int af, const char* src, void* dst); + +#define strcasecmp(s1, s2) _stricmp(s1, s2) +#define strncasecmp(s1, s2, c) _strnicmp(s1, s2, c) extern int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr); extern int p_snprintf(char *buffer, size_t count, const char *format, ...) GIT_FORMAT_PRINTF(3, 4); extern int p_mkstemp(char *tmp_path); -extern int p_setenv(const char* name, const char* value, int overwrite); -extern int p_stat(const char* path, struct stat* buf); extern int p_chdir(const char* path); extern int p_chmod(const char* path, mode_t mode); extern int p_rmdir(const char* path); extern int p_access(const char* path, mode_t mode); -extern int p_fsync(int fd); -extern int p_open(const char *path, int flags, ...); -extern int p_creat(const char *path, mode_t mode); -extern int p_getcwd(char *buffer_out, size_t size); -extern int p_rename(const char *from, const char *to); -extern int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags); -extern int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags); -extern int p_inet_pton(int af, const char* src, void* dst); +extern int p_ftruncate(int fd, long size); /* p_lstat is almost but not quite POSIX correct. Specifically, the use of * ENOTDIR is wrong, in that it does not mean precisely that a non-directory @@ -67,4 +51,7 @@ */ extern int p_lstat_posixly(const char *filename, struct stat *buf); +extern struct tm * p_localtime_r (const time_t *timer, struct tm *result); +extern struct tm * p_gmtime_r (const time_t *timer, struct tm *result); + #endif diff -Nru libgit2-0.20.0/src/win32/posix_w32.c libgit2-0.22.2/src/win32/posix_w32.c --- libgit2-0.20.0/src/win32/posix_w32.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/win32/posix_w32.c 2015-03-24 16:10:45.000000000 +0000 @@ -7,19 +7,82 @@ #include "../posix.h" #include "../fileops.h" #include "path.h" +#include "path_w32.h" #include "utf-conv.h" #include "repository.h" +#include "reparse.h" #include #include #include #include +#ifndef FILE_NAME_NORMALIZED +# define FILE_NAME_NORMALIZED 0 +#endif + +#ifndef IO_REPARSE_TAG_SYMLINK +#define IO_REPARSE_TAG_SYMLINK (0xA000000CL) +#endif + +/* Options which we always provide to _wopen. + * + * _O_BINARY - Raw access; no translation of CR or LF characters + * _O_NOINHERIT - Do not mark the created handle as inheritable by child processes. + * The Windows default is 'not inheritable', but the CRT's default (following + * POSIX convention) is 'inheritable'. We have no desire for our handles to be + * inheritable on Windows, so specify the flag to get default behavior back. */ +#define STANDARD_OPEN_FLAGS (_O_BINARY | _O_NOINHERIT) + +/* GetFinalPathNameByHandleW signature */ +typedef DWORD(WINAPI *PFGetFinalPathNameByHandleW)(HANDLE, LPWSTR, DWORD, DWORD); + +int p_ftruncate(int fd, long size) +{ +#if defined(_MSC_VER) && _MSC_VER >= 1500 + return _chsize_s(fd, size); +#else + return _chsize(fd, size); +#endif +} + +int p_mkdir(const char *path, mode_t mode) +{ + git_win32_path buf; + + GIT_UNUSED(mode); + + if (git_win32_path_from_utf8(buf, path) < 0) + return -1; + + return _wmkdir(buf); +} + +int p_link(const char *old, const char *new) +{ + GIT_UNUSED(old); + GIT_UNUSED(new); + errno = ENOSYS; + return -1; +} + int p_unlink(const char *path) { git_win32_path buf; - git_win32_path_from_c(buf, path); - _wchmod(buf, 0666); - return _wunlink(buf); + int error; + + if (git_win32_path_from_utf8(buf, path) < 0) + return -1; + + error = _wunlink(buf); + + /* If the file could not be deleted because it was + * read-only, clear the bit and try again */ + if (error == -1 && errno == EACCES) { + _wchmod(buf, 0666); + error = _wunlink(buf); + } + + return error; } int p_fsync(int fd) @@ -53,28 +116,90 @@ return (time_t)winTime; } +static bool path_is_volume(wchar_t *target, size_t target_len) +{ + return (target_len && wcsncmp(target, L"\\??\\Volume{", 11) == 0); +} + +/* On success, returns the length, in characters, of the path stored in dest. + * On failure, returns a negative value. */ +static int readlink_w( + git_win32_path dest, + const git_win32_path path) +{ + BYTE buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + GIT_REPARSE_DATA_BUFFER *reparse_buf = (GIT_REPARSE_DATA_BUFFER *)buf; + HANDLE handle = NULL; + DWORD ioctl_ret; + wchar_t *target; + size_t target_len; + + int error = -1; + + handle = CreateFileW(path, GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); + + if (handle == INVALID_HANDLE_VALUE) { + errno = ENOENT; + return -1; + } + + if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0, + reparse_buf, sizeof(buf), &ioctl_ret, NULL)) { + errno = EINVAL; + goto on_error; + } + + switch (reparse_buf->ReparseTag) { + case IO_REPARSE_TAG_SYMLINK: + target = reparse_buf->SymbolicLinkReparseBuffer.PathBuffer + + (reparse_buf->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)); + target_len = reparse_buf->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR); + break; + case IO_REPARSE_TAG_MOUNT_POINT: + target = reparse_buf->MountPointReparseBuffer.PathBuffer + + (reparse_buf->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)); + target_len = reparse_buf->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR); + break; + default: + errno = EINVAL; + goto on_error; + } + + if (path_is_volume(target, target_len)) { + /* This path is a reparse point that represents another volume mounted + * at this location, it is not a symbolic link our input was canonical. + */ + errno = EINVAL; + error = -1; + } else if (target_len) { + /* The path may need to have a prefix removed. */ + target_len = git_win32__canonicalize_path(target, target_len); + + /* Need one additional character in the target buffer + * for the terminating NULL. */ + if (GIT_WIN_PATH_UTF16 > target_len) { + wcscpy(dest, target); + error = (int)target_len; + } + } + +on_error: + CloseHandle(handle); + return error; +} + #define WIN32_IS_WSEP(CH) ((CH) == L'/' || (CH) == L'\\') -static int do_lstat( - const char *file_name, struct stat *buf, int posix_enotdir) +static int lstat_w( + wchar_t *path, + struct stat *buf, + bool posix_enotdir) { WIN32_FILE_ATTRIBUTE_DATA fdata; - git_win32_path fbuf; - wchar_t lastch; - int flen; - - flen = git_win32_path_from_c(fbuf, file_name); - - /* truncate trailing slashes */ - for (; flen > 0; --flen) { - lastch = fbuf[flen - 1]; - if (WIN32_IS_WSEP(lastch)) - fbuf[flen - 1] = L'\0'; - else if (lastch != L'\0') - break; - } - if (GetFileAttributesExW(fbuf, GetFileExInfoStandard, &fdata)) { + if (GetFileAttributesExW(path, GetFileExInfoStandard, &fdata)) { int fMode = S_IREAD; if (!buf) @@ -88,12 +213,6 @@ if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) fMode |= S_IWRITE; - if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) - fMode |= S_IFLNK; - - if ((fMode & (S_IFDIR | S_IFLNK)) == (S_IFDIR | S_IFLNK)) // junction - fMode ^= S_IFLNK; - buf->st_ino = 0; buf->st_gid = 0; buf->st_uid = 0; @@ -105,19 +224,17 @@ buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); - /* Windows symlinks have zero file size, call readlink to determine - * the length of the path pointed to, which we expect everywhere else - */ - if (S_ISLNK(fMode)) { - git_win32_path_as_utf8 target; - int readlink_result; + if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + git_win32_path target; - readlink_result = p_readlink(file_name, target, sizeof(target)); + if (readlink_w(target, path) >= 0) { + buf->st_mode = (buf->st_mode & ~S_IFMT) | S_IFLNK; - if (readlink_result == -1) - return -1; - - buf->st_size = strlen(target); + /* st_size gets the UTF-8 length of the target name, in bytes, + * not counting the NULL terminator */ + if ((buf->st_size = git__utf16_to_8(NULL, 0, target)) < 0) + return -1; + } } return 0; @@ -129,18 +246,23 @@ * file path is a regular file, otherwise set ENOENT. */ if (posix_enotdir) { + size_t path_len = wcslen(path); + /* scan up path until we find an existing item */ while (1) { + DWORD attrs; + /* remove last directory component */ - for (--flen; flen > 0 && !WIN32_IS_WSEP(fbuf[flen]); --flen); + for (path_len--; path_len > 0 && !WIN32_IS_WSEP(path[path_len]); path_len--); - if (flen <= 0) + if (path_len <= 0) break; - fbuf[flen] = L'\0'; + path[path_len] = L'\0'; + attrs = GetFileAttributesW(path); - if (GetFileAttributesExW(fbuf, GetFileExInfoStandard, &fdata)) { - if (!(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + if (attrs != INVALID_FILE_ATTRIBUTES) { + if (!(attrs & FILE_ATTRIBUTE_DIRECTORY)) errno = ENOTDIR; break; } @@ -150,108 +272,51 @@ return -1; } +static int do_lstat(const char *path, struct stat *buf, bool posixly_correct) +{ + git_win32_path path_w; + int len; + + if ((len = git_win32_path_from_utf8(path_w, path)) < 0) + return -1; + + git_win32__path_trim_end(path_w, len); + + return lstat_w(path_w, buf, posixly_correct); +} + int p_lstat(const char *filename, struct stat *buf) { - return do_lstat(filename, buf, 0); + return do_lstat(filename, buf, false); } int p_lstat_posixly(const char *filename, struct stat *buf) { - return do_lstat(filename, buf, 1); + return do_lstat(filename, buf, true); } - -/* - * Parts of the The p_readlink function are heavily inspired by the php - * readlink function in link_win32.c - * - * Copyright (c) 1999 - 2012 The PHP Group. All rights reserved. - * - * For details of the PHP license see http://www.php.net/license/3_01.txt - */ -int p_readlink(const char *link, char *target, size_t target_len) +int p_readlink(const char *path, char *buf, size_t bufsiz) { - typedef DWORD (WINAPI *fpath_func)(HANDLE, LPWSTR, DWORD, DWORD); - static fpath_func pGetFinalPath = NULL; - HANDLE hFile; - DWORD dwRet; - git_win32_path link_w; - wchar_t* target_w; - int error = 0; - - assert(link && target && target_len > 0); - - /* - * Try to load the pointer to pGetFinalPath dynamically, because - * it is not available in platforms older than Vista - */ - if (pGetFinalPath == NULL) { - HMODULE module = GetModuleHandle("kernel32"); - - if (module != NULL) - pGetFinalPath = (fpath_func)GetProcAddress(module, "GetFinalPathNameByHandleW"); - - if (pGetFinalPath == NULL) { - giterr_set(GITERR_OS, - "'GetFinalPathNameByHandleW' is not available in this platform"); - return -1; - } - } - - git_win32_path_from_c(link_w, link); - - hFile = CreateFileW(link_w, // file to open - GENERIC_READ, // open for reading - FILE_SHARE_READ, // share for reading - NULL, // default security - OPEN_EXISTING, // existing file only - FILE_FLAG_BACKUP_SEMANTICS, // normal file - NULL); // no attr. template + git_win32_path path_w, target_w; + git_win32_utf8_path target; + int len; - if (hFile == INVALID_HANDLE_VALUE) { - giterr_set(GITERR_OS, "Cannot open '%s' for reading", link); + /* readlink(2) does not NULL-terminate the string written + * to the target buffer. Furthermore, the target buffer need + * not be large enough to hold the entire result. A truncated + * result should be written in this case. Since this truncation + * could occur in the middle of the encoding of a code point, + * we need to buffer the result on the stack. */ + + if (git_win32_path_from_utf8(path_w, path) < 0 || + readlink_w(target_w, path_w) < 0 || + (len = git_win32_path_to_utf8(target, target_w)) < 0) return -1; - } - - target_w = (wchar_t*)git__malloc(target_len * sizeof(wchar_t)); - GITERR_CHECK_ALLOC(target_w); - - dwRet = pGetFinalPath(hFile, target_w, (DWORD)target_len, 0x0); - if (dwRet == 0 || - dwRet >= target_len || - !WideCharToMultiByte(CP_UTF8, 0, target_w, -1, target, - (int)(target_len * sizeof(char)), NULL, NULL)) - error = -1; - git__free(target_w); - CloseHandle(hFile); - - if (error) - return error; - - /* Skip first 4 characters if they are "\\?\" */ - if (dwRet > 4 && - target[0] == '\\' && target[1] == '\\' && - target[2] == '?' && target[3] == '\\') - { - unsigned int offset = 4; - dwRet -= 4; - - /* \??\UNC\ */ - if (dwRet > 7 && - target[4] == 'U' && target[5] == 'N' && target[6] == 'C') - { - offset += 2; - dwRet -= 2; - target[offset] = '\\'; - } - - memmove(target, target + offset, dwRet); - } + bufsiz = min((size_t)len, bufsiz); + memcpy(buf, target, bufsiz); - target[dwRet] = '\0'; - - return dwRet; + return (int)bufsiz; } int p_symlink(const char *old, const char *new) @@ -267,7 +332,8 @@ git_win32_path buf; mode_t mode = 0; - git_win32_path_from_c(buf, path); + if (git_win32_path_from_utf8(buf, path) < 0) + return -1; if (flags & O_CREAT) { va_list arg_list; @@ -277,142 +343,229 @@ va_end(arg_list); } - return _wopen(buf, flags | _O_BINARY, mode); + return _wopen(buf, flags | STANDARD_OPEN_FLAGS, mode); } int p_creat(const char *path, mode_t mode) { git_win32_path buf; - git_win32_path_from_c(buf, path); - return _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode); + + if (git_win32_path_from_utf8(buf, path) < 0) + return -1; + + return _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | STANDARD_OPEN_FLAGS, mode); } int p_getcwd(char *buffer_out, size_t size) { - int ret; - wchar_t *buf; + git_win32_path buf; + wchar_t *cwd = _wgetcwd(buf, GIT_WIN_PATH_UTF16); - if ((size_t)((int)size) != size) + if (!cwd) return -1; - buf = (wchar_t*)git__malloc(sizeof(wchar_t) * (int)size); - GITERR_CHECK_ALLOC(buf); + /* Convert the working directory back to UTF-8 */ + if (git__utf16_to_8(buffer_out, size, cwd) < 0) { + DWORD code = GetLastError(); - _wgetcwd(buf, (int)size); + if (code == ERROR_INSUFFICIENT_BUFFER) + errno = ERANGE; + else + errno = EINVAL; - ret = WideCharToMultiByte( - CP_UTF8, 0, buf, -1, buffer_out, (int)size, NULL, NULL); + return -1; + } - git__free(buf); - return !ret ? -1 : 0; + return 0; +} + +/* + * Returns the address of the GetFinalPathNameByHandleW function. + * This function is available on Windows Vista and higher. + */ +static PFGetFinalPathNameByHandleW get_fpnbyhandle(void) +{ + static PFGetFinalPathNameByHandleW pFunc = NULL; + PFGetFinalPathNameByHandleW toReturn = pFunc; + + if (!toReturn) { + HMODULE hModule = GetModuleHandleW(L"kernel32"); + + if (hModule) + toReturn = (PFGetFinalPathNameByHandleW)GetProcAddress(hModule, "GetFinalPathNameByHandleW"); + + pFunc = toReturn; + } + + assert(toReturn); + + return toReturn; +} + +static int getfinalpath_w( + git_win32_path dest, + const wchar_t *path) +{ + PFGetFinalPathNameByHandleW pgfp = get_fpnbyhandle(); + HANDLE hFile; + DWORD dwChars; + + if (!pgfp) + return -1; + + /* Use FILE_FLAG_BACKUP_SEMANTICS so we can open a directory. Do not + * specify FILE_FLAG_OPEN_REPARSE_POINT; we want to open a handle to the + * target of the link. */ + hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + + if (INVALID_HANDLE_VALUE == hFile) + return -1; + + /* Call GetFinalPathNameByHandle */ + dwChars = pgfp(hFile, dest, GIT_WIN_PATH_UTF16, FILE_NAME_NORMALIZED); + CloseHandle(hFile); + + if (!dwChars || dwChars >= GIT_WIN_PATH_UTF16) + return -1; + + /* The path may be delivered to us with a prefix; canonicalize */ + return (int)git_win32__canonicalize_path(dest, dwChars); +} + +static int follow_and_lstat_link(git_win32_path path, struct stat* buf) +{ + git_win32_path target_w; + + if (getfinalpath_w(target_w, path) < 0) + return -1; + + return lstat_w(target_w, buf, false); } int p_stat(const char* path, struct stat* buf) { - git_win32_path_as_utf8 target; - int error = 0; + git_win32_path path_w; + int len; - error = do_lstat(path, buf, 0); + if ((len = git_win32_path_from_utf8(path_w, path)) < 0 || + lstat_w(path_w, buf, false) < 0) + return -1; - /* We need not do this in a loop to unwind chains of symlinks since - * p_readlink calls GetFinalPathNameByHandle which does it for us. */ - if (error >= 0 && S_ISLNK(buf->st_mode) && - (error = p_readlink(path, target, sizeof(target))) >= 0) - error = do_lstat(target, buf, 0); + /* The item is a symbolic link or mount point. No need to iterate + * to follow multiple links; use GetFinalPathNameFromHandle. */ + if (S_ISLNK(buf->st_mode)) + return follow_and_lstat_link(path_w, buf); - return error; + return 0; } int p_chdir(const char* path) { git_win32_path buf; - git_win32_path_from_c(buf, path); + + if (git_win32_path_from_utf8(buf, path) < 0) + return -1; + return _wchdir(buf); } int p_chmod(const char* path, mode_t mode) { git_win32_path buf; - git_win32_path_from_c(buf, path); + + if (git_win32_path_from_utf8(buf, path) < 0) + return -1; + return _wchmod(buf, mode); } int p_rmdir(const char* path) { - int error; git_win32_path buf; - git_win32_path_from_c(buf, path); + int error; + + if (git_win32_path_from_utf8(buf, path) < 0) + return -1; error = _wrmdir(buf); - /* _wrmdir() is documented to return EACCES if "A program has an open - * handle to the directory." This sounds like what everybody else calls - * EBUSY. Let's convert appropriate error codes. - */ - if (GetLastError() == ERROR_SHARING_VIOLATION) - errno = EBUSY; + if (error == -1) { + switch (GetLastError()) { + /* _wrmdir() is documented to return EACCES if "A program has an open + * handle to the directory." This sounds like what everybody else calls + * EBUSY. Let's convert appropriate error codes. + */ + case ERROR_SHARING_VIOLATION: + errno = EBUSY; + break; - return error; -} + /* This error can be returned when trying to rmdir an extant file. */ + case ERROR_DIRECTORY: + errno = ENOTDIR; + break; + } + } -int p_hide_directory__w32(const char *path) -{ - git_win32_path buf; - git_win32_path_from_c(buf, path); - return (SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN) != 0) ? 0 : -1; + return error; } char *p_realpath(const char *orig_path, char *buffer) { - int ret; - git_win32_path orig_path_w; - git_win32_path buffer_w; + git_win32_path orig_path_w, buffer_w; - git_win32_path_from_c(orig_path_w, orig_path); + if (git_win32_path_from_utf8(orig_path_w, orig_path) < 0) + return NULL; - /* Implicitly use GetCurrentDirectory which can be a threading issue */ - ret = GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL); + /* Note that if the path provided is a relative path, then the current directory + * is used to resolve the path -- which is a concurrency issue because the current + * directory is a process-wide variable. */ + if (!GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL)) { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + errno = ENAMETOOLONG; + else + errno = EINVAL; - /* According to MSDN, a return value equals to zero means a failure. */ - if (ret == 0 || ret > GIT_WIN_PATH_UTF16) - buffer = NULL; + return NULL; + } - else if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) { - buffer = NULL; + /* The path must exist. */ + if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) { errno = ENOENT; + return NULL; } - else if (buffer == NULL) { - int buffer_sz = WideCharToMultiByte( - CP_UTF8, 0, buffer_w, -1, NULL, 0, NULL, NULL); - - if (!buffer_sz || - !(buffer = (char *)git__malloc(buffer_sz)) || - !WideCharToMultiByte( - CP_UTF8, 0, buffer_w, -1, buffer, buffer_sz, NULL, NULL)) - { - git__free(buffer); - buffer = NULL; - } + if (!buffer && !(buffer = git__malloc(GIT_WIN_PATH_UTF8))) { + errno = ENOMEM; + return NULL; } - else if (!WideCharToMultiByte( - CP_UTF8, 0, buffer_w, -1, buffer, GIT_PATH_MAX, NULL, NULL)) - buffer = NULL; + /* Convert the path to UTF-8. If the caller provided a buffer, then it + * is assumed to be GIT_WIN_PATH_UTF8 characters in size. If it isn't, + * then we may overflow. */ + if (git_win32_path_to_utf8(buffer, buffer_w) < 0) + return NULL; - if (buffer) - git_path_mkposix(buffer); + git_path_mkposix(buffer); return buffer; } int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr) { -#ifdef _MSC_VER +#if defined(_MSC_VER) int len; - if (count == 0 || - (len = _vsnprintf_s(buffer, count, _TRUNCATE, format, argptr)) < 0) + if (count == 0) + return _vscprintf(format, argptr); + + #if _MSC_VER >= 1500 + len = _vsnprintf_s(buffer, count, _TRUNCATE, format, argptr); + #else + len = _vsnprintf(buffer, count, format, argptr); + #endif + + if (len < 0) return _vscprintf(format, argptr); return len; @@ -433,11 +586,10 @@ return r; } -extern int p_creat(const char *path, mode_t mode); - +/* TODO: wut? */ int p_mkstemp(char *tmp_path) { -#if defined(_MSC_VER) +#if defined(_MSC_VER) && _MSC_VER >= 1500 if (_mktemp_s(tmp_path, strlen(tmp_path) + 1) != 0) return -1; #else @@ -445,32 +597,75 @@ return -1; #endif - return p_creat(tmp_path, 0744); //-V536 + return p_open(tmp_path, O_RDWR | O_CREAT | O_EXCL, 0744); //-V536 } -int p_setenv(const char* name, const char* value, int overwrite) +int p_access(const char* path, mode_t mode) { - if (overwrite != 1) + git_win32_path buf; + + if (git_win32_path_from_utf8(buf, path) < 0) return -1; - return (SetEnvironmentVariableA(name, value) == 0 ? -1 : 0); + return _waccess(buf, mode); } -int p_access(const char* path, mode_t mode) +static int ensure_writable(wchar_t *fpath) { - git_win32_path buf; - git_win32_path_from_c(buf, path); - return _waccess(buf, mode); + DWORD attrs; + + attrs = GetFileAttributesW(fpath); + if (attrs == INVALID_FILE_ATTRIBUTES) { + if (GetLastError() == ERROR_FILE_NOT_FOUND) + return 0; + + giterr_set(GITERR_OS, "failed to get attributes"); + return -1; + } + + if (!(attrs & FILE_ATTRIBUTE_READONLY)) + return 0; + + attrs &= ~FILE_ATTRIBUTE_READONLY; + if (!SetFileAttributesW(fpath, attrs)) { + giterr_set(GITERR_OS, "failed to set attributes"); + return -1; + } + + return 0; } int p_rename(const char *from, const char *to) { git_win32_path wfrom; git_win32_path wto; + int rename_tries; + int rename_succeeded; + int error; - git_win32_path_from_c(wfrom, from); - git_win32_path_from_c(wto, to); - return MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1; + if (git_win32_path_from_utf8(wfrom, from) < 0 || + git_win32_path_from_utf8(wto, to) < 0) + return -1; + + /* wait up to 50ms if file is locked by another thread or process */ + rename_tries = 0; + rename_succeeded = 0; + while (rename_tries < 10) { + if (ensure_writable(wto) == 0 && + MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0) { + rename_succeeded = 1; + break; + } + + error = GetLastError(); + if (error == ERROR_SHARING_VIOLATION || error == ERROR_ACCESS_DENIED) { + Sleep(5); + rename_tries++; + } else + break; + } + + return rename_succeeded ? 0 : -1; } int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags) diff -Nru libgit2-0.20.0/src/win32/precompiled.h libgit2-0.22.2/src/win32/precompiled.h --- libgit2-0.20.0/src/win32/precompiled.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/win32/precompiled.h 2015-03-24 16:10:45.000000000 +0000 @@ -1,6 +1,3 @@ -#include "git2.h" -#include "common.h" - #include #include #include @@ -9,6 +6,7 @@ #include #include #include +#include #include #include @@ -20,3 +18,6 @@ #ifdef GIT_THREADS #include "win32/pthread.h" #endif + +#include "git2.h" +#include "common.h" diff -Nru libgit2-0.20.0/src/win32/pthread.c libgit2-0.22.2/src/win32/pthread.c --- libgit2-0.20.0/src/win32/pthread.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/win32/pthread.c 2015-03-24 16:10:45.000000000 +0000 @@ -8,32 +8,64 @@ #include "pthread.h" #include "../global.h" -int pthread_create( - pthread_t *GIT_RESTRICT thread, +#define CLEAN_THREAD_EXIT 0x6F012842 + +/* The thread procedure stub used to invoke the caller's procedure + * and capture the return value for later collection. Windows will + * only hold a DWORD, but we need to be able to store an entire + * void pointer. This requires the indirection. */ +static DWORD WINAPI git_win32__threadproc(LPVOID lpParameter) +{ + git_win32_thread *thread = lpParameter; + + thread->result = thread->proc(thread->param); + + return CLEAN_THREAD_EXIT; +} + +int git_win32__thread_create( + git_win32_thread *GIT_RESTRICT thread, const pthread_attr_t *GIT_RESTRICT attr, void *(*start_routine)(void*), void *GIT_RESTRICT arg) { GIT_UNUSED(attr); - *thread = CreateThread( - NULL, 0, (LPTHREAD_START_ROUTINE)start_routine, arg, 0, NULL); - return *thread ? 0 : -1; -} - -int pthread_join(pthread_t thread, void **value_ptr) -{ - DWORD ret = WaitForSingleObject(thread, INFINITE); - - if (ret == WAIT_OBJECT_0) { - if (value_ptr != NULL) { - *value_ptr = NULL; - GetExitCodeThread(thread, (void *)value_ptr); - } - CloseHandle(thread); - return 0; + + thread->result = NULL; + thread->param = arg; + thread->proc = start_routine; + thread->thread = CreateThread( + NULL, 0, git_win32__threadproc, thread, 0, NULL); + + return thread->thread ? 0 : -1; +} + +int git_win32__thread_join( + git_win32_thread *thread, + void **value_ptr) +{ + DWORD exit; + + if (WaitForSingleObject(thread->thread, INFINITE) != WAIT_OBJECT_0) + return -1; + + if (!GetExitCodeThread(thread->thread, &exit)) { + CloseHandle(thread->thread); + return -1; } - return -1; + /* Check for the thread having exited uncleanly. If exit was unclean, + * then we don't have a return value to give back to the caller. */ + if (exit != CLEAN_THREAD_EXIT) { + assert(false); + thread->result = NULL; + } + + if (value_ptr) + *value_ptr = thread->result; + + CloseHandle(thread->thread); + return 0; } int pthread_mutex_init( @@ -144,9 +176,6 @@ return n ? n : 1; } - -static HINSTANCE win32_kernel32_dll; - typedef void (WINAPI *win32_srwlock_fn)(GIT_SRWLOCK *); static win32_srwlock_fn win32_srwlock_initialize; @@ -159,7 +188,7 @@ pthread_rwlock_t *GIT_RESTRICT lock, const pthread_rwlockattr_t *GIT_RESTRICT attr) { - (void)attr; + GIT_UNUSED(attr); if (win32_srwlock_initialize) win32_srwlock_initialize(&lock->native.srwl); @@ -217,38 +246,22 @@ return 0; } - -static void win32_pthread_shutdown(void) -{ - if (win32_kernel32_dll) { - FreeLibrary(win32_kernel32_dll); - win32_kernel32_dll = NULL; - } -} - int win32_pthread_initialize(void) { - if (win32_kernel32_dll) - return 0; + HMODULE hModule = GetModuleHandleW(L"kernel32"); - win32_kernel32_dll = LoadLibrary("Kernel32.dll"); - if (!win32_kernel32_dll) { - giterr_set(GITERR_OS, "Could not load Kernel32.dll!"); - return -1; + if (hModule) { + win32_srwlock_initialize = (win32_srwlock_fn) + GetProcAddress(hModule, "InitializeSRWLock"); + win32_srwlock_acquire_shared = (win32_srwlock_fn) + GetProcAddress(hModule, "AcquireSRWLockShared"); + win32_srwlock_release_shared = (win32_srwlock_fn) + GetProcAddress(hModule, "ReleaseSRWLockShared"); + win32_srwlock_acquire_exclusive = (win32_srwlock_fn) + GetProcAddress(hModule, "AcquireSRWLockExclusive"); + win32_srwlock_release_exclusive = (win32_srwlock_fn) + GetProcAddress(hModule, "ReleaseSRWLockExclusive"); } - win32_srwlock_initialize = (win32_srwlock_fn) - GetProcAddress(win32_kernel32_dll, "InitializeSRWLock"); - win32_srwlock_acquire_shared = (win32_srwlock_fn) - GetProcAddress(win32_kernel32_dll, "AcquireSRWLockShared"); - win32_srwlock_release_shared = (win32_srwlock_fn) - GetProcAddress(win32_kernel32_dll, "ReleaseSRWLockShared"); - win32_srwlock_acquire_exclusive = (win32_srwlock_fn) - GetProcAddress(win32_kernel32_dll, "AcquireSRWLockExclusive"); - win32_srwlock_release_exclusive = (win32_srwlock_fn) - GetProcAddress(win32_kernel32_dll, "ReleaseSRWLockExclusive"); - - git__on_shutdown(win32_pthread_shutdown); - return 0; } diff -Nru libgit2-0.20.0/src/win32/pthread.h libgit2-0.22.2/src/win32/pthread.h --- libgit2-0.20.0/src/win32/pthread.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/win32/pthread.h 2015-03-24 16:10:45.000000000 +0000 @@ -16,13 +16,19 @@ # define GIT_RESTRICT __restrict__ #endif +typedef struct { + HANDLE thread; + void *(*proc)(void *); + void *param; + void *result; +} git_win32_thread; + typedef int pthread_mutexattr_t; typedef int pthread_condattr_t; typedef int pthread_attr_t; typedef int pthread_rwlockattr_t; typedef CRITICAL_SECTION pthread_mutex_t; -typedef HANDLE pthread_t; typedef HANDLE pthread_cond_t; typedef struct { void *Ptr; } GIT_SRWLOCK; @@ -36,13 +42,26 @@ #define PTHREAD_MUTEX_INITIALIZER {(void*)-1} -int pthread_create( - pthread_t *GIT_RESTRICT thread, - const pthread_attr_t *GIT_RESTRICT attr, - void *(*start_routine)(void*), - void *GIT_RESTRICT arg); +int git_win32__thread_create( + git_win32_thread *GIT_RESTRICT, + const pthread_attr_t *GIT_RESTRICT, + void *(*) (void *), + void *GIT_RESTRICT); + +int git_win32__thread_join( + git_win32_thread *, + void **); + +#ifdef GIT_THREADS + +typedef git_win32_thread git_thread; + +#define git_thread_create(git_thread_ptr, attr, start_routine, arg) \ + git_win32__thread_create(git_thread_ptr, attr, start_routine, arg) +#define git_thread_join(git_thread_ptr, status) \ + git_win32__thread_join(git_thread_ptr, status) -int pthread_join(pthread_t, void **); +#endif int pthread_mutex_init( pthread_mutex_t *GIT_RESTRICT mutex, diff -Nru libgit2-0.20.0/src/win32/reparse.h libgit2-0.22.2/src/win32/reparse.h --- libgit2-0.20.0/src/win32/reparse.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/win32/reparse.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,57 @@ +/* +* 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_git_win32_reparse_h__ +#define INCLUDE_git_win32_reparse_h__ + +/* This structure is defined on MSDN at +* http://msdn.microsoft.com/en-us/library/windows/hardware/ff552012(v=vs.85).aspx +* +* It was formerly included in the Windows 2000 SDK and remains defined in +* MinGW, so we must define it with a silly name to avoid conflicting. +*/ +typedef struct _GIT_REPARSE_DATA_BUFFER { + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union { + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + }; +} GIT_REPARSE_DATA_BUFFER; + +#define REPARSE_DATA_HEADER_SIZE 8 +#define REPARSE_DATA_MOUNTPOINT_HEADER_SIZE 8 +#define REPARSE_DATA_UNION_SIZE 12 + +/* Missing in MinGW */ +#ifndef FSCTL_GET_REPARSE_POINT +# define FSCTL_GET_REPARSE_POINT 0x000900a8 +#endif + +/* Missing in MinGW */ +#ifndef FSCTL_SET_REPARSE_POINT +# define FSCTL_SET_REPARSE_POINT 0x000900a4 +#endif + +#endif diff -Nru libgit2-0.20.0/src/win32/utf-conv.c libgit2-0.22.2/src/win32/utf-conv.c --- libgit2-0.20.0/src/win32/utf-conv.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/win32/utf-conv.c 2015-03-24 16:10:45.000000000 +0000 @@ -8,74 +8,161 @@ #include "common.h" #include "utf-conv.h" -#define U16_LEAD(c) (wchar_t)(((c)>>10)+0xd7c0) -#define U16_TRAIL(c) (wchar_t)(((c)&0x3ff)|0xdc00) +#ifndef WC_ERR_INVALID_CHARS +# define WC_ERR_INVALID_CHARS 0x80 +#endif -#if 0 -void git__utf8_to_16(wchar_t *dest, size_t length, const char *src) +GIT_INLINE(DWORD) get_wc_flags(void) { - wchar_t *pDest = dest; - uint32_t ch; - const uint8_t* pSrc = (uint8_t*) src; - - assert(dest && src && length); - - length--; - - while(*pSrc && length > 0) { - ch = *pSrc++; - length--; - - if(ch < 0xc0) { - /* - * ASCII, or a trail byte in lead position which is treated like - * a single-byte sequence for better character boundary - * resynchronization after illegal sequences. - */ - *pDest++ = (wchar_t)ch; - continue; - } else if(ch < 0xe0) { /* U+0080..U+07FF */ - if (pSrc[0]) { - /* 0x3080 = (0xc0 << 6) + 0x80 */ - *pDest++ = (wchar_t)((ch << 6) + *pSrc++ - 0x3080); - continue; - } - } else if(ch < 0xf0) { /* U+0800..U+FFFF */ - if (pSrc[0] && pSrc[1]) { - /* no need for (ch & 0xf) because the upper bits are truncated after <<12 in the cast to (UChar) */ - /* 0x2080 = (0x80 << 6) + 0x80 */ - ch = (ch << 12) + (*pSrc++ << 6); - *pDest++ = (wchar_t)(ch + *pSrc++ - 0x2080); - continue; - } - } else /* f0..f4 */ { /* U+10000..U+10FFFF */ - if (length >= 1 && pSrc[0] && pSrc[1] && pSrc[2]) { - /* 0x3c82080 = (0xf0 << 18) + (0x80 << 12) + (0x80 << 6) + 0x80 */ - ch = (ch << 18) + (*pSrc++ << 12); - ch += *pSrc++ << 6; - ch += *pSrc++ - 0x3c82080; - *(pDest++) = U16_LEAD(ch); - *(pDest++) = U16_TRAIL(ch); - length--; /* two bytes for this character */ - continue; - } - } - - /* truncated character at the end */ - *pDest++ = 0xfffd; - break; + static char inited = 0; + static DWORD flags; + + /* Invalid code point check supported on Vista+ only */ + if (!inited) { + flags = git_has_win32_version(6, 0, 0) ? WC_ERR_INVALID_CHARS : 0; + inited = 1; } - *pDest++ = 0x0; + return flags; +} + +GIT_INLINE(void) git__set_errno(void) +{ + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) + errno = ENAMETOOLONG; + else + errno = EINVAL; } -#endif -int git__utf8_to_16(wchar_t * dest, size_t dest_size, const char *src) +/** + * Converts a UTF-8 string to wide characters. + * + * @param dest The buffer to receive the wide string. + * @param dest_size The size of the buffer, in characters. + * @param src The UTF-8 string to convert. + * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure + */ +int git__utf8_to_16(wchar_t *dest, size_t dest_size, const char *src) { - return MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, (int)dest_size); + int len; + + /* Length of -1 indicates NULL termination of the input string. Subtract 1 from the result to + * turn 0 into -1 (an error code) and to not count the NULL terminator as part of the string's + * length. MultiByteToWideChar never returns int's minvalue, so underflow is not possible */ + if ((len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, dest, (int)dest_size) - 1) < 0) + git__set_errno(); + + return len; } +/** + * Converts a wide string to UTF-8. + * + * @param dest The buffer to receive the UTF-8 string. + * @param dest_size The size of the buffer, in bytes. + * @param src The wide string to convert. + * @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure + */ int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src) { - return WideCharToMultiByte(CP_UTF8, 0, src, -1, dest, (int)dest_size, NULL, NULL); + int len; + + /* Length of -1 indicates NULL termination of the input string. Subtract 1 from the result to + * turn 0 into -1 (an error code) and to not count the NULL terminator as part of the string's + * length. WideCharToMultiByte never returns int's minvalue, so underflow is not possible */ + if ((len = WideCharToMultiByte(CP_UTF8, get_wc_flags(), src, -1, dest, (int)dest_size, NULL, NULL) - 1) < 0) + git__set_errno(); + + return len; +} + +/** + * Converts a UTF-8 string to wide characters. + * Memory is allocated to hold the converted string. + * The caller is responsible for freeing the string with git__free. + * + * @param dest Receives a pointer to the wide string. + * @param src The UTF-8 string to convert. + * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure + */ +int git__utf8_to_16_alloc(wchar_t **dest, const char *src) +{ + int utf16_size; + + *dest = NULL; + + /* Length of -1 indicates NULL termination of the input string */ + utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, NULL, 0); + + if (!utf16_size) { + git__set_errno(); + return -1; + } + + *dest = git__malloc(utf16_size * sizeof(wchar_t)); + + if (!*dest) { + errno = ENOMEM; + return -1; + } + + utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, src, -1, *dest, utf16_size); + + if (!utf16_size) { + git__set_errno(); + + git__free(*dest); + *dest = NULL; + } + + /* Subtract 1 from the result to turn 0 into -1 (an error code) and to not count the NULL + * terminator as part of the string's length. MultiByteToWideChar never returns int's minvalue, + * so underflow is not possible */ + return utf16_size - 1; +} + +/** + * Converts a wide string to UTF-8. + * Memory is allocated to hold the converted string. + * The caller is responsible for freeing the string with git__free. + * + * @param dest Receives a pointer to the UTF-8 string. + * @param src The wide string to convert. + * @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure + */ +int git__utf16_to_8_alloc(char **dest, const wchar_t *src) +{ + int utf8_size; + DWORD dwFlags = get_wc_flags(); + + *dest = NULL; + + /* Length of -1 indicates NULL termination of the input string */ + utf8_size = WideCharToMultiByte(CP_UTF8, dwFlags, src, -1, NULL, 0, NULL, NULL); + + if (!utf8_size) { + git__set_errno(); + return -1; + } + + *dest = git__malloc(utf8_size); + + if (!*dest) { + errno = ENOMEM; + return -1; + } + + utf8_size = WideCharToMultiByte(CP_UTF8, dwFlags, src, -1, *dest, utf8_size, NULL, NULL); + + if (!utf8_size) { + git__set_errno(); + + git__free(*dest); + *dest = NULL; + } + + /* Subtract 1 from the result to turn 0 into -1 (an error code) and to not count the NULL + * terminator as part of the string's length. MultiByteToWideChar never returns int's minvalue, + * so underflow is not possible */ + return utf8_size - 1; } diff -Nru libgit2-0.20.0/src/win32/utf-conv.h libgit2-0.22.2/src/win32/utf-conv.h --- libgit2-0.20.0/src/win32/utf-conv.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/win32/utf-conv.h 2015-03-24 16:10:45.000000000 +0000 @@ -10,29 +10,46 @@ #include #include "common.h" -/* Maximum characters in a Windows path plus one for NUL byte */ -#define GIT_WIN_PATH_UTF16 (260 + 1) - -/* Maximum bytes necessary to convert a full-length UTF16 path to UTF8 */ -#define GIT_WIN_PATH_UTF8 (260 * 4 + 1) - -typedef wchar_t git_win32_path[GIT_WIN_PATH_UTF16]; - -typedef char git_win32_path_as_utf8[GIT_WIN_PATH_UTF8]; - -/* dest_size is the size of dest in wchar_t's */ -int git__utf8_to_16(wchar_t * dest, size_t dest_size, const char *src); -/* dest_size is the size of dest in char's */ +/** + * Converts a UTF-8 string to wide characters. + * + * @param dest The buffer to receive the wide string. + * @param dest_size The size of the buffer, in characters. + * @param src The UTF-8 string to convert. + * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure + */ +int git__utf8_to_16(wchar_t *dest, size_t dest_size, const char *src); + +/** + * Converts a wide string to UTF-8. + * + * @param dest The buffer to receive the UTF-8 string. + * @param dest_size The size of the buffer, in bytes. + * @param src The wide string to convert. + * @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure + */ int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src); -GIT_INLINE(int) git_win32_path_from_c(git_win32_path dest, const char *src) -{ - return git__utf8_to_16(dest, GIT_WIN_PATH_UTF16, src); -} - -GIT_INLINE(int) git_win32_path_to_c(git_win32_path_as_utf8 dest, const wchar_t *src) -{ - return git__utf16_to_8(dest, GIT_WIN_PATH_UTF8, src); -} +/** + * Converts a UTF-8 string to wide characters. + * Memory is allocated to hold the converted string. + * The caller is responsible for freeing the string with git__free. + * + * @param dest Receives a pointer to the wide string. + * @param src The UTF-8 string to convert. + * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure + */ +int git__utf8_to_16_alloc(wchar_t **dest, const char *src); + +/** + * Converts a wide string to UTF-8. + * Memory is allocated to hold the converted string. + * The caller is responsible for freeing the string with git__free. + * + * @param dest Receives a pointer to the UTF-8 string. + * @param src The wide string to convert. + * @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure + */ +int git__utf16_to_8_alloc(char **dest, const wchar_t *src); #endif diff -Nru libgit2-0.20.0/src/win32/w32_util.c libgit2-0.22.2/src/win32/w32_util.c --- libgit2-0.20.0/src/win32/w32_util.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/win32/w32_util.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,139 @@ +/* + * 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 "w32_util.h" + +/** + * Creates a FindFirstFile(Ex) filter string from a UTF-8 path. + * The filter string enumerates all items in the directory. + * + * @param dest The buffer to receive the filter string. + * @param src The UTF-8 path of the directory to enumerate. + * @return True if the filter string was created successfully; false otherwise + */ +bool git_win32__findfirstfile_filter(git_win32_path dest, const char *src) +{ + static const wchar_t suffix[] = L"\\*"; + int len = git_win32_path_from_utf8(dest, src); + + /* Ensure the path was converted */ + if (len < 0) + return false; + + /* Ensure that the path does not end with a trailing slash, + * because we're about to add one. Don't rely our trim_end + * helper, because we want to remove the backslash even for + * drive letter paths, in this case. */ + if (len > 0 && + (dest[len - 1] == L'/' || dest[len - 1] == L'\\')) { + dest[len - 1] = L'\0'; + len--; + } + + /* Ensure we have enough room to add the suffix */ + if ((size_t)len >= GIT_WIN_PATH_UTF16 - CONST_STRLEN(suffix)) + return false; + + wcscat(dest, suffix); + return true; +} + +/** + * Ensures the given path (file or folder) has the +H (hidden) attribute set. + * + * @param path The path which should receive the +H bit. + * @return 0 on success; -1 on failure + */ +int git_win32__sethidden(const char *path) +{ + git_win32_path buf; + DWORD attrs; + + if (git_win32_path_from_utf8(buf, path) < 0) + return -1; + + attrs = GetFileAttributesW(buf); + + /* Ensure the path exists */ + if (attrs == INVALID_FILE_ATTRIBUTES) + return -1; + + /* If the item isn't already +H, add the bit */ + if ((attrs & FILE_ATTRIBUTE_HIDDEN) == 0 && + !SetFileAttributesW(buf, attrs | FILE_ATTRIBUTE_HIDDEN)) + return -1; + + return 0; +} + +/** + * Removes any trailing backslashes from a path, except in the case of a drive + * letter path (C:\, D:\, etc.). This function cannot fail. + * + * @param path The path which should be trimmed. + * @return The length of the modified string (<= the input length) + */ +size_t git_win32__path_trim_end(wchar_t *str, size_t len) +{ + while (1) { + if (!len || str[len - 1] != L'\\') + break; + + /* Don't trim backslashes from drive letter paths, which + * are 3 characters long and of the form C:\, D:\, etc. */ + if (len == 3 && git_win32__isalpha(str[0]) && str[1] == ':') + break; + + len--; + } + + str[len] = L'\0'; + + return len; +} + +/** + * Removes any of the following namespace prefixes from a path, + * if found: "\??\", "\\?\", "\\?\UNC\". This function cannot fail. + * + * @param path The path which should be converted. + * @return The length of the modified string (<= the input length) + */ +size_t git_win32__canonicalize_path(wchar_t *str, size_t len) +{ + static const wchar_t dosdevices_prefix[] = L"\\\?\?\\"; + static const wchar_t nt_prefix[] = L"\\\\?\\"; + static const wchar_t unc_prefix[] = L"UNC\\"; + size_t to_advance = 0; + + /* "\??\" -- DOS Devices prefix */ + if (len >= CONST_STRLEN(dosdevices_prefix) && + !wcsncmp(str, dosdevices_prefix, CONST_STRLEN(dosdevices_prefix))) { + to_advance += CONST_STRLEN(dosdevices_prefix); + len -= CONST_STRLEN(dosdevices_prefix); + } + /* "\\?\" -- NT namespace prefix */ + else if (len >= CONST_STRLEN(nt_prefix) && + !wcsncmp(str, nt_prefix, CONST_STRLEN(nt_prefix))) { + to_advance += CONST_STRLEN(nt_prefix); + len -= CONST_STRLEN(nt_prefix); + } + + /* "\??\UNC\", "\\?\UNC\" -- UNC prefix */ + if (to_advance && len >= CONST_STRLEN(unc_prefix) && + !wcsncmp(str + to_advance, unc_prefix, CONST_STRLEN(unc_prefix))) { + to_advance += CONST_STRLEN(unc_prefix); + len -= CONST_STRLEN(unc_prefix); + } + + if (to_advance) { + memmove(str, str + to_advance, len * sizeof(wchar_t)); + str[len] = L'\0'; + } + + return git_win32__path_trim_end(str, len); +} diff -Nru libgit2-0.20.0/src/win32/w32_util.h libgit2-0.22.2/src/win32/w32_util.h --- libgit2-0.20.0/src/win32/w32_util.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/win32/w32_util.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,55 @@ +/* + * 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_w32_util_h__ +#define INCLUDE_w32_util_h__ + +#include "utf-conv.h" +#include "path_w32.h" + +GIT_INLINE(bool) git_win32__isalpha(wchar_t c) +{ + return ((c >= L'A' && c <= L'Z') || (c >= L'a' && c <= L'z')); +} + +/** + * Creates a FindFirstFile(Ex) filter string from a UTF-8 path. + * The filter string enumerates all items in the directory. + * + * @param dest The buffer to receive the filter string. + * @param src The UTF-8 path of the directory to enumerate. + * @return True if the filter string was created successfully; false otherwise + */ +bool git_win32__findfirstfile_filter(git_win32_path dest, const char *src); + +/** + * Ensures the given path (file or folder) has the +H (hidden) attribute set. + * + * @param path The path which should receive the +H bit. + * @return 0 on success; -1 on failure + */ +int git_win32__sethidden(const char *path); + +/** + * Removes any trailing backslashes from a path, except in the case of a drive + * letter path (C:\, D:\, etc.). This function cannot fail. + * + * @param path The path which should be trimmed. + * @return The length of the modified string (<= the input length) + */ +size_t git_win32__path_trim_end(wchar_t *str, size_t len); + +/** + * Removes any of the following namespace prefixes from a path, + * if found: "\??\", "\\?\", "\\?\UNC\". This function cannot fail. + * + * @param path The path which should be converted. + * @return The length of the modified string (<= the input length) + */ +size_t git_win32__canonicalize_path(wchar_t *str, size_t len); + +#endif diff -Nru libgit2-0.20.0/src/xdiff/xdiffi.c libgit2-0.22.2/src/xdiff/xdiffi.c --- libgit2-0.20.0/src/xdiff/xdiffi.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/xdiff/xdiffi.c 2015-03-24 16:10:45.000000000 +0000 @@ -490,7 +490,7 @@ /* * Try to move back the possibly merged group of changes, to match - * the recorded postion in the other file. + * the recorded position in the other file. */ while (ixref < ix) { rchg[--ixs] = 1; diff -Nru libgit2-0.20.0/src/xdiff/xhistogram.c libgit2-0.22.2/src/xdiff/xhistogram.c --- libgit2-0.20.0/src/xdiff/xhistogram.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/src/xdiff/xhistogram.c 2015-03-24 16:10:45.000000000 +0000 @@ -55,7 +55,7 @@ struct record { unsigned int ptr, cnt; struct record *next; - } **records, /* an ocurrence */ + } **records, /* an occurrence */ **line_map; /* map of line to record chain */ chastore_t rcha; unsigned int *next_ptrs; diff -Nru libgit2-0.20.0/src/zstream.c libgit2-0.22.2/src/zstream.c --- libgit2-0.20.0/src/zstream.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/zstream.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,156 @@ +/* + * 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 + +#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) +{ + if (zs->zerr == Z_OK || zs->zerr == Z_STREAM_END) + return 0; + + if (zs->zerr == Z_MEM_ERROR) + giterr_set_oom(); + else if (zs->z.msg) + giterr_set(GITERR_ZLIB, zs->z.msg); + else + giterr_set(GITERR_ZLIB, "Unknown compression error"); + + return -1; +} + +int git_zstream_init(git_zstream *zstream) +{ + zstream->zerr = deflateInit(&zstream->z, Z_DEFAULT_COMPRESSION); + return zstream_seterr(zstream); +} + +void git_zstream_free(git_zstream *zstream) +{ + deflateEnd(&zstream->z); +} + +void git_zstream_reset(git_zstream *zstream) +{ + deflateReset(&zstream->z); + zstream->in = NULL; + zstream->in_len = 0; + zstream->zerr = Z_STREAM_END; +} + +int git_zstream_set_input(git_zstream *zstream, const void *in, size_t in_len) +{ + zstream->in = in; + zstream->in_len = in_len; + zstream->zerr = Z_OK; + return 0; +} + +bool git_zstream_done(git_zstream *zstream) +{ + return (!zstream->in_len && zstream->zerr == Z_STREAM_END); +} + +size_t git_zstream_suggest_output_len(git_zstream *zstream) +{ + if (zstream->in_len > ZSTREAM_BUFFER_SIZE) + return ZSTREAM_BUFFER_SIZE; + else if (zstream->in_len > ZSTREAM_BUFFER_MIN_EXTRA) + return zstream->in_len; + else + return ZSTREAM_BUFFER_MIN_EXTRA; +} + +int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream) +{ + int zflush = Z_FINISH; + size_t out_remain = *out_len; + + 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 */ + zstream->zerr = deflate(&zstream->z, zflush); + + if (zstream->zerr == Z_STREAM_ERROR) + return zstream_seterr(zstream); + + out_used = (out_queued - zstream->z.avail_out); + out_remain -= out_used; + out = ((char *)out) + out_used; + + in_used = (in_queued - zstream->z.avail_in); + zstream->in_len -= in_used; + zstream->in += in_used; + } + + /* either we finished the input or we did not flush the data */ + assert(zstream->in_len > 0 || zflush == Z_FINISH); + + /* set out_size to number of bytes actually written to output */ + *out_len = *out_len - out_remain; + + return 0; +} + +int git_zstream_deflatebuf(git_buf *out, const void *in, size_t in_len) +{ + git_zstream zs = GIT_ZSTREAM_INIT; + int error = 0; + + if ((error = git_zstream_init(&zs)) < 0) + return error; + + if ((error = git_zstream_set_input(&zs, in, in_len)) < 0) + goto done; + + while (!git_zstream_done(&zs)) { + size_t step = git_zstream_suggest_output_len(&zs), written; + + if ((error = git_buf_grow(out, out->size + step)) < 0) + goto done; + + written = out->asize - out->size; + + if ((error = git_zstream_get_output( + out->ptr + out->size, &written, &zs)) < 0) + goto done; + + out->size += written; + } + + /* NULL terminate for consistency if possible */ + if (out->size < out->asize) + out->ptr[out->size] = '\0'; + +done: + git_zstream_free(&zs); + return error; +} diff -Nru libgit2-0.20.0/src/zstream.h libgit2-0.22.2/src/zstream.h --- libgit2-0.20.0/src/zstream.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/src/zstream.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,39 @@ +/* + * 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_zstream_h__ +#define INCLUDE_zstream_h__ + +#include + +#include "common.h" +#include "buffer.h" + +typedef struct { + z_stream z; + const char *in; + size_t in_len; + int zerr; +} git_zstream; + +#define GIT_ZSTREAM_INIT {{0}} + +int git_zstream_init(git_zstream *zstream); +void git_zstream_free(git_zstream *zstream); + +int git_zstream_set_input(git_zstream *zstream, const void *in, size_t in_len); + +size_t git_zstream_suggest_output_len(git_zstream *zstream); + +int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream); + +bool git_zstream_done(git_zstream *zstream); + +void git_zstream_reset(git_zstream *zstream); + +int git_zstream_deflatebuf(git_buf *out, const void *in, size_t in_len); + +#endif /* INCLUDE_zstream_h__ */ diff -Nru libgit2-0.20.0/tests/attr/file.c libgit2-0.22.2/tests/attr/file.c --- libgit2-0.20.0/tests/attr/file.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/attr/file.c 2015-03-24 16:10:45.000000000 +0000 @@ -11,9 +11,9 @@ git_attr_assignment *assign; git_attr_rule *rule; - cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr0"))); + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr0"))); - cl_assert_equal_s(cl_fixture("attr/attr0"), file->key + 2); + cl_assert_equal_s(cl_fixture("attr/attr0"), file->entry->path); cl_assert(file->rules.length == 1); rule = get_rule(0); @@ -37,9 +37,9 @@ git_attr_rule *rule; git_attr_assignment *assign; - cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr1"))); + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr1"))); - cl_assert_equal_s(cl_fixture("attr/attr1"), file->key + 2); + cl_assert_equal_s(cl_fixture("attr/attr1"), file->entry->path); cl_assert(file->rules.length == 10); /* let's do a thorough check of this rule, then just verify @@ -121,9 +121,9 @@ git_attr_rule *rule; git_attr_assignment *assign; - cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr2"))); + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr2"))); - cl_assert_equal_s(cl_fixture("attr/attr2"), file->key + 2); + cl_assert_equal_s(cl_fixture("attr/attr2"), file->entry->path); cl_assert(file->rules.length == 11); check_one_assign(file, 0, 0, "pat0", "simple", EXPECT_TRUE, NULL); @@ -187,8 +187,8 @@ git_attr_rule *rule; git_attr_assignment *assign; - cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr3"))); - cl_assert_equal_s(cl_fixture("attr/attr3"), file->key + 2); + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr3"))); + cl_assert_equal_s(cl_fixture("attr/attr3"), file->entry->path); cl_assert(file->rules.length == 3); rule = get_rule(0); diff -Nru libgit2-0.20.0/tests/attr/ignore.c libgit2-0.22.2/tests/attr/ignore.c --- libgit2-0.20.0/tests/attr/ignore.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/attr/ignore.c 2015-03-24 16:10:45.000000000 +0000 @@ -16,13 +16,20 @@ g_repo = NULL; } -void assert_is_ignored(bool expected, const char *filepath) +static void assert_is_ignored_( + bool expected, const char *filepath, const char *file, int line) { - int is_ignored; + int is_ignored = 0; - cl_git_pass(git_ignore_path_is_ignored(&is_ignored, g_repo, filepath)); - cl_assert_equal_b(expected, is_ignored); + cl_git_pass_( + git_ignore_path_is_ignored(&is_ignored, g_repo, filepath), file, line); + + clar__assert_equal( + file, line, "expected != is_ignored", 1, "%d", + (int)(expected != 0), (int)(is_ignored != 0)); } +#define assert_is_ignored(expected, filepath) \ + assert_is_ignored_(expected, filepath, __FILE__, __LINE__) void test_attr_ignore__honor_temporary_rules(void) { @@ -54,6 +61,76 @@ assert_is_ignored(true, "NewFolder/NewFolder/File.txt"); } +void test_attr_ignore__full_paths(void) +{ + cl_git_rewritefile("attr/.gitignore", "Folder/*/Contained"); + + assert_is_ignored(true, "Folder/Middle/Contained"); + assert_is_ignored(false, "Folder/Middle/More/More/Contained"); + + cl_git_rewritefile("attr/.gitignore", "Folder/**/Contained"); + + assert_is_ignored(true, "Folder/Middle/Contained"); + assert_is_ignored(true, "Folder/Middle/More/More/Contained"); + + cl_git_rewritefile("attr/.gitignore", "Folder/**/Contained/*/Child"); + + assert_is_ignored(true, "Folder/Middle/Contained/Happy/Child"); + assert_is_ignored(false, "Folder/Middle/Contained/Not/Happy/Child"); + assert_is_ignored(true, "Folder/Middle/More/More/Contained/Happy/Child"); + assert_is_ignored(false, "Folder/Middle/More/More/Contained/Not/Happy/Child"); +} + +void test_attr_ignore__more_starstar_cases(void) +{ + cl_must_pass(p_unlink("attr/.gitignore")); + cl_git_mkfile( + "attr/dir/.gitignore", + "sub/**/*.html\n"); + + assert_is_ignored(false, "aaa.html"); + assert_is_ignored(false, "dir"); + assert_is_ignored(false, "dir/sub"); + assert_is_ignored(true, "dir/sub/sub2/aaa.html"); + assert_is_ignored(true, "dir/sub/aaa.html"); + assert_is_ignored(false, "dir/aaa.html"); + assert_is_ignored(false, "sub"); + assert_is_ignored(false, "sub/aaa.html"); + assert_is_ignored(false, "sub/sub2/aaa.html"); +} + +void test_attr_ignore__leading_stars(void) +{ + cl_git_rewritefile( + "attr/.gitignore", + "*/onestar\n" + "**/twostars\n" + "*/parent1/kid1/*\n" + "**/parent2/kid2/*\n"); + + assert_is_ignored(true, "dir1/onestar"); + assert_is_ignored(true, "dir1/onestar/child"); /* in ignored dir */ + assert_is_ignored(false, "dir1/dir2/onestar"); + + assert_is_ignored(true, "dir1/twostars"); + assert_is_ignored(true, "dir1/twostars/child"); /* in ignored dir */ + assert_is_ignored(true, "dir1/dir2/twostars"); + assert_is_ignored(true, "dir1/dir2/twostars/child"); /* in ignored dir */ + assert_is_ignored(true, "dir1/dir2/dir3/twostars"); + + assert_is_ignored(true, "dir1/parent1/kid1/file"); + assert_is_ignored(true, "dir1/parent1/kid1/file/inside/parent"); + assert_is_ignored(false, "dir1/dir2/parent1/kid1/file"); + assert_is_ignored(false, "dir1/parent1/file"); + assert_is_ignored(false, "dir1/kid1/file"); + + assert_is_ignored(true, "dir1/parent2/kid2/file"); + assert_is_ignored(true, "dir1/parent2/kid2/file/inside/parent"); + assert_is_ignored(true, "dir1/dir2/parent2/kid2/file"); + assert_is_ignored(true, "dir1/dir2/dir3/parent2/kid2/file"); + assert_is_ignored(false, "dir1/parent2/file"); + assert_is_ignored(false, "dir1/kid2/file"); +} void test_attr_ignore__skip_gitignore_directory(void) { @@ -71,22 +148,17 @@ void test_attr_ignore__expand_tilde_to_homedir(void) { - git_buf path = GIT_BUF_INIT; git_config *cfg; assert_is_ignored(false, "example.global_with_tilde"); - /* construct fake home with fake global excludes */ - - cl_must_pass(p_mkdir("home", 0777)); - cl_git_pass(git_path_prettify(&path, "home", NULL)); - cl_git_pass(git_libgit2_opts( - GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); + cl_fake_home(); - cl_git_mkfile("home/globalexcludes", "# found me\n*.global_with_tilde\n"); + /* construct fake home with fake global excludes */ + cl_git_mkfile("home/globalexclude", "# found me\n*.global_with_tilde\n"); cl_git_pass(git_repository_config(&cfg, g_repo)); - cl_git_pass(git_config_set_string(cfg, "core.excludesfile", "~/globalexcludes")); + cl_git_pass(git_config_set_string(cfg, "core.excludesfile", "~/globalexclude")); git_config_free(cfg); git_attr_cache_flush(g_repo); /* must reset to pick up change */ @@ -95,8 +167,9 @@ cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES)); - cl_git_pass(git_libgit2_opts( - GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL)); + cl_fake_home_cleanup(NULL); - git_buf_free(&path); + git_attr_cache_flush(g_repo); /* must reset to pick up change */ + + assert_is_ignored(false, "example.global_with_tilde"); } diff -Nru libgit2-0.20.0/tests/attr/lookup.c libgit2-0.22.2/tests/attr/lookup.c --- libgit2-0.20.0/tests/attr/lookup.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/attr/lookup.c 2015-03-24 16:10:45.000000000 +0000 @@ -9,8 +9,8 @@ git_attr_path path; const char *value = NULL; - cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr0"))); - cl_assert_equal_s(cl_fixture("attr/attr0"), file->key + 2); + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr0"))); + cl_assert_equal_s(cl_fixture("attr/attr0"), file->entry->path); cl_assert(file->rules.length == 1); cl_git_pass(git_attr_path__init(&path, "test", NULL)); @@ -129,8 +129,8 @@ { NULL, NULL, 0, NULL } }; - cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr1"))); - cl_assert_equal_s(cl_fixture("attr/attr1"), file->key + 2); + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr1"))); + cl_assert_equal_s(cl_fixture("attr/attr1"), file->entry->path); cl_assert(file->rules.length == 10); cl_git_pass(git_attr_path__init(&path, "/testing/for/pat0", NULL)); @@ -190,7 +190,7 @@ { NULL, NULL, 0, NULL } }; - cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr2"))); + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr2"))); cl_assert(file->rules.length == 11); run_test_cases(file, cases, 0); @@ -225,7 +225,7 @@ { NULL, NULL, 0, NULL } }; - cl_git_pass(git_attr_file__new_and_load(&file, cl_fixture("attr/attr3"))); + cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr3"))); cl_assert(file->rules.length == 3); run_test_cases(file, cases, 0); @@ -250,9 +250,9 @@ { NULL, NULL, 0, NULL } }; - cl_git_pass(git_attr_file__new(&file, 0, NULL, NULL)); + cl_git_pass(git_attr_file__new(&file, NULL, 0)); - cl_git_pass(git_attr_file__parse_buffer(NULL, NULL, "a* foo\nabc bar\n* baz", file)); + cl_git_pass(git_attr_file__parse_buffer(NULL, file, "a* foo\nabc bar\n* baz")); cl_assert(file->rules.length == 3); diff -Nru libgit2-0.20.0/tests/attr/repo.c libgit2-0.22.2/tests/attr/repo.c --- libgit2-0.20.0/tests/attr/repo.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/attr/repo.c 2015-03-24 16:10:45.000000000 +0000 @@ -4,16 +4,12 @@ #include "attr.h" #include "attr_expect.h" +#include "git2/sys/repository.h" static git_repository *g_repo = NULL; void test_attr_repo__initialize(void) { - /* Before each test, instantiate the attr repo from the fixtures and - * rename the .gitted to .git so it is a repo with a working dir. - * Also rename gitattributes to .gitattributes, because it contains - * macro definitions which are only allowed in the root. - */ g_repo = cl_git_sandbox_init("attr"); } @@ -23,54 +19,82 @@ g_repo = NULL; } +static struct attr_expected get_one_test_cases[] = { + { "root_test1", "repoattr", EXPECT_TRUE, NULL }, + { "root_test1", "rootattr", EXPECT_TRUE, NULL }, + { "root_test1", "missingattr", EXPECT_UNDEFINED, NULL }, + { "root_test1", "subattr", EXPECT_UNDEFINED, NULL }, + { "root_test1", "negattr", EXPECT_UNDEFINED, NULL }, + { "root_test2", "repoattr", EXPECT_TRUE, NULL }, + { "root_test2", "rootattr", EXPECT_FALSE, NULL }, + { "root_test2", "missingattr", EXPECT_UNDEFINED, NULL }, + { "root_test2", "multiattr", EXPECT_FALSE, NULL }, + { "root_test3", "repoattr", EXPECT_TRUE, NULL }, + { "root_test3", "rootattr", EXPECT_UNDEFINED, NULL }, + { "root_test3", "multiattr", EXPECT_STRING, "3" }, + { "root_test3", "multi2", EXPECT_UNDEFINED, NULL }, + { "sub/subdir_test1", "repoattr", EXPECT_TRUE, NULL }, + { "sub/subdir_test1", "rootattr", EXPECT_TRUE, NULL }, + { "sub/subdir_test1", "missingattr", EXPECT_UNDEFINED, NULL }, + { "sub/subdir_test1", "subattr", EXPECT_STRING, "yes" }, + { "sub/subdir_test1", "negattr", EXPECT_FALSE, NULL }, + { "sub/subdir_test1", "another", EXPECT_UNDEFINED, NULL }, + { "sub/subdir_test2.txt", "repoattr", EXPECT_TRUE, NULL }, + { "sub/subdir_test2.txt", "rootattr", EXPECT_TRUE, NULL }, + { "sub/subdir_test2.txt", "missingattr", EXPECT_UNDEFINED, NULL }, + { "sub/subdir_test2.txt", "subattr", EXPECT_STRING, "yes" }, + { "sub/subdir_test2.txt", "negattr", EXPECT_FALSE, NULL }, + { "sub/subdir_test2.txt", "another", EXPECT_STRING, "zero" }, + { "sub/subdir_test2.txt", "reposub", EXPECT_TRUE, NULL }, + { "sub/sub/subdir.txt", "another", EXPECT_STRING, "one" }, + { "sub/sub/subdir.txt", "reposubsub", EXPECT_TRUE, NULL }, + { "sub/sub/subdir.txt", "reposub", EXPECT_UNDEFINED, NULL }, + { "does-not-exist", "foo", EXPECT_STRING, "yes" }, + { "sub/deep/file", "deepdeep", EXPECT_TRUE, NULL }, + { "sub/sub/d/no", "test", EXPECT_STRING, "a/b/d/*" }, + { "sub/sub/d/yes", "test", EXPECT_UNDEFINED, NULL }, +}; + void test_attr_repo__get_one(void) { - struct attr_expected test_cases[] = { - { "root_test1", "repoattr", EXPECT_TRUE, NULL }, - { "root_test1", "rootattr", EXPECT_TRUE, NULL }, - { "root_test1", "missingattr", EXPECT_UNDEFINED, NULL }, - { "root_test1", "subattr", EXPECT_UNDEFINED, NULL }, - { "root_test1", "negattr", EXPECT_UNDEFINED, NULL }, - { "root_test2", "repoattr", EXPECT_TRUE, NULL }, - { "root_test2", "rootattr", EXPECT_FALSE, NULL }, - { "root_test2", "missingattr", EXPECT_UNDEFINED, NULL }, - { "root_test2", "multiattr", EXPECT_FALSE, NULL }, - { "root_test3", "repoattr", EXPECT_TRUE, NULL }, - { "root_test3", "rootattr", EXPECT_UNDEFINED, NULL }, - { "root_test3", "multiattr", EXPECT_STRING, "3" }, - { "root_test3", "multi2", EXPECT_UNDEFINED, NULL }, - { "sub/subdir_test1", "repoattr", EXPECT_TRUE, NULL }, - { "sub/subdir_test1", "rootattr", EXPECT_TRUE, NULL }, - { "sub/subdir_test1", "missingattr", EXPECT_UNDEFINED, NULL }, - { "sub/subdir_test1", "subattr", EXPECT_STRING, "yes" }, - { "sub/subdir_test1", "negattr", EXPECT_FALSE, NULL }, - { "sub/subdir_test1", "another", EXPECT_UNDEFINED, NULL }, - { "sub/subdir_test2.txt", "repoattr", EXPECT_TRUE, NULL }, - { "sub/subdir_test2.txt", "rootattr", EXPECT_TRUE, NULL }, - { "sub/subdir_test2.txt", "missingattr", EXPECT_UNDEFINED, NULL }, - { "sub/subdir_test2.txt", "subattr", EXPECT_STRING, "yes" }, - { "sub/subdir_test2.txt", "negattr", EXPECT_FALSE, NULL }, - { "sub/subdir_test2.txt", "another", EXPECT_STRING, "zero" }, - { "sub/subdir_test2.txt", "reposub", EXPECT_TRUE, NULL }, - { "sub/sub/subdir.txt", "another", EXPECT_STRING, "one" }, - { "sub/sub/subdir.txt", "reposubsub", EXPECT_TRUE, NULL }, - { "sub/sub/subdir.txt", "reposub", EXPECT_UNDEFINED, NULL }, - { "does-not-exist", "foo", EXPECT_STRING, "yes" }, - { "sub/deep/file", "deepdeep", EXPECT_TRUE, NULL }, - { "sub/sub/d/no", "test", EXPECT_STRING, "a/b/d/*" }, - { "sub/sub/d/yes", "test", EXPECT_UNDEFINED, NULL }, - { NULL, NULL, 0, NULL } - }, *scan; + int i; + + for (i = 0; i < (int)ARRAY_SIZE(get_one_test_cases); ++i) { + struct attr_expected *scan = &get_one_test_cases[i]; + const char *value; + + cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr)); + attr_check_expected( + scan->expected, scan->expected_str, scan->attr, value); + } + + cl_assert(git_attr_cache__is_cached( + g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/attributes")); + cl_assert(git_attr_cache__is_cached( + g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitattributes")); + cl_assert(git_attr_cache__is_cached( + g_repo, GIT_ATTR_FILE__FROM_FILE, "sub/.gitattributes")); +} + +void test_attr_repo__get_one_start_deep(void) +{ + int i; - for (scan = test_cases; scan->path != NULL; scan++) { + for (i = (int)ARRAY_SIZE(get_one_test_cases) - 1; i >= 0; --i) { + struct attr_expected *scan = &get_one_test_cases[i]; const char *value; + cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr)); - attr_check_expected(scan->expected, scan->expected_str, scan->attr, value); + attr_check_expected( + scan->expected, scan->expected_str, scan->attr, value); } - cl_assert(git_attr_cache__is_cached(g_repo, 0, ".git/info/attributes")); - cl_assert(git_attr_cache__is_cached(g_repo, 0, ".gitattributes")); - cl_assert(git_attr_cache__is_cached(g_repo, 0, "sub/.gitattributes")); + cl_assert(git_attr_cache__is_cached( + g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/attributes")); + cl_assert(git_attr_cache__is_cached( + g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitattributes")); + cl_assert(git_attr_cache__is_cached( + g_repo, GIT_ATTR_FILE__FROM_FILE, "sub/.gitattributes")); } void test_attr_repo__get_many(void) @@ -129,6 +153,8 @@ return 0; } +#define CANCEL_VALUE 12345 + static int cancel_iteration( const char *name, const char *value, @@ -140,7 +166,7 @@ *((int *)payload) -= 1; if (*((int *)payload) < 0) - return -1; + return CANCEL_VALUE; return 0; } @@ -166,7 +192,7 @@ count = 2; cl_assert_equal_i( - GIT_EUSER, git_attr_foreach( + CANCEL_VALUE, git_attr_foreach( g_repo, 0, "sub/subdir_test1", &cancel_iteration, &count) ); } @@ -291,7 +317,7 @@ cl_assert(!git_index_find(&index_pos, index, filename)); entry = git_index_get_byindex(index, index_pos); - cl_assert_equal_i(0, git_oid_streq(&entry->oid, expected_sha)); + cl_assert_equal_i(0, git_oid_streq(&entry->id, expected_sha)); } void test_attr_repo__staging_properly_normalizes_line_endings_according_to_gitattributes_directives(void) @@ -308,3 +334,45 @@ git_index_free(index); } + +void test_attr_repo__bare_repo_with_index(void) +{ + const char *names[4] = { "test1", "test2", "test3", "test4" }; + const char *values[4]; + git_index *index; + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_mkfile( + "attr/.gitattributes", + "*.txt test1 test2=foobar -test3\n" + "trial.txt -test1 test2=barfoo !test3 test4\n"); + cl_git_pass(git_index_add_bypath(index, ".gitattributes")); + git_index_free(index); + + cl_must_pass(p_unlink("attr/.gitattributes")); + cl_assert(!git_path_exists("attr/.gitattributes")); + + cl_git_pass(git_repository_set_bare(g_repo)); + + cl_git_pass(git_attr_get_many(values, g_repo, 0, "file.txt", 4, names)); + + cl_assert(GIT_ATTR_TRUE(values[0])); + cl_assert_equal_s("foobar", values[1]); + cl_assert(GIT_ATTR_FALSE(values[2])); + cl_assert(GIT_ATTR_UNSPECIFIED(values[3])); + + cl_git_pass(git_attr_get_many(values, g_repo, 0, "trial.txt", 4, names)); + + cl_assert(GIT_ATTR_FALSE(values[0])); + cl_assert_equal_s("barfoo", values[1]); + cl_assert(GIT_ATTR_UNSPECIFIED(values[2])); + cl_assert(GIT_ATTR_TRUE(values[3])); + + cl_git_pass(git_attr_get_many(values, g_repo, 0, "sub/sub/subdir.txt", 4, names)); + + cl_assert(GIT_ATTR_TRUE(values[0])); + cl_assert_equal_s("foobar", values[1]); + cl_assert(GIT_ATTR_FALSE(values[2])); + cl_assert(GIT_ATTR_UNSPECIFIED(values[3])); +} diff -Nru libgit2-0.20.0/tests/blame/blame_helpers.c libgit2-0.22.2/tests/blame/blame_helpers.c --- libgit2-0.20.0/tests/blame/blame_helpers.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/blame/blame_helpers.c 2015-03-24 16:10:45.000000000 +0000 @@ -17,7 +17,7 @@ void check_blame_hunk_index(git_repository *repo, git_blame *blame, int idx, int start_line, int len, char boundary, const char *commit_id, const char *orig_path) { - char expected[41] = {0}, actual[41] = {0}; + char expected[GIT_OID_HEXSZ+1] = {0}, actual[GIT_OID_HEXSZ+1] = {0}; const git_blame_hunk *hunk = git_blame_get_hunk_byindex(blame, idx); cl_assert(hunk); @@ -48,6 +48,9 @@ actual, expected); } cl_assert_equal_s(actual, expected); + cl_assert_equal_oid(&hunk->final_commit_id, &hunk->orig_commit_id); + + if (strcmp(hunk->orig_path, orig_path)) { hunk_message(idx, hunk, "has mismatched original path (got '%s', expected '%s')\n", hunk->orig_path, orig_path); diff -Nru libgit2-0.20.0/tests/blame/buffer.c libgit2-0.22.2/tests/blame/buffer.c --- libgit2-0.20.0/tests/blame/buffer.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/blame/buffer.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,7 +1,7 @@ #include "blame_helpers.h" -git_repository *g_repo; -git_blame *g_fileblame, *g_bufferblame; +static git_repository *g_repo; +static git_blame *g_fileblame, *g_bufferblame; void test_blame_buffer__initialize(void) { diff -Nru libgit2-0.20.0/tests/blame/harder.c libgit2-0.22.2/tests/blame/harder.c --- libgit2-0.20.0/tests/blame/harder.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/blame/harder.c 2015-03-24 16:10:45.000000000 +0000 @@ -36,6 +36,8 @@ /* TODO */ git_blame_options opts = GIT_BLAME_OPTIONS_INIT; + GIT_UNUSED(opts); + opts.flags = GIT_BLAME_TRACK_COPIES_SAME_FILE; } @@ -44,6 +46,8 @@ { git_blame_options opts = GIT_BLAME_OPTIONS_INIT; + GIT_UNUSED(opts); + /* Attribute the first hunk in b.txt to (E), since it was cut/pasted from * a.txt in (D). */ @@ -54,6 +58,8 @@ { git_blame_options opts = GIT_BLAME_OPTIONS_INIT; + GIT_UNUSED(opts); + /* Attribute the second hunk in b.txt to (E), since it was copy/pasted from * a.txt in (C). */ @@ -63,6 +69,8 @@ void test_blame_harder__ccc(void) { git_blame_options opts = GIT_BLAME_OPTIONS_INIT; + + GIT_UNUSED(opts); /* Attribute the third hunk in b.txt to (E). This hunk was deleted from * a.txt in (D), but reintroduced in (B). diff -Nru libgit2-0.20.0/tests/blame/simple.c libgit2-0.22.2/tests/blame/simple.c --- libgit2-0.20.0/tests/blame/simple.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/blame/simple.c 2015-03-24 16:10:45.000000000 +0000 @@ -135,13 +135,17 @@ git_blame_options opts = GIT_BLAME_OPTIONS_INIT; git_object *obj; - cl_git_pass(git_repository_open(&g_repo, cl_fixture("../.."))); + /* If we can't open the libgit2 repo or if it isn't a full repo + * with proper history, just skip this test */ + if (git_repository_open(&g_repo, cl_fixture("../..")) < 0) + cl_skip(); - /* This test can't work on a shallow clone */ if (git_repository_is_shallow(g_repo)) - return; + cl_skip(); + + if (git_revparse_single(&obj, g_repo, "359fc2d") < 0) + cl_skip(); - cl_git_pass(git_revparse_single(&obj, g_repo, "359fc2d")); git_oid_cpy(&opts.newest_commit, git_object_id(obj)); git_object_free(obj); @@ -303,3 +307,18 @@ check_blame_hunk_index(g_repo, g_blame, 0, 1, 1, 1, "be3563a", "branch_file.txt"); check_blame_hunk_index(g_repo, g_blame, 1, 2, 1, 0, "a65fedf", "branch_file.txt"); } + +void test_blame_simple__can_restrict_to_first_parent_commits(void) +{ + git_blame_options opts = GIT_BLAME_OPTIONS_INIT; + opts.flags |= GIT_BLAME_FIRST_PARENT; + + cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git"))); + + cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", &opts)); + cl_assert_equal_i(4, git_blame_get_hunk_count(g_blame)); + check_blame_hunk_index(g_repo, g_blame, 0, 1, 4, 0, "da237394", "b.txt"); + check_blame_hunk_index(g_repo, g_blame, 1, 5, 1, 1, "b99f7ac0", "b.txt"); + check_blame_hunk_index(g_repo, g_blame, 2, 6, 5, 0, "63d671eb", "b.txt"); + check_blame_hunk_index(g_repo, g_blame, 3, 11, 5, 0, "bc7c5ac2", "b.txt"); +} diff -Nru libgit2-0.20.0/tests/buf/oom.c libgit2-0.22.2/tests/buf/oom.c --- libgit2-0.20.0/tests/buf/oom.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/buf/oom.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,31 @@ +#include "clar_libgit2.h" +#include "buffer.h" + +#if defined(GIT_ARCH_64) +#define TOOBIG 0xffffffffffffff00 +#else +#define TOOBIG 0xffffff00 +#endif + +/** + * If we make a ridiculously large request the first time we + * actually allocate some space in the git_buf, the realloc() + * will fail. And because the git_buf_grow() wrapper always + * sets mark_oom, the code in git_buf_try_grow() will free + * the internal buffer and set it to git_buf__oom. + * + * We initialized the internal buffer to (the static variable) + * git_buf__initbuf. The purpose of this test is to make sure + * that we don't try to free the static buffer. + */ +void test_buf_oom__grow(void) +{ + git_buf buf = GIT_BUF_INIT; + + git_buf_clear(&buf); + + cl_assert(git_buf_grow(&buf, TOOBIG) == -1); + cl_assert(git_buf_oom(&buf)); + + git_buf_free(&buf); +} diff -Nru libgit2-0.20.0/tests/checkout/binaryunicode.c libgit2-0.22.2/tests/checkout/binaryunicode.c --- libgit2-0.20.0/tests/checkout/binaryunicode.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/checkout/binaryunicode.c 2015-03-24 16:10:45.000000000 +0000 @@ -21,7 +21,7 @@ git_oid oid, check; git_commit *commit; git_tree *tree; - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/branch1")); cl_git_pass(git_commit_lookup(&commit, g_repo, &oid)); @@ -37,12 +37,12 @@ /* Verify that the lenna.jpg file was checked out correctly */ cl_git_pass(git_oid_fromstr(&check, "8ab005d890fe53f65eda14b23672f60d9f4ec5a1")); cl_git_pass(git_odb_hashfile(&oid, "binaryunicode/lenna.jpg", GIT_OBJ_BLOB)); - cl_assert(git_oid_equal(&oid, &check)); + cl_assert_equal_oid(&oid, &check); /* Verify that the text file was checked out correctly */ cl_git_pass(git_oid_fromstr(&check, "965b223880dd4249e2c66a0cc0b4cffe1dc40f5a")); cl_git_pass(git_odb_hashfile(&oid, "binaryunicode/utf16_withbom_noeol_crlf.txt", GIT_OBJ_BLOB)); - cl_assert(git_oid_equal(&oid, &check)); + cl_assert_equal_oid(&oid, &check); } void test_checkout_binaryunicode__noautocrlf(void) diff -Nru libgit2-0.20.0/tests/checkout/conflict.c libgit2-0.22.2/tests/checkout/conflict.c --- libgit2-0.20.0/tests/checkout/conflict.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/checkout/conflict.c 2015-03-24 16:10:45.000000000 +0000 @@ -48,7 +48,7 @@ struct checkout_index_entry { uint16_t mode; - char oid_str[41]; + char oid_str[GIT_OID_HEXSZ+1]; int stage; char path[128]; }; @@ -61,12 +61,19 @@ void test_checkout_conflict__initialize(void) { + git_config *cfg; + g_repo = cl_git_sandbox_init(TEST_REPO_PATH); git_repository_index(&g_index, g_repo); cl_git_rewritefile( TEST_REPO_PATH "/.gitattributes", "* text eol=lf\n"); + + /* Ensure that the user's merge.conflictstyle doesn't interfere */ + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_set_string(cfg, "merge.conflictstyle", "merge")); + git_config_free(cfg); } void test_checkout_conflict__cleanup(void) @@ -96,7 +103,7 @@ entry.mode = entries[i].mode; entry.flags = entries[i].stage << GIT_IDXENTRY_STAGESHIFT; - git_oid_fromstr(&entry.oid, entries[i].oid_str); + git_oid_fromstr(&entry.id, entries[i].oid_str); entry.path = entries[i].path; cl_git_pass(git_index_add(g_index, &entry)); @@ -149,7 +156,7 @@ cl_git_pass(git_oid_fromstr(&expected, oid_str)); cl_git_pass(git_repository_hashfile(&actual, g_repo, path, GIT_OBJ_BLOB, NULL)); - cl_assert(git_oid_cmp(&expected, &actual) == 0); + cl_assert_equal_oid(&expected, &actual); } static void ensure_workdir_mode(const char *path, int mode) @@ -162,7 +169,7 @@ git_buf_joinpath(&fullpath, git_repository_workdir(g_repo), path)); cl_git_pass(p_stat(git_buf_cstr(&fullpath), &st)); - cl_assert_equal_i(mode, st.st_mode); + cl_assert_equal_i((mode & S_IRWXU), (st.st_mode & S_IRWXU)); git_buf_free(&fullpath); #endif @@ -200,7 +207,7 @@ void test_checkout_conflict__ignored(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy |= GIT_CHECKOUT_SKIP_UNMERGED; @@ -214,7 +221,7 @@ void test_checkout_conflict__ours(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy |= GIT_CHECKOUT_USE_OURS; @@ -227,7 +234,7 @@ void test_checkout_conflict__theirs(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy |= GIT_CHECKOUT_USE_THEIRS; @@ -241,7 +248,7 @@ void test_checkout_conflict__diff3(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; create_conflicting_index(); @@ -252,7 +259,7 @@ void test_checkout_conflict__automerge(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct checkout_index_entry checkout_index_entries[] = { { 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "automergeable.txt" }, @@ -270,7 +277,7 @@ void test_checkout_conflict__directory_file(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct checkout_index_entry checkout_index_entries[] = { { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-1" }, @@ -309,7 +316,7 @@ void test_checkout_conflict__directory_file_with_custom_labels(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct checkout_index_entry checkout_index_entries[] = { { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-1" }, @@ -350,7 +357,7 @@ void test_checkout_conflict__link_file(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct checkout_index_entry checkout_index_entries[] = { { 0100644, CONFLICTING_ANCESTOR_OID, 1, "link-1" }, @@ -386,7 +393,7 @@ void test_checkout_conflict__links(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct checkout_index_entry checkout_index_entries[] = { { 0120000, LINK_ANCESTOR_OID, 1, "link-1" }, @@ -411,7 +418,7 @@ void test_checkout_conflict__add_add(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct checkout_index_entry checkout_index_entries[] = { { 0100644, CONFLICTING_OURS_OID, 2, "conflicting.txt" }, @@ -431,7 +438,7 @@ void test_checkout_conflict__mode_change(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct checkout_index_entry checkout_index_entries[] = { { 0100644, CONFLICTING_ANCESTOR_OID, 1, "executable-1" }, @@ -488,7 +495,7 @@ void test_checkout_conflict__renames(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct checkout_index_entry checkout_index_entries[] = { { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" }, @@ -673,7 +680,7 @@ void test_checkout_conflict__rename_keep_ours(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct checkout_index_entry checkout_index_entries[] = { { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" }, @@ -840,7 +847,7 @@ void test_checkout_conflict__name_mangled_file_exists_in_workdir(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct checkout_index_entry checkout_index_entries[] = { { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "test-one-side-one.txt" }, @@ -980,7 +987,7 @@ void test_checkout_conflict__update_only(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct checkout_index_entry checkout_index_entries[] = { { 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "automergeable.txt" }, @@ -1025,7 +1032,7 @@ void test_checkout_conflict__path_filters(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; char *paths[] = { "conflicting-1.txt", "conflicting-3.txt" }; git_strarray patharray = {0}; @@ -1071,8 +1078,8 @@ { git_vector *paths = payload; - (void)completed_steps; - (void)total_steps; + GIT_UNUSED(completed_steps); + GIT_UNUSED(total_steps); if (path == NULL) return; @@ -1082,7 +1089,7 @@ void test_checkout_conflict__report_progress(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_vector paths = GIT_VECTOR_INIT; char *path; size_t i; diff -Nru libgit2-0.20.0/tests/checkout/crlf.c libgit2-0.22.2/tests/checkout/crlf.c --- libgit2-0.20.0/tests/checkout/crlf.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/checkout/crlf.c 2015-03-24 16:10:45.000000000 +0000 @@ -20,7 +20,7 @@ void test_checkout_crlf__detect_crlf_autocrlf_false(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; cl_repo_set_bool(g_repo, "core.autocrlf", false); @@ -35,7 +35,7 @@ { git_index *index; const git_index_entry *entry; - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; cl_repo_set_bool(g_repo, "core.autocrlf", false); @@ -55,7 +55,7 @@ void test_checkout_crlf__detect_crlf_autocrlf_true(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; cl_repo_set_bool(g_repo, "core.autocrlf", true); @@ -72,37 +72,31 @@ void test_checkout_crlf__more_lf_autocrlf_true(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; cl_repo_set_bool(g_repo, "core.autocrlf", true); git_checkout_head(g_repo, &opts); - if (GIT_EOL_NATIVE == GIT_EOL_LF) - check_file_contents("./crlf/more-lf", MORE_LF_TEXT_RAW); - else - check_file_contents("./crlf/more-lf", MORE_LF_TEXT_AS_CRLF); + check_file_contents("./crlf/more-lf", MORE_LF_TEXT_RAW); } void test_checkout_crlf__more_crlf_autocrlf_true(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; cl_repo_set_bool(g_repo, "core.autocrlf", true); git_checkout_head(g_repo, &opts); - if (GIT_EOL_NATIVE == GIT_EOL_LF) - check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_RAW); - else - check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_AS_CRLF); + check_file_contents("./crlf/more-crlf", MORE_CRLF_TEXT_RAW); } void test_checkout_crlf__all_crlf_autocrlf_true(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; cl_repo_set_bool(g_repo, "core.autocrlf", true); @@ -116,7 +110,7 @@ { git_index *index; const git_index_entry *entry; - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; cl_repo_set_bool(g_repo, "core.autocrlf", true); @@ -142,7 +136,7 @@ { git_index *index; git_blob *blob; - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; cl_git_mkfile("crlf/.gitattributes", @@ -174,13 +168,13 @@ /* check that blobs have $Id$ */ cl_git_pass(git_blob_lookup(&blob, g_repo, - & git_index_get_bypath(index, "lf.ident", 0)->oid)); + & git_index_get_bypath(index, "lf.ident", 0)->id)); cl_assert_equal_s( ALL_LF_TEXT_RAW "\n$Id$\n", git_blob_rawcontent(blob)); git_blob_free(blob); cl_git_pass(git_blob_lookup(&blob, g_repo, - & git_index_get_bypath(index, "more2.identcrlf", 0)->oid)); + & git_index_get_bypath(index, "more2.identcrlf", 0)->id)); cl_assert_equal_s( "\n$Id$\n" MORE_CRLF_TEXT_AS_LF, git_blob_rawcontent(blob)); git_blob_free(blob); @@ -229,3 +223,102 @@ git_index_free(index); } + +void test_checkout_crlf__autocrlf_false_no_attrs(void) +{ + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + + cl_repo_set_bool(g_repo, "core.autocrlf", false); + + git_checkout_head(g_repo, &opts); + + check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW); + check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW); +} + +void test_checkout_crlf__autocrlf_true_no_attrs(void) +{ + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + + cl_repo_set_bool(g_repo, "core.autocrlf", true); + + git_checkout_head(g_repo, &opts); + + if (GIT_EOL_NATIVE == GIT_EOL_CRLF) { + check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF); + check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_AS_CRLF); + } else { + check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW); + check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW); + } +} + +void test_checkout_crlf__autocrlf_input_no_attrs(void) +{ + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + + cl_repo_set_string(g_repo, "core.autocrlf", "input"); + + git_checkout_head(g_repo, &opts); + + check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW); + check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW); +} + +void test_checkout_crlf__autocrlf_false_text_auto_attr(void) +{ + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + + cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n"); + + cl_repo_set_bool(g_repo, "core.autocrlf", false); + + git_checkout_head(g_repo, &opts); + + if (GIT_EOL_NATIVE == GIT_EOL_CRLF) { + check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF); + check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_AS_CRLF); + } else { + check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW); + check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW); + } +} + +void test_checkout_crlf__autocrlf_true_text_auto_attr(void) +{ + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + + cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n"); + + cl_repo_set_bool(g_repo, "core.autocrlf", true); + + git_checkout_head(g_repo, &opts); + + if (GIT_EOL_NATIVE == GIT_EOL_CRLF) { + check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF); + check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_AS_CRLF); + } else { + check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW); + check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW); + } +} + +void test_checkout_crlf__autocrlf_input_text_auto_attr(void) +{ + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + + cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n"); + + cl_repo_set_string(g_repo, "core.autocrlf", "input"); + + git_checkout_head(g_repo, &opts); + + check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW); + check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW); +} diff -Nru libgit2-0.20.0/tests/checkout/head.c libgit2-0.22.2/tests/checkout/head.c --- libgit2-0.20.0/tests/checkout/head.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/checkout/head.c 2015-03-24 16:10:45.000000000 +0000 @@ -25,7 +25,7 @@ void test_checkout_head__with_index_only_tree(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_index *index; /* let's start by getting things into a known state */ diff -Nru libgit2-0.20.0/tests/checkout/icase.c libgit2-0.22.2/tests/checkout/icase.c --- libgit2-0.20.0/tests/checkout/icase.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/checkout/icase.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,130 @@ +#include "clar_libgit2.h" + +#include "git2/checkout.h" +#include "path.h" + +#ifdef GIT_WIN32 +# include +#endif + +static git_repository *repo; +static git_object *obj; +static git_checkout_options checkout_opts; + +void test_checkout_icase__initialize(void) +{ + git_oid id; + + repo = cl_git_sandbox_init("testrepo"); + + cl_git_pass(git_reference_name_to_id(&id, repo, "refs/heads/dir")); + cl_git_pass(git_object_lookup(&obj, repo, &id, GIT_OBJ_ANY)); + + git_checkout_init_options(&checkout_opts, GIT_CHECKOUT_OPTIONS_VERSION); + checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE; +} + +void test_checkout_icase__cleanup(void) +{ + git_object_free(obj); + cl_git_sandbox_cleanup(); +} + +static char *test_realpath(const char *in) +{ +#ifdef GIT_WIN32 + HANDLE fh; + HMODULE kerneldll; + char *filename; + + typedef DWORD (__stdcall *getfinalpathname)(HANDLE, LPSTR, DWORD, DWORD); + getfinalpathname getfinalpathfn; + + cl_assert(filename = malloc(MAX_PATH)); + cl_assert(kerneldll = LoadLibrary("kernel32.dll")); + cl_assert(getfinalpathfn = (getfinalpathname)GetProcAddress(kerneldll, "GetFinalPathNameByHandleA")); + + cl_assert(fh = CreateFileA(in, FILE_READ_ATTRIBUTES | STANDARD_RIGHTS_READ, FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL)); + + cl_win32_pass(getfinalpathfn(fh, filename, MAX_PATH, VOLUME_NAME_DOS)); + + CloseHandle(fh); + + git_path_mkposix(filename); + + return filename; +#else + return realpath(in, NULL); +#endif +} + +static void assert_name_is(const char *expected) +{ + char *actual; + size_t actual_len, expected_len, start; + + cl_assert(actual = test_realpath(expected)); + + expected_len = strlen(expected); + actual_len = strlen(actual); + cl_assert(actual_len >= expected_len); + + start = actual_len - expected_len; + cl_assert_equal_s(expected, actual + start); + + if (start) + cl_assert_equal_strn("/", actual + (start - 1), 1); + + free(actual); +} + +void test_checkout_icase__overwrites_files_for_files(void) +{ + cl_git_write2file("testrepo/NEW.txt", "neue file\n", 10, \ + O_WRONLY | O_CREAT | O_TRUNC, 0644); + + cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts)); + assert_name_is("testrepo/new.txt"); +} + +void test_checkout_icase__overwrites_links_for_files(void) +{ + cl_must_pass(p_symlink("../tmp", "testrepo/NEW.txt")); + + cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts)); + + cl_assert(!git_path_exists("tmp")); + assert_name_is("testrepo/new.txt"); +} + +void test_checkout_icase__overwites_folders_for_files(void) +{ + cl_must_pass(p_mkdir("testrepo/NEW.txt", 0777)); + + cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts)); + + assert_name_is("testrepo/new.txt"); + cl_assert(!git_path_isdir("testrepo/new.txt")); +} + +void test_checkout_icase__overwrites_files_for_folders(void) +{ + cl_git_write2file("testrepo/A", "neue file\n", 10, \ + O_WRONLY | O_CREAT | O_TRUNC, 0644); + + cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts)); + assert_name_is("testrepo/a"); + cl_assert(git_path_isdir("testrepo/a")); +} + +void test_checkout_icase__overwrites_links_for_folders(void) +{ + cl_must_pass(p_symlink("..", "testrepo/A")); + + cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts)); + + cl_assert(!git_path_exists("b.txt")); + assert_name_is("testrepo/a"); +} + diff -Nru libgit2-0.20.0/tests/checkout/index.c libgit2-0.22.2/tests/checkout/index.c --- libgit2-0.20.0/tests/checkout/index.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/checkout/index.c 2015-03-24 16:10:45.000000000 +0000 @@ -43,7 +43,7 @@ void test_checkout_index__can_create_missing_files(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; cl_assert_equal_i(false, git_path_isfile("./testrepo/README")); cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt")); @@ -60,7 +60,7 @@ void test_checkout_index__can_remove_untracked_files(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_futils_mkdir("./testrepo/dir/subdir/subsubdir", NULL, 0755, GIT_MKDIR_PATH); cl_git_mkfile("./testrepo/dir/one", "one\n"); @@ -78,7 +78,7 @@ void test_checkout_index__honor_the_specified_pathspecs(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; char *entries[] = { "*.txt" }; opts.paths.strings = entries; @@ -99,7 +99,7 @@ void test_checkout_index__honor_the_gitattributes_directives(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; const char *attributes = "branch_file.txt text eol=crlf\n" "new.txt text eol=lf\n"; @@ -119,7 +119,7 @@ void test_checkout_index__honor_coreautocrlf_setting_set_to_true(void) { #ifdef GIT_WIN32 - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; const char *expected_readme_text = "hey there\r\n"; cl_git_pass(p_unlink("./testrepo/.gitattributes")); @@ -135,7 +135,7 @@ void test_checkout_index__honor_coresymlinks_setting_set_to_true(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; cl_repo_set_bool(g_repo, "core.symlinks", true); @@ -161,7 +161,7 @@ void test_checkout_index__honor_coresymlinks_setting_set_to_false(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; cl_repo_set_bool(g_repo, "core.symlinks", false); @@ -174,7 +174,7 @@ void test_checkout_index__donot_overwrite_modified_file_by_default(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!"); @@ -190,7 +190,7 @@ void test_checkout_index__can_overwrite_modified_file(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!"); @@ -203,7 +203,7 @@ void test_checkout_index__options_disable_filters(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; cl_git_mkfile("./testrepo/.gitattributes", "*.txt text eol=crlf\n"); @@ -224,7 +224,7 @@ void test_checkout_index__options_dir_modes(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct stat st; git_oid oid; git_commit *commit; @@ -258,7 +258,7 @@ void test_checkout_index__options_override_file_modes(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct stat st; if (!cl_is_chmod_supported()) @@ -275,14 +275,14 @@ void test_checkout_index__options_open_flags(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; cl_git_mkfile("./testrepo/new.txt", "hi\n"); - opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; + opts.checkout_strategy = + GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_REMOVE_EXISTING; opts.file_open_flags = O_CREAT | O_RDWR | O_APPEND; - opts.checkout_strategy = GIT_CHECKOUT_FORCE; cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); check_file_contents("./testrepo/new.txt", "hi\nmy new file\n"); @@ -307,15 +307,15 @@ cl_assert_equal_i(GIT_CHECKOUT_NOTIFY_CONFLICT, why); cl_assert_equal_s(expectations->file, path); - cl_assert_equal_i(0, git_oid_streq(&baseline->oid, expectations->sha)); - cl_assert_equal_i(0, git_oid_streq(&target->oid, expectations->sha)); + cl_assert_equal_i(0, git_oid_streq(&baseline->id, expectations->sha)); + cl_assert_equal_i(0, git_oid_streq(&target->id, expectations->sha)); return 0; } void test_checkout_index__can_notify_of_skipped_files(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; struct notify_data data; cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!"); @@ -360,7 +360,7 @@ void test_checkout_index__wont_notify_of_expected_line_ending_changes(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; cl_git_pass(p_unlink("./testrepo/.gitattributes")); cl_repo_set_bool(g_repo, "core.autocrlf", true); @@ -385,7 +385,7 @@ void test_checkout_index__calls_progress_callback(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; int calls = 0; opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; @@ -398,7 +398,7 @@ void test_checkout_index__can_overcome_name_clashes(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_index *index; cl_git_pass(git_repository_index(&index, g_repo)); @@ -440,7 +440,7 @@ void test_checkout_index__validates_struct_version(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; const git_error *err; opts.version = 1024; @@ -459,7 +459,7 @@ void test_checkout_index__can_update_prefixed_files(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; cl_assert_equal_i(false, git_path_isfile("./testrepo/README")); cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt")); @@ -502,7 +502,7 @@ void test_checkout_index__issue_1397(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; test_checkout_index__cleanup(); @@ -519,7 +519,7 @@ void test_checkout_index__target_directory(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; checkout_counts cts; memset(&cts, 0, sizeof(cts)); @@ -551,7 +551,7 @@ void test_checkout_index__target_directory_from_bare(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_index *index; git_object *head = NULL; checkout_counts cts; @@ -600,7 +600,7 @@ void test_checkout_index__can_get_repo_from_index(void) { git_index *index; - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; cl_assert_equal_i(false, git_path_isfile("./testrepo/README")); cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt")); @@ -618,3 +618,101 @@ git_index_free(index); } + +static void add_conflict(git_index *index, const char *path) +{ + git_index_entry entry; + + memset(&entry, 0, sizeof(git_index_entry)); + + entry.mode = 0100644; + entry.path = path; + + git_oid_fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46"); + entry.flags = (1 << GIT_IDXENTRY_STAGESHIFT); + cl_git_pass(git_index_add(index, &entry)); + + git_oid_fromstr(&entry.id, "4e886e602529caa9ab11d71f86634bd1b6e0de10"); + entry.flags = (2 << GIT_IDXENTRY_STAGESHIFT); + cl_git_pass(git_index_add(index, &entry)); + + git_oid_fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863"); + entry.flags = (3 << GIT_IDXENTRY_STAGESHIFT); + cl_git_pass(git_index_add(index, &entry)); +} + +void test_checkout_index__writes_conflict_file(void) +{ + git_index *index; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_buf conflicting_buf = GIT_BUF_INIT; + + cl_git_pass(git_repository_index(&index, g_repo)); + + add_conflict(index, "conflicting.txt"); + cl_git_pass(git_index_write(index)); + + cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); + + cl_git_pass(git_futils_readbuffer(&conflicting_buf, "testrepo/conflicting.txt")); + cl_assert(strcmp(conflicting_buf.ptr, + "<<<<<<< ours\n" + "this file is changed in master and branch\n" + "=======\n" + "this file is changed in branch and master\n" + ">>>>>>> theirs\n") == 0); + git_buf_free(&conflicting_buf); + + git_index_free(index); +} + +void test_checkout_index__adding_conflict_removes_stage_0(void) +{ + git_index *new_index, *index; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + + cl_git_pass(git_index_new(&new_index)); + + add_conflict(new_index, "new.txt"); + cl_git_pass(git_checkout_index(g_repo, new_index, &opts)); + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_assert(git_index_get_bypath(index, "new.txt", 0) == NULL); + cl_assert(git_index_get_bypath(index, "new.txt", 1) != NULL); + cl_assert(git_index_get_bypath(index, "new.txt", 2) != NULL); + cl_assert(git_index_get_bypath(index, "new.txt", 3) != NULL); + + git_index_free(index); + git_index_free(new_index); +} + +void test_checkout_index__conflicts_honor_coreautocrlf(void) +{ +#ifdef GIT_WIN32 + git_index *index; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_buf conflicting_buf = GIT_BUF_INIT; + + cl_git_pass(p_unlink("./testrepo/.gitattributes")); + cl_repo_set_bool(g_repo, "core.autocrlf", true); + + cl_git_pass(git_repository_index(&index, g_repo)); + + add_conflict(index, "conflicting.txt"); + cl_git_pass(git_index_write(index)); + + cl_git_pass(git_checkout_index(g_repo, NULL, &opts)); + + cl_git_pass(git_futils_readbuffer(&conflicting_buf, "testrepo/conflicting.txt")); + cl_assert(strcmp(conflicting_buf.ptr, + "<<<<<<< ours\r\n" + "this file is changed in master and branch\r\n" + "=======\r\n" + "this file is changed in branch and master\r\n" + ">>>>>>> theirs\r\n") == 0); + git_buf_free(&conflicting_buf); + + git_index_free(index); +#endif +} diff -Nru libgit2-0.20.0/tests/checkout/nasty.c libgit2-0.22.2/tests/checkout/nasty.c --- libgit2-0.20.0/tests/checkout/nasty.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/checkout/nasty.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,366 @@ +#include "clar_libgit2.h" +#include "checkout_helpers.h" + +#include "git2/checkout.h" +#include "repository.h" +#include "buffer.h" +#include "fileops.h" + +static const char *repo_name = "nasty"; +static git_repository *repo; +static git_checkout_options checkout_opts; + +void test_checkout_nasty__initialize(void) +{ + repo = cl_git_sandbox_init(repo_name); + + GIT_INIT_STRUCTURE(&checkout_opts, GIT_CHECKOUT_OPTIONS_VERSION); + checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE; +} + +void test_checkout_nasty__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +static void test_checkout_passes(const char *refname, const char *filename) +{ + git_oid commit_id; + git_commit *commit; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_buf path = GIT_BUF_INIT; + + cl_git_pass(git_buf_joinpath(&path, repo_name, filename)); + + cl_git_pass(git_reference_name_to_id(&commit_id, repo, refname)); + cl_git_pass(git_commit_lookup(&commit, repo, &commit_id)); + + opts.checkout_strategy = GIT_CHECKOUT_FORCE | + GIT_CHECKOUT_DONT_UPDATE_INDEX; + + cl_git_pass(git_checkout_tree(repo, (const git_object *)commit, &opts)); + cl_assert(!git_path_exists(path.ptr)); + + git_commit_free(commit); + git_buf_free(&path); +} + +static void test_checkout_fails(const char *refname, const char *filename) +{ + git_oid commit_id; + git_commit *commit; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_buf path = GIT_BUF_INIT; + + cl_git_pass(git_buf_joinpath(&path, repo_name, filename)); + + cl_git_pass(git_reference_name_to_id(&commit_id, repo, refname)); + cl_git_pass(git_commit_lookup(&commit, repo, &commit_id)); + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_git_fail(git_checkout_tree(repo, (const git_object *)commit, &opts)); + cl_assert(!git_path_exists(path.ptr)); + + git_commit_free(commit); + git_buf_free(&path); +} + +/* A tree that contains ".git" as a tree, with a blob inside + * (".git/foobar"). + */ +void test_checkout_nasty__dotgit_tree(void) +{ + test_checkout_fails("refs/heads/dotgit_tree", ".git/foobar"); +} + +/* A tree that contains ".GIT" as a tree, with a blob inside + * (".GIT/foobar"). + */ +void test_checkout_nasty__dotcapitalgit_tree(void) +{ + test_checkout_fails("refs/heads/dotcapitalgit_tree", ".GIT/foobar"); +} + +/* A tree that contains a tree ".", with a blob inside ("./foobar"). + */ +void test_checkout_nasty__dot_tree(void) +{ + test_checkout_fails("refs/heads/dot_tree", "foobar"); +} + +/* A tree that contains a tree ".", with a tree ".git", with a blob + * inside ("./.git/foobar"). + */ +void test_checkout_nasty__dot_dotgit_tree(void) +{ + test_checkout_fails("refs/heads/dot_dotgit_tree", ".git/foobar"); +} + +/* A tree that contains a tree, with a tree "..", with a tree ".git", with a + * blob inside ("foo/../.git/foobar"). + */ +void test_checkout_nasty__dotdot_dotgit_tree(void) +{ + test_checkout_fails("refs/heads/dotdot_dotgit_tree", ".git/foobar"); +} + +/* A tree that contains a tree, with a tree "..", with a blob inside + * ("foo/../foobar"). + */ +void test_checkout_nasty__dotdot_tree(void) +{ + test_checkout_fails("refs/heads/dotdot_tree", "foobar"); +} + +/* A tree that contains a blob with the rogue name ".git/foobar" */ +void test_checkout_nasty__dotgit_path(void) +{ + test_checkout_fails("refs/heads/dotgit_path", ".git/foobar"); +} + +/* A tree that contains a blob with the rogue name ".GIT/foobar" */ +void test_checkout_nasty__dotcapitalgit_path(void) +{ + test_checkout_fails("refs/heads/dotcapitalgit_path", ".GIT/foobar"); +} + +/* A tree that contains a blob with the rogue name "./.git/foobar" */ +void test_checkout_nasty__dot_dotgit_path(void) +{ + test_checkout_fails("refs/heads/dot_dotgit_path", ".git/foobar"); +} + +/* A tree that contains a blob with the rogue name "./.GIT/foobar" */ +void test_checkout_nasty__dot_dotcapitalgit_path(void) +{ + test_checkout_fails("refs/heads/dot_dotcapitalgit_path", ".GIT/foobar"); +} + +/* A tree that contains a blob with the rogue name "foo/../.git/foobar" */ +void test_checkout_nasty__dotdot_dotgit_path(void) +{ + test_checkout_fails("refs/heads/dotdot_dotgit_path", ".git/foobar"); +} + +/* A tree that contains a blob with the rogue name "foo/../.GIT/foobar" */ +void test_checkout_nasty__dotdot_dotcapitalgit_path(void) +{ + test_checkout_fails("refs/heads/dotdot_dotcapitalgit_path", ".GIT/foobar"); +} + +/* A tree that contains a blob with the rogue name "foo/." */ +void test_checkout_nasty__dot_path(void) +{ + test_checkout_fails("refs/heads/dot_path", "./foobar"); +} + +/* A tree that contains a blob with the rogue name "foo/." */ +void test_checkout_nasty__dot_path_two(void) +{ + test_checkout_fails("refs/heads/dot_path_two", "foo/."); +} + +/* A tree that contains a blob with the rogue name "foo/../foobar" */ +void test_checkout_nasty__dotdot_path(void) +{ + test_checkout_fails("refs/heads/dotdot_path", "foobar"); +} + +/* A tree that contains an entry with a backslash ".git\foobar" */ +void test_checkout_nasty__dotgit_backslash_path(void) +{ +#ifdef GIT_WIN32 + test_checkout_fails("refs/heads/dotgit_backslash_path", ".git/foobar"); +#endif +} + +/* A tree that contains an entry with a backslash ".GIT\foobar" */ +void test_checkout_nasty__dotcapitalgit_backslash_path(void) +{ +#ifdef GIT_WIN32 + test_checkout_fails("refs/heads/dotcapitalgit_backslash_path", ".GIT/foobar"); +#endif +} + +/* A tree that contains an entry with a backslash ".\.GIT\foobar" */ +void test_checkout_nasty__dot_backslash_dotcapitalgit_path(void) +{ +#ifdef GIT_WIN32 + test_checkout_fails("refs/heads/dot_backslash_dotcapitalgit_path", ".GIT/foobar"); +#endif +} + +/* A tree that contains an entry ".git.", because Win32 APIs will drop the + * trailing slash. + */ +void test_checkout_nasty__dot_git_dot(void) +{ +#ifdef GIT_WIN32 + test_checkout_fails("refs/heads/dot_git_dot", ".git/foobar"); +#endif +} + +/* A tree that contains an entry "git~1", because that is typically the + * short name for ".git". + */ +void test_checkout_nasty__git_tilde1(void) +{ +#ifdef GIT_WIN32 + test_checkout_fails("refs/heads/git_tilde1", ".git/foobar"); +#endif +} + +/* A tree that contains an entry "git~2", when we have forced the short + * name for ".git" into "GIT~2". + */ +void test_checkout_nasty__git_custom_shortname(void) +{ +#ifdef GIT_WIN32 + if (!cl_sandbox_supports_8dot3()) + clar__skip(); + + cl_must_pass(p_rename("nasty/.git", "nasty/_temp")); + cl_git_write2file("nasty/git~1", "", 0, O_RDWR|O_CREAT, 0666); + cl_must_pass(p_rename("nasty/_temp", "nasty/.git")); + test_checkout_fails("refs/heads/git_tilde2", ".git/foobar"); +#endif +} + +/* A tree that contains an entry "git~3", which should be allowed, since + * it is not the typical short name ("GIT~1") or the actual short name + * ("GIT~2") for ".git". + */ +void test_checkout_nasty__only_looks_like_a_git_shortname(void) +{ +#ifdef GIT_WIN32 + git_oid commit_id; + git_commit *commit; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + + cl_must_pass(p_rename("nasty/.git", "nasty/_temp")); + cl_git_write2file("nasty/git~1", "", 0, O_RDWR|O_CREAT, 0666); + cl_must_pass(p_rename("nasty/_temp", "nasty/.git")); + + cl_git_pass(git_reference_name_to_id(&commit_id, repo, "refs/heads/git_tilde3")); + cl_git_pass(git_commit_lookup(&commit, repo, &commit_id)); + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_git_pass(git_checkout_tree(repo, (const git_object *)commit, &opts)); + cl_assert(git_path_exists("nasty/git~3/foobar")); + + git_commit_free(commit); +#endif +} + +/* A tree that contains an entry "git:", because Win32 APIs will reject + * that as looking too similar to a drive letter. + */ +void test_checkout_nasty__dot_git_colon(void) +{ +#ifdef GIT_WIN32 + test_checkout_fails("refs/heads/dot_git_colon", ".git/foobar"); +#endif +} + +/* A tree that contains an entry "git:foo", because Win32 APIs will turn + * that into ".git". + */ +void test_checkout_nasty__dot_git_colon_stuff(void) +{ +#ifdef GIT_WIN32 + test_checkout_fails("refs/heads/dot_git_colon_stuff", ".git/foobar"); +#endif +} + +/* Trees that contains entries with a tree ".git" that contain + * byte sequences: + * { 0xe2, 0x80, 0x8c } + * { 0xe2, 0x80, 0x8d } + * { 0xe2, 0x80, 0x8e } + * { 0xe2, 0x80, 0x8f } + * { 0xe2, 0x80, 0xaa } + * { 0xe2, 0x80, 0xab } + * { 0xe2, 0x80, 0xac } + * { 0xe2, 0x80, 0xad } + * { 0xe2, 0x81, 0xae } + * { 0xe2, 0x81, 0xaa } + * { 0xe2, 0x81, 0xab } + * { 0xe2, 0x81, 0xac } + * { 0xe2, 0x81, 0xad } + * { 0xe2, 0x81, 0xae } + * { 0xe2, 0x81, 0xaf } + * { 0xef, 0xbb, 0xbf } + * Because these map to characters that HFS filesystems "ignore". Thus + * ".git" will map to ".git". + */ +void test_checkout_nasty__dot_git_hfs_ignorable(void) +{ +#ifdef __APPLE__ + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_1", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_2", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_3", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_4", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_5", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_6", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_7", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_8", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_9", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_10", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_11", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_12", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_13", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_14", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_15", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_16", ".git/foobar"); +#endif +} + +void test_checkout_nasty__honors_core_protecthfs(void) +{ + cl_repo_set_bool(repo, "core.protectHFS", true); + + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_1", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_2", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_3", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_4", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_5", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_6", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_7", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_8", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_9", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_10", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_11", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_12", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_13", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_14", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_15", ".git/foobar"); + test_checkout_fails("refs/heads/dotgit_hfs_ignorable_16", ".git/foobar"); +} + +void test_checkout_nasty__honors_core_protectntfs(void) +{ + cl_repo_set_bool(repo, "core.protectNTFS", true); + + test_checkout_fails("refs/heads/dotgit_backslash_path", ".git/foobar"); + test_checkout_fails("refs/heads/dotcapitalgit_backslash_path", ".GIT/foobar"); + test_checkout_fails("refs/heads/dot_git_dot", ".git/foobar"); + test_checkout_fails("refs/heads/git_tilde1", ".git/foobar"); +} + +void test_checkout_nasty__symlink1(void) +{ + test_checkout_passes("refs/heads/symlink1", ".git/foobar"); +} + +void test_checkout_nasty__symlink2(void) +{ + test_checkout_passes("refs/heads/symlink2", ".git/foobar"); +} + +void test_checkout_nasty__symlink3(void) +{ + test_checkout_passes("refs/heads/symlink3", ".git/foobar"); +} + diff -Nru libgit2-0.20.0/tests/checkout/tree.c libgit2-0.22.2/tests/checkout/tree.c --- libgit2-0.20.0/tests/checkout/tree.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/checkout/tree.c 2015-03-24 16:10:45.000000000 +0000 @@ -7,14 +7,14 @@ #include "fileops.h" static git_repository *g_repo; -static git_checkout_opts g_opts; +static git_checkout_options g_opts; static git_object *g_object; void test_checkout_tree__initialize(void) { g_repo = cl_git_sandbox_init("testrepo"); - GIT_INIT_STRUCTURE(&g_opts, GIT_CHECKOUT_OPTS_VERSION); + GIT_INIT_STRUCTURE(&g_opts, GIT_CHECKOUT_OPTIONS_VERSION); g_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; } @@ -63,7 +63,7 @@ cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees")); cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts)); - cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees")); + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees", NULL, NULL)); cl_assert_equal_i(true, git_path_isdir("./testrepo/ab/")); cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/2.txt")); @@ -78,7 +78,7 @@ cl_git_pass(git_revparse_single(&g_object, g_repo, "master")); cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts)); - cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master")); + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master", NULL, NULL)); /* This directory should no longer exist */ cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/")); @@ -128,7 +128,7 @@ git_oid chomped_oid; git_commit* p_master_commit; git_commit* p_chomped_commit; - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_oid_fromstr(&master_oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); git_oid_fromstr(&chomped_oid, "e90810b8df3e80c413d903f631643c716887138d"); @@ -148,7 +148,7 @@ void test_checkout_tree__can_switch_branches(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_oid oid; git_object *obj = NULL; @@ -163,7 +163,7 @@ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); cl_git_pass(git_checkout_tree(g_repo, obj, &opts)); - cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir")); + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir", NULL, NULL)); cl_assert(git_path_isfile("testrepo/README")); cl_assert(git_path_isfile("testrepo/branch_file.txt")); @@ -183,7 +183,7 @@ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); cl_git_pass(git_checkout_tree(g_repo, obj, &opts)); - cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees")); + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees", NULL, NULL)); cl_assert(git_path_isfile("testrepo/README")); cl_assert(git_path_isfile("testrepo/branch_file.txt")); @@ -202,7 +202,7 @@ void test_checkout_tree__can_remove_untracked(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_REMOVE_UNTRACKED; @@ -216,7 +216,7 @@ void test_checkout_tree__can_remove_ignored(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; int ignored = 0; opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_REMOVE_IGNORED; @@ -235,9 +235,116 @@ cl_assert(!git_path_isfile("testrepo/ignored_file")); } +static int checkout_tree_with_blob_ignored_in_workdir(int strategy, bool isdir) +{ + git_oid oid; + git_object *obj = NULL; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + int ignored = 0, error; + + assert_on_branch(g_repo, "master"); + + /* do first checkout with FORCE because we don't know if testrepo + * base data is clean for a checkout or not + */ + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir")); + cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); + + cl_git_pass(git_checkout_tree(g_repo, obj, &opts)); + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir", NULL, NULL)); + + cl_assert(git_path_isfile("testrepo/README")); + cl_assert(git_path_isfile("testrepo/branch_file.txt")); + cl_assert(git_path_isfile("testrepo/new.txt")); + cl_assert(git_path_isfile("testrepo/a/b.txt")); + + cl_assert(!git_path_isdir("testrepo/ab")); + + assert_on_branch(g_repo, "dir"); + + git_object_free(obj); + + opts.checkout_strategy = strategy; + + if (isdir) { + cl_must_pass(p_mkdir("testrepo/ab", 0777)); + cl_must_pass(p_mkdir("testrepo/ab/4.txt", 0777)); + + cl_git_mkfile("testrepo/ab/4.txt/file1.txt", "as you wish"); + cl_git_mkfile("testrepo/ab/4.txt/file2.txt", "foo bar foo"); + cl_git_mkfile("testrepo/ab/4.txt/file3.txt", "inky blinky pinky clyde"); + + cl_assert(git_path_isdir("testrepo/ab/4.txt")); + } else { + cl_must_pass(p_mkdir("testrepo/ab", 0777)); + cl_git_mkfile("testrepo/ab/4.txt", "as you wish"); + + cl_assert(git_path_isfile("testrepo/ab/4.txt")); + } + + cl_git_pass(git_ignore_add_rule(g_repo, "ab/4.txt\n")); + + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "ab/4.txt")); + cl_assert_equal_i(1, ignored); + + cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/subtrees")); + cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); + + error = git_checkout_tree(g_repo, obj, &opts); + + git_object_free(obj); + + return error; +} + +void test_checkout_tree__conflict_on_ignored_when_not_overwriting(void) +{ + int error; + + cl_git_fail(error = checkout_tree_with_blob_ignored_in_workdir( + GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DONT_OVERWRITE_IGNORED, false)); + + cl_assert_equal_i(GIT_EMERGECONFLICT, error); +} + +void test_checkout_tree__can_overwrite_ignored_by_default(void) +{ + cl_git_pass(checkout_tree_with_blob_ignored_in_workdir(GIT_CHECKOUT_SAFE, false)); + + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees", NULL, NULL)); + + cl_assert(git_path_isfile("testrepo/ab/4.txt")); + + assert_on_branch(g_repo, "subtrees"); +} + +void test_checkout_tree__conflict_on_ignored_folder_when_not_overwriting(void) +{ + int error; + + cl_git_fail(error = checkout_tree_with_blob_ignored_in_workdir( + GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DONT_OVERWRITE_IGNORED, true)); + + cl_assert_equal_i(GIT_EMERGECONFLICT, error); +} + +void test_checkout_tree__can_overwrite_ignored_folder_by_default(void) +{ + cl_git_pass(checkout_tree_with_blob_ignored_in_workdir(GIT_CHECKOUT_SAFE, true)); + + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees", NULL, NULL)); + + cl_assert(git_path_isfile("testrepo/ab/4.txt")); + + assert_on_branch(g_repo, "subtrees"); + +} + void test_checkout_tree__can_update_only(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_oid oid; git_object *obj = NULL; @@ -260,7 +367,7 @@ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); cl_git_pass(git_checkout_tree(g_repo, obj, &opts)); - cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir")); + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir", NULL, NULL)); assert_on_branch(g_repo, "dir"); @@ -289,7 +396,7 @@ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts)); cl_git_pass( - git_repository_set_head_detached(g_repo, git_object_id(g_object))); + git_repository_set_head_detached(g_repo, git_object_id(g_object), NULL, NULL)); git_object_free(g_object); g_object = NULL; @@ -328,7 +435,7 @@ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts)); cl_git_pass( - git_repository_set_head_detached(g_repo, git_object_id(g_object))); + git_repository_set_head_detached(g_repo, git_object_id(g_object), NULL, NULL)); git_object_free(g_object); g_object = NULL; @@ -373,11 +480,11 @@ /* Create a branch pointing at the parent */ cl_git_pass(git_revparse_single(&g_object, g_repo, parent_sha)); cl_git_pass(git_branch_create(&branch, g_repo, - "potential_conflict", (git_commit *)g_object, 0)); + "potential_conflict", (git_commit *)g_object, 0, NULL, NULL)); /* Make HEAD point to this branch */ cl_git_pass(git_reference_symbolic_create( - &head, g_repo, "HEAD", git_reference_name(branch), 1)); + &head, g_repo, "HEAD", git_reference_name(branch), 1, NULL, NULL)); git_reference_free(head); git_reference_free(branch); @@ -447,7 +554,7 @@ void test_checkout_tree__donot_update_deleted_file_by_default(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_oid old_id, new_id; git_commit *old_commit = NULL, *new_commit = NULL; git_index *index = NULL; @@ -464,7 +571,7 @@ cl_git_pass(git_oid_fromstr(&old_id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); cl_git_pass(git_commit_lookup(&old_commit, g_repo, &old_id)); - cl_git_pass(git_reset(g_repo, (git_object *)old_commit, GIT_RESET_HARD)); + cl_git_pass(git_reset(g_repo, (git_object *)old_commit, GIT_RESET_HARD, NULL, NULL, NULL)); cl_git_pass(p_unlink("testrepo/branch_file.txt")); cl_git_pass(git_index_remove_bypath(index ,"branch_file.txt")); @@ -486,10 +593,77 @@ git_index_free(index); } +struct checkout_cancel_at { + const char *filename; + int error; + int count; +}; + +static int checkout_cancel_cb( + git_checkout_notify_t why, + const char *path, + const git_diff_file *b, + const git_diff_file *t, + const git_diff_file *w, + void *payload) +{ + struct checkout_cancel_at *ca = payload; + + GIT_UNUSED(why); GIT_UNUSED(b); GIT_UNUSED(t); GIT_UNUSED(w); + + ca->count++; + + if (!strcmp(path, ca->filename)) + return ca->error; + + return 0; +} + +void test_checkout_tree__can_cancel_checkout_from_notify(void) +{ + struct checkout_cancel_at ca; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_oid oid; + git_object *obj = NULL; + + assert_on_branch(g_repo, "master"); + + cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir")); + cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); + + ca.filename = "new.txt"; + ca.error = -5555; + ca.count = 0; + + opts.notify_flags = GIT_CHECKOUT_NOTIFY_UPDATED; + opts.notify_cb = checkout_cancel_cb; + opts.notify_payload = &ca; + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_assert(!git_path_exists("testrepo/new.txt")); + + cl_git_fail_with(git_checkout_tree(g_repo, obj, &opts), -5555); + + cl_assert(!git_path_exists("testrepo/new.txt")); + cl_assert_equal_i(4, ca.count); + + /* and again with a different stopping point and return code */ + ca.filename = "README"; + ca.error = 123; + ca.count = 0; + + cl_git_fail_with(git_checkout_tree(g_repo, obj, &opts), 123); + + cl_assert(!git_path_exists("testrepo/new.txt")); + cl_assert_equal_i(1, ca.count); + + git_object_free(obj); +} + void test_checkout_tree__can_checkout_with_last_workdir_item_missing(void) { git_index *index = NULL; - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_oid tree_id, commit_id; git_tree *tree = NULL; git_commit *commit = NULL; @@ -502,7 +676,7 @@ cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id)); cl_git_pass(git_checkout_tree(g_repo, (git_object *)commit, &opts)); - cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master")); + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master", NULL, NULL)); cl_git_pass(p_mkdir("./testrepo/this-is-dir", 0777)); cl_git_mkfile("./testrepo/this-is-dir/contained_file", "content\n"); @@ -525,7 +699,7 @@ void test_checkout_tree__issue_1397(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; const char *partial_oid = "8a7ef04"; git_object *tree = NULL; @@ -548,7 +722,7 @@ void test_checkout_tree__can_write_to_empty_dirs(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_oid oid; git_object *obj = NULL; @@ -574,7 +748,7 @@ void test_checkout_tree__fails_when_dir_in_use(void) { #ifdef GIT_WIN32 - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_oid oid; git_object *obj = NULL; @@ -607,7 +781,7 @@ void test_checkout_tree__can_continue_when_dir_in_use(void) { #ifdef GIT_WIN32 - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_oid oid; git_object *obj = NULL; @@ -640,7 +814,7 @@ void test_checkout_tree__target_directory_from_bare(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_oid oid; checkout_counts cts; memset(&cts, 0, sizeof(cts)); @@ -697,7 +871,7 @@ cl_assert(!git_path_exists(path)); } -static void create_conflict(void) +static void create_conflict(const char *path) { git_index *index; git_index_entry entry; @@ -707,16 +881,16 @@ memset(&entry, 0x0, sizeof(git_index_entry)); entry.mode = 0100644; entry.flags = 1 << GIT_IDXENTRY_STAGESHIFT; - git_oid_fromstr(&entry.oid, "d427e0b2e138501a3d15cc376077a3631e15bd46"); - entry.path = "conflicts.txt"; + git_oid_fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46"); + entry.path = path; cl_git_pass(git_index_add(index, &entry)); entry.flags = 2 << GIT_IDXENTRY_STAGESHIFT; - git_oid_fromstr(&entry.oid, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf"); + git_oid_fromstr(&entry.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf"); cl_git_pass(git_index_add(index, &entry)); entry.flags = 3 << GIT_IDXENTRY_STAGESHIFT; - git_oid_fromstr(&entry.oid, "2bd0a343aeef7a2cf0d158478966a6e587ff3863"); + git_oid_fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863"); cl_git_pass(git_index_add(index, &entry)); git_index_write(index); @@ -725,7 +899,7 @@ void test_checkout_tree__fails_when_conflicts_exist_in_index(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_oid oid; git_object *obj = NULL; @@ -734,9 +908,197 @@ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD")); cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY)); - create_conflict(); + create_conflict("conflicts.txt"); cl_git_fail(git_checkout_tree(g_repo, obj, &opts)); git_object_free(obj); } + +void test_checkout_tree__filemode_preserved_in_index(void) +{ + git_oid executable_oid; + git_commit *commit; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_index *index; + const git_index_entry *entry; + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass(git_oid_fromstr(&executable_oid, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6")); + cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid)); + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); + cl_assert(entry = git_index_get_bypath(index, "executable.txt", 0)); + cl_assert_equal_i(0100755, entry->mode); + + git_commit_free(commit); + git_index_free(index); +} + +void test_checkout_tree__removes_conflicts(void) +{ + git_oid commit_id; + git_commit *commit; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_index *index; + + cl_git_pass(git_oid_fromstr(&commit_id, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6")); + cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id)); + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_remove(index, "executable.txt", 0)); + + create_conflict("executable.txt"); + cl_git_mkfile("testrepo/executable.txt", "This is the conflict file.\n"); + + create_conflict("other.txt"); + cl_git_mkfile("testrepo/other.txt", "This is another conflict file.\n"); + + git_index_write(index); + + cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); + + cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 1)); + cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 2)); + cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 3)); + + cl_assert_equal_p(NULL, git_index_get_bypath(index, "other.txt", 1)); + cl_assert_equal_p(NULL, git_index_get_bypath(index, "other.txt", 2)); + cl_assert_equal_p(NULL, git_index_get_bypath(index, "other.txt", 3)); + + cl_assert(!git_path_exists("testrepo/other.txt")); + + git_commit_free(commit); + git_index_free(index); +} + + +void test_checkout_tree__removes_conflicts_only_by_pathscope(void) +{ + git_oid commit_id; + git_commit *commit; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_index *index; + const char *path = "executable.txt"; + + cl_git_pass(git_oid_fromstr(&commit_id, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6")); + cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id)); + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + opts.paths.count = 1; + opts.paths.strings = (char **)&path; + + cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_remove(index, "executable.txt", 0)); + + create_conflict("executable.txt"); + cl_git_mkfile("testrepo/executable.txt", "This is the conflict file.\n"); + + create_conflict("other.txt"); + cl_git_mkfile("testrepo/other.txt", "This is another conflict file.\n"); + + git_index_write(index); + + cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts)); + + cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 1)); + cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 2)); + cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 3)); + + cl_assert(git_index_get_bypath(index, "other.txt", 1) != NULL); + cl_assert(git_index_get_bypath(index, "other.txt", 2) != NULL); + cl_assert(git_index_get_bypath(index, "other.txt", 3) != NULL); + + cl_assert(git_path_exists("testrepo/other.txt")); + + git_commit_free(commit); + git_index_free(index); +} + +void test_checkout_tree__case_changing_rename(void) +{ + git_index *index; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; + git_oid master_id, dir_commit_id, tree_id, commit_id; + git_commit *master_commit, *dir_commit; + git_tree *tree; + git_signature *signature; + const git_index_entry *index_entry; + bool case_sensitive; + + assert_on_branch(g_repo, "master"); + + cl_git_pass(git_repository_index(&index, g_repo)); + + /* Switch branches and perform a case-changing rename */ + + opts.checkout_strategy = GIT_CHECKOUT_FORCE; + + cl_git_pass(git_reference_name_to_id(&dir_commit_id, g_repo, "refs/heads/dir")); + cl_git_pass(git_commit_lookup(&dir_commit, g_repo, &dir_commit_id)); + + cl_git_pass(git_checkout_tree(g_repo, (git_object *)dir_commit, &opts)); + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir", NULL, NULL)); + + cl_assert(git_path_isfile("testrepo/README")); + case_sensitive = !git_path_isfile("testrepo/readme"); + + cl_assert(index_entry = git_index_get_bypath(index, "README", 0)); + cl_assert_equal_s("README", index_entry->path); + + cl_git_pass(git_index_remove_bypath(index, "README")); + cl_git_pass(p_rename("testrepo/README", "testrepo/__readme__")); + cl_git_pass(p_rename("testrepo/__readme__", "testrepo/readme")); + cl_git_append2file("testrepo/readme", "An addendum..."); + cl_git_pass(git_index_add_bypath(index, "readme")); + + cl_git_pass(git_index_write(index)); + + cl_git_pass(git_index_write_tree(&tree_id, index)); + cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); + + cl_git_pass(git_signature_new(&signature, "Renamer", "rename@contoso.com", time(NULL), 0)); + + cl_git_pass(git_commit_create(&commit_id, g_repo, "refs/heads/dir", signature, signature, NULL, "case-changing rename", tree, 1, (const git_commit **)&dir_commit)); + + cl_assert(git_path_isfile("testrepo/readme")); + if (case_sensitive) + cl_assert(!git_path_isfile("testrepo/README")); + + cl_assert(index_entry = git_index_get_bypath(index, "readme", 0)); + cl_assert_equal_s("readme", index_entry->path); + + /* Switching back to master should rename readme -> README */ + opts.checkout_strategy = GIT_CHECKOUT_SAFE; + + cl_git_pass(git_reference_name_to_id(&master_id, g_repo, "refs/heads/master")); + cl_git_pass(git_commit_lookup(&master_commit, g_repo, &master_id)); + + cl_git_pass(git_checkout_tree(g_repo, (git_object *)master_commit, &opts)); + cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master", NULL, NULL)); + + assert_on_branch(g_repo, "master"); + + cl_assert(git_path_isfile("testrepo/README")); + if (case_sensitive) + cl_assert(!git_path_isfile("testrepo/readme")); + + cl_assert(index_entry = git_index_get_bypath(index, "README", 0)); + cl_assert_equal_s("README", index_entry->path); + + git_index_free(index); + git_signature_free(signature); + git_tree_free(tree); + git_commit_free(dir_commit); + git_commit_free(master_commit); +} + diff -Nru libgit2-0.20.0/tests/checkout/typechange.c libgit2-0.22.2/tests/checkout/typechange.c --- libgit2-0.20.0/tests/checkout/typechange.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/checkout/typechange.c 2015-03-24 16:10:45.000000000 +0000 @@ -107,7 +107,7 @@ { int i; git_object *obj; - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; for (i = 0; g_typechange_oids[i] != NULL; ++i) { cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i])); @@ -122,7 +122,7 @@ cl_git_pass(git_checkout_tree(g_repo, obj, &opts)); cl_git_pass( - git_repository_set_head_detached(g_repo, git_object_id(obj))); + git_repository_set_head_detached(g_repo, git_object_id(obj), NULL, NULL)); assert_workdir_matches_tree(g_repo, git_object_id(obj), NULL, true); @@ -194,7 +194,7 @@ { int i; git_object *obj; - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; notify_counts cts = {0}; opts.notify_flags = @@ -231,7 +231,7 @@ cl_assert(!git_path_exists("typechanges/untracked")); cl_git_pass( - git_repository_set_head_detached(g_repo, git_object_id(obj))); + git_repository_set_head_detached(g_repo, git_object_id(obj), NULL, NULL)); assert_workdir_matches_tree(g_repo, git_object_id(obj), NULL, true); diff -Nru libgit2-0.20.0/tests/cherrypick/bare.c libgit2-0.22.2/tests/cherrypick/bare.c --- libgit2-0.20.0/tests/cherrypick/bare.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/cherrypick/bare.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,106 @@ +#include "clar.h" +#include "clar_libgit2.h" + +#include "buffer.h" +#include "fileops.h" +#include "git2/cherrypick.h" + +#include "../merge/merge_helpers.h" + +#define TEST_REPO_PATH "cherrypick" + +static git_repository *repo; + +void test_cherrypick_bare__initialize(void) +{ + repo = cl_git_sandbox_init(TEST_REPO_PATH); +} + +void test_cherrypick_bare__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_cherrypick_bare__automerge(void) +{ + git_commit *head = NULL, *commit = NULL; + git_index *index = NULL; + git_oid head_oid, cherry_oid; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "38c05a857e831a7e759d83778bfc85d003e21c45", 0, "file1.txt" }, + { 0100644, "a661b5dec1004e2c62654ded3762370c27cf266b", 0, "file2.txt" }, + { 0100644, "df6b290e0bd6a89b01d69f66687e8abf385283ca", 0, "file3.txt" }, + }; + + git_oid_fromstr(&head_oid, "d3d77487660ee3c0194ee01dc5eaf478782b1c7e"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + + git_oid_fromstr(&cherry_oid, "cfc4f0999a8367568e049af4f72e452d40828a15"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + + cl_git_pass(git_cherrypick_commit(&index, repo, commit, head, 0, NULL)); + cl_assert(merge_test_index(index, merge_index_entries, 3)); + + git_index_free(index); + git_commit_free(head); + git_commit_free(commit); +} + +void test_cherrypick_bare__conflicts(void) +{ + git_commit *head = NULL, *commit = NULL; + git_index *index = NULL; + git_oid head_oid, cherry_oid; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "242e7977ba73637822ffb265b46004b9b0e5153b", 0, "file1.txt" }, + { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 1, "file2.txt" }, + { 0100644, "bd6ffc8c6c41f0f85ff9e3d61c9479516bac0024", 2, "file2.txt" }, + { 0100644, "563f6473a3858f99b80e5f93c660512ed38e1e6f", 3, "file2.txt" }, + { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 1, "file3.txt" }, + { 0100644, "1124c2c1ae07b26fded662d6c3f3631d9dc16f88", 2, "file3.txt" }, + { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 3, "file3.txt" }, + }; + + git_oid_fromstr(&head_oid, "bafbf6912c09505ac60575cd43d3f2aba3bd84d8"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + + git_oid_fromstr(&cherry_oid, "e9b63f3655b2ad80c0ff587389b5a9589a3a7110"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + + cl_git_pass(git_cherrypick_commit(&index, repo, commit, head, 0, NULL)); + cl_assert(merge_test_index(index, merge_index_entries, 7)); + + git_index_free(index); + git_commit_free(head); + git_commit_free(commit); +} + +void test_cherrypick_bare__orphan(void) +{ + git_commit *head = NULL, *commit = NULL; + git_index *index = NULL; + git_oid head_oid, cherry_oid; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "38c05a857e831a7e759d83778bfc85d003e21c45", 0, "file1.txt" }, + { 0100644, "a661b5dec1004e2c62654ded3762370c27cf266b", 0, "file2.txt" }, + { 0100644, "85a4a1d791973644f24c72f5e89420d3064cc452", 0, "file3.txt" }, + { 0100644, "9ccb9bf50c011fd58dcbaa65df917bf79539717f", 0, "orphan.txt" }, + }; + + git_oid_fromstr(&head_oid, "d3d77487660ee3c0194ee01dc5eaf478782b1c7e"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + + git_oid_fromstr(&cherry_oid, "74f06b5bfec6d33d7264f73606b57a7c0b963819"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + + cl_git_pass(git_cherrypick_commit(&index, repo, commit, head, 0, NULL)); + cl_assert(merge_test_index(index, merge_index_entries, 4)); + + git_index_free(index); + git_commit_free(head); + git_commit_free(commit); +} + diff -Nru libgit2-0.20.0/tests/cherrypick/workdir.c libgit2-0.22.2/tests/cherrypick/workdir.c --- libgit2-0.20.0/tests/cherrypick/workdir.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/cherrypick/workdir.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,470 @@ +#include "clar.h" +#include "clar_libgit2.h" + +#include "buffer.h" +#include "fileops.h" +#include "git2/cherrypick.h" + +#include "../merge/merge_helpers.h" + +#define TEST_REPO_PATH "cherrypick" + +static git_repository *repo; +static git_index *repo_index; + +// Fixture setup and teardown +void test_cherrypick_workdir__initialize(void) +{ + repo = cl_git_sandbox_init(TEST_REPO_PATH); + git_repository_index(&repo_index, repo); +} + +void test_cherrypick_workdir__cleanup(void) +{ + git_index_free(repo_index); + cl_git_sandbox_cleanup(); +} + +/* git reset --hard d3d77487660ee3c0194ee01dc5eaf478782b1c7e + * git cherry-pick cfc4f0999a8367568e049af4f72e452d40828a15 + * git cherry-pick 964ea3da044d9083181a88ba6701de9e35778bf4 + * git cherry-pick a43a050c588d4e92f11a6b139680923e9728477d + */ +void test_cherrypick_workdir__automerge(void) +{ + git_oid head_oid; + git_signature *signature = NULL; + size_t i; + + const char *cherrypick_oids[] = { + "cfc4f0999a8367568e049af4f72e452d40828a15", + "964ea3da044d9083181a88ba6701de9e35778bf4", + "a43a050c588d4e92f11a6b139680923e9728477d", + }; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "38c05a857e831a7e759d83778bfc85d003e21c45", 0, "file1.txt" }, + { 0100644, "a661b5dec1004e2c62654ded3762370c27cf266b", 0, "file2.txt" }, + { 0100644, "df6b290e0bd6a89b01d69f66687e8abf385283ca", 0, "file3.txt" }, + + { 0100644, "38c05a857e831a7e759d83778bfc85d003e21c45", 0, "file1.txt" }, + { 0100644, "bd8fc3c59fb52d3c8b5907ace7defa5803f82419", 0, "file2.txt" }, + { 0100644, "df6b290e0bd6a89b01d69f66687e8abf385283ca", 0, "file3.txt" }, + + { 0100644, "f06427bee380364bc7e0cb26a9245158e4726ce0", 0, "file1.txt" }, + { 0100644, "bd8fc3c59fb52d3c8b5907ace7defa5803f82419", 0, "file2.txt" }, + { 0100644, "df6b290e0bd6a89b01d69f66687e8abf385283ca", 0, "file3.txt" }, + }; + + cl_git_pass(git_signature_new(&signature, "Picker", "picker@example.org", time(NULL), 0)); + + git_oid_fromstr(&head_oid, "d3d77487660ee3c0194ee01dc5eaf478782b1c7e"); + + for (i = 0; i < 3; ++i) { + git_commit *head = NULL, *commit = NULL; + git_oid cherry_oid, cherrypicked_oid, cherrypicked_tree_oid; + git_tree *cherrypicked_tree = NULL; + + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + git_oid_fromstr(&cherry_oid, cherrypick_oids[i]); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + cl_git_pass(git_cherrypick(repo, commit, NULL)); + + cl_assert(git_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD")); + cl_assert(git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG")); + + cl_git_pass(git_index_write_tree(&cherrypicked_tree_oid, repo_index)); + cl_git_pass(git_tree_lookup(&cherrypicked_tree, repo, &cherrypicked_tree_oid)); + cl_git_pass(git_commit_create(&cherrypicked_oid, repo, "HEAD", signature, signature, NULL, + "Cherry picked!", cherrypicked_tree, 1, (const git_commit **)&head)); + + cl_assert(merge_test_index(repo_index, merge_index_entries + i * 3, 3)); + + git_oid_cpy(&head_oid, &cherrypicked_oid); + + git_tree_free(cherrypicked_tree); + git_commit_free(head); + git_commit_free(commit); + } + + git_signature_free(signature); +} + +/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15 + * git cherry-pick a43a050c588d4e92f11a6b139680923e9728477d*/ +void test_cherrypick_workdir__empty_result(void) +{ + git_oid head_oid; + git_signature *signature = NULL; + git_commit *head = NULL, *commit = NULL; + git_oid cherry_oid; + + const char *cherrypick_oid = "a43a050c588d4e92f11a6b139680923e9728477d"; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "19c5c7207054604b69c84d08a7571ef9672bb5c2", 0, "file1.txt" }, + { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 0, "file2.txt" }, + { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 0, "file3.txt" }, + }; + + cl_git_pass(git_signature_new(&signature, "Picker", "picker@example.org", time(NULL), 0)); + + git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15"); + + /* Create an untracked file that should not conflict */ + cl_git_mkfile(TEST_REPO_PATH "/file4.txt", ""); + cl_assert(git_path_exists(TEST_REPO_PATH "/file4.txt")); + + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + git_oid_fromstr(&cherry_oid, cherrypick_oid); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + cl_git_pass(git_cherrypick(repo, commit, NULL)); + + /* The resulting tree should not have changed, the change was already on HEAD */ + cl_assert(merge_test_index(repo_index, merge_index_entries, 3)); + + git_commit_free(head); + git_commit_free(commit); + + git_signature_free(signature); +} + +/* git reset --hard bafbf6912c09505ac60575cd43d3f2aba3bd84d8 + * git cherry-pick e9b63f3655b2ad80c0ff587389b5a9589a3a7110 + */ +void test_cherrypick_workdir__conflicts(void) +{ + git_commit *head = NULL, *commit = NULL; + git_oid head_oid, cherry_oid; + git_buf conflicting_buf = GIT_BUF_INIT, mergemsg_buf = GIT_BUF_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "242e7977ba73637822ffb265b46004b9b0e5153b", 0, "file1.txt" }, + { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 1, "file2.txt" }, + { 0100644, "bd6ffc8c6c41f0f85ff9e3d61c9479516bac0024", 2, "file2.txt" }, + { 0100644, "563f6473a3858f99b80e5f93c660512ed38e1e6f", 3, "file2.txt" }, + { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 1, "file3.txt" }, + { 0100644, "1124c2c1ae07b26fded662d6c3f3631d9dc16f88", 2, "file3.txt" }, + { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 3, "file3.txt" }, + }; + + git_oid_fromstr(&head_oid, "bafbf6912c09505ac60575cd43d3f2aba3bd84d8"); + + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + git_oid_fromstr(&cherry_oid, "e9b63f3655b2ad80c0ff587389b5a9589a3a7110"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + cl_git_pass(git_cherrypick(repo, commit, NULL)); + + cl_assert(git_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD")); + cl_assert(git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG")); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 7)); + + cl_git_pass(git_futils_readbuffer(&mergemsg_buf, + TEST_REPO_PATH "/.git/MERGE_MSG")); + cl_assert(strcmp(git_buf_cstr(&mergemsg_buf), + "Change all files\n" \ + "\n" \ + "Conflicts:\n" \ + "\tfile2.txt\n" \ + "\tfile3.txt\n") == 0); + + cl_git_pass(git_futils_readbuffer(&conflicting_buf, + TEST_REPO_PATH "/file2.txt")); + + cl_assert(strcmp(git_buf_cstr(&conflicting_buf), + "!File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2!!\n" \ + "File 2\n" \ + "File 2\n" \ + "File 2\n" \ + "<<<<<<< HEAD\n" \ + "File 2\n" \ + "=======\n" \ + "File 2!\n" \ + "File 2\n" \ + "File 2!\n" \ + ">>>>>>> e9b63f3... Change all files\n") == 0); + + cl_git_pass(git_futils_readbuffer(&conflicting_buf, + TEST_REPO_PATH "/file3.txt")); + + cl_assert(strcmp(git_buf_cstr(&conflicting_buf), + "!File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3!!\n" \ + "File 3\n" \ + "File 3\n" \ + "File 3\n" \ + "<<<<<<< HEAD\n" \ + "=======\n" \ + "File 3!\n" \ + "File 3!\n" \ + ">>>>>>> e9b63f3... Change all files\n") == 0); + + git_commit_free(commit); + git_commit_free(head); + git_buf_free(&mergemsg_buf); + git_buf_free(&conflicting_buf); +} + +/* git reset --hard bafbf6912c09505ac60575cd43d3f2aba3bd84d8 + * git cherry-pick -X ours e9b63f3655b2ad80c0ff587389b5a9589a3a7110 + */ +void test_cherrypick_workdir__conflict_use_ours(void) +{ + git_commit *head = NULL, *commit = NULL; + git_oid head_oid, cherry_oid; + git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "242e7977ba73637822ffb265b46004b9b0e5153b", 0, "file1.txt" }, + { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 1, "file2.txt" }, + { 0100644, "bd6ffc8c6c41f0f85ff9e3d61c9479516bac0024", 2, "file2.txt" }, + { 0100644, "563f6473a3858f99b80e5f93c660512ed38e1e6f", 3, "file2.txt" }, + { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 1, "file3.txt" }, + { 0100644, "1124c2c1ae07b26fded662d6c3f3631d9dc16f88", 2, "file3.txt" }, + { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 3, "file3.txt" }, + }; + + struct merge_index_entry merge_filesystem_entries[] = { + { 0100644, "242e7977ba73637822ffb265b46004b9b0e5153b", 0, "file1.txt" }, + { 0100644, "bd6ffc8c6c41f0f85ff9e3d61c9479516bac0024", 0, "file2.txt" }, + { 0100644, "1124c2c1ae07b26fded662d6c3f3631d9dc16f88", 0, "file3.txt" }, + }; + + /* leave the index in a conflicted state, but checkout "ours" to the workdir */ + opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS; + + git_oid_fromstr(&head_oid, "bafbf6912c09505ac60575cd43d3f2aba3bd84d8"); + + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + git_oid_fromstr(&cherry_oid, "e9b63f3655b2ad80c0ff587389b5a9589a3a7110"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + cl_git_pass(git_cherrypick(repo, commit, &opts)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 7)); + cl_assert(merge_test_workdir(repo, merge_filesystem_entries, 3)); + + /* resolve conflicts in the index by taking "ours" */ + opts.merge_opts.file_favor = GIT_MERGE_FILE_FAVOR_OURS; + + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + cl_git_pass(git_cherrypick(repo, commit, &opts)); + + cl_assert(merge_test_index(repo_index, merge_filesystem_entries, 3)); + cl_assert(merge_test_workdir(repo, merge_filesystem_entries, 3)); + + git_commit_free(commit); + git_commit_free(head); +} + +/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15 + * git cherry-pick 2a26c7e88b285613b302ba76712bc998863f3cbc + */ +void test_cherrypick_workdir__rename(void) +{ + git_commit *head, *commit; + git_oid head_oid, cherry_oid; + git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "19c5c7207054604b69c84d08a7571ef9672bb5c2", 0, "file1.txt" }, + { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 0, "file2.txt" }, + { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 0, "file3.txt.renamed" }, + }; + + opts.merge_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES; + opts.merge_opts.rename_threshold = 50; + + git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + git_oid_fromstr(&cherry_oid, "2a26c7e88b285613b302ba76712bc998863f3cbc"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + cl_git_pass(git_cherrypick(repo, commit, &opts)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 3)); + + git_commit_free(commit); + git_commit_free(head); +} + +/* git reset --hard 44cd2ed2052c9c68f9a439d208e9614dc2a55c70 + * git cherry-pick 2a26c7e88b285613b302ba76712bc998863f3cbc + */ +void test_cherrypick_workdir__both_renamed(void) +{ + git_commit *head, *commit; + git_oid head_oid, cherry_oid; + git_buf mergemsg_buf = GIT_BUF_INIT; + git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "19c5c7207054604b69c84d08a7571ef9672bb5c2", 0, "file1.txt" }, + { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 0, "file2.txt" }, + { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 1, "file3.txt" }, + { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 3, "file3.txt.renamed" }, + { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 2, "file3.txt.renamed_on_branch" }, + }; + + opts.merge_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES; + opts.merge_opts.rename_threshold = 50; + + git_oid_fromstr(&head_oid, "44cd2ed2052c9c68f9a439d208e9614dc2a55c70"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + git_oid_fromstr(&cherry_oid, "2a26c7e88b285613b302ba76712bc998863f3cbc"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + cl_git_pass(git_cherrypick(repo, commit, &opts)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 5)); + + cl_git_pass(git_futils_readbuffer(&mergemsg_buf, + TEST_REPO_PATH "/.git/MERGE_MSG")); + cl_assert(strcmp(git_buf_cstr(&mergemsg_buf), + "Renamed file3.txt -> file3.txt.renamed\n" \ + "\n" \ + "Conflicts:\n" \ + "\tfile3.txt\n" \ + "\tfile3.txt.renamed\n" \ + "\tfile3.txt.renamed_on_branch\n") == 0); + + git_buf_free(&mergemsg_buf); + git_commit_free(commit); + git_commit_free(head); +} + +void test_cherrypick_workdir__nonmerge_fails_mainline_specified(void) +{ + git_reference *head; + git_commit *commit; + git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT; + + cl_git_pass(git_repository_head(&head, repo)); + cl_git_pass(git_reference_peel((git_object **)&commit, head, GIT_OBJ_COMMIT)); + + opts.mainline = 1; + cl_must_fail(git_cherrypick(repo, commit, &opts)); + cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD")); + cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG")); + + git_reference_free(head); + git_commit_free(commit); +} + +/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15 + * git cherry-pick abe4603bc7cd5b8167a267e0e2418fd2348f8cff + */ +void test_cherrypick_workdir__merge_fails_without_mainline_specified(void) +{ + git_commit *head, *commit; + git_oid head_oid, cherry_oid; + + git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + git_oid_fromstr(&cherry_oid, "abe4603bc7cd5b8167a267e0e2418fd2348f8cff"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + + cl_must_fail(git_cherrypick(repo, commit, NULL)); + cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD")); + cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG")); + + git_commit_free(commit); + git_commit_free(head); +} + +/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15 + * git cherry-pick -m1 abe4603bc7cd5b8167a267e0e2418fd2348f8cff + */ +void test_cherrypick_workdir__merge_first_parent(void) +{ + git_commit *head, *commit; + git_oid head_oid, cherry_oid; + git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "f90f9dcbdac2cce5cc166346160e19cb693ef4e8", 0, "file1.txt" }, + { 0100644, "563f6473a3858f99b80e5f93c660512ed38e1e6f", 0, "file2.txt" }, + { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 0, "file3.txt" }, + }; + + opts.mainline = 1; + + git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + git_oid_fromstr(&cherry_oid, "abe4603bc7cd5b8167a267e0e2418fd2348f8cff"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + + cl_git_pass(git_cherrypick(repo, commit, &opts)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 3)); + + git_commit_free(commit); + git_commit_free(head); +} + +/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15 + * git cherry-pick -m2 abe4603bc7cd5b8167a267e0e2418fd2348f8cff + */ +void test_cherrypick_workdir__merge_second_parent(void) +{ + git_commit *head, *commit; + git_oid head_oid, cherry_oid; + git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "487434cace79238a7091e2220611d4f20a765690", 0, "file1.txt" }, + { 0100644, "e5183bfd18e3a0a691fadde2f0d5610b73282d31", 0, "file2.txt" }, + { 0100644, "409a1bec58bf35348e8b62b72bb9c1f45cf5a587", 0, "file3.txt" }, + }; + + opts.mainline = 2; + + git_oid_fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + git_oid_fromstr(&cherry_oid, "abe4603bc7cd5b8167a267e0e2418fd2348f8cff"); + cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid)); + + cl_git_pass(git_cherrypick(repo, commit, &opts)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 3)); + + git_commit_free(commit); + git_commit_free(head); +} + diff -Nru libgit2-0.20.0/tests/clar/fs.h libgit2-0.22.2/tests/clar/fs.h --- libgit2-0.20.0/tests/clar/fs.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/clar/fs.h 2015-03-24 16:10:45.000000000 +0000 @@ -12,7 +12,7 @@ #endif /* __MINGW32__ */ -static int +static int fs__dotordotdot(WCHAR *_tocheck) { return _tocheck[0] == '.' && @@ -201,7 +201,7 @@ DWORD source_attrs, dest_attrs; HANDLE find_handle; WIN32_FIND_DATAW find_data; - + /* The input paths are UTF-8. Convert them to wide characters * for use with the Windows API. */ cl_assert(MultiByteToWideChar(CP_UTF8, @@ -251,17 +251,22 @@ } #else + +#include +#include + static int shell_out(char * const argv[]) { - int status; + int status, piderr; pid_t pid; pid = fork(); if (pid < 0) { fprintf(stderr, - "System error: `fork()` call failed.\n"); + "System error: `fork()` call failed (%d) - %s\n", + errno, strerror(errno)); exit(-1); } @@ -269,7 +274,10 @@ execv(argv[0], argv); } - waitpid(pid, &status, 0); + do { + piderr = waitpid(pid, &status, WUNTRACED); + } while (piderr < 0 && (errno == EAGAIN || errno == EINTR)); + return WEXITSTATUS(status); } diff -Nru libgit2-0.20.0/tests/clar/print.h libgit2-0.22.2/tests/clar/print.h --- libgit2-0.20.0/tests/clar/print.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/clar/print.h 2015-03-24 16:10:45.000000000 +0000 @@ -35,11 +35,17 @@ fflush(stdout); } -static void clar_print_ontest(const char *test_name, int test_number, int failed) +static void clar_print_ontest(const char *test_name, int test_number, enum cl_test_status status) { (void)test_name; (void)test_number; - printf("%c", failed ? 'F' : '.'); + + switch(status) { + case CL_TEST_OK: printf("."); break; + case CL_TEST_FAILURE: printf("F"); break; + case CL_TEST_SKIP: printf("S"); break; + } + fflush(stdout); } diff -Nru libgit2-0.20.0/tests/clar/sandbox.h libgit2-0.22.2/tests/clar/sandbox.h --- libgit2-0.20.0/tests/clar/sandbox.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/clar/sandbox.h 2015-03-24 16:10:45.000000000 +0000 @@ -72,7 +72,12 @@ static int build_sandbox_path(void) { +#ifdef CLAR_TMPDIR + const char path_tail[] = CLAR_TMPDIR "_XXXXXX"; +#else const char path_tail[] = "clar_tmp_XXXXXX"; +#endif + size_t len; if (find_tmp_path(_clar_path, sizeof(_clar_path)) < 0) @@ -127,3 +132,8 @@ return 0; } +const char *clar_sandbox_path(void) +{ + return _clar_path; +} + diff -Nru libgit2-0.20.0/tests/clar.c libgit2-0.22.2/tests/clar.c --- libgit2-0.20.0/tests/clar.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/clar.c 2015-03-24 16:10:45.000000000 +0000 @@ -11,6 +11,7 @@ #include #include #include +#include /* required for sandboxing */ #include @@ -65,7 +66,12 @@ # ifndef PRIxZ # define PRIxZ "Ix" # endif + +# ifdef _MSC_VER + typedef struct stat STAT_T; +# else typedef struct _stat STAT_T; +# endif #else # include /* waitpid(2) */ # include @@ -101,10 +107,14 @@ }; static struct { + int argc; + char **argv; + + enum cl_test_status test_status; const char *active_test; const char *active_suite; - int suite_errors; + int total_skipped; int total_errors; int tests_ran; @@ -142,7 +152,7 @@ static void clar_print_init(int test_count, int suite_count, const char *suite_names); static void clar_print_shutdown(int test_count, int suite_count, int error_count); static void clar_print_error(int num, const struct clar_error *error); -static void clar_print_ontest(const char *test_name, int test_number, int failed); +static void clar_print_ontest(const char *test_name, int test_number, enum cl_test_status failed); static void clar_print_onsuite(const char *suite_name, int suite_index); static void clar_print_onabort(const char *msg, ...); @@ -178,8 +188,7 @@ const struct clar_func *initialize, const struct clar_func *cleanup) { - int error_st = _clar.suite_errors; - + _clar.test_status = CL_TEST_OK; _clar.trampoline_enabled = 1; if (setjmp(_clar.trampoline) == 0) { @@ -203,14 +212,11 @@ _clar.local_cleanup = NULL; _clar.local_cleanup_payload = NULL; - if (_clar.report_errors_only) + if (_clar.report_errors_only) { clar_report_errors(); - else - clar_print_ontest( - test->name, - _clar.tests_ran, - (_clar.suite_errors > error_st) - ); + } else { + clar_print_ontest(test->name, _clar.tests_ran, _clar.test_status); + } } static void @@ -229,7 +235,6 @@ clar_print_onsuite(suite->name, ++_clar.suites_ran); _clar.active_suite = suite->name; - _clar.suite_errors = 0; if (filter) { size_t suitelen = strlen(suite->name); @@ -348,8 +353,8 @@ } } -int -clar_test(int argc, char **argv) +void +clar_test_init(int argc, char **argv) { clar_print_init( (int)_clar_callback_count, @@ -362,8 +367,15 @@ exit(-1); } - if (argc > 1) - clar_parse_args(argc, argv); + _clar.argc = argc; + _clar.argv = argv; +} + +int +clar_test_run() +{ + if (_clar.argc > 1) + clar_parse_args(_clar.argc, _clar.argv); if (!_clar.suites_ran) { size_t i; @@ -371,6 +383,12 @@ clar_run_suite(&_clar_suites[i], NULL); } + return _clar.total_errors; +} + +void +clar_test_shutdown() +{ clar_print_shutdown( _clar.tests_ran, (int)_clar_suite_count, @@ -378,7 +396,37 @@ ); clar_unsandbox(); - return _clar.total_errors; +} + +int +clar_test(int argc, char **argv) +{ + int errors; + + clar_test_init(argc, argv); + errors = clar_test_run(); + clar_test_shutdown(); + + return errors; +} + +static void abort_test(void) +{ + if (!_clar.trampoline_enabled) { + clar_print_onabort( + "Fatal error: a cleanup method raised an exception."); + clar_report_errors(); + exit(-1); + } + + longjmp(_clar.trampoline, -1); +} + +void clar__skip(void) +{ + _clar.test_status = CL_TEST_SKIP; + _clar.total_skipped++; + abort_test(); } void clar__fail( @@ -408,19 +456,11 @@ if (description != NULL) error->description = strdup(description); - _clar.suite_errors++; _clar.total_errors++; + _clar.test_status = CL_TEST_FAILURE; - if (should_abort) { - if (!_clar.trampoline_enabled) { - clar_print_onabort( - "Fatal error: a cleanup method raised an exception."); - clar_report_errors(); - exit(-1); - } - - longjmp(_clar.trampoline, -1); - } + if (should_abort) + abort_test(); } void clar__assert( @@ -468,6 +508,59 @@ } } } + else if(!strcmp("%.*s", fmt)) { + const char *s1 = va_arg(args, const char *); + const char *s2 = va_arg(args, const char *); + int len = va_arg(args, int); + is_equal = (!s1 || !s2) ? (s1 == s2) : !strncmp(s1, s2, len); + + if (!is_equal) { + if (s1 && s2) { + int pos; + for (pos = 0; s1[pos] == s2[pos] && pos < len; ++pos) + /* find differing byte offset */; + p_snprintf(buf, sizeof(buf), "'%.*s' != '%.*s' (at byte %d)", + len, s1, len, s2, pos); + } else { + p_snprintf(buf, sizeof(buf), "'%.*s' != '%.*s'", len, s1, len, s2); + } + } + } + else if (!strcmp("%ls", fmt)) { + const wchar_t *wcs1 = va_arg(args, const wchar_t *); + const wchar_t *wcs2 = va_arg(args, const wchar_t *); + is_equal = (!wcs1 || !wcs2) ? (wcs1 == wcs2) : !wcscmp(wcs1, wcs2); + + if (!is_equal) { + if (wcs1 && wcs2) { + int pos; + for (pos = 0; wcs1[pos] == wcs2[pos] && wcs1[pos] && wcs2[pos]; ++pos) + /* find differing byte offset */; + p_snprintf(buf, sizeof(buf), "'%ls' != '%ls' (at byte %d)", + wcs1, wcs2, pos); + } else { + p_snprintf(buf, sizeof(buf), "'%ls' != '%ls'", wcs1, wcs2); + } + } + } + else if(!strcmp("%.*ls", fmt)) { + const wchar_t *wcs1 = va_arg(args, const wchar_t *); + const wchar_t *wcs2 = va_arg(args, const wchar_t *); + int len = va_arg(args, int); + is_equal = (!wcs1 || !wcs2) ? (wcs1 == wcs2) : !wcsncmp(wcs1, wcs2, len); + + if (!is_equal) { + if (wcs1 && wcs2) { + int pos; + for (pos = 0; wcs1[pos] == wcs2[pos] && pos < len; ++pos) + /* find differing byte offset */; + p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls' (at byte %d)", + len, wcs1, len, wcs2, pos); + } else { + p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls'", len, wcs1, len, wcs2); + } + } + } else if (!strcmp("%"PRIuZ, fmt) || !strcmp("%"PRIxZ, fmt)) { size_t sz1 = va_arg(args, size_t), sz2 = va_arg(args, size_t); is_equal = (sz1 == sz2); diff -Nru libgit2-0.20.0/tests/clar.h libgit2-0.22.2/tests/clar.h --- libgit2-0.20.0/tests/clar.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/clar.h 2015-03-24 16:10:45.000000000 +0000 @@ -9,8 +9,20 @@ #include +enum cl_test_status { + CL_TEST_OK, + CL_TEST_FAILURE, + CL_TEST_SKIP +}; + +void clar_test_init(int argc, char *argv[]); +int clar_test_run(void); +void clar_test_shutdown(void); + int clar_test(int argc, char *argv[]); +const char *clar_sandbox_path(void); + void cl_set_cleanup(void (*cleanup)(void *), void *opaque); void cl_fs_cleanup(void); @@ -54,12 +66,23 @@ #define cl_fail(desc) clar__fail(__FILE__, __LINE__, "Test failed.", desc, 1) #define cl_warning(desc) clar__fail(__FILE__, __LINE__, "Warning during test execution:", desc, 0) +#define cl_skip() clar__skip() + /** * Typed assertion macros */ #define cl_assert_equal_s(s1,s2) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2)) #define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2)) +#define cl_assert_equal_wcs(wcs1,wcs2) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%ls", (wcs1), (wcs2)) +#define cl_assert_equal_wcs_(wcs1,wcs2,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%ls", (wcs1), (wcs2)) + +#define cl_assert_equal_strn(s1,s2,len) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%.*s", (s1), (s2), (int)(len)) +#define cl_assert_equal_strn_(s1,s2,len,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%.*s", (s1), (s2), (int)(len)) + +#define cl_assert_equal_wcsn(wcs1,wcs2,len) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len)) +#define cl_assert_equal_wcsn_(wcs1,wcs2,len,note) clar__assert_equal(__FILE__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len)) + #define cl_assert_equal_i(i1,i2) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2)) #define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2)) #define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(__FILE__,__LINE__,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2)) @@ -68,6 +91,7 @@ #define cl_assert_equal_p(p1,p2) clar__assert_equal(__FILE__,__LINE__,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2)) +void clar__skip(void); void clar__fail( const char *file, diff -Nru libgit2-0.20.0/tests/clar_libgit2.c libgit2-0.22.2/tests/clar_libgit2.c --- libgit2-0.20.0/tests/clar_libgit2.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/clar_libgit2.c 2015-03-24 16:10:45.000000000 +0000 @@ -18,7 +18,7 @@ int fd; fd = p_creat(filename, 0666); - cl_assert(fd != 0); + cl_assert(fd != -1); if (content) { cl_must_pass(p_write(fd, content, strlen(content))); @@ -59,49 +59,44 @@ char *cl_getenv(const char *name) { - git_win32_path name_utf16; - DWORD alloc_len; - wchar_t *value_utf16; - char *value_utf8; + wchar_t *wide_name, *wide_value; + char *utf8_value = NULL; + DWORD value_len; - git_win32_path_from_c(name_utf16, name); - alloc_len = GetEnvironmentVariableW(name_utf16, NULL, 0); - if (alloc_len <= 0) - return NULL; + cl_assert(git__utf8_to_16_alloc(&wide_name, name) >= 0); - cl_assert(value_utf16 = git__calloc(alloc_len, sizeof(wchar_t))); + value_len = GetEnvironmentVariableW(wide_name, NULL, 0); - GetEnvironmentVariableW(name_utf16, value_utf16, alloc_len); - - alloc_len = alloc_len * 4 + 1; /* worst case UTF16->UTF8 growth */ - cl_assert(value_utf8 = git__calloc(alloc_len, 1)); - - git__utf16_to_8(value_utf8, alloc_len, value_utf16); - - git__free(value_utf16); + if (value_len) { + cl_assert(wide_value = git__malloc(value_len * sizeof(wchar_t))); + cl_assert(GetEnvironmentVariableW(wide_name, wide_value, value_len)); + cl_assert(git__utf16_to_8_alloc(&utf8_value, wide_value) >= 0); + git__free(wide_value); + } - return value_utf8; + git__free(wide_name); + return utf8_value; } int cl_setenv(const char *name, const char *value) { - git_win32_path name_utf16; - git_win32_path value_utf16; + wchar_t *wide_name, *wide_value = NULL; - git_win32_path_from_c(name_utf16, name); + cl_assert(git__utf8_to_16_alloc(&wide_name, name) >= 0); if (value) { - git_win32_path_from_c(value_utf16, value); - cl_assert(SetEnvironmentVariableW(name_utf16, value_utf16)); + cl_assert(git__utf8_to_16_alloc(&wide_value, value) >= 0); + cl_assert(SetEnvironmentVariableW(wide_name, wide_value)); } else { /* Windows XP returns 0 (failed) when passing NULL for lpValue when - * lpName does not exist in the environment block. This behavior - * seems to have changed in later versions. Don't check return value - * of SetEnvironmentVariable when passing NULL for lpValue. - */ - SetEnvironmentVariableW(name_utf16, NULL); + * lpName does not exist in the environment block. This behavior + * seems to have changed in later versions. Don't check the return value + * of SetEnvironmentVariable when passing NULL for lpValue. */ + SetEnvironmentVariableW(wide_name, NULL); } + git__free(wide_name); + git__free(wide_value); return 0; } @@ -115,8 +110,8 @@ git_win32_path dest_utf16; unsigned retries = 1; - git_win32_path_from_c(source_utf16, source); - git_win32_path_from_c(dest_utf16, dest); + cl_assert(git_win32_path_from_utf8(source_utf16, source) >= 0); + cl_assert(git_win32_path_from_utf8(dest_utf16, dest) >= 0); while (!MoveFileW(source_utf16, dest_utf16)) { /* Only retry if the error is ERROR_ACCESS_DENIED; @@ -415,11 +410,20 @@ int val = 0; git_config *config; cl_git_pass(git_repository_config(&config, repo)); - cl_git_pass(git_config_get_bool(&val, config, cfg));; + if (git_config_get_bool(&val, config, cfg) < 0) + giterr_clear(); git_config_free(config); return val; } +void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value) +{ + git_config *config; + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_set_string(config, cfg, value)); + git_config_free(config); +} + /* this is essentially the code from git__unescape modified slightly */ static size_t strip_cr_from_buf(char *start, size_t len) { @@ -468,7 +472,8 @@ p_snprintf( buf, sizeof(buf), "file content mismatch at byte %d", (int)(total_bytes + pos)); - clar__fail(file, line, buf, path, 1); + p_close(fd); + clar__fail(file, line, path, buf, 1); } expected_data += bytes; @@ -481,3 +486,72 @@ clar__assert_equal(file, line, "mismatched file length", 1, "%"PRIuZ, (size_t)expected_bytes, (size_t)total_bytes); } + +static char *_cl_restore_home = NULL; + +void cl_fake_home_cleanup(void *payload) +{ + char *restore = _cl_restore_home; + _cl_restore_home = NULL; + + GIT_UNUSED(payload); + + if (restore) { + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, restore)); + git__free(restore); + } +} + +void cl_fake_home(void) +{ + git_buf path = GIT_BUF_INIT; + + cl_git_pass(git_libgit2_opts( + GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &path)); + + _cl_restore_home = git_buf_detach(&path); + cl_set_cleanup(cl_fake_home_cleanup, NULL); + + if (!git_path_exists("home")) + cl_must_pass(p_mkdir("home", 0777)); + cl_git_pass(git_path_prettify(&path, "home", NULL)); + cl_git_pass(git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); + git_buf_free(&path); +} + +void cl_sandbox_set_search_path_defaults(void) +{ + const char *sandbox_path = clar_sandbox_path(); + + git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, sandbox_path); + git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, sandbox_path); + git_libgit2_opts( + GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, sandbox_path); +} + +#ifdef GIT_WIN32 +bool cl_sandbox_supports_8dot3(void) +{ + git_buf longpath = GIT_BUF_INIT; + char *shortname; + bool supported; + + cl_git_pass( + git_buf_joinpath(&longpath, clar_sandbox_path(), "longer_than_8dot3")); + + cl_git_write2file(longpath.ptr, "", 0, O_RDWR|O_CREAT, 0666); + shortname = git_win32_path_8dot3_name(longpath.ptr); + + supported = (shortname != NULL); + + git__free(shortname); + git_buf_free(&longpath); + + return supported; +} +#endif + diff -Nru libgit2-0.20.0/tests/clar_libgit2.h libgit2-0.22.2/tests/clar_libgit2.h --- libgit2-0.20.0/tests/clar_libgit2.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/clar_libgit2.h 2015-03-24 16:10:45.000000000 +0000 @@ -3,6 +3,7 @@ #include "clar.h" #include +#include #include "common.h" /** @@ -11,11 +12,13 @@ * * Use this wrapper around all `git_` library calls that return error codes! */ -#define cl_git_pass(expr) do { \ +#define cl_git_pass(expr) cl_git_pass_((expr), __FILE__, __LINE__) + +#define cl_git_pass_(expr, file, line) do { \ int _lg2_error; \ giterr_clear(); \ if ((_lg2_error = (expr)) != 0) \ - cl_git_report_failure(_lg2_error, __FILE__, __LINE__, "Function call failed: " #expr); \ + cl_git_report_failure(_lg2_error, file, line, "Function call failed: " #expr); \ } while (0) /** @@ -27,6 +30,17 @@ #define cl_git_fail_with(expr, error) cl_assert_equal_i(error,expr) +/** + * Like cl_git_pass, only for Win32 error code conventions + */ +#define cl_win32_pass(expr) do { \ + int _win32_res; \ + if ((_win32_res = (expr)) == 0) { \ + giterr_set(GITERR_OS, "Returned: %d, system error code: %d", _win32_res, GetLastError()); \ + cl_git_report_failure(_win32_res, __FILE__, __LINE__, "System call failed: " #expr); \ + } \ + } while(0) + void cl_git_report_failure(int, const char *, int, const char *); #define cl_assert_at_line(expr,file,line) \ @@ -38,7 +52,7 @@ { if (lo > val || hi < val) { char buf[128]; - snprintf(buf, sizeof(buf), "%d not in [%d,%d]", val, lo, hi); + p_snprintf(buf, sizeof(buf), "%d not in [%d,%d]", val, lo, hi); clar__fail(file, line, err, buf, should_abort); } } @@ -65,6 +79,24 @@ const char *file, int line); +GIT_INLINE(void) clar__assert_equal_oid( + const char *file, int line, const char *desc, + const git_oid *one, const git_oid *two) +{ + if (git_oid_cmp(one, two)) { + char err[] = "\"........................................\" != \"........................................\""; + + git_oid_fmt(&err[1], one); + git_oid_fmt(&err[47], two); + + clar__fail(file, line, desc, err, 1); + } +} + +#define cl_assert_equal_oid(one, two) \ + clar__assert_equal_oid(__FILE__, __LINE__, \ + "OID mismatch: " #one " != " #two, (one), (two)) + /* * Some utility macros for building long strings */ @@ -116,4 +148,20 @@ void cl_repo_set_bool(git_repository *repo, const char *cfg, int value); int cl_repo_get_bool(git_repository *repo, const char *cfg); +void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value); + +/* set up a fake "home" directory and set libgit2 GLOBAL search path. + * + * automatically configures cleanup function to restore the regular search + * path, although you can call it explicitly if you wish (with NULL). + */ +void cl_fake_home(void); +void cl_fake_home_cleanup(void *); + +void cl_sandbox_set_search_path_defaults(void); + +#ifdef GIT_WIN32 +bool cl_sandbox_supports_8dot3(void); +#endif + #endif diff -Nru libgit2-0.20.0/tests/clone/empty.c libgit2-0.22.2/tests/clone/empty.c --- libgit2-0.20.0/tests/clone/empty.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/clone/empty.c 2015-03-24 16:10:45.000000000 +0000 @@ -38,7 +38,7 @@ char *local_name = "refs/heads/master"; const char *expected_tracked_branch_name = "refs/remotes/origin/master"; const char *expected_remote_name = "origin"; - char buffer[1024]; + git_buf buf = GIT_BUF_INIT; git_reference *ref; cl_set_cleanup(&cleanup_repository, "./empty"); @@ -50,16 +50,16 @@ cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned, local_name)); /* ...one can still retrieve the name of the remote tracking reference */ - cl_assert_equal_i((int)strlen(expected_tracked_branch_name) + 1, - git_branch_upstream_name(buffer, 1024, g_repo_cloned, local_name)); + cl_git_pass(git_branch_upstream_name(&buf, g_repo_cloned, local_name)); - cl_assert_equal_s(expected_tracked_branch_name, buffer); + cl_assert_equal_s(expected_tracked_branch_name, buf.ptr); + git_buf_free(&buf); /* ...and the name of the remote... */ - cl_assert_equal_i((int)strlen(expected_remote_name) + 1, - git_branch_remote_name(buffer, 1024, g_repo_cloned, expected_tracked_branch_name)); + cl_git_pass(git_branch_remote_name(&buf, g_repo_cloned, expected_tracked_branch_name)); - cl_assert_equal_s(expected_remote_name, buffer); + cl_assert_equal_s(expected_remote_name, buf.ptr); + git_buf_free(&buf); /* ...even when the remote HEAD is unborn as well */ cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned, diff -Nru libgit2-0.20.0/tests/clone/local.c libgit2-0.22.2/tests/clone/local.c --- libgit2-0.20.0/tests/clone/local.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/clone/local.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,211 @@ +#include "clar_libgit2.h" + +#include "git2/clone.h" +#include "clone.h" +#include "buffer.h" +#include "path.h" +#include "posix.h" +#include "fileops.h" + +static int file_url(git_buf *buf, const char *host, const char *path) +{ + if (path[0] == '/') + path++; + + git_buf_clear(buf); + return git_buf_printf(buf, "file://%s/%s", host, path); +} + +static int git_style_unc_path(git_buf *buf, const char *host, const char *path) +{ + git_buf_clear(buf); + + if (host) + git_buf_printf(buf, "//%s/", host); + + if (path[0] == '/') + path++; + + if (git__isalpha(path[0]) && path[1] == ':' && path[2] == '/') { + git_buf_printf(buf, "%c$/", path[0]); + path += 3; + } + + git_buf_puts(buf, path); + + return git_buf_oom(buf) ? -1 : 0; +} + +static int unc_path(git_buf *buf, const char *host, const char *path) +{ + char *c; + + if (git_style_unc_path(buf, host, path) < 0) + return -1; + + for (c = buf->ptr; *c; c++) + if (*c == '/') + *c = '\\'; + + return 0; +} + +void test_clone_local__should_clone_local(void) +{ + git_buf buf = GIT_BUF_INIT; + + /* we use a fixture path because it needs to exist for us to want to clone */ + const char *path = cl_fixture("testrepo.git"); + + cl_git_pass(file_url(&buf, "", path)); + cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO)); + cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL)); + cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS)); + cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL)); + + cl_git_pass(file_url(&buf, "localhost", path)); + cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO)); + cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL)); + cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS)); + cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL)); + + cl_git_pass(file_url(&buf, "other-host.mycompany.com", path)); + cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO)); + cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL)); + cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS)); + cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL)); + + /* Ensure that file:/// urls are percent decoded: .git == %2e%67%69%74 */ + cl_git_pass(file_url(&buf, "", path)); + git_buf_shorten(&buf, 4); + cl_git_pass(git_buf_puts(&buf, "%2e%67%69%74")); + cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO)); + cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL)); + cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS)); + cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL)); + + cl_assert_equal_i(1, git_clone__should_clone_local(path, GIT_CLONE_LOCAL_AUTO)); + cl_assert_equal_i(1, git_clone__should_clone_local(path, GIT_CLONE_LOCAL)); + cl_assert_equal_i(1, git_clone__should_clone_local(path, GIT_CLONE_LOCAL_NO_LINKS)); + cl_assert_equal_i(0, git_clone__should_clone_local(path, GIT_CLONE_NO_LOCAL)); + + git_buf_free(&buf); +} + +void test_clone_local__hardlinks(void) +{ + git_repository *repo; + git_clone_options opts = GIT_CLONE_OPTIONS_INIT; + git_buf buf = GIT_BUF_INIT; + struct stat st; + + /* + * In this first clone, we just copy over, since the temp dir + * will often be in a different filesystem, so we cannot + * link. It also allows us to control the number of links + */ + opts.bare = true; + opts.local = GIT_CLONE_LOCAL_NO_LINKS; + cl_git_pass(git_clone(&repo, cl_fixture("testrepo.git"), "./clone.git", &opts)); + git_repository_free(repo); + + /* This second clone is in the same filesystem, so we can hardlink */ + + opts.local = GIT_CLONE_LOCAL; + cl_git_pass(git_clone(&repo, cl_git_path_url("clone.git"), "./clone2.git", &opts)); + +#ifndef GIT_WIN32 + git_buf_clear(&buf); + cl_git_pass(git_buf_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125")); + + cl_git_pass(p_stat(buf.ptr, &st)); + cl_assert_equal_i(2, st.st_nlink); +#endif + + git_repository_free(repo); + git_buf_clear(&buf); + + opts.local = GIT_CLONE_LOCAL_NO_LINKS; + cl_git_pass(git_clone(&repo, cl_git_path_url("clone.git"), "./clone3.git", &opts)); + + git_buf_clear(&buf); + cl_git_pass(git_buf_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125")); + + cl_git_pass(p_stat(buf.ptr, &st)); + cl_assert_equal_i(1, st.st_nlink); + + git_repository_free(repo); + + /* this one should automatically use links */ + cl_git_pass(git_clone(&repo, "./clone.git", "./clone4.git", NULL)); + +#ifndef GIT_WIN32 + git_buf_clear(&buf); + cl_git_pass(git_buf_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125")); + + cl_git_pass(p_stat(buf.ptr, &st)); + cl_assert_equal_i(3, st.st_nlink); +#endif + + git_buf_free(&buf); + git_repository_free(repo); + + cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES)); + cl_git_pass(git_futils_rmdir_r("./clone2.git", NULL, GIT_RMDIR_REMOVE_FILES)); + cl_git_pass(git_futils_rmdir_r("./clone3.git", NULL, GIT_RMDIR_REMOVE_FILES)); + cl_git_pass(git_futils_rmdir_r("./clone4.git", NULL, GIT_RMDIR_REMOVE_FILES)); +} + +void test_clone_local__standard_unc_paths_are_written_git_style(void) +{ +#ifdef GIT_WIN32 + git_repository *repo; + git_remote *remote; + git_clone_options opts = GIT_CLONE_OPTIONS_INIT; + git_buf unc = GIT_BUF_INIT, git_unc = GIT_BUF_INIT; + + /* we use a fixture path because it needs to exist for us to want to clone */ + const char *path = cl_fixture("testrepo.git"); + + cl_git_pass(unc_path(&unc, "localhost", path)); + cl_git_pass(git_style_unc_path(&git_unc, "localhost", path)); + + cl_git_pass(git_clone(&repo, unc.ptr, "./clone.git", &opts)); + cl_git_pass(git_remote_lookup(&remote, repo, "origin")); + + cl_assert_equal_s(git_unc.ptr, git_remote_url(remote)); + + git_remote_free(remote); + git_repository_free(repo); + git_buf_free(&unc); + git_buf_free(&git_unc); + + cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES)); +#endif +} + +void test_clone_local__git_style_unc_paths(void) +{ +#ifdef GIT_WIN32 + git_repository *repo; + git_remote *remote; + git_clone_options opts = GIT_CLONE_OPTIONS_INIT; + git_buf git_unc = GIT_BUF_INIT; + + /* we use a fixture path because it needs to exist for us to want to clone */ + const char *path = cl_fixture("testrepo.git"); + + cl_git_pass(git_style_unc_path(&git_unc, "localhost", path)); + + cl_git_pass(git_clone(&repo, git_unc.ptr, "./clone.git", &opts)); + cl_git_pass(git_remote_lookup(&remote, repo, "origin")); + + cl_assert_equal_s(git_unc.ptr, git_remote_url(remote)); + + git_remote_free(remote); + git_repository_free(repo); + git_buf_free(&git_unc); + + cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES)); +#endif +} diff -Nru libgit2-0.20.0/tests/clone/nonetwork.c libgit2-0.22.2/tests/clone/nonetwork.c --- libgit2-0.20.0/tests/clone/nonetwork.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/clone/nonetwork.c 2015-03-24 16:10:45.000000000 +0000 @@ -14,7 +14,7 @@ void test_clone_nonetwork__initialize(void) { - git_checkout_opts dummy_opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options dummy_opts = GIT_CHECKOUT_OPTIONS_INIT; git_remote_callbacks dummy_callbacks = GIT_REMOTE_CALLBACKS_INIT; g_repo = NULL; @@ -22,8 +22,9 @@ memset(&g_options, 0, sizeof(git_clone_options)); g_options.version = GIT_CLONE_OPTIONS_VERSION; g_options.checkout_opts = dummy_opts; - g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; g_options.remote_callbacks = dummy_callbacks; + cl_git_pass(git_signature_now(&g_options.signature, "Me", "foo@example.com")); } void test_clone_nonetwork__cleanup(void) @@ -43,6 +44,7 @@ g_remote = NULL; } + git_signature_free(g_options.signature); cl_fixture_cleanup("./foo"); } @@ -108,19 +110,32 @@ cl_git_fail(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); } +int custom_origin_name_remote_create( + git_remote **out, + git_repository *repo, + const char *name, + const char *url, + void *payload) +{ + GIT_UNUSED(name); + GIT_UNUSED(payload); + + return git_remote_create(out, repo, "my_origin", url); +} + void test_clone_nonetwork__custom_origin_name(void) { - g_options.remote_name = "my_origin"; - cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); + g_options.remote_cb = custom_origin_name_remote_create; + cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); - cl_git_pass(git_remote_load(&g_remote, g_repo, "my_origin")); + cl_git_pass(git_remote_lookup(&g_remote, g_repo, "my_origin")); } void test_clone_nonetwork__defaults(void) { cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", NULL)); cl_assert(g_repo); - cl_git_pass(git_remote_load(&g_remote, g_repo, "origin")); + cl_git_pass(git_remote_lookup(&g_remote, g_repo, "origin")); } void test_clone_nonetwork__cope_with_already_existing_directory(void) @@ -151,6 +166,61 @@ cl_git_pass(git_repository_head(&g_ref, g_repo)); cl_assert_equal_s(git_reference_name(g_ref), "refs/heads/test"); + + cl_assert(git_path_exists("foo/readme.txt")); +} + +static int clone_cancel_fetch_transfer_progress_cb( + const git_transfer_progress *stats, void *data) +{ + GIT_UNUSED(stats); GIT_UNUSED(data); + return -54321; +} + +void test_clone_nonetwork__can_cancel_clone_in_fetch(void) +{ + g_options.checkout_branch = "test"; + + g_options.remote_callbacks.transfer_progress = + clone_cancel_fetch_transfer_progress_cb; + + cl_git_fail_with(git_clone( + &g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options), + -54321); + + cl_assert(!g_repo); + cl_assert(!git_path_exists("foo/readme.txt")); +} + +static int clone_cancel_checkout_cb( + git_checkout_notify_t why, + const char *path, + const git_diff_file *b, + const git_diff_file *t, + const git_diff_file *w, + void *payload) +{ + const char *at_file = payload; + GIT_UNUSED(why); GIT_UNUSED(b); GIT_UNUSED(t); GIT_UNUSED(w); + if (!strcmp(path, at_file)) + return -12345; + return 0; +} + +void test_clone_nonetwork__can_cancel_clone_in_checkout(void) +{ + g_options.checkout_branch = "test"; + + g_options.checkout_opts.notify_flags = GIT_CHECKOUT_NOTIFY_UPDATED; + g_options.checkout_opts.notify_cb = clone_cancel_checkout_cb; + g_options.checkout_opts.notify_payload = "readme.txt"; + + cl_git_fail_with(git_clone( + &g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options), + -12345); + + cl_assert(!g_repo); + cl_assert(!git_path_exists("foo/readme.txt")); } void test_clone_nonetwork__can_detached_head(void) @@ -158,22 +228,87 @@ git_object *obj; git_repository *cloned; git_reference *cloned_head; + git_reflog *log; + const git_reflog_entry *entry; cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); cl_git_pass(git_revparse_single(&obj, g_repo, "master~1")); - cl_git_pass(git_repository_set_head_detached(g_repo, git_object_id(obj))); + cl_git_pass(git_repository_set_head_detached(g_repo, git_object_id(obj), NULL, NULL)); cl_git_pass(git_clone(&cloned, "./foo", "./foo1", &g_options)); cl_assert(git_repository_head_detached(cloned)); cl_git_pass(git_repository_head(&cloned_head, cloned)); - cl_assert(!git_oid_cmp(git_object_id(obj), git_reference_target(cloned_head))); + cl_assert_equal_oid(git_object_id(obj), git_reference_target(cloned_head)); + + cl_git_pass(git_reflog_read(&log, cloned, "HEAD")); + entry = git_reflog_entry_byindex(log, 0); + cl_assert_equal_s("foo@example.com", git_reflog_entry_committer(entry)->email); git_object_free(obj); git_reference_free(cloned_head); + git_reflog_free(log); git_repository_free(cloned); cl_fixture_cleanup("./foo1"); } + +static void assert_correct_reflog(const char *name) +{ + git_reflog *log; + const git_reflog_entry *entry; + char expected_log_message[128] = {0}; + + sprintf(expected_log_message, "clone: from %s", cl_git_fixture_url("testrepo.git")); + + cl_git_pass(git_reflog_read(&log, g_repo, name)); + cl_assert_equal_i(1, git_reflog_entrycount(log)); + entry = git_reflog_entry_byindex(log, 0); + cl_assert_equal_s(expected_log_message, git_reflog_entry_message(entry)); + cl_assert_equal_s("foo@example.com", git_reflog_entry_committer(entry)->email); + + git_reflog_free(log); +} + +void test_clone_nonetwork__clone_updates_reflog_properly(void) +{ + cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options)); + assert_correct_reflog("HEAD"); + assert_correct_reflog("refs/heads/master"); +} + +static void cleanup_repository(void *path) +{ + if (g_repo) { + git_repository_free(g_repo); + g_repo = NULL; + } + + cl_fixture_cleanup((const char *)path); +} + +void test_clone_nonetwork__clone_from_empty_sets_upstream(void) +{ + git_config *config; + git_repository *repo; + const char *str; + + /* Create an empty repo to clone from */ + cl_set_cleanup(&cleanup_repository, "./test1"); + cl_git_pass(git_repository_init(&g_repo, "./test1", 0)); + cl_set_cleanup(&cleanup_repository, "./repowithunborn"); + cl_git_pass(git_clone(&repo, "./test1", "./repowithunborn", NULL)); + + cl_git_pass(git_repository_config(&config, repo)); + + cl_git_pass(git_config_get_string(&str, config, "branch.master.remote")); + cl_assert_equal_s("origin", str); + cl_git_pass(git_config_get_string(&str, config, "branch.master.merge")); + cl_assert_equal_s("refs/heads/master", str); + + git_config_free(config); + git_repository_free(repo); + cl_fixture_cleanup("./repowithunborn"); +} diff -Nru libgit2-0.20.0/tests/clone/transport.c libgit2-0.22.2/tests/clone/transport.c --- libgit2-0.20.0/tests/clone/transport.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/clone/transport.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,51 @@ +#include "clar_libgit2.h" + +#include "git2/clone.h" +#include "git2/transport.h" +#include "git2/sys/transport.h" +#include "fileops.h" + +static int custom_transport( + git_transport **out, + git_remote *owner, + void *payload) +{ + *((int*)payload) = 1; + + return git_transport_local(out, owner, payload); +} + +static int custom_transport_remote_create( + git_remote **out, + git_repository *repo, + const char *name, + const char *url, + void *payload) +{ + int error; + + if ((error = git_remote_create(out, repo, name, url)) < 0) + return error; + + if ((error = git_remote_set_transport(*out, custom_transport, payload)) < 0) + return error; + + return 0; +} + +void test_clone_transport__custom_transport(void) +{ + git_repository *repo; + git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT; + int custom_transport_used = 0; + + clone_opts.remote_cb = custom_transport_remote_create; + clone_opts.remote_cb_payload = &custom_transport_used; + + cl_git_pass(git_clone(&repo, cl_fixture("testrepo.git"), "./custom_transport.git", &clone_opts)); + git_repository_free(repo); + + cl_git_pass(git_futils_rmdir_r("./custom_transport.git", NULL, GIT_RMDIR_REMOVE_FILES)); + + cl_assert(custom_transport_used == 1); +} diff -Nru libgit2-0.20.0/tests/commit/commit.c libgit2-0.22.2/tests/commit/commit.c --- libgit2-0.20.0/tests/commit/commit.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/commit/commit.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,4 +1,6 @@ #include "clar_libgit2.h" +#include "commit.h" +#include "git2/commit.h" static git_repository *_repo; @@ -36,11 +38,46 @@ cl_git_pass(git_commit_create(&oid, _repo, "refs/heads/foo/bar", s, s, NULL, "some msg", tree, 1, (const git_commit **) &commit)); + /* fail because the parent isn't the tip of the branch anymore */ + cl_git_fail(git_commit_create(&oid, _repo, "refs/heads/foo/bar", s, s, + NULL, "some msg", tree, 1, (const git_commit **) &commit)); + cl_git_pass(git_reference_lookup(&ref, _repo, "refs/heads/foo/bar")); - cl_assert(!git_oid_cmp(&oid, git_reference_target(ref))); + cl_assert_equal_oid(&oid, git_reference_target(ref)); git_tree_free(tree); git_commit_free(commit); git_signature_free(s); git_reference_free(ref); } + +void assert_commit_summary(const char *expected, const char *given) +{ + git_commit *dummy; + + cl_assert(dummy = git__calloc(1, sizeof(struct git_commit))); + + dummy->raw_message = git__strdup(given); + cl_assert_equal_s(expected, git_commit_summary(dummy)); + + git_commit__free(dummy); +} + +void test_commit_commit__summary(void) +{ + assert_commit_summary("One-liner with no trailing newline", "One-liner with no trailing newline"); + assert_commit_summary("One-liner with trailing newline", "One-liner with trailing newline\n"); + assert_commit_summary("Trimmed leading&trailing newlines", "\n\nTrimmed leading&trailing newlines\n\n"); + assert_commit_summary("First paragraph only", "\nFirst paragraph only\n\n(There are more!)"); + assert_commit_summary("First paragraph with unwrapped trailing\tlines", "\nFirst paragraph\nwith unwrapped\ntrailing\tlines\n\n(Yes, unwrapped!)"); + assert_commit_summary("\tLeading \ttabs", "\tLeading\n\ttabs\n\nis preserved"); + assert_commit_summary(" Leading Spaces", " Leading\n Spaces\n\nare preserved"); + assert_commit_summary("Trailing tabs\tare removed", "Trailing tabs\tare removed\t\t"); + assert_commit_summary("Trailing spaces are removed", "Trailing spaces are removed "); + assert_commit_summary("Trailing tabs", "Trailing tabs\t\n\nare removed"); + assert_commit_summary("Trailing spaces", "Trailing spaces \n\nare removed"); + assert_commit_summary("", ""); + assert_commit_summary("", " "); + assert_commit_summary("", "\n"); + assert_commit_summary("", "\n \n"); +} diff -Nru libgit2-0.20.0/tests/commit/signature.c libgit2-0.22.2/tests/commit/signature.c --- libgit2-0.20.0/tests/commit/signature.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/commit/signature.c 2015-03-24 16:10:45.000000000 +0000 @@ -56,8 +56,8 @@ cl_git_fail(try_build_signature("", "emeric.fermas@gmail.com", 1234567890, 60)); cl_git_fail(try_build_signature(" ", "emeric.fermas@gmail.com", 1234567890, 60)); - cl_git_pass(try_build_signature("nulltoken", "", 1234567890, 60)); - cl_git_pass(try_build_signature("nulltoken", " ", 1234567890, 60)); + cl_git_fail(try_build_signature("nulltoken", "", 1234567890, 60)); + cl_git_fail(try_build_signature("nulltoken", " ", 1234567890, 60)); } void test_commit_signature__create_one_char(void) diff -Nru libgit2-0.20.0/tests/commit/write.c libgit2-0.22.2/tests/commit/write.c --- libgit2-0.20.0/tests/commit/write.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/commit/write.c 2015-03-24 16:10:45.000000000 +0000 @@ -7,6 +7,8 @@ static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd"; static const char *root_commit_message = "This is a root commit\n\ This is a root commit and should be the only one in this branch\n"; +static const char *root_reflog_message = "commit (initial): This is a root commit \ + This is a root commit and should be the only one in this branch"; static char *head_old; static git_reference *head, *branch; static git_commit *commit; @@ -101,6 +103,8 @@ git_signature *author, *committer; const char *branch_name = "refs/heads/root-commit-branch"; git_tree *tree; + git_reflog *log; + const git_reflog_entry *entry; git_oid_fromstr(&tree_id, tree_oid); cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); @@ -116,7 +120,7 @@ cl_assert(head_old != NULL); git_reference_free(head); - cl_git_pass(git_reference_symbolic_create(&head, g_repo, "HEAD", branch_name, 1)); + cl_git_pass(git_reference_symbolic_create(&head, g_repo, "HEAD", branch_name, 1, NULL, NULL)); cl_git_pass(git_commit_create_v( &commit_id, /* out id */ @@ -130,7 +134,6 @@ 0)); git_object_free((git_object *)tree); - git_signature_free(committer); git_signature_free(author); /* @@ -142,6 +145,16 @@ cl_assert(git_commit_parentcount(commit) == 0); cl_git_pass(git_reference_lookup(&branch, g_repo, branch_name)); branch_oid = git_reference_target(branch); - cl_git_pass(git_oid_cmp(branch_oid, &commit_id)); + cl_assert_equal_oid(branch_oid, &commit_id); cl_assert_equal_s(root_commit_message, git_commit_message(commit)); + + cl_git_pass(git_reflog_read(&log, g_repo, branch_name)); + cl_assert_equal_i(1, git_reflog_entrycount(log)); + entry = git_reflog_entry_byindex(log, 0); + cl_assert_equal_s(committer->email, git_reflog_entry_committer(entry)->email); + cl_assert_equal_s(committer->name, git_reflog_entry_committer(entry)->name); + cl_assert_equal_s(root_reflog_message, git_reflog_entry_message(entry)); + + git_signature_free(committer); + git_reflog_free(log); } diff -Nru libgit2-0.20.0/tests/config/config_helpers.c libgit2-0.22.2/tests/config/config_helpers.c --- libgit2-0.20.0/tests/config/config_helpers.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/config/config_helpers.c 2015-03-24 16:10:45.000000000 +0000 @@ -35,3 +35,31 @@ cl_assert_equal_s(expected_value, out); } + +static int count_config_entries_cb( + const git_config_entry *entry, + void *payload) +{ + int *how_many = (int *)payload; + + GIT_UNUSED(entry); + + (*how_many)++; + + return 0; +} + +int count_config_entries_match(git_repository *repo, const char *pattern) +{ + git_config *config; + int how_many = 0; + + cl_git_pass(git_repository_config(&config, repo)); + + cl_assert_equal_i(0, git_config_foreach_match( + config, pattern, count_config_entries_cb, &how_many)); + + git_config_free(config); + + return how_many; +} diff -Nru libgit2-0.20.0/tests/config/config_helpers.h libgit2-0.22.2/tests/config/config_helpers.h --- libgit2-0.20.0/tests/config/config_helpers.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/config/config_helpers.h 2015-03-24 16:10:45.000000000 +0000 @@ -7,3 +7,7 @@ git_repository *repo, const char *name, const char *expected_value); + +extern int count_config_entries_match( + git_repository *repo, + const char *pattern); diff -Nru libgit2-0.20.0/tests/config/global.c libgit2-0.22.2/tests/config/global.c --- libgit2-0.20.0/tests/config/global.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/config/global.c 2015-03-24 16:10:45.000000000 +0000 @@ -6,18 +6,17 @@ { git_buf path = GIT_BUF_INIT; - cl_assert_equal_i(0, p_mkdir("home", 0777)); + cl_git_pass(git_futils_mkdir_r("home", NULL, 0777)); cl_git_pass(git_path_prettify(&path, "home", NULL)); cl_git_pass(git_libgit2_opts( GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); - cl_assert_equal_i(0, p_mkdir("xdg", 0777)); - cl_assert_equal_i(0, p_mkdir("xdg/git", 0777)); + cl_git_pass(git_futils_mkdir_r("xdg/git", NULL, 0777)); cl_git_pass(git_path_prettify(&path, "xdg/git", NULL)); cl_git_pass(git_libgit2_opts( GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr)); - cl_assert_equal_i(0, p_mkdir("etc", 0777)); + cl_git_pass(git_futils_mkdir_r("etc", NULL, 0777)); cl_git_pass(git_path_prettify(&path, "etc", NULL)); cl_git_pass(git_libgit2_opts( GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr)); @@ -27,13 +26,7 @@ void test_config_global__cleanup(void) { - cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES)); - cl_git_pass(git_futils_rmdir_r("xdg", NULL, GIT_RMDIR_REMOVE_FILES)); - cl_git_pass(git_futils_rmdir_r("etc", NULL, GIT_RMDIR_REMOVE_FILES)); - - git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, NULL); - git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, NULL); - git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL); + cl_sandbox_set_search_path_defaults(); } void test_config_global__open_global(void) diff -Nru libgit2-0.20.0/tests/config/include.c libgit2-0.22.2/tests/config/include.c --- libgit2-0.20.0/tests/config/include.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/config/include.c 2015-03-24 16:10:45.000000000 +0000 @@ -47,29 +47,8 @@ cl_assert_equal_s(str, "huzzah"); git_config_free(cfg); -} - -void test_config_include__refresh(void) -{ - git_config *cfg; - const char *str; - - cl_fixture_sandbox("config"); - - cl_git_pass(git_config_open_ondisk(&cfg, "config/config-include")); - - cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz")); - cl_assert_equal_s(str, "huzzah"); - - /* Change the included file and see if we refresh */ - cl_git_mkfile("config/config-included", "[foo \"bar\"]\nbaz = hurrah"); - cl_git_pass(git_config_refresh(cfg)); - - cl_git_pass(git_config_get_string(&str, cfg, "foo.bar.baz")); - cl_assert_equal_s(str, "hurrah"); - git_config_free(cfg); - cl_fixture_cleanup("config"); + cl_sandbox_set_search_path_defaults(); } /* We need to pretend that the variables were defined where the file was included */ @@ -104,6 +83,22 @@ cl_git_fail(git_config_open_ondisk(&cfg, "a")); - unlink("a"); - unlink("b"); + p_unlink("a"); + p_unlink("b"); +} + +void test_config_include__missing(void) +{ + git_config *cfg; + const char *str; + + cl_git_mkfile("including", "[include]\npath = nonexistentfile\n[foo]\nbar = baz"); + + giterr_clear(); + cl_git_pass(git_config_open_ondisk(&cfg, "including")); + cl_assert(giterr_last() == NULL); + cl_git_pass(git_config_get_string(&str, cfg, "foo.bar")); + cl_assert_equal_s(str, "baz"); + + git_config_free(cfg); } diff -Nru libgit2-0.20.0/tests/config/multivar.c libgit2-0.22.2/tests/config/multivar.c --- libgit2-0.20.0/tests/config/multivar.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/config/multivar.c 2015-03-24 16:10:45.000000000 +0000 @@ -231,13 +231,13 @@ n = 0; cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n)); - cl_assert(n == 2); + cl_assert_equal_i(2, n); cl_git_pass(git_config_delete_multivar(cfg, _name, "github")); n = 0; cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n)); - cl_assert(n == 1); + cl_assert_equal_i(1, n); git_config_free(cfg); @@ -245,7 +245,7 @@ n = 0; cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n)); - cl_assert(n == 1); + cl_assert_equal_i(1, n); git_config_free(cfg); } diff -Nru libgit2-0.20.0/tests/config/read.c libgit2-0.22.2/tests/config/read.c --- libgit2-0.20.0/tests/config/read.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/config/read.c 2015-03-24 16:10:45.000000000 +0000 @@ -247,7 +247,7 @@ count = 3; cl_git_fail(ret = git_config_foreach(cfg, cfg_callback_countdown, &count)); - cl_assert_equal_i(GIT_EUSER, ret); + cl_assert_equal_i(-100, ret); git_config_free(cfg); } diff -Nru libgit2-0.20.0/tests/config/refresh.c libgit2-0.22.2/tests/config/refresh.c --- libgit2-0.20.0/tests/config/refresh.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/config/refresh.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ -#include "clar_libgit2.h" - -#define TEST_FILE "config.refresh" - -void test_config_refresh__initialize(void) -{ -} - -void test_config_refresh__cleanup(void) -{ - cl_fixture_cleanup(TEST_FILE); -} - -void test_config_refresh__update_value(void) -{ - git_config *cfg; - int32_t v; - - cl_git_mkfile(TEST_FILE, "[section]\n\tvalue = 1\n\n"); - - /* By freeing the config, we make sure we flush the values */ - cl_git_pass(git_config_open_ondisk(&cfg, TEST_FILE)); - - cl_git_pass(git_config_get_int32(&v, cfg, "section.value")); - cl_assert_equal_i(1, v); - - cl_git_rewritefile(TEST_FILE, "[section]\n\tvalue = 10\n\n"); - - cl_git_pass(git_config_get_int32(&v, cfg, "section.value")); - cl_assert_equal_i(1, v); - - cl_git_pass(git_config_refresh(cfg)); - - cl_git_pass(git_config_get_int32(&v, cfg, "section.value")); - cl_assert_equal_i(10, v); - - git_config_free(cfg); -} - -void test_config_refresh__delete_value(void) -{ - git_config *cfg; - int32_t v; - - cl_git_mkfile(TEST_FILE, "[section]\n\tvalue = 1\n\n"); - - /* By freeing the config, we make sure we flush the values */ - cl_git_pass(git_config_open_ondisk(&cfg, TEST_FILE)); - - cl_git_pass(git_config_get_int32(&v, cfg, "section.value")); - cl_assert_equal_i(1, v); - cl_git_fail(git_config_get_int32(&v, cfg, "section.newval")); - - cl_git_rewritefile(TEST_FILE, "[section]\n\tnewval = 10\n\n"); - - cl_git_pass(git_config_get_int32(&v, cfg, "section.value")); - cl_assert_equal_i(1, v); - cl_git_fail(git_config_get_int32(&v, cfg, "section.newval")); - - cl_git_pass(git_config_refresh(cfg)); - - cl_git_fail(git_config_get_int32(&v, cfg, "section.value")); - cl_git_pass(git_config_get_int32(&v, cfg, "section.newval")); - cl_assert_equal_i(10, v); - - git_config_free(cfg); -} diff -Nru libgit2-0.20.0/tests/config/rename.c libgit2-0.22.2/tests/config/rename.c --- libgit2-0.20.0/tests/config/rename.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/config/rename.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,85 @@ +#include "clar_libgit2.h" +#include "config.h" + +static git_repository *g_repo = NULL; +static git_config *g_config = NULL; + +void test_config_rename__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_repository_config(&g_config, g_repo)); +} + +void test_config_rename__cleanup(void) +{ + git_config_free(g_config); + g_config = NULL; + + cl_git_sandbox_cleanup(); + g_repo = NULL; +} + +void test_config_rename__can_rename(void) +{ + const git_config_entry *ce; + + cl_git_pass(git_config_get_entry( + &ce, g_config, "branch.track-local.remote")); + cl_assert_equal_s(".", ce->value); + + cl_git_fail(git_config_get_entry( + &ce, g_config, "branch.local-track.remote")); + + cl_git_pass(git_config_rename_section( + g_repo, "branch.track-local", "branch.local-track")); + + cl_git_pass(git_config_get_entry( + &ce, g_config, "branch.local-track.remote")); + cl_assert_equal_s(".", ce->value); + + cl_git_fail(git_config_get_entry( + &ce, g_config, "branch.track-local.remote")); +} + +void test_config_rename__prevent_overwrite(void) +{ + const git_config_entry *ce; + + cl_git_pass(git_config_set_string( + g_config, "branch.local-track.remote", "yellow")); + + cl_git_pass(git_config_get_entry( + &ce, g_config, "branch.local-track.remote")); + cl_assert_equal_s("yellow", ce->value); + + cl_git_pass(git_config_rename_section( + g_repo, "branch.track-local", "branch.local-track")); + + cl_git_pass(git_config_get_entry( + &ce, g_config, "branch.local-track.remote")); + cl_assert_equal_s(".", ce->value); + + /* so, we don't currently prevent overwrite... */ + /* { + const git_error *err; + cl_assert((err = giterr_last()) != NULL); + cl_assert(err->message != NULL); + } */ +} + +static void assert_invalid_config_section_name( + git_repository *repo, const char *name) +{ + cl_git_fail_with( + git_config_rename_section(repo, "branch.remoteless", name), + GIT_EINVALIDSPEC); +} + +void test_config_rename__require_a_valid_new_name(void) +{ + assert_invalid_config_section_name(g_repo, ""); + assert_invalid_config_section_name(g_repo, "bra\nch"); + assert_invalid_config_section_name(g_repo, "branc#"); + assert_invalid_config_section_name(g_repo, "bra\nch.duh"); + assert_invalid_config_section_name(g_repo, "branc#.duh"); +} diff -Nru libgit2-0.20.0/tests/config/snapshot.c libgit2-0.22.2/tests/config/snapshot.c --- libgit2-0.20.0/tests/config/snapshot.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/config/snapshot.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,78 @@ +#include "clar_libgit2.h" + +void test_config_snapshot__create_snapshot(void) +{ + int32_t tmp; + git_config *cfg, *snapshot, *new_snapshot; + const char *filename = "config-ext-change"; + + cl_git_mkfile(filename, "[old]\nvalue = 5\n"); + + cl_git_pass(git_config_open_ondisk(&cfg, filename)); + + cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value")); + cl_assert_equal_i(5, tmp); + + cl_git_pass(git_config_snapshot(&snapshot, cfg)); + + /* Change the value on the file itself (simulate external process) */ + cl_git_mkfile(filename, "[old]\nvalue = 56\n"); + + cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value")); + cl_assert_equal_i(56, tmp); + + cl_git_pass(git_config_get_int32(&tmp, snapshot, "old.value")); + cl_assert_equal_i(5, tmp); + + /* Change the value on the file itself (simulate external process) */ + cl_git_mkfile(filename, "[old]\nvalue = 999\n"); + + cl_git_pass(git_config_snapshot(&new_snapshot, cfg)); + + /* New snapshot should see new value */ + cl_git_pass(git_config_get_int32(&tmp, new_snapshot, "old.value")); + cl_assert_equal_i(999, tmp); + + /* Old snapshot should still have the old value */ + cl_git_pass(git_config_get_int32(&tmp, snapshot, "old.value")); + cl_assert_equal_i(5, tmp); + + git_config_free(new_snapshot); + git_config_free(snapshot); + git_config_free(cfg); +} + +static int count_me(const git_config_entry *entry, void *payload) +{ + int *n = (int *) payload; + + GIT_UNUSED(entry); + + (*n)++; + + return 0; +} + +void test_config_snapshot__multivar(void) +{ + int count = 0; + git_config *cfg, *snapshot; + const char *filename = "config-file"; + + cl_git_mkfile(filename, "[old]\nvalue = 5\nvalue = 6\n"); + + cl_git_pass(git_config_open_ondisk(&cfg, filename)); + cl_git_pass(git_config_get_multivar_foreach(cfg, "old.value", NULL, count_me, &count)); + + cl_assert_equal_i(2, count); + + cl_git_pass(git_config_snapshot(&snapshot, cfg)); + git_config_free(cfg); + + count = 0; + cl_git_pass(git_config_get_multivar_foreach(snapshot, "old.value", NULL, count_me, &count)); + + cl_assert_equal_i(2, count); + + git_config_free(snapshot); +} diff -Nru libgit2-0.20.0/tests/config/stress.c libgit2-0.22.2/tests/config/stress.c --- libgit2-0.20.0/tests/config/stress.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/config/stress.c 2015-03-24 16:10:45.000000000 +0000 @@ -44,12 +44,24 @@ cl_git_pass(git_config_open_ondisk(&config, cl_fixture("config/config12"))); + cl_git_pass(git_config_get_string(&str, config, "some.section.test2")); + cl_assert_equal_s("hello", str); + + cl_git_pass(git_config_get_string(&str, config, "some.section.test3")); + cl_assert_equal_s("welcome", str); + cl_git_pass(git_config_get_string(&str, config, "some.section.other")); cl_assert_equal_s("hello! \" ; ; ; ", str); + cl_git_pass(git_config_get_string(&str, config, "some.section.other2")); + cl_assert_equal_s("cool! \" # # # ", str); + cl_git_pass(git_config_get_string(&str, config, "some.section.multi")); cl_assert_equal_s("hi, this is a ; multiline comment # with ;\n special chars and other stuff !@#", str); + cl_git_pass(git_config_get_string(&str, config, "some.section.multi2")); + cl_assert_equal_s("good, this is a ; multiline comment # with ;\n special chars and other stuff !@#", str); + cl_git_pass(git_config_get_string(&str, config, "some.section.back")); cl_assert_equal_s("this is \ba phrase", str); @@ -90,3 +102,16 @@ cl_assert_equal_s(path, str); git_config_free(config); } + +void test_config_stress__complex(void) +{ + git_config *config; + const char *str; + const char *path = "./config-immediate-multiline"; + + cl_git_mkfile(path, "[imm]\n multi = \"\\\nfoo\""); + cl_git_pass(git_config_open_ondisk(&config, path)); + cl_git_pass(git_config_get_string(&str, config, "imm.multi")); + cl_assert_equal_s(str, "foo"); + git_config_free(config); +} diff -Nru libgit2-0.20.0/tests/config/validkeyname.c libgit2-0.22.2/tests/config/validkeyname.c --- libgit2-0.20.0/tests/config/validkeyname.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/config/validkeyname.c 2015-03-24 16:10:45.000000000 +0000 @@ -46,23 +46,3 @@ assert_invalid_config_key_name("dif.dir\nstat.lines"); assert_invalid_config_key_name("dif.dirstat.li\nes"); } - -static void assert_invalid_config_section_name(git_repository *repo, const char *name) -{ - cl_git_fail_with(git_config_rename_section(repo, "branch.remoteless", name), GIT_EINVALIDSPEC); -} - -void test_config_validkeyname__renaming_a_section_requires_a_valid_name(void) -{ - git_repository *repo; - - cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); - - assert_invalid_config_section_name(repo, ""); - assert_invalid_config_section_name(repo, "bra\nch"); - assert_invalid_config_section_name(repo, "branc#"); - assert_invalid_config_section_name(repo, "bra\nch.duh"); - assert_invalid_config_section_name(repo, "branc#.duh"); - - git_repository_free(repo); -} diff -Nru libgit2-0.20.0/tests/config/write.c libgit2-0.22.2/tests/config/write.c --- libgit2-0.20.0/tests/config/write.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/config/write.c 2015-03-24 16:10:45.000000000 +0000 @@ -229,6 +229,22 @@ git_config_free(cfg); } +void test_config_write__add_section_at_file_with_no_clrf_at_the_end(void) +{ + git_config *cfg; + int i; + + cl_git_pass(git_config_open_ondisk(&cfg, "config17")); + cl_git_pass(git_config_set_int32(cfg, "diff.context", 10)); + git_config_free(cfg); + + cl_git_pass(git_config_open_ondisk(&cfg, "config17")); + cl_git_pass(git_config_get_int32(&i, cfg, "diff.context")); + cl_assert_equal_i(10, i); + + git_config_free(cfg); +} + void test_config_write__add_value_which_needs_quotes(void) { git_config *cfg; @@ -303,3 +319,26 @@ git_config_free(cfg); } + +void test_config_write__outside_change(void) +{ + int32_t tmp; + git_config *cfg; + const char *filename = "config-ext-change"; + + cl_git_mkfile(filename, "[old]\nvalue = 5\n"); + + cl_git_pass(git_config_open_ondisk(&cfg, filename)); + + cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value")); + + /* Change the value on the file itself (simulate external process) */ + cl_git_mkfile(filename, "[old]\nvalue = 6\n"); + + cl_git_pass(git_config_set_int32(cfg, "new.value", 7)); + + cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value")); + cl_assert_equal_i(6, tmp); + + git_config_free(cfg); +} diff -Nru libgit2-0.20.0/tests/core/buffer.c libgit2-0.22.2/tests/core/buffer.c --- libgit2-0.20.0/tests/core/buffer.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/core/buffer.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,7 +1,7 @@ #include "clar_libgit2.h" #include "buffer.h" #include "buf_text.h" -#include "hashsig.h" +#include "git2/sys/hashsig.h" #include "fileops.h" #define TESTSTR "Have you seen that? Have you seeeen that??" @@ -281,11 +281,10 @@ /* more variations on append tests */ void test_core_buffer__5(void) { - check_buf_append("", "", "", 0, 8); - check_buf_append("a", "", "a", 1, 8); + check_buf_append("", "", "", 0, 0); + check_buf_append("a", "", "a", 1, 0); check_buf_append("", "a", "a", 1, 8); check_buf_append("", "a", "a", 1, 8); - check_buf_append("a", "", "a", 1, 8); check_buf_append("a", "b", "ab", 2, 8); check_buf_append("", "abcdefgh", "abcdefgh", 8, 16); check_buf_append("abcdefgh", "", "abcdefgh", 8, 16); @@ -406,6 +405,23 @@ } static void +check_joinbuf_overlapped( + const char *oldval, + int ofs_a, + const char *b, + const char *expected) +{ + char sep = '/'; + git_buf buf = GIT_BUF_INIT; + + git_buf_sets(&buf, oldval); + git_buf_join(&buf, sep, buf.ptr + ofs_a, b); + cl_assert(git_buf_oom(&buf) == 0); + cl_assert_equal_s(expected, git_buf_cstr(&buf)); + git_buf_free(&buf); +} + +static void check_joinbuf_n_2( const char *a, const char *b, @@ -480,6 +496,20 @@ check_joinbuf_2("/abcd/", "defg/", "/abcd/defg/"); check_joinbuf_2("/abcd/", "/defg/", "/abcd/defg/"); + check_joinbuf_overlapped("abcd", 0, "efg", "abcd/efg"); + check_joinbuf_overlapped("abcd", 1, "efg", "bcd/efg"); + check_joinbuf_overlapped("abcd", 2, "efg", "cd/efg"); + check_joinbuf_overlapped("abcd", 3, "efg", "d/efg"); + check_joinbuf_overlapped("abcd", 4, "efg", "efg"); + check_joinbuf_overlapped("abc/", 2, "efg", "c/efg"); + check_joinbuf_overlapped("abc/", 3, "efg", "/efg"); + check_joinbuf_overlapped("abc/", 4, "efg", "efg"); + check_joinbuf_overlapped("abcd", 3, "", "d/"); + check_joinbuf_overlapped("abcd", 4, "", ""); + check_joinbuf_overlapped("abc/", 2, "", "c/"); + check_joinbuf_overlapped("abc/", 3, "", "/"); + check_joinbuf_overlapped("abc/", 4, "", ""); + check_joinbuf_n_2("", "", ""); check_joinbuf_n_2("", "a", "a"); check_joinbuf_n_2("", "/a", "/a"); @@ -568,6 +598,38 @@ git_buf_free(&a); } +void test_core_buffer__join3(void) +{ + git_buf a = GIT_BUF_INIT; + + cl_git_pass(git_buf_join3(&a, '/', "test", "string", "join")); + cl_assert_equal_s("test/string/join", a.ptr); + cl_git_pass(git_buf_join3(&a, '/', "test/", "string", "join")); + cl_assert_equal_s("test/string/join", a.ptr); + cl_git_pass(git_buf_join3(&a, '/', "test/", "/string", "join")); + cl_assert_equal_s("test/string/join", a.ptr); + cl_git_pass(git_buf_join3(&a, '/', "test/", "/string/", "join")); + cl_assert_equal_s("test/string/join", a.ptr); + cl_git_pass(git_buf_join3(&a, '/', "test/", "/string/", "/join")); + cl_assert_equal_s("test/string/join", a.ptr); + + cl_git_pass(git_buf_join3(&a, '/', "", "string", "join")); + cl_assert_equal_s("string/join", a.ptr); + cl_git_pass(git_buf_join3(&a, '/', "", "string/", "join")); + cl_assert_equal_s("string/join", a.ptr); + cl_git_pass(git_buf_join3(&a, '/', "", "string/", "/join")); + cl_assert_equal_s("string/join", a.ptr); + + cl_git_pass(git_buf_join3(&a, '/', "string", "", "join")); + cl_assert_equal_s("string/join", a.ptr); + cl_git_pass(git_buf_join3(&a, '/', "string/", "", "join")); + cl_assert_equal_s("string/join", a.ptr); + cl_git_pass(git_buf_join3(&a, '/', "string/", "", "/join")); + cl_assert_equal_s("string/join", a.ptr); + + git_buf_free(&a); +} + void test_core_buffer__11(void) { git_buf a = GIT_BUF_INIT; @@ -685,7 +747,7 @@ assert_unescape("", ""); } -void test_core_buffer__base64(void) +void test_core_buffer__encode_base64(void) { git_buf buf = GIT_BUF_INIT; @@ -696,20 +758,61 @@ * 0x 1d 06 21 29 1c 30 * d G h p c w */ - cl_git_pass(git_buf_put_base64(&buf, "this", 4)); + cl_git_pass(git_buf_encode_base64(&buf, "this", 4)); cl_assert_equal_s("dGhpcw==", buf.ptr); git_buf_clear(&buf); - cl_git_pass(git_buf_put_base64(&buf, "this!", 5)); + cl_git_pass(git_buf_encode_base64(&buf, "this!", 5)); cl_assert_equal_s("dGhpcyE=", buf.ptr); git_buf_clear(&buf); - cl_git_pass(git_buf_put_base64(&buf, "this!\n", 6)); + cl_git_pass(git_buf_encode_base64(&buf, "this!\n", 6)); cl_assert_equal_s("dGhpcyEK", buf.ptr); git_buf_free(&buf); } +void test_core_buffer__decode_base64(void) +{ + git_buf buf = GIT_BUF_INIT; + + cl_git_pass(git_buf_decode_base64(&buf, "dGhpcw==", 8)); + cl_assert_equal_s("this", buf.ptr); + + git_buf_clear(&buf); + cl_git_pass(git_buf_decode_base64(&buf, "dGhpcyE=", 8)); + cl_assert_equal_s("this!", buf.ptr); + + git_buf_clear(&buf); + cl_git_pass(git_buf_decode_base64(&buf, "dGhpcyEK", 8)); + cl_assert_equal_s("this!\n", buf.ptr); + + cl_git_fail(git_buf_decode_base64(&buf, "This is not a valid base64 string!!!", 36)); + cl_assert_equal_s("this!\n", buf.ptr); + + git_buf_free(&buf); +} + +void test_core_buffer__encode_base85(void) +{ + git_buf buf = GIT_BUF_INIT; + + cl_git_pass(git_buf_encode_base85(&buf, "this", 4)); + cl_assert_equal_s("bZBXF", buf.ptr); + git_buf_clear(&buf); + + cl_git_pass(git_buf_encode_base85(&buf, "two rnds", 8)); + cl_assert_equal_s("ba!tca&BaE", buf.ptr); + git_buf_clear(&buf); + + cl_git_pass(git_buf_encode_base85(&buf, "this is base 85 encoded", + strlen("this is base 85 encoded"))); + cl_assert_equal_s("bZBXFAZc?TVqtS-AUHK3Wo~0{WMyOk", buf.ptr); + git_buf_clear(&buf); + + git_buf_free(&buf); +} + void test_core_buffer__classify_with_utf8(void) { char *data0 = "Simple text\n"; @@ -727,7 +830,7 @@ cl_assert(!git_buf_text_contains_nul(&b)); b.ptr = data1; b.size = b.asize = data1len; - cl_assert(git_buf_text_is_binary(&b)); + cl_assert(!git_buf_text_is_binary(&b)); cl_assert(!git_buf_text_contains_nul(&b)); b.ptr = data2; b.size = b.asize = data2len; @@ -951,18 +1054,14 @@ git_buf_sets(&src, "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n"); - cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src)); - check_buf("crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n", tgt); - check_buf(src.ptr, tgt); + cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src)); cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src)); check_buf("crlf\ncrlf\ncrlf\ncrlf\n", tgt); git_buf_sets(&src, "\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf"); - cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src)); - check_buf("\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf", tgt); - check_buf(src.ptr, tgt); + cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src)); cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src)); check_buf("\ncrlf\ncrlf\ncrlf\ncrlf\ncrlf", tgt); @@ -971,8 +1070,7 @@ git_buf_sets(&src, "\nlf\nlf\ncrlf\r\nlf\nlf\ncrlf\r\n"); - cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src)); - check_buf("\r\nlf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\ncrlf\r\n", tgt); + cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src)); cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src)); check_buf("\nlf\nlf\ncrlf\nlf\nlf\ncrlf\n", tgt); @@ -980,8 +1078,7 @@ git_buf_sets(&src, "\ncrlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf"); - cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src)); - check_buf("\r\ncrlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf", tgt); + cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src)); cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src)); check_buf("\ncrlf\ncrlf\nlf\ncrlf\ncrlf", tgt); @@ -989,8 +1086,7 @@ git_buf_sets(&src, "\rcrlf\r\nlf\nlf\ncr\rcrlf\r\nlf\ncr\r"); - cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src)); - check_buf("\rcrlf\r\nlf\r\nlf\r\ncr\rcrlf\r\nlf\r\ncr\r", tgt); + cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src)); cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src)); check_buf("\rcrlf\nlf\nlf\ncr\rcrlf\nlf\ncr\r", tgt); @@ -1006,8 +1102,7 @@ /* blob correspondence tests */ git_buf_sets(&src, ALL_CRLF_TEXT_RAW); - cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src)); - check_buf(ALL_CRLF_TEXT_AS_CRLF, tgt); + cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src)); cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src)); check_buf(ALL_CRLF_TEXT_AS_LF, tgt); git_buf_free(&src); @@ -1022,16 +1117,14 @@ git_buf_free(&tgt); git_buf_sets(&src, MORE_CRLF_TEXT_RAW); - cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src)); - check_buf(MORE_CRLF_TEXT_AS_CRLF, tgt); + cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src)); cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src)); check_buf(MORE_CRLF_TEXT_AS_LF, tgt); git_buf_free(&src); git_buf_free(&tgt); git_buf_sets(&src, MORE_LF_TEXT_RAW); - cl_git_pass(git_buf_text_lf_to_crlf(&tgt, &src)); - check_buf(MORE_LF_TEXT_AS_CRLF, tgt); + cl_git_fail_with(GIT_PASSTHROUGH, git_buf_text_lf_to_crlf(&tgt, &src)); cl_git_pass(git_buf_text_crlf_to_lf(&tgt, &src)); check_buf(MORE_LF_TEXT_AS_LF, tgt); git_buf_free(&src); diff -Nru libgit2-0.20.0/tests/core/caps.c libgit2-0.22.2/tests/core/caps.c --- libgit2-0.20.0/tests/core/caps.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/core/caps.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -#include "clar_libgit2.h" - -void test_core_caps__0(void) -{ - int major, minor, rev, caps; - - git_libgit2_version(&major, &minor, &rev); - cl_assert_equal_i(LIBGIT2_VER_MAJOR, major); - cl_assert_equal_i(LIBGIT2_VER_MINOR, minor); - cl_assert_equal_i(LIBGIT2_VER_REVISION, rev); - - caps = git_libgit2_capabilities(); - -#ifdef GIT_THREADS - cl_assert((caps & GIT_CAP_THREADS) != 0); -#else - cl_assert((caps & GIT_CAP_THREADS) == 0); -#endif - -#if defined(GIT_SSL) || defined(GIT_WINHTTP) - cl_assert((caps & GIT_CAP_HTTPS) != 0); -#else - cl_assert((caps & GIT_CAP_HTTPS) == 0); -#endif - -#if defined(GIT_SSH) - cl_assert((caps & GIT_CAP_SSH) != 0); -#else - cl_assert((caps & GIT_CAP_SSH) == 0); -#endif -} diff -Nru libgit2-0.20.0/tests/core/copy.c libgit2-0.22.2/tests/core/copy.c --- libgit2-0.20.0/tests/core/copy.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/core/copy.c 2015-03-24 16:10:45.000000000 +0000 @@ -45,6 +45,16 @@ cl_assert(!git_path_isdir("an_dir")); } +void assert_hard_link(const char *path) +{ + /* we assert this by checking that there's more than one link to the file */ + struct stat st; + + cl_assert(git_path_isfile(path)); + cl_git_pass(p_stat(path, &st)); + cl_assert(st.st_nlink > 1); +} + void test_core_copy__tree(void) { struct stat st; @@ -122,5 +132,21 @@ cl_git_pass(git_futils_rmdir_r("t2", NULL, GIT_RMDIR_REMOVE_FILES)); cl_assert(!git_path_isdir("t2")); +#ifndef GIT_WIN32 + cl_git_pass(git_futils_cp_r("src", "t3", GIT_CPDIR_CREATE_EMPTY_DIRS | GIT_CPDIR_LINK_FILES, 0)); + cl_assert(git_path_isdir("t3")); + + cl_assert(git_path_isdir("t3")); + cl_assert(git_path_isdir("t3/b")); + cl_assert(git_path_isdir("t3/c")); + cl_assert(git_path_isdir("t3/c/d")); + cl_assert(git_path_isdir("t3/c/e")); + + assert_hard_link("t3/f1"); + assert_hard_link("t3/b/f2"); + assert_hard_link("t3/c/f3"); + assert_hard_link("t3/c/d/f4"); +#endif + cl_git_pass(git_futils_rmdir_r("src", NULL, GIT_RMDIR_REMOVE_FILES)); } diff -Nru libgit2-0.20.0/tests/core/env.c libgit2-0.22.2/tests/core/env.c --- libgit2-0.20.0/tests/core/env.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/core/env.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "fileops.h" +#include "sysdir.h" #include "path.h" #ifdef GIT_WIN32 @@ -20,7 +21,7 @@ "f\xc4\x80ke_\xc4\xa4ome", /* latin extended */ "f\xce\xb1\xce\xba\xce\xb5_h\xce\xbfm\xce\xad", /* having fun with greek */ "fa\xe0" "\xb8" "\x87" "e_\xe0" "\xb8" "\x99" "ome", /* thai characters */ - "f\xe1\x9cx80ke_\xe1\x9c\x91ome", /* tagalog characters */ + "f\xe1\x9c\x80ke_\xe1\x9c\x91ome", /* tagalog characters */ "\xe1\xb8\x9f\xe1\xba\xa2" "ke_ho" "\xe1" "\xb9" "\x81" "e", /* latin extended additional */ "\xf0\x9f\x98\x98\xf0\x9f\x98\x82", /* emoticons */ NULL @@ -39,14 +40,14 @@ } } -static void reset_global_search_path(void) +static void set_global_search_path_from_env(void) { - cl_git_pass(git_futils_dirs_set(GIT_FUTILS_DIR_GLOBAL, NULL)); + cl_git_pass(git_sysdir_set(GIT_SYSDIR_GLOBAL, NULL)); } -static void reset_system_search_path(void) +static void set_system_search_path_from_env(void) { - cl_git_pass(git_futils_dirs_set(GIT_FUTILS_DIR_SYSTEM, NULL)); + cl_git_pass(git_sysdir_set(GIT_SYSDIR_SYSTEM, NULL)); } void test_core_env__cleanup(void) @@ -68,9 +69,7 @@ (void)p_rmdir(*val); } - /* reset search paths to default */ - reset_global_search_path(); - reset_system_search_path(); + cl_sandbox_set_search_path_defaults(); } static void setenv_and_check(const char *name, const char *value) @@ -120,26 +119,26 @@ git_buf_rtruncate_at_char(&path, '/'); cl_assert_equal_i( - GIT_ENOTFOUND, git_futils_find_global_file(&found, testfile)); + GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile)); setenv_and_check("HOME", path.ptr); - reset_global_search_path(); + set_global_search_path_from_env(); - cl_git_pass(git_futils_find_global_file(&found, testfile)); + cl_git_pass(git_sysdir_find_global_file(&found, testfile)); cl_setenv("HOME", env_save[0]); - reset_global_search_path(); + set_global_search_path_from_env(); cl_assert_equal_i( - GIT_ENOTFOUND, git_futils_find_global_file(&found, testfile)); + GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile)); #ifdef GIT_WIN32 setenv_and_check("HOMEDRIVE", NULL); setenv_and_check("HOMEPATH", NULL); setenv_and_check("USERPROFILE", path.ptr); - reset_global_search_path(); + set_global_search_path_from_env(); - cl_git_pass(git_futils_find_global_file(&found, testfile)); + cl_git_pass(git_sysdir_find_global_file(&found, testfile)); { int root = git_path_root(path.ptr); @@ -147,19 +146,19 @@ if (root >= 0) { setenv_and_check("USERPROFILE", NULL); - reset_global_search_path(); + set_global_search_path_from_env(); cl_assert_equal_i( - GIT_ENOTFOUND, git_futils_find_global_file(&found, testfile)); + GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile)); old = path.ptr[root]; path.ptr[root] = '\0'; setenv_and_check("HOMEDRIVE", path.ptr); path.ptr[root] = old; setenv_and_check("HOMEPATH", &path.ptr[root]); - reset_global_search_path(); + set_global_search_path_from_env(); - cl_git_pass(git_futils_find_global_file(&found, testfile)); + cl_git_pass(git_sysdir_find_global_file(&found, testfile)); } } #endif @@ -177,38 +176,38 @@ git_buf path = GIT_BUF_INIT; cl_assert_equal_i( - GIT_ENOTFOUND, git_futils_find_global_file(&path, "nonexistentfile")); + GIT_ENOTFOUND, git_sysdir_find_global_file(&path, "nonexistentfile")); cl_git_pass(cl_setenv("HOME", "doesnotexist")); #ifdef GIT_WIN32 cl_git_pass(cl_setenv("HOMEPATH", "doesnotexist")); cl_git_pass(cl_setenv("USERPROFILE", "doesnotexist")); #endif - reset_global_search_path(); + set_global_search_path_from_env(); cl_assert_equal_i( - GIT_ENOTFOUND, git_futils_find_global_file(&path, "nonexistentfile")); + GIT_ENOTFOUND, git_sysdir_find_global_file(&path, "nonexistentfile")); cl_git_pass(cl_setenv("HOME", NULL)); #ifdef GIT_WIN32 cl_git_pass(cl_setenv("HOMEPATH", NULL)); cl_git_pass(cl_setenv("USERPROFILE", NULL)); #endif - reset_global_search_path(); - reset_system_search_path(); + set_global_search_path_from_env(); + set_system_search_path_from_env(); cl_assert_equal_i( - GIT_ENOTFOUND, git_futils_find_global_file(&path, "nonexistentfile")); + GIT_ENOTFOUND, git_sysdir_find_global_file(&path, "nonexistentfile")); cl_assert_equal_i( - GIT_ENOTFOUND, git_futils_find_system_file(&path, "nonexistentfile")); + GIT_ENOTFOUND, git_sysdir_find_system_file(&path, "nonexistentfile")); #ifdef GIT_WIN32 cl_git_pass(cl_setenv("PROGRAMFILES", NULL)); - reset_system_search_path(); + set_system_search_path_from_env(); cl_assert_equal_i( - GIT_ENOTFOUND, git_futils_find_system_file(&path, "nonexistentfile")); + GIT_ENOTFOUND, git_sysdir_find_system_file(&path, "nonexistentfile")); #endif git_buf_free(&path); @@ -217,7 +216,7 @@ static void check_global_searchpath( const char *path, int position, const char *file, git_buf *temp) { - char out[GIT_PATH_MAX]; + git_buf out = GIT_BUF_INIT; /* build and set new path */ if (position < 0) @@ -232,23 +231,25 @@ /* get path and make sure $PATH expansion worked */ cl_git_pass(git_libgit2_opts( - GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, out, sizeof(out))); + GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &out)); if (position < 0) - cl_assert(git__prefixcmp(out, path) == 0); + cl_assert(git__prefixcmp(out.ptr, path) == 0); else if (position > 0) - cl_assert(git__suffixcmp(out, path) == 0); + cl_assert(git__suffixcmp(out.ptr, path) == 0); else - cl_assert_equal_s(out, path); + cl_assert_equal_s(out.ptr, path); /* find file using new path */ - cl_git_pass(git_futils_find_global_file(temp, file)); + cl_git_pass(git_sysdir_find_global_file(temp, file)); /* reset path and confirm file not found */ cl_git_pass(git_libgit2_opts( GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL)); cl_assert_equal_i( - GIT_ENOTFOUND, git_futils_find_global_file(temp, file)); + GIT_ENOTFOUND, git_sysdir_find_global_file(temp, file)); + + git_buf_free(&out); } void test_core_env__2(void) @@ -285,7 +286,7 @@ /* default should be NOTFOUND */ cl_assert_equal_i( - GIT_ENOTFOUND, git_futils_find_global_file(&found, testfile)); + GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile)); /* try plain, append $PATH, and prepend $PATH */ check_global_searchpath(path.ptr, 0, testfile, &found); diff -Nru libgit2-0.20.0/tests/core/errors.c libgit2-0.22.2/tests/core/errors.c --- libgit2-0.20.0/tests/core/errors.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/core/errors.c 2015-03-24 16:10:45.000000000 +0000 @@ -85,3 +85,27 @@ giterr_clear(); } + +void test_core_errors__restore(void) +{ + git_error_state err_state = {0}; + + giterr_clear(); + cl_assert(giterr_last() == NULL); + + cl_assert_equal_i(0, giterr_capture(&err_state, 0)); + + memset(&err_state, 0x0, sizeof(git_error_state)); + + giterr_set(42, "Foo: %s", "bar"); + cl_assert_equal_i(-1, giterr_capture(&err_state, -1)); + + cl_assert(giterr_last() == NULL); + + giterr_set(99, "Bar: %s", "foo"); + + giterr_restore(&err_state); + + cl_assert_equal_i(42, giterr_last()->klass); + cl_assert_equal_s("Foo: bar", giterr_last()->message); +} diff -Nru libgit2-0.20.0/tests/core/features.c libgit2-0.22.2/tests/core/features.c --- libgit2-0.20.0/tests/core/features.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/core/features.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,31 @@ +#include "clar_libgit2.h" + +void test_core_features__0(void) +{ + int major, minor, rev, caps; + + git_libgit2_version(&major, &minor, &rev); + cl_assert_equal_i(LIBGIT2_VER_MAJOR, major); + cl_assert_equal_i(LIBGIT2_VER_MINOR, minor); + cl_assert_equal_i(LIBGIT2_VER_REVISION, rev); + + caps = git_libgit2_features(); + +#ifdef GIT_THREADS + cl_assert((caps & GIT_FEATURE_THREADS) != 0); +#else + cl_assert((caps & GIT_FEATURE_THREADS) == 0); +#endif + +#if defined(GIT_SSL) || defined(GIT_WINHTTP) + cl_assert((caps & GIT_FEATURE_HTTPS) != 0); +#else + cl_assert((caps & GIT_FEATURE_HTTPS) == 0); +#endif + +#if defined(GIT_SSH) + cl_assert((caps & GIT_FEATURE_SSH) != 0); +#else + cl_assert((caps & GIT_FEATURE_SSH) == 0); +#endif +} diff -Nru libgit2-0.20.0/tests/core/iconv.c libgit2-0.22.2/tests/core/iconv.c --- libgit2-0.20.0/tests/core/iconv.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/core/iconv.c 2015-03-24 16:10:45.000000000 +0000 @@ -39,8 +39,9 @@ { #ifdef GIT_USE_ICONV char *data = nfd; - size_t datalen = strlen(nfd); + size_t datalen, nfdlen = strlen(nfd); + datalen = nfdlen; cl_git_pass(git_path_iconv(&ic, &data, &datalen)); GIT_UNUSED(datalen); @@ -48,6 +49,15 @@ * (on platforms where iconv is enabled, of course). */ cl_assert_equal_s(nfc, data); + + /* should be able to do it multiple times with the same git_path_iconv_t */ + data = nfd; datalen = nfdlen; + cl_git_pass(git_path_iconv(&ic, &data, &datalen)); + cl_assert_equal_s(nfc, data); + + data = nfd; datalen = nfdlen; + cl_git_pass(git_path_iconv(&ic, &data, &datalen)); + cl_assert_equal_s(nfc, data); #endif } diff -Nru libgit2-0.20.0/tests/core/init.c libgit2-0.22.2/tests/core/init.c --- libgit2-0.20.0/tests/core/init.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/core/init.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,14 @@ +#include "clar_libgit2.h" + +void test_core_init__returns_count(void) +{ + /* libgit2_clar initializes us first, so we have an existing + * initialization. + */ + cl_assert_equal_i(2, git_libgit2_init()); + cl_assert_equal_i(3, git_libgit2_init()); + + cl_assert_equal_i(2, git_libgit2_shutdown()); + cl_assert_equal_i(1, git_libgit2_shutdown()); +} + diff -Nru libgit2-0.20.0/tests/core/link.c libgit2-0.22.2/tests/core/link.c --- libgit2-0.20.0/tests/core/link.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/core/link.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,632 @@ +#include "clar_libgit2.h" +#include "posix.h" +#include "buffer.h" +#include "path.h" + +#ifdef GIT_WIN32 +# include "win32/reparse.h" +#endif + +void test_core_link__cleanup(void) +{ +#ifdef GIT_WIN32 + RemoveDirectory("lstat_junction"); + RemoveDirectory("lstat_dangling"); + RemoveDirectory("lstat_dangling_dir"); + RemoveDirectory("lstat_dangling_junction"); + + RemoveDirectory("stat_junction"); + RemoveDirectory("stat_dangling"); + RemoveDirectory("stat_dangling_dir"); + RemoveDirectory("stat_dangling_junction"); +#endif +} + +#ifdef GIT_WIN32 +static bool should_run(void) +{ + static SID_IDENTIFIER_AUTHORITY authority = { SECURITY_NT_AUTHORITY }; + PSID admin_sid; + BOOL is_admin; + + cl_win32_pass(AllocateAndInitializeSid(&authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &admin_sid)); + cl_win32_pass(CheckTokenMembership(NULL, admin_sid, &is_admin)); + FreeSid(admin_sid); + + return is_admin ? true : false; +} +#else +static bool should_run(void) +{ + return true; +} +#endif + +static void do_symlink(const char *old, const char *new, int is_dir) +{ +#ifndef GIT_WIN32 + GIT_UNUSED(is_dir); + + cl_must_pass(symlink(old, new)); +#else + typedef DWORD (WINAPI *create_symlink_func)(LPCTSTR, LPCTSTR, DWORD); + HMODULE module; + create_symlink_func pCreateSymbolicLink; + + cl_assert(module = GetModuleHandle("kernel32")); + cl_assert(pCreateSymbolicLink = (create_symlink_func)GetProcAddress(module, "CreateSymbolicLinkA")); + + cl_win32_pass(pCreateSymbolicLink(new, old, is_dir)); +#endif +} + +static void do_hardlink(const char *old, const char *new) +{ +#ifndef GIT_WIN32 + cl_must_pass(link(old, new)); +#else + typedef DWORD (WINAPI *create_hardlink_func)(LPCTSTR, LPCTSTR, LPSECURITY_ATTRIBUTES); + HMODULE module; + create_hardlink_func pCreateHardLink; + + cl_assert(module = GetModuleHandle("kernel32")); + cl_assert(pCreateHardLink = (create_hardlink_func)GetProcAddress(module, "CreateHardLinkA")); + + cl_win32_pass(pCreateHardLink(new, old, 0)); +#endif +} + +#ifdef GIT_WIN32 + +static void do_junction(const char *old, const char *new) +{ + GIT_REPARSE_DATA_BUFFER *reparse_buf; + HANDLE handle; + git_buf unparsed_buf = GIT_BUF_INIT; + wchar_t *subst_utf16, *print_utf16; + DWORD ioctl_ret; + int subst_utf16_len, subst_byte_len, print_utf16_len, print_byte_len, ret; + USHORT reparse_buflen; + size_t i; + + /* Junction targets must be the unparsed name, starting with \??\, using + * backslashes instead of forward, and end in a trailing backslash. + * eg: \??\C:\Foo\ + */ + git_buf_puts(&unparsed_buf, "\\??\\"); + + for (i = 0; i < strlen(old); i++) + git_buf_putc(&unparsed_buf, old[i] == '/' ? '\\' : old[i]); + + git_buf_putc(&unparsed_buf, '\\'); + + subst_utf16_len = git__utf8_to_16(NULL, 0, git_buf_cstr(&unparsed_buf)); + subst_byte_len = subst_utf16_len * sizeof(WCHAR); + + print_utf16_len = subst_utf16_len - 4; + print_byte_len = subst_byte_len - (4 * sizeof(WCHAR)); + + /* The junction must be an empty directory before the junction attribute + * can be added. + */ + cl_win32_pass(CreateDirectoryA(new, NULL)); + + handle = CreateFileA(new, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); + cl_win32_pass(handle != INVALID_HANDLE_VALUE); + + reparse_buflen = (USHORT)(REPARSE_DATA_HEADER_SIZE + + REPARSE_DATA_MOUNTPOINT_HEADER_SIZE + + subst_byte_len + sizeof(WCHAR) + + print_byte_len + sizeof(WCHAR)); + + reparse_buf = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, reparse_buflen); + cl_assert(reparse_buf); + + subst_utf16 = reparse_buf->MountPointReparseBuffer.PathBuffer; + print_utf16 = subst_utf16 + subst_utf16_len + 1; + + ret = git__utf8_to_16(subst_utf16, subst_utf16_len + 1, + git_buf_cstr(&unparsed_buf)); + cl_assert_equal_i(subst_utf16_len, ret); + + ret = git__utf8_to_16(print_utf16, + print_utf16_len + 1, git_buf_cstr(&unparsed_buf) + 4); + cl_assert_equal_i(print_utf16_len, ret); + + reparse_buf->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT; + reparse_buf->MountPointReparseBuffer.SubstituteNameOffset = 0; + reparse_buf->MountPointReparseBuffer.SubstituteNameLength = subst_byte_len; + reparse_buf->MountPointReparseBuffer.PrintNameOffset = (USHORT)(subst_byte_len + sizeof(WCHAR)); + reparse_buf->MountPointReparseBuffer.PrintNameLength = print_byte_len; + reparse_buf->ReparseDataLength = reparse_buflen - REPARSE_DATA_HEADER_SIZE; + + cl_win32_pass(DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, + reparse_buf, reparse_buflen, NULL, 0, &ioctl_ret, NULL)); + + CloseHandle(handle); + LocalFree(reparse_buf); + + git_buf_free(&unparsed_buf); +} + +static void do_custom_reparse(const char *path) +{ + REPARSE_GUID_DATA_BUFFER *reparse_buf; + HANDLE handle; + DWORD ioctl_ret; + + const char *reparse_data = "Reparse points are silly."; + size_t reparse_buflen = REPARSE_GUID_DATA_BUFFER_HEADER_SIZE + + strlen(reparse_data) + 1; + + reparse_buf = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, reparse_buflen); + cl_assert(reparse_buf); + + reparse_buf->ReparseTag = 42; + reparse_buf->ReparseDataLength = (WORD)(strlen(reparse_data) + 1); + + reparse_buf->ReparseGuid.Data1 = 0xdeadbeef; + reparse_buf->ReparseGuid.Data2 = 0xdead; + reparse_buf->ReparseGuid.Data3 = 0xbeef; + reparse_buf->ReparseGuid.Data4[0] = 42; + reparse_buf->ReparseGuid.Data4[1] = 42; + reparse_buf->ReparseGuid.Data4[2] = 42; + reparse_buf->ReparseGuid.Data4[3] = 42; + reparse_buf->ReparseGuid.Data4[4] = 42; + reparse_buf->ReparseGuid.Data4[5] = 42; + reparse_buf->ReparseGuid.Data4[6] = 42; + reparse_buf->ReparseGuid.Data4[7] = 42; + reparse_buf->ReparseGuid.Data4[8] = 42; + + memcpy(reparse_buf->GenericReparseBuffer.DataBuffer, + reparse_data, strlen(reparse_data) + 1); + + handle = CreateFileA(path, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL); + cl_win32_pass(handle != INVALID_HANDLE_VALUE); + + cl_win32_pass(DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT, + reparse_buf, + reparse_buf->ReparseDataLength + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE, + NULL, 0, &ioctl_ret, NULL)); + + CloseHandle(handle); + LocalFree(reparse_buf); +} + +#endif + +void test_core_link__stat_regular_file(void) +{ + struct stat st; + + cl_git_rewritefile("stat_regfile", "This is a regular file!\n"); + + cl_must_pass(p_stat("stat_regfile", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(24, st.st_size); +} + +void test_core_link__lstat_regular_file(void) +{ + struct stat st; + + cl_git_rewritefile("lstat_regfile", "This is a regular file!\n"); + + cl_must_pass(p_stat("lstat_regfile", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(24, st.st_size); +} + +void test_core_link__stat_symlink(void) +{ + struct stat st; + + if (!should_run()) + clar__skip(); + + cl_git_rewritefile("stat_target", "This is the target of a symbolic link.\n"); + do_symlink("stat_target", "stat_symlink", 0); + + cl_must_pass(p_stat("stat_target", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(39, st.st_size); + + cl_must_pass(p_stat("stat_symlink", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(39, st.st_size); +} + +void test_core_link__stat_symlink_directory(void) +{ + struct stat st; + + if (!should_run()) + clar__skip(); + + p_mkdir("stat_dirtarget", 0777); + do_symlink("stat_dirtarget", "stat_dirlink", 1); + + cl_must_pass(p_stat("stat_dirtarget", &st)); + cl_assert(S_ISDIR(st.st_mode)); + + cl_must_pass(p_stat("stat_dirlink", &st)); + cl_assert(S_ISDIR(st.st_mode)); +} + +void test_core_link__stat_symlink_chain(void) +{ + struct stat st; + + if (!should_run()) + clar__skip(); + + cl_git_rewritefile("stat_final_target", "Final target of some symbolic links...\n"); + do_symlink("stat_final_target", "stat_chain_3", 0); + do_symlink("stat_chain_3", "stat_chain_2", 0); + do_symlink("stat_chain_2", "stat_chain_1", 0); + + cl_must_pass(p_stat("stat_chain_1", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(39, st.st_size); +} + +void test_core_link__stat_dangling_symlink(void) +{ + struct stat st; + + if (!should_run()) + clar__skip(); + + do_symlink("stat_nonexistent", "stat_dangling", 0); + + cl_must_fail(p_stat("stat_nonexistent", &st)); + cl_must_fail(p_stat("stat_dangling", &st)); +} + +void test_core_link__stat_dangling_symlink_directory(void) +{ + struct stat st; + + if (!should_run()) + clar__skip(); + + do_symlink("stat_nonexistent", "stat_dangling_dir", 1); + + cl_must_fail(p_stat("stat_nonexistent_dir", &st)); + cl_must_fail(p_stat("stat_dangling", &st)); +} + +void test_core_link__lstat_symlink(void) +{ + git_buf target_path = GIT_BUF_INIT; + struct stat st; + + if (!should_run()) + clar__skip(); + + /* Windows always writes the canonical path as the link target, so + * write the full path on all platforms. + */ + git_buf_join(&target_path, '/', clar_sandbox_path(), "lstat_target"); + + cl_git_rewritefile("lstat_target", "This is the target of a symbolic link.\n"); + do_symlink(git_buf_cstr(&target_path), "lstat_symlink", 0); + + cl_must_pass(p_lstat("lstat_target", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(39, st.st_size); + + cl_must_pass(p_lstat("lstat_symlink", &st)); + cl_assert(S_ISLNK(st.st_mode)); + cl_assert_equal_i(git_buf_len(&target_path), st.st_size); + + git_buf_free(&target_path); +} + +void test_core_link__lstat_symlink_directory(void) +{ + git_buf target_path = GIT_BUF_INIT; + struct stat st; + + if (!should_run()) + clar__skip(); + + git_buf_join(&target_path, '/', clar_sandbox_path(), "lstat_dirtarget"); + + p_mkdir("lstat_dirtarget", 0777); + do_symlink(git_buf_cstr(&target_path), "lstat_dirlink", 1); + + cl_must_pass(p_lstat("lstat_dirtarget", &st)); + cl_assert(S_ISDIR(st.st_mode)); + + cl_must_pass(p_lstat("lstat_dirlink", &st)); + cl_assert(S_ISLNK(st.st_mode)); + cl_assert_equal_i(git_buf_len(&target_path), st.st_size); + + git_buf_free(&target_path); +} + +void test_core_link__lstat_dangling_symlink(void) +{ + struct stat st; + + if (!should_run()) + clar__skip(); + + do_symlink("lstat_nonexistent", "lstat_dangling", 0); + + cl_must_fail(p_lstat("lstat_nonexistent", &st)); + + cl_must_pass(p_lstat("lstat_dangling", &st)); + cl_assert(S_ISLNK(st.st_mode)); + cl_assert_equal_i(strlen("lstat_nonexistent"), st.st_size); +} + +void test_core_link__lstat_dangling_symlink_directory(void) +{ + struct stat st; + + if (!should_run()) + clar__skip(); + + do_symlink("lstat_nonexistent", "lstat_dangling_dir", 1); + + cl_must_fail(p_lstat("lstat_nonexistent", &st)); + + cl_must_pass(p_lstat("lstat_dangling_dir", &st)); + cl_assert(S_ISLNK(st.st_mode)); + cl_assert_equal_i(strlen("lstat_nonexistent"), st.st_size); +} + +void test_core_link__stat_junction(void) +{ +#ifdef GIT_WIN32 + git_buf target_path = GIT_BUF_INIT; + struct stat st; + + git_buf_join(&target_path, '/', clar_sandbox_path(), "stat_junctarget"); + + p_mkdir("stat_junctarget", 0777); + do_junction(git_buf_cstr(&target_path), "stat_junction"); + + cl_must_pass(p_stat("stat_junctarget", &st)); + cl_assert(S_ISDIR(st.st_mode)); + + cl_must_pass(p_stat("stat_junction", &st)); + cl_assert(S_ISDIR(st.st_mode)); + + git_buf_free(&target_path); +#endif +} + +void test_core_link__stat_dangling_junction(void) +{ +#ifdef GIT_WIN32 + git_buf target_path = GIT_BUF_INIT; + struct stat st; + + git_buf_join(&target_path, '/', clar_sandbox_path(), "stat_nonexistent_junctarget"); + + p_mkdir("stat_nonexistent_junctarget", 0777); + do_junction(git_buf_cstr(&target_path), "stat_dangling_junction"); + + RemoveDirectory("stat_nonexistent_junctarget"); + + cl_must_fail(p_stat("stat_nonexistent_junctarget", &st)); + cl_must_fail(p_stat("stat_dangling_junction", &st)); + + git_buf_free(&target_path); +#endif +} + +void test_core_link__lstat_junction(void) +{ +#ifdef GIT_WIN32 + git_buf target_path = GIT_BUF_INIT; + struct stat st; + + git_buf_join(&target_path, '/', clar_sandbox_path(), "lstat_junctarget"); + + p_mkdir("lstat_junctarget", 0777); + do_junction(git_buf_cstr(&target_path), "lstat_junction"); + + cl_must_pass(p_lstat("lstat_junctarget", &st)); + cl_assert(S_ISDIR(st.st_mode)); + + cl_must_pass(p_lstat("lstat_junction", &st)); + cl_assert(S_ISLNK(st.st_mode)); + + git_buf_free(&target_path); +#endif +} + +void test_core_link__lstat_dangling_junction(void) +{ +#ifdef GIT_WIN32 + git_buf target_path = GIT_BUF_INIT; + struct stat st; + + git_buf_join(&target_path, '/', clar_sandbox_path(), "lstat_nonexistent_junctarget"); + + p_mkdir("lstat_nonexistent_junctarget", 0777); + do_junction(git_buf_cstr(&target_path), "lstat_dangling_junction"); + + RemoveDirectory("lstat_nonexistent_junctarget"); + + cl_must_fail(p_lstat("lstat_nonexistent_junctarget", &st)); + + cl_must_pass(p_lstat("lstat_dangling_junction", &st)); + cl_assert(S_ISLNK(st.st_mode)); + cl_assert_equal_i(git_buf_len(&target_path), st.st_size); + + git_buf_free(&target_path); +#endif +} + +void test_core_link__stat_hardlink(void) +{ + struct stat st; + + if (!should_run()) + clar__skip(); + + cl_git_rewritefile("stat_hardlink1", "This file has many names!\n"); + do_hardlink("stat_hardlink1", "stat_hardlink2"); + + cl_must_pass(p_stat("stat_hardlink1", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(26, st.st_size); + + cl_must_pass(p_stat("stat_hardlink2", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(26, st.st_size); +} + +void test_core_link__lstat_hardlink(void) +{ + struct stat st; + + if (!should_run()) + clar__skip(); + + cl_git_rewritefile("lstat_hardlink1", "This file has many names!\n"); + do_hardlink("lstat_hardlink1", "lstat_hardlink2"); + + cl_must_pass(p_lstat("lstat_hardlink1", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(26, st.st_size); + + cl_must_pass(p_lstat("lstat_hardlink2", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(26, st.st_size); +} + +void test_core_link__stat_reparse_point(void) +{ +#ifdef GIT_WIN32 + struct stat st; + + /* Generic reparse points should be treated as regular files, only + * symlinks and junctions should be treated as links. + */ + + cl_git_rewritefile("stat_reparse", "This is a reparse point!\n"); + do_custom_reparse("stat_reparse"); + + cl_must_pass(p_lstat("stat_reparse", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(25, st.st_size); +#endif +} + +void test_core_link__lstat_reparse_point(void) +{ +#ifdef GIT_WIN32 + struct stat st; + + cl_git_rewritefile("lstat_reparse", "This is a reparse point!\n"); + do_custom_reparse("lstat_reparse"); + + cl_must_pass(p_lstat("lstat_reparse", &st)); + cl_assert(S_ISREG(st.st_mode)); + cl_assert_equal_i(25, st.st_size); +#endif +} + +void test_core_link__readlink_nonexistent_file(void) +{ + char buf[2048]; + + cl_must_fail(p_readlink("readlink_nonexistent", buf, 2048)); + cl_assert_equal_i(ENOENT, errno); +} + +void test_core_link__readlink_normal_file(void) +{ + char buf[2048]; + + cl_git_rewritefile("readlink_regfile", "This is a regular file!\n"); + cl_must_fail(p_readlink("readlink_regfile", buf, 2048)); + cl_assert_equal_i(EINVAL, errno); +} + +void test_core_link__readlink_symlink(void) +{ + git_buf target_path = GIT_BUF_INIT; + int len; + char buf[2048]; + + if (!should_run()) + clar__skip(); + + git_buf_join(&target_path, '/', clar_sandbox_path(), "readlink_target"); + + cl_git_rewritefile("readlink_target", "This is the target of a symlink\n"); + do_symlink(git_buf_cstr(&target_path), "readlink_link", 0); + + len = p_readlink("readlink_link", buf, 2048); + cl_must_pass(len); + + buf[len] = 0; + + cl_assert_equal_s(git_buf_cstr(&target_path), buf); + + git_buf_free(&target_path); +} + +void test_core_link__readlink_dangling(void) +{ + git_buf target_path = GIT_BUF_INIT; + int len; + char buf[2048]; + + if (!should_run()) + clar__skip(); + + git_buf_join(&target_path, '/', clar_sandbox_path(), "readlink_nonexistent"); + + do_symlink(git_buf_cstr(&target_path), "readlink_dangling", 0); + + len = p_readlink("readlink_dangling", buf, 2048); + cl_must_pass(len); + + buf[len] = 0; + + cl_assert_equal_s(git_buf_cstr(&target_path), buf); + + git_buf_free(&target_path); +} + +void test_core_link__readlink_multiple(void) +{ + git_buf target_path = GIT_BUF_INIT, + path3 = GIT_BUF_INIT, path2 = GIT_BUF_INIT, path1 = GIT_BUF_INIT; + int len; + char buf[2048]; + + if (!should_run()) + clar__skip(); + + git_buf_join(&target_path, '/', clar_sandbox_path(), "readlink_final"); + git_buf_join(&path3, '/', clar_sandbox_path(), "readlink_3"); + git_buf_join(&path2, '/', clar_sandbox_path(), "readlink_2"); + git_buf_join(&path1, '/', clar_sandbox_path(), "readlink_1"); + + do_symlink(git_buf_cstr(&target_path), git_buf_cstr(&path3), 0); + do_symlink(git_buf_cstr(&path3), git_buf_cstr(&path2), 0); + do_symlink(git_buf_cstr(&path2), git_buf_cstr(&path1), 0); + + len = p_readlink("readlink_1", buf, 2048); + cl_must_pass(len); + + buf[len] = 0; + + cl_assert_equal_s(git_buf_cstr(&path2), buf); + + git_buf_free(&path1); + git_buf_free(&path2); + git_buf_free(&path3); + git_buf_free(&target_path); +} diff -Nru libgit2-0.20.0/tests/core/mkdir.c libgit2-0.22.2/tests/core/mkdir.c --- libgit2-0.20.0/tests/core/mkdir.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/core/mkdir.c 2015-03-24 16:10:45.000000000 +0000 @@ -186,3 +186,34 @@ cl_git_pass(git_path_lstat("r/mode2/is2/important2.1", &st)); check_mode(0777, st.st_mode); } + +void test_core_mkdir__mkdir_path_inside_unwriteable_parent(void) +{ + struct stat st; + mode_t *old; + + /* FAT filesystems don't support exec bit, nor group/world bits */ + if (!cl_is_chmod_supported()) + return; + + cl_assert((old = git__malloc(sizeof(mode_t))) != NULL); + *old = p_umask(022); + cl_set_cleanup(cleanup_chmod_root, old); + + cl_git_pass(git_futils_mkdir("r", NULL, 0777, 0)); + cl_git_pass(git_futils_mkdir("mode/is/important", "r", 0777, GIT_MKDIR_PATH)); + cl_git_pass(git_path_lstat("r/mode", &st)); + check_mode(0755, st.st_mode); + + cl_must_pass(p_chmod("r/mode", 0111)); + cl_git_pass(git_path_lstat("r/mode", &st)); + check_mode(0111, st.st_mode); + + cl_git_pass( + git_futils_mkdir("mode/is/okay/inside", "r", 0777, GIT_MKDIR_PATH)); + cl_git_pass(git_path_lstat("r/mode/is/okay/inside", &st)); + check_mode(0755, st.st_mode); + + cl_must_pass(p_chmod("r/mode", 0777)); +} + diff -Nru libgit2-0.20.0/tests/core/path.c libgit2-0.22.2/tests/core/path.c --- libgit2-0.20.0/tests/core/path.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/core/path.c 2015-03-24 16:10:45.000000000 +0000 @@ -350,37 +350,73 @@ typedef struct { int expect_idx; + int cancel_after; char **expect; } check_walkup_info; -static int check_one_walkup_step(void *ref, git_buf *path) +#define CANCEL_VALUE 1234 + +static int check_one_walkup_step(void *ref, const char *path) { check_walkup_info *info = (check_walkup_info *)ref; + + if (!info->cancel_after) { + cl_assert_equal_s(info->expect[info->expect_idx], "[CANCEL]"); + return CANCEL_VALUE; + } + info->cancel_after--; + cl_assert(info->expect[info->expect_idx] != NULL); - cl_assert_equal_s(info->expect[info->expect_idx], path->ptr); + cl_assert_equal_s(info->expect[info->expect_idx], path); info->expect_idx++; + return 0; } void test_core_path__11_walkup(void) { git_buf p = GIT_BUF_INIT; + char *expect[] = { - "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, - "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, - "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, - "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, - "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL, - "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL, - "this is a path", NULL, - "///a///b///c///d///e///", "///a///b///c///d///", "///a///b///c///", "///a///b///", "///a///", "///", NULL, - NULL + /* 1 */ "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, + /* 2 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, + /* 3 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, + /* 4 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL, + /* 5 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL, + /* 6 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL, + /* 7 */ "this_is_a_path", "", NULL, + /* 8 */ "this_is_a_path/", "", NULL, + /* 9 */ "///a///b///c///d///e///", "///a///b///c///d///", "///a///b///c///", "///a///b///", "///a///", "///", NULL, + /* 10 */ "a/b/c/", "a/b/", "a/", "", NULL, + /* 11 */ "a/b/c", "a/b/", "a/", "", NULL, + /* 12 */ "a/b/c/", "a/b/", "a/", NULL, + /* 13 */ "", NULL, + /* 14 */ "/", NULL, + /* 15 */ NULL + }; + + char *root[] = { + /* 1 */ NULL, + /* 2 */ NULL, + /* 3 */ "/", + /* 4 */ "", + /* 5 */ "/a/b", + /* 6 */ "/a/b/", + /* 7 */ NULL, + /* 8 */ NULL, + /* 9 */ NULL, + /* 10 */ NULL, + /* 11 */ NULL, + /* 12 */ "a/", + /* 13 */ NULL, + /* 14 */ NULL, }; - char *root[] = { NULL, NULL, "/", "", "/a/b", "/a/b/", NULL, NULL, NULL }; + int i, j; check_walkup_info info; info.expect = expect; + info.cancel_after = -1; for (i = 0, j = 0; expect[i] != NULL; i++, j++) { @@ -392,6 +428,41 @@ ); cl_assert_equal_s(p.ptr, expect[i]); + cl_assert(expect[info.expect_idx] == NULL); + i = info.expect_idx; + } + + git_buf_free(&p); +} + +void test_core_path__11a_walkup_cancel(void) +{ + git_buf p = GIT_BUF_INIT; + int cancel[] = { 3, 2, 1, 0 }; + char *expect[] = { + "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "[CANCEL]", NULL, + "/a/b/c/d/e", "/a/b/c/d/", "[CANCEL]", NULL, + "/a/b/c/d/e", "[CANCEL]", NULL, + "[CANCEL]", NULL, + NULL + }; + char *root[] = { NULL, NULL, "/", "", NULL }; + int i, j; + check_walkup_info info; + + info.expect = expect; + + for (i = 0, j = 0; expect[i] != NULL; i++, j++) { + + git_buf_sets(&p, expect[i]); + + info.cancel_after = cancel[j]; + info.expect_idx = i; + + cl_assert_equal_i( + CANCEL_VALUE, + git_path_walk_up(&p, root[j], check_one_walkup_step, &info) + ); /* skip to next run of expectations */ while (expect[i] != NULL) i++; diff -Nru libgit2-0.20.0/tests/core/pool.c libgit2-0.22.2/tests/core/pool.c --- libgit2-0.20.0/tests/core/pool.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/core/pool.c 2015-03-24 16:10:45.000000000 +0000 @@ -38,19 +38,19 @@ cl_assert(git_pool_malloc(&p, i) != NULL); /* with fixed page size, allocation must end up with these values */ - cl_assert(git_pool__open_pages(&p) == 1); - cl_assert(git_pool__full_pages(&p) == 505); + cl_assert_equal_i(1, git_pool__open_pages(&p)); + cl_assert_equal_i(507, git_pool__full_pages(&p)); git_pool_clear(&p); - cl_git_pass(git_pool_init(&p, 1, 4100)); + cl_git_pass(git_pool_init(&p, 1, 4120)); for (i = 2010; i > 0; i--) cl_assert(git_pool_malloc(&p, i) != NULL); /* with fixed page size, allocation must end up with these values */ - cl_assert(git_pool__open_pages(&p) == 1); - cl_assert(git_pool__full_pages(&p) == 492); + cl_assert_equal_i(1, git_pool__open_pages(&p)); + cl_assert_equal_i(492, git_pool__full_pages(&p)); git_pool_clear(&p); } @@ -139,7 +139,8 @@ git_pool p; cl_git_pass(git_pool_init(&p, 1, 100)); - cl_assert(git_pool_strndup(&p, "foo", -1) == NULL); + /* ensure 64 bit doesn't overflow */ + cl_assert(git_pool_strndup(&p, "foo", (size_t)-1) == NULL); git_pool_clear(&p); } diff -Nru libgit2-0.20.0/tests/core/pqueue.c libgit2-0.22.2/tests/core/pqueue.c --- libgit2-0.20.0/tests/core/pqueue.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/core/pqueue.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,128 @@ +#include "clar_libgit2.h" +#include "pqueue.h" + +static int cmp_ints(const void *v1, const void *v2) +{ + int i1 = *(int *)v1, i2 = *(int *)v2; + return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0; +} + +void test_core_pqueue__items_are_put_in_order(void) +{ + git_pqueue pq; + int i, vals[20]; + + cl_git_pass(git_pqueue_init(&pq, 0, 20, cmp_ints)); + + for (i = 0; i < 20; ++i) { + if (i < 10) + vals[i] = 10 - i; /* 10 down to 1 */ + else + vals[i] = i + 1; /* 11 up to 20 */ + + cl_git_pass(git_pqueue_insert(&pq, &vals[i])); + } + + cl_assert_equal_i(20, git_pqueue_size(&pq)); + + for (i = 1; i <= 20; ++i) { + void *p = git_pqueue_pop(&pq); + cl_assert(p); + cl_assert_equal_i(i, *(int *)p); + } + + cl_assert_equal_i(0, git_pqueue_size(&pq)); + + git_pqueue_free(&pq); +} + +void test_core_pqueue__interleave_inserts_and_pops(void) +{ + git_pqueue pq; + int chunk, v, i, vals[200]; + + cl_git_pass(git_pqueue_init(&pq, 0, 20, cmp_ints)); + + for (v = 0, chunk = 20; chunk <= 200; chunk += 20) { + /* push the next 20 */ + for (; v < chunk; ++v) { + vals[v] = (v & 1) ? 200 - v : v; + cl_git_pass(git_pqueue_insert(&pq, &vals[v])); + } + + /* pop the lowest 10 */ + for (i = 0; i < 10; ++i) + (void)git_pqueue_pop(&pq); + } + + cl_assert_equal_i(100, git_pqueue_size(&pq)); + + /* at this point, we've popped 0-99 */ + + for (v = 100; v < 200; ++v) { + void *p = git_pqueue_pop(&pq); + cl_assert(p); + cl_assert_equal_i(v, *(int *)p); + } + + cl_assert_equal_i(0, git_pqueue_size(&pq)); + + git_pqueue_free(&pq); +} + +void test_core_pqueue__max_heap_size(void) +{ + git_pqueue pq; + int i, vals[100]; + + cl_git_pass(git_pqueue_init(&pq, GIT_PQUEUE_FIXED_SIZE, 50, cmp_ints)); + + for (i = 0; i < 100; ++i) { + vals[i] = (i & 1) ? 100 - i : i; + cl_git_pass(git_pqueue_insert(&pq, &vals[i])); + } + + cl_assert_equal_i(50, git_pqueue_size(&pq)); + + for (i = 50; i < 100; ++i) { + void *p = git_pqueue_pop(&pq); + cl_assert(p); + cl_assert_equal_i(i, *(int *)p); + } + + cl_assert_equal_i(0, git_pqueue_size(&pq)); + + git_pqueue_free(&pq); + +} + +static int cmp_ints_like_commit_time(const void *a, const void *b) +{ + return *((const int *)a) < *((const int *)b); +} + +void test_core_pqueue__interleaved_pushes_and_pops(void) +{ + git_pqueue pq; + int i, j, *val; + static int commands[] = + { 6, 9, 8, 0, 5, 0, 7, 0, 4, 3, 0, 0, 0, 4, 0, 2, 0, 1, 0, 0, -1 }; + static int expected[] = + { 9, 8, 7, 6, 5, 4, 4, 3, 2, 1, -1 }; + + cl_git_pass(git_pqueue_init(&pq, 0, 10, cmp_ints_like_commit_time)); + + for (i = 0, j = 0; commands[i] >= 0; ++i) { + if (!commands[i]) { + cl_assert((val = git_pqueue_pop(&pq)) != NULL); + cl_assert_equal_i(expected[j], *val); + ++j; + } else { + cl_git_pass(git_pqueue_insert(&pq, &commands[i])); + } + } + + cl_assert_equal_i(0, git_pqueue_size(&pq)); + git_pqueue_free(&pq); +} + diff -Nru libgit2-0.20.0/tests/core/stat.c libgit2-0.22.2/tests/core/stat.c --- libgit2-0.20.0/tests/core/stat.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/core/stat.c 2015-03-24 16:10:45.000000000 +0000 @@ -95,3 +95,20 @@ cl_assert_error(ENOTDIR); } +void test_core_stat__root(void) +{ + const char *sandbox = clar_sandbox_path(); + git_buf root = GIT_BUF_INIT; + int root_len; + struct stat st; + + root_len = git_path_root(sandbox); + cl_assert(root_len >= 0); + + git_buf_set(&root, sandbox, root_len+1); + + cl_must_pass(p_stat(root.ptr, &st)); + cl_assert(S_ISDIR(st.st_mode)); + + git_buf_free(&root); +} diff -Nru libgit2-0.20.0/tests/core/strmap.c libgit2-0.22.2/tests/core/strmap.c --- libgit2-0.20.0/tests/core/strmap.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/core/strmap.c 2015-03-24 16:10:45.000000000 +0000 @@ -3,12 +3,22 @@ GIT__USE_STRMAP; +git_strmap *g_table; + +void test_core_strmap__initialize(void) +{ + cl_git_pass(git_strmap_alloc(&g_table)); + cl_assert(g_table != NULL); +} + +void test_core_strmap__cleanup(void) +{ + git_strmap_free(g_table); +} + void test_core_strmap__0(void) { - git_strmap *table = git_strmap_alloc(); - cl_assert(table != NULL); - cl_assert(git_strmap_num_entries(table) == 0); - git_strmap_free(table); + cl_assert(git_strmap_num_entries(g_table) == 0); } static void insert_strings(git_strmap *table, int count) @@ -37,21 +47,17 @@ { int i; char *str; - git_strmap *table = git_strmap_alloc(); - cl_assert(table != NULL); - insert_strings(table, 20); + insert_strings(g_table, 20); - cl_assert(git_strmap_exists(table, "aaaaaaaaa")); - cl_assert(git_strmap_exists(table, "ggggggggg")); - cl_assert(!git_strmap_exists(table, "aaaaaaaab")); - cl_assert(!git_strmap_exists(table, "abcdefghi")); + cl_assert(git_strmap_exists(g_table, "aaaaaaaaa")); + cl_assert(git_strmap_exists(g_table, "ggggggggg")); + cl_assert(!git_strmap_exists(g_table, "aaaaaaaab")); + cl_assert(!git_strmap_exists(g_table, "abcdefghi")); i = 0; - git_strmap_foreach_value(table, str, { i++; free(str); }); + git_strmap_foreach_value(g_table, str, { i++; free(str); }); cl_assert(i == 20); - - git_strmap_free(table); } void test_core_strmap__2(void) @@ -59,44 +65,36 @@ khiter_t pos; int i; char *str; - git_strmap *table = git_strmap_alloc(); - cl_assert(table != NULL); - insert_strings(table, 20); + insert_strings(g_table, 20); - cl_assert(git_strmap_exists(table, "aaaaaaaaa")); - cl_assert(git_strmap_exists(table, "ggggggggg")); - cl_assert(!git_strmap_exists(table, "aaaaaaaab")); - cl_assert(!git_strmap_exists(table, "abcdefghi")); - - cl_assert(git_strmap_exists(table, "bbbbbbbbb")); - pos = git_strmap_lookup_index(table, "bbbbbbbbb"); - cl_assert(git_strmap_valid_index(table, pos)); - cl_assert_equal_s(git_strmap_value_at(table, pos), "bbbbbbbbb"); - free(git_strmap_value_at(table, pos)); - git_strmap_delete_at(table, pos); + cl_assert(git_strmap_exists(g_table, "aaaaaaaaa")); + cl_assert(git_strmap_exists(g_table, "ggggggggg")); + cl_assert(!git_strmap_exists(g_table, "aaaaaaaab")); + cl_assert(!git_strmap_exists(g_table, "abcdefghi")); + + cl_assert(git_strmap_exists(g_table, "bbbbbbbbb")); + pos = git_strmap_lookup_index(g_table, "bbbbbbbbb"); + cl_assert(git_strmap_valid_index(g_table, pos)); + cl_assert_equal_s(git_strmap_value_at(g_table, pos), "bbbbbbbbb"); + free(git_strmap_value_at(g_table, pos)); + git_strmap_delete_at(g_table, pos); - cl_assert(!git_strmap_exists(table, "bbbbbbbbb")); + cl_assert(!git_strmap_exists(g_table, "bbbbbbbbb")); i = 0; - git_strmap_foreach_value(table, str, { i++; free(str); }); + git_strmap_foreach_value(g_table, str, { i++; free(str); }); cl_assert(i == 19); - - git_strmap_free(table); } void test_core_strmap__3(void) { int i; char *str; - git_strmap *table = git_strmap_alloc(); - cl_assert(table != NULL); - insert_strings(table, 10000); + insert_strings(g_table, 10000); i = 0; - git_strmap_foreach_value(table, str, { i++; free(str); }); + git_strmap_foreach_value(g_table, str, { i++; free(str); }); cl_assert(i == 10000); - - git_strmap_free(table); } diff -Nru libgit2-0.20.0/tests/core/vector.c libgit2-0.22.2/tests/core/vector.c --- libgit2-0.20.0/tests/core/vector.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/core/vector.c 2015-03-24 16:10:45.000000000 +0000 @@ -190,8 +190,9 @@ git_vector_free(&x); } -static int remove_ones(const git_vector *v, size_t idx) +static int remove_ones(const git_vector *v, size_t idx, void *p) { + GIT_UNUSED(p); return (git_vector_get(v, idx) == (void *)0x001); } @@ -206,7 +207,7 @@ git_vector_insert(&x, (void*) 0x001); cl_assert(x.length == 1); - git_vector_remove_matching(&x, remove_ones); + git_vector_remove_matching(&x, remove_ones, NULL); cl_assert(x.length == 0); git_vector_insert(&x, (void*) 0x001); @@ -214,7 +215,7 @@ git_vector_insert(&x, (void*) 0x001); cl_assert(x.length == 3); - git_vector_remove_matching(&x, remove_ones); + git_vector_remove_matching(&x, remove_ones, NULL); cl_assert(x.length == 0); git_vector_insert(&x, (void*) 0x002); @@ -223,7 +224,7 @@ git_vector_insert(&x, (void*) 0x001); cl_assert(x.length == 4); - git_vector_remove_matching(&x, remove_ones); + git_vector_remove_matching(&x, remove_ones, NULL); cl_assert(x.length == 2); git_vector_foreach(&x, i, compare) { @@ -238,7 +239,7 @@ git_vector_insert(&x, (void*) 0x001); cl_assert(x.length == 4); - git_vector_remove_matching(&x, remove_ones); + git_vector_remove_matching(&x, remove_ones, NULL); cl_assert(x.length == 2); git_vector_foreach(&x, i, compare) { @@ -253,7 +254,7 @@ git_vector_insert(&x, (void*) 0x001); cl_assert(x.length == 4); - git_vector_remove_matching(&x, remove_ones); + git_vector_remove_matching(&x, remove_ones, NULL); cl_assert(x.length == 2); git_vector_foreach(&x, i, compare) { @@ -268,7 +269,7 @@ git_vector_insert(&x, (void*) 0x003); cl_assert(x.length == 4); - git_vector_remove_matching(&x, remove_ones); + git_vector_remove_matching(&x, remove_ones, NULL); cl_assert(x.length == 4); git_vector_free(&x); diff -Nru libgit2-0.20.0/tests/core/zstream.c libgit2-0.22.2/tests/core/zstream.c --- libgit2-0.20.0/tests/core/zstream.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/core/zstream.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,143 @@ +#include "clar_libgit2.h" +#include "buffer.h" +#include "zstream.h" + +static const char *data = "This is a test test test of This is a test"; + +#define INFLATE_EXTRA 2 + +static void assert_zlib_equal_( + const void *expected, size_t e_len, + const void *compressed, size_t c_len, + const char *msg, const char *file, int line) +{ + z_stream stream; + char *expanded = git__calloc(1, e_len + INFLATE_EXTRA); + cl_assert(expanded); + + memset(&stream, 0, sizeof(stream)); + stream.next_out = (Bytef *)expanded; + stream.avail_out = (uInt)(e_len + INFLATE_EXTRA); + stream.next_in = (Bytef *)compressed; + stream.avail_in = (uInt)c_len; + + cl_assert(inflateInit(&stream) == Z_OK); + cl_assert(inflate(&stream, Z_FINISH)); + inflateEnd(&stream); + + clar__assert_equal( + file, line, msg, 1, + "%d", (int)stream.total_out, (int)e_len); + clar__assert_equal( + file, line, "Buffer len was not exact match", 1, + "%d", (int)stream.avail_out, (int)INFLATE_EXTRA); + + clar__assert( + memcmp(expanded, expected, e_len) == 0, + file, line, "uncompressed data did not match", NULL, 1); + + git__free(expanded); +} + +#define assert_zlib_equal(E,EL,C,CL) \ + assert_zlib_equal_(E, EL, C, CL, #EL " != " #CL, __FILE__, (int)__LINE__) + +void test_core_zstream__basic(void) +{ + git_zstream z = GIT_ZSTREAM_INIT; + char out[128]; + size_t outlen = sizeof(out); + + cl_git_pass(git_zstream_init(&z)); + cl_git_pass(git_zstream_set_input(&z, data, strlen(data) + 1)); + cl_git_pass(git_zstream_get_output(out, &outlen, &z)); + cl_assert(git_zstream_done(&z)); + cl_assert(outlen > 0); + git_zstream_free(&z); + + assert_zlib_equal(data, strlen(data) + 1, out, outlen); +} + +void test_core_zstream__buffer(void) +{ + git_buf out = GIT_BUF_INIT; + cl_git_pass(git_zstream_deflatebuf(&out, data, strlen(data) + 1)); + assert_zlib_equal(data, strlen(data) + 1, out.ptr, out.size); + git_buf_free(&out); +} + +#define BIG_STRING_PART "Big Data IS Big - Long Data IS Long - We need a buffer larger than 1024 x 1024 to make sure we trigger chunked compression - Big Big Data IS Bigger than Big - Long Long Data IS Longer than Long" + +static void compress_input_various_ways(git_buf *input) +{ + git_buf out1 = GIT_BUF_INIT, out2 = GIT_BUF_INIT; + size_t i, fixed_size = max(input->size / 2, 256); + char *fixed = git__malloc(fixed_size); + cl_assert(fixed); + + /* compress with deflatebuf */ + + cl_git_pass(git_zstream_deflatebuf(&out1, input->ptr, input->size)); + assert_zlib_equal(input->ptr, input->size, out1.ptr, out1.size); + + /* compress with various fixed size buffer (accumulating the output) */ + + for (i = 0; i < 3; ++i) { + git_zstream zs = GIT_ZSTREAM_INIT; + size_t use_fixed_size; + + switch (i) { + case 0: use_fixed_size = 256; break; + case 1: use_fixed_size = fixed_size / 2; break; + case 2: use_fixed_size = fixed_size; break; + } + cl_assert(use_fixed_size <= fixed_size); + + cl_git_pass(git_zstream_init(&zs)); + cl_git_pass(git_zstream_set_input(&zs, input->ptr, input->size)); + + while (!git_zstream_done(&zs)) { + size_t written = use_fixed_size; + cl_git_pass(git_zstream_get_output(fixed, &written, &zs)); + cl_git_pass(git_buf_put(&out2, fixed, written)); + } + + git_zstream_free(&zs); + assert_zlib_equal(input->ptr, input->size, out2.ptr, out2.size); + + /* did both approaches give the same data? */ + cl_assert_equal_sz(out1.size, out2.size); + cl_assert(!memcmp(out1.ptr, out2.ptr, out1.size)); + + git_buf_free(&out2); + } + + git_buf_free(&out1); + git__free(fixed); +} + +void test_core_zstream__big_data(void) +{ + git_buf in = GIT_BUF_INIT; + size_t scan, target; + + for (target = 1024; target <= 1024 * 1024 * 4; target *= 8) { + + /* make a big string that's easy to compress */ + git_buf_clear(&in); + while (in.size < target) + cl_git_pass( + git_buf_put(&in, BIG_STRING_PART, strlen(BIG_STRING_PART))); + + compress_input_various_ways(&in); + + /* make a big string that's hard to compress */ + srand(0xabad1dea); + for (scan = 0; scan < in.size; ++scan) + in.ptr[scan] = (char)rand(); + + compress_input_various_ways(&in); + } + + git_buf_free(&in); +} diff -Nru libgit2-0.20.0/tests/date/rfc2822.c libgit2-0.22.2/tests/date/rfc2822.c --- libgit2-0.20.0/tests/date/rfc2822.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/date/rfc2822.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,40 @@ +#include "clar_libgit2.h" + +#include "util.h" + +void test_date_rfc2822__format_rfc2822_no_offset(void) +{ + git_time t = {1397031663, 0}; + char buf[GIT_DATE_RFC2822_SZ]; + + cl_git_pass(git__date_rfc2822_fmt(buf, sizeof(buf), &t)); + cl_assert(strcmp(buf, "Wed, 9 Apr 2014 08:21:03 +0000") == 0); +} + +void test_date_rfc2822__format_rfc2822_positive_offset(void) +{ + git_time t = {1397031663, 120}; + char buf[GIT_DATE_RFC2822_SZ]; + + cl_git_pass(git__date_rfc2822_fmt(buf, sizeof(buf), &t)); + cl_assert(strcmp(buf, "Wed, 9 Apr 2014 10:21:03 +0200") == 0); +} + +void test_date_rfc2822__format_rfc2822_negative_offset(void) +{ + git_time t = {1397031663, -120}; + char buf[GIT_DATE_RFC2822_SZ]; + + cl_git_pass(git__date_rfc2822_fmt(buf, sizeof(buf), &t)); + cl_assert(strcmp(buf, "Wed, 9 Apr 2014 06:21:03 -0200") == 0); +} + +void test_date_rfc2822__format_rfc2822_buffer_too_small(void) +{ + // "Wed, 10 Apr 2014 08:21:03 +0000" + git_time t = {1397031663 + 86400, 0}; + char buf[GIT_DATE_RFC2822_SZ-1]; + + cl_git_fail(git__date_rfc2822_fmt(buf, sizeof(buf), &t)); +} + diff -Nru libgit2-0.20.0/tests/describe/describe.c libgit2-0.22.2/tests/describe/describe.c --- libgit2-0.20.0/tests/describe/describe.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/describe/describe.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,50 @@ +#include "clar_libgit2.h" +#include "describe_helpers.h" + +void test_describe_describe__can_describe_against_a_bare_repo(void) +{ + git_repository *repo; + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; + + cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); + + assert_describe("hard_tag", "HEAD", repo, &opts, &fmt_opts); + + opts.show_commit_oid_as_fallback = 1; + + assert_describe("be3563a*", "HEAD^", repo, &opts, &fmt_opts); + + git_repository_free(repo); +} + +static int delete_cb(git_reference *ref, void *payload) +{ + GIT_UNUSED(payload); + + cl_git_pass(git_reference_delete(ref)); + git_reference_free(ref); + + return 0; +} + +void test_describe_describe__cannot_describe_against_a_repo_with_no_ref(void) +{ + git_repository *repo; + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; + git_buf buf = GIT_BUF_INIT; + git_object *object; + git_describe_result *result = NULL; + + repo = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_revparse_single(&object, repo, "HEAD")); + + cl_git_pass(git_reference_foreach(repo, delete_cb, NULL)); + + cl_git_fail(git_describe_commit(&result, object, &opts)); + + git_describe_result_free(result); + git_object_free(object); + git_buf_free(&buf); + cl_git_sandbox_cleanup(); +} diff -Nru libgit2-0.20.0/tests/describe/describe_helpers.c libgit2-0.22.2/tests/describe/describe_helpers.c --- libgit2-0.20.0/tests/describe/describe_helpers.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/describe/describe_helpers.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,42 @@ +#include "describe_helpers.h" + +void assert_describe( + const char *expected_output, + const char *revparse_spec, + git_repository *repo, + git_describe_options *opts, + git_describe_format_options *fmt_opts) +{ + git_object *object; + git_buf label = GIT_BUF_INIT; + git_describe_result *result; + + cl_git_pass(git_revparse_single(&object, repo, revparse_spec)); + + cl_git_pass(git_describe_commit(&result, object, opts)); + cl_git_pass(git_describe_format(&label, result, fmt_opts)); + + cl_must_pass(p_fnmatch(expected_output, git_buf_cstr(&label), 0)); + + git_describe_result_free(result); + git_object_free(object); + git_buf_free(&label); +} + +void assert_describe_workdir( + const char *expected_output, + git_repository *repo, + git_describe_options *opts, + git_describe_format_options *fmt_opts) +{ + git_buf label = GIT_BUF_INIT; + git_describe_result *result; + + cl_git_pass(git_describe_workdir(&result, repo, opts)); + cl_git_pass(git_describe_format(&label, result, fmt_opts)); + + cl_must_pass(p_fnmatch(expected_output, git_buf_cstr(&label), 0)); + + git_describe_result_free(result); + git_buf_free(&label); +} diff -Nru libgit2-0.20.0/tests/describe/describe_helpers.h libgit2-0.22.2/tests/describe/describe_helpers.h --- libgit2-0.20.0/tests/describe/describe_helpers.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/describe/describe_helpers.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,15 @@ +#include "clar_libgit2.h" +#include "buffer.h" + +extern void assert_describe( + const char *expected_output, + const char *revparse_spec, + git_repository *repo, + git_describe_options *opts, + git_describe_format_options *fmt_opts); + +extern void assert_describe_workdir( + const char *expected_output, + git_repository *repo, + git_describe_options *opts, + git_describe_format_options *fmt_opts); diff -Nru libgit2-0.20.0/tests/describe/t6120.c libgit2-0.22.2/tests/describe/t6120.c --- libgit2-0.20.0/tests/describe/t6120.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/describe/t6120.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,156 @@ +#include "clar_libgit2.h" +#include "describe_helpers.h" +#include "repository.h" + +// Ported from https://github.com/git/git/blob/adfc1857bdb090786fd9d22c1acec39371c76048/t/t6120-describe.sh + +static git_repository *repo; + +void test_describe_t6120__initialize(void) +{ + repo = cl_git_sandbox_init("describe"); +} + +void test_describe_t6120__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_describe_t6120__default(void) +{ + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; + + assert_describe("A-*", "HEAD", repo, &opts, &fmt_opts); + assert_describe("A-*", "HEAD^", repo, &opts, &fmt_opts); + assert_describe("R-*", "HEAD^^", repo, &opts, &fmt_opts); + assert_describe("A-*", "HEAD^^2", repo, &opts, &fmt_opts); + assert_describe("B", "HEAD^^2^", repo, &opts, &fmt_opts); + assert_describe("R-*", "HEAD^^^", repo, &opts, &fmt_opts); +} + +void test_describe_t6120__tags(void) +{ + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; + opts.describe_strategy = GIT_DESCRIBE_TAGS; + + assert_describe("c-*", "HEAD", repo, &opts, &fmt_opts); + assert_describe("c-*", "HEAD^", repo, &opts, &fmt_opts); + assert_describe("e-*", "HEAD^^", repo, &opts, &fmt_opts); + assert_describe("c-*", "HEAD^^2", repo, &opts, &fmt_opts); + assert_describe("B", "HEAD^^2^", repo, &opts, &fmt_opts); + assert_describe("e", "HEAD^^^", repo, &opts, &fmt_opts); +} + +void test_describe_t6120__all(void) +{ + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; + opts.describe_strategy = GIT_DESCRIBE_ALL; + + assert_describe("heads/master", "HEAD", repo, &opts, &fmt_opts); + assert_describe("tags/c-*", "HEAD^", repo, &opts, &fmt_opts); + assert_describe("tags/e", "HEAD^^^", repo, &opts, &fmt_opts); +} + +void test_describe_t6120__longformat(void) +{ + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; + + fmt_opts.always_use_long_format = 1; + + assert_describe("B-0-*", "HEAD^^2^", repo, &opts, &fmt_opts); + assert_describe("A-3-*", "HEAD^^2", repo, &opts, &fmt_opts); +} + +void test_describe_t6120__firstparent(void) +{ + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; + opts.describe_strategy = GIT_DESCRIBE_TAGS; + + assert_describe("c-7-*", "HEAD", repo, &opts, &fmt_opts); + + opts.only_follow_first_parent = 1; + assert_describe("e-3-*", "HEAD", repo, &opts, &fmt_opts); +} + +void test_describe_t6120__workdir(void) +{ + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; + + assert_describe_workdir("A-*[0-9a-f]", repo, &opts, &fmt_opts); + cl_git_mkfile("describe/file", "something different"); + + fmt_opts.dirty_suffix = "-dirty"; + assert_describe_workdir("A-*[0-9a-f]-dirty", repo, &opts, &fmt_opts); + fmt_opts.dirty_suffix = ".mod"; + assert_describe_workdir("A-*[0-9a-f].mod", repo, &opts, &fmt_opts); +} + +static void commit_and_tag( + git_time_t *time, + const char *commit_msg, + const char *tag_name) +{ + git_index *index; + git_oid commit_id; + git_reference *ref; + + cl_git_pass(git_repository_index__weakptr(&index, repo)); + + cl_git_append2file("describe/file", "\n"); + + git_index_add_bypath(index, "describe/file"); + git_index_write(index); + + *time += 10; + cl_repo_commit_from_index(&commit_id, repo, NULL, *time, commit_msg); + + if (tag_name == NULL) + return; + + cl_git_pass(git_reference_create(&ref, repo, tag_name, &commit_id, 0, NULL, NULL)); + git_reference_free(ref); +} + +void test_describe_t6120__pattern(void) +{ + git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT; + git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT; + git_oid tag_id; + git_object *head; + git_signature *tagger; + git_time_t time; + + /* set-up matching pattern tests */ + cl_git_pass(git_revparse_single(&head, repo, "HEAD")); + + time = 1380553019; + cl_git_pass(git_signature_new(&tagger, "tagger", "tagger@libgit2.org", time, 0)); + cl_git_pass(git_tag_create(&tag_id, repo, "test-annotated", head, tagger, "test-annotated", 0)); + git_signature_free(tagger); + git_object_free(head); + + commit_and_tag(&time, "one more", "refs/tags/test1-lightweight"); + commit_and_tag(&time, "yet another", "refs/tags/test2-lightweight"); + commit_and_tag(&time, "even more", NULL); + + + /* Exercize */ + opts.pattern = "test-*"; + assert_describe("test-annotated-*", "HEAD", repo, &opts, &fmt_opts); + + opts.describe_strategy = GIT_DESCRIBE_TAGS; + opts.pattern = "test1-*"; + assert_describe("test1-lightweight-*", "HEAD", repo, &opts, &fmt_opts); + + opts.pattern = "test2-*"; + assert_describe("test2-lightweight-*", "HEAD", repo, &opts, &fmt_opts); + + fmt_opts.always_use_long_format = 1; + assert_describe("test2-lightweight-*", "HEAD^", repo, &opts, &fmt_opts); +} diff -Nru libgit2-0.20.0/tests/diff/binary.c libgit2-0.22.2/tests/diff/binary.c --- libgit2-0.20.0/tests/diff/binary.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/diff/binary.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,263 @@ +#include "clar_libgit2.h" + +#include "buffer.h" +#include "filebuf.h" + +static git_repository *repo; + +void test_diff_binary__initialize(void) +{ +} + +void test_diff_binary__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_patch( + const char *one, + const char *two, + const git_diff_options *opts, + const char *expected) +{ + git_oid id_one, id_two; + git_index *index = NULL; + git_commit *commit_one, *commit_two = NULL; + git_tree *tree_one, *tree_two; + git_diff *diff; + git_patch *patch; + git_buf actual = GIT_BUF_INIT; + + cl_git_pass(git_oid_fromstr(&id_one, one)); + cl_git_pass(git_commit_lookup(&commit_one, repo, &id_one)); + cl_git_pass(git_commit_tree(&tree_one, commit_one)); + + if (two) { + cl_git_pass(git_oid_fromstr(&id_two, two)); + cl_git_pass(git_commit_lookup(&commit_two, repo, &id_two)); + cl_git_pass(git_commit_tree(&tree_two, commit_two)); + } else { + cl_git_pass(git_repository_index(&index, repo)); + cl_git_pass(git_index_write_tree(&id_two, index)); + cl_git_pass(git_tree_lookup(&tree_two, repo, &id_two)); + } + + cl_git_pass(git_diff_tree_to_tree(&diff, repo, tree_one, tree_two, opts)); + + cl_git_pass(git_patch_from_diff(&patch, diff, 0)); + cl_git_pass(git_patch_to_buf(&actual, patch)); + + cl_assert_equal_s(expected, actual.ptr); + + git_buf_free(&actual); + git_patch_free(patch); + git_diff_free(diff); + git_tree_free(tree_one); + git_tree_free(tree_two); + git_commit_free(commit_one); + git_commit_free(commit_two); + git_index_free(index); +} + +void test_diff_binary__add_normal(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + const char *expected = + "diff --git a/binary.bin b/binary.bin\n" \ + "new file mode 100644\n" \ + "index 0000000..bd474b2\n" \ + "Binary files /dev/null and b/binary.bin differ\n"; + + repo = cl_git_sandbox_init("diff_format_email"); + test_patch( + "873806f6f27e631eb0b23e4b56bea2bfac14a373", + "897d3af16ca9e420cd071b1c4541bd2b91d04c8c", + &opts, + expected); +} + +void test_diff_binary__add(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + const char *expected = + "diff --git a/binary.bin b/binary.bin\n" \ + "new file mode 100644\n" \ + "index 0000000000000000000000000000000000000000..bd474b2519cc15eab801ff851cc7d50f0dee49a1\n" \ + "GIT binary patch\n" \ + "literal 3\n" \ + "Kc${Nk-~s>u4FC%O\n" + "\n" \ + "literal 0\n" \ + "Hc$@u4FC%O\n"; + + opts.flags = GIT_DIFF_SHOW_BINARY; + + repo = cl_git_sandbox_init("diff_format_email"); + test_patch( + "897d3af16ca9e420cd071b1c4541bd2b91d04c8c", + "8d7523f6fcb2404257889abe0d96f093d9f524f9", + &opts, + expected); +} + +void test_diff_binary__delete_normal(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + const char *expected = + "diff --git a/binary.bin b/binary.bin\n" \ + "deleted file mode 100644\n" \ + "index bd474b2..0000000\n" \ + "Binary files a/binary.bin and /dev/null differ\n"; + + repo = cl_git_sandbox_init("diff_format_email"); + test_patch( + "897d3af16ca9e420cd071b1c4541bd2b91d04c8c", + "873806f6f27e631eb0b23e4b56bea2bfac14a373", + &opts, + expected); +} + +void test_diff_binary__delete(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + const char *expected = + "diff --git a/binary.bin b/binary.bin\n" \ + "deleted file mode 100644\n" \ + "index bd474b2519cc15eab801ff851cc7d50f0dee49a1..0000000000000000000000000000000000000000\n" \ + "GIT binary patch\n" \ + "literal 0\n" \ + "Hc$@u4FC%O\n"; + + opts.flags = GIT_DIFF_SHOW_BINARY; + opts.id_abbrev = GIT_OID_HEXSZ; + + repo = cl_git_sandbox_init("diff_format_email"); + test_patch( + "897d3af16ca9e420cd071b1c4541bd2b91d04c8c", + "873806f6f27e631eb0b23e4b56bea2bfac14a373", + &opts, + expected); +} + +void test_diff_binary__delta(void) +{ + git_index *index; + git_buf contents = GIT_BUF_INIT; + size_t i; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + const char *expected = + "diff --git a/songof7cities.txt b/songof7cities.txt\n" \ + "index 4210ffd5c390b21dd5483375e75288dea9ede512..cc84ec183351c9944ed90a619ca08911924055b5 100644\n" \ + "GIT binary patch\n" \ + "delta 198\n" \ + "zc$}LmI8{(0BqLQJI6p64AwNwaIJGP_Pa)Ye#M3o+qJ$PQ;Y(X&QMK*C5^Br3bjG4d=XI^5@\n" \ + "JfH567LIG)KJdFSV\n" \ + "\n" \ + "delta 198\n" \ + "zc$}LmI8{(0BqLQJI6p64AwNwaIJGP_Pr*5}Br~;mqJ$PQ;Y(X&QMK*C5^Br3bjG4d=XI^5@\n" \ + "JfH567LIF3FM2!Fd\n"; + + opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY; + opts.id_abbrev = GIT_OID_HEXSZ; + + repo = cl_git_sandbox_init("renames"); + cl_git_pass(git_repository_index(&index, repo)); + + cl_git_pass(git_futils_readbuffer(&contents, "renames/songof7cities.txt")); + + for (i = 0; i < contents.size - 6; i++) { + if (strncmp(&contents.ptr[i], "Cities", 6) == 0) + memcpy(&contents.ptr[i], "cITIES", 6); + } + + cl_git_rewritefile("renames/songof7cities.txt", contents.ptr); + cl_git_pass(git_index_add_bypath(index, "songof7cities.txt")); + cl_git_pass(git_index_write(index)); + + test_patch( + "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13", + NULL, + &opts, + expected); + + git_index_free(index); + git_buf_free(&contents); +} + +void test_diff_binary__delta_append(void) +{ + git_index *index; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + const char *expected = + "diff --git a/untimely.txt b/untimely.txt\n" \ + "index 9a69d960ae94b060f56c2a8702545e2bb1abb935..1111d4f11f4b35bf6759e0fb714fe09731ef0840 100644\n" \ + "GIT binary patch\n" \ + "delta 32\n" \ + "nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \ + "\n" \ + "delta 7\n" \ + "Oc%18D`@*{63ljhg(E~C7\n"; + + opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY; + opts.id_abbrev = GIT_OID_HEXSZ; + + repo = cl_git_sandbox_init("renames"); + cl_git_pass(git_repository_index(&index, repo)); + + cl_git_append2file("renames/untimely.txt", "Oh that crazy Kipling!\r\n"); + cl_git_pass(git_index_add_bypath(index, "untimely.txt")); + cl_git_pass(git_index_write(index)); + + test_patch( + "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13", + NULL, + &opts, + expected); + + git_index_free(index); +} diff -Nru libgit2-0.20.0/tests/diff/blob.c libgit2-0.22.2/tests/diff/blob.c --- libgit2-0.20.0/tests/diff/blob.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/diff/blob.c 2015-03-24 16:10:45.000000000 +0000 @@ -26,7 +26,7 @@ g_repo = cl_git_sandbox_init("attr"); - cl_git_pass(git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION)); + cl_git_pass(git_diff_init_options(&opts, GIT_DIFF_OPTIONS_VERSION)); opts.context_lines = 1; memset(&expected, 0, sizeof(expected)); @@ -51,6 +51,20 @@ cl_git_sandbox_cleanup(); } +static void assert_one_modified( + int hunks, int lines, int ctxt, int adds, int dels, diff_expects *exp) +{ + cl_assert_equal_i(1, exp->files); + cl_assert_equal_i(1, exp->file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(0, exp->files_binary); + + cl_assert_equal_i(hunks, exp->hunks); + cl_assert_equal_i(lines, exp->lines); + cl_assert_equal_i(ctxt, exp->line_ctxt); + cl_assert_equal_i(adds, exp->line_adds); + cl_assert_equal_i(dels, exp->line_dels); +} + void test_diff_blob__can_compare_text_blobs(void) { git_blob *a, *b, *c; @@ -71,79 +85,81 @@ /* Doing the equivalent of a `git diff -U1` on these files */ /* diff on tests/resources/attr/root_test1 */ + memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( a, NULL, b, NULL, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + assert_one_modified(1, 6, 1, 5, 0, &expected); - cl_assert_equal_i(1, expected.files); - cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); - cl_assert_equal_i(0, expected.files_binary); - - cl_assert_equal_i(1, expected.hunks); - cl_assert_equal_i(6, expected.lines); - cl_assert_equal_i(1, expected.line_ctxt); - cl_assert_equal_i(5, expected.line_adds); - cl_assert_equal_i(0, expected.line_dels); + /* same diff but use direct buffers */ + memset(&expected, 0, sizeof(expected)); + cl_git_pass(git_diff_buffers( + git_blob_rawcontent(a), (size_t)git_blob_rawsize(a), NULL, + git_blob_rawcontent(b), (size_t)git_blob_rawsize(b), NULL, &opts, + diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + assert_one_modified(1, 6, 1, 5, 0, &expected); /* diff on tests/resources/attr/root_test2 */ memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( b, NULL, c, NULL, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); - - cl_assert_equal_i(1, expected.files); - cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); - cl_assert_equal_i(0, expected.files_binary); - - cl_assert_equal_i(1, expected.hunks); - cl_assert_equal_i(15, expected.lines); - cl_assert_equal_i(3, expected.line_ctxt); - cl_assert_equal_i(9, expected.line_adds); - cl_assert_equal_i(3, expected.line_dels); + assert_one_modified(1, 15, 3, 9, 3, &expected); /* diff on tests/resources/attr/root_test3 */ memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( a, NULL, c, NULL, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); - - cl_assert_equal_i(1, expected.files); - cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); - cl_assert_equal_i(0, expected.files_binary); - - cl_assert_equal_i(1, expected.hunks); - cl_assert_equal_i(13, expected.lines); - cl_assert_equal_i(0, expected.line_ctxt); - cl_assert_equal_i(12, expected.line_adds); - cl_assert_equal_i(1, expected.line_dels); + assert_one_modified(1, 13, 0, 12, 1, &expected); memset(&expected, 0, sizeof(expected)); cl_git_pass(git_diff_blobs( c, NULL, d, NULL, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); - - cl_assert_equal_i(1, expected.files); - cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); - cl_assert_equal_i(0, expected.files_binary); - - cl_assert_equal_i(2, expected.hunks); - cl_assert_equal_i(14, expected.lines); - cl_assert_equal_i(4, expected.line_ctxt); - cl_assert_equal_i(6, expected.line_adds); - cl_assert_equal_i(4, expected.line_dels); + assert_one_modified(2, 14, 4, 6, 4, &expected); git_blob_free(a); git_blob_free(b); git_blob_free(c); } +static void assert_patch_matches_blobs( + git_patch *p, git_blob *a, git_blob *b, + int hunks, int l0, int l1, int ctxt, int adds, int dels) +{ + const git_diff_delta *delta; + size_t tc, ta, td; + + cl_assert(p != NULL); + + delta = git_patch_get_delta(p); + cl_assert(delta != NULL); + + cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status); + cl_assert_equal_oid(git_blob_id(a), &delta->old_file.id); + cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size); + cl_assert_equal_oid(git_blob_id(b), &delta->new_file.id); + cl_assert_equal_sz(git_blob_rawsize(b), delta->new_file.size); + + cl_assert_equal_i(hunks, (int)git_patch_num_hunks(p)); + + if (hunks > 0) + cl_assert_equal_i(l0, git_patch_num_lines_in_hunk(p, 0)); + if (hunks > 1) + cl_assert_equal_i(l1, git_patch_num_lines_in_hunk(p, 1)); + + cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p)); + cl_assert_equal_i(ctxt, (int)tc); + cl_assert_equal_i(adds, (int)ta); + cl_assert_equal_i(dels, (int)td); +} + void test_diff_blob__can_compare_text_blobs_with_patch(void) { git_blob *a, *b, *c; git_oid a_oid, b_oid, c_oid; git_patch *p; - const git_diff_delta *delta; - size_t tc, ta, td; /* tests/resources/attr/root_test1 */ cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8)); @@ -161,92 +177,22 @@ /* diff on tests/resources/attr/root_test1 */ cl_git_pass(git_patch_from_blobs(&p, a, NULL, b, NULL, &opts)); - - cl_assert(p != NULL); - - delta = git_patch_get_delta(p); - cl_assert(delta != NULL); - cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status); - cl_assert(git_oid_equal(git_blob_id(a), &delta->old_file.oid)); - cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size); - cl_assert(git_oid_equal(git_blob_id(b), &delta->new_file.oid)); - cl_assert_equal_sz(git_blob_rawsize(b), delta->new_file.size); - - cl_assert_equal_i(1, (int)git_patch_num_hunks(p)); - cl_assert_equal_i(6, git_patch_num_lines_in_hunk(p, 0)); - - cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p)); - cl_assert_equal_i(1, (int)tc); - cl_assert_equal_i(5, (int)ta); - cl_assert_equal_i(0, (int)td); - + assert_patch_matches_blobs(p, a, b, 1, 6, 0, 1, 5, 0); git_patch_free(p); /* diff on tests/resources/attr/root_test2 */ cl_git_pass(git_patch_from_blobs(&p, b, NULL, c, NULL, &opts)); - - cl_assert(p != NULL); - - delta = git_patch_get_delta(p); - cl_assert(delta != NULL); - cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status); - cl_assert(git_oid_equal(git_blob_id(b), &delta->old_file.oid)); - cl_assert_equal_sz(git_blob_rawsize(b), delta->old_file.size); - cl_assert(git_oid_equal(git_blob_id(c), &delta->new_file.oid)); - cl_assert_equal_sz(git_blob_rawsize(c), delta->new_file.size); - - cl_assert_equal_i(1, (int)git_patch_num_hunks(p)); - cl_assert_equal_i(15, git_patch_num_lines_in_hunk(p, 0)); - - cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p)); - cl_assert_equal_i(3, (int)tc); - cl_assert_equal_i(9, (int)ta); - cl_assert_equal_i(3, (int)td); - + assert_patch_matches_blobs(p, b, c, 1, 15, 0, 3, 9, 3); git_patch_free(p); /* diff on tests/resources/attr/root_test3 */ cl_git_pass(git_patch_from_blobs(&p, a, NULL, c, NULL, &opts)); - - cl_assert(p != NULL); - - delta = git_patch_get_delta(p); - cl_assert(delta != NULL); - cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status); - cl_assert(git_oid_equal(git_blob_id(a), &delta->old_file.oid)); - cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size); - cl_assert(git_oid_equal(git_blob_id(c), &delta->new_file.oid)); - cl_assert_equal_sz(git_blob_rawsize(c), delta->new_file.size); - - cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p)); - cl_assert_equal_i(0, (int)tc); - cl_assert_equal_i(12, (int)ta); - cl_assert_equal_i(1, (int)td); - + assert_patch_matches_blobs(p, a, c, 1, 13, 0, 0, 12, 1); git_patch_free(p); /* one more */ cl_git_pass(git_patch_from_blobs(&p, c, NULL, d, NULL, &opts)); - - cl_assert(p != NULL); - - delta = git_patch_get_delta(p); - cl_assert(delta != NULL); - cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status); - cl_assert(git_oid_equal(git_blob_id(c), &delta->old_file.oid)); - cl_assert_equal_sz(git_blob_rawsize(c), delta->old_file.size); - cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid)); - cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size); - - cl_assert_equal_i(2, (int)git_patch_num_hunks(p)); - cl_assert_equal_i(5, git_patch_num_lines_in_hunk(p, 0)); - cl_assert_equal_i(9, git_patch_num_lines_in_hunk(p, 1)); - - cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p)); - cl_assert_equal_i(4, (int)tc); - cl_assert_equal_i(6, (int)ta); - cl_assert_equal_i(4, (int)td); - + assert_patch_matches_blobs(p, c, d, 2, 5, 9, 4, 6, 4); git_patch_free(p); git_blob_free(a); @@ -328,9 +274,9 @@ delta = git_patch_get_delta(p); cl_assert(delta != NULL); cl_assert_equal_i(GIT_DELTA_DELETED, delta->status); - cl_assert(git_oid_equal(git_blob_id(d), &delta->old_file.oid)); + cl_assert_equal_oid(git_blob_id(d), &delta->old_file.id); cl_assert_equal_sz(git_blob_rawsize(d), delta->old_file.size); - cl_assert(git_oid_iszero(&delta->new_file.oid)); + cl_assert(git_oid_iszero(&delta->new_file.id)); cl_assert_equal_sz(0, delta->new_file.size); cl_assert_equal_i(1, (int)git_patch_num_hunks(p)); @@ -353,9 +299,9 @@ delta = git_patch_get_delta(p); cl_assert(delta != NULL); cl_assert_equal_i(GIT_DELTA_ADDED, delta->status); - cl_assert(git_oid_iszero(&delta->old_file.oid)); + cl_assert(git_oid_iszero(&delta->old_file.id)); cl_assert_equal_sz(0, delta->old_file.size); - cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid)); + cl_assert_equal_oid(git_blob_id(d), &delta->new_file.id); cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size); cl_assert_equal_i(1, (int)git_patch_num_hunks(p)); @@ -446,9 +392,9 @@ cl_assert(delta != NULL); cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status); cl_assert_equal_sz(delta->old_file.size, git_blob_rawsize(d)); - cl_assert(git_oid_equal(git_blob_id(d), &delta->old_file.oid)); + cl_assert_equal_oid(git_blob_id(d), &delta->old_file.id); cl_assert_equal_sz(delta->new_file.size, git_blob_rawsize(d)); - cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid)); + cl_assert_equal_oid(git_blob_id(d), &delta->new_file.id); cl_assert_equal_i(0, (int)git_patch_num_hunks(p)); git_patch_free(p); @@ -460,9 +406,9 @@ cl_assert(delta != NULL); cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status); cl_assert_equal_sz(0, delta->old_file.size); - cl_assert(git_oid_iszero(&delta->old_file.oid)); + cl_assert(git_oid_iszero(&delta->old_file.id)); cl_assert_equal_sz(0, delta->new_file.size); - cl_assert(git_oid_iszero(&delta->new_file.oid)); + cl_assert(git_oid_iszero(&delta->new_file.id)); cl_assert_equal_i(0, (int)git_patch_num_hunks(p)); git_patch_free(p); @@ -656,14 +602,7 @@ /* diff from blob a to content of b */ quick_diff_blob_to_str(a, NULL, b_content, 0, NULL); - cl_assert_equal_i(1, expected.files); - cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); - cl_assert_equal_i(0, expected.files_binary); - cl_assert_equal_i(1, expected.hunks); - cl_assert_equal_i(6, expected.lines); - cl_assert_equal_i(1, expected.line_ctxt); - cl_assert_equal_i(5, expected.line_adds); - cl_assert_equal_i(0, expected.line_dels); + assert_one_modified(1, 6, 1, 5, 0, &expected); /* diff from blob a to content of a */ opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; @@ -853,7 +792,7 @@ size_t bin_len = 33; const char *changed; git_patch *p; - char *pout; + git_buf buf = GIT_BUF_INIT; /* set up custom diff drivers and 'diff' attribute mappings for them */ @@ -861,15 +800,15 @@ cl_git_pass(git_config_set_bool(cfg, "diff.iam_binary.binary", 1)); cl_git_pass(git_config_set_bool(cfg, "diff.iam_text.binary", 0)); cl_git_pass(git_config_set_string( - cfg, "diff.iam_alphactx.xfuncname", "^[A-Za-z]")); + cfg, "diff.iam_alphactx.xfuncname", "^[A-Za-z].*$")); cl_git_pass(git_config_set_bool(cfg, "diff.iam_textalpha.binary", 0)); cl_git_pass(git_config_set_string( - cfg, "diff.iam_textalpha.xfuncname", "^[A-Za-z]")); + cfg, "diff.iam_textalpha.xfuncname", "^[A-Za-z].*$")); cl_git_pass(git_config_set_string( - cfg, "diff.iam_numctx.funcname", "^[0-9]")); + cfg, "diff.iam_numctx.funcname", "^[0-9][0-9]*")); cl_git_pass(git_config_set_bool(cfg, "diff.iam_textnum.binary", 0)); cl_git_pass(git_config_set_string( - cfg, "diff.iam_textnum.funcname", "^[0-9]")); + cfg, "diff.iam_textnum.funcname", "^[0-9][0-9]*")); git_config_free(cfg); cl_git_append2file( @@ -910,14 +849,7 @@ changed = "Hello from the root\nMore lines\nAnd more\nGo here\n"; quick_diff_blob_to_str(nonbin, NULL, changed, 0, NULL); - cl_assert_equal_i(1, expected.files); - cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); - cl_assert_equal_i(0, expected.files_binary); - cl_assert_equal_i(1, expected.hunks); - cl_assert_equal_i(3, expected.lines); - cl_assert_equal_i(0, expected.line_ctxt); - cl_assert_equal_i(3, expected.line_adds); - cl_assert_equal_i(0, expected.line_dels); + assert_one_modified(1, 3, 0, 3, 0, &expected); quick_diff_blob_to_str(nonbin, "foo/bar.binary", changed, 0, NULL); cl_assert_equal_i(1, expected.files); @@ -925,33 +857,16 @@ cl_assert_equal_i(1, expected.files_binary); cl_assert_equal_i(0, expected.hunks); cl_assert_equal_i(0, expected.lines); - cl_assert_equal_i(0, expected.line_ctxt); - cl_assert_equal_i(0, expected.line_adds); - cl_assert_equal_i(0, expected.line_dels); quick_diff_blob_to_str(nonbin, "foo/bar.textary", changed, 0, NULL); - cl_assert_equal_i(1, expected.files); - cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); - cl_assert_equal_i(0, expected.files_binary); - cl_assert_equal_i(1, expected.hunks); - cl_assert_equal_i(3, expected.lines); - cl_assert_equal_i(0, expected.line_ctxt); - cl_assert_equal_i(3, expected.line_adds); - cl_assert_equal_i(0, expected.line_dels); + assert_one_modified(1, 3, 0, 3, 0, &expected); quick_diff_blob_to_str(nonbin, "foo/bar.alphary", changed, 0, NULL); - cl_assert_equal_i(1, expected.files); - cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); - cl_assert_equal_i(0, expected.files_binary); - cl_assert_equal_i(1, expected.hunks); - cl_assert_equal_i(3, expected.lines); - cl_assert_equal_i(0, expected.line_ctxt); - cl_assert_equal_i(3, expected.line_adds); - cl_assert_equal_i(0, expected.line_dels); + assert_one_modified(1, 3, 0, 3, 0, &expected); cl_git_pass(git_patch_from_blob_and_buffer( &p, nonbin, "zzz.normal", changed, strlen(changed), NULL, &opts)); - cl_git_pass(git_patch_to_str(&pout, p)); + cl_git_pass(git_patch_to_buf(&buf, p)); cl_assert_equal_s( "diff --git a/zzz.normal b/zzz.normal\n" "index 45141a7..75b0dbb 100644\n" @@ -960,23 +875,23 @@ "@@ -1,0 +2,3 @@ Hello from the root\n" "+More lines\n" "+And more\n" - "+Go here\n", pout); - git__free(pout); + "+Go here\n", buf.ptr); + git_buf_clear(&buf); git_patch_free(p); cl_git_pass(git_patch_from_blob_and_buffer( &p, nonbin, "zzz.binary", changed, strlen(changed), NULL, &opts)); - cl_git_pass(git_patch_to_str(&pout, p)); + cl_git_pass(git_patch_to_buf(&buf, p)); cl_assert_equal_s( "diff --git a/zzz.binary b/zzz.binary\n" "index 45141a7..75b0dbb 100644\n" - "Binary files a/zzz.binary and b/zzz.binary differ\n", pout); - git__free(pout); + "Binary files a/zzz.binary and b/zzz.binary differ\n", buf.ptr); + git_buf_clear(&buf); git_patch_free(p); cl_git_pass(git_patch_from_blob_and_buffer( &p, nonbin, "zzz.alphary", changed, strlen(changed), NULL, &opts)); - cl_git_pass(git_patch_to_str(&pout, p)); + cl_git_pass(git_patch_to_buf(&buf, p)); cl_assert_equal_s( "diff --git a/zzz.alphary b/zzz.alphary\n" "index 45141a7..75b0dbb 100644\n" @@ -985,13 +900,13 @@ "@@ -1,0 +2,3 @@ Hello from the root\n" "+More lines\n" "+And more\n" - "+Go here\n", pout); - git__free(pout); + "+Go here\n", buf.ptr); + git_buf_clear(&buf); git_patch_free(p); cl_git_pass(git_patch_from_blob_and_buffer( &p, nonbin, "zzz.numary", changed, strlen(changed), NULL, &opts)); - cl_git_pass(git_patch_to_str(&pout, p)); + cl_git_pass(git_patch_to_buf(&buf, p)); cl_assert_equal_s( "diff --git a/zzz.numary b/zzz.numary\n" "index 45141a7..75b0dbb 100644\n" @@ -1000,8 +915,8 @@ "@@ -1,0 +2,3 @@\n" "+More lines\n" "+And more\n" - "+Go here\n", pout); - git__free(pout); + "+Go here\n", buf.ptr); + git_buf_clear(&buf); git_patch_free(p); /* "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n" @@ -1012,17 +927,17 @@ cl_git_pass(git_patch_from_blob_and_buffer( &p, bin, "zzz.normal", changed, 37, NULL, &opts)); - cl_git_pass(git_patch_to_str(&pout, p)); + cl_git_pass(git_patch_to_buf(&buf, p)); cl_assert_equal_s( "diff --git a/zzz.normal b/zzz.normal\n" "index b435cd5..1604519 100644\n" - "Binary files a/zzz.normal and b/zzz.normal differ\n", pout); - git__free(pout); + "Binary files a/zzz.normal and b/zzz.normal differ\n", buf.ptr); + git_buf_clear(&buf); git_patch_free(p); cl_git_pass(git_patch_from_blob_and_buffer( &p, bin, "zzz.textary", changed, 37, NULL, &opts)); - cl_git_pass(git_patch_to_str(&pout, p)); + cl_git_pass(git_patch_to_buf(&buf, p)); cl_assert_equal_s( "diff --git a/zzz.textary b/zzz.textary\n" "index b435cd5..1604519 100644\n" @@ -1030,13 +945,13 @@ "+++ b/zzz.textary\n" "@@ -3 +3 @@\n" "-0123456789\n" - "+replace a line\n", pout); - git__free(pout); + "+replace a line\n", buf.ptr); + git_buf_clear(&buf); git_patch_free(p); cl_git_pass(git_patch_from_blob_and_buffer( &p, bin, "zzz.textalphary", changed, 37, NULL, &opts)); - cl_git_pass(git_patch_to_str(&pout, p)); + cl_git_pass(git_patch_to_buf(&buf, p)); cl_assert_equal_s( "diff --git a/zzz.textalphary b/zzz.textalphary\n" "index b435cd5..1604519 100644\n" @@ -1044,13 +959,13 @@ "+++ b/zzz.textalphary\n" "@@ -3 +3 @@\n" "-0123456789\n" - "+replace a line\n", pout); - git__free(pout); + "+replace a line\n", buf.ptr); + git_buf_clear(&buf); git_patch_free(p); cl_git_pass(git_patch_from_blob_and_buffer( &p, bin, "zzz.textnumary", changed, 37, NULL, &opts)); - cl_git_pass(git_patch_to_str(&pout, p)); + cl_git_pass(git_patch_to_buf(&buf, p)); cl_assert_equal_s( "diff --git a/zzz.textnumary b/zzz.textnumary\n" "index b435cd5..1604519 100644\n" @@ -1058,10 +973,36 @@ "+++ b/zzz.textnumary\n" "@@ -3 +3 @@ 0123456789\n" "-0123456789\n" - "+replace a line\n", pout); - git__free(pout); + "+replace a line\n", buf.ptr); + git_buf_clear(&buf); git_patch_free(p); + git_buf_free(&buf); git_blob_free(nonbin); git_blob_free(bin); } + +void test_diff_blob__can_compare_buffer_to_buffer(void) +{ + const char *a = "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\n"; + const char *b = "a\nB\nc\nd\nE\nF\nh\nj\nk\n"; + + opts.interhunk_lines = 0; + opts.context_lines = 0; + + memset(&expected, 0, sizeof(expected)); + + cl_git_pass(git_diff_buffers( + a, strlen(a), NULL, b, strlen(b), NULL, + &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + assert_one_modified(4, 9, 0, 4, 5, &expected); + + opts.flags ^= GIT_DIFF_REVERSE; + + memset(&expected, 0, sizeof(expected)); + + cl_git_pass(git_diff_buffers( + a, strlen(a), NULL, b, strlen(b), NULL, + &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); + assert_one_modified(4, 9, 0, 5, 4, &expected); +} diff -Nru libgit2-0.20.0/tests/diff/diff_helpers.c libgit2-0.22.2/tests/diff/diff_helpers.c --- libgit2-0.20.0/tests/diff/diff_helpers.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/diff/diff_helpers.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "diff_helpers.h" +#include "git2/sys/diff.h" git_tree *resolve_commit_oid_to_tree( git_repository *repo, @@ -11,12 +12,9 @@ git_tree *tree = NULL; if (git_oid_fromstrn(&oid, partial_oid, len) == 0) - git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJ_ANY); - cl_assert(obj); - if (git_object_type(obj) == GIT_OBJ_TREE) - return (git_tree *)obj; - cl_assert(git_object_type(obj) == GIT_OBJ_COMMIT); - cl_git_pass(git_commit_tree(&tree, (git_commit *)obj)); + cl_git_pass(git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJ_ANY)); + + cl_git_pass(git_object_peel((git_object **) &tree, obj, GIT_OBJ_TREE)); git_object_free(obj); return tree; } @@ -215,32 +213,16 @@ return GIT_EUSER; } -static int diff_print_cb( - const git_diff_delta *delta, - const git_diff_hunk *hunk, - const git_diff_line *line, - void *payload) -{ - FILE *fp = payload; - - GIT_UNUSED(delta); GIT_UNUSED(hunk); - - if (line->origin == GIT_DIFF_LINE_CONTEXT || - line->origin == GIT_DIFF_LINE_ADDITION || - line->origin == GIT_DIFF_LINE_DELETION) - fputc(line->origin, fp); - fwrite(line->content, 1, line->content_len, fp); - return 0; -} - void diff_print(FILE *fp, git_diff *diff) { - cl_git_pass(git_diff_print( - diff, GIT_DIFF_FORMAT_PATCH, diff_print_cb, fp ? fp : stderr)); + cl_git_pass( + git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, + git_diff_print_callback__to_file_handle, fp ? fp : stderr)); } void diff_print_raw(FILE *fp, git_diff *diff) { - cl_git_pass(git_diff_print( - diff, GIT_DIFF_FORMAT_RAW, diff_print_cb, fp ? fp : stderr)); + cl_git_pass( + git_diff_print(diff, GIT_DIFF_FORMAT_RAW, + git_diff_print_callback__to_file_handle, fp ? fp : stderr)); } diff -Nru libgit2-0.20.0/tests/diff/diffiter.c libgit2-0.22.2/tests/diff/diffiter.c --- libgit2-0.20.0/tests/diff/diffiter.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/diff/diffiter.c 2015-03-24 16:10:45.000000000 +0000 @@ -414,16 +414,16 @@ for (d = 0; d < num_d; ++d) { git_patch *patch; - char *text; + git_buf buf = GIT_BUF_INIT; cl_git_pass(git_patch_from_diff(&patch, diff, d)); cl_assert(patch != NULL); - cl_git_pass(git_patch_to_str(&text, patch)); + cl_git_pass(git_patch_to_buf(&buf, patch)); - cl_assert_equal_s(expected_patch_text[d], text); + cl_assert_equal_s(expected_patch_text[d], buf.ptr); - git__free(text); + git_buf_free(&buf); git_patch_free(patch); } diff -Nru libgit2-0.20.0/tests/diff/drivers.c libgit2-0.22.2/tests/diff/drivers.c --- libgit2-0.20.0/tests/diff/drivers.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/diff/drivers.c 2015-03-24 16:10:45.000000000 +0000 @@ -15,6 +15,23 @@ g_repo = NULL; } +static void overwrite_filemode(const char *expected, git_buf *actual) +{ + size_t offset; + char *found; + + found = strstr(expected, "100644"); + if (!found) + return; + + offset = ((const char *)found) - expected; + if (actual->size < offset + 6) + return; + + if (memcmp(&actual->ptr[offset], "100644", 6) != 0) + memcpy(&actual->ptr[offset], "100644", 6); +} + void test_diff_drivers__patterns(void) { git_config *cfg; @@ -22,7 +39,7 @@ git_tree *one; git_diff *diff; git_patch *patch; - char *text; + git_buf actual = GIT_BUF_INIT; const char *expected0 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Comes through the blood of the vanguards who\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n"; const char *expected1 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\nBinary files a/untimely.txt and b/untimely.txt differ\n"; const char *expected2 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Heaven delivers on earth the Hour that cannot be\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n"; @@ -45,10 +62,10 @@ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_str(&text, patch)); - cl_assert_equal_s(expected0, text); + cl_git_pass(git_patch_to_buf(&actual, patch)); + cl_assert_equal_s(expected0, actual.ptr); - git__free(text); + git_buf_free(&actual); git_patch_free(patch); git_diff_free(diff); @@ -60,10 +77,10 @@ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_str(&text, patch)); - cl_assert_equal_s(expected1, text); + cl_git_pass(git_patch_to_buf(&actual, patch)); + cl_assert_equal_s(expected1, actual.ptr); - git__free(text); + git_buf_free(&actual); git_patch_free(patch); git_diff_free(diff); @@ -75,10 +92,10 @@ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_str(&text, patch)); - cl_assert_equal_s(expected0, text); + cl_git_pass(git_patch_to_buf(&actual, patch)); + cl_assert_equal_s(expected0, actual.ptr); - git__free(text); + git_buf_free(&actual); git_patch_free(patch); git_diff_free(diff); @@ -92,10 +109,10 @@ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_str(&text, patch)); - cl_assert_equal_s(expected1, text); + cl_git_pass(git_patch_to_buf(&actual, patch)); + cl_assert_equal_s(expected1, actual.ptr); - git__free(text); + git_buf_free(&actual); git_patch_free(patch); git_diff_free(diff); @@ -106,17 +123,17 @@ cl_git_pass(git_repository_config(&cfg, g_repo)); cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 0)); - cl_git_pass(git_config_set_string(cfg, "diff.kipling0.xfuncname", "^H")); + cl_git_pass(git_config_set_string(cfg, "diff.kipling0.xfuncname", "^H.*$")); git_config_free(cfg); cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL)); cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_str(&text, patch)); - cl_assert_equal_s(expected2, text); + cl_git_pass(git_patch_to_buf(&actual, patch)); + cl_assert_equal_s(expected2, actual.ptr); - git__free(text); + git_buf_free(&actual); git_patch_free(patch); git_diff_free(diff); @@ -129,7 +146,7 @@ git_index *idx; git_diff *diff; git_patch *patch; - char *actual; + git_buf actual = GIT_BUF_INIT; const char *expected = "diff --git a/longlines.txt b/longlines.txt\nindex c1ce6ef..0134431 100644\n--- a/longlines.txt\n+++ b/longlines.txt\n@@ -3,3 +3,5 @@ Phasellus eget erat odio. Praesent at est iaculis, ultricies augue vel, dignissi\n Nam eget dolor fermentum, aliquet nisl at, convallis tellus. Pellentesque rhoncus erat enim, id porttitor elit euismod quis.\n Mauris sollicitudin magna odio, non egestas libero vehicula ut. Etiam et quam velit. Fusce eget libero rhoncus, ultricies felis sit amet, egestas purus.\n Aliquam in semper tellus. Pellentesque adipiscing rutrum velit, quis malesuada lacus consequat eget.\n+newline\n+newline\n"; g_repo = cl_git_sandbox_init("empty_standard_repo"); @@ -145,19 +162,91 @@ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL)); cl_assert_equal_sz(1, git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_str(&actual, patch)); + cl_git_pass(git_patch_to_buf(&actual, patch)); /* if chmod not supported, overwrite mode bits since anything is possible */ - if (!cl_is_chmod_supported()) { - size_t actual_len = strlen(actual); - if (actual_len > 72 && memcmp(&actual[66], "100644", 6) != 0) - memcpy(&actual[66], "100644", 6); - } + overwrite_filemode(expected, &actual); - cl_assert_equal_s(expected, actual); + cl_assert_equal_s(expected, actual.ptr); - free(actual); + git_buf_free(&actual); git_patch_free(patch); git_diff_free(diff); } +void test_diff_drivers__builtins(void) +{ + git_diff *diff; + git_patch *patch; + git_buf file = GIT_BUF_INIT, actual = GIT_BUF_INIT, expected = GIT_BUF_INIT; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_vector files = GIT_VECTOR_INIT; + size_t i; + char *path, *extension; + + g_repo = cl_git_sandbox_init("userdiff"); + + cl_git_pass(git_path_dirload("userdiff/files", 9, 0, 0, &files)); + + opts.interhunk_lines = 1; + opts.context_lines = 1; + opts.pathspec.count = 1; + + git_vector_foreach(&files, i, path) { + if (git__prefixcmp(path, "files/file.")) + continue; + extension = path + strlen("files/file."); + opts.pathspec.strings = &path; + + /* do diff with no special driver */ + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + cl_assert_equal_sz(1, git_diff_num_deltas(diff)); + cl_git_pass(git_patch_from_diff(&patch, diff, 0)); + cl_git_pass(git_patch_to_buf(&actual, patch)); + + git_buf_sets(&expected, "userdiff/expected/nodriver/diff."); + git_buf_puts(&expected, extension); + cl_git_pass(git_futils_readbuffer(&expected, expected.ptr)); + + overwrite_filemode(expected.ptr, &actual); + + cl_assert_equal_s(expected.ptr, actual.ptr); + + git_buf_clear(&actual); + git_patch_free(patch); + git_diff_free(diff); + + /* do diff with driver */ + + { + FILE *fp = fopen("userdiff/.gitattributes", "w"); + fprintf(fp, "*.%s diff=%s\n", extension, extension); + fclose(fp); + } + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + cl_assert_equal_sz(1, git_diff_num_deltas(diff)); + cl_git_pass(git_patch_from_diff(&patch, diff, 0)); + cl_git_pass(git_patch_to_buf(&actual, patch)); + + git_buf_sets(&expected, "userdiff/expected/driver/diff."); + git_buf_puts(&expected, extension); + cl_git_pass(git_futils_readbuffer(&expected, expected.ptr)); + + overwrite_filemode(expected.ptr, &actual); + + cl_assert_equal_s(expected.ptr, actual.ptr); + + git_buf_clear(&actual); + git_patch_free(patch); + git_diff_free(diff); + + git__free(path); + } + + git_buf_free(&file); + git_buf_free(&actual); + git_buf_free(&expected); + git_vector_free(&files); +} diff -Nru libgit2-0.20.0/tests/diff/format_email.c libgit2-0.22.2/tests/diff/format_email.c --- libgit2-0.20.0/tests/diff/format_email.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/diff/format_email.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,467 @@ +#include "clar.h" +#include "clar_libgit2.h" + +#include "buffer.h" +#include "commit.h" +#include "diff.h" + +static git_repository *repo; + +void test_diff_format_email__initialize(void) +{ + repo = cl_git_sandbox_init("diff_format_email"); +} + +void test_diff_format_email__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +static void assert_email_match( + const char *expected, + const char *oidstr, + git_diff_format_email_options *opts) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_buf buf = GIT_BUF_INIT; + + git_oid_fromstr(&oid, oidstr); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + + opts->id = git_commit_id(commit); + opts->author = git_commit_author(commit); + if (!opts->summary) + opts->summary = git_commit_summary(commit); + + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_pass(git_diff_format_email(&buf, diff, opts)); + + cl_assert_equal_s(expected, git_buf_cstr(&buf)); + git_buf_clear(&buf); + + cl_git_pass(git_diff_commit_as_email( + &buf, repo, commit, 1, 1, opts->flags, NULL)); + cl_assert_equal_s(expected, git_buf_cstr(&buf)); + + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_format_email__simple(void) +{ + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + const char *email = + "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys \n" \ + "Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \ + "Subject: [PATCH] Modify some content\n" \ + "\n" \ + "---\n" \ + " file1.txt | 8 +++++---\n" \ + " 1 file changed, 5 insertions(+), 3 deletions(-)\n" \ + "\n" \ + "diff --git a/file1.txt b/file1.txt\n" \ + "index 94aaae8..af8f41d 100644\n" \ + "--- a/file1.txt\n" \ + "+++ b/file1.txt\n" \ + "@@ -1,15 +1,17 @@\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "+_file1.txt_\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "+\n" \ + "+\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "+_file1.txt_\n" \ + "+_file1.txt_\n" \ + " file1.txt\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + + assert_email_match( + email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); +} + +void test_diff_format_email__multiple(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + git_buf buf = GIT_BUF_INIT; + + const char *email = + "From 10808fe9c9be5a190c0ba68d1a002233fb363508 Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys \n" \ + "Date: Thu, 10 Apr 2014 19:37:05 +0200\n" \ + "Subject: [PATCH 1/2] Added file2.txt file3.txt\n" \ + "\n" \ + "---\n" \ + " file2.txt | 5 +++++\n" \ + " file3.txt | 5 +++++\n" \ + " 2 files changed, 10 insertions(+), 0 deletions(-)\n" \ + " create mode 100644 file2.txt\n" \ + " create mode 100644 file3.txt\n" \ + "\n" \ + "diff --git a/file2.txt b/file2.txt\n" \ + "new file mode 100644\n" \ + "index 0000000..e909123\n" \ + "--- /dev/null\n" \ + "+++ b/file2.txt\n" \ + "@@ -0,0 +1,5 @@\n" \ + "+file2\n" \ + "+file2\n" \ + "+file2\n" \ + "+file2\n" \ + "+file2\n" \ + "diff --git a/file3.txt b/file3.txt\n" \ + "new file mode 100644\n" \ + "index 0000000..9435022\n" \ + "--- /dev/null\n" \ + "+++ b/file3.txt\n" \ + "@@ -0,0 +1,5 @@\n" \ + "+file3\n" \ + "+file3\n" \ + "+file3\n" \ + "+file3\n" \ + "+file3\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n" \ + "From 873806f6f27e631eb0b23e4b56bea2bfac14a373 Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys \n" \ + "Date: Thu, 10 Apr 2014 19:37:36 +0200\n" \ + "Subject: [PATCH 2/2] Modified file2.txt, file3.txt\n" \ + "\n" \ + "---\n" \ + " file2.txt | 2 +-\n" \ + " file3.txt | 2 +-\n" \ + " 2 files changed, 2 insertions(+), 2 deletions(-)\n" \ + "\n" \ + "diff --git a/file2.txt b/file2.txt\n" \ + "index e909123..7aff11d 100644\n" \ + "--- a/file2.txt\n" \ + "+++ b/file2.txt\n" \ + "@@ -1,5 +1,5 @@\n" \ + " file2\n" \ + " file2\n" \ + " file2\n" \ + "-file2\n" \ + "+file2!\n" \ + " file2\n" \ + "diff --git a/file3.txt b/file3.txt\n" \ + "index 9435022..9a2d780 100644\n" \ + "--- a/file3.txt\n" \ + "+++ b/file3.txt\n" \ + "@@ -1,5 +1,5 @@\n" \ + " file3\n" \ + "-file3\n" \ + "+file3!\n" \ + " file3\n" \ + " file3\n" \ + " file3\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + + + git_oid_fromstr(&oid, "10808fe9c9be5a190c0ba68d1a002233fb363508"); + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + + opts.id = git_commit_id(commit); + opts.author = git_commit_author(commit); + opts.summary = git_commit_summary(commit); + opts.patch_no = 1; + opts.total_patches = 2; + + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_pass(git_diff_format_email(&buf, diff, &opts)); + + git_diff_free(diff); + git_commit_free(commit); + diff = NULL; + commit = NULL; + + git_oid_fromstr(&oid, "873806f6f27e631eb0b23e4b56bea2bfac14a373"); + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + + opts.id = git_commit_id(commit); + opts.author = git_commit_author(commit); + opts.summary = git_commit_summary(commit); + opts.patch_no = 2; + opts.total_patches = 2; + + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_pass(git_diff_format_email(&buf, diff, &opts)); + + cl_assert_equal_s(email, git_buf_cstr(&buf)); + + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_format_email__exclude_marker(void) +{ + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + const char *email = + "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys \n" \ + "Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \ + "Subject: Modify some content\n" \ + "\n" \ + "---\n" \ + " file1.txt | 8 +++++---\n" \ + " 1 file changed, 5 insertions(+), 3 deletions(-)\n" \ + "\n" \ + "diff --git a/file1.txt b/file1.txt\n" \ + "index 94aaae8..af8f41d 100644\n" \ + "--- a/file1.txt\n" \ + "+++ b/file1.txt\n" \ + "@@ -1,15 +1,17 @@\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "+_file1.txt_\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "+\n" \ + "+\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "+_file1.txt_\n" \ + "+_file1.txt_\n" \ + " file1.txt\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + + opts.flags |= GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER; + + assert_email_match( + email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); +} + +void test_diff_format_email__invalid_no(void) +{ + git_oid oid; + git_commit *commit = NULL; + git_diff *diff = NULL; + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + git_buf buf = GIT_BUF_INIT; + + git_oid_fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92"); + + cl_git_pass(git_commit_lookup(&commit, repo, &oid)); + + opts.id = git_commit_id(commit); + opts.author = git_commit_author(commit); + opts.summary = git_commit_summary(commit); + opts.patch_no = 2; + opts.total_patches = 1; + + cl_git_pass(git_diff__commit(&diff, repo, commit, NULL)); + cl_git_fail(git_diff_format_email(&buf, diff, &opts)); + cl_git_fail(git_diff_commit_as_email(&buf, repo, commit, 2, 1, 0, NULL)); + cl_git_fail(git_diff_commit_as_email(&buf, repo, commit, 0, 0, 0, NULL)); + + git_diff_free(diff); + git_commit_free(commit); + git_buf_free(&buf); +} + +void test_diff_format_email__mode_change(void) +{ + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + const char *email = + "From 7ade76dd34bba4733cf9878079f9fd4a456a9189 Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys \n" \ + "Date: Thu, 10 Apr 2014 10:05:03 +0200\n" \ + "Subject: [PATCH] Update permissions\n" \ + "\n" \ + "---\n" \ + " file1.txt.renamed | 0\n" \ + " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \ + " mode change 100644 => 100755 file1.txt.renamed\n" \ + "\n" \ + "diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \ + "old mode 100644\n" \ + "new mode 100755\n" \ + "index a97157a..a97157a\n" \ + "--- a/file1.txt.renamed\n" \ + "+++ b/file1.txt.renamed\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + + assert_email_match( + email, "7ade76dd34bba4733cf9878079f9fd4a456a9189", &opts); +} + +void test_diff_format_email__rename_add_remove(void) +{ + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + const char *email = + "From 6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys \n" \ + "Date: Wed, 9 Apr 2014 21:15:56 +0200\n" \ + "Subject: [PATCH] Renamed file1.txt -> file1.txt.renamed\n" \ + "\n" \ + "---\n" \ + " file1.txt | 17 -----------------\n" \ + " file1.txt.renamed | 17 +++++++++++++++++\n" \ + " 2 files changed, 17 insertions(+), 17 deletions(-)\n" \ + " delete mode 100644 file1.txt\n" \ + " create mode 100644 file1.txt.renamed\n" \ + "\n" \ + "diff --git a/file1.txt b/file1.txt\n" \ + "deleted file mode 100644\n" \ + "index af8f41d..0000000\n" \ + "--- a/file1.txt\n" \ + "+++ /dev/null\n" \ + "@@ -1,17 +0,0 @@\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-_file1.txt_\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-\n" \ + "-\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-_file1.txt_\n" \ + "-_file1.txt_\n" \ + "-file1.txt\n" \ + "diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \ + "new file mode 100644\n" \ + "index 0000000..a97157a\n" \ + "--- /dev/null\n" \ + "+++ b/file1.txt.renamed\n" \ + "@@ -0,0 +1,17 @@\n" \ + "+file1.txt\n" \ + "+file1.txt\n" \ + "+_file1.txt_\n" \ + "+file1.txt\n" \ + "+file1.txt\n" \ + "+file1.txt_renamed\n" \ + "+file1.txt\n" \ + "+\n" \ + "+\n" \ + "+file1.txt\n" \ + "+file1.txt\n" \ + "+file1.txt_renamed\n" \ + "+file1.txt\n" \ + "+file1.txt\n" \ + "+_file1.txt_\n" \ + "+_file1.txt_\n" \ + "+file1.txt\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + + assert_email_match( + email, "6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d", &opts); +} + +void test_diff_format_email__multiline_summary(void) +{ + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + const char *email = + "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys \n" \ + "Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \ + "Subject: [PATCH] Modify some content\n" \ + "\n" \ + "---\n" \ + " file1.txt | 8 +++++---\n" \ + " 1 file changed, 5 insertions(+), 3 deletions(-)\n" \ + "\n" \ + "diff --git a/file1.txt b/file1.txt\n" \ + "index 94aaae8..af8f41d 100644\n" \ + "--- a/file1.txt\n" \ + "+++ b/file1.txt\n" \ + "@@ -1,15 +1,17 @@\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "+_file1.txt_\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "+\n" \ + "+\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + " file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "-file1.txt\n" \ + "+_file1.txt_\n" \ + "+_file1.txt_\n" \ + " file1.txt\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + + opts.summary = "Modify some content\nSome extra stuff here"; + + assert_email_match( + email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts); +} + +void test_diff_format_email__binary(void) +{ + git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT; + const char *email = + "From 8d7523f6fcb2404257889abe0d96f093d9f524f9 Mon Sep 17 00:00:00 2001\n" \ + "From: Jacques Germishuys \n" \ + "Date: Sun, 13 Apr 2014 18:10:18 +0200\n" \ + "Subject: [PATCH] Modified binary file\n" \ + "\n" \ + "---\n" \ + " binary.bin | Bin 3 -> 0 bytes\n" \ + " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \ + "\n" \ + "diff --git a/binary.bin b/binary.bin\n" \ + "index bd474b2..9ac35ff 100644\n" \ + "Binary files a/binary.bin and b/binary.bin differ\n" \ + "--\n" \ + "libgit2 " LIBGIT2_VERSION "\n" \ + "\n"; + /* TODO: Actually 0 bytes here should be 5!. Seems like we don't load the new content for binary files? */ + + opts.summary = "Modified binary file"; + + assert_email_match( + email, "8d7523f6fcb2404257889abe0d96f093d9f524f9", &opts); +} + diff -Nru libgit2-0.20.0/tests/diff/index.c libgit2-0.22.2/tests/diff/index.c --- libgit2-0.20.0/tests/diff/index.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/diff/index.c 2015-03-24 16:10:45.000000000 +0000 @@ -128,9 +128,7 @@ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts)); cl_assert_equal_i( - GIT_EUSER, - git_diff_foreach(diff, diff_stop_after_2_files, NULL, NULL, &exp) - ); + 1, git_diff_foreach(diff, diff_stop_after_2_files, NULL, NULL, &exp) ); cl_assert_equal_i(2, exp.files); diff -Nru libgit2-0.20.0/tests/diff/iterator.c libgit2-0.22.2/tests/diff/iterator.c --- libgit2-0.20.0/tests/diff/iterator.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/diff/iterator.c 2015-03-24 16:10:45.000000000 +0000 @@ -355,6 +355,7 @@ const char *sandbox, const char *start, const char *end, + git_iterator_flag_t flags, int expected_count, const char **expected_names, const char **expected_oids) @@ -362,11 +363,13 @@ git_index *index; git_iterator *i; const git_index_entry *entry; - int error, count = 0; + int error, count = 0, caps; git_repository *repo = cl_git_sandbox_init(sandbox); cl_git_pass(git_repository_index(&index, repo)); - cl_git_pass(git_iterator_for_index(&i, index, 0, start, end)); + caps = git_index_caps(index); + + cl_git_pass(git_iterator_for_index(&i, index, flags, start, end)); while (!(error = git_iterator_advance(&entry, i))) { cl_assert(entry); @@ -377,7 +380,7 @@ if (expected_oids != NULL) { git_oid oid; cl_git_pass(git_oid_fromstr(&oid, expected_oids[count])); - cl_assert_equal_i(git_oid_cmp(&oid, &entry->oid), 0); + cl_assert_equal_oid(&oid, &entry->id); } count++; @@ -388,6 +391,8 @@ cl_assert_equal_i(expected_count, count); git_iterator_free(i); + + cl_assert(caps == git_index_caps(index)); git_index_free(index); } @@ -446,7 +451,8 @@ void test_diff_iterator__index_0(void) { index_iterator_test( - "attr", NULL, NULL, 23, expected_index_0, expected_index_oids_0); + "attr", NULL, NULL, 0, ARRAY_SIZE(expected_index_0), + expected_index_0, expected_index_oids_0); } static const char *expected_index_range[] = { @@ -466,25 +472,26 @@ void test_diff_iterator__index_range(void) { index_iterator_test( - "attr", "root", "root", 4, expected_index_range, expected_index_oids_range); + "attr", "root", "root", 0, ARRAY_SIZE(expected_index_range), + expected_index_range, expected_index_oids_range); } void test_diff_iterator__index_range_empty_0(void) { index_iterator_test( - "attr", "empty", "empty", 0, NULL, NULL); + "attr", "empty", "empty", 0, 0, NULL, NULL); } void test_diff_iterator__index_range_empty_1(void) { index_iterator_test( - "attr", "z_empty_after", NULL, 0, NULL, NULL); + "attr", "z_empty_after", NULL, 0, 0, NULL, NULL); } void test_diff_iterator__index_range_empty_2(void) { index_iterator_test( - "attr", NULL, ".aaa_empty_before", 0, NULL, NULL); + "attr", NULL, ".aaa_empty_before", 0, 0, NULL, NULL); } static const char *expected_index_1[] = { @@ -522,9 +529,45 @@ void test_diff_iterator__index_1(void) { index_iterator_test( - "status", NULL, NULL, 13, expected_index_1, expected_index_oids_1); + "status", NULL, NULL, 0, ARRAY_SIZE(expected_index_1), + expected_index_1, expected_index_oids_1); } +static const char *expected_index_cs[] = { + "B", "D", "F", "H", "J", "L/1", "L/B", "L/D", "L/a", "L/c", + "a", "c", "e", "g", "i", "k/1", "k/B", "k/D", "k/a", "k/c", +}; + +static const char *expected_index_ci[] = { + "a", "B", "c", "D", "e", "F", "g", "H", "i", "J", + "k/1", "k/a", "k/B", "k/c", "k/D", "L/1", "L/a", "L/B", "L/c", "L/D", +}; + +void test_diff_iterator__index_case_folding(void) +{ + git_buf path = GIT_BUF_INIT; + int fs_is_ci = 0; + + cl_git_pass(git_buf_joinpath(&path, cl_fixture("icase"), ".gitted/CoNfIg")); + fs_is_ci = git_path_exists(path.ptr); + git_buf_free(&path); + + index_iterator_test( + "icase", NULL, NULL, 0, ARRAY_SIZE(expected_index_cs), + fs_is_ci ? expected_index_ci : expected_index_cs, NULL); + + cl_git_sandbox_cleanup(); + + index_iterator_test( + "icase", NULL, NULL, GIT_ITERATOR_IGNORE_CASE, + ARRAY_SIZE(expected_index_ci), expected_index_ci, NULL); + + cl_git_sandbox_cleanup(); + + index_iterator_test( + "icase", NULL, NULL, GIT_ITERATOR_DONT_IGNORE_CASE, + ARRAY_SIZE(expected_index_cs), expected_index_cs, NULL); +} /* -- WORKDIR ITERATOR TESTS -- */ @@ -543,7 +586,7 @@ git_repository *repo = cl_git_sandbox_init(sandbox); cl_git_pass(git_iterator_for_workdir( - &i, repo, GIT_ITERATOR_DONT_AUTOEXPAND, start, end)); + &i, repo, NULL, NULL, GIT_ITERATOR_DONT_AUTOEXPAND, start, end)); error = git_iterator_current(&entry, i); cl_assert((error == 0 && entry != NULL) || @@ -604,7 +647,7 @@ void test_diff_iterator__workdir_0(void) { - workdir_iterator_test("attr", NULL, NULL, 27, 1, NULL, "ign"); + workdir_iterator_test("attr", NULL, NULL, 23, 5, NULL, "ign"); } static const char *status_paths[] = { @@ -737,13 +780,13 @@ { "root_test2", false }, { "root_test3", false }, { "root_test4.txt", false }, - { "sub", false }, + { "sub/", false }, { "sub/.gitattributes", false }, { "sub/abc", false }, { "sub/dir/", true }, { "sub/file", false }, { "sub/ign/", true }, - { "sub/sub", false }, + { "sub/sub/", false }, { "sub/sub/.gitattributes", false }, { "sub/sub/dir", false }, /* file is not actually a dir */ { "sub/sub/file", false }, @@ -754,7 +797,7 @@ cl_git_mkfile("attr/sub/.git", "whatever"); cl_git_pass(git_iterator_for_workdir( - &i, repo, GIT_ITERATOR_DONT_AUTOEXPAND, "dir", "sub/sub/file")); + &i, repo, NULL, NULL, GIT_ITERATOR_DONT_AUTOEXPAND, "dir", "sub/sub/file")); cl_git_pass(git_iterator_current(&entry, i)); for (idx = 0; entry != NULL; ++idx) { @@ -789,7 +832,7 @@ static const char *expected[] = { "FIRST", "second", "THIRD", NULL }; cl_git_pass(git_iterator_for_workdir( - &i, repo, GIT_ITERATOR_IGNORE_CASE, start, end)); + &i, repo, NULL, NULL, GIT_ITERATOR_IGNORE_CASE, start, end)); cl_git_pass(git_iterator_current(&entry, i)); for (idx = 0; entry != NULL; ++idx) { diff -Nru libgit2-0.20.0/tests/diff/notify.c libgit2-0.22.2/tests/diff/notify.c --- libgit2-0.20.0/tests/diff/notify.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/diff/notify.c 2015-03-24 16:10:45.000000000 +0000 @@ -20,7 +20,7 @@ { bool found = false; notify_expected *exp = (notify_expected*)payload; - notify_expected *e;; + notify_expected *e; GIT_UNUSED(diff_so_far); @@ -182,10 +182,12 @@ opts.pathspec.count = 1; pathspec = "file_deleted"; - cl_git_fail(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + cl_git_fail_with( + git_diff_index_to_workdir(&diff, g_repo, NULL, &opts), -42); pathspec = "staged_changes_modified_file"; - cl_git_fail(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + cl_git_fail_with( + git_diff_index_to_workdir(&diff, g_repo, NULL, &opts), -42); } static int filter_all( diff -Nru libgit2-0.20.0/tests/diff/patch.c libgit2-0.22.2/tests/diff/patch.c --- libgit2-0.20.0/tests/diff/patch.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/diff/patch.c 2015-03-24 16:10:45.000000000 +0000 @@ -2,6 +2,7 @@ #include "git2/sys/repository.h" #include "diff_helpers.h" +#include "diff.h" #include "repository.h" #include "buf_text.h" @@ -30,8 +31,6 @@ const git_diff_line *line, void *payload) { - GIT_UNUSED(payload); - switch (line->origin) { case GIT_DIFF_LINE_FILE_HDR: cl_assert_equal_s(EXPECTED_HEADER, line->content); @@ -40,10 +39,12 @@ case GIT_DIFF_LINE_HUNK_HDR: cl_assert_equal_s(EXPECTED_HUNK, line->content); - /* Fall through */ + goto check_hunk; case GIT_DIFF_LINE_CONTEXT: case GIT_DIFF_LINE_DELETION: + if (payload != NULL) + return *(int *)payload; goto check_hunk; default: @@ -101,6 +102,39 @@ git_tree_free(one); } +void test_diff_patch__can_cancel_diff_print(void) +{ + const char *one_sha = "26a125e"; + const char *another_sha = "735b6a2"; + git_tree *one, *another; + git_diff *diff; + int fail_with; + + g_repo = cl_git_sandbox_init("status"); + + one = resolve_commit_oid_to_tree(g_repo, one_sha); + another = resolve_commit_oid_to_tree(g_repo, another_sha); + + cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL)); + + fail_with = -2323; + + cl_git_fail_with(git_diff_print( + diff, GIT_DIFF_FORMAT_PATCH, check_removal_cb, &fail_with), + fail_with); + + fail_with = 45; + + cl_git_fail_with(git_diff_print( + diff, GIT_DIFF_FORMAT_PATCH, check_removal_cb, &fail_with), + fail_with); + + git_diff_free(diff); + + git_tree_free(another); + git_tree_free(one); +} + void test_diff_patch__to_string(void) { const char *one_sha = "26a125e"; @@ -108,7 +142,7 @@ git_tree *one, *another; git_diff *diff; git_patch *patch; - char *text; + git_buf buf = GIT_BUF_INIT; const char *expected = "diff --git a/subdir.txt b/subdir.txt\ndeleted file mode 100644\nindex e8ee89e..0000000\n--- a/subdir.txt\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-Is it a bird?\n-Is it a plane?\n"; g_repo = cl_git_sandbox_init("status"); @@ -122,16 +156,16 @@ cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_str(&text, patch)); + cl_git_pass(git_patch_to_buf(&buf, patch)); - cl_assert_equal_s(expected, text); + cl_assert_equal_s(expected, buf.ptr); cl_assert_equal_sz(31, git_patch_size(patch, 0, 0, 0)); cl_assert_equal_sz(31, git_patch_size(patch, 1, 0, 0)); cl_assert_equal_sz(31 + 16, git_patch_size(patch, 1, 1, 0)); cl_assert_equal_sz(strlen(expected), git_patch_size(patch, 1, 1, 1)); - git__free(text); + git_buf_free(&buf); git_patch_free(patch); git_diff_free(diff); git_tree_free(another); @@ -145,7 +179,7 @@ git_config *cfg; git_diff *diff; git_patch *patch; - char *text; + git_buf buf = GIT_BUF_INIT; git_diff_options opts = GIT_DIFF_OPTIONS_INIT; char *onefile = "staged_changes_modified_file"; const char *expected1 = "diff --git c/staged_changes_modified_file i/staged_changes_modified_file\nindex 70bd944..906ee77 100644\n--- c/staged_changes_modified_file\n+++ i/staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n"; @@ -166,10 +200,10 @@ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_str(&text, patch)); - cl_assert_equal_s(expected1, text); + cl_git_pass(git_patch_to_buf(&buf, patch)); + cl_assert_equal_s(expected1, buf.ptr); - git__free(text); + git_buf_clear(&buf); git_patch_free(patch); git_diff_free(diff); @@ -177,10 +211,10 @@ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_str(&text, patch)); - cl_assert_equal_s(expected2, text); + cl_git_pass(git_patch_to_buf(&buf, patch)); + cl_assert_equal_s(expected2, buf.ptr); - git__free(text); + git_buf_clear(&buf); git_patch_free(patch); git_diff_free(diff); @@ -191,10 +225,10 @@ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_str(&text, patch)); - cl_assert_equal_s(expected3, text); + cl_git_pass(git_patch_to_buf(&buf, patch)); + cl_assert_equal_s(expected3, buf.ptr); - git__free(text); + git_buf_clear(&buf); git_patch_free(patch); git_diff_free(diff); @@ -205,13 +239,14 @@ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_str(&text, patch)); - cl_assert_equal_s(expected4, text); + cl_git_pass(git_patch_to_buf(&buf, patch)); + cl_assert_equal_s(expected4, buf.ptr); - git__free(text); + git_buf_clear(&buf); git_patch_free(patch); git_diff_free(diff); + git_buf_free(&buf); git_tree_free(one); git_config_free(cfg); } @@ -432,10 +467,10 @@ cl_assert_equal_sz(dels, actual_dels); if (expected != NULL) { - char *text; - cl_git_pass(git_patch_to_str(&text, patch)); - cl_assert_equal_s(expected, text); - git__free(text); + git_buf buf = GIT_BUF_INIT; + cl_git_pass(git_patch_to_buf(&buf, patch)); + cl_assert_equal_s(expected, buf.ptr); + git_buf_free(&buf); cl_assert_equal_sz( strlen(expected), git_patch_size(patch, 1, 1, 1)); diff -Nru libgit2-0.20.0/tests/diff/rename.c libgit2-0.22.2/tests/diff/rename.c --- libgit2-0.20.0/tests/diff/rename.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/diff/rename.c 2015-03-24 16:10:45.000000000 +0000 @@ -111,6 +111,28 @@ git_diff_free(diff); + cl_git_pass(git_diff_tree_to_tree( + &diff, g_repo, old_tree, new_tree, &diffopts)); + + /* git diff --find-copies-harder -M100 -B100 \ + * 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 \ + * 2bc7f351d20b53f1c72c16c4b036e491c478c49a + */ + opts.flags = GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED | + GIT_DIFF_FIND_EXACT_MATCH_ONLY; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + + cl_assert_equal_i(3, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); + + git_diff_free(diff); + git_tree_free(old_tree); git_tree_free(new_tree); } @@ -562,7 +584,7 @@ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; git_patch *patch; const git_diff_delta *delta; - char *text; + git_buf buf = GIT_BUF_INIT; const char *expected = "diff --git a/sixserving.txt b/ikeepsix.txt\nindex ad0a8e5..36020db 100644\n--- a/sixserving.txt\n+++ b/ikeepsix.txt\n@@ -1,3 +1,6 @@\n+I Keep Six Honest Serving-Men\n+=============================\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@@ -21,4 +24,4 @@ She sends'em abroad on her own affairs,\n One million Hows, two million Wheres,\n And seven million Whys!\n \n- -- Rudyard Kipling\n+ -- Rudyard Kipling\n"; old_tree = resolve_commit_oid_to_tree(g_repo, sha0); @@ -588,9 +610,9 @@ cl_assert((delta = git_patch_get_delta(patch)) != NULL); cl_assert_equal_i(GIT_DELTA_COPIED, (int)delta->status); - cl_git_pass(git_patch_to_str(&text, patch)); - cl_assert_equal_s(expected, text); - git__free(text); + cl_git_pass(git_patch_to_buf(&buf, patch)); + cl_assert_equal_s(expected, buf.ptr); + git_buf_free(&buf); git_patch_free(patch); @@ -907,7 +929,7 @@ git_reference *head, *selfsimilar; git_index *index; git_tree *tree; - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_diff *diff; git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT; @@ -919,10 +941,11 @@ char *ptr; opts.checkout_strategy = GIT_CHECKOUT_FORCE; + findopts.flags = GIT_DIFF_FIND_RENAMES; cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD")); cl_git_pass(git_reference_symbolic_set_target( - &selfsimilar, head, "refs/heads/renames_similar")); + &selfsimilar, head, "refs/heads/renames_similar", NULL, NULL)); cl_git_pass(git_checkout_head(g_repo, &opts)); cl_git_pass(git_repository_index(&index, g_repo)); @@ -993,7 +1016,7 @@ git_reference *head, *selfsimilar; git_index *index; git_tree *tree; - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_diff *diff; git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT; @@ -1003,10 +1026,11 @@ struct rename_expected expect = { 2, status, sources, targets }; opts.checkout_strategy = GIT_CHECKOUT_FORCE; + findopts.flags = GIT_DIFF_FIND_RENAMES; cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD")); cl_git_pass(git_reference_symbolic_set_target( - &selfsimilar, head, "refs/heads/renames_similar_two")); + &selfsimilar, head, "refs/heads/renames_similar_two", NULL, NULL)); cl_git_pass(git_checkout_head(g_repo, &opts)); cl_git_pass(git_repository_index(&index, g_repo)); @@ -1048,7 +1072,7 @@ git_reference *head, *selfsimilar; git_index *index; git_tree *tree; - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; git_diff *diff; git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT; @@ -1060,10 +1084,11 @@ struct rename_expected expect = { 2, status, sources, targets }; opts.checkout_strategy = GIT_CHECKOUT_FORCE; + findopts.flags = GIT_DIFF_FIND_RENAMES; cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD")); cl_git_pass(git_reference_symbolic_set_target( - &selfsimilar, head, "refs/heads/renames_similar_two")); + &selfsimilar, head, "refs/heads/renames_similar_two", NULL, NULL)); cl_git_pass(git_checkout_head(g_repo, &opts)); cl_git_pass(git_repository_index(&index, g_repo)); @@ -1284,3 +1309,280 @@ git_diff_free(diff); git_index_free(index); } + +void test_diff_rename__can_find_copy_to_split(void) +{ + git_buf c1 = GIT_BUF_INIT; + git_index *index; + git_tree *tree; + git_diff *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + diff_expects exp; + + cl_git_pass(git_futils_readbuffer(&c1, "renames/songof7cities.txt")); + cl_git_pass(git_futils_writebuffer(&c1, "renames/untimely.txt", 0, 0)); + + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_read_tree(index, tree)); + cl_git_pass(git_index_add_bypath(index, "untimely.txt")); + + diffopts.flags = GIT_DIFF_INCLUDE_UNMODIFIED; + + cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(4, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNMODIFIED]); + + opts.flags = GIT_DIFF_FIND_ALL; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(5, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNMODIFIED]); + + git_diff_free(diff); + git_tree_free(tree); + git_index_free(index); + + git_buf_free(&c1); +} + +void test_diff_rename__can_delete_unmodified_deltas(void) +{ + git_buf c1 = GIT_BUF_INIT; + git_index *index; + git_tree *tree; + git_diff *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + diff_expects exp; + + cl_git_pass(git_futils_readbuffer(&c1, "renames/songof7cities.txt")); + cl_git_pass(git_futils_writebuffer(&c1, "renames/untimely.txt", 0, 0)); + + cl_git_pass( + git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}")); + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_read_tree(index, tree)); + cl_git_pass(git_index_add_bypath(index, "untimely.txt")); + + diffopts.flags = GIT_DIFF_INCLUDE_UNMODIFIED; + + cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(4, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNMODIFIED]); + + opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_REMOVE_UNMODIFIED; + cl_git_pass(git_diff_find_similar(diff, &opts)); + + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(2, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]); + + git_diff_free(diff); + git_tree_free(tree); + git_index_free(index); + + git_buf_free(&c1); +} + +void test_diff_rename__matches_config_behavior(void) +{ + const char *sha0 = "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2"; + const char *sha1 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a"; + const char *sha2 = "1c068dee5790ef1580cfc4cd670915b48d790084"; + + git_tree *tree0, *tree1, *tree2; + git_config *cfg; + git_diff *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + diff_expects exp; + + opts.flags = GIT_DIFF_FIND_BY_CONFIG; + tree0 = resolve_commit_oid_to_tree(g_repo, sha0); + tree1 = resolve_commit_oid_to_tree(g_repo, sha1); + tree2 = resolve_commit_oid_to_tree(g_repo, sha2); + + diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; + cl_git_pass(git_repository_config(&cfg, g_repo)); + + /* diff.renames = false; no rename detection should happen */ + cl_git_pass(git_config_set_bool(cfg, "diff.renames", false)); + cl_git_pass(git_diff_tree_to_tree( + &diff, g_repo, tree0, tree1, &diffopts)); + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_find_similar(diff, &opts)); + cl_git_pass(git_diff_foreach(diff, + diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(4, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + git_diff_free(diff); + + /* diff.renames = true; should act like -M */ + cl_git_pass(git_config_set_bool(cfg, "diff.renames", true)); + cl_git_pass(git_diff_tree_to_tree( + &diff, g_repo, tree0, tree1, &diffopts)); + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_find_similar(diff, &opts)); + cl_git_pass(git_diff_foreach(diff, + diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(3, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); + git_diff_free(diff); + + /* diff.renames = copies; should act like -M -C */ + cl_git_pass(git_config_set_string(cfg, "diff.renames", "copies")); + cl_git_pass(git_diff_tree_to_tree( + &diff, g_repo, tree1, tree2, &diffopts)); + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_find_similar(diff, &opts)); + cl_git_pass(git_diff_foreach(diff, + diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(4, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]); + git_diff_free(diff); + + /* NULL find options is the same as GIT_DIFF_FIND_BY_CONFIG */ + cl_git_pass(git_diff_tree_to_tree( + &diff, g_repo, tree1, tree2, &diffopts)); + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_find_similar(diff, NULL)); + cl_git_pass(git_diff_foreach(diff, + diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(4, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]); + git_diff_free(diff); + + /* Cleanup */ + git_tree_free(tree0); + git_tree_free(tree1); + git_tree_free(tree2); + git_config_free(cfg); +} + +void test_diff_rename__can_override_thresholds_when_obeying_config(void) +{ + const char *sha1 = "2bc7f351d20b53f1c72c16c4b036e491c478c49a"; + const char *sha2 = "1c068dee5790ef1580cfc4cd670915b48d790084"; + + git_tree *tree1, *tree2; + git_config *cfg; + git_diff *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + diff_expects exp; + + tree1 = resolve_commit_oid_to_tree(g_repo, sha1); + tree2 = resolve_commit_oid_to_tree(g_repo, sha2); + + diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; + opts.flags = GIT_DIFF_FIND_BY_CONFIG; + + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_set_string(cfg, "diff.renames", "copies")); + git_config_free(cfg); + + /* copy threshold = 96%, should see creation of ikeepsix.txt */ + opts.copy_threshold = 96; + cl_git_pass(git_diff_tree_to_tree( + &diff, g_repo, tree1, tree2, &diffopts)); + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_find_similar(diff, &opts)); + cl_git_pass(git_diff_foreach(diff, + diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(4, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]); + git_diff_free(diff); + + /* copy threshold = 20%, should see sixserving.txt => ikeepsix.txt */ + opts.copy_threshold = 20; + cl_git_pass(git_diff_tree_to_tree( + &diff, g_repo, tree1, tree2, &diffopts)); + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_find_similar(diff, &opts)); + cl_git_pass(git_diff_foreach(diff, + diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(4, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]); + git_diff_free(diff); + + /* Cleanup */ + git_tree_free(tree1); + git_tree_free(tree2); +} + +void test_diff_rename__by_config_doesnt_mess_with_whitespace_settings(void) +{ + const char *sha1 = "1c068dee5790ef1580cfc4cd670915b48d790084"; + const char *sha2 = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13"; + + git_tree *tree1, *tree2; + git_config *cfg; + git_diff *diff; + git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; + git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT; + diff_expects exp; + + tree1 = resolve_commit_oid_to_tree(g_repo, sha1); + tree2 = resolve_commit_oid_to_tree(g_repo, sha2); + + diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; + opts.flags = GIT_DIFF_FIND_BY_CONFIG; + + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_set_string(cfg, "diff.renames", "copies")); + git_config_free(cfg); + + /* Don't ignore whitespace; this should find a change in sixserving.txt */ + opts.flags |= 0 | GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE; + cl_git_pass(git_diff_tree_to_tree( + &diff, g_repo, tree1, tree2, &diffopts)); + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_find_similar(diff, &opts)); + cl_git_pass(git_diff_foreach(diff, + diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(5, exp.files); + cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]); + git_diff_free(diff); + + /* Cleanup */ + git_tree_free(tree1); + git_tree_free(tree2); +} diff -Nru libgit2-0.20.0/tests/diff/stats.c libgit2-0.22.2/tests/diff/stats.c --- libgit2-0.20.0/tests/diff/stats.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/diff/stats.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,289 @@ +#include "clar.h" +#include "clar_libgit2.h" + +#include "buffer.h" +#include "commit.h" +#include "diff.h" + +static git_repository *_repo; +static git_diff_stats *_stats; + +void test_diff_stats__initialize(void) +{ + _repo = cl_git_sandbox_init("diff_format_email"); +} + +void test_diff_stats__cleanup(void) +{ + git_diff_stats_free(_stats); _stats = NULL; + cl_git_sandbox_cleanup(); +} + +static void diff_stats_from_commit_oid( + git_diff_stats **stats, const char *oidstr, bool rename) +{ + git_oid oid; + git_commit *commit; + git_diff *diff; + + git_oid_fromstr(&oid, oidstr); + cl_git_pass(git_commit_lookup(&commit, _repo, &oid)); + cl_git_pass(git_diff__commit(&diff, _repo, commit, NULL)); + if (rename) + cl_git_pass(git_diff_find_similar(diff, NULL)); + cl_git_pass(git_diff_get_stats(stats, diff)); + + git_diff_free(diff); + git_commit_free(commit); +} + +void test_diff_stats__stat(void) +{ + git_buf buf = GIT_BUF_INIT; + const char *stat = + " file1.txt | 8 +++++---\n" \ + " 1 file changed, 5 insertions(+), 3 deletions(-)\n"; + + diff_stats_from_commit_oid( + &_stats, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", false); + + cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(5, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(3, git_diff_stats_deletions(_stats)); + + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); + cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + git_buf_free(&buf); + + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 80)); + cl_assert(strcmp(git_buf_cstr(&buf), stat) == 0); + git_buf_free(&buf); +} + +void test_diff_stats__multiple_hunks(void) +{ + git_buf buf = GIT_BUF_INIT; + const char *stat = + " file2.txt | 5 +++--\n" \ + " file3.txt | 6 ++++--\n" \ + " 2 files changed, 7 insertions(+), 4 deletions(-)\n"; + + diff_stats_from_commit_oid( + &_stats, "cd471f0d8770371e1bc78bcbb38db4c7e4106bd2", false); + + cl_assert_equal_sz(2, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(7, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(4, git_diff_stats_deletions(_stats)); + + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); + git_buf_free(&buf); +} + +void test_diff_stats__numstat(void) +{ + git_buf buf = GIT_BUF_INIT; + const char *stat = + "3 2 file2.txt\n" + "4 2 file3.txt\n"; + + diff_stats_from_commit_oid( + &_stats, "cd471f0d8770371e1bc78bcbb38db4c7e4106bd2", false); + + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_NUMBER, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); + git_buf_free(&buf); +} + +void test_diff_stats__shortstat(void) +{ + git_buf buf = GIT_BUF_INIT; + const char *stat = + " 1 file changed, 5 insertions(+), 3 deletions(-)\n"; + + diff_stats_from_commit_oid( + &_stats, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", false); + + cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(5, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(3, git_diff_stats_deletions(_stats)); + + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_SHORT, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); + git_buf_free(&buf); +} + +void test_diff_stats__rename(void) +{ + git_buf buf = GIT_BUF_INIT; + const char *stat = + " file2.txt => file2.txt.renamed | 1 +\n" + " file3.txt => file3.txt.renamed | 4 +++-\n" + " 2 files changed, 4 insertions(+), 1 deletion(-)\n"; + + diff_stats_from_commit_oid( + &_stats, "8947a46e2097638ca6040ad4877246f4186ec3bd", true); + + cl_assert_equal_sz(2, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(4, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(1, git_diff_stats_deletions(_stats)); + + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); + git_buf_free(&buf); +} + +void test_diff_stats__rename_nochanges(void) +{ + git_buf buf = GIT_BUF_INIT; + const char *stat = + " file2.txt.renamed => file2.txt.renamed2 | 0\n" + " file3.txt.renamed => file3.txt.renamed2 | 0\n" + " 2 files changed, 0 insertions(+), 0 deletions(-)\n"; + + diff_stats_from_commit_oid( + &_stats, "3991dce9e71a0641ca49a6a4eea6c9e7ff402ed4", true); + + cl_assert_equal_sz(2, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(0, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(0, git_diff_stats_deletions(_stats)); + + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); + git_buf_free(&buf); +} + +void test_diff_stats__rename_and_modifiy(void) +{ + git_buf buf = GIT_BUF_INIT; + const char *stat = + " file2.txt.renamed2 | 2 +-\n" + " file3.txt.renamed2 => file3.txt.renamed | 0\n" + " 2 files changed, 1 insertion(+), 1 deletion(-)\n"; + + diff_stats_from_commit_oid( + &_stats, "4ca10087e696d2ba78d07b146a118e9a7096ed4f", true); + + cl_assert_equal_sz(2, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(1, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(1, git_diff_stats_deletions(_stats)); + + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); + git_buf_free(&buf); +} + +void test_diff_stats__rename_no_find(void) +{ + git_buf buf = GIT_BUF_INIT; + const char *stat = + " file2.txt | 5 -----\n" + " file2.txt.renamed | 6 ++++++\n" + " file3.txt | 5 -----\n" + " file3.txt.renamed | 7 +++++++\n" + " 4 files changed, 13 insertions(+), 10 deletions(-)\n"; + + diff_stats_from_commit_oid( + &_stats, "8947a46e2097638ca6040ad4877246f4186ec3bd", false); + + cl_assert_equal_sz(4, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(13, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(10, git_diff_stats_deletions(_stats)); + + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); + git_buf_free(&buf); +} + +void test_diff_stats__rename_nochanges_no_find(void) +{ + git_buf buf = GIT_BUF_INIT; + const char *stat = + " file2.txt.renamed | 6 ------\n" + " file2.txt.renamed2 | 6 ++++++\n" + " file3.txt.renamed | 7 -------\n" + " file3.txt.renamed2 | 7 +++++++\n" + " 4 files changed, 13 insertions(+), 13 deletions(-)\n"; + + diff_stats_from_commit_oid( + &_stats, "3991dce9e71a0641ca49a6a4eea6c9e7ff402ed4", false); + + cl_assert_equal_sz(4, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(13, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(13, git_diff_stats_deletions(_stats)); + + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); + git_buf_free(&buf); +} + +void test_diff_stats__rename_and_modifiy_no_find(void) +{ + git_buf buf = GIT_BUF_INIT; + const char *stat = + " file2.txt.renamed2 | 2 +-\n" + " file3.txt.renamed | 7 +++++++\n" + " file3.txt.renamed2 | 7 -------\n" + " 3 files changed, 8 insertions(+), 8 deletions(-)\n"; + + diff_stats_from_commit_oid( + &_stats, "4ca10087e696d2ba78d07b146a118e9a7096ed4f", false); + + cl_assert_equal_sz(3, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(8, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(8, git_diff_stats_deletions(_stats)); + + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); + git_buf_free(&buf); +} + +void test_diff_stats__binary(void) +{ + git_buf buf = GIT_BUF_INIT; + const char *stat = + " binary.bin | Bin 3 -> 0 bytes\n" + " 1 file changed, 0 insertions(+), 0 deletions(-)\n"; + /* TODO: Actually 0 bytes here should be 5!. Seems like we don't load the new content for binary files? */ + + diff_stats_from_commit_oid( + &_stats, "8d7523f6fcb2404257889abe0d96f093d9f524f9", false); + + cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats)); + cl_assert_equal_sz(0, git_diff_stats_insertions(_stats)); + cl_assert_equal_sz(0, git_diff_stats_deletions(_stats)); + + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); + git_buf_free(&buf); +} + +void test_diff_stats__binary_numstat(void) +{ + git_buf buf = GIT_BUF_INIT; + const char *stat = + "- - binary.bin\n"; + + diff_stats_from_commit_oid( + &_stats, "8d7523f6fcb2404257889abe0d96f093d9f524f9", false); + + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_NUMBER, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); + git_buf_free(&buf); +} + +void test_diff_stats__mode_change(void) +{ + git_buf buf = GIT_BUF_INIT; + const char *stat = + " file1.txt.renamed | 0\n" \ + " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \ + " mode change 100644 => 100755 file1.txt.renamed\n"; + + diff_stats_from_commit_oid( + &_stats, "7ade76dd34bba4733cf9878079f9fd4a456a9189", false); + + cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY, 0)); + cl_assert_equal_s(stat, git_buf_cstr(&buf)); + git_buf_free(&buf); +} diff -Nru libgit2-0.20.0/tests/diff/submodules.c libgit2-0.22.2/tests/diff/submodules.c --- libgit2-0.20.0/tests/diff/submodules.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/diff/submodules.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,6 +1,7 @@ #include "clar_libgit2.h" #include "repository.h" #include "posix.h" +#include "diff_helpers.h" #include "../submodule/submodule_helpers.h" static git_repository *g_repo = NULL; @@ -11,15 +12,18 @@ void test_diff_submodules__cleanup(void) { + cl_git_sandbox_cleanup(); } +#define get_buf_ptr(buf) ((buf)->asize ? (buf)->ptr : NULL) + static void check_diff_patches_at_line( git_diff *diff, const char **expected, const char *file, int line) { const git_diff_delta *delta; git_patch *patch = NULL; size_t d, num_d = git_diff_num_deltas(diff); - char *patch_text; + git_buf buf = GIT_BUF_INIT; for (d = 0; d < num_d; ++d, git_patch_free(patch)) { cl_git_pass(git_patch_from_diff(&patch, diff, d)); @@ -32,17 +36,21 @@ if (expected[d] && !strcmp(expected[d], "")) continue; + if (expected[d] && !strcmp(expected[d], "")) { + cl_assert_at_line(delta->status == GIT_DELTA_UNTRACKED, file, line); + continue; + } if (expected[d] && !strcmp(expected[d], "")) { - cl_git_pass(git_patch_to_str(&patch_text, patch)); + cl_git_pass(git_patch_to_buf(&buf, patch)); cl_assert_at_line(!strcmp(expected[d], ""), file, line); } - cl_git_pass(git_patch_to_str(&patch_text, patch)); + cl_git_pass(git_patch_to_buf(&buf, patch)); clar__assert_equal( file, line, "expected diff did not match actual diff", 1, - "%s", expected[d], patch_text); - git__free(patch_text); + "%s", expected[d], get_buf_ptr(&buf)); + git_buf_free(&buf); } cl_assert_at_line(expected[d] && !strcmp(expected[d], ""), file, line); @@ -113,7 +121,9 @@ git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff *diff = NULL, *diff2 = NULL; char *smpath = "testrepo"; - static const char *expected_none[] = { "" }; + static const char *expected_none[] = { + "" + }; static const char *expected_dirty[] = { "diff --git a/testrepo b/testrepo\nindex a65fedf..a65fedf 160000\n--- a/testrepo\n+++ b/testrepo\n@@ -1 +1 @@\n-Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750\n+Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750-dirty\n", /* testrepo.git */ "" @@ -121,8 +131,6 @@ g_repo = setup_fixture_submodules(); - cl_git_pass(git_submodule_reload_all(g_repo)); - opts.flags = GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_SHOW_UNTRACKED_CONTENT | GIT_DIFF_RECURSE_UNTRACKED_DIRS | @@ -155,8 +163,6 @@ git_diff_free(diff); - cl_git_pass(git_submodule_reload_all(g_repo)); - cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); check_diff_patches(diff, expected_dirty); git_diff_free(diff); @@ -168,8 +174,12 @@ git_diff *diff = NULL; static const char *expected[] = { "", /* .gitmodules */ + "", /* not-submodule */ + "", /* not */ "diff --git a/sm_changed_file b/sm_changed_file\nindex 4800958..4800958 160000\n--- a/sm_changed_file\n+++ b/sm_changed_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_file */ "diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */ + "", /* sm_changed_head- */ + "", /* sm_changed_head_ */ "diff --git a/sm_changed_index b/sm_changed_index\nindex 4800958..4800958 160000\n--- a/sm_changed_index\n+++ b/sm_changed_index\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_index */ "diff --git a/sm_changed_untracked_file b/sm_changed_untracked_file\nindex 4800958..4800958 160000\n--- a/sm_changed_untracked_file\n+++ b/sm_changed_untracked_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_untracked_file */ "diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */ @@ -178,6 +188,10 @@ g_repo = setup_fixture_submod2(); + /* bracket existing submodule with similarly named items */ + cl_git_mkfile("submod2/sm_changed_head-", "hello"); + cl_git_mkfile("submod2/sm_changed_head_", "hello"); + opts.flags = GIT_DIFF_INCLUDE_UNTRACKED; opts.old_prefix = "a"; opts.new_prefix = "b"; @@ -279,7 +293,8 @@ check_diff_patches(diff, expected_dirty); git_diff_free(diff); - cl_git_pass(git_submodule_reload_all(g_repo)); + git_submodule_free(sm); + cl_git_pass(git_submodule_lookup(&sm, g_repo, smpath)); cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); @@ -332,6 +347,8 @@ p_unlink("submod2/sm_changed_head/new_around_here"); + git_submodule_free(sm); + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); check_diff_patches(diff, expected_moved); git_diff_free(diff); @@ -347,6 +364,8 @@ git_config *cfg; static const char *expected_normal[] = { "", /* .gitmodules */ + "", /* not-submodule */ + "", /* not */ "diff --git a/sm_changed_file b/sm_changed_file\nindex 4800958..4800958 160000\n--- a/sm_changed_file\n+++ b/sm_changed_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_file */ "diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */ "diff --git a/sm_changed_index b/sm_changed_index\nindex 4800958..4800958 160000\n--- a/sm_changed_index\n+++ b/sm_changed_index\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_index */ @@ -356,10 +375,14 @@ }; static const char *expected_ignore_all[] = { "", /* .gitmodules */ + "", /* not-submodule */ + "", /* not */ "" }; static const char *expected_ignore_dirty[] = { "", /* .gitmodules */ + "", /* not-submodule */ + "", /* not */ "diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */ "diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */ "" @@ -421,3 +444,52 @@ git_config_free(cfg); } + +void test_diff_submodules__skips_empty_includes_used(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff *diff = NULL; + diff_expects exp; + + /* A side effect of of Git's handling of untracked directories and + * auto-ignoring of ".git" entries is that a newly initialized Git + * repo inside another repo will be skipped by diff, but one that + * actually has a commit it in will show as an untracked directory. + * Let's make sure that works. + */ + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + + opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED; + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(0, exp.files); + git_diff_free(diff); + + { + git_repository *r2; + cl_git_pass(git_repository_init(&r2, "empty_standard_repo/subrepo", 0)); + git_repository_free(r2); + } + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(1, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]); + git_diff_free(diff); + + cl_git_mkfile("empty_standard_repo/subrepo/README.txt", "hello\n"); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); + memset(&exp, 0, sizeof(exp)); + cl_git_pass(git_diff_foreach( + diff, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + cl_assert_equal_i(1, exp.files); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]); + git_diff_free(diff); +} diff -Nru libgit2-0.20.0/tests/diff/tree.c libgit2-0.22.2/tests/diff/tree.c --- libgit2-0.20.0/tests/diff/tree.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/diff/tree.c 2015-03-24 16:10:45.000000000 +0000 @@ -9,7 +9,7 @@ void test_diff_tree__initialize(void) { - cl_git_pass(git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION)); + cl_git_pass(git_diff_init_options(&opts, GIT_DIFF_OPTIONS_VERSION)); memset(&expect, 0, sizeof(expect)); diff -Nru libgit2-0.20.0/tests/diff/workdir.c libgit2-0.22.2/tests/diff/workdir.c --- libgit2-0.20.0/tests/diff/workdir.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/diff/workdir.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,13 +1,10 @@ #include "clar_libgit2.h" #include "diff_helpers.h" #include "repository.h" +#include "git2/sys/diff.h" static git_repository *g_repo = NULL; -void test_diff_workdir__initialize(void) -{ -} - void test_diff_workdir__cleanup(void) { cl_git_sandbox_cleanup(); @@ -60,6 +57,14 @@ cl_assert_equal_i(5, exp.line_dels); } + { + git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT; + cl_git_pass(git_diff_get_perfdata(&perf, diff)); + cl_assert_equal_sz( + 13 /* in root */ + 3 /* in subdir */, perf.stat_calls); + cl_assert_equal_sz(5, perf.oid_calculations); + } + git_diff_free(diff); } @@ -881,7 +886,7 @@ * only significant difference is that those Added items will show up * as Untracked items in the pure libgit2 diff. * - * Then add in the two extra ignored items "not" and "not-submodule" + * Then add in the two extra untracked items "not" and "not-submodule" * to get the 12 files reported here. */ @@ -890,8 +895,8 @@ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]); cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]); - cl_assert_equal_i(2, exp.file_status[GIT_DELTA_IGNORED]); - cl_assert_equal_i(8, exp.file_status[GIT_DELTA_UNTRACKED]); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(10, exp.file_status[GIT_DELTA_UNTRACKED]); /* the following numbers match "git diff 873585" exactly */ @@ -1375,7 +1380,7 @@ git_diff_options opts = GIT_DIFF_OPTIONS_INIT; git_diff *diff = NULL; git_patch *patch = NULL; - char *as_str = NULL; + git_buf buf = GIT_BUF_INIT; const char *expected_normal = "diff --git a/test.txt b/test.txt\nindex 34a5acc..d52725f 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,10 +1,7 @@\n When I wrote this\n I did not know\n-how to create\n-a patience diff\n I did not know\n how to create\n+a patience diff\n another problem\n-I did not know\n-how to create\n a minimal diff\n"; const char *expected_patience = "diff --git a/test.txt b/test.txt\nindex 34a5acc..d52725f 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,10 +1,7 @@\n When I wrote this\n I did not know\n+I did not know\n how to create\n a patience diff\n-I did not know\n-how to create\n another problem\n-I did not know\n-how to create\n a minimal diff\n"; @@ -1397,10 +1402,10 @@ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); cl_assert_equal_i(1, git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_str(&as_str, patch)); + cl_git_pass(git_patch_to_buf(&buf, patch)); - cl_assert_equal_s(expected_normal, as_str); - git__free(as_str); + cl_assert_equal_s(expected_normal, buf.ptr); + git_buf_clear(&buf); git_patch_free(patch); git_diff_free(diff); @@ -1409,10 +1414,12 @@ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts)); cl_assert_equal_i(1, git_diff_num_deltas(diff)); cl_git_pass(git_patch_from_diff(&patch, diff, 0)); - cl_git_pass(git_patch_to_str(&as_str, patch)); + cl_git_pass(git_patch_to_buf(&buf, patch)); + + cl_assert_equal_s(expected_patience, buf.ptr); + git_buf_clear(&buf); - cl_assert_equal_s(expected_patience, as_str); - git__free(as_str); + git_buf_free(&buf); git_patch_free(patch); git_diff_free(diff); } @@ -1488,3 +1495,202 @@ git_index_free(idx); } + +static int touch_file(void *payload, git_buf *path) +{ + int fd; + char b; + + GIT_UNUSED(payload); + if (git_path_isdir(path->ptr)) + return 0; + + cl_assert((fd = p_open(path->ptr, O_RDWR)) >= 0); + cl_assert_equal_i(1, p_read(fd, &b, 1)); + cl_must_pass(p_lseek(fd, 0, SEEK_SET)); + cl_must_pass(p_write(fd, &b, 1)); + cl_must_pass(p_close(fd)); + + return 0; +} + +static void basic_diff_status(git_diff **out, const git_diff_options *opts) +{ + diff_expects exp; + + cl_git_pass(git_diff_index_to_workdir(out, g_repo, NULL, opts)); + + memset(&exp, 0, sizeof(exp)); + + cl_git_pass(git_diff_foreach( + *out, diff_file_cb, diff_hunk_cb, diff_line_cb, &exp)); + + cl_assert_equal_i(13, exp.files); + cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]); + cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]); + cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]); + cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]); + cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]); +} + +void test_diff_workdir__can_update_index(void) +{ + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff *diff = NULL; + git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT; + + g_repo = cl_git_sandbox_init("status"); + + /* touch all the files so stat times are different */ + { + git_buf path = GIT_BUF_INIT; + cl_git_pass(git_buf_sets(&path, "status")); + cl_git_pass(git_path_direach(&path, 0, touch_file, NULL)); + git_buf_free(&path); + } + + opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED; + + basic_diff_status(&diff, &opts); + + cl_git_pass(git_diff_get_perfdata(&perf, diff)); + cl_assert_equal_sz(13 + 3, perf.stat_calls); + cl_assert_equal_sz(5, perf.oid_calculations); + + git_diff_free(diff); + + /* now allow diff to update stat cache */ + opts.flags |= GIT_DIFF_UPDATE_INDEX; + + basic_diff_status(&diff, &opts); + + cl_git_pass(git_diff_get_perfdata(&perf, diff)); + cl_assert_equal_sz(13 + 3, perf.stat_calls); + cl_assert_equal_sz(5, perf.oid_calculations); + + git_diff_free(diff); + + /* now if we do it again, we should see fewer OID calculations */ + + basic_diff_status(&diff, &opts); + + cl_git_pass(git_diff_get_perfdata(&perf, diff)); + cl_assert_equal_sz(13 + 3, perf.stat_calls); + cl_assert_equal_sz(0, perf.oid_calculations); + + git_diff_free(diff); +} + +#define STR7 "0123456" +#define STR8 "01234567" +#define STR40 STR8 STR8 STR8 STR8 STR8 +#define STR200 STR40 STR40 STR40 STR40 STR40 +#define STR999Z STR200 STR200 STR200 STR200 STR40 STR40 STR40 STR40 \ + STR8 STR8 STR8 STR8 STR7 "\0" +#define STR1000 STR200 STR200 STR200 STR200 STR200 +#define STR3999Z STR1000 STR1000 STR1000 STR999Z +#define STR4000 STR1000 STR1000 STR1000 STR1000 + +static void assert_delta_binary(git_diff *diff, size_t idx, int is_binary) +{ + git_patch *patch; + const git_diff_delta *delta; + + cl_git_pass(git_patch_from_diff(&patch, diff, idx)); + delta = git_patch_get_delta(patch); + cl_assert_equal_b((delta->flags & GIT_DIFF_FLAG_BINARY), is_binary); + git_patch_free(patch); +} + +void test_diff_workdir__binary_detection(void) +{ + git_index *idx; + git_diff *diff = NULL; + git_buf b = GIT_BUF_INIT; + int i; + git_buf data[10] = { + { "1234567890", 0, 0 }, /* 0 - all ascii text control */ + { "\xC3\x85\xC3\xBC\xE2\x80\xA0\x48\xC3\xB8\xCF\x80\xCE\xA9", 0, 0 }, /* 1 - UTF-8 multibyte text */ + { "\xEF\xBB\xBF\xC3\x9C\xE2\xA4\x92\xC6\x92\x38\xC2\xA3\xE2\x82\xAC", 0, 0 }, /* 2 - UTF-8 with BOM */ + { STR999Z, 0, 1000 }, /* 3 - ASCII with NUL at 1000 */ + { STR3999Z, 0, 4000 }, /* 4 - ASCII with NUL at 4000 */ + { STR4000 STR3999Z "x", 0, 8001 }, /* 5 - ASCII with NUL at 8000 */ + { STR4000 STR4000 "\0", 0, 8001 }, /* 6 - ASCII with NUL at 8001 */ + { "\x00\xDC\x00\x6E\x21\x39\xFE\x0E\x00\x63\x00\xF8" + "\x00\x64\x00\x65\x20\x48", 0, 18 }, /* 7 - UTF-16 text */ + { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d" + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d", + 0, 26 }, /* 8 - All non-printable characters (no NUL) */ + { "Hello \x01\x02\x03\x04\x05\x06 World!\x01\x02\x03\x04" + "\x05\x06\x07", 0, 26 }, /* 9 - 50-50 non-printable (no NUL) */ + }; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + cl_git_pass(git_repository_index(&idx, g_repo)); + + /* We start with ASCII in index and test data in workdir, + * then we will try with test data in index and ASCII in workdir. + */ + + cl_git_pass(git_buf_sets(&b, "empty_standard_repo/0")); + for (i = 0; i < 10; ++i) { + b.ptr[b.size - 1] = '0' + i; + cl_git_mkfile(b.ptr, "baseline"); + cl_git_pass(git_index_add_bypath(idx, &b.ptr[b.size - 1])); + + if (data[i].size == 0) + data[i].size = strlen(data[i].ptr); + cl_git_write2file( + b.ptr, data[i].ptr, data[i].size, O_WRONLY|O_TRUNC, 0664); + } + git_index_write(idx); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL)); + + cl_assert_equal_i(10, git_diff_num_deltas(diff)); + + /* using diff binary detection (i.e. looking for NUL byte) */ + assert_delta_binary(diff, 0, false); + assert_delta_binary(diff, 1, false); + assert_delta_binary(diff, 2, false); + assert_delta_binary(diff, 3, true); + assert_delta_binary(diff, 4, true); + assert_delta_binary(diff, 5, true); + assert_delta_binary(diff, 6, false); + assert_delta_binary(diff, 7, true); + assert_delta_binary(diff, 8, false); + assert_delta_binary(diff, 9, false); + /* The above have been checked to match command-line Git */ + + git_diff_free(diff); + + cl_git_pass(git_buf_sets(&b, "empty_standard_repo/0")); + for (i = 0; i < 10; ++i) { + b.ptr[b.size - 1] = '0' + i; + cl_git_pass(git_index_add_bypath(idx, &b.ptr[b.size - 1])); + + cl_git_write2file(b.ptr, "baseline\n", 9, O_WRONLY|O_TRUNC, 0664); + } + git_index_write(idx); + + cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL)); + + cl_assert_equal_i(10, git_diff_num_deltas(diff)); + + /* using diff binary detection (i.e. looking for NUL byte) */ + assert_delta_binary(diff, 0, false); + assert_delta_binary(diff, 1, false); + assert_delta_binary(diff, 2, false); + assert_delta_binary(diff, 3, true); + assert_delta_binary(diff, 4, true); + assert_delta_binary(diff, 5, true); + assert_delta_binary(diff, 6, false); + assert_delta_binary(diff, 7, true); + assert_delta_binary(diff, 8, false); + assert_delta_binary(diff, 9, false); + + git_diff_free(diff); + + git_index_free(idx); + git_buf_free(&b); +} diff -Nru libgit2-0.20.0/tests/fetchhead/fetchhead_data.h libgit2-0.22.2/tests/fetchhead/fetchhead_data.h --- libgit2-0.20.0/tests/fetchhead/fetchhead_data.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/fetchhead/fetchhead_data.h 2015-03-24 16:10:45.000000000 +0000 @@ -16,6 +16,11 @@ "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" \ "6e0c7bdb9b4ed93212491ee778ca1c65047cab4e\tnot-for-merge\ttag 'nearly-dangling' of git://github.com/libgit2/TestGitRepository\n" +#define FETCH_HEAD_WILDCARD_DATA2 \ + "49322bb17d3acc9146f98c97d078513228bbf3c0\t\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \ + "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \ + "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \ + #define FETCH_HEAD_NO_MERGE_DATA \ "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \ "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \ @@ -25,7 +30,19 @@ "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" \ "6e0c7bdb9b4ed93212491ee778ca1c65047cab4e\tnot-for-merge\ttag 'nearly-dangling' of git://github.com/libgit2/TestGitRepository\n" +#define FETCH_HEAD_NO_MERGE_DATA2 \ + "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \ + "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \ + "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \ + +#define FETCH_HEAD_NO_MERGE_DATA3 \ + "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \ + "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \ + "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \ + "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" \ #define FETCH_HEAD_EXPLICIT_DATA \ "0966a434eb1a025db6b71485ab63a3bfbea520b6\t\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" +#define FETCH_HEAD_QUOTE_DATA \ + "0966a434eb1a025db6b71485ab63a3bfbea520b6\t\tbranch 'first's-merge' of git://github.com/libgit2/TestGitRepository\n" diff -Nru libgit2-0.20.0/tests/fetchhead/nonetwork.c libgit2-0.22.2/tests/fetchhead/nonetwork.c --- libgit2-0.20.0/tests/fetchhead/nonetwork.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/fetchhead/nonetwork.c 2015-03-24 16:10:45.000000000 +0000 @@ -120,7 +120,7 @@ expected = git_vector_get(cb_data->fetchhead_vector, cb_data->idx); - cl_assert(git_oid_cmp(&expected->oid, oid) == 0); + cl_assert_equal_oid(&expected->oid, oid); cl_assert(expected->is_merge == is_merge); if (expected->ref_name) @@ -174,7 +174,7 @@ cl_assert(name == NULL); cl_assert(url == NULL); - cl_assert(git_oid_cmp(&expected, oid) == 0); + cl_assert_equal_oid(&expected, oid); cl_assert(is_merge == 1); return 0; @@ -201,7 +201,7 @@ cl_assert_equal_s("name", ref_name); cl_assert_equal_s("remote_url", remote_url); - cl_assert(git_oid_cmp(&expected, oid) == 0); + cl_assert_equal_oid(&expected, oid); cl_assert(is_merge == 0); return 0; @@ -228,7 +228,7 @@ cl_assert(ref_name == NULL); cl_assert_equal_s("remote_url", remote_url); - cl_assert(git_oid_cmp(&expected, oid) == 0); + cl_assert_equal_oid(&expected, oid); cl_assert(is_merge == 0); return 0; @@ -307,3 +307,48 @@ cl_assert(git__prefixcmp(giterr_last()->message, "Invalid description") == 0); } +static int assert_master_for_merge(const char *ref, const char *url, const git_oid *id, unsigned int is_merge, void *data) +{ + GIT_UNUSED(url); + GIT_UNUSED(id); + GIT_UNUSED(data); + + if (!strcmp("refs/heads/master", ref) && !is_merge) + return -1; + + return 0; +} + +void test_fetchhead_nonetwork__unborn_with_upstream(void) +{ + git_repository *repo; + git_remote *remote; + + /* Create an empty repo to clone from */ + cl_set_cleanup(&cleanup_repository, "./test1"); + cl_git_pass(git_repository_init(&g_repo, "./test1", 0)); + cl_set_cleanup(&cleanup_repository, "./repowithunborn"); + cl_git_pass(git_clone(&repo, "./test1", "./repowithunborn", NULL)); + + /* Simulate someone pushing to it by changing to one that has stuff */ + cl_git_pass(git_remote_lookup(&remote, repo, "origin")); + cl_git_pass(git_remote_set_url(remote, cl_fixture("testrepo.git"))); + cl_git_pass(git_remote_save(remote)); + + cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL)); + git_remote_free(remote); + + cl_git_pass(git_repository_fetchhead_foreach(repo, assert_master_for_merge, NULL)); + + git_repository_free(repo); + cl_fixture_cleanup("./repowithunborn"); +} + +void test_fetchhead_nonetwork__quote_in_branch_name(void) +{ + cl_set_cleanup(&cleanup_repository, "./test1"); + cl_git_pass(git_repository_init(&g_repo, "./test1", 0)); + + cl_git_rewritefile("./test1/.git/FETCH_HEAD", FETCH_HEAD_QUOTE_DATA); + cl_git_pass(git_repository_fetchhead_foreach(g_repo, read_noop, NULL)); +} diff -Nru libgit2-0.20.0/tests/filter/blob.c libgit2-0.22.2/tests/filter/blob.c --- libgit2-0.20.0/tests/filter/blob.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/filter/blob.c 2015-03-24 16:10:45.000000000 +0000 @@ -41,12 +41,45 @@ cl_git_pass(git_blob_filtered_content(&buf, blob, "file.lf", 1)); - cl_assert_equal_s(ALL_CRLF_TEXT_AS_LF, buf.ptr); + /* we never convert CRLF -> LF on platforms that have LF */ + cl_assert_equal_s(ALL_CRLF_TEXT_AS_CRLF, buf.ptr); git_buf_free(&buf); git_blob_free(blob); } +void test_filter_blob__sanitizes(void) +{ + git_blob *blob; + git_buf buf; + + cl_git_pass(git_revparse_single( + (git_object **)&blob, g_repo, "e69de29")); /* zero-byte */ + + cl_assert_equal_i(0, git_blob_rawsize(blob)); + cl_assert_equal_s("", git_blob_rawcontent(blob)); + + memset(&buf, 0, sizeof(git_buf)); + cl_git_pass(git_blob_filtered_content(&buf, blob, "file.bin", 1)); + cl_assert_equal_sz(0, buf.size); + cl_assert_equal_s("", buf.ptr); + git_buf_free(&buf); + + memset(&buf, 0, sizeof(git_buf)); + cl_git_pass(git_blob_filtered_content(&buf, blob, "file.crlf", 1)); + cl_assert_equal_sz(0, buf.size); + cl_assert_equal_s("", buf.ptr); + git_buf_free(&buf); + + memset(&buf, 0, sizeof(git_buf)); + cl_git_pass(git_blob_filtered_content(&buf, blob, "file.lf", 1)); + cl_assert_equal_sz(0, buf.size); + cl_assert_equal_s("", buf.ptr); + git_buf_free(&buf); + + git_blob_free(blob); +} + void test_filter_blob__ident(void) { git_oid id; diff -Nru libgit2-0.20.0/tests/filter/crlf.c libgit2-0.22.2/tests/filter/crlf.c --- libgit2-0.20.0/tests/filter/crlf.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/filter/crlf.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "git2/sys/filter.h" +#include "buffer.h" static git_repository *g_repo = NULL; @@ -24,7 +25,8 @@ git_filter *crlf; git_buf in = { 0 }, out = { 0 }; - cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_WORKTREE)); + cl_git_pass(git_filter_list_new( + &fl, g_repo, GIT_FILTER_TO_WORKTREE, 0)); crlf = git_filter_lookup(GIT_FILTER_CRLF); cl_assert(crlf != NULL); @@ -52,7 +54,8 @@ git_filter *crlf; git_buf in = { 0 }, out = { 0 }; - cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_ODB)); + cl_git_pass(git_filter_list_new( + &fl, g_repo, GIT_FILTER_TO_ODB, 0)); crlf = git_filter_lookup(GIT_FILTER_CRLF); cl_assert(crlf != NULL); @@ -68,4 +71,169 @@ git_filter_list_free(fl); git_buf_free(&out); +} + +void test_filter_crlf__with_safecrlf(void) +{ + git_filter_list *fl; + git_filter *crlf; + git_buf in = {0}, out = GIT_BUF_INIT; + + cl_repo_set_bool(g_repo, "core.safecrlf", true); + + cl_git_pass(git_filter_list_new( + &fl, g_repo, GIT_FILTER_TO_ODB, 0)); + + crlf = git_filter_lookup(GIT_FILTER_CRLF); + cl_assert(crlf != NULL); + + cl_git_pass(git_filter_list_push(fl, crlf, NULL)); + + /* Normalized \r\n succeeds with safecrlf */ + in.ptr = "Normal\r\nCRLF\r\nline-endings.\r\n"; + in.size = strlen(in.ptr); + + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + cl_assert_equal_s("Normal\nCRLF\nline-endings.\n", out.ptr); + + /* Mix of line endings fails with safecrlf */ + in.ptr = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n"; + in.size = strlen(in.ptr); + + cl_git_fail(git_filter_list_apply_to_data(&out, fl, &in)); + cl_assert_equal_i(giterr_last()->klass, GITERR_FILTER); + + /* Normalized \n is reversible, so does not fail with safecrlf */ + in.ptr = "Normal\nLF\nonly\nline-endings.\n"; + in.size = strlen(in.ptr); + + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + cl_assert_equal_s(in.ptr, out.ptr); + + git_filter_list_free(fl); + git_buf_free(&out); +} + +void test_filter_crlf__with_safecrlf_and_unsafe_allowed(void) +{ + git_filter_list *fl; + git_filter *crlf; + git_buf in = {0}, out = GIT_BUF_INIT; + + cl_repo_set_bool(g_repo, "core.safecrlf", true); + + cl_git_pass(git_filter_list_new( + &fl, g_repo, GIT_FILTER_TO_ODB, GIT_FILTER_OPT_ALLOW_UNSAFE)); + + crlf = git_filter_lookup(GIT_FILTER_CRLF); + cl_assert(crlf != NULL); + + cl_git_pass(git_filter_list_push(fl, crlf, NULL)); + + /* Normalized \r\n succeeds with safecrlf */ + in.ptr = "Normal\r\nCRLF\r\nline-endings.\r\n"; + in.size = strlen(in.ptr); + + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + cl_assert_equal_s("Normal\nCRLF\nline-endings.\n", out.ptr); + + /* Mix of line endings fails with safecrlf, but allowed to pass */ + in.ptr = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n"; + in.size = strlen(in.ptr); + + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + /* TODO: check for warning */ + cl_assert_equal_s("Mixed\nup\nLF\nand\nCRLF\nline-endings.\n", out.ptr); + + /* Normalized \n fails with safecrlf, but allowed to pass */ + in.ptr = "Normal\nLF\nonly\nline-endings.\n"; + in.size = strlen(in.ptr); + + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + /* TODO: check for warning */ + cl_assert_equal_s("Normal\nLF\nonly\nline-endings.\n", out.ptr); + + git_filter_list_free(fl); + git_buf_free(&out); +} + +void test_filter_crlf__no_safecrlf(void) +{ + git_filter_list *fl; + git_filter *crlf; + git_buf in = {0}, out = GIT_BUF_INIT; + + cl_git_pass(git_filter_list_new( + &fl, g_repo, GIT_FILTER_TO_ODB, 0)); + + crlf = git_filter_lookup(GIT_FILTER_CRLF); + cl_assert(crlf != NULL); + + cl_git_pass(git_filter_list_push(fl, crlf, NULL)); + + /* Normalized \r\n succeeds with safecrlf */ + in.ptr = "Normal\r\nCRLF\r\nline-endings.\r\n"; + in.size = strlen(in.ptr); + + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + cl_assert_equal_s("Normal\nCRLF\nline-endings.\n", out.ptr); + + /* Mix of line endings fails with safecrlf */ + in.ptr = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n"; + in.size = strlen(in.ptr); + + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + cl_assert_equal_s("Mixed\nup\nLF\nand\nCRLF\nline-endings.\n", out.ptr); + + /* Normalized \n fails with safecrlf */ + in.ptr = "Normal\nLF\nonly\nline-endings.\n"; + in.size = strlen(in.ptr); + + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + cl_assert_equal_s("Normal\nLF\nonly\nline-endings.\n", out.ptr); + + git_filter_list_free(fl); + git_buf_free(&out); +} + +void test_filter_crlf__safecrlf_warn(void) +{ + git_filter_list *fl; + git_filter *crlf; + git_buf in = {0}, out = GIT_BUF_INIT; + + cl_repo_set_string(g_repo, "core.safecrlf", "warn"); + + cl_git_pass(git_filter_list_new( + &fl, g_repo, GIT_FILTER_TO_ODB, 0)); + + crlf = git_filter_lookup(GIT_FILTER_CRLF); + cl_assert(crlf != NULL); + + cl_git_pass(git_filter_list_push(fl, crlf, NULL)); + + /* Normalized \r\n succeeds with safecrlf=warn */ + in.ptr = "Normal\r\nCRLF\r\nline-endings.\r\n"; + in.size = strlen(in.ptr); + + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + cl_assert_equal_s("Normal\nCRLF\nline-endings.\n", out.ptr); + + /* Mix of line endings succeeds with safecrlf=warn */ + in.ptr = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n"; + in.size = strlen(in.ptr); + + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + /* TODO: check for warning */ + cl_assert_equal_s("Mixed\nup\nLF\nand\nCRLF\nline-endings.\n", out.ptr); + + /* Normalized \n is reversible, so does not fail with safecrlf=warn */ + in.ptr = "Normal\nLF\nonly\nline-endings.\n"; + in.size = strlen(in.ptr); + + cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); + cl_assert_equal_s(in.ptr, out.ptr); + + git_filter_list_free(fl); + git_buf_free(&out); } diff -Nru libgit2-0.20.0/tests/filter/custom.c libgit2-0.22.2/tests/filter/custom.c --- libgit2-0.20.0/tests/filter/custom.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/filter/custom.c 2015-03-24 16:10:45.000000000 +0000 @@ -194,7 +194,7 @@ git_buf in = GIT_BUF_INIT_CONST(workdir_data, strlen(workdir_data)); cl_git_pass(git_filter_list_load( - &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_ODB)); + &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_ODB, 0)); cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); @@ -215,7 +215,7 @@ bitflipped_and_reversed_data, BITFLIPPED_AND_REVERSED_DATA_LEN); cl_git_pass(git_filter_list_load( - &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE)); + &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE, 0)); cl_git_pass(git_filter_list_apply_to_data(&out, fl, &in)); @@ -233,13 +233,13 @@ git_filter_list *fl; cl_git_pass(git_filter_list_load( - &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE)); + &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE, 0)); /* expect: bitflip, reverse, crlf */ cl_assert_equal_sz(3, git_filter_list_length(fl)); git_filter_list_free(fl); cl_git_pass(git_filter_list_load( - &fl, g_repo, NULL, "herocorp", GIT_FILTER_TO_WORKTREE)); + &fl, g_repo, NULL, "herocorp", GIT_FILTER_TO_WORKTREE, 0)); /* expect: bitflip, reverse - possibly crlf depending on global config */ { size_t flen = git_filter_list_length(fl); @@ -248,19 +248,20 @@ git_filter_list_free(fl); cl_git_pass(git_filter_list_load( - &fl, g_repo, NULL, "hero.bin", GIT_FILTER_TO_WORKTREE)); + &fl, g_repo, NULL, "hero.bin", GIT_FILTER_TO_WORKTREE, 0)); /* expect: bitflip, reverse */ cl_assert_equal_sz(2, git_filter_list_length(fl)); git_filter_list_free(fl); cl_git_pass(git_filter_list_load( - &fl, g_repo, NULL, "heroflip", GIT_FILTER_TO_WORKTREE)); + &fl, g_repo, NULL, "heroflip", GIT_FILTER_TO_WORKTREE, 0)); /* expect: bitflip (because of -reverse) */ cl_assert_equal_sz(1, git_filter_list_length(fl)); git_filter_list_free(fl); cl_git_pass(git_filter_list_load( - &fl, g_repo, NULL, "doesntapplytome.bin", GIT_FILTER_TO_WORKTREE)); + &fl, g_repo, NULL, "doesntapplytome.bin", + GIT_FILTER_TO_WORKTREE, 0)); /* expect: none */ cl_assert_equal_sz(0, git_filter_list_length(fl)); git_filter_list_free(fl); @@ -299,7 +300,7 @@ git_index_free(index); cl_git_pass(git_blob_lookup(&blob, g_repo, - & git_index_get_bypath(index, "hero.1.rev-ident", 0)->oid)); + & git_index_get_bypath(index, "hero.1.rev-ident", 0)->id)); cl_assert_equal_s( "\n!nuf evaH\n$dI$\ntset a si sihT", git_blob_rawcontent(blob)); cl_git_pass(git_blob_filtered_content(&buf, blob, "hero.1.rev-ident", 0)); @@ -310,7 +311,7 @@ git_blob_free(blob); cl_git_pass(git_blob_lookup(&blob, g_repo, - & git_index_get_bypath(index, "hero.2.rev-ident", 0)->oid)); + & git_index_get_bypath(index, "hero.2.rev-ident", 0)->id)); cl_assert_equal_s( "\n!yzarC\n$Id$\ntset rehtonA", git_blob_rawcontent(blob)); cl_git_pass(git_blob_filtered_content(&buf, blob, "hero.2.rev-ident", 0)); diff -Nru libgit2-0.20.0/tests/filter/ident.c libgit2-0.22.2/tests/filter/ident.c --- libgit2-0.20.0/tests/filter/ident.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/filter/ident.c 2015-03-24 16:10:45.000000000 +0000 @@ -39,7 +39,8 @@ git_filter_list *fl; git_filter *ident; - cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_WORKTREE)); + cl_git_pass(git_filter_list_new( + &fl, g_repo, GIT_FILTER_TO_WORKTREE, 0)); ident = git_filter_lookup(GIT_FILTER_IDENT); cl_assert(ident != NULL); @@ -78,7 +79,8 @@ git_filter_list *fl; git_filter *ident; - cl_git_pass(git_filter_list_new(&fl, g_repo, GIT_FILTER_TO_ODB)); + cl_git_pass(git_filter_list_new( + &fl, g_repo, GIT_FILTER_TO_ODB, 0)); ident = git_filter_lookup(GIT_FILTER_IDENT); cl_assert(ident != NULL); diff -Nru libgit2-0.20.0/tests/generate.py libgit2-0.22.2/tests/generate.py --- libgit2-0.20.0/tests/generate.py 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/generate.py 2015-03-24 16:10:45.000000000 +0000 @@ -80,7 +80,7 @@ return re.sub(SKIP_COMMENTS_REGEX, _replacer, text) def parse(self, contents): - TEST_FUNC_REGEX = r"^(void\s+(test_%s__(\w+))\(\s*void\s*\))\s*\{" + TEST_FUNC_REGEX = r"^(void\s+(test_%s__(\w+))\s*\(\s*void\s*\))\s*\{" contents = self._skip_comments(contents) regex = re.compile(TEST_FUNC_REGEX % self.name, re.MULTILINE) @@ -118,7 +118,7 @@ self.modified = True self.mtime = st.st_mtime - with open(path) as fp: + with codecs.open(path, encoding='utf-8') as fp: raw_content = fp.read() except IOError: @@ -150,7 +150,7 @@ for test_file in tests_in_module: full_path = os.path.join(root, test_file) - module_name = "_".join(module_root + [test_file[:-2]]) + module_name = "_".join(module_root + [test_file[:-2]]).replace("-", "_") modules.append((full_path, module_name)) @@ -223,14 +223,14 @@ data.write("static const size_t _clar_suite_count = %d;\n" % self.suite_count()) data.write("static const size_t _clar_callback_count = %d;\n" % self.callback_count()) - suite.save_cache() + self.save_cache() return True if __name__ == '__main__': from optparse import OptionParser parser = OptionParser() - parser.add_option('-f', '--force', dest='force', default=False) + parser.add_option('-f', '--force', action="store_true", dest='force', default=False) parser.add_option('-x', '--exclude', dest='excluded', action='append', default=[]) options, args = parser.parse_args() diff -Nru libgit2-0.20.0/tests/graph/descendant_of.c libgit2-0.22.2/tests/graph/descendant_of.c --- libgit2-0.20.0/tests/graph/descendant_of.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/graph/descendant_of.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,55 @@ +#include "clar_libgit2.h" + +static git_repository *_repo; +static git_commit *commit; + +void test_graph_descendant_of__initialize(void) +{ + git_oid oid; + + cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git"))); + + git_oid_fromstr(&oid, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + cl_git_pass(git_commit_lookup(&commit, _repo, &oid)); +} + +void test_graph_descendant_of__cleanup(void) +{ + git_commit_free(commit); + commit = NULL; + + git_repository_free(_repo); + _repo = NULL; +} + +void test_graph_descendant_of__returns_correct_result(void) +{ + git_commit *other; + + cl_assert_equal_i(0, git_graph_descendant_of(_repo, git_commit_id(commit), git_commit_id(commit))); + + + cl_git_pass(git_commit_nth_gen_ancestor(&other, commit, 1)); + + cl_assert_equal_i(1, git_graph_descendant_of(_repo, git_commit_id(commit), git_commit_id(other))); + cl_assert_equal_i(0, git_graph_descendant_of(_repo, git_commit_id(other), git_commit_id(commit))); + + git_commit_free(other); + + + cl_git_pass(git_commit_nth_gen_ancestor(&other, commit, 3)); + + cl_assert_equal_i(1, git_graph_descendant_of(_repo, git_commit_id(commit), git_commit_id(other))); + cl_assert_equal_i(0, git_graph_descendant_of(_repo, git_commit_id(other), git_commit_id(commit))); + + git_commit_free(other); + +} + +void test_graph_descendant_of__nopath(void) +{ + git_oid oid; + + git_oid_fromstr(&oid, "e90810b8df3e80c413d903f631643c716887138d"); + cl_assert_equal_i(0, git_graph_descendant_of(_repo, git_commit_id(commit), &oid)); +} diff -Nru libgit2-0.20.0/tests/index/addall.c libgit2-0.22.2/tests/index/addall.c --- libgit2-0.20.0/tests/index/addall.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/index/addall.c 2015-03-24 16:10:45.000000000 +0000 @@ -3,7 +3,8 @@ #include "posix.h" #include "fileops.h" -git_repository *g_repo = NULL; +static git_repository *g_repo = NULL; +#define TEST_DIR "addall" void test_index_addall__initialize(void) { @@ -13,6 +14,8 @@ { git_repository_free(g_repo); g_repo = NULL; + + cl_fixture_cleanup(TEST_DIR); } #define STATUS_INDEX_FLAGS \ @@ -132,6 +135,25 @@ } } +static void addall_create_test_repo(bool check_every_step) +{ + cl_git_pass(git_repository_init(&g_repo, TEST_DIR, false)); + if (check_every_step) + check_status(g_repo, 0, 0, 0, 0, 0, 0, 0); + + cl_git_mkfile(TEST_DIR "/file.foo", "a file"); + if (check_every_step) + check_status(g_repo, 0, 0, 0, 1, 0, 0, 0); + + cl_git_mkfile(TEST_DIR "/.gitignore", "*.foo\n"); + if (check_every_step) + check_status(g_repo, 0, 0, 0, 1, 0, 0, 1); + + cl_git_mkfile(TEST_DIR "/file.bar", "another file"); + if (check_every_step) + check_status(g_repo, 0, 0, 0, 2, 0, 0, 1); +} + void test_index_addall__repo_lifecycle(void) { int error; @@ -139,43 +161,33 @@ git_strarray paths = { NULL, 0 }; char *strs[1]; - cl_git_pass(git_repository_init(&g_repo, "addall", false)); - check_status(g_repo, 0, 0, 0, 0, 0, 0, 0); + addall_create_test_repo(true); cl_git_pass(git_repository_index(&index, g_repo)); - cl_git_mkfile("addall/file.foo", "a file"); - check_status(g_repo, 0, 0, 0, 1, 0, 0, 0); - - cl_git_mkfile("addall/.gitignore", "*.foo\n"); - check_status(g_repo, 0, 0, 0, 1, 0, 0, 1); - - cl_git_mkfile("addall/file.bar", "another file"); - check_status(g_repo, 0, 0, 0, 2, 0, 0, 1); - strs[0] = "file.*"; paths.strings = strs; paths.count = 1; cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); - check_stat_data(index, "addall/file.bar", true); + check_stat_data(index, TEST_DIR "/file.bar", true); check_status(g_repo, 1, 0, 0, 1, 0, 0, 1); - cl_git_rewritefile("addall/file.bar", "new content for file"); - check_stat_data(index, "addall/file.bar", false); + cl_git_rewritefile(TEST_DIR "/file.bar", "new content for file"); + check_stat_data(index, TEST_DIR "/file.bar", false); check_status(g_repo, 1, 0, 0, 1, 0, 1, 1); - cl_git_mkfile("addall/file.zzz", "yet another one"); - cl_git_mkfile("addall/other.zzz", "yet another one"); - cl_git_mkfile("addall/more.zzz", "yet another one"); + cl_git_mkfile(TEST_DIR "/file.zzz", "yet another one"); + cl_git_mkfile(TEST_DIR "/other.zzz", "yet another one"); + cl_git_mkfile(TEST_DIR "/more.zzz", "yet another one"); check_status(g_repo, 1, 0, 0, 4, 0, 1, 1); cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); - check_stat_data(index, "addall/file.bar", true); + check_stat_data(index, TEST_DIR "/file.bar", true); check_status(g_repo, 1, 0, 0, 4, 0, 0, 1); cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); - check_stat_data(index, "addall/file.zzz", true); + check_stat_data(index, TEST_DIR "/file.zzz", true); check_status(g_repo, 2, 0, 0, 3, 0, 0, 1); cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "first commit"); @@ -195,27 +207,27 @@ /* add with force - should allow */ cl_git_pass(git_index_add_all( index, &paths, GIT_INDEX_ADD_FORCE, NULL, NULL)); - check_stat_data(index, "addall/file.foo", true); + check_stat_data(index, TEST_DIR "/file.foo", true); check_status(g_repo, 1, 0, 0, 3, 0, 0, 0); /* now it's in the index, so regular add should work */ - cl_git_rewritefile("addall/file.foo", "new content for file"); - check_stat_data(index, "addall/file.foo", false); + cl_git_rewritefile(TEST_DIR "/file.foo", "new content for file"); + check_stat_data(index, TEST_DIR "/file.foo", false); check_status(g_repo, 1, 0, 0, 3, 0, 1, 0); cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL)); - check_stat_data(index, "addall/file.foo", true); + check_stat_data(index, TEST_DIR "/file.foo", true); check_status(g_repo, 1, 0, 0, 3, 0, 0, 0); cl_git_pass(git_index_add_bypath(index, "more.zzz")); - check_stat_data(index, "addall/more.zzz", true); + check_stat_data(index, TEST_DIR "/more.zzz", true); check_status(g_repo, 2, 0, 0, 2, 0, 0, 0); - cl_git_rewritefile("addall/file.zzz", "new content for file"); + cl_git_rewritefile(TEST_DIR "/file.zzz", "new content for file"); check_status(g_repo, 2, 0, 0, 2, 0, 1, 0); cl_git_pass(git_index_add_bypath(index, "file.zzz")); - check_stat_data(index, "addall/file.zzz", true); + check_stat_data(index, TEST_DIR "/file.zzz", true); check_status(g_repo, 2, 0, 1, 2, 0, 0, 0); strs[0] = "*.zzz"; @@ -228,7 +240,7 @@ cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "second commit"); check_status(g_repo, 0, 0, 0, 3, 0, 0, 0); - cl_must_pass(p_unlink("addall/file.zzz")); + cl_must_pass(p_unlink(TEST_DIR "/file.zzz")); check_status(g_repo, 0, 0, 0, 3, 1, 0, 0); /* update_all should be able to remove entries */ @@ -240,9 +252,9 @@ check_status(g_repo, 3, 1, 0, 0, 0, 0, 0); /* must be able to remove at any position while still updating other files */ - cl_must_pass(p_unlink("addall/.gitignore")); - cl_git_rewritefile("addall/file.zzz", "reconstructed file"); - cl_git_rewritefile("addall/more.zzz", "altered file reality"); + cl_must_pass(p_unlink(TEST_DIR "/.gitignore")); + cl_git_rewritefile(TEST_DIR "/file.zzz", "reconstructed file"); + cl_git_rewritefile(TEST_DIR "/more.zzz", "altered file reality"); check_status(g_repo, 3, 1, 0, 1, 1, 1, 0); cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); @@ -256,3 +268,89 @@ git_index_free(index); } + +static int addall_match_prefix( + const char *path, const char *matched_pathspec, void *payload) +{ + GIT_UNUSED(matched_pathspec); + return !git__prefixcmp(path, payload) ? 0 : 1; +} + +static int addall_match_suffix( + const char *path, const char *matched_pathspec, void *payload) +{ + GIT_UNUSED(matched_pathspec); + return !git__suffixcmp(path, payload) ? 0 : 1; +} + +static int addall_cancel_at( + const char *path, const char *matched_pathspec, void *payload) +{ + GIT_UNUSED(matched_pathspec); + return !strcmp(path, payload) ? -123 : 0; +} + +void test_index_addall__callback_filtering(void) +{ + git_index *index; + + addall_create_test_repo(false); + + cl_git_pass(git_repository_index(&index, g_repo)); + + cl_git_pass( + git_index_add_all(index, NULL, 0, addall_match_prefix, "file.")); + check_stat_data(index, TEST_DIR "/file.bar", true); + check_status(g_repo, 1, 0, 0, 1, 0, 0, 1); + + cl_git_mkfile(TEST_DIR "/file.zzz", "yet another one"); + cl_git_mkfile(TEST_DIR "/more.zzz", "yet another one"); + cl_git_mkfile(TEST_DIR "/other.zzz", "yet another one"); + + cl_git_pass(git_index_update_all(index, NULL, NULL, NULL)); + check_stat_data(index, TEST_DIR "/file.bar", true); + check_status(g_repo, 1, 0, 0, 4, 0, 0, 1); + + cl_git_pass( + git_index_add_all(index, NULL, 0, addall_match_prefix, "other")); + check_stat_data(index, TEST_DIR "/other.zzz", true); + check_status(g_repo, 2, 0, 0, 3, 0, 0, 1); + + cl_git_pass( + git_index_add_all(index, NULL, 0, addall_match_suffix, ".zzz")); + check_status(g_repo, 4, 0, 0, 1, 0, 0, 1); + + cl_git_pass( + git_index_remove_all(index, NULL, addall_match_suffix, ".zzz")); + check_status(g_repo, 1, 0, 0, 4, 0, 0, 1); + + cl_git_fail_with( + git_index_add_all(index, NULL, 0, addall_cancel_at, "more.zzz"), -123); + check_status(g_repo, 3, 0, 0, 2, 0, 0, 1); + + cl_git_fail_with( + git_index_add_all(index, NULL, 0, addall_cancel_at, "other.zzz"), -123); + check_status(g_repo, 4, 0, 0, 1, 0, 0, 1); + + cl_git_pass( + git_index_add_all(index, NULL, 0, addall_match_suffix, ".zzz")); + check_status(g_repo, 5, 0, 0, 0, 0, 0, 1); + + cl_must_pass(p_unlink(TEST_DIR "/file.zzz")); + cl_must_pass(p_unlink(TEST_DIR "/more.zzz")); + cl_must_pass(p_unlink(TEST_DIR "/other.zzz")); + + cl_git_fail_with( + git_index_update_all(index, NULL, addall_cancel_at, "more.zzz"), -123); + /* file.zzz removed from index (so Index Adds 5 -> 4) and + * more.zzz + other.zzz removed (so Worktree Dels 0 -> 2) */ + check_status(g_repo, 4, 0, 0, 0, 2, 0, 1); + + cl_git_fail_with( + git_index_update_all(index, NULL, addall_cancel_at, "other.zzz"), -123); + /* more.zzz removed from index (so Index Adds 4 -> 3) and + * Just other.zzz removed (so Worktree Dels 2 -> 1) */ + check_status(g_repo, 3, 0, 0, 0, 1, 0, 1); + + git_index_free(index); +} diff -Nru libgit2-0.20.0/tests/index/cache.c libgit2-0.22.2/tests/index/cache.c --- libgit2-0.20.0/tests/index/cache.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/index/cache.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,238 @@ +#include "clar_libgit2.h" +#include "git2.h" +#include "index.h" +#include "tree-cache.h" + +static git_repository *g_repo; + +void test_index_cache__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); +} + +void test_index_cache__cleanup(void) +{ + cl_git_sandbox_cleanup(); + g_repo = NULL; +} + +void test_index_cache__write_extension_at_root(void) +{ + git_index *index; + git_tree *tree; + git_oid id; + const char *tree_id_str = "45dd856fdd4d89b884c340ba0e047752d9b085d6"; + const char *index_file = "index-tree"; + + cl_git_pass(git_index_open(&index, index_file)); + cl_assert(index->tree == NULL); + cl_git_pass(git_oid_fromstr(&id, tree_id_str)); + cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); + cl_git_pass(git_index_read_tree(index, tree)); + git_tree_free(tree); + + cl_assert(index->tree); + cl_git_pass(git_index_write(index)); + git_index_free(index); + + cl_git_pass(git_index_open(&index, index_file)); + cl_assert(index->tree); + + cl_assert_equal_i(git_index_entrycount(index), index->tree->entry_count); + cl_assert_equal_i(0, index->tree->children_count); + + cl_assert(git_oid_equal(&id, &index->tree->oid)); + + cl_git_pass(p_unlink(index_file)); + git_index_free(index); +} + +void test_index_cache__write_extension_invalidated_root(void) +{ + git_index *index; + git_tree *tree; + git_oid id; + const char *tree_id_str = "45dd856fdd4d89b884c340ba0e047752d9b085d6"; + const char *index_file = "index-tree-invalidated"; + git_index_entry entry; + + cl_git_pass(git_index_open(&index, index_file)); + cl_assert(index->tree == NULL); + cl_git_pass(git_oid_fromstr(&id, tree_id_str)); + cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); + cl_git_pass(git_index_read_tree(index, tree)); + git_tree_free(tree); + + cl_assert(index->tree); + + memset(&entry, 0x0, sizeof(git_index_entry)); + git_oid_cpy(&entry.id, &git_index_get_byindex(index, 0)->id); + entry.mode = GIT_FILEMODE_BLOB; + entry.path = "some-new-file.txt"; + + cl_git_pass(git_index_add(index, &entry)); + + cl_assert_equal_i(-1, index->tree->entry_count); + + cl_git_pass(git_index_write(index)); + git_index_free(index); + + cl_git_pass(git_index_open(&index, index_file)); + cl_assert(index->tree); + + cl_assert_equal_i(-1, index->tree->entry_count); + cl_assert_equal_i(0, index->tree->children_count); + + cl_assert(git_oid_cmp(&id, &index->tree->oid)); + + cl_git_pass(p_unlink(index_file)); + git_index_free(index); +} + +void test_index_cache__read_tree_no_children(void) +{ + git_index *index; + git_index_entry entry; + git_tree *tree; + git_oid id; + + cl_git_pass(git_index_new(&index)); + cl_assert(index->tree == NULL); + cl_git_pass(git_oid_fromstr(&id, "45dd856fdd4d89b884c340ba0e047752d9b085d6")); + cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); + cl_git_pass(git_index_read_tree(index, tree)); + git_tree_free(tree); + + cl_assert(index->tree); + cl_assert(git_oid_equal(&id, &index->tree->oid)); + cl_assert_equal_i(0, index->tree->children_count); + cl_assert_equal_i(git_index_entrycount(index), index->tree->entry_count); + + memset(&entry, 0x0, sizeof(git_index_entry)); + entry.path = "new.txt"; + entry.mode = GIT_FILEMODE_BLOB; + git_oid_fromstr(&entry.id, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057"); + + cl_git_pass(git_index_add(index, &entry)); + cl_assert_equal_i(-1, index->tree->entry_count); + + git_index_free(index); +} + +void test_index_cache__two_levels(void) +{ + git_tree *tree; + git_oid tree_id; + git_index *index; + git_index_entry entry; + const git_tree_cache *tree_cache; + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_clear(index)); + + memset(&entry, 0x0, sizeof(entry)); + entry.mode = GIT_FILEMODE_BLOB; + cl_git_pass(git_oid_fromstr(&entry.id, "a8233120f6ad708f843d861ce2b7228ec4e3dec6")); + entry.path = "top-level.txt"; + cl_git_pass(git_index_add(index, &entry)); + + entry.path = "subdir/file.txt"; + cl_git_pass(git_index_add(index, &entry)); + + /* the read-tree fills the tree cache */ + cl_git_pass(git_index_write_tree(&tree_id, index)); + cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); + cl_git_pass(git_index_read_tree(index, tree)); + git_tree_free(tree); + cl_git_pass(git_index_write(index)); + + /* we now must have cache entries for "" and "subdir" */ + cl_assert(index->tree); + cl_assert(git_tree_cache_get(index->tree, "subdir")); + + cl_git_pass(git_index_read(index, true)); + /* we must still have cache entries for "" and "subdir", since we wrote it out */ + cl_assert(index->tree); + cl_assert(git_tree_cache_get(index->tree, "subdir")); + + entry.path = "top-level.txt"; + cl_git_pass(git_oid_fromstr(&entry.id, "3697d64be941a53d4ae8f6a271e4e3fa56b022cc")); + cl_git_pass(git_index_add(index, &entry)); + + /* writ out the index after we invalidate the root */ + cl_git_pass(git_index_write(index)); + cl_git_pass(git_index_read(index, true)); + + /* the cache for the subtree must still be valid, even if the root isn't */ + cl_assert(index->tree); + cl_assert_equal_i(-1, index->tree->entry_count); + cl_assert_equal_i(1, index->tree->children_count); + tree_cache = git_tree_cache_get(index->tree, "subdir"); + cl_assert(tree_cache); + cl_assert_equal_i(1, tree_cache->entry_count); + + git_index_free(index); +} + +void test_index_cache__read_tree_children(void) +{ + git_index *index; + git_index_entry entry; + git_tree *tree; + const git_tree_cache *cache; + git_oid tree_id; + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_clear(index)); + cl_assert(index->tree == NULL); + + + /* add a bunch of entries at different levels */ + memset(&entry, 0x0, sizeof(git_index_entry)); + entry.path = "top-level"; + entry.mode = GIT_FILEMODE_BLOB; + git_oid_fromstr(&entry.id, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057"); + cl_git_pass(git_index_add(index, &entry)); + + + entry.path = "subdir/some-file"; + cl_git_pass(git_index_add(index, &entry)); + + entry.path = "subdir/even-deeper/some-file"; + cl_git_pass(git_index_add(index, &entry)); + + entry.path = "subdir2/some-file"; + cl_git_pass(git_index_add(index, &entry)); + + cl_git_pass(git_index_write_tree(&tree_id, index)); + cl_git_pass(git_index_clear(index)); + cl_assert(index->tree == NULL); + + cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id)); + cl_git_pass(git_index_read_tree(index, tree)); + git_tree_free(tree); + + cl_assert(index->tree); + cl_assert_equal_i(2, index->tree->children_count); + + /* override with a slightly different id, also dummy */ + entry.path = "subdir/some-file"; + git_oid_fromstr(&entry.id, "45b983be36b73c0788dc9cbcb76cbb80fc7bb058"); + cl_git_pass(git_index_add(index, &entry)); + + cl_assert_equal_i(-1, index->tree->entry_count); + + cache = git_tree_cache_get(index->tree, "subdir"); + cl_assert(cache); + cl_assert_equal_i(-1, cache->entry_count); + + cache = git_tree_cache_get(index->tree, "subdir/even-deeper"); + cl_assert(cache); + cl_assert_equal_i(1, cache->entry_count); + + cache = git_tree_cache_get(index->tree, "subdir2"); + cl_assert(cache); + cl_assert_equal_i(1, cache->entry_count); + + git_index_free(index); +} diff -Nru libgit2-0.20.0/tests/index/collision.c libgit2-0.22.2/tests/index/collision.c --- libgit2-0.20.0/tests/index/collision.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/index/collision.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,106 @@ +#include "clar_libgit2.h" +#include "git2/repository.h" +#include "git2/index.h" + +git_repository *repo = NULL; + +void test_index_collision__cleanup(void) +{ + cl_git_sandbox_cleanup(); + repo = NULL; +} + +void test_index_collision__add(void) +{ + git_index *index; + git_index_entry entry; + git_oid tree_id; + git_tree *tree; + + repo = cl_git_sandbox_init("empty_standard_repo"); + cl_git_pass(git_repository_index(&index, repo)); + + memset(&entry, 0, sizeof(entry)); + entry.ctime.seconds = 12346789; + entry.mtime.seconds = 12346789; + entry.mode = 0100644; + entry.file_size = 0; + git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"); + + entry.path = "a/b"; + cl_git_pass(git_index_add(index, &entry)); + + /* create a tree/blob collision */ + entry.path = "a/b/c"; + cl_git_fail(git_index_add(index, &entry)); + + cl_git_pass(git_index_write_tree(&tree_id, index)); + cl_git_pass(git_tree_lookup(&tree, repo, &tree_id)); + + git_tree_free(tree); + git_index_free(index); +} + +void test_index_collision__add_with_highstage_1(void) +{ + git_index *index; + git_index_entry entry; + + repo = cl_git_sandbox_init("empty_standard_repo"); + cl_git_pass(git_repository_index(&index, repo)); + + memset(&entry, 0, sizeof(entry)); + entry.ctime.seconds = 12346789; + entry.mtime.seconds = 12346789; + entry.mode = 0100644; + entry.file_size = 0; + git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"); + + entry.path = "a/b"; + entry.flags = (2 << GIT_IDXENTRY_STAGESHIFT); + cl_git_pass(git_index_add(index, &entry)); + + /* create a blob beneath the previous tree entry */ + entry.path = "a/b/c"; + entry.flags = 0; + cl_git_pass(git_index_add(index, &entry)); + + /* create another tree entry above the blob */ + entry.path = "a/b"; + entry.flags = (1 << GIT_IDXENTRY_STAGESHIFT); + cl_git_pass(git_index_add(index, &entry)); + + git_index_free(index); +} + +void test_index_collision__add_with_highstage_2(void) +{ + git_index *index; + git_index_entry entry; + + repo = cl_git_sandbox_init("empty_standard_repo"); + cl_git_pass(git_repository_index(&index, repo)); + + memset(&entry, 0, sizeof(entry)); + entry.ctime.seconds = 12346789; + entry.mtime.seconds = 12346789; + entry.mode = 0100644; + entry.file_size = 0; + git_oid_fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"); + + entry.path = "a/b/c"; + entry.flags = (1 << GIT_IDXENTRY_STAGESHIFT); + cl_git_pass(git_index_add(index, &entry)); + + /* create a blob beneath the previous tree entry */ + entry.path = "a/b/c"; + entry.flags = (2 << GIT_IDXENTRY_STAGESHIFT); + cl_git_pass(git_index_add(index, &entry)); + + /* create another tree entry above the blob */ + entry.path = "a/b"; + entry.flags = (3 << GIT_IDXENTRY_STAGESHIFT); + cl_git_pass(git_index_add(index, &entry)); + + git_index_free(index); +} diff -Nru libgit2-0.20.0/tests/index/conflicts.c libgit2-0.22.2/tests/index/conflicts.c --- libgit2-0.20.0/tests/index/conflicts.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/index/conflicts.c 2015-03-24 16:10:45.000000000 +0000 @@ -47,15 +47,15 @@ ancestor_entry.path = "test-one.txt"; ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT); - git_oid_fromstr(&ancestor_entry.oid, TEST_ANCESTOR_OID); + git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID); our_entry.path = "test-one.txt"; ancestor_entry.flags |= (2 << GIT_IDXENTRY_STAGESHIFT); - git_oid_fromstr(&our_entry.oid, TEST_OUR_OID); + git_oid_fromstr(&our_entry.id, TEST_OUR_OID); their_entry.path = "test-one.txt"; ancestor_entry.flags |= (3 << GIT_IDXENTRY_STAGESHIFT); - git_oid_fromstr(&their_entry.oid, TEST_THEIR_OID); + git_oid_fromstr(&their_entry.id, TEST_THEIR_OID); cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry)); @@ -75,15 +75,15 @@ ancestor_entry.path = "test-one.txt"; ancestor_entry.flags |= (3 << GIT_IDXENTRY_STAGESHIFT); - git_oid_fromstr(&ancestor_entry.oid, TEST_ANCESTOR_OID); + git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID); our_entry.path = "test-one.txt"; ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT); - git_oid_fromstr(&our_entry.oid, TEST_OUR_OID); + git_oid_fromstr(&our_entry.id, TEST_OUR_OID); their_entry.path = "test-one.txt"; ancestor_entry.flags |= (2 << GIT_IDXENTRY_STAGESHIFT); - git_oid_fromstr(&their_entry.oid, TEST_THEIR_OID); + git_oid_fromstr(&their_entry.id, TEST_THEIR_OID); cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry)); @@ -107,13 +107,13 @@ cl_assert_equal_s("conflicts-one.txt", conflict_entry[0]->path); git_oid_fromstr(&oid, CONFLICTS_ONE_ANCESTOR_OID); - cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0); + cl_assert_equal_oid(&oid, &conflict_entry[0]->id); git_oid_fromstr(&oid, CONFLICTS_ONE_OUR_OID); - cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0); + cl_assert_equal_oid(&oid, &conflict_entry[1]->id); git_oid_fromstr(&oid, CONFLICTS_ONE_THEIR_OID); - cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0); + cl_assert_equal_oid(&oid, &conflict_entry[2]->id); cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], repo_index, "conflicts-two.txt")); @@ -121,13 +121,13 @@ cl_assert_equal_s("conflicts-two.txt", conflict_entry[0]->path); git_oid_fromstr(&oid, CONFLICTS_TWO_ANCESTOR_OID); - cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0); + cl_assert_equal_oid(&oid, &conflict_entry[0]->id); git_oid_fromstr(&oid, CONFLICTS_TWO_OUR_OID); - cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0); + cl_assert_equal_oid(&oid, &conflict_entry[1]->id); git_oid_fromstr(&oid, CONFLICTS_TWO_THEIR_OID); - cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0); + cl_assert_equal_oid(&oid, &conflict_entry[2]->id); } void test_index_conflicts__iterate(void) @@ -141,29 +141,29 @@ cl_git_pass(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator)); git_oid_fromstr(&oid, CONFLICTS_ONE_ANCESTOR_OID); - cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0); + cl_assert_equal_oid(&oid, &conflict_entry[0]->id); cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0); git_oid_fromstr(&oid, CONFLICTS_ONE_OUR_OID); - cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0); + cl_assert_equal_oid(&oid, &conflict_entry[1]->id); cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0); git_oid_fromstr(&oid, CONFLICTS_ONE_THEIR_OID); - cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0); + cl_assert_equal_oid(&oid, &conflict_entry[2]->id); cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0); cl_git_pass(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator)); git_oid_fromstr(&oid, CONFLICTS_TWO_ANCESTOR_OID); - cl_assert(git_oid_cmp(&conflict_entry[0]->oid, &oid) == 0); + cl_assert_equal_oid(&oid, &conflict_entry[0]->id); cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0); git_oid_fromstr(&oid, CONFLICTS_TWO_OUR_OID); - cl_assert(git_oid_cmp(&conflict_entry[1]->oid, &oid) == 0); + cl_assert_equal_oid(&oid, &conflict_entry[1]->id); cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0); git_oid_fromstr(&oid, CONFLICTS_TWO_THEIR_OID); - cl_assert(git_oid_cmp(&conflict_entry[2]->oid, &oid) == 0); + cl_assert_equal_oid(&oid, &conflict_entry[2]->id); cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0); cl_assert(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator) == GIT_ITEROVER); @@ -273,7 +273,7 @@ ancestor_entry.path = "test-one.txt"; ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT); - git_oid_fromstr(&ancestor_entry.oid, TEST_ANCESTOR_OID); + git_oid_fromstr(&ancestor_entry.id, TEST_ANCESTOR_OID); cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, NULL, NULL)); cl_assert(git_index_entrycount(repo_index) == 9); @@ -281,7 +281,7 @@ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], repo_index, "test-one.txt")); - cl_assert(git_oid_cmp(&ancestor_entry.oid, &conflict_entry[0]->oid) == 0); + cl_assert_equal_oid(&ancestor_entry.id, &conflict_entry[0]->id); cl_assert(conflict_entry[1] == NULL); cl_assert(conflict_entry[2] == NULL); } diff -Nru libgit2-0.20.0/tests/index/crlf.c libgit2-0.22.2/tests/index/crlf.c --- libgit2-0.20.0/tests/index/crlf.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/index/crlf.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,154 @@ +#include "clar_libgit2.h" +#include "../filter/crlf.h" + +#include "git2/checkout.h" +#include "repository.h" +#include "posix.h" + +#define FILE_CONTENTS_LF "one\ntwo\nthree\nfour\n" +#define FILE_CONTENTS_CRLF "one\r\ntwo\r\nthree\r\nfour\r\n" + +#define FILE_OID_LF "f384549cbeb481e437091320de6d1f2e15e11b4a" +#define FILE_OID_CRLF "7fbf4d847b191141d80f30c8ab03d2ad4cd543a9" + +static git_repository *g_repo; +static git_index *g_index; + +void test_index_crlf__initialize(void) +{ + g_repo = cl_git_sandbox_init("crlf"); + cl_git_pass(git_repository_index(&g_index, g_repo)); +} + +void test_index_crlf__cleanup(void) +{ + git_index_free(g_index); + cl_git_sandbox_cleanup(); +} + +void test_index_crlf__autocrlf_false_no_attrs(void) +{ + const git_index_entry *entry; + git_oid oid; + + cl_repo_set_bool(g_repo, "core.autocrlf", false); + + cl_git_mkfile("./crlf/newfile.txt", + (GIT_EOL_NATIVE == GIT_EOL_CRLF) ? FILE_CONTENTS_CRLF : FILE_CONTENTS_LF); + + cl_git_pass(git_index_add_bypath(g_index, "newfile.txt")); + entry = git_index_get_bypath(g_index, "newfile.txt", 0); + + cl_git_pass(git_oid_fromstr(&oid, + (GIT_EOL_NATIVE == GIT_EOL_CRLF) ? FILE_OID_CRLF : FILE_OID_LF)); + cl_assert_equal_oid(&oid, &entry->id); +} + +void test_index_crlf__autocrlf_true_no_attrs(void) +{ + const git_index_entry *entry; + git_oid oid; + + cl_repo_set_bool(g_repo, "core.autocrlf", true); + + cl_git_mkfile("./crlf/newfile.txt", + (GIT_EOL_NATIVE == GIT_EOL_CRLF) ? FILE_CONTENTS_CRLF : FILE_CONTENTS_LF); + + cl_git_pass(git_index_add_bypath(g_index, "newfile.txt")); + entry = git_index_get_bypath(g_index, "newfile.txt", 0); + + cl_git_pass(git_oid_fromstr(&oid, FILE_OID_LF)); + cl_assert_equal_oid(&oid, &entry->id); +} + +void test_index_crlf__autocrlf_input_no_attrs(void) +{ + const git_index_entry *entry; + git_oid oid; + + cl_repo_set_string(g_repo, "core.autocrlf", "input"); + + cl_git_mkfile("./crlf/newfile.txt", + (GIT_EOL_NATIVE == GIT_EOL_CRLF) ? FILE_CONTENTS_CRLF : FILE_CONTENTS_LF); + + cl_git_pass(git_index_add_bypath(g_index, "newfile.txt")); + entry = git_index_get_bypath(g_index, "newfile.txt", 0); + + cl_git_pass(git_oid_fromstr(&oid, FILE_OID_LF)); + cl_assert_equal_oid(&oid, &entry->id); +} + +void test_index_crlf__autocrlf_false_text_auto_attr(void) +{ + const git_index_entry *entry; + git_oid oid; + + cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n"); + + cl_repo_set_bool(g_repo, "core.autocrlf", false); + + cl_git_mkfile("./crlf/newfile.txt", + (GIT_EOL_NATIVE == GIT_EOL_CRLF) ? FILE_CONTENTS_CRLF : FILE_CONTENTS_LF); + + cl_git_pass(git_index_add_bypath(g_index, "newfile.txt")); + entry = git_index_get_bypath(g_index, "newfile.txt", 0); + + cl_git_pass(git_oid_fromstr(&oid, FILE_OID_LF)); + cl_assert_equal_oid(&oid, &entry->id); +} + +void test_index_crlf__autocrlf_true_text_auto_attr(void) +{ + const git_index_entry *entry; + git_oid oid; + + cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n"); + + cl_repo_set_bool(g_repo, "core.autocrlf", false); + + cl_git_mkfile("./crlf/newfile.txt", + (GIT_EOL_NATIVE == GIT_EOL_CRLF) ? FILE_CONTENTS_CRLF : FILE_CONTENTS_LF); + + cl_git_pass(git_index_add_bypath(g_index, "newfile.txt")); + entry = git_index_get_bypath(g_index, "newfile.txt", 0); + + cl_git_pass(git_oid_fromstr(&oid, FILE_OID_LF)); + cl_assert_equal_oid(&oid, &entry->id); +} + +void test_index_crlf__autocrlf_input_text_auto_attr(void) +{ + const git_index_entry *entry; + git_oid oid; + + cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n"); + + cl_repo_set_string(g_repo, "core.autocrlf", "input"); + + cl_git_mkfile("./crlf/newfile.txt", + (GIT_EOL_NATIVE == GIT_EOL_CRLF) ? FILE_CONTENTS_CRLF : FILE_CONTENTS_LF); + + cl_git_pass(git_index_add_bypath(g_index, "newfile.txt")); + entry = git_index_get_bypath(g_index, "newfile.txt", 0); + + cl_git_pass(git_oid_fromstr(&oid, FILE_OID_LF)); + cl_assert_equal_oid(&oid, &entry->id); +} + +void test_index_crlf__safecrlf_true_no_attrs(void) +{ + cl_repo_set_bool(g_repo, "core.autocrlf", true); + cl_repo_set_bool(g_repo, "core.safecrlf", true); + + cl_git_mkfile("crlf/newfile.txt", ALL_LF_TEXT_RAW); + cl_git_pass(git_index_add_bypath(g_index, "newfile.txt")); + + cl_git_mkfile("crlf/newfile.txt", ALL_CRLF_TEXT_RAW); + cl_git_pass(git_index_add_bypath(g_index, "newfile.txt")); + + cl_git_mkfile("crlf/newfile.txt", MORE_CRLF_TEXT_RAW); + cl_git_fail(git_index_add_bypath(g_index, "newfile.txt")); + + cl_git_mkfile("crlf/newfile.txt", MORE_LF_TEXT_RAW); + cl_git_fail(git_index_add_bypath(g_index, "newfile.txt")); +} diff -Nru libgit2-0.20.0/tests/index/filemodes.c libgit2-0.22.2/tests/index/filemodes.c --- libgit2-0.20.0/tests/index/filemodes.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/index/filemodes.c 2015-03-24 16:10:45.000000000 +0000 @@ -152,3 +152,20 @@ git_index_free(index); } + +void test_index_filemodes__invalid(void) +{ + git_index *index; + git_index_entry entry; + + cl_git_pass(git_repository_index(&index, g_repo)); + + entry.path = "foo"; + entry.mode = GIT_OBJ_BLOB; + cl_git_fail(git_index_add(index, &entry)); + + entry.mode = GIT_FILEMODE_BLOB; + cl_git_pass(git_index_add(index, &entry)); + + git_index_free(index); +} diff -Nru libgit2-0.20.0/tests/index/names.c libgit2-0.22.2/tests/index/names.c --- libgit2-0.20.0/tests/index/names.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/index/names.c 2015-03-24 16:10:45.000000000 +0000 @@ -86,10 +86,10 @@ { git_object *target; - retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876"); + cl_git_pass(git_revparse_single(&target, repo, "3a34580")); test_index_names__add(); - cl_git_pass(git_reset(repo, target, GIT_RESET_HARD)); + cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL, NULL, NULL)); cl_assert(git_index_name_entrycount(repo_index) == 0); git_object_free(target); @@ -99,10 +99,10 @@ { git_object *target; - retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876"); + cl_git_pass(git_revparse_single(&target, repo, "3a34580")); test_index_names__add(); - cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED)); + cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED, NULL, NULL, NULL)); cl_assert(git_index_name_entrycount(repo_index) == 0); git_object_free(target); @@ -112,7 +112,7 @@ { git_oid oid; git_object *obj; - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY; @@ -127,7 +127,7 @@ void test_index_names__cleaned_on_checkout_head(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY; @@ -138,7 +138,7 @@ void test_index_names__retained_on_checkout_index(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY; diff -Nru libgit2-0.20.0/tests/index/read_tree.c libgit2-0.22.2/tests/index/read_tree.c --- libgit2-0.20.0/tests/index/read_tree.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/index/read_tree.c 2015-03-24 16:10:45.000000000 +0000 @@ -37,7 +37,7 @@ git_tree_free(tree); cl_git_pass(git_index_write_tree(&tree_oid, index)); - cl_assert(git_oid_cmp(&expected, &tree_oid) == 0); + cl_assert_equal_oid(&expected, &tree_oid); git_index_free(index); git_repository_free(repo); diff -Nru libgit2-0.20.0/tests/index/rename.c libgit2-0.22.2/tests/index/rename.c --- libgit2-0.20.0/tests/index/rename.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/index/rename.c 2015-03-24 16:10:45.000000000 +0000 @@ -27,7 +27,7 @@ cl_assert(!git_index_find(&position, index, "lame.name.txt")); entry = git_index_get_byindex(index, position); - cl_assert(git_oid_cmp(&expected, &entry->oid) == 0); + cl_assert_equal_oid(&expected, &entry->id); /* This removes the entry from the index, but not from the object database */ cl_git_pass(git_index_remove(index, "lame.name.txt", 0)); @@ -41,7 +41,7 @@ cl_assert(!git_index_find(&position, index, "fancy.name.txt")); entry = git_index_get_byindex(index, position); - cl_assert(git_oid_cmp(&expected, &entry->oid) == 0); + cl_assert_equal_oid(&expected, &entry->id); git_index_free(index); git_repository_free(repo); diff -Nru libgit2-0.20.0/tests/index/reuc.c libgit2-0.22.2/tests/index/reuc.c --- libgit2-0.20.0/tests/index/reuc.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/index/reuc.c 2015-03-24 16:10:45.000000000 +0000 @@ -53,9 +53,9 @@ cl_assert(reuc->mode[0] == 0100644); cl_assert(reuc->mode[1] == 0100644); cl_assert(reuc->mode[2] == 0100644); - cl_assert(git_oid_cmp(&reuc->oid[0], &ancestor_oid) == 0); - cl_assert(git_oid_cmp(&reuc->oid[1], &our_oid) == 0); - cl_assert(git_oid_cmp(&reuc->oid[2], &their_oid) == 0); + cl_assert_equal_oid(&reuc->oid[0], &ancestor_oid); + cl_assert_equal_oid(&reuc->oid[1], &our_oid); + cl_assert_equal_oid(&reuc->oid[2], &their_oid); } void test_index_reuc__add_no_ancestor(void) @@ -78,9 +78,9 @@ cl_assert(reuc->mode[0] == 0); cl_assert(reuc->mode[1] == 0100644); cl_assert(reuc->mode[2] == 0100644); - cl_assert(git_oid_cmp(&reuc->oid[0], &ancestor_oid) == 0); - cl_assert(git_oid_cmp(&reuc->oid[1], &our_oid) == 0); - cl_assert(git_oid_cmp(&reuc->oid[2], &their_oid) == 0); + cl_assert_equal_oid(&reuc->oid[0], &ancestor_oid); + cl_assert_equal_oid(&reuc->oid[1], &our_oid); + cl_assert_equal_oid(&reuc->oid[2], &their_oid); } void test_index_reuc__read_bypath(void) @@ -97,11 +97,11 @@ cl_assert(reuc->mode[1] == 0100644); cl_assert(reuc->mode[2] == 0100644); git_oid_fromstr(&oid, TWO_ANCESTOR_OID); - cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[0], &oid); git_oid_fromstr(&oid, TWO_OUR_OID); - cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[1], &oid); git_oid_fromstr(&oid, TWO_THEIR_OID); - cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[2], &oid); cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "one.txt")); @@ -110,11 +110,11 @@ cl_assert(reuc->mode[1] == 0100644); cl_assert(reuc->mode[2] == 0100644); git_oid_fromstr(&oid, ONE_ANCESTOR_OID); - cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[0], &oid); git_oid_fromstr(&oid, ONE_OUR_OID); - cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[1], &oid); git_oid_fromstr(&oid, ONE_THEIR_OID); - cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[2], &oid); } void test_index_reuc__ignore_case(void) @@ -142,11 +142,11 @@ cl_assert(reuc->mode[1] == 0100644); cl_assert(reuc->mode[2] == 0100644); git_oid_fromstr(&oid, TWO_ANCESTOR_OID); - cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[0], &oid); git_oid_fromstr(&oid, TWO_OUR_OID); - cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[1], &oid); git_oid_fromstr(&oid, TWO_THEIR_OID); - cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[2], &oid); } void test_index_reuc__read_byindex(void) @@ -163,11 +163,11 @@ cl_assert(reuc->mode[1] == 0100644); cl_assert(reuc->mode[2] == 0100644); git_oid_fromstr(&oid, ONE_ANCESTOR_OID); - cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[0], &oid); git_oid_fromstr(&oid, ONE_OUR_OID); - cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[1], &oid); git_oid_fromstr(&oid, ONE_THEIR_OID); - cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[2], &oid); cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 1)); @@ -176,11 +176,11 @@ cl_assert(reuc->mode[1] == 0100644); cl_assert(reuc->mode[2] == 0100644); git_oid_fromstr(&oid, TWO_ANCESTOR_OID); - cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[0], &oid); git_oid_fromstr(&oid, TWO_OUR_OID); - cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[1], &oid); git_oid_fromstr(&oid, TWO_THEIR_OID); - cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[2], &oid); } void test_index_reuc__updates_existing(void) @@ -216,11 +216,11 @@ cl_assert_equal_s("TWO.txt", reuc->path); git_oid_fromstr(&oid, TWO_OUR_OID); - cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[0], &oid); git_oid_fromstr(&oid, TWO_THEIR_OID); - cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[1], &oid); git_oid_fromstr(&oid, TWO_ANCESTOR_OID); - cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[2], &oid); } void test_index_reuc__remove(void) @@ -242,11 +242,11 @@ cl_assert(reuc->mode[1] == 0100644); cl_assert(reuc->mode[2] == 0100644); git_oid_fromstr(&oid, TWO_ANCESTOR_OID); - cl_assert(git_oid_cmp(&reuc->oid[0], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[0], &oid); git_oid_fromstr(&oid, TWO_OUR_OID); - cl_assert(git_oid_cmp(&reuc->oid[1], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[1], &oid); git_oid_fromstr(&oid, TWO_THEIR_OID); - cl_assert(git_oid_cmp(&reuc->oid[2], &oid) == 0); + cl_assert_equal_oid(&reuc->oid[2], &oid); } void test_index_reuc__write(void) @@ -295,10 +295,10 @@ { git_object *target; - retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876"); + cl_git_pass(git_revparse_single(&target, repo, "3a34580")); test_index_reuc__add(); - cl_git_pass(git_reset(repo, target, GIT_RESET_HARD)); + cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL, NULL, NULL)); cl_assert(reuc_entry_exists() == false); git_object_free(target); @@ -308,10 +308,10 @@ { git_object *target; - retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876"); + cl_git_pass(git_revparse_single(&target, repo, "3a34580")); test_index_reuc__add(); - cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED)); + cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED, NULL, NULL, NULL)); cl_assert(reuc_entry_exists() == false); git_object_free(target); @@ -321,12 +321,12 @@ { git_object *target; - retrieve_target_from_oid(&target, repo, "3a34580a35add43a4cf361e8e9a30060a905c876"); + cl_git_pass(git_revparse_single(&target, repo, "3a34580")); - git_reset(repo, target, GIT_RESET_HARD); + git_reset(repo, target, GIT_RESET_HARD, NULL, NULL, NULL); test_index_reuc__add(); - cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT)); + cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT, NULL, NULL, NULL)); cl_assert(reuc_entry_exists() == true); git_object_free(target); @@ -336,7 +336,7 @@ { git_oid oid; git_object *obj; - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY; @@ -351,7 +351,7 @@ void test_index_reuc__cleaned_on_checkout_head(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY; @@ -362,7 +362,7 @@ void test_index_reuc__retained_on_checkout_index(void) { - git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY; diff -Nru libgit2-0.20.0/tests/index/tests.c libgit2-0.22.2/tests/index/tests.c --- libgit2-0.20.0/tests/index/tests.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/index/tests.c 2015-03-24 16:10:45.000000000 +0000 @@ -80,7 +80,7 @@ cl_assert(index->on_disk == 0); cl_assert(git_index_entrycount(index) == 0); - cl_assert(index->entries.sorted); + cl_assert(git_vector_is_sorted(&index->entries)); git_index_free(index); } @@ -95,7 +95,7 @@ cl_assert(index->on_disk); cl_assert(git_index_entrycount(index) == index_entry_count); - cl_assert(index->entries.sorted); + cl_assert(git_vector_is_sorted(&index->entries)); entries = (git_index_entry **)index->entries.contents; @@ -118,7 +118,7 @@ cl_assert(index->on_disk); cl_assert(git_index_entrycount(index) == index_entry_count_2); - cl_assert(index->entries.sorted); + cl_assert(git_vector_is_sorted(&index->entries)); cl_assert(index->tree != NULL); git_index_free(index); @@ -195,7 +195,7 @@ cl_git_pass(git_index_open(&index, "fake-index")); /* FIXME: this test is slightly dumb */ - cl_assert(index->entries.sorted); + cl_assert(git_vector_is_sorted(&index->entries)); git_index_free(index); } @@ -243,11 +243,11 @@ entry = git_index_get_byindex(index, 0); /* And the built-in hashing mechanism worked as expected */ - cl_assert(git_oid_cmp(&id1, &entry->oid) == 0); + cl_assert_equal_oid(&id1, &entry->id); /* Test access by path instead of index */ cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL); - cl_assert(git_oid_cmp(&id1, &entry->oid) == 0); + cl_assert_equal_oid(&id1, &entry->id); git_index_free(index); git_repository_free(repo); @@ -283,14 +283,14 @@ /* Make sure the initial SHA-1 is correct */ cl_assert((entry = git_index_get_bypath(index, "crlf_file.txt", 0)) != NULL); - cl_assert_(git_oid_cmp(&id1, &entry->oid) == 0, "first oid check"); + cl_assert_equal_oid(&id1, &entry->id); /* Update the index */ cl_git_pass(git_index_add_bypath(index, "crlf_file.txt")); /* Check the new SHA-1 */ cl_assert((entry = git_index_get_bypath(index, "crlf_file.txt", 0)) != NULL); - cl_assert_(git_oid_cmp(&id1, &entry->oid) == 0, "second oid check"); + cl_assert_equal_oid(&id1, &entry->id); git_index_free(index); } @@ -309,31 +309,145 @@ git_repository_free(bare_repo); } +static void add_invalid_filename(git_repository *repo, const char *fn) +{ + git_index *index; + git_buf path = GIT_BUF_INIT; + + cl_git_pass(git_repository_index(&index, repo)); + cl_assert(git_index_entrycount(index) == 0); + + git_buf_joinpath(&path, "./invalid", fn); + + cl_git_mkfile(path.ptr, NULL); + cl_git_fail(git_index_add_bypath(index, fn)); + cl_must_pass(p_unlink(path.ptr)); + + cl_assert(git_index_entrycount(index) == 0); + + git_buf_free(&path); + git_index_free(index); +} + /* Test that writing an invalid filename fails */ -void test_index_tests__write_invalid_filename(void) +void test_index_tests__add_invalid_filename(void) { git_repository *repo; + + p_mkdir("invalid", 0700); + + cl_git_pass(git_repository_init(&repo, "./invalid", 0)); + cl_must_pass(p_mkdir("./invalid/subdir", 0777)); + + /* cl_git_mkfile() needs the dir to exist */ + if (!git_path_exists("./invalid/.GIT")) + cl_must_pass(p_mkdir("./invalid/.GIT", 0777)); + if (!git_path_exists("./invalid/.GiT")) + cl_must_pass(p_mkdir("./invalid/.GiT", 0777)); + + add_invalid_filename(repo, ".git/hello"); + add_invalid_filename(repo, ".GIT/hello"); + add_invalid_filename(repo, ".GiT/hello"); + add_invalid_filename(repo, "./.git/hello"); + add_invalid_filename(repo, "./foo"); + add_invalid_filename(repo, "./bar"); + add_invalid_filename(repo, "subdir/../bar"); + + git_repository_free(repo); + + cl_fixture_cleanup("invalid"); +} + +static void replace_char(char *str, char in, char out) +{ + char *c = str; + + while (*c++) + if (*c == in) + *c = out; +} + +static void write_invalid_filename(git_repository *repo, const char *fn_orig) +{ git_index *index; git_oid expected; + const git_index_entry *entry; + git_buf path = GIT_BUF_INIT; + char *fn; - p_mkdir("read_tree", 0700); - - cl_git_pass(git_repository_init(&repo, "./read_tree", 0)); cl_git_pass(git_repository_index(&index, repo)); - cl_assert(git_index_entrycount(index) == 0); - cl_git_mkfile("./read_tree/.git/hello", NULL); + /* + * Sneak a valid path into the index, we'll update it + * to an invalid path when we try to write the index. + */ + fn = git__strdup(fn_orig); + replace_char(fn, '/', '_'); + + git_buf_joinpath(&path, "./invalid", fn); + + cl_git_mkfile(path.ptr, NULL); - cl_git_pass(git_index_add_bypath(index, ".git/hello")); + cl_git_pass(git_index_add_bypath(index, fn)); + + cl_assert(entry = git_index_get_bypath(index, fn, 0)); + + /* kids, don't try this at home */ + replace_char((char *)entry->path, '_', '/'); /* write-tree */ cl_git_fail(git_index_write_tree(&expected, index)); + p_unlink(path.ptr); + + cl_git_pass(git_index_remove_all(index, NULL, NULL, NULL)); + git_buf_free(&path); git_index_free(index); + git__free(fn); +} + +/* Test that writing an invalid filename fails */ +void test_index_tests__write_invalid_filename(void) +{ + git_repository *repo; + + p_mkdir("invalid", 0700); + + cl_git_pass(git_repository_init(&repo, "./invalid", 0)); + + write_invalid_filename(repo, ".git/hello"); + write_invalid_filename(repo, ".GIT/hello"); + write_invalid_filename(repo, ".GiT/hello"); + write_invalid_filename(repo, "./.git/hello"); + write_invalid_filename(repo, "./foo"); + write_invalid_filename(repo, "./bar"); + write_invalid_filename(repo, "foo/../bar"); + + git_repository_free(repo); + + cl_fixture_cleanup("invalid"); +} + +void test_index_tests__honors_protect_filesystems(void) +{ + git_repository *repo; + + p_mkdir("invalid", 0700); + + cl_git_pass(git_repository_init(&repo, "./invalid", 0)); + + cl_repo_set_bool(repo, "core.protectHFS", true); + cl_repo_set_bool(repo, "core.protectNTFS", true); + + write_invalid_filename(repo, ".git./hello"); + write_invalid_filename(repo, ".git\xe2\x80\xad/hello"); + write_invalid_filename(repo, "git~1/hello"); + write_invalid_filename(repo, ".git\xe2\x81\xaf/hello"); + git_repository_free(repo); - cl_fixture_cleanup("read_tree"); + cl_fixture_cleanup("invalid"); } void test_index_tests__remove_entry(void) @@ -506,7 +620,7 @@ cl_git_pass(git_index_open(&read_index, write_index->index_file_path)); cl_assert_equal_i(false, read_index->on_disk); - /* Stage two new files agaisnt the write_index */ + /* Stage two new files against the write_index */ cl_git_pass(git_index_add_bypath(write_index, "a.txt")); cl_git_pass(git_index_add_bypath(write_index, "b.txt")); @@ -543,3 +657,23 @@ cl_git_fail_with(git_index_open(&index, TEST_INDEXBAD_PATH), GIT_ERROR); } + +void test_index_tests__reload_while_ignoring_case(void) +{ + git_index *index; + unsigned int caps; + + cl_git_pass(git_index_open(&index, TEST_INDEX_PATH)); + cl_git_pass(git_vector_verify_sorted(&index->entries)); + + caps = git_index_caps(index); + cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEXCAP_IGNORE_CASE)); + cl_git_pass(git_index_read(index, true)); + cl_git_pass(git_vector_verify_sorted(&index->entries)); + + cl_git_pass(git_index_set_caps(index, caps | GIT_INDEXCAP_IGNORE_CASE)); + cl_git_pass(git_index_read(index, true)); + cl_git_pass(git_vector_verify_sorted(&index->entries)); + + git_index_free(index); +} diff -Nru libgit2-0.20.0/tests/main.c libgit2-0.22.2/tests/main.c --- libgit2-0.20.0/tests/main.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/main.c 2015-03-24 16:10:45.000000000 +0000 @@ -8,13 +8,18 @@ { int res; - git_threads_init(); + clar_test_init(argc, argv); + + git_libgit2_init(); + cl_sandbox_set_search_path_defaults(); /* Run the test suite */ - res = clar_test(argc, argv); + res = clar_test_run(); + + clar_test_shutdown(); giterr_clear(); - git_threads_shutdown(); + git_libgit2_shutdown(); return res; } diff -Nru libgit2-0.20.0/tests/merge/files.c libgit2-0.22.2/tests/merge/files.c --- libgit2-0.20.0/tests/merge/files.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/merge/files.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,175 @@ +#include "clar_libgit2.h" +#include "git2/repository.h" +#include "git2/merge.h" +#include "buffer.h" +#include "merge.h" +#include "merge_helpers.h" +#include "refs.h" +#include "fileops.h" + +#define TEST_REPO_PATH "merge-resolve" +#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index" + +static git_repository *repo; +static git_index *repo_index; + +// Fixture setup and teardown +void test_merge_files__initialize(void) +{ + git_config *cfg; + + repo = cl_git_sandbox_init(TEST_REPO_PATH); + git_repository_index(&repo_index, repo); + + /* Ensure that the user's merge.conflictstyle doesn't interfere */ + cl_git_pass(git_repository_config(&cfg, repo)); + cl_git_pass(git_config_set_string(cfg, "merge.conflictstyle", "merge")); + git_config_free(cfg); +} + +void test_merge_files__cleanup(void) +{ + git_index_free(repo_index); + cl_git_sandbox_cleanup(); +} + +void test_merge_files__automerge_from_bufs(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_result result = {0}; + const char *expected = "Zero\n1\n2\n3\n4\n5\n6\n7\n8\n9\nTen\n"; + + ancestor.ptr = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n"; + ancestor.size = strlen(ancestor.ptr); + ancestor.path = "testfile.txt"; + ancestor.mode = 0100755; + + ours.ptr = "Zero\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n"; + ours.size = strlen(ours.ptr); + ours.path = "testfile.txt"; + ours.mode = 0100755; + + theirs.ptr = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\nTen\n"; + theirs.size = strlen(theirs.ptr); + theirs.path = "testfile.txt"; + theirs.mode = 0100755; + + cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, 0)); + + cl_assert_equal_i(1, result.automergeable); + + cl_assert_equal_s("testfile.txt", result.path); + cl_assert_equal_i(0100755, result.mode); + + cl_assert_equal_i(strlen(expected), result.len); + cl_assert_equal_strn(expected, result.ptr, result.len); + + git_merge_file_result_free(&result); +} + +void test_merge_files__automerge_use_best_path_and_mode(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_result result = {0}; + const char *expected = "Zero\n1\n2\n3\n4\n5\n6\n7\n8\n9\nTen\n"; + + ancestor.ptr = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n"; + ancestor.size = strlen(ancestor.ptr); + ancestor.path = "testfile.txt"; + ancestor.mode = 0100755; + + ours.ptr = "Zero\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n"; + ours.size = strlen(ours.ptr); + ours.path = "testfile.txt"; + ours.mode = 0100644; + + theirs.ptr = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\nTen\n"; + theirs.size = strlen(theirs.ptr); + theirs.path = "theirs.txt"; + theirs.mode = 0100755; + + cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, 0)); + + cl_assert_equal_i(1, result.automergeable); + + cl_assert_equal_s("theirs.txt", result.path); + cl_assert_equal_i(0100644, result.mode); + + cl_assert_equal_i(strlen(expected), result.len); + cl_assert_equal_strn(expected, result.ptr, result.len); + + git_merge_file_result_free(&result); +} + +void test_merge_files__conflict_from_bufs(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_result result = {0}; + + const char *expected = "<<<<<<< testfile.txt\nAloha!\nOurs.\n=======\nHi!\nTheirs.\n>>>>>>> theirs.txt\n"; + size_t expected_len = strlen(expected); + + ancestor.ptr = "Hello!\nAncestor!\n"; + ancestor.size = strlen(ancestor.ptr); + ancestor.path = "testfile.txt"; + ancestor.mode = 0100755; + + ours.ptr = "Aloha!\nOurs.\n"; + ours.size = strlen(ours.ptr); + ours.path = "testfile.txt"; + ours.mode = 0100644; + + theirs.ptr = "Hi!\nTheirs.\n"; + theirs.size = strlen(theirs.ptr); + theirs.path = "theirs.txt"; + theirs.mode = 0100755; + + cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, NULL)); + + cl_assert_equal_i(0, result.automergeable); + + cl_assert_equal_s("theirs.txt", result.path); + cl_assert_equal_i(0100644, result.mode); + + cl_assert_equal_i(expected_len, result.len); + cl_assert_equal_strn(expected, result.ptr, expected_len); + + git_merge_file_result_free(&result); +} + +void test_merge_files__automerge_from_index(void) +{ + git_merge_file_result result = {0}; + git_index_entry ancestor, ours, theirs; + + git_oid_fromstr(&ancestor.id, "6212c31dab5e482247d7977e4f0dd3601decf13b"); + ancestor.path = "automergeable.txt"; + ancestor.mode = 0100644; + + git_oid_fromstr(&ours.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf"); + ours.path = "automergeable.txt"; + ours.mode = 0100755; + + git_oid_fromstr(&theirs.id, "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe"); + theirs.path = "newname.txt"; + theirs.mode = 0100644; + + cl_git_pass(git_merge_file_from_index(&result, repo, + &ancestor, &ours, &theirs, 0)); + + cl_assert_equal_i(1, result.automergeable); + + cl_assert_equal_s("newname.txt", result.path); + cl_assert_equal_i(0100755, result.mode); + + cl_assert_equal_i(strlen(AUTOMERGEABLE_MERGED_FILE), result.len); + cl_assert_equal_strn(AUTOMERGEABLE_MERGED_FILE, result.ptr, result.len); + + git_merge_file_result_free(&result); +} diff -Nru libgit2-0.20.0/tests/merge/merge_helpers.c libgit2-0.22.2/tests/merge/merge_helpers.c --- libgit2-0.20.0/tests/merge/merge_helpers.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/merge/merge_helpers.c 2015-03-24 16:10:45.000000000 +0000 @@ -6,11 +6,12 @@ #include "merge.h" #include "git2/merge.h" #include "git2/sys/index.h" +#include "git2/annotated_commit.h" int merge_trees_from_branches( git_index **index, git_repository *repo, const char *ours_name, const char *theirs_name, - git_merge_tree_opts *opts) + git_merge_options *opts) { git_commit *our_commit, *their_commit, *ancestor_commit = NULL; git_tree *our_tree, *their_tree, *ancestor_tree = NULL; @@ -52,25 +53,54 @@ return 0; } -int merge_branches(git_merge_result **result, git_repository *repo, const char *ours_branch, const char *theirs_branch, git_merge_opts *opts) +int merge_commits_from_branches( + git_index **index, git_repository *repo, + const char *ours_name, const char *theirs_name, + git_merge_options *opts) +{ + git_commit *our_commit, *their_commit; + git_oid our_oid, their_oid; + git_buf branch_buf = GIT_BUF_INIT; + + git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name); + cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr)); + cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid)); + + git_buf_clear(&branch_buf); + git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs_name); + cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr)); + cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid)); + + cl_git_pass(git_merge_commits(index, repo, our_commit, their_commit, opts)); + + git_buf_free(&branch_buf); + git_commit_free(our_commit); + git_commit_free(their_commit); + + return 0; +} + +int merge_branches(git_repository *repo, + const char *ours_branch, const char *theirs_branch, + git_merge_options *merge_opts, git_checkout_options *checkout_opts) { git_reference *head_ref, *theirs_ref; - git_merge_head *theirs_head; - git_checkout_opts head_checkout_opts = GIT_CHECKOUT_OPTS_INIT; + git_annotated_commit *theirs_head; + git_checkout_options head_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; head_checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE; - cl_git_pass(git_reference_symbolic_create(&head_ref, repo, "HEAD", ours_branch, 1)); + cl_git_pass(git_reference_symbolic_create(&head_ref, repo, "HEAD", ours_branch, 1, NULL, NULL)); cl_git_pass(git_checkout_head(repo, &head_checkout_opts)); cl_git_pass(git_reference_lookup(&theirs_ref, repo, theirs_branch)); - cl_git_pass(git_merge_head_from_ref(&theirs_head, repo, theirs_ref)); + cl_git_pass(git_annotated_commit_from_ref(&theirs_head, repo, theirs_ref)); - cl_git_pass(git_merge(result, repo, (const git_merge_head **)&theirs_head, 1, opts)); + cl_git_pass(git_merge(repo, (const git_annotated_commit **)&theirs_head, 1, merge_opts, checkout_opts)); git_reference_free(head_ref); git_reference_free(theirs_ref); - git_merge_head_free(theirs_head); + git_annotated_commit_free(theirs_head); return 0; } @@ -85,7 +115,7 @@ index_entry = index_entries->contents[i]; printf("%o ", index_entry->mode); - printf("%s ", git_oid_allocfmt(&index_entry->oid)); + printf("%s ", git_oid_allocfmt(&index_entry->id)); printf("%d ", git_index_entry_stage(index_entry)); printf("%s ", index_entry->path); printf("\n"); @@ -139,7 +169,7 @@ test_oid = 0; if (actual->mode != expected->mode || - (test_oid && git_oid_cmp(&actual->oid, &expected_oid) != 0) || + (test_oid && git_oid_cmp(&actual->id, &expected_oid) != 0) || git_index_entry_stage(actual) != expected->stage) return 0; @@ -298,7 +328,7 @@ int dircount(void *payload, git_buf *pathbuf) { - int *entries = payload; + size_t *entries = payload; size_t len = git_buf_len(pathbuf); if (len < 5 || strcmp(pathbuf->ptr + (git_buf_len(pathbuf) - 5), "/.git") != 0) diff -Nru libgit2-0.20.0/tests/merge/merge_helpers.h libgit2-0.22.2/tests/merge/merge_helpers.h --- libgit2-0.20.0/tests/merge/merge_helpers.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/merge/merge_helpers.h 2015-03-24 16:10:45.000000000 +0000 @@ -4,9 +4,52 @@ #include "merge.h" #include "git2/merge.h" +#define AUTOMERGEABLE_MERGED_FILE \ + "this file is changed in master\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is changed in branch\n" + +#define AUTOMERGEABLE_MERGED_FILE_CRLF \ + "this file is changed in master\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is automergeable\r\n" \ + "this file is changed in branch\r\n" + +#define CONFLICTING_MERGE_FILE \ + "<<<<<<< HEAD\n" \ + "this file is changed in master and branch\n" \ + "=======\n" \ + "this file is changed in branch and master\n" \ + ">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n" + +#define CONFLICTING_DIFF3_FILE \ + "<<<<<<< HEAD\n" \ + "this file is changed in master and branch\n" \ + "||||||| initial\n" \ + "this file is a conflict\n" \ + "=======\n" \ + "this file is changed in branch and master\n" \ + ">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n" + +#define CONFLICTING_UNION_FILE \ + "this file is changed in master and branch\n" \ + "this file is changed in branch and master\n" + + struct merge_index_entry { uint16_t mode; - char oid_str[41]; + char oid_str[GIT_OID_HEXSZ+1]; int stage; char path[128]; }; @@ -27,9 +70,9 @@ unsigned int ancestor_mode; unsigned int our_mode; unsigned int their_mode; - char ancestor_oid_str[41]; - char our_oid_str[41]; - char their_oid_str[41]; + char ancestor_oid_str[GIT_OID_HEXSZ+1]; + char our_oid_str[GIT_OID_HEXSZ+1]; + char their_oid_str[GIT_OID_HEXSZ+1]; }; struct merge_index_conflict_data { @@ -42,10 +85,16 @@ int merge_trees_from_branches( git_index **index, git_repository *repo, const char *ours_name, const char *theirs_name, - git_merge_tree_opts *opts); + git_merge_options *opts); + +int merge_commits_from_branches( + git_index **index, git_repository *repo, + const char *ours_name, const char *theirs_name, + git_merge_options *opts); -int merge_branches(git_merge_result **result, git_repository *repo, - const char *ours_branch, const char *theirs_branch, git_merge_opts *opts); +int merge_branches(git_repository *repo, + const char *ours_branch, const char *theirs_branch, + git_merge_options *merge_opts, git_checkout_options *checkout_opts); int merge_test_diff_list(git_merge_diff_list *diff_list, const struct merge_index_entry expected[], size_t expected_len); diff -Nru libgit2-0.20.0/tests/merge/trees/automerge.c libgit2-0.22.2/tests/merge/trees/automerge.c --- libgit2-0.20.0/tests/merge/trees/automerge.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/merge/trees/automerge.c 2015-03-24 16:10:45.000000000 +0000 @@ -54,28 +54,6 @@ "", \ "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5" } -#define AUTOMERGEABLE_MERGED_FILE \ - "this file is changed in master\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is changed in branch\n" - -#define AUTOMERGEABLE_MERGED_FILE_CRLF \ - "this file is changed in master\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is changed in branch\r\n" - // Fixture setup and teardown void test_merge_trees_automerge__initialize(void) { @@ -91,7 +69,7 @@ { git_index *index; const git_index_entry *entry; - git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; git_blob *blob; struct merge_index_entry merge_index_entries[] = { @@ -121,7 +99,7 @@ cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL); cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE)); - cl_git_pass(git_object_lookup((git_object **)&blob, repo, &entry->oid, GIT_OBJ_BLOB)); + cl_git_pass(git_object_lookup((git_object **)&blob, repo, &entry->id, GIT_OBJ_BLOB)); cl_assert(memcmp(git_blob_rawcontent(blob), AUTOMERGEABLE_MERGED_FILE, (size_t)entry->file_size) == 0); git_index_free(index); @@ -131,7 +109,7 @@ void test_merge_trees_automerge__favor_ours(void) { git_index *index; - git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; struct merge_index_entry merge_index_entries[] = { ADDED_IN_MASTER_INDEX_ENTRY, @@ -149,7 +127,7 @@ REMOVED_IN_MASTER_REUC_ENTRY, }; - opts.automerge_flags = GIT_MERGE_AUTOMERGE_FAVOR_OURS; + opts.file_favor = GIT_MERGE_FILE_FAVOR_OURS; cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts)); @@ -162,7 +140,7 @@ void test_merge_trees_automerge__favor_theirs(void) { git_index *index; - git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; struct merge_index_entry merge_index_entries[] = { ADDED_IN_MASTER_INDEX_ENTRY, @@ -180,7 +158,7 @@ REMOVED_IN_MASTER_REUC_ENTRY, }; - opts.automerge_flags = GIT_MERGE_AUTOMERGE_FAVOR_THEIRS; + opts.file_favor = GIT_MERGE_FILE_FAVOR_THEIRS; cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts)); @@ -193,7 +171,7 @@ void test_merge_trees_automerge__unrelated(void) { git_index *index; - git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; struct merge_index_entry merge_index_entries[] = { { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, diff -Nru libgit2-0.20.0/tests/merge/trees/commits.c libgit2-0.22.2/tests/merge/trees/commits.c --- libgit2-0.20.0/tests/merge/trees/commits.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/merge/trees/commits.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,131 @@ +#include "clar_libgit2.h" +#include "git2/repository.h" +#include "git2/merge.h" +#include "merge.h" +#include "../merge_helpers.h" + +static git_repository *repo; + +#define TEST_REPO_PATH "merge-resolve" + +void test_merge_trees_commits__initialize(void) +{ + repo = cl_git_sandbox_init(TEST_REPO_PATH); +} + +void test_merge_trees_commits__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_merge_trees_commits__automerge(void) +{ + git_index *index; + const git_index_entry *entry; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + git_blob *blob; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, + { 0100644, "f2e1550a0c9e53d5811175864a29536642ae3821", 0, "automergeable.txt" }, + { 0100644, "4eb04c9e79e88f6640d01ff5b25ca2a60764f216", 0, "changed-in-branch.txt" }, + { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" }, + + { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" }, + { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" }, + { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" }, + + { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" }, + }; + + struct merge_reuc_entry merge_reuc_entries[] = { + { "automergeable.txt", 0100644, 0100644, 0100644, \ + "6212c31dab5e482247d7977e4f0dd3601decf13b", \ + "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", \ + "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe" }, + { "removed-in-branch.txt", 0100644, 0100644, 0, \ + "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \ + "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \ + "" }, + { "removed-in-master.txt", 0100644, 0, 0100644, \ + "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", \ + "", \ + "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5" }, + }; + + cl_git_pass(merge_commits_from_branches(&index, repo, "master", "branch", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 8)); + cl_assert(merge_test_reuc(index, merge_reuc_entries, 3)); + + cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL); + cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE)); + + cl_git_pass(git_object_lookup((git_object **)&blob, repo, &entry->id, GIT_OBJ_BLOB)); + cl_assert(memcmp(git_blob_rawcontent(blob), AUTOMERGEABLE_MERGED_FILE, (size_t)entry->file_size) == 0); + + git_index_free(index); + git_blob_free(blob); +} + +void test_merge_trees_commits__no_ancestor(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, + { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 2, "automergeable.txt" }, + { 0100644, "d07ec190c306ec690bac349e87d01c4358e49bb2", 3, "automergeable.txt" }, + { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" }, + { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" }, + { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" }, + { 0100644, "4b253da36a0ae8bfce63aeabd8c5b58429925594", 3, "conflicting.txt" }, + { 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, "new-in-unrelated1.txt" }, + { 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, "new-in-unrelated2.txt" }, + { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, + { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" }, + }; + + cl_git_pass(merge_commits_from_branches(&index, repo, "master", "unrelated", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 11)); + + git_index_free(index); +} + + +void test_merge_trees_commits__df_conflict(void) +{ + git_index *index; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "49130a28ef567af9a6a6104c38773fedfa5f9742", 2, "dir-10" }, + { 0100644, "6c06dcd163587c2cc18be44857e0b71116382aeb", 3, "dir-10" }, + { 0100644, "43aafd43bea779ec74317dc361f45ae3f532a505", 0, "dir-6" }, + { 0100644, "a031a28ae70e33a641ce4b8a8f6317f1ab79dee4", 3, "dir-7" }, + { 0100644, "5012fd565b1393bdfda1805d4ec38ce6619e1fd1", 1, "dir-7/file.txt" }, + { 0100644, "a5563304ddf6caba25cb50323a2ea6f7dbfcadca", 2, "dir-7/file.txt" }, + { 0100644, "e9ad6ec3e38364a3d07feda7c4197d4d845c53b5", 0, "dir-8" }, + { 0100644, "3ef4d30382ca33fdeba9fda895a99e0891ba37aa", 2, "dir-9" }, + { 0100644, "fc4c636d6515e9e261f9260dbcf3cc6eca97ea08", 1, "dir-9/file.txt" }, + { 0100644, "76ab0e2868197ec158ddd6c78d8a0d2fd73d38f9", 3, "dir-9/file.txt" }, + { 0100644, "5c2411f8075f48a6b2fdb85ebc0d371747c4df15", 0, "file-1/new" }, + { 0100644, "a39a620dae5bc8b4e771cd4d251b7d080401a21e", 1, "file-2" }, + { 0100644, "d963979c237d08b6ba39062ee7bf64c7d34a27f8", 2, "file-2" }, + { 0100644, "5c341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d", 0, "file-2/new" }, + { 0100644, "9efe7723802d4305142eee177e018fee1572c4f4", 0, "file-3/new" }, + { 0100644, "bacac9b3493509aa15e1730e1545fc0919d1dae0", 1, "file-4" }, + { 0100644, "7663fce0130db092936b137cabd693ec234eb060", 3, "file-4" }, + { 0100644, "e49f917b448d1340b31d76e54ba388268fd4c922", 0, "file-4/new" }, + { 0100644, "cab2cf23998b40f1af2d9d9a756dc9e285a8df4b", 2, "file-5/new" }, + { 0100644, "f5504f36e6f4eb797a56fc5bac6c6c7f32969bf2", 3, "file-5/new" }, + }; + + cl_git_pass(merge_trees_from_branches(&index, repo, "df_side1", "df_side2", &opts)); + + cl_assert(merge_test_index(index, merge_index_entries, 20)); + + git_index_free(index); +} diff -Nru libgit2-0.20.0/tests/merge/trees/renames.c libgit2-0.22.2/tests/merge/trees/renames.c --- libgit2-0.20.0/tests/merge/trees/renames.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/merge/trees/renames.c 2015-03-24 16:10:45.000000000 +0000 @@ -27,7 +27,7 @@ void test_merge_trees_renames__index(void) { git_index *index; - git_merge_tree_opts *opts = NULL; + git_merge_options *opts = NULL; struct merge_index_entry merge_index_entries[] = { { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" }, @@ -205,7 +205,7 @@ void test_merge_trees_renames__no_rename_index(void) { git_index *index; - git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; struct merge_index_entry merge_index_entries[] = { { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" }, diff -Nru libgit2-0.20.0/tests/merge/trees/treediff.c libgit2-0.22.2/tests/merge/trees/treediff.c --- libgit2-0.20.0/tests/merge/trees/treediff.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/merge/trees/treediff.c 2015-03-24 16:10:45.000000000 +0000 @@ -3,7 +3,7 @@ #include "merge.h" #include "../merge_helpers.h" #include "diff.h" -#include "hashsig.h" +#include "git2/sys/hashsig.h" static git_repository *repo; @@ -44,7 +44,7 @@ git_oid ancestor_oid, ours_oid, theirs_oid; git_tree *ancestor_tree, *ours_tree, *theirs_tree; - git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; opts.flags |= GIT_MERGE_TREE_FIND_RENAMES; opts.target_limit = 1000; opts.rename_threshold = 50; diff -Nru libgit2-0.20.0/tests/merge/trees/trivial.c libgit2-0.22.2/tests/merge/trees/trivial.c --- libgit2-0.20.0/tests/merge/trees/trivial.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/merge/trees/trivial.c 2015-03-24 16:10:45.000000000 +0000 @@ -25,15 +25,13 @@ } -static int merge_trivial(git_index **index, const char *ours, const char *theirs, bool automerge) +static int merge_trivial(git_index **index, const char *ours, const char *theirs) { git_commit *our_commit, *their_commit, *ancestor_commit; git_tree *our_tree, *their_tree, *ancestor_tree; git_oid our_oid, their_oid, ancestor_oid; git_buf branch_buf = GIT_BUF_INIT; - git_merge_tree_opts opts = GIT_MERGE_TREE_OPTS_INIT; - - opts.automerge_flags |= automerge ? 0 : GIT_MERGE_AUTOMERGE_NONE; + git_merge_options opts = GIT_MERGE_OPTIONS_INIT; git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours); cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr)); @@ -86,7 +84,7 @@ git_index *result; const git_index_entry *entry; - cl_git_pass(merge_trivial(&result, "trivial-2alt", "trivial-2alt-branch", 0)); + cl_git_pass(merge_trivial(&result, "trivial-2alt", "trivial-2alt-branch")); cl_assert(entry = git_index_get_bypath(result, "new-in-branch.txt", 0)); cl_assert(git_index_reuc_entrycount(result) == 0); @@ -101,7 +99,7 @@ git_index *result; const git_index_entry *entry; - cl_git_pass(merge_trivial(&result, "trivial-3alt", "trivial-3alt-branch", 0)); + cl_git_pass(merge_trivial(&result, "trivial-3alt", "trivial-3alt-branch")); cl_assert(entry = git_index_get_bypath(result, "new-in-3alt.txt", 0)); cl_assert(git_index_reuc_entrycount(result) == 0); @@ -116,7 +114,7 @@ git_index *result; const git_index_entry *entry; - cl_git_pass(merge_trivial(&result, "trivial-4", "trivial-4-branch", 0)); + cl_git_pass(merge_trivial(&result, "trivial-4", "trivial-4-branch")); cl_assert((entry = git_index_get_bypath(result, "new-and-different.txt", 0)) == NULL); cl_assert(git_index_reuc_entrycount(result) == 0); @@ -134,7 +132,7 @@ git_index *result; const git_index_entry *entry; - cl_git_pass(merge_trivial(&result, "trivial-5alt-1", "trivial-5alt-1-branch", 0)); + cl_git_pass(merge_trivial(&result, "trivial-5alt-1", "trivial-5alt-1-branch")); cl_assert(entry = git_index_get_bypath(result, "new-and-same.txt", 0)); cl_assert(git_index_reuc_entrycount(result) == 0); @@ -149,7 +147,7 @@ git_index *result; const git_index_entry *entry; - cl_git_pass(merge_trivial(&result, "trivial-5alt-2", "trivial-5alt-2-branch", 0)); + cl_git_pass(merge_trivial(&result, "trivial-5alt-2", "trivial-5alt-2-branch")); cl_assert(entry = git_index_get_bypath(result, "modified-to-same.txt", 0)); cl_assert(git_index_reuc_entrycount(result) == 0); @@ -163,26 +161,9 @@ { git_index *result; const git_index_entry *entry; - - cl_git_pass(merge_trivial(&result, "trivial-6", "trivial-6-branch", 0)); - - cl_assert((entry = git_index_get_bypath(result, "removed-in-both.txt", 0)) == NULL); - cl_assert(git_index_reuc_entrycount(result) == 0); - - cl_assert(merge_trivial_conflict_entrycount(result) == 1); - cl_assert(entry = git_index_get_bypath(result, "removed-in-both.txt", 1)); - - git_index_free(result); -} - -/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */ -void test_merge_trees_trivial__6_automerge(void) -{ - git_index *result; - const git_index_entry *entry; const git_index_reuc_entry *reuc; - cl_git_pass(merge_trivial(&result, "trivial-6", "trivial-6-branch", 1)); + cl_git_pass(merge_trivial(&result, "trivial-6", "trivial-6-branch")); cl_assert((entry = git_index_get_bypath(result, "removed-in-both.txt", 0)) == NULL); cl_assert(git_index_reuc_entrycount(result) == 1); @@ -198,27 +179,9 @@ { git_index *result; const git_index_entry *entry; - - cl_git_pass(merge_trivial(&result, "trivial-8", "trivial-8-branch", 0)); - - cl_assert((entry = git_index_get_bypath(result, "removed-in-8.txt", 0)) == NULL); - cl_assert(git_index_reuc_entrycount(result) == 0); - - cl_assert(merge_trivial_conflict_entrycount(result) == 2); - cl_assert(entry = git_index_get_bypath(result, "removed-in-8.txt", 1)); - cl_assert(entry = git_index_get_bypath(result, "removed-in-8.txt", 3)); - - git_index_free(result); -} - -/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */ -void test_merge_trees_trivial__8_automerge(void) -{ - git_index *result; - const git_index_entry *entry; const git_index_reuc_entry *reuc; - cl_git_pass(merge_trivial(&result, "trivial-8", "trivial-8-branch", 1)); + cl_git_pass(merge_trivial(&result, "trivial-8", "trivial-8-branch")); cl_assert((entry = git_index_get_bypath(result, "removed-in-8.txt", 0)) == NULL); @@ -236,25 +199,7 @@ git_index *result; const git_index_entry *entry; - cl_git_pass(merge_trivial(&result, "trivial-7", "trivial-7-branch", 0)); - - cl_assert((entry = git_index_get_bypath(result, "removed-in-7.txt", 0)) == NULL); - cl_assert(git_index_reuc_entrycount(result) == 0); - - cl_assert(merge_trivial_conflict_entrycount(result) == 2); - cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 1)); - cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 3)); - - git_index_free(result); -} - -/* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge */ -void test_merge_trees_trivial__7_automerge(void) -{ - git_index *result; - const git_index_entry *entry; - - cl_git_pass(merge_trivial(&result, "trivial-7", "trivial-7-branch", 0)); + cl_git_pass(merge_trivial(&result, "trivial-7", "trivial-7-branch")); cl_assert((entry = git_index_get_bypath(result, "removed-in-7.txt", 0)) == NULL); cl_assert(git_index_reuc_entrycount(result) == 0); @@ -271,27 +216,9 @@ { git_index *result; const git_index_entry *entry; - - cl_git_pass(merge_trivial(&result, "trivial-10", "trivial-10-branch", 0)); - - cl_assert((entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 0)) == NULL); - cl_assert(git_index_reuc_entrycount(result) == 0); - - cl_assert(merge_trivial_conflict_entrycount(result) == 2); - cl_assert(entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 1)); - cl_assert(entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 2)); - - git_index_free(result); -} - -/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */ -void test_merge_trees_trivial__10_automerge(void) -{ - git_index *result; - const git_index_entry *entry; const git_index_reuc_entry *reuc; - cl_git_pass(merge_trivial(&result, "trivial-10", "trivial-10-branch", 1)); + cl_git_pass(merge_trivial(&result, "trivial-10", "trivial-10-branch")); cl_assert((entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 0)) == NULL); @@ -309,25 +236,7 @@ git_index *result; const git_index_entry *entry; - cl_git_pass(merge_trivial(&result, "trivial-9", "trivial-9-branch", 0)); - - cl_assert((entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 0)) == NULL); - cl_assert(git_index_reuc_entrycount(result) == 0); - - cl_assert(merge_trivial_conflict_entrycount(result) == 2); - cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 1)); - cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 2)); - - git_index_free(result); -} - -/* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge */ -void test_merge_trees_trivial__9_automerge(void) -{ - git_index *result; - const git_index_entry *entry; - - cl_git_pass(merge_trivial(&result, "trivial-9", "trivial-9-branch", 1)); + cl_git_pass(merge_trivial(&result, "trivial-9", "trivial-9-branch")); cl_assert((entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 0)) == NULL); cl_assert(git_index_reuc_entrycount(result) == 0); @@ -346,11 +255,11 @@ const git_index_entry *entry; git_oid expected_oid; - cl_git_pass(merge_trivial(&result, "trivial-13", "trivial-13-branch", 0)); + cl_git_pass(merge_trivial(&result, "trivial-13", "trivial-13-branch")); cl_assert(entry = git_index_get_bypath(result, "modified-in-13.txt", 0)); cl_git_pass(git_oid_fromstr(&expected_oid, "1cff9ec6a47a537380dedfdd17c9e76d74259a2b")); - cl_assert(git_oid_cmp(&entry->oid, &expected_oid) == 0); + cl_assert_equal_oid(&expected_oid, &entry->id); cl_assert(git_index_reuc_entrycount(result) == 0); cl_assert(merge_trivial_conflict_entrycount(result) == 0); @@ -365,11 +274,11 @@ const git_index_entry *entry; git_oid expected_oid; - cl_git_pass(merge_trivial(&result, "trivial-14", "trivial-14-branch", 0)); + cl_git_pass(merge_trivial(&result, "trivial-14", "trivial-14-branch")); cl_assert(entry = git_index_get_bypath(result, "modified-in-14-branch.txt", 0)); cl_git_pass(git_oid_fromstr(&expected_oid, "26153a3ff3649b6c2bb652d3f06878c6e0a172f9")); - cl_assert(git_oid_cmp(&entry->oid, &expected_oid) == 0); + cl_assert(git_oid_cmp(&entry->id, &expected_oid) == 0); cl_assert(git_index_reuc_entrycount(result) == 0); cl_assert(merge_trivial_conflict_entrycount(result) == 0); @@ -383,7 +292,7 @@ git_index *result; const git_index_entry *entry; - cl_git_pass(merge_trivial(&result, "trivial-11", "trivial-11-branch", 0)); + cl_git_pass(merge_trivial(&result, "trivial-11", "trivial-11-branch")); cl_assert((entry = git_index_get_bypath(result, "modified-in-both.txt", 0)) == NULL); cl_assert(git_index_reuc_entrycount(result) == 0); diff -Nru libgit2-0.20.0/tests/merge/workdir/analysis.c libgit2-0.22.2/tests/merge/workdir/analysis.c --- libgit2-0.20.0/tests/merge/workdir/analysis.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/merge/workdir/analysis.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,141 @@ +#include "clar_libgit2.h" +#include "git2/repository.h" +#include "git2/merge.h" +#include "git2/annotated_commit.h" +#include "git2/sys/index.h" +#include "merge.h" +#include "../merge_helpers.h" +#include "refs.h" +#include "posix.h" + +static git_repository *repo; +static git_index *repo_index; + +#define TEST_REPO_PATH "merge-resolve" +#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index" + +#define UPTODATE_BRANCH "master" +#define PREVIOUS_BRANCH "previous" + +#define FASTFORWARD_BRANCH "ff_branch" +#define FASTFORWARD_ID "fd89f8cffb663ac89095a0f9764902e93ceaca6a" + +#define NOFASTFORWARD_BRANCH "branch" +#define NOFASTFORWARD_ID "7cb63eed597130ba4abb87b3e544b85021905520" + + +// Fixture setup and teardown +void test_merge_workdir_analysis__initialize(void) +{ + repo = cl_git_sandbox_init(TEST_REPO_PATH); + git_repository_index(&repo_index, repo); +} + +void test_merge_workdir_analysis__cleanup(void) +{ + git_index_free(repo_index); + cl_git_sandbox_cleanup(); +} + +static void analysis_from_branch( + git_merge_analysis_t *merge_analysis, + git_merge_preference_t *merge_pref, + const char *branchname) +{ + git_buf refname = GIT_BUF_INIT; + git_reference *their_ref; + git_annotated_commit *their_head; + + git_buf_printf(&refname, "%s%s", GIT_REFS_HEADS_DIR, branchname); + + cl_git_pass(git_reference_lookup(&their_ref, repo, git_buf_cstr(&refname))); + cl_git_pass(git_annotated_commit_from_ref(&their_head, repo, their_ref)); + + cl_git_pass(git_merge_analysis(merge_analysis, merge_pref, repo, (const git_annotated_commit **)&their_head, 1)); + + git_buf_free(&refname); + git_annotated_commit_free(their_head); + git_reference_free(their_ref); +} + +void test_merge_workdir_analysis__fastforward(void) +{ + git_merge_analysis_t merge_analysis; + git_merge_preference_t merge_pref; + + analysis_from_branch(&merge_analysis, &merge_pref, FASTFORWARD_BRANCH); + cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD)); + cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL)); +} + +void test_merge_workdir_analysis__no_fastforward(void) +{ + git_merge_analysis_t merge_analysis; + git_merge_preference_t merge_pref; + + analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH); + cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, merge_analysis); +} + +void test_merge_workdir_analysis__uptodate(void) +{ + git_merge_analysis_t merge_analysis; + git_merge_preference_t merge_pref; + + analysis_from_branch(&merge_analysis, &merge_pref, UPTODATE_BRANCH); + cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, merge_analysis); +} + +void test_merge_workdir_analysis__uptodate_merging_prev_commit(void) +{ + git_merge_analysis_t merge_analysis; + git_merge_preference_t merge_pref; + + analysis_from_branch(&merge_analysis, &merge_pref, PREVIOUS_BRANCH); + cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, merge_analysis); +} + +void test_merge_workdir_analysis__unborn(void) +{ + git_merge_analysis_t merge_analysis; + git_merge_preference_t merge_pref; + git_buf master = GIT_BUF_INIT; + + git_buf_joinpath(&master, git_repository_path(repo), "refs/heads/master"); + p_unlink(git_buf_cstr(&master)); + + analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH); + cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD)); + cl_assert_equal_i(GIT_MERGE_ANALYSIS_UNBORN, (merge_analysis & GIT_MERGE_ANALYSIS_UNBORN)); + + git_buf_free(&master); +} + +void test_merge_workdir_analysis__fastforward_with_config_noff(void) +{ + git_config *config; + git_merge_analysis_t merge_analysis; + git_merge_preference_t merge_pref; + + git_repository_config(&config, repo); + git_config_set_string(config, "merge.ff", "false"); + + analysis_from_branch(&merge_analysis, &merge_pref, FASTFORWARD_BRANCH); + cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD)); + cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL)); + cl_assert_equal_i(GIT_MERGE_PREFERENCE_NO_FASTFORWARD, (merge_pref & GIT_MERGE_PREFERENCE_NO_FASTFORWARD)); +} + +void test_merge_workdir_analysis__no_fastforward_with_config_ffonly(void) +{ + git_config *config; + git_merge_analysis_t merge_analysis; + git_merge_preference_t merge_pref; + + git_repository_config(&config, repo); + git_config_set_string(config, "merge.ff", "only"); + + analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH); + cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL)); + cl_assert_equal_i(GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY, (merge_pref & GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY)); +} diff -Nru libgit2-0.20.0/tests/merge/workdir/dirty.c libgit2-0.22.2/tests/merge/workdir/dirty.c --- libgit2-0.20.0/tests/merge/workdir/dirty.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/merge/workdir/dirty.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,322 @@ +#include "clar_libgit2.h" +#include "git2/merge.h" +#include "buffer.h" +#include "merge.h" +#include "../merge_helpers.h" +#include "posix.h" + +#define TEST_REPO_PATH "merge-resolve" +#define MERGE_BRANCH_OID "7cb63eed597130ba4abb87b3e544b85021905520" + +#define AUTOMERGEABLE_MERGED_FILE \ + "this file is changed in master\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is automergeable\n" \ + "this file is changed in branch\n" + +#define CHANGED_IN_BRANCH_FILE \ + "changed in branch\n" + +static git_repository *repo; +static git_index *repo_index; + +static char *unaffected[][4] = { + { "added-in-master.txt", NULL }, + { "changed-in-master.txt", NULL }, + { "unchanged.txt", NULL }, + { "added-in-master.txt", "changed-in-master.txt", NULL }, + { "added-in-master.txt", "unchanged.txt", NULL }, + { "changed-in-master.txt", "unchanged.txt", NULL }, + { "added-in-master.txt", "changed-in-master.txt", "unchanged.txt", NULL }, + { "new_file.txt", NULL }, + { "new_file.txt", "unchanged.txt", NULL }, + { NULL }, +}; + +static char *affected[][5] = { + { "automergeable.txt", NULL }, + { "changed-in-branch.txt", NULL }, + { "conflicting.txt", NULL }, + { "removed-in-branch.txt", NULL }, + { "automergeable.txt", "changed-in-branch.txt", NULL }, + { "automergeable.txt", "conflicting.txt", NULL }, + { "automergeable.txt", "removed-in-branch.txt", NULL }, + { "changed-in-branch.txt", "conflicting.txt", NULL }, + { "changed-in-branch.txt", "removed-in-branch.txt", NULL }, + { "conflicting.txt", "removed-in-branch.txt", NULL }, + { "automergeable.txt", "changed-in-branch.txt", "conflicting.txt", NULL }, + { "automergeable.txt", "changed-in-branch.txt", "removed-in-branch.txt", NULL }, + { "automergeable.txt", "conflicting.txt", "removed-in-branch.txt", NULL }, + { "changed-in-branch.txt", "conflicting.txt", "removed-in-branch.txt", NULL }, + { "automergeable.txt", "changed-in-branch.txt", "conflicting.txt", "removed-in-branch.txt", NULL }, + { NULL }, +}; + +static char *result_contents[4][6] = { + { "automergeable.txt", AUTOMERGEABLE_MERGED_FILE, NULL, NULL }, + { "changed-in-branch.txt", CHANGED_IN_BRANCH_FILE, NULL, NULL }, + { "automergeable.txt", AUTOMERGEABLE_MERGED_FILE, "changed-in-branch.txt", CHANGED_IN_BRANCH_FILE, NULL, NULL }, + { NULL } +}; + +void test_merge_workdir_dirty__initialize(void) +{ + repo = cl_git_sandbox_init(TEST_REPO_PATH); + git_repository_index(&repo_index, repo); +} + +void test_merge_workdir_dirty__cleanup(void) +{ + git_index_free(repo_index); + cl_git_sandbox_cleanup(); +} + +static void set_core_autocrlf_to(git_repository *repo, bool value) +{ + git_config *cfg; + + cl_git_pass(git_repository_config(&cfg, repo)); + cl_git_pass(git_config_set_bool(cfg, "core.autocrlf", value)); + + git_config_free(cfg); +} + +static int merge_branch(void) +{ + git_oid their_oids[1]; + git_annotated_commit *their_head; + git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + int error; + + cl_git_pass(git_oid_fromstr(&their_oids[0], MERGE_BRANCH_OID)); + cl_git_pass(git_annotated_commit_lookup(&their_head, repo, &their_oids[0])); + + checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + error = git_merge(repo, (const git_annotated_commit **)&their_head, 1, &merge_opts, &checkout_opts); + + git_annotated_commit_free(their_head); + + return error; +} + +static void write_files(char *files[]) +{ + char *filename; + git_buf path = GIT_BUF_INIT, content = GIT_BUF_INIT; + size_t i; + + for (i = 0, filename = files[i]; filename; filename = files[++i]) { + git_buf_clear(&path); + git_buf_clear(&content); + + git_buf_printf(&path, "%s/%s", TEST_REPO_PATH, filename); + git_buf_printf(&content, "This is a dirty file in the working directory!\n\n" + "It will not be staged! Its filename is %s.\n", filename); + + cl_git_mkfile(path.ptr, content.ptr); + } + + git_buf_free(&path); + git_buf_free(&content); +} + +static void hack_index(char *files[]) +{ + char *filename; + struct stat statbuf; + git_buf path = GIT_BUF_INIT; + git_index_entry *entry; + size_t i; + + /* Update the index to suggest that checkout placed these files on + * disk, keeping the object id but updating the cache, which will + * emulate a Git implementation's different filter. + */ + for (i = 0, filename = files[i]; filename; filename = files[++i]) { + git_buf_clear(&path); + + cl_assert(entry = (git_index_entry *) + git_index_get_bypath(repo_index, filename, 0)); + + cl_git_pass(git_buf_printf(&path, "%s/%s", TEST_REPO_PATH, filename)); + cl_git_pass(p_stat(path.ptr, &statbuf)); + + entry->ctime.seconds = (git_time_t)statbuf.st_ctime; + entry->ctime.nanoseconds = 0; + entry->mtime.seconds = (git_time_t)statbuf.st_mtime; + entry->mtime.nanoseconds = 0; + entry->dev = statbuf.st_dev; + entry->ino = statbuf.st_ino; + entry->uid = statbuf.st_uid; + entry->gid = statbuf.st_gid; + entry->file_size = statbuf.st_size; + } + + git_buf_free(&path); +} + +static void stage_random_files(char *files[]) +{ + char *filename; + size_t i; + + write_files(files); + + for (i = 0, filename = files[i]; filename; filename = files[++i]) + cl_git_pass(git_index_add_bypath(repo_index, filename)); +} + +static void stage_content(char *content[]) +{ + git_reference *head; + git_object *head_object; + git_buf path = GIT_BUF_INIT; + char *filename, *text; + size_t i; + + cl_git_pass(git_repository_head(&head, repo)); + cl_git_pass(git_reference_peel(&head_object, head, GIT_OBJ_COMMIT)); + cl_git_pass(git_reset(repo, head_object, GIT_RESET_HARD, NULL, NULL, NULL)); + + for (i = 0, filename = content[i], text = content[++i]; + filename && text; + filename = content[++i], text = content[++i]) { + + git_buf_clear(&path); + + cl_git_pass(git_buf_printf(&path, "%s/%s", TEST_REPO_PATH, filename)); + + cl_git_mkfile(path.ptr, text); + cl_git_pass(git_index_add_bypath(repo_index, filename)); + } + + git_object_free(head_object); + git_reference_free(head); + git_buf_free(&path); +} + +static int merge_dirty_files(char *dirty_files[]) +{ + git_reference *head; + git_object *head_object; + int error; + + cl_git_pass(git_repository_head(&head, repo)); + cl_git_pass(git_reference_peel(&head_object, head, GIT_OBJ_COMMIT)); + cl_git_pass(git_reset(repo, head_object, GIT_RESET_HARD, NULL, NULL, NULL)); + + write_files(dirty_files); + + error = merge_branch(); + + git_object_free(head_object); + git_reference_free(head); + + return error; +} + +static int merge_differently_filtered_files(char *files[]) +{ + git_reference *head; + git_object *head_object; + int error; + + cl_git_pass(git_repository_head(&head, repo)); + cl_git_pass(git_reference_peel(&head_object, head, GIT_OBJ_COMMIT)); + cl_git_pass(git_reset(repo, head_object, GIT_RESET_HARD, NULL, NULL, NULL)); + + write_files(files); + hack_index(files); + + cl_git_pass(git_index_write(repo_index)); + + error = merge_branch(); + + git_object_free(head_object); + git_reference_free(head); + + return error; +} + +static int merge_staged_files(char *staged_files[]) +{ + stage_random_files(staged_files); + return merge_branch(); +} + +void test_merge_workdir_dirty__unaffected_dirty_files_allowed(void) +{ + char **files; + size_t i; + + for (i = 0, files = unaffected[i]; files[0]; files = unaffected[++i]) + cl_git_pass(merge_dirty_files(files)); +} + +void test_merge_workdir_dirty__unstaged_deletes_maintained(void) +{ + git_reference *head; + git_object *head_object; + + cl_git_pass(git_repository_head(&head, repo)); + cl_git_pass(git_reference_peel(&head_object, head, GIT_OBJ_COMMIT)); + cl_git_pass(git_reset(repo, head_object, GIT_RESET_HARD, NULL, NULL, NULL)); + + cl_git_pass(p_unlink("merge-resolve/unchanged.txt")); + + cl_git_pass(merge_branch()); + + git_object_free(head_object); + git_reference_free(head); +} + +void test_merge_workdir_dirty__affected_dirty_files_disallowed(void) +{ + char **files; + size_t i; + + for (i = 0, files = affected[i]; files[0]; files = affected[++i]) + cl_git_fail(merge_dirty_files(files)); +} + +void test_merge_workdir_dirty__staged_files_in_index_disallowed(void) +{ + char **files; + size_t i; + + for (i = 0, files = unaffected[i]; files[0]; files = unaffected[++i]) + cl_git_fail(merge_staged_files(files)); + + for (i = 0, files = affected[i]; files[0]; files = affected[++i]) + cl_git_fail(merge_staged_files(files)); +} + +void test_merge_workdir_dirty__identical_staged_files_allowed(void) +{ + char **content; + size_t i; + + set_core_autocrlf_to(repo, false); + + for (i = 0, content = result_contents[i]; content[0]; content = result_contents[++i]) { + stage_content(content); + + git_index_write(repo_index); + cl_git_pass(merge_branch()); + } +} + +void test_merge_workdir_dirty__honors_cache(void) +{ + char **files; + size_t i; + + for (i = 0, files = affected[i]; files[0]; files = affected[++i]) + cl_git_pass(merge_differently_filtered_files(files)); +} diff -Nru libgit2-0.20.0/tests/merge/workdir/fastforward.c libgit2-0.22.2/tests/merge/workdir/fastforward.c --- libgit2-0.20.0/tests/merge/workdir/fastforward.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/merge/workdir/fastforward.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,148 +0,0 @@ -#include "clar_libgit2.h" -#include "git2/repository.h" -#include "git2/merge.h" -#include "git2/sys/index.h" -#include "merge.h" -#include "../merge_helpers.h" -#include "refs.h" - -static git_repository *repo; -static git_index *repo_index; - -#define TEST_REPO_PATH "merge-resolve" -#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index" - -#define THEIRS_FASTFORWARD_BRANCH "ff_branch" -#define THEIRS_FASTFORWARD_OID "fd89f8cffb663ac89095a0f9764902e93ceaca6a" - -#define THEIRS_NOFASTFORWARD_BRANCH "branch" -#define THEIRS_NOFASTFORWARD_OID "7cb63eed597130ba4abb87b3e544b85021905520" - - -// Fixture setup and teardown -void test_merge_workdir_fastforward__initialize(void) -{ - repo = cl_git_sandbox_init(TEST_REPO_PATH); - git_repository_index(&repo_index, repo); -} - -void test_merge_workdir_fastforward__cleanup(void) -{ - git_index_free(repo_index); - cl_git_sandbox_cleanup(); -} - -static git_merge_result *merge_fastforward_branch(int flags) -{ - git_reference *their_ref; - git_merge_head *their_heads[1]; - git_merge_result *result; - git_merge_opts opts = GIT_MERGE_OPTS_INIT; - - opts.merge_flags = flags; - - cl_git_pass(git_reference_lookup(&their_ref, repo, GIT_REFS_HEADS_DIR THEIRS_FASTFORWARD_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, their_ref)); - - cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts)); - - git_merge_head_free(their_heads[0]); - git_reference_free(their_ref); - - return result; -} - -void test_merge_workdir_fastforward__fastforward(void) -{ - git_merge_result *result; - git_oid expected, ff_oid; - - cl_git_pass(git_oid_fromstr(&expected, THEIRS_FASTFORWARD_OID)); - - cl_assert(result = merge_fastforward_branch(0)); - cl_assert(git_merge_result_is_fastforward(result)); - cl_git_pass(git_merge_result_fastforward_oid(&ff_oid, result)); - cl_assert(git_oid_cmp(&ff_oid, &expected) == 0); - - git_merge_result_free(result); -} - -void test_merge_workdir_fastforward__fastforward_only(void) -{ - git_merge_result *result; - git_merge_opts opts = GIT_MERGE_OPTS_INIT; - git_reference *their_ref; - git_merge_head *their_head; - int error; - - opts.merge_flags = GIT_MERGE_FASTFORWARD_ONLY; - - cl_git_pass(git_reference_lookup(&their_ref, repo, GIT_REFS_HEADS_DIR THEIRS_NOFASTFORWARD_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_head, repo, their_ref)); - - cl_git_fail((error = git_merge(&result, repo, (const git_merge_head **)&their_head, 1, &opts))); - cl_assert(error == GIT_ENONFASTFORWARD); - - git_merge_head_free(their_head); - git_reference_free(their_ref); -} - -void test_merge_workdir_fastforward__no_fastforward(void) -{ - git_merge_result *result; - - struct merge_index_entry merge_index_entries[] = { - { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, - { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" }, - { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" }, - { 0100644, "bd9cb4cd0a770cb9adcb5fce212142ef40ea1c35", 0, "changed-in-master.txt" }, - { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" }, - { 0100644, "364bbe4ce80c7bd31e6307dce77d46e3e1759fb3", 0, "new-in-ff.txt" }, - { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, - { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" }, - }; - - cl_assert(result = merge_fastforward_branch(GIT_MERGE_NO_FASTFORWARD)); - cl_assert(!git_merge_result_is_fastforward(result)); - - cl_assert(merge_test_index(repo_index, merge_index_entries, 8)); - cl_assert(git_index_reuc_entrycount(repo_index) == 0); - - git_merge_result_free(result); -} - -void test_merge_workdir_fastforward__uptodate(void) -{ - git_reference *their_ref; - git_merge_head *their_heads[1]; - git_merge_result *result; - - cl_git_pass(git_reference_lookup(&their_ref, repo, GIT_HEAD_FILE)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, their_ref)); - - cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, NULL)); - - cl_assert(git_merge_result_is_uptodate(result)); - - git_merge_head_free(their_heads[0]); - git_reference_free(their_ref); - git_merge_result_free(result); -} - -void test_merge_workdir_fastforward__uptodate_merging_prev_commit(void) -{ - git_oid their_oid; - git_merge_head *their_heads[1]; - git_merge_result *result; - - cl_git_pass(git_oid_fromstr(&their_oid, "c607fc30883e335def28cd686b51f6cfa02b06ec")); - cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oid)); - - cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, NULL)); - - cl_assert(git_merge_result_is_uptodate(result)); - - git_merge_head_free(their_heads[0]); - git_merge_result_free(result); -} - diff -Nru libgit2-0.20.0/tests/merge/workdir/renames.c libgit2-0.22.2/tests/merge/workdir/renames.c --- libgit2-0.20.0/tests/merge/workdir/renames.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/merge/workdir/renames.c 2015-03-24 16:10:45.000000000 +0000 @@ -17,7 +17,14 @@ // Fixture setup and teardown void test_merge_workdir_renames__initialize(void) { + git_config *cfg; + repo = cl_git_sandbox_init(TEST_REPO_PATH); + + /* Ensure that the user's merge.conflictstyle doesn't interfere */ + cl_git_pass(git_repository_config(&cfg, repo)); + cl_git_pass(git_config_set_string(cfg, "merge.conflictstyle", "merge")); + git_config_free(cfg); } void test_merge_workdir_renames__cleanup(void) @@ -27,8 +34,7 @@ void test_merge_workdir_renames__renames(void) { - git_merge_result *result; - git_merge_opts opts = GIT_MERGE_OPTS_INIT; + git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT; struct merge_index_entry merge_index_entries[] = { { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" }, @@ -57,20 +63,18 @@ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed.txt~rename_conflict_theirs" }, }; - opts.merge_tree_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES; - opts.merge_tree_opts.rename_threshold = 50; + merge_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES; + merge_opts.rename_threshold = 50; - cl_git_pass(merge_branches(&result, repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &opts)); + cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &merge_opts, NULL)); cl_assert(merge_test_workdir(repo, merge_index_entries, 24)); - - git_merge_result_free(result); } void test_merge_workdir_renames__ours(void) { git_index *index; - git_merge_result *result; - git_merge_opts opts = GIT_MERGE_OPTS_INIT; + git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; struct merge_index_entry merge_index_entries[] = { { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" }, @@ -95,23 +99,21 @@ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "7-both-renamed.txt" }, }; - opts.merge_tree_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES; - opts.merge_tree_opts.rename_threshold = 50; - opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS; + merge_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES; + merge_opts.rename_threshold = 50; + checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS; - cl_git_pass(merge_branches(&result, repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &opts)); + cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &merge_opts, &checkout_opts)); cl_git_pass(git_repository_index(&index, repo)); cl_git_pass(git_index_write(index)); cl_assert(merge_test_workdir(repo, merge_index_entries, 20)); - git_merge_result_free(result); git_index_free(index); } void test_merge_workdir_renames__similar(void) { - git_merge_result *result; - git_merge_opts opts = GIT_MERGE_OPTS_INIT; + git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT; /* * Note: this differs slightly from the core git merge result - there, 4a is @@ -145,12 +147,10 @@ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed.txt~rename_conflict_theirs" }, }; - opts.merge_tree_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES; - opts.merge_tree_opts.rename_threshold = 50; + merge_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES; + merge_opts.rename_threshold = 50; - cl_git_pass(merge_branches(&result, repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &opts)); + cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &merge_opts, NULL)); cl_assert(merge_test_workdir(repo, merge_index_entries, 24)); - - git_merge_result_free(result); } diff -Nru libgit2-0.20.0/tests/merge/workdir/setup.c libgit2-0.22.2/tests/merge/workdir/setup.c --- libgit2-0.20.0/tests/merge/workdir/setup.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/merge/workdir/setup.c 2015-03-24 16:10:45.000000000 +0000 @@ -71,46 +71,20 @@ git_buf_free(&file_path_buf); } -/* git merge octo1 */ +/* git merge --no-ff octo1 */ void test_merge_workdir_setup__one_branch(void) { git_oid our_oid; git_reference *octo1_ref; - git_merge_head *our_head, *their_heads[1]; + git_annotated_commit *our_head, *their_heads[1]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, 0)); - - cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n")); - cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); - cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'\n")); - - git_reference_free(octo1_ref); - - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); -} - -/* git merge --no-ff octo1 */ -void test_merge_workdir_setup__no_fastforward(void) -{ - git_oid our_oid; - git_reference *octo1_ref; - git_merge_head *our_head, *their_heads[1]; - - cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); - - cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); - - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, GIT_MERGE_NO_FASTFORWARD)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 1)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); @@ -118,33 +92,33 @@ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'\n")); git_reference_free(octo1_ref); - - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); + + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); } -/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 */ +/* git merge --no-ff 16f825815cfd20a07a75c71554e82d8eede0b061 */ void test_merge_workdir_setup__one_oid(void) { git_oid our_oid; git_oid octo1_oid; - git_merge_head *our_head, *their_heads[1]; + git_annotated_commit *our_head, *their_heads[1]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID)); - cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_oid)); + cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &octo1_oid)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 1)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'\n")); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); } /* git merge octo1 octo2 */ @@ -153,30 +127,30 @@ git_oid our_oid; git_reference *octo1_ref; git_reference *octo2_ref; - git_merge_head *our_head, *their_heads[2]; + git_annotated_commit *our_head, *their_heads[2]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref)); cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 2)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO2_BRANCH "'\n")); git_reference_free(octo1_ref); git_reference_free(octo2_ref); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); } /* git merge octo1 octo2 octo3 */ @@ -186,35 +160,35 @@ git_reference *octo1_ref; git_reference *octo2_ref; git_reference *octo3_ref; - git_merge_head *our_head, *their_heads[3]; + git_annotated_commit *our_head, *their_heads[3]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref)); cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref)); cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo3_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[2], repo, octo3_ref)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 3)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "', '" OCTO2_BRANCH "' and '" OCTO3_BRANCH "'\n")); git_reference_free(octo1_ref); git_reference_free(octo2_ref); git_reference_free(octo3_ref); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); - git_merge_head_free(their_heads[2]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); + git_annotated_commit_free(their_heads[2]); } /* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 158dc7bedb202f5b26502bf3574faa7f4238d56c 50ce7d7d01217679e26c55939eef119e0c93e272 */ @@ -224,31 +198,31 @@ git_oid octo1_oid; git_oid octo2_oid; git_oid octo3_oid; - git_merge_head *our_head, *their_heads[3]; + git_annotated_commit *our_head, *their_heads[3]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID)); - cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_oid)); + cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &octo1_oid)); cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID)); - cl_git_pass(git_merge_head_from_oid(&their_heads[1], repo, &octo2_oid)); + cl_git_pass(git_annotated_commit_lookup(&their_heads[1], repo, &octo2_oid)); cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID)); - cl_git_pass(git_merge_head_from_oid(&their_heads[2], repo, &octo3_oid)); + cl_git_pass(git_annotated_commit_lookup(&their_heads[2], repo, &octo3_oid)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 3)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; commit '" OCTO2_OID "'; commit '" OCTO3_OID "'\n")); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); - git_merge_head_free(their_heads[2]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); + git_annotated_commit_free(their_heads[2]); } /* git merge octo1 158dc7bedb202f5b26502bf3574faa7f4238d56c */ @@ -257,29 +231,29 @@ git_oid our_oid; git_reference *octo1_ref; git_oid octo2_oid; - git_merge_head *our_head, *their_heads[2]; + git_annotated_commit *our_head, *their_heads[2]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref)); cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID)); - cl_git_pass(git_merge_head_from_oid(&their_heads[1], repo, &octo2_oid)); + cl_git_pass(git_annotated_commit_lookup(&their_heads[1], repo, &octo2_oid)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 2)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'; commit '" OCTO2_OID "'\n")); git_reference_free(octo1_ref); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); } /* git merge octo1 158dc7bedb202f5b26502bf3574faa7f4238d56c octo3 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae */ @@ -290,38 +264,38 @@ git_oid octo2_oid; git_reference *octo3_ref; git_oid octo4_oid; - git_merge_head *our_head, *their_heads[4]; + git_annotated_commit *our_head, *their_heads[4]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref)); cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID)); - cl_git_pass(git_merge_head_from_oid(&their_heads[1], repo, &octo2_oid)); + cl_git_pass(git_annotated_commit_lookup(&their_heads[1], repo, &octo2_oid)); cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo3_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[2], repo, octo3_ref)); cl_git_pass(git_oid_fromstr(&octo4_oid, OCTO4_OID)); - cl_git_pass(git_merge_head_from_oid(&their_heads[3], repo, &octo4_oid)); + cl_git_pass(git_annotated_commit_lookup(&their_heads[3], repo, &octo4_oid)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 4, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 4)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO3_BRANCH "'; commit '" OCTO2_OID "'; commit '" OCTO4_OID "'\n")); git_reference_free(octo1_ref); git_reference_free(octo3_ref); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); - git_merge_head_free(their_heads[2]); - git_merge_head_free(their_heads[3]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); + git_annotated_commit_free(their_heads[2]); + git_annotated_commit_free(their_heads[3]); } /* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 octo2 50ce7d7d01217679e26c55939eef119e0c93e272 octo4 */ @@ -332,38 +306,38 @@ git_reference *octo2_ref; git_oid octo3_oid; git_reference *octo4_ref; - git_merge_head *our_head, *their_heads[4]; + git_annotated_commit *our_head, *their_heads[4]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID)); - cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_oid)); + cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &octo1_oid)); cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref)); cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID)); - cl_git_pass(git_merge_head_from_oid(&their_heads[2], repo, &octo3_oid)); + cl_git_pass(git_annotated_commit_lookup(&their_heads[2], repo, &octo3_oid)); cl_git_pass(git_reference_lookup(&octo4_ref, repo, GIT_REFS_HEADS_DIR OCTO4_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[3], repo, octo4_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[3], repo, octo4_ref)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 4, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 4)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; branches '" OCTO2_BRANCH "' and '" OCTO4_BRANCH "'; commit '" OCTO3_OID "'\n")); git_reference_free(octo2_ref); git_reference_free(octo4_ref); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); - git_merge_head_free(their_heads[2]); - git_merge_head_free(their_heads[3]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); + git_annotated_commit_free(their_heads[2]); + git_annotated_commit_free(their_heads[3]); } /* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 octo2 50ce7d7d01217679e26c55939eef119e0c93e272 octo4 octo5 */ @@ -375,43 +349,43 @@ git_oid octo3_oid; git_reference *octo4_ref; git_reference *octo5_ref; - git_merge_head *our_head, *their_heads[5]; + git_annotated_commit *our_head, *their_heads[5]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID)); - cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_oid)); + cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &octo1_oid)); cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref)); cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID)); - cl_git_pass(git_merge_head_from_oid(&their_heads[2], repo, &octo3_oid)); + cl_git_pass(git_annotated_commit_lookup(&their_heads[2], repo, &octo3_oid)); cl_git_pass(git_reference_lookup(&octo4_ref, repo, GIT_REFS_HEADS_DIR OCTO4_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[3], repo, octo4_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[3], repo, octo4_ref)); cl_git_pass(git_reference_lookup(&octo5_ref, repo, GIT_REFS_HEADS_DIR OCTO5_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[4], repo, octo5_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[4], repo, octo5_ref)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 5, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 5)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n" OCTO5_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; branches '" OCTO2_BRANCH "', '" OCTO4_BRANCH "' and '" OCTO5_BRANCH "'; commit '" OCTO3_OID "'\n")); git_reference_free(octo2_ref); git_reference_free(octo4_ref); git_reference_free(octo5_ref); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); - git_merge_head_free(their_heads[2]); - git_merge_head_free(their_heads[3]); - git_merge_head_free(their_heads[4]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); + git_annotated_commit_free(their_heads[2]); + git_annotated_commit_free(their_heads[3]); + git_annotated_commit_free(their_heads[4]); } /* git merge octo1 octo1 octo1 */ @@ -421,35 +395,35 @@ git_reference *octo1_1_ref; git_reference *octo1_2_ref; git_reference *octo1_3_ref; - git_merge_head *our_head, *their_heads[3]; + git_annotated_commit *our_head, *their_heads[3]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_reference_lookup(&octo1_1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_1_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_1_ref)); cl_git_pass(git_reference_lookup(&octo1_2_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo1_2_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo1_2_ref)); cl_git_pass(git_reference_lookup(&octo1_3_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo1_3_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[2], repo, octo1_3_ref)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 3)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO1_OID "\n" OCTO1_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "', '" OCTO1_BRANCH "' and '" OCTO1_BRANCH "'\n")); git_reference_free(octo1_1_ref); git_reference_free(octo1_2_ref); git_reference_free(octo1_3_ref); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); - git_merge_head_free(their_heads[2]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); + git_annotated_commit_free(their_heads[2]); } /* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 16f825815cfd20a07a75c71554e82d8eede0b061 16f825815cfd20a07a75c71554e82d8eede0b061 */ @@ -459,31 +433,31 @@ git_oid octo1_1_oid; git_oid octo1_2_oid; git_oid octo1_3_oid; - git_merge_head *our_head, *their_heads[3]; + git_annotated_commit *our_head, *their_heads[3]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_oid_fromstr(&octo1_1_oid, OCTO1_OID)); - cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &octo1_1_oid)); + cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &octo1_1_oid)); cl_git_pass(git_oid_fromstr(&octo1_2_oid, OCTO1_OID)); - cl_git_pass(git_merge_head_from_oid(&their_heads[1], repo, &octo1_2_oid)); + cl_git_pass(git_annotated_commit_lookup(&their_heads[1], repo, &octo1_2_oid)); cl_git_pass(git_oid_fromstr(&octo1_3_oid, OCTO1_OID)); - cl_git_pass(git_merge_head_from_oid(&their_heads[2], repo, &octo1_3_oid)); + cl_git_pass(git_annotated_commit_lookup(&their_heads[2], repo, &octo1_3_oid)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 3)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO1_OID "\n" OCTO1_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; commit '" OCTO1_OID "'; commit '" OCTO1_OID "'\n")); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); - git_merge_head_free(their_heads[2]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); + git_annotated_commit_free(their_heads[2]); } static int create_remote_tracking_branch(const char *branch_name, const char *oid_str) @@ -534,27 +508,27 @@ { git_oid our_oid; git_reference *octo1_ref; - git_merge_head *our_head, *their_heads[1]; + git_annotated_commit *our_head, *their_heads[1]; cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID)); cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 1)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge remote-tracking branch 'refs/remotes/origin/" OCTO1_BRANCH "'\n")); git_reference_free(octo1_ref); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); } /* git merge refs/remotes/origin/octo1 refs/remotes/origin/octo2 */ @@ -563,33 +537,33 @@ git_oid our_oid; git_reference *octo1_ref; git_reference *octo2_ref; - git_merge_head *our_head, *their_heads[2]; + git_annotated_commit *our_head, *their_heads[2]; cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID)); cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID)); cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref)); cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 2)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge remote-tracking branches 'refs/remotes/origin/" OCTO1_BRANCH "' and 'refs/remotes/origin/" OCTO2_BRANCH "'\n")); git_reference_free(octo1_ref); git_reference_free(octo2_ref); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); } /* git merge refs/remotes/origin/octo1 refs/remotes/origin/octo2 refs/remotes/origin/octo3 */ @@ -599,39 +573,39 @@ git_reference *octo1_ref; git_reference *octo2_ref; git_reference *octo3_ref; - git_merge_head *our_head, *their_heads[3]; + git_annotated_commit *our_head, *their_heads[3]; cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID)); cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID)); cl_git_pass(create_remote_tracking_branch(OCTO3_BRANCH, OCTO3_OID)); cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref)); cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref)); cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO3_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo3_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[2], repo, octo3_ref)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 3)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge remote-tracking branches 'refs/remotes/origin/" OCTO1_BRANCH "', 'refs/remotes/origin/" OCTO2_BRANCH "' and 'refs/remotes/origin/" OCTO3_BRANCH "'\n")); git_reference_free(octo1_ref); git_reference_free(octo2_ref); git_reference_free(octo3_ref); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); - git_merge_head_free(their_heads[2]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); + git_annotated_commit_free(their_heads[2]); } /* git merge octo1 refs/remotes/origin/octo2 */ @@ -640,32 +614,32 @@ git_oid our_oid; git_reference *octo1_ref; git_reference *octo2_ref; - git_merge_head *our_head, *their_heads[2]; + git_annotated_commit *our_head, *their_heads[2]; cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID)); cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref)); cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 2)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "', remote-tracking branch 'refs/remotes/origin/" OCTO2_BRANCH "'\n")); git_reference_free(octo1_ref); git_reference_free(octo2_ref); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); } /* git merge refs/remotes/origin/octo1 octo2 */ @@ -674,32 +648,32 @@ git_oid our_oid; git_reference *octo1_ref; git_reference *octo2_ref; - git_merge_head *our_head, *their_heads[2]; + git_annotated_commit *our_head, *their_heads[2]; cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID)); cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref)); cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 2)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO2_BRANCH "', remote-tracking branch 'refs/remotes/origin/" OCTO1_BRANCH "'\n")); git_reference_free(octo1_ref); git_reference_free(octo2_ref); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); } /* git merge octo1 refs/remotes/origin/octo2 octo3 refs/remotes/origin/octo4 */ @@ -710,31 +684,31 @@ git_reference *octo2_ref; git_reference *octo3_ref; git_reference *octo4_ref; - git_merge_head *our_head, *their_heads[4]; + git_annotated_commit *our_head, *their_heads[4]; cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID)); cl_git_pass(create_remote_tracking_branch(OCTO4_BRANCH, OCTO4_OID)); cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref)); cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[1], repo, octo2_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref)); cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[2], repo, octo3_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[2], repo, octo3_ref)); cl_git_pass(git_reference_lookup(&octo4_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO4_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[3], repo, octo4_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[3], repo, octo4_ref)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 4, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 4)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO3_BRANCH "', remote-tracking branches 'refs/remotes/origin/" OCTO2_BRANCH "' and 'refs/remotes/origin/" OCTO4_BRANCH "'\n")); git_reference_free(octo1_ref); @@ -742,11 +716,11 @@ git_reference_free(octo3_ref); git_reference_free(octo4_ref); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); - git_merge_head_free(their_heads[2]); - git_merge_head_free(their_heads[3]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); + git_annotated_commit_free(their_heads[2]); + git_annotated_commit_free(their_heads[3]); } /* git pull origin branch octo1 */ @@ -754,23 +728,23 @@ { git_oid our_oid; git_oid octo1_1_oid; - git_merge_head *our_head, *their_heads[1]; + git_annotated_commit *our_head, *their_heads[1]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_oid_fromstr(&octo1_1_oid, OCTO1_OID)); - cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_1_oid)); + cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_1_oid)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 1, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 1)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch 'octo1' of http://remote.url/repo.git\n")); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); } /* git pull origin octo1 octo2 */ @@ -779,27 +753,27 @@ git_oid our_oid; git_oid octo1_oid; git_oid octo2_oid; - git_merge_head *our_head, *their_heads[2]; + git_annotated_commit *our_head, *their_heads[2]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID)); - cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_oid)); + cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_oid)); cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID)); - cl_git_pass(git_merge_head_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.url/repo.git", &octo2_oid)); + cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.url/repo.git", &octo2_oid)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 2, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 2)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO2_BRANCH "' of http://remote.url/repo.git\n")); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); } /* git pull origin octo1 octo2 octo3 */ @@ -809,31 +783,31 @@ git_oid octo1_oid; git_oid octo2_oid; git_oid octo3_oid; - git_merge_head *our_head, *their_heads[3]; + git_annotated_commit *our_head, *their_heads[3]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID)); - cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_oid)); + cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_oid)); cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID)); - cl_git_pass(git_merge_head_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.url/repo.git", &octo2_oid)); + cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.url/repo.git", &octo2_oid)); cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID)); - cl_git_pass(git_merge_head_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.url/repo.git", &octo3_oid)); + cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.url/repo.git", &octo3_oid)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 3)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "', '" OCTO2_BRANCH "' and '" OCTO3_BRANCH "' of http://remote.url/repo.git\n")); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); - git_merge_head_free(their_heads[2]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); + git_annotated_commit_free(their_heads[2]); } void test_merge_workdir_setup__three_remotes(void) @@ -842,31 +816,31 @@ git_oid octo1_oid; git_oid octo2_oid; git_oid octo3_oid; - git_merge_head *our_head, *their_heads[3]; + git_annotated_commit *our_head, *their_heads[3]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID)); - cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.first/repo.git", &octo1_oid)); + cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.first/repo.git", &octo1_oid)); cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID)); - cl_git_pass(git_merge_head_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.second/repo.git", &octo2_oid)); + cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.second/repo.git", &octo2_oid)); cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID)); - cl_git_pass(git_merge_head_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.third/repo.git", &octo3_oid)); + cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.third/repo.git", &octo3_oid)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 3, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 3)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "' of http://remote.first/repo.git, branch '" OCTO2_BRANCH "' of http://remote.second/repo.git, branch '" OCTO3_BRANCH "' of http://remote.third/repo.git\n")); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); - git_merge_head_free(their_heads[2]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); + git_annotated_commit_free(their_heads[2]); } void test_merge_workdir_setup__two_remotes(void) @@ -876,48 +850,75 @@ git_oid octo2_oid; git_oid octo3_oid; git_oid octo4_oid; - git_merge_head *our_head, *their_heads[4]; + git_annotated_commit *our_head, *their_heads[4]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_oid_fromstr(&octo1_oid, OCTO1_OID)); - cl_git_pass(git_merge_head_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.first/repo.git", &octo1_oid)); + cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.first/repo.git", &octo1_oid)); cl_git_pass(git_oid_fromstr(&octo2_oid, OCTO2_OID)); - cl_git_pass(git_merge_head_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.second/repo.git", &octo2_oid)); + cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.second/repo.git", &octo2_oid)); cl_git_pass(git_oid_fromstr(&octo3_oid, OCTO3_OID)); - cl_git_pass(git_merge_head_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.first/repo.git", &octo3_oid)); + cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.first/repo.git", &octo3_oid)); cl_git_pass(git_oid_fromstr(&octo4_oid, OCTO4_OID)); - cl_git_pass(git_merge_head_from_fetchhead(&their_heads[3], repo, GIT_REFS_HEADS_DIR OCTO4_BRANCH, "http://remote.second/repo.git", &octo4_oid)); + cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[3], repo, GIT_REFS_HEADS_DIR OCTO4_BRANCH, "http://remote.second/repo.git", &octo4_oid)); - cl_git_pass(git_merge__setup(repo, our_head, (const git_merge_head **)their_heads, 4, 0)); + cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 4)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); - cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "")); + cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff")); cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO3_BRANCH "' of http://remote.first/repo.git, branches '" OCTO2_BRANCH "' and '" OCTO4_BRANCH "' of http://remote.second/repo.git\n")); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_head_free(their_heads[1]); - git_merge_head_free(their_heads[2]); - git_merge_head_free(their_heads[3]); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); + git_annotated_commit_free(their_heads[1]); + git_annotated_commit_free(their_heads[2]); + git_annotated_commit_free(their_heads[3]); +} + +void test_merge_workdir_setup__id_from_head(void) +{ + git_oid expected_id; + const git_oid *id; + git_reference *ref; + git_annotated_commit *heads[3]; + + cl_git_pass(git_oid_fromstr(&expected_id, OCTO1_OID)); + cl_git_pass(git_annotated_commit_from_fetchhead(&heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &expected_id)); + id = git_annotated_commit_id(heads[0]); + cl_assert_equal_i(1, git_oid_equal(id, &expected_id)); + + cl_git_pass(git_annotated_commit_lookup(&heads[1], repo, &expected_id)); + id = git_annotated_commit_id(heads[1]); + cl_assert_equal_i(1, git_oid_equal(id, &expected_id)); + + cl_git_pass(git_reference_lookup(&ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); + cl_git_pass(git_annotated_commit_from_ref(&heads[2], repo, ref)); + id = git_annotated_commit_id(heads[2]); + cl_assert_equal_i(1, git_oid_equal(id, &expected_id)); + + git_reference_free(ref); + git_annotated_commit_free(heads[0]); + git_annotated_commit_free(heads[1]); + git_annotated_commit_free(heads[2]); } -struct merge_head_cb_data { +struct annotated_commit_cb_data { const char **oid_str; unsigned int len; unsigned int i; }; -static int merge_head_foreach_cb(const git_oid *oid, void *payload) +static int annotated_commit_foreach_cb(const git_oid *oid, void *payload) { git_oid expected_oid; - struct merge_head_cb_data *cb_data = payload; + struct annotated_commit_cb_data *cb_data = payload; git_oid_fromstr(&expected_oid, cb_data->oid_str[cb_data->i]); cl_assert(git_oid_cmp(&expected_oid, oid) == 0); @@ -930,7 +931,7 @@ int error; cl_git_fail((error = git_repository_mergehead_foreach(repo, - merge_head_foreach_cb, NULL))); + annotated_commit_foreach_cb, NULL))); cl_assert(error == GIT_ENOTFOUND); } @@ -941,7 +942,7 @@ write_file_contents(GIT_MERGE_HEAD_FILE, "invalid-oid\n"); cl_git_fail((error = git_repository_mergehead_foreach(repo, - merge_head_foreach_cb, NULL))); + annotated_commit_foreach_cb, NULL))); cl_assert(error == -1); } @@ -952,7 +953,7 @@ write_file_contents(GIT_MERGE_HEAD_FILE, THEIRS_SIMPLE_OID); cl_git_fail((error = git_repository_mergehead_foreach(repo, - merge_head_foreach_cb, NULL))); + annotated_commit_foreach_cb, NULL))); cl_assert(error == -1); } @@ -960,12 +961,12 @@ { const char *expected = THEIRS_SIMPLE_OID; - struct merge_head_cb_data cb_data = { &expected, 1 }; + struct annotated_commit_cb_data cb_data = { &expected, 1 }; write_file_contents(GIT_MERGE_HEAD_FILE, THEIRS_SIMPLE_OID "\n"); cl_git_pass(git_repository_mergehead_foreach(repo, - merge_head_foreach_cb, &cb_data)); + annotated_commit_foreach_cb, &cb_data)); cl_assert(cb_data.i == cb_data.len); } @@ -975,7 +976,7 @@ const char *expected[] = { THEIRS_SIMPLE_OID, OCTO1_OID, OCTO2_OID, OCTO3_OID, OCTO4_OID, OCTO5_OID }; - struct merge_head_cb_data cb_data = { expected, 6 }; + struct annotated_commit_cb_data cb_data = { expected, 6 }; write_file_contents(GIT_MERGE_HEAD_FILE, THEIRS_SIMPLE_OID "\n" @@ -986,7 +987,7 @@ OCTO5_OID "\n"); cl_git_pass(git_repository_mergehead_foreach(repo, - merge_head_foreach_cb, &cb_data)); + annotated_commit_foreach_cb, &cb_data)); cl_assert(cb_data.i == cb_data.len); } @@ -995,20 +996,16 @@ { git_oid our_oid; git_reference *octo1_ref; - git_merge_head *our_head, *their_heads[1]; - git_merge_result *result; - git_merge_opts opts = GIT_MERGE_OPTS_INIT; - - opts.merge_flags |= GIT_MERGE_NO_FASTFORWARD; + git_annotated_commit *our_head, *their_heads[1]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref)); - cl_git_pass(git_merge(&result, repo, (const git_merge_head **)&their_heads[0], 1, &opts)); + cl_git_pass(git_merge(repo, (const git_annotated_commit **)&their_heads[0], 1, NULL, NULL)); cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n")); cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n")); @@ -1017,32 +1014,27 @@ git_reference_free(octo1_ref); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_result_free(result); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); } void test_merge_workdir_setup__removed_after_failure(void) { git_oid our_oid; git_reference *octo1_ref; - git_merge_head *our_head, *their_heads[1]; - git_merge_result *result; - git_merge_opts opts = GIT_MERGE_OPTS_INIT; - - opts.merge_flags |= GIT_MERGE_NO_FASTFORWARD; + git_annotated_commit *our_head, *their_heads[1]; cl_git_pass(git_oid_fromstr(&our_oid, ORIG_HEAD)); - cl_git_pass(git_merge_head_from_oid(&our_head, repo, &our_oid)); + cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid)); cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, octo1_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref)); cl_git_rewritefile("merge-resolve/new-in-octo1.txt", "Conflicting file!\n\nMerge will fail!\n"); cl_git_fail(git_merge( - &result, repo, (const git_merge_head **)&their_heads[0], 1, &opts)); + repo, (const git_annotated_commit **)&their_heads[0], 1, NULL, NULL)); cl_assert(!git_path_exists("merge-resolve/" GIT_MERGE_HEAD_FILE)); cl_assert(!git_path_exists("merge-resolve/" GIT_ORIG_HEAD_FILE)); @@ -1051,7 +1043,6 @@ git_reference_free(octo1_ref); - git_merge_head_free(our_head); - git_merge_head_free(their_heads[0]); - git_merge_result_free(result); + git_annotated_commit_free(our_head); + git_annotated_commit_free(their_heads[0]); } diff -Nru libgit2-0.20.0/tests/merge/workdir/simple.c libgit2-0.22.2/tests/merge/workdir/simple.c --- libgit2-0.20.0/tests/merge/workdir/simple.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/merge/workdir/simple.c 2015-03-24 16:10:45.000000000 +0000 @@ -71,40 +71,19 @@ "", \ "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5" } -#define AUTOMERGEABLE_MERGED_FILE \ - "this file is changed in master\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is automergeable\n" \ - "this file is changed in branch\n" - -#define AUTOMERGEABLE_MERGED_FILE_CRLF \ - "this file is changed in master\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is automergeable\r\n" \ - "this file is changed in branch\r\n" - -#define CONFLICTING_DIFF3_FILE \ - "<<<<<<< HEAD\n" \ - "this file is changed in master and branch\n" \ - "=======\n" \ - "this file is changed in branch and master\n" \ - ">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n" // Fixture setup and teardown void test_merge_workdir_simple__initialize(void) { + git_config *cfg; + repo = cl_git_sandbox_init(TEST_REPO_PATH); git_repository_index(&repo_index, repo); + + /* Ensure that the user's merge.conflictstyle doesn't interfere */ + cl_git_pass(git_repository_config(&cfg, repo)); + cl_git_pass(git_config_set_string(cfg, "merge.conflictstyle", "merge")); + git_config_free(cfg); } void test_merge_workdir_simple__cleanup(void) @@ -113,23 +92,23 @@ cl_git_sandbox_cleanup(); } -static git_merge_result *merge_simple_branch(int automerge_flags, int checkout_strategy) +static void merge_simple_branch(int merge_file_favor, int addl_checkout_strategy) { git_oid their_oids[1]; - git_merge_head *their_heads[1]; - git_merge_result *result; - git_merge_opts opts = GIT_MERGE_OPTS_INIT; + git_annotated_commit *their_heads[1]; + git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; cl_git_pass(git_oid_fromstr(&their_oids[0], THEIRS_SIMPLE_OID)); - cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oids[0])); + cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &their_oids[0])); - opts.merge_tree_opts.automerge_flags = automerge_flags; - opts.checkout_opts.checkout_strategy = checkout_strategy; - cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts)); + merge_opts.file_favor = merge_file_favor; + checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS | + addl_checkout_strategy; - git_merge_head_free(their_heads[0]); + cl_git_pass(git_merge(repo, (const git_annotated_commit **)their_heads, 1, &merge_opts, &checkout_opts)); - return result; + git_annotated_commit_free(their_heads[0]); } static void set_core_autocrlf_to(git_repository *repo, bool value) @@ -146,7 +125,6 @@ { git_index *index; const git_index_entry *entry; - git_merge_result *result; git_buf automergeable_buf = GIT_BUF_INIT; struct merge_index_entry merge_index_entries[] = { @@ -171,8 +149,7 @@ set_core_autocrlf_to(repo, false); - cl_assert(result = merge_simple_branch(0, 0)); - cl_assert(!git_merge_result_is_fastforward(result)); + merge_simple_branch(0, 0); cl_git_pass(git_futils_readbuffer(&automergeable_buf, TEST_REPO_PATH "/automergeable.txt")); @@ -182,8 +159,6 @@ cl_assert(merge_test_index(repo_index, merge_index_entries, 8)); cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3)); - git_merge_result_free(result); - git_repository_index(&index, repo); cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL); @@ -197,8 +172,6 @@ #ifdef GIT_WIN32 git_index *index; const git_index_entry *entry; - - git_merge_result *result; git_buf automergeable_buf = GIT_BUF_INIT; struct merge_index_entry merge_index_entries[] = { @@ -222,8 +195,7 @@ set_core_autocrlf_to(repo, true); - cl_assert(result = merge_simple_branch(0, 0)); - cl_assert(!git_merge_result_is_fastforward(result)); + merge_simple_branch(0, 0); cl_git_pass(git_futils_readbuffer(&automergeable_buf, TEST_REPO_PATH "/automergeable.txt")); @@ -233,8 +205,6 @@ cl_assert(merge_test_index(repo_index, merge_index_entries, 8)); cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3)); - git_merge_result_free(result); - git_repository_index(&index, repo); cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL); @@ -244,9 +214,52 @@ #endif /* GIT_WIN32 */ } +void test_merge_workdir_simple__mergefile(void) +{ + git_buf conflicting_buf = GIT_BUF_INIT, mergemsg_buf = GIT_BUF_INIT; + + struct merge_index_entry merge_index_entries[] = { + ADDED_IN_MASTER_INDEX_ENTRY, + AUTOMERGEABLE_INDEX_ENTRY, + CHANGED_IN_BRANCH_INDEX_ENTRY, + CHANGED_IN_MASTER_INDEX_ENTRY, + + { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" }, + { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" }, + { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" }, + + UNCHANGED_INDEX_ENTRY, + }; + + struct merge_reuc_entry merge_reuc_entries[] = { + AUTOMERGEABLE_REUC_ENTRY, + REMOVED_IN_BRANCH_REUC_ENTRY, + REMOVED_IN_MASTER_REUC_ENTRY + }; + + set_core_autocrlf_to(repo, false); + + merge_simple_branch(0, 0); + + cl_git_pass(git_futils_readbuffer(&conflicting_buf, + TEST_REPO_PATH "/conflicting.txt")); + cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_MERGE_FILE) == 0); + cl_git_pass(git_futils_readbuffer(&mergemsg_buf, + TEST_REPO_PATH "/.git/MERGE_MSG")); + cl_assert(strcmp(git_buf_cstr(&mergemsg_buf), + "Merge commit '7cb63eed597130ba4abb87b3e544b85021905520'\n" \ + "\n" \ + "Conflicts:\n" \ + "\tconflicting.txt\n") == 0); + git_buf_free(&conflicting_buf); + git_buf_free(&mergemsg_buf); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 8)); + cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3)); +} + void test_merge_workdir_simple__diff3(void) { - git_merge_result *result; git_buf conflicting_buf = GIT_BUF_INIT; struct merge_index_entry merge_index_entries[] = { @@ -268,8 +281,9 @@ REMOVED_IN_MASTER_REUC_ENTRY }; - cl_assert(result = merge_simple_branch(0, 0)); - cl_assert(!git_merge_result_is_fastforward(result)); + set_core_autocrlf_to(repo, false); + + merge_simple_branch(0, GIT_CHECKOUT_CONFLICT_STYLE_DIFF3); cl_git_pass(git_futils_readbuffer(&conflicting_buf, TEST_REPO_PATH "/conflicting.txt")); @@ -278,13 +292,89 @@ cl_assert(merge_test_index(repo_index, merge_index_entries, 8)); cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3)); +} + +void test_merge_workdir_simple__union(void) +{ + git_buf conflicting_buf = GIT_BUF_INIT; + + struct merge_index_entry merge_index_entries[] = { + ADDED_IN_MASTER_INDEX_ENTRY, + AUTOMERGEABLE_INDEX_ENTRY, + CHANGED_IN_BRANCH_INDEX_ENTRY, + CHANGED_IN_MASTER_INDEX_ENTRY, + + { 0100644, "72cdb057b340205164478565e91eb71647e66891", 0, "conflicting.txt" }, + + UNCHANGED_INDEX_ENTRY, + }; + + struct merge_reuc_entry merge_reuc_entries[] = { + AUTOMERGEABLE_REUC_ENTRY, + CONFLICTING_REUC_ENTRY, + REMOVED_IN_BRANCH_REUC_ENTRY, + REMOVED_IN_MASTER_REUC_ENTRY + }; + + set_core_autocrlf_to(repo, false); + + merge_simple_branch(GIT_MERGE_FILE_FAVOR_UNION, 0); - git_merge_result_free(result); + cl_git_pass(git_futils_readbuffer(&conflicting_buf, + TEST_REPO_PATH "/conflicting.txt")); + cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_UNION_FILE) == 0); + git_buf_free(&conflicting_buf); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 6)); + cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 4)); } -void test_merge_workdir_simple__checkout_ours(void) +void test_merge_workdir_simple__diff3_from_config(void) +{ + git_config *config; + git_buf conflicting_buf = GIT_BUF_INIT; + + struct merge_index_entry merge_index_entries[] = { + ADDED_IN_MASTER_INDEX_ENTRY, + AUTOMERGEABLE_INDEX_ENTRY, + CHANGED_IN_BRANCH_INDEX_ENTRY, + CHANGED_IN_MASTER_INDEX_ENTRY, + + { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" }, + { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" }, + { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" }, + + UNCHANGED_INDEX_ENTRY, + }; + + struct merge_reuc_entry merge_reuc_entries[] = { + AUTOMERGEABLE_REUC_ENTRY, + REMOVED_IN_BRANCH_REUC_ENTRY, + REMOVED_IN_MASTER_REUC_ENTRY + }; + + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_set_string(config, "merge.conflictstyle", "diff3")); + + set_core_autocrlf_to(repo, false); + + merge_simple_branch(0, 0); + + cl_git_pass(git_futils_readbuffer(&conflicting_buf, + TEST_REPO_PATH "/conflicting.txt")); + cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_DIFF3_FILE) == 0); + git_buf_free(&conflicting_buf); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 8)); + cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3)); + + git_config_free(config); +} + +void test_merge_workdir_simple__merge_overrides_config(void) { - git_merge_result *result; + git_config *config; + git_buf conflicting_buf = GIT_BUF_INIT; struct merge_index_entry merge_index_entries[] = { ADDED_IN_MASTER_INDEX_ENTRY, @@ -305,21 +395,55 @@ REMOVED_IN_MASTER_REUC_ENTRY }; - cl_assert(result = merge_simple_branch(0, GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS)); - cl_assert(!git_merge_result_is_fastforward(result)); + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_set_string(config, "merge.conflictstyle", "diff3")); + + set_core_autocrlf_to(repo, false); + + merge_simple_branch(0, GIT_CHECKOUT_CONFLICT_STYLE_MERGE); + + cl_git_pass(git_futils_readbuffer(&conflicting_buf, + TEST_REPO_PATH "/conflicting.txt")); + cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_MERGE_FILE) == 0); + git_buf_free(&conflicting_buf); cl_assert(merge_test_index(repo_index, merge_index_entries, 8)); cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3)); - cl_assert(git_path_exists(TEST_REPO_PATH "/conflicting.txt")); + git_config_free(config); +} - git_merge_result_free(result); +void test_merge_workdir_simple__checkout_ours(void) +{ + struct merge_index_entry merge_index_entries[] = { + ADDED_IN_MASTER_INDEX_ENTRY, + AUTOMERGEABLE_INDEX_ENTRY, + CHANGED_IN_BRANCH_INDEX_ENTRY, + CHANGED_IN_MASTER_INDEX_ENTRY, + + { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" }, + { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" }, + { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" }, + + UNCHANGED_INDEX_ENTRY, + }; + + struct merge_reuc_entry merge_reuc_entries[] = { + AUTOMERGEABLE_REUC_ENTRY, + REMOVED_IN_BRANCH_REUC_ENTRY, + REMOVED_IN_MASTER_REUC_ENTRY + }; + + merge_simple_branch(0, GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 8)); + cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3)); + + cl_assert(git_path_exists(TEST_REPO_PATH "/conflicting.txt")); } void test_merge_workdir_simple__favor_ours(void) { - git_merge_result *result; - struct merge_index_entry merge_index_entries[] = { ADDED_IN_MASTER_INDEX_ENTRY, AUTOMERGEABLE_INDEX_ENTRY, @@ -336,19 +460,14 @@ REMOVED_IN_MASTER_REUC_ENTRY, }; - cl_assert(result = merge_simple_branch(GIT_MERGE_AUTOMERGE_FAVOR_OURS, 0)); - cl_assert(!git_merge_result_is_fastforward(result)); + merge_simple_branch(GIT_MERGE_FILE_FAVOR_OURS, 0); cl_assert(merge_test_index(repo_index, merge_index_entries, 6)); cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 4)); - - git_merge_result_free(result); } void test_merge_workdir_simple__favor_theirs(void) { - git_merge_result *result; - struct merge_index_entry merge_index_entries[] = { ADDED_IN_MASTER_INDEX_ENTRY, AUTOMERGEABLE_INDEX_ENTRY, @@ -365,22 +484,18 @@ REMOVED_IN_MASTER_REUC_ENTRY, }; - cl_assert(result = merge_simple_branch(GIT_MERGE_AUTOMERGE_FAVOR_THEIRS, 0)); - cl_assert(!git_merge_result_is_fastforward(result)); + merge_simple_branch(GIT_MERGE_FILE_FAVOR_THEIRS, 0); cl_assert(merge_test_index(repo_index, merge_index_entries, 6)); cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 4)); - - git_merge_result_free(result); } void test_merge_workdir_simple__directory_file(void) { git_reference *head; git_oid their_oids[1], head_commit_id; - git_merge_head *their_heads[1]; - git_merge_result *result; - git_merge_opts opts = GIT_MERGE_OPTS_INIT; + git_annotated_commit *their_heads[1]; + git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT; git_commit *head_commit; struct merge_index_entry merge_index_entries[] = { @@ -406,31 +521,29 @@ { 0100644, "f5504f36e6f4eb797a56fc5bac6c6c7f32969bf2", 3, "file-5/new" }, }; - cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_DIR OURS_DIRECTORY_FILE, 1)); + cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_DIR OURS_DIRECTORY_FILE, 1, NULL, NULL)); cl_git_pass(git_reference_name_to_id(&head_commit_id, repo, GIT_HEAD_FILE)); cl_git_pass(git_commit_lookup(&head_commit, repo, &head_commit_id)); - cl_git_pass(git_reset(repo, (git_object *)head_commit, GIT_RESET_HARD)); + cl_git_pass(git_reset(repo, (git_object *)head_commit, GIT_RESET_HARD, NULL, NULL, NULL)); cl_git_pass(git_oid_fromstr(&their_oids[0], THEIRS_DIRECTORY_FILE)); - cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oids[0])); + cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &their_oids[0])); - opts.merge_tree_opts.automerge_flags = 0; - cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts)); + merge_opts.file_favor = 0; + cl_git_pass(git_merge(repo, (const git_annotated_commit **)their_heads, 1, &merge_opts, NULL)); cl_assert(merge_test_index(repo_index, merge_index_entries, 20)); git_reference_free(head); git_commit_free(head_commit); - git_merge_head_free(their_heads[0]); - git_merge_result_free(result); + git_annotated_commit_free(their_heads[0]); } void test_merge_workdir_simple__unrelated(void) { git_oid their_oids[1]; - git_merge_head *their_heads[1]; - git_merge_result *result; - git_merge_opts opts = GIT_MERGE_OPTS_INIT; + git_annotated_commit *their_heads[1]; + git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT; struct merge_index_entry merge_index_entries[] = { { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, @@ -445,23 +558,21 @@ }; cl_git_pass(git_oid_fromstr(&their_oids[0], THEIRS_UNRELATED_PARENT)); - cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oids[0])); + cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &their_oids[0])); - opts.merge_tree_opts.automerge_flags = 0; - cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts)); + merge_opts.file_favor = 0; + cl_git_pass(git_merge(repo, (const git_annotated_commit **)their_heads, 1, &merge_opts, NULL)); cl_assert(merge_test_index(repo_index, merge_index_entries, 9)); - git_merge_head_free(their_heads[0]); - git_merge_result_free(result); + git_annotated_commit_free(their_heads[0]); } void test_merge_workdir_simple__unrelated_with_conflicts(void) { git_oid their_oids[1]; - git_merge_head *their_heads[1]; - git_merge_result *result; - git_merge_opts opts = GIT_MERGE_OPTS_INIT; + git_annotated_commit *their_heads[1]; + git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT; struct merge_index_entry merge_index_entries[] = { { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, @@ -478,14 +589,47 @@ }; cl_git_pass(git_oid_fromstr(&their_oids[0], THEIRS_UNRELATED_OID)); - cl_git_pass(git_merge_head_from_oid(&their_heads[0], repo, &their_oids[0])); + cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &their_oids[0])); - opts.merge_tree_opts.automerge_flags = 0; - cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts)); + merge_opts.file_favor = 0; + cl_git_pass(git_merge(repo, (const git_annotated_commit **)their_heads, 1, &merge_opts, NULL)); cl_assert(merge_test_index(repo_index, merge_index_entries, 11)); - git_merge_head_free(their_heads[0]); - git_merge_result_free(result); + git_annotated_commit_free(their_heads[0]); } +void test_merge_workdir_simple__binary(void) +{ + git_oid our_oid, their_oid, our_file_oid; + git_commit *our_commit; + git_annotated_commit *their_head; + const git_index_entry *binary_entry; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "1c51d885170f57a0c4e8c69ff6363d91a5b51f85", 1, "binary" }, + { 0100644, "23ed141a6ae1e798b2f721afedbe947c119111ba", 2, "binary" }, + { 0100644, "836b8b82b26cab22eaaed8820877c76d6c8bca19", 3, "binary" }, + }; + + cl_git_pass(git_oid_fromstr(&our_oid, "cc338e4710c9b257106b8d16d82f86458d5beaf1")); + cl_git_pass(git_oid_fromstr(&their_oid, "ad01aebfdf2ac13145efafe3f9fcf798882f1730")); + + cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid)); + cl_git_pass(git_reset(repo, (git_object *)our_commit, GIT_RESET_HARD, NULL, NULL, NULL)); + + cl_git_pass(git_annotated_commit_lookup(&their_head, repo, &their_oid)); + + cl_git_pass(git_merge(repo, (const git_annotated_commit **)&their_head, 1, NULL, NULL)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 3)); + + cl_git_pass(git_index_add_bypath(repo_index, "binary")); + cl_assert((binary_entry = git_index_get_bypath(repo_index, "binary", 0)) != NULL); + + cl_git_pass(git_oid_fromstr(&our_file_oid, "23ed141a6ae1e798b2f721afedbe947c119111ba")); + cl_assert(git_oid_cmp(&binary_entry->id, &our_file_oid) == 0); + + git_annotated_commit_free(their_head); + git_commit_free(our_commit); +} diff -Nru libgit2-0.20.0/tests/merge/workdir/submodules.c libgit2-0.22.2/tests/merge/workdir/submodules.c --- libgit2-0.20.0/tests/merge/workdir/submodules.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/merge/workdir/submodules.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,95 @@ +#include "clar_libgit2.h" +#include "git2/repository.h" +#include "git2/merge.h" +#include "buffer.h" +#include "merge.h" +#include "../merge_helpers.h" + +static git_repository *repo; + +#define TEST_REPO_PATH "merge-resolve" + +#define SUBMODULE_MAIN_BRANCH "submodules" +#define SUBMODULE_OTHER_BRANCH "submodules-branch" +#define SUBMODULE_OTHER2_BRANCH "submodules-branch2" + +#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index" + +// Fixture setup and teardown +void test_merge_workdir_submodules__initialize(void) +{ + repo = cl_git_sandbox_init(TEST_REPO_PATH); +} + +void test_merge_workdir_submodules__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_merge_workdir_submodules__automerge(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, "caff6b7d44973f53e3e0cf31d0d695188b19aec6", 0, ".gitmodules" }, + { 0100644, "950a663a6a7b2609eed1ed1ba9f41eb1a3192a9f", 0, "file1.txt" }, + { 0100644, "343e660b9cb4bee5f407c2e33fcb9df24d9407a4", 0, "file2.txt" }, + { 0160000, "d3d806a4bef96889117fd7ebac0e3cb5ec152932", 1, "submodule" }, + { 0160000, "297aa6cd028b3336c7802c7a6f49143da4e1602d", 2, "submodule" }, + { 0160000, "ae39c77c70cb6bad18bb471912460c4e1ba0f586", 3, "submodule" }, + }; + + cl_git_pass(git_reference_lookup(&our_ref, repo, "refs/heads/" SUBMODULE_MAIN_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, NULL, NULL)); + + cl_git_pass(git_reference_lookup(&their_ref, repo, "refs/heads/" SUBMODULE_OTHER_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, 6)); + + 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); +} + +void test_merge_workdir_submodules__take_changed(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, "caff6b7d44973f53e3e0cf31d0d695188b19aec6", 0, ".gitmodules" }, + { 0100644, "b438ff23300b2e0f80b84a6f30140dfa91e71423", 0, "file1.txt" }, + { 0100644, "f27fbafdfa6693f8f7a5128506fe3e338dbfcad2", 0, "file2.txt" }, + { 0160000, "297aa6cd028b3336c7802c7a6f49143da4e1602d", 0, "submodule" }, + }; + + cl_git_pass(git_reference_lookup(&our_ref, repo, "refs/heads/" SUBMODULE_MAIN_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, NULL, NULL)); + + cl_git_pass(git_reference_lookup(&their_ref, repo, "refs/heads/" SUBMODULE_OTHER2_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, 4)); + + 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); +} diff -Nru libgit2-0.20.0/tests/merge/workdir/trivial.c libgit2-0.22.2/tests/merge/workdir/trivial.c --- libgit2-0.20.0/tests/merge/workdir/trivial.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/merge/workdir/trivial.c 2015-03-24 16:10:45.000000000 +0000 @@ -28,36 +28,31 @@ } -static int merge_trivial(const char *ours, const char *theirs, bool automerge) +static int merge_trivial(const char *ours, const char *theirs) { git_buf branch_buf = GIT_BUF_INIT; - git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; git_reference *our_ref, *their_ref; - git_merge_head *their_heads[1]; - git_merge_opts opts = GIT_MERGE_OPTS_INIT; - git_merge_result *result; + git_annotated_commit *their_heads[1]; checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE; - opts.merge_tree_opts.automerge_flags |= automerge ? 0 : GIT_MERGE_AUTOMERGE_NONE; - git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours); - cl_git_pass(git_reference_symbolic_create(&our_ref, repo, "HEAD", branch_buf.ptr, 1)); + cl_git_pass(git_reference_symbolic_create(&our_ref, repo, "HEAD", branch_buf.ptr, 1, NULL, NULL)); cl_git_pass(git_checkout_head(repo, &checkout_opts)); git_buf_clear(&branch_buf); git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs); cl_git_pass(git_reference_lookup(&their_ref, repo, branch_buf.ptr)); - cl_git_pass(git_merge_head_from_ref(&their_heads[0], repo, their_ref)); + cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, their_ref)); - cl_git_pass(git_merge(&result, repo, (const git_merge_head **)their_heads, 1, &opts)); + cl_git_pass(git_merge(repo, (const git_annotated_commit **)their_heads, 1, NULL, NULL)); git_buf_free(&branch_buf); git_reference_free(our_ref); git_reference_free(their_ref); - git_merge_head_free(their_heads[0]); - git_merge_result_free(result); + git_annotated_commit_free(their_heads[0]); return 0; } @@ -83,7 +78,7 @@ { const git_index_entry *entry; - cl_git_pass(merge_trivial("trivial-2alt", "trivial-2alt-branch", 0)); + cl_git_pass(merge_trivial("trivial-2alt", "trivial-2alt-branch")); cl_assert(entry = git_index_get_bypath(repo_index, "new-in-branch.txt", 0)); cl_assert(git_index_reuc_entrycount(repo_index) == 0); @@ -95,7 +90,7 @@ { const git_index_entry *entry; - cl_git_pass(merge_trivial("trivial-3alt", "trivial-3alt-branch", 0)); + cl_git_pass(merge_trivial("trivial-3alt", "trivial-3alt-branch")); cl_assert(entry = git_index_get_bypath(repo_index, "new-in-3alt.txt", 0)); cl_assert(git_index_reuc_entrycount(repo_index) == 0); @@ -107,7 +102,7 @@ { const git_index_entry *entry; - cl_git_pass(merge_trivial("trivial-4", "trivial-4-branch", 0)); + cl_git_pass(merge_trivial("trivial-4", "trivial-4-branch")); cl_assert((entry = git_index_get_bypath(repo_index, "new-and-different.txt", 0)) == NULL); cl_assert(git_index_reuc_entrycount(repo_index) == 0); @@ -122,7 +117,7 @@ { const git_index_entry *entry; - cl_git_pass(merge_trivial("trivial-5alt-1", "trivial-5alt-1-branch", 0)); + cl_git_pass(merge_trivial("trivial-5alt-1", "trivial-5alt-1-branch")); cl_assert(entry = git_index_get_bypath(repo_index, "new-and-same.txt", 0)); cl_assert(git_index_reuc_entrycount(repo_index) == 0); @@ -134,7 +129,7 @@ { const git_index_entry *entry; - cl_git_pass(merge_trivial("trivial-5alt-2", "trivial-5alt-2-branch", 0)); + cl_git_pass(merge_trivial("trivial-5alt-2", "trivial-5alt-2-branch")); cl_assert(entry = git_index_get_bypath(repo_index, "modified-to-same.txt", 0)); cl_assert(git_index_reuc_entrycount(repo_index) == 0); @@ -145,23 +140,9 @@ void test_merge_workdir_trivial__6(void) { const git_index_entry *entry; - - cl_git_pass(merge_trivial("trivial-6", "trivial-6-branch", 0)); - - cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-both.txt", 0)) == NULL); - cl_assert(git_index_reuc_entrycount(repo_index) == 0); - - cl_assert(merge_trivial_conflict_entrycount() == 1); - cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-both.txt", 1)); -} - -/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */ -void test_merge_workdir_trivial__6_automerge(void) -{ - const git_index_entry *entry; const git_index_reuc_entry *reuc; - cl_git_pass(merge_trivial("trivial-6", "trivial-6-branch", 1)); + cl_git_pass(merge_trivial("trivial-6", "trivial-6-branch")); cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-both.txt", 0)) == NULL); cl_assert(git_index_reuc_entrycount(repo_index) == 1); @@ -174,24 +155,9 @@ void test_merge_workdir_trivial__8(void) { const git_index_entry *entry; - - cl_git_pass(merge_trivial("trivial-8", "trivial-8-branch", 0)); - - cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 0)) == NULL); - cl_assert(git_index_reuc_entrycount(repo_index) == 0); - - cl_assert(merge_trivial_conflict_entrycount() == 2); - cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 1)); - cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 3)); -} - -/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */ -void test_merge_workdir_trivial__8_automerge(void) -{ - const git_index_entry *entry; const git_index_reuc_entry *reuc; - cl_git_pass(merge_trivial("trivial-8", "trivial-8-branch", 1)); + cl_git_pass(merge_trivial("trivial-8", "trivial-8-branch")); cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 0)) == NULL); @@ -206,22 +172,7 @@ { const git_index_entry *entry; - cl_git_pass(merge_trivial("trivial-7", "trivial-7-branch", 0)); - - cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 0)) == NULL); - cl_assert(git_index_reuc_entrycount(repo_index) == 0); - - cl_assert(merge_trivial_conflict_entrycount() == 2); - cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 1)); - cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 3)); -} - -/* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge */ -void test_merge_workdir_trivial__7_automerge(void) -{ - const git_index_entry *entry; - - cl_git_pass(merge_trivial("trivial-7", "trivial-7-branch", 0)); + cl_git_pass(merge_trivial("trivial-7", "trivial-7-branch")); cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 0)) == NULL); cl_assert(git_index_reuc_entrycount(repo_index) == 0); @@ -235,24 +186,9 @@ void test_merge_workdir_trivial__10(void) { const git_index_entry *entry; - - cl_git_pass(merge_trivial("trivial-10", "trivial-10-branch", 0)); - - cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 0)) == NULL); - cl_assert(git_index_reuc_entrycount(repo_index) == 0); - - cl_assert(merge_trivial_conflict_entrycount() == 2); - cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 1)); - cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 2)); -} - -/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */ -void test_merge_workdir_trivial__10_automerge(void) -{ - const git_index_entry *entry; const git_index_reuc_entry *reuc; - cl_git_pass(merge_trivial("trivial-10", "trivial-10-branch", 1)); + cl_git_pass(merge_trivial("trivial-10", "trivial-10-branch")); cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 0)) == NULL); @@ -267,22 +203,7 @@ { const git_index_entry *entry; - cl_git_pass(merge_trivial("trivial-9", "trivial-9-branch", 0)); - - cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 0)) == NULL); - cl_assert(git_index_reuc_entrycount(repo_index) == 0); - - cl_assert(merge_trivial_conflict_entrycount() == 2); - cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 1)); - cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 2)); -} - -/* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge */ -void test_merge_workdir_trivial__9_automerge(void) -{ - const git_index_entry *entry; - - cl_git_pass(merge_trivial("trivial-9", "trivial-9-branch", 1)); + cl_git_pass(merge_trivial("trivial-9", "trivial-9-branch")); cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 0)) == NULL); cl_assert(git_index_reuc_entrycount(repo_index) == 0); @@ -298,11 +219,11 @@ const git_index_entry *entry; git_oid expected_oid; - cl_git_pass(merge_trivial("trivial-13", "trivial-13-branch", 0)); + cl_git_pass(merge_trivial("trivial-13", "trivial-13-branch")); cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-13.txt", 0)); cl_git_pass(git_oid_fromstr(&expected_oid, "1cff9ec6a47a537380dedfdd17c9e76d74259a2b")); - cl_assert(git_oid_cmp(&entry->oid, &expected_oid) == 0); + cl_assert(git_oid_cmp(&entry->id, &expected_oid) == 0); cl_assert(git_index_reuc_entrycount(repo_index) == 0); cl_assert(merge_trivial_conflict_entrycount() == 0); @@ -314,11 +235,11 @@ const git_index_entry *entry; git_oid expected_oid; - cl_git_pass(merge_trivial("trivial-14", "trivial-14-branch", 0)); + cl_git_pass(merge_trivial("trivial-14", "trivial-14-branch")); cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-14-branch.txt", 0)); cl_git_pass(git_oid_fromstr(&expected_oid, "26153a3ff3649b6c2bb652d3f06878c6e0a172f9")); - cl_assert(git_oid_cmp(&entry->oid, &expected_oid) == 0); + cl_assert(git_oid_cmp(&entry->id, &expected_oid) == 0); cl_assert(git_index_reuc_entrycount(repo_index) == 0); cl_assert(merge_trivial_conflict_entrycount() == 0); @@ -329,7 +250,7 @@ { const git_index_entry *entry; - cl_git_pass(merge_trivial("trivial-11", "trivial-11-branch", 0)); + cl_git_pass(merge_trivial("trivial-11", "trivial-11-branch")); cl_assert((entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 0)) == NULL); cl_assert(git_index_reuc_entrycount(repo_index) == 0); diff -Nru libgit2-0.20.0/tests/network/fetchlocal.c libgit2-0.22.2/tests/network/fetchlocal.c --- libgit2-0.20.0/tests/network/fetchlocal.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/network/fetchlocal.c 2015-03-24 16:10:45.000000000 +0000 @@ -4,6 +4,10 @@ #include "path.h" #include "remote.h" +static const char* tagger_name = "Vicent Marti"; +static const char* tagger_email = "vicent@github.com"; +static const char* tagger_message = "This is my tag.\n\nThere are many tags, but this one is mine\n"; + static int transfer_cb(const git_transfer_progress *stats, void *payload) { int *callcount = (int*)payload; @@ -17,6 +21,11 @@ cl_fixture_cleanup((char *)path); } +void test_network_fetchlocal__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + void test_network_fetchlocal__complete(void) { git_repository *repo; @@ -36,15 +45,291 @@ cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url)); git_remote_set_callbacks(origin, &callbacks); cl_git_pass(git_remote_connect(origin, GIT_DIRECTION_FETCH)); - cl_git_pass(git_remote_download(origin)); - cl_git_pass(git_remote_update_tips(origin)); + cl_git_pass(git_remote_download(origin, NULL)); + cl_git_pass(git_remote_update_tips(origin, NULL, NULL)); + + cl_git_pass(git_reference_list(&refnames, repo)); + cl_assert_equal_i(19, (int)refnames.count); + cl_assert(callcount > 0); + + git_strarray_free(&refnames); + git_remote_free(origin); + git_repository_free(repo); +} + +void test_network_fetchlocal__prune(void) +{ + git_repository *repo; + git_remote *origin; + int callcount = 0; + git_strarray refnames = {0}; + git_reference *ref; + git_repository *remote_repo = cl_git_sandbox_init("testrepo.git"); + const char *url = cl_git_path_url(git_repository_path(remote_repo)); + git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; + + callbacks.transfer_progress = transfer_cb; + callbacks.payload = &callcount; + + cl_set_cleanup(&cleanup_local_repo, "foo"); + cl_git_pass(git_repository_init(&repo, "foo", true)); + + cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url)); + git_remote_set_callbacks(origin, &callbacks); + cl_git_pass(git_remote_fetch(origin, NULL, NULL, NULL)); + + cl_git_pass(git_reference_list(&refnames, repo)); + cl_assert_equal_i(19, (int)refnames.count); + cl_assert(callcount > 0); + git_strarray_free(&refnames); + git_remote_free(origin); + + cl_git_pass(git_reference_lookup(&ref, remote_repo, "refs/heads/br2")); + cl_git_pass(git_reference_delete(ref)); + git_reference_free(ref); + + cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN)); + git_remote_set_callbacks(origin, &callbacks); + cl_git_pass(git_remote_connect(origin, GIT_DIRECTION_FETCH)); + cl_git_pass(git_remote_download(origin, NULL)); + cl_git_pass(git_remote_prune(origin)); + cl_git_pass(git_remote_update_tips(origin, NULL, NULL)); + + cl_git_pass(git_reference_list(&refnames, repo)); + cl_assert_equal_i(18, (int)refnames.count); + git_strarray_free(&refnames); + git_remote_free(origin); + + cl_git_pass(git_reference_lookup(&ref, remote_repo, "refs/heads/packed")); + cl_git_pass(git_reference_delete(ref)); + git_reference_free(ref); + + cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN)); + git_remote_set_callbacks(origin, &callbacks); + cl_git_pass(git_remote_connect(origin, GIT_DIRECTION_FETCH)); + cl_git_pass(git_remote_download(origin, NULL)); + cl_git_pass(git_remote_prune(origin)); + cl_git_pass(git_remote_update_tips(origin, NULL, NULL)); + + cl_git_pass(git_reference_list(&refnames, repo)); + cl_assert_equal_i(17, (int)refnames.count); + git_strarray_free(&refnames); + git_remote_free(origin); + + git_repository_free(repo); +} + +int update_tips_fail_on_call(const char *ref, const git_oid *old, const git_oid *new, void *data) +{ + GIT_UNUSED(ref); + GIT_UNUSED(old); + GIT_UNUSED(new); + GIT_UNUSED(data); + + cl_fail("update tips called"); + return 0; +} + +void assert_ref_exists(git_repository *repo, const char *name) +{ + git_reference *ref; + + cl_git_pass(git_reference_lookup(&ref, repo, name)); + git_reference_free(ref); +} + +void test_network_fetchlocal__prune_overlapping(void) +{ + git_repository *repo; + git_remote *origin; + int callcount = 0; + git_strarray refnames = {0}; + git_reference *ref; + git_config *config; + git_oid target; + + git_repository *remote_repo = cl_git_sandbox_init("testrepo.git"); + const char *url = cl_git_path_url(git_repository_path(remote_repo)); + + git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; + callbacks.transfer_progress = transfer_cb; + callbacks.payload = &callcount; + + cl_git_pass(git_reference_lookup(&ref, remote_repo, "refs/heads/master")); + git_oid_cpy(&target, git_reference_target(ref)); + git_reference_free(ref); + cl_git_pass(git_reference_create(&ref, remote_repo, "refs/pull/42/head", &target, 1, NULL, NULL)); + git_reference_free(ref); + + cl_set_cleanup(&cleanup_local_repo, "foo"); + cl_git_pass(git_repository_init(&repo, "foo", true)); + + cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url)); + git_remote_set_callbacks(origin, &callbacks); + + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_set_bool(config, "remote.origin.prune", true)); + cl_git_pass(git_config_set_multivar(config, "remote.origin.fetch", "^$", "refs/pull/*/head:refs/remotes/origin/pr/*")); + + git_remote_free(origin); + cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN)); + git_remote_set_callbacks(origin, &callbacks); + cl_git_pass(git_remote_fetch(origin, NULL, NULL, NULL)); + + assert_ref_exists(repo, "refs/remotes/origin/master"); + assert_ref_exists(repo, "refs/remotes/origin/pr/42"); + cl_git_pass(git_reference_list(&refnames, repo)); + cl_assert_equal_i(20, (int)refnames.count); + git_strarray_free(&refnames); + + cl_git_pass(git_config_delete_multivar(config, "remote.origin.fetch", "refs")); + cl_git_pass(git_config_set_multivar(config, "remote.origin.fetch", "^$", "refs/pull/*/head:refs/remotes/origin/pr/*")); + cl_git_pass(git_config_set_multivar(config, "remote.origin.fetch", "^$", "refs/heads/*:refs/remotes/origin/*")); + + git_remote_free(origin); + cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN)); + callbacks.update_tips = update_tips_fail_on_call; + git_remote_set_callbacks(origin, &callbacks); + cl_git_pass(git_remote_fetch(origin, NULL, NULL, NULL)); + + assert_ref_exists(repo, "refs/remotes/origin/master"); + assert_ref_exists(repo, "refs/remotes/origin/pr/42"); + cl_git_pass(git_reference_list(&refnames, repo)); + cl_assert_equal_i(20, (int)refnames.count); + git_strarray_free(&refnames); + + cl_git_pass(git_config_delete_multivar(config, "remote.origin.fetch", "refs")); + cl_git_pass(git_config_set_multivar(config, "remote.origin.fetch", "^$", "refs/heads/*:refs/remotes/origin/*")); + cl_git_pass(git_config_set_multivar(config, "remote.origin.fetch", "^$", "refs/pull/*/head:refs/remotes/origin/pr/*")); + + git_remote_free(origin); + cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN)); + callbacks.update_tips = update_tips_fail_on_call; + git_remote_set_callbacks(origin, &callbacks); + cl_git_pass(git_remote_fetch(origin, NULL, NULL, NULL)); + + git_config_free(config); + git_strarray_free(&refnames); + git_remote_free(origin); + git_repository_free(repo); +} + +void test_network_fetchlocal__fetchprune(void) +{ + git_repository *repo; + git_remote *origin; + int callcount = 0; + git_strarray refnames = {0}; + git_reference *ref; + git_config *config; + git_repository *remote_repo = cl_git_sandbox_init("testrepo.git"); + const char *url = cl_git_path_url(git_repository_path(remote_repo)); + git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; + + callbacks.transfer_progress = transfer_cb; + callbacks.payload = &callcount; + + cl_set_cleanup(&cleanup_local_repo, "foo"); + cl_git_pass(git_repository_init(&repo, "foo", true)); + + cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url)); + git_remote_set_callbacks(origin, &callbacks); + cl_git_pass(git_remote_fetch(origin, NULL, NULL, NULL)); cl_git_pass(git_reference_list(&refnames, repo)); cl_assert_equal_i(19, (int)refnames.count); cl_assert(callcount > 0); + git_strarray_free(&refnames); + git_remote_free(origin); + + cl_git_pass(git_reference_lookup(&ref, remote_repo, "refs/heads/br2")); + cl_git_pass(git_reference_delete(ref)); + git_reference_free(ref); + + cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN)); + git_remote_set_callbacks(origin, &callbacks); + cl_git_pass(git_remote_fetch(origin, NULL, NULL, NULL)); + cl_git_pass(git_remote_prune(origin)); + + cl_git_pass(git_reference_list(&refnames, repo)); + cl_assert_equal_i(18, (int)refnames.count); + git_strarray_free(&refnames); + git_remote_free(origin); + + cl_git_pass(git_reference_lookup(&ref, remote_repo, "refs/heads/packed")); + cl_git_pass(git_reference_delete(ref)); + git_reference_free(ref); + + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_set_bool(config, "remote.origin.prune", 1)); + git_config_free(config); + cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN)); + cl_assert_equal_i(1, git_remote_prune_refs(origin)); + git_remote_set_callbacks(origin, &callbacks); + cl_git_pass(git_remote_fetch(origin, NULL, NULL, NULL)); + cl_git_pass(git_reference_list(&refnames, repo)); + cl_assert_equal_i(17, (int)refnames.count); git_strarray_free(&refnames); git_remote_free(origin); + + git_repository_free(repo); +} + +void test_network_fetchlocal__prune_tag(void) +{ + git_repository *repo; + git_remote *origin; + int callcount = 0; + git_reference *ref; + git_config *config; + git_oid tag_id; + git_signature *tagger; + git_object *obj; + + git_repository *remote_repo = cl_git_sandbox_init("testrepo.git"); + const char *url = cl_git_path_url(git_repository_path(remote_repo)); + git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; + + callbacks.transfer_progress = transfer_cb; + callbacks.payload = &callcount; + + cl_set_cleanup(&cleanup_local_repo, "foo"); + cl_git_pass(git_repository_init(&repo, "foo", true)); + + cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url)); + git_remote_set_callbacks(origin, &callbacks); + cl_git_pass(git_remote_fetch(origin, NULL, NULL, NULL)); + git_remote_free(origin); + + cl_git_pass(git_revparse_single(&obj, repo, "origin/master")); + + cl_git_pass(git_reference_create(&ref, repo, "refs/remotes/origin/fake-remote", git_object_id(obj), 1, NULL, NULL)); + git_reference_free(ref); + + /* create signature */ + cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60)); + + cl_git_pass( + git_tag_create(&tag_id, repo, + "some-tag", obj, tagger, tagger_message, 0) + ); + git_signature_free(tagger); + + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_set_bool(config, "remote.origin.prune", 1)); + git_config_free(config); + cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN)); + cl_assert_equal_i(1, git_remote_prune_refs(origin)); + git_remote_set_callbacks(origin, &callbacks); + cl_git_pass(git_remote_fetch(origin, NULL, NULL, NULL)); + + assert_ref_exists(repo, "refs/tags/some-tag"); + cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, repo, "refs/remotes/origin/fake-remote")); + + git_object_free(obj); + git_remote_free(origin); + git_repository_free(repo); } @@ -74,8 +359,8 @@ cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url)); git_remote_set_callbacks(origin, &callbacks); cl_git_pass(git_remote_connect(origin, GIT_DIRECTION_FETCH)); - cl_git_pass(git_remote_download(origin)); - cl_git_pass(git_remote_update_tips(origin)); + cl_git_pass(git_remote_download(origin, NULL)); + cl_git_pass(git_remote_update_tips(origin, NULL, NULL)); git_strarray_free(&refnames); @@ -86,3 +371,157 @@ git_strarray_free(&refnames); git_remote_free(origin); } + +static int remote_mirror_cb(git_remote **out, git_repository *repo, + const char *name, const char *url, void *payload) +{ + int error; + git_remote *remote; + + GIT_UNUSED(payload); + + if ((error = git_remote_create(&remote, repo, name, url)) < 0) + return error; + + git_remote_clear_refspecs(remote); + + if ((error = git_remote_add_fetch(remote, "+refs/*:refs/*")) < 0) { + git_remote_free(remote); + return error; + } + + *out = remote; + return 0; +} + +void test_network_fetchlocal__clone_into_mirror(void) +{ + git_clone_options opts = GIT_CLONE_OPTIONS_INIT; + git_repository *repo; + git_reference *head; + + opts.bare = true; + opts.remote_cb = remote_mirror_cb; + cl_git_pass(git_clone(&repo, cl_git_fixture_url("testrepo.git"), "./foo.git", &opts)); + + cl_git_pass(git_reference_lookup(&head, repo, "HEAD")); + cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head)); + cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head)); + + git_reference_free(head); + git_repository_free(repo); + cl_fixture_cleanup("./foo.git"); +} + +void test_network_fetchlocal__multi_remotes(void) +{ + git_repository *repo = cl_git_sandbox_init("testrepo.git"); + git_remote *test, *test2; + git_strarray refnames = {0}; + git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; + + cl_set_cleanup(&cleanup_sandbox, NULL); + callbacks.transfer_progress = transfer_cb; + cl_git_pass(git_remote_lookup(&test, repo, "test")); + cl_git_pass(git_remote_set_url(test, cl_git_fixture_url("testrepo.git"))); + git_remote_set_callbacks(test, &callbacks); + cl_git_pass(git_remote_connect(test, GIT_DIRECTION_FETCH)); + cl_git_pass(git_remote_download(test, NULL)); + cl_git_pass(git_remote_update_tips(test, NULL, NULL)); + + cl_git_pass(git_reference_list(&refnames, repo)); + cl_assert_equal_i(32, (int)refnames.count); + git_strarray_free(&refnames); + + cl_git_pass(git_remote_lookup(&test2, repo, "test_with_pushurl")); + cl_git_pass(git_remote_set_url(test2, cl_git_fixture_url("testrepo.git"))); + git_remote_set_callbacks(test2, &callbacks); + cl_git_pass(git_remote_connect(test2, GIT_DIRECTION_FETCH)); + cl_git_pass(git_remote_download(test2, NULL)); + cl_git_pass(git_remote_update_tips(test2, NULL, NULL)); + + cl_git_pass(git_reference_list(&refnames, repo)); + cl_assert_equal_i(44, (int)refnames.count); + + git_strarray_free(&refnames); + git_remote_free(test); + git_remote_free(test2); +} + +static int sideband_cb(const char *str, int len, void *payload) +{ + int *count = (int *) payload; + + GIT_UNUSED(str); + GIT_UNUSED(len); + + (*count)++; + return 0; +} + +void test_network_fetchlocal__call_progress(void) +{ + git_repository *repo; + git_remote *remote; + git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; + int callcount = 0; + + cl_git_pass(git_repository_init(&repo, "foo.git", true)); + cl_set_cleanup(cleanup_local_repo, "foo.git"); + + cl_git_pass(git_remote_create_with_fetchspec(&remote, repo, "origin", cl_git_fixture_url("testrepo.git"), "+refs/heads/*:refs/heads/*")); + + callbacks.sideband_progress = sideband_cb; + callbacks.payload = &callcount; + cl_git_pass(git_remote_set_callbacks(remote, &callbacks)); + + cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL)); + cl_assert(callcount != 0); + + git_remote_free(remote); + git_repository_free(repo); +} + +void test_network_fetchlocal__prune_load_remote_prune_config(void) +{ + git_repository *repo; + git_remote *origin; + git_config *config; + git_repository *remote_repo = cl_git_sandbox_init("testrepo.git"); + const char *url = cl_git_path_url(git_repository_path(remote_repo)); + + cl_set_cleanup(&cleanup_local_repo, "foo"); + cl_git_pass(git_repository_init(&repo, "foo", true)); + + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_set_bool(config, "remote.origin.prune", 1)); + + cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url)); + cl_assert_equal_i(1, git_remote_prune_refs(origin)); + + git_config_free(config); + git_remote_free(origin); + git_repository_free(repo); +} + +void test_network_fetchlocal__prune_load_fetch_prune_config(void) +{ + git_repository *repo; + git_remote *origin; + git_config *config; + git_repository *remote_repo = cl_git_sandbox_init("testrepo.git"); + const char *url = cl_git_path_url(git_repository_path(remote_repo)); + + cl_set_cleanup(&cleanup_local_repo, "foo"); + cl_git_pass(git_repository_init(&repo, "foo", true)); + + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_set_bool(config, "fetch.prune", 1)); + + cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url)); + cl_assert_equal_i(1, git_remote_prune_refs(origin)); + + git_config_free(config); + git_remote_free(origin); + git_repository_free(repo); +} diff -Nru libgit2-0.20.0/tests/network/matchhost.c libgit2-0.22.2/tests/network/matchhost.c --- libgit2-0.20.0/tests/network/matchhost.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/network/matchhost.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,13 @@ +#include "clar_libgit2.h" +#include "netops.h" + +void test_network_matchhost__match(void) +{ + cl_git_pass(gitno__match_host("*.example.org", "www.example.org")); + cl_git_pass(gitno__match_host("*.foo.example.org", "www.foo.example.org")); + cl_git_fail(gitno__match_host("*.foo.example.org", "foo.example.org")); + cl_git_fail(gitno__match_host("*.foo.example.org", "www.example.org")); + cl_git_fail(gitno__match_host("*.example.org", "example.org")); + cl_git_fail(gitno__match_host("*.example.org", "www.foo.example.org")); + cl_git_fail(gitno__match_host("*.example.org", "blah.www.www.example.org")); +} diff -Nru libgit2-0.20.0/tests/network/refspecs.c libgit2-0.22.2/tests/network/refspecs.c --- libgit2-0.20.0/tests/network/refspecs.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/network/refspecs.c 2015-03-24 16:10:45.000000000 +0000 @@ -84,4 +84,65 @@ assert_refspec(GIT_DIRECTION_FETCH, "master", true); assert_refspec(GIT_DIRECTION_PUSH, "master", true); + + assert_refspec(GIT_DIRECTION_FETCH, "refs/pull/*/head:refs/remotes/origin/pr/*", true); +} + +static void assert_valid_transform(const char *refspec, const char *name, const char *result) +{ + git_refspec spec; + git_buf buf = GIT_BUF_INIT; + + git_refspec__parse(&spec, refspec, true); + cl_git_pass(git_refspec_transform(&buf, &spec, name)); + cl_assert_equal_s(result, buf.ptr); + + git_buf_free(&buf); + git_refspec__free(&spec); +} + +void test_network_refspecs__transform_mid_star(void) +{ + assert_valid_transform("refs/pull/*/head:refs/remotes/origin/pr/*", "refs/pull/23/head", "refs/remotes/origin/pr/23"); + assert_valid_transform("refs/heads/*:refs/remotes/origin/*", "refs/heads/master", "refs/remotes/origin/master"); + assert_valid_transform("refs/heads/*:refs/remotes/origin/*", "refs/heads/user/feature", "refs/remotes/origin/user/feature"); + assert_valid_transform("refs/heads/*:refs/heads/*", "refs/heads/master", "refs/heads/master"); + assert_valid_transform("refs/heads/*:refs/heads/*", "refs/heads/user/feature", "refs/heads/user/feature"); + assert_valid_transform("refs/*:refs/*", "refs/heads/master", "refs/heads/master"); +} + +static void assert_invalid_transform(const char *refspec, const char *name) +{ + git_refspec spec; + git_buf buf = GIT_BUF_INIT; + + git_refspec__parse(&spec, refspec, true); + cl_git_fail(git_refspec_transform(&buf, &spec, name)); + + git_buf_free(&buf); + git_refspec__free(&spec); +} + +void test_network_refspecs__invalid(void) +{ + assert_invalid_transform("refs/heads/*:refs/remotes/origin/*", "master"); + assert_invalid_transform("refs/heads/*:refs/remotes/origin/*", "refs/headz/master"); +} + +static void assert_invalid_rtransform(const char *refspec, const char *name) +{ + git_refspec spec; + git_buf buf = GIT_BUF_INIT; + + git_refspec__parse(&spec, refspec, true); + cl_git_fail(git_refspec_rtransform(&buf, &spec, name)); + + git_buf_free(&buf); + git_refspec__free(&spec); +} + +void test_network_refspecs__invalid_reverse(void) +{ + assert_invalid_rtransform("refs/heads/*:refs/remotes/origin/*", "master"); + assert_invalid_rtransform("refs/heads/*:refs/remotes/origin/*", "refs/remotes/o/master"); } diff -Nru libgit2-0.20.0/tests/network/remote/createthenload.c libgit2-0.22.2/tests/network/remote/createthenload.c --- libgit2-0.20.0/tests/network/remote/createthenload.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/network/remote/createthenload.c 2015-03-24 16:10:45.000000000 +0000 @@ -16,7 +16,7 @@ cl_git_pass(git_config_set_string(_config, "remote.origin.url", url)); git_config_free(_config); - cl_git_pass(git_remote_load(&_remote, _repo, "origin")); + cl_git_pass(git_remote_lookup(&_remote, _repo, "origin")); } void test_network_remote_createthenload__cleanup(void) diff -Nru libgit2-0.20.0/tests/network/remote/defaultbranch.c libgit2-0.22.2/tests/network/remote/defaultbranch.c --- libgit2-0.20.0/tests/network/remote/defaultbranch.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/network/remote/defaultbranch.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,108 @@ +#include "clar_libgit2.h" +#include "buffer.h" +#include "refspec.h" +#include "remote.h" + +static git_remote *g_remote; +static git_repository *g_repo_a, *g_repo_b; + +void test_network_remote_defaultbranch__initialize(void) +{ + g_repo_a = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_repository_init(&g_repo_b, "repo-b.git", true)); + cl_git_pass(git_remote_create(&g_remote, g_repo_b, "origin", git_repository_path(g_repo_a))); +} + +void test_network_remote_defaultbranch__cleanup(void) +{ + git_remote_free(g_remote); + git_repository_free(g_repo_b); + + cl_git_sandbox_cleanup(); + cl_fixture_cleanup("repo-b.git"); +} + +static void assert_default_branch(const char *should) +{ + git_buf name = GIT_BUF_INIT; + + cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH)); + cl_git_pass(git_remote_default_branch(&name, g_remote)); + cl_assert_equal_s(should, name.ptr); + git_buf_free(&name); +} + +void test_network_remote_defaultbranch__master(void) +{ + assert_default_branch("refs/heads/master"); +} + +void test_network_remote_defaultbranch__master_does_not_win(void) +{ + cl_git_pass(git_repository_set_head(g_repo_a, "refs/heads/not-good", NULL, NULL)); + assert_default_branch("refs/heads/not-good"); +} + +void test_network_remote_defaultbranch__master_on_detached(void) +{ + cl_git_pass(git_repository_detach_head(g_repo_a, NULL, NULL)); + assert_default_branch("refs/heads/master"); +} + +void test_network_remote_defaultbranch__no_default_branch(void) +{ + git_remote *remote_b; + const git_remote_head **heads; + size_t len; + git_buf buf = GIT_BUF_INIT; + + cl_git_pass(git_remote_create(&remote_b, g_repo_b, "self", git_repository_path(g_repo_b))); + cl_git_pass(git_remote_connect(remote_b, GIT_DIRECTION_FETCH)); + cl_git_pass(git_remote_ls(&heads, &len, remote_b)); + cl_assert_equal_i(0, len); + + cl_git_fail_with(GIT_ENOTFOUND, git_remote_default_branch(&buf, remote_b)); + + git_remote_free(remote_b); +} + +void test_network_remote_defaultbranch__detached_sharing_nonbranch_id(void) +{ + git_oid id, id_cloned; + git_reference *ref; + git_buf buf = GIT_BUF_INIT; + git_repository *cloned_repo; + + cl_git_pass(git_reference_name_to_id(&id, g_repo_a, "HEAD")); + cl_git_pass(git_repository_detach_head(g_repo_a, NULL, NULL)); + cl_git_pass(git_reference_remove(g_repo_a, "refs/heads/master")); + cl_git_pass(git_reference_remove(g_repo_a, "refs/heads/not-good")); + cl_git_pass(git_reference_create(&ref, g_repo_a, "refs/foo/bar", &id, 1, NULL, NULL)); + git_reference_free(ref); + + cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH)); + cl_git_fail_with(GIT_ENOTFOUND, git_remote_default_branch(&buf, g_remote)); + + cl_git_pass(git_clone(&cloned_repo, git_repository_path(g_repo_a), "./local-detached", NULL)); + + cl_assert(git_repository_head_detached(cloned_repo)); + cl_git_pass(git_reference_name_to_id(&id_cloned, g_repo_a, "HEAD")); + cl_assert(git_oid_equal(&id, &id_cloned)); + + git_repository_free(cloned_repo); +} + +void test_network_remote_defaultbranch__unborn_HEAD_with_branches(void) +{ + git_reference *ref; + git_repository *cloned_repo; + + cl_git_pass(git_reference_symbolic_create(&ref, g_repo_a, "HEAD", "refs/heads/i-dont-exist", 1, NULL, NULL)); + git_reference_free(ref); + + cl_git_pass(git_clone(&cloned_repo, git_repository_path(g_repo_a), "./semi-empty", NULL)); + + cl_assert(git_repository_head_unborn(cloned_repo)); + + git_repository_free(cloned_repo); +} diff -Nru libgit2-0.20.0/tests/network/remote/delete.c libgit2-0.22.2/tests/network/remote/delete.c --- libgit2-0.20.0/tests/network/remote/delete.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/network/remote/delete.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,46 @@ +#include "clar_libgit2.h" +#include "config/config_helpers.h" + +#include "repository.h" + +static git_repository *_repo; + +void test_network_remote_delete__initialize(void) +{ + _repo = cl_git_sandbox_init("testrepo.git"); +} + +void test_network_remote_delete__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_network_remote_delete__remove_remote_tracking_branches(void) +{ + git_reference *ref; + + cl_git_pass(git_remote_delete(_repo, "test")); + cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, _repo, "refs/remotes/test/master")); +} + +void test_network_remote_delete__remove_remote_configuration_settings(void) +{ + cl_assert(count_config_entries_match(_repo, "remote\\.test\\.+") > 0); + + cl_git_pass(git_remote_delete(_repo, "test")); + + cl_assert_equal_i(0, count_config_entries_match(_repo, "remote\\.test\\.+")); +} + +void test_network_remote_delete__remove_branch_upstream_configuration_settings(void) +{ + assert_config_entry_existence(_repo, "branch.mergeless.remote", true); + assert_config_entry_existence(_repo, "branch.master.remote", true); + + cl_git_pass(git_remote_delete(_repo, "test")); + + assert_config_entry_existence(_repo, "branch.mergeless.remote", false); + assert_config_entry_existence(_repo, "branch.mergeless.merge", false); + assert_config_entry_existence(_repo, "branch.master.remote", false); + assert_config_entry_existence(_repo, "branch.master.merge", false); +} diff -Nru libgit2-0.20.0/tests/network/remote/local.c libgit2-0.22.2/tests/network/remote/local.c --- libgit2-0.20.0/tests/network/remote/local.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/network/remote/local.c 2015-03-24 16:10:45.000000000 +0000 @@ -7,6 +7,14 @@ static git_buf file_path_buf = GIT_BUF_INIT; static git_remote *remote; +static char *push_refspec_strings[] = { + "refs/heads/master", +}; +static git_strarray push_array = { + push_refspec_strings, + 1, +}; + void test_network_remote_local__initialize(void) { cl_git_pass(git_repository_init(&repo, "remotelocal/", 0)); @@ -30,7 +38,7 @@ { git_buf_sets(&file_path_buf, cl_git_path_url(local_repository)); - cl_git_pass(git_remote_create_inmemory(&remote, repo, NULL, git_buf_cstr(&file_path_buf))); + cl_git_pass(git_remote_create_anonymous(&remote, repo, git_buf_cstr(&file_path_buf), NULL)); cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); } @@ -55,6 +63,17 @@ cl_assert_equal_i(refs_len, 28); } +void test_network_remote_local__retrieve_advertised_before_connect(void) +{ + const git_remote_head **refs; + size_t refs_len = 0; + + git_buf_sets(&file_path_buf, cl_git_path_url(cl_fixture("testrepo.git"))); + + cl_git_pass(git_remote_create_anonymous(&remote, repo, git_buf_cstr(&file_path_buf), NULL)); + cl_git_fail(git_remote_ls(&refs, &refs_len, remote)); +} + void test_network_remote_local__retrieve_advertised_references_after_disconnect(void) { const git_remote_head **refs; @@ -105,17 +124,21 @@ void test_network_remote_local__shorthand_fetch_refspec0(void) { - const char *refspec = "master:remotes/sloppy/master"; - const char *refspec2 = "master:boh/sloppy/master"; + char *refspec_strings[] = { + "master:remotes/sloppy/master", + "master:boh/sloppy/master", + }; + git_strarray array = { + refspec_strings, + 2, + }; git_reference *ref; connect_to_local_repository(cl_fixture("testrepo.git")); - cl_git_pass(git_remote_add_fetch(remote, refspec)); - cl_git_pass(git_remote_add_fetch(remote, refspec2)); - cl_git_pass(git_remote_download(remote)); - cl_git_pass(git_remote_update_tips(remote)); + cl_git_pass(git_remote_download(remote, &array)); + cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master")); git_reference_free(ref); @@ -126,18 +149,22 @@ void test_network_remote_local__shorthand_fetch_refspec1(void) { - const char *refspec = "master"; - const char *refspec2 = "hard_tag"; + char *refspec_strings[] = { + "master", + "hard_tag", + }; + git_strarray array = { + refspec_strings, + 2, + }; git_reference *ref; connect_to_local_repository(cl_fixture("testrepo.git")); git_remote_clear_refspecs(remote); - cl_git_pass(git_remote_add_fetch(remote, refspec)); - cl_git_pass(git_remote_add_fetch(remote, refspec2)); - cl_git_pass(git_remote_download(remote)); - cl_git_pass(git_remote_update_tips(remote)); + cl_git_pass(git_remote_download(remote, &array)); + cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master")); @@ -148,30 +175,38 @@ { git_reference *ref; - connect_to_local_repository(cl_fixture("testrepo.git")); + cl_git_pass(git_remote_create(&remote, repo, "tagopt", cl_git_path_url(cl_fixture("testrepo.git")))); git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_ALL); + cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL)); - cl_git_pass(git_remote_download(remote)); - cl_git_pass(git_remote_update_tips(remote)); - - - cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master")); - + cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/tagopt/master")); + git_reference_free(ref); cl_git_pass(git_reference_lookup(&ref, repo, "refs/tags/hard_tag")); git_reference_free(ref); + + git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_AUTO); + cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL)); + cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/tagopt/master")); + git_reference_free(ref); } void test_network_remote_local__push_to_bare_remote(void) { + char *refspec_strings[] = { + "master:master", + }; + git_strarray array = { + refspec_strings, + 1, + }; + /* Should be able to push to a bare remote */ git_remote *localremote; - git_push *push; /* Get some commits */ connect_to_local_repository(cl_fixture("testrepo.git")); - cl_git_pass(git_remote_add_fetch(remote, "master:master")); - cl_git_pass(git_remote_download(remote)); - cl_git_pass(git_remote_update_tips(remote)); + cl_git_pass(git_remote_download(remote, &array)); + cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); git_remote_disconnect(remote); /* Set up an empty bare repo to push into */ @@ -182,32 +217,75 @@ } /* Connect to the bare repo */ - cl_git_pass(git_remote_create_inmemory(&localremote, repo, NULL, "./localbare.git")); + cl_git_pass(git_remote_create_anonymous(&localremote, repo, "./localbare.git", NULL)); cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH)); /* Try to push */ - cl_git_pass(git_push_new(&push, localremote)); - cl_git_pass(git_push_add_refspec(push, "refs/heads/master:")); - cl_git_pass(git_push_finish(push)); - cl_assert(git_push_unpack_ok(push)); + cl_git_pass(git_remote_upload(remote, &push_array, NULL)); /* Clean up */ - git_push_free(push); git_remote_free(localremote); cl_fixture_cleanup("localbare.git"); } +void test_network_remote_local__push_to_bare_remote_with_file_url(void) +{ + char *refspec_strings[] = { + "master:master", + }; + git_strarray array = { + refspec_strings, + 1, + }; + /* Should be able to push to a bare remote */ + git_remote *localremote; + const char *url; + + /* Get some commits */ + connect_to_local_repository(cl_fixture("testrepo.git")); + cl_git_pass(git_remote_download(remote, &array)); + cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); + git_remote_disconnect(remote); + + /* Set up an empty bare repo to push into */ + { + git_repository *localbarerepo; + cl_git_pass(git_repository_init(&localbarerepo, "./localbare.git", 1)); + git_repository_free(localbarerepo); + } + + /* Create a file URL */ + url = cl_git_path_url("./localbare.git"); + + /* Connect to the bare repo */ + cl_git_pass(git_remote_create_anonymous(&localremote, repo, url, NULL)); + cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH)); + + /* Try to push */ + cl_git_pass(git_remote_upload(remote, &push_array, NULL)); + + /* Clean up */ + git_remote_free(localremote); + cl_fixture_cleanup("localbare.git"); +} + + void test_network_remote_local__push_to_non_bare_remote(void) { + char *refspec_strings[] = { + "master:master", + }; + git_strarray array = { + refspec_strings, + 1, + }; /* Shouldn't be able to push to a non-bare remote */ git_remote *localremote; - git_push *push; /* Get some commits */ connect_to_local_repository(cl_fixture("testrepo.git")); - cl_git_pass(git_remote_add_fetch(remote, "master:master")); - cl_git_pass(git_remote_download(remote)); - cl_git_pass(git_remote_update_tips(remote)); + cl_git_pass(git_remote_download(remote, &array)); + cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); git_remote_disconnect(remote); /* Set up an empty non-bare repo to push into */ @@ -218,17 +296,195 @@ } /* Connect to the bare repo */ - cl_git_pass(git_remote_create_inmemory(&localremote, repo, NULL, "./localnonbare")); + cl_git_pass(git_remote_create_anonymous(&localremote, repo, "./localnonbare", NULL)); cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH)); /* Try to push */ - cl_git_pass(git_push_new(&push, localremote)); - cl_git_pass(git_push_add_refspec(push, "refs/heads/master:")); - cl_git_fail_with(git_push_finish(push), GIT_EBAREREPO); - cl_assert_equal_i(0, git_push_unpack_ok(push)); + cl_git_fail_with(GIT_EBAREREPO, git_remote_upload(localremote, &push_array, NULL)); /* Clean up */ - git_push_free(push); git_remote_free(localremote); cl_fixture_cleanup("localbare.git"); } + +void test_network_remote_local__fetch(void) +{ + char *refspec_strings[] = { + "master:remotes/sloppy/master", + }; + git_strarray array = { + refspec_strings, + 1, + }; + + git_reflog *log; + const git_reflog_entry *entry; + git_signature *sig; + git_reference *ref; + + cl_git_pass(git_signature_now(&sig, "Foo Bar", "foo@example.com")); + + connect_to_local_repository(cl_fixture("testrepo.git")); + + cl_git_pass(git_remote_fetch(remote, &array, sig, "UPDAAAAAATE!!")); + + cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master")); + git_reference_free(ref); + + cl_git_pass(git_reflog_read(&log, repo, "refs/remotes/sloppy/master")); + cl_assert_equal_i(1, git_reflog_entrycount(log)); + entry = git_reflog_entry_byindex(log, 0); + cl_assert_equal_s("foo@example.com", git_reflog_entry_committer(entry)->email); + cl_assert_equal_s("UPDAAAAAATE!!", git_reflog_entry_message(entry)); + + git_reflog_free(log); + git_signature_free(sig); +} + +void test_network_remote_local__reflog(void) +{ + char *refspec_strings[] = { + "master:remotes/sloppy/master", + }; + git_strarray array = { + refspec_strings, + 1, + }; + + git_reflog *log; + const git_reflog_entry *entry; + git_signature *sig; + + cl_git_pass(git_signature_now(&sig, "Foo Bar", "foo@example.com")); + + connect_to_local_repository(cl_fixture("testrepo.git")); + + cl_git_pass(git_remote_download(remote, &array)); + cl_git_pass(git_remote_update_tips(remote, sig, "UPDAAAAAATE!!")); + + cl_git_pass(git_reflog_read(&log, repo, "refs/remotes/sloppy/master")); + cl_assert_equal_i(1, git_reflog_entrycount(log)); + entry = git_reflog_entry_byindex(log, 0); + cl_assert_equal_s("foo@example.com", git_reflog_entry_committer(entry)->email); + cl_assert_equal_s("UPDAAAAAATE!!", git_reflog_entry_message(entry)); + + git_reflog_free(log); + git_signature_free(sig); +} + +void test_network_remote_local__fetch_default_reflog_message(void) +{ + char *refspec_strings[] = { + "master:remotes/sloppy/master", + }; + git_strarray array = { + refspec_strings, + 1, + }; + + git_reflog *log; + const git_reflog_entry *entry; + git_signature *sig; + char expected_reflog_msg[1024]; + + cl_git_pass(git_signature_now(&sig, "Foo Bar", "foo@example.com")); + + connect_to_local_repository(cl_fixture("testrepo.git")); + + cl_git_pass(git_remote_fetch(remote, &array, sig, NULL)); + + cl_git_pass(git_reflog_read(&log, repo, "refs/remotes/sloppy/master")); + cl_assert_equal_i(1, git_reflog_entrycount(log)); + entry = git_reflog_entry_byindex(log, 0); + cl_assert_equal_s("foo@example.com", git_reflog_entry_committer(entry)->email); + + sprintf(expected_reflog_msg, "fetch %s", git_remote_url(remote)); + cl_assert_equal_s(expected_reflog_msg, git_reflog_entry_message(entry)); + + git_reflog_free(log); + git_signature_free(sig); +} + +void test_network_remote_local__opportunistic_update(void) +{ + git_reference *ref; + char *refspec_strings[] = { + "master", + }; + git_strarray array = { + refspec_strings, + 1, + }; + + /* this remote has a passive refspec of "refs/heads/:refs/remotes/origin/" */ + cl_git_pass(git_remote_create(&remote, repo, "origin", cl_git_fixture_url("testrepo.git"))); + /* and we pass the active refspec "master" */ + cl_git_pass(git_remote_fetch(remote, &array, NULL, NULL)); + + /* and we expect that to update our copy of origin's master */ + cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/origin/master")); + git_reference_free(ref); +} + +void test_network_remote_local__update_tips_for_new_remote(void) { + git_repository *src_repo; + git_repository *dst_repo; + git_remote *new_remote; + git_reference* branch; + + /* Copy test repo */ + cl_fixture_sandbox("testrepo.git"); + cl_git_pass(git_repository_open(&src_repo, "testrepo.git")); + + /* Set up an empty bare repo to push into */ + cl_git_pass(git_repository_init(&dst_repo, "./localbare.git", 1)); + + /* Push to bare repo */ + cl_git_pass(git_remote_create(&new_remote, src_repo, "bare", "./localbare.git")); + cl_git_pass(git_remote_connect(new_remote, GIT_DIRECTION_PUSH)); + cl_git_pass(git_remote_upload(new_remote, &push_array, NULL)); + + /* Update tips and make sure remote branch has been created */ + cl_git_pass(git_remote_update_tips(new_remote, NULL, NULL)); + cl_git_pass(git_branch_lookup(&branch, src_repo, "bare/master", GIT_BRANCH_REMOTE)); + + git_reference_free(branch); + git_remote_free(new_remote); + git_repository_free(dst_repo); + cl_fixture_cleanup("localbare.git"); + git_repository_free(src_repo); + cl_fixture_cleanup("testrepo.git"); +} + +void test_network_remote_local__push_delete(void) +{ + git_repository *src_repo; + git_repository *dst_repo; + git_remote *remote; + git_reference *ref; + char *spec_push[] = { "refs/heads/master" }; + char *spec_delete[] = { ":refs/heads/master" }; + git_strarray specs = { + spec_push, + 1, + }; + + src_repo = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_repository_init(&dst_repo, "target.git", 1)); + + cl_git_pass(git_remote_create(&remote, src_repo, "origin", "./target.git")); + + /* Push the master branch and verify it's there */ + cl_git_pass(git_remote_push(remote, &specs, NULL, NULL, NULL)); + cl_git_pass(git_reference_lookup(&ref, dst_repo, "refs/heads/master")); + git_reference_free(ref); + + specs.strings = spec_delete; + cl_git_pass(git_remote_push(remote, &specs, NULL, NULL, NULL)); + cl_git_fail(git_reference_lookup(&ref, dst_repo, "refs/heads/master")); + + git_remote_free(remote); + git_repository_free(dst_repo); + cl_fixture_cleanup("target.git"); + cl_git_sandbox_cleanup(); +} diff -Nru libgit2-0.20.0/tests/network/remote/remotes.c libgit2-0.22.2/tests/network/remote/remotes.c --- libgit2-0.20.0/tests/network/remote/remotes.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/network/remote/remotes.c 2015-03-24 16:10:45.000000000 +0000 @@ -11,7 +11,7 @@ { _repo = cl_git_sandbox_init("testrepo.git"); - cl_git_pass(git_remote_load(&_remote, _repo, "test")); + cl_git_pass(git_remote_lookup(&_remote, _repo, "test")); _refspec = git_remote_get_refspec(_remote, 0); cl_assert(_refspec != NULL); @@ -38,7 +38,7 @@ cl_assert_equal_s(git_remote__urlfordirection(_remote, GIT_DIRECTION_PUSH), "git://github.com/libgit2/libgit2"); - cl_git_pass(git_remote_load(&_remote2, _repo, "test_with_pushurl")); + cl_git_pass(git_remote_lookup(&_remote2, _repo, "test_with_pushurl")); cl_assert_equal_s(git_remote_name(_remote2), "test_with_pushurl"); cl_assert_equal_s(git_remote_url(_remote2), "git://github.com/libgit2/fetchlibgit2"); cl_assert_equal_s(git_remote_pushurl(_remote2), "git://github.com/libgit2/pushlibgit2"); @@ -60,49 +60,39 @@ cl_assert(git_remote_pushurl(_remote) == NULL); } -void test_network_remote_remotes__error_when_no_push_available(void) +void test_network_remote_remotes__error_when_not_found(void) { git_remote *r; - git_transport *t; - git_push *p; + cl_git_fail_with(git_remote_lookup(&r, _repo, "does-not-exist"), GIT_ENOTFOUND); - cl_git_pass(git_remote_create_inmemory(&r, _repo, NULL, cl_fixture("testrepo.git"))); + cl_assert(giterr_last() != NULL); + cl_assert(giterr_last()->klass == GITERR_CONFIG); +} - cl_git_pass(git_transport_local(&t,r,NULL)); +void test_network_remote_remotes__error_when_no_push_available(void) +{ + git_remote *r; + char *specs = { + "refs/heads/master", + }; + git_strarray arr = { + &specs, + 1, + }; - /* Make sure that push is really not available */ - t->push = NULL; - cl_git_pass(git_remote_set_transport(r, t)); - cl_git_pass(git_remote_connect(r, GIT_DIRECTION_PUSH)); - cl_git_pass(git_push_new(&p, r)); - cl_git_pass(git_push_add_refspec(p, "refs/heads/master")); - cl_git_fail_with(git_push_finish(p), GIT_ERROR); + cl_git_pass(git_remote_create_anonymous(&r, _repo, cl_fixture("testrepo.git"), NULL)); - git_push_free(p); - git_remote_free(r); -} + cl_git_pass(git_remote_set_transport(r, git_transport_local, NULL)); -void test_network_remote_remotes__parsing_ssh_remote(void) -{ - cl_assert( git_remote_valid_url("git@github.com:libgit2/libgit2.git") ); -} + cl_git_pass(git_remote_connect(r, GIT_DIRECTION_PUSH)); -void test_network_remote_remotes__parsing_local_path_fails_if_path_not_found(void) -{ - cl_assert( !git_remote_valid_url("/home/git/repos/libgit2.git") ); -} + /* Make sure that push is really not available */ + r->transport->push = NULL; -void test_network_remote_remotes__supported_transport_methods_are_supported(void) -{ - cl_assert( git_remote_supported_url("git://github.com/libgit2/libgit2") ); -} + cl_git_fail_with(-1, git_remote_upload(r, &arr, NULL)); -void test_network_remote_remotes__unsupported_transport_methods_are_unsupported(void) -{ -#ifndef GIT_SSH - cl_assert( !git_remote_supported_url("git@github.com:libgit2/libgit2.git") ); -#endif + git_remote_free(r); } void test_network_remote_remotes__refspec_parsing(void) @@ -129,6 +119,29 @@ cl_assert_equal_b(_refspec->push, false); } +void test_network_remote_remotes__dup(void) +{ + git_strarray array; + git_remote *dup; + + cl_git_pass(git_remote_dup(&dup, _remote)); + + cl_assert_equal_s(git_remote_name(dup), git_remote_name(_remote)); + cl_assert_equal_s(git_remote_url(dup), git_remote_url(_remote)); + cl_assert_equal_s(git_remote_pushurl(dup), git_remote_pushurl(_remote)); + + cl_git_pass(git_remote_get_fetch_refspecs(&array, _remote)); + cl_assert_equal_i(1, (int)array.count); + cl_assert_equal_s("+refs/heads/*:refs/remotes/test/*", array.strings[0]); + git_strarray_free(&array); + + cl_git_pass(git_remote_get_push_refspecs(&array, _remote)); + cl_assert_equal_i(0, (int)array.count); + git_strarray_free(&array); + + git_remote_free(dup); +} + void test_network_remote_remotes__add_pushspec(void) { size_t size; @@ -172,7 +185,7 @@ _remote = NULL; /* Load it from config and make sure everything matches */ - cl_git_pass(git_remote_load(&_remote, _repo, "upstream")); + cl_git_pass(git_remote_lookup(&_remote, _repo, "upstream")); cl_git_pass(git_remote_get_fetch_refspecs(&array, _remote)); cl_assert_equal_i(2, (int)array.count); @@ -195,7 +208,7 @@ git_remote_free(_remote); _remote = NULL; - cl_git_pass(git_remote_load(&_remote, _repo, "upstream")); + cl_git_pass(git_remote_lookup(&_remote, _repo, "upstream")); cl_assert(git_remote_pushurl(_remote) == NULL); } @@ -207,27 +220,20 @@ void test_network_remote_remotes__transform(void) { - char ref[1024] = {0}; + git_buf ref = GIT_BUF_INIT; - cl_git_pass(git_refspec_transform(ref, sizeof(ref), _refspec, "refs/heads/master")); - cl_assert_equal_s(ref, "refs/remotes/test/master"); + cl_git_pass(git_refspec_transform(&ref, _refspec, "refs/heads/master")); + cl_assert_equal_s(ref.ptr, "refs/remotes/test/master"); + git_buf_free(&ref); } void test_network_remote_remotes__transform_destination_to_source(void) { - char ref[1024] = {0}; - - cl_git_pass(git_refspec_rtransform(ref, sizeof(ref), _refspec, "refs/remotes/test/master")); - cl_assert_equal_s(ref, "refs/heads/master"); -} - -void test_network_remote_remotes__transform_r(void) -{ - git_buf buf = GIT_BUF_INIT; + git_buf ref = GIT_BUF_INIT; - cl_git_pass(git_refspec_transform_r(&buf, _refspec, "refs/heads/master")); - cl_assert_equal_s(git_buf_cstr(&buf), "refs/remotes/test/master"); - git_buf_free(&buf); + cl_git_pass(git_refspec_rtransform(&ref, _refspec, "refs/remotes/test/master")); + cl_assert_equal_s(ref.ptr, "refs/heads/master"); + git_buf_free(&ref); } void test_network_remote_remotes__missing_refspecs(void) @@ -239,11 +245,38 @@ cl_git_pass(git_repository_config(&cfg, _repo)); cl_git_pass(git_config_set_string(cfg, "remote.specless.url", "http://example.com")); - cl_git_pass(git_remote_load(&_remote, _repo, "specless")); + cl_git_pass(git_remote_lookup(&_remote, _repo, "specless")); git_config_free(cfg); } +void test_network_remote_remotes__nonmatch_upstream_refspec(void) +{ + git_config *config; + git_remote *remote; + char *specstr[] = { + "refs/tags/*:refs/tags/*", + }; + git_strarray specs = { + specstr, + 1, + }; + + cl_git_pass(git_remote_create(&remote, _repo, "taggy", git_repository_path(_repo))); + + /* + * Set the current branch's upstream remote to a dummy ref so we call into the code + * which tries to check for the current branch's upstream in the refspecs + */ + cl_git_pass(git_repository_config(&config, _repo)); + cl_git_pass(git_config_set_string(config, "branch.master.remote", "taggy")); + cl_git_pass(git_config_set_string(config, "branch.master.merge", "refs/heads/foo")); + + cl_git_pass(git_remote_fetch(remote, &specs, NULL, NULL)); + + git_remote_free(remote); +} + void test_network_remote_remotes__list(void) { git_strarray list; @@ -273,7 +306,7 @@ git_remote_free(_remote); _remote = NULL; - cl_assert_equal_i(GIT_ENOTFOUND, git_remote_load(&_remote, _repo, "just-left-few-minutes-ago")); + cl_assert_equal_i(GIT_ENOTFOUND, git_remote_lookup(&_remote, _repo, "just-left-few-minutes-ago")); } void test_network_remote_remotes__loading_with_an_invalid_name_returns_EINVALIDSPEC(void) @@ -281,7 +314,7 @@ git_remote_free(_remote); _remote = NULL; - cl_assert_equal_i(GIT_EINVALIDSPEC, git_remote_load(&_remote, _repo, "Inv@{id")); + cl_assert_equal_i(GIT_EINVALIDSPEC, git_remote_lookup(&_remote, _repo, "Inv@{id")); } /* @@ -304,7 +337,7 @@ git_remote_free(_remote); _remote = NULL; - cl_git_pass(git_remote_load(&_remote, _repo, "addtest")); + cl_git_pass(git_remote_lookup(&_remote, _repo, "addtest")); cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_AUTO, git_remote_autotag(_remote)); _refspec = git_vector_get(&_remote->refspecs, 0); @@ -327,7 +360,7 @@ { git_remote *remote; - cl_git_pass(git_remote_create_inmemory(&remote, _repo, NULL, "git://github.com/libgit2/libgit2")); + cl_git_pass(git_remote_create_anonymous(&remote, _repo, "git://github.com/libgit2/libgit2", NULL)); cl_assert_equal_p(NULL, git_remote_name(remote)); @@ -378,7 +411,7 @@ { git_remote *remote = NULL; - cl_git_pass(git_remote_load(&remote, _repo, "empty-remote-url")); + cl_git_pass(git_remote_lookup(&remote, _repo, "empty-remote-url")); cl_assert(remote->url == NULL); cl_assert(remote->pushurl == NULL); @@ -395,7 +428,7 @@ { git_remote *remote = NULL; - cl_git_pass(git_remote_load(&remote, _repo, "empty-remote-pushurl")); + cl_git_pass(git_remote_lookup(&remote, _repo, "empty-remote-pushurl")); cl_assert(remote->url == NULL); cl_assert(remote->pushurl == NULL); @@ -410,28 +443,7 @@ git_remote *remote = NULL; cl_git_fail_with( - git_remote_load(&remote, _repo, "no-remote-url"), GIT_ENOTFOUND); -} - -void test_network_remote_remotes__check_structure_version(void) -{ - git_transport transport = GIT_TRANSPORT_INIT; - const git_error *err; - - git_remote_free(_remote); - _remote = NULL; - cl_git_pass(git_remote_create_inmemory(&_remote, _repo, NULL, "test-protocol://localhost")); - - transport.version = 0; - cl_git_fail(git_remote_set_transport(_remote, &transport)); - err = giterr_last(); - cl_assert_equal_i(GITERR_INVALID, err->klass); - - giterr_clear(); - transport.version = 1024; - cl_git_fail(git_remote_set_transport(_remote, &transport)); - err = giterr_last(); - cl_assert_equal_i(GITERR_INVALID, err->klass); + git_remote_lookup(&remote, _repo, "no-remote-url"), GIT_ENOTFOUND); } void assert_cannot_create_remote(const char *name, int expected_error) @@ -487,7 +499,7 @@ git_strarray array; int i; - cl_git_pass(git_remote_create_inmemory(&remote, _repo, NULL, "git://github.com/libgit2/libgit2")); + cl_git_pass(git_remote_create_anonymous(&remote, _repo, "git://github.com/libgit2/libgit2", NULL)); for (i = 0; i < 3; i++) { cl_git_pass(git_remote_add_fetch(remote, fetch_refspecs[i])); @@ -508,3 +520,64 @@ git_remote_free(remote); } + +static int remote_single_branch(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload) +{ + char *fetch_refspecs[] = { + "refs/heads/first-merge:refs/remotes/origin/first-merge", + }; + git_strarray fetch_refspecs_strarray = { + fetch_refspecs, + 1, + }; + + GIT_UNUSED(payload); + + cl_git_pass(git_remote_create(out, repo, name, url)); + cl_git_pass(git_remote_set_fetch_refspecs(*out, &fetch_refspecs_strarray)); + + return 0; +} + +void test_network_remote_remotes__fetch_from_anonymous(void) +{ + git_remote *remote; + + cl_git_pass(git_remote_create_anonymous(&remote, _repo, cl_fixture("testrepo.git"), + "refs/heads/*:refs/other/*")); + cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL)); + git_remote_free(remote); +} + +void test_network_remote_remotes__single_branch(void) +{ + git_clone_options opts = GIT_CLONE_OPTIONS_INIT; + git_repository *repo; + git_strarray refs; + size_t i, count = 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_reference_list(&refs, repo)); + + for (i = 0; i < refs.count; i++) { + if (!git__prefixcmp(refs.strings[i], "refs/heads/")) + count++; + } + cl_assert_equal_i(1, count); + + git_strarray_free(&refs); + git_repository_free(repo); +} + +void test_network_remote_remotes__restricted_refspecs(void) +{ + git_clone_options opts = GIT_CLONE_OPTIONS_INIT; + git_repository *repo; + + opts.remote_cb = remote_single_branch; + + cl_git_fail_with(GIT_EINVALIDSPEC, git_clone(&repo, "git://github.com/libgit2/TestGitRepository", "./restrict-refspec", &opts)); +} diff -Nru libgit2-0.20.0/tests/network/remote/rename.c libgit2-0.22.2/tests/network/remote/rename.c --- libgit2-0.20.0/tests/network/remote/rename.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/network/remote/rename.c 2015-03-24 16:10:45.000000000 +0000 @@ -3,21 +3,16 @@ #include "repository.h" -static git_remote *_remote; static git_repository *_repo; +static const char *_remote_name = "test"; void test_network_remote_rename__initialize(void) { _repo = cl_git_sandbox_init("testrepo.git"); - - cl_git_pass(git_remote_load(&_remote, _repo, "test")); } void test_network_remote_rename__cleanup(void) { - git_remote_free(_remote); - _remote = NULL; - cl_git_sandbox_cleanup(); } @@ -33,10 +28,14 @@ void test_network_remote_rename__renaming_a_remote_moves_related_configuration_section(void) { + git_strarray problems = {0}; + assert_config_entry_existence(_repo, "remote.test.fetch", true); assert_config_entry_existence(_repo, "remote.just/renamed.fetch", false); - cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL)); + cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "just/renamed")); + cl_assert_equal_i(0, problems.count); + git_strarray_free(&problems); assert_config_entry_existence(_repo, "remote.test.fetch", false); assert_config_entry_existence(_repo, "remote.just/renamed.fetch", true); @@ -44,16 +43,24 @@ void test_network_remote_rename__renaming_a_remote_updates_branch_related_configuration_entries(void) { + git_strarray problems = {0}; + assert_config_entry_value(_repo, "branch.master.remote", "test"); - cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL)); + cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "just/renamed")); + cl_assert_equal_i(0, problems.count); + git_strarray_free(&problems); assert_config_entry_value(_repo, "branch.master.remote", "just/renamed"); } void test_network_remote_rename__renaming_a_remote_updates_default_fetchrefspec(void) { - cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL)); + git_strarray problems = {0}; + + cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "just/renamed")); + cl_assert_equal_i(0, problems.count); + git_strarray_free(&problems); assert_config_entry_value(_repo, "remote.just/renamed.fetch", "+refs/heads/*:refs/remotes/just/renamed/*"); } @@ -61,82 +68,78 @@ void test_network_remote_rename__renaming_a_remote_without_a_fetchrefspec_doesnt_create_one(void) { git_config *config; + git_remote *remote; + git_strarray problems = {0}; - git_remote_free(_remote); cl_git_pass(git_repository_config__weakptr(&config, _repo)); cl_git_pass(git_config_delete_entry(config, "remote.test.fetch")); - cl_git_pass(git_remote_load(&_remote, _repo, "test")); + cl_git_pass(git_remote_lookup(&remote, _repo, "test")); + git_remote_free(remote); assert_config_entry_existence(_repo, "remote.test.fetch", false); - cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL)); + cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "just/renamed")); + cl_assert_equal_i(0, problems.count); + git_strarray_free(&problems); assert_config_entry_existence(_repo, "remote.just/renamed.fetch", false); } -static int ensure_refspecs(const char* refspec_name, void *payload) -{ - int i = 0; - bool found = false; - const char ** exp = (const char **)payload; - - while (exp[i]) { - if (strcmp(exp[i++], refspec_name)) - continue; - - found = true; - break; - } - - cl_assert(found); - - return 0; -} - void test_network_remote_rename__renaming_a_remote_notifies_of_non_default_fetchrefspec(void) { git_config *config; + git_remote *remote; + git_strarray problems = {0}; - char *expected_refspecs[] = { - "+refs/*:refs/*", - NULL - }; - - git_remote_free(_remote); cl_git_pass(git_repository_config__weakptr(&config, _repo)); cl_git_pass(git_config_set_string(config, "remote.test.fetch", "+refs/*:refs/*")); - cl_git_pass(git_remote_load(&_remote, _repo, "test")); + cl_git_pass(git_remote_lookup(&remote, _repo, "test")); + git_remote_free(remote); - cl_git_pass(git_remote_rename(_remote, "just/renamed", ensure_refspecs, &expected_refspecs)); + cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "just/renamed")); + cl_assert_equal_i(1, problems.count); + cl_assert_equal_s("+refs/*:refs/*", problems.strings[0]); + git_strarray_free(&problems); assert_config_entry_value(_repo, "remote.just/renamed.fetch", "+refs/*:refs/*"); + + git_strarray_free(&problems); } void test_network_remote_rename__new_name_can_contain_dots(void) { - cl_git_pass(git_remote_rename(_remote, "just.renamed", dont_call_me_cb, NULL)); - cl_assert_equal_s("just.renamed", git_remote_name(_remote)); + git_strarray problems = {0}; + + cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "just.renamed")); + cl_assert_equal_i(0, problems.count); + git_strarray_free(&problems); + assert_config_entry_existence(_repo, "remote.just.renamed.fetch", true); } void test_network_remote_rename__new_name_must_conform_to_reference_naming_conventions(void) { + git_strarray problems = {0}; + cl_assert_equal_i( GIT_EINVALIDSPEC, - git_remote_rename(_remote, "new@{name", dont_call_me_cb, NULL)); + git_remote_rename(&problems, _repo, _remote_name, "new@{name")); } void test_network_remote_rename__renamed_name_is_persisted(void) { git_remote *renamed; git_repository *another_repo; + git_strarray problems = {0}; - cl_git_fail(git_remote_load(&renamed, _repo, "just/renamed")); + cl_git_fail(git_remote_lookup(&renamed, _repo, "just/renamed")); - cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL)); + cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "just/renamed")); + cl_assert_equal_i(0, problems.count); + git_strarray_free(&problems); cl_git_pass(git_repository_open(&another_repo, "testrepo.git")); - cl_git_pass(git_remote_load(&renamed, _repo, "just/renamed")); + cl_git_pass(git_remote_lookup(&renamed, _repo, "just/renamed")); git_remote_free(renamed); git_repository_free(another_repo); @@ -144,31 +147,109 @@ void test_network_remote_rename__cannot_overwrite_an_existing_remote(void) { - cl_assert_equal_i(GIT_EEXISTS, git_remote_rename(_remote, "test", dont_call_me_cb, NULL)); - cl_assert_equal_i(GIT_EEXISTS, git_remote_rename(_remote, "test_with_pushurl", dont_call_me_cb, NULL)); + git_strarray problems = {0}; + + cl_assert_equal_i(GIT_EEXISTS, git_remote_rename(&problems, _repo, _remote_name, "test")); + cl_assert_equal_i(GIT_EEXISTS, git_remote_rename(&problems, _repo, _remote_name, "test_with_pushurl")); } void test_network_remote_rename__renaming_a_remote_moves_the_underlying_reference(void) { git_reference *underlying; + git_strarray problems = {0}; cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&underlying, _repo, "refs/remotes/just/renamed")); cl_git_pass(git_reference_lookup(&underlying, _repo, "refs/remotes/test/master")); git_reference_free(underlying); - cl_git_pass(git_remote_rename(_remote, "just/renamed", dont_call_me_cb, NULL)); + cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "just/renamed")); + cl_assert_equal_i(0, problems.count); + git_strarray_free(&problems); cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&underlying, _repo, "refs/remotes/test/master")); cl_git_pass(git_reference_lookup(&underlying, _repo, "refs/remotes/just/renamed/master")); git_reference_free(underlying); } -void test_network_remote_rename__cannot_rename_an_inmemory_remote(void) +void test_network_remote_rename__overwrite_ref_in_target(void) { - git_remote *remote; + git_oid id; + char idstr[GIT_OID_HEXSZ + 1] = {0}; + git_reference *ref; + git_branch_t btype; + git_branch_iterator *iter; + git_strarray problems = {0}; - cl_git_pass(git_remote_create_inmemory(&remote, _repo, NULL, "file:///blah")); - cl_git_fail(git_remote_rename(remote, "newname", NULL, NULL)); + cl_git_pass(git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750")); + cl_git_pass(git_reference_create(&ref, _repo, "refs/remotes/renamed/master", &id, 1, NULL, NULL)); + git_reference_free(ref); - git_remote_free(remote); + cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "renamed")); + cl_assert_equal_i(0, problems.count); + git_strarray_free(&problems); + + /* make sure there's only one remote-tracking branch */ + cl_git_pass(git_branch_iterator_new(&iter, _repo, GIT_BRANCH_REMOTE)); + cl_git_pass(git_branch_next(&ref, &btype, iter)); + cl_assert_equal_s("refs/remotes/renamed/master", git_reference_name(ref)); + git_oid_fmt(idstr, git_reference_target(ref)); + cl_assert_equal_s("be3563ae3f795b2b4353bcce3a527ad0a4f7f644", idstr); + git_reference_free(ref); + + cl_git_fail_with(GIT_ITEROVER, git_branch_next(&ref, &btype, iter)); + git_branch_iterator_free(iter); +} + +void test_network_remote_rename__nonexistent_returns_enotfound(void) +{ + git_strarray problems = {0}; + + int err = git_remote_rename(&problems, _repo, "nonexistent", "renamed"); + + cl_assert_equal_i(GIT_ENOTFOUND, err); +} + +void test_network_remote_rename__symref_head(void) +{ + int error; + git_reference *ref; + git_branch_t btype; + git_branch_iterator *iter; + git_strarray problems = {0}; + char idstr[GIT_OID_HEXSZ + 1] = {0}; + git_vector refs; + + cl_git_pass(git_reference_symbolic_create(&ref, _repo, "refs/remotes/test/HEAD", "refs/remotes/test/master", 0, NULL, NULL)); + git_reference_free(ref); + + cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "renamed")); + cl_assert_equal_i(0, problems.count); + git_strarray_free(&problems); + + cl_git_pass(git_vector_init(&refs, 2, (git_vector_cmp) git_reference_cmp)); + cl_git_pass(git_branch_iterator_new(&iter, _repo, GIT_BRANCH_REMOTE)); + + while ((error = git_branch_next(&ref, &btype, iter)) == 0) { + cl_git_pass(git_vector_insert(&refs, ref)); + } + cl_assert_equal_i(GIT_ITEROVER, error); + git_vector_sort(&refs); + + cl_assert_equal_i(2, refs.length); + + ref = git_vector_get(&refs, 0); + cl_assert_equal_s("refs/remotes/renamed/HEAD", git_reference_name(ref)); + cl_assert_equal_s("refs/remotes/renamed/master", git_reference_symbolic_target(ref)); + git_reference_free(ref); + + ref = git_vector_get(&refs, 1); + cl_assert_equal_s("refs/remotes/renamed/master", git_reference_name(ref)); + git_oid_fmt(idstr, git_reference_target(ref)); + cl_assert_equal_s("be3563ae3f795b2b4353bcce3a527ad0a4f7f644", idstr); + git_reference_free(ref); + + git_vector_free(&refs); + + cl_git_fail_with(GIT_ITEROVER, git_branch_next(&ref, &btype, iter)); + git_branch_iterator_free(iter); } diff -Nru libgit2-0.20.0/tests/network/urlparse.c libgit2-0.22.2/tests/network/urlparse.c --- libgit2-0.20.0/tests/network/urlparse.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/network/urlparse.c 2015-03-24 16:10:45.000000000 +0000 @@ -33,6 +33,24 @@ cl_assert_equal_p(pass, NULL); } +void test_network_urlparse__root(void) +{ + cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, + "http://example.com/", "8080")); + cl_assert_equal_s(host, "example.com"); + cl_assert_equal_s(port, "8080"); + cl_assert_equal_s(path, "/"); + cl_assert_equal_p(user, NULL); + cl_assert_equal_p(pass, NULL); +} + +void test_network_urlparse__just_hostname(void) +{ + cl_git_fail_with(GIT_EINVALIDSPEC, + gitno_extract_url_parts(&host, &port, &path, &user, &pass, + "http://example.com", "8080")); +} + void test_network_urlparse__encoded_password(void) { cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, diff -Nru libgit2-0.20.0/tests/notes/notes.c libgit2-0.22.2/tests/notes/notes.c --- libgit2-0.20.0/tests/notes/notes.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/notes/notes.c 2015-03-24 16:10:45.000000000 +0000 @@ -21,7 +21,7 @@ git_blob *blob; cl_assert_equal_s(git_note_message(note), message); - cl_assert(!git_oid_cmp(git_note_oid(note), note_oid)); + cl_assert_equal_oid(git_note_id(note), note_oid); cl_git_pass(git_blob_lookup(&blob, _repo, note_oid)); cl_assert_equal_s(git_note_message(note), (const char *)git_blob_rawcontent(blob)); @@ -34,7 +34,7 @@ git_oid oid; cl_git_pass(git_oid_fromstr(&oid, target_sha)); - cl_git_pass(git_note_create(note_oid, _repo, _sig, _sig, canonical_namespace, &oid, message, 0)); + cl_git_pass(git_note_create(note_oid, _repo, canonical_namespace, _sig, _sig, &oid, message, 0)); } static struct { @@ -61,10 +61,10 @@ cl_assert(*count < EXPECTATIONS_COUNT); cl_git_pass(git_oid_fromstr(&expected_note_oid, list_expectations[*count].note_sha)); - cl_assert(git_oid_cmp(&expected_note_oid, blob_id) == 0); + cl_assert_equal_oid(&expected_note_oid, blob_id); cl_git_pass(git_oid_fromstr(&expected_target_oid, list_expectations[*count].annotated_object_sha)); - cl_assert(git_oid_cmp(&expected_target_oid, annotated_obj_id) == 0); + cl_assert_equal_oid(&expected_target_oid, annotated_obj_id); (*count)++; @@ -129,7 +129,7 @@ create_note(¬e_oid4, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 9fd7 and 4a20\n"); cl_assert_equal_i( - GIT_EUSER, + 1, git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes", note_cancel_cb, &retrieved_notes)); } @@ -195,12 +195,12 @@ cl_git_pass(git_oid_fromstr(&target_oid, "08b041783f40edfe12bb406c9c9a8a040177c125")); create_note(¬e_oid, NULL, "08b041783f40edfe12bb406c9c9a8a040177c125", "hello world\n"); - error = git_note_create(¬e_oid, _repo, _sig, _sig, NULL, &target_oid, "hello world\n", 0); + error = git_note_create(¬e_oid, _repo, NULL, _sig, _sig, &target_oid, "hello world\n", 0); cl_git_fail(error); cl_assert_equal_i(GIT_EEXISTS, error); create_note(¬e_oid, "refs/notes/some/namespace", "08b041783f40edfe12bb406c9c9a8a040177c125", "hello world\n"); - error = git_note_create(¬e_oid, _repo, _sig, _sig, "refs/notes/some/namespace", &target_oid, "hello world\n", 0); + error = git_note_create(¬e_oid, _repo, "refs/notes/some/namespace", _sig, _sig, &target_oid, "hello world\n", 0); cl_git_fail(error); cl_assert_equal_i(GIT_EEXISTS, error); } @@ -214,13 +214,13 @@ cl_git_pass(git_oid_fromstr(&target_oid, "08b041783f40edfe12bb406c9c9a8a040177c125")); create_note(¬e_oid, NULL, "08b041783f40edfe12bb406c9c9a8a040177c125", "hello old world\n"); - cl_git_pass(git_note_create(¬e_oid, _repo, _sig, _sig, NULL, &target_oid, "hello new world\n", 1)); + cl_git_pass(git_note_create(¬e_oid, _repo, NULL, _sig, _sig, &target_oid, "hello new world\n", 1)); cl_git_pass(git_note_read(¬e, _repo, NULL, &target_oid)); assert_note_equal(note, "hello new world\n", ¬e_oid); create_note(¬e_oid, "refs/notes/some/namespace", "08b041783f40edfe12bb406c9c9a8a040177c125", "hello old world\n"); - cl_git_pass(git_note_create(¬e_oid, _repo, _sig, _sig, "refs/notes/some/namespace", &target_oid, "hello new ref world\n", 1)); + cl_git_pass(git_note_create(¬e_oid, _repo, "refs/notes/some/namespace", _sig, _sig, &target_oid, "hello new ref world\n", 1)); cl_git_pass(git_note_read(&namespace_note, _repo, "refs/notes/some/namespace", &target_oid)); assert_note_equal(namespace_note, "hello new ref world\n", ¬e_oid); @@ -269,7 +269,7 @@ cl_git_pass(git_oid_fromstr(&target_oid, "08b041783f40edfe12bb406c9c9a8a040177c125")); for (i = 0; i < MESSAGES_COUNT; i++) { - cl_git_pass(git_note_create(¬e_oid, _repo, _sig, _sig, "refs/notes/fanout", &target_oid, messages[i], 0)); + cl_git_pass(git_note_create(¬e_oid, _repo, "refs/notes/fanout", _sig, _sig, &target_oid, messages[i], 0)); cl_git_pass(git_note_read(&_note, _repo, "refs/notes/fanout", &target_oid)); git_note_free(_note); @@ -290,7 +290,7 @@ cl_git_pass(git_note_read(¬e, _repo, "refs/notes/fanout", &target_oid)); cl_git_pass(git_oid_fromstr(¬e_oid, "08b041783f40edfe12bb406c9c9a8a040177c125")); - cl_assert(!git_oid_cmp(git_note_oid(note), ¬e_oid)); + cl_assert_equal_oid(git_note_id(note), ¬e_oid); git_note_free(note); } diff -Nru libgit2-0.20.0/tests/notes/notesref.c libgit2-0.22.2/tests/notes/notesref.c --- libgit2-0.20.0/tests/notes/notesref.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/notes/notesref.c 2015-03-24 16:10:45.000000000 +0000 @@ -42,17 +42,17 @@ cl_git_pass(git_config_set_string(_cfg, "core.notesRef", "refs/notes/mydefaultnotesref")); - cl_git_pass(git_note_create(¬e_oid, _repo, _sig, _sig, NULL, &oid, "test123test\n", 0)); + cl_git_pass(git_note_create(¬e_oid, _repo, NULL, _sig, _sig, &oid, "test123test\n", 0)); cl_git_pass(git_note_read(&_note, _repo, NULL, &oid)); cl_assert_equal_s("test123test\n", git_note_message(_note)); - cl_assert(!git_oid_cmp(git_note_oid(_note), ¬e_oid)); + cl_assert_equal_oid(git_note_id(_note), ¬e_oid); git_note_free(_note); cl_git_pass(git_note_read(&_note, _repo, "refs/notes/mydefaultnotesref", &oid)); cl_assert_equal_s("test123test\n", git_note_message(_note)); - cl_assert(!git_oid_cmp(git_note_oid(_note), ¬e_oid)); + cl_assert_equal_oid(git_note_id(_note), ¬e_oid); cl_git_pass(git_note_default_ref(&default_ref, _repo)); cl_assert_equal_s("refs/notes/mydefaultnotesref", default_ref); diff -Nru libgit2-0.20.0/tests/object/blob/filter.c libgit2-0.22.2/tests/object/blob/filter.c --- libgit2-0.20.0/tests/object/blob/filter.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/object/blob/filter.c 2015-03-24 16:10:45.000000000 +0000 @@ -112,7 +112,7 @@ git_config *cfg; int i; git_blob *blob; - git_buf out = GIT_BUF_INIT; + git_buf out = GIT_BUF_INIT, zeroed; cl_git_pass(git_repository_config(&cfg, g_repo)); cl_assert(cfg); @@ -121,19 +121,26 @@ cl_git_append2file("empty_standard_repo/.gitattributes", "*.txt text\n"); cl_git_pass(git_filter_list_load( - &fl, g_repo, NULL, "filename.txt", GIT_FILTER_TO_ODB)); + &fl, g_repo, NULL, "filename.txt", GIT_FILTER_TO_ODB, 0)); cl_assert(fl != NULL); for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) { cl_git_pass(git_blob_lookup(&blob, g_repo, &g_crlf_oids[i])); + /* try once with allocated blob */ cl_git_pass(git_filter_list_apply_to_blob(&out, fl, blob)); - cl_assert_equal_sz(g_crlf_filtered[i].size, out.size); - cl_assert_equal_i( 0, memcmp(out.ptr, g_crlf_filtered[i].ptr, out.size)); + /* try again with zeroed blob */ + memset(&zeroed, 0, sizeof(zeroed)); + cl_git_pass(git_filter_list_apply_to_blob(&zeroed, fl, blob)); + cl_assert_equal_sz(g_crlf_filtered[i].size, zeroed.size); + cl_assert_equal_i( + 0, memcmp(zeroed.ptr, g_crlf_filtered[i].ptr, zeroed.size)); + git_buf_free(&zeroed); + git_blob_free(blob); } diff -Nru libgit2-0.20.0/tests/object/blob/fromchunks.c libgit2-0.22.2/tests/object/blob/fromchunks.c --- libgit2-0.20.0/tests/object/blob/fromchunks.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/object/blob/fromchunks.c 2015-03-24 16:10:45.000000000 +0000 @@ -59,7 +59,7 @@ git_buf content = GIT_BUF_INIT; git_oid expected_oid, oid; int howmany = 7; - + cl_git_pass(git_oid_fromstr(&expected_oid, "321cbdf08803c744082332332838df6bd160f8f9")); cl_git_pass(git_blob_create_fromchunks(&oid, repo, NULL, text_chunked_source_cb, &howmany)); @@ -117,3 +117,40 @@ assert_named_chunked_blob("e9671e138a780833cb689753570fd10a55be84fb", "dummy.txt"); assert_named_chunked_blob("e9671e138a780833cb689753570fd10a55be84fb", "dummy.dunno"); } + +static int failing_chunked_source_cb( + char *content, size_t max_length, void *payload) +{ + int *count = (int *)payload; + + GIT_UNUSED(max_length); + + (*count)--; + if (*count == 0) + return -1234; + + strcpy(content, textual_content); + return (int)strlen(textual_content); +} + +void test_object_blob_fromchunks__can_stop_with_error(void) +{ + git_oid expected_oid, oid; + git_object *blob; + int howmany = 7; + + cl_git_pass(git_oid_fromstr( + &expected_oid, "321cbdf08803c744082332332838df6bd160f8f9")); + + cl_git_fail_with( + git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY), + GIT_ENOTFOUND); + + cl_git_fail_with(git_blob_create_fromchunks( + &oid, repo, NULL, failing_chunked_source_cb, &howmany), -1234); + + cl_git_fail_with( + git_object_lookup(&blob, repo, &expected_oid, GIT_OBJ_ANY), + GIT_ENOTFOUND); +} + diff -Nru libgit2-0.20.0/tests/object/cache.c libgit2-0.22.2/tests/object/cache.c --- libgit2-0.20.0/tests/object/cache.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/object/cache.c 2015-03-24 16:10:45.000000000 +0000 @@ -229,7 +229,7 @@ #ifdef GIT_THREADS for (th = 0; th < THREADCOUNT; ++th) { - cl_git_pass(git_thread_join(t[th], &data)); + cl_git_pass(git_thread_join(&t[th], &data)); cl_assert_equal_i(th, ((int *)data)[0]); git__free(data); } @@ -276,7 +276,7 @@ #ifdef GIT_THREADS for (th = 0; th < THREADCOUNT*2; ++th) { void *rval; - cl_git_pass(git_thread_join(t[th], &rval)); + cl_git_pass(git_thread_join(&t[th], &rval)); cl_assert_equal_i(th, *((int *)rval)); } #endif diff -Nru libgit2-0.20.0/tests/object/commit/commitstagedfile.c libgit2-0.22.2/tests/object/commit/commitstagedfile.c --- libgit2-0.20.0/tests/object/commit/commitstagedfile.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/object/commit/commitstagedfile.c 2015-03-24 16:10:45.000000000 +0000 @@ -25,7 +25,7 @@ git_oid expected_blob_oid, tree_oid, expected_tree_oid, commit_oid, expected_commit_oid; git_signature *signature; git_tree *tree; - char buffer[128]; + git_buf buffer; /* * The test below replicates the following git scenario @@ -77,7 +77,7 @@ entry = git_index_get_byindex(index, 0); - cl_assert(git_oid_cmp(&expected_blob_oid, &entry->oid) == 0); + cl_assert(git_oid_cmp(&expected_blob_oid, &entry->id) == 0); /* * Information about index entry should match test file @@ -111,7 +111,8 @@ cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid)); - cl_assert_equal_i(16, git_message_prettify(buffer, 128, "Initial commit", 0)); + memset(&buffer, 0, sizeof(git_buf)); + cl_git_pass(git_message_prettify(&buffer, "Initial commit", 0, '#')); cl_git_pass(git_commit_create_v( &commit_oid, @@ -120,13 +121,99 @@ signature, signature, NULL, - buffer, + buffer.ptr, tree, 0)); cl_assert(git_oid_cmp(&expected_commit_oid, &commit_oid) == 0); + git_buf_free(&buffer); git_signature_free(signature); git_tree_free(tree); git_index_free(index); } + +static void assert_commit_tree_has_n_entries(git_commit *c, int count) +{ + git_tree *tree; + cl_git_pass(git_commit_tree(&tree, c)); + cl_assert_equal_i(count, git_tree_entrycount(tree)); + git_tree_free(tree); +} + +static void assert_commit_is_head_(git_commit *c, const char *file, int line) +{ + git_commit *head; + cl_git_pass(git_revparse_single((git_object **)&head, repo, "HEAD")); + clar__assert(git_oid_equal(git_commit_id(c), git_commit_id(head)), file, line, "Commit is not the HEAD", NULL, 1); + git_commit_free(head); +} +#define assert_commit_is_head(C) assert_commit_is_head_((C),__FILE__,__LINE__) + +void test_object_commit_commitstagedfile__amend_commit(void) +{ + git_index *index; + git_oid old_oid, new_oid, tree_oid; + git_commit *old_commit, *new_commit; + git_tree *tree; + + /* make a commit */ + + cl_git_mkfile("treebuilder/myfile", "This is a file\n"); + cl_git_pass(git_repository_index(&index, repo)); + cl_git_pass(git_index_add_bypath(index, "myfile")); + cl_repo_commit_from_index(&old_oid, repo, NULL, 0, "first commit"); + + cl_git_pass(git_commit_lookup(&old_commit, repo, &old_oid)); + + cl_assert_equal_i(0, git_commit_parentcount(old_commit)); + assert_commit_tree_has_n_entries(old_commit, 1); + assert_commit_is_head(old_commit); + + /* let's amend the message of the HEAD commit */ + + cl_git_pass(git_commit_amend( + &new_oid, old_commit, "HEAD", NULL, NULL, NULL, "Initial commit", NULL)); + + /* fail because the commit isn't the tip of the branch anymore */ + cl_git_fail(git_commit_amend( + &new_oid, old_commit, "HEAD", NULL, NULL, NULL, "Initial commit", NULL)); + + cl_git_pass(git_commit_lookup(&new_commit, repo, &new_oid)); + + cl_assert_equal_i(0, git_commit_parentcount(new_commit)); + assert_commit_tree_has_n_entries(new_commit, 1); + assert_commit_is_head(new_commit); + + git_commit_free(old_commit); + + old_commit = new_commit; + + /* let's amend the tree of that last commit */ + + cl_git_mkfile("treebuilder/anotherfile", "This is another file\n"); + cl_git_pass(git_index_add_bypath(index, "anotherfile")); + cl_git_pass(git_index_write_tree(&tree_oid, index)); + cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid)); + cl_assert_equal_i(2, git_tree_entrycount(tree)); + + /* fail to amend on a ref which does not exist */ + cl_git_fail_with(GIT_ENOTFOUND, git_commit_amend( + &new_oid, old_commit, "refs/heads/nope", NULL, NULL, NULL, "Initial commit", tree)); + + cl_git_pass(git_commit_amend( + &new_oid, old_commit, "HEAD", NULL, NULL, NULL, "Initial commit", tree)); + git_tree_free(tree); + + cl_git_pass(git_commit_lookup(&new_commit, repo, &new_oid)); + + cl_assert_equal_i(0, git_commit_parentcount(new_commit)); + assert_commit_tree_has_n_entries(new_commit, 2); + assert_commit_is_head(new_commit); + + /* cleanup */ + + git_commit_free(old_commit); + git_commit_free(new_commit); + git_index_free(index); +} diff -Nru libgit2-0.20.0/tests/object/lookupbypath.c libgit2-0.22.2/tests/object/lookupbypath.c --- libgit2-0.20.0/tests/object/lookupbypath.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/object/lookupbypath.c 2015-03-24 16:10:45.000000000 +0000 @@ -52,16 +52,16 @@ { cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)g_root_tree, "subdir/subdir_test2.txt", GIT_OBJ_BLOB)); - cl_assert_equal_i(0, git_oid_cmp(git_object_id(g_expectedobject), - git_object_id(g_actualobject))); + cl_assert_equal_oid(git_object_id(g_expectedobject), + git_object_id(g_actualobject)); } void test_object_lookupbypath__from_head_commit(void) { cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)g_head_commit, "subdir/subdir_test2.txt", GIT_OBJ_BLOB)); - cl_assert_equal_i(0, git_oid_cmp(git_object_id(g_expectedobject), - git_object_id(g_actualobject))); + cl_assert_equal_oid(git_object_id(g_expectedobject), + git_object_id(g_actualobject)); } void test_object_lookupbypath__from_subdir_tree(void) @@ -74,8 +74,8 @@ cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)tree, "subdir_test2.txt", GIT_OBJ_BLOB)); - cl_assert_equal_i(0, git_oid_cmp(git_object_id(g_expectedobject), - git_object_id(g_actualobject))); + cl_assert_equal_oid(git_object_id(g_expectedobject), + git_object_id(g_actualobject)); git_tree_entry_free(entry); git_tree_free(tree); diff -Nru libgit2-0.20.0/tests/object/message.c libgit2-0.22.2/tests/object/message.c --- libgit2-0.20.0/tests/object/message.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/object/message.c 2015-03-24 16:10:45.000000000 +0000 @@ -6,7 +6,7 @@ { git_buf prettified_message = GIT_BUF_INIT; - git_message__prettify(&prettified_message, input, strip_comments); + git_message_prettify(&prettified_message, input, strip_comments, '#'); cl_assert_equal_s(expected_output, git_buf_cstr(&prettified_message)); git_buf_free(&prettified_message); @@ -172,65 +172,28 @@ void test_object_message__message_prettify(void) { - char buffer[100]; + git_buf buffer; - cl_assert(git_message_prettify(buffer, sizeof(buffer), "", 0) == 1); - cl_assert_equal_s(buffer, ""); - cl_assert(git_message_prettify(buffer, sizeof(buffer), "", 1) == 1); - cl_assert_equal_s(buffer, ""); - - cl_assert_equal_i(7, git_message_prettify(buffer, sizeof(buffer), "Short", 0)); - cl_assert_equal_s("Short\n", buffer); - cl_assert_equal_i(7, git_message_prettify(buffer, sizeof(buffer), "Short", 1)); - cl_assert_equal_s("Short\n", buffer); - - cl_assert(git_message_prettify(buffer, sizeof(buffer), "This is longer\nAnd multiline\n# with some comments still in\n", 0) > 0); - cl_assert_equal_s(buffer, "This is longer\nAnd multiline\n# with some comments still in\n"); - - cl_assert(git_message_prettify(buffer, sizeof(buffer), "This is longer\nAnd multiline\n# with some comments still in\n", 1) > 0); - cl_assert_equal_s(buffer, "This is longer\nAnd multiline\n"); - - /* try out overflow */ - cl_assert(git_message_prettify(buffer, sizeof(buffer), - "1234567890" "1234567890" "1234567890" "1234567890" "1234567890" - "1234567890" "1234567890" "1234567890" "1234567890" "12345678", - 0) > 0); - cl_assert_equal_s(buffer, - "1234567890" "1234567890" "1234567890" "1234567890" "1234567890" - "1234567890" "1234567890" "1234567890" "1234567890" "12345678\n"); - - cl_assert(git_message_prettify(buffer, sizeof(buffer), - "1234567890" "1234567890" "1234567890" "1234567890" "1234567890" - "1234567890" "1234567890" "1234567890" "1234567890" "12345678\n", - 0) > 0); - cl_assert_equal_s(buffer, - "1234567890" "1234567890" "1234567890" "1234567890" "1234567890" - "1234567890" "1234567890" "1234567890" "1234567890" "12345678\n"); - - cl_git_fail(git_message_prettify(buffer, sizeof(buffer), - "1234567890" "1234567890" "1234567890" "1234567890" "1234567890" - "1234567890" "1234567890" "1234567890" "1234567890" "123456789", - 0)); - cl_git_fail(git_message_prettify(buffer, sizeof(buffer), - "1234567890" "1234567890" "1234567890" "1234567890" "1234567890" - "1234567890" "1234567890" "1234567890" "1234567890" "123456789\n", - 0)); - cl_git_fail(git_message_prettify(buffer, sizeof(buffer), - "1234567890" "1234567890" "1234567890" "1234567890" "1234567890" - "1234567890" "1234567890" "1234567890" "1234567890" "1234567890", - 0)); - cl_git_fail(git_message_prettify(buffer, sizeof(buffer), - "1234567890" "1234567890" "1234567890" "1234567890" "1234567890" - "1234567890" "1234567890" "1234567890" "1234567890" "1234567890""x", - 0)); - - cl_assert(git_message_prettify(buffer, sizeof(buffer), - "1234567890" "1234567890" "1234567890" "1234567890" "1234567890\n" - "# 1234567890" "1234567890" "1234567890" "1234567890" "1234567890\n" - "1234567890", - 1) > 0); - - cl_assert(git_message_prettify(NULL, 0, "", 0) == 1); - cl_assert(git_message_prettify(NULL, 0, "Short test", 0) == 12); - cl_assert(git_message_prettify(NULL, 0, "Test\n# with\nComments", 1) == 15); + memset(&buffer, 0, sizeof(buffer)); + cl_git_pass(git_message_prettify(&buffer, "", 0, '#')); + cl_assert_equal_s(buffer.ptr, ""); + git_buf_free(&buffer); + cl_git_pass(git_message_prettify(&buffer, "", 1, '#')); + cl_assert_equal_s(buffer.ptr, ""); + git_buf_free(&buffer); + + cl_git_pass(git_message_prettify(&buffer, "Short", 0, '#')); + cl_assert_equal_s("Short\n", buffer.ptr); + git_buf_free(&buffer); + cl_git_pass(git_message_prettify(&buffer, "Short", 1, '#')); + cl_assert_equal_s("Short\n", buffer.ptr); + git_buf_free(&buffer); + + cl_git_pass(git_message_prettify(&buffer, "This is longer\nAnd multiline\n# with some comments still in\n", 0, '#')); + cl_assert_equal_s(buffer.ptr, "This is longer\nAnd multiline\n# with some comments still in\n"); + git_buf_free(&buffer); + + cl_git_pass(git_message_prettify(&buffer, "This is longer\nAnd multiline\n# with some comments still in\n", 1, '#')); + cl_assert_equal_s(buffer.ptr, "This is longer\nAnd multiline\n"); + git_buf_free(&buffer); } diff -Nru libgit2-0.20.0/tests/object/peel.c libgit2-0.22.2/tests/object/peel.c --- libgit2-0.20.0/tests/object/peel.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/object/peel.c 2015-03-24 16:10:45.000000000 +0000 @@ -29,7 +29,7 @@ cl_git_pass(git_object_peel(&peeled, obj, requested_type)); cl_git_pass(git_oid_fromstr(&expected_oid, expected_sha)); - cl_assert_equal_i(0, git_oid_cmp(&expected_oid, git_object_id(peeled))); + cl_assert_equal_oid(&expected_oid, git_object_id(peeled)); cl_assert_equal_i(expected_type, git_object_type(peeled)); @@ -63,28 +63,47 @@ "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_BLOB); } -void test_object_peel__can_peel_a_tag(void) +void test_object_peel__tag(void) { assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ_COMMIT, "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_COMMIT); assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ_TREE, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TREE); + assert_peel_error(GIT_EPEEL, "7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ_BLOB); + assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJ_ANY, + "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_COMMIT); } -void test_object_peel__can_peel_a_commit(void) +void test_object_peel__commit(void) { + assert_peel_error(GIT_EINVALIDSPEC, "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_BLOB); assert_peel("e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_TREE, - "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TREE); + "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TREE); + assert_peel("e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_COMMIT, + "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_COMMIT); + assert_peel_error(GIT_EINVALIDSPEC, "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_TAG); + assert_peel("e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_ANY, + "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TREE); } -void test_object_peel__cannot_peel_a_tree(void) +void test_object_peel__tree(void) { - assert_peel_error(GIT_EAMBIGUOUS, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_BLOB); + assert_peel_error(GIT_EINVALIDSPEC, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_BLOB); + assert_peel("53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TREE, + "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TREE); + assert_peel_error(GIT_EINVALIDSPEC, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_COMMIT); + assert_peel_error(GIT_EINVALIDSPEC, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TAG); + assert_peel_error(GIT_EINVALIDSPEC, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_ANY); } -void test_object_peel__cannot_peel_a_blob(void) +void test_object_peel__blob(void) { - assert_peel_error(GIT_ENOTFOUND, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_COMMIT); + assert_peel("0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_BLOB, + "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_BLOB); + assert_peel_error(GIT_EINVALIDSPEC, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_TREE); + assert_peel_error(GIT_EINVALIDSPEC, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_COMMIT); + assert_peel_error(GIT_EINVALIDSPEC, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_TAG); + assert_peel_error(GIT_EINVALIDSPEC, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_ANY); } void test_object_peel__target_any_object_for_type_change(void) @@ -96,10 +115,4 @@ /* commit to tree */ assert_peel("e90810b8df3e80c413d903f631643c716887138d", GIT_OBJ_ANY, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_TREE); - - /* fail to peel tree */ - assert_peel_error(GIT_EAMBIGUOUS, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJ_ANY); - - /* fail to peel blob */ - assert_peel_error(GIT_ENOTFOUND, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJ_ANY); } diff -Nru libgit2-0.20.0/tests/object/raw/chars.c libgit2-0.22.2/tests/object/raw/chars.c --- libgit2-0.20.0/tests/object/raw/chars.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/object/raw/chars.c 2015-03-24 16:10:45.000000000 +0000 @@ -12,7 +12,7 @@ 0xb7, 0x75, 0x21, 0x3c, 0x23, 0xa8, 0xbd, 0x74, 0xf5, 0xe0, }; - char in[41] = "16a67770b7d8d72317c4b775213c23a8bd74f5e0"; + char in[] = "16a67770b7d8d72317c4b775213c23a8bd74f5e0"; unsigned int i; for (i = 0; i < 256; i++) { diff -Nru libgit2-0.20.0/tests/object/raw/compare.c libgit2-0.22.2/tests/object/raw/compare.c --- libgit2-0.20.0/tests/object/raw/compare.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/object/raw/compare.c 2015-03-24 16:10:45.000000000 +0000 @@ -90,7 +90,7 @@ cl_assert_equal_s(exp, out); } -void test_object_raw_compare__compare_allocfmt_oids(void) +void test_object_raw_compare__compare_static_oids(void) { const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0"; git_oid in; @@ -98,10 +98,9 @@ cl_git_pass(git_oid_fromstr(&in, exp)); - out = git_oid_allocfmt(&in); + out = git_oid_tostr_s(&in); cl_assert(out); cl_assert_equal_s(exp, out); - git__free(out); } void test_object_raw_compare__compare_pathfmt_oids(void) diff -Nru libgit2-0.20.0/tests/object/shortid.c libgit2-0.22.2/tests/object/shortid.c --- libgit2-0.20.0/tests/object/shortid.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/object/shortid.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,51 @@ +#include "clar_libgit2.h" + +git_repository *_repo; + +void test_object_shortid__initialize(void) +{ + cl_git_pass(git_repository_open(&_repo, cl_fixture("duplicate.git"))); +} + +void test_object_shortid__cleanup(void) +{ + git_repository_free(_repo); + _repo = NULL; +} + +void test_object_shortid__select(void) +{ + git_oid full; + git_object *obj; + git_buf shorty = {0}; + + git_oid_fromstr(&full, "ce013625030ba8dba906f756967f9e9ca394464a"); + cl_git_pass(git_object_lookup(&obj, _repo, &full, GIT_OBJ_ANY)); + cl_git_pass(git_object_short_id(&shorty, obj)); + cl_assert_equal_i(7, shorty.size); + cl_assert_equal_s("ce01362", shorty.ptr); + git_object_free(obj); + + git_oid_fromstr(&full, "038d718da6a1ebbc6a7780a96ed75a70cc2ad6e2"); + cl_git_pass(git_object_lookup(&obj, _repo, &full, GIT_OBJ_ANY)); + cl_git_pass(git_object_short_id(&shorty, obj)); + cl_assert_equal_i(7, shorty.size); + cl_assert_equal_s("038d718", shorty.ptr); + git_object_free(obj); + + git_oid_fromstr(&full, "dea509d097ce692e167dfc6a48a7a280cc5e877e"); + cl_git_pass(git_object_lookup(&obj, _repo, &full, GIT_OBJ_ANY)); + cl_git_pass(git_object_short_id(&shorty, obj)); + cl_assert_equal_i(9, shorty.size); + cl_assert_equal_s("dea509d09", shorty.ptr); + git_object_free(obj); + + git_oid_fromstr(&full, "dea509d0b3cb8ee0650f6ca210bc83f4678851ba"); + cl_git_pass(git_object_lookup(&obj, _repo, &full, GIT_OBJ_ANY)); + cl_git_pass(git_object_short_id(&shorty, obj)); + cl_assert_equal_i(9, shorty.size); + cl_assert_equal_s("dea509d0b", shorty.ptr); + git_object_free(obj); + + git_buf_free(&shorty); +} diff -Nru libgit2-0.20.0/tests/object/tree/attributes.c libgit2-0.22.2/tests/object/tree/attributes.c --- libgit2-0.20.0/tests/object/tree/attributes.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/object/tree/attributes.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,9 +1,21 @@ #include "clar_libgit2.h" #include "tree.h" +static git_repository *repo; + static const char *blob_oid = "3d0970ec547fc41ef8a5882dde99c6adce65b021"; static const char *tree_oid = "1b05fdaa881ee45b48cbaa5e9b037d667a47745e"; +void test_object_tree_attributes__initialize(void) +{ + repo = cl_git_sandbox_init("deprecated-mode.git"); +} + +void test_object_tree_attributes__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + void test_object_tree_attributes__ensure_correctness_of_attributes_on_insertion(void) { git_treebuilder *builder; @@ -11,7 +23,7 @@ cl_git_pass(git_oid_fromstr(&oid, blob_oid)); - cl_git_pass(git_treebuilder_create(&builder, NULL)); + cl_git_pass(git_treebuilder_new(&builder, repo, NULL)); cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0777777)); cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0100666)); @@ -22,12 +34,10 @@ void test_object_tree_attributes__group_writable_tree_entries_created_with_an_antique_git_version_can_still_be_accessed(void) { - git_repository *repo; git_oid tid; git_tree *tree; const git_tree_entry *entry; - cl_git_pass(git_repository_open(&repo, cl_fixture("deprecated-mode.git"))); cl_git_pass(git_oid_fromstr(&tid, tree_oid)); cl_git_pass(git_tree_lookup(&tree, repo, &tid)); @@ -38,7 +48,6 @@ git_tree_entry_filemode(entry)); git_tree_free(tree); - git_repository_free(repo); } void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void) @@ -48,7 +57,7 @@ const git_tree_entry *entry; cl_git_pass(git_oid_fromstr(&bid, blob_oid)); - cl_git_pass(git_treebuilder_create(&builder, NULL)); + cl_git_pass(git_treebuilder_new(&builder, repo, NULL)); cl_git_fail(git_treebuilder_insert( &entry, @@ -62,25 +71,22 @@ void test_object_tree_attributes__normalize_attributes_when_creating_a_tree_from_an_existing_one(void) { - git_repository *repo; git_treebuilder *builder; git_oid tid, tid2; git_tree *tree; const git_tree_entry *entry; - repo = cl_git_sandbox_init("deprecated-mode.git"); - cl_git_pass(git_oid_fromstr(&tid, tree_oid)); cl_git_pass(git_tree_lookup(&tree, repo, &tid)); - cl_git_pass(git_treebuilder_create(&builder, tree)); + cl_git_pass(git_treebuilder_new(&builder, repo, tree)); entry = git_treebuilder_get(builder, "old_mode.txt"); cl_assert_equal_i( GIT_FILEMODE_BLOB, git_tree_entry_filemode(entry)); - cl_git_pass(git_treebuilder_write(&tid2, repo, builder)); + cl_git_pass(git_treebuilder_write(&tid2, builder)); git_treebuilder_free(builder); git_tree_free(tree); @@ -91,18 +97,14 @@ git_tree_entry_filemode(entry)); git_tree_free(tree); - cl_git_sandbox_cleanup(); } void test_object_tree_attributes__normalize_600(void) { git_oid id; git_tree *tree; - git_repository *repo; const git_tree_entry *entry; - repo = cl_git_sandbox_init("deprecated-mode.git"); - git_oid_fromstr(&id, "0810fb7818088ff5ac41ee49199b51473b1bd6c7"); cl_git_pass(git_tree_lookup(&tree, repo, &id)); @@ -111,5 +113,4 @@ cl_assert_equal_i(git_tree_entry_filemode_raw(entry), 0100600); git_tree_free(tree); - cl_git_sandbox_cleanup(); } diff -Nru libgit2-0.20.0/tests/object/tree/duplicateentries.c libgit2-0.22.2/tests/object/tree/duplicateentries.c --- libgit2-0.20.0/tests/object/tree/duplicateentries.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/object/tree/duplicateentries.c 2015-03-24 16:10:45.000000000 +0000 @@ -57,11 +57,11 @@ { git_treebuilder *builder; - cl_git_pass(git_treebuilder_create(&builder, NULL)); + cl_git_pass(git_treebuilder_new(&builder, _repo, NULL)); fn(builder); - cl_git_pass(git_treebuilder_write(out, _repo, builder)); + cl_git_pass(git_treebuilder_write(out, builder)); git_treebuilder_free(builder); } @@ -127,17 +127,17 @@ ancestor_entry.path = "duplicate"; ancestor_entry.mode = GIT_FILEMODE_BLOB; ancestor_entry.flags |= (1 << GIT_IDXENTRY_STAGESHIFT); - git_oid_fromstr(&ancestor_entry.oid, "a8233120f6ad708f843d861ce2b7228ec4e3dec6"); + git_oid_fromstr(&ancestor_entry.id, "a8233120f6ad708f843d861ce2b7228ec4e3dec6"); our_entry.path = "duplicate"; our_entry.mode = GIT_FILEMODE_BLOB; ancestor_entry.flags |= (2 << GIT_IDXENTRY_STAGESHIFT); - git_oid_fromstr(&our_entry.oid, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057"); + git_oid_fromstr(&our_entry.id, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057"); their_entry.path = "duplicate"; their_entry.mode = GIT_FILEMODE_BLOB; ancestor_entry.flags |= (3 << GIT_IDXENTRY_STAGESHIFT); - git_oid_fromstr(&their_entry.oid, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd"); + git_oid_fromstr(&their_entry.id, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd"); cl_git_pass(git_index_conflict_add(index, &ancestor_entry, &our_entry, &their_entry)); } diff -Nru libgit2-0.20.0/tests/object/tree/walk.c libgit2-0.22.2/tests/object/tree/walk.c --- libgit2-0.20.0/tests/object/tree/walk.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/object/tree/walk.c 2015-03-24 16:10:45.000000000 +0000 @@ -59,7 +59,7 @@ (*count) += 1; - return (*count == 2) ? -1 : 0; + return (*count == 2) ? -123 : 0; } static int treewalk_stop_immediately_cb( @@ -83,20 +83,20 @@ ct = 0; cl_assert_equal_i( - GIT_EUSER, git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_stop_cb, &ct)); + -123, git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_stop_cb, &ct)); cl_assert_equal_i(2, ct); ct = 0; cl_assert_equal_i( - GIT_EUSER, git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_stop_cb, &ct)); + -123, git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_stop_cb, &ct)); cl_assert_equal_i(2, ct); cl_assert_equal_i( - GIT_EUSER, git_tree_walk( + -100, git_tree_walk( tree, GIT_TREEWALK_PRE, treewalk_stop_immediately_cb, NULL)); cl_assert_equal_i( - GIT_EUSER, git_tree_walk( + -100, git_tree_walk( tree, GIT_TREEWALK_POST, treewalk_stop_immediately_cb, NULL)); git_tree_free(tree); @@ -152,7 +152,7 @@ memset(&data, 0, sizeof(data)); data.stop = "3.txt"; - cl_assert_equal_i(GIT_EUSER, git_tree_walk( + cl_assert_equal_i(-1, git_tree_walk( tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data)); cl_assert_equal_i(3, data.files); cl_assert_equal_i(2, data.dirs); @@ -168,7 +168,7 @@ memset(&data, 0, sizeof(data)); data.stop = "new.txt"; - cl_assert_equal_i(GIT_EUSER, git_tree_walk( + cl_assert_equal_i(-1, git_tree_walk( tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data)); cl_assert_equal_i(7, data.files); cl_assert_equal_i(4, data.dirs); diff -Nru libgit2-0.20.0/tests/object/tree/write.c libgit2-0.22.2/tests/object/tree/write.c --- libgit2-0.20.0/tests/object/tree/write.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/object/tree/write.c 2015-03-24 16:10:45.000000000 +0000 @@ -9,7 +9,7 @@ static git_repository *g_repo; -// Fixture setup and teardown +/* Fixture setup and teardown */ void test_object_tree_write__initialize(void) { g_repo = cl_git_sandbox_init("testrepo"); @@ -22,7 +22,7 @@ void test_object_tree_write__from_memory(void) { - // write a tree from a memory + /* write a tree from a memory */ git_treebuilder *builder; git_tree *tree; git_oid id, bid, rid, id2; @@ -31,9 +31,11 @@ git_oid_fromstr(&id2, second_tree); git_oid_fromstr(&bid, blob_oid); - //create a second tree from first tree using `git_treebuilder_insert` on REPOSITORY_FOLDER. + /* create a second tree from first tree using `git_treebuilder_insert` + * on REPOSITORY_FOLDER. + */ cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); - cl_git_pass(git_treebuilder_create(&builder, tree)); + cl_git_pass(git_treebuilder_new(&builder, g_repo, tree)); cl_git_fail(git_treebuilder_insert(NULL, builder, "", &bid, GIT_FILEMODE_BLOB)); @@ -51,7 +53,7 @@ cl_git_pass(git_treebuilder_insert( NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB)); - cl_git_pass(git_treebuilder_write(&rid, g_repo, builder)); + cl_git_pass(git_treebuilder_write(&rid, builder)); cl_assert(git_oid_cmp(&rid, &id2) == 0); @@ -61,7 +63,7 @@ void test_object_tree_write__subtree(void) { - // write a hierarchical tree from a memory + /* write a hierarchical tree from a memory */ git_treebuilder *builder; git_tree *tree; git_oid id, bid, subtree_id, id2, id3; @@ -72,25 +74,25 @@ git_oid_fromstr(&id3, third_tree); git_oid_fromstr(&bid, blob_oid); - //create subtree - cl_git_pass(git_treebuilder_create(&builder, NULL)); + /* create subtree */ + cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); cl_git_pass(git_treebuilder_insert( - NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB)); //-V536 - cl_git_pass(git_treebuilder_write(&subtree_id, g_repo, builder)); + NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB)); /* -V536 */ + cl_git_pass(git_treebuilder_write(&subtree_id, builder)); git_treebuilder_free(builder); - // create parent tree + /* create parent tree */ cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); - cl_git_pass(git_treebuilder_create(&builder, tree)); + cl_git_pass(git_treebuilder_new(&builder, g_repo, tree)); cl_git_pass(git_treebuilder_insert( - NULL, builder, "new", &subtree_id, GIT_FILEMODE_TREE)); //-V536 - cl_git_pass(git_treebuilder_write(&id_hiearar, g_repo, builder)); + NULL, builder, "new", &subtree_id, GIT_FILEMODE_TREE)); /* -V536 */ + cl_git_pass(git_treebuilder_write(&id_hiearar, builder)); git_treebuilder_free(builder); git_tree_free(tree); cl_assert(git_oid_cmp(&id_hiearar, &id3) == 0); - // check data is correct + /* check data is correct */ cl_git_pass(git_tree_lookup(&tree, g_repo, &id_hiearar)); cl_assert(2 == git_tree_entrycount(tree)); git_tree_free(tree); @@ -102,6 +104,7 @@ void test_object_tree_write__sorted_subtrees(void) { git_treebuilder *builder; + git_tree *tree; unsigned int i; int position_c = -1, position_cake = -1, position_config = -1; @@ -132,17 +135,18 @@ memset(&blank_oid, 0x0, sizeof(blank_oid)); - cl_git_pass(git_treebuilder_create(&builder, NULL)); + cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); for (i = 0; i < ARRAY_SIZE(entries); ++i) { cl_git_pass(git_treebuilder_insert(NULL, builder, entries[i].filename, &blank_oid, entries[i].attr)); } - cl_git_pass(git_treebuilder_write(&tree_oid, g_repo, builder)); + cl_git_pass(git_treebuilder_write(&tree_oid, builder)); - for (i = 0; i < builder->entries.length; ++i) { - git_tree_entry *entry = git_vector_get(&builder->entries, i); + cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid)); + for (i = 0; i < git_tree_entrycount(tree); i++) { + const git_tree_entry *entry = git_tree_entry_byindex(tree, i); if (strcmp(entry->filename, "c") == 0) position_c = i; @@ -154,6 +158,8 @@ position_config = i; } + git_tree_free(tree); + cl_assert(position_c != -1); cl_assert(position_cake != -1); cl_assert(position_config != -1); @@ -164,34 +170,35 @@ git_treebuilder_free(builder); } +static struct { + unsigned int attr; + const char *filename; +} _entries[] = { + { GIT_FILEMODE_BLOB, "aardvark" }, + { GIT_FILEMODE_BLOB, ".first" }, + { GIT_FILEMODE_BLOB, "apple" }, + { GIT_FILEMODE_BLOB, "last"}, + { GIT_FILEMODE_BLOB, "apple_after"}, + { GIT_FILEMODE_BLOB, "after_aardvark"}, + { 0, NULL }, +}; + void test_object_tree_write__removing_and_re_adding_in_treebuilder(void) { git_treebuilder *builder; - int i, aardvark_i, apple_i, apple_after_i, apple_extra_i, last_i; + int i, aardvark_i, apple_i, apple_after_i, apple_extra_i, last_i; git_oid blank_oid, tree_oid; git_tree *tree; - struct { - unsigned int attr; - const char *filename; - } entries[] = { - { GIT_FILEMODE_BLOB, "aardvark" }, - { GIT_FILEMODE_BLOB, ".first" }, - { GIT_FILEMODE_BLOB, "apple" }, - { GIT_FILEMODE_BLOB, "last"}, - { GIT_FILEMODE_BLOB, "apple_after"}, - { GIT_FILEMODE_BLOB, "after_aardvark"}, - { 0, NULL }, - }; memset(&blank_oid, 0x0, sizeof(blank_oid)); - cl_git_pass(git_treebuilder_create(&builder, NULL)); + cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); cl_assert_equal_i(0, (int)git_treebuilder_entrycount(builder)); - for (i = 0; entries[i].filename; ++i) + for (i = 0; _entries[i].filename; ++i) cl_git_pass(git_treebuilder_insert(NULL, - builder, entries[i].filename, &blank_oid, entries[i].attr)); + builder, _entries[i].filename, &blank_oid, _entries[i].attr)); cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder)); @@ -222,7 +229,7 @@ NULL, builder, "apple_extra", &blank_oid, GIT_FILEMODE_BLOB)); cl_assert_equal_i(7, (int)git_treebuilder_entrycount(builder)); - cl_git_pass(git_treebuilder_write(&tree_oid, g_repo, builder)); + cl_git_pass(git_treebuilder_write(&tree_oid, builder)); git_treebuilder_free(builder); @@ -260,3 +267,176 @@ git_tree_free(tree); } + +static int treebuilder_filter_prefixed( + const git_tree_entry *entry, void *payload) +{ + return !git__prefixcmp(git_tree_entry_name(entry), payload); +} + +void test_object_tree_write__filtering(void) +{ + git_treebuilder *builder; + int i; + git_oid blank_oid, tree_oid; + git_tree *tree; + + memset(&blank_oid, 0x0, sizeof(blank_oid)); + + cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); + + for (i = 0; _entries[i].filename; ++i) + cl_git_pass(git_treebuilder_insert(NULL, + builder, _entries[i].filename, &blank_oid, _entries[i].attr)); + + cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder)); + + cl_assert(git_treebuilder_get(builder, "apple") != NULL); + cl_assert(git_treebuilder_get(builder, "aardvark") != NULL); + cl_assert(git_treebuilder_get(builder, "last") != NULL); + + git_treebuilder_filter(builder, treebuilder_filter_prefixed, "apple"); + + cl_assert_equal_i(4, (int)git_treebuilder_entrycount(builder)); + + cl_assert(git_treebuilder_get(builder, "apple") == NULL); + cl_assert(git_treebuilder_get(builder, "aardvark") != NULL); + cl_assert(git_treebuilder_get(builder, "last") != NULL); + + git_treebuilder_filter(builder, treebuilder_filter_prefixed, "a"); + + cl_assert_equal_i(2, (int)git_treebuilder_entrycount(builder)); + + cl_assert(git_treebuilder_get(builder, "aardvark") == NULL); + cl_assert(git_treebuilder_get(builder, "last") != NULL); + + cl_git_pass(git_treebuilder_write(&tree_oid, builder)); + + git_treebuilder_free(builder); + + cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid)); + + cl_assert_equal_i(2, (int)git_tree_entrycount(tree)); + + git_tree_free(tree); +} + +void test_object_tree_write__cruel_paths(void) +{ + static const char *the_paths[] = { + "C:\\", + " : * ? \" \n < > |", + "a\\b", + "\\\\b\a", + ":\\", + "COM1", + "foo.aux", + REP1024("1234"), /* 4096 char string */ + REP1024("12345678"), /* 8192 char string */ + "\xC5\xAA\x6E\xC4\xAD\x63\xC5\x8D\x64\x65\xCC\xBD", /* Ūnĭcōde̽ */ + NULL + }; + git_treebuilder *builder; + git_tree *tree; + git_oid id, bid, subid; + const char **scan; + int count = 0, i, j; + git_tree_entry *te; + + git_oid_fromstr(&bid, blob_oid); + + /* create tree */ + cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); + for (scan = the_paths; *scan; ++scan) { + cl_git_pass(git_treebuilder_insert( + NULL, builder, *scan, &bid, GIT_FILEMODE_BLOB)); + count++; + } + cl_git_pass(git_treebuilder_write(&id, builder)); + git_treebuilder_free(builder); + + /* check data is correct */ + cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); + + cl_assert_equal_i(count, git_tree_entrycount(tree)); + + for (scan = the_paths; *scan; ++scan) { + const git_tree_entry *cte = git_tree_entry_byname(tree, *scan); + cl_assert(cte != NULL); + cl_assert_equal_s(*scan, git_tree_entry_name(cte)); + } + for (scan = the_paths; *scan; ++scan) { + cl_git_pass(git_tree_entry_bypath(&te, tree, *scan)); + cl_assert_equal_s(*scan, git_tree_entry_name(te)); + git_tree_entry_free(te); + } + + git_tree_free(tree); + + /* let's try longer paths */ + cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); + for (scan = the_paths; *scan; ++scan) { + cl_git_pass(git_treebuilder_insert( + NULL, builder, *scan, &id, GIT_FILEMODE_TREE)); + } + cl_git_pass(git_treebuilder_write(&subid, builder)); + git_treebuilder_free(builder); + + /* check data is correct */ + cl_git_pass(git_tree_lookup(&tree, g_repo, &subid)); + + cl_assert_equal_i(count, git_tree_entrycount(tree)); + + for (i = 0; i < count; ++i) { + for (j = 0; j < count; ++j) { + git_buf b = GIT_BUF_INIT; + cl_git_pass(git_buf_joinpath(&b, the_paths[i], the_paths[j])); + cl_git_pass(git_tree_entry_bypath(&te, tree, b.ptr)); + cl_assert_equal_s(the_paths[j], git_tree_entry_name(te)); + git_tree_entry_free(te); + git_buf_free(&b); + } + } + + git_tree_free(tree); +} + +void test_object_tree_write__protect_filesystems(void) +{ + git_treebuilder *builder; + git_oid bid; + + /* Ensure that (by default) we can write objects with funny names on + * platforms that are not affected. + */ + cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); + +#ifndef GIT_WIN32 + cl_git_pass(git_treebuilder_insert(NULL, builder, ".git.", &bid, GIT_FILEMODE_BLOB)); + cl_git_pass(git_treebuilder_insert(NULL, builder, "git~1", &bid, GIT_FILEMODE_BLOB)); +#endif + +#ifndef __APPLE__ + cl_git_pass(git_treebuilder_insert(NULL, builder, ".git\xef\xbb\xbf", &bid, GIT_FILEMODE_BLOB)); + cl_git_pass(git_treebuilder_insert(NULL, builder, ".git\xe2\x80\xad", &bid, GIT_FILEMODE_BLOB)); +#endif + + git_treebuilder_free(builder); + + /* Now turn on core.protectHFS and core.protectNTFS and validate that these + * paths are rejected. + */ + + cl_repo_set_bool(g_repo, "core.protectHFS", true); + cl_repo_set_bool(g_repo, "core.protectNTFS", true); + + cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL)); + + cl_git_fail(git_treebuilder_insert(NULL, builder, ".git.", &bid, GIT_FILEMODE_BLOB)); + cl_git_fail(git_treebuilder_insert(NULL, builder, "git~1", &bid, GIT_FILEMODE_BLOB)); + + cl_git_fail(git_treebuilder_insert(NULL, builder, ".git\xef\xbb\xbf", &bid, GIT_FILEMODE_BLOB)); + cl_git_fail(git_treebuilder_insert(NULL, builder, ".git\xe2\x80\xad", &bid, GIT_FILEMODE_BLOB)); + + git_treebuilder_free(builder); +} diff -Nru libgit2-0.20.0/tests/odb/backend/nobackend.c libgit2-0.22.2/tests/odb/backend/nobackend.c --- libgit2-0.20.0/tests/odb/backend/nobackend.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/odb/backend/nobackend.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,46 @@ +#include "clar_libgit2.h" +#include "repository.h" +#include "git2/sys/repository.h" + +static git_repository *_repo; + +void test_odb_backend_nobackend__initialize(void) +{ + git_config *config; + git_odb *odb; + git_refdb *refdb; + + cl_git_pass(git_repository_new(&_repo)); + cl_git_pass(git_config_new(&config)); + cl_git_pass(git_odb_new(&odb)); + cl_git_pass(git_refdb_new(&refdb, _repo)); + + git_repository_set_config(_repo, config); + git_repository_set_odb(_repo, odb); + git_repository_set_refdb(_repo, refdb); + + /* The set increases the refcount and we don't want them anymore */ + git_config_free(config); + git_odb_free(odb); + git_refdb_free(refdb); +} + +void test_odb_backend_nobackend__cleanup(void) +{ + git_repository_free(_repo); +} + +void test_odb_backend_nobackend__write_fails_gracefully(void) +{ + git_oid id; + git_odb *odb; + const git_error *err; + + git_repository_odb(&odb, _repo); + cl_git_fail(git_odb_write(&id, odb, "Hello world!\n", 13, GIT_OBJ_BLOB)); + + err = giterr_last(); + cl_assert_equal_s(err->message, "Cannot write object - unsupported in the loaded odb backends"); + + git_odb_free(odb); +} diff -Nru libgit2-0.20.0/tests/odb/emptyobjects.c libgit2-0.22.2/tests/odb/emptyobjects.c --- libgit2-0.20.0/tests/odb/emptyobjects.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/odb/emptyobjects.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,57 @@ +#include "clar_libgit2.h" +#include "odb.h" +#include "filebuf.h" + +git_repository *g_repo; + +void test_odb_emptyobjects__initialize(void) +{ + cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git"))); +} +void test_odb_emptyobjects__cleanup(void) +{ + git_repository_free(g_repo); +} + +void test_odb_emptyobjects__read(void) +{ + git_oid id; + git_blob *blob; + + cl_git_pass(git_oid_fromstr(&id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391")); + cl_git_pass(git_blob_lookup(&blob, g_repo, &id)); + cl_assert_equal_i(GIT_OBJ_BLOB, git_object_type((git_object *) blob)); + cl_assert(git_blob_rawcontent(blob)); + cl_assert_equal_s("", git_blob_rawcontent(blob)); + cl_assert_equal_i(0, git_blob_rawsize(blob)); + git_blob_free(blob); +} + +void test_odb_emptyobjects__read_tree(void) +{ + git_oid id; + git_tree *tree; + + cl_git_pass(git_oid_fromstr(&id, "4b825dc642cb6eb9a060e54bf8d69288fbee4904")); + cl_git_pass(git_tree_lookup(&tree, g_repo, &id)); + cl_assert_equal_i(GIT_OBJ_TREE, git_object_type((git_object *) tree)); + cl_assert_equal_i(0, git_tree_entrycount(tree)); + cl_assert_equal_p(NULL, git_tree_entry_byname(tree, "foo")); + git_tree_free(tree); +} + +void test_odb_emptyobjects__read_tree_odb(void) +{ + git_oid id; + git_odb *odb; + git_odb_object *tree_odb; + + cl_git_pass(git_oid_fromstr(&id, "4b825dc642cb6eb9a060e54bf8d69288fbee4904")); + cl_git_pass(git_repository_odb(&odb, g_repo)); + cl_git_pass(git_odb_read(&tree_odb, odb, &id)); + cl_assert(git_odb_object_data(tree_odb)); + cl_assert_equal_s("", git_odb_object_data(tree_odb)); + cl_assert_equal_i(0, git_odb_object_size(tree_odb)); + git_odb_object_free(tree_odb); + git_odb_free(odb); +} diff -Nru libgit2-0.20.0/tests/odb/foreach.c libgit2-0.22.2/tests/odb/foreach.c --- libgit2-0.20.0/tests/odb/foreach.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/odb/foreach.c 2015-03-24 16:10:45.000000000 +0000 @@ -2,10 +2,10 @@ #include "odb.h" #include "git2/odb_backend.h" #include "pack.h" +#include "buffer.h" static git_odb *_odb; static git_repository *_repo; -static int nobj; void test_odb_foreach__cleanup(void) { @@ -18,10 +18,10 @@ static int foreach_cb(const git_oid *oid, void *data) { - GIT_UNUSED(data); - GIT_UNUSED(oid); + int *nobj = data; + (*nobj)++; - nobj++; + GIT_UNUSED(oid); return 0; } @@ -38,43 +38,69 @@ */ void test_odb_foreach__foreach(void) { + int nobj = 0; + cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git"))); git_repository_odb(&_odb, _repo); - cl_git_pass(git_odb_foreach(_odb, foreach_cb, NULL)); + cl_git_pass(git_odb_foreach(_odb, foreach_cb, &nobj)); cl_assert_equal_i(47 + 1640, nobj); /* count + in-pack */ } void test_odb_foreach__one_pack(void) { git_odb_backend *backend = NULL; + int nobj = 0; cl_git_pass(git_odb_new(&_odb)); cl_git_pass(git_odb_backend_one_pack(&backend, cl_fixture("testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx"))); cl_git_pass(git_odb_add_backend(_odb, backend, 1)); _repo = NULL; - nobj = 0; - cl_git_pass(git_odb_foreach(_odb, foreach_cb, NULL)); + cl_git_pass(git_odb_foreach(_odb, foreach_cb, &nobj)); cl_assert(nobj == 1628); } static int foreach_stop_cb(const git_oid *oid, void *data) { - GIT_UNUSED(data); - GIT_UNUSED(oid); + int *nobj = data; + (*nobj)++; - nobj++; + GIT_UNUSED(oid); - return (nobj == 1000); + return (*nobj == 1000) ? -321 : 0; } void test_odb_foreach__interrupt_foreach(void) { - nobj = 0; + int nobj = 0; + cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git"))); git_repository_odb(&_odb, _repo); - cl_assert_equal_i(GIT_EUSER, git_odb_foreach(_odb, foreach_stop_cb, NULL)); + cl_assert_equal_i(-321, git_odb_foreach(_odb, foreach_stop_cb, &nobj)); cl_assert(nobj == 1000); } + +void test_odb_foreach__files_in_objects_dir(void) +{ + git_repository *repo; + git_odb *odb; + git_buf buf = GIT_BUF_INIT; + int nobj = 0; + + cl_fixture_sandbox("testrepo.git"); + cl_git_pass(git_repository_open(&repo, "testrepo.git")); + + cl_git_pass(git_buf_printf(&buf, "%s/objects/somefile", git_repository_path(repo))); + cl_git_mkfile(buf.ptr, ""); + git_buf_free(&buf); + + cl_git_pass(git_repository_odb(&odb, repo)); + cl_git_pass(git_odb_foreach(odb, foreach_cb, &nobj)); + cl_assert_equal_i(47 + 1640, nobj); /* count + in-pack */ + + git_odb_free(odb); + git_repository_free(repo); + cl_fixture_cleanup("testrepo.git"); +} diff -Nru libgit2-0.20.0/tests/odb/loose.c libgit2-0.22.2/tests/odb/loose.c --- libgit2-0.20.0/tests/odb/loose.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/odb/loose.c 2015-03-24 16:10:45.000000000 +0000 @@ -66,19 +66,25 @@ void test_odb_loose__exists(void) { - git_oid id, id2; + git_oid id, id2; git_odb *odb; - write_object_files(&one); + write_object_files(&one); cl_git_pass(git_odb_open(&odb, "test-objects")); - cl_git_pass(git_oid_fromstr(&id, one.id)); + cl_git_pass(git_oid_fromstr(&id, one.id)); + cl_assert(git_odb_exists(odb, &id)); - cl_assert(git_odb_exists(odb, &id)); + cl_git_pass(git_oid_fromstrp(&id, "8b137891")); + cl_git_pass(git_odb_exists_prefix(&id2, odb, &id, 8)); + cl_assert_equal_i(0, git_oid_streq(&id2, one.id)); + + /* Test for a missing object */ + cl_git_pass(git_oid_fromstr(&id, "8b137891791fe96927ad78e64b0aad7bded08baa")); + cl_assert(!git_odb_exists(odb, &id)); - /* Test for a non-existant object */ - cl_git_pass(git_oid_fromstr(&id2, "8b137891791fe96927ad78e64b0aad7bded08baa")); - cl_assert(!git_odb_exists(odb, &id2)); + cl_git_pass(git_oid_fromstrp(&id, "8b13789a")); + cl_assert_equal_i(GIT_ENOTFOUND, git_odb_exists_prefix(&id2, odb, &id, 8)); git_odb_free(odb); } diff -Nru libgit2-0.20.0/tests/odb/mixed.c libgit2-0.22.2/tests/odb/mixed.c --- libgit2-0.20.0/tests/odb/mixed.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/odb/mixed.c 2015-03-24 16:10:45.000000000 +0000 @@ -23,9 +23,14 @@ cl_git_pass(git_oid_fromstr(&oid, hex)); cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, GIT_OID_HEXSZ)); git_odb_object_free(obj); + + cl_git_pass(git_odb_exists_prefix(NULL, _odb, &oid, GIT_OID_HEXSZ)); + cl_git_pass(git_oid_fromstrn(&oid, short_hex, sizeof(short_hex) - 1)); cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, sizeof(short_hex) - 1)); git_odb_object_free(obj); + + cl_git_pass(git_odb_exists_prefix(NULL, _odb, &oid, sizeof(short_hex) - 1)); } /* some known sha collisions of file content: @@ -37,7 +42,7 @@ void test_odb_mixed__dup_oid_prefix_0(void) { char hex[10]; - git_oid oid; + git_oid oid, found; git_odb_object *obj; /* ambiguous in the same pack file */ @@ -46,10 +51,14 @@ cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); cl_assert_equal_i( GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); + cl_assert_equal_i( + GIT_EAMBIGUOUS, git_odb_exists_prefix(&found, _odb, &oid, strlen(hex))); strncpy(hex, "dea509d09", sizeof(hex)); cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); + cl_git_pass(git_odb_exists_prefix(&found, _odb, &oid, strlen(hex))); + cl_assert_equal_oid(&found, git_odb_object_id(obj)); git_odb_object_free(obj); strncpy(hex, "dea509d0b", sizeof(hex)); @@ -63,10 +72,14 @@ cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); cl_assert_equal_i( GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); + cl_assert_equal_i( + GIT_EAMBIGUOUS, git_odb_exists_prefix(&found, _odb, &oid, strlen(hex))); strncpy(hex, "81b5bff5b", sizeof(hex)); cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); + cl_git_pass(git_odb_exists_prefix(&found, _odb, &oid, strlen(hex))); + cl_assert_equal_oid(&found, git_odb_object_id(obj)); git_odb_object_free(obj); strncpy(hex, "81b5bff5f", sizeof(hex)); @@ -80,10 +93,14 @@ cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); cl_assert_equal_i( GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); + cl_assert_equal_i( + GIT_EAMBIGUOUS, git_odb_exists_prefix(&found, _odb, &oid, strlen(hex))); strncpy(hex, "0ddeaded9", sizeof(hex)); cl_git_pass(git_oid_fromstrn(&oid, hex, strlen(hex))); cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex))); + cl_git_pass(git_odb_exists_prefix(&found, _odb, &oid, strlen(hex))); + cl_assert_equal_oid(&found, git_odb_object_id(obj)); git_odb_object_free(obj); strncpy(hex, "0ddeadede", sizeof(hex)); diff -Nru libgit2-0.20.0/tests/online/clone.c libgit2-0.22.2/tests/online/clone.c --- libgit2-0.20.0/tests/online/clone.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/online/clone.c 2015-03-24 16:10:45.000000000 +0000 @@ -8,17 +8,18 @@ #define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository" #define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository" -#define BB_REPO_URL "https://libgit2@bitbucket.org/libgit2/testgitrepository.git" -#define BB_REPO_URL_WITH_PASS "https://libgit2:libgit2@bitbucket.org/libgit2/testgitrepository.git" -#define BB_REPO_URL_WITH_WRONG_PASS "https://libgit2:wrong@bitbucket.org/libgit2/testgitrepository.git" -#define ASSEMBLA_REPO_URL "https://libgit2:_Libgit2@git.assembla.com/libgit2-test-repos.git" +#define BB_REPO_URL "https://libgit3@bitbucket.org/libgit2/testgitrepository.git" +#define BB_REPO_URL_WITH_PASS "https://libgit3:libgit3@bitbucket.org/libgit2/testgitrepository.git" +#define BB_REPO_URL_WITH_WRONG_PASS "https://libgit3:wrong@bitbucket.org/libgit2/testgitrepository.git" + +#define SSH_REPO_URL "ssh://github.com/libgit2/TestGitRepository" static git_repository *g_repo; static git_clone_options g_options; void test_online_clone__initialize(void) { - git_checkout_opts dummy_opts = GIT_CHECKOUT_OPTS_INIT; + git_checkout_options dummy_opts = GIT_CHECKOUT_OPTIONS_INIT; git_remote_callbacks dummy_callbacks = GIT_REMOTE_CALLBACKS_INIT; g_repo = NULL; @@ -45,7 +46,7 @@ cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options)); cl_assert(!git_repository_is_bare(g_repo)); - cl_git_pass(git_remote_load(&origin, g_repo, "origin")); + cl_git_pass(git_remote_lookup(&origin, g_repo, "origin")); cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_AUTO, origin->download_tags); @@ -60,7 +61,7 @@ cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options)); cl_assert(git_repository_is_bare(g_repo)); - cl_git_pass(git_remote_load(&origin, g_repo, "origin")); + cl_git_pass(git_remote_lookup(&origin, g_repo, "origin")); git_remote_free(origin); } @@ -125,43 +126,61 @@ git_buf_free(&path); } -void test_online_clone__clone_into(void) +static int remote_mirror_cb(git_remote **out, git_repository *repo, + const char *name, const char *url, void *payload) { - git_buf path = GIT_BUF_INIT; + int error; git_remote *remote; - git_reference *head; - git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT; - git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; + git_remote_callbacks *callbacks = (git_remote_callbacks *) payload; - bool checkout_progress_cb_was_called = false, - fetch_progress_cb_was_called = false; - checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE; - checkout_opts.progress_cb = &checkout_progress; - checkout_opts.progress_payload = &checkout_progress_cb_was_called; + if ((error = git_remote_create(&remote, repo, name, url)) < 0) + return error; + + if ((error = git_remote_set_callbacks(remote, callbacks)) < 0) { + git_remote_free(remote); + return error; + } + + git_remote_clear_refspecs(remote); + + if ((error = git_remote_add_fetch(remote, "+refs/*:refs/*")) < 0) { + git_remote_free(remote); + return error; + } + + *out = remote; + return 0; +} + +void test_online_clone__clone_mirror(void) +{ + git_clone_options opts = GIT_CLONE_OPTIONS_INIT; + git_reference *head; + git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; - cl_git_pass(git_repository_init(&g_repo, "./foo", false)); - cl_git_pass(git_remote_create(&remote, g_repo, "origin", LIVE_REPO_URL)); + bool fetch_progress_cb_was_called = false; callbacks.transfer_progress = &fetch_progress; callbacks.payload = &fetch_progress_cb_was_called; - git_remote_set_callbacks(remote, &callbacks); - cl_git_pass(git_clone_into(g_repo, remote, &checkout_opts, NULL)); + opts.bare = true; + opts.remote_cb = remote_mirror_cb; + opts.remote_cb_payload = &callbacks; - cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "master.txt")); - cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&path))); + cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo.git", &opts)); cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD")); cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head)); cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head)); - cl_assert_equal_i(true, checkout_progress_cb_was_called); cl_assert_equal_i(true, fetch_progress_cb_was_called); - git_remote_free(remote); git_reference_free(head); - git_buf_free(&path); + git_repository_free(g_repo); + g_repo = NULL; + + cl_fixture_cleanup("./foo.git"); } static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *payload) @@ -192,35 +211,78 @@ { GIT_UNUSED(cred); GIT_UNUSED(url); GIT_UNUSED(username_from_url); GIT_UNUSED(allowed_types); GIT_UNUSED(data); - return -1; + return -172; } -void test_online_clone__cred_callback_failure_is_euser(void) +void test_online_clone__cred_callback_failure_return_code_is_tunnelled(void) { const char *remote_url = cl_getenv("GITTEST_REMOTE_URL"); const char *remote_user = cl_getenv("GITTEST_REMOTE_USER"); - const char *remote_default = cl_getenv("GITTEST_REMOTE_DEFAULT"); - int error; - if (!remote_url) { - printf("GITTEST_REMOTE_URL unset; skipping clone test\n"); - return; - } - - if (!remote_user && !remote_default) { - printf("GITTEST_REMOTE_USER and GITTEST_REMOTE_DEFAULT unset; skipping clone test\n"); - return; - } + if (!remote_url || !remote_user) + clar__skip(); g_options.remote_callbacks.credentials = cred_failure_cb; - cl_git_fail(error = git_clone(&g_repo, remote_url, "./foo", &g_options)); - cl_assert_equal_i(error, GIT_EUSER); + cl_git_fail_with(-172, git_clone(&g_repo, remote_url, "./foo", &g_options)); +} + +static int cred_count_calls_cb(git_cred **cred, const char *url, const char *user, + unsigned int allowed_types, void *data) +{ + size_t *counter = (size_t *) data; + + GIT_UNUSED(url); GIT_UNUSED(user); GIT_UNUSED(allowed_types); + + if (allowed_types == GIT_CREDTYPE_USERNAME) + return git_cred_username_new(cred, "foo"); + + (*counter)++; + + if (*counter == 3) + return GIT_EUSER; + + return git_cred_userpass_plaintext_new(cred, "foo", "bar"); +} + +void test_online_clone__cred_callback_called_again_on_auth_failure(void) +{ + const char *remote_url = cl_getenv("GITTEST_REMOTE_URL"); + const char *remote_user = cl_getenv("GITTEST_REMOTE_USER"); + size_t counter = 0; + + if (!remote_url || !remote_user) + clar__skip(); + + g_options.remote_callbacks.credentials = cred_count_calls_cb; + g_options.remote_callbacks.payload = &counter; + + cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, remote_url, "./foo", &g_options)); + cl_assert_equal_i(3, counter); +} + +int cred_default( + git_cred **cred, + const char *url, + const char *user_from_url, + unsigned int allowed_types, + void *payload) +{ + GIT_UNUSED(url); + GIT_UNUSED(user_from_url); + GIT_UNUSED(payload); + + if (!(allowed_types & GIT_CREDTYPE_DEFAULT)) + return 0; + + return git_cred_default_new(cred); } void test_online_clone__credentials(void) { - /* Remote URL environment variable must be set. User and password are optional. */ + /* Remote URL environment variable must be set. + * User and password are optional. + */ const char *remote_url = cl_getenv("GITTEST_REMOTE_URL"); git_cred_userpass_payload user_pass = { cl_getenv("GITTEST_REMOTE_USER"), @@ -229,8 +291,12 @@ if (!remote_url) return; - g_options.remote_callbacks.credentials = git_cred_userpass; - g_options.remote_callbacks.payload = &user_pass; + if (cl_getenv("GITTEST_REMOTE_DEFAULT")) { + g_options.remote_callbacks.credentials = cred_default; + } else { + g_options.remote_callbacks.credentials = git_cred_userpass; + g_options.remote_callbacks.payload = &user_pass; + } cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options)); git_repository_free(g_repo); g_repo = NULL; @@ -263,17 +329,12 @@ cl_fixture_cleanup("./foo"); } -void test_online_clone__assembla_style(void) -{ - cl_git_pass(git_clone(&g_repo, ASSEMBLA_REPO_URL, "./foo", NULL)); -} - static int cancel_at_half(const git_transfer_progress *stats, void *payload) { GIT_UNUSED(payload); if (stats->received_objects > (stats->total_objects/2)) - return 1; + return 4321; return 0; } @@ -281,11 +342,226 @@ { g_options.remote_callbacks.transfer_progress = cancel_at_half; - cl_git_fail_with(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options), GIT_EUSER); + cl_git_fail_with( + git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options), 4321); +} + +static int cred_cb(git_cred **cred, const char *url, const char *user_from_url, + unsigned int allowed_types, void *payload) +{ + const char *remote_user = cl_getenv("GITTEST_REMOTE_USER"); + const char *pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY"); + const char *privkey = cl_getenv("GITTEST_REMOTE_SSH_KEY"); + const char *passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE"); + + GIT_UNUSED(url); GIT_UNUSED(user_from_url); GIT_UNUSED(payload); + + if (allowed_types & GIT_CREDTYPE_USERNAME) + return git_cred_username_new(cred, remote_user); + + if (allowed_types & GIT_CREDTYPE_SSH_KEY) + return git_cred_ssh_key_new(cred, remote_user, pubkey, privkey, passphrase); + + giterr_set(GITERR_NET, "unexpected cred type"); + return -1; } +static int check_ssh_auth_methods(git_cred **cred, const char *url, const char *username_from_url, + unsigned int allowed_types, void *data) +{ + int *with_user = (int *) data; + GIT_UNUSED(cred); GIT_UNUSED(url); GIT_UNUSED(username_from_url); GIT_UNUSED(data); + if (!*with_user) + cl_assert_equal_i(GIT_CREDTYPE_USERNAME, allowed_types); + else + cl_assert(!(allowed_types & GIT_CREDTYPE_USERNAME)); + return GIT_EUSER; +} + +void test_online_clone__ssh_auth_methods(void) +{ + int with_user; +#ifndef GIT_SSH + clar__skip(); +#endif + g_options.remote_callbacks.credentials = check_ssh_auth_methods; + g_options.remote_callbacks.payload = &with_user; + with_user = 0; + cl_git_fail_with(GIT_EUSER, + git_clone(&g_repo, SSH_REPO_URL, "./foo", &g_options)); + with_user = 1; + cl_git_fail_with(GIT_EUSER, + git_clone(&g_repo, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options)); +} + +static int custom_remote_ssh_with_paths( + git_remote **out, + git_repository *repo, + const char *name, + const char *url, + void *payload) +{ + int error; + git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; + + if ((error = git_remote_create(out, repo, name, url)) < 0) + return error; + + if ((error = git_remote_set_transport(*out, git_transport_ssh_with_paths, payload)) < 0) + return error; + + callbacks.credentials = cred_cb; + git_remote_set_callbacks(*out, &callbacks); + + return 0; +} + +void test_online_clone__ssh_with_paths(void) +{ + char *bad_paths[] = { + "/bin/yes", + "/bin/false", + }; + char *good_paths[] = { + "/usr/bin/git-upload-pack", + "/usr/bin/git-receive-pack", + }; + git_strarray arr = { + bad_paths, + 2, + }; + + const char *remote_url = cl_getenv("GITTEST_REMOTE_URL"); + const char *remote_user = cl_getenv("GITTEST_REMOTE_USER"); + +#ifndef GIT_SSH + clar__skip(); +#endif + if (!remote_url || !remote_user || strncmp(remote_url, "ssh://", 5) != 0) + clar__skip(); + + g_options.remote_cb = custom_remote_ssh_with_paths; + g_options.remote_cb_payload = &arr; + + cl_git_fail(git_clone(&g_repo, remote_url, "./foo", &g_options)); + + arr.strings = good_paths; + cl_git_pass(git_clone(&g_repo, remote_url, "./foo", &g_options)); +} + +static int cred_foo_bar(git_cred **cred, const char *url, const char *username_from_url, + unsigned int allowed_types, void *data) + +{ + GIT_UNUSED(url); GIT_UNUSED(username_from_url); GIT_UNUSED(allowed_types); GIT_UNUSED(data); + + return git_cred_userpass_plaintext_new(cred, "foo", "bar"); +} + +void test_online_clone__ssh_cannot_change_username(void) +{ +#ifndef GIT_SSH + clar__skip(); +#endif + g_options.remote_callbacks.credentials = cred_foo_bar; + + cl_git_fail(git_clone(&g_repo, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options)); +} + +int ssh_certificate_check(git_cert *cert, int valid, const char *host, void *payload) +{ + git_cert_hostkey *key; + git_oid expected = {{0}}, actual = {{0}}; + const char *expected_str; + + GIT_UNUSED(valid); + GIT_UNUSED(payload); + + expected_str = cl_getenv("GITTEST_REMOTE_SSH_FINGERPRINT"); + cl_assert(expected_str); + + cl_git_pass(git_oid_fromstrp(&expected, expected_str)); + cl_assert_equal_i(GIT_CERT_HOSTKEY_LIBSSH2, cert->cert_type); + key = (git_cert_hostkey *) cert; + + /* + * We need to figure out how long our input was to check for + * the type. Here we abuse the fact that both hashes fit into + * our git_oid type. + */ + if (strlen(expected_str) == 32 && key->type & GIT_CERT_SSH_MD5) { + memcpy(&actual.id, key->hash_md5, 16); + } else if (strlen(expected_str) == 40 && key->type & GIT_CERT_SSH_SHA1) { + memcpy(&actual, key->hash_sha1, 20); + } else { + cl_fail("Cannot find a usable SSH hash"); + } + + cl_assert(!memcmp(&expected, &actual, 20)); + + cl_assert_equal_s("localhost", host); + + return GIT_EUSER; +} + +void test_online_clone__ssh_cert(void) +{ + g_options.remote_callbacks.certificate_check = ssh_certificate_check; + + if (!cl_getenv("GITTEST_REMOTE_SSH_FINGERPRINT")) + cl_skip(); + + cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, "ssh://localhost/foo", "./foo", &g_options)); +} + +void test_online_clone__url_with_no_path_returns_EINVALIDSPEC(void) +{ + cl_git_fail_with(git_clone(&g_repo, "http://github.com", "./foo", &g_options), + GIT_EINVALIDSPEC); +} + +static int fail_certificate_check(git_cert *cert, int valid, const char *host, void *payload) +{ + GIT_UNUSED(cert); + GIT_UNUSED(valid); + GIT_UNUSED(host); + GIT_UNUSED(payload); + + return GIT_ECERTIFICATE; +} + +void test_online_clone__certificate_invalid(void) +{ + g_options.remote_callbacks.certificate_check = fail_certificate_check; + + cl_git_fail_with(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options), + GIT_ECERTIFICATE); + +#ifdef GIT_SSH + cl_git_fail_with(git_clone(&g_repo, "ssh://github.com/libgit2/TestGitRepository", "./foo", &g_options), + GIT_ECERTIFICATE); +#endif +} + +static int succeed_certificate_check(git_cert *cert, int valid, const char *host, void *payload) +{ + GIT_UNUSED(cert); + GIT_UNUSED(valid); + GIT_UNUSED(payload); + + cl_assert_equal_s("github.com", host); + + return 0; +} + +void test_online_clone__certificate_valid(void) +{ + g_options.remote_callbacks.certificate_check = succeed_certificate_check; + + cl_git_pass(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options)); +} diff -Nru libgit2-0.20.0/tests/online/fetch.c libgit2-0.22.2/tests/online/fetch.c --- libgit2-0.20.0/tests/online/fetch.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/online/fetch.c 2015-03-24 16:10:45.000000000 +0000 @@ -47,8 +47,8 @@ git_remote_set_callbacks(remote, &callbacks); git_remote_set_autotag(remote, flag); cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); - cl_git_pass(git_remote_download(remote)); - cl_git_pass(git_remote_update_tips(remote)); + cl_git_pass(git_remote_download(remote, NULL)); + cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); git_remote_disconnect(remote); cl_assert_equal_i(counter, n); cl_assert(bytes_received > 0); @@ -81,6 +81,21 @@ do_fetch("http://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_NONE, 3); } +void test_online_fetch__fetch_twice(void) +{ + git_remote *remote; + cl_git_pass(git_remote_create(&remote, _repo, "test", "git://github.com/libgit2/TestGitRepository.git")); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); + cl_git_pass(git_remote_download(remote, NULL)); + git_remote_disconnect(remote); + + git_remote_connect(remote, GIT_DIRECTION_FETCH); + cl_git_pass(git_remote_download(remote, NULL)); + git_remote_disconnect(remote); + + git_remote_free(remote); +} + static int transferProgressCallback(const git_transfer_progress *stats, void *payload) { bool *invoked = (bool *)payload; @@ -105,7 +120,7 @@ cl_git_pass(git_repository_open(&_repository, "./fetch/lg2")); - cl_git_pass(git_remote_load(&remote, _repository, "origin")); + cl_git_pass(git_remote_lookup(&remote, _repository, "origin")); cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); cl_assert_equal_i(false, invoked); @@ -113,11 +128,11 @@ callbacks.transfer_progress = &transferProgressCallback; callbacks.payload = &invoked; git_remote_set_callbacks(remote, &callbacks); - cl_git_pass(git_remote_download(remote)); + cl_git_pass(git_remote_download(remote, NULL)); cl_assert_equal_i(false, invoked); - cl_git_pass(git_remote_update_tips(remote)); + cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); git_remote_disconnect(remote); git_remote_free(remote); @@ -129,7 +144,7 @@ GIT_UNUSED(payload); if (stats->received_objects > (stats->total_objects/2)) - return -1; + return -4321; return 0; } @@ -147,7 +162,7 @@ git_remote_set_callbacks(remote, &callbacks); cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); - cl_git_fail_with(git_remote_download(remote), GIT_EUSER); + cl_git_fail_with(git_remote_download(remote, NULL), -4321); git_remote_disconnect(remote); git_remote_free(remote); } @@ -169,3 +184,32 @@ git_remote_free(remote); } + +void test_online_fetch__remote_symrefs(void) +{ + const git_remote_head **refs; + size_t refs_len; + git_remote *remote; + + cl_git_pass(git_remote_create(&remote, _repo, "test", + "http://github.com/libgit2/TestGitRepository.git")); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); + git_remote_disconnect(remote); + cl_git_pass(git_remote_ls(&refs, &refs_len, remote)); + + cl_assert_equal_s("HEAD", refs[0]->name); + cl_assert_equal_s("refs/heads/master", refs[0]->symref_target); + + git_remote_free(remote); +} + +void test_online_fetch__twice(void) +{ + git_remote *remote; + + cl_git_pass(git_remote_create(&remote, _repo, "test", "http://github.com/libgit2/TestGitRepository.git")); + cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL)); + cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL)); + + git_remote_free(remote); +} diff -Nru libgit2-0.20.0/tests/online/fetchhead.c libgit2-0.22.2/tests/online/fetchhead.c --- libgit2-0.20.0/tests/online/fetchhead.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/online/fetchhead.c 2015-03-24 16:10:45.000000000 +0000 @@ -40,18 +40,20 @@ git_remote *remote; git_buf fetchhead_buf = GIT_BUF_INIT; int equals = 0; + git_strarray array, *active_refs = NULL; - cl_git_pass(git_remote_load(&remote, g_repo, "origin")); + cl_git_pass(git_remote_lookup(&remote, g_repo, "origin")); git_remote_set_autotag(remote, GIT_REMOTE_DOWNLOAD_TAGS_AUTO); if(fetchspec != NULL) { - git_remote_clear_refspecs(remote); - git_remote_add_fetch(remote, fetchspec); + array.count = 1; + array.strings = (char **) &fetchspec; + active_refs = &array; } cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); - cl_git_pass(git_remote_download(remote)); - cl_git_pass(git_remote_update_tips(remote)); + cl_git_pass(git_remote_download(remote, active_refs)); + cl_git_pass(git_remote_update_tips(remote, NULL, NULL)); git_remote_disconnect(remote); git_remote_free(remote); @@ -67,6 +69,11 @@ void test_online_fetchhead__wildcard_spec(void) { fetchhead_test_clone(); + fetchhead_test_fetch(NULL, FETCH_HEAD_WILDCARD_DATA2); + cl_git_pass(git_tag_delete(g_repo, "annotated_tag")); + cl_git_pass(git_tag_delete(g_repo, "blob")); + cl_git_pass(git_tag_delete(g_repo, "commit_tree")); + cl_git_pass(git_tag_delete(g_repo, "nearly-dangling")); fetchhead_test_fetch(NULL, FETCH_HEAD_WILDCARD_DATA); } @@ -87,5 +94,12 @@ cl_git_pass(git_config_delete_entry(config, "branch.master.merge")); git_config_free(config); + fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA2); + cl_git_pass(git_tag_delete(g_repo, "annotated_tag")); + cl_git_pass(git_tag_delete(g_repo, "blob")); + cl_git_pass(git_tag_delete(g_repo, "commit_tree")); + cl_git_pass(git_tag_delete(g_repo, "nearly-dangling")); fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA); + cl_git_pass(git_tag_delete(g_repo, "commit_tree")); + fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA3); } diff -Nru libgit2-0.20.0/tests/online/push.c libgit2-0.22.2/tests/online/push.c --- libgit2-0.20.0/tests/online/push.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/online/push.c 2015-03-24 16:10:45.000000000 +0000 @@ -50,6 +50,15 @@ GIT_UNUSED(user_from_url); GIT_UNUSED(payload); + if (GIT_CREDTYPE_USERNAME & allowed_types) { + if (!_remote_user) { + printf("GITTEST_REMOTE_USER must be set\n"); + return -1; + } + + return git_cred_username_new(cred, _remote_user); + } + if (GIT_CREDTYPE_DEFAULT & allowed_types) { if (!_remote_default) { printf("GITTEST_REMOTE_DEFAULT must be set to use NTLM/Negotiate credentials\n"); @@ -80,46 +89,38 @@ return -1; } -/* the results of a push status. when used for expected values, msg may be NULL - * to indicate that it should not be matched. */ -typedef struct { - const char *ref; - int success; - const char *msg; -} push_status; - /** * git_push_status_foreach callback that records status entries. * @param data (git_vector *) of push_status instances */ -static int record_push_status_cb(const char *ref, const char *msg, void *data) +static int record_push_status_cb(const char *ref, const char *msg, void *payload) { - git_vector *statuses = (git_vector *)data; + record_callbacks_data *data = (record_callbacks_data *) payload; push_status *s; - cl_assert(s = git__malloc(sizeof(*s))); - s->ref = ref; + cl_assert(s = git__calloc(1, sizeof(*s))); + if (ref) + cl_assert(s->ref = git__strdup(ref)); s->success = (msg == NULL); - s->msg = msg; + if (msg) + cl_assert(s->msg = git__strdup(msg)); - git_vector_insert(statuses, s); + git_vector_insert(&data->statuses, s); return 0; } -static void do_verify_push_status(git_push *push, const push_status expected[], const size_t expected_len) +static void do_verify_push_status(record_callbacks_data *data, const push_status expected[], const size_t expected_len) { - git_vector actual = GIT_VECTOR_INIT; + git_vector *actual = &data->statuses; push_status *iter; bool failed = false; size_t i; - git_push_status_foreach(push, record_push_status_cb, &actual); - - if (expected_len != actual.length) + if (expected_len != actual->length) failed = true; else - git_vector_foreach(&actual, i, iter) + git_vector_foreach(actual, i, iter) if (strcmp(expected[i].ref, iter->ref) || (expected[i].success != iter->success) || (expected[i].msg && (!iter->msg || strcmp(expected[i].msg, iter->msg)))) { @@ -140,7 +141,7 @@ git_buf_puts(&msg, "\nACTUAL:\n"); - git_vector_foreach(&actual, i, iter) { + git_vector_foreach(actual, i, iter) { if (iter->success) git_buf_printf(&msg, "%s: success\n", iter->ref); else @@ -152,10 +153,10 @@ git_buf_free(&msg); } - git_vector_foreach(&actual, i, iter) + git_vector_foreach(actual, i, iter) git__free(iter); - git_vector_free(&actual); + git_vector_free(actual); } /** @@ -197,28 +198,31 @@ git_branch_t branch_type; git_reference *ref; - /* Get current remote branches */ + /* Get current remote-tracking branches */ cl_git_pass(git_branch_iterator_new(&iter, remote->repo, GIT_BRANCH_REMOTE)); while ((error = git_branch_next(&ref, &branch_type, iter)) == 0) { cl_assert_equal_i(branch_type, GIT_BRANCH_REMOTE); cl_git_pass(git_vector_insert(&actual_refs, git__strdup(git_reference_name(ref)))); + + git_reference_free(ref); } cl_assert_equal_i(error, GIT_ITEROVER); + git_branch_iterator_free(iter); /* Loop through expected refs, make sure they exist */ for (i = 0; i < expected_refs_len; i++) { - /* Convert remote reference name into tracking branch name. + /* Convert remote reference name into remote-tracking branch name. * If the spec is not under refs/heads/, then skip. */ fetch_spec = git_remote__matching_refspec(remote, expected_refs[i].name); if (!fetch_spec) continue; - cl_git_pass(git_refspec_transform_r(&ref_name, fetch_spec, expected_refs[i].name)); + cl_git_pass(git_refspec_transform(&ref_name, fetch_spec, expected_refs[i].name)); /* Find matching remote branch */ git_vector_foreach(&actual_refs, j, actual_ref) { @@ -253,8 +257,7 @@ } failed: - - if(failed) + if (failed) cl_fail(git_buf_cstr(&msg)); git_vector_foreach(&actual_refs, i, actual_ref) @@ -263,15 +266,59 @@ git_vector_free(&actual_refs); git_buf_free(&msg); git_buf_free(&ref_name); - return; +} + +static void verify_update_tips_callback(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len) +{ + git_refspec *fetch_spec; + git_buf msg = GIT_BUF_INIT; + git_buf ref_name = GIT_BUF_INIT; + updated_tip *tip = NULL; + size_t i, j; + int failed = 0; + + for (i = 0; i < expected_refs_len; ++i) { + /* Convert remote reference name into tracking branch name. + * If the spec is not under refs/heads/, then skip. + */ + fetch_spec = git_remote__matching_refspec(remote, expected_refs[i].name); + if (!fetch_spec) + continue; + + cl_git_pass(git_refspec_transform(&ref_name, fetch_spec, expected_refs[i].name)); + + /* Find matching update_tip entry */ + git_vector_foreach(&_record_cbs_data.updated_tips, j, tip) { + if (!strcmp(git_buf_cstr(&ref_name), tip->name)) + break; + } + + if (j == _record_cbs_data.updated_tips.length) { + git_buf_printf(&msg, "Did not find expected updated tip entry for branch '%s'.", git_buf_cstr(&ref_name)); + failed = 1; + goto failed; + } + + if (git_oid_cmp(expected_refs[i].oid, tip->new_oid) != 0) { + git_buf_printf(&msg, "Updated tip ID does not match expected ID"); + failed = 1; + goto failed; + } + } + +failed: + if (failed) + cl_fail(git_buf_cstr(&msg)); + + git_buf_free(&ref_name); + git_buf_free(&msg); } void test_online_push__initialize(void) { git_vector delete_specs = GIT_VECTOR_INIT; const git_remote_head **heads; - size_t i, heads_len; - char *curr_del_spec; + size_t heads_len; _repo = cl_git_sandbox_init("push_src"); @@ -314,46 +361,42 @@ _remote_default = cl_getenv("GITTEST_REMOTE_DEFAULT"); _remote = NULL; - if (_remote_url) { - cl_git_pass(git_remote_create(&_remote, _repo, "test", _remote_url)); + /* Skip the test if we're missing the remote URL */ + if (!_remote_url) + cl_skip(); + + cl_git_pass(git_remote_create(&_remote, _repo, "test", _remote_url)); - record_callbacks_data_clear(&_record_cbs_data); - git_remote_set_callbacks(_remote, &_record_cbs); + record_callbacks_data_clear(&_record_cbs_data); + git_remote_set_callbacks(_remote, &_record_cbs); - cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH)); + cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH)); - /* Clean up previously pushed branches. Fails if receive.denyDeletes is - * set on the remote. Also, on Git 1.7.0 and newer, you must run - * 'git config receive.denyDeleteCurrent ignore' in the remote repo in - * order to delete the remote branch pointed to by HEAD (usually master). - * See: https://raw.github.com/git/git/master/Documentation/RelNotes/1.7.0.txt - */ - cl_git_pass(git_remote_ls(&heads, &heads_len, _remote)); - cl_git_pass(create_deletion_refspecs(&delete_specs, heads, heads_len)); - if (delete_specs.length) { - git_push *push; - - cl_git_pass(git_push_new(&push, _remote)); - - git_vector_foreach(&delete_specs, i, curr_del_spec) { - git_push_add_refspec(push, curr_del_spec); - git__free(curr_del_spec); - } + /* Clean up previously pushed branches. Fails if receive.denyDeletes is + * set on the remote. Also, on Git 1.7.0 and newer, you must run + * 'git config receive.denyDeleteCurrent ignore' in the remote repo in + * order to delete the remote branch pointed to by HEAD (usually master). + * See: https://raw.github.com/git/git/master/Documentation/RelNotes/1.7.0.txt + */ + cl_git_pass(git_remote_ls(&heads, &heads_len, _remote)); + cl_git_pass(create_deletion_refspecs(&delete_specs, heads, heads_len)); + if (delete_specs.length) { + git_strarray arr = { + (char **) delete_specs.contents, + delete_specs.length, + }; - cl_git_pass(git_push_finish(push)); - git_push_free(push); - } + cl_git_pass(git_remote_upload(_remote, &arr, NULL)); + } - git_remote_disconnect(_remote); - git_vector_free(&delete_specs); + git_remote_disconnect(_remote); + git_vector_free(&delete_specs); - /* Now that we've deleted everything, fetch from the remote */ - cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_FETCH)); - cl_git_pass(git_remote_download(_remote)); - cl_git_pass(git_remote_update_tips(_remote)); - git_remote_disconnect(_remote); - } else - printf("GITTEST_REMOTE_URL unset; skipping push test\n"); + /* Now that we've deleted everything, fetch from the remote */ + cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_FETCH)); + cl_git_pass(git_remote_download(_remote, NULL)); + cl_git_pass(git_remote_update_tips(_remote, NULL, NULL)); + git_remote_disconnect(_remote); } void test_online_push__cleanup(void) @@ -371,19 +414,27 @@ cl_git_sandbox_cleanup(); } -static int push_pack_progress_cb(int stage, unsigned int current, unsigned int total, void* payload) +static int push_pack_progress_cb( + int stage, unsigned int current, unsigned int total, void* payload) { - int *was_called = (int *) payload; + record_callbacks_data *data = (record_callbacks_data *) payload; GIT_UNUSED(stage); GIT_UNUSED(current); GIT_UNUSED(total); - *was_called = 1; + if (data->pack_progress_calls < 0) + return data->pack_progress_calls; + + data->pack_progress_calls++; return 0; } -static int push_transfer_progress_cb(unsigned int current, unsigned int total, size_t bytes, void* payload) +static int push_transfer_progress_cb( + unsigned int current, unsigned int total, size_t bytes, void* payload) { - int *was_called = (int *) payload; + record_callbacks_data *data = (record_callbacks_data *) payload; GIT_UNUSED(current); GIT_UNUSED(total); GIT_UNUSED(bytes); - *was_called = 1; + if (data->transfer_progress_calls < 0) + return data->transfer_progress_calls; + + data->transfer_progress_calls++; return 0; } @@ -397,64 +448,78 @@ * @param expected_ret expected return value from git_push_finish() * @param check_progress_cb Check that the push progress callbacks are called */ -static void do_push(const char *refspecs[], size_t refspecs_len, +static void do_push( + const char *refspecs[], size_t refspecs_len, push_status expected_statuses[], size_t expected_statuses_len, - expected_ref expected_refs[], size_t expected_refs_len, int expected_ret, int check_progress_cb) + expected_ref expected_refs[], size_t expected_refs_len, + int expected_ret, int check_progress_cb, int check_update_tips_cb) { - git_push *push; git_push_options opts = GIT_PUSH_OPTIONS_INIT; size_t i; - int ret; - int pack_progress_called = 0, transfer_progress_called = 0; + int error; + git_strarray specs = {0}; + git_signature *pusher; + git_remote_callbacks callbacks; + record_callbacks_data *data; if (_remote) { /* Auto-detect the number of threads to use */ opts.pb_parallelism = 0; - cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH)); + cl_git_pass(git_signature_now(&pusher, "Foo Bar", "foo@example.com")); - cl_git_pass(git_push_new(&push, _remote)); - cl_git_pass(git_push_set_options(push, &opts)); + memcpy(&callbacks, git_remote_get_callbacks(_remote), sizeof(callbacks)); + data = callbacks.payload; - if (check_progress_cb) - cl_git_pass(git_push_set_callbacks(push, push_pack_progress_cb, &pack_progress_called, push_transfer_progress_cb, &transfer_progress_called)); + callbacks.pack_progress = push_pack_progress_cb; + callbacks.push_transfer_progress = push_transfer_progress_cb; + callbacks.push_update_reference = record_push_status_cb; + cl_git_pass(git_remote_set_callbacks(_remote, &callbacks)); + + if (refspecs_len) { + specs.count = refspecs_len; + specs.strings = git__calloc(refspecs_len, sizeof(char *)); + cl_assert(specs.strings); + } for (i = 0; i < refspecs_len; i++) - cl_git_pass(git_push_add_refspec(push, refspecs[i])); + specs.strings[i] = (char *) refspecs[i]; + + /* if EUSER, then abort in transfer */ + if (check_progress_cb && expected_ret == GIT_EUSER) + data->transfer_progress_calls = GIT_EUSER; + + error = git_remote_push(_remote, &specs, &opts, pusher, "test push"); + git__free(specs.strings); if (expected_ret < 0) { - cl_git_fail(ret = git_push_finish(push)); - cl_assert_equal_i(0, git_push_unpack_ok(push)); - } - else { - cl_git_pass(ret = git_push_finish(push)); - cl_assert_equal_i(1, git_push_unpack_ok(push)); + cl_git_fail_with(expected_ret, error); + } else { + cl_git_pass(error); } - if (check_progress_cb) { - cl_assert_equal_i(1, pack_progress_called); - cl_assert_equal_i(1, transfer_progress_called); + if (check_progress_cb && expected_ret == 0) { + cl_assert(data->pack_progress_calls > 0); + cl_assert(data->transfer_progress_calls > 0); } - do_verify_push_status(push, expected_statuses, expected_statuses_len); - - cl_assert_equal_i(expected_ret, ret); + do_verify_push_status(data, expected_statuses, expected_statuses_len); verify_refs(_remote, expected_refs, expected_refs_len); - - cl_git_pass(git_push_update_tips(push)); verify_tracking_branches(_remote, expected_refs, expected_refs_len); - git_push_free(push); + if (check_update_tips_cb) + verify_update_tips_callback(_remote, expected_refs, expected_refs_len); - git_remote_disconnect(_remote); + git_signature_free(pusher); } + } /* Call push_finish() without ever calling git_push_add_refspec() */ void test_online_push__noop(void) { - do_push(NULL, 0, NULL, 0, NULL, 0, 0, 0); + do_push(NULL, 0, NULL, 0, NULL, 0, 0, 0, 1); } void test_online_push__b1(void) @@ -464,7 +529,7 @@ expected_ref exp_refs[] = { { "refs/heads/b1", &_oid_b1 } }; do_push(specs, ARRAY_SIZE(specs), exp_stats, ARRAY_SIZE(exp_stats), - exp_refs, ARRAY_SIZE(exp_refs), 0, 1); + exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1); } void test_online_push__b2(void) @@ -474,7 +539,7 @@ expected_ref exp_refs[] = { { "refs/heads/b2", &_oid_b2 } }; do_push(specs, ARRAY_SIZE(specs), exp_stats, ARRAY_SIZE(exp_stats), - exp_refs, ARRAY_SIZE(exp_refs), 0, 1); + exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1); } void test_online_push__b3(void) @@ -484,7 +549,7 @@ expected_ref exp_refs[] = { { "refs/heads/b3", &_oid_b3 } }; do_push(specs, ARRAY_SIZE(specs), exp_stats, ARRAY_SIZE(exp_stats), - exp_refs, ARRAY_SIZE(exp_refs), 0, 1); + exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1); } void test_online_push__b4(void) @@ -494,7 +559,7 @@ expected_ref exp_refs[] = { { "refs/heads/b4", &_oid_b4 } }; do_push(specs, ARRAY_SIZE(specs), exp_stats, ARRAY_SIZE(exp_stats), - exp_refs, ARRAY_SIZE(exp_refs), 0, 1); + exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1); } void test_online_push__b5(void) @@ -504,11 +569,20 @@ expected_ref exp_refs[] = { { "refs/heads/b5", &_oid_b5 } }; do_push(specs, ARRAY_SIZE(specs), exp_stats, ARRAY_SIZE(exp_stats), - exp_refs, ARRAY_SIZE(exp_refs), 0, 1); + exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1); +} + +void test_online_push__b5_cancel(void) +{ + const char *specs[] = { "refs/heads/b5:refs/heads/b5" }; + do_push(specs, ARRAY_SIZE(specs), NULL, 0, NULL, 0, GIT_EUSER, 1, 1); } void test_online_push__multi(void) { + git_reflog *log; + const git_reflog_entry *entry; + const char *specs[] = { "refs/heads/b1:refs/heads/b1", "refs/heads/b2:refs/heads/b2", @@ -532,16 +606,25 @@ }; do_push(specs, ARRAY_SIZE(specs), exp_stats, ARRAY_SIZE(exp_stats), - exp_refs, ARRAY_SIZE(exp_refs), 0, 1); + exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1); + + cl_git_pass(git_reflog_read(&log, _repo, "refs/remotes/test/b1")); + entry = git_reflog_entry_byindex(log, 0); + if (entry) { + cl_assert_equal_s("test push", git_reflog_entry_message(entry)); + cl_assert_equal_s("foo@example.com", git_reflog_entry_committer(entry)->email); + } + + git_reflog_free(log); } void test_online_push__implicit_tgt(void) { - const char *specs1[] = { "refs/heads/b1:" }; + const char *specs1[] = { "refs/heads/b1" }; push_status exp_stats1[] = { { "refs/heads/b1", 1 } }; expected_ref exp_refs1[] = { { "refs/heads/b1", &_oid_b1 } }; - const char *specs2[] = { "refs/heads/b2:" }; + const char *specs2[] = { "refs/heads/b2" }; push_status exp_stats2[] = { { "refs/heads/b2", 1 } }; expected_ref exp_refs2[] = { { "refs/heads/b1", &_oid_b1 }, @@ -550,10 +633,10 @@ do_push(specs1, ARRAY_SIZE(specs1), exp_stats1, ARRAY_SIZE(exp_stats1), - exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1); + exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1, 1); do_push(specs2, ARRAY_SIZE(specs2), exp_stats2, ARRAY_SIZE(exp_stats2), - exp_refs2, ARRAY_SIZE(exp_refs2), 0, 0); + exp_refs2, ARRAY_SIZE(exp_refs2), 0, 0, 0); } void test_online_push__fast_fwd(void) @@ -575,19 +658,19 @@ do_push(specs_init, ARRAY_SIZE(specs_init), exp_stats_init, ARRAY_SIZE(exp_stats_init), - exp_refs_init, ARRAY_SIZE(exp_refs_init), 0, 1); + exp_refs_init, ARRAY_SIZE(exp_refs_init), 0, 1, 1); do_push(specs_ff, ARRAY_SIZE(specs_ff), exp_stats_ff, ARRAY_SIZE(exp_stats_ff), - exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0, 0); + exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0, 0, 0); do_push(specs_reset, ARRAY_SIZE(specs_reset), exp_stats_init, ARRAY_SIZE(exp_stats_init), - exp_refs_init, ARRAY_SIZE(exp_refs_init), 0, 0); + exp_refs_init, ARRAY_SIZE(exp_refs_init), 0, 0, 0); do_push(specs_ff_force, ARRAY_SIZE(specs_ff_force), exp_stats_ff, ARRAY_SIZE(exp_stats_ff), - exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0, 0); + exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0, 0, 0); } void test_online_push__tag_commit(void) @@ -597,7 +680,7 @@ expected_ref exp_refs[] = { { "refs/tags/tag-commit", &_tag_commit } }; do_push(specs, ARRAY_SIZE(specs), exp_stats, ARRAY_SIZE(exp_stats), - exp_refs, ARRAY_SIZE(exp_refs), 0, 1); + exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1); } void test_online_push__tag_tree(void) @@ -607,7 +690,7 @@ expected_ref exp_refs[] = { { "refs/tags/tag-tree", &_tag_tree } }; do_push(specs, ARRAY_SIZE(specs), exp_stats, ARRAY_SIZE(exp_stats), - exp_refs, ARRAY_SIZE(exp_refs), 0, 1); + exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1); } void test_online_push__tag_blob(void) @@ -617,7 +700,7 @@ expected_ref exp_refs[] = { { "refs/tags/tag-blob", &_tag_blob } }; do_push(specs, ARRAY_SIZE(specs), exp_stats, ARRAY_SIZE(exp_stats), - exp_refs, ARRAY_SIZE(exp_refs), 0, 1); + exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1); } void test_online_push__tag_lightweight(void) @@ -627,7 +710,7 @@ expected_ref exp_refs[] = { { "refs/tags/tag-lightweight", &_tag_lightweight } }; do_push(specs, ARRAY_SIZE(specs), exp_stats, ARRAY_SIZE(exp_stats), - exp_refs, ARRAY_SIZE(exp_refs), 0, 1); + exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1); } void test_online_push__tag_to_tag(void) @@ -637,7 +720,7 @@ expected_ref exp_refs[] = { { "refs/tags/tag-tag", &_tag_tag } }; do_push(specs, ARRAY_SIZE(specs), exp_stats, ARRAY_SIZE(exp_stats), - exp_refs, ARRAY_SIZE(exp_refs), 0, 0); + exp_refs, ARRAY_SIZE(exp_refs), 0, 0, 0); } void test_online_push__force(void) @@ -654,16 +737,17 @@ do_push(specs1, ARRAY_SIZE(specs1), exp_stats1, ARRAY_SIZE(exp_stats1), - exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1); + exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1, 1); do_push(specs2, ARRAY_SIZE(specs2), NULL, 0, - exp_refs1, ARRAY_SIZE(exp_refs1), GIT_ENONFASTFORWARD, 0); + exp_refs1, ARRAY_SIZE(exp_refs1), GIT_ENONFASTFORWARD, 0, 0); /* Non-fast-forward update with force should pass. */ + record_callbacks_data_clear(&_record_cbs_data); do_push(specs2_force, ARRAY_SIZE(specs2_force), exp_stats2_force, ARRAY_SIZE(exp_stats2_force), - exp_refs2_force, ARRAY_SIZE(exp_refs2_force), 0, 1); + exp_refs2_force, ARRAY_SIZE(exp_refs2_force), 0, 1, 1); } void test_online_push__delete(void) @@ -694,7 +778,7 @@ do_push(specs1, ARRAY_SIZE(specs1), exp_stats1, ARRAY_SIZE(exp_stats1), - exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1); + exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1, 1); /* When deleting a non-existent branch, the git client sends zero for both * the old and new commit id. This should succeed on the server with the @@ -704,23 +788,23 @@ */ do_push(specs_del_fake, ARRAY_SIZE(specs_del_fake), exp_stats_fake, 1, - exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0); + exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0, 0); do_push(specs_del_fake_force, ARRAY_SIZE(specs_del_fake_force), exp_stats_fake, 1, - exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0); + exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0, 0); /* Delete one of the pushed branches. */ do_push(specs_delete, ARRAY_SIZE(specs_delete), exp_stats_delete, ARRAY_SIZE(exp_stats_delete), - exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0, 0); + exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0, 0, 0); /* Re-push branches and retry delete with force. */ do_push(specs1, ARRAY_SIZE(specs1), exp_stats1, ARRAY_SIZE(exp_stats1), - exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0); + exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0, 0); do_push(specs_delete_force, ARRAY_SIZE(specs_delete_force), exp_stats_delete, ARRAY_SIZE(exp_stats_delete), - exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0, 0); + exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0, 0, 0); } void test_online_push__bad_refspecs(void) @@ -728,16 +812,16 @@ /* All classes of refspecs that should be rejected by * git_push_add_refspec() should go in this test. */ - git_push *push; + char *specs = { + "b6:b6", + }; + git_strarray arr = { + &specs, + 1, + }; if (_remote) { -// cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH)); - cl_git_pass(git_push_new(&push, _remote)); - - /* Unexpanded branch names not supported */ - cl_git_fail(git_push_add_refspec(push, "b6:b6")); - - git_push_free(push); + cl_git_fail(git_remote_upload(_remote, &arr, NULL)); } } @@ -746,19 +830,10 @@ /* TODO: Expressions in refspecs doesn't actually work yet */ const char *specs_left_expr[] = { "refs/heads/b2~1:refs/heads/b2" }; - /* expect not NULL to indicate failure (core git replies "funny refname", - * other servers may be less pithy. */ - const char *specs_right_expr[] = { "refs/heads/b2:refs/heads/b2~1" }; - push_status exp_stats_right_expr[] = { { "refs/heads/b2~1", 0 } }; - /* TODO: Find a more precise way of checking errors than a exit code of -1. */ do_push(specs_left_expr, ARRAY_SIZE(specs_left_expr), NULL, 0, - NULL, 0, -1, 0); - - do_push(specs_right_expr, ARRAY_SIZE(specs_right_expr), - exp_stats_right_expr, ARRAY_SIZE(exp_stats_right_expr), - NULL, 0, 0, 1); + NULL, 0, -1, 0, 0); } void test_online_push__notes(void) @@ -768,17 +843,57 @@ const char *specs[] = { "refs/notes/commits:refs/notes/commits" }; push_status exp_stats[] = { { "refs/notes/commits", 1 } }; expected_ref exp_refs[] = { { "refs/notes/commits", &expected_oid } }; + const char *specs_del[] = { ":refs/notes/commits" }; + git_oid_fromstr(&expected_oid, "8461a99b27b7043e58ff6e1f5d2cf07d282534fb"); target_oid = &_oid_b6; /* Create note to push */ cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */ - cl_git_pass(git_note_create(¬e_oid, _repo, signature, signature, NULL, target_oid, "hello world\n", 0)); + cl_git_pass(git_note_create(¬e_oid, _repo, NULL, signature, signature, target_oid, "hello world\n", 0)); do_push(specs, ARRAY_SIZE(specs), exp_stats, ARRAY_SIZE(exp_stats), - exp_refs, ARRAY_SIZE(exp_refs), 0, 1); + exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1); + + /* And make sure to delete the note */ + + do_push(specs_del, ARRAY_SIZE(specs_del), + exp_stats, 1, + NULL, 0, 0, 0, 0); + + git_signature_free(signature); +} + +void test_online_push__configured(void) +{ + git_oid note_oid, *target_oid, expected_oid; + git_signature *signature; + const char *specs[] = { "refs/notes/commits:refs/notes/commits" }; + push_status exp_stats[] = { { "refs/notes/commits", 1 } }; + expected_ref exp_refs[] = { { "refs/notes/commits", &expected_oid } }; + const char *specs_del[] = { ":refs/notes/commits" }; + + git_oid_fromstr(&expected_oid, "8461a99b27b7043e58ff6e1f5d2cf07d282534fb"); + + target_oid = &_oid_b6; + + cl_git_pass(git_remote_add_push(_remote, specs[0])); + + /* Create note to push */ + cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */ + cl_git_pass(git_note_create(¬e_oid, _repo, NULL, signature, signature, target_oid, "hello world\n", 0)); + + do_push(NULL, 0, + exp_stats, ARRAY_SIZE(exp_stats), + exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1); + + /* And make sure to delete the note */ + + do_push(specs_del, ARRAY_SIZE(specs_del), + exp_stats, 1, + NULL, 0, 0, 0, 0); git_signature_free(signature); } diff -Nru libgit2-0.20.0/tests/online/push_util.c libgit2-0.22.2/tests/online/push_util.c --- libgit2-0.20.0/tests/online/push_util.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/online/push_util.c 2015-03-24 16:10:45.000000000 +0000 @@ -14,15 +14,31 @@ git__free(t); } +void push_status_free(push_status *s) +{ + git__free(s->ref); + git__free(s->msg); + git__free(s); +} + void record_callbacks_data_clear(record_callbacks_data *data) { size_t i; updated_tip *tip; + push_status *status; git_vector_foreach(&data->updated_tips, i, tip) updated_tip_free(tip); git_vector_free(&data->updated_tips); + + git_vector_foreach(&data->statuses, i, status) + push_status_free(status); + + git_vector_free(&data->statuses); + + data->pack_progress_calls = 0; + data->transfer_progress_calls = 0; } int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data) @@ -110,9 +126,8 @@ git_buf_puts(&msg, "Expected and actual refs differ:\nEXPECTED:\n"); for(i = 0; i < expected_refs_len; i++) { - cl_assert(oid_str = git_oid_allocfmt(expected_refs[i].oid)); + oid_str = git_oid_tostr_s(expected_refs[i].oid); cl_git_pass(git_buf_printf(&msg, "%s = %s\n", expected_refs[i].name, oid_str)); - git__free(oid_str); } git_buf_puts(&msg, "\nACTUAL:\n"); @@ -121,9 +136,8 @@ if (master_present && !strcmp(actual->name, "refs/heads/master")) continue; - cl_assert(oid_str = git_oid_allocfmt(&actual->oid)); + oid_str = git_oid_tostr_s(&actual->oid); cl_git_pass(git_buf_printf(&msg, "%s = %s\n", actual->name, oid_str)); - git__free(oid_str); } cl_fail(git_buf_cstr(&msg)); diff -Nru libgit2-0.20.0/tests/online/push_util.h libgit2-0.22.2/tests/online/push_util.h --- libgit2-0.20.0/tests/online/push_util.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/online/push_util.h 2015-03-24 16:10:45.000000000 +0000 @@ -12,7 +12,7 @@ * @param data pointer to a record_callbacks_data instance */ #define RECORD_CALLBACKS_INIT(data) \ - { GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, record_update_tips_cb, data } + { GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, NULL, record_update_tips_cb, NULL, NULL, NULL, data } typedef struct { char *name; @@ -22,6 +22,9 @@ typedef struct { git_vector updated_tips; + git_vector statuses; + int pack_progress_calls; + int transfer_progress_calls; } record_callbacks_data; typedef struct { @@ -29,6 +32,15 @@ const git_oid *oid; } expected_ref; +/* the results of a push status. when used for expected values, msg may be NULL + * to indicate that it should not be matched. */ +typedef struct { + char *ref; + int success; + char *msg; +} push_status; + + void updated_tip_free(updated_tip *t); void record_callbacks_data_clear(record_callbacks_data *data); diff -Nru libgit2-0.20.0/tests/pack/indexer.c libgit2-0.22.2/tests/pack/indexer.c --- libgit2-0.20.0/tests/pack/indexer.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/pack/indexer.c 2015-03-24 16:10:45.000000000 +0000 @@ -11,7 +11,7 @@ * This is a packfile with three objects. The second is a delta which * depends on the third, which is also a delta. */ -unsigned char out_of_order_pack[] = { +static const unsigned char out_of_order_pack[] = { 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03, 0x32, 0x78, 0x9c, 0x63, 0x67, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x76, 0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95, 0x10, @@ -23,13 +23,13 @@ 0x19, 0x87, 0x58, 0x80, 0x61, 0x09, 0x9a, 0x33, 0xca, 0x7a, 0x31, 0x92, 0x6f, 0xae, 0x66, 0x75 }; -unsigned int out_of_order_pack_len = 112; +static const unsigned int out_of_order_pack_len = 112; /* * Packfile with two objects. The second is a delta against an object * which is not in the packfile */ -unsigned char thin_pack[] = { +static const unsigned char thin_pack[] = { 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x32, 0x78, 0x9c, 0x63, 0x67, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x76, 0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95, 0x10, @@ -38,18 +38,19 @@ 0x3a, 0x6f, 0x39, 0xd1, 0xfe, 0x66, 0x68, 0x6b, 0xa5, 0xe5, 0xe2, 0x97, 0xac, 0x94, 0x6c, 0x76, 0x0b, 0x04 }; -unsigned int thin_pack_len = 78; +static const unsigned int thin_pack_len = 78; -unsigned char base_obj[] = { 07, 076 }; -unsigned int base_obj_len = 2; +static const unsigned char base_obj[] = { 07, 076 }; +static const unsigned int base_obj_len = 2; void test_pack_indexer__out_of_order(void) { - git_indexer *idx; - git_transfer_progress stats; + 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, out_of_order_pack, out_of_order_pack_len, &stats)); + cl_git_pass(git_indexer_append( + idx, out_of_order_pack, out_of_order_pack_len, &stats)); cl_git_pass(git_indexer_commit(idx, &stats)); cl_assert_equal_i(stats.total_objects, 3); @@ -61,8 +62,8 @@ void test_pack_indexer__fix_thin(void) { - git_indexer *idx; - git_transfer_progress stats; + git_indexer *idx = NULL; + git_transfer_progress stats = { 0 }; git_repository *repo; git_odb *odb; git_oid id, should_id; @@ -73,7 +74,7 @@ /* 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(!git_oid_cmp(&id, &should_id)); + 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, thin_pack, thin_pack_len, &stats)); @@ -85,7 +86,7 @@ cl_assert_equal_i(stats.local_objects, 1); git_oid_fromstr(&should_id, "11f0f69b334728fdd8bc86b80499f22f29d85b15"); - cl_assert(!git_oid_cmp(git_indexer_hash(idx), &should_id)); + cl_assert_equal_oid(&should_id, git_indexer_hash(idx)); git_indexer_free(idx); git_odb_free(odb); diff -Nru libgit2-0.20.0/tests/pack/packbuilder.c libgit2-0.22.2/tests/pack/packbuilder.c --- libgit2-0.20.0/tests/pack/packbuilder.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/pack/packbuilder.c 2015-03-24 16:10:45.000000000 +0000 @@ -12,14 +12,17 @@ static git_indexer *_indexer; static git_vector _commits; static int _commits_is_initialized; +static git_transfer_progress _stats; void test_pack_packbuilder__initialize(void) { _repo = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(p_chdir("testrepo.git")); cl_git_pass(git_revwalk_new(&_revwalker, _repo)); cl_git_pass(git_packbuilder_new(&_packbuilder, _repo)); cl_git_pass(git_vector_init(&_commits, 0, NULL)); _commits_is_initialized = 1; + memset(&_stats, 0, sizeof(_stats)); } void test_pack_packbuilder__cleanup(void) @@ -44,6 +47,7 @@ git_indexer_free(_indexer); _indexer = NULL; + cl_git_pass(p_chdir("..")); cl_git_sandbox_cleanup(); _repo = NULL; } @@ -89,7 +93,7 @@ git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT; git_hash_ctx ctx; git_oid hash; - char hex[41]; hex[40] = '\0'; + char hex[GIT_OID_HEXSZ+1]; hex[GIT_OID_HEXSZ] = '\0'; seed_packbuilder(); @@ -131,7 +135,7 @@ void test_pack_packbuilder__get_hash(void) { - char hex[41]; hex[40] = '\0'; + char hex[GIT_OID_HEXSZ+1]; hex[GIT_OID_HEXSZ] = '\0'; seed_packbuilder(); @@ -184,11 +188,10 @@ test_write_pack_permission(0666, 0666); } -static git_transfer_progress stats; static int foreach_cb(void *buf, size_t len, void *payload) { git_indexer *idx = (git_indexer *) payload; - cl_git_pass(git_indexer_append(idx, buf, len, &stats)); + cl_git_pass(git_indexer_append(idx, buf, len, &_stats)); return 0; } @@ -199,6 +202,24 @@ seed_packbuilder(); cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL)); cl_git_pass(git_packbuilder_foreach(_packbuilder, foreach_cb, idx)); - cl_git_pass(git_indexer_commit(idx, &stats)); + cl_git_pass(git_indexer_commit(idx, &_stats)); + git_indexer_free(idx); +} + +static int foreach_cancel_cb(void *buf, size_t len, void *payload) +{ + git_indexer *idx = (git_indexer *)payload; + cl_git_pass(git_indexer_append(idx, buf, len, &_stats)); + return (_stats.total_objects > 2) ? -1111 : 0; +} + +void test_pack_packbuilder__foreach_with_cancel(void) +{ + git_indexer *idx; + + seed_packbuilder(); + cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL)); + cl_git_fail_with( + git_packbuilder_foreach(_packbuilder, foreach_cancel_cb, idx), -1111); git_indexer_free(idx); } diff -Nru libgit2-0.20.0/tests/pack/sharing.c libgit2-0.22.2/tests/pack/sharing.c --- libgit2-0.20.0/tests/pack/sharing.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/pack/sharing.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,42 @@ +#include "clar_libgit2.h" +#include +#include "strmap.h" +#include "mwindow.h" +#include "pack.h" + +extern git_strmap *git__pack_cache; + +void test_pack_sharing__open_two_repos(void) +{ + git_repository *repo1, *repo2; + git_object *obj1, *obj2; + git_oid id; + git_strmap_iter pos; + void *data; + int error; + + cl_git_pass(git_repository_open(&repo1, cl_fixture("testrepo.git"))); + cl_git_pass(git_repository_open(&repo2, cl_fixture("testrepo.git"))); + + git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + + cl_git_pass(git_object_lookup(&obj1, repo1, &id, GIT_OBJ_ANY)); + cl_git_pass(git_object_lookup(&obj2, repo2, &id, GIT_OBJ_ANY)); + + pos = 0; + while ((error = git_strmap_next(&data, &pos, git__pack_cache)) == 0) { + struct git_pack_file *pack = (struct git_pack_file *) data; + + cl_assert_equal_i(2, pack->refcount.val); + } + + cl_assert_equal_i(3, git_strmap_num_entries(git__pack_cache)); + + git_object_free(obj1); + git_object_free(obj2); + git_repository_free(repo1); + git_repository_free(repo2); + + /* we don't want to keep the packs open after the repos go away */ + cl_assert_equal_i(0, git_strmap_num_entries(git__pack_cache)); +} diff -Nru libgit2-0.20.0/tests/path/core.c libgit2-0.22.2/tests/path/core.c --- libgit2-0.20.0/tests/path/core.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/path/core.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,354 @@ +#include "clar_libgit2.h" +#include "path.h" + +static void test_make_relative( + const char *expected_path, + const char *path, + const char *parent, + int expected_status) +{ + git_buf buf = GIT_BUF_INIT; + git_buf_puts(&buf, path); + cl_assert_equal_i(expected_status, git_path_make_relative(&buf, parent)); + cl_assert_equal_s(expected_path, buf.ptr); + git_buf_free(&buf); +} + +void test_path_core__make_relative(void) +{ + test_make_relative("foo.c", "/path/to/foo.c", "/path/to", 0); + test_make_relative("bar/foo.c", "/path/to/bar/foo.c", "/path/to", 0); + test_make_relative("foo.c", "/path/to/foo.c", "/path/to/", 0); + + test_make_relative("", "/path/to", "/path/to", 0); + test_make_relative("", "/path/to", "/path/to/", 0); + + test_make_relative("../", "/path/to", "/path/to/foo", 0); + + test_make_relative("../foo.c", "/path/to/foo.c", "/path/to/bar", 0); + test_make_relative("../bar/foo.c", "/path/to/bar/foo.c", "/path/to/baz", 0); + + test_make_relative("../../foo.c", "/path/to/foo.c", "/path/to/foo/bar", 0); + test_make_relative("../../foo/bar.c", "/path/to/foo/bar.c", "/path/to/bar/foo", 0); + + test_make_relative("../../foo.c", "/foo.c", "/bar/foo", 0); + + test_make_relative("foo.c", "/path/to/foo.c", "/path/to/", 0); + test_make_relative("../foo.c", "/path/to/foo.c", "/path/to/bar/", 0); + + test_make_relative("foo.c", "d:/path/to/foo.c", "d:/path/to", 0); + + test_make_relative("../foo", "/foo", "/bar", 0); + test_make_relative("path/to/foo.c", "/path/to/foo.c", "/", 0); + test_make_relative("../foo", "path/to/foo", "path/to/bar", 0); + + test_make_relative("/path/to/foo.c", "/path/to/foo.c", "d:/path/to", GIT_ENOTFOUND); + test_make_relative("d:/path/to/foo.c", "d:/path/to/foo.c", "/path/to", GIT_ENOTFOUND); + + test_make_relative("/path/to/foo.c", "/path/to/foo.c", "not-a-rooted-path", GIT_ENOTFOUND); + test_make_relative("not-a-rooted-path", "not-a-rooted-path", "/path/to", GIT_ENOTFOUND); + + test_make_relative("/path", "/path", "pathtofoo", GIT_ENOTFOUND); + test_make_relative("path", "path", "pathtofoo", GIT_ENOTFOUND); +} + +void test_path_core__isvalid_standard(void) +{ + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar/file.txt", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar/.file", 0)); +} + +void test_path_core__isvalid_empty_dir_component(void) +{ + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo//bar", 0)); + + /* leading slash */ + cl_assert_equal_b(false, git_path_isvalid(NULL, "/", 0)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "/foo", 0)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "/foo/bar", 0)); + + /* trailing slash */ + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/", 0)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar/", 0)); +} + +void test_path_core__isvalid_dot_and_dotdot(void) +{ + cl_assert_equal_b(true, git_path_isvalid(NULL, ".", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "./foo", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "./foo", 0)); + + cl_assert_equal_b(true, git_path_isvalid(NULL, "..", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "../foo", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/..", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "../foo", 0)); + + cl_assert_equal_b(false, git_path_isvalid(NULL, ".", GIT_PATH_REJECT_TRAVERSAL)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "./foo", GIT_PATH_REJECT_TRAVERSAL)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.", GIT_PATH_REJECT_TRAVERSAL)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "./foo", GIT_PATH_REJECT_TRAVERSAL)); + + cl_assert_equal_b(false, git_path_isvalid(NULL, "..", GIT_PATH_REJECT_TRAVERSAL)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "../foo", GIT_PATH_REJECT_TRAVERSAL)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/..", GIT_PATH_REJECT_TRAVERSAL)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "../foo", GIT_PATH_REJECT_TRAVERSAL)); +} + +void test_path_core__isvalid_dot_git(void) +{ + cl_assert_equal_b(true, git_path_isvalid(NULL, ".git", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, ".git/foo", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.git", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.git/bar", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.GIT/bar", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar/.Git", 0)); + + cl_assert_equal_b(false, git_path_isvalid(NULL, ".git", GIT_PATH_REJECT_DOT_GIT)); + cl_assert_equal_b(false, git_path_isvalid(NULL, ".git/foo", GIT_PATH_REJECT_DOT_GIT)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git", GIT_PATH_REJECT_DOT_GIT)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.git/bar", GIT_PATH_REJECT_DOT_GIT)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/.GIT/bar", GIT_PATH_REJECT_DOT_GIT)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar/.Git", GIT_PATH_REJECT_DOT_GIT)); + + cl_assert_equal_b(true, git_path_isvalid(NULL, "!git", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/!git", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "!git/bar", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, ".tig", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/.tig", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, ".tig/bar", 0)); +} + +void test_path_core__isvalid_backslash(void) +{ + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo\\file.txt", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar\\file.txt", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar\\", 0)); + + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo\\file.txt", GIT_PATH_REJECT_BACKSLASH)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar\\file.txt", GIT_PATH_REJECT_BACKSLASH)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar\\", GIT_PATH_REJECT_BACKSLASH)); +} + +void test_path_core__isvalid_trailing_dot(void) +{ + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo.", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo...", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar.", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo./bar", 0)); + + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo.", GIT_PATH_REJECT_TRAILING_DOT)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo...", GIT_PATH_REJECT_TRAILING_DOT)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar.", GIT_PATH_REJECT_TRAILING_DOT)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo./bar", GIT_PATH_REJECT_TRAILING_DOT)); +} + +void test_path_core__isvalid_trailing_space(void) +{ + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo ", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo ", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar ", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, " ", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo /bar", 0)); + + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo ", GIT_PATH_REJECT_TRAILING_SPACE)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo ", GIT_PATH_REJECT_TRAILING_SPACE)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar ", GIT_PATH_REJECT_TRAILING_SPACE)); + cl_assert_equal_b(false, git_path_isvalid(NULL, " ", GIT_PATH_REJECT_TRAILING_SPACE)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo /bar", GIT_PATH_REJECT_TRAILING_SPACE)); +} + +void test_path_core__isvalid_trailing_colon(void) +{ + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo:", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo/bar:", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, ":", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "foo:/bar", 0)); + + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo:", GIT_PATH_REJECT_TRAILING_COLON)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo/bar:", GIT_PATH_REJECT_TRAILING_COLON)); + cl_assert_equal_b(false, git_path_isvalid(NULL, ":", GIT_PATH_REJECT_TRAILING_COLON)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "foo:/bar", GIT_PATH_REJECT_TRAILING_COLON)); +} + +void test_path_core__isvalid_dotgit_ntfs(void) +{ + cl_assert_equal_b(true, git_path_isvalid(NULL, ".git", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, ".git ", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, ".git.", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, ".git.. .", 0)); + + cl_assert_equal_b(true, git_path_isvalid(NULL, "git~1", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "git~1 ", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "git~1.", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "git~1.. .", 0)); + + cl_assert_equal_b(false, git_path_isvalid(NULL, ".git", GIT_PATH_REJECT_DOT_GIT_NTFS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, ".git ", GIT_PATH_REJECT_DOT_GIT_NTFS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, ".git.", GIT_PATH_REJECT_DOT_GIT_NTFS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, ".git.. .", GIT_PATH_REJECT_DOT_GIT_NTFS)); + + cl_assert_equal_b(false, git_path_isvalid(NULL, "git~1", GIT_PATH_REJECT_DOT_GIT_NTFS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "git~1 ", GIT_PATH_REJECT_DOT_GIT_NTFS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "git~1.", GIT_PATH_REJECT_DOT_GIT_NTFS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "git~1.. .", GIT_PATH_REJECT_DOT_GIT_NTFS)); +} + +void test_path_core__isvalid_dos_paths(void) +{ + cl_assert_equal_b(true, git_path_isvalid(NULL, "aux", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "aux.", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "aux:", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "aux.asdf", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "aux.asdf\\zippy", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "aux:asdf\\foobar", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "con", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "prn", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "nul", 0)); + + cl_assert_equal_b(false, git_path_isvalid(NULL, "aux", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "aux.", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "aux:", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "aux.asdf", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "aux.asdf\\zippy", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "aux:asdf\\foobar", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "con", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "prn", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "nul", GIT_PATH_REJECT_DOS_PATHS)); + + cl_assert_equal_b(true, git_path_isvalid(NULL, "aux1", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "aux1", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "auxn", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "aux\\foo", GIT_PATH_REJECT_DOS_PATHS)); +} + +void test_path_core__isvalid_dos_paths_withnum(void) +{ + cl_assert_equal_b(true, git_path_isvalid(NULL, "com1", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "com1.", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "com1:", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "com1.asdf", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "com1.asdf\\zippy", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "com1:asdf\\foobar", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "com1\\foo", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "lpt1", 0)); + + cl_assert_equal_b(false, git_path_isvalid(NULL, "com1", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "com1.", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "com1:", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "com1.asdf", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "com1.asdf\\zippy", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "com1:asdf\\foobar", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "com1/foo", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "lpt1", GIT_PATH_REJECT_DOS_PATHS)); + + cl_assert_equal_b(true, git_path_isvalid(NULL, "com0", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "com0", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "com10", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "com10", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "comn", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "com1\\foo", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "lpt0", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "lpt10", GIT_PATH_REJECT_DOS_PATHS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "lptn", GIT_PATH_REJECT_DOS_PATHS)); +} + +void test_path_core__isvalid_nt_chars(void) +{ + cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf\001foo", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf\037bar", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "asdffoo", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf:foo", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf\"bar", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf|foo", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf?bar", 0)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "asdf*bar", 0)); + + cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf\001foo", GIT_PATH_REJECT_NT_CHARS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf\037bar", GIT_PATH_REJECT_NT_CHARS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "asdffoo", GIT_PATH_REJECT_NT_CHARS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf:foo", GIT_PATH_REJECT_NT_CHARS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf\"bar", GIT_PATH_REJECT_NT_CHARS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf|foo", GIT_PATH_REJECT_NT_CHARS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf?bar", GIT_PATH_REJECT_NT_CHARS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "asdf*bar", GIT_PATH_REJECT_NT_CHARS)); +} + +void test_path_core__isvalid_dotgit_with_hfs_ignorables(void) +{ + cl_assert_equal_b(false, git_path_isvalid(NULL, ".git", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, ".git\xe2\x80\x8c", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, ".gi\xe2\x80\x8dT", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, ".g\xe2\x80\x8eIt", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, ".\xe2\x80\x8fgIt", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "\xe2\x80\xaa.gIt", GIT_PATH_REJECT_DOT_GIT_HFS)); + + cl_assert_equal_b(false, git_path_isvalid(NULL, "\xe2\x80\xab.\xe2\x80\xacG\xe2\x80\xadI\xe2\x80\xaet", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "\xe2\x81\xab.\xe2\x80\xaaG\xe2\x81\xabI\xe2\x80\xact", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(false, git_path_isvalid(NULL, "\xe2\x81\xad.\xe2\x80\xaeG\xef\xbb\xbfIT", GIT_PATH_REJECT_DOT_GIT_HFS)); + + cl_assert_equal_b(true, git_path_isvalid(NULL, ".", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, ".g", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, ".gi", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, " .git", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "..git\xe2\x80\x8c", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, ".gi\xe2\x80\x8dT.", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, ".g\xe2\x80It", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, ".\xe2gIt", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, "\xe2\x80\xaa.gi", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, ".gi\x80\x8dT", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, ".gi\x8dT", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, ".g\xe2i\x80T\x8e", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, ".git\xe2\x80\xbf", GIT_PATH_REJECT_DOT_GIT_HFS)); + cl_assert_equal_b(true, git_path_isvalid(NULL, ".git\xe2\xab\x81", GIT_PATH_REJECT_DOT_GIT_HFS)); +} + +static void test_join_unrooted( + const char *expected_result, + ssize_t expected_rootlen, + const char *path, + const char *base) +{ + git_buf result = GIT_BUF_INIT; + ssize_t root_at; + + cl_git_pass(git_path_join_unrooted(&result, path, base, &root_at)); + cl_assert_equal_s(expected_result, result.ptr); + cl_assert_equal_i(expected_rootlen, root_at); + + git_buf_free(&result); +} + +void test_path_core__join_unrooted(void) +{ + git_buf out = GIT_BUF_INIT; + + test_join_unrooted("foo", 0, "foo", NULL); + test_join_unrooted("foo/bar", 0, "foo/bar", NULL); + + /* Relative paths have base prepended */ + test_join_unrooted("/foo/bar", 4, "bar", "/foo"); + test_join_unrooted("/foo/bar/foobar", 4, "bar/foobar", "/foo"); + test_join_unrooted("c:/foo/bar/foobar", 6, "bar/foobar", "c:/foo"); + test_join_unrooted("c:/foo/bar/foobar", 10, "foobar", "c:/foo/bar"); + + /* Absolute paths are not prepended with base */ + test_join_unrooted("/foo", 0, "/foo", "/asdf"); + test_join_unrooted("/foo/bar", 0, "/foo/bar", "/asdf"); + + /* Drive letter is given as root length on Windows */ + test_join_unrooted("c:/foo", 2, "c:/foo", "c:/asdf"); + test_join_unrooted("c:/foo/bar", 2, "c:/foo/bar", "c:/asdf"); + + /* Base is returned when it's provided and is the prefix */ + test_join_unrooted("c:/foo/bar/foobar", 6, "c:/foo/bar/foobar", "c:/foo"); + test_join_unrooted("c:/foo/bar/foobar", 10, "c:/foo/bar/foobar", "c:/foo/bar"); + + /* Trailing slash in the base is ignored */ + test_join_unrooted("c:/foo/bar/foobar", 6, "c:/foo/bar/foobar", "c:/foo/"); + + git_buf_free(&out); +} diff -Nru libgit2-0.20.0/tests/path/win32.c libgit2-0.22.2/tests/path/win32.c --- libgit2-0.20.0/tests/path/win32.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/path/win32.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,217 @@ + +#include "clar_libgit2.h" +#include "path.h" + +#ifdef GIT_WIN32 +#include "win32/path_w32.h" +#endif + +void test_utf8_to_utf16(const char *utf8_in, const wchar_t *utf16_expected) +{ +#ifdef GIT_WIN32 + git_win32_path path_utf16; + int path_utf16len; + + cl_assert((path_utf16len = git_win32_path_from_utf8(path_utf16, utf8_in)) >= 0); + cl_assert_equal_wcs(utf16_expected, path_utf16); + cl_assert_equal_i(wcslen(utf16_expected), path_utf16len); +#else + GIT_UNUSED(utf8_in); + GIT_UNUSED(utf16_expected); +#endif +} + +void test_path_win32__utf8_to_utf16(void) +{ +#ifdef GIT_WIN32 + test_utf8_to_utf16("C:\\", L"\\\\?\\C:\\"); + test_utf8_to_utf16("c:\\", L"\\\\?\\c:\\"); + test_utf8_to_utf16("C:/", L"\\\\?\\C:\\"); + test_utf8_to_utf16("c:/", L"\\\\?\\c:\\"); +#endif +} + +void test_path_win32__removes_trailing_slash(void) +{ +#ifdef GIT_WIN32 + test_utf8_to_utf16("C:\\Foo\\", L"\\\\?\\C:\\Foo"); + test_utf8_to_utf16("C:\\Foo\\\\", L"\\\\?\\C:\\Foo"); + test_utf8_to_utf16("C:\\Foo\\\\", L"\\\\?\\C:\\Foo"); + test_utf8_to_utf16("C:/Foo/", L"\\\\?\\C:\\Foo"); + test_utf8_to_utf16("C:/Foo///", L"\\\\?\\C:\\Foo"); +#endif +} + +void test_path_win32__squashes_multiple_slashes(void) +{ +#ifdef GIT_WIN32 + test_utf8_to_utf16("C:\\\\Foo\\Bar\\\\Foobar", L"\\\\?\\C:\\Foo\\Bar\\Foobar"); + test_utf8_to_utf16("C://Foo/Bar///Foobar", L"\\\\?\\C:\\Foo\\Bar\\Foobar"); +#endif +} + +void test_path_win32__unc(void) +{ +#ifdef GIT_WIN32 + test_utf8_to_utf16("\\\\server\\c$\\unc\\path", L"\\\\?\\UNC\\server\\c$\\unc\\path"); + test_utf8_to_utf16("//server/git/style/unc/path", L"\\\\?\\UNC\\server\\git\\style\\unc\\path"); +#endif +} + +void test_path_win32__honors_max_path(void) +{ +#ifdef GIT_WIN32 + git_win32_path path_utf16; + + test_utf8_to_utf16("C:\\This path is 259 chars and is the max length in windows\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij", + L"\\\\?\\C:\\This path is 259 chars and is the max length in windows\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij"); + test_utf8_to_utf16("\\\\unc\\paths may also be 259 characters including the server\\123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij", + L"\\\\?\\UNC\\unc\\paths may also be 259 characters including the server\\123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij"); + + cl_check_fail(git_win32_path_from_utf8(path_utf16, "C:\\This path is 260 chars and is sadly too long for windows\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij")); + cl_check_fail(git_win32_path_from_utf8(path_utf16, "\\\\unc\\paths are also bound by 260 character restrictions\\including the server name portion\\bcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij")); +#endif +} + +void test_path_win32__dot_and_dotdot(void) +{ +#ifdef GIT_WIN32 + test_utf8_to_utf16("C:\\Foo\\..\\Foobar", L"\\\\?\\C:\\Foobar"); + test_utf8_to_utf16("C:\\Foo\\Bar\\..\\Foobar", L"\\\\?\\C:\\Foo\\Foobar"); + test_utf8_to_utf16("C:\\Foo\\Bar\\..\\Foobar\\..", L"\\\\?\\C:\\Foo"); + test_utf8_to_utf16("C:\\Foobar\\..", L"\\\\?\\C:\\"); + test_utf8_to_utf16("C:/Foo/Bar/../Foobar", L"\\\\?\\C:\\Foo\\Foobar"); + test_utf8_to_utf16("C:/Foo/Bar/../Foobar/../Asdf/", L"\\\\?\\C:\\Foo\\Asdf"); + test_utf8_to_utf16("C:/Foo/Bar/../Foobar/..", L"\\\\?\\C:\\Foo"); + test_utf8_to_utf16("C:/Foo/..", L"\\\\?\\C:\\"); + + test_utf8_to_utf16("C:\\Foo\\Bar\\.\\Foobar", L"\\\\?\\C:\\Foo\\Bar\\Foobar"); + test_utf8_to_utf16("C:\\.\\Foo\\.\\Bar\\.\\Foobar\\.\\", L"\\\\?\\C:\\Foo\\Bar\\Foobar"); + test_utf8_to_utf16("C:/Foo/Bar/./Foobar", L"\\\\?\\C:\\Foo\\Bar\\Foobar"); + test_utf8_to_utf16("C:/Foo/../Bar/./Foobar/../", L"\\\\?\\C:\\Bar"); + + test_utf8_to_utf16("C:\\Foo\\..\\..\\Bar", L"\\\\?\\C:\\Bar"); +#endif +} + +void test_path_win32__absolute_from_no_drive_letter(void) +{ +#ifdef GIT_WIN32 + test_utf8_to_utf16("\\Foo", L"\\\\?\\C:\\Foo"); + test_utf8_to_utf16("\\Foo\\Bar", L"\\\\?\\C:\\Foo\\Bar"); + test_utf8_to_utf16("/Foo/Bar", L"\\\\?\\C:\\Foo\\Bar"); +#endif +} + +void test_path_win32__absolute_from_relative(void) +{ +#ifdef GIT_WIN32 + char cwd_backup[MAX_PATH]; + + cl_must_pass(p_getcwd(cwd_backup, MAX_PATH)); + cl_must_pass(p_chdir("C:/")); + + test_utf8_to_utf16("Foo", L"\\\\?\\C:\\Foo"); + test_utf8_to_utf16("..\\..\\Foo", L"\\\\?\\C:\\Foo"); + test_utf8_to_utf16("Foo\\..", L"\\\\?\\C:\\"); + test_utf8_to_utf16("Foo\\..\\..", L"\\\\?\\C:\\"); + test_utf8_to_utf16("", L"\\\\?\\C:\\"); + + cl_must_pass(p_chdir("C:/Windows")); + + test_utf8_to_utf16("Foo", L"\\\\?\\C:\\Windows\\Foo"); + test_utf8_to_utf16("Foo\\Bar", L"\\\\?\\C:\\Windows\\Foo\\Bar"); + test_utf8_to_utf16("..\\Foo", L"\\\\?\\C:\\Foo"); + test_utf8_to_utf16("Foo\\..\\Bar", L"\\\\?\\C:\\Windows\\Bar"); + test_utf8_to_utf16("", L"\\\\?\\C:\\Windows"); + + cl_must_pass(p_chdir(cwd_backup)); +#endif +} + +void test_canonicalize(const wchar_t *in, const wchar_t *expected) +{ +#ifdef GIT_WIN32 + git_win32_path canonical; + + cl_assert(wcslen(in) < MAX_PATH); + wcscpy(canonical, in); + + cl_must_pass(git_win32_path_canonicalize(canonical)); + cl_assert_equal_wcs(expected, canonical); +#else + GIT_UNUSED(in); + GIT_UNUSED(expected); +#endif +} + +void test_path_win32__canonicalize(void) +{ +#ifdef GIT_WIN32 + test_canonicalize(L"C:\\Foo\\Bar", L"C:\\Foo\\Bar"); + test_canonicalize(L"C:\\Foo\\", L"C:\\Foo"); + test_canonicalize(L"C:\\Foo\\\\", L"C:\\Foo"); + test_canonicalize(L"C:\\Foo\\..\\Bar", L"C:\\Bar"); + test_canonicalize(L"C:\\Foo\\..\\..\\Bar", L"C:\\Bar"); + test_canonicalize(L"C:\\Foo\\..\\..\\..\\..\\", L"C:\\"); + test_canonicalize(L"C:/Foo/Bar", L"C:\\Foo\\Bar"); + test_canonicalize(L"C:/", L"C:\\"); + + test_canonicalize(L"Foo\\\\Bar\\\\Asdf\\\\", L"Foo\\Bar\\Asdf"); + test_canonicalize(L"Foo\\\\Bar\\\\..\\\\Asdf\\", L"Foo\\Asdf"); + test_canonicalize(L"Foo\\\\Bar\\\\.\\\\Asdf\\", L"Foo\\Bar\\Asdf"); + test_canonicalize(L"Foo\\\\..\\Bar\\\\.\\\\Asdf\\", L"Bar\\Asdf"); + test_canonicalize(L"\\", L""); + test_canonicalize(L"", L""); + test_canonicalize(L"Foo\\..\\..\\..\\..", L""); + test_canonicalize(L"..\\..\\..\\..", L""); + test_canonicalize(L"\\..\\..\\..\\..", L""); + + test_canonicalize(L"\\\\?\\C:\\Foo\\Bar", L"\\\\?\\C:\\Foo\\Bar"); + test_canonicalize(L"\\\\?\\C:\\Foo\\Bar\\", L"\\\\?\\C:\\Foo\\Bar"); + test_canonicalize(L"\\\\?\\C:\\\\Foo\\.\\Bar\\\\..\\", L"\\\\?\\C:\\Foo"); + test_canonicalize(L"\\\\?\\C:\\\\", L"\\\\?\\C:\\"); + test_canonicalize(L"//?/C:/", L"\\\\?\\C:\\"); + test_canonicalize(L"//?/C:/../../Foo/", L"\\\\?\\C:\\Foo"); + test_canonicalize(L"//?/C:/Foo/../../", L"\\\\?\\C:\\"); + + test_canonicalize(L"\\\\?\\UNC\\server\\C$\\folder", L"\\\\?\\UNC\\server\\C$\\folder"); + test_canonicalize(L"\\\\?\\UNC\\server\\C$\\folder\\", L"\\\\?\\UNC\\server\\C$\\folder"); + test_canonicalize(L"\\\\?\\UNC\\server\\C$\\folder\\", L"\\\\?\\UNC\\server\\C$\\folder"); + test_canonicalize(L"\\\\?\\UNC\\server\\C$\\folder\\..\\..\\..\\..\\share\\", L"\\\\?\\UNC\\server\\share"); + + test_canonicalize(L"\\\\server\\share", L"\\\\server\\share"); + test_canonicalize(L"\\\\server\\share\\", L"\\\\server\\share"); + test_canonicalize(L"\\\\server\\share\\\\foo\\\\bar", L"\\\\server\\share\\foo\\bar"); + test_canonicalize(L"\\\\server\\\\share\\\\foo\\\\bar", L"\\\\server\\share\\foo\\bar"); + test_canonicalize(L"\\\\server\\share\\..\\foo", L"\\\\server\\foo"); + test_canonicalize(L"\\\\server\\..\\..\\share\\.\\foo", L"\\\\server\\share\\foo"); +#endif +} + +void test_path_win32__8dot3_name(void) +{ +#ifdef GIT_WIN32 + char *shortname; + + if (!cl_sandbox_supports_8dot3()) + clar__skip(); + + /* Some guaranteed short names */ + cl_assert_equal_s("PROGRA~1", (shortname = git_win32_path_8dot3_name("C:\\Program Files"))); + git__free(shortname); + + cl_assert_equal_s("WINDOWS", (shortname = git_win32_path_8dot3_name("C:\\WINDOWS"))); + git__free(shortname); + + /* Create some predictible short names */ + cl_must_pass(p_mkdir(".foo", 0777)); + cl_assert_equal_s("FOO~1", (shortname = git_win32_path_8dot3_name(".foo"))); + git__free(shortname); + + cl_git_write2file("bar~1", "foobar\n", 7, O_RDWR|O_CREAT, 0666); + cl_must_pass(p_mkdir(".bar", 0777)); + cl_assert_equal_s("BAR~2", (shortname = git_win32_path_8dot3_name(".bar"))); + git__free(shortname); +#endif +} diff -Nru libgit2-0.20.0/tests/rebase/abort.c libgit2-0.22.2/tests/rebase/abort.c --- libgit2-0.20.0/tests/rebase/abort.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/rebase/abort.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,158 @@ +#include "clar_libgit2.h" +#include "git2/rebase.h" +#include "merge.h" +#include "posix.h" +#include "annotated_commit.h" + +#include + +static git_repository *repo; + +// Fixture setup and teardown +void test_rebase_abort__initialize(void) +{ + repo = cl_git_sandbox_init("rebase"); +} + +void test_rebase_abort__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +static void test_abort(git_annotated_commit *branch, git_annotated_commit *onto) +{ + git_rebase *rebase; + git_reference *head_ref, *branch_ref = NULL; + git_signature *signature; + git_status_list *statuslist; + git_reflog *reflog; + const git_reflog_entry *reflog_entry; + + cl_git_pass(git_rebase_open(&rebase, repo)); + cl_git_pass(git_signature_new(&signature, "Rebaser", "rebaser@example.com", 1404157834, -400)); + cl_git_pass(git_rebase_abort(rebase, signature)); + + cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo)); + + /* Make sure the refs are updated appropriately */ + cl_git_pass(git_reference_lookup(&head_ref, repo, "HEAD")); + + if (branch->ref_name == NULL) + cl_assert_equal_oid(git_annotated_commit_id(branch), git_reference_target(head_ref)); + else { + cl_assert_equal_s("refs/heads/beef", git_reference_symbolic_target(head_ref)); + cl_git_pass(git_reference_lookup(&branch_ref, repo, git_reference_symbolic_target(head_ref))); + cl_assert_equal_oid(git_annotated_commit_id(branch), git_reference_target(branch_ref)); + } + + git_status_list_new(&statuslist, repo, NULL); + cl_assert_equal_i(0, git_status_list_entrycount(statuslist)); + git_status_list_free(statuslist); + + /* Make sure the reflogs are updated appropriately */ + cl_git_pass(git_reflog_read(&reflog, repo, "HEAD")); + + cl_assert(reflog_entry = git_reflog_entry_byindex(reflog, 0)); + cl_assert_equal_oid(git_annotated_commit_id(onto), git_reflog_entry_id_old(reflog_entry)); + cl_assert_equal_oid(git_annotated_commit_id(branch), git_reflog_entry_id_new(reflog_entry)); + cl_assert_equal_s("rebase: aborting", git_reflog_entry_message(reflog_entry)); + + git_reflog_free(reflog); + git_reference_free(head_ref); + git_reference_free(branch_ref); + git_signature_free(signature); + git_rebase_free(rebase); +} + +void test_rebase_abort__merge(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *onto_ref; + git_signature *signature; + git_annotated_commit *branch_head, *onto_head; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); + cl_git_pass(git_reference_lookup(&onto_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(&onto_head, repo, onto_ref)); + + cl_git_pass(git_signature_new(&signature, "Rebaser", "rebaser@example.com", 1404157834, -400)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, NULL, onto_head, signature, NULL)); + cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo)); + + test_abort(branch_head, onto_head); + + git_signature_free(signature); + + git_annotated_commit_free(branch_head); + git_annotated_commit_free(onto_head); + + git_reference_free(branch_ref); + git_reference_free(onto_ref); + git_rebase_free(rebase); +} + +void test_rebase_abort__detached_head(void) +{ + git_rebase *rebase; + git_oid branch_id; + git_reference *onto_ref; + git_signature *signature; + git_annotated_commit *branch_head, *onto_head; + + git_oid_fromstr(&branch_id, "b146bd7608eac53d9bf9e1a6963543588b555c64"); + cl_git_pass(git_reference_lookup(&onto_ref, repo, "refs/heads/master")); + + cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id)); + cl_git_pass(git_annotated_commit_from_ref(&onto_head, repo, onto_ref)); + + cl_git_pass(git_signature_new(&signature, "Rebaser", "rebaser@example.com", 1404157834, -400)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, NULL, onto_head, signature, NULL)); + cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo)); + + test_abort(branch_head, onto_head); + + git_signature_free(signature); + + git_annotated_commit_free(branch_head); + git_annotated_commit_free(onto_head); + + git_reference_free(onto_ref); + git_rebase_free(rebase); +} + +void test_rebase_abort__old_style_head_file(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *onto_ref; + git_signature *signature; + git_annotated_commit *branch_head, *onto_head; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); + cl_git_pass(git_reference_lookup(&onto_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(&onto_head, repo, onto_ref)); + + cl_git_pass(git_signature_new(&signature, "Rebaser", "rebaser@example.com", 1404157834, -400)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, NULL, onto_head, signature, NULL)); + cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo)); + + p_rename("rebase-merge/.git/rebase-merge/orig-head", + "rebase-merge/.git/rebase-merge/head"); + + test_abort(branch_head, onto_head); + + git_signature_free(signature); + + git_annotated_commit_free(branch_head); + git_annotated_commit_free(onto_head); + + git_reference_free(branch_ref); + git_reference_free(onto_ref); + git_rebase_free(rebase); +} diff -Nru libgit2-0.20.0/tests/rebase/iterator.c libgit2-0.22.2/tests/rebase/iterator.c --- libgit2-0.20.0/tests/rebase/iterator.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/rebase/iterator.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,108 @@ +#include "clar_libgit2.h" +#include "git2/rebase.h" +#include "posix.h" + +#include + +static git_repository *repo; +static git_index *_index; +static git_signature *signature; + +// Fixture setup and teardown +void test_rebase_iterator__initialize(void) +{ + repo = cl_git_sandbox_init("rebase"); + cl_git_pass(git_repository_index(&_index, repo)); + cl_git_pass(git_signature_now(&signature, "Rebaser", "rebaser@rebaser.rb")); +} + +void test_rebase_iterator__cleanup(void) +{ + git_signature_free(signature); + git_index_free(_index); + cl_git_sandbox_cleanup(); +} + +static void test_operations(git_rebase *rebase, size_t expected_current) +{ + size_t i, expected_count = 5; + git_oid expected_oid[5]; + git_rebase_operation *operation; + + git_oid_fromstr(&expected_oid[0], "da9c51a23d02d931a486f45ad18cda05cf5d2b94"); + git_oid_fromstr(&expected_oid[1], "8d1f13f93c4995760ac07d129246ac1ff64c0be9"); + git_oid_fromstr(&expected_oid[2], "3069cc907e6294623e5917ef6de663928c1febfb"); + git_oid_fromstr(&expected_oid[3], "588e5d2f04d49707fe4aab865e1deacaf7ef6787"); + git_oid_fromstr(&expected_oid[4], "b146bd7608eac53d9bf9e1a6963543588b555c64"); + + cl_assert_equal_i(expected_count, git_rebase_operation_entrycount(rebase)); + cl_assert_equal_i(expected_current, git_rebase_operation_current(rebase)); + + for (i = 0; i < expected_count; i++) { + operation = git_rebase_operation_byindex(rebase, i); + cl_assert_equal_i(GIT_REBASE_OPERATION_PICK, operation->type); + cl_assert_equal_oid(&expected_oid[i], &operation->id); + } +} + +void test_rebase_iterator__iterates(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_rebase_operation *rebase_operation; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + git_oid commit_id; + int error; + + checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); + 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)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, signature, NULL)); + test_operations(rebase, 0); + git_rebase_free(rebase); + + cl_git_pass(git_rebase_open(&rebase, repo)); + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, + NULL, NULL)); + test_operations(rebase, 0); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, + NULL, NULL)); + test_operations(rebase, 1); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, + NULL, NULL)); + test_operations(rebase, 2); + + git_rebase_free(rebase); + cl_git_pass(git_rebase_open(&rebase, repo)); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, + NULL, NULL)); + test_operations(rebase, 3); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, + NULL, NULL)); + test_operations(rebase, 4); + + cl_git_fail(error = git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_assert_equal_i(GIT_ITEROVER, error); + test_operations(rebase, 4); + + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_rebase_free(rebase); +} diff -Nru libgit2-0.20.0/tests/rebase/merge.c libgit2-0.22.2/tests/rebase/merge.c --- libgit2-0.20.0/tests/rebase/merge.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/rebase/merge.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,499 @@ +#include "clar_libgit2.h" +#include "git2/rebase.h" +#include "posix.h" +#include "signature.h" + +#include + +static git_repository *repo; +static git_signature *signature; + +static void set_core_autocrlf_to(git_repository *repo, bool value) +{ + git_config *cfg; + + cl_git_pass(git_repository_config(&cfg, repo)); + cl_git_pass(git_config_set_bool(cfg, "core.autocrlf", value)); + + git_config_free(cfg); +} + +// Fixture setup and teardown +void test_rebase_merge__initialize(void) +{ + repo = cl_git_sandbox_init("rebase"); + cl_git_pass(git_signature_new(&signature, + "Rebaser", "rebaser@rebaser.rb", 1405694510, 0)); + + set_core_autocrlf_to(repo, false); +} + +void test_rebase_merge__cleanup(void) +{ + git_signature_free(signature); + cl_git_sandbox_cleanup(); +} + +void test_rebase_merge__next(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_rebase_operation *rebase_operation; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + git_status_list *status_list; + const git_status_entry *status_entry; + git_oid pick_id, file1_id; + + checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); + 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)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, signature, NULL)); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + + git_oid_fromstr(&pick_id, "da9c51a23d02d931a486f45ad18cda05cf5d2b94"); + + cl_assert_equal_i(GIT_REBASE_OPERATION_PICK, rebase_operation->type); + cl_assert_equal_oid(&pick_id, &rebase_operation->id); + cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94\n", 41, "rebase/.git/rebase-merge/current"); + cl_assert_equal_file("1\n", 2, "rebase/.git/rebase-merge/msgnum"); + + cl_git_pass(git_status_list_new(&status_list, repo, NULL)); + cl_assert_equal_i(1, git_status_list_entrycount(status_list)); + cl_assert(status_entry = git_status_byindex(status_list, 0)); + + cl_assert_equal_s("beef.txt", status_entry->head_to_index->new_file.path); + + git_oid_fromstr(&file1_id, "8d95ea62e621f1d38d230d9e7d206e41096d76af"); + cl_assert_equal_oid(&file1_id, &status_entry->head_to_index->new_file.id); + + git_status_list_free(status_list); + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_rebase_free(rebase); +} + +void test_rebase_merge__next_with_conflicts(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_rebase_operation *rebase_operation; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + git_status_list *status_list; + const git_status_entry *status_entry; + git_oid pick_id; + + const char *expected_merge = +"ASPARAGUS SOUP.\n" +"\n" +"<<<<<<< master\n" +"TAKE FOUR LARGE BUNCHES of asparagus, scrape it nicely, cut off one inch\n" +"OF THE TOPS, and lay them in water, chop the stalks and put them on the\n" +"FIRE WITH A PIECE OF BACON, a large onion cut up, and pepper and salt;\n" +"ADD TWO QUARTS OF WATER, boil them till the stalks are quite soft, then\n" +"PULP THEM THROUGH A SIEVE, and strain the water to it, which must be put\n" +"=======\n" +"Take four large bunches of asparagus, scrape it nicely, CUT OFF ONE INCH\n" +"of the tops, and lay them in water, chop the stalks and PUT THEM ON THE\n" +"fire with a piece of bacon, a large onion cut up, and pepper and salt;\n" +"add two quarts of water, boil them till the stalks are quite soft, then\n" +"pulp them through a sieve, and strain the water to it, which must be put\n" +">>>>>>> Conflicting modification 1 to asparagus\n" +"back in the pot; put into it a chicken cut up, with the tops of\n" +"asparagus which had been laid by, boil it until these last articles are\n" +"sufficiently done, thicken with flour, butter and milk, and serve it up.\n"; + + checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + + 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)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, signature, NULL)); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + + git_oid_fromstr(&pick_id, "33f915f9e4dbd9f4b24430e48731a59b45b15500"); + + cl_assert_equal_i(GIT_REBASE_OPERATION_PICK, rebase_operation->type); + cl_assert_equal_oid(&pick_id, &rebase_operation->id); + cl_assert_equal_file("33f915f9e4dbd9f4b24430e48731a59b45b15500\n", 41, "rebase/.git/rebase-merge/current"); + cl_assert_equal_file("1\n", 2, "rebase/.git/rebase-merge/msgnum"); + + cl_git_pass(git_status_list_new(&status_list, repo, NULL)); + cl_assert_equal_i(1, git_status_list_entrycount(status_list)); + cl_assert(status_entry = git_status_byindex(status_list, 0)); + + cl_assert_equal_s("asparagus.txt", status_entry->head_to_index->new_file.path); + + cl_assert_equal_file(expected_merge, strlen(expected_merge), "rebase/asparagus.txt"); + + git_status_list_free(status_list); + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_rebase_free(rebase); +} + +void test_rebase_merge__next_stops_with_iterover(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_rebase_operation *rebase_operation; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + git_oid commit_id; + int error; + + checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); + 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)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, signature, NULL)); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, + NULL, NULL)); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, + NULL, NULL)); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, + NULL, NULL)); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, + NULL, NULL)); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, + NULL, NULL)); + + cl_git_fail(error = git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_assert_equal_i(GIT_ITEROVER, error); + + cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end"); + cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/msgnum"); + + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_rebase_free(rebase); +} + +void test_rebase_merge__commit(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_rebase_operation *rebase_operation; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + git_oid commit_id, tree_id, parent_id; + git_signature *author; + git_commit *commit; + git_reflog *reflog; + const git_reflog_entry *reflog_entry; + + checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); + 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)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, signature, NULL)); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, + NULL, NULL)); + + cl_git_pass(git_commit_lookup(&commit, repo, &commit_id)); + + git_oid_fromstr(&parent_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00"); + cl_assert_equal_i(1, git_commit_parentcount(commit)); + cl_assert_equal_oid(&parent_id, git_commit_parent_id(commit, 0)); + + git_oid_fromstr(&tree_id, "4461379789c777d2a6c1f2ee0e9d6c86731b9992"); + cl_assert_equal_oid(&tree_id, git_commit_tree_id(commit)); + + cl_assert_equal_s(NULL, git_commit_message_encoding(commit)); + cl_assert_equal_s("Modification 1 to beef\n", git_commit_message(commit)); + + cl_git_pass(git_signature_new(&author, + "Edward Thomson", "ethomson@edwardthomson.com", 1405621769, 0-(4*60))); + cl_assert(git_signature__equal(author, git_commit_author(commit))); + + cl_assert(git_signature__equal(signature, git_commit_committer(commit))); + + /* Make sure the reflogs are updated appropriately */ + cl_git_pass(git_reflog_read(&reflog, repo, "HEAD")); + cl_assert(reflog_entry = git_reflog_entry_byindex(reflog, 0)); + cl_assert_equal_oid(&parent_id, git_reflog_entry_id_old(reflog_entry)); + cl_assert_equal_oid(&commit_id, git_reflog_entry_id_new(reflog_entry)); + cl_assert_equal_s("rebase: Modification 1 to beef", git_reflog_entry_message(reflog_entry)); + + git_reflog_free(reflog); + git_signature_free(author); + git_commit_free(commit); + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_rebase_free(rebase); +} + +void test_rebase_merge__commit_updates_rewritten(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_rebase_operation *rebase_operation; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + git_oid commit_id; + + checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); + 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)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, signature, NULL)); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, + NULL, NULL)); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, + NULL, NULL)); + + cl_assert_equal_file( + "da9c51a23d02d931a486f45ad18cda05cf5d2b94 776e4c48922799f903f03f5f6e51da8b01e4cce0\n" + "8d1f13f93c4995760ac07d129246ac1ff64c0be9 ba1f9b4fd5cf8151f7818be2111cc0869f1eb95a\n", + 164, "rebase/.git/rebase-merge/rewritten"); + + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_rebase_free(rebase); +} + +void test_rebase_merge__commit_drops_already_applied(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_rebase_operation *rebase_operation; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + git_oid commit_id; + int error; + + checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); + cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/green_pea")); + + 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)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, signature, NULL)); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_git_fail(error = git_rebase_commit(&commit_id, rebase, NULL, signature, + NULL, NULL)); + + cl_assert_equal_i(GIT_EAPPLIED, error); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, + NULL, NULL)); + + cl_assert_equal_file( + "8d1f13f93c4995760ac07d129246ac1ff64c0be9 2ac4fb7b74c1287f6c792acad759e1ec01e18dae\n", + 82, "rebase/.git/rebase-merge/rewritten"); + + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_rebase_free(rebase); +} + +void test_rebase_merge__finish(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref, *head_ref; + git_annotated_commit *branch_head, *upstream_head; + git_rebase_operation *rebase_operation; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + git_oid commit_id; + git_reflog *reflog; + const git_reflog_entry *reflog_entry; + int error; + + checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/gravy")); + cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/veal")); + + 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)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, signature, NULL)); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, + NULL, NULL)); + + cl_git_fail(error = git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_assert_equal_i(GIT_ITEROVER, error); + + cl_git_pass(git_rebase_finish(rebase, signature, NULL)); + + cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo)); + + cl_git_pass(git_reference_lookup(&head_ref, repo, "HEAD")); + cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head_ref)); + cl_assert_equal_s("refs/heads/gravy", git_reference_symbolic_target(head_ref)); + + /* Make sure the reflogs are updated appropriately */ + cl_git_pass(git_reflog_read(&reflog, repo, "HEAD")); + cl_assert(reflog_entry = git_reflog_entry_byindex(reflog, 0)); + cl_assert_equal_oid(&commit_id, git_reflog_entry_id_old(reflog_entry)); + cl_assert_equal_oid(&commit_id, git_reflog_entry_id_new(reflog_entry)); + cl_assert_equal_s("rebase finished: returning to refs/heads/gravy", git_reflog_entry_message(reflog_entry)); + git_reflog_free(reflog); + + cl_git_pass(git_reflog_read(&reflog, repo, "refs/heads/gravy")); + cl_assert(reflog_entry = git_reflog_entry_byindex(reflog, 0)); + cl_assert_equal_oid(git_annotated_commit_id(branch_head), git_reflog_entry_id_old(reflog_entry)); + cl_assert_equal_oid(&commit_id, git_reflog_entry_id_new(reflog_entry)); + cl_assert_equal_s("rebase finished: refs/heads/gravy onto f87d14a4a236582a0278a916340a793714256864", git_reflog_entry_message(reflog_entry)); + + git_reflog_free(reflog); + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(head_ref); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_rebase_free(rebase); +} + +static void test_copy_note( + const git_rebase_options *opts, + bool should_exist) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_commit *branch_commit; + git_rebase_operation *rebase_operation; + git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT; + git_oid note_id, commit_id; + git_note *note = NULL; + int error; + + checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE; + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/gravy")); + cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/veal")); + + 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)); + + cl_git_pass(git_reference_peel((git_object **)&branch_commit, + branch_ref, GIT_OBJ_COMMIT)); + + /* Add a note to a commit */ + cl_git_pass(git_note_create(¬e_id, repo, "refs/notes/test", + git_commit_author(branch_commit), git_commit_committer(branch_commit), + git_commit_id(branch_commit), + "This is a commit note.", 0)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, signature, opts)); + + cl_git_pass(git_rebase_next(&rebase_operation, rebase, &checkout_opts)); + cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, + NULL, NULL)); + + cl_git_pass(git_rebase_finish(rebase, signature, opts)); + + cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo)); + + if (should_exist) { + cl_git_pass(git_note_read(¬e, repo, "refs/notes/test", &commit_id)); + cl_assert_equal_s("This is a commit note.", git_note_message(note)); + } else { + cl_git_fail(error = + git_note_read(¬e, repo, "refs/notes/test", &commit_id)); + cl_assert_equal_i(GIT_ENOTFOUND, error); + } + + git_note_free(note); + git_commit_free(branch_commit); + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_rebase_free(rebase); +} + +void test_rebase_merge__copy_notes_off_by_default(void) +{ + test_copy_note(NULL, 0); +} + +void test_rebase_merge__copy_notes_specified_in_options(void) +{ + git_rebase_options opts = GIT_REBASE_OPTIONS_INIT; + opts.rewrite_notes_ref = "refs/notes/test"; + + test_copy_note(&opts, 1); +} + +void test_rebase_merge__copy_notes_specified_in_config(void) +{ + git_config *config; + + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_set_string(config, + "notes.rewriteRef", "refs/notes/test")); + + test_copy_note(NULL, 1); +} + +void test_rebase_merge__copy_notes_disabled_in_config(void) +{ + git_config *config; + + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_set_bool(config, "notes.rewrite.rebase", 0)); + cl_git_pass(git_config_set_string(config, + "notes.rewriteRef", "refs/notes/test")); + + test_copy_note(NULL, 0); +} + diff -Nru libgit2-0.20.0/tests/rebase/setup.c libgit2-0.22.2/tests/rebase/setup.c --- libgit2-0.20.0/tests/rebase/setup.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/rebase/setup.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,342 @@ +#include "clar_libgit2.h" +#include "git2/rebase.h" +#include "posix.h" + +#include + +static git_repository *repo; +static git_index *_index; +static git_signature *signature; + +// Fixture setup and teardown +void test_rebase_setup__initialize(void) +{ + repo = cl_git_sandbox_init("rebase"); + cl_git_pass(git_repository_index(&_index, repo)); + cl_git_pass(git_signature_now(&signature, "Rebaser", "rebaser@rebaser.rb")); +} + +void test_rebase_setup__cleanup(void) +{ + git_signature_free(signature); + git_index_free(_index); + cl_git_sandbox_cleanup(); +} + +/* git checkout beef ; git rebase --merge master + * git checkout beef ; git rebase --merge master */ +void test_rebase_setup__blocked_when_in_progress(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + + cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo)); + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); + 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)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, signature, NULL)); + git_rebase_free(rebase); + + cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo)); + + cl_git_fail(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, signature, NULL)); + + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); +} + +/* git checkout beef ; git rebase --merge master */ +void test_rebase_setup__merge(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_reference *head; + git_commit *head_commit; + git_oid head_id; + + cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo)); + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); + 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)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, signature, NULL)); + + cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo)); + + git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00"); + cl_git_pass(git_repository_head(&head, repo)); + cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT)); + cl_assert_equal_oid(&head_id, git_commit_id(head_commit)); + + cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/ORIG_HEAD"); + + cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94\n", 41, "rebase/.git/rebase-merge/cmt.1"); + cl_assert_equal_file("8d1f13f93c4995760ac07d129246ac1ff64c0be9\n", 41, "rebase/.git/rebase-merge/cmt.2"); + cl_assert_equal_file("3069cc907e6294623e5917ef6de663928c1febfb\n", 41, "rebase/.git/rebase-merge/cmt.3"); + cl_assert_equal_file("588e5d2f04d49707fe4aab865e1deacaf7ef6787\n", 41, "rebase/.git/rebase-merge/cmt.4"); + cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/cmt.5"); + cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end"); + cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto"); + cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name"); + cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/orig-head"); + + git_commit_free(head_commit); + git_reference_free(head); + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_rebase_free(rebase); +} + +/* git checkout beef && git rebase --merge --root --onto master */ +void test_rebase_setup__merge_root(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *onto_ref; + git_annotated_commit *branch_head, *onto_head; + git_reference *head; + git_commit *head_commit; + git_oid head_id; + + cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo)); + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); + cl_git_pass(git_reference_lookup(&onto_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(&onto_head, repo, onto_ref)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, NULL, onto_head, signature, NULL)); + + git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00"); + cl_git_pass(git_repository_head(&head, repo)); + cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT)); + cl_assert_equal_oid(&head_id, git_commit_id(head_commit)); + + cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/ORIG_HEAD"); + + cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo)); + + cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94\n", 41, "rebase/.git/rebase-merge/cmt.1"); + cl_assert_equal_file("8d1f13f93c4995760ac07d129246ac1ff64c0be9\n", 41, "rebase/.git/rebase-merge/cmt.2"); + cl_assert_equal_file("3069cc907e6294623e5917ef6de663928c1febfb\n", 41, "rebase/.git/rebase-merge/cmt.3"); + cl_assert_equal_file("588e5d2f04d49707fe4aab865e1deacaf7ef6787\n", 41, "rebase/.git/rebase-merge/cmt.4"); + cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/cmt.5"); + cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end"); + cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto"); + cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name"); + cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/orig-head"); + + git_commit_free(head_commit); + git_reference_free(head); + git_annotated_commit_free(branch_head); + git_annotated_commit_free(onto_head); + git_reference_free(branch_ref); + git_reference_free(onto_ref); + git_rebase_free(rebase); +} + +/* git checkout gravy && git rebase --merge --onto master veal */ +void test_rebase_setup__merge_onto_and_upstream(void) +{ + git_rebase *rebase; + git_reference *branch1_ref, *branch2_ref, *onto_ref; + git_annotated_commit *branch1_head, *branch2_head, *onto_head; + git_reference *head; + git_commit *head_commit; + git_oid head_id; + + cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo)); + + cl_git_pass(git_reference_lookup(&branch1_ref, repo, "refs/heads/gravy")); + cl_git_pass(git_reference_lookup(&branch2_ref, repo, "refs/heads/veal")); + cl_git_pass(git_reference_lookup(&onto_ref, repo, "refs/heads/master")); + + cl_git_pass(git_annotated_commit_from_ref(&branch1_head, repo, branch1_ref)); + cl_git_pass(git_annotated_commit_from_ref(&branch2_head, repo, branch2_ref)); + cl_git_pass(git_annotated_commit_from_ref(&onto_head, repo, onto_ref)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch1_head, branch2_head, onto_head, signature, NULL)); + + git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00"); + cl_git_pass(git_repository_head(&head, repo)); + cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT)); + cl_assert_equal_oid(&head_id, git_commit_id(head_commit)); + + cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/ORIG_HEAD"); + + cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo)); + + cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/rebase-merge/cmt.1"); + cl_assert_equal_file("1\n", 2, "rebase/.git/rebase-merge/end"); + cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto"); + cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name"); + cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/rebase-merge/orig-head"); + + git_commit_free(head_commit); + git_reference_free(head); + git_annotated_commit_free(branch1_head); + git_annotated_commit_free(branch2_head); + git_annotated_commit_free(onto_head); + git_reference_free(branch1_ref); + git_reference_free(branch2_ref); + git_reference_free(onto_ref); + git_rebase_free(rebase); +} + +/* Ensure merge commits are dropped in a rebase */ +/* git checkout veal && git rebase --merge master */ +void test_rebase_setup__branch_with_merges(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_reference *head; + git_commit *head_commit; + git_oid head_id; + + cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo)); + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/veal")); + 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)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, signature, NULL)); + + cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo)); + + git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00"); + cl_git_pass(git_repository_head(&head, repo)); + cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT)); + cl_assert_equal_oid(&head_id, git_commit_id(head_commit)); + + cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/ORIG_HEAD"); + + cl_assert_equal_file("4bed71df7017283cac61bbf726197ad6a5a18b84\n", 41, "rebase/.git/rebase-merge/cmt.1"); + cl_assert_equal_file("2aa3ce842094e08ebac152b3d6d5b0fff39f9c6e\n", 41, "rebase/.git/rebase-merge/cmt.2"); + cl_assert_equal_file("3e8989b5a16d5258c935d998ef0e6bb139cc4757\n", 41, "rebase/.git/rebase-merge/cmt.3"); + cl_assert_equal_file("4cacc6f6e740a5bc64faa33e04b8ef0733d8a127\n", 41, "rebase/.git/rebase-merge/cmt.4"); + cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/rebase-merge/cmt.5"); + cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end"); + cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto"); + cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name"); + cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/rebase-merge/orig-head"); + + git_commit_free(head_commit); + git_reference_free(head); + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_rebase_free(rebase); +} + +/* git checkout barley && git rebase --merge master */ +void test_rebase_setup__orphan_branch(void) +{ + git_rebase *rebase; + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + git_reference *head; + git_commit *head_commit; + git_oid head_id; + + cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo)); + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/barley")); + 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)); + + cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, signature, NULL)); + + cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo)); + + git_oid_fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00"); + cl_git_pass(git_repository_head(&head, repo)); + cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJ_COMMIT)); + cl_assert_equal_oid(&head_id, git_commit_id(head_commit)); + + cl_assert_equal_file("12c084412b952396962eb420716df01022b847cc\n", 41, "rebase/.git/ORIG_HEAD"); + + cl_assert_equal_file("aa4c42aecdfc7cd989bbc3209934ea7cda3f4d88\n", 41, "rebase/.git/rebase-merge/cmt.1"); + cl_assert_equal_file("e4f809f826c1a9fc929874bc0e4644dd2f2a1af4\n", 41, "rebase/.git/rebase-merge/cmt.2"); + cl_assert_equal_file("9539b2cc291d6a6b1b266df8474d31fdd344dd79\n", 41, "rebase/.git/rebase-merge/cmt.3"); + cl_assert_equal_file("013cc32d341bab0e6f039f50f153c18986f16c58\n", 41, "rebase/.git/rebase-merge/cmt.4"); + cl_assert_equal_file("12c084412b952396962eb420716df01022b847cc\n", 41, "rebase/.git/rebase-merge/cmt.5"); + cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end"); + cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto"); + cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name"); + cl_assert_equal_file("12c084412b952396962eb420716df01022b847cc\n", 41, "rebase/.git/rebase-merge/orig-head"); + + git_commit_free(head_commit); + git_reference_free(head); + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_rebase_free(rebase); +} + +static int rebase_is_blocked(void) +{ + git_rebase *rebase = NULL; + int error; + + git_reference *branch_ref, *upstream_ref; + git_annotated_commit *branch_head, *upstream_head; + + cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo)); + + cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef")); + 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)); + + error = git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, signature, NULL); + + git_annotated_commit_free(branch_head); + git_annotated_commit_free(upstream_head); + + git_reference_free(branch_ref); + git_reference_free(upstream_ref); + git_rebase_free(rebase); + + return error; +} + +void test_rebase_setup__blocked_for_staged_change(void) +{ + cl_git_rewritefile("rebase/newfile.txt", "Stage an add"); + git_index_add_bypath(_index, "newfile.txt"); + cl_git_fail(rebase_is_blocked()); +} + +void test_rebase_setup__blocked_for_unstaged_change(void) +{ + cl_git_rewritefile("rebase/asparagus.txt", "Unstaged change"); + cl_git_fail(rebase_is_blocked()); +} + +void test_rebase_setup__not_blocked_for_untracked_add(void) +{ + cl_git_rewritefile("rebase/newfile.txt", "Untracked file"); + cl_git_pass(rebase_is_blocked()); +} + diff -Nru libgit2-0.20.0/tests/refs/branches/create.c libgit2-0.22.2/tests/refs/branches/create.c --- libgit2-0.20.0/tests/refs/branches/create.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/branches/create.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "refs.h" +#include "path.h" static git_repository *repo; static git_commit *target; @@ -7,10 +8,9 @@ void test_refs_branches_create__initialize(void) { - cl_fixture_sandbox("testrepo.git"); - cl_git_pass(git_repository_open(&repo, "testrepo.git")); - + repo = cl_git_sandbox_init("testrepo.git"); branch = NULL; + target = NULL; } void test_refs_branches_create__cleanup(void) @@ -21,23 +21,22 @@ git_commit_free(target); target = NULL; - git_repository_free(repo); + cl_git_sandbox_cleanup(); repo = NULL; - - cl_fixture_cleanup("testrepo.git"); } static void retrieve_target_from_oid(git_commit **out, git_repository *repo, const char *sha) { - git_oid oid; + git_object *obj; - cl_git_pass(git_oid_fromstr(&oid, sha)); - cl_git_pass(git_commit_lookup(out, repo, &oid)); + cl_git_pass(git_revparse_single(&obj, repo, sha)); + cl_git_pass(git_commit_lookup(out, repo, git_object_id(obj))); + git_object_free(obj); } static void retrieve_known_commit(git_commit **commit, git_repository *repo) { - retrieve_target_from_oid(commit, repo, "e90810b8df3e80c413d903f631643c716887138d"); + retrieve_target_from_oid(commit, repo, "e90810b8df3"); } #define NEW_BRANCH_NAME "new-branch-on-the-block" @@ -46,7 +45,7 @@ { retrieve_known_commit(&target, repo); - cl_git_pass(git_branch_create(&branch, repo, NEW_BRANCH_NAME, target, 0)); + cl_git_pass(git_branch_create(&branch, repo, NEW_BRANCH_NAME, target, 0, NULL, NULL)); cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target))); } @@ -54,23 +53,250 @@ { retrieve_known_commit(&target, repo); - cl_assert_equal_i(GIT_EEXISTS, git_branch_create(&branch, repo, "br2", target, 0)); + cl_assert_equal_i(GIT_EEXISTS, git_branch_create(&branch, repo, "br2", target, 0, NULL, NULL)); } void test_refs_branches_create__can_force_create_over_an_existing_branch(void) { retrieve_known_commit(&target, repo); - cl_git_pass(git_branch_create(&branch, repo, "br2", target, 1)); + cl_git_pass(git_branch_create(&branch, repo, "br2", target, 1, NULL, NULL)); cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target))); cl_assert_equal_s("refs/heads/br2", git_reference_name(branch)); } +void test_refs_branches_create__cannot_force_create_over_current_branch(void) +{ + const git_oid *oid; + git_reference *branch2; + retrieve_known_commit(&target, repo); + + cl_git_pass(git_branch_lookup(&branch2, repo, "master", GIT_BRANCH_LOCAL)); + cl_assert_equal_s("refs/heads/master", git_reference_name(branch2)); + cl_assert_equal_i(true, git_branch_is_head(branch2)); + oid = git_reference_target(branch2); + + cl_git_fail_with(-1, git_branch_create(&branch, repo, "master", target, 1, NULL, NULL)); + branch = NULL; + cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL)); + cl_assert_equal_s("refs/heads/master", git_reference_name(branch)); + cl_git_pass(git_oid_cmp(git_reference_target(branch), oid)); + git_reference_free(branch2); +} void test_refs_branches_create__creating_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void) { retrieve_known_commit(&target, repo); cl_assert_equal_i(GIT_EINVALIDSPEC, - git_branch_create(&branch, repo, "inv@{id", target, 0)); -} \ No newline at end of file + git_branch_create(&branch, repo, "inv@{id", target, 0, NULL, NULL)); +} + +void test_refs_branches_create__creation_creates_new_reflog(void) +{ + git_reflog *log; + const git_reflog_entry *entry; + git_signature *sig; + + cl_git_pass(git_signature_now(&sig, "me", "foo@example.com")); + + retrieve_known_commit(&target, repo); + cl_git_pass(git_branch_create(&branch, repo, NEW_BRANCH_NAME, target, false, sig, "create!")); + cl_git_pass(git_reflog_read(&log, repo, "refs/heads/" NEW_BRANCH_NAME)); + + cl_assert_equal_i(1, git_reflog_entrycount(log)); + entry = git_reflog_entry_byindex(log, 0); + cl_assert_equal_s("create!", git_reflog_entry_message(entry)); + cl_assert_equal_s("foo@example.com", git_reflog_entry_committer(entry)->email); + + git_reflog_free(log); + git_signature_free(sig); +} + +void test_refs_branches_create__default_reflog_message(void) +{ + git_reflog *log; + const git_reflog_entry *entry; + 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, NULL, NULL)); + cl_git_pass(git_reflog_read(&log, repo, "refs/heads/" NEW_BRANCH_NAME)); + + entry = git_reflog_entry_byindex(log, 0); + cl_assert_equal_s("Branch: created", git_reflog_entry_message(entry)); + cl_assert_equal_s(sig->email, git_reflog_entry_committer(entry)->email); + + git_reflog_free(log); + git_signature_free(sig); +} + +static void assert_branch_matches_name( + const char *expected, const char *lookup_as) +{ + git_reference *ref; + git_buf b = GIT_BUF_INIT; + + cl_git_pass(git_branch_lookup(&ref, repo, lookup_as, GIT_BRANCH_LOCAL)); + + cl_git_pass(git_buf_sets(&b, "refs/heads/")); + cl_git_pass(git_buf_puts(&b, expected)); + cl_assert_equal_s(b.ptr, git_reference_name(ref)); + + cl_git_pass( + git_oid_cmp(git_reference_target(ref), git_commit_id(target))); + + git_reference_free(ref); + git_buf_free(&b); +} + +void test_refs_branches_create__can_create_branch_with_unicode(void) +{ + const char *nfc = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D"; + const char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D"; + const char *emoji = "\xF0\x9F\x8D\xB7"; + const char *names[] = { nfc, nfd, emoji }; + const char *alt[] = { nfd, nfc, NULL }; + const char *expected[] = { nfc, nfd, emoji }; + unsigned int i; + bool fs_decompose_unicode = + git_path_does_fs_decompose_unicode(git_repository_path(repo)); + + retrieve_known_commit(&target, repo); + + if (cl_repo_get_bool(repo, "core.precomposeunicode")) + expected[1] = nfc; + /* test decomp. because not all Mac filesystems decompose unicode */ + else if (fs_decompose_unicode) + expected[0] = nfd; + + for (i = 0; i < ARRAY_SIZE(names); ++i) { + const char *name; + cl_git_pass(git_branch_create( + &branch, repo, names[i], target, 0, NULL, NULL)); + cl_git_pass(git_oid_cmp( + git_reference_target(branch), git_commit_id(target))); + + cl_git_pass(git_branch_name(&name, branch)); + cl_assert_equal_s(expected[i], name); + assert_branch_matches_name(expected[i], names[i]); + if (fs_decompose_unicode && alt[i]) + assert_branch_matches_name(expected[i], alt[i]); + + cl_git_pass(git_branch_delete(branch)); + git_reference_free(branch); + branch = NULL; + } +} + +/** + * Verify that we can create a branch with a name that matches the + * namespace of a previously delete branch. + * + * git branch level_one/level_two + * git branch -D level_one/level_two + * git branch level_one + * + * We expect the delete to have deleted the files: + * ".git/refs/heads/level_one/level_two" + * ".git/logs/refs/heads/level_one/level_two" + * It may or may not have deleted the (now empty) + * containing directories. To match git.git behavior, + * the second create needs to implicilty delete the + * directories and create the new files. + * "refs/heads/level_one" + * "logs/refs/heads/level_one" + * + * We should not fail to create the branch or its + * reflog because of an obsolete namespace container + * directory. + */ +void test_refs_branches_create__name_vs_namespace(void) +{ + const char * name; + struct item { + const char *first; + const char *second; + }; + static const struct item item[] = { + { "level_one/level_two", "level_one" }, + { "a/b/c/d/e", "a/b/c/d" }, + { "ss/tt/uu/vv/ww", "ss" }, + /* And one test case that is deeper. */ + { "xx1/xx2/xx3/xx4", "xx1/xx2/xx3/xx4/xx5/xx6" }, + { NULL, NULL }, + }; + const struct item *p; + + retrieve_known_commit(&target, repo); + + for (p=item; p->first; p++) { + cl_git_pass(git_branch_create(&branch, repo, p->first, target, 0, NULL, NULL)); + cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target))); + cl_git_pass(git_branch_name(&name, branch)); + cl_assert_equal_s(name, p->first); + + cl_git_pass(git_branch_delete(branch)); + git_reference_free(branch); + branch = NULL; + + cl_git_pass(git_branch_create(&branch, repo, p->second, target, 0, NULL, NULL)); + git_reference_free(branch); + branch = NULL; + } +} + +/** + * We still need to fail if part of the namespace is + * still in use. + */ +void test_refs_branches_create__name_vs_namespace_fail(void) +{ + const char * name; + struct item { + const char *first; + const char *first_alternate; + const char *second; + }; + static const struct item item[] = { + { "level_one/level_two", "level_one/alternate", "level_one" }, + { "a/b/c/d/e", "a/b/c/d/alternate", "a/b/c/d" }, + { "ss/tt/uu/vv/ww", "ss/alternate", "ss" }, + { NULL, NULL, NULL }, + }; + const struct item *p; + + retrieve_known_commit(&target, repo); + + for (p=item; p->first; p++) { + cl_git_pass(git_branch_create(&branch, repo, p->first, target, 0, NULL, NULL)); + cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target))); + cl_git_pass(git_branch_name(&name, branch)); + cl_assert_equal_s(name, p->first); + + cl_git_pass(git_branch_delete(branch)); + git_reference_free(branch); + branch = NULL; + + cl_git_pass(git_branch_create(&branch, repo, p->first_alternate, target, 0, NULL, NULL)); + cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target))); + cl_git_pass(git_branch_name(&name, branch)); + cl_assert_equal_s(name, p->first_alternate); + + /* we do not delete the alternate. */ + git_reference_free(branch); + branch = NULL; + + cl_git_fail(git_branch_create(&branch, repo, p->second, target, 0, NULL, NULL)); + git_reference_free(branch); + branch = NULL; + } +} diff -Nru libgit2-0.20.0/tests/refs/branches/delete.c libgit2-0.22.2/tests/refs/branches/delete.c --- libgit2-0.20.0/tests/refs/branches/delete.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/branches/delete.c 2015-03-24 16:10:45.000000000 +0000 @@ -10,11 +10,10 @@ { git_oid id; - cl_fixture_sandbox("testrepo.git"); - cl_git_pass(git_repository_open(&repo, "testrepo.git")); + repo = cl_git_sandbox_init("testrepo.git"); cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); - cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0)); + cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0, NULL, NULL)); } void test_refs_branches_delete__cleanup(void) @@ -22,10 +21,8 @@ git_reference_free(fake_remote); fake_remote = NULL; - git_repository_free(repo); + cl_git_sandbox_cleanup(); repo = NULL; - - cl_fixture_cleanup("testrepo.git"); } void test_refs_branches_delete__can_not_delete_a_branch_pointed_at_by_HEAD(void) @@ -78,7 +75,7 @@ git_reference_free(head); /* Detach HEAD and make it target the commit that "master" points to */ - git_repository_detach_head(repo); + git_repository_detach_head(repo, NULL, NULL); cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL)); cl_git_pass(git_branch_delete(branch)); @@ -115,3 +112,29 @@ assert_config_entry_existence(repo, "branch.track-local.remote", false); assert_config_entry_existence(repo, "branch.track-local.merge", false); } + +void test_refs_branches_delete__removes_reflog(void) +{ + git_reference *branch; + git_reflog *log; + git_oid oidzero = {{0}}; + git_signature *sig; + + /* Ensure the reflog has at least one entry */ + cl_git_pass(git_signature_now(&sig, "Me", "user@example.com")); + cl_git_pass(git_reflog_read(&log, repo, "refs/heads/track-local")); + cl_git_pass(git_reflog_append(log, &oidzero, sig, "message")); + cl_assert(git_reflog_entrycount(log) > 0); + git_signature_free(sig); + git_reflog_free(log); + + cl_git_pass(git_branch_lookup(&branch, repo, "track-local", GIT_BRANCH_LOCAL)); + cl_git_pass(git_branch_delete(branch)); + git_reference_free(branch); + + /* Reading a nonexistant reflog creates it, but it should be empty */ + cl_git_pass(git_reflog_read(&log, repo, "refs/heads/track-local")); + cl_assert_equal_i(0, git_reflog_entrycount(log)); + git_reflog_free(log); +} + diff -Nru libgit2-0.20.0/tests/refs/branches/ishead.c libgit2-0.22.2/tests/refs/branches/ishead.c --- libgit2-0.20.0/tests/refs/branches/ishead.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/branches/ishead.c 2015-03-24 16:10:45.000000000 +0000 @@ -7,7 +7,8 @@ void test_refs_branches_ishead__initialize(void) { - cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); + repo = cl_git_sandbox_init("testrepo.git"); + branch = NULL; } void test_refs_branches_ishead__cleanup(void) @@ -15,7 +16,7 @@ git_reference_free(branch); branch = NULL; - git_repository_free(repo); + cl_git_sandbox_cleanup(); repo = NULL; } @@ -28,34 +29,20 @@ void test_refs_branches_ishead__can_properly_handle_unborn_HEAD(void) { - git_repository_free(repo); - - repo = cl_git_sandbox_init("testrepo.git"); - make_head_unborn(repo, NON_EXISTING_HEAD); cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master")); cl_assert_equal_i(false, git_branch_is_head(branch)); - - cl_git_sandbox_cleanup(); - repo = NULL; } void test_refs_branches_ishead__can_properly_handle_missing_HEAD(void) { - git_repository_free(repo); - - repo = cl_git_sandbox_init("testrepo.git"); - delete_head(repo); cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master")); cl_assert_equal_i(false, git_branch_is_head(branch)); - - cl_git_sandbox_cleanup(); - repo = NULL; } void test_refs_branches_ishead__can_tell_if_a_branch_is_not_pointed_at_by_HEAD(void) @@ -95,12 +82,9 @@ { git_reference *linked, *super, *head; - git_repository_free(repo); - repo = cl_git_sandbox_init("testrepo.git"); - - cl_git_pass(git_reference_symbolic_create(&linked, repo, "refs/heads/linked", "refs/heads/master", 0)); - cl_git_pass(git_reference_symbolic_create(&super, repo, "refs/heads/super", "refs/heads/linked", 0)); - cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, "refs/heads/super", 1)); + cl_git_pass(git_reference_symbolic_create(&linked, repo, "refs/heads/linked", "refs/heads/master", 0, NULL, NULL)); + cl_git_pass(git_reference_symbolic_create(&super, repo, "refs/heads/super", "refs/heads/linked", 0, NULL, NULL)); + cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, "refs/heads/super", 1, NULL, NULL)); cl_assert_equal_i(false, git_branch_is_head(linked)); cl_assert_equal_i(false, git_branch_is_head(super)); @@ -111,6 +95,4 @@ git_reference_free(linked); git_reference_free(super); git_reference_free(head); - cl_git_sandbox_cleanup(); - repo = NULL; } diff -Nru libgit2-0.20.0/tests/refs/branches/iterator.c libgit2-0.22.2/tests/refs/branches/iterator.c --- libgit2-0.20.0/tests/refs/branches/iterator.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/branches/iterator.c 2015-03-24 16:10:45.000000000 +0000 @@ -12,7 +12,7 @@ cl_git_pass(git_repository_open(&repo, "testrepo.git")); cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); - cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0)); + cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0, NULL, NULL)); } void test_refs_branches_iterator__cleanup(void) @@ -48,7 +48,7 @@ void test_refs_branches_iterator__retrieve_all_branches(void) { - assert_retrieval(GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE, 14); + assert_retrieval(GIT_BRANCH_ALL, 14); } void test_refs_branches_iterator__retrieve_remote_branches(void) @@ -113,7 +113,7 @@ }; git_reference_free(fake_remote); - cl_git_pass(git_reference_symbolic_create(&fake_remote, repo, "refs/remotes/nulltoken/HEAD", "refs/remotes/nulltoken/master", 0)); + cl_git_pass(git_reference_symbolic_create(&fake_remote, repo, "refs/remotes/nulltoken/HEAD", "refs/remotes/nulltoken/master", 0, NULL, NULL)); assert_retrieval(GIT_BRANCH_REMOTE, 3); @@ -139,7 +139,7 @@ r2 = cl_git_sandbox_init("testrepo2"); - cl_git_pass(git_branch_iterator_new(&iter, r2, GIT_BRANCH_LOCAL | GIT_BRANCH_REMOTE)); + cl_git_pass(git_branch_iterator_new(&iter, r2, GIT_BRANCH_ALL)); contains_branches(exp, iter); git_branch_iterator_free(iter); diff -Nru libgit2-0.20.0/tests/refs/branches/move.c libgit2-0.22.2/tests/refs/branches/move.c --- libgit2-0.20.0/tests/refs/branches/move.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/branches/move.c 2015-03-24 16:10:45.000000000 +0000 @@ -22,7 +22,7 @@ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); - cl_git_pass(git_branch_move(&new_ref, original_ref, NEW_BRANCH_NAME, 0)); + cl_git_pass(git_branch_move(&new_ref, original_ref, NEW_BRANCH_NAME, 0, NULL, NULL)); cl_assert_equal_s(GIT_REFS_HEADS_DIR NEW_BRANCH_NAME, git_reference_name(new_ref)); git_reference_free(original_ref); @@ -36,11 +36,11 @@ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); /* Downward */ - cl_git_pass(git_branch_move(&new_ref, original_ref, "somewhere/" NEW_BRANCH_NAME, 0)); + cl_git_pass(git_branch_move(&new_ref, original_ref, "somewhere/" NEW_BRANCH_NAME, 0, NULL, NULL)); git_reference_free(original_ref); /* Upward */ - cl_git_pass(git_branch_move(&newer_ref, new_ref, "br2", 0)); + cl_git_pass(git_branch_move(&newer_ref, new_ref, "br2", 0, NULL, NULL)); git_reference_free(new_ref); git_reference_free(newer_ref); @@ -53,11 +53,11 @@ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); /* Downward */ - cl_git_pass(git_branch_move(&new_ref, original_ref, "br2/" NEW_BRANCH_NAME, 0)); + cl_git_pass(git_branch_move(&new_ref, original_ref, "br2/" NEW_BRANCH_NAME, 0, NULL, NULL)); git_reference_free(original_ref); /* Upward */ - cl_git_pass(git_branch_move(&newer_ref, new_ref, "br2", 0)); + cl_git_pass(git_branch_move(&newer_ref, new_ref, "br2", 0, NULL, NULL)); git_reference_free(new_ref); git_reference_free(newer_ref); @@ -66,12 +66,54 @@ void test_refs_branches_move__can_not_move_a_branch_if_its_destination_name_collide_with_an_existing_one(void) { git_reference *original_ref, *new_ref; + git_config *config; + const git_config_entry *ce; + char *original_remote, *original_merge; + + cl_git_pass(git_repository_config(&config, repo)); + + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.remote")); + original_remote = strdup(ce->value); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.merge")); + original_merge = strdup(ce->value); + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); - cl_assert_equal_i(GIT_EEXISTS, git_branch_move(&new_ref, original_ref, "master", 0)); + cl_assert_equal_i(GIT_EEXISTS, + git_branch_move(&new_ref, original_ref, "master", 0, NULL, NULL)); + + cl_assert(giterr_last()->message != NULL); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.remote")); + cl_assert_equal_s(original_remote, ce->value); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.merge")); + cl_assert_equal_s(original_merge, ce->value); + + + cl_assert_equal_i(GIT_EEXISTS, + git_branch_move(&new_ref, original_ref, "cannot-fetch", 0, NULL, NULL)); + cl_assert(giterr_last()->message != NULL); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.remote")); + cl_assert_equal_s(original_remote, ce->value); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.merge")); + cl_assert_equal_s(original_merge, ce->value); + + git_reference_free(original_ref); + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/track-local")); + + cl_assert_equal_i(GIT_EEXISTS, + git_branch_move(&new_ref, original_ref, "master", 0, NULL, NULL)); + + cl_assert(giterr_last()->message != NULL); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.remote")); + cl_assert_equal_s(original_remote, ce->value); + cl_git_pass(git_config_get_entry(&ce, config, "branch.master.merge")); + cl_assert_equal_s(original_merge, ce->value); + + free(original_remote); free(original_merge); git_reference_free(original_ref); + git_config_free(config); } void test_refs_branches_move__moving_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void) @@ -80,7 +122,7 @@ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); - cl_assert_equal_i(GIT_EINVALIDSPEC, git_branch_move(&new_ref, original_ref, "Inv@{id", 0)); + cl_assert_equal_i(GIT_EINVALIDSPEC, git_branch_move(&new_ref, original_ref, "Inv@{id", 0, NULL, NULL)); git_reference_free(original_ref); } @@ -90,7 +132,7 @@ git_reference *tag, *new_ref; cl_git_pass(git_reference_lookup(&tag, repo, "refs/tags/e90810b")); - cl_git_fail(git_branch_move(&new_ref, tag, NEW_BRANCH_NAME, 0)); + cl_git_fail(git_branch_move(&new_ref, tag, NEW_BRANCH_NAME, 0, NULL, NULL)); git_reference_free(tag); } @@ -101,7 +143,7 @@ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); - cl_git_pass(git_branch_move(&new_ref, original_ref, "master", 1)); + cl_git_pass(git_branch_move(&new_ref, original_ref, "master", 1, NULL, NULL)); git_reference_free(original_ref); git_reference_free(new_ref); @@ -119,7 +161,7 @@ assert_config_entry_existence(repo, "branch.moved.remote", false); assert_config_entry_existence(repo, "branch.moved.merge", false); - cl_git_pass(git_branch_move(&new_branch, branch, "moved", 0)); + cl_git_pass(git_branch_move(&new_branch, branch, "moved", 0, NULL, NULL)); git_reference_free(branch); assert_config_entry_existence(repo, "branch.track-local.remote", false); @@ -136,7 +178,7 @@ git_reference *new_branch; cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master")); - cl_git_pass(git_branch_move(&new_branch, branch, "master2", 0)); + cl_git_pass(git_branch_move(&new_branch, branch, "master2", 0, NULL, NULL)); git_reference_free(branch); git_reference_free(new_branch); @@ -144,3 +186,75 @@ cl_assert_equal_s("refs/heads/master2", git_reference_name(branch)); git_reference_free(branch); } + +void test_refs_branches_move__updates_the_reflog(void) +{ + git_reference *branch; + git_reference *new_branch; + git_reflog *log; + const git_reflog_entry *entry; + git_signature *sig; + + cl_git_pass(git_signature_now(&sig, "me", "foo@example.com")); + + cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master")); + cl_git_pass(git_branch_move(&new_branch, branch, "master2", 0, sig, "message")); + + cl_git_pass(git_reflog_read(&log, repo, git_reference_name(new_branch))); + 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_reference_free(branch); + git_reference_free(new_branch); + git_reflog_free(log); + git_signature_free(sig); +} + +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; + + 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")); + cl_git_pass(git_branch_move(&new_branch, branch, "master2", 0, NULL, NULL)); + + 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); + + 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; + const char *new_branch_name = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D"; + + cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2")); + cl_git_pass(git_branch_move(&new_ref, original_ref, new_branch_name, 0, NULL, NULL)); + + if (cl_repo_get_bool(repo, "core.precomposeunicode")) + cl_assert_equal_s(GIT_REFS_HEADS_DIR "\xC3\x85\x73\x74\x72\xC3\xB6\x6D", git_reference_name(new_ref)); + else + cl_assert_equal_s(GIT_REFS_HEADS_DIR "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D", git_reference_name(new_ref)); + + git_reference_free(original_ref); + git_reference_free(new_ref); +} diff -Nru libgit2-0.20.0/tests/refs/branches/remote.c libgit2-0.22.2/tests/refs/branches/remote.c --- libgit2-0.20.0/tests/refs/branches/remote.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/branches/remote.c 2015-03-24 16:10:45.000000000 +0000 @@ -21,53 +21,40 @@ void test_refs_branches_remote__can_get_remote_for_branch(void) { - char remotename[1024] = {0}; + git_buf remotename = {0}; - cl_assert_equal_i(expected_remote_name_length, - git_branch_remote_name(NULL, 0, g_repo, remote_tracking_branch_name)); + cl_git_pass(git_branch_remote_name(&remotename, g_repo, remote_tracking_branch_name)); - cl_assert_equal_i(expected_remote_name_length, - git_branch_remote_name(remotename, expected_remote_name_length, g_repo, - remote_tracking_branch_name)); - - cl_assert_equal_s("test", remotename); -} - -void test_refs_branches_remote__insufficient_buffer_returns_error(void) -{ - char remotename[1024] = {0}; - - cl_assert_equal_i(expected_remote_name_length, - git_branch_remote_name(NULL, 0, g_repo, remote_tracking_branch_name)); - - cl_git_fail_with(git_branch_remote_name(remotename, - expected_remote_name_length - 1, g_repo, remote_tracking_branch_name), - expected_remote_name_length); + cl_assert_equal_s("test", remotename.ptr); + git_buf_free(&remotename); } void test_refs_branches_remote__no_matching_remote_returns_error(void) { const char *unknown = "refs/remotes/nonexistent/master"; + git_buf buf; giterr_clear(); - cl_git_fail_with(git_branch_remote_name( - NULL, 0, g_repo, unknown), GIT_ENOTFOUND); + memset(&buf, 0, sizeof(git_buf)); + cl_git_fail_with(git_branch_remote_name(&buf, g_repo, unknown), GIT_ENOTFOUND); cl_assert(giterr_last() != NULL); } void test_refs_branches_remote__local_remote_returns_error(void) { const char *local = "refs/heads/master"; + git_buf buf; giterr_clear(); - cl_git_fail_with(git_branch_remote_name( - NULL, 0, g_repo, local), GIT_ERROR); + memset(&buf, 0, sizeof(git_buf)); + cl_git_fail_with(git_branch_remote_name(&buf, g_repo, local), GIT_ERROR); cl_assert(giterr_last() != NULL); } void test_refs_branches_remote__ambiguous_remote_returns_error(void) { git_remote *remote; + git_buf buf; /* Create the remote */ cl_git_pass(git_remote_create(&remote, g_repo, "addtest", "http://github.com/libgit2/libgit2")); @@ -80,7 +67,7 @@ git_remote_free(remote); giterr_clear(); - cl_git_fail_with(git_branch_remote_name(NULL, 0, g_repo, - remote_tracking_branch_name), GIT_EAMBIGUOUS); + memset(&buf, 0, sizeof(git_buf)); + cl_git_fail_with(git_branch_remote_name(&buf, g_repo, remote_tracking_branch_name), GIT_EAMBIGUOUS); cl_assert(giterr_last() != NULL); } diff -Nru libgit2-0.20.0/tests/refs/branches/upstream.c libgit2-0.22.2/tests/refs/branches/upstream.c --- libgit2-0.20.0/tests/refs/branches/upstream.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/branches/upstream.c 2015-03-24 16:10:45.000000000 +0000 @@ -61,12 +61,37 @@ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch)); } +void test_refs_branches_upstream__upstream_remote(void) +{ + git_buf buf = GIT_BUF_INIT; + + cl_git_pass(git_branch_upstream_remote(&buf, repo, "refs/heads/master")); + cl_assert_equal_s("test", buf.ptr); + git_buf_free(&buf); +} + +void test_refs_branches_upstream__upstream_remote_empty_value(void) +{ + git_repository *repository; + git_config *cfg; + git_buf buf = GIT_BUF_INIT; + + repository = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_repository_config(&cfg, repository)); + cl_git_pass(git_config_set_string(cfg, "branch.master.remote", "")); + cl_git_fail_with(GIT_ENOTFOUND, git_branch_upstream_remote(&buf, repository, "refs/heads/master")); + + cl_git_pass(git_config_delete_entry(cfg, "branch.master.remote")); + cl_git_fail_with(GIT_ENOTFOUND, git_branch_upstream_remote(&buf, repository, "refs/heads/master")); + cl_git_sandbox_cleanup(); +} + static void assert_merge_and_or_remote_key_missing(git_repository *repository, const git_commit *target, const char *entry_name) { git_reference *branch; cl_assert_equal_i(GIT_OBJ_COMMIT, git_object_type((git_object*)target)); - cl_git_pass(git_branch_create(&branch, repository, entry_name, (git_commit*)target, 0)); + cl_git_pass(git_branch_create(&branch, repository, entry_name, (git_commit*)target, 0, NULL, NULL)); cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch)); diff -Nru libgit2-0.20.0/tests/refs/branches/upstreamname.c libgit2-0.22.2/tests/refs/branches/upstreamname.c --- libgit2-0.20.0/tests/refs/branches/upstreamname.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/branches/upstreamname.c 2015-03-24 16:10:45.000000000 +0000 @@ -21,7 +21,7 @@ void test_refs_branches_upstreamname__can_retrieve_the_remote_tracking_reference_name_of_a_local_branch(void) { - cl_git_pass(git_branch_upstream__name( + cl_git_pass(git_branch_upstream_name( &upstream_name, repo, "refs/heads/master")); cl_assert_equal_s("refs/remotes/test/master", git_buf_cstr(&upstream_name)); @@ -29,14 +29,8 @@ void test_refs_branches_upstreamname__can_retrieve_the_local_upstream_reference_name_of_a_local_branch(void) { - cl_git_pass(git_branch_upstream__name( + cl_git_pass(git_branch_upstream_name( &upstream_name, repo, "refs/heads/track-local")); cl_assert_equal_s("refs/heads/master", git_buf_cstr(&upstream_name)); } - -void test_refs_branches_upstreamname__can_return_the_size_of_thelocal_upstream_reference_name_of_a_local_branch(void) -{ - cl_assert_equal_i((int)strlen("refs/heads/master") + 1, - git_branch_upstream_name(NULL, 0, repo, "refs/heads/track-local")); -} diff -Nru libgit2-0.20.0/tests/refs/crashes.c libgit2-0.22.2/tests/refs/crashes.c --- libgit2-0.20.0/tests/refs/crashes.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/crashes.c 2015-03-24 16:10:45.000000000 +0000 @@ -7,7 +7,7 @@ const char *REFNAME = "refs/heads/xxx"; cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git"))); - cl_git_pass(git_reference_symbolic_create(&ref, repo, REFNAME, "refs/heads/master", 0)); + cl_git_pass(git_reference_symbolic_create(&ref, repo, REFNAME, "refs/heads/master", 0, NULL, NULL)); cl_git_pass(git_reference_lookup(&ref2, repo, REFNAME)); cl_git_pass(git_reference_delete(ref)); git_reference_free(ref); diff -Nru libgit2-0.20.0/tests/refs/create.c libgit2-0.22.2/tests/refs/create.c --- libgit2-0.20.0/tests/refs/create.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/create.c 2015-03-24 16:10:45.000000000 +0000 @@ -32,7 +32,7 @@ git_oid_fromstr(&id, current_master_tip); /* Create and write the new symbolic reference */ - cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, current_head_target, 0)); + cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, current_head_target, 0, NULL, NULL)); /* Ensure the reference can be looked-up... */ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head_tracker)); @@ -45,7 +45,7 @@ cl_assert(git_reference_type(resolved_ref) == GIT_REF_OID); /* ...and that it points to the current master tip */ - cl_assert(git_oid_cmp(&id, git_reference_target(resolved_ref)) == 0); + cl_assert_equal_oid(&id, git_reference_target(resolved_ref)); git_reference_free(looked_up_ref); git_reference_free(resolved_ref); @@ -54,7 +54,7 @@ cl_git_pass(git_reference_lookup(&looked_up_ref, repo2, new_head_tracker)); cl_git_pass(git_reference_resolve(&resolved_ref, looked_up_ref)); - cl_assert(git_oid_cmp(&id, git_reference_target(resolved_ref)) == 0); + cl_assert_equal_oid(&id, git_reference_target(resolved_ref)); git_repository_free(repo2); @@ -73,10 +73,10 @@ git_oid_fromstr(&id, current_master_tip); - cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, current_head_target, 0)); + cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, current_head_target, 0, NULL, NULL)); cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head_tracker)); cl_git_pass(git_reference_resolve(&resolved_ref, looked_up_ref)); - cl_assert(git_oid_cmp(&id, git_reference_target(resolved_ref)) == 0); + cl_assert_equal_oid(&id, git_reference_target(resolved_ref)); git_reference_free(new_reference); git_reference_free(looked_up_ref); @@ -95,7 +95,7 @@ git_oid_fromstr(&id, current_master_tip); /* Create and write the new object id reference */ - cl_git_pass(git_reference_create(&new_reference, g_repo, new_head, &id, 0)); + cl_git_pass(git_reference_create(&new_reference, g_repo, new_head, &id, 0, NULL, NULL)); /* Ensure the reference can be looked-up... */ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head)); @@ -104,14 +104,14 @@ cl_assert_equal_s(looked_up_ref->name, new_head); /* ...and that it points to the current master tip */ - cl_assert(git_oid_cmp(&id, git_reference_target(looked_up_ref)) == 0); + cl_assert_equal_oid(&id, git_reference_target(looked_up_ref)); git_reference_free(looked_up_ref); /* Similar test with a fresh new repository */ cl_git_pass(git_repository_open(&repo2, "testrepo")); cl_git_pass(git_reference_lookup(&looked_up_ref, repo2, new_head)); - cl_assert(git_oid_cmp(&id, git_reference_target(looked_up_ref)) == 0); + cl_assert_equal_oid(&id, git_reference_target(looked_up_ref)); git_repository_free(repo2); @@ -130,7 +130,7 @@ git_oid_fromstr(&id, "deadbeef3f795b2b4353bcce3a527ad0a4f7f644"); /* Create and write the new object id reference */ - cl_git_fail(git_reference_create(&new_reference, g_repo, new_head, &id, 0)); + cl_git_fail(git_reference_create(&new_reference, g_repo, new_head, &id, 0, NULL, NULL)); /* Ensure the reference can't be looked-up... */ cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, new_head)); @@ -144,25 +144,67 @@ /* Make sure it works for oid and for symbolic both */ git_oid_fromstr(&oid, current_master_tip); - error = git_reference_create(&ref, g_repo, current_head_target, &oid, false); + error = git_reference_create(&ref, g_repo, current_head_target, &oid, false, NULL, NULL); cl_assert(error == GIT_EEXISTS); - error = git_reference_symbolic_create(&ref, g_repo, "HEAD", current_head_target, false); + error = git_reference_symbolic_create(&ref, g_repo, "HEAD", current_head_target, false, NULL, NULL); cl_assert(error == GIT_EEXISTS); } -void test_refs_create__creating_a_reference_with_an_invalid_name_returns_EINVALIDSPEC(void) +static void test_invalid_name(const char *name) { git_reference *new_reference; git_oid id; - const char *name = "refs/heads/inv@{id"; - git_oid_fromstr(&id, current_master_tip); cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_create( - &new_reference, g_repo, name, &id, 0)); + &new_reference, g_repo, name, &id, 0, NULL, NULL)); cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_symbolic_create( - &new_reference, g_repo, name, current_head_target, 0)); + &new_reference, g_repo, name, current_head_target, 0, NULL, NULL)); +} + +void test_refs_create__creating_a_reference_with_an_invalid_name_returns_EINVALIDSPEC(void) +{ + test_invalid_name("refs/heads/inv@{id"); + test_invalid_name("refs/heads/back\\slash"); + + test_invalid_name("refs/heads/foo "); + test_invalid_name("refs/heads/foo /bar"); + test_invalid_name("refs/heads/com1:bar/foo"); + + test_invalid_name("refs/heads/e:"); + test_invalid_name("refs/heads/c:/foo"); + + test_invalid_name("refs/heads/foo."); +} + +static void test_win32_name(const char *name) +{ + git_reference *new_reference = NULL; + git_oid id; + int ret; + + git_oid_fromstr(&id, current_master_tip); + + ret = git_reference_create(&new_reference, g_repo, name, &id, 0, NULL, NULL); + +#ifdef GIT_WIN32 + cl_assert_equal_i(GIT_EINVALIDSPEC, ret); +#else + cl_git_pass(ret); +#endif + + git_reference_free(new_reference); +} + +void test_refs_create__creating_a_loose_ref_with_invalid_windows_name(void) +{ + test_win32_name("refs/heads/foo./bar"); + + test_win32_name("refs/heads/aux"); + test_win32_name("refs/heads/aux.foo/bar"); + + test_win32_name("refs/heads/com1"); } diff -Nru libgit2-0.20.0/tests/refs/createwithlog.c libgit2-0.22.2/tests/refs/createwithlog.c --- libgit2-0.20.0/tests/refs/createwithlog.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/refs/createwithlog.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,51 @@ +#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_signature *signature; + 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_signature_now(&signature, "foo", "foo@bar")); + + cl_git_pass( + git_reference_create(&reference, g_repo, name, &id, 0, signature, 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); + git_signature_free(signature); +} diff -Nru libgit2-0.20.0/tests/refs/delete.c libgit2-0.22.2/tests/refs/delete.c --- libgit2-0.20.0/tests/refs/delete.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/delete.c 2015-03-24 16:10:45.000000000 +0000 @@ -66,7 +66,7 @@ git_oid_fromstr(&id, current_master_tip); /* Create and write the new object id reference */ - cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &id, 0)); + cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &id, 0, NULL, NULL)); git_reference_free(ref); /* Lookup the reference */ @@ -91,3 +91,17 @@ git_reference_free(ref); git_refdb_free(refdb); } + +void test_refs_delete__remove(void) +{ + git_reference *ref; + + /* Check that passing no old values lets us delete */ + + cl_git_pass(git_reference_lookup(&ref, g_repo, packed_test_head_name)); + git_reference_free(ref); + + cl_git_pass(git_reference_remove(g_repo, packed_test_head_name)); + + cl_git_fail(git_reference_lookup(&ref, g_repo, packed_test_head_name)); +} diff -Nru libgit2-0.20.0/tests/refs/foreachglob.c libgit2-0.22.2/tests/refs/foreachglob.c --- libgit2-0.20.0/tests/refs/foreachglob.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/foreachglob.c 2015-03-24 16:10:45.000000000 +0000 @@ -12,7 +12,7 @@ cl_git_pass(git_repository_open(&repo, "testrepo.git")); cl_git_pass(git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); - cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0)); + cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0, NULL, NULL)); } void test_refs_foreachglob__cleanup(void) @@ -81,14 +81,14 @@ (*count)++; - return (*count == 11); + return (*count == 11) ? -1000 : 0; } void test_refs_foreachglob__can_cancel(void) { int count = 0; - cl_assert_equal_i(GIT_EUSER, git_reference_foreach_glob( + cl_assert_equal_i(-1000, git_reference_foreach_glob( repo, "*", interrupt_cb, &count) ); cl_assert_equal_i(11, count); diff -Nru libgit2-0.20.0/tests/refs/iterator.c libgit2-0.22.2/tests/refs/iterator.c --- libgit2-0.20.0/tests/refs/iterator.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/iterator.c 2015-03-24 16:10:45.000000000 +0000 @@ -46,36 +46,43 @@ return strcmp(refa->name, refb->name); } +static void assert_all_refnames_match(git_vector *output) +{ + size_t i; + git_reference *ref; + + cl_assert_equal_sz(output->length, ARRAY_SIZE(refnames)); + + git_vector_sort(output); + + git_vector_foreach(output, i, ref) { + cl_assert_equal_s(ref->name, refnames[i]); + git_reference_free(ref); + } + + git_vector_free(output); +} + void test_refs_iterator__list(void) { git_reference_iterator *iter; git_vector output; git_reference *ref; - int error; - size_t i; cl_git_pass(git_vector_init(&output, 32, &refcmp_cb)); cl_git_pass(git_reference_iterator_new(&iter, repo)); - do { - error = git_reference_next(&ref, iter); - cl_assert(error == 0 || error == GIT_ITEROVER); - if (error != GIT_ITEROVER) { - cl_git_pass(git_vector_insert(&output, ref)); - } - } while (!error); + while (1) { + int error = git_reference_next(&ref, iter); + if (error == GIT_ITEROVER) + break; + cl_git_pass(error); + cl_git_pass(git_vector_insert(&output, ref)); + } git_reference_iterator_free(iter); - cl_assert_equal_sz(output.length, ARRAY_SIZE(refnames)); - - git_vector_sort(&output); - - git_vector_foreach(&output, i, ref) { - cl_assert_equal_s(ref->name, refnames[i]); - git_reference_free(ref); - } - git_vector_free(&output); + assert_all_refnames_match(&output); } void test_refs_iterator__empty(void) @@ -95,3 +102,120 @@ git_odb_free(odb); git_repository_free(empty); } + +static int refs_foreach_cb(git_reference *reference, void *payload) +{ + git_vector *output = payload; + cl_git_pass(git_vector_insert(output, reference)); + return 0; +} + +void test_refs_iterator__foreach(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); +} + +static int refs_foreach_cancel_cb(git_reference *reference, void *payload) +{ + int *cancel_after = payload; + + git_reference_free(reference); + + if (!*cancel_after) + return -333; + (*cancel_after)--; + return 0; +} + +void test_refs_iterator__foreach_can_cancel(void) +{ + int cancel_after = 3; + cl_git_fail_with( + git_reference_foreach(repo, refs_foreach_cancel_cb, &cancel_after), + -333); + cl_assert_equal_i(0, cancel_after); +} + +static int refs_foreach_name_cb(const char *name, void *payload) +{ + git_vector *output = payload; + cl_git_pass(git_vector_insert(output, git__strdup(name))); + return 0; +} + +void test_refs_iterator__foreach_name(void) +{ + git_vector output; + size_t i; + char *name; + + cl_git_pass(git_vector_init(&output, 32, &git__strcmp_cb)); + 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]); + git__free(name); + } + + git_vector_free(&output); +} + +static int refs_foreach_name_cancel_cb(const char *name, void *payload) +{ + int *cancel_after = payload; + if (!*cancel_after) + return -333; + GIT_UNUSED(name); + (*cancel_after)--; + return 0; +} + +void test_refs_iterator__foreach_name_can_cancel(void) +{ + int cancel_after = 5; + cl_git_fail_with( + git_reference_foreach_name( + repo, refs_foreach_name_cancel_cb, &cancel_after), + -333); + cl_assert_equal_i(0, cancel_after); +} + +void test_refs_iterator__concurrent_delete(void) +{ + git_reference_iterator *iter; + size_t full_count = 0, concurrent_count = 0; + const char *name; + int error; + + git_repository_free(repo); + repo = cl_git_sandbox_init("testrepo"); + + cl_git_pass(git_reference_iterator_new(&iter, repo)); + while ((error = git_reference_next_name(&name, iter)) == 0) { + full_count++; + } + + git_reference_iterator_free(iter); + cl_assert_equal_i(GIT_ITEROVER, error); + + cl_git_pass(git_reference_iterator_new(&iter, repo)); + while ((error = git_reference_next_name(&name, iter)) == 0) { + cl_git_pass(git_reference_remove(repo, name)); + concurrent_count++; + } + + git_reference_iterator_free(iter); + cl_assert_equal_i(GIT_ITEROVER, error); + + cl_assert_equal_i(full_count, concurrent_count); + + cl_git_sandbox_cleanup(); + repo = NULL; +} diff -Nru libgit2-0.20.0/tests/refs/lookup.c libgit2-0.22.2/tests/refs/lookup.c --- libgit2-0.20.0/tests/refs/lookup.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/lookup.c 2015-03-24 16:10:45.000000000 +0000 @@ -44,7 +44,7 @@ cl_git_pass(git_reference_name_to_id(&tag, g_repo, "refs/tags/point_to_blob")); cl_git_pass(git_oid_fromstr(&expected, "1385f264afb75a56a5bec74243be9b367ba4ca08")); - cl_assert(git_oid_cmp(&tag, &expected) == 0); + cl_assert_equal_oid(&expected, &tag); } void test_refs_lookup__namespace(void) diff -Nru libgit2-0.20.0/tests/refs/overwrite.c libgit2-0.22.2/tests/refs/overwrite.c --- libgit2-0.20.0/tests/refs/overwrite.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/overwrite.c 2015-03-24 16:10:45.000000000 +0000 @@ -27,8 +27,8 @@ git_reference *ref, *branch_ref; /* The target needds to exist and we need to check the name has changed */ - cl_git_pass(git_reference_symbolic_create(&branch_ref, g_repo, ref_branch_name, ref_master_name, 0)); - cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_branch_name, 0)); + cl_git_pass(git_reference_symbolic_create(&branch_ref, g_repo, ref_branch_name, ref_master_name, 0, NULL, NULL)); + cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_branch_name, 0, NULL, NULL)); git_reference_free(ref); /* Ensure it points to the right place*/ @@ -38,8 +38,8 @@ git_reference_free(ref); /* Ensure we can't create it unless we force it to */ - cl_git_fail(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0)); - cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 1)); + cl_git_fail(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0, NULL, NULL)); + cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 1, NULL, NULL)); git_reference_free(ref); /* Ensure it points to the right place */ @@ -63,7 +63,7 @@ git_reference_free(ref); /* Create it */ - cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 0)); + cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 0, NULL, NULL)); git_reference_free(ref); cl_git_pass(git_reference_lookup(&ref, g_repo, ref_test_name)); @@ -72,13 +72,13 @@ git_reference_free(ref); /* Ensure we can't overwrite unless we force it */ - cl_git_fail(git_reference_create(&ref, g_repo, ref_name, &id, 0)); - cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 1)); + cl_git_fail(git_reference_create(&ref, g_repo, ref_name, &id, 0, NULL, NULL)); + cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 1, NULL, NULL)); git_reference_free(ref); /* Ensure it has been overwritten */ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name)); - cl_assert(!git_oid_cmp(&id, git_reference_target(ref))); + cl_assert_equal_oid(&id, git_reference_target(ref)); git_reference_free(ref); } @@ -94,10 +94,10 @@ git_oid_cpy(&id, git_reference_target(ref)); git_reference_free(ref); - cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 0)); + cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 0, NULL, NULL)); git_reference_free(ref); - cl_git_fail(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0)); - cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 1)); + cl_git_fail(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0, NULL, NULL)); + cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 1, NULL, NULL)); git_reference_free(ref); /* Ensure it points to the right place */ @@ -120,17 +120,17 @@ git_reference_free(ref); /* Create the symbolic ref */ - cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0)); + cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0, NULL, NULL)); git_reference_free(ref); /* It shouldn't overwrite unless we tell it to */ - cl_git_fail(git_reference_create(&ref, g_repo, ref_name, &id, 0)); - cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 1)); + cl_git_fail(git_reference_create(&ref, g_repo, ref_name, &id, 0, NULL, NULL)); + cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 1, NULL, NULL)); git_reference_free(ref); /* Ensure it points to the right place */ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name)); cl_assert(git_reference_type(ref) & GIT_REF_OID); - cl_assert(!git_oid_cmp(git_reference_target(ref), &id)); + cl_assert_equal_oid(&id, git_reference_target(ref)); git_reference_free(ref); } diff -Nru libgit2-0.20.0/tests/refs/pack.c libgit2-0.22.2/tests/refs/pack.c --- libgit2-0.20.0/tests/refs/pack.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/pack.c 2015-03-24 16:10:45.000000000 +0000 @@ -91,13 +91,13 @@ /* make a bunch of references */ for (i = 0; i < 100; ++i) { - snprintf(name, sizeof(name), "refs/heads/symbolic-%03d", i); + p_snprintf(name, sizeof(name), "refs/heads/symbolic-%03d", i); cl_git_pass(git_reference_symbolic_create( - &ref, g_repo, name, "refs/heads/master", 0)); + &ref, g_repo, name, "refs/heads/master", 0, NULL, NULL)); git_reference_free(ref); - snprintf(name, sizeof(name), "refs/heads/direct-%03d", i); - cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0)); + p_snprintf(name, sizeof(name), "refs/heads/direct-%03d", i); + cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0, NULL, NULL)); git_reference_free(ref); } diff -Nru libgit2-0.20.0/tests/refs/peel.c libgit2-0.22.2/tests/refs/peel.c --- libgit2-0.20.0/tests/refs/peel.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/peel.c 2015-03-24 16:10:45.000000000 +0000 @@ -33,7 +33,7 @@ cl_git_pass(git_reference_peel(&peeled, ref, requested_type)); cl_git_pass(git_oid_fromstr(&expected_oid, expected_sha)); - cl_assert_equal_i(0, git_oid_cmp(&expected_oid, git_object_id(peeled))); + cl_assert_equal_oid(&expected_oid, git_object_id(peeled)); cl_assert_equal_i(expected_type, git_object_type(peeled)); @@ -93,7 +93,7 @@ void test_refs_peel__cannot_peel_into_a_non_existing_target(void) { - assert_peel_error(GIT_ENOTFOUND, "refs/tags/point_to_blob", GIT_OBJ_TAG); + assert_peel_error(GIT_EINVALIDSPEC, "refs/tags/point_to_blob", GIT_OBJ_TAG); } void test_refs_peel__can_peel_into_any_non_tag_object(void) diff -Nru libgit2-0.20.0/tests/refs/races.c libgit2-0.22.2/tests/refs/races.c --- libgit2-0.20.0/tests/refs/races.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/refs/races.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,152 @@ +#include "clar_libgit2.h" + +#include "repository.h" +#include "git2/reflog.h" +#include "reflog.h" +#include "ref_helpers.h" + +static const char *commit_id = "099fabac3a9ea935598528c27f866e34089c2eff"; +static const char *refname = "refs/heads/master"; +static const char *other_refname = "refs/heads/foo"; +static const char *other_commit_id = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"; + +static git_repository *g_repo; + +void test_refs_races__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); +} + +void test_refs_races__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_refs_races__create_matching(void) +{ + git_reference *ref, *ref2, *ref3; + git_oid id, other_id; + + git_oid_fromstr(&id, commit_id); + git_oid_fromstr(&other_id, other_commit_id); + + cl_git_fail_with(GIT_EMODIFIED, git_reference_create_matching(&ref, g_repo, refname, &other_id, 1, &other_id, NULL, NULL)); + + cl_git_pass(git_reference_lookup(&ref, g_repo, refname)); + cl_git_pass(git_reference_create_matching(&ref2, g_repo, refname, &other_id, 1, &id, NULL, NULL)); + cl_git_fail_with(GIT_EMODIFIED, git_reference_set_target(&ref3, ref, &other_id, NULL, NULL)); + + git_reference_free(ref); + git_reference_free(ref2); + git_reference_free(ref3); +} + +void test_refs_races__symbolic_create_matching(void) +{ + git_reference *ref, *ref2, *ref3; + git_oid id, other_id; + + git_oid_fromstr(&id, commit_id); + git_oid_fromstr(&other_id, other_commit_id); + + cl_git_fail_with(GIT_EMODIFIED, git_reference_symbolic_create_matching(&ref, g_repo, "HEAD", other_refname, 1, other_refname, NULL, NULL)); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD")); + cl_git_pass(git_reference_symbolic_create_matching(&ref2, g_repo, "HEAD", other_refname, 1, NULL, NULL, refname)); + cl_git_fail_with(GIT_EMODIFIED, git_reference_symbolic_set_target(&ref3, ref, other_refname, NULL, NULL)); + + git_reference_free(ref); + git_reference_free(ref2); + git_reference_free(ref3); +} + +void test_refs_races__delete(void) +{ + git_reference *ref, *ref2; + git_oid id, other_id; + + git_oid_fromstr(&id, commit_id); + git_oid_fromstr(&other_id, other_commit_id); + + /* We can delete a value that matches */ + cl_git_pass(git_reference_lookup(&ref, g_repo, refname)); + cl_git_pass(git_reference_delete(ref)); + git_reference_free(ref); + + /* We cannot delete a symbolic value that doesn't match */ + cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD")); + cl_git_pass(git_reference_symbolic_create_matching(&ref2, g_repo, "HEAD", other_refname, 1, NULL, NULL, refname)); + cl_git_fail_with(GIT_EMODIFIED, git_reference_delete(ref)); + + git_reference_free(ref); + git_reference_free(ref2); + + cl_git_pass(git_reference_create(&ref, g_repo, refname, &id, 1, NULL, NULL)); + git_reference_free(ref); + + /* We cannot delete an oid value that doesn't match */ + cl_git_pass(git_reference_lookup(&ref, g_repo, refname)); + cl_git_pass(git_reference_create_matching(&ref2, g_repo, refname, &other_id, 1, &id, NULL, NULL)); + cl_git_fail_with(GIT_EMODIFIED, git_reference_delete(ref)); + + git_reference_free(ref); + git_reference_free(ref2); +} + +void test_refs_races__switch_oid_to_symbolic(void) +{ + git_reference *ref, *ref2, *ref3; + git_oid id, other_id; + + git_oid_fromstr(&id, commit_id); + git_oid_fromstr(&other_id, other_commit_id); + + /* Removing a direct ref when it's currently symbolic should fail */ + cl_git_pass(git_reference_lookup(&ref, g_repo, refname)); + cl_git_pass(git_reference_symbolic_create(&ref2, g_repo, refname, other_refname, 1, NULL, NULL)); + cl_git_fail_with(GIT_EMODIFIED, git_reference_delete(ref)); + + git_reference_free(ref); + git_reference_free(ref2); + + cl_git_pass(git_reference_create(&ref, g_repo, refname, &id, 1, NULL, NULL)); + git_reference_free(ref); + + /* Updating a direct ref when it's currently symbolic should fail */ + cl_git_pass(git_reference_lookup(&ref, g_repo, refname)); + cl_git_pass(git_reference_symbolic_create(&ref2, g_repo, refname, other_refname, 1, NULL, NULL)); + cl_git_fail_with(GIT_EMODIFIED, git_reference_set_target(&ref3, ref, &other_id, NULL, NULL)); + + git_reference_free(ref); + git_reference_free(ref2); + git_reference_free(ref3); +} + +void test_refs_races__switch_symbolic_to_oid(void) +{ + git_reference *ref, *ref2, *ref3; + git_oid id, other_id; + + git_oid_fromstr(&id, commit_id); + git_oid_fromstr(&other_id, other_commit_id); + + /* Removing a symbolic ref when it's currently direct should fail */ + cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD")); + cl_git_pass(git_reference_create(&ref2, g_repo, "HEAD", &id, 1, NULL, NULL)); + cl_git_fail_with(GIT_EMODIFIED, git_reference_delete(ref)); + + git_reference_free(ref); + git_reference_free(ref2); + + cl_git_pass(git_reference_symbolic_create(&ref, g_repo, "HEAD", refname, 1, NULL, NULL)); + git_reference_free(ref); + + /* Updating a symbolic ref when it's currently direct should fail */ + cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD")); + cl_git_pass(git_reference_create(&ref2, g_repo, "HEAD", &id, 1, NULL, NULL)); + cl_git_fail_with(GIT_EMODIFIED, git_reference_symbolic_set_target(&ref3, ref, other_refname, NULL, NULL)); + + git_reference_free(ref); + git_reference_free(ref2); + git_reference_free(ref3); +} diff -Nru libgit2-0.20.0/tests/refs/read.c libgit2-0.22.2/tests/refs/read.c --- libgit2-0.20.0/tests/refs/read.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/read.c 2015-03-24 16:10:45.000000000 +0000 @@ -83,7 +83,7 @@ cl_assert(git_object_type(object) == GIT_OBJ_COMMIT); git_oid_fromstr(&id, current_master_tip); - cl_assert(git_oid_cmp(&id, git_object_id(object)) == 0); + cl_assert_equal_oid(&id, git_object_id(object)); git_object_free(object); @@ -111,7 +111,7 @@ cl_assert(git_object_type(object) == GIT_OBJ_COMMIT); git_oid_fromstr(&id, current_master_tip); - cl_assert(git_oid_cmp(&id, git_object_id(object)) == 0); + cl_assert_equal_oid(&id, git_object_id(object)); git_object_free(object); @@ -130,13 +130,13 @@ cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE)); cl_git_pass(git_reference_resolve(&resolved_ref, reference)); - cl_git_pass(git_oid_cmp(git_reference_target(comp_base_ref), git_reference_target(resolved_ref))); + cl_assert_equal_oid(git_reference_target(comp_base_ref), git_reference_target(resolved_ref)); git_reference_free(reference); git_reference_free(resolved_ref); cl_git_pass(git_reference_lookup(&reference, g_repo, current_head_target)); cl_git_pass(git_reference_resolve(&resolved_ref, reference)); - cl_git_pass(git_oid_cmp(git_reference_target(comp_base_ref), git_reference_target(resolved_ref))); + cl_assert_equal_oid(git_reference_target(comp_base_ref), git_reference_target(resolved_ref)); git_reference_free(reference); git_reference_free(resolved_ref); @@ -152,7 +152,7 @@ cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE)); cl_git_pass(git_reference_resolve(&resolved_ref, reference)); - cl_git_pass(git_oid_cmp(git_reference_target(master_ref), git_reference_target(resolved_ref))); + cl_assert_equal_oid(git_reference_target(master_ref), git_reference_target(resolved_ref)); git_reference_free(reference); git_reference_free(resolved_ref); @@ -201,7 +201,7 @@ cl_git_pass(git_reference_lookup(&test, g_repo, "refs/heads/test")); cl_git_pass(git_reference_lookup(&chomped, g_repo, "refs/heads/chomped")); - cl_git_pass(git_oid_cmp(git_reference_target(test), git_reference_target(chomped))); + cl_assert_equal_oid(git_reference_target(test), git_reference_target(chomped)); git_reference_free(test); git_reference_free(chomped); @@ -213,7 +213,7 @@ cl_git_pass(git_reference_lookup(&test, g_repo, "refs/heads/test")); cl_git_pass(git_reference_lookup(&trailing, g_repo, "refs/heads/trailing")); - cl_git_pass(git_oid_cmp(git_reference_target(test), git_reference_target(trailing))); + cl_assert_equal_oid(git_reference_target(test), git_reference_target(trailing)); git_reference_free(trailing); cl_git_pass(git_reference_lookup(&trailing, g_repo, "FETCH_HEAD")); @@ -271,6 +271,21 @@ assert_is_tag("refs/remotes/test/master", false); } +static void assert_is_note(const char *name, bool expected_noteness) +{ + git_reference *reference; + cl_git_pass(git_reference_lookup(&reference, g_repo, name)); + cl_assert_equal_i(expected_noteness, git_reference_is_note(reference)); + git_reference_free(reference); +} + +void test_refs_read__can_determine_if_a_reference_is_a_note(void) +{ + assert_is_note("refs/notes/fanout", true); + assert_is_note("refs/heads/packed", false); + assert_is_note("refs/remotes/test/master", false); +} + void test_refs_read__invalid_name_returns_EINVALIDSPEC(void) { git_reference *reference; diff -Nru libgit2-0.20.0/tests/refs/reflog/reflog.c libgit2-0.22.2/tests/refs/reflog/reflog.c --- libgit2-0.20.0/tests/refs/reflog/reflog.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/reflog/reflog.c 2015-03-24 16:10:45.000000000 +0000 @@ -49,17 +49,20 @@ /* Read and parse the reflog for this branch */ cl_git_pass(git_reflog_read(&reflog, repo2, new_ref)); - cl_assert_equal_i(2, (int)git_reflog_entrycount(reflog)); + cl_assert_equal_i(3, (int)git_reflog_entrycount(reflog)); + + /* The first one was the creation of the branch */ + entry = git_reflog_entry_byindex(reflog, 2); + cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0); entry = git_reflog_entry_byindex(reflog, 1); assert_signature(committer, entry->committer); - cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_HEX_ZERO) == 0); + cl_assert(git_oid_cmp(oid, &entry->oid_old) == 0); cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0); cl_assert(entry->msg == NULL); entry = git_reflog_entry_byindex(reflog, 0); assert_signature(committer, entry->committer); - cl_assert(git_oid_cmp(oid, &entry->oid_old) == 0); cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0); cl_assert_equal_s(commit_msg, entry->msg); @@ -79,7 +82,7 @@ /* Create a new branch pointing at the HEAD */ git_oid_fromstr(&oid, current_master_tip); - cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &oid, 0)); + cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &oid, 0, NULL, NULL)); git_reference_free(ref); cl_git_pass(git_signature_now(&committer, "foo", "foo@bar")); @@ -97,29 +100,6 @@ git_signature_free(committer); } -void test_refs_reflog_reflog__append_to_then_read(void) -{ - /* write a reflog for a given reference and ensure it can be read back */ - git_reference *ref; - git_oid oid; - git_signature *committer; - - /* Create a new branch pointing at the HEAD */ - git_oid_fromstr(&oid, current_master_tip); - cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &oid, 0)); - git_reference_free(ref); - - cl_git_pass(git_signature_now(&committer, "foo", "foo@bar")); - - cl_git_fail(git_reflog_append_to(g_repo, new_ref, &oid, committer, "no inner\nnewline")); - cl_git_pass(git_reflog_append_to(g_repo, new_ref, &oid, committer, NULL)); - cl_git_pass(git_reflog_append_to(g_repo, new_ref, &oid, committer, commit_msg "\n")); - - assert_appends(committer, &oid); - - git_signature_free(committer); -} - void test_refs_reflog_reflog__renaming_the_reference_moves_the_reflog(void) { git_reference *master, *new_master; @@ -134,7 +114,7 @@ cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&moved_log_path))); cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master")); - cl_git_pass(git_reference_rename(&new_master, master, "refs/moved", 0)); + cl_git_pass(git_reference_rename(&new_master, master, "refs/moved", 0, NULL, NULL)); git_reference_free(master); cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&master_log_path))); @@ -147,13 +127,7 @@ static void assert_has_reflog(bool expected_result, const char *name) { - git_reference *ref; - - cl_git_pass(git_reference_lookup(&ref, g_repo, name)); - - cl_assert_equal_i(expected_result, git_reference_has_log(ref)); - - git_reference_free(ref); + cl_assert_equal_i(expected_result, git_reference_has_log(g_repo, name)); } void test_refs_reflog_reflog__reference_has_reflog(void) @@ -191,7 +165,7 @@ cl_git_pass(git_reflog_write(reflog)); - cl_git_pass(git_reference_rename(&new_master, master, "refs/moved", 0)); + cl_git_pass(git_reference_rename(&new_master, master, "refs/moved", 0, NULL, NULL)); git_reference_free(master); cl_git_fail(git_reflog_write(reflog)); @@ -207,3 +181,156 @@ cl_assert_equal_i(GIT_EINVALIDSPEC, git_reflog_rename(g_repo, "refs/heads/master", "refs/heads/Inv@{id")); } + +void test_refs_reflog_reflog__write_only_std_locations(void) +{ + git_reference *ref; + git_oid id; + + git_oid_fromstr(&id, current_master_tip); + + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/foo", &id, 1, NULL, NULL)); + git_reference_free(ref); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/tags/foo", &id, 1, NULL, NULL)); + git_reference_free(ref); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/notes/foo", &id, 1, NULL, NULL)); + git_reference_free(ref); + + assert_has_reflog(true, "refs/heads/foo"); + assert_has_reflog(false, "refs/tags/foo"); + assert_has_reflog(true, "refs/notes/foo"); + +} + +void test_refs_reflog_reflog__write_when_explicitly_active(void) +{ + git_reference *ref; + git_oid id; + + git_oid_fromstr(&id, current_master_tip); + git_reference_ensure_log(g_repo, "refs/tags/foo"); + + cl_git_pass(git_reference_create(&ref, g_repo, "refs/tags/foo", &id, 1, NULL, NULL)); + git_reference_free(ref); + assert_has_reflog(true, "refs/tags/foo"); +} + +void test_refs_reflog_reflog__append_to_HEAD_when_changing_current_branch(void) +{ + size_t nlogs, nlogs_after; + git_reference *ref; + git_reflog *log; + git_oid id; + + cl_git_pass(git_reflog_read(&log, g_repo, "HEAD")); + nlogs = git_reflog_entrycount(log); + git_reflog_free(log); + + /* Move it back */ + git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/master", &id, 1, NULL, NULL)); + git_reference_free(ref); + + cl_git_pass(git_reflog_read(&log, g_repo, "HEAD")); + nlogs_after = git_reflog_entrycount(log); + git_reflog_free(log); + + cl_assert_equal_i(nlogs_after, nlogs + 1); +} + +void test_refs_reflog_reflog__do_not_append_when_no_update(void) +{ + size_t nlogs, nlogs_after; + git_reference *ref, *ref2; + git_reflog *log; + + cl_git_pass(git_reflog_read(&log, g_repo, "HEAD")); + nlogs = git_reflog_entrycount(log); + git_reflog_free(log); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master")); + cl_git_pass(git_reference_create(&ref2, g_repo, "refs/heads/master", + git_reference_target(ref), 1, NULL, NULL)); + + git_reference_free(ref); + git_reference_free(ref2); + + cl_git_pass(git_reflog_read(&log, g_repo, "HEAD")); + nlogs_after = git_reflog_entrycount(log); + git_reflog_free(log); + + cl_assert_equal_i(nlogs_after, nlogs); +} + +static void assert_no_reflog_update(void) +{ + size_t nlogs, nlogs_after; + size_t nlogs_master, nlogs_master_after; + git_reference *ref; + git_reflog *log; + git_oid id; + + cl_git_pass(git_reflog_read(&log, g_repo, "HEAD")); + nlogs = git_reflog_entrycount(log); + git_reflog_free(log); + + cl_git_pass(git_reflog_read(&log, g_repo, "refs/heads/master")); + nlogs_master = git_reflog_entrycount(log); + git_reflog_free(log); + + /* Move it back */ + git_oid_fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"); + cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/master", &id, 1, NULL, NULL)); + git_reference_free(ref); + + cl_git_pass(git_reflog_read(&log, g_repo, "HEAD")); + nlogs_after = git_reflog_entrycount(log); + git_reflog_free(log); + + cl_assert_equal_i(nlogs_after, nlogs); + + cl_git_pass(git_reflog_read(&log, g_repo, "refs/heads/master")); + nlogs_master_after = git_reflog_entrycount(log); + git_reflog_free(log); + + cl_assert_equal_i(nlogs_after, nlogs); + cl_assert_equal_i(nlogs_master_after, nlogs_master); + +} + +void test_refs_reflog_reflog__logallrefupdates_bare_set_false(void) +{ + git_config *config; + + cl_git_pass(git_repository_config(&config, g_repo)); + cl_git_pass(git_config_set_bool(config, "core.logallrefupdates", false)); + git_config_free(config); + + assert_no_reflog_update(); +} + +void test_refs_reflog_reflog__logallrefupdates_bare_unset(void) +{ + git_config *config; + + cl_git_pass(git_repository_config(&config, g_repo)); + cl_git_pass(git_config_delete_entry(config, "core.logallrefupdates")); + git_config_free(config); + + assert_no_reflog_update(); +} + +void test_refs_reflog_reflog__logallrefupdates_nonbare_set_false(void) +{ + git_config *config; + + cl_git_sandbox_cleanup(); + g_repo = cl_git_sandbox_init("testrepo"); + + + cl_git_pass(git_repository_config(&config, g_repo)); + cl_git_pass(git_config_set_bool(config, "core.logallrefupdates", false)); + git_config_free(config); + + assert_no_reflog_update(); +} diff -Nru libgit2-0.20.0/tests/refs/rename.c libgit2-0.22.2/tests/refs/rename.c --- libgit2-0.20.0/tests/refs/rename.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/rename.c 2015-03-24 16:10:45.000000000 +0000 @@ -49,7 +49,7 @@ cl_assert(reference_is_packed(looked_up_ref) == 0); /* Now that the reference is renamed... */ - cl_git_pass(git_reference_rename(&new_ref, looked_up_ref, new_name, 0)); + cl_git_pass(git_reference_rename(&new_ref, looked_up_ref, new_name, 0, NULL, NULL)); cl_assert_equal_s(new_ref->name, new_name); git_reference_free(looked_up_ref); @@ -91,7 +91,7 @@ cl_assert(reference_is_packed(looked_up_ref) != 0); /* Now that the reference is renamed... */ - cl_git_pass(git_reference_rename(&new_ref, looked_up_ref, brand_new_name, 0)); + cl_git_pass(git_reference_rename(&new_ref, looked_up_ref, brand_new_name, 0, NULL, NULL)); cl_assert_equal_s(new_ref->name, brand_new_name); git_reference_free(looked_up_ref); @@ -140,7 +140,7 @@ cl_assert(reference_is_packed(looked_up_ref) != 0); /* Now that the reference is renamed... */ - cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, brand_new_name, 0)); + cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, brand_new_name, 0, NULL, NULL)); git_reference_free(looked_up_ref); /* Lookup the other reference */ @@ -166,7 +166,7 @@ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name)); /* Can not be renamed to the name of another existing reference. */ - cl_git_fail(git_reference_rename(&renamed_ref, looked_up_ref, packed_test_head_name, 0)); + cl_git_fail(git_reference_rename(&renamed_ref, looked_up_ref, packed_test_head_name, 0, NULL, NULL)); git_reference_free(looked_up_ref); /* Failure to rename it hasn't corrupted its state */ @@ -187,12 +187,12 @@ /* Can not be renamed with an invalid name. */ cl_assert_equal_i( GIT_EINVALIDSPEC, - git_reference_rename(&renamed_ref, looked_up_ref, "Hello! I'm a very invalid name.", 0)); + git_reference_rename(&renamed_ref, looked_up_ref, "Hello! I'm a very invalid name.", 0, NULL, NULL)); /* Can not be renamed outside of the refs hierarchy * unless it's ALL_CAPS_AND_UNDERSCORES. */ - cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_rename(&renamed_ref, looked_up_ref, "i-will-sudo-you", 0)); + cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_rename(&renamed_ref, looked_up_ref, "i-will-sudo-you", 0, NULL, NULL)); /* Failure to rename it hasn't corrupted its state */ git_reference_free(looked_up_ref); @@ -213,14 +213,14 @@ git_oid_cpy(&oid, git_reference_target(looked_up_ref)); /* Can be force-renamed to the name of another existing reference. */ - cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, packed_test_head_name, 1)); + cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, packed_test_head_name, 1, NULL, NULL)); git_reference_free(looked_up_ref); git_reference_free(renamed_ref); /* Check we actually renamed it */ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name)); cl_assert_equal_s(looked_up_ref->name, packed_test_head_name); - cl_assert(!git_oid_cmp(&oid, git_reference_target(looked_up_ref))); + cl_assert_equal_oid(&oid, git_reference_target(looked_up_ref)); git_reference_free(looked_up_ref); /* And that the previous one doesn't exist any longer */ @@ -238,14 +238,14 @@ git_oid_cpy(&oid, git_reference_target(looked_up_ref)); /* Can be force-renamed to the name of another existing reference. */ - cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, "refs/heads/test", 1)); + cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, "refs/heads/test", 1, NULL, NULL)); git_reference_free(looked_up_ref); git_reference_free(renamed_ref); /* Check we actually renamed it */ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, "refs/heads/test")); cl_assert_equal_s(looked_up_ref->name, "refs/heads/test"); - cl_assert(!git_oid_cmp(&oid, git_reference_target(looked_up_ref))); + cl_assert_equal_oid(&oid, git_reference_target(looked_up_ref)); git_reference_free(looked_up_ref); /* And that the previous one doesn't exist any longer */ @@ -268,15 +268,15 @@ git_oid_cpy(&id, git_reference_target(ref)); /* Create loose references */ - cl_git_pass(git_reference_create(&ref_one, g_repo, ref_one_name, &id, 0)); - cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name, &id, 0)); + cl_git_pass(git_reference_create(&ref_one, g_repo, ref_one_name, &id, 0, NULL, NULL)); + cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name, &id, 0, NULL, NULL)); /* Pack everything */ cl_git_pass(git_repository_refdb(&refdb, g_repo)); cl_git_pass(git_refdb_compress(refdb)); /* Attempt to create illegal reference */ - cl_git_fail(git_reference_create(&ref_one_new, g_repo, ref_one_name_new, &id, 0)); + cl_git_fail(git_reference_create(&ref_one_new, g_repo, ref_one_name_new, &id, 0, NULL, NULL)); /* Illegal reference couldn't be created so this is supposed to fail */ cl_git_fail(git_reference_lookup(&ref_one_new, g_repo, ref_one_name_new)); @@ -301,13 +301,13 @@ git_oid_cpy(&id, git_reference_target(ref)); /* Create loose references */ - cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name, &id, 0)); + cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name, &id, 0, NULL, NULL)); /* An existing reference... */ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name)); /* Can be rename to a new name starting with the old name. */ - cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, ref_two_name_new, 0)); + cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, ref_two_name_new, 0, NULL, NULL)); git_reference_free(looked_up_ref); git_reference_free(renamed_ref); @@ -334,14 +334,14 @@ git_oid_cpy(&id, git_reference_target(ref)); /* Create loose references */ - cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name_new, &id, 0)); + cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name_new, &id, 0, NULL, NULL)); git_reference_free(ref_two); /* An existing reference... */ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new)); /* Can be renamed upward the reference tree. */ - cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, ref_two_name, 0)); + cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, ref_two_name, 0, NULL, NULL)); git_reference_free(looked_up_ref); git_reference_free(renamed_ref); @@ -361,7 +361,30 @@ cl_git_pass(git_reference_lookup(&ref, g_repo, packed_head_name)); - cl_assert_equal_i(GIT_EEXISTS, git_reference_rename(&new_ref, ref, packed_test_head_name, 0)); + cl_assert_equal_i(GIT_EEXISTS, git_reference_rename(&new_ref, ref, packed_test_head_name, 0, NULL, NULL)); 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; + git_signature *sig; + + cl_git_pass(git_signature_now(&sig, "me", "foo@example.com")); + + 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, + sig, "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); + git_signature_free(sig); +} diff -Nru libgit2-0.20.0/tests/refs/revparse.c libgit2-0.22.2/tests/refs/revparse.c --- libgit2-0.20.0/tests/refs/revparse.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/revparse.c 2015-03-24 16:10:45.000000000 +0000 @@ -24,11 +24,11 @@ error = git_revparse_ext(&obj, &ref, repo, spec); if (expected_oid != NULL) { - cl_assert_equal_i(0, error); + cl_git_pass(error); git_oid_fmt(objstr, git_object_id(obj)); cl_assert_equal_s(objstr, expected_oid); } else - cl_assert_equal_i(GIT_ENOTFOUND, error); + cl_git_fail(error); if (assert_reference_retrieval) { if (expected_refname == NULL) @@ -222,7 +222,7 @@ assert_invalid_single_spec("wrapped_tag^{trip}"); test_object("point_to_blob^{commit}", NULL); cl_assert_equal_i( - GIT_EAMBIGUOUS, git_revparse_single(&g_obj, g_repo, "wrapped_tag^{blob}")); + GIT_EPEEL, git_revparse_single(&g_obj, g_repo, "wrapped_tag^{blob}")); test_object("wrapped_tag^{commit}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); test_object("wrapped_tag^{tree}", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162"); @@ -325,7 +325,7 @@ cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&log_path))); cl_git_pass(git_reference_lookup(&master, repo, "refs/heads/master")); - cl_git_pass(git_reference_rename(&new_master, master, "refs/fakestash", 0)); + cl_git_pass(git_reference_rename(&new_master, master, "refs/fakestash", 0, NULL, NULL)); git_reference_free(master); cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&log_path))); @@ -596,7 +596,8 @@ repo, "refs/remotes/origin/bim_with_3d@11296", git_reference_target(head), - 0)); + 0, + NULL, NULL)); cl_git_pass(git_revparse_single(&target, repo, "origin/bim_with_3d@11296")); git_object_free(target); @@ -633,7 +634,7 @@ test_object_inrepo("blah-7-gc47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd", repo); cl_git_pass(git_revparse_single(&target, repo, "HEAD~3")); - cl_git_pass(git_branch_create(&branch, repo, "blah-7-gc47800c", (git_commit *)target, 0)); + cl_git_pass(git_branch_create(&branch, repo, "blah-7-gc47800c", (git_commit *)target, 0, NULL, NULL)); git_oid_tostr(sha, GIT_OID_HEXSZ + 1, git_object_id(target)); @@ -671,7 +672,7 @@ test_object_inrepo("a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo); cl_git_pass(git_revparse_single(&target, repo, "HEAD~3")); - cl_git_pass(git_branch_create(&branch, repo, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", (git_commit *)target, 0)); + cl_git_pass(git_branch_create(&branch, repo, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", (git_commit *)target, 0, NULL, NULL)); git_oid_tostr(sha, GIT_OID_HEXSZ + 1, git_object_id(target)); @@ -707,7 +708,7 @@ test_object_inrepo("c47800", "c47800c7266a2be04c571c04d5a6614691ea99bd", repo); cl_git_pass(git_revparse_single(&target, repo, "HEAD~3")); - cl_git_pass(git_branch_create(&branch, repo, "c47800", (git_commit *)target, 0)); + cl_git_pass(git_branch_create(&branch, repo, "c47800", (git_commit *)target, 0, NULL, NULL)); git_oid_tostr(sha, GIT_OID_HEXSZ + 1, git_object_id(target)); diff -Nru libgit2-0.20.0/tests/refs/settargetwithlog.c libgit2-0.22.2/tests/refs/settargetwithlog.c --- libgit2-0.20.0/tests/refs/settargetwithlog.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/refs/settargetwithlog.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,55 @@ +#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_signature *signature; + 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_signature_now(&signature, "foo", "foo@bar")); + + cl_git_pass(git_reference_set_target( + &reference_out, reference, &target_id, signature, 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); + git_signature_free(signature); +} diff -Nru libgit2-0.20.0/tests/refs/setter.c libgit2-0.22.2/tests/refs/setter.c --- libgit2-0.20.0/tests/refs/setter.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/setter.c 2015-03-24 16:10:45.000000000 +0000 @@ -34,14 +34,14 @@ cl_git_pass(git_reference_lookup(&test_ref, g_repo, ref_test_name)); cl_assert(git_reference_type(test_ref) == GIT_REF_OID); - cl_git_pass(git_reference_set_target(&new_ref, test_ref, &id)); + cl_git_pass(git_reference_set_target(&new_ref, test_ref, &id, NULL, NULL)); git_reference_free(test_ref); git_reference_free(new_ref); cl_git_pass(git_reference_lookup(&test_ref, g_repo, ref_test_name)); cl_assert(git_reference_type(test_ref) == GIT_REF_OID); - cl_assert(git_oid_cmp(&id, git_reference_target(test_ref)) == 0); + cl_assert_equal_oid(&id, git_reference_target(test_ref)); git_reference_free(test_ref); } @@ -53,7 +53,7 @@ cl_assert(git_reference_type(head) == GIT_REF_SYMBOLIC); cl_assert(strcmp(git_reference_symbolic_target(head), ref_master_name) == 0); - cl_git_pass(git_reference_symbolic_set_target(&new_head, head, ref_test_name)); + cl_git_pass(git_reference_symbolic_set_target(&new_head, head, ref_test_name, NULL, NULL)); git_reference_free(new_head); git_reference_free(head); @@ -73,7 +73,7 @@ cl_assert(git_reference_type(ref) == GIT_REF_OID); git_oid_cpy(&id, git_reference_target(ref)); - cl_git_fail(git_reference_symbolic_set_target(&new, ref, ref_name)); + cl_git_fail(git_reference_symbolic_set_target(&new, ref, ref_name, NULL, NULL)); git_reference_free(ref); } @@ -90,10 +90,10 @@ git_reference_free(ref); /* Create the symbolic ref */ - cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0)); + cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0, NULL, NULL)); /* Can't set an OID on a direct ref */ - cl_git_fail(git_reference_set_target(&new, ref, &id)); + cl_git_fail(git_reference_set_target(&new, ref, &id, NULL, NULL)); git_reference_free(ref); } diff -Nru libgit2-0.20.0/tests/refs/transactions.c libgit2-0.22.2/tests/refs/transactions.c --- libgit2-0.20.0/tests/refs/transactions.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/refs/transactions.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,110 @@ +#include "clar_libgit2.h" +#include "git2/transaction.h" + +static git_repository *g_repo; +static git_transaction *g_tx; + +void test_refs_transactions__initialize(void) +{ + g_repo = cl_git_sandbox_init("testrepo"); + cl_git_pass(git_transaction_new(&g_tx, g_repo)); +} + +void test_refs_transactions__cleanup(void) +{ + git_transaction_free(g_tx); + cl_git_sandbox_cleanup(); +} + +void test_refs_transactions__single_ref_oid(void) +{ + git_reference *ref; + git_oid id; + + git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + + cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master")); + cl_git_pass(git_transaction_set_target(g_tx, "refs/heads/master", &id, NULL, NULL)); + cl_git_pass(git_transaction_commit(g_tx)); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master")); + + cl_assert(!git_oid_cmp(&id, git_reference_target(ref))); + git_reference_free(ref); +} + +void test_refs_transactions__single_ref_symbolic(void) +{ + git_reference *ref; + + cl_git_pass(git_transaction_lock_ref(g_tx, "HEAD")); + cl_git_pass(git_transaction_set_symbolic_target(g_tx, "HEAD", "refs/heads/foo", NULL, NULL)); + cl_git_pass(git_transaction_commit(g_tx)); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD")); + + cl_assert_equal_s("refs/heads/foo", git_reference_symbolic_target(ref)); + git_reference_free(ref); +} + +void test_refs_transactions__single_ref_mix_types(void) +{ + git_reference *ref; + git_oid id; + + git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + + cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master")); + cl_git_pass(git_transaction_lock_ref(g_tx, "HEAD")); + cl_git_pass(git_transaction_set_symbolic_target(g_tx, "refs/heads/master", "refs/heads/foo", NULL, NULL)); + cl_git_pass(git_transaction_set_target(g_tx, "HEAD", &id, NULL, NULL)); + cl_git_pass(git_transaction_commit(g_tx)); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master")); + cl_assert_equal_s("refs/heads/foo", git_reference_symbolic_target(ref)); + git_reference_free(ref); + + cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD")); + cl_assert(!git_oid_cmp(&id, git_reference_target(ref))); + git_reference_free(ref); +} + +void test_refs_transactions__single_ref_delete(void) +{ + git_reference *ref; + + cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master")); + cl_git_pass(git_transaction_remove(g_tx, "refs/heads/master")); + cl_git_pass(git_transaction_commit(g_tx)); + + cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo, "refs/heads/master")); +} + +void test_refs_transactions__single_create(void) +{ + git_reference *ref; + const char *name = "refs/heads/new-branch"; + git_oid id; + + cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo, name)); + + cl_git_pass(git_transaction_lock_ref(g_tx, name)); + + git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + cl_git_pass(git_transaction_set_target(g_tx, name, &id, NULL, NULL)); + cl_git_pass(git_transaction_commit(g_tx)); + + cl_git_pass(git_reference_lookup(&ref, g_repo, name)); + cl_assert(!git_oid_cmp(&id, git_reference_target(ref))); + git_reference_free(ref); +} + +void test_refs_transactions__unlocked_set(void) +{ + git_oid id; + + cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master")); + git_oid_fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"); + cl_git_fail_with(GIT_ENOTFOUND, git_transaction_set_target(g_tx, "refs/heads/foo", &id, NULL, NULL)); + cl_git_pass(git_transaction_commit(g_tx)); +} diff -Nru libgit2-0.20.0/tests/refs/unicode.c libgit2-0.22.2/tests/refs/unicode.c --- libgit2-0.20.0/tests/refs/unicode.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/unicode.c 2015-03-24 16:10:45.000000000 +0000 @@ -24,7 +24,7 @@ /* Create the reference */ cl_git_pass(git_reference_lookup(&ref0, repo, master)); cl_git_pass(git_reference_create( - &ref1, repo, REFNAME, git_reference_target(ref0), 0)); + &ref1, repo, REFNAME, git_reference_target(ref0), 0, NULL, NULL)); cl_assert_equal_s(REFNAME, git_reference_name(ref1)); git_reference_free(ref0); @@ -32,8 +32,7 @@ cl_git_pass(git_repository_open(&repo2, "testrepo.git")); cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME)); - cl_assert_equal_i( - 0, git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2))); + cl_assert_equal_oid(git_reference_target(ref1), git_reference_target(ref2)); cl_assert_equal_s(REFNAME, git_reference_name(ref2)); git_reference_free(ref2); @@ -43,8 +42,7 @@ #define REFNAME_DECOMPOSED "refs/heads/" "A" "\314\212" "ngstro" "\314\210" "m" cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME_DECOMPOSED)); - cl_assert_equal_i( - 0, git_oid_cmp(git_reference_target(ref1), git_reference_target(ref2))); + cl_assert_equal_oid(git_reference_target(ref1), git_reference_target(ref2)); cl_assert_equal_s(REFNAME, git_reference_name(ref2)); git_reference_free(ref2); #endif diff -Nru libgit2-0.20.0/tests/refs/update.c libgit2-0.22.2/tests/refs/update.c --- libgit2-0.20.0/tests/refs/update.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/refs/update.c 2015-03-24 16:10:45.000000000 +0000 @@ -22,5 +22,5 @@ cl_assert_equal_i(GIT_REF_SYMBOLIC, git_reference_type(head)); git_reference_free(head); - cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_symbolic_create(&head, g_repo, GIT_HEAD_FILE, "refs/heads/inv@{id", 1)); + cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_symbolic_create(&head, g_repo, GIT_HEAD_FILE, "refs/heads/inv@{id", 1, NULL, NULL)); } diff -Nru libgit2-0.20.0/tests/repo/config.c libgit2-0.22.2/tests/repo/config.c --- libgit2-0.20.0/tests/repo/config.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/repo/config.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,4 +1,5 @@ #include "clar_libgit2.h" +#include "sysdir.h" #include "fileops.h" #include @@ -7,7 +8,8 @@ void test_repo_config__initialize(void) { cl_fixture_sandbox("empty_standard_repo"); - cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git")); + cl_git_pass(cl_rename( + "empty_standard_repo/.gitted", "empty_standard_repo/.git")); git_buf_clear(&path); @@ -17,15 +19,19 @@ void test_repo_config__cleanup(void) { - cl_git_pass(git_path_prettify(&path, "alternate", NULL)); - cl_git_pass(git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES)); + cl_sandbox_set_search_path_defaults(); + git_buf_free(&path); + + cl_git_pass( + git_futils_rmdir_r("alternate", NULL, GIT_RMDIR_REMOVE_FILES)); cl_assert(!git_path_isdir("alternate")); cl_fixture_cleanup("empty_standard_repo"); + } -void test_repo_config__open_missing_global(void) +void test_repo_config__can_open_global_when_there_is_no_file(void) { git_repository *repo; git_config *config, *global; @@ -39,23 +45,23 @@ cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); cl_git_pass(git_repository_config(&config, repo)); - cl_git_pass(git_config_open_level(&global, config, GIT_CONFIG_LEVEL_GLOBAL)); + cl_git_pass(git_config_open_level( + &global, config, GIT_CONFIG_LEVEL_GLOBAL)); cl_git_pass(git_config_set_string(global, "test.set", "42")); git_config_free(global); git_config_free(config); git_repository_free(repo); - - git_futils_dirs_global_shutdown(); } -void test_repo_config__open_missing_global_with_separators(void) +void test_repo_config__can_open_missing_global_with_separators(void) { git_repository *repo; git_config *config, *global; - cl_git_pass(git_buf_printf(&path, "%c%s", GIT_PATH_LIST_SEPARATOR, "dummy")); + cl_git_pass(git_buf_printf( + &path, "%c%s", GIT_PATH_LIST_SEPARATOR, "dummy")); cl_git_pass(git_libgit2_opts( GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr)); @@ -68,20 +74,19 @@ cl_git_pass(git_repository_open(&repo, "empty_standard_repo")); cl_git_pass(git_repository_config(&config, repo)); - cl_git_pass(git_config_open_level(&global, config, GIT_CONFIG_LEVEL_GLOBAL)); + cl_git_pass(git_config_open_level( + &global, config, GIT_CONFIG_LEVEL_GLOBAL)); cl_git_pass(git_config_set_string(global, "test.set", "42")); git_config_free(global); git_config_free(config); git_repository_free(repo); - - git_futils_dirs_global_shutdown(); } #include "repository.h" -void test_repo_config__read_no_configs(void) +void test_repo_config__read_with_no_configs_at_all(void) { git_repository *repo; int val; @@ -105,9 +110,9 @@ cl_assert_equal_i(GIT_ABBREV_DEFAULT, val); git_repository_free(repo); - git_futils_dirs_global_shutdown(); + /* with no local config, just system */ - /* with just system */ + cl_sandbox_set_search_path_defaults(); cl_must_pass(p_mkdir("alternate/1", 0777)); cl_git_pass(git_buf_joinpath(&path, path.ptr, "1")); @@ -122,7 +127,7 @@ cl_assert_equal_i(10, val); git_repository_free(repo); - /* with xdg + system */ + /* with just xdg + system */ cl_must_pass(p_mkdir("alternate/2", 0777)); path.ptr[path.size - 1] = '2'; @@ -203,6 +208,4 @@ cl_assert(!git_path_exists("empty_standard_repo/.git/config")); cl_assert(!git_path_exists("alternate/3/.gitconfig")); - - git_futils_dirs_global_shutdown(); } diff -Nru libgit2-0.20.0/tests/repo/discover.c libgit2-0.22.2/tests/repo/discover.c --- libgit2-0.20.0/tests/repo/discover.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/repo/discover.c 2015-03-24 16:10:45.000000000 +0000 @@ -25,12 +25,13 @@ static void ensure_repository_discover(const char *start_path, const char *ceiling_dirs, - const char *expected_path) + git_buf *expected_path) { - char found_path[GIT_PATH_MAX]; - cl_git_pass(git_repository_discover(found_path, sizeof(found_path), start_path, 0, ceiling_dirs)); + git_buf found_path = GIT_BUF_INIT; + cl_git_pass(git_repository_discover(&found_path, start_path, 0, ceiling_dirs)); //across_fs is always 0 as we can't automate the filesystem change tests - cl_assert_equal_s(found_path, expected_path); + cl_assert_equal_s(found_path.ptr, expected_path->ptr); + git_buf_free(&found_path); } static void write_file(const char *path, const char *content) @@ -69,42 +70,40 @@ void test_repo_discover__0(void) { - // test discover + // test discover git_repository *repo; - git_buf ceiling_dirs_buf = GIT_BUF_INIT; + git_buf ceiling_dirs_buf = GIT_BUF_INIT, repository_path = GIT_BUF_INIT, + sub_repository_path = GIT_BUF_INIT, found_path = GIT_BUF_INIT; const char *ceiling_dirs; - char repository_path[GIT_PATH_MAX]; - char sub_repository_path[GIT_PATH_MAX]; - char found_path[GIT_PATH_MAX]; const mode_t mode = 0777; git_futils_mkdir_r(DISCOVER_FOLDER, NULL, mode); append_ceiling_dir(&ceiling_dirs_buf, TEMP_REPO_FOLDER); ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf); - cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(repository_path, sizeof(repository_path), DISCOVER_FOLDER, 0, ceiling_dirs)); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&repository_path, DISCOVER_FOLDER, 0, ceiling_dirs)); cl_git_pass(git_repository_init(&repo, DISCOVER_FOLDER, 1)); - cl_git_pass(git_repository_discover(repository_path, sizeof(repository_path), DISCOVER_FOLDER, 0, ceiling_dirs)); + cl_git_pass(git_repository_discover(&repository_path, DISCOVER_FOLDER, 0, ceiling_dirs)); git_repository_free(repo); cl_git_pass(git_repository_init(&repo, SUB_REPOSITORY_FOLDER, 0)); cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, NULL, mode)); - cl_git_pass(git_repository_discover(sub_repository_path, sizeof(sub_repository_path), SUB_REPOSITORY_FOLDER, 0, ceiling_dirs)); + cl_git_pass(git_repository_discover(&sub_repository_path, SUB_REPOSITORY_FOLDER, 0, ceiling_dirs)); cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, NULL, mode)); - ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs, sub_repository_path); - ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path); - ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, ceiling_dirs, sub_repository_path); + ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs, &sub_repository_path); + ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB, ceiling_dirs, &sub_repository_path); + ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, ceiling_dirs, &sub_repository_path); cl_git_pass(git_futils_mkdir_r(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, NULL, mode)); write_file(REPOSITORY_ALTERNATE_FOLDER "/" DOT_GIT, "gitdir: ../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT); write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/" DOT_GIT, "gitdir: ../../../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT); write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB "/" DOT_GIT, "gitdir: ../../../../"); - ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs, sub_repository_path); - ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs, sub_repository_path); - ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path); - ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, repository_path); + ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs, &sub_repository_path); + ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs, &sub_repository_path); + ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, &sub_repository_path); + ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, &repository_path); cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER1, NULL, mode)); write_file(ALTERNATE_MALFORMED_FOLDER1 "/" DOT_GIT, "Anything but not gitdir:"); @@ -114,29 +113,31 @@ write_file(ALTERNATE_MALFORMED_FOLDER3 "/" DOT_GIT, "gitdir: \n\n\n"); cl_git_pass(git_futils_mkdir_r(ALTERNATE_NOT_FOUND_FOLDER, NULL, mode)); write_file(ALTERNATE_NOT_FOUND_FOLDER "/" DOT_GIT, "gitdir: a_repository_that_surely_does_not_exist"); - cl_git_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER1, 0, ceiling_dirs)); - cl_git_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER2, 0, ceiling_dirs)); - cl_git_fail(git_repository_discover(found_path, sizeof(found_path), ALTERNATE_MALFORMED_FOLDER3, 0, ceiling_dirs)); - cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(found_path, sizeof(found_path), ALTERNATE_NOT_FOUND_FOLDER, 0, ceiling_dirs)); + cl_git_fail(git_repository_discover(&found_path, ALTERNATE_MALFORMED_FOLDER1, 0, ceiling_dirs)); + cl_git_fail(git_repository_discover(&found_path, ALTERNATE_MALFORMED_FOLDER2, 0, ceiling_dirs)); + cl_git_fail(git_repository_discover(&found_path, ALTERNATE_MALFORMED_FOLDER3, 0, ceiling_dirs)); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, ALTERNATE_NOT_FOUND_FOLDER, 0, ceiling_dirs)); append_ceiling_dir(&ceiling_dirs_buf, SUB_REPOSITORY_FOLDER); ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf); //this must pass as ceiling_directories cannot predent the current //working directory to be checked - cl_git_pass(git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER, 0, ceiling_dirs)); - cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER_SUB, 0, ceiling_dirs)); - cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs)); - cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(found_path, sizeof(found_path), SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs)); + cl_git_pass(git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER, 0, ceiling_dirs)); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB, 0, ceiling_dirs)); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs)); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&found_path, SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs)); //.gitfile redirection should not be affected by ceiling directories - ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs, sub_repository_path); - ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs, sub_repository_path); - ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, sub_repository_path); - ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, repository_path); + ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs, &sub_repository_path); + ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs, &sub_repository_path); + ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs, &sub_repository_path); + ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs, &repository_path); cl_git_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, NULL, GIT_RMDIR_REMOVE_FILES)); git_repository_free(repo); git_buf_free(&ceiling_dirs_buf); + git_buf_free(&repository_path); + git_buf_free(&sub_repository_path); } diff -Nru libgit2-0.20.0/tests/repo/hashfile.c libgit2-0.22.2/tests/repo/hashfile.c --- libgit2-0.20.0/tests/repo/hashfile.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/repo/hashfile.c 2015-03-24 16:10:45.000000000 +0000 @@ -22,14 +22,14 @@ /* hash with repo relative path */ cl_git_pass(git_odb_hashfile(&a, "status/current_file", GIT_OBJ_BLOB)); cl_git_pass(git_repository_hashfile(&b, _repo, "current_file", GIT_OBJ_BLOB, NULL)); - cl_assert(git_oid_equal(&a, &b)); + cl_assert_equal_oid(&a, &b); cl_git_pass(git_buf_joinpath(&full, git_repository_workdir(_repo), "current_file")); /* hash with full path */ cl_git_pass(git_odb_hashfile(&a, full.ptr, GIT_OBJ_BLOB)); cl_git_pass(git_repository_hashfile(&b, _repo, full.ptr, GIT_OBJ_BLOB, NULL)); - cl_assert(git_oid_equal(&a, &b)); + cl_assert_equal_oid(&a, &b); /* hash with invalid type */ cl_git_fail(git_odb_hashfile(&a, full.ptr, GIT_OBJ_ANY)); @@ -58,12 +58,12 @@ /* equal hashes because filter is binary */ cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJ_BLOB)); cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJ_BLOB, NULL)); - cl_assert(git_oid_equal(&a, &b)); + cl_assert_equal_oid(&a, &b); /* equal hashes when 'as_file' points to binary filtering */ cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJ_BLOB)); cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJ_BLOB, "foo.bin")); - cl_assert(git_oid_equal(&a, &b)); + cl_assert_equal_oid(&a, &b); /* not equal hashes when 'as_file' points to text filtering */ cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJ_BLOB)); @@ -73,11 +73,11 @@ /* equal hashes when 'as_file' is empty and turns off filtering */ cl_git_pass(git_odb_hashfile(&a, "status/testfile.txt", GIT_OBJ_BLOB)); cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJ_BLOB, "")); - cl_assert(git_oid_equal(&a, &b)); + cl_assert_equal_oid(&a, &b); cl_git_pass(git_odb_hashfile(&a, "status/testfile.bin", GIT_OBJ_BLOB)); cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJ_BLOB, "")); - cl_assert(git_oid_equal(&a, &b)); + cl_assert_equal_oid(&a, &b); /* some hash type failures */ cl_git_fail(git_odb_hashfile(&a, "status/testfile.txt", 0)); diff -Nru libgit2-0.20.0/tests/repo/head.c libgit2-0.22.2/tests/repo/head.c --- libgit2-0.20.0/tests/repo/head.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/repo/head.c 2015-03-24 16:10:45.000000000 +0000 @@ -15,21 +15,42 @@ 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; + git_signature *sig; - cl_git_pass(git_repository_head_detached(repo)); + cl_git_pass(git_signature_now(&sig, "Foo Bar", "foo@example.com")); - cl_git_pass(git_repository_detach_head(repo)); + cl_assert_equal_i(false, git_repository_head_detached(repo)); + cl_git_pass(git_repository_detach_head(repo, sig, "CABLE DETACHED")); + check_last_reflog_entry(sig->email, "CABLE DETACHED"); cl_assert_equal_i(true, git_repository_head_detached(repo)); - /* take the reop back to it's original state */ - cl_git_pass(git_reference_symbolic_create(&ref, repo, "HEAD", "refs/heads/master", 1)); + /* take the repo back to it's original state */ + cl_git_pass(git_reference_symbolic_create(&ref, repo, "HEAD", "refs/heads/master", + true, sig, "REATTACH")); git_reference_free(ref); + check_last_reflog_entry(sig->email, "REATTACH"); cl_assert_equal_i(false, git_repository_head_detached(repo)); + git_signature_free(sig); } void test_repo_head__unborn_head(void) @@ -44,7 +65,7 @@ /* take the repo back to it's original state */ - cl_git_pass(git_reference_symbolic_create(&ref, repo, "HEAD", "refs/heads/master", 1)); + cl_git_pass(git_reference_symbolic_create(&ref, repo, "HEAD", "refs/heads/master", 1, NULL, NULL)); cl_assert(git_repository_head_unborn(repo) == 0); git_reference_free(ref); @@ -54,7 +75,7 @@ { git_reference *head; - cl_git_pass(git_repository_set_head(repo, "refs/heads/doesnt/exist/yet")); + cl_git_pass(git_repository_set_head(repo, "refs/heads/doesnt/exist/yet", NULL, NULL)); cl_assert_equal_i(false, git_repository_head_detached(repo)); @@ -63,19 +84,19 @@ void test_repo_head__set_head_Returns_ENOTFOUND_when_the_reference_doesnt_exist(void) { - cl_assert_equal_i(GIT_ENOTFOUND, git_repository_set_head(repo, "refs/tags/doesnt/exist/yet")); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_set_head(repo, "refs/tags/doesnt/exist/yet", NULL, NULL)); } void test_repo_head__set_head_Fails_when_the_reference_points_to_a_non_commitish(void) { - cl_git_fail(git_repository_set_head(repo, "refs/tags/point_to_blob")); + cl_git_fail(git_repository_set_head(repo, "refs/tags/point_to_blob", NULL, NULL)); } void test_repo_head__set_head_Attaches_HEAD_when_the_reference_points_to_a_branch(void) { git_reference *head; - cl_git_pass(git_repository_set_head(repo, "refs/heads/br2")); + cl_git_pass(git_repository_set_head(repo, "refs/heads/br2", NULL, NULL)); cl_assert_equal_i(false, git_repository_head_detached(repo)); @@ -102,7 +123,7 @@ void test_repo_head__set_head_Detaches_HEAD_when_the_reference_doesnt_point_to_a_branch(void) { - cl_git_pass(git_repository_set_head(repo, "refs/tags/test")); + cl_git_pass(git_repository_set_head(repo, "refs/tags/test", NULL, NULL)); cl_assert_equal_i(true, git_repository_head_detached(repo)); @@ -115,7 +136,7 @@ cl_git_pass(git_oid_fromstr(&oid, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef")); - cl_assert_equal_i(GIT_ENOTFOUND, git_repository_set_head_detached(repo, &oid)); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_set_head_detached(repo, &oid, NULL, NULL)); } void test_repo_head__set_head_detached_Fails_when_the_object_isnt_a_commitish(void) @@ -124,7 +145,7 @@ cl_git_pass(git_revparse_single(&blob, repo, "point_to_blob")); - cl_git_fail(git_repository_set_head_detached(repo, git_object_id(blob))); + cl_git_fail(git_repository_set_head_detached(repo, git_object_id(blob), NULL, NULL)); git_object_free(blob); } @@ -136,7 +157,7 @@ cl_git_pass(git_revparse_single(&tag, repo, "tags/test")); cl_assert_equal_i(GIT_OBJ_TAG, git_object_type(tag)); - cl_git_pass(git_repository_set_head_detached(repo, git_object_id(tag))); + cl_git_pass(git_repository_set_head_detached(repo, git_object_id(tag), NULL, NULL)); assert_head_is_correctly_detached(); @@ -147,7 +168,7 @@ { cl_assert_equal_i(false, git_repository_head_detached(repo)); - cl_git_pass(git_repository_detach_head(repo)); + cl_git_pass(git_repository_detach_head(repo, NULL, NULL)); assert_head_is_correctly_detached(); } @@ -156,9 +177,9 @@ { git_reference *head; - cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, "refs/tags/point_to_blob", 1)); + cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, "refs/tags/point_to_blob", 1, NULL, NULL)); - cl_git_fail(git_repository_detach_head(repo)); + cl_git_fail(git_repository_detach_head(repo, NULL, NULL)); git_reference_free(head); } @@ -167,7 +188,7 @@ { make_head_unborn(repo, NON_EXISTING_HEAD); - cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_detach_head(repo)); + cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_detach_head(repo, NULL, NULL)); } void test_repo_head__retrieving_an_unborn_branch_returns_GIT_EUNBORNBRANCH(void) @@ -194,3 +215,255 @@ 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; + + cl_git_pass(git_signature_now(&sig, "me", "foo@example.com")); + + cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked", sig, "message1")); + cl_git_pass(git_repository_set_head(repo, "refs/heads/unborn", sig, "message2")); + cl_git_pass(git_revparse_single(&tag, repo, "tags/test")); + cl_git_pass(git_repository_set_head_detached(repo, git_object_id(tag), sig, "message3")); + cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked", sig, "message4")); + + test_reflog(repo, 2, NULL, "refs/heads/haacked", "foo@example.com", "message1"); + test_reflog(repo, 1, NULL, "tags/test^{commit}", "foo@example.com", "message3"); + test_reflog(repo, 0, "tags/test^{commit}", "refs/heads/haacked", "foo@example.com", "message4"); + + 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 = "message1"; + git_oid_fromstr(&id, "e90810b8df3e80c413d903f631643c716887138d"); + cl_git_pass(git_repository_set_head_detached(repo, &id, sig, msg)); + assert_head_reflog(repo, 0, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "e90810b8df3e80c413d903f631643c716887138d", msg); + + msg = "message2"; + cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked", sig, msg)); + assert_head_reflog(repo, 0, "e90810b8df3e80c413d903f631643c716887138d", + "258f0e2a959a364e40ed6603d5d44fbb24765b10", msg); + + git_signature_free(sig); +} + +void test_repo_head__orphan_branch_does_not_count(void) +{ + git_signature *sig; + git_oid id; + const char *msg; + + cl_git_pass(git_signature_now(&sig, "me", "foo@example.com")); + + /* Have something known */ + msg = "message1"; + git_oid_fromstr(&id, "e90810b8df3e80c413d903f631643c716887138d"); + cl_git_pass(git_repository_set_head_detached(repo, &id, sig, msg)); + 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", sig, "ignored message")); + assert_head_reflog(repo, 0, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", + "e90810b8df3e80c413d903f631643c716887138d", msg); + + /* And coming back, we set the source to zero */ + msg = "message2"; + cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked", sig, msg)); + assert_head_reflog(repo, 0, "0000000000000000000000000000000000000000", + "258f0e2a959a364e40ed6603d5d44fbb24765b10", msg); + + git_signature_free(sig); +} + +void test_repo_head__set_to_current_target(void) +{ + git_signature *sig; + 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")); + + msg = "message 1"; + cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked", sig, msg)); + cl_git_pass(git_repository_set_head(repo, "refs/heads/haacked", sig, msg)); + + 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); + +} + +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); + + msg = "message 1"; + cl_git_pass(git_repository_set_head(repo, "refs/heads/orphan", sig, msg)); + + 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, sig, 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); + +} diff -Nru libgit2-0.20.0/tests/repo/headtree.c libgit2-0.22.2/tests/repo/headtree.c --- libgit2-0.20.0/tests/repo/headtree.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/repo/headtree.c 2015-03-24 16:10:45.000000000 +0000 @@ -20,7 +20,7 @@ void test_repo_headtree__can_retrieve_the_root_tree_from_a_detached_head(void) { - cl_git_pass(git_repository_detach_head(repo)); + cl_git_pass(git_repository_detach_head(repo, NULL, NULL)); cl_git_pass(git_repository_head_tree(&tree, repo)); diff -Nru libgit2-0.20.0/tests/repo/init.c libgit2-0.22.2/tests/repo/init.c --- libgit2-0.20.0/tests/repo/init.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/repo/init.c 2015-03-24 16:10:45.000000000 +0000 @@ -358,7 +358,7 @@ cl_assert_equal_s("refs/heads/development", git_reference_symbolic_target(ref)); git_reference_free(ref); - cl_git_pass(git_remote_load(&remote, _repo, "origin")); + cl_git_pass(git_remote_lookup(&remote, _repo, "origin")); cl_assert_equal_s("origin", git_remote_name(remote)); cl_assert_equal_s(opts.origin_url, git_remote_url(remote)); git_remote_free(remote); @@ -367,6 +367,85 @@ cl_fixture_cleanup("root"); } +void test_repo_init__relative_gitdir(void) +{ + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + git_config *cfg; + const char *worktree_path; + git_buf dot_git_content = GIT_BUF_INIT; + + opts.workdir_path = "../c_wd"; + opts.flags = + GIT_REPOSITORY_INIT_MKPATH | + GIT_REPOSITORY_INIT_RELATIVE_GITLINK | + GIT_REPOSITORY_INIT_NO_DOTGIT_DIR; + + /* make the directory first, then it should succeed */ + cl_git_pass(git_repository_init_ext(&_repo, "root/b/my_repository", &opts)); + + cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "root/b/c_wd/")); + cl_assert(!git__suffixcmp(git_repository_path(_repo), "root/b/my_repository/")); + cl_assert(!git_repository_is_bare(_repo)); + cl_assert(git_repository_is_empty(_repo)); + + /* Verify that the gitlink and worktree entries are relative */ + + /* Verify worktree */ + cl_git_pass(git_repository_config(&cfg, _repo)); + cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree")); + cl_assert_equal_s("../c_wd/", worktree_path); + + /* Verify gitlink */ + cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git")); + cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr); + + git_buf_free(&dot_git_content); + git_config_free(cfg); + cleanup_repository("root"); +} + +void test_repo_init__relative_gitdir_2(void) +{ + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + git_config *cfg; + const char *worktree_path; + git_buf dot_git_content = GIT_BUF_INIT; + git_buf full_path = GIT_BUF_INIT; + + cl_git_pass(git_path_prettify(&full_path, ".", NULL)); + cl_git_pass(git_buf_joinpath(&full_path, full_path.ptr, "root/b/c_wd")); + + opts.workdir_path = full_path.ptr; + opts.flags = + GIT_REPOSITORY_INIT_MKPATH | + GIT_REPOSITORY_INIT_RELATIVE_GITLINK | + GIT_REPOSITORY_INIT_NO_DOTGIT_DIR; + + /* make the directory first, then it should succeed */ + cl_git_pass(git_repository_init_ext(&_repo, "root/b/my_repository", &opts)); + git_buf_free(&full_path); + + cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "root/b/c_wd/")); + cl_assert(!git__suffixcmp(git_repository_path(_repo), "root/b/my_repository/")); + cl_assert(!git_repository_is_bare(_repo)); + cl_assert(git_repository_is_empty(_repo)); + + /* Verify that the gitlink and worktree entries are relative */ + + /* Verify worktree */ + cl_git_pass(git_repository_config(&cfg, _repo)); + cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree")); + cl_assert_equal_s("../c_wd/", worktree_path); + + /* Verify gitlink */ + cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git")); + cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr); + + git_buf_free(&dot_git_content); + git_config_free(cfg); + cleanup_repository("root"); +} + #define CLEAR_FOR_CORE_FILEMODE(M) ((M) &= ~0177) static void assert_hooks_match( @@ -431,6 +510,32 @@ GIT_MODE_TYPE(expect_mode), GIT_MODE_TYPE(st.st_mode), "%07o"); } +static const char *template_sandbox(const char *name) +{ + git_buf hooks_path = GIT_BUF_INIT, link_path = GIT_BUF_INIT; + const char *path = cl_fixture(name); + + cl_fixture_sandbox(name); + + /* create a symlink from link.sample to update.sample if the filesystem + * supports it. + */ + + cl_git_pass(git_buf_joinpath(&hooks_path, name, "hooks")); + cl_git_pass(git_buf_joinpath(&link_path, hooks_path.ptr, "link.sample")); + +#ifdef GIT_WIN32 + cl_git_mkfile(link_path.ptr, "#!/bin/sh\necho hello, world\n"); +#else + cl_must_pass(symlink("update.sample", link_path.ptr)); +#endif + + git_buf_free(&link_path); + git_buf_free(&hooks_path); + + return path; +} + void test_repo_init__extended_with_template(void) { git_buf expected = GIT_BUF_INIT; @@ -439,10 +544,11 @@ int filemode; cl_set_cleanup(&cleanup_repository, "templated.git"); + template_sandbox("template"); opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE | GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE; - opts.template_path = cl_fixture("template"); + opts.template_path = "template"; cl_git_pass(git_repository_init_ext(&_repo, "templated.git", &opts)); @@ -450,8 +556,7 @@ cl_assert(!git__suffixcmp(git_repository_path(_repo), "/templated.git/")); - cl_git_pass(git_futils_readbuffer( - &expected, cl_fixture("template/description"))); + cl_git_pass(git_futils_readbuffer(&expected, "template/description")); cl_git_pass(git_futils_readbuffer( &actual, "templated.git/description")); @@ -463,12 +568,14 @@ filemode = cl_repo_get_bool(_repo, "core.filemode"); assert_hooks_match( - cl_fixture("template"), git_repository_path(_repo), + "template", git_repository_path(_repo), "hooks/update.sample", filemode); assert_hooks_match( - cl_fixture("template"), git_repository_path(_repo), + "template", git_repository_path(_repo), "hooks/link.sample", filemode); + + cl_fixture_cleanup("template"); } void test_repo_init__extended_with_template_and_shared_mode(void) @@ -480,10 +587,11 @@ const char *repo_path = NULL; cl_set_cleanup(&cleanup_repository, "init_shared_from_tpl"); + template_sandbox("template"); opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE; - opts.template_path = cl_fixture("template"); + opts.template_path = "template"; opts.mode = GIT_REPOSITORY_INIT_SHARED_GROUP; cl_git_pass(git_repository_init_ext(&_repo, "init_shared_from_tpl", &opts)); @@ -494,7 +602,7 @@ filemode = cl_repo_get_bool(_repo, "core.filemode"); cl_git_pass(git_futils_readbuffer( - &expected, cl_fixture("template/description"))); + &expected, "template/description")); cl_git_pass(git_futils_readbuffer( &actual, "init_shared_from_tpl/.git/description")); @@ -513,15 +621,17 @@ /* for a non-symlinked hook, it should have shared permissions now */ assert_hooks_match( - cl_fixture("template"), git_repository_path(_repo), + "template", git_repository_path(_repo), "hooks/update.sample", filemode); /* for a symlinked hook, the permissions still should match the * source link, not the GIT_REPOSITORY_INIT_SHARED_GROUP value */ assert_hooks_match( - cl_fixture("template"), git_repository_path(_repo), + "template", git_repository_path(_repo), "hooks/link.sample", filemode); + + cl_fixture_cleanup("template"); } void test_repo_init__can_reinit_an_initialized_repository(void) @@ -604,3 +714,29 @@ git_index_free(index); } + +void test_repo_init__at_filesystem_root(void) +{ + git_repository *repo; + const char *sandbox = clar_sandbox_path(); + git_buf root = GIT_BUF_INIT; + int root_len; + + if (!cl_getenv("GITTEST_INVASIVE_FILESYSTEM")) + cl_skip(); + + root_len = git_path_root(sandbox); + cl_assert(root_len >= 0); + + git_buf_put(&root, sandbox, root_len+1); + git_buf_joinpath(&root, root.ptr, "libgit2_test_dir"); + + cl_assert(!git_path_exists(root.ptr)); + + cl_git_pass(git_repository_init(&repo, root.ptr, 0)); + cl_assert(git_path_isdir(root.ptr)); + cl_git_pass(git_futils_rmdir_r(root.ptr, NULL, GIT_RMDIR_REMOVE_FILES)); + + git_buf_free(&root); + git_repository_free(repo); +} diff -Nru libgit2-0.20.0/tests/repo/iterator.c libgit2-0.22.2/tests/repo/iterator.c --- libgit2-0.20.0/tests/repo/iterator.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/repo/iterator.c 2015-03-24 16:10:45.000000000 +0000 @@ -156,7 +156,7 @@ { git_iterator *i; git_index *index; - unsigned int caps; + int caps; g_repo = cl_git_sandbox_init("icase"); @@ -427,7 +427,7 @@ git_buf name = GIT_BUF_INIT; va_list arglist; - cl_git_pass(git_treebuilder_create(&builder, NULL)); /* start builder */ + cl_git_pass(git_treebuilder_new(&builder, repo, NULL)); /* start builder */ va_start(arglist, fmt); while (*scan) { @@ -451,7 +451,7 @@ } va_end(arglist); - cl_git_pass(git_treebuilder_write(out, repo, builder)); + cl_git_pass(git_treebuilder_write(out, builder)); git_treebuilder_free(builder); git_buf_free(&name); @@ -665,19 +665,19 @@ g_repo = cl_git_sandbox_init("icase"); /* auto expand with no tree entries */ - cl_git_pass(git_iterator_for_workdir(&i, g_repo, 0, NULL, NULL)); + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, 0, NULL, NULL)); expect_iterator_items(i, 20, NULL, 20, NULL); git_iterator_free(i); /* auto expand with tree entries */ cl_git_pass(git_iterator_for_workdir( - &i, g_repo, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + &i, g_repo, NULL, NULL, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); expect_iterator_items(i, 22, NULL, 22, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ cl_git_pass(git_iterator_for_workdir( - &i, g_repo, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); + &i, g_repo, NULL, NULL, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); expect_iterator_items(i, 12, NULL, 22, NULL); git_iterator_free(i); } @@ -692,66 +692,66 @@ flag = GIT_ITERATOR_DONT_IGNORE_CASE; /* auto expand with no tree entries */ - cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "c", "k/D")); + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, flag, "c", "k/D")); expect_iterator_items(i, 7, NULL, 7, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "k", "k/Z")); + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, flag, "k", "k/Z")); expect_iterator_items(i, 3, NULL, 3, NULL); git_iterator_free(i); /* auto expand with tree entries */ cl_git_pass(git_iterator_for_workdir( - &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); + &i, g_repo, NULL, NULL, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); expect_iterator_items(i, 8, NULL, 8, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_workdir( - &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); + &i, g_repo, NULL, NULL, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); expect_iterator_items(i, 4, NULL, 4, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ cl_git_pass(git_iterator_for_workdir( - &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); + &i, g_repo, NULL, NULL, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); expect_iterator_items(i, 5, NULL, 8, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_workdir( - &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); + &i, g_repo, NULL, NULL, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); expect_iterator_items(i, 1, NULL, 4, NULL); git_iterator_free(i); flag = GIT_ITERATOR_IGNORE_CASE; /* auto expand with no tree entries */ - cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "c", "k/D")); + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, flag, "c", "k/D")); expect_iterator_items(i, 13, NULL, 13, NULL); git_iterator_free(i); - cl_git_pass(git_iterator_for_workdir(&i, g_repo, flag, "k", "k/Z")); + cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, flag, "k", "k/Z")); expect_iterator_items(i, 5, NULL, 5, NULL); git_iterator_free(i); /* auto expand with tree entries */ cl_git_pass(git_iterator_for_workdir( - &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); + &i, g_repo, NULL, NULL, flag | GIT_ITERATOR_INCLUDE_TREES, "c", "k/D")); expect_iterator_items(i, 14, NULL, 14, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_workdir( - &i, g_repo, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); + &i, g_repo, NULL, NULL, flag | GIT_ITERATOR_INCLUDE_TREES, "k", "k/Z")); expect_iterator_items(i, 6, NULL, 6, NULL); git_iterator_free(i); /* no auto expand (implies trees included) */ cl_git_pass(git_iterator_for_workdir( - &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); + &i, g_repo, NULL, NULL, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "c", "k/D")); expect_iterator_items(i, 9, NULL, 14, NULL); git_iterator_free(i); cl_git_pass(git_iterator_for_workdir( - &i, g_repo, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); + &i, g_repo, NULL, NULL, flag | GIT_ITERATOR_DONT_AUTOEXPAND, "k", "k/Z")); expect_iterator_items(i, 1, NULL, 6, NULL); git_iterator_free(i); } @@ -804,13 +804,13 @@ build_workdir_tree("icase/dir02/sUB01", 50, 0); /* auto expand with no tree entries */ - cl_git_pass(git_iterator_for_workdir(&iter, g_repo, 0, NULL, NULL)); + cl_git_pass(git_iterator_for_workdir(&iter, g_repo, NULL, NULL, 0, NULL, NULL)); expect_iterator_items(iter, 125, NULL, 125, NULL); git_iterator_free(iter); /* auto expand with tree entries (empty dirs silently skipped) */ cl_git_pass(git_iterator_for_workdir( - &iter, g_repo, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); + &iter, g_repo, NULL, NULL, GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); expect_iterator_items(iter, 337, NULL, 337, NULL); git_iterator_free(iter); } @@ -960,3 +960,35 @@ git_iterator_free(i); } + +void test_repo_iterator__skips_fifos_and_such(void) +{ +#ifndef GIT_WIN32 + git_iterator *i; + const git_index_entry *e; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + + cl_must_pass(p_mkdir("empty_standard_repo/dir", 0777)); + cl_git_mkfile("empty_standard_repo/file", "not me"); + + cl_assert(!mkfifo("empty_standard_repo/fifo", 0777)); + cl_assert(!access("empty_standard_repo/fifo", F_OK)); + + cl_git_pass(git_iterator_for_filesystem( + &i, "empty_standard_repo", GIT_ITERATOR_INCLUDE_TREES | + GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); + + cl_git_pass(git_iterator_advance(&e, i)); /* .git */ + cl_assert(S_ISDIR(e->mode)); + cl_git_pass(git_iterator_advance(&e, i)); /* dir */ + cl_assert(S_ISDIR(e->mode)); + /* skips fifo */ + cl_git_pass(git_iterator_advance(&e, i)); /* file */ + cl_assert(S_ISREG(e->mode)); + + cl_assert_equal_i(GIT_ITEROVER, git_iterator_advance(&e, i)); + + git_iterator_free(i); +#endif +} diff -Nru libgit2-0.20.0/tests/repo/message.c libgit2-0.22.2/tests/repo/message.c --- libgit2-0.20.0/tests/repo/message.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/repo/message.c 2015-03-24 16:10:45.000000000 +0000 @@ -4,49 +4,36 @@ #include "posix.h" static git_repository *_repo; -static git_buf _path; -static char *_actual; void test_repo_message__initialize(void) { - _repo = cl_git_sandbox_init("testrepo.git"); + _repo = cl_git_sandbox_init("testrepo.git"); } void test_repo_message__cleanup(void) { - cl_git_sandbox_cleanup(); - git_buf_free(&_path); - git__free(_actual); - _actual = NULL; + cl_git_sandbox_cleanup(); } void test_repo_message__none(void) { - cl_assert_equal_i(GIT_ENOTFOUND, git_repository_message(NULL, 0, _repo)); + git_buf actual = GIT_BUF_INIT; + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_message(&actual, _repo)); } void test_repo_message__message(void) { + git_buf path = GIT_BUF_INIT, actual = GIT_BUF_INIT; const char expected[] = "Test\n\nThis is a test of the emergency broadcast system\n"; - ssize_t len; - cl_git_pass(git_buf_joinpath(&_path, git_repository_path(_repo), "MERGE_MSG")); - cl_git_mkfile(git_buf_cstr(&_path), expected); + cl_git_pass(git_buf_joinpath(&path, git_repository_path(_repo), "MERGE_MSG")); + cl_git_mkfile(git_buf_cstr(&path), expected); - len = git_repository_message(NULL, 0, _repo); - cl_assert(len > 0); - - _actual = git__malloc(len + 1); - cl_assert(_actual != NULL); - - /* Test non truncation */ - cl_assert(git_repository_message(_actual, len, _repo) > 0); - cl_assert_equal_s(expected, _actual); - - /* Test truncation and that trailing NUL is inserted */ - cl_assert(git_repository_message(_actual, 6, _repo) > 0); - cl_assert_equal_s("Test\n", _actual); - - cl_git_pass(p_unlink(git_buf_cstr(&_path))); - cl_assert_equal_i(GIT_ENOTFOUND, git_repository_message(NULL, 0, _repo)); + cl_git_pass(git_repository_message(&actual, _repo)); + cl_assert_equal_s(expected, git_buf_cstr(&actual)); + git_buf_free(&actual); + + cl_git_pass(p_unlink(git_buf_cstr(&path))); + cl_assert_equal_i(GIT_ENOTFOUND, git_repository_message(&actual, _repo)); + git_buf_free(&path); } diff -Nru libgit2-0.20.0/tests/repo/open.c libgit2-0.22.2/tests/repo/open.c --- libgit2-0.20.0/tests/repo/open.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/repo/open.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "fileops.h" +#include "sysdir.h" #include void test_repo_open__cleanup(void) @@ -101,14 +102,13 @@ void test_repo_open__from_git_new_workdir(void) { +#ifndef GIT_WIN32 /* The git-new-workdir script that ships with git sets up a bunch of * symlinks to create a second workdir that shares the object db with * another checkout. Libgit2 can open a repo that has been configured * this way. */ - cl_git_sandbox_init("empty_standard_repo"); -#ifndef GIT_WIN32 git_repository *repo2; git_buf link_tgt = GIT_BUF_INIT, link = GIT_BUF_INIT, body = GIT_BUF_INIT; const char **scan; @@ -121,6 +121,8 @@ "HEAD", NULL }; + cl_git_sandbox_init("empty_standard_repo"); + cl_git_pass(p_mkdir("alternate", 0777)); cl_git_pass(p_mkdir("alternate/.git", 0777)); @@ -296,7 +298,8 @@ git_config *config; cl_fixture_sandbox("empty_standard_repo"); - cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git")); + cl_git_pass(cl_rename( + "empty_standard_repo/.gitted", "empty_standard_repo/.git")); /* remove local config */ cl_git_pass(git_futils_rmdir_r( @@ -323,7 +326,7 @@ git_repository_free(repo); cl_fixture_cleanup("empty_standard_repo"); - git_futils_dirs_global_shutdown(); + cl_sandbox_set_search_path_defaults(); } void test_repo_open__force_bare(void) diff -Nru libgit2-0.20.0/tests/repo/pathspec.c libgit2-0.22.2/tests/repo/pathspec.c --- libgit2-0.20.0/tests/repo/pathspec.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/repo/pathspec.c 2015-03-24 16:10:45.000000000 +0000 @@ -167,7 +167,7 @@ cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps)); cl_assert_equal_sz(13, git_pathspec_match_list_entrycount(m)); - cl_assert_equal_s("这", git_pathspec_match_list_entry(m, 12)); + cl_assert_equal_s("\xE8\xBF\x99", git_pathspec_match_list_entry(m, 12)); git_pathspec_match_list_free(m); git_pathspec_free(ps); diff -Nru libgit2-0.20.0/tests/repo/repo_helpers.c libgit2-0.22.2/tests/repo/repo_helpers.c --- libgit2-0.20.0/tests/repo/repo_helpers.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/repo/repo_helpers.c 2015-03-24 16:10:45.000000000 +0000 @@ -7,7 +7,7 @@ { git_reference *head; - cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, target, 1)); + cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, target, 1, NULL, NULL)); git_reference_free(head); } diff -Nru libgit2-0.20.0/tests/repo/shallow.c libgit2-0.22.2/tests/repo/shallow.c --- libgit2-0.20.0/tests/repo/shallow.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/repo/shallow.c 2015-03-24 16:10:45.000000000 +0000 @@ -31,3 +31,9 @@ cl_assert_equal_i(1, git_repository_is_shallow(g_repo)); } +void test_repo_shallow__clears_errors(void) +{ + g_repo = cl_git_sandbox_init("testrepo.git"); + cl_assert_equal_i(0, git_repository_is_shallow(g_repo)); + cl_assert_equal_p(NULL, giterr_last()); +} diff -Nru libgit2-0.20.0/tests/repo/state.c libgit2-0.22.2/tests/repo/state.c --- libgit2-0.20.0/tests/repo/state.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/repo/state.c 2015-03-24 16:10:45.000000000 +0000 @@ -37,7 +37,7 @@ void test_repo_state__none_with_HEAD_detached(void) { - cl_git_pass(git_repository_detach_head(_repo)); + cl_git_pass(git_repository_detach_head(_repo, NULL, NULL)); assert_repo_state(GIT_REPOSITORY_STATE_NONE); } @@ -45,52 +45,70 @@ { setup_simple_state(GIT_MERGE_HEAD_FILE); assert_repo_state(GIT_REPOSITORY_STATE_MERGE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } void test_repo_state__revert(void) { setup_simple_state(GIT_REVERT_HEAD_FILE); assert_repo_state(GIT_REPOSITORY_STATE_REVERT); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } void test_repo_state__cherry_pick(void) { - setup_simple_state(GIT_CHERRY_PICK_HEAD_FILE); - assert_repo_state(GIT_REPOSITORY_STATE_CHERRY_PICK); + setup_simple_state(GIT_CHERRYPICK_HEAD_FILE); + assert_repo_state(GIT_REPOSITORY_STATE_CHERRYPICK); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } void test_repo_state__bisect(void) { setup_simple_state(GIT_BISECT_LOG_FILE); assert_repo_state(GIT_REPOSITORY_STATE_BISECT); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } void test_repo_state__rebase_interactive(void) { setup_simple_state(GIT_REBASE_MERGE_INTERACTIVE_FILE); assert_repo_state(GIT_REPOSITORY_STATE_REBASE_INTERACTIVE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } void test_repo_state__rebase_merge(void) { setup_simple_state(GIT_REBASE_MERGE_DIR "whatever"); assert_repo_state(GIT_REPOSITORY_STATE_REBASE_MERGE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } void test_repo_state__rebase(void) { setup_simple_state(GIT_REBASE_APPLY_REBASING_FILE); assert_repo_state(GIT_REPOSITORY_STATE_REBASE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } void test_repo_state__apply_mailbox(void) { setup_simple_state(GIT_REBASE_APPLY_APPLYING_FILE); assert_repo_state(GIT_REPOSITORY_STATE_APPLY_MAILBOX); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } void test_repo_state__apply_mailbox_or_rebase(void) { setup_simple_state(GIT_REBASE_APPLY_DIR "whatever"); assert_repo_state(GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE); + cl_git_pass(git_repository_state_cleanup(_repo)); + assert_repo_state(GIT_REPOSITORY_STATE_NONE); } diff -Nru libgit2-0.20.0/tests/reset/default.c libgit2-0.22.2/tests/reset/default.c --- libgit2-0.20.0/tests/reset/default.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/reset/default.c 2015-03-24 16:10:45.000000000 +0000 @@ -21,7 +21,6 @@ void test_reset_default__initialize(void) { - initialize("status"); } void test_reset_default__cleanup(void) @@ -57,7 +56,7 @@ if (!expected_shas) continue; - cl_git_pass(git_oid_streq(&entry->oid, expected_shas->strings[i])); + cl_git_pass(git_oid_streq(&entry->id, expected_shas->strings[i])); } else cl_assert_equal_i(should_exist, error != GIT_ENOTFOUND); } @@ -67,6 +66,8 @@ { char *paths[] = { "staged_changes", "staged_new_file" }; + initialize("status"); + _pathspecs.strings = paths; _pathspecs.count = 2; @@ -102,6 +103,8 @@ char *after_shas[] = { "32504b727382542f9f089e24fddac5e78533e96c", "061d42a44cacde5726057b67558821d95db96f19" }; + initialize("status"); + _pathspecs.strings = paths; _pathspecs.count = 2; before.strings = before_shas; @@ -139,7 +142,6 @@ char *paths[] = { "conflicts-one.txt" }; char *after_shas[] = { "1f85ca51b8e0aac893a621b61a9c2661d6aa6d81" }; - test_reset_default__cleanup(); initialize("mergedrepo"); _pathspecs.strings = paths; @@ -168,6 +170,8 @@ { char *paths[] = { "I_am_not_there.txt", "me_neither.txt" }; + initialize("status"); + _pathspecs.strings = paths; _pathspecs.count = 2; @@ -178,3 +182,31 @@ assert_content_in_index(&_pathspecs, false, NULL); } + +void test_reset_default__staged_rename_reset_delete(void) +{ + git_index_entry entry; + const git_index_entry *existing; + char *paths[] = { "new.txt" }; + + initialize("testrepo2"); + + existing = git_index_get_bypath(_index, "new.txt", 0); + cl_assert(existing); + memcpy(&entry, existing, sizeof(entry)); + + cl_git_pass(git_index_remove_bypath(_index, "new.txt")); + + entry.path = "renamed.txt"; + cl_git_pass(git_index_add(_index, &entry)); + + _pathspecs.strings = paths; + _pathspecs.count = 1; + + assert_content_in_index(&_pathspecs, false, NULL); + + cl_git_pass(git_revparse_single(&_target, _repo, "HEAD")); + cl_git_pass(git_reset_default(_repo, _target, &_pathspecs)); + + assert_content_in_index(&_pathspecs, true, NULL); +} diff -Nru libgit2-0.20.0/tests/reset/hard.c libgit2-0.22.2/tests/reset/hard.c --- libgit2-0.20.0/tests/reset/hard.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/reset/hard.c 2015-03-24 16:10:45.000000000 +0000 @@ -69,10 +69,9 @@ cl_assert_equal_s(before[i], content.ptr); } - retrieve_target_from_oid( - &target, repo, "26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f"); + cl_git_pass(git_revparse_single(&target, repo, "26a125e")); - cl_git_pass(git_reset(repo, target, GIT_RESET_HARD)); + cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL, NULL, NULL)); for (i = 0; i < 4; ++i) { cl_git_pass(git_buf_joinpath(&path, wd, files[i])); @@ -95,9 +94,9 @@ cl_git_pass(git_repository_open(&bare, cl_fixture("testrepo.git"))); cl_assert(git_repository_is_bare(bare) == true); - retrieve_target_from_oid(&target, bare, KNOWN_COMMIT_IN_BARE_REPO); + cl_git_pass(git_revparse_single(&target, bare, KNOWN_COMMIT_IN_BARE_REPO)); - cl_assert_equal_i(GIT_EBAREREPO, git_reset(bare, target, GIT_RESET_HARD)); + cl_assert_equal_i(GIT_EBAREREPO, git_reset(bare, target, GIT_RESET_HARD, NULL, NULL, NULL)); git_repository_free(bare); } @@ -111,7 +110,7 @@ entry.path = "conflicting_file"; entry.flags = (side << GIT_IDXENTRY_STAGESHIFT); entry.mode = 0100644; - git_oid_cpy(&entry.oid, oid); + git_oid_cpy(&entry.id, oid); cl_git_pass(git_index_add(index, &entry)); } @@ -152,8 +151,8 @@ unmerged_index_init(index, entries); cl_git_pass(git_index_write(index)); - retrieve_target_from_oid(&target, repo, "26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f"); - cl_git_pass(git_reset(repo, target, GIT_RESET_HARD)); + cl_git_pass(git_revparse_single(&target, repo, "26a125e")); + cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL, NULL, NULL)); cl_assert(git_path_exists("status/conflicting_file") == 0); @@ -183,8 +182,8 @@ cl_git_pass(git_buf_joinpath(&orig_head_path, git_repository_path(repo), "ORIG_HEAD")); cl_git_mkfile(git_buf_cstr(&orig_head_path), "0017bd4ab1ec30440b17bae1680cff124ab5f1f6"); - retrieve_target_from_oid(&target, repo, "0017bd4ab1ec30440b17bae1680cff124ab5f1f6"); - cl_git_pass(git_reset(repo, target, GIT_RESET_HARD)); + cl_git_pass(git_revparse_single(&target, repo, "0017bd4")); + cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL, NULL, NULL)); cl_assert(!git_path_exists(git_buf_cstr(&merge_head_path))); cl_assert(!git_path_exists(git_buf_cstr(&merge_msg_path))); @@ -198,3 +197,36 @@ git_buf_free(&merge_mode_path); git_buf_free(&orig_head_path); } + +void test_reset_hard__reflog_is_correct(void) +{ + const char *exp_msg = "commit: Add a file which name should appear before the " + "\"subdir/\" folder while being dealt with by the treewalker"; + + reflog_check(repo, "HEAD", 3, "emeric.fermas@gmail.com", exp_msg); + reflog_check(repo, "refs/heads/master", 3, "emeric.fermas@gmail.com", exp_msg); + + /* Branch not moving, no reflog entry */ + cl_git_pass(git_revparse_single(&target, repo, "HEAD^{commit}")); + cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL, NULL, NULL)); + reflog_check(repo, "HEAD", 3, "emeric.fermas@gmail.com", exp_msg); + reflog_check(repo, "refs/heads/master", 3, "emeric.fermas@gmail.com", exp_msg); + + git_object_free(target); + + /* Moved branch, expect default message */ + exp_msg = "reset: moving"; + cl_git_pass(git_revparse_single(&target, repo, "HEAD~^{commit}")); + cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL, NULL, NULL)); + reflog_check(repo, "HEAD", 4, NULL, exp_msg); + reflog_check(repo, "refs/heads/master", 4, NULL, exp_msg); + + git_object_free(target); + + /* Moved branch, expect custom message */ + exp_msg = "message1"; + cl_git_pass(git_revparse_single(&target, repo, "HEAD~^{commit}")); + cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL, NULL, "message1")); + reflog_check(repo, "HEAD", 5, NULL, exp_msg); + reflog_check(repo, "refs/heads/master", 5, NULL, exp_msg); +} diff -Nru libgit2-0.20.0/tests/reset/mixed.c libgit2-0.22.2/tests/reset/mixed.c --- libgit2-0.20.0/tests/reset/mixed.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/reset/mixed.c 2015-03-24 16:10:45.000000000 +0000 @@ -27,9 +27,9 @@ cl_git_pass(git_repository_open(&bare, cl_fixture("testrepo.git"))); cl_assert(git_repository_is_bare(bare) == true); - retrieve_target_from_oid(&target, bare, KNOWN_COMMIT_IN_BARE_REPO); + cl_git_pass(git_revparse_single(&target, bare, KNOWN_COMMIT_IN_BARE_REPO)); - cl_assert_equal_i(GIT_EBAREREPO, git_reset(bare, target, GIT_RESET_MIXED)); + cl_assert_equal_i(GIT_EBAREREPO, git_reset(bare, target, GIT_RESET_MIXED, NULL, NULL, NULL)); git_repository_free(bare); } @@ -40,10 +40,44 @@ cl_git_pass(git_status_file(&status, repo, "macro_bad")); cl_assert(status == GIT_STATUS_CURRENT); - retrieve_target_from_oid(&target, repo, "605812ab7fe421fdd325a935d35cb06a9234a7d7"); + cl_git_pass(git_revparse_single(&target, repo, "605812a")); - cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED)); + cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED, NULL, NULL, NULL)); cl_git_pass(git_status_file(&status, repo, "macro_bad")); cl_assert(status == GIT_STATUS_WT_NEW); } + +void test_reset_mixed__reflog_is_correct(void) +{ + const char *exp_msg = "commit: Updating test data so we can test inter-hunk-context"; + + reflog_check(repo, "HEAD", 9, "yoram.harmelin@gmail.com", exp_msg); + reflog_check(repo, "refs/heads/master", 9, "yoram.harmelin@gmail.com", exp_msg); + + /* Branch not moving, no reflog entry */ + cl_git_pass(git_revparse_single(&target, repo, "HEAD^{commit}")); + cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED, NULL, NULL, NULL)); + reflog_check(repo, "HEAD", 9, "yoram.harmelin@gmail.com", exp_msg); + reflog_check(repo, "refs/heads/master", 9, "yoram.harmelin@gmail.com", exp_msg); + + git_object_free(target); + target = NULL; + + /* Moved branch, expect default message */ + exp_msg = "reset: moving"; + cl_git_pass(git_revparse_single(&target, repo, "HEAD~^{commit}")); + cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED, NULL, NULL, NULL)); + reflog_check(repo, "HEAD", 10, NULL, exp_msg); + reflog_check(repo, "refs/heads/master", 10, NULL, exp_msg); + + git_object_free(target); + target = NULL; + + /* Moved branch, expect custom message */ + exp_msg = "message1"; + cl_git_pass(git_revparse_single(&target, repo, "HEAD~^{commit}")); + cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED, NULL, NULL, "message1")); + reflog_check(repo, "HEAD", 11, NULL, exp_msg); + reflog_check(repo, "refs/heads/master", 11, NULL, exp_msg); +} diff -Nru libgit2-0.20.0/tests/reset/reset_helpers.c libgit2-0.22.2/tests/reset/reset_helpers.c --- libgit2-0.20.0/tests/reset/reset_helpers.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/reset/reset_helpers.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,10 +1,20 @@ #include "clar_libgit2.h" #include "reset_helpers.h" -void retrieve_target_from_oid(git_object **object_out, git_repository *repo, const char *sha) +void reflog_check(git_repository *repo, const char *refname, + size_t exp_count, const char *exp_email, const char *exp_msg) { - git_oid oid; + git_reflog *log; + const git_reflog_entry *entry; - cl_git_pass(git_oid_fromstr(&oid, sha)); - cl_git_pass(git_object_lookup(object_out, repo, &oid, GIT_OBJ_ANY)); + cl_git_pass(git_reflog_read(&log, repo, refname)); + cl_assert_equal_i(exp_count, git_reflog_entrycount(log)); + entry = git_reflog_entry_byindex(log, 0); + + if (exp_email) + cl_assert_equal_s(exp_email, git_reflog_entry_committer(entry)->email); + if (exp_msg) + cl_assert_equal_s(exp_msg, git_reflog_entry_message(entry)); + + git_reflog_free(log); } diff -Nru libgit2-0.20.0/tests/reset/reset_helpers.h libgit2-0.22.2/tests/reset/reset_helpers.h --- libgit2-0.20.0/tests/reset/reset_helpers.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/reset/reset_helpers.h 2015-03-24 16:10:45.000000000 +0000 @@ -3,4 +3,5 @@ #define KNOWN_COMMIT_IN_BARE_REPO "e90810b8df3e80c413d903f631643c716887138d" #define KNOWN_COMMIT_IN_ATTR_REPO "217878ab49e1314388ea2e32dc6fdb58a1b969e0" -extern void retrieve_target_from_oid(git_object **object_out, git_repository *repo, const char *sha); +void reflog_check(git_repository *repo, const char *refname, + size_t exp_count, const char *exp_email, const char *exp_msg); diff -Nru libgit2-0.20.0/tests/reset/soft.c libgit2-0.22.2/tests/reset/soft.c --- libgit2-0.20.0/tests/reset/soft.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/reset/soft.c 2015-03-24 16:10:45.000000000 +0000 @@ -26,12 +26,11 @@ cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD")); cl_git_fail(git_oid_streq(&oid, KNOWN_COMMIT_IN_BARE_REPO)); - - retrieve_target_from_oid(&target, repo, KNOWN_COMMIT_IN_BARE_REPO); + cl_git_pass(git_revparse_single(&target, repo, KNOWN_COMMIT_IN_BARE_REPO)); cl_assert(git_repository_head_detached(repo) == should_be_detached); - cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT)); + cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT, NULL, NULL, NULL)); cl_assert(git_repository_head_detached(repo) == should_be_detached); @@ -46,7 +45,7 @@ void test_reset_soft__can_reset_the_detached_Head_to_the_specified_commit(void) { - git_repository_detach_head(repo); + git_repository_detach_head(repo, NULL, NULL); assert_reset_soft(true); } @@ -60,9 +59,9 @@ git_oid_fmt(raw_head_oid, &oid); raw_head_oid[GIT_OID_HEXSZ] = '\0'; - retrieve_target_from_oid(&target, repo, raw_head_oid); + cl_git_pass(git_revparse_single(&target, repo, raw_head_oid)); - cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT)); + cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT, NULL, NULL, NULL)); cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD")); cl_git_pass(git_oid_streq(&oid, raw_head_oid)); @@ -73,9 +72,9 @@ git_oid oid; /* b25fa35 is a tag, pointing to another tag which points to commit e90810b */ - retrieve_target_from_oid(&target, repo, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1"); + cl_git_pass(git_revparse_single(&target, repo, "b25fa35")); - cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT)); + cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT, NULL, NULL, NULL)); cl_assert(git_repository_head_detached(repo) == false); cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD")); @@ -85,27 +84,27 @@ void test_reset_soft__cannot_reset_to_a_tag_not_pointing_at_a_commit(void) { /* 53fc32d is the tree of commit e90810b */ - retrieve_target_from_oid(&target, repo, "53fc32d17276939fc79ed05badaef2db09990016"); + cl_git_pass(git_revparse_single(&target, repo, "53fc32d")); - cl_git_fail(git_reset(repo, target, GIT_RESET_SOFT)); + cl_git_fail(git_reset(repo, target, GIT_RESET_SOFT, NULL, NULL, NULL)); git_object_free(target); /* 521d87c is an annotated tag pointing to a blob */ - retrieve_target_from_oid(&target, repo, "521d87c1ec3aef9824daf6d96cc0ae3710766d91"); - cl_git_fail(git_reset(repo, target, GIT_RESET_SOFT)); + cl_git_pass(git_revparse_single(&target, repo, "521d87c")); + cl_git_fail(git_reset(repo, target, GIT_RESET_SOFT, NULL, NULL, NULL)); } void test_reset_soft__resetting_against_an_unborn_head_repo_makes_the_head_no_longer_unborn(void) { git_reference *head; - retrieve_target_from_oid(&target, repo, KNOWN_COMMIT_IN_BARE_REPO); + cl_git_pass(git_revparse_single(&target, repo, KNOWN_COMMIT_IN_BARE_REPO)); make_head_unborn(repo, NON_EXISTING_HEAD); cl_assert_equal_i(true, git_repository_head_unborn(repo)); - cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT)); + cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT, NULL, NULL, NULL)); cl_assert_equal_i(false, git_repository_head_unborn(repo)); @@ -119,13 +118,13 @@ { git_buf merge_head_path = GIT_BUF_INIT; - cl_git_pass(git_repository_detach_head(repo)); + cl_git_pass(git_repository_detach_head(repo, NULL, NULL)); cl_git_pass(git_buf_joinpath(&merge_head_path, git_repository_path(repo), "MERGE_HEAD")); cl_git_mkfile(git_buf_cstr(&merge_head_path), "beefbeefbeefbeefbeefbeefbeefbeefbeefbeef\n"); - retrieve_target_from_oid(&target, repo, KNOWN_COMMIT_IN_BARE_REPO); + cl_git_pass(git_revparse_single(&target, repo, KNOWN_COMMIT_IN_BARE_REPO)); - cl_assert_equal_i(GIT_EUNMERGED, git_reset(repo, target, GIT_RESET_SOFT)); + cl_assert_equal_i(GIT_EUNMERGED, git_reset(repo, target, GIT_RESET_SOFT, NULL, NULL, NULL)); cl_git_pass(p_unlink(git_buf_cstr(&merge_head_path))); git_buf_free(&merge_head_path); @@ -153,5 +152,31 @@ cl_git_pass(git_reference_peel(&target, head, GIT_OBJ_COMMIT)); git_reference_free(head); - cl_assert_equal_i(GIT_EUNMERGED, git_reset(repo, target, GIT_RESET_SOFT)); + cl_assert_equal_i(GIT_EUNMERGED, git_reset(repo, target, GIT_RESET_SOFT, NULL, NULL, NULL)); +} + +void test_reset_soft_reflog_is_correct(void) +{ + const char *exp_msg = "commit: Updating test data so we can test inter-hunk-context"; + + reflog_check(repo, "HEAD", 9, "yoram.harmelin@gmail.com", exp_msg); + reflog_check(repo, "refs/heads/master", 9, "yoram.harmelin@gmail.com", exp_msg); + + /* Branch not moving, no reflog entry */ + cl_git_pass(git_revparse_single(&target, repo, "HEAD^{commit}")); + cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT, NULL, NULL, NULL)); + reflog_check(repo, "HEAD", 9, "yoram.harmelin@gmail.com", exp_msg); + reflog_check(repo, "refs/heads/master", 9, "yoram.harmelin@gmail.com", exp_msg); + + /* Moved branch, expect default message */ + cl_git_pass(git_revparse_single(&target, repo, "HEAD~^{commit}")); + cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT, NULL, NULL, NULL)); + reflog_check(repo, "HEAD", 9, "yoram.harmelin@gmail.com", exp_msg); + reflog_check(repo, "refs/heads/master", 10, NULL, "reset: moving"); + + /* Moved branch, expect custom message */ + cl_git_pass(git_revparse_single(&target, repo, "HEAD~^{commit}")); + cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT, NULL, NULL, "message1")); + reflog_check(repo, "HEAD", 9, "yoram.harmelin@gmail.com", exp_msg); + reflog_check(repo, "refs/heads/master", 11, NULL, "message1"); } diff -Nru libgit2-0.20.0/tests/resources/cherrypick/file1.txt libgit2-0.22.2/tests/resources/cherrypick/file1.txt --- libgit2-0.20.0/tests/resources/cherrypick/file1.txt 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/file1.txt 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,15 @@ +!File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 +File 1 diff -Nru libgit2-0.20.0/tests/resources/cherrypick/file2.txt libgit2-0.22.2/tests/resources/cherrypick/file2.txt --- libgit2-0.20.0/tests/resources/cherrypick/file2.txt 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/file2.txt 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,15 @@ +!File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 +File 2 diff -Nru libgit2-0.20.0/tests/resources/cherrypick/file3.txt libgit2-0.22.2/tests/resources/cherrypick/file3.txt --- libgit2-0.20.0/tests/resources/cherrypick/file3.txt 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/file3.txt 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,15 @@ +!File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 +File 3 diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/config libgit2-0.22.2/tests/resources/cherrypick/.gitted/config --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/config 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/config 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,7 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true + precomposeunicode = true diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/HEAD libgit2-0.22.2/tests/resources/cherrypick/.gitted/HEAD --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/HEAD 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/HEAD 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +ref: refs/heads/automerge-branch Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/index and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/index differ diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/info/exclude libgit2-0.22.2/tests/resources/cherrypick/.gitted/info/exclude --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/info/exclude 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/info/exclude 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/01/a2b453c2647c71ccfefc285f2266d1f00b8253 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/01/a2b453c2647c71ccfefc285f2266d1f00b8253 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/02/67838e09bbc5969bba035be2d27c8a6de694d8 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/02/67838e09bbc5969bba035be2d27c8a6de694d8 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/06/3fc9f01e6e9ec2a8d8f749885e931875e50d37 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/06/3fc9f01e6e9ec2a8d8f749885e931875e50d37 differ diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/08/9ac03f76058b5ba0b44bb268f317f9242481e9 libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/08/9ac03f76058b5ba0b44bb268f317f9242481e9 --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/08/9ac03f76058b5ba0b44bb268f317f9242481e9 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/08/9ac03f76058b5ba0b44bb268f317f9242481e9 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +xA +0@Q9i"`LBMӅܾ)籂#բ +^CNb+%bRU!z1Jh)JO}딼 b>WI \qyϟ 祖QҔO`D6{tfm_sy@2("O- \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/0d/447a6c2528b06616cde3b209a4b4ea3dcb8d65 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/0d/447a6c2528b06616cde3b209a4b4ea3dcb8d65 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/11/24c2c1ae07b26fded662d6c3f3631d9dc16f88 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/11/24c2c1ae07b26fded662d6c3f3631d9dc16f88 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/12/905f4ea5b76f9d3fdcfe73e462201c06ae632a and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/12/905f4ea5b76f9d3fdcfe73e462201c06ae632a differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/19/c5c7207054604b69c84d08a7571ef9672bb5c2 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/19/c5c7207054604b69c84d08a7571ef9672bb5c2 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/1c/2116845780455ecf916538c1cc27c4222452af and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/1c/2116845780455ecf916538c1cc27c4222452af differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/1c/c85eb4ff0a8438fde1b14274c6f87f891b36a0 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/1c/c85eb4ff0a8438fde1b14274c6f87f891b36a0 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/1e/1cb7391d25dcd8daba88f1f627f3045982286c and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/1e/1cb7391d25dcd8daba88f1f627f3045982286c differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/20/fc1a4c9d994021f43d33ab75e4252e27ca661d and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/20/fc1a4c9d994021f43d33ab75e4252e27ca661d differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/28/d9eb4208074ad1cc84e71ccc908b34573f05d2 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/28/d9eb4208074ad1cc84e71ccc908b34573f05d2 differ diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/2a/26c7e88b285613b302ba76712bc998863f3cbc libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/2a/26c7e88b285613b302ba76712bc998863f3cbc --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/2a/26c7e88b285613b302ba76712bc998863f3cbc 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/2a/26c7e88b285613b302ba76712bc998863f3cbc 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xMj0)f_j]`$*N1C=@{^YZlLOҙUzub/X1"iuWN9b҄ZS&r4mrY:Qo+6{/{?gҎ`\k-U_u+5Οx9a?7W \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/2a/c3b376093de405b0a951bff578655b1c2b7fa1 libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/2a/c3b376093de405b0a951bff578655b1c2b7fa1 --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/2a/c3b376093de405b0a951bff578655b1c2b7fa1 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/2a/c3b376093de405b0a951bff578655b1c2b7fa1 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xJ0E]+fIwGLk ܞ{VG{כ*A9Ěc:PܔCJBk\2]}jPQD6b95xuO7v}{[c3 ޢԮ#E̻yɚgToPM/X \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/2c/acbcaabf785f1ac231e8519849d4ad38692f2c and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/2c/acbcaabf785f1ac231e8519849d4ad38692f2c differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/35/cb210149022c7379b0a67b0dec13cc628ff87d and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/35/cb210149022c7379b0a67b0dec13cc628ff87d differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/38/c05a857e831a7e759d83778bfc85d003e21c45 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/38/c05a857e831a7e759d83778bfc85d003e21c45 differ diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/3f/9eed8946df9e2c737d3b8dc0b8e78959aacd92 libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/3f/9eed8946df9e2c737d3b8dc0b8e78959aacd92 --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/3f/9eed8946df9e2c737d3b8dc0b8e78959aacd92 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/3f/9eed8946df9e2c737d3b8dc0b8e78959aacd92 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,5 @@ +xMj0)fjH]dԸDq#޾ +-t=YJ`HOt.DSJN.1I#gUo $ eR8ɇgj/F] +,MW8j-zپVxyk37d){pc +hn +bNzgUǡ}6xz*V8 \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/40/9a1bec58bf35348e8b62b72bb9c1f45cf5a587 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/40/9a1bec58bf35348e8b62b72bb9c1f45cf5a587 differ diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/44/cd2ed2052c9c68f9a439d208e9614dc2a55c70 libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/44/cd2ed2052c9c68f9a439d208e9614dc2a55c70 --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/44/cd2ed2052c9c68f9a439d208e9614dc2a55c70 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/44/cd2ed2052c9c68f9a439d208e9614dc2a55c70 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xN0C9+推$M$đX&i!M%{ gɲZpכ.0c$etBPpLꃛ,RITtA4E.TFrI lOkNl,۴oxcoO[o3wZO`lD.V=vWzd(ءux8O.K%M?Z \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/48/7434cace79238a7091e2220611d4f20a765690 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/48/7434cace79238a7091e2220611d4f20a765690 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/49/20ad2f17162dcc8823ad491444dcb87f5899c9 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/49/20ad2f17162dcc8823ad491444dcb87f5899c9 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/4c/532774cc1fea37f6efc2256763a64d38c8cdde and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/4c/532774cc1fea37f6efc2256763a64d38c8cdde differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/51/145af30d411a50195b66517d825e69bf57ed22 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/51/145af30d411a50195b66517d825e69bf57ed22 differ diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/54/61de53ffadbf15be4dd6345997c15689573209 libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/54/61de53ffadbf15be4dd6345997c15689573209 --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/54/61de53ffadbf15be4dd6345997c15689573209 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/54/61de53ffadbf15be4dd6345997c15689573209 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,4 @@ +xMj0)fjH]dԸDq#޾ +-t=YJ`HOt.DSJN.1I#gUo $ eR8ɇgj/F] +,MW8j-zپVxyk37d){pc +hn?Js=C:O #6V< \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/54/784f10955e92ab27e4fa832e40cb2baf1edbdc and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/54/784f10955e92ab27e4fa832e40cb2baf1edbdc differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/56/3f6473a3858f99b80e5f93c660512ed38e1e6f and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/56/3f6473a3858f99b80e5f93c660512ed38e1e6f differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/58/a957ef0061c1a8ef995c855dfab4f5da8d6617 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/58/a957ef0061c1a8ef995c855dfab4f5da8d6617 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/5d/c7e1f440ce74d5503a0dfbc6c30e091475f774 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/5d/c7e1f440ce74d5503a0dfbc6c30e091475f774 differ diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/5e/2206cda1c56430ad107a6866a829c159e0b9ea libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/5e/2206cda1c56430ad107a6866a829c159e0b9ea --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/5e/2206cda1c56430ad107a6866a829c159e0b9ea 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/5e/2206cda1c56430ad107a6866a829c159e0b9ea 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +x+)JMU044d040031QHI5+(aU E9s\uIXvKY;7nM3KdF"cx?35זzѨ1* \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/5f/77a2a13935ac62a629553f8944ad57b1ed8b4a and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/5f/77a2a13935ac62a629553f8944ad57b1ed8b4a differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/63/c0d92b95253c4a40d3883f423a54be47d2c4c8 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/63/c0d92b95253c4a40d3883f423a54be47d2c4c8 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/6c/e83eb5f0fd34a10c3d25c6b36d2ed7ec0d6ce7 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/6c/e83eb5f0fd34a10c3d25c6b36d2ed7ec0d6ce7 differ diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/6d/1c2afe5eeb9e497528e2780ac468a5465cbc96 libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/6d/1c2afe5eeb9e497528e2780ac468a5465cbc96 --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/6d/1c2afe5eeb9e497528e2780ac468a5465cbc96 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/6d/1c2afe5eeb9e497528e2780ac468a5465cbc96 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +x=j@@{ fvLpgp3̎FժmҾɺ,s7U$ 1 :HEc.d*1Qp5nzTh}A"I.SA:Hys }Z\YvminpymGYo`$DծBO{L|f^OA \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/74/f06b5bfec6d33d7264f73606b57a7c0b963819 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/74/f06b5bfec6d33d7264f73606b57a7c0b963819 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/82/8b08c52d2cba30952e0e008f60b25b5ba0d41a and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/82/8b08c52d2cba30952e0e008f60b25b5ba0d41a differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/85/36dd6f0ec3ddecb9f9b6c8c64c6d322cd01211 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/85/36dd6f0ec3ddecb9f9b6c8c64c6d322cd01211 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/85/a4a1d791973644f24c72f5e89420d3064cc452 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/85/a4a1d791973644f24c72f5e89420d3064cc452 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/8b/5c30499a71001189b647f4d5b57fa8f04897ce and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/8b/5c30499a71001189b647f4d5b57fa8f04897ce differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/96/4ea3da044d9083181a88ba6701de9e35778bf4 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/96/4ea3da044d9083181a88ba6701de9e35778bf4 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/9c/c39fca3765a2facbe31157f7d60c2602193f36 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/9c/c39fca3765a2facbe31157f7d60c2602193f36 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/9c/cb9bf50c011fd58dcbaa65df917bf79539717f and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/9c/cb9bf50c011fd58dcbaa65df917bf79539717f differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/a1/0b59f4280491afe6e430c30654a7acc67d4a33 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/a1/0b59f4280491afe6e430c30654a7acc67d4a33 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/a2/1b4bfe7a04ab18024fb57f4ae9a52a1acef394 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/a2/1b4bfe7a04ab18024fb57f4ae9a52a1acef394 differ diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/a4/3a050c588d4e92f11a6b139680923e9728477d libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/a4/3a050c588d4e92f11a6b139680923e9728477d --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/a4/3a050c588d4e92f11a6b139680923e9728477d 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/a4/3a050c588d4e92f11a6b139680923e9728477d 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xMj0uHBb4.J2G㽔qW9l=#`5GsDD5(ꋪlX!p!e$2N2{9IїWemו:y/om7pB]Q mw`^þA6 mT \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/a5/8ca3fee5eb68b11adc2703e5843f968c9dad1e and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/a5/8ca3fee5eb68b11adc2703e5843f968c9dad1e differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/a6/61b5dec1004e2c62654ded3762370c27cf266b and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/a6/61b5dec1004e2c62654ded3762370c27cf266b differ diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/a6/9ef8fcbb9a2c509a7dbf4f23d257eb551d5610 libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/a6/9ef8fcbb9a2c509a7dbf4f23d257eb551d5610 --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/a6/9ef8fcbb9a2c509a7dbf4f23d257eb551d5610 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/a6/9ef8fcbb9a2c509a7dbf4f23d257eb551d5610 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +x10@ѭuA,!] `,/hrǰGHKs՛*he8J*(&rTlJI؅$JD%YF}ipt:kot9楞`p)95]?}nsSnjGPOL \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/a8/3c6f70297b805dedc549e6583582966f6ebcab and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/a8/3c6f70297b805dedc549e6583582966f6ebcab differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/a9/020cd240774e4d672732bcb82d516d9685da76 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/a9/020cd240774e4d672732bcb82d516d9685da76 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/ab/4115f808bc585b60f822da7020af86d20f62c8 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/ab/4115f808bc585b60f822da7020af86d20f62c8 differ diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/ab/e4603bc7cd5b8167a267e0e2418fd2348f8cff libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/ab/e4603bc7cd5b8167a267e0e2418fd2348f8cff --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/ab/e4603bc7cd5b8167a267e0e2418fd2348f8cff 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/ab/e4603bc7cd5b8167a267e0e2418fd2348f8cff 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,4 @@ +xKN0DY?B쐐8DӞHNqܞF'ƇT!`*<+,YZ +%LވلM쓖X$N$S.cq89 +Dޚo{xFL)Ɣ}em] ^wv~z,&?iGz\//rS`^ +=ݣXfZ \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/b8/26e9b36e22e949ec885e7a1f3db496bbab6cd0 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/b8/26e9b36e22e949ec885e7a1f3db496bbab6cd0 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/ba/fbf6912c09505ac60575cd43d3f2aba3bd84d8 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/ba/fbf6912c09505ac60575cd43d3f2aba3bd84d8 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/bb/14296ffa9dfbf935ec9ce2f9ed7808d952226b and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/bb/14296ffa9dfbf935ec9ce2f9ed7808d952226b differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/bc/4dd0744364d1db380a9811bd264c101065231e and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/bc/4dd0744364d1db380a9811bd264c101065231e differ diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/bd/65d4083845ed5ed4e1fe5feb85ac395d0760c8 libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/bd/65d4083845ed5ed4e1fe5feb85ac395d0760c8 --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/bd/65d4083845ed5ed4e1fe5feb85ac395d0760c8 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/bd/65d4083845ed5ed4e1fe5feb85ac395d0760c8 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xA +B!֞bA6BD[o^@$ݾj-ڛtf]#":dKi 51S@Rq:q>qehM}P!:Cxεu!xXϟC>}ṟ,I炓ĥE9{0;KZq_˺Yt3V \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/cf/c4f0999a8367568e049af4f72e452d40828a15 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/cf/c4f0999a8367568e049af4f72e452d40828a15 differ diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/d0/f21e17beb5b9d953b1d8349049818a4f2edd1e libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/d0/f21e17beb5b9d953b1d8349049818a4f2edd1e --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/d0/f21e17beb5b9d953b1d8349049818a4f2edd1e 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/d0/f21e17beb5b9d953b1d8349049818a4f2edd1e 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xAj0E)f_,]z<]"޾!<^l4f=iM0Hir: <kiFGhI}S] 4F qfʚl蔵6svׯ[i ]R-7  uà:OI)p[!/=;&WY \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/d3/d77487660ee3c0194ee01dc5eaf478782b1c7e libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/d3/d77487660ee3c0194ee01dc5eaf478782b1c7e --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/d3/d77487660ee3c0194ee01dc5eaf478782b1c7e 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/d3/d77487660ee3c0194ee01dc5eaf478782b1c7e 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xKJ1a9E!I@df%x!AooGp-~[[l O2AW 6Rّ=yFW @Փ5l(drXG[.cEKѻgmV!Pso0v *Wd \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/e2/33b9ed408a95e9d4b65fec7fc34943a556deb2 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/e2/33b9ed408a95e9d4b65fec7fc34943a556deb2 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/e5/183bfd18e3a0a691fadde2f0d5610b73282d31 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/e5/183bfd18e3a0a691fadde2f0d5610b73282d31 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/e6/ae8889c40c77d7be02758235b5b3f7a4f2a129 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/e6/ae8889c40c77d7be02758235b5b3f7a4f2a129 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/e7/811a2bc55635f182750f0420da5ad232c1af91 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/e7/811a2bc55635f182750f0420da5ad232c1af91 differ diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/e9/b63f3655b2ad80c0ff587389b5a9589a3a7110 libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/e9/b63f3655b2ad80c0ff587389b5a9589a3a7110 --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/e9/b63f3655b2ad80c0ff587389b5a9589a3a7110 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/e9/b63f3655b2ad80c0ff587389b5a9589a3a7110 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xMj0@u<!Eɣ?$/rzn}]HG+z"*k +AHch]n RN-3Kp~}PK,J=dz#G?Vֲu:NvVfi cO;iZjEu y^-P@ \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/eb/da71fe44dcb60c53b8fbd53208a1204d32e959 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/eb/da71fe44dcb60c53b8fbd53208a1204d32e959 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/f0/5ed049854c1596a7cc0e957fab34961077f3ae and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/f0/5ed049854c1596a7cc0e957fab34961077f3ae differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/f0/a4e1c66bb548cd2b22eebefda703872e969775 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/f0/a4e1c66bb548cd2b22eebefda703872e969775 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/f2/ec8c8cf1a9fb7aa047a25a4308bfe860237ad4 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/f2/ec8c8cf1a9fb7aa047a25a4308bfe860237ad4 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/f5/684c96bf40c709877b56404cd8a5dd2d2a7978 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/f5/684c96bf40c709877b56404cd8a5dd2d2a7978 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/cherrypick/.gitted/objects/f9/0f9dcbdac2cce5cc166346160e19cb693ef4e8 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/cherrypick/.gitted/objects/f9/0f9dcbdac2cce5cc166346160e19cb693ef4e8 differ diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/refs/heads/automerge-branch libgit2-0.22.2/tests/resources/cherrypick/.gitted/refs/heads/automerge-branch --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/refs/heads/automerge-branch 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/refs/heads/automerge-branch 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +d3d77487660ee3c0194ee01dc5eaf478782b1c7e diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/refs/heads/master libgit2-0.22.2/tests/resources/cherrypick/.gitted/refs/heads/master --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/refs/heads/master 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/refs/heads/master 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +2a26c7e88b285613b302ba76712bc998863f3cbc diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/refs/heads/merge-branch libgit2-0.22.2/tests/resources/cherrypick/.gitted/refs/heads/merge-branch --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/refs/heads/merge-branch 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/refs/heads/merge-branch 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +abe4603bc7cd5b8167a267e0e2418fd2348f8cff diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/refs/heads/merge-conflicts libgit2-0.22.2/tests/resources/cherrypick/.gitted/refs/heads/merge-conflicts --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/refs/heads/merge-conflicts 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/refs/heads/merge-conflicts 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +bafbf6912c09505ac60575cd43d3f2aba3bd84d8 diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/refs/heads/merge-mainline libgit2-0.22.2/tests/resources/cherrypick/.gitted/refs/heads/merge-mainline --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/refs/heads/merge-mainline 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/refs/heads/merge-mainline 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +cfc4f0999a8367568e049af4f72e452d40828a15 diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/refs/heads/orphan libgit2-0.22.2/tests/resources/cherrypick/.gitted/refs/heads/orphan --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/refs/heads/orphan 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/refs/heads/orphan 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +74f06b5bfec6d33d7264f73606b57a7c0b963819 diff -Nru libgit2-0.20.0/tests/resources/cherrypick/.gitted/refs/heads/renames libgit2-0.22.2/tests/resources/cherrypick/.gitted/refs/heads/renames --- libgit2-0.20.0/tests/resources/cherrypick/.gitted/refs/heads/renames 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/cherrypick/.gitted/refs/heads/renames 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +44cd2ed2052c9c68f9a439d208e9614dc2a55c70 diff -Nru libgit2-0.20.0/tests/resources/config/config12 libgit2-0.22.2/tests/resources/config/config12 --- libgit2-0.20.0/tests/resources/config/config12 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/resources/config/config12 2015-03-24 16:10:45.000000000 +0000 @@ -1,7 +1,13 @@ [some "section"] test = hi ; comment + test2 = hello ; comment + test3 = welcome #comment other = "hello! \" ; ; ; " ; more test + other2 = "cool! \" # # # " # more test multi = "hi, this is a ; \ multiline comment # with ;\n special chars \ and other stuff !@#" + multi2 = "good, this is a ; \ +multiline comment # with ;\n special chars \ +and other stuff !@#" #^^^ back = "this is \ba phrase" diff -Nru libgit2-0.20.0/tests/resources/crlf/.gitted/objects/04/4bcd5c9bf5ebdd51e514a9a36457018f06f6e1 libgit2-0.22.2/tests/resources/crlf/.gitted/objects/04/4bcd5c9bf5ebdd51e514a9a36457018f06f6e1 --- libgit2-0.20.0/tests/resources/crlf/.gitted/objects/04/4bcd5c9bf5ebdd51e514a9a36457018f06f6e1 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/crlf/.gitted/objects/04/4bcd5c9bf5ebdd51e514a9a36457018f06f6e1 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +x-j0D{W4H++C(ɡ]aJU|}̃ʺ. кVE@vȔvBx=%l sDxH!x3E9AhPdUTk {k+Av`C2|h괟lR{~/]`z-̥<]M5?]udr&K! \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb libgit2-0.22.2/tests/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb --- libgit2-0.20.0/tests/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -x 1}Nۀ,b6K6`.ؾQoab-A0dXbtnr:0cy(*Y 1380209394 +0200 commit (initial): initial +108b485d8268ea595df8ffea74f0f4b186577d32 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken 1380209404 +0200 commit: second +4d6558b8fa764baeb0f19c1e857df91e0eda5a0f b240c0fb88c5a629e00ebc1275fa1f33e364a705 nulltoken 1380209414 +0200 commit: third +b240c0fb88c5a629e00ebc1275fa1f33e364a705 81f4b1aac643e6983fab370eae8aefccecbf3a4c nulltoken 1380209425 +0200 commit: A +81f4b1aac643e6983fab370eae8aefccecbf3a4c 6126a5f9c57ebc81e64370ec3095184ad92dab1c nulltoken 1380209445 +0200 commit: c +6126a5f9c57ebc81e64370ec3095184ad92dab1c 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken 1380209455 +0200 reset: moving to 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f +4d6558b8fa764baeb0f19c1e857df91e0eda5a0f 31fc9136820b507e938a9c6b88bf2c567a9f6f4b nulltoken 1380209465 +0200 commit: B +31fc9136820b507e938a9c6b88bf2c567a9f6f4b ce1c4f8b6120122e23d4442925d98c56c41917d8 nulltoken 1380209486 +0200 merge c: Merge made by the 'recursive' strategy. +ce1c4f8b6120122e23d4442925d98c56c41917d8 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken 1380209486 +0200 reset: moving to 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f +4d6558b8fa764baeb0f19c1e857df91e0eda5a0f 6a12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 nulltoken 1380209496 +0200 commit: D +6a12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 1e016431ec7b22dd3e23f3e6f5f68f358f9227cf nulltoken 1380209527 +0200 commit: another +1e016431ec7b22dd3e23f3e6f5f68f358f9227cf a9eb02af13df030159e39f70330d5c8a47655691 nulltoken 1380209547 +0200 commit: yet another +a9eb02af13df030159e39f70330d5c8a47655691 949b98e208015bfc0e2f573debc34ae2f97a7f0e nulltoken 1380209557 +0200 merge ce1c4f8b6120122e23d4442925d98c56c41917d8: Merge made by the 'recursive' strategy. +949b98e208015bfc0e2f573debc34ae2f97a7f0e a6095f816e81f64651595d488badc42399837d6a nulltoken 1380209567 +0200 commit: x diff -Nru libgit2-0.20.0/tests/resources/describe/.gitted/logs/refs/heads/master libgit2-0.22.2/tests/resources/describe/.gitted/logs/refs/heads/master --- libgit2-0.20.0/tests/resources/describe/.gitted/logs/refs/heads/master 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/describe/.gitted/logs/refs/heads/master 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,14 @@ +0000000000000000000000000000000000000000 108b485d8268ea595df8ffea74f0f4b186577d32 nulltoken 1380209394 +0200 commit (initial): initial +108b485d8268ea595df8ffea74f0f4b186577d32 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken 1380209404 +0200 commit: second +4d6558b8fa764baeb0f19c1e857df91e0eda5a0f b240c0fb88c5a629e00ebc1275fa1f33e364a705 nulltoken 1380209414 +0200 commit: third +b240c0fb88c5a629e00ebc1275fa1f33e364a705 81f4b1aac643e6983fab370eae8aefccecbf3a4c nulltoken 1380209425 +0200 commit: A +81f4b1aac643e6983fab370eae8aefccecbf3a4c 6126a5f9c57ebc81e64370ec3095184ad92dab1c nulltoken 1380209445 +0200 commit: c +6126a5f9c57ebc81e64370ec3095184ad92dab1c 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken 1380209455 +0200 reset: moving to 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f +4d6558b8fa764baeb0f19c1e857df91e0eda5a0f 31fc9136820b507e938a9c6b88bf2c567a9f6f4b nulltoken 1380209465 +0200 commit: B +31fc9136820b507e938a9c6b88bf2c567a9f6f4b ce1c4f8b6120122e23d4442925d98c56c41917d8 nulltoken 1380209486 +0200 merge c: Merge made by the 'recursive' strategy. +ce1c4f8b6120122e23d4442925d98c56c41917d8 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken 1380209486 +0200 reset: moving to 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f +4d6558b8fa764baeb0f19c1e857df91e0eda5a0f 6a12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 nulltoken 1380209496 +0200 commit: D +6a12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 1e016431ec7b22dd3e23f3e6f5f68f358f9227cf nulltoken 1380209527 +0200 commit: another +1e016431ec7b22dd3e23f3e6f5f68f358f9227cf a9eb02af13df030159e39f70330d5c8a47655691 nulltoken 1380209547 +0200 commit: yet another +a9eb02af13df030159e39f70330d5c8a47655691 949b98e208015bfc0e2f573debc34ae2f97a7f0e nulltoken 1380209557 +0200 merge ce1c4f8b6120122e23d4442925d98c56c41917d8: Merge made by the 'recursive' strategy. +949b98e208015bfc0e2f573debc34ae2f97a7f0e a6095f816e81f64651595d488badc42399837d6a nulltoken 1380209567 +0200 commit: x Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/03/00021985931292d0611b9232e757035fefc04d and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/03/00021985931292d0611b9232e757035fefc04d differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/10/8b485d8268ea595df8ffea74f0f4b186577d32 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/10/8b485d8268ea595df8ffea74f0f4b186577d32 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/10/bd08b099ecb79184c60183f5c94ca915f427ad and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/10/bd08b099ecb79184c60183f5c94ca915f427ad differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/17/8481050188cf00d7d9cd5a11e43ab8fab9294f and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/17/8481050188cf00d7d9cd5a11e43ab8fab9294f differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/19/1faf88a5826a99f475baaf8b13652c4e40bfe6 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/19/1faf88a5826a99f475baaf8b13652c4e40bfe6 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/1e/016431ec7b22dd3e23f3e6f5f68f358f9227cf and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/1e/016431ec7b22dd3e23f3e6f5f68f358f9227cf differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/22/3b7836fb19fdf64ba2d3cd6173c6a283141f78 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/22/3b7836fb19fdf64ba2d3cd6173c6a283141f78 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/25/d5edf8c0ef17e8a13b8da75913dcec4ea7afc1 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/25/d5edf8c0ef17e8a13b8da75913dcec4ea7afc1 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/31/fc9136820b507e938a9c6b88bf2c567a9f6f4b and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/31/fc9136820b507e938a9c6b88bf2c567a9f6f4b differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/42/8f9554a2eec22de29898819b579466af7c1583 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/42/8f9554a2eec22de29898819b579466af7c1583 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/4d/6558b8fa764baeb0f19c1e857df91e0eda5a0f and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/4d/6558b8fa764baeb0f19c1e857df91e0eda5a0f differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/4f/2d9ce01ad5249cabdc6565366af8aff85b1525 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/4f/2d9ce01ad5249cabdc6565366af8aff85b1525 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/52/912fbab0715dec53d43053966e78ad213ba359 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/52/912fbab0715dec53d43053966e78ad213ba359 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/61/26a5f9c57ebc81e64370ec3095184ad92dab1c and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/61/26a5f9c57ebc81e64370ec3095184ad92dab1c differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/62/d8fe9f6db631bd3a19140699101c9e281c9f9d and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/62/d8fe9f6db631bd3a19140699101c9e281c9f9d differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/65/a91bc2262480dce4c5979519aae6668368eb4e and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/65/a91bc2262480dce4c5979519aae6668368eb4e differ diff -Nru libgit2-0.20.0/tests/resources/describe/.gitted/objects/68/0166b6cd31f76354fee2572618e6b0142d05e6 libgit2-0.22.2/tests/resources/describe/.gitted/objects/68/0166b6cd31f76354fee2572618e6b0142d05e6 --- libgit2-0.20.0/tests/resources/describe/.gitted/objects/68/0166b6cd31f76354fee2572618e6b0142d05e6 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/describe/.gitted/objects/68/0166b6cd31f76354fee2572618e6b0142d05e6 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xK @]sٛ4|cJ[QhK% \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/aa/d8d5cef3915ab78b3227abaaac99b62db9eb54 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/aa/d8d5cef3915ab78b3227abaaac99b62db9eb54 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/aa/ddd4f14847e0e323924ec262c2343249a84f8b and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/aa/ddd4f14847e0e323924ec262c2343249a84f8b differ diff -Nru libgit2-0.20.0/tests/resources/describe/.gitted/objects/b2/40c0fb88c5a629e00ebc1275fa1f33e364a705 libgit2-0.22.2/tests/resources/describe/.gitted/objects/b2/40c0fb88c5a629e00ebc1275fa1f33e364a705 --- libgit2-0.20.0/tests/resources/describe/.gitted/objects/b2/40c0fb88c5a629e00ebc1275fa1f33e364a705 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/describe/.gitted/objects/b2/40c0fb88c5a629e00ebc1275fa1f33e364a705 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +xM +  AͧQ(WJc,ܿ7 j-+EnerY9 Xg* 8df +Ad预[NB yEfqho^ѫ>՗}޹\P‘$~ ش9GG \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/ce/1c4f8b6120122e23d4442925d98c56c41917d8 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/ce/1c4f8b6120122e23d4442925d98c56c41917d8 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/d5/aab219a814ddbe4b3aaedf03cdea491b218ec4 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/d5/aab219a814ddbe4b3aaedf03cdea491b218ec4 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/f2/ad6c76f0115a6ba5b00456a849810e7ec0af20 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/f2/ad6c76f0115a6ba5b00456a849810e7ec0af20 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/f7/0f10e4db19068f79bc43844b49f3eece45c4e8 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/f7/0f10e4db19068f79bc43844b49f3eece45c4e8 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/describe/.gitted/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/describe/.gitted/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871 differ diff -Nru libgit2-0.20.0/tests/resources/describe/.gitted/refs/heads/master libgit2-0.22.2/tests/resources/describe/.gitted/refs/heads/master --- libgit2-0.20.0/tests/resources/describe/.gitted/refs/heads/master 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/describe/.gitted/refs/heads/master 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +a6095f816e81f64651595d488badc42399837d6a diff -Nru libgit2-0.20.0/tests/resources/describe/.gitted/refs/tags/A libgit2-0.22.2/tests/resources/describe/.gitted/refs/tags/A --- libgit2-0.20.0/tests/resources/describe/.gitted/refs/tags/A 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/describe/.gitted/refs/tags/A 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +aaddd4f14847e0e323924ec262c2343249a84f8b diff -Nru libgit2-0.20.0/tests/resources/describe/.gitted/refs/tags/B libgit2-0.22.2/tests/resources/describe/.gitted/refs/tags/B --- libgit2-0.20.0/tests/resources/describe/.gitted/refs/tags/B 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/describe/.gitted/refs/tags/B 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +52912fbab0715dec53d43053966e78ad213ba359 diff -Nru libgit2-0.20.0/tests/resources/describe/.gitted/refs/tags/c libgit2-0.22.2/tests/resources/describe/.gitted/refs/tags/c --- libgit2-0.20.0/tests/resources/describe/.gitted/refs/tags/c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/describe/.gitted/refs/tags/c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +6126a5f9c57ebc81e64370ec3095184ad92dab1c diff -Nru libgit2-0.20.0/tests/resources/describe/.gitted/refs/tags/D libgit2-0.22.2/tests/resources/describe/.gitted/refs/tags/D --- libgit2-0.20.0/tests/resources/describe/.gitted/refs/tags/D 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/describe/.gitted/refs/tags/D 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +10bd08b099ecb79184c60183f5c94ca915f427ad diff -Nru libgit2-0.20.0/tests/resources/describe/.gitted/refs/tags/e libgit2-0.22.2/tests/resources/describe/.gitted/refs/tags/e --- libgit2-0.20.0/tests/resources/describe/.gitted/refs/tags/e 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/describe/.gitted/refs/tags/e 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +1e016431ec7b22dd3e23f3e6f5f68f358f9227cf diff -Nru libgit2-0.20.0/tests/resources/describe/.gitted/refs/tags/R libgit2-0.22.2/tests/resources/describe/.gitted/refs/tags/R --- libgit2-0.20.0/tests/resources/describe/.gitted/refs/tags/R 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/describe/.gitted/refs/tags/R 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +680166b6cd31f76354fee2572618e6b0142d05e6 diff -Nru libgit2-0.20.0/tests/resources/describe/side libgit2-0.22.2/tests/resources/describe/side --- libgit2-0.20.0/tests/resources/describe/side 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/describe/side 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +X diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/file1.txt.renamed libgit2-0.22.2/tests/resources/diff_format_email/file1.txt.renamed --- libgit2-0.20.0/tests/resources/diff_format_email/file1.txt.renamed 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/file1.txt.renamed 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,17 @@ +file1.txt +file1.txt +_file1.txt_ +file1.txt +file1.txt +file1.txt_renamed +file1.txt + + +file1.txt +file1.txt +file1.txt_renamed +file1.txt +file1.txt +_file1.txt_ +_file1.txt_ +file1.txt diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/file2.txt libgit2-0.22.2/tests/resources/diff_format_email/file2.txt --- libgit2-0.20.0/tests/resources/diff_format_email/file2.txt 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/file2.txt 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,5 @@ +file2 +file2 +file2 +file2! +file2 diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/file3.txt libgit2-0.22.2/tests/resources/diff_format_email/file3.txt --- libgit2-0.20.0/tests/resources/diff_format_email/file3.txt 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/file3.txt 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,5 @@ +file3 +file3! +file3 +file3 +file3 diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/.gitted/config libgit2-0.22.2/tests/resources/diff_format_email/.gitted/config --- libgit2-0.20.0/tests/resources/diff_format_email/.gitted/config 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/.gitted/config 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,7 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true + precomposeunicode = true diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/.gitted/HEAD libgit2-0.22.2/tests/resources/diff_format_email/.gitted/HEAD --- libgit2-0.20.0/tests/resources/diff_format_email/.gitted/HEAD 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/.gitted/HEAD 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +ref: refs/heads/master Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/index and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/index differ diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/.gitted/info/exclude libgit2-0.22.2/tests/resources/diff_format_email/.gitted/info/exclude --- libgit2-0.20.0/tests/resources/diff_format_email/.gitted/info/exclude 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/.gitted/info/exclude 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/0a/37045ca6d8503e9bcf06a12abbbc8e92664cce and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/0a/37045ca6d8503e9bcf06a12abbbc8e92664cce differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/10/808fe9c9be5a190c0ba68d1a002233fb363508 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/10/808fe9c9be5a190c0ba68d1a002233fb363508 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/13/ecf3d572dbc5e5b32c8ba067d1d1e0939572e8 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/13/ecf3d572dbc5e5b32c8ba067d1d1e0939572e8 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/17/cfad36e93db7706b16bef5ef842ba1e5ca06ab and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/17/cfad36e93db7706b16bef5ef842ba1e5ca06ab differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/1a/9932083f96b0db42552103d40076f62fa8235e and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/1a/9932083f96b0db42552103d40076f62fa8235e differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/1a/e3be57f869687d983066a0f5d2aaea1b82ddc5 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/1a/e3be57f869687d983066a0f5d2aaea1b82ddc5 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/1b/525b0a6c5218b069b601ce91fce8eaf0a54e20 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/1b/525b0a6c5218b069b601ce91fce8eaf0a54e20 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/1e/82c3b234e37da82e5b23e0e2a70bca68ee12c6 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/1e/82c3b234e37da82e5b23e0e2a70bca68ee12c6 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/1e/875da9b1e67f853b2eec3e202c21c867097234 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/1e/875da9b1e67f853b2eec3e202c21c867097234 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/20/609dbbc32bbfc827528eec3fcea2d024e6dd8a and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/20/609dbbc32bbfc827528eec3fcea2d024e6dd8a differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/23/f92946d3f38bd090f700d3e8e7b728ffc58264 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/23/f92946d3f38bd090f700d3e8e7b728ffc58264 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/24/97c5249408494e66e25070a8c74e49eaeeb6c3 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/24/97c5249408494e66e25070a8c74e49eaeeb6c3 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/24/9a4263be23b4d1c02484cb840b6eca4c6cf74d and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/24/9a4263be23b4d1c02484cb840b6eca4c6cf74d differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/25/2a3e19fd2c6fb7b20c111142c5bd5fb9ea6b8e and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/25/2a3e19fd2c6fb7b20c111142c5bd5fb9ea6b8e differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/27/93544db9060bab4f9169e5b89c82f9fa7c7fa6 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/27/93544db9060bab4f9169e5b89c82f9fa7c7fa6 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/29/1f1ff3cbb9a6f153678d9657679e3d4bf257df and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/29/1f1ff3cbb9a6f153678d9657679e3d4bf257df differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/2f/f7b811eee62a73959350b1f7349f6f4d0c882d and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/2f/f7b811eee62a73959350b1f7349f6f4d0c882d differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/39/91dce9e71a0641ca49a6a4eea6c9e7ff402ed4 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/39/91dce9e71a0641ca49a6a4eea6c9e7ff402ed4 differ diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/45/eef2a9317e179984649de247269e38cd5d99cf libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/45/eef2a9317e179984649de247269e38cd5d99cf --- libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/45/eef2a9317e179984649de247269e38cd5d99cf 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/45/eef2a9317e179984649de247269e38cd5d99cf 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xJ0ay_IIqwo0Lo+$&[zU&FY,:YCT8B UsH,BL)(r.ca }+^Y>mp՚_ sG)&k}GhzRļi* +Rrs X \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/4a/076277b884c519a932be67e346db2ac80a98fa and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/4a/076277b884c519a932be67e346db2ac80a98fa differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/4c/3bd7182ad66ea7aa20ba47ae82812b710d169c and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/4c/3bd7182ad66ea7aa20ba47ae82812b710d169c differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/4c/a10087e696d2ba78d07b146a118e9a7096ed4f and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/4c/a10087e696d2ba78d07b146a118e9a7096ed4f differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/4d/de2b17d1c982cd988f21d24350a214401e4a1e and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/4d/de2b17d1c982cd988f21d24350a214401e4a1e differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/4f/31e0248ac800a1edc78b74f74e86f5eba90e87 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/4f/31e0248ac800a1edc78b74f74e86f5eba90e87 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/50/17c9456d013b2c7712d29aab73b681c880f509 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/50/17c9456d013b2c7712d29aab73b681c880f509 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/50/438cfa585c1d15cf3650ed1bf641da937cc261 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/50/438cfa585c1d15cf3650ed1bf641da937cc261 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/52/c3cd1ff6234b95fecbaf9ef13624da17697b8d and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/52/c3cd1ff6234b95fecbaf9ef13624da17697b8d differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/55/0d730ba1b8c4937ea170b37c7ba91d792c0aaa and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/55/0d730ba1b8c4937ea170b37c7ba91d792c0aaa differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/66/81f1844dc677e5ff07ffd993461f5c441e6af5 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/66/81f1844dc677e5ff07ffd993461f5c441e6af5 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/69/ddefb5c245e2f9ee62bd4cabd8ebe60a01e448 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/69/ddefb5c245e2f9ee62bd4cabd8ebe60a01e448 differ diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/6b/6c2067c6d968f9bddb9b900ee1ab7e5b067430 libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/6b/6c2067c6d968f9bddb9b900ee1ab7e5b067430 --- libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/6b/6c2067c6d968f9bddb9b900ee1ab7e5b067430 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/6b/6c2067c6d968f9bddb9b900ee1ab7e5b067430 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +x; +1a X)$sD3{.+sšsj޵ 'fg=Cm⤄A LqGh~u$+g+H[^`ȏQW \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/7a/de76dd34bba4733cf9878079f9fd4a456a9189 libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/7a/de76dd34bba4733cf9878079f9fd4a456a9189 --- libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/7a/de76dd34bba4733cf9878079f9fd4a456a9189 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/7a/de76dd34bba4733cf9878079f9fd4a456a9189 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +xA +0E]d4M"`m5IޢGp>X4aWn3 y ,[FHoD-e +:ёcz0FbCÊ:_HNcwRHqN'6xF {4j[j$0U_y*&R \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/7a/ff11da95ca2be0bfb74b06e7cc1c480559dbe7 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/7a/ff11da95ca2be0bfb74b06e7cc1c480559dbe7 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/7f/854619451620f7fbcec7ea171675e615ce92b6 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/7f/854619451620f7fbcec7ea171675e615ce92b6 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/87/3806f6f27e631eb0b23e4b56bea2bfac14a373 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/87/3806f6f27e631eb0b23e4b56bea2bfac14a373 differ diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/89/47a46e2097638ca6040ad4877246f4186ec3bd libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/89/47a46e2097638ca6040ad4877246f4186ec3bd --- libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/89/47a46e2097638ca6040ad4877246f4186ec3bd 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/89/47a46e2097638ca6040ad4877246f4186ec3bd 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xAj0E)/ь,Jw.{8 ;Eo_C{nWZnYҩ7:8\u!Ni@0qaWR4Foڰt$ +S8"C'-ΫD1쇖}Gv߿7{y۬]eb"sc|bъL \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/89/7d3af16ca9e420cd071b1c4541bd2b91d04c8c libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/89/7d3af16ca9e420cd071b1c4541bd2b91d04c8c --- libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/89/7d3af16ca9e420cd071b1c4541bd2b91d04c8c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/89/7d3af16ca9e420cd071b1c4541bd2b91d04c8c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xAN0 @Q9Hȵ$Y!q 't1g$-~ֵ FWb[hqXY)9Q[ '͘9d&e‘us׶__yGo2lLD3HաsZ!;X"Pq \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/8d/7523f6fcb2404257889abe0d96f093d9f524f9 libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/8d/7523f6fcb2404257889abe0d96f093d9f524f9 --- libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/8d/7523f6fcb2404257889abe0d96f093d9f524f9 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/8d/7523f6fcb2404257889abe0d96f093d9f524f9 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xAJ0a9IӤw!f&_Utno#nF7JZLT$%BQVfe,,9+ElL$a$KAu|ެm>zO/@SNOfG!c+6+ ]ER \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/8d/fa038554d5b682a51bda8ee3038cee6c63be76 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/8d/fa038554d5b682a51bda8ee3038cee6c63be76 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/92/64b96c6d104d0e07ae33d3007b6a48246c6f92 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/92/64b96c6d104d0e07ae33d3007b6a48246c6f92 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/94/350226b3aa14efac831c803a51f7a09f3fc31a and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/94/350226b3aa14efac831c803a51f7a09f3fc31a differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/94/75e21dcbc515af8f641576400e4b450e5f4c03 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/94/75e21dcbc515af8f641576400e4b450e5f4c03 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/94/aaae8954e8bb613de636071da663a621695911 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/94/aaae8954e8bb613de636071da663a621695911 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/9a/2d780ac2ea0aeabdb9d2a876e6bbfff17b2c44 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/9a/2d780ac2ea0aeabdb9d2a876e6bbfff17b2c44 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/9a/c0329b8b7a4046210d8b8b02ac02055667de63 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/9a/c0329b8b7a4046210d8b8b02ac02055667de63 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/9a/c35ff15cd8864aeafd889e4826a3150f0b06c4 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/9a/c35ff15cd8864aeafd889e4826a3150f0b06c4 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/9b/997daca2a0beb5cc44b32c64f100a9a26d4d4b and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/9b/997daca2a0beb5cc44b32c64f100a9a26d4d4b differ diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/a3/ac918e3a6604294b239cb956363e83d71abb3b libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/a3/ac918e3a6604294b239cb956363e83d71abb3b --- libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/a3/ac918e3a6604294b239cb956363e83d71abb3b 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/a3/ac918e3a6604294b239cb956363e83d71abb3b 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xMJ1@a9ETDf7 xJRD5^nm]ihbSK5p)RD.V|ۄԢEBR"rPKi6|6!;e}^yK7KC H桏)uҠ;hQK \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/a5/ac978d4f2a1784f847f41223a34c3e78934238 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/a5/ac978d4f2a1784f847f41223a34c3e78934238 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/a7/29eab45c84563135e8631d4010230bc0479f1f and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/a7/29eab45c84563135e8631d4010230bc0479f1f differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/a9/7157a0d0571698728b6f2f7675b456c98c5961 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/a9/7157a0d0571698728b6f2f7675b456c98c5961 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/af/8f41d0cb7a3079a8f8e231ea2ab8b97837ce13 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/af/8f41d0cb7a3079a8f8e231ea2ab8b97837ce13 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/b0/5cecf1949d192b6df852b3f71853ef820ee235 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/b0/5cecf1949d192b6df852b3f71853ef820ee235 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/b4/f457c219dbb3517be908d4e70f0ada2fd8b8f9 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/b4/f457c219dbb3517be908d4e70f0ada2fd8b8f9 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/bd/474b2519cc15eab801ff851cc7d50f0dee49a1 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/bd/474b2519cc15eab801ff851cc7d50f0dee49a1 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/bd/f7ba6bc5c4e57ca6595928dcbe6753c8a663ff and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/bd/f7ba6bc5c4e57ca6595928dcbe6753c8a663ff differ diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/cb/a89408dc016f4caddb6dc886fcb58f587a78df libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/cb/a89408dc016f4caddb6dc886fcb58f587a78df --- libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/cb/a89408dc016f4caddb6dc886fcb58f587a78df 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/cb/a89408dc016f4caddb6dc886fcb58f587a78df 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +xK +0FaYE悤Cq&8t7[![p NĹIM~* }ʬ'lilxqԤw6kK +cX̱ ^0Wy^+ja,㲴:rCYvttZUOrRbmᯈc$M \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/cd/471f0d8770371e1bc78bcbb38db4c7e4106bd2 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/cd/471f0d8770371e1bc78bcbb38db4c7e4106bd2 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/cd/ed722d05305c6b181f188c118d2d9810f39bb8 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/cd/ed722d05305c6b181f188c118d2d9810f39bb8 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/ce/2792fcae8d704a56901754a0583a7418a21d8a and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/ce/2792fcae8d704a56901754a0583a7418a21d8a differ diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/d1/4aa252e52a709d03a3d3d0d965e177eb0a674e libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/d1/4aa252e52a709d03a3d3d0d965e177eb0a674e --- libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/d1/4aa252e52a709d03a3d3d0d965e177eb0a674e 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/d1/4aa252e52a709d03a3d3d0d965e177eb0a674e 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +x+)JMU01e040075UHI5+(+JKMMaXYB،|-a'{"z \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/d7/bb447df12c6a8aba8727005482fb211f11297a and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/d7/bb447df12c6a8aba8727005482fb211f11297a differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/db/e8727e4806ae88ccc3f0755cae8f8cb7efa2cc and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/db/e8727e4806ae88ccc3f0755cae8f8cb7efa2cc differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/e1/2af77c510e8ce4c261a3758736109c2c2dd1f0 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/e1/2af77c510e8ce4c261a3758736109c2c2dd1f0 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/e9/091231467304a5ef112de02361d795ef051ee1 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/e9/091231467304a5ef112de02361d795ef051ee1 differ diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/ee/251372f131d82e575f16fe51c778406d88f8c2 libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/ee/251372f131d82e575f16fe51c778406d88f8c2 --- libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/ee/251372f131d82e575f16fe51c778406d88f8c2 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/ee/251372f131d82e575f16fe51c778406d88f8c2 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xM +0@a9Ed3+o)-x4Imp*9Su$\0Nuؐ1^%u/X$e,BPLK# 9FpPĵ S^+/u,˰~y|~^Z-CƓHh^iĦj"R,HrK \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/f3/d35bd592fefd8280fc0c302fa9f27dbdd721a3 libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/f3/d35bd592fefd8280fc0c302fa9f27dbdd721a3 --- libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/f3/d35bd592fefd8280fc0c302fa9f27dbdd721a3 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/f3/d35bd592fefd8280fc0c302fa9f27dbdd721a3 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xA @Qלb&fhi)1bA1b#L&=ۿx?̥dN*3x`C]p-&H]ScpYk|[LB0Yq4Qu] ;KlHB \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/f4/07be01334e07bfb8f57cd2078f0ee3eb61e085 libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/f4/07be01334e07bfb8f57cd2078f0ee3eb61e085 --- libgit2-0.20.0/tests/resources/diff_format_email/.gitted/objects/f4/07be01334e07bfb8f57cd2078f0ee3eb61e085 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/diff_format_email/.gitted/objects/f4/07be01334e07bfb8f57cd2078f0ee3eb61e085 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xMN0 @a98HCBNvL~ܞ8omkЫ*pń31M6L1'4'rJ\!bk=Kts zgd!6mmir[u.^|h}_{{Me?u6">:叄ʃ6^Kd \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/modules/submodule/objects/c2/0765f6e24e8bbb63a648d0d11d84da63170190 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/modules/submodule/objects/c2/0765f6e24e8bbb63a648d0d11d84da63170190 differ diff -Nru libgit2-0.20.0/tests/resources/merge-resolve/.gitted/modules/submodule/objects/d3/d806a4bef96889117fd7ebac0e3cb5ec152932 libgit2-0.22.2/tests/resources/merge-resolve/.gitted/modules/submodule/objects/d3/d806a4bef96889117fd7ebac0e3cb5ec152932 --- libgit2-0.20.0/tests/resources/merge-resolve/.gitted/modules/submodule/objects/d3/d806a4bef96889117fd7ebac0e3cb5ec152932 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/merge-resolve/.gitted/modules/submodule/objects/d3/d806a4bef96889117fd7ebac0e3cb5ec152932 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +xA +0E]semDx$҂@gpQs^+ZD[a +,cGsBO# vhGpIZ4U{^c]zo@ǎ\M-\ # \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/58/87a5e516c53bd58efb0f02ec6aa031b6fe9ad7 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/58/87a5e516c53bd58efb0f02ec6aa031b6fe9ad7 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/60/61fe116ecba0800c26113ea1a7dfac2e16eeaf and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/60/61fe116ecba0800c26113ea1a7dfac2e16eeaf differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/62/33c6a0670228627f93c01cef32485a30403670 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/62/33c6a0670228627f93c01cef32485a30403670 differ diff -Nru libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/67/110d77886b2af6309b9212961e72b8583e5fa9 libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/67/110d77886b2af6309b9212961e72b8583e5fa9 --- libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/67/110d77886b2af6309b9212961e72b8583e5fa9 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/67/110d77886b2af6309b9212961e72b8583e5fa9 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +x=N1 ^r !J:.`'lP^gT343Ҕup*Z %l4irHz,곥[M]aJҐb5l8OX$XճEa")U$d2zODŽų>m'qZ渍O`lFO1!n'=-]A&e˯^o^ \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/6e/3b9eb35214d4e31ed5789afc7d520ac798ce55 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/6e/3b9eb35214d4e31ed5789afc7d520ac798ce55 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/72/cdb057b340205164478565e91eb71647e66891 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/72/cdb057b340205164478565e91eb71647e66891 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/7a/f14d9c679baaef35555095f4f5d33e9a569ab9 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/7a/f14d9c679baaef35555095f4f5d33e9a569ab9 differ diff -Nru libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/7c/04ca611203ed320c5f495b9813054dd23be3be libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/7c/04ca611203ed320c5f495b9813054dd23be3be --- libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/7c/04ca611203ed320c5f495b9813054dd23be3be 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/7c/04ca611203ed320c5f495b9813054dd23be3be 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xQ D{ -,tc^`%b(K|ͼd&k)Dsl<f4a1B8zsCvŘEQdO>E񧯵#}%xu z{yV%rɐdžp֨tDŽ +UrL \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/81/1c70fcb6d5bbd022d04cc31836d30b436f9551 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/81/1c70fcb6d5bbd022d04cc31836d30b436f9551 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/83/6b8b82b26cab22eaaed8820877c76d6c8bca19 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/83/6b8b82b26cab22eaaed8820877c76d6c8bca19 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/8b/7cd60d49ce3a1a770ece43b7d29b5cf462a33a and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/8b/7cd60d49ce3a1a770ece43b7d29b5cf462a33a differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/91/f44111cb1cb1358ac6944ad356ca1738813ea1 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/91/f44111cb1cb1358ac6944ad356ca1738813ea1 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/96/bca8d4f05cc4c5e33e4389f80a1309e86fe054 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/96/bca8d4f05cc4c5e33e4389f80a1309e86fe054 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/ad/01aebfdf2ac13145efafe3f9fcf798882f1730 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/ad/01aebfdf2ac13145efafe3f9fcf798882f1730 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/ad/26b598134264fd284292cb233fc0b2f25851da and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/ad/26b598134264fd284292cb233fc0b2f25851da differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/ca/ff6b7d44973f53e3e0cf31d0d695188b19aec6 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/ca/ff6b7d44973f53e3e0cf31d0d695188b19aec6 differ diff -Nru libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/cc/338e4710c9b257106b8d16d82f86458d5beaf1 libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/cc/338e4710c9b257106b8d16d82f86458d5beaf1 --- libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/cc/338e4710c9b257106b8d16d82f86458d5beaf1 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/cc/338e4710c9b257106b8d16d82f86458d5beaf1 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xK!]s3`bo1 gpWEGmx]6d +eaΉ碵z.Dv [hD[JﱶwX[.2nuVƉZڳF!x88GPP_?KN \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/d3/3cedf513c059e0515653fa2c2e386631387a05 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/d3/3cedf513c059e0515653fa2c2e386631387a05 differ diff -Nru libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/d7/308cc367b2cc23f710834ec1fd8ffbacf1b460 libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/d7/308cc367b2cc23f710834ec1fd8ffbacf1b460 --- libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/d7/308cc367b2cc23f710834ec1fd8ffbacf1b460 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/d7/308cc367b2cc23f710834ec1fd8ffbacf1b460 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xK @]s)Ё7 ]I(x{ ^,nлSo`/X)ٙB@GÔaD 4xwlCv?-79d,hF4Z ;ƝH}= \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/d8/dec75ff2f8b41d1c5bfef0cd57b7300c834f66 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/d8/dec75ff2f8b41d1c5bfef0cd57b7300c834f66 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/dd/2ae5ab264e5592aa754235d5ad5eac8f0ecdfd and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/dd/2ae5ab264e5592aa754235d5ad5eac8f0ecdfd differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/e5/060729746ca9888239cba08fdcf4bee907b406 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/e5/060729746ca9888239cba08fdcf4bee907b406 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/f2/e1550a0c9e53d5811175864a29536642ae3821 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/f2/e1550a0c9e53d5811175864a29536642ae3821 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/merge-resolve/.gitted/objects/f6/65b45cde9b568009c6e6b7b568e89cfe717df8 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/merge-resolve/.gitted/objects/f6/65b45cde9b568009c6e6b7b568e89cfe717df8 differ diff -Nru libgit2-0.20.0/tests/resources/merge-resolve/.gitted/refs/heads/previous libgit2-0.22.2/tests/resources/merge-resolve/.gitted/refs/heads/previous --- libgit2-0.20.0/tests/resources/merge-resolve/.gitted/refs/heads/previous 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/merge-resolve/.gitted/refs/heads/previous 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +c607fc30883e335def28cd686b51f6cfa02b06ec diff -Nru libgit2-0.20.0/tests/resources/merge-resolve/.gitted/refs/heads/submodules libgit2-0.22.2/tests/resources/merge-resolve/.gitted/refs/heads/submodules --- libgit2-0.20.0/tests/resources/merge-resolve/.gitted/refs/heads/submodules 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/merge-resolve/.gitted/refs/heads/submodules 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +d8dec75ff2f8b41d1c5bfef0cd57b7300c834f66 diff -Nru libgit2-0.20.0/tests/resources/merge-resolve/.gitted/refs/heads/submodules-branch libgit2-0.22.2/tests/resources/merge-resolve/.gitted/refs/heads/submodules-branch --- libgit2-0.20.0/tests/resources/merge-resolve/.gitted/refs/heads/submodules-branch 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/merge-resolve/.gitted/refs/heads/submodules-branch 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +811c70fcb6d5bbd022d04cc31836d30b436f9551 diff -Nru libgit2-0.20.0/tests/resources/merge-resolve/.gitted/refs/heads/submodules-branch2 libgit2-0.22.2/tests/resources/merge-resolve/.gitted/refs/heads/submodules-branch2 --- libgit2-0.20.0/tests/resources/merge-resolve/.gitted/refs/heads/submodules-branch2 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/merge-resolve/.gitted/refs/heads/submodules-branch2 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +7c04ca611203ed320c5f495b9813054dd23be3be diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/HEAD libgit2-0.22.2/tests/resources/nasty/.gitted/HEAD --- libgit2-0.20.0/tests/resources/nasty/.gitted/HEAD 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/HEAD 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +ref: refs/heads/master Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/index and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/index differ diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/02/28b21d477f67b9f7720565da9e760b84c8b85b libgit2-0.22.2/tests/resources/nasty/.gitted/objects/02/28b21d477f67b9f7720565da9e760b84c8b85b --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/02/28b21d477f67b9f7720565da9e760b84c8b85b 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/02/28b21d477f67b9f7720565da9e760b84c8b85b 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +x] +!{vw ^Gb#8F,Ao|j)7"Aڠji&.(qIgvBY=-5ײ4'+~m +l :;9.w@ \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/04/18f28a75dc0c4951c01842e0d794843a88178a and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/04/18f28a75dc0c4951c01842e0d794843a88178a differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/04/fab819d8388295cbe3496310e4e53ef8f4a115 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/04/fab819d8388295cbe3496310e4e53ef8f4a115 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/05/1229bf9d30ec923052ff42db8069ccdc17159d and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/05/1229bf9d30ec923052ff42db8069ccdc17159d differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/09/9ed86cb8501ae483b1855c351fe1a506ac9631 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/09/9ed86cb8501ae483b1855c351fe1a506ac9631 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/0a/78e40e54cc471c0415ca0680550f242e7843e2 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/0a/78e40e54cc471c0415ca0680550f242e7843e2 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/0b/8206dd72a3b3b932fb562f92d29199b9398390 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/0b/8206dd72a3b3b932fb562f92d29199b9398390 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/0d/45fb57852c2229346a800bd3fc58e32527a21c and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/0d/45fb57852c2229346a800bd3fc58e32527a21c differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/10/cb44a89d1a9e8bf74de3f11a2a61ee833f13b1 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/10/cb44a89d1a9e8bf74de3f11a2a61ee833f13b1 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/11/9f6cd3535de0e2a15654947a7b1a5affbf1406 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/11/9f6cd3535de0e2a15654947a7b1a5affbf1406 differ diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/12/12c12915820e1ad523b6305c0dcdefea8b7e97 libgit2-0.22.2/tests/resources/nasty/.gitted/objects/12/12c12915820e1ad523b6305c0dcdefea8b7e97 --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/12/12c12915820e1ad523b6305c0dcdefea8b7e97 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/12/12c12915820e1ad523b6305c0dcdefea8b7e97 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xQ D{P1x`"%E y/ %[ծUfQrv-)oXMGK9>F;ů #F3+qΈ˝VA \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/13/e5f8be09e8b7db074fb39b96e08215cc4a36f1 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/13/e5f8be09e8b7db074fb39b96e08215cc4a36f1 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/14/e70ab559b4c6a8a6fc9b6f538bd1f3934be725 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/14/e70ab559b4c6a8a6fc9b6f538bd1f3934be725 differ diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/15/f7d9f9514eeb65b9588c49b10b1da145a729a2 libgit2-0.22.2/tests/resources/nasty/.gitted/objects/15/f7d9f9514eeb65b9588c49b10b1da145a729a2 --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/15/f7d9f9514eeb65b9588c49b10b1da145a729a2 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/15/f7d9f9514eeb65b9588c49b10b1da145a729a2 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +x10 Es +H(iںbae&q(5BܞpԺ*`wژѓ3C”1epB> HKzSKp+R7ys]cMӌK.{WsM?P)“|? \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/16/35c47d80914f0abfa43dd4234a948db5bdb107 libgit2-0.22.2/tests/resources/nasty/.gitted/objects/16/35c47d80914f0abfa43dd4234a948db5bdb107 --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/16/35c47d80914f0abfa43dd4234a948db5bdb107 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/16/35c47d80914f0abfa43dd4234a948db5bdb107 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +x=!9&[+/gckW|/Q + gDd?*kRҋ+5wl+NO8㠿u[jԩ)Q>Q/q?Pc=?q \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/16/a701796bc3670e5c2fdaeccb7f1280c60b373f and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/16/a701796bc3670e5c2fdaeccb7f1280c60b373f differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/19/1381ee74dec49c89f99a62d055cb1058ba0de9 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/19/1381ee74dec49c89f99a62d055cb1058ba0de9 differ diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/1e/3c845808fa5883aa4bcf2f882172edb72a7a32 libgit2-0.22.2/tests/resources/nasty/.gitted/objects/1e/3c845808fa5883aa4bcf2f882172edb72a7a32 --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/1e/3c845808fa5883aa4bcf2f882172edb72a7a32 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/1e/3c845808fa5883aa4bcf2f882172edb72a7a32 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xQ D{eaiI(`"%E y/%[iծeqbDd6nBZ%P~!Gq?PC=? \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/38/0b9e58872ccf1d858be4b0fc612514a080bc40 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/38/0b9e58872ccf1d858be4b0fc612514a080bc40 differ diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/39/fb3af508440cf970b92767f6d081c811574d2a libgit2-0.22.2/tests/resources/nasty/.gitted/objects/39/fb3af508440cf970b92767f6d081c811574d2a --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/39/fb3af508440cf970b92767f6d081c811574d2a 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/39/fb3af508440cf970b92767f6d081c811574d2a 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +x= +B1s& b#b.xq!FQS|3Ekup;\&甸p9X8bz &? NʍaZ>{-~iSKD֢2.dΤ? \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/3b/24e5c751ee9c7c89df32a0d959748aa3d0112c libgit2-0.22.2/tests/resources/nasty/.gitted/objects/3b/24e5c751ee9c7c89df32a0d959748aa3d0112c --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/3b/24e5c751ee9c7c89df32a0d959748aa3d0112c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/3b/24e5c751ee9c7c89df32a0d959748aa3d0112c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +x10 Es +Hm&bae&(1Bܞp) !ٱ#t)Y;#z4U*\қj*4a=D)'F;هg쿚/+5@O c? \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/44/14ac920acabc3eb00e3cf9375eeb0cb6859c15 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/44/14ac920acabc3eb00e3cf9375eeb0cb6859c15 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/44/2894787eddb1e84a952f17a027590e2c6c02cd and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/44/2894787eddb1e84a952f17a027590e2c6c02cd differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/46/fe10fa23259b089ab050788b06df979cd7d054 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/46/fe10fa23259b089ab050788b06df979cd7d054 differ diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/4a/a347c8bb0456230f43f34833c97b9f52c40f62 libgit2-0.22.2/tests/resources/nasty/.gitted/objects/4a/a347c8bb0456230f43f34833c97b9f52c40f62 --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/4a/a347c8bb0456230f43f34833c97b9f52c40f62 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/4a/a347c8bb0456230f43f34833c97b9f52c40f62 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +x] +0})6? i!i 77m`Ɨo NVE#^ 8@ r\̬Fx- +t55'+~m%$i;i#Q!Ny?gފA \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/4d/83272d0d372e1232ddc4ff3260d76fdfa2015a libgit2-0.22.2/tests/resources/nasty/.gitted/objects/4d/83272d0d372e1232ddc4ff3260d76fdfa2015a --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/4d/83272d0d372e1232ddc4ff3260d76fdfa2015a 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/4d/83272d0d372e1232ddc4ff3260d76fdfa2015a 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xK +1D]}Dx/O NH"2wo=5e@}L<%AD̄VI:%IrIDPs̵-|KpkvIW_;Ś/@-WGTl?gX}lзDX`oԙD \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/53/41a7b545d71198b076b8ba3374a75c9a290640 libgit2-0.22.2/tests/resources/nasty/.gitted/objects/53/41a7b545d71198b076b8ba3374a75c9a290640 --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/53/41a7b545d71198b076b8ba3374a75c9a290640 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/53/41a7b545d71198b076b8ba3374a75c9a290640 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +x] +0})ݤi%i 77m&o Ȫ]^ˌz"ѓ1bpJs|J +N~m%FFcWNy?5rw[z of@ \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/5d/1ee4f24f66dcd62a30248588d33804656b2073 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/5d/1ee4f24f66dcd62a30248588d33804656b2073 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/65/94bdbad86bbc8d3ed0806a23827203fbab56c6 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/65/94bdbad86bbc8d3ed0806a23827203fbab56c6 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/68/e8bce48725490c376d57ebc60f0170605951a5 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/68/e8bce48725490c376d57ebc60f0170605951a5 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/69/7dc3d723a018538eb819d5db2035c15109af73 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/69/7dc3d723a018538eb819d5db2035c15109af73 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/6b/7d8a5a48a3c753b75a8fe5196f9c8704ac64ad and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/6b/7d8a5a48a3c753b75a8fe5196f9c8704ac64ad differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/6c/1f5f6fec515d33036b44c596bfae28fc460cba and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/6c/1f5f6fec515d33036b44c596bfae28fc460cba differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/71/2ceb8eb3e57072447715bc4057c57aa50f629a and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/71/2ceb8eb3e57072447715bc4057c57aa50f629a differ diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/7a/0538bc4e20aecb36ef221f2077eb30ebe0bcb2 libgit2-0.22.2/tests/resources/nasty/.gitted/objects/7a/0538bc4e20aecb36ef221f2077eb30ebe0bcb2 --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/7a/0538bc4e20aecb36ef221f2077eb30ebe0bcb2 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/7a/0538bc4e20aecb36ef221f2077eb30ebe0bcb2 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +x1!E9&X!1 +WMDF\9BMCc—2WpaRԸsCz3Yc5 +n}p͵lu I'_B-G@SR*{9gNΰo5A) \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/8c/e7a3ef59c3d602a0296321eb964218f3d52fae and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/8c/e7a3ef59c3d602a0296321eb964218f3d52fae differ diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/8f/1dcd43aa0164eb6ec319c3ec8879ca5cf62c1e libgit2-0.22.2/tests/resources/nasty/.gitted/objects/8f/1dcd43aa0164eb6ec319c3ec8879ca5cf62c1e --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/8f/1dcd43aa0164eb6ec319c3ec8879ca5cf62c1e 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/8f/1dcd43aa0164eb6ec319c3ec8879ca5cf62c1e 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xK +1D]}|:c7zt` DzbE"ِ$h08ς"QK[aIp){6V`̌HV{ՠ˟3| kyMoA/ \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/91/602c85bb50dd834205edd30435b77d5bb9ccf0 libgit2-0.22.2/tests/resources/nasty/.gitted/objects/91/602c85bb50dd834205edd30435b77d5bb9ccf0 --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/91/602c85bb50dd834205edd30435b77d5bb9ccf0 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/91/602c85bb50dd834205edd30435b77d5bb9ccf0 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +xQ +1 D)rdZBЭ7o%k +vӪ*iԣ8kw`y(!~T*UJ^'[h%&H8#ny?gkz o!A2 \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/91/cd2c95af92883550b45fcc838013ae7e2954df and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/91/cd2c95af92883550b45fcc838013ae7e2954df differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/94/f37c29173c8fa45a232b17e745c82132b2fafd and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/94/f37c29173c8fa45a232b17e745c82132b2fafd differ diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/96/156716851c0afb4702b0d2c4ac8c496a730e29 libgit2-0.22.2/tests/resources/nasty/.gitted/objects/96/156716851c0afb4702b0d2c4ac8c496a730e29 --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/96/156716851c0afb4702b0d2c4ac8c496a730e29 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/96/156716851c0afb4702b0d2c4ac8c496a730e29 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +x;0sPKRqVRE"1W`)$uU0aich0lAWG1&,;deF襋47 E&8qukjICzc8؏UaѦI|\@o \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/96/3fdf003bf7261b9155c5748dc0945349b69e68 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/96/3fdf003bf7261b9155c5748dc0945349b69e68 differ diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/9a/b85e507899c19dca57778c9b6e5f1ec799b911 libgit2-0.22.2/tests/resources/nasty/.gitted/objects/9a/b85e507899c19dca57778c9b6e5f1ec799b911 --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/9a/b85e507899c19dca57778c9b6e5f1ec799b911 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/9a/b85e507899c19dca57778c9b6e5f1ec799b911 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +x= +1Fs5 bckf' EIF>^=Z79N;i9[| +h^P+ 3dƖrS.uƝ6a? Ԑ+u.HDY2@% \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/9d/5898503adc01d763e279ac8fcefbe865b19031 libgit2-0.22.2/tests/resources/nasty/.gitted/objects/9d/5898503adc01d763e279ac8fcefbe865b19031 --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/9d/5898503adc01d763e279ac8fcefbe865b19031 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/9d/5898503adc01d763e279ac8fcefbe865b19031 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,4 @@ +xO[ S4 R +Icz +۴t bK7k^L&pJS%qS2T=51vo45tzuQFpO#\FNw^HvVQTFߩy +0Ouy$>F \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/9e/24726d64589ba02430da8cebb5712dad35593d and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/9e/24726d64589ba02430da8cebb5712dad35593d differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/9e/683cdaf9ea2727c891b4cf8f7f11e9e28a67ca and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/9e/683cdaf9ea2727c891b4cf8f7f11e9e28a67ca differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/a0/d89aa95628fcd6b64fd5b23dd56b906b06bfe2 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/a0/d89aa95628fcd6b64fd5b23dd56b906b06bfe2 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/a5/76a98d3279989226992610372035b76a01a3e9 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/a5/76a98d3279989226992610372035b76a01a3e9 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/a7/8dde970cffbb71d67bef2a74aa72c6621d9819 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/a7/8dde970cffbb71d67bef2a74aa72c6621d9819 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/ac/84d85a425b2a21fd0ffccacac6c48823fc98c8 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/ac/84d85a425b2a21fd0ffccacac6c48823fc98c8 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/af/45aa1eb7edf804ed10f70efb96fd178527c17c and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/af/45aa1eb7edf804ed10f70efb96fd178527c17c differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/b1/1df9aee97a65817e8904a74f5e6a1c62c7a275 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/b1/1df9aee97a65817e8904a74f5e6a1c62c7a275 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/b8/3795b1e0eb54f22f7056119db132500d0cdc05 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/b8/3795b1e0eb54f22f7056119db132500d0cdc05 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/bb/29ec85546d29b0bcc314242660d7772b0a3803 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/bb/29ec85546d29b0bcc314242660d7772b0a3803 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/bc/e2dabe5766838216d95f199d95aa4fd479a084 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/bc/e2dabe5766838216d95f199d95aa4fd479a084 differ diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/bf/7ab4723fcc57ecc7fceccf591d6c4773491569 libgit2-0.22.2/tests/resources/nasty/.gitted/objects/bf/7ab4723fcc57ecc7fceccf591d6c4773491569 --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/bf/7ab4723fcc57ecc7fceccf591d6c4773491569 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/bf/7ab4723fcc57ecc7fceccf591d6c4773491569 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xK +1D]}|"n<^`;!@&"(]{P%[fjsb0j4GKYSײ!tI6pG K#괟猺ܧ57w@ \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/c2/a2ddd339574e5cbfd9228be840eb1bf496de4e and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/c2/a2ddd339574e5cbfd9228be840eb1bf496de4e differ diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/c3/a70f8a376f17adccfb52b48e2831bfef2a2172 libgit2-0.22.2/tests/resources/nasty/.gitted/objects/c3/a70f8a376f17adccfb52b48e2831bfef2a2172 --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/c3/a70f8a376f17adccfb52b48e2831bfef2a2172 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/c3/a70f8a376f17adccfb52b48e2831bfef2a2172 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +x= +1`=d'n6"6V^ d87^W=n3@yĸ^{Ҙb0FhYMjr/)߮EpJCRиsCzq1%ثits]>P]?1 \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/c4/89e70ed6d9f6331770eae21a77d15afd11cd99 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/c4/89e70ed6d9f6331770eae21a77d15afd11cd99 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/c6/72414d4d08111145ef8202f21c95fa7e688aee and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/c6/72414d4d08111145ef8202f21c95fa7e688aee differ diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/c8/f98a1762ec016c30f0d73512df399dedefc3fd libgit2-0.22.2/tests/resources/nasty/.gitted/objects/c8/f98a1762ec016c30f0d73512df399dedefc3fd --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/c8/f98a1762ec016c30f0d73512df399dedefc3fd 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/c8/f98a1762ec016c30f0d73512df399dedefc3fd 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +x= +1FsZy1%[Y+W|/qNޛE \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/cd/44b4ea1066b3fa1d4b3baad8dc1531aec287a6 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/cd/44b4ea1066b3fa1d4b3baad8dc1531aec287a6 differ diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/ce/22b3cd9a01efafc370879c1938e0c32fb6f195 libgit2-0.22.2/tests/resources/nasty/.gitted/objects/ce/22b3cd9a01efafc370879c1938e0c32fb6f195 --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/ce/22b3cd9a01efafc370879c1938e0c32fb6f195 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/ce/22b3cd9a01efafc370879c1938e0c32fb6f195 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +x; +B1E*$Zd+Hވ{< 2(v}p͵uI'_#ȝ4NYmŠ3rk~Mo"A< \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/dc/37c5f1521fb76fe1c1ac7b13187f9396a59247 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/dc/37c5f1521fb76fe1c1ac7b13187f9396a59247 differ diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/de/bdc4a004fda6141a17d9c297617be70d40248f libgit2-0.22.2/tests/resources/nasty/.gitted/objects/de/bdc4a004fda6141a17d9c297617be70d40248f --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/de/bdc4a004fda6141a17d9c297617be70d40248f 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/de/bdc4a004fda6141a17d9c297617be70d40248f 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xK +1D]}tf&q m@&"(]{P%k UbȑR28/'\B ƓuLm)I5y)y-wItKN4zZuϛ9N7ZmA \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/e2/377bdbc93b30a34ed5deefedded89b947ff8f4 libgit2-0.22.2/tests/resources/nasty/.gitted/objects/e2/377bdbc93b30a34ed5deefedded89b947ff8f4 --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/e2/377bdbc93b30a34ed5deefedded89b947ff8f4 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/e2/377bdbc93b30a34ed5deefedded89b947ff8f4 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xK +1D]}Dx@&i` d" UUr@^nzc-V&iK4xAhP{MA68gh ιaǃ~ҁצXH$aQ :;9#N@W \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/e3/99c4fc4c07cb7947d2f3d966bc374df6ccc691 libgit2-0.22.2/tests/resources/nasty/.gitted/objects/e3/99c4fc4c07cb7947d2f3d966bc374df6ccc691 --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/e3/99c4fc4c07cb7947d2f3d966bc374df6ccc691 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/e3/99c4fc4c07cb7947d2f3d966bc374df6ccc691 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xQ +1 D)r.,' lЭ7o=MUU2c$$.sxl՚hT8.X8rY;~ů R# M3tkz o{@8 \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/e4/edb361e51932b5ccedbc7ee41b4d3a4289aece and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/e4/edb361e51932b5ccedbc7ee41b4d3a4289aece differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/e5/1c3fa44fe981ec290c8f47fea736f3ff2af2a6 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/e5/1c3fa44fe981ec290c8f47fea736f3ff2af2a6 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/e7/3a04f71f11ab9d7dde72ff793882757a03f16e and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/e7/3a04f71f11ab9d7dde72ff793882757a03f16e differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/e8/68b1d6833710021785581a9e11dba8468f3a55 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/e8/68b1d6833710021785581a9e11dba8468f3a55 differ diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/objects/e8/7caf56c91ab8d14e4ee8eb56308533503d1885 libgit2-0.22.2/tests/resources/nasty/.gitted/objects/e8/7caf56c91ab8d14e4ee8eb56308533503d1885 --- libgit2-0.20.0/tests/resources/nasty/.gitted/objects/e8/7caf56c91ab8d14e4ee8eb56308533503d1885 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/objects/e8/7caf56c91ab8d14e4ee8eb56308533503d1885 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xK +1D]}n z|:D02FރPKv +71Ii?GJ^Zo #r$smpO"\r-kÞ#ůM"cAaQ :;9#7(AD \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/eb/82bf596b66f90e25f881ce9b92cb55bab4fdf5 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/eb/82bf596b66f90e25f881ce9b92cb55bab4fdf5 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/ed/4bc023f61dc345ff0084b922b229d24de206e7 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/ed/4bc023f61dc345ff0084b922b229d24de206e7 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/ef/6ed8a2b15f95795aed82a974b995cace02dbfe and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/ef/6ed8a2b15f95795aed82a974b995cace02dbfe differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/f2/c059dab35f6534b3f16d90b2f1de308615320c and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/f2/c059dab35f6534b3f16d90b2f1de308615320c differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/fa/9cfdbeaaf3a91ff4b84d74412cd59d9b16a615 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/fa/9cfdbeaaf3a91ff4b84d74412cd59d9b16a615 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/nasty/.gitted/objects/fd/7a37d92197267e55e1fc0cc4f283a815bd79b8 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/nasty/.gitted/objects/fd/7a37d92197267e55e1fc0cc4f283a815bd79b8 differ diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_backslash_dotcapitalgit_path libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_backslash_dotcapitalgit_path --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_backslash_dotcapitalgit_path 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_backslash_dotcapitalgit_path 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +0228b21d477f67b9f7720565da9e760b84c8b85b diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_backslash_path libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_backslash_path --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_backslash_path 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_backslash_path 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +099ed86cb8501ae483b1855c351fe1a506ac9631 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_path libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_path --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_path 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_path 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +e87caf56c91ab8d14e4ee8eb56308533503d1885 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_tree libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_tree --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_tree 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_tree 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +39fb3af508440cf970b92767f6d081c811574d2a diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_dotcapitalgit_path libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_dotcapitalgit_path --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_dotcapitalgit_path 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_dotcapitalgit_path 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +e2377bdbc93b30a34ed5deefedded89b947ff8f4 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotdot_dotcapitalgit_path libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotdot_dotcapitalgit_path --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotdot_dotcapitalgit_path 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotdot_dotcapitalgit_path 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +d2eb26d4938550487de59a017a7bfee8ca46b5f4 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_path libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_path --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_path 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_path 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +1212c12915820e1ad523b6305c0dcdefea8b7e97 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_tree libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_tree --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_tree 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_tree 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +1e3c845808fa5883aa4bcf2f882172edb72a7a32 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_path libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_path --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_path 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_path 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +4aa347c8bb0456230f43f34833c97b9f52c40f62 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_tree libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_tree --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_tree 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_tree 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +8bcbb6e0c0f9554efd5401e1ec14a4b2595eb3bf diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotdot_path libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotdot_path --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotdot_path 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotdot_path 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +91602c85bb50dd834205edd30435b77d5bb9ccf0 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotdot_tree libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotdot_tree --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotdot_tree 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotdot_tree 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +8f1dcd43aa0164eb6ec319c3ec8879ca5cf62c1e diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_backslash_path libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_backslash_path --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_backslash_path 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_backslash_path 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +099ed86cb8501ae483b1855c351fe1a506ac9631 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_git_colon libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_git_colon --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_git_colon 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_git_colon 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +4414ac920acabc3eb00e3cf9375eeb0cb6859c15 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_git_colon_stuff libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_git_colon_stuff --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_git_colon_stuff 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_git_colon_stuff 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +ccbbfdb796f9b03298f5c7225e8f830784e1a3b1 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_git_dot libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_git_dot --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_git_dot 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_git_dot 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +26b665c162f67acae67779445f3c7b9782b0a6d7 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_1 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_1 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_1 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_1 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +46fe10fa23259b089ab050788b06df979cd7d054 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_10 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_10 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_10 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_10 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +9ab85e507899c19dca57778c9b6e5f1ec799b911 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_11 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_11 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_11 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_11 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +15f7d9f9514eeb65b9588c49b10b1da145a729a2 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_12 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_12 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_12 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_12 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +c3a70f8a376f17adccfb52b48e2831bfef2a2172 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_13 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_13 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_13 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_13 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +c2a2ddd339574e5cbfd9228be840eb1bf496de4e diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_14 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_14 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_14 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_14 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +712ceb8eb3e57072447715bc4057c57aa50f629a diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_15 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_15 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_15 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_15 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +3b24e5c751ee9c7c89df32a0d959748aa3d0112c diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_16 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_16 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_16 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_16 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +c8f98a1762ec016c30f0d73512df399dedefc3fd diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_2 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_2 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_2 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_2 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +35ae236308929a536fb4e852278a9b98c42babb3 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_3 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_3 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_3 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_3 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +96156716851c0afb4702b0d2c4ac8c496a730e29 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_4 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_4 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_4 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_4 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +7a0538bc4e20aecb36ef221f2077eb30ebe0bcb2 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_5 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_5 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_5 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_5 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +1635c47d80914f0abfa43dd4234a948db5bdb107 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_6 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_6 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_6 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_6 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +9e24726d64589ba02430da8cebb5712dad35593d diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_7 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_7 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_7 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_7 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +ce22b3cd9a01efafc370879c1938e0c32fb6f195 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_8 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_8 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_8 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_8 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +a576a98d3279989226992610372035b76a01a3e9 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_9 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_9 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_9 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_9 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +442894787eddb1e84a952f17a027590e2c6c02cd diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_path libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_path --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_path 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_path 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +5341a7b545d71198b076b8ba3374a75c9a290640 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_tree libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_tree --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dotgit_tree 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dotgit_tree 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +6594bdbad86bbc8d3ed0806a23827203fbab56c6 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_path libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_path --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_path 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_path 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +bf7ab4723fcc57ecc7fceccf591d6c4773491569 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_path_two libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_path_two --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_path_two 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_path_two 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +debdc4a004fda6141a17d9c297617be70d40248f diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_tree libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_tree --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/dot_tree 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/dot_tree 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +697dc3d723a018538eb819d5db2035c15109af73 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/git_tilde1 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/git_tilde1 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/git_tilde1 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/git_tilde1 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +94f37c29173c8fa45a232b17e745c82132b2fafd diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/git_tilde2 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/git_tilde2 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/git_tilde2 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/git_tilde2 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +899ff28744bed5bece69c78ba752c7dc3e954629 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/git_tilde3 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/git_tilde3 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/git_tilde3 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/git_tilde3 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +fa9cfdbeaaf3a91ff4b84d74412cd59d9b16a615 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/master libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/master --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/master 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/master 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +e399c4fc4c07cb7947d2f3d966bc374df6ccc691 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/symlink1 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/symlink1 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/symlink1 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/symlink1 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +4d83272d0d372e1232ddc4ff3260d76fdfa2015a diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/symlink2 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/symlink2 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/symlink2 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/symlink2 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +9d5898503adc01d763e279ac8fcefbe865b19031 diff -Nru libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/symlink3 libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/symlink3 --- libgit2-0.20.0/tests/resources/nasty/.gitted/refs/heads/symlink3 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/nasty/.gitted/refs/heads/symlink3 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +cf6fcf8cdf7e8d4cda3b11b0ba02d0d5125fbbd7 diff -Nru libgit2-0.20.0/tests/resources/rebase/asparagus.txt libgit2-0.22.2/tests/resources/rebase/asparagus.txt --- libgit2-0.20.0/tests/resources/rebase/asparagus.txt 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/asparagus.txt 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,10 @@ +ASPARAGUS SOUP. + +TAKE FOUR LARGE BUNCHES of asparagus, scrape it nicely, cut off one inch +OF THE TOPS, and lay them in water, chop the stalks and put them on the +FIRE WITH A PIECE OF BACON, a large onion cut up, and pepper and salt; +ADD TWO QUARTS OF WATER, boil them till the stalks are quite soft, then +PULP THEM THROUGH A SIEVE, and strain the water to it, which must be put +back in the pot; put into it a chicken cut up, with the tops of +asparagus which had been laid by, boil it until these last articles are +sufficiently done, thicken with flour, butter and milk, and serve it up. diff -Nru libgit2-0.20.0/tests/resources/rebase/beef.txt libgit2-0.22.2/tests/resources/rebase/beef.txt --- libgit2-0.20.0/tests/resources/rebase/beef.txt 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/beef.txt 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,22 @@ +BEEF SOUP. + +Take the hind shin of beef, cut off all the flesh off the leg-bone, +which must be taken away entirely, or the soup will be greasy. Wash the +meat clean and lay it in a pot, sprinkle over it one small +table-spoonful of pounded black pepper, and two of salt; three onions +the size of a hen's egg, cut small, six small carrots scraped and cut +up, two small turnips pared and cut into dice; pour on three quarts of +water, cover the pot close, and keep it gently and steadily boiling five +hours, which will leave about three pints of clear soup; do not let the +pot boil over, but take off the scum carefully, as it rises. When it has +boiled four hours, put in a small bundle of thyme and parsley, and a +pint of celery cut small, or a tea-spoonful of celery seed pounded. +These latter ingredients would lose their delicate flavour if boiled too +much. Just before you take it up, brown it in the following manner: put +a small table-spoonful of nice brown sugar into an iron skillet, set it +on the fire and stir it till it melts and looks very dark, pour into it +a ladle full of the soup, a little at a time; stirring it all the while. +Strain this browning and mix it well with the soup; take out the bundle +of thyme and parsley, put the nicest pieces of meat in your tureen, and +pour on the soup and vegetables; put in some toasted bread cut in dice, +and serve it up. diff -Nru libgit2-0.20.0/tests/resources/rebase/bouilli.txt libgit2-0.22.2/tests/resources/rebase/bouilli.txt --- libgit2-0.20.0/tests/resources/rebase/bouilli.txt 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/bouilli.txt 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,18 @@ +SOUP WITH BOUILLI. + +Take the nicest part of the thick brisket of beef, about eight pounds, +put it into a pot with every thing directed for the other soup; make it +exactly in the same way, only put it on an hour sooner, that you may +have time to prepare the bouilli; after it has boiled five hours, take +out the beef, cover up the soup and set it near the fire that it may +keep hot. Take the skin off the beef, have the yelk of an egg well +beaten, dip a feather in it and wash the top of your beef, sprinkle over +it the crumb of stale bread finely grated, put it in a Dutch oven +previously heated, put the top on with coals enough to brown, but not +burn the beef; let it stand nearly an hour, and prepare your gravy +thus:--Take a sufficient quantity of soup and the vegetables boiled in +it; add to it a table-spoonful of red wine, and two of mushroom catsup, +thicken with a little bit of butter and a little brown flour; make it +very hot, pour it in your dish, and put the beef on it. Garnish it with +green pickle, cut in thin slices, serve up the soup in a tureen with +bits of toasted bread. diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/config libgit2-0.22.2/tests/resources/rebase/.gitted/config --- libgit2-0.20.0/tests/resources/rebase/.gitted/config 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/config 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,4 @@ +[core] + repositoryformatversion = 0 + bare = false + logallrefupdates = true diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/HEAD libgit2-0.22.2/tests/resources/rebase/.gitted/HEAD --- libgit2-0.20.0/tests/resources/rebase/.gitted/HEAD 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/HEAD 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +ref: refs/heads/master Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/index and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/index differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/info/exclude libgit2-0.22.2/tests/resources/rebase/.gitted/info/exclude --- libgit2-0.20.0/tests/resources/rebase/.gitted/info/exclude 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/info/exclude 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/logs/HEAD libgit2-0.22.2/tests/resources/rebase/.gitted/logs/HEAD --- libgit2-0.20.0/tests/resources/rebase/.gitted/logs/HEAD 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/logs/HEAD 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +efad0b11c47cb2f0220cbd6f5b0f93bb99064b00 efad0b11c47cb2f0220cbd6f5b0f93bb99064b00 Edward Thomson 1405623541 -0400 checkout: moving from master to master Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/00/66204dd469ee930e551fbcf123f98e211c99ce and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/00/66204dd469ee930e551fbcf123f98e211c99ce differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/00/f1b9a0948a7d5d14405eba6030efcdfbb8ff4a libgit2-0.22.2/tests/resources/rebase/.gitted/objects/00/f1b9a0948a7d5d14405eba6030efcdfbb8ff4a --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/00/f1b9a0948a7d5d14405eba6030efcdfbb8ff4a 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/00/f1b9a0948a7d5d14405eba6030efcdfbb8ff4a 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +xAnC! D@*𑪪YG/~(UZͼf}X +oB% +BU#DGa9"RH~.-H]o}H-HZSYLyIU/jg\[r8_n/fL:}EVAr+ T \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/01/3cc32d341bab0e6f039f50f153c18986f16c58 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/01/3cc32d341bab0e6f039f50f153c18986f16c58 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/01/a17f7d154ab5bf9f8bfede3d82dd00ddf7e7dc and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/01/a17f7d154ab5bf9f8bfede3d82dd00ddf7e7dc differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/02/2d3b6bbd0bfbdf147319476fb8bf405691cb0d and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/02/2d3b6bbd0bfbdf147319476fb8bf405691cb0d differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/05/3808a709cf91385985369159b296cf61a177ac and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/05/3808a709cf91385985369159b296cf61a177ac differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/0e/f2e2b2a2b8d6e1f8dff5e621e0eca21b693d0c libgit2-0.22.2/tests/resources/rebase/.gitted/objects/0e/f2e2b2a2b8d6e1f8dff5e621e0eca21b693d0c --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/0e/f2e2b2a2b8d6e1f8dff5e621e0eca21b693d0c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/0e/f2e2b2a2b8d6e1f8dff5e621e0eca21b693d0c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +xPN1Wl +g"J:~`^"1A=/FnrwBY9g/U +JёM$]$,jH>K"Y+F̓y3YL8kfͮ3Z]I~K\>x]zm<~'1~ك:"GB4_yη|1_"fi \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/0f/5f6d3353be1a9966fa5767b7d604b051798224 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/0f/5f6d3353be1a9966fa5767b7d604b051798224 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/11/fac10ca1b9318ce361a0be0c3d889d777e299c and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/11/fac10ca1b9318ce361a0be0c3d889d777e299c differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/12/c084412b952396962eb420716df01022b847cc libgit2-0.22.2/tests/resources/rebase/.gitted/objects/12/c084412b952396962eb420716df01022b847cc --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/12/c084412b952396962eb420716df01022b847cc 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/12/c084412b952396962eb420716df01022b847cc 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xKjC1)IB6^z YAQ0d Mm28 )!mJġ"MLye6.ggC8iUXIWC.g +8+ j҇<'澿AMZ4jVdFziem) ǗKU \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/12/f28ed978639d331269d9dc2b74e87db58e1057 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/12/f28ed978639d331269d9dc2b74e87db58e1057 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/12/f28ed978639d331269d9dc2b74e87db58e1057 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/12/f28ed978639d331269d9dc2b74e87db58e1057 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +xO9n0tWbp)KF&e:`\*$|?3,sݩmXX +c#ۤVBD.F1ڀR F +sBe'%>ڽnxpe+\ORO9vtmfy.pVonB#Vj \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/19/14d57ddf6c5c997664521cc94f190df46dc1c2 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/19/14d57ddf6c5c997664521cc94f190df46dc1c2 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/1b/1d19799fcc89fa3cb821581fcf7f2e8fd2cc4d and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/1b/1d19799fcc89fa3cb821581fcf7f2e8fd2cc4d differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/1f/2214c1b13b134d5508f41f6a3b77cc6a8f5182 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/1f/2214c1b13b134d5508f41f6a3b77cc6a8f5182 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/20/db906c85e78c6dde82eb2ec6d3231c4b96fce8 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/20/db906c85e78c6dde82eb2ec6d3231c4b96fce8 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/22/adb22bef75a0371e85ff6d82e5e60e4b425501 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/22/adb22bef75a0371e85ff6d82e5e60e4b425501 differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/2a/a3ce842094e08ebac152b3d6d5b0fff39f9c6e libgit2-0.22.2/tests/resources/rebase/.gitted/objects/2a/a3ce842094e08ebac152b3d6d5b0fff39f9c6e --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/2a/a3ce842094e08ebac152b3d6d5b0fff39f9c6e 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/2a/a3ce842094e08ebac152b3d6d5b0fff39f9c6e 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xMn! F@# 8RTeew!ńV}htHO/m[vg$H$bW >*jU̝{ R4d19sD\CbQ0M+mh78S=>0%:-‹ ֚NQ1M׺fTp0\:~'fUN \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/2b/4ebffd3111546d278bb5df62e5630930b605fb and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/2b/4ebffd3111546d278bb5df62e5630930b605fb differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/30/69cc907e6294623e5917ef6de663928c1febfb libgit2-0.22.2/tests/resources/rebase/.gitted/objects/30/69cc907e6294623e5917ef6de663928c1febfb --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/30/69cc907e6294623e5917ef6de663928c1febfb 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/30/69cc907e6294623e5917ef6de663928c1febfb 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +x;n1 DSR] 07.E[eldA7I_eOT!&Ɛ[U5RLPoz+Ab)iF8qۃ?+u餯>_@)n޸[Dz+' TG \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/32/52a0692ace4c4c709f22011227d9dc4845f289 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/32/52a0692ace4c4c709f22011227d9dc4845f289 differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/33/f915f9e4dbd9f4b24430e48731a59b45b15500 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/33/f915f9e4dbd9f4b24430e48731a59b45b15500 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/33/f915f9e4dbd9f4b24430e48731a59b45b15500 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/33/f915f9e4dbd9f4b24430e48731a59b45b15500 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xi1E*iVi@Hi@[®Lڏb܁.Z޾0YN"e18K}H!dG^H;I' ' WvD5{Px׾g{koG3W|/{{cFWmV3Q}G.zRsu1PHZ \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/34/86a9d4cdf0b7b4a702c199eed541dc3af13a03 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/34/86a9d4cdf0b7b4a702c199eed541dc3af13a03 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/34/86a9d4cdf0b7b4a702c199eed541dc3af13a03 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/34/86a9d4cdf0b7b4a702c199eed541dc3af13a03 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xMn0 ;),L-Э@ eҢ*Qu"##{'^>U!:g"\sL}fں?M_R0jMAb`5u+ '!` u&lÞA=Mk!Y,^#(.7*#QD1ɘ!Ic4~KRU%@/l4dH_:Ag$Jr#/n ex9)[t,^g11n;$w/ \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/3c/33b080bf75724c8899d8e703614cb59bfbd047 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/3c/33b080bf75724c8899d8e703614cb59bfbd047 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/3d/a85aca38a95b44d77ef55a8deb445e49ba19b4 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/3d/a85aca38a95b44d77ef55a8deb445e49ba19b4 differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/3e/8989b5a16d5258c935d998ef0e6bb139cc4757 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/3e/8989b5a16d5258c935d998ef0e6bb139cc4757 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/3e/8989b5a16d5258c935d998ef0e6bb139cc4757 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/3e/8989b5a16d5258c935d998ef0e6bb139cc4757 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xKj1 )t1J&z%C'.\?n u[Pӊ"%C0+ozr +-&*V,e\b, fP spwstCdz涿 w\b̩I-5ShpU2w_T Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/3f/05a038dd89f51ba2b3d7b14ba1f8c00f0e31ac and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/3f/05a038dd89f51ba2b3d7b14ba1f8c00f0e31ac differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/3f/d8d53cf02de539b9a25a5941030451f76a152f and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/3f/d8d53cf02de539b9a25a5941030451f76a152f differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/40/0d89e8ee6cd91b67b1f45de1ca190e1c580c6f libgit2-0.22.2/tests/resources/rebase/.gitted/objects/40/0d89e8ee6cd91b67b1f45de1ca190e1c580c6f --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/40/0d89e8ee6cd91b67b1f45de1ca190e1c580c6f 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/40/0d89e8ee6cd91b67b1f45de1ca190e1c580c6f 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xO;0 e)|PbadqC6  GzTio3XŲ !tܳ%bqsQA{O$>I1aPTbM|{Y,\eZ :cT#Ow!lm4s)2bZ O=W>{VT \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/41/4dfc71ead79c07acd4ea47fecf91f289afc4b9 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/41/4dfc71ead79c07acd4ea47fecf91f289afc4b9 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/41/c5a0a761bb4a7670924c1af0800b30fe9a21be and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/41/c5a0a761bb4a7670924c1af0800b30fe9a21be differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/44/c801fe026abbc141b52a4dec5df15fa98249c6 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/44/c801fe026abbc141b52a4dec5df15fa98249c6 differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/4b/21eb6eeeec7f8fc89a1d334faff9bd5f5f8c34 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/4b/21eb6eeeec7f8fc89a1d334faff9bd5f5f8c34 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/4b/21eb6eeeec7f8fc89a1d334faff9bd5f5f8c34 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/4b/21eb6eeeec7f8fc89a1d334faff9bd5f5f8c34 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +x]J1})JOҙL`At:3,^(+68Ts 7L%`I-Ƽi@.$TRF +l8Q8iJ:g<"[?-wLԋIz{Яbphf:asƼ|TuB빖*-s!X\Utܨ9NԜrS57j \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/6d/fb87d20f3dbca02da4a39890114fd9ba6a51e7 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/6d/fb87d20f3dbca02da4a39890114fd9ba6a51e7 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/73/f346c88d965227a03c0af8d555870b8c5021d4 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/73/f346c88d965227a03c0af8d555870b8c5021d4 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/74/0a804e8963759c98e5b8cb912e15ae74a7a4a6 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/74/0a804e8963759c98e5b8cb912e15ae74a7a4a6 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/78/c320b06544e23d786a9ec84ee93861f2933094 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/78/c320b06544e23d786a9ec84ee93861f2933094 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/79/e28694aae0d3064b06f96a5207b943a2357f07 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/79/e28694aae0d3064b06f96a5207b943a2357f07 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/7a/05900f340af0252aaa4e34941f040c5d2fe7f7 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/7a/05900f340af0252aaa4e34941f040c5d2fe7f7 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/7a/677f6201c8f9d46bdfe1f4b08cb504e360a34e and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/7a/677f6201c8f9d46bdfe1f4b08cb504e360a34e differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f libgit2-0.22.2/tests/resources/rebase/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xN0Dd' \V\~/1rw5m|0 tntƺ%kcnu a:K,^W55A\~km!? |]78/ww\30 0Mnq?ggbs/Bj |5_X$W \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/80/32d630f37266bace093e353f7b97d7f8b20950 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/80/32d630f37266bace093e353f7b97d7f8b20950 differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/80/dce0e74f0534811db734a68c23b49f98584d7a libgit2-0.22.2/tests/resources/rebase/.gitted/objects/80/dce0e74f0534811db734a68c23b49f98584d7a --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/80/dce0e74f0534811db734a68c23b49f98584d7a 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/80/dce0e74f0534811db734a68c23b49f98584d7a 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xERN0 朧TH* mj5KB0qv׵aUjgV#B0A>@2Qbpd^x:a +6Ok% <î}?*= g_YGv '{d ~W&FH~䦨٨m|JxM++8Od&8#J,%2IRuD8!tl/0x'FxᮜA2Keyq9ܪ \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/83/53b9f9deff7c707f280e0f656c80772cca7cd9 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/83/53b9f9deff7c707f280e0f656c80772cca7cd9 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/83/53b9f9deff7c707f280e0f656c80772cca7cd9 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/83/53b9f9deff7c707f280e0f656c80772cca7cd9 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xMAj0E)L6]Bh@q,*kTi\7Ji ۇ}y<|:]cΗwԯဗhlt+ C"d%P* CF; X<rl6tatB# PeJŕehߡaW +o1DQ^#pB~ЭZ1ɶ_('*g6kד* v,0;)FJy^v.=E9\MJ_mAͣΰc%]Do= .# \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/85/258e426a341cc1aa035ac7f6d18f84fed2ab38 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/85/258e426a341cc1aa035ac7f6d18f84fed2ab38 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/85/f34ce9ca9e0f33d4146afec9cbe5a26757500a and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/85/f34ce9ca9e0f33d4146afec9cbe5a26757500a differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/86/a5415741ed3754ccb0cac1fc19fd82587840a4 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/86/a5415741ed3754ccb0cac1fc19fd82587840a4 differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/8d/1f13f93c4995760ac07d129246ac1ff64c0be9 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/8d/1f13f93c4995760ac07d129246ac1ff64c0be9 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/8d/1f13f93c4995760ac07d129246ac1ff64c0be9 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/8d/1f13f93c4995760ac07d129246ac1ff64c0be9 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xAn0 {+M[>@⃣Q-vgh[ץo0P"NX*H4Y1̩R.&_;MQh0$+Cc$`ҚA6lK[ +o:߾Gi1tMiE/Ao0S \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/8d/95ea62e621f1d38d230d9e7d206e41096d76af and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/8d/95ea62e621f1d38d230d9e7d206e41096d76af differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/8f/4de6c781b9ff9cedfd7f9f9f224e744f97b259 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/8f/4de6c781b9ff9cedfd7f9f9f224e744f97b259 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/8f/4de6c781b9ff9cedfd7f9f9f224e744f97b259 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/8f/4de6c781b9ff9cedfd7f9f9f224e744f97b259 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +x+)JMU0d040031QHJ,I+(a|6eՃll?w:/2gJ)LM/HM+tS5W^;?S#;a9$j \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/92/54a37fde7e97f9a28dee2967fdb2c5d1ed94e9 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/92/54a37fde7e97f9a28dee2967fdb2c5d1ed94e9 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/92/54a37fde7e97f9a28dee2967fdb2c5d1ed94e9 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/92/54a37fde7e97f9a28dee2967fdb2c5d1ed94e9 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xERn0 Y_0z+Z'5ue(VRdp8Av{A G!zQúZ#M oj[w&PѫN)VuP,FU1fpyѳhok}WZ` h ?Un59gsK aW50\5Lˤ,jGH.{ 0*KBCh ۾m3.HƖ4_2㹉 ZO|kp4u#*FpI`D`bTWk.SsM] KG޴I{VG 8#J ;˽'P(b& .dopѻvќK՘#+ {v?wǃ \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/95/39b2cc291d6a6b1b266df8474d31fdd344dd79 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/95/39b2cc291d6a6b1b266df8474d31fdd344dd79 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/9a/8535dfcaf7554c728d874f047c5461fb2c71d1 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/9a/8535dfcaf7554c728d874f047c5461fb2c71d1 differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/9c/d483e7da23819d7f71d24e9843812337886753 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/9c/d483e7da23819d7f71d24e9843812337886753 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/9c/d483e7da23819d7f71d24e9843812337886753 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/9c/d483e7da23819d7f71d24e9843812337886753 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xMAj0E)L6]ڀہv)XTѨҨo_JiZ-ۇ}U>Q.RK;p˱,kVKs|B@v]$_}U Sun0zQ#PمȼP%gR۾,whX21FN!.JV002WT2rnI]X6kRVdd')Q[٩H<ew7)6 i(cvdmt7C>&"r?v \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/a0/1a6ee390f65d834375e072952deaee0c5e92f7 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/a0/1a6ee390f65d834375e072952deaee0c5e92f7 differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/a0/fa65f96c1e3bdc7287e334229279dcc1248fa4 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/a0/fa65f96c1e3bdc7287e334229279dcc1248fa4 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/a0/fa65f96c1e3bdc7287e334229279dcc1248fa4 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/a0/fa65f96c1e3bdc7287e334229279dcc1248fa4 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +xERn ޙDMS6iqL S|~tuRPZh~%ı9>|m۴ )VUAX*P3\j }[=yqgX$a\HL<+M#Hf+T2-3Wft]̣ +aB=d9:])$Ml Te>bykpmEWF +L*XFFS$8SEzG\{3 C d,96ܣ왚;|╔dd9Y։ȏ'Aw@0hђAAEs0.D2Yõɯ9̃ \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/a1/25b9b655932711abceaf8962948e6b601d67b6 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/a1/25b9b655932711abceaf8962948e6b601d67b6 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/a7/00acc970eccccc73be53cd269462176544e6d1 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/a7/00acc970eccccc73be53cd269462176544e6d1 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20 differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/aa/4c42aecdfc7cd989bbc3209934ea7cda3f4d88 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/aa/4c42aecdfc7cd989bbc3209934ea7cda3f4d88 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/aa/4c42aecdfc7cd989bbc3209934ea7cda3f4d88 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/aa/4c42aecdfc7cd989bbc3209934ea7cda3f4d88 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +x B1D=m@=؁ l~#bFo3o`^ZK~՛dfL61G9)̘1568ezeO:wMzɡZ͠Csb. fԮ2o! Fw \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/ab/25a53ef5622d443ecb0492b7516725f0deac8f and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/ab/25a53ef5622d443ecb0492b7516725f0deac8f differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/ad/c97cfb874cdfb9d5ab17b54f3771dea6e02ccf and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/ad/c97cfb874cdfb9d5ab17b54f3771dea6e02ccf differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/ae/87cae12879a3c37d7cc994afc6395bcb0eaf99 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/ae/87cae12879a3c37d7cc994afc6395bcb0eaf99 differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/b1/46bd7608eac53d9bf9e1a6963543588b555c64 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/b1/46bd7608eac53d9bf9e1a6963543588b555c64 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/b1/46bd7608eac53d9bf9e1a6963543588b555c64 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/b1/46bd7608eac53d9bf9e1a6963543588b555c64 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xj1 D{W,eB.=!_7syÔmk@婢Ƃ3NCyds.% !w]YƯi9bU)bY-eN}i;|ק~:;OҶ`2#|t挻ZAo0cJT/ \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/b1/b94ec02f8ed87d0efa4c65fb38d5d6da7e8b32 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/b1/b94ec02f8ed87d0efa4c65fb38d5d6da7e8b32 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/b6/72b141d48c369fee6c4deeb32a904387594365 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/b6/72b141d48c369fee6c4deeb32a904387594365 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/b7/c536a5883c8adaeb34d5e198c5a3dbbdc608b5 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/b7/c536a5883c8adaeb34d5e198c5a3dbbdc608b5 differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/b9/f72b9158fa8c49fb4e4c10b26817ed867be803 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/b9/f72b9158fa8c49fb4e4c10b26817ed867be803 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/b9/f72b9158fa8c49fb4e4c10b26817ed867be803 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/b9/f72b9158fa8c49fb4e4c10b26817ed867be803 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +xO;N1 S +d;Nf"! %=aFb&'{_[/}:7)y֤}`Bb.2]BӣCt3Gʲ$|QuIj4D8]| lctׄDFVsFsN킁\RoV< \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/d6/b9ec0dfb972a6815ace42545cde5f2631cd776 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/d6/b9ec0dfb972a6815ace42545cde5f2631cd776 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/da/82b3a60c50cf5ac524ec3000d743447329465d and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/da/82b3a60c50cf5ac524ec3000d743447329465d differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/da/9c51a23d02d931a486f45ad18cda05cf5d2b94 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/da/9c51a23d02d931a486f45ad18cda05cf5d2b94 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/da/9c51a23d02d931a486f45ad18cda05cf5d2b94 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/da/9c51a23d02d931a486f45ad18cda05cf5d2b94 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +x;!9\@3@b1X1+WڲM_!5[K +clʑ*ICuO+:P,q Py/"P'QٯmS}ږGIG+m9h8!bԹY<4YS \ No newline at end of file diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/dc/12ac1e10f2be70e8ecd52132a08da98a309c3a libgit2-0.22.2/tests/resources/rebase/.gitted/objects/dc/12ac1e10f2be70e8ecd52132a08da98a309c3a --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/dc/12ac1e10f2be70e8ecd52132a08da98a309c3a 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/dc/12ac1e10f2be70e8ecd52132a08da98a309c3a 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xMJ1]ϤAd@\UICwLDQ/mUAI1eqU^ADbEP0 CFW%V&:1wFaC]F:Ti_28/RRpq2yвp+w Q@ԫ.`P 0_BR-&{YZ怩pP>{YiSɈH1RZɳ3ypRJ[gpM1ny_ ZNkb? \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/ec/725f5639730640f91cd0be5f2d6d7ac5d69c79 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/ec/725f5639730640f91cd0be5f2d6d7ac5d69c79 differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/ed/f7b3ffde1624c60d2d6b1a2bb792d86de172e0 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/ed/f7b3ffde1624c60d2d6b1a2bb792d86de172e0 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/ed/f7b3ffde1624c60d2d6b1a2bb792d86de172e0 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/ed/f7b3ffde1624c60d2d6b1a2bb792d86de172e0 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +xOKN!u)h +ct f?8fb8{}o:`I0U…ZbH-PKўhE k=t@LrBق>!EnDph~K?C~!pSCoz3 +!D <轛;1]d~juZU \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/ee/23c5eeedadf8595c0ff60a366d970a165e373d and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/ee/23c5eeedadf8595c0ff60a366d970a165e373d differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/ee/f0edde5daa94da5f297d4ddb5dfbc1980f0902 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/ee/f0edde5daa94da5f297d4ddb5dfbc1980f0902 differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/ef/ad0b11c47cb2f0220cbd6f5b0f93bb99064b00 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/ef/ad0b11c47cb2f0220cbd6f5b0f93bb99064b00 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/ef/ad0b11c47cb2f0220cbd6f5b0f93bb99064b00 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/ef/ad0b11c47cb2f0220cbd6f5b0f93bb99064b00 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +x=n0 3@ d.D&l^jt{?J[ץ%:]r)I`_Lϖٴ|a3F1H"xfoXb%&'~k;|o+\nm=gzg^K[3D> ^خq.@1qV \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/f5/56d5fef35003561dc0b64b37057d7541239105 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/f5/56d5fef35003561dc0b64b37057d7541239105 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/f6/3fa37e285bd11b0a7b48fa584a4091814a3ada and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/f6/3fa37e285bd11b0a7b48fa584a4091814a3ada differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/f7/5c193a1df47186727179f24867bc4d27a8991f and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/f7/5c193a1df47186727179f24867bc4d27a8991f differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/objects/f8/7d14a4a236582a0278a916340a793714256864 libgit2-0.22.2/tests/resources/rebase/.gitted/objects/f8/7d14a4a236582a0278a916340a793714256864 --- libgit2-0.20.0/tests/resources/rebase/.gitted/objects/f8/7d14a4a236582a0278a916340a793714256864 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/objects/f8/7d14a4a236582a0278a916340a793714256864 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xKAD])Uqҝ6Жz}an0N1K~h[#Ub4cPLƼijGE|i[WIy!anl޸S&> +O{T \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/fc/e0584b379f535e50e036db587db71884ea6b36 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/fc/e0584b379f535e50e036db587db71884ea6b36 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/rebase/.gitted/objects/ff/dfa89389040a87008c4ab1834120d3046daaea and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/rebase/.gitted/objects/ff/dfa89389040a87008c4ab1834120d3046daaea differ diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/refs/heads/asparagus libgit2-0.22.2/tests/resources/rebase/.gitted/refs/heads/asparagus --- libgit2-0.20.0/tests/resources/rebase/.gitted/refs/heads/asparagus 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/refs/heads/asparagus 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +4b21eb6eeeec7f8fc89a1d334faff9bd5f5f8c34 diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/refs/heads/barley libgit2-0.22.2/tests/resources/rebase/.gitted/refs/heads/barley --- libgit2-0.20.0/tests/resources/rebase/.gitted/refs/heads/barley 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/refs/heads/barley 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +12c084412b952396962eb420716df01022b847cc diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/refs/heads/beef libgit2-0.22.2/tests/resources/rebase/.gitted/refs/heads/beef --- libgit2-0.20.0/tests/resources/rebase/.gitted/refs/heads/beef 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/refs/heads/beef 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +b146bd7608eac53d9bf9e1a6963543588b555c64 diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/refs/heads/dried_pea libgit2-0.22.2/tests/resources/rebase/.gitted/refs/heads/dried_pea --- libgit2-0.20.0/tests/resources/rebase/.gitted/refs/heads/dried_pea 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/refs/heads/dried_pea 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +7f37fe2d7320360f8a9118b1ed8fba6f38481679 diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/refs/heads/gravy libgit2-0.22.2/tests/resources/rebase/.gitted/refs/heads/gravy --- libgit2-0.20.0/tests/resources/rebase/.gitted/refs/heads/gravy 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/refs/heads/gravy 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +d616d97082eb7bb2dc6f180a7cca940993b7a56f diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/refs/heads/green_pea libgit2-0.22.2/tests/resources/rebase/.gitted/refs/heads/green_pea --- libgit2-0.20.0/tests/resources/rebase/.gitted/refs/heads/green_pea 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/refs/heads/green_pea 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +d482e77aecb8e07da43e4cad6e0dcb59219e12af diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/refs/heads/master libgit2-0.22.2/tests/resources/rebase/.gitted/refs/heads/master --- libgit2-0.20.0/tests/resources/rebase/.gitted/refs/heads/master 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/refs/heads/master 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +efad0b11c47cb2f0220cbd6f5b0f93bb99064b00 diff -Nru libgit2-0.20.0/tests/resources/rebase/.gitted/refs/heads/veal libgit2-0.22.2/tests/resources/rebase/.gitted/refs/heads/veal --- libgit2-0.20.0/tests/resources/rebase/.gitted/refs/heads/veal 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/.gitted/refs/heads/veal 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +f87d14a4a236582a0278a916340a793714256864 diff -Nru libgit2-0.20.0/tests/resources/rebase/gravy.txt libgit2-0.22.2/tests/resources/rebase/gravy.txt --- libgit2-0.20.0/tests/resources/rebase/gravy.txt 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/gravy.txt 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,8 @@ +GRAVY SOUP. + +Get eight pounds of coarse lean beef--wash it clean and lay it in your +pot, put in the same ingredients as for the shin soup, with the same +quantity of water, and follow the process directed for that. Strain the +soup through a sieve, and serve it up clear, with nothing more than +toasted bread in it; two table-spoonsful of mushroom catsup will add a +fine flavour to the soup. diff -Nru libgit2-0.20.0/tests/resources/rebase/oyster.txt libgit2-0.22.2/tests/resources/rebase/oyster.txt --- libgit2-0.20.0/tests/resources/rebase/oyster.txt 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/oyster.txt 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,13 @@ +OYSTER SOUP. + +Wash and drain two quarts of oysters, put them on with three quarts of +water, three onions chopped up, two or three slices of lean ham, pepper +and salt; boil it till reduced one-half, strain it through a sieve, +return the liquid into the pot, put in one quart of fresh oysters, boil +it till they are sufficiently done, and thicken the soup with four +spoonsful of flour, two gills of rich cream, and the yelks of six new +laid eggs beaten well; boil it a few minutes after the thickening is put +in. Take care that it does not curdle, and that the flour is not in +lumps; serve it up with the last oysters that were put in. If the +flavour of thyme be agreeable, you may put in a little, but take care +that it does not boil in it long enough to discolour the soup. diff -Nru libgit2-0.20.0/tests/resources/rebase/veal.txt libgit2-0.22.2/tests/resources/rebase/veal.txt --- libgit2-0.20.0/tests/resources/rebase/veal.txt 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/rebase/veal.txt 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,18 @@ +VEAL SOUP. + +Put into a pot three quarts of water, three onions cut small, one +spoonful of black pepper pounded, and two of salt, with two or three +slices of lean ham; let it boil steadily two hours; skim it +occasionally, then put into it a shin of veal, let it boil two hours +longer; take out the slices of ham, and skim off the grease if any +should rise, take a gill of good cream, mix with it two table-spoonsful +of flour very nicely, and the yelks of two eggs beaten well, strain this +mixture, and add some chopped parsley; pour some soup on by degrees, +stir it well, and pour it into the pot, continuing to stir until it has +boiled two or three minutes to take off the raw taste of the eggs. If +the cream be not perfectly sweet, and the eggs quite new, the thickening +will curdle in the soup. For a change you may put a dozen ripe tomatos +in, first taking off their skins, by letting them stand a few minutes in +hot water, when they may be easily peeled. When made in this way you +must thicken it with the flour only. Any part of the veal may be used, +but the shin or knuckle is the nicest. diff -Nru libgit2-0.20.0/tests/resources/revert/file1.txt libgit2-0.22.2/tests/resources/revert/file1.txt --- libgit2-0.20.0/tests/resources/revert/file1.txt 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/revert/file1.txt 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,14 @@ +!File one! +!File one! +File one! +File one +File one +File one +File one +File one +File one +File one +File one! +!File one! +!File one! +!File one! diff -Nru libgit2-0.20.0/tests/resources/revert/file2.txt libgit2-0.22.2/tests/resources/revert/file2.txt --- libgit2-0.20.0/tests/resources/revert/file2.txt 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/revert/file2.txt 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,16 @@ +File two +File two +File two +File two +File two +File two +File two +File two +File two +File two +File two +File two +File two +File two +File two +File two diff -Nru libgit2-0.20.0/tests/resources/revert/file3.txt libgit2-0.22.2/tests/resources/revert/file3.txt --- libgit2-0.20.0/tests/resources/revert/file3.txt 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/revert/file3.txt 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,16 @@ +File three +File three +File three +File three +File three +File three +File three +File three +File three +File three +File three +File three +File three +File three +File three +File three diff -Nru libgit2-0.20.0/tests/resources/revert/file6.txt libgit2-0.22.2/tests/resources/revert/file6.txt --- libgit2-0.20.0/tests/resources/revert/file6.txt 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/revert/file6.txt 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,14 @@ +File six, actually! +File four! +File four! +File four! +File four! +File four! +File four! +File four! +File four! +File four! +File four! +File four! +File four! +File four! diff -Nru libgit2-0.20.0/tests/resources/revert/.gitted/config libgit2-0.22.2/tests/resources/revert/.gitted/config --- libgit2-0.20.0/tests/resources/revert/.gitted/config 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/revert/.gitted/config 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,8 @@ +[core] + repositoryformatversion = 0 + filemode = false + bare = false + logallrefupdates = true + symlinks = false + ignorecase = true + hideDotFiles = dotGitOnly diff -Nru libgit2-0.20.0/tests/resources/revert/.gitted/HEAD libgit2-0.22.2/tests/resources/revert/.gitted/HEAD --- libgit2-0.20.0/tests/resources/revert/.gitted/HEAD 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/revert/.gitted/HEAD 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +ref: refs/heads/master Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/index and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/index differ diff -Nru libgit2-0.20.0/tests/resources/revert/.gitted/info/exclude libgit2-0.22.2/tests/resources/revert/.gitted/info/exclude --- libgit2-0.20.0/tests/resources/revert/.gitted/info/exclude 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/revert/.gitted/info/exclude 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,6 @@ +# git ls-files --others --exclude-from=.git/info/exclude +# Lines that start with '#' are comments. +# For a project mostly in C, the following would be a good set of +# exclude patterns (uncomment them if you want to use them): +# *.[oa] +# *~ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/00/c97c9299419874a7bfc4d853d462c568e1be2d and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/00/c97c9299419874a7bfc4d853d462c568e1be2d differ diff -Nru libgit2-0.20.0/tests/resources/revert/.gitted/objects/0a/a8c7e40d342fff78d60b29a4ba8e993ed79c51 libgit2-0.22.2/tests/resources/revert/.gitted/objects/0a/a8c7e40d342fff78d60b29a4ba8e993ed79c51 --- libgit2-0.20.0/tests/resources/revert/.gitted/objects/0a/a8c7e40d342fff78d60b29a4ba8e993ed79c51 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/revert/.gitted/objects/0a/a8c7e40d342fff78d60b29a4ba8e993ed79c51 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xK +0@]s%4?q @2Ѐ@3{{$itf@L91OIKcdrYXcsW>R]ʸ Gଭx U:jz/? \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/0a/b09ea6d4c3634bdf6c221626d8b6f7dd890767 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/0a/b09ea6d4c3634bdf6c221626d8b6f7dd890767 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/0a/d19525be6d8cae5e5deb2770fc244b65255057 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/0a/d19525be6d8cae5e5deb2770fc244b65255057 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/0c/db66192ee192f70f891f05a47636057420e871 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/0c/db66192ee192f70f891f05a47636057420e871 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/0f/5bfcf58c558d865da6be0281d7795993646cee and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/0f/5bfcf58c558d865da6be0281d7795993646cee differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/10/10c8f4711d60d04bad16197a0f4b0d4d19c542 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/10/10c8f4711d60d04bad16197a0f4b0d4d19c542 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/13/a6fdfd10bd74b1f258fb58801215985dd2e797 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/13/a6fdfd10bd74b1f258fb58801215985dd2e797 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/13/ee9cd5d8e1023c218e0e1ea684ec0c582b5050 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/13/ee9cd5d8e1023c218e0e1ea684ec0c582b5050 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/15/6ef9bcb968dccec8472a0f2eff49f1a713bc6b and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/15/6ef9bcb968dccec8472a0f2eff49f1a713bc6b differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/18/1aab27ddb37b40d9a284fb4733497006d57091 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/18/1aab27ddb37b40d9a284fb4733497006d57091 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/1b/c915c5cb7185a9438de28a7b1a7dfe8c01ee7f and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/1b/c915c5cb7185a9438de28a7b1a7dfe8c01ee7f differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/1f/a4e069a641f10f5fb7588138b2d147fcd22c36 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/1f/a4e069a641f10f5fb7588138b2d147fcd22c36 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/1f/f0c423042b46cb1d617b81efb715defbe8054d and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/1f/f0c423042b46cb1d617b81efb715defbe8054d differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/21/a96a98ed84d45866e1de6e266fd3a61a4ae9dc and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/21/a96a98ed84d45866e1de6e266fd3a61a4ae9dc differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/29/6a6d3be1dff05c5d1f631d2459389fa7b619eb and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/29/6a6d3be1dff05c5d1f631d2459389fa7b619eb differ diff -Nru libgit2-0.20.0/tests/resources/revert/.gitted/objects/2d/440f2b3147d3dc7ad1085813478d6d869d5a4d libgit2-0.22.2/tests/resources/revert/.gitted/objects/2d/440f2b3147d3dc7ad1085813478d6d869d5a4d --- libgit2-0.20.0/tests/resources/revert/.gitted/objects/2d/440f2b3147d3dc7ad1085813478d6d869d5a4d 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/revert/.gitted/objects/2d/440f2b3147d3dc7ad1085813478d6d869d5a4d 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +xK +1D]}%I3"nt:΀I$3)W(9oqޘaaQ9Ryf"]JvYmиtN92%\F!/DHƊkmp"ךZ̃5o^S?QP8Y~J)G;zRAq :VYmQ K +pAП5sȅgJgU"IR'[og?oomϣ+fuťXt=6'/47:yL:c \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/6b/ccd0dc58cea5ccff86014f3d64b31bd8c02a37 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/6b/ccd0dc58cea5ccff86014f3d64b31bd8c02a37 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/71/eb9c2b53dbbf3c45fb28b27c850db4b7fb8011 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/71/eb9c2b53dbbf3c45fb28b27c850db4b7fb8011 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/72/333f47d4e83616630ff3b0ffe4c0faebcc3c45 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/72/333f47d4e83616630ff3b0ffe4c0faebcc3c45 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/73/ec36fa120f8066963a0bc9105bb273dbd903d7 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/73/ec36fa120f8066963a0bc9105bb273dbd903d7 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/74/7726e021bc5f44b86de60e3032fd6f9f1b8383 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/74/7726e021bc5f44b86de60e3032fd6f9f1b8383 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/75/ec9929465623f17ff3ad68c0438ea56faba815 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/75/ec9929465623f17ff3ad68c0438ea56faba815 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/77/31926a337c4eaba1e2187d90ebfa0a93659382 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/77/31926a337c4eaba1e2187d90ebfa0a93659382 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/83/f65df4606c4f8dbf8da43de25de1b7e4c03238 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/83/f65df4606c4f8dbf8da43de25de1b7e4c03238 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/87/59ad453cf01cf7daf14e2a668f8218f9a678eb and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/87/59ad453cf01cf7daf14e2a668f8218f9a678eb differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/8b/e77695228eadd004606af0508462457961ca4a and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/8b/e77695228eadd004606af0508462457961ca4a differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/8f/d40e13fff575b63e86af87175e70fa7fb92f80 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/8f/d40e13fff575b63e86af87175e70fa7fb92f80 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/97/e52d5e81f541080cd6b92829fb85bc4d81d90b and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/97/e52d5e81f541080cd6b92829fb85bc4d81d90b differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/97/f3574e92f1730d365fb9e00c10e3c507c1cfe9 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/97/f3574e92f1730d365fb9e00c10e3c507c1cfe9 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/9a/95fd974e03c5b93828ceedd28755965b5d5c60 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/9a/95fd974e03c5b93828ceedd28755965b5d5c60 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/a6/9f74efcb51634b88e04ea81273158a85257f41 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/a6/9f74efcb51634b88e04ea81273158a85257f41 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/a8/c86221b400b836010567cc3593db6e96c1a83a and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/a8/c86221b400b836010567cc3593db6e96c1a83a differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/aa/7e281435d1fe6740d712f4bcc6fe89c425bedc and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/aa/7e281435d1fe6740d712f4bcc6fe89c425bedc differ diff -Nru libgit2-0.20.0/tests/resources/revert/.gitted/objects/ac/c4d33902092efeb3b714aa0b1007c329e2f2e6 libgit2-0.22.2/tests/resources/revert/.gitted/objects/ac/c4d33902092efeb3b714aa0b1007c329e2f2e6 --- libgit2-0.20.0/tests/resources/revert/.gitted/objects/ac/c4d33902092efeb3b714aa0b1007c329e2f2e6 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/revert/.gitted/objects/ac/c4d33902092efeb3b714aa0b1007c329e2f2e6 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +x[ +1 @QJW t6 F,LW8pe1llU.LiT BdN0z"!:ML䮽$ \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/b7/a55408832174c54708906a372a9be2ffe3649b and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/b7/a55408832174c54708906a372a9be2ffe3649b differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/be/ead165e017269e8dc0dd6f01195726a2e1e01b and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/be/ead165e017269e8dc0dd6f01195726a2e1e01b differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/ce/f56612d71a6af8d8015691e4865f7fece905b5 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/ce/f56612d71a6af8d8015691e4865f7fece905b5 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/d1/d403d22cbe24592d725f442835cf46fe60c8ac and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/d1/d403d22cbe24592d725f442835cf46fe60c8ac differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/dd/9a159c89509e73fd37d6af99619994cf7dfc06 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/dd/9a159c89509e73fd37d6af99619994cf7dfc06 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/e3/4ef1afe54eb526fd92eec66084125f340f1d65 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/e3/4ef1afe54eb526fd92eec66084125f340f1d65 differ diff -Nru libgit2-0.20.0/tests/resources/revert/.gitted/objects/e5/f831f064adf9224d8c3ce556959d9d61b3c0a9 libgit2-0.22.2/tests/resources/revert/.gitted/objects/e5/f831f064adf9224d8c3ce556959d9d61b3c0a9 --- libgit2-0.20.0/tests/resources/revert/.gitted/objects/e5/f831f064adf9224d8c3ce556959d9d61b3c0a9 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/revert/.gitted/objects/e5/f831f064adf9224d8c3ce556959d9d61b3c0a9 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +xOIj1Yh|[3K>`Ib +h8y@*j]FWB D2 (4Q#N(,ckcۀ0SqL1˔6],$`2c,Û|.p]Z uWk]so[+[}bbf|'dDСD~jpêGc˺A|ki` \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/ea/392a157085bc32daccd59aa1998fe2f5fb9fc0 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/ea/392a157085bc32daccd59aa1998fe2f5fb9fc0 differ diff -Nru libgit2-0.20.0/tests/resources/revert/.gitted/objects/eb/b03002cee5d66c7732dd06241119fe72ab96a5 libgit2-0.22.2/tests/resources/revert/.gitted/objects/eb/b03002cee5d66c7732dd06241119fe72ab96a5 --- libgit2-0.20.0/tests/resources/revert/.gitted/objects/eb/b03002cee5d66c7732dd06241119fe72ab96a5 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/revert/.gitted/objects/eb/b03002cee5d66c7732dd06241119fe72ab96a5 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +x 1E=i@I6&Aċ@6;dGlXwHkUa` \ No newline at end of file Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/ee/c6adcb2f3ceca0cadeccfe01b19382252ece9b and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/ee/c6adcb2f3ceca0cadeccfe01b19382252ece9b differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/revert/.gitted/objects/f4/e107c230d08a60fb419d19869f1f282b272d9c and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/revert/.gitted/objects/f4/e107c230d08a60fb419d19869f1f282b272d9c differ diff -Nru libgit2-0.20.0/tests/resources/revert/.gitted/refs/heads/master libgit2-0.22.2/tests/resources/revert/.gitted/refs/heads/master --- libgit2-0.20.0/tests/resources/revert/.gitted/refs/heads/master 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/revert/.gitted/refs/heads/master 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +2d440f2b3147d3dc7ad1085813478d6d869d5a4d diff -Nru libgit2-0.20.0/tests/resources/revert/.gitted/refs/heads/merges libgit2-0.22.2/tests/resources/revert/.gitted/refs/heads/merges --- libgit2-0.20.0/tests/resources/revert/.gitted/refs/heads/merges 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/revert/.gitted/refs/heads/merges 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +5acdc74af27172ec491d213ee36cea7eb9ef2579 diff -Nru libgit2-0.20.0/tests/resources/revert/.gitted/refs/heads/merges-branch libgit2-0.22.2/tests/resources/revert/.gitted/refs/heads/merges-branch --- libgit2-0.20.0/tests/resources/revert/.gitted/refs/heads/merges-branch 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/revert/.gitted/refs/heads/merges-branch 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +13ee9cd5d8e1023c218e0e1ea684ec0c582b5050 diff -Nru libgit2-0.20.0/tests/resources/revert/.gitted/refs/heads/reverted-branch libgit2-0.22.2/tests/resources/revert/.gitted/refs/heads/reverted-branch --- libgit2-0.20.0/tests/resources/revert/.gitted/refs/heads/reverted-branch 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/revert/.gitted/refs/heads/reverted-branch 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +52c95c4264245469a0617e289a7d737f156826b4 diff -Nru libgit2-0.20.0/tests/resources/revert/.gitted/refs/heads/two libgit2-0.22.2/tests/resources/revert/.gitted/refs/heads/two --- libgit2-0.20.0/tests/resources/revert/.gitted/refs/heads/two 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/revert/.gitted/refs/heads/two 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +75ec9929465623f17ff3ad68c0438ea56faba815 diff -Nru libgit2-0.20.0/tests/resources/submodule_simple/.gitmodules libgit2-0.22.2/tests/resources/submodule_simple/.gitmodules --- libgit2-0.20.0/tests/resources/submodule_simple/.gitmodules 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/submodule_simple/.gitmodules 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +[submodule "testrepo"] + path = testrepo + url = ../testrepo.git diff -Nru libgit2-0.20.0/tests/resources/submodule_simple/.gitted/config libgit2-0.22.2/tests/resources/submodule_simple/.gitted/config --- libgit2-0.20.0/tests/resources/submodule_simple/.gitted/config 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/submodule_simple/.gitted/config 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,8 @@ +[core] + repositoryformatversion = 0 + filemode = false + bare = false + logallrefupdates = true + symlinks = false + ignorecase = true + hideDotFiles = dotGitOnly diff -Nru libgit2-0.20.0/tests/resources/submodule_simple/.gitted/description libgit2-0.22.2/tests/resources/submodule_simple/.gitted/description --- libgit2-0.20.0/tests/resources/submodule_simple/.gitted/description 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/submodule_simple/.gitted/description 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff -Nru libgit2-0.20.0/tests/resources/submodule_simple/.gitted/HEAD libgit2-0.22.2/tests/resources/submodule_simple/.gitted/HEAD --- libgit2-0.20.0/tests/resources/submodule_simple/.gitted/HEAD 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/submodule_simple/.gitted/HEAD 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +ref: refs/heads/master Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/submodule_simple/.gitted/index and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/submodule_simple/.gitted/index differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/submodule_simple/.gitted/objects/22/9cea838964f435d4fc2c11561ddb7447003609 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/submodule_simple/.gitted/objects/22/9cea838964f435d4fc2c11561ddb7447003609 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/submodule_simple/.gitted/objects/5b/19f7523fbf55c96153ff5a94875583f1115a36 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/submodule_simple/.gitted/objects/5b/19f7523fbf55c96153ff5a94875583f1115a36 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/submodule_simple/.gitted/objects/a8/575e6aaececba78823993e4f11abbc6172aabd and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/submodule_simple/.gitted/objects/a8/575e6aaececba78823993e4f11abbc6172aabd differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/submodule_simple/.gitted/objects/b4/f28943fad380f4ee3a9c6b95259b28204cc25a and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/submodule_simple/.gitted/objects/b4/f28943fad380f4ee3a9c6b95259b28204cc25a differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/submodule_simple/.gitted/objects/d6/9ff504a3ba631f2fdb35bff93cc8cb8e85f4f8 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/submodule_simple/.gitted/objects/d6/9ff504a3ba631f2fdb35bff93cc8cb8e85f4f8 differ diff -Nru libgit2-0.20.0/tests/resources/submodule_simple/.gitted/packed-refs libgit2-0.22.2/tests/resources/submodule_simple/.gitted/packed-refs --- libgit2-0.20.0/tests/resources/submodule_simple/.gitted/packed-refs 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/submodule_simple/.gitted/packed-refs 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,3 @@ +# pack-refs with: peeled fully-peeled +229cea838964f435d4fc2c11561ddb7447003609 refs/remotes/origin/alternate_1 +a8575e6aaececba78823993e4f11abbc6172aabd refs/remotes/origin/master diff -Nru libgit2-0.20.0/tests/resources/submodule_simple/.gitted/refs/heads/alternate_1 libgit2-0.22.2/tests/resources/submodule_simple/.gitted/refs/heads/alternate_1 --- libgit2-0.20.0/tests/resources/submodule_simple/.gitted/refs/heads/alternate_1 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/submodule_simple/.gitted/refs/heads/alternate_1 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +229cea838964f435d4fc2c11561ddb7447003609 diff -Nru libgit2-0.20.0/tests/resources/submodule_simple/.gitted/refs/heads/master libgit2-0.22.2/tests/resources/submodule_simple/.gitted/refs/heads/master --- libgit2-0.20.0/tests/resources/submodule_simple/.gitted/refs/heads/master 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/submodule_simple/.gitted/refs/heads/master 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +a8575e6aaececba78823993e4f11abbc6172aabd diff -Nru libgit2-0.20.0/tests/resources/template/hooks/link.sample libgit2-0.22.2/tests/resources/template/hooks/link.sample --- libgit2-0.20.0/tests/resources/template/hooks/link.sample 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/resources/template/hooks/link.sample 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -#!/bin/sh -# -# A sample hook to make sure that the `git_repository_init_ext()` function -# can correctly copy a hook over and set it up with the correct permissions. -# -# To enable a hook, you copy the file and remove the ".sample" suffix, but -# in this case, we're just making sure it gets copied correctly. - -echo "$GIT_DIR" Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/testrepo/.gitted/objects/1d/d0968be3ff95fcaecb6fa4245662db9fdc4568 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/testrepo/.gitted/objects/1d/d0968be3ff95fcaecb6fa4245662db9fdc4568 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/testrepo/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/testrepo/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/testrepo/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/testrepo/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/testrepo/.gitted/objects/af/e4393b2b2a965f06acf2ca9658eaa01e0cd6b6 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/testrepo/.gitted/objects/af/e4393b2b2a965f06acf2ca9658eaa01e0cd6b6 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/testrepo/.gitted/objects/ce/054d4c5e3c83522aed8bc061987b46b7ede3be and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/testrepo/.gitted/objects/ce/054d4c5e3c83522aed8bc061987b46b7ede3be differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/testrepo/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/testrepo/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46 differ diff -Nru libgit2-0.20.0/tests/resources/userdiff/after/file.html libgit2-0.22.2/tests/resources/userdiff/after/file.html --- libgit2-0.20.0/tests/resources/userdiff/after/file.html 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/after/file.html 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,41 @@ + + +

+
    +
  1. item 1.1
  2. +
  3. item 1.2 changed
  4. +
  5. item 1.3 changed
  6. +
  7. item 1.4
  8. +
  9. item 1.5
  10. +
  11. item 1.6
  12. +
  13. item 1.7
  14. +
  15. item 1.8
  16. +
  17. item 1.9
  18. +
  19. item 1.10 added
  20. +
+

+

+
    +
  1. item 2.1
  2. +
  3. item 2.2
  4. +
  5. item 2.3
  6. +
  7. item 2.4
  8. +
  9. item 2.5
  10. +
  11. item 2.6
  12. +
  13. item 2.7 changed
  14. +
  15. item 2.7.1 added
  16. +
  17. item 2.8
  18. +
+

+

+
    +
  1. item 3.1
  2. +
  3. item 3.2
  4. +
  5. item 3.3
  6. +
  7. item 3.4
  8. +
  9. item 3.5
  10. +
  11. item 3.6
  12. +
+

+ + diff -Nru libgit2-0.20.0/tests/resources/userdiff/after/file.javascript libgit2-0.22.2/tests/resources/userdiff/after/file.javascript --- libgit2-0.20.0/tests/resources/userdiff/after/file.javascript 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/after/file.javascript 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,108 @@ +define(function(require, exports, module) { + module.exports = Player; + + var Key = require("./key") + , Direction = require("./direction"); + + function Player(game) { + this.game = game; + + this.image = new Image("./assets/fighter.png"); + this.game.resources.add(this.image); + + this.x = 0; + this.y = 0; + + this.pixelX = 10; + this.pixelY = 10; + + this.animationStep = 0; + } + + Player.prototype.update = function() { + if (!this.isWalking()) { + this.handleInput(); + } + + if (this.isWalking()) { + // Increase the animation step. + this.animationStep = ++this.animationStep % 60; + + if (this.x * 32 > this.pixelX) { + this.pixelX++; + } else if (this.x * 32 < this.pixelX) { + this.pixelX--; + } + + if (this.y * 32 > this.pixelY) { + this.pixelY++; + } else if (this.y * 32 < this.pixelY) { + this.pixelY--; + } + } else { + // Reset the animation step. + this.animationStep = 0; + } + }; + + Player.prototype.handleInput = function() { + var keyboard = this.game.keyboard, finalAction, action, inputs = { + 'moveDown': keyboard.isDown(Key.DOWN), + 'moveUp': keyboard.isDown(Key.UP), + 'moveLeft': keyboard.isDown(Key.LEFT), + 'moveRight': keyboard.isDown(Key.RIGHT) + }; + + for (action in inputs) { + if (inputs[action]) { + if (!finalAction || inputs[finalAction] < inputs[action]) { + finalAction = action; + } + } + } + + this[finalAction] && this[finalAction](); + }; + + Player.prototype.isWalking = function() { + return this.x * 32 != this.pixelX || this.y * 32 != this.pixelY; + }; + + Player.prototype.moveDown = function() { + this.y += 1; + this.direction = Direction.DOWN; + }; + + Player.prototype.moveUp = function() { + this.y -= 1; + this.direction = Direction.UP; + }; + + Player.prototype.moveLeft = function() { + this.x -= 5; + this.direction = Direction.LEFT; + }; + + Player.prototype.moveRight = function() { + this.x += 1; + this.direction = Direction.RIGHT; + }; + + Player.prototype.draw = function(context) { + var offsetX = Math.floor(this.animationStep / 15) * 32, offsetY = 0; + + switch(this.direction) { + case Direction.UP: + offsetY = 48 * 3; + break; + case Direction.RIGHT: + offsetY = 48 * 2; + break; + case Direction.LEFT: + offsetY = 48; + break; + } + + context.drawImage(this.image.data, offsetX, offsetY, 32, 48, this.pixelX, this.pixelY, 32, 48); + }; +}); diff -Nru libgit2-0.20.0/tests/resources/userdiff/after/file.php libgit2-0.22.2/tests/resources/userdiff/after/file.php --- libgit2-0.20.0/tests/resources/userdiff/after/file.php 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/after/file.php 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,50 @@ +unique() + */ +class UniqueGenerator +{ + protected $generator; + protected $maxRetries; + protected $moreStuff; + protected $uniques = array(); + + public function __construct(Generator $generator, $maxRetries) + { + $this->generator = $generator; + $this->maxRetries = $maxRetries + 1; + } + + /** + * Catch and proxy all generator calls but return only unique values + */ + public function __get($attribute) + { + return $this->__call($attribute, array()); + } + + /** + * Catch and proxy all generator calls with arguments but return only unique values + */ + public function __call($name, $arguments) + { + $i = 0; + if (!isset($this->uniques[$name])) { + $this->uniques[$name] = array(); + } + do { + $res = call_user_func_array(array($this->generator, $name), $arguments); + $i++; + if ($i >= $this->maxRetries) { + throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a unique value', $this->maxRetries)); + } + } while (in_array($res, $this->uniques[$name])); + $this->uniques[$name][]= $res; + + return $res; + } +} diff -Nru libgit2-0.20.0/tests/resources/userdiff/before/file.html libgit2-0.22.2/tests/resources/userdiff/before/file.html --- libgit2-0.20.0/tests/resources/userdiff/before/file.html 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/before/file.html 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,41 @@ + + +

+
    +
  1. item 1.1
  2. +
  3. item 1.2
  4. +
  5. item 1.3
  6. +
  7. item 1.4
  8. +
  9. item 1.5
  10. +
  11. item 1.6
  12. +
  13. item 1.7
  14. +
  15. item 1.8
  16. +
  17. item 1.9
  18. +
+

+

+
    +
  1. item 2.1
  2. +
  3. item 2.2
  4. +
  5. item 2.3
  6. +
  7. item 2.4
  8. +
  9. item 2.5
  10. +
  11. item 2.6
  12. +
  13. item 2.7
  14. +
  15. item 2.8
  16. +
+

+

+
    +
  1. item 3.1
  2. +
  3. item 3.2
  4. +
  5. item 3.3
  6. +
  7. item 3.4
  8. +
  9. item 3.5
  10. +
  11. item 3.6
  12. +
  13. item 3.7
  14. +
  15. item 3.8
  16. +
+

+ + diff -Nru libgit2-0.20.0/tests/resources/userdiff/before/file.javascript libgit2-0.22.2/tests/resources/userdiff/before/file.javascript --- libgit2-0.20.0/tests/resources/userdiff/before/file.javascript 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/before/file.javascript 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,109 @@ +define(function(require, exports, module) { + module.exports = Player; + + var Key = require("./key") + , Direction = require("./direction") + , Image = require("./image"); + + function Player(game) { + this.game = game; + + this.image = new Image("./assets/fighter.png"); + this.game.resources.add(this.image); + + this.x = 0; + this.y = 0; + + this.pixelX = 0; + this.pixelY = 0; + + this.animationStep = 0; + } + + Player.prototype.update = function() { + if (!this.isWalking()) { + this.handleInput(); + } + + if (this.isWalking()) { + // Increase the animation step. + this.animationStep = ++this.animationStep % 60; + + if (this.x * 32 > this.pixelX) { + this.pixelX++; + } else if (this.x * 32 < this.pixelX) { + this.pixelX--; + } + + if (this.y * 32 > this.pixelY) { + this.pixelY++; + } else if (this.y * 32 < this.pixelY) { + this.pixelY--; + } + } else { + // Reset the animation step. + this.animationStep = 0; + } + }; + + Player.prototype.handleInput = function() { + var keyboard = this.game.keyboard, finalAction, action, inputs = { + 'moveDown': keyboard.isDown(Key.DOWN), + 'moveUp': keyboard.isDown(Key.UP), + 'moveLeft': keyboard.isDown(Key.LEFT), + 'moveRight': keyboard.isDown(Key.RIGHT) + }; + + for (action in inputs) { + if (inputs[action]) { + if (!finalAction || inputs[finalAction] < inputs[action]) { + finalAction = action; + } + } + } + + this[finalAction] && this[finalAction](); + }; + + Player.prototype.isWalking = function() { + return this.x * 32 != this.pixelX || this.y * 32 != this.pixelY; + }; + + Player.prototype.moveDown = function() { + this.y += 1; + this.direction = Direction.DOWN; + }; + + Player.prototype.moveUp = function() { + this.y -= 1; + this.direction = Direction.UP; + }; + + Player.prototype.moveLeft = function() { + this.x -= 1; + this.direction = Direction.LEFT; + }; + + Player.prototype.moveRight = function() { + this.x += 1; + this.direction = Direction.RIGHT; + }; + + Player.prototype.draw = function(context) { + var offsetX = Math.floor(this.animationStep / 15) * 32, offsetY = 0; + + switch(this.direction) { + case Direction.UP: + offsetY = 48 * 3; + break; + case Direction.RIGHT: + offsetY = 48 * 2; + break; + case Direction.LEFT: + offsetY = 48; + break; + } + + context.drawImage(this.image.data, offsetX, offsetY, 32, 48, this.pixelX, this.pixelY - 16, 32, 48); + }; +}); diff -Nru libgit2-0.20.0/tests/resources/userdiff/before/file.php libgit2-0.22.2/tests/resources/userdiff/before/file.php --- libgit2-0.20.0/tests/resources/userdiff/before/file.php 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/before/file.php 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,49 @@ +unique() + */ +class UniqueGenerator +{ + protected $generator; + protected $maxRetries; + protected $uniques = array(); + + public function __construct(Generator $generator, $maxRetries) + { + $this->generator = $generator; + $this->maxRetries = $maxRetries; + } + + /** + * Catch and proxy all generator calls but return only unique values + */ + public function __get($attribute) + { + return $this->__call($attribute, array()); + } + + /** + * Catch and proxy all generator calls with arguments but return only unique values + */ + public function __call($name, $arguments) + { + if (!isset($this->uniques[$name])) { + $this->uniques[$name] = array(); + } + $i = 0; + do { + $res = call_user_func_array(array($this->generator, $name), $arguments); + $i++; + if ($i > $this->maxRetries) { + throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a unique value', $this->maxRetries)); + } + } while (in_array($res, $this->uniques[$name])); + $this->uniques[$name][]= $res; + + return $res; + } +} diff -Nru libgit2-0.20.0/tests/resources/userdiff/expected/driver/diff.html libgit2-0.22.2/tests/resources/userdiff/expected/driver/diff.html --- libgit2-0.20.0/tests/resources/userdiff/expected/driver/diff.html 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/expected/driver/diff.html 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,26 @@ +diff --git a/files/file.html b/files/file.html +index 872d196..2320e2f 100644 +--- a/files/file.html ++++ b/files/file.html +@@ -5,4 +5,4 @@

+
  • item 1.1
  • +-
  • item 1.2
  • +-
  • item 1.3
  • ++
  • item 1.2 changed
  • ++
  • item 1.3 changed
  • +
  • item 1.4
  • +@@ -13,2 +13,3 @@

    +
  • item 1.9
  • ++
  • item 1.10 added
  • + +@@ -23,3 +24,4 @@

    +
  • item 2.6
  • +-
  • item 2.7
  • ++
  • item 2.7 changed
  • ++
  • item 2.7.1 added
  • +
  • item 2.8
  • +@@ -35,4 +37,2 @@

    +
  • item 3.6
  • +-
  • item 3.7
  • +-
  • item 3.8
  • + diff -Nru libgit2-0.20.0/tests/resources/userdiff/expected/driver/diff.javascript libgit2-0.22.2/tests/resources/userdiff/expected/driver/diff.javascript --- libgit2-0.20.0/tests/resources/userdiff/expected/driver/diff.javascript 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/expected/driver/diff.javascript 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,27 @@ +diff --git a/files/file.javascript b/files/file.javascript +index 0965b37..5391797 100644 +--- a/files/file.javascript ++++ b/files/file.javascript +@@ -4,4 +4,3 @@ function(require, exports, module) + var Key = require("./key") +- , Direction = require("./direction") +- , Image = require("./image"); ++ , Direction = require("./direction"); + +@@ -16,4 +15,4 @@ function Player(game) + +- this.pixelX = 0; +- this.pixelY = 0; ++ this.pixelX = 10; ++ this.pixelY = 10; + +@@ -82,3 +81,3 @@ Player.prototype.moveUp = function() + Player.prototype.moveLeft = function() { +- this.x -= 1; ++ this.x -= 5; + this.direction = Direction.LEFT; +@@ -106,3 +105,3 @@ Player.prototype.draw = function(context) + +- context.drawImage(this.image.data, offsetX, offsetY, 32, 48, this.pixelX, this.pixelY - 16, 32, 48); ++ context.drawImage(this.image.data, offsetX, offsetY, 32, 48, this.pixelX, this.pixelY, 32, 48); + }; diff -Nru libgit2-0.20.0/tests/resources/userdiff/expected/driver/diff.php libgit2-0.22.2/tests/resources/userdiff/expected/driver/diff.php --- libgit2-0.20.0/tests/resources/userdiff/expected/driver/diff.php 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/expected/driver/diff.php 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,26 @@ +diff --git a/files/file.php b/files/file.php +index 63250ad..967d646 100644 +--- a/files/file.php ++++ b/files/file.php +@@ -12,2 +12,3 @@ class UniqueGenerator + protected $maxRetries; ++ protected $moreStuff; + protected $uniques = array(); +@@ -17,3 +18,3 @@ public function __construct(Generator $generator, $maxRetries) + $this->generator = $generator; +- $this->maxRetries = $maxRetries; ++ $this->maxRetries = $maxRetries + 1; + } +@@ -33,10 +34,10 @@ public function __call($name, $arguments) + { ++ $i = 0; + if (!isset($this->uniques[$name])) { + $this->uniques[$name] = array(); + } +- $i = 0; + do { + $res = call_user_func_array(array($this->generator, $name), $arguments); + $i++; +- if ($i > $this->maxRetries) { ++ if ($i >= $this->maxRetries) { + throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a unique value', $this->maxRetries)); diff -Nru libgit2-0.20.0/tests/resources/userdiff/expected/nodriver/diff.html libgit2-0.22.2/tests/resources/userdiff/expected/nodriver/diff.html --- libgit2-0.20.0/tests/resources/userdiff/expected/nodriver/diff.html 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/expected/nodriver/diff.html 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,26 @@ +diff --git a/files/file.html b/files/file.html +index 872d196..2320e2f 100644 +--- a/files/file.html ++++ b/files/file.html +@@ -5,4 +5,4 @@ +
  • item 1.1
  • +-
  • item 1.2
  • +-
  • item 1.3
  • ++
  • item 1.2 changed
  • ++
  • item 1.3 changed
  • +
  • item 1.4
  • +@@ -13,2 +13,3 @@ +
  • item 1.9
  • ++
  • item 1.10 added
  • + +@@ -23,3 +24,4 @@ +
  • item 2.6
  • +-
  • item 2.7
  • ++
  • item 2.7 changed
  • ++
  • item 2.7.1 added
  • +
  • item 2.8
  • +@@ -35,4 +37,2 @@ +
  • item 3.6
  • +-
  • item 3.7
  • +-
  • item 3.8
  • + diff -Nru libgit2-0.20.0/tests/resources/userdiff/expected/nodriver/diff.javascript libgit2-0.22.2/tests/resources/userdiff/expected/nodriver/diff.javascript --- libgit2-0.20.0/tests/resources/userdiff/expected/nodriver/diff.javascript 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/expected/nodriver/diff.javascript 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,27 @@ +diff --git a/files/file.javascript b/files/file.javascript +index 0965b37..5391797 100644 +--- a/files/file.javascript ++++ b/files/file.javascript +@@ -4,4 +4,3 @@ define(function(require, exports, module) { + var Key = require("./key") +- , Direction = require("./direction") +- , Image = require("./image"); ++ , Direction = require("./direction"); + +@@ -16,4 +15,4 @@ define(function(require, exports, module) { + +- this.pixelX = 0; +- this.pixelY = 0; ++ this.pixelX = 10; ++ this.pixelY = 10; + +@@ -82,3 +81,3 @@ define(function(require, exports, module) { + Player.prototype.moveLeft = function() { +- this.x -= 1; ++ this.x -= 5; + this.direction = Direction.LEFT; +@@ -106,3 +105,3 @@ define(function(require, exports, module) { + +- context.drawImage(this.image.data, offsetX, offsetY, 32, 48, this.pixelX, this.pixelY - 16, 32, 48); ++ context.drawImage(this.image.data, offsetX, offsetY, 32, 48, this.pixelX, this.pixelY, 32, 48); + }; diff -Nru libgit2-0.20.0/tests/resources/userdiff/expected/nodriver/diff.php libgit2-0.22.2/tests/resources/userdiff/expected/nodriver/diff.php --- libgit2-0.20.0/tests/resources/userdiff/expected/nodriver/diff.php 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/expected/nodriver/diff.php 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,26 @@ +diff --git a/files/file.php b/files/file.php +index 63250ad..967d646 100644 +--- a/files/file.php ++++ b/files/file.php +@@ -12,2 +12,3 @@ class UniqueGenerator + protected $maxRetries; ++ protected $moreStuff; + protected $uniques = array(); +@@ -17,3 +18,3 @@ class UniqueGenerator + $this->generator = $generator; +- $this->maxRetries = $maxRetries; ++ $this->maxRetries = $maxRetries + 1; + } +@@ -33,10 +34,10 @@ class UniqueGenerator + { ++ $i = 0; + if (!isset($this->uniques[$name])) { + $this->uniques[$name] = array(); + } +- $i = 0; + do { + $res = call_user_func_array(array($this->generator, $name), $arguments); + $i++; +- if ($i > $this->maxRetries) { ++ if ($i >= $this->maxRetries) { + throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a unique value', $this->maxRetries)); diff -Nru libgit2-0.20.0/tests/resources/userdiff/files/file.html libgit2-0.22.2/tests/resources/userdiff/files/file.html --- libgit2-0.20.0/tests/resources/userdiff/files/file.html 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/files/file.html 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,41 @@ + + +

    +
      +
    1. item 1.1
    2. +
    3. item 1.2 changed
    4. +
    5. item 1.3 changed
    6. +
    7. item 1.4
    8. +
    9. item 1.5
    10. +
    11. item 1.6
    12. +
    13. item 1.7
    14. +
    15. item 1.8
    16. +
    17. item 1.9
    18. +
    19. item 1.10 added
    20. +
    +

    +

    +
      +
    1. item 2.1
    2. +
    3. item 2.2
    4. +
    5. item 2.3
    6. +
    7. item 2.4
    8. +
    9. item 2.5
    10. +
    11. item 2.6
    12. +
    13. item 2.7 changed
    14. +
    15. item 2.7.1 added
    16. +
    17. item 2.8
    18. +
    +

    +

    +
      +
    1. item 3.1
    2. +
    3. item 3.2
    4. +
    5. item 3.3
    6. +
    7. item 3.4
    8. +
    9. item 3.5
    10. +
    11. item 3.6
    12. +
    +

    + + diff -Nru libgit2-0.20.0/tests/resources/userdiff/files/file.javascript libgit2-0.22.2/tests/resources/userdiff/files/file.javascript --- libgit2-0.20.0/tests/resources/userdiff/files/file.javascript 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/files/file.javascript 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,108 @@ +define(function(require, exports, module) { + module.exports = Player; + + var Key = require("./key") + , Direction = require("./direction"); + + function Player(game) { + this.game = game; + + this.image = new Image("./assets/fighter.png"); + this.game.resources.add(this.image); + + this.x = 0; + this.y = 0; + + this.pixelX = 10; + this.pixelY = 10; + + this.animationStep = 0; + } + + Player.prototype.update = function() { + if (!this.isWalking()) { + this.handleInput(); + } + + if (this.isWalking()) { + // Increase the animation step. + this.animationStep = ++this.animationStep % 60; + + if (this.x * 32 > this.pixelX) { + this.pixelX++; + } else if (this.x * 32 < this.pixelX) { + this.pixelX--; + } + + if (this.y * 32 > this.pixelY) { + this.pixelY++; + } else if (this.y * 32 < this.pixelY) { + this.pixelY--; + } + } else { + // Reset the animation step. + this.animationStep = 0; + } + }; + + Player.prototype.handleInput = function() { + var keyboard = this.game.keyboard, finalAction, action, inputs = { + 'moveDown': keyboard.isDown(Key.DOWN), + 'moveUp': keyboard.isDown(Key.UP), + 'moveLeft': keyboard.isDown(Key.LEFT), + 'moveRight': keyboard.isDown(Key.RIGHT) + }; + + for (action in inputs) { + if (inputs[action]) { + if (!finalAction || inputs[finalAction] < inputs[action]) { + finalAction = action; + } + } + } + + this[finalAction] && this[finalAction](); + }; + + Player.prototype.isWalking = function() { + return this.x * 32 != this.pixelX || this.y * 32 != this.pixelY; + }; + + Player.prototype.moveDown = function() { + this.y += 1; + this.direction = Direction.DOWN; + }; + + Player.prototype.moveUp = function() { + this.y -= 1; + this.direction = Direction.UP; + }; + + Player.prototype.moveLeft = function() { + this.x -= 5; + this.direction = Direction.LEFT; + }; + + Player.prototype.moveRight = function() { + this.x += 1; + this.direction = Direction.RIGHT; + }; + + Player.prototype.draw = function(context) { + var offsetX = Math.floor(this.animationStep / 15) * 32, offsetY = 0; + + switch(this.direction) { + case Direction.UP: + offsetY = 48 * 3; + break; + case Direction.RIGHT: + offsetY = 48 * 2; + break; + case Direction.LEFT: + offsetY = 48; + break; + } + + context.drawImage(this.image.data, offsetX, offsetY, 32, 48, this.pixelX, this.pixelY, 32, 48); + }; +}); diff -Nru libgit2-0.20.0/tests/resources/userdiff/files/file.php libgit2-0.22.2/tests/resources/userdiff/files/file.php --- libgit2-0.20.0/tests/resources/userdiff/files/file.php 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/files/file.php 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,50 @@ +unique() + */ +class UniqueGenerator +{ + protected $generator; + protected $maxRetries; + protected $moreStuff; + protected $uniques = array(); + + public function __construct(Generator $generator, $maxRetries) + { + $this->generator = $generator; + $this->maxRetries = $maxRetries + 1; + } + + /** + * Catch and proxy all generator calls but return only unique values + */ + public function __get($attribute) + { + return $this->__call($attribute, array()); + } + + /** + * Catch and proxy all generator calls with arguments but return only unique values + */ + public function __call($name, $arguments) + { + $i = 0; + if (!isset($this->uniques[$name])) { + $this->uniques[$name] = array(); + } + do { + $res = call_user_func_array(array($this->generator, $name), $arguments); + $i++; + if ($i >= $this->maxRetries) { + throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a unique value', $this->maxRetries)); + } + } while (in_array($res, $this->uniques[$name])); + $this->uniques[$name][]= $res; + + return $res; + } +} diff -Nru libgit2-0.20.0/tests/resources/userdiff/.gitted/config libgit2-0.22.2/tests/resources/userdiff/.gitted/config --- libgit2-0.20.0/tests/resources/userdiff/.gitted/config 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/.gitted/config 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,7 @@ +[core] + repositoryformatversion = 0 + filemode = true + bare = false + logallrefupdates = true + ignorecase = true + precomposeunicode = true diff -Nru libgit2-0.20.0/tests/resources/userdiff/.gitted/description libgit2-0.22.2/tests/resources/userdiff/.gitted/description --- libgit2-0.20.0/tests/resources/userdiff/.gitted/description 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/.gitted/description 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +Unnamed repository; edit this file 'description' to name the repository. diff -Nru libgit2-0.20.0/tests/resources/userdiff/.gitted/HEAD libgit2-0.22.2/tests/resources/userdiff/.gitted/HEAD --- libgit2-0.20.0/tests/resources/userdiff/.gitted/HEAD 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/.gitted/HEAD 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +ref: refs/heads/master Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/userdiff/.gitted/index and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/userdiff/.gitted/index differ diff -Nru libgit2-0.20.0/tests/resources/userdiff/.gitted/info/refs libgit2-0.22.2/tests/resources/userdiff/.gitted/info/refs --- libgit2-0.20.0/tests/resources/userdiff/.gitted/info/refs 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/.gitted/info/refs 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1 @@ +60e3f7b244a5305e2c9fa4ef0e897f3b14f3b8dd refs/heads/master Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/userdiff/.gitted/objects/09/65b377c214bbe5e0d18fcdaf556df7fa7ed7c8 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/userdiff/.gitted/objects/09/65b377c214bbe5e0d18fcdaf556df7fa7ed7c8 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/userdiff/.gitted/objects/0c/20ef1409ae1df4d5a76cdbd98d5c33ccdb6bcc and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/userdiff/.gitted/objects/0c/20ef1409ae1df4d5a76cdbd98d5c33ccdb6bcc differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/userdiff/.gitted/objects/39/ea75107a09091ba54ff86fcc780b59477e42cd and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/userdiff/.gitted/objects/39/ea75107a09091ba54ff86fcc780b59477e42cd differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/userdiff/.gitted/objects/3c/c08384deae5957247bc36776ab626cc9e0582b and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/userdiff/.gitted/objects/3c/c08384deae5957247bc36776ab626cc9e0582b differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/userdiff/.gitted/objects/46/8d6f2afc940e14c76347fa9af26e429a3c9044 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/userdiff/.gitted/objects/46/8d6f2afc940e14c76347fa9af26e429a3c9044 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/userdiff/.gitted/objects/53/917973acfe0111f93c2cfaacf854be245880e8 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/userdiff/.gitted/objects/53/917973acfe0111f93c2cfaacf854be245880e8 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/userdiff/.gitted/objects/63/1d44e0c72e8cd1b594fa11d7d1ee8a6d67ff67 and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/userdiff/.gitted/objects/63/1d44e0c72e8cd1b594fa11d7d1ee8a6d67ff67 differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/userdiff/.gitted/objects/f3/be389d351e4bcc6dcc4b5fe22134ef0f63f8bd and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/userdiff/.gitted/objects/f3/be389d351e4bcc6dcc4b5fe22134ef0f63f8bd differ diff -Nru libgit2-0.20.0/tests/resources/userdiff/.gitted/objects/info/packs libgit2-0.22.2/tests/resources/userdiff/.gitted/objects/info/packs --- libgit2-0.20.0/tests/resources/userdiff/.gitted/objects/info/packs 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/.gitted/objects/info/packs 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +P pack-1652578900ac63564f2a24b9714529821276ceb9.pack + Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.idx and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.idx differ Binary files /tmp/OEzVFBGayt/libgit2-0.20.0/tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.pack and /tmp/sVbi9fehyc/libgit2-0.22.2/tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.pack differ diff -Nru libgit2-0.20.0/tests/resources/userdiff/.gitted/packed-refs libgit2-0.22.2/tests/resources/userdiff/.gitted/packed-refs --- libgit2-0.20.0/tests/resources/userdiff/.gitted/packed-refs 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/resources/userdiff/.gitted/packed-refs 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,2 @@ +# pack-refs with: peeled fully-peeled +60e3f7b244a5305e2c9fa4ef0e897f3b14f3b8dd refs/heads/master diff -Nru libgit2-0.20.0/tests/revert/bare.c libgit2-0.22.2/tests/revert/bare.c --- libgit2-0.20.0/tests/revert/bare.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/revert/bare.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,107 @@ +#include "clar.h" +#include "clar_libgit2.h" + +#include "buffer.h" +#include "fileops.h" +#include "git2/revert.h" + +#include "../merge/merge_helpers.h" + +#define TEST_REPO_PATH "revert" + +static git_repository *repo; + +// Fixture setup and teardown +void test_revert_bare__initialize(void) +{ + repo = cl_git_sandbox_init(TEST_REPO_PATH); +} + +void test_revert_bare__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_revert_bare__automerge(void) +{ + git_commit *head_commit, *revert_commit; + git_oid head_oid, revert_oid; + git_index *index; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "caf99de3a49827117bb66721010eac461b06a80c", 0, "file1.txt" }, + { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" }, + { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" }, + { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" }, + }; + + git_oid_fromstr(&head_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45"); + cl_git_pass(git_commit_lookup(&head_commit, repo, &head_oid)); + + git_oid_fromstr(&revert_oid, "d1d403d22cbe24592d725f442835cf46fe60c8ac"); + cl_git_pass(git_commit_lookup(&revert_commit, repo, &revert_oid)); + + cl_git_pass(git_revert_commit(&index, repo, revert_commit, head_commit, 0, NULL)); + cl_assert(merge_test_index(index, merge_index_entries, 4)); + + git_commit_free(revert_commit); + git_commit_free(head_commit); + git_index_free(index); +} + +void test_revert_bare__conflicts(void) +{ + git_reference *head_ref; + git_commit *head_commit, *revert_commit; + git_oid revert_oid; + git_index *index; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "7731926a337c4eaba1e2187d90ebfa0a93659382", 1, "file1.txt" }, + { 0100644, "4b8fcff56437e60f58e9a6bc630dd242ebf6ea2c", 2, "file1.txt" }, + { 0100644, "3a3ef367eaf3fe79effbfb0a56b269c04c2b59fe", 3, "file1.txt" }, + { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" }, + { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" }, + { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" }, + }; + + git_oid_fromstr(&revert_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45"); + + cl_git_pass(git_repository_head(&head_ref, repo)); + cl_git_pass(git_reference_peel((git_object **)&head_commit, head_ref, GIT_OBJ_COMMIT)); + + cl_git_pass(git_commit_lookup(&revert_commit, repo, &revert_oid)); + cl_git_pass(git_revert_commit(&index, repo, revert_commit, head_commit, 0, NULL)); + + cl_assert(git_index_has_conflicts(index)); + cl_assert(merge_test_index(index, merge_index_entries, 6)); + + git_commit_free(revert_commit); + git_commit_free(head_commit); + git_reference_free(head_ref); + git_index_free(index); +} + +void test_revert_bare__orphan(void) +{ + git_commit *head_commit, *revert_commit; + git_oid head_oid, revert_oid; + git_index *index; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "296a6d3be1dff05c5d1f631d2459389fa7b619eb", 0, "file-mainline.txt" }, + }; + + git_oid_fromstr(&head_oid, "39467716290f6df775a91cdb9a4eb39295018145"); + cl_git_pass(git_commit_lookup(&head_commit, repo, &head_oid)); + + git_oid_fromstr(&revert_oid, "ebb03002cee5d66c7732dd06241119fe72ab96a5"); + cl_git_pass(git_commit_lookup(&revert_commit, repo, &revert_oid)); + + cl_git_pass(git_revert_commit(&index, repo, revert_commit, head_commit, 0, NULL)); + cl_assert(merge_test_index(index, merge_index_entries, 1)); + + git_commit_free(revert_commit); + git_commit_free(head_commit); + git_index_free(index); +} diff -Nru libgit2-0.20.0/tests/revert/workdir.c libgit2-0.22.2/tests/revert/workdir.c --- libgit2-0.20.0/tests/revert/workdir.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/revert/workdir.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,575 @@ +#include "clar.h" +#include "clar_libgit2.h" + +#include "buffer.h" +#include "fileops.h" +#include "git2/revert.h" + +#include "../merge/merge_helpers.h" + +#define TEST_REPO_PATH "revert" + +static git_repository *repo; +static git_index *repo_index; + +// Fixture setup and teardown +void test_revert_workdir__initialize(void) +{ + git_config *cfg; + + repo = cl_git_sandbox_init(TEST_REPO_PATH); + git_repository_index(&repo_index, repo); + + /* Ensure that the user's merge.conflictstyle doesn't interfere */ + cl_git_pass(git_repository_config(&cfg, repo)); + cl_git_pass(git_config_set_string(cfg, "merge.conflictstyle", "merge")); + git_config_free(cfg); +} + +void test_revert_workdir__cleanup(void) +{ + git_index_free(repo_index); + cl_git_sandbox_cleanup(); +} + +/* git reset --hard 72333f47d4e83616630ff3b0ffe4c0faebcc3c45 + * git revert --no-commit d1d403d22cbe24592d725f442835cf46fe60c8ac */ +void test_revert_workdir__automerge(void) +{ + git_commit *head, *commit; + git_oid head_oid, revert_oid; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "caf99de3a49827117bb66721010eac461b06a80c", 0, "file1.txt" }, + { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" }, + { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" }, + { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" }, + }; + + git_oid_fromstr(&head_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + git_oid_fromstr(&revert_oid, "d1d403d22cbe24592d725f442835cf46fe60c8ac"); + cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid)); + cl_git_pass(git_revert(repo, commit, NULL)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 4)); + + git_commit_free(commit); + git_commit_free(head); +} + +/* git revert --no-commit 72333f47d4e83616630ff3b0ffe4c0faebcc3c45 */ +void test_revert_workdir__conflicts(void) +{ + git_reference *head_ref; + git_commit *head, *commit; + git_oid revert_oid; + git_buf conflicting_buf = GIT_BUF_INIT, mergemsg_buf = GIT_BUF_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "7731926a337c4eaba1e2187d90ebfa0a93659382", 1, "file1.txt" }, + { 0100644, "4b8fcff56437e60f58e9a6bc630dd242ebf6ea2c", 2, "file1.txt" }, + { 0100644, "3a3ef367eaf3fe79effbfb0a56b269c04c2b59fe", 3, "file1.txt" }, + { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" }, + { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" }, + { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" }, + }; + + git_oid_fromstr(&revert_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45"); + + cl_git_pass(git_repository_head(&head_ref, repo)); + cl_git_pass(git_reference_peel((git_object **)&head, head_ref, GIT_OBJ_COMMIT)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid)); + cl_git_pass(git_revert(repo, commit, NULL)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 6)); + + cl_git_pass(git_futils_readbuffer(&conflicting_buf, + TEST_REPO_PATH "/file1.txt")); + cl_assert(strcmp(conflicting_buf.ptr, "!File one!\n" \ + "!File one!\n" \ + "File one!\n" \ + "File one\n" \ + "File one\n" \ + "File one\n" \ + "File one\n" \ + "File one\n" \ + "File one\n" \ + "File one\n" \ + "<<<<<<< HEAD\n" \ + "File one!\n" \ + "!File one!\n" \ + "!File one!\n" \ + "!File one!\n" \ + "=======\n" \ + "File one\n" \ + "File one\n" \ + "File one\n" \ + "File one\n" \ + ">>>>>>> parent of 72333f4... automergeable changes\n") == 0); + + cl_assert(git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG")); + cl_git_pass(git_futils_readbuffer(&mergemsg_buf, + TEST_REPO_PATH "/.git/MERGE_MSG")); + cl_assert(strcmp(mergemsg_buf.ptr, + "Revert \"automergeable changes\"\n" \ + "\n" \ + "This reverts commit 72333f47d4e83616630ff3b0ffe4c0faebcc3c45.\n" + "\n" \ + "Conflicts:\n" \ + "\tfile1.txt\n") == 0); + + git_commit_free(commit); + git_commit_free(head); + git_reference_free(head_ref); + git_buf_free(&mergemsg_buf); + git_buf_free(&conflicting_buf); +} + +/* git reset --hard 39467716290f6df775a91cdb9a4eb39295018145 + * git revert --no-commit ebb03002cee5d66c7732dd06241119fe72ab96a5 +*/ +void test_revert_workdir__orphan(void) +{ + git_commit *head, *commit; + git_oid head_oid, revert_oid; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "296a6d3be1dff05c5d1f631d2459389fa7b619eb", 0, "file-mainline.txt" }, + }; + + git_oid_fromstr(&head_oid, "39467716290f6df775a91cdb9a4eb39295018145"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + git_oid_fromstr(&revert_oid, "ebb03002cee5d66c7732dd06241119fe72ab96a5"); + cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid)); + cl_git_pass(git_revert(repo, commit, NULL)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 1)); + + git_commit_free(commit); + git_commit_free(head); +} + +/* + * revert the same commit twice (when the first reverts cleanly): + * + * git revert 2d440f2 + * git revert 2d440f2 + */ +void test_revert_workdir__again(void) +{ + git_reference *head_ref; + git_commit *orig_head; + git_tree *reverted_tree; + git_oid reverted_tree_oid, reverted_commit_oid; + git_signature *signature; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "7731926a337c4eaba1e2187d90ebfa0a93659382", 0, "file1.txt" }, + { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" }, + { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" }, + { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" }, + }; + + cl_git_pass(git_repository_head(&head_ref, repo)); + cl_git_pass(git_reference_peel((git_object **)&orig_head, head_ref, GIT_OBJ_COMMIT)); + cl_git_pass(git_reset(repo, (git_object *)orig_head, GIT_RESET_HARD, NULL, NULL, NULL)); + + cl_git_pass(git_revert(repo, orig_head, NULL)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 4)); + + cl_git_pass(git_index_write_tree(&reverted_tree_oid, repo_index)); + cl_git_pass(git_tree_lookup(&reverted_tree, repo, &reverted_tree_oid)); + + cl_git_pass(git_signature_new(&signature, "Reverter", "reverter@example.org", time(NULL), 0)); + cl_git_pass(git_commit_create(&reverted_commit_oid, repo, "HEAD", signature, signature, NULL, "Reverted!", reverted_tree, 1, (const git_commit **)&orig_head)); + + cl_git_pass(git_revert(repo, orig_head, NULL)); + cl_assert(merge_test_index(repo_index, merge_index_entries, 4)); + + git_signature_free(signature); + git_tree_free(reverted_tree); + git_commit_free(orig_head); + git_reference_free(head_ref); +} + +/* git reset --hard 72333f47d4e83616630ff3b0ffe4c0faebcc3c45 + * git revert --no-commit d1d403d22cbe24592d725f442835cf46fe60c8ac */ +void test_revert_workdir__again_after_automerge(void) +{ + git_commit *head, *commit; + git_tree *reverted_tree; + git_oid head_oid, revert_oid, reverted_tree_oid, reverted_commit_oid; + git_signature *signature; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "caf99de3a49827117bb66721010eac461b06a80c", 0, "file1.txt" }, + { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" }, + { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" }, + { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" }, + }; + + struct merge_index_entry second_revert_entries[] = { + { 0100644, "3a3ef367eaf3fe79effbfb0a56b269c04c2b59fe", 1, "file1.txt" }, + { 0100644, "caf99de3a49827117bb66721010eac461b06a80c", 2, "file1.txt" }, + { 0100644, "747726e021bc5f44b86de60e3032fd6f9f1b8383", 3, "file1.txt" }, + { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" }, + { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" }, + { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" }, + }; + + git_oid_fromstr(&head_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + git_oid_fromstr(&revert_oid, "d1d403d22cbe24592d725f442835cf46fe60c8ac"); + cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid)); + cl_git_pass(git_revert(repo, commit, NULL)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 4)); + + cl_git_pass(git_index_write_tree(&reverted_tree_oid, repo_index)); + cl_git_pass(git_tree_lookup(&reverted_tree, repo, &reverted_tree_oid)); + + cl_git_pass(git_signature_new(&signature, "Reverter", "reverter@example.org", time(NULL), 0)); + cl_git_pass(git_commit_create(&reverted_commit_oid, repo, "HEAD", signature, signature, NULL, "Reverted!", reverted_tree, 1, (const git_commit **)&head)); + + cl_git_pass(git_revert(repo, commit, NULL)); + cl_assert(merge_test_index(repo_index, second_revert_entries, 6)); + + git_signature_free(signature); + git_tree_free(reverted_tree); + git_commit_free(commit); + git_commit_free(head); +} + +/* + * revert the same commit twice (when the first reverts cleanly): + * + * git revert 2d440f2 + * git revert 2d440f2 + */ +void test_revert_workdir__again_after_edit(void) +{ + git_reference *head_ref; + git_commit *orig_head, *commit; + git_tree *reverted_tree; + git_oid orig_head_oid, revert_oid, reverted_tree_oid, reverted_commit_oid; + git_signature *signature; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "3721552e06c4bdc7d478e0674e6304888545d5fd", 0, "file1.txt" }, + { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" }, + { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" }, + { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" }, + }; + + cl_git_pass(git_repository_head(&head_ref, repo)); + + cl_git_pass(git_oid_fromstr(&orig_head_oid, "399fb3aba3d9d13f7d40a9254ce4402067ef3149")); + cl_git_pass(git_commit_lookup(&orig_head, repo, &orig_head_oid)); + cl_git_pass(git_reset(repo, (git_object *)orig_head, GIT_RESET_HARD, NULL, NULL, NULL)); + + cl_git_pass(git_oid_fromstr(&revert_oid, "2d440f2b3147d3dc7ad1085813478d6d869d5a4d")); + cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid)); + + cl_git_pass(git_revert(repo, commit, NULL)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 4)); + + cl_git_pass(git_index_write_tree(&reverted_tree_oid, repo_index)); + cl_git_pass(git_tree_lookup(&reverted_tree, repo, &reverted_tree_oid)); + + cl_git_pass(git_signature_new(&signature, "Reverter", "reverter@example.org", time(NULL), 0)); + cl_git_pass(git_commit_create(&reverted_commit_oid, repo, "HEAD", signature, signature, NULL, "Reverted!", reverted_tree, 1, (const git_commit **)&orig_head)); + + cl_git_pass(git_revert(repo, commit, NULL)); + cl_assert(merge_test_index(repo_index, merge_index_entries, 4)); + + git_signature_free(signature); + git_tree_free(reverted_tree); + git_commit_free(commit); + git_commit_free(orig_head); + git_reference_free(head_ref); +} + +/* + * revert the same commit twice (when the first reverts cleanly): + * + * git reset --hard 75ec9929465623f17ff3ad68c0438ea56faba815 + * git revert 97e52d5e81f541080cd6b92829fb85bc4d81d90b + */ +void test_revert_workdir__again_after_edit_two(void) +{ + git_buf diff_buf = GIT_BUF_INIT; + git_config *config; + git_oid head_commit_oid, revert_commit_oid; + git_commit *head_commit, *revert_commit; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "a8c86221b400b836010567cc3593db6e96c1a83a", 1, "file.txt" }, + { 0100644, "46ff0854663aeb2182b9838c8da68e33ac23bc1e", 2, "file.txt" }, + { 0100644, "21a96a98ed84d45866e1de6e266fd3a61a4ae9dc", 3, "file.txt" }, + }; + + cl_git_pass(git_repository_config(&config, repo)); + cl_git_pass(git_config_set_bool(config, "core.autocrlf", 0)); + + cl_git_pass(git_oid_fromstr(&head_commit_oid, "75ec9929465623f17ff3ad68c0438ea56faba815")); + cl_git_pass(git_commit_lookup(&head_commit, repo, &head_commit_oid)); + cl_git_pass(git_reset(repo, (git_object *)head_commit, GIT_RESET_HARD, NULL, NULL, NULL)); + + cl_git_pass(git_oid_fromstr(&revert_commit_oid, "97e52d5e81f541080cd6b92829fb85bc4d81d90b")); + cl_git_pass(git_commit_lookup(&revert_commit, repo, &revert_commit_oid)); + + cl_git_pass(git_revert(repo, revert_commit, NULL)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 3)); + + cl_git_pass(git_futils_readbuffer(&diff_buf, "revert/file.txt")); + cl_assert(strcmp(diff_buf.ptr, "a\n" \ + "<<<<<<< HEAD\n" \ + "=======\n" \ + "a\n" \ + ">>>>>>> parent of 97e52d5... Revert me\n" \ + "a\n" \ + "a\n" \ + "a\n" \ + "a\n" \ + "ab\n") == 0); + + git_commit_free(revert_commit); + git_commit_free(head_commit); + git_config_free(config); + git_buf_free(&diff_buf); +} + +/* git reset --hard 72333f47d4e83616630ff3b0ffe4c0faebcc3c45 + * git revert --no-commit d1d403d22cbe24592d725f442835cf46fe60c8ac */ +void test_revert_workdir__conflict_use_ours(void) +{ + git_commit *head, *commit; + git_oid head_oid, revert_oid; + git_revert_options opts = GIT_REVERT_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "caf99de3a49827117bb66721010eac461b06a80c", 0, "file1.txt" }, + { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" }, + { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" }, + { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" }, + }; + + struct merge_index_entry merge_filesystem_entries[] = { + { 0100644, "caf99de3a49827117bb66721010eac461b06a80c", 0, "file1.txt" }, + { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" }, + { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" }, + { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" }, + }; + + opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS; + + git_oid_fromstr(&head_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + git_oid_fromstr(&revert_oid, "d1d403d22cbe24592d725f442835cf46fe60c8ac"); + cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid)); + cl_git_pass(git_revert(repo, commit, &opts)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 4)); + cl_assert(merge_test_workdir(repo, merge_filesystem_entries, 4)); + + git_commit_free(commit); + git_commit_free(head); +} + +/* git reset --hard cef56612d71a6af8d8015691e4865f7fece905b5 + * git revert --no-commit 55568c8de5322ff9a95d72747a239cdb64a19965 + */ +void test_revert_workdir__rename_1_of_2(void) +{ + git_commit *head, *commit; + git_oid head_oid, revert_oid; + git_revert_options opts = GIT_REVERT_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "747726e021bc5f44b86de60e3032fd6f9f1b8383", 0, "file1.txt" }, + { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" }, + { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" }, + { 0100644, "55acf326a69f0aab7a974ec53ffa55a50bcac14e", 3, "file4.txt" }, + { 0100644, "55acf326a69f0aab7a974ec53ffa55a50bcac14e", 1, "file5.txt" }, + { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 2, "file6.txt" }, + }; + + opts.merge_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES; + opts.merge_opts.rename_threshold = 50; + + git_oid_fromstr(&head_oid, "cef56612d71a6af8d8015691e4865f7fece905b5"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + git_oid_fromstr(&revert_oid, "55568c8de5322ff9a95d72747a239cdb64a19965"); + cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid)); + cl_git_pass(git_revert(repo, commit, &opts)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 6)); + + git_commit_free(commit); + git_commit_free(head); +} + +/* git reset --hard 55568c8de5322ff9a95d72747a239cdb64a19965 + * git revert --no-commit HEAD~1 */ +void test_revert_workdir__rename(void) +{ + git_commit *head, *commit; + git_oid head_oid, revert_oid; + git_revert_options opts = GIT_REVERT_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "55acf326a69f0aab7a974ec53ffa55a50bcac14e", 1, "file4.txt" }, + { 0100644, "55acf326a69f0aab7a974ec53ffa55a50bcac14e", 2, "file5.txt" }, + }; + + struct merge_name_entry merge_name_entries[] = { + { "file4.txt", "file5.txt", "" }, + }; + + opts.merge_opts.flags |= GIT_MERGE_TREE_FIND_RENAMES; + opts.merge_opts.rename_threshold = 50; + + git_oid_fromstr(&head_oid, "55568c8de5322ff9a95d72747a239cdb64a19965"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + git_oid_fromstr(&revert_oid, "0aa8c7e40d342fff78d60b29a4ba8e993ed79c51"); + cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid)); + cl_git_pass(git_revert(repo, commit, &opts)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 2)); + cl_assert(merge_test_names(repo_index, merge_name_entries, 1)); + + git_commit_free(commit); + git_commit_free(head); +} + +/* git revert --no-commit HEAD */ +void test_revert_workdir__head(void) +{ + git_reference *head; + git_commit *commit; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "7731926a337c4eaba1e2187d90ebfa0a93659382", 0, "file1.txt" }, + { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" }, + { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" }, + { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" }, + }; + + /* HEAD is 2d440f2b3147d3dc7ad1085813478d6d869d5a4d */ + cl_git_pass(git_repository_head(&head, repo)); + cl_git_pass(git_reference_peel((git_object **)&commit, head, GIT_OBJ_COMMIT)); + cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD, NULL, NULL, NULL)); + cl_git_pass(git_revert(repo, commit, NULL)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 4)); + cl_assert(merge_test_workdir(repo, merge_index_entries, 4)); + + git_reference_free(head); + git_commit_free(commit); +} + +void test_revert_workdir__nonmerge_fails_mainline_specified(void) +{ + git_reference *head; + git_commit *commit; + git_revert_options opts = GIT_REVERT_OPTIONS_INIT; + + cl_git_pass(git_repository_head(&head, repo)); + cl_git_pass(git_reference_peel((git_object **)&commit, head, GIT_OBJ_COMMIT)); + + opts.mainline = 1; + cl_must_fail(git_revert(repo, commit, &opts)); + cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG")); + cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/REVERT_HEAD")); + + git_reference_free(head); + git_commit_free(commit); +} + +/* git reset --hard 5acdc74af27172ec491d213ee36cea7eb9ef2579 + * git revert HEAD */ +void test_revert_workdir__merge_fails_without_mainline_specified(void) +{ + git_commit *head; + git_oid head_oid; + + git_oid_fromstr(&head_oid, "5acdc74af27172ec491d213ee36cea7eb9ef2579"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + cl_must_fail(git_revert(repo, head, NULL)); + cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG")); + cl_assert(!git_path_exists(TEST_REPO_PATH "/.git/REVERT_HEAD")); + + git_commit_free(head); +} + +/* git reset --hard 5acdc74af27172ec491d213ee36cea7eb9ef2579 + * git revert HEAD -m1 --no-commit */ +void test_revert_workdir__merge_first_parent(void) +{ + git_commit *head; + git_oid head_oid; + git_revert_options opts = GIT_REVERT_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "296a6d3be1dff05c5d1f631d2459389fa7b619eb", 0, "file-mainline.txt" }, + { 0100644, "0cdb66192ee192f70f891f05a47636057420e871", 0, "file1.txt" }, + { 0100644, "73ec36fa120f8066963a0bc9105bb273dbd903d7", 0, "file2.txt" }, + }; + + opts.mainline = 1; + + git_oid_fromstr(&head_oid, "5acdc74af27172ec491d213ee36cea7eb9ef2579"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + cl_git_pass(git_revert(repo, head, &opts)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 3)); + + git_commit_free(head); +} + +void test_revert_workdir__merge_second_parent(void) +{ + git_commit *head; + git_oid head_oid; + git_revert_options opts = GIT_REVERT_OPTIONS_INIT; + + struct merge_index_entry merge_index_entries[] = { + { 0100644, "33c6fd981c49a2abf2971482089350bfc5cda8ea", 0, "file-branch.txt" }, + { 0100644, "0cdb66192ee192f70f891f05a47636057420e871", 0, "file1.txt" }, + { 0100644, "73ec36fa120f8066963a0bc9105bb273dbd903d7", 0, "file2.txt" }, + }; + + opts.mainline = 2; + + git_oid_fromstr(&head_oid, "5acdc74af27172ec491d213ee36cea7eb9ef2579"); + cl_git_pass(git_commit_lookup(&head, repo, &head_oid)); + cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL, NULL, NULL)); + + cl_git_pass(git_revert(repo, head, &opts)); + + cl_assert(merge_test_index(repo_index, merge_index_entries, 3)); + + git_commit_free(head); +} diff -Nru libgit2-0.20.0/tests/revwalk/basic.c libgit2-0.22.2/tests/revwalk/basic.c --- libgit2-0.20.0/tests/revwalk/basic.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/revwalk/basic.c 2015-03-24 16:10:45.000000000 +0000 @@ -49,12 +49,12 @@ static int get_commit_index(git_oid *raw_oid) { int i; - char oid[40]; + char oid[GIT_OID_HEXSZ]; git_oid_fmt(oid, raw_oid); for (i = 0; i < commit_count; ++i) - if (memcmp(oid, commit_ids[i], 40) == 0) + if (memcmp(oid, commit_ids[i], GIT_OID_HEXSZ) == 0) return i; return -1; @@ -74,9 +74,9 @@ while (git_revwalk_next(&oid, walk) == 0) { result_array[i++] = get_commit_index(&oid); /*{ - char str[41]; + char str[GIT_OID_HEXSZ+1]; git_oid_fmt(str, &oid); - str[40] = 0; + str[GIT_OID_HEXSZ] = 0; printf(" %d) %s\n", i, str); }*/ } @@ -160,7 +160,7 @@ } /* git log --branches --oneline | wc -l => 14 */ - cl_assert(i == 14); + cl_assert_equal_i(i, 14); } void test_revwalk_basic__glob_heads_with_invalid(void) @@ -194,7 +194,7 @@ } /* git log HEAD --oneline | wc -l => 7 */ - cl_assert(i == 7); + cl_assert_equal_i(i, 7); } void test_revwalk_basic__push_head_hide_ref(void) @@ -212,7 +212,7 @@ } /* git log HEAD --oneline --not refs/heads/packed-test | wc -l => 4 */ - cl_assert(i == 4); + cl_assert_equal_i(i, 4); } void test_revwalk_basic__push_head_hide_ref_nobase(void) @@ -230,7 +230,78 @@ } /* git log HEAD --oneline --not refs/heads/packed | wc -l => 7 */ - cl_assert(i == 7); + cl_assert_equal_i(i, 7); +} + +/* +* $ git rev-list HEAD 5b5b02 ^refs/heads/packed-test +* a65fedf39aefe402d3bb6e24df4d4f5fe4547750 +* be3563ae3f795b2b4353bcce3a527ad0a4f7f644 +* c47800c7266a2be04c571c04d5a6614691ea99bd +* 9fd738e8f7967c078dceed8190330fc8648ee56a + +* $ git log HEAD 5b5b02 --oneline --not refs/heads/packed-test | wc -l => 4 +* a65fedf +* be3563a Merge branch 'br2' +* c47800c branch commit one +* 9fd738e a fourth commit +*/ +void test_revwalk_basic__multiple_push_1(void) +{ + int i = 0; + git_oid oid; + + revwalk_basic_setup_walk(NULL); + + cl_git_pass(git_revwalk_push_head(_walk)); + + cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/packed-test")); + + cl_git_pass(git_oid_fromstr(&oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644")); + cl_git_pass(git_revwalk_push(_walk, &oid)); + + while (git_revwalk_next(&oid, _walk) == 0) + i++; + + cl_assert_equal_i(i, 4); +} + +/* +* Difference between test_revwalk_basic__multiple_push_1 and +* test_revwalk_basic__multiple_push_2 is in the order reference +* refs/heads/packed-test and commit 5b5b02 are pushed. +* revwalk should return same commits in both the tests. + +* $ git rev-list 5b5b02 HEAD ^refs/heads/packed-test +* a65fedf39aefe402d3bb6e24df4d4f5fe4547750 +* be3563ae3f795b2b4353bcce3a527ad0a4f7f644 +* c47800c7266a2be04c571c04d5a6614691ea99bd +* 9fd738e8f7967c078dceed8190330fc8648ee56a + +* $ git log 5b5b02 HEAD --oneline --not refs/heads/packed-test | wc -l => 4 +* a65fedf +* be3563a Merge branch 'br2' +* c47800c branch commit one +* 9fd738e a fourth commit +*/ +void test_revwalk_basic__multiple_push_2(void) +{ + int i = 0; + git_oid oid; + + revwalk_basic_setup_walk(NULL); + + cl_git_pass(git_oid_fromstr(&oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644")); + cl_git_pass(git_revwalk_push(_walk, &oid)); + + cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/packed-test")); + + cl_git_pass(git_revwalk_push_head(_walk)); + + while (git_revwalk_next(&oid, _walk) == 0) + i++; + + cl_assert_equal_i(i, 4); } void test_revwalk_basic__disallow_non_commit(void) @@ -252,3 +323,100 @@ cl_git_pass(git_revwalk_push_range(_walk, "9fd738e~2..9fd738e")); cl_git_pass(test_walk_only(_walk, commit_sorting_segment, 1)); } + +void test_revwalk_basic__push_mixed(void) +{ + git_oid oid; + int i = 0; + + revwalk_basic_setup_walk(NULL); + + git_revwalk_reset(_walk); + git_revwalk_sorting(_walk, 0); + cl_git_pass(git_revwalk_push_glob(_walk, "tags")); + + while (git_revwalk_next(&oid, _walk) == 0) { + i++; + } + + /* git rev-list --count --glob=tags #=> 9 */ + cl_assert_equal_i(9, i); +} + +void test_revwalk_basic__push_all(void) +{ + git_oid oid; + int i = 0; + + revwalk_basic_setup_walk(NULL); + + git_revwalk_reset(_walk); + git_revwalk_sorting(_walk, 0); + cl_git_pass(git_revwalk_push_glob(_walk, "*")); + + while (git_revwalk_next(&oid, _walk) == 0) { + i++; + } + + /* git rev-list --count --all #=> 15 */ + cl_assert_equal_i(15, i); +} + +/* +* $ git rev-list br2 master e908 +* a65fedf39aefe402d3bb6e24df4d4f5fe4547750 +* e90810b8df3e80c413d903f631643c716887138d +* 6dcf9bf7541ee10456529833502442f385010c3d +* a4a7dce85cf63874e984719f4fdd239f5145052f +* be3563ae3f795b2b4353bcce3a527ad0a4f7f644 +* c47800c7266a2be04c571c04d5a6614691ea99bd +* 9fd738e8f7967c078dceed8190330fc8648ee56a +* 4a202b346bb0fb0db7eff3cffeb3c70babbd2045 +* 5b5b025afb0b4c913b4c338a42934a3863bf3644 +* 8496071c1b46c854b31185ea97743be6a8774479 +*/ + +void test_revwalk_basic__mimic_git_rev_list(void) +{ + git_oid oid; + + revwalk_basic_setup_walk(NULL); + git_revwalk_sorting(_walk, GIT_SORT_TIME); + + cl_git_pass(git_revwalk_push_ref(_walk, "refs/heads/br2")); + cl_git_pass(git_revwalk_push_ref(_walk, "refs/heads/master")); + cl_git_pass(git_oid_fromstr(&oid, "e90810b8df3e80c413d903f631643c716887138d")); + cl_git_pass(git_revwalk_push(_walk, &oid)); + + cl_git_pass(git_revwalk_next(&oid, _walk)); + cl_assert(!git_oid_streq(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750")); + + cl_git_pass(git_revwalk_next(&oid, _walk)); + cl_assert(!git_oid_streq(&oid, "e90810b8df3e80c413d903f631643c716887138d")); + + cl_git_pass(git_revwalk_next(&oid, _walk)); + cl_assert(!git_oid_streq(&oid, "6dcf9bf7541ee10456529833502442f385010c3d")); + + cl_git_pass(git_revwalk_next(&oid, _walk)); + cl_assert(!git_oid_streq(&oid, "a4a7dce85cf63874e984719f4fdd239f5145052f")); + + cl_git_pass(git_revwalk_next(&oid, _walk)); + cl_assert(!git_oid_streq(&oid, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); + + cl_git_pass(git_revwalk_next(&oid, _walk)); + cl_assert(!git_oid_streq(&oid, "c47800c7266a2be04c571c04d5a6614691ea99bd")); + + cl_git_pass(git_revwalk_next(&oid, _walk)); + cl_assert(!git_oid_streq(&oid, "9fd738e8f7967c078dceed8190330fc8648ee56a")); + + cl_git_pass(git_revwalk_next(&oid, _walk)); + cl_assert(!git_oid_streq(&oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045")); + + cl_git_pass(git_revwalk_next(&oid, _walk)); + cl_assert(!git_oid_streq(&oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644")); + + cl_git_pass(git_revwalk_next(&oid, _walk)); + cl_assert(!git_oid_streq(&oid, "8496071c1b46c854b31185ea97743be6a8774479")); + + cl_git_fail_with(git_revwalk_next(&oid, _walk), GIT_ITEROVER); +} diff -Nru libgit2-0.20.0/tests/revwalk/hidecb.c libgit2-0.22.2/tests/revwalk/hidecb.c --- libgit2-0.20.0/tests/revwalk/hidecb.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/revwalk/hidecb.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,201 @@ +#include "clar_libgit2.h" +/* +* a4a7dce [0] Merge branch 'master' into br2 +|\ +| * 9fd738e [1] a fourth commit +| * 4a202b3 [2] a third commit +* | c47800c [3] branch commit one +|/ +* 5b5b025 [5] another commit +* 8496071 [4] testing +*/ +static const char *commit_head = "a4a7dce85cf63874e984719f4fdd239f5145052f"; + +static const char *commit_strs[] = { + "a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */ + "9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */ + "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */ + "c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */ + "8496071c1b46c854b31185ea97743be6a8774479", /* 4 */ + "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */ +}; + +#define commit_count 6 + +static git_oid commit_ids[commit_count]; +static git_oid _head_id; +static git_repository *_repo; + + +void test_revwalk_hidecb__initialize(void) +{ + int i; + + cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git"))); + cl_git_pass(git_oid_fromstr(&_head_id, commit_head)); + + for (i = 0; i < commit_count; i++) + cl_git_pass(git_oid_fromstr(&commit_ids[i], commit_strs[i])); + +} + +void test_revwalk_hidecb__cleanup(void) +{ + git_repository_free(_repo); + _repo = NULL; +} + +/* Hide all commits */ +static int hide_every_commit_cb(const git_oid *commit_id, void *data) +{ + GIT_UNUSED(commit_id); + GIT_UNUSED(data); + + return 1; +} + +/* Do not hide anything */ +static int hide_none_cb(const git_oid *commit_id, void *data) +{ + GIT_UNUSED(commit_id); + GIT_UNUSED(data); + + return 0; +} + +/* Hide some commits */ +static int hide_commit_cb(const git_oid *commit_id, void *data) +{ + GIT_UNUSED(commit_id); + GIT_UNUSED(data); + + return (git_oid_cmp(commit_id, &commit_ids[5]) == 0); +} + +/* In payload data, pointer to a commit id is passed */ +static int hide_commit_use_payload_cb(const git_oid *commit_id, void *data) +{ + git_oid *hide_commit_id = data; + + return (git_oid_cmp(commit_id, hide_commit_id) == 0); +} + +void test_revwalk_hidecb__hide_all_cb(void) +{ + git_revwalk *walk; + git_oid id; + + cl_git_pass(git_revwalk_new(&walk, _repo)); + cl_git_pass(git_revwalk_add_hide_cb(walk, hide_every_commit_cb, NULL)); + cl_git_pass(git_revwalk_push(walk, &_head_id)); + + /* First call to git_revwalk_next should return GIT_ITEROVER */ + cl_assert_equal_i(GIT_ITEROVER, git_revwalk_next(&id, walk)); + + git_revwalk_free(walk); +} + + +void test_revwalk_hidecb__hide_none_cb(void) +{ + git_revwalk *walk; + int i, error; + git_oid id; + + cl_git_pass(git_revwalk_new(&walk, _repo)); + cl_git_pass(git_revwalk_add_hide_cb(walk, hide_none_cb, NULL)); + cl_git_pass(git_revwalk_push(walk, &_head_id)); + + /* It should return all 6 commits */ + i = 0; + while ((error = git_revwalk_next(&id, walk)) == 0) + i++; + + cl_assert_equal_i(i, 6); + cl_assert_equal_i(error, GIT_ITEROVER); + + git_revwalk_free(walk); +} + +void test_revwalk_hidecb__add_hide_cb_multiple_times(void) +{ + git_revwalk *walk; + + cl_git_pass(git_revwalk_new(&walk, _repo)); + cl_git_pass(git_revwalk_add_hide_cb(walk, hide_every_commit_cb, NULL)); + cl_git_fail(git_revwalk_add_hide_cb(walk, hide_every_commit_cb, NULL)); + + git_revwalk_free(walk); +} + +void test_revwalk_hidecb__add_hide_cb_during_walking(void) +{ + git_revwalk *walk; + git_oid id; + int error; + + cl_git_pass(git_revwalk_new(&walk, _repo)); + cl_git_pass(git_revwalk_push(walk, &_head_id)); + + /* Start walking without adding hide callback */ + cl_git_pass(git_revwalk_next(&id, walk)); + + /* Now add hide callback */ + cl_git_pass(git_revwalk_add_hide_cb(walk, hide_none_cb, NULL)); + + /* walk should be reset */ + error = git_revwalk_next(&id, walk); + cl_assert_equal_i(error, GIT_ITEROVER); + + git_revwalk_free(walk); +} + +void test_revwalk_hidecb__hide_some_commits(void) +{ + git_revwalk *walk; + git_oid id; + int i, error; + + cl_git_pass(git_revwalk_new(&walk, _repo)); + cl_git_pass(git_revwalk_push(walk, &_head_id)); + + /* Add hide callback */ + cl_git_pass(git_revwalk_add_hide_cb(walk, hide_commit_cb, NULL)); + + i = 0; + while ((error = git_revwalk_next(&id, walk)) == 0) { + cl_assert_equal_oid(&commit_ids[i], &id); + i++; + } + + cl_assert_equal_i(i, 4); + cl_assert_equal_i(error, GIT_ITEROVER); + + git_revwalk_free(walk); +} + +void test_revwalk_hidecb__test_payload(void) +{ + git_revwalk *walk; + git_oid id; + int i, error; + + cl_git_pass(git_revwalk_new(&walk, _repo)); + cl_git_pass(git_revwalk_push(walk, &_head_id)); + + /* Add hide callback, pass id of parent of initial commit as payload data */ + cl_git_pass(git_revwalk_add_hide_cb(walk, hide_commit_use_payload_cb, &commit_ids[5])); + + i = 0; + while ((error = git_revwalk_next(&id, walk)) == 0) { + cl_assert_equal_oid(&commit_ids[i], &id); + i++; + } + + /* walker should return four commits */ + cl_assert_equal_i(i, 4); + cl_assert_equal_i(error, GIT_ITEROVER); + + git_revwalk_free(walk); +} + diff -Nru libgit2-0.20.0/tests/revwalk/mergebase.c libgit2-0.22.2/tests/revwalk/mergebase.c --- libgit2-0.20.0/tests/revwalk/mergebase.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/revwalk/mergebase.c 2015-03-24 16:10:45.000000000 +0000 @@ -30,15 +30,15 @@ cl_git_pass(git_oid_fromstr(&expected, "5b5b025afb0b4c913b4c338a42934a3863bf3644")); cl_git_pass(git_merge_base(&result, _repo, &one, &two)); - cl_assert(git_oid_cmp(&result, &expected) == 0); + cl_assert_equal_oid(&expected, &result); cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &one, &two)); - cl_assert_equal_sz(ahead, 2); - cl_assert_equal_sz(behind, 1); + cl_assert_equal_sz(ahead, 1); + cl_assert_equal_sz(behind, 2); cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &two, &one)); - cl_assert_equal_sz(ahead, 1); - cl_assert_equal_sz(behind, 2); + cl_assert_equal_sz(ahead, 2); + cl_assert_equal_sz(behind, 1); } void test_revwalk_mergebase__single2(void) @@ -51,15 +51,15 @@ cl_git_pass(git_oid_fromstr(&expected, "c47800c7266a2be04c571c04d5a6614691ea99bd")); cl_git_pass(git_merge_base(&result, _repo, &one, &two)); - cl_assert(git_oid_cmp(&result, &expected) == 0); + cl_assert_equal_oid(&expected, &result); cl_git_pass(git_graph_ahead_behind( &ahead, &behind, _repo, &one, &two)); - cl_assert_equal_sz(ahead, 4); - cl_assert_equal_sz(behind, 1); - - cl_git_pass(git_graph_ahead_behind( &ahead, &behind, _repo, &two, &one)); cl_assert_equal_sz(ahead, 1); cl_assert_equal_sz(behind, 4); + + cl_git_pass(git_graph_ahead_behind( &ahead, &behind, _repo, &two, &one)); + cl_assert_equal_sz(ahead, 4); + cl_assert_equal_sz(behind, 1); } void test_revwalk_mergebase__merged_branch(void) @@ -72,18 +72,18 @@ cl_git_pass(git_oid_fromstr(&expected, "9fd738e8f7967c078dceed8190330fc8648ee56a")); cl_git_pass(git_merge_base(&result, _repo, &one, &two)); - cl_assert(git_oid_cmp(&result, &expected) == 0); + cl_assert_equal_oid(&expected, &result); cl_git_pass(git_merge_base(&result, _repo, &two, &one)); - cl_assert(git_oid_cmp(&result, &expected) == 0); + cl_assert_equal_oid(&expected, &result); cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &one, &two)); - cl_assert_equal_sz(ahead, 0); - cl_assert_equal_sz(behind, 3); - - cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &two, &one)); cl_assert_equal_sz(ahead, 3); cl_assert_equal_sz(behind, 0); + + cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &two, &one)); + cl_assert_equal_sz(ahead, 0); + cl_assert_equal_sz(behind, 3); } void test_revwalk_mergebase__two_way_merge(void) @@ -95,13 +95,13 @@ cl_git_pass(git_oid_fromstr(&two, "a953a018c5b10b20c86e69fef55ebc8ad4c5a417")); cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo2, &one, &two)); - cl_assert_equal_sz(ahead, 2); - cl_assert_equal_sz(behind, 8); + cl_assert_equal_sz(ahead, 8); + cl_assert_equal_sz(behind, 2); cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo2, &two, &one)); - cl_assert_equal_sz(ahead, 8); - cl_assert_equal_sz(behind, 2); + cl_assert_equal_sz(ahead, 2); + cl_assert_equal_sz(behind, 8); } void test_revwalk_mergebase__no_common_ancestor_returns_ENOTFOUND(void) @@ -119,20 +119,59 @@ cl_assert_equal_i(GIT_ENOTFOUND, error); cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &one, &two)); - cl_assert_equal_sz(2, ahead); - cl_assert_equal_sz(4, behind); + cl_assert_equal_sz(4, ahead); + cl_assert_equal_sz(2, behind); } void test_revwalk_mergebase__prefer_youngest_merge_base(void) { git_oid result, one, two, expected; - cl_git_pass(git_oid_fromstr(&one, "a4a7dce85cf63874e984719f4fdd239f5145052f ")); + cl_git_pass(git_oid_fromstr(&one, "a4a7dce85cf63874e984719f4fdd239f5145052f")); cl_git_pass(git_oid_fromstr(&two, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); cl_git_pass(git_oid_fromstr(&expected, "c47800c7266a2be04c571c04d5a6614691ea99bd")); cl_git_pass(git_merge_base(&result, _repo, &one, &two)); - cl_assert(git_oid_cmp(&result, &expected) == 0); + cl_assert_equal_oid(&expected, &result); +} + +void test_revwalk_mergebase__multiple_merge_bases(void) +{ + git_oid one, two, expected1, expected2; + git_oidarray result = {NULL, 0}; + + cl_git_pass(git_oid_fromstr(&one, "a4a7dce85cf63874e984719f4fdd239f5145052f")); + cl_git_pass(git_oid_fromstr(&two, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); + cl_git_pass(git_oid_fromstr(&expected1, "c47800c7266a2be04c571c04d5a6614691ea99bd")); + cl_git_pass(git_oid_fromstr(&expected2, "9fd738e8f7967c078dceed8190330fc8648ee56a")); + + cl_git_pass(git_merge_bases(&result, _repo, &one, &two)); + cl_assert_equal_i(2, result.count); + cl_assert_equal_oid(&expected1, &result.ids[0]); + cl_assert_equal_oid(&expected2, &result.ids[1]); + + git_oidarray_free(&result); +} + +void test_revwalk_mergebase__multiple_merge_bases_many_commits(void) +{ + git_oid expected1, expected2; + git_oidarray result = {NULL, 0}; + + git_oid *input = git__malloc(sizeof(git_oid) * 2); + + cl_git_pass(git_oid_fromstr(&input[0], "a4a7dce85cf63874e984719f4fdd239f5145052f")); + cl_git_pass(git_oid_fromstr(&input[1], "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); + cl_git_pass(git_oid_fromstr(&expected1, "c47800c7266a2be04c571c04d5a6614691ea99bd")); + cl_git_pass(git_oid_fromstr(&expected2, "9fd738e8f7967c078dceed8190330fc8648ee56a")); + + cl_git_pass(git_merge_bases_many(&result, _repo, 2, input)); + cl_assert_equal_i(2, result.count); + cl_assert_equal_oid(&expected1, &result.ids[0]); + cl_assert_equal_oid(&expected2, &result.ids[1]); + + git_oidarray_free(&result); + git__free(input); } void test_revwalk_mergebase__no_off_by_one_missing(void) @@ -177,7 +216,7 @@ cl_git_pass(git_merge_base_many(&oid, _repo, count, oids)); cl_git_pass(git_oid_fromstr(&expected, expected_sha)); - cl_assert(git_oid_cmp(&expected, &oid) == 0); + cl_assert_equal_oid(&expected, &oid); } git__free(oids); @@ -189,7 +228,6 @@ assert_mergebase_many(NULL, 3, "e90810", "41bc8c", "a65fed"); assert_mergebase_many(NULL, 3, "e90810", "a65fed", "41bc8c"); assert_mergebase_many(NULL, 3, "a65fed", "e90810", "41bc8c"); - assert_mergebase_many(NULL, 3, "a65fed", "e90810", "41bc8c"); assert_mergebase_many(NULL, 3, "a65fed", "41bc8c", "e90810"); assert_mergebase_many(NULL, 3, "e90810", "763d71", "a65fed"); @@ -209,6 +247,70 @@ assert_mergebase_many("5b5b025afb0b4c913b4c338a42934a3863bf3644", 5, "5b5b02", "763d71", "a4a7dc", "a65fed", "41bc8c"); } +static void assert_mergebase_octopus(const char *expected_sha, int count, ...) +{ + va_list ap; + int i; + git_oid *oids; + git_oid oid, expected; + char *partial_oid; + git_object *object; + + oids = git__malloc(count * sizeof(git_oid)); + cl_assert(oids != NULL); + + memset(oids, 0x0, count * sizeof(git_oid)); + + va_start(ap, count); + + for (i = 0; i < count; ++i) { + partial_oid = va_arg(ap, char *); + cl_git_pass(git_oid_fromstrn(&oid, partial_oid, strlen(partial_oid))); + + cl_git_pass(git_object_lookup_prefix(&object, _repo, &oid, strlen(partial_oid), GIT_OBJ_COMMIT)); + git_oid_cpy(&oids[i], git_object_id(object)); + git_object_free(object); + } + + va_end(ap); + + if (expected_sha == NULL) + cl_assert_equal_i(GIT_ENOTFOUND, git_merge_base_octopus(&oid, _repo, count, oids)); + else { + cl_git_pass(git_merge_base_octopus(&oid, _repo, count, oids)); + cl_git_pass(git_oid_fromstr(&expected, expected_sha)); + + cl_assert_equal_oid(&expected, &oid); + } + + git__free(oids); +} + +void test_revwalk_mergebase__octopus_no_common_ancestor_returns_ENOTFOUND(void) +{ + assert_mergebase_octopus(NULL, 3, "41bc8c", "e90810", "a65fed"); + assert_mergebase_octopus(NULL, 3, "e90810", "41bc8c", "a65fed"); + assert_mergebase_octopus(NULL, 3, "e90810", "a65fed", "41bc8c"); + assert_mergebase_octopus(NULL, 3, "a65fed", "e90810", "41bc8c"); + assert_mergebase_octopus(NULL, 3, "a65fed", "41bc8c", "e90810"); + + assert_mergebase_octopus(NULL, 3, "e90810", "763d71", "a65fed"); + + assert_mergebase_octopus(NULL, 3, "763d71", "e90810", "a65fed"); + assert_mergebase_octopus(NULL, 3, "763d71", "a65fed", "e90810"); + + assert_mergebase_octopus(NULL, 5, "5b5b02", "763d71", "a4a7dc", "a65fed", "41bc8c"); +} + +void test_revwalk_mergebase__octopus_merge_branch(void) +{ + assert_mergebase_octopus("8496071c1b46c854b31185ea97743be6a8774479", 3, "a65fed", "763d71", "849607"); + + assert_mergebase_octopus("8496071c1b46c854b31185ea97743be6a8774479", 3, "a65fed", "763d71", "849607"); + assert_mergebase_octopus("8496071c1b46c854b31185ea97743be6a8774479", 3, "a65fed", "849607", "763d71"); + assert_mergebase_octopus("8496071c1b46c854b31185ea97743be6a8774479", 3, "849607", "a65fed", "763d71"); +} + /* * testrepo.git $ git log --graph --all * * commit 763d71aadf09a7951596c9746c024e7eece7c7af diff -Nru libgit2-0.20.0/tests/revwalk/simplify.c libgit2-0.22.2/tests/revwalk/simplify.c --- libgit2-0.20.0/tests/revwalk/simplify.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/revwalk/simplify.c 2015-03-24 16:10:45.000000000 +0000 @@ -20,8 +20,8 @@ static const char *expected_str[] = { "a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */ "c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */ - "8496071c1b46c854b31185ea97743be6a8774479", /* 4 */ - "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */ + "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 4 */ + "8496071c1b46c854b31185ea97743be6a8774479", /* 5 */ }; void test_revwalk_simplify__first_parent(void) @@ -44,7 +44,7 @@ i = 0; while ((error = git_revwalk_next(&id, walk)) == 0) { - git_oid_cmp(&id, &expected[i]); + cl_assert_equal_oid(&expected[i], &id); i++; } diff -Nru libgit2-0.20.0/tests/stash/drop.c libgit2-0.22.2/tests/stash/drop.c --- libgit2-0.20.0/tests/stash/drop.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/stash/drop.c 2015-03-24 16:10:45.000000000 +0000 @@ -115,7 +115,7 @@ cl_git_pass(git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)); entry = git_reflog_entry_byindex(reflog, 0); - cl_assert_equal_i(0, git_oid_cmp(&oid, git_reflog_entry_id_old(entry))); + cl_assert_equal_oid(&oid, git_reflog_entry_id_old(entry)); cl_assert_equal_sz(count - 1, git_reflog_entrycount(reflog)); git_reflog_free(reflog); @@ -147,7 +147,7 @@ cl_git_pass(git_revparse_single(&top_stash, repo, "stash@{0}")); cl_git_pass(git_reference_name_to_id(out, repo, GIT_REFS_STASH_FILE)); - cl_assert_equal_i(true, git_oid_cmp(out, git_object_id(top_stash)) == 0); + cl_assert_equal_oid(out, git_object_id(top_stash)); git_object_free(top_stash); } @@ -162,13 +162,13 @@ retrieve_top_stash_id(&oid); cl_git_pass(git_revparse_single(&next_top_stash, repo, "stash@{1}")); - cl_assert(git_oid_cmp(&oid, git_object_id(next_top_stash)) != 0); + cl_assert(git_oid_cmp(&oid, git_object_id(next_top_stash))); cl_git_pass(git_stash_drop(repo, 0)); retrieve_top_stash_id(&oid); - cl_git_pass(git_oid_cmp(&oid, git_object_id(next_top_stash))); + cl_assert_equal_oid(&oid, git_object_id(next_top_stash)); git_object_free(next_top_stash); } diff -Nru libgit2-0.20.0/tests/stash/save.c libgit2-0.22.2/tests/stash/save.c --- libgit2-0.20.0/tests/stash/save.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/stash/save.c 2015-03-24 16:10:45.000000000 +0000 @@ -148,6 +148,25 @@ assert_blob_oid("refs/stash^3:just.ignore", NULL); } +void test_stash_save__untracked_skips_ignored(void) +{ + cl_git_append2file("stash/.gitignore", "bundle/vendor/\n"); + cl_must_pass(p_mkdir("stash/bundle", 0777)); + cl_must_pass(p_mkdir("stash/bundle/vendor", 0777)); + cl_git_mkfile("stash/bundle/vendor/blah", "contents\n"); + + cl_assert(git_path_exists("stash/when")); /* untracked */ + cl_assert(git_path_exists("stash/just.ignore")); /* ignored */ + cl_assert(git_path_exists("stash/bundle/vendor/blah")); /* ignored */ + + cl_git_pass(git_stash_save( + &stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED)); + + cl_assert(!git_path_exists("stash/when")); + cl_assert(git_path_exists("stash/bundle/vendor/blah")); + cl_assert(git_path_exists("stash/just.ignore")); +} + void test_stash_save__can_include_untracked_and_ignored_files(void) { cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED)); @@ -159,6 +178,8 @@ assert_blob_oid("refs/stash^3:who", NULL); assert_blob_oid("refs/stash^3:when", "b6ed15e81e2593d7bb6265eb4a991d29dc3e628b"); assert_blob_oid("refs/stash^3:just.ignore", "78925fb1236b98b37a35e9723033e627f97aa88b"); + + cl_assert(!git_path_exists("stash/just.ignore")); } #define MESSAGE "Look Ma! I'm on TV!" @@ -174,7 +195,7 @@ { git_reference *head; - cl_git_pass(git_reference_symbolic_create(&head, repo, "HEAD", "refs/heads/unborn", 1)); + cl_git_pass(git_reference_symbolic_create(&head, repo, "HEAD", "refs/heads/unborn", 1, NULL, NULL)); cl_assert_equal_i(GIT_EUNBORNBRANCH, git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT)); @@ -196,7 +217,7 @@ void test_stash_save__can_stash_against_a_detached_head(void) { - git_repository_detach_head(repo); + git_repository_detach_head(repo, NULL, NULL); cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT)); @@ -206,18 +227,12 @@ void test_stash_save__stashing_updates_the_reflog(void) { - char *sha; - assert_object_oid("refs/stash@{0}", NULL, GIT_OBJ_COMMIT); cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT)); - sha = git_oid_allocfmt(&stash_tip_oid); - - assert_object_oid("refs/stash@{0}", sha, GIT_OBJ_COMMIT); + assert_object_oid("refs/stash@{0}", git_oid_tostr_s(&stash_tip_oid), GIT_OBJ_COMMIT); assert_object_oid("refs/stash@{1}", NULL, GIT_OBJ_COMMIT); - - git__free(sha); } void test_stash_save__cannot_stash_when_there_are_no_local_change(void) @@ -340,7 +355,7 @@ void test_stash_save__including_untracked_without_any_untracked_file_creates_an_empty_tree(void) { - cl_git_pass(p_unlink("stash/when")); + cl_must_pass(p_unlink("stash/when")); assert_status(repo, "what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED); assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED); @@ -352,3 +367,35 @@ assert_object_oid("stash^3^{tree}", EMPTY_TREE, GIT_OBJ_TREE); } + +void test_stash_save__ignored_directory(void) +{ + cl_git_pass(p_mkdir("stash/ignored_directory", 0777)); + cl_git_pass(p_mkdir("stash/ignored_directory/sub", 0777)); + cl_git_mkfile("stash/ignored_directory/sub/some_file", "stuff"); + + assert_status(repo, "ignored_directory/sub/some_file", GIT_STATUS_WT_NEW); + cl_git_pass(git_ignore_add_rule(repo, "ignored_directory/")); + assert_status(repo, "ignored_directory/sub/some_file", GIT_STATUS_IGNORED); + + cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED)); + + cl_assert(!git_path_exists("stash/ignored_directory/sub/some_file")); + cl_assert(!git_path_exists("stash/ignored_directory/sub")); + cl_assert(!git_path_exists("stash/ignored_directory")); +} + +void test_stash_save__skip_submodules(void) +{ + git_repository *untracked_repo; + cl_git_pass(git_repository_init(&untracked_repo, "stash/untracked_repo", false)); + cl_git_mkfile("stash/untracked_repo/content", "stuff"); + git_repository_free(untracked_repo); + + assert_status(repo, "untracked_repo/", GIT_STATUS_WT_NEW); + + cl_git_pass(git_stash_save( + &stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED)); + + assert_status(repo, "untracked_repo/", GIT_STATUS_WT_NEW); +} diff -Nru libgit2-0.20.0/tests/stash/stash_helpers.c libgit2-0.22.2/tests/stash/stash_helpers.c --- libgit2-0.20.0/tests/stash/stash_helpers.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/stash/stash_helpers.c 2015-03-24 16:10:45.000000000 +0000 @@ -42,15 +42,11 @@ int status_flags) { unsigned int status; - int error; - error = git_status_file(&status, repo, path); - - if (status_flags < 0) { - cl_assert_equal_i(status_flags, error); - return; + if (status_flags < 0) + cl_assert_equal_i(status_flags, git_status_file(&status, repo, path)); + else { + cl_git_pass(git_status_file(&status, repo, path)); + cl_assert_equal_i((unsigned int)status_flags, status); } - - cl_assert_equal_i(0, error); - cl_assert_equal_i((unsigned int)status_flags, status); } diff -Nru libgit2-0.20.0/tests/stash/submodules.c libgit2-0.22.2/tests/stash/submodules.c --- libgit2-0.20.0/tests/stash/submodules.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/stash/submodules.c 2015-03-24 16:10:45.000000000 +0000 @@ -19,6 +19,9 @@ void test_stash_submodules__cleanup(void) { + git_submodule_free(sm); + sm = NULL; + git_signature_free(signature); signature = NULL; } diff -Nru libgit2-0.20.0/tests/status/ignore.c libgit2-0.22.2/tests/status/ignore.c --- libgit2-0.20.0/tests/status/ignore.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/status/ignore.c 2015-03-24 16:10:45.000000000 +0000 @@ -16,6 +16,23 @@ cl_git_sandbox_cleanup(); } +static void assert_ignored_( + bool expected, const char *filepath, const char *file, int line) +{ + int is_ignored = 0; + cl_git_pass_( + git_status_should_ignore(&is_ignored, g_repo, filepath), file, line); + clar__assert( + (expected != 0) == (is_ignored != 0), + file, line, "expected != is_ignored", filepath, 1); +} +#define assert_ignored(expected, filepath) \ + assert_ignored_(expected, filepath, __FILE__, __LINE__) +#define assert_is_ignored(filepath) \ + assert_ignored_(true, filepath, __FILE__, __LINE__) +#define refute_is_ignored(filepath) \ + assert_ignored_(false, filepath, __FILE__, __LINE__) + void test_status_ignore__0(void) { struct { @@ -47,51 +64,35 @@ g_repo = cl_git_sandbox_init("attr"); - for (one_test = test_cases; one_test->path != NULL; one_test++) { - int ignored; - cl_git_pass(git_status_should_ignore(&ignored, g_repo, one_test->path)); - cl_assert_(ignored == one_test->expected, one_test->path); - } + for (one_test = test_cases; one_test->path != NULL; one_test++) + assert_ignored(one_test->expected, one_test->path); /* confirm that ignore files were cached */ - cl_assert(git_attr_cache__is_cached(g_repo, 0, ".git/info/exclude")); - cl_assert(git_attr_cache__is_cached(g_repo, 0, ".gitignore")); + cl_assert(git_attr_cache__is_cached( + g_repo, GIT_ATTR_FILE__FROM_FILE, ".git/info/exclude")); + cl_assert(git_attr_cache__is_cached( + g_repo, GIT_ATTR_FILE__FROM_FILE, ".gitignore")); } void test_status_ignore__1(void) { - int ignored; - g_repo = cl_git_sandbox_init("attr"); cl_git_rewritefile("attr/.gitignore", "/*.txt\n/dir/\n"); git_attr_cache_flush(g_repo); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "root_test4.txt")); - cl_assert(ignored); - - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "sub/subdir_test2.txt")); - cl_assert(!ignored); - - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "dir")); - cl_assert(ignored); - - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "dir/")); - cl_assert(ignored); - - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "sub/dir")); - cl_assert(!ignored); - - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "sub/dir/")); - cl_assert(!ignored); + assert_is_ignored("root_test4.txt"); + refute_is_ignored("sub/subdir_test2.txt"); + assert_is_ignored("dir"); + assert_is_ignored("dir/"); + refute_is_ignored("sub/dir"); + refute_is_ignored("sub/dir/"); } - void test_status_ignore__empty_repo_with_gitignore_rewrite(void) { status_entry_single st; - int ignored; g_repo = cl_git_sandbox_init("empty_standard_repo"); @@ -106,8 +107,7 @@ cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt")); cl_assert(st.status == GIT_STATUS_WT_NEW); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "look-ma.txt")); - cl_assert(!ignored); + refute_is_ignored("look-ma.txt"); cl_git_rewritefile("empty_standard_repo/.gitignore", "*.nomatch\n"); @@ -119,8 +119,7 @@ cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt")); cl_assert(st.status == GIT_STATUS_WT_NEW); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "look-ma.txt")); - cl_assert(!ignored); + refute_is_ignored("look-ma.txt"); cl_git_rewritefile("empty_standard_repo/.gitignore", "*.txt\n"); @@ -132,8 +131,7 @@ cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt")); cl_assert(st.status == GIT_STATUS_IGNORED); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "look-ma.txt")); - cl_assert(ignored); + assert_is_ignored("look-ma.txt"); } void test_status_ignore__ignore_pattern_contains_space(void) @@ -179,7 +177,6 @@ void test_status_ignore__subdirectories(void) { status_entry_single st; - int ignored; g_repo = cl_git_sandbox_init("empty_standard_repo"); @@ -196,8 +193,7 @@ cl_git_pass(git_status_file(&st.status, g_repo, "ignore_me")); cl_assert(st.status == GIT_STATUS_IGNORED); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "ignore_me")); - cl_assert(ignored); + assert_is_ignored("ignore_me"); /* I've changed libgit2 so that the behavior here now differs from * core git but seems to make more sense. In core git, the following @@ -223,11 +219,37 @@ cl_git_pass(git_status_file(&st.status, g_repo, "test/ignore_me/file")); cl_assert(st.status == GIT_STATUS_IGNORED); - cl_git_pass( - git_status_should_ignore(&ignored, g_repo, "test/ignore_me/file")); - cl_assert(ignored); + assert_is_ignored("test/ignore_me/file"); } +static void make_test_data(const char *reponame, const char **files) +{ + const char **scan; + size_t repolen = strlen(reponame) + 1; + + g_repo = cl_git_sandbox_init(reponame); + + for (scan = files; *scan != NULL; ++scan) { + cl_git_pass(git_futils_mkdir( + *scan + repolen, reponame, + 0777, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST)); + cl_git_mkfile(*scan, "contents"); + } +} + +static const char *test_repo_1 = "empty_standard_repo"; +static const char *test_files_1[] = { + "empty_standard_repo/dir/a/ignore_me", + "empty_standard_repo/dir/b/ignore_me", + "empty_standard_repo/dir/ignore_me", + "empty_standard_repo/ignore_also/file", + "empty_standard_repo/ignore_me", + "empty_standard_repo/test/ignore_me/file", + "empty_standard_repo/test/ignore_me/file2", + "empty_standard_repo/test/ignore_me/and_me/file", + NULL +}; + void test_status_ignore__subdirectories_recursion(void) { /* Let's try again with recursing into ignored dirs turned on */ @@ -235,6 +257,9 @@ status_entry_counts counts; static const char *paths_r[] = { ".gitignore", + "dir/a/ignore_me", + "dir/b/ignore_me", + "dir/ignore_me", "ignore_also/file", "ignore_me", "test/ignore_me/and_me/file", @@ -242,49 +267,30 @@ "test/ignore_me/file2", }; static const unsigned int statuses_r[] = { - GIT_STATUS_WT_NEW, - GIT_STATUS_IGNORED, - GIT_STATUS_IGNORED, - GIT_STATUS_IGNORED, - GIT_STATUS_IGNORED, - GIT_STATUS_IGNORED, + GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, + GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, + GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, }; static const char *paths_nr[] = { ".gitignore", + "dir/a/ignore_me", + "dir/b/ignore_me", + "dir/ignore_me", "ignore_also/", "ignore_me", "test/ignore_me/", }; static const unsigned int statuses_nr[] = { GIT_STATUS_WT_NEW, - GIT_STATUS_IGNORED, - GIT_STATUS_IGNORED, - GIT_STATUS_IGNORED, + GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, + GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, }; - g_repo = cl_git_sandbox_init("empty_standard_repo"); - + make_test_data(test_repo_1, test_files_1); cl_git_rewritefile("empty_standard_repo/.gitignore", "ignore_me\n/ignore_also\n"); - cl_git_mkfile( - "empty_standard_repo/ignore_me", "I'm going to be ignored!"); - cl_git_pass(git_futils_mkdir_r( - "empty_standard_repo/test/ignore_me", NULL, 0775)); - cl_git_mkfile( - "empty_standard_repo/test/ignore_me/file", "I'm going to be ignored!"); - cl_git_mkfile( - "empty_standard_repo/test/ignore_me/file2", "Me, too!"); - cl_git_pass(git_futils_mkdir_r( - "empty_standard_repo/test/ignore_me/and_me", NULL, 0775)); - cl_git_mkfile( - "empty_standard_repo/test/ignore_me/and_me/file", "Deeply ignored"); - cl_git_pass(git_futils_mkdir_r( - "empty_standard_repo/ignore_also", NULL, 0775)); - cl_git_mkfile( - "empty_standard_repo/ignore_also/file", "I'm going to be ignored!"); - memset(&counts, 0x0, sizeof(status_entry_counts)); - counts.expected_entry_count = 6; + counts.expected_entry_count = 9; counts.expected_paths = paths_r; counts.expected_statuses = statuses_r; @@ -299,7 +305,7 @@ memset(&counts, 0x0, sizeof(status_entry_counts)); - counts.expected_entry_count = 4; + counts.expected_entry_count = 7; counts.expected_paths = paths_nr; counts.expected_statuses = statuses_nr; @@ -313,151 +319,256 @@ cl_assert_equal_i(0, counts.wrong_sorted_path); } -void test_status_ignore__adding_internal_ignores(void) +void test_status_ignore__subdirectories_not_at_root(void) { - int ignored; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + status_entry_counts counts; + static const char *paths_1[] = { + "dir/.gitignore", + "dir/a/ignore_me", + "dir/b/ignore_me", + "dir/ignore_me", + "ignore_also/file", + "ignore_me", + "test/.gitignore", + "test/ignore_me/and_me/file", + "test/ignore_me/file", + "test/ignore_me/file2", + }; + static const unsigned int statuses_1[] = { + GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, + GIT_STATUS_IGNORED, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, + GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, + }; + + make_test_data(test_repo_1, test_files_1); + cl_git_rewritefile("empty_standard_repo/dir/.gitignore", "ignore_me\n/ignore_also\n"); + cl_git_rewritefile("empty_standard_repo/test/.gitignore", "and_me\n"); + + memset(&counts, 0x0, sizeof(status_entry_counts)); + counts.expected_entry_count = 10; + counts.expected_paths = paths_1; + counts.expected_statuses = statuses_1; + + opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_RECURSE_IGNORED_DIRS; + + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__normal, &counts)); + + 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); +} + +void test_status_ignore__leading_slash_ignores(void) +{ + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + status_entry_counts counts; + static const char *paths_2[] = { + "dir/.gitignore", + "dir/a/ignore_me", + "dir/b/ignore_me", + "dir/ignore_me", + "ignore_also/file", + "ignore_me", + "test/.gitignore", + "test/ignore_me/and_me/file", + "test/ignore_me/file", + "test/ignore_me/file2", + }; + static const unsigned int statuses_2[] = { + GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, + GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, + GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, + }; + + make_test_data(test_repo_1, test_files_1); + + cl_fake_home(); + cl_git_mkfile("home/.gitignore", "/ignore_me\n"); + { + git_config *cfg; + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_set_string( + cfg, "core.excludesfile", "~/.gitignore")); + git_config_free(cfg); + } + + cl_git_rewritefile("empty_standard_repo/.git/info/exclude", "/ignore_also\n"); + cl_git_rewritefile("empty_standard_repo/dir/.gitignore", "/ignore_me\n"); + cl_git_rewritefile("empty_standard_repo/test/.gitignore", "/and_me\n"); + + memset(&counts, 0x0, sizeof(status_entry_counts)); + counts.expected_entry_count = 10; + counts.expected_paths = paths_2; + counts.expected_statuses = statuses_2; + + opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_RECURSE_IGNORED_DIRS; + + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__normal, &counts)); + + 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); +} + +void test_status_ignore__contained_dir_with_matching_name(void) +{ + static const char *test_files[] = { + "empty_standard_repo/subdir_match/aaa/subdir_match/file", + "empty_standard_repo/subdir_match/zzz_ignoreme", + NULL + }; + static const char *expected_paths[] = { + "subdir_match/.gitignore", + "subdir_match/aaa/subdir_match/file", + "subdir_match/zzz_ignoreme", + }; + static const unsigned int expected_statuses[] = { + GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED + }; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + status_entry_counts counts; + + make_test_data("empty_standard_repo", test_files); + cl_git_mkfile( + "empty_standard_repo/subdir_match/.gitignore", "*_ignoreme\n"); + + refute_is_ignored("subdir_match/aaa/subdir_match/file"); + assert_is_ignored("subdir_match/zzz_ignoreme"); + + memset(&counts, 0x0, sizeof(status_entry_counts)); + counts.expected_entry_count = 3; + counts.expected_paths = expected_paths; + counts.expected_statuses = expected_statuses; + + opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_RECURSE_IGNORED_DIRS; + + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__normal, &counts)); + + 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); +} + +void test_status_ignore__trailing_slash_star(void) +{ + static const char *test_files[] = { + "empty_standard_repo/file", + "empty_standard_repo/subdir/file", + "empty_standard_repo/subdir/sub2/sub3/file", + NULL + }; + make_test_data("empty_standard_repo", test_files); + cl_git_mkfile( + "empty_standard_repo/subdir/.gitignore", "/**/*\n"); + + refute_is_ignored("file"); + assert_is_ignored("subdir/sub2/sub3/file"); + assert_is_ignored("subdir/file"); +} + +void test_status_ignore__adding_internal_ignores(void) +{ g_repo = cl_git_sandbox_init("empty_standard_repo"); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt")); - cl_assert(!ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar")); - cl_assert(!ignored); + refute_is_ignored("one.txt"); + refute_is_ignored("two.bar"); cl_git_pass(git_ignore_add_rule(g_repo, "*.nomatch\n")); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt")); - cl_assert(!ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar")); - cl_assert(!ignored); + refute_is_ignored("one.txt"); + refute_is_ignored("two.bar"); cl_git_pass(git_ignore_add_rule(g_repo, "*.txt\n")); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt")); - cl_assert(ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar")); - cl_assert(!ignored); + assert_is_ignored("one.txt"); + refute_is_ignored("two.bar"); cl_git_pass(git_ignore_add_rule(g_repo, "*.bar\n")); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt")); - cl_assert(ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar")); - cl_assert(ignored); + assert_is_ignored("one.txt"); + assert_is_ignored("two.bar"); cl_git_pass(git_ignore_clear_internal_rules(g_repo)); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt")); - cl_assert(!ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar")); - cl_assert(!ignored); + refute_is_ignored("one.txt"); + refute_is_ignored("two.bar"); cl_git_pass(git_ignore_add_rule( g_repo, "multiple\n*.rules\n# comment line\n*.bar\n")); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.txt")); - cl_assert(!ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar")); - cl_assert(ignored); + refute_is_ignored("one.txt"); + assert_is_ignored("two.bar"); } void test_status_ignore__add_internal_as_first_thing(void) { - int ignored; const char *add_me = "\n#################\n## Eclipse\n#################\n\n*.pydevproject\n.project\n.metadata\nbin/\ntmp/\n*.tmp\n\n"; g_repo = cl_git_sandbox_init("empty_standard_repo"); cl_git_pass(git_ignore_add_rule(g_repo, add_me)); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "one.tmp")); - cl_assert(ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "two.bar")); - cl_assert(!ignored); + assert_is_ignored("one.tmp"); + refute_is_ignored("two.bar"); } void test_status_ignore__internal_ignores_inside_deep_paths(void) { - int ignored; const char *add_me = "Debug\nthis/is/deep\npatterned*/dir\n"; g_repo = cl_git_sandbox_init("empty_standard_repo"); cl_git_pass(git_ignore_add_rule(g_repo, add_me)); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "Debug")); - cl_assert(ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "and/Debug")); - cl_assert(ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "really/Debug/this/file")); - cl_assert(ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "Debug/what/I/say")); - cl_assert(ignored); - - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "and/NoDebug")); - cl_assert(!ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "NoDebug/this")); - cl_assert(!ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "please/NoDebug/this")); - cl_assert(!ignored); + assert_is_ignored("Debug"); + assert_is_ignored("and/Debug"); + assert_is_ignored("really/Debug/this/file"); + assert_is_ignored("Debug/what/I/say"); + + refute_is_ignored("and/NoDebug"); + refute_is_ignored("NoDebug/this"); + refute_is_ignored("please/NoDebug/this"); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/is/deep")); - cl_assert(ignored); + assert_is_ignored("this/is/deep"); /* pattern containing slash gets FNM_PATHNAME so all slashes must match */ - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "and/this/is/deep")); - cl_assert(!ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/is/deep/too")); - cl_assert(ignored); + refute_is_ignored("and/this/is/deep"); + assert_is_ignored("this/is/deep/too"); /* pattern containing slash gets FNM_PATHNAME so all slashes must match */ - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "but/this/is/deep/and/ignored")); - cl_assert(!ignored); + refute_is_ignored("but/this/is/deep/and/ignored"); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/is/not/deep")); - cl_assert(!ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "is/this/not/as/deep")); - cl_assert(!ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/is/deepish")); - cl_assert(!ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "xthis/is/deep")); - cl_assert(!ignored); + refute_is_ignored("this/is/not/deep"); + refute_is_ignored("is/this/not/as/deep"); + refute_is_ignored("this/is/deepish"); + refute_is_ignored("xthis/is/deep"); } void test_status_ignore__automatically_ignore_bad_files(void) { - int ignored; - g_repo = cl_git_sandbox_init("empty_standard_repo"); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, ".git")); - cl_assert(ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/file/.")); - cl_assert(ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/../funky")); - cl_assert(ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/whatever.c")); - cl_assert(!ignored); + assert_is_ignored(".git"); + assert_is_ignored("this/file/."); + assert_is_ignored("path/../funky"); + refute_is_ignored("path/whatever.c"); cl_git_pass(git_ignore_add_rule(g_repo, "*.c\n")); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, ".git")); - cl_assert(ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/file/.")); - cl_assert(ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/../funky")); - cl_assert(ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/whatever.c")); - cl_assert(ignored); + assert_is_ignored(".git"); + assert_is_ignored("this/file/."); + assert_is_ignored("path/../funky"); + assert_is_ignored("path/whatever.c"); cl_git_pass(git_ignore_clear_internal_rules(g_repo)); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, ".git")); - cl_assert(ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "this/file/.")); - cl_assert(ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/../funky")); - cl_assert(ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "path/whatever.c")); - cl_assert(!ignored); + assert_is_ignored(".git"); + assert_is_ignored("this/file/."); + assert_is_ignored("path/../funky"); + refute_is_ignored("path/whatever.c"); } void test_status_ignore__filenames_with_special_prefixes_do_not_interfere_with_status_retrieval(void) @@ -496,7 +607,6 @@ void test_status_ignore__issue_1766_negated_ignores(void) { - int ignored = 0; unsigned int status; g_repo = cl_git_sandbox_init("empty_standard_repo"); @@ -508,11 +618,8 @@ cl_git_mkfile( "empty_standard_repo/a/ignoreme", "I should be ignored\n"); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/.gitignore")); - cl_assert(!ignored); - - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/ignoreme")); - cl_assert(ignored); + refute_is_ignored("a/.gitignore"); + assert_is_ignored("a/ignoreme"); cl_git_pass(git_futils_mkdir_r( "empty_standard_repo/b", NULL, 0775)); @@ -521,18 +628,12 @@ cl_git_mkfile( "empty_standard_repo/b/ignoreme", "I should be ignored\n"); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "b/.gitignore")); - cl_assert(!ignored); - - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "b/ignoreme")); - cl_assert(ignored); + refute_is_ignored("b/.gitignore"); + assert_is_ignored("b/ignoreme"); /* shouldn't have changed results from first couple either */ - - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/.gitignore")); - cl_assert(!ignored); - cl_git_pass(git_status_should_ignore(&ignored, g_repo, "a/ignoreme")); - cl_assert(ignored); + refute_is_ignored("a/.gitignore"); + assert_is_ignored("a/ignoreme"); /* status should find the two ignore files and nothing else */ @@ -580,3 +681,279 @@ } } +static void add_one_to_index(const char *file) +{ + git_index *index; + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_add_bypath(index, file)); + git_index_free(index); +} + +/* Some further broken scenarios that have been reported */ +void test_status_ignore__more_breakage(void) +{ + static const char *test_files[] = { + "empty_standard_repo/d1/pfx-d2/d3/d4/d5/tracked", + "empty_standard_repo/d1/pfx-d2/d3/d4/d5/untracked", + "empty_standard_repo/d1/pfx-d2/d3/d4/untracked", + NULL + }; + + make_test_data("empty_standard_repo", test_files); + cl_git_mkfile( + "empty_standard_repo/.gitignore", + "/d1/pfx-*\n" + "!/d1/pfx-d2/\n" + "/d1/pfx-d2/*\n" + "!/d1/pfx-d2/d3/\n" + "/d1/pfx-d2/d3/*\n" + "!/d1/pfx-d2/d3/d4/\n"); + add_one_to_index("d1/pfx-d2/d3/d4/d5/tracked"); + + { + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + status_entry_counts counts; + static const char *files[] = { + ".gitignore", + "d1/pfx-d2/d3/d4/d5/tracked", + "d1/pfx-d2/d3/d4/d5/untracked", + "d1/pfx-d2/d3/d4/untracked", + }; + static const unsigned int statuses[] = { + GIT_STATUS_WT_NEW, + GIT_STATUS_INDEX_NEW, + GIT_STATUS_WT_NEW, + GIT_STATUS_WT_NEW, + }; + + memset(&counts, 0x0, sizeof(status_entry_counts)); + counts.expected_entry_count = 4; + counts.expected_paths = files; + counts.expected_statuses = statuses; + opts.flags = GIT_STATUS_OPT_DEFAULTS | + GIT_STATUS_OPT_INCLUDE_IGNORED | + GIT_STATUS_OPT_RECURSE_IGNORED_DIRS; + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__normal, &counts)); + + 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); + } + + refute_is_ignored("d1/pfx-d2/d3/d4/d5/tracked"); + refute_is_ignored("d1/pfx-d2/d3/d4/d5/untracked"); + refute_is_ignored("d1/pfx-d2/d3/d4/untracked"); +} + +void test_status_ignore__negative_ignores_inside_ignores(void) +{ + static const char *test_files[] = { + "empty_standard_repo/top/mid/btm/tracked", + "empty_standard_repo/top/mid/btm/untracked", + "empty_standard_repo/zoo/bar", + "empty_standard_repo/zoo/foo/bar", + NULL + }; + + make_test_data("empty_standard_repo", test_files); + cl_git_mkfile( + "empty_standard_repo/.gitignore", + "top\n" + "!top/mid/btm\n" + "zoo/*\n" + "!zoo/bar\n" + "!zoo/foo/bar\n"); + add_one_to_index("top/mid/btm/tracked"); + + { + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + status_entry_counts counts; + static const char *files[] = { + ".gitignore", "top/mid/btm/tracked", "top/mid/btm/untracked", + "zoo/bar", "zoo/foo/bar", + }; + static const unsigned int statuses[] = { + GIT_STATUS_WT_NEW, GIT_STATUS_INDEX_NEW, GIT_STATUS_IGNORED, + GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED, + }; + + memset(&counts, 0x0, sizeof(status_entry_counts)); + counts.expected_entry_count = 5; + counts.expected_paths = files; + counts.expected_statuses = statuses; + opts.flags = GIT_STATUS_OPT_DEFAULTS | + GIT_STATUS_OPT_INCLUDE_IGNORED | + GIT_STATUS_OPT_RECURSE_IGNORED_DIRS; + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__normal, &counts)); + + 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); + } + + assert_is_ignored("top/mid/btm/tracked"); + assert_is_ignored("top/mid/btm/untracked"); + refute_is_ignored("foo/bar"); +} + +void test_status_ignore__negative_ignores_in_slash_star(void) +{ + git_status_options status_opts = GIT_STATUS_OPTIONS_INIT; + git_status_list *list; + int found_look_ma = 0, found_what_about = 0; + size_t i; + static const char *test_files[] = { + "empty_standard_repo/bin/look-ma.txt", + "empty_standard_repo/bin/what-about-me.txt", + NULL + }; + + make_test_data("empty_standard_repo", test_files); + cl_git_mkfile( + "empty_standard_repo/.gitignore", + "bin/*\n" + "!bin/w*\n"); + + assert_is_ignored("bin/look-ma.txt"); + refute_is_ignored("bin/what-about-me.txt"); + + status_opts.flags = GIT_STATUS_OPT_DEFAULTS; + cl_git_pass(git_status_list_new(&list, g_repo, &status_opts)); + for (i = 0; i < git_status_list_entrycount(list); i++) { + const git_status_entry *entry = git_status_byindex(list, i); + + if (!strcmp("bin/look-ma.txt", entry->index_to_workdir->new_file.path)) + found_look_ma = 1; + + if (!strcmp("bin/what-about-me.txt", entry->index_to_workdir->new_file.path)) + found_what_about = 1; + } + git_status_list_free(list); + + cl_assert(found_look_ma); + cl_assert(found_what_about); +} + +void test_status_ignore__negative_ignores_without_trailing_slash_inside_ignores(void) +{ + git_status_options status_opts = GIT_STATUS_OPTIONS_INIT; + git_status_list *list; + int found_parent_file = 0, found_parent_child1_file = 0, found_parent_child2_file = 0; + size_t i; + static const char *test_files[] = { + "empty_standard_repo/parent/file.txt", + "empty_standard_repo/parent/force.txt", + "empty_standard_repo/parent/child1/file.txt", + "empty_standard_repo/parent/child2/file.txt", + NULL + }; + + make_test_data("empty_standard_repo", test_files); + cl_git_mkfile( + "empty_standard_repo/.gitignore", + "parent/*\n" + "!parent/force.txt\n" + "!parent/child1\n" + "!parent/child2/\n"); + + add_one_to_index("parent/force.txt"); + + assert_is_ignored("parent/file.txt"); + refute_is_ignored("parent/force.txt"); + refute_is_ignored("parent/child1/file.txt"); + refute_is_ignored("parent/child2/file.txt"); + + status_opts.flags = GIT_STATUS_OPT_DEFAULTS; + cl_git_pass(git_status_list_new(&list, g_repo, &status_opts)); + for (i = 0; i < git_status_list_entrycount(list); i++) { + const git_status_entry *entry = git_status_byindex(list, i); + + if (!entry->index_to_workdir) + continue; + + if (!strcmp("parent/file.txt", entry->index_to_workdir->new_file.path)) + found_parent_file = 1; + + if (!strcmp("parent/force.txt", entry->index_to_workdir->new_file.path)) + found_parent_file = 1; + + if (!strcmp("parent/child1/file.txt", entry->index_to_workdir->new_file.path)) + found_parent_child1_file = 1; + + if (!strcmp("parent/child2/file.txt", entry->index_to_workdir->new_file.path)) + found_parent_child2_file = 1; + } + git_status_list_free(list); + + cl_assert(found_parent_file); + cl_assert(found_parent_child1_file); + cl_assert(found_parent_child2_file); +} + +void test_status_ignore__filename_with_cr(void) +{ + int ignored; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + cl_git_mkfile("empty_standard_repo/.gitignore", "Icon\r\r\n"); + + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Icon\r")); + cl_assert_equal_i(1, ignored); + + cl_git_mkfile("empty_standard_repo/.gitignore", "Ico\rn\n"); + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Ico\rn")); + cl_assert_equal_i(1, ignored); + + cl_git_mkfile("empty_standard_repo/.gitignore", "Ico\rn\r\n"); + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Ico\rn")); + cl_assert_equal_i(1, ignored); + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Ico\rn\r")); + cl_assert_equal_i(0, ignored); + + cl_git_mkfile("empty_standard_repo/.gitignore", "Ico\rn\r\r\n"); + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Ico\rn\r")); + cl_assert_equal_i(1, ignored); + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Icon\r")); + cl_assert_equal_i(0, ignored); + + cl_git_mkfile("empty_standard_repo/.gitignore", "Icon\r\n"); + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Icon\r")); + cl_assert_equal_i(0, ignored); + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Icon")); + cl_assert_equal_i(1, ignored); +} + +void test_status_ignore__subdir_doesnt_match_above(void) +{ + int ignored, icase = 0, error; + git_config *cfg; + + g_repo = cl_git_sandbox_init("empty_standard_repo"); + + cl_git_pass(git_repository_config_snapshot(&cfg, g_repo)); + error = git_config_get_bool(&icase, cfg, "core.ignorecase"); + git_config_free(cfg); + if (error == GIT_ENOTFOUND) + error = 0; + + cl_git_pass(error); + + cl_git_pass(p_mkdir("empty_standard_repo/src", 0777)); + cl_git_pass(p_mkdir("empty_standard_repo/src/src", 0777)); + cl_git_mkfile("empty_standard_repo/src/.gitignore", "src\n"); + cl_git_mkfile("empty_standard_repo/.gitignore", ""); + + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "src/test.txt")); + cl_assert_equal_i(0, ignored); + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "src/src/test.txt")); + cl_assert_equal_i(1, ignored); + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "src/foo/test.txt")); + cl_assert_equal_i(0, ignored); + + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "SRC/src/test.txt")); + cl_assert_equal_i(icase, ignored); + cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "src/SRC/test.txt")); + cl_assert_equal_i(icase, ignored); +} diff -Nru libgit2-0.20.0/tests/status/renames.c libgit2-0.22.2/tests/status/renames.c --- libgit2-0.20.0/tests/status/renames.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/status/renames.c 2015-03-24 16:10:45.000000000 +0000 @@ -20,40 +20,36 @@ cl_git_sandbox_cleanup(); } -static void rename_file(git_repository *repo, const char *oldname, const char *newname) +static void _rename_helper( + git_repository *repo, const char *from, const char *to, const char *extra) { git_buf oldpath = GIT_BUF_INIT, newpath = GIT_BUF_INIT; - git_buf_joinpath(&oldpath, git_repository_workdir(repo), oldname); - git_buf_joinpath(&newpath, git_repository_workdir(repo), newname); + cl_git_pass(git_buf_joinpath( + &oldpath, git_repository_workdir(repo), from)); + cl_git_pass(git_buf_joinpath( + &newpath, git_repository_workdir(repo), to)); cl_git_pass(p_rename(oldpath.ptr, newpath.ptr)); - git_buf_free(&oldpath); - git_buf_free(&newpath); -} - -static void rename_and_edit_file(git_repository *repo, const char *oldname, const char *newname) -{ - git_buf oldpath = GIT_BUF_INIT, newpath = GIT_BUF_INIT; - - git_buf_joinpath(&oldpath, git_repository_workdir(repo), oldname); - git_buf_joinpath(&newpath, git_repository_workdir(repo), newname); - - cl_git_pass(p_rename(oldpath.ptr, newpath.ptr)); - cl_git_append2file(newpath.ptr, "Added at the end to keep similarity!"); + if (extra) + cl_git_append2file(newpath.ptr, extra); git_buf_free(&oldpath); git_buf_free(&newpath); } +#define rename_file(R,O,N) _rename_helper((R), (O), (N), NULL) +#define rename_and_edit_file(R,O,N) \ + _rename_helper((R), (O), (N), "Added at the end to keep similarity!") + struct status_entry { git_status_t status; const char *oldname; const char *newname; }; -static void test_status( +static void check_status( git_status_list *status_list, struct status_entry *expected_list, size_t expected_len) @@ -61,9 +57,9 @@ const git_status_entry *actual; const struct status_entry *expected; const char *oldname, *newname; - size_t i; + size_t i, files_in_status = git_status_list_entrycount(status_list); - cl_assert_equal_sz(expected_len, git_status_list_entrycount(status_list)); + cl_assert_equal_sz(expected_len, files_in_status); for (i = 0; i < expected_len; i++) { actual = git_status_byindex(status_list, i); @@ -82,10 +78,12 @@ else cl_assert(expected->oldname == NULL); - if (newname) - cl_assert(git__strcmp(newname, expected->newname) == 0); - else - cl_assert(expected->newname == NULL); + if (actual->status & (GIT_STATUS_INDEX_RENAMED|GIT_STATUS_WT_RENAMED)) { + if (newname) + cl_assert(git__strcmp(newname, expected->newname) == 0); + else + cl_assert(expected->newname == NULL); + } } } @@ -109,7 +107,7 @@ cl_git_pass(git_index_write(index)); cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); - test_status(statuslist, expected, 1); + check_status(statuslist, expected, 1); git_status_list_free(statuslist); git_index_free(index); @@ -149,7 +147,7 @@ cl_git_pass(git_index_write(index)); cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); - test_status(statuslist, expected, 4); + check_status(statuslist, expected, 4); git_status_list_free(statuslist); git_index_free(index); @@ -178,7 +176,7 @@ cl_git_pass(git_index_write(index)); cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); - test_status(statuslist, expected, 2); + check_status(statuslist, expected, 2); git_status_list_free(statuslist); git_index_free(index); @@ -208,7 +206,7 @@ cl_git_pass(git_index_write(index)); cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); - test_status(statuslist, expected, 2); + check_status(statuslist, expected, 2); git_status_list_free(statuslist); git_index_free(index); @@ -228,7 +226,7 @@ rename_file(g_repo, "ikeepsix.txt", "newname.txt"); cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); - test_status(statuslist, expected, 1); + check_status(statuslist, expected, 1); git_status_list_free(statuslist); } @@ -254,7 +252,7 @@ rename_and_edit_file(g_repo, "untimely.txt", "bbb.txt"); cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); - test_status(statuslist, expected, 4); + check_status(statuslist, expected, 4); git_status_list_free(statuslist); } @@ -278,7 +276,7 @@ rename_file(g_repo, "_temp_.txt", "sixserving.txt"); cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); - test_status(statuslist, expected, 2); + check_status(statuslist, expected, 2); git_status_list_free(statuslist); git_index_free(index); @@ -309,7 +307,7 @@ rename_file(g_repo, "newname-index.txt", "newname-workdir.txt"); cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); - test_status(statuslist, expected, 1); + check_status(statuslist, expected, 1); git_status_list_free(statuslist); git_index_free(index); @@ -355,7 +353,7 @@ rename_file(g_repo, "untimely-index.txt", "untimely-both.txt"); cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); - test_status(statuslist, expected, 4); + check_status(statuslist, expected, 4); git_status_list_free(statuslist); git_index_free(index); @@ -399,7 +397,7 @@ rename_file(g_repo, "_temp_.txt", "sixserving.txt"); cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); - test_status(statuslist, expected, 3); + check_status(statuslist, expected, 3); git_status_list_free(statuslist); git_index_free(index); @@ -440,7 +438,7 @@ "This is enough content for the file to be rewritten.\n"); cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); - test_status(statuslist, expected, 1); + check_status(statuslist, expected, 1); git_status_list_free(statuslist); git_index_free(index); @@ -481,7 +479,7 @@ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); - test_status(statuslist, (index_caps & GIT_INDEXCAP_IGNORE_CASE) ? + check_status(statuslist, (index_caps & GIT_INDEXCAP_IGNORE_CASE) ? expected_icase : expected_case, 1); git_status_list_free(statuslist); @@ -548,10 +546,166 @@ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); - test_status(statuslist, (index_caps & GIT_INDEXCAP_IGNORE_CASE) ? + check_status(statuslist, (index_caps & GIT_INDEXCAP_IGNORE_CASE) ? expected_icase : expected_case, 4); git_status_list_free(statuslist); git_index_free(index); } + +void test_status_renames__zero_byte_file_does_not_fail(void) +{ + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + + struct status_entry expected[] = { + { GIT_STATUS_WT_DELETED, "ikeepsix.txt", "ikeepsix.txt" }, + { GIT_STATUS_WT_NEW, "zerobyte.txt", "zerobyte.txt" }, + }; + + opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES | + GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX | + GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR | + GIT_STATUS_OPT_INCLUDE_IGNORED | + GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS | + GIT_STATUS_SHOW_INDEX_AND_WORKDIR | + GIT_STATUS_OPT_RECURSE_IGNORED_DIRS; + + p_unlink("renames/ikeepsix.txt"); + cl_git_mkfile("renames/zerobyte.txt", ""); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + check_status(statuslist, expected, 2); + git_status_list_free(statuslist); +} + +#ifdef GIT_USE_ICONV +static char *nfc = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D"; +static char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D"; +#endif + +void test_status_renames__precomposed_unicode_rename(void) +{ +#ifdef GIT_USE_ICONV + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected0[] = { + { GIT_STATUS_WT_NEW, nfd, NULL }, + { GIT_STATUS_WT_DELETED, "sixserving.txt", NULL }, + }; + struct status_entry expected1[] = { + { GIT_STATUS_WT_RENAMED, "sixserving.txt", nfd }, + }; + struct status_entry expected2[] = { + { GIT_STATUS_WT_DELETED, "sixserving.txt", NULL }, + { GIT_STATUS_WT_NEW, nfc, NULL }, + }; + struct status_entry expected3[] = { + { GIT_STATUS_WT_RENAMED, "sixserving.txt", nfc }, + }; + + rename_file(g_repo, "sixserving.txt", nfc); + + opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED; + + cl_repo_set_bool(g_repo, "core.precomposeunicode", false); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + check_status(statuslist, expected0, ARRAY_SIZE(expected0)); + git_status_list_free(statuslist); + + opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + check_status(statuslist, expected1, ARRAY_SIZE(expected1)); + git_status_list_free(statuslist); + + cl_repo_set_bool(g_repo, "core.precomposeunicode", true); + + opts.flags &= ~GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + check_status(statuslist, expected2, ARRAY_SIZE(expected2)); + git_status_list_free(statuslist); + + opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + check_status(statuslist, expected3, ARRAY_SIZE(expected3)); + git_status_list_free(statuslist); +#endif +} + +void test_status_renames__precomposed_unicode_toggle_is_rename(void) +{ +#ifdef GIT_USE_ICONV + git_status_list *statuslist; + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + struct status_entry expected0[] = { + { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", nfd }, + }; + struct status_entry expected1[] = { + { GIT_STATUS_WT_RENAMED, nfd, nfc }, + }; + struct status_entry expected2[] = { + { GIT_STATUS_INDEX_RENAMED, nfd, nfc }, + }; + struct status_entry expected3[] = { + { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, nfd, nfd }, + }; + + cl_repo_set_bool(g_repo, "core.precomposeunicode", false); + rename_file(g_repo, "ikeepsix.txt", nfd); + + { + git_index *index; + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt")); + cl_git_pass(git_index_add_bypath(index, nfd)); + cl_git_pass(git_index_write(index)); + git_index_free(index); + } + + opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX | + GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR; + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + check_status(statuslist, expected0, ARRAY_SIZE(expected0)); + git_status_list_free(statuslist); + + cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "commit nfd"); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + cl_assert_equal_sz(0, git_status_list_entrycount(statuslist)); + git_status_list_free(statuslist); + + cl_repo_set_bool(g_repo, "core.precomposeunicode", true); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + check_status(statuslist, expected1, ARRAY_SIZE(expected1)); + git_status_list_free(statuslist); + + { + git_index *index; + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_index_remove_bypath(index, nfd)); + cl_git_pass(git_index_add_bypath(index, nfc)); + cl_git_pass(git_index_write(index)); + git_index_free(index); + } + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + check_status(statuslist, expected2, ARRAY_SIZE(expected2)); + git_status_list_free(statuslist); + + cl_repo_set_bool(g_repo, "core.precomposeunicode", false); + + cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts)); + check_status(statuslist, expected3, ARRAY_SIZE(expected3)); + git_status_list_free(statuslist); +#endif +} + diff -Nru libgit2-0.20.0/tests/status/single.c libgit2-0.22.2/tests/status/single.c --- libgit2-0.20.0/tests/status/single.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/status/single.c 2015-03-24 16:10:45.000000000 +0000 @@ -22,7 +22,7 @@ cl_set_cleanup(&cleanup__remove_file, (void *)file_name); cl_git_pass(git_odb_hashfile(&actual_id, file_name, GIT_OBJ_BLOB)); - cl_assert(git_oid_cmp(&expected_id, &actual_id) == 0); + cl_assert_equal_oid(&expected_id, &actual_id); } /* test retrieving OID from an empty file apart from the ODB */ @@ -40,6 +40,6 @@ cl_set_cleanup(&cleanup__remove_file, (void *)file_name); cl_git_pass(git_odb_hashfile(&actual_id, file_name, GIT_OBJ_BLOB)); - cl_assert(git_oid_cmp(&expected_id, &actual_id) == 0); + cl_assert_equal_oid(&expected_id, &actual_id); } diff -Nru libgit2-0.20.0/tests/status/status_helpers.c libgit2-0.22.2/tests/status/status_helpers.c --- libgit2-0.20.0/tests/status/status_helpers.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/status/status_helpers.c 2015-03-24 16:10:45.000000000 +0000 @@ -9,20 +9,13 @@ if (counts->debug) cb_status__print(path, status_flags, NULL); - if (counts->entry_count >= counts->expected_entry_count) { + if (counts->entry_count >= counts->expected_entry_count) counts->wrong_status_flags_count++; - goto exit; - } - - if (strcmp(path, counts->expected_paths[counts->entry_count])) { + else if (strcmp(path, counts->expected_paths[counts->entry_count])) counts->wrong_sorted_path++; - goto exit; - } - - if (status_flags != counts->expected_statuses[counts->entry_count]) + else if (status_flags != counts->expected_statuses[counts->entry_count]) counts->wrong_status_flags_count++; -exit: counts->entry_count++; return 0; } @@ -89,6 +82,9 @@ if (status_flags & GIT_STATUS_IGNORED) { wstatus = 'I'; wcount++; } + if (status_flags & GIT_STATUS_WT_UNREADABLE) { + wstatus = 'X'; wcount++; + } fprintf(stderr, "%c%c %s (%d/%d%s)\n", istatus, wstatus, path, icount, wcount, diff -Nru libgit2-0.20.0/tests/status/status_helpers.h libgit2-0.22.2/tests/status/status_helpers.h --- libgit2-0.20.0/tests/status/status_helpers.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/status/status_helpers.h 2015-03-24 16:10:45.000000000 +0000 @@ -8,9 +8,19 @@ const unsigned int* expected_statuses; const char** expected_paths; int expected_entry_count; + const char *file; + int line; bool debug; } status_entry_counts; +#define status_counts_init(counts, paths, statuses) do { \ + memset(&(counts), 0, sizeof(counts)); \ + (counts).expected_statuses = (statuses); \ + (counts).expected_paths = (paths); \ + (counts).file = __FILE__; \ + (counts).line = __LINE__; \ + } while (0) + /* cb_status__normal takes payload of "status_entry_counts *" */ extern int cb_status__normal( diff -Nru libgit2-0.20.0/tests/status/submodules.c libgit2-0.22.2/tests/status/submodules.c --- libgit2-0.20.0/tests/status/submodules.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/status/submodules.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,7 +1,5 @@ #include "clar_libgit2.h" -#include "buffer.h" -#include "path.h" -#include "posix.h" +#include "fileops.h" #include "status_helpers.h" #include "../submodule/submodule_helpers.h" @@ -29,6 +27,7 @@ cl_assert(sm != NULL); cl_assert_equal_s("testrepo", git_submodule_name(sm)); cl_assert_equal_s("testrepo", git_submodule_path(sm)); + git_submodule_free(sm); } void test_status_submodules__0(void) @@ -71,8 +70,15 @@ status_entry_counts *counts = payload; int idx = counts->entry_count++; - cl_assert_equal_s(counts->expected_paths[idx], p); - cl_assert(counts->expected_statuses[idx] == s); + clar__assert_equal( + counts->file, counts->line, + "Status path mismatch", 1, + "%s", counts->expected_paths[idx], p); + + clar__assert_equal( + counts->file, counts->line, + "Status code mismatch", 1, + "%o", counts->expected_statuses[idx], s); return 0; } @@ -87,13 +93,9 @@ cl_assert(git_path_isdir("submodules/testrepo/.git")); cl_assert(git_path_isfile("submodules/.gitmodules")); - memset(&counts, 0, sizeof(counts)); - counts.expected_paths = expected_files; - counts.expected_statuses = expected_status; + status_counts_init(counts, expected_files, expected_status); - cl_git_pass( - git_status_foreach(g_repo, cb_status__match, &counts) - ); + cl_git_pass( git_status_foreach(g_repo, cb_status__match, &counts) ); cl_assert_equal_i(6, counts.entry_count); } @@ -136,32 +138,28 @@ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); cl_git_pass(git_submodule_open(&smrepo, sm)); + git_submodule_free(sm); /* move submodule HEAD to c47800c7266a2be04c571c04d5a6614691ea99bd */ cl_git_pass( git_oid_fromstr(&oid, "c47800c7266a2be04c571c04d5a6614691ea99bd")); - cl_git_pass(git_repository_set_head_detached(smrepo, &oid)); + cl_git_pass(git_repository_set_head_detached(smrepo, &oid, NULL, NULL)); /* first do a normal status, which should now include the submodule */ - memset(&counts, 0, sizeof(counts)); - counts.expected_paths = expected_files_with_sub; - counts.expected_statuses = expected_status_with_sub; - opts.flags = GIT_STATUS_OPT_DEFAULTS; + status_counts_init( + counts, expected_files_with_sub, expected_status_with_sub); cl_git_pass( git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts)); cl_assert_equal_i(7, counts.entry_count); /* try again with EXCLUDE_SUBMODULES which should skip it */ - memset(&counts, 0, sizeof(counts)); - counts.expected_paths = expected_files; - counts.expected_statuses = expected_status; - opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_EXCLUDE_SUBMODULES; + status_counts_init(counts, expected_files, expected_status); cl_git_pass( git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts)); cl_assert_equal_i(6, counts.entry_count); @@ -199,25 +197,330 @@ /* first do a normal status, which should now include the submodule */ - memset(&counts, 0, sizeof(counts)); - counts.expected_paths = expected_files_with_sub; - counts.expected_statuses = expected_status_with_sub; - opts.flags = GIT_STATUS_OPT_DEFAULTS; + status_counts_init( + counts, expected_files_with_sub, expected_status_with_sub); cl_git_pass( git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts)); cl_assert_equal_i(7, counts.entry_count); /* try again with EXCLUDE_SUBMODULES which should skip it */ - memset(&counts, 0, sizeof(counts)); - counts.expected_paths = expected_files; - counts.expected_statuses = expected_status; - opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_EXCLUDE_SUBMODULES; + status_counts_init(counts, expected_files, expected_status); cl_git_pass( git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts)); cl_assert_equal_i(6, counts.entry_count); } + +void test_status_submodules__uninitialized(void) +{ + git_repository *cloned_repo; + git_status_list *statuslist; + + g_repo = cl_git_sandbox_init("submod2"); + + cl_git_pass(git_clone(&cloned_repo, "submod2", "submod2-clone", NULL)); + + cl_git_pass(git_status_list_new(&statuslist, cloned_repo, NULL)); + cl_assert_equal_i(0, git_status_list_entrycount(statuslist)); + + git_status_list_free(statuslist); + git_repository_free(cloned_repo); + cl_git_sandbox_cleanup(); +} + +void test_status_submodules__contained_untracked_repo(void) +{ + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + status_entry_counts counts; + git_repository *contained; + static const char *expected_files_not_ignored[] = { + ".gitmodules", + "added", + "deleted", + "modified", + "untracked" + }; + static unsigned int expected_status_not_ignored[] = { + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_INDEX_NEW, + GIT_STATUS_INDEX_DELETED, + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_WT_NEW, + }; + static const char *expected_files_with_untracked[] = { + ".gitmodules", + "added", + "deleted", + "dir/file.md", + "modified", + "untracked" + }; + static const char *expected_files_with_untracked_dir[] = { + ".gitmodules", + "added", + "deleted", + "dir/", + "modified", + "untracked" + }; + static unsigned int expected_status_with_untracked[] = { + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_INDEX_NEW, + GIT_STATUS_INDEX_DELETED, + GIT_STATUS_WT_NEW, + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_WT_NEW + }; + + g_repo = setup_fixture_submodules(); + + /* skip empty directory */ + + cl_must_pass(p_mkdir("submodules/dir", 0777)); + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED; + + status_counts_init( + counts, expected_files_not_ignored, expected_status_not_ignored); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__match, &counts)); + cl_assert_equal_i(5, counts.entry_count); + + /* still skipping because empty == ignored */ + + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; + + status_counts_init( + counts, expected_files_not_ignored, expected_status_not_ignored); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__match, &counts)); + cl_assert_equal_i(5, counts.entry_count); + + /* find non-ignored contents of directory */ + + cl_git_mkfile("submodules/dir/file.md", "hello"); + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; + + status_counts_init( + counts, expected_files_with_untracked, expected_status_with_untracked); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__match, &counts)); + cl_assert_equal_i(6, counts.entry_count); + + /* but skip if all content is ignored */ + + cl_git_append2file("submodules/.git/info/exclude", "\n*.md\n\n"); + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; + + status_counts_init( + counts, expected_files_not_ignored, expected_status_not_ignored); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__match, &counts)); + cl_assert_equal_i(5, counts.entry_count); + + /* same is true if it contains a git link */ + + cl_git_mkfile("submodules/dir/.git", "gitlink: ../.git"); + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; + + status_counts_init( + counts, expected_files_not_ignored, expected_status_not_ignored); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__match, &counts)); + cl_assert_equal_i(5, counts.entry_count); + + /* but if it contains tracked files, it should just show up as a + * directory and exclude the files in it + */ + + cl_git_mkfile("submodules/dir/another_file", "hello"); + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; + + status_counts_init( + counts, expected_files_with_untracked_dir, + expected_status_with_untracked); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__match, &counts)); + cl_assert_equal_i(6, counts.entry_count); + + /* that applies to a git repo with a .git directory too */ + + cl_must_pass(p_unlink("submodules/dir/.git")); + cl_git_pass(git_repository_init(&contained, "submodules/dir", false)); + git_repository_free(contained); + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS; + + status_counts_init( + counts, expected_files_with_untracked_dir, + expected_status_with_untracked); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__match, &counts)); + cl_assert_equal_i(6, counts.entry_count); + + /* same result even if we don't recurse into subdirectories */ + + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED; + + status_counts_init( + counts, expected_files_with_untracked_dir, + expected_status_with_untracked); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__match, &counts)); + cl_assert_equal_i(6, counts.entry_count); + + /* and if we remove the untracked file, it goes back to ignored */ + + cl_must_pass(p_unlink("submodules/dir/another_file")); + + status_counts_init( + counts, expected_files_not_ignored, expected_status_not_ignored); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__match, &counts)); + cl_assert_equal_i(5, counts.entry_count); +} + +void test_status_submodules__broken_stuff_that_git_allows(void) +{ + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + status_entry_counts counts; + git_repository *contained; + static const char *expected_files_with_broken[] = { + ".gitmodules", + "added", + "broken/tracked", + "deleted", + "ignored", + "modified", + "untracked" + }; + static unsigned int expected_status_with_broken[] = { + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_INDEX_NEW, + GIT_STATUS_INDEX_NEW, + GIT_STATUS_INDEX_DELETED, + GIT_STATUS_IGNORED, + GIT_STATUS_WT_MODIFIED, + GIT_STATUS_WT_NEW, + }; + + g_repo = setup_fixture_submodules(); + + opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | + GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS | + GIT_STATUS_OPT_INCLUDE_IGNORED; + + /* make a directory and stick a tracked item into the index */ + { + git_index *idx; + cl_must_pass(p_mkdir("submodules/broken", 0777)); + cl_git_mkfile("submodules/broken/tracked", "tracked content"); + cl_git_pass(git_repository_index(&idx, g_repo)); + cl_git_pass(git_index_add_bypath(idx, "broken/tracked")); + cl_git_pass(git_index_write(idx)); + git_index_free(idx); + } + + status_counts_init( + counts, expected_files_with_broken, expected_status_with_broken); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__match, &counts)); + cl_assert_equal_i(7, counts.entry_count); + + /* directory with tracked items that looks a little bit like a repo */ + + cl_must_pass(p_mkdir("submodules/broken/.git", 0777)); + cl_must_pass(p_mkdir("submodules/broken/.git/info", 0777)); + cl_git_mkfile("submodules/broken/.git/info/exclude", "# bogus"); + + status_counts_init( + counts, expected_files_with_broken, expected_status_with_broken); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__match, &counts)); + cl_assert_equal_i(7, counts.entry_count); + + /* directory with tracked items that is a repo */ + + cl_git_pass(git_futils_rmdir_r( + "submodules/broken/.git", NULL, GIT_RMDIR_REMOVE_FILES)); + cl_git_pass(git_repository_init(&contained, "submodules/broken", false)); + git_repository_free(contained); + + status_counts_init( + counts, expected_files_with_broken, expected_status_with_broken); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__match, &counts)); + cl_assert_equal_i(7, counts.entry_count); + + /* directory with tracked items that claims to be a submodule but is not */ + + cl_git_pass(git_futils_rmdir_r( + "submodules/broken/.git", NULL, GIT_RMDIR_REMOVE_FILES)); + cl_git_append2file("submodules/.gitmodules", + "\n[submodule \"broken\"]\n" + "\tpath = broken\n" + "\turl = https://github.com/not/used\n\n"); + + status_counts_init( + counts, expected_files_with_broken, expected_status_with_broken); + cl_git_pass(git_status_foreach_ext( + g_repo, &opts, cb_status__match, &counts)); + cl_assert_equal_i(7, counts.entry_count); +} + +void test_status_submodules__entry_but_dir_tracked(void) +{ + git_repository *repo; + git_status_list *status; + git_diff *diff; + git_index *index; + git_tree *tree; + + cl_git_pass(git_repository_init(&repo, "mixed-submodule", 0)); + cl_git_mkfile("mixed-submodule/.gitmodules", "[submodule \"sub\"]\n path = sub\n url = ../foo\n"); + cl_git_pass(p_mkdir("mixed-submodule/sub", 0777)); + cl_git_mkfile("mixed-submodule/sub/file", ""); + + /* Create the commit with sub/file as a file, and an entry for sub in the modules list */ + { + git_oid tree_id, commit_id; + git_signature *sig; + git_reference *ref; + + cl_git_pass(git_repository_index(&index, repo)); + cl_git_pass(git_index_add_bypath(index, ".gitmodules")); + cl_git_pass(git_index_add_bypath(index, "sub/file")); + cl_git_pass(git_index_write(index)); + cl_git_pass(git_index_write_tree(&tree_id, index)); + cl_git_pass(git_signature_now(&sig, "Sloppy Submoduler", "sloppy@example.com")); + cl_git_pass(git_tree_lookup(&tree, repo, &tree_id)); + cl_git_pass(git_commit_create(&commit_id, repo, NULL, sig, sig, NULL, "message", tree, 0, NULL)); + cl_git_pass(git_reference_create(&ref, repo, "refs/heads/master", &commit_id, 1, sig, "commit: foo")); + git_reference_free(ref); + git_signature_free(sig); + } + + cl_git_pass(git_diff_tree_to_index(&diff, repo, tree, index, NULL)); + cl_assert_equal_i(0, git_diff_num_deltas(diff)); + git_diff_free(diff); + + cl_git_pass(git_diff_index_to_workdir(&diff, repo, index, NULL)); + cl_assert_equal_i(0, git_diff_num_deltas(diff)); + git_diff_free(diff); + + cl_git_pass(git_status_list_new(&status, repo, NULL)); + cl_assert_equal_i(0, git_status_list_entrycount(status)); + + git_status_list_free(status); + git_index_free(index); + git_tree_free(tree); + git_repository_free(repo); +} diff -Nru libgit2-0.20.0/tests/status/worktree.c libgit2-0.22.2/tests/status/worktree.c --- libgit2-0.20.0/tests/status/worktree.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/status/worktree.c 2015-03-24 16:10:45.000000000 +0000 @@ -5,6 +5,8 @@ #include "posix.h" #include "util.h" #include "path.h" +#include "../diff/diff_helpers.h" +#include "git2/sys/diff.h" /** * Cleanup @@ -40,11 +42,15 @@ cl_assert_equal_i(0, counts.wrong_sorted_path); } -void assert_show(const int entry_counts, const char *entry_paths[], - const unsigned int entry_statuses[], git_status_show_t show) +void assert_show( + const int entry_counts, + const char *entry_paths[], + const unsigned int entry_statuses[], + git_repository *repo, + git_status_show_t show, + unsigned int extra_flags) { status_entry_counts counts; - git_repository *repo = cl_git_sandbox_init("status"); git_status_options opts = GIT_STATUS_OPTIONS_INIT; memset(&counts, 0x0, sizeof(status_entry_counts)); @@ -52,7 +58,7 @@ counts.expected_paths = entry_paths; counts.expected_statuses = entry_statuses; - opts.flags = GIT_STATUS_OPT_DEFAULTS; + opts.flags = GIT_STATUS_OPT_DEFAULTS | extra_flags; opts.show = show; cl_git_pass( @@ -67,19 +73,19 @@ void test_status_worktree__show_index_and_workdir(void) { assert_show(entry_count0, entry_paths0, entry_statuses0, - GIT_STATUS_SHOW_INDEX_AND_WORKDIR); + cl_git_sandbox_init("status"), GIT_STATUS_SHOW_INDEX_AND_WORKDIR, 0); } void test_status_worktree__show_index_only(void) { assert_show(entry_count5, entry_paths5, entry_statuses5, - GIT_STATUS_SHOW_INDEX_ONLY); + cl_git_sandbox_init("status"), GIT_STATUS_SHOW_INDEX_ONLY, 0); } void test_status_worktree__show_workdir_only(void) { assert_show(entry_count6, entry_paths6, entry_statuses6, - GIT_STATUS_SHOW_WORKDIR_ONLY); + cl_git_sandbox_init("status"), GIT_STATUS_SHOW_WORKDIR_ONLY, 0); } /* this test is equivalent to t18-status.c:statuscb1 */ @@ -455,15 +461,15 @@ memset(&their_entry, 0x0, sizeof(git_index_entry)); ancestor_entry.path = "modified_file"; - git_oid_fromstr(&ancestor_entry.oid, + git_oid_fromstr(&ancestor_entry.id, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); our_entry.path = "modified_file"; - git_oid_fromstr(&our_entry.oid, + git_oid_fromstr(&our_entry.id, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); their_entry.path = "modified_file"; - git_oid_fromstr(&their_entry.oid, + git_oid_fromstr(&their_entry.id, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); cl_git_pass(git_status_file(&status, repo, "modified_file")); @@ -552,7 +558,7 @@ (*count)++; - return (*count == 8); + return (*count == 8) ? -111 : 0; } void test_status_worktree__interruptable_foreach(void) @@ -561,7 +567,7 @@ git_repository *repo = cl_git_sandbox_init("status"); cl_assert_equal_i( - GIT_EUSER, git_status_foreach(repo, cb_status__interrupt, &count) + -111, git_status_foreach(repo, cb_status__interrupt, &count) ); cl_assert_equal_i(8, count); @@ -578,7 +584,11 @@ cl_git_pass(git_status_file(&status, repo, "current_file")); - cl_assert_equal_i(GIT_STATUS_CURRENT, status); + /* stat data on file should no longer match stat cache, even though + * file diff will be empty because of line-ending conversion - matches + * the Git command-line behavior here. + */ + cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status); } void test_status_worktree__line_endings_dont_count_as_changes_with_autocrlf_issue_1397(void) @@ -605,15 +615,15 @@ memset(&their_entry, 0x0, sizeof(git_index_entry)); ancestor_entry.path = "modified_file"; - git_oid_fromstr(&ancestor_entry.oid, + git_oid_fromstr(&ancestor_entry.id, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); our_entry.path = "modified_file"; - git_oid_fromstr(&our_entry.oid, + git_oid_fromstr(&our_entry.id, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); their_entry.path = "modified_file"; - git_oid_fromstr(&their_entry.oid, + git_oid_fromstr(&their_entry.id, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a"); cl_git_pass(git_status_file(&status, repo, "modified_file")); @@ -871,5 +881,160 @@ 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); +} + +/* The update stat cache tests mostly just mirror other tests and try + * to make sure that updating the stat cache doesn't change the results + * while reducing the amount of work that needs to be done + */ + +static void check_status0(git_status_list *status) +{ + size_t i, max_i = git_status_list_entrycount(status); + cl_assert_equal_sz(entry_count0, max_i); + for (i = 0; i < max_i; ++i) { + const git_status_entry *entry = git_status_byindex(status, i); + cl_assert_equal_i(entry_statuses0[i], entry->status); + } +} + +void test_status_worktree__update_stat_cache_0(void) +{ + git_repository *repo = cl_git_sandbox_init("status"); + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + git_status_list *status; + git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT; + + opts.flags = GIT_STATUS_OPT_DEFAULTS; + + cl_git_pass(git_status_list_new(&status, repo, &opts)); + check_status0(status); + cl_git_pass(git_status_list_get_perfdata(&perf, status)); + cl_assert_equal_sz(13 + 3, perf.stat_calls); + cl_assert_equal_sz(5, perf.oid_calculations); + + git_status_list_free(status); + + opts.flags |= GIT_STATUS_OPT_UPDATE_INDEX; + + cl_git_pass(git_status_list_new(&status, repo, &opts)); + check_status0(status); + cl_git_pass(git_status_list_get_perfdata(&perf, status)); + cl_assert_equal_sz(13 + 3, perf.stat_calls); + cl_assert_equal_sz(5, perf.oid_calculations); + + git_status_list_free(status); + + opts.flags &= ~GIT_STATUS_OPT_UPDATE_INDEX; + + cl_git_pass(git_status_list_new(&status, repo, &opts)); + check_status0(status); + cl_git_pass(git_status_list_get_perfdata(&perf, status)); + cl_assert_equal_sz(13 + 3, perf.stat_calls); + cl_assert_equal_sz(0, perf.oid_calculations); + + git_status_list_free(status); +} + +void test_status_worktree__unreadable(void) +{ +#ifndef GIT_WIN32 + const char *expected_paths[] = { "no_permission/foo" }; + const unsigned int expected_statuses[] = {GIT_STATUS_WT_UNREADABLE}; + + git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + status_entry_counts counts = {0}; + + /* Create directory with no read permission */ + cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", NULL, 0777)); + cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy"); + p_chmod("empty_standard_repo/no_permission", 0644); + + counts.expected_entry_count = 1; + counts.expected_paths = expected_paths; + counts.expected_statuses = expected_statuses; + + opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY; + opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_INCLUDE_UNREADABLE; + + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) ); + + /* Restore permissions so we can cleanup :) */ + p_chmod("empty_standard_repo/no_permission", 0777); + + 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); +#endif +} + +void test_status_worktree__unreadable_not_included(void) +{ +#ifndef GIT_WIN32 + const char *expected_paths[] = { "no_permission/" }; + const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW}; + + git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + status_entry_counts counts = {0}; + + /* Create directory with no read permission */ + cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", NULL, 0777)); + cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy"); + p_chmod("empty_standard_repo/no_permission", 0644); + + counts.expected_entry_count = 1; + counts.expected_paths = expected_paths; + counts.expected_statuses = expected_statuses; + + opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY; + opts.flags = (GIT_STATUS_OPT_INCLUDE_IGNORED | GIT_STATUS_OPT_INCLUDE_UNTRACKED); + + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) ); + + /* Restore permissions so we can cleanup :) */ + p_chmod("empty_standard_repo/no_permission", 0777); + + 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); +#endif +} + +void test_status_worktree__unreadable_as_untracked(void) +{ + const char *expected_paths[] = { "no_permission/foo" }; + const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW}; + + git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + status_entry_counts counts = {0}; + + /* Create directory with no read permission */ + cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", NULL, 0777)); + cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy"); + p_chmod("empty_standard_repo/no_permission", 0644); + + counts.expected_entry_count = 1; + counts.expected_paths = expected_paths; + counts.expected_statuses = expected_statuses; + + opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY; + opts.flags = GIT_STATUS_OPT_DEFAULTS | + GIT_STATUS_OPT_INCLUDE_UNREADABLE | + GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED; + + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) ); + + /* Restore permissions so we can cleanup :) */ + p_chmod("empty_standard_repo/no_permission", 0777); + + 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); } diff -Nru libgit2-0.20.0/tests/status/worktree_init.c libgit2-0.22.2/tests/status/worktree_init.c --- libgit2-0.20.0/tests/status/worktree_init.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/status/worktree_init.c 2015-03-24 16:10:45.000000000 +0000 @@ -127,7 +127,6 @@ git_index *index; status_entry_single result; unsigned int status_flags; - int error; #define FILE_WITH_BRACKET "LICENSE[1].md" #define FILE_WITHOUT_BRACKET "LICENSE1.md" @@ -195,9 +194,7 @@ cl_git_pass(git_status_file(&status_flags, repo, "LICENSE\\[1\\].md")); cl_assert(status_flags == GIT_STATUS_INDEX_NEW); - error = git_status_file(&status_flags, repo, FILE_WITH_BRACKET); - cl_git_fail(error); - cl_assert_equal_i(GIT_EAMBIGUOUS, error); + cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET)); git_index_free(index); git_repository_free(repo); diff -Nru libgit2-0.20.0/tests/stress/diff.c libgit2-0.22.2/tests/stress/diff.c --- libgit2-0.20.0/tests/stress/diff.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/stress/diff.c 2015-03-24 16:10:45.000000000 +0000 @@ -97,14 +97,14 @@ cl_git_pass(git_repository_index(&index, g_repo)); for (i = 0; i < 100; i += 1) { - snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i); + p_snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i); for (j = i * 256; j > 0; --j) git_buf_printf(&b, "more content %d\n", i); cl_git_mkfile(tmp, b.ptr); } for (i = 0; i < 100; i += 1) { - snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i); + p_snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i); cl_git_pass(git_index_add_bypath(index, tmp + strlen("renames/"))); } @@ -128,15 +128,15 @@ git_buf_printf(&b, "%08d\n" ANOTHER_POEM "%08d\n" ANOTHER_POEM ANOTHER_POEM, 0, 0); for (i = 0; i < 2500; i += 1) { - snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i); - snprintf(b.ptr, 9, "%08d", i); + p_snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i); + p_snprintf(b.ptr, 9, "%08d", i); b.ptr[8] = '\n'; cl_git_mkfile(tmp, b.ptr); } git_buf_free(&b); for (i = 0; i < 2500; i += 1) { - snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i); + p_snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i); cl_git_pass(git_index_add_bypath(index, tmp + strlen("renames/"))); } diff -Nru libgit2-0.20.0/tests/structinit/structinit.c libgit2-0.22.2/tests/structinit/structinit.c --- libgit2-0.20.0/tests/structinit/structinit.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/structinit/structinit.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,133 @@ +#include "clar_libgit2.h" +#include +#include +#include +#include + +#define STRINGIFY(s) #s + +/* Checks two conditions for the specified structure: + * 1. That the initializers for the latest version produces the same + * in-memory representation. + * 2. That the function-based initializer supports all versions from 1...n, + * where n is the latest version (often represented by GIT_*_VERSION). + * + * Parameters: + * structname: The name of the structure to test, e.g. git_blame_options. + * structver: The latest version of the specified structure. + * macroinit: The macro that initializes the latest version of the structure. + * funcinitname: The function that initializes the structure. Must have the + * signature "int (structname* instance, int version)". + */ +#define CHECK_MACRO_FUNC_INIT_EQUAL(structname, structver, macroinit, funcinitname) \ +do { \ + structname structname##_macro_latest = macroinit; \ + structname structname##_func_latest; \ + int structname##_curr_ver = structver - 1; \ + cl_git_pass(funcinitname(&structname##_func_latest, structver)); \ + cl_check_( \ + memcmp(&structname##_macro_latest, &structname##_func_latest, \ + sizeof(structname)) == 0, \ + "Macro-based and function-based initializer for " STRINGIFY(structname) \ + " are not equivalent."); \ + \ + while (structname##_curr_ver > 0) \ + { \ + structname macro; \ + cl_git_pass(funcinitname(¯o, structname##_curr_ver)); \ + structname##_curr_ver--; \ + }\ +} while(0) + +void test_structinit_structinit__compare(void) +{ + /* blame */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_blame_options, GIT_BLAME_OPTIONS_VERSION, \ + GIT_BLAME_OPTIONS_INIT, git_blame_init_options); + + /* checkout */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_checkout_options, GIT_CHECKOUT_OPTIONS_VERSION, \ + GIT_CHECKOUT_OPTIONS_INIT, git_checkout_init_options); + + /* clone */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_clone_options, GIT_CLONE_OPTIONS_VERSION, \ + GIT_CLONE_OPTIONS_INIT, git_clone_init_options); + + /* diff */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_diff_options, GIT_DIFF_OPTIONS_VERSION, \ + GIT_DIFF_OPTIONS_INIT, git_diff_init_options); + + /* diff_find */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_diff_find_options, GIT_DIFF_FIND_OPTIONS_VERSION, \ + GIT_DIFF_FIND_OPTIONS_INIT, git_diff_find_init_options); + + /* merge_file_input */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_merge_file_input, GIT_MERGE_FILE_INPUT_VERSION, \ + GIT_MERGE_FILE_INPUT_INIT, git_merge_file_init_input); + + /* merge_file */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_merge_file_options, GIT_MERGE_FILE_OPTIONS_VERSION, \ + GIT_MERGE_FILE_OPTIONS_INIT, git_merge_file_init_options); + + /* merge_tree */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_merge_options, GIT_MERGE_OPTIONS_VERSION, \ + GIT_MERGE_OPTIONS_INIT, git_merge_init_options); + + /* push */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_push_options, GIT_PUSH_OPTIONS_VERSION, \ + GIT_PUSH_OPTIONS_INIT, git_push_init_options); + + /* remote */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_remote_callbacks, GIT_REMOTE_CALLBACKS_VERSION, \ + GIT_REMOTE_CALLBACKS_INIT, git_remote_init_callbacks); + + /* repository_init */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_repository_init_options, GIT_REPOSITORY_INIT_OPTIONS_VERSION, \ + GIT_REPOSITORY_INIT_OPTIONS_INIT, git_repository_init_init_options); + + /* revert */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_revert_options, GIT_REVERT_OPTIONS_VERSION, \ + GIT_REVERT_OPTIONS_INIT, git_revert_init_options); + + /* status */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_status_options, GIT_STATUS_OPTIONS_VERSION, \ + GIT_STATUS_OPTIONS_INIT, git_status_init_options); + + /* transport */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_transport, GIT_TRANSPORT_VERSION, \ + GIT_TRANSPORT_INIT, git_transport_init); + + /* config_backend */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_config_backend, GIT_CONFIG_BACKEND_VERSION, \ + GIT_CONFIG_BACKEND_INIT, git_config_init_backend); + + /* odb_backend */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_odb_backend, GIT_ODB_BACKEND_VERSION, \ + GIT_ODB_BACKEND_INIT, git_odb_init_backend); + + /* refdb_backend */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_refdb_backend, GIT_REFDB_BACKEND_VERSION, \ + GIT_REFDB_BACKEND_INIT, git_refdb_init_backend); + + /* submodule update */ + CHECK_MACRO_FUNC_INIT_EQUAL( \ + git_submodule_update_options, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, \ + GIT_SUBMODULE_UPDATE_OPTIONS_INIT, git_submodule_update_init_options); +} diff -Nru libgit2-0.20.0/tests/submodule/add.c libgit2-0.22.2/tests/submodule/add.c --- libgit2-0.20.0/tests/submodule/add.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/submodule/add.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,138 @@ +#include "clar_libgit2.h" +#include "posix.h" +#include "path.h" +#include "submodule_helpers.h" +#include "fileops.h" + +static git_repository *g_repo = NULL; + +void test_submodule_add__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +static void assert_submodule_url(const char* name, const char *url) +{ + git_config *cfg; + const char *s; + git_buf key = GIT_BUF_INIT; + + cl_git_pass(git_repository_config(&cfg, g_repo)); + + cl_git_pass(git_buf_printf(&key, "submodule.%s.url", name)); + cl_git_pass(git_config_get_string(&s, cfg, git_buf_cstr(&key))); + cl_assert_equal_s(s, url); + + git_config_free(cfg); + git_buf_free(&key); +} + +void test_submodule_add__url_absolute(void) +{ + git_submodule *sm; + git_config *cfg; + git_repository *repo; + const char *worktree_path; + git_buf dot_git_content = GIT_BUF_INIT; + + g_repo = setup_fixture_submod2(); + + /* re-add existing submodule */ + cl_git_fail_with( + GIT_EEXISTS, + git_submodule_add_setup(NULL, g_repo, "whatever", "sm_unchanged", 1)); + + /* add a submodule using a gitlink */ + + cl_git_pass( + git_submodule_add_setup(&sm, g_repo, "https://github.com/libgit2/libgit2.git", "sm_libgit2", 1) + ); + git_submodule_free(sm); + + cl_assert(git_path_isfile("submod2/" "sm_libgit2" "/.git")); + + cl_assert(git_path_isdir("submod2/.git/modules")); + cl_assert(git_path_isdir("submod2/.git/modules/" "sm_libgit2")); + cl_assert(git_path_isfile("submod2/.git/modules/" "sm_libgit2" "/HEAD")); + assert_submodule_url("sm_libgit2", "https://github.com/libgit2/libgit2.git"); + + cl_git_pass(git_repository_open(&repo, "submod2/" "sm_libgit2")); + + /* Verify worktree path is relative */ + cl_git_pass(git_repository_config(&cfg, repo)); + cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree")); + cl_assert_equal_s("../../../sm_libgit2/", worktree_path); + + /* Verify gitdir path is relative */ + cl_git_pass(git_futils_readbuffer(&dot_git_content, "submod2/" "sm_libgit2" "/.git")); + cl_assert_equal_s("gitdir: ../.git/modules/sm_libgit2/", dot_git_content.ptr); + + git_config_free(cfg); + git_repository_free(repo); + git_buf_free(&dot_git_content); + + /* add a submodule not using a gitlink */ + + cl_git_pass( + git_submodule_add_setup(&sm, g_repo, "https://github.com/libgit2/libgit2.git", "sm_libgit2b", 0) + ); + git_submodule_free(sm); + + cl_assert(git_path_isdir("submod2/" "sm_libgit2b" "/.git")); + cl_assert(git_path_isfile("submod2/" "sm_libgit2b" "/.git/HEAD")); + cl_assert(!git_path_exists("submod2/.git/modules/" "sm_libgit2b")); + assert_submodule_url("sm_libgit2b", "https://github.com/libgit2/libgit2.git"); +} + +void test_submodule_add__url_relative(void) +{ + git_submodule *sm; + git_remote *remote; + git_strarray problems = {0}; + + /* default remote url is https://github.com/libgit2/false.git */ + g_repo = cl_git_sandbox_init("testrepo2"); + + /* make sure we don't default to origin - rename origin -> test_remote */ + cl_git_pass(git_remote_rename(&problems, g_repo, "origin", "test_remote")); + cl_assert_equal_i(0, problems.count); + git_strarray_free(&problems); + cl_git_fail(git_remote_lookup(&remote, g_repo, "origin")); + + cl_git_pass( + git_submodule_add_setup(&sm, g_repo, "../TestGitRepository", "TestGitRepository", 1) + ); + git_submodule_free(sm); + + assert_submodule_url("TestGitRepository", "https://github.com/libgit2/TestGitRepository"); +} + +void test_submodule_add__url_relative_to_origin(void) +{ + git_submodule *sm; + + /* default remote url is https://github.com/libgit2/false.git */ + g_repo = cl_git_sandbox_init("testrepo2"); + + cl_git_pass( + git_submodule_add_setup(&sm, g_repo, "../TestGitRepository", "TestGitRepository", 1) + ); + git_submodule_free(sm); + + assert_submodule_url("TestGitRepository", "https://github.com/libgit2/TestGitRepository"); +} + +void test_submodule_add__url_relative_to_workdir(void) +{ + git_submodule *sm; + + /* In this repo, HEAD (master) has no remote tracking branc h*/ + g_repo = cl_git_sandbox_init("testrepo"); + + cl_git_pass( + git_submodule_add_setup(&sm, g_repo, "./", "TestGitRepository", 1) + ); + git_submodule_free(sm); + + assert_submodule_url("TestGitRepository", git_repository_workdir(g_repo)); +} diff -Nru libgit2-0.20.0/tests/submodule/init.c libgit2-0.22.2/tests/submodule/init.c --- libgit2-0.20.0/tests/submodule/init.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/submodule/init.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,115 @@ +#include "clar_libgit2.h" +#include "posix.h" +#include "path.h" +#include "submodule_helpers.h" +#include "fileops.h" + +static git_repository *g_repo = NULL; + +void test_submodule_init__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_submodule_init__absolute_url(void) +{ + git_submodule *sm; + git_config *cfg; + git_buf absolute_url = GIT_BUF_INIT; + const char *config_url; + + g_repo = setup_fixture_submodule_simple(); + + cl_assert(git_path_dirname_r(&absolute_url, git_repository_workdir(g_repo)) > 0); + cl_git_pass(git_buf_joinpath(&absolute_url, absolute_url.ptr, "testrepo.git")); + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); + + /* write the absolute url to the .gitmodules file*/ + cl_git_pass(git_submodule_set_url(sm, absolute_url.ptr)); + + /* verify that the .gitmodules is set with an absolute path*/ + cl_assert_equal_s(absolute_url.ptr, git_submodule_url(sm)); + + /* init and verify that absolute path is written to .git/config */ + cl_git_pass(git_submodule_init(sm, false)); + + cl_git_pass(git_repository_config(&cfg, g_repo)); + + git_config_get_string(&config_url, cfg, "submodule.testrepo.url"); + cl_assert_equal_s(absolute_url.ptr, config_url); + + git_buf_free(&absolute_url); + git_config_free(cfg); + git_submodule_free(sm); +} + +void test_submodule_init__relative_url(void) +{ + git_submodule *sm; + git_config *cfg; + git_buf absolute_url = GIT_BUF_INIT; + const char *config_url; + + g_repo = setup_fixture_submodule_simple(); + + cl_assert(git_path_dirname_r(&absolute_url, git_repository_workdir(g_repo)) > 0); + cl_git_pass(git_buf_joinpath(&absolute_url, absolute_url.ptr, "testrepo.git")); + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); + + /* verify that the .gitmodules is set with an absolute path*/ + cl_assert_equal_s("../testrepo.git", git_submodule_url(sm)); + + /* init and verify that absolute path is written to .git/config */ + cl_git_pass(git_submodule_init(sm, false)); + + cl_git_pass(git_repository_config(&cfg, g_repo)); + + git_config_get_string(&config_url, cfg, "submodule.testrepo.url"); + cl_assert_equal_s(absolute_url.ptr, config_url); + + git_buf_free(&absolute_url); + git_config_free(cfg); + git_submodule_free(sm); +} + +void test_submodule_init__relative_url_detached_head(void) +{ + git_submodule *sm; + git_config *cfg; + git_buf absolute_url = GIT_BUF_INIT; + const char *config_url; + git_reference *head_ref = NULL; + git_object *head_commit = NULL; + + g_repo = setup_fixture_submodule_simple(); + + /* Put the parent repository into a detached head state. */ + cl_git_pass(git_repository_head(&head_ref, g_repo)); + cl_git_pass(git_reference_peel(&head_commit, head_ref, GIT_OBJ_COMMIT)); + + cl_git_pass(git_repository_set_head_detached(g_repo, git_commit_id((git_commit *)head_commit), NULL, NULL)); + + cl_assert(git_path_dirname_r(&absolute_url, git_repository_workdir(g_repo)) > 0); + cl_git_pass(git_buf_joinpath(&absolute_url, absolute_url.ptr, "testrepo.git")); + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); + + /* verify that the .gitmodules is set with an absolute path*/ + cl_assert_equal_s("../testrepo.git", git_submodule_url(sm)); + + /* init and verify that absolute path is written to .git/config */ + cl_git_pass(git_submodule_init(sm, false)); + + cl_git_pass(git_repository_config(&cfg, g_repo)); + + git_config_get_string(&config_url, cfg, "submodule.testrepo.url"); + cl_assert_equal_s(absolute_url.ptr, config_url); + + git_buf_free(&absolute_url); + git_config_free(cfg); + git_object_free(head_commit); + git_reference_free(head_ref); + git_submodule_free(sm); +} diff -Nru libgit2-0.20.0/tests/submodule/lookup.c libgit2-0.22.2/tests/submodule/lookup.c --- libgit2-0.20.0/tests/submodule/lookup.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/submodule/lookup.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,7 +1,7 @@ #include "clar_libgit2.h" #include "submodule_helpers.h" -#include "posix.h" #include "git2/sys/repository.h" +#include "fileops.h" static git_repository *g_repo = NULL; @@ -12,31 +12,25 @@ void test_submodule_lookup__simple_lookup(void) { - git_submodule *sm; - - /* lookup existing */ - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); - cl_assert(sm); + assert_submodule_exists(g_repo, "sm_unchanged"); /* lookup pending change in .gitmodules that is not in HEAD */ - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); - cl_assert(sm); + assert_submodule_exists(g_repo, "sm_added_and_uncommited"); - /* lookup pending change in .gitmodules that is neither in HEAD nor index */ - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only")); - cl_assert(sm); + /* lookup pending change in .gitmodules that is not in HEAD nor index */ + assert_submodule_exists(g_repo, "sm_gitmodules_only"); /* lookup git repo subdir that is not added as submodule */ - cl_assert(git_submodule_lookup(&sm, g_repo, "not-submodule") == GIT_EEXISTS); + refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS); /* lookup existing directory that is not a submodule */ - cl_assert(git_submodule_lookup(&sm, g_repo, "just_a_dir") == GIT_ENOTFOUND); + refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND); /* lookup existing file that is not a submodule */ - cl_assert(git_submodule_lookup(&sm, g_repo, "just_a_file") == GIT_ENOTFOUND); + refute_submodule_exists(g_repo, "just_a_file", GIT_ENOTFOUND); /* lookup non-existent item */ - cl_assert(git_submodule_lookup(&sm, g_repo, "no_such_file") == GIT_ENOTFOUND); + refute_submodule_exists(g_repo, "no_such_file", GIT_ENOTFOUND); } void test_submodule_lookup__accessors(void) @@ -55,7 +49,10 @@ cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0); cl_assert(git_submodule_ignore(sm) == GIT_SUBMODULE_IGNORE_NONE); - cl_assert(git_submodule_update(sm) == GIT_SUBMODULE_UPDATE_CHECKOUT); + cl_assert(git_submodule_update_strategy(sm) == GIT_SUBMODULE_UPDATE_CHECKOUT); + + git_submodule_free(sm); + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); cl_assert_equal_s("sm_changed_head", git_submodule_name(sm)); @@ -65,6 +62,9 @@ cl_assert(git_oid_streq(git_submodule_wd_id(sm), "3d9386c507f6b093471a3e324085657a3c2b4247") == 0); + git_submodule_free(sm); + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); cl_assert_equal_s("sm_added_and_uncommited", git_submodule_name(sm)); @@ -72,6 +72,9 @@ cl_assert(git_submodule_head_id(sm) == NULL); cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0); + git_submodule_free(sm); + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits")); cl_assert_equal_s("sm_missing_commits", git_submodule_name(sm)); @@ -79,6 +82,8 @@ cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0); cl_assert(git_oid_streq(git_submodule_wd_id(sm), "5e4963595a9774b90524d35a807169049de8ccad") == 0); + + git_submodule_free(sm); } typedef struct { @@ -104,69 +109,163 @@ void test_submodule_lookup__lookup_even_with_unborn_head(void) { git_reference *head; - git_submodule *sm; /* put us on an unborn branch */ cl_git_pass(git_reference_symbolic_create( - &head, g_repo, "HEAD", "refs/heads/garbage", 1)); + &head, g_repo, "HEAD", "refs/heads/garbage", 1, NULL, NULL)); git_reference_free(head); - /* lookup existing */ - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); - cl_assert(sm); - - /* lookup pending change in .gitmodules that is not in HEAD */ - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); - cl_assert(sm); - - /* lookup pending change in .gitmodules that is neither in HEAD nor index */ - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only")); - cl_assert(sm); - - /* lookup git repo subdir that is not added as submodule */ - cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, g_repo, "not-submodule")); - - /* lookup existing directory that is not a submodule */ - cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_dir")); - - /* lookup existing file that is not a submodule */ - cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_file")); - - /* lookup non-existent item */ - cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "no_such_file")); + test_submodule_lookup__simple_lookup(); /* baseline should still pass */ } void test_submodule_lookup__lookup_even_with_missing_index(void) { git_index *idx; - git_submodule *sm; /* give the repo an empty index */ cl_git_pass(git_index_new(&idx)); git_repository_set_index(g_repo, idx); git_index_free(idx); - /* lookup existing */ - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); - cl_assert(sm); + test_submodule_lookup__simple_lookup(); /* baseline should still pass */ +} - /* lookup pending change in .gitmodules that is not in HEAD */ - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); - cl_assert(sm); +static void baseline_tests(void) +{ + /* small baseline that should work even if we change the index or make + * commits from the index + */ + assert_submodule_exists(g_repo, "sm_unchanged"); + assert_submodule_exists(g_repo, "sm_gitmodules_only"); + refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS); +} - /* lookup pending change in .gitmodules that is neither in HEAD nor index */ - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only")); - cl_assert(sm); +static void add_submodule_with_commit(const char *name) +{ + git_submodule *sm; + git_repository *smrepo; + git_index *idx; + git_buf p = GIT_BUF_INIT; - /* lookup git repo subdir that is not added as submodule */ - cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, g_repo, "not-submodule")); + cl_git_pass(git_submodule_add_setup(&sm, g_repo, + "https://github.com/libgit2/libgit2.git", name, 1)); - /* lookup existing directory that is not a submodule */ - cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_dir")); + assert_submodule_exists(g_repo, name); - /* lookup existing file that is not a submodule */ - cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "just_a_file")); + cl_git_pass(git_submodule_open(&smrepo, sm)); + cl_git_pass(git_repository_index(&idx, smrepo)); - /* lookup non-existent item */ - cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, "no_such_file")); + cl_git_pass(git_buf_joinpath(&p, git_repository_workdir(smrepo), "file")); + cl_git_mkfile(p.ptr, "new file"); + git_buf_free(&p); + + cl_git_pass(git_index_add_bypath(idx, "file")); + cl_git_pass(git_index_write(idx)); + git_index_free(idx); + + cl_repo_commit_from_index(NULL, smrepo, NULL, 0, "initial commit"); + git_repository_free(smrepo); + + cl_git_pass(git_submodule_add_finalize(sm)); + + git_submodule_free(sm); +} + +void test_submodule_lookup__just_added(void) +{ + git_submodule *sm; + git_buf snap1 = GIT_BUF_INIT, snap2 = GIT_BUF_INIT; + git_reference *original_head = NULL; + + refute_submodule_exists(g_repo, "sm_just_added", GIT_ENOTFOUND); + refute_submodule_exists(g_repo, "sm_just_added_2", GIT_ENOTFOUND); + refute_submodule_exists(g_repo, "sm_just_added_idx", GIT_ENOTFOUND); + refute_submodule_exists(g_repo, "sm_just_added_head", GIT_ENOTFOUND); + refute_submodule_exists(g_repo, "mismatch_name", GIT_ENOTFOUND); + refute_submodule_exists(g_repo, "mismatch_path", GIT_ENOTFOUND); + baseline_tests(); + + cl_git_pass(git_futils_readbuffer(&snap1, "submod2/.gitmodules")); + cl_git_pass(git_repository_head(&original_head, g_repo)); + + cl_git_pass(git_submodule_add_setup(&sm, g_repo, + "https://github.com/libgit2/libgit2.git", "sm_just_added", 1)); + git_submodule_free(sm); + assert_submodule_exists(g_repo, "sm_just_added"); + + cl_git_pass(git_submodule_add_setup(&sm, g_repo, + "https://github.com/libgit2/libgit2.git", "sm_just_added_2", 1)); + assert_submodule_exists(g_repo, "sm_just_added_2"); + cl_git_fail(git_submodule_add_finalize(sm)); /* fails if no HEAD */ + git_submodule_free(sm); + + add_submodule_with_commit("sm_just_added_head"); + cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "commit new sm to head"); + assert_submodule_exists(g_repo, "sm_just_added_head"); + + add_submodule_with_commit("sm_just_added_idx"); + assert_submodule_exists(g_repo, "sm_just_added_idx"); + + cl_git_pass(git_futils_readbuffer(&snap2, "submod2/.gitmodules")); + + cl_git_append2file( + "submod2/.gitmodules", + "\n[submodule \"mismatch_name\"]\n" + "\tpath = mismatch_path\n" + "\turl = https://example.com/example.git\n\n"); + + assert_submodule_exists(g_repo, "mismatch_name"); + assert_submodule_exists(g_repo, "mismatch_path"); + assert_submodule_exists(g_repo, "sm_just_added"); + assert_submodule_exists(g_repo, "sm_just_added_2"); + assert_submodule_exists(g_repo, "sm_just_added_idx"); + assert_submodule_exists(g_repo, "sm_just_added_head"); + baseline_tests(); + + cl_git_rewritefile("submod2/.gitmodules", snap2.ptr); + git_buf_free(&snap2); + + refute_submodule_exists(g_repo, "mismatch_name", GIT_ENOTFOUND); + refute_submodule_exists(g_repo, "mismatch_path", GIT_ENOTFOUND); + assert_submodule_exists(g_repo, "sm_just_added"); + assert_submodule_exists(g_repo, "sm_just_added_2"); + assert_submodule_exists(g_repo, "sm_just_added_idx"); + assert_submodule_exists(g_repo, "sm_just_added_head"); + baseline_tests(); + + cl_git_rewritefile("submod2/.gitmodules", snap1.ptr); + git_buf_free(&snap1); + + refute_submodule_exists(g_repo, "mismatch_name", GIT_ENOTFOUND); + refute_submodule_exists(g_repo, "mismatch_path", GIT_ENOTFOUND); + /* note error code change, because add_setup made a repo in the workdir */ + refute_submodule_exists(g_repo, "sm_just_added", GIT_EEXISTS); + refute_submodule_exists(g_repo, "sm_just_added_2", GIT_EEXISTS); + /* these still exist in index and head respectively */ + assert_submodule_exists(g_repo, "sm_just_added_idx"); + assert_submodule_exists(g_repo, "sm_just_added_head"); + baseline_tests(); + + { + git_index *idx; + cl_git_pass(git_repository_index(&idx, g_repo)); + cl_git_pass(git_index_remove_bypath(idx, "sm_just_added_idx")); + cl_git_pass(git_index_remove_bypath(idx, "sm_just_added_head")); + cl_git_pass(git_index_write(idx)); + git_index_free(idx); + } + + refute_submodule_exists(g_repo, "sm_just_added_idx", GIT_EEXISTS); + assert_submodule_exists(g_repo, "sm_just_added_head"); + + { + git_signature *sig; + cl_git_pass(git_signature_now(&sig, "resetter", "resetter@email.com")); + cl_git_pass(git_reference_create(NULL, g_repo, "refs/heads/master", git_reference_target(original_head), 1, sig, "move head back")); + git_signature_free(sig); + git_reference_free(original_head); + } + + refute_submodule_exists(g_repo, "sm_just_added_head", GIT_EEXISTS); } + diff -Nru libgit2-0.20.0/tests/submodule/modify.c libgit2-0.22.2/tests/submodule/modify.c --- libgit2-0.20.0/tests/submodule/modify.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/submodule/modify.c 2015-03-24 16:10:45.000000000 +0000 @@ -7,59 +7,12 @@ #define SM_LIBGIT2_URL "https://github.com/libgit2/libgit2.git" #define SM_LIBGIT2 "sm_libgit2" -#define SM_LIBGIT2B "sm_libgit2b" void test_submodule_modify__initialize(void) { g_repo = setup_fixture_submod2(); } -void test_submodule_modify__add(void) -{ - git_submodule *sm; - git_config *cfg; - const char *s; - - /* re-add existing submodule */ - cl_assert( - git_submodule_add_setup(NULL, g_repo, "whatever", "sm_unchanged", 1) == - GIT_EEXISTS ); - - /* add a submodule using a gitlink */ - - cl_git_pass( - git_submodule_add_setup(&sm, g_repo, SM_LIBGIT2_URL, SM_LIBGIT2, 1) - ); - - cl_assert(git_path_isfile("submod2/" SM_LIBGIT2 "/.git")); - - cl_assert(git_path_isdir("submod2/.git/modules")); - cl_assert(git_path_isdir("submod2/.git/modules/" SM_LIBGIT2)); - cl_assert(git_path_isfile("submod2/.git/modules/" SM_LIBGIT2 "/HEAD")); - - cl_git_pass(git_repository_config(&cfg, g_repo)); - cl_git_pass( - git_config_get_string(&s, cfg, "submodule." SM_LIBGIT2 ".url")); - cl_assert_equal_s(s, SM_LIBGIT2_URL); - git_config_free(cfg); - - /* add a submodule not using a gitlink */ - - cl_git_pass( - git_submodule_add_setup(&sm, g_repo, SM_LIBGIT2_URL, SM_LIBGIT2B, 0) - ); - - cl_assert(git_path_isdir("submod2/" SM_LIBGIT2B "/.git")); - cl_assert(git_path_isfile("submod2/" SM_LIBGIT2B "/.git/HEAD")); - cl_assert(!git_path_exists("submod2/.git/modules/" SM_LIBGIT2B)); - - cl_git_pass(git_repository_config(&cfg, g_repo)); - cl_git_pass( - git_config_get_string(&s, cfg, "submodule." SM_LIBGIT2B ".url")); - cl_assert_equal_s(s, SM_LIBGIT2_URL); - git_config_free(cfg); -} - static int delete_one_config(const git_config_entry *entry, void *payload) { git_config *cfg = payload; @@ -95,7 +48,7 @@ /* call init and see that settings are copied */ cl_git_pass(git_submodule_foreach(g_repo, init_one_submodule, NULL)); - git_submodule_reload_all(g_repo); + git_submodule_reload_all(g_repo, 1); /* confirm submodule data in config */ cl_git_pass(git_repository_config(&cfg, g_repo)); @@ -116,6 +69,26 @@ return git_submodule_sync(sm); } +static void assert_submodule_url_is_synced( + git_submodule *sm, const char *parent_key, const char *child_key) +{ + git_config *cfg; + const char *str; + git_repository *smrepo; + + cl_git_pass(git_repository_config(&cfg, g_repo)); + cl_git_pass(git_config_get_string(&str, cfg, parent_key)); + cl_assert_equal_s(git_submodule_url(sm), str); + git_config_free(cfg); + + cl_git_pass(git_submodule_open(&smrepo, sm)); + cl_git_pass(git_repository_config(&cfg, smrepo)); + cl_git_pass(git_config_get_string(&str, cfg, child_key)); + cl_assert_equal_s(git_submodule_url(sm), str); + git_config_free(cfg); + git_repository_free(smrepo); +} + void test_submodule_modify__sync(void) { git_submodule *sm1, *sm2, *sm3; @@ -151,14 +124,16 @@ cl_git_pass(git_submodule_foreach(g_repo, sync_one_submodule, NULL)); /* check that submodule config is updated */ - cl_git_pass(git_repository_config(&cfg, g_repo)); - cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM1".url")); - cl_assert_equal_s(git_submodule_url(sm1), str); - cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM2".url")); - cl_assert_equal_s(git_submodule_url(sm2), str); - cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM3".url")); - cl_assert_equal_s(git_submodule_url(sm3), str); - git_config_free(cfg); + assert_submodule_url_is_synced( + sm1, "submodule."SM1".url", "branch.origin.remote"); + assert_submodule_url_is_synced( + sm2, "submodule."SM2".url", "branch.origin.remote"); + assert_submodule_url_is_synced( + sm3, "submodule."SM3".url", "branch.origin.remote"); + + git_submodule_free(sm1); + git_submodule_free(sm2); + git_submodule_free(sm3); } void test_submodule_modify__edit_and_save(void) @@ -168,7 +143,7 @@ git_submodule_ignore_t old_ignore; git_submodule_update_t old_update; git_repository *r2; - int old_fetchrecurse; + git_submodule_recurse_t old_fetchrecurse; cl_git_pass(git_submodule_lookup(&sm1, g_repo, "sm_changed_head")); @@ -178,30 +153,33 @@ cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL)); old_ignore = git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_UNTRACKED); old_update = git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_REBASE); - old_fetchrecurse = git_submodule_set_fetch_recurse_submodules(sm1, 1); + old_fetchrecurse = git_submodule_set_fetch_recurse_submodules( + sm1, GIT_SUBMODULE_RECURSE_YES); cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1)); cl_assert_equal_i( - (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm1)); + GIT_SUBMODULE_IGNORE_UNTRACKED, git_submodule_ignore(sm1)); + cl_assert_equal_i( + GIT_SUBMODULE_UPDATE_REBASE, git_submodule_update_strategy(sm1)); cl_assert_equal_i( - (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update(sm1)); - cl_assert_equal_i(1, git_submodule_fetch_recurse_submodules(sm1)); + GIT_SUBMODULE_RECURSE_YES, git_submodule_fetch_recurse_submodules(sm1)); /* revert without saving (and confirm setters return old value) */ cl_git_pass(git_submodule_set_url(sm1, old_url)); cl_assert_equal_i( - (int)GIT_SUBMODULE_IGNORE_UNTRACKED, - (int)git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_RESET)); + GIT_SUBMODULE_IGNORE_UNTRACKED, + git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_RESET)); cl_assert_equal_i( - (int)GIT_SUBMODULE_UPDATE_REBASE, - (int)git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_RESET)); + GIT_SUBMODULE_UPDATE_REBASE, + git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_RESET)); cl_assert_equal_i( - 1, git_submodule_set_fetch_recurse_submodules(sm1, old_fetchrecurse)); + GIT_SUBMODULE_RECURSE_YES, git_submodule_set_fetch_recurse_submodules( + sm1, GIT_SUBMODULE_RECURSE_RESET)); /* check that revert was successful */ cl_assert_equal_s(old_url, git_submodule_url(sm1)); cl_assert_equal_i((int)old_ignore, (int)git_submodule_ignore(sm1)); - cl_assert_equal_i((int)old_update, (int)git_submodule_update(sm1)); + cl_assert_equal_i((int)old_update, (int)git_submodule_update_strategy(sm1)); cl_assert_equal_i( old_fetchrecurse, git_submodule_fetch_recurse_submodules(sm1)); @@ -209,7 +187,7 @@ cl_git_pass(git_submodule_set_url(sm1, SM_LIBGIT2_URL)); git_submodule_set_ignore(sm1, GIT_SUBMODULE_IGNORE_UNTRACKED); git_submodule_set_update(sm1, GIT_SUBMODULE_UPDATE_REBASE); - git_submodule_set_fetch_recurse_submodules(sm1, 1); + git_submodule_set_fetch_recurse_submodules(sm1, GIT_SUBMODULE_RECURSE_YES); /* call save */ cl_git_pass(git_submodule_save(sm1)); @@ -224,18 +202,18 @@ cl_assert_equal_i( (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm1)); cl_assert_equal_i( - (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update(sm1)); - cl_assert_equal_i(1, git_submodule_fetch_recurse_submodules(sm1)); + (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update_strategy(sm1)); + cl_assert_equal_i(GIT_SUBMODULE_RECURSE_YES, git_submodule_fetch_recurse_submodules(sm1)); /* call reload and check that the new values are loaded */ - cl_git_pass(git_submodule_reload(sm1)); + cl_git_pass(git_submodule_reload(sm1, 0)); cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm1)); cl_assert_equal_i( (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm1)); cl_assert_equal_i( - (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update(sm1)); - cl_assert_equal_i(1, git_submodule_fetch_recurse_submodules(sm1)); + (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update_strategy(sm1)); + cl_assert_equal_i(GIT_SUBMODULE_RECURSE_YES, git_submodule_fetch_recurse_submodules(sm1)); /* open a second copy of the repo and compare submodule */ cl_git_pass(git_repository_open(&r2, "submod2")); @@ -243,11 +221,25 @@ cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm2)); cl_assert_equal_i( - (int)GIT_SUBMODULE_IGNORE_UNTRACKED, (int)git_submodule_ignore(sm2)); + GIT_SUBMODULE_IGNORE_UNTRACKED, git_submodule_ignore(sm2)); + cl_assert_equal_i( + GIT_SUBMODULE_UPDATE_REBASE, git_submodule_update_strategy(sm2)); + cl_assert_equal_i( + GIT_SUBMODULE_RECURSE_NO, git_submodule_fetch_recurse_submodules(sm2)); + + /* set fetchRecurseSubmodules on-demand */ + cl_git_pass(git_submodule_reload(sm1, 0)); + git_submodule_set_fetch_recurse_submodules(sm1, GIT_SUBMODULE_RECURSE_ONDEMAND); + cl_assert_equal_i( + GIT_SUBMODULE_RECURSE_ONDEMAND, git_submodule_fetch_recurse_submodules(sm1)); + /* call save */ + cl_git_pass(git_submodule_save(sm1)); + cl_git_pass(git_submodule_reload(sm1, 0)); cl_assert_equal_i( - (int)GIT_SUBMODULE_UPDATE_REBASE, (int)git_submodule_update(sm2)); - cl_assert_equal_i(1, git_submodule_fetch_recurse_submodules(sm2)); + GIT_SUBMODULE_RECURSE_ONDEMAND, git_submodule_fetch_recurse_submodules(sm1)); + git_submodule_free(sm1); + git_submodule_free(sm2); git_repository_free(r2); git__free(old_url); } diff -Nru libgit2-0.20.0/tests/submodule/nosubs.c libgit2-0.22.2/tests/submodule/nosubs.c --- libgit2-0.20.0/tests/submodule/nosubs.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/submodule/nosubs.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,176 @@ +/* test the submodule APIs on repositories where there are no submodules */ + +#include "clar_libgit2.h" +#include "posix.h" +#include "fileops.h" + +void test_submodule_nosubs__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_submodule_nosubs__lookup(void) +{ + git_repository *repo = cl_git_sandbox_init("status"); + git_submodule *sm = NULL; + + p_mkdir("status/subrepo", 0777); + cl_git_mkfile("status/subrepo/.git", "gitdir: ../.git"); + + cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, repo, "subdir")); + + cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, repo, "subrepo")); + + cl_git_pass(git_submodule_reload_all(repo, 0)); + + cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, repo, "subdir")); + + cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, repo, "subrepo")); +} + +void test_submodule_nosubs__immediate_reload(void) +{ + git_repository *repo = cl_git_sandbox_init("status"); + cl_git_pass(git_submodule_reload_all(repo, 0)); +} + +static int fake_submod_cb(git_submodule *sm, const char *n, void *p) +{ + GIT_UNUSED(sm); GIT_UNUSED(n); GIT_UNUSED(p); + return 0; +} + +void test_submodule_nosubs__foreach(void) +{ + git_repository *repo = cl_git_sandbox_init("status"); + cl_git_pass(git_submodule_foreach(repo, fake_submod_cb, NULL)); +} + +void test_submodule_nosubs__add(void) +{ + git_repository *repo = cl_git_sandbox_init("status"); + git_submodule *sm, *sm2; + + cl_git_pass(git_submodule_add_setup(&sm, repo, "https://github.com/libgit2/libgit2.git", "submodules/libgit2", 1)); + + cl_git_pass(git_submodule_lookup(&sm2, repo, "submodules/libgit2")); + git_submodule_free(sm2); + + cl_git_pass(git_submodule_foreach(repo, fake_submod_cb, NULL)); + cl_git_pass(git_submodule_reload_all(repo, 0)); + + git_submodule_free(sm); +} + +void test_submodule_nosubs__reload_add_reload(void) +{ + git_repository *repo = cl_git_sandbox_init("status"); + git_submodule *sm; + + cl_git_pass(git_submodule_reload_all(repo, 0)); + + /* try one add with a reload (to make sure no errors happen) */ + + cl_git_pass(git_submodule_add_setup(&sm, repo, + "https://github.com/libgit2/libgit2.git", "submodules/libgit2", 1)); + + cl_git_pass(git_submodule_reload_all(repo, 0)); + + cl_assert_equal_s("submodules/libgit2", git_submodule_name(sm)); + git_submodule_free(sm); + + cl_git_pass(git_submodule_lookup(&sm, repo, "submodules/libgit2")); + cl_assert_equal_s("submodules/libgit2", git_submodule_name(sm)); + git_submodule_free(sm); + + /* try one add without a reload (to make sure cache inval works, too) */ + + cl_git_pass(git_submodule_add_setup(&sm, repo, + "https://github.com/libgit2/libgit2.git", "libgit2-again", 1)); + cl_assert_equal_s("libgit2-again", git_submodule_name(sm)); + git_submodule_free(sm); + + cl_git_pass(git_submodule_lookup(&sm, repo, "libgit2-again")); + cl_assert_equal_s("libgit2-again", git_submodule_name(sm)); + git_submodule_free(sm); +} + +void test_submodule_nosubs__bad_gitmodules(void) +{ + git_repository *repo = cl_git_sandbox_init("status"); + + cl_git_mkfile("status/.gitmodules", "[submodule \"foobar\"]\tpath=blargle\n\turl=\n\tbranch=\n\tupdate=flooble\n\n"); + cl_git_fail(git_submodule_reload_all(repo, 0)); + + cl_git_rewritefile("status/.gitmodules", "[submodule \"foobar\"]\tpath=blargle\n\turl=\n\tbranch=\n\tupdate=rebase\n\n"); + cl_git_pass(git_submodule_reload_all(repo, 0)); + + cl_git_pass(git_submodule_lookup(NULL, repo, "foobar")); + cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(NULL, repo, "subdir")); +} + +void test_submodule_nosubs__add_and_delete(void) +{ + git_repository *repo = cl_git_sandbox_init("status"); + git_submodule *sm; + git_buf buf = GIT_BUF_INIT; + + /* note lack of calls to git_submodule_reload_all - this *should* work */ + + cl_git_fail(git_submodule_lookup(NULL, repo, "libgit2")); + cl_git_fail(git_submodule_lookup(NULL, repo, "submodules/libgit2")); + + /* create */ + + cl_git_pass(git_submodule_add_setup( + &sm, repo, "https://github.com/libgit2/libgit2.git", "submodules/libgit2", 1)); + cl_assert_equal_s("submodules/libgit2", git_submodule_name(sm)); + cl_assert_equal_s("submodules/libgit2", git_submodule_path(sm)); + git_submodule_free(sm); + + cl_git_pass(git_futils_readbuffer(&buf, "status/.gitmodules")); + cl_assert(strstr(buf.ptr, "[submodule \"submodules/libgit2\"]") != NULL); + cl_assert(strstr(buf.ptr, "path = submodules/libgit2") != NULL); + git_buf_free(&buf); + + /* lookup */ + + cl_git_fail(git_submodule_lookup(&sm, repo, "libgit2")); + cl_git_pass(git_submodule_lookup(&sm, repo, "submodules/libgit2")); + cl_assert_equal_s("submodules/libgit2", git_submodule_name(sm)); + cl_assert_equal_s("submodules/libgit2", git_submodule_path(sm)); + git_submodule_free(sm); + + /* update name */ + + cl_git_rewritefile( + "status/.gitmodules", + "[submodule \"libgit2\"]\n" + " path = submodules/libgit2\n" + " url = https://github.com/libgit2/libgit2.git\n"); + + cl_git_pass(git_submodule_lookup(&sm, repo, "libgit2")); + cl_assert_equal_s("libgit2", git_submodule_name(sm)); + cl_assert_equal_s("submodules/libgit2", git_submodule_path(sm)); + git_submodule_free(sm); + cl_git_pass(git_submodule_lookup(&sm, repo, "submodules/libgit2")); + git_submodule_free(sm); + + /* revert name update */ + + cl_git_rewritefile( + "status/.gitmodules", + "[submodule \"submodules/libgit2\"]\n" + " path = submodules/libgit2\n" + " url = https://github.com/libgit2/libgit2.git\n"); + + cl_git_fail(git_submodule_lookup(&sm, repo, "libgit2")); + cl_git_pass(git_submodule_lookup(&sm, repo, "submodules/libgit2")); + git_submodule_free(sm); + + /* remove completely */ + + cl_must_pass(p_unlink("status/.gitmodules")); + cl_git_fail(git_submodule_lookup(&sm, repo, "libgit2")); + cl_git_fail(git_submodule_lookup(&sm, repo, "submodules/libgit2")); +} diff -Nru libgit2-0.20.0/tests/submodule/repository_init.c libgit2-0.22.2/tests/submodule/repository_init.c --- libgit2-0.20.0/tests/submodule/repository_init.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/submodule/repository_init.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,42 @@ +#include "clar_libgit2.h" +#include "posix.h" +#include "path.h" +#include "submodule_helpers.h" +#include "fileops.h" + +static git_repository *g_repo = NULL; + +void test_submodule_repository_init__basic(void) +{ + git_submodule *sm; + git_repository *repo; + git_config *cfg; + const char *worktree_path; + git_buf dot_git_content = GIT_BUF_INIT; + + g_repo = setup_fixture_submod2(); + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only")); + cl_git_pass(git_submodule_init(sm, 0)); + cl_git_pass(git_submodule_repo_init(&repo, sm, 1)); + + /* Verify worktree */ + cl_git_pass(git_repository_config(&cfg, repo)); + cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree")); + cl_assert_equal_s("../../../sm_gitmodules_only/", worktree_path); + + /* Verify gitlink */ + cl_git_pass(git_futils_readbuffer(&dot_git_content, "submod2/" "sm_gitmodules_only" "/.git")); + cl_assert_equal_s("gitdir: ../.git/modules/sm_gitmodules_only/", dot_git_content.ptr); + + cl_assert(git_path_isfile("submod2/" "sm_gitmodules_only" "/.git")); + + cl_assert(git_path_isdir("submod2/.git/modules")); + cl_assert(git_path_isdir("submod2/.git/modules/" "sm_gitmodules_only")); + cl_assert(git_path_isfile("submod2/.git/modules/" "sm_gitmodules_only" "/HEAD")); + + git_config_free(cfg); + git_submodule_free(sm); + git_repository_free(repo); + git_buf_free(&dot_git_content); +} diff -Nru libgit2-0.20.0/tests/submodule/status.c libgit2-0.22.2/tests/submodule/status.c --- libgit2-0.20.0/tests/submodule/status.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/submodule/status.c 2015-03-24 16:10:45.000000000 +0000 @@ -18,19 +18,43 @@ void test_submodule_status__unchanged(void) { - unsigned int status, expected; - git_submodule *sm; - - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); - cl_git_pass(git_submodule_status(&status, sm)); - cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - - expected = GIT_SUBMODULE_STATUS_IN_HEAD | + unsigned int status = get_submodule_status(g_repo, "sm_unchanged"); + unsigned int expected = + GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_WD; - cl_assert(status == expected); + cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); + cl_assert(expected == status); +} + +static void rm_submodule(const char *name) +{ + git_buf path = GIT_BUF_INIT; + cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), name)); + cl_git_pass(git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES)); + git_buf_free(&path); +} + +static void add_submodule_to_index(const char *name) +{ + git_submodule *sm; + cl_git_pass(git_submodule_lookup(&sm, g_repo, name)); + cl_git_pass(git_submodule_add_to_index(sm, true)); + git_submodule_free(sm); +} + +static void rm_submodule_from_index(const char *name) +{ + git_index *index; + size_t pos; + + cl_git_pass(git_repository_index(&index, g_repo)); + cl_assert(!git_index_find(&pos, index, name)); + cl_git_pass(git_index_remove(index, name, 0)); + cl_git_pass(git_index_write(index)); + git_index_free(index); } /* 4 values of GIT_SUBMODULE_IGNORE to check */ @@ -38,81 +62,49 @@ void test_submodule_status__ignore_none(void) { unsigned int status; - git_submodule *sm; - git_buf path = GIT_BUF_INIT; - cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged")); - cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES)); + rm_submodule("sm_unchanged"); - cl_assert_equal_i(GIT_ENOTFOUND, - git_submodule_lookup(&sm, g_repo, "just_a_dir")); - cl_assert_equal_i(GIT_EEXISTS, - git_submodule_lookup(&sm, g_repo, "not-submodule")); - cl_assert_equal_i(GIT_EEXISTS, - git_submodule_lookup(&sm, g_repo, "not")); + refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND); + refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS); + refute_submodule_exists(g_repo, "not", GIT_EEXISTS); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_changed_index"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_changed_head"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_changed_file"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_changed_untracked_file"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNTRACKED) != 0); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_missing_commits"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_added_and_uncommited"); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0); /* removed sm_unchanged for deleted workdir */ - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_unchanged"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0); /* now mkdir sm_unchanged to test uninitialized */ - cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0)); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); - cl_git_pass(git_submodule_reload(sm)); - cl_git_pass(git_submodule_status(&status, sm)); + cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0)); + status = get_submodule_status(g_repo, "sm_unchanged"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0); /* update sm_changed_head in index */ - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); - cl_git_pass(git_submodule_add_to_index(sm, true)); - /* reload is not needed because add_to_index updates the submodule data */ - cl_git_pass(git_submodule_status(&status, sm)); + add_submodule_to_index("sm_changed_head"); + status = get_submodule_status(g_repo, "sm_changed_head"); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0); /* remove sm_changed_head from index */ - { - git_index *index; - size_t pos; - - cl_git_pass(git_repository_index(&index, g_repo)); - cl_assert(!git_index_find(&pos, index, "sm_changed_head")); - cl_git_pass(git_index_remove(index, "sm_changed_head", 0)); - cl_git_pass(git_index_write(index)); - - git_index_free(index); - } - - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); - cl_git_pass(git_submodule_reload(sm)); - cl_git_pass(git_submodule_status(&status, sm)); + rm_submodule_from_index("sm_changed_head"); + status = get_submodule_status(g_repo, "sm_changed_head"); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_DELETED) != 0); - - git_buf_free(&path); } static int set_sm_ignore(git_submodule *sm, const char *name, void *payload) @@ -126,191 +118,136 @@ void test_submodule_status__ignore_untracked(void) { unsigned int status; - git_submodule *sm; - git_buf path = GIT_BUF_INIT; git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_UNTRACKED; - cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged")); - cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES)); - + rm_submodule("sm_unchanged"); cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign)); - cl_git_fail(git_submodule_lookup(&sm, g_repo, "not-submodule")); + refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND); + refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS); + refute_submodule_exists(g_repo, "not", GIT_EEXISTS); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_changed_index"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_changed_head"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_changed_file"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_changed_untracked_file"); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_missing_commits"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_added_and_uncommited"); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0); /* removed sm_unchanged for deleted workdir */ - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_unchanged"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0); /* now mkdir sm_unchanged to test uninitialized */ - cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0)); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); - cl_git_pass(git_submodule_reload(sm)); - cl_git_pass(git_submodule_status(&status, sm)); + cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0)); + status = get_submodule_status(g_repo, "sm_unchanged"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0); /* update sm_changed_head in index */ - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); - cl_git_pass(git_submodule_add_to_index(sm, true)); - /* reload is not needed because add_to_index updates the submodule data */ - cl_git_pass(git_submodule_status(&status, sm)); + add_submodule_to_index("sm_changed_head"); + status = get_submodule_status(g_repo, "sm_changed_head"); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0); - - git_buf_free(&path); } void test_submodule_status__ignore_dirty(void) { unsigned int status; - git_submodule *sm; - git_buf path = GIT_BUF_INIT; git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_DIRTY; - cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged")); - cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES)); - + rm_submodule("sm_unchanged"); cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign)); - cl_assert_equal_i(GIT_ENOTFOUND, - git_submodule_lookup(&sm, g_repo, "just_a_dir")); - cl_assert_equal_i(GIT_EEXISTS, - git_submodule_lookup(&sm, g_repo, "not-submodule")); - cl_assert_equal_i(GIT_EEXISTS, - git_submodule_lookup(&sm, g_repo, "not")); + refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND); + refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS); + refute_submodule_exists(g_repo, "not", GIT_EEXISTS); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_changed_index"); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_changed_head"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_changed_file"); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_changed_untracked_file"); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_missing_commits"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_added_and_uncommited"); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0); /* removed sm_unchanged for deleted workdir */ - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_unchanged"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0); /* now mkdir sm_unchanged to test uninitialized */ - cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0)); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); - cl_git_pass(git_submodule_reload(sm)); - cl_git_pass(git_submodule_status(&status, sm)); + cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0)); + status = get_submodule_status(g_repo, "sm_unchanged"); cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0); /* update sm_changed_head in index */ - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); - cl_git_pass(git_submodule_add_to_index(sm, true)); - /* reload is not needed because add_to_index updates the submodule data */ - cl_git_pass(git_submodule_status(&status, sm)); + add_submodule_to_index("sm_changed_head"); + status = get_submodule_status(g_repo, "sm_changed_head"); cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0); - - git_buf_free(&path); } void test_submodule_status__ignore_all(void) { unsigned int status; - git_submodule *sm; - git_buf path = GIT_BUF_INIT; git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_ALL; - cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged")); - cl_git_pass(git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES)); - + rm_submodule("sm_unchanged"); cl_git_pass(git_submodule_foreach(g_repo, set_sm_ignore, &ign)); - cl_assert_equal_i(GIT_ENOTFOUND, - git_submodule_lookup(&sm, g_repo, "just_a_dir")); - cl_assert_equal_i(GIT_EEXISTS, - git_submodule_lookup(&sm, g_repo, "not-submodule")); - cl_assert_equal_i(GIT_EEXISTS, - git_submodule_lookup(&sm, g_repo, "not")); + refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND); + refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS); + refute_submodule_exists(g_repo, "not", GIT_EEXISTS); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_index")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_changed_index"); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_changed_head"); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_file")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_changed_file"); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_untracked_file")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_changed_untracked_file"); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_missing_commits"); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_added_and_uncommited"); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); /* removed sm_unchanged for deleted workdir */ - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); - cl_git_pass(git_submodule_status(&status, sm)); + status = get_submodule_status(g_repo, "sm_unchanged"); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); /* now mkdir sm_unchanged to test uninitialized */ - cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0)); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); - cl_git_pass(git_submodule_reload(sm)); - cl_git_pass(git_submodule_status(&status, sm)); + cl_git_pass(git_futils_mkdir("sm_unchanged", "submod2", 0755, 0)); + status = get_submodule_status(g_repo, "sm_unchanged"); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); /* update sm_changed_head in index */ - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head")); - cl_git_pass(git_submodule_add_to_index(sm, true)); - /* reload is not needed because add_to_index updates the submodule data */ - cl_git_pass(git_submodule_status(&status, sm)); + add_submodule_to_index("sm_changed_head"); + status = get_submodule_status(g_repo, "sm_changed_head"); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); - - git_buf_free(&path); } typedef struct { @@ -324,7 +261,7 @@ { submodule_expectations *exp = payload; - while (git__suffixcmp(exp->paths[exp->counter], "/") == 0) + while (exp->statuses[exp->counter] < 0) exp->counter++; cl_assert_equal_i(exp->statuses[exp->counter], (int)status_flags); @@ -345,8 +282,10 @@ "just_a_dir/", "just_a_dir/contents", "just_a_file", - "not", - "not-submodule", + "not-submodule/", + "not-submodule/README.txt", + "not/", + "not/README.txt", "README.txt", "sm_added_and_uncommited", "sm_changed_file", @@ -359,11 +298,13 @@ }; static int expected_flags[] = { GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED, /* ".gitmodules" */ - 0, /* "just_a_dir/" will be skipped */ + -1, /* "just_a_dir/" will be skipped */ GIT_STATUS_CURRENT, /* "just_a_dir/contents" */ GIT_STATUS_CURRENT, /* "just_a_file" */ - GIT_STATUS_IGNORED, /* "not" (contains .git) */ - GIT_STATUS_IGNORED, /* "not-submodule" (contains .git) */ + GIT_STATUS_WT_NEW, /* "not-submodule/" untracked item */ + -1, /* "not-submodule/README.txt" */ + GIT_STATUS_WT_NEW, /* "not/" untracked item */ + -1, /* "not/README.txt" */ GIT_STATUS_CURRENT, /* "README.txt */ GIT_STATUS_INDEX_NEW, /* "sm_added_and_uncommited" */ GIT_STATUS_WT_MODIFIED, /* "sm_changed_file" */ @@ -376,14 +317,17 @@ }; submodule_expectations exp = { 0, expected, expected_flags }; git_status_options opts = GIT_STATUS_OPTIONS_INIT; + git_index *index; - cl_git_pass(git_iterator_for_workdir(&iter, g_repo, + cl_git_pass(git_repository_index(&index, g_repo)); + cl_git_pass(git_iterator_for_workdir(&iter, g_repo, index, NULL, GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES, NULL, NULL)); for (i = 0; !git_iterator_advance(&entry, iter); ++i) cl_assert_equal_s(expected[i], entry->path); git_iterator_free(iter); + git_index_free(index); opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_INCLUDE_UNMODIFIED | @@ -397,29 +341,23 @@ void test_submodule_status__untracked_dirs_containing_ignored_files(void) { - git_buf path = GIT_BUF_INIT; unsigned int status, expected; - git_submodule *sm; - - cl_git_pass(git_buf_joinpath(&path, git_repository_path(g_repo), "modules/sm_unchanged/info/exclude")); - cl_git_append2file(git_buf_cstr(&path), "\n*.ignored\n"); - cl_git_pass(git_buf_joinpath(&path, git_repository_workdir(g_repo), "sm_unchanged/directory")); - cl_git_pass(git_futils_mkdir(git_buf_cstr(&path), NULL, 0755, 0)); - cl_git_pass(git_buf_joinpath(&path, git_buf_cstr(&path), "i_am.ignored")); - cl_git_mkfile(git_buf_cstr(&path), "ignored this file, please\n"); + cl_git_append2file( + "submod2/.git/modules/sm_unchanged/info/exclude", "\n*.ignored\n"); - cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged")); - cl_git_pass(git_submodule_status(&status, sm)); + cl_git_pass( + git_futils_mkdir("sm_unchanged/directory", "submod2", 0755, 0)); + cl_git_mkfile( + "submod2/sm_unchanged/directory/i_am.ignored", + "ignore this file, please\n"); + status = get_submodule_status(g_repo, "sm_unchanged"); cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status)); expected = GIT_SUBMODULE_STATUS_IN_HEAD | GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_CONFIG | GIT_SUBMODULE_STATUS_IN_WD; - cl_assert(status == expected); - - git_buf_free(&path); } diff -Nru libgit2-0.20.0/tests/submodule/submodule_helpers.c libgit2-0.22.2/tests/submodule/submodule_helpers.c --- libgit2-0.20.0/tests/submodule/submodule_helpers.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/submodule/submodule_helpers.c 2015-03-24 16:10:45.000000000 +0000 @@ -19,8 +19,8 @@ cl_git_pass(git_buf_joinpath(&in_f, workdir, "gitmodules")); cl_git_pass(git_buf_joinpath(&out_f, workdir, ".gitmodules")); - cl_assert((in = fopen(in_f.ptr, "r")) != NULL); - cl_assert((out = fopen(out_f.ptr, "w")) != NULL); + cl_assert((in = fopen(in_f.ptr, "rb")) != NULL); + cl_assert((out = fopen(out_f.ptr, "wb")) != NULL); while (fgets(line, sizeof(line), in) != NULL) { char *scan = line; @@ -125,3 +125,68 @@ return repo; } + +git_repository *setup_fixture_submodule_simple(void) +{ + git_repository *repo = cl_git_sandbox_init("submodule_simple"); + + cl_fixture_sandbox("testrepo.git"); + p_mkdir("submodule_simple/testrepo", 0777); + + cl_set_cleanup(cleanup_fixture_submodules, "testrepo.git"); + + cl_git_pass(git_repository_reinit_filesystem(repo, 1)); + + return repo; +} + +void assert__submodule_exists( + git_repository *repo, const char *name, + const char *msg, const char *file, int line) +{ + git_submodule *sm; + int error = git_submodule_lookup(&sm, repo, name); + if (error) + cl_git_report_failure(error, file, line, msg); + cl_assert_at_line(sm != NULL, file, line); + git_submodule_free(sm); +} + +void refute__submodule_exists( + git_repository *repo, const char *name, int expected_error, + const char *msg, const char *file, int line) +{ + git_submodule *sm; + clar__assert_equal( + file, line, msg, 1, "%i", + expected_error, (int)(git_submodule_lookup(&sm, repo, name))); +} + +unsigned int get_submodule_status(git_repository *repo, const char *name) +{ + git_submodule *sm = NULL; + unsigned int status = 0; + + cl_git_pass(git_submodule_lookup(&sm, repo, name)); + cl_assert(sm); + cl_git_pass(git_submodule_status(&status, sm)); + git_submodule_free(sm); + + return status; +} + +static int print_submodules(git_submodule *sm, const char *name, void *p) +{ + unsigned int loc = 0; + GIT_UNUSED(p); + git_submodule_location(&loc, sm); + fprintf(stderr, "# submodule %s (at %s) flags %x\n", + name, git_submodule_path(sm), loc); + return 0; +} + +void dump_submodules(git_repository *repo) +{ + git_submodule_foreach(repo, print_submodules, NULL); +} + diff -Nru libgit2-0.20.0/tests/submodule/submodule_helpers.h libgit2-0.22.2/tests/submodule/submodule_helpers.h --- libgit2-0.20.0/tests/submodule/submodule_helpers.h 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/submodule/submodule_helpers.h 2015-03-24 16:10:45.000000000 +0000 @@ -3,3 +3,20 @@ /* these will automatically set a cleanup callback */ extern git_repository *setup_fixture_submodules(void); extern git_repository *setup_fixture_submod2(void); +extern git_repository *setup_fixture_submodule_simple(void); + +extern unsigned int get_submodule_status(git_repository *, const char *); + +extern void assert__submodule_exists( + git_repository *, const char *, const char *, const char *, int); + +#define assert_submodule_exists(repo,name) \ + assert__submodule_exists(repo, name, "git_submodule_lookup(" #name ") failed", __FILE__, __LINE__) + +extern void refute__submodule_exists( + git_repository *, const char *, int err, const char *, const char *, int); + +#define refute_submodule_exists(repo,name,code) \ + refute__submodule_exists(repo, name, code, "expected git_submodule_lookup(" #name ") to fail with error " #code, __FILE__, __LINE__) + +extern void dump_submodules(git_repository *repo); diff -Nru libgit2-0.20.0/tests/submodule/update.c libgit2-0.22.2/tests/submodule/update.c --- libgit2-0.20.0/tests/submodule/update.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/submodule/update.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,380 @@ +#include "clar_libgit2.h" +#include "posix.h" +#include "path.h" +#include "submodule_helpers.h" +#include "fileops.h" + +static git_repository *g_repo = NULL; + +void test_submodule_update__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +void test_submodule_update__unitialized_submodule_no_init(void) +{ + git_submodule *sm; + git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT; + + g_repo = setup_fixture_submodule_simple(); + + /* get the submodule */ + cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); + + /* updating an unitialized repository throws */ + cl_git_fail_with( + GIT_ERROR, + git_submodule_update(sm, 0, &update_options)); + + git_submodule_free(sm); +} + +struct update_submodule_cb_payload { + int update_tips_called; + int checkout_progress_called; + int checkout_notify_called; +}; + +static void checkout_progress_cb( + const char *path, + size_t completed_steps, + size_t total_steps, + void *payload) +{ + struct update_submodule_cb_payload *update_payload = payload; + + GIT_UNUSED(path); + GIT_UNUSED(completed_steps); + GIT_UNUSED(total_steps); + + update_payload->checkout_progress_called = 1; +} + +static int checkout_notify_cb( + git_checkout_notify_t why, + const char *path, + const git_diff_file *baseline, + const git_diff_file *target, + const git_diff_file *workdir, + void *payload) +{ + struct update_submodule_cb_payload *update_payload = payload; + + GIT_UNUSED(why); + GIT_UNUSED(path); + GIT_UNUSED(baseline); + GIT_UNUSED(target); + GIT_UNUSED(workdir); + + update_payload->checkout_notify_called = 1; + + return 0; +} + +static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *data) +{ + struct update_submodule_cb_payload *update_payload = data; + + GIT_UNUSED(refname); + GIT_UNUSED(a); + GIT_UNUSED(b); + + update_payload->update_tips_called = 1; + + return 1; +} + +void test_submodule_update__update_submodule(void) +{ + git_submodule *sm; + git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT; + unsigned int submodule_status = 0; + struct update_submodule_cb_payload update_payload = { 0 }; + + g_repo = setup_fixture_submodule_simple(); + + update_options.checkout_opts.progress_cb = checkout_progress_cb; + update_options.checkout_opts.progress_payload = &update_payload; + + update_options.remote_callbacks.update_tips = update_tips; + update_options.remote_callbacks.payload = &update_payload; + + /* get the submodule */ + cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); + + /* verify the initial state of the submodule */ + cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS_IN_CONFIG | + GIT_SUBMODULE_STATUS_WD_UNINITIALIZED); + + /* initialize and update the submodule */ + cl_git_pass(git_submodule_init(sm, 0)); + cl_git_pass(git_submodule_update(sm, 0, &update_options)); + + /* verify state */ + cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS_IN_CONFIG | + GIT_SUBMODULE_STATUS_IN_WD); + + cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + + /* verify that the expected callbacks have been called. */ + cl_assert_equal_i(1, update_payload.checkout_progress_called); + cl_assert_equal_i(1, update_payload.update_tips_called); + + git_submodule_free(sm); +} + +void test_submodule_update__update_and_init_submodule(void) +{ + git_submodule *sm; + git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT; + unsigned int submodule_status = 0; + + g_repo = setup_fixture_submodule_simple(); + + /* get the submodule */ + cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); + + cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS_IN_CONFIG | + GIT_SUBMODULE_STATUS_WD_UNINITIALIZED); + + /* update (with option to initialize sub repo) */ + cl_git_pass(git_submodule_update(sm, 1, &update_options)); + + /* verify expected state */ + cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + + git_submodule_free(sm); +} + +void test_submodule_update__update_already_checked_out_submodule(void) +{ + git_submodule *sm = NULL; + git_checkout_options checkout_options = GIT_CHECKOUT_OPTIONS_INIT; + git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT; + unsigned int submodule_status = 0; + git_reference *branch_reference = NULL; + git_object *branch_commit = NULL; + struct update_submodule_cb_payload update_payload = { 0 }; + + g_repo = setup_fixture_submodule_simple(); + + update_options.checkout_opts.progress_cb = checkout_progress_cb; + update_options.checkout_opts.progress_payload = &update_payload; + + /* Initialize and update the sub repository */ + cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); + + cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS_IN_CONFIG | + GIT_SUBMODULE_STATUS_WD_UNINITIALIZED); + + cl_git_pass(git_submodule_update(sm, 1, &update_options)); + + /* verify expected state */ + cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + + /* checkout the alternate_1 branch */ + checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE; + + cl_git_pass(git_reference_lookup(&branch_reference, g_repo, "refs/heads/alternate_1")); + cl_git_pass(git_reference_peel(&branch_commit, branch_reference, GIT_OBJ_COMMIT)); + cl_git_pass(git_checkout_tree(g_repo, branch_commit, &checkout_options)); + cl_git_pass(git_repository_set_head(g_repo, git_reference_name(branch_reference), NULL, NULL)); + + /* + * Verify state after checkout of parent repository. The submodule ID in the + * HEAD commit and index should be updated, but not the workdir. + */ + + cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS_IN_CONFIG | + GIT_SUBMODULE_STATUS_IN_WD | + GIT_SUBMODULE_STATUS_WD_MODIFIED); + + cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + + /* + * Update the submodule and verify the state. + * Now, the HEAD, index, and Workdir commits should all be updated to + * the new commit. + */ + cl_git_pass(git_submodule_update(sm, 0, &update_options)); + cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + cl_assert(git_oid_streq(git_submodule_wd_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + + /* verify that the expected callbacks have been called. */ + cl_assert_equal_i(1, update_payload.checkout_progress_called); + + git_submodule_free(sm); + git_object_free(branch_commit); + git_reference_free(branch_reference); +} + +void test_submodule_update__update_blocks_on_dirty_wd(void) +{ + git_submodule *sm = NULL; + git_checkout_options checkout_options = GIT_CHECKOUT_OPTIONS_INIT; + git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT; + unsigned int submodule_status = 0; + git_reference *branch_reference = NULL; + git_object *branch_commit = NULL; + struct update_submodule_cb_payload update_payload = { 0 }; + + g_repo = setup_fixture_submodule_simple(); + + update_options.checkout_opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT; + update_options.checkout_opts.notify_cb = checkout_notify_cb; + update_options.checkout_opts.notify_payload = &update_payload; + + /* Initialize and update the sub repository */ + cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); + + cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS_IN_CONFIG | + GIT_SUBMODULE_STATUS_WD_UNINITIALIZED); + + cl_git_pass(git_submodule_update(sm, 1, &update_options)); + + /* verify expected state */ + cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + + /* checkout the alternate_1 branch */ + checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE; + + cl_git_pass(git_reference_lookup(&branch_reference, g_repo, "refs/heads/alternate_1")); + cl_git_pass(git_reference_peel(&branch_commit, branch_reference, GIT_OBJ_COMMIT)); + cl_git_pass(git_checkout_tree(g_repo, branch_commit, &checkout_options)); + cl_git_pass(git_repository_set_head(g_repo, git_reference_name(branch_reference), NULL, NULL)); + + /* + * Verify state after checkout of parent repository. The submodule ID in the + * HEAD commit and index should be updated, but not the workdir. + */ + + cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS_IN_CONFIG | + GIT_SUBMODULE_STATUS_IN_WD | + GIT_SUBMODULE_STATUS_WD_MODIFIED); + + cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + + /* + * Create a conflicting edit in the subrepository to verify that + * the submodule update action is blocked. + */ + cl_git_write2file("submodule_simple/testrepo/branch_file.txt", "a conflicting edit", 0, + O_WRONLY | O_CREAT | O_TRUNC, 0755); + + cl_git_fail(git_submodule_update(sm, 0, &update_options)); + + /* verify that the expected callbacks have been called. */ + cl_assert_equal_i(1, update_payload.checkout_notify_called); + + /* verify that the submodule state has not changed. */ + cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + + git_submodule_free(sm); + git_object_free(branch_commit); + git_reference_free(branch_reference); +} + +void test_submodule_update__can_force_update(void) +{ + git_submodule *sm = NULL; + git_checkout_options checkout_options = GIT_CHECKOUT_OPTIONS_INIT; + git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT; + unsigned int submodule_status = 0; + git_reference *branch_reference = NULL; + git_object *branch_commit = NULL; + + g_repo = setup_fixture_submodule_simple(); + + /* Initialize and update the sub repository */ + cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo")); + + cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS_IN_CONFIG | + GIT_SUBMODULE_STATUS_WD_UNINITIALIZED); + + cl_git_pass(git_submodule_update(sm, 1, &update_options)); + + /* verify expected state */ + cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + + /* checkout the alternate_1 branch */ + checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE; + + cl_git_pass(git_reference_lookup(&branch_reference, g_repo, "refs/heads/alternate_1")); + cl_git_pass(git_reference_peel(&branch_commit, branch_reference, GIT_OBJ_COMMIT)); + cl_git_pass(git_checkout_tree(g_repo, branch_commit, &checkout_options)); + cl_git_pass(git_repository_set_head(g_repo, git_reference_name(branch_reference), NULL, NULL)); + + /* + * Verify state after checkout of parent repository. The submodule ID in the + * HEAD commit and index should be updated, but not the workdir. + */ + cl_git_pass(git_submodule_status(&submodule_status, sm)); + cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD | + GIT_SUBMODULE_STATUS_IN_INDEX | + GIT_SUBMODULE_STATUS_IN_CONFIG | + GIT_SUBMODULE_STATUS_IN_WD | + GIT_SUBMODULE_STATUS_WD_MODIFIED); + + cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0); + cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + + /* + * Create a conflicting edit in the subrepository to verify that + * the submodule update action is blocked. + */ + cl_git_write2file("submodule_simple/testrepo/branch_file.txt", "a conflicting edit", 0, + O_WRONLY | O_CREAT | O_TRUNC, 0777); + + /* forcefully checkout and verify the submodule state was updated. */ + update_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE; + cl_git_pass(git_submodule_update(sm, 0, &update_options)); + cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + cl_assert(git_oid_streq(git_submodule_wd_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0); + + git_submodule_free(sm); + git_object_free(branch_commit); + git_reference_free(branch_reference); +} diff -Nru libgit2-0.20.0/tests/threads/basic.c libgit2-0.22.2/tests/threads/basic.c --- libgit2-0.20.0/tests/threads/basic.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/threads/basic.c 2015-03-24 16:10:45.000000000 +0000 @@ -1,5 +1,6 @@ #include "clar_libgit2.h" +#include "thread_helpers.h" #include "cache.h" @@ -26,11 +27,24 @@ { git_repository *nested_repo; - git_threads_init(); + git_libgit2_init(); cl_git_pass(git_repository_open(&nested_repo, cl_fixture("testrepo.git"))); git_repository_free(nested_repo); - git_threads_shutdown(); + git_libgit2_shutdown(); cl_git_pass(git_repository_open(&nested_repo, cl_fixture("testrepo.git"))); git_repository_free(nested_repo); } + +static void *set_error(void *dummy) +{ + giterr_set(GITERR_INVALID, "oh no, something happened!\n"); + + return dummy; +} + +/* Set errors so we can check that we free it */ +void test_threads_basic__set_error(void) +{ + run_in_parallel(1, 4, set_error, NULL, NULL); +} diff -Nru libgit2-0.20.0/tests/threads/diff.c libgit2-0.22.2/tests/threads/diff.c --- libgit2-0.20.0/tests/threads/diff.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/threads/diff.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,196 @@ +#include "clar_libgit2.h" +#include "thread_helpers.h" + +#ifdef GIT_THREADS + +# if defined(GIT_WIN32) +# define git_thread_yield() Sleep(0) +# elif defined(__FreeBSD__) || defined(__MidnightBSD__) || defined(__DragonFly__) +# define git_thread_yield() pthread_yield() +# else +# define git_thread_yield() sched_yield() +# endif + +#else +# define git_thread_yield() (void)0 +#endif + +static git_repository *_repo; +static git_tree *_a, *_b; +static git_atomic _counts[4]; +static int _check_counts; + +#define THREADS 20 + +void test_threads_diff__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +static void setup_trees(void) +{ + git_index *idx; + + _repo = cl_git_sandbox_reopen(); /* reopen sandbox to flush caches */ + + /* avoid competing to load initial index */ + cl_git_pass(git_repository_index(&idx, _repo)); + git_index_free(idx); + + cl_git_pass(git_revparse_single( + (git_object **)&_a, _repo, "0017bd4ab1^{tree}")); + cl_git_pass(git_revparse_single( + (git_object **)&_b, _repo, "26a125ee1b^{tree}")); + + memset(_counts, 0, sizeof(_counts)); +} + +static void free_trees(void) +{ + git_tree_free(_a); _a = NULL; + git_tree_free(_b); _b = NULL; + + if (_check_counts) { + cl_assert_equal_i(288, git_atomic_get(&_counts[0])); + cl_assert_equal_i(112, git_atomic_get(&_counts[1])); + cl_assert_equal_i( 80, git_atomic_get(&_counts[2])); + cl_assert_equal_i( 96, git_atomic_get(&_counts[3])); + } +} + +static void *run_index_diffs(void *arg) +{ + int thread = *(int *)arg; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff *diff = NULL; + size_t i; + int exp[4] = { 0, 0, 0, 0 }; + + switch (thread & 0x03) { + case 0: /* diff index to workdir */; + cl_git_pass(git_diff_index_to_workdir(&diff, _repo, NULL, &opts)); + break; + case 1: /* diff tree 'a' to index */; + cl_git_pass(git_diff_tree_to_index(&diff, _repo, _a, NULL, &opts)); + break; + case 2: /* diff tree 'b' to index */; + cl_git_pass(git_diff_tree_to_index(&diff, _repo, _b, NULL, &opts)); + break; + case 3: /* diff index to workdir (explicit index) */; + { + git_index *idx; + cl_git_pass(git_repository_index(&idx, _repo)); + cl_git_pass(git_diff_index_to_workdir(&diff, _repo, idx, &opts)); + git_index_free(idx); + break; + } + } + + /* keep some diff stats to make sure results are as expected */ + + i = git_diff_num_deltas(diff); + git_atomic_add(&_counts[0], (int32_t)i); + exp[0] = (int)i; + + while (i > 0) { + switch (git_diff_get_delta(diff, --i)->status) { + case GIT_DELTA_MODIFIED: exp[1]++; git_atomic_inc(&_counts[1]); break; + case GIT_DELTA_ADDED: exp[2]++; git_atomic_inc(&_counts[2]); break; + case GIT_DELTA_DELETED: exp[3]++; git_atomic_inc(&_counts[3]); break; + default: break; + } + } + + switch (thread & 0x03) { + case 0: case 3: + cl_assert_equal_i(8, exp[0]); cl_assert_equal_i(4, exp[1]); + cl_assert_equal_i(0, exp[2]); cl_assert_equal_i(4, exp[3]); + break; + case 1: + cl_assert_equal_i(12, exp[0]); cl_assert_equal_i(3, exp[1]); + cl_assert_equal_i(7, exp[2]); cl_assert_equal_i(2, exp[3]); + break; + case 2: + cl_assert_equal_i(8, exp[0]); cl_assert_equal_i(3, exp[1]); + cl_assert_equal_i(3, exp[2]); cl_assert_equal_i(2, exp[3]); + break; + } + + git_diff_free(diff); + giterr_clear(); + + return arg; +} + +void test_threads_diff__concurrent_diffs(void) +{ + _repo = cl_git_sandbox_init("status"); + _check_counts = 1; + + run_in_parallel( + 5, 32, run_index_diffs, setup_trees, free_trees); +} + +static void *run_index_diffs_with_modifier(void *arg) +{ + int thread = *(int *)arg; + git_diff_options opts = GIT_DIFF_OPTIONS_INIT; + git_diff *diff = NULL; + git_index *idx = NULL; + + cl_git_pass(git_repository_index(&idx, _repo)); + + /* have first thread altering the index as we go */ + if (thread == 0) { + int i; + + for (i = 0; i < 300; ++i) { + switch (i & 0x03) { + case 0: (void)git_index_add_bypath(idx, "new_file"); break; + case 1: (void)git_index_remove_bypath(idx, "modified_file"); break; + case 2: (void)git_index_remove_bypath(idx, "new_file"); break; + case 3: (void)git_index_add_bypath(idx, "modified_file"); break; + } + git_thread_yield(); + } + + goto done; + } + + /* only use explicit index in this test to prevent reloading */ + + switch (thread & 0x03) { + case 0: /* diff index to workdir */; + cl_git_pass(git_diff_index_to_workdir(&diff, _repo, idx, &opts)); + break; + case 1: /* diff tree 'a' to index */; + cl_git_pass(git_diff_tree_to_index(&diff, _repo, _a, idx, &opts)); + break; + case 2: /* diff tree 'b' to index */; + cl_git_pass(git_diff_tree_to_index(&diff, _repo, _b, idx, &opts)); + break; + case 3: /* diff index to workdir reversed */; + opts.flags |= GIT_DIFF_REVERSE; + cl_git_pass(git_diff_index_to_workdir(&diff, _repo, idx, &opts)); + break; + } + + /* results will be unpredictable with index modifier thread running */ + + git_diff_free(diff); + +done: + git_index_free(idx); + giterr_clear(); + + return arg; +} + +void test_threads_diff__with_concurrent_index_modified(void) +{ + _repo = cl_git_sandbox_init("status"); + _check_counts = 0; + + run_in_parallel( + 5, 16, run_index_diffs_with_modifier, setup_trees, free_trees); +} diff -Nru libgit2-0.20.0/tests/threads/iterator.c libgit2-0.22.2/tests/threads/iterator.c --- libgit2-0.20.0/tests/threads/iterator.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/threads/iterator.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,49 @@ +#include "clar_libgit2.h" +#include "thread_helpers.h" +#include "iterator.h" + +static git_repository *_repo; + +void test_threads_iterator__cleanup(void) +{ + cl_git_sandbox_cleanup(); +} + +static void *run_workdir_iterator(void *arg) +{ + int error = 0; + git_iterator *iter; + const git_index_entry *entry = NULL; + + cl_git_pass(git_iterator_for_workdir( + &iter, _repo, NULL, NULL, GIT_ITERATOR_DONT_AUTOEXPAND, NULL, NULL)); + + while (!error) { + if (entry && entry->mode == GIT_FILEMODE_TREE) { + error = git_iterator_advance_into(&entry, iter); + + if (error == GIT_ENOTFOUND) + error = git_iterator_advance(&entry, iter); + } else { + error = git_iterator_advance(&entry, iter); + } + + if (!error) + (void)git_iterator_current_is_ignored(iter); + } + + cl_assert_equal_i(GIT_ITEROVER, error); + + git_iterator_free(iter); + giterr_clear(); + return arg; +} + + +void test_threads_iterator__workdir(void) +{ + _repo = cl_git_sandbox_init("status"); + + run_in_parallel( + 1, 20, run_workdir_iterator, NULL, NULL); +} diff -Nru libgit2-0.20.0/tests/threads/refdb.c libgit2-0.22.2/tests/threads/refdb.c --- libgit2-0.20.0/tests/threads/refdb.c 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/threads/refdb.c 2015-03-24 16:10:45.000000000 +0000 @@ -37,6 +37,7 @@ git_reference_iterator_free(i); + giterr_clear(); return arg; } @@ -57,8 +58,8 @@ /* make a bunch of references */ for (r = 0; r < 200; ++r) { - snprintf(name, sizeof(name), "refs/heads/direct-%03d", r); - cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0)); + p_snprintf(name, sizeof(name), "refs/heads/direct-%03d", r); + cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0, NULL, NULL)); git_reference_free(ref); } @@ -83,7 +84,7 @@ #ifdef GIT_THREADS for (t = 0; t < THREADS; ++t) { - cl_git_pass(git_thread_join(th[t], NULL)); + cl_git_pass(git_thread_join(&th[t], NULL)); } #endif @@ -101,8 +102,8 @@ cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD")); for (i = 0; i < 10; ++i) { - snprintf(name, sizeof(name), "refs/heads/thread-%03d-%02d", *id, i); - cl_git_pass(git_reference_create(&ref[i], g_repo, name, &head, 0)); + p_snprintf(name, sizeof(name), "refs/heads/thread-%03d-%02d", *id, i); + cl_git_pass(git_reference_create(&ref[i], g_repo, name, &head, 0, NULL, NULL)); if (i == 5) { git_refdb *refdb; @@ -115,6 +116,7 @@ for (i = 0; i < 10; ++i) git_reference_free(ref[i]); + giterr_clear(); return arg; } @@ -125,7 +127,7 @@ char name[128]; for (i = 0; i < 10; ++i) { - snprintf( + p_snprintf( name, sizeof(name), "refs/heads/thread-%03d-%02d", (*id) & ~0x3, i); if (!git_reference_lookup(&ref, g_repo, name)) { @@ -141,6 +143,7 @@ } } + giterr_clear(); return arg; } @@ -164,8 +167,8 @@ /* make a bunch of references */ for (r = 0; r < 50; ++r) { - snprintf(name, sizeof(name), "refs/heads/starter-%03d", r); - cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0)); + p_snprintf(name, sizeof(name), "refs/heads/starter-%03d", r); + cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0, NULL, NULL)); git_reference_free(ref); } @@ -187,17 +190,22 @@ } id[t] = t; -#ifdef GIT_THREADS - cl_git_pass(git_thread_create(&th[t], NULL, fn, &id[t])); -#else + + /* It appears with all reflog writing changes, etc., that this + * test has started to fail quite frequently, so let's disable it + * for now by just running on a single thread... + */ +/* #ifdef GIT_THREADS */ +/* cl_git_pass(git_thread_create(&th[t], NULL, fn, &id[t])); */ +/* #else */ fn(&id[t]); -#endif +/* #endif */ } #ifdef GIT_THREADS - for (t = 0; t < THREADS; ++t) { - cl_git_pass(git_thread_join(th[t], NULL)); - } +/* for (t = 0; t < THREADS; ++t) { */ +/* cl_git_pass(git_thread_join(th[t], NULL)); */ +/* } */ memset(th, 0, sizeof(th)); @@ -207,7 +215,7 @@ } for (t = 0; t < THREADS; ++t) { - cl_git_pass(git_thread_join(th[t], NULL)); + cl_git_pass(git_thread_join(&th[t], NULL)); } #endif } diff -Nru libgit2-0.20.0/tests/threads/thread_helpers.c libgit2-0.22.2/tests/threads/thread_helpers.c --- libgit2-0.20.0/tests/threads/thread_helpers.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/threads/thread_helpers.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,44 @@ +#include "clar_libgit2.h" +#include "thread_helpers.h" + +void run_in_parallel( + int repeats, + int threads, + void *(*func)(void *), + void (*before_test)(void), + void (*after_test)(void)) +{ + int r, t, *id = git__calloc(threads, sizeof(int)); +#ifdef GIT_THREADS + git_thread *th = git__calloc(threads, sizeof(git_thread)); + cl_assert(th != NULL); +#else + void *th = NULL; +#endif + + cl_assert(id != NULL); + + for (r = 0; r < repeats; ++r) { + if (before_test) before_test(); + + for (t = 0; t < threads; ++t) { + id[t] = t; +#ifdef GIT_THREADS + cl_git_pass(git_thread_create(&th[t], NULL, func, &id[t])); +#else + cl_assert(func(&id[t]) == &id[t]); +#endif + } + +#ifdef GIT_THREADS + for (t = 0; t < threads; ++t) + cl_git_pass(git_thread_join(&th[t], NULL)); + memset(th, 0, threads * sizeof(git_thread)); +#endif + + if (after_test) after_test(); + } + + git__free(id); + git__free(th); +} diff -Nru libgit2-0.20.0/tests/threads/thread_helpers.h libgit2-0.22.2/tests/threads/thread_helpers.h --- libgit2-0.20.0/tests/threads/thread_helpers.h 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/threads/thread_helpers.h 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,8 @@ +#include "thread-utils.h" + +void run_in_parallel( + int repeats, + int threads, + void *(*func)(void *), + void (*before_test)(void), + void (*after_test)(void)); diff -Nru libgit2-0.20.0/tests/transport/register.c libgit2-0.22.2/tests/transport/register.c --- libgit2-0.20.0/tests/transport/register.c 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/tests/transport/register.c 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,68 @@ +#include "clar_libgit2.h" +#include "git2/sys/transport.h" + +static git_transport _transport = GIT_TRANSPORT_INIT; + +static int dummy_transport(git_transport **transport, git_remote *owner, void *param) +{ + *transport = &_transport; + GIT_UNUSED(owner); + GIT_UNUSED(param); + return 0; +} + +void test_transport_register__custom_transport(void) +{ + git_transport *transport; + + cl_git_pass(git_transport_register("something", dummy_transport, NULL)); + + cl_git_pass(git_transport_new(&transport, NULL, "something://somepath")); + + cl_assert(transport == &_transport); + + cl_git_pass(git_transport_unregister("something")); +} + +void test_transport_register__custom_transport_error_doubleregister(void) +{ + cl_git_pass(git_transport_register("something", dummy_transport, NULL)); + + cl_git_fail_with(git_transport_register("something", dummy_transport, NULL), GIT_EEXISTS); + + cl_git_pass(git_transport_unregister("something")); +} + +void test_transport_register__custom_transport_error_remove_non_existing(void) +{ + cl_git_fail_with(git_transport_unregister("something"), GIT_ENOTFOUND); +} + +void test_transport_register__custom_transport_ssh(void) +{ + git_transport *transport; + +#ifndef GIT_SSH + cl_git_fail_with(git_transport_new(&transport, NULL, "ssh://somehost:somepath"), -1); + cl_git_fail_with(git_transport_new(&transport, NULL, "git@somehost:somepath"), -1); +#else + cl_git_pass(git_transport_new(&transport, NULL, "git@somehost:somepath")); + transport->free(transport); +#endif + + cl_git_pass(git_transport_register("ssh", dummy_transport, NULL)); + + cl_git_pass(git_transport_new(&transport, NULL, "git@somehost:somepath")); + + cl_assert(transport == &_transport); + + cl_git_pass(git_transport_unregister("ssh")); + +#ifndef GIT_SSH + cl_git_fail_with(git_transport_new(&transport, NULL, "ssh://somehost:somepath"), -1); + cl_git_fail_with(git_transport_new(&transport, NULL, "git@somehost:somepath"), -1); +#else + cl_git_pass(git_transport_new(&transport, NULL, "git@somehost:somepath")); + transport->free(transport); +#endif +} diff -Nru libgit2-0.20.0/tests/valgrind-supp-mac.txt libgit2-0.22.2/tests/valgrind-supp-mac.txt --- libgit2-0.20.0/tests/valgrind-supp-mac.txt 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/tests/valgrind-supp-mac.txt 2015-03-24 16:10:45.000000000 +0000 @@ -103,14 +103,6 @@ fun:ssl23_connect } { - mac-ssl-uninitialized-4 - Memcheck:Param - ... - obj:/usr/lib/libcrypto.0.9.8.dylib - ... - fun:ssl23_connect -} -{ mac-ssl-leak-1 Memcheck:Leak ... diff -Nru libgit2-0.20.0/THREADING.md libgit2-0.22.2/THREADING.md --- libgit2-0.20.0/THREADING.md 1970-01-01 00:00:00.000000000 +0000 +++ libgit2-0.22.2/THREADING.md 2015-03-24 16:10:45.000000000 +0000 @@ -0,0 +1,89 @@ +Threads in libgit2 +================== + +You may safely use any libgit2 object from any thread, though there +may be issues depending on the cryptographic libraries libgit2 or its +dependencies link to (more on this later). For libgit2 itself, +provided you take the following into consideration you won't run into +issues: + +Sharing objects +--------------- + +Use an object from a single thread at a time. Most data structures do +not guard against concurrent access themselves. This is because they +are rarely used in isolation and it makes more sense to synchronize +access via a larger lock or similar mechanism. + +There are some objects which are read-only/immutable and are thus safe +to share across threads, such as references and configuration +snapshots. + +Error messages +-------------- + +The error message is thread-local. The `giterr_last()` call must +happen on the same thread as the error in order to get the +message. Often this will be the case regardless, but if you use +something like the [GCD](http://en.wikipedia.org/wiki/Grand_Central_Dispatch) +on Mac OS X (where code is executed on an arbitrary thread), the code +must make sure to retrieve the error code on the thread where the error +happened. + +Threads and cryptographic libraries +======================================= + +On Windows +---------- + +When built as a native Windows DLL, libgit2 uses WinCNG and WinHTTP, +both of which are thread-safe. You do not need to do anything special. + +When using libssh2 which itself uses WinCNG, there are no special +steps necessary. If you are using a MinGW or similar environment where +libssh2 uses OpenSSL or libgcrypt, then the non-Windows case affects +you. + +Non-Windows +----------- + +On the rest of the platforms, libgit2 uses OpenSSL to be able to use +HTTPS as a transport. This library is made to be thread-implementation +agnostic, and the users of the library must set which locking function +it should use. This means that libgit2 cannot know what to set as the +user of libgit2 may use OpenSSL independently and the locking settings +must survive libgit2 shutting down. + +libgit2 does provide a last-resort convenience function +`git_openssl_set_locking()` (available in `sys/openssl.h`) to use the +platform-native mutex mechanisms to perform the locking, which you may +rely on if you do not want to use OpenSSL outside of libgit2, or you +know that libgit2 will outlive the rest of the operations. It is not +safe to use OpenSSL multi-threaded after libgit2's shutdown function +has been called. + +If your programming language offers a package/bindings for OpenSSL, +you should very strongly prefer to use that in order to set up +locking, as they provide a level of coördination which is impossible +when using this function. + +See the +[OpenSSL documentation](https://www.openssl.org/docs/crypto/threads.html) +on threading for more details. + +Be also aware that libgit2 may not always link against OpenSSL in the +future if there are alternatives provided by the system. + +libssh2 may be linked against OpenSSL or libgcrypt. If it uses +OpenSSL, you only need to set up threading for OpenSSL once and the +above paragraphs are enough. If it uses libgcrypt, then you need to +set up its locking before using it multi-threaded. libgit2 has no +direct connection to libgcrypt and thus has not convenience functions for +it (but libgcrypt has macros). Read libgcrypt's +[threading documentation for more information](http://www.gnupg.org/documentation/manuals/gcrypt/Multi_002dThreading.html) + +It is your responsibility as an application author or packager to know +what your dependencies are linked against and to take the appropriate +steps to ensure the cryptographic libraries are thread-safe. We agree +that this situation is far from ideal but at this time it is something +the application authors need to deal with. diff -Nru libgit2-0.20.0/.travis.yml libgit2-0.22.2/.travis.yml --- libgit2-0.20.0/.travis.yml 2013-11-20 11:53:33.000000000 +0000 +++ libgit2-0.22.2/.travis.yml 2015-03-24 16:10:45.000000000 +0000 @@ -3,23 +3,47 @@ language: c +os: + - linux + - osx + compiler: - gcc - clang # Settings to try env: + global: + - secure: "YnhS+8n6B+uoyaYfaJ3Lei7cSJqHDPiKJCKFIF2c87YDfmCvAJke8QtE7IzjYDs7UFkTCM4ox+ph2bERUrxZbSCyEkHdjIZpKuMJfYWja/jgMqTMxdyOH9y8JLFbZsSXDIXDwqBlC6vVyl1fP90M35wuWcNTs6tctfVWVofEFbs=" + matrix: - OPTIONS="-DTHREADSAFE=ON -DCMAKE_BUILD_TYPE=Release" - - OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=ON" + - OPTIONS="-DTHREADSAFE=OFF -DBUILD_EXAMPLES=ON" matrix: + fast_finish: true + exclude: + - os: osx + compiler: gcc include: - compiler: i586-mingw32msvc-gcc - env: OPTIONS="-DBUILD_CLAR=OFF -DWIN32=ON -DMINGW=ON" + env: OPTIONS="-DBUILD_CLAR=OFF -DWIN32=ON -DMINGW=ON -DUSE_SSH=OFF" + os: linux + - compiler: gcc + env: COVERITY=1 + os: linux + - compiler: gcc + env: + - VALGRIND=1 + OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DCMAKE_BUILD_TYPE=Debug" + os: linux + allow_failures: + - env: COVERITY=1 + - env: + - VALGRIND=1 + OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=OFF -DCMAKE_BUILD_TYPE=Debug" install: - - sudo apt-get -qq update - - sudo apt-get -qq install cmake libssh2-1-dev openssh-client openssh-server + - ./script/install-deps-${TRAVIS_OS_NAME}.sh # Run the Build script and tests script: @@ -27,13 +51,14 @@ # Run Tests after_success: - - sudo apt-get -qq install valgrind - - valgrind --leak-check=full --show-reachable=yes --suppressions=./libgit2_clar.supp _build/libgit2_clar -ionline + - if [ "$TRAVIS_OS_NAME" = "linux" -a -n "$VALGRIND" ]; then sudo apt-get -qq install valgrind; fi + - if [ "$TRAVIS_OS_NAME" = "linux" -a -n "$VALGRIND" ]; then valgrind --leak-check=full --show-reachable=yes --suppressions=./libgit2_clar.supp _build/libgit2_clar -ionline; fi -# Only watch the development branch +# Only watch the development and master branches branches: only: - development + - master # Notify development list when needed notifications: