mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-15 08:00:52 +00:00
Merge tag 'upstream/0.19.0'
* tag 'upstream/0.19.0': (384 commits) libgit2 v0.19.0 "gut merge" test-rename: This is not a decimal, silly Fix a leak in the local transport code. Fix checkout tests on Windows Work around reparse point stat issues Fixed most documentation header bugs Add O_CLOEXEC to open calls Constrain mkdir calls to avoid extra mkdirs Do not redefine WC_ERR_INVALID_CHARS Addition checkout target directory tests Loosen ensure_not_bare rules in checkout Add target directory to checkout Fix checkout of modified file when missing from wd test asserting checkout should not recreate deleted files Add test for fixed diff bug Fix comment and copyright in example Add example implementation of long format status Add status flags to force output sort order Initial implementation of status example Fix bug in diff untracked dir scan ...
This commit is contained in:
commit
73ceef02ee
2
.gitignore
vendored
2
.gitignore
vendored
@ -24,8 +24,8 @@ msvc/Release/
|
|||||||
*.sdf
|
*.sdf
|
||||||
*.opensdf
|
*.opensdf
|
||||||
*.aps
|
*.aps
|
||||||
CMake*
|
|
||||||
*.cmake
|
*.cmake
|
||||||
|
!cmake/Modules/*.cmake
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*~
|
*~
|
||||||
tags
|
tags
|
||||||
|
17
.mailmap
17
.mailmap
@ -1,3 +1,18 @@
|
|||||||
Vicent Martí <vicent@github.com> Vicent Marti <tanoku@gmail.com>
|
Vicent Martí <vicent@github.com> Vicent Marti <tanoku@gmail.com>
|
||||||
Vicent Martí <vicent@github.com> Vicent Martí <tanoku@gmail.com>
|
Vicent Martí <vicent@github.com> Vicent Martí <tanoku@gmail.com>
|
||||||
Michael Schubert <schu@schu.io> schu <schu-github@schulog.org>
|
Michael Schubert <schu@schu.io> schu <schu-github@schulog.org>
|
||||||
|
Ben Straub <bs@github.com> Ben Straub <ben@straubnet.net>
|
||||||
|
Ben Straub <bs@github.com> Ben Straub <bstraub@github.com>
|
||||||
|
Carlos Martín Nieto <cmn@dwim.me> <carlos@cmartin.tk>
|
||||||
|
Carlos Martín Nieto <cmn@dwim.me> <cmn@elego.de>
|
||||||
|
nulltoken <emeric.fermas@gmail.com> <emeric.fermas@gmail.com>
|
||||||
|
Scott J. Goldman <scottjg@github.com> <scottjgo@gmail.com>
|
||||||
|
Martin Woodward <martin.woodward@microsoft.com> <martinwo@microsoft.com>
|
||||||
|
Peter Drahoš <drahosp@gmail.com> <drahosp@gmail.com>
|
||||||
|
Adam Roben <adam@github.com> <adam@roben.org>
|
||||||
|
Adam Roben <adam@github.com> <adam@github.com>
|
||||||
|
Xavier L. <xavier.l@afrosoft.tk> <xavier.l@afrosoft.ca>
|
||||||
|
Xavier L. <xavier.l@afrosoft.tk> <xavier.l@afrosoft.tk>
|
||||||
|
Sascha Cunz <sascha@babbelbox.org> <Sascha@BabbelBox.org>
|
||||||
|
Authmillenon <authmillenon@googlemail.com> <martin@ucsmail.de>
|
||||||
|
Authmillenon <authmillenon@googlemail.com> <authmillenon@googlemail.com>
|
||||||
|
@ -25,6 +25,10 @@ install:
|
|||||||
|
|
||||||
# Run the Build script
|
# Run the Build script
|
||||||
script:
|
script:
|
||||||
|
- mkdir _temp
|
||||||
|
- git init --bare _temp/test.git
|
||||||
|
- git daemon --listen=localhost --export-all --enable=receive-pack --base-path=_temp _temp 2>/dev/null &
|
||||||
|
- export GITTEST_REMOTE_URL="git://localhost/test.git"
|
||||||
- mkdir _build
|
- mkdir _build
|
||||||
- cd _build
|
- cd _build
|
||||||
- cmake .. -DCMAKE_INSTALL_PREFIX=../_install $OPTIONS
|
- cmake .. -DCMAKE_INSTALL_PREFIX=../_install $OPTIONS
|
||||||
|
@ -14,6 +14,9 @@
|
|||||||
PROJECT(libgit2 C)
|
PROJECT(libgit2 C)
|
||||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
|
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
|
||||||
|
|
||||||
|
# Add find modules to the path
|
||||||
|
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
|
||||||
|
|
||||||
# Build options
|
# Build options
|
||||||
#
|
#
|
||||||
OPTION( SONAME "Set the (SO)VERSION of the target" ON )
|
OPTION( SONAME "Set the (SO)VERSION of the target" ON )
|
||||||
@ -24,6 +27,8 @@ OPTION( BUILD_EXAMPLES "Build library usage example apps" OFF )
|
|||||||
OPTION( TAGS "Generate tags" OFF )
|
OPTION( TAGS "Generate tags" OFF )
|
||||||
OPTION( PROFILE "Generate profiling information" OFF )
|
OPTION( PROFILE "Generate profiling information" OFF )
|
||||||
OPTION( ENABLE_TRACE "Enables tracing support" OFF )
|
OPTION( ENABLE_TRACE "Enables tracing support" OFF )
|
||||||
|
OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF )
|
||||||
|
|
||||||
IF(MSVC)
|
IF(MSVC)
|
||||||
# This option is only availalbe when building with MSVC. By default,
|
# This option is only availalbe when building with MSVC. By default,
|
||||||
# libgit2 is build using the stdcall calling convention, as that's what
|
# libgit2 is build using the stdcall calling convention, as that's what
|
||||||
@ -97,14 +102,22 @@ ELSE ()
|
|||||||
IF (NOT AMIGA)
|
IF (NOT AMIGA)
|
||||||
FIND_PACKAGE(OpenSSL)
|
FIND_PACKAGE(OpenSSL)
|
||||||
ENDIF ()
|
ENDIF ()
|
||||||
FILE(GLOB SRC_HTTP deps/http-parser/*.c)
|
|
||||||
INCLUDE_DIRECTORIES(deps/http-parser)
|
FIND_PACKAGE(HTTP_Parser QUIET)
|
||||||
|
IF (HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2)
|
||||||
|
INCLUDE_DIRECTORIES(${HTTP_PARSER_INCLUDE_DIRS})
|
||||||
|
LINK_LIBRARIES(${HTTP_PARSER_LIBRARIES})
|
||||||
|
ELSE()
|
||||||
|
MESSAGE("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)
|
||||||
|
ENDIF()
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
# Specify sha1 implementation
|
# Specify sha1 implementation
|
||||||
IF (WIN32 AND NOT MINGW AND NOT SHA1_TYPE STREQUAL "builtin")
|
IF (WIN32 AND NOT MINGW AND NOT SHA1_TYPE STREQUAL "builtin")
|
||||||
ADD_DEFINITIONS(-DWIN32_SHA1)
|
ADD_DEFINITIONS(-DWIN32_SHA1)
|
||||||
FILE(GLOB SRC_SHA1 src/hash/hash_win32.c)
|
FILE(GLOB SRC_SHA1 src/hash/hash_win32.c)
|
||||||
ELSEIF (OPENSSL_FOUND AND NOT SHA1_TYPE STREQUAL "builtin")
|
ELSEIF (OPENSSL_FOUND AND NOT SHA1_TYPE STREQUAL "builtin")
|
||||||
ADD_DEFINITIONS(-DOPENSSL_SHA1)
|
ADD_DEFINITIONS(-DOPENSSL_SHA1)
|
||||||
ELSE()
|
ELSE()
|
||||||
@ -123,14 +136,14 @@ IF(WIN32 OR AMIGA)
|
|||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
# Optional external dependency: zlib
|
# Optional external dependency: zlib
|
||||||
IF(NOT ZLIB_LIBRARY)
|
# It's optional, but FIND_PACKAGE gives a warning that looks more like an
|
||||||
# It's optional, but FIND_PACKAGE gives a warning that looks more like an
|
# error.
|
||||||
# error.
|
FIND_PACKAGE(ZLIB QUIET)
|
||||||
FIND_PACKAGE(ZLIB QUIET)
|
|
||||||
ENDIF()
|
|
||||||
IF (ZLIB_FOUND)
|
IF (ZLIB_FOUND)
|
||||||
INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS})
|
INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS})
|
||||||
LINK_LIBRARIES(${ZLIB_LIBRARIES})
|
LINK_LIBRARIES(${ZLIB_LIBRARIES})
|
||||||
|
# Fake the message CMake would have shown
|
||||||
|
MESSAGE("-- Found zlib: ${ZLIB_LIBRARY}")
|
||||||
ELSE()
|
ELSE()
|
||||||
MESSAGE( "zlib was not found; using bundled 3rd-party sources." )
|
MESSAGE( "zlib was not found; using bundled 3rd-party sources." )
|
||||||
INCLUDE_DIRECTORIES(deps/zlib)
|
INCLUDE_DIRECTORIES(deps/zlib)
|
||||||
@ -138,10 +151,19 @@ ELSE()
|
|||||||
FILE(GLOB SRC_ZLIB deps/zlib/*.c)
|
FILE(GLOB SRC_ZLIB deps/zlib/*.c)
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
|
IF(NOT LIBSSH2_LIBRARY)
|
||||||
|
FIND_PACKAGE(LIBSSH2 QUIET)
|
||||||
|
ENDIF()
|
||||||
|
IF (LIBSSH2_FOUND)
|
||||||
|
ADD_DEFINITIONS(-DGIT_SSH)
|
||||||
|
INCLUDE_DIRECTORIES(${LIBSSH2_INCLUDE_DIR})
|
||||||
|
SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES})
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
# Platform specific compilation flags
|
# Platform specific compilation flags
|
||||||
IF (MSVC)
|
IF (MSVC)
|
||||||
|
|
||||||
STRING(REPLACE "/Zm1000" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
STRING(REPLACE "/Zm1000" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
||||||
|
|
||||||
# /GF - String pooling
|
# /GF - String pooling
|
||||||
# /MP - Parallel build
|
# /MP - Parallel build
|
||||||
@ -156,7 +178,7 @@ IF (MSVC)
|
|||||||
SET(CRT_FLAG_DEBUG "/MTd")
|
SET(CRT_FLAG_DEBUG "/MTd")
|
||||||
SET(CRT_FLAG_RELEASE "/MT")
|
SET(CRT_FLAG_RELEASE "/MT")
|
||||||
ELSE()
|
ELSE()
|
||||||
SET(CRT_FLAG_DEBUG "/MDd")
|
SET(CRT_FLAG_DEBUG "/MDd")
|
||||||
SET(CRT_FLAG_RELEASE "/MD")
|
SET(CRT_FLAG_RELEASE "/MD")
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
@ -277,14 +299,24 @@ ELSE()
|
|||||||
ENDIF()
|
ENDIF()
|
||||||
FILE(GLOB SRC_GIT2 src/*.c src/transports/*.c src/xdiff/*.c)
|
FILE(GLOB SRC_GIT2 src/*.c src/transports/*.c src/xdiff/*.c)
|
||||||
|
|
||||||
|
# Determine architecture of the machine
|
||||||
|
IF (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
ADD_DEFINITIONS(-DGIT_ARCH_64)
|
||||||
|
ELSEIF (CMAKE_SIZEOF_VOID_P EQUAL 4)
|
||||||
|
ADD_DEFINITIONS(-DGIT_ARCH_32)
|
||||||
|
ELSE()
|
||||||
|
message(FATAL_ERROR "Unsupported architecture")
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
# Compile and link libgit2
|
# Compile and link libgit2
|
||||||
ADD_LIBRARY(git2 ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC})
|
ADD_LIBRARY(git2 ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC})
|
||||||
TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES})
|
TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES})
|
||||||
|
TARGET_LINK_LIBRARIES(git2 ${SSH_LIBRARIES})
|
||||||
TARGET_OS_LIBRARIES(git2)
|
TARGET_OS_LIBRARIES(git2)
|
||||||
|
|
||||||
# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240)
|
# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240)
|
||||||
# Win64+MSVC+static libs = linker error
|
# Win64+MSVC+static libs = linker error
|
||||||
IF(MSVC AND NOT BUILD_SHARED_LIBS AND (${CMAKE_SIZEOF_VOID_P} MATCHES "8") )
|
IF(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS)
|
||||||
SET_TARGET_PROPERTIES(git2 PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64")
|
SET_TARGET_PROPERTIES(git2 PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64")
|
||||||
ENDIF()
|
ENDIF()
|
||||||
|
|
||||||
@ -293,6 +325,10 @@ MSVC_SPLIT_SOURCES(git2)
|
|||||||
IF (SONAME)
|
IF (SONAME)
|
||||||
SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING})
|
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_VERSION_MAJOR})
|
||||||
|
IF (LIBGIT2_FILENAME)
|
||||||
|
ADD_DEFINITIONS(-DLIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
|
||||||
|
SET_TARGET_PROPERTIES(git2 PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
|
||||||
|
ENDIF()
|
||||||
ENDIF()
|
ENDIF()
|
||||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libgit2.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libgit2.pc @ONLY)
|
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/libgit2.pc.in ${CMAKE_CURRENT_BINARY_DIR}/libgit2.pc @ONLY)
|
||||||
|
|
||||||
@ -328,7 +364,7 @@ IF (BUILD_CLAR)
|
|||||||
|
|
||||||
ADD_CUSTOM_COMMAND(
|
ADD_CUSTOM_COMMAND(
|
||||||
OUTPUT ${CLAR_PATH}/clar.suite
|
OUTPUT ${CLAR_PATH}/clar.suite
|
||||||
COMMAND ${PYTHON_EXECUTABLE} generate.py -xonline .
|
COMMAND ${PYTHON_EXECUTABLE} generate.py -f -xonline .
|
||||||
DEPENDS ${SRC_TEST}
|
DEPENDS ${SRC_TEST}
|
||||||
WORKING_DIRECTORY ${CLAR_PATH}
|
WORKING_DIRECTORY ${CLAR_PATH}
|
||||||
)
|
)
|
||||||
@ -340,6 +376,7 @@ IF (BUILD_CLAR)
|
|||||||
ADD_EXECUTABLE(libgit2_clar ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1})
|
ADD_EXECUTABLE(libgit2_clar ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1})
|
||||||
|
|
||||||
TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES})
|
TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES})
|
||||||
|
TARGET_LINK_LIBRARIES(libgit2_clar ${SSH_LIBRARIES})
|
||||||
TARGET_OS_LIBRARIES(libgit2_clar)
|
TARGET_OS_LIBRARIES(libgit2_clar)
|
||||||
MSVC_SPLIT_SOURCES(libgit2_clar)
|
MSVC_SPLIT_SOURCES(libgit2_clar)
|
||||||
|
|
||||||
|
13
README.md
13
README.md
@ -19,6 +19,7 @@ release its source code.
|
|||||||
* Archives: <http://librelist.com/browser/libgit2/>
|
* Archives: <http://librelist.com/browser/libgit2/>
|
||||||
* Website: <http://libgit2.github.com>
|
* Website: <http://libgit2.github.com>
|
||||||
* API documentation: <http://libgit2.github.com/libgit2>
|
* API documentation: <http://libgit2.github.com/libgit2>
|
||||||
|
* IRC: #libgit2 on irc.freenode.net.
|
||||||
|
|
||||||
What It Can Do
|
What It Can Do
|
||||||
==================================
|
==================================
|
||||||
@ -139,7 +140,7 @@ Here are the bindings to libgit2 that are currently available:
|
|||||||
* Parrot Virtual Machine
|
* Parrot Virtual Machine
|
||||||
* parrot-libgit2 <https://github.com/letolabs/parrot-libgit2>
|
* parrot-libgit2 <https://github.com/letolabs/parrot-libgit2>
|
||||||
* Perl
|
* Perl
|
||||||
* git-xs-pm <https://github.com/ingydotnet/git-xs-pm>
|
* Git-Raw <https://github.com/ghedo/p5-Git-Raw>
|
||||||
* PHP
|
* PHP
|
||||||
* php-git <https://github.com/libgit2/php-git>
|
* php-git <https://github.com/libgit2/php-git>
|
||||||
* Python
|
* Python
|
||||||
@ -155,15 +156,7 @@ we can add it to the list.
|
|||||||
How Can I Contribute?
|
How Can I Contribute?
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
Fork libgit2/libgit2 on GitHub, add your improvement, push it to a branch
|
Check the [contribution guidelines](CONTRIBUTING.md).
|
||||||
in your fork named for the topic, send a pull request. If you change the
|
|
||||||
API or make other large changes, make a note of it in docs/rel-notes/ in a
|
|
||||||
file named after the next release.
|
|
||||||
|
|
||||||
You can also file bugs or feature requests under the libgit2 project on
|
|
||||||
GitHub, or join us on the mailing list by sending an email to:
|
|
||||||
|
|
||||||
libgit2@librelist.com
|
|
||||||
|
|
||||||
|
|
||||||
License
|
License
|
||||||
|
39
cmake/Modules/FindHTTP_Parser.cmake
Normal file
39
cmake/Modules/FindHTTP_Parser.cmake
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# - Try to find http-parser
|
||||||
|
#
|
||||||
|
# Defines the following variables:
|
||||||
|
#
|
||||||
|
# HTTP_PARSER_FOUND - system has http-parser
|
||||||
|
# HTTP_PARSER_INCLUDE_DIR - the http-parser include directory
|
||||||
|
# HTTP_PARSER_LIBRARIES - Link these to use http-parser
|
||||||
|
# HTTP_PARSER_VERSION_MAJOR - major version
|
||||||
|
# HTTP_PARSER_VERSION_MINOR - minor version
|
||||||
|
# HTTP_PARSER_VERSION_STRING - the version of http-parser found
|
||||||
|
|
||||||
|
# Find the header and library
|
||||||
|
FIND_PATH(HTTP_PARSER_INCLUDE_DIR NAMES http_parser.h)
|
||||||
|
FIND_LIBRARY(HTTP_PARSER_LIBRARY NAMES http_parser libhttp_parser)
|
||||||
|
|
||||||
|
# Found the header, read version
|
||||||
|
if (HTTP_PARSER_INCLUDE_DIR AND EXISTS "${HTTP_PARSER_INCLUDE_DIR}/http_parser.h")
|
||||||
|
FILE(READ "${HTTP_PARSER_INCLUDE_DIR}/http_parser.h" HTTP_PARSER_H)
|
||||||
|
IF (HTTP_PARSER_H)
|
||||||
|
STRING(REGEX REPLACE ".*#define[\t ]+HTTP_PARSER_VERSION_MAJOR[\t ]+([0-9]+).*" "\\1" HTTP_PARSER_VERSION_MAJOR "${HTTP_PARSER_H}")
|
||||||
|
STRING(REGEX REPLACE ".*#define[\t ]+HTTP_PARSER_VERSION_MINOR[\t ]+([0-9]+).*" "\\1" HTTP_PARSER_VERSION_MINOR "${HTTP_PARSER_H}")
|
||||||
|
SET(HTTP_PARSER_VERSION_STRING "${HTTP_PARSER_VERSION_MAJOR}.${HTTP_PARSER_VERSION_MINOR}")
|
||||||
|
ENDIF()
|
||||||
|
UNSET(HTTP_PARSER_H)
|
||||||
|
ENDIF()
|
||||||
|
|
||||||
|
# Handle the QUIETLY and REQUIRED arguments and set HTTP_PARSER_FOUND
|
||||||
|
# to TRUE if all listed variables are TRUE
|
||||||
|
INCLUDE(FindPackageHandleStandardArgs)
|
||||||
|
FIND_PACKAGE_HANDLE_STANDARD_ARGS(HTTP_Parser REQUIRED_VARS HTTP_PARSER_INCLUDE_DIR HTTP_PARSER_LIBRARY)
|
||||||
|
|
||||||
|
# Hide advanced variables
|
||||||
|
MARK_AS_ADVANCED(HTTP_PARSER_INCLUDE_DIR HTTP_PARSER_LIBRARY)
|
||||||
|
|
||||||
|
# Set standard variables
|
||||||
|
IF (HTTP_PARSER_FOUND)
|
||||||
|
SET(HTTP_PARSER_LIBRARIES ${HTTP_PARSER_LIBRARY})
|
||||||
|
set(HTTP_PARSER_INCLUDE_DIRS ${HTTP_PARSER_INCLUDE_DIR})
|
||||||
|
ENDIF()
|
44
cmake/Modules/FindLIBSSH2.cmake
Normal file
44
cmake/Modules/FindLIBSSH2.cmake
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
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)
|
||||||
|
|
88
docs/diff-internals.md
Normal file
88
docs/diff-internals.md
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
Diff is broken into four phases:
|
||||||
|
|
||||||
|
1. Building a list of things that have changed. These changes are called
|
||||||
|
deltas (git_diff_delta objects) and are grouped into a git_diff_list.
|
||||||
|
2. Applying file similarity measurement for rename and copy detection (and
|
||||||
|
to potentially split files that have changed radically). This step is
|
||||||
|
optional.
|
||||||
|
3. Computing the textual diff for each delta. Not all deltas have a
|
||||||
|
meaningful textual diff. For those that do, the textual diff can
|
||||||
|
either be generated on the fly and passed to output callbacks or can be
|
||||||
|
turned into a git_diff_patch object.
|
||||||
|
4. Formatting the diff and/or patch into standard text formats (such as
|
||||||
|
patches, raw lists, etc).
|
||||||
|
|
||||||
|
In the source code, step 1 is implemented in `src/diff.c`, step 2 in
|
||||||
|
`src/diff_tform.c`, step 3 in `src/diff_patch.c`, and step 4 in
|
||||||
|
`src/diff_print.c`. Additionally, when it comes to accessing file
|
||||||
|
content, everything goes through diff drivers that are implemented in
|
||||||
|
`src/diff_driver.c`.
|
||||||
|
|
||||||
|
External Objects
|
||||||
|
----------------
|
||||||
|
|
||||||
|
* `git_diff_options` repesents 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
|
||||||
|
way - it contains two `git_diff_file` plus a status and other stuff.
|
||||||
|
* `git_diff_list` is a list of deltas along with information about how
|
||||||
|
those particular deltas were found.
|
||||||
|
* `git_diff_patch` represents the actual diff between a pair of items. In
|
||||||
|
some cases, a delta may not have a corresponding patch, if the objects
|
||||||
|
are binary, for example. The content of a patch will be a set of hunks
|
||||||
|
and lines.
|
||||||
|
* A `hunk` is range of lines described by a `git_diff_range` (i.e. "lines
|
||||||
|
10-20 in the old file became lines 12-23 in the new"). It will have a
|
||||||
|
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).
|
||||||
|
|
||||||
|
Internal Objects
|
||||||
|
----------------
|
||||||
|
|
||||||
|
* `git_diff_file_content` is an internal structure that represents the
|
||||||
|
data on one side of an item to be diffed; it is an augmented
|
||||||
|
`git_diff_file` with more flags and the actual file data.
|
||||||
|
** it is created from a repository plus a) a git_diff_file, b) a git_blob,
|
||||||
|
or c) raw data and size
|
||||||
|
** there are three main operations on git_diff_file_content:
|
||||||
|
*** _initialization_ sets up the data structure and does what it can up to,
|
||||||
|
but not including loading and looking at the actual data
|
||||||
|
*** _loading_ loads the data, preprocesses it (i.e. applies filters) and
|
||||||
|
potentially analyzes it (to decide if binary)
|
||||||
|
*** _free_ releases loaded data and frees any allocated memory
|
||||||
|
|
||||||
|
* The internal structure of a `git_diff_patch` stores the actual diff
|
||||||
|
between a pair of `git_diff_file_content` items
|
||||||
|
** it may be "unset" if the items are not diffable
|
||||||
|
** "empty" if the items are the same
|
||||||
|
** otherwise it will consist of a set of hunks each of which covers some
|
||||||
|
number of lines of context, additions and deletions
|
||||||
|
** a patch is created from two git_diff_file_content items
|
||||||
|
** a patch is fully instantiated in three phases:
|
||||||
|
*** initial creation and initialization
|
||||||
|
*** loading of data and preliminary data examination
|
||||||
|
*** diffing of data and optional storage of diffs
|
||||||
|
** (TBD) if a patch is asked to store the diffs and the size of the diff
|
||||||
|
is significantly smaller than the raw data of the two sides, then the
|
||||||
|
patch may be flattened using a pool of string data
|
||||||
|
|
||||||
|
* `git_diff_output` is an internal structure that represents an output
|
||||||
|
target for a `git_diff_patch`
|
||||||
|
** It consists of file, hunk, and line callbacks, plus a payload
|
||||||
|
** There is a standard flattened output that can be used for plain text output
|
||||||
|
** Typically we use a `git_xdiff_output` which drives the callbacks via the
|
||||||
|
xdiff code taken from core Git.
|
||||||
|
|
||||||
|
* `git_diff_driver` is an internal structure that encapsulates the logic
|
||||||
|
for a given type of file
|
||||||
|
** a driver is looked up based on the name and mode of a file.
|
||||||
|
** the driver can then be used to:
|
||||||
|
*** determine if a file is binary (by attributes, by git_diff_options
|
||||||
|
settings, or by examining the content)
|
||||||
|
*** give you a function pointer that is used to evaluate function context
|
||||||
|
for hunk headers
|
||||||
|
** At some point, the logic for getting a filtered version of file content
|
||||||
|
or calculating the OID of a file may be moved into the driver.
|
41
docs/merge-df_conflicts.txt
Normal file
41
docs/merge-df_conflicts.txt
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
Anc / Our / Thr represent the ancestor / ours / theirs side of a merge
|
||||||
|
from branch "branch" into HEAD. Workdir represents the expected files in
|
||||||
|
the working directory. Index represents the expected files in the index,
|
||||||
|
with stage markers.
|
||||||
|
|
||||||
|
Anc Our Thr Workdir Index
|
||||||
|
1 D D
|
||||||
|
D/F D/F D/F [0]
|
||||||
|
|
||||||
|
2 D D+ D~HEAD (mod/del) D/F [0]
|
||||||
|
D/F D/F D [1]
|
||||||
|
D [2]
|
||||||
|
|
||||||
|
3 D D D/F D/F [0]
|
||||||
|
D/F
|
||||||
|
|
||||||
|
4 D D+ D~branch (mod/del) D/F [0]
|
||||||
|
D/F D/F D [1]
|
||||||
|
D [3]
|
||||||
|
|
||||||
|
5 D D/F (add/add) D/F [2]
|
||||||
|
D/F D/F [3]
|
||||||
|
D/F
|
||||||
|
|
||||||
|
6 D/F D/F D D [0]
|
||||||
|
D
|
||||||
|
|
||||||
|
7 D/F D/F+ D/F (mod/del) D/F [1]
|
||||||
|
D D~branch (fil/dir) D/F [2]
|
||||||
|
D [3]
|
||||||
|
|
||||||
|
8 D/F D/F D D [0]
|
||||||
|
D
|
||||||
|
|
||||||
|
9 D/F D/F+ D/F (mod/del) D/F [1]
|
||||||
|
D D~HEAD (fil/dir) D [2]
|
||||||
|
D/F [3]
|
||||||
|
|
||||||
|
10 D/F D/F (fil/dir) D/F [0]
|
||||||
|
D D~HEAD D [2]
|
||||||
|
D
|
@ -3,7 +3,7 @@
|
|||||||
CC = gcc
|
CC = gcc
|
||||||
CFLAGS = -g -I../include -I../src -Wall -Wextra -Wmissing-prototypes -Wno-missing-field-initializers
|
CFLAGS = -g -I../include -I../src -Wall -Wextra -Wmissing-prototypes -Wno-missing-field-initializers
|
||||||
LFLAGS = -L../build -lgit2 -lz
|
LFLAGS = -L../build -lgit2 -lz
|
||||||
APPS = general showindex diff rev-list
|
APPS = general showindex diff rev-list cat-file status
|
||||||
|
|
||||||
all: $(APPS)
|
all: $(APPS)
|
||||||
|
|
||||||
|
229
examples/cat-file.c
Normal file
229
examples/cat-file.c
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <git2.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static git_repository *g_repo;
|
||||||
|
|
||||||
|
static void check(int error, const char *message)
|
||||||
|
{
|
||||||
|
if (error) {
|
||||||
|
fprintf(stderr, "%s (%d)\n", message, error);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usage(const char *message, const char *arg)
|
||||||
|
{
|
||||||
|
if (message && arg)
|
||||||
|
fprintf(stderr, "%s: %s\n", message, arg);
|
||||||
|
else if (message)
|
||||||
|
fprintf(stderr, "%s\n", message);
|
||||||
|
fprintf(stderr, "usage: cat-file (-t | -s | -e | -p) [<options>] <object>\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int check_str_param(
|
||||||
|
const char *arg, const char *pattern, const char **val)
|
||||||
|
{
|
||||||
|
size_t len = strlen(pattern);
|
||||||
|
if (strncmp(arg, pattern, len))
|
||||||
|
return 0;
|
||||||
|
*val = (const char *)(arg + len);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_signature(const char *header, const git_signature *sig)
|
||||||
|
{
|
||||||
|
char sign;
|
||||||
|
int offset, hours, minutes;
|
||||||
|
|
||||||
|
if (!sig)
|
||||||
|
return;
|
||||||
|
|
||||||
|
offset = sig->when.offset;
|
||||||
|
if (offset < 0) {
|
||||||
|
sign = '-';
|
||||||
|
offset = -offset;
|
||||||
|
} else {
|
||||||
|
sign = '+';
|
||||||
|
}
|
||||||
|
|
||||||
|
hours = offset / 60;
|
||||||
|
minutes = offset % 60;
|
||||||
|
|
||||||
|
printf("%s %s <%s> %ld %c%02d%02d\n",
|
||||||
|
header, sig->name, sig->email, (long)sig->when.time,
|
||||||
|
sign, hours, minutes);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show_blob(const git_blob *blob)
|
||||||
|
{
|
||||||
|
/* ? Does this need crlf filtering? */
|
||||||
|
fwrite(git_blob_rawcontent(blob), git_blob_rawsize(blob), 1, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show_tree(const git_tree *tree)
|
||||||
|
{
|
||||||
|
size_t i, max_i = (int)git_tree_entrycount(tree);
|
||||||
|
char oidstr[GIT_OID_HEXSZ + 1];
|
||||||
|
const git_tree_entry *te;
|
||||||
|
|
||||||
|
for (i = 0; i < max_i; ++i) {
|
||||||
|
te = git_tree_entry_byindex(tree, i);
|
||||||
|
|
||||||
|
git_oid_tostr(oidstr, sizeof(oidstr), git_tree_entry_id(te));
|
||||||
|
|
||||||
|
printf("%06o %s %s\t%s\n",
|
||||||
|
git_tree_entry_filemode(te),
|
||||||
|
git_object_type2string(git_tree_entry_type(te)),
|
||||||
|
oidstr, git_tree_entry_name(te));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show_commit(const git_commit *commit)
|
||||||
|
{
|
||||||
|
unsigned int i, max_i;
|
||||||
|
char oidstr[GIT_OID_HEXSZ + 1];
|
||||||
|
|
||||||
|
git_oid_tostr(oidstr, sizeof(oidstr), git_commit_tree_id(commit));
|
||||||
|
printf("tree %s\n", oidstr);
|
||||||
|
|
||||||
|
max_i = (unsigned int)git_commit_parentcount(commit);
|
||||||
|
for (i = 0; i < max_i; ++i) {
|
||||||
|
git_oid_tostr(oidstr, sizeof(oidstr), git_commit_parent_id(commit, i));
|
||||||
|
printf("parent %s\n", oidstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
print_signature("author", git_commit_author(commit));
|
||||||
|
print_signature("committer", git_commit_committer(commit));
|
||||||
|
|
||||||
|
if (git_commit_message(commit))
|
||||||
|
printf("\n%s\n", git_commit_message(commit));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show_tag(const git_tag *tag)
|
||||||
|
{
|
||||||
|
char oidstr[GIT_OID_HEXSZ + 1];
|
||||||
|
|
||||||
|
git_oid_tostr(oidstr, sizeof(oidstr), git_tag_target_id(tag));;
|
||||||
|
printf("object %s\n", oidstr);
|
||||||
|
printf("type %s\n", git_object_type2string(git_tag_target_type(tag)));
|
||||||
|
printf("tag %s\n", git_tag_name(tag));
|
||||||
|
print_signature("tagger", git_tag_tagger(tag));
|
||||||
|
|
||||||
|
if (git_tag_message(tag))
|
||||||
|
printf("\n%s\n", git_tag_message(tag));
|
||||||
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SHOW_TYPE = 1,
|
||||||
|
SHOW_SIZE = 2,
|
||||||
|
SHOW_NONE = 3,
|
||||||
|
SHOW_PRETTY = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
const char *dir = ".", *rev = NULL;
|
||||||
|
int i, action = 0, verbose = 0;
|
||||||
|
git_object *obj = NULL;
|
||||||
|
char oidstr[GIT_OID_HEXSZ + 1];
|
||||||
|
|
||||||
|
git_threads_init();
|
||||||
|
|
||||||
|
for (i = 1; i < argc; ++i) {
|
||||||
|
char *a = argv[i];
|
||||||
|
|
||||||
|
if (a[0] != '-') {
|
||||||
|
if (rev != NULL)
|
||||||
|
usage("Only one rev should be provided", NULL);
|
||||||
|
else
|
||||||
|
rev = a;
|
||||||
|
}
|
||||||
|
else if (!strcmp(a, "-t"))
|
||||||
|
action = SHOW_TYPE;
|
||||||
|
else if (!strcmp(a, "-s"))
|
||||||
|
action = SHOW_SIZE;
|
||||||
|
else if (!strcmp(a, "-e"))
|
||||||
|
action = SHOW_NONE;
|
||||||
|
else if (!strcmp(a, "-p"))
|
||||||
|
action = SHOW_PRETTY;
|
||||||
|
else if (!strcmp(a, "-q"))
|
||||||
|
verbose = 0;
|
||||||
|
else if (!strcmp(a, "-v"))
|
||||||
|
verbose = 1;
|
||||||
|
else if (!strcmp(a, "--help") || !strcmp(a, "-h"))
|
||||||
|
usage(NULL, NULL);
|
||||||
|
else if (!check_str_param(a, "--git-dir=", &dir))
|
||||||
|
usage("Unknown option", a);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!action || !rev)
|
||||||
|
usage(NULL, NULL);
|
||||||
|
|
||||||
|
check(git_repository_open_ext(&g_repo, dir, 0, NULL),
|
||||||
|
"Could not open repository");
|
||||||
|
|
||||||
|
if (git_revparse_single(&obj, g_repo, rev) < 0) {
|
||||||
|
fprintf(stderr, "Could not resolve '%s'\n", rev);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
if (verbose) {
|
||||||
|
char oidstr[GIT_OID_HEXSZ + 1];
|
||||||
|
git_oid_tostr(oidstr, sizeof(oidstr), git_object_id(obj));
|
||||||
|
|
||||||
|
printf("%s %s\n--\n",
|
||||||
|
git_object_type2string(git_object_type(obj)), oidstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case SHOW_TYPE:
|
||||||
|
printf("%s\n", git_object_type2string(git_object_type(obj)));
|
||||||
|
break;
|
||||||
|
case SHOW_SIZE: {
|
||||||
|
git_odb *odb;
|
||||||
|
git_odb_object *odbobj;
|
||||||
|
|
||||||
|
check(git_repository_odb(&odb, g_repo), "Could not open ODB");
|
||||||
|
check(git_odb_read(&odbobj, odb, git_object_id(obj)),
|
||||||
|
"Could not find obj");
|
||||||
|
|
||||||
|
printf("%ld\n", (long)git_odb_object_size(odbobj));
|
||||||
|
|
||||||
|
git_odb_object_free(odbobj);
|
||||||
|
git_odb_free(odb);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SHOW_NONE:
|
||||||
|
/* just want return result */
|
||||||
|
break;
|
||||||
|
case SHOW_PRETTY:
|
||||||
|
|
||||||
|
switch (git_object_type(obj)) {
|
||||||
|
case GIT_OBJ_BLOB:
|
||||||
|
show_blob((const git_blob *)obj);
|
||||||
|
break;
|
||||||
|
case GIT_OBJ_COMMIT:
|
||||||
|
show_commit((const git_commit *)obj);
|
||||||
|
break;
|
||||||
|
case GIT_OBJ_TREE:
|
||||||
|
show_tree((const git_tree *)obj);
|
||||||
|
break;
|
||||||
|
case GIT_OBJ_TAG:
|
||||||
|
show_tag((const git_tag *)obj);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printf("unknown %s\n", oidstr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
git_object_free(obj);
|
||||||
|
git_repository_free(g_repo);
|
||||||
|
|
||||||
|
git_threads_shutdown();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@ -84,6 +84,10 @@ static int check_uint16_param(const char *arg, const char *pattern, uint16_t *va
|
|||||||
char *endptr = NULL;
|
char *endptr = NULL;
|
||||||
if (strncmp(arg, pattern, len))
|
if (strncmp(arg, pattern, len))
|
||||||
return 0;
|
return 0;
|
||||||
|
if (arg[len] == '\0' && pattern[len - 1] != '=')
|
||||||
|
return 1;
|
||||||
|
if (arg[len] == '=')
|
||||||
|
len++;
|
||||||
strval = strtoul(arg + len, &endptr, 0);
|
strval = strtoul(arg + len, &endptr, 0);
|
||||||
if (endptr == arg)
|
if (endptr == arg)
|
||||||
return 0;
|
return 0;
|
||||||
@ -110,14 +114,24 @@ static void usage(const char *message, const char *arg)
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
FORMAT_PATCH = 0,
|
||||||
|
FORMAT_COMPACT = 1,
|
||||||
|
FORMAT_RAW = 2
|
||||||
|
};
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
git_repository *repo = NULL;
|
git_repository *repo = NULL;
|
||||||
git_tree *t1 = NULL, *t2 = NULL;
|
git_tree *t1 = NULL, *t2 = NULL;
|
||||||
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
||||||
|
git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
|
||||||
git_diff_list *diff;
|
git_diff_list *diff;
|
||||||
int i, color = -1, compact = 0, cached = 0;
|
int i, color = -1, format = FORMAT_PATCH, cached = 0;
|
||||||
char *a, *dir = ".", *treeish1 = NULL, *treeish2 = NULL;
|
char *a, *treeish1 = NULL, *treeish2 = NULL;
|
||||||
|
const char *dir = ".";
|
||||||
|
|
||||||
|
git_threads_init();
|
||||||
|
|
||||||
/* parse arguments as copied from git-diff */
|
/* parse arguments as copied from git-diff */
|
||||||
|
|
||||||
@ -134,11 +148,13 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
else if (!strcmp(a, "-p") || !strcmp(a, "-u") ||
|
else if (!strcmp(a, "-p") || !strcmp(a, "-u") ||
|
||||||
!strcmp(a, "--patch"))
|
!strcmp(a, "--patch"))
|
||||||
compact = 0;
|
format = FORMAT_PATCH;
|
||||||
else if (!strcmp(a, "--cached"))
|
else if (!strcmp(a, "--cached"))
|
||||||
cached = 1;
|
cached = 1;
|
||||||
else if (!strcmp(a, "--name-status"))
|
else if (!strcmp(a, "--name-status"))
|
||||||
compact = 1;
|
format = FORMAT_COMPACT;
|
||||||
|
else if (!strcmp(a, "--raw"))
|
||||||
|
format = FORMAT_RAW;
|
||||||
else if (!strcmp(a, "--color"))
|
else if (!strcmp(a, "--color"))
|
||||||
color = 0;
|
color = 0;
|
||||||
else if (!strcmp(a, "--no-color"))
|
else if (!strcmp(a, "--no-color"))
|
||||||
@ -157,12 +173,27 @@ int main(int argc, char *argv[])
|
|||||||
opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
|
opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
|
||||||
else if (!strcmp(a, "--untracked"))
|
else if (!strcmp(a, "--untracked"))
|
||||||
opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
|
opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
|
||||||
|
else if (check_uint16_param(a, "-M", &findopts.rename_threshold) ||
|
||||||
|
check_uint16_param(a, "--find-renames",
|
||||||
|
&findopts.rename_threshold))
|
||||||
|
findopts.flags |= GIT_DIFF_FIND_RENAMES;
|
||||||
|
else if (check_uint16_param(a, "-C", &findopts.copy_threshold) ||
|
||||||
|
check_uint16_param(a, "--find-copies",
|
||||||
|
&findopts.copy_threshold))
|
||||||
|
findopts.flags |= GIT_DIFF_FIND_COPIES;
|
||||||
|
else if (!strcmp(a, "--find-copies-harder"))
|
||||||
|
findopts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
|
||||||
|
else if (!strncmp(a, "-B", 2) || !strncmp(a, "--break-rewrites", 16)) {
|
||||||
|
/* TODO: parse thresholds */
|
||||||
|
findopts.flags |= GIT_DIFF_FIND_REWRITES;
|
||||||
|
}
|
||||||
else if (!check_uint16_param(a, "-U", &opts.context_lines) &&
|
else if (!check_uint16_param(a, "-U", &opts.context_lines) &&
|
||||||
!check_uint16_param(a, "--unified=", &opts.context_lines) &&
|
!check_uint16_param(a, "--unified=", &opts.context_lines) &&
|
||||||
!check_uint16_param(a, "--inter-hunk-context=",
|
!check_uint16_param(a, "--inter-hunk-context=",
|
||||||
&opts.interhunk_lines) &&
|
&opts.interhunk_lines) &&
|
||||||
!check_str_param(a, "--src-prefix=", &opts.old_prefix) &&
|
!check_str_param(a, "--src-prefix=", &opts.old_prefix) &&
|
||||||
!check_str_param(a, "--dst-prefix=", &opts.new_prefix))
|
!check_str_param(a, "--dst-prefix=", &opts.new_prefix) &&
|
||||||
|
!check_str_param(a, "--git-dir=", &dir))
|
||||||
usage("Unknown arg", a);
|
usage("Unknown arg", a);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,13 +231,24 @@ int main(int argc, char *argv[])
|
|||||||
else
|
else
|
||||||
check(git_diff_index_to_workdir(&diff, repo, NULL, &opts), "Diff");
|
check(git_diff_index_to_workdir(&diff, repo, NULL, &opts), "Diff");
|
||||||
|
|
||||||
|
if ((findopts.flags & GIT_DIFF_FIND_ALL) != 0)
|
||||||
|
check(git_diff_find_similar(diff, &findopts),
|
||||||
|
"finding renames and copies ");
|
||||||
|
|
||||||
if (color >= 0)
|
if (color >= 0)
|
||||||
fputs(colors[0], stdout);
|
fputs(colors[0], stdout);
|
||||||
|
|
||||||
if (compact)
|
switch (format) {
|
||||||
check(git_diff_print_compact(diff, printer, &color), "Displaying diff");
|
case FORMAT_PATCH:
|
||||||
else
|
|
||||||
check(git_diff_print_patch(diff, printer, &color), "Displaying diff");
|
check(git_diff_print_patch(diff, printer, &color), "Displaying diff");
|
||||||
|
break;
|
||||||
|
case FORMAT_COMPACT:
|
||||||
|
check(git_diff_print_compact(diff, printer, &color), "Displaying diff");
|
||||||
|
break;
|
||||||
|
case FORMAT_RAW:
|
||||||
|
check(git_diff_print_raw(diff, printer, &color), "Displaying diff");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (color >= 0)
|
if (color >= 0)
|
||||||
fputs(colors[0], stdout);
|
fputs(colors[0], stdout);
|
||||||
@ -216,6 +258,8 @@ int main(int argc, char *argv[])
|
|||||||
git_tree_free(t2);
|
git_tree_free(t2);
|
||||||
git_repository_free(repo);
|
git_repository_free(repo);
|
||||||
|
|
||||||
|
git_threads_shutdown();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,7 +453,7 @@ int main (int argc, char** argv)
|
|||||||
// Here we will implement something like `git for-each-ref` simply listing
|
// Here we will implement something like `git for-each-ref` simply listing
|
||||||
// out all available references and the object SHA they resolve to.
|
// out all available references and the object SHA they resolve to.
|
||||||
git_strarray ref_list;
|
git_strarray ref_list;
|
||||||
git_reference_list(&ref_list, repo, GIT_REF_LISTALL);
|
git_reference_list(&ref_list, repo);
|
||||||
|
|
||||||
const char *refname;
|
const char *refname;
|
||||||
git_reference *ref;
|
git_reference *ref;
|
||||||
|
@ -54,6 +54,8 @@ int main(int argc, char **argv)
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
git_threads_init();
|
||||||
|
|
||||||
for (i = 0; commands[i].name != NULL; ++i) {
|
for (i = 0; commands[i].name != NULL; ++i) {
|
||||||
if (!strcmp(argv[1], commands[i].name))
|
if (!strcmp(argv[1], commands[i].name))
|
||||||
return run_command(commands[i].fn, --argc, ++argv);
|
return run_command(commands[i].fn, --argc, ++argv);
|
||||||
|
@ -117,4 +117,3 @@ int main (int argc, char **argv)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
443
examples/status.c
Normal file
443
examples/status.c
Normal file
@ -0,0 +1,443 @@
|
|||||||
|
/*
|
||||||
|
* 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 <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
enum {
|
||||||
|
FORMAT_DEFAULT = 0,
|
||||||
|
FORMAT_LONG = 1,
|
||||||
|
FORMAT_SHORT = 2,
|
||||||
|
FORMAT_PORCELAIN = 3,
|
||||||
|
};
|
||||||
|
#define MAX_PATHSPEC 8
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This example demonstrates the use of the libgit2 status APIs,
|
||||||
|
* particularly the `git_status_list` object, to roughly simulate the
|
||||||
|
* output of running `git status`. It serves as a simple example of
|
||||||
|
* using those APIs to get basic status information.
|
||||||
|
*
|
||||||
|
* This does not have:
|
||||||
|
* - Robust error handling
|
||||||
|
* - Colorized or paginated output formatting
|
||||||
|
*
|
||||||
|
* This does have:
|
||||||
|
* - Examples of translating command line arguments to the status
|
||||||
|
* options settings to mimic `git status` results.
|
||||||
|
* - A sample status formatter that matches the default "long" format
|
||||||
|
* from `git status`
|
||||||
|
* - A sample status formatter that matches the "short" format
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void check(int error, const char *message, const char *extra)
|
||||||
|
{
|
||||||
|
const git_error *lg2err;
|
||||||
|
const char *lg2msg = "", *lg2spacer = "";
|
||||||
|
|
||||||
|
if (!error)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((lg2err = giterr_last()) != NULL && lg2err->message != NULL) {
|
||||||
|
lg2msg = lg2err->message;
|
||||||
|
lg2spacer = " - ";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extra)
|
||||||
|
fprintf(stderr, "%s '%s' [%d]%s%s\n",
|
||||||
|
message, extra, error, lg2spacer, lg2msg);
|
||||||
|
else
|
||||||
|
fprintf(stderr, "%s [%d]%s%s\n",
|
||||||
|
message, error, lg2spacer, lg2msg);
|
||||||
|
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fail(const char *message)
|
||||||
|
{
|
||||||
|
check(-1, message, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void show_branch(git_repository *repo, int format)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
const char *branch = NULL;
|
||||||
|
git_reference *head = NULL;
|
||||||
|
|
||||||
|
error = git_repository_head(&head, repo);
|
||||||
|
|
||||||
|
if (error == GIT_EORPHANEDHEAD || error == GIT_ENOTFOUND)
|
||||||
|
branch = NULL;
|
||||||
|
else if (!error) {
|
||||||
|
branch = git_reference_name(head);
|
||||||
|
if (!strncmp(branch, "refs/heads/", strlen("refs/heads/")))
|
||||||
|
branch += strlen("refs/heads/");
|
||||||
|
} else
|
||||||
|
check(error, "failed to get current branch", NULL);
|
||||||
|
|
||||||
|
if (format == FORMAT_LONG)
|
||||||
|
printf("# On branch %s\n",
|
||||||
|
branch ? branch : "Not currently on any branch.");
|
||||||
|
else
|
||||||
|
printf("## %s\n", branch ? branch : "HEAD (no branch)");
|
||||||
|
|
||||||
|
git_reference_free(head);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_long(git_repository *repo, git_status_list *status)
|
||||||
|
{
|
||||||
|
size_t i, maxi = git_status_list_entrycount(status);
|
||||||
|
const git_status_entry *s;
|
||||||
|
int header = 0, changes_in_index = 0;
|
||||||
|
int changed_in_workdir = 0, rm_in_workdir = 0;
|
||||||
|
const char *old_path, *new_path;
|
||||||
|
|
||||||
|
(void)repo;
|
||||||
|
|
||||||
|
/* print index changes */
|
||||||
|
|
||||||
|
for (i = 0; i < maxi; ++i) {
|
||||||
|
char *istatus = NULL;
|
||||||
|
|
||||||
|
s = git_status_byindex(status, i);
|
||||||
|
|
||||||
|
if (s->status == GIT_STATUS_CURRENT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (s->status & GIT_STATUS_WT_DELETED)
|
||||||
|
rm_in_workdir = 1;
|
||||||
|
|
||||||
|
if (s->status & GIT_STATUS_INDEX_NEW)
|
||||||
|
istatus = "new file: ";
|
||||||
|
if (s->status & GIT_STATUS_INDEX_MODIFIED)
|
||||||
|
istatus = "modified: ";
|
||||||
|
if (s->status & GIT_STATUS_INDEX_DELETED)
|
||||||
|
istatus = "deleted: ";
|
||||||
|
if (s->status & GIT_STATUS_INDEX_RENAMED)
|
||||||
|
istatus = "renamed: ";
|
||||||
|
if (s->status & GIT_STATUS_INDEX_TYPECHANGE)
|
||||||
|
istatus = "typechange:";
|
||||||
|
|
||||||
|
if (istatus == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!header) {
|
||||||
|
printf("# Changes to be committed:\n");
|
||||||
|
printf("# (use \"git reset HEAD <file>...\" to unstage)\n");
|
||||||
|
printf("#\n");
|
||||||
|
header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_path = s->head_to_index->old_file.path;
|
||||||
|
new_path = s->head_to_index->new_file.path;
|
||||||
|
|
||||||
|
if (old_path && new_path && strcmp(old_path, new_path))
|
||||||
|
printf("#\t%s %s -> %s\n", istatus, old_path, new_path);
|
||||||
|
else
|
||||||
|
printf("#\t%s %s\n", istatus, old_path ? old_path : new_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header) {
|
||||||
|
changes_in_index = 1;
|
||||||
|
printf("#\n");
|
||||||
|
}
|
||||||
|
header = 0;
|
||||||
|
|
||||||
|
/* print workdir changes to tracked files */
|
||||||
|
|
||||||
|
for (i = 0; i < maxi; ++i) {
|
||||||
|
char *wstatus = NULL;
|
||||||
|
|
||||||
|
s = git_status_byindex(status, i);
|
||||||
|
|
||||||
|
if (s->status == GIT_STATUS_CURRENT || s->index_to_workdir == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (s->status & GIT_STATUS_WT_MODIFIED)
|
||||||
|
wstatus = "modified: ";
|
||||||
|
if (s->status & GIT_STATUS_WT_DELETED)
|
||||||
|
wstatus = "deleted: ";
|
||||||
|
if (s->status & GIT_STATUS_WT_RENAMED)
|
||||||
|
wstatus = "renamed: ";
|
||||||
|
if (s->status & GIT_STATUS_WT_TYPECHANGE)
|
||||||
|
wstatus = "typechange:";
|
||||||
|
|
||||||
|
if (wstatus == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!header) {
|
||||||
|
printf("# Changes not staged for commit:\n");
|
||||||
|
printf("# (use \"git add%s <file>...\" to update what will be committed)\n", rm_in_workdir ? "/rm" : "");
|
||||||
|
printf("# (use \"git checkout -- <file>...\" to discard changes in working directory)\n");
|
||||||
|
printf("#\n");
|
||||||
|
header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_path = s->index_to_workdir->old_file.path;
|
||||||
|
new_path = s->index_to_workdir->new_file.path;
|
||||||
|
|
||||||
|
if (old_path && new_path && strcmp(old_path, new_path))
|
||||||
|
printf("#\t%s %s -> %s\n", wstatus, old_path, new_path);
|
||||||
|
else
|
||||||
|
printf("#\t%s %s\n", wstatus, old_path ? old_path : new_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header) {
|
||||||
|
changed_in_workdir = 1;
|
||||||
|
printf("#\n");
|
||||||
|
}
|
||||||
|
header = 0;
|
||||||
|
|
||||||
|
/* print untracked files */
|
||||||
|
|
||||||
|
header = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < maxi; ++i) {
|
||||||
|
s = git_status_byindex(status, i);
|
||||||
|
|
||||||
|
if (s->status == GIT_STATUS_WT_NEW) {
|
||||||
|
|
||||||
|
if (!header) {
|
||||||
|
printf("# Untracked files:\n");
|
||||||
|
printf("# (use \"git add <file>...\" to include in what will be committed)\n");
|
||||||
|
printf("#\n");
|
||||||
|
header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("#\t%s\n", s->index_to_workdir->old_file.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header = 0;
|
||||||
|
|
||||||
|
/* print ignored files */
|
||||||
|
|
||||||
|
for (i = 0; i < maxi; ++i) {
|
||||||
|
s = git_status_byindex(status, i);
|
||||||
|
|
||||||
|
if (s->status == GIT_STATUS_IGNORED) {
|
||||||
|
|
||||||
|
if (!header) {
|
||||||
|
printf("# Ignored files:\n");
|
||||||
|
printf("# (use \"git add -f <file>...\" to include in what will be committed)\n");
|
||||||
|
printf("#\n");
|
||||||
|
header = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("#\t%s\n", s->index_to_workdir->old_file.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!changes_in_index && changed_in_workdir)
|
||||||
|
printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_short(git_repository *repo, git_status_list *status)
|
||||||
|
{
|
||||||
|
size_t i, maxi = git_status_list_entrycount(status);
|
||||||
|
const git_status_entry *s;
|
||||||
|
char istatus, wstatus;
|
||||||
|
const char *extra, *a, *b, *c;
|
||||||
|
|
||||||
|
for (i = 0; i < maxi; ++i) {
|
||||||
|
s = git_status_byindex(status, i);
|
||||||
|
|
||||||
|
if (s->status == GIT_STATUS_CURRENT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
a = b = c = NULL;
|
||||||
|
istatus = wstatus = ' ';
|
||||||
|
extra = "";
|
||||||
|
|
||||||
|
if (s->status & GIT_STATUS_INDEX_NEW)
|
||||||
|
istatus = 'A';
|
||||||
|
if (s->status & GIT_STATUS_INDEX_MODIFIED)
|
||||||
|
istatus = 'M';
|
||||||
|
if (s->status & GIT_STATUS_INDEX_DELETED)
|
||||||
|
istatus = 'D';
|
||||||
|
if (s->status & GIT_STATUS_INDEX_RENAMED)
|
||||||
|
istatus = 'R';
|
||||||
|
if (s->status & GIT_STATUS_INDEX_TYPECHANGE)
|
||||||
|
istatus = 'T';
|
||||||
|
|
||||||
|
if (s->status & GIT_STATUS_WT_NEW) {
|
||||||
|
if (istatus == ' ')
|
||||||
|
istatus = '?';
|
||||||
|
wstatus = '?';
|
||||||
|
}
|
||||||
|
if (s->status & GIT_STATUS_WT_MODIFIED)
|
||||||
|
wstatus = 'M';
|
||||||
|
if (s->status & GIT_STATUS_WT_DELETED)
|
||||||
|
wstatus = 'D';
|
||||||
|
if (s->status & GIT_STATUS_WT_RENAMED)
|
||||||
|
wstatus = 'R';
|
||||||
|
if (s->status & GIT_STATUS_WT_TYPECHANGE)
|
||||||
|
wstatus = 'T';
|
||||||
|
|
||||||
|
if (s->status & GIT_STATUS_IGNORED) {
|
||||||
|
istatus = '!';
|
||||||
|
wstatus = '!';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (istatus == '?' && wstatus == '?')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (s->index_to_workdir &&
|
||||||
|
s->index_to_workdir->new_file.mode == GIT_FILEMODE_COMMIT)
|
||||||
|
{
|
||||||
|
git_submodule *sm = NULL;
|
||||||
|
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)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (s->head_to_index) {
|
||||||
|
a = s->head_to_index->old_file.path;
|
||||||
|
b = s->head_to_index->new_file.path;
|
||||||
|
}
|
||||||
|
if (s->index_to_workdir) {
|
||||||
|
if (!a)
|
||||||
|
a = s->index_to_workdir->old_file.path;
|
||||||
|
if (!b)
|
||||||
|
b = s->index_to_workdir->old_file.path;
|
||||||
|
c = s->index_to_workdir->new_file.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (istatus == 'R') {
|
||||||
|
if (wstatus == 'R')
|
||||||
|
printf("%c%c %s %s %s%s\n", istatus, wstatus, a, b, c, extra);
|
||||||
|
else
|
||||||
|
printf("%c%c %s %s%s\n", istatus, wstatus, a, b, extra);
|
||||||
|
} else {
|
||||||
|
if (wstatus == 'R')
|
||||||
|
printf("%c%c %s %s%s\n", istatus, wstatus, a, c, extra);
|
||||||
|
else
|
||||||
|
printf("%c%c %s%s\n", istatus, wstatus, a, extra);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < maxi; ++i) {
|
||||||
|
s = git_status_byindex(status, i);
|
||||||
|
|
||||||
|
if (s->status == GIT_STATUS_WT_NEW)
|
||||||
|
printf("?? %s\n", s->index_to_workdir->old_file.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
git_repository *repo = NULL;
|
||||||
|
int i, npaths = 0, format = FORMAT_DEFAULT, zterm = 0, showbranch = 0;
|
||||||
|
git_status_options opt = GIT_STATUS_OPTIONS_INIT;
|
||||||
|
git_status_list *status;
|
||||||
|
char *repodir = ".", *pathspec[MAX_PATHSPEC];
|
||||||
|
|
||||||
|
opt.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
|
||||||
|
opt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
|
||||||
|
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
|
||||||
|
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
|
||||||
|
|
||||||
|
for (i = 1; i < argc; ++i) {
|
||||||
|
if (argv[i][0] != '-') {
|
||||||
|
if (npaths < MAX_PATHSPEC)
|
||||||
|
pathspec[npaths++] = argv[i];
|
||||||
|
else
|
||||||
|
fail("Example only supports a limited pathspec");
|
||||||
|
}
|
||||||
|
else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--short"))
|
||||||
|
format = FORMAT_SHORT;
|
||||||
|
else if (!strcmp(argv[i], "--long"))
|
||||||
|
format = FORMAT_LONG;
|
||||||
|
else if (!strcmp(argv[i], "--porcelain"))
|
||||||
|
format = FORMAT_PORCELAIN;
|
||||||
|
else if (!strcmp(argv[i], "-b") || !strcmp(argv[i], "--branch"))
|
||||||
|
showbranch = 1;
|
||||||
|
else if (!strcmp(argv[i], "-z")) {
|
||||||
|
zterm = 1;
|
||||||
|
if (format == FORMAT_DEFAULT)
|
||||||
|
format = FORMAT_PORCELAIN;
|
||||||
|
}
|
||||||
|
else if (!strcmp(argv[i], "--ignored"))
|
||||||
|
opt.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
|
||||||
|
else if (!strcmp(argv[i], "-uno") ||
|
||||||
|
!strcmp(argv[i], "--untracked-files=no"))
|
||||||
|
opt.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED;
|
||||||
|
else if (!strcmp(argv[i], "-unormal") ||
|
||||||
|
!strcmp(argv[i], "--untracked-files=normal"))
|
||||||
|
opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
|
||||||
|
else if (!strcmp(argv[i], "-uall") ||
|
||||||
|
!strcmp(argv[i], "--untracked-files=all"))
|
||||||
|
opt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
|
||||||
|
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
|
||||||
|
else if (!strcmp(argv[i], "--ignore-submodules=all"))
|
||||||
|
opt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
|
||||||
|
else if (!strncmp(argv[i], "--git-dir=", strlen("--git-dir=")))
|
||||||
|
repodir = argv[i] + strlen("--git-dir=");
|
||||||
|
else
|
||||||
|
check(-1, "Unsupported option", argv[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format == FORMAT_DEFAULT)
|
||||||
|
format = FORMAT_LONG;
|
||||||
|
if (format == FORMAT_LONG)
|
||||||
|
showbranch = 1;
|
||||||
|
if (npaths > 0) {
|
||||||
|
opt.pathspec.strings = pathspec;
|
||||||
|
opt.pathspec.count = npaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to open the repository at the given path (or at the current
|
||||||
|
* directory if none was given).
|
||||||
|
*/
|
||||||
|
check(git_repository_open_ext(&repo, repodir, 0, NULL),
|
||||||
|
"Could not open repository", repodir);
|
||||||
|
|
||||||
|
if (git_repository_is_bare(repo))
|
||||||
|
fail("Cannot report status on bare repository");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Run status on the repository
|
||||||
|
*
|
||||||
|
* Because we want to simluate a full "git status" run and want to
|
||||||
|
* support some command line options, we use `git_status_foreach_ext()`
|
||||||
|
* instead of just the plain status call. This allows (a) iterating
|
||||||
|
* over the index and then the workdir and (b) extra flags that control
|
||||||
|
* which files are included. If you just want simple status (e.g. to
|
||||||
|
* enumerate files that are modified) then you probably don't need the
|
||||||
|
* extended API.
|
||||||
|
*/
|
||||||
|
check(git_status_list_new(&status, repo, &opt),
|
||||||
|
"Could not get status", NULL);
|
||||||
|
|
||||||
|
if (showbranch)
|
||||||
|
show_branch(repo, format);
|
||||||
|
|
||||||
|
if (format == FORMAT_LONG)
|
||||||
|
print_long(repo, status);
|
||||||
|
else
|
||||||
|
print_short(repo, status);
|
||||||
|
|
||||||
|
git_status_list_free(status);
|
||||||
|
git_repository_free(repo);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -141,7 +141,7 @@ GIT_EXTERN(git_attr_t) git_attr_value(const char *attr);
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_attr_get(
|
GIT_EXTERN(int) git_attr_get(
|
||||||
const char **value_out,
|
const char **value_out,
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
uint32_t flags,
|
uint32_t flags,
|
||||||
const char *path,
|
const char *path,
|
||||||
const char *name);
|
const char *name);
|
||||||
@ -162,7 +162,7 @@ GIT_EXTERN(int) git_attr_get(
|
|||||||
* Then you could loop through the 3 values to get the settings for
|
* Then you could loop through the 3 values to get the settings for
|
||||||
* the three attributes you asked about.
|
* the three attributes you asked about.
|
||||||
*
|
*
|
||||||
* @param values An array of num_attr entries that will have string
|
* @param values_out An array of num_attr entries that will have string
|
||||||
* pointers written into it for the values of the attributes.
|
* pointers written into it for the values of the attributes.
|
||||||
* You should not modify or free the values that are written
|
* You should not modify or free the values that are written
|
||||||
* into this array (although of course, you should free the
|
* into this array (although of course, you should free the
|
||||||
@ -228,7 +228,7 @@ GIT_EXTERN(void) git_attr_cache_flush(
|
|||||||
* function allows you to add others. For example, to add the default
|
* function allows you to add others. For example, to add the default
|
||||||
* macro, you would call:
|
* macro, you would call:
|
||||||
*
|
*
|
||||||
* git_attr_add_macro(repo, "binary", "-diff -crlf");
|
* git_attr_add_macro(repo, "binary", "-diff -crlf");
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_attr_add_macro(
|
GIT_EXTERN(int) git_attr_add_macro(
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
|
@ -29,10 +29,7 @@ GIT_BEGIN_DECL
|
|||||||
* @param id identity of the blob to locate.
|
* @param id identity of the blob to locate.
|
||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
GIT_INLINE(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git_oid *id)
|
GIT_EXTERN(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git_oid *id);
|
||||||
{
|
|
||||||
return git_object_lookup((git_object **)blob, repo, id, GIT_OBJ_BLOB);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lookup a blob object from a repository,
|
* Lookup a blob object from a repository,
|
||||||
@ -46,10 +43,7 @@ GIT_INLINE(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git
|
|||||||
* @param len the length of the short identifier
|
* @param len the length of the short identifier
|
||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
GIT_INLINE(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, size_t len)
|
GIT_EXTERN(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, size_t len);
|
||||||
{
|
|
||||||
return git_object_lookup_prefix((git_object **)blob, repo, id, len, GIT_OBJ_BLOB);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close an open blob
|
* Close an open blob
|
||||||
@ -62,11 +56,7 @@ GIT_INLINE(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, co
|
|||||||
*
|
*
|
||||||
* @param blob the blob to close
|
* @param blob the blob to close
|
||||||
*/
|
*/
|
||||||
|
GIT_EXTERN(void) git_blob_free(git_blob *blob);
|
||||||
GIT_INLINE(void) git_blob_free(git_blob *blob)
|
|
||||||
{
|
|
||||||
git_object_free((git_object *) blob);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the id of a blob.
|
* Get the id of a blob.
|
||||||
@ -74,11 +64,15 @@ GIT_INLINE(void) git_blob_free(git_blob *blob)
|
|||||||
* @param blob a previously loaded blob.
|
* @param blob a previously loaded blob.
|
||||||
* @return SHA1 hash for this blob.
|
* @return SHA1 hash for this blob.
|
||||||
*/
|
*/
|
||||||
GIT_INLINE(const git_oid *) git_blob_id(const git_blob *blob)
|
GIT_EXTERN(const git_oid *) git_blob_id(const git_blob *blob);
|
||||||
{
|
|
||||||
return git_object_id((const git_object *)blob);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the repository that contains the blob.
|
||||||
|
*
|
||||||
|
* @param blob A previously loaded blob.
|
||||||
|
* @return Repository that contains this blob.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(git_repository *) git_blob_owner(const git_blob *blob);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a read-only buffer with the raw content of a blob.
|
* Get a read-only buffer with the raw content of a blob.
|
||||||
|
@ -58,7 +58,8 @@ GIT_EXTERN(int) git_branch_create(
|
|||||||
* Delete an existing branch reference.
|
* Delete an existing branch reference.
|
||||||
*
|
*
|
||||||
* If the branch is successfully deleted, the passed reference
|
* If the branch is successfully deleted, the passed reference
|
||||||
* object will be freed and invalidated.
|
* object will be invalidated. The reference must be freed manually
|
||||||
|
* by the user.
|
||||||
*
|
*
|
||||||
* @param branch A valid reference representing a branch
|
* @param branch A valid reference representing a branch
|
||||||
* @return 0 on success, or an error code.
|
* @return 0 on success, or an error code.
|
||||||
@ -237,7 +238,7 @@ GIT_EXTERN(int) git_branch_is_head(
|
|||||||
*
|
*
|
||||||
* @return Number of characters in the reference name
|
* @return Number of characters in the reference name
|
||||||
* including the trailing NUL byte; GIT_ENOTFOUND
|
* including the trailing NUL byte; GIT_ENOTFOUND
|
||||||
* when no remote matching remote was gound,
|
* when no remote matching remote was found,
|
||||||
* GIT_EAMBIGUOUS when the branch maps to several remotes,
|
* GIT_EAMBIGUOUS when the branch maps to several remotes,
|
||||||
* otherwise an error code.
|
* otherwise an error code.
|
||||||
*/
|
*/
|
||||||
|
@ -134,6 +134,9 @@ typedef enum {
|
|||||||
/** Treat pathspec as simple list of exact match file paths */
|
/** Treat pathspec as simple list of exact match file paths */
|
||||||
GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH = (1u << 13),
|
GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH = (1u << 13),
|
||||||
|
|
||||||
|
/** Ignore directories in use, they will be left empty */
|
||||||
|
GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES = (1u << 18),
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
|
* THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
|
||||||
*/
|
*/
|
||||||
@ -180,6 +183,8 @@ typedef enum {
|
|||||||
GIT_CHECKOUT_NOTIFY_UPDATED = (1u << 2),
|
GIT_CHECKOUT_NOTIFY_UPDATED = (1u << 2),
|
||||||
GIT_CHECKOUT_NOTIFY_UNTRACKED = (1u << 3),
|
GIT_CHECKOUT_NOTIFY_UNTRACKED = (1u << 3),
|
||||||
GIT_CHECKOUT_NOTIFY_IGNORED = (1u << 4),
|
GIT_CHECKOUT_NOTIFY_IGNORED = (1u << 4),
|
||||||
|
|
||||||
|
GIT_CHECKOUT_NOTIFY_ALL = 0x0FFFFu
|
||||||
} git_checkout_notify_t;
|
} git_checkout_notify_t;
|
||||||
|
|
||||||
/** Checkout notification callback function */
|
/** Checkout notification callback function */
|
||||||
@ -231,6 +236,8 @@ typedef struct git_checkout_opts {
|
|||||||
git_strarray paths;
|
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 */
|
||||||
} git_checkout_opts;
|
} git_checkout_opts;
|
||||||
|
|
||||||
#define GIT_CHECKOUT_OPTS_VERSION 1
|
#define GIT_CHECKOUT_OPTS_VERSION 1
|
||||||
|
@ -51,6 +51,8 @@ GIT_BEGIN_DECL
|
|||||||
* - `cred_acquire_cb` is a callback to be used if credentials are required
|
* - `cred_acquire_cb` is a callback to be used if credentials are required
|
||||||
* during the initial fetch.
|
* during the initial fetch.
|
||||||
* - `cred_acquire_payload` is the payload for the above callback.
|
* - `cred_acquire_payload` is the payload for the above callback.
|
||||||
|
* - `transport_flags` is flags used to create transport if no transport is
|
||||||
|
* provided.
|
||||||
* - `transport` is a custom transport to be used for the initial fetch. NULL
|
* - `transport` is a custom transport to be used for the initial fetch. NULL
|
||||||
* means use the transport autodetected from the URL.
|
* means use the transport autodetected from the URL.
|
||||||
* - `remote_callbacks` may be used to specify custom progress callbacks for
|
* - `remote_callbacks` may be used to specify custom progress callbacks for
|
||||||
@ -75,6 +77,7 @@ typedef struct git_clone_options {
|
|||||||
const char *push_spec;
|
const char *push_spec;
|
||||||
git_cred_acquire_cb cred_acquire_cb;
|
git_cred_acquire_cb cred_acquire_cb;
|
||||||
void *cred_acquire_payload;
|
void *cred_acquire_payload;
|
||||||
|
git_transport_flags_t transport_flags;
|
||||||
git_transport *transport;
|
git_transport *transport;
|
||||||
git_remote_callbacks *remote_callbacks;
|
git_remote_callbacks *remote_callbacks;
|
||||||
git_remote_autotag_option_t remote_autotag;
|
git_remote_autotag_option_t remote_autotag;
|
||||||
|
@ -30,10 +30,7 @@ GIT_BEGIN_DECL
|
|||||||
* an annotated tag it will be peeled back to the commit.
|
* an annotated tag it will be peeled back to the commit.
|
||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
GIT_INLINE(int) git_commit_lookup(git_commit **commit, git_repository *repo, const git_oid *id)
|
GIT_EXTERN(int) git_commit_lookup(git_commit **commit, git_repository *repo, const git_oid *id);
|
||||||
{
|
|
||||||
return git_object_lookup((git_object **)commit, repo, id, GIT_OBJ_COMMIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lookup a commit object from a repository,
|
* Lookup a commit object from a repository,
|
||||||
@ -48,10 +45,7 @@ GIT_INLINE(int) git_commit_lookup(git_commit **commit, git_repository *repo, con
|
|||||||
* @param len the length of the short identifier
|
* @param len the length of the short identifier
|
||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
GIT_INLINE(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, size_t len)
|
GIT_EXTERN(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, size_t len);
|
||||||
{
|
|
||||||
return git_object_lookup_prefix((git_object **)commit, repo, id, len, GIT_OBJ_COMMIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close an open commit
|
* Close an open commit
|
||||||
@ -65,10 +59,7 @@ GIT_INLINE(int) git_commit_lookup_prefix(git_commit **commit, git_repository *re
|
|||||||
* @param commit the commit to close
|
* @param commit the commit to close
|
||||||
*/
|
*/
|
||||||
|
|
||||||
GIT_INLINE(void) git_commit_free(git_commit *commit)
|
GIT_EXTERN(void) git_commit_free(git_commit *commit);
|
||||||
{
|
|
||||||
git_object_free((git_object *) commit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the id of a commit.
|
* Get the id of a commit.
|
||||||
@ -76,10 +67,15 @@ GIT_INLINE(void) git_commit_free(git_commit *commit)
|
|||||||
* @param commit a previously loaded commit.
|
* @param commit a previously loaded commit.
|
||||||
* @return object identity for the commit.
|
* @return object identity for the commit.
|
||||||
*/
|
*/
|
||||||
GIT_INLINE(const git_oid *) git_commit_id(const git_commit *commit)
|
GIT_EXTERN(const git_oid *) git_commit_id(const git_commit *commit);
|
||||||
{
|
|
||||||
return git_object_id((const git_object *)commit);
|
/**
|
||||||
}
|
* Get the repository that contains the commit.
|
||||||
|
*
|
||||||
|
* @param commit A previously loaded commit.
|
||||||
|
* @return Repository that contains this commit.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(git_repository *) git_commit_owner(const git_commit *commit);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the encoding for the message of a commit,
|
* Get the encoding for the message of a commit,
|
||||||
@ -168,7 +164,10 @@ GIT_EXTERN(unsigned int) git_commit_parentcount(const git_commit *commit);
|
|||||||
* @param n the position of the parent (from 0 to `parentcount`)
|
* @param n the position of the parent (from 0 to `parentcount`)
|
||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_commit_parent(git_commit **out, git_commit *commit, unsigned int n);
|
GIT_EXTERN(int) git_commit_parent(
|
||||||
|
git_commit **out,
|
||||||
|
const git_commit *commit,
|
||||||
|
unsigned int n);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the oid of a specified parent for a commit. This is different from
|
* Get the oid of a specified parent for a commit. This is different from
|
||||||
@ -179,7 +178,9 @@ GIT_EXTERN(int) git_commit_parent(git_commit **out, git_commit *commit, unsigned
|
|||||||
* @param n the position of the parent (from 0 to `parentcount`)
|
* @param n the position of the parent (from 0 to `parentcount`)
|
||||||
* @return the id of the parent, NULL on error.
|
* @return the id of the parent, NULL on error.
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(const git_oid *) git_commit_parent_id(git_commit *commit, unsigned int n);
|
GIT_EXTERN(const git_oid *) git_commit_parent_id(
|
||||||
|
const git_commit *commit,
|
||||||
|
unsigned int n);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the commit object that is the <n>th generation ancestor
|
* Get the commit object that is the <n>th generation ancestor
|
||||||
@ -201,14 +202,12 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor(
|
|||||||
unsigned int n);
|
unsigned int n);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new commit in the repository using `git_object`
|
* Create new commit in the repository from a list of `git_object` pointers
|
||||||
* instances as parameters.
|
|
||||||
*
|
*
|
||||||
* The message will not be cleaned up. This can be achieved
|
* The message will not be cleaned up automatically. You can do that with
|
||||||
* through `git_message_prettify()`.
|
* the `git_message_prettify()` function.
|
||||||
*
|
*
|
||||||
* @param id Pointer where to store the OID of the
|
* @param id Pointer in which to store the OID of the newly created commit
|
||||||
* newly created commit
|
|
||||||
*
|
*
|
||||||
* @param repo Repository where to store the commit
|
* @param repo Repository where to store the commit
|
||||||
*
|
*
|
||||||
@ -219,73 +218,69 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor(
|
|||||||
* make it point to this commit. If the reference doesn't
|
* make it point to this commit. If the reference doesn't
|
||||||
* exist yet, it will be created.
|
* exist yet, it will be created.
|
||||||
*
|
*
|
||||||
* @param author Signature representing the author and the authory
|
* @param author Signature with author and author time of commit
|
||||||
* time of this commit
|
|
||||||
*
|
*
|
||||||
* @param committer Signature representing the committer and the
|
* @param committer Signature with committer and * commit time of commit
|
||||||
* commit time of this commit
|
|
||||||
*
|
*
|
||||||
* @param message_encoding The encoding for the message in the
|
* @param message_encoding The encoding for the message in the
|
||||||
* commit, represented with a standard encoding name.
|
* commit, represented with a standard encoding name.
|
||||||
* E.g. "UTF-8". If NULL, no encoding header is written and
|
* E.g. "UTF-8". If NULL, no encoding header is written and
|
||||||
* UTF-8 is assumed.
|
* UTF-8 is assumed.
|
||||||
*
|
*
|
||||||
* @param message Full message for this commit
|
* @param message Full message for this commit
|
||||||
*
|
*
|
||||||
* @param tree An instance of a `git_tree` object that will
|
* @param tree An instance of a `git_tree` object that will
|
||||||
* be used as the tree for the commit. This tree object must
|
* be used as the tree for the commit. This tree object must
|
||||||
* also be owned by the given `repo`.
|
* also be owned by the given `repo`.
|
||||||
*
|
*
|
||||||
* @param parent_count Number of parents for this commit
|
* @param parent_count Number of parents for this commit
|
||||||
*
|
*
|
||||||
* @param parents[] Array of `parent_count` pointers to `git_commit`
|
* @param parents Array of `parent_count` pointers to `git_commit`
|
||||||
* objects that will be used as the parents for this commit. This
|
* objects that will be used as the parents for this commit. This
|
||||||
* array may be NULL if `parent_count` is 0 (root commit). All the
|
* array may be NULL if `parent_count` is 0 (root commit). All the
|
||||||
* given commits must be owned by the `repo`.
|
* given commits must be owned by the `repo`.
|
||||||
*
|
*
|
||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
* The created commit will be written to the Object Database and
|
* The created commit will be written to the Object Database and
|
||||||
* the given reference will be updated to point to it
|
* the given reference will be updated to point to it
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_commit_create(
|
GIT_EXTERN(int) git_commit_create(
|
||||||
git_oid *id,
|
git_oid *id,
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const char *update_ref,
|
const char *update_ref,
|
||||||
const git_signature *author,
|
const git_signature *author,
|
||||||
const git_signature *committer,
|
const git_signature *committer,
|
||||||
const char *message_encoding,
|
const char *message_encoding,
|
||||||
const char *message,
|
const char *message,
|
||||||
const git_tree *tree,
|
const git_tree *tree,
|
||||||
int parent_count,
|
int parent_count,
|
||||||
const git_commit *parents[]);
|
const git_commit *parents[]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new commit in the repository using a variable
|
* Create new commit in the repository using a variable argument list.
|
||||||
* argument list.
|
|
||||||
*
|
*
|
||||||
* The message will be cleaned up from excess whitespace
|
* The message will be cleaned up from excess whitespace and it will be made
|
||||||
* it will be made sure that the last line ends with a '\n'.
|
* sure that the last line ends with a '\n'.
|
||||||
*
|
*
|
||||||
* The parents for the commit are specified as a variable
|
* The parents for the commit are specified as a variable list of pointers
|
||||||
* list of pointers to `const git_commit *`. Note that this
|
* to `const git_commit *`. Note that this is a convenience method which may
|
||||||
* is a convenience method which may not be safe to export
|
* not be safe to export for certain languages or compilers
|
||||||
* for certain languages or compilers
|
|
||||||
*
|
*
|
||||||
* All other parameters remain the same
|
* All other parameters remain the same at `git_commit_create()`.
|
||||||
*
|
*
|
||||||
* @see git_commit_create
|
* @see git_commit_create
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_commit_create_v(
|
GIT_EXTERN(int) git_commit_create_v(
|
||||||
git_oid *id,
|
git_oid *id,
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const char *update_ref,
|
const char *update_ref,
|
||||||
const git_signature *author,
|
const git_signature *author,
|
||||||
const git_signature *committer,
|
const git_signature *committer,
|
||||||
const char *message_encoding,
|
const char *message_encoding,
|
||||||
const char *message,
|
const char *message,
|
||||||
const git_tree *tree,
|
const git_tree *tree,
|
||||||
int parent_count,
|
int parent_count,
|
||||||
...);
|
...);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
GIT_END_DECL
|
GIT_END_DECL
|
||||||
|
@ -103,10 +103,10 @@ 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 capabilities of libgit2.
|
||||||
*/
|
*/
|
||||||
enum {
|
typedef enum {
|
||||||
GIT_CAP_THREADS = ( 1 << 0 ),
|
GIT_CAP_THREADS = ( 1 << 0 ),
|
||||||
GIT_CAP_HTTPS = ( 1 << 1 )
|
GIT_CAP_HTTPS = ( 1 << 1 )
|
||||||
};
|
} git_cap_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Query compile time options for libgit2.
|
* Query compile time options for libgit2.
|
||||||
@ -114,69 +114,100 @@ enum {
|
|||||||
* @return A combination of GIT_CAP_* values.
|
* @return A combination of GIT_CAP_* values.
|
||||||
*
|
*
|
||||||
* - GIT_CAP_THREADS
|
* - GIT_CAP_THREADS
|
||||||
* Libgit2 was compiled with thread support. Note that thread support is still to be seen as a
|
* Libgit2 was compiled with thread support. Note that thread support is
|
||||||
* 'work in progress'.
|
* 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_CAP_HTTPS
|
||||||
* Libgit2 supports the https:// protocol. This requires the open ssl library to be
|
* Libgit2 supports the https:// protocol. This requires the openssl
|
||||||
* found when compiling libgit2.
|
* library to be found when compiling libgit2.
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_libgit2_capabilities(void);
|
GIT_EXTERN(int) git_libgit2_capabilities(void);
|
||||||
|
|
||||||
|
|
||||||
enum {
|
typedef enum {
|
||||||
GIT_OPT_GET_MWINDOW_SIZE,
|
GIT_OPT_GET_MWINDOW_SIZE,
|
||||||
GIT_OPT_SET_MWINDOW_SIZE,
|
GIT_OPT_SET_MWINDOW_SIZE,
|
||||||
GIT_OPT_GET_MWINDOW_MAPPED_LIMIT,
|
GIT_OPT_GET_MWINDOW_MAPPED_LIMIT,
|
||||||
GIT_OPT_SET_MWINDOW_MAPPED_LIMIT,
|
GIT_OPT_SET_MWINDOW_MAPPED_LIMIT,
|
||||||
GIT_OPT_GET_SEARCH_PATH,
|
GIT_OPT_GET_SEARCH_PATH,
|
||||||
GIT_OPT_SET_SEARCH_PATH,
|
GIT_OPT_SET_SEARCH_PATH,
|
||||||
GIT_OPT_GET_ODB_CACHE_SIZE,
|
GIT_OPT_SET_CACHE_OBJECT_LIMIT,
|
||||||
GIT_OPT_SET_ODB_CACHE_SIZE,
|
GIT_OPT_SET_CACHE_MAX_SIZE,
|
||||||
};
|
GIT_OPT_ENABLE_CACHING,
|
||||||
|
GIT_OPT_GET_CACHED_MEMORY
|
||||||
|
} git_libgit2_opt_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set or query a library global option
|
* Set or query a library global option
|
||||||
*
|
*
|
||||||
* Available options:
|
* Available options:
|
||||||
*
|
*
|
||||||
* opts(GIT_OPT_GET_MWINDOW_SIZE, size_t *):
|
* * opts(GIT_OPT_GET_MWINDOW_SIZE, size_t *):
|
||||||
* Get the maximum mmap window size
|
|
||||||
*
|
*
|
||||||
* opts(GIT_OPT_SET_MWINDOW_SIZE, size_t):
|
* > Get the maximum mmap window size
|
||||||
* Set the maximum mmap window size
|
|
||||||
*
|
*
|
||||||
* opts(GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, size_t *):
|
* * opts(GIT_OPT_SET_MWINDOW_SIZE, size_t):
|
||||||
* Get the maximum memory that will be mapped in total by the library
|
|
||||||
*
|
*
|
||||||
* opts(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, size_t):
|
* > Set the maximum mmap window size
|
||||||
* Set the maximum amount of memory that can be mapped at any time
|
*
|
||||||
|
* * opts(GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, size_t *):
|
||||||
|
*
|
||||||
|
* > Get the maximum memory that will be mapped in total by the library
|
||||||
|
*
|
||||||
|
* * opts(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, size_t):
|
||||||
|
*
|
||||||
|
* >Set the maximum amount of memory that can be mapped at any time
|
||||||
* by the library
|
* by the library
|
||||||
*
|
*
|
||||||
* opts(GIT_OPT_GET_SEARCH_PATH, int level, char *out, size_t len)
|
* * opts(GIT_OPT_GET_SEARCH_PATH, int level, char *out, size_t len)
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path)
|
* > Get the search path for a given level of config data. "level" must
|
||||||
* Set the search path for a level of config data. The search path
|
* > be one of `GIT_CONFIG_LEVEL_SYSTEM`, `GIT_CONFIG_LEVEL_GLOBAL`, or
|
||||||
* applied to shared attributes and ignore files, too.
|
* > `GIT_CONFIG_LEVEL_XDG`. The search path is written to the `out`
|
||||||
* - `path` lists directories delimited by GIT_PATH_LIST_SEPARATOR.
|
* > buffer up to size `len`. Returns GIT_EBUFS if buffer is too small.
|
||||||
* Pass NULL to reset to the default (generally based on environment
|
|
||||||
* variables). Use magic path `$PATH` to include the old value
|
|
||||||
* of the path (if you want to prepend or append, for instance).
|
|
||||||
* - `level` must be GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_GLOBAL,
|
|
||||||
* or GIT_CONFIG_LEVEL_XDG.
|
|
||||||
*
|
*
|
||||||
* opts(GIT_OPT_GET_ODB_CACHE_SIZE):
|
* * opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path)
|
||||||
* Get the size of the libgit2 odb cache.
|
|
||||||
*
|
*
|
||||||
* opts(GIT_OPT_SET_ODB_CACHE_SIZE):
|
* > Set the search path for a level of config data. The search path
|
||||||
* Set the size of the of the libgit2 odb cache. This needs
|
* > applied to shared attributes and ignore files, too.
|
||||||
* to be done before git_repository_open is called, since
|
* >
|
||||||
* git_repository_open initializes the odb layer. Defaults
|
* > - `path` lists directories delimited by GIT_PATH_LIST_SEPARATOR.
|
||||||
* to 128.
|
* > Pass NULL to reset to the default (generally based on environment
|
||||||
|
* > variables). Use magic path `$PATH` to include the old value
|
||||||
|
* > of the path (if you want to prepend or append, for instance).
|
||||||
|
* >
|
||||||
|
* > - `level` must be GIT_CONFIG_LEVEL_SYSTEM, GIT_CONFIG_LEVEL_GLOBAL,
|
||||||
|
* > or GIT_CONFIG_LEVEL_XDG.
|
||||||
|
*
|
||||||
|
* * opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, git_otype type, size_t size)
|
||||||
|
*
|
||||||
|
* > Set the maximum data size for the given type of object to be
|
||||||
|
* > considered eligible for caching in memory. Setting to value to
|
||||||
|
* > zero means that that type of object will not be cached.
|
||||||
|
* > Defaults to 0 for GIT_OBJ_BLOB (i.e. won't cache blobs) and 4k
|
||||||
|
* > for GIT_OBJ_COMMIT, GIT_OBJ_TREE, and GIT_OBJ_TAG.
|
||||||
|
*
|
||||||
|
* * opts(GIT_OPT_SET_CACHE_MAX_SIZE, ssize_t max_storage_bytes)
|
||||||
|
*
|
||||||
|
* > Set the maximum total data size that will be cached in memory
|
||||||
|
* > 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.
|
||||||
|
*
|
||||||
|
* * opts(GIT_OPT_ENABLE_CACHING, int enabled)
|
||||||
|
*
|
||||||
|
* > Enable or disable caching completely.
|
||||||
|
* >
|
||||||
|
* > Because caches are repository-specific, disabling the cache
|
||||||
|
* > cannot immediately clear all cached objects, but each cache will
|
||||||
|
* > be cleared on the next attempt to update anything in it.
|
||||||
|
*
|
||||||
|
* * opts(GIT_OPT_GET_CACHED_MEMORY, ssize_t *current, ssize_t *allowed)
|
||||||
|
*
|
||||||
|
* > Get the current bytes in cache and the maximum that would be
|
||||||
|
* > allowed in the cache.
|
||||||
*
|
*
|
||||||
* @param option Option key
|
* @param option Option key
|
||||||
* @param ... value to set the option
|
* @param ... value to set the option
|
||||||
|
@ -27,45 +27,41 @@ GIT_BEGIN_DECL
|
|||||||
* git_config_open_default() and git_repository_config() honor those
|
* git_config_open_default() and git_repository_config() honor those
|
||||||
* priority levels as well.
|
* priority levels as well.
|
||||||
*/
|
*/
|
||||||
enum {
|
typedef enum {
|
||||||
GIT_CONFIG_LEVEL_SYSTEM = 1, /**< System-wide configuration file. */
|
/** System-wide configuration file; /etc/gitconfig on Linux systems */
|
||||||
GIT_CONFIG_LEVEL_XDG = 2, /**< XDG compatible configuration file (.config/git/config). */
|
GIT_CONFIG_LEVEL_SYSTEM = 1,
|
||||||
GIT_CONFIG_LEVEL_GLOBAL = 3, /**< User-specific configuration file, also called Global configuration file. */
|
|
||||||
GIT_CONFIG_LEVEL_LOCAL = 4, /**< Repository specific configuration file. */
|
/** XDG compatible configuration file; typically ~/.config/git/config */
|
||||||
GIT_CONFIG_HIGHEST_LEVEL = -1, /**< Represents the highest level of a config file. */
|
GIT_CONFIG_LEVEL_XDG = 2,
|
||||||
};
|
|
||||||
|
/** User-specific configuration file (also called Global configuration
|
||||||
|
* file); typically ~/.gitconfig
|
||||||
|
*/
|
||||||
|
GIT_CONFIG_LEVEL_GLOBAL = 3,
|
||||||
|
|
||||||
|
/** Repository specific configuration file; $WORK_DIR/.git/config on
|
||||||
|
* non-bare repos
|
||||||
|
*/
|
||||||
|
GIT_CONFIG_LEVEL_LOCAL = 4,
|
||||||
|
|
||||||
|
/** Application specific configuration file; freely defined by applications
|
||||||
|
*/
|
||||||
|
GIT_CONFIG_LEVEL_APP = 5,
|
||||||
|
|
||||||
|
/** Represents the highest level available config file (i.e. the most
|
||||||
|
* specific config file available that actually is loaded)
|
||||||
|
*/
|
||||||
|
GIT_CONFIG_HIGHEST_LEVEL = -1,
|
||||||
|
} git_config_level_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *name;
|
const char *name;
|
||||||
const char *value;
|
const char *value;
|
||||||
unsigned int level;
|
git_config_level_t level;
|
||||||
} git_config_entry;
|
} git_config_entry;
|
||||||
|
|
||||||
typedef int (*git_config_foreach_cb)(const git_config_entry *, void *);
|
typedef int (*git_config_foreach_cb)(const git_config_entry *, void *);
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic backend that implements the interface to
|
|
||||||
* access a configuration file
|
|
||||||
*/
|
|
||||||
struct git_config_backend {
|
|
||||||
unsigned int version;
|
|
||||||
struct git_config *cfg;
|
|
||||||
|
|
||||||
/* Open means open the file/database and parse if necessary */
|
|
||||||
int (*open)(struct git_config_backend *, unsigned int level);
|
|
||||||
int (*get)(const struct git_config_backend *, const char *key, const git_config_entry **entry);
|
|
||||||
int (*get_multivar)(struct git_config_backend *, const char *key, const char *regexp, git_config_foreach_cb callback, void *payload);
|
|
||||||
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 (*foreach)(struct git_config_backend *, const char *, git_config_foreach_cb callback, void *payload);
|
|
||||||
int (*refresh)(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}
|
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GIT_CVAR_FALSE = 0,
|
GIT_CVAR_FALSE = 0,
|
||||||
GIT_CVAR_TRUE = 1,
|
GIT_CVAR_TRUE = 1,
|
||||||
@ -123,7 +119,7 @@ GIT_EXTERN(int) git_config_find_xdg(char *out, size_t length);
|
|||||||
* If /etc/gitconfig doesn't exist, it will look for
|
* If /etc/gitconfig doesn't exist, it will look for
|
||||||
* %PROGRAMFILES%\Git\etc\gitconfig.
|
* %PROGRAMFILES%\Git\etc\gitconfig.
|
||||||
|
|
||||||
* @param global_config_path Buffer to store the path in
|
* @param out Buffer to store the path in
|
||||||
* @param length size of the buffer in bytes
|
* @param length size of the buffer in bytes
|
||||||
* @return 0 if a system configuration file has been
|
* @return 0 if a system configuration file has been
|
||||||
* found. Its path will be stored in `buffer`.
|
* found. Its path will be stored in `buffer`.
|
||||||
@ -153,30 +149,6 @@ GIT_EXTERN(int) git_config_open_default(git_config **out);
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_config_new(git_config **out);
|
GIT_EXTERN(int) git_config_new(git_config **out);
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a generic config file instance to an existing config
|
|
||||||
*
|
|
||||||
* Note that the configuration object will free the file
|
|
||||||
* automatically.
|
|
||||||
*
|
|
||||||
* Further queries on this config object will access each
|
|
||||||
* of the config file instances in order (instances with
|
|
||||||
* a higher priority level will be accessed first).
|
|
||||||
*
|
|
||||||
* @param cfg the configuration to add the file to
|
|
||||||
* @param file the configuration file (backend) to add
|
|
||||||
* @param level the priority level of the backend
|
|
||||||
* @param force if a config file already exists for the given
|
|
||||||
* priority level, replace it
|
|
||||||
* @return 0 on success, GIT_EEXISTS when adding more than one file
|
|
||||||
* for a given priority level (and force_replace set to 0), or error code
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(int) git_config_add_backend(
|
|
||||||
git_config *cfg,
|
|
||||||
git_config_backend *file,
|
|
||||||
unsigned int level,
|
|
||||||
int force);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an on-disk config file instance to an existing config
|
* Add an on-disk config file instance to an existing config
|
||||||
*
|
*
|
||||||
@ -192,10 +164,9 @@ GIT_EXTERN(int) git_config_add_backend(
|
|||||||
* a higher priority level will be accessed first).
|
* a higher priority level will be accessed first).
|
||||||
*
|
*
|
||||||
* @param cfg the configuration to add the file to
|
* @param cfg the configuration to add the file to
|
||||||
* @param path path to the configuration file (backend) to add
|
* @param path path to the configuration file to add
|
||||||
* @param level the priority level of the backend
|
* @param level the priority level of the backend
|
||||||
* @param force if a config file already exists for the given
|
* @param force replace config file at the given priority level
|
||||||
* priority level, replace it
|
|
||||||
* @return 0 on success, GIT_EEXISTS when adding more than one file
|
* @return 0 on success, GIT_EEXISTS when adding more than one file
|
||||||
* for a given priority level (and force_replace set to 0),
|
* for a given priority level (and force_replace set to 0),
|
||||||
* GIT_ENOTFOUND when the file doesn't exist or error code
|
* GIT_ENOTFOUND when the file doesn't exist or error code
|
||||||
@ -203,7 +174,7 @@ GIT_EXTERN(int) git_config_add_backend(
|
|||||||
GIT_EXTERN(int) git_config_add_file_ondisk(
|
GIT_EXTERN(int) git_config_add_file_ondisk(
|
||||||
git_config *cfg,
|
git_config *cfg,
|
||||||
const char *path,
|
const char *path,
|
||||||
unsigned int level,
|
git_config_level_t level,
|
||||||
int force);
|
int force);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -238,9 +209,24 @@ GIT_EXTERN(int) git_config_open_ondisk(git_config **out, const char *path);
|
|||||||
* multi-level parent config, or an error code
|
* multi-level parent config, or an error code
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_config_open_level(
|
GIT_EXTERN(int) git_config_open_level(
|
||||||
git_config **out,
|
git_config **out,
|
||||||
const git_config *parent,
|
const git_config *parent,
|
||||||
unsigned int level);
|
git_config_level_t level);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open the global/XDG configuration file according to git's rules
|
||||||
|
*
|
||||||
|
* Git allows you to store your global configuration at
|
||||||
|
* `$HOME/.config` or `$XDG_CONFIG_HOME/git/config`. For backwards
|
||||||
|
* compatability, the XDG file shouldn't be used unless the use has
|
||||||
|
* created it explicitly. With this function you'll open the correct
|
||||||
|
* one to write to.
|
||||||
|
*
|
||||||
|
* @param out pointer in which to store the config object
|
||||||
|
* @param config the config object in which to look
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_config_open_global(git_config **out, git_config *config);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reload changed config files
|
* Reload changed config files
|
||||||
@ -274,7 +260,7 @@ GIT_EXTERN(void) git_config_free(git_config *cfg);
|
|||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_config_get_entry(
|
GIT_EXTERN(int) git_config_get_entry(
|
||||||
const git_config_entry **out,
|
const git_config_entry **out,
|
||||||
const git_config *cfg,
|
const git_config *cfg,
|
||||||
const char *name);
|
const char *name);
|
||||||
|
|
||||||
@ -349,8 +335,8 @@ GIT_EXTERN(int) git_config_get_string(const char **out, const git_config *cfg, c
|
|||||||
* @param name the variable's name
|
* @param name the variable's name
|
||||||
* @param regexp regular expression to filter which variables we're
|
* @param regexp regular expression to filter which variables we're
|
||||||
* interested in. Use NULL to indicate all
|
* interested in. Use NULL to indicate all
|
||||||
* @param fn the function to be called on each value of the variable
|
* @param callback the function to be called on each value of the variable
|
||||||
* @param data opaque pointer to pass to the callback
|
* @param payload opaque pointer to pass to the callback
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_config_get_multivar(const git_config *cfg, const char *name, const char *regexp, git_config_foreach_cb callback, void *payload);
|
GIT_EXTERN(int) git_config_get_multivar(const git_config *cfg, const char *name, const char *regexp, git_config_foreach_cb callback, void *payload);
|
||||||
|
|
||||||
@ -492,11 +478,11 @@ GIT_EXTERN(int) git_config_foreach_match(
|
|||||||
* @return 0 on success, error code otherwise
|
* @return 0 on success, error code otherwise
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_config_get_mapped(
|
GIT_EXTERN(int) git_config_get_mapped(
|
||||||
int *out,
|
int *out,
|
||||||
const git_config *cfg,
|
const git_config *cfg,
|
||||||
const char *name,
|
const char *name,
|
||||||
const git_cvar_map *maps,
|
const git_cvar_map *maps,
|
||||||
size_t map_n);
|
size_t map_n);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps a string value to an integer constant
|
* Maps a string value to an integer constant
|
||||||
|
@ -30,11 +30,11 @@ typedef struct git_cred_userpass_payload {
|
|||||||
/**
|
/**
|
||||||
* Stock callback usable as a git_cred_acquire_cb. This calls
|
* Stock callback usable as a git_cred_acquire_cb. This calls
|
||||||
* git_cred_userpass_plaintext_new unless the protocol has not specified
|
* git_cred_userpass_plaintext_new unless the protocol has not specified
|
||||||
* GIT_CREDTYPE_USERPASS_PLAINTEXT as an allowed type.
|
* `GIT_CREDTYPE_USERPASS_PLAINTEXT` as an allowed type.
|
||||||
*
|
*
|
||||||
* @param cred The newly created credential object.
|
* @param cred The newly created credential object.
|
||||||
* @param url The resource for which we are demanding a credential.
|
* @param url The resource for which we are demanding a credential.
|
||||||
* @param username_from_url The username that was embedded in a "user@host"
|
* @param user_from_url The username that was embedded in a "user@host"
|
||||||
* remote url, or NULL if not included.
|
* remote url, or NULL if not included.
|
||||||
* @param allowed_types A bitmask stating which cred types are OK to return.
|
* @param allowed_types A bitmask stating which cred types are OK to return.
|
||||||
* @param payload The payload provided when specifying this callback. (This is
|
* @param payload The payload provided when specifying this callback. (This is
|
||||||
|
@ -88,42 +88,69 @@ typedef enum {
|
|||||||
GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8),
|
GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8),
|
||||||
/** Include unmodified files in the diff list */
|
/** Include unmodified files in the diff list */
|
||||||
GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9),
|
GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9),
|
||||||
/** Even with GIT_DIFF_INCLUDE_UNTRACKED, an entire untracked directory
|
|
||||||
* will be marked with only a single entry in the diff list; this flag
|
/** Even with GIT_DIFF_INCLUDE_UNTRACKED, an entire untracked
|
||||||
* adds all files under the directory as UNTRACKED entries, too.
|
* directory will be marked with only a single entry in the diff list
|
||||||
|
* (a la what core Git does in `git status`); this flag adds *all*
|
||||||
|
* files under untracked directories as UNTRACKED entries, too.
|
||||||
*/
|
*/
|
||||||
GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10),
|
GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10),
|
||||||
|
|
||||||
/** If the pathspec is set in the diff options, this flags means to
|
/** If the pathspec is set in the diff options, this flags means to
|
||||||
* apply it as an exact match instead of as an fnmatch pattern.
|
* apply it as an exact match instead of as an fnmatch pattern.
|
||||||
*/
|
*/
|
||||||
GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11),
|
GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11),
|
||||||
|
|
||||||
/** Use case insensitive filename comparisons */
|
/** Use case insensitive filename comparisons */
|
||||||
GIT_DIFF_DELTAS_ARE_ICASE = (1 << 12),
|
GIT_DIFF_DELTAS_ARE_ICASE = (1 << 12),
|
||||||
/** When generating patch text, include the content of untracked files */
|
|
||||||
|
/** When generating patch text, include the content of untracked
|
||||||
|
* files. This automatically turns on GIT_DIFF_INCLUDE_UNTRACKED but
|
||||||
|
* it does not turn on GIT_DIFF_RECURSE_UNTRACKED_DIRS. Add that
|
||||||
|
* flag if you want the content of every single UNTRACKED file.
|
||||||
|
*/
|
||||||
GIT_DIFF_INCLUDE_UNTRACKED_CONTENT = (1 << 13),
|
GIT_DIFF_INCLUDE_UNTRACKED_CONTENT = (1 << 13),
|
||||||
|
|
||||||
/** Disable updating of the `binary` flag in delta records. This is
|
/** Disable updating of the `binary` flag in delta records. This is
|
||||||
* useful when iterating over a diff if you don't need hunk and data
|
* useful when iterating over a diff if you don't need hunk and data
|
||||||
* callbacks and want to avoid having to load file completely.
|
* callbacks and want to avoid having to load file completely.
|
||||||
*/
|
*/
|
||||||
GIT_DIFF_SKIP_BINARY_CHECK = (1 << 14),
|
GIT_DIFF_SKIP_BINARY_CHECK = (1 << 14),
|
||||||
|
|
||||||
/** Normally, a type change between files will be converted into a
|
/** Normally, a type change between files will be converted into a
|
||||||
* DELETED record for the old and an ADDED record for the new; this
|
* DELETED record for the old and an ADDED record for the new; this
|
||||||
* options enabled the generation of TYPECHANGE delta records.
|
* options enabled the generation of TYPECHANGE delta records.
|
||||||
*/
|
*/
|
||||||
GIT_DIFF_INCLUDE_TYPECHANGE = (1 << 15),
|
GIT_DIFF_INCLUDE_TYPECHANGE = (1 << 15),
|
||||||
|
|
||||||
/** Even with GIT_DIFF_INCLUDE_TYPECHANGE, blob->tree changes still
|
/** Even with GIT_DIFF_INCLUDE_TYPECHANGE, blob->tree changes still
|
||||||
* generally show as a DELETED blob. This flag tries to correctly
|
* generally show as a DELETED blob. This flag tries to correctly
|
||||||
* label blob->tree transitions as TYPECHANGE records with new_file's
|
* label blob->tree transitions as TYPECHANGE records with new_file's
|
||||||
* mode set to tree. Note: the tree SHA will not be available.
|
* mode set to tree. Note: the tree SHA will not be available.
|
||||||
*/
|
*/
|
||||||
GIT_DIFF_INCLUDE_TYPECHANGE_TREES = (1 << 16),
|
GIT_DIFF_INCLUDE_TYPECHANGE_TREES = (1 << 16),
|
||||||
|
|
||||||
/** Ignore file mode changes */
|
/** Ignore file mode changes */
|
||||||
GIT_DIFF_IGNORE_FILEMODE = (1 << 17),
|
GIT_DIFF_IGNORE_FILEMODE = (1 << 17),
|
||||||
|
|
||||||
/** Even with GIT_DIFF_INCLUDE_IGNORED, an entire ignored directory
|
/** Even with GIT_DIFF_INCLUDE_IGNORED, an entire ignored directory
|
||||||
* will be marked with only a single entry in the diff list; this flag
|
* will be marked with only a single entry in the diff list; this flag
|
||||||
* adds all files under the directory as IGNORED entries, too.
|
* adds all files under the directory as IGNORED entries, too.
|
||||||
*/
|
*/
|
||||||
GIT_DIFF_RECURSE_IGNORED_DIRS = (1 << 18),
|
GIT_DIFF_RECURSE_IGNORED_DIRS = (1 << 18),
|
||||||
|
|
||||||
|
/** Core Git scans inside untracked directories, labeling them IGNORED
|
||||||
|
* if they are empty or only contain ignored files; a directory is
|
||||||
|
* consider UNTRACKED only if it has an actual untracked file in it.
|
||||||
|
* This scan is extra work for a case you often don't care about. This
|
||||||
|
* flag makes libgit2 immediately label an untracked directory as
|
||||||
|
* UNTRACKED without looking inside it (which differs from core Git).
|
||||||
|
* Of course, ignore rules are still checked for the directory itself.
|
||||||
|
*/
|
||||||
|
GIT_DIFF_FAST_UNTRACKED_DIRS = (1 << 19),
|
||||||
|
|
||||||
|
/** Treat all files as binary, disabling text diffs */
|
||||||
|
GIT_DIFF_FORCE_BINARY = (1 << 20),
|
||||||
} git_diff_option_t;
|
} git_diff_option_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -224,6 +251,19 @@ typedef struct {
|
|||||||
* `NOT_BINARY` flag set to avoid examining file contents if you do not pass
|
* `NOT_BINARY` flag set to avoid examining file contents if you do not pass
|
||||||
* in hunk and/or line callbacks to the diff foreach iteration function. It
|
* in hunk and/or line callbacks to the diff foreach iteration function. It
|
||||||
* will just use the git attributes for those files.
|
* will just use the git attributes for those files.
|
||||||
|
*
|
||||||
|
* The similarity score is zero unless you call `git_diff_find_similar()`
|
||||||
|
* which does a similarity analysis of files in the diff. Use that
|
||||||
|
* function to do rename and copy detection, and to split heavily modified
|
||||||
|
* files in add/delete pairs. After that call, deltas with a status of
|
||||||
|
* GIT_DELTA_RENAMED or GIT_DELTA_COPIED will have a similarity score
|
||||||
|
* between 0 and 100 indicating how similar the old and new sides are.
|
||||||
|
*
|
||||||
|
* If you ask `git_diff_find_similar` to find heavily modified files to
|
||||||
|
* break, but to not *actually* break the records, then GIT_DELTA_MODIFIED
|
||||||
|
* records may have a non-zero similarity score if the self-similarity is
|
||||||
|
* below the split threshold. To display this value like core Git, invert
|
||||||
|
* the score (a la `printf("M%03d", 100 - delta->similarity)`).
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
git_diff_file old_file;
|
git_diff_file old_file;
|
||||||
@ -337,8 +377,10 @@ typedef enum {
|
|||||||
GIT_DIFF_LINE_CONTEXT = ' ',
|
GIT_DIFF_LINE_CONTEXT = ' ',
|
||||||
GIT_DIFF_LINE_ADDITION = '+',
|
GIT_DIFF_LINE_ADDITION = '+',
|
||||||
GIT_DIFF_LINE_DELETION = '-',
|
GIT_DIFF_LINE_DELETION = '-',
|
||||||
GIT_DIFF_LINE_ADD_EOFNL = '\n', /**< Removed line w/o LF & added one with */
|
|
||||||
GIT_DIFF_LINE_DEL_EOFNL = '\0', /**< LF was removed at end of file */
|
GIT_DIFF_LINE_CONTEXT_EOFNL = '=', /**< Both files have no LF at end */
|
||||||
|
GIT_DIFF_LINE_ADD_EOFNL = '>', /**< Old has no LF at end, new does */
|
||||||
|
GIT_DIFF_LINE_DEL_EOFNL = '<', /**< Old has LF at end, new does not */
|
||||||
|
|
||||||
/* The following values will only be sent to a `git_diff_data_cb` when
|
/* The following values will only be sent to a `git_diff_data_cb` when
|
||||||
* the content of a diff is being formatted (eg. through
|
* the content of a diff is being formatted (eg. through
|
||||||
@ -387,18 +429,28 @@ typedef enum {
|
|||||||
/** consider unmodified as copy sources? (`--find-copies-harder`) */
|
/** consider unmodified as copy sources? (`--find-copies-harder`) */
|
||||||
GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED = (1 << 3),
|
GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED = (1 << 3),
|
||||||
|
|
||||||
/** split large rewrites into delete/add pairs (`--break-rewrites=/M`) */
|
/** mark large rewrites for split (`--break-rewrites=/M`) */
|
||||||
GIT_DIFF_FIND_AND_BREAK_REWRITES = (1 << 4),
|
GIT_DIFF_FIND_REWRITES = (1 << 4),
|
||||||
|
/** actually split large rewrites into delete/add pairs */
|
||||||
|
GIT_DIFF_BREAK_REWRITES = (1 << 5),
|
||||||
|
/** 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 */
|
||||||
|
GIT_DIFF_FIND_FOR_UNTRACKED = (1 << 6),
|
||||||
|
|
||||||
/** turn on all finding features */
|
/** turn on all finding features */
|
||||||
GIT_DIFF_FIND_ALL = (0x1f),
|
GIT_DIFF_FIND_ALL = (0x0ff),
|
||||||
|
|
||||||
/** measure similarity ignoring leading whitespace (default) */
|
/** measure similarity ignoring leading whitespace (default) */
|
||||||
GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE = 0,
|
GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE = 0,
|
||||||
/** measure similarity ignoring all whitespace */
|
/** measure similarity ignoring all whitespace */
|
||||||
GIT_DIFF_FIND_IGNORE_WHITESPACE = (1 << 6),
|
GIT_DIFF_FIND_IGNORE_WHITESPACE = (1 << 12),
|
||||||
/** measure similarity including all data */
|
/** measure similarity including all data */
|
||||||
GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE = (1 << 7),
|
GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE = (1 << 13),
|
||||||
|
/** measure similarity only by comparing SHAs (fast and cheap) */
|
||||||
|
GIT_DIFF_FIND_EXACT_MATCH_ONLY = (1 << 14),
|
||||||
} git_diff_find_t;
|
} git_diff_find_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -425,7 +477,10 @@ typedef struct {
|
|||||||
* - `copy_threshold` is the same as the -C option with a value
|
* - `copy_threshold` is the same as the -C option with a value
|
||||||
* - `rename_from_rewrite_threshold` matches the top of the -B option
|
* - `rename_from_rewrite_threshold` matches the top of the -B option
|
||||||
* - `break_rewrite_threshold` matches the bottom of the -B option
|
* - `break_rewrite_threshold` matches the bottom of the -B option
|
||||||
* - `target_limit` matches the -l option
|
* - `rename_limit` is the maximum number of matches to consider for
|
||||||
|
* a particular file. This is a little different from the `-l` option
|
||||||
|
* to regular Git because we will still process up to this many matches
|
||||||
|
* before abandoning the search.
|
||||||
*
|
*
|
||||||
* The `metric` option allows you to plug in a custom similarity metric.
|
* The `metric` option allows you to plug in a custom similarity metric.
|
||||||
* Set it to NULL for the default internal metric which is based on sampling
|
* Set it to NULL for the default internal metric which is based on sampling
|
||||||
@ -437,21 +492,21 @@ typedef struct {
|
|||||||
unsigned int version;
|
unsigned int version;
|
||||||
|
|
||||||
/** Combination of git_diff_find_t values (default FIND_RENAMES) */
|
/** Combination of git_diff_find_t values (default FIND_RENAMES) */
|
||||||
unsigned int flags;
|
uint32_t flags;
|
||||||
|
|
||||||
/** Similarity to consider a file renamed (default 50) */
|
/** Similarity to consider a file renamed (default 50) */
|
||||||
unsigned int rename_threshold;
|
uint16_t rename_threshold;
|
||||||
/** Similarity of modified to be eligible rename source (default 50) */
|
/** Similarity of modified to be eligible rename source (default 50) */
|
||||||
unsigned int rename_from_rewrite_threshold;
|
uint16_t rename_from_rewrite_threshold;
|
||||||
/** Similarity to consider a file a copy (default 50) */
|
/** Similarity to consider a file a copy (default 50) */
|
||||||
unsigned int copy_threshold;
|
uint16_t copy_threshold;
|
||||||
/** Similarity to split modify into delete/add pair (default 60) */
|
/** Similarity to split modify into delete/add pair (default 60) */
|
||||||
unsigned int break_rewrite_threshold;
|
uint16_t break_rewrite_threshold;
|
||||||
|
|
||||||
/** Maximum similarity sources to examine (a la diff's `-l` option or
|
/** Maximum similarity sources to examine for a file (somewhat like
|
||||||
* the `diff.renameLimit` config) (default 200)
|
* git-diff's `-l` option or `diff.renameLimit` config) (default 200)
|
||||||
*/
|
*/
|
||||||
unsigned int target_limit;
|
size_t rename_limit;
|
||||||
|
|
||||||
/** Pluggable similarity metric; pass NULL to use internal metric */
|
/** Pluggable similarity metric; pass NULL to use internal metric */
|
||||||
git_diff_similarity_metric *metric;
|
git_diff_similarity_metric *metric;
|
||||||
@ -469,6 +524,8 @@ typedef struct {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Deallocate a diff list.
|
* Deallocate a diff list.
|
||||||
|
*
|
||||||
|
* @param diff The previously created diff list; cannot be used after free.
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(void) git_diff_list_free(git_diff_list *diff);
|
GIT_EXTERN(void) git_diff_list_free(git_diff_list *diff);
|
||||||
|
|
||||||
@ -478,12 +535,14 @@ GIT_EXTERN(void) git_diff_list_free(git_diff_list *diff);
|
|||||||
* This is equivalent to `git diff <old-tree> <new-tree>`
|
* This is equivalent to `git diff <old-tree> <new-tree>`
|
||||||
*
|
*
|
||||||
* The first tree will be used for the "old_file" side of the delta and the
|
* The first tree will be used for the "old_file" side of the delta and the
|
||||||
* second tree will be used for the "new_file" side of the delta.
|
* second tree will be used for the "new_file" side of the delta. You can
|
||||||
|
* pass NULL to indicate an empty tree, although it is an error to pass
|
||||||
|
* NULL for both the `old_tree` and `new_tree`.
|
||||||
*
|
*
|
||||||
* @param diff Output pointer to a git_diff_list pointer to be allocated.
|
* @param diff Output pointer to a git_diff_list pointer to be allocated.
|
||||||
* @param repo The repository containing the trees.
|
* @param repo The repository containing the trees.
|
||||||
* @param old_tree A git_tree object to diff from.
|
* @param old_tree A git_tree object to diff from, or NULL for empty tree.
|
||||||
* @param new_tree A git_tree object to diff to.
|
* @param new_tree A git_tree object to diff to, or NULL for empty tree.
|
||||||
* @param opts Structure with options to influence diff or NULL for defaults.
|
* @param opts Structure with options to influence diff or NULL for defaults.
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_diff_tree_to_tree(
|
GIT_EXTERN(int) git_diff_tree_to_tree(
|
||||||
@ -504,7 +563,7 @@ GIT_EXTERN(int) git_diff_tree_to_tree(
|
|||||||
*
|
*
|
||||||
* @param diff Output pointer to a git_diff_list pointer to be allocated.
|
* @param diff Output pointer to a git_diff_list pointer to be allocated.
|
||||||
* @param repo The repository containing the tree and index.
|
* @param repo The repository containing the tree and index.
|
||||||
* @param old_tree A git_tree object to diff from.
|
* @param old_tree A git_tree object to diff from, or NULL for empty tree.
|
||||||
* @param index The index to diff with; repo index used if NULL.
|
* @param index The index to diff with; repo index used if NULL.
|
||||||
* @param opts Structure with options to influence diff or NULL for defaults.
|
* @param opts Structure with options to influence diff or NULL for defaults.
|
||||||
*/
|
*/
|
||||||
@ -563,7 +622,7 @@ GIT_EXTERN(int) git_diff_index_to_workdir(
|
|||||||
*
|
*
|
||||||
* @param diff A pointer to a git_diff_list pointer that will be allocated.
|
* @param diff A pointer to a git_diff_list pointer that will be allocated.
|
||||||
* @param repo The repository containing the tree.
|
* @param repo The repository containing the tree.
|
||||||
* @param old_tree A git_tree object to diff from.
|
* @param old_tree A git_tree object to diff from, or NULL for empty tree.
|
||||||
* @param opts Structure with options to influence diff or NULL for defaults.
|
* @param opts Structure with options to influence diff or NULL for defaults.
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_diff_tree_to_workdir(
|
GIT_EXTERN(int) git_diff_tree_to_workdir(
|
||||||
@ -663,6 +722,22 @@ GIT_EXTERN(int) git_diff_print_compact(
|
|||||||
git_diff_data_cb print_cb,
|
git_diff_data_cb print_cb,
|
||||||
void *payload);
|
void *payload);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate over a diff generating text output like "git diff --raw".
|
||||||
|
*
|
||||||
|
* Returning a non-zero value from the callbacks will terminate the
|
||||||
|
* iteration and cause this return `GIT_EUSER`.
|
||||||
|
*
|
||||||
|
* @param diff A git_diff_list generated by one of the above functions.
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_diff_print_raw(
|
||||||
|
git_diff_list *diff,
|
||||||
|
git_diff_data_cb print_cb,
|
||||||
|
void *payload);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Look up the single character abbreviation for a delta status code.
|
* Look up the single character abbreviation for a delta status code.
|
||||||
*
|
*
|
||||||
@ -672,7 +747,7 @@ GIT_EXTERN(int) git_diff_print_compact(
|
|||||||
* letters for your own purposes. This function does just that. By the
|
* letters for your own purposes. This function does just that. By the
|
||||||
* way, unmodified will return a space (i.e. ' ').
|
* way, unmodified will return a space (i.e. ' ').
|
||||||
*
|
*
|
||||||
* @param delta_t The git_delta_t value to look up
|
* @param status The git_delta_t value to look up
|
||||||
* @return The single character label for that code
|
* @return The single character label for that code
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(char) git_diff_status_char(git_delta_t status);
|
GIT_EXTERN(char) git_diff_status_char(git_delta_t status);
|
||||||
@ -785,7 +860,7 @@ GIT_EXTERN(size_t) git_diff_patch_num_hunks(
|
|||||||
* @param total_additions Count of addition lines in output, can be NULL.
|
* @param total_additions Count of addition lines in output, can be NULL.
|
||||||
* @param total_deletions Count of deletion lines in output, can be NULL.
|
* @param total_deletions Count of deletion lines in output, can be NULL.
|
||||||
* @param patch The git_diff_patch object
|
* @param patch The git_diff_patch object
|
||||||
* @return Number of lines in hunk or -1 if invalid hunk index
|
* @return 0 on success, <0 on error
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_diff_patch_line_stats(
|
GIT_EXTERN(int) git_diff_patch_line_stats(
|
||||||
size_t *total_context,
|
size_t *total_context,
|
||||||
@ -843,7 +918,7 @@ GIT_EXTERN(int) git_diff_patch_num_lines_in_hunk(
|
|||||||
* @param new_lineno Line number in new file or -1 if line is deleted
|
* @param new_lineno Line number in new file or -1 if line is deleted
|
||||||
* @param patch The patch to look in
|
* @param patch The patch to look in
|
||||||
* @param hunk_idx The index of the hunk
|
* @param hunk_idx The index of the hunk
|
||||||
* @param line_of_index The index of the line in the hunk
|
* @param line_of_hunk The index of the line in the hunk
|
||||||
* @return 0 on success, <0 on failure
|
* @return 0 on success, <0 on failure
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_diff_patch_get_line_in_hunk(
|
GIT_EXTERN(int) git_diff_patch_get_line_in_hunk(
|
||||||
@ -907,17 +982,52 @@ GIT_EXTERN(int) git_diff_patch_to_str(
|
|||||||
* to 1 and no call to the hunk_cb nor line_cb will be made (unless you pass
|
* to 1 and no call to the hunk_cb nor line_cb will be made (unless you pass
|
||||||
* `GIT_DIFF_FORCE_TEXT` of course).
|
* `GIT_DIFF_FORCE_TEXT` of course).
|
||||||
*
|
*
|
||||||
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
|
* @param old_blob Blob for old side of diff, or NULL for empty blob
|
||||||
|
* @param old_as_path Treat old blob as if it had this filename; can be NULL
|
||||||
|
* @param new_blob Blob for new side of diff, or NULL for empty blob
|
||||||
|
* @param new_as_path Treat new blob 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, GIT_EUSER on non-zero callback return, or error code
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_diff_blobs(
|
GIT_EXTERN(int) git_diff_blobs(
|
||||||
const git_blob *old_blob,
|
const git_blob *old_blob,
|
||||||
|
const char *old_as_path,
|
||||||
const git_blob *new_blob,
|
const git_blob *new_blob,
|
||||||
|
const char *new_as_path,
|
||||||
const git_diff_options *options,
|
const git_diff_options *options,
|
||||||
git_diff_file_cb file_cb,
|
git_diff_file_cb file_cb,
|
||||||
git_diff_hunk_cb hunk_cb,
|
git_diff_hunk_cb hunk_cb,
|
||||||
git_diff_data_cb line_cb,
|
git_diff_data_cb line_cb,
|
||||||
void *payload);
|
void *payload);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directly generate a patch from the difference between two blobs.
|
||||||
|
*
|
||||||
|
* This is just like `git_diff_blobs()` except it generates a patch object
|
||||||
|
* for the difference instead of directly making callbacks. You can use the
|
||||||
|
* standard `git_diff_patch` accessor functions to read the patch data, and
|
||||||
|
* you must call `git_diff_patch_free()` on the patch when done.
|
||||||
|
*
|
||||||
|
* @param out The generated patch; NULL on error
|
||||||
|
* @param old_blob Blob for old side of diff, or NULL for empty blob
|
||||||
|
* @param old_as_path Treat old blob as if it had this filename; can be NULL
|
||||||
|
* @param new_blob Blob for new side of diff, or NULL for empty blob
|
||||||
|
* @param new_as_path Treat new blob 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_diff_patch_from_blobs(
|
||||||
|
git_diff_patch **out,
|
||||||
|
const git_blob *old_blob,
|
||||||
|
const char *old_as_path,
|
||||||
|
const git_blob *new_blob,
|
||||||
|
const char *new_as_path,
|
||||||
|
const git_diff_options *opts);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directly run a diff between a blob and a buffer.
|
* Directly run a diff between a blob and a buffer.
|
||||||
*
|
*
|
||||||
@ -930,18 +1040,57 @@ GIT_EXTERN(int) git_diff_blobs(
|
|||||||
* entire content of the buffer added). Passing NULL to the buffer will do
|
* entire content of the buffer added). Passing NULL to the buffer will do
|
||||||
* the reverse, with GIT_DELTA_REMOVED and blob content removed.
|
* the reverse, with GIT_DELTA_REMOVED and blob content removed.
|
||||||
*
|
*
|
||||||
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
|
* @param old_blob Blob for old side of diff, or NULL for empty blob
|
||||||
|
* @param old_as_path Treat old blob as if it had this filename; can be NULL
|
||||||
|
* @param buffer Raw data for new side of diff, or NULL for empty
|
||||||
|
* @param buffer_len Length of raw data for new side of diff
|
||||||
|
* @param buffer_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 data_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
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_diff_blob_to_buffer(
|
GIT_EXTERN(int) git_diff_blob_to_buffer(
|
||||||
const git_blob *old_blob,
|
const git_blob *old_blob,
|
||||||
|
const char *old_as_path,
|
||||||
const char *buffer,
|
const char *buffer,
|
||||||
size_t buffer_len,
|
size_t buffer_len,
|
||||||
|
const char *buffer_as_path,
|
||||||
const git_diff_options *options,
|
const git_diff_options *options,
|
||||||
git_diff_file_cb file_cb,
|
git_diff_file_cb file_cb,
|
||||||
git_diff_hunk_cb hunk_cb,
|
git_diff_hunk_cb hunk_cb,
|
||||||
git_diff_data_cb data_cb,
|
git_diff_data_cb data_cb,
|
||||||
void *payload);
|
void *payload);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directly generate a patch from the difference between a blob and a buffer.
|
||||||
|
*
|
||||||
|
* This is just like `git_diff_blob_to_buffer()` except it generates a patch
|
||||||
|
* object for the difference instead of directly making callbacks. You can
|
||||||
|
* use the standard `git_diff_patch` accessor functions to read the patch
|
||||||
|
* data, and you must call `git_diff_patch_free()` on the patch when done.
|
||||||
|
*
|
||||||
|
* @param out The generated patch; NULL on error
|
||||||
|
* @param old_blob Blob for old side of diff, or NULL for empty blob
|
||||||
|
* @param old_as_path Treat old blob as if it had this filename; can be NULL
|
||||||
|
* @param buffer Raw data for new side of diff, or NULL for empty
|
||||||
|
* @param buffer_len Length of raw data for new side of diff
|
||||||
|
* @param buffer_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_diff_patch_from_blob_and_buffer(
|
||||||
|
git_diff_patch **out,
|
||||||
|
const git_blob *old_blob,
|
||||||
|
const char *old_as_path,
|
||||||
|
const char *buffer,
|
||||||
|
size_t buffer_len,
|
||||||
|
const char *buffer_as_path,
|
||||||
|
const git_diff_options *opts);
|
||||||
|
|
||||||
|
|
||||||
GIT_END_DECL
|
GIT_END_DECL
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
GIT_BEGIN_DECL
|
GIT_BEGIN_DECL
|
||||||
|
|
||||||
/** Generic return codes */
|
/** Generic return codes */
|
||||||
enum {
|
typedef enum {
|
||||||
GIT_OK = 0,
|
GIT_OK = 0,
|
||||||
GIT_ERROR = -1,
|
GIT_ERROR = -1,
|
||||||
GIT_ENOTFOUND = -3,
|
GIT_ENOTFOUND = -3,
|
||||||
@ -35,7 +35,7 @@ enum {
|
|||||||
|
|
||||||
GIT_PASSTHROUGH = -30,
|
GIT_PASSTHROUGH = -30,
|
||||||
GIT_ITEROVER = -31,
|
GIT_ITEROVER = -31,
|
||||||
};
|
} git_error_code;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char *message;
|
char *message;
|
||||||
@ -100,7 +100,7 @@ GIT_EXTERN(void) giterr_clear(void);
|
|||||||
*
|
*
|
||||||
* @param error_class One of the `git_error_t` enum above describing the
|
* @param error_class One of the `git_error_t` enum above describing the
|
||||||
* general subsystem that is responsible for the error.
|
* general subsystem that is responsible for the error.
|
||||||
* @param message The formatted error message to keep
|
* @param string The formatted error message to keep
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(void) giterr_set_str(int error_class, const char *string);
|
GIT_EXTERN(void) giterr_set_str(int error_class, const char *string);
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include "indexer.h"
|
#include "indexer.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "oid.h"
|
#include "oid.h"
|
||||||
|
#include "strarray.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file git2/index.h
|
* @file git2/index.h
|
||||||
@ -21,50 +22,29 @@
|
|||||||
*/
|
*/
|
||||||
GIT_BEGIN_DECL
|
GIT_BEGIN_DECL
|
||||||
|
|
||||||
#define GIT_IDXENTRY_NAMEMASK (0x0fff)
|
/** Time structure used in a git index entry */
|
||||||
#define GIT_IDXENTRY_STAGEMASK (0x3000)
|
|
||||||
#define GIT_IDXENTRY_EXTENDED (0x4000)
|
|
||||||
#define GIT_IDXENTRY_VALID (0x8000)
|
|
||||||
#define GIT_IDXENTRY_STAGESHIFT 12
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Flags are divided into two parts: in-memory flags and
|
|
||||||
* on-disk ones. Flags in GIT_IDXENTRY_EXTENDED_FLAGS
|
|
||||||
* will get saved on-disk.
|
|
||||||
*
|
|
||||||
* In-memory only flags:
|
|
||||||
*/
|
|
||||||
#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)
|
|
||||||
|
|
||||||
#define GIT_IDXENTRY_UNPACKED (1 << 8)
|
|
||||||
#define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Extended on-disk flags:
|
|
||||||
*/
|
|
||||||
#define GIT_IDXENTRY_INTENT_TO_ADD (1 << 13)
|
|
||||||
#define GIT_IDXENTRY_SKIP_WORKTREE (1 << 14)
|
|
||||||
/* GIT_IDXENTRY_EXTENDED2 is for future extension */
|
|
||||||
#define GIT_IDXENTRY_EXTENDED2 (1 << 15)
|
|
||||||
|
|
||||||
#define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE)
|
|
||||||
|
|
||||||
/** Time used in a git index entry */
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
git_time_t seconds;
|
git_time_t seconds;
|
||||||
/* nsec should not be stored as time_t compatible */
|
/* nsec should not be stored as time_t compatible */
|
||||||
unsigned int nanoseconds;
|
unsigned int nanoseconds;
|
||||||
} git_index_time;
|
} git_index_time;
|
||||||
|
|
||||||
/** Memory representation of a file entry in the index. */
|
/**
|
||||||
|
* In-memory representation of a file entry in the index.
|
||||||
|
*
|
||||||
|
* This is a public structure that represents a file entry in the index.
|
||||||
|
* The meaning of the fields corresponds to core Git's documentation (in
|
||||||
|
* "Documentation/technical/index-format.txt").
|
||||||
|
*
|
||||||
|
* The `flags` field consists of a number of bit fields which can be
|
||||||
|
* accessed via the first set of `GIT_IDXENTRY_...` bitmasks below. These
|
||||||
|
* flags are all read from and persisted to disk.
|
||||||
|
*
|
||||||
|
* The `flags_extended` field also has a number of bit fields which can be
|
||||||
|
* accessed via the later `GIT_IDXENTRY_...` bitmasks below. Some of
|
||||||
|
* these flags are read from and written to disk, but some are set aside
|
||||||
|
* for in-memory only reference.
|
||||||
|
*/
|
||||||
typedef struct git_index_entry {
|
typedef struct git_index_entry {
|
||||||
git_index_time ctime;
|
git_index_time ctime;
|
||||||
git_index_time mtime;
|
git_index_time mtime;
|
||||||
@ -84,20 +64,79 @@ typedef struct git_index_entry {
|
|||||||
char *path;
|
char *path;
|
||||||
} git_index_entry;
|
} git_index_entry;
|
||||||
|
|
||||||
/** Representation of a resolve undo entry in the index. */
|
/**
|
||||||
typedef struct git_index_reuc_entry {
|
* Bitmasks for on-disk fields of `git_index_entry`'s `flags`
|
||||||
unsigned int mode[3];
|
*
|
||||||
git_oid oid[3];
|
* These bitmasks match the four fields in the `git_index_entry` `flags`
|
||||||
char *path;
|
* value both in memory and on disk. You can use them to interpret the
|
||||||
} git_index_reuc_entry;
|
* data in the `flags`.
|
||||||
|
*/
|
||||||
|
#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)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitmasks for on-disk fields of `git_index_entry`'s `flags_extended`
|
||||||
|
*
|
||||||
|
* In memory, the `flags_extended` fields are divided into two parts: the
|
||||||
|
* fields that are read from and written to disk, and other fields that
|
||||||
|
* 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
|
||||||
|
* 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)
|
||||||
|
|
||||||
|
#define GIT_IDXENTRY_UNPACKED (1 << 8)
|
||||||
|
#define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9)
|
||||||
|
|
||||||
/** Capabilities of system that affect index actions. */
|
/** Capabilities of system that affect index actions. */
|
||||||
enum {
|
typedef enum {
|
||||||
GIT_INDEXCAP_IGNORE_CASE = 1,
|
GIT_INDEXCAP_IGNORE_CASE = 1,
|
||||||
GIT_INDEXCAP_NO_FILEMODE = 2,
|
GIT_INDEXCAP_NO_FILEMODE = 2,
|
||||||
GIT_INDEXCAP_NO_SYMLINKS = 4,
|
GIT_INDEXCAP_NO_SYMLINKS = 4,
|
||||||
GIT_INDEXCAP_FROM_OWNER = ~0u
|
GIT_INDEXCAP_FROM_OWNER = ~0u
|
||||||
};
|
} git_indexcap_t;
|
||||||
|
|
||||||
|
/** Callback for APIs that add/remove/update files matching pathspec */
|
||||||
|
typedef int (*git_index_matched_path_cb)(
|
||||||
|
const char *path, const char *matched_pathspec, void *payload);
|
||||||
|
|
||||||
|
/** Flags for APIs that add files matching pathspec */
|
||||||
|
typedef enum {
|
||||||
|
GIT_INDEX_ADD_DEFAULT = 0,
|
||||||
|
GIT_INDEX_ADD_FORCE = (1u << 0),
|
||||||
|
GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH = (1u << 1),
|
||||||
|
GIT_INDEX_ADD_CHECK_PATHSPEC = (1u << 2),
|
||||||
|
} git_index_add_option_t;
|
||||||
|
|
||||||
/** @name Index File Functions
|
/** @name Index File Functions
|
||||||
*
|
*
|
||||||
@ -272,11 +311,9 @@ GIT_EXTERN(void) git_index_clear(git_index *index);
|
|||||||
/**
|
/**
|
||||||
* Get a pointer to one of the entries in the index
|
* Get a pointer to one of the entries in the index
|
||||||
*
|
*
|
||||||
* The values of this entry can be modified (except the path)
|
* The entry is not modifiable and should not be freed. Because the
|
||||||
* and the changes will be written back to disk on the next
|
* `git_index_entry` struct is a publicly defined struct, you should
|
||||||
* write() call.
|
* be able to make your own permanent copy of the data if necessary.
|
||||||
*
|
|
||||||
* The entry should not be freed by the caller.
|
|
||||||
*
|
*
|
||||||
* @param index an existing index object
|
* @param index an existing index object
|
||||||
* @param n the position of the entry
|
* @param n the position of the entry
|
||||||
@ -288,11 +325,9 @@ GIT_EXTERN(const git_index_entry *) git_index_get_byindex(
|
|||||||
/**
|
/**
|
||||||
* Get a pointer to one of the entries in the index
|
* Get a pointer to one of the entries in the index
|
||||||
*
|
*
|
||||||
* The values of this entry can be modified (except the path)
|
* The entry is not modifiable and should not be freed. Because the
|
||||||
* and the changes will be written back to disk on the next
|
* `git_index_entry` struct is a publicly defined struct, you should
|
||||||
* write() call.
|
* be able to make your own permanent copy of the data if necessary.
|
||||||
*
|
|
||||||
* The entry should not be freed by the caller.
|
|
||||||
*
|
*
|
||||||
* @param index an existing index object
|
* @param index an existing index object
|
||||||
* @param path path to search
|
* @param path path to search
|
||||||
@ -342,8 +377,7 @@ GIT_EXTERN(int) git_index_add(git_index *index, const git_index_entry *source_en
|
|||||||
/**
|
/**
|
||||||
* Return the stage number from a git index entry
|
* Return the stage number from a git index entry
|
||||||
*
|
*
|
||||||
* This entry is calculated from the entry's flag
|
* This entry is calculated from the entry's flag attribute like this:
|
||||||
* attribute like this:
|
|
||||||
*
|
*
|
||||||
* (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT
|
* (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT
|
||||||
*
|
*
|
||||||
@ -399,6 +433,108 @@ GIT_EXTERN(int) git_index_add_bypath(git_index *index, const char *path);
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_index_remove_bypath(git_index *index, const char *path);
|
GIT_EXTERN(int) git_index_remove_bypath(git_index *index, const char *path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add or update index entries matching files in the working directory.
|
||||||
|
*
|
||||||
|
* This method will fail in bare index instances.
|
||||||
|
*
|
||||||
|
* The `pathspec` is a list of file names or shell glob patterns that will
|
||||||
|
* matched against files in the repository's working directory. Each file
|
||||||
|
* that matches will be added to the index (either updating an existing
|
||||||
|
* entry or adding a new entry). You can disable glob expansion and force
|
||||||
|
* exact matching with the `GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH` flag.
|
||||||
|
*
|
||||||
|
* Files that are ignored will be skipped (unlike `git_index_add_bypath`).
|
||||||
|
* If a file is already tracked in the index, then it *will* be updated
|
||||||
|
* even if it is ignored. Pass the `GIT_INDEX_ADD_FORCE` flag to
|
||||||
|
* skip the checking of ignore rules.
|
||||||
|
*
|
||||||
|
* To emulate `git add -A` and generate an error if the pathspec contains
|
||||||
|
* the exact path of an ignored file (when not using FORCE), add the
|
||||||
|
* `GIT_INDEX_ADD_CHECK_PATHSPEC` flag. This checks that each entry
|
||||||
|
* in the `pathspec` that is an exact match to a filename on disk is
|
||||||
|
* either not ignored or already in the index. If this check fails, the
|
||||||
|
* function will return GIT_EINVALIDSPEC.
|
||||||
|
*
|
||||||
|
* To emulate `git add -A` with the "dry-run" option, just use a callback
|
||||||
|
* function that always returns a positive value. See below for details.
|
||||||
|
*
|
||||||
|
* If any files are currently the result of a merge conflict, those files
|
||||||
|
* will no longer be marked as conflicting. The data about the conflicts
|
||||||
|
* will be moved to the "resolve undo" (REUC) section.
|
||||||
|
*
|
||||||
|
* If you provide a callback function, it will be invoked on each matching
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @param index an existing index object
|
||||||
|
* @param pathspec array of path patterns
|
||||||
|
* @param flags combination of git_index_add_option_t flags
|
||||||
|
* @param callback notification callback for each added/updated path (also
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_index_add_all(
|
||||||
|
git_index *index,
|
||||||
|
const git_strarray *pathspec,
|
||||||
|
unsigned int flags,
|
||||||
|
git_index_matched_path_cb callback,
|
||||||
|
void *payload);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all matching index entries.
|
||||||
|
*
|
||||||
|
* If you provide a callback function, it will be invoked on each matching
|
||||||
|
* item in the index immediately *before* it is removed. Return 0 to
|
||||||
|
* remove the item, > 0 to skip the item, and < 0 to abort the scan.
|
||||||
|
*
|
||||||
|
* @param index An existing index object
|
||||||
|
* @param pathspec array of path patterns
|
||||||
|
* @param callback notification callback for each removed path (also
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_index_remove_all(
|
||||||
|
git_index *index,
|
||||||
|
const git_strarray *pathspec,
|
||||||
|
git_index_matched_path_cb callback,
|
||||||
|
void *payload);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update all index entries to match the working directory
|
||||||
|
*
|
||||||
|
* This method will fail in bare index instances.
|
||||||
|
*
|
||||||
|
* This scans the existing index entries and synchronizes them with the
|
||||||
|
* working directory, deleting them if the corresponding working directory
|
||||||
|
* file no longer exists otherwise updating the information (including
|
||||||
|
* adding the latest version of file to the ODB if needed).
|
||||||
|
*
|
||||||
|
* If you provide a callback function, it will be invoked on each matching
|
||||||
|
* item in the index immediately *before* it is updated (either refreshed
|
||||||
|
* or removed depending on working directory state). Return 0 to proceed
|
||||||
|
* with updating the item, > 0 to skip the item, and < 0 to abort the scan.
|
||||||
|
*
|
||||||
|
* @param index An existing index object
|
||||||
|
* @param pathspec array of path patterns
|
||||||
|
* @param callback notification callback for each updated path (also
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_index_update_all(
|
||||||
|
git_index *index,
|
||||||
|
const git_strarray *pathspec,
|
||||||
|
git_index_matched_path_cb callback,
|
||||||
|
void *payload);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find the first position of any entries which point to given
|
* Find the first position of any entries which point to given
|
||||||
* path in the Git index.
|
* path in the Git index.
|
||||||
@ -434,7 +570,7 @@ GIT_EXTERN(int) git_index_find(size_t *at_pos, git_index *index, const char *pat
|
|||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_index_conflict_add(
|
GIT_EXTERN(int) git_index_conflict_add(
|
||||||
git_index *index,
|
git_index *index,
|
||||||
const git_index_entry *ancestor_entry,
|
const git_index_entry *ancestor_entry,
|
||||||
const git_index_entry *our_entry,
|
const git_index_entry *our_entry,
|
||||||
const git_index_entry *their_entry);
|
const git_index_entry *their_entry);
|
||||||
@ -442,9 +578,9 @@ GIT_EXTERN(int) git_index_conflict_add(
|
|||||||
/**
|
/**
|
||||||
* Get the index entries that represent a conflict of a single file.
|
* Get the index entries that represent a conflict of a single file.
|
||||||
*
|
*
|
||||||
* The values of this entry can be modified (except the paths)
|
* The entries are not modifiable and should not be freed. Because the
|
||||||
* and the changes will be written back to disk on the next
|
* `git_index_entry` struct is a publicly defined struct, you should
|
||||||
* write() call.
|
* be able to make your own permanent copy of the data if necessary.
|
||||||
*
|
*
|
||||||
* @param ancestor_out Pointer to store the ancestor entry
|
* @param ancestor_out Pointer to store the ancestor entry
|
||||||
* @param our_out Pointer to store the our entry
|
* @param our_out Pointer to store the our entry
|
||||||
@ -452,7 +588,12 @@ GIT_EXTERN(int) git_index_conflict_add(
|
|||||||
* @param index an existing index object
|
* @param index an existing index object
|
||||||
* @param path path to search
|
* @param path path to search
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_index_conflict_get(git_index_entry **ancestor_out, git_index_entry **our_out, git_index_entry **their_out, git_index *index, const char *path);
|
GIT_EXTERN(int) git_index_conflict_get(
|
||||||
|
const git_index_entry **ancestor_out,
|
||||||
|
const git_index_entry **our_out,
|
||||||
|
const git_index_entry **their_out,
|
||||||
|
git_index *index,
|
||||||
|
const char *path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the index entries that represent a conflict of a single file.
|
* Removes the index entries that represent a conflict of a single file.
|
||||||
@ -476,101 +617,39 @@ GIT_EXTERN(void) git_index_conflict_cleanup(git_index *index);
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_index_has_conflicts(const git_index *index);
|
GIT_EXTERN(int) git_index_has_conflicts(const git_index *index);
|
||||||
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
/** @name Resolve Undo (REUC) index entry manipulation.
|
|
||||||
*
|
|
||||||
* These functions work on the Resolve Undo index extension and contains
|
|
||||||
* data about the original files that led to a merge conflict.
|
|
||||||
*/
|
|
||||||
/**@{*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the count of resolve undo entries currently in the index.
|
* Create an iterator for the conflicts in the index. You may not modify the
|
||||||
|
* index while iterating, the results are undefined.
|
||||||
*
|
*
|
||||||
* @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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the resolve undo entry that points to the given path in the Git
|
|
||||||
* index.
|
|
||||||
*
|
|
||||||
* @param at_pos the address to which the position of the reuc entry is written (optional)
|
|
||||||
* @param index an existing index object
|
|
||||||
* @param path path to search
|
|
||||||
* @return 0 if found, < 0 otherwise (GIT_ENOTFOUND)
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(int) git_index_reuc_find(size_t *at_pos, git_index *index, const char *path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a resolve undo entry from the index.
|
|
||||||
*
|
|
||||||
* The returned entry is read-only and should not be modified
|
|
||||||
* or freed by the caller.
|
|
||||||
*
|
|
||||||
* @param index an existing index object
|
|
||||||
* @param path path to search
|
|
||||||
* @return the resolve undo entry; NULL if not found
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_bypath(git_index *index, const char *path);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a resolve undo entry from the index.
|
|
||||||
*
|
|
||||||
* The returned entry is read-only and should not be modified
|
|
||||||
* or freed by the caller.
|
|
||||||
*
|
|
||||||
* @param index an existing index object
|
|
||||||
* @param n the position of the entry
|
|
||||||
* @return a pointer to the resolve undo entry; NULL if out of bounds
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_byindex(git_index *index, size_t n);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a resolve undo entry for a file based on the given parameters.
|
|
||||||
*
|
|
||||||
* The resolve undo entry contains the OIDs of files that were involved
|
|
||||||
* in a merge conflict after the conflict has been resolved. This allows
|
|
||||||
* conflicts to be re-resolved later.
|
|
||||||
*
|
|
||||||
* If there exists a resolve undo entry for the given path in the index,
|
|
||||||
* it will be removed.
|
|
||||||
*
|
|
||||||
* This method will fail in bare index instances.
|
|
||||||
*
|
|
||||||
* @param index an existing index object
|
|
||||||
* @param path filename to add
|
|
||||||
* @param ancestor_mode mode of the ancestor file
|
|
||||||
* @param ancestor_id oid of the ancestor file
|
|
||||||
* @param our_mode mode of our file
|
|
||||||
* @param our_id oid of our file
|
|
||||||
* @param their_mode mode of their file
|
|
||||||
* @param their_id oid of their file
|
|
||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_index_reuc_add(git_index *index, const char *path,
|
GIT_EXTERN(int) git_index_conflict_iterator_new(
|
||||||
int ancestor_mode, git_oid *ancestor_id,
|
git_index_conflict_iterator **iterator_out,
|
||||||
int our_mode, git_oid *our_id,
|
git_index *index);
|
||||||
int their_mode, git_oid *their_id);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove an resolve undo entry from the index
|
* Returns the current conflict (ancestor, ours and theirs entry) and
|
||||||
|
* advance the iterator internally to the next value.
|
||||||
*
|
*
|
||||||
* @param index an existing index object
|
* @param ancestor_out Pointer to store the ancestor side of the conflict
|
||||||
* @param n position of the resolve undo entry to remove
|
* @param our_out Pointer to store our side of the conflict
|
||||||
* @return 0 or an error code
|
* @param their_out Pointer to store their side of the conflict
|
||||||
|
* @return 0 (no error), GIT_ITEROVER (iteration is done) or an error code
|
||||||
|
* (negative value)
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_index_reuc_remove(git_index *index, size_t n);
|
GIT_EXTERN(int) git_index_conflict_next(
|
||||||
|
const git_index_entry **ancestor_out,
|
||||||
|
const git_index_entry **our_out,
|
||||||
|
const git_index_entry **their_out,
|
||||||
|
git_index_conflict_iterator *iterator);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all resolve undo entries from the index
|
* Frees a `git_index_conflict_iterator`.
|
||||||
*
|
*
|
||||||
* @param index an existing index object
|
* @param iterator pointer to the iterator
|
||||||
* @return 0 or an error code
|
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(void) git_index_reuc_clear(git_index *index);
|
GIT_EXTERN(void) git_index_conflict_iterator_free(
|
||||||
|
git_index_conflict_iterator *iterator);
|
||||||
|
|
||||||
/**@}*/
|
/**@}*/
|
||||||
|
|
||||||
|
@ -8,31 +8,11 @@
|
|||||||
#define _INCLUDE_git_indexer_h__
|
#define _INCLUDE_git_indexer_h__
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "types.h"
|
||||||
#include "oid.h"
|
#include "oid.h"
|
||||||
|
|
||||||
GIT_BEGIN_DECL
|
GIT_BEGIN_DECL
|
||||||
|
|
||||||
/**
|
|
||||||
* This is passed as the first argument to the callback to allow the
|
|
||||||
* user to see the progress.
|
|
||||||
*/
|
|
||||||
typedef struct git_transfer_progress {
|
|
||||||
unsigned int total_objects;
|
|
||||||
unsigned int indexed_objects;
|
|
||||||
unsigned int received_objects;
|
|
||||||
size_t received_bytes;
|
|
||||||
} git_transfer_progress;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type for progress callbacks during indexing. Return a value less than zero
|
|
||||||
* to cancel the transfer.
|
|
||||||
*
|
|
||||||
* @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 struct git_indexer_stream git_indexer_stream;
|
typedef struct git_indexer_stream git_indexer_stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,7 +21,7 @@ typedef struct git_indexer_stream git_indexer_stream;
|
|||||||
* @param out where to store the indexer instance
|
* @param out where to store the indexer instance
|
||||||
* @param path to the directory where the packfile should be stored
|
* @param path to the directory where the packfile should be stored
|
||||||
* @param progress_cb function to call with progress information
|
* @param progress_cb function to call with progress information
|
||||||
* @param progress_payload payload for the progress callback
|
* @param progress_cb_payload payload for the progress callback
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_indexer_stream_new(
|
GIT_EXTERN(int) git_indexer_stream_new(
|
||||||
git_indexer_stream **out,
|
git_indexer_stream **out,
|
||||||
|
@ -283,18 +283,18 @@ _inline
|
|||||||
#endif // STATIC_IMAXDIV ]
|
#endif // STATIC_IMAXDIV ]
|
||||||
imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)
|
imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)
|
||||||
{
|
{
|
||||||
imaxdiv_t result;
|
imaxdiv_t result;
|
||||||
|
|
||||||
result.quot = numer / denom;
|
result.quot = numer / denom;
|
||||||
result.rem = numer % denom;
|
result.rem = numer % denom;
|
||||||
|
|
||||||
if (numer < 0 && result.rem > 0) {
|
if (numer < 0 && result.rem > 0) {
|
||||||
// did division wrong; must fix up
|
// did division wrong; must fix up
|
||||||
++result.quot;
|
++result.quot;
|
||||||
result.rem -= denom;
|
result.rem -= denom;
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7.8.2.3 The strtoimax and strtoumax functions
|
// 7.8.2.3 The strtoimax and strtoumax functions
|
||||||
|
@ -7,19 +7,64 @@
|
|||||||
#ifndef INCLUDE_git_merge_h__
|
#ifndef INCLUDE_git_merge_h__
|
||||||
#define INCLUDE_git_merge_h__
|
#define INCLUDE_git_merge_h__
|
||||||
|
|
||||||
#include "common.h"
|
#include "git2/common.h"
|
||||||
#include "types.h"
|
#include "git2/types.h"
|
||||||
#include "oid.h"
|
#include "git2/oid.h"
|
||||||
|
#include "git2/checkout.h"
|
||||||
|
#include "git2/index.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file git2/merge.h
|
* @file git2/merge.h
|
||||||
* @brief Git merge-base routines
|
* @brief Git merge routines
|
||||||
* @defgroup git_revwalk Git merge-base routines
|
* @defgroup git_merge Git merge routines
|
||||||
* @ingroup Git
|
* @ingroup Git
|
||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
GIT_BEGIN_DECL
|
GIT_BEGIN_DECL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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`.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
/** Detect renames */
|
||||||
|
GIT_MERGE_TREE_FIND_RENAMES = (1 << 0),
|
||||||
|
} git_merge_tree_flag_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automerge options for `git_merge_trees_opts`.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
unsigned int version;
|
||||||
|
git_merge_tree_flag_t flags;
|
||||||
|
|
||||||
|
/** Similarity to consider a file renamed (default 50) */
|
||||||
|
unsigned int rename_threshold;
|
||||||
|
|
||||||
|
/** Maximum similarity sources to examine (overrides the
|
||||||
|
* `merge.renameLimit` config) (default 200)
|
||||||
|
*/
|
||||||
|
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}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a merge base between two commits
|
* Find a merge base between two commits
|
||||||
*
|
*
|
||||||
@ -50,6 +95,79 @@ GIT_EXTERN(int) git_merge_base_many(
|
|||||||
const git_oid input_array[],
|
const git_oid input_array[],
|
||||||
size_t length);
|
size_t length);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a `git_merge_head` from the given reference
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_merge_head_from_ref(
|
||||||
|
git_merge_head **out,
|
||||||
|
git_repository *repo,
|
||||||
|
git_reference *ref);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a `git_merge_head` from the given fetch head data
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_merge_head_from_oid(
|
||||||
|
git_merge_head **out,
|
||||||
|
git_repository *repo,
|
||||||
|
const git_oid *oid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees a `git_merge_head`
|
||||||
|
*
|
||||||
|
* @param head merge head to free
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(void) git_merge_head_free(
|
||||||
|
git_merge_head *head);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge two trees, producing a `git_index` that reflects the result of
|
||||||
|
* the merge.
|
||||||
|
*
|
||||||
|
* 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 ancestor_tree the common ancestor between the trees (or null if none)
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_merge_trees(
|
||||||
|
git_index **out,
|
||||||
|
git_repository *repo,
|
||||||
|
const git_tree *ancestor_tree,
|
||||||
|
const git_tree *our_tree,
|
||||||
|
const git_tree *their_tree,
|
||||||
|
const git_merge_tree_opts *opts);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
GIT_END_DECL
|
GIT_END_DECL
|
||||||
#endif
|
#endif
|
||||||
|
@ -10,8 +10,6 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "oid.h"
|
#include "oid.h"
|
||||||
#include "odb_backend.h"
|
|
||||||
#include "indexer.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file git2/odb.h
|
* @file git2/odb.h
|
||||||
@ -22,6 +20,11 @@
|
|||||||
*/
|
*/
|
||||||
GIT_BEGIN_DECL
|
GIT_BEGIN_DECL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function type for callbacks from git_odb_foreach.
|
||||||
|
*/
|
||||||
|
typedef int (*git_odb_foreach_cb)(const git_oid *id, void *payload);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new object database with no backends.
|
* Create a new object database with no backends.
|
||||||
*
|
*
|
||||||
@ -52,42 +55,6 @@ GIT_EXTERN(int) git_odb_new(git_odb **out);
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_odb_open(git_odb **out, const char *objects_dir);
|
GIT_EXTERN(int) git_odb_open(git_odb **out, const char *objects_dir);
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a custom backend to an existing Object DB
|
|
||||||
*
|
|
||||||
* The backends are checked in relative ordering, based on the
|
|
||||||
* value of the `priority` parameter.
|
|
||||||
*
|
|
||||||
* Read <odb_backends.h> for more information.
|
|
||||||
*
|
|
||||||
* @param odb database to add the backend to
|
|
||||||
* @param backend pointer to a git_odb_backend instance
|
|
||||||
* @param priority Value for ordering the backends queue
|
|
||||||
* @return 0 on success; error code otherwise
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(int) git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add a custom backend to an existing Object DB; this
|
|
||||||
* backend will work as an alternate.
|
|
||||||
*
|
|
||||||
* Alternate backends are always checked for objects *after*
|
|
||||||
* all the main backends have been exhausted.
|
|
||||||
*
|
|
||||||
* The backends are checked in relative ordering, based on the
|
|
||||||
* value of the `priority` parameter.
|
|
||||||
*
|
|
||||||
* Writing is disabled on alternate backends.
|
|
||||||
*
|
|
||||||
* Read <odb_backends.h> for more information.
|
|
||||||
*
|
|
||||||
* @param odb database to add the backend to
|
|
||||||
* @param backend pointer to a git_odb_backend instance
|
|
||||||
* @param priority Value for ordering the backends queue
|
|
||||||
* @return 0 on success; error code otherwise
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(int) git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add an on-disk alternate to an existing Object DB.
|
* Add an on-disk alternate to an existing Object DB.
|
||||||
*
|
*
|
||||||
@ -406,6 +373,60 @@ GIT_EXTERN(size_t) git_odb_object_size(git_odb_object *object);
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(git_otype) git_odb_object_type(git_odb_object *object);
|
GIT_EXTERN(git_otype) git_odb_object_type(git_odb_object *object);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a custom backend to an existing Object DB
|
||||||
|
*
|
||||||
|
* The backends are checked in relative ordering, based on the
|
||||||
|
* value of the `priority` parameter.
|
||||||
|
*
|
||||||
|
* Read <odb_backends.h> for more information.
|
||||||
|
*
|
||||||
|
* @param odb database to add the backend to
|
||||||
|
* @param backend pointer to a git_odb_backend instance
|
||||||
|
* @param priority Value for ordering the backends queue
|
||||||
|
* @return 0 on success; error code otherwise
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a custom backend to an existing Object DB; this
|
||||||
|
* backend will work as an alternate.
|
||||||
|
*
|
||||||
|
* Alternate backends are always checked for objects *after*
|
||||||
|
* all the main backends have been exhausted.
|
||||||
|
*
|
||||||
|
* The backends are checked in relative ordering, based on the
|
||||||
|
* value of the `priority` parameter.
|
||||||
|
*
|
||||||
|
* Writing is disabled on alternate backends.
|
||||||
|
*
|
||||||
|
* Read <odb_backends.h> for more information.
|
||||||
|
*
|
||||||
|
* @param odb database to add the backend to
|
||||||
|
* @param backend pointer to a git_odb_backend instance
|
||||||
|
* @param priority Value for ordering the backends queue
|
||||||
|
* @return 0 on success; error code otherwise
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of ODB backend objects
|
||||||
|
*
|
||||||
|
* @param odb object database
|
||||||
|
* @return number of backends in the ODB
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(size_t) git_odb_num_backends(git_odb *odb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup an ODB backend object by index
|
||||||
|
*
|
||||||
|
* @param out output pointer to ODB backend at pos
|
||||||
|
* @param odb object database
|
||||||
|
* @param pos index into object database backend list
|
||||||
|
* @return 0 on success; GIT_ENOTFOUND if pos is invalid; other errors < 0
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
GIT_END_DECL
|
GIT_END_DECL
|
||||||
#endif
|
#endif
|
||||||
|
@ -7,143 +7,84 @@
|
|||||||
#ifndef INCLUDE_git_odb_backend_h__
|
#ifndef INCLUDE_git_odb_backend_h__
|
||||||
#define INCLUDE_git_odb_backend_h__
|
#define INCLUDE_git_odb_backend_h__
|
||||||
|
|
||||||
#include "common.h"
|
#include "git2/common.h"
|
||||||
#include "types.h"
|
#include "git2/types.h"
|
||||||
#include "oid.h"
|
|
||||||
#include "indexer.h"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file git2/backend.h
|
* @file git2/backend.h
|
||||||
* @brief Git custom backend functions
|
* @brief Git custom backend functions
|
||||||
* @defgroup git_backend Git custom backend API
|
* @defgroup git_odb Git object database routines
|
||||||
* @ingroup Git
|
* @ingroup Git
|
||||||
* @{
|
* @{
|
||||||
*/
|
*/
|
||||||
GIT_BEGIN_DECL
|
GIT_BEGIN_DECL
|
||||||
|
|
||||||
struct git_odb_stream;
|
/*
|
||||||
struct git_odb_writepack;
|
* Constructors for in-box ODB backends.
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function type for callbacks from git_odb_foreach.
|
* Create a backend for the packfiles.
|
||||||
|
*
|
||||||
|
* @param out location to store the odb backend pointer
|
||||||
|
* @param objects_dir the Git repository's objects directory
|
||||||
|
*
|
||||||
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
typedef int (*git_odb_foreach_cb)(const git_oid *id, void *payload);
|
GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **out, const char *objects_dir);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An instance for a custom backend
|
* Create a backend for loose objects
|
||||||
|
*
|
||||||
|
* @param out location to store the odb backend pointer
|
||||||
|
* @param objects_dir the Git repository's objects directory
|
||||||
|
* @param compression_level zlib compression level to use
|
||||||
|
* @param do_fsync whether to do an fsync() after writing (currently ignored)
|
||||||
|
*
|
||||||
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
struct git_odb_backend {
|
GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **out, const char *objects_dir, int compression_level, int do_fsync);
|
||||||
unsigned int version;
|
|
||||||
git_odb *odb;
|
|
||||||
|
|
||||||
/* read and read_prefix each return to libgit2 a buffer which
|
/**
|
||||||
* will be freed later. The buffer should be allocated using
|
* Create a backend out of a single packfile
|
||||||
* the function git_odb_backend_malloc to ensure that it can
|
*
|
||||||
* be safely freed later. */
|
* This can be useful for inspecting the contents of a single
|
||||||
int (* read)(
|
* packfile.
|
||||||
void **, size_t *, git_otype *,
|
*
|
||||||
struct git_odb_backend *,
|
* @param out location to store the odb backend pointer
|
||||||
const git_oid *);
|
* @param index_file path to the packfile's .idx file
|
||||||
|
*
|
||||||
/* To find a unique object given a prefix
|
* @return 0 or an error code
|
||||||
* of its oid.
|
*/
|
||||||
* The oid given must be so that the
|
GIT_EXTERN(int) git_odb_backend_one_pack(git_odb_backend **out, const char *index_file);
|
||||||
* remaining (GIT_OID_HEXSZ - len)*4 bits
|
|
||||||
* are 0s.
|
|
||||||
*/
|
|
||||||
int (* read_prefix)(
|
|
||||||
git_oid *,
|
|
||||||
void **, size_t *, git_otype *,
|
|
||||||
struct git_odb_backend *,
|
|
||||||
const git_oid *,
|
|
||||||
size_t);
|
|
||||||
|
|
||||||
int (* read_header)(
|
|
||||||
size_t *, git_otype *,
|
|
||||||
struct git_odb_backend *,
|
|
||||||
const git_oid *);
|
|
||||||
|
|
||||||
/* The writer may assume that the object
|
|
||||||
* has already been hashed and is passed
|
|
||||||
* in the first parameter.
|
|
||||||
*/
|
|
||||||
int (* write)(
|
|
||||||
git_oid *,
|
|
||||||
struct git_odb_backend *,
|
|
||||||
const void *,
|
|
||||||
size_t,
|
|
||||||
git_otype);
|
|
||||||
|
|
||||||
int (* writestream)(
|
|
||||||
struct git_odb_stream **,
|
|
||||||
struct git_odb_backend *,
|
|
||||||
size_t,
|
|
||||||
git_otype);
|
|
||||||
|
|
||||||
int (* readstream)(
|
|
||||||
struct git_odb_stream **,
|
|
||||||
struct git_odb_backend *,
|
|
||||||
const git_oid *);
|
|
||||||
|
|
||||||
int (* exists)(
|
|
||||||
struct git_odb_backend *,
|
|
||||||
const git_oid *);
|
|
||||||
|
|
||||||
int (* refresh)(struct git_odb_backend *);
|
|
||||||
|
|
||||||
int (* foreach)(
|
|
||||||
struct git_odb_backend *,
|
|
||||||
git_odb_foreach_cb cb,
|
|
||||||
void *payload);
|
|
||||||
|
|
||||||
int (* writepack)(
|
|
||||||
struct git_odb_writepack **,
|
|
||||||
struct git_odb_backend *,
|
|
||||||
git_transfer_progress_callback progress_cb,
|
|
||||||
void *progress_payload);
|
|
||||||
|
|
||||||
void (* free)(struct git_odb_backend *);
|
|
||||||
};
|
|
||||||
|
|
||||||
#define GIT_ODB_BACKEND_VERSION 1
|
|
||||||
#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION}
|
|
||||||
|
|
||||||
/** Streaming mode */
|
/** Streaming mode */
|
||||||
enum {
|
typedef enum {
|
||||||
GIT_STREAM_RDONLY = (1 << 1),
|
GIT_STREAM_RDONLY = (1 << 1),
|
||||||
GIT_STREAM_WRONLY = (1 << 2),
|
GIT_STREAM_WRONLY = (1 << 2),
|
||||||
GIT_STREAM_RW = (GIT_STREAM_RDONLY | GIT_STREAM_WRONLY),
|
GIT_STREAM_RW = (GIT_STREAM_RDONLY | GIT_STREAM_WRONLY),
|
||||||
};
|
} git_odb_stream_t;
|
||||||
|
|
||||||
/** A stream to read/write from a backend */
|
/** A stream to read/write from a backend */
|
||||||
struct git_odb_stream {
|
struct git_odb_stream {
|
||||||
struct git_odb_backend *backend;
|
git_odb_backend *backend;
|
||||||
unsigned int mode;
|
unsigned int mode;
|
||||||
|
|
||||||
int (*read)(struct git_odb_stream *stream, char *buffer, size_t len);
|
int (*read)(git_odb_stream *stream, char *buffer, size_t len);
|
||||||
int (*write)(struct git_odb_stream *stream, const char *buffer, size_t len);
|
int (*write)(git_odb_stream *stream, const char *buffer, size_t len);
|
||||||
int (*finalize_write)(git_oid *oid_p, struct git_odb_stream *stream);
|
int (*finalize_write)(git_oid *oid_p, git_odb_stream *stream);
|
||||||
void (*free)(struct git_odb_stream *stream);
|
void (*free)(git_odb_stream *stream);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** A stream to write a pack file to the ODB */
|
/** A stream to write a pack file to the ODB */
|
||||||
struct git_odb_writepack {
|
struct git_odb_writepack {
|
||||||
struct git_odb_backend *backend;
|
git_odb_backend *backend;
|
||||||
|
|
||||||
int (*add)(struct git_odb_writepack *writepack, const void *data, size_t size, git_transfer_progress *stats);
|
int (*add)(git_odb_writepack *writepack, const void *data, size_t size, git_transfer_progress *stats);
|
||||||
int (*commit)(struct git_odb_writepack *writepack, git_transfer_progress *stats);
|
int (*commit)(git_odb_writepack *writepack, git_transfer_progress *stats);
|
||||||
void (*free)(struct git_odb_writepack *writepack);
|
void (*free)(git_odb_writepack *writepack);
|
||||||
};
|
};
|
||||||
|
|
||||||
GIT_EXTERN(void *) git_odb_backend_malloc(git_odb_backend *backend, size_t len);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructors for in-box ODB backends.
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **out, const char *objects_dir);
|
|
||||||
GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **out, const char *objects_dir, int compression_level, int do_fsync);
|
|
||||||
GIT_EXTERN(int) git_odb_backend_one_pack(git_odb_backend **out, const char *index_file);
|
|
||||||
|
|
||||||
GIT_END_DECL
|
GIT_END_DECL
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -85,10 +85,21 @@ GIT_EXTERN(void) git_oid_fromraw(git_oid *out, const unsigned char *raw);
|
|||||||
* needed for an oid encoded in hex (40 bytes). Only the
|
* needed for an oid encoded in hex (40 bytes). Only the
|
||||||
* oid digits are written; a '\\0' terminator must be added
|
* oid digits are written; a '\\0' terminator must be added
|
||||||
* by the caller if it is required.
|
* by the caller if it is required.
|
||||||
* @param oid oid structure to format.
|
* @param id oid structure to format.
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(void) git_oid_fmt(char *out, const git_oid *id);
|
GIT_EXTERN(void) git_oid_fmt(char *out, const git_oid *id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format a git_oid into a partial hex string.
|
||||||
|
*
|
||||||
|
* @param out output hex string; you say how many bytes to write.
|
||||||
|
* If the number of bytes is > GIT_OID_HEXSZ, extra bytes
|
||||||
|
* will be zeroed; if not, a '\0' terminator is NOT added.
|
||||||
|
* @param n number of characters to write into out string
|
||||||
|
* @param id oid structure to format.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(void) git_oid_nfmt(char *out, size_t n, const git_oid *id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format a git_oid into a loose-object path string.
|
* Format a git_oid into a loose-object path string.
|
||||||
*
|
*
|
||||||
@ -107,7 +118,7 @@ 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 newly allocated c-string.
|
||||||
*
|
*
|
||||||
* @param oid the oid structure to format
|
* @param id the oid structure to format
|
||||||
* @return the c-string; NULL if memory is exhausted. Caller must
|
* @return the c-string; NULL if memory is exhausted. Caller must
|
||||||
* deallocate the string with git__free().
|
* deallocate the string with git__free().
|
||||||
*/
|
*/
|
||||||
@ -117,10 +128,12 @@ GIT_EXTERN(char *) git_oid_allocfmt(const git_oid *id);
|
|||||||
* Format a git_oid into a buffer as a hex format c-string.
|
* Format a git_oid into a buffer as a hex format c-string.
|
||||||
*
|
*
|
||||||
* If the buffer is smaller than GIT_OID_HEXSZ+1, then the resulting
|
* If the buffer is smaller than GIT_OID_HEXSZ+1, then the resulting
|
||||||
* oid c-string will be truncated to n-1 characters. If there are
|
* oid c-string will be truncated to n-1 characters (but will still be
|
||||||
* any input parameter errors (out == NULL, n == 0, oid == NULL),
|
* NUL-byte terminated).
|
||||||
* then a pointer to an empty string is returned, so that the return
|
*
|
||||||
* value can always be printed.
|
* If there are any input parameter errors (out == NULL, n == 0, oid ==
|
||||||
|
* NULL), then a pointer to an empty string is returned, so that the
|
||||||
|
* return value can always be printed.
|
||||||
*
|
*
|
||||||
* @param out the buffer into which the oid string is output.
|
* @param out the buffer into which the oid string is output.
|
||||||
* @param n the size of the out buffer.
|
* @param n the size of the out buffer.
|
||||||
@ -145,19 +158,7 @@ GIT_EXTERN(void) git_oid_cpy(git_oid *out, const git_oid *src);
|
|||||||
* @param b second oid structure.
|
* @param b second oid structure.
|
||||||
* @return <0, 0, >0 if a < b, a == b, a > b.
|
* @return <0, 0, >0 if a < b, a == b, a > b.
|
||||||
*/
|
*/
|
||||||
GIT_INLINE(int) git_oid_cmp(const git_oid *a, const git_oid *b)
|
GIT_EXTERN(int) git_oid_cmp(const git_oid *a, const git_oid *b);
|
||||||
{
|
|
||||||
const unsigned char *sha1 = a->id;
|
|
||||||
const unsigned char *sha2 = b->id;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < GIT_OID_RAWSZ; i++, sha1++, sha2++) {
|
|
||||||
if (*sha1 != *sha2)
|
|
||||||
return *sha1 - *sha2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compare two oid structures for equality
|
* Compare two oid structures for equality
|
||||||
@ -192,6 +193,16 @@ GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, size_t len);
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_oid_streq(const git_oid *id, const char *str);
|
GIT_EXTERN(int) git_oid_streq(const git_oid *id, const char *str);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare an oid to an hex formatted object id.
|
||||||
|
*
|
||||||
|
* @param id oid structure.
|
||||||
|
* @param str input hex string of an object id.
|
||||||
|
* @return -1 if str is not valid, <0 if id sorts before str,
|
||||||
|
* 0 if id matches str, >0 if id sorts after str.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_oid_strcmp(const git_oid *id, const char *str);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check is an oid is all zeros.
|
* Check is an oid is all zeros.
|
||||||
*
|
*
|
||||||
|
@ -95,14 +95,32 @@ GIT_EXTERN(int) git_packbuilder_insert(git_packbuilder *pb, const git_oid *id, c
|
|||||||
GIT_EXTERN(int) git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *id);
|
GIT_EXTERN(int) git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write the new pack and the corresponding index to path
|
* Insert a commit object
|
||||||
|
*
|
||||||
|
* This will add a commit as well as the completed referenced tree.
|
||||||
*
|
*
|
||||||
* @param pb The packbuilder
|
* @param pb The packbuilder
|
||||||
* @param path Directory to store the new pack and index
|
* @param id The oid of the commit
|
||||||
*
|
*
|
||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_packbuilder_write(git_packbuilder *pb, const char *file);
|
GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the new pack and corresponding index file to path.
|
||||||
|
*
|
||||||
|
* @param pb The packbuilder
|
||||||
|
* @param path to the directory where the packfile and index should be stored
|
||||||
|
* @param progress_cb function to call with progress information from the indexer (optional)
|
||||||
|
* @param progress_cb_payload payload for the progress callback (optional)
|
||||||
|
*
|
||||||
|
* @return 0 or an error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_packbuilder_write(
|
||||||
|
git_packbuilder *pb,
|
||||||
|
const char *path,
|
||||||
|
git_transfer_progress_callback progress_cb,
|
||||||
|
void *progress_cb_payload);
|
||||||
|
|
||||||
typedef int (*git_packbuilder_foreach_cb)(void *buf, size_t size, void *payload);
|
typedef int (*git_packbuilder_foreach_cb)(void *buf, size_t size, void *payload);
|
||||||
/**
|
/**
|
||||||
|
@ -21,22 +21,6 @@
|
|||||||
*/
|
*/
|
||||||
GIT_BEGIN_DECL
|
GIT_BEGIN_DECL
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new reference. Either an oid or a symbolic target must be
|
|
||||||
* specified.
|
|
||||||
*
|
|
||||||
* @param refdb the reference database to associate with this reference
|
|
||||||
* @param name the reference name
|
|
||||||
* @param oid the object id for a direct reference
|
|
||||||
* @param symbolic the target for a symbolic reference
|
|
||||||
* @return the created git_reference or NULL on error
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(git_reference *) git_reference__alloc(
|
|
||||||
git_refdb *refdb,
|
|
||||||
const char *name,
|
|
||||||
const git_oid *oid,
|
|
||||||
const char *symbolic);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new reference database with no backends.
|
* Create a new reference database with no backends.
|
||||||
*
|
*
|
||||||
@ -78,20 +62,6 @@ GIT_EXTERN(int) git_refdb_compress(git_refdb *refdb);
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(void) git_refdb_free(git_refdb *refdb);
|
GIT_EXTERN(void) git_refdb_free(git_refdb *refdb);
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the custom backend to an existing reference DB
|
|
||||||
*
|
|
||||||
* Read <refdb_backends.h> for more information.
|
|
||||||
*
|
|
||||||
* @param refdb database to add the backend to
|
|
||||||
* @param backend pointer to a git_refdb_backend instance
|
|
||||||
* @param priority Value for ordering the backends queue
|
|
||||||
* @return 0 on success; error code otherwise
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(int) git_refdb_set_backend(
|
|
||||||
git_refdb *refdb,
|
|
||||||
git_refdb_backend *backend);
|
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
GIT_END_DECL
|
GIT_END_DECL
|
||||||
|
|
||||||
|
@ -1,109 +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_refdb_backend_h__
|
|
||||||
#define INCLUDE_git_refdb_backend_h__
|
|
||||||
|
|
||||||
#include "common.h"
|
|
||||||
#include "types.h"
|
|
||||||
#include "oid.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @file git2/refdb_backend.h
|
|
||||||
* @brief Git custom refs backend functions
|
|
||||||
* @defgroup git_refdb_backend Git custom refs backend API
|
|
||||||
* @ingroup Git
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
GIT_BEGIN_DECL
|
|
||||||
|
|
||||||
/** An instance for a custom backend */
|
|
||||||
struct git_refdb_backend {
|
|
||||||
unsigned int version;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Queries the refdb backend to determine if the given ref_name
|
|
||||||
* exists. A refdb implementation must provide this function.
|
|
||||||
*/
|
|
||||||
int (*exists)(
|
|
||||||
int *exists,
|
|
||||||
struct git_refdb_backend *backend,
|
|
||||||
const char *ref_name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Queries the refdb backend for a given reference. A refdb
|
|
||||||
* implementation must provide this function.
|
|
||||||
*/
|
|
||||||
int (*lookup)(
|
|
||||||
git_reference **out,
|
|
||||||
struct git_refdb_backend *backend,
|
|
||||||
const char *ref_name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enumerates each reference in the refdb. A refdb implementation must
|
|
||||||
* provide this function.
|
|
||||||
*/
|
|
||||||
int (*foreach)(
|
|
||||||
struct git_refdb_backend *backend,
|
|
||||||
unsigned int list_flags,
|
|
||||||
git_reference_foreach_cb callback,
|
|
||||||
void *payload);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enumerates each reference in the refdb that matches the given
|
|
||||||
* glob string. A refdb implementation may provide this function;
|
|
||||||
* if it is not provided, foreach will be used and the results filtered
|
|
||||||
* against the glob.
|
|
||||||
*/
|
|
||||||
int (*foreach_glob)(
|
|
||||||
struct git_refdb_backend *backend,
|
|
||||||
const char *glob,
|
|
||||||
unsigned int list_flags,
|
|
||||||
git_reference_foreach_cb callback,
|
|
||||||
void *payload);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the given reference to the refdb. A refdb implementation
|
|
||||||
* must provide this function.
|
|
||||||
*/
|
|
||||||
int (*write)(struct git_refdb_backend *backend, const git_reference *ref);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes the given reference from the refdb. A refdb implementation
|
|
||||||
* must provide this function.
|
|
||||||
*/
|
|
||||||
int (*delete)(struct git_refdb_backend *backend, const git_reference *ref);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Suggests that the given refdb compress or optimize its references.
|
|
||||||
* This mechanism is implementation specific. (For on-disk reference
|
|
||||||
* databases, this may pack all loose references.) A refdb
|
|
||||||
* implementation may provide this function; if it is not provided,
|
|
||||||
* nothing will be done.
|
|
||||||
*/
|
|
||||||
int (*compress)(struct git_refdb_backend *backend);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Frees any resources held by the refdb. A refdb implementation may
|
|
||||||
* provide this function; if it is not provided, nothing will be done.
|
|
||||||
*/
|
|
||||||
void (*free)(struct git_refdb_backend *backend);
|
|
||||||
};
|
|
||||||
|
|
||||||
#define GIT_ODB_BACKEND_VERSION 1
|
|
||||||
#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructors for default refdb backends.
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(int) git_refdb_backend_fs(
|
|
||||||
struct git_refdb_backend **backend_out,
|
|
||||||
git_repository *repo,
|
|
||||||
git_refdb *refdb);
|
|
||||||
|
|
||||||
GIT_END_DECL
|
|
||||||
|
|
||||||
#endif
|
|
@ -48,12 +48,25 @@ GIT_EXTERN(int) git_reference_lookup(git_reference **out, git_repository *repo,
|
|||||||
*
|
*
|
||||||
* @param out Pointer to oid to be filled in
|
* @param out Pointer to oid to be filled in
|
||||||
* @param repo The repository in which to look up the reference
|
* @param repo The repository in which to look up the reference
|
||||||
* @param name The long name for the reference
|
* @param name The long name for the reference (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0, ...)
|
||||||
* @return 0 on success, ENOTFOUND, EINVALIDSPEC or an error code.
|
* @return 0 on success, ENOTFOUND, EINVALIDSPEC or an error code.
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_reference_name_to_id(
|
GIT_EXTERN(int) git_reference_name_to_id(
|
||||||
git_oid *out, git_repository *repo, const char *name);
|
git_oid *out, git_repository *repo, const char *name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @param out pointer in which to store the reference
|
||||||
|
* @param repo the repository in which to look
|
||||||
|
* @param shorthand the short name for the reference
|
||||||
|
* @return 0 or an error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_reference_dwim(git_reference **out, git_repository *repo, const char *shorthand);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new symbolic reference.
|
* Create a new symbolic reference.
|
||||||
*
|
*
|
||||||
@ -132,6 +145,17 @@ GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo,
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(const git_oid *) git_reference_target(const git_reference *ref);
|
GIT_EXTERN(const git_oid *) git_reference_target(const git_reference *ref);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the peeled OID target of this reference.
|
||||||
|
*
|
||||||
|
* This peeled OID only applies to direct references that point to
|
||||||
|
* a hard Tag object: it is the result of peeling such Tag.
|
||||||
|
*
|
||||||
|
* @param ref The reference
|
||||||
|
* @return a pointer to the oid if available, NULL otherwise
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(const git_oid *) git_reference_target_peel(const git_reference *ref);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get full name to the reference pointed to by a symbolic reference.
|
* Get full name to the reference pointed to by a symbolic reference.
|
||||||
*
|
*
|
||||||
@ -174,7 +198,7 @@ GIT_EXTERN(const char *) git_reference_name(const git_reference *ref);
|
|||||||
* If a direct reference is passed as an argument, a copy of that
|
* If a direct reference is passed as an argument, a copy of that
|
||||||
* reference is returned. This copy must be manually freed too.
|
* reference is returned. This copy must be manually freed too.
|
||||||
*
|
*
|
||||||
* @param resolved_ref Pointer to the peeled reference
|
* @param out Pointer to the peeled reference
|
||||||
* @param ref The reference
|
* @param ref The reference
|
||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
@ -233,11 +257,6 @@ GIT_EXTERN(int) git_reference_set_target(
|
|||||||
* The new name will be checked for validity.
|
* The new name will be checked for validity.
|
||||||
* See `git_reference_create_symbolic()` for rules about valid names.
|
* See `git_reference_create_symbolic()` for rules about valid names.
|
||||||
*
|
*
|
||||||
* On success, the given git_reference will be deleted from disk and a
|
|
||||||
* new `git_reference` will be returned.
|
|
||||||
*
|
|
||||||
* The reference will be immediately renamed in-memory and on disk.
|
|
||||||
*
|
|
||||||
* If the `force` flag is not enabled, and there's already
|
* If the `force` flag is not enabled, and there's already
|
||||||
* a reference with the given name, the renaming will fail.
|
* a reference with the given name, the renaming will fail.
|
||||||
*
|
*
|
||||||
@ -247,13 +266,13 @@ GIT_EXTERN(int) git_reference_set_target(
|
|||||||
* the reflog if it exists.
|
* the reflog if it exists.
|
||||||
*
|
*
|
||||||
* @param ref The reference to rename
|
* @param ref The reference to rename
|
||||||
* @param name The new name for the reference
|
* @param new_name The new name for the reference
|
||||||
* @param force Overwrite an existing reference
|
* @param force Overwrite an existing reference
|
||||||
* @return 0 on success, EINVALIDSPEC, EEXISTS or an error code
|
* @return 0 on success, EINVALIDSPEC, EEXISTS or an error code
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_reference_rename(
|
GIT_EXTERN(int) git_reference_rename(
|
||||||
git_reference **out,
|
git_reference **new_ref,
|
||||||
git_reference *ref,
|
git_reference *ref,
|
||||||
const char *new_name,
|
const char *new_name,
|
||||||
int force);
|
int force);
|
||||||
@ -273,12 +292,6 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref);
|
|||||||
/**
|
/**
|
||||||
* Fill a list with all the references that can be found in a repository.
|
* Fill a list with all the references that can be found in a repository.
|
||||||
*
|
*
|
||||||
* Using the `list_flags` parameter, the listed references may be filtered
|
|
||||||
* by type (`GIT_REF_OID` or `GIT_REF_SYMBOLIC`) or using a bitwise OR of
|
|
||||||
* `git_ref_t` values. To include packed refs, include `GIT_REF_PACKED`.
|
|
||||||
* For convenience, use the value `GIT_REF_LISTALL` to obtain all
|
|
||||||
* references, including packed ones.
|
|
||||||
*
|
|
||||||
* The string array will be filled with the names of all references; these
|
* The string array will be filled with the names of all references; these
|
||||||
* values are owned by the user and should be free'd manually when no
|
* values are owned by the user and should be free'd manually when no
|
||||||
* longer needed, using `git_strarray_free()`.
|
* longer needed, using `git_strarray_free()`.
|
||||||
@ -286,39 +299,36 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref);
|
|||||||
* @param array Pointer to a git_strarray structure where
|
* @param array Pointer to a git_strarray structure where
|
||||||
* the reference names will be stored
|
* the reference names will be stored
|
||||||
* @param repo Repository where to find the refs
|
* @param repo Repository where to find the refs
|
||||||
* @param list_flags Filtering flags for the reference listing
|
|
||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo, unsigned int list_flags);
|
GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo);
|
||||||
|
|
||||||
typedef int (*git_reference_foreach_cb)(const char *refname, void *payload);
|
typedef int (*git_reference_foreach_cb)(git_reference *reference, void *payload);
|
||||||
|
typedef int (*git_reference_foreach_name_cb)(const char *name, void *payload);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a callback on each reference in the repository.
|
* Perform a callback on each reference in the repository.
|
||||||
*
|
*
|
||||||
* Using the `list_flags` parameter, the references may be filtered by
|
|
||||||
* type (`GIT_REF_OID` or `GIT_REF_SYMBOLIC`) or using a bitwise OR of
|
|
||||||
* `git_ref_t` values. To include packed refs, include `GIT_REF_PACKED`.
|
|
||||||
* For convenience, use the value `GIT_REF_LISTALL` to obtain all
|
|
||||||
* references, including packed ones.
|
|
||||||
*
|
|
||||||
* The `callback` function will be called for each reference in the
|
* 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 name of the reference and the `payload` value
|
||||||
* passed to this method. Returning a non-zero value from the callback
|
* passed to this method. Returning a non-zero value from the callback
|
||||||
* will terminate the iteration.
|
* will terminate the iteration.
|
||||||
*
|
*
|
||||||
* @param repo Repository where to find the refs
|
* @param repo Repository where to find the refs
|
||||||
* @param list_flags Filtering flags for the reference listing.
|
|
||||||
* @param callback Function which will be called for every listed ref
|
* @param callback Function which will be called for every listed ref
|
||||||
* @param payload Additional data to pass to the callback
|
* @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, GIT_EUSER on non-zero callback, or error code
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_reference_foreach(
|
GIT_EXTERN(int) git_reference_foreach(
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
unsigned int list_flags,
|
|
||||||
git_reference_foreach_cb callback,
|
git_reference_foreach_cb callback,
|
||||||
void *payload);
|
void *payload);
|
||||||
|
|
||||||
|
GIT_EXTERN(int) git_reference_foreach_name(
|
||||||
|
git_repository *repo,
|
||||||
|
git_reference_foreach_name_cb callback,
|
||||||
|
void *payload);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free the given reference.
|
* Free the given reference.
|
||||||
*
|
*
|
||||||
@ -335,6 +345,49 @@ GIT_EXTERN(void) git_reference_free(git_reference *ref);
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2);
|
GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an iterator for the repo's references
|
||||||
|
*
|
||||||
|
* @param out pointer in which to store the iterator
|
||||||
|
* @param repo the repository
|
||||||
|
* @return 0 or an error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_reference_iterator_new(
|
||||||
|
git_reference_iterator **out,
|
||||||
|
git_repository *repo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an iterator for the repo's references that match the
|
||||||
|
* specified glob
|
||||||
|
*
|
||||||
|
* @param out pointer in which to store the iterator
|
||||||
|
* @param repo the repository
|
||||||
|
* @param glob the glob to match against the reference names
|
||||||
|
* @return 0 or an error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_reference_iterator_glob_new(
|
||||||
|
git_reference_iterator **out,
|
||||||
|
git_repository *repo,
|
||||||
|
const char *glob);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next reference
|
||||||
|
*
|
||||||
|
* @param out pointer in which to store the reference
|
||||||
|
* @param iter the iterator
|
||||||
|
* @return 0, GIT_ITEROVER if there are no more; or an error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_reference_next(git_reference **out, git_reference_iterator *iter);
|
||||||
|
|
||||||
|
GIT_EXTERN(int) git_reference_next_name(const char **out, git_reference_iterator *iter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free the iterator and its associated resources
|
||||||
|
*
|
||||||
|
* @param iter the iterator to free
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(void) git_reference_iterator_free(git_reference_iterator *iter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a callback on each reference in the repository whose name
|
* Perform a callback on each reference in the repository whose name
|
||||||
* matches the given pattern.
|
* matches the given pattern.
|
||||||
@ -349,7 +402,6 @@ GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2);
|
|||||||
*
|
*
|
||||||
* @param repo Repository where to find the refs
|
* @param repo Repository where to find the refs
|
||||||
* @param glob Pattern to match (fnmatch-style) against reference name.
|
* @param glob Pattern to match (fnmatch-style) against reference name.
|
||||||
* @param list_flags Filtering flags for the reference listing.
|
|
||||||
* @param callback Function which will be called for every listed ref
|
* @param callback Function which will be called for every listed ref
|
||||||
* @param payload Additional data to pass to the callback
|
* @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, GIT_EUSER on non-zero callback, or error code
|
||||||
@ -357,8 +409,7 @@ GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2);
|
|||||||
GIT_EXTERN(int) git_reference_foreach_glob(
|
GIT_EXTERN(int) git_reference_foreach_glob(
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const char *glob,
|
const char *glob,
|
||||||
unsigned int list_flags,
|
git_reference_foreach_name_cb callback,
|
||||||
git_reference_foreach_cb callback,
|
|
||||||
void *payload);
|
void *payload);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -411,6 +462,13 @@ typedef enum {
|
|||||||
* (e.g., foo/<star>/bar but not foo/bar<star>).
|
* (e.g., foo/<star>/bar but not foo/bar<star>).
|
||||||
*/
|
*/
|
||||||
GIT_REF_FORMAT_REFSPEC_PATTERN = (1 << 1),
|
GIT_REF_FORMAT_REFSPEC_PATTERN = (1 << 1),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interpret the name as part of a refspec in shorthand form
|
||||||
|
* so the `ONELEVEL` naming rules aren't enforced and 'master'
|
||||||
|
* becomes a valid name.
|
||||||
|
*/
|
||||||
|
GIT_REF_FORMAT_REFSPEC_SHORTHAND = (1 << 2),
|
||||||
} git_reference_normalize_t;
|
} git_reference_normalize_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -448,9 +506,9 @@ GIT_EXTERN(int) git_reference_normalize_name(
|
|||||||
* If you pass `GIT_OBJ_ANY` as the target type, then the object
|
* If you pass `GIT_OBJ_ANY` as the target type, then the object
|
||||||
* will be peeled until a non-tag object is met.
|
* will be peeled until a non-tag object is met.
|
||||||
*
|
*
|
||||||
* @param peeled Pointer to the peeled git_object
|
* @param out Pointer to the peeled git_object
|
||||||
* @param ref The reference to be processed
|
* @param ref The reference to be processed
|
||||||
* @param target_type The type of the requested object (GIT_OBJ_COMMIT,
|
* @param type The type of the requested object (GIT_OBJ_COMMIT,
|
||||||
* GIT_OBJ_TAG, GIT_OBJ_TREE, GIT_OBJ_BLOB or GIT_OBJ_ANY).
|
* 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
|
* @return 0 on success, GIT_EAMBIGUOUS, GIT_ENOTFOUND or an error code
|
||||||
*/
|
*/
|
||||||
@ -475,6 +533,21 @@ GIT_EXTERN(int) git_reference_peel(
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_reference_is_valid_name(const char *refname);
|
GIT_EXTERN(int) git_reference_is_valid_name(const char *refname);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the reference's short name
|
||||||
|
*
|
||||||
|
* This will transform the reference name into a name "human-readable"
|
||||||
|
* version. If no shortname is appropriate, it will return the full
|
||||||
|
* name.
|
||||||
|
*
|
||||||
|
* The memory is owned by the reference and must not be freed.
|
||||||
|
*
|
||||||
|
* @param ref a reference
|
||||||
|
* @return the human-readable version of the name
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(const char *) git_reference_shorthand(git_reference *ref);
|
||||||
|
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
GIT_END_DECL
|
GIT_END_DECL
|
||||||
#endif
|
#endif
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
#include "net.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file git2/refspec.h
|
* @file git2/refspec.h
|
||||||
@ -35,6 +36,14 @@ GIT_EXTERN(const char *) git_refspec_src(const git_refspec *refspec);
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(const char *) git_refspec_dst(const git_refspec *refspec);
|
GIT_EXTERN(const char *) git_refspec_dst(const git_refspec *refspec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the refspec's string
|
||||||
|
*
|
||||||
|
* @param refspec the refspec
|
||||||
|
* @returns the refspec's original string
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(const char *) git_refspec_string(const git_refspec *refspec);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the force update setting
|
* Get the force update setting
|
||||||
*
|
*
|
||||||
@ -43,6 +52,14 @@ GIT_EXTERN(const char *) git_refspec_dst(const git_refspec *refspec);
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_refspec_force(const git_refspec *refspec);
|
GIT_EXTERN(int) git_refspec_force(const git_refspec *refspec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the refspec's direction.
|
||||||
|
*
|
||||||
|
* @param spec refspec
|
||||||
|
* @return GIT_DIRECTION_FETCH or GIT_DIRECTION_PUSH
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(git_direction) git_refspec_direction(const git_refspec *spec);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a refspec's source descriptor matches a reference
|
* Check if a refspec's source descriptor matches a reference
|
||||||
*
|
*
|
||||||
|
@ -142,39 +142,79 @@ GIT_EXTERN(int) git_remote_set_url(git_remote *remote, const char* url);
|
|||||||
GIT_EXTERN(int) git_remote_set_pushurl(git_remote *remote, const char* url);
|
GIT_EXTERN(int) git_remote_set_pushurl(git_remote *remote, const char* url);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the remote's fetch refspec
|
* Add a fetch refspec to the remote
|
||||||
*
|
*
|
||||||
* @param remote the remote
|
* @param remote the remote
|
||||||
* @apram spec the new fetch refspec
|
* @apram refspec the new fetch refspec
|
||||||
* @return 0 or an error value
|
* @return 0 or an error value
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_remote_set_fetchspec(git_remote *remote, const char *spec);
|
GIT_EXTERN(int) git_remote_add_fetch(git_remote *remote, const char *refspec);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the fetch refspec
|
* Get the remote's list of fetch refspecs
|
||||||
*
|
*
|
||||||
* @param remote the remote
|
* The memory is owned by the user and should be freed with
|
||||||
* @return a pointer to the fetch refspec or NULL if it doesn't exist
|
* `git_strarray_free`.
|
||||||
|
*
|
||||||
|
* @param array pointer to the array in which to store the strings
|
||||||
|
* @param remote the remote to query
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(const git_refspec *) git_remote_fetchspec(const git_remote *remote);
|
GIT_EXTERN(int) git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the remote's push refspec
|
* Add a push refspec to the remote
|
||||||
*
|
*
|
||||||
* @param remote the remote
|
* @param remote the remote
|
||||||
* @param spec the new push refspec
|
* @param refspec the new push refspec
|
||||||
* @return 0 or an error value
|
* @return 0 or an error value
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_remote_set_pushspec(git_remote *remote, const char *spec);
|
GIT_EXTERN(int) git_remote_add_push(git_remote *remote, const char *refspec);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the push refspec
|
* Get the remote's list of push refspecs
|
||||||
|
*
|
||||||
|
* The memory is owned by the user and should be freed with
|
||||||
|
* `git_strarray_free`.
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the refspecs
|
||||||
|
*
|
||||||
|
* Remove all configured fetch and push refspecs from the remote.
|
||||||
*
|
*
|
||||||
* @param remote the remote
|
* @param remote the remote
|
||||||
* @return a pointer to the push refspec or NULL if it doesn't exist
|
|
||||||
*/
|
*/
|
||||||
|
GIT_EXTERN(void) git_remote_clear_refspecs(git_remote *remote);
|
||||||
|
|
||||||
GIT_EXTERN(const git_refspec *) git_remote_pushspec(const git_remote *remote);
|
/**
|
||||||
|
* Get the number of refspecs for a remote
|
||||||
|
*
|
||||||
|
* @param remote the remote
|
||||||
|
* @return the amount of refspecs configured in this remote
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(size_t) git_remote_refspec_count(git_remote *remote);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a refspec from the remote
|
||||||
|
*
|
||||||
|
* @param remote the remote to query
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a refspec from the remote
|
||||||
|
*
|
||||||
|
* @param remote the remote to query
|
||||||
|
* @param n the refspec to remove
|
||||||
|
* @return 0 or GIT_ENOTFOUND
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_remote_remove_refspec(git_remote *remote, size_t n);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open a connection to a remote
|
* Open a connection to a remote
|
||||||
@ -184,7 +224,8 @@ GIT_EXTERN(const git_refspec *) git_remote_pushspec(const git_remote *remote);
|
|||||||
* starts up a specific binary which can only do the one or the other.
|
* starts up a specific binary which can only do the one or the other.
|
||||||
*
|
*
|
||||||
* @param remote the remote to connect to
|
* @param remote the remote to connect to
|
||||||
* @param direction whether you want to receive or send data
|
* @param direction GIT_DIRECTION_FETCH if you want to fetch or
|
||||||
|
* GIT_DIRECTION_PUSH if you want to push
|
||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction);
|
GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction);
|
||||||
@ -218,7 +259,7 @@ GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void
|
|||||||
* @param progress_cb function to call with progress information. Be aware that
|
* @param progress_cb function to call with progress information. Be aware that
|
||||||
* this is called inline with network and indexing operations, so performance
|
* this is called inline with network and indexing operations, so performance
|
||||||
* may be affected.
|
* may be affected.
|
||||||
* @param progress_payload payload for the progress callback
|
* @param payload payload for the progress callback
|
||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_remote_download(
|
GIT_EXTERN(int) git_remote_download(
|
||||||
@ -279,7 +320,7 @@ GIT_EXTERN(int) git_remote_update_tips(git_remote *remote);
|
|||||||
* Return whether a string is a valid remote URL
|
* Return whether a string is a valid remote URL
|
||||||
*
|
*
|
||||||
* @param url the url to check
|
* @param url the url to check
|
||||||
* @param 1 if the url is valid, 0 otherwise
|
* @return 1 if the url is valid, 0 otherwise
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_remote_valid_url(const char *url);
|
GIT_EXTERN(int) git_remote_valid_url(const char *url);
|
||||||
|
|
||||||
@ -385,10 +426,9 @@ GIT_EXTERN(int) git_remote_set_callbacks(git_remote *remote, git_remote_callback
|
|||||||
GIT_EXTERN(const git_transfer_progress *) git_remote_stats(git_remote *remote);
|
GIT_EXTERN(const git_transfer_progress *) git_remote_stats(git_remote *remote);
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GIT_REMOTE_DOWNLOAD_TAGS_UNSET,
|
GIT_REMOTE_DOWNLOAD_TAGS_AUTO = 0,
|
||||||
GIT_REMOTE_DOWNLOAD_TAGS_NONE,
|
GIT_REMOTE_DOWNLOAD_TAGS_NONE = 1,
|
||||||
GIT_REMOTE_DOWNLOAD_TAGS_AUTO,
|
GIT_REMOTE_DOWNLOAD_TAGS_ALL = 2
|
||||||
GIT_REMOTE_DOWNLOAD_TAGS_ALL
|
|
||||||
} git_remote_autotag_option_t;
|
} git_remote_autotag_option_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,6 +123,19 @@ GIT_EXTERN(int) git_repository_open_ext(
|
|||||||
unsigned int flags,
|
unsigned int flags,
|
||||||
const char *ceiling_dirs);
|
const char *ceiling_dirs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a bare repository on the serverside.
|
||||||
|
*
|
||||||
|
* This is a fast open for bare repositories that will come in handy
|
||||||
|
* if you're e.g. hosting git repositories and need to access them
|
||||||
|
* efficiently
|
||||||
|
*
|
||||||
|
* @param out Pointer to the repo which will be opened.
|
||||||
|
* @param bare_path Direct path to the bare repository
|
||||||
|
* @return 0 on success, or an error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_repository_open_bare(git_repository **out, const char *bare_path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Free a previously allocated repository
|
* Free a previously allocated repository
|
||||||
*
|
*
|
||||||
@ -387,21 +400,6 @@ GIT_EXTERN(int) git_repository_is_bare(git_repository *repo);
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_repository_config(git_config **out, git_repository *repo);
|
GIT_EXTERN(int) git_repository_config(git_config **out, git_repository *repo);
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the configuration file for this repository
|
|
||||||
*
|
|
||||||
* This configuration file will be used for all configuration
|
|
||||||
* queries involving this repository.
|
|
||||||
*
|
|
||||||
* The repository will keep a reference to the config file;
|
|
||||||
* the user must still free the config after setting it
|
|
||||||
* to the repository, or it will leak.
|
|
||||||
*
|
|
||||||
* @param repo A repository object
|
|
||||||
* @param config A Config object
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(void) git_repository_set_config(git_repository *repo, git_config *config);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Object Database for this repository.
|
* Get the Object Database for this repository.
|
||||||
*
|
*
|
||||||
@ -418,21 +416,6 @@ GIT_EXTERN(void) git_repository_set_config(git_repository *repo, git_config *con
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_repository_odb(git_odb **out, git_repository *repo);
|
GIT_EXTERN(int) git_repository_odb(git_odb **out, git_repository *repo);
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the Object Database for this repository
|
|
||||||
*
|
|
||||||
* The ODB will be used for all object-related operations
|
|
||||||
* involving this repository.
|
|
||||||
*
|
|
||||||
* The repository will keep a reference to the ODB; the user
|
|
||||||
* must still free the ODB object after setting it to the
|
|
||||||
* repository, or it will leak.
|
|
||||||
*
|
|
||||||
* @param repo A repository object
|
|
||||||
* @param odb An ODB object
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(void) git_repository_set_odb(git_repository *repo, git_odb *odb);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Reference Database Backend for this repository.
|
* Get the Reference Database Backend for this repository.
|
||||||
*
|
*
|
||||||
@ -449,23 +432,6 @@ GIT_EXTERN(void) git_repository_set_odb(git_repository *repo, git_odb *odb);
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_repository_refdb(git_refdb **out, git_repository *repo);
|
GIT_EXTERN(int) git_repository_refdb(git_refdb **out, git_repository *repo);
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the Reference Database Backend for this repository
|
|
||||||
*
|
|
||||||
* The refdb will be used for all reference related operations
|
|
||||||
* involving this repository.
|
|
||||||
*
|
|
||||||
* The repository will keep a reference to the refdb; the user
|
|
||||||
* must still free the refdb object after setting it to the
|
|
||||||
* repository, or it will leak.
|
|
||||||
*
|
|
||||||
* @param repo A repository object
|
|
||||||
* @param refdb An refdb object
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(void) git_repository_set_refdb(
|
|
||||||
git_repository *repo,
|
|
||||||
git_refdb *refdb);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the Index file for this repository.
|
* Get the Index file for this repository.
|
||||||
*
|
*
|
||||||
@ -482,21 +448,6 @@ GIT_EXTERN(void) git_repository_set_refdb(
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_repository_index(git_index **out, git_repository *repo);
|
GIT_EXTERN(int) git_repository_index(git_index **out, git_repository *repo);
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the index file for this repository
|
|
||||||
*
|
|
||||||
* This index will be used for all index-related operations
|
|
||||||
* involving this repository.
|
|
||||||
*
|
|
||||||
* The repository will keep a reference to the index file;
|
|
||||||
* the user must still free the index after setting it
|
|
||||||
* to the repository, or it will leak.
|
|
||||||
*
|
|
||||||
* @param repo A repository object
|
|
||||||
* @param index An index object
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(void) git_repository_set_index(git_repository *repo, git_index *index);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve git's prepared message
|
* Retrieve git's prepared message
|
||||||
*
|
*
|
||||||
@ -509,10 +460,19 @@ GIT_EXTERN(void) git_repository_set_index(git_repository *repo, git_index *index
|
|||||||
* Use this function to get the contents of this file. Don't forget to
|
* Use this function to get the contents of this file. Don't forget to
|
||||||
* remove the file after you create the commit.
|
* 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 out Buffer to write data into or NULL to just read required size
|
||||||
* @param len Length of buffer in bytes
|
* @param len Length of `out` buffer in bytes
|
||||||
* @param repo Repository to read prepared message from
|
* @param repo Repository to read prepared message from
|
||||||
* @return Bytes written to buffer, GIT_ENOTFOUND if no message, or -1 on error
|
* @return GIT_ENOUTFOUND if no message exists, other value < 0 for other
|
||||||
|
* errors, or total bytes in message (may be > `len`) on success
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_repository_message(char *out, size_t len, git_repository *repo);
|
GIT_EXTERN(int) git_repository_message(char *out, size_t len, git_repository *repo);
|
||||||
|
|
||||||
@ -559,7 +519,7 @@ typedef int (*git_repository_mergehead_foreach_cb)(const git_oid *oid,
|
|||||||
*
|
*
|
||||||
* @param repo A repository object
|
* @param repo A repository object
|
||||||
* @param callback Callback function
|
* @param callback Callback function
|
||||||
* @param apyload Pointer to callback data (optional)
|
* @param payload Pointer to callback data (optional)
|
||||||
* @return 0 on success, GIT_ENOTFOUND, GIT_EUSER or error
|
* @return 0 on success, GIT_ENOTFOUND, GIT_EUSER or error
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_repository_mergehead_foreach(git_repository *repo,
|
GIT_EXTERN(int) git_repository_mergehead_foreach(git_repository *repo,
|
||||||
@ -585,11 +545,11 @@ GIT_EXTERN(int) git_repository_mergehead_foreach(git_repository *repo,
|
|||||||
* applied when calculating the hash.
|
* applied when calculating the hash.
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_repository_hashfile(
|
GIT_EXTERN(int) git_repository_hashfile(
|
||||||
git_oid *out,
|
git_oid *out,
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const char *path,
|
const char *path,
|
||||||
git_otype type,
|
git_otype type,
|
||||||
const char *as_path);
|
const char *as_path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make the repository HEAD point to the specified reference.
|
* Make the repository HEAD point to the specified reference.
|
||||||
@ -675,6 +635,37 @@ typedef enum {
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_repository_state(git_repository *repo);
|
GIT_EXTERN(int) git_repository_state(git_repository *repo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the active namespace for this Git Repository
|
||||||
|
*
|
||||||
|
* This namespace affects all reference operations for the repo.
|
||||||
|
* See `man gitnamespaces`
|
||||||
|
*
|
||||||
|
* @param repo The repo
|
||||||
|
* @param nmspace The namespace. This should not include the refs
|
||||||
|
* folder, e.g. to namespace all references under `refs/namespaces/foo/`,
|
||||||
|
* use `foo` as the namespace.
|
||||||
|
* @return 0 on success, -1 on error
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_repository_set_namespace(git_repository *repo, const char *nmspace);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the currently active namespace for this repository
|
||||||
|
*
|
||||||
|
* @param repo The repo
|
||||||
|
* @return the active namespace, or NULL if there isn't one
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(const char *) git_repository_get_namespace(git_repository *repo);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the repository was a shallow clone
|
||||||
|
*
|
||||||
|
* @param repo The repository
|
||||||
|
* @return 1 if shallow, zero if not
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_repository_is_shallow(git_repository *repo);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
GIT_END_DECL
|
GIT_END_DECL
|
||||||
#endif
|
#endif
|
||||||
|
@ -72,9 +72,9 @@ GIT_EXTERN(int) git_reset(
|
|||||||
* @return 0 on success or an error code < 0
|
* @return 0 on success or an error code < 0
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_reset_default(
|
GIT_EXTERN(int) git_reset_default(
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
git_object *target,
|
git_object *target,
|
||||||
git_strarray* pathspecs);
|
git_strarray* pathspecs);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
GIT_END_DECL
|
GIT_END_DECL
|
||||||
|
@ -32,6 +32,28 @@ GIT_BEGIN_DECL
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_revparse_single(git_object **out, git_repository *repo, const char *spec);
|
GIT_EXTERN(int) git_revparse_single(git_object **out, git_repository *repo, const char *spec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a single object, as specified by a revision string.
|
||||||
|
* See `man gitrevisions`,
|
||||||
|
* or http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
|
||||||
|
* information on the syntax accepted.
|
||||||
|
*
|
||||||
|
* In some cases (`@{<-n>}` or `<branchname>@{upstream}`), the expression may
|
||||||
|
* point to an intermediate reference. When such expressions are being passed
|
||||||
|
* in, `reference_out` will be valued as well.
|
||||||
|
*
|
||||||
|
* @param object_out pointer to output object
|
||||||
|
* @param reference_out pointer to output reference or NULL
|
||||||
|
* @param repo the repository to search in
|
||||||
|
* @param spec the textual specification for an object
|
||||||
|
* @return 0 on success, GIT_ENOTFOUND, GIT_EAMBIGUOUS, GIT_EINVALIDSPEC
|
||||||
|
* or an error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_revparse_ext(
|
||||||
|
git_object **object_out,
|
||||||
|
git_reference **reference_out,
|
||||||
|
git_repository *repo,
|
||||||
|
const char *spec);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Revparse flags. These indicate the intended behavior of the spec passed to
|
* Revparse flags. These indicate the intended behavior of the spec passed to
|
||||||
|
@ -89,7 +89,7 @@ typedef int (*git_stash_cb)(
|
|||||||
*
|
*
|
||||||
* @param repo Repository where to find the stash.
|
* @param repo Repository where to find the stash.
|
||||||
*
|
*
|
||||||
* @param callabck Callback to invoke per found stashed state. The most recent
|
* @param callback Callback to invoke per found stashed state. The most recent
|
||||||
* stash state will be enumerated first.
|
* stash state will be enumerated first.
|
||||||
*
|
*
|
||||||
* @param payload Extra parameter to callback function.
|
* @param payload Extra parameter to callback function.
|
||||||
|
@ -42,6 +42,7 @@ typedef enum {
|
|||||||
GIT_STATUS_WT_MODIFIED = (1u << 8),
|
GIT_STATUS_WT_MODIFIED = (1u << 8),
|
||||||
GIT_STATUS_WT_DELETED = (1u << 9),
|
GIT_STATUS_WT_DELETED = (1u << 9),
|
||||||
GIT_STATUS_WT_TYPECHANGE = (1u << 10),
|
GIT_STATUS_WT_TYPECHANGE = (1u << 10),
|
||||||
|
GIT_STATUS_WT_RENAMED = (1u << 11),
|
||||||
|
|
||||||
GIT_STATUS_IGNORED = (1u << 14),
|
GIT_STATUS_IGNORED = (1u << 14),
|
||||||
} git_status_t;
|
} git_status_t;
|
||||||
@ -58,44 +59,20 @@ typedef enum {
|
|||||||
typedef int (*git_status_cb)(
|
typedef int (*git_status_cb)(
|
||||||
const char *path, unsigned int status_flags, void *payload);
|
const char *path, unsigned int status_flags, void *payload);
|
||||||
|
|
||||||
/**
|
|
||||||
* Gather file statuses and run a callback for each one.
|
|
||||||
*
|
|
||||||
* The callback is passed the path of the file, the status (a combination of
|
|
||||||
* the `git_status_t` values above) and the `payload` data pointer passed
|
|
||||||
* into this function.
|
|
||||||
*
|
|
||||||
* If the callback returns a non-zero value, this function will stop looping
|
|
||||||
* and return GIT_EUSER.
|
|
||||||
*
|
|
||||||
* @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
|
|
||||||
*/
|
|
||||||
GIT_EXTERN(int) git_status_foreach(
|
|
||||||
git_repository *repo,
|
|
||||||
git_status_cb callback,
|
|
||||||
void *payload);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For extended status, select the files on which to report status.
|
* For extended status, select the files on which to report status.
|
||||||
*
|
*
|
||||||
* - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This is the
|
* - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This roughly
|
||||||
* rough equivalent of `git status --porcelain` where each file
|
* matches `git status --porcelain` where each file gets a callback
|
||||||
* will receive a callback indicating its status in the index and
|
* indicating its status in the index and in the working directory.
|
||||||
* in the workdir.
|
* - GIT_STATUS_SHOW_INDEX_ONLY only gives status based on HEAD to index
|
||||||
* - GIT_STATUS_SHOW_INDEX_ONLY will only make callbacks for index
|
* comparison, not looking at working directory changes.
|
||||||
* side of status. The status of the index contents relative to
|
* - GIT_STATUS_SHOW_WORKDIR_ONLY only gives status based on index to
|
||||||
* the HEAD will be given.
|
* working directory comparison, not comparing the index to the HEAD.
|
||||||
* - GIT_STATUS_SHOW_WORKDIR_ONLY will only make callbacks for the
|
* - GIT_STATUS_SHOW_INDEX_THEN_WORKDIR runs index-only then workdir-only,
|
||||||
* workdir side of status, reporting the status of workdir content
|
* issuing (up to) two callbacks per file (first index, then workdir).
|
||||||
* relative to the index.
|
* This is slightly more efficient than separate calls and can make it
|
||||||
* - GIT_STATUS_SHOW_INDEX_THEN_WORKDIR behaves like index-only
|
* easier to emulate plain `git status` text output.
|
||||||
* followed by workdir-only, causing two callbacks to be issued
|
|
||||||
* per file (first index then workdir). This is slightly more
|
|
||||||
* efficient than making separate calls. This makes it easier to
|
|
||||||
* emulate the output of a plain `git status`.
|
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
|
GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
|
||||||
@ -110,26 +87,36 @@ typedef enum {
|
|||||||
* - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should be made
|
* - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should be made
|
||||||
* on untracked files. These will only be made if the workdir files are
|
* on untracked files. These will only be made if the workdir files are
|
||||||
* included in the status "show" option.
|
* included in the status "show" option.
|
||||||
* - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files should get
|
* - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files get callbacks.
|
||||||
* callbacks. Again, these callbacks will only be made if the workdir
|
* Again, these callbacks will only be made if the workdir files are
|
||||||
* files are included in the status "show" option. Right now, there is
|
* included in the status "show" option.
|
||||||
* no option to include all files in directories that are ignored
|
|
||||||
* completely.
|
|
||||||
* - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback should be
|
* - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback should be
|
||||||
* made even on unmodified files.
|
* made even on unmodified files.
|
||||||
* - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that directories which
|
* - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that submodules should be
|
||||||
* appear to be submodules should just be skipped over.
|
* skipped. This only applies if there are no pending typechanges to
|
||||||
* - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that the contents of
|
* the submodule (either from or to another type).
|
||||||
* untracked directories should be included in the status. Normally if
|
* - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that all files in
|
||||||
* an entire directory is new, then just the top-level directory will be
|
* untracked directories should be included. Normally if an entire
|
||||||
* included (with a trailing slash on the entry name). Given this flag,
|
* directory is new, then just the top-level directory is included (with
|
||||||
* the directory itself will not be included, but all the files in it
|
* a trailing slash on the entry name). This flag says to include all
|
||||||
* will.
|
* of the individual files in the directory instead.
|
||||||
* - GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH indicates that the given path
|
* - GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH indicates that the given path
|
||||||
* will be treated as a literal path, and not as a pathspec.
|
* should be treated as a literal path, and not as a pathspec pattern.
|
||||||
* - GIT_STATUS_OPT_RECURSE_IGNORED_DIRS indicates that the contents of
|
* - GIT_STATUS_OPT_RECURSE_IGNORED_DIRS indicates that the contents of
|
||||||
* ignored directories should be included in the status. This is like
|
* ignored directories should be included in the status. This is like
|
||||||
* doing `git ls-files -o -i --exclude-standard` with core git.
|
* doing `git ls-files -o -i --exclude-standard` with core git.
|
||||||
|
* - GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX indicates that rename detection
|
||||||
|
* should be processed between the head and the index and enables
|
||||||
|
* the GIT_STATUS_INDEX_RENAMED as a possible status flag.
|
||||||
|
* - GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR indicates tha rename
|
||||||
|
* detection should be run between the index and the working directory
|
||||||
|
* and enabled GIT_STATUS_WT_RENAMED as a possible status flag.
|
||||||
|
* - GIT_STATUS_OPT_SORT_CASE_SENSITIVELY overrides the native case
|
||||||
|
* sensitivity for the file system and forces the output to be in
|
||||||
|
* case-sensitive order
|
||||||
|
* - GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY overrides the native case
|
||||||
|
* sensitivity for the file system and forces the output to be in
|
||||||
|
* case-insensitive order
|
||||||
*
|
*
|
||||||
* Calling `git_status_foreach()` is like calling the extended version
|
* Calling `git_status_foreach()` is like calling the extended version
|
||||||
* with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
|
* with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
|
||||||
@ -137,13 +124,17 @@ typedef enum {
|
|||||||
* together as `GIT_STATUS_OPT_DEFAULTS` if you want them as a baseline.
|
* together as `GIT_STATUS_OPT_DEFAULTS` if you want them as a baseline.
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0),
|
GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0),
|
||||||
GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1),
|
GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1),
|
||||||
GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2),
|
GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2),
|
||||||
GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3),
|
GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3),
|
||||||
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
|
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
|
||||||
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
|
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
|
||||||
GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6),
|
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_t;
|
} git_status_opt_t;
|
||||||
|
|
||||||
#define GIT_STATUS_OPT_DEFAULTS \
|
#define GIT_STATUS_OPT_DEFAULTS \
|
||||||
@ -177,6 +168,47 @@ typedef struct {
|
|||||||
#define GIT_STATUS_OPTIONS_VERSION 1
|
#define GIT_STATUS_OPTIONS_VERSION 1
|
||||||
#define GIT_STATUS_OPTIONS_INIT {GIT_STATUS_OPTIONS_VERSION}
|
#define GIT_STATUS_OPTIONS_INIT {GIT_STATUS_OPTIONS_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.
|
||||||
|
*
|
||||||
|
* The `status` value provides the status flags for this file.
|
||||||
|
*
|
||||||
|
* The `head_to_index` value provides detailed information about the
|
||||||
|
* differences between the file in HEAD and the file in the index.
|
||||||
|
*
|
||||||
|
* The `index_to_workdir` value provides detailed information about the
|
||||||
|
* differences between the file in the index and the file in the
|
||||||
|
* working directory.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
git_status_t status;
|
||||||
|
git_diff_delta *head_to_index;
|
||||||
|
git_diff_delta *index_to_workdir;
|
||||||
|
} git_status_entry;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gather file statuses and run a callback for each one.
|
||||||
|
*
|
||||||
|
* The callback is passed the path of the file, the status (a combination of
|
||||||
|
* the `git_status_t` values above) and the `payload` data pointer passed
|
||||||
|
* into this function.
|
||||||
|
*
|
||||||
|
* If the callback returns a non-zero value, this function will stop looping
|
||||||
|
* and return GIT_EUSER.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_status_foreach(
|
||||||
|
git_repository *repo,
|
||||||
|
git_status_cb callback,
|
||||||
|
void *payload);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gather file status information and run callbacks as requested.
|
* Gather file status information and run callbacks as requested.
|
||||||
*
|
*
|
||||||
@ -215,6 +247,49 @@ GIT_EXTERN(int) git_status_file(
|
|||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const char *path);
|
const char *path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gather file status information and populate the `git_status_list`.
|
||||||
|
*
|
||||||
|
* @param out Pointer to store the status results in
|
||||||
|
* @param repo Repository object
|
||||||
|
* @param opts Status options structure
|
||||||
|
* @return 0 on success or error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_status_list_new(
|
||||||
|
git_status_list **out,
|
||||||
|
git_repository *repo,
|
||||||
|
const git_status_options *opts);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the count of status entries in this list.
|
||||||
|
*
|
||||||
|
* @param statuslist Existing status list object
|
||||||
|
* @return the number of status entries
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(size_t) git_status_list_entrycount(
|
||||||
|
git_status_list *statuslist);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a pointer to one of the entries in the status list.
|
||||||
|
*
|
||||||
|
* The entry is not modifiable and should not be freed.
|
||||||
|
*
|
||||||
|
* @param statuslist Existing status list object
|
||||||
|
* @param idx Position of the entry
|
||||||
|
* @return Pointer to the entry; NULL if out of bounds
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(const git_status_entry *) git_status_byindex(
|
||||||
|
git_status_list *statuslist,
|
||||||
|
size_t idx);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free an existing status list
|
||||||
|
*
|
||||||
|
* @param statuslist Existing status list object
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(void) git_status_list_free(
|
||||||
|
git_status_list *statuslist);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test if the ignore rules apply to a given file.
|
* Test if the ignore rules apply to a given file.
|
||||||
*
|
*
|
||||||
|
@ -20,8 +20,8 @@ GIT_BEGIN_DECL
|
|||||||
|
|
||||||
/** Array of strings */
|
/** Array of strings */
|
||||||
typedef struct git_strarray {
|
typedef struct git_strarray {
|
||||||
char **strings;
|
char **strings;
|
||||||
size_t count;
|
size_t count;
|
||||||
} git_strarray;
|
} git_strarray;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -103,20 +103,20 @@ typedef enum {
|
|||||||
* * WD_UNTRACKED - wd contains untracked files
|
* * WD_UNTRACKED - wd contains untracked files
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GIT_SUBMODULE_STATUS_IN_HEAD = (1u << 0),
|
GIT_SUBMODULE_STATUS_IN_HEAD = (1u << 0),
|
||||||
GIT_SUBMODULE_STATUS_IN_INDEX = (1u << 1),
|
GIT_SUBMODULE_STATUS_IN_INDEX = (1u << 1),
|
||||||
GIT_SUBMODULE_STATUS_IN_CONFIG = (1u << 2),
|
GIT_SUBMODULE_STATUS_IN_CONFIG = (1u << 2),
|
||||||
GIT_SUBMODULE_STATUS_IN_WD = (1u << 3),
|
GIT_SUBMODULE_STATUS_IN_WD = (1u << 3),
|
||||||
GIT_SUBMODULE_STATUS_INDEX_ADDED = (1u << 4),
|
GIT_SUBMODULE_STATUS_INDEX_ADDED = (1u << 4),
|
||||||
GIT_SUBMODULE_STATUS_INDEX_DELETED = (1u << 5),
|
GIT_SUBMODULE_STATUS_INDEX_DELETED = (1u << 5),
|
||||||
GIT_SUBMODULE_STATUS_INDEX_MODIFIED = (1u << 6),
|
GIT_SUBMODULE_STATUS_INDEX_MODIFIED = (1u << 6),
|
||||||
GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = (1u << 7),
|
GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = (1u << 7),
|
||||||
GIT_SUBMODULE_STATUS_WD_ADDED = (1u << 8),
|
GIT_SUBMODULE_STATUS_WD_ADDED = (1u << 8),
|
||||||
GIT_SUBMODULE_STATUS_WD_DELETED = (1u << 9),
|
GIT_SUBMODULE_STATUS_WD_DELETED = (1u << 9),
|
||||||
GIT_SUBMODULE_STATUS_WD_MODIFIED = (1u << 10),
|
GIT_SUBMODULE_STATUS_WD_MODIFIED = (1u << 10),
|
||||||
GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = (1u << 11),
|
GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = (1u << 11),
|
||||||
GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = (1u << 12),
|
GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = (1u << 12),
|
||||||
GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13),
|
GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13),
|
||||||
} git_submodule_status_t;
|
} git_submodule_status_t;
|
||||||
|
|
||||||
#define GIT_SUBMODULE_STATUS__IN_FLAGS \
|
#define GIT_SUBMODULE_STATUS__IN_FLAGS \
|
||||||
@ -481,7 +481,7 @@ GIT_EXTERN(int) git_submodule_sync(git_submodule *submodule);
|
|||||||
* function will return distinct `git_repository` objects. This will only
|
* function will return distinct `git_repository` objects. This will only
|
||||||
* work if the submodule is checked out into the working directory.
|
* work if the submodule is checked out into the working directory.
|
||||||
*
|
*
|
||||||
* @param subrepo Pointer to the submodule repo which was opened
|
* @param repo Pointer to the submodule repo which was opened
|
||||||
* @param submodule Submodule to be opened
|
* @param submodule Submodule to be opened
|
||||||
* @return 0 on success, <0 if submodule repo could not be opened.
|
* @return 0 on success, <0 if submodule repo could not be opened.
|
||||||
*/
|
*/
|
||||||
@ -531,7 +531,7 @@ GIT_EXTERN(int) git_submodule_status(
|
|||||||
* This can be useful if you want to know if the submodule is present in the
|
* This can be useful if you want to know if the submodule is present in the
|
||||||
* working directory at this point in time, etc.
|
* working directory at this point in time, etc.
|
||||||
*
|
*
|
||||||
* @param status Combination of first four `GIT_SUBMODULE_STATUS` flags
|
* @param location_status Combination of first four `GIT_SUBMODULE_STATUS` flags
|
||||||
* @param submodule Submodule for which to get status
|
* @param submodule Submodule for which to get status
|
||||||
* @return 0 on success, <0 on error
|
* @return 0 on success, <0 on error
|
||||||
*/
|
*/
|
||||||
|
46
include/git2/sys/commit.h
Normal file
46
include/git2/sys/commit.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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_commit_h__
|
||||||
|
#define INCLUDE_sys_git_commit_h__
|
||||||
|
|
||||||
|
#include "git2/common.h"
|
||||||
|
#include "git2/types.h"
|
||||||
|
#include "git2/oid.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file git2/sys/commit.h
|
||||||
|
* @brief Low-level Git commit creation
|
||||||
|
* @defgroup git_backend Git custom backend APIs
|
||||||
|
* @ingroup Git
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
GIT_BEGIN_DECL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_commit_create_from_oids(
|
||||||
|
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_oid *tree,
|
||||||
|
int parent_count,
|
||||||
|
const git_oid *parents[]);
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
GIT_END_DECL
|
||||||
|
#endif
|
71
include/git2/sys/config.h
Normal file
71
include/git2/sys/config.h
Normal file
@ -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.
|
||||||
|
*/
|
||||||
|
#ifndef INCLUDE_sys_git_config_backend_h__
|
||||||
|
#define INCLUDE_sys_git_config_backend_h__
|
||||||
|
|
||||||
|
#include "git2/common.h"
|
||||||
|
#include "git2/types.h"
|
||||||
|
#include "git2/config.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file git2/sys/config.h
|
||||||
|
* @brief Git config backend routines
|
||||||
|
* @defgroup git_backend Git custom backend APIs
|
||||||
|
* @ingroup Git
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
GIT_BEGIN_DECL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic backend that implements the interface to
|
||||||
|
* access a configuration file
|
||||||
|
*/
|
||||||
|
struct git_config_backend {
|
||||||
|
unsigned int version;
|
||||||
|
struct git_config *cfg;
|
||||||
|
|
||||||
|
/* Open means open the file/database and parse if necessary */
|
||||||
|
int (*open)(struct git_config_backend *, git_config_level_t level);
|
||||||
|
int (*get)(const struct git_config_backend *, const char *key, const git_config_entry **entry);
|
||||||
|
int (*get_multivar)(struct git_config_backend *, const char *key, const char *regexp, git_config_foreach_cb callback, void *payload);
|
||||||
|
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 (*foreach)(struct git_config_backend *, const char *, git_config_foreach_cb callback, void *payload);
|
||||||
|
int (*refresh)(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}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a generic config file instance to an existing config
|
||||||
|
*
|
||||||
|
* Note that the configuration object will free the file
|
||||||
|
* automatically.
|
||||||
|
*
|
||||||
|
* Further queries on this config object will access each
|
||||||
|
* of the config file instances in order (instances with
|
||||||
|
* a higher priority level will be accessed first).
|
||||||
|
*
|
||||||
|
* @param cfg the configuration to add the file to
|
||||||
|
* @param file the configuration file (backend) to add
|
||||||
|
* @param level the priority level of the backend
|
||||||
|
* @param force if a config file already exists for the given
|
||||||
|
* priority level, replace it
|
||||||
|
* @return 0 on success, GIT_EEXISTS when adding more than one file
|
||||||
|
* for a given priority level (and force_replace set to 0), or error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_config_add_backend(
|
||||||
|
git_config *cfg,
|
||||||
|
git_config_backend *file,
|
||||||
|
git_config_level_t level,
|
||||||
|
int force);
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
GIT_END_DECL
|
||||||
|
#endif
|
179
include/git2/sys/index.h
Normal file
179
include/git2/sys/index.h
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
* 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_index_h__
|
||||||
|
#define INCLUDE_sys_git_index_h__
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file git2/sys/index.h
|
||||||
|
* @brief Low-level Git index manipulation routines
|
||||||
|
* @defgroup git_backend Git custom backend APIs
|
||||||
|
* @ingroup Git
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
GIT_BEGIN_DECL
|
||||||
|
|
||||||
|
/** Representation of a rename conflict entry in the index. */
|
||||||
|
typedef struct git_index_name_entry {
|
||||||
|
char *ancestor;
|
||||||
|
char *ours;
|
||||||
|
char *theirs;
|
||||||
|
} git_index_name_entry;
|
||||||
|
|
||||||
|
/** Representation of a resolve undo entry in the index. */
|
||||||
|
typedef struct git_index_reuc_entry {
|
||||||
|
unsigned int mode[3];
|
||||||
|
git_oid oid[3];
|
||||||
|
char *path;
|
||||||
|
} git_index_reuc_entry;
|
||||||
|
|
||||||
|
/** @name Conflict Name entry functions
|
||||||
|
*
|
||||||
|
* These functions work on rename conflict entries.
|
||||||
|
*/
|
||||||
|
/**@{*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the count of filename conflict entries currently in the index.
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a filename conflict entry from the index.
|
||||||
|
*
|
||||||
|
* The returned entry is read-only and should not be modified
|
||||||
|
* or freed by the caller.
|
||||||
|
*
|
||||||
|
* @param index an existing index object
|
||||||
|
* @param n the position of the entry
|
||||||
|
* @return a pointer to the filename conflict entry; NULL if out of bounds
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(const git_index_name_entry *) git_index_name_get_byindex(
|
||||||
|
git_index *index, size_t n);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Record the filenames involved in a rename conflict.
|
||||||
|
*
|
||||||
|
* @param index an existing index object
|
||||||
|
* @param ancestor the path of the file as it existed in the ancestor
|
||||||
|
* @param ours the path of the file as it existed in our tree
|
||||||
|
* @param theirs the path of the file as it existed in their tree
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_index_name_add(git_index *index,
|
||||||
|
const char *ancestor, const char *ours, const char *theirs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all filename conflict entries.
|
||||||
|
*
|
||||||
|
* @param index an existing index object
|
||||||
|
* @return 0 or an error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(void) git_index_name_clear(git_index *index);
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** @name Resolve Undo (REUC) index entry manipulation.
|
||||||
|
*
|
||||||
|
* These functions work on the Resolve Undo index extension and contains
|
||||||
|
* data about the original files that led to a merge conflict.
|
||||||
|
*/
|
||||||
|
/**@{*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the count of resolve undo entries currently in the index.
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the resolve undo entry that points to the given path in the Git
|
||||||
|
* index.
|
||||||
|
*
|
||||||
|
* @param at_pos the address to which the position of the reuc entry is written (optional)
|
||||||
|
* @param index an existing index object
|
||||||
|
* @param path path to search
|
||||||
|
* @return 0 if found, < 0 otherwise (GIT_ENOTFOUND)
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_index_reuc_find(size_t *at_pos, git_index *index, const char *path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a resolve undo entry from the index.
|
||||||
|
*
|
||||||
|
* The returned entry is read-only and should not be modified
|
||||||
|
* or freed by the caller.
|
||||||
|
*
|
||||||
|
* @param index an existing index object
|
||||||
|
* @param path path to search
|
||||||
|
* @return the resolve undo entry; NULL if not found
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_bypath(git_index *index, const char *path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a resolve undo entry from the index.
|
||||||
|
*
|
||||||
|
* The returned entry is read-only and should not be modified
|
||||||
|
* or freed by the caller.
|
||||||
|
*
|
||||||
|
* @param index an existing index object
|
||||||
|
* @param n the position of the entry
|
||||||
|
* @return a pointer to the resolve undo entry; NULL if out of bounds
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_byindex(git_index *index, size_t n);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a resolve undo entry for a file based on the given parameters.
|
||||||
|
*
|
||||||
|
* The resolve undo entry contains the OIDs of files that were involved
|
||||||
|
* in a merge conflict after the conflict has been resolved. This allows
|
||||||
|
* conflicts to be re-resolved later.
|
||||||
|
*
|
||||||
|
* If there exists a resolve undo entry for the given path in the index,
|
||||||
|
* it will be removed.
|
||||||
|
*
|
||||||
|
* This method will fail in bare index instances.
|
||||||
|
*
|
||||||
|
* @param index an existing index object
|
||||||
|
* @param path filename to add
|
||||||
|
* @param ancestor_mode mode of the ancestor file
|
||||||
|
* @param ancestor_id oid of the ancestor file
|
||||||
|
* @param our_mode mode of our file
|
||||||
|
* @param our_id oid of our file
|
||||||
|
* @param their_mode mode of their file
|
||||||
|
* @param their_id oid of their file
|
||||||
|
* @return 0 or an error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_index_reuc_add(git_index *index, const char *path,
|
||||||
|
int ancestor_mode, const git_oid *ancestor_id,
|
||||||
|
int our_mode, const git_oid *our_id,
|
||||||
|
int their_mode, const git_oid *their_id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an resolve undo entry from the index
|
||||||
|
*
|
||||||
|
* @param index an existing index object
|
||||||
|
* @param n position of the resolve undo entry to remove
|
||||||
|
* @return 0 or an error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_index_reuc_remove(git_index *index, size_t n);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove all resolve undo entries from the index
|
||||||
|
*
|
||||||
|
* @param index an existing index object
|
||||||
|
* @return 0 or an error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(void) git_index_reuc_clear(git_index *index);
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
GIT_END_DECL
|
||||||
|
#endif
|
86
include/git2/sys/odb_backend.h
Normal file
86
include/git2/sys/odb_backend.h
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* 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_backend_h__
|
||||||
|
#define INCLUDE_sys_git_odb_backend_h__
|
||||||
|
|
||||||
|
#include "git2/common.h"
|
||||||
|
#include "git2/types.h"
|
||||||
|
#include "git2/oid.h"
|
||||||
|
#include "git2/odb.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file git2/sys/backend.h
|
||||||
|
* @brief Git custom backend implementors functions
|
||||||
|
* @defgroup git_backend Git custom backend APIs
|
||||||
|
* @ingroup Git
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
GIT_BEGIN_DECL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An instance for a custom backend
|
||||||
|
*/
|
||||||
|
struct git_odb_backend {
|
||||||
|
unsigned int version;
|
||||||
|
git_odb *odb;
|
||||||
|
|
||||||
|
/* read and read_prefix each return to libgit2 a buffer which
|
||||||
|
* will be freed later. The buffer should be allocated using
|
||||||
|
* the function git_odb_backend_malloc to ensure that it can
|
||||||
|
* be safely freed later. */
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
int (* read_prefix)(
|
||||||
|
git_oid *, void **, size_t *, git_otype *,
|
||||||
|
git_odb_backend *, const git_oid *, size_t);
|
||||||
|
|
||||||
|
int (* read_header)(
|
||||||
|
size_t *, git_otype *, git_odb_backend *, const git_oid *);
|
||||||
|
|
||||||
|
/* The writer may assume that the object
|
||||||
|
* has already been hashed and is passed
|
||||||
|
* in the first parameter.
|
||||||
|
*/
|
||||||
|
int (* write)(
|
||||||
|
git_oid *, git_odb_backend *, const void *, size_t, git_otype);
|
||||||
|
|
||||||
|
int (* writestream)(
|
||||||
|
git_odb_stream **, git_odb_backend *, size_t, git_otype);
|
||||||
|
|
||||||
|
int (* readstream)(
|
||||||
|
git_odb_stream **, git_odb_backend *, const git_oid *);
|
||||||
|
|
||||||
|
int (* exists)(
|
||||||
|
git_odb_backend *, const git_oid *);
|
||||||
|
|
||||||
|
int (* refresh)(git_odb_backend *);
|
||||||
|
|
||||||
|
int (* foreach)(
|
||||||
|
git_odb_backend *, git_odb_foreach_cb cb, void *payload);
|
||||||
|
|
||||||
|
int (* writepack)(
|
||||||
|
git_odb_writepack **, git_odb_backend *,
|
||||||
|
git_transfer_progress_callback progress_cb, void *progress_payload);
|
||||||
|
|
||||||
|
void (* free)(git_odb_backend *);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define GIT_ODB_BACKEND_VERSION 1
|
||||||
|
#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION}
|
||||||
|
|
||||||
|
GIT_EXTERN(void *) git_odb_backend_malloc(git_odb_backend *backend, size_t len);
|
||||||
|
|
||||||
|
GIT_END_DECL
|
||||||
|
|
||||||
|
#endif
|
158
include/git2/sys/refdb_backend.h
Normal file
158
include/git2/sys/refdb_backend.h
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
/*
|
||||||
|
* 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_refdb_backend_h__
|
||||||
|
#define INCLUDE_sys_git_refdb_backend_h__
|
||||||
|
|
||||||
|
#include "git2/common.h"
|
||||||
|
#include "git2/types.h"
|
||||||
|
#include "git2/oid.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file git2/refdb_backend.h
|
||||||
|
* @brief Git custom refs backend functions
|
||||||
|
* @defgroup git_refdb_backend Git custom refs backend API
|
||||||
|
* @ingroup Git
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
GIT_BEGIN_DECL
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Every backend's iterator must have a pointer to itself as the first
|
||||||
|
* element, so the API can talk to it. You'd define your iterator as
|
||||||
|
*
|
||||||
|
* struct my_iterator {
|
||||||
|
* git_reference_iterator parent;
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* and assign `iter->parent.backend` to your `git_refdb_backend`.
|
||||||
|
*/
|
||||||
|
struct git_reference_iterator {
|
||||||
|
git_refdb *db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current reference and advance the iterator.
|
||||||
|
*/
|
||||||
|
int (*next)(
|
||||||
|
git_reference **ref,
|
||||||
|
git_reference_iterator *iter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the current reference and advance the iterator
|
||||||
|
*/
|
||||||
|
int (*next_name)(
|
||||||
|
const char **ref_name,
|
||||||
|
git_reference_iterator *iter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free the iterator
|
||||||
|
*/
|
||||||
|
void (*free)(
|
||||||
|
git_reference_iterator *iter);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** An instance for a custom backend */
|
||||||
|
struct git_refdb_backend {
|
||||||
|
unsigned int version;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries the refdb backend to determine if the given ref_name
|
||||||
|
* exists. A refdb implementation must provide this function.
|
||||||
|
*/
|
||||||
|
int (*exists)(
|
||||||
|
int *exists,
|
||||||
|
git_refdb_backend *backend,
|
||||||
|
const char *ref_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Queries the refdb backend for a given reference. A refdb
|
||||||
|
* implementation must provide this function.
|
||||||
|
*/
|
||||||
|
int (*lookup)(
|
||||||
|
git_reference **out,
|
||||||
|
git_refdb_backend *backend,
|
||||||
|
const char *ref_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allocate an iterator object for the backend.
|
||||||
|
*
|
||||||
|
* A refdb implementation must provide this function.
|
||||||
|
*/
|
||||||
|
int (*iterator)(
|
||||||
|
git_reference_iterator **iter,
|
||||||
|
struct git_refdb_backend *backend,
|
||||||
|
const char *glob);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Writes the given reference to the refdb. A refdb implementation
|
||||||
|
* must provide this function.
|
||||||
|
*/
|
||||||
|
int (*write)(git_refdb_backend *backend,
|
||||||
|
const git_reference *ref, int force);
|
||||||
|
|
||||||
|
int (*rename)(
|
||||||
|
git_reference **out, git_refdb_backend *backend,
|
||||||
|
const char *old_name, const char *new_name, int force);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the given reference from the refdb. A refdb implementation
|
||||||
|
* must provide this function.
|
||||||
|
*/
|
||||||
|
int (*delete)(git_refdb_backend *backend, const char *ref_name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suggests that the given refdb compress or optimize its references.
|
||||||
|
* This mechanism is implementation specific. (For on-disk reference
|
||||||
|
* databases, this may pack all loose references.) A refdb
|
||||||
|
* implementation may provide this function; if it is not provided,
|
||||||
|
* nothing will be done.
|
||||||
|
*/
|
||||||
|
int (*compress)(git_refdb_backend *backend);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Frees any resources held by the refdb. A refdb implementation may
|
||||||
|
* provide this function; if it is not provided, nothing will be done.
|
||||||
|
*/
|
||||||
|
void (*free)(git_refdb_backend *backend);
|
||||||
|
};
|
||||||
|
|
||||||
|
#define GIT_ODB_BACKEND_VERSION 1
|
||||||
|
#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructors for default filesystem-based refdb backend
|
||||||
|
*
|
||||||
|
* Under normal usage, this is called for you when the repository is
|
||||||
|
* opened / created, but you can use this to explicitly construct a
|
||||||
|
* filesystem refdb backend for a repository.
|
||||||
|
*
|
||||||
|
* @param backend_out Output pointer to the git_refdb_backend object
|
||||||
|
* @param repo Git repository to access
|
||||||
|
* @return 0 on success, <0 error code on failure
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_refdb_backend_fs(
|
||||||
|
git_refdb_backend **backend_out,
|
||||||
|
git_repository *repo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the custom backend to an existing reference DB
|
||||||
|
*
|
||||||
|
* The `git_refdb` will take ownership of the `git_refdb_backend` so you
|
||||||
|
* should NOT free it after calling this function.
|
||||||
|
*
|
||||||
|
* @param refdb database to add the backend to
|
||||||
|
* @param backend pointer to a git_refdb_backend instance
|
||||||
|
* @return 0 on success; error code otherwise
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_refdb_set_backend(
|
||||||
|
git_refdb *refdb,
|
||||||
|
git_refdb_backend *backend);
|
||||||
|
|
||||||
|
GIT_END_DECL
|
||||||
|
|
||||||
|
#endif
|
38
include/git2/sys/refs.h
Normal file
38
include/git2/sys/refs.h
Normal file
@ -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_sys_git_refdb_h__
|
||||||
|
#define INCLUDE_sys_git_refdb_h__
|
||||||
|
|
||||||
|
#include "git2/common.h"
|
||||||
|
#include "git2/types.h"
|
||||||
|
#include "git2/oid.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new direct reference from an OID.
|
||||||
|
*
|
||||||
|
* @param name the reference name
|
||||||
|
* @param oid the object id for a direct reference
|
||||||
|
* @param symbolic the target for a symbolic reference
|
||||||
|
* @return the created git_reference or NULL on error
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(git_reference *) git_reference__alloc(
|
||||||
|
const char *name,
|
||||||
|
const git_oid *oid,
|
||||||
|
const git_oid *peel);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new symbolic reference.
|
||||||
|
*
|
||||||
|
* @param name the reference name
|
||||||
|
* @param symbolic the target for a symbolic reference
|
||||||
|
* @return the created git_reference or NULL on error
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(git_reference *) git_reference__alloc_symbolic(
|
||||||
|
const char *name,
|
||||||
|
const char *target);
|
||||||
|
|
||||||
|
#endif
|
106
include/git2/sys/repository.h
Normal file
106
include/git2/sys/repository.h
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* 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_repository_h__
|
||||||
|
#define INCLUDE_sys_git_repository_h__
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file git2/sys/repository.h
|
||||||
|
* @brief Git repository custom implementation routines
|
||||||
|
* @defgroup git_backend Git custom backend APIs
|
||||||
|
* @ingroup Git
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
GIT_BEGIN_DECL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new repository with neither backends nor config object
|
||||||
|
*
|
||||||
|
* Note that this is only useful if you wish to associate the repository
|
||||||
|
* with a non-filesystem-backed object database and config store.
|
||||||
|
*
|
||||||
|
* @param out The blank repository
|
||||||
|
* @return 0 on success, or an error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_repository_new(git_repository **out);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reset all the internal state in a repository.
|
||||||
|
*
|
||||||
|
* This will free all the mapped memory and internal objects
|
||||||
|
* of the repository and leave it in a "blank" state.
|
||||||
|
*
|
||||||
|
* There's no need to call this function directly unless you're
|
||||||
|
* trying to aggressively cleanup the repo before its
|
||||||
|
* deallocation. `git_repository_free` already performs this operation
|
||||||
|
* before deallocation the repo.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(void) git_repository__cleanup(git_repository *repo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the configuration file for this repository
|
||||||
|
*
|
||||||
|
* This configuration file will be used for all configuration
|
||||||
|
* queries involving this repository.
|
||||||
|
*
|
||||||
|
* The repository will keep a reference to the config file;
|
||||||
|
* the user must still free the config after setting it
|
||||||
|
* to the repository, or it will leak.
|
||||||
|
*
|
||||||
|
* @param repo A repository object
|
||||||
|
* @param config A Config object
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(void) git_repository_set_config(git_repository *repo, git_config *config);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Object Database for this repository
|
||||||
|
*
|
||||||
|
* The ODB will be used for all object-related operations
|
||||||
|
* involving this repository.
|
||||||
|
*
|
||||||
|
* The repository will keep a reference to the ODB; the user
|
||||||
|
* must still free the ODB object after setting it to the
|
||||||
|
* repository, or it will leak.
|
||||||
|
*
|
||||||
|
* @param repo A repository object
|
||||||
|
* @param odb An ODB object
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(void) git_repository_set_odb(git_repository *repo, git_odb *odb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Reference Database Backend for this repository
|
||||||
|
*
|
||||||
|
* The refdb will be used for all reference related operations
|
||||||
|
* involving this repository.
|
||||||
|
*
|
||||||
|
* The repository will keep a reference to the refdb; the user
|
||||||
|
* must still free the refdb object after setting it to the
|
||||||
|
* repository, or it will leak.
|
||||||
|
*
|
||||||
|
* @param repo A repository object
|
||||||
|
* @param refdb An refdb object
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(void) git_repository_set_refdb(git_repository *repo, git_refdb *refdb);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the index file for this repository
|
||||||
|
*
|
||||||
|
* This index will be used for all index-related operations
|
||||||
|
* involving this repository.
|
||||||
|
*
|
||||||
|
* The repository will keep a reference to the index file;
|
||||||
|
* the user must still free the index after setting it
|
||||||
|
* to the repository, or it will leak.
|
||||||
|
*
|
||||||
|
* @param repo A repository object
|
||||||
|
* @param index An index object
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(void) git_repository_set_index(git_repository *repo, git_index *index);
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
GIT_END_DECL
|
||||||
|
#endif
|
@ -30,12 +30,8 @@ GIT_BEGIN_DECL
|
|||||||
* @param id identity of the tag to locate.
|
* @param id identity of the tag to locate.
|
||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
GIT_INLINE(int) git_tag_lookup(
|
GIT_EXTERN(int) git_tag_lookup(
|
||||||
git_tag **out, git_repository *repo, const git_oid *id)
|
git_tag **out, git_repository *repo, const git_oid *id);
|
||||||
{
|
|
||||||
return git_object_lookup(
|
|
||||||
(git_object **)out, repo, id, (git_otype)GIT_OBJ_TAG);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lookup a tag object from the repository,
|
* Lookup a tag object from the repository,
|
||||||
@ -49,12 +45,8 @@ GIT_INLINE(int) git_tag_lookup(
|
|||||||
* @param len the length of the short identifier
|
* @param len the length of the short identifier
|
||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
GIT_INLINE(int) git_tag_lookup_prefix(
|
GIT_EXTERN(int) git_tag_lookup_prefix(
|
||||||
git_tag **out, git_repository *repo, const git_oid *id, size_t len)
|
git_tag **out, git_repository *repo, const git_oid *id, size_t len);
|
||||||
{
|
|
||||||
return git_object_lookup_prefix(
|
|
||||||
(git_object **)out, repo, id, len, (git_otype)GIT_OBJ_TAG);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close an open tag
|
* Close an open tag
|
||||||
@ -66,12 +58,7 @@ GIT_INLINE(int) git_tag_lookup_prefix(
|
|||||||
*
|
*
|
||||||
* @param tag the tag to close
|
* @param tag the tag to close
|
||||||
*/
|
*/
|
||||||
|
GIT_EXTERN(void) git_tag_free(git_tag *tag);
|
||||||
GIT_INLINE(void) git_tag_free(git_tag *tag)
|
|
||||||
{
|
|
||||||
git_object_free((git_object *)tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the id of a tag.
|
* Get the id of a tag.
|
||||||
@ -81,6 +68,14 @@ GIT_INLINE(void) git_tag_free(git_tag *tag)
|
|||||||
*/
|
*/
|
||||||
GIT_EXTERN(const git_oid *) git_tag_id(const git_tag *tag);
|
GIT_EXTERN(const git_oid *) git_tag_id(const git_tag *tag);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the repository that contains the tag.
|
||||||
|
*
|
||||||
|
* @param tag A previously loaded tag.
|
||||||
|
* @return Repository that contains this tag.
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(git_repository *) git_tag_owner(const git_tag *tag);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the tagged object of a tag
|
* Get the tagged object of a tag
|
||||||
*
|
*
|
||||||
@ -182,6 +177,37 @@ GIT_EXTERN(int) git_tag_create(
|
|||||||
const char *message,
|
const char *message,
|
||||||
int force);
|
int force);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new tag in the object database pointing to a git_object
|
||||||
|
*
|
||||||
|
* The message will not be cleaned up. This can be achieved
|
||||||
|
* through `git_message_prettify()`.
|
||||||
|
*
|
||||||
|
* @param oid Pointer where to store the OID of the
|
||||||
|
* newly created tag
|
||||||
|
*
|
||||||
|
* @param repo Repository where to store the tag
|
||||||
|
*
|
||||||
|
* @param tag_name Name for the tag
|
||||||
|
*
|
||||||
|
* @param target Object to which this tag points. This object
|
||||||
|
* must belong to the given `repo`.
|
||||||
|
*
|
||||||
|
* @param tagger Signature of the tagger for this tag, and
|
||||||
|
* of the tagging time
|
||||||
|
*
|
||||||
|
* @param message Full message for this tag
|
||||||
|
*
|
||||||
|
* @return 0 on success or an error code
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_tag_annotation_create(
|
||||||
|
git_oid *oid,
|
||||||
|
git_repository *repo,
|
||||||
|
const char *tag_name,
|
||||||
|
const git_object *target,
|
||||||
|
const git_signature *tagger,
|
||||||
|
const char *message);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new tag in the repository from a buffer
|
* Create a new tag in the repository from a buffer
|
||||||
*
|
*
|
||||||
|
@ -32,7 +32,7 @@ typedef enum {
|
|||||||
|
|
||||||
/** Errors that do not impact the program's execution */
|
/** Errors that do not impact the program's execution */
|
||||||
GIT_TRACE_ERROR = 2,
|
GIT_TRACE_ERROR = 2,
|
||||||
|
|
||||||
/** Warnings that suggest abnormal data */
|
/** Warnings that suggest abnormal data */
|
||||||
GIT_TRACE_WARN = 3,
|
GIT_TRACE_WARN = 3,
|
||||||
|
|
||||||
@ -65,4 +65,3 @@ GIT_EXTERN(int) git_trace_set(git_trace_level_t level, git_trace_callback cb);
|
|||||||
/** @} */
|
/** @} */
|
||||||
GIT_END_DECL
|
GIT_END_DECL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -11,6 +11,10 @@
|
|||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
|
#ifdef GIT_SSH
|
||||||
|
#include <libssh2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @file git2/transport.h
|
* @file git2/transport.h
|
||||||
* @brief Git transport interfaces and functions
|
* @brief Git transport interfaces and functions
|
||||||
@ -27,6 +31,8 @@ GIT_BEGIN_DECL
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
/* git_cred_userpass_plaintext */
|
/* git_cred_userpass_plaintext */
|
||||||
GIT_CREDTYPE_USERPASS_PLAINTEXT = 1,
|
GIT_CREDTYPE_USERPASS_PLAINTEXT = 1,
|
||||||
|
GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE = 2,
|
||||||
|
GIT_CREDTYPE_SSH_PUBLICKEY = 3,
|
||||||
} git_credtype_t;
|
} git_credtype_t;
|
||||||
|
|
||||||
/* The base structure for all credential types */
|
/* The base structure for all credential types */
|
||||||
@ -43,6 +49,27 @@ typedef struct git_cred_userpass_plaintext {
|
|||||||
char *password;
|
char *password;
|
||||||
} git_cred_userpass_plaintext;
|
} git_cred_userpass_plaintext;
|
||||||
|
|
||||||
|
#ifdef GIT_SSH
|
||||||
|
typedef LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*git_cred_sign_callback));
|
||||||
|
|
||||||
|
/* A ssh key file and passphrase */
|
||||||
|
typedef struct git_cred_ssh_keyfile_passphrase {
|
||||||
|
git_cred parent;
|
||||||
|
char *publickey;
|
||||||
|
char *privatekey;
|
||||||
|
char *passphrase;
|
||||||
|
} git_cred_ssh_keyfile_passphrase;
|
||||||
|
|
||||||
|
/* A ssh public key and authentication callback */
|
||||||
|
typedef struct git_cred_ssh_publickey {
|
||||||
|
git_cred parent;
|
||||||
|
char *publickey;
|
||||||
|
size_t publickey_len;
|
||||||
|
void *sign_callback;
|
||||||
|
void *sign_data;
|
||||||
|
} git_cred_ssh_publickey;
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new plain-text username and password credential object.
|
* Creates a new plain-text username and password credential object.
|
||||||
* The supplied credential parameter will be internally duplicated.
|
* The supplied credential parameter will be internally duplicated.
|
||||||
@ -57,6 +84,42 @@ GIT_EXTERN(int) git_cred_userpass_plaintext_new(
|
|||||||
const char *username,
|
const char *username,
|
||||||
const char *password);
|
const char *password);
|
||||||
|
|
||||||
|
#ifdef GIT_SSH
|
||||||
|
/**
|
||||||
|
* Creates a new ssh key file and passphrase credential object.
|
||||||
|
* The supplied credential parameter will be internally duplicated.
|
||||||
|
*
|
||||||
|
* @param out The newly created credential object.
|
||||||
|
* @param publickey The path to the public key of the credential.
|
||||||
|
* @param privatekey The path to the private key of the credential.
|
||||||
|
* @param passphrase The passphrase of the credential.
|
||||||
|
* @return 0 for success or an error code for failure
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_cred_ssh_keyfile_passphrase_new(
|
||||||
|
git_cred **out,
|
||||||
|
const char *publickey,
|
||||||
|
const char *privatekey,
|
||||||
|
const char *passphrase);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ssh public key credential object.
|
||||||
|
* The supplied credential parameter will be internally duplicated.
|
||||||
|
*
|
||||||
|
* @param out The newly created credential object.
|
||||||
|
* @param publickey The bytes of the public key.
|
||||||
|
* @param publickey_len The length of the public key in bytes.
|
||||||
|
* @param sign_callback The callback method for authenticating.
|
||||||
|
* @param sign_data The abstract data sent to the sign_callback method.
|
||||||
|
* @return 0 for success or an error code for failure
|
||||||
|
*/
|
||||||
|
GIT_EXTERN(int) git_cred_ssh_publickey_new(
|
||||||
|
git_cred **out,
|
||||||
|
const char *publickey,
|
||||||
|
size_t publickey_len,
|
||||||
|
git_cred_sign_callback,
|
||||||
|
void *sign_data);
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signature of a function which acquires a credential object.
|
* Signature of a function which acquires a credential object.
|
||||||
*
|
*
|
||||||
@ -319,6 +382,17 @@ GIT_EXTERN(int) git_smart_subtransport_git(
|
|||||||
git_smart_subtransport **out,
|
git_smart_subtransport **out,
|
||||||
git_transport* owner);
|
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 ***
|
*** End interface for subtransports for the smart transport ***
|
||||||
*/
|
*/
|
||||||
|
@ -29,11 +29,8 @@ GIT_BEGIN_DECL
|
|||||||
* @param id Identity of the tree to locate.
|
* @param id Identity of the tree to locate.
|
||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
GIT_INLINE(int) git_tree_lookup(
|
GIT_EXTERN(int) git_tree_lookup(
|
||||||
git_tree **out, git_repository *repo, const git_oid *id)
|
git_tree **out, git_repository *repo, const git_oid *id);
|
||||||
{
|
|
||||||
return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_TREE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lookup a tree object from the repository,
|
* Lookup a tree object from the repository,
|
||||||
@ -41,21 +38,17 @@ GIT_INLINE(int) git_tree_lookup(
|
|||||||
*
|
*
|
||||||
* @see git_object_lookup_prefix
|
* @see git_object_lookup_prefix
|
||||||
*
|
*
|
||||||
* @param tree pointer to the looked up tree
|
* @param out pointer to the looked up tree
|
||||||
* @param repo the repo to use when locating the tree.
|
* @param repo the repo to use when locating the tree.
|
||||||
* @param id identity of the tree to locate.
|
* @param id identity of the tree to locate.
|
||||||
* @param len the length of the short identifier
|
* @param len the length of the short identifier
|
||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
*/
|
*/
|
||||||
GIT_INLINE(int) git_tree_lookup_prefix(
|
GIT_EXTERN(int) git_tree_lookup_prefix(
|
||||||
git_tree **out,
|
git_tree **out,
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const git_oid *id,
|
const git_oid *id,
|
||||||
size_t len)
|
size_t len);
|
||||||
{
|
|
||||||
return git_object_lookup_prefix(
|
|
||||||
(git_object **)out, repo, id, len, GIT_OBJ_TREE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close an open tree
|
* Close an open tree
|
||||||
@ -67,10 +60,7 @@ GIT_INLINE(int) git_tree_lookup_prefix(
|
|||||||
*
|
*
|
||||||
* @param tree The tree to close
|
* @param tree The tree to close
|
||||||
*/
|
*/
|
||||||
GIT_INLINE(void) git_tree_free(git_tree *tree)
|
GIT_EXTERN(void) git_tree_free(git_tree *tree);
|
||||||
{
|
|
||||||
git_object_free((git_object *)tree);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the id of a tree.
|
* Get the id of a tree.
|
||||||
@ -107,7 +97,7 @@ GIT_EXTERN(size_t) git_tree_entrycount(const git_tree *tree);
|
|||||||
* @return the tree entry; NULL if not found
|
* @return the tree entry; NULL if not found
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname(
|
GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname(
|
||||||
git_tree *tree, const char *filename);
|
const git_tree *tree, const char *filename);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lookup a tree entry by its position in the tree
|
* Lookup a tree entry by its position in the tree
|
||||||
@ -120,7 +110,7 @@ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname(
|
|||||||
* @return the tree entry; NULL if not found
|
* @return the tree entry; NULL if not found
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex(
|
GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex(
|
||||||
git_tree *tree, size_t idx);
|
const git_tree *tree, size_t idx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lookup a tree entry by SHA value.
|
* Lookup a tree entry by SHA value.
|
||||||
@ -146,12 +136,12 @@ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byoid(
|
|||||||
*
|
*
|
||||||
* @param out Pointer where to store the tree entry
|
* @param out Pointer where to store the tree entry
|
||||||
* @param root Previously loaded tree which is the root of the relative path
|
* @param root Previously loaded tree which is the root of the relative path
|
||||||
* @param subtree_path Path to the contained entry
|
* @param path Path to the contained entry
|
||||||
* @return 0 on success; GIT_ENOTFOUND if the path does not exist
|
* @return 0 on success; GIT_ENOTFOUND if the path does not exist
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(int) git_tree_entry_bypath(
|
GIT_EXTERN(int) git_tree_entry_bypath(
|
||||||
git_tree_entry **out,
|
git_tree_entry **out,
|
||||||
git_tree *root,
|
const git_tree *root,
|
||||||
const char *path);
|
const char *path);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -222,7 +212,7 @@ GIT_EXTERN(int) git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entr
|
|||||||
*
|
*
|
||||||
* You must call `git_object_free()` on the object when you are done with it.
|
* You must call `git_object_free()` on the object when you are done with it.
|
||||||
*
|
*
|
||||||
* @param object pointer to the converted object
|
* @param object_out pointer to the converted object
|
||||||
* @param repo repository where to lookup the pointed object
|
* @param repo repository where to lookup the pointed object
|
||||||
* @param entry a tree entry
|
* @param entry a tree entry
|
||||||
* @return 0 or an error code
|
* @return 0 or an error code
|
||||||
@ -261,7 +251,7 @@ GIT_EXTERN(void) git_treebuilder_clear(git_treebuilder *bld);
|
|||||||
/**
|
/**
|
||||||
* Get the number of entries listed in a treebuilder
|
* Get the number of entries listed in a treebuilder
|
||||||
*
|
*
|
||||||
* @param tree a previously loaded treebuilder.
|
* @param bld a previously loaded treebuilder.
|
||||||
* @return the number of entries in the treebuilder
|
* @return the number of entries in the treebuilder
|
||||||
*/
|
*/
|
||||||
GIT_EXTERN(unsigned int) git_treebuilder_entrycount(git_treebuilder *bld);
|
GIT_EXTERN(unsigned int) git_treebuilder_entrycount(git_treebuilder *bld);
|
||||||
|
@ -131,6 +131,9 @@ typedef struct git_treebuilder git_treebuilder;
|
|||||||
/** Memory representation of an index file. */
|
/** Memory representation of an index file. */
|
||||||
typedef struct git_index git_index;
|
typedef struct git_index git_index;
|
||||||
|
|
||||||
|
/** An interator for conflicts in the index. */
|
||||||
|
typedef struct git_index_conflict_iterator git_index_conflict_iterator;
|
||||||
|
|
||||||
/** Memory representation of a set of config files */
|
/** Memory representation of a set of config files */
|
||||||
typedef struct git_config git_config;
|
typedef struct git_config git_config;
|
||||||
|
|
||||||
@ -165,6 +168,16 @@ typedef struct git_signature {
|
|||||||
/** In-memory representation of a reference. */
|
/** In-memory representation of a reference. */
|
||||||
typedef struct git_reference git_reference;
|
typedef struct git_reference git_reference;
|
||||||
|
|
||||||
|
/** 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;
|
||||||
|
|
||||||
|
/** Representation of a status collection */
|
||||||
|
typedef struct git_status_list git_status_list;
|
||||||
|
|
||||||
|
|
||||||
/** Basic type of any Git reference. */
|
/** Basic type of any Git reference. */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GIT_REF_INVALID = 0, /** Invalid reference */
|
GIT_REF_INVALID = 0, /** Invalid reference */
|
||||||
@ -196,6 +209,26 @@ typedef struct git_push git_push;
|
|||||||
typedef struct git_remote_head git_remote_head;
|
typedef struct git_remote_head git_remote_head;
|
||||||
typedef struct git_remote_callbacks git_remote_callbacks;
|
typedef struct git_remote_callbacks git_remote_callbacks;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is passed as the first argument to the callback to allow the
|
||||||
|
* user to see the progress.
|
||||||
|
*/
|
||||||
|
typedef struct git_transfer_progress {
|
||||||
|
unsigned int total_objects;
|
||||||
|
unsigned int indexed_objects;
|
||||||
|
unsigned int received_objects;
|
||||||
|
size_t received_bytes;
|
||||||
|
} git_transfer_progress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Type for progress callbacks during indexing. Return a value less than zero
|
||||||
|
* to cancel the transfer.
|
||||||
|
*
|
||||||
|
* @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);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
GIT_END_DECL
|
GIT_END_DECL
|
||||||
|
|
||||||
|
@ -7,9 +7,9 @@
|
|||||||
#ifndef INCLUDE_git_version_h__
|
#ifndef INCLUDE_git_version_h__
|
||||||
#define INCLUDE_git_version_h__
|
#define INCLUDE_git_version_h__
|
||||||
|
|
||||||
#define LIBGIT2_VERSION "0.18.0"
|
#define LIBGIT2_VERSION "0.19.0"
|
||||||
#define LIBGIT2_VER_MAJOR 0
|
#define LIBGIT2_VER_MAJOR 0
|
||||||
#define LIBGIT2_VER_MINOR 18
|
#define LIBGIT2_VER_MINOR 19
|
||||||
#define LIBGIT2_VER_REVISION 0
|
#define LIBGIT2_VER_REVISION 0
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
66
src/array.h
Normal file
66
src/array.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* 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_array_h__
|
||||||
|
#define INCLUDE_array_h__
|
||||||
|
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use this to declare a typesafe resizable array of items, a la:
|
||||||
|
*
|
||||||
|
* git_array_t(int) my_ints = GIT_ARRAY_INIT;
|
||||||
|
* ...
|
||||||
|
* int *i = git_array_alloc(my_ints);
|
||||||
|
* GITERR_CHECK_ALLOC(i);
|
||||||
|
* ...
|
||||||
|
* git_array_clear(my_ints);
|
||||||
|
*
|
||||||
|
* You may also want to do things like:
|
||||||
|
*
|
||||||
|
* typedef git_array_t(my_struct) my_struct_array_t;
|
||||||
|
*/
|
||||||
|
#define git_array_t(type) struct { type *ptr; uint32_t size, asize; }
|
||||||
|
|
||||||
|
#define GIT_ARRAY_INIT { NULL, 0, 0 }
|
||||||
|
|
||||||
|
#define git_array_init(a) \
|
||||||
|
do { (a).size = (a).asize = 0; (a).ptr = NULL; } while (0)
|
||||||
|
|
||||||
|
#define git_array_clear(a) \
|
||||||
|
do { git__free((a).ptr); git_array_init(a); } while (0)
|
||||||
|
|
||||||
|
#define GITERR_CHECK_ARRAY(a) GITERR_CHECK_ALLOC((a).ptr)
|
||||||
|
|
||||||
|
|
||||||
|
typedef git_array_t(void) git_array_generic_t;
|
||||||
|
|
||||||
|
/* use a generic array for growth so this can return the new item */
|
||||||
|
GIT_INLINE(void *) git_array_grow(git_array_generic_t *a, size_t item_size)
|
||||||
|
{
|
||||||
|
uint32_t new_size = (a->size < 8) ? 8 : a->asize * 3 / 2;
|
||||||
|
void *new_array = git__realloc(a->ptr, new_size * item_size);
|
||||||
|
if (!new_array) {
|
||||||
|
git_array_clear(*a);
|
||||||
|
return NULL;
|
||||||
|
} else {
|
||||||
|
a->ptr = new_array; a->asize = new_size; a->size++;
|
||||||
|
return (((char *)a->ptr) + (a->size - 1) * item_size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define git_array_alloc(a) \
|
||||||
|
((a).size >= (a).asize) ? \
|
||||||
|
git_array_grow((git_array_generic_t *)&(a), sizeof(*(a).ptr)) : \
|
||||||
|
(a).ptr ? &(a).ptr[(a).size++] : NULL
|
||||||
|
|
||||||
|
#define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : NULL)
|
||||||
|
|
||||||
|
#define git_array_get(a, i) (((i) < (a).size) ? &(a).ptr[(i)] : NULL)
|
||||||
|
|
||||||
|
#define git_array_size(a) (a).size
|
||||||
|
|
||||||
|
#endif
|
39
src/attr.c
39
src/attr.c
@ -36,7 +36,7 @@ static int collect_attr_files(
|
|||||||
|
|
||||||
int git_attr_get(
|
int git_attr_get(
|
||||||
const char **value,
|
const char **value,
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
uint32_t flags,
|
uint32_t flags,
|
||||||
const char *pathname,
|
const char *pathname,
|
||||||
const char *name)
|
const char *name)
|
||||||
@ -88,10 +88,10 @@ typedef struct {
|
|||||||
|
|
||||||
int git_attr_get_many(
|
int git_attr_get_many(
|
||||||
const char **values,
|
const char **values,
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
uint32_t flags,
|
uint32_t flags,
|
||||||
const char *pathname,
|
const char *pathname,
|
||||||
size_t num_attr,
|
size_t num_attr,
|
||||||
const char **names)
|
const char **names)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
@ -151,7 +151,7 @@ cleanup:
|
|||||||
|
|
||||||
|
|
||||||
int git_attr_foreach(
|
int git_attr_foreach(
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
uint32_t flags,
|
uint32_t flags,
|
||||||
const char *pathname,
|
const char *pathname,
|
||||||
int (*callback)(const char *name, const char *value, void *payload),
|
int (*callback)(const char *name, const char *value, void *payload),
|
||||||
@ -312,7 +312,7 @@ static int load_attr_blob_from_index(
|
|||||||
|
|
||||||
entry = git_index_get_byindex(index, pos);
|
entry = git_index_get_byindex(index, pos);
|
||||||
|
|
||||||
if (old_oid && git_oid_cmp(old_oid, &entry->oid) == 0)
|
if (old_oid && git_oid__cmp(old_oid, &entry->oid) == 0)
|
||||||
return GIT_ENOTFOUND;
|
return GIT_ENOTFOUND;
|
||||||
|
|
||||||
if ((error = git_blob_lookup(blob, repo, &entry->oid)) < 0)
|
if ((error = git_blob_lookup(blob, repo, &entry->oid)) < 0)
|
||||||
@ -596,26 +596,33 @@ static int collect_attr_files(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int attr_cache__lookup_path(
|
static int attr_cache__lookup_path(
|
||||||
const char **out, git_config *cfg, const char *key, const char *fallback)
|
char **out, git_config *cfg, const char *key, const char *fallback)
|
||||||
{
|
{
|
||||||
git_buf buf = GIT_BUF_INIT;
|
git_buf buf = GIT_BUF_INIT;
|
||||||
int error;
|
int error;
|
||||||
|
const char *cfgval = NULL;
|
||||||
|
|
||||||
if (!(error = git_config_get_string(out, cfg, key)))
|
*out = NULL;
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (error == GIT_ENOTFOUND) {
|
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();
|
giterr_clear();
|
||||||
error = 0;
|
error = 0;
|
||||||
|
|
||||||
if (!git_futils_find_xdg_file(&buf, fallback))
|
if (!git_futils_find_xdg_file(&buf, fallback))
|
||||||
*out = git_buf_detach(&buf);
|
*out = git_buf_detach(&buf);
|
||||||
else
|
|
||||||
*out = NULL;
|
|
||||||
|
|
||||||
git_buf_free(&buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
git_buf_free(&buf);
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -696,6 +703,12 @@ void git_attr_cache_flush(
|
|||||||
|
|
||||||
git_pool_clear(&cache->pool);
|
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;
|
cache->initialized = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,7 +397,8 @@ int git_attr_fnmatch__parse(
|
|||||||
|
|
||||||
*base = scan;
|
*base = scan;
|
||||||
|
|
||||||
spec->length = scan - pattern;
|
if ((spec->length = scan - pattern) == 0)
|
||||||
|
return GIT_ENOTFOUND;
|
||||||
|
|
||||||
if (pattern[spec->length - 1] == '/') {
|
if (pattern[spec->length - 1] == '/') {
|
||||||
spec->length--;
|
spec->length--;
|
||||||
@ -497,7 +498,7 @@ int git_attr_assignment__parse(
|
|||||||
|
|
||||||
assert(assigns && !assigns->length);
|
assert(assigns && !assigns->length);
|
||||||
|
|
||||||
assigns->_cmp = sort_by_hash_and_name;
|
git_vector_set_cmp(assigns, sort_by_hash_and_name);
|
||||||
|
|
||||||
while (*scan && *scan != '\n') {
|
while (*scan && *scan != '\n') {
|
||||||
const char *name_start, *value_start;
|
const char *name_start, *value_start;
|
||||||
|
@ -47,14 +47,14 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
git_refcount unused;
|
git_refcount unused;
|
||||||
const char *name;
|
const char *name;
|
||||||
uint32_t name_hash;
|
uint32_t name_hash;
|
||||||
} git_attr_name;
|
} git_attr_name;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
git_refcount rc; /* for macros */
|
git_refcount rc; /* for macros */
|
||||||
char *name;
|
char *name;
|
||||||
uint32_t name_hash;
|
uint32_t name_hash;
|
||||||
const char *value;
|
const char *value;
|
||||||
} git_attr_assignment;
|
} git_attr_assignment;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -13,10 +13,10 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
int initialized;
|
int initialized;
|
||||||
git_pool pool;
|
git_pool pool;
|
||||||
git_strmap *files; /* hash path to git_attr_file of rules */
|
git_strmap *files; /* hash path to git_attr_file of rules */
|
||||||
git_strmap *macros; /* hash name to vector<git_attr_assignment> */
|
git_strmap *macros; /* hash name to vector<git_attr_assignment> */
|
||||||
const char *cfg_attr_file; /* cached value of core.attributesfile */
|
char *cfg_attr_file; /* cached value of core.attributesfile */
|
||||||
const char *cfg_excl_file; /* cached value of core.excludesfile */
|
char *cfg_excl_file; /* cached value of core.excludesfile */
|
||||||
} git_attr_cache;
|
} git_attr_cache;
|
||||||
|
|
||||||
extern int git_attr_cache__init(git_repository *repo);
|
extern int git_attr_cache__init(git_repository *repo);
|
||||||
|
22
src/blob.c
22
src/blob.c
@ -8,8 +8,10 @@
|
|||||||
#include "git2/common.h"
|
#include "git2/common.h"
|
||||||
#include "git2/object.h"
|
#include "git2/object.h"
|
||||||
#include "git2/repository.h"
|
#include "git2/repository.h"
|
||||||
|
#include "git2/odb_backend.h"
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "filebuf.h"
|
||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
#include "filter.h"
|
#include "filter.h"
|
||||||
#include "buf_text.h"
|
#include "buf_text.h"
|
||||||
@ -17,32 +19,34 @@
|
|||||||
const void *git_blob_rawcontent(const git_blob *blob)
|
const void *git_blob_rawcontent(const git_blob *blob)
|
||||||
{
|
{
|
||||||
assert(blob);
|
assert(blob);
|
||||||
return blob->odb_object->raw.data;
|
return git_odb_object_data(blob->odb_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
git_off_t git_blob_rawsize(const git_blob *blob)
|
git_off_t git_blob_rawsize(const git_blob *blob)
|
||||||
{
|
{
|
||||||
assert(blob);
|
assert(blob);
|
||||||
return (git_off_t)blob->odb_object->raw.len;
|
return (git_off_t)git_odb_object_size(blob->odb_object);
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_blob__getbuf(git_buf *buffer, git_blob *blob)
|
int git_blob__getbuf(git_buf *buffer, git_blob *blob)
|
||||||
{
|
{
|
||||||
return git_buf_set(
|
return git_buf_set(
|
||||||
buffer, blob->odb_object->raw.data, blob->odb_object->raw.len);
|
buffer,
|
||||||
|
git_odb_object_data(blob->odb_object),
|
||||||
|
git_odb_object_size(blob->odb_object));
|
||||||
}
|
}
|
||||||
|
|
||||||
void git_blob__free(git_blob *blob)
|
void git_blob__free(void *blob)
|
||||||
{
|
{
|
||||||
git_odb_object_free(blob->odb_object);
|
git_odb_object_free(((git_blob *)blob)->odb_object);
|
||||||
git__free(blob);
|
git__free(blob);
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_blob__parse(git_blob *blob, git_odb_object *odb_obj)
|
int git_blob__parse(void *blob, git_odb_object *odb_obj)
|
||||||
{
|
{
|
||||||
assert(blob);
|
assert(blob);
|
||||||
git_cached_obj_incref((git_cached_obj *)odb_obj);
|
git_cached_obj_incref((git_cached_obj *)odb_obj);
|
||||||
blob->odb_object = odb_obj;
|
((git_blob *)blob)->odb_object = odb_obj;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,8 +318,8 @@ int git_blob_is_binary(git_blob *blob)
|
|||||||
|
|
||||||
assert(blob);
|
assert(blob);
|
||||||
|
|
||||||
content.ptr = blob->odb_object->raw.data;
|
content.ptr = blob->odb_object->buffer;
|
||||||
content.size = min(blob->odb_object->raw.len, 4000);
|
content.size = min(blob->odb_object->cached.size, 4000);
|
||||||
|
|
||||||
return git_buf_text_is_binary(&content);
|
return git_buf_text_is_binary(&content);
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,8 @@ struct git_blob {
|
|||||||
git_odb_object *odb_object;
|
git_odb_object *odb_object;
|
||||||
};
|
};
|
||||||
|
|
||||||
void git_blob__free(git_blob *blob);
|
void git_blob__free(void *blob);
|
||||||
int git_blob__parse(git_blob *blob, git_odb_object *obj);
|
int git_blob__parse(void *blob, git_odb_object *obj);
|
||||||
int git_blob__getbuf(git_buf *buffer, git_blob *blob);
|
int git_blob__getbuf(git_buf *buffer, git_blob *blob);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
116
src/branch.c
116
src/branch.c
@ -11,6 +11,7 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "refspec.h"
|
#include "refspec.h"
|
||||||
#include "refs.h"
|
#include "refs.h"
|
||||||
|
#include "remote.h"
|
||||||
|
|
||||||
#include "git2/branch.h"
|
#include "git2/branch.h"
|
||||||
|
|
||||||
@ -123,40 +124,48 @@ on_error:
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
git_branch_foreach_cb branch_cb;
|
|
||||||
void *callback_payload;
|
|
||||||
unsigned int branch_type;
|
|
||||||
} branch_foreach_filter;
|
|
||||||
|
|
||||||
static int branch_foreach_cb(const char *branch_name, void *payload)
|
|
||||||
{
|
|
||||||
branch_foreach_filter *filter = (branch_foreach_filter *)payload;
|
|
||||||
|
|
||||||
if (filter->branch_type & GIT_BRANCH_LOCAL &&
|
|
||||||
git__prefixcmp(branch_name, GIT_REFS_HEADS_DIR) == 0)
|
|
||||||
return filter->branch_cb(branch_name + strlen(GIT_REFS_HEADS_DIR), GIT_BRANCH_LOCAL, filter->callback_payload);
|
|
||||||
|
|
||||||
if (filter->branch_type & GIT_BRANCH_REMOTE &&
|
|
||||||
git__prefixcmp(branch_name, GIT_REFS_REMOTES_DIR) == 0)
|
|
||||||
return filter->branch_cb(branch_name + strlen(GIT_REFS_REMOTES_DIR), GIT_BRANCH_REMOTE, filter->callback_payload);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int git_branch_foreach(
|
int git_branch_foreach(
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
unsigned int list_flags,
|
unsigned int list_flags,
|
||||||
git_branch_foreach_cb branch_cb,
|
git_branch_foreach_cb callback,
|
||||||
void *payload)
|
void *payload)
|
||||||
{
|
{
|
||||||
branch_foreach_filter filter;
|
git_reference_iterator *iter;
|
||||||
|
git_reference *ref;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
filter.branch_cb = branch_cb;
|
if (git_reference_iterator_new(&iter, repo) < 0)
|
||||||
filter.branch_type = list_flags;
|
return -1;
|
||||||
filter.callback_payload = payload;
|
|
||||||
|
|
||||||
return git_reference_foreach(repo, GIT_REF_LISTALL, &branch_foreach_cb, (void *)&filter);
|
while ((error = git_reference_next(&ref, iter)) == 0) {
|
||||||
|
if (list_flags & GIT_BRANCH_LOCAL &&
|
||||||
|
git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) == 0) {
|
||||||
|
if (callback(ref->name + strlen(GIT_REFS_HEADS_DIR),
|
||||||
|
GIT_BRANCH_LOCAL, payload)) {
|
||||||
|
error = GIT_EUSER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (list_flags & GIT_BRANCH_REMOTE &&
|
||||||
|
git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR) == 0) {
|
||||||
|
if (callback(ref->name + strlen(GIT_REFS_REMOTES_DIR),
|
||||||
|
GIT_BRANCH_REMOTE, payload)) {
|
||||||
|
error = GIT_EUSER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
git_reference_free(ref);
|
||||||
|
|
||||||
|
/* check if the callback has cancelled iteration */
|
||||||
|
if (error == GIT_EUSER)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error == GIT_ITEROVER)
|
||||||
|
error = 0;
|
||||||
|
|
||||||
|
git_reference_iterator_free(iter);
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_branch_move(
|
int git_branch_move(
|
||||||
@ -175,18 +184,21 @@ int git_branch_move(
|
|||||||
if (!git_reference_is_branch(branch))
|
if (!git_reference_is_branch(branch))
|
||||||
return not_a_local_branch(git_reference_name(branch));
|
return not_a_local_branch(git_reference_name(branch));
|
||||||
|
|
||||||
if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0 ||
|
error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name);
|
||||||
(error = git_buf_printf(&old_config_section, "branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR))) < 0 ||
|
if (error < 0)
|
||||||
(error = git_buf_printf(&new_config_section, "branch.%s", new_branch_name)) < 0)
|
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
|
git_buf_printf(&old_config_section,
|
||||||
|
"branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR));
|
||||||
|
|
||||||
|
git_buf_printf(&new_config_section, "branch.%s", new_branch_name);
|
||||||
|
|
||||||
if ((error = git_config_rename_section(git_reference_owner(branch),
|
if ((error = git_config_rename_section(git_reference_owner(branch),
|
||||||
git_buf_cstr(&old_config_section),
|
git_buf_cstr(&old_config_section),
|
||||||
git_buf_cstr(&new_config_section))) < 0)
|
git_buf_cstr(&new_config_section))) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
if ((error = git_reference_rename(out, branch, git_buf_cstr(&new_reference_name), force)) < 0)
|
error = git_reference_rename(out, branch, git_buf_cstr(&new_reference_name), force);
|
||||||
goto done;
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
git_buf_free(&new_reference_name);
|
git_buf_free(&new_reference_name);
|
||||||
@ -275,6 +287,8 @@ int git_branch_upstream__name(
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if (!*remote_name || !*merge_name) {
|
if (!*remote_name || !*merge_name) {
|
||||||
|
giterr_set(GITERR_REFERENCE,
|
||||||
|
"branch '%s' does not have an upstream", canonical_branch_name);
|
||||||
error = GIT_ENOTFOUND;
|
error = GIT_ENOTFOUND;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@ -283,12 +297,10 @@ int git_branch_upstream__name(
|
|||||||
if ((error = git_remote_load(&remote, repo, remote_name)) < 0)
|
if ((error = git_remote_load(&remote, repo, remote_name)) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
refspec = git_remote_fetchspec(remote);
|
refspec = git_remote__matching_refspec(remote, merge_name);
|
||||||
if (refspec == NULL
|
if (!refspec) {
|
||||||
|| refspec->src == NULL
|
error = GIT_ENOTFOUND;
|
||||||
|| refspec->dst == NULL) {
|
goto cleanup;
|
||||||
error = GIT_ENOTFOUND;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (git_refspec_transform_r(&buf, refspec, merge_name) < 0)
|
if (git_refspec_transform_r(&buf, refspec, merge_name) < 0)
|
||||||
@ -333,11 +345,8 @@ static int remote_name(git_buf *buf, git_repository *repo, const char *canonical
|
|||||||
if ((error = git_remote_load(&remote, repo, remote_list.strings[i])) < 0)
|
if ((error = git_remote_load(&remote, repo, remote_list.strings[i])) < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
fetchspec = git_remote_fetchspec(remote);
|
fetchspec = git_remote__matching_dst_refspec(remote, canonical_branch_name);
|
||||||
|
if (fetchspec) {
|
||||||
/* Defensivly check that we have a fetchspec */
|
|
||||||
if (fetchspec &&
|
|
||||||
git_refspec_dst_matches(fetchspec, canonical_branch_name)) {
|
|
||||||
/* If we have not already set out yet, then set
|
/* If we have not already set out yet, then set
|
||||||
* it to the matching remote name. Otherwise
|
* it to the matching remote name. Otherwise
|
||||||
* multiple remotes match this reference, and it
|
* multiple remotes match this reference, and it
|
||||||
@ -346,6 +355,9 @@ static int remote_name(git_buf *buf, git_repository *repo, const char *canonical
|
|||||||
remote_name = remote_list.strings[i];
|
remote_name = remote_list.strings[i];
|
||||||
} else {
|
} else {
|
||||||
git_remote_free(remote);
|
git_remote_free(remote);
|
||||||
|
|
||||||
|
giterr_set(GITERR_REFERENCE,
|
||||||
|
"Reference '%s' is ambiguous", canonical_branch_name);
|
||||||
error = GIT_EAMBIGUOUS;
|
error = GIT_EAMBIGUOUS;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
@ -358,6 +370,8 @@ static int remote_name(git_buf *buf, git_repository *repo, const char *canonical
|
|||||||
git_buf_clear(buf);
|
git_buf_clear(buf);
|
||||||
error = git_buf_puts(buf, remote_name);
|
error = git_buf_puts(buf, remote_name);
|
||||||
} else {
|
} else {
|
||||||
|
giterr_set(GITERR_REFERENCE,
|
||||||
|
"Could not determine remote for '%s'", canonical_branch_name);
|
||||||
error = GIT_ENOTFOUND;
|
error = GIT_ENOTFOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,7 +391,7 @@ int git_branch_remote_name(char *buffer, size_t buffer_len, git_repository *repo
|
|||||||
if (buffer)
|
if (buffer)
|
||||||
git_buf_copy_cstr(buffer, buffer_len, &buf);
|
git_buf_copy_cstr(buffer, buffer_len, &buf);
|
||||||
|
|
||||||
ret = git_buf_len(&buf) + 1;
|
ret = (int)git_buf_len(&buf) + 1;
|
||||||
git_buf_free(&buf);
|
git_buf_free(&buf);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -494,8 +508,11 @@ int git_branch_set_upstream(git_reference *branch, const char *upstream_name)
|
|||||||
local = 1;
|
local = 1;
|
||||||
else if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_REMOTE) == 0)
|
else if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_REMOTE) == 0)
|
||||||
local = 0;
|
local = 0;
|
||||||
else
|
else {
|
||||||
|
giterr_set(GITERR_REFERENCE,
|
||||||
|
"Cannot set upstream for branch '%s'", shortname);
|
||||||
return GIT_ENOTFOUND;
|
return GIT_ENOTFOUND;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If it's local, the remote is "." and the branch name is
|
* If it's local, the remote is "." and the branch name is
|
||||||
@ -515,16 +532,17 @@ int git_branch_set_upstream(git_reference *branch, const char *upstream_name)
|
|||||||
goto on_error;
|
goto on_error;
|
||||||
|
|
||||||
if (local) {
|
if (local) {
|
||||||
if (git_buf_puts(&value, git_reference_name(branch)) < 0)
|
git_buf_clear(&value);
|
||||||
|
if (git_buf_puts(&value, git_reference_name(upstream)) < 0)
|
||||||
goto on_error;
|
goto on_error;
|
||||||
} else {
|
} else {
|
||||||
/* Get the remoe-tracking branch's refname in its repo */
|
/* Get the remoe-tracking branch's refname in its repo */
|
||||||
if (git_remote_load(&remote, repo, git_buf_cstr(&value)) < 0)
|
if (git_remote_load(&remote, repo, git_buf_cstr(&value)) < 0)
|
||||||
goto on_error;
|
goto on_error;
|
||||||
|
|
||||||
fetchspec = git_remote_fetchspec(remote);
|
fetchspec = git_remote__matching_dst_refspec(remote, git_reference_name(upstream));
|
||||||
git_buf_clear(&value);
|
git_buf_clear(&value);
|
||||||
if (git_refspec_transform_l(&value, fetchspec, git_reference_name(upstream)) < 0)
|
if (!fetchspec || git_refspec_transform_l(&value, fetchspec, git_reference_name(upstream)) < 0)
|
||||||
goto on_error;
|
goto on_error;
|
||||||
|
|
||||||
git_remote_free(remote);
|
git_remote_free(remote);
|
||||||
|
294
src/cache.c
294
src/cache.c
@ -11,100 +11,270 @@
|
|||||||
#include "thread-utils.h"
|
#include "thread-utils.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
|
#include "odb.h"
|
||||||
|
#include "object.h"
|
||||||
#include "git2/oid.h"
|
#include "git2/oid.h"
|
||||||
|
|
||||||
int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr)
|
GIT__USE_OIDMAP
|
||||||
|
|
||||||
|
bool git_cache__enabled = true;
|
||||||
|
ssize_t git_cache__max_storage = (256 * 1024 * 1024);
|
||||||
|
git_atomic_ssize git_cache__current_storage = {0};
|
||||||
|
|
||||||
|
static size_t git_cache__max_object_size[8] = {
|
||||||
|
0, /* GIT_OBJ__EXT1 */
|
||||||
|
4096, /* GIT_OBJ_COMMIT */
|
||||||
|
4096, /* GIT_OBJ_TREE */
|
||||||
|
0, /* GIT_OBJ_BLOB */
|
||||||
|
4096, /* GIT_OBJ_TAG */
|
||||||
|
0, /* GIT_OBJ__EXT2 */
|
||||||
|
0, /* GIT_OBJ_OFS_DELTA */
|
||||||
|
0 /* GIT_OBJ_REF_DELTA */
|
||||||
|
};
|
||||||
|
|
||||||
|
int git_cache_set_max_object_size(git_otype type, size_t size)
|
||||||
{
|
{
|
||||||
if (size < 8)
|
if (type < 0 || (size_t)type >= ARRAY_SIZE(git_cache__max_object_size)) {
|
||||||
size = 8;
|
giterr_set(GITERR_INVALID, "type out of range");
|
||||||
size = git__size_t_powerof2(size);
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
cache->size_mask = size - 1;
|
git_cache__max_object_size[type] = size;
|
||||||
cache->lru_count = 0;
|
|
||||||
cache->free_obj = free_ptr;
|
|
||||||
|
|
||||||
git_mutex_init(&cache->lock);
|
|
||||||
|
|
||||||
cache->nodes = git__malloc(size * sizeof(git_cached_obj *));
|
|
||||||
GITERR_CHECK_ALLOC(cache->nodes);
|
|
||||||
|
|
||||||
memset(cache->nodes, 0x0, size * sizeof(git_cached_obj *));
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void git_cache_dump_stats(git_cache *cache)
|
||||||
|
{
|
||||||
|
git_cached_obj *object;
|
||||||
|
|
||||||
|
if (kh_size(cache->map) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
printf("Cache %p: %d items cached, %d bytes\n",
|
||||||
|
cache, kh_size(cache->map), (int)cache->used_memory);
|
||||||
|
|
||||||
|
kh_foreach_value(cache->map, object, {
|
||||||
|
char oid_str[9];
|
||||||
|
printf(" %s%c %s (%d)\n",
|
||||||
|
git_object_type2string(object->type),
|
||||||
|
object->flags == GIT_CACHE_STORE_PARSED ? '*' : ' ',
|
||||||
|
git_oid_tostr(oid_str, sizeof(oid_str), &object->oid),
|
||||||
|
(int)object->size
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_cache_init(git_cache *cache)
|
||||||
|
{
|
||||||
|
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");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* called with lock */
|
||||||
|
static void clear_cache(git_cache *cache)
|
||||||
|
{
|
||||||
|
git_cached_obj *evict = NULL;
|
||||||
|
|
||||||
|
if (kh_size(cache->map) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
kh_foreach_value(cache->map, evict, {
|
||||||
|
git_cached_obj_decref(evict);
|
||||||
|
});
|
||||||
|
|
||||||
|
kh_clear(oid, cache->map);
|
||||||
|
git_atomic_ssize_add(&git_cache__current_storage, -cache->used_memory);
|
||||||
|
cache->used_memory = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_cache_clear(git_cache *cache)
|
||||||
|
{
|
||||||
|
if (git_mutex_lock(&cache->lock) < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
clear_cache(cache);
|
||||||
|
|
||||||
|
git_mutex_unlock(&cache->lock);
|
||||||
|
}
|
||||||
|
|
||||||
void git_cache_free(git_cache *cache)
|
void git_cache_free(git_cache *cache)
|
||||||
{
|
{
|
||||||
size_t i;
|
git_cache_clear(cache);
|
||||||
|
git_oidmap_free(cache->map);
|
||||||
for (i = 0; i < (cache->size_mask + 1); ++i) {
|
|
||||||
if (cache->nodes[i] != NULL)
|
|
||||||
git_cached_obj_decref(cache->nodes[i], cache->free_obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
git_mutex_free(&cache->lock);
|
git_mutex_free(&cache->lock);
|
||||||
git__free(cache->nodes);
|
git__memzero(cache, sizeof(*cache));
|
||||||
}
|
}
|
||||||
|
|
||||||
void *git_cache_get(git_cache *cache, const git_oid *oid)
|
/* Called with lock */
|
||||||
|
static void cache_evict_entries(git_cache *cache)
|
||||||
{
|
{
|
||||||
uint32_t hash;
|
uint32_t seed = rand();
|
||||||
git_cached_obj *node = NULL, *result = NULL;
|
size_t evict_count = 8;
|
||||||
|
ssize_t evicted_memory = 0;
|
||||||
|
|
||||||
memcpy(&hash, oid->id, sizeof(hash));
|
/* do not infinite loop if there's not enough entries to evict */
|
||||||
|
if (evict_count > kh_size(cache->map)) {
|
||||||
if (git_mutex_lock(&cache->lock)) {
|
clear_cache(cache);
|
||||||
giterr_set(GITERR_THREAD, "unable to lock cache mutex");
|
return;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
while (evict_count > 0) {
|
||||||
node = cache->nodes[hash & cache->size_mask];
|
khiter_t pos = seed++ % kh_end(cache->map);
|
||||||
|
|
||||||
if (node != NULL && git_oid_cmp(&node->oid, oid) == 0) {
|
if (kh_exist(cache->map, pos)) {
|
||||||
git_cached_obj_incref(node);
|
git_cached_obj *evict = kh_val(cache->map, pos);
|
||||||
result = node;
|
|
||||||
|
evict_count--;
|
||||||
|
evicted_memory += evict->size;
|
||||||
|
git_cached_obj_decref(evict);
|
||||||
|
|
||||||
|
kh_del(oid, cache->map, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
git_mutex_unlock(&cache->lock);
|
|
||||||
|
|
||||||
return result;
|
cache->used_memory -= evicted_memory;
|
||||||
|
git_atomic_ssize_add(&git_cache__current_storage, -evicted_memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
void *git_cache_try_store(git_cache *cache, void *_entry)
|
static bool cache_should_store(git_otype object_type, size_t object_size)
|
||||||
{
|
{
|
||||||
git_cached_obj *entry = _entry;
|
size_t max_size = git_cache__max_object_size[object_type];
|
||||||
uint32_t hash;
|
return git_cache__enabled && object_size < max_size;
|
||||||
|
}
|
||||||
|
|
||||||
memcpy(&hash, &entry->oid, sizeof(uint32_t));
|
static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags)
|
||||||
|
{
|
||||||
|
khiter_t pos;
|
||||||
|
git_cached_obj *entry = NULL;
|
||||||
|
|
||||||
if (git_mutex_lock(&cache->lock)) {
|
if (!git_cache__enabled || git_mutex_lock(&cache->lock) < 0)
|
||||||
giterr_set(GITERR_THREAD, "unable to lock cache mutex");
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
{
|
pos = kh_get(oid, cache->map, oid);
|
||||||
git_cached_obj *node = cache->nodes[hash & cache->size_mask];
|
if (pos != kh_end(cache->map)) {
|
||||||
|
entry = kh_val(cache->map, pos);
|
||||||
|
|
||||||
/* increase the refcount on this object, because
|
if (flags && entry->flags != flags) {
|
||||||
* the cache now owns it */
|
entry = NULL;
|
||||||
git_cached_obj_incref(entry);
|
|
||||||
|
|
||||||
if (node == NULL) {
|
|
||||||
cache->nodes[hash & cache->size_mask] = entry;
|
|
||||||
} else if (git_oid_cmp(&node->oid, &entry->oid) == 0) {
|
|
||||||
git_cached_obj_decref(entry, cache->free_obj);
|
|
||||||
entry = node;
|
|
||||||
} else {
|
} else {
|
||||||
git_cached_obj_decref(node, cache->free_obj);
|
git_cached_obj_incref(entry);
|
||||||
cache->nodes[hash & cache->size_mask] = entry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* increase the refcount again, because we are
|
|
||||||
* returning it to the user */
|
|
||||||
git_cached_obj_incref(entry);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
git_mutex_unlock(&cache->lock);
|
git_mutex_unlock(&cache->lock);
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *cache_store(git_cache *cache, git_cached_obj *entry)
|
||||||
|
{
|
||||||
|
khiter_t pos;
|
||||||
|
|
||||||
|
git_cached_obj_incref(entry);
|
||||||
|
|
||||||
|
if (!git_cache__enabled && cache->used_memory > 0) {
|
||||||
|
git_cache_clear(cache);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cache_should_store(entry->type, entry->size))
|
||||||
|
return entry;
|
||||||
|
|
||||||
|
if (git_mutex_lock(&cache->lock) < 0)
|
||||||
|
return entry;
|
||||||
|
|
||||||
|
/* soften the load on the cache */
|
||||||
|
if (git_cache__current_storage.val > git_cache__max_storage)
|
||||||
|
cache_evict_entries(cache);
|
||||||
|
|
||||||
|
pos = kh_get(oid, cache->map, &entry->oid);
|
||||||
|
|
||||||
|
/* not found */
|
||||||
|
if (pos == kh_end(cache->map)) {
|
||||||
|
int rval;
|
||||||
|
|
||||||
|
pos = kh_put(oid, cache->map, &entry->oid, &rval);
|
||||||
|
if (rval >= 0) {
|
||||||
|
kh_key(cache->map, pos) = &entry->oid;
|
||||||
|
kh_val(cache->map, pos) = entry;
|
||||||
|
git_cached_obj_incref(entry);
|
||||||
|
cache->used_memory += entry->size;
|
||||||
|
git_atomic_ssize_add(&git_cache__current_storage, (ssize_t)entry->size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* found */
|
||||||
|
else {
|
||||||
|
git_cached_obj *stored_entry = kh_val(cache->map, pos);
|
||||||
|
|
||||||
|
if (stored_entry->flags == entry->flags) {
|
||||||
|
git_cached_obj_decref(entry);
|
||||||
|
git_cached_obj_incref(stored_entry);
|
||||||
|
entry = stored_entry;
|
||||||
|
} else if (stored_entry->flags == GIT_CACHE_STORE_RAW &&
|
||||||
|
entry->flags == GIT_CACHE_STORE_PARSED) {
|
||||||
|
git_cached_obj_decref(stored_entry);
|
||||||
|
git_cached_obj_incref(entry);
|
||||||
|
|
||||||
|
kh_key(cache->map, pos) = &entry->oid;
|
||||||
|
kh_val(cache->map, pos) = entry;
|
||||||
|
} else {
|
||||||
|
/* NO OP */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
git_mutex_unlock(&cache->lock);
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *git_cache_store_raw(git_cache *cache, git_odb_object *entry)
|
||||||
|
{
|
||||||
|
entry->cached.flags = GIT_CACHE_STORE_RAW;
|
||||||
|
return cache_store(cache, (git_cached_obj *)entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *git_cache_store_parsed(git_cache *cache, git_object *entry)
|
||||||
|
{
|
||||||
|
entry->cached.flags = GIT_CACHE_STORE_PARSED;
|
||||||
|
return cache_store(cache, (git_cached_obj *)entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid)
|
||||||
|
{
|
||||||
|
return cache_get(cache, oid, GIT_CACHE_STORE_RAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid)
|
||||||
|
{
|
||||||
|
return cache_get(cache, oid, GIT_CACHE_STORE_PARSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *git_cache_get_any(git_cache *cache, const git_oid *oid)
|
||||||
|
{
|
||||||
|
return cache_get(cache, oid, GIT_CACHE_STORE_ANY);
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_cached_obj_decref(void *_obj)
|
||||||
|
{
|
||||||
|
git_cached_obj *obj = _obj;
|
||||||
|
|
||||||
|
if (git_atomic_dec(&obj->refcount) == 0) {
|
||||||
|
switch (obj->flags) {
|
||||||
|
case GIT_CACHE_STORE_RAW:
|
||||||
|
git_odb_object__free(_obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GIT_CACHE_STORE_PARSED:
|
||||||
|
git_object__free(_obj);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
git__free(_obj);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
55
src/cache.h
55
src/cache.h
@ -12,30 +12,49 @@
|
|||||||
#include "git2/odb.h"
|
#include "git2/odb.h"
|
||||||
|
|
||||||
#include "thread-utils.h"
|
#include "thread-utils.h"
|
||||||
|
#include "oidmap.h"
|
||||||
|
|
||||||
#define GIT_DEFAULT_CACHE_SIZE 128
|
enum {
|
||||||
|
GIT_CACHE_STORE_ANY = 0,
|
||||||
typedef void (*git_cached_obj_freeptr)(void *);
|
GIT_CACHE_STORE_RAW = 1,
|
||||||
|
GIT_CACHE_STORE_PARSED = 2
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
git_oid oid;
|
git_oid oid;
|
||||||
|
int16_t type; /* git_otype value */
|
||||||
|
uint16_t flags; /* GIT_CACHE_STORE value */
|
||||||
|
size_t size;
|
||||||
git_atomic refcount;
|
git_atomic refcount;
|
||||||
} git_cached_obj;
|
} git_cached_obj;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
git_cached_obj **nodes;
|
git_oidmap *map;
|
||||||
git_mutex lock;
|
git_mutex lock;
|
||||||
|
ssize_t used_memory;
|
||||||
unsigned int lru_count;
|
|
||||||
size_t size_mask;
|
|
||||||
git_cached_obj_freeptr free_obj;
|
|
||||||
} git_cache;
|
} git_cache;
|
||||||
|
|
||||||
int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr);
|
extern bool git_cache__enabled;
|
||||||
void git_cache_free(git_cache *cache);
|
extern ssize_t git_cache__max_storage;
|
||||||
|
extern git_atomic_ssize git_cache__current_storage;
|
||||||
|
|
||||||
void *git_cache_try_store(git_cache *cache, void *entry);
|
int git_cache_set_max_object_size(git_otype type, size_t size);
|
||||||
void *git_cache_get(git_cache *cache, const git_oid *oid);
|
|
||||||
|
int git_cache_init(git_cache *cache);
|
||||||
|
void git_cache_free(git_cache *cache);
|
||||||
|
void git_cache_clear(git_cache *cache);
|
||||||
|
|
||||||
|
void *git_cache_store_raw(git_cache *cache, git_odb_object *entry);
|
||||||
|
void *git_cache_store_parsed(git_cache *cache, git_object *entry);
|
||||||
|
|
||||||
|
git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid);
|
||||||
|
git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid);
|
||||||
|
void *git_cache_get_any(git_cache *cache, const git_oid *oid);
|
||||||
|
|
||||||
|
GIT_INLINE(size_t) git_cache_size(git_cache *cache)
|
||||||
|
{
|
||||||
|
return (size_t)kh_size(cache->map);
|
||||||
|
}
|
||||||
|
|
||||||
GIT_INLINE(void) git_cached_obj_incref(void *_obj)
|
GIT_INLINE(void) git_cached_obj_incref(void *_obj)
|
||||||
{
|
{
|
||||||
@ -43,12 +62,6 @@ GIT_INLINE(void) git_cached_obj_incref(void *_obj)
|
|||||||
git_atomic_inc(&obj->refcount);
|
git_atomic_inc(&obj->refcount);
|
||||||
}
|
}
|
||||||
|
|
||||||
GIT_INLINE(void) git_cached_obj_decref(void *_obj, git_cached_obj_freeptr free_obj)
|
void git_cached_obj_decref(void *_obj);
|
||||||
{
|
|
||||||
git_cached_obj *obj = _obj;
|
|
||||||
|
|
||||||
if (git_atomic_dec(&obj->refcount) == 0)
|
|
||||||
free_obj(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
242
src/checkout.c
242
src/checkout.c
@ -16,9 +16,11 @@
|
|||||||
#include "git2/config.h"
|
#include "git2/config.h"
|
||||||
#include "git2/diff.h"
|
#include "git2/diff.h"
|
||||||
#include "git2/submodule.h"
|
#include "git2/submodule.h"
|
||||||
|
#include "git2/sys/index.h"
|
||||||
|
|
||||||
#include "refs.h"
|
#include "refs.h"
|
||||||
#include "repository.h"
|
#include "repository.h"
|
||||||
|
#include "index.h"
|
||||||
#include "filter.h"
|
#include "filter.h"
|
||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
#include "diff.h"
|
#include "diff.h"
|
||||||
@ -119,6 +121,7 @@ static bool checkout_is_workdir_modified(
|
|||||||
const git_index_entry *wditem)
|
const git_index_entry *wditem)
|
||||||
{
|
{
|
||||||
git_oid oid;
|
git_oid oid;
|
||||||
|
const git_index_entry *ie;
|
||||||
|
|
||||||
/* handle "modified" submodule */
|
/* handle "modified" submodule */
|
||||||
if (wditem->mode == GIT_FILEMODE_COMMIT) {
|
if (wditem->mode == GIT_FILEMODE_COMMIT) {
|
||||||
@ -137,7 +140,18 @@ static bool checkout_is_workdir_modified(
|
|||||||
if (!sm_oid)
|
if (!sm_oid)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return (git_oid_cmp(&baseitem->oid, sm_oid) != 0);
|
return (git_oid__cmp(&baseitem->oid, sm_oid) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Look at the cache to decide if the workdir is modified. If not,
|
||||||
|
* we can simply compare the oid in the cache to the baseitem instead
|
||||||
|
* of hashing the file.
|
||||||
|
*/
|
||||||
|
if ((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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* depending on where base is coming from, we may or may not know
|
/* depending on where base is coming from, we may or may not know
|
||||||
@ -151,7 +165,7 @@ static bool checkout_is_workdir_modified(
|
|||||||
wditem->file_size, &oid) < 0)
|
wditem->file_size, &oid) < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return (git_oid_cmp(&baseitem->oid, &oid) != 0);
|
return (git_oid__cmp(&baseitem->oid, &oid) != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CHECKOUT_ACTION_IF(FLAG,YES,NO) \
|
#define CHECKOUT_ACTION_IF(FLAG,YES,NO) \
|
||||||
@ -176,6 +190,10 @@ static int checkout_action_common(
|
|||||||
action = (action & ~CHECKOUT_ACTION__UPDATE_BLOB) |
|
action = (action & ~CHECKOUT_ACTION__UPDATE_BLOB) |
|
||||||
CHECKOUT_ACTION__UPDATE_SUBMODULE;
|
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;
|
||||||
|
|
||||||
notify = GIT_CHECKOUT_NOTIFY_UPDATED;
|
notify = GIT_CHECKOUT_NOTIFY_UPDATED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,9 +220,11 @@ static int checkout_action_no_wd(
|
|||||||
action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, NONE);
|
action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, NONE);
|
||||||
break;
|
break;
|
||||||
case GIT_DELTA_ADDED: /* case 2 or 28 (and 5 but not really) */
|
case GIT_DELTA_ADDED: /* case 2 or 28 (and 5 but not really) */
|
||||||
case GIT_DELTA_MODIFIED: /* case 13 (and 35 but not really) */
|
|
||||||
action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
|
action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
|
||||||
break;
|
break;
|
||||||
|
case GIT_DELTA_MODIFIED: /* case 13 (and 35 but not really) */
|
||||||
|
action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, CONFLICT);
|
||||||
|
break;
|
||||||
case GIT_DELTA_TYPECHANGE: /* case 21 (B->T) and 28 (T->B)*/
|
case GIT_DELTA_TYPECHANGE: /* case 21 (B->T) and 28 (T->B)*/
|
||||||
if (delta->new_file.mode == GIT_FILEMODE_TREE)
|
if (delta->new_file.mode == GIT_FILEMODE_TREE)
|
||||||
action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
|
action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
|
||||||
@ -454,6 +474,7 @@ static int checkout_action(
|
|||||||
int cmp = -1, act;
|
int cmp = -1, act;
|
||||||
int (*strcomp)(const char *, const char *) = data->diff->strcomp;
|
int (*strcomp)(const char *, const char *) = data->diff->strcomp;
|
||||||
int (*pfxcomp)(const char *str, const char *pfx) = data->diff->pfxcomp;
|
int (*pfxcomp)(const char *str, const char *pfx) = data->diff->pfxcomp;
|
||||||
|
int error;
|
||||||
|
|
||||||
/* move workdir iterator to follow along with deltas */
|
/* move workdir iterator to follow along with deltas */
|
||||||
|
|
||||||
@ -477,9 +498,9 @@ static int checkout_action(
|
|||||||
if (cmp == 0) {
|
if (cmp == 0) {
|
||||||
if (wd->mode == GIT_FILEMODE_TREE) {
|
if (wd->mode == GIT_FILEMODE_TREE) {
|
||||||
/* case 2 - entry prefixed by workdir tree */
|
/* case 2 - entry prefixed by workdir tree */
|
||||||
if (git_iterator_advance_into(&wd, workdir) < 0)
|
error = git_iterator_advance_into_or_over(&wd, workdir);
|
||||||
|
if (error && error != GIT_ITEROVER)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
*wditem_ptr = wd;
|
*wditem_ptr = wd;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -494,8 +515,10 @@ static int checkout_action(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* case 1 - handle wd item (if it matches pathspec) */
|
/* case 1 - handle wd item (if it matches pathspec) */
|
||||||
if (checkout_action_wd_only(data, workdir, wd, pathspec) < 0 ||
|
if (checkout_action_wd_only(data, workdir, wd, pathspec) < 0)
|
||||||
git_iterator_advance(&wd, workdir) < 0)
|
goto fail;
|
||||||
|
if ((error = git_iterator_advance(&wd, workdir)) < 0 &&
|
||||||
|
error != GIT_ITEROVER)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
*wditem_ptr = wd;
|
*wditem_ptr = wd;
|
||||||
@ -518,8 +541,9 @@ static int checkout_action(
|
|||||||
if (delta->status == GIT_DELTA_TYPECHANGE) {
|
if (delta->status == GIT_DELTA_TYPECHANGE) {
|
||||||
if (delta->old_file.mode == GIT_FILEMODE_TREE) {
|
if (delta->old_file.mode == GIT_FILEMODE_TREE) {
|
||||||
act = checkout_action_with_wd(data, delta, wd);
|
act = checkout_action_with_wd(data, delta, wd);
|
||||||
if (git_iterator_advance_into(&wd, workdir) < 0)
|
if ((error = git_iterator_advance_into(&wd, workdir)) < 0 &&
|
||||||
wd = NULL;
|
error != GIT_ENOTFOUND)
|
||||||
|
goto fail;
|
||||||
*wditem_ptr = wd;
|
*wditem_ptr = wd;
|
||||||
return act;
|
return act;
|
||||||
}
|
}
|
||||||
@ -529,8 +553,9 @@ static int checkout_action(
|
|||||||
delta->old_file.mode == GIT_FILEMODE_COMMIT)
|
delta->old_file.mode == GIT_FILEMODE_COMMIT)
|
||||||
{
|
{
|
||||||
act = checkout_action_with_wd(data, delta, wd);
|
act = checkout_action_with_wd(data, delta, wd);
|
||||||
if (git_iterator_advance(&wd, workdir) < 0)
|
if ((error = git_iterator_advance(&wd, workdir)) < 0 &&
|
||||||
wd = NULL;
|
error != GIT_ITEROVER)
|
||||||
|
goto fail;
|
||||||
*wditem_ptr = wd;
|
*wditem_ptr = wd;
|
||||||
return act;
|
return act;
|
||||||
}
|
}
|
||||||
@ -561,6 +586,9 @@ static int checkout_remaining_wd_items(
|
|||||||
error = git_iterator_advance(&wd, workdir);
|
error = git_iterator_advance(&wd, workdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (error == GIT_ITEROVER)
|
||||||
|
error = 0;
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -582,7 +610,8 @@ static int checkout_get_actions(
|
|||||||
git_pathspec_init(&pathspec, &data->opts.paths, &pathpool) < 0)
|
git_pathspec_init(&pathspec, &data->opts.paths, &pathpool) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if ((error = git_iterator_current(&wditem, workdir)) < 0)
|
if ((error = git_iterator_current(&wditem, workdir)) < 0 &&
|
||||||
|
error != GIT_ITEROVER)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
deltas = &data->diff->deltas;
|
deltas = &data->diff->deltas;
|
||||||
@ -655,33 +684,26 @@ static int buffer_to_file(
|
|||||||
int file_open_flags,
|
int file_open_flags,
|
||||||
mode_t file_mode)
|
mode_t file_mode)
|
||||||
{
|
{
|
||||||
int fd, error;
|
int error;
|
||||||
|
|
||||||
if ((error = git_futils_mkpath2file(path, dir_mode)) < 0)
|
if ((error = git_futils_mkpath2file(path, dir_mode)) < 0)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
if ((fd = p_open(path, file_open_flags, file_mode)) < 0) {
|
if ((error = git_futils_writebuffer(
|
||||||
giterr_set(GITERR_OS, "Could not open '%s' for writing", path);
|
buffer, path, file_open_flags, file_mode)) < 0)
|
||||||
return fd;
|
return error;
|
||||||
|
|
||||||
|
if (st != NULL && (error = p_stat(path, st)) < 0) {
|
||||||
|
giterr_set(GITERR_OS, "Error while statting '%s'", path);
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((error = p_write(fd, git_buf_cstr(buffer), git_buf_len(buffer))) < 0) {
|
if ((file_mode & 0100) != 0 && (error = p_chmod(path, file_mode)) < 0) {
|
||||||
giterr_set(GITERR_OS, "Could not write to '%s'", path);
|
|
||||||
(void)p_close(fd);
|
|
||||||
} else {
|
|
||||||
if ((error = p_close(fd)) < 0)
|
|
||||||
giterr_set(GITERR_OS, "Error while closing '%s'", path);
|
|
||||||
|
|
||||||
if ((error = p_stat(path, st)) < 0)
|
|
||||||
giterr_set(GITERR_OS, "Error while statting '%s'", path);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!error &&
|
|
||||||
(file_mode & 0100) != 0 &&
|
|
||||||
(error = p_chmod(path, file_mode)) < 0)
|
|
||||||
giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
|
giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
return error;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int blob_content_to_file(
|
static int blob_content_to_file(
|
||||||
@ -698,8 +720,8 @@ static int blob_content_to_file(
|
|||||||
git_vector filters = GIT_VECTOR_INIT;
|
git_vector filters = GIT_VECTOR_INIT;
|
||||||
|
|
||||||
/* Create a fake git_buf from the blob raw data... */
|
/* Create a fake git_buf from the blob raw data... */
|
||||||
filtered.ptr = blob->odb_object->raw.data;
|
filtered.ptr = (void *)git_blob_rawcontent(blob);
|
||||||
filtered.size = blob->odb_object->raw.len;
|
filtered.size = (size_t)git_blob_rawsize(blob);
|
||||||
/* ... and make sure it doesn't get unexpectedly freed */
|
/* ... and make sure it doesn't get unexpectedly freed */
|
||||||
dont_free_filtered = true;
|
dont_free_filtered = true;
|
||||||
|
|
||||||
@ -747,17 +769,24 @@ cleanup:
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int blob_content_to_link(
|
static int blob_content_to_link(
|
||||||
struct stat *st, git_blob *blob, const char *path, int can_symlink)
|
struct stat *st,
|
||||||
|
git_blob *blob,
|
||||||
|
const char *path,
|
||||||
|
mode_t dir_mode,
|
||||||
|
int can_symlink)
|
||||||
{
|
{
|
||||||
git_buf linktarget = GIT_BUF_INIT;
|
git_buf linktarget = GIT_BUF_INIT;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
if ((error = git_futils_mkpath2file(path, dir_mode)) < 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
if ((error = git_blob__getbuf(&linktarget, blob)) < 0)
|
if ((error = git_blob__getbuf(&linktarget, blob)) < 0)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
if (can_symlink) {
|
if (can_symlink) {
|
||||||
if ((error = p_symlink(git_buf_cstr(&linktarget), path)) < 0)
|
if ((error = p_symlink(git_buf_cstr(&linktarget), path)) < 0)
|
||||||
giterr_set(GITERR_CHECKOUT, "Could not create symlink %s\n", path);
|
giterr_set(GITERR_OS, "Could not create symlink %s\n", path);
|
||||||
} else {
|
} else {
|
||||||
error = git_futils_fake_symlink(git_buf_cstr(&linktarget), path);
|
error = git_futils_fake_symlink(git_buf_cstr(&linktarget), path);
|
||||||
}
|
}
|
||||||
@ -792,6 +821,31 @@ static int checkout_update_index(
|
|||||||
return git_index_add(data->index, &entry);
|
return git_index_add(data->index, &entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int checkout_submodule_update_index(
|
||||||
|
checkout_data *data,
|
||||||
|
const git_diff_file *file)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
/* update the index unless prevented */
|
||||||
|
if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) != 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
git_buf_truncate(&data->path, data->workdir_len);
|
||||||
|
if (git_buf_puts(&data->path, file->path) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (p_stat(git_buf_cstr(&data->path), &st) < 0) {
|
||||||
|
giterr_set(
|
||||||
|
GITERR_CHECKOUT, "Could not stat submodule %s\n", file->path);
|
||||||
|
return GIT_ENOTFOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
st.st_mode = GIT_FILEMODE_COMMIT;
|
||||||
|
|
||||||
|
return checkout_update_index(data, file, &st);
|
||||||
|
}
|
||||||
|
|
||||||
static int checkout_submodule(
|
static int checkout_submodule(
|
||||||
checkout_data *data,
|
checkout_data *data,
|
||||||
const git_diff_file *file)
|
const git_diff_file *file)
|
||||||
@ -804,12 +858,21 @@ static int checkout_submodule(
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if ((error = git_futils_mkdir(
|
if ((error = git_futils_mkdir(
|
||||||
file->path, git_repository_workdir(data->repo),
|
file->path, data->opts.target_directory,
|
||||||
data->opts.dir_mode, GIT_MKDIR_PATH)) < 0)
|
data->opts.dir_mode, GIT_MKDIR_PATH)) < 0)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
if ((error = git_submodule_lookup(&sm, data->repo, file->path)) < 0)
|
if ((error = git_submodule_lookup(&sm, 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
|
||||||
|
*/
|
||||||
|
if (error == GIT_ENOTFOUND) {
|
||||||
|
giterr_clear();
|
||||||
|
return checkout_submodule_update_index(data, file);
|
||||||
|
}
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO: Support checkout_strategy options. Two circumstances:
|
/* TODO: Support checkout_strategy options. Two circumstances:
|
||||||
* 1 - submodule already checked out, but we need to move the HEAD
|
* 1 - submodule already checked out, but we need to move the HEAD
|
||||||
@ -820,26 +883,7 @@ static int checkout_submodule(
|
|||||||
* command should probably be able to. Do we need a submodule callback?
|
* command should probably be able to. Do we need a submodule callback?
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* update the index unless prevented */
|
return checkout_submodule_update_index(data, file);
|
||||||
if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0) {
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
git_buf_truncate(&data->path, data->workdir_len);
|
|
||||||
if (git_buf_puts(&data->path, file->path) < 0)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if ((error = p_stat(git_buf_cstr(&data->path), &st)) < 0) {
|
|
||||||
giterr_set(
|
|
||||||
GITERR_CHECKOUT, "Could not stat submodule %s\n", file->path);
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
st.st_mode = GIT_FILEMODE_COMMIT;
|
|
||||||
|
|
||||||
error = checkout_update_index(data, file, &st);
|
|
||||||
}
|
|
||||||
|
|
||||||
return error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void report_progress(
|
static void report_progress(
|
||||||
@ -897,7 +941,7 @@ static int checkout_blob(
|
|||||||
|
|
||||||
if (S_ISLNK(file->mode))
|
if (S_ISLNK(file->mode))
|
||||||
error = blob_content_to_link(
|
error = blob_content_to_link(
|
||||||
&st, blob, git_buf_cstr(&data->path), data->can_symlink);
|
&st, blob, git_buf_cstr(&data->path), data->opts.dir_mode, data->can_symlink);
|
||||||
else
|
else
|
||||||
error = blob_content_to_file(
|
error = blob_content_to_file(
|
||||||
&st, blob, git_buf_cstr(&data->path), file->mode, &data->opts);
|
&st, blob, git_buf_cstr(&data->path), file->mode, &data->opts);
|
||||||
@ -938,6 +982,9 @@ static int checkout_remove_the_old(
|
|||||||
uint32_t flg = GIT_RMDIR_EMPTY_PARENTS |
|
uint32_t flg = GIT_RMDIR_EMPTY_PARENTS |
|
||||||
GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS;
|
GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS;
|
||||||
|
|
||||||
|
if (data->opts.checkout_strategy & GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES)
|
||||||
|
flg |= GIT_RMDIR_SKIP_NONEMPTY;
|
||||||
|
|
||||||
git_buf_truncate(&data->path, data->workdir_len);
|
git_buf_truncate(&data->path, data->workdir_len);
|
||||||
|
|
||||||
git_vector_foreach(&data->diff->deltas, i, delta) {
|
git_vector_foreach(&data->diff->deltas, i, delta) {
|
||||||
@ -983,7 +1030,7 @@ static int checkout_deferred_remove(git_repository *repo, const char *path)
|
|||||||
{
|
{
|
||||||
#if 0
|
#if 0
|
||||||
int error = git_futils_rmdir_r(
|
int error = git_futils_rmdir_r(
|
||||||
path, git_repository_workdir(repo), GIT_RMDIR_EMPTY_PARENTS);
|
path, data->opts.target_directory, GIT_RMDIR_EMPTY_PARENTS);
|
||||||
|
|
||||||
if (error == GIT_ENOTFOUND) {
|
if (error == GIT_ENOTFOUND) {
|
||||||
error = 0;
|
error = 0;
|
||||||
@ -1107,7 +1154,6 @@ static int checkout_data_init(
|
|||||||
git_checkout_opts *proposed)
|
git_checkout_opts *proposed)
|
||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
git_config *cfg;
|
|
||||||
git_repository *repo = git_iterator_owner(target);
|
git_repository *repo = git_iterator_owner(target);
|
||||||
|
|
||||||
memset(data, 0, sizeof(*data));
|
memset(data, 0, sizeof(*data));
|
||||||
@ -1117,10 +1163,8 @@ static int checkout_data_init(
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((error = git_repository__ensure_not_bare(repo, "checkout")) < 0)
|
if ((!proposed || !proposed->target_directory) &&
|
||||||
return error;
|
(error = git_repository__ensure_not_bare(repo, "checkout")) < 0)
|
||||||
|
|
||||||
if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
|
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
data->repo = repo;
|
data->repo = repo;
|
||||||
@ -1133,9 +1177,19 @@ static int checkout_data_init(
|
|||||||
else
|
else
|
||||||
memmove(&data->opts, proposed, sizeof(git_checkout_opts));
|
memmove(&data->opts, proposed, sizeof(git_checkout_opts));
|
||||||
|
|
||||||
|
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)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
/* refresh config and index content unless NO_REFRESH is given */
|
/* refresh config and index content unless NO_REFRESH is given */
|
||||||
if ((data->opts.checkout_strategy & GIT_CHECKOUT_NO_REFRESH) == 0) {
|
if ((data->opts.checkout_strategy & GIT_CHECKOUT_NO_REFRESH) == 0) {
|
||||||
if ((error = git_config_refresh(cfg)) < 0)
|
git_config *cfg;
|
||||||
|
|
||||||
|
if ((error = git_repository_config__weakptr(&cfg, repo)) < 0 ||
|
||||||
|
(error = git_config_refresh(cfg)) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
/* if we are checking out the index, don't reload,
|
/* if we are checking out the index, don't reload,
|
||||||
@ -1172,19 +1226,13 @@ static int checkout_data_init(
|
|||||||
|
|
||||||
data->pfx = git_pathspec_prefix(&data->opts.paths);
|
data->pfx = git_pathspec_prefix(&data->opts.paths);
|
||||||
|
|
||||||
error = git_config_get_bool(&data->can_symlink, cfg, "core.symlinks");
|
if ((error = git_repository__cvar(
|
||||||
if (error < 0) {
|
&data->can_symlink, repo, GIT_CVAR_SYMLINKS)) < 0)
|
||||||
if (error != GIT_ENOTFOUND)
|
goto cleanup;
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
/* If "core.symlinks" is not found anywhere, default to true. */
|
|
||||||
data->can_symlink = true;
|
|
||||||
giterr_clear();
|
|
||||||
error = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!data->opts.baseline) {
|
if (!data->opts.baseline) {
|
||||||
data->opts_free_baseline = true;
|
data->opts_free_baseline = true;
|
||||||
|
|
||||||
error = checkout_lookup_head_tree(&data->opts.baseline, repo);
|
error = checkout_lookup_head_tree(&data->opts.baseline, repo);
|
||||||
|
|
||||||
if (error == GIT_EORPHANEDHEAD) {
|
if (error == GIT_EORPHANEDHEAD) {
|
||||||
@ -1198,7 +1246,8 @@ static int checkout_data_init(
|
|||||||
|
|
||||||
if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
|
if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
|
||||||
(error = git_pool_init(&data->pool, 1, 0)) < 0 ||
|
(error = git_pool_init(&data->pool, 1, 0)) < 0 ||
|
||||||
(error = git_buf_puts(&data->path, git_repository_workdir(repo))) < 0)
|
(error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 ||
|
||||||
|
(error = git_path_to_dir(&data->path)) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
data->workdir_len = git_buf_len(&data->path);
|
data->workdir_len = git_buf_len(&data->path);
|
||||||
@ -1246,11 +1295,13 @@ int git_checkout_iterator(
|
|||||||
GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE;
|
GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE;
|
||||||
|
|
||||||
if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 ||
|
if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 ||
|
||||||
(error = git_iterator_for_workdir(
|
(error = git_iterator_for_workdir_ext(
|
||||||
&workdir, data.repo, iterflags | GIT_ITERATOR_DONT_AUTOEXPAND,
|
&workdir, data.repo, data.opts.target_directory,
|
||||||
|
iterflags | GIT_ITERATOR_DONT_AUTOEXPAND,
|
||||||
data.pfx, data.pfx)) < 0 ||
|
data.pfx, data.pfx)) < 0 ||
|
||||||
(error = git_iterator_for_tree(
|
(error = git_iterator_for_tree(
|
||||||
&baseline, data.opts.baseline, iterflags, data.pfx, data.pfx)) < 0)
|
&baseline, data.opts.baseline,
|
||||||
|
iterflags, data.pfx, data.pfx)) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
/* Should not have case insensitivity mismatch */
|
/* Should not have case insensitivity mismatch */
|
||||||
@ -1318,8 +1369,19 @@ int git_checkout_index(
|
|||||||
int error;
|
int error;
|
||||||
git_iterator *index_i;
|
git_iterator *index_i;
|
||||||
|
|
||||||
if ((error = git_repository__ensure_not_bare(repo, "checkout index")) < 0)
|
if (!index && !repo) {
|
||||||
return error;
|
giterr_set(GITERR_CHECKOUT,
|
||||||
|
"Must provide either repository or index to checkout");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (index && repo && git_index_owner(index) != repo) {
|
||||||
|
giterr_set(GITERR_CHECKOUT,
|
||||||
|
"Index to checkout does not match repository");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!repo)
|
||||||
|
repo = git_index_owner(index);
|
||||||
|
|
||||||
if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
|
if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
|
||||||
return error;
|
return error;
|
||||||
@ -1343,8 +1405,19 @@ int git_checkout_tree(
|
|||||||
git_tree *tree = NULL;
|
git_tree *tree = NULL;
|
||||||
git_iterator *tree_i = NULL;
|
git_iterator *tree_i = NULL;
|
||||||
|
|
||||||
if ((error = git_repository__ensure_not_bare(repo, "checkout tree")) < 0)
|
if (!treeish && !repo) {
|
||||||
return error;
|
giterr_set(GITERR_CHECKOUT,
|
||||||
|
"Must provide either repository or tree to checkout");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (treeish && repo && git_object_owner(treeish) != repo) {
|
||||||
|
giterr_set(GITERR_CHECKOUT,
|
||||||
|
"Object to checkout does not match repository");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!repo)
|
||||||
|
repo = git_object_owner(treeish);
|
||||||
|
|
||||||
if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) {
|
if (git_object_peel((git_object **)&tree, treeish, GIT_OBJ_TREE) < 0) {
|
||||||
giterr_set(
|
giterr_set(
|
||||||
@ -1369,8 +1442,7 @@ int git_checkout_head(
|
|||||||
git_tree *head = NULL;
|
git_tree *head = NULL;
|
||||||
git_iterator *head_i = NULL;
|
git_iterator *head_i = NULL;
|
||||||
|
|
||||||
if ((error = git_repository__ensure_not_bare(repo, "checkout head")) < 0)
|
assert(repo);
|
||||||
return error;
|
|
||||||
|
|
||||||
if (!(error = checkout_lookup_head_tree(&head, repo)) &&
|
if (!(error = checkout_lookup_head_tree(&head, repo)) &&
|
||||||
!(error = git_iterator_for_tree(&head_i, head, 0, NULL, NULL)))
|
!(error = git_iterator_for_tree(&head_i, head, 0, NULL, NULL)))
|
||||||
|
110
src/clone.c
110
src/clone.c
@ -21,6 +21,7 @@
|
|||||||
#include "fileops.h"
|
#include "fileops.h"
|
||||||
#include "refs.h"
|
#include "refs.h"
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
|
#include "repository.h"
|
||||||
|
|
||||||
static int create_branch(
|
static int create_branch(
|
||||||
git_reference **branch,
|
git_reference **branch,
|
||||||
@ -132,14 +133,14 @@ static int reference_matches_remote_head(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (git_oid_cmp(&head_info->remote_head_oid, &oid) == 0) {
|
if (git_oid__cmp(&head_info->remote_head_oid, &oid) == 0) {
|
||||||
/* Determine the local reference name from the remote tracking one */
|
/* Determine the local reference name from the remote tracking one */
|
||||||
if (git_refspec_transform_l(
|
if (git_refspec_transform_l(
|
||||||
&head_info->branchname,
|
&head_info->branchname,
|
||||||
head_info->refspec,
|
head_info->refspec,
|
||||||
reference_name) < 0)
|
reference_name) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (git_buf_len(&head_info->branchname) > 0) {
|
if (git_buf_len(&head_info->branchname) > 0) {
|
||||||
if (git_buf_sets(
|
if (git_buf_sets(
|
||||||
&head_info->branchname,
|
&head_info->branchname,
|
||||||
@ -187,6 +188,7 @@ static int get_head_callback(git_remote_head *head, void *payload)
|
|||||||
static int update_head_to_remote(git_repository *repo, git_remote *remote)
|
static int update_head_to_remote(git_repository *repo, git_remote *remote)
|
||||||
{
|
{
|
||||||
int retcode = -1;
|
int retcode = -1;
|
||||||
|
git_refspec dummy_spec;
|
||||||
git_remote_head *remote_head;
|
git_remote_head *remote_head;
|
||||||
struct head_info head_info;
|
struct head_info head_info;
|
||||||
git_buf remote_master_name = GIT_BUF_INIT;
|
git_buf remote_master_name = GIT_BUF_INIT;
|
||||||
@ -211,8 +213,13 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote)
|
|||||||
git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid);
|
git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid);
|
||||||
git_buf_init(&head_info.branchname, 16);
|
git_buf_init(&head_info.branchname, 16);
|
||||||
head_info.repo = repo;
|
head_info.repo = repo;
|
||||||
head_info.refspec = git_remote_fetchspec(remote);
|
head_info.refspec = git_remote__matching_refspec(remote, GIT_REFS_HEADS_MASTER_FILE);
|
||||||
head_info.found = 0;
|
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 */
|
/* Determine the remote tracking reference name from the local master */
|
||||||
if (git_refspec_transform_r(
|
if (git_refspec_transform_r(
|
||||||
@ -235,9 +242,8 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Not master. Check all the other refs. */
|
/* Not master. Check all the other refs. */
|
||||||
if (git_reference_foreach(
|
if (git_reference_foreach_name(
|
||||||
repo,
|
repo,
|
||||||
GIT_REF_LISTALL,
|
|
||||||
reference_matches_remote_head,
|
reference_matches_remote_head,
|
||||||
&head_info) < 0)
|
&head_info) < 0)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@ -269,7 +275,7 @@ static int update_head_to_branch(
|
|||||||
int retcode;
|
int retcode;
|
||||||
git_buf remote_branch_name = GIT_BUF_INIT;
|
git_buf remote_branch_name = GIT_BUF_INIT;
|
||||||
git_reference* remote_ref = NULL;
|
git_reference* remote_ref = NULL;
|
||||||
|
|
||||||
assert(options->checkout_branch);
|
assert(options->checkout_branch);
|
||||||
|
|
||||||
if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s",
|
if ((retcode = git_buf_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s",
|
||||||
@ -317,18 +323,24 @@ static int create_and_configure_origin(
|
|||||||
(error = git_remote_set_callbacks(origin, options->remote_callbacks)) < 0)
|
(error = git_remote_set_callbacks(origin, options->remote_callbacks)) < 0)
|
||||||
goto on_error;
|
goto on_error;
|
||||||
|
|
||||||
if (options->fetch_spec &&
|
if (options->fetch_spec) {
|
||||||
(error = git_remote_set_fetchspec(origin, options->fetch_spec)) < 0)
|
git_remote_clear_refspecs(origin);
|
||||||
goto on_error;
|
if ((error = git_remote_add_fetch(origin, options->fetch_spec)) < 0)
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
|
||||||
if (options->push_spec &&
|
if (options->push_spec &&
|
||||||
(error = git_remote_set_pushspec(origin, options->push_spec)) < 0)
|
(error = git_remote_add_push(origin, options->push_spec)) < 0)
|
||||||
goto on_error;
|
goto on_error;
|
||||||
|
|
||||||
if (options->pushurl &&
|
if (options->pushurl &&
|
||||||
(error = git_remote_set_pushurl(origin, options->pushurl)) < 0)
|
(error = git_remote_set_pushurl(origin, options->pushurl)) < 0)
|
||||||
goto on_error;
|
goto on_error;
|
||||||
|
|
||||||
|
if (options->transport_flags == GIT_TRANSPORTFLAGS_NO_CHECK_CERT) {
|
||||||
|
git_remote_check_cert(origin, 0);
|
||||||
|
}
|
||||||
|
|
||||||
if ((error = git_remote_save(origin)) < 0)
|
if ((error = git_remote_save(origin)) < 0)
|
||||||
goto on_error;
|
goto on_error;
|
||||||
|
|
||||||
@ -347,50 +359,48 @@ static int setup_remotes_and_fetch(
|
|||||||
const git_clone_options *options)
|
const git_clone_options *options)
|
||||||
{
|
{
|
||||||
int retcode = GIT_ERROR;
|
int retcode = GIT_ERROR;
|
||||||
git_remote *origin;
|
git_remote *origin = NULL;
|
||||||
|
|
||||||
/* Construct an origin remote */
|
/* Construct an origin remote */
|
||||||
if (!create_and_configure_origin(&origin, repo, url, options)) {
|
if ((retcode = create_and_configure_origin(&origin, repo, url, options)) < 0)
|
||||||
git_remote_set_update_fetchhead(origin, 0);
|
goto on_error;
|
||||||
|
|
||||||
/* Connect and download everything */
|
git_remote_set_update_fetchhead(origin, 0);
|
||||||
if (!git_remote_connect(origin, GIT_DIRECTION_FETCH)) {
|
|
||||||
if (!(retcode = git_remote_download(origin, options->fetch_progress_cb,
|
|
||||||
options->fetch_progress_payload))) {
|
|
||||||
/* Create "origin/foo" branches for all remote branches */
|
|
||||||
if (!git_remote_update_tips(origin)) {
|
|
||||||
/* Point HEAD to the requested branch */
|
|
||||||
if (options->checkout_branch) {
|
|
||||||
if (!update_head_to_branch(repo, options))
|
|
||||||
retcode = 0;
|
|
||||||
}
|
|
||||||
/* Point HEAD to the same ref as the remote's head */
|
|
||||||
else if (!update_head_to_remote(repo, origin)) {
|
|
||||||
retcode = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
git_remote_disconnect(origin);
|
|
||||||
}
|
|
||||||
git_remote_free(origin);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* If the download_tags value has not been specified, then make sure to
|
||||||
|
* download tags as well. It is set here because we want to download tags
|
||||||
|
* on the initial clone, but do not want to persist the value in the
|
||||||
|
* configuration file.
|
||||||
|
*/
|
||||||
|
if (origin->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_AUTO &&
|
||||||
|
((retcode = git_remote_add_fetch(origin, "refs/tags/*:refs/tags/*")) < 0))
|
||||||
|
goto on_error;
|
||||||
|
|
||||||
|
/* Connect and download everything */
|
||||||
|
if ((retcode = git_remote_connect(origin, GIT_DIRECTION_FETCH)) < 0)
|
||||||
|
goto on_error;
|
||||||
|
|
||||||
|
if ((retcode = git_remote_download(origin, options->fetch_progress_cb,
|
||||||
|
options->fetch_progress_payload)) < 0)
|
||||||
|
goto on_error;
|
||||||
|
|
||||||
|
/* Create "origin/foo" branches for all remote branches */
|
||||||
|
if ((retcode = git_remote_update_tips(origin)) < 0)
|
||||||
|
goto on_error;
|
||||||
|
|
||||||
|
/* Point HEAD to the requested branch */
|
||||||
|
if (options->checkout_branch)
|
||||||
|
retcode = update_head_to_branch(repo, options);
|
||||||
|
/* Point HEAD to the same ref as the remote's head */
|
||||||
|
else
|
||||||
|
retcode = update_head_to_remote(repo, origin);
|
||||||
|
|
||||||
|
on_error:
|
||||||
|
git_remote_free(origin);
|
||||||
return retcode;
|
return retcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static bool path_is_okay(const char *path)
|
|
||||||
{
|
|
||||||
/* The path must either not exist, or be an empty directory */
|
|
||||||
if (!git_path_exists(path)) return true;
|
|
||||||
if (!git_path_is_empty_dir(path)) {
|
|
||||||
giterr_set(GITERR_INVALID,
|
|
||||||
"'%s' exists and is not an empty directory", path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool should_checkout(
|
static bool should_checkout(
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
bool is_bare,
|
bool is_bare,
|
||||||
@ -417,7 +427,6 @@ static void normalize_options(git_clone_options *dst, const git_clone_options *s
|
|||||||
|
|
||||||
/* Provide defaults for null pointers */
|
/* Provide defaults for null pointers */
|
||||||
if (!dst->remote_name) dst->remote_name = "origin";
|
if (!dst->remote_name) dst->remote_name = "origin";
|
||||||
if (!dst->remote_autotag) dst->remote_autotag = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_clone(
|
int git_clone(
|
||||||
@ -436,7 +445,10 @@ int git_clone(
|
|||||||
normalize_options(&normOptions, options);
|
normalize_options(&normOptions, options);
|
||||||
GITERR_CHECK_VERSION(&normOptions, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
|
GITERR_CHECK_VERSION(&normOptions, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
|
||||||
|
|
||||||
if (!path_is_okay(local_path)) {
|
/* Only clone to a new directory or an empty directory */
|
||||||
|
if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) {
|
||||||
|
giterr_set(GITERR_INVALID,
|
||||||
|
"'%s' exists and is not an empty directory", local_path);
|
||||||
return GIT_ERROR;
|
return GIT_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
114
src/commit.c
114
src/commit.c
@ -9,6 +9,7 @@
|
|||||||
#include "git2/object.h"
|
#include "git2/object.h"
|
||||||
#include "git2/repository.h"
|
#include "git2/repository.h"
|
||||||
#include "git2/signature.h"
|
#include "git2/signature.h"
|
||||||
|
#include "git2/sys/commit.h"
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "odb.h"
|
#include "odb.h"
|
||||||
@ -30,8 +31,10 @@ static void clear_parents(git_commit *commit)
|
|||||||
git_vector_clear(&commit->parent_ids);
|
git_vector_clear(&commit->parent_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
void git_commit__free(git_commit *commit)
|
void git_commit__free(void *_commit)
|
||||||
{
|
{
|
||||||
|
git_commit *commit = _commit;
|
||||||
|
|
||||||
clear_parents(commit);
|
clear_parents(commit);
|
||||||
git_vector_free(&commit->parent_ids);
|
git_vector_free(&commit->parent_ids);
|
||||||
|
|
||||||
@ -44,16 +47,16 @@ void git_commit__free(git_commit *commit)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int git_commit_create_v(
|
int git_commit_create_v(
|
||||||
git_oid *oid,
|
git_oid *oid,
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const char *update_ref,
|
const char *update_ref,
|
||||||
const git_signature *author,
|
const git_signature *author,
|
||||||
const git_signature *committer,
|
const git_signature *committer,
|
||||||
const char *message_encoding,
|
const char *message_encoding,
|
||||||
const char *message,
|
const char *message,
|
||||||
const git_tree *tree,
|
const git_tree *tree,
|
||||||
int parent_count,
|
int parent_count,
|
||||||
...)
|
...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
int i, res;
|
int i, res;
|
||||||
@ -76,30 +79,28 @@ int git_commit_create_v(
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_commit_create(
|
int git_commit_create_from_oids(
|
||||||
git_oid *oid,
|
git_oid *oid,
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const char *update_ref,
|
const char *update_ref,
|
||||||
const git_signature *author,
|
const git_signature *author,
|
||||||
const git_signature *committer,
|
const git_signature *committer,
|
||||||
const char *message_encoding,
|
const char *message_encoding,
|
||||||
const char *message,
|
const char *message,
|
||||||
const git_tree *tree,
|
const git_oid *tree,
|
||||||
int parent_count,
|
int parent_count,
|
||||||
const git_commit *parents[])
|
const git_oid *parents[])
|
||||||
{
|
{
|
||||||
git_buf commit = GIT_BUF_INIT;
|
git_buf commit = GIT_BUF_INIT;
|
||||||
int i;
|
int i;
|
||||||
git_odb *odb;
|
git_odb *odb;
|
||||||
|
|
||||||
assert(git_object_owner((const git_object *)tree) == repo);
|
assert(oid && repo && tree && parent_count >= 0);
|
||||||
|
|
||||||
git_oid__writebuf(&commit, "tree ", git_object_id((const git_object *)tree));
|
git_oid__writebuf(&commit, "tree ", tree);
|
||||||
|
|
||||||
for (i = 0; i < parent_count; ++i) {
|
for (i = 0; i < parent_count; ++i)
|
||||||
assert(git_object_owner((const git_object *)parents[i]) == repo);
|
git_oid__writebuf(&commit, "parent ", parents[i]);
|
||||||
git_oid__writebuf(&commit, "parent ", git_object_id((const git_object *)parents[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
git_signature__writebuf(&commit, "author ", author);
|
git_signature__writebuf(&commit, "author ", author);
|
||||||
git_signature__writebuf(&commit, "committer ", committer);
|
git_signature__writebuf(&commit, "committer ", committer);
|
||||||
@ -131,10 +132,47 @@ on_error:
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
|
int git_commit_create(
|
||||||
|
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,
|
||||||
|
const git_commit *parents[])
|
||||||
{
|
{
|
||||||
const char *buffer = data;
|
int retval, i;
|
||||||
const char *buffer_end = (const char *)data + len;
|
const git_oid **parent_oids;
|
||||||
|
|
||||||
|
assert(parent_count >= 0);
|
||||||
|
assert(git_object_owner((const git_object *)tree) == repo);
|
||||||
|
|
||||||
|
parent_oids = git__malloc(parent_count * sizeof(git_oid *));
|
||||||
|
GITERR_CHECK_ALLOC(parent_oids);
|
||||||
|
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
git__free((void *)parent_oids);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_commit__parse(void *_commit, git_odb_object *odb_obj)
|
||||||
|
{
|
||||||
|
git_commit *commit = _commit;
|
||||||
|
const char *buffer = git_odb_object_data(odb_obj);
|
||||||
|
const char *buffer_end = buffer + git_odb_object_size(odb_obj);
|
||||||
git_oid parent_id;
|
git_oid parent_id;
|
||||||
|
|
||||||
if (git_vector_init(&commit->parent_ids, 4, NULL) < 0)
|
if (git_vector_init(&commit->parent_ids, 4, NULL) < 0)
|
||||||
@ -206,12 +244,6 @@ bad_buffer:
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_commit__parse(git_commit *commit, git_odb_object *obj)
|
|
||||||
{
|
|
||||||
assert(commit);
|
|
||||||
return git_commit__parse_buffer(commit, obj->raw.data, obj->raw.len);
|
|
||||||
}
|
|
||||||
|
|
||||||
#define GIT_COMMIT_GETTER(_rvalue, _name, _return) \
|
#define GIT_COMMIT_GETTER(_rvalue, _name, _return) \
|
||||||
_rvalue git_commit_##_name(const git_commit *commit) \
|
_rvalue git_commit_##_name(const git_commit *commit) \
|
||||||
{\
|
{\
|
||||||
@ -234,14 +266,16 @@ int git_commit_tree(git_tree **tree_out, const git_commit *commit)
|
|||||||
return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id);
|
return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
const git_oid *git_commit_parent_id(git_commit *commit, unsigned int n)
|
const git_oid *git_commit_parent_id(
|
||||||
|
const git_commit *commit, unsigned int n)
|
||||||
{
|
{
|
||||||
assert(commit);
|
assert(commit);
|
||||||
|
|
||||||
return git_vector_get(&commit->parent_ids, n);
|
return git_vector_get(&commit->parent_ids, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n)
|
int git_commit_parent(
|
||||||
|
git_commit **parent, const git_commit *commit, unsigned int n)
|
||||||
{
|
{
|
||||||
const git_oid *parent_id;
|
const git_oid *parent_id;
|
||||||
assert(commit);
|
assert(commit);
|
||||||
@ -260,7 +294,7 @@ int git_commit_nth_gen_ancestor(
|
|||||||
const git_commit *commit,
|
const git_commit *commit,
|
||||||
unsigned int n)
|
unsigned int n)
|
||||||
{
|
{
|
||||||
git_commit *current, *parent;
|
git_commit *current, *parent = NULL;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
assert(ancestor && commit);
|
assert(ancestor && commit);
|
||||||
|
@ -27,8 +27,7 @@ struct git_commit {
|
|||||||
char *message;
|
char *message;
|
||||||
};
|
};
|
||||||
|
|
||||||
void git_commit__free(git_commit *c);
|
void git_commit__free(void *commit);
|
||||||
int git_commit__parse(git_commit *commit, git_odb_object *obj);
|
int git_commit__parse(void *commit, git_odb_object *obj);
|
||||||
|
|
||||||
int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len);
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -100,12 +100,15 @@ git_commit_list_node *git_commit_list_pop(git_commit_list **stack)
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int commit_quick_parse(git_revwalk *walk, git_commit_list_node *commit, git_rawobj *raw)
|
static int commit_quick_parse(
|
||||||
|
git_revwalk *walk,
|
||||||
|
git_commit_list_node *commit,
|
||||||
|
const uint8_t *buffer,
|
||||||
|
size_t buffer_len)
|
||||||
{
|
{
|
||||||
const size_t parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1;
|
const size_t parent_len = strlen("parent ") + GIT_OID_HEXSZ + 1;
|
||||||
unsigned char *buffer = raw->data;
|
const uint8_t *buffer_end = buffer + buffer_len;
|
||||||
unsigned char *buffer_end = buffer + raw->len;
|
const uint8_t *parents_start, *committer_start;
|
||||||
unsigned char *parents_start, *committer_start;
|
|
||||||
int i, parents = 0;
|
int i, parents = 0;
|
||||||
int commit_time;
|
int commit_time;
|
||||||
|
|
||||||
@ -124,7 +127,7 @@ static int commit_quick_parse(git_revwalk *walk, git_commit_list_node *commit, g
|
|||||||
for (i = 0; i < parents; ++i) {
|
for (i = 0; i < parents; ++i) {
|
||||||
git_oid oid;
|
git_oid oid;
|
||||||
|
|
||||||
if (git_oid_fromstr(&oid, (char *)buffer + strlen("parent ")) < 0)
|
if (git_oid_fromstr(&oid, (const char *)buffer + strlen("parent ")) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
commit->parents[i] = git_revwalk__commit_lookup(walk, &oid);
|
commit->parents[i] = git_revwalk__commit_lookup(walk, &oid);
|
||||||
@ -182,11 +185,14 @@ int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit)
|
|||||||
if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < 0)
|
if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < 0)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
if (obj->raw.type != GIT_OBJ_COMMIT) {
|
if (obj->cached.type != GIT_OBJ_COMMIT) {
|
||||||
giterr_set(GITERR_INVALID, "Object is no commit object");
|
giterr_set(GITERR_INVALID, "Object is no commit object");
|
||||||
error = -1;
|
error = -1;
|
||||||
} else
|
} else
|
||||||
error = commit_quick_parse(walk, commit, &obj->raw);
|
error = commit_quick_parse(
|
||||||
|
walk, commit,
|
||||||
|
(const uint8_t *)git_odb_object_data(obj),
|
||||||
|
git_odb_object_size(obj));
|
||||||
|
|
||||||
git_odb_object_free(obj);
|
git_odb_object_free(obj);
|
||||||
return error;
|
return error;
|
||||||
|
155
src/config.c
155
src/config.c
@ -9,6 +9,7 @@
|
|||||||
#include "fileops.h"
|
#include "fileops.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "git2/config.h"
|
#include "git2/config.h"
|
||||||
|
#include "git2/sys/config.h"
|
||||||
#include "vector.h"
|
#include "vector.h"
|
||||||
#include "buf_text.h"
|
#include "buf_text.h"
|
||||||
#include "config_file.h"
|
#include "config_file.h"
|
||||||
@ -22,7 +23,7 @@ typedef struct {
|
|||||||
git_refcount rc;
|
git_refcount rc;
|
||||||
|
|
||||||
git_config_backend *file;
|
git_config_backend *file;
|
||||||
unsigned int level;
|
git_config_level_t level;
|
||||||
} file_internal;
|
} file_internal;
|
||||||
|
|
||||||
static void file_internal_free(file_internal *internal)
|
static void file_internal_free(file_internal *internal)
|
||||||
@ -39,12 +40,14 @@ static void config_free(git_config *cfg)
|
|||||||
size_t i;
|
size_t i;
|
||||||
file_internal *internal;
|
file_internal *internal;
|
||||||
|
|
||||||
for(i = 0; i < cfg->files.length; ++i){
|
for (i = 0; i < cfg->files.length; ++i) {
|
||||||
internal = git_vector_get(&cfg->files, i);
|
internal = git_vector_get(&cfg->files, i);
|
||||||
GIT_REFCOUNT_DEC(internal, file_internal_free);
|
GIT_REFCOUNT_DEC(internal, file_internal_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
git_vector_free(&cfg->files);
|
git_vector_free(&cfg->files);
|
||||||
|
|
||||||
|
git__memzero(cfg, sizeof(*cfg));
|
||||||
git__free(cfg);
|
git__free(cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,17 +89,19 @@ int git_config_new(git_config **out)
|
|||||||
int git_config_add_file_ondisk(
|
int git_config_add_file_ondisk(
|
||||||
git_config *cfg,
|
git_config *cfg,
|
||||||
const char *path,
|
const char *path,
|
||||||
unsigned int level,
|
git_config_level_t level,
|
||||||
int force)
|
int force)
|
||||||
{
|
{
|
||||||
git_config_backend *file = NULL;
|
git_config_backend *file = NULL;
|
||||||
|
struct stat st;
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
assert(cfg && path);
|
assert(cfg && path);
|
||||||
|
|
||||||
if (!git_path_isfile(path)) {
|
res = p_stat(path, &st);
|
||||||
giterr_set(GITERR_CONFIG, "Cannot find config file '%s'", path);
|
if (res < 0 && errno != ENOENT) {
|
||||||
return GIT_ENOTFOUND;
|
giterr_set(GITERR_CONFIG, "Error stat'ing config file '%s'", path);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (git_config_file__ondisk(&file, path) < 0)
|
if (git_config_file__ondisk(&file, path) < 0)
|
||||||
@ -135,11 +140,11 @@ int git_config_open_ondisk(git_config **out, const char *path)
|
|||||||
static int find_internal_file_by_level(
|
static int find_internal_file_by_level(
|
||||||
file_internal **internal_out,
|
file_internal **internal_out,
|
||||||
const git_config *cfg,
|
const git_config *cfg,
|
||||||
int level)
|
git_config_level_t level)
|
||||||
{
|
{
|
||||||
int pos = -1;
|
int pos = -1;
|
||||||
file_internal *internal;
|
file_internal *internal;
|
||||||
unsigned int i;
|
size_t i;
|
||||||
|
|
||||||
/* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config file
|
/* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config file
|
||||||
* which has the highest level. As config files are stored in a vector
|
* which has the highest level. As config files are stored in a vector
|
||||||
@ -150,14 +155,14 @@ static int find_internal_file_by_level(
|
|||||||
pos = 0;
|
pos = 0;
|
||||||
} else {
|
} else {
|
||||||
git_vector_foreach(&cfg->files, i, internal) {
|
git_vector_foreach(&cfg->files, i, internal) {
|
||||||
if (internal->level == (unsigned int)level)
|
if (internal->level == level)
|
||||||
pos = i;
|
pos = (int)i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pos == -1) {
|
if (pos == -1) {
|
||||||
giterr_set(GITERR_CONFIG,
|
giterr_set(GITERR_CONFIG,
|
||||||
"No config file exists for the given level '%i'", level);
|
"No config file exists for the given level '%i'", (int)level);
|
||||||
return GIT_ENOTFOUND;
|
return GIT_ENOTFOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,21 +177,21 @@ static int duplicate_level(void **old_raw, void *new_raw)
|
|||||||
|
|
||||||
GIT_UNUSED(new_raw);
|
GIT_UNUSED(new_raw);
|
||||||
|
|
||||||
giterr_set(GITERR_CONFIG, "A file with the same level (%i) has already been added to the config", (*old)->level);
|
giterr_set(GITERR_CONFIG, "A file with the same level (%i) has already been added to the config", (int)(*old)->level);
|
||||||
return GIT_EEXISTS;
|
return GIT_EEXISTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void try_remove_existing_file_internal(
|
static void try_remove_existing_file_internal(
|
||||||
git_config *cfg,
|
git_config *cfg,
|
||||||
unsigned int level)
|
git_config_level_t level)
|
||||||
{
|
{
|
||||||
int pos = -1;
|
int pos = -1;
|
||||||
file_internal *internal;
|
file_internal *internal;
|
||||||
unsigned int i;
|
size_t i;
|
||||||
|
|
||||||
git_vector_foreach(&cfg->files, i, internal) {
|
git_vector_foreach(&cfg->files, i, internal) {
|
||||||
if (internal->level == level)
|
if (internal->level == level)
|
||||||
pos = i;
|
pos = (int)i;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pos == -1)
|
if (pos == -1)
|
||||||
@ -203,7 +208,7 @@ static void try_remove_existing_file_internal(
|
|||||||
static int git_config__add_internal(
|
static int git_config__add_internal(
|
||||||
git_config *cfg,
|
git_config *cfg,
|
||||||
file_internal *internal,
|
file_internal *internal,
|
||||||
unsigned int level,
|
git_config_level_t level,
|
||||||
int force)
|
int force)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
@ -224,10 +229,18 @@ static int git_config__add_internal(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int git_config_open_global(git_config **cfg_out, git_config *cfg)
|
||||||
|
{
|
||||||
|
if (!git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_XDG))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_GLOBAL);
|
||||||
|
}
|
||||||
|
|
||||||
int git_config_open_level(
|
int git_config_open_level(
|
||||||
git_config **cfg_out,
|
git_config **cfg_out,
|
||||||
const git_config *cfg_parent,
|
const git_config *cfg_parent,
|
||||||
unsigned int level)
|
git_config_level_t level)
|
||||||
{
|
{
|
||||||
git_config *cfg;
|
git_config *cfg;
|
||||||
file_internal *internal;
|
file_internal *internal;
|
||||||
@ -252,7 +265,7 @@ int git_config_open_level(
|
|||||||
int git_config_add_backend(
|
int git_config_add_backend(
|
||||||
git_config *cfg,
|
git_config *cfg,
|
||||||
git_config_backend *file,
|
git_config_backend *file,
|
||||||
unsigned int level,
|
git_config_level_t level,
|
||||||
int force)
|
int force)
|
||||||
{
|
{
|
||||||
file_internal *internal;
|
file_internal *internal;
|
||||||
@ -292,6 +305,9 @@ int git_config_refresh(git_config *cfg)
|
|||||||
error = file->refresh(file);
|
error = file->refresh(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL)
|
||||||
|
git_repository__cvar_cache_clear(GIT_REFCOUNT_OWNER(cfg));
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,21 +341,30 @@ int git_config_foreach_match(
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**************
|
||||||
|
* Setters
|
||||||
|
**************/
|
||||||
|
|
||||||
|
static int config_error_nofiles(const char *name)
|
||||||
|
{
|
||||||
|
giterr_set(GITERR_CONFIG,
|
||||||
|
"Cannot set value for '%s' when no config files exist", name);
|
||||||
|
return GIT_ENOTFOUND;
|
||||||
|
}
|
||||||
|
|
||||||
int git_config_delete_entry(git_config *cfg, const char *name)
|
int git_config_delete_entry(git_config *cfg, const char *name)
|
||||||
{
|
{
|
||||||
git_config_backend *file;
|
git_config_backend *file;
|
||||||
file_internal *internal;
|
file_internal *internal;
|
||||||
|
|
||||||
internal = git_vector_get(&cfg->files, 0);
|
internal = git_vector_get(&cfg->files, 0);
|
||||||
|
if (!internal || !internal->file)
|
||||||
|
return config_error_nofiles(name);
|
||||||
file = internal->file;
|
file = internal->file;
|
||||||
|
|
||||||
return file->del(file, name);
|
return file->del(file, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**************
|
|
||||||
* Setters
|
|
||||||
**************/
|
|
||||||
|
|
||||||
int git_config_set_int64(git_config *cfg, const char *name, int64_t value)
|
int git_config_set_int64(git_config *cfg, const char *name, int64_t value)
|
||||||
{
|
{
|
||||||
char str_value[32]; /* All numbers should fit in here */
|
char str_value[32]; /* All numbers should fit in here */
|
||||||
@ -359,6 +384,7 @@ int git_config_set_bool(git_config *cfg, const char *name, int value)
|
|||||||
|
|
||||||
int git_config_set_string(git_config *cfg, const char *name, const char *value)
|
int git_config_set_string(git_config *cfg, const char *name, const char *value)
|
||||||
{
|
{
|
||||||
|
int error;
|
||||||
git_config_backend *file;
|
git_config_backend *file;
|
||||||
file_internal *internal;
|
file_internal *internal;
|
||||||
|
|
||||||
@ -368,9 +394,16 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal = git_vector_get(&cfg->files, 0);
|
internal = git_vector_get(&cfg->files, 0);
|
||||||
|
if (!internal || !internal->file)
|
||||||
|
return config_error_nofiles(name);
|
||||||
file = internal->file;
|
file = internal->file;
|
||||||
|
|
||||||
return file->set(file, name, value);
|
error = file->set(file, name, value);
|
||||||
|
|
||||||
|
if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL)
|
||||||
|
git_repository__cvar_cache_clear(GIT_REFCOUNT_OWNER(cfg));
|
||||||
|
|
||||||
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/***********
|
/***********
|
||||||
@ -426,19 +459,28 @@ static int get_string_at_file(const char **out, const git_config_backend *file,
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int config_error_notfound(const char *name)
|
||||||
|
{
|
||||||
|
giterr_set(GITERR_CONFIG, "Config value '%s' was not found", name);
|
||||||
|
return GIT_ENOTFOUND;
|
||||||
|
}
|
||||||
|
|
||||||
static int get_string(const char **out, const git_config *cfg, const char *name)
|
static int get_string(const char **out, const git_config *cfg, const char *name)
|
||||||
{
|
{
|
||||||
file_internal *internal;
|
file_internal *internal;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
int res;
|
||||||
|
|
||||||
git_vector_foreach(&cfg->files, i, internal) {
|
git_vector_foreach(&cfg->files, i, internal) {
|
||||||
int res = get_string_at_file(out, internal->file, name);
|
if (!internal || !internal->file)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
res = get_string_at_file(out, internal->file, name);
|
||||||
if (res != GIT_ENOTFOUND)
|
if (res != GIT_ENOTFOUND)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GIT_ENOTFOUND;
|
return config_error_notfound(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_config_get_bool(int *out, const git_config *cfg, const char *name)
|
int git_config_get_bool(int *out, const git_config *cfg, const char *name)
|
||||||
@ -468,21 +510,27 @@ int git_config_get_entry(const git_config_entry **out, const git_config *cfg, co
|
|||||||
{
|
{
|
||||||
file_internal *internal;
|
file_internal *internal;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
git_config_backend *file;
|
||||||
|
int ret;
|
||||||
|
|
||||||
*out = NULL;
|
*out = NULL;
|
||||||
|
|
||||||
git_vector_foreach(&cfg->files, i, internal) {
|
git_vector_foreach(&cfg->files, i, internal) {
|
||||||
git_config_backend *file = internal->file;
|
if (!internal || !internal->file)
|
||||||
int ret = file->get(file, name, out);
|
continue;
|
||||||
|
file = internal->file;
|
||||||
|
|
||||||
|
ret = file->get(file, name, out);
|
||||||
if (ret != GIT_ENOTFOUND)
|
if (ret != GIT_ENOTFOUND)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GIT_ENOTFOUND;
|
return config_error_notfound(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_config_get_multivar(const git_config *cfg, const char *name, const char *regexp,
|
int git_config_get_multivar(
|
||||||
git_config_foreach_cb cb, void *payload)
|
const git_config *cfg, const char *name, const char *regexp,
|
||||||
|
git_config_foreach_cb cb, void *payload)
|
||||||
{
|
{
|
||||||
file_internal *internal;
|
file_internal *internal;
|
||||||
git_config_backend *file;
|
git_config_backend *file;
|
||||||
@ -495,13 +543,16 @@ int git_config_get_multivar(const git_config *cfg, const char *name, const char
|
|||||||
*/
|
*/
|
||||||
for (i = cfg->files.length; i > 0; --i) {
|
for (i = cfg->files.length; i > 0; --i) {
|
||||||
internal = git_vector_get(&cfg->files, i - 1);
|
internal = git_vector_get(&cfg->files, i - 1);
|
||||||
|
if (!internal || !internal->file)
|
||||||
|
continue;
|
||||||
file = internal->file;
|
file = internal->file;
|
||||||
|
|
||||||
ret = file->get_multivar(file, name, regexp, cb, payload);
|
ret = file->get_multivar(file, name, regexp, cb, payload);
|
||||||
if (ret < 0 && ret != GIT_ENOTFOUND)
|
if (ret < 0 && ret != GIT_ENOTFOUND)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return (ret == GIT_ENOTFOUND) ? config_error_notfound(name) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
|
int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
|
||||||
@ -510,6 +561,8 @@ int git_config_set_multivar(git_config *cfg, const char *name, const char *regex
|
|||||||
file_internal *internal;
|
file_internal *internal;
|
||||||
|
|
||||||
internal = git_vector_get(&cfg->files, 0);
|
internal = git_vector_get(&cfg->files, 0);
|
||||||
|
if (!internal || !internal->file)
|
||||||
|
return config_error_nofiles(name);
|
||||||
file = internal->file;
|
file = internal->file;
|
||||||
|
|
||||||
return file->set_multivar(file, name, regexp, value);
|
return file->set_multivar(file, name, regexp, value);
|
||||||
@ -570,17 +623,46 @@ int git_config_find_system(char *system_config_path, size_t length)
|
|||||||
system_config_path, length, git_config_find_system_r);
|
system_config_path, length, git_config_find_system_r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* no paths, so give up */
|
||||||
|
if (git_buf_len(paths) == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
start = git_buf_cstr(paths);
|
||||||
|
sep = strchr(start, GIT_PATH_LIST_SEPARATOR);
|
||||||
|
|
||||||
|
if (sep)
|
||||||
|
len = sep - start;
|
||||||
|
else
|
||||||
|
len = paths->size;
|
||||||
|
|
||||||
|
if (git_buf_set(buf, start, len) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return git_buf_joinpath(buf, buf->ptr, GIT_CONFIG_FILENAME_GLOBAL);
|
||||||
|
}
|
||||||
|
|
||||||
int git_config_open_default(git_config **out)
|
int git_config_open_default(git_config **out)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
git_config *cfg = NULL;
|
git_config *cfg = NULL;
|
||||||
git_buf buf = GIT_BUF_INIT;
|
git_buf buf = GIT_BUF_INIT;
|
||||||
|
|
||||||
error = git_config_new(&cfg);
|
if ((error = git_config_new(&cfg)) < 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
if (!error && !git_config_find_global_r(&buf))
|
if (!git_config_find_global_r(&buf) || !git_config__global_location(&buf)) {
|
||||||
error = git_config_add_file_ondisk(cfg, buf.ptr,
|
error = git_config_add_file_ondisk(cfg, buf.ptr,
|
||||||
GIT_CONFIG_LEVEL_GLOBAL, 0);
|
GIT_CONFIG_LEVEL_GLOBAL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (!error && !git_config_find_xdg_r(&buf))
|
if (!error && !git_config_find_xdg_r(&buf))
|
||||||
error = git_config_add_file_ondisk(cfg, buf.ptr,
|
error = git_config_add_file_ondisk(cfg, buf.ptr,
|
||||||
@ -592,7 +674,7 @@ int git_config_open_default(git_config **out)
|
|||||||
|
|
||||||
git_buf_free(&buf);
|
git_buf_free(&buf);
|
||||||
|
|
||||||
if (error && cfg) {
|
if (error) {
|
||||||
git_config_free(cfg);
|
git_config_free(cfg);
|
||||||
cfg = NULL;
|
cfg = NULL;
|
||||||
}
|
}
|
||||||
@ -605,6 +687,7 @@ int git_config_open_default(git_config **out)
|
|||||||
/***********
|
/***********
|
||||||
* Parsers
|
* Parsers
|
||||||
***********/
|
***********/
|
||||||
|
|
||||||
int git_config_lookup_map_value(
|
int git_config_lookup_map_value(
|
||||||
int *out,
|
int *out,
|
||||||
const git_cvar_map *maps,
|
const git_cvar_map *maps,
|
||||||
|
@ -28,6 +28,9 @@ 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_xdg_r(git_buf *system_config_path);
|
||||||
extern int git_config_find_system_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(
|
extern int git_config_rename_section(
|
||||||
git_repository *repo,
|
git_repository *repo,
|
||||||
const char *old_section_name, /* eg "branch.dummy" */
|
const char *old_section_name, /* eg "branch.dummy" */
|
||||||
|
@ -26,7 +26,7 @@ struct map_data {
|
|||||||
* files that have the text property set. Alternatives are lf, crlf
|
* files that have the text property set. Alternatives are lf, crlf
|
||||||
* and native, which uses the platform's native line ending. The default
|
* and native, which uses the platform's native line ending. The default
|
||||||
* value is native. See gitattributes(5) for more information on
|
* value is native. See gitattributes(5) for more information on
|
||||||
* end-of-line conversion.
|
* end-of-line conversion.
|
||||||
*/
|
*/
|
||||||
static git_cvar_map _cvar_map_eol[] = {
|
static git_cvar_map _cvar_map_eol[] = {
|
||||||
{GIT_CVAR_FALSE, NULL, GIT_EOL_UNSET},
|
{GIT_CVAR_FALSE, NULL, GIT_EOL_UNSET},
|
||||||
@ -37,7 +37,7 @@ static git_cvar_map _cvar_map_eol[] = {
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* core.autocrlf
|
* core.autocrlf
|
||||||
* Setting this variable to "true" is almost the same as setting
|
* Setting this variable to "true" is almost the same as setting
|
||||||
* the text attribute to "auto" on all files except that text files are
|
* the text attribute to "auto" on all files except that text files are
|
||||||
* not guaranteed to be normalized: files that contain CRLF in the
|
* not guaranteed to be normalized: files that contain CRLF in the
|
||||||
* repository will not be touched. Use this setting if you want to have
|
* repository will not be touched. Use this setting if you want to have
|
||||||
@ -51,9 +51,22 @@ static git_cvar_map _cvar_map_autocrlf[] = {
|
|||||||
{GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT}
|
{GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic map for integer values
|
||||||
|
*/
|
||||||
|
static git_cvar_map _cvar_map_int[] = {
|
||||||
|
{GIT_CVAR_INT32, NULL, 0},
|
||||||
|
};
|
||||||
|
|
||||||
static struct map_data _cvar_maps[] = {
|
static struct map_data _cvar_maps[] = {
|
||||||
{"core.autocrlf", _cvar_map_autocrlf, ARRAY_SIZE(_cvar_map_autocrlf), GIT_AUTO_CRLF_DEFAULT},
|
{"core.autocrlf", _cvar_map_autocrlf, ARRAY_SIZE(_cvar_map_autocrlf), GIT_AUTO_CRLF_DEFAULT},
|
||||||
{"core.eol", _cvar_map_eol, ARRAY_SIZE(_cvar_map_eol), GIT_EOL_DEFAULT}
|
{"core.eol", _cvar_map_eol, ARRAY_SIZE(_cvar_map_eol), GIT_EOL_DEFAULT},
|
||||||
|
{"core.symlinks", NULL, 0, GIT_SYMLINKS_DEFAULT },
|
||||||
|
{"core.ignorecase", NULL, 0, GIT_IGNORECASE_DEFAULT },
|
||||||
|
{"core.filemode", NULL, 0, GIT_FILEMODE_DEFAULT },
|
||||||
|
{"core.ignorestat", NULL, 0, GIT_IGNORESTAT_DEFAULT },
|
||||||
|
{"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT },
|
||||||
|
{"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_DEFAULT },
|
||||||
};
|
};
|
||||||
|
|
||||||
int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
|
int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
|
||||||
@ -69,12 +82,16 @@ int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
|
|||||||
if (error < 0)
|
if (error < 0)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
error = git_config_get_mapped(out,
|
if (data->maps)
|
||||||
config, data->cvar_name, data->maps, data->map_count);
|
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)
|
if (error == GIT_ENOTFOUND) {
|
||||||
|
giterr_clear();
|
||||||
*out = data->default_value;
|
*out = data->default_value;
|
||||||
|
}
|
||||||
else if (error < 0)
|
else if (error < 0)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
#include "buf_text.h"
|
#include "buf_text.h"
|
||||||
#include "git2/config.h"
|
#include "git2/config.h"
|
||||||
|
#include "git2/sys/config.h"
|
||||||
#include "git2/types.h"
|
#include "git2/types.h"
|
||||||
#include "strmap.h"
|
#include "strmap.h"
|
||||||
|
|
||||||
@ -80,10 +81,10 @@ typedef struct {
|
|||||||
time_t file_mtime;
|
time_t file_mtime;
|
||||||
size_t file_size;
|
size_t file_size;
|
||||||
|
|
||||||
unsigned int level;
|
git_config_level_t level;
|
||||||
} diskfile_backend;
|
} diskfile_backend;
|
||||||
|
|
||||||
static int config_parse(diskfile_backend *cfg_file, unsigned int level);
|
static int config_parse(diskfile_backend *cfg_file, git_config_level_t level);
|
||||||
static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value);
|
static int parse_variable(diskfile_backend *cfg, char **var_name, char **var_value);
|
||||||
static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
|
static int config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
|
||||||
static char *escape_value(const char *ptr);
|
static char *escape_value(const char *ptr);
|
||||||
@ -180,7 +181,7 @@ static void free_vars(git_strmap *values)
|
|||||||
git_strmap_free(values);
|
git_strmap_free(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int config_open(git_config_backend *cfg, unsigned int level)
|
static int config_open(git_config_backend *cfg, git_config_level_t level)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
diskfile_backend *b = (diskfile_backend *)cfg;
|
diskfile_backend *b = (diskfile_backend *)cfg;
|
||||||
@ -295,7 +296,7 @@ cleanup:
|
|||||||
|
|
||||||
static int config_set(git_config_backend *cfg, const char *name, const char *value)
|
static int config_set(git_config_backend *cfg, const char *name, const char *value)
|
||||||
{
|
{
|
||||||
cvar_t *var = NULL, *old_var;
|
cvar_t *var = NULL, *old_var = NULL;
|
||||||
diskfile_backend *b = (diskfile_backend *)cfg;
|
diskfile_backend *b = (diskfile_backend *)cfg;
|
||||||
char *key, *esc_value = NULL;
|
char *key, *esc_value = NULL;
|
||||||
khiter_t pos;
|
khiter_t pos;
|
||||||
@ -481,8 +482,10 @@ static int config_set_multivar(
|
|||||||
|
|
||||||
pos = git_strmap_lookup_index(b->values, key);
|
pos = git_strmap_lookup_index(b->values, key);
|
||||||
if (!git_strmap_valid_index(b->values, pos)) {
|
if (!git_strmap_valid_index(b->values, pos)) {
|
||||||
|
/* If we don't have it, behave like a normal set */
|
||||||
|
result = config_set(cfg, name, value);
|
||||||
git__free(key);
|
git__free(key);
|
||||||
return GIT_ENOTFOUND;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
var = git_strmap_value_at(b->values, pos);
|
var = git_strmap_value_at(b->values, pos);
|
||||||
@ -962,7 +965,7 @@ static int strip_comments(char *line, int in_quotes)
|
|||||||
return quote_count;
|
return quote_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int config_parse(diskfile_backend *cfg_file, unsigned int level)
|
static int config_parse(diskfile_backend *cfg_file, git_config_level_t level)
|
||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
char *current_section = NULL;
|
char *current_section = NULL;
|
||||||
|
@ -5,14 +5,16 @@
|
|||||||
* a Linking Exception. For full terms see the included COPYING file.
|
* a Linking Exception. For full terms see the included COPYING file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "git2/attr.h"
|
||||||
|
#include "git2/blob.h"
|
||||||
|
#include "git2/index.h"
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "fileops.h"
|
#include "fileops.h"
|
||||||
#include "hash.h"
|
#include "hash.h"
|
||||||
#include "filter.h"
|
#include "filter.h"
|
||||||
#include "buf_text.h"
|
#include "buf_text.h"
|
||||||
#include "repository.h"
|
#include "repository.h"
|
||||||
#include "git2/attr.h"
|
|
||||||
#include "git2/blob.h"
|
|
||||||
|
|
||||||
struct crlf_attrs {
|
struct crlf_attrs {
|
||||||
int crlf_action;
|
int crlf_action;
|
||||||
|
@ -823,8 +823,8 @@ static void pending_number(struct tm *tm, int *num)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static git_time_t approxidate_str(const char *date,
|
static git_time_t approxidate_str(const char *date,
|
||||||
const struct timeval *tv,
|
const struct timeval *tv,
|
||||||
int *error_ret)
|
int *error_ret)
|
||||||
{
|
{
|
||||||
int number = 0;
|
int number = 0;
|
||||||
int touched = 0;
|
int touched = 0;
|
||||||
@ -866,7 +866,7 @@ int git__date_parse(git_time_t *out, const char *date)
|
|||||||
int offset, error_ret=0;
|
int offset, error_ret=0;
|
||||||
|
|
||||||
if (!parse_date_basic(date, ×tamp, &offset)) {
|
if (!parse_date_basic(date, ×tamp, &offset)) {
|
||||||
*out = timestamp;
|
*out = timestamp;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +168,7 @@ git_delta_create_index(const void *buf, unsigned long bufsize)
|
|||||||
memset(hash, 0, hsize * sizeof(*hash));
|
memset(hash, 0, hsize * sizeof(*hash));
|
||||||
|
|
||||||
/* allocate an array to count hash entries */
|
/* allocate an array to count hash entries */
|
||||||
hash_count = calloc(hsize, sizeof(*hash_count));
|
hash_count = git__calloc(hsize, sizeof(*hash_count));
|
||||||
if (!hash_count) {
|
if (!hash_count) {
|
||||||
git__free(index);
|
git__free(index);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
994
src/diff.c
994
src/diff.c
File diff suppressed because it is too large
Load Diff
36
src/diff.h
36
src/diff.h
@ -26,17 +26,31 @@ enum {
|
|||||||
GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */
|
GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */
|
||||||
GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */
|
GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */
|
||||||
GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
|
GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
|
||||||
|
GIT_DIFFCAPS_TRUST_NANOSECS = (1 << 5), /* use stat time nanoseconds */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define DIFF_FLAGS_KNOWN_BINARY (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)
|
||||||
|
#define DIFF_FLAGS_NOT_BINARY (GIT_DIFF_FLAG_NOT_BINARY|GIT_DIFF_FLAG__NO_DATA)
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
GIT_DIFF_FLAG__FREE_PATH = (1 << 7), /* `path` is allocated memory */
|
GIT_DIFF_FLAG__FREE_PATH = (1 << 7), /* `path` is allocated memory */
|
||||||
GIT_DIFF_FLAG__FREE_DATA = (1 << 8), /* internal file data is allocated */
|
GIT_DIFF_FLAG__FREE_DATA = (1 << 8), /* internal file data is allocated */
|
||||||
GIT_DIFF_FLAG__UNMAP_DATA = (1 << 9), /* internal file data is mmap'ed */
|
GIT_DIFF_FLAG__UNMAP_DATA = (1 << 9), /* internal file data is mmap'ed */
|
||||||
GIT_DIFF_FLAG__NO_DATA = (1 << 10), /* file data should not be loaded */
|
GIT_DIFF_FLAG__NO_DATA = (1 << 10), /* file data should not be loaded */
|
||||||
GIT_DIFF_FLAG__TO_DELETE = (1 << 11), /* delete entry during rename det. */
|
GIT_DIFF_FLAG__FREE_BLOB = (1 << 11), /* release the blob when done */
|
||||||
GIT_DIFF_FLAG__TO_SPLIT = (1 << 12), /* split entry during rename det. */
|
GIT_DIFF_FLAG__LOADED = (1 << 12), /* file data has been loaded */
|
||||||
|
|
||||||
|
GIT_DIFF_FLAG__TO_DELETE = (1 << 16), /* delete entry during rename det. */
|
||||||
|
GIT_DIFF_FLAG__TO_SPLIT = (1 << 17), /* split entry during rename det. */
|
||||||
|
GIT_DIFF_FLAG__IS_RENAME_TARGET = (1 << 18),
|
||||||
|
GIT_DIFF_FLAG__IS_RENAME_SOURCE = (1 << 19),
|
||||||
|
GIT_DIFF_FLAG__HAS_SELF_SIMILARITY = (1 << 20),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define GIT_DIFF_FLAG__CLEAR_INTERNAL(F) (F) = ((F) & 0x00FFFF)
|
||||||
|
|
||||||
|
#define GIT_DIFF__VERBOSE (1 << 30)
|
||||||
|
|
||||||
struct git_diff_list {
|
struct git_diff_list {
|
||||||
git_refcount rc;
|
git_refcount rc;
|
||||||
git_repository *repo;
|
git_repository *repo;
|
||||||
@ -60,6 +74,7 @@ extern void git_diff__cleanup_modes(
|
|||||||
extern void git_diff_list_addref(git_diff_list *diff);
|
extern void git_diff_list_addref(git_diff_list *diff);
|
||||||
|
|
||||||
extern int git_diff_delta__cmp(const void *a, const void *b);
|
extern int git_diff_delta__cmp(const void *a, const void *b);
|
||||||
|
extern int git_diff_delta__casecmp(const void *a, const void *b);
|
||||||
|
|
||||||
extern bool git_diff_delta__should_skip(
|
extern bool git_diff_delta__should_skip(
|
||||||
const git_diff_options *opts, const git_diff_delta *delta);
|
const git_diff_options *opts, const git_diff_delta *delta);
|
||||||
@ -74,5 +89,22 @@ extern int git_diff__from_iterators(
|
|||||||
git_iterator *new_iter,
|
git_iterator *new_iter,
|
||||||
const git_diff_options *opts);
|
const git_diff_options *opts);
|
||||||
|
|
||||||
|
extern int git_diff__paired_foreach(
|
||||||
|
git_diff_list *idx2head,
|
||||||
|
git_diff_list *wd2idx,
|
||||||
|
int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
|
||||||
|
void *payload);
|
||||||
|
|
||||||
|
extern int git_diff_find_similar__hashsig_for_file(
|
||||||
|
void **out, const git_diff_file *f, const char *path, void *p);
|
||||||
|
|
||||||
|
extern int git_diff_find_similar__hashsig_for_buf(
|
||||||
|
void **out, const git_diff_file *f, const char *buf, size_t len, void *p);
|
||||||
|
|
||||||
|
extern void git_diff_find_similar__hashsig_free(void *sig, void *payload);
|
||||||
|
|
||||||
|
extern int git_diff_find_similar__calc_similarity(
|
||||||
|
int *score, void *siga, void *sigb, void *payload);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
405
src/diff_driver.c
Normal file
405
src/diff_driver.c
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
/*
|
||||||
|
* 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/attr.h"
|
||||||
|
|
||||||
|
#include "diff.h"
|
||||||
|
#include "diff_patch.h"
|
||||||
|
#include "diff_driver.h"
|
||||||
|
#include "strmap.h"
|
||||||
|
#include "map.h"
|
||||||
|
#include "buf_text.h"
|
||||||
|
#include "repository.h"
|
||||||
|
|
||||||
|
GIT__USE_STRMAP;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DIFF_DRIVER_AUTO = 0,
|
||||||
|
DIFF_DRIVER_BINARY = 1,
|
||||||
|
DIFF_DRIVER_TEXT = 2,
|
||||||
|
DIFF_DRIVER_PATTERNLIST = 3,
|
||||||
|
} git_diff_driver_t;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
DIFF_CONTEXT_FIND_NORMAL = 0,
|
||||||
|
DIFF_CONTEXT_FIND_ICASE = (1 << 0),
|
||||||
|
DIFF_CONTEXT_FIND_EXT = (1 << 1),
|
||||||
|
};
|
||||||
|
|
||||||
|
/* data for finding function context for a given file type */
|
||||||
|
struct git_diff_driver {
|
||||||
|
git_diff_driver_t type;
|
||||||
|
uint32_t binary_flags;
|
||||||
|
uint32_t other_flags;
|
||||||
|
git_array_t(regex_t) fn_patterns;
|
||||||
|
regex_t word_pattern;
|
||||||
|
char name[GIT_FLEX_ARRAY];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct git_diff_driver_registry {
|
||||||
|
git_strmap *drivers;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define FORCE_DIFFABLE (GIT_DIFF_FORCE_TEXT | GIT_DIFF_FORCE_BINARY)
|
||||||
|
|
||||||
|
static git_diff_driver global_drivers[3] = {
|
||||||
|
{ DIFF_DRIVER_AUTO, 0, 0, },
|
||||||
|
{ DIFF_DRIVER_BINARY, GIT_DIFF_FORCE_BINARY, 0 },
|
||||||
|
{ DIFF_DRIVER_TEXT, GIT_DIFF_FORCE_TEXT, 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
git_diff_driver_registry *git_diff_driver_registry_new()
|
||||||
|
{
|
||||||
|
git_diff_driver_registry *reg =
|
||||||
|
git__calloc(1, sizeof(git_diff_driver_registry));
|
||||||
|
if (!reg)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if ((reg->drivers = git_strmap_alloc()) == NULL) {
|
||||||
|
git_diff_driver_registry_free(reg);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_diff_driver_registry_free(git_diff_driver_registry *reg)
|
||||||
|
{
|
||||||
|
git_diff_driver *drv;
|
||||||
|
|
||||||
|
if (!reg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
git_strmap_foreach_value(reg->drivers, drv, git_diff_driver_free(drv));
|
||||||
|
git_strmap_free(reg->drivers);
|
||||||
|
git__free(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_driver_add_funcname(
|
||||||
|
git_diff_driver *drv, const char *name, int regex_flags)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
regex_t re, *re_ptr;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
re_ptr = git_array_alloc(drv->fn_patterns);
|
||||||
|
GITERR_CHECK_ALLOC(re_ptr);
|
||||||
|
|
||||||
|
memcpy(re_ptr, &re, sizeof(re));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_driver_xfuncname(const git_config_entry *entry, void *payload)
|
||||||
|
{
|
||||||
|
return diff_driver_add_funcname(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static git_diff_driver_registry *git_repository_driver_registry(
|
||||||
|
git_repository *repo)
|
||||||
|
{
|
||||||
|
if (!repo->diff_drivers) {
|
||||||
|
git_diff_driver_registry *reg = git_diff_driver_registry_new();
|
||||||
|
reg = git__compare_and_swap(&repo->diff_drivers, NULL, reg);
|
||||||
|
|
||||||
|
if (reg != NULL) /* if we race, free losing allocation */
|
||||||
|
git_diff_driver_registry_free(reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!repo->diff_drivers)
|
||||||
|
giterr_set(GITERR_REPOSITORY, "Unable to create diff driver registry");
|
||||||
|
|
||||||
|
return repo->diff_drivers;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int git_diff_driver_load(
|
||||||
|
git_diff_driver **out, git_repository *repo, const char *driver_name)
|
||||||
|
{
|
||||||
|
int error = 0, bval;
|
||||||
|
git_diff_driver_registry *reg;
|
||||||
|
git_diff_driver *drv;
|
||||||
|
size_t namelen = strlen(driver_name);
|
||||||
|
khiter_t pos;
|
||||||
|
git_config *cfg;
|
||||||
|
git_buf name = GIT_BUF_INIT;
|
||||||
|
const char *val;
|
||||||
|
bool found_driver = false;
|
||||||
|
|
||||||
|
reg = git_repository_driver_registry(repo);
|
||||||
|
if (!reg)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
drv = git__calloc(1, sizeof(git_diff_driver) + namelen + 1);
|
||||||
|
GITERR_CHECK_ALLOC(drv);
|
||||||
|
drv->type = DIFF_DRIVER_AUTO;
|
||||||
|
memcpy(drv->name, driver_name, namelen);
|
||||||
|
|
||||||
|
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.<driver>.binary unspecified, so just continue */
|
||||||
|
giterr_clear();
|
||||||
|
} else if (git_config_parse_bool(&bval, val) < 0) {
|
||||||
|
/* TODO: warn that diff.<driver>.binary has invalid value */
|
||||||
|
giterr_clear();
|
||||||
|
} else if (bval) {
|
||||||
|
/* if diff.<driver>.binary is true, just return the binary driver */
|
||||||
|
*out = &global_drivers[DIFF_DRIVER_BINARY];
|
||||||
|
goto done;
|
||||||
|
} else {
|
||||||
|
/* if diff.<driver>.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: warn if diff.<name>.command or diff.<name>.textconv are set */
|
||||||
|
|
||||||
|
git_buf_truncate(&name, namelen + strlen("diff.."));
|
||||||
|
git_buf_put(&name, "xfuncname", strlen("xfuncname"));
|
||||||
|
if ((error = git_config_get_multivar(
|
||||||
|
cfg, name.ptr, NULL, diff_driver_xfuncname, drv)) < 0) {
|
||||||
|
if (error != GIT_ENOTFOUND)
|
||||||
|
goto done;
|
||||||
|
giterr_clear(); /* no diff.<driver>.xfuncname, so just continue */
|
||||||
|
}
|
||||||
|
|
||||||
|
git_buf_truncate(&name, namelen + strlen("diff.."));
|
||||||
|
git_buf_put(&name, "funcname", strlen("funcname"));
|
||||||
|
if ((error = git_config_get_multivar(
|
||||||
|
cfg, name.ptr, NULL, diff_driver_funcname, drv)) < 0) {
|
||||||
|
if (error != GIT_ENOTFOUND)
|
||||||
|
goto done;
|
||||||
|
giterr_clear(); /* no diff.<driver>.funcname, so just continue */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we found any patterns, set driver type to use correct callback */
|
||||||
|
if (git_array_size(drv->fn_patterns) > 0) {
|
||||||
|
drv->type = DIFF_DRIVER_PATTERNLIST;
|
||||||
|
found_driver = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.<driver>.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);
|
||||||
|
goto done;
|
||||||
|
} else {
|
||||||
|
found_driver = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: look up diff.<driver>.algorithm to turn on minimal / patience
|
||||||
|
* diff in drv->other_flags
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* if no driver config found at all, fall back on AUTO driver */
|
||||||
|
if (!found_driver)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* store driver in registry */
|
||||||
|
git_strmap_insert(reg->drivers, drv->name, drv, error);
|
||||||
|
if (error < 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
*out = drv;
|
||||||
|
|
||||||
|
done:
|
||||||
|
git_buf_free(&name);
|
||||||
|
|
||||||
|
if (!*out)
|
||||||
|
*out = &global_drivers[DIFF_DRIVER_AUTO];
|
||||||
|
|
||||||
|
if (drv && drv != *out)
|
||||||
|
git_diff_driver_free(drv);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_diff_driver_lookup(
|
||||||
|
git_diff_driver **out, git_repository *repo, const char *path)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
const char *value;
|
||||||
|
|
||||||
|
assert(out);
|
||||||
|
|
||||||
|
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 (GIT_ATTR_FALSE(value))
|
||||||
|
*out = &global_drivers[DIFF_DRIVER_BINARY];
|
||||||
|
else if (GIT_ATTR_TRUE(value))
|
||||||
|
*out = &global_drivers[DIFF_DRIVER_TEXT];
|
||||||
|
|
||||||
|
/* otherwise look for driver information in config and build driver */
|
||||||
|
else if ((error = git_diff_driver_load(out, repo, value)) < 0) {
|
||||||
|
if (error != GIT_ENOTFOUND)
|
||||||
|
return error;
|
||||||
|
else
|
||||||
|
giterr_clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
use_auto:
|
||||||
|
if (!*out)
|
||||||
|
*out = &global_drivers[DIFF_DRIVER_AUTO];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_diff_driver_free(git_diff_driver *driver)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (!driver)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < git_array_size(driver->fn_patterns); ++i)
|
||||||
|
regfree(git_array_get(driver->fn_patterns, i));
|
||||||
|
git_array_clear(driver->fn_patterns);
|
||||||
|
|
||||||
|
regfree(&driver->word_pattern);
|
||||||
|
|
||||||
|
git__free(driver);
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_diff_driver_update_options(
|
||||||
|
uint32_t *option_flags, git_diff_driver *driver)
|
||||||
|
{
|
||||||
|
if ((*option_flags & FORCE_DIFFABLE) == 0)
|
||||||
|
*option_flags |= driver->binary_flags;
|
||||||
|
|
||||||
|
*option_flags |= driver->other_flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_UNUSED(driver);
|
||||||
|
|
||||||
|
/* TODO: provide encoding / binary detection callbacks that can
|
||||||
|
* be UTF-8 aware, etc. For now, instead of trying to be smart,
|
||||||
|
* let's just use the simple NUL-byte detection that core git uses.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* previously was: if (git_buf_text_is_binary(&search)) */
|
||||||
|
if (git_buf_text_contains_nul(&search))
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_context_line__simple(
|
||||||
|
git_diff_driver *driver, const char *line, size_t line_len)
|
||||||
|
{
|
||||||
|
GIT_UNUSED(driver);
|
||||||
|
GIT_UNUSED(line_len);
|
||||||
|
return (git__isalpha(*line) || *line == '_' || *line == '$');
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_context_line__pattern_match(
|
||||||
|
git_diff_driver *driver, const char *line, size_t line_len)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
GIT_UNUSED(line_len);
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long diff_context_find(
|
||||||
|
const char *line,
|
||||||
|
long line_len,
|
||||||
|
char *out,
|
||||||
|
long out_size,
|
||||||
|
void *payload)
|
||||||
|
{
|
||||||
|
git_diff_find_context_payload *ctxt = payload;
|
||||||
|
|
||||||
|
if (git_buf_set(&ctxt->line, line, (size_t)line_len) < 0)
|
||||||
|
return -1;
|
||||||
|
git_buf_rtrim(&ctxt->line);
|
||||||
|
|
||||||
|
if (!ctxt->line.size)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!ctxt->match_line ||
|
||||||
|
!ctxt->match_line(ctxt->driver, ctxt->line.ptr, ctxt->line.size))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
git_buf_truncate(&ctxt->line, (size_t)out_size);
|
||||||
|
git_buf_copy_cstr(out, (size_t)out_size, &ctxt->line);
|
||||||
|
|
||||||
|
return (long)ctxt->line.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_diff_find_context_init(
|
||||||
|
git_diff_find_context_fn *findfn_out,
|
||||||
|
git_diff_find_context_payload *payload_out,
|
||||||
|
git_diff_driver *driver)
|
||||||
|
{
|
||||||
|
*findfn_out = driver ? diff_context_find : NULL;
|
||||||
|
|
||||||
|
memset(payload_out, 0, sizeof(*payload_out));
|
||||||
|
if (driver) {
|
||||||
|
payload_out->driver = driver;
|
||||||
|
payload_out->match_line = (driver->type == DIFF_DRIVER_PATTERNLIST) ?
|
||||||
|
diff_context_line__pattern_match : diff_context_line__simple;
|
||||||
|
git_buf_init(&payload_out->line, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_diff_find_context_clear(git_diff_find_context_payload *payload)
|
||||||
|
{
|
||||||
|
if (payload) {
|
||||||
|
git_buf_free(&payload->line);
|
||||||
|
payload->driver = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
49
src/diff_driver.h
Normal file
49
src/diff_driver.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* 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_diff_driver_h__
|
||||||
|
#define INCLUDE_diff_driver_h__
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
typedef struct git_diff_driver_registry git_diff_driver_registry;
|
||||||
|
|
||||||
|
git_diff_driver_registry *git_diff_driver_registry_new(void);
|
||||||
|
void git_diff_driver_registry_free(git_diff_driver_registry *);
|
||||||
|
|
||||||
|
typedef struct git_diff_driver git_diff_driver;
|
||||||
|
|
||||||
|
int git_diff_driver_lookup(git_diff_driver **, git_repository *, const char *);
|
||||||
|
void git_diff_driver_free(git_diff_driver *);
|
||||||
|
|
||||||
|
/* diff option flags to force off and on for this driver */
|
||||||
|
void git_diff_driver_update_options(uint32_t *option_flags, git_diff_driver *);
|
||||||
|
|
||||||
|
/* returns -1 meaning "unknown", 0 meaning not binary, 1 meaning binary */
|
||||||
|
int git_diff_driver_content_is_binary(
|
||||||
|
git_diff_driver *, const char *content, size_t content_len);
|
||||||
|
|
||||||
|
typedef long (*git_diff_find_context_fn)(
|
||||||
|
const char *, long, char *, long, void *);
|
||||||
|
|
||||||
|
typedef int (*git_diff_find_context_line)(
|
||||||
|
git_diff_driver *, const char *, size_t);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
git_diff_driver *driver;
|
||||||
|
git_diff_find_context_line match_line;
|
||||||
|
git_buf line;
|
||||||
|
} git_diff_find_context_payload;
|
||||||
|
|
||||||
|
void git_diff_find_context_init(
|
||||||
|
git_diff_find_context_fn *findfn_out,
|
||||||
|
git_diff_find_context_payload *payload_out,
|
||||||
|
git_diff_driver *driver);
|
||||||
|
|
||||||
|
void git_diff_find_context_clear(git_diff_find_context_payload *);
|
||||||
|
|
||||||
|
#endif
|
447
src/diff_file.c
Normal file
447
src/diff_file.c
Normal file
@ -0,0 +1,447 @@
|
|||||||
|
/*
|
||||||
|
* 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/blob.h"
|
||||||
|
#include "git2/submodule.h"
|
||||||
|
#include "diff.h"
|
||||||
|
#include "diff_file.h"
|
||||||
|
#include "odb.h"
|
||||||
|
#include "fileops.h"
|
||||||
|
#include "filter.h"
|
||||||
|
|
||||||
|
#define DIFF_MAX_FILESIZE 0x20000000
|
||||||
|
|
||||||
|
static bool diff_file_content_binary_by_size(git_diff_file_content *fc)
|
||||||
|
{
|
||||||
|
/* if we have diff opts, check max_size vs file size */
|
||||||
|
if ((fc->file->flags & DIFF_FLAGS_KNOWN_BINARY) == 0 &&
|
||||||
|
fc->opts_max_size > 0 &&
|
||||||
|
fc->file->size > fc->opts_max_size)
|
||||||
|
fc->file->flags |= GIT_DIFF_FLAG_BINARY;
|
||||||
|
|
||||||
|
return ((fc->file->flags & GIT_DIFF_FLAG_BINARY) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void diff_file_content_binary_by_content(git_diff_file_content *fc)
|
||||||
|
{
|
||||||
|
if ((fc->file->flags & DIFF_FLAGS_KNOWN_BINARY) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (git_diff_driver_content_is_binary(
|
||||||
|
fc->driver, fc->map.data, fc->map.len)) {
|
||||||
|
case 0: fc->file->flags |= GIT_DIFF_FLAG_NOT_BINARY; break;
|
||||||
|
case 1: fc->file->flags |= GIT_DIFF_FLAG_BINARY; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_file_content_init_common(
|
||||||
|
git_diff_file_content *fc, const git_diff_options *opts)
|
||||||
|
{
|
||||||
|
fc->opts_flags = opts ? opts->flags : GIT_DIFF_NORMAL;
|
||||||
|
|
||||||
|
if (opts && opts->max_size >= 0)
|
||||||
|
fc->opts_max_size = opts->max_size ?
|
||||||
|
opts->max_size : DIFF_MAX_FILESIZE;
|
||||||
|
|
||||||
|
if (fc->src == GIT_ITERATOR_TYPE_EMPTY)
|
||||||
|
fc->src = GIT_ITERATOR_TYPE_TREE;
|
||||||
|
|
||||||
|
if (!fc->driver &&
|
||||||
|
git_diff_driver_lookup(&fc->driver, fc->repo, fc->file->path) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* give driver a chance to modify options */
|
||||||
|
git_diff_driver_update_options(&fc->opts_flags, fc->driver);
|
||||||
|
|
||||||
|
/* make sure file is conceivable mmap-able */
|
||||||
|
if ((git_off_t)((size_t)fc->file->size) != fc->file->size)
|
||||||
|
fc->file->flags |= GIT_DIFF_FLAG_BINARY;
|
||||||
|
/* check if user is forcing text diff the file */
|
||||||
|
else if (fc->opts_flags & GIT_DIFF_FORCE_TEXT) {
|
||||||
|
fc->file->flags &= ~GIT_DIFF_FLAG_BINARY;
|
||||||
|
fc->file->flags |= GIT_DIFF_FLAG_NOT_BINARY;
|
||||||
|
}
|
||||||
|
/* check if user is forcing binary diff the file */
|
||||||
|
else if (fc->opts_flags & GIT_DIFF_FORCE_BINARY) {
|
||||||
|
fc->file->flags &= ~GIT_DIFF_FLAG_NOT_BINARY;
|
||||||
|
fc->file->flags |= GIT_DIFF_FLAG_BINARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
diff_file_content_binary_by_size(fc);
|
||||||
|
|
||||||
|
if ((fc->flags & GIT_DIFF_FLAG__NO_DATA) != 0) {
|
||||||
|
fc->flags |= GIT_DIFF_FLAG__LOADED;
|
||||||
|
fc->map.len = 0;
|
||||||
|
fc->map.data = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((fc->flags & GIT_DIFF_FLAG__LOADED) != 0)
|
||||||
|
diff_file_content_binary_by_content(fc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_diff_file_content__init_from_diff(
|
||||||
|
git_diff_file_content *fc,
|
||||||
|
git_diff_list *diff,
|
||||||
|
size_t delta_index,
|
||||||
|
bool use_old)
|
||||||
|
{
|
||||||
|
git_diff_delta *delta = git_vector_get(&diff->deltas, delta_index);
|
||||||
|
bool has_data = true;
|
||||||
|
|
||||||
|
memset(fc, 0, sizeof(*fc));
|
||||||
|
fc->repo = diff->repo;
|
||||||
|
fc->file = use_old ? &delta->old_file : &delta->new_file;
|
||||||
|
fc->src = use_old ? diff->old_src : diff->new_src;
|
||||||
|
|
||||||
|
if (git_diff_driver_lookup(&fc->driver, fc->repo, fc->file->path) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
switch (delta->status) {
|
||||||
|
case GIT_DELTA_ADDED:
|
||||||
|
has_data = !use_old; break;
|
||||||
|
case GIT_DELTA_DELETED:
|
||||||
|
has_data = use_old; break;
|
||||||
|
case GIT_DELTA_UNTRACKED:
|
||||||
|
has_data = !use_old &&
|
||||||
|
(diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) != 0;
|
||||||
|
break;
|
||||||
|
case GIT_DELTA_MODIFIED:
|
||||||
|
case GIT_DELTA_COPIED:
|
||||||
|
case GIT_DELTA_RENAMED:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
has_data = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_data)
|
||||||
|
fc->flags |= GIT_DIFF_FLAG__NO_DATA;
|
||||||
|
|
||||||
|
return diff_file_content_init_common(fc, &diff->opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
memset(fc, 0, sizeof(*fc));
|
||||||
|
fc->repo = repo;
|
||||||
|
fc->file = as_file;
|
||||||
|
fc->blob = blob;
|
||||||
|
|
||||||
|
if (!blob) {
|
||||||
|
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->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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return diff_file_content_init_common(fc, opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_file_content_commit_to_str(
|
||||||
|
git_diff_file_content *fc, bool check_status)
|
||||||
|
{
|
||||||
|
char oid[GIT_OID_HEXSZ+1];
|
||||||
|
git_buf content = GIT_BUF_INIT;
|
||||||
|
const char *status = "";
|
||||||
|
|
||||||
|
if (check_status) {
|
||||||
|
int error = 0;
|
||||||
|
git_submodule *sm = NULL;
|
||||||
|
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) {
|
||||||
|
/* GIT_EEXISTS means a "submodule" that has not been git added */
|
||||||
|
if (error == GIT_EEXISTS)
|
||||||
|
error = 0;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update OID if we didn't have it previously */
|
||||||
|
if ((fc->file->flags & GIT_DIFF_FLAG_VALID_OID) == 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
|
||||||
|
status = "-dirty";
|
||||||
|
}
|
||||||
|
|
||||||
|
git_oid_tostr(oid, sizeof(oid), &fc->file->oid);
|
||||||
|
if (git_buf_printf(&content, "Subproject commit %s%s\n", oid, status) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
fc->map.len = git_buf_len(&content);
|
||||||
|
fc->map.data = git_buf_detach(&content);
|
||||||
|
fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_file_content_load_blob(git_diff_file_content *fc)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
git_odb_object *odb_obj = NULL;
|
||||||
|
|
||||||
|
if (git_oid_iszero(&fc->file->oid))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (fc->file->mode == GIT_FILEMODE_COMMIT)
|
||||||
|
return diff_file_content_commit_to_str(fc, false);
|
||||||
|
|
||||||
|
/* if we don't know size, try to peek at object header first */
|
||||||
|
if (!fc->file->size) {
|
||||||
|
git_odb *odb;
|
||||||
|
size_t len;
|
||||||
|
git_otype type;
|
||||||
|
|
||||||
|
if (!(error = git_repository_odb__weakptr(&odb, fc->repo))) {
|
||||||
|
error = git_odb__read_header_or_object(
|
||||||
|
&odb_obj, &len, &type, odb, &fc->file->oid);
|
||||||
|
git_odb_free(odb);
|
||||||
|
}
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
fc->file->size = len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (diff_file_content_binary_by_size(fc))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (odb_obj != NULL) {
|
||||||
|
error = git_object__from_odb_object(
|
||||||
|
(git_object **)&fc->blob, fc->repo, odb_obj, GIT_OBJ_BLOB);
|
||||||
|
git_odb_object_free(odb_obj);
|
||||||
|
} else {
|
||||||
|
error = git_blob_lookup(
|
||||||
|
(git_blob **)&fc->blob, fc->repo, &fc->file->oid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
fc->flags |= GIT_DIFF_FLAG__FREE_BLOB;
|
||||||
|
fc->map.data = (void *)git_blob_rawcontent(fc->blob);
|
||||||
|
fc->map.len = (size_t)git_blob_rawsize(fc->blob);
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_file_content_load_workdir_symlink(
|
||||||
|
git_diff_file_content *fc, git_buf *path)
|
||||||
|
{
|
||||||
|
ssize_t alloc_len, read_len;
|
||||||
|
|
||||||
|
/* link path on disk could be UTF-16, so prepare a buffer that is
|
||||||
|
* big enough to handle some UTF-8 data expansion
|
||||||
|
*/
|
||||||
|
alloc_len = (ssize_t)(fc->file->size * 2) + 1;
|
||||||
|
|
||||||
|
fc->map.data = git__calloc(alloc_len, sizeof(char));
|
||||||
|
GITERR_CHECK_ALLOC(fc->map.data);
|
||||||
|
|
||||||
|
fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
|
||||||
|
|
||||||
|
read_len = p_readlink(git_buf_cstr(path), fc->map.data, alloc_len);
|
||||||
|
if (read_len < 0) {
|
||||||
|
giterr_set(GITERR_OS, "Failed to read symlink '%s'", fc->file->path);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fc->map.len = read_len;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_file_content_load_workdir_file(
|
||||||
|
git_diff_file_content *fc, git_buf *path)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
git_vector filters = GIT_VECTOR_INIT;
|
||||||
|
git_buf raw = GIT_BUF_INIT, filtered = GIT_BUF_INIT;
|
||||||
|
git_file fd = git_futils_open_ro(git_buf_cstr(path));
|
||||||
|
|
||||||
|
if (fd < 0)
|
||||||
|
return fd;
|
||||||
|
|
||||||
|
if (!fc->file->size &&
|
||||||
|
!(fc->file->size = git_futils_filesize(fd)))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (diff_file_content_binary_by_size(fc))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if ((error = git_filters_load(
|
||||||
|
&filters, fc->repo, fc->file->path, GIT_FILTER_TO_ODB)) < 0)
|
||||||
|
goto cleanup;
|
||||||
|
/* error >= is a filter count */
|
||||||
|
|
||||||
|
if (error == 0) {
|
||||||
|
if (!(error = git_futils_mmap_ro(
|
||||||
|
&fc->map, fd, 0, (size_t)fc->file->size)))
|
||||||
|
fc->flags |= GIT_DIFF_FLAG__UNMAP_DATA;
|
||||||
|
else /* fall through to try readbuffer below */
|
||||||
|
giterr_clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error != 0) {
|
||||||
|
error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size);
|
||||||
|
if (error < 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (!filters.length)
|
||||||
|
git_buf_swap(&filtered, &raw);
|
||||||
|
else
|
||||||
|
error = git_filters_apply(&filtered, &raw, &filters);
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
fc->map.len = git_buf_len(&filtered);
|
||||||
|
fc->map.data = git_buf_detach(&filtered);
|
||||||
|
fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
git_buf_free(&raw);
|
||||||
|
git_buf_free(&filtered);
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
git_filters_free(&filters);
|
||||||
|
p_close(fd);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_file_content_load_workdir(git_diff_file_content *fc)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
git_buf path = GIT_BUF_INIT;
|
||||||
|
|
||||||
|
if (fc->file->mode == GIT_FILEMODE_COMMIT)
|
||||||
|
return diff_file_content_commit_to_str(fc, true);
|
||||||
|
|
||||||
|
if (fc->file->mode == GIT_FILEMODE_TREE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (git_buf_joinpath(
|
||||||
|
&path, git_repository_workdir(fc->repo), fc->file->path) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (S_ISLNK(fc->file->mode))
|
||||||
|
error = diff_file_content_load_workdir_symlink(fc, &path);
|
||||||
|
else
|
||||||
|
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) {
|
||||||
|
error = git_odb_hash(
|
||||||
|
&fc->file->oid, fc->map.data, fc->map.len, GIT_OBJ_BLOB);
|
||||||
|
fc->file->flags |= GIT_DIFF_FLAG_VALID_OID;
|
||||||
|
}
|
||||||
|
|
||||||
|
git_buf_free(&path);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_diff_file_content__load(git_diff_file_content *fc)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
if ((fc->flags & GIT_DIFF_FLAG__LOADED) != 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((fc->file->flags & GIT_DIFF_FLAG_BINARY) != 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (fc->src == GIT_ITERATOR_TYPE_WORKDIR)
|
||||||
|
error = diff_file_content_load_workdir(fc);
|
||||||
|
else
|
||||||
|
error = diff_file_content_load_blob(fc);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
fc->flags |= GIT_DIFF_FLAG__LOADED;
|
||||||
|
|
||||||
|
diff_file_content_binary_by_content(fc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_diff_file_content__unload(git_diff_file_content *fc)
|
||||||
|
{
|
||||||
|
if (fc->flags & GIT_DIFF_FLAG__FREE_DATA) {
|
||||||
|
git__free(fc->map.data);
|
||||||
|
fc->map.data = "";
|
||||||
|
fc->map.len = 0;
|
||||||
|
fc->flags &= ~GIT_DIFF_FLAG__FREE_DATA;
|
||||||
|
}
|
||||||
|
else if (fc->flags & GIT_DIFF_FLAG__UNMAP_DATA) {
|
||||||
|
git_futils_mmap_free(&fc->map);
|
||||||
|
fc->map.data = "";
|
||||||
|
fc->map.len = 0;
|
||||||
|
fc->flags &= ~GIT_DIFF_FLAG__UNMAP_DATA;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fc->flags & GIT_DIFF_FLAG__FREE_BLOB) {
|
||||||
|
git_blob_free((git_blob *)fc->blob);
|
||||||
|
fc->blob = NULL;
|
||||||
|
fc->flags &= ~GIT_DIFF_FLAG__FREE_BLOB;
|
||||||
|
}
|
||||||
|
|
||||||
|
fc->flags &= ~GIT_DIFF_FLAG__LOADED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_diff_file_content__clear(git_diff_file_content *fc)
|
||||||
|
{
|
||||||
|
git_diff_file_content__unload(fc);
|
||||||
|
|
||||||
|
/* for now, nothing else to do */
|
||||||
|
}
|
58
src/diff_file.h
Normal file
58
src/diff_file.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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_diff_file_h__
|
||||||
|
#define INCLUDE_diff_file_h__
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "diff.h"
|
||||||
|
#include "diff_driver.h"
|
||||||
|
#include "map.h"
|
||||||
|
|
||||||
|
/* expanded information for one side of a delta */
|
||||||
|
typedef struct {
|
||||||
|
git_repository *repo;
|
||||||
|
git_diff_file *file;
|
||||||
|
git_diff_driver *driver;
|
||||||
|
uint32_t flags;
|
||||||
|
uint32_t opts_flags;
|
||||||
|
git_off_t opts_max_size;
|
||||||
|
git_iterator_type_t src;
|
||||||
|
const git_blob *blob;
|
||||||
|
git_map map;
|
||||||
|
} git_diff_file_content;
|
||||||
|
|
||||||
|
extern int git_diff_file_content__init_from_diff(
|
||||||
|
git_diff_file_content *fc,
|
||||||
|
git_diff_list *diff,
|
||||||
|
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);
|
||||||
|
|
||||||
|
extern 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);
|
||||||
|
|
||||||
|
/* this loads the blob/file-on-disk as needed */
|
||||||
|
extern int git_diff_file_content__load(git_diff_file_content *fc);
|
||||||
|
|
||||||
|
/* this releases the blob/file-in-memory */
|
||||||
|
extern void git_diff_file_content__unload(git_diff_file_content *fc);
|
||||||
|
|
||||||
|
/* this unloads and also releases any other resources */
|
||||||
|
extern void git_diff_file_content__clear(git_diff_file_content *fc);
|
||||||
|
|
||||||
|
#endif
|
1819
src/diff_output.c
1819
src/diff_output.c
File diff suppressed because it is too large
Load Diff
@ -1,93 +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_diff_output_h__
|
|
||||||
#define INCLUDE_diff_output_h__
|
|
||||||
|
|
||||||
#include "git2/blob.h"
|
|
||||||
#include "diff.h"
|
|
||||||
#include "map.h"
|
|
||||||
#include "xdiff/xdiff.h"
|
|
||||||
|
|
||||||
#define MAX_DIFF_FILESIZE 0x20000000
|
|
||||||
|
|
||||||
enum {
|
|
||||||
GIT_DIFF_PATCH_ALLOCATED = (1 << 0),
|
|
||||||
GIT_DIFF_PATCH_PREPPED = (1 << 1),
|
|
||||||
GIT_DIFF_PATCH_LOADED = (1 << 2),
|
|
||||||
GIT_DIFF_PATCH_DIFFABLE = (1 << 3),
|
|
||||||
GIT_DIFF_PATCH_DIFFED = (1 << 4),
|
|
||||||
};
|
|
||||||
|
|
||||||
/* context for performing diffs */
|
|
||||||
typedef struct {
|
|
||||||
git_repository *repo;
|
|
||||||
git_diff_list *diff;
|
|
||||||
const git_diff_options *opts;
|
|
||||||
git_diff_file_cb file_cb;
|
|
||||||
git_diff_hunk_cb hunk_cb;
|
|
||||||
git_diff_data_cb data_cb;
|
|
||||||
void *payload;
|
|
||||||
int error;
|
|
||||||
git_diff_range range;
|
|
||||||
xdemitconf_t xdiff_config;
|
|
||||||
xpparam_t xdiff_params;
|
|
||||||
} diff_context;
|
|
||||||
|
|
||||||
/* cached information about a single span in a diff */
|
|
||||||
typedef struct diff_patch_line diff_patch_line;
|
|
||||||
struct diff_patch_line {
|
|
||||||
const char *ptr;
|
|
||||||
size_t len;
|
|
||||||
size_t lines, oldno, newno;
|
|
||||||
char origin;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* cached information about a hunk in a diff */
|
|
||||||
typedef struct diff_patch_hunk diff_patch_hunk;
|
|
||||||
struct diff_patch_hunk {
|
|
||||||
git_diff_range range;
|
|
||||||
char header[128];
|
|
||||||
size_t header_len;
|
|
||||||
size_t line_start;
|
|
||||||
size_t line_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct git_diff_patch {
|
|
||||||
git_refcount rc;
|
|
||||||
git_diff_list *diff; /* for refcount purposes, maybe NULL for blob diffs */
|
|
||||||
git_diff_delta *delta;
|
|
||||||
diff_context *ctxt; /* only valid while generating patch */
|
|
||||||
git_iterator_type_t old_src;
|
|
||||||
git_iterator_type_t new_src;
|
|
||||||
git_blob *old_blob;
|
|
||||||
git_blob *new_blob;
|
|
||||||
git_map old_data;
|
|
||||||
git_map new_data;
|
|
||||||
uint32_t flags;
|
|
||||||
diff_patch_hunk *hunks;
|
|
||||||
size_t hunks_asize, hunks_size;
|
|
||||||
diff_patch_line *lines;
|
|
||||||
size_t lines_asize, lines_size;
|
|
||||||
size_t oldno, newno;
|
|
||||||
};
|
|
||||||
|
|
||||||
/* context for performing diff on a single delta */
|
|
||||||
typedef struct {
|
|
||||||
git_diff_patch *patch;
|
|
||||||
uint32_t prepped : 1;
|
|
||||||
uint32_t loaded : 1;
|
|
||||||
uint32_t diffable : 1;
|
|
||||||
uint32_t diffed : 1;
|
|
||||||
} diff_delta_context;
|
|
||||||
|
|
||||||
extern int git_diff__paired_foreach(
|
|
||||||
git_diff_list *idx2head,
|
|
||||||
git_diff_list *wd2idx,
|
|
||||||
int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
|
|
||||||
void *payload);
|
|
||||||
|
|
||||||
#endif
|
|
991
src/diff_patch.c
Normal file
991
src/diff_patch.c
Normal file
@ -0,0 +1,991 @@
|
|||||||
|
/*
|
||||||
|
* 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 "diff.h"
|
||||||
|
#include "diff_file.h"
|
||||||
|
#include "diff_driver.h"
|
||||||
|
#include "diff_patch.h"
|
||||||
|
#include "diff_xdiff.h"
|
||||||
|
|
||||||
|
/* cached information about a single span in a diff */
|
||||||
|
typedef struct diff_patch_line diff_patch_line;
|
||||||
|
struct diff_patch_line {
|
||||||
|
const char *ptr;
|
||||||
|
size_t len;
|
||||||
|
size_t lines, oldno, newno;
|
||||||
|
char origin;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* cached information about a hunk in a diff */
|
||||||
|
typedef struct diff_patch_hunk diff_patch_hunk;
|
||||||
|
struct diff_patch_hunk {
|
||||||
|
git_diff_range range;
|
||||||
|
char header[128];
|
||||||
|
size_t header_len;
|
||||||
|
size_t line_start;
|
||||||
|
size_t line_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct git_diff_patch {
|
||||||
|
git_refcount rc;
|
||||||
|
git_diff_list *diff; /* for refcount purposes, maybe NULL for blob diffs */
|
||||||
|
git_diff_delta *delta;
|
||||||
|
size_t delta_index;
|
||||||
|
git_diff_file_content ofile;
|
||||||
|
git_diff_file_content nfile;
|
||||||
|
uint32_t flags;
|
||||||
|
git_array_t(diff_patch_hunk) hunks;
|
||||||
|
git_array_t(diff_patch_line) lines;
|
||||||
|
size_t oldno, newno;
|
||||||
|
size_t content_size;
|
||||||
|
git_pool flattened;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
GIT_DIFF_PATCH_ALLOCATED = (1 << 0),
|
||||||
|
GIT_DIFF_PATCH_INITIALIZED = (1 << 1),
|
||||||
|
GIT_DIFF_PATCH_LOADED = (1 << 2),
|
||||||
|
GIT_DIFF_PATCH_DIFFABLE = (1 << 3),
|
||||||
|
GIT_DIFF_PATCH_DIFFED = (1 << 4),
|
||||||
|
GIT_DIFF_PATCH_FLATTENED = (1 << 5),
|
||||||
|
};
|
||||||
|
|
||||||
|
static void diff_output_init(git_diff_output*, const git_diff_options*,
|
||||||
|
git_diff_file_cb, git_diff_hunk_cb, git_diff_data_cb, void*);
|
||||||
|
|
||||||
|
static void diff_output_to_patch(git_diff_output *, git_diff_patch *);
|
||||||
|
|
||||||
|
static void diff_patch_update_binary(git_diff_patch *patch)
|
||||||
|
{
|
||||||
|
if ((patch->delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((patch->ofile.file->flags & GIT_DIFF_FLAG_BINARY) != 0 ||
|
||||||
|
(patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0)
|
||||||
|
patch->delta->flags |= GIT_DIFF_FLAG_BINARY;
|
||||||
|
|
||||||
|
else if ((patch->ofile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0 &&
|
||||||
|
(patch->nfile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0)
|
||||||
|
patch->delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void diff_patch_init_common(git_diff_patch *patch)
|
||||||
|
{
|
||||||
|
diff_patch_update_binary(patch);
|
||||||
|
|
||||||
|
if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
|
||||||
|
patch->flags |= GIT_DIFF_PATCH_LOADED; /* set LOADED but not DIFFABLE */
|
||||||
|
|
||||||
|
patch->flags |= GIT_DIFF_PATCH_INITIALIZED;
|
||||||
|
|
||||||
|
if (patch->diff)
|
||||||
|
git_diff_list_addref(patch->diff);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_patch_init_from_diff(
|
||||||
|
git_diff_patch *patch, git_diff_list *diff, size_t delta_index)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
memset(patch, 0, sizeof(*patch));
|
||||||
|
patch->diff = diff;
|
||||||
|
patch->delta = git_vector_get(&diff->deltas, delta_index);
|
||||||
|
patch->delta_index = delta_index;
|
||||||
|
|
||||||
|
if ((error = git_diff_file_content__init_from_diff(
|
||||||
|
&patch->ofile, diff, delta_index, true)) < 0 ||
|
||||||
|
(error = git_diff_file_content__init_from_diff(
|
||||||
|
&patch->nfile, diff, delta_index, false)) < 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
diff_patch_init_common(patch);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_patch_alloc_from_diff(
|
||||||
|
git_diff_patch **out,
|
||||||
|
git_diff_list *diff,
|
||||||
|
size_t delta_index)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
git_diff_patch *patch = git__calloc(1, sizeof(git_diff_patch));
|
||||||
|
GITERR_CHECK_ALLOC(patch);
|
||||||
|
|
||||||
|
if (!(error = diff_patch_init_from_diff(patch, diff, delta_index))) {
|
||||||
|
patch->flags |= GIT_DIFF_PATCH_ALLOCATED;
|
||||||
|
GIT_REFCOUNT_INC(patch);
|
||||||
|
} else {
|
||||||
|
git__free(patch);
|
||||||
|
patch = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = patch;
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_patch_load(git_diff_patch *patch, git_diff_output *output)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
bool incomplete_data;
|
||||||
|
|
||||||
|
if ((patch->flags & GIT_DIFF_PATCH_LOADED) != 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* if no hunk and data callbacks and user doesn't care if data looks
|
||||||
|
* binary, then there is no need to actually load the data
|
||||||
|
*/
|
||||||
|
if ((patch->ofile.opts_flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0 &&
|
||||||
|
output && !output->hunk_cb && !output->data_cb)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
incomplete_data =
|
||||||
|
(((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 ||
|
||||||
|
(patch->ofile.file->flags & GIT_DIFF_FLAG_VALID_OID) != 0) &&
|
||||||
|
((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 ||
|
||||||
|
(patch->nfile.file->flags & GIT_DIFF_FLAG_VALID_OID) != 0));
|
||||||
|
|
||||||
|
/* always try to load workdir content first because filtering may
|
||||||
|
* need 2x data size and this minimizes peak memory footprint
|
||||||
|
*/
|
||||||
|
if (patch->ofile.src == GIT_ITERATOR_TYPE_WORKDIR) {
|
||||||
|
if ((error = git_diff_file_content__load(&patch->ofile)) < 0 ||
|
||||||
|
(patch->ofile.file->flags & GIT_DIFF_FLAG_BINARY) != 0)
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (patch->nfile.src == GIT_ITERATOR_TYPE_WORKDIR) {
|
||||||
|
if ((error = git_diff_file_content__load(&patch->nfile)) < 0 ||
|
||||||
|
(patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0)
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* once workdir has been tried, load other data as needed */
|
||||||
|
if (patch->ofile.src != GIT_ITERATOR_TYPE_WORKDIR) {
|
||||||
|
if ((error = git_diff_file_content__load(&patch->ofile)) < 0 ||
|
||||||
|
(patch->ofile.file->flags & GIT_DIFF_FLAG_BINARY) != 0)
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (patch->nfile.src != GIT_ITERATOR_TYPE_WORKDIR) {
|
||||||
|
if ((error = git_diff_file_content__load(&patch->nfile)) < 0 ||
|
||||||
|
(patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0)
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we were previously missing an oid, update MODIFIED->UNMODIFIED */
|
||||||
|
if (incomplete_data &&
|
||||||
|
patch->ofile.file->mode == patch->nfile.file->mode &&
|
||||||
|
git_oid_equal(&patch->ofile.file->oid, &patch->nfile.file->oid) &&
|
||||||
|
patch->delta->status == GIT_DELTA_MODIFIED) /* not RENAMED/COPIED! */
|
||||||
|
patch->delta->status = GIT_DELTA_UNMODIFIED;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
diff_patch_update_binary(patch);
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
/* patch is diffable only for non-binary, modified files where
|
||||||
|
* at least one side has data and the data actually changed
|
||||||
|
*/
|
||||||
|
if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) == 0 &&
|
||||||
|
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)))
|
||||||
|
patch->flags |= GIT_DIFF_PATCH_DIFFABLE;
|
||||||
|
|
||||||
|
patch->flags |= GIT_DIFF_PATCH_LOADED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_patch_file_callback(
|
||||||
|
git_diff_patch *patch, git_diff_output *output)
|
||||||
|
{
|
||||||
|
float progress;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_patch_generate(git_diff_patch *patch, git_diff_output *output)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
if ((patch->flags & GIT_DIFF_PATCH_DIFFED) != 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((patch->flags & GIT_DIFF_PATCH_LOADED) == 0 &&
|
||||||
|
(error = diff_patch_load(patch, output)) < 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
if ((patch->flags & GIT_DIFF_PATCH_DIFFABLE) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (output->diff_cb != NULL &&
|
||||||
|
!(error = output->diff_cb(output, patch)))
|
||||||
|
patch->flags |= GIT_DIFF_PATCH_DIFFED;
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void diff_patch_free(git_diff_patch *patch)
|
||||||
|
{
|
||||||
|
git_diff_file_content__clear(&patch->ofile);
|
||||||
|
git_diff_file_content__clear(&patch->nfile);
|
||||||
|
|
||||||
|
git_array_clear(patch->lines);
|
||||||
|
git_array_clear(patch->hunks);
|
||||||
|
|
||||||
|
git_diff_list_free(patch->diff); /* decrements refcount */
|
||||||
|
patch->diff = NULL;
|
||||||
|
|
||||||
|
git_pool_clear(&patch->flattened);
|
||||||
|
|
||||||
|
if (patch->flags & GIT_DIFF_PATCH_ALLOCATED)
|
||||||
|
git__free(patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_required(git_diff_list *diff, const char *action)
|
||||||
|
{
|
||||||
|
if (diff)
|
||||||
|
return 0;
|
||||||
|
giterr_set(GITERR_INVALID, "Must provide valid diff to %s", action);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_diff_foreach(
|
||||||
|
git_diff_list *diff,
|
||||||
|
git_diff_file_cb file_cb,
|
||||||
|
git_diff_hunk_cb hunk_cb,
|
||||||
|
git_diff_data_cb data_cb,
|
||||||
|
void *payload)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
git_xdiff_output xo;
|
||||||
|
size_t idx;
|
||||||
|
git_diff_patch patch;
|
||||||
|
|
||||||
|
if (diff_required(diff, "git_diff_foreach") < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
diff_output_init((git_diff_output *)&xo,
|
||||||
|
&diff->opts, file_cb, hunk_cb, data_cb, payload);
|
||||||
|
git_xdiff_init(&xo, &diff->opts);
|
||||||
|
|
||||||
|
git_vector_foreach(&diff->deltas, idx, patch.delta) {
|
||||||
|
|
||||||
|
/* check flags against patch status */
|
||||||
|
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, (git_diff_output *)&xo);
|
||||||
|
|
||||||
|
if (!error)
|
||||||
|
error = diff_patch_generate(&patch, (git_diff_output *)&xo);
|
||||||
|
|
||||||
|
git_diff_patch_free(&patch);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error < 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error == GIT_EUSER)
|
||||||
|
giterr_clear(); /* don't leave error message set invalidly */
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
git_diff_patch patch;
|
||||||
|
git_diff_delta delta;
|
||||||
|
char paths[GIT_FLEX_ARRAY];
|
||||||
|
} diff_patch_with_delta;
|
||||||
|
|
||||||
|
static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
git_diff_patch *patch = &pd->patch;
|
||||||
|
bool has_old = ((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
|
||||||
|
bool has_new = ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
|
||||||
|
|
||||||
|
pd->delta.status = has_new ?
|
||||||
|
(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))
|
||||||
|
pd->delta.status = GIT_DELTA_UNMODIFIED;
|
||||||
|
|
||||||
|
patch->delta = &pd->delta;
|
||||||
|
|
||||||
|
diff_patch_init_common(patch);
|
||||||
|
|
||||||
|
if (pd->delta.status == GIT_DELTA_UNMODIFIED &&
|
||||||
|
!(patch->ofile.opts_flags & GIT_DIFF_INCLUDE_UNMODIFIED))
|
||||||
|
return error;
|
||||||
|
|
||||||
|
error = diff_patch_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(
|
||||||
|
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,
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
pd->patch.delta = &pd->delta;
|
||||||
|
|
||||||
|
pd->delta.old_file.path = old_path;
|
||||||
|
pd->delta.new_file.path = new_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)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
return diff_single_generate(pd, xo);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_patch_with_delta_alloc(
|
||||||
|
diff_patch_with_delta **out,
|
||||||
|
const char **old_path,
|
||||||
|
const char **new_path)
|
||||||
|
{
|
||||||
|
diff_patch_with_delta *pd;
|
||||||
|
size_t old_len = *old_path ? strlen(*old_path) : 0;
|
||||||
|
size_t new_len = *new_path ? strlen(*new_path) : 0;
|
||||||
|
|
||||||
|
*out = pd = git__calloc(1, sizeof(*pd) + old_len + new_len + 2);
|
||||||
|
GITERR_CHECK_ALLOC(pd);
|
||||||
|
|
||||||
|
pd->patch.flags = GIT_DIFF_PATCH_ALLOCATED;
|
||||||
|
|
||||||
|
if (*old_path) {
|
||||||
|
memcpy(&pd->paths[0], *old_path, old_len);
|
||||||
|
*old_path = &pd->paths[0];
|
||||||
|
} else if (*new_path)
|
||||||
|
*old_path = &pd->paths[old_len + 1];
|
||||||
|
|
||||||
|
if (*new_path) {
|
||||||
|
memcpy(&pd->paths[old_len + 1], *new_path, new_len);
|
||||||
|
*new_path = &pd->paths[old_len + 1];
|
||||||
|
} else if (*old_path)
|
||||||
|
*new_path = &pd->paths[0];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_diff_blobs(
|
||||||
|
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_cb file_cb,
|
||||||
|
git_diff_hunk_cb hunk_cb,
|
||||||
|
git_diff_data_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(
|
||||||
|
(git_diff_output *)&xo, 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;
|
||||||
|
|
||||||
|
error = diff_patch_from_blobs(
|
||||||
|
&pd, &xo, old_blob, old_path, new_blob, new_path, opts);
|
||||||
|
|
||||||
|
git_diff_patch_free((git_diff_patch *)&pd);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_diff_patch_from_blobs(
|
||||||
|
git_diff_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)
|
||||||
|
{
|
||||||
|
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, &new_path) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
memset(&xo, 0, sizeof(xo));
|
||||||
|
|
||||||
|
diff_output_to_patch((git_diff_output *)&xo, &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)
|
||||||
|
*out = (git_diff_patch *)pd;
|
||||||
|
else
|
||||||
|
git_diff_patch_free((git_diff_patch *)pd);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_patch_from_blob_and_buffer(
|
||||||
|
diff_patch_with_delta *pd,
|
||||||
|
git_xdiff_output *xo,
|
||||||
|
const git_blob *old_blob,
|
||||||
|
const char *old_path,
|
||||||
|
const char *buf,
|
||||||
|
size_t buflen,
|
||||||
|
const char *buf_path,
|
||||||
|
const git_diff_options *opts)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
return diff_single_generate(pd, xo);
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_diff_blob_to_buffer(
|
||||||
|
const git_blob *old_blob,
|
||||||
|
const char *old_path,
|
||||||
|
const char *buf,
|
||||||
|
size_t buflen,
|
||||||
|
const char *buf_path,
|
||||||
|
const git_diff_options *opts,
|
||||||
|
git_diff_file_cb file_cb,
|
||||||
|
git_diff_hunk_cb hunk_cb,
|
||||||
|
git_diff_data_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(
|
||||||
|
(git_diff_output *)&xo, 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_diff_patch_free((git_diff_patch *)&pd);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_diff_patch_from_blob_and_buffer(
|
||||||
|
git_diff_patch **out,
|
||||||
|
const git_blob *old_blob,
|
||||||
|
const char *old_path,
|
||||||
|
const char *buf,
|
||||||
|
size_t buflen,
|
||||||
|
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((git_diff_output *)&xo, &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);
|
||||||
|
|
||||||
|
if (!error)
|
||||||
|
*out = (git_diff_patch *)pd;
|
||||||
|
else
|
||||||
|
git_diff_patch_free((git_diff_patch *)pd);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_diff_get_patch(
|
||||||
|
git_diff_patch **patch_ptr,
|
||||||
|
const git_diff_delta **delta_ptr,
|
||||||
|
git_diff_list *diff,
|
||||||
|
size_t idx)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
git_xdiff_output xo;
|
||||||
|
git_diff_delta *delta = NULL;
|
||||||
|
git_diff_patch *patch = NULL;
|
||||||
|
|
||||||
|
if (patch_ptr) *patch_ptr = NULL;
|
||||||
|
if (delta_ptr) *delta_ptr = NULL;
|
||||||
|
|
||||||
|
if (diff_required(diff, "git_diff_get_patch") < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
delta = git_vector_get(&diff->deltas, idx);
|
||||||
|
if (!delta) {
|
||||||
|
giterr_set(GITERR_INVALID, "Index out of range for delta in diff");
|
||||||
|
return GIT_ENOTFOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delta_ptr)
|
||||||
|
*delta_ptr = delta;
|
||||||
|
|
||||||
|
if (git_diff_delta__should_skip(&diff->opts, delta))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* don't load the patch data unless we need it for binary check */
|
||||||
|
if (!patch_ptr &&
|
||||||
|
((delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0 ||
|
||||||
|
(diff->opts.flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if ((error = diff_patch_alloc_from_diff(&patch, diff, idx)) < 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
diff_output_to_patch((git_diff_output *)&xo, patch);
|
||||||
|
git_xdiff_init(&xo, &diff->opts);
|
||||||
|
|
||||||
|
error = diff_patch_file_callback(patch, (git_diff_output *)&xo);
|
||||||
|
|
||||||
|
if (!error)
|
||||||
|
error = diff_patch_generate(patch, (git_diff_output *)&xo);
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
/* if cumulative diff size is < 0.5 total size, flatten the patch */
|
||||||
|
/* unload the file content */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error || !patch_ptr)
|
||||||
|
git_diff_patch_free(patch);
|
||||||
|
else
|
||||||
|
*patch_ptr = patch;
|
||||||
|
|
||||||
|
if (error == GIT_EUSER)
|
||||||
|
giterr_clear(); /* don't leave error message set invalidly */
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_diff_patch_free(git_diff_patch *patch)
|
||||||
|
{
|
||||||
|
if (patch)
|
||||||
|
GIT_REFCOUNT_DEC(patch, diff_patch_free);
|
||||||
|
}
|
||||||
|
|
||||||
|
const git_diff_delta *git_diff_patch_delta(git_diff_patch *patch)
|
||||||
|
{
|
||||||
|
assert(patch);
|
||||||
|
return patch->delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t git_diff_patch_num_hunks(git_diff_patch *patch)
|
||||||
|
{
|
||||||
|
assert(patch);
|
||||||
|
return git_array_size(patch->hunks);
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_diff_patch_line_stats(
|
||||||
|
size_t *total_ctxt,
|
||||||
|
size_t *total_adds,
|
||||||
|
size_t *total_dels,
|
||||||
|
const git_diff_patch *patch)
|
||||||
|
{
|
||||||
|
size_t totals[3], idx;
|
||||||
|
|
||||||
|
memset(totals, 0, sizeof(totals));
|
||||||
|
|
||||||
|
for (idx = 0; idx < git_array_size(patch->lines); ++idx) {
|
||||||
|
diff_patch_line *line = git_array_get(patch->lines, idx);
|
||||||
|
if (!line)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch (line->origin) {
|
||||||
|
case GIT_DIFF_LINE_CONTEXT: totals[0]++; break;
|
||||||
|
case GIT_DIFF_LINE_ADDITION: totals[1]++; break;
|
||||||
|
case GIT_DIFF_LINE_DELETION: totals[2]++; break;
|
||||||
|
default:
|
||||||
|
/* diff --stat and --numstat don't count EOFNL marks because
|
||||||
|
* they will always be paired with a ADDITION or DELETION line.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (total_ctxt)
|
||||||
|
*total_ctxt = totals[0];
|
||||||
|
if (total_adds)
|
||||||
|
*total_adds = totals[1];
|
||||||
|
if (total_dels)
|
||||||
|
*total_dels = totals[2];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_error_outofrange(const char *thing)
|
||||||
|
{
|
||||||
|
giterr_set(GITERR_INVALID, "Diff patch %s index out of range", thing);
|
||||||
|
return GIT_ENOTFOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_diff_patch_get_hunk(
|
||||||
|
const git_diff_range **range,
|
||||||
|
const char **header,
|
||||||
|
size_t *header_len,
|
||||||
|
size_t *lines_in_hunk,
|
||||||
|
git_diff_patch *patch,
|
||||||
|
size_t hunk_idx)
|
||||||
|
{
|
||||||
|
diff_patch_hunk *hunk;
|
||||||
|
assert(patch);
|
||||||
|
|
||||||
|
hunk = git_array_get(patch->hunks, hunk_idx);
|
||||||
|
|
||||||
|
if (!hunk) {
|
||||||
|
if (range) *range = NULL;
|
||||||
|
if (header) *header = NULL;
|
||||||
|
if (header_len) *header_len = 0;
|
||||||
|
if (lines_in_hunk) *lines_in_hunk = 0;
|
||||||
|
return diff_error_outofrange("hunk");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (range) *range = &hunk->range;
|
||||||
|
if (header) *header = hunk->header;
|
||||||
|
if (header_len) *header_len = hunk->header_len;
|
||||||
|
if (lines_in_hunk) *lines_in_hunk = hunk->line_count;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_diff_patch_num_lines_in_hunk(git_diff_patch *patch, size_t hunk_idx)
|
||||||
|
{
|
||||||
|
diff_patch_hunk *hunk;
|
||||||
|
assert(patch);
|
||||||
|
|
||||||
|
if (!(hunk = git_array_get(patch->hunks, hunk_idx)))
|
||||||
|
return diff_error_outofrange("hunk");
|
||||||
|
return (int)hunk->line_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_diff_patch_get_line_in_hunk(
|
||||||
|
char *line_origin,
|
||||||
|
const char **content,
|
||||||
|
size_t *content_len,
|
||||||
|
int *old_lineno,
|
||||||
|
int *new_lineno,
|
||||||
|
git_diff_patch *patch,
|
||||||
|
size_t hunk_idx,
|
||||||
|
size_t line_of_hunk)
|
||||||
|
{
|
||||||
|
diff_patch_hunk *hunk;
|
||||||
|
diff_patch_line *line;
|
||||||
|
const char *thing;
|
||||||
|
|
||||||
|
assert(patch);
|
||||||
|
|
||||||
|
if (!(hunk = git_array_get(patch->hunks, hunk_idx))) {
|
||||||
|
thing = "hunk";
|
||||||
|
goto notfound;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line_of_hunk >= hunk->line_count ||
|
||||||
|
!(line = git_array_get(
|
||||||
|
patch->lines, hunk->line_start + line_of_hunk))) {
|
||||||
|
thing = "line";
|
||||||
|
goto notfound;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line_origin) *line_origin = line->origin;
|
||||||
|
if (content) *content = line->ptr;
|
||||||
|
if (content_len) *content_len = line->len;
|
||||||
|
if (old_lineno) *old_lineno = (int)line->oldno;
|
||||||
|
if (new_lineno) *new_lineno = (int)line->newno;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
notfound:
|
||||||
|
if (line_origin) *line_origin = GIT_DIFF_LINE_CONTEXT;
|
||||||
|
if (content) *content = NULL;
|
||||||
|
if (content_len) *content_len = 0;
|
||||||
|
if (old_lineno) *old_lineno = -1;
|
||||||
|
if (new_lineno) *new_lineno = -1;
|
||||||
|
|
||||||
|
return diff_error_outofrange(thing);
|
||||||
|
}
|
||||||
|
|
||||||
|
git_diff_list *git_diff_patch__diff(git_diff_patch *patch)
|
||||||
|
{
|
||||||
|
return patch->diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
git_diff_driver *git_diff_patch__driver(git_diff_patch *patch)
|
||||||
|
{
|
||||||
|
/* ofile driver is representative for whole patch */
|
||||||
|
return patch->ofile.driver;
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_diff_patch__old_data(
|
||||||
|
char **ptr, size_t *len, git_diff_patch *patch)
|
||||||
|
{
|
||||||
|
*ptr = patch->ofile.map.data;
|
||||||
|
*len = patch->ofile.map.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_diff_patch__new_data(
|
||||||
|
char **ptr, size_t *len, git_diff_patch *patch)
|
||||||
|
{
|
||||||
|
*ptr = patch->nfile.map.data;
|
||||||
|
*len = patch->nfile.map.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int git_diff_patch__invoke_callbacks(
|
||||||
|
git_diff_patch *patch,
|
||||||
|
git_diff_file_cb file_cb,
|
||||||
|
git_diff_hunk_cb hunk_cb,
|
||||||
|
git_diff_data_cb line_cb,
|
||||||
|
void *payload)
|
||||||
|
{
|
||||||
|
int error = 0;
|
||||||
|
uint32_t i, j;
|
||||||
|
|
||||||
|
if (file_cb)
|
||||||
|
error = file_cb(patch->delta, 0, payload);
|
||||||
|
|
||||||
|
if (!hunk_cb && !line_cb)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
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->range, h->header, h->header_len, payload);
|
||||||
|
|
||||||
|
if (!line_cb)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (j = 0; !error && j < h->line_count; ++j) {
|
||||||
|
diff_patch_line *l =
|
||||||
|
git_array_get(patch->lines, h->line_start + j);
|
||||||
|
|
||||||
|
error = line_cb(
|
||||||
|
patch->delta, &h->range, l->origin, l->ptr, l->len, payload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int diff_patch_file_cb(
|
||||||
|
const git_diff_delta *delta,
|
||||||
|
float progress,
|
||||||
|
void *payload)
|
||||||
|
{
|
||||||
|
GIT_UNUSED(delta); GIT_UNUSED(progress); GIT_UNUSED(payload);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_patch_hunk_cb(
|
||||||
|
const git_diff_delta *delta,
|
||||||
|
const git_diff_range *range,
|
||||||
|
const char *header,
|
||||||
|
size_t header_len,
|
||||||
|
void *payload)
|
||||||
|
{
|
||||||
|
git_diff_patch *patch = payload;
|
||||||
|
diff_patch_hunk *hunk;
|
||||||
|
|
||||||
|
GIT_UNUSED(delta);
|
||||||
|
|
||||||
|
hunk = git_array_alloc(patch->hunks);
|
||||||
|
GITERR_CHECK_ALLOC(hunk);
|
||||||
|
|
||||||
|
memcpy(&hunk->range, range, sizeof(hunk->range));
|
||||||
|
|
||||||
|
assert(header_len + 1 < sizeof(hunk->header));
|
||||||
|
memcpy(&hunk->header, header, header_len);
|
||||||
|
hunk->header[header_len] = '\0';
|
||||||
|
hunk->header_len = header_len;
|
||||||
|
|
||||||
|
hunk->line_start = git_array_size(patch->lines);
|
||||||
|
hunk->line_count = 0;
|
||||||
|
|
||||||
|
patch->oldno = range->old_start;
|
||||||
|
patch->newno = range->new_start;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_patch_line_cb(
|
||||||
|
const git_diff_delta *delta,
|
||||||
|
const git_diff_range *range,
|
||||||
|
char line_origin,
|
||||||
|
const char *content,
|
||||||
|
size_t content_len,
|
||||||
|
void *payload)
|
||||||
|
{
|
||||||
|
git_diff_patch *patch = payload;
|
||||||
|
diff_patch_hunk *hunk;
|
||||||
|
diff_patch_line *line;
|
||||||
|
|
||||||
|
GIT_UNUSED(delta);
|
||||||
|
GIT_UNUSED(range);
|
||||||
|
|
||||||
|
hunk = git_array_last(patch->hunks);
|
||||||
|
GITERR_CHECK_ALLOC(hunk);
|
||||||
|
|
||||||
|
line = git_array_alloc(patch->lines);
|
||||||
|
GITERR_CHECK_ALLOC(line);
|
||||||
|
|
||||||
|
line->ptr = content;
|
||||||
|
line->len = content_len;
|
||||||
|
line->origin = line_origin;
|
||||||
|
|
||||||
|
patch->content_size += content_len;
|
||||||
|
|
||||||
|
/* do some bookkeeping so we can provide old/new line numbers */
|
||||||
|
|
||||||
|
for (line->lines = 0; content_len > 0; --content_len) {
|
||||||
|
if (*content++ == '\n')
|
||||||
|
++line->lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (line_origin) {
|
||||||
|
case GIT_DIFF_LINE_ADDITION:
|
||||||
|
case GIT_DIFF_LINE_DEL_EOFNL:
|
||||||
|
line->oldno = -1;
|
||||||
|
line->newno = patch->newno;
|
||||||
|
patch->newno += line->lines;
|
||||||
|
break;
|
||||||
|
case GIT_DIFF_LINE_DELETION:
|
||||||
|
case GIT_DIFF_LINE_ADD_EOFNL:
|
||||||
|
line->oldno = patch->oldno;
|
||||||
|
line->newno = -1;
|
||||||
|
patch->oldno += line->lines;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
line->oldno = patch->oldno;
|
||||||
|
line->newno = patch->newno;
|
||||||
|
patch->oldno += line->lines;
|
||||||
|
patch->newno += line->lines;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
hunk->line_count++;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void diff_output_init(
|
||||||
|
git_diff_output *out,
|
||||||
|
const git_diff_options *opts,
|
||||||
|
git_diff_file_cb file_cb,
|
||||||
|
git_diff_hunk_cb hunk_cb,
|
||||||
|
git_diff_data_cb data_cb,
|
||||||
|
void *payload)
|
||||||
|
{
|
||||||
|
GIT_UNUSED(opts);
|
||||||
|
|
||||||
|
memset(out, 0, sizeof(*out));
|
||||||
|
|
||||||
|
out->file_cb = file_cb;
|
||||||
|
out->hunk_cb = hunk_cb;
|
||||||
|
out->data_cb = data_cb;
|
||||||
|
out->payload = payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void diff_output_to_patch(git_diff_output *out, git_diff_patch *patch)
|
||||||
|
{
|
||||||
|
diff_output_init(
|
||||||
|
out, NULL,
|
||||||
|
diff_patch_file_cb, diff_patch_hunk_cb, diff_patch_line_cb, patch);
|
||||||
|
}
|
46
src/diff_patch.h
Normal file
46
src/diff_patch.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
* 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_diff_patch_h__
|
||||||
|
#define INCLUDE_diff_patch_h__
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "diff.h"
|
||||||
|
#include "diff_file.h"
|
||||||
|
#include "array.h"
|
||||||
|
|
||||||
|
extern git_diff_list *git_diff_patch__diff(git_diff_patch *);
|
||||||
|
|
||||||
|
extern git_diff_driver *git_diff_patch__driver(git_diff_patch *);
|
||||||
|
|
||||||
|
extern void git_diff_patch__old_data(char **, size_t *, git_diff_patch *);
|
||||||
|
extern void git_diff_patch__new_data(char **, size_t *, git_diff_patch *);
|
||||||
|
|
||||||
|
extern int git_diff_patch__invoke_callbacks(
|
||||||
|
git_diff_patch *patch,
|
||||||
|
git_diff_file_cb file_cb,
|
||||||
|
git_diff_hunk_cb hunk_cb,
|
||||||
|
git_diff_data_cb line_cb,
|
||||||
|
void *payload);
|
||||||
|
|
||||||
|
typedef struct git_diff_output git_diff_output;
|
||||||
|
struct git_diff_output {
|
||||||
|
/* these callbacks are issued with the diff data */
|
||||||
|
git_diff_file_cb file_cb;
|
||||||
|
git_diff_hunk_cb hunk_cb;
|
||||||
|
git_diff_data_cb data_cb;
|
||||||
|
void *payload;
|
||||||
|
|
||||||
|
/* this records the actual error in cases where it may be obscured */
|
||||||
|
int error;
|
||||||
|
|
||||||
|
/* this callback is used to do the diff and drive the other callbacks.
|
||||||
|
* see diff_xdiff.h for how to use this in practice for now.
|
||||||
|
*/
|
||||||
|
int (*diff_cb)(git_diff_output *output, git_diff_patch *patch);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
430
src/diff_print.c
Normal file
430
src/diff_print.c
Normal file
@ -0,0 +1,430 @@
|
|||||||
|
/*
|
||||||
|
* 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 "diff.h"
|
||||||
|
#include "diff_patch.h"
|
||||||
|
#include "buffer.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
git_diff_list *diff;
|
||||||
|
git_diff_data_cb print_cb;
|
||||||
|
void *payload;
|
||||||
|
git_buf *buf;
|
||||||
|
int oid_strlen;
|
||||||
|
} diff_print_info;
|
||||||
|
|
||||||
|
static int diff_print_info_init(
|
||||||
|
diff_print_info *pi,
|
||||||
|
git_buf *out, git_diff_list *diff, git_diff_data_cb cb, void *payload)
|
||||||
|
{
|
||||||
|
pi->diff = diff;
|
||||||
|
pi->print_cb = cb;
|
||||||
|
pi->payload = payload;
|
||||||
|
pi->buf = out;
|
||||||
|
|
||||||
|
if (!diff || !diff->repo)
|
||||||
|
pi->oid_strlen = GIT_ABBREV_DEFAULT;
|
||||||
|
else if (git_repository__cvar(
|
||||||
|
&pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
pi->oid_strlen += 1; /* for NUL byte */
|
||||||
|
|
||||||
|
if (pi->oid_strlen < 2)
|
||||||
|
pi->oid_strlen = 2;
|
||||||
|
else if (pi->oid_strlen > GIT_OID_HEXSZ + 1)
|
||||||
|
pi->oid_strlen = GIT_OID_HEXSZ + 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char diff_pick_suffix(int mode)
|
||||||
|
{
|
||||||
|
if (S_ISDIR(mode))
|
||||||
|
return '/';
|
||||||
|
else if (mode & 0100) /* -V536 */
|
||||||
|
/* in git, modes are very regular, so we must have 0100755 mode */
|
||||||
|
return '*';
|
||||||
|
else
|
||||||
|
return ' ';
|
||||||
|
}
|
||||||
|
|
||||||
|
char git_diff_status_char(git_delta_t status)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int callback_error(void)
|
||||||
|
{
|
||||||
|
giterr_clear();
|
||||||
|
return GIT_EUSER;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_print_one_compact(
|
||||||
|
const git_diff_delta *delta, float progress, void *data)
|
||||||
|
{
|
||||||
|
diff_print_info *pi = data;
|
||||||
|
git_buf *out = pi->buf;
|
||||||
|
char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
|
||||||
|
int (*strcomp)(const char *, const char *) =
|
||||||
|
pi->diff ? pi->diff->strcomp : git__strcmp;
|
||||||
|
|
||||||
|
GIT_UNUSED(progress);
|
||||||
|
|
||||||
|
if (code == ' ')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
old_suffix = diff_pick_suffix(delta->old_file.mode);
|
||||||
|
new_suffix = diff_pick_suffix(delta->new_file.mode);
|
||||||
|
|
||||||
|
git_buf_clear(out);
|
||||||
|
|
||||||
|
if (delta->old_file.path != delta->new_file.path &&
|
||||||
|
strcomp(delta->old_file.path,delta->new_file.path) != 0)
|
||||||
|
git_buf_printf(out, "%c\t%s%c -> %s%c\n", code,
|
||||||
|
delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
|
||||||
|
else if (delta->old_file.mode != delta->new_file.mode &&
|
||||||
|
delta->old_file.mode != 0 && delta->new_file.mode != 0)
|
||||||
|
git_buf_printf(out, "%c\t%s%c (%o -> %o)\n", code,
|
||||||
|
delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode);
|
||||||
|
else if (old_suffix != ' ')
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
|
||||||
|
git_buf_cstr(out), git_buf_len(out), pi->payload))
|
||||||
|
return callback_error();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* print a git_diff_list to a print callback in compact format */
|
||||||
|
int git_diff_print_compact(
|
||||||
|
git_diff_list *diff,
|
||||||
|
git_diff_data_cb print_cb,
|
||||||
|
void *payload)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
git_buf buf = GIT_BUF_INIT;
|
||||||
|
diff_print_info pi;
|
||||||
|
|
||||||
|
if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
|
||||||
|
error = git_diff_foreach(diff, diff_print_one_compact, NULL, NULL, &pi);
|
||||||
|
|
||||||
|
git_buf_free(&buf);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_print_one_raw(
|
||||||
|
const git_diff_delta *delta, float progress, void *data)
|
||||||
|
{
|
||||||
|
diff_print_info *pi = data;
|
||||||
|
git_buf *out = pi->buf;
|
||||||
|
char code = git_diff_status_char(delta->status);
|
||||||
|
char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
|
||||||
|
|
||||||
|
GIT_UNUSED(progress);
|
||||||
|
|
||||||
|
if (code == ' ')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
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_buf_printf(
|
||||||
|
out, ":%06o %06o %s... %s... %c",
|
||||||
|
delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code);
|
||||||
|
|
||||||
|
if (delta->similarity > 0)
|
||||||
|
git_buf_printf(out, "%03u", delta->similarity);
|
||||||
|
|
||||||
|
if (delta->old_file.path != delta->new_file.path)
|
||||||
|
git_buf_printf(
|
||||||
|
out, "\t%s %s\n", delta->old_file.path, delta->new_file.path);
|
||||||
|
else
|
||||||
|
git_buf_printf(
|
||||||
|
out, "\t%s\n", delta->old_file.path ?
|
||||||
|
delta->old_file.path : delta->new_file.path);
|
||||||
|
|
||||||
|
if (git_buf_oom(out))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
|
||||||
|
git_buf_cstr(out), git_buf_len(out), pi->payload))
|
||||||
|
return callback_error();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* print a git_diff_list to a print callback in raw output format */
|
||||||
|
int git_diff_print_raw(
|
||||||
|
git_diff_list *diff,
|
||||||
|
git_diff_data_cb print_cb,
|
||||||
|
void *payload)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
git_buf buf = GIT_BUF_INIT;
|
||||||
|
diff_print_info pi;
|
||||||
|
|
||||||
|
if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
|
||||||
|
error = git_diff_foreach(diff, diff_print_one_raw, NULL, NULL, &pi);
|
||||||
|
|
||||||
|
git_buf_free(&buf);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_print_oid_range(diff_print_info *pi, const git_diff_delta *delta)
|
||||||
|
{
|
||||||
|
git_buf *out = pi->buf;
|
||||||
|
char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
|
||||||
|
|
||||||
|
git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid);
|
||||||
|
git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid);
|
||||||
|
|
||||||
|
/* TODO: Match git diff more closely */
|
||||||
|
if (delta->old_file.mode == delta->new_file.mode) {
|
||||||
|
git_buf_printf(out, "index %s..%s %o\n",
|
||||||
|
start_oid, end_oid, delta->old_file.mode);
|
||||||
|
} else {
|
||||||
|
if (delta->old_file.mode == 0) {
|
||||||
|
git_buf_printf(out, "new file mode %o\n", delta->new_file.mode);
|
||||||
|
} else if (delta->new_file.mode == 0) {
|
||||||
|
git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode);
|
||||||
|
} else {
|
||||||
|
git_buf_printf(out, "old mode %o\n", delta->old_file.mode);
|
||||||
|
git_buf_printf(out, "new mode %o\n", delta->new_file.mode);
|
||||||
|
}
|
||||||
|
git_buf_printf(out, "index %s..%s\n", start_oid, end_oid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (git_buf_oom(out))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_print_patch_file(
|
||||||
|
const git_diff_delta *delta, float progress, void *data)
|
||||||
|
{
|
||||||
|
diff_print_info *pi = data;
|
||||||
|
const char *oldpfx = pi->diff ? pi->diff->opts.old_prefix : NULL;
|
||||||
|
const char *oldpath = delta->old_file.path;
|
||||||
|
const char *newpfx = pi->diff ? pi->diff->opts.new_prefix : NULL;
|
||||||
|
const char *newpath = delta->new_file.path;
|
||||||
|
uint32_t opts_flags = pi->diff ? pi->diff->opts.flags : GIT_DIFF_NORMAL;
|
||||||
|
|
||||||
|
GIT_UNUSED(progress);
|
||||||
|
|
||||||
|
if (S_ISDIR(delta->new_file.mode) ||
|
||||||
|
delta->status == GIT_DELTA_UNMODIFIED ||
|
||||||
|
delta->status == GIT_DELTA_IGNORED ||
|
||||||
|
(delta->status == GIT_DELTA_UNTRACKED &&
|
||||||
|
(opts_flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!oldpfx)
|
||||||
|
oldpfx = DIFF_OLD_PREFIX_DEFAULT;
|
||||||
|
if (!newpfx)
|
||||||
|
newpfx = DIFF_NEW_PREFIX_DEFAULT;
|
||||||
|
|
||||||
|
git_buf_clear(pi->buf);
|
||||||
|
git_buf_printf(pi->buf, "diff --git %s%s %s%s\n",
|
||||||
|
oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
|
||||||
|
|
||||||
|
if (diff_print_oid_range(pi, delta) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (git_oid_iszero(&delta->old_file.oid)) {
|
||||||
|
oldpfx = "";
|
||||||
|
oldpath = "/dev/null";
|
||||||
|
}
|
||||||
|
if (git_oid_iszero(&delta->new_file.oid)) {
|
||||||
|
newpfx = "";
|
||||||
|
newpath = "/dev/null";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) {
|
||||||
|
git_buf_printf(pi->buf, "--- %s%s\n", oldpfx, oldpath);
|
||||||
|
git_buf_printf(pi->buf, "+++ %s%s\n", newpfx, newpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (git_buf_oom(pi->buf))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
|
||||||
|
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||||
|
return callback_error();
|
||||||
|
|
||||||
|
if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
git_buf_clear(pi->buf);
|
||||||
|
git_buf_printf(
|
||||||
|
pi->buf, "Binary files %s%s and %s%s differ\n",
|
||||||
|
oldpfx, oldpath, newpfx, newpath);
|
||||||
|
if (git_buf_oom(pi->buf))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_BINARY,
|
||||||
|
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||||
|
return callback_error();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_print_patch_hunk(
|
||||||
|
const git_diff_delta *d,
|
||||||
|
const git_diff_range *r,
|
||||||
|
const char *header,
|
||||||
|
size_t header_len,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
diff_print_info *pi = data;
|
||||||
|
|
||||||
|
if (S_ISDIR(d->new_file.mode))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
git_buf_clear(pi->buf);
|
||||||
|
if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (pi->print_cb(d, r, GIT_DIFF_LINE_HUNK_HDR,
|
||||||
|
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||||
|
return callback_error();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_print_patch_line(
|
||||||
|
const git_diff_delta *delta,
|
||||||
|
const git_diff_range *range,
|
||||||
|
char line_origin, /* GIT_DIFF_LINE value from above */
|
||||||
|
const char *content,
|
||||||
|
size_t content_len,
|
||||||
|
void *data)
|
||||||
|
{
|
||||||
|
diff_print_info *pi = data;
|
||||||
|
|
||||||
|
if (S_ISDIR(delta->new_file.mode))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
git_buf_clear(pi->buf);
|
||||||
|
|
||||||
|
if (line_origin == GIT_DIFF_LINE_ADDITION ||
|
||||||
|
line_origin == GIT_DIFF_LINE_DELETION ||
|
||||||
|
line_origin == GIT_DIFF_LINE_CONTEXT)
|
||||||
|
git_buf_printf(pi->buf, "%c%.*s", line_origin, (int)content_len, content);
|
||||||
|
else if (content_len > 0)
|
||||||
|
git_buf_printf(pi->buf, "%.*s", (int)content_len, content);
|
||||||
|
|
||||||
|
if (git_buf_oom(pi->buf))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (pi->print_cb(delta, range, line_origin,
|
||||||
|
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||||
|
return callback_error();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* print a git_diff_list to an output callback in patch format */
|
||||||
|
int git_diff_print_patch(
|
||||||
|
git_diff_list *diff,
|
||||||
|
git_diff_data_cb print_cb,
|
||||||
|
void *payload)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
git_buf buf = GIT_BUF_INIT;
|
||||||
|
diff_print_info pi;
|
||||||
|
|
||||||
|
if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
|
||||||
|
error = git_diff_foreach(
|
||||||
|
diff, diff_print_patch_file, diff_print_patch_hunk,
|
||||||
|
diff_print_patch_line, &pi);
|
||||||
|
|
||||||
|
git_buf_free(&buf);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* print a git_diff_patch to an output callback */
|
||||||
|
int git_diff_patch_print(
|
||||||
|
git_diff_patch *patch,
|
||||||
|
git_diff_data_cb print_cb,
|
||||||
|
void *payload)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
git_buf temp = GIT_BUF_INIT;
|
||||||
|
diff_print_info pi;
|
||||||
|
|
||||||
|
assert(patch && print_cb);
|
||||||
|
|
||||||
|
if (!(error = diff_print_info_init(
|
||||||
|
&pi, &temp, git_diff_patch__diff(patch), print_cb, payload)))
|
||||||
|
error = git_diff_patch__invoke_callbacks(
|
||||||
|
patch, diff_print_patch_file, diff_print_patch_hunk,
|
||||||
|
diff_print_patch_line, &pi);
|
||||||
|
|
||||||
|
git_buf_free(&temp);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int diff_print_to_buffer_cb(
|
||||||
|
const git_diff_delta *delta,
|
||||||
|
const git_diff_range *range,
|
||||||
|
char line_origin,
|
||||||
|
const char *content,
|
||||||
|
size_t content_len,
|
||||||
|
void *payload)
|
||||||
|
{
|
||||||
|
git_buf *output = payload;
|
||||||
|
GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(line_origin);
|
||||||
|
return git_buf_put(output, content, content_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* print a git_diff_patch to a string buffer */
|
||||||
|
int git_diff_patch_to_str(
|
||||||
|
char **string,
|
||||||
|
git_diff_patch *patch)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
git_buf output = GIT_BUF_INIT;
|
||||||
|
|
||||||
|
error = git_diff_patch_print(patch, diff_print_to_buffer_cb, &output);
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
|
||||||
|
*string = git_buf_detach(&output);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
687
src/diff_tform.c
687
src/diff_tform.c
@ -5,10 +5,14 @@
|
|||||||
* a Linking Exception. For full terms see the included COPYING file.
|
* a Linking Exception. For full terms see the included COPYING file.
|
||||||
*/
|
*/
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "diff.h"
|
|
||||||
#include "git2/config.h"
|
#include "git2/config.h"
|
||||||
#include "git2/blob.h"
|
#include "git2/blob.h"
|
||||||
|
|
||||||
|
#include "diff.h"
|
||||||
#include "hashsig.h"
|
#include "hashsig.h"
|
||||||
|
#include "path.h"
|
||||||
|
#include "fileops.h"
|
||||||
|
|
||||||
static git_diff_delta *diff_delta__dup(
|
static git_diff_delta *diff_delta__dup(
|
||||||
const git_diff_delta *d, git_pool *pool)
|
const git_diff_delta *d, git_pool *pool)
|
||||||
@ -18,12 +22,15 @@ static git_diff_delta *diff_delta__dup(
|
|||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
memcpy(delta, d, sizeof(git_diff_delta));
|
memcpy(delta, d, sizeof(git_diff_delta));
|
||||||
|
GIT_DIFF_FLAG__CLEAR_INTERNAL(delta->flags);
|
||||||
|
|
||||||
delta->old_file.path = git_pool_strdup(pool, d->old_file.path);
|
if (d->old_file.path != NULL) {
|
||||||
if (delta->old_file.path == NULL)
|
delta->old_file.path = git_pool_strdup(pool, d->old_file.path);
|
||||||
goto fail;
|
if (delta->old_file.path == NULL)
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
if (d->new_file.path != d->old_file.path) {
|
if (d->new_file.path != d->old_file.path && d->new_file.path != NULL) {
|
||||||
delta->new_file.path = git_pool_strdup(pool, d->new_file.path);
|
delta->new_file.path = git_pool_strdup(pool, d->new_file.path);
|
||||||
if (delta->new_file.path == NULL)
|
if (delta->new_file.path == NULL)
|
||||||
goto fail;
|
goto fail;
|
||||||
@ -170,7 +177,7 @@ int git_diff_merge(
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_similar__hashsig_for_file(
|
int git_diff_find_similar__hashsig_for_file(
|
||||||
void **out, const git_diff_file *f, const char *path, void *p)
|
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)p;
|
||||||
@ -178,7 +185,7 @@ static int find_similar__hashsig_for_file(
|
|||||||
|
|
||||||
GIT_UNUSED(f);
|
GIT_UNUSED(f);
|
||||||
error = git_hashsig_create_fromfile((git_hashsig **)out, path, opt);
|
error = git_hashsig_create_fromfile((git_hashsig **)out, path, opt);
|
||||||
|
|
||||||
if (error == GIT_EBUFS) {
|
if (error == GIT_EBUFS) {
|
||||||
error = 0;
|
error = 0;
|
||||||
giterr_clear();
|
giterr_clear();
|
||||||
@ -187,15 +194,15 @@ static int find_similar__hashsig_for_file(
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_similar__hashsig_for_buf(
|
int git_diff_find_similar__hashsig_for_buf(
|
||||||
void **out, const git_diff_file *f, const char *buf, size_t len, void *p)
|
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)p;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
GIT_UNUSED(f);
|
GIT_UNUSED(f);
|
||||||
error = git_hashsig_create((git_hashsig **)out, buf, len, opt);
|
error = git_hashsig_create((git_hashsig **)out, buf, len, opt);
|
||||||
|
|
||||||
if (error == GIT_EBUFS) {
|
if (error == GIT_EBUFS) {
|
||||||
error = 0;
|
error = 0;
|
||||||
giterr_clear();
|
giterr_clear();
|
||||||
@ -204,13 +211,13 @@ static int find_similar__hashsig_for_buf(
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void find_similar__hashsig_free(void *sig, void *payload)
|
void git_diff_find_similar__hashsig_free(void *sig, void *payload)
|
||||||
{
|
{
|
||||||
GIT_UNUSED(payload);
|
GIT_UNUSED(payload);
|
||||||
git_hashsig_free(sig);
|
git_hashsig_free(sig);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int find_similar__calc_similarity(
|
int git_diff_find_similar__calc_similarity(
|
||||||
int *score, void *siga, void *sigb, void *payload)
|
int *score, void *siga, void *sigb, void *payload)
|
||||||
{
|
{
|
||||||
GIT_UNUSED(payload);
|
GIT_UNUSED(payload);
|
||||||
@ -220,7 +227,7 @@ static int find_similar__calc_similarity(
|
|||||||
|
|
||||||
#define DEFAULT_THRESHOLD 50
|
#define DEFAULT_THRESHOLD 50
|
||||||
#define DEFAULT_BREAK_REWRITE_THRESHOLD 60
|
#define DEFAULT_BREAK_REWRITE_THRESHOLD 60
|
||||||
#define DEFAULT_TARGET_LIMIT 200
|
#define DEFAULT_RENAME_LIMIT 200
|
||||||
|
|
||||||
static int normalize_find_opts(
|
static int normalize_find_opts(
|
||||||
git_diff_list *diff,
|
git_diff_list *diff,
|
||||||
@ -253,12 +260,25 @@ static int normalize_find_opts(
|
|||||||
|
|
||||||
/* some flags imply others */
|
/* some flags imply others */
|
||||||
|
|
||||||
|
if (opts->flags & GIT_DIFF_FIND_EXACT_MATCH_ONLY) {
|
||||||
|
/* if we are only looking for exact matches, then don't turn
|
||||||
|
* MODIFIED items into ADD/DELETE pairs because it's too picky
|
||||||
|
*/
|
||||||
|
opts->flags &= ~(GIT_DIFF_FIND_REWRITES | GIT_DIFF_BREAK_REWRITES);
|
||||||
|
|
||||||
|
/* similarly, don't look for self-rewrites to split */
|
||||||
|
opts->flags &= ~GIT_DIFF_FIND_RENAMES_FROM_REWRITES;
|
||||||
|
}
|
||||||
|
|
||||||
if (opts->flags & GIT_DIFF_FIND_RENAMES_FROM_REWRITES)
|
if (opts->flags & GIT_DIFF_FIND_RENAMES_FROM_REWRITES)
|
||||||
opts->flags |= GIT_DIFF_FIND_RENAMES;
|
opts->flags |= GIT_DIFF_FIND_RENAMES;
|
||||||
|
|
||||||
if (opts->flags & GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED)
|
if (opts->flags & GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED)
|
||||||
opts->flags |= GIT_DIFF_FIND_COPIES;
|
opts->flags |= GIT_DIFF_FIND_COPIES;
|
||||||
|
|
||||||
|
if (opts->flags & GIT_DIFF_BREAK_REWRITES)
|
||||||
|
opts->flags |= GIT_DIFF_FIND_REWRITES;
|
||||||
|
|
||||||
#define USE_DEFAULT(X) ((X) == 0 || (X) > 100)
|
#define USE_DEFAULT(X) ((X) == 0 || (X) > 100)
|
||||||
|
|
||||||
if (USE_DEFAULT(opts->rename_threshold))
|
if (USE_DEFAULT(opts->rename_threshold))
|
||||||
@ -275,15 +295,15 @@ static int normalize_find_opts(
|
|||||||
|
|
||||||
#undef USE_DEFAULT
|
#undef USE_DEFAULT
|
||||||
|
|
||||||
if (!opts->target_limit) {
|
if (!opts->rename_limit) {
|
||||||
int32_t limit = 0;
|
int32_t limit = 0;
|
||||||
|
|
||||||
opts->target_limit = DEFAULT_TARGET_LIMIT;
|
opts->rename_limit = DEFAULT_RENAME_LIMIT;
|
||||||
|
|
||||||
if (git_config_get_int32(&limit, cfg, "diff.renameLimit") < 0)
|
if (git_config_get_int32(&limit, cfg, "diff.renameLimit") < 0)
|
||||||
giterr_clear();
|
giterr_clear();
|
||||||
else if (limit > 0)
|
else if (limit > 0)
|
||||||
opts->target_limit = limit;
|
opts->rename_limit = limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* assign the internal metric with whitespace flag as payload */
|
/* assign the internal metric with whitespace flag as payload */
|
||||||
@ -291,10 +311,10 @@ static int normalize_find_opts(
|
|||||||
opts->metric = git__malloc(sizeof(git_diff_similarity_metric));
|
opts->metric = git__malloc(sizeof(git_diff_similarity_metric));
|
||||||
GITERR_CHECK_ALLOC(opts->metric);
|
GITERR_CHECK_ALLOC(opts->metric);
|
||||||
|
|
||||||
opts->metric->file_signature = find_similar__hashsig_for_file;
|
opts->metric->file_signature = git_diff_find_similar__hashsig_for_file;
|
||||||
opts->metric->buffer_signature = find_similar__hashsig_for_buf;
|
opts->metric->buffer_signature = git_diff_find_similar__hashsig_for_buf;
|
||||||
opts->metric->free_signature = find_similar__hashsig_free;
|
opts->metric->free_signature = git_diff_find_similar__hashsig_free;
|
||||||
opts->metric->similarity = find_similar__calc_similarity;
|
opts->metric->similarity = git_diff_find_similar__calc_similarity;
|
||||||
|
|
||||||
if (opts->flags & GIT_DIFF_FIND_IGNORE_WHITESPACE)
|
if (opts->flags & GIT_DIFF_FIND_IGNORE_WHITESPACE)
|
||||||
opts->metric->payload = (void *)GIT_HASHSIG_IGNORE_WHITESPACE;
|
opts->metric->payload = (void *)GIT_HASHSIG_IGNORE_WHITESPACE;
|
||||||
@ -307,11 +327,12 @@ static int normalize_find_opts(
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int apply_splits_and_deletes(git_diff_list *diff, size_t expected_size)
|
static int apply_splits_and_deletes(
|
||||||
|
git_diff_list *diff, size_t expected_size, bool actually_split)
|
||||||
{
|
{
|
||||||
git_vector onto = GIT_VECTOR_INIT;
|
git_vector onto = GIT_VECTOR_INIT;
|
||||||
size_t i;
|
size_t i;
|
||||||
git_diff_delta *delta;
|
git_diff_delta *delta, *deleted;
|
||||||
|
|
||||||
if (git_vector_init(&onto, expected_size, git_diff_delta__cmp) < 0)
|
if (git_vector_init(&onto, expected_size, git_diff_delta__cmp) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
@ -321,9 +342,11 @@ static int apply_splits_and_deletes(git_diff_list *diff, size_t expected_size)
|
|||||||
if ((delta->flags & GIT_DIFF_FLAG__TO_DELETE) != 0)
|
if ((delta->flags & GIT_DIFF_FLAG__TO_DELETE) != 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if ((delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) {
|
if ((delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0 && actually_split) {
|
||||||
git_diff_delta *deleted = diff_delta__dup(delta, &diff->pool);
|
delta->similarity = 0;
|
||||||
if (!deleted)
|
|
||||||
|
/* make new record for DELETED side of split */
|
||||||
|
if (!(deleted = diff_delta__dup(delta, &diff->pool)))
|
||||||
goto on_error;
|
goto on_error;
|
||||||
|
|
||||||
deleted->status = GIT_DELTA_DELETED;
|
deleted->status = GIT_DELTA_DELETED;
|
||||||
@ -334,32 +357,46 @@ static int apply_splits_and_deletes(git_diff_list *diff, size_t expected_size)
|
|||||||
if (git_vector_insert(&onto, deleted) < 0)
|
if (git_vector_insert(&onto, deleted) < 0)
|
||||||
goto on_error;
|
goto on_error;
|
||||||
|
|
||||||
delta->status = GIT_DELTA_ADDED;
|
if (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR)
|
||||||
|
delta->status = GIT_DELTA_UNTRACKED;
|
||||||
|
else
|
||||||
|
delta->status = GIT_DELTA_ADDED;
|
||||||
memset(&delta->old_file, 0, sizeof(delta->old_file));
|
memset(&delta->old_file, 0, sizeof(delta->old_file));
|
||||||
delta->old_file.path = delta->new_file.path;
|
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_OID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* clean up delta before inserting into new list */
|
||||||
|
GIT_DIFF_FLAG__CLEAR_INTERNAL(delta->flags);
|
||||||
|
|
||||||
|
if (delta->status != GIT_DELTA_COPIED &&
|
||||||
|
delta->status != GIT_DELTA_RENAMED &&
|
||||||
|
(delta->status != GIT_DELTA_MODIFIED || actually_split))
|
||||||
|
delta->similarity = 0;
|
||||||
|
|
||||||
|
/* insert into new list */
|
||||||
if (git_vector_insert(&onto, delta) < 0)
|
if (git_vector_insert(&onto, delta) < 0)
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* cannot return an error past this point */
|
/* cannot return an error past this point */
|
||||||
git_vector_foreach(&diff->deltas, i, delta)
|
|
||||||
|
/* free deltas from old list that didn't make it to the new one */
|
||||||
|
git_vector_foreach(&diff->deltas, i, delta) {
|
||||||
if ((delta->flags & GIT_DIFF_FLAG__TO_DELETE) != 0)
|
if ((delta->flags & GIT_DIFF_FLAG__TO_DELETE) != 0)
|
||||||
git__free(delta);
|
git__free(delta);
|
||||||
|
}
|
||||||
|
|
||||||
/* swap new delta list into place */
|
/* swap new delta list into place */
|
||||||
git_vector_sort(&onto);
|
|
||||||
git_vector_swap(&diff->deltas, &onto);
|
git_vector_swap(&diff->deltas, &onto);
|
||||||
git_vector_free(&onto);
|
git_vector_free(&onto);
|
||||||
|
git_vector_sort(&diff->deltas);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
on_error:
|
on_error:
|
||||||
git_vector_foreach(&onto, i, delta)
|
git_vector_foreach(&onto, i, delta)
|
||||||
git__free(delta);
|
git__free(delta);
|
||||||
|
|
||||||
git_vector_free(&onto);
|
git_vector_free(&onto);
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
@ -373,21 +410,25 @@ GIT_INLINE(git_diff_file *) similarity_get_file(git_diff_list *diff, size_t idx)
|
|||||||
|
|
||||||
static int similarity_calc(
|
static int similarity_calc(
|
||||||
git_diff_list *diff,
|
git_diff_list *diff,
|
||||||
git_diff_find_options *opts,
|
const git_diff_find_options *opts,
|
||||||
size_t file_idx,
|
size_t file_idx,
|
||||||
void **cache)
|
void **cache)
|
||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
git_diff_file *file = similarity_get_file(diff, file_idx);
|
git_diff_file *file = similarity_get_file(diff, file_idx);
|
||||||
git_iterator_type_t src = (file_idx & 1) ? diff->old_src : diff->new_src;
|
git_iterator_type_t src = (file_idx & 1) ? diff->new_src : diff->old_src;
|
||||||
|
|
||||||
if (src == GIT_ITERATOR_TYPE_WORKDIR) { /* compute hashsig from file */
|
if (src == GIT_ITERATOR_TYPE_WORKDIR) { /* compute hashsig from file */
|
||||||
git_buf path = GIT_BUF_INIT;
|
git_buf path = GIT_BUF_INIT;
|
||||||
|
|
||||||
/* TODO: apply wd-to-odb filters to file data if necessary */
|
/* TODO: apply wd-to-odb filters to file data if necessary */
|
||||||
|
|
||||||
if (!(error = git_buf_joinpath(
|
if ((error = git_buf_joinpath(
|
||||||
&path, git_repository_workdir(diff->repo), file->path)))
|
&path, git_repository_workdir(diff->repo), file->path)) < 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
/* if path is not a regular file, just skip this item */
|
||||||
|
if (git_path_isfile(path.ptr))
|
||||||
error = opts->metric->file_signature(
|
error = opts->metric->file_signature(
|
||||||
&cache[file_idx], file, path.ptr, opts->metric->payload);
|
&cache[file_idx], file, path.ptr, opts->metric->payload);
|
||||||
|
|
||||||
@ -398,8 +439,11 @@ static int similarity_calc(
|
|||||||
|
|
||||||
/* TODO: add max size threshold a la diff? */
|
/* TODO: add max size threshold a la diff? */
|
||||||
|
|
||||||
if ((error = git_blob_lookup(&blob, diff->repo, &file->oid)) < 0)
|
if (git_blob_lookup(&blob, diff->repo, &file->oid) < 0) {
|
||||||
return error;
|
/* if lookup fails, just skip this item in similarity calc */
|
||||||
|
giterr_clear();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
blobsize = git_blob_rawsize(blob);
|
blobsize = git_blob_rawsize(blob);
|
||||||
if (!git__is_sizet(blobsize)) /* ? what to do ? */
|
if (!git__is_sizet(blobsize)) /* ? what to do ? */
|
||||||
@ -415,268 +459,485 @@ static int similarity_calc(
|
|||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define FLAG_SET(opts,flag_name) (((opts)->flags & flag_name) != 0)
|
||||||
|
|
||||||
|
/* - score < 0 means files cannot be compared
|
||||||
|
* - score >= 100 means files are exact match
|
||||||
|
* - score == 0 means files are completely different
|
||||||
|
*/
|
||||||
static int similarity_measure(
|
static int similarity_measure(
|
||||||
|
int *score,
|
||||||
git_diff_list *diff,
|
git_diff_list *diff,
|
||||||
git_diff_find_options *opts,
|
const git_diff_find_options *opts,
|
||||||
void **cache,
|
void **cache,
|
||||||
size_t a_idx,
|
size_t a_idx,
|
||||||
size_t b_idx)
|
size_t b_idx)
|
||||||
{
|
{
|
||||||
int score = 0;
|
|
||||||
git_diff_file *a_file = similarity_get_file(diff, a_idx);
|
git_diff_file *a_file = similarity_get_file(diff, a_idx);
|
||||||
git_diff_file *b_file = similarity_get_file(diff, b_idx);
|
git_diff_file *b_file = similarity_get_file(diff, b_idx);
|
||||||
|
bool exact_match = FLAG_SET(opts, GIT_DIFF_FIND_EXACT_MATCH_ONLY);
|
||||||
|
|
||||||
|
*score = -1;
|
||||||
|
|
||||||
|
/* don't try to compare files of different types */
|
||||||
if (GIT_MODE_TYPE(a_file->mode) != GIT_MODE_TYPE(b_file->mode))
|
if (GIT_MODE_TYPE(a_file->mode) != GIT_MODE_TYPE(b_file->mode))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (git_oid_cmp(&a_file->oid, &b_file->oid) == 0)
|
/* if exact match is requested, force calculation of missing OIDs now */
|
||||||
return 100;
|
if (exact_match) {
|
||||||
|
if (git_oid_iszero(&a_file->oid) &&
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (git_oid_iszero(&b_file->oid) &&
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check OID match as a quick test */
|
||||||
|
if (git_oid__cmp(&a_file->oid, &b_file->oid) == 0) {
|
||||||
|
*score = 100;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* don't calculate signatures if we are doing exact match */
|
||||||
|
if (exact_match) {
|
||||||
|
*score = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* update signature cache if needed */
|
/* update signature cache if needed */
|
||||||
if (!cache[a_idx] && similarity_calc(diff, opts, a_idx, cache) < 0)
|
if (!cache[a_idx] && similarity_calc(diff, opts, a_idx, cache) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
if (!cache[b_idx] && similarity_calc(diff, opts, b_idx, cache) < 0)
|
if (!cache[b_idx] && similarity_calc(diff, opts, b_idx, cache) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* some metrics may not wish to process this file (too big / too small) */
|
/* some metrics may not wish to process this file (too big / too small) */
|
||||||
if (!cache[a_idx] || !cache[b_idx])
|
if (!cache[a_idx] || !cache[b_idx])
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* compare signatures */
|
/* compare signatures */
|
||||||
if (opts->metric->similarity(
|
return opts->metric->similarity(
|
||||||
&score, cache[a_idx], cache[b_idx], opts->metric->payload) < 0)
|
score, cache[a_idx], cache[b_idx], opts->metric->payload);
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* clip score */
|
|
||||||
if (score < 0)
|
|
||||||
score = 0;
|
|
||||||
else if (score > 100)
|
|
||||||
score = 100;
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#define FLAG_SET(opts,flag_name) ((opts.flags & flag_name) != 0)
|
static int calc_self_similarity(
|
||||||
|
git_diff_list *diff,
|
||||||
|
const git_diff_find_options *opts,
|
||||||
|
size_t delta_idx,
|
||||||
|
void **cache)
|
||||||
|
{
|
||||||
|
int error, similarity = -1;
|
||||||
|
git_diff_delta *delta = GIT_VECTOR_GET(&diff->deltas, delta_idx);
|
||||||
|
|
||||||
|
if ((delta->flags & GIT_DIFF_FLAG__HAS_SELF_SIMILARITY) != 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
error = similarity_measure(
|
||||||
|
&similarity, diff, opts, cache, 2 * delta_idx, 2 * delta_idx + 1);
|
||||||
|
if (error < 0)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
if (similarity >= 0) {
|
||||||
|
delta->similarity = (uint32_t)similarity;
|
||||||
|
delta->flags |= GIT_DIFF_FLAG__HAS_SELF_SIMILARITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_rename_target(
|
||||||
|
git_diff_list *diff,
|
||||||
|
const git_diff_find_options *opts,
|
||||||
|
size_t delta_idx,
|
||||||
|
void **cache)
|
||||||
|
{
|
||||||
|
git_diff_delta *delta = GIT_VECTOR_GET(&diff->deltas, delta_idx);
|
||||||
|
|
||||||
|
/* skip things that aren't plain blobs */
|
||||||
|
if (!GIT_MODE_ISBLOB(delta->new_file.mode))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* only consider ADDED, RENAMED, COPIED, and split MODIFIED as
|
||||||
|
* targets; maybe include UNTRACKED and IGNORED if requested.
|
||||||
|
*/
|
||||||
|
switch (delta->status) {
|
||||||
|
case GIT_DELTA_UNMODIFIED:
|
||||||
|
case GIT_DELTA_DELETED:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case GIT_DELTA_MODIFIED:
|
||||||
|
if (!FLAG_SET(opts, GIT_DIFF_FIND_REWRITES) &&
|
||||||
|
!FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (calc_self_similarity(diff, opts, delta_idx, cache) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (FLAG_SET(opts, GIT_DIFF_BREAK_REWRITES) &&
|
||||||
|
delta->similarity < opts->break_rewrite_threshold) {
|
||||||
|
delta->flags |= GIT_DIFF_FLAG__TO_SPLIT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) &&
|
||||||
|
delta->similarity < opts->rename_from_rewrite_threshold)
|
||||||
|
break;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case GIT_DELTA_UNTRACKED:
|
||||||
|
case GIT_DELTA_IGNORED:
|
||||||
|
if (!FLAG_SET(opts, GIT_DIFF_FIND_FOR_UNTRACKED))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: /* all other status values should be checked */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
delta->flags |= GIT_DIFF_FLAG__IS_RENAME_TARGET;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_rename_source(
|
||||||
|
git_diff_list *diff,
|
||||||
|
const git_diff_find_options *opts,
|
||||||
|
size_t delta_idx,
|
||||||
|
void **cache)
|
||||||
|
{
|
||||||
|
git_diff_delta *delta = GIT_VECTOR_GET(&diff->deltas, delta_idx);
|
||||||
|
|
||||||
|
/* skip things that aren't blobs */
|
||||||
|
if (!GIT_MODE_ISBLOB(delta->old_file.mode))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (delta->status) {
|
||||||
|
case GIT_DELTA_ADDED:
|
||||||
|
case GIT_DELTA_UNTRACKED:
|
||||||
|
case GIT_DELTA_IGNORED:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case GIT_DELTA_DELETED:
|
||||||
|
case GIT_DELTA_TYPECHANGE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GIT_DELTA_UNMODIFIED:
|
||||||
|
if (!FLAG_SET(opts, GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: /* MODIFIED, RENAMED, COPIED */
|
||||||
|
/* if we're finding copies, this could be a source */
|
||||||
|
if (FLAG_SET(opts, GIT_DIFF_FIND_COPIES))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* otherwise, this is only a source if we can split it */
|
||||||
|
if (!FLAG_SET(opts, GIT_DIFF_FIND_REWRITES) &&
|
||||||
|
!FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (calc_self_similarity(diff, opts, delta_idx, cache) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (FLAG_SET(opts, GIT_DIFF_BREAK_REWRITES) &&
|
||||||
|
delta->similarity < opts->break_rewrite_threshold) {
|
||||||
|
delta->flags |= GIT_DIFF_FLAG__TO_SPLIT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) &&
|
||||||
|
delta->similarity < opts->rename_from_rewrite_threshold)
|
||||||
|
break;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
delta->flags |= GIT_DIFF_FLAG__IS_RENAME_SOURCE;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
GIT_INLINE(bool) delta_is_split(git_diff_delta *delta)
|
||||||
|
{
|
||||||
|
return (delta->status == GIT_DELTA_TYPECHANGE ||
|
||||||
|
(delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIT_INLINE(bool) delta_is_new_only(git_diff_delta *delta)
|
||||||
|
{
|
||||||
|
return (delta->status == GIT_DELTA_ADDED ||
|
||||||
|
delta->status == GIT_DELTA_UNTRACKED ||
|
||||||
|
delta->status == GIT_DELTA_IGNORED);
|
||||||
|
}
|
||||||
|
|
||||||
|
GIT_INLINE(void) delta_make_rename(
|
||||||
|
git_diff_delta *to, const git_diff_delta *from, uint32_t similarity)
|
||||||
|
{
|
||||||
|
to->status = GIT_DELTA_RENAMED;
|
||||||
|
to->similarity = similarity;
|
||||||
|
memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
|
||||||
|
to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t idx;
|
||||||
|
uint32_t similarity;
|
||||||
|
} diff_find_match;
|
||||||
|
|
||||||
int git_diff_find_similar(
|
int git_diff_find_similar(
|
||||||
git_diff_list *diff,
|
git_diff_list *diff,
|
||||||
git_diff_find_options *given_opts)
|
git_diff_find_options *given_opts)
|
||||||
{
|
{
|
||||||
size_t i, j, cache_size, *matches;
|
size_t i, j, sigcache_size;
|
||||||
int error = 0, similarity;
|
int error = 0, similarity;
|
||||||
git_diff_delta *from, *to;
|
git_diff_delta *from, *to;
|
||||||
git_diff_find_options opts;
|
git_diff_find_options opts;
|
||||||
size_t tried_targets, num_rewrites = 0;
|
size_t num_srcs = 0, num_tgts = 0, tried_srcs = 0, tried_tgts = 0;
|
||||||
void **cache;
|
size_t num_rewrites = 0, num_updates = 0, num_bumped = 0;
|
||||||
|
void **sigcache; /* cache of similarity metric file signatures */
|
||||||
|
diff_find_match *match_srcs = NULL, *match_tgts = NULL, *best_match;
|
||||||
|
git_diff_file swap;
|
||||||
|
|
||||||
if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0)
|
if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
/* TODO: maybe abort if deltas.length > target_limit ??? */
|
/* TODO: maybe abort if deltas.length > rename_limit ??? */
|
||||||
|
if (!git__is_uint32(diff->deltas.length))
|
||||||
|
return 0;
|
||||||
|
|
||||||
cache_size = diff->deltas.length * 2; /* must store b/c length may change */
|
sigcache_size = diff->deltas.length * 2; /* keep size b/c diff may change */
|
||||||
cache = git__calloc(cache_size, sizeof(void *));
|
sigcache = git__calloc(sigcache_size, sizeof(void *));
|
||||||
GITERR_CHECK_ALLOC(cache);
|
GITERR_CHECK_ALLOC(sigcache);
|
||||||
|
|
||||||
matches = git__calloc(diff->deltas.length, sizeof(size_t));
|
/* Label rename sources and targets
|
||||||
GITERR_CHECK_ALLOC(matches);
|
*
|
||||||
|
* This will also set self-similarity scores for MODIFIED files and
|
||||||
|
* mark them for splitting if break-rewrites is enabled
|
||||||
|
*/
|
||||||
|
git_vector_foreach(&diff->deltas, i, to) {
|
||||||
|
if (is_rename_source(diff, &opts, i, sigcache))
|
||||||
|
++num_srcs;
|
||||||
|
|
||||||
/* first break MODIFIED records that are too different (if requested) */
|
if (is_rename_target(diff, &opts, i, sigcache))
|
||||||
|
++num_tgts;
|
||||||
if (FLAG_SET(opts, GIT_DIFF_FIND_AND_BREAK_REWRITES)) {
|
|
||||||
git_vector_foreach(&diff->deltas, i, from) {
|
|
||||||
if (from->status != GIT_DELTA_MODIFIED)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
similarity = similarity_measure(
|
|
||||||
diff, &opts, cache, 2 * i, 2 * i + 1);
|
|
||||||
|
|
||||||
if (similarity < 0) {
|
|
||||||
error = similarity;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((unsigned int)similarity < opts.break_rewrite_threshold) {
|
|
||||||
from->flags |= GIT_DIFF_FLAG__TO_SPLIT;
|
|
||||||
num_rewrites++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* next find the most similar delta for each rename / copy candidate */
|
/* if there are no candidate srcs or tgts, we're done */
|
||||||
|
if (!num_srcs || !num_tgts)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
git_vector_foreach(&diff->deltas, i, from) {
|
match_tgts = git__calloc(diff->deltas.length, sizeof(diff_find_match));
|
||||||
tried_targets = 0;
|
GITERR_CHECK_ALLOC(match_tgts);
|
||||||
|
match_srcs = git__calloc(diff->deltas.length, sizeof(diff_find_match));
|
||||||
|
GITERR_CHECK_ALLOC(match_srcs);
|
||||||
|
|
||||||
/* skip things that aren't blobs */
|
/*
|
||||||
if (GIT_MODE_TYPE(from->old_file.mode) !=
|
* Find best-fit matches for rename / copy candidates
|
||||||
GIT_MODE_TYPE(GIT_FILEMODE_BLOB))
|
*/
|
||||||
|
|
||||||
|
find_best_matches:
|
||||||
|
tried_tgts = num_bumped = 0;
|
||||||
|
|
||||||
|
git_vector_foreach(&diff->deltas, i, to) {
|
||||||
|
/* skip things that are not rename targets */
|
||||||
|
if ((to->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* don't check UNMODIFIED files as source unless given option */
|
tried_srcs = 0;
|
||||||
if (from->status == GIT_DELTA_UNMODIFIED &&
|
|
||||||
!FLAG_SET(opts, GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* skip all but DELETED files unless copy detection is on */
|
git_vector_foreach(&diff->deltas, j, from) {
|
||||||
if (!FLAG_SET(opts, GIT_DIFF_FIND_COPIES) &&
|
/* skip things that are not rename sources */
|
||||||
from->status != GIT_DELTA_DELETED &&
|
if ((from->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) == 0)
|
||||||
(from->flags & GIT_DIFF_FLAG__TO_SPLIT) == 0)
|
continue;
|
||||||
continue;
|
|
||||||
|
|
||||||
git_vector_foreach(&diff->deltas, j, to) {
|
/* calculate similarity for this pair and find best match */
|
||||||
if (i == j)
|
if (i == j)
|
||||||
continue;
|
similarity = -1; /* don't measure self-similarity here */
|
||||||
|
else if ((error = similarity_measure(
|
||||||
/* skip things that aren't blobs */
|
&similarity, diff, &opts, sigcache, 2 * j, 2 * i + 1)) < 0)
|
||||||
if (GIT_MODE_TYPE(to->new_file.mode) !=
|
|
||||||
GIT_MODE_TYPE(GIT_FILEMODE_BLOB))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
switch (to->status) {
|
|
||||||
case GIT_DELTA_ADDED:
|
|
||||||
case GIT_DELTA_UNTRACKED:
|
|
||||||
case GIT_DELTA_RENAMED:
|
|
||||||
case GIT_DELTA_COPIED:
|
|
||||||
break;
|
|
||||||
case GIT_DELTA_MODIFIED:
|
|
||||||
if ((to->flags & GIT_DIFF_FLAG__TO_SPLIT) == 0)
|
|
||||||
continue;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* only the above status values should be checked */
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* cap on maximum files we'll examine (per "from" file) */
|
|
||||||
if (++tried_targets > opts.target_limit)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* calculate similarity and see if this pair beats the
|
|
||||||
* similarity score of the current best pair.
|
|
||||||
*/
|
|
||||||
similarity = similarity_measure(
|
|
||||||
diff, &opts, cache, 2 * i, 2 * j + 1);
|
|
||||||
|
|
||||||
if (similarity < 0) {
|
|
||||||
error = similarity;
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
|
/* if this pairing is better for the src and the tgt, keep it */
|
||||||
|
if (similarity > 0 &&
|
||||||
|
match_tgts[i].similarity < (uint32_t)similarity &&
|
||||||
|
match_srcs[j].similarity < (uint32_t)similarity)
|
||||||
|
{
|
||||||
|
if (match_tgts[i].similarity > 0) {
|
||||||
|
match_tgts[match_srcs[j].idx].similarity = 0;
|
||||||
|
match_srcs[match_tgts[i].idx].similarity = 0;
|
||||||
|
++num_bumped;
|
||||||
|
}
|
||||||
|
|
||||||
|
match_tgts[i].similarity = (uint32_t)similarity;
|
||||||
|
match_tgts[i].idx = (uint32_t)j;
|
||||||
|
|
||||||
|
match_srcs[j].similarity = (uint32_t)similarity;
|
||||||
|
match_srcs[j].idx = (uint32_t)i;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (to->similarity < (unsigned int)similarity) {
|
if (++tried_srcs >= num_srcs)
|
||||||
to->similarity = (unsigned int)similarity;
|
break;
|
||||||
matches[j] = i + 1;
|
|
||||||
}
|
/* cap on maximum targets we'll examine (per "to" file) */
|
||||||
|
if (tried_srcs > opts.rename_limit)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (++tried_tgts >= num_tgts)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* next rewrite the diffs with renames / copies */
|
if (num_bumped > 0) /* try again if we bumped some items */
|
||||||
|
goto find_best_matches;
|
||||||
|
|
||||||
git_vector_foreach(&diff->deltas, j, to) {
|
/*
|
||||||
if (!matches[j]) {
|
* Rewrite the diffs with renames / copies
|
||||||
assert(to->similarity == 0);
|
*/
|
||||||
|
|
||||||
|
tried_tgts = 0;
|
||||||
|
|
||||||
|
git_vector_foreach(&diff->deltas, i, to) {
|
||||||
|
/* skip things that are not rename targets */
|
||||||
|
if ((to->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
i = matches[j] - 1;
|
/* check if this delta was the target of a similarity */
|
||||||
from = GIT_VECTOR_GET(&diff->deltas, i);
|
best_match = &match_tgts[i];
|
||||||
assert(from);
|
if (!best_match->similarity)
|
||||||
|
continue;
|
||||||
|
|
||||||
/* four possible outcomes here:
|
j = best_match->idx;
|
||||||
* 1. old DELETED and if over rename threshold,
|
from = GIT_VECTOR_GET(&diff->deltas, j);
|
||||||
* new becomes RENAMED and old goes away
|
|
||||||
* 2. old SPLIT and if over rename threshold,
|
/* possible scenarios:
|
||||||
* new becomes RENAMED and old becomes ADDED (clear SPLIT)
|
* 1. from DELETE to ADD/UNTRACK/IGNORE = RENAME
|
||||||
* 3. old was MODIFIED but FIND_RENAMES_FROM_REWRITES is on and
|
* 2. from DELETE to SPLIT/TYPECHANGE = RENAME + DELETE
|
||||||
* old is more similar to new than it is to itself, in which
|
* 3. from SPLIT/TYPECHANGE to ADD/UNTRACK/IGNORE = ADD + RENAME
|
||||||
* case, new becomes RENAMED and old becomed ADDED
|
* 4. from SPLIT/TYPECHANGE to SPLIT/TYPECHANGE = RENAME + SPLIT
|
||||||
* 4. otherwise if over copy threshold, new becomes COPIED
|
* 5. from OTHER to ADD/UNTRACK/IGNORE = OTHER + COPY
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (from->status == GIT_DELTA_DELETED) {
|
if (from->status == GIT_DELTA_DELETED) {
|
||||||
if (to->similarity < opts.rename_threshold) {
|
|
||||||
to->similarity = 0;
|
if (delta_is_new_only(to)) {
|
||||||
continue;
|
|
||||||
|
if (best_match->similarity < opts.rename_threshold)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
delta_make_rename(to, from, best_match->similarity);
|
||||||
|
|
||||||
|
from->flags |= GIT_DIFF_FLAG__TO_DELETE;
|
||||||
|
num_rewrites++;
|
||||||
|
} else {
|
||||||
|
assert(delta_is_split(to));
|
||||||
|
|
||||||
|
if (best_match->similarity < opts.rename_from_rewrite_threshold)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
memcpy(&swap, &to->old_file, sizeof(swap));
|
||||||
|
|
||||||
|
delta_make_rename(to, from, best_match->similarity);
|
||||||
|
num_rewrites--;
|
||||||
|
|
||||||
|
from->status = GIT_DELTA_DELETED;
|
||||||
|
memcpy(&from->old_file, &swap, sizeof(from->old_file));
|
||||||
|
memset(&from->new_file, 0, sizeof(from->new_file));
|
||||||
|
from->new_file.path = from->old_file.path;
|
||||||
|
from->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
|
||||||
|
|
||||||
|
num_updates++;
|
||||||
}
|
}
|
||||||
|
|
||||||
to->status = GIT_DELTA_RENAMED;
|
|
||||||
memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
|
|
||||||
|
|
||||||
from->flags |= GIT_DIFF_FLAG__TO_DELETE;
|
|
||||||
num_rewrites++;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (from->status == GIT_DELTA_MODIFIED &&
|
else if (delta_is_split(from)) {
|
||||||
(from->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0)
|
|
||||||
{
|
|
||||||
if (to->similarity < opts.rename_threshold) {
|
|
||||||
to->similarity = 0;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
to->status = GIT_DELTA_RENAMED;
|
if (delta_is_new_only(to)) {
|
||||||
memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
|
|
||||||
|
|
||||||
from->status = GIT_DELTA_ADDED;
|
if (best_match->similarity < opts.rename_threshold)
|
||||||
from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
|
continue;
|
||||||
memset(&from->old_file, 0, sizeof(from->old_file));
|
|
||||||
num_rewrites--;
|
|
||||||
|
|
||||||
continue;
|
delta_make_rename(to, from, best_match->similarity);
|
||||||
}
|
|
||||||
|
|
||||||
if (from->status == GIT_DELTA_MODIFIED &&
|
from->status = (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ?
|
||||||
FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) &&
|
GIT_DELTA_UNTRACKED : GIT_DELTA_ADDED;
|
||||||
to->similarity > opts.rename_threshold)
|
|
||||||
{
|
|
||||||
similarity = similarity_measure(
|
|
||||||
diff, &opts, cache, 2 * i, 2 * i + 1);
|
|
||||||
|
|
||||||
if (similarity < 0) {
|
|
||||||
error = similarity;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((unsigned int)similarity < opts.rename_from_rewrite_threshold) {
|
|
||||||
to->status = GIT_DELTA_RENAMED;
|
|
||||||
memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
|
|
||||||
|
|
||||||
from->status = GIT_DELTA_ADDED;
|
|
||||||
memset(&from->old_file, 0, sizeof(from->old_file));
|
memset(&from->old_file, 0, sizeof(from->old_file));
|
||||||
from->old_file.path = to->old_file.path;
|
from->old_file.path = from->new_file.path;
|
||||||
from->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
|
from->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
|
||||||
|
|
||||||
continue;
|
from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
|
||||||
|
num_rewrites--;
|
||||||
|
|
||||||
|
num_updates++;
|
||||||
|
} else {
|
||||||
|
assert(delta_is_split(from));
|
||||||
|
|
||||||
|
if (best_match->similarity < opts.rename_from_rewrite_threshold)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
memcpy(&swap, &to->old_file, sizeof(swap));
|
||||||
|
|
||||||
|
delta_make_rename(to, from, best_match->similarity);
|
||||||
|
num_rewrites--;
|
||||||
|
num_updates++;
|
||||||
|
|
||||||
|
memcpy(&from->old_file, &swap, sizeof(from->old_file));
|
||||||
|
|
||||||
|
/* if we've just swapped the new element into the correct
|
||||||
|
* place, clear the SPLIT flag
|
||||||
|
*/
|
||||||
|
if (match_tgts[j].idx == i &&
|
||||||
|
match_tgts[j].similarity >
|
||||||
|
opts.rename_from_rewrite_threshold) {
|
||||||
|
|
||||||
|
from->status = GIT_DELTA_RENAMED;
|
||||||
|
from->similarity = match_tgts[j].similarity;
|
||||||
|
match_tgts[j].similarity = 0;
|
||||||
|
from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
|
||||||
|
num_rewrites--;
|
||||||
|
}
|
||||||
|
/* otherwise, if we just overwrote a source, update mapping */
|
||||||
|
else if (j > i && match_srcs[i].similarity > 0) {
|
||||||
|
match_tgts[match_srcs[i].idx].idx = j;
|
||||||
|
}
|
||||||
|
|
||||||
|
num_updates++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (to->similarity < opts.copy_threshold) {
|
else if (delta_is_new_only(to)) {
|
||||||
to->similarity = 0;
|
if (!FLAG_SET(&opts, GIT_DIFF_FIND_COPIES) ||
|
||||||
continue;
|
best_match->similarity < opts.copy_threshold)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
to->status = GIT_DELTA_COPIED;
|
||||||
|
to->similarity = best_match->similarity;
|
||||||
|
memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
|
||||||
|
|
||||||
|
num_updates++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* convert "to" to a COPIED record */
|
|
||||||
to->status = GIT_DELTA_COPIED;
|
|
||||||
memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (num_rewrites > 0) {
|
/*
|
||||||
assert(num_rewrites < diff->deltas.length);
|
* Actually split and delete entries as needed
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (num_rewrites > 0 || num_updates > 0)
|
||||||
error = apply_splits_and_deletes(
|
error = apply_splits_and_deletes(
|
||||||
diff, diff->deltas.length - num_rewrites);
|
diff, diff->deltas.length - num_rewrites,
|
||||||
}
|
FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES));
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
git__free(matches);
|
git__free(match_srcs);
|
||||||
|
git__free(match_tgts);
|
||||||
|
|
||||||
for (i = 0; i < cache_size; ++i) {
|
for (i = 0; i < sigcache_size; ++i) {
|
||||||
if (cache[i] != NULL)
|
if (sigcache[i] != NULL)
|
||||||
opts.metric->free_signature(cache[i], opts.metric->payload);
|
opts.metric->free_signature(sigcache[i], opts.metric->payload);
|
||||||
}
|
}
|
||||||
git__free(cache);
|
git__free(sigcache);
|
||||||
|
|
||||||
if (!given_opts || !given_opts->metric)
|
if (!given_opts || !given_opts->metric)
|
||||||
git__free(opts.metric);
|
git__free(opts.metric);
|
||||||
|
166
src/diff_xdiff.c
Normal file
166
src/diff_xdiff.c
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* 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 "diff.h"
|
||||||
|
#include "diff_driver.h"
|
||||||
|
#include "diff_patch.h"
|
||||||
|
#include "diff_xdiff.h"
|
||||||
|
|
||||||
|
static int git_xdiff_scan_int(const char **str, int *value)
|
||||||
|
{
|
||||||
|
const char *scan = *str;
|
||||||
|
int v = 0, digits = 0;
|
||||||
|
/* find next digit */
|
||||||
|
for (scan = *str; *scan && !git__isdigit(*scan); scan++);
|
||||||
|
/* parse next number */
|
||||||
|
for (; git__isdigit(*scan); scan++, digits++)
|
||||||
|
v = (v * 10) + (*scan - '0');
|
||||||
|
*str = scan;
|
||||||
|
*value = v;
|
||||||
|
return (digits > 0) ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int git_xdiff_parse_hunk(git_diff_range *range, const char *header)
|
||||||
|
{
|
||||||
|
/* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
|
||||||
|
if (*header != '@')
|
||||||
|
return -1;
|
||||||
|
if (git_xdiff_scan_int(&header, &range->old_start) < 0)
|
||||||
|
return -1;
|
||||||
|
if (*header == ',') {
|
||||||
|
if (git_xdiff_scan_int(&header, &range->old_lines) < 0)
|
||||||
|
return -1;
|
||||||
|
} else
|
||||||
|
range->old_lines = 1;
|
||||||
|
if (git_xdiff_scan_int(&header, &range->new_start) < 0)
|
||||||
|
return -1;
|
||||||
|
if (*header == ',') {
|
||||||
|
if (git_xdiff_scan_int(&header, &range->new_lines) < 0)
|
||||||
|
return -1;
|
||||||
|
} else
|
||||||
|
range->new_lines = 1;
|
||||||
|
if (range->old_start < 0 || range->new_start < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
git_xdiff_output *xo;
|
||||||
|
git_diff_patch *patch;
|
||||||
|
git_diff_range range;
|
||||||
|
} git_xdiff_info;
|
||||||
|
|
||||||
|
static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
|
||||||
|
{
|
||||||
|
git_xdiff_info *info = priv;
|
||||||
|
git_diff_patch *patch = info->patch;
|
||||||
|
const git_diff_delta *delta = git_diff_patch_delta(patch);
|
||||||
|
git_diff_output *output = &info->xo->output;
|
||||||
|
|
||||||
|
if (len == 1) {
|
||||||
|
output->error = git_xdiff_parse_hunk(&info->range, bufs[0].ptr);
|
||||||
|
if (output->error < 0)
|
||||||
|
return output->error;
|
||||||
|
|
||||||
|
if (output->hunk_cb != NULL &&
|
||||||
|
output->hunk_cb(delta, &info->range,
|
||||||
|
bufs[0].ptr, bufs[0].size, output->payload))
|
||||||
|
output->error = GIT_EUSER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == 2 || len == 3) {
|
||||||
|
/* expect " "/"-"/"+", then data */
|
||||||
|
char origin =
|
||||||
|
(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
|
||||||
|
(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
|
||||||
|
GIT_DIFF_LINE_CONTEXT;
|
||||||
|
|
||||||
|
if (output->data_cb != NULL &&
|
||||||
|
output->data_cb(delta, &info->range,
|
||||||
|
origin, bufs[1].ptr, bufs[1].size, output->payload))
|
||||||
|
output->error = GIT_EUSER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len == 3 && !output->error) {
|
||||||
|
/* If we have a '+' and a third buf, then we have added a line
|
||||||
|
* without a newline and the old code had one, so DEL_EOFNL.
|
||||||
|
* If we have a '-' and a third buf, then we have removed a line
|
||||||
|
* with out a newline but added a blank line, so ADD_EOFNL.
|
||||||
|
*/
|
||||||
|
char origin =
|
||||||
|
(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL :
|
||||||
|
(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL :
|
||||||
|
GIT_DIFF_LINE_CONTEXT_EOFNL;
|
||||||
|
|
||||||
|
if (output->data_cb != NULL &&
|
||||||
|
output->data_cb(delta, &info->range,
|
||||||
|
origin, bufs[2].ptr, bufs[2].size, output->payload))
|
||||||
|
output->error = GIT_EUSER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output->error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
|
||||||
|
{
|
||||||
|
git_xdiff_output *xo = (git_xdiff_output *)output;
|
||||||
|
git_xdiff_info info;
|
||||||
|
git_diff_find_context_payload findctxt;
|
||||||
|
mmfile_t xd_old_data, xd_new_data;
|
||||||
|
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
info.patch = patch;
|
||||||
|
info.xo = xo;
|
||||||
|
|
||||||
|
xo->callback.priv = &info;
|
||||||
|
|
||||||
|
git_diff_find_context_init(
|
||||||
|
&xo->config.find_func, &findctxt, git_diff_patch__driver(patch));
|
||||||
|
xo->config.find_func_priv = &findctxt;
|
||||||
|
|
||||||
|
if (xo->config.find_func != NULL)
|
||||||
|
xo->config.flags |= XDL_EMIT_FUNCNAMES;
|
||||||
|
else
|
||||||
|
xo->config.flags &= ~XDL_EMIT_FUNCNAMES;
|
||||||
|
|
||||||
|
/* TODO: check ofile.opts_flags to see if driver-specific per-file
|
||||||
|
* updates are needed to xo->params.flags
|
||||||
|
*/
|
||||||
|
|
||||||
|
git_diff_patch__old_data(&xd_old_data.ptr, &xd_old_data.size, patch);
|
||||||
|
git_diff_patch__new_data(&xd_new_data.ptr, &xd_new_data.size, patch);
|
||||||
|
|
||||||
|
xdl_diff(&xd_old_data, &xd_new_data,
|
||||||
|
&xo->params, &xo->config, &xo->callback);
|
||||||
|
|
||||||
|
git_diff_find_context_clear(&findctxt);
|
||||||
|
|
||||||
|
return xo->output.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts)
|
||||||
|
{
|
||||||
|
uint32_t flags = opts ? opts->flags : GIT_DIFF_NORMAL;
|
||||||
|
|
||||||
|
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)
|
||||||
|
xo->params.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
|
||||||
|
if (flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
|
||||||
|
xo->params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
|
||||||
|
|
||||||
|
memset(&xo->callback, 0, sizeof(xo->callback));
|
||||||
|
xo->callback.outf = git_xdiff_cb;
|
||||||
|
}
|
28
src/diff_xdiff.h
Normal file
28
src/diff_xdiff.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* 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_diff_xdiff_h__
|
||||||
|
#define INCLUDE_diff_xdiff_h__
|
||||||
|
|
||||||
|
#include "diff.h"
|
||||||
|
#include "diff_patch.h"
|
||||||
|
#include "xdiff/xdiff.h"
|
||||||
|
|
||||||
|
/* A git_xdiff_output is a git_diff_output with extra fields necessary
|
||||||
|
* to use libxdiff. Calling git_xdiff_init() will set the diff_cb field
|
||||||
|
* of the output to use xdiff to generate the diffs.
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
git_diff_output output;
|
||||||
|
|
||||||
|
xdemitconf_t config;
|
||||||
|
xpparam_t params;
|
||||||
|
xdemitcb_t callback;
|
||||||
|
} git_xdiff_output;
|
||||||
|
|
||||||
|
void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts);
|
||||||
|
|
||||||
|
#endif
|
15
src/fetch.c
15
src/fetch.c
@ -16,6 +16,8 @@
|
|||||||
#include "pack.h"
|
#include "pack.h"
|
||||||
#include "fetch.h"
|
#include "fetch.h"
|
||||||
#include "netops.h"
|
#include "netops.h"
|
||||||
|
#include "repository.h"
|
||||||
|
#include "refs.h"
|
||||||
|
|
||||||
struct filter_payload {
|
struct filter_payload {
|
||||||
git_remote *remote;
|
git_remote *remote;
|
||||||
@ -34,10 +36,16 @@ static int filter_ref__cb(git_remote_head *head, void *payload)
|
|||||||
|
|
||||||
if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0)
|
if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0)
|
||||||
p->found_head = 1;
|
p->found_head = 1;
|
||||||
else if (git_refspec_src_matches(p->spec, head->name))
|
else if (p->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 (git_refspec_src_matches(p->tagspec, head->name))
|
||||||
match = 1;
|
match = 1;
|
||||||
else if (p->remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL &&
|
else
|
||||||
git_refspec_src_matches(p->tagspec, head->name))
|
return 0;
|
||||||
|
} else if (git_remote__matching_refspec(p->remote, head->name))
|
||||||
match = 1;
|
match = 1;
|
||||||
|
|
||||||
if (!match)
|
if (!match)
|
||||||
@ -68,7 +76,6 @@ static int filter_wants(git_remote *remote)
|
|||||||
* not interested in any particular branch but just the remote's
|
* not interested in any particular branch but just the remote's
|
||||||
* HEAD, which will be stored in FETCH_HEAD after the fetch.
|
* HEAD, which will be stored in FETCH_HEAD after the fetch.
|
||||||
*/
|
*/
|
||||||
p.spec = git_remote_fetchspec(remote);
|
|
||||||
p.tagspec = &tagspec;
|
p.tagspec = &tagspec;
|
||||||
p.found_head = 0;
|
p.found_head = 0;
|
||||||
p.remote = remote;
|
p.remote = remote;
|
||||||
|
145
src/fileops.c
145
src/fileops.c
@ -61,9 +61,11 @@ int git_futils_creat_locked(const char *path, const mode_t mode)
|
|||||||
wchar_t buf[GIT_WIN_PATH];
|
wchar_t buf[GIT_WIN_PATH];
|
||||||
|
|
||||||
git__utf8_to_16(buf, GIT_WIN_PATH, path);
|
git__utf8_to_16(buf, GIT_WIN_PATH, path);
|
||||||
fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
|
fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC |
|
||||||
|
O_EXCL | O_BINARY | O_CLOEXEC, mode);
|
||||||
#else
|
#else
|
||||||
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
|
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC |
|
||||||
|
O_EXCL | O_BINARY | O_CLOEXEC, mode);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
@ -202,6 +204,32 @@ int git_futils_readbuffer(git_buf *buf, const char *path)
|
|||||||
return git_futils_readbuffer_updated(buf, path, NULL, NULL, NULL);
|
return git_futils_readbuffer_updated(buf, path, NULL, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int git_futils_writebuffer(
|
||||||
|
const git_buf *buf, const char *path, int flags, mode_t mode)
|
||||||
|
{
|
||||||
|
int fd, error = 0;
|
||||||
|
|
||||||
|
if (flags <= 0)
|
||||||
|
flags = O_CREAT | O_TRUNC | O_WRONLY;
|
||||||
|
if (!mode)
|
||||||
|
mode = GIT_FILEMODE_BLOB;
|
||||||
|
|
||||||
|
if ((fd = p_open(path, flags, mode)) < 0) {
|
||||||
|
giterr_set(GITERR_OS, "Could not open '%s' for writing", path);
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((error = p_write(fd, git_buf_cstr(buf), git_buf_len(buf))) < 0) {
|
||||||
|
giterr_set(GITERR_OS, "Could not write to '%s'", path);
|
||||||
|
(void)p_close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((error = p_close(fd)) < 0)
|
||||||
|
giterr_set(GITERR_OS, "Error while closing '%s'", path);
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
|
int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
|
||||||
{
|
{
|
||||||
if (git_futils_mkpath2file(to, dirmode) < 0)
|
if (git_futils_mkpath2file(to, dirmode) < 0)
|
||||||
@ -253,8 +281,9 @@ int git_futils_mkdir(
|
|||||||
{
|
{
|
||||||
int error = -1;
|
int error = -1;
|
||||||
git_buf make_path = GIT_BUF_INIT;
|
git_buf make_path = GIT_BUF_INIT;
|
||||||
ssize_t root = 0;
|
ssize_t root = 0, min_root_len;
|
||||||
char lastch, *tail;
|
char lastch = '/', *tail;
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
/* build path and find "root" where we should start calling mkdir */
|
/* build path and find "root" where we should start calling mkdir */
|
||||||
if (git_path_join_unrooted(&make_path, path, base, &root) < 0)
|
if (git_path_join_unrooted(&make_path, path, base, &root) < 0)
|
||||||
@ -262,7 +291,7 @@ int git_futils_mkdir(
|
|||||||
|
|
||||||
if (make_path.size == 0) {
|
if (make_path.size == 0) {
|
||||||
giterr_set(GITERR_OS, "Attempt to create empty path");
|
giterr_set(GITERR_OS, "Attempt to create empty path");
|
||||||
goto fail;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* remove trailing slashes on path */
|
/* remove trailing slashes on path */
|
||||||
@ -279,19 +308,32 @@ int git_futils_mkdir(
|
|||||||
if ((flags & GIT_MKDIR_SKIP_LAST) != 0)
|
if ((flags & GIT_MKDIR_SKIP_LAST) != 0)
|
||||||
git_buf_rtruncate_at_char(&make_path, '/');
|
git_buf_rtruncate_at_char(&make_path, '/');
|
||||||
|
|
||||||
|
/* if nothing left after truncation, then we're done! */
|
||||||
|
if (!make_path.size) {
|
||||||
|
error = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
/* if we are not supposed to make the whole path, reset root */
|
/* if we are not supposed to make the whole path, reset root */
|
||||||
if ((flags & GIT_MKDIR_PATH) == 0)
|
if ((flags & GIT_MKDIR_PATH) == 0)
|
||||||
root = git_buf_rfind(&make_path, '/');
|
root = git_buf_rfind(&make_path, '/');
|
||||||
|
|
||||||
|
/* advance root past drive name or network mount prefix */
|
||||||
|
min_root_len = git_path_root(make_path.ptr);
|
||||||
|
if (root < min_root_len)
|
||||||
|
root = min_root_len;
|
||||||
|
while (root >= 0 && make_path.ptr[root] == '/')
|
||||||
|
++root;
|
||||||
|
|
||||||
/* clip root to make_path length */
|
/* clip root to make_path length */
|
||||||
if (root >= (ssize_t)make_path.size)
|
if (root > (ssize_t)make_path.size)
|
||||||
root = (ssize_t)make_path.size - 1;
|
root = (ssize_t)make_path.size; /* i.e. NUL byte of string */
|
||||||
if (root < 0)
|
if (root < 0)
|
||||||
root = 0;
|
root = 0;
|
||||||
|
|
||||||
tail = & make_path.ptr[root];
|
/* walk down tail of path making each directory */
|
||||||
|
for (tail = &make_path.ptr[root]; *tail; *tail = lastch) {
|
||||||
|
|
||||||
while (*tail) {
|
|
||||||
/* advance tail to include next path component */
|
/* advance tail to include next path component */
|
||||||
while (*tail == '/')
|
while (*tail == '/')
|
||||||
tail++;
|
tail++;
|
||||||
@ -301,68 +343,49 @@ int git_futils_mkdir(
|
|||||||
/* truncate path at next component */
|
/* truncate path at next component */
|
||||||
lastch = *tail;
|
lastch = *tail;
|
||||||
*tail = '\0';
|
*tail = '\0';
|
||||||
|
st.st_mode = 0;
|
||||||
|
|
||||||
/* make directory */
|
/* make directory */
|
||||||
if (p_mkdir(make_path.ptr, mode) < 0) {
|
if (p_mkdir(make_path.ptr, mode) < 0) {
|
||||||
int already_exists = 0;
|
int tmp_errno = errno;
|
||||||
|
|
||||||
switch (errno) {
|
/* ignore error if directory already exists */
|
||||||
case EEXIST:
|
if (p_stat(make_path.ptr, &st) < 0 ||
|
||||||
if (!lastch && (flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
|
!(S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))) {
|
||||||
!git_path_isdir(make_path.ptr)) {
|
errno = tmp_errno;
|
||||||
giterr_set(
|
giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr);
|
||||||
GITERR_OS, "Existing path is not a directory '%s'",
|
goto done;
|
||||||
make_path.ptr);
|
|
||||||
error = GIT_ENOTFOUND;
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
already_exists = 1;
|
|
||||||
break;
|
|
||||||
case ENOSYS:
|
|
||||||
/* Solaris can generate this error if you try to mkdir
|
|
||||||
* a path which is already a mount point. In that case,
|
|
||||||
* the path does already exist; but it's not implied by
|
|
||||||
* the definition of the error, so let's recheck */
|
|
||||||
if (git_path_isdir(make_path.ptr)) {
|
|
||||||
already_exists = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Fall through */
|
|
||||||
errno = ENOSYS;
|
|
||||||
default:
|
|
||||||
giterr_set(GITERR_OS, "Failed to make directory '%s'",
|
|
||||||
make_path.ptr);
|
|
||||||
goto fail;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (already_exists && (flags & GIT_MKDIR_EXCL) != 0) {
|
/* with exclusive create, existing dir is an error */
|
||||||
giterr_set(GITERR_OS, "Directory already exists '%s'",
|
if ((flags & GIT_MKDIR_EXCL) != 0) {
|
||||||
make_path.ptr);
|
giterr_set(GITERR_OS, "Directory already exists '%s'", make_path.ptr);
|
||||||
error = GIT_EEXISTS;
|
error = GIT_EEXISTS;
|
||||||
goto fail;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* chmod if requested */
|
/* chmod if requested and necessary */
|
||||||
if ((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
|
if (((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
|
||||||
((flags & GIT_MKDIR_CHMOD) != 0 && lastch == '\0'))
|
(lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) &&
|
||||||
{
|
st.st_mode != mode &&
|
||||||
if (p_chmod(make_path.ptr, mode) < 0) {
|
(error = p_chmod(make_path.ptr, mode)) < 0) {
|
||||||
giterr_set(GITERR_OS, "Failed to set permissions on '%s'",
|
giterr_set(GITERR_OS, "Failed to set permissions on '%s'", make_path.ptr);
|
||||||
make_path.ptr);
|
goto done;
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*tail = lastch;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
git_buf_free(&make_path);
|
error = 0;
|
||||||
return 0;
|
|
||||||
|
|
||||||
fail:
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
done:
|
||||||
git_buf_free(&make_path);
|
git_buf_free(&make_path);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
@ -444,7 +467,7 @@ static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path)
|
|||||||
|
|
||||||
if (data->error < 0) {
|
if (data->error < 0) {
|
||||||
if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 &&
|
if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 &&
|
||||||
(errno == ENOTEMPTY || errno == EEXIST))
|
(errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY))
|
||||||
data->error = 0;
|
data->error = 0;
|
||||||
else
|
else
|
||||||
futils__error_cannot_rmdir(path->ptr, NULL);
|
futils__error_cannot_rmdir(path->ptr, NULL);
|
||||||
@ -480,7 +503,7 @@ static int futils__rmdir_empty_parent(void *opaque, git_buf *path)
|
|||||||
if (en == ENOENT || en == ENOTDIR) {
|
if (en == ENOENT || en == ENOTDIR) {
|
||||||
giterr_clear();
|
giterr_clear();
|
||||||
error = 0;
|
error = 0;
|
||||||
} else if (en == ENOTEMPTY || en == EEXIST) {
|
} else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) {
|
||||||
giterr_clear();
|
giterr_clear();
|
||||||
error = GIT_ITEROVER;
|
error = GIT_ITEROVER;
|
||||||
} else {
|
} else {
|
||||||
@ -988,8 +1011,10 @@ int git_futils_filestamp_check(
|
|||||||
if (stamp == NULL)
|
if (stamp == NULL)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (p_stat(path, &st) < 0)
|
if (p_stat(path, &st) < 0) {
|
||||||
|
giterr_set(GITERR_OS, "Could not stat '%s'", path);
|
||||||
return GIT_ENOTFOUND;
|
return GIT_ENOTFOUND;
|
||||||
|
}
|
||||||
|
|
||||||
if (stamp->mtime == (git_time_t)st.st_mtime &&
|
if (stamp->mtime == (git_time_t)st.st_mtime &&
|
||||||
stamp->size == (git_off_t)st.st_size &&
|
stamp->size == (git_off_t)st.st_size &&
|
||||||
|
@ -22,6 +22,9 @@ extern int git_futils_readbuffer_updated(
|
|||||||
git_buf *obj, const char *path, time_t *mtime, size_t *size, int *updated);
|
git_buf *obj, const char *path, time_t *mtime, size_t *size, int *updated);
|
||||||
extern int git_futils_readbuffer_fd(git_buf *obj, git_file fd, size_t len);
|
extern int git_futils_readbuffer_fd(git_buf *obj, git_file fd, size_t len);
|
||||||
|
|
||||||
|
extern int git_futils_writebuffer(
|
||||||
|
const git_buf *buf, const char *path, int open_flags, mode_t mode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* File utils
|
* File utils
|
||||||
*
|
*
|
||||||
@ -223,6 +226,7 @@ extern git_off_t git_futils_filesize(git_file fd);
|
|||||||
#define GIT_MODE_PERMS_MASK 0777
|
#define GIT_MODE_PERMS_MASK 0777
|
||||||
#define GIT_CANONICAL_PERMS(MODE) (((MODE) & 0100) ? 0755 : 0644)
|
#define GIT_CANONICAL_PERMS(MODE) (((MODE) & 0100) ? 0755 : 0644)
|
||||||
#define GIT_MODE_TYPE(MODE) ((MODE) & ~GIT_MODE_PERMS_MASK)
|
#define GIT_MODE_TYPE(MODE) ((MODE) & ~GIT_MODE_PERMS_MASK)
|
||||||
|
#define GIT_MODE_ISBLOB(MODE) (GIT_MODE_TYPE(MODE) == GIT_MODE_TYPE(GIT_FILEMODE_BLOB))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a mode_t from the OS to a legal git mode_t value.
|
* Convert a mode_t from the OS to a legal git mode_t value.
|
||||||
|
12
src/global.c
12
src/global.c
@ -61,7 +61,8 @@ int git_threads_init(void)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
_tls_index = TlsAlloc();
|
_tls_index = TlsAlloc();
|
||||||
git_mutex_init(&git__mwindow_mutex);
|
if (git_mutex_init(&git__mwindow_mutex))
|
||||||
|
return -1;
|
||||||
|
|
||||||
/* Initialize any other subsystems that have global state */
|
/* Initialize any other subsystems that have global state */
|
||||||
if ((error = git_hash_global_init()) >= 0)
|
if ((error = git_hash_global_init()) >= 0)
|
||||||
@ -121,7 +122,8 @@ int git_threads_init(void)
|
|||||||
if (_tls_init)
|
if (_tls_init)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
git_mutex_init(&git__mwindow_mutex);
|
if (git_mutex_init(&git__mwindow_mutex))
|
||||||
|
return -1;
|
||||||
pthread_key_create(&_tls_key, &cb__free_status);
|
pthread_key_create(&_tls_key, &cb__free_status);
|
||||||
|
|
||||||
/* Initialize any other subsystems that have global state */
|
/* Initialize any other subsystems that have global state */
|
||||||
@ -135,6 +137,12 @@ int git_threads_init(void)
|
|||||||
|
|
||||||
void git_threads_shutdown(void)
|
void git_threads_shutdown(void)
|
||||||
{
|
{
|
||||||
|
if (_tls_init) {
|
||||||
|
void *ptr = pthread_getspecific(_tls_key);
|
||||||
|
pthread_setspecific(_tls_key, NULL);
|
||||||
|
git__free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
pthread_key_delete(_tls_key);
|
pthread_key_delete(_tls_key);
|
||||||
_tls_init = 0;
|
_tls_init = 0;
|
||||||
git_mutex_free(&git__mwindow_mutex);
|
git_mutex_free(&git__mwindow_mutex);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user