Merge tag 'upstream/0.21.0'

* tag 'upstream/0.21.0': (723 commits)
  test: remove assembla clone test
  test: use read-only account
  http: fix typo in credentials logic
  ssl: init everything all the time
  ssl: init also without threads
  ssl: cargo-cult thread safety
  ssl: use locking
  remote: update documentation
  netops: init OpenSSL once under lock
  revwalk: more sensible array handling
  pathspec: use C guards in header
  treebuilder: insert sorted
  remote: fix rename docs
  Bump version to 0.21.0
  Change SOVERSION at API breaks
  React to review feedback
  Win32: Fix object::cache::threadmania test on x64
  Win32: Fix diff::workdir::submodules test #2361
  Win32: Fix failing clone_mirror test
  remote: don't free the remote on delete
  ...
This commit is contained in:
Russell Sim 2014-06-21 11:18:03 +10:00
commit da6fa9054a
784 changed files with 30876 additions and 10868 deletions

View File

@ -3,23 +3,39 @@
language: c
os:
- linux
- osx
compiler:
- gcc
- clang
# Settings to try
env:
global:
- secure: "YnhS+8n6B+uoyaYfaJ3Lei7cSJqHDPiKJCKFIF2c87YDfmCvAJke8QtE7IzjYDs7UFkTCM4ox+ph2bERUrxZbSCyEkHdjIZpKuMJfYWja/jgMqTMxdyOH9y8JLFbZsSXDIXDwqBlC6vVyl1fP90M35wuWcNTs6tctfVWVofEFbs="
matrix:
- OPTIONS="-DTHREADSAFE=ON -DCMAKE_BUILD_TYPE=Release"
- OPTIONS="-DBUILD_CLAR=ON -DBUILD_EXAMPLES=ON"
matrix:
fast_finish: true
exclude:
- os: osx
compiler: gcc
include:
- compiler: i586-mingw32msvc-gcc
env: OPTIONS="-DBUILD_CLAR=OFF -DWIN32=ON -DMINGW=ON"
env: OPTIONS="-DBUILD_CLAR=OFF -DWIN32=ON -DMINGW=ON -DUSE_SSH=OFF"
os: linux
- compiler: gcc
env: COVERITY=1
os: linux
allow_failures:
- env: COVERITY=1
install:
- sudo apt-get -qq update
- sudo apt-get -qq install cmake libssh2-1-dev openssh-client openssh-server
- ./script/install-deps-${TRAVIS_OS_NAME}.sh
# Run the Build script and tests
script:
@ -27,8 +43,8 @@ script:
# Run Tests
after_success:
- sudo apt-get -qq install valgrind
- valgrind --leak-check=full --show-reachable=yes --suppressions=./libgit2_clar.supp _build/libgit2_clar -ionline
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get -qq install valgrind; fi
- if [ "$TRAVIS_OS_NAME" = "linux" ]; then valgrind --leak-check=full --show-reachable=yes --suppressions=./libgit2_clar.supp _build/libgit2_clar -ionline; fi
# Only watch the development branch
branches:

View File

@ -6,6 +6,7 @@ Alexei Sholik
Andreas Ericsson
Anton "antong" Gyllenberg
Ankur Sethi
Arthur Schreiber
Ben Noordhuis
Ben Straub
Benjamin C Meyer
@ -25,6 +26,7 @@ Florian Forster
Holger Weiss
Ingmar Vanhassel
J. David Ibáñez
Jacques Germishuys
Jakob Pfender
Jason Penny
Jason R. McNeil

View File

@ -15,7 +15,10 @@ PROJECT(libgit2 C)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
# Add find modules to the path
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/")
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/")
INCLUDE(CheckLibraryExists)
INCLUDE(AddCFlagIfSupported)
# Build options
#
@ -33,8 +36,9 @@ OPTION( ANDROID "Build for android NDK" OFF )
OPTION( USE_ICONV "Link with and use iconv library" OFF )
OPTION( USE_SSH "Link with libssh to enable SSH support" ON )
OPTION( VALGRIND "Configure build for valgrind" OFF )
IF(APPLE)
IF(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
SET( USE_ICONV ON )
ENDIF()
@ -54,6 +58,10 @@ IF(MSVC)
# By default, libgit2 is built with WinHTTP. To use the built-in
# HTTP transport, invoke CMake with the "-DWINHTTP=OFF" argument.
OPTION( WINHTTP "Use Win32 WinHTTP routines" ON )
ADD_DEFINITIONS(-D_SCL_SECURE_NO_WARNINGS)
ADD_DEFINITIONS(-D_CRT_SECURE_NO_DEPRECATE)
ADD_DEFINITIONS(-D_CRT_NONSTDC_NO_DEPRECATE)
ENDIF()
# This variable will contain the libraries we need to put into
@ -77,17 +85,13 @@ FUNCTION(TARGET_OS_LIBRARIES target)
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
TARGET_LINK_LIBRARIES(${target} socket nsl)
SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lsocket -lnsl" PARENT_SCOPE)
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Linux")
ENDIF()
CHECK_LIBRARY_EXISTS(rt clock_gettime "time.h" NEED_LIBRT)
IF(NEED_LIBRT)
TARGET_LINK_LIBRARIES(${target} rt)
SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lrt" PARENT_SCOPE)
ENDIF()
IF(USE_ICONV)
TARGET_LINK_LIBRARIES(${target} iconv)
ADD_DEFINITIONS(-DGIT_USE_ICONV)
SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -liconv" PARENT_SCOPE)
ENDIF()
IF(THREADSAFE)
TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT})
ENDIF()
@ -123,6 +127,9 @@ STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"[0-9]+\\.([0-9]+).*$" "\\1" LIBGIT2_V
STRING(REGEX REPLACE "^.*LIBGIT2_VERSION \"[0-9]+\\.[0-9]+\\.([0-9]+).*$" "\\1" LIBGIT2_VERSION_REV "${GIT2_HEADER}")
SET(LIBGIT2_VERSION_STRING "${LIBGIT2_VERSION_MAJOR}.${LIBGIT2_VERSION_MINOR}.${LIBGIT2_VERSION_REV}")
FILE(STRINGS "include/git2/version.h" GIT2_HEADER_SOVERSION REGEX "^#define LIBGIT2_SOVERSION [0-9]+$")
STRING(REGEX REPLACE "^.*LIBGIT2_SOVERSION ([0-9]+)$" "\\1" LIBGIT2_SOVERSION "${GIT2_HEADER_SOVERSION}")
# Find required dependencies
INCLUDE_DIRECTORIES(src include)
@ -135,13 +142,13 @@ ELSE ()
FIND_PACKAGE(OpenSSL)
ENDIF ()
FIND_PACKAGE(HTTP_Parser QUIET)
FIND_PACKAGE(HTTP_Parser)
IF (HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2)
INCLUDE_DIRECTORIES(${HTTP_PARSER_INCLUDE_DIRS})
LINK_LIBRARIES(${HTTP_PARSER_LIBRARIES})
SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lhttp_parser")
ELSE()
MESSAGE("http-parser was not found or is too old; using bundled 3rd-party sources.")
MESSAGE(STATUS "http-parser was not found or is too old; using bundled 3rd-party sources.")
INCLUDE_DIRECTORIES(deps/http-parser)
FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h)
ENDIF()
@ -153,7 +160,11 @@ IF (WIN32 AND NOT MINGW AND NOT SHA1_TYPE STREQUAL "builtin")
FILE(GLOB SRC_SHA1 src/hash/hash_win32.c)
ELSEIF (OPENSSL_FOUND AND NOT SHA1_TYPE STREQUAL "builtin")
ADD_DEFINITIONS(-DOPENSSL_SHA1)
SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} openssl")
IF (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lssl")
ELSE()
SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} openssl")
ENDIF ()
ELSE()
FILE(GLOB SRC_SHA1 src/hash/hash_generic.c)
ENDIF()
@ -164,34 +175,31 @@ IF (ENABLE_TRACE STREQUAL "ON")
ENDIF()
# Include POSIX regex when it is required
IF(WIN32 OR AMIGA OR ANDROID)
IF(WIN32 OR AMIGA OR ANDROID OR CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
INCLUDE_DIRECTORIES(deps/regex)
SET(SRC_REGEX deps/regex/regex.c)
ENDIF()
# Optional external dependency: zlib
# It's optional, but FIND_PACKAGE gives a warning that looks more like an
# error.
FIND_PACKAGE(ZLIB QUIET)
FIND_PACKAGE(ZLIB)
IF (ZLIB_FOUND)
INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS})
LINK_LIBRARIES(${ZLIB_LIBRARIES})
IF(APPLE)
IF(APPLE OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lz")
ELSE()
SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} zlib")
ENDIF()
# Fake the message CMake would have shown
MESSAGE("-- Found zlib: ${ZLIB_LIBRARY}")
ELSE()
MESSAGE( "zlib was not found; using bundled 3rd-party sources." )
MESSAGE(STATUS "zlib was not found; using bundled 3rd-party sources." )
INCLUDE_DIRECTORIES(deps/zlib)
ADD_DEFINITIONS(-DNO_VIZ -DSTDC -DNO_GZIP)
FILE(GLOB SRC_ZLIB deps/zlib/*.c deps/zlib/*.h)
ENDIF()
IF (USE_SSH AND NOT MINGW)
FIND_PACKAGE(LIBSSH2 QUIET)
# Optional external dependency: libssh2
IF (USE_SSH)
FIND_PACKAGE(LIBSSH2)
ENDIF()
IF (LIBSSH2_FOUND)
ADD_DEFINITIONS(-DGIT_SSH)
@ -200,6 +208,15 @@ IF (LIBSSH2_FOUND)
SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES})
ENDIF()
# Optional external dependency: iconv
IF (USE_ICONV)
FIND_PACKAGE(Iconv)
ENDIF()
IF (ICONV_FOUND)
ADD_DEFINITIONS(-DGIT_USE_ICONV)
INCLUDE_DIRECTORIES(${ICONV_INCLUDE_DIR})
SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} ${ICONV_LIBRARIES}")
ENDIF()
# Platform specific compilation flags
IF (MSVC)
@ -274,7 +291,11 @@ IF (MSVC)
# Precompiled headers
ELSE ()
SET(CMAKE_C_FLAGS "-D_GNU_SOURCE -Wall -Wextra -Wno-missing-field-initializers -Wstrict-aliasing=2 -Wstrict-prototypes ${CMAKE_C_FLAGS}")
SET(CMAKE_C_FLAGS "-D_GNU_SOURCE -Wall -Wextra ${CMAKE_C_FLAGS}")
IF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
SET(CMAKE_C_FLAGS "-std=c99 -D_POSIX_C_SOURCE=200112L -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS ${CMAKE_C_FLAGS}")
ENDIF()
IF (WIN32 AND NOT CYGWIN)
SET(CMAKE_C_FLAGS_DEBUG "-D_DEBUG")
@ -288,11 +309,22 @@ ELSE ()
ADD_DEFINITIONS(-D__USE_MINGW_ANSI_STDIO=1)
ELSEIF (BUILD_SHARED_LIBS)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden -fPIC")
ADD_C_FLAG_IF_SUPPORTED(-fvisibility=hidden)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
ENDIF ()
ADD_C_FLAG_IF_SUPPORTED(-Wno-missing-field-initializers)
ADD_C_FLAG_IF_SUPPORTED(-Wstrict-aliasing=2)
ADD_C_FLAG_IF_SUPPORTED(-Wstrict-prototypes)
ADD_C_FLAG_IF_SUPPORTED(-Wdeclaration-after-statement)
ADD_C_FLAG_IF_SUPPORTED(-Wno-unused-const-variable)
ADD_C_FLAG_IF_SUPPORTED(-Wno-unused-function)
IF (APPLE) # Apple deprecated OpenSSL
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations")
ENDIF ()
ADD_C_FLAG_IF_SUPPORTED(-Wno-deprecated-declarations)
ENDIF()
IF (PROFILE)
SET(CMAKE_C_FLAGS "-pg ${CMAKE_C_FLAGS}")
SET(CMAKE_EXE_LINKER_FLAGS "-pg ${CMAKE_EXE_LINKER_FLAGS}")
@ -317,7 +349,7 @@ ENDIF()
IF (THREADSAFE)
IF (NOT WIN32)
find_package(Threads REQUIRED)
FIND_PACKAGE(Threads REQUIRED)
ENDIF()
ADD_DEFINITIONS(-DGIT_THREADS)
@ -333,9 +365,11 @@ IF (WIN32 AND NOT CYGWIN)
ADD_DEFINITIONS(-DWIN32 -D_WIN32_WINNT=0x0501)
FILE(GLOB SRC_OS src/win32/*.c src/win32/*.h)
ELSEIF (AMIGA)
ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R)
FILE(GLOB SRC_OS src/amiga/*.c src/amiga/*.h)
ADD_DEFINITIONS(-DNO_ADDRINFO -DNO_READDIR_R -DNO_MMAP)
ELSE()
IF (VALGRIND)
ADD_DEFINITIONS(-DNO_MMAP)
ENDIF()
FILE(GLOB SRC_OS src/unix/*.c src/unix/*.h)
ENDIF()
FILE(GLOB SRC_GIT2 src/*.c src/*.h src/transports/*.c src/transports/*.h src/xdiff/*.c src/xdiff/*.h)
@ -346,13 +380,14 @@ IF (CMAKE_SIZEOF_VOID_P EQUAL 8)
ELSEIF (CMAKE_SIZEOF_VOID_P EQUAL 4)
ADD_DEFINITIONS(-DGIT_ARCH_32)
ELSE()
message(FATAL_ERROR "Unsupported architecture")
MESSAGE(FATAL_ERROR "Unsupported architecture")
ENDIF()
# Compile and link libgit2
ADD_LIBRARY(git2 ${SRC_H} ${SRC_GIT2} ${SRC_OS} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${SRC_SHA1} ${WIN_RC})
TARGET_LINK_LIBRARIES(git2 ${SSL_LIBRARIES})
TARGET_LINK_LIBRARIES(git2 ${SSH_LIBRARIES})
TARGET_LINK_LIBRARIES(git2 ${ICONV_LIBRARIES})
TARGET_OS_LIBRARIES(git2)
# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240)
@ -365,7 +400,7 @@ MSVC_SPLIT_SOURCES(git2)
IF (SONAME)
SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING})
SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_VERSION_MAJOR})
SET_TARGET_PROPERTIES(git2 PROPERTIES SOVERSION ${LIBGIT2_SOVERSION})
IF (LIBGIT2_FILENAME)
ADD_DEFINITIONS(-DLIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
SET_TARGET_PROPERTIES(git2 PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
@ -418,6 +453,7 @@ IF (BUILD_CLAR)
TARGET_LINK_LIBRARIES(libgit2_clar ${SSL_LIBRARIES})
TARGET_LINK_LIBRARIES(libgit2_clar ${SSH_LIBRARIES})
TARGET_LINK_LIBRARIES(libgit2_clar ${ICONV_LIBRARIES})
TARGET_OS_LIBRARIES(libgit2_clar)
MSVC_SPLIT_SOURCES(libgit2_clar)
@ -433,7 +469,7 @@ ENDIF ()
IF (TAGS)
FIND_PROGRAM(CTAGS ctags)
IF (NOT CTAGS)
message(FATAL_ERROR "Could not find ctags command")
MESSAGE(FATAL_ERROR "Could not find ctags command")
ENDIF ()
FILE(GLOB_RECURSE SRC_ALL *.[ch])

View File

@ -5,9 +5,13 @@ your help.
## Licensing
By contributing to libgit2, you agree to release your contribution under the terms of the license.
For code under `examples`, this is governed by the [CC0 Public Domain Dedication](examples/COPYING).
All other code is released under the [GPL v2 with linking exception](COPYING).
By contributing to libgit2, you agree to release your contribution under
the terms of the license. Except for the `examples` directory, all code
is released under the [GPL v2 with linking exception](COPYING).
The `examples` code is governed by the
[CC0 Public Domain Dedication](examples/COPYING), so that you may copy
from them into your own application.
## Discussion & Chat
@ -76,15 +80,19 @@ 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.
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)
(a.k.a. C89) with some specific conventions for function and type naming,
code formatting, and testing.
The public API of `libgit2` is [ANSI C](http://en.wikipedia.org/wiki/ANSI_C)
(a.k.a. C89) compatible. Internally, `libgit2` is written using a portable
subset of C99 - in order to compile with GCC, Clang, MSVC, etc., we keep
local variable declarations at the tops of blocks only and avoid `//` style
comments. Additionally, `libgit2` follows some extra conventions for
function and type naming, code formatting, and testing.
We like to keep the source code consistent and easy to read. Maintaining
this takes some discipline, but it's been more than worth it. Take a look
@ -93,22 +101,4 @@ at the
## Starter Projects
So, you want to start helping out with `libgit2`? That's fantastic? We
welcome contributions and we promise we'll try to be nice.
If you want to jump in, you can look at our issues list to see if there
are any unresolved issues to jump in on. Also, here is a list of some
smaller project ideas that could help you become familiar with the code
base and make a nice first step:
* Convert a `git_*modulename*_foreach()` callback-based iteration API
into a `git_*modulename*_iterator` object with a create/advance style
of API. This helps folks writing language bindings and usually isn't
too complicated.
* Write a new `examples/` program that mirrors a particular core git
command. (See `examples/diff.c` for example.) This lets you (and us)
easily exercise a particular facet of the API and measure compatability
and feature parity with core git.
* Submit a PR to clarify documentation! While we do try to document all of
the APIs, your fresh eyes on the documentation will find areas that are
confusing much more easily.
See our [projects list](https://github.com/libgit2/libgit2/blob/development/PROJECTS.md).

View File

@ -6,14 +6,18 @@ guidelines that should help with that.
## Compatibility
`libgit2` runs on many different platforms with many different compilers.
It is written in [ANSI C](http://en.wikipedia.org/wiki/ANSI_C) (a.k.a. C89)
with some specific standards for function and type naming, code formatting,
and testing.
We try to avoid more recent extensions to maximize portability. We also, to
the greatest extent possible, try to avoid lots of `#ifdef`s inside the core
code base. This is somewhat unavoidable, but since it can really hamper
maintainability, we keep it to a minimum.
The public API of `libgit2` is [ANSI C](http://en.wikipedia.org/wiki/ANSI_C)
(a.k.a. C89) compatible.
Internally, `libgit2` is written using a portable subset of C99 - in order
to maximize compatibility (e.g. with MSVC) we avoid certain C99
extensions. Specifically, we keep local variable declarations at the tops
of blocks only and we avoid `//` style comments.
Also, to the greatest extent possible, we try to avoid lots of `#ifdef`s
inside the core code base. This is somewhat unavoidable, but since it can
really hamper maintainability, we keep it to a minimum.
## Match Surrounding Code
@ -209,6 +213,9 @@ All inlined functions must be declared as:
GIT_INLINE(result_type) git_modulename_functionname(arg_list);
```
`GIT_INLINE` (or `inline`) should not be used in public headers in order
to preserve ANSI C compatibility.
## Tests
`libgit2` uses the [clar](https://github.com/vmg/clar) testing framework.

75
COPYING
View File

@ -388,19 +388,7 @@ Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler
----------------------------------------------------------------------
The priority queue implementation is based on code licensed under the
Apache 2.0 license:
Copyright 2010 Volkan Yazıcı <volkan.yazici@gmail.com>
Copyright 2006-2010 The Apache Software Foundation
The full text of the Apache 2.0 license is available at:
http://www.apache.org/licenses/LICENSE-2.0
----------------------------------------------------------------------
The Clay framework is licensed under the MIT license:
The Clar framework is licensed under the MIT license:
Copyright (C) 2011 by Vicent Marti
@ -930,64 +918,3 @@ necessary. Here is a sample; alter the names:
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.
--------------------------------------------------------------------

85
PROJECTS.md Normal file
View File

@ -0,0 +1,85 @@
Projects For LibGit2
====================
So, you want to start helping out with `libgit2`? That's fantastic! We
welcome contributions and we promise we'll try to be nice.
This is a list of libgit2 related projects that new contributors can take
on. It includes a number of good starter projects and well as some larger
ideas that no one is actively working on.
## Before You Start
Please start by reading the README.md, CONTRIBUTING.md, and CONVENTIONS.md
files before diving into one of these projects. Those will explain our
work flow and coding conventions to help ensure that your work will be
easily integrated into libgit2.
Next, work through the build instructions and make sure you can clone the
repository, compile it, and run the tests successfully. That will make
sure that your development environment is set up correctly and you are
ready to start on libgit2 development.
## Starter Projects
These are good small projects to get started with libgit2.
* Look at the `examples/` programs, find an existing one that mirrors a
core Git command and add a missing command-line option. There are many
gaps right now and this helps demonstrate how to use the library. Here
are some specific ideas:
* Fix the `examples/diff.c` implementation of the `-B`
(a.k.a. `--break-rewrites`) command line option to actually look for
the optional `[<n>][/<m>]` configuration values. There is an
existing comment that reads `/* TODO: parse thresholds */`. The
trick to this one will be doing it in a manner that is clean and
simple, but still handles the various cases correctly (e.g. `-B/70%`
is apparently a legal setting).
* Implement the `--log-size` option for `examples/log.c`. I think all
the data is available, you would just need to add the code into the
`print_commit()` routine (along with a way of passing the option
into that function).
* As an extension to the matching idea for `examples/log.c`, add the
`-i` option to use `strcasestr()` for matches.
* For `examples/log.c`, implement the `--first-parent` option now that
libgit2 supports it in the revwalk API.
* Pick a Git command that is not already emulated in `examples/` and write
a new example that mirrors the behavior. Examples don't have to be
perfect emulations, but should demonstrate how to use the libgit2 APIs
to get results that are similar to Git commands. This lets you (and us)
easily exercise a particular facet of the API and measure compatability
and feature parity with core git.
* Submit a PR to clarify documentation! While we do try to document all of
the APIs, your fresh eyes on the documentation will find areas that are
confusing much more easily.
* Add support for the symref protocol extension, so we don't guess
what the remote's default branch is
[#2006](https://github.com/libgit2/libgit2/issues/2006)
If none of these appeal to you, take a look at our issues list to see if
there are any unresolved issues you'd like to jump in on.
## Larger Projects
These are ideas for larger projects mostly taken from our backlog of
[Issues](https://github.com/libgit2/libgit2/issues). Please don't dive
into one of these as a first project for libgit2 - we'd rather get to
know you first by successfully shipping your work on one of the smaller
projects above.
* Port part of the Git test suite to run against the command line emulation
in examples/
* Fix symlink support for files in the .git directory (i.e. don't overwrite
the symlinks when writing the file contents back out)
* Implement a 'git describe' like API
* Add hooks API to enumerate and manage hooks (not run them at this point)
* Isolate logic of ignore evaluation into a standalone API
* Upgrade internal libxdiff code to latest from core Git
* Add a hashtable lookup for files in the index instead of binary search
every time
* Make the index write the cache out to disk (with tests to gain
confidence that the caching invalidation works correctly)
* Have the tree builder use a hash table when building instead of a
list.
* Move the tagopt mechanism to the newer git 1.9 interpretation of
--tags [#2120](https://github.com/libgit2/libgit2/issues/2120)

View File

@ -2,10 +2,11 @@ libgit2 - the Git linkable library
==================================
[![Build Status](https://secure.travis-ci.org/libgit2/libgit2.png?branch=development)](http://travis-ci.org/libgit2/libgit2)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/639/badge.svg)](https://scan.coverity.com/projects/639)
`libgit2` is a portable, pure C implementation of the Git core methods provided as a
re-entrant linkable library with a solid API, allowing you to write native
speed custom Git applications in any language with bindings.
`libgit2` is a portable, pure C implementation of the Git core methods
provided as a re-entrant linkable library with a solid API, allowing you to
write native speed custom Git applications in any language with bindings.
`libgit2` is licensed under a **very permissive license** (GPLv2 with a special
Linking Exception). This basically means that you can link it (unmodified)
@ -19,18 +20,19 @@ Additionally, the example code has been released to the public domain (see the
* API documentation: <http://libgit2.github.com/libgit2>
* IRC: [#libgit2](irc://irc.freenode.net/libgit2) on irc.freenode.net.
* Mailing list: The libgit2 mailing list was
traditionally hosted in Librelist but has been deprecated. We encourage you to
traditionally hosted in Librelist but has been deprecated. We encourage you to
[use StackOverflow](http://stackoverflow.com/questions/tagged/libgit2) instead for any questions regarding
the library, or [open an issue](https://github.com/libgit2/libgit2/issues)
on GitHub for bug reports. The mailing list archives are still available at
the library, or [open an issue](https://github.com/libgit2/libgit2/issues)
on GitHub for bug reports. The mailing list archives are still available at
<http://librelist.com/browser/libgit2/>.
What It Can Do
==============
`libgit2` is already very usable and is being used in production for many applications including the GitHub.com site, in Plastic SCM
and also powering Microsoft's Visual Studio tools for Git. The library provides:
`libgit2` is already very usable and is being used in production for many
applications including the GitHub.com site, in Plastic SCM and also powering
Microsoft's Visual Studio tools for Git. The library provides:
* SHA conversions, formatting and shortening
* abstracted ODB backend system
@ -64,7 +66,7 @@ Under Unix-like systems, like Linux, \*BSD and Mac OS X, libgit2 expects `pthrea
they should be installed by default on all systems. Under Windows, libgit2 uses the native Windows API
for threading.
The `libgit2` library is built using `CMake 2.6+` (<http://www.cmake.org>) on all platforms.
The `libgit2` library is built using [CMake](<http://www.cmake.org>) (version 2.6 or newer) on all platforms.
On most systems you can build the library using the following commands
@ -164,11 +166,12 @@ Here are the bindings to libgit2 that are currently available:
* libgit2-glib <https://live.gnome.org/Libgit2-glib>
* Haskell
* hgit2 <https://github.com/fpco/gitlib>
* Java
* Jagged <https://github.com/ethomson/jagged>
* Lua
* luagit2 <https://github.com/libgit2/luagit2>
* .NET
* libgit2sharp <https://github.com/libgit2/libgit2sharp>
* libgit2net, low level bindings superseded by libgit2sharp <https://github.com/txdv/libgit2net>
* Node.js
* node-gitteh <https://github.com/libgit2/node-gitteh>
* nodegit <https://github.com/tbranyen/nodegit>
@ -182,8 +185,12 @@ Here are the bindings to libgit2 that are currently available:
* Git-Raw <https://github.com/ghedo/p5-Git-Raw>
* PHP
* php-git <https://github.com/libgit2/php-git>
* PowerShell
* GitPowerShell <https://github.com/ethomson/gitpowershell>
* Python
* pygit2 <https://github.com/libgit2/pygit2>
* R
* git2r <https://github.com/ropensci/git2r>
* Ruby
* Rugged <https://github.com/libgit2/rugged>
* Vala
@ -195,14 +202,16 @@ we can add it to the list.
How Can I Contribute?
==================================
Check the [contribution guidelines](CONTRIBUTING.md).
Check the [contribution guidelines](CONTRIBUTING.md) to understand our
workflow, the libgit2 [coding conventions](CONVENTIONS.md), and out list of
[good starting projects](PROJECTS.md).
License
==================================
`libgit2` is under GPL2 **with linking exemption**. This means you
can link to and use the library from any program, proprietary or open source; paid
or gratis. However, you cannot modify libgit2 and distribute it without
`libgit2` is under GPL2 **with linking exemption**. This means you can link to
and use the library from any program, proprietary or open source; paid or
gratis. However, you cannot modify libgit2 and distribute it without
supplying the source.
See the COPYING file for the full license text.
See the [COPYING file](COPYING) for the full license text.

View File

@ -0,0 +1,16 @@
# - Append compiler flag to CMAKE_C_FLAGS if compiler supports it
# ADD_C_FLAG_IF_SUPPORTED(<flag>)
# <flag> - the compiler flag to test
# This internally calls the CHECK_C_COMPILER_FLAG macro.
INCLUDE(CheckCCompilerFlag)
MACRO(ADD_C_FLAG_IF_SUPPORTED _FLAG)
STRING(TOUPPER ${_FLAG} UPCASE)
STRING(REGEX REPLACE "^-" "" UPCASE_PRETTY ${UPCASE})
CHECK_C_COMPILER_FLAG(${_FLAG} IS_${UPCASE_PRETTY}_SUPPORTED)
IF(IS_${UPCASE_PRETTY}_SUPPORTED)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_FLAG}")
ENDIF()
ENDMACRO()

View File

@ -0,0 +1,43 @@
# - Try to find Iconv
# Once done this will define
#
# ICONV_FOUND - system has Iconv
# ICONV_INCLUDE_DIR - the Iconv include directory
# ICONV_LIBRARIES - Link these to use Iconv
#
IF(ICONV_INCLUDE_DIR AND ICONV_LIBRARIES)
# Already in cache, be silent
SET(ICONV_FIND_QUIETLY TRUE)
ENDIF()
FIND_PATH(ICONV_INCLUDE_DIR iconv.h PATHS /opt/local/include NO_DEFAULT_PATH)
FIND_PATH(ICONV_INCLUDE_DIR iconv.h)
FIND_LIBRARY(iconv_lib NAMES iconv libiconv libiconv-2 c NO_DEFAULT_PATH PATHS /opt/local/lib)
FIND_LIBRARY(iconv_lib NAMES iconv libiconv libiconv-2 c)
IF(ICONV_INCLUDE_DIR AND iconv_lib)
SET(ICONV_FOUND TRUE)
ENDIF()
IF(ICONV_FOUND)
# split iconv into -L and -l linker options, so we can set them for pkg-config
GET_FILENAME_COMPONENT(iconv_path ${iconv_lib} PATH)
GET_FILENAME_COMPONENT(iconv_name ${iconv_lib} NAME_WE)
STRING(REGEX REPLACE "^lib" "" iconv_name ${iconv_name})
SET(ICONV_LIBRARIES "-L${iconv_path} -l${iconv_name}")
IF(NOT ICONV_FIND_QUIETLY)
MESSAGE(STATUS "Found Iconv: ${ICONV_LIBRARIES}")
ENDIF(NOT ICONV_FIND_QUIETLY)
ELSE()
IF(Iconv_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "Could not find Iconv")
ENDIF(Iconv_FIND_REQUIRED)
ENDIF()
MARK_AS_ADVANCED(
ICONV_INCLUDE_DIR
ICONV_LIBRARIES
)

13
deps/regex/regex.c vendored
View File

@ -67,10 +67,17 @@
#include "regex_internal.h"
#include "regex_internal.c"
#ifdef GAWK
#define bool int
#define true (1)
#define false (0)
# define bool int
# ifndef true
# define true (1)
# endif
# ifndef false
# define false (0)
# endif
#endif
#include "regcomp.c"
#include "regexec.c"

View File

@ -21,7 +21,7 @@ content, everything goes through diff drivers that are implemented in
External Objects
----------------
* `git_diff_options` repesents user choices about how a diff should be
* `git_diff_options` represents user choices about how a diff should be
performed and is passed to most diff generating functions.
* `git_diff_file` represents an item on one side of a possible delta
* `git_diff_delta` represents a pair of items that have changed in some
@ -37,7 +37,7 @@ External Objects
header that compactly represents that information, and it will have a
number of lines of context surrounding added and deleted lines.
* A `line` is simple a line of data along with a `git_diff_line_t` value
that tells how the data should be interpretted (e.g. context or added).
that tells how the data should be interpreted (e.g. context or added).
Internal Objects
----------------

2
examples/.gitignore vendored
View File

@ -8,4 +8,6 @@ init
log
rev-parse
status
tag
for-each-ref
*.dSYM

View File

@ -3,7 +3,8 @@
CC = gcc
CFLAGS = -g -I../include -I../src -Wall -Wextra -Wmissing-prototypes -Wno-missing-field-initializers
LFLAGS = -L../build -lgit2 -lz
APPS = general showindex diff rev-list cat-file status log rev-parse init blame
APPS = general showindex diff rev-list cat-file status log rev-parse init blame tag
APPS += for-each-ref
all: $(APPS)

View File

@ -78,7 +78,7 @@ int print_matched_cb(const char *path, const char *matched_pathspec, void *paylo
git_status_t status;
(void)matched_pathspec;
if (git_status_file(&status, p.repo, path)) {
if (git_status_file((unsigned int*)(&status), p.repo, path)) {
return -1; //abort
}

View File

@ -14,6 +14,11 @@
#include "common.h"
#ifdef _MSC_VER
#define snprintf sprintf_s
#define strcasecmp strcmpi
#endif
/**
* This example demonstrates how to invoke the libgit2 blame API to roughly
* simulate the output of `git blame` and a few of its command line arguments.
@ -26,6 +31,7 @@ struct opts {
int M;
int start_line;
int end_line;
int F;
};
static void parse_opts(struct opts *o, int argc, char *argv[]);
@ -47,6 +53,7 @@ int main(int argc, char *argv[])
parse_opts(&o, argc, argv);
if (o.M) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
if (o.C) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES;
if (o.F) blameopts.flags |= GIT_BLAME_FIRST_PARENT;
/** Open the repository. */
check_lg2(git_repository_open_ext(&repo, ".", 0, NULL), "Couldn't open repository", NULL);
@ -100,8 +107,9 @@ int main(int argc, char *argv[])
if (break_on_null_hunk && !hunk) break;
if (hunk) {
break_on_null_hunk = 1;
char sig[128] = {0};
break_on_null_hunk = 1;
git_oid_tostr(oid, 10, &hunk->final_commit_id);
snprintf(sig, 30, "%s <%s>", hunk->final_signature->name, hunk->final_signature->email);
@ -141,6 +149,7 @@ static void usage(const char *msg, const char *arg)
fprintf(stderr, " -L <n,m> process only line range n-m, counting from 1\n");
fprintf(stderr, " -M find line moves within and across files\n");
fprintf(stderr, " -C find line copies within and across files\n");
fprintf(stderr, " -F follow only the first parent commits\n");
fprintf(stderr, "\n");
exit(1);
}
@ -169,6 +178,8 @@ static void parse_opts(struct opts *o, int argc, char *argv[])
o->M = 1;
else if (!strcasecmp(a, "-C"))
o->C = 1;
else if (!strcasecmp(a, "-F"))
o->F = 1;
else if (!strcasecmp(a, "-L")) {
i++; a = argv[i];
if (i >= argc) fatal("Not enough arguments to -L", NULL);

View File

@ -42,7 +42,7 @@ static void print_signature(const char *header, const git_signature *sig)
static void show_blob(const git_blob *blob)
{
/* ? Does this need crlf filtering? */
fwrite(git_blob_rawcontent(blob), git_blob_rawsize(blob), 1, stdout);
fwrite(git_blob_rawcontent(blob), (size_t)git_blob_rawsize(blob), 1, stdout);
}
/** Show each entry with its type, id and attributes */

View File

@ -156,7 +156,7 @@ int diff_output(
const git_diff_line *l,
void *p)
{
FILE *fp = p;
FILE *fp = (FILE*)p;
(void)d; (void)h;

View File

@ -33,12 +33,27 @@ static const char *colors[] = {
"\033[36m" /* cyan */
};
enum {
OUTPUT_DIFF = (1 << 0),
OUTPUT_STAT = (1 << 1),
OUTPUT_SHORTSTAT = (1 << 2),
OUTPUT_NUMSTAT = (1 << 3),
OUTPUT_SUMMARY = (1 << 4)
};
enum {
CACHE_NORMAL = 0,
CACHE_ONLY = 1,
CACHE_NONE = 2
};
/** The 'opts' struct captures all the various parsed command line options. */
struct opts {
git_diff_options diffopts;
git_diff_find_options findopts;
int color;
int cached;
int cache;
int output;
git_diff_format_t format;
const char *treeish1;
const char *treeish2;
@ -46,9 +61,11 @@ struct opts {
};
/** These functions are implemented at the end */
static void usage(const char *message, const char *arg);
static void parse_opts(struct opts *o, int argc, char *argv[]);
static int color_printer(
const git_diff_delta*, const git_diff_hunk*, const git_diff_line*, void*);
static void diff_print_stats(git_diff *diff, struct opts *o);
int main(int argc, char *argv[])
{
@ -57,7 +74,7 @@ int main(int argc, char *argv[])
git_diff *diff;
struct opts o = {
GIT_DIFF_OPTIONS_INIT, GIT_DIFF_FIND_OPTIONS_INIT,
-1, 0, GIT_DIFF_FORMAT_PATCH, NULL, NULL, "."
-1, 0, 0, GIT_DIFF_FORMAT_PATCH, NULL, NULL, "."
};
git_threads_init();
@ -74,6 +91,7 @@ int main(int argc, char *argv[])
* * &lt;sha1&gt; --cached
* * &lt;sha1&gt;
* * --cached
* * --nocache (don't use index data in diff at all)
* * nothing
*
* Currently ranged arguments like &lt;sha1&gt;..&lt;sha2&gt; and &lt;sha1&gt;...&lt;sha2&gt;
@ -89,20 +107,23 @@ int main(int argc, char *argv[])
check_lg2(
git_diff_tree_to_tree(&diff, repo, t1, t2, &o.diffopts),
"diff trees", NULL);
else if (t1 && o.cached)
check_lg2(
git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts),
"diff tree to index", NULL);
else if (o.cache != CACHE_NORMAL) {
if (!t1)
treeish_to_tree(&t1, repo, "HEAD");
if (o.cache == CACHE_NONE)
check_lg2(
git_diff_tree_to_workdir(&diff, repo, t1, &o.diffopts),
"diff tree to working directory", NULL);
else
check_lg2(
git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts),
"diff tree to index", NULL);
}
else if (t1)
check_lg2(
git_diff_tree_to_workdir_with_index(&diff, repo, t1, &o.diffopts),
"diff tree to working directory", NULL);
else if (o.cached) {
treeish_to_tree(&t1, repo, "HEAD");
check_lg2(
git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts),
"diff tree to index", NULL);
}
else
check_lg2(
git_diff_index_to_workdir(&diff, repo, NULL, &o.diffopts),
@ -117,15 +138,23 @@ int main(int argc, char *argv[])
/** Generate simple output using libgit2 display helper. */
if (o.color >= 0)
fputs(colors[0], stdout);
if (!o.output)
o.output = OUTPUT_DIFF;
check_lg2(
git_diff_print(diff, o.format, color_printer, &o.color),
"displaying diff", NULL);
if (o.output != OUTPUT_DIFF)
diff_print_stats(diff, &o);
if (o.color >= 0)
fputs(colors[0], stdout);
if ((o.output & OUTPUT_DIFF) != 0) {
if (o.color >= 0)
fputs(colors[0], stdout);
check_lg2(
git_diff_print(diff, o.format, color_printer, &o.color),
"displaying diff", NULL);
if (o.color >= 0)
fputs(colors[0], stdout);
}
/** Cleanup before exiting. */
@ -200,16 +229,25 @@ static void parse_opts(struct opts *o, int argc, char *argv[])
usage("Only one or two tree identifiers can be provided", NULL);
}
else if (!strcmp(a, "-p") || !strcmp(a, "-u") ||
!strcmp(a, "--patch"))
!strcmp(a, "--patch")) {
o->output |= OUTPUT_DIFF;
o->format = GIT_DIFF_FORMAT_PATCH;
}
else if (!strcmp(a, "--cached"))
o->cached = 1;
else if (!strcmp(a, "--name-only"))
o->cache = CACHE_ONLY;
else if (!strcmp(a, "--nocache"))
o->cache = CACHE_NONE;
else if (!strcmp(a, "--name-only") || !strcmp(a, "--format=name"))
o->format = GIT_DIFF_FORMAT_NAME_ONLY;
else if (!strcmp(a, "--name-status"))
else if (!strcmp(a, "--name-status") ||
!strcmp(a, "--format=name-status"))
o->format = GIT_DIFF_FORMAT_NAME_STATUS;
else if (!strcmp(a, "--raw"))
else if (!strcmp(a, "--raw") || !strcmp(a, "--format=raw"))
o->format = GIT_DIFF_FORMAT_RAW;
else if (!strcmp(a, "--format=diff-index")) {
o->format = GIT_DIFF_FORMAT_RAW;
o->diffopts.id_abbrev = 40;
}
else if (!strcmp(a, "--color"))
o->color = 0;
else if (!strcmp(a, "--no-color"))
@ -228,6 +266,18 @@ static void parse_opts(struct opts *o, int argc, char *argv[])
o->diffopts.flags |= GIT_DIFF_INCLUDE_IGNORED;
else if (!strcmp(a, "--untracked"))
o->diffopts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
else if (!strcmp(a, "--patience"))
o->diffopts.flags |= GIT_DIFF_PATIENCE;
else if (!strcmp(a, "--minimal"))
o->diffopts.flags |= GIT_DIFF_MINIMAL;
else if (!strcmp(a, "--stat"))
o->output |= OUTPUT_STAT;
else if (!strcmp(a, "--numstat"))
o->output |= OUTPUT_NUMSTAT;
else if (!strcmp(a, "--shortstat"))
o->output |= OUTPUT_SHORTSTAT;
else if (!strcmp(a, "--summary"))
o->output |= OUTPUT_SUMMARY;
else if (match_uint16_arg(
&o->findopts.rename_threshold, &args, "-M") ||
match_uint16_arg(
@ -249,9 +299,39 @@ static void parse_opts(struct opts *o, int argc, char *argv[])
&o->diffopts.context_lines, &args, "--unified") &&
!match_uint16_arg(
&o->diffopts.interhunk_lines, &args, "--inter-hunk-context") &&
!match_uint16_arg(
&o->diffopts.id_abbrev, &args, "--abbrev") &&
!match_str_arg(&o->diffopts.old_prefix, &args, "--src-prefix") &&
!match_str_arg(&o->diffopts.new_prefix, &args, "--dst-prefix") &&
!match_str_arg(&o->dir, &args, "--git-dir"))
usage("Unknown command line argument", a);
}
}
/** Display diff output with "--stat", "--numstat", or "--shortstat" */
static void diff_print_stats(git_diff *diff, struct opts *o)
{
git_diff_stats *stats;
git_buf b = GIT_BUF_INIT_CONST(NULL, 0);
git_diff_stats_format_t format = 0;
check_lg2(
git_diff_get_stats(&stats, diff), "generating stats for diff", NULL);
if (o->output & OUTPUT_STAT)
format |= GIT_DIFF_STATS_FULL;
if (o->output & OUTPUT_SHORTSTAT)
format |= GIT_DIFF_STATS_SHORT;
if (o->output & OUTPUT_NUMSTAT)
format |= GIT_DIFF_STATS_NUMBER;
if (o->output & OUTPUT_SUMMARY)
format |= GIT_DIFF_STATS_INCLUDE_SUMMARY;
check_lg2(
git_diff_stats_to_buf(&b, stats, format, 80), "formatting stats", NULL);
fputs(b.ptr, stdout);
git_buf_free(&b);
git_diff_stats_free(stats);
}

46
examples/for-each-ref.c Normal file
View File

@ -0,0 +1,46 @@
#include <git2.h>
#include <stdio.h>
#include "common.h"
static int show_ref(git_reference *ref, void *data)
{
git_repository *repo = data;
git_reference *resolved = NULL;
char hex[GIT_OID_HEXSZ+1];
const git_oid *oid;
git_object *obj;
if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
check_lg2(git_reference_resolve(&resolved, ref),
"Unable to resolve symbolic reference",
git_reference_name(ref));
oid = git_reference_target(resolved ? resolved : ref);
git_oid_fmt(hex, oid);
hex[GIT_OID_HEXSZ] = 0;
check_lg2(git_object_lookup(&obj, repo, oid, GIT_OBJ_ANY),
"Unable to lookup object", hex);
printf("%s %-6s\t%s\n",
hex,
git_object_type2string(git_object_type(obj)),
git_reference_name(ref));
if (resolved)
git_reference_free(resolved);
return 0;
}
int main(int argc, char **argv)
{
git_repository *repo;
if (argc != 1 || argv[1] /* silence -Wunused-parameter */)
fatal("Sorry, no for-each-ref options supported yet", NULL);
check_lg2(git_repository_open(&repo, "."),
"Could not open repository", NULL);
check_lg2(git_reference_foreach(repo, show_ref, repo),
"Could not iterate over references", NULL);
return 0;
}

View File

@ -47,11 +47,10 @@
// as an example.
static void check_error(int error_code, const char *action)
{
const git_error *error = giterr_last();
if (!error_code)
return;
const git_error *error = giterr_last();
printf("Error %d %s - %s\n", error_code, action,
(error && error->message) ? error->message : "???");

View File

@ -54,8 +54,9 @@ struct log_options {
int min_parents, max_parents;
git_time_t before;
git_time_t after;
char *author;
char *committer;
const char *author;
const char *committer;
const char *grep;
};
/** utility functions that parse options and help with log output */
@ -65,6 +66,9 @@ static void print_time(const git_time *intime, const char *prefix);
static void print_commit(git_commit *commit);
static int match_with_parent(git_commit *commit, int i, git_diff_options *);
/** utility functions for filtering */
static int signature_matches(const git_signature *sig, const char *filter);
static int log_message_matches(const git_commit *commit, const char *filter);
int main(int argc, char *argv[])
{
@ -128,6 +132,15 @@ int main(int argc, char *argv[])
continue;
}
if (!signature_matches(git_commit_author(commit), opt.author))
continue;
if (!signature_matches(git_commit_committer(commit), opt.committer))
continue;
if (!log_message_matches(commit, opt.grep))
continue;
if (count++ < opt.skip)
continue;
if (opt.limit != -1 && printed++ >= opt.limit) {
@ -172,6 +185,32 @@ int main(int argc, char *argv[])
return 0;
}
/** Determine if the given git_signature does not contain the filter text. */
static int signature_matches(const git_signature *sig, const char *filter) {
if (filter == NULL)
return 1;
if (sig != NULL &&
(strstr(sig->name, filter) != NULL ||
strstr(sig->email, filter) != NULL))
return 1;
return 0;
}
static int log_message_matches(const git_commit *commit, const char *filter) {
const char *message = NULL;
if (filter == NULL)
return 1;
if ((message = git_commit_message(commit)) != NULL &&
strstr(message, filter) != NULL)
return 1;
return 0;
}
/** Push object (for hide or show) onto revwalker. */
static void push_rev(struct log_state *s, git_object *obj, int hide)
{
@ -401,6 +440,12 @@ static int parse_options(
set_sorting(s, GIT_SORT_TOPOLOGICAL);
else if (!strcmp(a, "--reverse"))
set_sorting(s, GIT_SORT_REVERSE);
else if (match_str_arg(&opt->author, &args, "--author"))
/** Found valid --author */;
else if (match_str_arg(&opt->committer, &args, "--committer"))
/** Found valid --committer */;
else if (match_str_arg(&opt->grep, &args, "--grep"))
/** Found valid --grep */;
else if (match_str_arg(&s->repodir, &args, "--git-dir"))
/** Found git-dir. */;
else if (match_int_arg(&opt->skip, &args, "--skip", 0))

View File

@ -22,7 +22,7 @@ static void print_progress(const progress_data *pd)
int index_percent = (100*pd->fetch_progress.indexed_objects) / pd->fetch_progress.total_objects;
int checkout_percent = pd->total_steps > 0
? (100 * pd->completed_steps) / pd->total_steps
: 0.f;
: 0;
int kbytes = pd->fetch_progress.received_bytes / 1024;
if (pd->fetch_progress.received_objects == pd->fetch_progress.total_objects) {
@ -62,7 +62,7 @@ int do_clone(git_repository *repo, int argc, char **argv)
progress_data pd = {{0}};
git_repository *cloned_repo = NULL;
git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
git_checkout_opts checkout_opts = GIT_CHECKOUT_OPTS_INIT;
git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
const char *url = argv[1];
const char *path = argv[2];
int error;

View File

@ -91,13 +91,13 @@ int fetch(git_repository *repo, int argc, char **argv)
// Figure out whether it's a named remote or a URL
printf("Fetching %s for repo %p\n", argv[1], repo);
if (git_remote_load(&remote, repo, argv[1]) < 0) {
if (git_remote_create_inmemory(&remote, repo, NULL, argv[1]) < 0)
if (git_remote_create_anonymous(&remote, repo, argv[1], NULL) < 0)
return -1;
}
// Set up the callbacks (only update_tips for now)
callbacks.update_tips = &update_cb;
callbacks.progress = &progress_cb;
callbacks.sideband_progress = &progress_cb;
callbacks.credentials = cred_acquire_cb;
git_remote_set_callbacks(remote, &callbacks);
@ -156,7 +156,7 @@ int fetch(git_repository *repo, int argc, char **argv)
// right commits. This may be needed even if there was no packfile
// to download, which can happen e.g. when the branches have been
// changed but all the neede objects are available locally.
if (git_remote_update_tips(remote) < 0)
if (git_remote_update_tips(remote, NULL, NULL) < 0)
return -1;
git_remote_free(remote);

View File

@ -15,7 +15,7 @@ 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);
error = git_remote_create_anonymous(&remote, repo, name, NULL);
if (error < 0)
goto cleanup;
}

View File

@ -49,7 +49,7 @@ int main (int argc, char** argv)
for (i = 0; i < ecount; ++i) {
const git_index_entry *e = git_index_get_byindex(index, i);
git_oid_fmt(out, &e->oid);
git_oid_fmt(out, &e->id);
printf("File Path: %s\n", e->path);
printf(" Stage: %d\n", git_index_entry_stage(e));

View File

@ -13,6 +13,12 @@
*/
#include "common.h"
#ifdef _WIN32
# include <Windows.h>
# define sleep(a) Sleep(a * 1000)
#else
# include <unistd.h>
#endif
/**
* This example demonstrates the use of the libgit2 status APIs,
@ -44,19 +50,22 @@ enum {
#define MAX_PATHSPEC 8
struct opts {
git_status_options statusopt;
char *repodir;
char *pathspec[MAX_PATHSPEC];
int npaths;
int format;
int zterm;
int showbranch;
git_status_options statusopt;
char *repodir;
char *pathspec[MAX_PATHSPEC];
int npaths;
int format;
int zterm;
int showbranch;
int showsubmod;
int repeat;
};
static void parse_opts(struct opts *o, int argc, char *argv[]);
static void show_branch(git_repository *repo, int format);
static void print_long(git_status_list *status);
static void print_short(git_repository *repo, git_status_list *status);
static int print_submod(git_submodule *sm, const char *name, void *payload);
int main(int argc, char *argv[])
{
@ -84,6 +93,10 @@ int main(int argc, char *argv[])
fatal("Cannot report status on bare repository",
git_repository_path(repo));
show_status:
if (o.repeat)
printf("\033[H\033[2J");
/**
* Run status on the repository
*
@ -98,17 +111,29 @@ int main(int argc, char *argv[])
* about what results are presented.
*/
check_lg2(git_status_list_new(&status, repo, &o.statusopt),
"Could not get status", NULL);
"Could not get status", NULL);
if (o.showbranch)
show_branch(repo, o.format);
if (o.showsubmod) {
int submod_count = 0;
check_lg2(git_submodule_foreach(repo, print_submod, &submod_count),
"Cannot iterate submodules", o.repodir);
}
if (o.format == FORMAT_LONG)
print_long(status);
else
print_short(repo, status);
git_status_list_free(status);
if (o.repeat) {
sleep(o.repeat);
goto show_status;
}
git_repository_free(repo);
git_threads_shutdown();
@ -363,22 +388,25 @@ static void print_short(git_repository *repo, git_status_list *status)
unsigned int smstatus = 0;
if (!git_submodule_lookup(
&sm, repo, s->index_to_workdir->new_file.path) &&
!git_submodule_status(&smstatus, sm))
{
if (smstatus & GIT_SUBMODULE_STATUS_WD_MODIFIED)
extra = " (new commits)";
else if (smstatus & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED)
extra = " (modified content)";
else if (smstatus & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED)
extra = " (modified content)";
else if (smstatus & GIT_SUBMODULE_STATUS_WD_UNTRACKED)
extra = " (untracked content)";
&sm, repo, s->index_to_workdir->new_file.path)) {
if (!git_submodule_status(&smstatus, sm)) {
if (smstatus & GIT_SUBMODULE_STATUS_WD_MODIFIED)
extra = " (new commits)";
else if (smstatus & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED)
extra = " (modified content)";
else if (smstatus & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED)
extra = " (modified content)";
else if (smstatus & GIT_SUBMODULE_STATUS_WD_UNTRACKED)
extra = " (untracked content)";
}
}
git_submodule_free(sm);
}
/**
* Now that we have all the information, it's time to format the output.
* Now that we have all the information, format the output.
*/
if (s->head_to_index) {
@ -414,6 +442,21 @@ static void print_short(git_repository *repo, git_status_list *status)
}
}
static int print_submod(git_submodule *sm, const char *name, void *payload)
{
int *count = payload;
(void)name;
if (*count == 0)
printf("# Submodules\n");
(*count)++;
printf("# - submodule '%s' at %s\n",
git_submodule_name(sm), git_submodule_path(sm));
return 0;
}
/**
* Parse options that git's status command supports.
*/
@ -459,6 +502,12 @@ static void parse_opts(struct opts *o, int argc, char *argv[])
o->statusopt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
else if (!strncmp(a, "--git-dir=", strlen("--git-dir=")))
o->repodir = a + strlen("--git-dir=");
else if (!strcmp(a, "--repeat"))
o->repeat = 10;
else if (match_int_arg(&o->repeat, &args, "--repeat", 0))
/* okay */;
else if (!strcmp(a, "--list-submodules"))
o->showsubmod = 1;
else
check_lg2(-1, "Unsupported option", a);
}

319
examples/tag.c Normal file
View File

@ -0,0 +1,319 @@
/*
* libgit2 "tag" example - shows how to list, create and delete tags
*
* Written by the libgit2 contributors
*
* To the extent possible under law, the author(s) have dedicated all copyright
* and related and neighboring rights to this software to the public domain
* worldwide. This software is distributed without any warranty.
*
* You should have received a copy of the CC0 Public Domain Dedication along
* with this software. If not, see
* <http://creativecommons.org/publicdomain/zero/1.0/>.
*/
#include "common.h"
/**
* The following example partially reimplements the `git tag` command
* and some of its options.
*
* These commands should work:
* - Tag name listing (`tag`)
* - Filtered tag listing with messages (`tag -n3 -l "v0.1*"`)
* - Lightweight tag creation (`tag test v0.18.0`)
* - Tag creation (`tag -a -m "Test message" test v0.18.0`)
* - Tag deletion (`tag -d test`)
*
* The command line parsing logic is simplified and doesn't handle
* all of the use cases.
*/
/** tag_options represents the parsed command line options */
typedef struct {
const char *message;
const char *pattern;
const char *tag_name;
const char *target;
int num_lines;
int force;
} tag_options;
/** tag_state represents the current program state for dragging around */
typedef struct {
git_repository *repo;
tag_options *opts;
} tag_state;
/** An action to execute based on the command line arguments */
typedef void (*tag_action)(tag_state *state);
typedef struct args_info args_info;
static void check(int result, const char *message)
{
if (result) fatal(message, NULL);
}
/** Tag listing: Print individual message lines */
static void print_list_lines(const char *message, const tag_state *state)
{
const char *msg = message;
int num = state->opts->num_lines - 1;
if (!msg) return;
/** first line - headline */
while(*msg && *msg != '\n') printf("%c", *msg++);
/** skip over new lines */
while(*msg && *msg == '\n') msg++;
printf("\n");
/** print just headline? */
if (num == 0) return;
if (*msg && msg[1]) printf("\n");
/** print individual commit/tag lines */
while (*msg && num-- >= 2) {
printf(" ");
while (*msg && *msg != '\n') printf("%c", *msg++);
/** handle consecutive new lines */
if (*msg && *msg == '\n' && msg[1] == '\n') {
num--;
printf("\n");
}
while(*msg && *msg == '\n') msg++;
printf("\n");
}
}
/** Tag listing: Print an actual tag object */
static void print_tag(git_tag *tag, const tag_state *state)
{
printf("%-16s", git_tag_name(tag));
if (state->opts->num_lines) {
const char *msg = git_tag_message(tag);
print_list_lines(msg, state);
} else {
printf("\n");
}
}
/** Tag listing: Print a commit (target of a lightweight tag) */
static void print_commit(git_commit *commit, const char *name,
const tag_state *state)
{
printf("%-16s", name);
if (state->opts->num_lines) {
const char *msg = git_commit_message(commit);
print_list_lines(msg, state);
} else {
printf("\n");
}
}
/** Tag listing: Fallback, should not happen */
static void print_name(const char *name)
{
printf("%s\n", name);
}
/** Tag listing: Lookup tags based on ref name and dispatch to print */
static int each_tag(const char *name, tag_state *state)
{
git_repository *repo = state->repo;
git_object *obj;
check_lg2(git_revparse_single(&obj, repo, name),
"Failed to lookup rev", name);
switch (git_object_type(obj)) {
case GIT_OBJ_TAG:
print_tag((git_tag *) obj, state);
break;
case GIT_OBJ_COMMIT:
print_commit((git_commit *) obj, name, state);
break;
default:
print_name(name);
}
git_object_free(obj);
return 0;
}
static void action_list_tags(tag_state *state)
{
const char *pattern = state->opts->pattern;
git_strarray tag_names = {0};
size_t i;
check_lg2(git_tag_list_match(&tag_names, pattern ? pattern : "*", state->repo),
"Unable to get list of tags", NULL);
for(i = 0; i < tag_names.count; i++) {
each_tag(tag_names.strings[i], state);
}
git_strarray_free(&tag_names);
}
static void action_delete_tag(tag_state *state)
{
tag_options *opts = state->opts;
git_object *obj;
git_buf abbrev_oid = {0};
check(!opts->tag_name, "Name required");
check_lg2(git_revparse_single(&obj, state->repo, opts->tag_name),
"Failed to lookup rev", opts->tag_name);
check_lg2(git_object_short_id(&abbrev_oid, obj),
"Unable to get abbreviated OID", opts->tag_name);
check_lg2(git_tag_delete(state->repo, opts->tag_name),
"Unable to delete tag", opts->tag_name);
printf("Deleted tag '%s' (was %s)\n", opts->tag_name, abbrev_oid.ptr);
git_buf_free(&abbrev_oid);
git_object_free(obj);
}
static void action_create_lighweight_tag(tag_state *state)
{
git_repository *repo = state->repo;
tag_options *opts = state->opts;
git_oid oid;
git_object *target;
check(!opts->tag_name, "Name required");
if (!opts->target) opts->target = "HEAD";
check(!opts->target, "Target required");
check_lg2(git_revparse_single(&target, repo, opts->target),
"Unable to resolve spec", opts->target);
check_lg2(git_tag_create_lightweight(&oid, repo, opts->tag_name,
target, opts->force), "Unable to create tag", NULL);
git_object_free(target);
}
static void action_create_tag(tag_state *state)
{
git_repository *repo = state->repo;
tag_options *opts = state->opts;
git_signature *tagger;
git_oid oid;
git_object *target;
check(!opts->tag_name, "Name required");
check(!opts->message, "Message required");
if (!opts->target) opts->target = "HEAD";
check_lg2(git_revparse_single(&target, repo, opts->target),
"Unable to resolve spec", opts->target);
check_lg2(git_signature_default(&tagger, repo),
"Unable to create signature", NULL);
check_lg2(git_tag_create(&oid, repo, opts->tag_name,
target, tagger, opts->message, opts->force), "Unable to create tag", NULL);
git_object_free(target);
git_signature_free(tagger);
}
static void print_usage()
{
fprintf(stderr, "usage: see `git help tag`\n");
exit(1);
}
/** Parse command line arguments and choose action to run when done */
static void parse_options(tag_action *action, tag_options *opts, int argc, char **argv)
{
args_info args = ARGS_INFO_INIT;
*action = &action_list_tags;
for (args.pos = 1; args.pos < argc; ++args.pos) {
const char *curr = argv[args.pos];
if (curr[0] != '-') {
if (!opts->tag_name)
opts->tag_name = curr;
else if (!opts->target)
opts->target = curr;
else
print_usage();
if (*action != &action_create_tag)
*action = &action_create_lighweight_tag;
} else if (!strcmp(curr, "-n")) {
opts->num_lines = 1;
*action = &action_list_tags;
} else if (!strcmp(curr, "-a")) {
*action = &action_create_tag;
} else if (!strcmp(curr, "-f")) {
opts->force = 1;
} else if (match_int_arg(&opts->num_lines, &args, "-n", 0)) {
*action = &action_list_tags;
} else if (match_str_arg(&opts->pattern, &args, "-l")) {
*action = &action_list_tags;
} else if (match_str_arg(&opts->tag_name, &args, "-d")) {
*action = &action_delete_tag;
} else if (match_str_arg(&opts->message, &args, "-m")) {
*action = &action_create_tag;
}
}
}
/** Initialize tag_options struct */
static void tag_options_init(tag_options *opts)
{
memset(opts, 0, sizeof(*opts));
opts->message = NULL;
opts->pattern = NULL;
opts->tag_name = NULL;
opts->target = NULL;
opts->num_lines = 0;
opts->force = 0;
}
int main(int argc, char **argv)
{
git_repository *repo;
tag_options opts;
tag_action action;
tag_state state;
git_threads_init();
check_lg2(git_repository_open_ext(&repo, ".", 0, NULL),
"Could not open repository", NULL);
tag_options_init(&opts);
parse_options(&action, &opts, argc, argv);
state.repo = repo;
state.opts = &opts;
action(&state);
git_repository_free(repo);
git_threads_shutdown();
return 0;
}

View File

@ -28,9 +28,6 @@
# his/her consent on a patch-by-patch basis.
# "???" means the person is a prominent contributor who has
# not yet made his/her standpoint clear.
# "ign" means the authors consent is ignored for the purpose
# of libification. This is because the author has contributed
# to areas that aren't interesting for the library.
#
# Please try to keep the list alphabetically ordered. It will
# help in case we get all 600-ish git.git authors on it.
@ -39,33 +36,39 @@
# but has otherwise not contributed to git.)
#
ok Adam Simpkins <adam@adamsimpkins.net> (http transport)
ok Adrian Johnson <ajohnson@redneon.com>
ok Alexey Shumkin <alex.crezoff@gmail.com>
ok Andreas Ericsson <ae@op5.se>
ok Boyd Lynn Gerber <gerberb@zenez.com>
ok Brandon Casey <drafnel@gmail.com>
ok Brian Downing <bdowning@lavos.net>
ok Brian Gernhardt <benji@silverinsanity.com>
ok Christian Couder <chriscool@tuxfamily.org>
ok Daniel Barkalow <barkalow@iabervon.org>
ok Florian Forster <octo@verplant.org>
ok Gustaf Hendeby <hendeby@isy.liu.se>
ok Holger Weiss <holger@zedat.fu-berlin.de>
ok Jeff King <peff@peff.net>
ok Johannes Schindelin <Johannes.Schindelin@gmx.de>
ok Johannes Sixt <j6t@kdbg.org>
ask Jonathan Nieder <jrnieder@gmail.com>
ok Junio C Hamano <gitster@pobox.com>
ok Kristian Høgsberg <krh@redhat.com>
ok Linus Torvalds <torvalds@linux-foundation.org>
ok Lukas Sandström <lukass@etek.chalmers.se>
ok Matthieu Moy <Matthieu.Moy@imag.fr>
ign Mike McCormack <mike@codeweavers.com> (imap-send)
ok Michael Haggerty <mhagger@alum.mit.edu>
ok Nicolas Pitre <nico@fluxnic.net> <nico@cam.org>
ok Paolo Bonzini <bonzini@gnu.org>
ok Paul Kocher <paul@cryptography.com>
ok Peter Hagervall <hager@cs.umu.se>
ok Petr Onderka <gsvick@gmail.com>
ok Pierre Habouzit <madcoder@debian.org>
ok Pieter de Bie <pdebie@ai.rug.nl>
ok René Scharfe <rene.scharfe@lsrfire.ath.cx>
ign Robert Shearman <rob@codeweavers.com> (imap-send)
ok Sebastian Schuberth <sschuberth@gmail.com>
ok Shawn O. Pearce <spearce@spearce.org>
ok Steffen Prohaska <prohaska@zib.de>
ok Sven Verdoolaege <skimo@kotnet.org>
ask Thomas Rast <tr@thomasrast.ch> (ok before 6-Oct-2013)
ok Torsten Bögershausen <tboegi@web.de>

View File

@ -14,6 +14,7 @@
#include "git2/branch.h"
#include "git2/buffer.h"
#include "git2/checkout.h"
#include "git2/cherrypick.h"
#include "git2/clone.h"
#include "git2/commit.h"
#include "git2/common.h"
@ -43,6 +44,7 @@
#include "git2/remote.h"
#include "git2/repository.h"
#include "git2/reset.h"
#include "git2/revert.h"
#include "git2/revparse.h"
#include "git2/revwalk.h"
#include "git2/signature.h"

View File

@ -199,8 +199,9 @@ typedef int (*git_attr_foreach_cb)(const char *name, const char *value, void *pa
* only once per attribute name, even if there are multiple
* rules for a given file. The highest priority rule will be
* used. Return a non-zero value from this to stop looping.
* The value will be returned from `git_attr_foreach`.
* @param payload Passed on as extra parameter to callback function.
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_attr_foreach(
git_repository *repo,

View File

@ -40,6 +40,9 @@ typedef enum {
* commit (like `git blame -CCC`). Implies SAME_COMMIT_COPIES.
* NOT IMPLEMENTED. */
GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = (1<<3),
/** Restrict the search of commits to those reachable following only the
* first parents. */
GIT_BLAME_FIRST_PARENT = (1<<4),
} git_blame_flag_t;
/**
@ -79,6 +82,18 @@ typedef struct git_blame_options {
#define GIT_BLAME_OPTIONS_VERSION 1
#define GIT_BLAME_OPTIONS_INIT {GIT_BLAME_OPTIONS_VERSION}
/**
* Initializes a `git_blame_options` with default values. Equivalent to
* creating an instance with GIT_BLAME_OPTIONS_INIT.
*
* @param opts The `git_blame_options` struct to initialize
* @param version Version of struct; pass `GIT_BLAME_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_blame_init_options(
git_blame_options *opts,
unsigned int version);
/**
* Structure that represents a blame hunk.
*
@ -183,7 +198,7 @@ GIT_EXTERN(int) git_blame_buffer(
git_blame **out,
git_blame *reference,
const char *buffer,
uint32_t buffer_len);
size_t buffer_len);
/**
* Free memory allocated by git_blame_file or git_blame_buffer.

View File

@ -84,7 +84,7 @@ GIT_EXTERN(git_repository *) git_blob_owner(const git_blob *blob);
* time.
*
* @param blob pointer to the blob
* @return the pointer; NULL if the blob has no contents
* @return the pointer
*/
GIT_EXTERN(const void *) git_blob_rawcontent(const git_blob *blob);
@ -159,37 +159,32 @@ typedef int (*git_blob_chunk_cb)(char *content, size_t max_length, void *payload
* Write a loose blob to the Object Database from a
* provider of chunks of data.
*
* Provided the `hintpath` parameter is filled, its value
* will help to determine what git filters should be applied
* to the object before it can be placed to the object database.
* If the `hintpath` parameter is filled, it will be used to determine
* what git filters should be applied to the object before it is written
* to the object database.
*
* The implementation of the callback MUST respect the following rules:
*
* The implementation of the callback has to respect the
* following rules:
* - `content` must be filled by the callback. The maximum number of
* bytes that the buffer can accept per call is defined by the
* `max_length` parameter. Allocation and freeing of the buffer will
* be taken care of by libgit2.
*
* - `content` will have to be filled by the consumer. The maximum number
* of bytes that the buffer can accept per call is defined by the
* `max_length` parameter. Allocation and freeing of the buffer will be taken
* care of by the function.
* - The `callback` must return the number of bytes that have been
* written to the `content` buffer.
*
* - The callback is expected to return the number of bytes
* that `content` have been filled with.
*
* - When there is no more data to stream, the callback should
* return 0. This will prevent it from being invoked anymore.
*
* - When an error occurs, the callback should return -1.
* - When there is no more data to stream, `callback` should return
* 0. This will prevent it from being invoked anymore.
*
* - If an error occurs, the callback should return a negative value.
* This value will be returned to the caller.
*
* @param id Return the id of the written blob
*
* @param repo repository where the blob will be written.
* This repository can be bare or not.
*
* @param hintpath if not NULL, will help selecting the filters
* to apply onto the content of the blob to be created.
*
* @return 0 or an error code
* @param repo Repository where the blob will be written.
* This repository can be bare or not.
* @param hintpath If not NULL, will be used to select data filters
* to apply onto the content of the blob to be created.
* @return 0 or error code (from either libgit2 or callback function)
*/
GIT_EXTERN(int) git_blob_create_fromchunks(
git_oid *id,
@ -201,26 +196,27 @@ GIT_EXTERN(int) git_blob_create_fromchunks(
/**
* Write an in-memory buffer to the ODB as a blob
*
* @param oid return the oid of the written blob
* @param id return the id of the written blob
* @param repo repository where to blob will be written
* @param buffer data to be written into the blob
* @param len length of the data
* @return 0 or an error code
*/
GIT_EXTERN(int) git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len);
GIT_EXTERN(int) git_blob_create_frombuffer(
git_oid *id, git_repository *repo, const void *buffer, size_t len);
/**
* Determine if the blob content is most certainly binary or not.
*
* The heuristic used to guess if a file is binary is taken from core git:
* Searching for NUL bytes and looking for a reasonable ratio of printable
* to non-printable characters among the first 4000 bytes.
* to non-printable characters among the first 8000 bytes.
*
* @param blob The blob which content should be analyzed
* @return 1 if the content of the blob is detected
* as binary; 0 otherwise.
*/
GIT_EXTERN(int) git_blob_is_binary(git_blob *blob);
GIT_EXTERN(int) git_blob_is_binary(const git_blob *blob);
/** @} */
GIT_END_DECL

View File

@ -43,6 +43,12 @@ GIT_BEGIN_DECL
*
* @param force Overwrite existing branch.
*
* @param signature The identity that will used to populate the reflog entry
*
* @param log_message The one line long message to be appended to the reflog.
* If NULL, the default is "Branch: created"; if you want something more
* useful, provide a message.
*
* @return 0, GIT_EINVALIDSPEC or an error code.
* A proper reference is written in the refs/heads namespace
* pointing to the provided target commit.
@ -52,7 +58,9 @@ GIT_EXTERN(int) git_branch_create(
git_repository *repo,
const char *branch_name,
const git_commit *target,
int force);
int force,
const git_signature *signature,
const char *log_message);
/**
* Delete an existing branch reference.
@ -76,7 +84,7 @@ typedef struct git_branch_iterator git_branch_iterator;
* @param repo Repository where to find the branches.
* @param list_flags Filtering flags for the branch
* listing. Valid values are GIT_BRANCH_LOCAL, GIT_BRANCH_REMOTE
* or a combination of the two.
* or GIT_BRANCH_ALL.
*
* @return 0 on success or an error code
*/
@ -115,13 +123,19 @@ GIT_EXTERN(void) git_branch_iterator_free(git_branch_iterator *iter);
*
* @param force Overwrite existing branch.
*
* @param signature The identity that will used to populate the reflog entry
*
* @param log_message The one line long message to be appended to the reflog
*
* @return 0 on success, GIT_EINVALIDSPEC or an error code.
*/
GIT_EXTERN(int) git_branch_move(
git_reference **out,
git_reference *branch,
const char *new_branch_name,
int force);
int force,
const git_signature *signature,
const char *log_message);
/**
* Lookup a branch by its name in a repository.
@ -165,8 +179,9 @@ GIT_EXTERN(int) git_branch_lookup(
* @return 0 on success; otherwise an error code (e.g., if the
* ref is no local or remote branch).
*/
GIT_EXTERN(int) git_branch_name(const char **out,
git_reference *ref);
GIT_EXTERN(int) git_branch_name(
const char **out,
const git_reference *ref);
/**
* Return the reference supporting the remote tracking branch,
@ -182,7 +197,7 @@ GIT_EXTERN(int) git_branch_name(const char **out,
*/
GIT_EXTERN(int) git_branch_upstream(
git_reference **out,
git_reference *branch);
const git_reference *branch);
/**
* Set the upstream configuration for a given local branch
@ -200,25 +215,20 @@ GIT_EXTERN(int) git_branch_set_upstream(git_reference *branch, const char *upstr
* Return the name of the reference supporting the remote tracking branch,
* given the name of a local branch reference.
*
* @param tracking_branch_name_out The user-allocated buffer which will be
* filled with the name of the reference. Pass NULL if you just want to
* get the needed size of the name of the reference as the output value.
*
* @param buffer_size Size of the `out` buffer in bytes.
* @param out Pointer to the user-allocated git_buf which will be
* filled with the name of the reference.
*
* @param repo the repository where the branches live
*
* @param canonical_branch_name name of the local branch.
* @param refname reference name of the local branch.
*
* @return number of characters in the reference name
* including the trailing NUL byte; GIT_ENOTFOUND when no remote tracking
* reference exists, otherwise an error code.
* @return 0, GIT_ENOTFOUND when no remote tracking reference exists,
* otherwise an error code.
*/
GIT_EXTERN(int) git_branch_upstream_name(
char *tracking_branch_name_out,
size_t buffer_size,
git_buf *out,
git_repository *repo,
const char *canonical_branch_name);
const char *refname);
/**
* Determine if the current local branch is pointed at by HEAD.
@ -229,30 +239,24 @@ GIT_EXTERN(int) git_branch_upstream_name(
* error code otherwise.
*/
GIT_EXTERN(int) git_branch_is_head(
git_reference *branch);
const git_reference *branch);
/**
* Return the name of remote that the remote tracking branch belongs to.
*
* @param remote_name_out The user-allocated buffer which will be
* filled with the name of the remote. Pass NULL if you just want to
* get the needed size of the name of the remote as the output value.
*
* @param buffer_size Size of the `out` buffer in bytes.
* @param out Pointer to the user-allocated git_buf which will be filled iwth the name of the remote.
*
* @param repo The repository where the branch lives.
*
* @param canonical_branch_name name of the remote tracking branch.
*
* @return Number of characters in the reference name
* including the trailing NUL byte; GIT_ENOTFOUND
* @return 0, GIT_ENOTFOUND
* when no remote matching remote was found,
* GIT_EAMBIGUOUS when the branch maps to several remotes,
* otherwise an error code.
*/
GIT_EXTERN(int) git_branch_remote_name(
char *remote_name_out,
size_t buffer_size,
git_buf *out,
git_repository *repo,
const char *canonical_branch_name);

View File

@ -43,17 +43,17 @@ GIT_BEGIN_DECL
* In between those are `GIT_CHECKOUT_SAFE` and `GIT_CHECKOUT_SAFE_CREATE`
* both of which only make modifications that will not lose changes.
*
* | target == baseline | target != baseline |
* ---------------------|-----------------------|----------------------|
* workdir == baseline | no action | create, update, or |
* | | delete file |
* ---------------------|-----------------------|----------------------|
* workdir exists and | no action | conflict (notify |
* is != baseline | notify dirty MODIFIED | and cancel checkout) |
* ---------------------|-----------------------|----------------------|
* workdir missing, | create if SAFE_CREATE | create file |
* baseline present | notify dirty DELETED | |
* ---------------------|-----------------------|----------------------|
* | target == baseline | target != baseline |
* ---------------------|-----------------------|----------------------|
* workdir == baseline | no action | create, update, or |
* | | delete file |
* ---------------------|-----------------------|----------------------|
* workdir exists and | no action | conflict (notify |
* is != baseline | notify dirty MODIFIED | and cancel checkout) |
* ---------------------|-----------------------|----------------------|
* workdir missing, | create if SAFE_CREATE | create file |
* baseline present | notify dirty DELETED | |
* ---------------------|-----------------------|----------------------|
*
* The only difference between SAFE and SAFE_CREATE is that SAFE_CREATE
* will cause a file to be checked out if it is missing from the working
@ -99,9 +99,14 @@ GIT_BEGIN_DECL
* files with unmerged index entries instead. GIT_CHECKOUT_USE_OURS and
* GIT_CHECKOUT_USE_THEIRS to proceed with the checkout using either the
* stage 2 ("ours") or stage 3 ("theirs") version of files in the index.
*
* - GIT_CHECKOUT_DONT_OVERWRITE_IGNORED prevents ignored files from being
* overwritten. Normally, files that are ignored in the working directory
* are not considered "precious" and may be overwritten if the checkout
* target contains that file.
*/
typedef enum {
GIT_CHECKOUT_NONE = 0, /** default is a dry run, no actual updates */
GIT_CHECKOUT_NONE = 0, /**< default is a dry run, no actual updates */
/** Allow safe updates that cannot overwrite uncommitted data */
GIT_CHECKOUT_SAFE = (1u << 0),
@ -144,6 +149,15 @@ typedef enum {
/** Ignore directories in use, they will be left empty */
GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES = (1u << 18),
/** Don't overwrite ignored files that exist in the checkout target */
GIT_CHECKOUT_DONT_OVERWRITE_IGNORED = (1u << 19),
/** Write normal merge files for conflicts */
GIT_CHECKOUT_CONFLICT_STYLE_MERGE = (1u << 20),
/** Include common ancestor data in diff3 format files for conflicts */
GIT_CHECKOUT_CONFLICT_STYLE_DIFF3 = (1u << 21),
/**
* THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
*/
@ -174,7 +188,12 @@ typedef enum {
* - GIT_CHECKOUT_NOTIFY_IGNORED notifies about ignored files.
*
* Returning a non-zero value from this callback will cancel the checkout.
* Notification callbacks are made prior to modifying any files on disk.
* The non-zero return value will be propagated back and returned by the
* git_checkout_... call.
*
* Notification callbacks are made prior to modifying any files on disk,
* so canceling on any notification will still happen prior to any files
* being modified.
*/
typedef enum {
GIT_CHECKOUT_NOTIFY_NONE = 0,
@ -206,12 +225,12 @@ typedef void (*git_checkout_progress_cb)(
/**
* Checkout options structure
*
* Zero out for defaults. Initialize with `GIT_CHECKOUT_OPTS_INIT` macro to
* Zero out for defaults. Initialize with `GIT_CHECKOUT_OPTIONS_INIT` macro to
* correctly set the `version` field. E.g.
*
* git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
* git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
*/
typedef struct git_checkout_opts {
typedef struct git_checkout_options {
unsigned int version;
unsigned int checkout_strategy; /** default will be a dry run */
@ -239,12 +258,25 @@ typedef struct git_checkout_opts {
const char *target_directory; /** alternative checkout path to workdir */
const char *ancestor_label; /** the name of the common ancestor side of conflicts */
const char *our_label; /** the name of the "our" side of conflicts */
const char *their_label; /** the name of the "their" side of conflicts */
} git_checkout_opts;
} git_checkout_options;
#define GIT_CHECKOUT_OPTS_VERSION 1
#define GIT_CHECKOUT_OPTS_INIT {GIT_CHECKOUT_OPTS_VERSION}
#define GIT_CHECKOUT_OPTIONS_VERSION 1
#define GIT_CHECKOUT_OPTIONS_INIT {GIT_CHECKOUT_OPTIONS_VERSION}
/**
* Initializes a `git_checkout_options` with default values. Equivalent to
* creating an instance with GIT_CHECKOUT_OPTIONS_INIT.
*
* @param opts the `git_checkout_options` struct to initialize.
* @param version Version of struct; pass `GIT_CHECKOUT_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_checkout_init_options(
git_checkout_options *opts,
unsigned int version);
/**
* Updates files in the index and the working tree to match the content of
@ -252,13 +284,13 @@ typedef struct git_checkout_opts {
*
* @param repo repository to check out (must be non-bare)
* @param opts specifies checkout options (may be NULL)
* @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing
* branch, GIT_ERROR otherwise (use giterr_last for information
* about the error)
* @return 0 on success, GIT_EUNBORNBRANCH if HEAD points to a non
* existing branch, non-zero value returned by `notify_cb`, or
* other error code < 0 (use giterr_last for error details)
*/
GIT_EXTERN(int) git_checkout_head(
git_repository *repo,
const git_checkout_opts *opts);
const git_checkout_options *opts);
/**
* Updates files in the working tree to match the content of the index.
@ -266,13 +298,13 @@ GIT_EXTERN(int) git_checkout_head(
* @param repo repository into which to check out (must be non-bare)
* @param index index to be checked out (or NULL to use repository index)
* @param opts specifies checkout options (may be NULL)
* @return 0 on success, GIT_ERROR otherwise (use giterr_last for information
* about the error)
* @return 0 on success, non-zero return value from `notify_cb`, or error
* code < 0 (use giterr_last for error details)
*/
GIT_EXTERN(int) git_checkout_index(
git_repository *repo,
git_index *index,
const git_checkout_opts *opts);
const git_checkout_options *opts);
/**
* Updates files in the index and working tree to match the content of the
@ -282,13 +314,13 @@ GIT_EXTERN(int) git_checkout_index(
* @param treeish a commit, tag or tree which content will be used to update
* the working directory (or NULL to use HEAD)
* @param opts specifies checkout options (may be NULL)
* @return 0 on success, GIT_ERROR otherwise (use giterr_last for information
* about the error)
* @return 0 on success, non-zero return value from `notify_cb`, or error
* code < 0 (use giterr_last for error details)
*/
GIT_EXTERN(int) git_checkout_tree(
git_repository *repo,
const git_object *treeish,
const git_checkout_opts *opts);
const git_checkout_options *opts);
/** @} */
GIT_END_DECL

87
include/git2/cherrypick.h Normal file
View File

@ -0,0 +1,87 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_git_cherrypick_h__
#define INCLUDE_git_cherrypick_h__
#include "common.h"
#include "types.h"
#include "merge.h"
/**
* @file git2/cherrypick.h
* @brief Git cherry-pick routines
* @defgroup git_cherrypick Git cherry-pick routines
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
typedef struct {
unsigned int version;
/** For merge commits, the "mainline" is treated as the parent. */
unsigned int mainline;
git_merge_options merge_opts;
git_checkout_options checkout_opts;
} git_cherry_pick_options;
#define GIT_CHERRY_PICK_OPTIONS_VERSION 1
#define GIT_CHERRY_PICK_OPTIONS_INIT {GIT_CHERRY_PICK_OPTIONS_VERSION, 0, GIT_MERGE_OPTIONS_INIT, GIT_CHECKOUT_OPTIONS_INIT}
/**
* Initializes a `git_cherry_pick_options` with default values. Equivalent to
* creating an instance with GIT_CHERRY_PICK_OPTIONS_INIT.
*
* @param opts the `git_cherry_pick_options` struct to initialize
* @param version Version of struct; pass `GIT_CHERRY_PICK_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_cherry_pick_init_options(
git_cherry_pick_options *opts,
unsigned int version);
/**
* Cherry-picks the given commit against the given "our" commit, producing an
* index that reflects the result of the cherry-pick.
*
* The returned index must be freed explicitly with `git_index_free`.
*
* @param out pointer to store the index result in
* @param repo the repository that contains the given commits
* @param cherry_pick_commit the commit to cherry-pick
* @param our_commit the commit to revert against (eg, HEAD)
* @param mainline the parent of the revert commit, if it is a merge
* @param merge_options the merge options (or null for defaults)
* @return zero on success, -1 on failure.
*/
GIT_EXTERN(int) git_cherry_pick_commit(
git_index **out,
git_repository *repo,
git_commit *cherry_pick_commit,
git_commit *our_commit,
unsigned int mainline,
const git_merge_options *merge_options);
/**
* Cherry-pick the given commit, producing changes in the index and working directory.
*
* @param repo the repository to cherry-pick
* @param commit the commit to cherry-pick
* @param cherry_pick_options the cherry-pick options (or null for defaults)
* @return zero on success, -1 on failure.
*/
GIT_EXTERN(int) git_cherry_pick(
git_repository *repo,
git_commit *commit,
const git_cherry_pick_options *cherry_pick_options);
/** @} */
GIT_END_DECL
#endif

View File

@ -23,42 +23,109 @@
*/
GIT_BEGIN_DECL
/**
* Options for bypassing the git-aware transport on clone. Bypassing
* it means that instead of a fetch, libgit2 will copy the object
* database directory instead of figuring out what it needs, which is
* faster. If possible, it will hardlink the files to save space.
*/
typedef enum {
/**
* Auto-detect (default), libgit2 will bypass the git-aware
* transport for local paths, but use a normal fetch for
* `file://` urls.
*/
GIT_CLONE_LOCAL_AUTO,
/**
* Bypass the git-aware transport even for a `file://` url.
*/
GIT_CLONE_LOCAL,
/**
* Do no bypass the git-aware transport
*/
GIT_CLONE_NO_LOCAL,
/**
* Bypass the git-aware transport, but do not try to use
* hardlinks.
*/
GIT_CLONE_LOCAL_NO_LINKS,
} git_clone_local_t;
/**
* Clone options structure
*
* Use zeros to indicate default settings. It's easiest to use the
* `GIT_CLONE_OPTIONS_INIT` macro:
* Use the GIT_CLONE_OPTIONS_INIT to get the default settings, like this:
*
* git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
*
* - `checkout_opts` is options for the checkout step. To disable checkout,
* set the `checkout_strategy` to GIT_CHECKOUT_DEFAULT.
* - `bare` should be set to zero to create a standard repo, non-zero for
* a bare repo
* - `ignore_cert_errors` should be set to 1 if errors validating the remote host's
* certificate should be ignored.
*
* ** "origin" remote options: **
* - `remote_name` is the name given to the "origin" remote. The default is
* "origin".
* - `checkout_branch` gives the name of the branch to checkout. NULL means
* use the remote's HEAD.
*/
typedef struct git_clone_options {
unsigned int version;
git_checkout_opts checkout_opts;
/**
* These options are passed to the checkout step. To disable
* checkout, set the `checkout_strategy` to
* `GIT_CHECKOUT_NONE`. Generally you will want the use
* GIT_CHECKOUT_SAFE_CREATE to create all files in the working
* directory for the newly cloned repository.
*/
git_checkout_options checkout_opts;
/**
* Callbacks to use for reporting fetch progress.
*/
git_remote_callbacks remote_callbacks;
/**
* Set to zero (false) to create a standard repo, or non-zero
* for a bare repo
*/
int bare;
/**
* Set to 1 if errors validating the remote host's certificate
* should be ignored.
*/
int ignore_cert_errors;
/**
* Whether to use a fetch or copy the object database.
*/
git_clone_local_t local;
/**
* The name to be given to the remote that will be
* created. The default is "origin".
*/
const char *remote_name;
/**
* The name of the branch to checkout. NULL means use the
* remote's default branch.
*/
const char* checkout_branch;
/**
* The identity used when updating the reflog. NULL means to
* use the default signature using the config.
*/
git_signature *signature;
} git_clone_options;
#define GIT_CLONE_OPTIONS_VERSION 1
#define GIT_CLONE_OPTIONS_INIT {GIT_CLONE_OPTIONS_VERSION, {GIT_CHECKOUT_OPTS_VERSION, GIT_CHECKOUT_SAFE_CREATE}, GIT_REMOTE_CALLBACKS_INIT}
#define GIT_CLONE_OPTIONS_INIT {GIT_CLONE_OPTIONS_VERSION, {GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE_CREATE}, GIT_REMOTE_CALLBACKS_INIT}
/**
* Initializes a `git_clone_options` with default values. Equivalent to
* creating an instance with GIT_CLONE_OPTIONS_INIT.
*
* @param opts The `git_clone_options` struct to initialize
* @param version Version of struct; pass `GIT_CLONE_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_clone_init_options(
git_clone_options *opts,
unsigned int version);
/**
* Clone a remote repository.
@ -70,16 +137,17 @@ typedef struct git_clone_options {
* @param out pointer that will receive the resulting repository object
* @param url the remote repository to clone
* @param local_path local directory to clone to
* @param options configuration options for the clone. If NULL, the function
* works as though GIT_OPTIONS_INIT were passed.
* @return 0 on success, GIT_ERROR otherwise (use giterr_last for information
* about the error)
* @param options configuration options for the clone. If NULL, the
* function works as though GIT_OPTIONS_INIT were passed.
* @return 0 on success, any non-zero return value from a callback
* function, or a negative value to indicate an error (use
* `giterr_last` for a detailed error message)
*/
GIT_EXTERN(int) git_clone(
git_repository **out,
const char *url,
const char *local_path,
const git_clone_options *options);
git_repository **out,
const char *url,
const char *local_path,
const git_clone_options *options);
/**
* Clone into a repository
@ -91,11 +159,48 @@ GIT_EXTERN(int) git_clone(
* @param repo the repository to use
* @param remote the remote repository to clone from
* @param co_opts options to use during checkout
* @param branch the branch to checkout after the clone, pass NULL for the remote's
* default branch
* @return 0 on success or an error code
* @param branch the branch to checkout after the clone, pass NULL for the
* remote's default branch
* @param signature The identity used when updating the reflog.
* @return 0 on success, any non-zero return value from a callback
* function, or a negative value to indicate an error (use
* `giterr_last` for a detailed error message)
*/
GIT_EXTERN(int) git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch);
GIT_EXTERN(int) git_clone_into(
git_repository *repo,
git_remote *remote,
const git_checkout_options *co_opts,
const char *branch,
const git_signature *signature);
/**
* Perform a local clone into a repository
*
* A "local clone" bypasses any git-aware protocols and simply copies
* over the object database from the source repository. It is often
* faster than a git-aware clone, but no verification of the data is
* performed, and can copy over too much data.
*
* @param repo the repository to use
* @param remote the remote repository to clone from
* @param co_opts options to use during checkout
* @param branch the branch to checkout after the clone, pass NULL for the
* remote's default branch
* @param link wether to use hardlinks instead of copying
* objects. This is only possible if both repositories are on the same
* filesystem.
* @param signature the identity used when updating the reflog
* @return 0 on success, any non-zero return value from a callback
* function, or a negative value to indicate an error (use
* `giterr_last` for a detailed error message)
*/
GIT_EXTERN(int) git_clone_local_into(
git_repository *repo,
git_remote *remote,
const git_checkout_options *co_opts,
const char *branch,
int link,
const git_signature *signature);
/** @} */
GIT_END_DECL

View File

@ -116,6 +116,17 @@ GIT_EXTERN(const char *) git_commit_message(const git_commit *commit);
*/
GIT_EXTERN(const char *) git_commit_message_raw(const git_commit *commit);
/**
* Get the short "summary" of the git commit message.
*
* The returned message is the summary of the commit, comprising the
* first paragraph of the message with whitespace trimmed and squashed.
*
* @param commit a previously loaded commit.
* @return the summary of a commit or NULL on error
*/
GIT_EXTERN(const char *) git_commit_summary(git_commit *commit);
/**
* Get the commit time (i.e. committer time) of a commit.
*
@ -231,8 +242,8 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor(
/**
* Create new commit in the repository from a list of `git_object` pointers
*
* The message will not be cleaned up automatically. You can do that with
* the `git_message_prettify()` function.
* The message will **not** be cleaned up automatically. You can do that
* with the `git_message_prettify()` function.
*
* @param id Pointer in which to store the OID of the newly created commit
*
@ -243,7 +254,8 @@ GIT_EXTERN(int) git_commit_nth_gen_ancestor(
* is not direct, it will be resolved to a direct reference.
* Use "HEAD" to update the HEAD of the current branch and
* make it point to this commit. If the reference doesn't
* exist yet, it will be created.
* exist yet, it will be created. If it does exist, the first
* parent must be the tip of this branch.
*
* @param author Signature with author and author time of commit
*
@ -280,20 +292,20 @@ GIT_EXTERN(int) git_commit_create(
const char *message_encoding,
const char *message,
const git_tree *tree,
int parent_count,
size_t parent_count,
const git_commit *parents[]);
/**
* Create new commit in the repository using a variable argument list.
*
* The message will be cleaned up from excess whitespace and it will be made
* sure that the last line ends with a '\n'.
* The message will **not** be cleaned up automatically. You can do that
* with the `git_message_prettify()` function.
*
* The parents for the commit are specified as a variable list of pointers
* to `const git_commit *`. Note that this is a convenience method which may
* not be safe to export for certain languages or compilers
*
* All other parameters remain the same at `git_commit_create()`.
* All other parameters remain the same as `git_commit_create()`.
*
* @see git_commit_create
*/
@ -306,9 +318,40 @@ GIT_EXTERN(int) git_commit_create_v(
const char *message_encoding,
const char *message,
const git_tree *tree,
int parent_count,
size_t parent_count,
...);
/**
* Amend an existing commit by replacing only non-NULL values.
*
* This creates a new commit that is exactly the same as the old commit,
* except that any non-NULL values will be updated. The new commit has
* the same parents as the old commit.
*
* The `update_ref` value works as in the regular `git_commit_create()`,
* updating the ref to point to the newly rewritten commit. If you want
* to amend a commit that is not currently the tip of the branch and then
* rewrite the following commits to reach a ref, pass this as NULL and
* update the rest of the commit chain and ref separately.
*
* Unlike `git_commit_create()`, the `author`, `committer`, `message`,
* `message_encoding`, and `tree` parameters can be NULL in which case this
* will use the values from the original `commit_to_amend`.
*
* All parameters have the same meanings as in `git_commit_create()`.
*
* @see git_commit_create
*/
GIT_EXTERN(int) git_commit_amend(
git_oid *id,
const git_commit *commit_to_amend,
const char *update_ref,
const git_signature *author,
const git_signature *committer,
const char *message_encoding,
const char *message,
const git_tree *tree);
/** @} */
GIT_END_DECL
#endif

View File

@ -37,13 +37,6 @@
# define GIT_EXTERN(type) extern type
#endif
/** Declare a function as always inlined. */
#if defined(_MSC_VER)
# define GIT_INLINE(type) static __inline type
#else
# define GIT_INLINE(type) static inline type
#endif
/** Declare a function's takes printf style arguments. */
#ifdef __GNUC__
# define GIT_FORMAT_PRINTF(a,b) __attribute__((format (printf, a, b)))
@ -101,29 +94,34 @@ GIT_BEGIN_DECL
GIT_EXTERN(void) git_libgit2_version(int *major, int *minor, int *rev);
/**
* Combinations of these values describe the capabilities of libgit2.
* Combinations of these values describe the features with which libgit2
* was compiled
*/
typedef enum {
GIT_CAP_THREADS = ( 1 << 0 ),
GIT_CAP_HTTPS = ( 1 << 1 ),
GIT_CAP_SSH = ( 1 << 2 ),
} git_cap_t;
GIT_FEATURE_THREADS = (1 << 0),
GIT_FEATURE_HTTPS = (1 << 1),
GIT_FEATURE_SSH = (1 << 2),
} git_feature_t;
/**
* Query compile time options for libgit2.
*
* @return A combination of GIT_CAP_* values.
* @return A combination of GIT_FEATURE_* values.
*
* - GIT_CAP_THREADS
* - GIT_FEATURE_THREADS
* Libgit2 was compiled with thread support. Note that thread support is
* still to be seen as a 'work in progress' - basic object lookups are
* believed to be threadsafe, but other operations may not be.
*
* - GIT_CAP_HTTPS
* - GIT_FEATURE_HTTPS
* Libgit2 supports the https:// protocol. This requires the openssl
* library to be found when compiling libgit2.
*
* - GIT_FEATURE_SSH
* Libgit2 supports the SSH protocol for network operations. This requires
* the libssh2 library to be found when compiling libgit2
*/
GIT_EXTERN(int) git_libgit2_capabilities(void);
GIT_EXTERN(int) git_libgit2_features(void);
typedef enum {
@ -163,12 +161,12 @@ typedef enum {
* >Set the maximum amount of memory that can be mapped at any time
* by the library
*
* * opts(GIT_OPT_GET_SEARCH_PATH, int level, char *out, size_t len)
* * opts(GIT_OPT_GET_SEARCH_PATH, int level, git_buf *buf)
*
* > Get the search path for a given level of config data. "level" must
* > be one of `GIT_CONFIG_LEVEL_SYSTEM`, `GIT_CONFIG_LEVEL_GLOBAL`, or
* > `GIT_CONFIG_LEVEL_XDG`. The search path is written to the `out`
* > buffer up to size `len`. Returns GIT_EBUFS if buffer is too small.
* > buffer.
*
* * opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path)
*
@ -197,7 +195,7 @@ typedef enum {
* > across all repositories before libgit2 starts evicting objects
* > from the cache. This is a soft limit, in that the library might
* > briefly exceed it, but will start aggressively evicting objects
* > from cache when that happens. The default cache size is 256Mb.
* > from cache when that happens. The default cache size is 256MB.
*
* * opts(GIT_OPT_ENABLE_CACHING, int enabled)
*
@ -212,11 +210,10 @@ typedef enum {
* > Get the current bytes in cache and the maximum that would be
* > allowed in the cache.
*
* * opts(GIT_OPT_GET_TEMPLATE_PATH, char *out, size_t len)
* * opts(GIT_OPT_GET_TEMPLATE_PATH, git_buf *out)
*
* > Get the default template path.
* > The path is written to the `out`
* > buffer up to size `len`. Returns GIT_EBUFS if buffer is too small.
* > The path is written to the `out` buffer.
*
* * opts(GIT_OPT_SET_TEMPLATE_PATH, const char *path)
*

View File

@ -9,6 +9,7 @@
#include "common.h"
#include "types.h"
#include "buffer.h"
/**
* @file git2/config.h
@ -90,11 +91,10 @@ typedef struct {
* This method will not guess the path to the xdg compatible
* config file (.config/git/config).
*
* @param out Buffer to store the path in
* @param length size of the buffer in bytes
* @return 0 if a global configuration file has been found. Its path will be stored in `buffer`.
* @param out Pointer to a user-allocated git_buf in which to store the path
* @return 0 if a global configuration file has been found. Its path will be stored in `out`.
*/
GIT_EXTERN(int) git_config_find_global(char *out, size_t length);
GIT_EXTERN(int) git_config_find_global(git_buf *out);
/**
* Locate the path to the global xdg compatible configuration file
@ -107,25 +107,23 @@ GIT_EXTERN(int) git_config_find_global(char *out, size_t length);
* may be used on any `git_config` call to load the
* xdg compatible configuration file.
*
* @param out Buffer to store the path in
* @param length size of the buffer in bytes
* @param out Pointer to a user-allocated git_buf in which to store the path
* @return 0 if a xdg compatible configuration file has been
* found. Its path will be stored in `buffer`.
* found. Its path will be stored in `out`.
*/
GIT_EXTERN(int) git_config_find_xdg(char *out, size_t length);
GIT_EXTERN(int) git_config_find_xdg(git_buf *out);
/**
* Locate the path to the system configuration file
*
* If /etc/gitconfig doesn't exist, it will look for
* %PROGRAMFILES%\Git\etc\gitconfig.
* @param out Buffer to store the path in
* @param length size of the buffer in bytes
*
* @param out Pointer to a user-allocated git_buf in which to store the path
* @return 0 if a system configuration file has been
* found. Its path will be stored in `buffer`.
* found. Its path will be stored in `out`.
*/
GIT_EXTERN(int) git_config_find_system(char *out, size_t length);
GIT_EXTERN(int) git_config_find_system(git_buf *out);
/**
* Open the global, XDG and system configuration files
@ -228,6 +226,22 @@ GIT_EXTERN(int) git_config_open_level(
*/
GIT_EXTERN(int) git_config_open_global(git_config **out, git_config *config);
/**
* Create a snapshot of the configuration
*
* Create a snapshot of the current state of a configuration, which
* allows you to look into a consistent view of the configuration for
* looking up complex values (e.g. a remote, submodule).
*
* The string returned when querying such a config object is valid
* until it is freed.
*
* @param out pointer in which to store the snapshot config object
* @param config configuration to snapshot
* @return 0 or an error code
*/
GIT_EXTERN(int) git_config_snapshot(git_config **out, git_config *config);
/**
* Reload changed config files
@ -314,7 +328,8 @@ GIT_EXTERN(int) git_config_get_bool(int *out, const git_config *cfg, const char
* Get the value of a string config variable.
*
* The string is owned by the variable and should not be freed by the
* user.
* user. The pointer will be valid until the next operation on this
* config object.
*
* All config files will be looked into, in the order of their
* defined level. A higher level means a higher priority. The
@ -355,6 +370,9 @@ GIT_EXTERN(int) git_config_multivar_iterator_new(git_config_iterator **out, cons
/**
* Return the current entry and advance the iterator
*
* The pointers returned by this function are valid until the iterator
* is freed.
*
* @param entry pointer to store the entry
* @param iter the iterator
* @return 0 or an error code. GIT_ITEROVER if the iteration has completed
@ -450,13 +468,16 @@ GIT_EXTERN(int) git_config_delete_multivar(git_config *cfg, const char *name, co
*
* The callback receives the normalized name and value of each variable
* in the config backend, and the data pointer passed to this function.
* As soon as one of the callback functions returns something other than 0,
* this function stops iterating and returns `GIT_EUSER`.
* If the callback returns a non-zero value, the function stops iterating
* and returns that value to the caller.
*
* The pointers passed to the callback are only valid as long as the
* iteration is ongoing.
*
* @param cfg where to get the variables from
* @param callback the function to call on each variable
* @param payload the data to pass to the callback
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_config_foreach(
const git_config *cfg,
@ -493,6 +514,9 @@ GIT_EXTERN(int) git_config_iterator_glob_new(git_config_iterator **out, const gi
* regular expression that filters which config keys are passed to the
* callback.
*
* The pointers passed to the callback are only valid as long as the
* iteration is ongoing.
*
* @param cfg where to get the variables from
* @param regexp regular expression to match against config names
* @param callback the function to call on each variable
@ -612,8 +636,8 @@ GIT_EXTERN(int) git_config_parse_int64(int64_t *out, const char *value);
GIT_EXTERN(int) git_config_backend_foreach_match(
git_config_backend *backend,
const char *regexp,
int (*fn)(const git_config_entry *, void *),
void *data);
git_config_foreach_cb callback,
void *payload);
/** @} */

View File

@ -145,6 +145,13 @@ typedef enum {
*/
GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS = (1u << 14),
/** When diff finds a file in the working directory with stat
* information different from the index, but the OID ends up being the
* same, write the correct stat information into the index. Note:
* without this flag, diff will always leave the index untouched.
*/
GIT_DIFF_UPDATE_INDEX = (1u << 15),
/*
* Options controlling how output will be generated
*/
@ -180,6 +187,10 @@ typedef enum {
/** Take extra time to find minimal diff */
GIT_DIFF_MINIMAL = (1 << 29),
/** Include the necessary deflate / delta information so that `git-apply`
* can apply given diff information to binary files.
*/
GIT_DIFF_SHOW_BINARY = (1 << 30),
} git_diff_option_t;
/**
@ -201,9 +212,9 @@ typedef struct git_diff git_diff;
* considered reserved for internal or future use.
*/
typedef enum {
GIT_DIFF_FLAG_BINARY = (1u << 0), /** file(s) treated as binary data */
GIT_DIFF_FLAG_NOT_BINARY = (1u << 1), /** file(s) treated as text data */
GIT_DIFF_FLAG_VALID_OID = (1u << 2), /** `oid` value is known correct */
GIT_DIFF_FLAG_BINARY = (1u << 0), /**< file(s) treated as binary data */
GIT_DIFF_FLAG_NOT_BINARY = (1u << 1), /**< file(s) treated as text data */
GIT_DIFF_FLAG_VALID_ID = (1u << 2), /**< `id` value is known correct */
} git_diff_flag_t;
/**
@ -217,15 +228,15 @@ typedef enum {
* DELETED pairs).
*/
typedef enum {
GIT_DELTA_UNMODIFIED = 0, /** no changes */
GIT_DELTA_ADDED = 1, /** entry does not exist in old version */
GIT_DELTA_DELETED = 2, /** entry does not exist in new version */
GIT_DELTA_MODIFIED = 3, /** entry content changed between old and new */
GIT_DELTA_RENAMED = 4, /** entry was renamed between old and new */
GIT_DELTA_COPIED = 5, /** entry was copied from another old entry */
GIT_DELTA_IGNORED = 6, /** entry is ignored item in workdir */
GIT_DELTA_UNTRACKED = 7, /** entry is untracked item in workdir */
GIT_DELTA_TYPECHANGE = 8, /** type of entry changed between old and new */
GIT_DELTA_UNMODIFIED = 0, /**< no changes */
GIT_DELTA_ADDED = 1, /**< entry does not exist in old version */
GIT_DELTA_DELETED = 2, /**< entry does not exist in new version */
GIT_DELTA_MODIFIED = 3, /**< entry content changed between old and new */
GIT_DELTA_RENAMED = 4, /**< entry was renamed between old and new */
GIT_DELTA_COPIED = 5, /**< entry was copied from another old entry */
GIT_DELTA_IGNORED = 6, /**< entry is ignored item in workdir */
GIT_DELTA_UNTRACKED = 7, /**< entry is untracked item in workdir */
GIT_DELTA_TYPECHANGE = 8, /**< type of entry changed between old and new */
} git_delta_t;
/**
@ -250,7 +261,7 @@ typedef enum {
* be restricted to one of the `git_filemode_t` values.
*/
typedef struct {
git_oid oid;
git_oid id;
const char *path;
git_off_t size;
uint32_t flags;
@ -361,7 +372,7 @@ typedef struct {
uint16_t context_lines; /**< defaults to 3 */
uint16_t interhunk_lines; /**< defaults to 0 */
uint16_t oid_abbrev; /**< default 'core.abbrev' or 7 if unset */
uint16_t id_abbrev; /**< default 'core.abbrev' or 7 if unset */
git_off_t max_size; /**< defaults to 512MB */
const char *old_prefix; /**< defaults to "a" */
const char *new_prefix; /**< defaults to "b" */
@ -376,6 +387,18 @@ typedef struct {
#define GIT_DIFF_OPTIONS_INIT \
{GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_DEFAULT, {NULL,0}, NULL, NULL, 3}
/**
* Initializes a `git_diff_options` with default values. Equivalent to
* creating an instance with GIT_DIFF_OPTIONS_INIT.
*
* @param opts The `git_diff_options` struct to initialize
* @param version Version of struct; pass `GIT_DIFF_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_diff_init_options(
git_diff_options *opts,
unsigned int version);
/**
* When iterating over a diff, callback that will be made per file.
*
@ -393,12 +416,12 @@ typedef int (*git_diff_file_cb)(
*/
typedef struct git_diff_hunk git_diff_hunk;
struct git_diff_hunk {
int old_start; /** Starting line number in old_file */
int old_lines; /** Number of lines in old_file */
int new_start; /** Starting line number in new_file */
int new_lines; /** Number of lines in new_file */
size_t header_len; /** Number of bytes in header text */
char header[128]; /** Header text, NUL-byte terminated */
int old_start; /**< Starting line number in old_file */
int old_lines; /**< Number of lines in old_file */
int new_start; /**< Starting line number in new_file */
int new_lines; /**< Number of lines in new_file */
size_t header_len; /**< Number of bytes in header text */
char header[128]; /**< Header text, NUL-byte terminated */
};
/**
@ -441,13 +464,13 @@ typedef enum {
*/
typedef struct git_diff_line git_diff_line;
struct git_diff_line {
char origin; /** A git_diff_line_t value */
int old_lineno; /** Line number in old file or -1 for added line */
int new_lineno; /** Line number in new file or -1 for deleted line */
int num_lines; /** Number of newline characters in content */
size_t content_len; /** Number of bytes of data */
git_off_t content_offset; /** Offset in the original file to the content */
const char *content; /** Pointer to diff text, not NUL-byte terminated */
char origin; /**< A git_diff_line_t value */
int old_lineno; /**< Line number in old file or -1 for added line */
int new_lineno; /**< Line number in new file or -1 for deleted line */
int num_lines; /**< Number of newline characters in content */
size_t content_len; /**< Number of bytes of data */
git_off_t content_offset; /**< Offset in the original file to the content */
const char *content; /**< Pointer to diff text, not NUL-byte terminated */
};
/**
@ -459,50 +482,83 @@ struct git_diff_line {
* of lines of file and hunk headers.
*/
typedef int (*git_diff_line_cb)(
const git_diff_delta *delta, /** delta that contains this data */
const git_diff_hunk *hunk, /** hunk containing this data */
const git_diff_line *line, /** line data */
void *payload); /** user reference data */
const git_diff_delta *delta, /**< delta that contains this data */
const git_diff_hunk *hunk, /**< hunk containing this data */
const git_diff_line *line, /**< line data */
void *payload); /**< user reference data */
/**
* Flags to control the behavior of diff rename/copy detection.
*/
typedef enum {
/** look for renames? (`--find-renames`) */
/** Obey `diff.renames`. Overridden by any other GIT_DIFF_FIND_... flag. */
GIT_DIFF_FIND_BY_CONFIG = 0,
/** Look for renames? (`--find-renames`) */
GIT_DIFF_FIND_RENAMES = (1u << 0),
/** consider old side of modified for renames? (`--break-rewrites=N`) */
/** Consider old side of MODIFIED for renames? (`--break-rewrites=N`) */
GIT_DIFF_FIND_RENAMES_FROM_REWRITES = (1u << 1),
/** look for copies? (a la `--find-copies`) */
/** Look for copies? (a la `--find-copies`). */
GIT_DIFF_FIND_COPIES = (1u << 2),
/** consider unmodified as copy sources? (`--find-copies-harder`) */
/** Consider UNMODIFIED as copy sources? (`--find-copies-harder`).
*
* For this to work correctly, use GIT_DIFF_INCLUDE_UNMODIFIED when
* the initial `git_diff` is being generated.
*/
GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED = (1u << 3),
/** mark large rewrites for split (`--break-rewrites=/M`) */
/** Mark significant rewrites for split (`--break-rewrites=/M`) */
GIT_DIFF_FIND_REWRITES = (1u << 4),
/** actually split large rewrites into delete/add pairs */
/** Actually split large rewrites into delete/add pairs */
GIT_DIFF_BREAK_REWRITES = (1u << 5),
/** mark rewrites for split and break into delete/add pairs */
/** Mark rewrites for split and break into delete/add pairs */
GIT_DIFF_FIND_AND_BREAK_REWRITES =
(GIT_DIFF_FIND_REWRITES | GIT_DIFF_BREAK_REWRITES),
/** find renames/copies for untracked items in working directory */
/** Find renames/copies for UNTRACKED items in working directory.
*
* For this to work correctly, use GIT_DIFF_INCLUDE_UNTRACKED when the
* initial `git_diff` is being generated (and obviously the diff must
* be against the working directory for this to make sense).
*/
GIT_DIFF_FIND_FOR_UNTRACKED = (1u << 6),
/** turn on all finding features */
/** Turn on all finding features. */
GIT_DIFF_FIND_ALL = (0x0ff),
/** measure similarity ignoring leading whitespace (default) */
/** Measure similarity ignoring leading whitespace (default) */
GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE = 0,
/** measure similarity ignoring all whitespace */
/** Measure similarity ignoring all whitespace */
GIT_DIFF_FIND_IGNORE_WHITESPACE = (1u << 12),
/** measure similarity including all data */
/** Measure similarity including all data */
GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE = (1u << 13),
/** measure similarity only by comparing SHAs (fast and cheap) */
/** Measure similarity only by comparing SHAs (fast and cheap) */
GIT_DIFF_FIND_EXACT_MATCH_ONLY = (1u << 14),
/** do not break rewrites unless they contribute to a rename */
/** Do not break rewrites unless they contribute to a rename.
*
* Normally, GIT_DIFF_FIND_AND_BREAK_REWRITES will measure the self-
* similarity of modified files and split the ones that have changed a
* lot into a DELETE / ADD pair. Then the sides of that pair will be
* considered candidates for rename and copy detection.
*
* If you add this flag in and the split pair is *not* used for an
* actual rename or copy, then the modified record will be restored to
* a regular MODIFIED record instead of being split.
*/
GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY = (1u << 15),
/** Remove any UNMODIFIED deltas after find_similar is done.
*
* Using GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED to emulate the
* --find-copies-harder behavior requires building a diff with the
* GIT_DIFF_INCLUDE_UNMODIFIED flag. If you do not want UNMODIFIED
* records in the final result, pass this flag to have them removed.
*/
GIT_DIFF_FIND_REMOVE_UNMODIFIED = (1u << 16),
} git_diff_find_t;
/**
@ -543,7 +599,11 @@ typedef struct {
typedef struct {
unsigned int version;
/** Combination of git_diff_find_t values (default FIND_RENAMES) */
/**
* Combination of git_diff_find_t values (default GIT_DIFF_FIND_BY_CONFIG).
* NOTE: if you don't explicitly set this, `diff.renames` could be set
* to false, resulting in `git_diff_find_similar` doing nothing.
*/
uint32_t flags;
/** Similarity to consider a file renamed (default 50) */
@ -567,6 +627,18 @@ typedef struct {
#define GIT_DIFF_FIND_OPTIONS_VERSION 1
#define GIT_DIFF_FIND_OPTIONS_INIT {GIT_DIFF_FIND_OPTIONS_VERSION}
/**
* Initializes a `git_diff_find_options` with default values. Equivalent to
* creating an instance with GIT_DIFF_FIND_OPTIONS_INIT.
*
* @param opts The `git_diff_find_options` struct to initialize
* @param version Version of struct; pass `GIT_DIFF_FIND_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_diff_find_init_options(
git_diff_find_options *opts,
unsigned int version);
/** @name Diff Generator Functions
*
* These are the functions you would use to create (or destroy) a
@ -662,24 +734,17 @@ GIT_EXTERN(int) git_diff_index_to_workdir(
* The tree you provide will be used for the "old_file" side of the delta,
* and the working directory will be used for the "new_file" side.
*
* Please note: this is *NOT* the same as `git diff <treeish>`. Running
* `git diff HEAD` or the like actually uses information from the index,
* along with the tree and working directory info.
* This is not the same as `git diff <treeish>` or `git diff-index
* <treeish>`. Those commands use information from the index, whereas this
* function strictly returns the differences between the tree and the files
* in the working directory, regardless of the state of the index. Use
* `git_diff_tree_to_workdir_with_index` to emulate those commands.
*
* This function returns strictly the differences between the tree and the
* files contained in the working directory, regardless of the state of
* files in the index. It may come as a surprise, but there is no direct
* equivalent in core git.
*
* To emulate `git diff <tree>`, use `git_diff_tree_to_workdir_with_index`
* (or `git_diff_tree_to_index` and `git_diff_index_to_workdir`, then call
* `git_diff_merge` on the results). That will yield a `git_diff` that
* matches the git output.
*
* If this seems confusing, take the case of a file with a staged deletion
* where the file has then been put back into the working dir and modified.
* The tree-to-workdir diff for that file is 'modified', but core git would
* show status 'deleted' since there is a pending deletion in the index.
* To see difference between this and `git_diff_tree_to_workdir_with_index`,
* consider the example of a staged file deletion where the file has then
* been put back into the working dir and further modified. The
* tree-to-workdir diff for that file is 'modified', but `git diff` would
* show status 'deleted' since there is a staged delete.
*
* @param diff A pointer to a git_diff pointer that will be allocated.
* @param repo The repository containing the tree.
@ -744,23 +809,6 @@ GIT_EXTERN(int) git_diff_find_similar(
git_diff *diff,
const git_diff_find_options *options);
/**
* Initialize diff options structure
*
* In most cases, you can probably just use `GIT_DIFF_OPTIONS_INIT` to
* initialize the diff options structure, but in some cases that is not
* going to work. You can call this function instead. Note that you
* must pass both a pointer to the structure to be initialized and the
* `GIT_DIFF_OPTIONS_VERSION` value from the header you compiled with.
*
* @param options Pointer to git_diff_options memory to be initialized
* @param version Should be `GIT_DIFF_OPTIONS_VERSION`
* @return 0 on success, negative on failure (such as unsupported version)
*/
GIT_EXTERN(int) git_diff_options_init(
git_diff_options *options,
unsigned int version);
/**@}*/
@ -833,7 +881,7 @@ GIT_EXTERN(int) git_diff_is_sorted_icase(const git_diff *diff);
* files whose only changed is a file mode change.
*
* Returning a non-zero value from any of the callbacks will terminate
* the iteration and cause this return `GIT_EUSER`.
* the iteration and return the value to the user.
*
* @param diff A git_diff generated by one of the above functions.
* @param file_cb Callback function to make per file in the diff.
@ -844,7 +892,7 @@ GIT_EXTERN(int) git_diff_is_sorted_icase(const git_diff *diff);
* same callback will be made for context lines, added, and
* removed lines, and even for a deleted trailing newline.
* @param payload Reference pointer that will be passed to your callbacks.
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_diff_foreach(
git_diff *diff,
@ -881,13 +929,13 @@ typedef enum {
* Iterate over a diff generating formatted text output.
*
* Returning a non-zero value from the callbacks will terminate the
* iteration and cause this return `GIT_EUSER`.
* iteration and return the non-zero value to the caller.
*
* @param diff A git_diff generated by one of the above functions.
* @param format A git_diff_format_t value to pick the text format.
* @param print_cb Callback to make per line of diff text.
* @param payload Reference pointer that will be passed to your callback.
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_diff_print(
git_diff *diff,
@ -927,7 +975,7 @@ GIT_EXTERN(int) git_diff_print(
* @param hunk_cb Callback for each hunk in diff; can be NULL
* @param line_cb Callback for each line in diff; can be NULL
* @param payload Payload passed to each callback function
* @return 0 on success, GIT_EUSER on non-zero callback return, or error code
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_diff_blobs(
const git_blob *old_blob,
@ -962,7 +1010,7 @@ GIT_EXTERN(int) git_diff_blobs(
* @param hunk_cb Callback for each hunk in diff; can be NULL
* @param line_cb Callback for each line in diff; can be NULL
* @param payload Payload passed to each callback function
* @return 0 on success, GIT_EUSER on non-zero callback return, or error code
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_diff_blob_to_buffer(
const git_blob *old_blob,
@ -976,6 +1024,214 @@ GIT_EXTERN(int) git_diff_blob_to_buffer(
git_diff_line_cb line_cb,
void *payload);
/**
* Directly run a diff between two buffers.
*
* Even more than with `git_diff_blobs`, comparing two buffer lacks
* context, so the `git_diff_file` parameters to the callbacks will be
* faked a la the rules for `git_diff_blobs()`.
*
* @param old_buffer Raw data for old side of diff, or NULL for empty
* @param old_len Length of the raw data for old side of the diff
* @param old_as_path Treat old buffer as if it had this filename; can be NULL
* @param new_buffer Raw data for new side of diff, or NULL for empty
* @param new_len Length of raw data for new side of diff
* @param new_as_path Treat buffer as if it had this filename; can be NULL
* @param options Options for diff, or NULL for default options
* @param file_cb Callback for "file"; made once if there is a diff; can be NULL
* @param hunk_cb Callback for each hunk in diff; can be NULL
* @param line_cb Callback for each line in diff; can be NULL
* @param payload Payload passed to each callback function
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_diff_buffers(
const void *old_buffer,
size_t old_len,
const char *old_as_path,
const void *new_buffer,
size_t new_len,
const char *new_as_path,
const git_diff_options *options,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
git_diff_line_cb line_cb,
void *payload);
/**
* This is an opaque structure which is allocated by `git_diff_get_stats`.
* You are responsible for releasing the object memory when done, using the
* `git_diff_stats_free()` function.
*/
typedef struct git_diff_stats git_diff_stats;
/**
* Formatting options for diff stats
*/
typedef enum {
/** No stats*/
GIT_DIFF_STATS_NONE = 0,
/** Full statistics, equivalent of `--stat` */
GIT_DIFF_STATS_FULL = (1u << 0),
/** Short statistics, equivalent of `--shortstat` */
GIT_DIFF_STATS_SHORT = (1u << 1),
/** Number statistics, equivalent of `--numstat` */
GIT_DIFF_STATS_NUMBER = (1u << 2),
/** Extended header information such as creations, renames and mode changes, equivalent of `--summary` */
GIT_DIFF_STATS_INCLUDE_SUMMARY = (1u << 3),
} git_diff_stats_format_t;
/**
* Accumlate diff statistics for all patches.
*
* @param out Structure containg the diff statistics.
* @param diff A git_diff generated by one of the above functions.
* @return 0 on success; non-zero on error
*/
GIT_EXTERN(int) git_diff_get_stats(
git_diff_stats **out,
git_diff *diff);
/**
* Get the total number of files changed in a diff
*
* @param stats A `git_diff_stats` generated by one of the above functions.
* @return total number of files changed in the diff
*/
GIT_EXTERN(size_t) git_diff_stats_files_changed(
const git_diff_stats *stats);
/**
* Get the total number of insertions in a diff
*
* @param stats A `git_diff_stats` generated by one of the above functions.
* @return total number of insertions in the diff
*/
GIT_EXTERN(size_t) git_diff_stats_insertions(
const git_diff_stats *stats);
/**
* Get the total number of deletions in a diff
*
* @param stats A `git_diff_stats` generated by one of the above functions.
* @return total number of deletions in the diff
*/
GIT_EXTERN(size_t) git_diff_stats_deletions(
const git_diff_stats *stats);
/**
* Print diff statistics to a `git_buf`.
*
* @param out buffer to store the formatted diff statistics in.
* @param stats A `git_diff_stats` generated by one of the above functions.
* @param format Formatting option.
* @param width Target width for output (only affects GIT_DIFF_STATS_FULL)
* @return 0 on success; non-zero on error
*/
GIT_EXTERN(int) git_diff_stats_to_buf(
git_buf *out,
const git_diff_stats *stats,
git_diff_stats_format_t format,
size_t width);
/**
* Deallocate a `git_diff_stats`.
*
* @param stats The previously created statistics object;
* cannot be used after free.
*/
GIT_EXTERN(void) git_diff_stats_free(git_diff_stats *stats);
/**
* Formatting options for diff e-mail generation
*/
typedef enum {
/** Normal patch, the default */
GIT_DIFF_FORMAT_EMAIL_NONE = 0,
/** Don't insert "[PATCH]" in the subject header*/
GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER = (1 << 0),
} git_diff_format_email_flags_t;
/**
* Options for controlling the formatting of the generated e-mail.
*/
typedef struct {
unsigned int version;
git_diff_format_email_flags_t flags;
/** This patch number */
size_t patch_no;
/** Total number of patches in this series */
size_t total_patches;
/** id to use for the commit */
const git_oid *id;
/** Summary of the change */
const char *summary;
/** Author of the change */
const git_signature *author;
} git_diff_format_email_options;
#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION 1
#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT {GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, 0, 1, 1, NULL, NULL, NULL}
/**
* Create an e-mail ready patch from a diff.
*
* @param out buffer to store the e-mail patch in
* @param diff containing the commit
* @param opts structure with options to influence content and formatting.
* @return 0 or an error code
*/
GIT_EXTERN(int) git_diff_format_email(
git_buf *out,
git_diff *diff,
const git_diff_format_email_options *opts);
/**
* Create an e-mail ready patch for a commit.
*
* Does not support creating patches for merge commits (yet).
*
* @param out buffer to store the e-mail patch in
* @param repo containing the commit
* @param commit pointer to up commit
* @param patch_no patch number of the commit
* @param total_patches total number of patches in the patch set
* @param flags determines the formatting of the e-mail
* @param diff_opts structure with options to influence diff or NULL for defaults.
* @return 0 or an error code
*/
GIT_EXTERN(int) git_diff_commit_as_email(
git_buf *out,
git_repository *repo,
git_commit *commit,
size_t patch_no,
size_t total_patches,
git_diff_format_email_flags_t flags,
const git_diff_options *diff_opts);
/**
* Initializes a `git_diff_format_email_options` with default values.
*
* Equivalent to creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT.
*
* @param opts The `git_diff_format_email_options` struct to initialize
* @param version Version of struct; pass `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_diff_format_email_init_options(
git_diff_format_email_options *opts,
unsigned int version);
GIT_END_DECL

View File

@ -8,7 +8,6 @@
#define INCLUDE_git_errors_h__
#include "common.h"
#include "buffer.h"
/**
* @file git2/errors.h
@ -20,25 +19,39 @@ GIT_BEGIN_DECL
/** Generic return codes */
typedef enum {
GIT_OK = 0,
GIT_ERROR = -1,
GIT_ENOTFOUND = -3,
GIT_EEXISTS = -4,
GIT_EAMBIGUOUS = -5,
GIT_EBUFS = -6,
GIT_EUSER = -7,
GIT_EBAREREPO = -8,
GIT_EUNBORNBRANCH = -9,
GIT_EUNMERGED = -10,
GIT_ENONFASTFORWARD = -11,
GIT_EINVALIDSPEC = -12,
GIT_EMERGECONFLICT = -13,
GIT_ELOCKED = -14,
GIT_OK = 0, /**< No error */
GIT_PASSTHROUGH = -30,
GIT_ITEROVER = -31,
GIT_ERROR = -1, /**< Generic error */
GIT_ENOTFOUND = -3, /**< Requested object could not be found */
GIT_EEXISTS = -4, /**< Object exists preventing operation */
GIT_EAMBIGUOUS = -5, /**< More than one object matches */
GIT_EBUFS = -6, /**< Output buffer too short to hold data */
/* GIT_EUSER is a special error that is never generated by libgit2
* code. You can return it from a callback (e.g to stop an iteration)
* to know that it was generated by the callback and not by libgit2.
*/
GIT_EUSER = -7,
GIT_EBAREREPO = -8, /**< Operation not allowed on bare repository */
GIT_EUNBORNBRANCH = -9, /**< HEAD refers to branch with no commits */
GIT_EUNMERGED = -10, /**< Merge in progress prevented operation */
GIT_ENONFASTFORWARD = -11, /**< Reference was not fast-forwardable */
GIT_EINVALIDSPEC = -12, /**< Name/ref spec was not in a valid format */
GIT_EMERGECONFLICT = -13, /**< Merge conflicts prevented operation */
GIT_ELOCKED = -14, /**< Lock file prevented operation */
GIT_EMODIFIED = -15, /**< Reference value does not match expected */
GIT_PASSTHROUGH = -30, /**< Internal only */
GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */
} git_error_code;
/**
* Structure to store extra details of the last error that occurred.
*
* This is kept on a per-thread basis if GIT_THREADS was defined when the
* library was build, otherwise one is kept globally for the library
*/
typedef struct {
char *message;
int klass;
@ -71,6 +84,9 @@ typedef enum {
GITERR_MERGE,
GITERR_SSH,
GITERR_FILTER,
GITERR_REVERT,
GITERR_CALLBACK,
GITERR_CHERRYPICK,
} git_error_t;
/**
@ -90,7 +106,7 @@ GIT_EXTERN(void) giterr_clear(void);
* Get the last error data and clear it.
*
* This copies the last error into the given `git_error` struct
* and returns 0 if the copy was successful, leaving the error
* and returns 0 if the copy was successful, leaving the error
* cleared as if `giterr_clear` had been called.
*
* If there was no existing error in the library, -1 will be returned

View File

@ -35,6 +35,11 @@ typedef enum {
GIT_FILTER_CLEAN = GIT_FILTER_TO_ODB,
} git_filter_mode_t;
typedef enum {
GIT_FILTER_OPT_DEFAULT = 0u,
GIT_FILTER_OPT_ALLOW_UNSAFE = (1u << 0),
} git_filter_opt_t;
/**
* A filter that can transform file data
*
@ -75,6 +80,7 @@ typedef struct git_filter_list git_filter_list;
* @param blob The blob to which the filter will be applied (if known)
* @param path Relative path of the file to be filtered
* @param mode Filtering direction (WT->ODB or ODB->WT)
* @param options Combination of `git_filter_opt_t` flags
* @return 0 on success (which could still return NULL if no filters are
* needed for the requested file), <0 on error
*/
@ -83,7 +89,8 @@ GIT_EXTERN(int) git_filter_list_load(
git_repository *repo,
git_blob *blob, /* can be NULL */
const char *path,
git_filter_mode_t mode);
git_filter_mode_t mode,
uint32_t options);
/**
* Apply filter list to a data buffer.

View File

@ -36,6 +36,20 @@ GIT_BEGIN_DECL
*/
GIT_EXTERN(int) git_graph_ahead_behind(size_t *ahead, size_t *behind, git_repository *repo, const git_oid *local, const git_oid *upstream);
/**
* Determine if a commit is the descendant of another commit.
*
* @param commit a previously loaded commit.
* @param ancestor a potential ancestor commit.
* @return 1 if the given commit is a descendant of the potential ancestor,
* 0 if not, error code otherwise.
*/
GIT_EXTERN(int) git_graph_descendant_of(
git_repository *repo,
const git_oid *commit,
const git_oid *ancestor);
/** @} */
GIT_END_DECL
#endif

View File

@ -56,12 +56,12 @@ typedef struct git_index_entry {
unsigned int gid;
git_off_t file_size;
git_oid oid;
git_oid id;
unsigned short flags;
unsigned short flags_extended;
char *path;
const char *path;
} git_index_entry;
/**
@ -73,11 +73,19 @@ typedef struct git_index_entry {
*/
#define GIT_IDXENTRY_NAMEMASK (0x0fff)
#define GIT_IDXENTRY_STAGEMASK (0x3000)
#define GIT_IDXENTRY_EXTENDED (0x4000)
#define GIT_IDXENTRY_VALID (0x8000)
#define GIT_IDXENTRY_STAGESHIFT 12
#define GIT_IDXENTRY_STAGE(E) (((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT)
typedef enum {
GIT_IDXENTRY_EXTENDED = (0x4000),
GIT_IDXENTRY_VALID = (0x8000),
} git_indxentry_flag_t;
#define GIT_IDXENTRY_STAGE(E) \
(((E)->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT)
#define GIT_IDXENTRY_STAGE_SET(E,S) do { \
(E)->flags = ((E)->flags & ~GIT_IDXENTRY_STAGEMASK) | \
(((S) & 0x03) << GIT_IDXENTRY_STAGESHIFT); } while (0)
/**
* Bitmasks for on-disk fields of `git_index_entry`'s `flags_extended`
@ -87,43 +95,43 @@ typedef struct git_index_entry {
* in-memory only and used by libgit2. Only the flags in
* `GIT_IDXENTRY_EXTENDED_FLAGS` will get saved on-disk.
*
* These bitmasks match the three fields in the `git_index_entry`
* `flags_extended` value that belong on disk. You can use them to
* interpret the data in the `flags_extended`.
*/
#define GIT_IDXENTRY_INTENT_TO_ADD (1 << 13)
#define GIT_IDXENTRY_SKIP_WORKTREE (1 << 14)
/* GIT_IDXENTRY_EXTENDED2 is reserved for future extension */
#define GIT_IDXENTRY_EXTENDED2 (1 << 15)
#define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE)
/**
* Bitmasks for in-memory only fields of `git_index_entry`'s `flags_extended`
*
* These bitmasks match the other fields in the `git_index_entry`
* `flags_extended` value that are only used in-memory by libgit2. You
* Thee first three bitmasks match the three fields in the
* `git_index_entry` `flags_extended` value that belong on disk. You
* can use them to interpret the data in the `flags_extended`.
*
* The rest of the bitmasks match the other fields in the `git_index_entry`
* `flags_extended` value that are only used in-memory by libgit2.
* You can use them to interpret the data in the `flags_extended`.
*
*/
#define GIT_IDXENTRY_UPDATE (1 << 0)
#define GIT_IDXENTRY_REMOVE (1 << 1)
#define GIT_IDXENTRY_UPTODATE (1 << 2)
#define GIT_IDXENTRY_ADDED (1 << 3)
typedef enum {
#define GIT_IDXENTRY_HASHED (1 << 4)
#define GIT_IDXENTRY_UNHASHED (1 << 5)
#define GIT_IDXENTRY_WT_REMOVE (1 << 6) /* remove in work directory */
#define GIT_IDXENTRY_CONFLICTED (1 << 7)
GIT_IDXENTRY_INTENT_TO_ADD = (1 << 13),
GIT_IDXENTRY_SKIP_WORKTREE = (1 << 14),
/** Reserved for future extension */
GIT_IDXENTRY_EXTENDED2 = (1 << 15),
#define GIT_IDXENTRY_UNPACKED (1 << 8)
#define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9)
GIT_IDXENTRY_EXTENDED_FLAGS = (GIT_IDXENTRY_INTENT_TO_ADD | GIT_IDXENTRY_SKIP_WORKTREE),
GIT_IDXENTRY_UPDATE = (1 << 0),
GIT_IDXENTRY_REMOVE = (1 << 1),
GIT_IDXENTRY_UPTODATE = (1 << 2),
GIT_IDXENTRY_ADDED = (1 << 3),
GIT_IDXENTRY_HASHED = (1 << 4),
GIT_IDXENTRY_UNHASHED = (1 << 5),
GIT_IDXENTRY_WT_REMOVE = (1 << 6), /**< remove in work directory */
GIT_IDXENTRY_CONFLICTED = (1 << 7),
GIT_IDXENTRY_UNPACKED = (1 << 8),
GIT_IDXENTRY_NEW_SKIP_WORKTREE = (1 << 9),
} git_idxentry_extended_flag_t;
/** Capabilities of system that affect index actions. */
typedef enum {
GIT_INDEXCAP_IGNORE_CASE = 1u,
GIT_INDEXCAP_NO_FILEMODE = 2u,
GIT_INDEXCAP_NO_SYMLINKS = 4u,
GIT_INDEXCAP_FROM_OWNER = ~0u
GIT_INDEXCAP_IGNORE_CASE = 1,
GIT_INDEXCAP_NO_FILEMODE = 2,
GIT_INDEXCAP_NO_SYMLINKS = 4,
GIT_INDEXCAP_FROM_OWNER = -1,
} git_indexcap_t;
/** Callback for APIs that add/remove/update files matching pathspec */
@ -158,8 +166,8 @@ typedef enum {
* to back it.
*
* Since there is no ODB or working directory behind this index,
* any Index methods which rely on these (e.g. index_add) will
* fail with the GIT_EBAREINDEX error code.
* any Index methods which rely on these (e.g. index_add_bypath)
* will fail with the GIT_ERROR error code.
*
* If you need to access the index of an actual repository,
* use the `git_repository_index` wrapper.
@ -206,7 +214,7 @@ GIT_EXTERN(git_repository *) git_index_owner(const git_index *index);
* @param index An existing index object
* @return A combination of GIT_INDEXCAP values
*/
GIT_EXTERN(unsigned int) git_index_caps(const git_index *index);
GIT_EXTERN(int) git_index_caps(const git_index *index);
/**
* Set index capabilities flags.
@ -219,7 +227,7 @@ GIT_EXTERN(unsigned int) git_index_caps(const git_index *index);
* @param caps A combination of GIT_INDEXCAP values
* @return 0 on success, -1 on failure
*/
GIT_EXTERN(int) git_index_set_caps(git_index *index, unsigned int caps);
GIT_EXTERN(int) git_index_set_caps(git_index *index, int caps);
/**
* Update the contents of an existing index object in memory by reading
@ -255,7 +263,7 @@ GIT_EXTERN(int) git_index_write(git_index *index);
* @param index an existing index object
* @return path to index file or NULL for in-memory index
*/
GIT_EXTERN(const char *) git_index_path(git_index *index);
GIT_EXTERN(const char *) git_index_path(const git_index *index);
/**
* Read a tree into the index file with stats
@ -327,12 +335,14 @@ GIT_EXTERN(size_t) git_index_entrycount(const git_index *index);
/**
* Clear the contents (all the entries) of an index object.
* This clears the index object in memory; changes must be manually
* written to disk for them to take effect.
*
* This clears the index object in memory; changes must be explicitly
* written to disk for them to take effect persistently.
*
* @param index an existing index object
* @return 0 on success, error code < 0 on failure
*/
GIT_EXTERN(void) git_index_clear(git_index *index);
GIT_EXTERN(int) git_index_clear(git_index *index);
/**
* Get a pointer to one of the entries in the index
@ -405,10 +415,10 @@ GIT_EXTERN(int) git_index_add(git_index *index, const git_index_entry *source_en
*
* This entry is calculated from the entry's flag attribute like this:
*
* (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT
* (entry->flags & GIT_IDXENTRY_STAGEMASK) >> GIT_IDXENTRY_STAGESHIFT
*
* @param entry The entry
* @returns the stage number
* @return the stage number
*/
GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry);
@ -493,7 +503,7 @@ GIT_EXTERN(int) git_index_remove_bypath(git_index *index, const char *path);
* item in the working directory immediately *before* it is added to /
* updated in the index. Returning zero will add the item to the index,
* greater than zero will skip the item, and less than zero will abort the
* scan and cause GIT_EUSER to be returned.
* scan and return that value to the caller.
*
* @param index an existing index object
* @param pathspec array of path patterns
@ -502,7 +512,7 @@ GIT_EXTERN(int) git_index_remove_bypath(git_index *index, const char *path);
* gets index of matching pathspec entry); can be NULL;
* return 0 to add, >0 to skip, <0 to abort scan.
* @param payload payload passed through to callback function
* @return 0 or an error code
* @return 0 on success, negative callback return value, or error code
*/
GIT_EXTERN(int) git_index_add_all(
git_index *index,
@ -524,7 +534,7 @@ GIT_EXTERN(int) git_index_add_all(
* gets index of matching pathspec entry); can be NULL;
* return 0 to add, >0 to skip, <0 to abort scan.
* @param payload payload passed through to callback function
* @return 0 or an error code
* @return 0 on success, negative callback return value, or error code
*/
GIT_EXTERN(int) git_index_remove_all(
git_index *index,
@ -553,7 +563,7 @@ GIT_EXTERN(int) git_index_remove_all(
* gets index of matching pathspec entry); can be NULL;
* return 0 to add, >0 to skip, <0 to abort scan.
* @param payload payload passed through to callback function
* @return 0 or an error code
* @return 0 on success, negative callback return value, or error code
*/
GIT_EXTERN(int) git_index_update_all(
git_index *index,
@ -568,8 +578,7 @@ GIT_EXTERN(int) git_index_update_all(
* @param at_pos the address to which the position of the index entry is written (optional)
* @param index an existing index object
* @param path path to search
* @return a zero-based position in the index if found;
* GIT_ENOTFOUND otherwise
* @return a zero-based position in the index if found; GIT_ENOTFOUND otherwise
*/
GIT_EXTERN(int) git_index_find(size_t *at_pos, git_index *index, const char *path);
@ -613,6 +622,7 @@ GIT_EXTERN(int) git_index_conflict_add(
* @param their_out Pointer to store the their entry
* @param index an existing index object
* @param path path to search
* @return 0 or an error code
*/
GIT_EXTERN(int) git_index_conflict_get(
const git_index_entry **ancestor_out,
@ -625,16 +635,18 @@ GIT_EXTERN(int) git_index_conflict_get(
* Removes the index entries that represent a conflict of a single file.
*
* @param index an existing index object
* @param path to search
* @param path path to remove conflicts for
* @return 0 or an error code
*/
GIT_EXTERN(int) git_index_conflict_remove(git_index *index, const char *path);
/**
* Remove all conflicts in the index (entries with a stage greater than 0.)
* Remove all conflicts in the index (entries with a stage greater than 0).
*
* @param index an existing index object
* @return 0 or an error code
*/
GIT_EXTERN(void) git_index_conflict_cleanup(git_index *index);
GIT_EXTERN(int) git_index_conflict_cleanup(git_index *index);
/**
* Determine if the index contains entries representing file conflicts.
@ -644,9 +656,12 @@ GIT_EXTERN(void) git_index_conflict_cleanup(git_index *index);
GIT_EXTERN(int) git_index_has_conflicts(const git_index *index);
/**
* Create an iterator for the conflicts in the index. You may not modify the
* index while iterating, the results are undefined.
* Create an iterator for the conflicts in the index.
*
* The index must not be modified while iterating; the results are undefined.
*
* @param iterator_out The newly created conflict iterator
* @param index The index to scan
* @return 0 or an error code
*/
GIT_EXTERN(int) git_index_conflict_iterator_new(

View File

@ -32,7 +32,7 @@ GIT_EXTERN(int) git_indexer_new(
const char *path,
unsigned int mode,
git_odb *odb,
git_transfer_progress_callback progress_cb,
git_transfer_progress_cb progress_cb,
void *progress_cb_payload);
/**

View File

@ -22,71 +22,288 @@
*/
GIT_BEGIN_DECL
/**
* The file inputs to `git_merge_file`. Callers should populate the
* `git_merge_file_input` structure with descriptions of the files in
* each side of the conflict for use in producing the merge file.
*/
typedef struct {
unsigned int version;
/** Pointer to the contents of the file. */
const char *ptr;
/** Size of the contents pointed to in `ptr`. */
size_t size;
/** File name of the conflicted file, or `NULL` to not merge the path. */
const char *path;
/** File mode of the conflicted file, or `0` to not merge the mode. */
unsigned int mode;
} git_merge_file_input;
#define GIT_MERGE_FILE_INPUT_VERSION 1
#define GIT_MERGE_FILE_INPUT_INIT {GIT_MERGE_FILE_INPUT_VERSION}
/**
* Initializes a `git_merge_file_input` with default values. Equivalent to
* creating an instance with GIT_MERGE_FILE_INPUT_INIT.
*
* @param opts the `git_merge_file_input` instance to initialize.
* @param version the version of the struct; you should pass
* `GIT_MERGE_FILE_INPUT_VERSION` here.
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_merge_file_init_input(
git_merge_file_input *opts,
unsigned int version);
/**
* Flags for `git_merge_tree` options. A combination of these flags can be
* passed in via the `flags` value in the `git_merge_tree_opts`.
* passed in via the `flags` value in the `git_merge_options`.
*/
typedef enum {
/** Detect renames */
/**
* Detect renames that occur between the common ancestor and the "ours"
* side or the common ancestor and the "theirs" side. This will enable
* the ability to merge between a modified and renamed file.
*/
GIT_MERGE_TREE_FIND_RENAMES = (1 << 0),
} git_merge_tree_flag_t;
/**
* Automerge options for `git_merge_trees_opts`.
* Merge file favor options for `git_merge_options` instruct the file-level
* merging functionality how to deal with conflicting regions of the files.
*/
typedef enum {
GIT_MERGE_AUTOMERGE_NORMAL = 0,
GIT_MERGE_AUTOMERGE_NONE = 1,
GIT_MERGE_AUTOMERGE_FAVOR_OURS = 2,
GIT_MERGE_AUTOMERGE_FAVOR_THEIRS = 3,
} git_merge_automerge_flags;
/**
* When a region of a file is changed in both branches, a conflict
* will be recorded in the index so that `git_checkout` can produce
* a merge file with conflict markers in the working directory.
* This is the default.
*/
GIT_MERGE_FILE_FAVOR_NORMAL = 0,
/**
* When a region of a file is changed in both branches, the file
* created in the index will contain the "ours" side of any conflicting
* region. The index will not record a conflict.
*/
GIT_MERGE_FILE_FAVOR_OURS = 1,
/**
* When a region of a file is changed in both branches, the file
* created in the index will contain the "theirs" side of any conflicting
* region. The index will not record a conflict.
*/
GIT_MERGE_FILE_FAVOR_THEIRS = 2,
/**
* When a region of a file is changed in both branches, the file
* created in the index will contain each unique line from each side,
* which has the result of combining both files. The index will not
* record a conflict.
*/
GIT_MERGE_FILE_FAVOR_UNION = 3,
} git_merge_file_favor_t;
typedef enum {
/* Defaults */
GIT_MERGE_FILE_DEFAULT = 0,
/* Create standard conflicted merge files */
GIT_MERGE_FILE_STYLE_MERGE = (1 << 0),
/* Create diff3-style files */
GIT_MERGE_FILE_STYLE_DIFF3 = (1 << 1),
/* Condense non-alphanumeric regions for simplified diff file */
GIT_MERGE_FILE_SIMPLIFY_ALNUM = (1 << 2),
} git_merge_file_flags_t;
typedef struct {
unsigned int version;
/**
* Label for the ancestor file side of the conflict which will be prepended
* to labels in diff3-format merge files.
*/
const char *ancestor_label;
/**
* Label for our file side of the conflict which will be prepended
* to labels in merge files.
*/
const char *our_label;
/**
* Label for their file side of the conflict which will be prepended
* to labels in merge files.
*/
const char *their_label;
/** The file to favor in region conflicts. */
git_merge_file_favor_t favor;
/** Merge file flags. */
git_merge_file_flags_t flags;
} git_merge_file_options;
#define GIT_MERGE_FILE_OPTIONS_VERSION 1
#define GIT_MERGE_FILE_OPTIONS_INIT {GIT_MERGE_FILE_OPTIONS_VERSION}
/**
* Initializes a `git_merge_file_options` with default values. Equivalent to
* creating an instance with GIT_MERGE_FILE_OPTIONS_INIT.
*
* @param opts the `git_merge_file_options` instance to initialize.
* @param version the version of the struct; you should pass
* `GIT_MERGE_FILE_OPTIONS_VERSION` here.
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_merge_file_init_options(
git_merge_file_options *opts,
unsigned int version);
typedef struct {
/**
* True if the output was automerged, false if the output contains
* conflict markers.
*/
unsigned int automergeable;
/**
* The path that the resultant merge file should use, or NULL if a
* filename conflict would occur.
*/
const char *path;
/** The mode that the resultant merge file should use. */
unsigned int mode;
/** The contents of the merge. */
const char *ptr;
/** The length of the merge contents. */
size_t len;
} git_merge_file_result;
typedef struct {
unsigned int version;
git_merge_tree_flag_t flags;
/** Similarity to consider a file renamed (default 50) */
/**
* Similarity to consider a file renamed (default 50). If
* `GIT_MERGE_TREE_FIND_RENAMES` is enabled, added files will be compared
* with deleted files to determine their similarity. Files that are
* more similar than the rename threshold (percentage-wise) will be
* treated as a rename.
*/
unsigned int rename_threshold;
/** Maximum similarity sources to examine (overrides the
* `merge.renameLimit` config) (default 200)
/**
* Maximum similarity sources to examine for renames (default 200).
* If the number of rename candidates (add / delete pairs) is greater
* than this value, inexact rename detection is aborted.
*
* This setting overrides the `merge.renameLimit` configuration value.
*/
unsigned int target_limit;
/** Pluggable similarity metric; pass NULL to use internal metric */
git_diff_similarity_metric *metric;
/** Flags for automerging content. */
git_merge_automerge_flags automerge_flags;
} git_merge_tree_opts;
#define GIT_MERGE_TREE_OPTS_VERSION 1
#define GIT_MERGE_TREE_OPTS_INIT {GIT_MERGE_TREE_OPTS_VERSION}
/** Flags for handling conflicting content. */
git_merge_file_favor_t file_favor;
} git_merge_options;
#define GIT_MERGE_OPTIONS_VERSION 1
#define GIT_MERGE_OPTIONS_INIT {GIT_MERGE_OPTIONS_VERSION}
/**
* Option flags for `git_merge`.
* Initializes a `git_merge_options` with default values. Equivalent to
* creating an instance with GIT_MERGE_OPTIONS_INIT.
*
* GIT_MERGE_NO_FASTFORWARD - Do not fast-forward.
* @param opts the `git_merge_options` instance to initialize.
* @param version the version of the struct; you should pass
* `GIT_MERGE_OPTIONS_VERSION` here.
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_merge_init_options(
git_merge_options *opts,
unsigned int version);
/**
* The results of `git_merge_analysis` indicate the merge opportunities.
*/
typedef enum {
GIT_MERGE_NO_FASTFORWARD = 1,
GIT_MERGE_FASTFORWARD_ONLY = 2,
} git_merge_flags_t;
/** No merge is possible. (Unused.) */
GIT_MERGE_ANALYSIS_NONE = 0,
typedef struct {
unsigned int version;
/**
* A "normal" merge; both HEAD and the given merge input have diverged
* from their common ancestor. The divergent commits must be merged.
*/
GIT_MERGE_ANALYSIS_NORMAL = (1 << 0),
git_merge_flags_t merge_flags;
git_merge_tree_opts merge_tree_opts;
/**
* All given merge inputs are reachable from HEAD, meaning the
* repository is up-to-date and no merge needs to be performed.
*/
GIT_MERGE_ANALYSIS_UP_TO_DATE = (1 << 1),
git_checkout_opts checkout_opts;
} git_merge_opts;
/**
* The given merge input is a fast-forward from HEAD and no merge
* needs to be performed. Instead, the client can check out the
* given merge input.
*/
GIT_MERGE_ANALYSIS_FASTFORWARD = (1 << 2),
#define GIT_MERGE_OPTS_VERSION 1
#define GIT_MERGE_OPTS_INIT {GIT_MERGE_OPTS_VERSION, 0, GIT_MERGE_TREE_OPTS_INIT, GIT_CHECKOUT_OPTS_INIT}
/**
* The HEAD of the current repository is "unborn" and does not point to
* a valid commit. No merge can be performed, but the caller may wish
* to simply set HEAD to the target commit(s).
*/
GIT_MERGE_ANALYSIS_UNBORN = (1 << 3),
} git_merge_analysis_t;
typedef enum {
/*
* No configuration was found that suggests a preferred behavior for
* merge.
*/
GIT_MERGE_PREFERENCE_NONE = 0,
/**
* There is a `merge.ff=false` configuration setting, suggesting that
* the user does not want to allow a fast-forward merge.
*/
GIT_MERGE_PREFERENCE_NO_FASTFORWARD = (1 << 0),
/**
* There is a `merge.ff=only` configuration setting, suggesting that
* the user only wants fast-forward merges.
*/
GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY = (1 << 1),
} git_merge_preference_t;
/**
* Analyzes the given branch(es) and determines the opportunities for
* merging them into the HEAD of the repository.
*
* @param analysis_out analysis enumeration that the result is written into
* @param repo the repository to merge
* @param their_heads the heads to merge into
* @param their_heads_len the number of heads to merge
* @return 0 on success or error code
*/
GIT_EXTERN(int) git_merge_analysis(
git_merge_analysis_t *analysis_out,
git_merge_preference_t *preference_out,
git_repository *repo,
const git_merge_head **their_heads,
size_t their_heads_len);
/**
* Find a merge base between two commits
@ -95,7 +312,7 @@ typedef struct {
* @param repo the repository where the commits exist
* @param one one of the commits
* @param two the other commit
* @return Zero on success; GIT_ENOTFOUND or -1 on failure.
* @return 0 on success, GIT_ENOTFOUND if not found or error code
*/
GIT_EXTERN(int) git_merge_base(
git_oid *out,
@ -119,27 +336,44 @@ GIT_EXTERN(int) git_merge_base_many(
const git_oid input_array[]);
/**
* Creates a `git_merge_head` from the given reference
* Find a merge base in preparation for an octopus merge
*
* @param out the OID of a merge base considering all the commits
* @param repo the repository where the commits exist
* @param length The number of commits in the provided `input_array`
* @param input_array oids of the commits
* @return Zero on success; GIT_ENOTFOUND or -1 on failure.
*/
GIT_EXTERN(int) git_merge_base_octopus(
git_oid *out,
git_repository *repo,
size_t length,
const git_oid input_array[]);
/**
* Creates a `git_merge_head` from the given reference. The resulting
* git_merge_head must be freed with `git_merge_head_free`.
*
* @param out pointer to store the git_merge_head result in
* @param repo repository that contains the given reference
* @param ref reference to use as a merge input
* @return zero on success, -1 on failure.
* @return 0 on success or error code
*/
GIT_EXTERN(int) git_merge_head_from_ref(
git_merge_head **out,
git_repository *repo,
git_reference *ref);
const git_reference *ref);
/**
* Creates a `git_merge_head` from the given fetch head data
* Creates a `git_merge_head` from the given fetch head data. The resulting
* git_merge_head must be freed with `git_merge_head_free`.
*
* @param out pointer to store the git_merge_head result in
* @param repo repository that contains the given commit
* @param branch_name name of the (remote) branch
* @param remote_url url of the remote
* @param oid the commit object id to use as a merge input
* @return zero on success, -1 on failure.
* @return 0 on success or error code
*/
GIT_EXTERN(int) git_merge_head_from_fetchhead(
git_merge_head **out,
@ -149,29 +383,93 @@ GIT_EXTERN(int) git_merge_head_from_fetchhead(
const git_oid *oid);
/**
* Creates a `git_merge_head` from the given commit id
* Creates a `git_merge_head` from the given commit id. The resulting
* git_merge_head must be freed with `git_merge_head_free`.
*
* @param out pointer to store the git_merge_head result in
* @param repo repository that contains the given commit
* @param oid the commit object id to use as a merge input
* @return zero on success, -1 on failure.
* @param id the commit object id to use as a merge input
* @return 0 on success or error code
*/
GIT_EXTERN(int) git_merge_head_from_oid(
GIT_EXTERN(int) git_merge_head_from_id(
git_merge_head **out,
git_repository *repo,
const git_oid *oid);
const git_oid *id);
/**
* Frees a `git_merge_head`
* Gets the commit ID that the given `git_merge_head` refers to.
*
* @param head the given merge head
* @return commit id
*/
GIT_EXTERN(const git_oid *) git_merge_head_id(
const git_merge_head *head);
/**
* Frees a `git_merge_head`.
*
* @param head merge head to free
*/
GIT_EXTERN(void) git_merge_head_free(
git_merge_head *head);
/**
* Merge two files as they exist in the in-memory data structures, using
* the given common ancestor as the baseline, producing a
* `git_merge_file_result` that reflects the merge result. The
* `git_merge_file_result` must be freed with `git_merge_file_result_free`.
*
* Note that this function does not reference a repository and any
* configuration must be passed as `git_merge_file_options`.
*
* @param out The git_merge_file_result to be filled in
* @param ancestor The contents of the ancestor file
* @param ours The contents of the file in "our" side
* @param theirs The contents of the file in "their" side
* @param opts The merge file options or `NULL` for defaults
* @return 0 on success or error code
*/
GIT_EXTERN(int) git_merge_file(
git_merge_file_result *out,
const git_merge_file_input *ancestor,
const git_merge_file_input *ours,
const git_merge_file_input *theirs,
const git_merge_file_options *opts);
/**
* Merge two files as they exist in the index, using the given common
* ancestor as the baseline, producing a `git_merge_file_result` that
* reflects the merge result. The `git_merge_file_result` must be freed with
* `git_merge_file_result_free`.
*
* @param out The git_merge_file_result to be filled in
* @param repo The repository
* @param ancestor The index entry for the ancestor file (stage level 1)
* @param ours The index entry for our file (stage level 2)
* @param theirs The index entry for their file (stage level 3)
* @param opts The merge file options or NULL
* @return 0 on success or error code
*/
GIT_EXTERN(int) git_merge_file_from_index(
git_merge_file_result *out,
git_repository *repo,
const git_index_entry *ancestor,
const git_index_entry *ours,
const git_index_entry *theirs,
const git_merge_file_options *opts);
/**
* Frees a `git_merge_file_result`.
*
* @param result The result to free or `NULL`
*/
GIT_EXTERN(void) git_merge_file_result_free(git_merge_file_result *result);
/**
* Merge two trees, producing a `git_index` that reflects the result of
* the merge.
* the merge. The index may be written as-is to the working directory
* or checked out. If the index is to be converted to a tree, the caller
* should resolve any conflicts that arose as part of the merge.
*
* The returned index must be freed explicitly with `git_index_free`.
*
@ -181,7 +479,7 @@ GIT_EXTERN(void) git_merge_head_free(
* @param our_tree the tree that reflects the destination tree
* @param their_tree the tree to merge in to `our_tree`
* @param opts the merge tree options (or null for defaults)
* @return zero on success, -1 on failure.
* @return 0 on success or error code
*/
GIT_EXTERN(int) git_merge_trees(
git_index **out,
@ -189,44 +487,49 @@ GIT_EXTERN(int) git_merge_trees(
const git_tree *ancestor_tree,
const git_tree *our_tree,
const git_tree *their_tree,
const git_merge_tree_opts *opts);
const git_merge_options *opts);
/**
* Merges the given commits into HEAD, producing a new commit.
* Merge two commits, producing a `git_index` that reflects the result of
* the merge. The index may be written as-is to the working directory
* or checked out. If the index is to be converted to a tree, the caller
* should resolve any conflicts that arose as part of the merge.
*
* The returned index must be freed explicitly with `git_index_free`.
*
* @param out pointer to store the index result in
* @param repo repository that contains the given trees
* @param our_commit the commit that reflects the destination tree
* @param their_commit the commit to merge in to `our_commit`
* @param opts the merge tree options (or null for defaults)
* @return 0 on success or error code
*/
GIT_EXTERN(int) git_merge_commits(
git_index **out,
git_repository *repo,
const git_commit *our_commit,
const git_commit *their_commit,
const git_merge_options *opts);
/**
* Merges the given commit(s) into HEAD, writing the results into the working
* directory. Any changes are staged for commit and any conflicts are written
* to the index. Callers should inspect the repository's index after this
* completes, resolve any conflicts and prepare a commit.
*
* @param out the results of the merge
* @param repo the repository to merge
* @param merge_heads the heads to merge into
* @param merge_heads_len the number of heads to merge
* @param flags merge flags
* @param their_heads the heads to merge into
* @param their_heads_len the number of heads to merge
* @param merge_opts merge options
* @param checkout_opts checkout options
* @return 0 on success or error code
*/
GIT_EXTERN(int) git_merge(
git_merge_result **out,
git_repository *repo,
const git_merge_head **their_heads,
size_t their_heads_len,
const git_merge_opts *opts);
/**
* Returns true if a merge is up-to-date (we were asked to merge the target
* into itself.)
*/
GIT_EXTERN(int) git_merge_result_is_uptodate(git_merge_result *merge_result);
/**
* Returns true if a merge is eligible for fastforward
*/
GIT_EXTERN(int) git_merge_result_is_fastforward(git_merge_result *merge_result);
/**
* Gets the fast-forward OID if the merge was a fastforward.
*
* @param out the OID of the fast-forward
* @param merge_result the results of the merge
*/
GIT_EXTERN(int) git_merge_result_fastforward_oid(git_oid *out, git_merge_result *merge_result);
GIT_EXTERN(void) git_merge_result_free(git_merge_result *merge_result);
const git_merge_options *merge_opts,
const git_checkout_options *checkout_opts);
/** @} */
GIT_END_DECL

View File

@ -8,6 +8,7 @@
#define INCLUDE_git_message_h__
#include "common.h"
#include "buffer.h"
/**
* @file git2/message.h
@ -23,25 +24,19 @@ GIT_BEGIN_DECL
*
* Optionally, can remove lines starting with a "#".
*
* @param out The user-allocated buffer which will be filled with the
* cleaned up message. Pass NULL if you just want to get the needed
* size of the prettified message as the output value.
*
* @param out_size Size of the `out` buffer in bytes.
* @param out The user-allocated git_buf which will be filled with the
* cleaned up message.
*
* @param message The message to be prettified.
*
* @param strip_comments Non-zero to remove lines starting with "#", 0 to
* leave them in.
* @param strip_comments Non-zero to remove comment lines, 0 to leave them in.
*
* @return -1 on error, else number of characters in prettified message
* including the trailing NUL byte
* @param comment_char Comment character. Lines starting with this character
* are considered to be comments and removed if `strip_comments` is non-zero.
*
* @return 0 or an error code.
*/
GIT_EXTERN(int) git_message_prettify(
char *out,
size_t out_size,
const char *message,
int strip_comments);
GIT_EXTERN(int) git_message_prettify(git_buf *out, const char *message, int strip_comments, char comment_char);
/** @} */
GIT_END_DECL

View File

@ -41,6 +41,11 @@ struct git_remote_head {
git_oid oid;
git_oid loid;
char *name;
/**
* If the server send a symref mapping for this ref, this will
* point to the target.
*/
char *symref_target;
};
/**

View File

@ -106,12 +106,12 @@ GIT_EXTERN(const char *) git_note_message(const git_note *note);
/**
* Get the note object OID
* Get the note object's id
*
* @param note the note
* @return the note object OID
* @return the note object's id
*/
GIT_EXTERN(const git_oid *) git_note_oid(const git_note *note);
GIT_EXTERN(const git_oid *) git_note_id(const git_note *note);
/**
* Add a note for an object
@ -189,7 +189,7 @@ GIT_EXTERN(int) git_note_default_ref(const char **out, git_repository *repo);
*
* @param payload Extra parameter to callback function.
*
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_note_foreach(
git_repository *repo,

View File

@ -10,6 +10,7 @@
#include "common.h"
#include "types.h"
#include "oid.h"
#include "buffer.h"
/**
* @file git2/object.h
@ -103,6 +104,20 @@ GIT_EXTERN(int) git_object_lookup_bypath(
*/
GIT_EXTERN(const git_oid *) git_object_id(const git_object *obj);
/**
* Get a short abbreviated OID string for the object
*
* This starts at the "core.abbrev" length (default 7 characters) and
* iteratively extends to a longer string if that length is ambiguous.
* The result will be unambiguous (at least until new objects are added to
* the repository).
*
* @param out Buffer to write string into
* @param obj The object to get an ID for
* @return 0 on success, <0 for error
*/
GIT_EXTERN(int) git_object_short_id(git_buf *out, const git_object *obj);
/**
* Get the object type of an object
*
@ -143,7 +158,7 @@ GIT_EXTERN(git_repository *) git_object_owner(const git_object *obj);
GIT_EXTERN(void) git_object_free(git_object *object);
/**
* Convert an object type to it's string representation.
* Convert an object type to its string representation.
*
* The result is a pointer to a string in static memory and
* should not be free()'ed.

View File

@ -158,6 +158,19 @@ GIT_EXTERN(int) git_odb_read_header(size_t *len_out, git_otype *type_out, git_od
*/
GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
/**
* Determine if objects can be found in the object database from a short OID.
*
* @param out The full OID of the found object if just one is found.
* @param db The database to be searched for the given object.
* @param short_id A prefix of the id of the object to read.
* @param len The length of the prefix.
* @return 0 if found, GIT_ENOTFOUND if not found, GIT_EAMBIGUOUS if multiple
* matches were found, other value < 0 if there was a read error.
*/
GIT_EXTERN(int) git_odb_exists_prefix(
git_oid *out, git_odb *db, const git_oid *short_id, size_t len);
/**
* Refresh the object database to load newly added files.
*
@ -189,7 +202,7 @@ GIT_EXTERN(int) git_odb_refresh(struct git_odb *db);
* @param db database to use
* @param cb the callback to call for each object
* @param payload data to pass to the callback
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload);
@ -325,7 +338,7 @@ GIT_EXTERN(int) git_odb_open_rstream(git_odb_stream **out, git_odb *db, const gi
GIT_EXTERN(int) git_odb_write_pack(
git_odb_writepack **out,
git_odb *db,
git_transfer_progress_callback progress_cb,
git_transfer_progress_cb progress_cb,
void *progress_payload);
/**

View File

@ -167,10 +167,7 @@ GIT_EXTERN(int) git_oid_cmp(const git_oid *a, const git_oid *b);
* @param b second oid structure.
* @return true if equal, false otherwise
*/
GIT_INLINE(int) git_oid_equal(const git_oid *a, const git_oid *b)
{
return !git_oid_cmp(a, b);
}
GIT_EXTERN(int) git_oid_equal(const git_oid *a, const git_oid *b);
/**
* Compare the first 'len' hexadecimal characters (packets of 4 bits)

View File

@ -52,7 +52,7 @@ typedef enum {
GIT_PACKBUILDER_ADDING_OBJECTS = 0,
GIT_PACKBUILDER_DELTAFICATION = 1,
} git_packbuilder_stage_t;
/**
* Initialize a new packbuilder
*
@ -114,6 +114,17 @@ GIT_EXTERN(int) git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *
*/
GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *id);
/**
* Write the contents of the packfile to an in-memory buffer
*
* The contents of the buffer will become a valid packfile, even though there
* will be no attached index
*
* @param buf Buffer where to write the packfile
* @param pb The packbuilder
*/
GIT_EXTERN(int) git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb);
/**
* Write the new pack and corresponding index file to path.
*
@ -129,7 +140,7 @@ GIT_EXTERN(int) git_packbuilder_write(
git_packbuilder *pb,
const char *path,
unsigned int mode,
git_transfer_progress_callback progress_cb,
git_transfer_progress_cb progress_cb,
void *progress_cb_payload);
/**
@ -143,6 +154,7 @@ GIT_EXTERN(int) git_packbuilder_write(
GIT_EXTERN(const git_oid *) git_packbuilder_hash(git_packbuilder *pb);
typedef int (*git_packbuilder_foreach_cb)(void *buf, size_t size, void *payload);
/**
* Create the new pack and pass each object to the callback
*

View File

@ -105,6 +105,34 @@ GIT_EXTERN(int) git_patch_from_blob_and_buffer(
const char *buffer_as_path,
const git_diff_options *opts);
/**
* Directly generate a patch from the difference between two buffers.
*
* This is just like `git_diff_buffers()` except it generates a patch
* object for the difference instead of directly making callbacks. You can
* use the standard `git_patch` accessor functions to read the patch
* data, and you must call `git_patch_free()` on the patch when done.
*
* @param out The generated patch; NULL on error
* @param old_buffer Raw data for old side of diff, or NULL for empty
* @param old_len Length of the raw data for old side of the diff
* @param old_as_path Treat old buffer as if it had this filename; can be NULL
* @param new_buffer Raw data for new side of diff, or NULL for empty
* @param new_len Length of raw data for new side of diff
* @param new_as_path Treat buffer as if it had this filename; can be NULL
* @param opts Options for diff, or NULL for default options
* @return 0 on success or error code < 0
*/
GIT_EXTERN(int) git_patch_from_buffers(
git_patch **out,
const void *old_buffer,
size_t old_len,
const char *old_as_path,
const char *new_buffer,
size_t new_len,
const char *new_as_path,
const git_diff_options *opts);
/**
* Free a git_patch object.
*/
@ -113,12 +141,12 @@ GIT_EXTERN(void) git_patch_free(git_patch *patch);
/**
* Get the delta associated with a patch
*/
GIT_EXTERN(const git_diff_delta *) git_patch_get_delta(git_patch *patch);
GIT_EXTERN(const git_diff_delta *) git_patch_get_delta(const git_patch *patch);
/**
* Get the number of hunks in a patch
*/
GIT_EXTERN(size_t) git_patch_num_hunks(git_patch *patch);
GIT_EXTERN(size_t) git_patch_num_hunks(const git_patch *patch);
/**
* Get line counts of each type in a patch.
@ -169,7 +197,7 @@ GIT_EXTERN(int) git_patch_get_hunk(
* @return Number of lines in hunk or -1 if invalid hunk index
*/
GIT_EXTERN(int) git_patch_num_lines_in_hunk(
git_patch *patch,
const git_patch *patch,
size_t hunk_idx);
/**
@ -218,13 +246,13 @@ GIT_EXTERN(size_t) git_patch_size(
* Serialize the patch to text via callback.
*
* Returning a non-zero value from the callback will terminate the iteration
* and cause this return `GIT_EUSER`.
* and return that value to the caller.
*
* @param patch A git_patch representing changes to one file
* @param print_cb Callback function to output lines of the patch. Will be
* called for file headers, hunk headers, and diff lines.
* @param payload Reference pointer that will be passed to your callbacks.
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_patch_print(
git_patch *patch,
@ -234,15 +262,14 @@ GIT_EXTERN(int) git_patch_print(
/**
* Get the content of a patch as a single diff text.
*
* @param string Allocated string; caller must free.
* @param out The git_buf to be filled in
* @param patch A git_patch representing changes to one file
* @return 0 on success, <0 on failure.
*/
GIT_EXTERN(int) git_patch_to_str(
char **string,
GIT_EXTERN(int) git_patch_to_buf(
git_buf *out,
git_patch *patch);
GIT_END_DECL
/**@}*/

View File

@ -12,6 +12,8 @@
#include "strarray.h"
#include "diff.h"
GIT_BEGIN_DECL
/**
* Compiled pathspec
*/
@ -257,4 +259,5 @@ GIT_EXTERN(size_t) git_pathspec_match_list_failed_entrycount(
GIT_EXTERN(const char *) git_pathspec_match_list_failed_entry(
const git_pathspec_match_list *m, size_t pos);
GIT_END_DECL
#endif

View File

@ -39,6 +39,19 @@ typedef struct {
#define GIT_PUSH_OPTIONS_VERSION 1
#define GIT_PUSH_OPTIONS_INIT { GIT_PUSH_OPTIONS_VERSION }
/**
* Initializes a `git_push_options` with default values. Equivalent to
* creating an instance with GIT_PUSH_OPTIONS_INIT.
*
* @param opts the `git_push_options` instance to initialize.
* @param version the version of the struct; you should pass
* `GIT_PUSH_OPTIONS_VERSION` here.
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_push_init_options(
git_push_options *opts,
unsigned int version);
/** Push network progress notification function */
typedef int (*git_push_transfer_progress)(
unsigned int current,
@ -103,10 +116,16 @@ GIT_EXTERN(int) git_push_add_refspec(git_push *push, const char *refspec);
* Update remote tips after a push
*
* @param push The push object
* @param signature The identity to use when updating reflogs
* @param reflog_message The message to insert into the reflogs. If NULL, the
* default is "update by push".
*
* @return 0 or an error code
*/
GIT_EXTERN(int) git_push_update_tips(git_push *push);
GIT_EXTERN(int) git_push_update_tips(
git_push *push,
const git_signature *signature,
const char *reflog_message);
/**
* Actually push all given refspecs
@ -129,20 +148,22 @@ GIT_EXTERN(int) git_push_finish(git_push *push);
*
* @return true if remote side successfully unpacked, false otherwise
*/
GIT_EXTERN(int) git_push_unpack_ok(git_push *push);
GIT_EXTERN(int) git_push_unpack_ok(const git_push *push);
/**
* Call callback `cb' on each status
* Invoke callback `cb' on each status entry
*
* For each of the updated references, we receive a status report in the
* form of `ok refs/heads/master` or `ng refs/heads/master <msg>`.
* `msg != NULL` means the reference has not been updated for the given
* reason.
*
* Return a non-zero value from the callback to stop the loop.
*
* @param push The push object
* @param cb The callback to call on each object
*
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_push_status_foreach(git_push *push,
int (*cb)(const char *ref, const char *msg, void *data),

View File

@ -47,7 +47,7 @@ GIT_EXTERN(int) git_reflog_read(git_reflog **out, git_repository *repo, const c
GIT_EXTERN(int) git_reflog_write(git_reflog *reflog);
/**
* Add a new entry to the reflog.
* Add a new entry to the in-memory reflog.
*
* `msg` is optional and can be NULL.
*
@ -59,23 +59,6 @@ GIT_EXTERN(int) git_reflog_write(git_reflog *reflog);
*/
GIT_EXTERN(int) git_reflog_append(git_reflog *reflog, const git_oid *id, const git_signature *committer, const char *msg);
/**
* Add a new entry to the named reflog.
*
* This utility function loads the named reflog, appends to it and
* writes it back out to the backend.
*
* `msg` is optional and can be NULL.
*
* @param repo the repository to act on
* @param name the reflog's name
* @param id the OID the reference is now pointing to
* @param committer the signature of the committer
* @param msg the reflog message
* @return 0 or an error code
*/
GIT_EXTERN(int) git_reflog_append_to(git_repository *repo, const char *name, const git_oid *id, const git_signature *committer, const char *msg);
/**
* Rename a reflog
*
@ -86,7 +69,7 @@ GIT_EXTERN(int) git_reflog_append_to(git_repository *repo, const char *name, con
*
* @param repo the repository
* @param old_name the old name of the reference
* @param new_name the new name of the reference
* @param name the new name of the reference
* @return 0 on success, GIT_EINVALIDSPEC or an error code
*/
GIT_EXTERN(int) git_reflog_rename(git_repository *repo, const char *old_name, const char *name);

View File

@ -27,7 +27,7 @@ GIT_BEGIN_DECL
* The returned reference must be freed by the user.
*
* The name will be checked for validity.
* See `git_reference_create_symbolic()` for rules about valid names.
* See `git_reference_symbolic_create()` for rules about valid names.
*
* @param out pointer to the looked-up reference
* @param repo the repository to look up the reference
@ -67,6 +67,48 @@ GIT_EXTERN(int) git_reference_name_to_id(
*/
GIT_EXTERN(int) git_reference_dwim(git_reference **out, git_repository *repo, const char *shorthand);
/**
* Conditionally create a new symbolic reference.
*
* A symbolic reference is a reference name that refers to another
* reference name. If the other name moves, the symbolic name will move,
* too. As a simple example, the "HEAD" reference might refer to
* "refs/heads/master" while on the "master" branch of a repository.
*
* The symbolic reference will be created in the repository and written to
* the disk. The generated reference object must be freed by the user.
*
* Valid reference names must follow one of two patterns:
*
* 1. Top-level names must contain only capital letters and underscores,
* and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
* 2. Names prefixed with "refs/" can be almost anything. You must avoid
* the characters '~', '^', ':', '\\', '?', '[', and '*', and the
* sequences ".." and "@{" which have special meaning to revparse.
*
* This function will return an error if a reference already exists with the
* given name unless `force` is true, in which case it will be overwritten.
*
* The signature and message for the reflog will be ignored if the
* reference does not belong in the standard set (HEAD, branches and
* remote-tracking branches) and it does not have a reflog.
*
* It will return GIT_EMODIFIED if the reference's value at the time
* of updating does not match the one passed through `current_value`
* (i.e. if the ref has changed since the user read it).
*
* @param out Pointer to the newly created reference
* @param repo Repository where that reference will live
* @param name The name of the reference
* @param target The target of the reference
* @param force Overwrite existing references
* @param current_value The expected value of the reference when updating
* @param signature The identity that will used to populate the reflog entry
* @param log_message The one line long message to be appended to the reflog
* @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC, GIT_EMODIFIED or an error code
*/
GIT_EXTERN(int) git_reference_symbolic_create_matching(git_reference **out, git_repository *repo, const char *name, const char *target, int force, const char *current_value, const git_signature *signature, const char *log_message);
/**
* Create a new symbolic reference.
*
@ -89,14 +131,20 @@ GIT_EXTERN(int) git_reference_dwim(git_reference **out, git_repository *repo, co
* This function will return an error if a reference already exists with the
* given name unless `force` is true, in which case it will be overwritten.
*
* The signature and message for the reflog will be ignored if the
* reference does not belong in the standard set (HEAD, branches and
* remote-tracking branches) and it does not have a reflog.
*
* @param out Pointer to the newly created reference
* @param repo Repository where that reference will live
* @param name The name of the reference
* @param target The target of the reference
* @param force Overwrite existing references
* @param signature The identity that will used to populate the reflog entry
* @param log_message The one line long message to be appended to the reflog
* @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
*/
GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repository *repo, const char *name, const char *target, int force);
GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repository *repo, const char *name, const char *target, int force, const git_signature *signature, const char *log_message);
/**
* Create a new direct reference.
@ -121,14 +169,64 @@ GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repositor
* This function will return an error if a reference already exists with the
* given name unless `force` is true, in which case it will be overwritten.
*
* The signature and message for the reflog will be ignored if the
* reference does not belong in the standard set (HEAD, branches and
* remote-tracking branches) and and it does not have a reflog.
*
* @param out Pointer to the newly created reference
* @param repo Repository where that reference will live
* @param name The name of the reference
* @param id The object id pointed to by the reference.
* @param force Overwrite existing references
* @param signature The identity that will used to populate the reflog entry
* @param log_message The one line long message to be appended to the reflog
* @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
*/
GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force);
GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force, const git_signature *signature, const char *log_message);
/**
* Conditionally create new direct reference
*
* A direct reference (also called an object id reference) refers directly
* to a specific object id (a.k.a. OID or SHA) in the repository. The id
* permanently refers to the object (although the reference itself can be
* moved). For example, in libgit2 the direct ref "refs/tags/v0.17.0"
* refers to OID 5b9fac39d8a76b9139667c26a63e6b3f204b3977.
*
* The direct reference will be created in the repository and written to
* the disk. The generated reference object must be freed by the user.
*
* Valid reference names must follow one of two patterns:
*
* 1. Top-level names must contain only capital letters and underscores,
* and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
* 2. Names prefixed with "refs/" can be almost anything. You must avoid
* the characters '~', '^', ':', '\\', '?', '[', and '*', and the
* sequences ".." and "@{" which have special meaning to revparse.
*
* This function will return an error if a reference already exists with the
* given name unless `force` is true, in which case it will be overwritten.
*
* The signature and message for the reflog will be ignored if the
* reference does not belong in the standard set (HEAD, branches and
* remote-tracking branches) and and it does not have a reflog.
*
* It will return GIT_EMODIFIED if the reference's value at the time
* of updating does not match the one passed through `current_id`
* (i.e. if the ref has changed since the user read it).
*
* @param out Pointer to the newly created reference
* @param repo Repository where that reference will live
* @param name The name of the reference
* @param id The object id pointed to by the reference.
* @param force Overwrite existing references
* @param current_id The expected value of the reference at the time of update
* @param signature The identity that will used to populate the reflog entry
* @param log_message The one line long message to be appended to the reflog
* @return 0 on success, GIT_EMODIFIED if the value of the reference
* has changed, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
*/
GIT_EXTERN(int) git_reference_create_matching(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force, const git_oid *current_id, const git_signature *signature, const char *log_message);
/**
* Get the OID pointed to by a direct reference.
@ -179,7 +277,7 @@ GIT_EXTERN(git_ref_t) git_reference_type(const git_reference *ref);
/**
* Get the full name of a reference.
*
* See `git_reference_create_symbolic()` for rules about valid names.
* See `git_reference_symbolic_create()` for rules about valid names.
*
* @param ref The reference
* @return the full name for the ref
@ -220,20 +318,28 @@ GIT_EXTERN(git_repository *) git_reference_owner(const git_reference *ref);
* The new reference will be written to disk, overwriting the given reference.
*
* The target name will be checked for validity.
* See `git_reference_create_symbolic()` for rules about valid names.
* See `git_reference_symbolic_create()` for rules about valid names.
*
* The signature and message for the reflog will be ignored if the
* reference does not belong in the standard set (HEAD, branches and
* remote-tracking branches) and and it does not have a reflog.
*
* @param out Pointer to the newly created reference
* @param ref The reference
* @param target The new target for the reference
* @param signature The identity that will used to populate the reflog entry
* @param log_message The one line long message to be appended to the reflog
* @return 0 on success, GIT_EINVALIDSPEC or an error code
*/
GIT_EXTERN(int) git_reference_symbolic_set_target(
git_reference **out,
git_reference *ref,
const char *target);
const char *target,
const git_signature *signature,
const char *log_message);
/**
* Create a new reference with the same name as the given reference but a
* Conditionally create a new reference with the same name as the given reference but a
* different OID target. The reference must be a direct reference, otherwise
* this will fail.
*
@ -242,12 +348,17 @@ GIT_EXTERN(int) git_reference_symbolic_set_target(
* @param out Pointer to the newly created reference
* @param ref The reference
* @param id The new target OID for the reference
* @return 0 or an error code
* @param signature The identity that will used to populate the reflog entry
* @param log_message The one line long message to be appended to the reflog
* @return 0 on success, GIT_EMODIFIED if the value of the reference
* has changed since it was read, or an error code
*/
GIT_EXTERN(int) git_reference_set_target(
git_reference **out,
git_reference *ref,
const git_oid *id);
const git_oid *id,
const git_signature *signature,
const char *log_message);
/**
* Rename an existing reference.
@ -255,7 +366,7 @@ GIT_EXTERN(int) git_reference_set_target(
* This method works for both direct and symbolic references.
*
* The new name will be checked for validity.
* See `git_reference_create_symbolic()` for rules about valid names.
* See `git_reference_symbolic_create()` for rules about valid names.
*
* If the `force` flag is not enabled, and there's already
* a reference with the given name, the renaming will fail.
@ -268,6 +379,8 @@ GIT_EXTERN(int) git_reference_set_target(
* @param ref The reference to rename
* @param new_name The new name for the reference
* @param force Overwrite an existing reference
* @param signature The identity that will used to populate the reflog entry
* @param log_message The one line long message to be appended to the reflog
* @return 0 on success, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
*
*/
@ -275,7 +388,9 @@ GIT_EXTERN(int) git_reference_rename(
git_reference **new_ref,
git_reference *ref,
const char *new_name,
int force);
int force,
const git_signature *signature,
const char *log_message);
/**
* Delete an existing reference.
@ -284,11 +399,25 @@ GIT_EXTERN(int) git_reference_rename(
* will be immediately removed on disk but the memory will not be freed.
* Callers must call `git_reference_free`.
*
* This function will return an error if the reference has changed
* from the time it was looked up.
*
* @param ref The reference to remove
* @return 0 or an error code
* @return 0, GIT_EMODIFIED or an error code
*/
GIT_EXTERN(int) git_reference_delete(git_reference *ref);
/**
* Delete an existing reference by name
*
* This method removes the named reference from the repository without
* looking at its old value.
*
* @param name The reference to remove
* @return 0 or an error code
*/
GIT_EXTERN(int) git_reference_remove(git_repository *repo, const char *name);
/**
* Fill a list with all the references that can be found in a repository.
*
@ -310,20 +439,33 @@ typedef int (*git_reference_foreach_name_cb)(const char *name, void *payload);
* Perform a callback on each reference in the repository.
*
* The `callback` function will be called for each reference in the
* repository, receiving the name of the reference and the `payload` value
* repository, receiving the reference object and the `payload` value
* passed to this method. Returning a non-zero value from the callback
* will terminate the iteration.
*
* @param repo Repository where to find the refs
* @param callback Function which will be called for every listed ref
* @param payload Additional data to pass to the callback
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_reference_foreach(
git_repository *repo,
git_reference_foreach_cb callback,
void *payload);
/**
* Perform a callback on the fully-qualified name of each reference.
*
* The `callback` function will be called for each reference in the
* repository, receiving the name of the reference and the `payload` value
* passed to this method. Returning a non-zero value from the callback
* will terminate the iteration.
*
* @param repo Repository where to find the refs
* @param callback Function which will be called for every listed ref name
* @param payload Additional data to pass to the callback
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_reference_foreach_name(
git_repository *repo,
git_reference_foreach_name_cb callback,
@ -343,7 +485,9 @@ GIT_EXTERN(void) git_reference_free(git_reference *ref);
* @param ref2 The second git_reference
* @return 0 if the same, else a stable but meaningless ordering.
*/
GIT_EXTERN(int) git_reference_cmp(git_reference *ref1, git_reference *ref2);
GIT_EXTERN(int) git_reference_cmp(
const git_reference *ref1,
const git_reference *ref2);
/**
* Create an iterator for the repo's references
@ -379,6 +523,17 @@ GIT_EXTERN(int) git_reference_iterator_glob_new(
*/
GIT_EXTERN(int) git_reference_next(git_reference **out, git_reference_iterator *iter);
/**
* Get the next reference's name
*
* This function is provided for convenience in case only the names
* are interesting as it avoids the allocation of the `git_reference`
* object which `git_reference_next()` needs.
*
* @param out pointer in which to store the string
* @param iter the iterator
* @return 0, GIT_ITEROVER if there are no more; or an error code
*/
GIT_EXTERN(int) git_reference_next_name(const char **out, git_reference_iterator *iter);
/**
@ -415,12 +570,24 @@ GIT_EXTERN(int) git_reference_foreach_glob(
/**
* Check if a reflog exists for the specified reference.
*
* @param ref A git reference
*
* @param repo the repository
* @param refname the reference's name
* @return 0 when no reflog can be found, 1 when it exists;
* otherwise an error code.
*/
GIT_EXTERN(int) git_reference_has_log(git_reference *ref);
GIT_EXTERN(int) git_reference_has_log(git_repository *repo, const char *refname);
/**
* Ensure there is a reflog for a particular reference.
*
* Make sure that successive updates to the reference will append to
* its log.
*
* @param repo the repository
* @param refname the reference's name
* @return 0 or an error code.
*/
GIT_EXTERN(int) git_reference_ensure_log(git_repository *repo, const char *refname);
/**
* Check if a reference is a local branch.
@ -430,7 +597,7 @@ GIT_EXTERN(int) git_reference_has_log(git_reference *ref);
* @return 1 when the reference lives in the refs/heads
* namespace; 0 otherwise.
*/
GIT_EXTERN(int) git_reference_is_branch(git_reference *ref);
GIT_EXTERN(int) git_reference_is_branch(const git_reference *ref);
/**
* Check if a reference is a remote tracking branch
@ -440,7 +607,7 @@ GIT_EXTERN(int) git_reference_is_branch(git_reference *ref);
* @return 1 when the reference lives in the refs/remotes
* namespace; 0 otherwise.
*/
GIT_EXTERN(int) git_reference_is_remote(git_reference *ref);
GIT_EXTERN(int) git_reference_is_remote(const git_reference *ref);
/**
* Check if a reference is a tag
@ -450,7 +617,17 @@ GIT_EXTERN(int) git_reference_is_remote(git_reference *ref);
* @return 1 when the reference lives in the refs/tags
* namespace; 0 otherwise.
*/
GIT_EXTERN(int) git_reference_is_tag(git_reference *ref);
GIT_EXTERN(int) git_reference_is_tag(const git_reference *ref);
/**
* Check if a reference is a note
*
* @param ref A git reference
*
* @return 1 when the reference lives in the refs/notes
* namespace; 0 otherwise.
*/
GIT_EXTERN(int) git_reference_is_note(const git_reference *ref);
typedef enum {
GIT_REF_FORMAT_NORMAL = 0u,
@ -490,7 +667,7 @@ typedef enum {
* Once normalized, if the reference name is valid, it will be returned in
* the user allocated buffer.
*
* See `git_reference_create_symbolic()` for rules about valid names.
* See `git_reference_symbolic_create()` for rules about valid names.
*
* @param buffer_out User allocated buffer to store normalized name
* @param buffer_size Size of buffer_out
@ -554,7 +731,7 @@ GIT_EXTERN(int) git_reference_is_valid_name(const char *refname);
* @param ref a reference
* @return the human-readable version of the name
*/
GIT_EXTERN(const char *) git_reference_shorthand(git_reference *ref);
GIT_EXTERN(const char *) git_reference_shorthand(const git_reference *ref);
/** @} */

View File

@ -10,6 +10,7 @@
#include "common.h"
#include "types.h"
#include "net.h"
#include "buffer.h"
/**
* @file git2/refspec.h
@ -82,23 +83,21 @@ GIT_EXTERN(int) git_refspec_dst_matches(const git_refspec *refspec, const char *
* Transform a reference to its target following the refspec's rules
*
* @param out where to store the target name
* @param outlen the size of the `out` buffer
* @param spec the refspec
* @param name the name of the reference to transform
* @return 0, GIT_EBUFS or another error
*/
GIT_EXTERN(int) git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name);
GIT_EXTERN(int) git_refspec_transform(git_buf *out, const git_refspec *spec, const char *name);
/**
* Transform a target reference to its source reference following the refspec's rules
*
* @param out where to store the source reference name
* @param outlen the size of the `out` buffer
* @param spec the refspec
* @param name the name of the reference to transform
* @return 0, GIT_EBUFS or another error
*/
GIT_EXTERN(int) git_refspec_rtransform(char *out, size_t outlen, const git_refspec *spec, const char *name);
GIT_EXTERN(int) git_refspec_rtransform(git_buf *out, const git_refspec *spec, const char *name);
GIT_END_DECL

View File

@ -62,10 +62,10 @@ GIT_EXTERN(int) git_remote_create_with_fetchspec(
const char *fetch);
/**
* Create a remote in memory
* Create an anonymous remote
*
* Create a remote with the given refspec in memory. You can use
* this when you have a URL instead of a remote's name. Note that in-memory
* Create a remote with the given url and refspec in memory. You can use
* this when you have a URL instead of a remote's name. Note that anonymous
* remotes cannot be converted to persisted remotes.
*
* The name, when provided, will be checked for validity.
@ -73,15 +73,15 @@ GIT_EXTERN(int) git_remote_create_with_fetchspec(
*
* @param out pointer to the new remote object
* @param repo the associated repository
* @param fetch the fetch refspec to use for this remote.
* @param url the remote repository's URL
* @param fetch the fetch refspec to use for this remote.
* @return 0 or an error code
*/
GIT_EXTERN(int) git_remote_create_inmemory(
GIT_EXTERN(int) git_remote_create_anonymous(
git_remote **out,
git_repository *repo,
const char *fetch,
const char *url);
const char *url,
const char *fetch);
/**
* Get the information for a particular remote
@ -107,6 +107,18 @@ GIT_EXTERN(int) git_remote_load(git_remote **out, git_repository *repo, const ch
*/
GIT_EXTERN(int) git_remote_save(const git_remote *remote);
/**
* Create a copy of an existing remote. All internal strings are also
* duplicated. Callbacks are not duplicated.
*
* Call `git_remote_free` to free the data.
*
* @param dest pointer where to store the copy
* @param source object to copy
* @return 0 or an error code
*/
GIT_EXTERN(int) git_remote_dup(git_remote **dest, git_remote *source);
/**
* Get the remote's repository
*
@ -182,7 +194,7 @@ GIT_EXTERN(int) git_remote_add_fetch(git_remote *remote, const char *refspec);
* @param array pointer to the array in which to store the strings
* @param remote the remote to query
*/
GIT_EXTERN(int) git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote);
GIT_EXTERN(int) git_remote_get_fetch_refspecs(git_strarray *array, const git_remote *remote);
/**
* Set the remote's list of fetch refspecs
@ -215,7 +227,7 @@ GIT_EXTERN(int) git_remote_add_push(git_remote *remote, const char *refspec);
* @param array pointer to the array in which to store the strings
* @param remote the remote to query
*/
GIT_EXTERN(int) git_remote_get_push_refspecs(git_strarray *array, git_remote *remote);
GIT_EXTERN(int) git_remote_get_push_refspecs(git_strarray *array, const git_remote *remote);
/**
* Set the remote's list of push refspecs
@ -242,7 +254,7 @@ GIT_EXTERN(void) git_remote_clear_refspecs(git_remote *remote);
* @param remote the remote
* @return the amount of refspecs configured in this remote
*/
GIT_EXTERN(size_t) git_remote_refspec_count(git_remote *remote);
GIT_EXTERN(size_t) git_remote_refspec_count(const git_remote *remote);
/**
* Get a refspec from the remote
@ -251,7 +263,7 @@ GIT_EXTERN(size_t) git_remote_refspec_count(git_remote *remote);
* @param n the refspec to get
* @return the nth refspec
*/
GIT_EXTERN(const git_refspec *)git_remote_get_refspec(git_remote *remote, size_t n);
GIT_EXTERN(const git_refspec *)git_remote_get_refspec(const git_remote *remote, size_t n);
/**
* Open a connection to a remote
@ -307,7 +319,7 @@ GIT_EXTERN(int) git_remote_download(git_remote *remote);
* @param remote the remote
* @return 1 if it's connected, 0 otherwise.
*/
GIT_EXTERN(int) git_remote_connected(git_remote *remote);
GIT_EXTERN(int) git_remote_connected(const git_remote *remote);
/**
* Cancel the operation
@ -343,9 +355,16 @@ GIT_EXTERN(void) git_remote_free(git_remote *remote);
* Update the tips to the new state
*
* @param remote the remote to update
* @param signature The identity to use when updating reflogs
* @param reflog_message The message to insert into the reflogs. If NULL, the
* default is "fetch <name>", where <name> is the name of
* the remote (or its url, for in-memory remotes).
* @return 0 or an error code
*/
GIT_EXTERN(int) git_remote_update_tips(git_remote *remote);
GIT_EXTERN(int) git_remote_update_tips(
git_remote *remote,
const git_signature *signature,
const char *reflog_message);
/**
* Download new data and update tips
@ -354,9 +373,15 @@ GIT_EXTERN(int) git_remote_update_tips(git_remote *remote);
* disconnect and update the remote-tracking branches.
*
* @param remote the remote to fetch from
* @param signature The identity to use when updating reflogs
* @param reflog_message The message to insert into the reflogs. If NULL, the
* default is "fetch"
* @return 0 or an error code
*/
GIT_EXTERN(int) git_remote_fetch(git_remote *remote);
GIT_EXTERN(int) git_remote_fetch(
git_remote *remote,
const git_signature *signature,
const char *reflog_message);
/**
* Return whether a string is a valid remote URL
@ -432,7 +457,7 @@ struct git_remote_callbacks {
* progress side-band will be passed to this function (this is
* the 'counting objects' output.
*/
int (*progress)(const char *str, int len, void *data);
git_transport_message_cb sideband_progress;
/**
* Completion is called when different parts of the download
@ -443,15 +468,18 @@ struct git_remote_callbacks {
/**
* This will be called if the remote host requires
* authentication in order to connect to it.
*
* Returning GIT_PASSTHROUGH will make libgit2 behave as
* though this field isn't set.
*/
int (*credentials)(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *data);
git_cred_acquire_cb credentials;
/**
* During the download of new data, this will be regularly
* called with the current count of progress done by the
* indexer.
*/
int (*transfer_progress)(const git_transfer_progress *stats, void *data);
git_transfer_progress_cb transfer_progress;
/**
* Each time a reference is updated locally, this function
@ -469,6 +497,18 @@ struct git_remote_callbacks {
#define GIT_REMOTE_CALLBACKS_VERSION 1
#define GIT_REMOTE_CALLBACKS_INIT {GIT_REMOTE_CALLBACKS_VERSION}
/**
* Initializes a `git_remote_callbacks` with default values. Equivalent to
* creating an instance with GIT_REMOTE_CALLBACKS_INIT.
*
* @param opts the `git_remote_callbacks` struct to initialize
* @param version Version of struct; pass `GIT_REMOTE_CALLBACKS_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_remote_init_callbacks(
git_remote_callbacks *opts,
unsigned int version);
/**
* Set the callbacks for a remote
*
@ -481,6 +521,17 @@ struct git_remote_callbacks {
*/
GIT_EXTERN(int) git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks);
/**
* Retrieve the current callback structure
*
* This provides read access to the callbacks structure as the remote
* sees it.
*
* @param remote the remote to query
* @return a pointer to the callbacks structure
*/
GIT_EXTERN(const git_remote_callbacks *) git_remote_get_callbacks(git_remote *remote);
/**
* Get the statistics structure that is filled in by the fetch operation.
*/
@ -498,7 +549,7 @@ typedef enum {
* @param remote the remote to query
* @return the auto-follow setting
*/
GIT_EXTERN(git_remote_autotag_option_t) git_remote_autotag(git_remote *remote);
GIT_EXTERN(git_remote_autotag_option_t) git_remote_autotag(const git_remote *remote);
/**
* Set the tag auto-follow setting
@ -521,18 +572,17 @@ GIT_EXTERN(void) git_remote_set_autotag(
*
* A temporary in-memory remote cannot be given a name with this method.
*
* @param problems non-default refspecs cannot be renamed and will be
* stored here for further processing by the caller. Always free this
* strarray on succesful return.
* @param remote the remote to rename
* @param new_name the new name the remote should bear
* @param callback Optional callback to notify the consumer of fetch refspecs
* that haven't been automatically updated and need potential manual tweaking.
* @param payload Additional data to pass to the callback
* @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
*/
GIT_EXTERN(int) git_remote_rename(
git_strarray *problems,
git_remote *remote,
const char *new_name,
git_remote_rename_problem_cb callback,
void *payload);
const char *new_name);
/**
* Retrieve the update FETCH_HEAD setting.
@ -559,6 +609,35 @@ GIT_EXTERN(void) git_remote_set_update_fetchhead(git_remote *remote, int value);
*/
GIT_EXTERN(int) git_remote_is_valid_name(const char *remote_name);
/**
* Delete an existing persisted remote.
*
* All remote-tracking branches and configuration settings
* for the remote will be removed.
*
* @param remote A valid remote
* @return 0 on success, or an error code.
*/
GIT_EXTERN(int) git_remote_delete(git_remote *remote);
/**
* Retrieve the name of the remote's default branch
*
* The default branch of a repository is the branch which HEAD points
* to. If the remote does not support reporting this information
* directly, it performs the guess as git does; that is, if there are
* multiple branches which point to the same commit, the first one is
* chosen. If the master branch is a candidate, it wins.
*
* This function must only be called after connecting.
*
* @param out the buffern in which to store the reference name
* @param remote the remote
* @return 0, GIT_ENOTFOUND if the remote does not have any references
* or none of them point to HEAD's commit, or an error message.
*/
GIT_EXTERN(int) git_remote_default_branch(git_buf *out, git_remote *remote);
/** @} */
GIT_END_DECL
#endif

View File

@ -10,6 +10,7 @@
#include "common.h"
#include "types.h"
#include "oid.h"
#include "buffer.h"
/**
* @file git2/repository.h
@ -58,10 +59,8 @@ GIT_EXTERN(int) git_repository_wrap_odb(git_repository **out, git_odb *odb);
* The method will automatically detect if the repository is bare
* (if there is a repository).
*
* @param path_out The user allocated buffer which will
* contain the found path.
*
* @param path_size repository_path size
* @param out A pointer to a user-allocated git_buf which will contain
* the found path.
*
* @param start_path The base path where the lookup starts.
*
@ -77,8 +76,7 @@ GIT_EXTERN(int) git_repository_wrap_odb(git_repository **out, git_odb *odb);
* @return 0 or an error code
*/
GIT_EXTERN(int) git_repository_discover(
char *path_out,
size_t path_size,
git_buf *out,
const char *start_path,
int across_fs,
const char *ceiling_dirs);
@ -269,6 +267,18 @@ typedef struct {
#define GIT_REPOSITORY_INIT_OPTIONS_VERSION 1
#define GIT_REPOSITORY_INIT_OPTIONS_INIT {GIT_REPOSITORY_INIT_OPTIONS_VERSION}
/**
* Initializes a `git_repository_init_options` with default values. Equivalent
* to creating an instance with GIT_REPOSITORY_INIT_OPTIONS_INIT.
*
* @param opts the `git_repository_init_options` struct to initialize
* @param version Version of struct; pass `GIT_REPOSITORY_INIT_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_repository_init_init_options(
git_repository_init_options *opts,
unsigned int version);
/**
* Create a new Git repository in the given folder with extended controls.
*
@ -398,12 +408,28 @@ GIT_EXTERN(int) git_repository_is_bare(git_repository *repo);
* The configuration file must be freed once it's no longer
* being used by the user.
*
* @param out Pointer to store the loaded config file
* @param out Pointer to store the loaded configuration
* @param repo A repository object
* @return 0, or an error code
*/
GIT_EXTERN(int) git_repository_config(git_config **out, git_repository *repo);
/**
* Get a snapshot of the repository's configuration
*
* Convenience function to take a snapshot from the repository's
* configuration. The contents of this snapshot will not change,
* even if the underlying config files are modified.
*
* The configuration file must be freed once it's no longer
* being used by the user.
*
* @param out Pointer to store the loaded configuration
* @param repo the repository
* @return 0, or an error code
*/
GIT_EXTERN(int) git_repository_config_snapshot(git_config **out, git_repository *repo);
/**
* Get the Object Database for this repository.
*
@ -464,21 +490,11 @@ GIT_EXTERN(int) git_repository_index(git_index **out, git_repository *repo);
* Use this function to get the contents of this file. Don't forget to
* remove the file after you create the commit.
*
* If the repository message exists and there are no errors reading it, this
* returns the bytes needed to store the message in memory (i.e. message
* file size plus one terminating NUL byte). That value is returned even if
* `out` is NULL or `len` is shorter than the necessary size.
*
* The `out` buffer will *always* be NUL terminated, even if truncation
* occurs.
*
* @param out Buffer to write data into or NULL to just read required size
* @param len Length of `out` buffer in bytes
* @param out git_buf to write data into
* @param repo Repository to read prepared message from
* @return GIT_ENOTFOUND if no message exists, other value < 0 for other
* errors, or total bytes in message (may be > `len`) on success
* @return 0, GIT_ENOTFOUND if no message exists or an error code
*/
GIT_EXTERN(int) git_repository_message(char *out, size_t len, git_repository *repo);
GIT_EXTERN(int) git_repository_message(git_buf *out, git_repository *repo);
/**
* Remove git's prepared message.
@ -488,13 +504,13 @@ GIT_EXTERN(int) git_repository_message(char *out, size_t len, git_repository *re
GIT_EXTERN(int) git_repository_message_remove(git_repository *repo);
/**
* Remove all the metadata associated with an ongoing git merge, including
* MERGE_HEAD, MERGE_MSG, etc.
* Remove all the metadata associated with an ongoing command like merge,
* revert, cherry-pick, etc. For example: MERGE_HEAD, MERGE_MSG, etc.
*
* @param repo A repository object
* @return 0 on success, or error
*/
GIT_EXTERN(int) git_repository_merge_cleanup(git_repository *repo);
GIT_EXTERN(int) git_repository_state_cleanup(git_repository *repo);
typedef int (*git_repository_fetchhead_foreach_cb)(const char *ref_name,
const char *remote_url,
@ -503,14 +519,18 @@ typedef int (*git_repository_fetchhead_foreach_cb)(const char *ref_name,
void *payload);
/**
* Call callback 'callback' for each entry in the given FETCH_HEAD file.
* Invoke 'callback' for each entry in the given FETCH_HEAD file.
*
* Return a non-zero value from the callback to stop the loop.
*
* @param repo A repository object
* @param callback Callback function
* @param payload Pointer to callback data (optional)
* @return 0 on success, GIT_ENOTFOUND, GIT_EUSER or error
* @return 0 on success, non-zero callback return value, GIT_ENOTFOUND if
* there is no FETCH_HEAD file, or other error code.
*/
GIT_EXTERN(int) git_repository_fetchhead_foreach(git_repository *repo,
GIT_EXTERN(int) git_repository_fetchhead_foreach(
git_repository *repo,
git_repository_fetchhead_foreach_cb callback,
void *payload);
@ -518,15 +538,19 @@ typedef int (*git_repository_mergehead_foreach_cb)(const git_oid *oid,
void *payload);
/**
* If a merge is in progress, call callback 'cb' for each commit ID in the
* If a merge is in progress, invoke 'callback' for each commit ID in the
* MERGE_HEAD file.
*
* Return a non-zero value from the callback to stop the loop.
*
* @param repo A repository object
* @param callback Callback function
* @param payload Pointer to callback data (optional)
* @return 0 on success, GIT_ENOTFOUND, GIT_EUSER or error
* @return 0 on success, non-zero callback return value, GIT_ENOTFOUND if
* there is no MERGE_HEAD file, or other error code.
*/
GIT_EXTERN(int) git_repository_mergehead_foreach(git_repository *repo,
GIT_EXTERN(int) git_repository_mergehead_foreach(
git_repository *repo,
git_repository_mergehead_foreach_cb callback,
void *payload);
@ -538,6 +562,10 @@ GIT_EXTERN(int) git_repository_mergehead_foreach(git_repository *repo,
* hash a file in the repository and you want to apply filtering rules (e.g.
* crlf filters) before generating the SHA, then use this function.
*
* Note: if the repository has `core.safecrlf` set to fail and the
* filtering triggers that failure, then this function will return an
* error and not calculate the hash of the file.
*
* @param out Output value of calculated SHA
* @param repo Repository pointer
* @param path Path to file on disk whose contents should be hashed. If the
@ -547,6 +575,7 @@ GIT_EXTERN(int) git_repository_mergehead_foreach(git_repository *repo,
* NULL, then the `path` parameter will be used instead. If
* this is passed as the empty string, then no filters will be
* applied when calculating the hash.
* @return 0 on success, or an error code
*/
GIT_EXTERN(int) git_repository_hashfile(
git_oid *out,
@ -571,11 +600,15 @@ GIT_EXTERN(int) git_repository_hashfile(
*
* @param repo Repository pointer
* @param refname Canonical name of the reference the HEAD should point at
* @param signature The identity that will used to populate the reflog entry
* @param log_message The one line long message to be appended to the reflog
* @return 0 on success, or an error code
*/
GIT_EXTERN(int) git_repository_set_head(
git_repository* repo,
const char* refname);
const char* refname,
const git_signature *signature,
const char *log_message);
/**
* Make the repository HEAD directly point to the Commit.
@ -591,11 +624,15 @@ GIT_EXTERN(int) git_repository_set_head(
*
* @param repo Repository pointer
* @param commitish Object id of the Commit the HEAD should point to
* @param signature The identity that will used to populate the reflog entry
* @param log_message The one line long message to be appended to the reflog
* @return 0 on success, or an error code
*/
GIT_EXTERN(int) git_repository_set_head_detached(
git_repository* repo,
const git_oid* commitish);
const git_oid* commitish,
const git_signature *signature,
const char *log_message);
/**
* Detach the HEAD.
@ -611,11 +648,15 @@ GIT_EXTERN(int) git_repository_set_head_detached(
* Otherwise, the HEAD will be detached and point to the peeled Commit.
*
* @param repo Repository pointer
* @param signature The identity that will used to populate the reflog entry
* @param reflog_message The one line long message to be appended to the reflog
* @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing
* branch or an error code
*/
GIT_EXTERN(int) git_repository_detach_head(
git_repository* repo);
git_repository* repo,
const git_signature *signature,
const char *reflog_message);
typedef enum {
GIT_REPOSITORY_STATE_NONE,

View File

@ -19,9 +19,9 @@ GIT_BEGIN_DECL
* Kinds of reset operation
*/
typedef enum {
GIT_RESET_SOFT = 1, /** Move the head to the given commit */
GIT_RESET_MIXED = 2, /** SOFT plus reset index to the commit */
GIT_RESET_HARD = 3, /** MIXED plus changes in working tree discarded */
GIT_RESET_SOFT = 1, /**< Move the head to the given commit */
GIT_RESET_MIXED = 2, /**< SOFT plus reset index to the commit */
GIT_RESET_HARD = 3, /**< MIXED plus changes in working tree discarded */
} git_reset_t;
/**
@ -48,10 +48,21 @@ typedef enum {
*
* @param reset_type Kind of reset operation to perform.
*
* @param signature The identity that will used to populate the reflog entry
*
* @param log_message The one line long message to be appended to the reflog.
* The reflog is only updated if the affected direct reference is actually
* changing. If NULL, the default is "reset: moving"; if you want something more
* useful, provide a message.
*
* @return 0 on success or an error code
*/
GIT_EXTERN(int) git_reset(
git_repository *repo, git_object *target, git_reset_t reset_type);
git_repository *repo,
git_object *target,
git_reset_t reset_type,
git_signature *signature,
const char *log_message);
/**
* Updates some entries in the index from the target commit tree.

86
include/git2/revert.h Normal file
View File

@ -0,0 +1,86 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_git_revert_h__
#define INCLUDE_git_revert_h__
#include "common.h"
#include "types.h"
#include "merge.h"
/**
* @file git2/revert.h
* @brief Git revert routines
* @defgroup git_revert Git revert routines
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
typedef struct {
unsigned int version;
/** For merge commits, the "mainline" is treated as the parent. */
unsigned int mainline;
git_merge_options merge_opts;
git_checkout_options checkout_opts;
} git_revert_options;
#define GIT_REVERT_OPTIONS_VERSION 1
#define GIT_REVERT_OPTIONS_INIT {GIT_REVERT_OPTIONS_VERSION, 0, GIT_MERGE_OPTIONS_INIT, GIT_CHECKOUT_OPTIONS_INIT}
/**
* Initializes a `git_revert_options` with default values. Equivalent to
* creating an instance with GIT_REVERT_OPTIONS_INIT.
*
* @param opts the `git_revert_options` struct to initialize
* @param version Version of struct; pass `GIT_REVERT_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_revert_init_options(
git_revert_options *opts,
unsigned int version);
/**
* Reverts the given commit against the given "our" commit, producing an
* index that reflects the result of the revert.
*
* The returned index must be freed explicitly with `git_index_free`.
*
* @param out pointer to store the index result in
* @param repo the repository that contains the given commits
* @param revert_commit the commit to revert
* @param our_commit the commit to revert against (eg, HEAD)
* @param mainline the parent of the revert commit, if it is a merge
* @param merge_options the merge options (or null for defaults)
* @return zero on success, -1 on failure.
*/
int git_revert_commit(
git_index **out,
git_repository *repo,
git_commit *revert_commit,
git_commit *our_commit,
unsigned int mainline,
const git_merge_options *merge_options);
/**
* Reverts the given commit, producing changes in the working directory.
*
* @param repo the repository to revert
* @param commit the commit to revert
* @param given_opts merge flags
* @return zero on success, -1 on failure.
*/
GIT_EXTERN(int) git_revert(
git_repository *repo,
git_commit *commit,
const git_revert_options *given_opts);
/** @} */
GIT_END_DECL
#endif

View File

@ -87,7 +87,7 @@ GIT_EXTERN(void) git_revwalk_reset(git_revwalk *walker);
/**
* Mark a commit to start traversal from.
*
* The given OID must belong to a commit on the walked
* The given OID must belong to a committish on the walked
* repository.
*
* The given commit will be used as one of the roots
@ -108,7 +108,10 @@ GIT_EXTERN(int) git_revwalk_push(git_revwalk *walk, const git_oid *id);
* pattern will be pushed to the revision walker.
*
* A leading 'refs/' is implied if not present as well as a trailing
* '/ *' if the glob lacks '?', '*' or '['.
* '/\*' if the glob lacks '?', '\*' or '['.
*
* Any references matching this glob which do not point to a
* committish will be ignored.
*
* @param walk the walker being used for the traversal
* @param glob the glob pattern references should match
@ -127,7 +130,7 @@ GIT_EXTERN(int) git_revwalk_push_head(git_revwalk *walk);
/**
* Mark a commit (and its ancestors) uninteresting for the output.
*
* The given OID must belong to a commit on the walked
* The given OID must belong to a committish on the walked
* repository.
*
* The resolved commit and all its parents will be hidden from the
@ -147,7 +150,10 @@ GIT_EXTERN(int) git_revwalk_hide(git_revwalk *walk, const git_oid *commit_id);
* revision walk.
*
* A leading 'refs/' is implied if not present as well as a trailing
* '/ *' if the glob lacks '?', '*' or '['.
* '/\*' if the glob lacks '?', '\*' or '['.
*
* Any references matching this glob which do not point to a
* committish will be ignored.
*
* @param walk the walker being used for the traversal
* @param glob the glob pattern references should match
@ -166,7 +172,7 @@ GIT_EXTERN(int) git_revwalk_hide_head(git_revwalk *walk);
/**
* Push the OID pointed to by a reference
*
* The reference must point to a commit.
* The reference must point to a committish.
*
* @param walk the walker being used for the traversal
* @param refname the reference to push
@ -177,7 +183,7 @@ GIT_EXTERN(int) git_revwalk_push_ref(git_revwalk *walk, const char *refname);
/**
* Hide the OID pointed to by a reference
*
* The reference must point to a commit.
* The reference must point to a committish.
*
* @param walk the walker being used for the traversal
* @param refname the reference to hide
@ -255,6 +261,30 @@ GIT_EXTERN(void) git_revwalk_free(git_revwalk *walk);
*/
GIT_EXTERN(git_repository *) git_revwalk_repository(git_revwalk *walk);
/**
* This is a callback function that user can provide to hide a
* commit and its parents. If the callback function returns non-zero value,
* then this commit and its parents will be hidden.
*
* @param commit_id oid of Commit
* @param payload User-specified pointer to data to be passed as data payload
*/
typedef int(*git_revwalk_hide_cb)(
const git_oid *commit_id,
void *payload);
/**
* Adds a callback function to hide a commit and its parents
*
* @param walk the revision walker
* @param hide_cb callback function to hide a commit and its parents
* @param payload data payload to be passed to callback function
*/
GIT_EXTERN(int) git_revwalk_add_hide_cb(
git_revwalk *walk,
git_revwalk_hide_cb hide_cb,
void *payload);
/** @} */
GIT_END_DECL
#endif

View File

@ -68,10 +68,11 @@ GIT_EXTERN(int) git_signature_default(git_signature **out, git_repository *repo)
*
* Call `git_signature_free()` to free the data.
*
* @param sig signature to duplicated
* @return a copy of sig, NULL on out of memory
* @param dest pointer where to store the copy
* @param sig signature to duplicate
* @return 0 or an error code
*/
GIT_EXTERN(git_signature *) git_signature_dup(const git_signature *sig);
GIT_EXTERN(int) git_signature_dup(git_signature **dest, const git_signature *sig);
/**
* Free an existing signature.

View File

@ -62,19 +62,15 @@ GIT_EXTERN(int) git_stash_save(
unsigned int flags);
/**
* When iterating over all the stashed states, callback that will be
* issued per entry.
* This is a callback function you can provide to iterate over all the
* stashed states that will be invoked per entry.
*
* @param index The position within the stash list. 0 points to the
* most recent stashed state.
*
* most recent stashed state.
* @param message The stash message.
*
* @param stash_id The commit oid of the stashed state.
*
* @param payload Extra parameter to callback function.
*
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
* @return 0 to continue iterating or non-zero to stop
*/
typedef int (*git_stash_cb)(
size_t index,
@ -89,12 +85,12 @@ typedef int (*git_stash_cb)(
*
* @param repo Repository where to find the stash.
*
* @param callback Callback to invoke per found stashed state. The most recent
* stash state will be enumerated first.
* @param callback Callback to invoke per found stashed state. The most
* recent stash state will be enumerated first.
*
* @param payload Extra parameter to callback function.
*
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_stash_foreach(
git_repository *repo,

View File

@ -121,6 +121,11 @@ typedef enum {
* - GIT_STATUS_OPT_NO_REFRESH bypasses the default status behavior of
* doing a "soft" index reload (i.e. reloading the index data if the
* file on disk has been modified outside libgit2).
* - GIT_STATUS_OPT_UPDATE_INDEX tells libgit2 to refresh the stat cache
* in the index for files that are unchanged but have out of date stat
* information in the index. It will result in less work being done on
* subsequent calls to get status. This is mutually exclusive with the
* NO_REFRESH option.
*
* Calling `git_status_foreach()` is like calling the extended version
* with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
@ -141,6 +146,7 @@ typedef enum {
GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10),
GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11),
GIT_STATUS_OPT_NO_REFRESH = (1u << 12),
GIT_STATUS_OPT_UPDATE_INDEX = (1u << 13),
} git_status_opt_t;
#define GIT_STATUS_OPT_DEFAULTS \
@ -174,6 +180,18 @@ typedef struct {
#define GIT_STATUS_OPTIONS_VERSION 1
#define GIT_STATUS_OPTIONS_INIT {GIT_STATUS_OPTIONS_VERSION}
/**
* Initializes a `git_status_options` with default values. Equivalent to
* creating an instance with GIT_STATUS_OPTIONS_INIT.
*
* @param opts The `git_status_options` instance to initialize.
* @param version Version of struct; pass `GIT_STATUS_OPTIONS_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_status_init_options(
git_status_options *opts,
unsigned int version);
/**
* A status entry, providing the differences between the file as it exists
* in HEAD and the index, and providing the differences between the index
@ -203,12 +221,12 @@ typedef struct {
* into this function.
*
* If the callback returns a non-zero value, this function will stop looping
* and return GIT_EUSER.
* and return that value to caller.
*
* @param repo A repository object
* @param callback The function to call on each file
* @param payload Pointer to pass through to callback function
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_status_foreach(
git_repository *repo,
@ -223,11 +241,16 @@ GIT_EXTERN(int) git_status_foreach(
* in what order. See the `git_status_options` structure for details
* about the additional controls that this makes available.
*
* Note that if a `pathspec` is given in the `git_status_options` to filter
* the status, then the results from rename detection (if you enable it) may
* not be accurate. To do rename detection properly, this must be called
* with no `pathspec` so that all files can be considered.
*
* @param repo Repository object
* @param opts Status options structure
* @param callback The function to call on each file
* @param payload Pointer to pass through to callback function
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_status_foreach_ext(
git_repository *repo,
@ -238,8 +261,20 @@ GIT_EXTERN(int) git_status_foreach_ext(
/**
* Get file status for a single file.
*
* This is not quite the same as calling `git_status_foreach_ext()` with
* the pathspec set to the specified path.
* This tries to get status for the filename that you give. If no files
* match that name (in either the HEAD, index, or working directory), this
* returns GIT_ENOTFOUND.
*
* If the name matches multiple files (for example, if the `path` names a
* directory or if running on a case- insensitive filesystem and yet the
* HEAD has two entries that both match the path), then this returns
* GIT_EAMBIGUOUS because it cannot give correct results.
*
* This does not do any sort of rename detection. Renames require a set of
* targets and because of the path filtering, there is not enough
* information to check renames correctly. To check file status with rename
* detection, there is no choice but to do a full `git_status_list_new` and
* scan through looking for the path that you are interested in.
*
* @param status_flags Output combination of git_status_t values for file
* @param repo A repository object
@ -256,6 +291,11 @@ GIT_EXTERN(int) git_status_file(
/**
* Gather file status information and populate the `git_status_list`.
*
* Note that if a `pathspec` is given in the `git_status_options` to filter
* the status, then the results from rename detection (if you enable it) may
* not be accurate. To do rename detection properly, this must be called
* with no `pathspec` so that all files can be considered.
*
* @param out Pointer to store the status results in
* @param repo Repository object
* @param opts Status options structure
@ -269,6 +309,9 @@ GIT_EXTERN(int) git_status_list_new(
/**
* Gets the count of status entries in this list.
*
* If there are no changes in status (at least according the options given
* when the status list was created), this can return 0.
*
* @param statuslist Existing status list object
* @return the number of status entries
*/

View File

@ -97,7 +97,8 @@ typedef enum {
(((S) & GIT_SUBMODULE_STATUS__INDEX_FLAGS) == 0)
#define GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(S) \
(((S) & GIT_SUBMODULE_STATUS__WD_FLAGS) == 0)
(((S) & (GIT_SUBMODULE_STATUS__WD_FLAGS & \
~GIT_SUBMODULE_STATUS_WD_UNINITIALIZED)) == 0)
#define GIT_SUBMODULE_STATUS_IS_WD_DIRTY(S) \
(((S) & (GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED | \
@ -114,29 +115,35 @@ typedef enum {
*
* - The submodule is not mentioned in the HEAD, the index, and the config,
* but does "exist" in the working directory (i.e. there is a subdirectory
* that is a valid self-contained git repo). In this case, this function
* returns GIT_EEXISTS to indicate the the submodule exists but not in a
* that appears to be a Git repository). In this case, this function
* returns GIT_EEXISTS to indicate a sub-repository exists but not in a
* state where a git_submodule can be instantiated.
* - The submodule is not mentioned in the HEAD, index, or config and the
* working directory doesn't contain a value git repo at that path.
* There may or may not be anything else at that path, but nothing that
* looks like a submodule. In this case, this returns GIT_ENOTFOUND.
*
* The submodule object is owned by the containing repo and will be freed
* when the repo is freed. The caller need not free the submodule.
* You must call `git_submodule_free` when done with the submodule.
*
* @param submodule Pointer to submodule description object pointer..
* @param repo The repository.
* @param name The name of the submodule. Trailing slashes will be ignored.
* @param out Output ptr to submodule; pass NULL to just get return code
* @param repo The parent repository
* @param name The name of or path to the submodule; trailing slashes okay
* @return 0 on success, GIT_ENOTFOUND if submodule does not exist,
* GIT_EEXISTS if submodule exists in working directory only, -1 on
* other errors.
* GIT_EEXISTS if a repository is found in working directory only,
* -1 on other errors.
*/
GIT_EXTERN(int) git_submodule_lookup(
git_submodule **submodule,
git_submodule **out,
git_repository *repo,
const char *name);
/**
* Release a submodule
*
* @param submodule Submodule object
*/
GIT_EXTERN(void) git_submodule_free(git_submodule *submodule);
/**
* Iterate over all tracked submodules of a repository.
*
@ -174,9 +181,11 @@ GIT_EXTERN(int) git_submodule_foreach(
* `git_submodule_add_finalize()` to wrap up adding the new submodule and
* .gitmodules to the index to be ready to commit.
*
* @param submodule The newly created submodule ready to open for clone
* @param repo Superproject repository to contain the new submodule
* @param url URL for the submodules remote
* You must call `git_submodule_free` on the submodule object when done.
*
* @param out The newly created submodule ready to open for clone
* @param repo The repository in which you want to create the submodule
* @param url URL for the submodule's remote
* @param path Path at which the submodule should be created
* @param use_gitlink Should workdir contain a gitlink to the repo in
* .git/modules vs. repo directly in workdir.
@ -184,7 +193,7 @@ GIT_EXTERN(int) git_submodule_foreach(
* -1 on other errors.
*/
GIT_EXTERN(int) git_submodule_add_setup(
git_submodule **submodule,
git_submodule **out,
git_repository *repo,
const char *url,
const char *path,
@ -270,6 +279,24 @@ GIT_EXTERN(const char *) git_submodule_path(git_submodule *submodule);
*/
GIT_EXTERN(const char *) git_submodule_url(git_submodule *submodule);
/**
* Resolve a submodule url relative to the given repository.
*
* @param out buffer to store the absolute submodule url in
* @param repo Pointer to repository object
* @param url Relative url
* @return 0 or an error code
*/
GIT_EXTERN(int) git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *url);
/**
* Get the branch for the submodule.
*
* @param submodule Pointer to submodule object
* @return Pointer to the submodule branch
*/
GIT_EXTERN(const char *) git_submodule_branch(git_submodule *submodule);
/**
* Set the URL for the submodule.
*
@ -410,7 +437,7 @@ GIT_EXTERN(git_submodule_update_t) git_submodule_set_update(
*
* @return 0 if fetchRecurseSubmodules is false, 1 if true
*/
GIT_EXTERN(int) git_submodule_fetch_recurse_submodules(
GIT_EXTERN(git_submodule_recurse_t) git_submodule_fetch_recurse_submodules(
git_submodule *submodule);
/**
@ -424,9 +451,9 @@ GIT_EXTERN(int) git_submodule_fetch_recurse_submodules(
* @param fetch_recurse_submodules Boolean value
* @return old value for fetchRecurseSubmodules
*/
GIT_EXTERN(int) git_submodule_set_fetch_recurse_submodules(
GIT_EXTERN(git_submodule_recurse_t) git_submodule_set_fetch_recurse_submodules(
git_submodule *submodule,
int fetch_recurse_submodules);
git_submodule_recurse_t fetch_recurse_submodules);
/**
* Copy submodule info into ".git/config" file.
@ -474,15 +501,23 @@ GIT_EXTERN(int) git_submodule_open(
*
* Call this to reread cached submodule information for this submodule if
* you have reason to believe that it has changed.
*
* @param submodule The submodule to reload
* @param force Force reload even if the data doesn't seem out of date
* @return 0 on success, <0 on error
*/
GIT_EXTERN(int) git_submodule_reload(git_submodule *submodule);
GIT_EXTERN(int) git_submodule_reload(git_submodule *submodule, int force);
/**
* Reread all submodule info.
*
* Call this to reload all cached submodule information for the repo.
*
* @param repo The repository to reload submodule data for
* @param force Force full reload even if the data doesn't seem out of date
* @return 0 on success, <0 on error
*/
GIT_EXTERN(int) git_submodule_reload_all(git_repository *repo);
GIT_EXTERN(int) git_submodule_reload_all(git_repository *repo, int force);
/**
* Get the status for a submodule.

View File

@ -21,16 +21,18 @@
GIT_BEGIN_DECL
/**
* Create new commit in the repository from a list of `git_oid` values
* Create new commit in the repository from a list of `git_oid` values.
*
* See documentation for `git_commit_create()` for information about the
* parameters, as the meaning is identical excepting that `tree` and
* `parents` now take `git_oid`. This is a dangerous API in that nor
* the `tree`, neither the `parents` list of `git_oid`s are checked for
* validity.
*
* @see git_commit_create
*/
GIT_EXTERN(int) git_commit_create_from_oids(
git_oid *oid,
GIT_EXTERN(int) git_commit_create_from_ids(
git_oid *id,
git_repository *repo,
const char *update_ref,
const git_signature *author,
@ -38,9 +40,41 @@ GIT_EXTERN(int) git_commit_create_from_oids(
const char *message_encoding,
const char *message,
const git_oid *tree,
int parent_count,
size_t parent_count,
const git_oid *parents[]);
/**
* Callback function to return parents for commit.
*
* This is invoked with the count of the number of parents processed so far
* along with the user supplied payload. This should return a git_oid of
* the next parent or NULL if all parents have been provided.
*/
typedef const git_oid *(*git_commit_parent_callback)(size_t idx, void *payload);
/**
* Create a new commit in the repository with an callback to supply parents.
*
* See documentation for `git_commit_create()` for information about the
* parameters, as the meaning is identical excepting that `tree` takes a
* `git_oid` and doesn't check for validity, and `parent_cb` is invoked
* with `parent_payload` and should return `git_oid` values or NULL to
* indicate that all parents are accounted for.
*
* @see git_commit_create
*/
GIT_EXTERN(int) git_commit_create_from_callback(
git_oid *id,
git_repository *repo,
const char *update_ref,
const git_signature *author,
const git_signature *committer,
const char *message_encoding,
const char *message,
const git_oid *tree,
git_commit_parent_callback parent_cb,
void *parent_payload);
/** @} */
GIT_END_DECL
#endif

View File

@ -57,18 +57,32 @@ 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)(struct git_config_backend *, const char *key, const git_config_entry **entry);
int (*set)(struct git_config_backend *, const char *key, const char *value);
int (*set_multivar)(git_config_backend *cfg, const char *name, const char *regexp, const char *value);
int (*del)(struct git_config_backend *, const char *key);
int (*del_multivar)(struct git_config_backend *, const char *key, const char *regexp);
int (*iterator)(git_config_iterator **, struct git_config_backend *);
int (*refresh)(struct git_config_backend *);
/** Produce a read-only version of this backend */
int (*snapshot)(struct git_config_backend **, struct git_config_backend *);
void (*free)(struct git_config_backend *);
};
#define GIT_CONFIG_BACKEND_VERSION 1
#define GIT_CONFIG_BACKEND_INIT {GIT_CONFIG_BACKEND_VERSION}
/**
* Initializes a `git_config_backend` with default values. Equivalent to
* creating an instance with GIT_CONFIG_BACKEND_INIT.
*
* @param opts the `git_config_backend` struct to initialize.
* @param version Version of struct; pass `GIT_CONFIG_BACKEND_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_config_init_backend(
git_config_backend *backend,
unsigned int version);
/**
* Add a generic config file instance to an existing config
*

91
include/git2/sys/diff.h Normal file
View File

@ -0,0 +1,91 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_sys_git_diff_h__
#define INCLUDE_sys_git_diff_h__
#include "git2/common.h"
#include "git2/types.h"
#include "git2/oid.h"
#include "git2/diff.h"
#include "git2/status.h"
/**
* @file git2/sys/diff.h
* @brief Low-level Git diff utilities
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
* Diff print callback that writes to a git_buf.
*
* This function is provided not for you to call it directly, but instead
* so you can use it as a function pointer to the `git_diff_print` or
* `git_patch_print` APIs. When using those APIs, you specify a callback
* to actually handle the diff and/or patch data.
*
* Use this callback to easily write that data to a `git_buf` buffer. You
* must pass a `git_buf *` value as the payload to the `git_diff_print`
* and/or `git_patch_print` function. The data will be appended to the
* buffer (after any existing content).
*/
GIT_EXTERN(int) git_diff_print_callback__to_buf(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
const git_diff_line *line,
void *payload); /*< payload must be a `git_buf *` */
/**
* Diff print callback that writes to stdio FILE handle.
*
* This function is provided not for you to call it directly, but instead
* so you can use it as a function pointer to the `git_diff_print` or
* `git_patch_print` APIs. When using those APIs, you specify a callback
* to actually handle the diff and/or patch data.
*
* Use this callback to easily write that data to a stdio FILE handle. You
* must pass a `FILE *` value (such as `stdout` or `stderr` or the return
* value from `fopen()`) as the payload to the `git_diff_print`
* and/or `git_patch_print` function. If you pass NULL, this will write
* data to `stdout`.
*/
GIT_EXTERN(int) git_diff_print_callback__to_file_handle(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
const git_diff_line *line,
void *payload); /*< payload must be a `FILE *` */
typedef struct {
unsigned int version;
size_t stat_calls;
size_t oid_calculations;
} git_diff_perfdata;
#define GIT_DIFF_PERFDATA_VERSION 1
#define GIT_DIFF_PERFDATA_INIT {GIT_DIFF_PERFDATA_VERSION,0,0}
/**
* Get performance data for a diff object.
*
* @param out Structure to be filled with diff performance data
* @param diff Diff to read performance data from
* @return 0 for success, <0 for error
*/
GIT_EXTERN(int) git_diff_get_perfdata(
git_diff_perfdata *out, const git_diff *diff);
/**
* Get performance data for diffs from a git_status_list
*/
GIT_EXTERN(int) git_status_list_get_perfdata(
git_diff_perfdata *out, const git_status_list *status);
/** @} */
GIT_END_DECL
#endif

View File

@ -55,7 +55,10 @@ GIT_EXTERN(git_filter *) git_filter_lookup(const char *name);
* your own chains of filters.
*/
GIT_EXTERN(int) git_filter_list_new(
git_filter_list **out, git_repository *repo, git_filter_mode_t mode);
git_filter_list **out,
git_repository *repo,
git_filter_mode_t mode,
uint32_t options);
/**
* Add a filter to a filter list with the given payload.
@ -115,10 +118,15 @@ GIT_EXTERN(uint16_t) git_filter_source_filemode(const git_filter_source *src);
GIT_EXTERN(const git_oid *) git_filter_source_id(const git_filter_source *src);
/**
* Get the git_filter_mode_t to be applied
* Get the git_filter_mode_t to be used
*/
GIT_EXTERN(git_filter_mode_t) git_filter_source_mode(const git_filter_source *src);
/**
* Get the combination git_filter_opt_t options to be applied
*/
GIT_EXTERN(uint32_t) git_filter_source_options(const git_filter_source *src);
/*
* struct git_filter
*
@ -149,6 +157,7 @@ typedef int (*git_filter_init_fn)(git_filter *self);
* Specified as `filter.shutdown`, this is an optional callback invoked
* when the filter is unregistered or when libgit2 is shutting down. It
* will be called once at most and should release resources as needed.
* This may be called even if the `initialize` callback was not made.
*
* Typically this function will free the `git_filter` object itself.
*/

View File

@ -0,0 +1,85 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#ifndef INCLUDE_sys_git_odb_mempack_h__
#define INCLUDE_sys_git_odb_mempack_h__
#include "git2/common.h"
#include "git2/types.h"
#include "git2/oid.h"
#include "git2/odb.h"
/**
* @file git2/sys/mempack.h
* @brief Custom ODB backend that permits packing objects in-memory
* @defgroup git_backend Git custom backend APIs
* @ingroup Git
* @{
*/
GIT_BEGIN_DECL
/**
* Instantiate a new mempack backend.
*
* The backend must be added to an existing ODB with the highest
* priority.
*
* git_mempack_new(&mempacker);
* git_repository_odb(&odb, repository);
* git_odb_add_backend(odb, mempacker, 999);
*
* Once the backend has been loaded, all writes to the ODB will
* instead be queued in memory, and can be finalized with
* `git_mempack_dump`.
*
* Subsequent reads will also be served from the in-memory store
* to ensure consistency, until the memory store is dumped.
*
* @param out Poiter where to store the ODB backend
* @return 0 on success; error code otherwise
*/
int git_mempack_new(git_odb_backend **out);
/**
* Dump all the queued in-memory writes to a packfile.
*
* The contents of the packfile will be stored in the given buffer.
* It is the caller's responsability to ensure that the generated
* packfile is available to the repository (e.g. by writing it
* to disk, or doing something crazy like distributing it across
* several copies of the repository over a network).
*
* Once the generated packfile is available to the repository,
* call `git_mempack_reset` to cleanup the memory store.
*
* Calling `git_mempack_reset` before the packfile has been
* written to disk will result in an inconsistent repository
* (the objects in the memory store won't be accessible).
*
* @param pack Buffer where to store the raw packfile
* @param repo The active repository where the backend is loaded
* @param backend The mempack backend
* @return 0 on success; error code otherwise
*/
int git_mempack_dump(git_buf *pack, git_repository *repo, git_odb_backend *backend);
/**
* Reset the memory packer by clearing all the queued objects.
*
* This assumes that `git_mempack_dump` has been called before to
* store all the queued objects into a single packfile.
*
* Alternatively, call `reset` without a previous dump to "undo"
* all the recently written objects, giving transaction-like
* semantics to the Git repository.
*
* @param backend The mempack backend
*/
void git_mempack_reset(git_odb_backend *backend);
GIT_END_DECL
#endif

View File

@ -35,11 +35,8 @@ struct git_odb_backend {
int (* read)(
void **, size_t *, git_otype *, git_odb_backend *, const git_oid *);
/* To find a unique object given a prefix
* of its oid.
* The oid given must be so that the
* remaining (GIT_OID_HEXSZ - len)*4 bits
* are 0s.
/* To find a unique object given a prefix of its oid. The oid given
* must be so that the remaining (GIT_OID_HEXSZ - len)*4 bits are 0s.
*/
int (* read_prefix)(
git_oid *, void **, size_t *, git_otype *,
@ -64,6 +61,9 @@ struct git_odb_backend {
int (* exists)(
git_odb_backend *, const git_oid *);
int (* exists_prefix)(
git_oid *, git_odb_backend *, const git_oid *, size_t);
/**
* If the backend implements a refreshing mechanism, it should be exposed
* through this endpoint. Each call to `git_odb_refresh()` will invoke it.
@ -81,7 +81,7 @@ struct git_odb_backend {
int (* writepack)(
git_odb_writepack **, git_odb_backend *, git_odb *odb,
git_transfer_progress_callback progress_cb, void *progress_payload);
git_transfer_progress_cb progress_cb, void *progress_payload);
void (* free)(git_odb_backend *);
};
@ -89,6 +89,18 @@ struct git_odb_backend {
#define GIT_ODB_BACKEND_VERSION 1
#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION}
/**
* Initializes a `git_odb_backend` with default values. Equivalent to
* creating an instance with GIT_ODB_BACKEND_INIT.
*
* @param opts the `git_odb_backend` struct to initialize.
* @param version Version the struct; pass `GIT_ODB_BACKEND_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_odb_init_backend(
git_odb_backend *backend,
unsigned int version);
GIT_EXTERN(void *) git_odb_backend_malloc(git_odb_backend *backend, size_t len);
GIT_END_DECL

View File

@ -93,17 +93,20 @@ struct git_refdb_backend {
* must provide this function.
*/
int (*write)(git_refdb_backend *backend,
const git_reference *ref, int force);
const git_reference *ref, int force,
const git_signature *who, const char *message,
const git_oid *old, const char *old_target);
int (*rename)(
git_reference **out, git_refdb_backend *backend,
const char *old_name, const char *new_name, int force);
const char *old_name, const char *new_name, int force,
const git_signature *who, const char *message);
/**
* Deletes the given reference from the refdb. A refdb implementation
* must provide this function.
*/
int (*del)(git_refdb_backend *backend, const char *ref_name);
int (*del)(git_refdb_backend *backend, const char *ref_name, const git_oid *old_id, const char *old_target);
/**
* Suggests that the given refdb compress or optimize its references.
@ -114,6 +117,17 @@ struct git_refdb_backend {
*/
int (*compress)(git_refdb_backend *backend);
/**
* Query whether a particular reference has a log (may be empty)
*/
int (*has_log)(git_refdb_backend *backend, const char *refname);
/**
* Make sure a particular reference will have a reflog which
* will be appended to on writes.
*/
int (*ensure_log)(git_refdb_backend *backend, const char *refname);
/**
* Frees any resources held by the refdb. A refdb implementation may
* provide this function; if it is not provided, nothing will be done.
@ -144,6 +158,18 @@ struct git_refdb_backend {
#define GIT_REFDB_BACKEND_VERSION 1
#define GIT_REFDB_BACKEND_INIT {GIT_REFDB_BACKEND_VERSION}
/**
* Initializes a `git_refdb_backend` with default values. Equivalent to
* creating an instance with GIT_REFDB_BACKEND_INIT.
*
* @param opts the `git_refdb_backend` struct to initialize
* @param version Version of struct; pass `GIT_REFDB_BACKEND_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_refdb_init_backend(
git_refdb_backend *backend,
unsigned int version);
/**
* Constructors for default filesystem-based refdb backend
*

View File

@ -41,6 +41,9 @@ typedef enum {
/* git_cred_default */
GIT_CREDTYPE_DEFAULT = (1u << 3),
/* git_cred_ssh_interactive */
GIT_CREDTYPE_SSH_INTERACTIVE = (1u << 4),
} git_credtype_t;
/* The base structure for all credential types */
@ -60,8 +63,10 @@ typedef struct {
#ifdef GIT_SSH
typedef LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*git_cred_sign_callback));
typedef LIBSSH2_USERAUTH_KBDINT_RESPONSE_FUNC((*git_cred_ssh_interactive_callback));
#else
typedef int (*git_cred_sign_callback)(void *, ...);
typedef int (*git_cred_ssh_interactive_callback)(void *, ...);
#endif
/**
@ -75,6 +80,16 @@ typedef struct git_cred_ssh_key {
char *passphrase;
} git_cred_ssh_key;
/**
* Keyboard-interactive based ssh authentication
*/
typedef struct git_cred_ssh_interactive {
git_cred parent;
char *username;
git_cred_ssh_interactive_callback prompt_callback;
void *payload;
} git_cred_ssh_interactive;
/**
* A key with a custom signature function
*/
@ -83,8 +98,8 @@ typedef struct git_cred_ssh_custom {
char *username;
char *publickey;
size_t publickey_len;
void *sign_callback;
void *sign_data;
git_cred_sign_callback sign_callback;
void *payload;
} git_cred_ssh_custom;
/** A key for NTLM/Kerberos "default" credentials */
@ -130,6 +145,33 @@ GIT_EXTERN(int) git_cred_ssh_key_new(
const char *privatekey,
const char *passphrase);
/**
* Create a new ssh keyboard-interactive based credential object.
* The supplied credential parameter will be internally duplicated.
*
* @param username Username to use to authenticate.
* @param prompt_callback The callback method used for prompts.
* @param payload Additional data to pass to the callback.
* @return 0 for success or an error code for failure.
*/
GIT_EXTERN(int) git_cred_ssh_interactive_new(
git_cred **out,
const char *username,
git_cred_ssh_interactive_callback prompt_callback,
void *payload);
/**
* Create a new ssh key credential object used for querying an ssh-agent.
* The supplied credential parameter will be internally duplicated.
*
* @param out The newly created credential object.
* @param username username to use to authenticate
* @return 0 for success or an error code for failure
*/
GIT_EXTERN(int) git_cred_ssh_key_from_agent(
git_cred **out,
const char *username);
/**
* Create an ssh key credential with a custom signing function.
*
@ -144,8 +186,8 @@ GIT_EXTERN(int) git_cred_ssh_key_new(
* @param username username to use to authenticate
* @param publickey The bytes of the public key.
* @param publickey_len The length of the public key in bytes.
* @param sign_fn The callback method to sign the data during the challenge.
* @param sign_data The data to pass to the sign function.
* @param sign_callback The callback method to sign the data during the challenge.
* @param payload Additional data to pass to the callback.
* @return 0 for success or an error code for failure
*/
GIT_EXTERN(int) git_cred_ssh_custom_new(
@ -153,8 +195,8 @@ GIT_EXTERN(int) git_cred_ssh_custom_new(
const char *username,
const char *publickey,
size_t publickey_len,
git_cred_sign_callback sign_fn,
void *sign_data);
git_cred_sign_callback sign_callback,
void *payload);
/**
* Create a "default" credential usable for Negotiate mechanisms like NTLM
@ -173,7 +215,8 @@ GIT_EXTERN(int) git_cred_default_new(git_cred **out);
* remote url, or NULL if not included.
* - allowed_types: A bitmask stating which cred types are OK to return.
* - payload: The payload provided when specifying this callback.
* - returns 0 for success or non-zero to indicate an error
* - returns 0 for success, < 0 to indicate an error, > 0 to indicate
* no credential was acquired
*/
typedef int (*git_cred_acquire_cb)(
git_cred **cred,
@ -244,7 +287,7 @@ struct git_transport {
git_transport *transport,
git_repository *repo,
git_transfer_progress *stats,
git_transfer_progress_callback progress_cb,
git_transfer_progress_cb progress_cb,
void *progress_payload);
/* Checks to see if the transport is connected */
@ -267,6 +310,18 @@ struct git_transport {
#define GIT_TRANSPORT_VERSION 1
#define GIT_TRANSPORT_INIT {GIT_TRANSPORT_VERSION}
/**
* Initializes a `git_transport` with default values. Equivalent to
* creating an instance with GIT_TRANSPORT_INIT.
*
* @param opts the `git_transport` struct to initialize
* @param version Version of struct; pass `GIT_TRANSPORT_VERSION`
* @return Zero on success; -1 on failure.
*/
GIT_EXTERN(int) git_transport_init(
git_transport *opts,
unsigned int version);
/**
* Function to use to create a transport from a URL. The transport database
* is scanned to find a transport that implements the scheme of the URI (i.e.

View File

@ -121,11 +121,11 @@ GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex(
* Warning: this must examine every entry in the tree, so it is not fast.
*
* @param tree a previously loaded tree.
* @param oid the sha being looked for
* @param id the sha being looked for
* @return the tree entry; NULL if not found
*/
GIT_EXTERN(const git_tree_entry *) git_tree_entry_byoid(
const git_tree *tree, const git_oid *oid);
GIT_EXTERN(const git_tree_entry *) git_tree_entry_byid(
const git_tree *tree, const git_oid *id);
/**
* Retrieve a tree entry contained in a tree or in any of its subtrees,
@ -150,10 +150,11 @@ GIT_EXTERN(int) git_tree_entry_bypath(
* Create a copy of a tree entry. The returned copy is owned by the user,
* and must be freed explicitly with `git_tree_entry_free()`.
*
* @param entry A tree entry to duplicate
* @return a copy of the original entry or NULL on error (alloc failure)
* @param dest pointer where to store the copy
* @param source tree entry to duplicate
* @return 0 or an error code
*/
GIT_EXTERN(git_tree_entry *) git_tree_entry_dup(const git_tree_entry *entry);
GIT_EXTERN(int) git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source);
/**
* Free a user-owned tree entry
@ -332,11 +333,18 @@ GIT_EXTERN(int) git_treebuilder_insert(
GIT_EXTERN(int) git_treebuilder_remove(
git_treebuilder *bld, const char *filename);
/**
* Callback for git_treebuilder_filter
*
* The return value is treated as a boolean, with zero indicating that the
* entry should be left alone and any non-zero value meaning that the
* entry should be removed from the treebuilder list (i.e. filtered out).
*/
typedef int (*git_treebuilder_filter_cb)(
const git_tree_entry *entry, void *payload);
/**
* Filter the entries in the tree
* Selectively remove entries in the tree
*
* The `filter` callback will be called for each entry in the tree with a
* pointer to the entry and the provided `payload`; if the callback returns
@ -344,7 +352,7 @@ typedef int (*git_treebuilder_filter_cb)(
*
* @param bld Tree builder
* @param filter Callback to filter entries
* @param payload Extra data to pass to filter
* @param payload Extra data to pass to filter callback
*/
GIT_EXTERN(void) git_treebuilder_filter(
git_treebuilder *bld,

View File

@ -131,7 +131,7 @@ typedef struct git_treebuilder git_treebuilder;
/** Memory representation of an index file. */
typedef struct git_index git_index;
/** An interator for conflicts in the index. */
/** An iterator for conflicts in the index. */
typedef struct git_index_conflict_iterator git_index_conflict_iterator;
/** Memory representation of a set of config files */
@ -154,15 +154,15 @@ typedef struct git_packbuilder git_packbuilder;
/** Time in a signature */
typedef struct git_time {
git_time_t time; /** time in seconds from epoch */
int offset; /** timezone offset, in minutes */
git_time_t time; /**< time in seconds from epoch */
int offset; /**< timezone offset, in minutes */
} git_time;
/** An action signature (e.g. for committers, taggers, etc) */
typedef struct git_signature {
char *name; /** full name of the author */
char *email; /** email of the author */
git_time when; /** time when the action happened */
char *name; /**< full name of the author */
char *email; /**< email of the author */
git_time when; /**< time when the action happened */
} git_signature;
/** In-memory representation of a reference. */
@ -183,9 +183,9 @@ typedef struct git_status_list git_status_list;
/** Basic type of any Git reference. */
typedef enum {
GIT_REF_INVALID = 0, /** Invalid reference */
GIT_REF_OID = 1, /** A reference which points at an object id */
GIT_REF_SYMBOLIC = 2, /** A reference which points at another reference */
GIT_REF_INVALID = 0, /**< Invalid reference */
GIT_REF_OID = 1, /**< A reference which points at an object id */
GIT_REF_SYMBOLIC = 2, /**< A reference which points at another reference */
GIT_REF_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC,
} git_ref_t;
@ -193,6 +193,7 @@ typedef enum {
typedef enum {
GIT_BRANCH_LOCAL = 1,
GIT_BRANCH_REMOTE = 2,
GIT_BRANCH_ALL = GIT_BRANCH_LOCAL|GIT_BRANCH_REMOTE,
} git_branch_t;
/** Valid modes for index and tree entries. */
@ -240,7 +241,7 @@ typedef struct git_transfer_progress {
* @param stats Structure containing information about the state of the transfer
* @param payload Payload provided by caller
*/
typedef int (*git_transfer_progress_callback)(const git_transfer_progress *stats, void *payload);
typedef int (*git_transfer_progress_cb)(const git_transfer_progress *stats, void *payload);
/**
* Opaque structure representing a submodule.
@ -313,16 +314,35 @@ typedef enum {
* when we don't want any particular ignore rule to be specified.
*/
typedef enum {
GIT_SUBMODULE_IGNORE_RESET = -1, /* reset to on-disk value */
GIT_SUBMODULE_IGNORE_RESET = -1, /**< reset to on-disk value */
GIT_SUBMODULE_IGNORE_NONE = 1, /* any change or untracked == dirty */
GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /* dirty if tracked files change */
GIT_SUBMODULE_IGNORE_DIRTY = 3, /* only dirty if HEAD moved */
GIT_SUBMODULE_IGNORE_ALL = 4, /* never dirty */
GIT_SUBMODULE_IGNORE_NONE = 1, /**< any change or untracked == dirty */
GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /**< dirty if tracked files change */
GIT_SUBMODULE_IGNORE_DIRTY = 3, /**< only dirty if HEAD moved */
GIT_SUBMODULE_IGNORE_ALL = 4, /**< never dirty */
GIT_SUBMODULE_IGNORE_DEFAULT = 0
} git_submodule_ignore_t;
/**
* Options for submodule recurse.
*
* Represent the value of `submodule.$name.fetchRecurseSubmodules`
*
* * GIT_SUBMODULE_RECURSE_RESET - reset to the on-disk value
* * GIT_SUBMODULE_RECURSE_NO - do no recurse into submodules
* * GIT_SUBMODULE_RECURSE_YES - recurse into submodules
* * GIT_SUBMODULE_RECURSE_ONDEMAND - recurse into submodules only when
* commit not already in local clone
*/
typedef enum {
GIT_SUBMODULE_RECURSE_RESET = -1,
GIT_SUBMODULE_RECURSE_NO = 0,
GIT_SUBMODULE_RECURSE_YES = 1,
GIT_SUBMODULE_RECURSE_ONDEMAND = 2,
} git_submodule_recurse_t;
/** @} */
GIT_END_DECL

View File

@ -7,9 +7,11 @@
#ifndef INCLUDE_git_version_h__
#define INCLUDE_git_version_h__
#define LIBGIT2_VERSION "0.20.0"
#define LIBGIT2_VERSION "0.21.0"
#define LIBGIT2_VER_MAJOR 0
#define LIBGIT2_VER_MINOR 20
#define LIBGIT2_VER_MINOR 21
#define LIBGIT2_VER_REVISION 0
#define LIBGIT2_SOVERSION 21
#endif

View File

@ -1,5 +1,11 @@
#!/bin/sh
if [ -n "$COVERITY" ];
then
./script/coverity.sh;
exit $?;
fi
# Create a test repo which we can use for the online::push tests
mkdir $HOME/_temp
git init --bare $HOME/_temp/test.git
@ -28,5 +34,5 @@ export GITTEST_REMOTE_SSH_PUBKEY="$HOME/.ssh/id_rsa.pub"
export GITTEST_REMOTE_SSH_PASSPHRASE=""
if [ -e ./libgit2_clar ]; then
./libgit2_clar -sonline::push
./libgit2_clar -sonline::push -sonline::clone::cred_callback_failure
fi

57
script/coverity.sh Executable file
View File

@ -0,0 +1,57 @@
#!/bin/bash
set -e
# Environment check
[ -z "$COVERITY_TOKEN" ] && echo "Need to set a coverity token" && exit 1
# Only run this on our branches
echo "Pull request: $TRAVIS_PULL_REQUEST | Slug: $TRAVIS_REPO_SLUG"
if [ "$TRAVIS_PULL_REQUEST" != "false" -o "$TRAVIS_REPO_SLUG" != "libgit2/libgit2" ];
then
echo "Only analyzing 'development' on the main repo."
exit 0
fi
COV_VERSION=6.6.1
case `uname -m` in
i?86) BITS=32 ;;
amd64|x86_64) BITS=64 ;;
esac
SCAN_TOOL=https://scan.coverity.com/download/linux-${BITS}
TOOL_BASE=`pwd`/_coverity-scan
# Install coverity tools
if [ ! -d $TOOL_BASE ]; then
echo "Downloading coverity..."
mkdir -p $TOOL_BASE
cd $TOOL_BASE
wget -O coverity_tool.tgz $SCAN_TOOL \
--post-data "project=libgit2&token=$COVERITY_TOKEN"
tar xzf coverity_tool.tgz
cd ..
TOOL_DIR=`find $TOOL_BASE -type d -name 'cov-analysis*'`
ln -s $TOOL_DIR $TOOL_BASE/cov-analysis
fi
COV_BUILD="$TOOL_BASE/cov-analysis/bin/cov-build"
# Configure and build
rm -rf _build
mkdir _build
cd _build
cmake .. -DTHREADSAFE=ON
COVERITY_UNSUPPORTED=1 \
$COV_BUILD --dir cov-int \
cmake --build .
# Upload results
tar czf libgit2.tgz cov-int
SHA=`git rev-parse --short HEAD`
curl \
--form project=libgit2 \
--form token=$COVERITY_TOKEN \
--form email=bs@github.com \
--form file=@libgit2.tgz \
--form version=$SHA \
--form description="Travis build" \
http://scan5.coverity.com/cgi-bin/upload.py

6
script/install-deps-linux.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/sh
set -x
sudo apt-get -qq update &&
sudo apt-get -qq install cmake libssh2-1-dev openssh-client openssh-server

5
script/install-deps-osx.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
set -x
brew install libssh2 cmake

View File

@ -1,48 +0,0 @@
/*
* Copyright (C) the libgit2 contributors. All rights reserved.
*
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
#include <git2/common.h>
#ifndef GIT_WIN32
#include "posix.h"
#include "map.h"
#include <errno.h>
int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, git_off_t offset)
{
GIT_MMAP_VALIDATE(out, len, prot, flags);
out->data = NULL;
out->len = 0;
if ((prot & GIT_PROT_WRITE) && ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)) {
giterr_set(GITERR_OS, "Trying to map shared-writeable");
return -1;
}
out->data = malloc(len);
GITERR_CHECK_ALLOC(out->data);
if ((p_lseek(fd, offset, SEEK_SET) < 0) || ((size_t)p_read(fd, out->data, len) != len)) {
giterr_set(GITERR_OS, "mmap emulation failed");
return -1;
}
out->len = len;
return 0;
}
int p_munmap(git_map *map)
{
assert(map != NULL);
free(map->data);
return 0;
}
#endif

View File

@ -7,7 +7,7 @@
#ifndef INCLUDE_array_h__
#define INCLUDE_array_h__
#include "util.h"
#include "common.h"
/*
* Use this to declare a typesafe resizable array of items, a la:

View File

@ -1,8 +1,8 @@
#include "common.h"
#include "repository.h"
#include "fileops.h"
#include "sysdir.h"
#include "config.h"
#include "attr.h"
#include "attr_file.h"
#include "ignore.h"
#include "git2/oid.h"
#include <ctype.h>
@ -33,6 +33,7 @@ static int collect_attr_files(
const char *path,
git_vector *files);
static void release_attr_files(git_vector *files);
int git_attr_get(
const char **value,
@ -49,6 +50,8 @@ int git_attr_get(
git_attr_name attr;
git_attr_rule *rule;
assert(value && repo && name);
*value = NULL;
if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
@ -57,6 +60,7 @@ int git_attr_get(
if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
goto cleanup;
memset(&attr, 0, sizeof(attr));
attr.name = name;
attr.name_hash = git_attr_file__name_hash(name);
@ -74,7 +78,7 @@ int git_attr_get(
}
cleanup:
git_vector_free(&files);
release_attr_files(&files);
git_attr_path__free(&path);
return error;
@ -103,6 +107,11 @@ int git_attr_get_many(
attr_get_many_info *info = NULL;
size_t num_found = 0;
if (!num_attr)
return 0;
assert(values && repo && names);
if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
return -1;
@ -145,7 +154,7 @@ int git_attr_get_many(
}
cleanup:
git_vector_free(&files);
release_attr_files(&files);
git_attr_path__free(&path);
git__free(info);
@ -169,15 +178,15 @@ int git_attr_foreach(
git_attr_assignment *assign;
git_strmap *seen = NULL;
assert(repo && callback);
if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
return -1;
if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0 ||
(error = git_strmap_alloc(&seen)) < 0)
goto cleanup;
seen = git_strmap_alloc();
GITERR_CHECK_ALLOC(seen);
git_vector_foreach(&files, i, file) {
git_attr_file__foreach_matching_rule(file, &path, j, rule) {
@ -193,8 +202,7 @@ int git_attr_foreach(
error = callback(assign->name, assign->value, payload);
if (error) {
giterr_clear();
error = GIT_EUSER;
giterr_set_after_callback(error);
goto cleanup;
}
}
@ -203,12 +211,79 @@ int git_attr_foreach(
cleanup:
git_strmap_free(seen);
git_vector_free(&files);
release_attr_files(&files);
git_attr_path__free(&path);
return error;
}
static int preload_attr_file(
git_repository *repo,
git_attr_file_source source,
const char *base,
const char *file)
{
int error;
git_attr_file *preload = NULL;
if (!file)
return 0;
if (!(error = git_attr_cache__get(
&preload, repo, source, base, file, git_attr_file__parse_buffer)))
git_attr_file__free(preload);
return error;
}
static int attr_setup(git_repository *repo)
{
int error = 0;
const char *workdir = git_repository_workdir(repo);
git_index *idx = NULL;
git_buf sys = GIT_BUF_INIT;
if ((error = git_attr_cache__init(repo)) < 0)
return error;
/* preload attribute files that could contain macros so the
* definitions will be available for later file parsing
*/
if (!(error = git_sysdir_find_system_file(&sys, GIT_ATTR_FILE_SYSTEM))) {
error = preload_attr_file(
repo, GIT_ATTR_FILE__FROM_FILE, NULL, sys.ptr);
git_buf_free(&sys);
}
if (error < 0) {
if (error == GIT_ENOTFOUND) {
giterr_clear();
error = 0;
} else
return error;
}
if ((error = preload_attr_file(
repo, GIT_ATTR_FILE__FROM_FILE,
NULL, git_repository_attr_cache(repo)->cfg_attr_file)) < 0)
return error;
if ((error = preload_attr_file(
repo, GIT_ATTR_FILE__FROM_FILE,
git_repository_path(repo), GIT_ATTR_FILE_INREPO)) < 0)
return error;
if (workdir != NULL &&
(error = preload_attr_file(
repo, GIT_ATTR_FILE__FROM_FILE, workdir, GIT_ATTR_FILE)) < 0)
return error;
if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
(error = preload_attr_file(
repo, GIT_ATTR_FILE__FROM_INDEX, NULL, GIT_ATTR_FILE)) < 0)
return error;
return error;
}
int git_attr_add_macro(
git_repository *repo,
@ -219,8 +294,8 @@ int git_attr_add_macro(
git_attr_rule *macro = NULL;
git_pool *pool;
if (git_attr_cache__init(repo) < 0)
return -1;
if ((error = git_attr_cache__init(repo)) < 0)
return error;
macro = git__calloc(1, sizeof(git_attr_rule));
GITERR_CHECK_ALLOC(macro);
@ -244,237 +319,6 @@ int git_attr_add_macro(
return error;
}
bool git_attr_cache__is_cached(
git_repository *repo, git_attr_file_source source, const char *path)
{
git_buf cache_key = GIT_BUF_INIT;
git_strmap *files = git_repository_attr_cache(repo)->files;
const char *workdir = git_repository_workdir(repo);
bool rval;
if (workdir && git__prefixcmp(path, workdir) == 0)
path += strlen(workdir);
if (git_buf_printf(&cache_key, "%d#%s", (int)source, path) < 0)
return false;
rval = git_strmap_exists(files, git_buf_cstr(&cache_key));
git_buf_free(&cache_key);
return rval;
}
static int load_attr_file(
const char **data,
git_futils_filestamp *stamp,
const char *filename)
{
int error;
git_buf content = GIT_BUF_INIT;
error = git_futils_filestamp_check(stamp, filename);
if (error < 0)
return error;
/* if error == 0, then file is up to date. By returning GIT_ENOTFOUND,
* we tell the caller not to reparse this file...
*/
if (!error)
return GIT_ENOTFOUND;
error = git_futils_readbuffer(&content, filename);
if (error < 0) {
/* convert error into ENOTFOUND so failed permissions / invalid
* file type don't actually stop the operation in progress.
*/
return GIT_ENOTFOUND;
/* TODO: once warnings are available, issue a warning callback */
}
*data = git_buf_detach(&content);
return 0;
}
static int load_attr_blob_from_index(
const char **content,
git_blob **blob,
git_repository *repo,
const git_oid *old_oid,
const char *relfile)
{
int error;
size_t pos;
git_index *index;
const git_index_entry *entry;
if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
(error = git_index_find(&pos, index, relfile)) < 0)
return error;
entry = git_index_get_byindex(index, pos);
if (old_oid && git_oid__cmp(old_oid, &entry->oid) == 0)
return GIT_ENOTFOUND;
if ((error = git_blob_lookup(blob, repo, &entry->oid)) < 0)
return error;
*content = git_blob_rawcontent(*blob);
return 0;
}
static int load_attr_from_cache(
git_attr_file **file,
git_attr_cache *cache,
git_attr_file_source source,
const char *relative_path)
{
git_buf cache_key = GIT_BUF_INIT;
khiter_t cache_pos;
*file = NULL;
if (!cache || !cache->files)
return 0;
if (git_buf_printf(&cache_key, "%d#%s", (int)source, relative_path) < 0)
return -1;
cache_pos = git_strmap_lookup_index(cache->files, cache_key.ptr);
git_buf_free(&cache_key);
if (git_strmap_valid_index(cache->files, cache_pos))
*file = git_strmap_value_at(cache->files, cache_pos);
return 0;
}
int git_attr_cache__internal_file(
git_repository *repo,
const char *filename,
git_attr_file **file)
{
int error = 0;
git_attr_cache *cache = git_repository_attr_cache(repo);
khiter_t cache_pos = git_strmap_lookup_index(cache->files, filename);
if (git_strmap_valid_index(cache->files, cache_pos)) {
*file = git_strmap_value_at(cache->files, cache_pos);
return 0;
}
if (git_attr_file__new(file, 0, filename, &cache->pool) < 0)
return -1;
git_strmap_insert(cache->files, (*file)->key + 2, *file, error);
if (error > 0)
error = 0;
return error;
}
int git_attr_cache__push_file(
git_repository *repo,
const char *base,
const char *filename,
git_attr_file_source source,
git_attr_file_parser parse,
void* parsedata,
git_vector *stack)
{
int error = 0;
git_buf path = GIT_BUF_INIT;
const char *workdir = git_repository_workdir(repo);
const char *relfile, *content = NULL;
git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file *file = NULL;
git_blob *blob = NULL;
git_futils_filestamp stamp;
assert(filename && stack);
/* join base and path as needed */
if (base != NULL && git_path_root(filename) < 0) {
if (git_buf_joinpath(&path, base, filename) < 0)
return -1;
filename = path.ptr;
}
relfile = filename;
if (workdir && git__prefixcmp(relfile, workdir) == 0)
relfile += strlen(workdir);
/* check cache */
if (load_attr_from_cache(&file, cache, source, relfile) < 0)
return -1;
/* if not in cache, load data, parse, and cache */
if (source == GIT_ATTR_FILE_FROM_FILE) {
git_futils_filestamp_set(
&stamp, file ? &file->cache_data.stamp : NULL);
error = load_attr_file(&content, &stamp, filename);
} else {
error = load_attr_blob_from_index(&content, &blob,
repo, file ? &file->cache_data.oid : NULL, relfile);
}
if (error) {
/* not finding a file is not an error for this function */
if (error == GIT_ENOTFOUND) {
giterr_clear();
error = 0;
}
goto finish;
}
/* if we got here, we have to parse and/or reparse the file */
if (file)
git_attr_file__clear_rules(file);
else {
error = git_attr_file__new(&file, source, relfile, &cache->pool);
if (error < 0)
goto finish;
}
if (parse && (error = parse(repo, parsedata, content, file)) < 0)
goto finish;
git_strmap_insert(cache->files, file->key, file, error); //-V595
if (error > 0)
error = 0;
/* remember "cache buster" file signature */
if (blob)
git_oid_cpy(&file->cache_data.oid, git_object_id((git_object *)blob));
else
git_futils_filestamp_set(&file->cache_data.stamp, &stamp);
finish:
/* push file onto vector if we found one*/
if (!error && file != NULL)
error = git_vector_insert(stack, file);
if (error != 0)
git_attr_file__free(file);
if (blob)
git_blob_free(blob);
else
git__free((void *)content);
git_buf_free(&path);
return error;
}
#define push_attr_file(R,S,B,F) \
git_attr_cache__push_file((R),(B),(F),GIT_ATTR_FILE_FROM_FILE,git_attr_file__parse_buffer,NULL,(S))
typedef struct {
git_repository *repo;
uint32_t flags;
@ -483,7 +327,7 @@ typedef struct {
git_vector *files;
} attr_walk_up_info;
int git_attr_cache__decide_sources(
static int attr_decide_sources(
uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs)
{
int count = 0;
@ -491,42 +335,76 @@ int git_attr_cache__decide_sources(
switch (flags & 0x03) {
case GIT_ATTR_CHECK_FILE_THEN_INDEX:
if (has_wd)
srcs[count++] = GIT_ATTR_FILE_FROM_FILE;
srcs[count++] = GIT_ATTR_FILE__FROM_FILE;
if (has_index)
srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;
srcs[count++] = GIT_ATTR_FILE__FROM_INDEX;
break;
case GIT_ATTR_CHECK_INDEX_THEN_FILE:
if (has_index)
srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;
srcs[count++] = GIT_ATTR_FILE__FROM_INDEX;
if (has_wd)
srcs[count++] = GIT_ATTR_FILE_FROM_FILE;
srcs[count++] = GIT_ATTR_FILE__FROM_FILE;
break;
case GIT_ATTR_CHECK_INDEX_ONLY:
if (has_index)
srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;
srcs[count++] = GIT_ATTR_FILE__FROM_INDEX;
break;
}
return count;
}
static int push_attr_file(
git_repository *repo,
git_vector *list,
git_attr_file_source source,
const char *base,
const char *filename)
{
int error = 0;
git_attr_file *file = NULL;
error = git_attr_cache__get(
&file, repo, source, base, filename, git_attr_file__parse_buffer);
if (error < 0)
return error;
if (file != NULL) {
if ((error = git_vector_insert(list, file)) < 0)
git_attr_file__free(file);
}
return error;
}
static int push_one_attr(void *ref, git_buf *path)
{
int error = 0, n_src, i;
attr_walk_up_info *info = (attr_walk_up_info *)ref;
git_attr_file_source src[2];
n_src = git_attr_cache__decide_sources(
n_src = attr_decide_sources(
info->flags, info->workdir != NULL, info->index != NULL, src);
for (i = 0; !error && i < n_src; ++i)
error = git_attr_cache__push_file(
info->repo, path->ptr, GIT_ATTR_FILE, src[i],
git_attr_file__parse_buffer, NULL, info->files);
error = push_attr_file(
info->repo, info->files, src[i], path->ptr, GIT_ATTR_FILE);
return error;
}
static void release_attr_files(git_vector *files)
{
size_t i;
git_attr_file *file;
git_vector_foreach(files, i, file) {
git_attr_file__free(file);
files->contents[i] = NULL;
}
git_vector_free(files);
}
static int collect_attr_files(
git_repository *repo,
uint32_t flags,
@ -536,11 +414,10 @@ static int collect_attr_files(
int error;
git_buf dir = GIT_BUF_INIT;
const char *workdir = git_repository_workdir(repo);
attr_walk_up_info info;
attr_walk_up_info info = { NULL };
if (git_attr_cache__init(repo) < 0 ||
git_vector_init(files, 4, NULL) < 0)
return -1;
if ((error = attr_setup(repo)) < 0)
return error;
/* Resolve path in a non-bare repo */
if (workdir != NULL)
@ -558,7 +435,8 @@ static int collect_attr_files(
*/
error = push_attr_file(
repo, files, git_repository_path(repo), GIT_ATTR_FILE_INREPO);
repo, files, GIT_ATTR_FILE__FROM_FILE,
git_repository_path(repo), GIT_ATTR_FILE_INREPO);
if (error < 0)
goto cleanup;
@ -575,15 +453,17 @@ static int collect_attr_files(
if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) {
error = push_attr_file(
repo, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file);
repo, files, GIT_ATTR_FILE__FROM_FILE,
NULL, git_repository_attr_cache(repo)->cfg_attr_file);
if (error < 0)
goto cleanup;
}
if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM);
error = git_sysdir_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM);
if (!error)
error = push_attr_file(repo, files, NULL, dir.ptr);
error = push_attr_file(
repo, files, GIT_ATTR_FILE__FROM_FILE, NULL, dir.ptr);
else if (error == GIT_ENOTFOUND) {
giterr_clear();
error = 0;
@ -592,153 +472,8 @@ static int collect_attr_files(
cleanup:
if (error < 0)
git_vector_free(files);
release_attr_files(files);
git_buf_free(&dir);
return error;
}
static int attr_cache__lookup_path(
char **out, git_config *cfg, const char *key, const char *fallback)
{
git_buf buf = GIT_BUF_INIT;
int error;
const char *cfgval = NULL;
*out = NULL;
if (!(error = git_config_get_string(&cfgval, cfg, key))) {
/* expand leading ~/ as needed */
if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' &&
!git_futils_find_global_file(&buf, &cfgval[2]))
*out = git_buf_detach(&buf);
else if (cfgval)
*out = git__strdup(cfgval);
} else if (error == GIT_ENOTFOUND) {
giterr_clear();
error = 0;
if (!git_futils_find_xdg_file(&buf, fallback))
*out = git_buf_detach(&buf);
}
git_buf_free(&buf);
return error;
}
int git_attr_cache__init(git_repository *repo)
{
int ret;
git_attr_cache *cache = git_repository_attr_cache(repo);
git_config *cfg;
if (cache->initialized)
return 0;
/* cache config settings for attributes and ignores */
if (git_repository_config__weakptr(&cfg, repo) < 0)
return -1;
ret = attr_cache__lookup_path(
&cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG, GIT_ATTR_FILE_XDG);
if (ret < 0)
return ret;
ret = attr_cache__lookup_path(
&cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG, GIT_IGNORE_FILE_XDG);
if (ret < 0)
return ret;
/* allocate hashtable for attribute and ignore file contents */
if (cache->files == NULL) {
cache->files = git_strmap_alloc();
GITERR_CHECK_ALLOC(cache->files);
}
/* allocate hashtable for attribute macros */
if (cache->macros == NULL) {
cache->macros = git_strmap_alloc();
GITERR_CHECK_ALLOC(cache->macros);
}
/* allocate string pool */
if (git_pool_init(&cache->pool, 1, 0) < 0)
return -1;
cache->initialized = 1;
/* insert default macros */
return git_attr_add_macro(repo, "binary", "-diff -crlf -text");
}
void git_attr_cache_flush(
git_repository *repo)
{
git_attr_cache *cache;
if (!repo)
return;
cache = git_repository_attr_cache(repo);
if (cache->files != NULL) {
git_attr_file *file;
git_strmap_foreach_value(cache->files, file, {
git_attr_file__free(file);
});
git_strmap_free(cache->files);
}
if (cache->macros != NULL) {
git_attr_rule *rule;
git_strmap_foreach_value(cache->macros, rule, {
git_attr_rule__free(rule);
});
git_strmap_free(cache->macros);
}
git_pool_clear(&cache->pool);
git__free(cache->cfg_attr_file);
cache->cfg_attr_file = NULL;
git__free(cache->cfg_excl_file);
cache->cfg_excl_file = NULL;
cache->initialized = 0;
}
int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
{
git_strmap *macros = git_repository_attr_cache(repo)->macros;
int error;
/* TODO: generate warning log if (macro->assigns.length == 0) */
if (macro->assigns.length == 0)
return 0;
git_strmap_insert(macros, macro->match.pattern, macro, error);
return (error < 0) ? -1 : 0;
}
git_attr_rule *git_attr_cache__lookup_macro(
git_repository *repo, const char *name)
{
git_strmap *macros = git_repository_attr_cache(repo)->macros;
khiter_t pos;
pos = git_strmap_lookup_index(macros, name);
if (!git_strmap_valid_index(macros, pos))
return NULL;
return (git_attr_rule *)git_strmap_value_at(macros, pos);
}

View File

@ -8,38 +8,6 @@
#define INCLUDE_attr_h__
#include "attr_file.h"
#define GIT_ATTR_CONFIG "core.attributesfile"
#define GIT_IGNORE_CONFIG "core.excludesfile"
typedef int (*git_attr_file_parser)(
git_repository *, void *, const char *, git_attr_file *);
extern int git_attr_cache__insert_macro(
git_repository *repo, git_attr_rule *macro);
extern git_attr_rule *git_attr_cache__lookup_macro(
git_repository *repo, const char *name);
extern int git_attr_cache__push_file(
git_repository *repo,
const char *base,
const char *filename,
git_attr_file_source source,
git_attr_file_parser parse,
void *parsedata, /* passed through to parse function */
git_vector *stack);
extern int git_attr_cache__internal_file(
git_repository *repo,
const char *key,
git_attr_file **file_ptr);
/* returns true if path is in cache */
extern bool git_attr_cache__is_cached(
git_repository *repo, git_attr_file_source source, const char *path);
extern int git_attr_cache__decide_sources(
uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs);
#include "attrcache.h"
#endif

View File

@ -1,11 +1,210 @@
#include "common.h"
#include "repository.h"
#include "filebuf.h"
#include "attr.h"
#include "attr_file.h"
#include "attrcache.h"
#include "git2/blob.h"
#include "git2/tree.h"
#include "index.h"
#include <ctype.h>
static void attr_file_free(git_attr_file *file)
{
bool unlock = !git_mutex_lock(&file->lock);
git_attr_file__clear_rules(file, false);
git_pool_clear(&file->pool);
if (unlock)
git_mutex_unlock(&file->lock);
git_mutex_free(&file->lock);
git__memzero(file, sizeof(*file));
git__free(file);
}
int git_attr_file__new(
git_attr_file **out,
git_attr_file_entry *entry,
git_attr_file_source source)
{
git_attr_file *attrs = git__calloc(1, sizeof(git_attr_file));
GITERR_CHECK_ALLOC(attrs);
if (git_mutex_init(&attrs->lock) < 0) {
giterr_set(GITERR_OS, "Failed to initialize lock");
git__free(attrs);
return -1;
}
if (git_pool_init(&attrs->pool, 1, 0) < 0) {
attr_file_free(attrs);
return -1;
}
GIT_REFCOUNT_INC(attrs);
attrs->entry = entry;
attrs->source = source;
*out = attrs;
return 0;
}
int git_attr_file__clear_rules(git_attr_file *file, bool need_lock)
{
unsigned int i;
git_attr_rule *rule;
if (need_lock && git_mutex_lock(&file->lock) < 0) {
giterr_set(GITERR_OS, "Failed to lock attribute file");
return -1;
}
git_vector_foreach(&file->rules, i, rule)
git_attr_rule__free(rule);
git_vector_free(&file->rules);
if (need_lock)
git_mutex_unlock(&file->lock);
return 0;
}
void git_attr_file__free(git_attr_file *file)
{
if (!file)
return;
GIT_REFCOUNT_DEC(file, attr_file_free);
}
static int attr_file_oid_from_index(
git_oid *oid, git_repository *repo, const char *path)
{
int error;
git_index *idx;
size_t pos;
const git_index_entry *entry;
if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
(error = git_index__find_pos(&pos, idx, path, 0, 0)) < 0)
return error;
if (!(entry = git_index_get_byindex(idx, pos)))
return GIT_ENOTFOUND;
*oid = entry->id;
return 0;
}
int git_attr_file__load(
git_attr_file **out,
git_repository *repo,
git_attr_file_entry *entry,
git_attr_file_source source,
git_attr_file_parser parser)
{
int error = 0;
git_blob *blob = NULL;
git_buf content = GIT_BUF_INIT;
const char *data = NULL;
git_attr_file *file;
struct stat st;
*out = NULL;
switch (source) {
case GIT_ATTR_FILE__IN_MEMORY:
/* in-memory attribute file doesn't need data */
break;
case GIT_ATTR_FILE__FROM_INDEX: {
git_oid id;
if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 ||
(error = git_blob_lookup(&blob, repo, &id)) < 0)
return error;
data = git_blob_rawcontent(blob);
break;
}
case GIT_ATTR_FILE__FROM_FILE: {
int fd;
if (p_stat(entry->fullpath, &st) < 0)
return git_path_set_error(errno, entry->fullpath, "stat");
if (S_ISDIR(st.st_mode))
return GIT_ENOTFOUND;
/* For open or read errors, return ENOTFOUND to skip item */
/* TODO: issue warning when warning API is available */
if ((fd = git_futils_open_ro(entry->fullpath)) < 0)
return GIT_ENOTFOUND;
error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size);
p_close(fd);
if (error < 0)
return GIT_ENOTFOUND;
data = content.ptr;
break;
}
default:
giterr_set(GITERR_INVALID, "Unknown file source %d", source);
return -1;
}
if ((error = git_attr_file__new(&file, entry, source)) < 0)
goto cleanup;
if (parser && (error = parser(repo, file, data)) < 0) {
git_attr_file__free(file);
goto cleanup;
}
/* write cache breaker */
if (source == GIT_ATTR_FILE__FROM_INDEX)
git_oid_cpy(&file->cache_data.oid, git_blob_id(blob));
else if (source == GIT_ATTR_FILE__FROM_FILE)
git_futils_filestamp_set_from_stat(&file->cache_data.stamp, &st);
/* else always cacheable */
*out = file;
cleanup:
git_blob_free(blob);
git_buf_free(&content);
return error;
}
int git_attr_file__out_of_date(git_repository *repo, git_attr_file *file)
{
if (!file)
return 1;
switch (file->source) {
case GIT_ATTR_FILE__IN_MEMORY:
return 0;
case GIT_ATTR_FILE__FROM_FILE:
return git_futils_filestamp_check(
&file->cache_data.stamp, file->entry->fullpath);
case GIT_ATTR_FILE__FROM_INDEX: {
int error;
git_oid id;
if ((error = attr_file_oid_from_index(
&id, repo, file->entry->path)) < 0)
return error;
return (git_oid__cmp(&file->cache_data.oid, &id) != 0);
}
default:
giterr_set(GITERR_INVALID, "Invalid file type %d", file->source);
return -1;
}
}
static int sort_by_hash_and_name(const void *a_raw, const void *b_raw);
static void git_attr_rule__clear(git_attr_rule *rule);
static bool parse_optimized_patterns(
@ -13,91 +212,42 @@ static bool parse_optimized_patterns(
git_pool *pool,
const char *pattern);
int git_attr_file__new(
git_attr_file **attrs_ptr,
git_attr_file_source from,
const char *path,
git_pool *pool)
{
git_attr_file *attrs = NULL;
attrs = git__calloc(1, sizeof(git_attr_file));
GITERR_CHECK_ALLOC(attrs);
if (pool)
attrs->pool = pool;
else {
attrs->pool = git__calloc(1, sizeof(git_pool));
if (!attrs->pool || git_pool_init(attrs->pool, 1, 0) < 0)
goto fail;
attrs->pool_is_allocated = true;
}
if (path) {
size_t len = strlen(path);
attrs->key = git_pool_malloc(attrs->pool, (uint32_t)len + 3);
GITERR_CHECK_ALLOC(attrs->key);
attrs->key[0] = '0' + (char)from;
attrs->key[1] = '#';
memcpy(&attrs->key[2], path, len);
attrs->key[len + 2] = '\0';
}
if (git_vector_init(&attrs->rules, 4, NULL) < 0)
goto fail;
*attrs_ptr = attrs;
return 0;
fail:
git_attr_file__free(attrs);
attrs_ptr = NULL;
return -1;
}
int git_attr_file__parse_buffer(
git_repository *repo, void *parsedata, const char *buffer, git_attr_file *attrs)
git_repository *repo, git_attr_file *attrs, const char *data)
{
int error = 0;
const char *scan = NULL;
char *context = NULL;
const char *scan = data, *context = NULL;
git_attr_rule *rule = NULL;
GIT_UNUSED(parsedata);
assert(buffer && attrs);
scan = buffer;
/* if subdir file path, convert context for file paths */
if (attrs->key && git__suffixcmp(attrs->key, "/" GIT_ATTR_FILE) == 0) {
context = attrs->key + 2;
context[strlen(context) - strlen(GIT_ATTR_FILE)] = '\0';
if (attrs->entry &&
git_path_root(attrs->entry->path) < 0 &&
!git__suffixcmp(attrs->entry->path, "/" GIT_ATTR_FILE))
context = attrs->entry->path;
if (git_mutex_lock(&attrs->lock) < 0) {
giterr_set(GITERR_OS, "Failed to lock attribute file");
return -1;
}
while (!error && *scan) {
/* allocate rule if needed */
if (!rule) {
if (!(rule = git__calloc(1, sizeof(git_attr_rule)))) {
error = -1;
break;
}
rule->match.flags = GIT_ATTR_FNMATCH_ALLOWNEG |
GIT_ATTR_FNMATCH_ALLOWMACRO;
if (!rule && !(rule = git__calloc(1, sizeof(*rule)))) {
error = -1;
break;
}
rule->match.flags =
GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO;
/* parse the next "pattern attr attr attr" line */
if (!(error = git_attr_fnmatch__parse(
&rule->match, attrs->pool, context, &scan)) &&
&rule->match, &attrs->pool, context, &scan)) &&
!(error = git_attr_assignment__parse(
repo, attrs->pool, &rule->assigns, &scan)))
repo, &attrs->pool, &rule->assigns, &scan)))
{
if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO)
/* should generate error/warning if this is coming from any
* file other than .gitattributes at repo root.
*/
/* TODO: warning if macro found in file below repo root */
error = git_attr_cache__insert_macro(repo, rule);
else
error = git_vector_insert(&attrs->rules, rule);
@ -113,66 +263,12 @@ int git_attr_file__parse_buffer(
}
}
git_mutex_unlock(&attrs->lock);
git_attr_rule__free(rule);
/* restore file path used for context */
if (context)
context[strlen(context)] = '.'; /* first char of GIT_ATTR_FILE */
return error;
}
int git_attr_file__new_and_load(
git_attr_file **attrs_ptr,
const char *path)
{
int error;
git_buf content = GIT_BUF_INIT;
if ((error = git_attr_file__new(attrs_ptr, 0, path, NULL)) < 0)
return error;
if (!(error = git_futils_readbuffer(&content, path)))
error = git_attr_file__parse_buffer(
NULL, NULL, git_buf_cstr(&content), *attrs_ptr);
git_buf_free(&content);
if (error) {
git_attr_file__free(*attrs_ptr);
*attrs_ptr = NULL;
}
return error;
}
void git_attr_file__clear_rules(git_attr_file *file)
{
unsigned int i;
git_attr_rule *rule;
git_vector_foreach(&file->rules, i, rule)
git_attr_rule__free(rule);
git_vector_free(&file->rules);
}
void git_attr_file__free(git_attr_file *file)
{
if (!file)
return;
git_attr_file__clear_rules(file);
if (file->pool_is_allocated) {
git_pool_clear(file->pool);
git__free(file->pool);
}
file->pool = NULL;
git__free(file);
}
uint32_t git_attr_file__name_hash(const char *name)
{
uint32_t h = 5381;
@ -183,10 +279,9 @@ uint32_t git_attr_file__name_hash(const char *name)
return h;
}
int git_attr_file__lookup_one(
git_attr_file *file,
const git_attr_path *path,
git_attr_path *path,
const char *attr,
const char **value)
{
@ -212,30 +307,83 @@ int git_attr_file__lookup_one(
return 0;
}
int git_attr_file__load_standalone(git_attr_file **out, const char *path)
{
int error;
git_attr_file *file;
git_buf content = GIT_BUF_INIT;
error = git_attr_file__new(&file, NULL, GIT_ATTR_FILE__FROM_FILE);
if (error < 0)
return error;
error = git_attr_cache__alloc_file_entry(
&file->entry, NULL, path, &file->pool);
if (error < 0) {
git_attr_file__free(file);
return error;
}
/* because the cache entry is allocated from the file's own pool, we
* don't have to free it - freeing file+pool will free cache entry, too.
*/
if (!(error = git_futils_readbuffer(&content, path))) {
error = git_attr_file__parse_buffer(NULL, file, content.ptr);
git_buf_free(&content);
}
if (error < 0)
git_attr_file__free(file);
else
*out = file;
return error;
}
bool git_attr_fnmatch__match(
git_attr_fnmatch *match,
const git_attr_path *path)
git_attr_path *path)
{
int fnm;
int icase_flags = (match->flags & GIT_ATTR_FNMATCH_ICASE) ? FNM_CASEFOLD : 0;
const char *filename;
int flags = 0;
if (match->flags & GIT_ATTR_FNMATCH_DIRECTORY && !path->is_dir)
return false;
if (match->flags & GIT_ATTR_FNMATCH_ICASE)
flags |= FNM_CASEFOLD;
if (match->flags & GIT_ATTR_FNMATCH_LEADINGDIR)
flags |= FNM_LEADING_DIR;
if (match->flags & GIT_ATTR_FNMATCH_FULLPATH)
fnm = p_fnmatch(match->pattern, path->path, FNM_PATHNAME | icase_flags);
else if (path->is_dir)
fnm = p_fnmatch(match->pattern, path->basename, FNM_LEADING_DIR | icase_flags);
else
fnm = p_fnmatch(match->pattern, path->basename, icase_flags);
if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) {
filename = path->path;
flags |= FNM_PATHNAME;
} else {
filename = path->basename;
return (fnm == FNM_NOMATCH) ? false : true;
if (path->is_dir)
flags |= FNM_LEADING_DIR;
}
if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir) {
int matchval;
/* for attribute checks or root ignore checks, fail match */
if (!(match->flags & GIT_ATTR_FNMATCH_IGNORE) ||
path->basename == path->path)
return false;
/* for ignore checks, use container of current item for check */
path->basename[-1] = '\0';
flags |= FNM_LEADING_DIR;
matchval = p_fnmatch(match->pattern, path->path, flags);
path->basename[-1] = '/';
return (matchval != FNM_NOMATCH);
}
return (p_fnmatch(match->pattern, filename, flags) != FNM_NOMATCH);
}
bool git_attr_rule__match(
git_attr_rule *rule,
const git_attr_path *path)
git_attr_path *path)
{
bool matched = git_attr_fnmatch__match(&rule->match, path);
@ -245,7 +393,6 @@ bool git_attr_rule__match(
return matched;
}
git_attr_assignment *git_attr_rule__lookup_assignment(
git_attr_rule *rule, const char *name)
{
@ -344,7 +491,7 @@ void git_attr_path__free(git_attr_path *info)
int git_attr_fnmatch__parse(
git_attr_fnmatch *spec,
git_pool *pool,
const char *source,
const char *context,
const char **base)
{
const char *pattern, *scan;
@ -410,20 +557,31 @@ int git_attr_fnmatch__parse(
if (--slash_count <= 0)
spec->flags = spec->flags & ~GIT_ATTR_FNMATCH_FULLPATH;
}
if ((spec->flags & GIT_ATTR_FNMATCH_NOLEADINGDIR) == 0 &&
spec->length >= 2 &&
pattern[spec->length - 1] == '*' &&
pattern[spec->length - 2] == '/') {
spec->length -= 2;
spec->flags = spec->flags | GIT_ATTR_FNMATCH_LEADINGDIR;
/* leave FULLPATH match on, however */
}
if ((spec->flags & GIT_ATTR_FNMATCH_FULLPATH) != 0 &&
source != NULL && git_path_root(pattern) < 0)
context != NULL && git_path_root(pattern) < 0)
{
size_t sourcelen = strlen(source);
/* use context path minus the trailing filename */
char *slash = strrchr(context, '/');
size_t contextlen = slash ? slash - context + 1 : 0;
/* given an unrooted fullpath match from a file inside a repo,
* prefix the pattern with the relative directory of the source file
*/
spec->pattern = git_pool_malloc(
pool, (uint32_t)(sourcelen + spec->length + 1));
pool, (uint32_t)(contextlen + spec->length + 1));
if (spec->pattern) {
memcpy(spec->pattern, source, sourcelen);
memcpy(spec->pattern + sourcelen, pattern, spec->length);
spec->length += sourcelen;
memcpy(spec->pattern, context, contextlen);
memcpy(spec->pattern + contextlen, pattern, spec->length);
spec->length += contextlen;
spec->pattern[spec->length] = '\0';
}
} else {
@ -436,6 +594,7 @@ int git_attr_fnmatch__parse(
} else {
/* strip '\' that might have be used for internal whitespace */
spec->length = git__unescape(spec->pattern);
/* TODO: convert remaining '\' into '/' for POSIX ??? */
}
return 0;

View File

@ -30,10 +30,20 @@
#define GIT_ATTR_FNMATCH_MATCH_ALL (1U << 8)
#define GIT_ATTR_FNMATCH_ALLOWNEG (1U << 9)
#define GIT_ATTR_FNMATCH_ALLOWMACRO (1U << 10)
#define GIT_ATTR_FNMATCH_LEADINGDIR (1U << 11)
#define GIT_ATTR_FNMATCH_NOLEADINGDIR (1U << 12)
#define GIT_ATTR_FNMATCH__INCOMING \
(GIT_ATTR_FNMATCH_ALLOWSPACE | \
GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO)
(GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG | \
GIT_ATTR_FNMATCH_ALLOWMACRO | GIT_ATTR_FNMATCH_NOLEADINGDIR)
typedef enum {
GIT_ATTR_FILE__IN_MEMORY = 0,
GIT_ATTR_FILE__FROM_FILE = 1,
GIT_ATTR_FILE__FROM_INDEX = 2,
GIT_ATTR_FILE_NUM_SOURCES = 3
} git_attr_file_source;
extern const char *git_attr__true;
extern const char *git_attr__false;
@ -63,17 +73,32 @@ typedef struct {
const char *value;
} git_attr_assignment;
typedef struct git_attr_file_entry git_attr_file_entry;
typedef struct {
char *key; /* cache "source#path" this was loaded from */
git_vector rules; /* vector of <rule*> or <fnmatch*> */
git_pool *pool;
bool pool_is_allocated;
git_refcount rc;
git_mutex lock;
git_attr_file_entry *entry;
git_attr_file_source source;
git_vector rules; /* vector of <rule*> or <fnmatch*> */
git_pool pool;
union {
git_oid oid;
git_futils_filestamp stamp;
} cache_data;
} git_attr_file;
struct git_attr_file_entry {
git_attr_file *file[GIT_ATTR_FILE_NUM_SOURCES];
const char *path; /* points into fullpath */
char fullpath[GIT_FLEX_ARRAY];
};
typedef int (*git_attr_file_parser)(
git_repository *repo,
git_attr_file *file,
const char *data);
typedef struct {
git_buf full;
char *path;
@ -81,31 +106,39 @@ typedef struct {
int is_dir;
} git_attr_path;
typedef enum {
GIT_ATTR_FILE_FROM_FILE = 0,
GIT_ATTR_FILE_FROM_INDEX = 1
} git_attr_file_source;
/*
* git_attr_file API
*/
extern int git_attr_file__new(
git_attr_file **attrs_ptr, git_attr_file_source src, const char *path, git_pool *pool);
int git_attr_file__new(
git_attr_file **out,
git_attr_file_entry *entry,
git_attr_file_source source);
extern int git_attr_file__new_and_load(
git_attr_file **attrs_ptr, const char *path);
void git_attr_file__free(git_attr_file *file);
extern void git_attr_file__free(git_attr_file *file);
int git_attr_file__load(
git_attr_file **out,
git_repository *repo,
git_attr_file_entry *ce,
git_attr_file_source source,
git_attr_file_parser parser);
extern void git_attr_file__clear_rules(git_attr_file *file);
int git_attr_file__load_standalone(
git_attr_file **out, const char *path);
extern int git_attr_file__parse_buffer(
git_repository *repo, void *parsedata, const char *buf, git_attr_file *file);
int git_attr_file__out_of_date(
git_repository *repo, git_attr_file *file);
extern int git_attr_file__lookup_one(
int git_attr_file__parse_buffer(
git_repository *repo, git_attr_file *attrs, const char *data);
int git_attr_file__clear_rules(
git_attr_file *file, bool need_lock);
int git_attr_file__lookup_one(
git_attr_file *file,
const git_attr_path *path,
git_attr_path *path,
const char *attr,
const char **value);
@ -114,7 +147,7 @@ extern int git_attr_file__lookup_one(
git_vector_rforeach(&(file)->rules, (iter), (rule)) \
if (git_attr_rule__match((rule), (path)))
extern uint32_t git_attr_file__name_hash(const char *name);
uint32_t git_attr_file__name_hash(const char *name);
/*
@ -129,13 +162,13 @@ extern int git_attr_fnmatch__parse(
extern bool git_attr_fnmatch__match(
git_attr_fnmatch *rule,
const git_attr_path *path);
git_attr_path *path);
extern void git_attr_rule__free(git_attr_rule *rule);
extern bool git_attr_rule__match(
git_attr_rule *rule,
const git_attr_path *path);
git_attr_path *path);
extern git_attr_assignment *git_attr_rule__lookup_assignment(
git_attr_rule *rule, const char *name);

449
src/attrcache.c Normal file
View File

@ -0,0 +1,449 @@
#include "common.h"
#include "repository.h"
#include "attr_file.h"
#include "config.h"
#include "sysdir.h"
#include "ignore.h"
GIT__USE_STRMAP;
GIT_INLINE(int) attr_cache_lock(git_attr_cache *cache)
{
GIT_UNUSED(cache); /* avoid warning if threading is off */
if (git_mutex_lock(&cache->lock) < 0) {
giterr_set(GITERR_OS, "Unable to get attr cache lock");
return -1;
}
return 0;
}
GIT_INLINE(void) attr_cache_unlock(git_attr_cache *cache)
{
GIT_UNUSED(cache); /* avoid warning if threading is off */
git_mutex_unlock(&cache->lock);
}
GIT_INLINE(git_attr_file_entry *) attr_cache_lookup_entry(
git_attr_cache *cache, const char *path)
{
khiter_t pos = git_strmap_lookup_index(cache->files, path);
if (git_strmap_valid_index(cache->files, pos))
return git_strmap_value_at(cache->files, pos);
else
return NULL;
}
int git_attr_cache__alloc_file_entry(
git_attr_file_entry **out,
const char *base,
const char *path,
git_pool *pool)
{
size_t baselen = 0, pathlen = strlen(path);
size_t cachesize = sizeof(git_attr_file_entry) + pathlen + 1;
git_attr_file_entry *ce;
if (base != NULL && git_path_root(path) < 0) {
baselen = strlen(base);
cachesize += baselen;
if (baselen && base[baselen - 1] != '/')
cachesize++;
}
ce = git_pool_mallocz(pool, (uint32_t)cachesize);
GITERR_CHECK_ALLOC(ce);
if (baselen) {
memcpy(ce->fullpath, base, baselen);
if (base[baselen - 1] != '/')
ce->fullpath[baselen++] = '/';
}
memcpy(&ce->fullpath[baselen], path, pathlen);
ce->path = &ce->fullpath[baselen];
*out = ce;
return 0;
}
/* call with attrcache locked */
static int attr_cache_make_entry(
git_attr_file_entry **out, git_repository *repo, const char *path)
{
int error = 0;
git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file_entry *entry = NULL;
error = git_attr_cache__alloc_file_entry(
&entry, git_repository_workdir(repo), path, &cache->pool);
if (!error) {
git_strmap_insert(cache->files, entry->path, entry, error);
if (error > 0)
error = 0;
}
*out = entry;
return error;
}
/* insert entry or replace existing if we raced with another thread */
static int attr_cache_upsert(git_attr_cache *cache, git_attr_file *file)
{
git_attr_file_entry *entry;
git_attr_file *old;
if (attr_cache_lock(cache) < 0)
return -1;
entry = attr_cache_lookup_entry(cache, file->entry->path);
GIT_REFCOUNT_OWN(file, entry);
GIT_REFCOUNT_INC(file);
old = git__compare_and_swap(
&entry->file[file->source], entry->file[file->source], file);
if (old) {
GIT_REFCOUNT_OWN(old, NULL);
git_attr_file__free(old);
}
attr_cache_unlock(cache);
return 0;
}
static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file)
{
int error = 0;
git_attr_file_entry *entry;
if (!file)
return 0;
if ((error = attr_cache_lock(cache)) < 0)
return error;
if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL)
file = git__compare_and_swap(&entry->file[file->source], file, NULL);
attr_cache_unlock(cache);
if (file) {
GIT_REFCOUNT_OWN(file, NULL);
git_attr_file__free(file);
}
return error;
}
/* Look up cache entry and file.
* - If entry is not present, create it while the cache is locked.
* - If file is present, increment refcount before returning it, so the
* cache can be unlocked and it won't go away.
*/
static int attr_cache_lookup(
git_attr_file **out_file,
git_attr_file_entry **out_entry,
git_repository *repo,
git_attr_file_source source,
const char *base,
const char *filename)
{
int error = 0;
git_buf path = GIT_BUF_INIT;
const char *wd = git_repository_workdir(repo), *relfile;
git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file_entry *entry = NULL;
git_attr_file *file = NULL;
/* join base and path as needed */
if (base != NULL && git_path_root(filename) < 0) {
if (git_buf_joinpath(&path, base, filename) < 0)
return -1;
filename = path.ptr;
}
relfile = filename;
if (wd && !git__prefixcmp(relfile, wd))
relfile += strlen(wd);
/* check cache for existing entry */
if ((error = attr_cache_lock(cache)) < 0)
goto cleanup;
entry = attr_cache_lookup_entry(cache, relfile);
if (!entry)
error = attr_cache_make_entry(&entry, repo, relfile);
else if (entry->file[source] != NULL) {
file = entry->file[source];
GIT_REFCOUNT_INC(file);
}
attr_cache_unlock(cache);
cleanup:
*out_file = file;
*out_entry = entry;
git_buf_free(&path);
return error;
}
int git_attr_cache__get(
git_attr_file **out,
git_repository *repo,
git_attr_file_source source,
const char *base,
const char *filename,
git_attr_file_parser parser)
{
int error = 0;
git_attr_cache *cache = git_repository_attr_cache(repo);
git_attr_file_entry *entry = NULL;
git_attr_file *file = NULL, *updated = NULL;
if ((error = attr_cache_lookup(
&file, &entry, repo, source, base, filename)) < 0)
return error;
/* load file if we don't have one or if existing one is out of date */
if (!file || (error = git_attr_file__out_of_date(repo, file)) > 0)
error = git_attr_file__load(&updated, repo, entry, source, parser);
/* if we loaded the file, insert into and/or update cache */
if (updated) {
if ((error = attr_cache_upsert(cache, updated)) < 0)
git_attr_file__free(updated);
else {
git_attr_file__free(file); /* offset incref from lookup */
file = updated;
}
}
/* if file could not be loaded */
if (error < 0) {
/* remove existing entry */
if (file) {
attr_cache_remove(cache, file);
git_attr_file__free(file); /* offset incref from lookup */
file = NULL;
}
/* no error if file simply doesn't exist */
if (error == GIT_ENOTFOUND) {
giterr_clear();
error = 0;
}
}
*out = file;
return error;
}
bool git_attr_cache__is_cached(
git_repository *repo,
git_attr_file_source source,
const char *filename)
{
git_attr_cache *cache = git_repository_attr_cache(repo);
git_strmap *files;
khiter_t pos;
git_attr_file_entry *entry;
if (!cache || !(files = cache->files))
return false;
pos = git_strmap_lookup_index(files, filename);
if (!git_strmap_valid_index(files, pos))
return false;
entry = git_strmap_value_at(files, pos);
return entry && (entry->file[source] != NULL);
}
static int attr_cache__lookup_path(
char **out, git_config *cfg, const char *key, const char *fallback)
{
git_buf buf = GIT_BUF_INIT;
int error;
const git_config_entry *entry = NULL;
*out = NULL;
if ((error = git_config__lookup_entry(&entry, cfg, key, false)) < 0)
return error;
if (entry) {
const char *cfgval = entry->value;
/* expand leading ~/ as needed */
if (cfgval && cfgval[0] == '~' && cfgval[1] == '/' &&
!git_sysdir_find_global_file(&buf, &cfgval[2]))
*out = git_buf_detach(&buf);
else if (cfgval)
*out = git__strdup(cfgval);
}
else if (!git_sysdir_find_xdg_file(&buf, fallback))
*out = git_buf_detach(&buf);
git_buf_free(&buf);
return error;
}
static void attr_cache__free(git_attr_cache *cache)
{
bool unlock;
if (!cache)
return;
unlock = (git_mutex_lock(&cache->lock) == 0);
if (cache->files != NULL) {
git_attr_file_entry *entry;
git_attr_file *file;
int i;
git_strmap_foreach_value(cache->files, entry, {
for (i = 0; i < GIT_ATTR_FILE_NUM_SOURCES; ++i) {
if ((file = git__swap(entry->file[i], NULL)) != NULL) {
GIT_REFCOUNT_OWN(file, NULL);
git_attr_file__free(file);
}
}
});
git_strmap_free(cache->files);
}
if (cache->macros != NULL) {
git_attr_rule *rule;
git_strmap_foreach_value(cache->macros, rule, {
git_attr_rule__free(rule);
});
git_strmap_free(cache->macros);
}
git_pool_clear(&cache->pool);
git__free(cache->cfg_attr_file);
cache->cfg_attr_file = NULL;
git__free(cache->cfg_excl_file);
cache->cfg_excl_file = NULL;
if (unlock)
git_mutex_unlock(&cache->lock);
git_mutex_free(&cache->lock);
git__free(cache);
}
int git_attr_cache__do_init(git_repository *repo)
{
int ret = 0;
git_attr_cache *cache = git_repository_attr_cache(repo);
git_config *cfg = NULL;
if (cache)
return 0;
cache = git__calloc(1, sizeof(git_attr_cache));
GITERR_CHECK_ALLOC(cache);
/* set up lock */
if (git_mutex_init(&cache->lock) < 0) {
giterr_set(GITERR_OS, "Unable to initialize lock for attr cache");
git__free(cache);
return -1;
}
if ((ret = git_repository_config_snapshot(&cfg, repo)) < 0)
goto cancel;
/* cache config settings for attributes and ignores */
ret = attr_cache__lookup_path(
&cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG, GIT_ATTR_FILE_XDG);
if (ret < 0)
goto cancel;
ret = attr_cache__lookup_path(
&cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG, GIT_IGNORE_FILE_XDG);
if (ret < 0)
goto cancel;
/* allocate hashtable for attribute and ignore file contents,
* hashtable for attribute macros, and string pool
*/
if ((ret = git_strmap_alloc(&cache->files)) < 0 ||
(ret = git_strmap_alloc(&cache->macros)) < 0 ||
(ret = git_pool_init(&cache->pool, 1, 0)) < 0)
goto cancel;
cache = git__compare_and_swap(&repo->attrcache, NULL, cache);
if (cache)
goto cancel; /* raced with another thread, free this but no error */
git_config_free(cfg);
/* insert default macros */
return git_attr_add_macro(repo, "binary", "-diff -crlf -text");
cancel:
attr_cache__free(cache);
git_config_free(cfg);
return ret;
}
void git_attr_cache_flush(git_repository *repo)
{
git_attr_cache *cache;
/* this could be done less expensively, but for now, we'll just free
* the entire attrcache and let the next use reinitialize it...
*/
if (repo && (cache = git__swap(repo->attrcache, NULL)) != NULL)
attr_cache__free(cache);
}
int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
{
git_attr_cache *cache = git_repository_attr_cache(repo);
git_strmap *macros = cache->macros;
int error;
/* TODO: generate warning log if (macro->assigns.length == 0) */
if (macro->assigns.length == 0)
return 0;
if (git_mutex_lock(&cache->lock) < 0) {
giterr_set(GITERR_OS, "Unable to get attr cache lock");
error = -1;
} else {
git_strmap_insert(macros, macro->match.pattern, macro, error);
git_mutex_unlock(&cache->lock);
}
return (error < 0) ? -1 : 0;
}
git_attr_rule *git_attr_cache__lookup_macro(
git_repository *repo, const char *name)
{
git_strmap *macros = git_repository_attr_cache(repo)->macros;
khiter_t pos;
pos = git_strmap_lookup_index(macros, name);
if (!git_strmap_valid_index(macros, pos))
return NULL;
return (git_attr_rule *)git_strmap_value_at(macros, pos);
}

View File

@ -7,18 +7,50 @@
#ifndef INCLUDE_attrcache_h__
#define INCLUDE_attrcache_h__
#include "pool.h"
#include "attr_file.h"
#include "strmap.h"
#define GIT_ATTR_CONFIG "core.attributesfile"
#define GIT_IGNORE_CONFIG "core.excludesfile"
typedef struct {
int initialized;
git_pool pool;
git_strmap *files; /* hash path to git_attr_file of rules */
git_strmap *macros; /* hash name to vector<git_attr_assignment> */
char *cfg_attr_file; /* cached value of core.attributesfile */
char *cfg_excl_file; /* cached value of core.excludesfile */
git_strmap *files; /* hash path to git_attr_cache_entry records */
git_strmap *macros; /* hash name to vector<git_attr_assignment> */
git_mutex lock;
git_pool pool;
} git_attr_cache;
extern int git_attr_cache__init(git_repository *repo);
extern int git_attr_cache__do_init(git_repository *repo);
#define git_attr_cache__init(REPO) \
(git_repository_attr_cache(REPO) ? 0 : git_attr_cache__do_init(REPO))
/* get file - loading and reload as needed */
extern int git_attr_cache__get(
git_attr_file **file,
git_repository *repo,
git_attr_file_source source,
const char *base,
const char *filename,
git_attr_file_parser parser);
extern bool git_attr_cache__is_cached(
git_repository *repo,
git_attr_file_source source,
const char *path);
extern int git_attr_cache__alloc_file_entry(
git_attr_file_entry **out,
const char *base,
const char *path,
git_pool *pool);
extern int git_attr_cache__insert_macro(
git_repository *repo, git_attr_rule *macro);
extern git_attr_rule *git_attr_cache__lookup_macro(
git_repository *repo, const char *name);
#endif

View File

@ -7,7 +7,7 @@
#ifndef INCLUDE_bitvec_h__
#define INCLUDE_bitvec_h__
#include "util.h"
#include "common.h"
/*
* This is a silly little fixed length bit vector type that will store

View File

@ -20,12 +20,15 @@
static int hunk_byfinalline_search_cmp(const void *key, const void *entry)
{
uint16_t lineno = (uint16_t)*(size_t*)key;
git_blame_hunk *hunk = (git_blame_hunk*)entry;
if (lineno < hunk->final_start_line_number)
size_t lineno = *(size_t*)key;
size_t lines_in_hunk = (size_t)hunk->lines_in_hunk;
size_t final_start_line_number = (size_t)hunk->final_start_line_number;
if (lineno < final_start_line_number)
return -1;
if (lineno >= hunk->final_start_line_number + hunk->lines_in_hunk)
if (lineno >= final_start_line_number + lines_in_hunk)
return 1;
return 0;
}
@ -76,8 +79,8 @@ static git_blame_hunk* dup_hunk(git_blame_hunk *hunk)
git_oid_cpy(&newhunk->orig_commit_id, &hunk->orig_commit_id);
git_oid_cpy(&newhunk->final_commit_id, &hunk->final_commit_id);
newhunk->boundary = hunk->boundary;
newhunk->final_signature = git_signature_dup(hunk->final_signature);
newhunk->orig_signature = git_signature_dup(hunk->orig_signature);
git_signature_dup(&newhunk->final_signature, hunk->final_signature);
git_signature_dup(&newhunk->orig_signature, hunk->orig_signature);
return newhunk;
}
@ -95,7 +98,7 @@ static void shift_hunks_by(git_vector *v, size_t start_line, int shift_by)
{
size_t i;
if (!git_vector_bsearch2( &i, v, hunk_byfinalline_search_cmp, &start_line)) {
if (!git_vector_bsearch2(&i, v, hunk_byfinalline_search_cmp, &start_line)) {
for (; i < v->length; i++) {
git_blame_hunk *hunk = (git_blame_hunk*)v->contents[i];
hunk->final_start_line_number += shift_by;
@ -108,17 +111,22 @@ git_blame* git_blame__alloc(
git_blame_options opts,
const char *path)
{
git_blame *gbr = (git_blame*)git__calloc(1, sizeof(git_blame));
if (!gbr) {
giterr_set_oom();
git_blame *gbr = git__calloc(1, sizeof(git_blame));
if (!gbr)
return NULL;
}
git_vector_init(&gbr->hunks, 8, hunk_cmp);
git_vector_init(&gbr->paths, 8, paths_cmp);
gbr->repository = repo;
gbr->options = opts;
gbr->path = git__strdup(path);
git_vector_insert(&gbr->paths, git__strdup(path));
if (git_vector_init(&gbr->hunks, 8, hunk_cmp) < 0 ||
git_vector_init(&gbr->paths, 8, paths_cmp) < 0 ||
(gbr->path = git__strdup(path)) == NULL ||
git_vector_insert(&gbr->paths, git__strdup(path)) < 0)
{
git_blame_free(gbr);
return NULL;
}
return gbr;
}
@ -126,7 +134,6 @@ void git_blame_free(git_blame *blame)
{
size_t i;
git_blame_hunk *hunk;
char *path;
if (!blame) return;
@ -134,13 +141,11 @@ void git_blame_free(git_blame *blame)
free_hunk(hunk);
git_vector_free(&blame->hunks);
git_vector_foreach(&blame->paths, i, path)
git__free(path);
git_vector_free(&blame->paths);
git_vector_free_deep(&blame->paths);
git_array_clear(blame->line_index);
git__free((void*)blame->path);
git__free(blame->path);
git_blob_free(blame->final_blob);
git__free(blame);
}
@ -159,10 +164,10 @@ const git_blame_hunk *git_blame_get_hunk_byindex(git_blame *blame, uint32_t inde
const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, uint32_t lineno)
{
size_t i;
size_t i, new_lineno = (size_t)lineno;
assert(blame);
if (!git_vector_bsearch2( &i, &blame->hunks, hunk_byfinalline_search_cmp, &lineno)) {
if (!git_vector_bsearch2(&i, &blame->hunks, hunk_byfinalline_search_cmp, &new_lineno)) {
return git_blame_get_hunk_byindex(blame, (uint32_t)i);
}
@ -239,7 +244,7 @@ static int index_blob_lines(git_blame *blame)
git_off_t len = blame->final_buf_size;
int num = 0, incomplete = 0, bol = 1;
size_t *i;
if (len && buf[len-1] != '\n')
incomplete++; /* incomplete line at the end */
while (len--) {
@ -260,13 +265,15 @@ static int index_blob_lines(git_blame *blame)
blame->num_lines = num + incomplete;
return blame->num_lines;
}
static git_blame_hunk* hunk_from_entry(git_blame__entry *e)
{
git_blame_hunk *h = new_hunk(
e->lno+1, e->num_lines, e->s_lno+1, e->suspect->path);
git_oid_cpy(&h->final_commit_id, git_commit_id(e->suspect->commit));
h->final_signature = git_signature_dup(git_commit_author(e->suspect->commit));
git_oid_cpy(&h->orig_commit_id, git_commit_id(e->suspect->commit));
git_signature_dup(&h->final_signature, git_commit_author(e->suspect->commit));
git_signature_dup(&h->orig_signature, git_commit_author(e->suspect->commit));
h->boundary = e->is_boundary ? 1 : 0;
return h;
}
@ -282,8 +289,6 @@ static int load_blob(git_blame *blame)
goto cleanup;
error = git_object_lookup_bypath((git_object**)&blame->final_blob,
(git_object*)blame->final, blame->path, GIT_OBJ_BLOB);
if (error < 0)
goto cleanup;
cleanup:
return error;
@ -448,7 +453,7 @@ int git_blame_buffer(
git_blame **out,
git_blame *reference,
const char *buffer,
uint32_t buffer_len)
size_t buffer_len)
{
git_blame *blame;
git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
@ -474,3 +479,10 @@ int git_blame_buffer(
*out = blame;
return 0;
}
int git_blame_init_options(git_blame_options *opts, unsigned int version)
{
GIT_INIT_STRUCTURE_FROM_TEMPLATE(
opts, version, git_blame_options, GIT_BLAME_OPTIONS_INIT);
return 0;
}

View File

@ -64,7 +64,7 @@ typedef struct git_blame__entry {
} git_blame__entry;
struct git_blame {
const char *path;
char *path;
git_repository *repository;
git_blame_options options;

View File

@ -485,12 +485,14 @@ static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt
git_blame__origin *sg_buf[16];
git_blame__origin *porigin, **sg_origin = sg_buf;
GIT_UNUSED(opt);
num_parents = git_commit_parentcount(commit);
if (!git_oid_cmp(git_commit_id(commit), &blame->options.oldest_commit))
/* Stop at oldest specified commit */
num_parents = 0;
else if (opt & GIT_BLAME_FIRST_PARENT && num_parents > 1)
/* Limit search to the first parent */
num_parents = 1;
if (!num_parents) {
git_oid_cpy(&blame->options.oldest_commit, git_commit_id(commit));
goto finish;

View File

@ -50,25 +50,28 @@ int git_blob__parse(void *blob, git_odb_object *odb_obj)
return 0;
}
int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len)
int git_blob_create_frombuffer(
git_oid *id, git_repository *repo, const void *buffer, size_t len)
{
int error;
git_odb *odb;
git_odb_stream *stream;
assert(id && repo);
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
(error = git_odb_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < 0)
return error;
if ((error = git_odb_stream_write(stream, buffer, len)) == 0)
error = git_odb_stream_finalize_write(oid, stream);
error = git_odb_stream_finalize_write(id, stream);
git_odb_stream_free(stream);
return error;
}
static int write_file_stream(
git_oid *oid, git_odb *odb, const char *path, git_off_t file_size)
git_oid *id, git_odb *odb, const char *path, git_off_t file_size)
{
int fd, error;
char buffer[4096];
@ -97,14 +100,14 @@ static int write_file_stream(
}
if (!error)
error = git_odb_stream_finalize_write(oid, stream);
error = git_odb_stream_finalize_write(id, stream);
git_odb_stream_free(stream);
return error;
}
static int write_file_filtered(
git_oid *oid,
git_oid *id,
git_off_t *size,
git_odb *odb,
const char *full_path,
@ -119,7 +122,7 @@ static int write_file_filtered(
if (!error) {
*size = tgt.size;
error = git_odb_write(oid, odb, tgt.ptr, tgt.size, GIT_OBJ_BLOB);
error = git_odb_write(id, odb, tgt.ptr, tgt.size, GIT_OBJ_BLOB);
}
git_buf_free(&tgt);
@ -127,7 +130,7 @@ static int write_file_filtered(
}
static int write_symlink(
git_oid *oid, git_odb *odb, const char *path, size_t link_size)
git_oid *id, git_odb *odb, const char *path, size_t link_size)
{
char *link_data;
ssize_t read_len;
@ -143,13 +146,13 @@ static int write_symlink(
return -1;
}
error = git_odb_write(oid, odb, (void *)link_data, link_size, GIT_OBJ_BLOB);
error = git_odb_write(id, odb, (void *)link_data, link_size, GIT_OBJ_BLOB);
git__free(link_data);
return error;
}
int git_blob__create_from_paths(
git_oid *oid,
git_oid *id,
struct stat *out_st,
git_repository *repo,
const char *content_path,
@ -188,24 +191,25 @@ int git_blob__create_from_paths(
mode = hint_mode ? hint_mode : st.st_mode;
if (S_ISLNK(mode)) {
error = write_symlink(oid, odb, content_path, (size_t)size);
error = write_symlink(id, odb, content_path, (size_t)size);
} else {
git_filter_list *fl = NULL;
if (try_load_filters)
/* Load the filters for writing this file to the ODB */
error = git_filter_list_load(
&fl, repo, NULL, hint_path, GIT_FILTER_TO_ODB);
&fl, repo, NULL, hint_path,
GIT_FILTER_TO_ODB, GIT_FILTER_OPT_DEFAULT);
if (error < 0)
/* well, that didn't work */;
else if (fl == NULL)
/* No filters need to be applied to the document: we can stream
* directly from disk */
error = write_file_stream(oid, odb, content_path, size);
error = write_file_stream(id, odb, content_path, size);
else {
/* We need to apply one or more filters */
error = write_file_filtered(oid, &size, odb, content_path, fl);
error = write_file_filtered(id, &size, odb, content_path, fl);
git_filter_list_free(fl);
}
@ -233,13 +237,13 @@ done:
}
int git_blob_create_fromworkdir(
git_oid *oid, git_repository *repo, const char *path)
git_oid *id, git_repository *repo, const char *path)
{
return git_blob__create_from_paths(oid, NULL, repo, NULL, path, 0, true);
return git_blob__create_from_paths(id, NULL, repo, NULL, path, 0, true);
}
int git_blob_create_fromdisk(
git_oid *oid, git_repository *repo, const char *path)
git_oid *id, git_repository *repo, const char *path)
{
int error;
git_buf full_path = GIT_BUF_INIT;
@ -257,7 +261,7 @@ int git_blob_create_fromdisk(
hintpath += strlen(workdir);
error = git_blob__create_from_paths(
oid, NULL, repo, git_buf_cstr(&full_path), hintpath, 0, true);
id, NULL, repo, git_buf_cstr(&full_path), hintpath, 0, true);
git_buf_free(&full_path);
return error;
@ -266,63 +270,72 @@ int git_blob_create_fromdisk(
#define BUFFER_SIZE 4096
int git_blob_create_fromchunks(
git_oid *oid,
git_oid *id,
git_repository *repo,
const char *hintpath,
int (*source_cb)(char *content, size_t max_length, void *payload),
void *payload)
{
int error = -1, read_bytes;
int error;
char *content = NULL;
git_filebuf file = GIT_FILEBUF_INIT;
git_buf path = GIT_BUF_INIT;
if (git_buf_joinpath(
&path, git_repository_path(repo), GIT_OBJECTS_DIR "streamed") < 0)
assert(id && repo && source_cb);
if ((error = git_buf_joinpath(
&path, git_repository_path(repo), GIT_OBJECTS_DIR "streamed")) < 0)
goto cleanup;
content = git__malloc(BUFFER_SIZE);
GITERR_CHECK_ALLOC(content);
if (git_filebuf_open(&file, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY, 0666) < 0)
if ((error = git_filebuf_open(
&file, git_buf_cstr(&path), GIT_FILEBUF_TEMPORARY, 0666)) < 0)
goto cleanup;
while (1) {
read_bytes = source_cb(content, BUFFER_SIZE, payload);
int read_bytes = source_cb(content, BUFFER_SIZE, payload);
assert(read_bytes <= BUFFER_SIZE);
if (read_bytes <= 0)
if (!read_bytes)
break;
if (git_filebuf_write(&file, content, read_bytes) < 0)
if (read_bytes > BUFFER_SIZE) {
giterr_set(GITERR_OBJECT, "Invalid chunk size while creating blob");
error = GIT_EBUFS;
} else if (read_bytes < 0) {
error = giterr_set_after_callback(read_bytes);
} else {
error = git_filebuf_write(&file, content, read_bytes);
}
if (error < 0)
goto cleanup;
}
if (read_bytes < 0)
goto cleanup;
if (git_filebuf_flush(&file) < 0)
if ((error = git_filebuf_flush(&file)) < 0)
goto cleanup;
error = git_blob__create_from_paths(
oid, NULL, repo, file.path_lock, hintpath, 0, hintpath != NULL);
id, NULL, repo, file.path_lock, hintpath, 0, hintpath != NULL);
cleanup:
git_buf_free(&path);
git_filebuf_cleanup(&file);
git__free(content);
return error;
}
int git_blob_is_binary(git_blob *blob)
int git_blob_is_binary(const git_blob *blob)
{
git_buf content;
assert(blob);
content.ptr = blob->odb_object->buffer;
content.size = min(blob->odb_object->cached.size, 4000);
content.size =
min(blob->odb_object->cached.size, GIT_FILTER_BYTES_TO_CHECK_NUL);
content.asize = 0;
return git_buf_text_is_binary(&content);
@ -339,11 +352,14 @@ int git_blob_filtered_content(
assert(blob && path && out);
git_buf_sanitize(out);
if (check_for_binary_data && git_blob_is_binary(blob))
return 0;
if (!(error = git_filter_list_load(
&fl, git_blob_owner(blob), blob, path, GIT_FILTER_TO_WORKTREE))) {
&fl, git_blob_owner(blob), blob, path,
GIT_FILTER_TO_WORKTREE, GIT_FILTER_OPT_DEFAULT))) {
error = git_filter_list_apply_to_blob(out, fl, blob);

View File

@ -21,27 +21,22 @@ static int retrieve_branch_reference(
const char *branch_name,
int is_remote)
{
git_reference *branch;
int error = -1;
git_reference *branch = NULL;
int error = 0;
char *prefix;
git_buf ref_name = GIT_BUF_INIT;
*branch_reference_out = NULL;
prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR;
if (git_buf_joinpath(&ref_name, prefix, branch_name) < 0)
goto cleanup;
if ((error = git_buf_joinpath(&ref_name, prefix, branch_name)) < 0)
/* OOM */;
else if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0)
giterr_set(
GITERR_REFERENCE, "Cannot locate %s branch '%s'",
is_remote ? "remote-tracking" : "local", branch_name);
if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0) {
giterr_set(GITERR_REFERENCE,
"Cannot locate %s branch '%s'.", is_remote ? "remote-tracking" : "local", branch_name);
goto cleanup;
}
*branch_reference_out = branch; /* will be NULL on error */
*branch_reference_out = branch;
cleanup:
git_buf_free(&ref_name);
return error;
}
@ -59,26 +54,53 @@ int git_branch_create(
git_repository *repository,
const char *branch_name,
const git_commit *commit,
int force)
int force,
const git_signature *signature,
const char *log_message)
{
int is_head = 0;
git_reference *branch = NULL;
git_buf canonical_branch_name = GIT_BUF_INIT;
git_buf canonical_branch_name = GIT_BUF_INIT,
log_message_buf = GIT_BUF_INIT;
int error = -1;
assert(branch_name && commit && ref_out);
assert(git_object_owner((const git_object *)commit) == repository);
if (force && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) {
error = git_branch_is_head(branch);
git_reference_free(branch);
branch = NULL;
if (error < 0)
goto cleanup;
is_head = error;
}
if (is_head && force) {
giterr_set(GITERR_REFERENCE, "Cannot force update branch '%s' as it is "
"the current HEAD of the repository.", branch_name);
error = -1;
goto cleanup;
}
if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)
goto cleanup;
if (git_buf_sets(&log_message_buf, log_message ? log_message : "Branch: created") < 0)
goto cleanup;
error = git_reference_create(&branch, repository,
git_buf_cstr(&canonical_branch_name), git_commit_id(commit), force);
git_buf_cstr(&canonical_branch_name), git_commit_id(commit), force, signature,
git_buf_cstr(&log_message_buf));
if (!error)
*ref_out = branch;
cleanup:
git_buf_free(&canonical_branch_name);
git_buf_free(&log_message_buf);
return error;
}
@ -90,33 +112,35 @@ int git_branch_delete(git_reference *branch)
assert(branch);
if (!git_reference_is_branch(branch) &&
!git_reference_is_remote(branch)) {
giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.", git_reference_name(branch));
return -1;
if (!git_reference_is_branch(branch) && !git_reference_is_remote(branch)) {
giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.",
git_reference_name(branch));
return GIT_ENOTFOUND;
}
if ((is_head = git_branch_is_head(branch)) < 0)
return is_head;
if (is_head) {
giterr_set(GITERR_REFERENCE,
"Cannot delete branch '%s' as it is the current HEAD of the repository.", git_reference_name(branch));
giterr_set(GITERR_REFERENCE, "Cannot delete branch '%s' as it is "
"the current HEAD of the repository.", git_reference_name(branch));
return -1;
}
if (git_buf_printf(&config_section, "branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0)
if (git_buf_join(&config_section, '.', "branch",
git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0)
goto on_error;
if (git_config_rename_section(
git_reference_owner(branch),
git_buf_cstr(&config_section),
NULL) < 0)
goto on_error;
git_reference_owner(branch), git_buf_cstr(&config_section), NULL) < 0)
goto on_error;
if (git_reference_delete(branch) < 0)
goto on_error;
if (git_reflog_delete(git_reference_owner(branch), git_reference_name(branch)) < 0)
goto on_error;
error = 0;
on_error:
@ -182,6 +206,9 @@ void git_branch_iterator_free(git_branch_iterator *_iter)
{
branch_iter *iter = (branch_iter *) _iter;
if (iter == NULL)
return;
git_reference_iterator_free(iter->iter);
git__free(iter);
}
@ -190,11 +217,14 @@ int git_branch_move(
git_reference **out,
git_reference *branch,
const char *new_branch_name,
int force)
int force,
const git_signature *signature,
const char *log_message)
{
git_buf new_reference_name = GIT_BUF_INIT,
old_config_section = GIT_BUF_INIT,
new_config_section = GIT_BUF_INIT;
old_config_section = GIT_BUF_INIT,
new_config_section = GIT_BUF_INIT,
log_message_buf = GIT_BUF_INIT;
int error;
assert(branch && new_branch_name);
@ -202,26 +232,40 @@ int git_branch_move(
if (!git_reference_is_branch(branch))
return not_a_local_branch(git_reference_name(branch));
error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name);
if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)
goto done;
if (log_message) {
if ((error = git_buf_sets(&log_message_buf, log_message)) < 0)
goto done;
} else {
if ((error = git_buf_printf(&log_message_buf, "Branch: renamed %s to %s",
git_reference_name(branch), git_buf_cstr(&new_reference_name))) < 0)
goto done;
}
/* first update ref then config so failure won't trash config */
error = git_reference_rename(
out, branch, git_buf_cstr(&new_reference_name), force,
signature, git_buf_cstr(&log_message_buf));
if (error < 0)
goto done;
git_buf_printf(&old_config_section,
"branch.%s", git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR));
git_buf_join(&old_config_section, '.', "branch",
git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR));
git_buf_join(&new_config_section, '.', "branch", new_branch_name);
git_buf_printf(&new_config_section, "branch.%s", new_branch_name);
if ((error = git_config_rename_section(git_reference_owner(branch),
error = git_config_rename_section(
git_reference_owner(branch),
git_buf_cstr(&old_config_section),
git_buf_cstr(&new_config_section))) < 0)
goto done;
error = git_reference_rename(out, branch, git_buf_cstr(&new_reference_name), force);
git_buf_cstr(&new_config_section));
done:
git_buf_free(&new_reference_name);
git_buf_free(&old_config_section);
git_buf_free(&new_config_section);
git_buf_free(&log_message_buf);
return error;
}
@ -237,7 +281,9 @@ int git_branch_lookup(
return retrieve_branch_reference(ref_out, repo, branch_name, branch_type == GIT_BRANCH_REMOTE);
}
int git_branch_name(const char **out, git_reference *ref)
int git_branch_name(
const char **out,
const git_reference *ref)
{
const char *branch_name;
@ -260,17 +306,13 @@ int git_branch_name(const char **out, git_reference *ref)
static int retrieve_upstream_configuration(
const char **out,
git_repository *repo,
const git_config *config,
const char *canonical_branch_name,
const char *format)
{
git_config *config;
git_buf buf = GIT_BUF_INIT;
int error;
if (git_repository_config__weakptr(&config, repo) < 0)
return -1;
if (git_buf_printf(&buf, format,
canonical_branch_name + strlen(GIT_REFS_HEADS_DIR)) < 0)
return -1;
@ -280,33 +322,39 @@ static int retrieve_upstream_configuration(
return error;
}
int git_branch_upstream__name(
git_buf *tracking_name,
int git_branch_upstream_name(
git_buf *out,
git_repository *repo,
const char *canonical_branch_name)
const char *refname)
{
const char *remote_name, *merge_name;
git_buf buf = GIT_BUF_INIT;
int error = -1;
git_remote *remote = NULL;
const git_refspec *refspec;
git_config *config;
assert(tracking_name && canonical_branch_name);
assert(out && refname);
if (!git_reference__is_branch(canonical_branch_name))
return not_a_local_branch(canonical_branch_name);
git_buf_sanitize(out);
if (!git_reference__is_branch(refname))
return not_a_local_branch(refname);
if ((error = git_repository_config_snapshot(&config, repo)) < 0)
return error;
if ((error = retrieve_upstream_configuration(
&remote_name, repo, canonical_branch_name, "branch.%s.remote")) < 0)
&remote_name, config, refname, "branch.%s.remote")) < 0)
goto cleanup;
if ((error = retrieve_upstream_configuration(
&merge_name, repo, canonical_branch_name, "branch.%s.merge")) < 0)
&merge_name, config, refname, "branch.%s.merge")) < 0)
goto cleanup;
if (!*remote_name || !*merge_name) {
giterr_set(GITERR_REFERENCE,
"branch '%s' does not have an upstream", canonical_branch_name);
"branch '%s' does not have an upstream", refname);
error = GIT_ENOTFOUND;
goto cleanup;
}
@ -321,21 +369,22 @@ int git_branch_upstream__name(
goto cleanup;
}
if (git_refspec_transform_r(&buf, refspec, merge_name) < 0)
if (git_refspec_transform(&buf, refspec, merge_name) < 0)
goto cleanup;
} else
if (git_buf_sets(&buf, merge_name) < 0)
goto cleanup;
error = git_buf_set(tracking_name, git_buf_cstr(&buf), git_buf_len(&buf));
error = git_buf_set(out, git_buf_cstr(&buf), git_buf_len(&buf));
cleanup:
git_config_free(config);
git_remote_free(remote);
git_buf_free(&buf);
return error;
}
static int remote_name(git_buf *buf, git_repository *repo, const char *canonical_branch_name)
int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refname)
{
git_strarray remote_list = {0};
size_t i;
@ -344,12 +393,14 @@ static int remote_name(git_buf *buf, git_repository *repo, const char *canonical
int error = 0;
char *remote_name = NULL;
assert(buf && repo && canonical_branch_name);
assert(buf && repo && refname);
git_buf_sanitize(buf);
/* Verify that this is a remote branch */
if (!git_reference__is_remote(canonical_branch_name)) {
if (!git_reference__is_remote(refname)) {
giterr_set(GITERR_INVALID, "Reference '%s' is not a remote branch.",
canonical_branch_name);
refname);
error = GIT_ERROR;
goto cleanup;
}
@ -363,7 +414,7 @@ static int remote_name(git_buf *buf, git_repository *repo, const char *canonical
if ((error = git_remote_load(&remote, repo, remote_list.strings[i])) < 0)
continue;
fetchspec = git_remote__matching_dst_refspec(remote, canonical_branch_name);
fetchspec = git_remote__matching_dst_refspec(remote, refname);
if (fetchspec) {
/* If we have not already set out yet, then set
* it to the matching remote name. Otherwise
@ -375,7 +426,7 @@ static int remote_name(git_buf *buf, git_repository *repo, const char *canonical
git_remote_free(remote);
giterr_set(GITERR_REFERENCE,
"Reference '%s' is ambiguous", canonical_branch_name);
"Reference '%s' is ambiguous", refname);
error = GIT_EAMBIGUOUS;
goto cleanup;
}
@ -389,76 +440,26 @@ static int remote_name(git_buf *buf, git_repository *repo, const char *canonical
error = git_buf_puts(buf, remote_name);
} else {
giterr_set(GITERR_REFERENCE,
"Could not determine remote for '%s'", canonical_branch_name);
"Could not determine remote for '%s'", refname);
error = GIT_ENOTFOUND;
}
cleanup:
if (error < 0)
git_buf_free(buf);
git_strarray_free(&remote_list);
return error;
}
int git_branch_remote_name(char *buffer, size_t buffer_len, git_repository *repo, const char *refname)
{
int ret;
git_buf buf = GIT_BUF_INIT;
if ((ret = remote_name(&buf, repo, refname)) < 0)
return ret;
if (buffer)
git_buf_copy_cstr(buffer, buffer_len, &buf);
ret = (int)git_buf_len(&buf) + 1;
git_buf_free(&buf);
return ret;
}
int git_branch_upstream_name(
char *tracking_branch_name_out,
size_t buffer_size,
git_repository *repo,
const char *canonical_branch_name)
{
git_buf buf = GIT_BUF_INIT;
int error;
assert(canonical_branch_name);
if (tracking_branch_name_out && buffer_size)
*tracking_branch_name_out = '\0';
if ((error = git_branch_upstream__name(
&buf, repo, canonical_branch_name)) < 0)
goto cleanup;
if (tracking_branch_name_out && buf.size + 1 > buffer_size) { /* +1 for NUL byte */
giterr_set(
GITERR_INVALID,
"Buffer too short to hold the tracked reference name.");
error = -1;
goto cleanup;
}
if (tracking_branch_name_out)
git_buf_copy_cstr(tracking_branch_name_out, buffer_size, &buf);
error = (int)buf.size + 1;
cleanup:
git_buf_free(&buf);
return (int)error;
}
int git_branch_upstream(
git_reference **tracking_out,
git_reference *branch)
git_reference **tracking_out,
const git_reference *branch)
{
int error;
git_buf tracking_name = GIT_BUF_INIT;
if ((error = git_branch_upstream__name(&tracking_name,
if ((error = git_branch_upstream_name(&tracking_name,
git_reference_owner(branch), git_reference_name(branch))) < 0)
return error;
@ -541,7 +542,7 @@ int git_branch_set_upstream(git_reference *branch, const char *upstream_name)
if (local)
git_buf_puts(&value, ".");
else
remote_name(&value, repo, git_reference_name(upstream));
git_branch_remote_name(&value, repo, git_reference_name(upstream));
if (git_buf_printf(&key, "branch.%s.remote", shortname) < 0)
goto on_error;
@ -560,7 +561,7 @@ int git_branch_set_upstream(git_reference *branch, const char *upstream_name)
fetchspec = git_remote__matching_dst_refspec(remote, git_reference_name(upstream));
git_buf_clear(&value);
if (!fetchspec || git_refspec_transform_l(&value, fetchspec, git_reference_name(upstream)) < 0)
if (!fetchspec || git_refspec_rtransform(&value, fetchspec, git_reference_name(upstream)) < 0)
goto on_error;
git_remote_free(remote);
@ -590,7 +591,7 @@ on_error:
}
int git_branch_is_head(
git_reference *branch)
const git_reference *branch)
{
git_reference *head;
bool is_same = false;

View File

@ -7,7 +7,6 @@
#include "buffer.h"
#include "posix.h"
#include "git2/buffer.h"
#include <stdarg.h>
#include <ctype.h>
/* Used as default value for git_buf->ptr so that people can always
@ -66,8 +65,10 @@ int git_buf_try_grow(
new_ptr = git__realloc(new_ptr, new_size);
if (!new_ptr) {
if (mark_oom)
if (mark_oom) {
if (buf->ptr) git__free(buf->ptr);
buf->ptr = git_buf__oom;
}
return -1;
}
@ -100,12 +101,23 @@ void git_buf_free(git_buf *buf)
git_buf_init(buf, 0);
}
void git_buf_sanitize(git_buf *buf)
{
if (buf->ptr == NULL) {
assert(buf->size == 0 && buf->asize == 0);
buf->ptr = git_buf__initbuf;
} else if (buf->asize > buf->size)
buf->ptr[buf->size] = '\0';
}
void git_buf_clear(git_buf *buf)
{
buf->size = 0;
if (!buf->ptr)
if (!buf->ptr) {
buf->ptr = git_buf__initbuf;
buf->asize = 0;
}
if (buf->asize > 0)
buf->ptr[0] = '\0';
@ -120,8 +132,11 @@ int git_buf_set(git_buf *buf, const void *data, size_t len)
ENSURE_SIZE(buf, len + 1);
memmove(buf->ptr, data, len);
}
buf->size = len;
buf->ptr[buf->size] = '\0';
if (buf->asize > buf->size)
buf->ptr[buf->size] = '\0';
}
return 0;
}
@ -139,6 +154,15 @@ int git_buf_putc(git_buf *buf, char c)
return 0;
}
int git_buf_putcn(git_buf *buf, char c, size_t len)
{
ENSURE_SIZE(buf, buf->size + len + 1);
memset(buf->ptr + buf->size, c, len);
buf->size += len;
buf->ptr[buf->size] = '\0';
return 0;
}
int git_buf_put(git_buf *buf, const char *data, size_t len)
{
ENSURE_SIZE(buf, buf->size + len + 1);
@ -194,6 +218,42 @@ int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
return 0;
}
static const char b85str[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
int git_buf_put_base85(git_buf *buf, const char *data, size_t len)
{
ENSURE_SIZE(buf, buf->size + (5 * ((len / 4) + !!(len % 4))) + 1);
while (len) {
uint32_t acc = 0;
char b85[5];
int i;
for (i = 24; i >= 0; i -= 8) {
uint8_t ch = *data++;
acc |= ch << i;
if (--len == 0)
break;
}
for (i = 4; i >= 0; i--) {
int val = acc % 85;
acc /= 85;
b85[i] = b85str[val];
}
for (i = 0; i < 5; i++)
buf->ptr[buf->size++] = b85[i];
}
buf->ptr[buf->size] = '\0';
return 0;
}
int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
{
int len;
@ -272,19 +332,20 @@ void git_buf_consume(git_buf *buf, const char *end)
void git_buf_truncate(git_buf *buf, size_t len)
{
if (len < buf->size) {
buf->size = len;
if (len >= buf->size)
return;
buf->size = len;
if (buf->size < buf->asize)
buf->ptr[buf->size] = '\0';
}
}
void git_buf_shorten(git_buf *buf, size_t amount)
{
if (amount > buf->size)
amount = buf->size;
buf->size = buf->size - amount;
buf->ptr[buf->size] = '\0';
if (buf->size > amount)
git_buf_truncate(buf, buf->size - amount);
else
git_buf_clear(buf);
}
void git_buf_rtruncate_at_char(git_buf *buf, char separator)
@ -424,7 +485,7 @@ int git_buf_join(
ssize_t offset_a = -1;
/* not safe to have str_b point internally to the buffer */
assert(str_b < buf->ptr || str_b > buf->ptr + buf->size);
assert(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
/* figure out if we need to insert a separator */
if (separator && strlen_a) {
@ -439,13 +500,14 @@ int git_buf_join(
if (git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1) < 0)
return -1;
assert(buf->ptr);
/* fix up internal pointers */
if (offset_a >= 0)
str_a = buf->ptr + offset_a;
/* do the actual copying */
if (offset_a != 0)
if (offset_a != 0 && str_a)
memmove(buf->ptr, str_a, strlen_a);
if (need_sep)
buf->ptr[strlen_a] = separator;
@ -457,6 +519,59 @@ int git_buf_join(
return 0;
}
int git_buf_join3(
git_buf *buf,
char separator,
const char *str_a,
const char *str_b,
const char *str_c)
{
size_t len_a = strlen(str_a), len_b = strlen(str_b), len_c = strlen(str_c);
int sep_a = 0, sep_b = 0;
char *tgt;
/* for this function, disallow pointers into the existing buffer */
assert(str_a < buf->ptr || str_a >= buf->ptr + buf->size);
assert(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
assert(str_c < buf->ptr || str_c >= buf->ptr + buf->size);
if (separator) {
if (len_a > 0) {
while (*str_b == separator) { str_b++; len_b--; }
sep_a = (str_a[len_a - 1] != separator);
}
if (len_a > 0 || len_b > 0)
while (*str_c == separator) { str_c++; len_c--; }
if (len_b > 0)
sep_b = (str_b[len_b - 1] != separator);
}
if (git_buf_grow(buf, len_a + sep_a + len_b + sep_b + len_c + 1) < 0)
return -1;
tgt = buf->ptr;
if (len_a) {
memcpy(tgt, str_a, len_a);
tgt += len_a;
}
if (sep_a)
*tgt++ = separator;
if (len_b) {
memcpy(tgt, str_b, len_b);
tgt += len_b;
}
if (sep_b)
*tgt++ = separator;
if (len_c)
memcpy(tgt, str_c, len_c);
buf->size = len_a + sep_a + len_b + sep_b + len_c;
buf->ptr[buf->size] = '\0';
return 0;
}
void git_buf_rtrim(git_buf *buf)
{
while (buf->size > 0) {
@ -466,7 +581,8 @@ void git_buf_rtrim(git_buf *buf)
buf->size--;
}
buf->ptr[buf->size] = '\0';
if (buf->asize > buf->size)
buf->ptr[buf->size] = '\0';
}
int git_buf_cmp(const git_buf *a, const git_buf *b)
@ -490,8 +606,7 @@ int git_buf_splice(
/* Ported from git.git
* https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
*/
if (git_buf_grow(buf, git_buf_len(buf) + nb_to_insert - nb_to_remove) < 0)
return -1;
ENSURE_SIZE(buf, buf->size + nb_to_insert - nb_to_insert + 1);
memmove(buf->ptr + where + nb_to_insert,
buf->ptr + where + nb_to_remove,

View File

@ -10,7 +10,6 @@
#include "common.h"
#include "git2/strarray.h"
#include "git2/buffer.h"
#include <stdarg.h>
/* typedef struct {
* char *ptr;
@ -50,6 +49,15 @@ extern void git_buf_init(git_buf *buf, size_t initial_size);
extern int git_buf_try_grow(
git_buf *buf, size_t target_size, bool mark_oom, bool preserve_external);
/**
* Sanitizes git_buf structures provided from user input. Users of the
* library, when providing git_buf's, may wish to provide a NULL ptr for
* ease of handling. The buffer routines, however, expect a non-NULL ptr
* always. This helper method simply handles NULL input, converting to a
* git_buf__initbuf.
*/
extern void git_buf_sanitize(git_buf *buf);
extern void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
extern char *git_buf_detach(git_buf *buf);
extern void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
@ -80,6 +88,7 @@ GIT_INLINE(bool) git_buf_oom(const git_buf *buf)
*/
int git_buf_sets(git_buf *buf, const char *string);
int git_buf_putc(git_buf *buf, char c);
int git_buf_putcn(git_buf *buf, char c, size_t len);
int git_buf_put(git_buf *buf, const char *data, size_t len);
int git_buf_puts(git_buf *buf, const char *string);
int git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
@ -90,8 +99,12 @@ void git_buf_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);
/** General join with separator */
int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
/** Fast join of two strings - first may legally point into `buf` data */
int git_buf_join(git_buf *buf, char separator, const char *str_a, const char *str_b);
/** Fast join of three strings - cannot reference `buf` data */
int git_buf_join3(git_buf *buf, char separator, const char *str_a, const char *str_b, const char *str_c);
/**
* Join two strings as paths, inserting a slash between as needed.
@ -145,6 +158,9 @@ int git_buf_cmp(const git_buf *a, const git_buf *b);
/* Write data as base64 encoded in buffer */
int git_buf_put_base64(git_buf *buf, const char *data, size_t len);
/* Write data as "base85" encoded in buffer */
int git_buf_put_base85(git_buf *buf, const char *data, size_t len);
/*
* Insert, remove or replace a portion of the buffer.
*

Some files were not shown because too many files have changed in this diff Show More