mirror of
https://git.proxmox.com/git/libgit2
synced 2026-01-06 17:13:55 +00:00
Merge branch 'development'
This commit is contained in:
commit
29d7242b1d
2
.gitignore
vendored
2
.gitignore
vendored
@ -24,8 +24,8 @@ msvc/Release/
|
||||
*.sdf
|
||||
*.opensdf
|
||||
*.aps
|
||||
CMake*
|
||||
*.cmake
|
||||
!cmake/Modules/*.cmake
|
||||
.DS_Store
|
||||
*~
|
||||
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>
|
||||
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
|
||||
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
|
||||
- cd _build
|
||||
- cmake .. -DCMAKE_INSTALL_PREFIX=../_install $OPTIONS
|
||||
|
||||
@ -14,6 +14,9 @@
|
||||
PROJECT(libgit2 C)
|
||||
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
|
||||
#
|
||||
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( PROFILE "Generate profiling information" OFF )
|
||||
OPTION( ENABLE_TRACE "Enables tracing support" OFF )
|
||||
OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF )
|
||||
|
||||
IF(MSVC)
|
||||
# This option is only availalbe when building with MSVC. By default,
|
||||
# libgit2 is build using the stdcall calling convention, as that's what
|
||||
@ -97,14 +102,22 @@ ELSE ()
|
||||
IF (NOT AMIGA)
|
||||
FIND_PACKAGE(OpenSSL)
|
||||
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()
|
||||
|
||||
# Specify sha1 implementation
|
||||
IF (WIN32 AND NOT MINGW AND NOT SHA1_TYPE STREQUAL "builtin")
|
||||
ADD_DEFINITIONS(-DWIN32_SHA1)
|
||||
FILE(GLOB SRC_SHA1 src/hash/hash_win32.c)
|
||||
ADD_DEFINITIONS(-DWIN32_SHA1)
|
||||
FILE(GLOB SRC_SHA1 src/hash/hash_win32.c)
|
||||
ELSEIF (OPENSSL_FOUND AND NOT SHA1_TYPE STREQUAL "builtin")
|
||||
ADD_DEFINITIONS(-DOPENSSL_SHA1)
|
||||
ELSE()
|
||||
@ -123,14 +136,14 @@ IF(WIN32 OR AMIGA)
|
||||
ENDIF()
|
||||
|
||||
# Optional external dependency: zlib
|
||||
IF(NOT ZLIB_LIBRARY)
|
||||
# It's optional, but FIND_PACKAGE gives a warning that looks more like an
|
||||
# error.
|
||||
FIND_PACKAGE(ZLIB QUIET)
|
||||
ENDIF()
|
||||
# It's optional, but FIND_PACKAGE gives a warning that looks more like an
|
||||
# error.
|
||||
FIND_PACKAGE(ZLIB QUIET)
|
||||
IF (ZLIB_FOUND)
|
||||
INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS})
|
||||
LINK_LIBRARIES(${ZLIB_LIBRARIES})
|
||||
# Fake the message CMake would have shown
|
||||
MESSAGE("-- Found zlib: ${ZLIB_LIBRARY}")
|
||||
ELSE()
|
||||
MESSAGE( "zlib was not found; using bundled 3rd-party sources." )
|
||||
INCLUDE_DIRECTORIES(deps/zlib)
|
||||
@ -138,10 +151,19 @@ ELSE()
|
||||
FILE(GLOB SRC_ZLIB deps/zlib/*.c)
|
||||
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
|
||||
IF (MSVC)
|
||||
|
||||
STRING(REPLACE "/Zm1000" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
||||
STRING(REPLACE "/Zm1000" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
|
||||
|
||||
# /GF - String pooling
|
||||
# /MP - Parallel build
|
||||
@ -156,7 +178,7 @@ IF (MSVC)
|
||||
SET(CRT_FLAG_DEBUG "/MTd")
|
||||
SET(CRT_FLAG_RELEASE "/MT")
|
||||
ELSE()
|
||||
SET(CRT_FLAG_DEBUG "/MDd")
|
||||
SET(CRT_FLAG_DEBUG "/MDd")
|
||||
SET(CRT_FLAG_RELEASE "/MD")
|
||||
ENDIF()
|
||||
|
||||
@ -277,14 +299,24 @@ ELSE()
|
||||
ENDIF()
|
||||
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
|
||||
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 ${SSH_LIBRARIES})
|
||||
TARGET_OS_LIBRARIES(git2)
|
||||
|
||||
# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240)
|
||||
# Win64+MSVC+static libs = linker error
|
||||
IF(MSVC AND 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")
|
||||
ENDIF()
|
||||
|
||||
@ -293,6 +325,10 @@ MSVC_SPLIT_SOURCES(git2)
|
||||
IF (SONAME)
|
||||
SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING})
|
||||
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()
|
||||
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(
|
||||
OUTPUT ${CLAR_PATH}/clar.suite
|
||||
COMMAND ${PYTHON_EXECUTABLE} generate.py -xonline .
|
||||
COMMAND ${PYTHON_EXECUTABLE} generate.py -f -xonline .
|
||||
DEPENDS ${SRC_TEST}
|
||||
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})
|
||||
|
||||
TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES})
|
||||
TARGET_LINK_LIBRARIES(libgit2_clar ${SSH_LIBRARIES})
|
||||
TARGET_OS_LIBRARIES(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/>
|
||||
* Website: <http://libgit2.github.com>
|
||||
* API documentation: <http://libgit2.github.com/libgit2>
|
||||
* IRC: #libgit2 on irc.freenode.net.
|
||||
|
||||
What It Can Do
|
||||
==================================
|
||||
@ -139,7 +140,7 @@ Here are the bindings to libgit2 that are currently available:
|
||||
* Parrot Virtual Machine
|
||||
* parrot-libgit2 <https://github.com/letolabs/parrot-libgit2>
|
||||
* Perl
|
||||
* git-xs-pm <https://github.com/ingydotnet/git-xs-pm>
|
||||
* Git-Raw <https://github.com/ghedo/p5-Git-Raw>
|
||||
* PHP
|
||||
* php-git <https://github.com/libgit2/php-git>
|
||||
* Python
|
||||
@ -155,15 +156,7 @@ we can add it to the list.
|
||||
How Can I Contribute?
|
||||
==================================
|
||||
|
||||
Fork libgit2/libgit2 on GitHub, add your improvement, push it to a branch
|
||||
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
|
||||
Check the [contribution guidelines](CONTRIBUTING.md).
|
||||
|
||||
|
||||
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
|
||||
CFLAGS = -g -I../include -I../src -Wall -Wextra -Wmissing-prototypes -Wno-missing-field-initializers
|
||||
LFLAGS = -L../build -lgit2 -lz
|
||||
APPS = general showindex diff rev-list
|
||||
APPS = general showindex diff rev-list cat-file status
|
||||
|
||||
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;
|
||||
if (strncmp(arg, pattern, len))
|
||||
return 0;
|
||||
if (arg[len] == '\0' && pattern[len - 1] != '=')
|
||||
return 1;
|
||||
if (arg[len] == '=')
|
||||
len++;
|
||||
strval = strtoul(arg + len, &endptr, 0);
|
||||
if (endptr == arg)
|
||||
return 0;
|
||||
@ -110,14 +114,24 @@ static void usage(const char *message, const char *arg)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
enum {
|
||||
FORMAT_PATCH = 0,
|
||||
FORMAT_COMPACT = 1,
|
||||
FORMAT_RAW = 2
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
git_repository *repo = NULL;
|
||||
git_tree *t1 = NULL, *t2 = NULL;
|
||||
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
||||
git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
|
||||
git_diff_list *diff;
|
||||
int i, color = -1, compact = 0, cached = 0;
|
||||
char *a, *dir = ".", *treeish1 = NULL, *treeish2 = NULL;
|
||||
int i, color = -1, format = FORMAT_PATCH, cached = 0;
|
||||
char *a, *treeish1 = NULL, *treeish2 = NULL;
|
||||
const char *dir = ".";
|
||||
|
||||
git_threads_init();
|
||||
|
||||
/* 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") ||
|
||||
!strcmp(a, "--patch"))
|
||||
compact = 0;
|
||||
format = FORMAT_PATCH;
|
||||
else if (!strcmp(a, "--cached"))
|
||||
cached = 1;
|
||||
else if (!strcmp(a, "--name-status"))
|
||||
compact = 1;
|
||||
format = FORMAT_COMPACT;
|
||||
else if (!strcmp(a, "--raw"))
|
||||
format = FORMAT_RAW;
|
||||
else if (!strcmp(a, "--color"))
|
||||
color = 0;
|
||||
else if (!strcmp(a, "--no-color"))
|
||||
@ -157,12 +173,27 @@ int main(int argc, char *argv[])
|
||||
opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
|
||||
else if (!strcmp(a, "--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) &&
|
||||
!check_uint16_param(a, "--unified=", &opts.context_lines) &&
|
||||
!check_uint16_param(a, "--inter-hunk-context=",
|
||||
&opts.interhunk_lines) &&
|
||||
!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);
|
||||
}
|
||||
|
||||
@ -200,13 +231,24 @@ int main(int argc, char *argv[])
|
||||
else
|
||||
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)
|
||||
fputs(colors[0], stdout);
|
||||
|
||||
if (compact)
|
||||
check(git_diff_print_compact(diff, printer, &color), "Displaying diff");
|
||||
else
|
||||
switch (format) {
|
||||
case FORMAT_PATCH:
|
||||
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)
|
||||
fputs(colors[0], stdout);
|
||||
@ -216,6 +258,8 @@ int main(int argc, char *argv[])
|
||||
git_tree_free(t2);
|
||||
git_repository_free(repo);
|
||||
|
||||
git_threads_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -453,7 +453,7 @@ int main (int argc, char** argv)
|
||||
// Here we will implement something like `git for-each-ref` simply listing
|
||||
// out all available references and the object SHA they resolve to.
|
||||
git_strarray ref_list;
|
||||
git_reference_list(&ref_list, repo, GIT_REF_LISTALL);
|
||||
git_reference_list(&ref_list, repo);
|
||||
|
||||
const char *refname;
|
||||
git_reference *ref;
|
||||
|
||||
@ -54,6 +54,8 @@ int main(int argc, char **argv)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
git_threads_init();
|
||||
|
||||
for (i = 0; commands[i].name != NULL; ++i) {
|
||||
if (!strcmp(argv[1], commands[i].name))
|
||||
return run_command(commands[i].fn, --argc, ++argv);
|
||||
|
||||
@ -117,4 +117,3 @@ int main (int argc, char **argv)
|
||||
|
||||
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(
|
||||
const char **value_out,
|
||||
git_repository *repo,
|
||||
git_repository *repo,
|
||||
uint32_t flags,
|
||||
const char *path,
|
||||
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
|
||||
* 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.
|
||||
* You should not modify or free the values that are written
|
||||
* 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
|
||||
* 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_repository *repo,
|
||||
|
||||
@ -29,10 +29,7 @@ GIT_BEGIN_DECL
|
||||
* @param id identity of the blob to locate.
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(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);
|
||||
}
|
||||
GIT_EXTERN(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git_oid *id);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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)
|
||||
{
|
||||
return git_object_lookup_prefix((git_object **)blob, repo, id, len, GIT_OBJ_BLOB);
|
||||
}
|
||||
GIT_EXTERN(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, size_t len);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
GIT_INLINE(void) git_blob_free(git_blob *blob)
|
||||
{
|
||||
git_object_free((git_object *) blob);
|
||||
}
|
||||
GIT_EXTERN(void) git_blob_free(git_blob *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.
|
||||
* @return SHA1 hash for this blob.
|
||||
*/
|
||||
GIT_INLINE(const git_oid *) git_blob_id(const git_blob *blob)
|
||||
{
|
||||
return git_object_id((const git_object *)blob);
|
||||
}
|
||||
GIT_EXTERN(const git_oid *) git_blob_id(const git_blob *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.
|
||||
|
||||
@ -58,7 +58,8 @@ GIT_EXTERN(int) git_branch_create(
|
||||
* Delete an existing branch 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
|
||||
* @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
|
||||
* 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,
|
||||
* otherwise an error code.
|
||||
*/
|
||||
|
||||
@ -134,6 +134,9 @@ typedef enum {
|
||||
/** Treat pathspec as simple list of exact match file paths */
|
||||
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
|
||||
*/
|
||||
@ -180,6 +183,8 @@ typedef enum {
|
||||
GIT_CHECKOUT_NOTIFY_UPDATED = (1u << 2),
|
||||
GIT_CHECKOUT_NOTIFY_UNTRACKED = (1u << 3),
|
||||
GIT_CHECKOUT_NOTIFY_IGNORED = (1u << 4),
|
||||
|
||||
GIT_CHECKOUT_NOTIFY_ALL = 0x0FFFFu
|
||||
} git_checkout_notify_t;
|
||||
|
||||
/** Checkout notification callback function */
|
||||
@ -231,6 +236,8 @@ typedef struct git_checkout_opts {
|
||||
git_strarray paths;
|
||||
|
||||
git_tree *baseline; /** expected content of workdir, defaults to HEAD */
|
||||
|
||||
const char *target_directory; /** alternative checkout path to workdir */
|
||||
} git_checkout_opts;
|
||||
|
||||
#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
|
||||
* during the initial fetch.
|
||||
* - `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
|
||||
* means use the transport autodetected from the URL.
|
||||
* - `remote_callbacks` may be used to specify custom progress callbacks for
|
||||
@ -75,6 +77,7 @@ typedef struct git_clone_options {
|
||||
const char *push_spec;
|
||||
git_cred_acquire_cb cred_acquire_cb;
|
||||
void *cred_acquire_payload;
|
||||
git_transport_flags_t transport_flags;
|
||||
git_transport *transport;
|
||||
git_remote_callbacks *remote_callbacks;
|
||||
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.
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(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);
|
||||
}
|
||||
GIT_EXTERN(int) git_commit_lookup(git_commit **commit, git_repository *repo, const git_oid *id);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @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)
|
||||
{
|
||||
return git_object_lookup_prefix((git_object **)commit, repo, id, len, GIT_OBJ_COMMIT);
|
||||
}
|
||||
GIT_EXTERN(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, size_t len);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
GIT_INLINE(void) git_commit_free(git_commit *commit)
|
||||
{
|
||||
git_object_free((git_object *) commit);
|
||||
}
|
||||
GIT_EXTERN(void) git_commit_free(git_commit *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.
|
||||
* @return object identity for the commit.
|
||||
*/
|
||||
GIT_INLINE(const git_oid *) git_commit_id(const git_commit *commit)
|
||||
{
|
||||
return git_object_id((const git_object *)commit);
|
||||
}
|
||||
GIT_EXTERN(const git_oid *) git_commit_id(const git_commit *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,
|
||||
@ -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`)
|
||||
* @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
|
||||
@ -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`)
|
||||
* @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
|
||||
@ -201,14 +202,12 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor(
|
||||
unsigned int n);
|
||||
|
||||
/**
|
||||
* Create a new commit in the repository using `git_object`
|
||||
* instances as parameters.
|
||||
* Create new commit in the repository from a list of `git_object` pointers
|
||||
*
|
||||
* The message will not be cleaned up. This can be achieved
|
||||
* through `git_message_prettify()`.
|
||||
* The message will not be cleaned up automatically. You can do that with
|
||||
* the `git_message_prettify()` function.
|
||||
*
|
||||
* @param id Pointer where to store the OID of the
|
||||
* newly created commit
|
||||
* @param id Pointer in which to store the OID of the newly created 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
|
||||
* exist yet, it will be created.
|
||||
*
|
||||
* @param author Signature representing the author and the authory
|
||||
* time of this commit
|
||||
* @param author Signature with author and author time of commit
|
||||
*
|
||||
* @param committer Signature representing the committer and the
|
||||
* commit time of this commit
|
||||
* @param committer Signature with committer and * commit time of commit
|
||||
*
|
||||
* @param message_encoding The encoding for the message in the
|
||||
* commit, represented with a standard encoding name.
|
||||
* E.g. "UTF-8". If NULL, no encoding header is written and
|
||||
* UTF-8 is assumed.
|
||||
* commit, represented with a standard encoding name.
|
||||
* E.g. "UTF-8". If NULL, no encoding header is written and
|
||||
* UTF-8 is assumed.
|
||||
*
|
||||
* @param message Full message for this commit
|
||||
*
|
||||
* @param tree An instance of a `git_tree` object that will
|
||||
* be used as the tree for the commit. This tree object must
|
||||
* also be owned by the given `repo`.
|
||||
* be used as the tree for the commit. This tree object must
|
||||
* also be owned by the given `repo`.
|
||||
*
|
||||
* @param parent_count Number of parents for this commit
|
||||
*
|
||||
* @param parents[] Array of `parent_count` pointers to `git_commit`
|
||||
* 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
|
||||
* given commits must be owned by the `repo`.
|
||||
* @param parents Array of `parent_count` pointers to `git_commit`
|
||||
* 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
|
||||
* given commits must be owned by the `repo`.
|
||||
*
|
||||
* @return 0 or an error code
|
||||
* The created commit will be written to the Object Database and
|
||||
* the given reference will be updated to point to it
|
||||
*/
|
||||
GIT_EXTERN(int) git_commit_create(
|
||||
git_oid *id,
|
||||
git_repository *repo,
|
||||
const char *update_ref,
|
||||
const git_signature *author,
|
||||
const git_signature *committer,
|
||||
const char *message_encoding,
|
||||
const char *message,
|
||||
const git_tree *tree,
|
||||
int parent_count,
|
||||
const git_commit *parents[]);
|
||||
git_oid *id,
|
||||
git_repository *repo,
|
||||
const char *update_ref,
|
||||
const git_signature *author,
|
||||
const git_signature *committer,
|
||||
const char *message_encoding,
|
||||
const char *message,
|
||||
const git_tree *tree,
|
||||
int parent_count,
|
||||
const git_commit *parents[]);
|
||||
|
||||
/**
|
||||
* Create a new commit in the repository using a variable
|
||||
* argument list.
|
||||
* Create new commit in the repository using a variable argument list.
|
||||
*
|
||||
* The message will be cleaned up from excess whitespace
|
||||
* it will be made sure that the last line ends with a '\n'.
|
||||
* The message will be cleaned up from excess whitespace and it will be made
|
||||
* sure that the last line ends with a '\n'.
|
||||
*
|
||||
* The parents for the commit are specified as a variable
|
||||
* list of pointers to `const git_commit *`. Note that this
|
||||
* is a convenience method which may not be safe to export
|
||||
* for certain languages or compilers
|
||||
* The parents for the commit are specified as a variable list of pointers
|
||||
* to `const git_commit *`. Note that this is a convenience method which may
|
||||
* not be safe to export for certain languages or compilers
|
||||
*
|
||||
* All other parameters remain the same
|
||||
* All other parameters remain the same at `git_commit_create()`.
|
||||
*
|
||||
* @see git_commit_create
|
||||
*/
|
||||
GIT_EXTERN(int) git_commit_create_v(
|
||||
git_oid *id,
|
||||
git_repository *repo,
|
||||
const char *update_ref,
|
||||
const git_signature *author,
|
||||
const git_signature *committer,
|
||||
const char *message_encoding,
|
||||
const char *message,
|
||||
const git_tree *tree,
|
||||
int parent_count,
|
||||
...);
|
||||
git_oid *id,
|
||||
git_repository *repo,
|
||||
const char *update_ref,
|
||||
const git_signature *author,
|
||||
const git_signature *committer,
|
||||
const char *message_encoding,
|
||||
const char *message,
|
||||
const git_tree *tree,
|
||||
int parent_count,
|
||||
...);
|
||||
|
||||
/** @} */
|
||||
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.
|
||||
*/
|
||||
enum {
|
||||
typedef enum {
|
||||
GIT_CAP_THREADS = ( 1 << 0 ),
|
||||
GIT_CAP_HTTPS = ( 1 << 1 )
|
||||
};
|
||||
} git_cap_t;
|
||||
|
||||
/**
|
||||
* Query compile time options for libgit2.
|
||||
@ -114,69 +114,100 @@ enum {
|
||||
* @return A combination of GIT_CAP_* values.
|
||||
*
|
||||
* - GIT_CAP_THREADS
|
||||
* Libgit2 was compiled with thread support. Note that thread support is still to be seen as a
|
||||
* 'work in progress'.
|
||||
* Libgit2 was compiled with thread support. Note that thread support is
|
||||
* still to be seen as a 'work in progress' - basic object lookups are
|
||||
* believed to be threadsafe, but other operations may not be.
|
||||
*
|
||||
* - GIT_CAP_HTTPS
|
||||
* Libgit2 supports the https:// protocol. This requires the open ssl library to be
|
||||
* found when compiling libgit2.
|
||||
* Libgit2 supports the https:// protocol. This requires the openssl
|
||||
* library to be found when compiling libgit2.
|
||||
*/
|
||||
GIT_EXTERN(int) git_libgit2_capabilities(void);
|
||||
|
||||
|
||||
enum {
|
||||
typedef enum {
|
||||
GIT_OPT_GET_MWINDOW_SIZE,
|
||||
GIT_OPT_SET_MWINDOW_SIZE,
|
||||
GIT_OPT_GET_MWINDOW_MAPPED_LIMIT,
|
||||
GIT_OPT_SET_MWINDOW_MAPPED_LIMIT,
|
||||
GIT_OPT_GET_SEARCH_PATH,
|
||||
GIT_OPT_SET_SEARCH_PATH,
|
||||
GIT_OPT_GET_ODB_CACHE_SIZE,
|
||||
GIT_OPT_SET_ODB_CACHE_SIZE,
|
||||
};
|
||||
GIT_OPT_SET_CACHE_OBJECT_LIMIT,
|
||||
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
|
||||
*
|
||||
* Available options:
|
||||
*
|
||||
* opts(GIT_OPT_GET_MWINDOW_SIZE, size_t *):
|
||||
* Get the maximum mmap window size
|
||||
* * opts(GIT_OPT_GET_MWINDOW_SIZE, size_t *):
|
||||
*
|
||||
* opts(GIT_OPT_SET_MWINDOW_SIZE, size_t):
|
||||
* Set the maximum mmap window size
|
||||
* > Get the maximum mmap window size
|
||||
*
|
||||
* 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_SIZE, size_t):
|
||||
*
|
||||
* opts(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, size_t):
|
||||
* Set the maximum amount of memory that can be mapped at any time
|
||||
* > Set the maximum mmap window size
|
||||
*
|
||||
* * 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
|
||||
*
|
||||
* 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_GET_SEARCH_PATH, int level, char *out, size_t len)
|
||||
*
|
||||
* opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path)
|
||||
* Set the search path for a level of config data. The search path
|
||||
* applied to shared attributes and ignore files, too.
|
||||
* - `path` lists directories delimited by GIT_PATH_LIST_SEPARATOR.
|
||||
* 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.
|
||||
* > 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_GET_ODB_CACHE_SIZE):
|
||||
* Get the size of the libgit2 odb cache.
|
||||
* * opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path)
|
||||
*
|
||||
* opts(GIT_OPT_SET_ODB_CACHE_SIZE):
|
||||
* Set the size of the of the libgit2 odb cache. This needs
|
||||
* to be done before git_repository_open is called, since
|
||||
* git_repository_open initializes the odb layer. Defaults
|
||||
* to 128.
|
||||
* > Set the search path for a level of config data. The search path
|
||||
* > applied to shared attributes and ignore files, too.
|
||||
* >
|
||||
* > - `path` lists directories delimited by GIT_PATH_LIST_SEPARATOR.
|
||||
* > 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 ... value to set the option
|
||||
|
||||
@ -27,45 +27,41 @@ GIT_BEGIN_DECL
|
||||
* git_config_open_default() and git_repository_config() honor those
|
||||
* priority levels as well.
|
||||
*/
|
||||
enum {
|
||||
GIT_CONFIG_LEVEL_SYSTEM = 1, /**< System-wide configuration file. */
|
||||
GIT_CONFIG_LEVEL_XDG = 2, /**< XDG compatible configuration file (.config/git/config). */
|
||||
GIT_CONFIG_LEVEL_GLOBAL = 3, /**< User-specific configuration file, also called Global configuration file. */
|
||||
GIT_CONFIG_LEVEL_LOCAL = 4, /**< Repository specific configuration file. */
|
||||
GIT_CONFIG_HIGHEST_LEVEL = -1, /**< Represents the highest level of a config file. */
|
||||
};
|
||||
typedef enum {
|
||||
/** System-wide configuration file; /etc/gitconfig on Linux systems */
|
||||
GIT_CONFIG_LEVEL_SYSTEM = 1,
|
||||
|
||||
/** XDG compatible configuration file; typically ~/.config/git/config */
|
||||
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 {
|
||||
const char *name;
|
||||
const char *value;
|
||||
unsigned int level;
|
||||
git_config_level_t level;
|
||||
} git_config_entry;
|
||||
|
||||
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 {
|
||||
GIT_CVAR_FALSE = 0,
|
||||
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
|
||||
* %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
|
||||
* @return 0 if a system configuration file has been
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
@ -192,10 +164,9 @@ GIT_EXTERN(int) git_config_add_backend(
|
||||
* a higher priority level will be accessed first).
|
||||
*
|
||||
* @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 force if a config file already exists for the given
|
||||
* priority level, replace it
|
||||
* @param force replace config file at the given priority level
|
||||
* @return 0 on success, GIT_EEXISTS when adding more than one file
|
||||
* for a given priority level (and force_replace set to 0),
|
||||
* GIT_ENOTFOUND when the file doesn't exist or error code
|
||||
@ -203,7 +174,7 @@ GIT_EXTERN(int) git_config_add_backend(
|
||||
GIT_EXTERN(int) git_config_add_file_ondisk(
|
||||
git_config *cfg,
|
||||
const char *path,
|
||||
unsigned int level,
|
||||
git_config_level_t level,
|
||||
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
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_open_level(
|
||||
git_config **out,
|
||||
const git_config *parent,
|
||||
unsigned int level);
|
||||
git_config **out,
|
||||
const git_config *parent,
|
||||
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
|
||||
@ -274,7 +260,7 @@ GIT_EXTERN(void) git_config_free(git_config *cfg);
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_get_entry(
|
||||
const git_config_entry **out,
|
||||
const git_config_entry **out,
|
||||
const git_config *cfg,
|
||||
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 regexp regular expression to filter which variables we're
|
||||
* interested in. Use NULL to indicate all
|
||||
* @param fn the function to be called on each value of the variable
|
||||
* @param data opaque pointer to pass to the callback
|
||||
* @param callback the function to be called on each value of the variable
|
||||
* @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);
|
||||
|
||||
@ -492,11 +478,11 @@ GIT_EXTERN(int) git_config_foreach_match(
|
||||
* @return 0 on success, error code otherwise
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_get_mapped(
|
||||
int *out,
|
||||
const git_config *cfg,
|
||||
const char *name,
|
||||
const git_cvar_map *maps,
|
||||
size_t map_n);
|
||||
int *out,
|
||||
const git_config *cfg,
|
||||
const char *name,
|
||||
const git_cvar_map *maps,
|
||||
size_t map_n);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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 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.
|
||||
* @param allowed_types A bitmask stating which cred types are OK to return.
|
||||
* @param payload The payload provided when specifying this callback. (This is
|
||||
|
||||
@ -88,42 +88,69 @@ typedef enum {
|
||||
GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8),
|
||||
/** Include unmodified files in the diff list */
|
||||
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
|
||||
* adds all files under the directory as UNTRACKED entries, too.
|
||||
|
||||
/** Even with GIT_DIFF_INCLUDE_UNTRACKED, an entire untracked
|
||||
* 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),
|
||||
|
||||
/** 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.
|
||||
*/
|
||||
GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1 << 11),
|
||||
|
||||
/** Use case insensitive filename comparisons */
|
||||
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),
|
||||
|
||||
/** 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
|
||||
* callbacks and want to avoid having to load file completely.
|
||||
*/
|
||||
GIT_DIFF_SKIP_BINARY_CHECK = (1 << 14),
|
||||
|
||||
/** Normally, a type change between files will be converted into a
|
||||
* DELETED record for the old and an ADDED record for the new; this
|
||||
* options enabled the generation of TYPECHANGE delta records.
|
||||
*/
|
||||
GIT_DIFF_INCLUDE_TYPECHANGE = (1 << 15),
|
||||
|
||||
/** Even with GIT_DIFF_INCLUDE_TYPECHANGE, blob->tree changes still
|
||||
* generally show as a DELETED blob. This flag tries to correctly
|
||||
* label blob->tree transitions as TYPECHANGE records with new_file's
|
||||
* mode set to tree. Note: the tree SHA will not be available.
|
||||
*/
|
||||
GIT_DIFF_INCLUDE_TYPECHANGE_TREES = (1 << 16),
|
||||
|
||||
/** Ignore file mode changes */
|
||||
GIT_DIFF_IGNORE_FILEMODE = (1 << 17),
|
||||
|
||||
/** Even with GIT_DIFF_INCLUDE_IGNORED, an entire ignored directory
|
||||
* will be marked with only a single entry in the diff list; this flag
|
||||
* adds all files under the directory as IGNORED entries, too.
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
@ -224,6 +251,19 @@ typedef struct {
|
||||
* `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
|
||||
* 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 {
|
||||
git_diff_file old_file;
|
||||
@ -337,8 +377,10 @@ typedef enum {
|
||||
GIT_DIFF_LINE_CONTEXT = ' ',
|
||||
GIT_DIFF_LINE_ADDITION = '+',
|
||||
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 content of a diff is being formatted (eg. through
|
||||
@ -387,18 +429,28 @@ typedef enum {
|
||||
/** consider unmodified as copy sources? (`--find-copies-harder`) */
|
||||
GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED = (1 << 3),
|
||||
|
||||
/** split large rewrites into delete/add pairs (`--break-rewrites=/M`) */
|
||||
GIT_DIFF_FIND_AND_BREAK_REWRITES = (1 << 4),
|
||||
/** mark large rewrites for split (`--break-rewrites=/M`) */
|
||||
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 */
|
||||
GIT_DIFF_FIND_ALL = (0x1f),
|
||||
GIT_DIFF_FIND_ALL = (0x0ff),
|
||||
|
||||
/** measure similarity ignoring leading whitespace (default) */
|
||||
GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE = 0,
|
||||
/** measure similarity ignoring all whitespace */
|
||||
GIT_DIFF_FIND_IGNORE_WHITESPACE = (1 << 6),
|
||||
GIT_DIFF_FIND_IGNORE_WHITESPACE = (1 << 12),
|
||||
/** 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;
|
||||
|
||||
/**
|
||||
@ -425,7 +477,10 @@ typedef struct {
|
||||
* - `copy_threshold` is the same as the -C option with a value
|
||||
* - `rename_from_rewrite_threshold` matches the top 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.
|
||||
* Set it to NULL for the default internal metric which is based on sampling
|
||||
@ -437,21 +492,21 @@ typedef struct {
|
||||
unsigned int version;
|
||||
|
||||
/** Combination of git_diff_find_t values (default FIND_RENAMES) */
|
||||
unsigned int flags;
|
||||
uint32_t flags;
|
||||
|
||||
/** 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) */
|
||||
unsigned int rename_from_rewrite_threshold;
|
||||
uint16_t rename_from_rewrite_threshold;
|
||||
/** 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) */
|
||||
unsigned int break_rewrite_threshold;
|
||||
uint16_t break_rewrite_threshold;
|
||||
|
||||
/** Maximum similarity sources to examine (a la diff's `-l` option or
|
||||
* the `diff.renameLimit` config) (default 200)
|
||||
/** Maximum similarity sources to examine for a file (somewhat like
|
||||
* 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 */
|
||||
git_diff_similarity_metric *metric;
|
||||
@ -469,6 +524,8 @@ typedef struct {
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
@ -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>`
|
||||
*
|
||||
* 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 repo The repository containing the trees.
|
||||
* @param old_tree A git_tree object to diff from.
|
||||
* @param new_tree A git_tree object to diff to.
|
||||
* @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, or NULL for empty tree.
|
||||
* @param opts Structure with options to influence diff or NULL for defaults.
|
||||
*/
|
||||
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 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 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 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.
|
||||
*/
|
||||
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,
|
||||
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.
|
||||
*
|
||||
@ -672,7 +747,7 @@ GIT_EXTERN(int) git_diff_print_compact(
|
||||
* letters for your own purposes. This function does just that. By the
|
||||
* 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
|
||||
*/
|
||||
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_deletions Count of deletion lines in output, can be NULL.
|
||||
* @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(
|
||||
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 patch The patch to look in
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
* `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(
|
||||
const git_blob *old_blob,
|
||||
const char *old_as_path,
|
||||
const git_blob *new_blob,
|
||||
const char *new_as_path,
|
||||
const git_diff_options *options,
|
||||
git_diff_file_cb file_cb,
|
||||
git_diff_hunk_cb hunk_cb,
|
||||
git_diff_data_cb line_cb,
|
||||
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.
|
||||
*
|
||||
@ -930,18 +1040,57 @@ GIT_EXTERN(int) git_diff_blobs(
|
||||
* entire content of the buffer added). Passing NULL to the buffer will do
|
||||
* 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(
|
||||
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 *options,
|
||||
git_diff_file_cb file_cb,
|
||||
git_diff_hunk_cb hunk_cb,
|
||||
git_diff_data_cb data_cb,
|
||||
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
|
||||
|
||||
/** @} */
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/** Generic return codes */
|
||||
enum {
|
||||
typedef enum {
|
||||
GIT_OK = 0,
|
||||
GIT_ERROR = -1,
|
||||
GIT_ENOTFOUND = -3,
|
||||
@ -35,7 +35,7 @@ enum {
|
||||
|
||||
GIT_PASSTHROUGH = -30,
|
||||
GIT_ITEROVER = -31,
|
||||
};
|
||||
} git_error_code;
|
||||
|
||||
typedef struct {
|
||||
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
|
||||
* 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);
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include "indexer.h"
|
||||
#include "types.h"
|
||||
#include "oid.h"
|
||||
#include "strarray.h"
|
||||
|
||||
/**
|
||||
* @file git2/index.h
|
||||
@ -21,50 +22,29 @@
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
#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
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
/** Time structure used in a git index entry */
|
||||
typedef struct {
|
||||
git_time_t seconds;
|
||||
/* nsec should not be stored as time_t compatible */
|
||||
unsigned int nanoseconds;
|
||||
} 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 {
|
||||
git_index_time ctime;
|
||||
git_index_time mtime;
|
||||
@ -84,20 +64,79 @@ typedef struct git_index_entry {
|
||||
char *path;
|
||||
} git_index_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;
|
||||
/**
|
||||
* Bitmasks for on-disk fields of `git_index_entry`'s `flags`
|
||||
*
|
||||
* These bitmasks match the four fields in the `git_index_entry` `flags`
|
||||
* value both in memory and on disk. You can use them to interpret the
|
||||
* 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. */
|
||||
enum {
|
||||
typedef enum {
|
||||
GIT_INDEXCAP_IGNORE_CASE = 1,
|
||||
GIT_INDEXCAP_NO_FILEMODE = 2,
|
||||
GIT_INDEXCAP_NO_SYMLINKS = 4,
|
||||
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
|
||||
*
|
||||
@ -272,11 +311,9 @@ GIT_EXTERN(void) git_index_clear(git_index *index);
|
||||
/**
|
||||
* Get a pointer to one of the entries in the index
|
||||
*
|
||||
* The values of this entry can be modified (except the path)
|
||||
* and the changes will be written back to disk on the next
|
||||
* write() call.
|
||||
*
|
||||
* The entry should not be freed by the caller.
|
||||
* The entry is not modifiable and should not be freed. Because the
|
||||
* `git_index_entry` struct is a publicly defined struct, you should
|
||||
* be able to make your own permanent copy of the data if necessary.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @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
|
||||
*
|
||||
* The values of this entry can be modified (except the path)
|
||||
* and the changes will be written back to disk on the next
|
||||
* write() call.
|
||||
*
|
||||
* The entry should not be freed by the caller.
|
||||
* The entry is not modifiable and should not be freed. Because the
|
||||
* `git_index_entry` struct is a publicly defined struct, you should
|
||||
* be able to make your own permanent copy of the data if necessary.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @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
|
||||
*
|
||||
* This entry is calculated from the entry's flag
|
||||
* attribute like this:
|
||||
* This entry is calculated from the entry's flag attribute like this:
|
||||
*
|
||||
* (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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_conflict_add(
|
||||
git_index *index,
|
||||
git_index *index,
|
||||
const git_index_entry *ancestor_entry,
|
||||
const git_index_entry *our_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.
|
||||
*
|
||||
* The values of this entry can be modified (except the paths)
|
||||
* and the changes will be written back to disk on the next
|
||||
* write() call.
|
||||
* The entries are not modifiable and should not be freed. Because the
|
||||
* `git_index_entry` struct is a publicly defined struct, you should
|
||||
* be able to make your own permanent copy of the data if necessary.
|
||||
*
|
||||
* @param ancestor_out Pointer to store the ancestor 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 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.
|
||||
@ -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);
|
||||
|
||||
/**@}*/
|
||||
|
||||
/** @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
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_reuc_add(git_index *index, const char *path,
|
||||
int ancestor_mode, git_oid *ancestor_id,
|
||||
int our_mode, git_oid *our_id,
|
||||
int their_mode, git_oid *their_id);
|
||||
GIT_EXTERN(int) git_index_conflict_iterator_new(
|
||||
git_index_conflict_iterator **iterator_out,
|
||||
git_index *index);
|
||||
|
||||
/**
|
||||
* 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 n position of the resolve undo entry to remove
|
||||
* @return 0 or an error code
|
||||
* @param ancestor_out Pointer to store the ancestor side of the conflict
|
||||
* @param our_out Pointer to store our side of the conflict
|
||||
* @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
|
||||
* @return 0 or an error code
|
||||
* @param iterator pointer to the iterator
|
||||
*/
|
||||
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__
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
#include "oid.h"
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
@ -41,7 +21,7 @@ typedef struct git_indexer_stream git_indexer_stream;
|
||||
* @param out where to store the indexer instance
|
||||
* @param path to the directory where the packfile should be stored
|
||||
* @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_indexer_stream **out,
|
||||
|
||||
@ -283,18 +283,18 @@ _inline
|
||||
#endif // STATIC_IMAXDIV ]
|
||||
imaxdiv_t __cdecl imaxdiv(intmax_t numer, intmax_t denom)
|
||||
{
|
||||
imaxdiv_t result;
|
||||
imaxdiv_t result;
|
||||
|
||||
result.quot = numer / denom;
|
||||
result.rem = numer % denom;
|
||||
result.quot = numer / denom;
|
||||
result.rem = numer % denom;
|
||||
|
||||
if (numer < 0 && result.rem > 0) {
|
||||
// did division wrong; must fix up
|
||||
++result.quot;
|
||||
result.rem -= denom;
|
||||
}
|
||||
if (numer < 0 && result.rem > 0) {
|
||||
// did division wrong; must fix up
|
||||
++result.quot;
|
||||
result.rem -= denom;
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
// 7.8.2.3 The strtoimax and strtoumax functions
|
||||
|
||||
@ -7,19 +7,64 @@
|
||||
#ifndef INCLUDE_git_merge_h__
|
||||
#define INCLUDE_git_merge_h__
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
#include "oid.h"
|
||||
#include "git2/common.h"
|
||||
#include "git2/types.h"
|
||||
#include "git2/oid.h"
|
||||
#include "git2/checkout.h"
|
||||
#include "git2/index.h"
|
||||
|
||||
/**
|
||||
* @file git2/merge.h
|
||||
* @brief Git merge-base routines
|
||||
* @defgroup git_revwalk Git merge-base routines
|
||||
* @brief Git merge routines
|
||||
* @defgroup git_merge Git merge routines
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
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
|
||||
*
|
||||
@ -50,6 +95,79 @@ GIT_EXTERN(int) git_merge_base_many(
|
||||
const git_oid input_array[],
|
||||
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
|
||||
#endif
|
||||
|
||||
@ -10,8 +10,6 @@
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
#include "oid.h"
|
||||
#include "odb_backend.h"
|
||||
#include "indexer.h"
|
||||
|
||||
/**
|
||||
* @file git2/odb.h
|
||||
@ -22,6 +20,11 @@
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@ -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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
#endif
|
||||
|
||||
@ -7,143 +7,84 @@
|
||||
#ifndef INCLUDE_git_odb_backend_h__
|
||||
#define INCLUDE_git_odb_backend_h__
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
#include "oid.h"
|
||||
#include "indexer.h"
|
||||
#include "git2/common.h"
|
||||
#include "git2/types.h"
|
||||
|
||||
/**
|
||||
* @file git2/backend.h
|
||||
* @brief Git custom backend functions
|
||||
* @defgroup git_backend Git custom backend API
|
||||
* @defgroup git_odb Git object database routines
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
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 {
|
||||
unsigned int version;
|
||||
git_odb *odb;
|
||||
GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **out, const char *objects_dir, int compression_level, int do_fsync);
|
||||
|
||||
/* 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 *,
|
||||
struct 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 *,
|
||||
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}
|
||||
/**
|
||||
* Create a backend out of a single packfile
|
||||
*
|
||||
* This can be useful for inspecting the contents of a single
|
||||
* packfile.
|
||||
*
|
||||
* @param out location to store the odb backend pointer
|
||||
* @param index_file path to the packfile's .idx file
|
||||
*
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_backend_one_pack(git_odb_backend **out, const char *index_file);
|
||||
|
||||
/** Streaming mode */
|
||||
enum {
|
||||
typedef enum {
|
||||
GIT_STREAM_RDONLY = (1 << 1),
|
||||
GIT_STREAM_WRONLY = (1 << 2),
|
||||
GIT_STREAM_RW = (GIT_STREAM_RDONLY | GIT_STREAM_WRONLY),
|
||||
};
|
||||
} git_odb_stream_t;
|
||||
|
||||
/** A stream to read/write from a backend */
|
||||
struct git_odb_stream {
|
||||
struct git_odb_backend *backend;
|
||||
git_odb_backend *backend;
|
||||
unsigned int mode;
|
||||
|
||||
int (*read)(struct git_odb_stream *stream, char *buffer, size_t len);
|
||||
int (*write)(struct git_odb_stream *stream, const char *buffer, size_t len);
|
||||
int (*finalize_write)(git_oid *oid_p, struct git_odb_stream *stream);
|
||||
void (*free)(struct git_odb_stream *stream);
|
||||
int (*read)(git_odb_stream *stream, char *buffer, size_t len);
|
||||
int (*write)(git_odb_stream *stream, const char *buffer, size_t len);
|
||||
int (*finalize_write)(git_oid *oid_p, git_odb_stream *stream);
|
||||
void (*free)(git_odb_stream *stream);
|
||||
};
|
||||
|
||||
/** A stream to write a pack file to the ODB */
|
||||
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 (*commit)(struct git_odb_writepack *writepack, git_transfer_progress *stats);
|
||||
void (*free)(struct git_odb_writepack *writepack);
|
||||
int (*add)(git_odb_writepack *writepack, const void *data, size_t size, git_transfer_progress *stats);
|
||||
int (*commit)(git_odb_writepack *writepack, git_transfer_progress *stats);
|
||||
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
|
||||
|
||||
#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
|
||||
* oid digits are written; a '\\0' terminator must be added
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -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.
|
||||
*
|
||||
* @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
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* 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.
|
||||
* oid c-string will be truncated to n-1 characters (but will still be
|
||||
* NUL-byte terminated).
|
||||
*
|
||||
* 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 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.
|
||||
* @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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
GIT_EXTERN(int) git_oid_cmp(const git_oid *a, const git_oid *b);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
||||
@ -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);
|
||||
|
||||
/**
|
||||
* 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 path Directory to store the new pack and index
|
||||
* @param id The oid of the commit
|
||||
*
|
||||
* @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);
|
||||
/**
|
||||
|
||||
@ -21,22 +21,6 @@
|
||||
*/
|
||||
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.
|
||||
*
|
||||
@ -78,20 +62,6 @@ GIT_EXTERN(int) git_refdb_compress(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
|
||||
|
||||
|
||||
@ -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 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.
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_name_to_id(
|
||||
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.
|
||||
*
|
||||
@ -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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -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
|
||||
* 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
|
||||
* @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.
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* @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
|
||||
* @return 0 on success, EINVALIDSPEC, EEXISTS or an error code
|
||||
*
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_rename(
|
||||
git_reference **out,
|
||||
git_reference **new_ref,
|
||||
git_reference *ref,
|
||||
const char *new_name,
|
||||
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.
|
||||
*
|
||||
* 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
|
||||
* values are owned by the user and should be free'd manually when no
|
||||
* 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
|
||||
* the reference names will be stored
|
||||
* @param repo Repository where to find the refs
|
||||
* @param list_flags Filtering flags for the reference listing
|
||||
* @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.
|
||||
*
|
||||
* 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
|
||||
* repository, receiving the name of the reference and the `payload` value
|
||||
* passed to this method. Returning a non-zero value from the callback
|
||||
* will terminate the iteration.
|
||||
*
|
||||
* @param repo Repository where to find the refs
|
||||
* @param list_flags Filtering flags for the reference listing.
|
||||
* @param callback Function which will be called for every listed ref
|
||||
* @param payload Additional data to pass to the callback
|
||||
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_foreach(
|
||||
git_repository *repo,
|
||||
unsigned int list_flags,
|
||||
git_reference_foreach_cb callback,
|
||||
void *payload);
|
||||
|
||||
GIT_EXTERN(int) git_reference_foreach_name(
|
||||
git_repository *repo,
|
||||
git_reference_foreach_name_cb callback,
|
||||
void *payload);
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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 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 payload Additional data to pass to the callback
|
||||
* @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_repository *repo,
|
||||
const char *glob,
|
||||
unsigned int list_flags,
|
||||
git_reference_foreach_cb callback,
|
||||
git_reference_foreach_name_cb callback,
|
||||
void *payload);
|
||||
|
||||
/**
|
||||
@ -411,6 +462,13 @@ typedef enum {
|
||||
* (e.g., foo/<star>/bar but not foo/bar<star>).
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
@ -448,9 +506,9 @@ GIT_EXTERN(int) git_reference_normalize_name(
|
||||
* If you pass `GIT_OBJ_ANY` as the target type, then the object
|
||||
* 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 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).
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
#endif
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
#include "net.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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
@ -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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
||||
@ -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);
|
||||
|
||||
/**
|
||||
* Set the remote's fetch refspec
|
||||
* Add a fetch refspec to the remote
|
||||
*
|
||||
* @param remote the remote
|
||||
* @apram spec the new fetch refspec
|
||||
* @apram refspec the new fetch refspec
|
||||
* @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
|
||||
* @return a pointer to the fetch refspec or NULL if it doesn't exist
|
||||
* 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(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 spec the new push refspec
|
||||
* @param refspec the new push refspec
|
||||
* @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
|
||||
* @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
|
||||
@ -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.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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
|
||||
* this is called inline with network and indexing operations, so performance
|
||||
* may be affected.
|
||||
* @param progress_payload payload for the progress callback
|
||||
* @param payload payload for the progress callback
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @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);
|
||||
|
||||
@ -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);
|
||||
|
||||
typedef enum {
|
||||
GIT_REMOTE_DOWNLOAD_TAGS_UNSET,
|
||||
GIT_REMOTE_DOWNLOAD_TAGS_NONE,
|
||||
GIT_REMOTE_DOWNLOAD_TAGS_AUTO,
|
||||
GIT_REMOTE_DOWNLOAD_TAGS_ALL
|
||||
GIT_REMOTE_DOWNLOAD_TAGS_AUTO = 0,
|
||||
GIT_REMOTE_DOWNLOAD_TAGS_NONE = 1,
|
||||
GIT_REMOTE_DOWNLOAD_TAGS_ALL = 2
|
||||
} git_remote_autotag_option_t;
|
||||
|
||||
/**
|
||||
|
||||
@ -123,6 +123,19 @@ GIT_EXTERN(int) git_repository_open_ext(
|
||||
unsigned int flags,
|
||||
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
|
||||
*
|
||||
@ -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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -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);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
@ -482,21 +448,6 @@ GIT_EXTERN(void) git_repository_set_refdb(
|
||||
*/
|
||||
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
|
||||
*
|
||||
@ -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
|
||||
* remove the file after you create the commit.
|
||||
*
|
||||
* If the repository message exists and there are no errors reading it, this
|
||||
* returns the bytes needed to store the message in memory (i.e. message
|
||||
* file size plus one terminating NUL byte). That value is returned even if
|
||||
* `out` is NULL or `len` is shorter than the necessary size.
|
||||
*
|
||||
* The `out` buffer will *always* be NUL terminated, even if truncation
|
||||
* occurs.
|
||||
*
|
||||
* @param out Buffer to write data into or NULL to just read required size
|
||||
* @param len Length of buffer in bytes
|
||||
* @param len Length of `out` buffer in bytes
|
||||
* @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);
|
||||
|
||||
@ -559,7 +519,7 @@ typedef int (*git_repository_mergehead_foreach_cb)(const git_oid *oid,
|
||||
*
|
||||
* @param repo A repository object
|
||||
* @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
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_hashfile(
|
||||
git_oid *out,
|
||||
git_repository *repo,
|
||||
const char *path,
|
||||
git_otype type,
|
||||
const char *as_path);
|
||||
git_oid *out,
|
||||
git_repository *repo,
|
||||
const char *path,
|
||||
git_otype type,
|
||||
const char *as_path);
|
||||
|
||||
/**
|
||||
* Make the repository HEAD point to the specified reference.
|
||||
@ -675,6 +635,37 @@ typedef enum {
|
||||
*/
|
||||
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
|
||||
#endif
|
||||
|
||||
@ -72,9 +72,9 @@ GIT_EXTERN(int) git_reset(
|
||||
* @return 0 on success or an error code < 0
|
||||
*/
|
||||
GIT_EXTERN(int) git_reset_default(
|
||||
git_repository *repo,
|
||||
git_object *target,
|
||||
git_strarray* pathspecs);
|
||||
git_repository *repo,
|
||||
git_object *target,
|
||||
git_strarray* pathspecs);
|
||||
|
||||
/** @} */
|
||||
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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
@ -89,7 +89,7 @@ typedef int (*git_stash_cb)(
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @param payload Extra parameter to callback function.
|
||||
|
||||
@ -42,6 +42,7 @@ typedef enum {
|
||||
GIT_STATUS_WT_MODIFIED = (1u << 8),
|
||||
GIT_STATUS_WT_DELETED = (1u << 9),
|
||||
GIT_STATUS_WT_TYPECHANGE = (1u << 10),
|
||||
GIT_STATUS_WT_RENAMED = (1u << 11),
|
||||
|
||||
GIT_STATUS_IGNORED = (1u << 14),
|
||||
} git_status_t;
|
||||
@ -58,44 +59,20 @@ typedef enum {
|
||||
typedef int (*git_status_cb)(
|
||||
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.
|
||||
*
|
||||
* - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This is the
|
||||
* rough equivalent of `git status --porcelain` where each file
|
||||
* will receive a callback indicating its status in the index and
|
||||
* in the workdir.
|
||||
* - GIT_STATUS_SHOW_INDEX_ONLY will only make callbacks for index
|
||||
* side of status. The status of the index contents relative to
|
||||
* the HEAD will be given.
|
||||
* - GIT_STATUS_SHOW_WORKDIR_ONLY will only make callbacks for the
|
||||
* workdir side of status, reporting the status of workdir content
|
||||
* relative to the index.
|
||||
* - GIT_STATUS_SHOW_INDEX_THEN_WORKDIR behaves like index-only
|
||||
* 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`.
|
||||
* - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This roughly
|
||||
* matches `git status --porcelain` where each file gets a callback
|
||||
* indicating its status in the index and in the working directory.
|
||||
* - GIT_STATUS_SHOW_INDEX_ONLY only gives status based on HEAD to index
|
||||
* comparison, not looking at working directory changes.
|
||||
* - GIT_STATUS_SHOW_WORKDIR_ONLY only gives status based on index to
|
||||
* working directory comparison, not comparing the index to the HEAD.
|
||||
* - GIT_STATUS_SHOW_INDEX_THEN_WORKDIR runs index-only then workdir-only,
|
||||
* issuing (up to) two callbacks per file (first index, then workdir).
|
||||
* This is slightly more efficient than separate calls and can make it
|
||||
* easier to emulate plain `git status` text output.
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
|
||||
@ -110,26 +87,36 @@ typedef enum {
|
||||
* - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should be made
|
||||
* on untracked files. These will only be made if the workdir files are
|
||||
* included in the status "show" option.
|
||||
* - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files should get
|
||||
* callbacks. Again, these callbacks will only be made if the workdir
|
||||
* files are included in the status "show" option. Right now, there is
|
||||
* no option to include all files in directories that are ignored
|
||||
* completely.
|
||||
* - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files get callbacks.
|
||||
* Again, these callbacks will only be made if the workdir files are
|
||||
* included in the status "show" option.
|
||||
* - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback should be
|
||||
* made even on unmodified files.
|
||||
* - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that directories which
|
||||
* appear to be submodules should just be skipped over.
|
||||
* - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that the contents of
|
||||
* untracked directories should be included in the status. Normally if
|
||||
* an entire directory is new, then just the top-level directory will be
|
||||
* included (with a trailing slash on the entry name). Given this flag,
|
||||
* the directory itself will not be included, but all the files in it
|
||||
* will.
|
||||
* - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that submodules should be
|
||||
* skipped. This only applies if there are no pending typechanges to
|
||||
* the submodule (either from or to another type).
|
||||
* - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that all files in
|
||||
* untracked directories should be included. Normally if an entire
|
||||
* directory is new, then just the top-level directory is included (with
|
||||
* a trailing slash on the entry name). This flag says to include all
|
||||
* of the individual files in the directory instead.
|
||||
* - 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
|
||||
* ignored directories should be included in the status. This is like
|
||||
* 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
|
||||
* 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.
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0),
|
||||
GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1),
|
||||
GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2),
|
||||
GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3),
|
||||
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
|
||||
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
|
||||
GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6),
|
||||
GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0),
|
||||
GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1),
|
||||
GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2),
|
||||
GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3),
|
||||
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
|
||||
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
|
||||
GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6),
|
||||
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1u << 7),
|
||||
GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8),
|
||||
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1u << 9),
|
||||
GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10),
|
||||
} git_status_opt_t;
|
||||
|
||||
#define GIT_STATUS_OPT_DEFAULTS \
|
||||
@ -177,6 +168,47 @@ typedef struct {
|
||||
#define GIT_STATUS_OPTIONS_VERSION 1
|
||||
#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.
|
||||
*
|
||||
@ -215,6 +247,49 @@ GIT_EXTERN(int) git_status_file(
|
||||
git_repository *repo,
|
||||
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.
|
||||
*
|
||||
|
||||
@ -20,8 +20,8 @@ GIT_BEGIN_DECL
|
||||
|
||||
/** Array of strings */
|
||||
typedef struct git_strarray {
|
||||
char **strings;
|
||||
size_t count;
|
||||
char **strings;
|
||||
size_t count;
|
||||
} git_strarray;
|
||||
|
||||
/**
|
||||
|
||||
@ -103,20 +103,20 @@ typedef enum {
|
||||
* * WD_UNTRACKED - wd contains untracked files
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_SUBMODULE_STATUS_IN_HEAD = (1u << 0),
|
||||
GIT_SUBMODULE_STATUS_IN_INDEX = (1u << 1),
|
||||
GIT_SUBMODULE_STATUS_IN_CONFIG = (1u << 2),
|
||||
GIT_SUBMODULE_STATUS_IN_WD = (1u << 3),
|
||||
GIT_SUBMODULE_STATUS_INDEX_ADDED = (1u << 4),
|
||||
GIT_SUBMODULE_STATUS_INDEX_DELETED = (1u << 5),
|
||||
GIT_SUBMODULE_STATUS_INDEX_MODIFIED = (1u << 6),
|
||||
GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = (1u << 7),
|
||||
GIT_SUBMODULE_STATUS_WD_ADDED = (1u << 8),
|
||||
GIT_SUBMODULE_STATUS_WD_DELETED = (1u << 9),
|
||||
GIT_SUBMODULE_STATUS_WD_MODIFIED = (1u << 10),
|
||||
GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = (1u << 11),
|
||||
GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = (1u << 12),
|
||||
GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13),
|
||||
GIT_SUBMODULE_STATUS_IN_HEAD = (1u << 0),
|
||||
GIT_SUBMODULE_STATUS_IN_INDEX = (1u << 1),
|
||||
GIT_SUBMODULE_STATUS_IN_CONFIG = (1u << 2),
|
||||
GIT_SUBMODULE_STATUS_IN_WD = (1u << 3),
|
||||
GIT_SUBMODULE_STATUS_INDEX_ADDED = (1u << 4),
|
||||
GIT_SUBMODULE_STATUS_INDEX_DELETED = (1u << 5),
|
||||
GIT_SUBMODULE_STATUS_INDEX_MODIFIED = (1u << 6),
|
||||
GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = (1u << 7),
|
||||
GIT_SUBMODULE_STATUS_WD_ADDED = (1u << 8),
|
||||
GIT_SUBMODULE_STATUS_WD_DELETED = (1u << 9),
|
||||
GIT_SUBMODULE_STATUS_WD_MODIFIED = (1u << 10),
|
||||
GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = (1u << 11),
|
||||
GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = (1u << 12),
|
||||
GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13),
|
||||
} git_submodule_status_t;
|
||||
|
||||
#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
|
||||
* 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
|
||||
* @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
|
||||
* 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
|
||||
* @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.
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_tag_lookup(
|
||||
git_tag **out, git_repository *repo, const git_oid *id)
|
||||
{
|
||||
return git_object_lookup(
|
||||
(git_object **)out, repo, id, (git_otype)GIT_OBJ_TAG);
|
||||
}
|
||||
GIT_EXTERN(int) git_tag_lookup(
|
||||
git_tag **out, git_repository *repo, const git_oid *id);
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_tag_lookup_prefix(
|
||||
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);
|
||||
}
|
||||
GIT_EXTERN(int) git_tag_lookup_prefix(
|
||||
git_tag **out, git_repository *repo, const git_oid *id, size_t len);
|
||||
|
||||
/**
|
||||
* Close an open tag
|
||||
@ -66,12 +58,7 @@ GIT_INLINE(int) git_tag_lookup_prefix(
|
||||
*
|
||||
* @param tag the tag to close
|
||||
*/
|
||||
|
||||
GIT_INLINE(void) git_tag_free(git_tag *tag)
|
||||
{
|
||||
git_object_free((git_object *)tag);
|
||||
}
|
||||
|
||||
GIT_EXTERN(void) git_tag_free(git_tag *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);
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
@ -182,6 +177,37 @@ GIT_EXTERN(int) git_tag_create(
|
||||
const char *message,
|
||||
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
|
||||
*
|
||||
|
||||
@ -32,7 +32,7 @@ typedef enum {
|
||||
|
||||
/** Errors that do not impact the program's execution */
|
||||
GIT_TRACE_ERROR = 2,
|
||||
|
||||
|
||||
/** Warnings that suggest abnormal data */
|
||||
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
|
||||
#endif
|
||||
|
||||
|
||||
@ -11,6 +11,10 @@
|
||||
#include "net.h"
|
||||
#include "types.h"
|
||||
|
||||
#ifdef GIT_SSH
|
||||
#include <libssh2.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @file git2/transport.h
|
||||
* @brief Git transport interfaces and functions
|
||||
@ -27,6 +31,8 @@ GIT_BEGIN_DECL
|
||||
typedef enum {
|
||||
/* git_cred_userpass_plaintext */
|
||||
GIT_CREDTYPE_USERPASS_PLAINTEXT = 1,
|
||||
GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE = 2,
|
||||
GIT_CREDTYPE_SSH_PUBLICKEY = 3,
|
||||
} git_credtype_t;
|
||||
|
||||
/* The base structure for all credential types */
|
||||
@ -43,6 +49,27 @@ typedef struct git_cred_userpass_plaintext {
|
||||
char *password;
|
||||
} 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.
|
||||
* 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 *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.
|
||||
*
|
||||
@ -319,6 +382,17 @@ GIT_EXTERN(int) git_smart_subtransport_git(
|
||||
git_smart_subtransport **out,
|
||||
git_transport* owner);
|
||||
|
||||
/**
|
||||
* Create an instance of the ssh subtransport.
|
||||
*
|
||||
* @param out The newly created subtransport
|
||||
* @param owner The smart transport to own this subtransport
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_smart_subtransport_ssh(
|
||||
git_smart_subtransport **out,
|
||||
git_transport* owner);
|
||||
|
||||
/*
|
||||
*** End interface for subtransports for the smart transport ***
|
||||
*/
|
||||
|
||||
@ -29,11 +29,8 @@ GIT_BEGIN_DECL
|
||||
* @param id Identity of the tree to locate.
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_tree_lookup(
|
||||
git_tree **out, git_repository *repo, const git_oid *id)
|
||||
{
|
||||
return git_object_lookup((git_object **)out, repo, id, GIT_OBJ_TREE);
|
||||
}
|
||||
GIT_EXTERN(int) git_tree_lookup(
|
||||
git_tree **out, git_repository *repo, const git_oid *id);
|
||||
|
||||
/**
|
||||
* Lookup a tree object from the repository,
|
||||
@ -41,21 +38,17 @@ GIT_INLINE(int) git_tree_lookup(
|
||||
*
|
||||
* @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 id identity of the tree to locate.
|
||||
* @param len the length of the short identifier
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_tree_lookup_prefix(
|
||||
GIT_EXTERN(int) git_tree_lookup_prefix(
|
||||
git_tree **out,
|
||||
git_repository *repo,
|
||||
const git_oid *id,
|
||||
size_t len)
|
||||
{
|
||||
return git_object_lookup_prefix(
|
||||
(git_object **)out, repo, id, len, GIT_OBJ_TREE);
|
||||
}
|
||||
size_t len);
|
||||
|
||||
/**
|
||||
* Close an open tree
|
||||
@ -67,10 +60,7 @@ GIT_INLINE(int) git_tree_lookup_prefix(
|
||||
*
|
||||
* @param tree The tree to close
|
||||
*/
|
||||
GIT_INLINE(void) git_tree_free(git_tree *tree)
|
||||
{
|
||||
git_object_free((git_object *)tree);
|
||||
}
|
||||
GIT_EXTERN(void) git_tree_free(git_tree *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
|
||||
*/
|
||||
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
|
||||
@ -120,7 +110,7 @@ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname(
|
||||
* @return the tree entry; NULL if not found
|
||||
*/
|
||||
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.
|
||||
@ -146,12 +136,12 @@ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byoid(
|
||||
*
|
||||
* @param out Pointer where to store the tree entry
|
||||
* @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
|
||||
*/
|
||||
GIT_EXTERN(int) git_tree_entry_bypath(
|
||||
git_tree_entry **out,
|
||||
git_tree *root,
|
||||
const git_tree *root,
|
||||
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.
|
||||
*
|
||||
* @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 entry a tree entry
|
||||
* @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
|
||||
*
|
||||
* @param tree a previously loaded treebuilder.
|
||||
* @param bld a previously loaded treebuilder.
|
||||
* @return the number of entries in the treebuilder
|
||||
*/
|
||||
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. */
|
||||
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 */
|
||||
typedef struct git_config git_config;
|
||||
|
||||
@ -165,6 +168,16 @@ typedef struct git_signature {
|
||||
/** In-memory representation of a 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. */
|
||||
typedef enum {
|
||||
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_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
|
||||
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
#ifndef 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_MINOR 18
|
||||
#define LIBGIT2_VER_MINOR 19
|
||||
#define LIBGIT2_VER_REVISION 0
|
||||
|
||||
#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(
|
||||
const char **value,
|
||||
git_repository *repo,
|
||||
git_repository *repo,
|
||||
uint32_t flags,
|
||||
const char *pathname,
|
||||
const char *name)
|
||||
@ -88,10 +88,10 @@ typedef struct {
|
||||
|
||||
int git_attr_get_many(
|
||||
const char **values,
|
||||
git_repository *repo,
|
||||
git_repository *repo,
|
||||
uint32_t flags,
|
||||
const char *pathname,
|
||||
size_t num_attr,
|
||||
size_t num_attr,
|
||||
const char **names)
|
||||
{
|
||||
int error;
|
||||
@ -151,7 +151,7 @@ cleanup:
|
||||
|
||||
|
||||
int git_attr_foreach(
|
||||
git_repository *repo,
|
||||
git_repository *repo,
|
||||
uint32_t flags,
|
||||
const char *pathname,
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
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(
|
||||
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;
|
||||
int error;
|
||||
const char *cfgval = NULL;
|
||||
|
||||
if (!(error = git_config_get_string(out, cfg, key)))
|
||||
return 0;
|
||||
*out = NULL;
|
||||
|
||||
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();
|
||||
error = 0;
|
||||
|
||||
if (!git_futils_find_xdg_file(&buf, fallback))
|
||||
*out = git_buf_detach(&buf);
|
||||
else
|
||||
*out = NULL;
|
||||
|
||||
git_buf_free(&buf);
|
||||
}
|
||||
|
||||
git_buf_free(&buf);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -696,6 +703,12 @@ void git_attr_cache_flush(
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@ -397,7 +397,8 @@ int git_attr_fnmatch__parse(
|
||||
|
||||
*base = scan;
|
||||
|
||||
spec->length = scan - pattern;
|
||||
if ((spec->length = scan - pattern) == 0)
|
||||
return GIT_ENOTFOUND;
|
||||
|
||||
if (pattern[spec->length - 1] == '/') {
|
||||
spec->length--;
|
||||
@ -497,7 +498,7 @@ int git_attr_assignment__parse(
|
||||
|
||||
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') {
|
||||
const char *name_start, *value_start;
|
||||
|
||||
@ -47,14 +47,14 @@ typedef struct {
|
||||
typedef struct {
|
||||
git_refcount unused;
|
||||
const char *name;
|
||||
uint32_t name_hash;
|
||||
uint32_t name_hash;
|
||||
} git_attr_name;
|
||||
|
||||
typedef struct {
|
||||
git_refcount rc; /* for macros */
|
||||
char *name;
|
||||
uint32_t name_hash;
|
||||
const char *value;
|
||||
uint32_t name_hash;
|
||||
const char *value;
|
||||
} git_attr_assignment;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@ -13,10 +13,10 @@
|
||||
typedef struct {
|
||||
int initialized;
|
||||
git_pool pool;
|
||||
git_strmap *files; /* hash path to git_attr_file of rules */
|
||||
git_strmap *macros; /* hash name to vector<git_attr_assignment> */
|
||||
const char *cfg_attr_file; /* cached value of core.attributesfile */
|
||||
const char *cfg_excl_file; /* cached value of core.excludesfile */
|
||||
git_strmap *files; /* hash path to git_attr_file of rules */
|
||||
git_strmap *macros; /* hash name to vector<git_attr_assignment> */
|
||||
char *cfg_attr_file; /* cached value of core.attributesfile */
|
||||
char *cfg_excl_file; /* cached value of core.excludesfile */
|
||||
} git_attr_cache;
|
||||
|
||||
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/object.h"
|
||||
#include "git2/repository.h"
|
||||
#include "git2/odb_backend.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "filebuf.h"
|
||||
#include "blob.h"
|
||||
#include "filter.h"
|
||||
#include "buf_text.h"
|
||||
@ -17,32 +19,34 @@
|
||||
const void *git_blob_rawcontent(const git_blob *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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
git_cached_obj_incref((git_cached_obj *)odb_obj);
|
||||
blob->odb_object = odb_obj;
|
||||
((git_blob *)blob)->odb_object = odb_obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -314,8 +318,8 @@ int git_blob_is_binary(git_blob *blob)
|
||||
|
||||
assert(blob);
|
||||
|
||||
content.ptr = blob->odb_object->raw.data;
|
||||
content.size = min(blob->odb_object->raw.len, 4000);
|
||||
content.ptr = blob->odb_object->buffer;
|
||||
content.size = min(blob->odb_object->cached.size, 4000);
|
||||
|
||||
return git_buf_text_is_binary(&content);
|
||||
}
|
||||
|
||||
@ -17,8 +17,8 @@ struct git_blob {
|
||||
git_odb_object *odb_object;
|
||||
};
|
||||
|
||||
void git_blob__free(git_blob *blob);
|
||||
int git_blob__parse(git_blob *blob, git_odb_object *obj);
|
||||
void git_blob__free(void *blob);
|
||||
int git_blob__parse(void *blob, git_odb_object *obj);
|
||||
int git_blob__getbuf(git_buf *buffer, git_blob *blob);
|
||||
|
||||
#endif
|
||||
|
||||
116
src/branch.c
116
src/branch.c
@ -11,6 +11,7 @@
|
||||
#include "config.h"
|
||||
#include "refspec.h"
|
||||
#include "refs.h"
|
||||
#include "remote.h"
|
||||
|
||||
#include "git2/branch.h"
|
||||
|
||||
@ -123,40 +124,48 @@ on_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(
|
||||
git_repository *repo,
|
||||
unsigned int list_flags,
|
||||
git_branch_foreach_cb branch_cb,
|
||||
git_branch_foreach_cb callback,
|
||||
void *payload)
|
||||
{
|
||||
branch_foreach_filter filter;
|
||||
git_reference_iterator *iter;
|
||||
git_reference *ref;
|
||||
int error = 0;
|
||||
|
||||
filter.branch_cb = branch_cb;
|
||||
filter.branch_type = list_flags;
|
||||
filter.callback_payload = payload;
|
||||
if (git_reference_iterator_new(&iter, repo) < 0)
|
||||
return -1;
|
||||
|
||||
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(
|
||||
@ -175,18 +184,21 @@ int git_branch_move(
|
||||
if (!git_reference_is_branch(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_printf(&old_config_section, "branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR))) < 0 ||
|
||||
(error = git_buf_printf(&new_config_section, "branch.%s", new_branch_name)) < 0)
|
||||
error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name);
|
||||
if (error < 0)
|
||||
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),
|
||||
git_buf_cstr(&old_config_section),
|
||||
git_buf_cstr(&new_config_section))) < 0)
|
||||
goto done;
|
||||
|
||||
if ((error = git_reference_rename(out, branch, git_buf_cstr(&new_reference_name), force)) < 0)
|
||||
goto done;
|
||||
|
||||
error = git_reference_rename(out, branch, git_buf_cstr(&new_reference_name), force);
|
||||
|
||||
done:
|
||||
git_buf_free(&new_reference_name);
|
||||
@ -275,6 +287,8 @@ int git_branch_upstream__name(
|
||||
goto cleanup;
|
||||
|
||||
if (!*remote_name || !*merge_name) {
|
||||
giterr_set(GITERR_REFERENCE,
|
||||
"branch '%s' does not have an upstream", canonical_branch_name);
|
||||
error = GIT_ENOTFOUND;
|
||||
goto cleanup;
|
||||
}
|
||||
@ -283,12 +297,10 @@ int git_branch_upstream__name(
|
||||
if ((error = git_remote_load(&remote, repo, remote_name)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
refspec = git_remote_fetchspec(remote);
|
||||
if (refspec == NULL
|
||||
|| refspec->src == NULL
|
||||
|| refspec->dst == NULL) {
|
||||
error = GIT_ENOTFOUND;
|
||||
goto cleanup;
|
||||
refspec = git_remote__matching_refspec(remote, merge_name);
|
||||
if (!refspec) {
|
||||
error = GIT_ENOTFOUND;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
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)
|
||||
continue;
|
||||
|
||||
fetchspec = git_remote_fetchspec(remote);
|
||||
|
||||
/* Defensivly check that we have a fetchspec */
|
||||
if (fetchspec &&
|
||||
git_refspec_dst_matches(fetchspec, canonical_branch_name)) {
|
||||
fetchspec = git_remote__matching_dst_refspec(remote, canonical_branch_name);
|
||||
if (fetchspec) {
|
||||
/* If we have not already set out yet, then set
|
||||
* it to the matching remote name. Otherwise
|
||||
* 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];
|
||||
} else {
|
||||
git_remote_free(remote);
|
||||
|
||||
giterr_set(GITERR_REFERENCE,
|
||||
"Reference '%s' is ambiguous", canonical_branch_name);
|
||||
error = GIT_EAMBIGUOUS;
|
||||
goto cleanup;
|
||||
}
|
||||
@ -358,6 +370,8 @@ static int remote_name(git_buf *buf, git_repository *repo, const char *canonical
|
||||
git_buf_clear(buf);
|
||||
error = git_buf_puts(buf, remote_name);
|
||||
} else {
|
||||
giterr_set(GITERR_REFERENCE,
|
||||
"Could not determine remote for '%s'", canonical_branch_name);
|
||||
error = GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
@ -377,7 +391,7 @@ int git_branch_remote_name(char *buffer, size_t buffer_len, git_repository *repo
|
||||
if (buffer)
|
||||
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);
|
||||
|
||||
return ret;
|
||||
@ -494,8 +508,11 @@ int git_branch_set_upstream(git_reference *branch, const char *upstream_name)
|
||||
local = 1;
|
||||
else if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_REMOTE) == 0)
|
||||
local = 0;
|
||||
else
|
||||
else {
|
||||
giterr_set(GITERR_REFERENCE,
|
||||
"Cannot set upstream for branch '%s'", shortname);
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
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;
|
||||
} else {
|
||||
/* Get the remoe-tracking branch's refname in its repo */
|
||||
if (git_remote_load(&remote, repo, git_buf_cstr(&value)) < 0)
|
||||
goto on_error;
|
||||
|
||||
fetchspec = git_remote_fetchspec(remote);
|
||||
fetchspec = git_remote__matching_dst_refspec(remote, git_reference_name(upstream));
|
||||
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;
|
||||
|
||||
git_remote_free(remote);
|
||||
|
||||
294
src/cache.c
294
src/cache.c
@ -11,100 +11,270 @@
|
||||
#include "thread-utils.h"
|
||||
#include "util.h"
|
||||
#include "cache.h"
|
||||
#include "odb.h"
|
||||
#include "object.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)
|
||||
size = 8;
|
||||
size = git__size_t_powerof2(size);
|
||||
if (type < 0 || (size_t)type >= ARRAY_SIZE(git_cache__max_object_size)) {
|
||||
giterr_set(GITERR_INVALID, "type out of range");
|
||||
return -1;
|
||||
}
|
||||
|
||||
cache->size_mask = size - 1;
|
||||
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 *));
|
||||
git_cache__max_object_size[type] = size;
|
||||
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)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
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_cache_clear(cache);
|
||||
git_oidmap_free(cache->map);
|
||||
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;
|
||||
git_cached_obj *node = NULL, *result = NULL;
|
||||
uint32_t seed = rand();
|
||||
size_t evict_count = 8;
|
||||
ssize_t evicted_memory = 0;
|
||||
|
||||
memcpy(&hash, oid->id, sizeof(hash));
|
||||
|
||||
if (git_mutex_lock(&cache->lock)) {
|
||||
giterr_set(GITERR_THREAD, "unable to lock cache mutex");
|
||||
return NULL;
|
||||
/* do not infinite loop if there's not enough entries to evict */
|
||||
if (evict_count > kh_size(cache->map)) {
|
||||
clear_cache(cache);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
node = cache->nodes[hash & cache->size_mask];
|
||||
while (evict_count > 0) {
|
||||
khiter_t pos = seed++ % kh_end(cache->map);
|
||||
|
||||
if (node != NULL && git_oid_cmp(&node->oid, oid) == 0) {
|
||||
git_cached_obj_incref(node);
|
||||
result = node;
|
||||
if (kh_exist(cache->map, pos)) {
|
||||
git_cached_obj *evict = kh_val(cache->map, pos);
|
||||
|
||||
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;
|
||||
uint32_t hash;
|
||||
size_t max_size = git_cache__max_object_size[object_type];
|
||||
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)) {
|
||||
giterr_set(GITERR_THREAD, "unable to lock cache mutex");
|
||||
if (!git_cache__enabled || git_mutex_lock(&cache->lock) < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
{
|
||||
git_cached_obj *node = cache->nodes[hash & cache->size_mask];
|
||||
pos = kh_get(oid, cache->map, oid);
|
||||
if (pos != kh_end(cache->map)) {
|
||||
entry = kh_val(cache->map, pos);
|
||||
|
||||
/* increase the refcount on this object, because
|
||||
* the cache now owns it */
|
||||
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;
|
||||
if (flags && entry->flags != flags) {
|
||||
entry = NULL;
|
||||
} else {
|
||||
git_cached_obj_decref(node, cache->free_obj);
|
||||
cache->nodes[hash & cache->size_mask] = entry;
|
||||
git_cached_obj_incref(entry);
|
||||
}
|
||||
|
||||
/* increase the refcount again, because we are
|
||||
* returning it to the user */
|
||||
git_cached_obj_incref(entry);
|
||||
|
||||
}
|
||||
|
||||
git_mutex_unlock(&cache->lock);
|
||||
|
||||
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 "thread-utils.h"
|
||||
#include "oidmap.h"
|
||||
|
||||
#define GIT_DEFAULT_CACHE_SIZE 128
|
||||
|
||||
typedef void (*git_cached_obj_freeptr)(void *);
|
||||
enum {
|
||||
GIT_CACHE_STORE_ANY = 0,
|
||||
GIT_CACHE_STORE_RAW = 1,
|
||||
GIT_CACHE_STORE_PARSED = 2
|
||||
};
|
||||
|
||||
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_cached_obj;
|
||||
|
||||
typedef struct {
|
||||
git_cached_obj **nodes;
|
||||
git_mutex lock;
|
||||
|
||||
unsigned int lru_count;
|
||||
size_t size_mask;
|
||||
git_cached_obj_freeptr free_obj;
|
||||
git_oidmap *map;
|
||||
git_mutex lock;
|
||||
ssize_t used_memory;
|
||||
} git_cache;
|
||||
|
||||
int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr);
|
||||
void git_cache_free(git_cache *cache);
|
||||
extern bool git_cache__enabled;
|
||||
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);
|
||||
void *git_cache_get(git_cache *cache, const git_oid *oid);
|
||||
int git_cache_set_max_object_size(git_otype type, size_t size);
|
||||
|
||||
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)
|
||||
{
|
||||
@ -43,12 +62,6 @@ GIT_INLINE(void) git_cached_obj_incref(void *_obj)
|
||||
git_atomic_inc(&obj->refcount);
|
||||
}
|
||||
|
||||
GIT_INLINE(void) git_cached_obj_decref(void *_obj, git_cached_obj_freeptr free_obj)
|
||||
{
|
||||
git_cached_obj *obj = _obj;
|
||||
|
||||
if (git_atomic_dec(&obj->refcount) == 0)
|
||||
free_obj(obj);
|
||||
}
|
||||
void git_cached_obj_decref(void *_obj);
|
||||
|
||||
#endif
|
||||
|
||||
242
src/checkout.c
242
src/checkout.c
@ -16,9 +16,11 @@
|
||||
#include "git2/config.h"
|
||||
#include "git2/diff.h"
|
||||
#include "git2/submodule.h"
|
||||
#include "git2/sys/index.h"
|
||||
|
||||
#include "refs.h"
|
||||
#include "repository.h"
|
||||
#include "index.h"
|
||||
#include "filter.h"
|
||||
#include "blob.h"
|
||||
#include "diff.h"
|
||||
@ -119,6 +121,7 @@ static bool checkout_is_workdir_modified(
|
||||
const git_index_entry *wditem)
|
||||
{
|
||||
git_oid oid;
|
||||
const git_index_entry *ie;
|
||||
|
||||
/* handle "modified" submodule */
|
||||
if (wditem->mode == GIT_FILEMODE_COMMIT) {
|
||||
@ -137,7 +140,18 @@ static bool checkout_is_workdir_modified(
|
||||
if (!sm_oid)
|
||||
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
|
||||
@ -151,7 +165,7 @@ static bool checkout_is_workdir_modified(
|
||||
wditem->file_size, &oid) < 0)
|
||||
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) \
|
||||
@ -176,6 +190,10 @@ static int checkout_action_common(
|
||||
action = (action & ~CHECKOUT_ACTION__UPDATE_BLOB) |
|
||||
CHECKOUT_ACTION__UPDATE_SUBMODULE;
|
||||
|
||||
/* to "update" a symlink, we must remove the old one first */
|
||||
if (delta->new_file.mode == GIT_FILEMODE_LINK && wd != NULL)
|
||||
action |= CHECKOUT_ACTION__REMOVE;
|
||||
|
||||
notify = GIT_CHECKOUT_NOTIFY_UPDATED;
|
||||
}
|
||||
|
||||
@ -202,9 +220,11 @@ static int checkout_action_no_wd(
|
||||
action = CHECKOUT_ACTION_IF(SAFE_CREATE, UPDATE_BLOB, NONE);
|
||||
break;
|
||||
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);
|
||||
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)*/
|
||||
if (delta->new_file.mode == GIT_FILEMODE_TREE)
|
||||
action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
|
||||
@ -454,6 +474,7 @@ static int checkout_action(
|
||||
int cmp = -1, act;
|
||||
int (*strcomp)(const char *, const char *) = data->diff->strcomp;
|
||||
int (*pfxcomp)(const char *str, const char *pfx) = data->diff->pfxcomp;
|
||||
int error;
|
||||
|
||||
/* move workdir iterator to follow along with deltas */
|
||||
|
||||
@ -477,9 +498,9 @@ static int checkout_action(
|
||||
if (cmp == 0) {
|
||||
if (wd->mode == GIT_FILEMODE_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;
|
||||
|
||||
*wditem_ptr = wd;
|
||||
continue;
|
||||
}
|
||||
@ -494,8 +515,10 @@ static int checkout_action(
|
||||
}
|
||||
|
||||
/* case 1 - handle wd item (if it matches pathspec) */
|
||||
if (checkout_action_wd_only(data, workdir, wd, pathspec) < 0 ||
|
||||
git_iterator_advance(&wd, workdir) < 0)
|
||||
if (checkout_action_wd_only(data, workdir, wd, pathspec) < 0)
|
||||
goto fail;
|
||||
if ((error = git_iterator_advance(&wd, workdir)) < 0 &&
|
||||
error != GIT_ITEROVER)
|
||||
goto fail;
|
||||
|
||||
*wditem_ptr = wd;
|
||||
@ -518,8 +541,9 @@ static int checkout_action(
|
||||
if (delta->status == GIT_DELTA_TYPECHANGE) {
|
||||
if (delta->old_file.mode == GIT_FILEMODE_TREE) {
|
||||
act = checkout_action_with_wd(data, delta, wd);
|
||||
if (git_iterator_advance_into(&wd, workdir) < 0)
|
||||
wd = NULL;
|
||||
if ((error = git_iterator_advance_into(&wd, workdir)) < 0 &&
|
||||
error != GIT_ENOTFOUND)
|
||||
goto fail;
|
||||
*wditem_ptr = wd;
|
||||
return act;
|
||||
}
|
||||
@ -529,8 +553,9 @@ static int checkout_action(
|
||||
delta->old_file.mode == GIT_FILEMODE_COMMIT)
|
||||
{
|
||||
act = checkout_action_with_wd(data, delta, wd);
|
||||
if (git_iterator_advance(&wd, workdir) < 0)
|
||||
wd = NULL;
|
||||
if ((error = git_iterator_advance(&wd, workdir)) < 0 &&
|
||||
error != GIT_ITEROVER)
|
||||
goto fail;
|
||||
*wditem_ptr = wd;
|
||||
return act;
|
||||
}
|
||||
@ -561,6 +586,9 @@ static int checkout_remaining_wd_items(
|
||||
error = git_iterator_advance(&wd, workdir);
|
||||
}
|
||||
|
||||
if (error == GIT_ITEROVER)
|
||||
error = 0;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -582,7 +610,8 @@ static int checkout_get_actions(
|
||||
git_pathspec_init(&pathspec, &data->opts.paths, &pathpool) < 0)
|
||||
return -1;
|
||||
|
||||
if ((error = git_iterator_current(&wditem, workdir)) < 0)
|
||||
if ((error = git_iterator_current(&wditem, workdir)) < 0 &&
|
||||
error != GIT_ITEROVER)
|
||||
goto fail;
|
||||
|
||||
deltas = &data->diff->deltas;
|
||||
@ -655,33 +684,26 @@ static int buffer_to_file(
|
||||
int file_open_flags,
|
||||
mode_t file_mode)
|
||||
{
|
||||
int fd, error;
|
||||
int error;
|
||||
|
||||
if ((error = git_futils_mkpath2file(path, dir_mode)) < 0)
|
||||
return error;
|
||||
|
||||
if ((fd = p_open(path, file_open_flags, file_mode)) < 0) {
|
||||
giterr_set(GITERR_OS, "Could not open '%s' for writing", path);
|
||||
return fd;
|
||||
if ((error = git_futils_writebuffer(
|
||||
buffer, path, file_open_flags, file_mode)) < 0)
|
||||
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) {
|
||||
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)
|
||||
if ((file_mode & 0100) != 0 && (error = p_chmod(path, file_mode)) < 0) {
|
||||
giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
|
||||
return error;
|
||||
}
|
||||
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int blob_content_to_file(
|
||||
@ -698,8 +720,8 @@ static int blob_content_to_file(
|
||||
git_vector filters = GIT_VECTOR_INIT;
|
||||
|
||||
/* Create a fake git_buf from the blob raw data... */
|
||||
filtered.ptr = blob->odb_object->raw.data;
|
||||
filtered.size = blob->odb_object->raw.len;
|
||||
filtered.ptr = (void *)git_blob_rawcontent(blob);
|
||||
filtered.size = (size_t)git_blob_rawsize(blob);
|
||||
/* ... and make sure it doesn't get unexpectedly freed */
|
||||
dont_free_filtered = true;
|
||||
|
||||
@ -747,17 +769,24 @@ cleanup:
|
||||
}
|
||||
|
||||
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;
|
||||
int error;
|
||||
|
||||
if ((error = git_futils_mkpath2file(path, dir_mode)) < 0)
|
||||
return error;
|
||||
|
||||
if ((error = git_blob__getbuf(&linktarget, blob)) < 0)
|
||||
return error;
|
||||
|
||||
if (can_symlink) {
|
||||
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 {
|
||||
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);
|
||||
}
|
||||
|
||||
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(
|
||||
checkout_data *data,
|
||||
const git_diff_file *file)
|
||||
@ -804,12 +858,21 @@ static int checkout_submodule(
|
||||
return 0;
|
||||
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
/* TODO: Support checkout_strategy options. Two circumstances:
|
||||
* 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?
|
||||
*/
|
||||
|
||||
/* update the index unless prevented */
|
||||
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;
|
||||
return checkout_submodule_update_index(data, file);
|
||||
}
|
||||
|
||||
static void report_progress(
|
||||
@ -897,7 +941,7 @@ static int checkout_blob(
|
||||
|
||||
if (S_ISLNK(file->mode))
|
||||
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
|
||||
error = blob_content_to_file(
|
||||
&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 |
|
||||
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_vector_foreach(&data->diff->deltas, i, delta) {
|
||||
@ -983,7 +1030,7 @@ static int checkout_deferred_remove(git_repository *repo, const char *path)
|
||||
{
|
||||
#if 0
|
||||
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) {
|
||||
error = 0;
|
||||
@ -1107,7 +1154,6 @@ static int checkout_data_init(
|
||||
git_checkout_opts *proposed)
|
||||
{
|
||||
int error = 0;
|
||||
git_config *cfg;
|
||||
git_repository *repo = git_iterator_owner(target);
|
||||
|
||||
memset(data, 0, sizeof(*data));
|
||||
@ -1117,10 +1163,8 @@ static int checkout_data_init(
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((error = git_repository__ensure_not_bare(repo, "checkout")) < 0)
|
||||
return error;
|
||||
|
||||
if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
|
||||
if ((!proposed || !proposed->target_directory) &&
|
||||
(error = git_repository__ensure_not_bare(repo, "checkout")) < 0)
|
||||
return error;
|
||||
|
||||
data->repo = repo;
|
||||
@ -1133,9 +1177,19 @@ static int checkout_data_init(
|
||||
else
|
||||
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 */
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
|
||||
error = git_config_get_bool(&data->can_symlink, cfg, "core.symlinks");
|
||||
if (error < 0) {
|
||||
if (error != GIT_ENOTFOUND)
|
||||
goto cleanup;
|
||||
|
||||
/* If "core.symlinks" is not found anywhere, default to true. */
|
||||
data->can_symlink = true;
|
||||
giterr_clear();
|
||||
error = 0;
|
||||
}
|
||||
if ((error = git_repository__cvar(
|
||||
&data->can_symlink, repo, GIT_CVAR_SYMLINKS)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (!data->opts.baseline) {
|
||||
data->opts_free_baseline = true;
|
||||
|
||||
error = checkout_lookup_head_tree(&data->opts.baseline, repo);
|
||||
|
||||
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 ||
|
||||
(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;
|
||||
|
||||
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;
|
||||
|
||||
if ((error = git_iterator_reset(target, data.pfx, data.pfx)) < 0 ||
|
||||
(error = git_iterator_for_workdir(
|
||||
&workdir, data.repo, iterflags | GIT_ITERATOR_DONT_AUTOEXPAND,
|
||||
(error = git_iterator_for_workdir_ext(
|
||||
&workdir, data.repo, data.opts.target_directory,
|
||||
iterflags | GIT_ITERATOR_DONT_AUTOEXPAND,
|
||||
data.pfx, data.pfx)) < 0 ||
|
||||
(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;
|
||||
|
||||
/* Should not have case insensitivity mismatch */
|
||||
@ -1318,8 +1369,19 @@ int git_checkout_index(
|
||||
int error;
|
||||
git_iterator *index_i;
|
||||
|
||||
if ((error = git_repository__ensure_not_bare(repo, "checkout index")) < 0)
|
||||
return error;
|
||||
if (!index && !repo) {
|
||||
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)
|
||||
return error;
|
||||
@ -1343,8 +1405,19 @@ int git_checkout_tree(
|
||||
git_tree *tree = NULL;
|
||||
git_iterator *tree_i = NULL;
|
||||
|
||||
if ((error = git_repository__ensure_not_bare(repo, "checkout tree")) < 0)
|
||||
return error;
|
||||
if (!treeish && !repo) {
|
||||
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) {
|
||||
giterr_set(
|
||||
@ -1369,8 +1442,7 @@ int git_checkout_head(
|
||||
git_tree *head = NULL;
|
||||
git_iterator *head_i = NULL;
|
||||
|
||||
if ((error = git_repository__ensure_not_bare(repo, "checkout head")) < 0)
|
||||
return error;
|
||||
assert(repo);
|
||||
|
||||
if (!(error = checkout_lookup_head_tree(&head, repo)) &&
|
||||
!(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 "refs.h"
|
||||
#include "path.h"
|
||||
#include "repository.h"
|
||||
|
||||
static int create_branch(
|
||||
git_reference **branch,
|
||||
@ -132,14 +133,14 @@ static int reference_matches_remote_head(
|
||||
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 */
|
||||
if (git_refspec_transform_l(
|
||||
&head_info->branchname,
|
||||
&head_info->branchname,
|
||||
head_info->refspec,
|
||||
reference_name) < 0)
|
||||
return -1;
|
||||
|
||||
|
||||
if (git_buf_len(&head_info->branchname) > 0) {
|
||||
if (git_buf_sets(
|
||||
&head_info->branchname,
|
||||
@ -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)
|
||||
{
|
||||
int retcode = -1;
|
||||
git_refspec dummy_spec;
|
||||
git_remote_head *remote_head;
|
||||
struct head_info head_info;
|
||||
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_buf_init(&head_info.branchname, 16);
|
||||
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;
|
||||
|
||||
if (head_info.refspec == NULL) {
|
||||
memset(&dummy_spec, 0, sizeof(git_refspec));
|
||||
head_info.refspec = &dummy_spec;
|
||||
}
|
||||
|
||||
/* Determine the remote tracking reference name from the local master */
|
||||
if (git_refspec_transform_r(
|
||||
@ -235,9 +242,8 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote)
|
||||
}
|
||||
|
||||
/* Not master. Check all the other refs. */
|
||||
if (git_reference_foreach(
|
||||
if (git_reference_foreach_name(
|
||||
repo,
|
||||
GIT_REF_LISTALL,
|
||||
reference_matches_remote_head,
|
||||
&head_info) < 0)
|
||||
goto cleanup;
|
||||
@ -269,7 +275,7 @@ static int update_head_to_branch(
|
||||
int retcode;
|
||||
git_buf remote_branch_name = GIT_BUF_INIT;
|
||||
git_reference* remote_ref = NULL;
|
||||
|
||||
|
||||
assert(options->checkout_branch);
|
||||
|
||||
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)
|
||||
goto on_error;
|
||||
|
||||
if (options->fetch_spec &&
|
||||
(error = git_remote_set_fetchspec(origin, options->fetch_spec)) < 0)
|
||||
goto on_error;
|
||||
if (options->fetch_spec) {
|
||||
git_remote_clear_refspecs(origin);
|
||||
if ((error = git_remote_add_fetch(origin, options->fetch_spec)) < 0)
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (options->pushurl &&
|
||||
(error = git_remote_set_pushurl(origin, options->pushurl)) < 0)
|
||||
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)
|
||||
goto on_error;
|
||||
|
||||
@ -347,50 +359,48 @@ static int setup_remotes_and_fetch(
|
||||
const git_clone_options *options)
|
||||
{
|
||||
int retcode = GIT_ERROR;
|
||||
git_remote *origin;
|
||||
git_remote *origin = NULL;
|
||||
|
||||
/* Construct an origin remote */
|
||||
if (!create_and_configure_origin(&origin, repo, url, options)) {
|
||||
git_remote_set_update_fetchhead(origin, 0);
|
||||
if ((retcode = create_and_configure_origin(&origin, repo, url, options)) < 0)
|
||||
goto on_error;
|
||||
|
||||
/* Connect and download everything */
|
||||
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);
|
||||
}
|
||||
git_remote_set_update_fetchhead(origin, 0);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
||||
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(
|
||||
git_repository *repo,
|
||||
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 */
|
||||
if (!dst->remote_name) dst->remote_name = "origin";
|
||||
if (!dst->remote_autotag) dst->remote_autotag = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
|
||||
}
|
||||
|
||||
int git_clone(
|
||||
@ -436,7 +445,10 @@ int git_clone(
|
||||
normalize_options(&normOptions, 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;
|
||||
}
|
||||
|
||||
|
||||
114
src/commit.c
114
src/commit.c
@ -9,6 +9,7 @@
|
||||
#include "git2/object.h"
|
||||
#include "git2/repository.h"
|
||||
#include "git2/signature.h"
|
||||
#include "git2/sys/commit.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "odb.h"
|
||||
@ -30,8 +31,10 @@ static void clear_parents(git_commit *commit)
|
||||
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);
|
||||
git_vector_free(&commit->parent_ids);
|
||||
|
||||
@ -44,16 +47,16 @@ void git_commit__free(git_commit *commit)
|
||||
}
|
||||
|
||||
int git_commit_create_v(
|
||||
git_oid *oid,
|
||||
git_repository *repo,
|
||||
const char *update_ref,
|
||||
const git_signature *author,
|
||||
const git_signature *committer,
|
||||
const char *message_encoding,
|
||||
const char *message,
|
||||
const git_tree *tree,
|
||||
int parent_count,
|
||||
...)
|
||||
git_oid *oid,
|
||||
git_repository *repo,
|
||||
const char *update_ref,
|
||||
const git_signature *author,
|
||||
const git_signature *committer,
|
||||
const char *message_encoding,
|
||||
const char *message,
|
||||
const git_tree *tree,
|
||||
int parent_count,
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, res;
|
||||
@ -76,30 +79,28 @@ int git_commit_create_v(
|
||||
return res;
|
||||
}
|
||||
|
||||
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[])
|
||||
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_buf commit = GIT_BUF_INIT;
|
||||
int i;
|
||||
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) {
|
||||
assert(git_object_owner((const git_object *)parents[i]) == repo);
|
||||
git_oid__writebuf(&commit, "parent ", git_object_id((const git_object *)parents[i]));
|
||||
}
|
||||
for (i = 0; i < parent_count; ++i)
|
||||
git_oid__writebuf(&commit, "parent ", parents[i]);
|
||||
|
||||
git_signature__writebuf(&commit, "author ", author);
|
||||
git_signature__writebuf(&commit, "committer ", committer);
|
||||
@ -131,10 +132,47 @@ on_error:
|
||||
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;
|
||||
const char *buffer_end = (const char *)data + len;
|
||||
int retval, i;
|
||||
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;
|
||||
|
||||
if (git_vector_init(&commit->parent_ids, 4, NULL) < 0)
|
||||
@ -206,12 +244,6 @@ bad_buffer:
|
||||
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) \
|
||||
_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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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;
|
||||
assert(commit);
|
||||
@ -260,7 +294,7 @@ int git_commit_nth_gen_ancestor(
|
||||
const git_commit *commit,
|
||||
unsigned int n)
|
||||
{
|
||||
git_commit *current, *parent;
|
||||
git_commit *current, *parent = NULL;
|
||||
int error;
|
||||
|
||||
assert(ancestor && commit);
|
||||
|
||||
@ -27,8 +27,7 @@ struct git_commit {
|
||||
char *message;
|
||||
};
|
||||
|
||||
void git_commit__free(git_commit *c);
|
||||
int git_commit__parse(git_commit *commit, git_odb_object *obj);
|
||||
void git_commit__free(void *commit);
|
||||
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
|
||||
|
||||
@ -100,12 +100,15 @@ git_commit_list_node *git_commit_list_pop(git_commit_list **stack)
|
||||
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;
|
||||
unsigned char *buffer = raw->data;
|
||||
unsigned char *buffer_end = buffer + raw->len;
|
||||
unsigned char *parents_start, *committer_start;
|
||||
const uint8_t *buffer_end = buffer + buffer_len;
|
||||
const uint8_t *parents_start, *committer_start;
|
||||
int i, parents = 0;
|
||||
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) {
|
||||
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;
|
||||
|
||||
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)
|
||||
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");
|
||||
error = -1;
|
||||
} 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);
|
||||
return error;
|
||||
|
||||
155
src/config.c
155
src/config.c
@ -9,6 +9,7 @@
|
||||
#include "fileops.h"
|
||||
#include "config.h"
|
||||
#include "git2/config.h"
|
||||
#include "git2/sys/config.h"
|
||||
#include "vector.h"
|
||||
#include "buf_text.h"
|
||||
#include "config_file.h"
|
||||
@ -22,7 +23,7 @@ typedef struct {
|
||||
git_refcount rc;
|
||||
|
||||
git_config_backend *file;
|
||||
unsigned int level;
|
||||
git_config_level_t level;
|
||||
} file_internal;
|
||||
|
||||
static void file_internal_free(file_internal *internal)
|
||||
@ -39,12 +40,14 @@ static void config_free(git_config *cfg)
|
||||
size_t i;
|
||||
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);
|
||||
GIT_REFCOUNT_DEC(internal, file_internal_free);
|
||||
}
|
||||
|
||||
git_vector_free(&cfg->files);
|
||||
|
||||
git__memzero(cfg, sizeof(*cfg));
|
||||
git__free(cfg);
|
||||
}
|
||||
|
||||
@ -86,17 +89,19 @@ int git_config_new(git_config **out)
|
||||
int git_config_add_file_ondisk(
|
||||
git_config *cfg,
|
||||
const char *path,
|
||||
unsigned int level,
|
||||
git_config_level_t level,
|
||||
int force)
|
||||
{
|
||||
git_config_backend *file = NULL;
|
||||
struct stat st;
|
||||
int res;
|
||||
|
||||
assert(cfg && path);
|
||||
|
||||
if (!git_path_isfile(path)) {
|
||||
giterr_set(GITERR_CONFIG, "Cannot find config file '%s'", path);
|
||||
return GIT_ENOTFOUND;
|
||||
res = p_stat(path, &st);
|
||||
if (res < 0 && errno != ENOENT) {
|
||||
giterr_set(GITERR_CONFIG, "Error stat'ing config file '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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(
|
||||
file_internal **internal_out,
|
||||
const git_config *cfg,
|
||||
int level)
|
||||
git_config_level_t level)
|
||||
{
|
||||
int pos = -1;
|
||||
file_internal *internal;
|
||||
unsigned int i;
|
||||
size_t i;
|
||||
|
||||
/* 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
|
||||
@ -150,14 +155,14 @@ static int find_internal_file_by_level(
|
||||
pos = 0;
|
||||
} else {
|
||||
git_vector_foreach(&cfg->files, i, internal) {
|
||||
if (internal->level == (unsigned int)level)
|
||||
pos = i;
|
||||
if (internal->level == level)
|
||||
pos = (int)i;
|
||||
}
|
||||
}
|
||||
|
||||
if (pos == -1) {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -172,21 +177,21 @@ static int duplicate_level(void **old_raw, void *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;
|
||||
}
|
||||
|
||||
static void try_remove_existing_file_internal(
|
||||
git_config *cfg,
|
||||
unsigned int level)
|
||||
git_config_level_t level)
|
||||
{
|
||||
int pos = -1;
|
||||
file_internal *internal;
|
||||
unsigned int i;
|
||||
size_t i;
|
||||
|
||||
git_vector_foreach(&cfg->files, i, internal) {
|
||||
if (internal->level == level)
|
||||
pos = i;
|
||||
pos = (int)i;
|
||||
}
|
||||
|
||||
if (pos == -1)
|
||||
@ -203,7 +208,7 @@ static void try_remove_existing_file_internal(
|
||||
static int git_config__add_internal(
|
||||
git_config *cfg,
|
||||
file_internal *internal,
|
||||
unsigned int level,
|
||||
git_config_level_t level,
|
||||
int force)
|
||||
{
|
||||
int result;
|
||||
@ -224,10 +229,18 @@ static int git_config__add_internal(
|
||||
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(
|
||||
git_config **cfg_out,
|
||||
const git_config *cfg_parent,
|
||||
unsigned int level)
|
||||
git_config **cfg_out,
|
||||
const git_config *cfg_parent,
|
||||
git_config_level_t level)
|
||||
{
|
||||
git_config *cfg;
|
||||
file_internal *internal;
|
||||
@ -252,7 +265,7 @@ int git_config_open_level(
|
||||
int git_config_add_backend(
|
||||
git_config *cfg,
|
||||
git_config_backend *file,
|
||||
unsigned int level,
|
||||
git_config_level_t level,
|
||||
int force)
|
||||
{
|
||||
file_internal *internal;
|
||||
@ -292,6 +305,9 @@ int git_config_refresh(git_config *cfg)
|
||||
error = file->refresh(file);
|
||||
}
|
||||
|
||||
if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL)
|
||||
git_repository__cvar_cache_clear(GIT_REFCOUNT_OWNER(cfg));
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -325,21 +341,30 @@ int git_config_foreach_match(
|
||||
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)
|
||||
{
|
||||
git_config_backend *file;
|
||||
file_internal *internal;
|
||||
|
||||
internal = git_vector_get(&cfg->files, 0);
|
||||
if (!internal || !internal->file)
|
||||
return config_error_nofiles(name);
|
||||
file = internal->file;
|
||||
|
||||
return file->del(file, name);
|
||||
}
|
||||
|
||||
/**************
|
||||
* Setters
|
||||
**************/
|
||||
|
||||
int git_config_set_int64(git_config *cfg, const char *name, int64_t value)
|
||||
{
|
||||
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 error;
|
||||
git_config_backend *file;
|
||||
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);
|
||||
if (!internal || !internal->file)
|
||||
return config_error_nofiles(name);
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
file_internal *internal;
|
||||
unsigned int i;
|
||||
int res;
|
||||
|
||||
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)
|
||||
return res;
|
||||
}
|
||||
|
||||
return GIT_ENOTFOUND;
|
||||
return config_error_notfound(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;
|
||||
unsigned int i;
|
||||
git_config_backend *file;
|
||||
int ret;
|
||||
|
||||
*out = NULL;
|
||||
|
||||
git_vector_foreach(&cfg->files, i, internal) {
|
||||
git_config_backend *file = internal->file;
|
||||
int ret = file->get(file, name, out);
|
||||
if (!internal || !internal->file)
|
||||
continue;
|
||||
file = internal->file;
|
||||
|
||||
ret = file->get(file, name, out);
|
||||
if (ret != GIT_ENOTFOUND)
|
||||
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,
|
||||
git_config_foreach_cb cb, void *payload)
|
||||
int git_config_get_multivar(
|
||||
const git_config *cfg, const char *name, const char *regexp,
|
||||
git_config_foreach_cb cb, void *payload)
|
||||
{
|
||||
file_internal *internal;
|
||||
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) {
|
||||
internal = git_vector_get(&cfg->files, i - 1);
|
||||
if (!internal || !internal->file)
|
||||
continue;
|
||||
file = internal->file;
|
||||
|
||||
ret = file->get_multivar(file, name, regexp, cb, payload);
|
||||
if (ret < 0 && ret != GIT_ENOTFOUND)
|
||||
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)
|
||||
@ -510,6 +561,8 @@ int git_config_set_multivar(git_config *cfg, const char *name, const char *regex
|
||||
file_internal *internal;
|
||||
|
||||
internal = git_vector_get(&cfg->files, 0);
|
||||
if (!internal || !internal->file)
|
||||
return config_error_nofiles(name);
|
||||
file = internal->file;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 error;
|
||||
git_config *cfg = NULL;
|
||||
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,
|
||||
GIT_CONFIG_LEVEL_GLOBAL, 0);
|
||||
}
|
||||
|
||||
if (!error && !git_config_find_xdg_r(&buf))
|
||||
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);
|
||||
|
||||
if (error && cfg) {
|
||||
if (error) {
|
||||
git_config_free(cfg);
|
||||
cfg = NULL;
|
||||
}
|
||||
@ -605,6 +687,7 @@ int git_config_open_default(git_config **out)
|
||||
/***********
|
||||
* Parsers
|
||||
***********/
|
||||
|
||||
int git_config_lookup_map_value(
|
||||
int *out,
|
||||
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_system_r(git_buf *system_config_path);
|
||||
|
||||
|
||||
extern int git_config__global_location(git_buf *buf);
|
||||
|
||||
extern int git_config_rename_section(
|
||||
git_repository *repo,
|
||||
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
|
||||
* and native, which uses the platform's native line ending. The default
|
||||
* 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[] = {
|
||||
{GIT_CVAR_FALSE, NULL, GIT_EOL_UNSET},
|
||||
@ -37,7 +37,7 @@ static git_cvar_map _cvar_map_eol[] = {
|
||||
|
||||
/*
|
||||
* 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
|
||||
* not guaranteed to be normalized: files that contain CRLF in the
|
||||
* 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}
|
||||
};
|
||||
|
||||
/*
|
||||
* Generic map for integer values
|
||||
*/
|
||||
static git_cvar_map _cvar_map_int[] = {
|
||||
{GIT_CVAR_INT32, NULL, 0},
|
||||
};
|
||||
|
||||
static struct map_data _cvar_maps[] = {
|
||||
{"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)
|
||||
@ -69,12 +82,16 @@ int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
error = git_config_get_mapped(out,
|
||||
config, data->cvar_name, data->maps, data->map_count);
|
||||
if (data->maps)
|
||||
error = git_config_get_mapped(
|
||||
out, config, data->cvar_name, data->maps, data->map_count);
|
||||
else
|
||||
error = git_config_get_bool(out, config, data->cvar_name);
|
||||
|
||||
if (error == GIT_ENOTFOUND)
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
giterr_clear();
|
||||
*out = data->default_value;
|
||||
|
||||
}
|
||||
else if (error < 0)
|
||||
return error;
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include "buffer.h"
|
||||
#include "buf_text.h"
|
||||
#include "git2/config.h"
|
||||
#include "git2/sys/config.h"
|
||||
#include "git2/types.h"
|
||||
#include "strmap.h"
|
||||
|
||||
@ -80,10 +81,10 @@ typedef struct {
|
||||
time_t file_mtime;
|
||||
size_t file_size;
|
||||
|
||||
unsigned int level;
|
||||
git_config_level_t level;
|
||||
} 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 config_write(diskfile_backend *cfg, const char *key, const regex_t *preg, const char *value);
|
||||
static char *escape_value(const char *ptr);
|
||||
@ -180,7 +181,7 @@ static void free_vars(git_strmap *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;
|
||||
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)
|
||||
{
|
||||
cvar_t *var = NULL, *old_var;
|
||||
cvar_t *var = NULL, *old_var = NULL;
|
||||
diskfile_backend *b = (diskfile_backend *)cfg;
|
||||
char *key, *esc_value = NULL;
|
||||
khiter_t pos;
|
||||
@ -481,8 +482,10 @@ static int config_set_multivar(
|
||||
|
||||
pos = git_strmap_lookup_index(b->values, key);
|
||||
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);
|
||||
return GIT_ENOTFOUND;
|
||||
return result;
|
||||
}
|
||||
|
||||
var = git_strmap_value_at(b->values, pos);
|
||||
@ -962,7 +965,7 @@ static int strip_comments(char *line, int in_quotes)
|
||||
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;
|
||||
char *current_section = NULL;
|
||||
|
||||
@ -5,14 +5,16 @@
|
||||
* 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 "fileops.h"
|
||||
#include "hash.h"
|
||||
#include "filter.h"
|
||||
#include "buf_text.h"
|
||||
#include "repository.h"
|
||||
#include "git2/attr.h"
|
||||
#include "git2/blob.h"
|
||||
|
||||
struct crlf_attrs {
|
||||
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,
|
||||
const struct timeval *tv,
|
||||
int *error_ret)
|
||||
const struct timeval *tv,
|
||||
int *error_ret)
|
||||
{
|
||||
int number = 0;
|
||||
int touched = 0;
|
||||
@ -866,7 +866,7 @@ int git__date_parse(git_time_t *out, const char *date)
|
||||
int offset, error_ret=0;
|
||||
|
||||
if (!parse_date_basic(date, ×tamp, &offset)) {
|
||||
*out = timestamp;
|
||||
*out = timestamp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@ -168,7 +168,7 @@ git_delta_create_index(const void *buf, unsigned long bufsize)
|
||||
memset(hash, 0, hsize * sizeof(*hash));
|
||||
|
||||
/* 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) {
|
||||
git__free(index);
|
||||
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_CTIME = (1 << 3), /* use st_ctime? */
|
||||
GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
|
||||
GIT_DIFFCAPS_TRUST_NANOSECS = (1 << 5), /* use stat time nanoseconds */
|
||||
};
|
||||
|
||||
#define DIFF_FLAGS_KNOWN_BINARY (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)
|
||||
#define DIFF_FLAGS_NOT_BINARY (GIT_DIFF_FLAG_NOT_BINARY|GIT_DIFF_FLAG__NO_DATA)
|
||||
|
||||
enum {
|
||||
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__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__TO_DELETE = (1 << 11), /* delete entry during rename det. */
|
||||
GIT_DIFF_FLAG__TO_SPLIT = (1 << 12), /* split entry during rename det. */
|
||||
GIT_DIFF_FLAG__FREE_BLOB = (1 << 11), /* release the blob when done */
|
||||
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 {
|
||||
git_refcount rc;
|
||||
git_repository *repo;
|
||||
@ -60,6 +74,7 @@ extern void git_diff__cleanup_modes(
|
||||
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__casecmp(const void *a, const void *b);
|
||||
|
||||
extern bool git_diff_delta__should_skip(
|
||||
const git_diff_options *opts, const git_diff_delta *delta);
|
||||
@ -74,5 +89,22 @@ extern int git_diff__from_iterators(
|
||||
git_iterator *new_iter,
|
||||
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
|
||||
|
||||
|
||||
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.
|
||||
*/
|
||||
#include "common.h"
|
||||
#include "diff.h"
|
||||
|
||||
#include "git2/config.h"
|
||||
#include "git2/blob.h"
|
||||
|
||||
#include "diff.h"
|
||||
#include "hashsig.h"
|
||||
#include "path.h"
|
||||
#include "fileops.h"
|
||||
|
||||
static git_diff_delta *diff_delta__dup(
|
||||
const git_diff_delta *d, git_pool *pool)
|
||||
@ -18,12 +22,15 @@ static git_diff_delta *diff_delta__dup(
|
||||
return NULL;
|
||||
|
||||
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 (delta->old_file.path == NULL)
|
||||
goto fail;
|
||||
if (d->old_file.path != NULL) {
|
||||
delta->old_file.path = git_pool_strdup(pool, d->old_file.path);
|
||||
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);
|
||||
if (delta->new_file.path == NULL)
|
||||
goto fail;
|
||||
@ -170,7 +177,7 @@ int git_diff_merge(
|
||||
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)
|
||||
{
|
||||
git_hashsig_option_t opt = (git_hashsig_option_t)p;
|
||||
@ -178,7 +185,7 @@ static int find_similar__hashsig_for_file(
|
||||
|
||||
GIT_UNUSED(f);
|
||||
error = git_hashsig_create_fromfile((git_hashsig **)out, path, opt);
|
||||
|
||||
|
||||
if (error == GIT_EBUFS) {
|
||||
error = 0;
|
||||
giterr_clear();
|
||||
@ -187,15 +194,15 @@ static int find_similar__hashsig_for_file(
|
||||
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)
|
||||
{
|
||||
git_hashsig_option_t opt = (git_hashsig_option_t)p;
|
||||
int error = 0;
|
||||
|
||||
|
||||
GIT_UNUSED(f);
|
||||
error = git_hashsig_create((git_hashsig **)out, buf, len, opt);
|
||||
|
||||
|
||||
if (error == GIT_EBUFS) {
|
||||
error = 0;
|
||||
giterr_clear();
|
||||
@ -204,13 +211,13 @@ static int find_similar__hashsig_for_buf(
|
||||
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_hashsig_free(sig);
|
||||
}
|
||||
|
||||
static int find_similar__calc_similarity(
|
||||
int git_diff_find_similar__calc_similarity(
|
||||
int *score, void *siga, void *sigb, void *payload)
|
||||
{
|
||||
GIT_UNUSED(payload);
|
||||
@ -220,7 +227,7 @@ static int find_similar__calc_similarity(
|
||||
|
||||
#define DEFAULT_THRESHOLD 50
|
||||
#define DEFAULT_BREAK_REWRITE_THRESHOLD 60
|
||||
#define DEFAULT_TARGET_LIMIT 200
|
||||
#define DEFAULT_RENAME_LIMIT 200
|
||||
|
||||
static int normalize_find_opts(
|
||||
git_diff_list *diff,
|
||||
@ -253,12 +260,25 @@ static int normalize_find_opts(
|
||||
|
||||
/* 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)
|
||||
opts->flags |= GIT_DIFF_FIND_RENAMES;
|
||||
|
||||
if (opts->flags & GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED)
|
||||
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)
|
||||
|
||||
if (USE_DEFAULT(opts->rename_threshold))
|
||||
@ -275,15 +295,15 @@ static int normalize_find_opts(
|
||||
|
||||
#undef USE_DEFAULT
|
||||
|
||||
if (!opts->target_limit) {
|
||||
if (!opts->rename_limit) {
|
||||
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)
|
||||
giterr_clear();
|
||||
else if (limit > 0)
|
||||
opts->target_limit = limit;
|
||||
opts->rename_limit = limit;
|
||||
}
|
||||
|
||||
/* 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));
|
||||
GITERR_CHECK_ALLOC(opts->metric);
|
||||
|
||||
opts->metric->file_signature = find_similar__hashsig_for_file;
|
||||
opts->metric->buffer_signature = find_similar__hashsig_for_buf;
|
||||
opts->metric->free_signature = find_similar__hashsig_free;
|
||||
opts->metric->similarity = find_similar__calc_similarity;
|
||||
opts->metric->file_signature = git_diff_find_similar__hashsig_for_file;
|
||||
opts->metric->buffer_signature = git_diff_find_similar__hashsig_for_buf;
|
||||
opts->metric->free_signature = git_diff_find_similar__hashsig_free;
|
||||
opts->metric->similarity = git_diff_find_similar__calc_similarity;
|
||||
|
||||
if (opts->flags & GIT_DIFF_FIND_IGNORE_WHITESPACE)
|
||||
opts->metric->payload = (void *)GIT_HASHSIG_IGNORE_WHITESPACE;
|
||||
@ -307,11 +327,12 @@ static int normalize_find_opts(
|
||||
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;
|
||||
size_t i;
|
||||
git_diff_delta *delta;
|
||||
git_diff_delta *delta, *deleted;
|
||||
|
||||
if (git_vector_init(&onto, expected_size, git_diff_delta__cmp) < 0)
|
||||
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)
|
||||
continue;
|
||||
|
||||
if ((delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) {
|
||||
git_diff_delta *deleted = diff_delta__dup(delta, &diff->pool);
|
||||
if (!deleted)
|
||||
if ((delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0 && actually_split) {
|
||||
delta->similarity = 0;
|
||||
|
||||
/* make new record for DELETED side of split */
|
||||
if (!(deleted = diff_delta__dup(delta, &diff->pool)))
|
||||
goto on_error;
|
||||
|
||||
deleted->status = GIT_DELTA_DELETED;
|
||||
@ -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)
|
||||
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));
|
||||
delta->old_file.path = delta->new_file.path;
|
||||
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)
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
git__free(delta);
|
||||
}
|
||||
|
||||
/* swap new delta list into place */
|
||||
git_vector_sort(&onto);
|
||||
git_vector_swap(&diff->deltas, &onto);
|
||||
git_vector_free(&onto);
|
||||
git_vector_sort(&diff->deltas);
|
||||
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
git_vector_foreach(&onto, i, delta)
|
||||
git__free(delta);
|
||||
|
||||
git_vector_free(&onto);
|
||||
|
||||
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(
|
||||
git_diff_list *diff,
|
||||
git_diff_find_options *opts,
|
||||
const git_diff_find_options *opts,
|
||||
size_t file_idx,
|
||||
void **cache)
|
||||
{
|
||||
int error = 0;
|
||||
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 */
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
|
||||
/* TODO: apply wd-to-odb filters to file data if necessary */
|
||||
|
||||
if (!(error = git_buf_joinpath(
|
||||
&path, git_repository_workdir(diff->repo), file->path)))
|
||||
if ((error = git_buf_joinpath(
|
||||
&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(
|
||||
&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? */
|
||||
|
||||
if ((error = git_blob_lookup(&blob, diff->repo, &file->oid)) < 0)
|
||||
return error;
|
||||
if (git_blob_lookup(&blob, diff->repo, &file->oid) < 0) {
|
||||
/* if lookup fails, just skip this item in similarity calc */
|
||||
giterr_clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
blobsize = git_blob_rawsize(blob);
|
||||
if (!git__is_sizet(blobsize)) /* ? what to do ? */
|
||||
@ -415,268 +459,485 @@ static int similarity_calc(
|
||||
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(
|
||||
int *score,
|
||||
git_diff_list *diff,
|
||||
git_diff_find_options *opts,
|
||||
const git_diff_find_options *opts,
|
||||
void **cache,
|
||||
size_t a_idx,
|
||||
size_t b_idx)
|
||||
{
|
||||
int score = 0;
|
||||
git_diff_file *a_file = similarity_get_file(diff, a_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))
|
||||
return 0;
|
||||
|
||||
if (git_oid_cmp(&a_file->oid, &b_file->oid) == 0)
|
||||
return 100;
|
||||
/* if exact match is requested, force calculation of missing OIDs now */
|
||||
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 */
|
||||
if (!cache[a_idx] && similarity_calc(diff, opts, a_idx, cache) < 0)
|
||||
return -1;
|
||||
if (!cache[b_idx] && similarity_calc(diff, opts, b_idx, cache) < 0)
|
||||
return -1;
|
||||
|
||||
|
||||
/* some metrics may not wish to process this file (too big / too small) */
|
||||
if (!cache[a_idx] || !cache[b_idx])
|
||||
return 0;
|
||||
|
||||
/* compare signatures */
|
||||
if (opts->metric->similarity(
|
||||
&score, cache[a_idx], cache[b_idx], opts->metric->payload) < 0)
|
||||
return -1;
|
||||
|
||||
/* clip score */
|
||||
if (score < 0)
|
||||
score = 0;
|
||||
else if (score > 100)
|
||||
score = 100;
|
||||
|
||||
return score;
|
||||
return opts->metric->similarity(
|
||||
score, cache[a_idx], cache[b_idx], opts->metric->payload);
|
||||
}
|
||||
|
||||
#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(
|
||||
git_diff_list *diff,
|
||||
git_diff_find_options *given_opts)
|
||||
{
|
||||
size_t i, j, cache_size, *matches;
|
||||
size_t i, j, sigcache_size;
|
||||
int error = 0, similarity;
|
||||
git_diff_delta *from, *to;
|
||||
git_diff_find_options opts;
|
||||
size_t tried_targets, num_rewrites = 0;
|
||||
void **cache;
|
||||
size_t num_srcs = 0, num_tgts = 0, tried_srcs = 0, tried_tgts = 0;
|
||||
size_t num_rewrites = 0, num_updates = 0, num_bumped = 0;
|
||||
void **sigcache; /* cache of similarity metric file signatures */
|
||||
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)
|
||||
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 */
|
||||
cache = git__calloc(cache_size, sizeof(void *));
|
||||
GITERR_CHECK_ALLOC(cache);
|
||||
sigcache_size = diff->deltas.length * 2; /* keep size b/c diff may change */
|
||||
sigcache = git__calloc(sigcache_size, sizeof(void *));
|
||||
GITERR_CHECK_ALLOC(sigcache);
|
||||
|
||||
matches = git__calloc(diff->deltas.length, sizeof(size_t));
|
||||
GITERR_CHECK_ALLOC(matches);
|
||||
/* Label rename sources and targets
|
||||
*
|
||||
* 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 (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++;
|
||||
}
|
||||
}
|
||||
if (is_rename_target(diff, &opts, i, sigcache))
|
||||
++num_tgts;
|
||||
}
|
||||
|
||||
/* 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) {
|
||||
tried_targets = 0;
|
||||
match_tgts = git__calloc(diff->deltas.length, sizeof(diff_find_match));
|
||||
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) !=
|
||||
GIT_MODE_TYPE(GIT_FILEMODE_BLOB))
|
||||
/*
|
||||
* Find best-fit matches for rename / copy candidates
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
/* don't check UNMODIFIED files as source unless given option */
|
||||
if (from->status == GIT_DELTA_UNMODIFIED &&
|
||||
!FLAG_SET(opts, GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED))
|
||||
continue;
|
||||
tried_srcs = 0;
|
||||
|
||||
/* skip all but DELETED files unless copy detection is on */
|
||||
if (!FLAG_SET(opts, GIT_DIFF_FIND_COPIES) &&
|
||||
from->status != GIT_DELTA_DELETED &&
|
||||
(from->flags & GIT_DIFF_FLAG__TO_SPLIT) == 0)
|
||||
continue;
|
||||
git_vector_foreach(&diff->deltas, j, from) {
|
||||
/* skip things that are not rename sources */
|
||||
if ((from->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) == 0)
|
||||
continue;
|
||||
|
||||
git_vector_foreach(&diff->deltas, j, to) {
|
||||
/* calculate similarity for this pair and find best match */
|
||||
if (i == j)
|
||||
continue;
|
||||
|
||||
/* skip things that aren't blobs */
|
||||
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;
|
||||
similarity = -1; /* don't measure self-similarity here */
|
||||
else if ((error = similarity_measure(
|
||||
&similarity, diff, &opts, sigcache, 2 * j, 2 * i + 1)) < 0)
|
||||
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) {
|
||||
to->similarity = (unsigned int)similarity;
|
||||
matches[j] = i + 1;
|
||||
}
|
||||
if (++tried_srcs >= num_srcs)
|
||||
break;
|
||||
|
||||
/* 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]) {
|
||||
assert(to->similarity == 0);
|
||||
/*
|
||||
* Rewrite the diffs with renames / copies
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
i = matches[j] - 1;
|
||||
from = GIT_VECTOR_GET(&diff->deltas, i);
|
||||
assert(from);
|
||||
/* check if this delta was the target of a similarity */
|
||||
best_match = &match_tgts[i];
|
||||
if (!best_match->similarity)
|
||||
continue;
|
||||
|
||||
/* four possible outcomes here:
|
||||
* 1. old DELETED and if over rename threshold,
|
||||
* new becomes RENAMED and old goes away
|
||||
* 2. old SPLIT and if over rename threshold,
|
||||
* new becomes RENAMED and old becomes ADDED (clear SPLIT)
|
||||
* 3. old was MODIFIED but FIND_RENAMES_FROM_REWRITES is on and
|
||||
* old is more similar to new than it is to itself, in which
|
||||
* case, new becomes RENAMED and old becomed ADDED
|
||||
* 4. otherwise if over copy threshold, new becomes COPIED
|
||||
j = best_match->idx;
|
||||
from = GIT_VECTOR_GET(&diff->deltas, j);
|
||||
|
||||
/* possible scenarios:
|
||||
* 1. from DELETE to ADD/UNTRACK/IGNORE = RENAME
|
||||
* 2. from DELETE to SPLIT/TYPECHANGE = RENAME + DELETE
|
||||
* 3. from SPLIT/TYPECHANGE to ADD/UNTRACK/IGNORE = ADD + RENAME
|
||||
* 4. from SPLIT/TYPECHANGE to SPLIT/TYPECHANGE = RENAME + SPLIT
|
||||
* 5. from OTHER to ADD/UNTRACK/IGNORE = OTHER + COPY
|
||||
*/
|
||||
|
||||
if (from->status == GIT_DELTA_DELETED) {
|
||||
if (to->similarity < opts.rename_threshold) {
|
||||
to->similarity = 0;
|
||||
continue;
|
||||
|
||||
if (delta_is_new_only(to)) {
|
||||
|
||||
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 &&
|
||||
(from->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0)
|
||||
{
|
||||
if (to->similarity < opts.rename_threshold) {
|
||||
to->similarity = 0;
|
||||
continue;
|
||||
}
|
||||
else if (delta_is_split(from)) {
|
||||
|
||||
to->status = GIT_DELTA_RENAMED;
|
||||
memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
|
||||
if (delta_is_new_only(to)) {
|
||||
|
||||
from->status = GIT_DELTA_ADDED;
|
||||
from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
|
||||
memset(&from->old_file, 0, sizeof(from->old_file));
|
||||
num_rewrites--;
|
||||
if (best_match->similarity < opts.rename_threshold)
|
||||
continue;
|
||||
|
||||
continue;
|
||||
}
|
||||
delta_make_rename(to, from, best_match->similarity);
|
||||
|
||||
if (from->status == GIT_DELTA_MODIFIED &&
|
||||
FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) &&
|
||||
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;
|
||||
from->status = (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ?
|
||||
GIT_DELTA_UNTRACKED : GIT_DELTA_ADDED;
|
||||
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;
|
||||
|
||||
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) {
|
||||
to->similarity = 0;
|
||||
continue;
|
||||
else if (delta_is_new_only(to)) {
|
||||
if (!FLAG_SET(&opts, GIT_DIFF_FIND_COPIES) ||
|
||||
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(
|
||||
diff, diff->deltas.length - num_rewrites);
|
||||
}
|
||||
diff, diff->deltas.length - num_rewrites,
|
||||
FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES));
|
||||
|
||||
cleanup:
|
||||
git__free(matches);
|
||||
git__free(match_srcs);
|
||||
git__free(match_tgts);
|
||||
|
||||
for (i = 0; i < cache_size; ++i) {
|
||||
if (cache[i] != NULL)
|
||||
opts.metric->free_signature(cache[i], opts.metric->payload);
|
||||
for (i = 0; i < sigcache_size; ++i) {
|
||||
if (sigcache[i] != NULL)
|
||||
opts.metric->free_signature(sigcache[i], opts.metric->payload);
|
||||
}
|
||||
git__free(cache);
|
||||
git__free(sigcache);
|
||||
|
||||
if (!given_opts || !given_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 "fetch.h"
|
||||
#include "netops.h"
|
||||
#include "repository.h"
|
||||
#include "refs.h"
|
||||
|
||||
struct filter_payload {
|
||||
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)
|
||||
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;
|
||||
else if (p->remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL &&
|
||||
git_refspec_src_matches(p->tagspec, head->name))
|
||||
else
|
||||
return 0;
|
||||
} else if (git_remote__matching_refspec(p->remote, head->name))
|
||||
match = 1;
|
||||
|
||||
if (!match)
|
||||
@ -68,7 +76,6 @@ static int filter_wants(git_remote *remote)
|
||||
* not interested in any particular branch but just the remote's
|
||||
* HEAD, which will be stored in FETCH_HEAD after the fetch.
|
||||
*/
|
||||
p.spec = git_remote_fetchspec(remote);
|
||||
p.tagspec = &tagspec;
|
||||
p.found_head = 0;
|
||||
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];
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
if (git_futils_mkpath2file(to, dirmode) < 0)
|
||||
@ -253,8 +281,9 @@ int git_futils_mkdir(
|
||||
{
|
||||
int error = -1;
|
||||
git_buf make_path = GIT_BUF_INIT;
|
||||
ssize_t root = 0;
|
||||
char lastch, *tail;
|
||||
ssize_t root = 0, min_root_len;
|
||||
char lastch = '/', *tail;
|
||||
struct stat st;
|
||||
|
||||
/* build path and find "root" where we should start calling mkdir */
|
||||
if (git_path_join_unrooted(&make_path, path, base, &root) < 0)
|
||||
@ -262,7 +291,7 @@ int git_futils_mkdir(
|
||||
|
||||
if (make_path.size == 0) {
|
||||
giterr_set(GITERR_OS, "Attempt to create empty path");
|
||||
goto fail;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* remove trailing slashes on path */
|
||||
@ -279,19 +308,32 @@ int git_futils_mkdir(
|
||||
if ((flags & GIT_MKDIR_SKIP_LAST) != 0)
|
||||
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 ((flags & GIT_MKDIR_PATH) == 0)
|
||||
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 */
|
||||
if (root >= (ssize_t)make_path.size)
|
||||
root = (ssize_t)make_path.size - 1;
|
||||
if (root > (ssize_t)make_path.size)
|
||||
root = (ssize_t)make_path.size; /* i.e. NUL byte of string */
|
||||
if (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 */
|
||||
while (*tail == '/')
|
||||
tail++;
|
||||
@ -301,68 +343,49 @@ int git_futils_mkdir(
|
||||
/* truncate path at next component */
|
||||
lastch = *tail;
|
||||
*tail = '\0';
|
||||
st.st_mode = 0;
|
||||
|
||||
/* make directory */
|
||||
if (p_mkdir(make_path.ptr, mode) < 0) {
|
||||
int already_exists = 0;
|
||||
int tmp_errno = errno;
|
||||
|
||||
switch (errno) {
|
||||
case EEXIST:
|
||||
if (!lastch && (flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
|
||||
!git_path_isdir(make_path.ptr)) {
|
||||
giterr_set(
|
||||
GITERR_OS, "Existing path is not a directory '%s'",
|
||||
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;
|
||||
/* ignore error if directory already exists */
|
||||
if (p_stat(make_path.ptr, &st) < 0 ||
|
||||
!(S_ISDIR(st.st_mode) || S_ISLNK(st.st_mode))) {
|
||||
errno = tmp_errno;
|
||||
giterr_set(GITERR_OS, "Failed to make directory '%s'", make_path.ptr);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (already_exists && (flags & GIT_MKDIR_EXCL) != 0) {
|
||||
giterr_set(GITERR_OS, "Directory already exists '%s'",
|
||||
make_path.ptr);
|
||||
/* with exclusive create, existing dir is an error */
|
||||
if ((flags & GIT_MKDIR_EXCL) != 0) {
|
||||
giterr_set(GITERR_OS, "Directory already exists '%s'", make_path.ptr);
|
||||
error = GIT_EEXISTS;
|
||||
goto fail;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* chmod if requested */
|
||||
if ((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
|
||||
((flags & GIT_MKDIR_CHMOD) != 0 && lastch == '\0'))
|
||||
{
|
||||
if (p_chmod(make_path.ptr, mode) < 0) {
|
||||
giterr_set(GITERR_OS, "Failed to set permissions on '%s'",
|
||||
make_path.ptr);
|
||||
goto fail;
|
||||
}
|
||||
/* chmod if requested and necessary */
|
||||
if (((flags & GIT_MKDIR_CHMOD_PATH) != 0 ||
|
||||
(lastch == '\0' && (flags & GIT_MKDIR_CHMOD) != 0)) &&
|
||||
st.st_mode != mode &&
|
||||
(error = p_chmod(make_path.ptr, mode)) < 0) {
|
||||
giterr_set(GITERR_OS, "Failed to set permissions on '%s'", make_path.ptr);
|
||||
goto done;
|
||||
}
|
||||
|
||||
*tail = lastch;
|
||||
}
|
||||
|
||||
git_buf_free(&make_path);
|
||||
return 0;
|
||||
error = 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);
|
||||
return error;
|
||||
}
|
||||
@ -444,7 +467,7 @@ static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path)
|
||||
|
||||
if (data->error < 0) {
|
||||
if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 &&
|
||||
(errno == ENOTEMPTY || errno == EEXIST))
|
||||
(errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY))
|
||||
data->error = 0;
|
||||
else
|
||||
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) {
|
||||
giterr_clear();
|
||||
error = 0;
|
||||
} else if (en == ENOTEMPTY || en == EEXIST) {
|
||||
} else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) {
|
||||
giterr_clear();
|
||||
error = GIT_ITEROVER;
|
||||
} else {
|
||||
@ -988,8 +1011,10 @@ int git_futils_filestamp_check(
|
||||
if (stamp == NULL)
|
||||
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;
|
||||
}
|
||||
|
||||
if (stamp->mtime == (git_time_t)st.st_mtime &&
|
||||
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);
|
||||
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
|
||||
*
|
||||
@ -223,6 +226,7 @@ extern git_off_t git_futils_filesize(git_file fd);
|
||||
#define GIT_MODE_PERMS_MASK 0777
|
||||
#define GIT_CANONICAL_PERMS(MODE) (((MODE) & 0100) ? 0755 : 0644)
|
||||
#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.
|
||||
|
||||
12
src/global.c
12
src/global.c
@ -61,7 +61,8 @@ int git_threads_init(void)
|
||||
return 0;
|
||||
|
||||
_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 */
|
||||
if ((error = git_hash_global_init()) >= 0)
|
||||
@ -121,7 +122,8 @@ int git_threads_init(void)
|
||||
if (_tls_init)
|
||||
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);
|
||||
|
||||
/* Initialize any other subsystems that have global state */
|
||||
@ -135,6 +137,12 @@ int git_threads_init(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);
|
||||
_tls_init = 0;
|
||||
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