mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-19 18:39:26 +00:00
Merge branch 'development' into blame_rebased
Conflicts: include/git2.h
This commit is contained in:
commit
549931679a
21
.travis.yml
21
.travis.yml
@ -1,7 +1,6 @@
|
||||
# Travis-CI Build for libgit2
|
||||
# see travis-ci.org for details
|
||||
|
||||
# As CMake is not officially supported we use erlang VMs
|
||||
language: c
|
||||
|
||||
compiler:
|
||||
@ -18,26 +17,18 @@ matrix:
|
||||
- compiler: i586-mingw32msvc-gcc
|
||||
env: OPTIONS="-DBUILD_CLAR=OFF -DWIN32=ON -DMINGW=ON"
|
||||
|
||||
# Make sure CMake is installed
|
||||
install:
|
||||
- sudo apt-get update >/dev/null
|
||||
- sudo apt-get -q install cmake valgrind
|
||||
- sudo apt-get -qq update
|
||||
- sudo apt-get -qq install cmake libssh2-1-dev openssh-client openssh-server
|
||||
|
||||
# Run the Build script
|
||||
# Run the Build script and tests
|
||||
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
|
||||
- cmake --build . --target install
|
||||
- ctest -V .
|
||||
- script/cibuild.sh
|
||||
|
||||
# Run Tests
|
||||
after_success:
|
||||
- valgrind --leak-check=full --show-reachable=yes --suppressions=../libgit2_clar.supp ./libgit2_clar -ionline
|
||||
- sudo apt-get -qq install valgrind
|
||||
- valgrind --leak-check=full --show-reachable=yes --suppressions=./libgit2_clar.supp _build/libgit2_clar -ionline
|
||||
|
||||
# Only watch the development branch
|
||||
branches:
|
||||
|
@ -29,17 +29,16 @@ OPTION( PROFILE "Generate profiling information" OFF )
|
||||
OPTION( ENABLE_TRACE "Enables tracing support" OFF )
|
||||
OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF )
|
||||
|
||||
OPTION( ANDROID "Build for android NDK" 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
|
||||
# the CLR expects by default and how the Windows API is built.
|
||||
# This option is only available when building with MSVC. By default, libgit2
|
||||
# is build using the cdecl calling convention, which is useful if you're
|
||||
# writing C. However, the CLR and Win32 API both expect stdcall.
|
||||
#
|
||||
# If you are writing a C or C++ program and want to link to libgit2, you
|
||||
# have to either:
|
||||
# - Add /Gz to the compiler options of _your_ program / library.
|
||||
# - Turn this off by invoking CMake with the "-DSTDCALL=Off" argument.
|
||||
#
|
||||
OPTION( STDCALL "Build libgit2 with the __stdcall convention" ON )
|
||||
# If you are writing a CLR program and want to link to libgit2, you'll want
|
||||
# to turn this on by invoking CMake with the "-DSTDCALL=ON" argument.
|
||||
OPTION( STDCALL "Build libgit2 with the __stdcall convention" OFF )
|
||||
|
||||
# This option must match the settings used in your program, in particular if you
|
||||
# are linking statically
|
||||
@ -110,7 +109,7 @@ ELSE ()
|
||||
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)
|
||||
FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
@ -130,30 +129,28 @@ IF (ENABLE_TRACE STREQUAL "ON")
|
||||
ENDIF()
|
||||
|
||||
# Include POSIX regex when it is required
|
||||
IF(WIN32 OR AMIGA)
|
||||
IF(WIN32 OR AMIGA OR ANDROID)
|
||||
INCLUDE_DIRECTORIES(deps/regex)
|
||||
SET(SRC_REGEX deps/regex/regex.c)
|
||||
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}")
|
||||
ELSEIF (NOT ZLIB_LIBRARY)
|
||||
ELSE()
|
||||
MESSAGE( "zlib was not found; using bundled 3rd-party sources." )
|
||||
INCLUDE_DIRECTORIES(deps/zlib)
|
||||
ADD_DEFINITIONS(-DNO_VIZ -DSTDC -DNO_GZIP)
|
||||
FILE(GLOB SRC_ZLIB deps/zlib/*.c)
|
||||
FILE(GLOB SRC_ZLIB deps/zlib/*.c deps/zlib/*.h)
|
||||
ENDIF()
|
||||
|
||||
IF(NOT LIBSSH2_LIBRARY)
|
||||
IF (NOT MINGW)
|
||||
FIND_PACKAGE(LIBSSH2 QUIET)
|
||||
ENDIF()
|
||||
IF (LIBSSH2_FOUND)
|
||||
@ -162,6 +159,7 @@ IF (LIBSSH2_FOUND)
|
||||
SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES})
|
||||
ENDIF()
|
||||
|
||||
|
||||
# Platform specific compilation flags
|
||||
IF (MSVC)
|
||||
|
||||
@ -287,19 +285,19 @@ ENDIF()
|
||||
ADD_DEFINITIONS(-D_FILE_OFFSET_BITS=64)
|
||||
|
||||
# Collect sourcefiles
|
||||
FILE(GLOB SRC_H include/git2/*.h)
|
||||
FILE(GLOB SRC_H include/git2.h include/git2/*.h include/git2/sys/*.h)
|
||||
|
||||
# On Windows use specific platform sources
|
||||
IF (WIN32 AND NOT CYGWIN)
|
||||
ADD_DEFINITIONS(-DWIN32 -D_WIN32_WINNT=0x0501)
|
||||
FILE(GLOB SRC_OS src/win32/*.c)
|
||||
FILE(GLOB SRC_OS src/win32/*.c src/win32/*.h)
|
||||
ELSEIF (AMIGA)
|
||||
ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R)
|
||||
FILE(GLOB SRC_OS src/amiga/*.c)
|
||||
FILE(GLOB SRC_OS src/amiga/*.c src/amiga/*.h)
|
||||
ELSE()
|
||||
FILE(GLOB SRC_OS src/unix/*.c)
|
||||
FILE(GLOB SRC_OS src/unix/*.c src/unix/*.h)
|
||||
ENDIF()
|
||||
FILE(GLOB SRC_GIT2 src/*.c src/transports/*.c src/xdiff/*.c)
|
||||
FILE(GLOB SRC_GIT2 src/*.c src/*.h src/transports/*.c src/transports/*.h src/xdiff/*.c src/xdiff/*.h)
|
||||
|
||||
# Determine architecture of the machine
|
||||
IF (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
@ -311,7 +309,7 @@ ELSE()
|
||||
ENDIF()
|
||||
|
||||
# Compile and link libgit2
|
||||
ADD_LIBRARY(git2 ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC})
|
||||
ADD_LIBRARY(git2 ${SRC_H} ${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)
|
||||
@ -361,12 +359,12 @@ IF (BUILD_CLAR)
|
||||
ADD_DEFINITIONS(-DCLAR_RESOURCES=\"${TEST_RESOURCES}\")
|
||||
|
||||
INCLUDE_DIRECTORIES(${CLAR_PATH})
|
||||
FILE(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c)
|
||||
FILE(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c ${CLAR_PATH}/*/*.h)
|
||||
SET(SRC_CLAR "${CLAR_PATH}/main.c" "${CLAR_PATH}/clar_libgit2.c" "${CLAR_PATH}/clar.c")
|
||||
|
||||
ADD_CUSTOM_COMMAND(
|
||||
OUTPUT ${CLAR_PATH}/clar.suite
|
||||
COMMAND ${PYTHON_EXECUTABLE} generate.py -f -xonline .
|
||||
COMMAND ${PYTHON_EXECUTABLE} generate.py -f -xonline -xstress .
|
||||
DEPENDS ${SRC_TEST}
|
||||
WORKING_DIRECTORY ${CLAR_PATH}
|
||||
)
|
||||
@ -375,7 +373,7 @@ IF (BUILD_CLAR)
|
||||
${CLAR_PATH}/clar.c
|
||||
PROPERTIES OBJECT_DEPENDS ${CLAR_PATH}/clar.suite)
|
||||
|
||||
ADD_EXECUTABLE(libgit2_clar ${SRC_GIT2} ${SRC_OS} ${SRC_CLAR} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1})
|
||||
ADD_EXECUTABLE(libgit2_clar ${SRC_H} ${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})
|
||||
@ -411,23 +409,5 @@ IF (TAGS)
|
||||
ENDIF ()
|
||||
|
||||
IF (BUILD_EXAMPLES)
|
||||
FILE(GLOB_RECURSE EXAMPLE_SRC examples/network/*.c)
|
||||
ADD_EXECUTABLE(cgit2 ${EXAMPLE_SRC})
|
||||
IF(WIN32)
|
||||
TARGET_LINK_LIBRARIES(cgit2 git2)
|
||||
ELSE()
|
||||
TARGET_LINK_LIBRARIES(cgit2 git2 pthread)
|
||||
ENDIF()
|
||||
|
||||
ADD_EXECUTABLE(git-diff examples/diff.c)
|
||||
TARGET_LINK_LIBRARIES(git-diff git2)
|
||||
|
||||
ADD_EXECUTABLE(git-general examples/general.c)
|
||||
TARGET_LINK_LIBRARIES(git-general git2)
|
||||
|
||||
ADD_EXECUTABLE(git-showindex examples/showindex.c)
|
||||
TARGET_LINK_LIBRARIES(git-showindex git2)
|
||||
|
||||
ADD_EXECUTABLE(git-rev-list examples/rev-list.c)
|
||||
TARGET_LINK_LIBRARIES(git-rev-list git2)
|
||||
ADD_SUBDIRECTORY(examples)
|
||||
ENDIF ()
|
||||
|
@ -48,6 +48,12 @@ Please include a nice description of your changes with your PR; if we have
|
||||
to read the whole diff to figure out why you're contributing in the first
|
||||
place, you're less likely to get feedback and have your change merged in.
|
||||
|
||||
If you are working on a particular area then feel free to submit a PR that
|
||||
highlights your work in progress (and flag in the PR title that it's not
|
||||
ready to merge). This will help in getting visibility for your fix, allow
|
||||
others to comment early on the changes and also let others know that you
|
||||
are currently working on something.
|
||||
|
||||
## Porting Code From Other Open-Source Projects
|
||||
|
||||
`libgit2` is licensed under the terms of the GPL v2 with a linking
|
||||
@ -57,14 +63,17 @@ The most common case is porting code from core Git. Git is a pure GPL
|
||||
project, which means that in order to port code to this project, we need the
|
||||
explicit permission of the author. Check the
|
||||
[`git.git-authors`](https://github.com/libgit2/libgit2/blob/development/git.git-authors)
|
||||
file for authors who have already consented; feel free to add someone if
|
||||
you've obtained their consent.
|
||||
file for authors who have already consented.
|
||||
|
||||
Other licenses have other requirements; check the license of the library
|
||||
you're porting code *from* to see what you need to do. As a general rule,
|
||||
MIT and BSD (3-clause) licenses are typically no problem. Apache 2.0
|
||||
license typically doesn't work due to GPL incompatibility.
|
||||
|
||||
If you are pulling in code from core Git, another project or code you've pulled from
|
||||
a forum / Stack Overflow then please flag this in your PR and also make sure you've
|
||||
given proper credit to the original author in the code snippet.
|
||||
|
||||
## Style Guide
|
||||
|
||||
`libgit2` is written in [ANSI C](http://en.wikipedia.org/wiki/ANSI_C)
|
||||
|
63
COPYING
63
COPYING
@ -928,3 +928,66 @@ necessary. Here is a sample; alter the names:
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Portions of src/win32/posix_w32.c are derrived from link_win32.c in PHP:
|
||||
|
||||
--------------------------------------------------------------------
|
||||
The PHP License, version 3.01
|
||||
Copyright (c) 1999 - 2012 The PHP Group. All rights reserved.
|
||||
--------------------------------------------------------------------
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, is permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
|
||||
3. The name "PHP" must not be used to endorse or promote products
|
||||
derived from this software without prior written permission. For
|
||||
written permission, please contact group@php.net.
|
||||
|
||||
4. Products derived from this software may not be called "PHP", nor
|
||||
may "PHP" appear in their name, without prior written permission
|
||||
from group@php.net. You may indicate that your software works in
|
||||
conjunction with PHP by saying "Foo for PHP" instead of calling
|
||||
it "PHP Foo" or "phpfoo"
|
||||
|
||||
5. The PHP Group may publish revised and/or new versions of the
|
||||
license from time to time. Each version will be given a
|
||||
distinguishing version number.
|
||||
Once covered code has been published under a particular version
|
||||
of the license, you may always continue to use it under the terms
|
||||
of that version. You may also choose to use such covered code
|
||||
under the terms of any subsequent version of the license
|
||||
published by the PHP Group. No one other than the PHP Group has
|
||||
the right to modify the terms applicable to covered code created
|
||||
under this License.
|
||||
|
||||
6. Redistributions of any form whatsoever must retain the following
|
||||
acknowledgment:
|
||||
"This product includes PHP software, freely available from
|
||||
<http://www.php.net/software/>".
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND
|
||||
ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP
|
||||
DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
||||
OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
--------------------------------------------------------------------
|
||||
|
||||
|
@ -1,15 +1,33 @@
|
||||
PLATFORM=$(shell uname -o)
|
||||
PLATFORM=$(shell uname -s)
|
||||
|
||||
ifneq (,$(CROSS_COMPILE))
|
||||
PREFIX=$(CROSS_COMPILE)-
|
||||
else
|
||||
PREFIX=
|
||||
endif
|
||||
|
||||
MINGW=0
|
||||
ifneq (,$(findstring MINGW32,$(PLATFORM)))
|
||||
MINGW=1
|
||||
endif
|
||||
ifneq (,$(findstring mingw,$(CROSS_COMPILE)))
|
||||
MINGW=1
|
||||
endif
|
||||
|
||||
rm=rm -f
|
||||
AR=ar cq
|
||||
RANLIB=ranlib
|
||||
AR=$(PREFIX)ar cq
|
||||
RANLIB=$(PREFIX)ranlib
|
||||
|
||||
LIBNAME=libgit2.a
|
||||
ifeq ($(PLATFORM),Msys)
|
||||
|
||||
ifeq ($(MINGW),1)
|
||||
CC=gcc
|
||||
else
|
||||
CC=cc
|
||||
endif
|
||||
|
||||
CC:=$(PREFIX)$(CC)
|
||||
|
||||
INCLUDES= -I. -Isrc -Iinclude -Ideps/http-parser -Ideps/zlib
|
||||
|
||||
DEFINES= $(INCLUDES) -DNO_VIZ -DSTDC -DNO_GZIP -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE $(EXTRA_DEFINES)
|
||||
@ -17,10 +35,10 @@ CFLAGS= -g $(DEFINES) -Wall -Wextra -O2 $(EXTRA_CFLAGS)
|
||||
|
||||
SRCS = $(wildcard src/*.c) $(wildcard src/transports/*.c) $(wildcard src/xdiff/*.c) $(wildcard deps/http-parser/*.c) $(wildcard deps/zlib/*.c) src/hash/hash_generic.c
|
||||
|
||||
ifeq ($(PLATFORM),Msys)
|
||||
ifeq ($(MINGW),1)
|
||||
SRCS += $(wildcard src/win32/*.c) $(wildcard src/compat/*.c) deps/regex/regex.c
|
||||
INCLUDES += -Ideps/regex
|
||||
DEFINES += -DWIN32 -D_WIN32_WINNT=0x0501
|
||||
DEFINES += -DWIN32 -D_WIN32_WINNT=0x0501 -D__USE_MINGW_ANSI_STDIO=1
|
||||
else
|
||||
SRCS += $(wildcard src/unix/*.c)
|
||||
CFLAGS += -fPIC
|
||||
|
46
README.md
46
README.md
@ -11,20 +11,24 @@ libgit2 is licensed under a **very permissive license** (GPLv2 with a special Li
|
||||
This basically means that you can link it (unmodified) with any kind of software without having to
|
||||
release its source code.
|
||||
|
||||
* Mailing list: ~~<libgit2@librelist.org>~~
|
||||
The libgit2 mailing list has
|
||||
traditionally been hosted in Librelist, but Librelist is and has always
|
||||
been a shitshow. We encourage you to [open an issue](https://github.com/libgit2/libgit2/issues)
|
||||
on GitHub instead for any questions regarding the library.
|
||||
* Archives: <http://librelist.com/browser/libgit2/>
|
||||
* Website: <http://libgit2.github.com>
|
||||
* StackOverflow Tag: [libgit2](http://stackoverflow.com/questions/tagged/libgit2)
|
||||
* Issues: <https://github.com/libgit2/libgit2/issues>
|
||||
* API documentation: <http://libgit2.github.com/libgit2>
|
||||
* IRC: #libgit2 on irc.freenode.net.
|
||||
* Mailing list: The libgit2 mailing list was
|
||||
traditionally hosted in Librelist but has been deprecated. We encourage you to
|
||||
[use StackOverflow](http://stackoverflow.com/questions/tagged/libgit2) instead for any questions regarding
|
||||
the library, or [open an issue](https://github.com/libgit2/libgit2/issues)
|
||||
on GitHub for bug reports. The mailing list archives are still available at
|
||||
<http://librelist.com/browser/libgit2/>.
|
||||
|
||||
|
||||
What It Can Do
|
||||
==================================
|
||||
|
||||
libgit2 is already very usable.
|
||||
libgit2 is already very usable and is being used in production for many applications including the GitHub.com site, in Plastic SCM
|
||||
and also powering Microsoft's Visual Studio tools for Git. The library provides:
|
||||
|
||||
* SHA conversions, formatting and shortening
|
||||
* abstracted ODB backend system
|
||||
@ -104,6 +108,28 @@ See [the wiki]
|
||||
(https://github.com/libgit2/libgit2/wiki/Building-libgit2-on-Windows)
|
||||
for more detailed instructions.
|
||||
|
||||
Android
|
||||
-------
|
||||
|
||||
Extract toolchain from NDK using, `make-standalone-toolchain.sh` script.
|
||||
Optionaly, crosscompile and install OpenSSL inside of it. Then create CMake
|
||||
toolchain file that configures paths to your crosscompiler (substitude `{PATH}`
|
||||
with full path to the toolchain):
|
||||
|
||||
SET(CMAKE_SYSTEM_NAME Linux)
|
||||
SET(CMAKE_SYSTEM_VERSION Android)
|
||||
|
||||
SET(CMAKE_C_COMPILER {PATH}/bin/arm-linux-androideabi-gcc)
|
||||
SET(CMAKE_CXX_COMPILER {PATH}/bin/arm-linux-androideabi-g++)
|
||||
SET(CMAKE_FIND_ROOT_PATH {PATH}/sysroot/)
|
||||
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
|
||||
Add `-DCMAKE_TOOLCHAIN_FILE={pathToToolchainFile} -DANDROID=1` to cmake command
|
||||
when configuring.
|
||||
|
||||
Language Bindings
|
||||
==================================
|
||||
|
||||
@ -118,9 +144,9 @@ Here are the bindings to libgit2 that are currently available:
|
||||
* Delphi
|
||||
* GitForDelphi <https://github.com/libgit2/GitForDelphi>
|
||||
* Erlang
|
||||
* Geef <https://github.com/schacon/geef>
|
||||
* Geef <https://github.com/carlosmn/geef>
|
||||
* Go
|
||||
* go-git <https://github.com/str1ngs/go-git>
|
||||
* git2go <https://github.com/libgit2/git2go>
|
||||
* GObject
|
||||
* libgit2-glib <https://live.gnome.org/Libgit2-glib>
|
||||
* Haskell
|
||||
@ -128,8 +154,8 @@ Here are the bindings to libgit2 that are currently available:
|
||||
* Lua
|
||||
* luagit2 <https://github.com/libgit2/luagit2>
|
||||
* .NET
|
||||
* libgit2net, low level bindings <https://github.com/txdv/libgit2net>
|
||||
* libgit2sharp <https://github.com/libgit2/libgit2sharp>
|
||||
* libgit2net, low level bindings superceeded by libgit2sharp <https://github.com/txdv/libgit2net>
|
||||
* Node.js
|
||||
* node-gitteh <https://github.com/libgit2/node-gitteh>
|
||||
* nodegit <https://github.com/tbranyen/nodegit>
|
||||
|
14
examples/CMakeLists.txt
Normal file
14
examples/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
||||
FILE(GLOB_RECURSE SRC_EXAMPLE_GIT2 network/*.c network/*.h)
|
||||
ADD_EXECUTABLE(cgit2 ${SRC_EXAMPLE_GIT2})
|
||||
IF(WIN32 OR ANDROID)
|
||||
TARGET_LINK_LIBRARIES(cgit2 git2)
|
||||
ELSE()
|
||||
TARGET_LINK_LIBRARIES(cgit2 git2 pthread)
|
||||
ENDIF()
|
||||
|
||||
FILE(GLOB SRC_EXAMPLE_APPS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.c)
|
||||
FOREACH(src_app ${SRC_EXAMPLE_APPS})
|
||||
STRING(REPLACE ".c" "" app_name ${src_app})
|
||||
ADD_EXECUTABLE(${app_name} ${src_app})
|
||||
TARGET_LINK_LIBRARIES(${app_name} git2)
|
||||
ENDFOREACH()
|
@ -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 cat-file
|
||||
APPS = general showindex diff rev-list cat-file status log rev-parse init
|
||||
|
||||
all: $(APPS)
|
||||
|
||||
|
143
examples/add.c
Normal file
143
examples/add.c
Normal file
@ -0,0 +1,143 @@
|
||||
#include <git2.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
enum print_options {
|
||||
SKIP = 1,
|
||||
VERBOSE = 2,
|
||||
UPDATE = 4,
|
||||
};
|
||||
|
||||
struct print_payload {
|
||||
enum print_options options;
|
||||
git_repository *repo;
|
||||
};
|
||||
|
||||
void init_array(git_strarray *array, int argc, char **argv)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
array->count = argc;
|
||||
array->strings = malloc(sizeof(char*) * array->count);
|
||||
assert(array->strings!=NULL);
|
||||
|
||||
for(i=0; i<array->count; i++) {
|
||||
array->strings[i]=argv[i];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int print_matched_cb(const char *path, const char *matched_pathspec, void *payload)
|
||||
{
|
||||
struct print_payload p = *(struct print_payload*)(payload);
|
||||
int ret;
|
||||
git_status_t status;
|
||||
(void)matched_pathspec;
|
||||
|
||||
if (git_status_file(&status, p.repo, path)) {
|
||||
return -1; //abort
|
||||
}
|
||||
|
||||
if (status & GIT_STATUS_WT_MODIFIED ||
|
||||
status & GIT_STATUS_WT_NEW) {
|
||||
printf("add '%s'\n", path);
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
if(p.options & SKIP) {
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void print_usage(void)
|
||||
{
|
||||
fprintf(stderr, "usage: add [options] [--] file-spec [file-spec] [...]\n\n");
|
||||
fprintf(stderr, "\t-n, --dry-run dry run\n");
|
||||
fprintf(stderr, "\t-v, --verbose be verbose\n");
|
||||
fprintf(stderr, "\t-u, --update update tracked files\n");
|
||||
}
|
||||
|
||||
|
||||
int main (int argc, char** argv)
|
||||
{
|
||||
git_index_matched_path_cb matched_cb = NULL;
|
||||
git_repository *repo = NULL;
|
||||
git_index *index;
|
||||
git_strarray array = {0};
|
||||
int i, options = 0;
|
||||
struct print_payload payload = {0};
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
if (argv[i][0] != '-') {
|
||||
break;
|
||||
}
|
||||
else if(!strcmp(argv[i], "--verbose") || !strcmp(argv[i], "-v")) {
|
||||
options |= VERBOSE;
|
||||
}
|
||||
else if(!strcmp(argv[i], "--dry-run") || !strcmp(argv[i], "-n")) {
|
||||
options |= SKIP;
|
||||
}
|
||||
else if(!strcmp(argv[i], "--update") || !strcmp(argv[i], "-u")) {
|
||||
options |= UPDATE;
|
||||
}
|
||||
else if(!strcmp(argv[i], "-h")) {
|
||||
print_usage();
|
||||
break;
|
||||
}
|
||||
else if(!strcmp(argv[i], "--")) {
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Unsupported option %s.\n", argv[i]);
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc<=i) {
|
||||
print_usage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
git_threads_init();
|
||||
|
||||
init_array(&array, argc-i, argv+i);
|
||||
|
||||
if (git_repository_open(&repo, ".") < 0) {
|
||||
fprintf(stderr, "No git repository\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (git_repository_index(&index, repo) < 0) {
|
||||
fprintf(stderr, "Could not open repository index\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (options&VERBOSE || options&SKIP) {
|
||||
matched_cb = &print_matched_cb;
|
||||
}
|
||||
|
||||
payload.options = options;
|
||||
payload.repo = repo;
|
||||
|
||||
if (options&UPDATE) {
|
||||
git_index_update_all(index, &array, matched_cb, &payload);
|
||||
} else {
|
||||
git_index_add_all(index, &array, 0, matched_cb, &payload);
|
||||
}
|
||||
|
||||
git_index_write(index);
|
||||
git_index_free(index);
|
||||
git_repository_free(repo);
|
||||
|
||||
git_threads_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
245
examples/init.c
Normal file
245
examples/init.c
Normal file
@ -0,0 +1,245 @@
|
||||
/*
|
||||
* This is a sample program that is similar to "git init". See the
|
||||
* documentation for that (try "git help init") to understand what this
|
||||
* program is emulating.
|
||||
*
|
||||
* This demonstrates using the libgit2 APIs to initialize a new repository.
|
||||
*
|
||||
* This also contains a special additional option that regular "git init"
|
||||
* does not support which is "--initial-commit" to make a first empty commit.
|
||||
* That is demonstrated in the "create_initial_commit" helper function.
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <git2.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
/* not actually good error handling */
|
||||
static void fail(const char *msg, const char *arg)
|
||||
{
|
||||
if (arg)
|
||||
fprintf(stderr, "%s %s\n", msg, arg);
|
||||
else
|
||||
fprintf(stderr, "%s\n", msg);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void usage(const char *error, const char *arg)
|
||||
{
|
||||
fprintf(stderr, "error: %s '%s'\n", error, arg);
|
||||
fprintf(stderr, "usage: init [-q | --quiet] [--bare] "
|
||||
"[--template=<dir>] [--shared[=perms]] <directory>\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* simple string prefix test used in argument parsing */
|
||||
static size_t is_prefixed(const char *arg, const char *pfx)
|
||||
{
|
||||
size_t len = strlen(pfx);
|
||||
return !strncmp(arg, pfx, len) ? len : 0;
|
||||
}
|
||||
|
||||
/* parse the tail of the --shared= argument */
|
||||
static uint32_t parse_shared(const char *shared)
|
||||
{
|
||||
if (!strcmp(shared, "false") || !strcmp(shared, "umask"))
|
||||
return GIT_REPOSITORY_INIT_SHARED_UMASK;
|
||||
|
||||
else if (!strcmp(shared, "true") || !strcmp(shared, "group"))
|
||||
return GIT_REPOSITORY_INIT_SHARED_GROUP;
|
||||
|
||||
else if (!strcmp(shared, "all") || !strcmp(shared, "world") ||
|
||||
!strcmp(shared, "everybody"))
|
||||
return GIT_REPOSITORY_INIT_SHARED_ALL;
|
||||
|
||||
else if (shared[0] == '0') {
|
||||
long val;
|
||||
char *end = NULL;
|
||||
val = strtol(shared + 1, &end, 8);
|
||||
if (end == shared + 1 || *end != 0)
|
||||
usage("invalid octal value for --shared", shared);
|
||||
return (uint32_t)val;
|
||||
}
|
||||
|
||||
else
|
||||
usage("unknown value for --shared", shared);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* forward declaration of helper to make an empty parent-less commit */
|
||||
static void create_initial_commit(git_repository *repo);
|
||||
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
git_repository *repo = NULL;
|
||||
int no_options = 1, quiet = 0, bare = 0, initial_commit = 0, i;
|
||||
uint32_t shared = GIT_REPOSITORY_INIT_SHARED_UMASK;
|
||||
const char *template = NULL, *gitdir = NULL, *dir = NULL;
|
||||
size_t pfxlen;
|
||||
|
||||
git_threads_init();
|
||||
|
||||
/* Process arguments */
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
char *a = argv[i];
|
||||
|
||||
if (a[0] == '-')
|
||||
no_options = 0;
|
||||
|
||||
if (a[0] != '-') {
|
||||
if (dir != NULL)
|
||||
usage("extra argument", a);
|
||||
dir = a;
|
||||
}
|
||||
else if (!strcmp(a, "-q") || !strcmp(a, "--quiet"))
|
||||
quiet = 1;
|
||||
else if (!strcmp(a, "--bare"))
|
||||
bare = 1;
|
||||
else if ((pfxlen = is_prefixed(a, "--template=")) > 0)
|
||||
template = a + pfxlen;
|
||||
else if (!strcmp(a, "--separate-git-dir"))
|
||||
gitdir = argv[++i];
|
||||
else if ((pfxlen = is_prefixed(a, "--separate-git-dir=")) > 0)
|
||||
gitdir = a + pfxlen;
|
||||
else if (!strcmp(a, "--shared"))
|
||||
shared = GIT_REPOSITORY_INIT_SHARED_GROUP;
|
||||
else if ((pfxlen = is_prefixed(a, "--shared=")) > 0)
|
||||
shared = parse_shared(a + pfxlen);
|
||||
else if (!strcmp(a, "--initial-commit"))
|
||||
initial_commit = 1;
|
||||
else
|
||||
usage("unknown option", a);
|
||||
}
|
||||
|
||||
if (!dir)
|
||||
usage("must specify directory to init", NULL);
|
||||
|
||||
/* Initialize repository */
|
||||
|
||||
if (no_options) {
|
||||
/* No options were specified, so let's demonstrate the default
|
||||
* simple case of git_repository_init() API usage...
|
||||
*/
|
||||
|
||||
if (git_repository_init(&repo, dir, 0) < 0)
|
||||
fail("Could not initialize repository", dir);
|
||||
}
|
||||
else {
|
||||
/* Some command line options were specified, so we'll use the
|
||||
* extended init API to handle them
|
||||
*/
|
||||
git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
|
||||
|
||||
if (bare)
|
||||
opts.flags |= GIT_REPOSITORY_INIT_BARE;
|
||||
|
||||
if (template) {
|
||||
opts.flags |= GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
|
||||
opts.template_path = template;
|
||||
}
|
||||
|
||||
if (gitdir) {
|
||||
/* if you specified a separate git directory, then initialize
|
||||
* the repository at that path and use the second path as the
|
||||
* working directory of the repository (with a git-link file)
|
||||
*/
|
||||
opts.workdir_path = dir;
|
||||
dir = gitdir;
|
||||
}
|
||||
|
||||
if (shared != 0)
|
||||
opts.mode = shared;
|
||||
|
||||
if (git_repository_init_ext(&repo, dir, &opts) < 0)
|
||||
fail("Could not initialize repository", dir);
|
||||
}
|
||||
|
||||
/* Print a message to stdout like "git init" does */
|
||||
|
||||
if (!quiet) {
|
||||
if (bare || gitdir)
|
||||
dir = git_repository_path(repo);
|
||||
else
|
||||
dir = git_repository_workdir(repo);
|
||||
|
||||
printf("Initialized empty Git repository in %s\n", dir);
|
||||
}
|
||||
|
||||
/* As an extension to the basic "git init" command, this example
|
||||
* gives the option to create an empty initial commit. This is
|
||||
* mostly to demonstrate what it takes to do that, but also some
|
||||
* people like to have that empty base commit in their repo.
|
||||
*/
|
||||
if (initial_commit) {
|
||||
create_initial_commit(repo);
|
||||
printf("Created empty initial commit\n");
|
||||
}
|
||||
|
||||
git_repository_free(repo);
|
||||
git_threads_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Unlike regular "git init", this example shows how to create an initial
|
||||
* empty commit in the repository. This is the helper function that does
|
||||
* that.
|
||||
*/
|
||||
static void create_initial_commit(git_repository *repo)
|
||||
{
|
||||
git_signature *sig;
|
||||
git_index *index;
|
||||
git_oid tree_id, commit_id;
|
||||
git_tree *tree;
|
||||
|
||||
/* First use the config to initialize a commit signature for the user */
|
||||
|
||||
if (git_signature_default(&sig, repo) < 0)
|
||||
fail("Unable to create a commit signature.",
|
||||
"Perhaps 'user.name' and 'user.email' are not set");
|
||||
|
||||
/* Now let's create an empty tree for this commit */
|
||||
|
||||
if (git_repository_index(&index, repo) < 0)
|
||||
fail("Could not open repository index", NULL);
|
||||
|
||||
/* Outside of this example, you could call git_index_add_bypath()
|
||||
* here to put actual files into the index. For our purposes, we'll
|
||||
* leave it empty for now.
|
||||
*/
|
||||
|
||||
if (git_index_write_tree(&tree_id, index) < 0)
|
||||
fail("Unable to write initial tree from index", NULL);
|
||||
|
||||
git_index_free(index);
|
||||
|
||||
if (git_tree_lookup(&tree, repo, &tree_id) < 0)
|
||||
fail("Could not look up initial tree", NULL);
|
||||
|
||||
/* Ready to create the initial commit
|
||||
*
|
||||
* Normally creating a commit would involve looking up the current
|
||||
* HEAD commit and making that be the parent of the initial commit,
|
||||
* but here this is the first commit so there will be no parent.
|
||||
*/
|
||||
|
||||
if (git_commit_create_v(
|
||||
&commit_id, repo, "HEAD", sig, sig,
|
||||
NULL, "Initial commit", tree, 0) < 0)
|
||||
fail("Could not create the initial commit", NULL);
|
||||
|
||||
/* Clean up so we don't leak memory */
|
||||
|
||||
git_tree_free(tree);
|
||||
git_signature_free(sig);
|
||||
}
|
406
examples/log.c
Normal file
406
examples/log.c
Normal file
@ -0,0 +1,406 @@
|
||||
#include <stdio.h>
|
||||
#include <git2.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void check(int error, const char *message, const char *arg)
|
||||
{
|
||||
if (!error)
|
||||
return;
|
||||
if (arg)
|
||||
fprintf(stderr, "%s '%s' (%d)\n", message, arg, error);
|
||||
else
|
||||
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: log [<options>]\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct log_state {
|
||||
git_repository *repo;
|
||||
const char *repodir;
|
||||
git_revwalk *walker;
|
||||
int hide;
|
||||
int sorting;
|
||||
};
|
||||
|
||||
static void set_sorting(struct log_state *s, unsigned int sort_mode)
|
||||
{
|
||||
if (!s->repo) {
|
||||
if (!s->repodir) s->repodir = ".";
|
||||
check(git_repository_open_ext(&s->repo, s->repodir, 0, NULL),
|
||||
"Could not open repository", s->repodir);
|
||||
}
|
||||
|
||||
if (!s->walker)
|
||||
check(git_revwalk_new(&s->walker, s->repo),
|
||||
"Could not create revision walker", NULL);
|
||||
|
||||
if (sort_mode == GIT_SORT_REVERSE)
|
||||
s->sorting = s->sorting ^ GIT_SORT_REVERSE;
|
||||
else
|
||||
s->sorting = sort_mode | (s->sorting & GIT_SORT_REVERSE);
|
||||
|
||||
git_revwalk_sorting(s->walker, s->sorting);
|
||||
}
|
||||
|
||||
static void push_rev(struct log_state *s, git_object *obj, int hide)
|
||||
{
|
||||
hide = s->hide ^ hide;
|
||||
|
||||
if (!s->walker) {
|
||||
check(git_revwalk_new(&s->walker, s->repo),
|
||||
"Could not create revision walker", NULL);
|
||||
git_revwalk_sorting(s->walker, s->sorting);
|
||||
}
|
||||
|
||||
if (!obj)
|
||||
check(git_revwalk_push_head(s->walker),
|
||||
"Could not find repository HEAD", NULL);
|
||||
else if (hide)
|
||||
check(git_revwalk_hide(s->walker, git_object_id(obj)),
|
||||
"Reference does not refer to a commit", NULL);
|
||||
else
|
||||
check(git_revwalk_push(s->walker, git_object_id(obj)),
|
||||
"Reference does not refer to a commit", NULL);
|
||||
|
||||
git_object_free(obj);
|
||||
}
|
||||
|
||||
static int add_revision(struct log_state *s, const char *revstr)
|
||||
{
|
||||
git_revspec revs;
|
||||
int hide = 0;
|
||||
|
||||
if (!s->repo) {
|
||||
if (!s->repodir) s->repodir = ".";
|
||||
check(git_repository_open_ext(&s->repo, s->repodir, 0, NULL),
|
||||
"Could not open repository", s->repodir);
|
||||
}
|
||||
|
||||
if (!revstr) {
|
||||
push_rev(s, NULL, hide);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*revstr == '^') {
|
||||
revs.flags = GIT_REVPARSE_SINGLE;
|
||||
hide = !hide;
|
||||
|
||||
if (git_revparse_single(&revs.from, s->repo, revstr + 1) < 0)
|
||||
return -1;
|
||||
} else if (git_revparse(&revs, s->repo, revstr) < 0)
|
||||
return -1;
|
||||
|
||||
if ((revs.flags & GIT_REVPARSE_SINGLE) != 0)
|
||||
push_rev(s, revs.from, hide);
|
||||
else {
|
||||
push_rev(s, revs.to, hide);
|
||||
|
||||
if ((revs.flags & GIT_REVPARSE_MERGE_BASE) != 0) {
|
||||
git_oid base;
|
||||
check(git_merge_base(&base, s->repo,
|
||||
git_object_id(revs.from), git_object_id(revs.to)),
|
||||
"Could not find merge base", revstr);
|
||||
check(git_object_lookup(&revs.to, s->repo, &base, GIT_OBJ_COMMIT),
|
||||
"Could not find merge base commit", NULL);
|
||||
|
||||
push_rev(s, revs.to, hide);
|
||||
}
|
||||
|
||||
push_rev(s, revs.from, !hide);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_time(const git_time *intime, const char *prefix)
|
||||
{
|
||||
char sign, out[32];
|
||||
struct tm intm;
|
||||
int offset, hours, minutes;
|
||||
time_t t;
|
||||
|
||||
offset = intime->offset;
|
||||
if (offset < 0) {
|
||||
sign = '-';
|
||||
offset = -offset;
|
||||
} else {
|
||||
sign = '+';
|
||||
}
|
||||
|
||||
hours = offset / 60;
|
||||
minutes = offset % 60;
|
||||
|
||||
t = (time_t)intime->time + (intime->offset * 60);
|
||||
|
||||
gmtime_r(&t, &intm);
|
||||
strftime(out, sizeof(out), "%a %b %e %T %Y", &intm);
|
||||
|
||||
printf("%s%s %c%02d%02d\n", prefix, out, sign, hours, minutes);
|
||||
}
|
||||
|
||||
static void print_commit(git_commit *commit)
|
||||
{
|
||||
char buf[GIT_OID_HEXSZ + 1];
|
||||
int i, count;
|
||||
const git_signature *sig;
|
||||
const char *scan, *eol;
|
||||
|
||||
git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
|
||||
printf("commit %s\n", buf);
|
||||
|
||||
if ((count = (int)git_commit_parentcount(commit)) > 1) {
|
||||
printf("Merge:");
|
||||
for (i = 0; i < count; ++i) {
|
||||
git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
|
||||
printf(" %s", buf);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if ((sig = git_commit_author(commit)) != NULL) {
|
||||
printf("Author: %s <%s>\n", sig->name, sig->email);
|
||||
print_time(&sig->when, "Date: ");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
for (scan = git_commit_message(commit); scan && *scan; ) {
|
||||
for (eol = scan; *eol && *eol != '\n'; ++eol) /* find eol */;
|
||||
|
||||
printf(" %.*s\n", (int)(eol - scan), scan);
|
||||
scan = *eol ? eol + 1 : NULL;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int print_diff(
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_range *range,
|
||||
char usage,
|
||||
const char *line,
|
||||
size_t line_len,
|
||||
void *data)
|
||||
{
|
||||
(void)delta; (void)range; (void)usage; (void)line_len; (void)data;
|
||||
fputs(line, stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int match_int(int *value, const char *arg, int allow_negative)
|
||||
{
|
||||
char *found;
|
||||
*value = (int)strtol(arg, &found, 10);
|
||||
return (found && *found == '\0' && (allow_negative || *value >= 0));
|
||||
}
|
||||
|
||||
static int match_int_arg(
|
||||
int *value, const char *arg, const char *pfx, int allow_negative)
|
||||
{
|
||||
size_t pfxlen = strlen(pfx);
|
||||
if (strncmp(arg, pfx, pfxlen) != 0)
|
||||
return 0;
|
||||
if (!match_int(value, arg + pfxlen, allow_negative))
|
||||
usage("Invalid value after argument", arg);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int match_with_parent(
|
||||
git_commit *commit, int i, git_diff_options *opts)
|
||||
{
|
||||
git_commit *parent;
|
||||
git_tree *a, *b;
|
||||
git_diff_list *diff;
|
||||
int ndeltas;
|
||||
|
||||
check(git_commit_parent(&parent, commit, (size_t)i), "Get parent", NULL);
|
||||
check(git_commit_tree(&a, parent), "Tree for parent", NULL);
|
||||
check(git_commit_tree(&b, commit), "Tree for commit", NULL);
|
||||
check(git_diff_tree_to_tree(&diff, git_commit_owner(commit), a, b, opts),
|
||||
"Checking diff between parent and commit", NULL);
|
||||
|
||||
ndeltas = (int)git_diff_num_deltas(diff);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
git_tree_free(a);
|
||||
git_tree_free(b);
|
||||
git_commit_free(parent);
|
||||
|
||||
return ndeltas > 0;
|
||||
}
|
||||
|
||||
struct log_options {
|
||||
int show_diff;
|
||||
int skip, limit;
|
||||
int min_parents, max_parents;
|
||||
git_time_t before;
|
||||
git_time_t after;
|
||||
char *author;
|
||||
char *committer;
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i, count = 0, printed = 0, parents;
|
||||
char *a;
|
||||
struct log_state s;
|
||||
struct log_options opt;
|
||||
git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
|
||||
git_oid oid;
|
||||
git_commit *commit = NULL;
|
||||
git_pathspec *ps = NULL;
|
||||
|
||||
git_threads_init();
|
||||
|
||||
memset(&s, 0, sizeof(s));
|
||||
s.sorting = GIT_SORT_TIME;
|
||||
|
||||
memset(&opt, 0, sizeof(opt));
|
||||
opt.max_parents = -1;
|
||||
opt.limit = -1;
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
a = argv[i];
|
||||
|
||||
if (a[0] != '-') {
|
||||
if (!add_revision(&s, a))
|
||||
++count;
|
||||
else /* try failed revision parse as filename */
|
||||
break;
|
||||
} else if (!strcmp(a, "--")) {
|
||||
++i;
|
||||
break;
|
||||
}
|
||||
else if (!strcmp(a, "--date-order"))
|
||||
set_sorting(&s, GIT_SORT_TIME);
|
||||
else if (!strcmp(a, "--topo-order"))
|
||||
set_sorting(&s, GIT_SORT_TOPOLOGICAL);
|
||||
else if (!strcmp(a, "--reverse"))
|
||||
set_sorting(&s, GIT_SORT_REVERSE);
|
||||
else if (!strncmp(a, "--git-dir=", strlen("--git-dir=")))
|
||||
s.repodir = a + strlen("--git-dir=");
|
||||
else if (match_int_arg(&opt.skip, a, "--skip=", 0))
|
||||
/* found valid --skip */;
|
||||
else if (match_int_arg(&opt.limit, a, "--max-count=", 0))
|
||||
/* found valid --max-count */;
|
||||
else if (a[1] >= '0' && a[1] <= '9') {
|
||||
if (!match_int(&opt.limit, a + 1, 0))
|
||||
usage("Invalid limit on number of commits", a);
|
||||
} else if (!strcmp(a, "-n")) {
|
||||
if (i + 1 == argc || !match_int(&opt.limit, argv[i + 1], 0))
|
||||
usage("Argument -n not followed by valid count", argv[i + 1]);
|
||||
else
|
||||
++i;
|
||||
}
|
||||
else if (!strcmp(a, "--merges"))
|
||||
opt.min_parents = 2;
|
||||
else if (!strcmp(a, "--no-merges"))
|
||||
opt.max_parents = 1;
|
||||
else if (!strcmp(a, "--no-min-parents"))
|
||||
opt.min_parents = 0;
|
||||
else if (!strcmp(a, "--no-max-parents"))
|
||||
opt.max_parents = -1;
|
||||
else if (match_int_arg(&opt.max_parents, a, "--max-parents=", 1))
|
||||
/* found valid --max-parents */;
|
||||
else if (match_int_arg(&opt.min_parents, a, "--min-parents=", 0))
|
||||
/* found valid --min_parents */;
|
||||
else if (!strcmp(a, "-p") || !strcmp(a, "-u") || !strcmp(a, "--patch"))
|
||||
opt.show_diff = 1;
|
||||
else
|
||||
usage("Unsupported argument", a);
|
||||
}
|
||||
|
||||
if (!count)
|
||||
add_revision(&s, NULL);
|
||||
|
||||
diffopts.pathspec.strings = &argv[i];
|
||||
diffopts.pathspec.count = argc - i;
|
||||
if (diffopts.pathspec.count > 0)
|
||||
check(git_pathspec_new(&ps, &diffopts.pathspec),
|
||||
"Building pathspec", NULL);
|
||||
|
||||
printed = count = 0;
|
||||
|
||||
for (; !git_revwalk_next(&oid, s.walker); git_commit_free(commit)) {
|
||||
check(git_commit_lookup(&commit, s.repo, &oid),
|
||||
"Failed to look up commit", NULL);
|
||||
|
||||
parents = (int)git_commit_parentcount(commit);
|
||||
if (parents < opt.min_parents)
|
||||
continue;
|
||||
if (opt.max_parents > 0 && parents > opt.max_parents)
|
||||
continue;
|
||||
|
||||
if (diffopts.pathspec.count > 0) {
|
||||
int unmatched = parents;
|
||||
|
||||
if (parents == 0) {
|
||||
git_tree *tree;
|
||||
check(git_commit_tree(&tree, commit), "Get tree", NULL);
|
||||
if (git_pathspec_match_tree(
|
||||
NULL, tree, GIT_PATHSPEC_NO_MATCH_ERROR, ps) != 0)
|
||||
unmatched = 1;
|
||||
git_tree_free(tree);
|
||||
} else if (parents == 1) {
|
||||
unmatched = match_with_parent(commit, 0, &diffopts) ? 0 : 1;
|
||||
} else {
|
||||
for (i = 0; i < parents; ++i) {
|
||||
if (match_with_parent(commit, i, &diffopts))
|
||||
unmatched--;
|
||||
}
|
||||
}
|
||||
|
||||
if (unmatched > 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (count++ < opt.skip)
|
||||
continue;
|
||||
if (opt.limit != -1 && printed++ >= opt.limit) {
|
||||
git_commit_free(commit);
|
||||
break;
|
||||
}
|
||||
|
||||
print_commit(commit);
|
||||
|
||||
if (opt.show_diff) {
|
||||
git_tree *a = NULL, *b = NULL;
|
||||
git_diff_list *diff = NULL;
|
||||
|
||||
if (parents > 1)
|
||||
continue;
|
||||
check(git_commit_tree(&b, commit), "Get tree", NULL);
|
||||
if (parents == 1) {
|
||||
git_commit *parent;
|
||||
check(git_commit_parent(&parent, commit, 0), "Get parent", NULL);
|
||||
check(git_commit_tree(&a, parent), "Tree for parent", NULL);
|
||||
git_commit_free(parent);
|
||||
}
|
||||
|
||||
check(git_diff_tree_to_tree(
|
||||
&diff, git_commit_owner(commit), a, b, &diffopts),
|
||||
"Diff commit with parent", NULL);
|
||||
check(git_diff_print_patch(diff, print_diff, NULL),
|
||||
"Displaying diff", NULL);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
git_tree_free(a);
|
||||
git_tree_free(b);
|
||||
}
|
||||
}
|
||||
|
||||
git_pathspec_free(ps);
|
||||
git_revwalk_free(s.walker);
|
||||
git_repository_free(s.repo);
|
||||
git_threads_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
@ -11,7 +11,8 @@ OBJECTS = \
|
||||
ls-remote.o \
|
||||
fetch.o \
|
||||
clone.o \
|
||||
index-pack.o
|
||||
index-pack.o \
|
||||
common.o
|
||||
|
||||
all: $(OBJECTS)
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) -o git2 $(OBJECTS) $(LIBRARIES)
|
||||
|
@ -9,19 +9,6 @@
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
|
||||
/* Shamelessly borrowed from http://stackoverflow.com/questions/3417837/
|
||||
* with permission of the original author, Martin Pool.
|
||||
* http://sourcefrog.net/weblog/software/languages/C/unused.html
|
||||
*/
|
||||
#ifdef UNUSED
|
||||
#elif defined(__GNUC__)
|
||||
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
|
||||
#elif defined(__LCLINT__)
|
||||
# define UNUSED(x) /*@unused@*/ x
|
||||
#else
|
||||
# define UNUSED(x) x
|
||||
#endif
|
||||
|
||||
typedef struct progress_data {
|
||||
git_transfer_progress fetch_progress;
|
||||
size_t completed_steps;
|
||||
@ -63,24 +50,6 @@ static void checkout_progress(const char *path, size_t cur, size_t tot, void *pa
|
||||
print_progress(pd);
|
||||
}
|
||||
|
||||
static int cred_acquire(git_cred **out,
|
||||
const char * UNUSED(url),
|
||||
const char * UNUSED(username_from_url),
|
||||
unsigned int UNUSED(allowed_types),
|
||||
void * UNUSED(payload))
|
||||
{
|
||||
char username[128] = {0};
|
||||
char password[128] = {0};
|
||||
|
||||
printf("Username: ");
|
||||
scanf("%s", username);
|
||||
|
||||
/* Yup. Right there on your terminal. Careful where you copy/paste output. */
|
||||
printf("Password: ");
|
||||
scanf("%s", password);
|
||||
|
||||
return git_cred_userpass_plaintext_new(out, username, password);
|
||||
}
|
||||
|
||||
int do_clone(git_repository *repo, int argc, char **argv)
|
||||
{
|
||||
@ -107,7 +76,7 @@ int do_clone(git_repository *repo, int argc, char **argv)
|
||||
clone_opts.checkout_opts = checkout_opts;
|
||||
clone_opts.fetch_progress_cb = &fetch_progress;
|
||||
clone_opts.fetch_progress_payload = &pd;
|
||||
clone_opts.cred_acquire_cb = cred_acquire;
|
||||
clone_opts.cred_acquire_cb = cred_acquire_cb;
|
||||
|
||||
// Do the clone
|
||||
error = git_clone(&cloned_repo, url, path, &clone_opts);
|
||||
|
34
examples/network/common.c
Normal file
34
examples/network/common.c
Normal file
@ -0,0 +1,34 @@
|
||||
#include "common.h"
|
||||
#include <stdio.h>
|
||||
|
||||
/* Shamelessly borrowed from http://stackoverflow.com/questions/3417837/
|
||||
* with permission of the original author, Martin Pool.
|
||||
* http://sourcefrog.net/weblog/software/languages/C/unused.html
|
||||
*/
|
||||
#ifdef UNUSED
|
||||
#elif defined(__GNUC__)
|
||||
# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
|
||||
#elif defined(__LCLINT__)
|
||||
# define UNUSED(x) /*@unused@*/ x
|
||||
#else
|
||||
# define UNUSED(x) x
|
||||
#endif
|
||||
|
||||
int cred_acquire_cb(git_cred **out,
|
||||
const char * UNUSED(url),
|
||||
const char * UNUSED(username_from_url),
|
||||
unsigned int UNUSED(allowed_types),
|
||||
void * UNUSED(payload))
|
||||
{
|
||||
char username[128] = {0};
|
||||
char password[128] = {0};
|
||||
|
||||
printf("Username: ");
|
||||
scanf("%s", username);
|
||||
|
||||
/* Yup. Right there on your terminal. Careful where you copy/paste output. */
|
||||
printf("Password: ");
|
||||
scanf("%s", password);
|
||||
|
||||
return git_cred_userpass_plaintext_new(out, username, password);
|
||||
}
|
@ -12,6 +12,12 @@ int fetch(git_repository *repo, int argc, char **argv);
|
||||
int index_pack(git_repository *repo, int argc, char **argv);
|
||||
int do_clone(git_repository *repo, int argc, char **argv);
|
||||
|
||||
int cred_acquire_cb(git_cred **out,
|
||||
const char * url,
|
||||
const char * username_from_url,
|
||||
unsigned int allowed_types,
|
||||
void *payload);
|
||||
|
||||
#ifndef PRIuZ
|
||||
/* Define the printf format specifer to use for size_t output */
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
|
@ -92,6 +92,7 @@ int fetch(git_repository *repo, int argc, char **argv)
|
||||
callbacks.update_tips = &update_cb;
|
||||
callbacks.progress = &progress_cb;
|
||||
git_remote_set_callbacks(remote, &callbacks);
|
||||
git_remote_set_cred_acquire_cb(remote, &cred_acquire_cb, NULL);
|
||||
|
||||
// Set up the information for the background worker thread
|
||||
data.remote = remote;
|
||||
|
@ -14,31 +14,6 @@ static int show_ref__cb(git_remote_head *head, void *payload)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int use_unnamed(git_repository *repo, const char *url)
|
||||
{
|
||||
git_remote *remote = NULL;
|
||||
int error;
|
||||
|
||||
// Create an instance of a remote from the URL. The transport to use
|
||||
// is detected from the URL
|
||||
error = git_remote_create_inmemory(&remote, repo, NULL, url);
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
// When connecting, the underlying code needs to know wether we
|
||||
// want to push or fetch
|
||||
error = git_remote_connect(remote, GIT_DIRECTION_FETCH);
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
// With git_remote_ls we can retrieve the advertised heads
|
||||
error = git_remote_ls(remote, &show_ref__cb, NULL);
|
||||
|
||||
cleanup:
|
||||
git_remote_free(remote);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int use_remote(git_repository *repo, char *name)
|
||||
{
|
||||
git_remote *remote = NULL;
|
||||
@ -46,8 +21,13 @@ static int use_remote(git_repository *repo, char *name)
|
||||
|
||||
// Find the remote by name
|
||||
error = git_remote_load(&remote, repo, name);
|
||||
if (error < 0) {
|
||||
error = git_remote_create_inmemory(&remote, repo, NULL, name);
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
git_remote_set_cred_acquire_cb(remote, &cred_acquire_cb, NULL);
|
||||
|
||||
error = git_remote_connect(remote, GIT_DIRECTION_FETCH);
|
||||
if (error < 0)
|
||||
@ -72,12 +52,7 @@ int ls_remote(git_repository *repo, int argc, char **argv)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* If there's a ':' in the name, assume it's an URL */
|
||||
if (strchr(argv[1], ':') != NULL) {
|
||||
error = use_unnamed(repo, argv[1]);
|
||||
} else {
|
||||
error = use_remote(repo, argv[1]);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
106
examples/rev-parse.c
Normal file
106
examples/rev-parse.c
Normal file
@ -0,0 +1,106 @@
|
||||
#include <stdio.h>
|
||||
#include <git2.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void check(int error, const char *message, const char *arg)
|
||||
{
|
||||
if (!error)
|
||||
return;
|
||||
if (arg)
|
||||
fprintf(stderr, "%s %s (%d)\n", message, arg, error);
|
||||
else
|
||||
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: rev-parse [ --option ] <args>...\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct parse_state {
|
||||
git_repository *repo;
|
||||
const char *repodir;
|
||||
int not;
|
||||
};
|
||||
|
||||
static int parse_revision(struct parse_state *ps, const char *revstr)
|
||||
{
|
||||
git_revspec rs;
|
||||
char str[GIT_OID_HEXSZ + 1];
|
||||
|
||||
if (!ps->repo) {
|
||||
if (!ps->repodir)
|
||||
ps->repodir = ".";
|
||||
check(git_repository_open_ext(&ps->repo, ps->repodir, 0, NULL),
|
||||
"Could not open repository from", ps->repodir);
|
||||
}
|
||||
|
||||
check(git_revparse(&rs, ps->repo, revstr), "Could not parse", revstr);
|
||||
|
||||
if ((rs.flags & GIT_REVPARSE_SINGLE) != 0) {
|
||||
git_oid_tostr(str, sizeof(str), git_object_id(rs.from));
|
||||
printf("%s\n", str);
|
||||
git_object_free(rs.from);
|
||||
}
|
||||
else if ((rs.flags & GIT_REVPARSE_RANGE) != 0) {
|
||||
git_oid_tostr(str, sizeof(str), git_object_id(rs.to));
|
||||
printf("%s\n", str);
|
||||
git_object_free(rs.to);
|
||||
|
||||
if ((rs.flags & GIT_REVPARSE_MERGE_BASE) != 0) {
|
||||
git_oid base;
|
||||
check(git_merge_base(&base, ps->repo,
|
||||
git_object_id(rs.from), git_object_id(rs.to)),
|
||||
"Could not find merge base", revstr);
|
||||
|
||||
git_oid_tostr(str, sizeof(str), &base);
|
||||
printf("%s\n", str);
|
||||
}
|
||||
|
||||
git_oid_tostr(str, sizeof(str), git_object_id(rs.from));
|
||||
printf("^%s\n", str);
|
||||
git_object_free(rs.from);
|
||||
}
|
||||
else {
|
||||
check(0, "Invalid results from git_revparse", revstr);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
char *a;
|
||||
struct parse_state ps;
|
||||
|
||||
git_threads_init();
|
||||
|
||||
memset(&ps, 0, sizeof(ps));
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
a = argv[i];
|
||||
|
||||
if (a[0] != '-') {
|
||||
if (parse_revision(&ps, a) != 0)
|
||||
break;
|
||||
} else if (!strcmp(a, "--not"))
|
||||
ps.not = !ps.not;
|
||||
else if (!strncmp(a, "--git-dir=", strlen("--git-dir=")))
|
||||
ps.repodir = a + strlen("--git-dir=");
|
||||
else
|
||||
usage("Cannot handle argument", a);
|
||||
}
|
||||
|
||||
git_repository_free(ps.repo);
|
||||
git_threads_shutdown();
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -56,6 +56,7 @@
|
||||
#include "git2/message.h"
|
||||
#include "git2/pack.h"
|
||||
#include "git2/stash.h"
|
||||
#include "git2/pathspec.h"
|
||||
#include "git2/blame.h"
|
||||
|
||||
#endif
|
||||
|
@ -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
|
||||
|
@ -183,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 */
|
||||
@ -234,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
|
||||
|
@ -67,6 +67,7 @@ typedef struct git_clone_options {
|
||||
unsigned int version;
|
||||
|
||||
git_checkout_opts checkout_opts;
|
||||
git_repository_init_options *init_options;
|
||||
int bare;
|
||||
git_transfer_progress_callback fetch_progress_cb;
|
||||
void *fetch_progress_payload;
|
||||
|
@ -24,17 +24,24 @@ GIT_BEGIN_DECL
|
||||
/**
|
||||
* Lookup a commit object from a repository.
|
||||
*
|
||||
* The returned object should be released with `git_commit_free` when no
|
||||
* longer needed.
|
||||
*
|
||||
* @param commit pointer to the looked up commit
|
||||
* @param repo the repo to use when locating the commit.
|
||||
* @param id identity of the commit to locate. If the object is
|
||||
* an annotated tag it will be peeled back to the commit.
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_commit_lookup(git_commit **commit, git_repository *repo, const git_oid *id);
|
||||
GIT_EXTERN(int) git_commit_lookup(
|
||||
git_commit **commit, git_repository *repo, const git_oid *id);
|
||||
|
||||
/**
|
||||
* Lookup a commit object from a repository,
|
||||
* given a prefix of its identifier (short id).
|
||||
* Lookup a commit object from a repository, given a prefix of its
|
||||
* identifier (short id).
|
||||
*
|
||||
* The returned object should be released with `git_commit_free` when no
|
||||
* longer needed.
|
||||
*
|
||||
* @see git_object_lookup_prefix
|
||||
*
|
||||
@ -45,7 +52,8 @@ GIT_EXTERN(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_EXTERN(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, size_t len);
|
||||
GIT_EXTERN(int) git_commit_lookup_prefix(
|
||||
git_commit **commit, git_repository *repo, const git_oid *id, size_t len);
|
||||
|
||||
/**
|
||||
* Close an open commit
|
||||
@ -129,6 +137,14 @@ GIT_EXTERN(const git_signature *) git_commit_committer(const git_commit *commit)
|
||||
*/
|
||||
GIT_EXTERN(const git_signature *) git_commit_author(const git_commit *commit);
|
||||
|
||||
/**
|
||||
* Get the full raw text of the commit header.
|
||||
*
|
||||
* @param commit a previously loaded commit
|
||||
* @return the header text of the commit
|
||||
*/
|
||||
GIT_EXTERN(const char *) git_commit_raw_header(const git_commit *commit);
|
||||
|
||||
/**
|
||||
* Get the tree pointed to by a commit.
|
||||
*
|
||||
@ -235,7 +251,7 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor(
|
||||
*
|
||||
* @param parent_count Number of parents for this commit
|
||||
*
|
||||
* @param parents[] Array of `parent_count` pointers to `git_commit`
|
||||
* @param parents Array of `parent_count` pointers to `git_commit`
|
||||
* objects that will be used as the parents for this commit. This
|
||||
* array may be NULL if `parent_count` is 0 (root commit). All the
|
||||
* given commits must be owned by the `repo`.
|
||||
|
@ -105,7 +105,8 @@ GIT_EXTERN(void) git_libgit2_version(int *major, int *minor, int *rev);
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_CAP_THREADS = ( 1 << 0 ),
|
||||
GIT_CAP_HTTPS = ( 1 << 1 )
|
||||
GIT_CAP_HTTPS = ( 1 << 1 ),
|
||||
GIT_CAP_SSH = ( 1 << 2 ),
|
||||
} git_cap_t;
|
||||
|
||||
/**
|
||||
|
@ -61,6 +61,7 @@ typedef struct {
|
||||
} git_config_entry;
|
||||
|
||||
typedef int (*git_config_foreach_cb)(const git_config_entry *, void *);
|
||||
typedef struct git_config_iterator git_config_iterator;
|
||||
|
||||
typedef enum {
|
||||
GIT_CVAR_FALSE = 0,
|
||||
@ -119,7 +120,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`.
|
||||
@ -327,7 +328,7 @@ GIT_EXTERN(int) git_config_get_bool(int *out, const git_config *cfg, const char
|
||||
GIT_EXTERN(int) git_config_get_string(const char **out, const git_config *cfg, const char *name);
|
||||
|
||||
/**
|
||||
* Get each value of a multivar.
|
||||
* Get each value of a multivar in a foreach callback
|
||||
*
|
||||
* The callback will be called on each variable found
|
||||
*
|
||||
@ -335,10 +336,37 @@ 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);
|
||||
GIT_EXTERN(int) git_config_get_multivar_foreach(const git_config *cfg, const char *name, const char *regexp, git_config_foreach_cb callback, void *payload);
|
||||
|
||||
/**
|
||||
* Get each value of a multivar
|
||||
*
|
||||
* @param out pointer to store the iterator
|
||||
* @param cfg where to look for the variable
|
||||
* @param name the variable's name
|
||||
* @param regexp regular expression to filter which variables we're
|
||||
* interested in. Use NULL to indicate all
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp);
|
||||
|
||||
/**
|
||||
* Return the current entry and advance the iterator
|
||||
*
|
||||
* @param entry pointer to store the entry
|
||||
* @param iter the iterator
|
||||
* @return 0 or an error code. GIT_ITEROVER if the iteration has completed
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_next(git_config_entry **entry, git_config_iterator *iter);
|
||||
|
||||
/**
|
||||
* Free a config iterator
|
||||
*
|
||||
* @param iter the iterator to free
|
||||
*/
|
||||
GIT_EXTERN(void) git_config_iterator_free(git_config_iterator *iter);
|
||||
|
||||
/**
|
||||
* Set the value of an integer config variable in the config file
|
||||
@ -424,6 +452,29 @@ GIT_EXTERN(int) git_config_foreach(
|
||||
git_config_foreach_cb callback,
|
||||
void *payload);
|
||||
|
||||
/**
|
||||
* Iterate over all the config variables
|
||||
*
|
||||
* Use `git_config_next` to advance the iteration and
|
||||
* `git_config_iterator_free` when done.
|
||||
*
|
||||
* @param out pointer to store the iterator
|
||||
* @param cfg where to ge the variables from
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_iterator_new(git_config_iterator **out, const git_config *cfg);
|
||||
|
||||
/**
|
||||
* Iterate over all the config variables whose name matches a pattern
|
||||
*
|
||||
* Use `git_config_next` to advance the iteration and
|
||||
* `git_config_iterator_free` when done.
|
||||
*
|
||||
* @param out pointer to store the iterator
|
||||
* @param cfg where to ge the variables from
|
||||
* @param regexp regular expression to match the names
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp);
|
||||
|
||||
/**
|
||||
* Perform an operation on each config variable matching a regular expression.
|
||||
*
|
||||
@ -535,6 +586,25 @@ GIT_EXTERN(int) git_config_parse_int32(int32_t *out, const char *value);
|
||||
GIT_EXTERN(int) git_config_parse_int64(int64_t *out, const char *value);
|
||||
|
||||
|
||||
/**
|
||||
* Perform an operation on each config variable in given config backend
|
||||
* matching a regular expression.
|
||||
*
|
||||
* This behaviors like `git_config_foreach_match` except instead of all config
|
||||
* entries it just enumerates through the given backend entry.
|
||||
*
|
||||
* @param backend where to get the variables from
|
||||
* @param regexp regular expression to match against config names (can be NULL)
|
||||
* @param callback the function to call on each variable
|
||||
* @param payload the data to pass to the callback
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_backend_foreach_match(
|
||||
git_config_backend *backend,
|
||||
const char *regexp,
|
||||
int (*fn)(const git_config_entry *, void *),
|
||||
void *data);
|
||||
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
@ -7,7 +7,7 @@
|
||||
#ifndef INCLUDE_git_cred_helpers_h__
|
||||
#define INCLUDE_git_cred_helpers_h__
|
||||
|
||||
#include "git2/transport.h"
|
||||
#include "transport.h"
|
||||
|
||||
/**
|
||||
* @file git2/cred_helpers.h
|
||||
|
@ -78,7 +78,7 @@ typedef enum {
|
||||
GIT_DIFF_IGNORE_WHITESPACE_CHANGE = (1 << 3),
|
||||
/** Ignore whitespace at end of line */
|
||||
GIT_DIFF_IGNORE_WHITESPACE_EOL = (1 << 4),
|
||||
/** Exclude submodules from the diff completely */
|
||||
/** Treat all submodules as unmodified */
|
||||
GIT_DIFF_IGNORE_SUBMODULES = (1 << 5),
|
||||
/** Use the "patience diff" algorithm (currently unimplemented) */
|
||||
GIT_DIFF_PATIENCE = (1 << 6),
|
||||
@ -314,6 +314,8 @@ typedef int (*git_diff_notify_cb)(
|
||||
* - `notify_cb` is an optional callback function, notifying the consumer of
|
||||
* which files are being examined as the diff is generated
|
||||
* - `notify_payload` is the payload data to pass to the `notify_cb` function
|
||||
* - `ignore_submodules` overrides the submodule ignore setting for all
|
||||
* submodules in the diff.
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int version; /**< version for the struct */
|
||||
@ -326,6 +328,7 @@ typedef struct {
|
||||
git_off_t max_size; /**< defaults to 512MB */
|
||||
git_diff_notify_cb notify_cb;
|
||||
void *notify_payload;
|
||||
git_submodule_ignore_t ignore_submodules; /** << submodule ignore rule */
|
||||
} git_diff_options;
|
||||
|
||||
#define GIT_DIFF_OPTIONS_VERSION 1
|
||||
@ -388,7 +391,7 @@ typedef enum {
|
||||
*/
|
||||
GIT_DIFF_LINE_FILE_HDR = 'F',
|
||||
GIT_DIFF_LINE_HUNK_HDR = 'H',
|
||||
GIT_DIFF_LINE_BINARY = 'B'
|
||||
GIT_DIFF_LINE_BINARY = 'B' /**< For "Binary files x and y differ" */
|
||||
} git_diff_line_t;
|
||||
|
||||
/**
|
||||
@ -451,6 +454,9 @@ typedef enum {
|
||||
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),
|
||||
|
||||
/** do not break rewrites unless they contribute to a rename */
|
||||
GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY = (1 << 15),
|
||||
} git_diff_find_t;
|
||||
|
||||
/**
|
||||
@ -747,7 +753,7 @@ GIT_EXTERN(int) git_diff_print_raw(
|
||||
* 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);
|
||||
@ -797,6 +803,14 @@ GIT_EXTERN(size_t) git_diff_num_deltas_of_type(
|
||||
git_diff_list *diff,
|
||||
git_delta_t type);
|
||||
|
||||
/**
|
||||
* Check if deltas are sorted case sensitively or insensitively.
|
||||
*
|
||||
* @param diff Diff list to check
|
||||
* @return 0 if case sensitive, 1 if case is ignored
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_is_sorted_icase(const git_diff_list *diff);
|
||||
|
||||
/**
|
||||
* Return the diff delta and patch for an entry in the diff list.
|
||||
*
|
||||
@ -918,7 +932,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(
|
||||
@ -931,6 +945,28 @@ GIT_EXTERN(int) git_diff_patch_get_line_in_hunk(
|
||||
size_t hunk_idx,
|
||||
size_t line_of_hunk);
|
||||
|
||||
/**
|
||||
* Look up size of patch diff data in bytes
|
||||
*
|
||||
* This returns the raw size of the patch data. This only includes the
|
||||
* actual data from the lines of the diff, not the file or hunk headers.
|
||||
*
|
||||
* If you pass `include_context` as true (non-zero), this will be the size
|
||||
* of all of the diff output; if you pass it as false (zero), this will
|
||||
* only include the actual changed lines (as if `context_lines` was 0).
|
||||
*
|
||||
* @param patch A git_diff_patch representing changes to one file
|
||||
* @param include_context Include context lines in size if non-zero
|
||||
* @param include_hunk_headers Include hunk header lines if non-zero
|
||||
* @param include_file_headers Include file header lines if non-zero
|
||||
* @return The number of bytes of data
|
||||
*/
|
||||
GIT_EXTERN(size_t) git_diff_patch_size(
|
||||
git_diff_patch *patch,
|
||||
int include_context,
|
||||
int include_hunk_headers,
|
||||
int include_file_headers);
|
||||
|
||||
/**
|
||||
* Serialize the patch to text via callback.
|
||||
*
|
||||
@ -983,7 +1019,9 @@ GIT_EXTERN(int) git_diff_patch_to_str(
|
||||
* `GIT_DIFF_FORCE_TEXT` of course).
|
||||
*
|
||||
* @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
|
||||
@ -993,7 +1031,9 @@ GIT_EXTERN(int) git_diff_patch_to_str(
|
||||
*/
|
||||
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,
|
||||
@ -1010,14 +1050,18 @@ GIT_EXTERN(int) git_diff_blobs(
|
||||
*
|
||||
* @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 options Options for diff, or NULL for default options
|
||||
* @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);
|
||||
|
||||
/**
|
||||
@ -1033,19 +1077,23 @@ GIT_EXTERN(int) git_diff_patch_from_blobs(
|
||||
* the reverse, with GIT_DELTA_REMOVED and blob content removed.
|
||||
*
|
||||
* @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 line_cb Callback for each line 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,
|
||||
@ -1062,16 +1110,20 @@ GIT_EXTERN(int) git_diff_blob_to_buffer(
|
||||
*
|
||||
* @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 options Options for diff, or NULL for default options
|
||||
* @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 *buf,
|
||||
size_t buflen,
|
||||
const char *old_as_path,
|
||||
const char *buffer,
|
||||
size_t buffer_len,
|
||||
const char *buffer_as_path,
|
||||
const git_diff_options *opts);
|
||||
|
||||
|
||||
|
@ -32,6 +32,7 @@ typedef enum {
|
||||
GIT_ENONFASTFORWARD = -11,
|
||||
GIT_EINVALIDSPEC = -12,
|
||||
GIT_EMERGECONFLICT = -13,
|
||||
GIT_ELOCKED = -14,
|
||||
|
||||
GIT_PASSTHROUGH = -30,
|
||||
GIT_ITEROVER = -31,
|
||||
@ -100,7 +101,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
|
||||
@ -125,6 +126,26 @@ typedef enum {
|
||||
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;
|
||||
|
||||
/**
|
||||
* Match any index stage.
|
||||
*
|
||||
* Some index APIs take a stage to match; pass this value to match
|
||||
* any entry matching the path regardless of stage.
|
||||
*/
|
||||
#define GIT_INDEX_STAGE_ANY -1
|
||||
|
||||
/** @name Index File Functions
|
||||
*
|
||||
* These functions work on the index file itself.
|
||||
@ -218,6 +239,14 @@ GIT_EXTERN(int) git_index_read(git_index *index);
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_write(git_index *index);
|
||||
|
||||
/**
|
||||
* Get the full path to the index file on disk.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @return path to index file or NULL for in-memory index
|
||||
*/
|
||||
GIT_EXTERN(const char *) git_index_path(git_index *index);
|
||||
|
||||
/**
|
||||
* Read a tree into the index file with stats
|
||||
*
|
||||
@ -420,6 +449,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.
|
||||
@ -531,7 +662,7 @@ GIT_EXTERN(int) git_index_conflict_next(
|
||||
/**
|
||||
* Frees a `git_index_conflict_iterator`.
|
||||
*
|
||||
* @param it pointer to the iterator
|
||||
* @param iterator pointer to the iterator
|
||||
*/
|
||||
GIT_EXTERN(void) git_index_conflict_iterator_free(
|
||||
git_index_conflict_iterator *iterator);
|
||||
|
@ -21,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,
|
||||
|
@ -7,11 +7,11 @@
|
||||
#ifndef INCLUDE_git_merge_h__
|
||||
#define INCLUDE_git_merge_h__
|
||||
|
||||
#include "git2/common.h"
|
||||
#include "git2/types.h"
|
||||
#include "git2/oid.h"
|
||||
#include "git2/checkout.h"
|
||||
#include "git2/index.h"
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
#include "oid.h"
|
||||
#include "checkout.h"
|
||||
#include "index.h"
|
||||
|
||||
/**
|
||||
* @file git2/merge.h
|
||||
@ -141,7 +141,7 @@ GIT_EXTERN(int) git_merge_head_from_oid(
|
||||
/**
|
||||
* Frees a `git_merge_head`
|
||||
*
|
||||
* @param the merge head to free
|
||||
* @param head merge head to free
|
||||
*/
|
||||
GIT_EXTERN(void) git_merge_head_free(
|
||||
git_merge_head *head);
|
||||
|
@ -99,7 +99,7 @@ GIT_EXTERN(int) git_note_read(
|
||||
/**
|
||||
* Get the note message
|
||||
*
|
||||
* @param note
|
||||
* @param note the note
|
||||
* @return the note message
|
||||
*/
|
||||
GIT_EXTERN(const char *) git_note_message(const git_note *note);
|
||||
@ -108,7 +108,7 @@ GIT_EXTERN(const char *) git_note_message(const git_note *note);
|
||||
/**
|
||||
* Get the note object OID
|
||||
*
|
||||
* @param note
|
||||
* @param note the note
|
||||
* @return the note object OID
|
||||
*/
|
||||
GIT_EXTERN(const git_oid *) git_note_oid(const git_note *note);
|
||||
|
@ -36,7 +36,7 @@ GIT_BEGIN_DECL
|
||||
* @param repo the repository to look up the object
|
||||
* @param id the unique identifier for the object
|
||||
* @param type the type of the object
|
||||
* @return a reference to the object
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_object_lookup(
|
||||
git_object **object,
|
||||
|
@ -219,18 +219,12 @@ GIT_EXTERN(int) git_odb_write(git_oid *out, git_odb *odb, const void *data, size
|
||||
* The type and final length of the object must be specified
|
||||
* when opening the stream.
|
||||
*
|
||||
* The returned stream will be of type `GIT_STREAM_WRONLY` and
|
||||
* will have the following methods:
|
||||
* The returned stream will be of type `GIT_STREAM_WRONLY`, and it
|
||||
* won't be effective until `git_odb_stream_finalize_write` is called
|
||||
* and returns without an error
|
||||
*
|
||||
* - stream->write: write `n` bytes into the stream
|
||||
* - stream->finalize_write: close the stream and store the object in
|
||||
* the odb
|
||||
* - stream->free: free the stream
|
||||
*
|
||||
* The streaming write won't be effective until `stream->finalize_write`
|
||||
* is called and returns without an error
|
||||
*
|
||||
* The stream must always be free'd or will leak memory.
|
||||
* The stream must always be freed when done with `git_odb_stream_free` or
|
||||
* will leak memory.
|
||||
*
|
||||
* @see git_odb_stream
|
||||
*
|
||||
@ -242,6 +236,48 @@ GIT_EXTERN(int) git_odb_write(git_oid *out, git_odb *odb, const void *data, size
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_open_wstream(git_odb_stream **out, git_odb *db, size_t size, git_otype type);
|
||||
|
||||
/**
|
||||
* Write to an odb stream
|
||||
*
|
||||
* This method will fail if the total number of received bytes exceeds the
|
||||
* size declared with `git_odb_open_wstream()`
|
||||
*
|
||||
* @param stream the stream
|
||||
* @param buffer the data to write
|
||||
* @param len the buffer's length
|
||||
* @return 0 if the write succeeded; error code otherwise
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_stream_write(git_odb_stream *stream, const char *buffer, size_t len);
|
||||
|
||||
/**
|
||||
* Finish writing to an odb stream
|
||||
*
|
||||
* The object will take its final name and will be available to the
|
||||
* odb.
|
||||
*
|
||||
* This method will fail if the total number of received bytes
|
||||
* differs from the size declared with `git_odb_open_wstream()`
|
||||
*
|
||||
* @param out pointer to store the resulting object's id
|
||||
* @param stream the stream
|
||||
* @return 0 on success; an error code otherwise
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream);
|
||||
|
||||
/**
|
||||
* Read from an odb stream
|
||||
*
|
||||
* Most backends don't implement streaming reads
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_stream_read(git_odb_stream *stream, char *buffer, size_t len);
|
||||
|
||||
/**
|
||||
* Free an odb stream
|
||||
*
|
||||
* @param stream the stream to free
|
||||
*/
|
||||
GIT_EXTERN(void) git_odb_stream_free(git_odb_stream *stream);
|
||||
|
||||
/**
|
||||
* Open a stream to read an object from the ODB
|
||||
*
|
||||
|
@ -7,8 +7,8 @@
|
||||
#ifndef INCLUDE_git_odb_backend_h__
|
||||
#define INCLUDE_git_odb_backend_h__
|
||||
|
||||
#include "git2/common.h"
|
||||
#include "git2/types.h"
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
|
||||
/**
|
||||
* @file git2/backend.h
|
||||
@ -65,14 +65,50 @@ typedef enum {
|
||||
GIT_STREAM_RW = (GIT_STREAM_RDONLY | GIT_STREAM_WRONLY),
|
||||
} git_odb_stream_t;
|
||||
|
||||
/** A stream to read/write from a backend */
|
||||
/**
|
||||
* A stream to read/write from a backend.
|
||||
*
|
||||
* This represents a stream of data being written to or read from a
|
||||
* backend. When writing, the frontend functions take care of
|
||||
* calculating the object's id and all `finalize_write` needs to do is
|
||||
* store the object with the id it is passed.
|
||||
*/
|
||||
struct git_odb_stream {
|
||||
git_odb_backend *backend;
|
||||
unsigned int mode;
|
||||
void *hash_ctx;
|
||||
|
||||
size_t declared_size;
|
||||
size_t received_bytes;
|
||||
|
||||
/**
|
||||
* Write at most `len` bytes into `buffer` and advance the stream.
|
||||
*/
|
||||
int (*read)(git_odb_stream *stream, char *buffer, size_t len);
|
||||
|
||||
/**
|
||||
* Write `len` bytes from `buffer` into the stream.
|
||||
*/
|
||||
int (*write)(git_odb_stream *stream, const char *buffer, size_t len);
|
||||
int (*finalize_write)(git_oid *oid_p, git_odb_stream *stream);
|
||||
|
||||
/**
|
||||
* Store the contents of the stream as an object with the id
|
||||
* specified in `oid`.
|
||||
*
|
||||
* This method might not be invoked if:
|
||||
* - an error occurs earlier with the `write` callback,
|
||||
* - the object referred to by `oid` already exists in any backend, or
|
||||
* - the final number of received bytes differs from the size declared
|
||||
* with `git_odb_open_wstream()`
|
||||
*/
|
||||
int (*finalize_write)(git_odb_stream *stream, const git_oid *oid);
|
||||
|
||||
/**
|
||||
* Free the stream's memory.
|
||||
*
|
||||
* This method might be called without a call to `finalize_write` if
|
||||
* an error occurs or if the object is already present in the ODB.
|
||||
*/
|
||||
void (*free)(git_odb_stream *stream);
|
||||
};
|
||||
|
||||
|
@ -85,7 +85,7 @@ 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);
|
||||
|
||||
@ -96,7 +96,7 @@ GIT_EXTERN(void) git_oid_fmt(char *out, const git_oid *id);
|
||||
* 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 oid oid structure to format.
|
||||
* @param id oid structure to format.
|
||||
*/
|
||||
GIT_EXTERN(void) git_oid_nfmt(char *out, size_t n, const git_oid *id);
|
||||
|
||||
@ -118,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().
|
||||
*/
|
||||
@ -188,8 +188,7 @@ GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, size_t len);
|
||||
*
|
||||
* @param id oid structure.
|
||||
* @param str input hex string of an object id.
|
||||
* @return GIT_ENOTOID if str is not a valid hex string,
|
||||
* 0 in case of a match, GIT_ERROR otherwise.
|
||||
* @return 0 in case of a match, -1 otherwise.
|
||||
*/
|
||||
GIT_EXTERN(int) git_oid_streq(const git_oid *id, const char *str);
|
||||
|
||||
@ -241,13 +240,13 @@ GIT_EXTERN(git_oid_shorten *) git_oid_shorten_new(size_t min_length);
|
||||
* or freed.
|
||||
*
|
||||
* For performance reasons, there is a hard-limit of how many
|
||||
* OIDs can be added to a single set (around ~22000, assuming
|
||||
* OIDs can be added to a single set (around ~32000, assuming
|
||||
* a mostly randomized distribution), which should be enough
|
||||
* for any kind of program, and keeps the algorithm fast and
|
||||
* memory-efficient.
|
||||
*
|
||||
* Attempting to add more than those OIDs will result in a
|
||||
* GIT_ENOMEM error
|
||||
* GITERR_INVALID error
|
||||
*
|
||||
* @param os a `git_oid_shorten` instance
|
||||
* @param text_id an OID in text form
|
||||
|
@ -112,7 +112,7 @@ GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid
|
||||
* @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_payload payload for the progress callback (optional)
|
||||
* @param progress_cb_payload payload for the progress callback (optional)
|
||||
*
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
@ -137,7 +137,7 @@ GIT_EXTERN(int) git_packbuilder_foreach(git_packbuilder *pb, git_packbuilder_for
|
||||
* Get the total number of objects the packbuilder will write out
|
||||
*
|
||||
* @param pb the packbuilder
|
||||
* @return
|
||||
* @return the number of objects in the packfile
|
||||
*/
|
||||
GIT_EXTERN(uint32_t) git_packbuilder_object_count(git_packbuilder *pb);
|
||||
|
||||
@ -145,7 +145,7 @@ GIT_EXTERN(uint32_t) git_packbuilder_object_count(git_packbuilder *pb);
|
||||
* Get the number of objects the packbuilder has already written out
|
||||
*
|
||||
* @param pb the packbuilder
|
||||
* @return
|
||||
* @return the number of objects which have already been written
|
||||
*/
|
||||
GIT_EXTERN(uint32_t) git_packbuilder_written(git_packbuilder *pb);
|
||||
|
||||
|
260
include/git2/pathspec.h
Normal file
260
include/git2/pathspec.h
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
* 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_pathspec_h__
|
||||
#define INCLUDE_git_pathspec_h__
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
#include "strarray.h"
|
||||
#include "diff.h"
|
||||
|
||||
/**
|
||||
* Compiled pathspec
|
||||
*/
|
||||
typedef struct git_pathspec git_pathspec;
|
||||
|
||||
/**
|
||||
* List of filenames matching a pathspec
|
||||
*/
|
||||
typedef struct git_pathspec_match_list git_pathspec_match_list;
|
||||
|
||||
/**
|
||||
* Options controlling how pathspec match should be executed
|
||||
*
|
||||
* - GIT_PATHSPEC_IGNORE_CASE forces match to ignore case; otherwise
|
||||
* match will use native case sensitivity of platform filesystem
|
||||
* - GIT_PATHSPEC_USE_CASE forces case sensitive match; otherwise
|
||||
* match will use native case sensitivity of platform filesystem
|
||||
* - GIT_PATHSPEC_NO_GLOB disables glob patterns and just uses simple
|
||||
* string comparison for matching
|
||||
* - GIT_PATHSPEC_NO_MATCH_ERROR means the match functions return error
|
||||
* code GIT_ENOTFOUND if no matches are found; otherwise no matches is
|
||||
* still success (return 0) but `git_pathspec_match_list_entrycount`
|
||||
* will indicate 0 matches.
|
||||
* - GIT_PATHSPEC_FIND_FAILURES means that the `git_pathspec_match_list`
|
||||
* should track which patterns matched which files so that at the end of
|
||||
* the match we can identify patterns that did not match any files.
|
||||
* - GIT_PATHSPEC_FAILURES_ONLY means that the `git_pathspec_match_list`
|
||||
* does not need to keep the actual matching filenames. Use this to
|
||||
* just test if there were any matches at all or in combination with
|
||||
* GIT_PATHSPEC_FIND_FAILURES to validate a pathspec.
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_PATHSPEC_DEFAULT = 0,
|
||||
GIT_PATHSPEC_IGNORE_CASE = (1u << 0),
|
||||
GIT_PATHSPEC_USE_CASE = (1u << 1),
|
||||
GIT_PATHSPEC_NO_GLOB = (1u << 2),
|
||||
GIT_PATHSPEC_NO_MATCH_ERROR = (1u << 3),
|
||||
GIT_PATHSPEC_FIND_FAILURES = (1u << 4),
|
||||
GIT_PATHSPEC_FAILURES_ONLY = (1u << 5),
|
||||
} git_pathspec_flag_t;
|
||||
|
||||
/**
|
||||
* Compile a pathspec
|
||||
*
|
||||
* @param out Output of the compiled pathspec
|
||||
* @param pathspec A git_strarray of the paths to match
|
||||
* @return 0 on success, <0 on failure
|
||||
*/
|
||||
GIT_EXTERN(int) git_pathspec_new(
|
||||
git_pathspec **out, const git_strarray *pathspec);
|
||||
|
||||
/**
|
||||
* Free a pathspec
|
||||
*
|
||||
* @param ps The compiled pathspec
|
||||
*/
|
||||
GIT_EXTERN(void) git_pathspec_free(git_pathspec *ps);
|
||||
|
||||
/**
|
||||
* Try to match a path against a pathspec
|
||||
*
|
||||
* Unlike most of the other pathspec matching functions, this will not
|
||||
* fall back on the native case-sensitivity for your platform. You must
|
||||
* explicitly pass flags to control case sensitivity or else this will
|
||||
* fall back on being case sensitive.
|
||||
*
|
||||
* @param ps The compiled pathspec
|
||||
* @param flags Combination of git_pathspec_flag_t options to control match
|
||||
* @param path The pathname to attempt to match
|
||||
* @return 1 is path matches spec, 0 if it does not
|
||||
*/
|
||||
GIT_EXTERN(int) git_pathspec_matches_path(
|
||||
const git_pathspec *ps, uint32_t flags, const char *path);
|
||||
|
||||
/**
|
||||
* Match a pathspec against the working directory of a repository.
|
||||
*
|
||||
* This matches the pathspec against the current files in the working
|
||||
* directory of the repository. It is an error to invoke this on a bare
|
||||
* repo. This handles git ignores (i.e. ignored files will not be
|
||||
* considered to match the `pathspec` unless the file is tracked in the
|
||||
* index).
|
||||
*
|
||||
* If `out` is not NULL, this returns a `git_patchspec_match_list`. That
|
||||
* contains the list of all matched filenames (unless you pass the
|
||||
* `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
|
||||
* pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
|
||||
* flag). You must call `git_pathspec_match_list_free()` on this object.
|
||||
*
|
||||
* @param out Output list of matches; pass NULL to just get return value
|
||||
* @param repo The repository in which to match; bare repo is an error
|
||||
* @param flags Combination of git_pathspec_flag_t options to control match
|
||||
* @param ps Pathspec to be matched
|
||||
* @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
|
||||
* the GIT_PATHSPEC_NO_MATCH_ERROR flag was given
|
||||
*/
|
||||
GIT_EXTERN(int) git_pathspec_match_workdir(
|
||||
git_pathspec_match_list **out,
|
||||
git_repository *repo,
|
||||
uint32_t flags,
|
||||
git_pathspec *ps);
|
||||
|
||||
/**
|
||||
* Match a pathspec against entries in an index.
|
||||
*
|
||||
* This matches the pathspec against the files in the repository index.
|
||||
*
|
||||
* NOTE: At the moment, the case sensitivity of this match is controlled
|
||||
* by the current case-sensitivity of the index object itself and the
|
||||
* USE_CASE and IGNORE_CASE flags will have no effect. This behavior will
|
||||
* be corrected in a future release.
|
||||
*
|
||||
* If `out` is not NULL, this returns a `git_patchspec_match_list`. That
|
||||
* contains the list of all matched filenames (unless you pass the
|
||||
* `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
|
||||
* pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
|
||||
* flag). You must call `git_pathspec_match_list_free()` on this object.
|
||||
*
|
||||
* @param out Output list of matches; pass NULL to just get return value
|
||||
* @param index The index to match against
|
||||
* @param flags Combination of git_pathspec_flag_t options to control match
|
||||
* @param ps Pathspec to be matched
|
||||
* @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
|
||||
* the GIT_PATHSPEC_NO_MATCH_ERROR flag is used
|
||||
*/
|
||||
GIT_EXTERN(int) git_pathspec_match_index(
|
||||
git_pathspec_match_list **out,
|
||||
git_index *index,
|
||||
uint32_t flags,
|
||||
git_pathspec *ps);
|
||||
|
||||
/**
|
||||
* Match a pathspec against files in a tree.
|
||||
*
|
||||
* This matches the pathspec against the files in the given tree.
|
||||
*
|
||||
* If `out` is not NULL, this returns a `git_patchspec_match_list`. That
|
||||
* contains the list of all matched filenames (unless you pass the
|
||||
* `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
|
||||
* pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
|
||||
* flag). You must call `git_pathspec_match_list_free()` on this object.
|
||||
*
|
||||
* @param out Output list of matches; pass NULL to just get return value
|
||||
* @param tree The root-level tree to match against
|
||||
* @param flags Combination of git_pathspec_flag_t options to control match
|
||||
* @param ps Pathspec to be matched
|
||||
* @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
|
||||
* the GIT_PATHSPEC_NO_MATCH_ERROR flag is used
|
||||
*/
|
||||
GIT_EXTERN(int) git_pathspec_match_tree(
|
||||
git_pathspec_match_list **out,
|
||||
git_tree *tree,
|
||||
uint32_t flags,
|
||||
git_pathspec *ps);
|
||||
|
||||
/**
|
||||
* Match a pathspec against files in a diff list.
|
||||
*
|
||||
* This matches the pathspec against the files in the given diff list.
|
||||
*
|
||||
* If `out` is not NULL, this returns a `git_patchspec_match_list`. That
|
||||
* contains the list of all matched filenames (unless you pass the
|
||||
* `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
|
||||
* pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
|
||||
* flag). You must call `git_pathspec_match_list_free()` on this object.
|
||||
*
|
||||
* @param out Output list of matches; pass NULL to just get return value
|
||||
* @param diff A generated diff list
|
||||
* @param flags Combination of git_pathspec_flag_t options to control match
|
||||
* @param ps Pathspec to be matched
|
||||
* @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
|
||||
* the GIT_PATHSPEC_NO_MATCH_ERROR flag is used
|
||||
*/
|
||||
GIT_EXTERN(int) git_pathspec_match_diff(
|
||||
git_pathspec_match_list **out,
|
||||
git_diff_list *diff,
|
||||
uint32_t flags,
|
||||
git_pathspec *ps);
|
||||
|
||||
/**
|
||||
* Free memory associates with a git_pathspec_match_list
|
||||
*
|
||||
* @param m The git_pathspec_match_list to be freed
|
||||
*/
|
||||
GIT_EXTERN(void) git_pathspec_match_list_free(git_pathspec_match_list *m);
|
||||
|
||||
/**
|
||||
* Get the number of items in a match list.
|
||||
*
|
||||
* @param m The git_pathspec_match_list object
|
||||
* @return Number of items in match list
|
||||
*/
|
||||
GIT_EXTERN(size_t) git_pathspec_match_list_entrycount(
|
||||
const git_pathspec_match_list *m);
|
||||
|
||||
/**
|
||||
* Get a matching filename by position.
|
||||
*
|
||||
* This routine cannot be used if the match list was generated by
|
||||
* `git_pathspec_match_diff`. If so, it will always return NULL.
|
||||
*
|
||||
* @param m The git_pathspec_match_list object
|
||||
* @param pos The index into the list
|
||||
* @return The filename of the match
|
||||
*/
|
||||
GIT_EXTERN(const char *) git_pathspec_match_list_entry(
|
||||
const git_pathspec_match_list *m, size_t pos);
|
||||
|
||||
/**
|
||||
* Get a matching diff delta by position.
|
||||
*
|
||||
* This routine can only be used if the match list was generated by
|
||||
* `git_pathspec_match_diff`. Otherwise it will always return NULL.
|
||||
*
|
||||
* @param m The git_pathspec_match_list object
|
||||
* @param pos The index into the list
|
||||
* @return The filename of the match
|
||||
*/
|
||||
GIT_EXTERN(const git_diff_delta *) git_pathspec_match_list_diff_entry(
|
||||
const git_pathspec_match_list *m, size_t pos);
|
||||
|
||||
/**
|
||||
* Get the number of pathspec items that did not match.
|
||||
*
|
||||
* This will be zero unless you passed GIT_PATHSPEC_FIND_FAILURES when
|
||||
* generating the git_pathspec_match_list.
|
||||
*
|
||||
* @param m The git_pathspec_match_list object
|
||||
* @return Number of items in original pathspec that had no matches
|
||||
*/
|
||||
GIT_EXTERN(size_t) git_pathspec_match_list_failed_entrycount(
|
||||
const git_pathspec_match_list *m);
|
||||
|
||||
/**
|
||||
* Get an original pathspec string that had no matches.
|
||||
*
|
||||
* This will be return NULL for positions out of range.
|
||||
*
|
||||
* @param m The git_pathspec_match_list object
|
||||
* @param pos The index into the failed items
|
||||
* @return The pathspec pattern that didn't match anything
|
||||
*/
|
||||
GIT_EXTERN(const char *) git_pathspec_match_list_failed_entry(
|
||||
const git_pathspec_match_list *m, size_t pos);
|
||||
|
||||
#endif
|
@ -98,7 +98,7 @@ GIT_EXTERN(int) git_push_finish(git_push *push);
|
||||
*
|
||||
* @param push The push object
|
||||
*
|
||||
* @return true if equal, false otherwise
|
||||
* @return true if remote side successfully unpacked, false otherwise
|
||||
*/
|
||||
GIT_EXTERN(int) git_push_unpack_ok(git_push *push);
|
||||
|
||||
|
@ -32,7 +32,7 @@ GIT_BEGIN_DECL
|
||||
* @param out pointer to the looked-up reference
|
||||
* @param repo the repository to look up the reference
|
||||
* @param name the long name for the reference (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0, ...)
|
||||
* @return 0 on success, ENOTFOUND, EINVALIDSPEC or an error code.
|
||||
* @return 0 on success, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code.
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_lookup(git_reference **out, git_repository *repo, const char *name);
|
||||
|
||||
@ -49,7 +49,7 @@ 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 (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0, ...)
|
||||
* @return 0 on success, ENOTFOUND, EINVALIDSPEC or an error code.
|
||||
* @return 0 on success, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code.
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_name_to_id(
|
||||
git_oid *out, git_repository *repo, const char *name);
|
||||
@ -62,7 +62,7 @@ GIT_EXTERN(int) git_reference_name_to_id(
|
||||
*
|
||||
* @param out pointer in which to store the reference
|
||||
* @param repo the repository in which to look
|
||||
* @param shrothand the short name for the reference
|
||||
* @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);
|
||||
@ -94,7 +94,7 @@ GIT_EXTERN(int) git_reference_dwim(git_reference **out, git_repository *repo, co
|
||||
* @param name The name of the reference
|
||||
* @param target The target of the reference
|
||||
* @param force Overwrite existing references
|
||||
* @return 0 on success, EEXISTS, EINVALIDSPEC or an error code
|
||||
* @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repository *repo, const char *name, const char *target, int force);
|
||||
|
||||
@ -126,7 +126,7 @@ GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repositor
|
||||
* @param name The name of the reference
|
||||
* @param id The object id pointed to by the reference.
|
||||
* @param force Overwrite existing references
|
||||
* @return 0 on success, EEXISTS, EINVALIDSPEC or an error code
|
||||
* @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force);
|
||||
|
||||
@ -198,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
|
||||
*/
|
||||
@ -225,7 +225,7 @@ GIT_EXTERN(git_repository *) git_reference_owner(const git_reference *ref);
|
||||
* @param out Pointer to the newly created reference
|
||||
* @param ref The reference
|
||||
* @param target The new target for the reference
|
||||
* @return 0 on success, EINVALIDSPEC or an error code
|
||||
* @return 0 on success, GIT_EINVALIDSPEC or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_symbolic_set_target(
|
||||
git_reference **out,
|
||||
@ -266,9 +266,9 @@ 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
|
||||
* @return 0 on success, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
|
||||
*
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_rename(
|
||||
@ -375,7 +375,7 @@ GIT_EXTERN(int) git_reference_iterator_glob_new(
|
||||
*
|
||||
* @param out pointer in which to store the reference
|
||||
* @param iter the iterator
|
||||
* @param 0, GIT_ITEROVER if there are no more; or an error code
|
||||
* @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);
|
||||
|
||||
@ -442,6 +442,15 @@ GIT_EXTERN(int) git_reference_is_branch(git_reference *ref);
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_is_remote(git_reference *ref);
|
||||
|
||||
/**
|
||||
* Check if a reference is a tag
|
||||
*
|
||||
* @param ref A git reference
|
||||
*
|
||||
* @return 1 when the reference lives in the refs/tags
|
||||
* namespace; 0 otherwise.
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_is_tag(git_reference *ref);
|
||||
|
||||
typedef enum {
|
||||
GIT_REF_FORMAT_NORMAL = 0,
|
||||
@ -488,7 +497,7 @@ typedef enum {
|
||||
* @param name Reference name to be checked.
|
||||
* @param flags Flags to constrain name validation rules - see the
|
||||
* GIT_REF_FORMAT constants above.
|
||||
* @return 0 on success, GIT_EBUFS if buffer is too small, EINVALIDSPEC
|
||||
* @return 0 on success, GIT_EBUFS if buffer is too small, GIT_EINVALIDSPEC
|
||||
* or an error code.
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_normalize_name(
|
||||
@ -506,9 +515,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
|
||||
*/
|
||||
|
@ -55,7 +55,7 @@ GIT_EXTERN(int) git_refspec_force(const git_refspec *refspec);
|
||||
/**
|
||||
* Get the refspec's direction.
|
||||
*
|
||||
* @param the refspec
|
||||
* @param spec refspec
|
||||
* @return GIT_DIRECTION_FETCH or GIT_DIRECTION_PUSH
|
||||
*/
|
||||
GIT_EXTERN(git_direction) git_refspec_direction(const git_refspec *spec);
|
||||
|
@ -25,13 +25,6 @@
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
typedef int (*git_remote_rename_problem_cb)(const char *problematic_refspec, void *payload);
|
||||
/*
|
||||
* TODO: This functions still need to be implemented:
|
||||
* - _listcb/_foreach
|
||||
* - _add
|
||||
* - _rename
|
||||
* - _del (needs support from config)
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add a remote with the default fetch refspec to the repository's configuration. This
|
||||
@ -95,6 +88,14 @@ GIT_EXTERN(int) git_remote_load(git_remote **out, git_repository *repo, const ch
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_save(const git_remote *remote);
|
||||
|
||||
/**
|
||||
* Get the remote's repository
|
||||
*
|
||||
* @param remote the remote
|
||||
* @return a pointer to the repository
|
||||
*/
|
||||
GIT_EXTERN(git_repository *) git_remote_owner(const git_remote *remote);
|
||||
|
||||
/**
|
||||
* Get the remote's name
|
||||
*
|
||||
@ -247,19 +248,20 @@ GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction);
|
||||
GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload);
|
||||
|
||||
/**
|
||||
* Download the packfile
|
||||
* Download and index the packfile
|
||||
*
|
||||
* Negotiate what objects should be downloaded and download the
|
||||
* packfile with those objects. The packfile is downloaded with a
|
||||
* temporary filename, as it's final name is not known yet. If there
|
||||
* was no packfile needed (all the objects were available locally),
|
||||
* filename will be NULL and the function will return success.
|
||||
* Connect to the remote if it hasn't been done yet, negotiate with
|
||||
* the remote git which objects are missing, download and index the
|
||||
* packfile.
|
||||
*
|
||||
* The .idx file will be created and both it and the packfile with be
|
||||
* renamed to their final name.
|
||||
*
|
||||
* @param remote the remote to download from
|
||||
* @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(
|
||||
@ -320,7 +322,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);
|
||||
|
||||
|
@ -94,10 +94,14 @@ GIT_EXTERN(int) git_repository_discover(
|
||||
* changes from the `stat` system call). (E.g. Searching in a user's home
|
||||
* directory "/home/user/source/" will not return "/.git/" as the found
|
||||
* repo if "/" is a different filesystem than "/home".)
|
||||
* * GIT_REPOSITORY_OPEN_BARE - Open repository as a bare repo regardless
|
||||
* of core.bare config, and defer loading config file for faster setup.
|
||||
* Unlike `git_repository_open_bare`, this can follow gitlinks.
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_REPOSITORY_OPEN_NO_SEARCH = (1 << 0),
|
||||
GIT_REPOSITORY_OPEN_CROSS_FS = (1 << 1),
|
||||
GIT_REPOSITORY_OPEN_BARE = (1 << 2),
|
||||
} git_repository_open_flag_t;
|
||||
|
||||
/**
|
||||
@ -178,7 +182,7 @@ GIT_EXTERN(int) git_repository_init(
|
||||
* when initializing a new repo. Details of individual values are:
|
||||
*
|
||||
* * BARE - Create a bare repository with no working directory.
|
||||
* * NO_REINIT - Return an EEXISTS error if the repo_path appears to
|
||||
* * NO_REINIT - Return an GIT_EEXISTS error if the repo_path appears to
|
||||
* already be an git repository.
|
||||
* * NO_DOTGIT_DIR - Normally a "/.git/" will be appended to the repo
|
||||
* path for non-bare repos (if it is not already there), but
|
||||
@ -471,7 +475,7 @@ GIT_EXTERN(int) git_repository_index(git_index **out, git_repository *repo);
|
||||
* @param out Buffer to write data into or NULL to just read required size
|
||||
* @param len Length of `out` buffer in bytes
|
||||
* @param repo Repository to read prepared message from
|
||||
* @return GIT_ENOUTFOUND if no message exists, other value < 0 for other
|
||||
* @return GIT_ENOTFOUND 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);
|
||||
@ -519,7 +523,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,
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
|
||||
|
||||
/**
|
||||
* @file git2/revparse.h
|
||||
* @brief Git revision parsing routines
|
||||
@ -21,27 +20,37 @@
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
* The returned object should be released with `git_object_free` when no
|
||||
* longer needed.
|
||||
*
|
||||
* @param out pointer to output object
|
||||
* @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_single(git_object **out, git_repository *repo, const char *spec);
|
||||
GIT_EXTERN(int) git_revparse_single(
|
||||
git_object **out, git_repository *repo, const char *spec);
|
||||
|
||||
/**
|
||||
* Find a single object, as specified by a revision string.
|
||||
* See `man gitrevisions`,
|
||||
* or http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
|
||||
* Find a single object and intermediate reference 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.
|
||||
*
|
||||
* The returned object should be released with `git_object_free` and the
|
||||
* returned reference with `git_reference_free` when no longer needed.
|
||||
*
|
||||
* @param object_out pointer to output object
|
||||
* @param reference_out pointer to output reference or NULL
|
||||
* @param repo the repository to search in
|
||||
@ -76,17 +85,19 @@ typedef struct {
|
||||
git_object *from;
|
||||
/** The right element of the revspec; must be freed by the user */
|
||||
git_object *to;
|
||||
/** The intent of the revspec */
|
||||
/** The intent of the revspec (i.e. `git_revparse_mode_t` flags) */
|
||||
unsigned int flags;
|
||||
} git_revspec;
|
||||
|
||||
/**
|
||||
* Parse a revision string for `from`, `to`, and intent. See `man gitrevisions` or
|
||||
* http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for information
|
||||
* on the syntax accepted.
|
||||
* Parse a revision string for `from`, `to`, and intent.
|
||||
*
|
||||
* @param revspec Pointer to an user-allocated git_revspec struct where the result
|
||||
* of the rev-parse will be stored
|
||||
* See `man gitrevisions` or
|
||||
* http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
|
||||
* information on the syntax accepted.
|
||||
*
|
||||
* @param revspec Pointer to an user-allocated git_revspec struct where
|
||||
* the result of the rev-parse will be stored
|
||||
* @param repo the repository to search in
|
||||
* @param spec the rev-parse spec to parse
|
||||
* @return 0 on success, GIT_INVALIDSPEC, GIT_ENOTFOUND, GIT_EAMBIGUOUS or an error code
|
||||
|
@ -231,6 +231,14 @@ GIT_EXTERN(void) git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode);
|
||||
*/
|
||||
GIT_EXTERN(int) git_revwalk_push_range(git_revwalk *walk, const char *range);
|
||||
|
||||
/**
|
||||
* Simplify the history by first-parent
|
||||
*
|
||||
* No parents other than the first for each commit will be enqueued.
|
||||
*/
|
||||
GIT_EXTERN(void) git_revwalk_simplify_first_parent(git_revwalk *walk);
|
||||
|
||||
|
||||
/**
|
||||
* Free a revision walker previously allocated.
|
||||
*
|
||||
|
@ -48,6 +48,19 @@ GIT_EXTERN(int) git_signature_new(git_signature **out, const char *name, const c
|
||||
*/
|
||||
GIT_EXTERN(int) git_signature_now(git_signature **out, const char *name, const char *email);
|
||||
|
||||
/**
|
||||
* Create a new action signature with default user and now timestamp.
|
||||
*
|
||||
* This looks up the user.name and user.email from the configuration and
|
||||
* uses the current time as the timestamp, and creates a new signature
|
||||
* based on that information. It will return GIT_ENOTFOUND if either the
|
||||
* user.name or user.email are not set.
|
||||
*
|
||||
* @param out new signature
|
||||
* @param repo repository pointer
|
||||
* @return 0 on success, GIT_ENOTFOUND if config is missing, or error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_signature_default(git_signature **out, git_repository *repo);
|
||||
|
||||
/**
|
||||
* Create a copy of an existing signature. All internal strings are also
|
||||
|
@ -57,7 +57,7 @@ typedef enum {
|
||||
GIT_EXTERN(int) git_stash_save(
|
||||
git_oid *out,
|
||||
git_repository *repo,
|
||||
git_signature *stasher,
|
||||
const git_signature *stasher,
|
||||
const char *message,
|
||||
unsigned int flags);
|
||||
|
||||
@ -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;
|
||||
@ -59,49 +60,24 @@ typedef int (*git_status_cb)(
|
||||
const char *path, unsigned int status_flags, void *payload);
|
||||
|
||||
/**
|
||||
* Gather file statuses and run a callback for each one.
|
||||
* Select the files on which to report status.
|
||||
*
|
||||
* 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.
|
||||
* With `git_status_foreach_ext`, this will control which changes get
|
||||
* callbacks. With `git_status_list_new`, these will control which
|
||||
* changes are included in the list.
|
||||
*
|
||||
* 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` regarding which files are
|
||||
* included and in what order.
|
||||
* - 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.
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
|
||||
GIT_STATUS_SHOW_INDEX_ONLY = 1,
|
||||
GIT_STATUS_SHOW_WORKDIR_ONLY = 2,
|
||||
GIT_STATUS_SHOW_INDEX_THEN_WORKDIR = 3,
|
||||
} git_status_show_t;
|
||||
|
||||
/**
|
||||
@ -110,26 +86,38 @@ 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 that 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
|
||||
* - GIT_STATUS_OPT_RENAMES_FROM_REWRITES indicates that rename detection
|
||||
* should include rewritten files
|
||||
*
|
||||
* Calling `git_status_foreach()` is like calling the extended version
|
||||
* with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
|
||||
@ -144,6 +132,11 @@ typedef enum {
|
||||
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
|
||||
GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
|
||||
GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6),
|
||||
GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1u << 7),
|
||||
GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8),
|
||||
GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1u << 9),
|
||||
GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10),
|
||||
GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11),
|
||||
} git_status_opt_t;
|
||||
|
||||
#define GIT_STATUS_OPT_DEFAULTS \
|
||||
@ -177,6 +170,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.
|
||||
*
|
||||
@ -203,18 +237,61 @@ GIT_EXTERN(int) git_status_foreach_ext(
|
||||
* This is not quite the same as calling `git_status_foreach_ext()` with
|
||||
* the pathspec set to the specified path.
|
||||
*
|
||||
* @param status_flags The status value for the file
|
||||
* @param status_flags Output combination of git_status_t values for file
|
||||
* @param repo A repository object
|
||||
* @param path The file to retrieve status for, rooted at the repo's workdir
|
||||
* @param path The file to retrieve status for relative to the repo workdir
|
||||
* @return 0 on success, GIT_ENOTFOUND if the file is not found in the HEAD,
|
||||
* index, and work tree, GIT_EINVALIDPATH if `path` points at a folder,
|
||||
* GIT_EAMBIGUOUS if "path" matches multiple files, -1 on other error.
|
||||
* index, and work tree, GIT_EAMBIGUOUS if `path` matches multiple files
|
||||
* or if it refers to a folder, and -1 on other errors.
|
||||
*/
|
||||
GIT_EXTERN(int) git_status_file(
|
||||
unsigned int *status_flags,
|
||||
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.
|
||||
*
|
||||
|
@ -14,51 +14,18 @@
|
||||
/**
|
||||
* @file git2/submodule.h
|
||||
* @brief Git submodule management utilities
|
||||
* @defgroup git_submodule Git submodule management routines
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* Opaque structure representing a submodule.
|
||||
*
|
||||
* Submodule support in libgit2 builds a list of known submodules and keeps
|
||||
* it in the repository. The list is built from the .gitmodules file, the
|
||||
* .git/config file, the index, and the HEAD tree. Items in the working
|
||||
* directory that look like submodules (i.e. a git repo) but are not
|
||||
* mentioned in those places won't be tracked.
|
||||
*/
|
||||
typedef struct git_submodule git_submodule;
|
||||
|
||||
/**
|
||||
* Values that could be specified for the update rule of a submodule.
|
||||
*
|
||||
* Use the DEFAULT value if you have altered the update value via
|
||||
* `git_submodule_set_update()` and wish to reset to the original default.
|
||||
* @defgroup git_submodule Git submodule management routines
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_SUBMODULE_UPDATE_DEFAULT = -1,
|
||||
GIT_SUBMODULE_UPDATE_CHECKOUT = 0,
|
||||
GIT_SUBMODULE_UPDATE_REBASE = 1,
|
||||
GIT_SUBMODULE_UPDATE_MERGE = 2,
|
||||
GIT_SUBMODULE_UPDATE_NONE = 3
|
||||
} git_submodule_update_t;
|
||||
|
||||
/**
|
||||
* Values that could be specified for how closely to examine the
|
||||
* working directory when getting submodule status.
|
||||
*
|
||||
* Use the DEFUALT value if you have altered the ignore value via
|
||||
* `git_submodule_set_ignore()` and wish to reset to the original value.
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_SUBMODULE_IGNORE_DEFAULT = -1, /* reset to default */
|
||||
GIT_SUBMODULE_IGNORE_NONE = 0, /* any change or untracked == dirty */
|
||||
GIT_SUBMODULE_IGNORE_UNTRACKED = 1, /* dirty if tracked files change */
|
||||
GIT_SUBMODULE_IGNORE_DIRTY = 2, /* only dirty if HEAD moved */
|
||||
GIT_SUBMODULE_IGNORE_ALL = 3 /* never dirty */
|
||||
} git_submodule_ignore_t;
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* Return codes for submodule status.
|
||||
@ -119,19 +86,9 @@ typedef enum {
|
||||
GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13),
|
||||
} git_submodule_status_t;
|
||||
|
||||
#define GIT_SUBMODULE_STATUS__IN_FLAGS \
|
||||
(GIT_SUBMODULE_STATUS_IN_HEAD | \
|
||||
GIT_SUBMODULE_STATUS_IN_INDEX | \
|
||||
GIT_SUBMODULE_STATUS_IN_CONFIG | \
|
||||
GIT_SUBMODULE_STATUS_IN_WD)
|
||||
|
||||
#define GIT_SUBMODULE_STATUS__INDEX_FLAGS \
|
||||
(GIT_SUBMODULE_STATUS_INDEX_ADDED | \
|
||||
GIT_SUBMODULE_STATUS_INDEX_DELETED | \
|
||||
GIT_SUBMODULE_STATUS_INDEX_MODIFIED)
|
||||
|
||||
#define GIT_SUBMODULE_STATUS__WD_FLAGS \
|
||||
~(GIT_SUBMODULE_STATUS__IN_FLAGS | GIT_SUBMODULE_STATUS__INDEX_FLAGS)
|
||||
#define GIT_SUBMODULE_STATUS__IN_FLAGS 0x000Fu
|
||||
#define GIT_SUBMODULE_STATUS__INDEX_FLAGS 0x0070u
|
||||
#define GIT_SUBMODULE_STATUS__WD_FLAGS 0x3F80u
|
||||
|
||||
#define GIT_SUBMODULE_STATUS_IS_UNMODIFIED(S) \
|
||||
(((S) & ~GIT_SUBMODULE_STATUS__IN_FLAGS) == 0)
|
||||
@ -359,9 +316,10 @@ GIT_EXTERN(const git_oid *) git_submodule_head_id(git_submodule *submodule);
|
||||
GIT_EXTERN(const git_oid *) git_submodule_wd_id(git_submodule *submodule);
|
||||
|
||||
/**
|
||||
* Get the ignore rule for the submodule.
|
||||
* Get the ignore rule that will be used for the submodule.
|
||||
*
|
||||
* There are four ignore values:
|
||||
* These values control the behavior of `git_submodule_status()` for this
|
||||
* submodule. There are four ignore values:
|
||||
*
|
||||
* - **GIT_SUBMODULE_IGNORE_NONE** will consider any change to the contents
|
||||
* of the submodule from a clean checkout to be dirty, including the
|
||||
@ -375,6 +333,13 @@ GIT_EXTERN(const git_oid *) git_submodule_wd_id(git_submodule *submodule);
|
||||
* - **GIT_SUBMODULE_IGNORE_ALL** means not to open the submodule repo.
|
||||
* The working directory will be consider clean so long as there is a
|
||||
* checked out version present.
|
||||
*
|
||||
* plus the special **GIT_SUBMODULE_IGNORE_RESET** which can be used with
|
||||
* `git_submodule_set_ignore()` to revert to the on-disk setting.
|
||||
*
|
||||
* @param submodule The submodule to check
|
||||
* @return The current git_submodule_ignore_t valyue what will be used for
|
||||
* this submodule.
|
||||
*/
|
||||
GIT_EXTERN(git_submodule_ignore_t) git_submodule_ignore(
|
||||
git_submodule *submodule);
|
||||
@ -382,15 +347,17 @@ GIT_EXTERN(git_submodule_ignore_t) git_submodule_ignore(
|
||||
/**
|
||||
* Set the ignore rule for the submodule.
|
||||
*
|
||||
* This sets the ignore rule in memory for the submodule. This will be used
|
||||
* for any following actions (such as `git_submodule_status()`) while the
|
||||
* submodule is in memory. You should call `git_submodule_save()` if you
|
||||
* want to persist the new ignore role.
|
||||
* This sets the in-memory ignore rule for the submodule which will
|
||||
* control the behavior of `git_submodule_status()`.
|
||||
*
|
||||
* Calling this again with GIT_SUBMODULE_IGNORE_DEFAULT or calling
|
||||
* `git_submodule_reload()` will revert the rule to the value that was in the
|
||||
* original config.
|
||||
* To make changes persistent, call `git_submodule_save()` to write the
|
||||
* value to disk (in the ".gitmodules" and ".git/config" files).
|
||||
*
|
||||
* Call with `GIT_SUBMODULE_IGNORE_RESET` or call `git_submodule_reload()`
|
||||
* to revert the in-memory rule to the value that is on disk.
|
||||
*
|
||||
* @param submodule The submodule to update
|
||||
* @param ignore The new value for the ignore rule
|
||||
* @return old value for ignore
|
||||
*/
|
||||
GIT_EXTERN(git_submodule_ignore_t) git_submodule_set_ignore(
|
||||
@ -398,7 +365,16 @@ GIT_EXTERN(git_submodule_ignore_t) git_submodule_set_ignore(
|
||||
git_submodule_ignore_t ignore);
|
||||
|
||||
/**
|
||||
* Get the update rule for the submodule.
|
||||
* Get the update rule that will be used for the submodule.
|
||||
*
|
||||
* This value controls the behavior of the `git submodule update` command.
|
||||
* There are four useful values documented with `git_submodule_update_t`
|
||||
* plus the `GIT_SUBMODULE_UPDATE_RESET` which can be used to revert to
|
||||
* the on-disk setting.
|
||||
*
|
||||
* @param submodule The submodule to check
|
||||
* @return The current git_submodule_update_t value that will be used
|
||||
* for this submodule.
|
||||
*/
|
||||
GIT_EXTERN(git_submodule_update_t) git_submodule_update(
|
||||
git_submodule *submodule);
|
||||
@ -406,13 +382,17 @@ GIT_EXTERN(git_submodule_update_t) git_submodule_update(
|
||||
/**
|
||||
* Set the update rule for the submodule.
|
||||
*
|
||||
* This sets the update rule in memory for the submodule. You should call
|
||||
* `git_submodule_save()` if you want to persist the new update rule.
|
||||
* The initial value comes from the ".git/config" setting of
|
||||
* `submodule.$name.update` for this submodule (which is initialized from
|
||||
* the ".gitmodules" file). Using this function sets the update rule in
|
||||
* memory for the submodule. Call `git_submodule_save()` to write out the
|
||||
* new update rule.
|
||||
*
|
||||
* Calling this again with GIT_SUBMODULE_UPDATE_DEFAULT or calling
|
||||
* `git_submodule_reload()` will revert the rule to the value that was in the
|
||||
* original config.
|
||||
* Calling this again with GIT_SUBMODULE_UPDATE_RESET or calling
|
||||
* `git_submodule_reload()` will revert the rule to the on disk value.
|
||||
*
|
||||
* @param submodule The submodule to update
|
||||
* @param update The new value to use
|
||||
* @return old value for update
|
||||
*/
|
||||
GIT_EXTERN(git_submodule_update_t) git_submodule_set_update(
|
||||
@ -481,7 +461,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 +511,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
|
||||
*/
|
||||
|
@ -20,6 +20,33 @@
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* Every iterator must have this struct as its first element, so the
|
||||
* API can talk to it. You'd define your iterator as
|
||||
*
|
||||
* struct my_iterator {
|
||||
* git_config_iterator parent;
|
||||
* ...
|
||||
* }
|
||||
*
|
||||
* and assign `iter->parent.backend` to your `git_config_backend`.
|
||||
*/
|
||||
struct git_config_iterator {
|
||||
git_config_backend *backend;
|
||||
unsigned int flags;
|
||||
|
||||
/**
|
||||
* Return the current entry and advance the iterator. The
|
||||
* memory belongs to the library.
|
||||
*/
|
||||
int (*next)(git_config_entry **entry, git_config_iterator *iter);
|
||||
|
||||
/**
|
||||
* Free the iterator
|
||||
*/
|
||||
void (*free)(git_config_iterator *iter);
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic backend that implements the interface to
|
||||
* access a configuration file
|
||||
@ -31,11 +58,10 @@ struct git_config_backend {
|
||||
/* 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 (*iterator)(git_config_iterator **, struct git_config_backend *);
|
||||
int (*refresh)(struct git_config_backend *);
|
||||
void (*free)(struct git_config_backend *);
|
||||
};
|
||||
|
@ -72,7 +72,6 @@ GIT_EXTERN(int) git_index_name_add(git_index *index,
|
||||
* 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);
|
||||
|
||||
@ -168,7 +167,6 @@ 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);
|
||||
|
||||
|
@ -48,12 +48,12 @@ struct git_odb_backend {
|
||||
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.
|
||||
/**
|
||||
* Write an object into the backend. The id of the object has
|
||||
* already been calculated and is passed in.
|
||||
*/
|
||||
int (* write)(
|
||||
git_oid *, git_odb_backend *, const void *, size_t, git_otype);
|
||||
git_odb_backend *, const git_oid *, const void *, size_t, git_otype);
|
||||
|
||||
int (* writestream)(
|
||||
git_odb_stream **, git_odb_backend *, size_t, git_otype);
|
||||
@ -64,6 +64,16 @@ struct git_odb_backend {
|
||||
int (* exists)(
|
||||
git_odb_backend *, const git_oid *);
|
||||
|
||||
/**
|
||||
* If the backend implements a refreshing mechanism, it should be exposed
|
||||
* through this endpoint. Each call to `git_odb_refresh()` will invoke it.
|
||||
*
|
||||
* However, the backend implementation should try to stay up-to-date as much
|
||||
* as possible by itself as libgit2 will not automatically invoke
|
||||
* `git_odb_refresh()`. For instance, a potential strategy for the backend
|
||||
* implementation to achieve this could be to internally invoke this
|
||||
* endpoint on failed lookups (ie. `exists()`, `read()`, `read_header()`).
|
||||
*/
|
||||
int (* refresh)(git_odb_backend *);
|
||||
|
||||
int (* foreach)(
|
||||
|
@ -16,7 +16,7 @@
|
||||
*
|
||||
* @param name the reference name
|
||||
* @param oid the object id for a direct reference
|
||||
* @param symbolic the target for a symbolic reference
|
||||
* @param peel the first non-tag object's OID, or NULL
|
||||
* @return the created git_reference or NULL on error
|
||||
*/
|
||||
GIT_EXTERN(git_reference *) git_reference__alloc(
|
||||
@ -28,7 +28,7 @@ GIT_EXTERN(git_reference *) git_reference__alloc(
|
||||
* Create a new symbolic reference.
|
||||
*
|
||||
* @param name the reference name
|
||||
* @param symbolic the target for a symbolic reference
|
||||
* @param target the target for a symbolic reference
|
||||
* @return the created git_reference or NULL on error
|
||||
*/
|
||||
GIT_EXTERN(git_reference *) git_reference__alloc_symbolic(
|
||||
|
@ -36,14 +36,15 @@ typedef enum {
|
||||
} git_credtype_t;
|
||||
|
||||
/* The base structure for all credential types */
|
||||
typedef struct git_cred {
|
||||
typedef struct git_cred git_cred;
|
||||
|
||||
struct git_cred {
|
||||
git_credtype_t credtype;
|
||||
void (*free)(
|
||||
struct git_cred *cred);
|
||||
} git_cred;
|
||||
void (*free)(git_cred *cred);
|
||||
};
|
||||
|
||||
/* A plaintext username and password */
|
||||
typedef struct git_cred_userpass_plaintext {
|
||||
typedef struct {
|
||||
git_cred parent;
|
||||
char *username;
|
||||
char *password;
|
||||
@ -51,10 +52,14 @@ typedef struct git_cred_userpass_plaintext {
|
||||
|
||||
#ifdef GIT_SSH
|
||||
typedef LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*git_cred_sign_callback));
|
||||
#else
|
||||
typedef int (*git_cred_sign_callback)(void *, ...);
|
||||
#endif
|
||||
|
||||
/* A ssh key file and passphrase */
|
||||
typedef struct git_cred_ssh_keyfile_passphrase {
|
||||
git_cred parent;
|
||||
char *username;
|
||||
char *publickey;
|
||||
char *privatekey;
|
||||
char *passphrase;
|
||||
@ -63,12 +68,20 @@ typedef struct git_cred_ssh_keyfile_passphrase {
|
||||
/* A ssh public key and authentication callback */
|
||||
typedef struct git_cred_ssh_publickey {
|
||||
git_cred parent;
|
||||
char *username;
|
||||
char *publickey;
|
||||
size_t publickey_len;
|
||||
void *sign_callback;
|
||||
void *sign_data;
|
||||
} git_cred_ssh_publickey;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Check whether a credential object contains username information.
|
||||
*
|
||||
* @param cred object to check
|
||||
* @return 1 if the credential object has non-NULL username, 0 otherwise
|
||||
*/
|
||||
GIT_EXTERN(int) git_cred_has_username(git_cred *cred);
|
||||
|
||||
/**
|
||||
* Creates a new plain-text username and password credential object.
|
||||
@ -84,12 +97,12 @@ 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 username username to use to authenticate
|
||||
* @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.
|
||||
@ -97,6 +110,7 @@ GIT_EXTERN(int) git_cred_userpass_plaintext_new(
|
||||
*/
|
||||
GIT_EXTERN(int) git_cred_ssh_keyfile_passphrase_new(
|
||||
git_cred **out,
|
||||
const char *username,
|
||||
const char *publickey,
|
||||
const char *privatekey,
|
||||
const char *passphrase);
|
||||
@ -106,19 +120,20 @@ GIT_EXTERN(int) git_cred_ssh_keyfile_passphrase_new(
|
||||
* The supplied credential parameter will be internally duplicated.
|
||||
*
|
||||
* @param out The newly created credential object.
|
||||
* @param username username to use to authenticate
|
||||
* @param publickey The bytes of the public key.
|
||||
* @param publickey_len The length of the public key in bytes.
|
||||
* @param sign_callback The callback method for authenticating.
|
||||
* @param sign_fn 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 *username,
|
||||
const char *publickey,
|
||||
size_t publickey_len,
|
||||
git_cred_sign_callback,
|
||||
git_cred_sign_callback sign_fn,
|
||||
void *sign_data);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Signature of a function which acquires a credential object.
|
||||
@ -152,17 +167,21 @@ typedef enum {
|
||||
|
||||
typedef void (*git_transport_message_cb)(const char *str, int len, void *data);
|
||||
|
||||
typedef struct git_transport {
|
||||
typedef struct git_transport git_transport;
|
||||
|
||||
struct git_transport {
|
||||
unsigned int version;
|
||||
/* Set progress and error callbacks */
|
||||
int (*set_callbacks)(struct git_transport *transport,
|
||||
int (*set_callbacks)(
|
||||
git_transport *transport,
|
||||
git_transport_message_cb progress_cb,
|
||||
git_transport_message_cb error_cb,
|
||||
void *payload);
|
||||
|
||||
/* Connect the transport to the remote repository, using the given
|
||||
* direction. */
|
||||
int (*connect)(struct git_transport *transport,
|
||||
int (*connect)(
|
||||
git_transport *transport,
|
||||
const char *url,
|
||||
git_cred_acquire_cb cred_acquire_cb,
|
||||
void *cred_acquire_payload,
|
||||
@ -172,17 +191,19 @@ typedef struct git_transport {
|
||||
/* This function may be called after a successful call to connect(). The
|
||||
* provided callback is invoked for each ref discovered on the remote
|
||||
* end. */
|
||||
int (*ls)(struct git_transport *transport,
|
||||
int (*ls)(
|
||||
git_transport *transport,
|
||||
git_headlist_cb list_cb,
|
||||
void *payload);
|
||||
|
||||
/* Executes the push whose context is in the git_push object. */
|
||||
int (*push)(struct git_transport *transport, git_push *push);
|
||||
int (*push)(git_transport *transport, git_push *push);
|
||||
|
||||
/* This function may be called after a successful call to connect(), when
|
||||
* the direction is FETCH. The function performs a negotiation to calculate
|
||||
* the wants list for the fetch. */
|
||||
int (*negotiate_fetch)(struct git_transport *transport,
|
||||
int (*negotiate_fetch)(
|
||||
git_transport *transport,
|
||||
git_repository *repo,
|
||||
const git_remote_head * const *refs,
|
||||
size_t count);
|
||||
@ -190,28 +211,29 @@ typedef struct git_transport {
|
||||
/* This function may be called after a successful call to negotiate_fetch(),
|
||||
* when the direction is FETCH. This function retrieves the pack file for
|
||||
* the fetch from the remote end. */
|
||||
int (*download_pack)(struct git_transport *transport,
|
||||
int (*download_pack)(
|
||||
git_transport *transport,
|
||||
git_repository *repo,
|
||||
git_transfer_progress *stats,
|
||||
git_transfer_progress_callback progress_cb,
|
||||
void *progress_payload);
|
||||
|
||||
/* Checks to see if the transport is connected */
|
||||
int (*is_connected)(struct git_transport *transport);
|
||||
int (*is_connected)(git_transport *transport);
|
||||
|
||||
/* Reads the flags value previously passed into connect() */
|
||||
int (*read_flags)(struct git_transport *transport, int *flags);
|
||||
int (*read_flags)(git_transport *transport, int *flags);
|
||||
|
||||
/* Cancels any outstanding transport operation */
|
||||
void (*cancel)(struct git_transport *transport);
|
||||
void (*cancel)(git_transport *transport);
|
||||
|
||||
/* This function is the reverse of connect() -- it terminates the
|
||||
* connection to the remote end. */
|
||||
int (*close)(struct git_transport *transport);
|
||||
int (*close)(git_transport *transport);
|
||||
|
||||
/* Frees/destructs the git_transport object. */
|
||||
void (*free)(struct git_transport *transport);
|
||||
} git_transport;
|
||||
void (*free)(git_transport *transport);
|
||||
};
|
||||
|
||||
#define GIT_TRANSPORT_VERSION 1
|
||||
#define GIT_TRANSPORT_INIT {GIT_TRANSPORT_VERSION}
|
||||
@ -299,35 +321,36 @@ typedef enum {
|
||||
GIT_SERVICE_RECEIVEPACK = 4,
|
||||
} git_smart_service_t;
|
||||
|
||||
struct git_smart_subtransport;
|
||||
typedef struct git_smart_subtransport git_smart_subtransport;
|
||||
typedef struct git_smart_subtransport_stream git_smart_subtransport_stream;
|
||||
|
||||
/* A stream used by the smart transport to read and write data
|
||||
* from a subtransport */
|
||||
typedef struct git_smart_subtransport_stream {
|
||||
struct git_smart_subtransport_stream {
|
||||
/* The owning subtransport */
|
||||
struct git_smart_subtransport *subtransport;
|
||||
git_smart_subtransport *subtransport;
|
||||
|
||||
int (*read)(
|
||||
struct git_smart_subtransport_stream *stream,
|
||||
git_smart_subtransport_stream *stream,
|
||||
char *buffer,
|
||||
size_t buf_size,
|
||||
size_t *bytes_read);
|
||||
|
||||
int (*write)(
|
||||
struct git_smart_subtransport_stream *stream,
|
||||
git_smart_subtransport_stream *stream,
|
||||
const char *buffer,
|
||||
size_t len);
|
||||
|
||||
void (*free)(
|
||||
struct git_smart_subtransport_stream *stream);
|
||||
} git_smart_subtransport_stream;
|
||||
git_smart_subtransport_stream *stream);
|
||||
};
|
||||
|
||||
/* An implementation of a subtransport which carries data for the
|
||||
* smart transport */
|
||||
typedef struct git_smart_subtransport {
|
||||
struct git_smart_subtransport {
|
||||
int (* action)(
|
||||
git_smart_subtransport_stream **out,
|
||||
struct git_smart_subtransport *transport,
|
||||
git_smart_subtransport *transport,
|
||||
const char *url,
|
||||
git_smart_service_t action);
|
||||
|
||||
@ -337,10 +360,10 @@ typedef struct git_smart_subtransport {
|
||||
*
|
||||
* 1. UPLOADPACK_LS -> UPLOADPACK
|
||||
* 2. RECEIVEPACK_LS -> RECEIVEPACK */
|
||||
int (* close)(struct git_smart_subtransport *transport);
|
||||
int (*close)(git_smart_subtransport *transport);
|
||||
|
||||
void (* free)(struct git_smart_subtransport *transport);
|
||||
} git_smart_subtransport;
|
||||
void (*free)(git_smart_subtransport *transport);
|
||||
};
|
||||
|
||||
/* A function which creates a new subtransport for the smart transport */
|
||||
typedef int (*git_smart_subtransport_cb)(
|
||||
|
@ -38,7 +38,7 @@ GIT_EXTERN(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
|
||||
@ -136,7 +136,7 @@ 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(
|
||||
@ -208,11 +208,11 @@ GIT_EXTERN(git_filemode_t) git_tree_entry_filemode(const git_tree_entry *entry);
|
||||
GIT_EXTERN(int) git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2);
|
||||
|
||||
/**
|
||||
* Convert a tree entry to the git_object it points too.
|
||||
* Convert a tree entry to the git_object it points to.
|
||||
*
|
||||
* 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
|
||||
@ -251,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);
|
||||
|
@ -174,6 +174,9 @@ 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 {
|
||||
@ -226,6 +229,77 @@ typedef struct git_transfer_progress {
|
||||
*/
|
||||
typedef int (*git_transfer_progress_callback)(const git_transfer_progress *stats, void *payload);
|
||||
|
||||
/**
|
||||
* Opaque structure representing a submodule.
|
||||
*/
|
||||
typedef struct git_submodule git_submodule;
|
||||
|
||||
/**
|
||||
* Submodule update values
|
||||
*
|
||||
* These values represent settings for the `submodule.$name.update`
|
||||
* configuration value which says how to handle `git submodule update` for
|
||||
* this submodule. The value is usually set in the ".gitmodules" file and
|
||||
* copied to ".git/config" when the submodule is initialized.
|
||||
*
|
||||
* You can override this setting on a per-submodule basis with
|
||||
* `git_submodule_set_update()` and write the changed value to disk using
|
||||
* `git_submodule_save()`. If you have overwritten the value, you can
|
||||
* revert it by passing `GIT_SUBMODULE_UPDATE_RESET` to the set function.
|
||||
*
|
||||
* The values are:
|
||||
*
|
||||
* - GIT_SUBMODULE_UPDATE_RESET: reset to the on-disk value.
|
||||
* - GIT_SUBMODULE_UPDATE_CHECKOUT: the default; when a submodule is
|
||||
* updated, checkout the new detached HEAD to the submodule directory.
|
||||
* - GIT_SUBMODULE_UPDATE_REBASE: update by rebasing the current checked
|
||||
* out branch onto the commit from the superproject.
|
||||
* - GIT_SUBMODULE_UPDATE_MERGE: update by merging the commit in the
|
||||
* superproject into the current checkout out branch of the submodule.
|
||||
* - GIT_SUBMODULE_UPDATE_NONE: do not update this submodule even when
|
||||
* the commit in the superproject is updated.
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_SUBMODULE_UPDATE_RESET = -1,
|
||||
GIT_SUBMODULE_UPDATE_CHECKOUT = 1,
|
||||
GIT_SUBMODULE_UPDATE_REBASE = 2,
|
||||
GIT_SUBMODULE_UPDATE_MERGE = 3,
|
||||
GIT_SUBMODULE_UPDATE_NONE = 4
|
||||
} git_submodule_update_t;
|
||||
|
||||
/**
|
||||
* Submodule ignore values
|
||||
*
|
||||
* These values represent settings for the `submodule.$name.ignore`
|
||||
* configuration value which says how deeply to look at the working
|
||||
* directory when getting submodule status.
|
||||
*
|
||||
* You can override this value in memory on a per-submodule basis with
|
||||
* `git_submodule_set_ignore()` and can write the changed value to disk
|
||||
* with `git_submodule_save()`. If you have overwritten the value, you
|
||||
* can revert to the on disk value by using `GIT_SUBMODULE_IGNORE_RESET`.
|
||||
*
|
||||
* The values are:
|
||||
*
|
||||
* - GIT_SUBMODULE_IGNORE_RESET: reset to the on-disk value.
|
||||
* - GIT_SUBMODULE_IGNORE_NONE: don't ignore any change - i.e. even an
|
||||
* untracked file, will mark the submodule as dirty. Ignored files are
|
||||
* still ignored, of course.
|
||||
* - GIT_SUBMODULE_IGNORE_UNTRACKED: ignore untracked files; only changes
|
||||
* to tracked files, or the index or the HEAD commit will matter.
|
||||
* - GIT_SUBMODULE_IGNORE_DIRTY: ignore changes in the working directory,
|
||||
* only considering changes if the HEAD of submodule has moved from the
|
||||
* value in the superproject.
|
||||
* - GIT_SUBMODULE_IGNORE_ALL: never check if the submodule is dirty
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_SUBMODULE_IGNORE_RESET = -1, /* reset to on-disk value */
|
||||
GIT_SUBMODULE_IGNORE_NONE = 1, /* any change or untracked == dirty */
|
||||
GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /* dirty if tracked files change */
|
||||
GIT_SUBMODULE_IGNORE_DIRTY = 3, /* only dirty if HEAD moved */
|
||||
GIT_SUBMODULE_IGNORE_ALL = 4 /* never dirty */
|
||||
} git_submodule_ignore_t;
|
||||
|
||||
/** @} */
|
||||
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
|
||||
|
32
script/cibuild.sh
Executable file
32
script/cibuild.sh
Executable file
@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Create a test repo which we can use for the online::push tests
|
||||
mkdir $HOME/_temp
|
||||
git init --bare $HOME/_temp/test.git
|
||||
git daemon --listen=localhost --export-all --enable=receive-pack --base-path=$HOME/_temp $HOME/_temp 2>/dev/null &
|
||||
export GITTEST_REMOTE_URL="git://localhost/test.git"
|
||||
|
||||
mkdir _build
|
||||
cd _build
|
||||
cmake .. -DCMAKE_INSTALL_PREFIX=../_install $OPTIONS || exit $?
|
||||
cmake --build . --target install || exit $?
|
||||
ctest -V . || exit $?
|
||||
|
||||
# Now that we've tested the raw git protocol, let's set up ssh to we
|
||||
# can do the push tests over it
|
||||
|
||||
killall git-daemon
|
||||
sudo start ssh
|
||||
ssh-keygen -t rsa -f ~/.ssh/id_rsa -N "" -q
|
||||
cat ~/.ssh/id_rsa.pub >>~/.ssh/authorized_keys
|
||||
ssh-keyscan -t rsa localhost >>~/.ssh/known_hosts
|
||||
|
||||
export GITTEST_REMOTE_URL="ssh://localhost/$HOME/_temp/test.git"
|
||||
export GITTEST_REMOTE_USER=$USER
|
||||
export GITTEST_REMOTE_SSH_KEY="$HOME/.ssh/id_rsa"
|
||||
export GITTEST_REMOTE_SSH_PUBKEY="$HOME/.ssh/id_rsa.pub"
|
||||
export GITTEST_REMOTE_SSH_PASSPHRASE=""
|
||||
|
||||
if [ -e ./libgit2_clar ]; then
|
||||
./libgit2_clar -sonline::push
|
||||
fi
|
18
src/array.h
18
src/array.h
@ -30,37 +30,45 @@
|
||||
#define git_array_init(a) \
|
||||
do { (a).size = (a).asize = 0; (a).ptr = NULL; } while (0)
|
||||
|
||||
#define git_array_init_to_size(a, desired) \
|
||||
do { (a).size = 0; (a).asize = desired; (a).ptr = git__calloc(desired, sizeof(*(a).ptr)); } 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;
|
||||
typedef git_array_t(char) 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)
|
||||
GIT_INLINE(void *) git_array_grow(void *_a, size_t item_size)
|
||||
{
|
||||
git_array_generic_t *a = _a;
|
||||
uint32_t new_size = (a->size < 8) ? 8 : a->asize * 3 / 2;
|
||||
void *new_array = git__realloc(a->ptr, new_size * item_size);
|
||||
char *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);
|
||||
return 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)) : \
|
||||
git_array_grow(&(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_pop(a) ((a).size ? &(a).ptr[--(a).size] : NULL)
|
||||
|
||||
#define git_array_get(a, i) (((i) < (a).size) ? &(a).ptr[(i)] : NULL)
|
||||
|
||||
#define git_array_size(a) (a).size
|
||||
|
||||
#define git_array_valid_index(a, i) ((i) < (a).size)
|
||||
|
||||
#endif
|
||||
|
@ -79,10 +79,14 @@ int git_attr_file__parse_buffer(
|
||||
|
||||
while (!error && *scan) {
|
||||
/* allocate rule if needed */
|
||||
if (!rule && !(rule = git__calloc(1, sizeof(git_attr_rule)))) {
|
||||
if (!rule) {
|
||||
if (!(rule = git__calloc(1, sizeof(git_attr_rule)))) {
|
||||
error = -1;
|
||||
break;
|
||||
}
|
||||
rule->match.flags = GIT_ATTR_FNMATCH_ALLOWNEG |
|
||||
GIT_ATTR_FNMATCH_ALLOWMACRO;
|
||||
}
|
||||
|
||||
/* parse the next "pattern attr attr attr" line */
|
||||
if (!(error = git_attr_fnmatch__parse(
|
||||
@ -351,8 +355,8 @@ int git_attr_fnmatch__parse(
|
||||
if (parse_optimized_patterns(spec, pool, *base))
|
||||
return 0;
|
||||
|
||||
spec->flags = (spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE);
|
||||
allow_space = (spec->flags != 0);
|
||||
spec->flags = (spec->flags & GIT_ATTR_FNMATCH__INCOMING);
|
||||
allow_space = ((spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE) != 0);
|
||||
|
||||
pattern = *base;
|
||||
|
||||
@ -362,7 +366,7 @@ int git_attr_fnmatch__parse(
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
if (*pattern == '[') {
|
||||
if (*pattern == '[' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWMACRO) != 0) {
|
||||
if (strncmp(pattern, "[attr]", 6) == 0) {
|
||||
spec->flags = spec->flags | GIT_ATTR_FNMATCH_MACRO;
|
||||
pattern += 6;
|
||||
@ -370,7 +374,7 @@ int git_attr_fnmatch__parse(
|
||||
/* else a character range like [a-e]* which is accepted */
|
||||
}
|
||||
|
||||
if (*pattern == '!') {
|
||||
if (*pattern == '!' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWNEG) != 0) {
|
||||
spec->flags = spec->flags | GIT_ATTR_FNMATCH_NEGATIVE;
|
||||
pattern++;
|
||||
}
|
||||
@ -498,7 +502,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;
|
||||
|
@ -28,6 +28,12 @@
|
||||
#define GIT_ATTR_FNMATCH_ALLOWSPACE (1U << 6)
|
||||
#define GIT_ATTR_FNMATCH_ICASE (1U << 7)
|
||||
#define GIT_ATTR_FNMATCH_MATCH_ALL (1U << 8)
|
||||
#define GIT_ATTR_FNMATCH_ALLOWNEG (1U << 9)
|
||||
#define GIT_ATTR_FNMATCH_ALLOWMACRO (1U << 10)
|
||||
|
||||
#define GIT_ATTR_FNMATCH__INCOMING \
|
||||
(GIT_ATTR_FNMATCH_ALLOWSPACE | \
|
||||
GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO)
|
||||
|
||||
extern const char *git_attr__true;
|
||||
extern const char *git_attr__false;
|
||||
|
75
src/bitvec.h
Normal file
75
src/bitvec.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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_bitvec_h__
|
||||
#define INCLUDE_bitvec_h__
|
||||
|
||||
#include "util.h"
|
||||
|
||||
/*
|
||||
* This is a silly little fixed length bit vector type that will store
|
||||
* vectors of 64 bits or less directly in the structure and allocate
|
||||
* memory for vectors longer than 64 bits. You can use the two versions
|
||||
* transparently through the API and avoid heap allocation completely when
|
||||
* using a short bit vector as a result.
|
||||
*/
|
||||
typedef struct {
|
||||
size_t length;
|
||||
union {
|
||||
uint64_t *words;
|
||||
uint64_t bits;
|
||||
} u;
|
||||
} git_bitvec;
|
||||
|
||||
GIT_INLINE(int) git_bitvec_init(git_bitvec *bv, size_t capacity)
|
||||
{
|
||||
memset(bv, 0x0, sizeof(*bv));
|
||||
|
||||
if (capacity >= 64) {
|
||||
bv->length = (capacity / 64) + 1;
|
||||
bv->u.words = git__calloc(bv->length, sizeof(uint64_t));
|
||||
if (!bv->u.words)
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define GIT_BITVEC_MASK(BIT) ((uint64_t)1 << (BIT % 64))
|
||||
#define GIT_BITVEC_WORD(BV, BIT) (BV->length ? &BV->u.words[BIT / 64] : &BV->u.bits)
|
||||
|
||||
GIT_INLINE(void) git_bitvec_set(git_bitvec *bv, size_t bit, bool on)
|
||||
{
|
||||
uint64_t *word = GIT_BITVEC_WORD(bv, bit);
|
||||
uint64_t mask = GIT_BITVEC_MASK(bit);
|
||||
|
||||
if (on)
|
||||
*word |= mask;
|
||||
else
|
||||
*word &= ~mask;
|
||||
}
|
||||
|
||||
GIT_INLINE(bool) git_bitvec_get(git_bitvec *bv, size_t bit)
|
||||
{
|
||||
uint64_t *word = GIT_BITVEC_WORD(bv, bit);
|
||||
return (*word & GIT_BITVEC_MASK(bit)) != 0;
|
||||
}
|
||||
|
||||
GIT_INLINE(void) git_bitvec_clear(git_bitvec *bv)
|
||||
{
|
||||
if (!bv->length)
|
||||
bv->u.bits = 0;
|
||||
else
|
||||
memset(bv->u.words, 0x0, bv->length * sizeof(uint64_t));
|
||||
}
|
||||
|
||||
GIT_INLINE(void) git_bitvec_free(git_bitvec *bv)
|
||||
{
|
||||
if (bv->length)
|
||||
git__free(bv->u.words);
|
||||
}
|
||||
|
||||
#endif
|
101
src/blob.c
101
src/blob.c
@ -60,10 +60,10 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b
|
||||
(error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < 0)
|
||||
return error;
|
||||
|
||||
if ((error = stream->write(stream, buffer, len)) == 0)
|
||||
error = stream->finalize_write(oid, stream);
|
||||
if ((error = git_odb_stream_write(stream, buffer, len)) == 0)
|
||||
error = git_odb_stream_finalize_write(oid, stream);
|
||||
|
||||
stream->free(stream);
|
||||
git_odb_stream_free(stream);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -80,12 +80,12 @@ static int write_file_stream(
|
||||
return error;
|
||||
|
||||
if ((fd = git_futils_open_ro(path)) < 0) {
|
||||
stream->free(stream);
|
||||
git_odb_stream_free(stream);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (!error && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) {
|
||||
error = stream->write(stream, buffer, read_len);
|
||||
error = git_odb_stream_write(stream, buffer, read_len);
|
||||
written += read_len;
|
||||
}
|
||||
|
||||
@ -97,14 +97,15 @@ static int write_file_stream(
|
||||
}
|
||||
|
||||
if (!error)
|
||||
error = stream->finalize_write(oid, stream);
|
||||
error = git_odb_stream_finalize_write(oid, stream);
|
||||
|
||||
stream->free(stream);
|
||||
git_odb_stream_free(stream);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int write_file_filtered(
|
||||
git_oid *oid,
|
||||
git_off_t *size,
|
||||
git_odb *odb,
|
||||
const char *full_path,
|
||||
git_vector *filters)
|
||||
@ -123,8 +124,11 @@ static int write_file_filtered(
|
||||
git_buf_free(&source);
|
||||
|
||||
/* Write the file to disk if it was properly filtered */
|
||||
if (!error)
|
||||
if (!error) {
|
||||
*size = dest.size;
|
||||
|
||||
error = git_odb_write(oid, odb, dest.ptr, dest.size, GIT_OBJ_BLOB);
|
||||
}
|
||||
|
||||
git_buf_free(&dest);
|
||||
return error;
|
||||
@ -152,21 +156,46 @@ static int write_symlink(
|
||||
return error;
|
||||
}
|
||||
|
||||
static int blob_create_internal(git_oid *oid, git_repository *repo, const char *content_path, const char *hint_path, bool try_load_filters)
|
||||
int git_blob__create_from_paths(
|
||||
git_oid *oid,
|
||||
struct stat *out_st,
|
||||
git_repository *repo,
|
||||
const char *content_path,
|
||||
const char *hint_path,
|
||||
mode_t hint_mode,
|
||||
bool try_load_filters)
|
||||
{
|
||||
int error;
|
||||
struct stat st;
|
||||
git_odb *odb = NULL;
|
||||
git_off_t size;
|
||||
mode_t mode;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
|
||||
assert(hint_path || !try_load_filters);
|
||||
|
||||
if ((error = git_path_lstat(content_path, &st)) < 0 || (error = git_repository_odb__weakptr(&odb, repo)) < 0)
|
||||
return error;
|
||||
if (!content_path) {
|
||||
if (git_repository__ensure_not_bare(repo, "create blob from file") < 0)
|
||||
return GIT_EBAREREPO;
|
||||
|
||||
if (git_buf_joinpath(
|
||||
&path, git_repository_workdir(repo), hint_path) < 0)
|
||||
return -1;
|
||||
|
||||
content_path = path.ptr;
|
||||
}
|
||||
|
||||
if ((error = git_path_lstat(content_path, &st)) < 0 ||
|
||||
(error = git_repository_odb(&odb, repo)) < 0)
|
||||
goto done;
|
||||
|
||||
if (out_st)
|
||||
memcpy(out_st, &st, sizeof(st));
|
||||
|
||||
size = st.st_size;
|
||||
mode = hint_mode ? hint_mode : st.st_mode;
|
||||
|
||||
if (S_ISLNK(st.st_mode)) {
|
||||
if (S_ISLNK(mode)) {
|
||||
error = write_symlink(oid, odb, content_path, (size_t)size);
|
||||
} else {
|
||||
git_vector write_filters = GIT_VECTOR_INIT;
|
||||
@ -187,7 +216,8 @@ static int blob_create_internal(git_oid *oid, git_repository *repo, const char *
|
||||
error = write_file_stream(oid, odb, content_path, size);
|
||||
} else {
|
||||
/* We need to apply one or more filters */
|
||||
error = write_file_filtered(oid, odb, content_path, &write_filters);
|
||||
error = write_file_filtered(
|
||||
oid, &size, odb, content_path, &write_filters);
|
||||
}
|
||||
|
||||
git_filters_free(&write_filters);
|
||||
@ -207,34 +237,21 @@ static int blob_create_internal(git_oid *oid, git_repository *repo, const char *
|
||||
*/
|
||||
}
|
||||
|
||||
done:
|
||||
git_odb_free(odb);
|
||||
git_buf_free(&path);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_blob_create_fromworkdir(git_oid *oid, git_repository *repo, const char *path)
|
||||
int git_blob_create_fromworkdir(
|
||||
git_oid *oid, git_repository *repo, const char *path)
|
||||
{
|
||||
git_buf full_path = GIT_BUF_INIT;
|
||||
const char *workdir;
|
||||
int error;
|
||||
|
||||
if ((error = git_repository__ensure_not_bare(repo, "create blob from file")) < 0)
|
||||
return error;
|
||||
|
||||
workdir = git_repository_workdir(repo);
|
||||
|
||||
if (git_buf_joinpath(&full_path, workdir, path) < 0) {
|
||||
git_buf_free(&full_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = blob_create_internal(
|
||||
oid, repo, git_buf_cstr(&full_path),
|
||||
git_buf_cstr(&full_path) + strlen(workdir), true);
|
||||
|
||||
git_buf_free(&full_path);
|
||||
return error;
|
||||
return git_blob__create_from_paths(oid, NULL, repo, NULL, path, 0, true);
|
||||
}
|
||||
|
||||
int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *path)
|
||||
int git_blob_create_fromdisk(
|
||||
git_oid *oid, git_repository *repo, const char *path)
|
||||
{
|
||||
int error;
|
||||
git_buf full_path = GIT_BUF_INIT;
|
||||
@ -251,8 +268,8 @@ int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *pat
|
||||
if (workdir && !git__prefixcmp(hintpath, workdir))
|
||||
hintpath += strlen(workdir);
|
||||
|
||||
error = blob_create_internal(
|
||||
oid, repo, git_buf_cstr(&full_path), hintpath, true);
|
||||
error = git_blob__create_from_paths(
|
||||
oid, NULL, repo, git_buf_cstr(&full_path), hintpath, 0, true);
|
||||
|
||||
git_buf_free(&full_path);
|
||||
return error;
|
||||
@ -272,11 +289,8 @@ int git_blob_create_fromchunks(
|
||||
git_filebuf file = GIT_FILEBUF_INIT;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
|
||||
if (git_buf_join_n(
|
||||
&path, '/', 3,
|
||||
git_repository_path(repo),
|
||||
GIT_OBJECTS_DIR,
|
||||
"streamed") < 0)
|
||||
if (git_buf_joinpath(
|
||||
&path, git_repository_path(repo), GIT_OBJECTS_DIR "streamed") < 0)
|
||||
goto cleanup;
|
||||
|
||||
content = git__malloc(BUFFER_SIZE);
|
||||
@ -303,7 +317,8 @@ int git_blob_create_fromchunks(
|
||||
if (git_filebuf_flush(&file) < 0)
|
||||
goto cleanup;
|
||||
|
||||
error = blob_create_internal(oid, repo, file.path_lock, hintpath, hintpath != NULL);
|
||||
error = git_blob__create_from_paths(
|
||||
oid, NULL, repo, file.path_lock, hintpath, 0, hintpath != NULL);
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&path);
|
||||
|
@ -21,4 +21,13 @@ 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);
|
||||
|
||||
extern int git_blob__create_from_paths(
|
||||
git_oid *out_oid,
|
||||
struct stat *out_st,
|
||||
git_repository *repo,
|
||||
const char *full_path,
|
||||
const char *hint_path,
|
||||
mode_t hint_mode,
|
||||
bool apply_filters);
|
||||
|
||||
#endif
|
||||
|
@ -132,7 +132,7 @@ int git_branch_foreach(
|
||||
{
|
||||
git_reference_iterator *iter;
|
||||
git_reference *ref;
|
||||
int error;
|
||||
int error = 0;
|
||||
|
||||
if (git_reference_iterator_new(&iter, repo) < 0)
|
||||
return -1;
|
||||
@ -143,7 +143,6 @@ int git_branch_foreach(
|
||||
if (callback(ref->name + strlen(GIT_REFS_HEADS_DIR),
|
||||
GIT_BRANCH_LOCAL, payload)) {
|
||||
error = GIT_EUSER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,11 +151,14 @@ int git_branch_foreach(
|
||||
if (callback(ref->name + strlen(GIT_REFS_REMOTES_DIR),
|
||||
GIT_BRANCH_REMOTE, payload)) {
|
||||
error = GIT_EUSER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
git_reference_free(ref);
|
||||
|
||||
/* check if the callback has cancelled iteration */
|
||||
if (error == GIT_EUSER)
|
||||
break;
|
||||
}
|
||||
|
||||
if (error == GIT_ITEROVER)
|
||||
|
@ -170,8 +170,14 @@ int git_buf_text_common_prefix(git_buf *buf, const git_strarray *strings)
|
||||
bool git_buf_text_is_binary(const git_buf *buf)
|
||||
{
|
||||
const char *scan = buf->ptr, *end = buf->ptr + buf->size;
|
||||
git_bom_t bom;
|
||||
int printable = 0, nonprintable = 0;
|
||||
|
||||
scan += git_buf_text_detect_bom(&bom, buf, 0);
|
||||
|
||||
if (bom > GIT_BOM_UTF8)
|
||||
return 1;
|
||||
|
||||
while (scan < end) {
|
||||
unsigned char c = *scan++;
|
||||
|
||||
@ -262,7 +268,7 @@ bool git_buf_text_gather_stats(
|
||||
while (scan < end) {
|
||||
unsigned char c = *scan++;
|
||||
|
||||
if ((c > 0x1F && c < 0x7F) || c > 0x9f)
|
||||
if (c > 0x1F && c != 0x7F)
|
||||
stats->printable++;
|
||||
else switch (c) {
|
||||
case '\0':
|
||||
|
@ -259,6 +259,15 @@ void git_buf_truncate(git_buf *buf, size_t len)
|
||||
}
|
||||
}
|
||||
|
||||
void git_buf_shorten(git_buf *buf, size_t amount)
|
||||
{
|
||||
if (amount > buf->size)
|
||||
amount = buf->size;
|
||||
|
||||
buf->size = buf->size - amount;
|
||||
buf->ptr[buf->size] = '\0';
|
||||
}
|
||||
|
||||
void git_buf_rtruncate_at_char(git_buf *buf, char separator)
|
||||
{
|
||||
ssize_t idx = git_buf_rfind_next(buf, separator);
|
||||
|
@ -91,6 +91,7 @@ int git_buf_vprintf(git_buf *buf, const char *format, va_list ap);
|
||||
void git_buf_clear(git_buf *buf);
|
||||
void git_buf_consume(git_buf *buf, const char *end);
|
||||
void git_buf_truncate(git_buf *buf, size_t len);
|
||||
void git_buf_shorten(git_buf *buf, size_t amount);
|
||||
void git_buf_rtruncate_at_char(git_buf *path, char separator);
|
||||
|
||||
int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
|
||||
|
@ -54,8 +54,12 @@
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
typedef unsigned char bool;
|
||||
# ifndef true
|
||||
# define true 1
|
||||
# endif
|
||||
# ifndef false
|
||||
# define false 0
|
||||
# endif
|
||||
#else
|
||||
# include <stdbool.h>
|
||||
#endif
|
||||
|
@ -220,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);
|
||||
@ -244,10 +246,10 @@ static int checkout_action_wd_only(
|
||||
bool remove = false;
|
||||
git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE;
|
||||
|
||||
if (!git_pathspec_match_path(
|
||||
if (!git_pathspec__match(
|
||||
pathspec, wd->path,
|
||||
(data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
|
||||
git_iterator_ignore_case(workdir), NULL))
|
||||
git_iterator_ignore_case(workdir), NULL, NULL))
|
||||
return 0;
|
||||
|
||||
/* check if item is tracked in the index but not in the checkout diff */
|
||||
@ -605,7 +607,7 @@ static int checkout_get_actions(
|
||||
uint32_t *actions = NULL;
|
||||
|
||||
if (data->opts.paths.count > 0 &&
|
||||
git_pathspec_init(&pathspec, &data->opts.paths, &pathpool) < 0)
|
||||
git_pathspec__vinit(&pathspec, &data->opts.paths, &pathpool) < 0)
|
||||
return -1;
|
||||
|
||||
if ((error = git_iterator_current(&wditem, workdir)) < 0 &&
|
||||
@ -657,7 +659,7 @@ static int checkout_get_actions(
|
||||
goto fail;
|
||||
}
|
||||
|
||||
git_pathspec_free(&pathspec);
|
||||
git_pathspec__vfree(&pathspec);
|
||||
git_pool_clear(&pathpool);
|
||||
|
||||
return 0;
|
||||
@ -668,7 +670,7 @@ fail:
|
||||
*actions_ptr = NULL;
|
||||
git__free(actions);
|
||||
|
||||
git_pathspec_free(&pathspec);
|
||||
git_pathspec__vfree(&pathspec);
|
||||
git_pool_clear(&pathpool);
|
||||
|
||||
return error;
|
||||
@ -691,17 +693,14 @@ static int buffer_to_file(
|
||||
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 (st != NULL && (error = p_stat(path, st)) < 0)
|
||||
giterr_set(GITERR_OS, "Error statting '%s'", path);
|
||||
|
||||
if ((file_mode & 0100) != 0 && (error = p_chmod(path, file_mode)) < 0) {
|
||||
else if (GIT_PERMS_IS_EXEC(file_mode) &&
|
||||
(error = p_chmod(path, file_mode)) < 0)
|
||||
giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return error;
|
||||
}
|
||||
|
||||
static int blob_content_to_file(
|
||||
@ -856,7 +855,7 @@ 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;
|
||||
|
||||
@ -1028,7 +1027,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;
|
||||
@ -1161,7 +1160,8 @@ static int checkout_data_init(
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((error = git_repository__ensure_not_bare(repo, "checkout")) < 0)
|
||||
if ((!proposed || !proposed->target_directory) &&
|
||||
(error = git_repository__ensure_not_bare(repo, "checkout")) < 0)
|
||||
return error;
|
||||
|
||||
data->repo = repo;
|
||||
@ -1174,6 +1174,13 @@ 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) {
|
||||
git_config *cfg;
|
||||
@ -1236,7 +1243,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);
|
||||
@ -1284,11 +1292,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 */
|
||||
@ -1356,8 +1366,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;
|
||||
@ -1381,8 +1402,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(
|
||||
@ -1407,8 +1439,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)))
|
||||
|
14
src/clone.c
14
src/clone.c
@ -418,7 +418,7 @@ static bool should_checkout(
|
||||
return !git_repository_head_orphan(repo);
|
||||
}
|
||||
|
||||
static void normalize_options(git_clone_options *dst, const git_clone_options *src)
|
||||
static void normalize_options(git_clone_options *dst, const git_clone_options *src, git_repository_init_options *initOptions)
|
||||
{
|
||||
git_clone_options default_options = GIT_CLONE_OPTIONS_INIT;
|
||||
if (!src) src = &default_options;
|
||||
@ -427,6 +427,13 @@ 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->init_options)
|
||||
{
|
||||
dst->init_options = initOptions;
|
||||
initOptions->flags = GIT_REPOSITORY_INIT_MKPATH;
|
||||
if (dst->bare)
|
||||
initOptions->flags |= GIT_REPOSITORY_INIT_BARE;
|
||||
}
|
||||
}
|
||||
|
||||
int git_clone(
|
||||
@ -439,10 +446,11 @@ int git_clone(
|
||||
git_repository *repo = NULL;
|
||||
git_clone_options normOptions;
|
||||
int remove_directory_on_failure = 0;
|
||||
git_repository_init_options initOptions = GIT_REPOSITORY_INIT_OPTIONS_INIT;
|
||||
|
||||
assert(out && url && local_path);
|
||||
|
||||
normalize_options(&normOptions, options);
|
||||
normalize_options(&normOptions, options, &initOptions);
|
||||
GITERR_CHECK_VERSION(&normOptions, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
|
||||
|
||||
/* Only clone to a new directory or an empty directory */
|
||||
@ -455,7 +463,7 @@ int git_clone(
|
||||
/* Only remove the directory on failure if we create it */
|
||||
remove_directory_on_failure = !git_path_exists(local_path);
|
||||
|
||||
if (!(retcode = git_repository_init(&repo, local_path, normOptions.bare))) {
|
||||
if (!(retcode = git_repository_init_ext(&repo, local_path, normOptions.init_options))) {
|
||||
if ((retcode = setup_remotes_and_fetch(repo, url, &normOptions)) < 0) {
|
||||
/* Failed to fetch; clean up */
|
||||
git_repository_free(repo);
|
||||
|
75
src/commit.c
75
src/commit.c
@ -19,30 +19,19 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
static void clear_parents(git_commit *commit)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < commit->parent_ids.length; ++i) {
|
||||
git_oid *parent = git_vector_get(&commit->parent_ids, i);
|
||||
git__free(parent);
|
||||
}
|
||||
|
||||
git_vector_clear(&commit->parent_ids);
|
||||
}
|
||||
|
||||
void git_commit__free(void *_commit)
|
||||
{
|
||||
git_commit *commit = _commit;
|
||||
|
||||
clear_parents(commit);
|
||||
git_vector_free(&commit->parent_ids);
|
||||
git_array_clear(commit->parent_ids);
|
||||
|
||||
git_signature_free(commit->author);
|
||||
git_signature_free(commit->committer);
|
||||
|
||||
git__free(commit->raw_header);
|
||||
git__free(commit->message);
|
||||
git__free(commit->message_encoding);
|
||||
|
||||
git__free(commit);
|
||||
}
|
||||
|
||||
@ -171,12 +160,35 @@ int git_commit_create(
|
||||
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);
|
||||
const char *buffer_start = git_odb_object_data(odb_obj), *buffer;
|
||||
const char *buffer_end = buffer_start + git_odb_object_size(odb_obj);
|
||||
git_oid parent_id;
|
||||
uint32_t parent_count = 0;
|
||||
size_t header_len;
|
||||
|
||||
if (git_vector_init(&commit->parent_ids, 4, NULL) < 0)
|
||||
return -1;
|
||||
/* find end-of-header (counting parents as we go) */
|
||||
for (buffer = buffer_start; buffer < buffer_end; ++buffer) {
|
||||
if (!strncmp("\n\n", buffer, 2)) {
|
||||
++buffer;
|
||||
break;
|
||||
}
|
||||
if (!strncmp("\nparent ", buffer, strlen("\nparent ")))
|
||||
++parent_count;
|
||||
}
|
||||
|
||||
header_len = buffer - buffer_start;
|
||||
commit->raw_header = git__strndup(buffer_start, header_len);
|
||||
GITERR_CHECK_ALLOC(commit->raw_header);
|
||||
|
||||
/* point "buffer" to header data */
|
||||
buffer = commit->raw_header;
|
||||
buffer_end = commit->raw_header + header_len;
|
||||
|
||||
if (parent_count < 1)
|
||||
parent_count = 1;
|
||||
|
||||
git_array_init_to_size(commit->parent_ids, parent_count);
|
||||
GITERR_CHECK_ARRAY(commit->parent_ids);
|
||||
|
||||
if (git_oid__parse(&commit->tree_id, &buffer, buffer_end, "tree ") < 0)
|
||||
goto bad_buffer;
|
||||
@ -186,13 +198,10 @@ int git_commit__parse(void *_commit, git_odb_object *odb_obj)
|
||||
*/
|
||||
|
||||
while (git_oid__parse(&parent_id, &buffer, buffer_end, "parent ") == 0) {
|
||||
git_oid *new_id = git__malloc(sizeof(git_oid));
|
||||
git_oid *new_id = git_array_alloc(commit->parent_ids);
|
||||
GITERR_CHECK_ALLOC(new_id);
|
||||
|
||||
git_oid_cpy(new_id, &parent_id);
|
||||
|
||||
if (git_vector_insert(&commit->parent_ids, new_id) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
commit->author = git__malloc(sizeof(git_signature));
|
||||
@ -208,8 +217,8 @@ int git_commit__parse(void *_commit, git_odb_object *odb_obj)
|
||||
if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
|
||||
return -1;
|
||||
|
||||
/* Parse add'l header entries until blank line found */
|
||||
while (buffer < buffer_end && *buffer != '\n') {
|
||||
/* Parse add'l header entries */
|
||||
while (buffer < buffer_end) {
|
||||
const char *eoln = buffer;
|
||||
while (eoln < buffer_end && *eoln != '\n')
|
||||
++eoln;
|
||||
@ -223,15 +232,18 @@ int git_commit__parse(void *_commit, git_odb_object *odb_obj)
|
||||
|
||||
if (eoln < buffer_end && *eoln == '\n')
|
||||
++eoln;
|
||||
|
||||
buffer = eoln;
|
||||
}
|
||||
|
||||
/* buffer is now at the end of the header, double-check and move forward into the message */
|
||||
if (buffer < buffer_end && *buffer == '\n')
|
||||
buffer++;
|
||||
/* point "buffer" to data after header */
|
||||
buffer = git_odb_object_data(odb_obj);
|
||||
buffer_end = buffer + git_odb_object_size(odb_obj);
|
||||
|
||||
/* parse commit message */
|
||||
buffer += header_len;
|
||||
if (*buffer == '\n')
|
||||
++buffer;
|
||||
|
||||
/* extract commit message */
|
||||
if (buffer <= buffer_end) {
|
||||
commit->message = git__strndup(buffer, buffer_end - buffer);
|
||||
GITERR_CHECK_ALLOC(commit->message);
|
||||
@ -255,9 +267,10 @@ GIT_COMMIT_GETTER(const git_signature *, author, commit->author)
|
||||
GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer)
|
||||
GIT_COMMIT_GETTER(const char *, message, commit->message)
|
||||
GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding)
|
||||
GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header)
|
||||
GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time)
|
||||
GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset)
|
||||
GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)commit->parent_ids.length)
|
||||
GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids))
|
||||
GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id);
|
||||
|
||||
int git_commit_tree(git_tree **tree_out, const git_commit *commit)
|
||||
@ -271,7 +284,7 @@ const git_oid *git_commit_parent_id(
|
||||
{
|
||||
assert(commit);
|
||||
|
||||
return git_vector_get(&commit->parent_ids, n);
|
||||
return git_array_get(commit->parent_ids, n);
|
||||
}
|
||||
|
||||
int git_commit_parent(
|
||||
|
@ -10,14 +10,14 @@
|
||||
#include "git2/commit.h"
|
||||
#include "tree.h"
|
||||
#include "repository.h"
|
||||
#include "vector.h"
|
||||
#include "array.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
struct git_commit {
|
||||
git_object object;
|
||||
|
||||
git_vector parent_ids;
|
||||
git_array_t(git_oid) parent_ids;
|
||||
git_oid tree_id;
|
||||
|
||||
git_signature *author;
|
||||
@ -25,6 +25,7 @@ struct git_commit {
|
||||
|
||||
char *message_encoding;
|
||||
char *message;
|
||||
char *raw_header;
|
||||
};
|
||||
|
||||
void git_commit__free(void *commit);
|
||||
|
@ -36,7 +36,7 @@ git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_
|
||||
git_commit_list *p;
|
||||
|
||||
while ((p = *pp) != NULL) {
|
||||
if (git_commit_list_time_cmp(p->item, item) < 0)
|
||||
if (git_commit_list_time_cmp(p->item, item) > 0)
|
||||
break;
|
||||
|
||||
pp = &p->next;
|
||||
|
393
src/config.c
393
src/config.c
@ -315,30 +315,241 @@ int git_config_refresh(git_config *cfg)
|
||||
* Loop over all the variables
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
git_config_iterator parent;
|
||||
git_config_iterator *current;
|
||||
const git_config *cfg;
|
||||
regex_t regex;
|
||||
int has_regex;
|
||||
size_t i;
|
||||
} all_iter;
|
||||
|
||||
static int find_next_backend(size_t *out, const git_config *cfg, size_t i)
|
||||
{
|
||||
file_internal *internal;
|
||||
|
||||
for (; i > 0; --i) {
|
||||
internal = git_vector_get(&cfg->files, i - 1);
|
||||
if (!internal || !internal->file)
|
||||
continue;
|
||||
|
||||
*out = i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
|
||||
{
|
||||
all_iter *iter = (all_iter *) _iter;
|
||||
file_internal *internal;
|
||||
git_config_backend *backend;
|
||||
size_t i;
|
||||
int error = 0;
|
||||
|
||||
if (iter->current != NULL &&
|
||||
(error = iter->current->next(entry, iter->current)) == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (error < 0 && error != GIT_ITEROVER)
|
||||
return error;
|
||||
|
||||
do {
|
||||
if (find_next_backend(&i, iter->cfg, iter->i) < 0)
|
||||
return GIT_ITEROVER;
|
||||
|
||||
internal = git_vector_get(&iter->cfg->files, i - 1);
|
||||
backend = internal->file;
|
||||
iter->i = i - 1;
|
||||
|
||||
if (iter->current)
|
||||
iter->current->free(iter->current);
|
||||
|
||||
iter->current = NULL;
|
||||
error = backend->iterator(&iter->current, backend);
|
||||
if (error == GIT_ENOTFOUND)
|
||||
continue;
|
||||
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
error = iter->current->next(entry, iter->current);
|
||||
/* If this backend is empty, then keep going */
|
||||
if (error == GIT_ITEROVER)
|
||||
continue;
|
||||
|
||||
return error;
|
||||
|
||||
} while(1);
|
||||
|
||||
return GIT_ITEROVER;
|
||||
}
|
||||
|
||||
static int all_iter_glob_next(git_config_entry **entry, git_config_iterator *_iter)
|
||||
{
|
||||
int error;
|
||||
all_iter *iter = (all_iter *) _iter;
|
||||
|
||||
/*
|
||||
* We use the "normal" function to grab the next one across
|
||||
* backends and then apply the regex
|
||||
*/
|
||||
while ((error = all_iter_next(entry, _iter)) == 0) {
|
||||
/* skip non-matching keys if regexp was provided */
|
||||
if (regexec(&iter->regex, (*entry)->name, 0, NULL, 0) != 0)
|
||||
continue;
|
||||
|
||||
/* and simply return if we like the entry's name */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void all_iter_free(git_config_iterator *_iter)
|
||||
{
|
||||
all_iter *iter = (all_iter *) _iter;
|
||||
|
||||
if (iter->current)
|
||||
iter->current->free(iter->current);
|
||||
|
||||
git__free(iter);
|
||||
}
|
||||
|
||||
static void all_iter_glob_free(git_config_iterator *_iter)
|
||||
{
|
||||
all_iter *iter = (all_iter *) _iter;
|
||||
|
||||
regfree(&iter->regex);
|
||||
all_iter_free(_iter);
|
||||
}
|
||||
|
||||
int git_config_iterator_new(git_config_iterator **out, const git_config *cfg)
|
||||
{
|
||||
all_iter *iter;
|
||||
|
||||
iter = git__calloc(1, sizeof(all_iter));
|
||||
GITERR_CHECK_ALLOC(iter);
|
||||
|
||||
iter->parent.free = all_iter_free;
|
||||
iter->parent.next = all_iter_next;
|
||||
|
||||
iter->i = cfg->files.length;
|
||||
iter->cfg = cfg;
|
||||
|
||||
*out = (git_config_iterator *) iter;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp)
|
||||
{
|
||||
all_iter *iter;
|
||||
int result;
|
||||
|
||||
if (regexp == NULL)
|
||||
return git_config_iterator_new(out, cfg);
|
||||
|
||||
iter = git__calloc(1, sizeof(all_iter));
|
||||
GITERR_CHECK_ALLOC(iter);
|
||||
|
||||
if ((result = regcomp(&iter->regex, regexp, REG_EXTENDED)) < 0) {
|
||||
giterr_set_regex(&iter->regex, result);
|
||||
regfree(&iter->regex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
iter->parent.next = all_iter_glob_next;
|
||||
iter->parent.free = all_iter_glob_free;
|
||||
iter->i = cfg->files.length;
|
||||
iter->cfg = cfg;
|
||||
|
||||
*out = (git_config_iterator *) iter;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_config_foreach(
|
||||
const git_config *cfg, git_config_foreach_cb cb, void *payload)
|
||||
{
|
||||
return git_config_foreach_match(cfg, NULL, cb, payload);
|
||||
}
|
||||
|
||||
int git_config_backend_foreach_match(
|
||||
git_config_backend *backend,
|
||||
const char *regexp,
|
||||
int (*fn)(const git_config_entry *, void *),
|
||||
void *data)
|
||||
{
|
||||
git_config_entry *entry;
|
||||
git_config_iterator* iter;
|
||||
regex_t regex;
|
||||
int result = 0;
|
||||
|
||||
if (regexp != NULL) {
|
||||
if ((result = regcomp(®ex, regexp, REG_EXTENDED)) < 0) {
|
||||
giterr_set_regex(®ex, result);
|
||||
regfree(®ex);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((result = backend->iterator(&iter, backend)) < 0) {
|
||||
iter = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
while(!(iter->next(&entry, iter) < 0)) {
|
||||
/* skip non-matching keys if regexp was provided */
|
||||
if (regexp && regexec(®ex, entry->name, 0, NULL, 0) != 0)
|
||||
continue;
|
||||
|
||||
/* abort iterator on non-zero return value */
|
||||
if (fn(entry, data)) {
|
||||
giterr_clear();
|
||||
result = GIT_EUSER;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (regexp != NULL)
|
||||
regfree(®ex);
|
||||
|
||||
iter->free(iter);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int git_config_foreach_match(
|
||||
const git_config *cfg,
|
||||
const char *regexp,
|
||||
git_config_foreach_cb cb,
|
||||
void *payload)
|
||||
{
|
||||
int ret = 0;
|
||||
size_t i;
|
||||
file_internal *internal;
|
||||
git_config_backend *file;
|
||||
int error;
|
||||
git_config_iterator *iter;
|
||||
git_config_entry *entry;
|
||||
|
||||
for (i = 0; i < cfg->files.length && ret == 0; ++i) {
|
||||
internal = git_vector_get(&cfg->files, i);
|
||||
file = internal->file;
|
||||
ret = file->foreach(file, regexp, cb, payload);
|
||||
if ((error = git_config_iterator_glob_new(&iter, cfg, regexp)) < 0)
|
||||
return error;
|
||||
|
||||
while ((error = git_config_next(&entry, iter)) == 0) {
|
||||
if(cb(entry, payload)) {
|
||||
giterr_clear();
|
||||
error = GIT_EUSER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
git_config_iterator_free(iter);
|
||||
|
||||
if (error == GIT_ITEROVER)
|
||||
error = 0;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/**************
|
||||
@ -528,31 +739,114 @@ int git_config_get_entry(const git_config_entry **out, const git_config *cfg, co
|
||||
return config_error_notfound(name);
|
||||
}
|
||||
|
||||
int git_config_get_multivar(
|
||||
int git_config_get_multivar_foreach(
|
||||
const git_config *cfg, const char *name, const char *regexp,
|
||||
git_config_foreach_cb cb, void *payload)
|
||||
{
|
||||
file_internal *internal;
|
||||
git_config_backend *file;
|
||||
int ret = GIT_ENOTFOUND;
|
||||
size_t i;
|
||||
int err, found;
|
||||
git_config_iterator *iter;
|
||||
git_config_entry *entry;
|
||||
|
||||
/*
|
||||
* This loop runs the "wrong" way 'round because we need to
|
||||
* look at every value from the most general to most specific
|
||||
*/
|
||||
for (i = cfg->files.length; i > 0; --i) {
|
||||
internal = git_vector_get(&cfg->files, i - 1);
|
||||
if (!internal || !internal->file)
|
||||
continue;
|
||||
file = internal->file;
|
||||
if ((err = git_config_multivar_iterator_new(&iter, cfg, name, regexp)) < 0)
|
||||
return err;
|
||||
|
||||
ret = file->get_multivar(file, name, regexp, cb, payload);
|
||||
if (ret < 0 && ret != GIT_ENOTFOUND)
|
||||
return ret;
|
||||
found = 0;
|
||||
while ((err = iter->next(&entry, iter)) == 0) {
|
||||
found = 1;
|
||||
if(cb(entry, payload)) {
|
||||
iter->free(iter);
|
||||
return GIT_EUSER;
|
||||
}
|
||||
}
|
||||
|
||||
return (ret == GIT_ENOTFOUND) ? config_error_notfound(name) : 0;
|
||||
iter->free(iter);
|
||||
if (err == GIT_ITEROVER)
|
||||
err = 0;
|
||||
|
||||
if (found == 0 && err == 0)
|
||||
err = config_error_notfound(name);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
git_config_iterator parent;
|
||||
git_config_iterator *iter;
|
||||
char *name;
|
||||
regex_t regex;
|
||||
int have_regex;
|
||||
} multivar_iter;
|
||||
|
||||
static int multivar_iter_next(git_config_entry **entry, git_config_iterator *_iter)
|
||||
{
|
||||
multivar_iter *iter = (multivar_iter *) _iter;
|
||||
int error = 0;
|
||||
|
||||
while ((error = iter->iter->next(entry, iter->iter)) == 0) {
|
||||
if (git__strcmp(iter->name, (*entry)->name))
|
||||
continue;
|
||||
|
||||
if (!iter->have_regex)
|
||||
return 0;
|
||||
|
||||
if (regexec(&iter->regex, (*entry)->value, 0, NULL, 0) == 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void multivar_iter_free(git_config_iterator *_iter)
|
||||
{
|
||||
multivar_iter *iter = (multivar_iter *) _iter;
|
||||
|
||||
iter->iter->free(iter->iter);
|
||||
|
||||
git__free(iter->name);
|
||||
regfree(&iter->regex);
|
||||
git__free(iter);
|
||||
}
|
||||
|
||||
int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp)
|
||||
{
|
||||
multivar_iter *iter = NULL;
|
||||
git_config_iterator *inner = NULL;
|
||||
int error;
|
||||
|
||||
if ((error = git_config_iterator_new(&inner, cfg)) < 0)
|
||||
return error;
|
||||
|
||||
iter = git__calloc(1, sizeof(multivar_iter));
|
||||
GITERR_CHECK_ALLOC(iter);
|
||||
|
||||
if ((error = git_config__normalize_name(name, &iter->name)) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (regexp != NULL) {
|
||||
error = regcomp(&iter->regex, regexp, REG_EXTENDED);
|
||||
if (error < 0) {
|
||||
giterr_set_regex(&iter->regex, error);
|
||||
error = -1;
|
||||
regfree(&iter->regex);
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
iter->have_regex = 1;
|
||||
}
|
||||
|
||||
iter->iter = inner;
|
||||
iter->parent.free = multivar_iter_free;
|
||||
iter->parent.next = multivar_iter_next;
|
||||
|
||||
*out = (git_config_iterator *) iter;
|
||||
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
|
||||
inner->free(inner);
|
||||
git__free(iter);
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
|
||||
@ -568,6 +862,16 @@ int git_config_set_multivar(git_config *cfg, const char *name, const char *regex
|
||||
return file->set_multivar(file, name, regexp, value);
|
||||
}
|
||||
|
||||
int git_config_next(git_config_entry **entry, git_config_iterator *iter)
|
||||
{
|
||||
return iter->next(entry, iter);
|
||||
}
|
||||
|
||||
void git_config_iterator_free(git_config_iterator *iter)
|
||||
{
|
||||
iter->free(iter);
|
||||
}
|
||||
|
||||
static int git_config__find_file_to_path(
|
||||
char *out, size_t outlen, int (*find)(git_buf *buf))
|
||||
{
|
||||
@ -811,6 +1115,41 @@ fail_parse:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Take something the user gave us and make it nice for our hash function */
|
||||
int git_config__normalize_name(const char *in, char **out)
|
||||
{
|
||||
char *name, *fdot, *ldot;
|
||||
|
||||
assert(in && out);
|
||||
|
||||
name = git__strdup(in);
|
||||
GITERR_CHECK_ALLOC(name);
|
||||
|
||||
fdot = strchr(name, '.');
|
||||
ldot = strrchr(name, '.');
|
||||
|
||||
if (fdot == NULL || fdot == name || ldot == NULL || !ldot[1])
|
||||
goto invalid;
|
||||
|
||||
/* Validate and downcase up to first dot and after last dot */
|
||||
if (git_config_file_normalize_section(name, fdot) < 0 ||
|
||||
git_config_file_normalize_section(ldot + 1, NULL) < 0)
|
||||
goto invalid;
|
||||
|
||||
/* If there is a middle range, make sure it doesn't have newlines */
|
||||
while (fdot < ldot)
|
||||
if (*fdot++ == '\n')
|
||||
goto invalid;
|
||||
|
||||
*out = name;
|
||||
return 0;
|
||||
|
||||
invalid:
|
||||
git__free(name);
|
||||
giterr_set(GITERR_CONFIG, "Invalid config item name '%s'", in);
|
||||
return GIT_EINVALIDSPEC;
|
||||
}
|
||||
|
||||
struct rename_data {
|
||||
git_config *config;
|
||||
git_buf *name;
|
||||
|
@ -49,4 +49,7 @@ extern int git_config_rename_section(
|
||||
*/
|
||||
extern int git_config_file__ondisk(struct git_config_backend **out, const char *path);
|
||||
|
||||
extern int git_config__normalize_name(const char *in, char **out);
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -27,6 +27,13 @@ typedef struct cvar_t {
|
||||
git_config_entry *entry;
|
||||
} cvar_t;
|
||||
|
||||
typedef struct git_config_file_iter {
|
||||
git_config_iterator parent;
|
||||
git_strmap_iter iter;
|
||||
cvar_t* next_var;
|
||||
} git_config_file_iter;
|
||||
|
||||
|
||||
#define CVAR_LIST_HEAD(list) ((list)->head)
|
||||
|
||||
#define CVAR_LIST_TAIL(list) ((list)->tail)
|
||||
@ -129,41 +136,6 @@ int git_config_file_normalize_section(char *start, char *end)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Take something the user gave us and make it nice for our hash function */
|
||||
static int normalize_name(const char *in, char **out)
|
||||
{
|
||||
char *name, *fdot, *ldot;
|
||||
|
||||
assert(in && out);
|
||||
|
||||
name = git__strdup(in);
|
||||
GITERR_CHECK_ALLOC(name);
|
||||
|
||||
fdot = strchr(name, '.');
|
||||
ldot = strrchr(name, '.');
|
||||
|
||||
if (fdot == NULL || fdot == name || ldot == NULL || !ldot[1])
|
||||
goto invalid;
|
||||
|
||||
/* Validate and downcase up to first dot and after last dot */
|
||||
if (git_config_file_normalize_section(name, fdot) < 0 ||
|
||||
git_config_file_normalize_section(ldot + 1, NULL) < 0)
|
||||
goto invalid;
|
||||
|
||||
/* If there is a middle range, make sure it doesn't have newlines */
|
||||
while (fdot < ldot)
|
||||
if (*fdot++ == '\n')
|
||||
goto invalid;
|
||||
|
||||
*out = name;
|
||||
return 0;
|
||||
|
||||
invalid:
|
||||
git__free(name);
|
||||
giterr_set(GITERR_CONFIG, "Invalid config item name '%s'", in);
|
||||
return GIT_EINVALIDSPEC;
|
||||
}
|
||||
|
||||
static void free_vars(git_strmap *values)
|
||||
{
|
||||
cvar_t *var = NULL;
|
||||
@ -247,51 +219,56 @@ static void backend_free(git_config_backend *_backend)
|
||||
git__free(backend);
|
||||
}
|
||||
|
||||
static int file_foreach(
|
||||
git_config_backend *backend,
|
||||
const char *regexp,
|
||||
int (*fn)(const git_config_entry *, void *),
|
||||
void *data)
|
||||
static void config_iterator_free(
|
||||
git_config_iterator* iter)
|
||||
{
|
||||
git__free(iter);
|
||||
}
|
||||
|
||||
static int config_iterator_next(
|
||||
git_config_entry **entry,
|
||||
git_config_iterator *iter)
|
||||
{
|
||||
git_config_file_iter *it = (git_config_file_iter *) iter;
|
||||
diskfile_backend *b = (diskfile_backend *) it->parent.backend;
|
||||
int err = 0;
|
||||
cvar_t * var;
|
||||
|
||||
if (it->next_var == NULL) {
|
||||
err = git_strmap_next((void**) &var, &(it->iter), b->values);
|
||||
} else {
|
||||
var = it->next_var;
|
||||
}
|
||||
|
||||
if (err < 0) {
|
||||
it->next_var = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
*entry = var->entry;
|
||||
it->next_var = CVAR_LIST_NEXT(var);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_iterator_new(
|
||||
git_config_iterator **iter,
|
||||
struct git_config_backend* backend)
|
||||
{
|
||||
diskfile_backend *b = (diskfile_backend *)backend;
|
||||
cvar_t *var, *next_var;
|
||||
const char *key;
|
||||
regex_t regex;
|
||||
int result = 0;
|
||||
git_config_file_iter *it = git__calloc(1, sizeof(git_config_file_iter));
|
||||
|
||||
GITERR_CHECK_ALLOC(it);
|
||||
|
||||
it->parent.backend = backend;
|
||||
it->iter = git_strmap_begin(b->values);
|
||||
it->next_var = NULL;
|
||||
|
||||
it->parent.next = config_iterator_next;
|
||||
it->parent.free = config_iterator_free;
|
||||
*iter = (git_config_iterator *) it;
|
||||
|
||||
if (!b->values)
|
||||
return 0;
|
||||
|
||||
if (regexp != NULL) {
|
||||
if ((result = regcomp(®ex, regexp, REG_EXTENDED)) < 0) {
|
||||
giterr_set_regex(®ex, result);
|
||||
regfree(®ex);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
git_strmap_foreach(b->values, key, var,
|
||||
for (; var != NULL; var = next_var) {
|
||||
next_var = CVAR_LIST_NEXT(var);
|
||||
|
||||
/* skip non-matching keys if regexp was provided */
|
||||
if (regexp && regexec(®ex, key, 0, NULL, 0) != 0)
|
||||
continue;
|
||||
|
||||
/* abort iterator on non-zero return value */
|
||||
if (fn(var->entry, data)) {
|
||||
giterr_clear();
|
||||
result = GIT_EUSER;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
cleanup:
|
||||
if (regexp != NULL)
|
||||
regfree(®ex);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int config_set(git_config_backend *cfg, const char *name, const char *value)
|
||||
@ -302,7 +279,7 @@ static int config_set(git_config_backend *cfg, const char *name, const char *val
|
||||
khiter_t pos;
|
||||
int rval, ret;
|
||||
|
||||
if ((rval = normalize_name(name, &key)) < 0)
|
||||
if ((rval = git_config__normalize_name(name, &key)) < 0)
|
||||
return rval;
|
||||
|
||||
/*
|
||||
@ -385,7 +362,7 @@ static int config_get(const git_config_backend *cfg, const char *name, const git
|
||||
khiter_t pos;
|
||||
int error;
|
||||
|
||||
if ((error = normalize_name(name, &key)) < 0)
|
||||
if ((error = git_config__normalize_name(name, &key)) < 0)
|
||||
return error;
|
||||
|
||||
pos = git_strmap_lookup_index(b->values, key);
|
||||
@ -400,70 +377,6 @@ static int config_get(const git_config_backend *cfg, const char *name, const git
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_get_multivar(
|
||||
git_config_backend *cfg,
|
||||
const char *name,
|
||||
const char *regex_str,
|
||||
int (*fn)(const git_config_entry *, void *),
|
||||
void *data)
|
||||
{
|
||||
cvar_t *var;
|
||||
diskfile_backend *b = (diskfile_backend *)cfg;
|
||||
char *key;
|
||||
khiter_t pos;
|
||||
int error;
|
||||
|
||||
if ((error = normalize_name(name, &key)) < 0)
|
||||
return error;
|
||||
|
||||
pos = git_strmap_lookup_index(b->values, key);
|
||||
git__free(key);
|
||||
|
||||
if (!git_strmap_valid_index(b->values, pos))
|
||||
return GIT_ENOTFOUND;
|
||||
|
||||
var = git_strmap_value_at(b->values, pos);
|
||||
|
||||
if (regex_str != NULL) {
|
||||
regex_t regex;
|
||||
int result;
|
||||
|
||||
/* regex matching; build the regex */
|
||||
result = regcomp(®ex, regex_str, REG_EXTENDED);
|
||||
if (result < 0) {
|
||||
giterr_set_regex(®ex, result);
|
||||
regfree(®ex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* and throw the callback only on the variables that
|
||||
* match the regex */
|
||||
do {
|
||||
if (regexec(®ex, var->entry->value, 0, NULL, 0) == 0) {
|
||||
/* early termination by the user is not an error;
|
||||
* just break and return successfully */
|
||||
if (fn(var->entry, data) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
var = var->next;
|
||||
} while (var != NULL);
|
||||
regfree(®ex);
|
||||
} else {
|
||||
/* no regex; go through all the variables */
|
||||
do {
|
||||
/* early termination by the user is not an error;
|
||||
* just break and return successfully */
|
||||
if (fn(var->entry, data) < 0)
|
||||
break;
|
||||
|
||||
var = var->next;
|
||||
} while (var != NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int config_set_multivar(
|
||||
git_config_backend *cfg, const char *name, const char *regexp, const char *value)
|
||||
{
|
||||
@ -477,7 +390,7 @@ static int config_set_multivar(
|
||||
|
||||
assert(regexp);
|
||||
|
||||
if ((result = normalize_name(name, &key)) < 0)
|
||||
if ((result = git_config__normalize_name(name, &key)) < 0)
|
||||
return result;
|
||||
|
||||
pos = git_strmap_lookup_index(b->values, key);
|
||||
@ -550,7 +463,7 @@ static int config_delete(git_config_backend *cfg, const char *name)
|
||||
int result;
|
||||
khiter_t pos;
|
||||
|
||||
if ((result = normalize_name(name, &key)) < 0)
|
||||
if ((result = git_config__normalize_name(name, &key)) < 0)
|
||||
return result;
|
||||
|
||||
pos = git_strmap_lookup_index(b->values, key);
|
||||
@ -590,11 +503,10 @@ int git_config_file__ondisk(git_config_backend **out, const char *path)
|
||||
|
||||
backend->parent.open = config_open;
|
||||
backend->parent.get = config_get;
|
||||
backend->parent.get_multivar = config_get_multivar;
|
||||
backend->parent.set = config_set;
|
||||
backend->parent.set_multivar = config_set_multivar;
|
||||
backend->parent.del = config_delete;
|
||||
backend->parent.foreach = file_foreach;
|
||||
backend->parent.iterator = config_iterator_new;
|
||||
backend->parent.refresh = config_refresh;
|
||||
backend->parent.free = backend_free;
|
||||
|
||||
@ -792,6 +704,11 @@ static int parse_section_header_ext(diskfile_backend *cfg, const char *line, con
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
set_parse_error(cfg, 0, "Unexpected end-of-line in section header");
|
||||
git_buf_free(&buf);
|
||||
return -1;
|
||||
|
||||
case '"':
|
||||
++quote_marks;
|
||||
continue;
|
||||
@ -801,6 +718,12 @@ static int parse_section_header_ext(diskfile_backend *cfg, const char *line, con
|
||||
|
||||
switch (c) {
|
||||
case '"':
|
||||
if (&line[rpos-1] == last_quote) {
|
||||
set_parse_error(cfg, 0, "Missing closing quotation mark in section header");
|
||||
git_buf_free(&buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
case '\\':
|
||||
break;
|
||||
|
||||
@ -1293,6 +1216,9 @@ static char *escape_value(const char *ptr)
|
||||
assert(ptr);
|
||||
|
||||
len = strlen(ptr);
|
||||
if (!len)
|
||||
return git__calloc(1, sizeof(char));
|
||||
|
||||
git_buf_grow(&buf, len);
|
||||
|
||||
while (*ptr != '\0') {
|
||||
@ -1395,7 +1321,7 @@ static int parse_multiline_variable(diskfile_backend *cfg, git_buf *value, int i
|
||||
* standard, this character **has** to be last one in the buf, with
|
||||
* no whitespace after it */
|
||||
assert(is_multiline_var(value->ptr));
|
||||
git_buf_truncate(value, git_buf_len(value) - 1);
|
||||
git_buf_shorten(value, 1);
|
||||
|
||||
proc_line = fixup_line(line, in_quotes);
|
||||
if (proc_line == NULL) {
|
||||
|
@ -42,7 +42,7 @@ GIT_INLINE(int) git_config_file_foreach(
|
||||
int (*fn)(const git_config_entry *entry, void *data),
|
||||
void *data)
|
||||
{
|
||||
return cfg->foreach(cfg, NULL, fn, data);
|
||||
return git_config_backend_foreach_match(cfg, NULL, fn, data);
|
||||
}
|
||||
|
||||
GIT_INLINE(int) git_config_file_foreach_match(
|
||||
@ -51,7 +51,7 @@ GIT_INLINE(int) git_config_file_foreach_match(
|
||||
int (*fn)(const git_config_entry *entry, void *data),
|
||||
void *data)
|
||||
{
|
||||
return cfg->foreach(cfg, regexp, fn, data);
|
||||
return git_config_backend_foreach_match(cfg, regexp, fn, data);
|
||||
}
|
||||
|
||||
extern int git_config_file_normalize_section(char *start, char *end);
|
||||
|
12
src/date.c
12
src/date.c
@ -823,15 +823,13 @@ static void pending_number(struct tm *tm, int *num)
|
||||
}
|
||||
|
||||
static git_time_t approxidate_str(const char *date,
|
||||
const struct timeval *tv,
|
||||
time_t time_sec,
|
||||
int *error_ret)
|
||||
{
|
||||
int number = 0;
|
||||
int touched = 0;
|
||||
struct tm tm = {0}, now;
|
||||
time_t time_sec;
|
||||
|
||||
time_sec = tv->tv_sec;
|
||||
p_localtime_r(&time_sec, &tm);
|
||||
now = tm;
|
||||
|
||||
@ -861,7 +859,7 @@ static git_time_t approxidate_str(const char *date,
|
||||
|
||||
int git__date_parse(git_time_t *out, const char *date)
|
||||
{
|
||||
struct timeval tv;
|
||||
time_t time_sec;
|
||||
git_time_t timestamp;
|
||||
int offset, error_ret=0;
|
||||
|
||||
@ -870,7 +868,9 @@ int git__date_parse(git_time_t *out, const char *date)
|
||||
return 0;
|
||||
}
|
||||
|
||||
p_gettimeofday(&tv, NULL);
|
||||
*out = approxidate_str(date, &tv, &error_ret);
|
||||
if (time(&time_sec) == -1)
|
||||
return -1;
|
||||
|
||||
*out = approxidate_str(date, time_sec, &error_ret);
|
||||
return error_ret;
|
||||
}
|
||||
|
265
src/diff.c
265
src/diff.c
@ -13,6 +13,7 @@
|
||||
#include "pathspec.h"
|
||||
#include "index.h"
|
||||
#include "odb.h"
|
||||
#include "submodule.h"
|
||||
|
||||
#define DIFF_FLAG_IS_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) != 0)
|
||||
#define DIFF_FLAG_ISNT_SET(DIFF,FLAG) (((DIFF)->opts.flags & (FLAG)) == 0)
|
||||
@ -77,15 +78,11 @@ static int diff_delta__from_one(
|
||||
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED))
|
||||
return 0;
|
||||
|
||||
if (entry->mode == GIT_FILEMODE_COMMIT &&
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
|
||||
return 0;
|
||||
|
||||
if (!git_pathspec_match_path(
|
||||
if (!git_pathspec__match(
|
||||
&diff->pathspec, entry->path,
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE),
|
||||
&matched_pathspec))
|
||||
&matched_pathspec, NULL))
|
||||
return 0;
|
||||
|
||||
delta = diff_delta__alloc(diff, status, entry->path);
|
||||
@ -134,16 +131,12 @@ static int diff_delta__from_two(
|
||||
{
|
||||
git_diff_delta *delta;
|
||||
int notify_res;
|
||||
const char *canonical_path = old_entry->path;
|
||||
|
||||
if (status == GIT_DELTA_UNMODIFIED &&
|
||||
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED))
|
||||
return 0;
|
||||
|
||||
if (old_entry->mode == GIT_FILEMODE_COMMIT &&
|
||||
new_entry->mode == GIT_FILEMODE_COMMIT &&
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES))
|
||||
return 0;
|
||||
|
||||
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
|
||||
uint32_t temp_mode = old_mode;
|
||||
const git_index_entry *temp_entry = old_entry;
|
||||
@ -153,7 +146,7 @@ static int diff_delta__from_two(
|
||||
new_mode = temp_mode;
|
||||
}
|
||||
|
||||
delta = diff_delta__alloc(diff, status, old_entry->path);
|
||||
delta = diff_delta__alloc(diff, status, canonical_path);
|
||||
GITERR_CHECK_ALLOC(delta);
|
||||
|
||||
git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
|
||||
@ -246,6 +239,11 @@ GIT_INLINE(const char *) diff_delta__path(const git_diff_delta *delta)
|
||||
return str;
|
||||
}
|
||||
|
||||
const char *git_diff_delta__path(const git_diff_delta *delta)
|
||||
{
|
||||
return diff_delta__path(delta);
|
||||
}
|
||||
|
||||
int git_diff_delta__cmp(const void *a, const void *b)
|
||||
{
|
||||
const git_diff_delta *da = a, *db = b;
|
||||
@ -253,6 +251,33 @@ int git_diff_delta__cmp(const void *a, const void *b)
|
||||
return val ? val : ((int)da->status - (int)db->status);
|
||||
}
|
||||
|
||||
int git_diff_delta__casecmp(const void *a, const void *b)
|
||||
{
|
||||
const git_diff_delta *da = a, *db = b;
|
||||
int val = strcasecmp(diff_delta__path(da), diff_delta__path(db));
|
||||
return val ? val : ((int)da->status - (int)db->status);
|
||||
}
|
||||
|
||||
GIT_INLINE(const char *) diff_delta__i2w_path(const git_diff_delta *delta)
|
||||
{
|
||||
return delta->old_file.path ?
|
||||
delta->old_file.path : delta->new_file.path;
|
||||
}
|
||||
|
||||
int git_diff_delta__i2w_cmp(const void *a, const void *b)
|
||||
{
|
||||
const git_diff_delta *da = a, *db = b;
|
||||
int val = strcmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
|
||||
return val ? val : ((int)da->status - (int)db->status);
|
||||
}
|
||||
|
||||
int git_diff_delta__i2w_casecmp(const void *a, const void *b)
|
||||
{
|
||||
const git_diff_delta *da = a, *db = b;
|
||||
int val = strcasecmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
|
||||
return val ? val : ((int)da->status - (int)db->status);
|
||||
}
|
||||
|
||||
bool git_diff_delta__should_skip(
|
||||
const git_diff_options *opts, const git_diff_delta *delta)
|
||||
{
|
||||
@ -356,6 +381,8 @@ static git_diff_list *diff_list_alloc(
|
||||
diff->strncomp = git__strncasecmp;
|
||||
diff->pfxcomp = git__prefixcmp_icase;
|
||||
diff->entrycomp = git_index_entry__cmp_icase;
|
||||
|
||||
git_vector_set_cmp(&diff->deltas, git_diff_delta__casecmp);
|
||||
}
|
||||
|
||||
return diff;
|
||||
@ -377,7 +404,7 @@ static int diff_list_apply_options(
|
||||
DIFF_FLAG_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE, icase);
|
||||
|
||||
/* initialize pathspec from options */
|
||||
if (git_pathspec_init(&diff->pathspec, &opts->pathspec, pool) < 0)
|
||||
if (git_pathspec__vinit(&diff->pathspec, &opts->pathspec, pool) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -415,8 +442,18 @@ static int diff_list_apply_options(
|
||||
if (!opts) {
|
||||
diff->opts.context_lines = config_int(cfg, "diff.context", 3);
|
||||
|
||||
if (config_bool(cfg, "diff.ignoreSubmodules", 0))
|
||||
diff->opts.flags |= GIT_DIFF_IGNORE_SUBMODULES;
|
||||
/* add other defaults here */
|
||||
}
|
||||
|
||||
/* if ignore_submodules not explicitly set, check diff config */
|
||||
if (diff->opts.ignore_submodules <= 0) {
|
||||
const char *str;
|
||||
|
||||
if (git_config_get_string(&str , cfg, "diff.ignoreSubmodules") < 0)
|
||||
giterr_clear();
|
||||
else if (str != NULL &&
|
||||
git_submodule_parse_ignore(&diff->opts.ignore_submodules, str) < 0)
|
||||
giterr_clear();
|
||||
}
|
||||
|
||||
/* if either prefix is not set, figure out appropriate value */
|
||||
@ -463,7 +500,7 @@ static void diff_list_free(git_diff_list *diff)
|
||||
}
|
||||
git_vector_free(&diff->deltas);
|
||||
|
||||
git_pathspec_free(&diff->pathspec);
|
||||
git_pathspec__vfree(&diff->pathspec);
|
||||
git_pool_clear(&diff->pool);
|
||||
|
||||
git__memzero(diff, sizeof(*diff));
|
||||
@ -580,35 +617,44 @@ static int maybe_modified_submodule(
|
||||
int error = 0;
|
||||
git_submodule *sub;
|
||||
unsigned int sm_status = 0;
|
||||
const git_oid *sm_oid;
|
||||
git_submodule_ignore_t ign = diff->opts.ignore_submodules;
|
||||
|
||||
*status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
if (!DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES) &&
|
||||
!(error = git_submodule_lookup(
|
||||
&sub, diff->repo, info->nitem->path)) &&
|
||||
git_submodule_ignore(sub) != GIT_SUBMODULE_IGNORE_ALL &&
|
||||
!(error = git_submodule_status(&sm_status, sub)))
|
||||
{
|
||||
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES) ||
|
||||
ign == GIT_SUBMODULE_IGNORE_ALL)
|
||||
return 0;
|
||||
|
||||
if ((error = git_submodule_lookup(
|
||||
&sub, diff->repo, info->nitem->path)) < 0) {
|
||||
|
||||
/* GIT_EEXISTS means dir with .git in it was found - ignore it */
|
||||
if (error == GIT_EEXISTS) {
|
||||
giterr_clear();
|
||||
error = 0;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
if (ign <= 0 && git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL)
|
||||
return 0;
|
||||
|
||||
if ((error = git_submodule__status(
|
||||
&sm_status, NULL, NULL, found_oid, sub, ign)) < 0)
|
||||
return error;
|
||||
|
||||
/* check IS_WD_UNMODIFIED because this case is only used
|
||||
* when the new side of the diff is the working directory
|
||||
*/
|
||||
if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status))
|
||||
*status = GIT_DELTA_MODIFIED;
|
||||
|
||||
/* grab OID while we are here */
|
||||
if (git_oid_iszero(&info->nitem->oid) &&
|
||||
(sm_oid = git_submodule_wd_id(sub)) != NULL)
|
||||
git_oid_cpy(found_oid, sm_oid);
|
||||
}
|
||||
/* now that we have a HEAD OID, check if HEAD moved */
|
||||
if ((sm_status & GIT_SUBMODULE_STATUS_IN_WD) != 0 &&
|
||||
!git_oid_equal(&info->oitem->oid, found_oid))
|
||||
*status = GIT_DELTA_MODIFIED;
|
||||
|
||||
/* GIT_EEXISTS means a dir with .git in it was found - ignore it */
|
||||
if (error == GIT_EEXISTS) {
|
||||
giterr_clear();
|
||||
error = 0;
|
||||
}
|
||||
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int maybe_modified(
|
||||
@ -624,11 +670,11 @@ static int maybe_modified(
|
||||
bool new_is_workdir = (info->new_iter->type == GIT_ITERATOR_TYPE_WORKDIR);
|
||||
const char *matched_pathspec;
|
||||
|
||||
if (!git_pathspec_match_path(
|
||||
if (!git_pathspec__match(
|
||||
&diff->pathspec, oitem->path,
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE),
|
||||
&matched_pathspec))
|
||||
&matched_pathspec, NULL))
|
||||
return 0;
|
||||
|
||||
memset(&noid, 0, sizeof(noid));
|
||||
@ -665,8 +711,10 @@ static int maybe_modified(
|
||||
}
|
||||
}
|
||||
|
||||
/* if oids and modes match, then file is unmodified */
|
||||
else if (git_oid_equal(&oitem->oid, &nitem->oid) && omode == nmode)
|
||||
/* if oids and modes match (and are valid), then file is unmodified */
|
||||
else if (git_oid_equal(&oitem->oid, &nitem->oid) &&
|
||||
omode == nmode &&
|
||||
!git_oid_iszero(&oitem->oid))
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
/* if we have an unknown OID and a workdir iterator, then check some
|
||||
@ -707,7 +755,7 @@ static int maybe_modified(
|
||||
/* if we got here and decided that the files are modified, but we
|
||||
* haven't calculated the OID of the new item, then calculate it now
|
||||
*/
|
||||
if (status != GIT_DELTA_UNMODIFIED && git_oid_iszero(&nitem->oid)) {
|
||||
if (status == GIT_DELTA_MODIFIED && git_oid_iszero(&nitem->oid)) {
|
||||
if (git_oid_iszero(&noid)) {
|
||||
if (git_diff__oid_for_file(diff->repo,
|
||||
nitem->path, nitem->mode, nitem->file_size, &noid) < 0)
|
||||
@ -774,10 +822,15 @@ static int diff_scan_inside_untracked_dir(
|
||||
|
||||
/* need to recurse into non-ignored directories */
|
||||
if (!is_ignored && S_ISDIR(info->nitem->mode)) {
|
||||
if ((error = git_iterator_advance_into(
|
||||
&info->nitem, info->new_iter)) < 0)
|
||||
break;
|
||||
error = git_iterator_advance_into(&info->nitem, info->new_iter);
|
||||
|
||||
if (!error)
|
||||
continue;
|
||||
else if (error == GIT_ENOTFOUND) {
|
||||
error = 0;
|
||||
is_ignored = true; /* treat empty as ignored */
|
||||
} else
|
||||
break; /* real error, must stop */
|
||||
}
|
||||
|
||||
/* found a non-ignored item - treat parent dir as untracked */
|
||||
@ -825,7 +878,7 @@ static int handle_unmatched_new_item(
|
||||
git_buf_clear(&info->ignore_prefix);
|
||||
}
|
||||
|
||||
if (S_ISDIR(nitem->mode)) {
|
||||
if (nitem->mode == GIT_FILEMODE_TREE) {
|
||||
bool recurse_into_dir = contains_oitem;
|
||||
|
||||
/* if not already inside an ignored dir, check if this is ignored */
|
||||
@ -929,6 +982,16 @@ static int handle_unmatched_new_item(
|
||||
else if (info->new_iter->type != GIT_ITERATOR_TYPE_WORKDIR)
|
||||
delta_type = GIT_DELTA_ADDED;
|
||||
|
||||
else if (nitem->mode == GIT_FILEMODE_COMMIT) {
|
||||
git_submodule *sm;
|
||||
|
||||
/* ignore things that are not actual submodules */
|
||||
if (git_submodule_lookup(&sm, info->repo, nitem->path) != 0) {
|
||||
giterr_clear();
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
}
|
||||
}
|
||||
|
||||
/* Actually create the record for this item if necessary */
|
||||
if ((error = diff_delta__from_one(diff, delta_type, nitem)) < 0)
|
||||
return error;
|
||||
@ -1119,17 +1182,40 @@ int git_diff_tree_to_index(
|
||||
const git_diff_options *opts)
|
||||
{
|
||||
int error = 0;
|
||||
bool reset_index_ignore_case = false;
|
||||
|
||||
assert(diff && repo);
|
||||
|
||||
if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
|
||||
return error;
|
||||
|
||||
if (index->ignore_case) {
|
||||
git_index__set_ignore_case(index, false);
|
||||
reset_index_ignore_case = true;
|
||||
}
|
||||
|
||||
DIFF_FROM_ITERATORS(
|
||||
git_iterator_for_tree(&a, old_tree, 0, pfx, pfx),
|
||||
git_iterator_for_index(&b, index, 0, pfx, pfx)
|
||||
);
|
||||
|
||||
if (reset_index_ignore_case) {
|
||||
git_index__set_ignore_case(index, true);
|
||||
|
||||
if (!error) {
|
||||
git_diff_list *d = *diff;
|
||||
|
||||
d->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
|
||||
d->strcomp = git__strcasecmp;
|
||||
d->strncomp = git__strncasecmp;
|
||||
d->pfxcomp = git__prefixcmp_icase;
|
||||
d->entrycomp = git_index_entry__cmp_icase;
|
||||
|
||||
git_vector_set_cmp(&d->deltas, git_diff_delta__casecmp);
|
||||
git_vector_sort(&d->deltas);
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -1195,52 +1281,99 @@ size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type)
|
||||
return count;
|
||||
}
|
||||
|
||||
int git_diff_is_sorted_icase(const git_diff_list *diff)
|
||||
{
|
||||
return (diff->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0;
|
||||
}
|
||||
|
||||
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),
|
||||
git_diff_list *head2idx,
|
||||
git_diff_list *idx2wd,
|
||||
int (*cb)(git_diff_delta *h2i, git_diff_delta *i2w, void *payload),
|
||||
void *payload)
|
||||
{
|
||||
int cmp;
|
||||
git_diff_delta *i2h, *w2i;
|
||||
git_diff_delta *h2i, *i2w;
|
||||
size_t i, j, i_max, j_max;
|
||||
int (*strcomp)(const char *, const char *);
|
||||
int (*strcomp)(const char *, const char *) = git__strcmp;
|
||||
bool h2i_icase, i2w_icase, icase_mismatch;
|
||||
|
||||
i_max = idx2head ? idx2head->deltas.length : 0;
|
||||
j_max = wd2idx ? wd2idx->deltas.length : 0;
|
||||
i_max = head2idx ? head2idx->deltas.length : 0;
|
||||
j_max = idx2wd ? idx2wd->deltas.length : 0;
|
||||
if (!i_max && !j_max)
|
||||
return 0;
|
||||
|
||||
/* Get appropriate strcmp function */
|
||||
strcomp = idx2head ? idx2head->strcomp : wd2idx ? wd2idx->strcomp : NULL;
|
||||
|
||||
/* Assert both iterators use matching ignore-case. If this function ever
|
||||
* supports merging diffs that are not sorted by the same function, then
|
||||
* it will need to spool and sort on one of the results before merging
|
||||
/* At some point, tree-to-index diffs will probably never ignore case,
|
||||
* even if that isn't true now. Index-to-workdir diffs may or may not
|
||||
* ignore case, but the index filename for the idx2wd diff should
|
||||
* still be using the canonical case-preserving name.
|
||||
*
|
||||
* Therefore the main thing we need to do here is make sure the diffs
|
||||
* are traversed in a compatible order. To do this, we temporarily
|
||||
* resort a mismatched diff to get the order correct.
|
||||
*
|
||||
* In order to traverse renames in the index->workdir, we need to
|
||||
* ensure that we compare the index name on both sides, so we
|
||||
* always sort by the old name in the i2w list.
|
||||
*/
|
||||
if (idx2head && wd2idx) {
|
||||
assert(idx2head->strcomp == wd2idx->strcomp);
|
||||
h2i_icase = head2idx != NULL &&
|
||||
(head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0;
|
||||
|
||||
i2w_icase = idx2wd != NULL &&
|
||||
(idx2wd->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0;
|
||||
|
||||
icase_mismatch =
|
||||
(head2idx != NULL && idx2wd != NULL && h2i_icase != i2w_icase);
|
||||
|
||||
if (icase_mismatch && h2i_icase) {
|
||||
git_vector_set_cmp(&head2idx->deltas, git_diff_delta__cmp);
|
||||
git_vector_sort(&head2idx->deltas);
|
||||
}
|
||||
|
||||
if (i2w_icase && !icase_mismatch) {
|
||||
strcomp = git__strcasecmp;
|
||||
|
||||
git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__i2w_casecmp);
|
||||
git_vector_sort(&idx2wd->deltas);
|
||||
} else if (idx2wd != NULL) {
|
||||
git_vector_set_cmp(&idx2wd->deltas, git_diff_delta__i2w_cmp);
|
||||
git_vector_sort(&idx2wd->deltas);
|
||||
}
|
||||
|
||||
for (i = 0, j = 0; i < i_max || j < j_max; ) {
|
||||
i2h = idx2head ? GIT_VECTOR_GET(&idx2head->deltas,i) : NULL;
|
||||
w2i = wd2idx ? GIT_VECTOR_GET(&wd2idx->deltas,j) : NULL;
|
||||
h2i = head2idx ? GIT_VECTOR_GET(&head2idx->deltas, i) : NULL;
|
||||
i2w = idx2wd ? GIT_VECTOR_GET(&idx2wd->deltas, j) : NULL;
|
||||
|
||||
cmp = !w2i ? -1 : !i2h ? 1 :
|
||||
strcomp(i2h->old_file.path, w2i->old_file.path);
|
||||
cmp = !i2w ? -1 : !h2i ? 1 :
|
||||
strcomp(h2i->new_file.path, i2w->old_file.path);
|
||||
|
||||
if (cmp < 0) {
|
||||
if (cb(i2h, NULL, payload))
|
||||
if (cb(h2i, NULL, payload))
|
||||
return GIT_EUSER;
|
||||
i++;
|
||||
} else if (cmp > 0) {
|
||||
if (cb(NULL, w2i, payload))
|
||||
if (cb(NULL, i2w, payload))
|
||||
return GIT_EUSER;
|
||||
j++;
|
||||
} else {
|
||||
if (cb(i2h, w2i, payload))
|
||||
if (cb(h2i, i2w, payload))
|
||||
return GIT_EUSER;
|
||||
i++; j++;
|
||||
}
|
||||
}
|
||||
|
||||
/* restore case-insensitive delta sort */
|
||||
if (icase_mismatch && h2i_icase) {
|
||||
git_vector_set_cmp(&head2idx->deltas, git_diff_delta__casecmp);
|
||||
git_vector_sort(&head2idx->deltas);
|
||||
}
|
||||
|
||||
/* restore idx2wd sort by new path */
|
||||
if (idx2wd != NULL) {
|
||||
git_vector_set_cmp(&idx2wd->deltas,
|
||||
i2w_icase ? git_diff_delta__casecmp : git_diff_delta__cmp);
|
||||
git_vector_sort(&idx2wd->deltas);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
46
src/diff.h
46
src/diff.h
@ -16,6 +16,7 @@
|
||||
#include "iterator.h"
|
||||
#include "repository.h"
|
||||
#include "pool.h"
|
||||
#include "odb.h"
|
||||
|
||||
#define DIFF_OLD_PREFIX_DEFAULT "a/"
|
||||
#define DIFF_NEW_PREFIX_DEFAULT "b/"
|
||||
@ -74,10 +75,20 @@ 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 const char *git_diff_delta__path(const git_diff_delta *delta);
|
||||
|
||||
extern bool git_diff_delta__should_skip(
|
||||
const git_diff_options *opts, const git_diff_delta *delta);
|
||||
|
||||
extern int git_diff_delta__format_file_header(
|
||||
git_buf *out,
|
||||
const git_diff_delta *delta,
|
||||
const char *oldpfx,
|
||||
const char *newpfx,
|
||||
int oid_strlen);
|
||||
|
||||
extern int git_diff__oid_for_file(
|
||||
git_repository *, const char *, uint16_t, git_off_t, git_oid *);
|
||||
|
||||
@ -94,17 +105,44 @@ extern int git_diff__paired_foreach(
|
||||
int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
|
||||
void *payload);
|
||||
|
||||
int git_diff_find_similar__hashsig_for_file(
|
||||
extern int git_diff_find_similar__hashsig_for_file(
|
||||
void **out, const git_diff_file *f, const char *path, void *p);
|
||||
|
||||
int git_diff_find_similar__hashsig_for_buf(
|
||||
extern int git_diff_find_similar__hashsig_for_buf(
|
||||
void **out, const git_diff_file *f, const char *buf, size_t len, void *p);
|
||||
|
||||
void git_diff_find_similar__hashsig_free(void *sig, void *payload);
|
||||
extern void git_diff_find_similar__hashsig_free(void *sig, void *payload);
|
||||
|
||||
int git_diff_find_similar__calc_similarity(
|
||||
extern int git_diff_find_similar__calc_similarity(
|
||||
int *score, void *siga, void *sigb, void *payload);
|
||||
|
||||
/*
|
||||
* Sometimes a git_diff_file will have a zero size; this attempts to
|
||||
* fill in the size without loading the blob if possible. If that is
|
||||
* not possible, then it will return the git_odb_object that had to be
|
||||
* loaded and the caller can use it or dispose of it as needed.
|
||||
*/
|
||||
GIT_INLINE(int) git_diff_file__resolve_zero_size(
|
||||
git_diff_file *file, git_odb_object **odb_obj, git_repository *repo)
|
||||
{
|
||||
int error;
|
||||
git_odb *odb;
|
||||
size_t len;
|
||||
git_otype type;
|
||||
|
||||
if ((error = git_repository_odb(&odb, repo)) < 0)
|
||||
return error;
|
||||
|
||||
error = git_odb__read_header_or_object(
|
||||
odb_obj, &len, &type, odb, &file->oid);
|
||||
|
||||
git_odb_free(odb);
|
||||
|
||||
if (!error)
|
||||
file->size = (git_off_t)len;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -187,7 +187,7 @@ static int git_diff_driver_load(
|
||||
|
||||
git_buf_truncate(&name, namelen + strlen("diff.."));
|
||||
git_buf_put(&name, "xfuncname", strlen("xfuncname"));
|
||||
if ((error = git_config_get_multivar(
|
||||
if ((error = git_config_get_multivar_foreach(
|
||||
cfg, name.ptr, NULL, diff_driver_xfuncname, drv)) < 0) {
|
||||
if (error != GIT_ENOTFOUND)
|
||||
goto done;
|
||||
@ -196,7 +196,7 @@ static int git_diff_driver_load(
|
||||
|
||||
git_buf_truncate(&name, namelen + strlen("diff.."));
|
||||
git_buf_put(&name, "funcname", strlen("funcname"));
|
||||
if ((error = git_config_get_multivar(
|
||||
if ((error = git_config_get_multivar_foreach(
|
||||
cfg, name.ptr, NULL, diff_driver_funcname, drv)) < 0) {
|
||||
if (error != GIT_ENOTFOUND)
|
||||
goto done;
|
||||
@ -373,10 +373,11 @@ static long diff_context_find(
|
||||
!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);
|
||||
if (out_size > (long)ctxt->line.size)
|
||||
out_size = (long)ctxt->line.size;
|
||||
memcpy(out, ctxt->line.ptr, (size_t)out_size);
|
||||
|
||||
return (long)ctxt->line.size;
|
||||
return out_size;
|
||||
}
|
||||
|
||||
void git_diff_find_context_init(
|
||||
|
173
src/diff_file.c
173
src/diff_file.c
@ -18,23 +18,23 @@
|
||||
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 &&
|
||||
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;
|
||||
fc->file->size > fc->opts_max_size)
|
||||
fc->file->flags |= GIT_DIFF_FLAG_BINARY;
|
||||
|
||||
return ((fc->file.flags & GIT_DIFF_FLAG_BINARY) != 0);
|
||||
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)
|
||||
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;
|
||||
case 0: fc->file->flags |= GIT_DIFF_FLAG_NOT_BINARY; break;
|
||||
case 1: fc->file->flags |= GIT_DIFF_FLAG_BINARY; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
@ -48,38 +48,39 @@ static int diff_file_content_init_common(
|
||||
fc->opts_max_size = opts->max_size ?
|
||||
opts->max_size : DIFF_MAX_FILESIZE;
|
||||
|
||||
if (!fc->driver) {
|
||||
if (git_diff_driver_lookup(&fc->driver, fc->repo, "") < 0)
|
||||
return -1;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
fc->file->flags &= ~GIT_DIFF_FLAG_NOT_BINARY;
|
||||
fc->file->flags |= GIT_DIFF_FLAG_BINARY;
|
||||
}
|
||||
|
||||
diff_file_content_binary_by_size(fc);
|
||||
|
||||
if ((fc->file.flags & GIT_DIFF_FLAG__NO_DATA) != 0) {
|
||||
fc->file.flags |= GIT_DIFF_FLAG__LOADED;
|
||||
if ((fc->flags & GIT_DIFF_FLAG__NO_DATA) != 0) {
|
||||
fc->flags |= GIT_DIFF_FLAG__LOADED;
|
||||
fc->map.len = 0;
|
||||
fc->map.data = "";
|
||||
}
|
||||
|
||||
if ((fc->file.flags & GIT_DIFF_FLAG__LOADED) != 0)
|
||||
if ((fc->flags & GIT_DIFF_FLAG__LOADED) != 0)
|
||||
diff_file_content_binary_by_content(fc);
|
||||
|
||||
return 0;
|
||||
@ -92,15 +93,14 @@ int git_diff_file_content__init_from_diff(
|
||||
bool use_old)
|
||||
{
|
||||
git_diff_delta *delta = git_vector_get(&diff->deltas, delta_index);
|
||||
git_diff_file *file = use_old ? &delta->old_file : &delta->new_file;
|
||||
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;
|
||||
memcpy(&fc->file, file, sizeof(fc->file));
|
||||
|
||||
if (git_diff_driver_lookup(&fc->driver, fc->repo, file->path) < 0)
|
||||
if (git_diff_driver_lookup(&fc->driver, fc->repo, fc->file->path) < 0)
|
||||
return -1;
|
||||
|
||||
switch (delta->status) {
|
||||
@ -122,7 +122,7 @@ int git_diff_file_content__init_from_diff(
|
||||
}
|
||||
|
||||
if (!has_data)
|
||||
fc->file.flags |= GIT_DIFF_FLAG__NO_DATA;
|
||||
fc->flags |= GIT_DIFF_FLAG__NO_DATA;
|
||||
|
||||
return diff_file_content_init_common(fc, &diff->opts);
|
||||
}
|
||||
@ -131,21 +131,24 @@ 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)
|
||||
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->file.flags |= GIT_DIFF_FLAG__NO_DATA;
|
||||
fc->flags |= GIT_DIFF_FLAG__NO_DATA;
|
||||
} else {
|
||||
fc->file.flags |= GIT_DIFF_FLAG__LOADED | GIT_DIFF_FLAG_VALID_OID;
|
||||
fc->file.size = git_blob_rawsize(blob);
|
||||
fc->file.mode = 0644;
|
||||
git_oid_cpy(&fc->file.oid, git_blob_id(blob));
|
||||
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.len = (size_t)fc->file->size;
|
||||
fc->map.data = (char *)git_blob_rawcontent(blob);
|
||||
}
|
||||
|
||||
@ -157,18 +160,21 @@ int git_diff_file_content__init_from_raw(
|
||||
git_repository *repo,
|
||||
const git_diff_options *opts,
|
||||
const char *buf,
|
||||
size_t buflen)
|
||||
size_t buflen,
|
||||
git_diff_file *as_file)
|
||||
{
|
||||
memset(fc, 0, sizeof(*fc));
|
||||
fc->repo = repo;
|
||||
fc->file = as_file;
|
||||
|
||||
if (!buf) {
|
||||
fc->file.flags |= GIT_DIFF_FLAG__NO_DATA;
|
||||
fc->flags |= GIT_DIFF_FLAG__NO_DATA;
|
||||
} else {
|
||||
fc->file.flags |= GIT_DIFF_FLAG__LOADED | GIT_DIFF_FLAG_VALID_OID;
|
||||
fc->file.size = buflen;
|
||||
fc->file.mode = 0644;
|
||||
git_odb_hash(&fc->file.oid, buf, buflen, GIT_OBJ_BLOB);
|
||||
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;
|
||||
@ -190,7 +196,7 @@ static int diff_file_content_commit_to_str(
|
||||
unsigned int sm_status = 0;
|
||||
const git_oid *sm_head;
|
||||
|
||||
if ((error = git_submodule_lookup(&sm, fc->repo, fc->file.path)) < 0 ||
|
||||
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)
|
||||
@ -199,25 +205,25 @@ static int diff_file_content_commit_to_str(
|
||||
}
|
||||
|
||||
/* update OID if we didn't have it previously */
|
||||
if ((fc->file.flags & GIT_DIFF_FLAG_VALID_OID) == 0 &&
|
||||
if ((fc->file->flags & GIT_DIFF_FLAG_VALID_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;
|
||||
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);
|
||||
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->file.flags |= GIT_DIFF_FLAG__FREE_DATA;
|
||||
fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -227,27 +233,17 @@ 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))
|
||||
if (git_oid_iszero(&fc->file->oid))
|
||||
return 0;
|
||||
|
||||
if (fc->file.mode == GIT_FILEMODE_COMMIT)
|
||||
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)
|
||||
if (!fc->file->size) {
|
||||
if ((error = git_diff_file__resolve_zero_size(
|
||||
fc->file, &odb_obj, fc->repo)) < 0)
|
||||
return error;
|
||||
|
||||
fc->file.size = len;
|
||||
}
|
||||
|
||||
if (diff_file_content_binary_by_size(fc))
|
||||
@ -259,11 +255,11 @@ static int diff_file_content_load_blob(git_diff_file_content *fc)
|
||||
git_odb_object_free(odb_obj);
|
||||
} else {
|
||||
error = git_blob_lookup(
|
||||
(git_blob **)&fc->blob, fc->repo, &fc->file.oid);
|
||||
(git_blob **)&fc->blob, fc->repo, &fc->file->oid);
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
fc->file.flags |= GIT_DIFF_FLAG__FREE_BLOB;
|
||||
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);
|
||||
}
|
||||
@ -279,16 +275,16 @@ static int diff_file_content_load_workdir_symlink(
|
||||
/* 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;
|
||||
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->file.flags |= GIT_DIFF_FLAG__FREE_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);
|
||||
giterr_set(GITERR_OS, "Failed to read symlink '%s'", fc->file->path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -307,28 +303,28 @@ static int diff_file_content_load_workdir_file(
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
if (!fc->file.size &&
|
||||
!(fc->file.size = git_futils_filesize(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)
|
||||
&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->file.flags |= GIT_DIFF_FLAG__UNMAP_DATA;
|
||||
&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);
|
||||
error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size);
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
@ -340,7 +336,7 @@ static int diff_file_content_load_workdir_file(
|
||||
if (!error) {
|
||||
fc->map.len = git_buf_len(&filtered);
|
||||
fc->map.data = git_buf_detach(&filtered);
|
||||
fc->file.flags |= GIT_DIFF_FLAG__FREE_DATA;
|
||||
fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
|
||||
}
|
||||
|
||||
git_buf_free(&raw);
|
||||
@ -359,26 +355,26 @@ 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)
|
||||
if (fc->file->mode == GIT_FILEMODE_COMMIT)
|
||||
return diff_file_content_commit_to_str(fc, true);
|
||||
|
||||
if (fc->file.mode == GIT_FILEMODE_TREE)
|
||||
if (fc->file->mode == GIT_FILEMODE_TREE)
|
||||
return 0;
|
||||
|
||||
if (git_buf_joinpath(
|
||||
&path, git_repository_workdir(fc->repo), fc->file.path) < 0)
|
||||
&path, git_repository_workdir(fc->repo), fc->file->path) < 0)
|
||||
return -1;
|
||||
|
||||
if (S_ISLNK(fc->file.mode))
|
||||
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) {
|
||||
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;
|
||||
&fc->file->oid, fc->map.data, fc->map.len, GIT_OBJ_BLOB);
|
||||
fc->file->flags |= GIT_DIFF_FLAG_VALID_OID;
|
||||
}
|
||||
|
||||
git_buf_free(&path);
|
||||
@ -389,10 +385,10 @@ int git_diff_file_content__load(git_diff_file_content *fc)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if ((fc->file.flags & GIT_DIFF_FLAG__LOADED) != 0)
|
||||
if ((fc->flags & GIT_DIFF_FLAG__LOADED) != 0)
|
||||
return 0;
|
||||
|
||||
if (fc->file.flags & GIT_DIFF_FLAG_BINARY)
|
||||
if ((fc->file->flags & GIT_DIFF_FLAG_BINARY) != 0)
|
||||
return 0;
|
||||
|
||||
if (fc->src == GIT_ITERATOR_TYPE_WORKDIR)
|
||||
@ -402,7 +398,7 @@ int git_diff_file_content__load(git_diff_file_content *fc)
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
fc->file.flags |= GIT_DIFF_FLAG__LOADED;
|
||||
fc->flags |= GIT_DIFF_FLAG__LOADED;
|
||||
|
||||
diff_file_content_binary_by_content(fc);
|
||||
|
||||
@ -411,26 +407,29 @@ int git_diff_file_content__load(git_diff_file_content *fc)
|
||||
|
||||
void git_diff_file_content__unload(git_diff_file_content *fc)
|
||||
{
|
||||
if (fc->file.flags & GIT_DIFF_FLAG__FREE_DATA) {
|
||||
if ((fc->flags & GIT_DIFF_FLAG__LOADED) == 0)
|
||||
return;
|
||||
|
||||
if (fc->flags & GIT_DIFF_FLAG__FREE_DATA) {
|
||||
git__free(fc->map.data);
|
||||
fc->map.data = "";
|
||||
fc->map.len = 0;
|
||||
fc->file.flags &= ~GIT_DIFF_FLAG__FREE_DATA;
|
||||
fc->flags &= ~GIT_DIFF_FLAG__FREE_DATA;
|
||||
}
|
||||
else if (fc->file.flags & GIT_DIFF_FLAG__UNMAP_DATA) {
|
||||
else if (fc->flags & GIT_DIFF_FLAG__UNMAP_DATA) {
|
||||
git_futils_mmap_free(&fc->map);
|
||||
fc->map.data = "";
|
||||
fc->map.len = 0;
|
||||
fc->file.flags &= ~GIT_DIFF_FLAG__UNMAP_DATA;
|
||||
fc->flags &= ~GIT_DIFF_FLAG__UNMAP_DATA;
|
||||
}
|
||||
|
||||
if (fc->file.flags & GIT_DIFF_FLAG__FREE_BLOB) {
|
||||
if (fc->flags & GIT_DIFF_FLAG__FREE_BLOB) {
|
||||
git_blob_free((git_blob *)fc->blob);
|
||||
fc->blob = NULL;
|
||||
fc->file.flags &= ~GIT_DIFF_FLAG__FREE_BLOB;
|
||||
fc->flags &= ~GIT_DIFF_FLAG__FREE_BLOB;
|
||||
}
|
||||
|
||||
fc->file.flags &= ~GIT_DIFF_FLAG__LOADED;
|
||||
fc->flags &= ~GIT_DIFF_FLAG__LOADED;
|
||||
}
|
||||
|
||||
void git_diff_file_content__clear(git_diff_file_content *fc)
|
||||
|
@ -15,8 +15,9 @@
|
||||
/* expanded information for one side of a delta */
|
||||
typedef struct {
|
||||
git_repository *repo;
|
||||
git_diff_file file;
|
||||
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;
|
||||
@ -34,14 +35,16 @@ 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);
|
||||
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);
|
||||
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);
|
||||
|
256
src/diff_patch.c
256
src/diff_patch.c
@ -10,6 +10,7 @@
|
||||
#include "diff_driver.h"
|
||||
#include "diff_patch.h"
|
||||
#include "diff_xdiff.h"
|
||||
#include "fileops.h"
|
||||
|
||||
/* cached information about a single span in a diff */
|
||||
typedef struct diff_patch_line diff_patch_line;
|
||||
@ -41,7 +42,7 @@ struct git_diff_patch {
|
||||
git_array_t(diff_patch_hunk) hunks;
|
||||
git_array_t(diff_patch_line) lines;
|
||||
size_t oldno, newno;
|
||||
size_t content_size;
|
||||
size_t content_size, context_size, header_size;
|
||||
git_pool flattened;
|
||||
};
|
||||
|
||||
@ -64,12 +65,12 @@ 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)
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -143,42 +144,46 @@ static int diff_patch_load(git_diff_patch *patch, git_diff_output *output)
|
||||
output && !output->hunk_cb && !output->data_cb)
|
||||
return 0;
|
||||
|
||||
#define DIFF_FLAGS_KNOWN_DATA (GIT_DIFF_FLAG__NO_DATA|GIT_DIFF_FLAG_VALID_OID)
|
||||
|
||||
incomplete_data =
|
||||
((patch->ofile.file.flags & DIFF_FLAGS_KNOWN_DATA) != 0 &&
|
||||
(patch->nfile.file.flags & DIFF_FLAGS_KNOWN_DATA) != 0);
|
||||
(((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)
|
||||
(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)
|
||||
(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)
|
||||
(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)
|
||||
(patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* if we were previously missing an oid, reassess UNMODIFIED state */
|
||||
/* if previously missing an oid, and now that we have it the two sides
|
||||
* are the same (and not submodules), 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->ofile.file->mode == patch->nfile.file->mode &&
|
||||
patch->ofile.file->mode != GIT_FILEMODE_COMMIT &&
|
||||
git_oid_equal(&patch->ofile.file->oid, &patch->nfile.file->oid) &&
|
||||
patch->delta->status == GIT_DELTA_MODIFIED) /* not RENAMED/COPIED! */
|
||||
patch->delta->status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
cleanup:
|
||||
@ -192,7 +197,7 @@ cleanup:
|
||||
patch->delta->status != GIT_DELTA_UNMODIFIED &&
|
||||
(patch->ofile.map.len || patch->nfile.map.len) &&
|
||||
(patch->ofile.map.len != patch->nfile.map.len ||
|
||||
!git_oid_equal(&patch->ofile.file.oid, &patch->nfile.file.oid)))
|
||||
!git_oid_equal(&patch->ofile.file->oid, &patch->nfile.file->oid)))
|
||||
patch->flags |= GIT_DIFF_PATCH_DIFFABLE;
|
||||
|
||||
patch->flags |= GIT_DIFF_PATCH_LOADED;
|
||||
@ -225,6 +230,10 @@ static int diff_patch_generate(git_diff_patch *patch, git_diff_output *output)
|
||||
if ((patch->flags & GIT_DIFF_PATCH_DIFFED) != 0)
|
||||
return 0;
|
||||
|
||||
/* if we are not looking at the hunks and lines, don't do the diff */
|
||||
if (!output->hunk_cb && !output->data_cb)
|
||||
return 0;
|
||||
|
||||
if ((patch->flags & GIT_DIFF_PATCH_LOADED) == 0 &&
|
||||
(error = diff_patch_load(patch, output)) < 0)
|
||||
return error;
|
||||
@ -279,21 +288,22 @@ int git_diff_foreach(
|
||||
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);
|
||||
diff_output_init(
|
||||
&xo.output, &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);
|
||||
error = diff_patch_file_callback(&patch, &xo.output);
|
||||
|
||||
if (!error)
|
||||
error = diff_patch_generate(&patch, (git_diff_output *)&xo);
|
||||
error = diff_patch_generate(&patch, &xo.output);
|
||||
|
||||
git_diff_patch_free(&patch);
|
||||
}
|
||||
@ -310,26 +320,31 @@ int git_diff_foreach(
|
||||
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.file.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
|
||||
bool has_new = ((patch->nfile.file.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
|
||||
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))
|
||||
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)
|
||||
@ -345,7 +360,9 @@ 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;
|
||||
@ -355,29 +372,61 @@ static int diff_patch_from_blobs(
|
||||
|
||||
GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
|
||||
|
||||
pd->patch.delta = &pd->delta;
|
||||
|
||||
if (!repo) /* return two NULL items as UNMODIFIED delta */
|
||||
return 0;
|
||||
|
||||
if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) {
|
||||
const git_blob *swap = old_blob;
|
||||
old_blob = new_blob;
|
||||
new_blob = swap;
|
||||
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)) < 0 ||
|
||||
&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)) < 0)
|
||||
&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,
|
||||
@ -392,12 +441,18 @@ int git_diff_blobs(
|
||||
memset(&xo, 0, sizeof(xo));
|
||||
|
||||
diff_output_init(
|
||||
(git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload);
|
||||
&xo.output, opts, file_cb, hunk_cb, data_cb, payload);
|
||||
git_xdiff_init(&xo, opts);
|
||||
|
||||
error = diff_patch_from_blobs(&pd, &xo, old_blob, new_blob, opts);
|
||||
if (!old_path && new_path)
|
||||
old_path = new_path;
|
||||
else if (!new_path && old_path)
|
||||
new_path = old_path;
|
||||
|
||||
git_diff_patch_free((git_diff_patch *)&pd);
|
||||
error = diff_patch_from_blobs(
|
||||
&pd, &xo, old_blob, old_path, new_blob, new_path, opts);
|
||||
|
||||
git_diff_patch_free(&pd.patch);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -405,7 +460,9 @@ int git_diff_blobs(
|
||||
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;
|
||||
@ -415,16 +472,18 @@ int git_diff_patch_from_blobs(
|
||||
assert(out);
|
||||
*out = NULL;
|
||||
|
||||
pd = git__calloc(1, sizeof(*pd));
|
||||
GITERR_CHECK_ALLOC(pd);
|
||||
pd->patch.flags = GIT_DIFF_PATCH_ALLOCATED;
|
||||
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);
|
||||
diff_output_to_patch(&xo.output, &pd->patch);
|
||||
git_xdiff_init(&xo, opts);
|
||||
|
||||
if (!(error = diff_patch_from_blobs(pd, &xo, old_blob, new_blob, 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);
|
||||
@ -436,8 +495,10 @@ 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;
|
||||
@ -448,28 +509,36 @@ static int diff_patch_from_blob_and_buffer(
|
||||
|
||||
pd->patch.delta = &pd->delta;
|
||||
|
||||
if (!repo && !buf) /* return two NULL items as UNMODIFIED delta */
|
||||
return 0;
|
||||
|
||||
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->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->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->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->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,
|
||||
@ -484,13 +553,18 @@ int git_diff_blob_to_buffer(
|
||||
memset(&xo, 0, sizeof(xo));
|
||||
|
||||
diff_output_init(
|
||||
(git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload);
|
||||
&xo.output, opts, file_cb, hunk_cb, data_cb, payload);
|
||||
git_xdiff_init(&xo, opts);
|
||||
|
||||
error = diff_patch_from_blob_and_buffer(
|
||||
&pd, &xo, old_blob, buf, buflen, opts);
|
||||
if (!old_path && buf_path)
|
||||
old_path = buf_path;
|
||||
else if (!buf_path && old_path)
|
||||
buf_path = old_path;
|
||||
|
||||
git_diff_patch_free((git_diff_patch *)&pd);
|
||||
error = diff_patch_from_blob_and_buffer(
|
||||
&pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts);
|
||||
|
||||
git_diff_patch_free(&pd.patch);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -498,8 +572,10 @@ int git_diff_blob_to_buffer(
|
||||
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;
|
||||
@ -509,17 +585,18 @@ int git_diff_patch_from_blob_and_buffer(
|
||||
assert(out);
|
||||
*out = NULL;
|
||||
|
||||
pd = git__calloc(1, sizeof(*pd));
|
||||
GITERR_CHECK_ALLOC(pd);
|
||||
pd->patch.flags = GIT_DIFF_PATCH_ALLOCATED;
|
||||
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);
|
||||
diff_output_to_patch(&xo.output, &pd->patch);
|
||||
git_xdiff_init(&xo, opts);
|
||||
|
||||
if (!(error = diff_patch_from_blob_and_buffer(
|
||||
pd, &xo, old_blob, buf, buflen, 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);
|
||||
@ -565,13 +642,13 @@ int git_diff_get_patch(
|
||||
if ((error = diff_patch_alloc_from_diff(&patch, diff, idx)) < 0)
|
||||
return error;
|
||||
|
||||
diff_output_to_patch((git_diff_output *)&xo, patch);
|
||||
diff_output_to_patch(&xo.output, patch);
|
||||
git_xdiff_init(&xo, &diff->opts);
|
||||
|
||||
error = diff_patch_file_callback(patch, (git_diff_output *)&xo);
|
||||
error = diff_patch_file_callback(patch, &xo.output);
|
||||
|
||||
if (!error)
|
||||
error = diff_patch_generate(patch, (git_diff_output *)&xo);
|
||||
error = diff_patch_generate(patch, &xo.output);
|
||||
|
||||
if (!error) {
|
||||
/* if cumulative diff size is < 0.5 total size, flatten the patch */
|
||||
@ -733,6 +810,39 @@ notfound:
|
||||
return diff_error_outofrange(thing);
|
||||
}
|
||||
|
||||
size_t git_diff_patch_size(
|
||||
git_diff_patch *patch,
|
||||
int include_context,
|
||||
int include_hunk_headers,
|
||||
int include_file_headers)
|
||||
{
|
||||
size_t out;
|
||||
|
||||
assert(patch);
|
||||
|
||||
out = patch->content_size;
|
||||
|
||||
if (!include_context)
|
||||
out -= patch->context_size;
|
||||
|
||||
if (include_hunk_headers)
|
||||
out += patch->header_size;
|
||||
|
||||
if (include_file_headers) {
|
||||
git_buf file_header = GIT_BUF_INIT;
|
||||
|
||||
if (git_diff_delta__format_file_header(
|
||||
&file_header, patch->delta, NULL, NULL, 0) < 0)
|
||||
giterr_clear();
|
||||
else
|
||||
out += git_buf_len(&file_header);
|
||||
|
||||
git_buf_free(&file_header);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
git_diff_list *git_diff_patch__diff(git_diff_patch *patch)
|
||||
{
|
||||
return patch->diff;
|
||||
@ -827,6 +937,8 @@ static int diff_patch_hunk_cb(
|
||||
hunk->header[header_len] = '\0';
|
||||
hunk->header_len = header_len;
|
||||
|
||||
patch->header_size += header_len;
|
||||
|
||||
hunk->line_start = git_array_size(patch->lines);
|
||||
hunk->line_count = 0;
|
||||
|
||||
@ -847,6 +959,7 @@ static int diff_patch_line_cb(
|
||||
git_diff_patch *patch = payload;
|
||||
diff_patch_hunk *hunk;
|
||||
diff_patch_line *line;
|
||||
const char *content_end = content + content_len;
|
||||
|
||||
GIT_UNUSED(delta);
|
||||
GIT_UNUSED(range);
|
||||
@ -861,34 +974,43 @@ static int diff_patch_line_cb(
|
||||
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) {
|
||||
line->lines = 0;
|
||||
while (content < content_end)
|
||||
if (*content++ == '\n')
|
||||
++line->lines;
|
||||
}
|
||||
|
||||
patch->content_size += content_len;
|
||||
|
||||
switch (line_origin) {
|
||||
case GIT_DIFF_LINE_ADDITION:
|
||||
patch->content_size += 1;
|
||||
case GIT_DIFF_LINE_DEL_EOFNL:
|
||||
line->oldno = -1;
|
||||
line->newno = patch->newno;
|
||||
patch->newno += line->lines;
|
||||
break;
|
||||
case GIT_DIFF_LINE_DELETION:
|
||||
patch->content_size += 1;
|
||||
case GIT_DIFF_LINE_ADD_EOFNL:
|
||||
line->oldno = patch->oldno;
|
||||
line->newno = -1;
|
||||
patch->oldno += line->lines;
|
||||
break;
|
||||
default:
|
||||
case GIT_DIFF_LINE_CONTEXT:
|
||||
patch->content_size += 1;
|
||||
patch->context_size += 1;
|
||||
case GIT_DIFF_LINE_CONTEXT_EOFNL:
|
||||
patch->context_size += content_len;
|
||||
line->oldno = patch->oldno;
|
||||
line->newno = patch->newno;
|
||||
patch->oldno += line->lines;
|
||||
patch->newno += line->lines;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
hunk->line_count++;
|
||||
|
225
src/diff_print.c
225
src/diff_print.c
@ -7,7 +7,7 @@
|
||||
#include "common.h"
|
||||
#include "diff.h"
|
||||
#include "diff_patch.h"
|
||||
#include "buffer.h"
|
||||
#include "fileops.h"
|
||||
|
||||
typedef struct {
|
||||
git_diff_list *diff;
|
||||
@ -21,14 +21,15 @@ static int diff_print_info_init(
|
||||
diff_print_info *pi,
|
||||
git_buf *out, git_diff_list *diff, git_diff_data_cb cb, void *payload)
|
||||
{
|
||||
assert(diff && diff->repo);
|
||||
|
||||
pi->diff = diff;
|
||||
pi->print_cb = cb;
|
||||
pi->payload = payload;
|
||||
pi->buf = out;
|
||||
|
||||
if (git_repository__cvar(&pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0)
|
||||
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 */
|
||||
@ -41,11 +42,11 @@ static int diff_print_info_init(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char pick_suffix(int mode)
|
||||
static char diff_pick_suffix(int mode)
|
||||
{
|
||||
if (S_ISDIR(mode))
|
||||
return '/';
|
||||
else if (mode & 0100) //-V536
|
||||
else if (GIT_PERMS_IS_EXEC(mode)) /* -V536 */
|
||||
/* in git, modes are very regular, so we must have 0100755 mode */
|
||||
return '*';
|
||||
else
|
||||
@ -76,45 +77,49 @@ static int callback_error(void)
|
||||
return GIT_EUSER;
|
||||
}
|
||||
|
||||
static int print_compact(
|
||||
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 = pick_suffix(delta->old_file.mode);
|
||||
new_suffix = pick_suffix(delta->new_file.mode);
|
||||
old_suffix = diff_pick_suffix(delta->old_file.mode);
|
||||
new_suffix = diff_pick_suffix(delta->new_file.mode);
|
||||
|
||||
git_buf_clear(pi->buf);
|
||||
git_buf_clear(out);
|
||||
|
||||
if (delta->old_file.path != delta->new_file.path &&
|
||||
pi->diff->strcomp(delta->old_file.path,delta->new_file.path) != 0)
|
||||
git_buf_printf(pi->buf, "%c\t%s%c -> %s%c\n", code,
|
||||
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(pi->buf, "%c\t%s%c (%o -> %o)\n", code,
|
||||
delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode);
|
||||
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 (old_suffix != ' ')
|
||||
git_buf_printf(pi->buf, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
|
||||
git_buf_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
|
||||
else
|
||||
git_buf_printf(pi->buf, "%c\t%s\n", code, delta->old_file.path);
|
||||
git_buf_printf(out, "%c\t%s\n", code, delta->old_file.path);
|
||||
|
||||
if (git_buf_oom(pi->buf))
|
||||
if (git_buf_oom(out))
|
||||
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))
|
||||
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,
|
||||
@ -125,17 +130,18 @@ int git_diff_print_compact(
|
||||
diff_print_info pi;
|
||||
|
||||
if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
|
||||
error = git_diff_foreach(diff, print_compact, NULL, NULL, &pi);
|
||||
error = git_diff_foreach(diff, diff_print_one_compact, NULL, NULL, &pi);
|
||||
|
||||
git_buf_free(&buf);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int print_raw(
|
||||
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];
|
||||
|
||||
@ -144,36 +150,37 @@ static int print_raw(
|
||||
if (code == ' ')
|
||||
return 0;
|
||||
|
||||
git_buf_clear(pi->buf);
|
||||
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(
|
||||
pi->buf, ":%06o %06o %s... %s... %c",
|
||||
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(pi->buf, "%03u", delta->similarity);
|
||||
git_buf_printf(out, "%03u", delta->similarity);
|
||||
|
||||
if (delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED)
|
||||
if (delta->old_file.path != delta->new_file.path)
|
||||
git_buf_printf(
|
||||
pi->buf, "\t%s %s\n", delta->old_file.path, delta->new_file.path);
|
||||
out, "\t%s %s\n", delta->old_file.path, delta->new_file.path);
|
||||
else
|
||||
git_buf_printf(
|
||||
pi->buf, "\t%s\n", delta->old_file.path ?
|
||||
out, "\t%s\n", delta->old_file.path ?
|
||||
delta->old_file.path : delta->new_file.path);
|
||||
|
||||
if (git_buf_oom(pi->buf))
|
||||
if (git_buf_oom(out))
|
||||
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))
|
||||
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,
|
||||
@ -184,72 +191,53 @@ int git_diff_print_raw(
|
||||
diff_print_info pi;
|
||||
|
||||
if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
|
||||
error = git_diff_foreach(diff, print_raw, NULL, NULL, &pi);
|
||||
error = git_diff_foreach(diff, diff_print_one_raw, NULL, NULL, &pi);
|
||||
|
||||
git_buf_free(&buf);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int print_oid_range(diff_print_info *pi, const git_diff_delta *delta)
|
||||
static int diff_print_oid_range(
|
||||
git_buf *out, const git_diff_delta *delta, int oid_strlen)
|
||||
{
|
||||
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);
|
||||
git_oid_tostr(start_oid, oid_strlen, &delta->old_file.oid);
|
||||
git_oid_tostr(end_oid, oid_strlen, &delta->new_file.oid);
|
||||
|
||||
/* TODO: Match git diff more closely */
|
||||
if (delta->old_file.mode == delta->new_file.mode) {
|
||||
git_buf_printf(pi->buf, "index %s..%s %o\n",
|
||||
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(pi->buf, "new file mode %o\n", delta->new_file.mode);
|
||||
git_buf_printf(out, "new file mode %o\n", delta->new_file.mode);
|
||||
} else if (delta->new_file.mode == 0) {
|
||||
git_buf_printf(pi->buf, "deleted file mode %o\n", delta->old_file.mode);
|
||||
git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode);
|
||||
} else {
|
||||
git_buf_printf(pi->buf, "old mode %o\n", delta->old_file.mode);
|
||||
git_buf_printf(pi->buf, "new mode %o\n", delta->new_file.mode);
|
||||
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(pi->buf, "index %s..%s\n", start_oid, end_oid);
|
||||
git_buf_printf(out, "index %s..%s\n", start_oid, end_oid);
|
||||
}
|
||||
|
||||
if (git_buf_oom(pi->buf))
|
||||
if (git_buf_oom(out))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_patch_file(
|
||||
const git_diff_delta *delta, float progress, void *data)
|
||||
static int diff_delta_format_with_paths(
|
||||
git_buf *out,
|
||||
const git_diff_delta *delta,
|
||||
const char *oldpfx,
|
||||
const char *newpfx,
|
||||
const char *template)
|
||||
{
|
||||
diff_print_info *pi = data;
|
||||
const char *oldpfx = pi->diff->opts.old_prefix;
|
||||
const char *oldpath = delta->old_file.path;
|
||||
const char *newpfx = pi->diff->opts.new_prefix;
|
||||
const char *newpath = delta->new_file.path;
|
||||
|
||||
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 &&
|
||||
(pi->diff->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 (print_oid_range(pi, delta) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_oid_iszero(&delta->old_file.oid)) {
|
||||
oldpfx = "";
|
||||
oldpath = "/dev/null";
|
||||
@ -259,12 +247,59 @@ static int print_patch_file(
|
||||
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);
|
||||
}
|
||||
return git_buf_printf(out, template, oldpfx, oldpath, newpfx, newpath);
|
||||
}
|
||||
|
||||
if (git_buf_oom(pi->buf))
|
||||
int git_diff_delta__format_file_header(
|
||||
git_buf *out,
|
||||
const git_diff_delta *delta,
|
||||
const char *oldpfx,
|
||||
const char *newpfx,
|
||||
int oid_strlen)
|
||||
{
|
||||
if (!oldpfx)
|
||||
oldpfx = DIFF_OLD_PREFIX_DEFAULT;
|
||||
if (!newpfx)
|
||||
newpfx = DIFF_NEW_PREFIX_DEFAULT;
|
||||
if (!oid_strlen)
|
||||
oid_strlen = GIT_ABBREV_DEFAULT + 1;
|
||||
|
||||
git_buf_clear(out);
|
||||
|
||||
git_buf_printf(out, "diff --git %s%s %s%s\n",
|
||||
oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
|
||||
|
||||
if (diff_print_oid_range(out, delta, oid_strlen) < 0)
|
||||
return -1;
|
||||
|
||||
if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
|
||||
diff_delta_format_with_paths(
|
||||
out, delta, oldpfx, newpfx, "--- %s%s\n+++ %s%s\n");
|
||||
|
||||
return git_buf_oom(out) ? -1 : 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 : DIFF_OLD_PREFIX_DEFAULT;
|
||||
const char *newpfx =
|
||||
pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT;
|
||||
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 (git_diff_delta__format_file_header(
|
||||
pi->buf, delta, oldpfx, newpfx, pi->oid_strlen) < 0)
|
||||
return -1;
|
||||
|
||||
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
|
||||
@ -275,10 +310,10 @@ static int print_patch_file(
|
||||
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))
|
||||
|
||||
if (diff_delta_format_with_paths(
|
||||
pi->buf, delta, oldpfx, newpfx,
|
||||
"Binary files %s%s and %s%s differ\n") < 0)
|
||||
return -1;
|
||||
|
||||
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_BINARY,
|
||||
@ -288,7 +323,7 @@ static int print_patch_file(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_patch_hunk(
|
||||
static int diff_print_patch_hunk(
|
||||
const git_diff_delta *d,
|
||||
const git_diff_range *r,
|
||||
const char *header,
|
||||
@ -311,7 +346,7 @@ static int print_patch_hunk(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_patch_line(
|
||||
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 */
|
||||
@ -343,6 +378,7 @@ static int print_patch_line(
|
||||
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,
|
||||
@ -354,27 +390,15 @@ int git_diff_print_patch(
|
||||
|
||||
if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
|
||||
error = git_diff_foreach(
|
||||
diff, print_patch_file, print_patch_hunk, print_patch_line, &pi);
|
||||
diff, diff_print_patch_file, diff_print_patch_hunk,
|
||||
diff_print_patch_line, &pi);
|
||||
|
||||
git_buf_free(&buf);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
static int 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 an output callback */
|
||||
int git_diff_patch_print(
|
||||
git_diff_patch *patch,
|
||||
git_diff_data_cb print_cb,
|
||||
@ -389,13 +413,28 @@ int git_diff_patch_print(
|
||||
if (!(error = diff_print_info_init(
|
||||
&pi, &temp, git_diff_patch__diff(patch), print_cb, payload)))
|
||||
error = git_diff_patch__invoke_callbacks(
|
||||
patch, print_patch_file, print_patch_hunk, print_patch_line, &pi);
|
||||
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)
|
||||
@ -403,7 +442,7 @@ int git_diff_patch_to_str(
|
||||
int error;
|
||||
git_buf output = GIT_BUF_INIT;
|
||||
|
||||
error = git_diff_patch_print(patch, print_to_buffer_cb, &output);
|
||||
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...
|
||||
|
493
src/diff_tform.c
493
src/diff_tform.c
@ -408,57 +408,99 @@ GIT_INLINE(git_diff_file *) similarity_get_file(git_diff_list *diff, size_t idx)
|
||||
return (idx & 1) ? &delta->new_file : &delta->old_file;
|
||||
}
|
||||
|
||||
static int similarity_calc(
|
||||
git_diff_list *diff,
|
||||
typedef struct {
|
||||
size_t idx;
|
||||
git_iterator_type_t src;
|
||||
git_repository *repo;
|
||||
git_diff_file *file;
|
||||
git_buf data;
|
||||
git_odb_object *odb_obj;
|
||||
git_blob *blob;
|
||||
} similarity_info;
|
||||
|
||||
static int similarity_init(
|
||||
similarity_info *info, git_diff_list *diff, size_t file_idx)
|
||||
{
|
||||
info->idx = file_idx;
|
||||
info->src = (file_idx & 1) ? diff->new_src : diff->old_src;
|
||||
info->repo = diff->repo;
|
||||
info->file = similarity_get_file(diff, file_idx);
|
||||
info->odb_obj = NULL;
|
||||
info->blob = NULL;
|
||||
git_buf_init(&info->data, 0);
|
||||
|
||||
if (info->file->size > 0)
|
||||
return 0;
|
||||
|
||||
return git_diff_file__resolve_zero_size(
|
||||
info->file, &info->odb_obj, info->repo);
|
||||
}
|
||||
|
||||
static int similarity_sig(
|
||||
similarity_info *info,
|
||||
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->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 */
|
||||
git_diff_file *file = info->file;
|
||||
|
||||
if (info->src == GIT_ITERATOR_TYPE_WORKDIR) {
|
||||
if ((error = git_buf_joinpath(
|
||||
&path, git_repository_workdir(diff->repo), file->path)) < 0)
|
||||
&info->data, git_repository_workdir(info->repo), file->path)) < 0)
|
||||
return error;
|
||||
|
||||
/* if path is not a regular file, just skip this item */
|
||||
if (git_path_isfile(path.ptr))
|
||||
if (!git_path_isfile(info->data.ptr))
|
||||
return 0;
|
||||
|
||||
/* TODO: apply wd-to-odb filters to file data if necessary */
|
||||
|
||||
error = opts->metric->file_signature(
|
||||
&cache[file_idx], file, path.ptr, opts->metric->payload);
|
||||
&cache[info->idx], info->file,
|
||||
info->data.ptr, opts->metric->payload);
|
||||
} else {
|
||||
/* if we didn't initially know the size, we might have an odb_obj
|
||||
* around from earlier, so convert that, otherwise load the blob now
|
||||
*/
|
||||
if (info->odb_obj != NULL)
|
||||
error = git_object__from_odb_object(
|
||||
(git_object **)&info->blob, info->repo,
|
||||
info->odb_obj, GIT_OBJ_BLOB);
|
||||
else
|
||||
error = git_blob_lookup(&info->blob, info->repo, &file->oid);
|
||||
|
||||
git_buf_free(&path);
|
||||
} else { /* compute hashsig from blob buffer */
|
||||
git_blob *blob = NULL;
|
||||
git_off_t blobsize;
|
||||
|
||||
/* TODO: add max size threshold a la diff? */
|
||||
|
||||
if (git_blob_lookup(&blob, diff->repo, &file->oid) < 0) {
|
||||
if (error < 0) {
|
||||
/* if lookup fails, just skip this item in similarity calc */
|
||||
giterr_clear();
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
size_t sz;
|
||||
|
||||
blobsize = git_blob_rawsize(blob);
|
||||
if (!git__is_sizet(blobsize)) /* ? what to do ? */
|
||||
blobsize = (size_t)-1;
|
||||
/* index size may not be actual blob size if filtered */
|
||||
if (file->size != git_blob_rawsize(info->blob))
|
||||
file->size = git_blob_rawsize(info->blob);
|
||||
|
||||
sz = (size_t)(git__is_sizet(file->size) ? file->size : -1);
|
||||
|
||||
error = opts->metric->buffer_signature(
|
||||
&cache[file_idx], file, git_blob_rawcontent(blob),
|
||||
(size_t)blobsize, opts->metric->payload);
|
||||
|
||||
git_blob_free(blob);
|
||||
&cache[info->idx], info->file,
|
||||
git_blob_rawcontent(info->blob), sz, opts->metric->payload);
|
||||
}
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void similarity_unload(similarity_info *info)
|
||||
{
|
||||
if (info->odb_obj)
|
||||
git_odb_object_free(info->odb_obj);
|
||||
|
||||
if (info->blob)
|
||||
git_blob_free(info->blob);
|
||||
else
|
||||
git_buf_free(&info->data);
|
||||
}
|
||||
|
||||
#define FLAG_SET(opts,flag_name) (((opts)->flags & flag_name) != 0)
|
||||
|
||||
/* - score < 0 means files cannot be compared
|
||||
@ -476,6 +518,8 @@ static int similarity_measure(
|
||||
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);
|
||||
int error = 0;
|
||||
similarity_info a_info, b_info;
|
||||
|
||||
*score = -1;
|
||||
|
||||
@ -483,7 +527,7 @@ static int similarity_measure(
|
||||
if (GIT_MODE_TYPE(a_file->mode) != GIT_MODE_TYPE(b_file->mode))
|
||||
return 0;
|
||||
|
||||
/* if exact match is requested, force calculation of missing OIDs */
|
||||
/* 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 &&
|
||||
@ -510,19 +554,44 @@ static int similarity_measure(
|
||||
return 0;
|
||||
}
|
||||
|
||||
memset(&a_info, 0, sizeof(a_info));
|
||||
memset(&b_info, 0, sizeof(b_info));
|
||||
|
||||
/* set up similarity data (will try to update missing file sizes) */
|
||||
if (!cache[a_idx] && (error = similarity_init(&a_info, diff, a_idx)) < 0)
|
||||
return error;
|
||||
if (!cache[b_idx] && (error = similarity_init(&b_info, diff, b_idx)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* check if file sizes are nowhere near each other */
|
||||
if (a_file->size > 127 &&
|
||||
b_file->size > 127 &&
|
||||
(a_file->size > (b_file->size << 3) ||
|
||||
b_file->size > (a_file->size << 3)))
|
||||
goto cleanup;
|
||||
|
||||
/* 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;
|
||||
if (!cache[a_idx]) {
|
||||
if ((error = similarity_sig(&a_info, opts, cache)) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
if (!cache[b_idx]) {
|
||||
if ((error = similarity_sig(&b_info, opts, cache)) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* some metrics may not wish to process this file (too big / too small) */
|
||||
if (!cache[a_idx] || !cache[b_idx])
|
||||
return 0;
|
||||
|
||||
/* compare signatures */
|
||||
return opts->metric->similarity(
|
||||
/* calculate similarity provided that the metric choose to process
|
||||
* both the a and b files (some may not if file is too big, etc).
|
||||
*/
|
||||
if (cache[a_idx] && cache[b_idx])
|
||||
error = opts->metric->similarity(
|
||||
score, cache[a_idx], cache[b_idx], opts->metric->payload);
|
||||
|
||||
cleanup:
|
||||
similarity_unload(&a_info);
|
||||
similarity_unload(&b_info);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int calc_self_similarity(
|
||||
@ -590,11 +659,13 @@ static bool is_rename_target(
|
||||
return false;
|
||||
|
||||
case GIT_DELTA_UNTRACKED:
|
||||
case GIT_DELTA_IGNORED:
|
||||
if (!FLAG_SET(opts, GIT_DIFF_FIND_FOR_UNTRACKED))
|
||||
return false;
|
||||
break;
|
||||
|
||||
case GIT_DELTA_IGNORED:
|
||||
return false;
|
||||
|
||||
default: /* all other status values should be checked */
|
||||
break;
|
||||
}
|
||||
@ -673,6 +744,15 @@ GIT_INLINE(bool) delta_is_new_only(git_diff_delta *delta)
|
||||
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;
|
||||
@ -682,85 +762,156 @@ int git_diff_find_similar(
|
||||
git_diff_list *diff,
|
||||
git_diff_find_options *given_opts)
|
||||
{
|
||||
size_t i, j, cache_size;
|
||||
size_t s, t;
|
||||
int error = 0, similarity;
|
||||
git_diff_delta *from, *to;
|
||||
git_diff_delta *src, *tgt;
|
||||
git_diff_find_options opts;
|
||||
size_t num_rewrites = 0, num_updates = 0;
|
||||
void **cache; /* cache of similarity metric file signatures */
|
||||
diff_find_match *match_sources, *match_targets; /* cache of best matches */
|
||||
size_t num_deltas, num_srcs = 0, num_tgts = 0;
|
||||
size_t tried_srcs = 0, tried_tgts = 0;
|
||||
size_t num_rewrites = 0, num_updates = 0, num_bumped = 0;
|
||||
void **sigcache; /* cache of similarity metric file signatures */
|
||||
diff_find_match *tgt2src = NULL;
|
||||
diff_find_match *src2tgt = NULL;
|
||||
diff_find_match *tgt2src_copy = NULL;
|
||||
diff_find_match *best_match;
|
||||
git_diff_file swap;
|
||||
|
||||
if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0)
|
||||
return error;
|
||||
|
||||
num_deltas = diff->deltas.length;
|
||||
|
||||
/* TODO: maybe abort if deltas.length > rename_limit ??? */
|
||||
if (!git__is_uint32(diff->deltas.length))
|
||||
if (!git__is_uint32(num_deltas))
|
||||
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 = git__calloc(num_deltas * 2, sizeof(void *));
|
||||
GITERR_CHECK_ALLOC(sigcache);
|
||||
|
||||
match_sources = git__calloc(diff->deltas.length, sizeof(diff_find_match));
|
||||
match_targets = git__calloc(diff->deltas.length, sizeof(diff_find_match));
|
||||
GITERR_CHECK_ALLOC(match_sources);
|
||||
GITERR_CHECK_ALLOC(match_targets);
|
||||
/* 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, t, tgt) {
|
||||
if (is_rename_source(diff, &opts, t, sigcache))
|
||||
++num_srcs;
|
||||
|
||||
/* next find the most similar delta for each rename / copy candidate */
|
||||
if (is_rename_target(diff, &opts, t, sigcache))
|
||||
++num_tgts;
|
||||
|
||||
git_vector_foreach(&diff->deltas, i, to) {
|
||||
size_t tried_sources = 0;
|
||||
if ((tgt->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0)
|
||||
num_rewrites++;
|
||||
}
|
||||
|
||||
match_targets[i].idx = (uint32_t)i;
|
||||
match_targets[i].similarity = 0;
|
||||
|
||||
/* skip things that are not rename targets */
|
||||
if (!is_rename_target(diff, &opts, i, cache))
|
||||
continue;
|
||||
|
||||
git_vector_foreach(&diff->deltas, j, from) {
|
||||
if (i == j)
|
||||
continue;
|
||||
|
||||
/* skip things that are not rename sources */
|
||||
if (!is_rename_source(diff, &opts, j, cache))
|
||||
continue;
|
||||
|
||||
/* cap on maximum targets we'll examine (per "to" file) */
|
||||
if (++tried_sources > opts.rename_limit)
|
||||
break;
|
||||
|
||||
/* calculate similarity for this pair and find best match */
|
||||
if ((error = similarity_measure(
|
||||
&similarity, diff, &opts, cache, 2 * j, 2 * i + 1)) < 0)
|
||||
/* if there are no candidate srcs or tgts, we're done */
|
||||
if (!num_srcs || !num_tgts)
|
||||
goto cleanup;
|
||||
|
||||
if (similarity < 0) { /* not actually comparable */
|
||||
--tried_sources;
|
||||
src2tgt = git__calloc(num_deltas, sizeof(diff_find_match));
|
||||
GITERR_CHECK_ALLOC(src2tgt);
|
||||
tgt2src = git__calloc(num_deltas, sizeof(diff_find_match));
|
||||
GITERR_CHECK_ALLOC(tgt2src);
|
||||
|
||||
if (FLAG_SET(&opts, GIT_DIFF_FIND_COPIES)) {
|
||||
tgt2src_copy = git__calloc(num_deltas, sizeof(diff_find_match));
|
||||
GITERR_CHECK_ALLOC(tgt2src_copy);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find best-fit matches for rename / copy candidates
|
||||
*/
|
||||
|
||||
find_best_matches:
|
||||
tried_tgts = num_bumped = 0;
|
||||
|
||||
git_vector_foreach(&diff->deltas, t, tgt) {
|
||||
/* skip things that are not rename targets */
|
||||
if ((tgt->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
|
||||
continue;
|
||||
|
||||
tried_srcs = 0;
|
||||
|
||||
git_vector_foreach(&diff->deltas, s, src) {
|
||||
/* skip things that are not rename sources */
|
||||
if ((src->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) == 0)
|
||||
continue;
|
||||
|
||||
/* calculate similarity for this pair and find best match */
|
||||
if (s == t)
|
||||
similarity = -1; /* don't measure self-similarity here */
|
||||
else if ((error = similarity_measure(
|
||||
&similarity, diff, &opts, sigcache, 2 * s, 2 * t + 1)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (similarity < 0)
|
||||
continue;
|
||||
|
||||
/* is this a better rename? */
|
||||
if (tgt2src[t].similarity < (uint32_t)similarity &&
|
||||
src2tgt[s].similarity < (uint32_t)similarity)
|
||||
{
|
||||
/* eject old mapping */
|
||||
if (src2tgt[s].similarity > 0) {
|
||||
tgt2src[src2tgt[s].idx].similarity = 0;
|
||||
num_bumped++;
|
||||
}
|
||||
if (tgt2src[t].similarity > 0) {
|
||||
src2tgt[tgt2src[t].idx].similarity = 0;
|
||||
num_bumped++;
|
||||
}
|
||||
|
||||
if (match_targets[i].similarity < (uint32_t)similarity &&
|
||||
match_sources[j].similarity < (uint32_t)similarity) {
|
||||
match_targets[i].similarity = (uint32_t)similarity;
|
||||
match_sources[j].similarity = (uint32_t)similarity;
|
||||
match_targets[i].idx = (uint32_t)j;
|
||||
match_sources[j].idx = (uint32_t)i;
|
||||
}
|
||||
}
|
||||
/* write new mapping */
|
||||
tgt2src[t].idx = (uint32_t)s;
|
||||
tgt2src[t].similarity = (uint32_t)similarity;
|
||||
src2tgt[s].idx = (uint32_t)t;
|
||||
src2tgt[s].similarity = (uint32_t)similarity;
|
||||
}
|
||||
|
||||
/* next rewrite the diffs with renames / copies */
|
||||
/* keep best absolute match for copies */
|
||||
if (tgt2src_copy != NULL &&
|
||||
tgt2src_copy[t].similarity < (uint32_t)similarity)
|
||||
{
|
||||
tgt2src_copy[t].idx = (uint32_t)s;
|
||||
tgt2src_copy[t].similarity = (uint32_t)similarity;
|
||||
}
|
||||
|
||||
if (++tried_srcs >= num_srcs)
|
||||
break;
|
||||
|
||||
/* cap on maximum targets we'll examine (per "tgt" file) */
|
||||
if (tried_srcs > opts.rename_limit)
|
||||
break;
|
||||
}
|
||||
|
||||
if (++tried_tgts >= num_tgts)
|
||||
break;
|
||||
}
|
||||
|
||||
if (num_bumped > 0) /* try again if we bumped some items */
|
||||
goto find_best_matches;
|
||||
|
||||
/*
|
||||
* Rewrite the diffs with renames / copies
|
||||
*/
|
||||
|
||||
tried_tgts = 0;
|
||||
|
||||
git_vector_foreach(&diff->deltas, t, tgt) {
|
||||
/* skip things that are not rename targets */
|
||||
if ((tgt->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
|
||||
continue;
|
||||
|
||||
git_vector_foreach(&diff->deltas, i, to) {
|
||||
/* check if this delta was the target of a similarity */
|
||||
if ((similarity = (int)match_targets[i].similarity) <= 0)
|
||||
if (tgt2src[t].similarity)
|
||||
best_match = &tgt2src[t];
|
||||
else if (tgt2src_copy && tgt2src_copy[t].similarity)
|
||||
best_match = &tgt2src_copy[t];
|
||||
else
|
||||
continue;
|
||||
|
||||
assert(to && (to->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) != 0);
|
||||
|
||||
from = GIT_VECTOR_GET(&diff->deltas, match_targets[i].idx);
|
||||
assert(from && (from->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) != 0);
|
||||
s = best_match->idx;
|
||||
src = GIT_VECTOR_GET(&diff->deltas, s);
|
||||
|
||||
/* possible scenarios:
|
||||
* 1. from DELETE to ADD/UNTRACK/IGNORE = RENAME
|
||||
@ -770,135 +921,137 @@ int git_diff_find_similar(
|
||||
* 5. from OTHER to ADD/UNTRACK/IGNORE = OTHER + COPY
|
||||
*/
|
||||
|
||||
if (from->status == GIT_DELTA_DELETED) {
|
||||
if (src->status == GIT_DELTA_DELETED) {
|
||||
|
||||
if (delta_is_new_only(to)) {
|
||||
if (delta_is_new_only(tgt)) {
|
||||
|
||||
if (similarity < (int)opts.rename_threshold)
|
||||
if (best_match->similarity < opts.rename_threshold)
|
||||
continue;
|
||||
|
||||
from->status = GIT_DELTA_RENAMED;
|
||||
from->similarity = (uint32_t)similarity;
|
||||
memcpy(&from->new_file, &to->new_file, sizeof(from->new_file));
|
||||
|
||||
to->flags |= GIT_DIFF_FLAG__TO_DELETE;
|
||||
delta_make_rename(tgt, src, best_match->similarity);
|
||||
|
||||
src->flags |= GIT_DIFF_FLAG__TO_DELETE;
|
||||
num_rewrites++;
|
||||
} else {
|
||||
assert(delta_is_split(to));
|
||||
assert(delta_is_split(tgt));
|
||||
|
||||
if (similarity < (int)opts.rename_from_rewrite_threshold)
|
||||
if (best_match->similarity < opts.rename_from_rewrite_threshold)
|
||||
continue;
|
||||
|
||||
from->status = GIT_DELTA_RENAMED;
|
||||
from->similarity = (uint32_t)similarity;
|
||||
memcpy(&from->new_file, &to->new_file, sizeof(from->new_file));
|
||||
memcpy(&swap, &tgt->old_file, sizeof(swap));
|
||||
|
||||
to->status = GIT_DELTA_DELETED;
|
||||
memset(&to->new_file, 0, sizeof(to->new_file));
|
||||
to->new_file.path = to->old_file.path;
|
||||
to->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
|
||||
if ((to->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) {
|
||||
to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
|
||||
delta_make_rename(tgt, src, best_match->similarity);
|
||||
num_rewrites--;
|
||||
}
|
||||
|
||||
src->status = GIT_DELTA_DELETED;
|
||||
memcpy(&src->old_file, &swap, sizeof(src->old_file));
|
||||
memset(&src->new_file, 0, sizeof(src->new_file));
|
||||
src->new_file.path = src->old_file.path;
|
||||
src->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
|
||||
|
||||
num_updates++;
|
||||
|
||||
if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) {
|
||||
/* what used to be at src t is now at src s */
|
||||
tgt2src[src2tgt[t].idx].idx = (uint32_t)s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else if (delta_is_split(from)) {
|
||||
git_diff_file swap;
|
||||
else if (delta_is_split(src)) {
|
||||
|
||||
if (delta_is_new_only(to)) {
|
||||
if (delta_is_new_only(tgt)) {
|
||||
|
||||
if (similarity < (int)opts.rename_threshold)
|
||||
if (best_match->similarity < opts.rename_threshold)
|
||||
continue;
|
||||
|
||||
memcpy(&swap, &from->new_file, sizeof(swap));
|
||||
delta_make_rename(tgt, src, best_match->similarity);
|
||||
|
||||
from->status = GIT_DELTA_RENAMED;
|
||||
from->similarity = (uint32_t)similarity;
|
||||
memcpy(&from->new_file, &to->new_file, sizeof(from->new_file));
|
||||
if ((from->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) {
|
||||
from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
|
||||
num_rewrites--;
|
||||
}
|
||||
|
||||
to->status = (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ?
|
||||
src->status = (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ?
|
||||
GIT_DELTA_UNTRACKED : GIT_DELTA_ADDED;
|
||||
memcpy(&to->new_file, &swap, sizeof(to->new_file));
|
||||
to->old_file.path = to->new_file.path;
|
||||
memset(&src->old_file, 0, sizeof(src->old_file));
|
||||
src->old_file.path = src->new_file.path;
|
||||
src->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
|
||||
|
||||
src->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
|
||||
num_rewrites--;
|
||||
|
||||
num_updates++;
|
||||
} else {
|
||||
assert(delta_is_split(from));
|
||||
assert(delta_is_split(src));
|
||||
|
||||
if (similarity < (int)opts.rename_from_rewrite_threshold)
|
||||
if (best_match->similarity < opts.rename_from_rewrite_threshold)
|
||||
continue;
|
||||
|
||||
memcpy(&swap, &to->new_file, sizeof(swap));
|
||||
memcpy(&swap, &tgt->old_file, sizeof(swap));
|
||||
|
||||
to->status = GIT_DELTA_RENAMED;
|
||||
to->similarity = (uint32_t)similarity;
|
||||
memcpy(&to->new_file, &from->new_file, sizeof(to->new_file));
|
||||
if ((to->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0) {
|
||||
to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
|
||||
delta_make_rename(tgt, src, best_match->similarity);
|
||||
num_rewrites--;
|
||||
}
|
||||
num_updates++;
|
||||
|
||||
memcpy(&from->new_file, &swap, sizeof(from->new_file));
|
||||
if ((from->flags & GIT_DIFF_FLAG__TO_SPLIT) == 0) {
|
||||
from->flags |= GIT_DIFF_FLAG__TO_SPLIT;
|
||||
num_rewrites++;
|
||||
}
|
||||
memcpy(&src->old_file, &swap, sizeof(src->old_file));
|
||||
|
||||
/* in the off chance that we've just swapped the new
|
||||
* element into the correct place, clear the SPLIT flag
|
||||
/* if we've just swapped the new element into the correct
|
||||
* place, clear the SPLIT flag
|
||||
*/
|
||||
if (match_targets[match_targets[i].idx].idx == i &&
|
||||
match_targets[match_targets[i].idx].similarity >
|
||||
if (tgt2src[s].idx == t &&
|
||||
tgt2src[s].similarity >
|
||||
opts.rename_from_rewrite_threshold) {
|
||||
|
||||
from->status = GIT_DELTA_RENAMED;
|
||||
from->similarity =
|
||||
(uint32_t)match_targets[match_targets[i].idx].similarity;
|
||||
match_targets[match_targets[i].idx].similarity = 0;
|
||||
from->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
|
||||
src->status = GIT_DELTA_RENAMED;
|
||||
src->similarity = tgt2src[s].similarity;
|
||||
tgt2src[s].similarity = 0;
|
||||
src->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
|
||||
num_rewrites--;
|
||||
}
|
||||
/* otherwise, if we just overwrote a source, update mapping */
|
||||
else if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) {
|
||||
/* what used to be at src t is now at src s */
|
||||
tgt2src[src2tgt[t].idx].idx = (uint32_t)s;
|
||||
}
|
||||
|
||||
num_updates++;
|
||||
}
|
||||
}
|
||||
|
||||
else if (delta_is_new_only(to)) {
|
||||
if (!FLAG_SET(&opts, GIT_DIFF_FIND_COPIES) ||
|
||||
similarity < (int)opts.copy_threshold)
|
||||
else if (delta_is_new_only(tgt)) {
|
||||
if (!FLAG_SET(&opts, GIT_DIFF_FIND_COPIES))
|
||||
continue;
|
||||
|
||||
to->status = GIT_DELTA_COPIED;
|
||||
to->similarity = (uint32_t)similarity;
|
||||
memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
|
||||
if (tgt2src_copy[t].similarity < opts.copy_threshold)
|
||||
continue;
|
||||
|
||||
/* always use best possible source for copy */
|
||||
best_match = &tgt2src_copy[t];
|
||||
src = GIT_VECTOR_GET(&diff->deltas, best_match->idx);
|
||||
|
||||
tgt->status = GIT_DELTA_COPIED;
|
||||
tgt->similarity = best_match->similarity;
|
||||
memcpy(&tgt->old_file, &src->old_file, sizeof(tgt->old_file));
|
||||
|
||||
num_updates++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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,
|
||||
FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES));
|
||||
FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES) &&
|
||||
!FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY));
|
||||
|
||||
cleanup:
|
||||
git__free(match_sources);
|
||||
git__free(match_targets);
|
||||
git__free(tgt2src);
|
||||
git__free(src2tgt);
|
||||
git__free(tgt2src_copy);
|
||||
|
||||
for (i = 0; i < cache_size; ++i) {
|
||||
if (cache[i] != NULL)
|
||||
opts.metric->free_signature(cache[i], opts.metric->payload);
|
||||
for (t = 0; t < num_deltas * 2; ++t) {
|
||||
if (sigcache[t] != NULL)
|
||||
opts.metric->free_signature(sigcache[t], opts.metric->payload);
|
||||
}
|
||||
git__free(cache);
|
||||
git__free(sigcache);
|
||||
|
||||
if (!given_opts || !given_opts->metric)
|
||||
git__free(opts.metric);
|
||||
|
@ -53,7 +53,7 @@ static int lock_file(git_filebuf *file, int flags)
|
||||
giterr_clear(); /* actual OS error code just confuses */
|
||||
giterr_set(GITERR_OS,
|
||||
"Failed to lock file '%s' for writing", file->path_lock);
|
||||
return -1;
|
||||
return GIT_ELOCKED;
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ static int lock_file(git_filebuf *file, int flags)
|
||||
}
|
||||
|
||||
if (file->fd < 0)
|
||||
return -1;
|
||||
return file->fd;
|
||||
|
||||
file->fd_is_open = true;
|
||||
|
||||
@ -197,7 +197,7 @@ static int write_deflate(git_filebuf *file, void *source, size_t len)
|
||||
|
||||
int git_filebuf_open(git_filebuf *file, const char *path, int flags)
|
||||
{
|
||||
int compression;
|
||||
int compression, error = -1;
|
||||
size_t path_len;
|
||||
|
||||
/* opening an already open buffer is a programming error;
|
||||
@ -282,7 +282,7 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
|
||||
memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH);
|
||||
|
||||
/* open the file for locking */
|
||||
if (lock_file(file, flags) < 0)
|
||||
if ((error = lock_file(file, flags)) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@ -290,7 +290,7 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
|
||||
|
||||
cleanup:
|
||||
git_filebuf_cleanup(file);
|
||||
return -1;
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_filebuf_hash(git_oid *oid, git_filebuf *file)
|
||||
|
@ -58,17 +58,19 @@ int git_futils_creat_locked(const char *path, const mode_t mode)
|
||||
int fd;
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
wchar_t buf[GIT_WIN_PATH];
|
||||
git_win32_path buf;
|
||||
|
||||
git__utf8_to_16(buf, GIT_WIN_PATH, path);
|
||||
fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
|
||||
git_win32_path_from_c(buf, path);
|
||||
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) {
|
||||
giterr_set(GITERR_OS, "Failed to create locked file '%s'", path);
|
||||
return -1;
|
||||
return errno == EEXIST ? GIT_ELOCKED : -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
@ -108,7 +110,7 @@ git_off_t git_futils_filesize(git_file fd)
|
||||
mode_t git_futils_canonical_mode(mode_t raw_mode)
|
||||
{
|
||||
if (S_ISREG(raw_mode))
|
||||
return S_IFREG | GIT_CANONICAL_PERMS(raw_mode);
|
||||
return S_IFREG | GIT_PERMS_CANONICAL(raw_mode);
|
||||
else if (S_ISLNK(raw_mode))
|
||||
return S_IFLNK;
|
||||
else if (S_ISGITLINK(raw_mode))
|
||||
@ -145,6 +147,7 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len)
|
||||
int git_futils_readbuffer_updated(
|
||||
git_buf *buf, const char *path, time_t *mtime, size_t *size, int *updated)
|
||||
{
|
||||
int error = 0;
|
||||
git_file fd;
|
||||
struct stat st;
|
||||
bool changed = false;
|
||||
@ -154,11 +157,15 @@ int git_futils_readbuffer_updated(
|
||||
if (updated != NULL)
|
||||
*updated = 0;
|
||||
|
||||
if ((fd = git_futils_open_ro(path)) < 0)
|
||||
return fd;
|
||||
if (p_stat(path, &st) < 0) {
|
||||
error = errno;
|
||||
giterr_set(GITERR_OS, "Failed to stat '%s'", path);
|
||||
if (error == ENOENT || error == ENOTDIR)
|
||||
return GIT_ENOTFOUND;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
|
||||
p_close(fd);
|
||||
if (S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
|
||||
giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
@ -175,7 +182,6 @@ int git_futils_readbuffer_updated(
|
||||
changed = true;
|
||||
|
||||
if (!changed) {
|
||||
p_close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -184,6 +190,9 @@ int git_futils_readbuffer_updated(
|
||||
if (size != NULL)
|
||||
*size = (size_t)st.st_size;
|
||||
|
||||
if ((fd = git_futils_open_ro(path)) < 0)
|
||||
return fd;
|
||||
|
||||
if (git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size) < 0) {
|
||||
p_close(fd);
|
||||
return -1;
|
||||
@ -220,6 +229,7 @@ int git_futils_writebuffer(
|
||||
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);
|
||||
return error;
|
||||
}
|
||||
|
||||
if ((error = p_close(fd)) < 0)
|
||||
@ -320,7 +330,7 @@ int git_futils_mkdir(
|
||||
min_root_len = git_path_root(make_path.ptr);
|
||||
if (root < min_root_len)
|
||||
root = min_root_len;
|
||||
while (make_path.ptr[root] == '/')
|
||||
while (root >= 0 && make_path.ptr[root] == '/')
|
||||
++root;
|
||||
|
||||
/* clip root to make_path length */
|
||||
@ -625,6 +635,18 @@ static git_futils_dirs_guess_cb git_futils__dir_guess[GIT_FUTILS_DIR__MAX] = {
|
||||
git_futils_guess_xdg_dirs,
|
||||
};
|
||||
|
||||
int git_futils_dirs_global_init(void)
|
||||
{
|
||||
git_futils_dir_t i;
|
||||
const git_buf *path;
|
||||
int error = 0;
|
||||
|
||||
for (i = 0; !error && i < GIT_FUTILS_DIR__MAX; i++)
|
||||
error = git_futils_dirs_get(&path, i);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int git_futils_check_selector(git_futils_dir_t which)
|
||||
{
|
||||
if (which < GIT_FUTILS_DIR__MAX)
|
||||
@ -847,6 +869,7 @@ typedef struct {
|
||||
uint32_t flags;
|
||||
uint32_t mkdir_flags;
|
||||
mode_t dirmode;
|
||||
int error;
|
||||
} cp_r_info;
|
||||
|
||||
#define GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT (1u << 10)
|
||||
@ -885,20 +908,23 @@ static int _cp_r_callback(void *ref, git_buf *from)
|
||||
return 0;
|
||||
|
||||
if (git_buf_joinpath(
|
||||
&info->to, info->to_root, from->ptr + info->from_prefix) < 0)
|
||||
return -1;
|
||||
&info->to, info->to_root, from->ptr + info->from_prefix) < 0) {
|
||||
error = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (p_lstat(info->to.ptr, &to_st) < 0) {
|
||||
if (errno != ENOENT && errno != ENOTDIR) {
|
||||
giterr_set(GITERR_OS,
|
||||
"Could not access %s while copying files", info->to.ptr);
|
||||
return -1;
|
||||
error = -1;
|
||||
goto exit;
|
||||
}
|
||||
} else
|
||||
exists = true;
|
||||
|
||||
if ((error = git_path_lstat(from->ptr, &from_st)) < 0)
|
||||
return error;
|
||||
goto exit;
|
||||
|
||||
if (S_ISDIR(from_st.st_mode)) {
|
||||
mode_t oldmode = info->dirmode;
|
||||
@ -912,13 +938,14 @@ static int _cp_r_callback(void *ref, git_buf *from)
|
||||
error = _cp_r_mkdir(info, from);
|
||||
|
||||
/* recurse onto target directory */
|
||||
if (!error && (!exists || S_ISDIR(to_st.st_mode)))
|
||||
error = git_path_direach(from, _cp_r_callback, info);
|
||||
if (!error && (!exists || S_ISDIR(to_st.st_mode)) &&
|
||||
((error = git_path_direach(from, _cp_r_callback, info)) == GIT_EUSER))
|
||||
error = info->error;
|
||||
|
||||
if (oldmode != 0)
|
||||
info->dirmode = oldmode;
|
||||
|
||||
return error;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (exists) {
|
||||
@ -928,7 +955,8 @@ static int _cp_r_callback(void *ref, git_buf *from)
|
||||
if (p_unlink(info->to.ptr) < 0) {
|
||||
giterr_set(GITERR_OS, "Cannot overwrite existing file '%s'",
|
||||
info->to.ptr);
|
||||
return -1;
|
||||
error = -1;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
@ -941,7 +969,7 @@ static int _cp_r_callback(void *ref, git_buf *from)
|
||||
/* Make container directory on demand if needed */
|
||||
if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 &&
|
||||
(error = _cp_r_mkdir(info, from)) < 0)
|
||||
return error;
|
||||
goto exit;
|
||||
|
||||
/* make symlink or regular file */
|
||||
if (S_ISLNK(from_st.st_mode))
|
||||
@ -950,11 +978,13 @@ static int _cp_r_callback(void *ref, git_buf *from)
|
||||
mode_t usemode = from_st.st_mode;
|
||||
|
||||
if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0)
|
||||
usemode = (usemode & 0111) ? 0777 : 0666;
|
||||
usemode = GIT_PERMS_FOR_WRITE(usemode);
|
||||
|
||||
error = git_futils_cp(from->ptr, info->to.ptr, usemode);
|
||||
}
|
||||
|
||||
exit:
|
||||
info->error = error;
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -975,6 +1005,7 @@ int git_futils_cp_r(
|
||||
info.flags = flags;
|
||||
info.dirmode = dirmode;
|
||||
info.from_prefix = path.size;
|
||||
info.error = 0;
|
||||
git_buf_init(&info.to, 0);
|
||||
|
||||
/* precalculate mkdir flags */
|
||||
@ -996,6 +1027,9 @@ int git_futils_cp_r(
|
||||
git_buf_free(&path);
|
||||
git_buf_free(&info.to);
|
||||
|
||||
if (error == GIT_EUSER)
|
||||
error = info.error;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -223,9 +223,13 @@ extern int git_futils_open_ro(const char *path);
|
||||
*/
|
||||
extern git_off_t git_futils_filesize(git_file fd);
|
||||
|
||||
#define GIT_PERMS_IS_EXEC(MODE) (((MODE) & 0111) != 0)
|
||||
#define GIT_PERMS_CANONICAL(MODE) (GIT_PERMS_IS_EXEC(MODE) ? 0755 : 0644)
|
||||
#define GIT_PERMS_FOR_WRITE(MODE) (GIT_PERMS_IS_EXEC(MODE) ? 0777 : 0666)
|
||||
|
||||
#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_TYPE_MASK 0170000
|
||||
#define GIT_MODE_TYPE(MODE) ((MODE) & GIT_MODE_TYPE_MASK)
|
||||
#define GIT_MODE_ISBLOB(MODE) (GIT_MODE_TYPE(MODE) == GIT_MODE_TYPE(GIT_FILEMODE_BLOB))
|
||||
|
||||
/**
|
||||
@ -244,7 +248,7 @@ extern mode_t git_futils_canonical_mode(mode_t raw_mode);
|
||||
* @param out buffer to populate with the mapping information.
|
||||
* @param fd open descriptor to configure the mapping from.
|
||||
* @param begin first byte to map, this should be page aligned.
|
||||
* @param end number of bytes to map.
|
||||
* @param len number of bytes to map.
|
||||
* @return
|
||||
* - 0 on success;
|
||||
* - -1 on error.
|
||||
@ -278,7 +282,7 @@ extern void git_futils_mmap_free(git_map *map);
|
||||
/**
|
||||
* Find a "global" file (i.e. one in a user's home directory).
|
||||
*
|
||||
* @param pathbuf buffer to write the full path into
|
||||
* @param path buffer to write the full path into
|
||||
* @param filename name of file to find in the home directory
|
||||
* @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
|
||||
*/
|
||||
@ -287,7 +291,7 @@ extern int git_futils_find_global_file(git_buf *path, const char *filename);
|
||||
/**
|
||||
* Find an "XDG" file (i.e. one in user's XDG config path).
|
||||
*
|
||||
* @param pathbuf buffer to write the full path into
|
||||
* @param path buffer to write the full path into
|
||||
* @param filename name of file to find in the home directory
|
||||
* @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
|
||||
*/
|
||||
@ -296,7 +300,7 @@ extern int git_futils_find_xdg_file(git_buf *path, const char *filename);
|
||||
/**
|
||||
* Find a "system" file (i.e. one shared for all users of the system).
|
||||
*
|
||||
* @param pathbuf buffer to write the full path into
|
||||
* @param path buffer to write the full path into
|
||||
* @param filename name of file to find in the home directory
|
||||
* @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
|
||||
*/
|
||||
@ -309,6 +313,13 @@ typedef enum {
|
||||
GIT_FUTILS_DIR__MAX = 3,
|
||||
} git_futils_dir_t;
|
||||
|
||||
/**
|
||||
* Configures global data for configuration file search paths.
|
||||
*
|
||||
* @return 0 on success, <0 on failure
|
||||
*/
|
||||
extern int git_futils_dirs_global_init(void);
|
||||
|
||||
/**
|
||||
* Get the search path for global/system/xdg files
|
||||
*
|
||||
|
21
src/global.c
21
src/global.c
@ -65,26 +65,28 @@ int git_threads_init(void)
|
||||
return -1;
|
||||
|
||||
/* Initialize any other subsystems that have global state */
|
||||
if ((error = git_hash_global_init()) >= 0)
|
||||
_tls_init = 1;
|
||||
|
||||
if (error == 0)
|
||||
if ((error = git_hash_global_init()) >= 0 &&
|
||||
(error = git_futils_dirs_global_init()) >= 0)
|
||||
_tls_init = 1;
|
||||
|
||||
GIT_MEMORY_BARRIER;
|
||||
|
||||
win32_pthread_initialize();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void git_threads_shutdown(void)
|
||||
{
|
||||
/* Shut down any subsystems that have global state */
|
||||
win32_pthread_shutdown();
|
||||
git_futils_dirs_free();
|
||||
git_hash_global_shutdown();
|
||||
|
||||
TlsFree(_tls_index);
|
||||
_tls_init = 0;
|
||||
git_mutex_free(&git__mwindow_mutex);
|
||||
|
||||
/* Shut down any subsystems that have global state */
|
||||
git_hash_global_shutdown();
|
||||
git_futils_dirs_free();
|
||||
git_mutex_free(&git__mwindow_mutex);
|
||||
}
|
||||
|
||||
git_global_st *git__global_state(void)
|
||||
@ -127,7 +129,8 @@ int git_threads_init(void)
|
||||
pthread_key_create(&_tls_key, &cb__free_status);
|
||||
|
||||
/* Initialize any other subsystems that have global state */
|
||||
if ((error = git_hash_global_init()) >= 0)
|
||||
if ((error = git_hash_global_init()) >= 0 &&
|
||||
(error = git_futils_dirs_global_init()) >= 0)
|
||||
_tls_init = 1;
|
||||
|
||||
GIT_MEMORY_BARRIER;
|
||||
|
@ -20,33 +20,16 @@ static struct git_hash_prov hash_prov = {0};
|
||||
/* Initialize CNG, if available */
|
||||
GIT_INLINE(int) hash_cng_prov_init(void)
|
||||
{
|
||||
OSVERSIONINFOEX version_test = {0};
|
||||
DWORD version_test_mask;
|
||||
DWORDLONG version_condition_mask = 0;
|
||||
char dll_path[MAX_PATH];
|
||||
DWORD dll_path_len, size_len;
|
||||
|
||||
return -1;
|
||||
|
||||
/* Only use CNG on Windows 2008 / Vista SP1 or better (Windows 6.0 SP1) */
|
||||
version_test.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
||||
version_test.dwMajorVersion = 6;
|
||||
version_test.dwMinorVersion = 0;
|
||||
version_test.wServicePackMajor = 1;
|
||||
version_test.wServicePackMinor = 0;
|
||||
|
||||
version_test_mask = (VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR);
|
||||
|
||||
VER_SET_CONDITION(version_condition_mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
|
||||
VER_SET_CONDITION(version_condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL);
|
||||
VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
|
||||
VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
|
||||
|
||||
if (!VerifyVersionInfo(&version_test, version_test_mask, version_condition_mask))
|
||||
if (!git_has_win32_version(6, 0, 1))
|
||||
return -1;
|
||||
|
||||
/* Load bcrypt.dll explicitly from the system directory */
|
||||
if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 || dll_path_len > MAX_PATH ||
|
||||
if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 ||
|
||||
dll_path_len > MAX_PATH ||
|
||||
StringCchCat(dll_path, MAX_PATH, "\\") < 0 ||
|
||||
StringCchCat(dll_path, MAX_PATH, GIT_HASH_CNG_DLL_NAME) < 0 ||
|
||||
(hash_prov.prov.cng.dll = LoadLibrary(dll_path)) == NULL)
|
||||
|
204
src/hashsig.c
204
src/hashsig.c
@ -13,12 +13,15 @@ typedef uint64_t hashsig_state;
|
||||
|
||||
#define HASHSIG_SCALE 100
|
||||
|
||||
#define HASHSIG_HASH_WINDOW 32
|
||||
#define HASHSIG_HASH_START 0
|
||||
#define HASHSIG_MAX_RUN 80
|
||||
#define HASHSIG_HASH_START 0x012345678ABCDEF0LL
|
||||
#define HASHSIG_HASH_SHIFT 5
|
||||
#define HASHSIG_HASH_MASK 0x7FFFFFFF
|
||||
|
||||
#define HASHSIG_HASH_MIX(S,CH) \
|
||||
(S) = ((S) << HASHSIG_HASH_SHIFT) - (S) + (hashsig_state)(CH)
|
||||
|
||||
#define HASHSIG_HEAP_SIZE ((1 << 7) - 1)
|
||||
#define HASHSIG_HEAP_MIN_SIZE 4
|
||||
|
||||
typedef int (*hashsig_cmp)(const void *a, const void *b, void *);
|
||||
|
||||
@ -28,14 +31,6 @@ typedef struct {
|
||||
hashsig_t values[HASHSIG_HEAP_SIZE];
|
||||
} hashsig_heap;
|
||||
|
||||
typedef struct {
|
||||
hashsig_state state, shift_n;
|
||||
char window[HASHSIG_HASH_WINDOW];
|
||||
int win_len, win_pos, saw_lf;
|
||||
} hashsig_in_progress;
|
||||
|
||||
#define HASHSIG_IN_PROGRESS_INIT { HASHSIG_HASH_START, 1, {0}, 0, 0, 1 }
|
||||
|
||||
struct git_hashsig {
|
||||
hashsig_heap mins;
|
||||
hashsig_heap maxs;
|
||||
@ -43,8 +38,8 @@ struct git_hashsig {
|
||||
int considered;
|
||||
};
|
||||
|
||||
#define HEAP_LCHILD_OF(I) (((I)*2)+1)
|
||||
#define HEAP_RCHILD_OF(I) (((I)*2)+2)
|
||||
#define HEAP_LCHILD_OF(I) (((I)<<1)+1)
|
||||
#define HEAP_RCHILD_OF(I) (((I)<<1)+2)
|
||||
#define HEAP_PARENT_OF(I) (((I)-1)>>1)
|
||||
|
||||
static void hashsig_heap_init(hashsig_heap *h, hashsig_cmp cmp)
|
||||
@ -115,134 +110,109 @@ static void hashsig_heap_sort(hashsig_heap *h)
|
||||
|
||||
static void hashsig_heap_insert(hashsig_heap *h, hashsig_t val)
|
||||
{
|
||||
/* if heap is full, pop top if new element should replace it */
|
||||
if (h->size == h->asize && h->cmp(&val, &h->values[0], NULL) > 0) {
|
||||
h->size--;
|
||||
h->values[0] = h->values[h->size];
|
||||
hashsig_heap_down(h, 0);
|
||||
}
|
||||
|
||||
/* if heap is not full, insert new element */
|
||||
if (h->size < h->asize) {
|
||||
h->values[h->size++] = val;
|
||||
hashsig_heap_up(h, h->size - 1);
|
||||
}
|
||||
|
||||
/* if heap is full, pop top if new element should replace it */
|
||||
else if (h->cmp(&val, &h->values[0], NULL) > 0) {
|
||||
h->size--;
|
||||
h->values[0] = h->values[h->size];
|
||||
hashsig_heap_down(h, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
GIT_INLINE(bool) hashsig_include_char(
|
||||
char ch, git_hashsig_option_t opt, int *saw_lf)
|
||||
typedef struct {
|
||||
int use_ignores;
|
||||
uint8_t ignore_ch[256];
|
||||
} hashsig_in_progress;
|
||||
|
||||
static void hashsig_in_progress_init(
|
||||
hashsig_in_progress *prog, git_hashsig *sig)
|
||||
{
|
||||
if ((opt & GIT_HASHSIG_IGNORE_WHITESPACE) && git__isspace(ch))
|
||||
return false;
|
||||
int i;
|
||||
|
||||
if (opt & GIT_HASHSIG_SMART_WHITESPACE) {
|
||||
if (ch == '\r' || (*saw_lf && git__isspace(ch)))
|
||||
return false;
|
||||
|
||||
*saw_lf = (ch == '\n');
|
||||
switch (sig->opt) {
|
||||
case GIT_HASHSIG_IGNORE_WHITESPACE:
|
||||
for (i = 0; i < 256; ++i)
|
||||
prog->ignore_ch[i] = git__isspace_nonlf(i);
|
||||
prog->use_ignores = 1;
|
||||
break;
|
||||
case GIT_HASHSIG_SMART_WHITESPACE:
|
||||
for (i = 0; i < 256; ++i)
|
||||
prog->ignore_ch[i] = git__isspace(i);
|
||||
prog->use_ignores = 1;
|
||||
break;
|
||||
default:
|
||||
memset(prog, 0, sizeof(*prog));
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void hashsig_initial_window(
|
||||
git_hashsig *sig,
|
||||
const char **data,
|
||||
size_t size,
|
||||
hashsig_in_progress *prog)
|
||||
{
|
||||
hashsig_state state, shift_n;
|
||||
int win_len;
|
||||
const char *scan, *end;
|
||||
|
||||
/* init until we have processed at least HASHSIG_HASH_WINDOW data */
|
||||
|
||||
if (prog->win_len >= HASHSIG_HASH_WINDOW)
|
||||
return;
|
||||
|
||||
state = prog->state;
|
||||
win_len = prog->win_len;
|
||||
shift_n = prog->shift_n;
|
||||
|
||||
scan = *data;
|
||||
end = scan + size;
|
||||
|
||||
while (scan < end && win_len < HASHSIG_HASH_WINDOW) {
|
||||
char ch = *scan++;
|
||||
|
||||
if (!hashsig_include_char(ch, sig->opt, &prog->saw_lf))
|
||||
continue;
|
||||
|
||||
state = (state * HASHSIG_HASH_SHIFT + ch) & HASHSIG_HASH_MASK;
|
||||
|
||||
if (!win_len)
|
||||
shift_n = 1;
|
||||
else
|
||||
shift_n = (shift_n * HASHSIG_HASH_SHIFT) & HASHSIG_HASH_MASK;
|
||||
|
||||
prog->window[win_len++] = ch;
|
||||
}
|
||||
|
||||
/* insert initial hash if we just finished */
|
||||
|
||||
if (win_len == HASHSIG_HASH_WINDOW) {
|
||||
hashsig_heap_insert(&sig->mins, (hashsig_t)state);
|
||||
hashsig_heap_insert(&sig->maxs, (hashsig_t)state);
|
||||
sig->considered = 1;
|
||||
}
|
||||
|
||||
prog->state = state;
|
||||
prog->win_len = win_len;
|
||||
prog->shift_n = shift_n;
|
||||
|
||||
*data = scan;
|
||||
}
|
||||
#define HASHSIG_IN_PROGRESS_INIT { 1 }
|
||||
|
||||
static int hashsig_add_hashes(
|
||||
git_hashsig *sig,
|
||||
const char *data,
|
||||
const uint8_t *data,
|
||||
size_t size,
|
||||
hashsig_in_progress *prog)
|
||||
{
|
||||
const char *scan = data, *end = data + size;
|
||||
hashsig_state state, shift_n, rmv;
|
||||
const uint8_t *scan = data, *end = data + size;
|
||||
hashsig_state state = HASHSIG_HASH_START;
|
||||
int use_ignores = prog->use_ignores, len;
|
||||
uint8_t ch;
|
||||
|
||||
if (prog->win_len < HASHSIG_HASH_WINDOW)
|
||||
hashsig_initial_window(sig, &scan, size, prog);
|
||||
while (scan < end) {
|
||||
state = HASHSIG_HASH_START;
|
||||
|
||||
state = prog->state;
|
||||
shift_n = prog->shift_n;
|
||||
for (len = 0; scan < end && len < HASHSIG_MAX_RUN; ) {
|
||||
ch = *scan;
|
||||
|
||||
/* advance window, adding new chars and removing old */
|
||||
if (use_ignores)
|
||||
for (; scan < end && git__isspace_nonlf(ch); ch = *scan)
|
||||
++scan;
|
||||
else if (sig->opt != GIT_HASHSIG_NORMAL)
|
||||
for (; scan < end && ch == '\r'; ch = *scan)
|
||||
++scan;
|
||||
|
||||
for (; scan < end; ++scan) {
|
||||
char ch = *scan;
|
||||
/* peek at next character to decide what to do next */
|
||||
if (sig->opt == GIT_HASHSIG_SMART_WHITESPACE)
|
||||
use_ignores = (ch == '\n');
|
||||
|
||||
if (!hashsig_include_char(ch, sig->opt, &prog->saw_lf))
|
||||
continue;
|
||||
if (scan >= end)
|
||||
break;
|
||||
++scan;
|
||||
|
||||
rmv = shift_n * prog->window[prog->win_pos];
|
||||
/* check run terminator */
|
||||
if (ch == '\n' || ch == '\0')
|
||||
break;
|
||||
|
||||
state = (state - rmv) & HASHSIG_HASH_MASK;
|
||||
state = (state * HASHSIG_HASH_SHIFT) & HASHSIG_HASH_MASK;
|
||||
state = (state + ch) & HASHSIG_HASH_MASK;
|
||||
|
||||
hashsig_heap_insert(&sig->mins, (hashsig_t)state);
|
||||
hashsig_heap_insert(&sig->maxs, (hashsig_t)state);
|
||||
sig->considered++;
|
||||
|
||||
prog->window[prog->win_pos] = ch;
|
||||
prog->win_pos = (prog->win_pos + 1) % HASHSIG_HASH_WINDOW;
|
||||
++len;
|
||||
HASHSIG_HASH_MIX(state, ch);
|
||||
}
|
||||
|
||||
prog->state = state;
|
||||
if (len > 0) {
|
||||
hashsig_heap_insert(&sig->mins, (hashsig_t)state);
|
||||
hashsig_heap_insert(&sig->maxs, (hashsig_t)state);
|
||||
|
||||
sig->considered++;
|
||||
|
||||
while (scan < end && (*scan == '\n' || !*scan))
|
||||
++scan;
|
||||
}
|
||||
}
|
||||
|
||||
prog->use_ignores = use_ignores;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hashsig_finalize_hashes(git_hashsig *sig)
|
||||
{
|
||||
if (sig->mins.size < HASHSIG_HEAP_SIZE) {
|
||||
if (sig->mins.size < HASHSIG_HEAP_MIN_SIZE) {
|
||||
giterr_set(GITERR_INVALID,
|
||||
"File too small for similarity signature calculation");
|
||||
return GIT_EBUFS;
|
||||
@ -274,11 +244,13 @@ int git_hashsig_create(
|
||||
git_hashsig_option_t opts)
|
||||
{
|
||||
int error;
|
||||
hashsig_in_progress prog = HASHSIG_IN_PROGRESS_INIT;
|
||||
hashsig_in_progress prog;
|
||||
git_hashsig *sig = hashsig_alloc(opts);
|
||||
GITERR_CHECK_ALLOC(sig);
|
||||
|
||||
error = hashsig_add_hashes(sig, buf, buflen, &prog);
|
||||
hashsig_in_progress_init(&prog, sig);
|
||||
|
||||
error = hashsig_add_hashes(sig, (const uint8_t *)buf, buflen, &prog);
|
||||
|
||||
if (!error)
|
||||
error = hashsig_finalize_hashes(sig);
|
||||
@ -296,10 +268,10 @@ int git_hashsig_create_fromfile(
|
||||
const char *path,
|
||||
git_hashsig_option_t opts)
|
||||
{
|
||||
char buf[4096];
|
||||
uint8_t buf[0x1000];
|
||||
ssize_t buflen = 0;
|
||||
int error = 0, fd;
|
||||
hashsig_in_progress prog = HASHSIG_IN_PROGRESS_INIT;
|
||||
hashsig_in_progress prog;
|
||||
git_hashsig *sig = hashsig_alloc(opts);
|
||||
GITERR_CHECK_ALLOC(sig);
|
||||
|
||||
@ -308,6 +280,8 @@ int git_hashsig_create_fromfile(
|
||||
return fd;
|
||||
}
|
||||
|
||||
hashsig_in_progress_init(&prog, sig);
|
||||
|
||||
while (!error) {
|
||||
if ((buflen = p_read(fd, buf, sizeof(buf))) <= 0) {
|
||||
if ((error = (int)buflen) < 0)
|
||||
@ -362,6 +336,12 @@ static int hashsig_heap_compare(const hashsig_heap *a, const hashsig_heap *b)
|
||||
|
||||
int git_hashsig_compare(const git_hashsig *a, const git_hashsig *b)
|
||||
{
|
||||
/* if we have fewer than the maximum number of elements, then just use
|
||||
* one array since the two arrays will be the same
|
||||
*/
|
||||
if (a->mins.size < HASHSIG_HEAP_SIZE)
|
||||
return hashsig_heap_compare(&a->mins, &b->mins);
|
||||
else
|
||||
return (hashsig_heap_compare(&a->mins, &b->mins) +
|
||||
hashsig_heap_compare(&a->maxs, &b->maxs)) / 2;
|
||||
}
|
||||
|
96
src/ignore.c
96
src/ignore.c
@ -37,7 +37,7 @@ static int parse_ignore_file(
|
||||
GITERR_CHECK_ALLOC(match);
|
||||
}
|
||||
|
||||
match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE;
|
||||
match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
|
||||
|
||||
if (!(error = git_attr_fnmatch__parse(
|
||||
match, ignores->pool, context, &scan)))
|
||||
@ -159,7 +159,7 @@ int git_ignore__push_dir(git_ignores *ign, const char *dir)
|
||||
{
|
||||
if (git_buf_joinpath(&ign->dir, ign->dir.ptr, dir) < 0)
|
||||
return -1;
|
||||
else
|
||||
|
||||
return push_ignore_file(
|
||||
ign->repo, ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
|
||||
}
|
||||
@ -168,8 +168,27 @@ int git_ignore__pop_dir(git_ignores *ign)
|
||||
{
|
||||
if (ign->ign_path.length > 0) {
|
||||
git_attr_file *file = git_vector_last(&ign->ign_path);
|
||||
if (git__suffixcmp(ign->dir.ptr, file->key + 2) == 0)
|
||||
const char *start, *end, *scan;
|
||||
size_t keylen;
|
||||
|
||||
/* - ign->dir looks something like "a/b" (or "a/b/c/d")
|
||||
* - file->key looks something like "0#a/b/.gitignore
|
||||
*
|
||||
* We are popping the last directory off ign->dir. We also want to
|
||||
* remove the file from the vector if the directory part of the key
|
||||
* matches the ign->dir path. We need to test if the "a/b" part of
|
||||
* the file key matches the path we are about to pop.
|
||||
*/
|
||||
|
||||
for (start = end = scan = &file->key[2]; *scan; ++scan)
|
||||
if (*scan == '/')
|
||||
end = scan; /* point 'end' to last '/' in key */
|
||||
keylen = (end - start) + 1;
|
||||
|
||||
if (ign->dir.size >= keylen &&
|
||||
!memcmp(ign->dir.ptr + ign->dir.size - keylen, start, keylen))
|
||||
git_vector_pop(&ign->ign_path);
|
||||
|
||||
git_buf_rtruncate_at_char(&ign->dir, '/');
|
||||
}
|
||||
return 0;
|
||||
@ -298,12 +317,9 @@ int git_ignore_path_is_ignored(
|
||||
path.full.size = (tail - path.full.ptr);
|
||||
path.is_dir = (tail == end) ? full_is_dir : true;
|
||||
|
||||
/* update ignores for new path fragment */
|
||||
if (path.basename == path.path)
|
||||
error = git_ignore__for_path(repo, path.path, &ignores);
|
||||
else
|
||||
error = git_ignore__push_dir(&ignores, path.basename);
|
||||
if (error < 0)
|
||||
/* initialize ignores the first time through */
|
||||
if (path.basename == path.path &&
|
||||
(error = git_ignore__for_path(repo, path.path, &ignores)) < 0)
|
||||
break;
|
||||
|
||||
/* first process builtins - success means path was found */
|
||||
@ -327,6 +343,10 @@ int git_ignore_path_is_ignored(
|
||||
if (tail == end)
|
||||
break;
|
||||
|
||||
/* now add this directory to list of ignores */
|
||||
if ((error = git_ignore__push_dir(&ignores, path.path)) < 0)
|
||||
break;
|
||||
|
||||
/* reinstate divider in path */
|
||||
*tail = '/';
|
||||
while (*tail == '/') tail++;
|
||||
@ -340,3 +360,61 @@ cleanup:
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
int git_ignore__check_pathspec_for_exact_ignores(
|
||||
git_repository *repo,
|
||||
git_vector *vspec,
|
||||
bool no_fnmatch)
|
||||
{
|
||||
int error = 0;
|
||||
size_t i;
|
||||
git_attr_fnmatch *match;
|
||||
int ignored;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
const char *wd, *filename;
|
||||
git_index *idx;
|
||||
|
||||
if ((error = git_repository__ensure_not_bare(
|
||||
repo, "validate pathspec")) < 0 ||
|
||||
(error = git_repository_index(&idx, repo)) < 0)
|
||||
return error;
|
||||
|
||||
wd = git_repository_workdir(repo);
|
||||
|
||||
git_vector_foreach(vspec, i, match) {
|
||||
/* skip wildcard matches (if they are being used) */
|
||||
if ((match->flags & GIT_ATTR_FNMATCH_HASWILD) != 0 &&
|
||||
!no_fnmatch)
|
||||
continue;
|
||||
|
||||
filename = match->pattern;
|
||||
|
||||
/* if file is already in the index, it's fine */
|
||||
if (git_index_get_bypath(idx, filename, 0) != NULL)
|
||||
continue;
|
||||
|
||||
if ((error = git_buf_joinpath(&path, wd, filename)) < 0)
|
||||
break;
|
||||
|
||||
/* is there a file on disk that matches this exactly? */
|
||||
if (!git_path_isfile(path.ptr))
|
||||
continue;
|
||||
|
||||
/* is that file ignored? */
|
||||
if ((error = git_ignore_path_is_ignored(&ignored, repo, filename)) < 0)
|
||||
break;
|
||||
|
||||
if (ignored) {
|
||||
giterr_set(GITERR_INVALID, "pathspec contains ignored file '%s'",
|
||||
filename);
|
||||
error = GIT_EINVALIDSPEC;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
git_index_free(idx);
|
||||
git_buf_free(&path);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
14
src/ignore.h
14
src/ignore.h
@ -24,14 +24,15 @@
|
||||
*/
|
||||
typedef struct {
|
||||
git_repository *repo;
|
||||
git_buf dir;
|
||||
git_buf dir; /* current directory reflected in ign_path */
|
||||
git_attr_file *ign_internal;
|
||||
git_vector ign_path;
|
||||
git_vector ign_global;
|
||||
int ignore_case;
|
||||
} git_ignores;
|
||||
|
||||
extern int git_ignore__for_path(git_repository *repo, const char *path, git_ignores *ign);
|
||||
extern int git_ignore__for_path(
|
||||
git_repository *repo, const char *path, git_ignores *ign);
|
||||
|
||||
extern int git_ignore__push_dir(git_ignores *ign, const char *dir);
|
||||
|
||||
@ -41,4 +42,13 @@ extern void git_ignore__free(git_ignores *ign);
|
||||
|
||||
extern int git_ignore__lookup(git_ignores *ign, const char *path, int *ignored);
|
||||
|
||||
/* command line Git sometimes generates an error message if given a
|
||||
* pathspec that contains an exact match to an ignored file (provided
|
||||
* --force isn't also given). This makes it easy to check it that has
|
||||
* happened. Returns GIT_EINVALIDSPEC if the pathspec contains ignored
|
||||
* exact matches (that are not already present in the index).
|
||||
*/
|
||||
extern int git_ignore__check_pathspec_for_exact_ignores(
|
||||
git_repository *repo, git_vector *pathspec, bool no_fnmatch);
|
||||
|
||||
#endif
|
||||
|
393
src/index.c
393
src/index.c
@ -15,6 +15,9 @@
|
||||
#include "hash.h"
|
||||
#include "iterator.h"
|
||||
#include "pathspec.h"
|
||||
#include "ignore.h"
|
||||
#include "blob.h"
|
||||
|
||||
#include "git2/odb.h"
|
||||
#include "git2/oid.h"
|
||||
#include "git2/blob.h"
|
||||
@ -99,8 +102,6 @@ static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
|
||||
static bool is_index_extended(git_index *index);
|
||||
static int write_index(git_index *index, git_filebuf *file);
|
||||
|
||||
static int index_find(size_t *at_pos, git_index *index, const char *path, int stage);
|
||||
|
||||
static void index_entry_free(git_index_entry *entry);
|
||||
static void index_entry_reuc_free(git_index_reuc_entry *reuc);
|
||||
|
||||
@ -112,7 +113,7 @@ static int index_srch(const void *key, const void *array_member)
|
||||
|
||||
ret = strcmp(srch_key->path, entry->path);
|
||||
|
||||
if (ret == 0)
|
||||
if (ret == 0 && srch_key->stage != GIT_INDEX_STAGE_ANY)
|
||||
ret = srch_key->stage - GIT_IDXENTRY_STAGE(entry);
|
||||
|
||||
return ret;
|
||||
@ -126,7 +127,7 @@ static int index_isrch(const void *key, const void *array_member)
|
||||
|
||||
ret = strcasecmp(srch_key->path, entry->path);
|
||||
|
||||
if (ret == 0)
|
||||
if (ret == 0 && srch_key->stage != GIT_INDEX_STAGE_ANY)
|
||||
ret = srch_key->stage - GIT_IDXENTRY_STAGE(entry);
|
||||
|
||||
return ret;
|
||||
@ -259,6 +260,22 @@ static int reuc_icmp(const void *a, const void *b)
|
||||
return strcasecmp(info_a->path, info_b->path);
|
||||
}
|
||||
|
||||
static void index_entry_reuc_free(git_index_reuc_entry *reuc)
|
||||
{
|
||||
if (!reuc)
|
||||
return;
|
||||
git__free(reuc->path);
|
||||
git__free(reuc);
|
||||
}
|
||||
|
||||
static void index_entry_free(git_index_entry *entry)
|
||||
{
|
||||
if (!entry)
|
||||
return;
|
||||
git__free(entry->path);
|
||||
git__free(entry);
|
||||
}
|
||||
|
||||
static unsigned int index_create_mode(unsigned int mode)
|
||||
{
|
||||
if (S_ISLNK(mode))
|
||||
@ -267,7 +284,7 @@ static unsigned int index_create_mode(unsigned int mode)
|
||||
if (S_ISDIR(mode) || (mode & S_IFMT) == (S_IFLNK | S_IFDIR))
|
||||
return (S_IFLNK | S_IFDIR);
|
||||
|
||||
return S_IFREG | ((mode & 0100) ? 0755 : 0644);
|
||||
return S_IFREG | GIT_PERMS_CANONICAL(mode);
|
||||
}
|
||||
|
||||
static unsigned int index_merge_mode(
|
||||
@ -288,16 +305,16 @@ void git_index__set_ignore_case(git_index *index, bool ignore_case)
|
||||
{
|
||||
index->ignore_case = ignore_case;
|
||||
|
||||
index->entries._cmp = ignore_case ? index_icmp : index_cmp;
|
||||
index->entries_cmp_path = ignore_case ? index_icmp_path : index_cmp_path;
|
||||
index->entries_search = ignore_case ? index_isrch : index_srch;
|
||||
index->entries_search_path = ignore_case ? index_isrch_path : index_srch_path;
|
||||
index->entries.sorted = 0;
|
||||
|
||||
git_vector_set_cmp(&index->entries, ignore_case ? index_icmp : index_cmp);
|
||||
git_vector_sort(&index->entries);
|
||||
|
||||
index->reuc._cmp = ignore_case ? reuc_icmp : reuc_cmp;
|
||||
index->reuc_search = ignore_case ? reuc_isrch : reuc_srch;
|
||||
index->reuc.sorted = 0;
|
||||
|
||||
git_vector_set_cmp(&index->reuc, ignore_case ? reuc_icmp : reuc_cmp);
|
||||
git_vector_sort(&index->reuc);
|
||||
}
|
||||
|
||||
@ -365,11 +382,8 @@ static void index_entries_free(git_vector *entries)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < entries->length; ++i) {
|
||||
git_index_entry *e = git_vector_get(entries, i);
|
||||
git__free(e->path);
|
||||
git__free(e);
|
||||
}
|
||||
for (i = 0; i < entries->length; ++i)
|
||||
index_entry_free(git__swap(entries->contents[i], NULL));
|
||||
|
||||
git_vector_clear(entries);
|
||||
}
|
||||
@ -484,8 +498,12 @@ int git_index_write(git_index *index)
|
||||
git_vector_sort(&index->reuc);
|
||||
|
||||
if ((error = git_filebuf_open(
|
||||
&file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < 0)
|
||||
&file, index->index_file_path, GIT_FILEBUF_HASH_CONTENTS)) < 0) {
|
||||
if (error == GIT_ELOCKED)
|
||||
giterr_set(GITERR_INDEX, "The index is locked. This might be due to a concurrrent or crashed process");
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
if ((error = write_index(index, &file)) < 0) {
|
||||
git_filebuf_cleanup(&file);
|
||||
@ -503,6 +521,12 @@ int git_index_write(git_index *index)
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char * git_index_path(git_index *index)
|
||||
{
|
||||
assert(index);
|
||||
return index->index_file_path;
|
||||
}
|
||||
|
||||
int git_index_write_tree(git_oid *oid, git_index *index)
|
||||
{
|
||||
git_repository *repo;
|
||||
@ -547,7 +571,7 @@ const git_index_entry *git_index_get_bypath(
|
||||
|
||||
git_vector_sort(&index->entries);
|
||||
|
||||
if (index_find(&pos, index, path, stage) < 0) {
|
||||
if (git_index__find(&pos, index, path, stage) < 0) {
|
||||
giterr_set(GITERR_INDEX, "Index does not contain %s", path);
|
||||
return NULL;
|
||||
}
|
||||
@ -585,42 +609,23 @@ int git_index_entry__cmp_icase(const void *a, const void *b)
|
||||
return strcasecmp(entry_a->path, entry_b->path);
|
||||
}
|
||||
|
||||
static int index_entry_init(git_index_entry **entry_out, git_index *index, const char *rel_path)
|
||||
static int index_entry_init(
|
||||
git_index_entry **entry_out, git_index *index, const char *rel_path)
|
||||
{
|
||||
int error = 0;
|
||||
git_index_entry *entry = NULL;
|
||||
struct stat st;
|
||||
git_oid oid;
|
||||
const char *workdir;
|
||||
git_buf full_path = GIT_BUF_INIT;
|
||||
int error;
|
||||
|
||||
if (INDEX_OWNER(index) == NULL)
|
||||
return create_index_error(-1,
|
||||
"Could not initialize index entry. "
|
||||
"Index is not backed up by an existing repository.");
|
||||
|
||||
workdir = git_repository_workdir(INDEX_OWNER(index));
|
||||
|
||||
if (!workdir)
|
||||
return create_index_error(GIT_EBAREREPO,
|
||||
"Could not initialize index entry. Repository is bare");
|
||||
|
||||
if ((error = git_buf_joinpath(&full_path, workdir, rel_path)) < 0)
|
||||
return error;
|
||||
|
||||
if ((error = git_path_lstat(full_path.ptr, &st)) < 0) {
|
||||
git_buf_free(&full_path);
|
||||
return error;
|
||||
}
|
||||
|
||||
git_buf_free(&full_path); /* done with full path */
|
||||
|
||||
/* There is no need to validate the rel_path here, since it will be
|
||||
* immediately validated by the call to git_blob_create_fromfile.
|
||||
*/
|
||||
|
||||
/* write the blob to disk and get the oid */
|
||||
if ((error = git_blob_create_fromworkdir(&oid, INDEX_OWNER(index), rel_path)) < 0)
|
||||
/* write the blob to disk and get the oid and stat info */
|
||||
error = git_blob__create_from_paths(
|
||||
&oid, &st, INDEX_OWNER(index), NULL, rel_path, 0, true);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
entry = git__calloc(1, sizeof(git_index_entry));
|
||||
@ -668,15 +673,6 @@ static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void index_entry_reuc_free(git_index_reuc_entry *reuc)
|
||||
{
|
||||
if (!reuc)
|
||||
return;
|
||||
|
||||
git__free(reuc->path);
|
||||
git__free(reuc);
|
||||
}
|
||||
|
||||
static git_index_entry *index_entry_dup(const git_index_entry *source_entry)
|
||||
{
|
||||
git_index_entry *entry;
|
||||
@ -695,14 +691,6 @@ static git_index_entry *index_entry_dup(const git_index_entry *source_entry)
|
||||
return entry;
|
||||
}
|
||||
|
||||
static void index_entry_free(git_index_entry *entry)
|
||||
{
|
||||
if (!entry)
|
||||
return;
|
||||
git__free(entry->path);
|
||||
git__free(entry);
|
||||
}
|
||||
|
||||
static int index_insert(git_index *index, git_index_entry *entry, int replace)
|
||||
{
|
||||
size_t path_length, position;
|
||||
@ -721,7 +709,8 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace)
|
||||
entry->flags |= GIT_IDXENTRY_NAMEMASK;
|
||||
|
||||
/* look if an entry with this path already exists */
|
||||
if (!index_find(&position, index, entry->path, GIT_IDXENTRY_STAGE(entry))) {
|
||||
if (!git_index__find(
|
||||
&position, index, entry->path, GIT_IDXENTRY_STAGE(entry))) {
|
||||
existing = (git_index_entry **)&index->entries.contents[position];
|
||||
|
||||
/* update filemode to existing values if stat is not trusted */
|
||||
@ -734,8 +723,9 @@ static int index_insert(git_index *index, git_index_entry *entry, int replace)
|
||||
if (!replace || !existing)
|
||||
return git_vector_insert(&index->entries, entry);
|
||||
|
||||
/* exists, replace it */
|
||||
git__free((*existing)->path);
|
||||
/* exists, replace it (preserving name from existing entry) */
|
||||
git__free(entry->path);
|
||||
entry->path = (*existing)->path;
|
||||
git__free(*existing);
|
||||
*existing = entry;
|
||||
|
||||
@ -832,7 +822,7 @@ int git_index_remove(git_index *index, const char *path, int stage)
|
||||
|
||||
git_vector_sort(&index->entries);
|
||||
|
||||
if (index_find(&position, index, path, stage) < 0) {
|
||||
if (git_index__find(&position, index, path, stage) < 0) {
|
||||
giterr_set(GITERR_INDEX, "Index does not contain %s at stage %d",
|
||||
path, stage);
|
||||
return GIT_ENOTFOUND;
|
||||
@ -888,7 +878,8 @@ int git_index_remove_directory(git_index *index, const char *dir, int stage)
|
||||
return error;
|
||||
}
|
||||
|
||||
static int index_find(size_t *at_pos, git_index *index, const char *path, int stage)
|
||||
int git_index__find(
|
||||
size_t *at_pos, git_index *index, const char *path, int stage)
|
||||
{
|
||||
struct entry_srch_key srch_key;
|
||||
|
||||
@ -897,7 +888,8 @@ static int index_find(size_t *at_pos, git_index *index, const char *path, int st
|
||||
srch_key.path = path;
|
||||
srch_key.stage = stage;
|
||||
|
||||
return git_vector_bsearch2(at_pos, &index->entries, index->entries_search, &srch_key);
|
||||
return git_vector_bsearch2(
|
||||
at_pos, &index->entries, index->entries_search, &srch_key);
|
||||
}
|
||||
|
||||
int git_index_find(size_t *at_pos, git_index *index, const char *path)
|
||||
@ -1354,14 +1346,11 @@ int git_index_reuc_remove(git_index *index, size_t position)
|
||||
void git_index_reuc_clear(git_index *index)
|
||||
{
|
||||
size_t i;
|
||||
git_index_reuc_entry *reuc;
|
||||
|
||||
assert(index);
|
||||
|
||||
git_vector_foreach(&index->reuc, i, reuc) {
|
||||
git__free(reuc->path);
|
||||
git__free(reuc);
|
||||
}
|
||||
for (i = 0; i < index->reuc.length; ++i)
|
||||
index_entry_reuc_free(git__swap(index->reuc.contents[i], NULL));
|
||||
|
||||
git_vector_clear(&index->reuc);
|
||||
}
|
||||
@ -1386,7 +1375,7 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
|
||||
while (size) {
|
||||
git_index_reuc_entry *lost;
|
||||
|
||||
len = strlen(buffer) + 1;
|
||||
len = p_strnlen(buffer, size) + 1;
|
||||
if (size <= len)
|
||||
return index_error_invalid("reading reuc entries");
|
||||
|
||||
@ -1406,14 +1395,18 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
|
||||
|
||||
if (git__strtol32(&tmp, buffer, &endptr, 8) < 0 ||
|
||||
!endptr || endptr == buffer || *endptr ||
|
||||
(unsigned)tmp > UINT_MAX)
|
||||
(unsigned)tmp > UINT_MAX) {
|
||||
index_entry_reuc_free(lost);
|
||||
return index_error_invalid("reading reuc entry stage");
|
||||
}
|
||||
|
||||
lost->mode[i] = tmp;
|
||||
|
||||
len = (endptr + 1) - buffer;
|
||||
if (size <= len)
|
||||
if (size <= len) {
|
||||
index_entry_reuc_free(lost);
|
||||
return index_error_invalid("reading reuc entry stage");
|
||||
}
|
||||
|
||||
size -= len;
|
||||
buffer += len;
|
||||
@ -1423,8 +1416,10 @@ static int read_reuc(git_index *index, const char *buffer, size_t size)
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (!lost->mode[i])
|
||||
continue;
|
||||
if (size < 20)
|
||||
if (size < 20) {
|
||||
index_entry_reuc_free(lost);
|
||||
return index_error_invalid("reading reuc entry oid");
|
||||
}
|
||||
|
||||
git_oid_fromraw(&lost->oid[i], (const unsigned char *) buffer);
|
||||
size -= 20;
|
||||
@ -1453,7 +1448,7 @@ static int read_conflict_names(git_index *index, const char *buffer, size_t size
|
||||
return -1;
|
||||
|
||||
#define read_conflict_name(ptr) \
|
||||
len = strlen(buffer) + 1; \
|
||||
len = p_strnlen(buffer, size) + 1; \
|
||||
if (size < len) \
|
||||
return index_error_invalid("reading conflict name entries"); \
|
||||
\
|
||||
@ -1580,7 +1575,8 @@ static size_t read_extension(git_index *index, const char *buffer, size_t buffer
|
||||
|
||||
total_size = dest.extension_size + sizeof(struct index_extension);
|
||||
|
||||
if (buffer_size < total_size ||
|
||||
if (dest.extension_size > total_size ||
|
||||
buffer_size < total_size ||
|
||||
buffer_size - total_size < INDEX_FOOTER_SIZE)
|
||||
return 0;
|
||||
|
||||
@ -1955,8 +1951,9 @@ int git_index_entry_stage(const git_index_entry *entry)
|
||||
}
|
||||
|
||||
typedef struct read_tree_data {
|
||||
git_index *index;
|
||||
git_vector *old_entries;
|
||||
git_vector *new_entries;
|
||||
git_vector_cmp entries_search;
|
||||
} read_tree_data;
|
||||
|
||||
static int read_tree_cb(
|
||||
@ -1987,7 +1984,7 @@ static int read_tree_cb(
|
||||
skey.stage = 0;
|
||||
|
||||
if (!git_vector_bsearch2(
|
||||
&pos, data->old_entries, data->index->entries_search, &skey) &&
|
||||
&pos, data->old_entries, data->entries_search, &skey) &&
|
||||
(old_entry = git_vector_get(data->old_entries, pos)) != NULL &&
|
||||
entry->mode == old_entry->mode &&
|
||||
git_oid_equal(&entry->oid, &old_entry->oid))
|
||||
@ -2005,7 +2002,7 @@ static int read_tree_cb(
|
||||
entry->path = git_buf_detach(&path);
|
||||
git_buf_free(&path);
|
||||
|
||||
if (git_vector_insert(&data->index->entries, entry) < 0) {
|
||||
if (git_vector_insert(data->new_entries, entry) < 0) {
|
||||
index_entry_free(entry);
|
||||
return -1;
|
||||
}
|
||||
@ -2019,22 +2016,22 @@ int git_index_read_tree(git_index *index, const git_tree *tree)
|
||||
git_vector entries = GIT_VECTOR_INIT;
|
||||
read_tree_data data;
|
||||
|
||||
git_vector_set_cmp(&entries, index->entries._cmp); /* match sort */
|
||||
|
||||
data.old_entries = &index->entries;
|
||||
data.new_entries = &entries;
|
||||
data.entries_search = index->entries_search;
|
||||
|
||||
git_vector_sort(&index->entries);
|
||||
|
||||
entries._cmp = index->entries._cmp;
|
||||
git_vector_swap(&entries, &index->entries);
|
||||
|
||||
git_index_clear(index);
|
||||
|
||||
data.index = index;
|
||||
data.old_entries = &entries;
|
||||
|
||||
error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data);
|
||||
|
||||
index_entries_free(&entries);
|
||||
git_vector_free(&entries);
|
||||
git_vector_sort(&entries);
|
||||
|
||||
git_vector_sort(&index->entries);
|
||||
git_index_clear(index);
|
||||
|
||||
git_vector_swap(&entries, &index->entries);
|
||||
git_vector_free(&entries);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -2043,3 +2040,219 @@ git_repository *git_index_owner(const git_index *index)
|
||||
{
|
||||
return INDEX_OWNER(index);
|
||||
}
|
||||
|
||||
int git_index_add_all(
|
||||
git_index *index,
|
||||
const git_strarray *paths,
|
||||
unsigned int flags,
|
||||
git_index_matched_path_cb cb,
|
||||
void *payload)
|
||||
{
|
||||
int error;
|
||||
git_repository *repo;
|
||||
git_iterator *wditer = NULL;
|
||||
const git_index_entry *wd = NULL;
|
||||
git_index_entry *entry;
|
||||
git_pathspec ps;
|
||||
const char *match;
|
||||
size_t existing;
|
||||
bool no_fnmatch = (flags & GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH) != 0;
|
||||
int ignorecase;
|
||||
git_oid blobid;
|
||||
|
||||
assert(index);
|
||||
|
||||
if (INDEX_OWNER(index) == NULL)
|
||||
return create_index_error(-1,
|
||||
"Could not add paths to index. "
|
||||
"Index is not backed up by an existing repository.");
|
||||
|
||||
repo = INDEX_OWNER(index);
|
||||
if ((error = git_repository__ensure_not_bare(repo, "index add all")) < 0)
|
||||
return error;
|
||||
|
||||
if (git_repository__cvar(&ignorecase, repo, GIT_CVAR_IGNORECASE) < 0)
|
||||
return -1;
|
||||
|
||||
if ((error = git_pathspec__init(&ps, paths)) < 0)
|
||||
return error;
|
||||
|
||||
/* optionally check that pathspec doesn't mention any ignored files */
|
||||
if ((flags & GIT_INDEX_ADD_CHECK_PATHSPEC) != 0 &&
|
||||
(flags & GIT_INDEX_ADD_FORCE) == 0 &&
|
||||
(error = git_ignore__check_pathspec_for_exact_ignores(
|
||||
repo, &ps.pathspec, no_fnmatch)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_iterator_for_workdir(
|
||||
&wditer, repo, 0, ps.prefix, ps.prefix)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
while (!(error = git_iterator_advance(&wd, wditer))) {
|
||||
|
||||
/* check if path actually matches */
|
||||
if (!git_pathspec__match(
|
||||
&ps.pathspec, wd->path, no_fnmatch, ignorecase, &match, NULL))
|
||||
continue;
|
||||
|
||||
/* skip ignored items that are not already in the index */
|
||||
if ((flags & GIT_INDEX_ADD_FORCE) == 0 &&
|
||||
git_iterator_current_is_ignored(wditer) &&
|
||||
git_index__find(&existing, index, wd->path, 0) < 0)
|
||||
continue;
|
||||
|
||||
/* issue notification callback if requested */
|
||||
if (cb && (error = cb(wd->path, match, payload)) != 0) {
|
||||
if (error > 0) /* return > 0 means skip this one */
|
||||
continue;
|
||||
if (error < 0) { /* return < 0 means abort */
|
||||
giterr_clear();
|
||||
error = GIT_EUSER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: Should we check if the file on disk is already an exact
|
||||
* match to the file in the index and skip this work if it is?
|
||||
*/
|
||||
|
||||
/* write the blob to disk and get the oid */
|
||||
if ((error = git_blob_create_fromworkdir(&blobid, repo, wd->path)) < 0)
|
||||
break;
|
||||
|
||||
/* make the new entry to insert */
|
||||
if ((entry = index_entry_dup(wd)) == NULL) {
|
||||
error = -1;
|
||||
break;
|
||||
}
|
||||
entry->oid = blobid;
|
||||
|
||||
/* add working directory item to index */
|
||||
if ((error = index_insert(index, entry, 1)) < 0) {
|
||||
index_entry_free(entry);
|
||||
break;
|
||||
}
|
||||
|
||||
git_tree_cache_invalidate_path(index->tree, wd->path);
|
||||
|
||||
/* add implies conflict resolved, move conflict entries to REUC */
|
||||
if ((error = index_conflict_to_reuc(index, wd->path)) < 0) {
|
||||
if (error != GIT_ENOTFOUND)
|
||||
break;
|
||||
giterr_clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (error == GIT_ITEROVER)
|
||||
error = 0;
|
||||
|
||||
cleanup:
|
||||
git_iterator_free(wditer);
|
||||
git_pathspec__clear(&ps);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
enum {
|
||||
INDEX_ACTION_NONE = 0,
|
||||
INDEX_ACTION_UPDATE = 1,
|
||||
INDEX_ACTION_REMOVE = 2,
|
||||
};
|
||||
|
||||
static int index_apply_to_all(
|
||||
git_index *index,
|
||||
int action,
|
||||
const git_strarray *paths,
|
||||
git_index_matched_path_cb cb,
|
||||
void *payload)
|
||||
{
|
||||
int error = 0;
|
||||
size_t i;
|
||||
git_pathspec ps;
|
||||
const char *match;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
|
||||
assert(index);
|
||||
|
||||
if ((error = git_pathspec__init(&ps, paths)) < 0)
|
||||
return error;
|
||||
|
||||
git_vector_sort(&index->entries);
|
||||
|
||||
for (i = 0; !error && i < index->entries.length; ++i) {
|
||||
git_index_entry *entry = git_vector_get(&index->entries, i);
|
||||
|
||||
/* check if path actually matches */
|
||||
if (!git_pathspec__match(
|
||||
&ps.pathspec, entry->path, false, index->ignore_case,
|
||||
&match, NULL))
|
||||
continue;
|
||||
|
||||
/* issue notification callback if requested */
|
||||
if (cb && (error = cb(entry->path, match, payload)) != 0) {
|
||||
if (error > 0) { /* return > 0 means skip this one */
|
||||
error = 0;
|
||||
continue;
|
||||
}
|
||||
if (error < 0) { /* return < 0 means abort */
|
||||
giterr_clear();
|
||||
error = GIT_EUSER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* index manipulation may alter entry, so don't depend on it */
|
||||
if ((error = git_buf_sets(&path, entry->path)) < 0)
|
||||
break;
|
||||
|
||||
switch (action) {
|
||||
case INDEX_ACTION_NONE:
|
||||
break;
|
||||
case INDEX_ACTION_UPDATE:
|
||||
error = git_index_add_bypath(index, path.ptr);
|
||||
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
giterr_clear();
|
||||
|
||||
error = git_index_remove_bypath(index, path.ptr);
|
||||
|
||||
if (!error) /* back up foreach if we removed this */
|
||||
i--;
|
||||
}
|
||||
break;
|
||||
case INDEX_ACTION_REMOVE:
|
||||
if (!(error = git_index_remove_bypath(index, path.ptr)))
|
||||
i--; /* back up foreach if we removed this */
|
||||
break;
|
||||
default:
|
||||
giterr_set(GITERR_INVALID, "Unknown index action %d", action);
|
||||
error = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
git_buf_free(&path);
|
||||
git_pathspec__clear(&ps);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_index_remove_all(
|
||||
git_index *index,
|
||||
const git_strarray *pathspec,
|
||||
git_index_matched_path_cb cb,
|
||||
void *payload)
|
||||
{
|
||||
return index_apply_to_all(
|
||||
index, INDEX_ACTION_REMOVE, pathspec, cb, payload);
|
||||
}
|
||||
|
||||
int git_index_update_all(
|
||||
git_index *index,
|
||||
const git_strarray *pathspec,
|
||||
git_index_matched_path_cb cb,
|
||||
void *payload)
|
||||
{
|
||||
return index_apply_to_all(
|
||||
index, INDEX_ACTION_UPDATE, pathspec, cb, payload);
|
||||
}
|
||||
|
@ -47,13 +47,17 @@ struct git_index_conflict_iterator {
|
||||
size_t cur;
|
||||
};
|
||||
|
||||
extern void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st);
|
||||
extern void git_index_entry__init_from_stat(
|
||||
git_index_entry *entry, struct stat *st);
|
||||
|
||||
extern size_t git_index__prefix_position(git_index *index, const char *path);
|
||||
|
||||
extern int git_index_entry__cmp(const void *a, const void *b);
|
||||
extern int git_index_entry__cmp_icase(const void *a, const void *b);
|
||||
|
||||
extern int git_index__find(
|
||||
size_t *at_pos, git_index *index, const char *path, int stage);
|
||||
|
||||
extern void git_index__set_ignore_case(git_index *index, bool ignore_case);
|
||||
|
||||
#endif
|
||||
|
@ -325,7 +325,7 @@ static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t ent
|
||||
/* FIXME: Parse the object instead of hashing it */
|
||||
if (git_odb__hashobj(&oid, obj) < 0) {
|
||||
giterr_set(GITERR_INDEXER, "Failed to hash object");
|
||||
return -1;
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
pentry = git__calloc(1, sizeof(struct git_pack_entry));
|
||||
@ -602,7 +602,7 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *
|
||||
git_vector_sort(&idx->objects);
|
||||
|
||||
git_buf_sets(&filename, idx->pack->pack_name);
|
||||
git_buf_truncate(&filename, filename.size - strlen("pack"));
|
||||
git_buf_shorten(&filename, strlen("pack"));
|
||||
git_buf_puts(&filename, "idx");
|
||||
if (git_buf_oom(&filename))
|
||||
return -1;
|
||||
|
@ -1321,9 +1321,10 @@ static void workdir_iterator__free(git_iterator *self)
|
||||
git_ignore__free(&wi->ignores);
|
||||
}
|
||||
|
||||
int git_iterator_for_workdir(
|
||||
int git_iterator_for_workdir_ext(
|
||||
git_iterator **out,
|
||||
git_repository *repo,
|
||||
const char *repo_workdir,
|
||||
git_iterator_flag_t flags,
|
||||
const char *start,
|
||||
const char *end)
|
||||
@ -1331,8 +1332,11 @@ int git_iterator_for_workdir(
|
||||
int error;
|
||||
workdir_iterator *wi;
|
||||
|
||||
if (!repo_workdir) {
|
||||
if (git_repository__ensure_not_bare(repo, "scan working directory") < 0)
|
||||
return GIT_EBAREREPO;
|
||||
repo_workdir = git_repository_workdir(repo);
|
||||
}
|
||||
|
||||
/* initialize as an fs iterator then do overrides */
|
||||
wi = git__calloc(1, sizeof(workdir_iterator));
|
||||
@ -1346,13 +1350,13 @@ int git_iterator_for_workdir(
|
||||
wi->fi.update_entry_cb = workdir_iterator__update_entry;
|
||||
|
||||
if ((error = iterator__update_ignore_case((git_iterator *)wi, flags)) < 0 ||
|
||||
(error = git_ignore__for_path(repo, "", &wi->ignores)) < 0)
|
||||
(error = git_ignore__for_path(repo, ".gitignore", &wi->ignores)) < 0)
|
||||
{
|
||||
git_iterator_free((git_iterator *)wi);
|
||||
return error;
|
||||
}
|
||||
|
||||
return fs_iterator__initialize(out, &wi->fi, git_repository_workdir(repo));
|
||||
return fs_iterator__initialize(out, &wi->fi, repo_workdir);
|
||||
}
|
||||
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user