mirror of
https://git.proxmox.com/git/libgit2
synced 2025-05-17 22:59:32 +00:00
Merge branch 'development'
Conflicts: .travis.yml
This commit is contained in:
commit
5b9fac39d8
@ -22,7 +22,7 @@ script:
|
||||
|
||||
# Run Tests
|
||||
after_script:
|
||||
- ctest .
|
||||
- ctest -V .
|
||||
|
||||
# Only watch the development branch
|
||||
branches:
|
||||
|
2
AUTHORS
2
AUTHORS
@ -36,7 +36,6 @@ Marc Pegon
|
||||
Marcel Groothuis
|
||||
Marco Villegas
|
||||
Olivier Ramonat
|
||||
Peter Drahos
|
||||
Peter Drahoš
|
||||
Pierre Habouzit
|
||||
Przemyslaw Pawelczyk
|
||||
@ -44,6 +43,7 @@ Ramsay Jones
|
||||
Robert G. Jakabosky
|
||||
Romain Geissler
|
||||
Romain Muller
|
||||
Russell Belfer
|
||||
Sakari Jokinen
|
||||
Sam
|
||||
Sarath Lakshman
|
||||
|
@ -28,6 +28,10 @@ FILE(GLOB SRC_HTTP deps/http-parser/*.c)
|
||||
|
||||
IF (NOT WIN32)
|
||||
FIND_PACKAGE(ZLIB)
|
||||
ELSE()
|
||||
# Windows doesn't understand POSIX regex on its own
|
||||
INCLUDE_DIRECTORIES(deps/regex)
|
||||
SET(SRC_REGEX deps/regex/regex.c)
|
||||
ENDIF()
|
||||
|
||||
IF (ZLIB_FOUND)
|
||||
@ -47,29 +51,34 @@ SET(INSTALL_INC include CACHE PATH "Where to install headers to.")
|
||||
# Build options
|
||||
OPTION (BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" ON)
|
||||
OPTION (THREADSAFE "Build libgit2 as threadsafe" OFF)
|
||||
OPTION (BUILD_TESTS "Build Tests" ON)
|
||||
OPTION (BUILD_CLAR "Build Tests using the Clar suite" OFF)
|
||||
OPTION (BUILD_CLAR "Build Tests using the Clar suite" ON)
|
||||
OPTION (TAGS "Generate tags" OFF)
|
||||
OPTION (PROFILE "Generate profiling information" OFF)
|
||||
|
||||
# Platform specific compilation flags
|
||||
IF (MSVC)
|
||||
# Not using __stdcall with the CRT causes problems
|
||||
OPTION (STDCALL "Buildl libgit2 with the __stdcall convention" ON)
|
||||
|
||||
SET(CMAKE_C_FLAGS "/W4 /nologo /Zi ${CMAKE_C_FLAGS}")
|
||||
SET(CMAKE_C_FLAGS "/W4 /MP /nologo /Zi ${CMAKE_C_FLAGS}")
|
||||
IF (STDCALL)
|
||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Gz")
|
||||
ENDIF ()
|
||||
# TODO: bring back /RTC1 /RTCc
|
||||
SET(CMAKE_C_FLAGS_DEBUG "/Od /DEBUG /MTd")
|
||||
SET(CMAKE_C_FLAGS_DEBUG "/Od /DEBUG /MTd /RTC1 /RTCs /RTCu")
|
||||
SET(CMAKE_C_FLAGS_RELEASE "/MT /O2")
|
||||
SET(WIN_RC "src/win32/git2.rc")
|
||||
|
||||
# Precompiled headers
|
||||
ELSE ()
|
||||
SET(CMAKE_C_FLAGS "-O2 -g -D_GNU_SOURCE -Wall -Wextra -Wno-missing-field-initializers -Wstrict-aliasing=2 -Wstrict-prototypes -Wmissing-prototypes ${CMAKE_C_FLAGS}")
|
||||
SET(CMAKE_C_FLAGS "-O2 -g -D_GNU_SOURCE -fvisibility=hidden -Wall -Wextra -Wno-missing-field-initializers -Wstrict-aliasing=2 -Wstrict-prototypes -Wmissing-prototypes ${CMAKE_C_FLAGS}")
|
||||
SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
|
||||
IF (NOT MINGW) # MinGW always does PIC and complains if we tell it to
|
||||
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
|
||||
ENDIF ()
|
||||
IF (PROFILE)
|
||||
SET(CMAKE_C_FLAGS "-pg ${CMAKE_C_FLAGS}")
|
||||
SET(CMAKE_EXE_LINKER_FLAGS "-pg ${CMAKE_EXE_LINKER_FLAGS}")
|
||||
ENDIF ()
|
||||
ENDIF()
|
||||
|
||||
# Build Debug by default
|
||||
@ -92,14 +101,16 @@ FILE(GLOB SRC_H include/git2/*.h)
|
||||
|
||||
# On Windows use specific platform sources
|
||||
IF (WIN32 AND NOT CYGWIN)
|
||||
ADD_DEFINITIONS(-DWIN32 -D_DEBUG)
|
||||
FILE(GLOB SRC src/*.c src/transports/*.c src/win32/*.c)
|
||||
ADD_DEFINITIONS(-DWIN32 -D_DEBUG -D_WIN32_WINNT=0x0501)
|
||||
FILE(GLOB SRC src/*.c src/transports/*.c src/xdiff/*.c src/win32/*.c src/compat/*.c)
|
||||
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
|
||||
FILE(GLOB SRC src/*.c src/transports/*.c src/xdiff/*.c src/unix/*.c src/compat/*.c)
|
||||
ELSE()
|
||||
FILE(GLOB SRC src/*.c src/transports/*.c src/unix/*.c)
|
||||
FILE(GLOB SRC src/*.c src/transports/*.c src/xdiff/*.c src/unix/*.c)
|
||||
ENDIF ()
|
||||
|
||||
# Compile and link libgit2
|
||||
ADD_LIBRARY(git2 ${SRC} ${SRC_ZLIB} ${SRC_HTTP} ${WIN_RC})
|
||||
ADD_LIBRARY(git2 ${SRC} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX} ${WIN_RC})
|
||||
|
||||
IF (WIN32)
|
||||
TARGET_LINK_LIBRARIES(git2 ws2_32)
|
||||
@ -123,31 +134,15 @@ INSTALL(DIRECTORY include/git2 DESTINATION ${INSTALL_INC} )
|
||||
INSTALL(FILES include/git2.h DESTINATION ${INSTALL_INC} )
|
||||
|
||||
# Tests
|
||||
IF (BUILD_TESTS)
|
||||
SET(TEST_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/tests/resources" CACHE PATH "Path to test resources.")
|
||||
ADD_DEFINITIONS(-DTEST_RESOURCES=\"${TEST_RESOURCES}\")
|
||||
|
||||
INCLUDE_DIRECTORIES(tests)
|
||||
FILE(GLOB SRC_TEST tests/t??-*.c)
|
||||
|
||||
ADD_EXECUTABLE(libgit2_test tests/test_main.c tests/test_lib.c tests/test_helpers.c ${SRC} ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP})
|
||||
TARGET_LINK_LIBRARIES(libgit2_test ${CMAKE_THREAD_LIBS_INIT})
|
||||
IF (WIN32)
|
||||
TARGET_LINK_LIBRARIES(libgit2_test ws2_32)
|
||||
ELSEIF (CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
|
||||
TARGET_LINK_LIBRARIES(libgit2_test socket nsl)
|
||||
ENDIF ()
|
||||
|
||||
ENABLE_TESTING()
|
||||
ADD_TEST(libgit2_test libgit2_test)
|
||||
ENDIF ()
|
||||
|
||||
IF (BUILD_CLAR)
|
||||
|
||||
FIND_PACKAGE(PythonInterp REQUIRED)
|
||||
|
||||
SET(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/tests/resources/")
|
||||
SET(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/tests-clar/resources/")
|
||||
SET(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tests-clar")
|
||||
SET(CLAR_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/tests-clar/resources" CACHE PATH "Path to test resources.")
|
||||
ADD_DEFINITIONS(-DCLAR_FIXTURE_PATH=\"${CLAR_FIXTURES}\")
|
||||
ADD_DEFINITIONS(-DCLAR_RESOURCES=\"${TEST_RESOURCES}\")
|
||||
|
||||
INCLUDE_DIRECTORIES(${CLAR_PATH})
|
||||
FILE(GLOB_RECURSE SRC_TEST ${CLAR_PATH}/*/*.c ${CLAR_PATH}/clar_helpers.c ${CLAR_PATH}/testlib.c)
|
||||
@ -158,7 +153,7 @@ IF (BUILD_CLAR)
|
||||
DEPENDS ${CLAR_PATH}/clar ${SRC_TEST}
|
||||
WORKING_DIRECTORY ${CLAR_PATH}
|
||||
)
|
||||
ADD_EXECUTABLE(libgit2_clar ${SRC} ${CLAR_PATH}/clar_main.c ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP})
|
||||
ADD_EXECUTABLE(libgit2_clar ${SRC} ${CLAR_PATH}/clar_main.c ${SRC_TEST} ${SRC_ZLIB} ${SRC_HTTP} ${SRC_REGEX})
|
||||
TARGET_LINK_LIBRARIES(libgit2_clar ${CMAKE_THREAD_LIBS_INIT})
|
||||
IF (WIN32)
|
||||
TARGET_LINK_LIBRARIES(libgit2_clar ws2_32)
|
||||
|
507
COPYING
507
COPYING
@ -1,4 +1,4 @@
|
||||
libgit2 is Copyright (C) 2009-2011 the libgit2 contributors,
|
||||
libgit2 is Copyright (C) 2009-2012 the libgit2 contributors,
|
||||
unless otherwise stated. See the AUTHORS file for details.
|
||||
|
||||
Note that the only valid version of the GPL as far as this project
|
||||
@ -422,4 +422,509 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
----------------------------------------------------------------------
|
||||
|
||||
The regex library (deps/regex/) is licensed under the GNU LGPL
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
||||
|
@ -6,7 +6,7 @@ LIBNAME=libgit2.a
|
||||
|
||||
INCLUDES= -I. -Isrc -Iinclude -Ideps/http-parser -Ideps/zlib
|
||||
|
||||
DEFINES= $(INCLUDES) -DNO_VIZ -DSTDC -DNO_GZIP -D_FILE_OFFSET_BITS=64
|
||||
DEFINES= $(INCLUDES) -DNO_VIZ -DSTDC -DNO_GZIP -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
|
||||
CFLAGS= -g $(DEFINES) -Wall -Wextra -fPIC -O2
|
||||
|
||||
SRCS = $(wildcard src/*.c) $(wildcard src/transports/*.c) $(wildcard src/unix/*.c) $(wildcard deps/http-parser/*.c) $(wildcard deps/zlib/*.c)
|
||||
|
63
README.md
63
README.md
@ -1,6 +1,8 @@
|
||||
libgit2 - the Git linkable library
|
||||
======================
|
||||
|
||||
[](http://travis-ci.org/libgit2/libgit2)
|
||||
|
||||
libgit2 is a portable, pure C implementation of the Git core methods provided as a
|
||||
re-entrant linkable library with a solid API, allowing you to write native
|
||||
speed custom Git applications in any language with bindings.
|
||||
@ -10,6 +12,7 @@ This basically means that you can link it (unmodified) with any kind of software
|
||||
release its source code.
|
||||
|
||||
* Mailing list: <libgit2@librelist.org>
|
||||
* Archives: <http://librelist.com/browser/libgit2/>
|
||||
* Website: <http://libgit2.github.com>
|
||||
* API documentation: <http://libgit2.github.com/libgit2>
|
||||
* Usage guide: <http://libgit2.github.com/api.html>
|
||||
@ -67,7 +70,7 @@ The following CMake variables are declared:
|
||||
- `INSTALL_LIB`: Where to install libraries to.
|
||||
- `INSTALL_INC`: Where to install headers to.
|
||||
- `BUILD_SHARED_LIBS`: Build libgit2 as a Shared Library (defaults to ON)
|
||||
- `BUILD_TESTS`: Build the libgit2 test suite (defaults to ON)
|
||||
- `BUILD_CLAR`: Build [Clar](https://github.com/tanoku/clar)-based test suite (defaults to ON)
|
||||
- `THREADSAFE`: Build libgit2 with threading support (defaults to OFF)
|
||||
|
||||
Language Bindings
|
||||
@ -75,29 +78,49 @@ Language Bindings
|
||||
|
||||
Here are the bindings to libgit2 that are currently available:
|
||||
|
||||
* Rugged (Ruby bindings) <https://github.com/libgit2/rugged>
|
||||
* objective-git (Objective-C bindings) <https://github.com/libgit2/objective-git>
|
||||
* pygit2 (Python bindings) <https://github.com/libgit2/pygit2>
|
||||
* libgit2sharp (.NET bindings) <https://github.com/libgit2/libgit2sharp>
|
||||
* php-git (PHP bindings) <https://github.com/libgit2/php-git>
|
||||
* luagit2 (Lua bindings) <https://github.com/libgit2/luagit2>
|
||||
* GitForDelphi (Delphi bindings) <https://github.com/libgit2/GitForDelphi>
|
||||
* node-gitteh (Node.js bindings) <https://github.com/libgit2/node-gitteh>
|
||||
* nodegit (Node.js bindings) <https://github.com/tbranyen/nodegit>
|
||||
* go-git (Go bindings) <https://github.com/str1ngs/go-git>
|
||||
* libqgit2 (C++ Qt bindings) <https://projects.kde.org/projects/playground/libs/libqgit2/>
|
||||
* libgit2-ocaml (ocaml bindings) <https://github.com/burdges/libgit2-ocaml>
|
||||
* Geef (Erlang bindings) <https://github.com/schacon/geef>
|
||||
* libgit2net (.NET bindings, low level) <https://github.com/txdv/libgit2net>
|
||||
* parrot-libgit2 (Parrot Virtual Machine bindings) <https://github.com/letolabs/parrot-libgit2>
|
||||
* hgit2 (Haskell bindings) <https://github.com/norm2782/hgit2>
|
||||
* git-xs-pm (Perl bindings) <https://github.com/ingydotnet/git-xs-pm>
|
||||
* libgit2.vapi (Vala bindings) <https://github.com/apmasell/vapis/blob/master/libgit2.vapi>
|
||||
* C++
|
||||
* libqgit2, Qt bindings <https://projects.kde.org/projects/playground/libs/libqgit2/>
|
||||
* Chicken Scheme
|
||||
* chicken-git <https://wiki.call-cc.org/egg/git>
|
||||
* Delphi
|
||||
* GitForDelphi <https://github.com/libgit2/GitForDelphi>
|
||||
* Erlang
|
||||
* Geef <https://github.com/schacon/geef>
|
||||
* Go
|
||||
* go-git <https://github.com/str1ngs/go-git>
|
||||
* GObject
|
||||
* libgit2-glib <https://github.com/nacho/libgit2-glib>
|
||||
* Haskell
|
||||
* hgit2 <https://github.com/norm2782/hgit2>
|
||||
* Lua
|
||||
* luagit2 <https://github.com/libgit2/luagit2>
|
||||
* .NET
|
||||
* libgit2net, low level bindings <https://github.com/txdv/libgit2net>
|
||||
* libgit2sharp <https://github.com/libgit2/libgit2sharp>
|
||||
* Node.js
|
||||
* node-gitteh <https://github.com/libgit2/node-gitteh>
|
||||
* nodegit <https://github.com/tbranyen/nodegit>
|
||||
* Objective-C
|
||||
* objective-git <https://github.com/libgit2/objective-git>
|
||||
* OCaml
|
||||
* libgit2-ocaml <https://github.com/burdges/libgit2-ocaml>
|
||||
* Parrot Virtual Machine
|
||||
* parrot-libgit2 <https://github.com/letolabs/parrot-libgit2>
|
||||
* Perl
|
||||
* git-xs-pm <https://github.com/ingydotnet/git-xs-pm>
|
||||
* PHP
|
||||
* php-git <https://github.com/libgit2/php-git>
|
||||
* Python
|
||||
* pygit2 <https://github.com/libgit2/pygit2>
|
||||
* Ruby
|
||||
* Rugged <https://github.com/libgit2/rugged>
|
||||
* Vala
|
||||
* libgit2.vapi <https://github.com/apmasell/vapis/blob/master/libgit2.vapi>
|
||||
|
||||
If you start another language binding to libgit2, please let us know so
|
||||
we can add it to the list.
|
||||
|
||||
How Can I Contribute
|
||||
How Can I Contribute?
|
||||
==================================
|
||||
|
||||
Fork libgit2/libgit2 on GitHub, add your improvement, push it to a branch
|
||||
|
7
deps/regex/config.h
vendored
Normal file
7
deps/regex/config.h
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
#ifndef _REGEX_CONFIG_H_
|
||||
#define _REGEX_CONFIG_H_
|
||||
|
||||
# define GAWK
|
||||
# define NO_MBSUPPORT
|
||||
|
||||
#endif
|
3856
deps/regex/regcomp.c
vendored
Normal file
3856
deps/regex/regcomp.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
85
deps/regex/regex.c
vendored
Normal file
85
deps/regex/regex.c
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
/* Extended regular expression matching and search library.
|
||||
Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA. */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
/* Make sure noone compiles this code with a C++ compiler. */
|
||||
#ifdef __cplusplus
|
||||
# error "This is C code, use a C compiler"
|
||||
#endif
|
||||
|
||||
#ifdef _LIBC
|
||||
/* We have to keep the namespace clean. */
|
||||
# define regfree(preg) __regfree (preg)
|
||||
# define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef)
|
||||
# define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags)
|
||||
# define regerror(errcode, preg, errbuf, errbuf_size) \
|
||||
__regerror(errcode, preg, errbuf, errbuf_size)
|
||||
# define re_set_registers(bu, re, nu, st, en) \
|
||||
__re_set_registers (bu, re, nu, st, en)
|
||||
# define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \
|
||||
__re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
|
||||
# define re_match(bufp, string, size, pos, regs) \
|
||||
__re_match (bufp, string, size, pos, regs)
|
||||
# define re_search(bufp, string, size, startpos, range, regs) \
|
||||
__re_search (bufp, string, size, startpos, range, regs)
|
||||
# define re_compile_pattern(pattern, length, bufp) \
|
||||
__re_compile_pattern (pattern, length, bufp)
|
||||
# define re_set_syntax(syntax) __re_set_syntax (syntax)
|
||||
# define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \
|
||||
__re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop)
|
||||
# define re_compile_fastmap(bufp) __re_compile_fastmap (bufp)
|
||||
|
||||
# include "../locale/localeinfo.h"
|
||||
#endif
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
#include <stdio.h> /* for size_t */
|
||||
#endif
|
||||
|
||||
/* On some systems, limits.h sets RE_DUP_MAX to a lower value than
|
||||
GNU regex allows. Include it before <regex.h>, which correctly
|
||||
#undefs RE_DUP_MAX and sets it to the right value. */
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef GAWK
|
||||
#undef alloca
|
||||
#define alloca alloca_is_bad_you_should_never_use_it
|
||||
#endif
|
||||
#include <regex.h>
|
||||
#include "regex_internal.h"
|
||||
|
||||
#include "regex_internal.c"
|
||||
#ifdef GAWK
|
||||
#define bool int
|
||||
#define true (1)
|
||||
#define false (0)
|
||||
#endif
|
||||
#include "regcomp.c"
|
||||
#include "regexec.c"
|
||||
|
||||
/* Binary backward compatibility. */
|
||||
#if _LIBC
|
||||
# include <shlib-compat.h>
|
||||
# if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3)
|
||||
link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.")
|
||||
int re_max_failures = 2000;
|
||||
# endif
|
||||
#endif
|
582
deps/regex/regex.h
vendored
Normal file
582
deps/regex/regex.h
vendored
Normal file
@ -0,0 +1,582 @@
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* Definitions for data structures and routines for the regular
|
||||
expression library.
|
||||
Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006,2008
|
||||
Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, write to the Free
|
||||
Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
02110-1301 USA. */
|
||||
|
||||
#ifndef _REGEX_H
|
||||
#define _REGEX_H 1
|
||||
|
||||
#ifdef HAVE_STDDEF_H
|
||||
#include <stddef.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#ifndef _LIBC
|
||||
#define __USE_GNU 1
|
||||
#endif
|
||||
|
||||
/* Allow the use in C++ code. */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* The following two types have to be signed and unsigned integer type
|
||||
wide enough to hold a value of a pointer. For most ANSI compilers
|
||||
ptrdiff_t and size_t should be likely OK. Still size of these two
|
||||
types is 2 for Microsoft C. Ugh... */
|
||||
typedef long int s_reg_t;
|
||||
typedef unsigned long int active_reg_t;
|
||||
|
||||
/* The following bits are used to determine the regexp syntax we
|
||||
recognize. The set/not-set meanings are chosen so that Emacs syntax
|
||||
remains the value 0. The bits are given in alphabetical order, and
|
||||
the definitions shifted by one from the previous bit; thus, when we
|
||||
add or remove a bit, only one other definition need change. */
|
||||
typedef unsigned long int reg_syntax_t;
|
||||
|
||||
#ifdef __USE_GNU
|
||||
/* If this bit is not set, then \ inside a bracket expression is literal.
|
||||
If set, then such a \ quotes the following character. */
|
||||
# define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1)
|
||||
|
||||
/* If this bit is not set, then + and ? are operators, and \+ and \? are
|
||||
literals.
|
||||
If set, then \+ and \? are operators and + and ? are literals. */
|
||||
# define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1)
|
||||
|
||||
/* If this bit is set, then character classes are supported. They are:
|
||||
[:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:],
|
||||
[:space:], [:print:], [:punct:], [:graph:], and [:cntrl:].
|
||||
If not set, then character classes are not supported. */
|
||||
# define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1)
|
||||
|
||||
/* If this bit is set, then ^ and $ are always anchors (outside bracket
|
||||
expressions, of course).
|
||||
If this bit is not set, then it depends:
|
||||
^ is an anchor if it is at the beginning of a regular
|
||||
expression or after an open-group or an alternation operator;
|
||||
$ is an anchor if it is at the end of a regular expression, or
|
||||
before a close-group or an alternation operator.
|
||||
|
||||
This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because
|
||||
POSIX draft 11.2 says that * etc. in leading positions is undefined.
|
||||
We already implemented a previous draft which made those constructs
|
||||
invalid, though, so we haven't changed the code back. */
|
||||
# define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1)
|
||||
|
||||
/* If this bit is set, then special characters are always special
|
||||
regardless of where they are in the pattern.
|
||||
If this bit is not set, then special characters are special only in
|
||||
some contexts; otherwise they are ordinary. Specifically,
|
||||
* + ? and intervals are only special when not after the beginning,
|
||||
open-group, or alternation operator. */
|
||||
# define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1)
|
||||
|
||||
/* If this bit is set, then *, +, ?, and { cannot be first in an re or
|
||||
immediately after an alternation or begin-group operator. */
|
||||
# define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1)
|
||||
|
||||
/* If this bit is set, then . matches newline.
|
||||
If not set, then it doesn't. */
|
||||
# define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1)
|
||||
|
||||
/* If this bit is set, then . doesn't match NUL.
|
||||
If not set, then it does. */
|
||||
# define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1)
|
||||
|
||||
/* If this bit is set, nonmatching lists [^...] do not match newline.
|
||||
If not set, they do. */
|
||||
# define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1)
|
||||
|
||||
/* If this bit is set, either \{...\} or {...} defines an
|
||||
interval, depending on RE_NO_BK_BRACES.
|
||||
If not set, \{, \}, {, and } are literals. */
|
||||
# define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1)
|
||||
|
||||
/* If this bit is set, +, ? and | aren't recognized as operators.
|
||||
If not set, they are. */
|
||||
# define RE_LIMITED_OPS (RE_INTERVALS << 1)
|
||||
|
||||
/* If this bit is set, newline is an alternation operator.
|
||||
If not set, newline is literal. */
|
||||
# define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1)
|
||||
|
||||
/* If this bit is set, then `{...}' defines an interval, and \{ and \}
|
||||
are literals.
|
||||
If not set, then `\{...\}' defines an interval. */
|
||||
# define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1)
|
||||
|
||||
/* If this bit is set, (...) defines a group, and \( and \) are literals.
|
||||
If not set, \(...\) defines a group, and ( and ) are literals. */
|
||||
# define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1)
|
||||
|
||||
/* If this bit is set, then \<digit> matches <digit>.
|
||||
If not set, then \<digit> is a back-reference. */
|
||||
# define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1)
|
||||
|
||||
/* If this bit is set, then | is an alternation operator, and \| is literal.
|
||||
If not set, then \| is an alternation operator, and | is literal. */
|
||||
# define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1)
|
||||
|
||||
/* If this bit is set, then an ending range point collating higher
|
||||
than the starting range point, as in [z-a], is invalid.
|
||||
If not set, then when ending range point collates higher than the
|
||||
starting range point, the range is ignored. */
|
||||
# define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1)
|
||||
|
||||
/* If this bit is set, then an unmatched ) is ordinary.
|
||||
If not set, then an unmatched ) is invalid. */
|
||||
# define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1)
|
||||
|
||||
/* If this bit is set, succeed as soon as we match the whole pattern,
|
||||
without further backtracking. */
|
||||
# define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1)
|
||||
|
||||
/* If this bit is set, do not process the GNU regex operators.
|
||||
If not set, then the GNU regex operators are recognized. */
|
||||
# define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1)
|
||||
|
||||
/* If this bit is set, a syntactically invalid interval is treated as
|
||||
a string of ordinary characters. For example, the ERE 'a{1' is
|
||||
treated as 'a\{1'. */
|
||||
# define RE_INVALID_INTERVAL_ORD (RE_NO_GNU_OPS << 1)
|
||||
|
||||
/* If this bit is set, then ignore case when matching.
|
||||
If not set, then case is significant. */
|
||||
# define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1)
|
||||
|
||||
/* This bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only
|
||||
for ^, because it is difficult to scan the regex backwards to find
|
||||
whether ^ should be special. */
|
||||
# define RE_CARET_ANCHORS_HERE (RE_ICASE << 1)
|
||||
|
||||
/* If this bit is set, then \{ cannot be first in an bre or
|
||||
immediately after an alternation or begin-group operator. */
|
||||
# define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1)
|
||||
|
||||
/* If this bit is set, then no_sub will be set to 1 during
|
||||
re_compile_pattern. */
|
||||
#define RE_NO_SUB (RE_CONTEXT_INVALID_DUP << 1)
|
||||
#endif
|
||||
|
||||
/* This global variable defines the particular regexp syntax to use (for
|
||||
some interfaces). When a regexp is compiled, the syntax used is
|
||||
stored in the pattern buffer, so changing this does not affect
|
||||
already-compiled regexps. */
|
||||
extern reg_syntax_t re_syntax_options;
|
||||
|
||||
#ifdef __USE_GNU
|
||||
/* Define combinations of the above bits for the standard possibilities.
|
||||
(The [[[ comments delimit what gets put into the Texinfo file, so
|
||||
don't delete them!) */
|
||||
/* [[[begin syntaxes]]] */
|
||||
#define RE_SYNTAX_EMACS 0
|
||||
|
||||
#define RE_SYNTAX_AWK \
|
||||
(RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \
|
||||
| RE_NO_BK_PARENS | RE_NO_BK_REFS \
|
||||
| RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \
|
||||
| RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS \
|
||||
| RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS)
|
||||
|
||||
#define RE_SYNTAX_GNU_AWK \
|
||||
((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \
|
||||
| RE_INVALID_INTERVAL_ORD) \
|
||||
& ~(RE_DOT_NOT_NULL | RE_CONTEXT_INDEP_OPS \
|
||||
| RE_CONTEXT_INVALID_OPS ))
|
||||
|
||||
#define RE_SYNTAX_POSIX_AWK \
|
||||
(RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \
|
||||
| RE_INTERVALS | RE_NO_GNU_OPS \
|
||||
| RE_INVALID_INTERVAL_ORD)
|
||||
|
||||
#define RE_SYNTAX_GREP \
|
||||
(RE_BK_PLUS_QM | RE_CHAR_CLASSES \
|
||||
| RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \
|
||||
| RE_NEWLINE_ALT)
|
||||
|
||||
#define RE_SYNTAX_EGREP \
|
||||
(RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \
|
||||
| RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \
|
||||
| RE_NEWLINE_ALT | RE_NO_BK_PARENS \
|
||||
| RE_NO_BK_VBAR)
|
||||
|
||||
#define RE_SYNTAX_POSIX_EGREP \
|
||||
(RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES \
|
||||
| RE_INVALID_INTERVAL_ORD)
|
||||
|
||||
/* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */
|
||||
#define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC
|
||||
|
||||
#define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC
|
||||
|
||||
/* Syntax bits common to both basic and extended POSIX regex syntax. */
|
||||
#define _RE_SYNTAX_POSIX_COMMON \
|
||||
(RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \
|
||||
| RE_INTERVALS | RE_NO_EMPTY_RANGES)
|
||||
|
||||
#define RE_SYNTAX_POSIX_BASIC \
|
||||
(_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP)
|
||||
|
||||
/* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes
|
||||
RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this
|
||||
isn't minimal, since other operators, such as \`, aren't disabled. */
|
||||
#define RE_SYNTAX_POSIX_MINIMAL_BASIC \
|
||||
(_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS)
|
||||
|
||||
#define RE_SYNTAX_POSIX_EXTENDED \
|
||||
(_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
|
||||
| RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \
|
||||
| RE_NO_BK_PARENS | RE_NO_BK_VBAR \
|
||||
| RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD)
|
||||
|
||||
/* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is
|
||||
removed and RE_NO_BK_REFS is added. */
|
||||
#define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \
|
||||
(_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \
|
||||
| RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \
|
||||
| RE_NO_BK_PARENS | RE_NO_BK_REFS \
|
||||
| RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD)
|
||||
/* [[[end syntaxes]]] */
|
||||
|
||||
/* Maximum number of duplicates an interval can allow. Some systems
|
||||
(erroneously) define this in other header files, but we want our
|
||||
value, so remove any previous define. */
|
||||
# ifdef RE_DUP_MAX
|
||||
# undef RE_DUP_MAX
|
||||
# endif
|
||||
/* If sizeof(int) == 2, then ((1 << 15) - 1) overflows. */
|
||||
# define RE_DUP_MAX (0x7fff)
|
||||
#endif
|
||||
|
||||
|
||||
/* POSIX `cflags' bits (i.e., information for `regcomp'). */
|
||||
|
||||
/* If this bit is set, then use extended regular expression syntax.
|
||||
If not set, then use basic regular expression syntax. */
|
||||
#define REG_EXTENDED 1
|
||||
|
||||
/* If this bit is set, then ignore case when matching.
|
||||
If not set, then case is significant. */
|
||||
#define REG_ICASE (REG_EXTENDED << 1)
|
||||
|
||||
/* If this bit is set, then anchors do not match at newline
|
||||
characters in the string.
|
||||
If not set, then anchors do match at newlines. */
|
||||
#define REG_NEWLINE (REG_ICASE << 1)
|
||||
|
||||
/* If this bit is set, then report only success or fail in regexec.
|
||||
If not set, then returns differ between not matching and errors. */
|
||||
#define REG_NOSUB (REG_NEWLINE << 1)
|
||||
|
||||
|
||||
/* POSIX `eflags' bits (i.e., information for regexec). */
|
||||
|
||||
/* If this bit is set, then the beginning-of-line operator doesn't match
|
||||
the beginning of the string (presumably because it's not the
|
||||
beginning of a line).
|
||||
If not set, then the beginning-of-line operator does match the
|
||||
beginning of the string. */
|
||||
#define REG_NOTBOL 1
|
||||
|
||||
/* Like REG_NOTBOL, except for the end-of-line. */
|
||||
#define REG_NOTEOL (1 << 1)
|
||||
|
||||
/* Use PMATCH[0] to delimit the start and end of the search in the
|
||||
buffer. */
|
||||
#define REG_STARTEND (1 << 2)
|
||||
|
||||
|
||||
/* If any error codes are removed, changed, or added, update the
|
||||
`re_error_msg' table in regex.c. */
|
||||
typedef enum
|
||||
{
|
||||
#if defined _XOPEN_SOURCE || defined __USE_XOPEN2K
|
||||
REG_ENOSYS = -1, /* This will never happen for this implementation. */
|
||||
#endif
|
||||
|
||||
REG_NOERROR = 0, /* Success. */
|
||||
REG_NOMATCH, /* Didn't find a match (for regexec). */
|
||||
|
||||
/* POSIX regcomp return error codes. (In the order listed in the
|
||||
standard.) */
|
||||
REG_BADPAT, /* Invalid pattern. */
|
||||
REG_ECOLLATE, /* Inalid collating element. */
|
||||
REG_ECTYPE, /* Invalid character class name. */
|
||||
REG_EESCAPE, /* Trailing backslash. */
|
||||
REG_ESUBREG, /* Invalid back reference. */
|
||||
REG_EBRACK, /* Unmatched left bracket. */
|
||||
REG_EPAREN, /* Parenthesis imbalance. */
|
||||
REG_EBRACE, /* Unmatched \{. */
|
||||
REG_BADBR, /* Invalid contents of \{\}. */
|
||||
REG_ERANGE, /* Invalid range end. */
|
||||
REG_ESPACE, /* Ran out of memory. */
|
||||
REG_BADRPT, /* No preceding re for repetition op. */
|
||||
|
||||
/* Error codes we've added. */
|
||||
REG_EEND, /* Premature end. */
|
||||
REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */
|
||||
REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */
|
||||
} reg_errcode_t;
|
||||
|
||||
/* This data structure represents a compiled pattern. Before calling
|
||||
the pattern compiler, the fields `buffer', `allocated', `fastmap',
|
||||
`translate', and `no_sub' can be set. After the pattern has been
|
||||
compiled, the `re_nsub' field is available. All other fields are
|
||||
private to the regex routines. */
|
||||
|
||||
#ifndef RE_TRANSLATE_TYPE
|
||||
# define __RE_TRANSLATE_TYPE unsigned char *
|
||||
# ifdef __USE_GNU
|
||||
# define RE_TRANSLATE_TYPE __RE_TRANSLATE_TYPE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __USE_GNU
|
||||
# define __REPB_PREFIX(name) name
|
||||
#else
|
||||
# define __REPB_PREFIX(name) __##name
|
||||
#endif
|
||||
|
||||
struct re_pattern_buffer
|
||||
{
|
||||
/* Space that holds the compiled pattern. It is declared as
|
||||
`unsigned char *' because its elements are sometimes used as
|
||||
array indexes. */
|
||||
unsigned char *__REPB_PREFIX(buffer);
|
||||
|
||||
/* Number of bytes to which `buffer' points. */
|
||||
unsigned long int __REPB_PREFIX(allocated);
|
||||
|
||||
/* Number of bytes actually used in `buffer'. */
|
||||
unsigned long int __REPB_PREFIX(used);
|
||||
|
||||
/* Syntax setting with which the pattern was compiled. */
|
||||
reg_syntax_t __REPB_PREFIX(syntax);
|
||||
|
||||
/* Pointer to a fastmap, if any, otherwise zero. re_search uses the
|
||||
fastmap, if there is one, to skip over impossible starting points
|
||||
for matches. */
|
||||
char *__REPB_PREFIX(fastmap);
|
||||
|
||||
/* Either a translate table to apply to all characters before
|
||||
comparing them, or zero for no translation. The translation is
|
||||
applied to a pattern when it is compiled and to a string when it
|
||||
is matched. */
|
||||
__RE_TRANSLATE_TYPE __REPB_PREFIX(translate);
|
||||
|
||||
/* Number of subexpressions found by the compiler. */
|
||||
size_t re_nsub;
|
||||
|
||||
/* Zero if this pattern cannot match the empty string, one else.
|
||||
Well, in truth it's used only in `re_search_2', to see whether or
|
||||
not we should use the fastmap, so we don't set this absolutely
|
||||
perfectly; see `re_compile_fastmap' (the `duplicate' case). */
|
||||
unsigned __REPB_PREFIX(can_be_null) : 1;
|
||||
|
||||
/* If REGS_UNALLOCATED, allocate space in the `regs' structure
|
||||
for `max (RE_NREGS, re_nsub + 1)' groups.
|
||||
If REGS_REALLOCATE, reallocate space if necessary.
|
||||
If REGS_FIXED, use what's there. */
|
||||
#ifdef __USE_GNU
|
||||
# define REGS_UNALLOCATED 0
|
||||
# define REGS_REALLOCATE 1
|
||||
# define REGS_FIXED 2
|
||||
#endif
|
||||
unsigned __REPB_PREFIX(regs_allocated) : 2;
|
||||
|
||||
/* Set to zero when `regex_compile' compiles a pattern; set to one
|
||||
by `re_compile_fastmap' if it updates the fastmap. */
|
||||
unsigned __REPB_PREFIX(fastmap_accurate) : 1;
|
||||
|
||||
/* If set, `re_match_2' does not return information about
|
||||
subexpressions. */
|
||||
unsigned __REPB_PREFIX(no_sub) : 1;
|
||||
|
||||
/* If set, a beginning-of-line anchor doesn't match at the beginning
|
||||
of the string. */
|
||||
unsigned __REPB_PREFIX(not_bol) : 1;
|
||||
|
||||
/* Similarly for an end-of-line anchor. */
|
||||
unsigned __REPB_PREFIX(not_eol) : 1;
|
||||
|
||||
/* If true, an anchor at a newline matches. */
|
||||
unsigned __REPB_PREFIX(newline_anchor) : 1;
|
||||
};
|
||||
|
||||
typedef struct re_pattern_buffer regex_t;
|
||||
|
||||
/* Type for byte offsets within the string. POSIX mandates this. */
|
||||
typedef int regoff_t;
|
||||
|
||||
|
||||
#ifdef __USE_GNU
|
||||
/* This is the structure we store register match data in. See
|
||||
regex.texinfo for a full description of what registers match. */
|
||||
struct re_registers
|
||||
{
|
||||
unsigned num_regs;
|
||||
regoff_t *start;
|
||||
regoff_t *end;
|
||||
};
|
||||
|
||||
|
||||
/* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer,
|
||||
`re_match_2' returns information about at least this many registers
|
||||
the first time a `regs' structure is passed. */
|
||||
# ifndef RE_NREGS
|
||||
# define RE_NREGS 30
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
/* POSIX specification for registers. Aside from the different names than
|
||||
`re_registers', POSIX uses an array of structures, instead of a
|
||||
structure of arrays. */
|
||||
typedef struct
|
||||
{
|
||||
regoff_t rm_so; /* Byte offset from string's start to substring's start. */
|
||||
regoff_t rm_eo; /* Byte offset from string's start to substring's end. */
|
||||
} regmatch_t;
|
||||
|
||||
/* Declarations for routines. */
|
||||
|
||||
#ifdef __USE_GNU
|
||||
/* Sets the current default syntax to SYNTAX, and return the old syntax.
|
||||
You can also simply assign to the `re_syntax_options' variable. */
|
||||
extern reg_syntax_t re_set_syntax (reg_syntax_t __syntax);
|
||||
|
||||
/* Compile the regular expression PATTERN, with length LENGTH
|
||||
and syntax given by the global `re_syntax_options', into the buffer
|
||||
BUFFER. Return NULL if successful, and an error string if not. */
|
||||
extern const char *re_compile_pattern (const char *__pattern, size_t __length,
|
||||
struct re_pattern_buffer *__buffer);
|
||||
|
||||
|
||||
/* Compile a fastmap for the compiled pattern in BUFFER; used to
|
||||
accelerate searches. Return 0 if successful and -2 if was an
|
||||
internal error. */
|
||||
extern int re_compile_fastmap (struct re_pattern_buffer *__buffer);
|
||||
|
||||
|
||||
/* Search in the string STRING (with length LENGTH) for the pattern
|
||||
compiled into BUFFER. Start searching at position START, for RANGE
|
||||
characters. Return the starting position of the match, -1 for no
|
||||
match, or -2 for an internal error. Also return register
|
||||
information in REGS (if REGS and BUFFER->no_sub are nonzero). */
|
||||
extern int re_search (struct re_pattern_buffer *__buffer, const char *__cstring,
|
||||
int __length, int __start, int __range,
|
||||
struct re_registers *__regs);
|
||||
|
||||
|
||||
/* Like `re_search', but search in the concatenation of STRING1 and
|
||||
STRING2. Also, stop searching at index START + STOP. */
|
||||
extern int re_search_2 (struct re_pattern_buffer *__buffer,
|
||||
const char *__string1, int __length1,
|
||||
const char *__string2, int __length2, int __start,
|
||||
int __range, struct re_registers *__regs, int __stop);
|
||||
|
||||
|
||||
/* Like `re_search', but return how many characters in STRING the regexp
|
||||
in BUFFER matched, starting at position START. */
|
||||
extern int re_match (struct re_pattern_buffer *__buffer, const char *__cstring,
|
||||
int __length, int __start, struct re_registers *__regs);
|
||||
|
||||
|
||||
/* Relates to `re_match' as `re_search_2' relates to `re_search'. */
|
||||
extern int re_match_2 (struct re_pattern_buffer *__buffer,
|
||||
const char *__string1, int __length1,
|
||||
const char *__string2, int __length2, int __start,
|
||||
struct re_registers *__regs, int __stop);
|
||||
|
||||
|
||||
/* Set REGS to hold NUM_REGS registers, storing them in STARTS and
|
||||
ENDS. Subsequent matches using BUFFER and REGS will use this memory
|
||||
for recording register information. STARTS and ENDS must be
|
||||
allocated with malloc, and must each be at least `NUM_REGS * sizeof
|
||||
(regoff_t)' bytes long.
|
||||
|
||||
If NUM_REGS == 0, then subsequent matches should allocate their own
|
||||
register data.
|
||||
|
||||
Unless this function is called, the first search or match using
|
||||
PATTERN_BUFFER will allocate its own register data, without
|
||||
freeing the old data. */
|
||||
extern void re_set_registers (struct re_pattern_buffer *__buffer,
|
||||
struct re_registers *__regs,
|
||||
unsigned int __num_regs,
|
||||
regoff_t *__starts, regoff_t *__ends);
|
||||
#endif /* Use GNU */
|
||||
|
||||
#if defined _REGEX_RE_COMP || (defined _LIBC && defined __USE_BSD)
|
||||
# ifndef _CRAY
|
||||
/* 4.2 bsd compatibility. */
|
||||
extern char *re_comp (const char *);
|
||||
extern int re_exec (const char *);
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* GCC 2.95 and later have "__restrict"; C99 compilers have
|
||||
"restrict", and "configure" may have defined "restrict". */
|
||||
#ifndef __restrict
|
||||
# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__))
|
||||
# if defined restrict || 199901L <= __STDC_VERSION__
|
||||
# define __restrict restrict
|
||||
# else
|
||||
# define __restrict
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
/* gcc 3.1 and up support the [restrict] syntax. */
|
||||
#ifndef __restrict_arr
|
||||
# if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) \
|
||||
&& !defined __GNUG__
|
||||
# define __restrict_arr __restrict
|
||||
# else
|
||||
# define __restrict_arr
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* POSIX compatibility. */
|
||||
extern int regcomp (regex_t *__restrict __preg,
|
||||
const char *__restrict __pattern,
|
||||
int __cflags);
|
||||
|
||||
extern int regexec (const regex_t *__restrict __preg,
|
||||
const char *__restrict __cstring, size_t __nmatch,
|
||||
regmatch_t __pmatch[__restrict_arr],
|
||||
int __eflags);
|
||||
|
||||
extern size_t regerror (int __errcode, const regex_t *__restrict __preg,
|
||||
char *__restrict __errbuf, size_t __errbuf_size);
|
||||
|
||||
extern void regfree (regex_t *__preg);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* C++ */
|
||||
|
||||
#endif /* regex.h */
|
1744
deps/regex/regex_internal.c
vendored
Normal file
1744
deps/regex/regex_internal.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
810
deps/regex/regex_internal.h
vendored
Normal file
810
deps/regex/regex_internal.h
vendored
Normal file
@ -0,0 +1,810 @@
|
||||
/* Extended regular expression matching and search library.
|
||||
Copyright (C) 2002-2005, 2007, 2008, 2010 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA. */
|
||||
|
||||
#ifndef _REGEX_INTERNAL_H
|
||||
#define _REGEX_INTERNAL_H 1
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined HAVE_LANGINFO_H || defined HAVE_LANGINFO_CODESET || defined _LIBC
|
||||
# include <langinfo.h>
|
||||
#endif
|
||||
#if defined HAVE_LOCALE_H || defined _LIBC
|
||||
# include <locale.h>
|
||||
#endif
|
||||
#if defined HAVE_WCHAR_H || defined _LIBC
|
||||
# include <wchar.h>
|
||||
#endif /* HAVE_WCHAR_H || _LIBC */
|
||||
#if defined HAVE_WCTYPE_H || defined _LIBC
|
||||
# include <wctype.h>
|
||||
#endif /* HAVE_WCTYPE_H || _LIBC */
|
||||
#if defined HAVE_STDBOOL_H || defined _LIBC
|
||||
# include <stdbool.h>
|
||||
#endif /* HAVE_STDBOOL_H || _LIBC */
|
||||
#if !defined(ZOS_USS)
|
||||
#if defined HAVE_STDINT_H || defined _LIBC
|
||||
# include <stdint.h>
|
||||
#endif /* HAVE_STDINT_H || _LIBC */
|
||||
#endif /* !ZOS_USS */
|
||||
#if defined _LIBC
|
||||
# include <bits/libc-lock.h>
|
||||
#else
|
||||
# define __libc_lock_define(CLASS,NAME)
|
||||
# define __libc_lock_init(NAME) do { } while (0)
|
||||
# define __libc_lock_lock(NAME) do { } while (0)
|
||||
# define __libc_lock_unlock(NAME) do { } while (0)
|
||||
#endif
|
||||
|
||||
#ifndef GAWK
|
||||
/* In case that the system doesn't have isblank(). */
|
||||
#if !defined _LIBC && !defined HAVE_ISBLANK && !defined isblank
|
||||
# define isblank(ch) ((ch) == ' ' || (ch) == '\t')
|
||||
#endif
|
||||
#else /* GAWK */
|
||||
/*
|
||||
* This is a freaking mess. On glibc systems you have to define
|
||||
* a magic constant to get isblank() out of <ctype.h>, since it's
|
||||
* a C99 function. To heck with all that and borrow a page from
|
||||
* dfa.c's book.
|
||||
*/
|
||||
|
||||
static int
|
||||
is_blank (int c)
|
||||
{
|
||||
return (c == ' ' || c == '\t');
|
||||
}
|
||||
#endif /* GAWK */
|
||||
|
||||
#ifdef _LIBC
|
||||
# ifndef _RE_DEFINE_LOCALE_FUNCTIONS
|
||||
# define _RE_DEFINE_LOCALE_FUNCTIONS 1
|
||||
# include <locale/localeinfo.h>
|
||||
# include <locale/elem-hash.h>
|
||||
# include <locale/coll-lookup.h>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* This is for other GNU distributions with internationalized messages. */
|
||||
#if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC
|
||||
# include <libintl.h>
|
||||
# ifdef _LIBC
|
||||
# undef gettext
|
||||
# define gettext(msgid) \
|
||||
INTUSE(__dcgettext) (_libc_intl_domainname, msgid, LC_MESSAGES)
|
||||
# endif
|
||||
#else
|
||||
# define gettext(msgid) (msgid)
|
||||
#endif
|
||||
|
||||
#ifndef gettext_noop
|
||||
/* This define is so xgettext can find the internationalizable
|
||||
strings. */
|
||||
# define gettext_noop(String) String
|
||||
#endif
|
||||
|
||||
/* For loser systems without the definition. */
|
||||
#ifndef SIZE_MAX
|
||||
# define SIZE_MAX ((size_t) -1)
|
||||
#endif
|
||||
|
||||
#ifndef NO_MBSUPPORT
|
||||
#include "mbsupport.h" /* gawk */
|
||||
#endif
|
||||
#ifndef MB_CUR_MAX
|
||||
#define MB_CUR_MAX 1
|
||||
#endif
|
||||
|
||||
#if (defined MBS_SUPPORT) || _LIBC
|
||||
# define RE_ENABLE_I18N
|
||||
#endif
|
||||
|
||||
#if __GNUC__ >= 3
|
||||
# define BE(expr, val) __builtin_expect (expr, val)
|
||||
#else
|
||||
# define BE(expr, val) (expr)
|
||||
# ifdef inline
|
||||
# undef inline
|
||||
# endif
|
||||
# define inline
|
||||
#endif
|
||||
|
||||
/* Number of single byte character. */
|
||||
#define SBC_MAX 256
|
||||
|
||||
#define COLL_ELEM_LEN_MAX 8
|
||||
|
||||
/* The character which represents newline. */
|
||||
#define NEWLINE_CHAR '\n'
|
||||
#define WIDE_NEWLINE_CHAR L'\n'
|
||||
|
||||
/* Rename to standard API for using out of glibc. */
|
||||
#ifndef _LIBC
|
||||
# ifdef __wctype
|
||||
# undef __wctype
|
||||
# endif
|
||||
# define __wctype wctype
|
||||
# ifdef __iswctype
|
||||
# undef __iswctype
|
||||
# endif
|
||||
# define __iswctype iswctype
|
||||
# define __btowc btowc
|
||||
# define __mbrtowc mbrtowc
|
||||
#undef __mempcpy /* GAWK */
|
||||
# define __mempcpy mempcpy
|
||||
# define __wcrtomb wcrtomb
|
||||
# define __regfree regfree
|
||||
# define attribute_hidden
|
||||
#endif /* not _LIBC */
|
||||
|
||||
#ifdef __GNUC__
|
||||
# define __attribute(arg) __attribute__ (arg)
|
||||
#else
|
||||
# define __attribute(arg)
|
||||
#endif
|
||||
|
||||
extern const char __re_error_msgid[] attribute_hidden;
|
||||
extern const size_t __re_error_msgid_idx[] attribute_hidden;
|
||||
|
||||
/* An integer used to represent a set of bits. It must be unsigned,
|
||||
and must be at least as wide as unsigned int. */
|
||||
typedef unsigned long int bitset_word_t;
|
||||
/* All bits set in a bitset_word_t. */
|
||||
#define BITSET_WORD_MAX ULONG_MAX
|
||||
/* Number of bits in a bitset_word_t. */
|
||||
#define BITSET_WORD_BITS (sizeof (bitset_word_t) * CHAR_BIT)
|
||||
/* Number of bitset_word_t in a bit_set. */
|
||||
#define BITSET_WORDS (SBC_MAX / BITSET_WORD_BITS)
|
||||
typedef bitset_word_t bitset_t[BITSET_WORDS];
|
||||
typedef bitset_word_t *re_bitset_ptr_t;
|
||||
typedef const bitset_word_t *re_const_bitset_ptr_t;
|
||||
|
||||
#define bitset_set(set,i) \
|
||||
(set[i / BITSET_WORD_BITS] |= (bitset_word_t) 1 << i % BITSET_WORD_BITS)
|
||||
#define bitset_clear(set,i) \
|
||||
(set[i / BITSET_WORD_BITS] &= ~((bitset_word_t) 1 << i % BITSET_WORD_BITS))
|
||||
#define bitset_contain(set,i) \
|
||||
(set[i / BITSET_WORD_BITS] & ((bitset_word_t) 1 << i % BITSET_WORD_BITS))
|
||||
#define bitset_empty(set) memset (set, '\0', sizeof (bitset_t))
|
||||
#define bitset_set_all(set) memset (set, '\xff', sizeof (bitset_t))
|
||||
#define bitset_copy(dest,src) memcpy (dest, src, sizeof (bitset_t))
|
||||
|
||||
#define PREV_WORD_CONSTRAINT 0x0001
|
||||
#define PREV_NOTWORD_CONSTRAINT 0x0002
|
||||
#define NEXT_WORD_CONSTRAINT 0x0004
|
||||
#define NEXT_NOTWORD_CONSTRAINT 0x0008
|
||||
#define PREV_NEWLINE_CONSTRAINT 0x0010
|
||||
#define NEXT_NEWLINE_CONSTRAINT 0x0020
|
||||
#define PREV_BEGBUF_CONSTRAINT 0x0040
|
||||
#define NEXT_ENDBUF_CONSTRAINT 0x0080
|
||||
#define WORD_DELIM_CONSTRAINT 0x0100
|
||||
#define NOT_WORD_DELIM_CONSTRAINT 0x0200
|
||||
|
||||
typedef enum
|
||||
{
|
||||
INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
|
||||
WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT,
|
||||
WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
|
||||
INSIDE_NOTWORD = PREV_NOTWORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT,
|
||||
LINE_FIRST = PREV_NEWLINE_CONSTRAINT,
|
||||
LINE_LAST = NEXT_NEWLINE_CONSTRAINT,
|
||||
BUF_FIRST = PREV_BEGBUF_CONSTRAINT,
|
||||
BUF_LAST = NEXT_ENDBUF_CONSTRAINT,
|
||||
WORD_DELIM = WORD_DELIM_CONSTRAINT,
|
||||
NOT_WORD_DELIM = NOT_WORD_DELIM_CONSTRAINT
|
||||
} re_context_type;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int alloc;
|
||||
int nelem;
|
||||
int *elems;
|
||||
} re_node_set;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NON_TYPE = 0,
|
||||
|
||||
/* Node type, These are used by token, node, tree. */
|
||||
CHARACTER = 1,
|
||||
END_OF_RE = 2,
|
||||
SIMPLE_BRACKET = 3,
|
||||
OP_BACK_REF = 4,
|
||||
OP_PERIOD = 5,
|
||||
#ifdef RE_ENABLE_I18N
|
||||
COMPLEX_BRACKET = 6,
|
||||
OP_UTF8_PERIOD = 7,
|
||||
#endif /* RE_ENABLE_I18N */
|
||||
|
||||
/* We define EPSILON_BIT as a macro so that OP_OPEN_SUBEXP is used
|
||||
when the debugger shows values of this enum type. */
|
||||
#define EPSILON_BIT 8
|
||||
OP_OPEN_SUBEXP = EPSILON_BIT | 0,
|
||||
OP_CLOSE_SUBEXP = EPSILON_BIT | 1,
|
||||
OP_ALT = EPSILON_BIT | 2,
|
||||
OP_DUP_ASTERISK = EPSILON_BIT | 3,
|
||||
ANCHOR = EPSILON_BIT | 4,
|
||||
|
||||
/* Tree type, these are used only by tree. */
|
||||
CONCAT = 16,
|
||||
SUBEXP = 17,
|
||||
|
||||
/* Token type, these are used only by token. */
|
||||
OP_DUP_PLUS = 18,
|
||||
OP_DUP_QUESTION,
|
||||
OP_OPEN_BRACKET,
|
||||
OP_CLOSE_BRACKET,
|
||||
OP_CHARSET_RANGE,
|
||||
OP_OPEN_DUP_NUM,
|
||||
OP_CLOSE_DUP_NUM,
|
||||
OP_NON_MATCH_LIST,
|
||||
OP_OPEN_COLL_ELEM,
|
||||
OP_CLOSE_COLL_ELEM,
|
||||
OP_OPEN_EQUIV_CLASS,
|
||||
OP_CLOSE_EQUIV_CLASS,
|
||||
OP_OPEN_CHAR_CLASS,
|
||||
OP_CLOSE_CHAR_CLASS,
|
||||
OP_WORD,
|
||||
OP_NOTWORD,
|
||||
OP_SPACE,
|
||||
OP_NOTSPACE,
|
||||
BACK_SLASH
|
||||
|
||||
} re_token_type_t;
|
||||
|
||||
#ifdef RE_ENABLE_I18N
|
||||
typedef struct
|
||||
{
|
||||
/* Multibyte characters. */
|
||||
wchar_t *mbchars;
|
||||
|
||||
/* Collating symbols. */
|
||||
# ifdef _LIBC
|
||||
int32_t *coll_syms;
|
||||
# endif
|
||||
|
||||
/* Equivalence classes. */
|
||||
# ifdef _LIBC
|
||||
int32_t *equiv_classes;
|
||||
# endif
|
||||
|
||||
/* Range expressions. */
|
||||
# ifdef _LIBC
|
||||
uint32_t *range_starts;
|
||||
uint32_t *range_ends;
|
||||
# else /* not _LIBC */
|
||||
wchar_t *range_starts;
|
||||
wchar_t *range_ends;
|
||||
# endif /* not _LIBC */
|
||||
|
||||
/* Character classes. */
|
||||
wctype_t *char_classes;
|
||||
|
||||
/* If this character set is the non-matching list. */
|
||||
unsigned int non_match : 1;
|
||||
|
||||
/* # of multibyte characters. */
|
||||
int nmbchars;
|
||||
|
||||
/* # of collating symbols. */
|
||||
int ncoll_syms;
|
||||
|
||||
/* # of equivalence classes. */
|
||||
int nequiv_classes;
|
||||
|
||||
/* # of range expressions. */
|
||||
int nranges;
|
||||
|
||||
/* # of character classes. */
|
||||
int nchar_classes;
|
||||
} re_charset_t;
|
||||
#endif /* RE_ENABLE_I18N */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union
|
||||
{
|
||||
unsigned char c; /* for CHARACTER */
|
||||
re_bitset_ptr_t sbcset; /* for SIMPLE_BRACKET */
|
||||
#ifdef RE_ENABLE_I18N
|
||||
re_charset_t *mbcset; /* for COMPLEX_BRACKET */
|
||||
#endif /* RE_ENABLE_I18N */
|
||||
int idx; /* for BACK_REF */
|
||||
re_context_type ctx_type; /* for ANCHOR */
|
||||
} opr;
|
||||
#if __GNUC__ >= 2
|
||||
re_token_type_t type : 8;
|
||||
#else
|
||||
re_token_type_t type;
|
||||
#endif
|
||||
unsigned int constraint : 10; /* context constraint */
|
||||
unsigned int duplicated : 1;
|
||||
unsigned int opt_subexp : 1;
|
||||
#ifdef RE_ENABLE_I18N
|
||||
unsigned int accept_mb : 1;
|
||||
/* These 2 bits can be moved into the union if needed (e.g. if running out
|
||||
of bits; move opr.c to opr.c.c and move the flags to opr.c.flags). */
|
||||
unsigned int mb_partial : 1;
|
||||
#endif
|
||||
unsigned int word_char : 1;
|
||||
} re_token_t;
|
||||
|
||||
#define IS_EPSILON_NODE(type) ((type) & EPSILON_BIT)
|
||||
|
||||
struct re_string_t
|
||||
{
|
||||
/* Indicate the raw buffer which is the original string passed as an
|
||||
argument of regexec(), re_search(), etc.. */
|
||||
const unsigned char *raw_mbs;
|
||||
/* Store the multibyte string. In case of "case insensitive mode" like
|
||||
REG_ICASE, upper cases of the string are stored, otherwise MBS points
|
||||
the same address that RAW_MBS points. */
|
||||
unsigned char *mbs;
|
||||
#ifdef RE_ENABLE_I18N
|
||||
/* Store the wide character string which is corresponding to MBS. */
|
||||
wint_t *wcs;
|
||||
int *offsets;
|
||||
mbstate_t cur_state;
|
||||
#endif
|
||||
/* Index in RAW_MBS. Each character mbs[i] corresponds to
|
||||
raw_mbs[raw_mbs_idx + i]. */
|
||||
int raw_mbs_idx;
|
||||
/* The length of the valid characters in the buffers. */
|
||||
int valid_len;
|
||||
/* The corresponding number of bytes in raw_mbs array. */
|
||||
int valid_raw_len;
|
||||
/* The length of the buffers MBS and WCS. */
|
||||
int bufs_len;
|
||||
/* The index in MBS, which is updated by re_string_fetch_byte. */
|
||||
int cur_idx;
|
||||
/* length of RAW_MBS array. */
|
||||
int raw_len;
|
||||
/* This is RAW_LEN - RAW_MBS_IDX + VALID_LEN - VALID_RAW_LEN. */
|
||||
int len;
|
||||
/* End of the buffer may be shorter than its length in the cases such
|
||||
as re_match_2, re_search_2. Then, we use STOP for end of the buffer
|
||||
instead of LEN. */
|
||||
int raw_stop;
|
||||
/* This is RAW_STOP - RAW_MBS_IDX adjusted through OFFSETS. */
|
||||
int stop;
|
||||
|
||||
/* The context of mbs[0]. We store the context independently, since
|
||||
the context of mbs[0] may be different from raw_mbs[0], which is
|
||||
the beginning of the input string. */
|
||||
unsigned int tip_context;
|
||||
/* The translation passed as a part of an argument of re_compile_pattern. */
|
||||
RE_TRANSLATE_TYPE trans;
|
||||
/* Copy of re_dfa_t's word_char. */
|
||||
re_const_bitset_ptr_t word_char;
|
||||
/* 1 if REG_ICASE. */
|
||||
unsigned char icase;
|
||||
unsigned char is_utf8;
|
||||
unsigned char map_notascii;
|
||||
unsigned char mbs_allocated;
|
||||
unsigned char offsets_needed;
|
||||
unsigned char newline_anchor;
|
||||
unsigned char word_ops_used;
|
||||
int mb_cur_max;
|
||||
};
|
||||
typedef struct re_string_t re_string_t;
|
||||
|
||||
|
||||
struct re_dfa_t;
|
||||
typedef struct re_dfa_t re_dfa_t;
|
||||
|
||||
#ifndef _LIBC
|
||||
# ifdef __i386__
|
||||
# define internal_function __attribute ((regparm (3), stdcall))
|
||||
# else
|
||||
# define internal_function
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef NOT_IN_libc
|
||||
static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr,
|
||||
int new_buf_len)
|
||||
internal_function;
|
||||
# ifdef RE_ENABLE_I18N
|
||||
static void build_wcs_buffer (re_string_t *pstr) internal_function;
|
||||
static reg_errcode_t build_wcs_upper_buffer (re_string_t *pstr)
|
||||
internal_function;
|
||||
# endif /* RE_ENABLE_I18N */
|
||||
static void build_upper_buffer (re_string_t *pstr) internal_function;
|
||||
static void re_string_translate_buffer (re_string_t *pstr) internal_function;
|
||||
static unsigned int re_string_context_at (const re_string_t *input, int idx,
|
||||
int eflags)
|
||||
internal_function __attribute ((pure));
|
||||
#endif
|
||||
#define re_string_peek_byte(pstr, offset) \
|
||||
((pstr)->mbs[(pstr)->cur_idx + offset])
|
||||
#define re_string_fetch_byte(pstr) \
|
||||
((pstr)->mbs[(pstr)->cur_idx++])
|
||||
#define re_string_first_byte(pstr, idx) \
|
||||
((idx) == (pstr)->valid_len || (pstr)->wcs[idx] != WEOF)
|
||||
#define re_string_is_single_byte_char(pstr, idx) \
|
||||
((pstr)->wcs[idx] != WEOF && ((pstr)->valid_len == (idx) + 1 \
|
||||
|| (pstr)->wcs[(idx) + 1] != WEOF))
|
||||
#define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx)
|
||||
#define re_string_cur_idx(pstr) ((pstr)->cur_idx)
|
||||
#define re_string_get_buffer(pstr) ((pstr)->mbs)
|
||||
#define re_string_length(pstr) ((pstr)->len)
|
||||
#define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx])
|
||||
#define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx))
|
||||
#define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx))
|
||||
|
||||
#ifndef _LIBC
|
||||
# if HAVE_ALLOCA
|
||||
# if (_MSC_VER)
|
||||
# include <malloc.h>
|
||||
# define __libc_use_alloca(n) 0
|
||||
# else
|
||||
# include <alloca.h>
|
||||
/* The OS usually guarantees only one guard page at the bottom of the stack,
|
||||
and a page size can be as small as 4096 bytes. So we cannot safely
|
||||
allocate anything larger than 4096 bytes. Also care for the possibility
|
||||
of a few compiler-allocated temporary stack slots. */
|
||||
# define __libc_use_alloca(n) ((n) < 4032)
|
||||
# endif
|
||||
# else
|
||||
/* alloca is implemented with malloc, so just use malloc. */
|
||||
# define __libc_use_alloca(n) 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t)))
|
||||
/* SunOS 4.1.x realloc doesn't accept null pointers: pre-Standard C. Sigh. */
|
||||
#define re_realloc(p,t,n) ((p != NULL) ? (t *) realloc (p,(n)*sizeof(t)) : (t *) calloc(n,sizeof(t)))
|
||||
#define re_free(p) free (p)
|
||||
|
||||
struct bin_tree_t
|
||||
{
|
||||
struct bin_tree_t *parent;
|
||||
struct bin_tree_t *left;
|
||||
struct bin_tree_t *right;
|
||||
struct bin_tree_t *first;
|
||||
struct bin_tree_t *next;
|
||||
|
||||
re_token_t token;
|
||||
|
||||
/* `node_idx' is the index in dfa->nodes, if `type' == 0.
|
||||
Otherwise `type' indicate the type of this node. */
|
||||
int node_idx;
|
||||
};
|
||||
typedef struct bin_tree_t bin_tree_t;
|
||||
|
||||
#define BIN_TREE_STORAGE_SIZE \
|
||||
((1024 - sizeof (void *)) / sizeof (bin_tree_t))
|
||||
|
||||
struct bin_tree_storage_t
|
||||
{
|
||||
struct bin_tree_storage_t *next;
|
||||
bin_tree_t data[BIN_TREE_STORAGE_SIZE];
|
||||
};
|
||||
typedef struct bin_tree_storage_t bin_tree_storage_t;
|
||||
|
||||
#define CONTEXT_WORD 1
|
||||
#define CONTEXT_NEWLINE (CONTEXT_WORD << 1)
|
||||
#define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1)
|
||||
#define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1)
|
||||
|
||||
#define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD)
|
||||
#define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE)
|
||||
#define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF)
|
||||
#define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF)
|
||||
#define IS_ORDINARY_CONTEXT(c) ((c) == 0)
|
||||
|
||||
#define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_')
|
||||
#define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR)
|
||||
#define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_')
|
||||
#define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR)
|
||||
|
||||
#define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \
|
||||
((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
|
||||
|| ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
|
||||
|| ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\
|
||||
|| ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context)))
|
||||
|
||||
#define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \
|
||||
((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \
|
||||
|| (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \
|
||||
|| (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \
|
||||
|| (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context)))
|
||||
|
||||
struct re_dfastate_t
|
||||
{
|
||||
unsigned int hash;
|
||||
re_node_set nodes;
|
||||
re_node_set non_eps_nodes;
|
||||
re_node_set inveclosure;
|
||||
re_node_set *entrance_nodes;
|
||||
struct re_dfastate_t **trtable, **word_trtable;
|
||||
unsigned int context : 4;
|
||||
unsigned int halt : 1;
|
||||
/* If this state can accept `multi byte'.
|
||||
Note that we refer to multibyte characters, and multi character
|
||||
collating elements as `multi byte'. */
|
||||
unsigned int accept_mb : 1;
|
||||
/* If this state has backreference node(s). */
|
||||
unsigned int has_backref : 1;
|
||||
unsigned int has_constraint : 1;
|
||||
};
|
||||
typedef struct re_dfastate_t re_dfastate_t;
|
||||
|
||||
struct re_state_table_entry
|
||||
{
|
||||
int num;
|
||||
int alloc;
|
||||
re_dfastate_t **array;
|
||||
};
|
||||
|
||||
/* Array type used in re_sub_match_last_t and re_sub_match_top_t. */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int next_idx;
|
||||
int alloc;
|
||||
re_dfastate_t **array;
|
||||
} state_array_t;
|
||||
|
||||
/* Store information about the node NODE whose type is OP_CLOSE_SUBEXP. */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int node;
|
||||
int str_idx; /* The position NODE match at. */
|
||||
state_array_t path;
|
||||
} re_sub_match_last_t;
|
||||
|
||||
/* Store information about the node NODE whose type is OP_OPEN_SUBEXP.
|
||||
And information about the node, whose type is OP_CLOSE_SUBEXP,
|
||||
corresponding to NODE is stored in LASTS. */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int str_idx;
|
||||
int node;
|
||||
state_array_t *path;
|
||||
int alasts; /* Allocation size of LASTS. */
|
||||
int nlasts; /* The number of LASTS. */
|
||||
re_sub_match_last_t **lasts;
|
||||
} re_sub_match_top_t;
|
||||
|
||||
struct re_backref_cache_entry
|
||||
{
|
||||
int node;
|
||||
int str_idx;
|
||||
int subexp_from;
|
||||
int subexp_to;
|
||||
char more;
|
||||
char unused;
|
||||
unsigned short int eps_reachable_subexps_map;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
/* The string object corresponding to the input string. */
|
||||
re_string_t input;
|
||||
#if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
|
||||
const re_dfa_t *const dfa;
|
||||
#else
|
||||
const re_dfa_t *dfa;
|
||||
#endif
|
||||
/* EFLAGS of the argument of regexec. */
|
||||
int eflags;
|
||||
/* Where the matching ends. */
|
||||
int match_last;
|
||||
int last_node;
|
||||
/* The state log used by the matcher. */
|
||||
re_dfastate_t **state_log;
|
||||
int state_log_top;
|
||||
/* Back reference cache. */
|
||||
int nbkref_ents;
|
||||
int abkref_ents;
|
||||
struct re_backref_cache_entry *bkref_ents;
|
||||
int max_mb_elem_len;
|
||||
int nsub_tops;
|
||||
int asub_tops;
|
||||
re_sub_match_top_t **sub_tops;
|
||||
} re_match_context_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
re_dfastate_t **sifted_states;
|
||||
re_dfastate_t **limited_states;
|
||||
int last_node;
|
||||
int last_str_idx;
|
||||
re_node_set limits;
|
||||
} re_sift_context_t;
|
||||
|
||||
struct re_fail_stack_ent_t
|
||||
{
|
||||
int idx;
|
||||
int node;
|
||||
regmatch_t *regs;
|
||||
re_node_set eps_via_nodes;
|
||||
};
|
||||
|
||||
struct re_fail_stack_t
|
||||
{
|
||||
int num;
|
||||
int alloc;
|
||||
struct re_fail_stack_ent_t *stack;
|
||||
};
|
||||
|
||||
struct re_dfa_t
|
||||
{
|
||||
re_token_t *nodes;
|
||||
size_t nodes_alloc;
|
||||
size_t nodes_len;
|
||||
int *nexts;
|
||||
int *org_indices;
|
||||
re_node_set *edests;
|
||||
re_node_set *eclosures;
|
||||
re_node_set *inveclosures;
|
||||
struct re_state_table_entry *state_table;
|
||||
re_dfastate_t *init_state;
|
||||
re_dfastate_t *init_state_word;
|
||||
re_dfastate_t *init_state_nl;
|
||||
re_dfastate_t *init_state_begbuf;
|
||||
bin_tree_t *str_tree;
|
||||
bin_tree_storage_t *str_tree_storage;
|
||||
re_bitset_ptr_t sb_char;
|
||||
int str_tree_storage_idx;
|
||||
|
||||
/* number of subexpressions `re_nsub' is in regex_t. */
|
||||
unsigned int state_hash_mask;
|
||||
int init_node;
|
||||
int nbackref; /* The number of backreference in this dfa. */
|
||||
|
||||
/* Bitmap expressing which backreference is used. */
|
||||
bitset_word_t used_bkref_map;
|
||||
bitset_word_t completed_bkref_map;
|
||||
|
||||
unsigned int has_plural_match : 1;
|
||||
/* If this dfa has "multibyte node", which is a backreference or
|
||||
a node which can accept multibyte character or multi character
|
||||
collating element. */
|
||||
unsigned int has_mb_node : 1;
|
||||
unsigned int is_utf8 : 1;
|
||||
unsigned int map_notascii : 1;
|
||||
unsigned int word_ops_used : 1;
|
||||
int mb_cur_max;
|
||||
bitset_t word_char;
|
||||
reg_syntax_t syntax;
|
||||
int *subexp_map;
|
||||
#ifdef DEBUG
|
||||
char* re_str;
|
||||
#endif
|
||||
#if defined _LIBC
|
||||
__libc_lock_define (, lock)
|
||||
#endif
|
||||
};
|
||||
|
||||
#define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set))
|
||||
#define re_node_set_remove(set,id) \
|
||||
(re_node_set_remove_at (set, re_node_set_contains (set, id) - 1))
|
||||
#define re_node_set_empty(p) ((p)->nelem = 0)
|
||||
#define re_node_set_free(set) re_free ((set)->elems)
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
SB_CHAR,
|
||||
MB_CHAR,
|
||||
EQUIV_CLASS,
|
||||
COLL_SYM,
|
||||
CHAR_CLASS
|
||||
} bracket_elem_type;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
bracket_elem_type type;
|
||||
union
|
||||
{
|
||||
unsigned char ch;
|
||||
unsigned char *name;
|
||||
wchar_t wch;
|
||||
} opr;
|
||||
} bracket_elem_t;
|
||||
|
||||
|
||||
/* Inline functions for bitset operation. */
|
||||
static inline void
|
||||
bitset_not (bitset_t set)
|
||||
{
|
||||
int bitset_i;
|
||||
for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
|
||||
set[bitset_i] = ~set[bitset_i];
|
||||
}
|
||||
|
||||
static inline void
|
||||
bitset_merge (bitset_t dest, const bitset_t src)
|
||||
{
|
||||
int bitset_i;
|
||||
for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
|
||||
dest[bitset_i] |= src[bitset_i];
|
||||
}
|
||||
|
||||
static inline void
|
||||
bitset_mask (bitset_t dest, const bitset_t src)
|
||||
{
|
||||
int bitset_i;
|
||||
for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i)
|
||||
dest[bitset_i] &= src[bitset_i];
|
||||
}
|
||||
|
||||
#ifdef RE_ENABLE_I18N
|
||||
/* Inline functions for re_string. */
|
||||
static inline int
|
||||
internal_function __attribute ((pure))
|
||||
re_string_char_size_at (const re_string_t *pstr, int idx)
|
||||
{
|
||||
int byte_idx;
|
||||
if (pstr->mb_cur_max == 1)
|
||||
return 1;
|
||||
for (byte_idx = 1; idx + byte_idx < pstr->valid_len; ++byte_idx)
|
||||
if (pstr->wcs[idx + byte_idx] != WEOF)
|
||||
break;
|
||||
return byte_idx;
|
||||
}
|
||||
|
||||
static inline wint_t
|
||||
internal_function __attribute ((pure))
|
||||
re_string_wchar_at (const re_string_t *pstr, int idx)
|
||||
{
|
||||
if (pstr->mb_cur_max == 1)
|
||||
return (wint_t) pstr->mbs[idx];
|
||||
return (wint_t) pstr->wcs[idx];
|
||||
}
|
||||
|
||||
# ifndef NOT_IN_libc
|
||||
static int
|
||||
internal_function __attribute ((pure))
|
||||
re_string_elem_size_at (const re_string_t *pstr, int idx)
|
||||
{
|
||||
# ifdef _LIBC
|
||||
const unsigned char *p, *extra;
|
||||
const int32_t *table, *indirect;
|
||||
int32_t tmp;
|
||||
# include <locale/weight.h>
|
||||
uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
|
||||
|
||||
if (nrules != 0)
|
||||
{
|
||||
table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
|
||||
extra = (const unsigned char *)
|
||||
_NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB);
|
||||
indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
|
||||
_NL_COLLATE_INDIRECTMB);
|
||||
p = pstr->mbs + idx;
|
||||
tmp = findidx (&p);
|
||||
return p - pstr->mbs - idx;
|
||||
}
|
||||
else
|
||||
# endif /* _LIBC */
|
||||
return 1;
|
||||
}
|
||||
# endif
|
||||
#endif /* RE_ENABLE_I18N */
|
||||
|
||||
#endif /* _REGEX_INTERNAL_H */
|
4369
deps/regex/regexec.c
vendored
Normal file
4369
deps/regex/regexec.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
111
docs/error-handling.md
Normal file
111
docs/error-handling.md
Normal file
@ -0,0 +1,111 @@
|
||||
Error reporting in libgit2
|
||||
==========================
|
||||
|
||||
Error reporting is performed on an explicit `git_error **` argument, which appears at the end of all API calls that can return an error. Yes, this does clutter the API.
|
||||
|
||||
When a function fails, an error is set on the error variable **and** returns one of the generic error codes.
|
||||
|
||||
~~~c
|
||||
int git_repository_open(git_repository **repository, const char *path, git_error **error)
|
||||
{
|
||||
// perform some opening
|
||||
if (p_exists(path) < 0) {
|
||||
giterr_set(error, GITERR_REPOSITORY, "The path '%s' doesn't exist", path);
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
if (try_to_parse(path, error) < 0)
|
||||
return GIT_ERROR;
|
||||
|
||||
...
|
||||
}
|
||||
~~~
|
||||
|
||||
The simple error API
|
||||
--------------------
|
||||
|
||||
- `void giterr_set(git_error **, int, const char *, ...)`: the main function used to set an error. It allocates a new error object and stores it in the passed error pointer. It has no return value. The arguments for `giterr_set` are as follows:
|
||||
|
||||
- `git_error **error_ptr`: the pointer where the error will be created.
|
||||
- `int error_class`: the class for the error. This is **not** an error code: this is an speficic enum that specifies the error family. The point is to map these families 1-1 with Exception types on higher level languages (e.g. GitRepositoryException)
|
||||
- `const char *error_str, ...`: the error string, with optional formatting arguments
|
||||
|
||||
- `void giterr_free(git_error *)`: takes an error and frees it. This function is available in the external API.
|
||||
|
||||
- `void giterr_clear(git_error **)`: clears an error previously set in an error pointer, setting it to NULL and calling `giterr_free` on it.
|
||||
|
||||
- `void giterr_propagate(git_error **, git_error *)`: moves an error to a given error pointer, handling the case when the error pointer is NULL (in that case the error gets freed, because it cannot be propagated).
|
||||
|
||||
The new error code return values
|
||||
--------------------------------
|
||||
|
||||
We are doing this the POSIX way: one error code for each "expected failure", and a generic error code for all the critical failures.
|
||||
|
||||
For instance: A reference lookup can have an expected failure (which is when the reference cannot be found), and a critical failure (which could be any of a long list of things that could go wrong, such as the refs packfile being corrupted, a loose ref being written with the wrong permissions, etc). We cannot have distinct error codes for every single error in the library, hence `git_reference_lookup` would return GIT_SUCCESS if the operation was successful, GIT_ENOTFOUND when the reference doesn't exist, and GIT_ERROR when an error happens -- **the error is then detailed in the `git_error` parameter**.
|
||||
|
||||
Please be smart when returning error codes. Functions have max two "expected errors", and in most cases only one.
|
||||
|
||||
Writing error messages
|
||||
----------------------
|
||||
|
||||
Here are some guidelines when writing error messages:
|
||||
|
||||
- Use proper English, and an impersonal or past tenses: *The given path does not exist*, *Failed to lookup object in ODB*
|
||||
|
||||
- Use short, direct and objective messages. **One line, max**. libgit2 is a low level library: think that all the messages reported will be thrown as Ruby or Python exceptions. Think how long are common exception messages in those languages.
|
||||
|
||||
- **Do not add redundant information to the error message**, specially information that can be infered from the context.
|
||||
|
||||
E.g. in `git_repository_open`, do not report a message like "Failed to open repository: path not found". Somebody is
|
||||
calling that function. If it fails, he already knows that the repository failed to open!
|
||||
|
||||
General guidelines for error reporting
|
||||
--------------------------------------
|
||||
|
||||
- We never handle programming errors with these functions. Programming errors are `assert`ed, and when their source is internal, fixed as soon as possible. This is C, people.
|
||||
|
||||
Example of programming errors that would **not** be handled: passing NULL to a function that expects a valid pointer; passing a `git_tree` to a function that expects a `git_commit`. All these cases need to be identified with `assert` and fixed asap.
|
||||
|
||||
Example of a runtime error: failing to parse a `git_tree` because it contains invalid data. Failing to open a file because it doesn't exist on disk. These errors would be handled, and a `git_error` would be set.
|
||||
|
||||
- The `git_error **` argument is always the last in the signature of all API calls. No exceptions.
|
||||
|
||||
- When the programmer (or us, internally) doesn't need error handling, he can pass `NULL` to the `git_error **` param. This means that the errors won't be *reported*, but obviously they still will be handled (i.e. the failing function will interrupt and return cleanly). This is transparently handled by `giterr_set`
|
||||
|
||||
- `git_error *` **must be initialized to `NULL` before passing its value to a function!!**
|
||||
|
||||
~~~c
|
||||
git_error *err;
|
||||
git_error *good_error = NULL;
|
||||
|
||||
git_foo_func(arg1, arg2, &error); // invalid: `error` is not initialized
|
||||
git_foo_func2(arg1, arg2, &good_error); // OK!
|
||||
git_foo_func3(arg1, arg2, NULL); // OK! But no error reporting!
|
||||
~~~
|
||||
|
||||
- Piling up errors is an error! Don't do this! Errors must always be free'd when a function returns.
|
||||
|
||||
~~~c
|
||||
git_error *error = NULL;
|
||||
|
||||
git_foo_func1(arg1, &error);
|
||||
git_foo_func2(arg2, &error); // WRONG! What if func1 failed? `error` would leak!
|
||||
~~~
|
||||
|
||||
- Likewise: do not rethrow errors internally!
|
||||
|
||||
~~~c
|
||||
int git_commit_create(..., git_error **error)
|
||||
{
|
||||
if (git_reference_exists("HEAD", error) < 0) {
|
||||
/* HEAD does not exist; create it so we can commit... */
|
||||
if (git_reference_create("HEAD", error) < 0) {
|
||||
/* error could be rethrown */
|
||||
}
|
||||
}
|
||||
|
||||
- Remember that errors are now allocated, and hence they need to be free'd after they've been used. Failure to do so internally (e.g. in the already seen examples of error piling) will be reported by Valgrind, so we can easily find where are we rethrowing errors.
|
||||
|
||||
- Remember that any function that fails **will set an error object**, and that object will be freed.
|
@ -1,13 +1,15 @@
|
||||
.PHONY: all
|
||||
|
||||
CC = gcc
|
||||
CFLAGS = -g -I../include
|
||||
CFLAGS = -g -I../include -I../src
|
||||
LFLAGS = -L../build -lgit2 -lz
|
||||
APPS = general showindex diff
|
||||
|
||||
all: general showindex
|
||||
all: $(APPS)
|
||||
|
||||
% : %.c
|
||||
$(CC) -o $@ $(CFLAGS) $< $(LFLAGS)
|
||||
|
||||
clean:
|
||||
$(RM) general showindex
|
||||
$(RM) $(APPS)
|
||||
$(RM) -r *.dSYM
|
||||
|
240
examples/diff.c
Normal file
240
examples/diff.c
Normal file
@ -0,0 +1,240 @@
|
||||
#include <stdio.h>
|
||||
#include <git2.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void check(int error, const char *message)
|
||||
{
|
||||
if (error) {
|
||||
fprintf(stderr, "%s (%d)\n", message, error);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int resolve_to_tree(git_repository *repo, const char *identifier, git_tree **tree)
|
||||
{
|
||||
int err = 0;
|
||||
size_t len = strlen(identifier);
|
||||
git_oid oid;
|
||||
git_object *obj = NULL;
|
||||
|
||||
/* try to resolve as OID */
|
||||
if (git_oid_fromstrn(&oid, identifier, len) == 0)
|
||||
git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJ_ANY);
|
||||
|
||||
/* try to resolve as reference */
|
||||
if (obj == NULL) {
|
||||
git_reference *ref, *resolved;
|
||||
if (git_reference_lookup(&ref, repo, identifier) == 0) {
|
||||
git_reference_resolve(&resolved, ref);
|
||||
git_reference_free(ref);
|
||||
if (resolved) {
|
||||
git_object_lookup(&obj, repo, git_reference_oid(resolved), GIT_OBJ_ANY);
|
||||
git_reference_free(resolved);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (obj == NULL)
|
||||
return GIT_ENOTFOUND;
|
||||
|
||||
switch (git_object_type(obj)) {
|
||||
case GIT_OBJ_TREE:
|
||||
*tree = (git_tree *)obj;
|
||||
break;
|
||||
case GIT_OBJ_COMMIT:
|
||||
err = git_commit_tree(tree, (git_commit *)obj);
|
||||
git_object_free(obj);
|
||||
break;
|
||||
default:
|
||||
err = GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
char *colors[] = {
|
||||
"\033[m", /* reset */
|
||||
"\033[1m", /* bold */
|
||||
"\033[31m", /* red */
|
||||
"\033[32m", /* green */
|
||||
"\033[36m" /* cyan */
|
||||
};
|
||||
|
||||
int printer(
|
||||
void *data,
|
||||
git_diff_delta *delta,
|
||||
git_diff_range *range,
|
||||
char usage,
|
||||
const char *line,
|
||||
size_t line_len)
|
||||
{
|
||||
int *last_color = data, color = 0;
|
||||
|
||||
if (*last_color >= 0) {
|
||||
switch (usage) {
|
||||
case GIT_DIFF_LINE_ADDITION: color = 3; break;
|
||||
case GIT_DIFF_LINE_DELETION: color = 2; break;
|
||||
case GIT_DIFF_LINE_ADD_EOFNL: color = 3; break;
|
||||
case GIT_DIFF_LINE_DEL_EOFNL: color = 2; break;
|
||||
case GIT_DIFF_LINE_FILE_HDR: color = 1; break;
|
||||
case GIT_DIFF_LINE_HUNK_HDR: color = 4; break;
|
||||
default: color = 0;
|
||||
}
|
||||
if (color != *last_color) {
|
||||
if (*last_color == 1 || color == 1)
|
||||
fputs(colors[0], stdout);
|
||||
fputs(colors[color], stdout);
|
||||
*last_color = color;
|
||||
}
|
||||
}
|
||||
|
||||
fputs(line, stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int check_uint16_param(const char *arg, const char *pattern, uint16_t *val)
|
||||
{
|
||||
size_t len = strlen(pattern);
|
||||
uint16_t strval;
|
||||
char *endptr = NULL;
|
||||
if (strncmp(arg, pattern, len))
|
||||
return 0;
|
||||
strval = strtoul(arg + len, &endptr, 0);
|
||||
if (endptr == arg)
|
||||
return 0;
|
||||
*val = strval;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int check_str_param(const char *arg, const char *pattern, char **val)
|
||||
{
|
||||
size_t len = strlen(pattern);
|
||||
if (strncmp(arg, pattern, len))
|
||||
return 0;
|
||||
*val = (char *)(arg + len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
void usage(const char *message, const char *arg)
|
||||
{
|
||||
if (message && arg)
|
||||
fprintf(stderr, "%s: %s\n", message, arg);
|
||||
else if (message)
|
||||
fprintf(stderr, "%s\n", message);
|
||||
fprintf(stderr, "usage: diff [<tree-oid> [<tree-oid>]]\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
char path[GIT_PATH_MAX];
|
||||
git_repository *repo = NULL;
|
||||
git_tree *t1 = NULL, *t2 = NULL;
|
||||
git_diff_options opts = {0};
|
||||
git_diff_list *diff;
|
||||
int i, color = -1, compact = 0, cached = 0;
|
||||
char *a, *dir = ".", *treeish1 = NULL, *treeish2 = NULL;
|
||||
|
||||
/* parse arguments as copied from git-diff */
|
||||
|
||||
for (i = 1; i < argc; ++i) {
|
||||
a = argv[i];
|
||||
|
||||
if (a[0] != '-') {
|
||||
if (treeish1 == NULL)
|
||||
treeish1 = a;
|
||||
else if (treeish2 == NULL)
|
||||
treeish2 = a;
|
||||
else
|
||||
usage("Only one or two tree identifiers can be provided", NULL);
|
||||
}
|
||||
else if (!strcmp(a, "-p") || !strcmp(a, "-u") ||
|
||||
!strcmp(a, "--patch"))
|
||||
compact = 0;
|
||||
else if (!strcmp(a, "--cached"))
|
||||
cached = 1;
|
||||
else if (!strcmp(a, "--name-status"))
|
||||
compact = 1;
|
||||
else if (!strcmp(a, "--color"))
|
||||
color = 0;
|
||||
else if (!strcmp(a, "--no-color"))
|
||||
color = -1;
|
||||
else if (!strcmp(a, "-R"))
|
||||
opts.flags |= GIT_DIFF_REVERSE;
|
||||
else if (!strcmp(a, "-a") || !strcmp(a, "--text"))
|
||||
opts.flags |= GIT_DIFF_FORCE_TEXT;
|
||||
else if (!strcmp(a, "--ignore-space-at-eol"))
|
||||
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL;
|
||||
else if (!strcmp(a, "-b") || !strcmp(a, "--ignore-space-change"))
|
||||
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE;
|
||||
else if (!strcmp(a, "-w") || !strcmp(a, "--ignore-all-space"))
|
||||
opts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
|
||||
else if (!strcmp(a, "--ignored"))
|
||||
opts.flags |= GIT_DIFF_INCLUDE_IGNORED;
|
||||
else if (!strcmp(a, "--untracked"))
|
||||
opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
|
||||
else if (!check_uint16_param(a, "-U", &opts.context_lines) &&
|
||||
!check_uint16_param(a, "--unified=", &opts.context_lines) &&
|
||||
!check_uint16_param(a, "--inter-hunk-context=",
|
||||
&opts.interhunk_lines) &&
|
||||
!check_str_param(a, "--src-prefix=", &opts.old_prefix) &&
|
||||
!check_str_param(a, "--dst-prefix=", &opts.new_prefix))
|
||||
usage("Unknown arg", a);
|
||||
}
|
||||
|
||||
/* open repo */
|
||||
|
||||
check(git_repository_discover(path, sizeof(path), dir, 0, "/"),
|
||||
"Could not discover repository");
|
||||
check(git_repository_open(&repo, path),
|
||||
"Could not open repository");
|
||||
|
||||
if (treeish1)
|
||||
check(resolve_to_tree(repo, treeish1, &t1), "Looking up first tree");
|
||||
if (treeish2)
|
||||
check(resolve_to_tree(repo, treeish2, &t2), "Looking up second tree");
|
||||
|
||||
/* <sha1> <sha2> */
|
||||
/* <sha1> --cached */
|
||||
/* <sha1> */
|
||||
/* --cached */
|
||||
/* nothing */
|
||||
|
||||
if (t1 && t2)
|
||||
check(git_diff_tree_to_tree(repo, &opts, t1, t2, &diff), "Diff");
|
||||
else if (t1 && cached)
|
||||
check(git_diff_index_to_tree(repo, &opts, t1, &diff), "Diff");
|
||||
else if (t1) {
|
||||
git_diff_list *diff2;
|
||||
check(git_diff_index_to_tree(repo, &opts, t1, &diff), "Diff");
|
||||
check(git_diff_workdir_to_index(repo, &opts, &diff2), "Diff");
|
||||
check(git_diff_merge(diff, diff2), "Merge diffs");
|
||||
git_diff_list_free(diff2);
|
||||
}
|
||||
else if (cached) {
|
||||
check(resolve_to_tree(repo, "HEAD", &t1), "looking up HEAD");
|
||||
check(git_diff_index_to_tree(repo, &opts, t1, &diff), "Diff");
|
||||
}
|
||||
else
|
||||
check(git_diff_workdir_to_index(repo, &opts, &diff), "Diff");
|
||||
|
||||
if (color >= 0)
|
||||
fputs(colors[0], stdout);
|
||||
|
||||
if (compact)
|
||||
check(git_diff_print_compact(diff, &color, printer), "Displaying diff");
|
||||
else
|
||||
check(git_diff_print_patch(diff, &color, printer), "Displaying diff");
|
||||
|
||||
if (color >= 0)
|
||||
fputs(colors[0], stdout);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
git_tree_free(t1);
|
||||
git_tree_free(t2);
|
||||
git_repository_free(repo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -273,7 +273,7 @@ int main (int argc, char** argv)
|
||||
|
||||
// Once you have the entry object, you can access the content or subtree (or commit, in the case
|
||||
// of submodules) that it points to. You can also get the mode if you want.
|
||||
git_tree_entry_2object(&objt, repo, entry); // blob
|
||||
git_tree_entry_to_object(&objt, repo, entry); // blob
|
||||
|
||||
// Remember to close the looked-up object once you are done using it
|
||||
git_object_free(objt);
|
||||
@ -335,7 +335,7 @@ int main (int argc, char** argv)
|
||||
// We can then lookup and parse the commited pointed at by the returned OID;
|
||||
// note that this operation is specially fast since the raw contents of the commit object will
|
||||
// be cached in memory
|
||||
while ((git_revwalk_next(&oid, walk)) == GIT_SUCCESS) {
|
||||
while ((git_revwalk_next(&oid, walk)) == 0) {
|
||||
error = git_commit_lookup(&wcommit, repo, &oid);
|
||||
cmsg = git_commit_message(wcommit);
|
||||
cauth = git_commit_author(wcommit);
|
||||
|
@ -2,7 +2,7 @@ default: all
|
||||
|
||||
CC = gcc
|
||||
CFLAGS += -g
|
||||
CFLAGS += -I../../include -L../../ -lgit2
|
||||
CFLAGS += -I../../include -L../../ -lgit2 -lpthread
|
||||
|
||||
OBJECTS = \
|
||||
git2.o \
|
||||
|
@ -3,95 +3,111 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
static int rename_packfile(char *packname, git_indexer *idx)
|
||||
struct dl_data {
|
||||
git_remote *remote;
|
||||
git_off_t *bytes;
|
||||
git_indexer_stats *stats;
|
||||
int ret;
|
||||
int finished;
|
||||
};
|
||||
|
||||
static void *download(void *ptr)
|
||||
{
|
||||
char path[GIT_PATH_MAX], oid[GIT_OID_HEXSZ + 1], *slash;
|
||||
int ret;
|
||||
struct dl_data *data = (struct dl_data *)ptr;
|
||||
|
||||
strcpy(path, packname);
|
||||
slash = strrchr(path, '/');
|
||||
// Connect to the remote end specifying that we want to fetch
|
||||
// information from it.
|
||||
if (git_remote_connect(data->remote, GIT_DIR_FETCH) < 0) {
|
||||
data->ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (!slash)
|
||||
return GIT_EINVALIDARGS;
|
||||
// Download the packfile and index it. This function updates the
|
||||
// amount of received data and the indexer stats which lets you
|
||||
// inform the user about progress.
|
||||
if (git_remote_download(data->remote, data->bytes, data->stats) < 0) {
|
||||
data->ret = -1;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
memset(oid, 0x0, sizeof(oid));
|
||||
// The name of the packfile is given by it's hash which you can get
|
||||
// with git_indexer_hash after the index has been written out to
|
||||
// disk. Rename the packfile to its "real" name in the same
|
||||
// directory as it was originally (libgit2 stores it in the folder
|
||||
// where the packs go, so a rename in place is the right thing to do here
|
||||
git_oid_fmt(oid, git_indexer_hash(idx));
|
||||
ret = sprintf(slash + 1, "pack-%s.pack", oid);
|
||||
if(ret < 0)
|
||||
return GIT_EOSERR;
|
||||
data->ret = 0;
|
||||
|
||||
printf("Renaming pack to %s\n", path);
|
||||
return rename(packname, path);
|
||||
exit:
|
||||
data->finished = 1;
|
||||
pthread_exit(&data->ret);
|
||||
}
|
||||
|
||||
int update_cb(const char *refname, const git_oid *a, const git_oid *b)
|
||||
{
|
||||
const char *action;
|
||||
char a_str[GIT_OID_HEXSZ+1], b_str[GIT_OID_HEXSZ+1];
|
||||
|
||||
git_oid_fmt(b_str, b);
|
||||
b_str[GIT_OID_HEXSZ] = '\0';
|
||||
|
||||
if (git_oid_iszero(a)) {
|
||||
printf("[new] %.20s %s\n", b_str, refname);
|
||||
} else {
|
||||
git_oid_fmt(a_str, a);
|
||||
a_str[GIT_OID_HEXSZ] = '\0';
|
||||
printf("[updated] %.10s..%.10s %s\n", a_str, b_str, refname);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fetch(git_repository *repo, int argc, char **argv)
|
||||
{
|
||||
git_remote *remote = NULL;
|
||||
git_indexer *idx = NULL;
|
||||
git_off_t bytes = 0;
|
||||
git_indexer_stats stats;
|
||||
int error;
|
||||
char *packname = NULL;
|
||||
pthread_t worker;
|
||||
struct dl_data data;
|
||||
|
||||
// Get the remote and connect to it
|
||||
// Figure out whether it's a named remote or a URL
|
||||
printf("Fetching %s\n", argv[1]);
|
||||
error = git_remote_new(&remote, repo, argv[1], NULL);
|
||||
if (error < GIT_SUCCESS)
|
||||
return error;
|
||||
|
||||
error = git_remote_connect(remote, GIT_DIR_FETCH);
|
||||
if (error < GIT_SUCCESS)
|
||||
return error;
|
||||
|
||||
// Download the packfile from the server. As we don't know its hash
|
||||
// yet, it will get a temporary filename
|
||||
error = git_remote_download(&packname, remote);
|
||||
if (error < GIT_SUCCESS)
|
||||
return error;
|
||||
|
||||
// No error and a NULL packname means no packfile was needed
|
||||
if (packname != NULL) {
|
||||
printf("The packname is %s\n", packname);
|
||||
|
||||
// Create a new instance indexer
|
||||
error = git_indexer_new(&idx, packname);
|
||||
if (error < GIT_SUCCESS)
|
||||
return error;
|
||||
|
||||
// This should be run in paralel, but it'd be too complicated for the example
|
||||
error = git_indexer_run(idx, &stats);
|
||||
if (error < GIT_SUCCESS)
|
||||
return error;
|
||||
|
||||
printf("Received %d objects\n", stats.total);
|
||||
|
||||
// Write the index file. The index will be stored with the
|
||||
// correct filename
|
||||
error = git_indexer_write(idx);
|
||||
if (error < GIT_SUCCESS)
|
||||
return error;
|
||||
|
||||
error = rename_packfile(packname, idx);
|
||||
if (error < GIT_SUCCESS)
|
||||
return error;
|
||||
if (git_remote_load(&remote, repo, argv[1]) < 0) {
|
||||
if (git_remote_new(&remote, repo, NULL, argv[1], NULL) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set up the information for the background worker thread
|
||||
data.remote = remote;
|
||||
data.bytes = &bytes;
|
||||
data.stats = &stats;
|
||||
data.ret = 0;
|
||||
data.finished = 0;
|
||||
memset(&stats, 0, sizeof(stats));
|
||||
|
||||
pthread_create(&worker, NULL, download, &data);
|
||||
|
||||
// Loop while the worker thread is still running. Here we show processed
|
||||
// and total objects in the pack and the amount of received
|
||||
// data. Most frontends will probably want to show a percentage and
|
||||
// the download rate.
|
||||
do {
|
||||
usleep(10000);
|
||||
printf("\rReceived %d/%d objects in %d bytes", stats.processed, stats.total, bytes);
|
||||
} while (!data.finished);
|
||||
printf("\rReceived %d/%d objects in %d bytes\n", stats.processed, stats.total, bytes);
|
||||
|
||||
// Disconnect the underlying connection to prevent from idling.
|
||||
git_remote_disconnect(remote);
|
||||
|
||||
// Update the references in the remote's namespace to point to the
|
||||
// right commits. This may be needed even if there was no packfile
|
||||
// to download, which can happen e.g. when the branches have been
|
||||
// changed but all the neede objects are available locally.
|
||||
error = git_remote_update_tips(remote);
|
||||
if (error < GIT_SUCCESS)
|
||||
return error;
|
||||
if (git_remote_update_tips(remote, update_cb) < 0)
|
||||
return -1;
|
||||
|
||||
free(packname);
|
||||
git_indexer_free(idx);
|
||||
git_remote_free(remote);
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
git_remote_free(remote);
|
||||
return -1;
|
||||
}
|
||||
|
@ -25,13 +25,17 @@ int run_command(git_cb fn, int argc, char **argv)
|
||||
// repository and pass it to the function.
|
||||
|
||||
error = git_repository_open(&repo, ".git");
|
||||
if (error < GIT_SUCCESS)
|
||||
if (error < 0)
|
||||
repo = NULL;
|
||||
|
||||
// Run the command. If something goes wrong, print the error message to stderr
|
||||
error = fn(repo, argc, argv);
|
||||
if (error < GIT_SUCCESS)
|
||||
fprintf(stderr, "Bad news:\n %s\n", git_lasterror());
|
||||
if (error < 0) {
|
||||
if (giterr_last() == NULL)
|
||||
fprintf(stderr, "Error without message");
|
||||
else
|
||||
fprintf(stderr, "Bad news:\n %s\n", giterr_last()->message);
|
||||
}
|
||||
|
||||
if(repo)
|
||||
git_repository_free(repo);
|
||||
|
@ -12,6 +12,61 @@ int index_cb(const git_indexer_stats *stats, void *data)
|
||||
}
|
||||
|
||||
int index_pack(git_repository *repo, int argc, char **argv)
|
||||
{
|
||||
git_indexer_stream *idx;
|
||||
git_indexer_stats stats = {0, 0};
|
||||
int error, fd;
|
||||
char hash[GIT_OID_HEXSZ + 1] = {0};
|
||||
ssize_t read_bytes;
|
||||
char buf[512];
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "I need a packfile\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (git_indexer_stream_new(&idx, ".git") < 0) {
|
||||
puts("bad idx");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((fd = open(argv[1], 0)) < 0) {
|
||||
perror("open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
do {
|
||||
read_bytes = read(fd, buf, sizeof(buf));
|
||||
if (read_bytes < 0)
|
||||
break;
|
||||
|
||||
if ((error = git_indexer_stream_add(idx, buf, read_bytes, &stats)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
printf("\rIndexing %d of %d", stats.processed, stats.total);
|
||||
} while (read_bytes > 0);
|
||||
|
||||
if (read_bytes < 0) {
|
||||
error = -1;
|
||||
perror("failed reading");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((error = git_indexer_stream_finalize(idx, &stats)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
printf("\rIndexing %d of %d\n", stats.processed, stats.total);
|
||||
|
||||
git_oid_fmt(hash, git_indexer_stream_hash(idx));
|
||||
puts(hash);
|
||||
|
||||
cleanup:
|
||||
close(fd);
|
||||
git_indexer_stream_free(idx);
|
||||
return error;
|
||||
}
|
||||
|
||||
int index_pack_old(git_repository *repo, int argc, char **argv)
|
||||
{
|
||||
git_indexer *indexer;
|
||||
git_indexer_stats stats;
|
||||
@ -25,13 +80,13 @@ int index_pack(git_repository *repo, int argc, char **argv)
|
||||
|
||||
// Create a new indexer
|
||||
error = git_indexer_new(&indexer, argv[1]);
|
||||
if (error < GIT_SUCCESS)
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
// Index the packfile. This function can take a very long time and
|
||||
// should be run in a worker thread.
|
||||
error = git_indexer_run(indexer, &stats);
|
||||
if (error < GIT_SUCCESS)
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
// Write the information out to an index file
|
||||
@ -43,5 +98,5 @@ int index_pack(git_repository *repo, int argc, char **argv)
|
||||
|
||||
git_indexer_free(indexer);
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ static int show_ref__cb(git_remote_head *head, void *payload)
|
||||
char oid[GIT_OID_HEXSZ + 1] = {0};
|
||||
git_oid_fmt(oid, &head->oid);
|
||||
printf("%s\t%s\n", oid, head->name);
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int use_unnamed(git_repository *repo, const char *url)
|
||||
@ -19,14 +19,14 @@ int use_unnamed(git_repository *repo, const char *url)
|
||||
|
||||
// Create an instance of a remote from the URL. The transport to use
|
||||
// is detected from the URL
|
||||
error = git_remote_new(&remote, repo, url, NULL);
|
||||
if (error < GIT_SUCCESS)
|
||||
error = git_remote_new(&remote, repo, NULL, url, NULL);
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
// When connecting, the underlying code needs to know wether we
|
||||
// want to push or fetch
|
||||
error = git_remote_connect(remote, GIT_DIR_FETCH);
|
||||
if (error < GIT_SUCCESS)
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
// With git_remote_ls we can retrieve the advertised heads
|
||||
@ -44,11 +44,11 @@ int use_remote(git_repository *repo, char *name)
|
||||
|
||||
// Find the remote by name
|
||||
error = git_remote_load(&remote, repo, name);
|
||||
if (error < GIT_SUCCESS)
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
error = git_remote_connect(remote, GIT_DIR_FETCH);
|
||||
if (error < GIT_SUCCESS)
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
error = git_remote_ls(remote, &show_ref__cb, NULL);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -13,7 +13,6 @@
|
||||
#include "git2/common.h"
|
||||
#include "git2/threads.h"
|
||||
#include "git2/errors.h"
|
||||
#include "git2/zlib.h"
|
||||
|
||||
#include "git2/types.h"
|
||||
|
||||
@ -23,6 +22,7 @@
|
||||
|
||||
#include "git2/repository.h"
|
||||
#include "git2/revwalk.h"
|
||||
#include "git2/merge.h"
|
||||
#include "git2/refs.h"
|
||||
#include "git2/reflog.h"
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
#include "git2/commit.h"
|
||||
#include "git2/tag.h"
|
||||
#include "git2/tree.h"
|
||||
#include "git2/diff.h"
|
||||
|
||||
#include "git2/index.h"
|
||||
#include "git2/config.h"
|
||||
@ -40,5 +41,7 @@
|
||||
#include "git2/net.h"
|
||||
#include "git2/status.h"
|
||||
#include "git2/indexer.h"
|
||||
#include "git2/submodule.h"
|
||||
#include "git2/notes.h"
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -19,42 +19,186 @@
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
#define GIT_ATTR_TRUE git_attr__true
|
||||
#define GIT_ATTR_FALSE git_attr__false
|
||||
#define GIT_ATTR_UNSPECIFIED NULL
|
||||
|
||||
GIT_EXTERN(const char *)git_attr__true;
|
||||
GIT_EXTERN(const char *)git_attr__false;
|
||||
|
||||
/**
|
||||
* GIT_ATTR_TRUE checks if an attribute is set on. In core git
|
||||
* parlance, this the value for "Set" attributes.
|
||||
*
|
||||
* For example, if the attribute file contains:
|
||||
*
|
||||
* *.c foo
|
||||
*
|
||||
* Then for file `xyz.c` looking up attribute "foo" gives a value for
|
||||
* which `GIT_ATTR_TRUE(value)` is true.
|
||||
*/
|
||||
#define GIT_ATTR_TRUE(attr) ((attr) == git_attr__true)
|
||||
|
||||
/**
|
||||
* Lookup attribute for path returning string caller must free
|
||||
* GIT_ATTR_FALSE checks if an attribute is set off. In core git
|
||||
* parlance, this is the value for attributes that are "Unset" (not to
|
||||
* be confused with values that a "Unspecified").
|
||||
*
|
||||
* For example, if the attribute file contains:
|
||||
*
|
||||
* *.h -foo
|
||||
*
|
||||
* Then for file `zyx.h` looking up attribute "foo" gives a value for
|
||||
* which `GIT_ATTR_FALSE(value)` is true.
|
||||
*/
|
||||
#define GIT_ATTR_FALSE(attr) ((attr) == git_attr__false)
|
||||
|
||||
/**
|
||||
* GIT_ATTR_UNSPECIFIED checks if an attribute is unspecified. This
|
||||
* may be due to the attribute not being mentioned at all or because
|
||||
* the attribute was explicitly set unspecified via the `!` operator.
|
||||
*
|
||||
* For example, if the attribute file contains:
|
||||
*
|
||||
* *.c foo
|
||||
* *.h -foo
|
||||
* onefile.c !foo
|
||||
*
|
||||
* Then for `onefile.c` looking up attribute "foo" yields a value with
|
||||
* `GIT_ATTR_UNSPECIFIED(value)` of true. Also, looking up "foo" on
|
||||
* file `onefile.rb` or looking up "bar" on any file will all give
|
||||
* `GIT_ATTR_UNSPECIFIED(value)` of true.
|
||||
*/
|
||||
#define GIT_ATTR_UNSPECIFIED(attr) (!(attr) || (attr) == git_attr__unset)
|
||||
|
||||
/**
|
||||
* GIT_ATTR_HAS_VALUE checks if an attribute is set to a value (as
|
||||
* opposied to TRUE, FALSE or UNSPECIFIED). This would be the case if
|
||||
* for a file with something like:
|
||||
*
|
||||
* *.txt eol=lf
|
||||
*
|
||||
* Given this, looking up "eol" for `onefile.txt` will give back the
|
||||
* string "lf" and `GIT_ATTR_SET_TO_VALUE(attr)` will return true.
|
||||
*/
|
||||
#define GIT_ATTR_HAS_VALUE(attr) \
|
||||
((attr) && (attr) != git_attr__unset && \
|
||||
(attr) != git_attr__true && (attr) != git_attr__false)
|
||||
|
||||
GIT_EXTERN(const char *) git_attr__true;
|
||||
GIT_EXTERN(const char *) git_attr__false;
|
||||
GIT_EXTERN(const char *) git_attr__unset;
|
||||
|
||||
/**
|
||||
* Check attribute flags: Reading values from index and working directory.
|
||||
*
|
||||
* When checking attributes, it is possible to check attribute files
|
||||
* in both the working directory (if there is one) and the index (if
|
||||
* there is one). You can explicitly choose where to check and in
|
||||
* which order using the following flags.
|
||||
*
|
||||
* Core git usually checks the working directory then the index,
|
||||
* except during a checkout when it checks the index first. It will
|
||||
* use index only for creating archives or for a bare repo (if an
|
||||
* index has been specified for the bare repo).
|
||||
*/
|
||||
#define GIT_ATTR_CHECK_FILE_THEN_INDEX 0
|
||||
#define GIT_ATTR_CHECK_INDEX_THEN_FILE 1
|
||||
#define GIT_ATTR_CHECK_INDEX_ONLY 2
|
||||
|
||||
/**
|
||||
* Check attribute flags: Using the system attributes file.
|
||||
*
|
||||
* Normally, attribute checks include looking in the /etc (or system
|
||||
* equivalent) directory for a `gitattributes` file. Passing this
|
||||
* flag will cause attribute checks to ignore that file.
|
||||
*/
|
||||
#define GIT_ATTR_CHECK_NO_SYSTEM (1 << 2)
|
||||
|
||||
/**
|
||||
* Look up the value of one git attribute for path.
|
||||
*
|
||||
* @param value_out Output of the value of the attribute. Use the GIT_ATTR_...
|
||||
* macros to test for TRUE, FALSE, UNSPECIFIED, etc. or just
|
||||
* use the string value for attributes set to a value. You
|
||||
* should NOT modify or free this value.
|
||||
* @param repo The repository containing the path.
|
||||
* @param flags A combination of GIT_ATTR_CHECK... flags.
|
||||
* @param path The path to check for attributes. Relative paths are
|
||||
* interpreted relative to the repo root. The file does
|
||||
* not have to exist, but if it does not, then it will be
|
||||
* treated as a plain file (not a directory).
|
||||
* @param name The name of the attribute to look up.
|
||||
*/
|
||||
GIT_EXTERN(int) git_attr_get(
|
||||
git_repository *repo, const char *path, const char *name,
|
||||
const char **value);
|
||||
const char **value_out,
|
||||
git_repository *repo,
|
||||
uint32_t flags,
|
||||
const char *path,
|
||||
const char *name);
|
||||
|
||||
/**
|
||||
* Lookup list of attributes for path, populating array of strings
|
||||
* Look up a list of git attributes for path.
|
||||
*
|
||||
* Use this if you have a known list of attributes that you want to
|
||||
* look up in a single call. This is somewhat more efficient than
|
||||
* calling `git_attr_get()` multiple times.
|
||||
*
|
||||
* For example, you might write:
|
||||
*
|
||||
* const char *attrs[] = { "crlf", "diff", "foo" };
|
||||
* const char **values[3];
|
||||
* git_attr_get_many(values, repo, 0, "my/fun/file.c", 3, attrs);
|
||||
*
|
||||
* Then you could loop through the 3 values to get the settings for
|
||||
* the three attributes you asked about.
|
||||
*
|
||||
* @param values An array of num_attr entries that will have string
|
||||
* pointers written into it for the values of the attributes.
|
||||
* You should not modify or free the values that are written
|
||||
* into this array (although of course, you should free the
|
||||
* array itself if you allocated it).
|
||||
* @param repo The repository containing the path.
|
||||
* @param flags A combination of GIT_ATTR_CHECK... flags.
|
||||
* @param path The path inside the repo to check attributes. This
|
||||
* does not have to exist, but if it does not, then
|
||||
* it will be treated as a plain file (i.e. not a directory).
|
||||
* @param num_attr The number of attributes being looked up
|
||||
* @param names An array of num_attr strings containing attribute names.
|
||||
*/
|
||||
GIT_EXTERN(int) git_attr_get_many(
|
||||
git_repository *repo, const char *path,
|
||||
size_t num_attr, const char **names,
|
||||
const char **values);
|
||||
const char **values_out,
|
||||
git_repository *repo,
|
||||
uint32_t flags,
|
||||
const char *path,
|
||||
size_t num_attr,
|
||||
const char **names);
|
||||
|
||||
/**
|
||||
* Perform an operation on each attribute of a path.
|
||||
* Loop over all the git attributes for a path.
|
||||
*
|
||||
* @param repo The repository containing the path.
|
||||
* @param flags A combination of GIT_ATTR_CHECK... flags.
|
||||
* @param path The path inside the repo to check attributes. This
|
||||
* does not have to exist, but if it does not, then
|
||||
* it will be treated as a plain file (i.e. not a directory).
|
||||
* @param callback The function that will be invoked on each attribute
|
||||
* and attribute value. The name parameter will be the name
|
||||
* of the attribute and the value will be the value it is
|
||||
* set to, including possibly NULL if the attribute is
|
||||
* explicitly set to UNSPECIFIED using the ! sign. This
|
||||
* will be invoked only once per attribute name, even if
|
||||
* there are multiple rules for a given file. The highest
|
||||
* priority rule will be used.
|
||||
* @param payload Passed on as extra parameter to callback function.
|
||||
*/
|
||||
GIT_EXTERN(int) git_attr_foreach(
|
||||
git_repository *repo, const char *path,
|
||||
git_repository *repo,
|
||||
uint32_t flags,
|
||||
const char *path,
|
||||
int (*callback)(const char *name, const char *value, void *payload),
|
||||
void *payload);
|
||||
|
||||
/**
|
||||
* Flush the gitattributes cache.
|
||||
*
|
||||
* Call this if you have reason to believe that the attributes files
|
||||
* on disk no longer match the cached contents of memory.
|
||||
* Call this if you have reason to believe that the attributes files on
|
||||
* disk no longer match the cached contents of memory. This will cause
|
||||
* the attributes files to be reloaded the next time that an attribute
|
||||
* access function is called.
|
||||
*/
|
||||
GIT_EXTERN(void) git_attr_cache_flush(
|
||||
git_repository *repo);
|
||||
@ -62,7 +206,7 @@ GIT_EXTERN(void) git_attr_cache_flush(
|
||||
/**
|
||||
* Add a macro definition.
|
||||
*
|
||||
* Macros will automatically be loaded from the top level .gitattributes
|
||||
* Macros will automatically be loaded from the top level `.gitattributes`
|
||||
* file of the repository (plus the build-in "binary" macro). This
|
||||
* function allows you to add others. For example, to add the default
|
||||
* macro, you would call:
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -27,7 +27,7 @@ GIT_BEGIN_DECL
|
||||
* @param blob pointer to the looked up blob
|
||||
* @param repo the repo to use when locating the blob.
|
||||
* @param id identity of the blob to locate.
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git_oid *id)
|
||||
{
|
||||
@ -44,7 +44,7 @@ GIT_INLINE(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git
|
||||
* @param repo the repo to use when locating the blob.
|
||||
* @param id identity of the blob to locate.
|
||||
* @param len the length of the short identifier
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, unsigned int len)
|
||||
{
|
||||
@ -99,10 +99,22 @@ GIT_EXTERN(size_t) git_blob_rawsize(git_blob *blob);
|
||||
* this repository cannot be bare
|
||||
* @param path file from which the blob will be created,
|
||||
* relative to the repository's working dir
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path);
|
||||
|
||||
/**
|
||||
* Read a file from the filesystem and write its content
|
||||
* to the Object Database as a loose blob
|
||||
*
|
||||
* @param oid return the id of the written blob
|
||||
* @param repo repository where the blob will be written.
|
||||
* this repository can be bare or not
|
||||
* @param path file from which the blob will be created
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *path);
|
||||
|
||||
|
||||
/**
|
||||
* Write an in-memory buffer to the ODB as a blob
|
||||
@ -111,7 +123,7 @@ GIT_EXTERN(int) git_blob_create_fromfile(git_oid *oid, git_repository *repo, con
|
||||
* @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 GIT_SUCCESS or an error code
|
||||
* @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);
|
||||
|
||||
|
@ -1,15 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* 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_branch_h__
|
||||
#define INCLUDE_branch_h__
|
||||
#ifndef INCLUDE_git_branch_h__
|
||||
#define INCLUDE_git_branch_h__
|
||||
|
||||
struct git_branch {
|
||||
char *remote; /* TODO: Make this a git_remote */
|
||||
char *merge;
|
||||
};
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
|
||||
/**
|
||||
* @file git2/branch.h
|
||||
* @brief Git branch parsing routines
|
||||
* @defgroup git_branch Git branch management
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* Create a new branch pointing at a target commit
|
||||
*
|
||||
* A new direct reference will be created pointing to
|
||||
* this target commit. If `force` is true and a reference
|
||||
* already exists with the given name, it'll be replaced.
|
||||
*
|
||||
* @param oid_out Pointer where to store the OID of the target commit.
|
||||
*
|
||||
* @param repo Repository where to store the branch.
|
||||
*
|
||||
* @param branch_name Name for the branch; this name is
|
||||
* validated for consistency. It should also not conflict with
|
||||
* an already existing branch name.
|
||||
*
|
||||
* @param target Object to which this branch should point. This object
|
||||
* must belong to the given `repo` and can either be a git_commit or a
|
||||
* git_tag. When a git_tag is being passed, it should be dereferencable
|
||||
* to a git_commit which oid will be used as the target of the branch.
|
||||
*
|
||||
* @param force Overwrite existing branch.
|
||||
*
|
||||
* @return 0 or an error code.
|
||||
* A proper reference is written in the refs/heads namespace
|
||||
* pointing to the provided target commit.
|
||||
*/
|
||||
GIT_EXTERN(int) git_branch_create(
|
||||
git_oid *oid_out,
|
||||
git_repository *repo,
|
||||
const char *branch_name,
|
||||
const git_object *target,
|
||||
int force);
|
||||
|
||||
/**
|
||||
* Delete an existing branch reference.
|
||||
*
|
||||
* @param repo Repository where lives the branch.
|
||||
*
|
||||
* @param branch_name Name of the branch to be deleted;
|
||||
* this name is validated for consistency.
|
||||
*
|
||||
* @param branch_type Type of the considered branch. This should
|
||||
* be valued with either GIT_BRANCH_LOCAL or GIT_BRANCH_REMOTE.
|
||||
*
|
||||
* @return 0 on success, GIT_ENOTFOUND if the branch
|
||||
* doesn't exist or an error code.
|
||||
*/
|
||||
GIT_EXTERN(int) git_branch_delete(
|
||||
git_repository *repo,
|
||||
const char *branch_name,
|
||||
git_branch_t branch_type);
|
||||
|
||||
/**
|
||||
* Fill a list with all the branches in the Repository
|
||||
*
|
||||
* The string array will be filled with the names of the
|
||||
* matching branches; these values are owned by the user and
|
||||
* should be free'd manually when no longer needed, using
|
||||
* `git_strarray_free`.
|
||||
*
|
||||
* @param branch_names Pointer to a git_strarray structure
|
||||
* where the branch names will be stored.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @return 0 or an error code.
|
||||
*/
|
||||
GIT_EXTERN(int) git_branch_list(
|
||||
git_strarray *branch_names,
|
||||
git_repository *repo,
|
||||
unsigned int list_flags);
|
||||
|
||||
/**
|
||||
* Move/rename an existing branch reference.
|
||||
*
|
||||
* @param repo Repository where lives the branch.
|
||||
*
|
||||
* @param old_branch_name Current name of the branch to be moved;
|
||||
* this name is validated for consistency.
|
||||
*
|
||||
* @param new_branch_name Target name of the branch once the move
|
||||
* is performed; this name is validated for consistency.
|
||||
*
|
||||
* @param force Overwrite existing branch.
|
||||
*
|
||||
* @return 0 on success, GIT_ENOTFOUND if the branch
|
||||
* doesn't exist or an error code.
|
||||
*/
|
||||
GIT_EXTERN(int) git_branch_move(
|
||||
git_repository *repo,
|
||||
const char *old_branch_name,
|
||||
const char *new_branch_name,
|
||||
int force);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -28,7 +28,7 @@ GIT_BEGIN_DECL
|
||||
* @param repo the repo to use when locating the commit.
|
||||
* @param id identity of the commit to locate. If the object is
|
||||
* an annotated tag it will be peeled back to the commit.
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_commit_lookup(git_commit **commit, git_repository *repo, const git_oid *id)
|
||||
{
|
||||
@ -46,7 +46,7 @@ GIT_INLINE(int) git_commit_lookup(git_commit **commit, git_repository *repo, con
|
||||
* @param id identity of the commit to locate. If the object is
|
||||
* an annotated tag it will be peeled back to the commit.
|
||||
* @param len the length of the short identifier
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_commit_lookup_prefix(git_commit **commit, git_repository *repo, const git_oid *id, unsigned len)
|
||||
{
|
||||
@ -135,7 +135,7 @@ GIT_EXTERN(const git_signature *) git_commit_author(git_commit *commit);
|
||||
*
|
||||
* @param tree_out pointer where to store the tree object
|
||||
* @param commit a previously loaded commit.
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_commit_tree(git_tree **tree_out, git_commit *commit);
|
||||
|
||||
@ -163,7 +163,7 @@ GIT_EXTERN(unsigned int) git_commit_parentcount(git_commit *commit);
|
||||
* @param parent Pointer where to store the parent commit
|
||||
* @param commit a previously loaded commit.
|
||||
* @param n the position of the parent (from 0 to `parentcount`)
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n);
|
||||
|
||||
@ -182,6 +182,9 @@ GIT_EXTERN(const git_oid *) git_commit_parent_oid(git_commit *commit, unsigned i
|
||||
* Create a new commit in the repository using `git_object`
|
||||
* instances as parameters.
|
||||
*
|
||||
* The message will be cleaned up from excess whitespace
|
||||
* it will be made sure that the last line ends with a '\n'.
|
||||
*
|
||||
* @param oid Pointer where to store the OID of the
|
||||
* newly created commit
|
||||
*
|
||||
@ -191,7 +194,8 @@ GIT_EXTERN(const git_oid *) git_commit_parent_oid(git_commit *commit, unsigned i
|
||||
* will be updated to point to this commit. If the reference
|
||||
* 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
|
||||
* make it point to this commit. If the reference doesn't
|
||||
* exist yet, it will be created.
|
||||
*
|
||||
* @param author Signature representing the author and the authory
|
||||
* time of this commit
|
||||
@ -217,7 +221,7 @@ GIT_EXTERN(const git_oid *) git_commit_parent_oid(git_commit *commit, unsigned i
|
||||
* array may be NULL if `parent_count` is 0 (root commit). All the
|
||||
* given commits must be owned by the `repo`.
|
||||
*
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
* The created commit will be written to the Object Database and
|
||||
* the given reference will be updated to point to it
|
||||
*/
|
||||
@ -237,6 +241,9 @@ GIT_EXTERN(int) git_commit_create(
|
||||
* Create a new commit in the repository using a variable
|
||||
* argument list.
|
||||
*
|
||||
* The message will be cleaned up from excess whitespace
|
||||
* it will be made sure that the last line ends with a '\n'.
|
||||
*
|
||||
* The 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
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -51,7 +51,7 @@
|
||||
# define GIT_FORMAT_PRINTF(a,b) /* empty */
|
||||
#endif
|
||||
|
||||
#if (defined(_WIN32) || defined(_WIN64)) && !defined(__CYGWIN__)
|
||||
#if (defined(_WIN32)) && !defined(__CYGWIN__)
|
||||
#define GIT_WIN32 1
|
||||
#endif
|
||||
|
||||
@ -77,7 +77,7 @@ GIT_BEGIN_DECL
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The maximum length of a git valid git path.
|
||||
* The maximum length of a valid git path.
|
||||
*/
|
||||
#define GIT_PATH_MAX 4096
|
||||
|
||||
@ -87,6 +87,7 @@ typedef struct {
|
||||
} git_strarray;
|
||||
|
||||
GIT_EXTERN(void) git_strarray_free(git_strarray *array);
|
||||
GIT_EXTERN(int) git_strarray_copy(git_strarray *tgt, const git_strarray *src);
|
||||
|
||||
/**
|
||||
* Return the version of the libgit2 library
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -29,12 +29,27 @@ struct git_config_file {
|
||||
/* Open means open the file/database and parse if necessary */
|
||||
int (*open)(struct git_config_file *);
|
||||
int (*get)(struct git_config_file *, const char *key, const char **value);
|
||||
int (*get_multivar)(struct git_config_file *, const char *key, const char *regexp, int (*fn)(const char *, void *), void *data);
|
||||
int (*set)(struct git_config_file *, const char *key, const char *value);
|
||||
int (*set_multivar)(git_config_file *cfg, const char *name, const char *regexp, const char *value);
|
||||
int (*del)(struct git_config_file *, const char *key);
|
||||
int (*foreach)(struct git_config_file *, int (*fn)(const char *, const char *, void *), void *data);
|
||||
void (*free)(struct git_config_file *);
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
GIT_CVAR_FALSE = 0,
|
||||
GIT_CVAR_TRUE = 1,
|
||||
GIT_CVAR_INT32,
|
||||
GIT_CVAR_STRING
|
||||
} git_cvar_t;
|
||||
|
||||
typedef struct {
|
||||
git_cvar_t cvar_type;
|
||||
const char *str_match;
|
||||
int map_value;
|
||||
} git_cvar_map;
|
||||
|
||||
/**
|
||||
* Locate the path to the global configuration file
|
||||
*
|
||||
@ -47,10 +62,10 @@ struct git_config_file {
|
||||
* global configuration file.
|
||||
*
|
||||
* @param global_config_path Buffer of GIT_PATH_MAX length to store the path
|
||||
* @return GIT_SUCCESS if a global configuration file has been
|
||||
* @return 0 if a global configuration file has been
|
||||
* found. Its path will be stored in `buffer`.
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_find_global(char *global_config_path);
|
||||
GIT_EXTERN(int) git_config_find_global(char *global_config_path, size_t length);
|
||||
|
||||
/**
|
||||
* Locate the path to the system configuration file
|
||||
@ -59,10 +74,10 @@ GIT_EXTERN(int) git_config_find_global(char *global_config_path);
|
||||
* %PROGRAMFILES%\Git\etc\gitconfig.
|
||||
|
||||
* @param system_config_path Buffer of GIT_PATH_MAX length to store the path
|
||||
* @return GIT_SUCCESS if a system configuration file has been
|
||||
* @return 0 if a system configuration file has been
|
||||
* found. Its path will be stored in `buffer`.
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_find_system(char *system_config_path);
|
||||
GIT_EXTERN(int) git_config_find_system(char *system_config_path, size_t length);
|
||||
|
||||
/**
|
||||
* Open the global configuration file
|
||||
@ -71,7 +86,7 @@ GIT_EXTERN(int) git_config_find_system(char *system_config_path);
|
||||
* and opens the located file, if it exists.
|
||||
*
|
||||
* @param out Pointer to store the config instance
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_open_global(git_config **out);
|
||||
|
||||
@ -95,7 +110,7 @@ GIT_EXTERN(int) git_config_file__ondisk(struct git_config_file **out, const char
|
||||
* can do anything with it.
|
||||
*
|
||||
* @param out pointer to the new configuration
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_new(git_config **out);
|
||||
|
||||
@ -112,7 +127,7 @@ GIT_EXTERN(int) git_config_new(git_config **out);
|
||||
* @param cfg the configuration to add the file to
|
||||
* @param file the configuration file (backend) to add
|
||||
* @param priority the priority the backend should have
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_add_file(git_config *cfg, git_config_file *file, int priority);
|
||||
|
||||
@ -133,7 +148,7 @@ GIT_EXTERN(int) git_config_add_file(git_config *cfg, git_config_file *file, int
|
||||
* @param cfg the configuration to add the file to
|
||||
* @param path path to the configuration file (backend) to add
|
||||
* @param priority the priority the backend should have
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_add_file_ondisk(git_config *cfg, const char *path, int priority);
|
||||
|
||||
@ -148,7 +163,7 @@ GIT_EXTERN(int) git_config_add_file_ondisk(git_config *cfg, const char *path, in
|
||||
*
|
||||
* @param cfg The configuration instance to create
|
||||
* @param path Path to the on-disk file to open
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_open_ondisk(git_config **cfg, const char *path);
|
||||
|
||||
@ -162,22 +177,22 @@ GIT_EXTERN(void) git_config_free(git_config *cfg);
|
||||
/**
|
||||
* Get the value of an integer config variable.
|
||||
*
|
||||
* @param out pointer to the variable where the value should be stored
|
||||
* @param cfg where to look for the variable
|
||||
* @param name the variable's name
|
||||
* @param out pointer to the variable where the value should be stored
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_get_int32(git_config *cfg, const char *name, int32_t *out);
|
||||
GIT_EXTERN(int) git_config_get_int32(int32_t *out, git_config *cfg, const char *name);
|
||||
|
||||
/**
|
||||
* Get the value of a long integer config variable.
|
||||
*
|
||||
* @param out pointer to the variable where the value should be stored
|
||||
* @param cfg where to look for the variable
|
||||
* @param name the variable's name
|
||||
* @param out pointer to the variable where the value should be stored
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_get_int64(git_config *cfg, const char *name, int64_t *out);
|
||||
GIT_EXTERN(int) git_config_get_int64(int64_t *out, git_config *cfg, const char *name);
|
||||
|
||||
/**
|
||||
* Get the value of a boolean config variable.
|
||||
@ -185,12 +200,12 @@ GIT_EXTERN(int) git_config_get_int64(git_config *cfg, const char *name, int64_t
|
||||
* This function uses the usual C convention of 0 being false and
|
||||
* anything else true.
|
||||
*
|
||||
* @param out pointer to the variable where the value should be stored
|
||||
* @param cfg where to look for the variable
|
||||
* @param name the variable's name
|
||||
* @param out pointer to the variable where the value should be stored
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_get_bool(git_config *cfg, const char *name, int *out);
|
||||
GIT_EXTERN(int) git_config_get_bool(int *out, git_config *cfg, const char *name);
|
||||
|
||||
/**
|
||||
* Get the value of a string config variable.
|
||||
@ -198,12 +213,26 @@ GIT_EXTERN(int) git_config_get_bool(git_config *cfg, const char *name, int *out)
|
||||
* The string is owned by the variable and should not be freed by the
|
||||
* user.
|
||||
*
|
||||
* @param out pointer to the variable's value
|
||||
* @param cfg where to look for the variable
|
||||
* @param name the variable's name
|
||||
* @param out pointer to the variable's value
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_get_string(git_config *cfg, const char *name, const char **out);
|
||||
GIT_EXTERN(int) git_config_get_string(const char **out, git_config *cfg, const char *name);
|
||||
|
||||
/**
|
||||
* Get each value of a multivar.
|
||||
*
|
||||
* The callback will be called on each variable found
|
||||
*
|
||||
* @param cfg where to look for the variable
|
||||
* @param name the variable's name
|
||||
* @param regexp regular expression to filter which variables we're
|
||||
* interested in. Use NULL to indicate all
|
||||
* @param fn the function to be called on each value of the variable
|
||||
* @param data opaque pointer to pass to the callback
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_get_multivar(git_config *cfg, const char *name, const char *regexp, int (*fn)(const char *, void *), void *data);
|
||||
|
||||
/**
|
||||
* Set the value of an integer config variable.
|
||||
@ -211,7 +240,7 @@ GIT_EXTERN(int) git_config_get_string(git_config *cfg, const char *name, const c
|
||||
* @param cfg where to look for the variable
|
||||
* @param name the variable's name
|
||||
* @param value Integer value for the variable
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_set_int32(git_config *cfg, const char *name, int32_t value);
|
||||
|
||||
@ -221,7 +250,7 @@ GIT_EXTERN(int) git_config_set_int32(git_config *cfg, const char *name, int32_t
|
||||
* @param cfg where to look for the variable
|
||||
* @param name the variable's name
|
||||
* @param value Long integer value for the variable
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_set_int64(git_config *cfg, const char *name, int64_t value);
|
||||
|
||||
@ -231,7 +260,7 @@ GIT_EXTERN(int) git_config_set_int64(git_config *cfg, const char *name, int64_t
|
||||
* @param cfg where to look for the variable
|
||||
* @param name the variable's name
|
||||
* @param value the value to store
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_set_bool(git_config *cfg, const char *name, int value);
|
||||
|
||||
@ -244,10 +273,21 @@ GIT_EXTERN(int) git_config_set_bool(git_config *cfg, const char *name, int value
|
||||
* @param cfg where to look for the variable
|
||||
* @param name the variable's name
|
||||
* @param value the string to store.
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_set_string(git_config *cfg, const char *name, const char *value);
|
||||
|
||||
|
||||
/**
|
||||
* Set a multivar
|
||||
*
|
||||
* @param cfg where to look for the variable
|
||||
* @param name the variable's name
|
||||
* @param regexp a regular expression to indicate which values to replace
|
||||
* @param value the new value.
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value);
|
||||
|
||||
/**
|
||||
* Delete a config variable
|
||||
*
|
||||
@ -267,13 +307,50 @@ GIT_EXTERN(int) git_config_delete(git_config *cfg, const char *name);
|
||||
* @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 GIT_SUCCESS or the return value of the callback which didn't return 0
|
||||
* @return 0 or the return value of the callback which didn't return 0
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_foreach(
|
||||
git_config *cfg,
|
||||
int (*callback)(const char *var_name, const char *value, void *payload),
|
||||
void *payload);
|
||||
|
||||
|
||||
/**
|
||||
* Query the value of a config variable and return it mapped to
|
||||
* an integer constant.
|
||||
*
|
||||
* This is a helper method to easily map different possible values
|
||||
* to a variable to integer constants that easily identify them.
|
||||
*
|
||||
* A mapping array looks as follows:
|
||||
*
|
||||
* git_cvar_map autocrlf_mapping[3] = {
|
||||
* {GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE},
|
||||
* {GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE},
|
||||
* {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT},
|
||||
* {GIT_CVAR_STRING, "default", GIT_AUTO_CRLF_DEFAULT}};
|
||||
*
|
||||
* On any "false" value for the variable (e.g. "false", "FALSE", "no"), the
|
||||
* mapping will store `GIT_AUTO_CRLF_FALSE` in the `out` parameter.
|
||||
*
|
||||
* The same thing applies for any "true" value such as "true", "yes" or "1", storing
|
||||
* the `GIT_AUTO_CRLF_TRUE` variable.
|
||||
*
|
||||
* Otherwise, if the value matches the string "input" (with case insensitive comparison),
|
||||
* the given constant will be stored in `out`, and likewise for "default".
|
||||
*
|
||||
* If not a single match can be made to store in `out`, an error code will be
|
||||
* returned.
|
||||
*
|
||||
* @param out place to store the result of the mapping
|
||||
* @param cfg config file to get the variables from
|
||||
* @param name name of the config variable to lookup
|
||||
* @param maps array of `git_cvar_map` objects specifying the possible mappings
|
||||
* @param map_n number of mapping objects in `maps`
|
||||
* @return 0 on success, error code otherwise
|
||||
*/
|
||||
GIT_EXTERN(int) git_config_get_mapped(int *out, git_config *cfg, const char *name, git_cvar_map *maps, size_t map_n);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
362
include/git2/diff.h
Normal file
362
include/git2/diff.h
Normal file
@ -0,0 +1,362 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* 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_diff_h__
|
||||
#define INCLUDE_git_diff_h__
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
#include "oid.h"
|
||||
#include "tree.h"
|
||||
#include "refs.h"
|
||||
|
||||
/**
|
||||
* @file git2/diff.h
|
||||
* @brief Git tree and file differencing routines.
|
||||
*
|
||||
* Calculating diffs is generally done in two phases: building a diff list
|
||||
* then traversing the diff list. This makes is easier to share logic
|
||||
* across the various types of diffs (tree vs tree, workdir vs index, etc.),
|
||||
* and also allows you to insert optional diff list post-processing phases,
|
||||
* such as rename detected, in between the steps. When you are done with a
|
||||
* diff list object, it must be freed.
|
||||
*
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
enum {
|
||||
GIT_DIFF_NORMAL = 0,
|
||||
GIT_DIFF_REVERSE = (1 << 0),
|
||||
GIT_DIFF_FORCE_TEXT = (1 << 1),
|
||||
GIT_DIFF_IGNORE_WHITESPACE = (1 << 2),
|
||||
GIT_DIFF_IGNORE_WHITESPACE_CHANGE = (1 << 3),
|
||||
GIT_DIFF_IGNORE_WHITESPACE_EOL = (1 << 4),
|
||||
GIT_DIFF_IGNORE_SUBMODULES = (1 << 5),
|
||||
GIT_DIFF_PATIENCE = (1 << 6),
|
||||
GIT_DIFF_INCLUDE_IGNORED = (1 << 7),
|
||||
GIT_DIFF_INCLUDE_UNTRACKED = (1 << 8),
|
||||
GIT_DIFF_INCLUDE_UNMODIFIED = (1 << 9),
|
||||
GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1 << 10),
|
||||
};
|
||||
|
||||
/**
|
||||
* Structure describing options about how the diff should be executed.
|
||||
*
|
||||
* Setting all values of the structure to zero will yield the default
|
||||
* values. Similarly, passing NULL for the options structure will
|
||||
* give the defaults. The default values are marked below.
|
||||
*
|
||||
* @todo Most of the parameters here are not actually supported at this time.
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t flags; /**< defaults to GIT_DIFF_NORMAL */
|
||||
uint16_t context_lines; /**< defaults to 3 */
|
||||
uint16_t interhunk_lines; /**< defaults to 3 */
|
||||
char *old_prefix; /**< defaults to "a" */
|
||||
char *new_prefix; /**< defaults to "b" */
|
||||
git_strarray pathspec; /**< defaults to show all paths */
|
||||
} git_diff_options;
|
||||
|
||||
/**
|
||||
* The diff list object that contains all individual file deltas.
|
||||
*/
|
||||
typedef struct git_diff_list git_diff_list;
|
||||
|
||||
enum {
|
||||
GIT_DIFF_FILE_VALID_OID = (1 << 0),
|
||||
GIT_DIFF_FILE_FREE_PATH = (1 << 1),
|
||||
GIT_DIFF_FILE_BINARY = (1 << 2),
|
||||
GIT_DIFF_FILE_NOT_BINARY = (1 << 3),
|
||||
GIT_DIFF_FILE_FREE_DATA = (1 << 4),
|
||||
GIT_DIFF_FILE_UNMAP_DATA = (1 << 5)
|
||||
};
|
||||
|
||||
/**
|
||||
* What type of change is described by a git_diff_delta?
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_DELTA_UNMODIFIED = 0,
|
||||
GIT_DELTA_ADDED = 1,
|
||||
GIT_DELTA_DELETED = 2,
|
||||
GIT_DELTA_MODIFIED = 3,
|
||||
GIT_DELTA_RENAMED = 4,
|
||||
GIT_DELTA_COPIED = 5,
|
||||
GIT_DELTA_IGNORED = 6,
|
||||
GIT_DELTA_UNTRACKED = 7
|
||||
} git_delta_t;
|
||||
|
||||
/**
|
||||
* Description of one side of a diff.
|
||||
*/
|
||||
typedef struct {
|
||||
git_oid oid;
|
||||
char *path;
|
||||
uint16_t mode;
|
||||
git_off_t size;
|
||||
unsigned int flags;
|
||||
} git_diff_file;
|
||||
|
||||
/**
|
||||
* Description of changes to one file.
|
||||
*
|
||||
* When iterating over a diff list object, this will generally be passed to
|
||||
* most callback functions and you can use the contents to understand
|
||||
* exactly what has changed.
|
||||
*
|
||||
* Under some circumstances, not all fields will be filled in, but the code
|
||||
* generally tries to fill in as much as possible. One example is that the
|
||||
* "binary" field will not actually look at file contents if you do not
|
||||
* pass in hunk and/or line callbacks to the diff foreach iteration function.
|
||||
* It will just use the git attributes for those files.
|
||||
*/
|
||||
typedef struct {
|
||||
git_diff_file old_file;
|
||||
git_diff_file new_file;
|
||||
git_delta_t status;
|
||||
unsigned int similarity; /**< for RENAMED and COPIED, value 0-100 */
|
||||
int binary;
|
||||
} git_diff_delta;
|
||||
|
||||
/**
|
||||
* When iterating over a diff, callback that will be made per file.
|
||||
*/
|
||||
typedef int (*git_diff_file_fn)(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
float progress);
|
||||
|
||||
/**
|
||||
* Structure describing a hunk of a diff.
|
||||
*/
|
||||
typedef struct {
|
||||
int old_start;
|
||||
int old_lines;
|
||||
int new_start;
|
||||
int new_lines;
|
||||
} git_diff_range;
|
||||
|
||||
/**
|
||||
* When iterating over a diff, callback that will be made per hunk.
|
||||
*/
|
||||
typedef int (*git_diff_hunk_fn)(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
git_diff_range *range,
|
||||
const char *header,
|
||||
size_t header_len);
|
||||
|
||||
/**
|
||||
* Line origin constants.
|
||||
*
|
||||
* These values describe where a line came from and will be passed to
|
||||
* the git_diff_data_fn when iterating over a diff. There are some
|
||||
* special origin contants at the end that are used for the text
|
||||
* output callbacks to demarcate lines that are actually part of
|
||||
* the file or hunk headers.
|
||||
*/
|
||||
enum {
|
||||
/* these values will be sent to `git_diff_data_fn` along with the line */
|
||||
GIT_DIFF_LINE_CONTEXT = ' ',
|
||||
GIT_DIFF_LINE_ADDITION = '+',
|
||||
GIT_DIFF_LINE_DELETION = '-',
|
||||
GIT_DIFF_LINE_ADD_EOFNL = '\n', /**< LF was added at end of file */
|
||||
GIT_DIFF_LINE_DEL_EOFNL = '\0', /**< LF was removed at end of file */
|
||||
/* these values will only be sent to a `git_diff_data_fn` when the content
|
||||
* of a diff is being formatted (eg. through git_diff_print_patch() or
|
||||
* git_diff_print_compact(), for instance).
|
||||
*/
|
||||
GIT_DIFF_LINE_FILE_HDR = 'F',
|
||||
GIT_DIFF_LINE_HUNK_HDR = 'H',
|
||||
GIT_DIFF_LINE_BINARY = 'B'
|
||||
};
|
||||
|
||||
/**
|
||||
* When iterating over a diff, callback that will be made per text diff
|
||||
* line. In this context, the provided range will be NULL.
|
||||
*
|
||||
* When printing a diff, callback that will be made to output each line
|
||||
* of text. This uses some extra GIT_DIFF_LINE_... constants for output
|
||||
* of lines of file and hunk headers.
|
||||
*/
|
||||
typedef int (*git_diff_data_fn)(
|
||||
void *cb_data,
|
||||
git_diff_delta *delta,
|
||||
git_diff_range *range,
|
||||
char line_origin, /**< GIT_DIFF_LINE_... value from above */
|
||||
const char *content,
|
||||
size_t content_len);
|
||||
|
||||
/** @name Diff List Generator Functions
|
||||
*
|
||||
* These are the functions you would use to create (or destroy) a
|
||||
* git_diff_list from various objects in a repository.
|
||||
*/
|
||||
/**@{*/
|
||||
|
||||
/**
|
||||
* Deallocate a diff list.
|
||||
*/
|
||||
GIT_EXTERN(void) git_diff_list_free(git_diff_list *diff);
|
||||
|
||||
/**
|
||||
* Compute a difference between two tree objects.
|
||||
*
|
||||
* @param repo The repository containing the trees.
|
||||
* @param opts Structure with options to influence diff or NULL for defaults.
|
||||
* @param old_tree A git_tree object to diff from.
|
||||
* @param new_tree A git_tree object to diff to.
|
||||
* @param diff A pointer to a git_diff_list pointer that will be allocated.
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_tree_to_tree(
|
||||
git_repository *repo,
|
||||
const git_diff_options *opts, /**< can be NULL for defaults */
|
||||
git_tree *old_tree,
|
||||
git_tree *new_tree,
|
||||
git_diff_list **diff);
|
||||
|
||||
/**
|
||||
* Compute a difference between a tree and the index.
|
||||
*
|
||||
* @param repo The repository containing the tree and index.
|
||||
* @param opts Structure with options to influence diff or NULL for defaults.
|
||||
* @param old_tree A git_tree object to diff from.
|
||||
* @param diff A pointer to a git_diff_list pointer that will be allocated.
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_index_to_tree(
|
||||
git_repository *repo,
|
||||
const git_diff_options *opts, /**< can be NULL for defaults */
|
||||
git_tree *old_tree,
|
||||
git_diff_list **diff);
|
||||
|
||||
/**
|
||||
* Compute a difference between the working directory and the index.
|
||||
*
|
||||
* @param repo The repository.
|
||||
* @param opts Structure with options to influence diff or NULL for defaults.
|
||||
* @param diff A pointer to a git_diff_list pointer that will be allocated.
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_workdir_to_index(
|
||||
git_repository *repo,
|
||||
const git_diff_options *opts, /**< can be NULL for defaults */
|
||||
git_diff_list **diff);
|
||||
|
||||
/**
|
||||
* Compute a difference between the working directory and a tree.
|
||||
*
|
||||
* This returns strictly the differences between the tree and the
|
||||
* files contained in the working directory, regardless of the state
|
||||
* of files in the index. There is no direct equivalent in C git.
|
||||
*
|
||||
* This is *NOT* the same as 'git diff HEAD' or 'git diff <SHA>'. Those
|
||||
* commands diff the tree, the index, and the workdir. To emulate those
|
||||
* functions, call `git_diff_index_to_tree` and `git_diff_workdir_to_index`,
|
||||
* then call `git_diff_merge` on the results.
|
||||
*
|
||||
* @param repo The repository containing the tree.
|
||||
* @param opts Structure with options to influence diff or NULL for defaults.
|
||||
* @param old_tree A git_tree object to diff from.
|
||||
* @param diff A pointer to a git_diff_list pointer that will be allocated.
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_workdir_to_tree(
|
||||
git_repository *repo,
|
||||
const git_diff_options *opts, /**< can be NULL for defaults */
|
||||
git_tree *old_tree,
|
||||
git_diff_list **diff);
|
||||
|
||||
/**
|
||||
* Merge one diff list into another.
|
||||
*
|
||||
* This merges items from the "from" list into the "onto" list. The
|
||||
* resulting diff list will have all items that appear in either list.
|
||||
* If an item appears in both lists, then it will be "merged" to appear
|
||||
* as if the old version was from the "onto" list and the new version
|
||||
* is from the "from" list (with the exception that if the item has a
|
||||
* pending DELETE in the middle, then it will show as deleted).
|
||||
*
|
||||
* @param onto Diff to merge into.
|
||||
* @param from Diff to merge.
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_merge(
|
||||
git_diff_list *onto,
|
||||
const git_diff_list *from);
|
||||
|
||||
/**@}*/
|
||||
|
||||
|
||||
/** @name Diff List Processor Functions
|
||||
*
|
||||
* These are the functions you apply to a diff list to process it
|
||||
* or read it in some way.
|
||||
*/
|
||||
/**@{*/
|
||||
|
||||
/**
|
||||
* Iterate over a diff list issuing callbacks.
|
||||
*
|
||||
* If the hunk and/or line callbacks are not NULL, then this will calculate
|
||||
* text diffs for all files it thinks are not binary. If those are both
|
||||
* NULL, then this will not bother with the text diffs, so it can be
|
||||
* efficient.
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_foreach(
|
||||
git_diff_list *diff,
|
||||
void *cb_data,
|
||||
git_diff_file_fn file_cb,
|
||||
git_diff_hunk_fn hunk_cb,
|
||||
git_diff_data_fn line_cb);
|
||||
|
||||
/**
|
||||
* Iterate over a diff generating text output like "git diff --name-status".
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_print_compact(
|
||||
git_diff_list *diff,
|
||||
void *cb_data,
|
||||
git_diff_data_fn print_cb);
|
||||
|
||||
/**
|
||||
* Iterate over a diff generating text output like "git diff".
|
||||
*
|
||||
* This is a super easy way to generate a patch from a diff.
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_print_patch(
|
||||
git_diff_list *diff,
|
||||
void *cb_data,
|
||||
git_diff_data_fn print_cb);
|
||||
|
||||
/**@}*/
|
||||
|
||||
|
||||
/*
|
||||
* Misc
|
||||
*/
|
||||
|
||||
/**
|
||||
* Directly run a text diff on two blobs.
|
||||
*
|
||||
* Compared to a file, a blob lacks some contextual information. As such, the
|
||||
* `git_diff_file` parameters of the callbacks will be filled accordingly to the following:
|
||||
* `mode` will be set to 0, `path` will be set to NULL. When dealing with a NULL blob, `oid`
|
||||
* will be set to 0.
|
||||
*
|
||||
* When at least one of the blobs being dealt with is binary, the `git_diff_delta` binary
|
||||
* attribute will be set to 1 and no call to the hunk_cb nor line_cb will be made.
|
||||
*/
|
||||
GIT_EXTERN(int) git_diff_blobs(
|
||||
git_blob *old_blob,
|
||||
git_blob *new_blob,
|
||||
git_diff_options *options,
|
||||
void *cb_data,
|
||||
git_diff_file_fn file_cb,
|
||||
git_diff_hunk_fn hunk_cb,
|
||||
git_diff_data_fn line_cb);
|
||||
|
||||
GIT_END_DECL
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -17,127 +17,91 @@
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
typedef enum {
|
||||
#ifdef GIT_OLD_ERRORS
|
||||
enum {
|
||||
GIT_SUCCESS = 0,
|
||||
GIT_ERROR = -1,
|
||||
|
||||
/** Input was not a properly formatted Git object id. */
|
||||
GIT_ENOTOID = -2,
|
||||
|
||||
/** Input does not exist in the scope searched. */
|
||||
GIT_ENOTFOUND = -3,
|
||||
|
||||
/** Not enough space available. */
|
||||
GIT_ENOMEM = -4,
|
||||
|
||||
/** Consult the OS error information. */
|
||||
GIT_EOSERR = -5,
|
||||
|
||||
/** The specified object is of invalid type */
|
||||
GIT_EOBJTYPE = -6,
|
||||
|
||||
/** The specified repository is invalid */
|
||||
GIT_ENOTAREPO = -7,
|
||||
|
||||
/** The object type is invalid or doesn't match */
|
||||
GIT_EINVALIDTYPE = -8,
|
||||
|
||||
/** The object cannot be written because it's missing internal data */
|
||||
GIT_EMISSINGOBJDATA = -9,
|
||||
|
||||
/** The packfile for the ODB is corrupted */
|
||||
GIT_EPACKCORRUPTED = -10,
|
||||
|
||||
/** Failed to acquire or release a file lock */
|
||||
GIT_EFLOCKFAIL = -11,
|
||||
|
||||
/** The Z library failed to inflate/deflate an object's data */
|
||||
GIT_EZLIB = -12,
|
||||
|
||||
/** The queried object is currently busy */
|
||||
GIT_EBUSY = -13,
|
||||
|
||||
/** The index file is not backed up by an existing repository */
|
||||
GIT_EBAREINDEX = -14,
|
||||
|
||||
/** The name of the reference is not valid */
|
||||
GIT_EINVALIDREFNAME = -15,
|
||||
|
||||
/** The specified reference has its data corrupted */
|
||||
GIT_EREFCORRUPTED = -16,
|
||||
|
||||
/** The specified symbolic reference is too deeply nested */
|
||||
GIT_ETOONESTEDSYMREF = -17,
|
||||
|
||||
/** The pack-refs file is either corrupted or its format is not currently supported */
|
||||
GIT_EPACKEDREFSCORRUPTED = -18,
|
||||
|
||||
/** The path is invalid */
|
||||
GIT_EINVALIDPATH = -19,
|
||||
|
||||
/** The revision walker is empty; there are no more commits left to iterate */
|
||||
GIT_EREVWALKOVER = -20,
|
||||
|
||||
/** The state of the reference is not valid */
|
||||
GIT_EINVALIDREFSTATE = -21,
|
||||
|
||||
/** This feature has not been implemented yet */
|
||||
GIT_ENOTIMPLEMENTED = -22,
|
||||
|
||||
/** A reference with this name already exists */
|
||||
GIT_EEXISTS = -23,
|
||||
|
||||
/** The given integer literal is too large to be parsed */
|
||||
GIT_EOVERFLOW = -24,
|
||||
|
||||
/** The given literal is not a valid number */
|
||||
GIT_ENOTNUM = -25,
|
||||
|
||||
/** Streaming error */
|
||||
GIT_ESTREAM = -26,
|
||||
|
||||
/** invalid arguments to function */
|
||||
GIT_EINVALIDARGS = -27,
|
||||
|
||||
/** The specified object has its data corrupted */
|
||||
GIT_EOBJCORRUPTED = -28,
|
||||
|
||||
/** The given short oid is ambiguous */
|
||||
GIT_EAMBIGUOUSOIDPREFIX = -29,
|
||||
|
||||
/** Skip and passthrough the given ODB backend */
|
||||
GIT_EAMBIGUOUS = -29,
|
||||
GIT_EPASSTHROUGH = -30,
|
||||
|
||||
/** The path pattern and string did not match */
|
||||
GIT_ENOMATCH = -31,
|
||||
|
||||
/** The buffer is too short to satisfy the request */
|
||||
GIT_ESHORTBUFFER = -32,
|
||||
};
|
||||
#endif
|
||||
|
||||
/** Generic return codes */
|
||||
enum {
|
||||
GIT_OK = 0,
|
||||
GIT_ERROR = -1,
|
||||
GIT_ENOTFOUND = -3,
|
||||
GIT_EEXISTS = -4,
|
||||
GIT_EAMBIGUOUS = -5,
|
||||
GIT_EBUFS = -6,
|
||||
|
||||
GIT_PASSTHROUGH = -30,
|
||||
GIT_REVWALKOVER = -31,
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
char *message;
|
||||
int klass;
|
||||
} git_error;
|
||||
|
||||
/**
|
||||
* Return a detailed error string with the latest error
|
||||
* that occurred in the library.
|
||||
* @return a string explaining the error
|
||||
*/
|
||||
GIT_EXTERN(const char *) git_lasterror(void);
|
||||
typedef enum {
|
||||
GITERR_NOMEMORY,
|
||||
GITERR_OS,
|
||||
GITERR_INVALID,
|
||||
GITERR_REFERENCE,
|
||||
GITERR_ZLIB,
|
||||
GITERR_REPOSITORY,
|
||||
GITERR_CONFIG,
|
||||
GITERR_REGEX,
|
||||
GITERR_ODB,
|
||||
GITERR_INDEX,
|
||||
GITERR_OBJECT,
|
||||
GITERR_NET,
|
||||
GITERR_TAG,
|
||||
GITERR_TREE,
|
||||
GITERR_INDEXER,
|
||||
} git_error_t;
|
||||
|
||||
/**
|
||||
* strerror() for the Git library
|
||||
* Return the last `git_error` object that was generated for the
|
||||
* current thread or NULL if no error has occurred.
|
||||
*
|
||||
* Get a string description for a given error code.
|
||||
* NOTE: This method will be eventually deprecated in favor
|
||||
* of the new `git_lasterror`.
|
||||
*
|
||||
* @param num The error code to explain
|
||||
* @return a string explaining the error code
|
||||
* @return A git_error object.
|
||||
*/
|
||||
GIT_EXTERN(const char *) git_strerror(int num);
|
||||
GIT_EXTERN(const git_error *) giterr_last(void);
|
||||
|
||||
/**
|
||||
* Clear the latest library error
|
||||
* Clear the last library error that occurred for this thread.
|
||||
*/
|
||||
GIT_EXTERN(void) git_clearerror(void);
|
||||
GIT_EXTERN(void) giterr_clear(void);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -106,7 +106,7 @@ typedef struct git_index_entry_unmerged {
|
||||
*
|
||||
* @param index the pointer for the new index
|
||||
* @param index_path the path to the index file in disk
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_open(git_index **index, const char *index_path);
|
||||
|
||||
@ -131,7 +131,7 @@ GIT_EXTERN(void) git_index_free(git_index *index);
|
||||
* by reading from the hard disk.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_read(git_index *index);
|
||||
|
||||
@ -140,7 +140,7 @@ GIT_EXTERN(int) git_index_read(git_index *index);
|
||||
* using an atomic file lock.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_write(git_index *index);
|
||||
|
||||
@ -176,7 +176,7 @@ GIT_EXTERN(void) git_index_uniq(git_index *index);
|
||||
* @param index an existing index object
|
||||
* @param path filename to add
|
||||
* @param stage stage for the entry
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_add(git_index *index, const char *path, int stage);
|
||||
|
||||
@ -188,7 +188,7 @@ GIT_EXTERN(int) git_index_add(git_index *index, const char *path, int stage);
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @param source_entry new entry object
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_add2(git_index *index, const git_index_entry *source_entry);
|
||||
|
||||
@ -207,7 +207,7 @@ GIT_EXTERN(int) git_index_add2(git_index *index, const git_index_entry *source_e
|
||||
* @param index an existing index object
|
||||
* @param path filename to add
|
||||
* @param stage stage for the entry
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_append(git_index *index, const char *path, int stage);
|
||||
|
||||
@ -224,7 +224,7 @@ GIT_EXTERN(int) git_index_append(git_index *index, const char *path, int stage);
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @param source_entry new entry object
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_append2(git_index *index, const git_index_entry *source_entry);
|
||||
|
||||
@ -233,7 +233,7 @@ GIT_EXTERN(int) git_index_append2(git_index *index, const git_index_entry *sourc
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @param position position of the entry to remove
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_remove(git_index *index, int position);
|
||||
|
||||
@ -312,7 +312,7 @@ GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry);
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @param tree tree to read
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_index_read_tree(git_index *index, git_tree *tree);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -23,6 +23,51 @@ typedef struct git_indexer_stats {
|
||||
|
||||
|
||||
typedef struct git_indexer git_indexer;
|
||||
typedef struct git_indexer_stream git_indexer_stream;
|
||||
|
||||
/**
|
||||
* Create a new streaming indexer instance
|
||||
*
|
||||
* @param out where to store the inexer instance
|
||||
* @param path to the gitdir (metadata directory)
|
||||
*/
|
||||
GIT_EXTERN(int) git_indexer_stream_new(git_indexer_stream **out, const char *gitdir);
|
||||
|
||||
/**
|
||||
* Add data to the indexer
|
||||
*
|
||||
* @param idx the indexer
|
||||
* @param data the data to add
|
||||
* @param size the size of the data
|
||||
* @param stats stat storage
|
||||
*/
|
||||
GIT_EXTERN(int) git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_indexer_stats *stats);
|
||||
|
||||
/**
|
||||
* Finalize the pack and index
|
||||
*
|
||||
* Resolve any pending deltas and write out the index file
|
||||
*
|
||||
* @param idx the indexer
|
||||
*/
|
||||
GIT_EXTERN(int) git_indexer_stream_finalize(git_indexer_stream *idx, git_indexer_stats *stats);
|
||||
|
||||
/**
|
||||
* Get the packfile's hash
|
||||
*
|
||||
* A packfile's name is derived from the sorted hashing of all object
|
||||
* names. This is only correct after the index has been finalized.
|
||||
*
|
||||
* @param idx the indexer instance
|
||||
*/
|
||||
GIT_EXTERN(const git_oid *) git_indexer_stream_hash(git_indexer_stream *idx);
|
||||
|
||||
/**
|
||||
* Free the indexer and its resources
|
||||
*
|
||||
* @param idx the indexer to free
|
||||
*/
|
||||
GIT_EXTERN(void) git_indexer_stream_free(git_indexer_stream *idx);
|
||||
|
||||
/**
|
||||
* Create a new indexer instance
|
||||
|
35
include/git2/merge.h
Normal file
35
include/git2/merge.h
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* 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_merge_h__
|
||||
#define INCLUDE_git_merge_h__
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
#include "oid.h"
|
||||
|
||||
/**
|
||||
* @file git2/merge.h
|
||||
* @brief Git merge-base routines
|
||||
* @defgroup git_revwalk Git merge-base routines
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* Find a merge base between two commits
|
||||
*
|
||||
* @param out the OID of a merge base between 'one' and 'two'
|
||||
* @param repo the repository where the commits exist
|
||||
* @param one one of the commits
|
||||
* @param two the other commit
|
||||
*/
|
||||
GIT_EXTERN(int) git_merge_base(git_oid *out, git_repository *repo, git_oid *one, git_oid *two);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
|
139
include/git2/notes.h
Normal file
139
include/git2/notes.h
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* 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_note_h__
|
||||
#define INCLUDE_git_note_h__
|
||||
|
||||
#include "oid.h"
|
||||
|
||||
/**
|
||||
* @file git2/notes.h
|
||||
* @brief Git notes management routines
|
||||
* @defgroup git_note Git notes management routines
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* Read the note for an object
|
||||
*
|
||||
* The note must be freed manually by the user.
|
||||
*
|
||||
* @param note the note; NULL in case of error
|
||||
* @param repo the Git repository
|
||||
* @param notes_ref OID reference to use (optional); defaults to "refs/notes/commits"
|
||||
* @param oid OID of the object
|
||||
*
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_note_read(git_note **note, git_repository *repo,
|
||||
const char *notes_ref, const git_oid *oid);
|
||||
|
||||
/**
|
||||
* Get the note message
|
||||
*
|
||||
* @param note
|
||||
* @return the note message
|
||||
*/
|
||||
GIT_EXTERN(const char *) git_note_message(git_note *note);
|
||||
|
||||
|
||||
/**
|
||||
* Get the note object OID
|
||||
*
|
||||
* @param note
|
||||
* @return the note object OID
|
||||
*/
|
||||
GIT_EXTERN(const git_oid *) git_note_oid(git_note *note);
|
||||
|
||||
|
||||
/**
|
||||
* Add a note for an object
|
||||
*
|
||||
* @param oid pointer to store the OID (optional); NULL in case of error
|
||||
* @param repo the Git repository
|
||||
* @param author signature of the notes commit author
|
||||
* @param committer signature of the notes commit committer
|
||||
* @param notes_ref OID reference to update (optional); defaults to "refs/notes/commits"
|
||||
* @param oid The OID of the object
|
||||
* @param oid The note to add for object oid
|
||||
*
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_note_create(git_oid *out, git_repository *repo,
|
||||
git_signature *author, git_signature *committer,
|
||||
const char *notes_ref, const git_oid *oid,
|
||||
const char *note);
|
||||
|
||||
|
||||
/**
|
||||
* Remove the note for an object
|
||||
*
|
||||
* @param repo the Git repository
|
||||
* @param notes_ref OID reference to use (optional); defaults to "refs/notes/commits"
|
||||
* @param author signature of the notes commit author
|
||||
* @param committer signature of the notes commit committer
|
||||
* @param oid the oid which note's to be removed
|
||||
*
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_note_remove(git_repository *repo, const char *notes_ref,
|
||||
git_signature *author, git_signature *committer,
|
||||
const git_oid *oid);
|
||||
|
||||
/**
|
||||
* Free a git_note object
|
||||
*
|
||||
* @param note git_note object
|
||||
*/
|
||||
GIT_EXTERN(void) git_note_free(git_note *note);
|
||||
|
||||
/**
|
||||
* Get the default notes reference for a repository
|
||||
*
|
||||
* @param out Pointer to the default notes reference
|
||||
* @param repo The Git repository
|
||||
*
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_note_default_ref(const char **out, git_repository *repo);
|
||||
|
||||
/**
|
||||
* Basic components of a note
|
||||
*
|
||||
* - Oid of the blob containing the message
|
||||
* - Oid of the git object being annotated
|
||||
*/
|
||||
typedef struct {
|
||||
git_oid blob_oid;
|
||||
git_oid annotated_object_oid;
|
||||
} git_note_data;
|
||||
|
||||
/**
|
||||
* Loop over all the notes within a specified namespace
|
||||
* and issue a callback for each one.
|
||||
*
|
||||
* @param repo Repository where to find the notes.
|
||||
*
|
||||
* @param notes_ref OID reference to read from (optional); defaults to "refs/notes/commits".
|
||||
*
|
||||
* @param note_cb Callback to invoke per found annotation.
|
||||
*
|
||||
* @param payload Extra parameter to callback function.
|
||||
*
|
||||
* @return 0 or an error code.
|
||||
*/
|
||||
GIT_EXTERN(int) git_note_foreach(
|
||||
git_repository *repo,
|
||||
const char *notes_ref,
|
||||
int (*note_cb)(git_note_data *note_data, void *payload),
|
||||
void *payload
|
||||
);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -69,7 +69,7 @@ GIT_EXTERN(int) git_object_lookup(
|
||||
* @param id a short identifier for the object
|
||||
* @param len the length of the short identifier
|
||||
* @param type the type of the object
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_object_lookup_prefix(
|
||||
git_object **object_out,
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -29,7 +29,7 @@ GIT_BEGIN_DECL
|
||||
*
|
||||
* @param out location to store the database pointer, if opened.
|
||||
* Set to NULL if the open failed.
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_new(git_odb **out);
|
||||
|
||||
@ -47,7 +47,7 @@ GIT_EXTERN(int) git_odb_new(git_odb **out);
|
||||
* @param out location to store the database pointer, if opened.
|
||||
* Set to NULL if the open failed.
|
||||
* @param objects_dir path of the backends' "objects" directory.
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_open(git_odb **out, const char *objects_dir);
|
||||
|
||||
@ -108,7 +108,7 @@ GIT_EXTERN(void) git_odb_free(git_odb *db);
|
||||
* @param db database to search for the object in.
|
||||
* @param id identity of the object to read.
|
||||
* @return
|
||||
* - GIT_SUCCESS if the object was read;
|
||||
* - 0 if the object was read;
|
||||
* - GIT_ENOTFOUND if the object is not in the database.
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id);
|
||||
@ -135,7 +135,7 @@ GIT_EXTERN(int) git_odb_read(git_odb_object **out, git_odb *db, const git_oid *i
|
||||
* @param db database to search for the object in.
|
||||
* @param short_id a prefix of the id of the object to read.
|
||||
* @param len the length of the prefix
|
||||
* @return GIT_SUCCESS if the object was read;
|
||||
* @return 0 if the object was read;
|
||||
* GIT_ENOTFOUND if the object is not in the database.
|
||||
* GIT_EAMBIGUOUS if the prefix is ambiguous (several objects match the prefix)
|
||||
*/
|
||||
@ -156,7 +156,7 @@ GIT_EXTERN(int) git_odb_read_prefix(git_odb_object **out, git_odb *db, const git
|
||||
* @param db database to search for the object in.
|
||||
* @param id identity of the object to read.
|
||||
* @return
|
||||
* - GIT_SUCCESS if the object was read;
|
||||
* - 0 if the object was read;
|
||||
* - GIT_ENOTFOUND if the object is not in the database.
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id);
|
||||
@ -188,7 +188,7 @@ GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
|
||||
* @param data buffer with the data to storr
|
||||
* @param len size of the buffer
|
||||
* @param type type of the data to store
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_write(git_oid *oid, git_odb *odb, const void *data, size_t len, git_otype type);
|
||||
|
||||
@ -257,7 +257,7 @@ GIT_EXTERN(int) git_odb_open_rstream(git_odb_stream **stream, git_odb *db, const
|
||||
* @param data data to hash
|
||||
* @param len size of the data
|
||||
* @param type of the data to hash
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_hash(git_oid *id, const void *data, size_t len, git_otype type);
|
||||
|
||||
@ -270,7 +270,7 @@ GIT_EXTERN(int) git_odb_hash(git_oid *id, const void *data, size_t len, git_otyp
|
||||
* @param out oid structure the result is written into.
|
||||
* @param path file to read and determine object id for
|
||||
* @param type the type of the object that will be hashed
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_hashfile(git_oid *out, const char *path, git_otype type);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -74,6 +74,13 @@ struct git_odb_backend {
|
||||
void (* free)(struct git_odb_backend *);
|
||||
};
|
||||
|
||||
/** Streaming mode */
|
||||
enum {
|
||||
GIT_STREAM_RDONLY = (1 << 1),
|
||||
GIT_STREAM_WRONLY = (1 << 2),
|
||||
GIT_STREAM_RW = (GIT_STREAM_RDONLY | GIT_STREAM_WRONLY),
|
||||
};
|
||||
|
||||
/** A stream to read/write from a backend */
|
||||
struct git_odb_stream {
|
||||
struct git_odb_backend *backend;
|
||||
@ -85,13 +92,6 @@ struct git_odb_stream {
|
||||
void (*free)(struct git_odb_stream *stream);
|
||||
};
|
||||
|
||||
/** Streaming mode */
|
||||
typedef enum {
|
||||
GIT_STREAM_RDONLY = (1 << 1),
|
||||
GIT_STREAM_WRONLY = (1 << 2),
|
||||
GIT_STREAM_RW = (GIT_STREAM_RDONLY | GIT_STREAM_WRONLY),
|
||||
} git_odb_streammode;
|
||||
|
||||
GIT_EXTERN(int) git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir);
|
||||
GIT_EXTERN(int) git_odb_backend_loose(git_odb_backend **backend_out, const char *objects_dir, int compression_level, int do_fsync);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -43,7 +43,7 @@ struct _git_oid {
|
||||
* @param str input hex string; must be pointing at the start of
|
||||
* the hex sequence and have at least the number of bytes
|
||||
* needed for an oid encoded in hex (40 bytes).
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_oid_fromstr(git_oid *out, const char *str);
|
||||
|
||||
@ -56,7 +56,7 @@ GIT_EXTERN(int) git_oid_fromstr(git_oid *out, const char *str);
|
||||
* @param out oid structure the result is written into.
|
||||
* @param str input hex string of at least size `length`
|
||||
* @param length length of the input string
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_oid_fromstrn(git_oid *out, const char *str, size_t length);
|
||||
|
||||
@ -119,7 +119,7 @@ GIT_EXTERN(char *) git_oid_allocfmt(const git_oid *oid);
|
||||
* @return the out buffer pointer, assuming no input parameter
|
||||
* errors, otherwise a pointer to an empty string.
|
||||
*/
|
||||
GIT_EXTERN(char *) git_oid_to_string(char *out, size_t n, const git_oid *oid);
|
||||
GIT_EXTERN(char *) git_oid_tostr(char *out, size_t n, const git_oid *oid);
|
||||
|
||||
/**
|
||||
* Copy an oid from one structure to another.
|
||||
@ -155,10 +155,15 @@ GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, unsigned int le
|
||||
* @param a oid structure.
|
||||
* @param str input hex string of an object id.
|
||||
* @return GIT_ENOTOID if str is not a valid hex string,
|
||||
* GIT_SUCCESS in case of a match, GIT_ERROR otherwise.
|
||||
* 0 in case of a match, GIT_ERROR otherwise.
|
||||
*/
|
||||
GIT_EXTERN(int) git_oid_streq(const git_oid *a, const char *str);
|
||||
|
||||
/**
|
||||
* Check is an oid is all zeros.
|
||||
*/
|
||||
GIT_EXTERN(int) git_oid_iszero(const git_oid *a);
|
||||
|
||||
/**
|
||||
* OID Shortener object
|
||||
*/
|
||||
@ -178,7 +183,7 @@ typedef struct git_oid_shorten git_oid_shorten;
|
||||
* be unique.
|
||||
* @return a `git_oid_shorten` instance, NULL if OOM
|
||||
*/
|
||||
git_oid_shorten *git_oid_shorten_new(size_t min_length);
|
||||
GIT_EXTERN(git_oid_shorten *) git_oid_shorten_new(size_t min_length);
|
||||
|
||||
/**
|
||||
* Add a new OID to set of shortened OIDs and calculate
|
||||
@ -204,14 +209,14 @@ git_oid_shorten *git_oid_shorten_new(size_t min_length);
|
||||
* added so far to the set; or an error code (<0) if an
|
||||
* error occurs.
|
||||
*/
|
||||
int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid);
|
||||
GIT_EXTERN(int) git_oid_shorten_add(git_oid_shorten *os, const char *text_oid);
|
||||
|
||||
/**
|
||||
* Free an OID shortener instance
|
||||
*
|
||||
* @param os a `git_oid_shorten` instance
|
||||
*/
|
||||
void git_oid_shorten_free(git_oid_shorten *os);
|
||||
GIT_EXTERN(void) git_oid_shorten_free(git_oid_shorten *os);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -28,7 +28,7 @@ GIT_BEGIN_DECL
|
||||
*
|
||||
* @param reflog pointer to reflog
|
||||
* @param ref reference to read the reflog for
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reflog_read(git_reflog **reflog, git_reference *ref);
|
||||
|
||||
@ -46,7 +46,7 @@ GIT_EXTERN(int) git_reflog_read(git_reflog **reflog, git_reference *ref);
|
||||
* @param oid_old the OID the reference was pointing to
|
||||
* @param committer the signature of the committer
|
||||
* @param msg the reflog message
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reflog_write(git_reference *ref, const git_oid *oid_old, const git_signature *committer, const char *msg);
|
||||
|
||||
@ -55,7 +55,7 @@ GIT_EXTERN(int) git_reflog_write(git_reference *ref, const git_oid *oid_old, con
|
||||
*
|
||||
* @param ref the reference
|
||||
* @param new_name the new name of the reference
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reflog_rename(git_reference *ref, const char *new_name);
|
||||
|
||||
@ -63,7 +63,7 @@ GIT_EXTERN(int) git_reflog_rename(git_reference *ref, const char *new_name);
|
||||
* Delete the reflog for the given reference
|
||||
*
|
||||
* @param ref the reference
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reflog_delete(git_reference *ref);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -28,10 +28,21 @@ GIT_BEGIN_DECL
|
||||
* @param reference_out pointer to the looked-up reference
|
||||
* @param repo the repository to look up the reference
|
||||
* @param name the long name for the reference (e.g. HEAD, ref/heads/master, refs/tags/v0.1.0, ...)
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_lookup(git_reference **reference_out, git_repository *repo, const char *name);
|
||||
|
||||
/**
|
||||
* Lookup a reference by name and resolve immediately to OID.
|
||||
*
|
||||
* @param oid Pointer to oid to be filled in
|
||||
* @param repo The repository in which to look up the reference
|
||||
* @param name The long name for the reference
|
||||
* @return 0 on success, -1 if name could not be resolved
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_name_to_oid(
|
||||
git_oid *out, git_repository *repo, const char *name);
|
||||
|
||||
/**
|
||||
* Create a new symbolic reference.
|
||||
*
|
||||
@ -48,7 +59,7 @@ GIT_EXTERN(int) git_reference_lookup(git_reference **reference_out, git_reposito
|
||||
* @param name The name of the reference
|
||||
* @param target The target of the reference
|
||||
* @param force Overwrite existing references
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_create_symbolic(git_reference **ref_out, git_repository *repo, const char *name, const char *target, int force);
|
||||
|
||||
@ -68,7 +79,7 @@ GIT_EXTERN(int) git_reference_create_symbolic(git_reference **ref_out, git_repos
|
||||
* @param name The name of the reference
|
||||
* @param id The object id pointed to by the reference.
|
||||
* @param force Overwrite existing references
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_create_oid(git_reference **ref_out, git_repository *repo, const char *name, const git_oid *id, int force);
|
||||
|
||||
@ -100,7 +111,7 @@ GIT_EXTERN(const char *) git_reference_target(git_reference *ref);
|
||||
* @param ref The reference
|
||||
* @return the type
|
||||
*/
|
||||
GIT_EXTERN(git_rtype) git_reference_type(git_reference *ref);
|
||||
GIT_EXTERN(git_ref_t) git_reference_type(git_reference *ref);
|
||||
|
||||
/**
|
||||
* Get the full name of a reference
|
||||
@ -126,7 +137,7 @@ GIT_EXTERN(const char *) git_reference_name(git_reference *ref);
|
||||
*
|
||||
* @param resolved_ref Pointer to the peeled reference
|
||||
* @param ref The reference
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_resolve(git_reference **resolved_ref, git_reference *ref);
|
||||
|
||||
@ -149,7 +160,7 @@ GIT_EXTERN(git_repository *) git_reference_owner(git_reference *ref);
|
||||
*
|
||||
* @param ref The reference
|
||||
* @param target The new target for the reference
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_set_target(git_reference *ref, const char *target);
|
||||
|
||||
@ -164,7 +175,7 @@ GIT_EXTERN(int) git_reference_set_target(git_reference *ref, const char *target)
|
||||
*
|
||||
* @param ref The reference
|
||||
* @param id The new target OID for the reference
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_set_oid(git_reference *ref, const git_oid *id);
|
||||
|
||||
@ -191,7 +202,7 @@ GIT_EXTERN(int) git_reference_set_oid(git_reference *ref, const git_oid *id);
|
||||
* @param ref The reference to rename
|
||||
* @param new_name The new name for the reference
|
||||
* @param force Overwrite an existing reference
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *new_name, int force);
|
||||
@ -205,7 +216,7 @@ GIT_EXTERN(int) git_reference_rename(git_reference *ref, const char *new_name, i
|
||||
* memory. The given reference pointer will no longer be valid.
|
||||
*
|
||||
* @param ref The reference to remove
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_delete(git_reference *ref);
|
||||
|
||||
@ -220,7 +231,7 @@ GIT_EXTERN(int) git_reference_delete(git_reference *ref);
|
||||
* the loose references will be removed from disk.
|
||||
*
|
||||
* @param repo Repository where the loose refs will be packed
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_packall(git_repository *repo);
|
||||
|
||||
@ -243,9 +254,9 @@ GIT_EXTERN(int) git_reference_packall(git_repository *repo);
|
||||
* @param repo Repository where to find the refs
|
||||
* @param list_flags Filtering flags for the reference
|
||||
* listing.
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_listall(git_strarray *array, git_repository *repo, unsigned int list_flags);
|
||||
GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo, unsigned int list_flags);
|
||||
|
||||
|
||||
/**
|
||||
@ -265,7 +276,7 @@ GIT_EXTERN(int) git_reference_listall(git_strarray *array, git_repository *repo,
|
||||
* listing.
|
||||
* @param callback Function which will be called for every listed ref
|
||||
* @param payload Additional data to pass to the callback
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_foreach(git_repository *repo, unsigned int list_flags, int (*callback)(const char *, void *), void *payload);
|
||||
|
||||
@ -293,7 +304,7 @@ GIT_EXTERN(int) git_reference_is_packed(git_reference *ref);
|
||||
* returned and the reference pointer will be invalidated.
|
||||
*
|
||||
* @param ref The reference to reload
|
||||
* @return GIT_SUCCESS on success, or an error code
|
||||
* @return 0 on success, or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reference_reload(git_reference *ref);
|
||||
|
||||
@ -304,6 +315,15 @@ GIT_EXTERN(int) git_reference_reload(git_reference *ref);
|
||||
*/
|
||||
GIT_EXTERN(void) git_reference_free(git_reference *ref);
|
||||
|
||||
/**
|
||||
* Compare two references.
|
||||
*
|
||||
* @param ref1 The first git_reference
|
||||
* @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_END_DECL
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -7,6 +7,7 @@
|
||||
#ifndef INCLUDE_git_refspec_h__
|
||||
#define INCLUDE_git_refspec_h__
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
|
||||
/**
|
||||
@ -24,7 +25,7 @@ GIT_BEGIN_DECL
|
||||
* @param refspec the refspec
|
||||
* @return the refspec's source specifier
|
||||
*/
|
||||
const char *git_refspec_src(const git_refspec *refspec);
|
||||
GIT_EXTERN(const char *) git_refspec_src(const git_refspec *refspec);
|
||||
|
||||
/**
|
||||
* Get the destination specifier
|
||||
@ -32,17 +33,16 @@ const char *git_refspec_src(const git_refspec *refspec);
|
||||
* @param refspec the refspec
|
||||
* @return the refspec's destination specifier
|
||||
*/
|
||||
const char *git_refspec_dst(const git_refspec *refspec);
|
||||
GIT_EXTERN(const char *) git_refspec_dst(const git_refspec *refspec);
|
||||
|
||||
/**
|
||||
* Match a refspec's source descriptor with a reference name
|
||||
* Check if a refspec's source descriptor matches a reference
|
||||
*
|
||||
* @param refspec the refspec
|
||||
* @param refname the name of the reference to check
|
||||
* @return GIT_SUCCESS on successful match; GIT_ENOMACH on match
|
||||
* failure or an error code on other failure
|
||||
* @return 1 if the refspec matches, 0 otherwise
|
||||
*/
|
||||
int git_refspec_src_match(const git_refspec *refspec, const char *refname);
|
||||
GIT_EXTERN(int) git_refspec_src_matches(const git_refspec *refspec, const char *refname);
|
||||
|
||||
/**
|
||||
* Transform a reference to its target following the refspec's rules
|
||||
@ -51,9 +51,9 @@ int git_refspec_src_match(const git_refspec *refspec, const char *refname);
|
||||
* @param outlen the size ouf the `out` buffer
|
||||
* @param spec the refspec
|
||||
* @param name the name of the reference to transform
|
||||
* @return GIT_SUCCESS, GIT_ESHORTBUFFER or another error
|
||||
* @return 0, GIT_EBUFS or another error
|
||||
*/
|
||||
int git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name);
|
||||
GIT_EXTERN(int) git_refspec_transform(char *out, size_t outlen, const git_refspec *spec, const char *name);
|
||||
|
||||
GIT_END_DECL
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -11,6 +11,7 @@
|
||||
#include "repository.h"
|
||||
#include "refspec.h"
|
||||
#include "net.h"
|
||||
#include "indexer.h"
|
||||
|
||||
/**
|
||||
* @file git2/remote.h
|
||||
@ -37,11 +38,12 @@ GIT_BEGIN_DECL
|
||||
*
|
||||
* @param out pointer to the new remote object
|
||||
* @param repo the associtated repository
|
||||
* @param url the remote repository's URL
|
||||
* @param name the remote's name
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @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_new(git_remote **out, git_repository *repo, const char *url, const char *name);
|
||||
GIT_EXTERN(int) git_remote_new(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch);
|
||||
|
||||
/**
|
||||
* Get the information for a particular remote
|
||||
@ -49,10 +51,18 @@ GIT_EXTERN(int) git_remote_new(git_remote **out, git_repository *repo, const cha
|
||||
* @param out pointer to the new remote object
|
||||
* @param cfg the repository's configuration
|
||||
* @param name the remote's name
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_load(git_remote **out, git_repository *repo, const char *name);
|
||||
|
||||
/**
|
||||
* Save a remote to its repository's configuration
|
||||
*
|
||||
* @param remote the remote to save to config
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_save(const git_remote *remote);
|
||||
|
||||
/**
|
||||
* Get the remote's name
|
||||
*
|
||||
@ -69,6 +79,15 @@ GIT_EXTERN(const char *) git_remote_name(git_remote *remote);
|
||||
*/
|
||||
GIT_EXTERN(const char *) git_remote_url(git_remote *remote);
|
||||
|
||||
/**
|
||||
* Set the remote's fetch refspec
|
||||
*
|
||||
* @param remote the remote
|
||||
* @apram spec the new fetch refspec
|
||||
* @return 0 or an error value
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_set_fetchspec(git_remote *remote, const char *spec);
|
||||
|
||||
/**
|
||||
* Get the fetch refspec
|
||||
*
|
||||
@ -77,6 +96,15 @@ GIT_EXTERN(const char *) git_remote_url(git_remote *remote);
|
||||
*/
|
||||
GIT_EXTERN(const git_refspec *) git_remote_fetchspec(git_remote *remote);
|
||||
|
||||
/**
|
||||
* Set the remote's push refspec
|
||||
*
|
||||
* @param remote the remote
|
||||
* @apram spec the new push refspec
|
||||
* @return 0 or an error value
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_set_pushspec(git_remote *remote, const char *spec);
|
||||
|
||||
/**
|
||||
* Get the push refspec
|
||||
*
|
||||
@ -95,7 +123,7 @@ GIT_EXTERN(const git_refspec *) git_remote_pushspec(git_remote *remote);
|
||||
*
|
||||
* @param remote the remote to connect to
|
||||
* @param direction whether you want to receive or send data
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_connect(git_remote *remote, int direction);
|
||||
|
||||
@ -107,7 +135,7 @@ GIT_EXTERN(int) git_remote_connect(git_remote *remote, int direction);
|
||||
*
|
||||
* @param refs where to store the refs
|
||||
* @param remote the remote
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload);
|
||||
|
||||
@ -122,9 +150,9 @@ GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void
|
||||
*
|
||||
* @param remote the remote to download from
|
||||
* @param filename where to store the temproray filename
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_download(char **filename, git_remote *remote);
|
||||
GIT_EXTERN(int) git_remote_download(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);
|
||||
|
||||
/**
|
||||
* Check whether the remote is connected
|
||||
@ -149,6 +177,9 @@ GIT_EXTERN(void) git_remote_disconnect(git_remote *remote);
|
||||
/**
|
||||
* Free the memory associated with a remote
|
||||
*
|
||||
* This also disconnects from the remote, if the connection
|
||||
* has not been closed yet (using git_remote_disconnect).
|
||||
*
|
||||
* @param remote the remote to free
|
||||
*/
|
||||
GIT_EXTERN(void) git_remote_free(git_remote *remote);
|
||||
@ -156,12 +187,10 @@ GIT_EXTERN(void) git_remote_free(git_remote *remote);
|
||||
/**
|
||||
* Update the tips to the new state
|
||||
*
|
||||
* Make sure that you only call this once you've successfully indexed
|
||||
* or expanded the packfile.
|
||||
*
|
||||
* @param remote the remote to update
|
||||
* @param cb callback to run on each ref update. 'a' is the old value, 'b' is then new value
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_update_tips(git_remote *remote);
|
||||
GIT_EXTERN(int) git_remote_update_tips(git_remote *remote, int (*cb)(const char *refname, const git_oid *a, const git_oid *b));
|
||||
|
||||
/**
|
||||
* Return whether a string is a valid remote URL
|
||||
@ -171,6 +200,35 @@ GIT_EXTERN(int) git_remote_update_tips(git_remote *remote);
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_valid_url(const char *url);
|
||||
|
||||
/**
|
||||
* Return whether the passed URL is supported by this version of the library.
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return 1 if the url is supported, 0 otherwise
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_supported_url(const char* url);
|
||||
|
||||
/**
|
||||
* Get a list of the configured remotes for a repo
|
||||
*
|
||||
* The string array must be freed by the user.
|
||||
*
|
||||
* @param remotes_list a string array with the names of the remotes
|
||||
* @param repo the repository to query
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_list(git_strarray *remotes_list, git_repository *repo);
|
||||
|
||||
/**
|
||||
* Add a remote with the default fetch refspec to the repository's configuration
|
||||
*
|
||||
* @param out the resulting remote
|
||||
* @param repo the repository in which to create the remote
|
||||
* @param name the remote's name
|
||||
* @param url the remote's url
|
||||
*/
|
||||
GIT_EXTERN(int) git_remote_add(git_remote **out, git_repository *repo, const char *name, const char *url);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -31,7 +31,7 @@ GIT_BEGIN_DECL
|
||||
*
|
||||
* @param repository pointer to the repo which will be opened
|
||||
* @param path the path to the repository
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_open(git_repository **repository, const char *path);
|
||||
|
||||
@ -61,7 +61,7 @@ GIT_EXTERN(int) git_repository_open(git_repository **repository, const char *pat
|
||||
* start_path no matter start_path appears in ceiling_dirs ceiling_dirs
|
||||
* might be NULL (which is equivalent to an empty string)
|
||||
*
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_discover(
|
||||
char *repository_path,
|
||||
@ -70,6 +70,20 @@ GIT_EXTERN(int) git_repository_discover(
|
||||
int across_fs,
|
||||
const char *ceiling_dirs);
|
||||
|
||||
enum {
|
||||
GIT_REPOSITORY_OPEN_NO_SEARCH = (1 << 0),
|
||||
GIT_REPOSITORY_OPEN_CROSS_FS = (1 << 1),
|
||||
};
|
||||
|
||||
/**
|
||||
* Find and open a repository with extended controls.
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_open_ext(
|
||||
git_repository **repo,
|
||||
const char *start_path,
|
||||
uint32_t flags,
|
||||
const char *ceiling_dirs);
|
||||
|
||||
/**
|
||||
* Free a previously allocated repository
|
||||
*
|
||||
@ -95,7 +109,7 @@ GIT_EXTERN(void) git_repository_free(git_repository *repo);
|
||||
* at the pointed path. If false, provided path will be considered as the working
|
||||
* directory into which the .git directory will be created.
|
||||
*
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare);
|
||||
|
||||
@ -180,7 +194,7 @@ GIT_EXTERN(const char *) git_repository_workdir(git_repository *repo);
|
||||
*
|
||||
* @param repo A repository object
|
||||
* @param workdir The path to a working directory
|
||||
* @return GIT_SUCCESS, or an error code
|
||||
* @return 0, or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_set_workdir(git_repository *repo, const char *workdir);
|
||||
|
||||
@ -204,7 +218,7 @@ GIT_EXTERN(int) git_repository_is_bare(git_repository *repo);
|
||||
*
|
||||
* @param out Pointer to store the loaded config file
|
||||
* @param repo A repository object
|
||||
* @return GIT_SUCCESS, or an error code
|
||||
* @return 0, or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_config(git_config **out, git_repository *repo);
|
||||
|
||||
@ -235,7 +249,7 @@ GIT_EXTERN(void) git_repository_set_config(git_repository *repo, git_config *con
|
||||
*
|
||||
* @param out Pointer to store the loaded ODB
|
||||
* @param repo A repository object
|
||||
* @return GIT_SUCCESS, or an error code
|
||||
* @return 0, or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_odb(git_odb **out, git_repository *repo);
|
||||
|
||||
@ -266,7 +280,7 @@ GIT_EXTERN(void) git_repository_set_odb(git_repository *repo, git_odb *odb);
|
||||
*
|
||||
* @param out Pointer to store the loaded index
|
||||
* @param repo A repository object
|
||||
* @return GIT_SUCCESS, or an error code
|
||||
* @return 0, or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_index(git_index **out, git_repository *repo);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -65,7 +65,7 @@ GIT_BEGIN_DECL
|
||||
*
|
||||
* @param walker pointer to the new revision walker
|
||||
* @param repo the repo to walk through
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_revwalk_new(git_revwalk **walker, git_repository *repo);
|
||||
|
||||
@ -97,10 +97,32 @@ GIT_EXTERN(void) git_revwalk_reset(git_revwalk *walker);
|
||||
*
|
||||
* @param walk the walker being used for the traversal.
|
||||
* @param oid the oid of the commit to start from.
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_revwalk_push(git_revwalk *walk, const git_oid *oid);
|
||||
|
||||
/**
|
||||
* Push matching references
|
||||
*
|
||||
* The OIDs pinted to by the references that match the given glob
|
||||
* pattern will be pushed to the revision walker.
|
||||
*
|
||||
* A leading 'refs/' is implied it not present as well as a trailing
|
||||
* '/ *' if the glob lacks '?', '*' or '['.
|
||||
*
|
||||
* @param walk the walker being used for the traversal
|
||||
* @param glob the glob pattern references should match
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_revwalk_push_glob(git_revwalk *walk, const char *glob);
|
||||
|
||||
/**
|
||||
* Push the repository's HEAD
|
||||
*
|
||||
* @param walk the walker being used for the traversal
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_revwalk_push_head(git_revwalk *walk);
|
||||
|
||||
/**
|
||||
* Mark a commit (and its ancestors) uninteresting for the output.
|
||||
@ -113,10 +135,56 @@ GIT_EXTERN(int) git_revwalk_push(git_revwalk *walk, const git_oid *oid);
|
||||
*
|
||||
* @param walk the walker being used for the traversal.
|
||||
* @param oid the oid of commit that will be ignored during the traversal
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_revwalk_hide(git_revwalk *walk, const git_oid *oid);
|
||||
|
||||
/**
|
||||
* Hide matching references.
|
||||
*
|
||||
* The OIDs pinted to by the references that match the given glob
|
||||
* pattern and their ancestors will be hidden from the output on the
|
||||
* revision walk.
|
||||
*
|
||||
* A leading 'refs/' is implied it not present as well as a trailing
|
||||
* '/ *' if the glob lacks '?', '*' or '['.
|
||||
*
|
||||
* @param walk the walker being used for the traversal
|
||||
* @param glob the glob pattern references should match
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_revwalk_hide_glob(git_revwalk *walk, const char *glob);
|
||||
|
||||
/**
|
||||
* Hide the repository's HEAD
|
||||
*
|
||||
* @param walk the walker being used for the traversal
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param walk the walker being used for the traversal
|
||||
* @param refname the referece to push
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param walk the walker being used for the traversal
|
||||
* @param refname the referece to hide
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_revwalk_hide_ref(git_revwalk *walk, const char *refname);
|
||||
|
||||
/**
|
||||
* Get the next commit from the revision walk.
|
||||
*
|
||||
@ -132,8 +200,8 @@ GIT_EXTERN(int) git_revwalk_hide(git_revwalk *walk, const git_oid *oid);
|
||||
*
|
||||
* @param oid Pointer where to store the oid of the next commit
|
||||
* @param walk the walker to pop the commit from.
|
||||
* @return GIT_SUCCESS if the next commit was found;
|
||||
* GIT_EREVWALKOVER if there are no commits left to iterate
|
||||
* @return 0 if the next commit was found;
|
||||
* GIT_REVWALKOVER if there are no commits left to iterate
|
||||
*/
|
||||
GIT_EXTERN(int) git_revwalk_next(git_oid *oid, git_revwalk *walk);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -28,7 +28,7 @@ GIT_BEGIN_DECL
|
||||
* @param email email of the person
|
||||
* @param time time when the action happened
|
||||
* @param offset timezone offset in minutes for the time
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset);
|
||||
|
||||
@ -39,7 +39,7 @@ GIT_EXTERN(int) git_signature_new(git_signature **sig_out, const char *name, con
|
||||
* @param sig_out new signature, in case of error NULL
|
||||
* @param name name of the person
|
||||
* @param email email of the person
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_signature_now(git_signature **sig_out, const char *name, const char *email);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -19,32 +19,111 @@
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
#define GIT_STATUS_CURRENT 0
|
||||
enum {
|
||||
GIT_STATUS_CURRENT = 0,
|
||||
|
||||
/** Flags for index status */
|
||||
#define GIT_STATUS_INDEX_NEW (1 << 0)
|
||||
#define GIT_STATUS_INDEX_MODIFIED (1 << 1)
|
||||
#define GIT_STATUS_INDEX_DELETED (1 << 2)
|
||||
GIT_STATUS_INDEX_NEW = (1 << 0),
|
||||
GIT_STATUS_INDEX_MODIFIED = (1 << 1),
|
||||
GIT_STATUS_INDEX_DELETED = (1 << 2),
|
||||
|
||||
/** Flags for worktree status */
|
||||
#define GIT_STATUS_WT_NEW (1 << 3)
|
||||
#define GIT_STATUS_WT_MODIFIED (1 << 4)
|
||||
#define GIT_STATUS_WT_DELETED (1 << 5)
|
||||
GIT_STATUS_WT_NEW = (1 << 3),
|
||||
GIT_STATUS_WT_MODIFIED = (1 << 4),
|
||||
GIT_STATUS_WT_DELETED = (1 << 5),
|
||||
|
||||
#define GIT_STATUS_IGNORED (1 << 6)
|
||||
GIT_STATUS_IGNORED = (1 << 6),
|
||||
};
|
||||
|
||||
/**
|
||||
* Gather file statuses and run a callback for each one.
|
||||
*
|
||||
* The callback is passed the path of the file, the status and the data pointer
|
||||
* passed to this function. If the callback returns something other than
|
||||
* GIT_SUCCESS, this function will return that value.
|
||||
* The callback is passed the path of the file, the status and the data
|
||||
* pointer passed to this function. If the callback returns something other
|
||||
* than 0, this function will return that value.
|
||||
*
|
||||
* @param repo a repository object
|
||||
* @param callback the function to call on each file
|
||||
* @return GIT_SUCCESS or the return value of the callback which did not return GIT_SUCCESS
|
||||
* @return 0 on success or the return value of the callback that was non-zero
|
||||
*/
|
||||
GIT_EXTERN(int) git_status_foreach(git_repository *repo, int (*callback)(const char *, unsigned int, void *), void *payload);
|
||||
GIT_EXTERN(int) git_status_foreach(
|
||||
git_repository *repo,
|
||||
int (*callback)(const char *, unsigned int, void *),
|
||||
void *payload);
|
||||
|
||||
/**
|
||||
* Select the files on which to report status.
|
||||
*
|
||||
* - GIT_STATUS_SHOW_INDEX_AND_WORKDIR is the default. This is the
|
||||
* rough equivalent of `git status --porcelain` where each file
|
||||
* will receive a callback indicating its status in the index and
|
||||
* in the workdir.
|
||||
* - GIT_STATUS_SHOW_INDEX_ONLY will only make callbacks for index
|
||||
* side of status. The status of the index contents relative to
|
||||
* the HEAD will be given.
|
||||
* - GIT_STATUS_SHOW_WORKDIR_ONLY will only make callbacks for the
|
||||
* workdir side of status, reporting the status of workdir content
|
||||
* relative to the index.
|
||||
* - GIT_STATUS_SHOW_INDEX_THEN_WORKDIR behaves like index-only
|
||||
* followed by workdir-only, causing two callbacks to be issued
|
||||
* per file (first index then workdir). This is slightly more
|
||||
* efficient than making separate calls. This makes it easier to
|
||||
* emulate the output of a plain `git status`.
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
|
||||
GIT_STATUS_SHOW_INDEX_ONLY = 1,
|
||||
GIT_STATUS_SHOW_WORKDIR_ONLY = 2,
|
||||
GIT_STATUS_SHOW_INDEX_THEN_WORKDIR = 3,
|
||||
} git_status_show_t;
|
||||
|
||||
/**
|
||||
* Flags to control status callbacks
|
||||
*
|
||||
* - GIT_STATUS_OPT_INCLUDE_UNTRACKED says that callbacks should
|
||||
* be made on untracked files. These will only be made if the
|
||||
* workdir files are included in the status "show" option.
|
||||
* - GIT_STATUS_OPT_INCLUDE_IGNORED says that ignored files should
|
||||
* get callbacks. Again, these callbacks will only be made if
|
||||
* the workdir files are included in the status "show" option.
|
||||
* Right now, there is no option to include all files in
|
||||
* directories that are ignored completely.
|
||||
* - GIT_STATUS_OPT_INCLUDE_UNMODIFIED indicates that callback
|
||||
* should be made even on unmodified files.
|
||||
* - GIT_STATUS_OPT_EXCLUDE_SUBMODULES indicates that directories
|
||||
* which appear to be submodules should just be skipped over.
|
||||
* - GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS indicates that the
|
||||
* contents of untracked directories should be included in the
|
||||
* status. Normally if an entire directory is new, then just
|
||||
* the top-level directory will be included (with a trailing
|
||||
* slash on the entry name). Given this flag, the directory
|
||||
* itself will not be included, but all the files in it will.
|
||||
*/
|
||||
|
||||
enum {
|
||||
GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1 << 0),
|
||||
GIT_STATUS_OPT_INCLUDE_IGNORED = (1 << 1),
|
||||
GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1 << 2),
|
||||
GIT_STATUS_OPT_EXCLUDE_SUBMODULED = (1 << 3),
|
||||
GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1 << 4),
|
||||
};
|
||||
|
||||
/**
|
||||
* Options to control how callbacks will be made by
|
||||
* `git_status_foreach_ext()`.
|
||||
*/
|
||||
typedef struct {
|
||||
git_status_show_t show;
|
||||
unsigned int flags;
|
||||
git_strarray pathspec;
|
||||
} git_status_options;
|
||||
|
||||
/**
|
||||
* Gather file status information and run callbacks as requested.
|
||||
*/
|
||||
GIT_EXTERN(int) git_status_foreach_ext(
|
||||
git_repository *repo,
|
||||
const git_status_options *opts,
|
||||
int (*callback)(const char *, unsigned int, void *),
|
||||
void *payload);
|
||||
|
||||
/**
|
||||
* Get file status for a single file
|
||||
@ -54,9 +133,12 @@ GIT_EXTERN(int) git_status_foreach(git_repository *repo, int (*callback)(const c
|
||||
* @param path the file to retrieve status for, rooted at the repo's workdir
|
||||
* @return GIT_EINVALIDPATH when `path` points at a folder, GIT_ENOTFOUND when
|
||||
* the file doesn't exist in any of HEAD, the index or the worktree,
|
||||
* GIT_SUCCESS otherwise
|
||||
* 0 otherwise
|
||||
*/
|
||||
GIT_EXTERN(int) git_status_file(unsigned int *status_flags, git_repository *repo, const char *path);
|
||||
GIT_EXTERN(int) git_status_file(
|
||||
unsigned int *status_flags,
|
||||
git_repository *repo,
|
||||
const char *path);
|
||||
|
||||
/**
|
||||
* Test if the ignore rules apply to a given file.
|
||||
@ -66,13 +148,16 @@ GIT_EXTERN(int) git_status_file(unsigned int *status_flags, git_repository *repo
|
||||
* would be ignored regardless of whether the file is already in the index
|
||||
* or in the repository.
|
||||
*
|
||||
* @param repo a repository object
|
||||
* @param path the file to check ignores for, rooted at the repo's workdir
|
||||
* @param ignored boolean returning 0 if the file is not ignored, 1 if it is
|
||||
* @return GIT_SUCCESS if the ignore rules could be processed for the file
|
||||
* (regardless of whether it exists or not), or an error < 0 if they could not.
|
||||
* @param repo a repository object
|
||||
* @param path the file to check ignores for, rooted at the repo's workdir.
|
||||
* @return 0 if ignore rules could be processed for the file (regardless
|
||||
* of whether it exists or not), or an error < 0 if they could not.
|
||||
*/
|
||||
GIT_EXTERN(int) git_status_should_ignore(git_repository *repo, const char *path, int *ignored);
|
||||
GIT_EXTERN(int) git_status_should_ignore(
|
||||
int *ignored,
|
||||
git_repository *repo,
|
||||
const char *path);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
|
103
include/git2/submodule.h
Normal file
103
include/git2/submodule.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (C) 2012 the libgit2 contributors
|
||||
*
|
||||
* 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_submodule_h__
|
||||
#define INCLUDE_git_submodule_h__
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
#include "oid.h"
|
||||
|
||||
/**
|
||||
* @file git2/submodule.h
|
||||
* @brief Git submodule management utilities
|
||||
* @defgroup git_submodule Git submodule management routines
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
typedef enum {
|
||||
GIT_SUBMODULE_UPDATE_CHECKOUT = 0,
|
||||
GIT_SUBMODULE_UPDATE_REBASE = 1,
|
||||
GIT_SUBMODULE_UPDATE_MERGE = 2
|
||||
} git_submodule_update_t;
|
||||
|
||||
typedef enum {
|
||||
GIT_SUBMODULE_IGNORE_ALL = 0, /* never dirty */
|
||||
GIT_SUBMODULE_IGNORE_DIRTY = 1, /* only dirty if HEAD moved */
|
||||
GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /* dirty if tracked files change */
|
||||
GIT_SUBMODULE_IGNORE_NONE = 3 /* any change or untracked == dirty */
|
||||
} git_submodule_ignore_t;
|
||||
|
||||
/**
|
||||
* Description of submodule
|
||||
*
|
||||
* This record describes a submodule found in a repository. There
|
||||
* should be an entry for every submodule found in the HEAD and for
|
||||
* every submodule described in .gitmodules. The fields are as follows:
|
||||
*
|
||||
* - `name` is the name of the submodule from .gitmodules.
|
||||
* - `path` is the path to the submodule from the repo working directory.
|
||||
* It is almost always the same as `name`.
|
||||
* - `url` is the url for the submodule.
|
||||
* - `oid` is the HEAD SHA1 for the submodule.
|
||||
* - `update` is a value from above - see gitmodules(5) update.
|
||||
* - `ignore` is a value from above - see gitmodules(5) ignore.
|
||||
* - `fetch_recurse` is 0 or 1 - see gitmodules(5) fetchRecurseSubmodules.
|
||||
* - `refcount` is for internal use.
|
||||
*
|
||||
* If the submodule has been added to .gitmodules but not yet git added,
|
||||
* then the `oid` will be zero. If the submodule has been deleted, but
|
||||
* the delete has not been committed yet, then the `oid` will be set, but
|
||||
* the `url` will be NULL.
|
||||
*/
|
||||
typedef struct {
|
||||
char *name;
|
||||
char *path;
|
||||
char *url;
|
||||
git_oid oid; /* sha1 of submodule HEAD ref or zero if not committed */
|
||||
git_submodule_update_t update;
|
||||
git_submodule_ignore_t ignore;
|
||||
int fetch_recurse;
|
||||
int refcount;
|
||||
} git_submodule;
|
||||
|
||||
/**
|
||||
* Iterate over all submodules of a repository.
|
||||
*
|
||||
* @param repo The repository
|
||||
* @param callback Function to be called with the name of each submodule.
|
||||
* Return a non-zero value to terminate the iteration.
|
||||
* @param payload Extra data to pass to callback
|
||||
* @return 0 on success, -1 on error, or non-zero return value of callback
|
||||
*/
|
||||
GIT_EXTERN(int) git_submodule_foreach(
|
||||
git_repository *repo,
|
||||
int (*callback)(const char *name, void *payload),
|
||||
void *payload);
|
||||
|
||||
/**
|
||||
* Lookup submodule information by name or path.
|
||||
*
|
||||
* Given either the submodule name or path (they are ususally the same),
|
||||
* this returns a structure describing the submodule. If the submodule
|
||||
* does not exist, this will return GIT_ENOTFOUND and set the submodule
|
||||
* pointer to NULL.
|
||||
*
|
||||
* @param submodule Pointer to submodule description object pointer..
|
||||
* @param repo The repository.
|
||||
* @param name The name of the submodule. Trailing slashes will be ignored.
|
||||
* @return 0 on success, GIT_ENOTFOUND if submodule does not exist, -1 on error
|
||||
*/
|
||||
GIT_EXTERN(int) git_submodule_lookup(
|
||||
git_submodule **submodule,
|
||||
git_repository *repo,
|
||||
const char *name);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -27,7 +27,7 @@ GIT_BEGIN_DECL
|
||||
* @param tag pointer to the looked up tag
|
||||
* @param repo the repo to use when locating the tag.
|
||||
* @param id identity of the tag to locate.
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_tag_lookup(git_tag **tag, git_repository *repo, const git_oid *id)
|
||||
{
|
||||
@ -44,7 +44,7 @@ GIT_INLINE(int) git_tag_lookup(git_tag **tag, git_repository *repo, const git_oi
|
||||
* @param repo the repo to use when locating the tag.
|
||||
* @param id identity of the tag to locate.
|
||||
* @param len the length of the short identifier
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_tag_lookup_prefix(git_tag **tag, git_repository *repo, const git_oid *id, unsigned int len)
|
||||
{
|
||||
@ -85,7 +85,7 @@ GIT_EXTERN(const git_oid *) git_tag_id(git_tag *tag);
|
||||
*
|
||||
* @param target pointer where to store the target
|
||||
* @param tag a previously loaded tag.
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_tag_target(git_object **target, git_tag *tag);
|
||||
|
||||
@ -137,6 +137,9 @@ GIT_EXTERN(const char *) git_tag_message(git_tag *tag);
|
||||
* this tag object. If `force` is true and a reference
|
||||
* already exists with the given name, it'll be replaced.
|
||||
*
|
||||
* The message will be cleaned up from excess whitespace
|
||||
* it will be made sure that the last line ends with a '\n'.
|
||||
*
|
||||
* @param oid Pointer where to store the OID of the
|
||||
* newly created tag. If the tag already exists, this parameter
|
||||
* will be the oid of the existing tag, and the function will
|
||||
@ -158,7 +161,7 @@ GIT_EXTERN(const char *) git_tag_message(git_tag *tag);
|
||||
*
|
||||
* @param force Overwrite existing references
|
||||
*
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
* A tag object is written to the ODB, and a proper reference
|
||||
* is written in the /refs/tags folder, pointing to it
|
||||
*/
|
||||
@ -209,7 +212,7 @@ GIT_EXTERN(int) git_tag_create_frombuffer(
|
||||
*
|
||||
* @param force Overwrite existing references
|
||||
*
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
* A proper reference is written in the /refs/tags folder,
|
||||
* pointing to the provided target object
|
||||
*/
|
||||
@ -228,7 +231,7 @@ GIT_EXTERN(int) git_tag_create_lightweight(
|
||||
* @param tag_name Name of the tag to be deleted;
|
||||
* this name is validated for consistency.
|
||||
*
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_tag_delete(
|
||||
git_repository *repo,
|
||||
@ -245,7 +248,7 @@ GIT_EXTERN(int) git_tag_delete(
|
||||
* @param tag_names Pointer to a git_strarray structure where
|
||||
* the tag names will be stored
|
||||
* @param repo Repository where to find the tags
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_tag_list(
|
||||
git_strarray *tag_names,
|
||||
@ -267,13 +270,28 @@ GIT_EXTERN(int) git_tag_list(
|
||||
* the tag names will be stored
|
||||
* @param pattern Standard fnmatch pattern
|
||||
* @param repo Repository where to find the tags
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_tag_list_match(
|
||||
git_strarray *tag_names,
|
||||
const char *pattern,
|
||||
git_repository *repo);
|
||||
|
||||
/**
|
||||
* Recursively peel a tag until a non tag git_object
|
||||
* is met
|
||||
*
|
||||
* The retrieved `tag_target` object is owned by the repository
|
||||
* and should be closed with the `git_object_free` method.
|
||||
*
|
||||
* @param tag_target Pointer to the peeled git_object
|
||||
* @param tag The tag to be processed
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_tag_peel(
|
||||
git_object **tag_target,
|
||||
git_tag *tag);
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -27,7 +27,7 @@ GIT_BEGIN_DECL
|
||||
* @param tree pointer to the looked up tree
|
||||
* @param repo the repo to use when locating the tree.
|
||||
* @param id identity of the tree to locate.
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_tree_lookup(git_tree **tree, git_repository *repo, const git_oid *id)
|
||||
{
|
||||
@ -44,7 +44,7 @@ GIT_INLINE(int) git_tree_lookup(git_tree **tree, git_repository *repo, const git
|
||||
* @param repo the repo to use when locating the tree.
|
||||
* @param id identity of the tree to locate.
|
||||
* @param len the length of the short identifier
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_INLINE(int) git_tree_lookup_prefix(git_tree **tree, git_repository *repo, const git_oid *id, unsigned int len)
|
||||
{
|
||||
@ -141,9 +141,9 @@ GIT_EXTERN(git_otype) git_tree_entry_type(const git_tree_entry *entry);
|
||||
* @param object pointer to the converted object
|
||||
* @param repo repository where to lookup the pointed object
|
||||
* @param entry a tree entry
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_tree_entry_2object(git_object **object_out, git_repository *repo, const git_tree_entry *entry);
|
||||
GIT_EXTERN(int) git_tree_entry_to_object(git_object **object_out, git_repository *repo, const git_tree_entry *entry);
|
||||
|
||||
/**
|
||||
* Write a tree to the ODB from the index file
|
||||
@ -159,7 +159,7 @@ GIT_EXTERN(int) git_tree_entry_2object(git_object **object_out, git_repository *
|
||||
*
|
||||
* @param oid Pointer where to store the written tree
|
||||
* @param index Index to write
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_tree_create_fromindex(git_oid *oid, git_index *index);
|
||||
|
||||
@ -229,7 +229,7 @@ GIT_EXTERN(const git_tree_entry *) git_treebuilder_get(git_treebuilder *bld, con
|
||||
* @param filename Filename of the entry
|
||||
* @param id SHA1 oid of the entry
|
||||
* @param attributes Folder attributes of the entry
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_treebuilder_insert(git_tree_entry **entry_out, git_treebuilder *bld, const char *filename, const git_oid *id, unsigned int attributes);
|
||||
|
||||
@ -264,7 +264,7 @@ GIT_EXTERN(void) git_treebuilder_filter(git_treebuilder *bld, int (*filter)(cons
|
||||
* @param oid Pointer where to store the written OID
|
||||
* @param repo Repository where to store the object
|
||||
* @param bld Tree builder to write
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_treebuilder_write(git_oid *oid, git_repository *repo, git_treebuilder *bld);
|
||||
|
||||
@ -278,8 +278,7 @@ GIT_EXTERN(int) git_treebuilder_write(git_oid *oid, git_repository *repo, git_tr
|
||||
* @param subtree Pointer where to store the subtree
|
||||
* @param root A previously loaded tree which will be the root of the relative path
|
||||
* @param subtree_path Path to the contained subtree
|
||||
* @return GIT_SUCCESS on success; GIT_ENOTFOUND if the path does not lead to a
|
||||
* subtree, GIT_EINVALIDPATH or an error code
|
||||
* @return 0 on success; GIT_ENOTFOUND if the path does not lead to a subtree
|
||||
*/
|
||||
GIT_EXTERN(int) git_tree_get_subtree(git_tree **subtree, git_tree *root, const char *subtree_path);
|
||||
|
||||
@ -309,45 +308,11 @@ enum git_treewalk_mode {
|
||||
* @param callback Function to call on each tree entry
|
||||
* @param mode Traversal mode (pre or post-order)
|
||||
* @param payload Opaque pointer to be passed on each callback
|
||||
* @return GIT_SUCCESS or an error code
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payload);
|
||||
|
||||
/** @} */
|
||||
|
||||
typedef enum {
|
||||
GIT_STATUS_ADDED = 1,
|
||||
GIT_STATUS_DELETED = 2,
|
||||
GIT_STATUS_MODIFIED = 3,
|
||||
} git_status_t;
|
||||
|
||||
typedef struct {
|
||||
unsigned int old_attr;
|
||||
unsigned int new_attr;
|
||||
git_oid old_oid;
|
||||
git_oid new_oid;
|
||||
git_status_t status;
|
||||
const char *path;
|
||||
} git_tree_diff_data;
|
||||
|
||||
typedef int (*git_tree_diff_cb)(const git_tree_diff_data *ptr, void *data);
|
||||
|
||||
/**
|
||||
* Diff two trees
|
||||
*
|
||||
* Compare two trees. For each difference in the trees, the callback
|
||||
* will be called with a git_tree_diff_data filled with the relevant
|
||||
* information.
|
||||
*
|
||||
* @param old the "old" tree
|
||||
* @param newer the "newer" tree
|
||||
* @param cb callback
|
||||
* @param data data to give to the callback
|
||||
* @return GIT_SUCCESS or an error code
|
||||
*/
|
||||
int git_tree_diff(git_tree *old, git_tree *newer, git_tree_diff_cb cb, void *data);
|
||||
|
||||
int git_tree_diff_index_recursive(git_tree *tree, git_index *index, git_tree_diff_cb cb, void *data);
|
||||
|
||||
GIT_END_DECL
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -131,6 +131,9 @@ typedef struct git_reflog_entry git_reflog_entry;
|
||||
/** Representation of a reference log */
|
||||
typedef struct git_reflog git_reflog;
|
||||
|
||||
/** Representation of a git note */
|
||||
typedef struct git_note git_note;
|
||||
|
||||
/** Time in a signature */
|
||||
typedef struct git_time {
|
||||
git_time_t time; /** time in seconds from epoch */
|
||||
@ -155,8 +158,13 @@ typedef enum {
|
||||
GIT_REF_PACKED = 4,
|
||||
GIT_REF_HAS_PEEL = 8,
|
||||
GIT_REF_LISTALL = GIT_REF_OID|GIT_REF_SYMBOLIC|GIT_REF_PACKED,
|
||||
} git_rtype;
|
||||
} git_ref_t;
|
||||
|
||||
/** Basic type of any Git branch. */
|
||||
typedef enum {
|
||||
GIT_BRANCH_LOCAL = 1,
|
||||
GIT_BRANCH_REMOTE = 2,
|
||||
} git_branch_t;
|
||||
|
||||
typedef struct git_refspec git_refspec;
|
||||
typedef struct git_remote git_remote;
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -7,9 +7,9 @@
|
||||
#ifndef INCLUDE_git_version_h__
|
||||
#define INCLUDE_git_version_h__
|
||||
|
||||
#define LIBGIT2_VERSION "0.16.0"
|
||||
#define LIBGIT2_VERSION "0.17.0"
|
||||
#define LIBGIT2_VER_MAJOR 0
|
||||
#define LIBGIT2_VER_MINOR 16
|
||||
#define LIBGIT2_VER_MINOR 17
|
||||
#define LIBGIT2_VER_REVISION 0
|
||||
|
||||
#endif
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
|
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
*
|
||||
* 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_zlib_h__
|
||||
#define INCLUDE_git_zlib_h__
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
/**
|
||||
* @file git2/zlib.h
|
||||
* @brief Git data compression routines
|
||||
* @defgroup git_zlib Git data compression routines
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
#if defined(NO_DEFLATE_BOUND) || ZLIB_VERNUM < 0x1200
|
||||
/**
|
||||
* deflateBound returns an upper bound on the compressed size.
|
||||
*
|
||||
* This is a stub function used when zlib does not supply the
|
||||
* deflateBound() implementation itself.
|
||||
*
|
||||
* @param stream the stream pointer.
|
||||
* @param s total length of the source data (in bytes).
|
||||
* @return maximum length of the compressed data.
|
||||
*/
|
||||
GIT_INLINE(size_t) deflateBound(z_streamp stream, size_t s)
|
||||
{
|
||||
return (s + ((s + 7) >> 3) + ((s + 63) >> 6) + 11);
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
GIT_END_DECL
|
||||
#endif
|
6
packaging/rpm/README
Normal file
6
packaging/rpm/README
Normal file
@ -0,0 +1,6 @@
|
||||
To build RPM pakcages for Fedora, follow these steps:
|
||||
cp packaging/rpm/libgit2.spec ~/rpmbuild/SPECS
|
||||
cd ~/rpmbuild/SOURCES
|
||||
wget https://github.com/downloads/libgit2/libgit2/libgit2-0.16.0.tar.gz
|
||||
cd ~/rpmbuild/SPECS
|
||||
rpmbuild -ba libgit2.spec
|
106
packaging/rpm/libgit2.spec
Normal file
106
packaging/rpm/libgit2.spec
Normal file
@ -0,0 +1,106 @@
|
||||
#
|
||||
# spec file for package libgit2
|
||||
#
|
||||
# Copyright (c) 2012 Saleem Ansari <tuxdna@gmail.com>
|
||||
# Copyright (c) 2012 SUSE LINUX Products GmbH, Nuernberg, Germany.
|
||||
# Copyright (c) 2011, Sascha Peilicke <saschpe@gmx.de>
|
||||
#
|
||||
# All modifications and additions to the file contributed by third parties
|
||||
# remain the property of their copyright owners, unless otherwise agreed
|
||||
# upon. The license for this file, and modifications and additions to the
|
||||
# file, is the same license as for the pristine package itself (unless the
|
||||
# license for the pristine package is not an Open Source License, in which
|
||||
# case the license is the MIT License). An "Open Source License" is a
|
||||
# license that conforms to the Open Source Definition (Version 1.9)
|
||||
# published by the Open Source Initiative.
|
||||
|
||||
# Please submit bugfixes or comments via http://bugs.opensuse.org/
|
||||
#
|
||||
Name: libgit2
|
||||
Version: 0.16.0
|
||||
Release: 1
|
||||
Summary: C git library
|
||||
License: GPL-2.0 with linking
|
||||
Group: Development/Libraries/C and C++
|
||||
Url: http://libgit2.github.com/
|
||||
Source0: https://github.com/downloads/libgit2/libgit2/libgit2-0.16.0.tar.gz
|
||||
BuildRequires: cmake
|
||||
BuildRequires: pkgconfig
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-build
|
||||
%if 0%{?fedora} || 0%{?rhel_version} || 0%{?centos_version}
|
||||
BuildRequires: openssl-devel
|
||||
%else
|
||||
BuildRequires: libopenssl-devel
|
||||
%endif
|
||||
|
||||
%description
|
||||
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.
|
||||
|
||||
%package -n %{name}-0
|
||||
Summary: C git library
|
||||
Group: System/Libraries
|
||||
|
||||
%description -n %{name}-0
|
||||
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.
|
||||
|
||||
%package devel
|
||||
Summary: C git library
|
||||
Group: Development/Libraries/C and C++
|
||||
Requires: %{name}-0 >= %{version}
|
||||
|
||||
%description devel
|
||||
This package contains all necessary include files and libraries needed
|
||||
to compile and develop applications that use libgit2.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
cmake . \
|
||||
-DCMAKE_C_FLAGS:STRING="%{optflags}" \
|
||||
-DCMAKE_INSTALL_PREFIX:PATH=%{_prefix} \
|
||||
-DINSTALL_LIB:PATH=%{_libdir}
|
||||
make %{?_smp_mflags}
|
||||
|
||||
%install
|
||||
%make_install
|
||||
|
||||
%post -n %{name}-0 -p /sbin/ldconfig
|
||||
%postun -n %{name}-0 -p /sbin/ldconfig
|
||||
|
||||
%files -n %{name}-0
|
||||
%defattr (-,root,root)
|
||||
%doc AUTHORS COPYING README.md
|
||||
%{_libdir}/%{name}.so.*
|
||||
|
||||
%files devel
|
||||
%defattr (-,root,root)
|
||||
%doc CONVENTIONS examples
|
||||
%{_libdir}/%{name}.so
|
||||
%{_includedir}/git2*
|
||||
%{_libdir}/pkgconfig/libgit2.pc
|
||||
|
||||
%changelog
|
||||
* Tue Mar 04 2012 tuxdna@gmail.com
|
||||
- Update to version 0.16.0
|
||||
* Tue Jan 31 2012 jengelh@medozas.de
|
||||
- Provide pkgconfig symbols
|
||||
* Thu Oct 27 2011 saschpe@suse.de
|
||||
- Change license to 'GPL-2.0 with linking', fixes bnc#726789
|
||||
* Wed Oct 26 2011 saschpe@suse.de
|
||||
- Update to version 0.15.0:
|
||||
* Upstream doesn't provide changes
|
||||
- Removed outdated %%clean section
|
||||
* Tue Jan 18 2011 saschpe@gmx.de
|
||||
- Proper Requires for devel package
|
||||
* Tue Jan 18 2011 saschpe@gmx.de
|
||||
- Set BuildRequires to "openssl-devel" also for RHEL and CentOS
|
||||
* Tue Jan 18 2011 saschpe@gmx.de
|
||||
- Initial commit (0.0.1)
|
||||
- Added patch to fix shared library soname
|
576
src/attr.c
576
src/attr.c
@ -3,13 +3,21 @@
|
||||
#include "config.h"
|
||||
#include <ctype.h>
|
||||
|
||||
GIT__USE_STRMAP;
|
||||
|
||||
static int collect_attr_files(
|
||||
git_repository *repo, const char *path, git_vector *files);
|
||||
git_repository *repo,
|
||||
uint32_t flags,
|
||||
const char *path,
|
||||
git_vector *files);
|
||||
|
||||
|
||||
int git_attr_get(
|
||||
git_repository *repo, const char *pathname,
|
||||
const char *name, const char **value)
|
||||
const char **value,
|
||||
git_repository *repo,
|
||||
uint32_t flags,
|
||||
const char *pathname,
|
||||
const char *name)
|
||||
{
|
||||
int error;
|
||||
git_attr_path path;
|
||||
@ -21,10 +29,11 @@ int git_attr_get(
|
||||
|
||||
*value = NULL;
|
||||
|
||||
if ((error = git_attr_path__init(
|
||||
&path, pathname, git_repository_workdir(repo))) < GIT_SUCCESS ||
|
||||
(error = collect_attr_files(repo, pathname, &files)) < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Could not get attribute for %s", pathname);
|
||||
if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
|
||||
return -1;
|
||||
|
||||
if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
attr.name = name;
|
||||
attr.name_hash = git_attr_file__name_hash(name);
|
||||
@ -33,18 +42,17 @@ int git_attr_get(
|
||||
|
||||
git_attr_file__foreach_matching_rule(file, &path, j, rule) {
|
||||
int pos = git_vector_bsearch(&rule->assigns, &attr);
|
||||
git_clearerror(); /* okay if search failed */
|
||||
|
||||
if (pos >= 0) {
|
||||
*value = ((git_attr_assignment *)git_vector_get(
|
||||
&rule->assigns, pos))->value;
|
||||
goto found;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
found:
|
||||
cleanup:
|
||||
git_vector_free(&files);
|
||||
git_attr_path__free(&path);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -56,8 +64,12 @@ typedef struct {
|
||||
} attr_get_many_info;
|
||||
|
||||
int git_attr_get_many(
|
||||
git_repository *repo, const char *pathname,
|
||||
size_t num_attr, const char **names, const char **values)
|
||||
const char **values,
|
||||
git_repository *repo,
|
||||
uint32_t flags,
|
||||
const char *pathname,
|
||||
size_t num_attr,
|
||||
const char **names)
|
||||
{
|
||||
int error;
|
||||
git_attr_path path;
|
||||
@ -70,15 +82,14 @@ int git_attr_get_many(
|
||||
|
||||
memset((void *)values, 0, sizeof(const char *) * num_attr);
|
||||
|
||||
if ((error = git_attr_path__init(
|
||||
&path, pathname, git_repository_workdir(repo))) < GIT_SUCCESS ||
|
||||
(error = collect_attr_files(repo, pathname, &files)) < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Could not get attributes for %s", pathname);
|
||||
if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
|
||||
return -1;
|
||||
|
||||
if ((info = git__calloc(num_attr, sizeof(attr_get_many_info))) == NULL) {
|
||||
git__rethrow(GIT_ENOMEM, "Could not get attributes for %s", pathname);
|
||||
if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
info = git__calloc(num_attr, sizeof(attr_get_many_info));
|
||||
GITERR_CHECK_ALLOC(info);
|
||||
|
||||
git_vector_foreach(&files, i, file) {
|
||||
|
||||
@ -96,8 +107,6 @@ int git_attr_get_many(
|
||||
}
|
||||
|
||||
pos = git_vector_bsearch(&rule->assigns, &info[k].name);
|
||||
git_clearerror(); /* okay if search failed */
|
||||
|
||||
if (pos >= 0) {
|
||||
info[k].found = (git_attr_assignment *)
|
||||
git_vector_get(&rule->assigns, pos);
|
||||
@ -112,6 +121,7 @@ int git_attr_get_many(
|
||||
|
||||
cleanup:
|
||||
git_vector_free(&files);
|
||||
git_attr_path__free(&path);
|
||||
git__free(info);
|
||||
|
||||
return error;
|
||||
@ -119,7 +129,9 @@ cleanup:
|
||||
|
||||
|
||||
int git_attr_foreach(
|
||||
git_repository *repo, const char *pathname,
|
||||
git_repository *repo,
|
||||
uint32_t flags,
|
||||
const char *pathname,
|
||||
int (*callback)(const char *name, const char *value, void *payload),
|
||||
void *payload)
|
||||
{
|
||||
@ -130,18 +142,16 @@ int git_attr_foreach(
|
||||
git_attr_file *file;
|
||||
git_attr_rule *rule;
|
||||
git_attr_assignment *assign;
|
||||
git_hashtable *seen = NULL;
|
||||
git_strmap *seen = NULL;
|
||||
|
||||
if ((error = git_attr_path__init(
|
||||
&path, pathname, git_repository_workdir(repo))) < GIT_SUCCESS ||
|
||||
(error = collect_attr_files(repo, pathname, &files)) < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Could not get attributes for %s", pathname);
|
||||
if (git_attr_path__init(&path, pathname, git_repository_workdir(repo)) < 0)
|
||||
return -1;
|
||||
|
||||
seen = git_hashtable_alloc(8, git_hash__strhash_cb, git_hash__strcmp_cb);
|
||||
if (!seen) {
|
||||
error = GIT_ENOMEM;
|
||||
if ((error = collect_attr_files(repo, flags, pathname, &files)) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
seen = git_strmap_alloc();
|
||||
GITERR_CHECK_ALLOC(seen);
|
||||
|
||||
git_vector_foreach(&files, i, file) {
|
||||
|
||||
@ -149,27 +159,23 @@ int git_attr_foreach(
|
||||
|
||||
git_vector_foreach(&rule->assigns, k, assign) {
|
||||
/* skip if higher priority assignment was already seen */
|
||||
if (git_hashtable_lookup(seen, assign->name))
|
||||
if (git_strmap_exists(seen, assign->name))
|
||||
continue;
|
||||
|
||||
error = git_hashtable_insert(seen, assign->name, assign);
|
||||
if (error != GIT_SUCCESS)
|
||||
goto cleanup;
|
||||
git_strmap_insert(seen, assign->name, assign, error);
|
||||
if (error >= 0)
|
||||
error = callback(assign->name, assign->value, payload);
|
||||
|
||||
error = callback(assign->name, assign->value, payload);
|
||||
if (error != GIT_SUCCESS)
|
||||
if (error != 0)
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (seen)
|
||||
git_hashtable_free(seen);
|
||||
git_strmap_free(seen);
|
||||
git_vector_free(&files);
|
||||
|
||||
if (error != GIT_SUCCESS)
|
||||
(void)git__rethrow(error, "Could not get attributes for %s", pathname);
|
||||
git_attr_path__free(&path);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -182,124 +188,332 @@ int git_attr_add_macro(
|
||||
{
|
||||
int error;
|
||||
git_attr_rule *macro = NULL;
|
||||
git_pool *pool;
|
||||
|
||||
if ((error = git_attr_cache__init(repo)) < GIT_SUCCESS)
|
||||
return error;
|
||||
if (git_attr_cache__init(repo) < 0)
|
||||
return -1;
|
||||
|
||||
macro = git__calloc(1, sizeof(git_attr_rule));
|
||||
if (!macro)
|
||||
return GIT_ENOMEM;
|
||||
GITERR_CHECK_ALLOC(macro);
|
||||
|
||||
macro->match.pattern = git__strdup(name);
|
||||
if (!macro->match.pattern) {
|
||||
git__free(macro);
|
||||
return GIT_ENOMEM;
|
||||
}
|
||||
pool = &git_repository_attr_cache(repo)->pool;
|
||||
|
||||
macro->match.pattern = git_pool_strdup(pool, name);
|
||||
GITERR_CHECK_ALLOC(macro->match.pattern);
|
||||
|
||||
macro->match.length = strlen(macro->match.pattern);
|
||||
macro->match.flags = GIT_ATTR_FNMATCH_MACRO;
|
||||
|
||||
error = git_attr_assignment__parse(repo, ¯o->assigns, &values);
|
||||
error = git_attr_assignment__parse(repo, pool, ¯o->assigns, &values);
|
||||
|
||||
if (error == GIT_SUCCESS)
|
||||
if (!error)
|
||||
error = git_attr_cache__insert_macro(repo, macro);
|
||||
|
||||
if (error < GIT_SUCCESS)
|
||||
if (error < 0)
|
||||
git_attr_rule__free(macro);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_attr_cache__is_cached(git_repository *repo, const char *path)
|
||||
bool git_attr_cache__is_cached(
|
||||
git_repository *repo, git_attr_file_source source, const char *path)
|
||||
{
|
||||
const char *cache_key = path;
|
||||
if (repo && git__prefixcmp(cache_key, git_repository_workdir(repo)) == 0)
|
||||
cache_key += strlen(git_repository_workdir(repo));
|
||||
return (git_hashtable_lookup(repo->attrcache.files, cache_key) == NULL);
|
||||
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;
|
||||
}
|
||||
|
||||
/* add git_attr_file to vector of files, loading if needed */
|
||||
int git_attr_cache__push_file(
|
||||
git_repository *repo,
|
||||
git_vector *stack,
|
||||
const char *base,
|
||||
const char *filename,
|
||||
int (*loader)(git_repository *, const char *, git_attr_file *))
|
||||
static int load_attr_file(
|
||||
const char **data,
|
||||
git_attr_file_stat_sig *sig,
|
||||
const char *filename)
|
||||
{
|
||||
int error = GIT_SUCCESS;
|
||||
git_attr_cache *cache = &repo->attrcache;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
git_attr_file *file = NULL;
|
||||
int add_to_cache = 0;
|
||||
const char *cache_key;
|
||||
int error;
|
||||
git_buf content = GIT_BUF_INIT;
|
||||
struct stat st;
|
||||
|
||||
if (base != NULL) {
|
||||
if ((error = git_buf_joinpath(&path, base, filename)) < GIT_SUCCESS)
|
||||
goto cleanup;
|
||||
filename = path.ptr;
|
||||
if (p_stat(filename, &st) < 0)
|
||||
return GIT_ENOTFOUND;
|
||||
|
||||
if (sig != NULL &&
|
||||
(git_time_t)st.st_mtime == sig->seconds &&
|
||||
(git_off_t)st.st_size == sig->size &&
|
||||
(unsigned int)st.st_ino == sig->ino)
|
||||
return GIT_ENOTFOUND;
|
||||
|
||||
error = git_futils_readbuffer_updated(&content, filename, NULL, NULL);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
if (sig != NULL) {
|
||||
sig->seconds = (git_time_t)st.st_mtime;
|
||||
sig->size = (git_off_t)st.st_size;
|
||||
sig->ino = (unsigned int)st.st_ino;
|
||||
}
|
||||
|
||||
/* either get attr_file from cache or read from disk */
|
||||
cache_key = filename;
|
||||
if (repo && git__prefixcmp(cache_key, git_repository_workdir(repo)) == 0)
|
||||
cache_key += strlen(git_repository_workdir(repo));
|
||||
*data = git_buf_detach(&content);
|
||||
|
||||
file = git_hashtable_lookup(cache->files, cache_key);
|
||||
if (file == NULL && git_path_exists(filename) == GIT_SUCCESS) {
|
||||
if ((error = git_attr_file__new(&file)) == GIT_SUCCESS) {
|
||||
if ((error = loader(repo, filename, file)) < GIT_SUCCESS) {
|
||||
git_attr_file__free(file);
|
||||
file = NULL;
|
||||
}
|
||||
}
|
||||
add_to_cache = (error == GIT_SUCCESS);
|
||||
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;
|
||||
git_index *index;
|
||||
git_index_entry *entry;
|
||||
|
||||
if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
|
||||
(error = git_index_find(index, relfile)) < 0)
|
||||
return error;
|
||||
|
||||
entry = git_index_get(index, error);
|
||||
|
||||
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 (error == GIT_SUCCESS && file != NULL) {
|
||||
/* add file to vector, if we found it */
|
||||
error = git_vector_insert(stack, file);
|
||||
if (git_attr_file__new(file, 0, filename, &cache->pool) < 0)
|
||||
return -1;
|
||||
|
||||
/* add file to cache, if it is new */
|
||||
/* do this after above step b/c it is not critical */
|
||||
if (error == GIT_SUCCESS && add_to_cache && file->path != NULL)
|
||||
error = git_hashtable_insert(cache->files, file->path, file);
|
||||
}
|
||||
git_strmap_insert(cache->files, (*file)->key + 2, *file, error);
|
||||
if (error > 0)
|
||||
error = 0;
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&path);
|
||||
return error;
|
||||
}
|
||||
|
||||
#define push_attrs(R,S,B,F) \
|
||||
git_attr_cache__push_file((R),(S),(B),(F),git_attr_file__from_file)
|
||||
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,
|
||||
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_attr_file_stat_sig st;
|
||||
|
||||
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) {
|
||||
if (file)
|
||||
memcpy(&st, &file->cache_data.st, sizeof(st));
|
||||
else
|
||||
memset(&st, 0, sizeof(st));
|
||||
|
||||
error = load_attr_file(&content, &st, 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 (!file &&
|
||||
(error = git_attr_file__new(&file, source, relfile, &cache->pool)) < 0)
|
||||
goto finish;
|
||||
|
||||
if (parse && (error = parse(repo, content, file)) < 0)
|
||||
goto finish;
|
||||
|
||||
git_strmap_insert(cache->files, file->key, file, error);
|
||||
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
|
||||
memcpy(&file->cache_data.st, &st, sizeof(st));
|
||||
|
||||
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,(S))
|
||||
|
||||
typedef struct {
|
||||
git_repository *repo;
|
||||
uint32_t flags;
|
||||
const char *workdir;
|
||||
git_index *index;
|
||||
git_vector *files;
|
||||
} attr_walk_up_info;
|
||||
|
||||
int git_attr_cache__decide_sources(
|
||||
uint32_t flags, bool has_wd, bool has_index, git_attr_file_source *srcs)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
switch (flags & 0x03) {
|
||||
case GIT_ATTR_CHECK_FILE_THEN_INDEX:
|
||||
if (has_wd)
|
||||
srcs[count++] = GIT_ATTR_FILE_FROM_FILE;
|
||||
if (has_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;
|
||||
if (has_wd)
|
||||
srcs[count++] = GIT_ATTR_FILE_FROM_FILE;
|
||||
break;
|
||||
case GIT_ATTR_CHECK_INDEX_ONLY:
|
||||
if (has_index)
|
||||
srcs[count++] = GIT_ATTR_FILE_FROM_INDEX;
|
||||
break;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
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;
|
||||
return push_attrs(info->repo, info->files, path->ptr, GIT_ATTR_FILE);
|
||||
git_attr_file_source src[2];
|
||||
|
||||
n_src = git_attr_cache__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, info->files);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int collect_attr_files(
|
||||
git_repository *repo, const char *path, git_vector *files)
|
||||
git_repository *repo,
|
||||
uint32_t flags,
|
||||
const char *path,
|
||||
git_vector *files)
|
||||
{
|
||||
int error = GIT_SUCCESS;
|
||||
int error;
|
||||
git_buf dir = GIT_BUF_INIT;
|
||||
git_config *cfg;
|
||||
const char *workdir = git_repository_workdir(repo);
|
||||
attr_walk_up_info info;
|
||||
|
||||
if ((error = git_attr_cache__init(repo)) < GIT_SUCCESS)
|
||||
goto cleanup;
|
||||
if (git_attr_cache__init(repo) < 0 ||
|
||||
git_vector_init(files, 4, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
if ((error = git_vector_init(files, 4, NULL)) < GIT_SUCCESS)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_path_find_dir(&dir, path, workdir)) < GIT_SUCCESS)
|
||||
/* Resolve path in a non-bare repo */
|
||||
if (workdir != NULL)
|
||||
error = git_path_find_dir(&dir, path, workdir);
|
||||
else
|
||||
error = git_path_dirname_r(&dir, path);
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* in precendence order highest to lowest:
|
||||
@ -309,38 +523,40 @@ static int collect_attr_files(
|
||||
* - $GIT_PREFIX/etc/gitattributes
|
||||
*/
|
||||
|
||||
error = push_attrs(repo, files, repo->path_repository, GIT_ATTR_FILE_INREPO);
|
||||
if (error < GIT_SUCCESS)
|
||||
error = push_attr_file(
|
||||
repo, files, git_repository_path(repo), GIT_ATTR_FILE_INREPO);
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
info.repo = repo;
|
||||
info.repo = repo;
|
||||
info.flags = flags;
|
||||
info.workdir = workdir;
|
||||
if (git_repository_index__weakptr(&info.index, repo) < 0)
|
||||
giterr_clear(); /* no error even if there is no index */
|
||||
info.files = files;
|
||||
|
||||
error = git_path_walk_up(&dir, workdir, push_one_attr, &info);
|
||||
if (error < GIT_SUCCESS)
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_repository_config(&cfg, repo)) == GIT_SUCCESS) {
|
||||
const char *core_attribs = NULL;
|
||||
git_config_get_string(cfg, GIT_ATTR_CONFIG, &core_attribs);
|
||||
git_clearerror(); /* don't care if attributesfile is not set */
|
||||
if (core_attribs)
|
||||
error = push_attrs(repo, files, NULL, core_attribs);
|
||||
git_config_free(cfg);
|
||||
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);
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (error == GIT_SUCCESS) {
|
||||
if ((flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
|
||||
error = git_futils_find_system_file(&dir, GIT_ATTR_FILE_SYSTEM);
|
||||
if (error == GIT_SUCCESS)
|
||||
error = push_attrs(repo, files, NULL, dir.ptr);
|
||||
if (!error)
|
||||
error = push_attr_file(repo, files, NULL, dir.ptr);
|
||||
else if (error == GIT_ENOTFOUND)
|
||||
error = GIT_SUCCESS;
|
||||
error = 0;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (error < GIT_SUCCESS) {
|
||||
git__rethrow(error, "Could not get attributes for '%s'", path);
|
||||
if (error < 0)
|
||||
git_vector_free(files);
|
||||
}
|
||||
git_buf_free(&dir);
|
||||
|
||||
return error;
|
||||
@ -349,70 +565,108 @@ static int collect_attr_files(
|
||||
|
||||
int git_attr_cache__init(git_repository *repo)
|
||||
{
|
||||
int error = GIT_SUCCESS;
|
||||
git_attr_cache *cache = &repo->attrcache;
|
||||
int ret;
|
||||
git_attr_cache *cache = git_repository_attr_cache(repo);
|
||||
git_config *cfg;
|
||||
|
||||
if (cache->initialized)
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
|
||||
/* cache config settings for attributes and ignores */
|
||||
if (git_repository_config__weakptr(&cfg, repo) < 0)
|
||||
return -1;
|
||||
|
||||
ret = git_config_get_string(&cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG);
|
||||
if (ret < 0 && ret != GIT_ENOTFOUND)
|
||||
return ret;
|
||||
|
||||
ret = git_config_get_string(&cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG);
|
||||
if (ret < 0 && ret != GIT_ENOTFOUND)
|
||||
return ret;
|
||||
|
||||
giterr_clear();
|
||||
|
||||
/* allocate hashtable for attribute and ignore file contents */
|
||||
if (cache->files == NULL) {
|
||||
cache->files = git_hashtable_alloc(
|
||||
8, git_hash__strhash_cb, git_hash__strcmp_cb);
|
||||
if (!cache->files)
|
||||
return git__throw(GIT_ENOMEM, "Could not initialize attribute cache");
|
||||
cache->files = git_strmap_alloc();
|
||||
GITERR_CHECK_ALLOC(cache->files);
|
||||
}
|
||||
|
||||
/* allocate hashtable for attribute macros */
|
||||
if (cache->macros == NULL) {
|
||||
cache->macros = git_hashtable_alloc(
|
||||
8, git_hash__strhash_cb, git_hash__strcmp_cb);
|
||||
if (!cache->macros)
|
||||
return git__throw(GIT_ENOMEM, "Could not initialize attribute cache");
|
||||
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 */
|
||||
error = git_attr_add_macro(repo, "binary", "-diff -crlf");
|
||||
|
||||
return error;
|
||||
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;
|
||||
|
||||
if (repo->attrcache.files) {
|
||||
const void *GIT_UNUSED(name);
|
||||
cache = git_repository_attr_cache(repo);
|
||||
|
||||
if (cache->files != NULL) {
|
||||
git_attr_file *file;
|
||||
|
||||
GIT_HASHTABLE_FOREACH(repo->attrcache.files, name, file,
|
||||
git_attr_file__free(file));
|
||||
git_strmap_foreach_value(cache->files, file, {
|
||||
git_attr_file__free(file);
|
||||
});
|
||||
|
||||
git_hashtable_free(repo->attrcache.files);
|
||||
repo->attrcache.files = NULL;
|
||||
git_strmap_free(cache->files);
|
||||
}
|
||||
|
||||
if (repo->attrcache.macros) {
|
||||
const void *GIT_UNUSED(name);
|
||||
if (cache->macros != NULL) {
|
||||
git_attr_rule *rule;
|
||||
|
||||
GIT_HASHTABLE_FOREACH(repo->attrcache.macros, name, rule,
|
||||
git_attr_rule__free(rule));
|
||||
git_strmap_foreach_value(cache->macros, rule, {
|
||||
git_attr_rule__free(rule);
|
||||
});
|
||||
|
||||
git_hashtable_free(repo->attrcache.macros);
|
||||
repo->attrcache.macros = NULL;
|
||||
git_strmap_free(cache->macros);
|
||||
}
|
||||
|
||||
repo->attrcache.initialized = 0;
|
||||
git_pool_clear(&cache->pool);
|
||||
|
||||
cache->initialized = 0;
|
||||
}
|
||||
|
||||
int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
|
||||
{
|
||||
if (macro->assigns.length == 0)
|
||||
return git__throw(GIT_EMISSINGOBJDATA, "git attribute macro with no values");
|
||||
git_strmap *macros = git_repository_attr_cache(repo)->macros;
|
||||
int error;
|
||||
|
||||
return git_hashtable_insert(
|
||||
repo->attrcache.macros, macro->match.pattern, macro);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
|
41
src/attr.h
41
src/attr.h
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -8,26 +8,49 @@
|
||||
#define INCLUDE_attr_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_hashtable *files; /* hash path to git_attr_file of rules */
|
||||
git_hashtable *macros; /* hash name to vector<git_attr_assignment> */
|
||||
git_pool pool;
|
||||
git_strmap *files; /* hash path to git_attr_file of rules */
|
||||
git_strmap *macros; /* hash name to vector<git_attr_assignment> */
|
||||
const char *cfg_attr_file; /* cached value of core.attributesfile */
|
||||
const char *cfg_excl_file; /* cached value of core.excludesfile */
|
||||
} git_attr_cache;
|
||||
|
||||
typedef int (*git_attr_file_parser)(
|
||||
git_repository *, const char *, git_attr_file *);
|
||||
|
||||
extern int git_attr_cache__init(git_repository *repo);
|
||||
|
||||
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,
|
||||
git_vector *stack,
|
||||
const char *base,
|
||||
const char *filename,
|
||||
int (*loader)(git_repository *, const char *, git_attr_file *));
|
||||
const char *base,
|
||||
const char *filename,
|
||||
git_attr_file_source source,
|
||||
git_attr_file_parser parse,
|
||||
git_vector *stack);
|
||||
|
||||
/* returns GIT_SUCCESS if path is in cache */
|
||||
extern int git_attr_cache__is_cached(git_repository *repo, const char *path);
|
||||
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);
|
||||
|
||||
#endif
|
||||
|
312
src/attr_file.c
312
src/attr_file.c
@ -1,62 +1,65 @@
|
||||
#include "common.h"
|
||||
#include "repository.h"
|
||||
#include "filebuf.h"
|
||||
#include "git2/blob.h"
|
||||
#include "git2/tree.h"
|
||||
#include <ctype.h>
|
||||
|
||||
const char *git_attr__true = "[internal]__TRUE__";
|
||||
const char *git_attr__false = "[internal]__FALSE__";
|
||||
const char *git_attr__unset = "[internal]__UNSET__";
|
||||
|
||||
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);
|
||||
|
||||
int git_attr_file__new(git_attr_file **attrs_ptr)
|
||||
int git_attr_file__new(
|
||||
git_attr_file **attrs_ptr,
|
||||
git_attr_file_source from,
|
||||
const char *path,
|
||||
git_pool *pool)
|
||||
{
|
||||
int error;
|
||||
git_attr_file *attrs = NULL;
|
||||
|
||||
attrs = git__calloc(1, sizeof(git_attr_file));
|
||||
if (attrs == NULL)
|
||||
error = GIT_ENOMEM;
|
||||
else
|
||||
error = git_vector_init(&attrs->rules, 4, NULL);
|
||||
GITERR_CHECK_ALLOC(attrs);
|
||||
|
||||
if (error != GIT_SUCCESS) {
|
||||
git__rethrow(error, "Could not allocate attribute storage");
|
||||
git__free(attrs);
|
||||
attrs = NULL;
|
||||
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' + 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;
|
||||
|
||||
return error;
|
||||
fail:
|
||||
git_attr_file__free(attrs);
|
||||
attrs_ptr = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_attr_file__set_path(
|
||||
git_repository *repo, const char *path, git_attr_file *file)
|
||||
{
|
||||
if (file->path != NULL) {
|
||||
git__free(file->path);
|
||||
file->path = NULL;
|
||||
}
|
||||
|
||||
if (repo == NULL)
|
||||
file->path = git__strdup(path);
|
||||
else {
|
||||
const char *workdir = git_repository_workdir(repo);
|
||||
|
||||
if (workdir && git__prefixcmp(path, workdir) == 0)
|
||||
file->path = git__strdup(path + strlen(workdir));
|
||||
else
|
||||
file->path = git__strdup(path);
|
||||
}
|
||||
|
||||
return (file->path == NULL) ? GIT_ENOMEM : GIT_SUCCESS;
|
||||
}
|
||||
|
||||
int git_attr_file__from_buffer(
|
||||
int git_attr_file__parse_buffer(
|
||||
git_repository *repo, const char *buffer, git_attr_file *attrs)
|
||||
{
|
||||
int error = GIT_SUCCESS;
|
||||
int error = 0;
|
||||
const char *scan = NULL;
|
||||
char *context = NULL;
|
||||
git_attr_rule *rule = NULL;
|
||||
@ -65,22 +68,24 @@ int git_attr_file__from_buffer(
|
||||
|
||||
scan = buffer;
|
||||
|
||||
if (attrs->path && git__suffixcmp(attrs->path, GIT_ATTR_FILE) == 0) {
|
||||
context = git__strndup(attrs->path,
|
||||
strlen(attrs->path) - strlen(GIT_ATTR_FILE));
|
||||
if (!context) error = GIT_ENOMEM;
|
||||
/* 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';
|
||||
}
|
||||
|
||||
while (error == GIT_SUCCESS && *scan) {
|
||||
while (!error && *scan) {
|
||||
/* allocate rule if needed */
|
||||
if (!rule && !(rule = git__calloc(1, sizeof(git_attr_rule)))) {
|
||||
error = GIT_ENOMEM;
|
||||
error = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* parse the next "pattern attr attr attr" line */
|
||||
if (!(error = git_attr_fnmatch__parse(&rule->match, context, &scan)) &&
|
||||
!(error = git_attr_assignment__parse(repo, &rule->assigns, &scan)))
|
||||
if (!(error = git_attr_fnmatch__parse(
|
||||
&rule->match, attrs->pool, context, &scan)) &&
|
||||
!(error = git_attr_assignment__parse(
|
||||
repo, attrs->pool, &rule->assigns, &scan)))
|
||||
{
|
||||
if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO)
|
||||
/* should generate error/warning if this is coming from any
|
||||
@ -92,39 +97,44 @@ int git_attr_file__from_buffer(
|
||||
}
|
||||
|
||||
/* if the rule wasn't a pattern, on to the next */
|
||||
if (error != GIT_SUCCESS) {
|
||||
if (error < 0) {
|
||||
git_attr_rule__clear(rule); /* reset rule contents */
|
||||
if (error == GIT_ENOTFOUND)
|
||||
error = GIT_SUCCESS;
|
||||
error = 0;
|
||||
} else {
|
||||
rule = NULL; /* vector now "owns" the rule */
|
||||
}
|
||||
}
|
||||
|
||||
git_attr_rule__free(rule);
|
||||
git__free(context);
|
||||
|
||||
/* restore file path used for context */
|
||||
if (context)
|
||||
context[strlen(context)] = '.'; /* first char of GIT_ATTR_FILE */
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_attr_file__from_file(
|
||||
git_repository *repo, const char *path, git_attr_file *file)
|
||||
int git_attr_file__new_and_load(
|
||||
git_attr_file **attrs_ptr,
|
||||
const char *path)
|
||||
{
|
||||
int error = GIT_SUCCESS;
|
||||
git_fbuffer fbuf = GIT_FBUFFER_INIT;
|
||||
int error;
|
||||
git_buf content = GIT_BUF_INIT;
|
||||
|
||||
assert(path && file);
|
||||
if ((error = git_attr_file__new(attrs_ptr, 0, path, NULL)) < 0)
|
||||
return error;
|
||||
|
||||
if (file->path == NULL)
|
||||
error = git_attr_file__set_path(repo, path, file);
|
||||
if (!(error = git_futils_readbuffer(&content, path)))
|
||||
error = git_attr_file__parse_buffer(
|
||||
NULL, git_buf_cstr(&content), *attrs_ptr);
|
||||
|
||||
if (error == GIT_SUCCESS &&
|
||||
(error = git_futils_readbuffer(&fbuf, path)) == GIT_SUCCESS)
|
||||
error = git_attr_file__from_buffer(repo, fbuf.data, file);
|
||||
git_buf_free(&content);
|
||||
|
||||
git_futils_freebuffer(&fbuf);
|
||||
if (error != GIT_SUCCESS)
|
||||
git__rethrow(error, "Could not open attribute file '%s'", path);
|
||||
if (error) {
|
||||
git_attr_file__free(*attrs_ptr);
|
||||
*attrs_ptr = NULL;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -142,15 +152,18 @@ void git_attr_file__free(git_attr_file *file)
|
||||
|
||||
git_vector_free(&file->rules);
|
||||
|
||||
git__free(file->path);
|
||||
file->path = NULL;
|
||||
if (file->pool_is_allocated) {
|
||||
git_pool_clear(file->pool);
|
||||
git__free(file->pool);
|
||||
}
|
||||
file->pool = NULL;
|
||||
|
||||
git__free(file);
|
||||
}
|
||||
|
||||
unsigned long git_attr_file__name_hash(const char *name)
|
||||
uint32_t git_attr_file__name_hash(const char *name)
|
||||
{
|
||||
unsigned long h = 5381;
|
||||
uint32_t h = 5381;
|
||||
int c;
|
||||
assert(name);
|
||||
while ((c = (int)*name++) != 0)
|
||||
@ -176,7 +189,6 @@ int git_attr_file__lookup_one(
|
||||
|
||||
git_attr_file__foreach_matching_rule(file, path, i, rule) {
|
||||
int pos = git_vector_bsearch(&rule->assigns, &name);
|
||||
git_clearerror(); /* okay if search failed */
|
||||
|
||||
if (pos >= 0) {
|
||||
*value = ((git_attr_assignment *)
|
||||
@ -185,37 +197,37 @@ int git_attr_file__lookup_one(
|
||||
}
|
||||
}
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int git_attr_fnmatch__match(
|
||||
bool git_attr_fnmatch__match(
|
||||
git_attr_fnmatch *match,
|
||||
const git_attr_path *path)
|
||||
{
|
||||
int matched = FNM_NOMATCH;
|
||||
int fnm;
|
||||
|
||||
if (match->flags & GIT_ATTR_FNMATCH_DIRECTORY && !path->is_dir)
|
||||
return matched;
|
||||
return false;
|
||||
|
||||
if (match->flags & GIT_ATTR_FNMATCH_FULLPATH)
|
||||
matched = p_fnmatch(match->pattern, path->path, FNM_PATHNAME);
|
||||
fnm = p_fnmatch(match->pattern, path->path, FNM_PATHNAME);
|
||||
else if (path->is_dir)
|
||||
matched = p_fnmatch(match->pattern, path->basename, FNM_LEADING_DIR);
|
||||
fnm = p_fnmatch(match->pattern, path->basename, FNM_LEADING_DIR);
|
||||
else
|
||||
matched = p_fnmatch(match->pattern, path->basename, 0);
|
||||
fnm = p_fnmatch(match->pattern, path->basename, 0);
|
||||
|
||||
return matched;
|
||||
return (fnm == FNM_NOMATCH) ? false : true;
|
||||
}
|
||||
|
||||
int git_attr_rule__match(
|
||||
bool git_attr_rule__match(
|
||||
git_attr_rule *rule,
|
||||
const git_attr_path *path)
|
||||
{
|
||||
int matched = git_attr_fnmatch__match(&rule->match, path);
|
||||
bool matched = git_attr_fnmatch__match(&rule->match, path);
|
||||
|
||||
if (rule->match.flags & GIT_ATTR_FNMATCH_NEGATIVE)
|
||||
matched = (matched == GIT_SUCCESS) ? FNM_NOMATCH : GIT_SUCCESS;
|
||||
matched = !matched;
|
||||
|
||||
return matched;
|
||||
}
|
||||
@ -230,7 +242,6 @@ git_attr_assignment *git_attr_rule__lookup_assignment(
|
||||
key.name_hash = git_attr_file__name_hash(name);
|
||||
|
||||
pos = git_vector_bsearch(&rule->assigns, &key);
|
||||
git_clearerror(); /* okay if search failed */
|
||||
|
||||
return (pos >= 0) ? git_vector_get(&rule->assigns, pos) : NULL;
|
||||
}
|
||||
@ -238,25 +249,48 @@ git_attr_assignment *git_attr_rule__lookup_assignment(
|
||||
int git_attr_path__init(
|
||||
git_attr_path *info, const char *path, const char *base)
|
||||
{
|
||||
assert(info && path);
|
||||
info->path = path;
|
||||
info->basename = strrchr(path, '/');
|
||||
/* build full path as best we can */
|
||||
git_buf_init(&info->full, 0);
|
||||
|
||||
if (base != NULL && git_path_root(path) < 0) {
|
||||
if (git_buf_joinpath(&info->full, base, path) < 0)
|
||||
return -1;
|
||||
info->path = info->full.ptr + strlen(base);
|
||||
} else {
|
||||
if (git_buf_sets(&info->full, path) < 0)
|
||||
return -1;
|
||||
info->path = info->full.ptr;
|
||||
}
|
||||
|
||||
/* remove trailing slashes */
|
||||
while (info->full.size > 0) {
|
||||
if (info->full.ptr[info->full.size - 1] != '/')
|
||||
break;
|
||||
info->full.size--;
|
||||
}
|
||||
info->full.ptr[info->full.size] = '\0';
|
||||
|
||||
/* skip leading slashes in path */
|
||||
while (*info->path == '/')
|
||||
info->path++;
|
||||
|
||||
/* find trailing basename component */
|
||||
info->basename = strrchr(info->path, '/');
|
||||
if (info->basename)
|
||||
info->basename++;
|
||||
if (!info->basename || !*info->basename)
|
||||
info->basename = path;
|
||||
info->basename = info->path;
|
||||
|
||||
if (base != NULL && git_path_root(path) < 0) {
|
||||
git_buf full_path = GIT_BUF_INIT;
|
||||
int error = git_buf_joinpath(&full_path, base, path);
|
||||
if (error == GIT_SUCCESS)
|
||||
info->is_dir = (git_path_isdir(full_path.ptr) == GIT_SUCCESS);
|
||||
git_buf_free(&full_path);
|
||||
return error;
|
||||
}
|
||||
info->is_dir = (git_path_isdir(path) == GIT_SUCCESS);
|
||||
info->is_dir = (int)git_path_isdir(info->full.ptr);
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void git_attr_path__free(git_attr_path *info)
|
||||
{
|
||||
git_buf_free(&info->full);
|
||||
info->path = NULL;
|
||||
info->basename = NULL;
|
||||
}
|
||||
|
||||
|
||||
@ -293,12 +327,13 @@ int git_attr_path__init(
|
||||
*/
|
||||
|
||||
/*
|
||||
* This will return GIT_SUCCESS if the spec was filled out,
|
||||
* This will return 0 if the spec was filled out,
|
||||
* GIT_ENOTFOUND if the fnmatch does not require matching, or
|
||||
* another error code there was an actual problem.
|
||||
*/
|
||||
int git_attr_fnmatch__parse(
|
||||
git_attr_fnmatch *spec,
|
||||
git_pool *pool,
|
||||
const char *source,
|
||||
const char **base)
|
||||
{
|
||||
@ -309,7 +344,7 @@ int git_attr_fnmatch__parse(
|
||||
|
||||
pattern = *base;
|
||||
|
||||
while (isspace(*pattern)) pattern++;
|
||||
while (git__isspace(*pattern)) pattern++;
|
||||
if (!*pattern || *pattern == '#') {
|
||||
*base = git__next_line(pattern);
|
||||
return GIT_ENOTFOUND;
|
||||
@ -333,13 +368,19 @@ int git_attr_fnmatch__parse(
|
||||
slash_count = 0;
|
||||
for (scan = pattern; *scan != '\0'; ++scan) {
|
||||
/* scan until (non-escaped) white space */
|
||||
if (isspace(*scan) && *(scan - 1) != '\\')
|
||||
if (git__isspace(*scan) && *(scan - 1) != '\\')
|
||||
break;
|
||||
|
||||
if (*scan == '/') {
|
||||
spec->flags = spec->flags | GIT_ATTR_FNMATCH_FULLPATH;
|
||||
slash_count++;
|
||||
if (pattern == scan)
|
||||
pattern++;
|
||||
}
|
||||
/* remember if we see an unescaped wildcard in pattern */
|
||||
else if (git__iswildcard(*scan) &&
|
||||
(scan == pattern || (*(scan - 1) != '\\')))
|
||||
spec->flags = spec->flags | GIT_ATTR_FNMATCH_HASWILD;
|
||||
}
|
||||
|
||||
*base = scan;
|
||||
@ -360,7 +401,8 @@ int git_attr_fnmatch__parse(
|
||||
/* 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__malloc(sourcelen + spec->length + 1);
|
||||
spec->pattern = git_pool_malloc(
|
||||
pool, (uint32_t)(sourcelen + spec->length + 1));
|
||||
if (spec->pattern) {
|
||||
memcpy(spec->pattern, source, sourcelen);
|
||||
memcpy(spec->pattern + sourcelen, pattern, spec->length);
|
||||
@ -368,12 +410,12 @@ int git_attr_fnmatch__parse(
|
||||
spec->pattern[spec->length] = '\0';
|
||||
}
|
||||
} else {
|
||||
spec->pattern = git__strndup(pattern, spec->length);
|
||||
spec->pattern = git_pool_strndup(pool, pattern, spec->length);
|
||||
}
|
||||
|
||||
if (!spec->pattern) {
|
||||
*base = git__next_line(pattern);
|
||||
return GIT_ENOMEM;
|
||||
return -1;
|
||||
} else {
|
||||
/* strip '\' that might have be used for internal whitespace */
|
||||
char *to = spec->pattern;
|
||||
@ -389,7 +431,7 @@ int git_attr_fnmatch__parse(
|
||||
}
|
||||
}
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sort_by_hash_and_name(const void *a_raw, const void *b_raw)
|
||||
@ -407,14 +449,11 @@ static int sort_by_hash_and_name(const void *a_raw, const void *b_raw)
|
||||
|
||||
static void git_attr_assignment__free(git_attr_assignment *assign)
|
||||
{
|
||||
git__free(assign->name);
|
||||
/* name and value are stored in a git_pool associated with the
|
||||
* git_attr_file, so they do not need to be freed here
|
||||
*/
|
||||
assign->name = NULL;
|
||||
|
||||
if (assign->is_allocated) {
|
||||
git__free((void *)assign->value);
|
||||
assign->value = NULL;
|
||||
}
|
||||
|
||||
assign->value = NULL;
|
||||
git__free(assign);
|
||||
}
|
||||
|
||||
@ -430,10 +469,11 @@ static int merge_assignments(void **old_raw, void *new_raw)
|
||||
|
||||
int git_attr_assignment__parse(
|
||||
git_repository *repo,
|
||||
git_pool *pool,
|
||||
git_vector *assigns,
|
||||
const char **base)
|
||||
{
|
||||
int error = GIT_SUCCESS;
|
||||
int error;
|
||||
const char *scan = *base;
|
||||
git_attr_assignment *assign = NULL;
|
||||
|
||||
@ -441,39 +481,35 @@ int git_attr_assignment__parse(
|
||||
|
||||
assigns->_cmp = sort_by_hash_and_name;
|
||||
|
||||
while (*scan && *scan != '\n' && error == GIT_SUCCESS) {
|
||||
while (*scan && *scan != '\n') {
|
||||
const char *name_start, *value_start;
|
||||
|
||||
/* skip leading blanks */
|
||||
while (isspace(*scan) && *scan != '\n') scan++;
|
||||
while (git__isspace(*scan) && *scan != '\n') scan++;
|
||||
|
||||
/* allocate assign if needed */
|
||||
if (!assign) {
|
||||
assign = git__calloc(1, sizeof(git_attr_assignment));
|
||||
if (!assign) {
|
||||
error = GIT_ENOMEM;
|
||||
break;
|
||||
}
|
||||
GITERR_CHECK_ALLOC(assign);
|
||||
GIT_REFCOUNT_INC(assign);
|
||||
}
|
||||
|
||||
assign->name_hash = 5381;
|
||||
assign->value = GIT_ATTR_TRUE;
|
||||
assign->is_allocated = 0;
|
||||
assign->value = git_attr__true;
|
||||
|
||||
/* look for magic name prefixes */
|
||||
if (*scan == '-') {
|
||||
assign->value = GIT_ATTR_FALSE;
|
||||
assign->value = git_attr__false;
|
||||
scan++;
|
||||
} else if (*scan == '!') {
|
||||
assign->value = NULL; /* explicit unspecified state */
|
||||
assign->value = git_attr__unset; /* explicit unspecified state */
|
||||
scan++;
|
||||
} else if (*scan == '#') /* comment rest of line */
|
||||
break;
|
||||
|
||||
/* find the name */
|
||||
name_start = scan;
|
||||
while (*scan && !isspace(*scan) && *scan != '=') {
|
||||
while (*scan && !git__isspace(*scan) && *scan != '=') {
|
||||
assign->name_hash =
|
||||
((assign->name_hash << 5) + assign->name_hash) + *scan;
|
||||
scan++;
|
||||
@ -482,37 +518,29 @@ int git_attr_assignment__parse(
|
||||
/* must have found lone prefix (" - ") or leading = ("=foo")
|
||||
* or end of buffer -- advance until whitespace and continue
|
||||
*/
|
||||
while (*scan && !isspace(*scan)) scan++;
|
||||
while (*scan && !git__isspace(*scan)) scan++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* allocate permanent storage for name */
|
||||
assign->name = git__strndup(name_start, scan - name_start);
|
||||
if (!assign->name) {
|
||||
error = GIT_ENOMEM;
|
||||
break;
|
||||
}
|
||||
assign->name = git_pool_strndup(pool, name_start, scan - name_start);
|
||||
GITERR_CHECK_ALLOC(assign->name);
|
||||
|
||||
/* if there is an equals sign, find the value */
|
||||
if (*scan == '=') {
|
||||
for (value_start = ++scan; *scan && !isspace(*scan); ++scan);
|
||||
for (value_start = ++scan; *scan && !git__isspace(*scan); ++scan);
|
||||
|
||||
/* if we found a value, allocate permanent storage for it */
|
||||
if (scan > value_start) {
|
||||
assign->value = git__strndup(value_start, scan - value_start);
|
||||
if (!assign->value) {
|
||||
error = GIT_ENOMEM;
|
||||
break;
|
||||
} else {
|
||||
assign->is_allocated = 1;
|
||||
}
|
||||
assign->value = git_pool_strndup(pool, value_start, scan - value_start);
|
||||
GITERR_CHECK_ALLOC(assign->value);
|
||||
}
|
||||
}
|
||||
|
||||
/* expand macros (if given a repo with a macro cache) */
|
||||
if (repo != NULL && assign->value == GIT_ATTR_TRUE) {
|
||||
if (repo != NULL && assign->value == git_attr__true) {
|
||||
git_attr_rule *macro =
|
||||
git_hashtable_lookup(repo->attrcache.macros, assign->name);
|
||||
git_attr_cache__lookup_macro(repo, assign->name);
|
||||
|
||||
if (macro != NULL) {
|
||||
unsigned int i;
|
||||
@ -523,35 +551,27 @@ int git_attr_assignment__parse(
|
||||
|
||||
error = git_vector_insert_sorted(
|
||||
assigns, massign, &merge_assignments);
|
||||
|
||||
if (error == GIT_EEXISTS)
|
||||
error = GIT_SUCCESS;
|
||||
else if (error != GIT_SUCCESS)
|
||||
break;
|
||||
if (error < 0 && error != GIT_EEXISTS)
|
||||
return error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* insert allocated assign into vector */
|
||||
error = git_vector_insert_sorted(assigns, assign, &merge_assignments);
|
||||
if (error == GIT_EEXISTS)
|
||||
error = GIT_SUCCESS;
|
||||
else if (error < GIT_SUCCESS)
|
||||
break;
|
||||
if (error < 0 && error != GIT_EEXISTS)
|
||||
return error;
|
||||
|
||||
/* clear assign since it is now "owned" by the vector */
|
||||
assign = NULL;
|
||||
}
|
||||
|
||||
if (!assigns->length)
|
||||
error = git__throw(GIT_ENOTFOUND, "No attribute assignments found for rule");
|
||||
|
||||
if (assign != NULL)
|
||||
git_attr_assignment__free(assign);
|
||||
|
||||
*base = git__next_line(scan);
|
||||
|
||||
return error;
|
||||
return (assigns->length == 0) ? GIT_ENOTFOUND : 0;
|
||||
}
|
||||
|
||||
static void git_attr_rule__clear(git_attr_rule *rule)
|
||||
@ -568,7 +588,7 @@ static void git_attr_rule__clear(git_attr_rule *rule)
|
||||
git_vector_free(&rule->assigns);
|
||||
}
|
||||
|
||||
git__free(rule->match.pattern);
|
||||
/* match.pattern is stored in a git_pool, so no need to free */
|
||||
rule->match.pattern = NULL;
|
||||
rule->match.length = 0;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -9,18 +9,19 @@
|
||||
|
||||
#include "git2/attr.h"
|
||||
#include "vector.h"
|
||||
#include "hashtable.h"
|
||||
#include "pool.h"
|
||||
#include "buffer.h"
|
||||
|
||||
#define GIT_ATTR_FILE ".gitattributes"
|
||||
#define GIT_ATTR_FILE_INREPO "info/attributes"
|
||||
#define GIT_ATTR_FILE_SYSTEM "gitattributes"
|
||||
#define GIT_ATTR_CONFIG "core.attributesfile"
|
||||
|
||||
#define GIT_ATTR_FNMATCH_NEGATIVE (1U << 0)
|
||||
#define GIT_ATTR_FNMATCH_DIRECTORY (1U << 1)
|
||||
#define GIT_ATTR_FNMATCH_FULLPATH (1U << 2)
|
||||
#define GIT_ATTR_FNMATCH_MACRO (1U << 3)
|
||||
#define GIT_ATTR_FNMATCH_IGNORE (1U << 4)
|
||||
#define GIT_ATTR_FNMATCH_HASWILD (1U << 5)
|
||||
|
||||
typedef struct {
|
||||
char *pattern;
|
||||
@ -36,42 +37,59 @@ typedef struct {
|
||||
typedef struct {
|
||||
git_refcount unused;
|
||||
const char *name;
|
||||
unsigned long name_hash;
|
||||
uint32_t name_hash;
|
||||
} git_attr_name;
|
||||
|
||||
typedef struct {
|
||||
git_refcount rc; /* for macros */
|
||||
char *name;
|
||||
unsigned long name_hash;
|
||||
uint32_t name_hash;
|
||||
const char *value;
|
||||
int is_allocated;
|
||||
} git_attr_assignment;
|
||||
|
||||
typedef struct {
|
||||
char *path; /* cache the path this was loaded from */
|
||||
git_time_t seconds;
|
||||
git_off_t size;
|
||||
unsigned int ino;
|
||||
} git_attr_file_stat_sig;
|
||||
|
||||
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;
|
||||
union {
|
||||
git_oid oid;
|
||||
git_attr_file_stat_sig st;
|
||||
} cache_data;
|
||||
} git_attr_file;
|
||||
|
||||
typedef struct {
|
||||
git_buf full;
|
||||
const char *path;
|
||||
const char *basename;
|
||||
int is_dir;
|
||||
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);
|
||||
extern int git_attr_file__new(
|
||||
git_attr_file **attrs_ptr, git_attr_file_source src, const char *path, git_pool *pool);
|
||||
|
||||
extern int git_attr_file__new_and_load(
|
||||
git_attr_file **attrs_ptr, const char *path);
|
||||
|
||||
extern void git_attr_file__free(git_attr_file *file);
|
||||
|
||||
extern int git_attr_file__from_buffer(
|
||||
extern int git_attr_file__parse_buffer(
|
||||
git_repository *repo, const char *buf, git_attr_file *file);
|
||||
extern int git_attr_file__from_file(
|
||||
git_repository *repo, const char *path, git_attr_file *file);
|
||||
|
||||
extern int git_attr_file__set_path(
|
||||
git_repository *repo, const char *path, git_attr_file *file);
|
||||
|
||||
extern int git_attr_file__lookup_one(
|
||||
git_attr_file *file,
|
||||
@ -82,9 +100,9 @@ extern int git_attr_file__lookup_one(
|
||||
/* loop over rules in file from bottom to top */
|
||||
#define git_attr_file__foreach_matching_rule(file, path, iter, rule) \
|
||||
git_vector_rforeach(&(file)->rules, (iter), (rule)) \
|
||||
if (git_attr_rule__match((rule), (path)) == GIT_SUCCESS)
|
||||
if (git_attr_rule__match((rule), (path)))
|
||||
|
||||
extern unsigned long git_attr_file__name_hash(const char *name);
|
||||
extern uint32_t git_attr_file__name_hash(const char *name);
|
||||
|
||||
|
||||
/*
|
||||
@ -93,16 +111,17 @@ extern unsigned long git_attr_file__name_hash(const char *name);
|
||||
|
||||
extern int git_attr_fnmatch__parse(
|
||||
git_attr_fnmatch *spec,
|
||||
git_pool *pool,
|
||||
const char *source,
|
||||
const char **base);
|
||||
|
||||
extern int git_attr_fnmatch__match(
|
||||
extern bool git_attr_fnmatch__match(
|
||||
git_attr_fnmatch *rule,
|
||||
const git_attr_path *path);
|
||||
|
||||
extern void git_attr_rule__free(git_attr_rule *rule);
|
||||
|
||||
extern int git_attr_rule__match(
|
||||
extern bool git_attr_rule__match(
|
||||
git_attr_rule *rule,
|
||||
const git_attr_path *path);
|
||||
|
||||
@ -112,8 +131,11 @@ extern git_attr_assignment *git_attr_rule__lookup_assignment(
|
||||
extern int git_attr_path__init(
|
||||
git_attr_path *info, const char *path, const char *base);
|
||||
|
||||
extern void git_attr_path__free(git_attr_path *info);
|
||||
|
||||
extern int git_attr_assignment__parse(
|
||||
git_repository *repo, /* needed to expand macros */
|
||||
git_pool *pool,
|
||||
git_vector *assigns,
|
||||
const char **scan);
|
||||
|
||||
|
245
src/blob.c
245
src/blob.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "blob.h"
|
||||
#include "filter.h"
|
||||
|
||||
const void *git_blob_rawcontent(git_blob *blob)
|
||||
{
|
||||
@ -24,6 +25,12 @@ size_t git_blob_rawsize(git_blob *blob)
|
||||
return blob->odb_object->raw.len;
|
||||
}
|
||||
|
||||
int git_blob__getbuf(git_buf *buffer, git_blob *blob)
|
||||
{
|
||||
return git_buf_set(
|
||||
buffer, blob->odb_object->raw.data, blob->odb_object->raw.len);
|
||||
}
|
||||
|
||||
void git_blob__free(git_blob *blob)
|
||||
{
|
||||
git_odb_object_free(blob->odb_object);
|
||||
@ -35,7 +42,7 @@ int git_blob__parse(git_blob *blob, git_odb_object *odb_obj)
|
||||
assert(blob);
|
||||
git_cached_obj_incref((git_cached_obj *)odb_obj);
|
||||
blob->odb_object = odb_obj;
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *buffer, size_t len)
|
||||
@ -44,99 +51,189 @@ int git_blob_create_frombuffer(git_oid *oid, git_repository *repo, const void *b
|
||||
git_odb *odb;
|
||||
git_odb_stream *stream;
|
||||
|
||||
error = git_repository_odb__weakptr(&odb, repo);
|
||||
if (error < GIT_SUCCESS)
|
||||
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_open_wstream(&stream, odb, len, GIT_OBJ_BLOB)) < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Failed to create blob");
|
||||
if ((error = stream->write(stream, buffer, len)) == 0)
|
||||
error = stream->finalize_write(oid, stream);
|
||||
|
||||
if ((error = stream->write(stream, buffer, len)) < GIT_SUCCESS) {
|
||||
stream->free(stream);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int write_file_stream(
|
||||
git_oid *oid, git_odb *odb, const char *path, git_off_t file_size)
|
||||
{
|
||||
int fd, error;
|
||||
char buffer[4096];
|
||||
git_odb_stream *stream = NULL;
|
||||
|
||||
if ((error = git_odb_open_wstream(
|
||||
&stream, odb, (size_t)file_size, GIT_OBJ_BLOB)) < 0)
|
||||
return error;
|
||||
|
||||
if ((fd = git_futils_open_ro(path)) < 0) {
|
||||
stream->free(stream);
|
||||
return error;
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = stream->finalize_write(oid, stream);
|
||||
while (!error && file_size > 0) {
|
||||
ssize_t read_len = p_read(fd, buffer, sizeof(buffer));
|
||||
|
||||
if (read_len < 0) {
|
||||
giterr_set(
|
||||
GITERR_OS, "Failed to create blob. Can't read whole file");
|
||||
error = -1;
|
||||
}
|
||||
else if (!(error = stream->write(stream, buffer, read_len)))
|
||||
file_size -= read_len;
|
||||
}
|
||||
|
||||
p_close(fd);
|
||||
|
||||
if (!error)
|
||||
error = stream->finalize_write(oid, stream);
|
||||
|
||||
stream->free(stream);
|
||||
return error;
|
||||
}
|
||||
|
||||
if (error < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Failed to create blob");
|
||||
static int write_file_filtered(
|
||||
git_oid *oid,
|
||||
git_odb *odb,
|
||||
const char *full_path,
|
||||
git_vector *filters)
|
||||
{
|
||||
int error;
|
||||
git_buf source = GIT_BUF_INIT;
|
||||
git_buf dest = GIT_BUF_INIT;
|
||||
|
||||
return GIT_SUCCESS;
|
||||
if ((error = git_futils_readbuffer(&source, full_path)) < 0)
|
||||
return error;
|
||||
|
||||
error = git_filters_apply(&dest, &source, filters);
|
||||
|
||||
/* Free the source as soon as possible. This can be big in memory,
|
||||
* and we don't want to ODB write to choke */
|
||||
git_buf_free(&source);
|
||||
|
||||
/* Write the file to disk if it was properly filtered */
|
||||
if (!error)
|
||||
error = git_odb_write(oid, odb, dest.ptr, dest.size, GIT_OBJ_BLOB);
|
||||
|
||||
git_buf_free(&dest);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int write_symlink(
|
||||
git_oid *oid, git_odb *odb, const char *path, size_t link_size)
|
||||
{
|
||||
char *link_data;
|
||||
ssize_t read_len;
|
||||
int error;
|
||||
|
||||
link_data = git__malloc(link_size);
|
||||
GITERR_CHECK_ALLOC(link_data);
|
||||
|
||||
read_len = p_readlink(path, link_data, link_size);
|
||||
if (read_len != (ssize_t)link_size) {
|
||||
giterr_set(GITERR_OS, "Failed to create blob. Can't read symlink '%s'", path);
|
||||
git__free(link_data);
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = git_odb_write(oid, odb, (void *)link_data, link_size, GIT_OBJ_BLOB);
|
||||
git__free(link_data);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int blob_create_internal(git_oid *oid, git_repository *repo, const char *path)
|
||||
{
|
||||
int error;
|
||||
struct stat st;
|
||||
git_odb *odb = NULL;
|
||||
git_off_t size;
|
||||
|
||||
if ((error = git_path_lstat(path, &st)) < 0 || (error = git_repository_odb__weakptr(&odb, repo)) < 0)
|
||||
return error;
|
||||
|
||||
size = st.st_size;
|
||||
|
||||
if (S_ISLNK(st.st_mode)) {
|
||||
error = write_symlink(oid, odb, path, (size_t)size);
|
||||
} else {
|
||||
git_vector write_filters = GIT_VECTOR_INIT;
|
||||
int filter_count;
|
||||
|
||||
/* Load the filters for writing this file to the ODB */
|
||||
filter_count = git_filters_load(
|
||||
&write_filters, repo, path, GIT_FILTER_TO_ODB);
|
||||
|
||||
if (filter_count < 0) {
|
||||
/* Negative value means there was a critical error */
|
||||
error = filter_count;
|
||||
} else if (filter_count == 0) {
|
||||
/* No filters need to be applied to the document: we can stream
|
||||
* directly from disk */
|
||||
error = write_file_stream(oid, odb, path, size);
|
||||
} else {
|
||||
/* We need to apply one or more filters */
|
||||
error = write_file_filtered(oid, odb, path, &write_filters);
|
||||
}
|
||||
|
||||
git_filters_free(&write_filters);
|
||||
|
||||
/*
|
||||
* TODO: eventually support streaming filtered files, for files
|
||||
* which are bigger than a given threshold. This is not a priority
|
||||
* because applying a filter in streaming mode changes the final
|
||||
* size of the blob, and without knowing its final size, the blob
|
||||
* cannot be written in stream mode to the ODB.
|
||||
*
|
||||
* The plan is to do streaming writes to a tempfile on disk and then
|
||||
* opening streaming that file to the ODB, using
|
||||
* `write_file_stream`.
|
||||
*
|
||||
* CAREFULLY DESIGNED APIS YO
|
||||
*/
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_blob_create_fromfile(git_oid *oid, git_repository *repo, const char *path)
|
||||
{
|
||||
int error = GIT_SUCCESS;
|
||||
int islnk = 0;
|
||||
int fd = 0;
|
||||
git_buf full_path = GIT_BUF_INIT;
|
||||
char buffer[2048];
|
||||
git_off_t size;
|
||||
git_odb_stream *stream = NULL;
|
||||
struct stat st;
|
||||
const char *workdir;
|
||||
git_odb *odb;
|
||||
int error;
|
||||
|
||||
workdir = git_repository_workdir(repo);
|
||||
if (workdir == NULL)
|
||||
return git__throw(GIT_ENOTFOUND, "Failed to create blob. (No working directory found)");
|
||||
assert(workdir); /* error to call this on bare repo */
|
||||
|
||||
error = git_buf_joinpath(&full_path, workdir, path);
|
||||
if (error < GIT_SUCCESS)
|
||||
return error;
|
||||
|
||||
error = p_lstat(full_path.ptr, &st);
|
||||
if (error < 0) {
|
||||
error = git__throw(GIT_EOSERR, "Failed to stat blob. %s", strerror(errno));
|
||||
goto cleanup;
|
||||
if (git_buf_joinpath(&full_path, workdir, path) < 0) {
|
||||
git_buf_free(&full_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
islnk = S_ISLNK(st.st_mode);
|
||||
size = st.st_size;
|
||||
error = blob_create_internal(oid, repo, git_buf_cstr(&full_path));
|
||||
|
||||
error = git_repository_odb__weakptr(&odb, repo);
|
||||
if (error < GIT_SUCCESS)
|
||||
goto cleanup;
|
||||
|
||||
if (!islnk) {
|
||||
if ((fd = p_open(full_path.ptr, O_RDONLY)) < 0) {
|
||||
error = git__throw(GIT_ENOTFOUND, "Failed to create blob. Could not open '%s'", full_path.ptr
|
||||
);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if ((error = git_odb_open_wstream(&stream, odb, (size_t)size, GIT_OBJ_BLOB)) < GIT_SUCCESS)
|
||||
goto cleanup;
|
||||
|
||||
while (size > 0) {
|
||||
ssize_t read_len;
|
||||
|
||||
if (!islnk)
|
||||
read_len = p_read(fd, buffer, sizeof(buffer));
|
||||
else
|
||||
read_len = p_readlink(full_path.ptr, buffer, sizeof(buffer));
|
||||
|
||||
if (read_len < 0) {
|
||||
error = git__throw(GIT_EOSERR, "Failed to create blob. Can't read full file");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
stream->write(stream, buffer, read_len);
|
||||
size -= read_len;
|
||||
}
|
||||
|
||||
error = stream->finalize_write(oid, stream);
|
||||
|
||||
cleanup:
|
||||
if (stream)
|
||||
stream->free(stream);
|
||||
if (!islnk && fd)
|
||||
p_close(fd);
|
||||
git_buf_free(&full_path);
|
||||
|
||||
return error == GIT_SUCCESS ? GIT_SUCCESS :
|
||||
git__rethrow(error, "Failed to create blob");
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_blob_create_fromdisk(git_oid *oid, git_repository *repo, const char *path)
|
||||
{
|
||||
int error;
|
||||
git_buf full_path = GIT_BUF_INIT;
|
||||
|
||||
if ((error = git_path_prettify(&full_path, path, NULL)) < 0) {
|
||||
git_buf_free(&full_path);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = blob_create_internal(oid, repo, git_buf_cstr(&full_path));
|
||||
|
||||
git_buf_free(&full_path);
|
||||
return error;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -19,5 +19,6 @@ struct git_blob {
|
||||
|
||||
void git_blob__free(git_blob *blob);
|
||||
int git_blob__parse(git_blob *blob, git_odb_object *obj);
|
||||
int git_blob__getbuf(git_buf *buffer, git_blob *blob);
|
||||
|
||||
#endif
|
||||
|
208
src/branch.c
Normal file
208
src/branch.c
Normal file
@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "commit.h"
|
||||
#include "branch.h"
|
||||
#include "tag.h"
|
||||
|
||||
static int retrieve_branch_reference(
|
||||
git_reference **branch_reference_out,
|
||||
git_repository *repo,
|
||||
const char *branch_name,
|
||||
int is_remote)
|
||||
{
|
||||
git_reference *branch;
|
||||
int error = -1;
|
||||
char *prefix;
|
||||
git_buf ref_name = GIT_BUF_INIT;
|
||||
|
||||
*branch_reference_out = NULL;
|
||||
|
||||
prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR;
|
||||
|
||||
if (git_buf_joinpath(&ref_name, prefix, branch_name) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0) {
|
||||
giterr_set(GITERR_REFERENCE,
|
||||
"Cannot locate %s branch '%s'.", is_remote ? "remote-tracking" : "local", branch_name);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
*branch_reference_out = branch;
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&ref_name);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int create_error_invalid(const char *msg)
|
||||
{
|
||||
giterr_set(GITERR_INVALID, "Cannot create branch - %s", msg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_branch_create(
|
||||
git_oid *oid_out,
|
||||
git_repository *repo,
|
||||
const char *branch_name,
|
||||
const git_object *target,
|
||||
int force)
|
||||
{
|
||||
git_otype target_type = GIT_OBJ_BAD;
|
||||
git_object *commit = NULL;
|
||||
git_reference *branch = NULL;
|
||||
git_buf canonical_branch_name = GIT_BUF_INIT;
|
||||
int error = -1;
|
||||
|
||||
assert(repo && branch_name && target && oid_out);
|
||||
|
||||
if (git_object_owner(target) != repo)
|
||||
return create_error_invalid("The given target does not belong to this repository");
|
||||
|
||||
target_type = git_object_type(target);
|
||||
|
||||
switch (target_type)
|
||||
{
|
||||
case GIT_OBJ_TAG:
|
||||
if (git_tag_peel(&commit, (git_tag *)target) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (git_object_type(commit) != GIT_OBJ_COMMIT) {
|
||||
create_error_invalid("The given target does not resolve to a commit");
|
||||
goto cleanup;
|
||||
}
|
||||
break;
|
||||
|
||||
case GIT_OBJ_COMMIT:
|
||||
commit = (git_object *)target;
|
||||
break;
|
||||
|
||||
default:
|
||||
return create_error_invalid("Only git_tag and git_commit objects are valid targets.");
|
||||
}
|
||||
|
||||
if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (git_reference_create_oid(&branch, repo, git_buf_cstr(&canonical_branch_name), git_object_id(commit), force) < 0)
|
||||
goto cleanup;
|
||||
|
||||
git_oid_cpy(oid_out, git_reference_oid(branch));
|
||||
error = 0;
|
||||
|
||||
cleanup:
|
||||
if (target_type == GIT_OBJ_TAG)
|
||||
git_object_free(commit);
|
||||
|
||||
git_reference_free(branch);
|
||||
git_buf_free(&canonical_branch_name);
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_branch_delete(git_repository *repo, const char *branch_name, git_branch_t branch_type)
|
||||
{
|
||||
git_reference *branch = NULL;
|
||||
git_reference *head = NULL;
|
||||
int error;
|
||||
|
||||
assert((branch_type == GIT_BRANCH_LOCAL) || (branch_type == GIT_BRANCH_REMOTE));
|
||||
|
||||
if ((error = retrieve_branch_reference(&branch, repo, branch_name, branch_type == GIT_BRANCH_REMOTE)) < 0)
|
||||
return error;
|
||||
|
||||
if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) {
|
||||
giterr_set(GITERR_REFERENCE, "Cannot locate HEAD.");
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
if ((git_reference_type(head) == GIT_REF_SYMBOLIC)
|
||||
&& (strcmp(git_reference_target(head), git_reference_name(branch)) == 0)) {
|
||||
giterr_set(GITERR_REFERENCE,
|
||||
"Cannot delete branch '%s' as it is the current HEAD of the repository.", branch_name);
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
if (git_reference_delete(branch) < 0)
|
||||
goto on_error;
|
||||
|
||||
git_reference_free(head);
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
git_reference_free(head);
|
||||
git_reference_free(branch);
|
||||
return -1;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
git_vector *branchlist;
|
||||
unsigned int branch_type;
|
||||
} branch_filter_data;
|
||||
|
||||
static int branch_list_cb(const char *branch_name, void *payload)
|
||||
{
|
||||
branch_filter_data *filter = (branch_filter_data *)payload;
|
||||
|
||||
if ((filter->branch_type & GIT_BRANCH_LOCAL && git__prefixcmp(branch_name, GIT_REFS_HEADS_DIR) == 0)
|
||||
|| (filter->branch_type & GIT_BRANCH_REMOTE && git__prefixcmp(branch_name, GIT_REFS_REMOTES_DIR) == 0))
|
||||
return git_vector_insert(filter->branchlist, git__strdup(branch_name));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_branch_list(git_strarray *branch_names, git_repository *repo, unsigned int list_flags)
|
||||
{
|
||||
int error;
|
||||
branch_filter_data filter;
|
||||
git_vector branchlist;
|
||||
|
||||
assert(branch_names && repo);
|
||||
|
||||
if (git_vector_init(&branchlist, 8, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
filter.branchlist = &branchlist;
|
||||
filter.branch_type = list_flags;
|
||||
|
||||
error = git_reference_foreach(repo, GIT_REF_LISTALL, &branch_list_cb, (void *)&filter);
|
||||
if (error < 0) {
|
||||
git_vector_free(&branchlist);
|
||||
return -1;
|
||||
}
|
||||
|
||||
branch_names->strings = (char **)branchlist.contents;
|
||||
branch_names->count = branchlist.length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_branch_move(git_repository *repo, const char *old_branch_name, const char *new_branch_name, int force)
|
||||
{
|
||||
git_reference *reference = NULL;
|
||||
git_buf old_reference_name = GIT_BUF_INIT, new_reference_name = GIT_BUF_INIT;
|
||||
int error = 0;
|
||||
|
||||
if ((error = git_buf_joinpath(&old_reference_name, GIT_REFS_HEADS_DIR, old_branch_name)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* We need to be able to return GIT_ENOTFOUND */
|
||||
if ((error = git_reference_lookup(&reference, repo, git_buf_cstr(&old_reference_name))) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
error = git_reference_rename(reference, git_buf_cstr(&new_reference_name), force);
|
||||
|
||||
cleanup:
|
||||
git_reference_free(reference);
|
||||
git_buf_free(&old_reference_name);
|
||||
git_buf_free(&new_reference_name);
|
||||
|
||||
return error;
|
||||
}
|
17
src/branch.h
Normal file
17
src/branch.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* 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_branch_h__
|
||||
#define INCLUDE_branch_h__
|
||||
|
||||
#include "git2/branch.h"
|
||||
|
||||
struct git_branch {
|
||||
char *remote; /* TODO: Make this a git_remote */
|
||||
char *merge;
|
||||
};
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
|
209
src/buffer.c
209
src/buffer.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -7,22 +7,25 @@
|
||||
#include "buffer.h"
|
||||
#include "posix.h"
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
|
||||
/* Used as default value for git_buf->ptr so that people can always
|
||||
* assume ptr is non-NULL and zero terminated even for new git_bufs.
|
||||
*/
|
||||
char git_buf_initbuf[1];
|
||||
char git_buf__initbuf[1];
|
||||
|
||||
char git_buf__oom[1];
|
||||
|
||||
#define ENSURE_SIZE(b, d) \
|
||||
if ((ssize_t)(d) > buf->asize && git_buf_grow(b, (d)) < GIT_SUCCESS)\
|
||||
return GIT_ENOMEM;
|
||||
if ((d) > buf->asize && git_buf_grow(b, (d)) < 0)\
|
||||
return -1;
|
||||
|
||||
|
||||
void git_buf_init(git_buf *buf, size_t initial_size)
|
||||
{
|
||||
buf->asize = 0;
|
||||
buf->size = 0;
|
||||
buf->ptr = git_buf_initbuf;
|
||||
buf->ptr = git_buf__initbuf;
|
||||
|
||||
if (initial_size)
|
||||
git_buf_grow(buf, initial_size);
|
||||
@ -31,8 +34,8 @@ void git_buf_init(git_buf *buf, size_t initial_size)
|
||||
int git_buf_grow(git_buf *buf, size_t target_size)
|
||||
{
|
||||
int error = git_buf_try_grow(buf, target_size);
|
||||
if (error != GIT_SUCCESS)
|
||||
buf->asize = -1;
|
||||
if (error != 0)
|
||||
buf->ptr = git_buf__oom;
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -41,17 +44,17 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
|
||||
char *new_ptr;
|
||||
size_t new_size;
|
||||
|
||||
if (buf->asize < 0)
|
||||
return GIT_ENOMEM;
|
||||
if (buf->ptr == git_buf__oom)
|
||||
return -1;
|
||||
|
||||
if (target_size <= (size_t)buf->asize)
|
||||
return GIT_SUCCESS;
|
||||
if (target_size <= buf->asize)
|
||||
return 0;
|
||||
|
||||
if (buf->asize == 0) {
|
||||
new_size = target_size;
|
||||
new_ptr = NULL;
|
||||
} else {
|
||||
new_size = (size_t)buf->asize;
|
||||
new_size = buf->asize;
|
||||
new_ptr = buf->ptr;
|
||||
}
|
||||
|
||||
@ -64,9 +67,8 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
|
||||
new_size = (new_size + 7) & ~7;
|
||||
|
||||
new_ptr = git__realloc(new_ptr, new_size);
|
||||
/* if realloc fails, return without modifying the git_buf */
|
||||
if (!new_ptr)
|
||||
return GIT_ENOMEM;
|
||||
return -1;
|
||||
|
||||
buf->asize = new_size;
|
||||
buf->ptr = new_ptr;
|
||||
@ -76,14 +78,14 @@ int git_buf_try_grow(git_buf *buf, size_t target_size)
|
||||
buf->size = buf->asize - 1;
|
||||
buf->ptr[buf->size] = '\0';
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void git_buf_free(git_buf *buf)
|
||||
{
|
||||
if (!buf) return;
|
||||
|
||||
if (buf->ptr != git_buf_initbuf)
|
||||
if (buf->ptr != git_buf__initbuf && buf->ptr != git_buf__oom)
|
||||
git__free(buf->ptr);
|
||||
|
||||
git_buf_init(buf, 0);
|
||||
@ -96,16 +98,6 @@ void git_buf_clear(git_buf *buf)
|
||||
buf->ptr[0] = '\0';
|
||||
}
|
||||
|
||||
int git_buf_oom(const git_buf *buf)
|
||||
{
|
||||
return (buf->asize < 0);
|
||||
}
|
||||
|
||||
int git_buf_lasterror(const git_buf *buf)
|
||||
{
|
||||
return (buf->asize < 0) ? GIT_ENOMEM : GIT_SUCCESS;
|
||||
}
|
||||
|
||||
int git_buf_set(git_buf *buf, const char *data, size_t len)
|
||||
{
|
||||
if (len == 0 || data == NULL) {
|
||||
@ -118,7 +110,7 @@ int git_buf_set(git_buf *buf, const char *data, size_t len)
|
||||
buf->size = len;
|
||||
buf->ptr[buf->size] = '\0';
|
||||
}
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_buf_sets(git_buf *buf, const char *string)
|
||||
@ -131,7 +123,7 @@ int git_buf_putc(git_buf *buf, char c)
|
||||
ENSURE_SIZE(buf, buf->size + 2);
|
||||
buf->ptr[buf->size++] = c;
|
||||
buf->ptr[buf->size] = '\0';
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_buf_put(git_buf *buf, const char *data, size_t len)
|
||||
@ -140,7 +132,7 @@ int git_buf_put(git_buf *buf, const char *data, size_t len)
|
||||
memmove(buf->ptr + buf->size, data, len);
|
||||
buf->size += len;
|
||||
buf->ptr[buf->size] = '\0';
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_buf_puts(git_buf *buf, const char *string)
|
||||
@ -149,24 +141,29 @@ int git_buf_puts(git_buf *buf, const char *string)
|
||||
return git_buf_put(buf, string, strlen(string));
|
||||
}
|
||||
|
||||
int git_buf_printf(git_buf *buf, const char *format, ...)
|
||||
int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
|
||||
{
|
||||
int len;
|
||||
va_list arglist;
|
||||
|
||||
ENSURE_SIZE(buf, buf->size + 1);
|
||||
ENSURE_SIZE(buf, buf->size + (strlen(format) * 2));
|
||||
|
||||
while (1) {
|
||||
va_start(arglist, format);
|
||||
len = p_vsnprintf(buf->ptr + buf->size, buf->asize - buf->size, format, arglist);
|
||||
va_end(arglist);
|
||||
va_list args;
|
||||
va_copy(args, ap);
|
||||
|
||||
len = p_vsnprintf(
|
||||
buf->ptr + buf->size,
|
||||
buf->asize - buf->size,
|
||||
format, args
|
||||
);
|
||||
|
||||
if (len < 0) {
|
||||
buf->asize = -1;
|
||||
return GIT_ENOMEM;
|
||||
git__free(buf->ptr);
|
||||
buf->ptr = git_buf__oom;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (len + 1 <= buf->asize - buf->size) {
|
||||
if ((size_t)len + 1 <= buf->asize - buf->size) {
|
||||
buf->size += len;
|
||||
break;
|
||||
}
|
||||
@ -174,7 +171,19 @@ int git_buf_printf(git_buf *buf, const char *format, ...)
|
||||
ENSURE_SIZE(buf, buf->size + len + 1);
|
||||
}
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_buf_printf(git_buf *buf, const char *format, ...)
|
||||
{
|
||||
int r;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, format);
|
||||
r = git_buf_vprintf(buf, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
|
||||
@ -205,14 +214,20 @@ void git_buf_consume(git_buf *buf, const char *end)
|
||||
}
|
||||
}
|
||||
|
||||
void git_buf_truncate(git_buf *buf, ssize_t len)
|
||||
void git_buf_truncate(git_buf *buf, size_t len)
|
||||
{
|
||||
if (len >= 0 && len < buf->size) {
|
||||
if (len < buf->size) {
|
||||
buf->size = len;
|
||||
buf->ptr[buf->size] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
void git_buf_rtruncate_at_char(git_buf *buf, char separator)
|
||||
{
|
||||
ssize_t idx = git_buf_rfind_next(buf, separator);
|
||||
git_buf_truncate(buf, idx < 0 ? 0 : (size_t)idx);
|
||||
}
|
||||
|
||||
void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
|
||||
{
|
||||
git_buf t = *buf_a;
|
||||
@ -224,7 +239,7 @@ char *git_buf_detach(git_buf *buf)
|
||||
{
|
||||
char *data = buf->ptr;
|
||||
|
||||
if (buf->asize <= 0)
|
||||
if (buf->asize == 0 || buf->ptr == git_buf__oom)
|
||||
return NULL;
|
||||
|
||||
git_buf_init(buf, 0);
|
||||
@ -232,7 +247,7 @@ char *git_buf_detach(git_buf *buf)
|
||||
return data;
|
||||
}
|
||||
|
||||
void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize)
|
||||
void git_buf_attach(git_buf *buf, char *ptr, size_t asize)
|
||||
{
|
||||
git_buf_free(buf);
|
||||
|
||||
@ -251,9 +266,9 @@ void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize)
|
||||
int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, error = GIT_SUCCESS;
|
||||
size_t total_size = 0;
|
||||
char *out;
|
||||
int i;
|
||||
size_t total_size = 0, original_size = buf->size;
|
||||
char *out, *original = buf->ptr;
|
||||
|
||||
if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
|
||||
++total_size; /* space for initial separator */
|
||||
@ -277,9 +292,10 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
|
||||
va_end(ap);
|
||||
|
||||
/* expand buffer if needed */
|
||||
if (total_size > 0 &&
|
||||
(error = git_buf_grow(buf, buf->size + total_size + 1)) < GIT_SUCCESS)
|
||||
return error;
|
||||
if (total_size == 0)
|
||||
return 0;
|
||||
if (git_buf_grow(buf, buf->size + total_size + 1) < 0)
|
||||
return -1;
|
||||
|
||||
out = buf->ptr + buf->size;
|
||||
|
||||
@ -296,12 +312,23 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
|
||||
if (!segment)
|
||||
continue;
|
||||
|
||||
/* deal with join that references buffer's original content */
|
||||
if (segment >= original && segment < original + original_size) {
|
||||
size_t offset = (segment - original);
|
||||
segment = buf->ptr + offset;
|
||||
segment_len = original_size - offset;
|
||||
} else {
|
||||
segment_len = strlen(segment);
|
||||
}
|
||||
|
||||
/* skip leading separators */
|
||||
if (out > buf->ptr && out[-1] == separator)
|
||||
while (*segment == separator) segment++;
|
||||
while (segment_len > 0 && *segment == separator) {
|
||||
segment++;
|
||||
segment_len--;
|
||||
}
|
||||
|
||||
/* copy over next buffer */
|
||||
segment_len = strlen(segment);
|
||||
if (segment_len > 0) {
|
||||
memmove(out, segment, segment_len);
|
||||
out += segment_len;
|
||||
@ -317,7 +344,7 @@ int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
|
||||
buf->size = out - buf->ptr;
|
||||
buf->ptr[buf->size] = '\0';
|
||||
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_buf_join(
|
||||
@ -326,8 +353,7 @@ int git_buf_join(
|
||||
const char *str_a,
|
||||
const char *str_b)
|
||||
{
|
||||
int error = GIT_SUCCESS;
|
||||
size_t strlen_a = strlen(str_a);
|
||||
size_t strlen_a = str_a ? strlen(str_a) : 0;
|
||||
size_t strlen_b = strlen(str_b);
|
||||
int need_sep = 0;
|
||||
ssize_t offset_a = -1;
|
||||
@ -346,9 +372,8 @@ int git_buf_join(
|
||||
if (str_a >= buf->ptr && str_a < buf->ptr + buf->size)
|
||||
offset_a = str_a - buf->ptr;
|
||||
|
||||
error = git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1);
|
||||
if (error < GIT_SUCCESS)
|
||||
return error;
|
||||
if (git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1) < 0)
|
||||
return -1;
|
||||
|
||||
/* fix up internal pointers */
|
||||
if (offset_a >= 0)
|
||||
@ -364,5 +389,73 @@ int git_buf_join(
|
||||
buf->size = strlen_a + strlen_b + need_sep;
|
||||
buf->ptr[buf->size] = '\0';
|
||||
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void git_buf_rtrim(git_buf *buf)
|
||||
{
|
||||
while (buf->size > 0) {
|
||||
if (!git__isspace(buf->ptr[buf->size - 1]))
|
||||
break;
|
||||
|
||||
buf->size--;
|
||||
}
|
||||
|
||||
buf->ptr[buf->size] = '\0';
|
||||
}
|
||||
|
||||
int git_buf_cmp(const git_buf *a, const git_buf *b)
|
||||
{
|
||||
int result = memcmp(a->ptr, b->ptr, min(a->size, b->size));
|
||||
return (result != 0) ? result :
|
||||
(a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
|
||||
}
|
||||
|
||||
int git_buf_common_prefix(git_buf *buf, const git_strarray *strings)
|
||||
{
|
||||
size_t i;
|
||||
const char *str, *pfx;
|
||||
|
||||
git_buf_clear(buf);
|
||||
|
||||
if (!strings || !strings->count)
|
||||
return 0;
|
||||
|
||||
/* initialize common prefix to first string */
|
||||
if (git_buf_sets(buf, strings->strings[0]) < 0)
|
||||
return -1;
|
||||
|
||||
/* go through the rest of the strings, truncating to shared prefix */
|
||||
for (i = 1; i < strings->count; ++i) {
|
||||
|
||||
for (str = strings->strings[i], pfx = buf->ptr;
|
||||
*str && *str == *pfx; str++, pfx++)
|
||||
/* scanning */;
|
||||
|
||||
git_buf_truncate(buf, pfx - buf->ptr);
|
||||
|
||||
if (!buf->size)
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool git_buf_is_binary(const git_buf *buf)
|
||||
{
|
||||
size_t i;
|
||||
int printable = 0, nonprintable = 0;
|
||||
|
||||
for (i = 0; i < buf->size; i++) {
|
||||
unsigned char c = buf->ptr[i];
|
||||
if (c > 0x1F && c < 0x7F)
|
||||
printable++;
|
||||
else if (c == '\0')
|
||||
return true;
|
||||
else if (!git__isspace(c))
|
||||
nonprintable++;
|
||||
}
|
||||
|
||||
return ((printable >> 7) < nonprintable);
|
||||
}
|
||||
|
||||
|
68
src/buffer.h
68
src/buffer.h
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -8,15 +8,17 @@
|
||||
#define INCLUDE_buffer_h__
|
||||
|
||||
#include "common.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
typedef struct {
|
||||
char *ptr;
|
||||
ssize_t asize, size;
|
||||
size_t asize, size;
|
||||
} git_buf;
|
||||
|
||||
extern char git_buf_initbuf[];
|
||||
extern char git_buf__initbuf[];
|
||||
extern char git_buf__oom[];
|
||||
|
||||
#define GIT_BUF_INIT { git_buf_initbuf, 0, 0 }
|
||||
#define GIT_BUF_INIT { git_buf__initbuf, 0, 0 }
|
||||
|
||||
/**
|
||||
* Initialize a git_buf structure.
|
||||
@ -32,7 +34,7 @@ void git_buf_init(git_buf *buf, size_t initial_size);
|
||||
* If the allocation fails, this will return an error and the buffer
|
||||
* will be marked as invalid for future operations. The existing
|
||||
* contents of the buffer will be preserved however.
|
||||
* @return GIT_SUCCESS or GIT_ENOMEM on failure
|
||||
* @return 0 on success or -1 on failure
|
||||
*/
|
||||
int git_buf_grow(git_buf *buf, size_t target_size);
|
||||
|
||||
@ -47,7 +49,7 @@ int git_buf_try_grow(git_buf *buf, size_t target_size);
|
||||
void git_buf_free(git_buf *buf);
|
||||
void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
|
||||
char *git_buf_detach(git_buf *buf);
|
||||
void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize);
|
||||
void git_buf_attach(git_buf *buf, char *ptr, size_t asize);
|
||||
|
||||
/**
|
||||
* Test if there have been any reallocation failures with this git_buf.
|
||||
@ -57,23 +59,21 @@ void git_buf_attach(git_buf *buf, char *ptr, ssize_t asize);
|
||||
* further calls to modify the buffer will fail. Check git_buf_oom() at the
|
||||
* end of your sequence and it will be true if you ran out of memory at any
|
||||
* point with that buffer.
|
||||
* @return 0 if no error, 1 if allocation error.
|
||||
*
|
||||
* @return false if no error, true if allocation error
|
||||
*/
|
||||
int git_buf_oom(const git_buf *buf);
|
||||
|
||||
/**
|
||||
* Just like git_buf_oom, except returns appropriate error code.
|
||||
* @return GIT_ENOMEM if allocation error, GIT_SUCCESS if not.
|
||||
*/
|
||||
int git_buf_lasterror(const git_buf *buf);
|
||||
GIT_INLINE(bool) git_buf_oom(const git_buf *buf)
|
||||
{
|
||||
return (buf->ptr == git_buf__oom);
|
||||
}
|
||||
|
||||
/*
|
||||
* The functions below that return int values, will return GIT_ENOMEM
|
||||
* if they fail to expand the git_buf when they are called, otherwise
|
||||
* GIT_SUCCESS. Passing a git_buf that has failed an allocation will
|
||||
* automatically return GIT_ENOMEM for all further calls. As a result,
|
||||
* you can ignore the return code of these functions and call them in a
|
||||
* series then just call git_buf_lasterror at the end.
|
||||
* Functions below that return int value error codes will return 0 on
|
||||
* success or -1 on failure (which generally means an allocation failed).
|
||||
* Using a git_buf where the allocation has failed with result in -1 from
|
||||
* all further calls using that buffer. As a result, you can ignore the
|
||||
* return code of these functions and call them in a series then just call
|
||||
* git_buf_oom at the end.
|
||||
*/
|
||||
int git_buf_set(git_buf *buf, const char *data, size_t len);
|
||||
int git_buf_sets(git_buf *buf, const char *string);
|
||||
@ -81,37 +81,55 @@ int git_buf_putc(git_buf *buf, char c);
|
||||
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);
|
||||
int git_buf_vprintf(git_buf *buf, const char *format, va_list ap);
|
||||
void git_buf_clear(git_buf *buf);
|
||||
void git_buf_consume(git_buf *buf, const char *end);
|
||||
void git_buf_truncate(git_buf *buf, ssize_t len);
|
||||
void git_buf_truncate(git_buf *buf, size_t len);
|
||||
void git_buf_rtruncate_at_char(git_buf *path, char separator);
|
||||
|
||||
int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...);
|
||||
int git_buf_join(git_buf *buf, char separator, const char *str_a, const char *str_b);
|
||||
|
||||
/**
|
||||
* Join two strings as paths, inserting a slash between as needed.
|
||||
* @return error code or GIT_SUCCESS
|
||||
* @return 0 on success, -1 on failure
|
||||
*/
|
||||
GIT_INLINE(int) git_buf_joinpath(git_buf *buf, const char *a, const char *b)
|
||||
{
|
||||
return git_buf_join(buf, '/', a, b);
|
||||
}
|
||||
|
||||
GIT_INLINE(const char *) git_buf_cstr(git_buf *buf)
|
||||
GIT_INLINE(const char *) git_buf_cstr(const git_buf *buf)
|
||||
{
|
||||
return buf->ptr;
|
||||
}
|
||||
|
||||
GIT_INLINE(size_t) git_buf_len(const git_buf *buf)
|
||||
{
|
||||
return buf->size;
|
||||
}
|
||||
|
||||
void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf);
|
||||
|
||||
#define git_buf_PUTS(buf, str) git_buf_put(buf, str, sizeof(str) - 1)
|
||||
|
||||
GIT_INLINE(int) git_buf_rfind_next(git_buf *buf, char ch)
|
||||
GIT_INLINE(ssize_t) git_buf_rfind_next(git_buf *buf, char ch)
|
||||
{
|
||||
int idx = buf->size - 1;
|
||||
ssize_t idx = (ssize_t)buf->size - 1;
|
||||
while (idx >= 0 && buf->ptr[idx] == ch) idx--;
|
||||
while (idx >= 0 && buf->ptr[idx] != ch) idx--;
|
||||
return idx;
|
||||
}
|
||||
|
||||
/* Remove whitespace from the end of the buffer */
|
||||
void git_buf_rtrim(git_buf *buf);
|
||||
|
||||
int git_buf_cmp(const git_buf *a, const git_buf *b);
|
||||
|
||||
/* Fill buf with the common prefix of a array of strings */
|
||||
int git_buf_common_prefix(git_buf *buf, const git_strarray *strings);
|
||||
|
||||
/* Check if buffer looks like it contains binary data */
|
||||
bool git_buf_is_binary(const git_buf *buf);
|
||||
|
||||
#endif
|
||||
|
18
src/cache.c
18
src/cache.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -9,21 +9,14 @@
|
||||
#include "repository.h"
|
||||
#include "commit.h"
|
||||
#include "thread-utils.h"
|
||||
#include "util.h"
|
||||
#include "cache.h"
|
||||
|
||||
int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_ptr)
|
||||
{
|
||||
if (size < 8)
|
||||
size = 8;
|
||||
|
||||
/* round up size to closest power of 2 */
|
||||
size--;
|
||||
size |= size >> 1;
|
||||
size |= size >> 2;
|
||||
size |= size >> 4;
|
||||
size |= size >> 8;
|
||||
size |= size >> 16;
|
||||
size++;
|
||||
size = git__size_t_powerof2(size);
|
||||
|
||||
cache->size_mask = size - 1;
|
||||
cache->lru_count = 0;
|
||||
@ -32,11 +25,10 @@ int git_cache_init(git_cache *cache, size_t size, git_cached_obj_freeptr free_pt
|
||||
git_mutex_init(&cache->lock);
|
||||
|
||||
cache->nodes = git__malloc(size * sizeof(git_cached_obj *));
|
||||
if (cache->nodes == NULL)
|
||||
return GIT_ENOMEM;
|
||||
GITERR_CHECK_ALLOC(cache->nodes);
|
||||
|
||||
memset(cache->nodes, 0x0, size * sizeof(git_cached_obj *));
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void git_cache_free(git_cache *cache)
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -33,21 +33,7 @@
|
||||
# define GIT_TYPEOF(x)
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
# define GIT_UNUSED(x)
|
||||
#else
|
||||
# ifdef __GNUC__
|
||||
# define GIT_UNUSED(x) x __attribute__ ((__unused__))
|
||||
# else
|
||||
# define GIT_UNUSED(x) x
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define GIT_UNUSED_ARG(x) ((void)(x)); /* note trailing ; */
|
||||
#else
|
||||
#define GIT_UNUSED_ARG(x)
|
||||
#endif
|
||||
#define GIT_UNUSED(x) ((void)(x))
|
||||
|
||||
/* Define the printf format specifer to use for size_t output */
|
||||
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||
@ -64,4 +50,20 @@
|
||||
# pragma warning ( disable : 4127 )
|
||||
#endif
|
||||
|
||||
#if defined (_MSC_VER)
|
||||
typedef unsigned char bool;
|
||||
# define true 1
|
||||
# define false 0
|
||||
#else
|
||||
# include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#ifndef va_copy
|
||||
# ifdef __va_copy
|
||||
# define va_copy(dst, src) __va_copy(dst, src)
|
||||
# else
|
||||
# define va_copy(dst, src) ((dst) = (src))
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#endif /* INCLUDE_compat_h__ */
|
||||
|
196
src/commit.c
196
src/commit.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -14,6 +14,7 @@
|
||||
#include "odb.h"
|
||||
#include "commit.h"
|
||||
#include "signature.h"
|
||||
#include "message.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
@ -69,24 +70,84 @@ int git_commit_create_v(
|
||||
...)
|
||||
{
|
||||
va_list ap;
|
||||
int i, error;
|
||||
int i, res;
|
||||
const git_commit **parents;
|
||||
|
||||
parents = git__malloc(parent_count * sizeof(git_commit *));
|
||||
GITERR_CHECK_ALLOC(parents);
|
||||
|
||||
va_start(ap, parent_count);
|
||||
for (i = 0; i < parent_count; ++i)
|
||||
parents[i] = va_arg(ap, const git_commit *);
|
||||
va_end(ap);
|
||||
|
||||
error = git_commit_create(
|
||||
res = git_commit_create(
|
||||
oid, repo, update_ref, author, committer,
|
||||
message_encoding, message,
|
||||
tree, parent_count, parents);
|
||||
|
||||
git__free((void *)parents);
|
||||
return res;
|
||||
}
|
||||
|
||||
return error;
|
||||
/* Update the reference named `ref_name` so it points to `oid` */
|
||||
static int update_reference(git_repository *repo, git_oid *oid, const char *ref_name)
|
||||
{
|
||||
git_reference *ref;
|
||||
int res;
|
||||
|
||||
res = git_reference_lookup(&ref, repo, ref_name);
|
||||
|
||||
/* If we haven't found the reference at all, we assume we need to create
|
||||
* a new reference and that's it */
|
||||
if (res == GIT_ENOTFOUND) {
|
||||
giterr_clear();
|
||||
return git_reference_create_oid(NULL, repo, ref_name, oid, 1);
|
||||
}
|
||||
|
||||
if (res < 0)
|
||||
return -1;
|
||||
|
||||
/* If we have found a reference, but it's symbolic, we need to update
|
||||
* the direct reference it points to */
|
||||
if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
|
||||
git_reference *aux;
|
||||
const char *sym_target;
|
||||
|
||||
/* The target pointed at by this reference */
|
||||
sym_target = git_reference_target(ref);
|
||||
|
||||
/* resolve the reference to the target it points to */
|
||||
res = git_reference_resolve(&aux, ref);
|
||||
|
||||
/*
|
||||
* if the symbolic reference pointed to an inexisting ref,
|
||||
* this is means we're creating a new branch, for example.
|
||||
* We need to create a new direct reference with that name
|
||||
*/
|
||||
if (res == GIT_ENOTFOUND) {
|
||||
giterr_clear();
|
||||
res = git_reference_create_oid(NULL, repo, sym_target, oid, 1);
|
||||
git_reference_free(ref);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* free the original symbolic reference now; not before because
|
||||
* we're using the `sym_target` pointer */
|
||||
git_reference_free(ref);
|
||||
|
||||
if (res < 0)
|
||||
return -1;
|
||||
|
||||
/* store the newly found direct reference in its place */
|
||||
ref = aux;
|
||||
}
|
||||
|
||||
/* ref is made to point to `oid`: ref is either the original reference,
|
||||
* or the target of the symbolic reference we've looked up */
|
||||
res = git_reference_set_oid(ref, oid);
|
||||
git_reference_free(ref);
|
||||
return res;
|
||||
}
|
||||
|
||||
int git_commit_create(
|
||||
@ -101,21 +162,16 @@ int git_commit_create(
|
||||
int parent_count,
|
||||
const git_commit *parents[])
|
||||
{
|
||||
git_buf commit = GIT_BUF_INIT;
|
||||
int error, i;
|
||||
git_buf commit = GIT_BUF_INIT, cleaned_message = GIT_BUF_INIT;
|
||||
int i;
|
||||
git_odb *odb;
|
||||
|
||||
if (git_object_owner((const git_object *)tree) != repo)
|
||||
return git__throw(GIT_EINVALIDARGS, "The given tree does not belong to this repository");
|
||||
assert(git_object_owner((const git_object *)tree) == repo);
|
||||
|
||||
git_oid__writebuf(&commit, "tree ", git_object_id((const git_object *)tree));
|
||||
|
||||
for (i = 0; i < parent_count; ++i) {
|
||||
if (git_object_owner((const git_object *)parents[i]) != repo) {
|
||||
error = git__throw(GIT_EINVALIDARGS, "The given parent does not belong to this repository");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
assert(git_object_owner((const git_object *)parents[i]) == repo);
|
||||
git_oid__writebuf(&commit, "parent ", git_object_id((const git_object *)parents[i]));
|
||||
}
|
||||
|
||||
@ -126,65 +182,34 @@ int git_commit_create(
|
||||
git_buf_printf(&commit, "encoding %s\n", message_encoding);
|
||||
|
||||
git_buf_putc(&commit, '\n');
|
||||
git_buf_puts(&commit, message);
|
||||
|
||||
if (git_buf_oom(&commit)) {
|
||||
error = git__throw(git_buf_lasterror(&commit),
|
||||
"Not enough memory to build the commit data");
|
||||
goto cleanup;
|
||||
}
|
||||
/* Remove comments by default */
|
||||
if (git_message_prettify(&cleaned_message, message, 1) < 0)
|
||||
goto on_error;
|
||||
|
||||
error = git_repository_odb__weakptr(&odb, repo);
|
||||
if (error < GIT_SUCCESS)
|
||||
goto cleanup;
|
||||
if (git_buf_puts(&commit, git_buf_cstr(&cleaned_message)) < 0)
|
||||
goto on_error;
|
||||
|
||||
git_buf_free(&cleaned_message);
|
||||
|
||||
if (git_repository_odb__weakptr(&odb, repo) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (git_odb_write(oid, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT) < 0)
|
||||
goto on_error;
|
||||
|
||||
error = git_odb_write(oid, odb, commit.ptr, commit.size, GIT_OBJ_COMMIT);
|
||||
git_buf_free(&commit);
|
||||
|
||||
if (error == GIT_SUCCESS && update_ref != NULL) {
|
||||
git_reference *head;
|
||||
git_reference *target;
|
||||
if (update_ref != NULL)
|
||||
return update_reference(repo, oid, update_ref);
|
||||
|
||||
error = git_reference_lookup(&head, repo, update_ref);
|
||||
if (error < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Failed to create commit");
|
||||
return 0;
|
||||
|
||||
error = git_reference_resolve(&target, head);
|
||||
if (error < GIT_SUCCESS) {
|
||||
if (error != GIT_ENOTFOUND) {
|
||||
git_reference_free(head);
|
||||
return git__rethrow(error, "Failed to create commit");
|
||||
}
|
||||
/*
|
||||
* The target of the reference was not found. This can happen
|
||||
* just after a repository has been initialized (the master
|
||||
* branch doesn't exist yet, as it doesn't have anything to
|
||||
* point to) or after an orphan checkout, so if the target
|
||||
* branch doesn't exist yet, create it and return.
|
||||
*/
|
||||
error = git_reference_create_oid(&target, repo, git_reference_target(head), oid, 1);
|
||||
|
||||
git_reference_free(head);
|
||||
if (error == GIT_SUCCESS)
|
||||
git_reference_free(target);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
error = git_reference_set_oid(target, oid);
|
||||
|
||||
git_reference_free(head);
|
||||
git_reference_free(target);
|
||||
}
|
||||
|
||||
if (error < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Failed to create commit");
|
||||
|
||||
return GIT_SUCCESS;
|
||||
|
||||
cleanup:
|
||||
on_error:
|
||||
git_buf_free(&commit);
|
||||
return error;
|
||||
git_buf_free(&cleaned_message);
|
||||
giterr_set(GITERR_OBJECT, "Failed to create commit.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
|
||||
@ -193,35 +218,40 @@ int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
|
||||
const char *buffer_end = (const char *)data + len;
|
||||
|
||||
git_oid parent_oid;
|
||||
int error;
|
||||
|
||||
git_vector_init(&commit->parent_oids, 4, NULL);
|
||||
|
||||
if ((error = git_oid__parse(&commit->tree_oid, &buffer, buffer_end, "tree ")) < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Failed to parse buffer");
|
||||
if (git_oid__parse(&commit->tree_oid, &buffer, buffer_end, "tree ") < 0)
|
||||
goto bad_buffer;
|
||||
|
||||
/*
|
||||
* TODO: commit grafts!
|
||||
*/
|
||||
|
||||
while (git_oid__parse(&parent_oid, &buffer, buffer_end, "parent ") == GIT_SUCCESS) {
|
||||
while (git_oid__parse(&parent_oid, &buffer, buffer_end, "parent ") == 0) {
|
||||
git_oid *new_oid;
|
||||
|
||||
new_oid = git__malloc(sizeof(git_oid));
|
||||
GITERR_CHECK_ALLOC(new_oid);
|
||||
|
||||
git_oid_cpy(new_oid, &parent_oid);
|
||||
|
||||
if (git_vector_insert(&commit->parent_oids, new_oid) < GIT_SUCCESS)
|
||||
return GIT_ENOMEM;
|
||||
if (git_vector_insert(&commit->parent_oids, new_oid) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
commit->author = git__malloc(sizeof(git_signature));
|
||||
if ((error = git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n')) < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Failed to parse commit");
|
||||
GITERR_CHECK_ALLOC(commit->author);
|
||||
|
||||
if (git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n') < 0)
|
||||
return -1;
|
||||
|
||||
/* Always parse the committer; we need the commit time */
|
||||
commit->committer = git__malloc(sizeof(git_signature));
|
||||
if ((error = git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n')) < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Failed to parse commit");
|
||||
GITERR_CHECK_ALLOC(commit->committer);
|
||||
|
||||
if (git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n') < 0)
|
||||
return -1;
|
||||
|
||||
if (git__prefixcmp(buffer, "encoding ") == 0) {
|
||||
const char *encoding_end;
|
||||
@ -232,8 +262,7 @@ int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
|
||||
encoding_end++;
|
||||
|
||||
commit->message_encoding = git__strndup(buffer, encoding_end - buffer);
|
||||
if (!commit->message_encoding)
|
||||
return GIT_ENOMEM;
|
||||
GITERR_CHECK_ALLOC(commit->message_encoding);
|
||||
|
||||
buffer = encoding_end;
|
||||
}
|
||||
@ -244,11 +273,14 @@ int git_commit__parse_buffer(git_commit *commit, const void *data, size_t len)
|
||||
|
||||
if (buffer <= buffer_end) {
|
||||
commit->message = git__strndup(buffer, buffer_end - buffer);
|
||||
if (!commit->message)
|
||||
return GIT_ENOMEM;
|
||||
GITERR_CHECK_ALLOC(commit->message);
|
||||
}
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
|
||||
bad_buffer:
|
||||
giterr_set(GITERR_OBJECT, "Failed to parse bad commit object");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_commit__parse(git_commit *commit, git_odb_object *obj)
|
||||
@ -286,8 +318,10 @@ int git_commit_parent(git_commit **parent, git_commit *commit, unsigned int n)
|
||||
assert(commit);
|
||||
|
||||
parent_oid = git_vector_get(&commit->parent_oids, n);
|
||||
if (parent_oid == NULL)
|
||||
return git__throw(GIT_ENOTFOUND, "Parent %u does not exist", n);
|
||||
if (parent_oid == NULL) {
|
||||
giterr_set(GITERR_INVALID, "Parent %u does not exist", n);
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
return git_commit_lookup(parent, commit->object.repo, parent_oid);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
|
14
src/common.h
14
src/common.h
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -46,6 +46,8 @@
|
||||
#include "thread-utils.h"
|
||||
#include "bswap.h"
|
||||
|
||||
#include <regex.h>
|
||||
|
||||
extern void git___throw(const char *, ...) GIT_FORMAT_PRINTF(1, 2);
|
||||
#define git__throw(error, ...) \
|
||||
(git___throw(__VA_ARGS__), error)
|
||||
@ -54,6 +56,16 @@ extern void git___rethrow(const char *, ...) GIT_FORMAT_PRINTF(1, 2);
|
||||
#define git__rethrow(error, ...) \
|
||||
(git___rethrow(__VA_ARGS__), error)
|
||||
|
||||
|
||||
#define GITERR_CHECK_ALLOC(ptr) if (ptr == NULL) { return -1; }
|
||||
|
||||
void giterr_set_oom(void);
|
||||
void giterr_set(int error_class, const char *string, ...);
|
||||
void giterr_clear(void);
|
||||
void giterr_set_str(int error_class, const char *string);
|
||||
void giterr_set_regex(const regex_t *regex, int error_code);
|
||||
|
||||
|
||||
#include "util.h"
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
@ -1,11 +1,11 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* 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_fnmatch__w32_h__
|
||||
#define INCLUDE_fnmatch__w32_h__
|
||||
#ifndef INCLUDE_fnmatch__compat_h__
|
||||
#define INCLUDE_fnmatch__compat_h__
|
||||
|
||||
#include "common.h"
|
||||
|
368
src/config.c
368
src/config.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -7,7 +7,6 @@
|
||||
|
||||
#include "common.h"
|
||||
#include "fileops.h"
|
||||
#include "hashtable.h"
|
||||
#include "config.h"
|
||||
#include "git2/config.h"
|
||||
#include "vector.h"
|
||||
@ -60,84 +59,77 @@ int git_config_new(git_config **out)
|
||||
git_config *cfg;
|
||||
|
||||
cfg = git__malloc(sizeof(git_config));
|
||||
if (cfg == NULL)
|
||||
return GIT_ENOMEM;
|
||||
GITERR_CHECK_ALLOC(cfg);
|
||||
|
||||
memset(cfg, 0x0, sizeof(git_config));
|
||||
|
||||
if (git_vector_init(&cfg->files, 3, config_backend_cmp) < 0) {
|
||||
git__free(cfg);
|
||||
return GIT_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
*out = cfg;
|
||||
GIT_REFCOUNT_INC(cfg);
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_config_add_file_ondisk(git_config *cfg, const char *path, int priority)
|
||||
{
|
||||
git_config_file *file = NULL;
|
||||
int error;
|
||||
|
||||
error = git_config_file__ondisk(&file, path);
|
||||
if (error < GIT_SUCCESS)
|
||||
return error;
|
||||
if (git_config_file__ondisk(&file, path) < 0)
|
||||
return -1;
|
||||
|
||||
error = git_config_add_file(cfg, file, priority);
|
||||
if (error < GIT_SUCCESS) {
|
||||
if (git_config_add_file(cfg, file, priority) < 0) {
|
||||
/*
|
||||
* free manually; the file is not owned by the config
|
||||
* instance yet and will not be freed on cleanup
|
||||
*/
|
||||
file->free(file);
|
||||
return error;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_config_open_ondisk(git_config **cfg, const char *path)
|
||||
{
|
||||
int error;
|
||||
if (git_config_new(cfg) < 0)
|
||||
return -1;
|
||||
|
||||
error = git_config_new(cfg);
|
||||
if (error < GIT_SUCCESS)
|
||||
return error;
|
||||
|
||||
error = git_config_add_file_ondisk(*cfg, path, 1);
|
||||
if (error < GIT_SUCCESS)
|
||||
if (git_config_add_file_ondisk(*cfg, path, 1) < 0) {
|
||||
git_config_free(*cfg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_config_add_file(git_config *cfg, git_config_file *file, int priority)
|
||||
{
|
||||
file_internal *internal;
|
||||
int error;
|
||||
int result;
|
||||
|
||||
assert(cfg && file);
|
||||
|
||||
if ((error = file->open(file)) < GIT_SUCCESS)
|
||||
return git__throw(error, "Failed to open config file");
|
||||
if ((result = file->open(file)) < 0)
|
||||
return result;
|
||||
|
||||
internal = git__malloc(sizeof(file_internal));
|
||||
if (internal == NULL)
|
||||
return GIT_ENOMEM;
|
||||
GITERR_CHECK_ALLOC(internal);
|
||||
|
||||
internal->file = file;
|
||||
internal->priority = priority;
|
||||
|
||||
if (git_vector_insert(&cfg->files, internal) < 0) {
|
||||
git__free(internal);
|
||||
return GIT_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
git_vector_sort(&cfg->files);
|
||||
internal->file->cfg = cfg;
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -146,7 +138,7 @@ int git_config_add_file(git_config *cfg, git_config_file *file, int priority)
|
||||
|
||||
int git_config_foreach(git_config *cfg, int (*fn)(const char *, const char *, void *), void *data)
|
||||
{
|
||||
int ret = GIT_SUCCESS;
|
||||
int ret = 0;
|
||||
unsigned int i;
|
||||
file_internal *internal;
|
||||
git_config_file *file;
|
||||
@ -165,8 +157,7 @@ int git_config_delete(git_config *cfg, const char *name)
|
||||
file_internal *internal;
|
||||
git_config_file *file;
|
||||
|
||||
if (cfg->files.length == 0)
|
||||
return git__throw(GIT_EINVALIDARGS, "Cannot delete variable; no files open in the `git_config` instance");
|
||||
assert(cfg->files.length);
|
||||
|
||||
internal = git_vector_get(&cfg->files, 0);
|
||||
file = internal->file;
|
||||
@ -200,8 +191,7 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
|
||||
file_internal *internal;
|
||||
git_config_file *file;
|
||||
|
||||
if (cfg->files.length == 0)
|
||||
return git__throw(GIT_EINVALIDARGS, "Cannot set variable value; no files open in the `git_config` instance");
|
||||
assert(cfg->files.length);
|
||||
|
||||
internal = git_vector_get(&cfg->files, 0);
|
||||
file = internal->file;
|
||||
@ -209,23 +199,13 @@ int git_config_set_string(git_config *cfg, const char *name, const char *value)
|
||||
return file->set(file, name, value);
|
||||
}
|
||||
|
||||
/***********
|
||||
* Getters
|
||||
***********/
|
||||
|
||||
int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
|
||||
static int parse_int64(int64_t *out, const char *value)
|
||||
{
|
||||
const char *value, *num_end;
|
||||
int ret;
|
||||
const char *num_end;
|
||||
int64_t num;
|
||||
|
||||
ret = git_config_get_string(cfg, name, &value);
|
||||
if (ret < GIT_SUCCESS)
|
||||
return git__rethrow(ret, "Failed to retrieve value for '%s'", name);
|
||||
|
||||
ret = git__strtol64(&num, value, &num_end, 0);
|
||||
if (ret < GIT_SUCCESS)
|
||||
return git__rethrow(ret, "Failed to convert value for '%s'", name);
|
||||
if (git__strtol64(&num, value, &num_end, 0) < 0)
|
||||
return -1;
|
||||
|
||||
switch (*num_end) {
|
||||
case 'g':
|
||||
@ -245,96 +225,217 @@ int git_config_get_int64(git_config *cfg, const char *name, int64_t *out)
|
||||
/* check that that there are no more characters after the
|
||||
* given modifier suffix */
|
||||
if (num_end[1] != '\0')
|
||||
return git__throw(GIT_EINVALIDTYPE,
|
||||
"Failed to get value for '%s'. Invalid type suffix", name);
|
||||
return -1;
|
||||
|
||||
/* fallthrough */
|
||||
|
||||
case '\0':
|
||||
*out = num;
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
return git__throw(GIT_EINVALIDTYPE,
|
||||
"Failed to get value for '%s'. Value is of invalid type", name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int git_config_get_int32(git_config *cfg, const char *name, int32_t *out)
|
||||
static int parse_int32(int32_t *out, const char *value)
|
||||
{
|
||||
int64_t tmp_long;
|
||||
int32_t tmp_int;
|
||||
int ret;
|
||||
int64_t tmp;
|
||||
int32_t truncate;
|
||||
|
||||
ret = git_config_get_int64(cfg, name, &tmp_long);
|
||||
if (ret < GIT_SUCCESS)
|
||||
return git__rethrow(ret, "Failed to convert value for '%s'", name);
|
||||
|
||||
tmp_int = tmp_long & 0xFFFFFFFF;
|
||||
if (tmp_int != tmp_long)
|
||||
return git__throw(GIT_EOVERFLOW, "Value for '%s' is too large", name);
|
||||
if (parse_int64(&tmp, value) < 0)
|
||||
return -1;
|
||||
|
||||
*out = tmp_int;
|
||||
truncate = tmp & 0xFFFFFFFF;
|
||||
if (truncate != tmp)
|
||||
return -1;
|
||||
|
||||
return ret;
|
||||
*out = truncate;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_config_get_bool(git_config *cfg, const char *name, int *out)
|
||||
/***********
|
||||
* Getters
|
||||
***********/
|
||||
int git_config_lookup_map_value(
|
||||
git_cvar_map *maps, size_t map_n, const char *value, int *out)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (!value)
|
||||
return GIT_ENOTFOUND;
|
||||
|
||||
for (i = 0; i < map_n; ++i) {
|
||||
git_cvar_map *m = maps + i;
|
||||
|
||||
switch (m->cvar_type) {
|
||||
case GIT_CVAR_FALSE:
|
||||
case GIT_CVAR_TRUE: {
|
||||
int bool_val;
|
||||
|
||||
if (git__parse_bool(&bool_val, value) == 0 &&
|
||||
bool_val == (int)m->cvar_type) {
|
||||
*out = m->map_value;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GIT_CVAR_INT32:
|
||||
if (parse_int32(out, value) == 0)
|
||||
return 0;
|
||||
break;
|
||||
|
||||
case GIT_CVAR_STRING:
|
||||
if (strcasecmp(value, m->str_match) == 0) {
|
||||
*out = m->map_value;
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
int git_config_get_mapped(
|
||||
int *out,
|
||||
git_config *cfg,
|
||||
const char *name,
|
||||
git_cvar_map *maps,
|
||||
size_t map_n)
|
||||
{
|
||||
const char *value;
|
||||
int error = GIT_SUCCESS;
|
||||
int ret;
|
||||
|
||||
error = git_config_get_string(cfg, name, &value);
|
||||
if (error < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Failed to get value for %s", name);
|
||||
ret = git_config_get_string(&value, cfg, name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* A missing value means true */
|
||||
if (value == NULL) {
|
||||
*out = 1;
|
||||
return GIT_SUCCESS;
|
||||
}
|
||||
if (!git_config_lookup_map_value(maps, map_n, value, out))
|
||||
return 0;
|
||||
|
||||
if (!strcasecmp(value, "true") ||
|
||||
!strcasecmp(value, "yes") ||
|
||||
!strcasecmp(value, "on")) {
|
||||
*out = 1;
|
||||
return GIT_SUCCESS;
|
||||
}
|
||||
if (!strcasecmp(value, "false") ||
|
||||
!strcasecmp(value, "no") ||
|
||||
!strcasecmp(value, "off")) {
|
||||
*out = 0;
|
||||
return GIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Try to parse it as an integer */
|
||||
error = git_config_get_int32(cfg, name, out);
|
||||
if (error == GIT_SUCCESS)
|
||||
*out = !!(*out);
|
||||
|
||||
if (error < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Failed to get value for %s", name);
|
||||
return error;
|
||||
giterr_set(GITERR_CONFIG,
|
||||
"Failed to map the '%s' config variable with a valid value", name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_config_get_string(git_config *cfg, const char *name, const char **out)
|
||||
int git_config_get_int64(int64_t *out, git_config *cfg, const char *name)
|
||||
{
|
||||
const char *value;
|
||||
int ret;
|
||||
|
||||
ret = git_config_get_string(&value, cfg, name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (parse_int64(out, value) < 0) {
|
||||
giterr_set(GITERR_CONFIG, "Failed to parse '%s' as an integer", value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_config_get_int32(int32_t *out, git_config *cfg, const char *name)
|
||||
{
|
||||
const char *value;
|
||||
int ret;
|
||||
|
||||
ret = git_config_get_string(&value, cfg, name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (parse_int32(out, value) < 0) {
|
||||
giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a 32-bit integer", value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_config_get_bool(int *out, git_config *cfg, const char *name)
|
||||
{
|
||||
const char *value;
|
||||
int ret;
|
||||
|
||||
ret = git_config_get_string(&value, cfg, name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (git__parse_bool(out, value) == 0)
|
||||
return 0;
|
||||
|
||||
if (parse_int32(out, value) == 0) {
|
||||
*out = !!(*out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
giterr_set(GITERR_CONFIG, "Failed to parse '%s' as a boolean value", value);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_config_get_string(const char **out, git_config *cfg, const char *name)
|
||||
{
|
||||
file_internal *internal;
|
||||
unsigned int i;
|
||||
|
||||
assert(cfg->files.length);
|
||||
|
||||
*out = NULL;
|
||||
|
||||
git_vector_foreach(&cfg->files, i, internal) {
|
||||
git_config_file *file = internal->file;
|
||||
int ret = file->get(file, name, out);
|
||||
if (ret != GIT_ENOTFOUND)
|
||||
return ret;
|
||||
}
|
||||
|
||||
giterr_set(GITERR_CONFIG, "Config variable '%s' not found", name);
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
int git_config_get_multivar(git_config *cfg, const char *name, const char *regexp,
|
||||
int (*fn)(const char *value, void *data), void *data)
|
||||
{
|
||||
file_internal *internal;
|
||||
git_config_file *file;
|
||||
int error = GIT_ENOTFOUND;
|
||||
int ret = GIT_ENOTFOUND;
|
||||
unsigned int i;
|
||||
|
||||
if (cfg->files.length == 0)
|
||||
return git__throw(GIT_EINVALIDARGS, "Cannot get variable value; no files open in the `git_config` instance");
|
||||
assert(cfg->files.length);
|
||||
|
||||
for (i = 0; i < cfg->files.length; ++i) {
|
||||
internal = git_vector_get(&cfg->files, i);
|
||||
/*
|
||||
* This loop runs the "wrong" way 'round because we need to
|
||||
* look at every value from the most general to most specific
|
||||
*/
|
||||
for (i = cfg->files.length; i > 0; --i) {
|
||||
internal = git_vector_get(&cfg->files, i - 1);
|
||||
file = internal->file;
|
||||
if ((error = file->get(file, name, out)) == GIT_SUCCESS)
|
||||
return GIT_SUCCESS;
|
||||
ret = file->get_multivar(file, name, regexp, fn, data);
|
||||
if (ret < 0 && ret != GIT_ENOTFOUND)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return git__throw(error, "Config value '%s' not found", name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
|
||||
{
|
||||
file_internal *internal;
|
||||
git_config_file *file;
|
||||
int ret = GIT_ENOTFOUND;
|
||||
unsigned int i;
|
||||
|
||||
for (i = cfg->files.length; i > 0; --i) {
|
||||
internal = git_vector_get(&cfg->files, i - 1);
|
||||
file = internal->file;
|
||||
ret = file->set_multivar(file, name, regexp, value);
|
||||
if (ret < 0 && ret != GIT_ENOTFOUND)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_config_find_global_r(git_buf *path)
|
||||
@ -342,21 +443,26 @@ int git_config_find_global_r(git_buf *path)
|
||||
return git_futils_find_global_file(path, GIT_CONFIG_FILENAME);
|
||||
}
|
||||
|
||||
int git_config_find_global(char *global_config_path)
|
||||
int git_config_find_global(char *global_config_path, size_t length)
|
||||
{
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
int error = git_config_find_global_r(&path);
|
||||
int ret = git_config_find_global_r(&path);
|
||||
|
||||
if (error == GIT_SUCCESS) {
|
||||
if (path.size > GIT_PATH_MAX)
|
||||
error = git__throw(GIT_ESHORTBUFFER, "Path is too long");
|
||||
else
|
||||
git_buf_copy_cstr(global_config_path, GIT_PATH_MAX, &path);
|
||||
if (ret < 0) {
|
||||
git_buf_free(&path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
git_buf_free(&path);
|
||||
if (path.size >= length) {
|
||||
git_buf_free(&path);
|
||||
giterr_set(GITERR_NOMEMORY,
|
||||
"Path is to long to fit on the given buffer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return error;
|
||||
git_buf_copy_cstr(global_config_path, length, &path);
|
||||
git_buf_free(&path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_config_find_system_r(git_buf *path)
|
||||
@ -364,31 +470,39 @@ int git_config_find_system_r(git_buf *path)
|
||||
return git_futils_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM);
|
||||
}
|
||||
|
||||
int git_config_find_system(char *system_config_path)
|
||||
int git_config_find_system(char *system_config_path, size_t length)
|
||||
{
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
int error = git_config_find_system_r(&path);
|
||||
int ret = git_config_find_system_r(&path);
|
||||
|
||||
if (error == GIT_SUCCESS) {
|
||||
if (path.size > GIT_PATH_MAX)
|
||||
error = git__throw(GIT_ESHORTBUFFER, "Path is too long");
|
||||
else
|
||||
git_buf_copy_cstr(system_config_path, GIT_PATH_MAX, &path);
|
||||
if (ret < 0) {
|
||||
git_buf_free(&path);
|
||||
return ret;
|
||||
}
|
||||
|
||||
git_buf_free(&path);
|
||||
if (path.size >= length) {
|
||||
git_buf_free(&path);
|
||||
giterr_set(GITERR_NOMEMORY,
|
||||
"Path is to long to fit on the given buffer");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return error;
|
||||
git_buf_copy_cstr(system_config_path, length, &path);
|
||||
git_buf_free(&path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_config_open_global(git_config **out)
|
||||
{
|
||||
int error;
|
||||
char global_path[GIT_PATH_MAX];
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
|
||||
if ((error = git_config_find_global(global_path)) < GIT_SUCCESS)
|
||||
if ((error = git_config_find_global_r(&path)) < 0)
|
||||
return error;
|
||||
|
||||
return git_config_open_ondisk(out, global_path);
|
||||
error = git_config_open_ondisk(out, git_buf_cstr(&path));
|
||||
git_buf_free(&path);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -25,4 +25,9 @@ struct git_config {
|
||||
extern int git_config_find_global_r(git_buf *global_config_path);
|
||||
extern int git_config_find_system_r(git_buf *system_config_path);
|
||||
|
||||
extern int git_config_parse_bool(int *out, const char *bool_string);
|
||||
|
||||
extern int git_config_lookup_map_value(
|
||||
git_cvar_map *maps, size_t map_n, const char *value, int *out);
|
||||
|
||||
#endif
|
||||
|
94
src/config_cache.c
Normal file
94
src/config_cache.c
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "fileops.h"
|
||||
#include "config.h"
|
||||
#include "git2/config.h"
|
||||
#include "vector.h"
|
||||
#include "filter.h"
|
||||
#include "repository.h"
|
||||
|
||||
struct map_data {
|
||||
const char *cvar_name;
|
||||
git_cvar_map *maps;
|
||||
size_t map_count;
|
||||
int default_value;
|
||||
};
|
||||
|
||||
/*
|
||||
* core.eol
|
||||
* Sets the line ending type to use in the working directory for
|
||||
* files that have the text property set. Alternatives are lf, crlf
|
||||
* and native, which uses the platform’s native line ending. The default
|
||||
* value is native. See gitattributes(5) for more information on
|
||||
* end-of-line conversion.
|
||||
*/
|
||||
static git_cvar_map _cvar_map_eol[] = {
|
||||
{GIT_CVAR_FALSE, NULL, GIT_EOL_UNSET},
|
||||
{GIT_CVAR_STRING, "lf", GIT_EOL_LF},
|
||||
{GIT_CVAR_STRING, "crlf", GIT_EOL_CRLF},
|
||||
{GIT_CVAR_STRING, "native", GIT_EOL_NATIVE}
|
||||
};
|
||||
|
||||
/*
|
||||
* core.autocrlf
|
||||
* Setting this variable to "true" is almost the same as setting
|
||||
* the text attribute to "auto" on all files except that text files are
|
||||
* not guaranteed to be normalized: files that contain CRLF in the
|
||||
* repository will not be touched. Use this setting if you want to have
|
||||
* CRLF line endings in your working directory even though the repository
|
||||
* does not have normalized line endings. This variable can be set to input,
|
||||
* in which case no output conversion is performed.
|
||||
*/
|
||||
static git_cvar_map _cvar_map_autocrlf[] = {
|
||||
{GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE},
|
||||
{GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE},
|
||||
{GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT}
|
||||
};
|
||||
|
||||
static struct map_data _cvar_maps[] = {
|
||||
{"core.autocrlf", _cvar_map_autocrlf, ARRAY_SIZE(_cvar_map_autocrlf), GIT_AUTO_CRLF_DEFAULT},
|
||||
{"core.eol", _cvar_map_eol, ARRAY_SIZE(_cvar_map_eol), GIT_EOL_DEFAULT}
|
||||
};
|
||||
|
||||
int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
|
||||
{
|
||||
*out = repo->cvar_cache[(int)cvar];
|
||||
|
||||
if (*out == GIT_CVAR_NOT_CACHED) {
|
||||
struct map_data *data = &_cvar_maps[(int)cvar];
|
||||
git_config *config;
|
||||
int error;
|
||||
|
||||
error = git_repository_config__weakptr(&config, repo);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
error = git_config_get_mapped(out,
|
||||
config, data->cvar_name, data->maps, data->map_count);
|
||||
|
||||
if (error == GIT_ENOTFOUND)
|
||||
*out = data->default_value;
|
||||
|
||||
else if (error < 0)
|
||||
return error;
|
||||
|
||||
repo->cvar_cache[(int)cvar] = *out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void git_repository__cvar_cache_clear(git_repository *repo)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < GIT_CVAR_CACHE_MAX; ++i)
|
||||
repo->cvar_cache[i] = GIT_CVAR_NOT_CACHED;
|
||||
}
|
||||
|
1130
src/config_file.c
1130
src/config_file.c
File diff suppressed because it is too large
Load Diff
31
src/config_file.h
Normal file
31
src/config_file.h
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2012 the libgit2 contributors
|
||||
*
|
||||
* 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_config_file_h__
|
||||
#define INCLUDE_config_file_h__
|
||||
|
||||
#include "git2/config.h"
|
||||
|
||||
GIT_INLINE(int) git_config_file_open(git_config_file *cfg)
|
||||
{
|
||||
return cfg->open(cfg);
|
||||
}
|
||||
|
||||
GIT_INLINE(void) git_config_file_free(git_config_file *cfg)
|
||||
{
|
||||
cfg->free(cfg);
|
||||
}
|
||||
|
||||
GIT_INLINE(int) git_config_file_foreach(
|
||||
git_config_file *cfg,
|
||||
int (*fn)(const char *key, const char *value, void *data),
|
||||
void *data)
|
||||
{
|
||||
return cfg->foreach(cfg, fn, data);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
228
src/crlf.c
Normal file
228
src/crlf.c
Normal file
@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "fileops.h"
|
||||
#include "hash.h"
|
||||
#include "filter.h"
|
||||
#include "repository.h"
|
||||
|
||||
#include "git2/attr.h"
|
||||
|
||||
struct crlf_attrs {
|
||||
int crlf_action;
|
||||
int eol;
|
||||
};
|
||||
|
||||
struct crlf_filter {
|
||||
git_filter f;
|
||||
struct crlf_attrs attrs;
|
||||
};
|
||||
|
||||
static int check_crlf(const char *value)
|
||||
{
|
||||
if (GIT_ATTR_TRUE(value))
|
||||
return GIT_CRLF_TEXT;
|
||||
|
||||
if (GIT_ATTR_FALSE(value))
|
||||
return GIT_CRLF_BINARY;
|
||||
|
||||
if (GIT_ATTR_UNSPECIFIED(value))
|
||||
return GIT_CRLF_GUESS;
|
||||
|
||||
if (strcmp(value, "input") == 0)
|
||||
return GIT_CRLF_INPUT;
|
||||
|
||||
if (strcmp(value, "auto") == 0)
|
||||
return GIT_CRLF_AUTO;
|
||||
|
||||
return GIT_CRLF_GUESS;
|
||||
}
|
||||
|
||||
static int check_eol(const char *value)
|
||||
{
|
||||
if (GIT_ATTR_UNSPECIFIED(value))
|
||||
return GIT_EOL_UNSET;
|
||||
|
||||
if (strcmp(value, "lf") == 0)
|
||||
return GIT_EOL_LF;
|
||||
|
||||
if (strcmp(value, "crlf") == 0)
|
||||
return GIT_EOL_CRLF;
|
||||
|
||||
return GIT_EOL_UNSET;
|
||||
}
|
||||
|
||||
static int crlf_input_action(struct crlf_attrs *ca)
|
||||
{
|
||||
if (ca->crlf_action == GIT_CRLF_BINARY)
|
||||
return GIT_CRLF_BINARY;
|
||||
|
||||
if (ca->eol == GIT_EOL_LF)
|
||||
return GIT_CRLF_INPUT;
|
||||
|
||||
if (ca->eol == GIT_EOL_CRLF)
|
||||
return GIT_CRLF_CRLF;
|
||||
|
||||
return ca->crlf_action;
|
||||
}
|
||||
|
||||
static int crlf_load_attributes(struct crlf_attrs *ca, git_repository *repo, const char *path)
|
||||
{
|
||||
#define NUM_CONV_ATTRS 3
|
||||
|
||||
static const char *attr_names[NUM_CONV_ATTRS] = {
|
||||
"crlf", "eol", "text",
|
||||
};
|
||||
|
||||
const char *attr_vals[NUM_CONV_ATTRS];
|
||||
int error;
|
||||
|
||||
error = git_attr_get_many(attr_vals,
|
||||
repo, 0, path, NUM_CONV_ATTRS, attr_names);
|
||||
|
||||
if (error == GIT_ENOTFOUND) {
|
||||
ca->crlf_action = GIT_CRLF_GUESS;
|
||||
ca->eol = GIT_EOL_UNSET;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (error == 0) {
|
||||
ca->crlf_action = check_crlf(attr_vals[2]); /* text */
|
||||
if (ca->crlf_action == GIT_CRLF_GUESS)
|
||||
ca->crlf_action = check_crlf(attr_vals[0]); /* clrf */
|
||||
|
||||
ca->eol = check_eol(attr_vals[1]); /* eol */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int drop_crlf(git_buf *dest, const git_buf *source)
|
||||
{
|
||||
const char *scan = source->ptr, *next;
|
||||
const char *scan_end = git_buf_cstr(source) + git_buf_len(source);
|
||||
|
||||
/* Main scan loop. Find the next carriage return and copy the
|
||||
* whole chunk up to that point to the destination buffer.
|
||||
*/
|
||||
while ((next = memchr(scan, '\r', scan_end - scan)) != NULL) {
|
||||
/* copy input up to \r */
|
||||
if (next > scan)
|
||||
git_buf_put(dest, scan, next - scan);
|
||||
|
||||
/* Do not drop \r unless it is followed by \n */
|
||||
if (*(next + 1) != '\n')
|
||||
git_buf_putc(dest, '\r');
|
||||
|
||||
scan = next + 1;
|
||||
}
|
||||
|
||||
/* If there was no \r, then tell the library to skip this filter */
|
||||
if (scan == source->ptr)
|
||||
return -1;
|
||||
|
||||
/* Copy remaining input into dest */
|
||||
git_buf_put(dest, scan, scan_end - scan);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int crlf_apply_to_odb(git_filter *self, git_buf *dest, const git_buf *source)
|
||||
{
|
||||
struct crlf_filter *filter = (struct crlf_filter *)self;
|
||||
|
||||
assert(self && dest && source);
|
||||
|
||||
/* Empty file? Nothing to do */
|
||||
if (git_buf_len(source) == 0)
|
||||
return 0;
|
||||
|
||||
/* Heuristics to see if we can skip the conversion.
|
||||
* Straight from Core Git.
|
||||
*/
|
||||
if (filter->attrs.crlf_action == GIT_CRLF_AUTO ||
|
||||
filter->attrs.crlf_action == GIT_CRLF_GUESS) {
|
||||
|
||||
git_text_stats stats;
|
||||
git_text_gather_stats(&stats, source);
|
||||
|
||||
/*
|
||||
* We're currently not going to even try to convert stuff
|
||||
* that has bare CR characters. Does anybody do that crazy
|
||||
* stuff?
|
||||
*/
|
||||
if (stats.cr != stats.crlf)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* And add some heuristics for binary vs text, of course...
|
||||
*/
|
||||
if (git_text_is_binary(&stats))
|
||||
return -1;
|
||||
|
||||
#if 0
|
||||
if (crlf_action == CRLF_GUESS) {
|
||||
/*
|
||||
* If the file in the index has any CR in it, do not convert.
|
||||
* This is the new safer autocrlf handling.
|
||||
*/
|
||||
if (has_cr_in_index(path))
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!stats.cr)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Actually drop the carriage returns */
|
||||
return drop_crlf(dest, source);
|
||||
}
|
||||
|
||||
int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path)
|
||||
{
|
||||
struct crlf_attrs ca;
|
||||
struct crlf_filter *filter;
|
||||
int error;
|
||||
|
||||
/* Load gitattributes for the path */
|
||||
if ((error = crlf_load_attributes(&ca, repo, path)) < 0)
|
||||
return error;
|
||||
|
||||
/*
|
||||
* Use the core Git logic to see if we should perform CRLF for this file
|
||||
* based on its attributes & the value of `core.auto_crlf`
|
||||
*/
|
||||
ca.crlf_action = crlf_input_action(&ca);
|
||||
|
||||
if (ca.crlf_action == GIT_CRLF_BINARY)
|
||||
return 0;
|
||||
|
||||
if (ca.crlf_action == GIT_CRLF_GUESS) {
|
||||
int auto_crlf;
|
||||
|
||||
if ((error = git_repository__cvar(
|
||||
&auto_crlf, repo, GIT_CVAR_AUTO_CRLF)) < 0)
|
||||
return error;
|
||||
|
||||
if (auto_crlf == GIT_AUTO_CRLF_FALSE)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If we're good, we create a new filter object and push it
|
||||
* into the filters array */
|
||||
filter = git__malloc(sizeof(struct crlf_filter));
|
||||
GITERR_CHECK_ALLOC(filter);
|
||||
|
||||
filter->f.apply = &crlf_apply_to_odb;
|
||||
filter->f.do_free = NULL;
|
||||
memcpy(&filter->attrs, &ca, sizeof(struct crlf_attrs));
|
||||
|
||||
return git_vector_insert(filters, filter);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -51,14 +51,19 @@ int git__delta_apply(
|
||||
* if not we would underflow while accessing data from the
|
||||
* base object, resulting in data corruption or segfault.
|
||||
*/
|
||||
if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len))
|
||||
return git__throw(GIT_ERROR, "Failed to apply delta. Base size does not match given data");
|
||||
if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len)) {
|
||||
giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hdr_sz(&res_sz, &delta, delta_end) < 0)
|
||||
return git__throw(GIT_ERROR, "Failed to apply delta. Base size does not match given data");
|
||||
if (hdr_sz(&res_sz, &delta, delta_end) < 0) {
|
||||
giterr_set(GITERR_INVALID, "Failed to apply delta. Base size does not match given data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
res_dp = git__malloc(res_sz + 1);
|
||||
GITERR_CHECK_ALLOC(res_dp);
|
||||
|
||||
if ((res_dp = git__malloc(res_sz + 1)) == NULL)
|
||||
return GIT_ENOMEM;
|
||||
res_dp[res_sz] = '\0';
|
||||
out->data = res_dp;
|
||||
out->len = res_sz;
|
||||
@ -106,10 +111,11 @@ int git__delta_apply(
|
||||
|
||||
if (delta != delta_end || res_sz)
|
||||
goto fail;
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
git__free(out->data);
|
||||
out->data = NULL;
|
||||
return git__throw(GIT_ERROR, "Failed to apply delta");
|
||||
giterr_set(GITERR_INVALID, "Failed to apply delta");
|
||||
return -1;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -20,7 +20,7 @@
|
||||
* @param delta the delta to execute copy/insert instructions from.
|
||||
* @param delta_len total number of bytes in the delta.
|
||||
* @return
|
||||
* - GIT_SUCCESS on a successful delta unpack.
|
||||
* - 0 on a successful delta unpack.
|
||||
* - GIT_ERROR if the delta is corrupt or doesn't match the base.
|
||||
*/
|
||||
extern int git__delta_apply(
|
||||
|
783
src/diff.c
Normal file
783
src/diff.c
Normal file
@ -0,0 +1,783 @@
|
||||
/*
|
||||
* Copyright (C) 2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
#include "common.h"
|
||||
#include "git2/diff.h"
|
||||
#include "diff.h"
|
||||
#include "fileops.h"
|
||||
#include "config.h"
|
||||
#include "attr_file.h"
|
||||
|
||||
static char *diff_prefix_from_pathspec(const git_strarray *pathspec)
|
||||
{
|
||||
git_buf prefix = GIT_BUF_INIT;
|
||||
const char *scan;
|
||||
|
||||
if (git_buf_common_prefix(&prefix, pathspec) < 0)
|
||||
return NULL;
|
||||
|
||||
/* diff prefix will only be leading non-wildcards */
|
||||
for (scan = prefix.ptr; *scan && !git__iswildcard(*scan); ++scan);
|
||||
git_buf_truncate(&prefix, scan - prefix.ptr);
|
||||
|
||||
if (prefix.size > 0)
|
||||
return git_buf_detach(&prefix);
|
||||
|
||||
git_buf_free(&prefix);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool diff_pathspec_is_interesting(const git_strarray *pathspec)
|
||||
{
|
||||
const char *str;
|
||||
|
||||
if (pathspec == NULL || pathspec->count == 0)
|
||||
return false;
|
||||
if (pathspec->count > 1)
|
||||
return true;
|
||||
|
||||
str = pathspec->strings[0];
|
||||
if (!str || !str[0] || (!str[1] && (str[0] == '*' || str[0] == '.')))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool diff_path_matches_pathspec(git_diff_list *diff, const char *path)
|
||||
{
|
||||
unsigned int i;
|
||||
git_attr_fnmatch *match;
|
||||
|
||||
if (!diff->pathspec.length)
|
||||
return true;
|
||||
|
||||
git_vector_foreach(&diff->pathspec, i, match) {
|
||||
int result = p_fnmatch(match->pattern, path, 0);
|
||||
|
||||
/* if we didn't match, look for exact dirname prefix match */
|
||||
if (result == FNM_NOMATCH &&
|
||||
(match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
|
||||
strncmp(path, match->pattern, match->length) == 0 &&
|
||||
path[match->length] == '/')
|
||||
result = 0;
|
||||
|
||||
if (result == 0)
|
||||
return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? false : true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static git_diff_delta *diff_delta__alloc(
|
||||
git_diff_list *diff,
|
||||
git_delta_t status,
|
||||
const char *path)
|
||||
{
|
||||
git_diff_delta *delta = git__calloc(1, sizeof(git_diff_delta));
|
||||
if (!delta)
|
||||
return NULL;
|
||||
|
||||
delta->old_file.path = git_pool_strdup(&diff->pool, path);
|
||||
if (delta->old_file.path == NULL) {
|
||||
git__free(delta);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
delta->new_file.path = delta->old_file.path;
|
||||
|
||||
if (diff->opts.flags & GIT_DIFF_REVERSE) {
|
||||
switch (status) {
|
||||
case GIT_DELTA_ADDED: status = GIT_DELTA_DELETED; break;
|
||||
case GIT_DELTA_DELETED: status = GIT_DELTA_ADDED; break;
|
||||
default: break; /* leave other status values alone */
|
||||
}
|
||||
}
|
||||
delta->status = status;
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
static git_diff_delta *diff_delta__dup(
|
||||
const git_diff_delta *d, git_pool *pool)
|
||||
{
|
||||
git_diff_delta *delta = git__malloc(sizeof(git_diff_delta));
|
||||
if (!delta)
|
||||
return NULL;
|
||||
|
||||
memcpy(delta, d, sizeof(git_diff_delta));
|
||||
|
||||
delta->old_file.path = git_pool_strdup(pool, d->old_file.path);
|
||||
if (delta->old_file.path == NULL)
|
||||
goto fail;
|
||||
|
||||
if (d->new_file.path != d->old_file.path) {
|
||||
delta->new_file.path = git_pool_strdup(pool, d->new_file.path);
|
||||
if (delta->new_file.path == NULL)
|
||||
goto fail;
|
||||
} else {
|
||||
delta->new_file.path = delta->old_file.path;
|
||||
}
|
||||
|
||||
return delta;
|
||||
|
||||
fail:
|
||||
git__free(delta);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static git_diff_delta *diff_delta__merge_like_cgit(
|
||||
const git_diff_delta *a, const git_diff_delta *b, git_pool *pool)
|
||||
{
|
||||
git_diff_delta *dup = diff_delta__dup(a, pool);
|
||||
if (!dup)
|
||||
return NULL;
|
||||
|
||||
if (git_oid_cmp(&dup->new_file.oid, &b->new_file.oid) == 0)
|
||||
return dup;
|
||||
|
||||
git_oid_cpy(&dup->new_file.oid, &b->new_file.oid);
|
||||
|
||||
dup->new_file.mode = b->new_file.mode;
|
||||
dup->new_file.size = b->new_file.size;
|
||||
dup->new_file.flags = b->new_file.flags;
|
||||
|
||||
/* Emulate C git for merging two diffs (a la 'git diff <sha>').
|
||||
*
|
||||
* When C git does a diff between the work dir and a tree, it actually
|
||||
* diffs with the index but uses the workdir contents. This emulates
|
||||
* those choices so we can emulate the type of diff.
|
||||
*/
|
||||
if (git_oid_cmp(&dup->old_file.oid, &dup->new_file.oid) == 0) {
|
||||
if (dup->status == GIT_DELTA_DELETED)
|
||||
/* preserve pending delete info */;
|
||||
else if (b->status == GIT_DELTA_UNTRACKED ||
|
||||
b->status == GIT_DELTA_IGNORED)
|
||||
dup->status = b->status;
|
||||
else
|
||||
dup->status = GIT_DELTA_UNMODIFIED;
|
||||
}
|
||||
else if (dup->status == GIT_DELTA_UNMODIFIED ||
|
||||
b->status == GIT_DELTA_DELETED)
|
||||
dup->status = b->status;
|
||||
|
||||
return dup;
|
||||
}
|
||||
|
||||
static int diff_delta__from_one(
|
||||
git_diff_list *diff,
|
||||
git_delta_t status,
|
||||
const git_index_entry *entry)
|
||||
{
|
||||
git_diff_delta *delta;
|
||||
|
||||
if (status == GIT_DELTA_IGNORED &&
|
||||
(diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
|
||||
return 0;
|
||||
|
||||
if (status == GIT_DELTA_UNTRACKED &&
|
||||
(diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
|
||||
return 0;
|
||||
|
||||
if (!diff_path_matches_pathspec(diff, entry->path))
|
||||
return 0;
|
||||
|
||||
delta = diff_delta__alloc(diff, status, entry->path);
|
||||
GITERR_CHECK_ALLOC(delta);
|
||||
|
||||
/* This fn is just for single-sided diffs */
|
||||
assert(status != GIT_DELTA_MODIFIED);
|
||||
|
||||
if (delta->status == GIT_DELTA_DELETED) {
|
||||
delta->old_file.mode = entry->mode;
|
||||
delta->old_file.size = entry->file_size;
|
||||
git_oid_cpy(&delta->old_file.oid, &entry->oid);
|
||||
} else /* ADDED, IGNORED, UNTRACKED */ {
|
||||
delta->new_file.mode = entry->mode;
|
||||
delta->new_file.size = entry->file_size;
|
||||
git_oid_cpy(&delta->new_file.oid, &entry->oid);
|
||||
}
|
||||
|
||||
delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
|
||||
delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
|
||||
|
||||
if (git_vector_insert(&diff->deltas, delta) < 0) {
|
||||
git__free(delta);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int diff_delta__from_two(
|
||||
git_diff_list *diff,
|
||||
git_delta_t status,
|
||||
const git_index_entry *old_entry,
|
||||
const git_index_entry *new_entry,
|
||||
git_oid *new_oid)
|
||||
{
|
||||
git_diff_delta *delta;
|
||||
|
||||
if (status == GIT_DELTA_UNMODIFIED &&
|
||||
(diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
|
||||
return 0;
|
||||
|
||||
if ((diff->opts.flags & GIT_DIFF_REVERSE) != 0) {
|
||||
const git_index_entry *temp = old_entry;
|
||||
old_entry = new_entry;
|
||||
new_entry = temp;
|
||||
}
|
||||
|
||||
delta = diff_delta__alloc(diff, status, old_entry->path);
|
||||
GITERR_CHECK_ALLOC(delta);
|
||||
|
||||
delta->old_file.mode = old_entry->mode;
|
||||
git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
|
||||
delta->old_file.flags |= GIT_DIFF_FILE_VALID_OID;
|
||||
|
||||
delta->new_file.mode = new_entry->mode;
|
||||
git_oid_cpy(&delta->new_file.oid, new_oid ? new_oid : &new_entry->oid);
|
||||
if (new_oid || !git_oid_iszero(&new_entry->oid))
|
||||
delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
|
||||
|
||||
if (git_vector_insert(&diff->deltas, delta) < 0) {
|
||||
git__free(delta);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *diff_strdup_prefix(git_pool *pool, const char *prefix)
|
||||
{
|
||||
size_t len = strlen(prefix);
|
||||
|
||||
/* append '/' at end if needed */
|
||||
if (len > 0 && prefix[len - 1] != '/')
|
||||
return git_pool_strcat(pool, prefix, "/");
|
||||
else
|
||||
return git_pool_strndup(pool, prefix, len + 1);
|
||||
}
|
||||
|
||||
static int diff_delta__cmp(const void *a, const void *b)
|
||||
{
|
||||
const git_diff_delta *da = a, *db = b;
|
||||
int val = strcmp(da->old_file.path, db->old_file.path);
|
||||
return val ? val : ((int)da->status - (int)db->status);
|
||||
}
|
||||
|
||||
static int config_bool(git_config *cfg, const char *name, int defvalue)
|
||||
{
|
||||
int val = defvalue;
|
||||
|
||||
if (git_config_get_bool(&val, cfg, name) < 0)
|
||||
giterr_clear();
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static git_diff_list *git_diff_list_alloc(
|
||||
git_repository *repo, const git_diff_options *opts)
|
||||
{
|
||||
git_config *cfg;
|
||||
size_t i;
|
||||
git_diff_list *diff = git__calloc(1, sizeof(git_diff_list));
|
||||
if (diff == NULL)
|
||||
return NULL;
|
||||
|
||||
diff->repo = repo;
|
||||
|
||||
if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0 ||
|
||||
git_pool_init(&diff->pool, 1, 0) < 0)
|
||||
goto fail;
|
||||
|
||||
/* load config values that affect diff behavior */
|
||||
if (git_repository_config__weakptr(&cfg, repo) < 0)
|
||||
goto fail;
|
||||
if (config_bool(cfg, "core.symlinks", 1))
|
||||
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_HAS_SYMLINKS;
|
||||
if (config_bool(cfg, "core.ignorestat", 0))
|
||||
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_ASSUME_UNCHANGED;
|
||||
if (config_bool(cfg, "core.filemode", 1))
|
||||
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_EXEC_BIT;
|
||||
if (config_bool(cfg, "core.trustctime", 1))
|
||||
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_CTIME;
|
||||
/* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
|
||||
|
||||
if (opts == NULL)
|
||||
return diff;
|
||||
|
||||
memcpy(&diff->opts, opts, sizeof(git_diff_options));
|
||||
memset(&diff->opts.pathspec, 0, sizeof(diff->opts.pathspec));
|
||||
|
||||
diff->opts.old_prefix = diff_strdup_prefix(&diff->pool,
|
||||
opts->old_prefix ? opts->old_prefix : DIFF_OLD_PREFIX_DEFAULT);
|
||||
diff->opts.new_prefix = diff_strdup_prefix(&diff->pool,
|
||||
opts->new_prefix ? opts->new_prefix : DIFF_NEW_PREFIX_DEFAULT);
|
||||
|
||||
if (!diff->opts.old_prefix || !diff->opts.new_prefix)
|
||||
goto fail;
|
||||
|
||||
if (diff->opts.flags & GIT_DIFF_REVERSE) {
|
||||
char *swap = diff->opts.old_prefix;
|
||||
diff->opts.old_prefix = diff->opts.new_prefix;
|
||||
diff->opts.new_prefix = swap;
|
||||
}
|
||||
|
||||
/* only copy pathspec if it is "interesting" so we can test
|
||||
* diff->pathspec.length > 0 to know if it is worth calling
|
||||
* fnmatch as we iterate.
|
||||
*/
|
||||
if (!diff_pathspec_is_interesting(&opts->pathspec))
|
||||
return diff;
|
||||
|
||||
if (git_vector_init(
|
||||
&diff->pathspec, (unsigned int)opts->pathspec.count, NULL) < 0)
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < opts->pathspec.count; ++i) {
|
||||
int ret;
|
||||
const char *pattern = opts->pathspec.strings[i];
|
||||
git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch));
|
||||
if (!match)
|
||||
goto fail;
|
||||
ret = git_attr_fnmatch__parse(match, &diff->pool, NULL, &pattern);
|
||||
if (ret == GIT_ENOTFOUND) {
|
||||
git__free(match);
|
||||
continue;
|
||||
} else if (ret < 0)
|
||||
goto fail;
|
||||
|
||||
if (git_vector_insert(&diff->pathspec, match) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return diff;
|
||||
|
||||
fail:
|
||||
git_diff_list_free(diff);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void git_diff_list_free(git_diff_list *diff)
|
||||
{
|
||||
git_diff_delta *delta;
|
||||
git_attr_fnmatch *match;
|
||||
unsigned int i;
|
||||
|
||||
if (!diff)
|
||||
return;
|
||||
|
||||
git_vector_foreach(&diff->deltas, i, delta) {
|
||||
git__free(delta);
|
||||
diff->deltas.contents[i] = NULL;
|
||||
}
|
||||
git_vector_free(&diff->deltas);
|
||||
|
||||
git_vector_foreach(&diff->pathspec, i, match) {
|
||||
git__free(match);
|
||||
diff->pathspec.contents[i] = NULL;
|
||||
}
|
||||
git_vector_free(&diff->pathspec);
|
||||
|
||||
git_pool_clear(&diff->pool);
|
||||
git__free(diff);
|
||||
}
|
||||
|
||||
static int oid_for_workdir_item(
|
||||
git_repository *repo,
|
||||
const git_index_entry *item,
|
||||
git_oid *oid)
|
||||
{
|
||||
int result;
|
||||
git_buf full_path = GIT_BUF_INIT;
|
||||
|
||||
if (git_buf_joinpath(&full_path, git_repository_workdir(repo), item->path) < 0)
|
||||
return -1;
|
||||
|
||||
/* calculate OID for file if possible*/
|
||||
if (S_ISLNK(item->mode))
|
||||
result = git_odb__hashlink(oid, full_path.ptr);
|
||||
else if (!git__is_sizet(item->file_size)) {
|
||||
giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
|
||||
result = -1;
|
||||
} else {
|
||||
int fd = git_futils_open_ro(full_path.ptr);
|
||||
if (fd < 0)
|
||||
result = fd;
|
||||
else {
|
||||
result = git_odb__hashfd(
|
||||
oid, fd, (size_t)item->file_size, GIT_OBJ_BLOB);
|
||||
p_close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
git_buf_free(&full_path);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#define EXEC_BIT_MASK 0000111
|
||||
|
||||
static int maybe_modified(
|
||||
git_iterator *old_iter,
|
||||
const git_index_entry *oitem,
|
||||
git_iterator *new_iter,
|
||||
const git_index_entry *nitem,
|
||||
git_diff_list *diff)
|
||||
{
|
||||
git_oid noid, *use_noid = NULL;
|
||||
git_delta_t status = GIT_DELTA_MODIFIED;
|
||||
unsigned int omode = oitem->mode;
|
||||
unsigned int nmode = nitem->mode;
|
||||
|
||||
GIT_UNUSED(old_iter);
|
||||
|
||||
if (!diff_path_matches_pathspec(diff, oitem->path))
|
||||
return 0;
|
||||
|
||||
/* on platforms with no symlinks, promote plain files to symlinks */
|
||||
if (S_ISLNK(omode) && S_ISREG(nmode) &&
|
||||
!(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))
|
||||
nmode = GIT_MODE_TYPE(omode) | (nmode & GIT_MODE_PERMS_MASK);
|
||||
|
||||
/* on platforms with no execmode, clear exec bit from comparisons */
|
||||
if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_EXEC_BIT)) {
|
||||
omode = omode & ~EXEC_BIT_MASK;
|
||||
nmode = nmode & ~EXEC_BIT_MASK;
|
||||
}
|
||||
|
||||
/* support "assume unchanged" (badly, b/c we still stat everything) */
|
||||
if ((diff->diffcaps & GIT_DIFFCAPS_ASSUME_UNCHANGED) != 0)
|
||||
status = (oitem->flags_extended & GIT_IDXENTRY_INTENT_TO_ADD) ?
|
||||
GIT_DELTA_MODIFIED : GIT_DELTA_UNMODIFIED;
|
||||
|
||||
/* support "skip worktree" index bit */
|
||||
else if ((oitem->flags_extended & GIT_IDXENTRY_SKIP_WORKTREE) != 0)
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
/* if basic type of file changed, then split into delete and add */
|
||||
else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
|
||||
if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
|
||||
diff_delta__from_one(diff, GIT_DELTA_ADDED, nitem) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if oids and modes match, then file is unmodified */
|
||||
else if (git_oid_cmp(&oitem->oid, &nitem->oid) == 0 &&
|
||||
omode == nmode)
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
/* if we have a workdir item with an unknown oid, check deeper */
|
||||
else if (git_oid_iszero(&nitem->oid) && new_iter->type == GIT_ITERATOR_WORKDIR) {
|
||||
/* TODO: add check against index file st_mtime to avoid racy-git */
|
||||
|
||||
/* if they files look exactly alike, then we'll assume the same */
|
||||
if (oitem->file_size == nitem->file_size &&
|
||||
(!(diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) ||
|
||||
(oitem->ctime.seconds == nitem->ctime.seconds)) &&
|
||||
oitem->mtime.seconds == nitem->mtime.seconds &&
|
||||
(!(diff->diffcaps & GIT_DIFFCAPS_USE_DEV) ||
|
||||
(oitem->dev == nitem->dev)) &&
|
||||
oitem->ino == nitem->ino &&
|
||||
oitem->uid == nitem->uid &&
|
||||
oitem->gid == nitem->gid)
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
else if (S_ISGITLINK(nmode)) {
|
||||
git_submodule *sub;
|
||||
|
||||
if ((diff->opts.flags & GIT_DIFF_IGNORE_SUBMODULES) != 0)
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
else if (git_submodule_lookup(&sub, diff->repo, nitem->path) < 0)
|
||||
return -1;
|
||||
else if (sub->ignore == GIT_SUBMODULE_IGNORE_ALL)
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
else {
|
||||
/* TODO: support other GIT_SUBMODULE_IGNORE values */
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: check git attributes so we will not have to read the file
|
||||
* in if it is marked binary.
|
||||
*/
|
||||
|
||||
else if (oid_for_workdir_item(diff->repo, nitem, &noid) < 0)
|
||||
return -1;
|
||||
|
||||
else if (git_oid_cmp(&oitem->oid, &noid) == 0 &&
|
||||
omode == nmode)
|
||||
status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
/* store calculated oid so we don't have to recalc later */
|
||||
use_noid = &noid;
|
||||
}
|
||||
|
||||
return diff_delta__from_two(diff, status, oitem, nitem, use_noid);
|
||||
}
|
||||
|
||||
static int diff_from_iterators(
|
||||
git_repository *repo,
|
||||
const git_diff_options *opts, /**< can be NULL for defaults */
|
||||
git_iterator *old_iter,
|
||||
git_iterator *new_iter,
|
||||
git_diff_list **diff_ptr)
|
||||
{
|
||||
const git_index_entry *oitem, *nitem;
|
||||
git_buf ignore_prefix = GIT_BUF_INIT;
|
||||
git_diff_list *diff = git_diff_list_alloc(repo, opts);
|
||||
if (!diff)
|
||||
goto fail;
|
||||
|
||||
diff->old_src = old_iter->type;
|
||||
diff->new_src = new_iter->type;
|
||||
|
||||
if (git_iterator_current(old_iter, &oitem) < 0 ||
|
||||
git_iterator_current(new_iter, &nitem) < 0)
|
||||
goto fail;
|
||||
|
||||
/* run iterators building diffs */
|
||||
while (oitem || nitem) {
|
||||
|
||||
/* create DELETED records for old items not matched in new */
|
||||
if (oitem && (!nitem || strcmp(oitem->path, nitem->path) < 0)) {
|
||||
if (diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem) < 0 ||
|
||||
git_iterator_advance(old_iter, &oitem) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* create ADDED, TRACKED, or IGNORED records for new items not
|
||||
* matched in old (and/or descend into directories as needed)
|
||||
*/
|
||||
else if (nitem && (!oitem || strcmp(oitem->path, nitem->path) > 0)) {
|
||||
git_delta_t delta_type = GIT_DELTA_UNTRACKED;
|
||||
|
||||
/* check if contained in ignored parent directory */
|
||||
if (git_buf_len(&ignore_prefix) &&
|
||||
git__prefixcmp(nitem->path, git_buf_cstr(&ignore_prefix)) == 0)
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
|
||||
if (S_ISDIR(nitem->mode)) {
|
||||
/* recurse into directory only if there are tracked items in
|
||||
* it or if the user requested the contents of untracked
|
||||
* directories and it is not under an ignored directory.
|
||||
*/
|
||||
if ((oitem && git__prefixcmp(oitem->path, nitem->path) == 0) ||
|
||||
(delta_type == GIT_DELTA_UNTRACKED &&
|
||||
(diff->opts.flags & GIT_DIFF_RECURSE_UNTRACKED_DIRS) != 0))
|
||||
{
|
||||
/* if this directory is ignored, remember it as the
|
||||
* "ignore_prefix" for processing contained items
|
||||
*/
|
||||
if (delta_type == GIT_DELTA_UNTRACKED &&
|
||||
git_iterator_current_is_ignored(new_iter))
|
||||
git_buf_sets(&ignore_prefix, nitem->path);
|
||||
|
||||
if (git_iterator_advance_into_directory(new_iter, &nitem) < 0)
|
||||
goto fail;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* In core git, the next two "else if" clauses are effectively
|
||||
* reversed -- i.e. when an untracked file contained in an
|
||||
* ignored directory is individually ignored, it shows up as an
|
||||
* ignored file in the diff list, even though other untracked
|
||||
* files in the same directory are skipped completely.
|
||||
*
|
||||
* To me, this is odd. If the directory is ignored and the file
|
||||
* is untracked, we should skip it consistently, regardless of
|
||||
* whether it happens to match a pattern in the ignore file.
|
||||
*
|
||||
* To match the core git behavior, just reverse the following
|
||||
* two "else if" cases so that individual file ignores are
|
||||
* checked before container directory exclusions are used to
|
||||
* skip the file.
|
||||
*/
|
||||
else if (delta_type == GIT_DELTA_IGNORED) {
|
||||
if (git_iterator_advance(new_iter, &nitem) < 0)
|
||||
goto fail;
|
||||
continue; /* ignored parent directory, so skip completely */
|
||||
}
|
||||
|
||||
else if (git_iterator_current_is_ignored(new_iter))
|
||||
delta_type = GIT_DELTA_IGNORED;
|
||||
|
||||
else if (new_iter->type != GIT_ITERATOR_WORKDIR)
|
||||
delta_type = GIT_DELTA_ADDED;
|
||||
|
||||
if (diff_delta__from_one(diff, delta_type, nitem) < 0 ||
|
||||
git_iterator_advance(new_iter, &nitem) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* otherwise item paths match, so create MODIFIED record
|
||||
* (or ADDED and DELETED pair if type changed)
|
||||
*/
|
||||
else {
|
||||
assert(oitem && nitem && strcmp(oitem->path, nitem->path) == 0);
|
||||
|
||||
if (maybe_modified(old_iter, oitem, new_iter, nitem, diff) < 0 ||
|
||||
git_iterator_advance(old_iter, &oitem) < 0 ||
|
||||
git_iterator_advance(new_iter, &nitem) < 0)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
git_iterator_free(old_iter);
|
||||
git_iterator_free(new_iter);
|
||||
git_buf_free(&ignore_prefix);
|
||||
|
||||
*diff_ptr = diff;
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
git_iterator_free(old_iter);
|
||||
git_iterator_free(new_iter);
|
||||
git_buf_free(&ignore_prefix);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
*diff_ptr = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
int git_diff_tree_to_tree(
|
||||
git_repository *repo,
|
||||
const git_diff_options *opts, /**< can be NULL for defaults */
|
||||
git_tree *old_tree,
|
||||
git_tree *new_tree,
|
||||
git_diff_list **diff)
|
||||
{
|
||||
git_iterator *a = NULL, *b = NULL;
|
||||
char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
|
||||
|
||||
assert(repo && old_tree && new_tree && diff);
|
||||
|
||||
if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
|
||||
git_iterator_for_tree_range(&b, repo, new_tree, prefix, prefix) < 0)
|
||||
return -1;
|
||||
|
||||
git__free(prefix);
|
||||
|
||||
return diff_from_iterators(repo, opts, a, b, diff);
|
||||
}
|
||||
|
||||
int git_diff_index_to_tree(
|
||||
git_repository *repo,
|
||||
const git_diff_options *opts,
|
||||
git_tree *old_tree,
|
||||
git_diff_list **diff)
|
||||
{
|
||||
git_iterator *a = NULL, *b = NULL;
|
||||
char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
|
||||
|
||||
assert(repo && diff);
|
||||
|
||||
if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
|
||||
git_iterator_for_index_range(&b, repo, prefix, prefix) < 0)
|
||||
return -1;
|
||||
|
||||
git__free(prefix);
|
||||
|
||||
return diff_from_iterators(repo, opts, a, b, diff);
|
||||
}
|
||||
|
||||
int git_diff_workdir_to_index(
|
||||
git_repository *repo,
|
||||
const git_diff_options *opts,
|
||||
git_diff_list **diff)
|
||||
{
|
||||
git_iterator *a = NULL, *b = NULL;
|
||||
char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
|
||||
|
||||
assert(repo && diff);
|
||||
|
||||
if (git_iterator_for_index_range(&a, repo, prefix, prefix) < 0 ||
|
||||
git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)
|
||||
return -1;
|
||||
|
||||
git__free(prefix);
|
||||
|
||||
return diff_from_iterators(repo, opts, a, b, diff);
|
||||
}
|
||||
|
||||
|
||||
int git_diff_workdir_to_tree(
|
||||
git_repository *repo,
|
||||
const git_diff_options *opts,
|
||||
git_tree *old_tree,
|
||||
git_diff_list **diff)
|
||||
{
|
||||
git_iterator *a = NULL, *b = NULL;
|
||||
char *prefix = opts ? diff_prefix_from_pathspec(&opts->pathspec) : NULL;
|
||||
|
||||
assert(repo && old_tree && diff);
|
||||
|
||||
if (git_iterator_for_tree_range(&a, repo, old_tree, prefix, prefix) < 0 ||
|
||||
git_iterator_for_workdir_range(&b, repo, prefix, prefix) < 0)
|
||||
return -1;
|
||||
|
||||
git__free(prefix);
|
||||
|
||||
return diff_from_iterators(repo, opts, a, b, diff);
|
||||
}
|
||||
|
||||
int git_diff_merge(
|
||||
git_diff_list *onto,
|
||||
const git_diff_list *from)
|
||||
{
|
||||
int error = 0;
|
||||
git_pool onto_pool;
|
||||
git_vector onto_new;
|
||||
git_diff_delta *delta;
|
||||
unsigned int i, j;
|
||||
|
||||
assert(onto && from);
|
||||
|
||||
if (!from->deltas.length)
|
||||
return 0;
|
||||
|
||||
if (git_vector_init(&onto_new, onto->deltas.length, diff_delta__cmp) < 0 ||
|
||||
git_pool_init(&onto_pool, 1, 0) < 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
|
||||
git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
|
||||
const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
|
||||
int cmp = !f ? -1 : !o ? 1 : strcmp(o->old_file.path, f->old_file.path);
|
||||
|
||||
if (cmp < 0) {
|
||||
delta = diff_delta__dup(o, &onto_pool);
|
||||
i++;
|
||||
} else if (cmp > 0) {
|
||||
delta = diff_delta__dup(f, &onto_pool);
|
||||
j++;
|
||||
} else {
|
||||
delta = diff_delta__merge_like_cgit(o, f, &onto_pool);
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
|
||||
if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
git_vector_swap(&onto->deltas, &onto_new);
|
||||
git_pool_swap(&onto->pool, &onto_pool);
|
||||
onto->new_src = from->new_src;
|
||||
}
|
||||
|
||||
git_vector_foreach(&onto_new, i, delta)
|
||||
git__free(delta);
|
||||
git_vector_free(&onto_new);
|
||||
git_pool_clear(&onto_pool);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
40
src/diff.h
Normal file
40
src/diff.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
#ifndef INCLUDE_diff_h__
|
||||
#define INCLUDE_diff_h__
|
||||
|
||||
#include <stdio.h>
|
||||
#include "vector.h"
|
||||
#include "buffer.h"
|
||||
#include "iterator.h"
|
||||
#include "repository.h"
|
||||
#include "pool.h"
|
||||
|
||||
#define DIFF_OLD_PREFIX_DEFAULT "a/"
|
||||
#define DIFF_NEW_PREFIX_DEFAULT "b/"
|
||||
|
||||
enum {
|
||||
GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */
|
||||
GIT_DIFFCAPS_ASSUME_UNCHANGED = (1 << 1), /* use stat? */
|
||||
GIT_DIFFCAPS_TRUST_EXEC_BIT = (1 << 2), /* use st_mode exec bit? */
|
||||
GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */
|
||||
GIT_DIFFCAPS_USE_DEV = (1 << 4), /* use st_dev? */
|
||||
};
|
||||
|
||||
struct git_diff_list {
|
||||
git_repository *repo;
|
||||
git_diff_options opts;
|
||||
git_vector pathspec;
|
||||
git_vector deltas; /* vector of git_diff_file_delta */
|
||||
git_pool pool;
|
||||
git_iterator_type_t old_src;
|
||||
git_iterator_type_t new_src;
|
||||
uint32_t diffcaps;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
786
src/diff_output.c
Normal file
786
src/diff_output.c
Normal file
@ -0,0 +1,786 @@
|
||||
/*
|
||||
* Copyright (C) 2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
#include "common.h"
|
||||
#include "git2/diff.h"
|
||||
#include "git2/attr.h"
|
||||
#include "git2/blob.h"
|
||||
#include "xdiff/xdiff.h"
|
||||
#include <ctype.h>
|
||||
#include "diff.h"
|
||||
#include "map.h"
|
||||
#include "fileops.h"
|
||||
#include "filter.h"
|
||||
|
||||
typedef struct {
|
||||
git_diff_list *diff;
|
||||
void *cb_data;
|
||||
git_diff_hunk_fn hunk_cb;
|
||||
git_diff_data_fn line_cb;
|
||||
unsigned int index;
|
||||
git_diff_delta *delta;
|
||||
git_diff_range range;
|
||||
} diff_output_info;
|
||||
|
||||
static int read_next_int(const char **str, int *value)
|
||||
{
|
||||
const char *scan = *str;
|
||||
int v = 0, digits = 0;
|
||||
/* find next digit */
|
||||
for (scan = *str; *scan && !isdigit(*scan); scan++);
|
||||
/* parse next number */
|
||||
for (; isdigit(*scan); scan++, digits++)
|
||||
v = (v * 10) + (*scan - '0');
|
||||
*str = scan;
|
||||
*value = v;
|
||||
return (digits > 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len)
|
||||
{
|
||||
diff_output_info *info = priv;
|
||||
|
||||
if (len == 1 && info->hunk_cb) {
|
||||
git_diff_range range = { -1, 0, -1, 0 };
|
||||
const char *scan = bufs[0].ptr;
|
||||
|
||||
/* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
|
||||
if (*scan != '@')
|
||||
return -1;
|
||||
|
||||
if (read_next_int(&scan, &range.old_start) < 0)
|
||||
return -1;
|
||||
if (*scan == ',' && read_next_int(&scan, &range.old_lines) < 0)
|
||||
return -1;
|
||||
|
||||
if (read_next_int(&scan, &range.new_start) < 0)
|
||||
return -1;
|
||||
if (*scan == ',' && read_next_int(&scan, &range.new_lines) < 0)
|
||||
return -1;
|
||||
|
||||
if (range.old_start < 0 || range.new_start < 0)
|
||||
return -1;
|
||||
|
||||
memcpy(&info->range, &range, sizeof(git_diff_range));
|
||||
|
||||
return info->hunk_cb(
|
||||
info->cb_data, info->delta, &range, bufs[0].ptr, bufs[0].size);
|
||||
}
|
||||
|
||||
if ((len == 2 || len == 3) && info->line_cb) {
|
||||
int origin;
|
||||
|
||||
/* expect " "/"-"/"+", then data, then maybe newline */
|
||||
origin =
|
||||
(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
|
||||
(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
|
||||
GIT_DIFF_LINE_CONTEXT;
|
||||
|
||||
if (info->line_cb(
|
||||
info->cb_data, info->delta, &info->range, origin, bufs[1].ptr, bufs[1].size) < 0)
|
||||
return -1;
|
||||
|
||||
/* deal with adding and removing newline at EOF */
|
||||
if (len == 3) {
|
||||
if (origin == GIT_DIFF_LINE_ADDITION)
|
||||
origin = GIT_DIFF_LINE_ADD_EOFNL;
|
||||
else
|
||||
origin = GIT_DIFF_LINE_DEL_EOFNL;
|
||||
|
||||
return info->line_cb(
|
||||
info->cb_data, info->delta, &info->range, origin, bufs[2].ptr, bufs[2].size);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define BINARY_DIFF_FLAGS (GIT_DIFF_FILE_BINARY|GIT_DIFF_FILE_NOT_BINARY)
|
||||
|
||||
static int update_file_is_binary_by_attr(git_repository *repo, git_diff_file *file)
|
||||
{
|
||||
const char *value;
|
||||
if (git_attr_get(&value, repo, 0, file->path, "diff") < 0)
|
||||
return -1;
|
||||
|
||||
if (GIT_ATTR_FALSE(value))
|
||||
file->flags |= GIT_DIFF_FILE_BINARY;
|
||||
else if (GIT_ATTR_TRUE(value))
|
||||
file->flags |= GIT_DIFF_FILE_NOT_BINARY;
|
||||
/* otherwise leave file->flags alone */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void update_delta_is_binary(git_diff_delta *delta)
|
||||
{
|
||||
if ((delta->old_file.flags & GIT_DIFF_FILE_BINARY) != 0 ||
|
||||
(delta->new_file.flags & GIT_DIFF_FILE_BINARY) != 0)
|
||||
delta->binary = 1;
|
||||
else if ((delta->old_file.flags & GIT_DIFF_FILE_NOT_BINARY) != 0 ||
|
||||
(delta->new_file.flags & GIT_DIFF_FILE_NOT_BINARY) != 0)
|
||||
delta->binary = 0;
|
||||
/* otherwise leave delta->binary value untouched */
|
||||
}
|
||||
|
||||
static int file_is_binary_by_attr(
|
||||
git_diff_list *diff,
|
||||
git_diff_delta *delta)
|
||||
{
|
||||
int error = 0, mirror_new;
|
||||
|
||||
delta->binary = -1;
|
||||
|
||||
/* make sure files are conceivably mmap-able */
|
||||
if ((git_off_t)((size_t)delta->old_file.size) != delta->old_file.size ||
|
||||
(git_off_t)((size_t)delta->new_file.size) != delta->new_file.size)
|
||||
{
|
||||
delta->old_file.flags |= GIT_DIFF_FILE_BINARY;
|
||||
delta->new_file.flags |= GIT_DIFF_FILE_BINARY;
|
||||
delta->binary = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check if user is forcing us to text diff these files */
|
||||
if (diff->opts.flags & GIT_DIFF_FORCE_TEXT) {
|
||||
delta->old_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
|
||||
delta->new_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
|
||||
delta->binary = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check diff attribute +, -, or 0 */
|
||||
if (update_file_is_binary_by_attr(diff->repo, &delta->old_file) < 0)
|
||||
return -1;
|
||||
|
||||
mirror_new = (delta->new_file.path == delta->old_file.path ||
|
||||
strcmp(delta->new_file.path, delta->old_file.path) == 0);
|
||||
if (mirror_new)
|
||||
delta->new_file.flags &= (delta->old_file.flags & BINARY_DIFF_FLAGS);
|
||||
else
|
||||
error = update_file_is_binary_by_attr(diff->repo, &delta->new_file);
|
||||
|
||||
update_delta_is_binary(delta);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int file_is_binary_by_content(
|
||||
git_diff_delta *delta,
|
||||
git_map *old_data,
|
||||
git_map *new_data)
|
||||
{
|
||||
git_buf search;
|
||||
|
||||
if ((delta->old_file.flags & BINARY_DIFF_FLAGS) == 0) {
|
||||
search.ptr = old_data->data;
|
||||
search.size = min(old_data->len, 4000);
|
||||
|
||||
if (git_buf_is_binary(&search))
|
||||
delta->old_file.flags |= GIT_DIFF_FILE_BINARY;
|
||||
else
|
||||
delta->old_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
|
||||
}
|
||||
|
||||
if ((delta->new_file.flags & BINARY_DIFF_FLAGS) == 0) {
|
||||
search.ptr = new_data->data;
|
||||
search.size = min(new_data->len, 4000);
|
||||
|
||||
if (git_buf_is_binary(&search))
|
||||
delta->new_file.flags |= GIT_DIFF_FILE_BINARY;
|
||||
else
|
||||
delta->new_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
|
||||
}
|
||||
|
||||
update_delta_is_binary(delta);
|
||||
|
||||
/* TODO: if value != NULL, implement diff drivers */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void setup_xdiff_options(
|
||||
git_diff_options *opts, xdemitconf_t *cfg, xpparam_t *param)
|
||||
{
|
||||
memset(cfg, 0, sizeof(xdemitconf_t));
|
||||
memset(param, 0, sizeof(xpparam_t));
|
||||
|
||||
cfg->ctxlen =
|
||||
(!opts || !opts->context_lines) ? 3 : opts->context_lines;
|
||||
cfg->interhunkctxlen =
|
||||
(!opts || !opts->interhunk_lines) ? 3 : opts->interhunk_lines;
|
||||
|
||||
if (!opts)
|
||||
return;
|
||||
|
||||
if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE)
|
||||
param->flags |= XDF_WHITESPACE_FLAGS;
|
||||
if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE)
|
||||
param->flags |= XDF_IGNORE_WHITESPACE_CHANGE;
|
||||
if (opts->flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
|
||||
param->flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
|
||||
}
|
||||
|
||||
static int get_blob_content(
|
||||
git_repository *repo,
|
||||
const git_oid *oid,
|
||||
git_map *map,
|
||||
git_blob **blob)
|
||||
{
|
||||
if (git_oid_iszero(oid))
|
||||
return 0;
|
||||
|
||||
if (git_blob_lookup(blob, repo, oid) < 0)
|
||||
return -1;
|
||||
|
||||
map->data = (void *)git_blob_rawcontent(*blob);
|
||||
map->len = git_blob_rawsize(*blob);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_workdir_content(
|
||||
git_repository *repo,
|
||||
git_diff_file *file,
|
||||
git_map *map)
|
||||
{
|
||||
int error = 0;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
|
||||
if (git_buf_joinpath(&path, git_repository_workdir(repo), file->path) < 0)
|
||||
return -1;
|
||||
|
||||
if (S_ISLNK(file->mode)) {
|
||||
ssize_t read_len;
|
||||
|
||||
file->flags |= GIT_DIFF_FILE_FREE_DATA;
|
||||
file->flags |= GIT_DIFF_FILE_BINARY;
|
||||
|
||||
map->data = git__malloc((size_t)file->size + 1);
|
||||
GITERR_CHECK_ALLOC(map->data);
|
||||
|
||||
read_len = p_readlink(path.ptr, map->data, (size_t)file->size + 1);
|
||||
if (read_len != (ssize_t)file->size) {
|
||||
giterr_set(GITERR_OS, "Failed to read symlink '%s'", file->path);
|
||||
error = -1;
|
||||
} else
|
||||
map->len = read_len;
|
||||
}
|
||||
else {
|
||||
error = git_futils_mmap_ro_file(map, path.ptr);
|
||||
file->flags |= GIT_DIFF_FILE_UNMAP_DATA;
|
||||
}
|
||||
git_buf_free(&path);
|
||||
return error;
|
||||
}
|
||||
|
||||
static void release_content(git_diff_file *file, git_map *map, git_blob *blob)
|
||||
{
|
||||
if (blob != NULL)
|
||||
git_blob_free(blob);
|
||||
|
||||
if (file->flags & GIT_DIFF_FILE_FREE_DATA) {
|
||||
git__free(map->data);
|
||||
map->data = NULL;
|
||||
file->flags &= ~GIT_DIFF_FILE_FREE_DATA;
|
||||
}
|
||||
else if (file->flags & GIT_DIFF_FILE_UNMAP_DATA) {
|
||||
git_futils_mmap_free(map);
|
||||
map->data = NULL;
|
||||
file->flags &= ~GIT_DIFF_FILE_UNMAP_DATA;
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_map_from_mmfile(git_map *dst, mmfile_t *src) {
|
||||
assert(dst && src);
|
||||
|
||||
dst->data = src->ptr;
|
||||
dst->len = src->size;
|
||||
#ifdef GIT_WIN32
|
||||
dst->fmh = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
int git_diff_foreach(
|
||||
git_diff_list *diff,
|
||||
void *data,
|
||||
git_diff_file_fn file_cb,
|
||||
git_diff_hunk_fn hunk_cb,
|
||||
git_diff_data_fn line_cb)
|
||||
{
|
||||
int error = 0;
|
||||
diff_output_info info;
|
||||
git_diff_delta *delta;
|
||||
xpparam_t xdiff_params;
|
||||
xdemitconf_t xdiff_config;
|
||||
xdemitcb_t xdiff_callback;
|
||||
|
||||
info.diff = diff;
|
||||
info.cb_data = data;
|
||||
info.hunk_cb = hunk_cb;
|
||||
info.line_cb = line_cb;
|
||||
|
||||
setup_xdiff_options(&diff->opts, &xdiff_config, &xdiff_params);
|
||||
memset(&xdiff_callback, 0, sizeof(xdiff_callback));
|
||||
xdiff_callback.outf = diff_output_cb;
|
||||
xdiff_callback.priv = &info;
|
||||
|
||||
git_vector_foreach(&diff->deltas, info.index, delta) {
|
||||
git_blob *old_blob = NULL, *new_blob = NULL;
|
||||
git_map old_data, new_data;
|
||||
mmfile_t old_xdiff_data, new_xdiff_data;
|
||||
|
||||
if (delta->status == GIT_DELTA_UNMODIFIED &&
|
||||
(diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
|
||||
continue;
|
||||
|
||||
if (delta->status == GIT_DELTA_IGNORED &&
|
||||
(diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
|
||||
continue;
|
||||
|
||||
if (delta->status == GIT_DELTA_UNTRACKED &&
|
||||
(diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
|
||||
continue;
|
||||
|
||||
if ((error = file_is_binary_by_attr(diff, delta)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
old_data.data = "";
|
||||
old_data.len = 0;
|
||||
new_data.data = "";
|
||||
new_data.len = 0;
|
||||
|
||||
/* TODO: Partial blob reading to defer loading whole blob.
|
||||
* I.e. I want a blob with just the first 4kb loaded, then
|
||||
* later on I will read the rest of the blob if needed.
|
||||
*/
|
||||
|
||||
/* map files */
|
||||
if (delta->binary != 1 &&
|
||||
(hunk_cb || line_cb) &&
|
||||
(delta->status == GIT_DELTA_DELETED ||
|
||||
delta->status == GIT_DELTA_MODIFIED))
|
||||
{
|
||||
if (diff->old_src == GIT_ITERATOR_WORKDIR)
|
||||
error = get_workdir_content(diff->repo, &delta->old_file, &old_data);
|
||||
else
|
||||
error = get_blob_content(
|
||||
diff->repo, &delta->old_file.oid, &old_data, &old_blob);
|
||||
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (delta->binary != 1 &&
|
||||
(hunk_cb || line_cb || git_oid_iszero(&delta->new_file.oid)) &&
|
||||
(delta->status == GIT_DELTA_ADDED ||
|
||||
delta->status == GIT_DELTA_MODIFIED))
|
||||
{
|
||||
if (diff->new_src == GIT_ITERATOR_WORKDIR)
|
||||
error = get_workdir_content(diff->repo, &delta->new_file, &new_data);
|
||||
else
|
||||
error = get_blob_content(
|
||||
diff->repo, &delta->new_file.oid, &new_data, &new_blob);
|
||||
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((delta->new_file.flags | GIT_DIFF_FILE_VALID_OID) == 0) {
|
||||
error = git_odb_hash(
|
||||
&delta->new_file.oid, new_data.data, new_data.len, GIT_OBJ_BLOB);
|
||||
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* since we did not have the definitive oid, we may have
|
||||
* incorrect status and need to skip this item.
|
||||
*/
|
||||
if (git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid) == 0) {
|
||||
delta->status = GIT_DELTA_UNMODIFIED;
|
||||
if ((diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* if we have not already decided whether file is binary,
|
||||
* check the first 4K for nul bytes to decide...
|
||||
*/
|
||||
if (delta->binary == -1) {
|
||||
error = file_is_binary_by_content(
|
||||
delta, &old_data, &new_data);
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* TODO: if ignore_whitespace is set, then we *must* do text
|
||||
* diffs to tell if a file has really been changed.
|
||||
*/
|
||||
|
||||
if (file_cb != NULL) {
|
||||
error = file_cb(data, delta, (float)info.index / diff->deltas.length);
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* don't do hunk and line diffs if file is binary */
|
||||
if (delta->binary == 1)
|
||||
goto cleanup;
|
||||
|
||||
/* nothing to do if we did not get data */
|
||||
if (!old_data.len && !new_data.len)
|
||||
goto cleanup;
|
||||
|
||||
assert(hunk_cb || line_cb);
|
||||
|
||||
info.delta = delta;
|
||||
old_xdiff_data.ptr = old_data.data;
|
||||
old_xdiff_data.size = old_data.len;
|
||||
new_xdiff_data.ptr = new_data.data;
|
||||
new_xdiff_data.size = new_data.len;
|
||||
|
||||
xdl_diff(&old_xdiff_data, &new_xdiff_data,
|
||||
&xdiff_params, &xdiff_config, &xdiff_callback);
|
||||
|
||||
cleanup:
|
||||
release_content(&delta->old_file, &old_data, old_blob);
|
||||
release_content(&delta->new_file, &new_data, new_blob);
|
||||
|
||||
if (error < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
typedef struct {
|
||||
git_diff_list *diff;
|
||||
git_diff_data_fn print_cb;
|
||||
void *cb_data;
|
||||
git_buf *buf;
|
||||
} diff_print_info;
|
||||
|
||||
static char pick_suffix(int mode)
|
||||
{
|
||||
if (S_ISDIR(mode))
|
||||
return '/';
|
||||
else if (mode & 0100)
|
||||
/* in git, modes are very regular, so we must have 0100755 mode */
|
||||
return '*';
|
||||
else
|
||||
return ' ';
|
||||
}
|
||||
|
||||
static int print_compact(void *data, git_diff_delta *delta, float progress)
|
||||
{
|
||||
diff_print_info *pi = data;
|
||||
char code, old_suffix, new_suffix;
|
||||
|
||||
GIT_UNUSED(progress);
|
||||
|
||||
switch (delta->status) {
|
||||
case GIT_DELTA_ADDED: code = 'A'; break;
|
||||
case GIT_DELTA_DELETED: code = 'D'; break;
|
||||
case GIT_DELTA_MODIFIED: code = 'M'; break;
|
||||
case GIT_DELTA_RENAMED: code = 'R'; break;
|
||||
case GIT_DELTA_COPIED: code = 'C'; break;
|
||||
case GIT_DELTA_IGNORED: code = 'I'; break;
|
||||
case GIT_DELTA_UNTRACKED: code = '?'; break;
|
||||
default: code = 0;
|
||||
}
|
||||
|
||||
if (!code)
|
||||
return 0;
|
||||
|
||||
old_suffix = pick_suffix(delta->old_file.mode);
|
||||
new_suffix = pick_suffix(delta->new_file.mode);
|
||||
|
||||
git_buf_clear(pi->buf);
|
||||
|
||||
if (delta->old_file.path != delta->new_file.path &&
|
||||
strcmp(delta->old_file.path,delta->new_file.path) != 0)
|
||||
git_buf_printf(pi->buf, "%c\t%s%c -> %s%c\n", code,
|
||||
delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
|
||||
else if (delta->old_file.mode != delta->new_file.mode &&
|
||||
delta->old_file.mode != 0 && delta->new_file.mode != 0)
|
||||
git_buf_printf(pi->buf, "%c\t%s%c (%o -> %o)\n", code,
|
||||
delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode);
|
||||
else if (old_suffix != ' ')
|
||||
git_buf_printf(pi->buf, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
|
||||
else
|
||||
git_buf_printf(pi->buf, "%c\t%s\n", code, delta->old_file.path);
|
||||
|
||||
if (git_buf_oom(pi->buf))
|
||||
return -1;
|
||||
|
||||
return pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
|
||||
}
|
||||
|
||||
int git_diff_print_compact(
|
||||
git_diff_list *diff,
|
||||
void *cb_data,
|
||||
git_diff_data_fn print_cb)
|
||||
{
|
||||
int error;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
diff_print_info pi;
|
||||
|
||||
pi.diff = diff;
|
||||
pi.print_cb = print_cb;
|
||||
pi.cb_data = cb_data;
|
||||
pi.buf = &buf;
|
||||
|
||||
error = git_diff_foreach(diff, &pi, print_compact, NULL, NULL);
|
||||
|
||||
git_buf_free(&buf);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
static int print_oid_range(diff_print_info *pi, git_diff_delta *delta)
|
||||
{
|
||||
char start_oid[8], end_oid[8];
|
||||
|
||||
/* TODO: Determine a good actual OID range to print */
|
||||
git_oid_tostr(start_oid, sizeof(start_oid), &delta->old_file.oid);
|
||||
git_oid_tostr(end_oid, sizeof(end_oid), &delta->new_file.oid);
|
||||
|
||||
/* TODO: Match git diff more closely */
|
||||
if (delta->old_file.mode == delta->new_file.mode) {
|
||||
git_buf_printf(pi->buf, "index %s..%s %o\n",
|
||||
start_oid, end_oid, delta->old_file.mode);
|
||||
} else {
|
||||
if (delta->old_file.mode == 0) {
|
||||
git_buf_printf(pi->buf, "new file mode %o\n", delta->new_file.mode);
|
||||
} else if (delta->new_file.mode == 0) {
|
||||
git_buf_printf(pi->buf, "deleted file mode %o\n", delta->old_file.mode);
|
||||
} else {
|
||||
git_buf_printf(pi->buf, "old mode %o\n", delta->old_file.mode);
|
||||
git_buf_printf(pi->buf, "new mode %o\n", delta->new_file.mode);
|
||||
}
|
||||
git_buf_printf(pi->buf, "index %s..%s\n", start_oid, end_oid);
|
||||
}
|
||||
|
||||
if (git_buf_oom(pi->buf))
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int print_patch_file(void *data, git_diff_delta *delta, float progress)
|
||||
{
|
||||
diff_print_info *pi = data;
|
||||
const char *oldpfx = pi->diff->opts.old_prefix;
|
||||
const char *oldpath = delta->old_file.path;
|
||||
const char *newpfx = pi->diff->opts.new_prefix;
|
||||
const char *newpath = delta->new_file.path;
|
||||
int result;
|
||||
|
||||
GIT_UNUSED(progress);
|
||||
|
||||
if (!oldpfx)
|
||||
oldpfx = DIFF_OLD_PREFIX_DEFAULT;
|
||||
|
||||
if (!newpfx)
|
||||
newpfx = DIFF_NEW_PREFIX_DEFAULT;
|
||||
|
||||
git_buf_clear(pi->buf);
|
||||
git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
|
||||
|
||||
if (print_oid_range(pi, delta) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_oid_iszero(&delta->old_file.oid)) {
|
||||
oldpfx = "";
|
||||
oldpath = "/dev/null";
|
||||
}
|
||||
if (git_oid_iszero(&delta->new_file.oid)) {
|
||||
newpfx = "";
|
||||
newpath = "/dev/null";
|
||||
}
|
||||
|
||||
if (delta->binary != 1) {
|
||||
git_buf_printf(pi->buf, "--- %s%s\n", oldpfx, oldpath);
|
||||
git_buf_printf(pi->buf, "+++ %s%s\n", newpfx, newpath);
|
||||
}
|
||||
|
||||
if (git_buf_oom(pi->buf))
|
||||
return -1;
|
||||
|
||||
result = pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
if (delta->binary != 1)
|
||||
return 0;
|
||||
|
||||
git_buf_clear(pi->buf);
|
||||
git_buf_printf(
|
||||
pi->buf, "Binary files %s%s and %s%s differ\n",
|
||||
oldpfx, oldpath, newpfx, newpath);
|
||||
if (git_buf_oom(pi->buf))
|
||||
return -1;
|
||||
|
||||
return pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_BINARY, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
|
||||
}
|
||||
|
||||
static int print_patch_hunk(
|
||||
void *data,
|
||||
git_diff_delta *d,
|
||||
git_diff_range *r,
|
||||
const char *header,
|
||||
size_t header_len)
|
||||
{
|
||||
diff_print_info *pi = data;
|
||||
|
||||
git_buf_clear(pi->buf);
|
||||
if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0)
|
||||
return -1;
|
||||
|
||||
return pi->print_cb(pi->cb_data, d, r, GIT_DIFF_LINE_HUNK_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
|
||||
}
|
||||
|
||||
static int print_patch_line(
|
||||
void *data,
|
||||
git_diff_delta *delta,
|
||||
git_diff_range *range,
|
||||
char line_origin, /* GIT_DIFF_LINE value from above */
|
||||
const char *content,
|
||||
size_t content_len)
|
||||
{
|
||||
diff_print_info *pi = data;
|
||||
|
||||
git_buf_clear(pi->buf);
|
||||
|
||||
if (line_origin == GIT_DIFF_LINE_ADDITION ||
|
||||
line_origin == GIT_DIFF_LINE_DELETION ||
|
||||
line_origin == GIT_DIFF_LINE_CONTEXT)
|
||||
git_buf_printf(pi->buf, "%c%.*s", line_origin, (int)content_len, content);
|
||||
else if (content_len > 0)
|
||||
git_buf_printf(pi->buf, "%.*s", (int)content_len, content);
|
||||
|
||||
if (git_buf_oom(pi->buf))
|
||||
return -1;
|
||||
|
||||
return pi->print_cb(pi->cb_data, delta, range, line_origin, git_buf_cstr(pi->buf), git_buf_len(pi->buf));
|
||||
}
|
||||
|
||||
int git_diff_print_patch(
|
||||
git_diff_list *diff,
|
||||
void *cb_data,
|
||||
git_diff_data_fn print_cb)
|
||||
{
|
||||
int error;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
diff_print_info pi;
|
||||
|
||||
pi.diff = diff;
|
||||
pi.print_cb = print_cb;
|
||||
pi.cb_data = cb_data;
|
||||
pi.buf = &buf;
|
||||
|
||||
error = git_diff_foreach(
|
||||
diff, &pi, print_patch_file, print_patch_hunk, print_patch_line);
|
||||
|
||||
git_buf_free(&buf);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_diff_blobs(
|
||||
git_blob *old_blob,
|
||||
git_blob *new_blob,
|
||||
git_diff_options *options,
|
||||
void *cb_data,
|
||||
git_diff_file_fn file_cb,
|
||||
git_diff_hunk_fn hunk_cb,
|
||||
git_diff_data_fn line_cb)
|
||||
{
|
||||
diff_output_info info;
|
||||
git_diff_delta delta;
|
||||
mmfile_t old_data, new_data;
|
||||
git_map old_map, new_map;
|
||||
xpparam_t xdiff_params;
|
||||
xdemitconf_t xdiff_config;
|
||||
xdemitcb_t xdiff_callback;
|
||||
git_blob *new, *old;
|
||||
|
||||
memset(&delta, 0, sizeof(delta));
|
||||
|
||||
new = new_blob;
|
||||
old = old_blob;
|
||||
|
||||
if (options && (options->flags & GIT_DIFF_REVERSE)) {
|
||||
git_blob *swap = old;
|
||||
old = new;
|
||||
new = swap;
|
||||
}
|
||||
|
||||
if (old) {
|
||||
old_data.ptr = (char *)git_blob_rawcontent(old);
|
||||
old_data.size = git_blob_rawsize(old);
|
||||
git_oid_cpy(&delta.old_file.oid, git_object_id((const git_object *)old));
|
||||
} else {
|
||||
old_data.ptr = "";
|
||||
old_data.size = 0;
|
||||
}
|
||||
|
||||
if (new) {
|
||||
new_data.ptr = (char *)git_blob_rawcontent(new);
|
||||
new_data.size = git_blob_rawsize(new);
|
||||
git_oid_cpy(&delta.new_file.oid, git_object_id((const git_object *)new));
|
||||
} else {
|
||||
new_data.ptr = "";
|
||||
new_data.size = 0;
|
||||
}
|
||||
|
||||
/* populate a "fake" delta record */
|
||||
delta.status = new ?
|
||||
(old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) :
|
||||
(old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED);
|
||||
|
||||
if (git_oid_cmp(&delta.new_file.oid, &delta.old_file.oid) == 0)
|
||||
delta.status = GIT_DELTA_UNMODIFIED;
|
||||
|
||||
delta.old_file.size = old_data.size;
|
||||
delta.new_file.size = new_data.size;
|
||||
|
||||
fill_map_from_mmfile(&old_map, &old_data);
|
||||
fill_map_from_mmfile(&new_map, &new_data);
|
||||
|
||||
if (file_is_binary_by_content(&delta, &old_map, &new_map) < 0)
|
||||
return -1;
|
||||
|
||||
if (file_cb != NULL) {
|
||||
int error = file_cb(cb_data, &delta, 1);
|
||||
if (error < 0)
|
||||
return error;
|
||||
}
|
||||
|
||||
/* don't do hunk and line diffs if the two blobs are identical */
|
||||
if (delta.status == GIT_DELTA_UNMODIFIED)
|
||||
return 0;
|
||||
|
||||
/* don't do hunk and line diffs if file is binary */
|
||||
if (delta.binary == 1)
|
||||
return 0;
|
||||
|
||||
info.diff = NULL;
|
||||
info.delta = δ
|
||||
info.cb_data = cb_data;
|
||||
info.hunk_cb = hunk_cb;
|
||||
info.line_cb = line_cb;
|
||||
|
||||
setup_xdiff_options(options, &xdiff_config, &xdiff_params);
|
||||
memset(&xdiff_callback, 0, sizeof(xdiff_callback));
|
||||
xdiff_callback.outf = diff_output_cb;
|
||||
xdiff_callback.priv = &info;
|
||||
|
||||
xdl_diff(&old_data, &new_data, &xdiff_params, &xdiff_config, &xdiff_callback);
|
||||
|
||||
return 0;
|
||||
}
|
165
src/errors.c
165
src/errors.c
@ -1,104 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
#include "common.h"
|
||||
#include "global.h"
|
||||
#include "posix.h"
|
||||
#include "buffer.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
static struct {
|
||||
int num;
|
||||
const char *str;
|
||||
} error_codes[] = {
|
||||
{GIT_ERROR, "Unspecified error"},
|
||||
{GIT_ENOTOID, "Input was not a properly formatted Git object id."},
|
||||
{GIT_ENOTFOUND, "Object does not exist in the scope searched."},
|
||||
{GIT_ENOMEM, "Not enough space available."},
|
||||
{GIT_EOSERR, "Consult the OS error information."},
|
||||
{GIT_EOBJTYPE, "The specified object is of invalid type"},
|
||||
{GIT_EOBJCORRUPTED, "The specified object has its data corrupted"},
|
||||
{GIT_ENOTAREPO, "The specified repository is invalid"},
|
||||
{GIT_EINVALIDTYPE, "The object or config variable type is invalid or doesn't match"},
|
||||
{GIT_EMISSINGOBJDATA, "The object cannot be written that because it's missing internal data"},
|
||||
{GIT_EPACKCORRUPTED, "The packfile for the ODB is corrupted"},
|
||||
{GIT_EFLOCKFAIL, "Failed to adquire or release a file lock"},
|
||||
{GIT_EZLIB, "The Z library failed to inflate/deflate an object's data"},
|
||||
{GIT_EBUSY, "The queried object is currently busy"},
|
||||
{GIT_EINVALIDPATH, "The path is invalid"},
|
||||
{GIT_EBAREINDEX, "The index file is not backed up by an existing repository"},
|
||||
{GIT_EINVALIDREFNAME, "The name of the reference is not valid"},
|
||||
{GIT_EREFCORRUPTED, "The specified reference has its data corrupted"},
|
||||
{GIT_ETOONESTEDSYMREF, "The specified symbolic reference is too deeply nested"},
|
||||
{GIT_EPACKEDREFSCORRUPTED, "The pack-refs file is either corrupted of its format is not currently supported"},
|
||||
{GIT_EINVALIDPATH, "The path is invalid" },
|
||||
{GIT_EREVWALKOVER, "The revision walker is empty; there are no more commits left to iterate"},
|
||||
{GIT_EINVALIDREFSTATE, "The state of the reference is not valid"},
|
||||
{GIT_ENOTIMPLEMENTED, "This feature has not been implemented yet"},
|
||||
{GIT_EEXISTS, "A reference with this name already exists"},
|
||||
{GIT_EOVERFLOW, "The given integer literal is too large to be parsed"},
|
||||
{GIT_ENOTNUM, "The given literal is not a valid number"},
|
||||
{GIT_EAMBIGUOUSOIDPREFIX, "The given oid prefix is ambiguous"},
|
||||
/********************************************
|
||||
* New error handling
|
||||
********************************************/
|
||||
|
||||
static git_error g_git_oom_error = {
|
||||
"Out of memory",
|
||||
GITERR_NOMEMORY
|
||||
};
|
||||
|
||||
const char *git_strerror(int num)
|
||||
static void set_error(int error_class, char *string)
|
||||
{
|
||||
size_t i;
|
||||
git_error *error = &GIT_GLOBAL->error_t;
|
||||
|
||||
if (num == GIT_EOSERR)
|
||||
return strerror(errno);
|
||||
for (i = 0; i < ARRAY_SIZE(error_codes); i++)
|
||||
if (num == error_codes[i].num)
|
||||
return error_codes[i].str;
|
||||
git__free(error->message);
|
||||
|
||||
return "Unknown error";
|
||||
error->message = string;
|
||||
error->klass = error_class;
|
||||
|
||||
GIT_GLOBAL->last_error = error;
|
||||
}
|
||||
|
||||
#define ERROR_MAX_LEN 1024
|
||||
|
||||
void git___rethrow(const char *msg, ...)
|
||||
void giterr_set_oom(void)
|
||||
{
|
||||
char new_error[ERROR_MAX_LEN];
|
||||
char *last_error;
|
||||
char *old_error = NULL;
|
||||
|
||||
va_list va;
|
||||
|
||||
last_error = GIT_GLOBAL->error.last;
|
||||
|
||||
va_start(va, msg);
|
||||
vsnprintf(new_error, ERROR_MAX_LEN, msg, va);
|
||||
va_end(va);
|
||||
|
||||
old_error = git__strdup(last_error);
|
||||
|
||||
snprintf(last_error, ERROR_MAX_LEN, "%s \n - %s", new_error, old_error);
|
||||
|
||||
git__free(old_error);
|
||||
GIT_GLOBAL->last_error = &g_git_oom_error;
|
||||
}
|
||||
|
||||
void git___throw(const char *msg, ...)
|
||||
void giterr_set(int error_class, const char *string, ...)
|
||||
{
|
||||
va_list va;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
va_list arglist;
|
||||
|
||||
va_start(va, msg);
|
||||
vsnprintf(GIT_GLOBAL->error.last, ERROR_MAX_LEN, msg, va);
|
||||
va_end(va);
|
||||
int unix_error_code = 0;
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
DWORD win32_error_code = 0;
|
||||
#endif
|
||||
|
||||
if (error_class == GITERR_OS) {
|
||||
unix_error_code = errno;
|
||||
errno = 0;
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
win32_error_code = GetLastError();
|
||||
SetLastError(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
va_start(arglist, string);
|
||||
git_buf_vprintf(&buf, string, arglist);
|
||||
va_end(arglist);
|
||||
|
||||
/* automatically suffix strerror(errno) for GITERR_OS errors */
|
||||
if (error_class == GITERR_OS) {
|
||||
|
||||
if (unix_error_code != 0) {
|
||||
git_buf_PUTS(&buf, ": ");
|
||||
git_buf_puts(&buf, strerror(unix_error_code));
|
||||
}
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
else if (win32_error_code != 0) {
|
||||
LPVOID lpMsgBuf = NULL;
|
||||
|
||||
FormatMessage(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
NULL, win32_error_code, 0, (LPTSTR) &lpMsgBuf, 0, NULL);
|
||||
|
||||
if (lpMsgBuf) {
|
||||
git_buf_PUTS(&buf, ": ");
|
||||
git_buf_puts(&buf, lpMsgBuf);
|
||||
LocalFree(lpMsgBuf);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!git_buf_oom(&buf))
|
||||
set_error(error_class, git_buf_detach(&buf));
|
||||
}
|
||||
|
||||
const char *git_lasterror(void)
|
||||
void giterr_set_str(int error_class, const char *string)
|
||||
{
|
||||
char *last_error = GIT_GLOBAL->error.last;
|
||||
char *message = git__strdup(string);
|
||||
|
||||
if (!last_error[0])
|
||||
return NULL;
|
||||
|
||||
return last_error;
|
||||
if (message)
|
||||
set_error(error_class, message);
|
||||
}
|
||||
|
||||
void git_clearerror(void)
|
||||
void giterr_set_regex(const regex_t *regex, int error_code)
|
||||
{
|
||||
char *last_error = GIT_GLOBAL->error.last;
|
||||
last_error[0] = '\0';
|
||||
char error_buf[1024];
|
||||
regerror(error_code, regex, error_buf, sizeof(error_buf));
|
||||
giterr_set_str(GITERR_REGEX, error_buf);
|
||||
}
|
||||
|
||||
void giterr_clear(void)
|
||||
{
|
||||
GIT_GLOBAL->last_error = NULL;
|
||||
}
|
||||
|
||||
const git_error *giterr_last(void)
|
||||
{
|
||||
return GIT_GLOBAL->last_error;
|
||||
}
|
||||
|
||||
|
154
src/fetch.c
154
src/fetch.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -9,6 +9,7 @@
|
||||
#include "git2/oid.h"
|
||||
#include "git2/refs.h"
|
||||
#include "git2/revwalk.h"
|
||||
#include "git2/indexer.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "transport.h"
|
||||
@ -28,19 +29,17 @@ struct filter_payload {
|
||||
static int filter_ref__cb(git_remote_head *head, void *payload)
|
||||
{
|
||||
struct filter_payload *p = payload;
|
||||
int error;
|
||||
|
||||
if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0) {
|
||||
p->found_head = 1;
|
||||
} else {
|
||||
/* If it doesn't match the refpec, we don't want it */
|
||||
error = git_refspec_src_match(p->spec, head->name);
|
||||
if (!git_refspec_src_matches(p->spec, head->name))
|
||||
return 0;
|
||||
|
||||
if (error == GIT_ENOMATCH)
|
||||
return GIT_SUCCESS;
|
||||
|
||||
if (error < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Error matching remote ref name");
|
||||
/* Don't even try to ask for the annotation target */
|
||||
if (!git__suffixcmp(head->name, "^{}"))
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If we have the object, mark it so we don't ask for it */
|
||||
@ -54,7 +53,6 @@ static int filter_ref__cb(git_remote_head *head, void *payload)
|
||||
|
||||
static int filter_wants(git_remote *remote)
|
||||
{
|
||||
int error;
|
||||
struct filter_payload p;
|
||||
|
||||
git_vector_clear(&remote->refs);
|
||||
@ -69,9 +67,8 @@ static int filter_wants(git_remote *remote)
|
||||
p.found_head = 0;
|
||||
p.remote = remote;
|
||||
|
||||
error = git_repository_odb__weakptr(&p.odb, remote->repo);
|
||||
if (error < GIT_SUCCESS)
|
||||
return error;
|
||||
if (git_repository_odb__weakptr(&p.odb, remote->repo) < 0)
|
||||
return -1;
|
||||
|
||||
return remote->transport->ls(remote->transport, &filter_ref__cb, &p);
|
||||
}
|
||||
@ -83,19 +80,16 @@ static int filter_wants(git_remote *remote)
|
||||
*/
|
||||
int git_fetch_negotiate(git_remote *remote)
|
||||
{
|
||||
int error;
|
||||
git_transport *t = remote->transport;
|
||||
|
||||
error = filter_wants(remote);
|
||||
if (error < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Failed to filter the reference list for wants");
|
||||
if (filter_wants(remote) < 0) {
|
||||
giterr_set(GITERR_NET, "Failed to filter the reference list for wants");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Don't try to negotiate when we don't want anything */
|
||||
if (remote->refs.length == 0)
|
||||
return GIT_SUCCESS;
|
||||
|
||||
if (!remote->need_pack)
|
||||
return GIT_SUCCESS;
|
||||
if (remote->refs.length == 0 || !remote->need_pack)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Now we have everything set up so we can start tell the server
|
||||
@ -104,75 +98,103 @@ int git_fetch_negotiate(git_remote *remote)
|
||||
return t->negotiate_fetch(t, remote->repo, &remote->refs);
|
||||
}
|
||||
|
||||
int git_fetch_download_pack(char **out, git_remote *remote)
|
||||
int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats)
|
||||
{
|
||||
if(!remote->need_pack) {
|
||||
*out = NULL;
|
||||
return GIT_SUCCESS;
|
||||
}
|
||||
if(!remote->need_pack)
|
||||
return 0;
|
||||
|
||||
return remote->transport->download_pack(out, remote->transport, remote->repo);
|
||||
return remote->transport->download_pack(remote->transport, remote->repo, bytes, stats);
|
||||
}
|
||||
|
||||
/* Receiving data from a socket and storing it is pretty much the same for git and HTTP */
|
||||
int git_fetch__download_pack(
|
||||
char **out,
|
||||
const char *buffered,
|
||||
size_t buffered_size,
|
||||
GIT_SOCKET fd,
|
||||
git_repository *repo)
|
||||
git_repository *repo,
|
||||
git_off_t *bytes,
|
||||
git_indexer_stats *stats)
|
||||
{
|
||||
git_filebuf file = GIT_FILEBUF_INIT;
|
||||
int error;
|
||||
int recvd;
|
||||
char buff[1024];
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
static const char suff[] = "/objects/pack/pack-received";
|
||||
gitno_buffer buf;
|
||||
git_indexer_stream *idx;
|
||||
|
||||
gitno_buffer_setup(&buf, buff, sizeof(buff), fd);
|
||||
|
||||
if (memcmp(buffered, "PACK", strlen("PACK"))) {
|
||||
return git__throw(GIT_ERROR, "The pack doesn't start with the signature");
|
||||
giterr_set(GITERR_NET, "The pack doesn't start with the signature");
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = git_buf_joinpath(&path, repo->path_repository, suff);
|
||||
if (error < GIT_SUCCESS)
|
||||
goto cleanup;
|
||||
if (git_indexer_stream_new(&idx, git_repository_path(repo)) < 0)
|
||||
return -1;
|
||||
|
||||
error = git_filebuf_open(&file, path.ptr, GIT_FILEBUF_TEMPORARY);
|
||||
if (error < GIT_SUCCESS)
|
||||
goto cleanup;
|
||||
memset(stats, 0, sizeof(git_indexer_stats));
|
||||
if (git_indexer_stream_add(idx, buffered, buffered_size, stats) < 0)
|
||||
goto on_error;
|
||||
|
||||
/* Part of the packfile has been received, don't loose it */
|
||||
error = git_filebuf_write(&file, buffered, buffered_size);
|
||||
if (error < GIT_SUCCESS)
|
||||
goto cleanup;
|
||||
*bytes = buffered_size;
|
||||
|
||||
while (1) {
|
||||
error = git_filebuf_write(&file, buf.data, buf.offset);
|
||||
if (error < GIT_SUCCESS)
|
||||
goto cleanup;
|
||||
do {
|
||||
if (git_indexer_stream_add(idx, buf.data, buf.offset, stats) < 0)
|
||||
goto on_error;
|
||||
|
||||
gitno_consume_n(&buf, buf.offset);
|
||||
error = gitno_recv(&buf);
|
||||
if (error < GIT_SUCCESS)
|
||||
goto cleanup;
|
||||
if (error == 0) /* Orderly shutdown */
|
||||
break;
|
||||
}
|
||||
if ((recvd = gitno_recv(&buf)) < 0)
|
||||
goto on_error;
|
||||
|
||||
*out = git__strdup(file.path_lock);
|
||||
if (*out == NULL) {
|
||||
error = GIT_ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
*bytes += recvd;
|
||||
} while(recvd > 0);
|
||||
|
||||
/* A bit dodgy, but we need to keep the pack at the temporary path */
|
||||
error = git_filebuf_commit_at(&file, file.path_lock, GIT_PACK_FILE_MODE);
|
||||
cleanup:
|
||||
if (error < GIT_SUCCESS)
|
||||
git_filebuf_cleanup(&file);
|
||||
git_buf_free(&path);
|
||||
if (git_indexer_stream_finalize(idx, stats))
|
||||
goto on_error;
|
||||
|
||||
return error;
|
||||
git_indexer_stream_free(idx);
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
git_indexer_stream_free(idx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_fetch_setup_walk(git_revwalk **out, git_repository *repo)
|
||||
{
|
||||
git_revwalk *walk;
|
||||
git_strarray refs;
|
||||
unsigned int i;
|
||||
git_reference *ref;
|
||||
|
||||
if (git_reference_list(&refs, repo, GIT_REF_LISTALL) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_revwalk_new(&walk, repo) < 0)
|
||||
return -1;
|
||||
|
||||
git_revwalk_sorting(walk, GIT_SORT_TIME);
|
||||
|
||||
for (i = 0; i < refs.count; ++i) {
|
||||
/* No tags */
|
||||
if (!git__prefixcmp(refs.strings[i], GIT_REFS_TAGS_DIR))
|
||||
continue;
|
||||
|
||||
if (git_reference_lookup(&ref, repo, refs.strings[i]) < 0)
|
||||
goto on_error;
|
||||
|
||||
if (git_reference_type(ref) == GIT_REF_SYMBOLIC)
|
||||
continue;
|
||||
if (git_revwalk_push(walk, git_reference_oid(ref)) < 0)
|
||||
goto on_error;
|
||||
|
||||
git_reference_free(ref);
|
||||
}
|
||||
|
||||
git_strarray_free(&refs);
|
||||
*out = walk;
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
git_reference_free(ref);
|
||||
git_strarray_free(&refs);
|
||||
return -1;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -10,9 +10,10 @@
|
||||
#include "netops.h"
|
||||
|
||||
int git_fetch_negotiate(git_remote *remote);
|
||||
int git_fetch_download_pack(char **out, git_remote *remote);
|
||||
int git_fetch_download_pack(git_remote *remote, git_off_t *bytes, git_indexer_stats *stats);
|
||||
|
||||
int git_fetch__download_pack(char **out, const char *buffered, size_t buffered_size,
|
||||
GIT_SOCKET fd, git_repository *repo);
|
||||
int git_fetch__download_pack(const char *buffered, size_t buffered_size, GIT_SOCKET fd,
|
||||
git_repository *repo, git_off_t *bytes, git_indexer_stats *stats);
|
||||
int git_fetch_setup_walk(git_revwalk **out, git_repository *repo);
|
||||
|
||||
#endif
|
||||
|
245
src/filebuf.c
245
src/filebuf.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -14,13 +14,46 @@
|
||||
|
||||
static const size_t WRITE_BUFFER_SIZE = (4096 * 2);
|
||||
|
||||
enum buferr_t {
|
||||
BUFERR_OK = 0,
|
||||
BUFERR_WRITE,
|
||||
BUFERR_ZLIB,
|
||||
BUFERR_MEM
|
||||
};
|
||||
|
||||
#define ENSURE_BUF_OK(buf) if ((buf)->last_error != BUFERR_OK) { return -1; }
|
||||
|
||||
static int verify_last_error(git_filebuf *file)
|
||||
{
|
||||
switch (file->last_error) {
|
||||
case BUFERR_WRITE:
|
||||
giterr_set(GITERR_OS, "Failed to write out file");
|
||||
return -1;
|
||||
|
||||
case BUFERR_MEM:
|
||||
giterr_set_oom();
|
||||
return -1;
|
||||
|
||||
case BUFERR_ZLIB:
|
||||
giterr_set(GITERR_ZLIB,
|
||||
"Buffer error when writing out ZLib data");
|
||||
return -1;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int lock_file(git_filebuf *file, int flags)
|
||||
{
|
||||
if (git_path_exists(file->path_lock) == 0) {
|
||||
if (git_path_exists(file->path_lock) == true) {
|
||||
if (flags & GIT_FILEBUF_FORCE)
|
||||
p_unlink(file->path_lock);
|
||||
else
|
||||
return git__throw(GIT_EOSERR, "Failed to lock file");
|
||||
else {
|
||||
giterr_set(GITERR_OS,
|
||||
"Failed to lock file '%s' for writing", file->path_lock);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* create path to the file buffer is required */
|
||||
@ -32,16 +65,22 @@ static int lock_file(git_filebuf *file, int flags)
|
||||
}
|
||||
|
||||
if (file->fd < 0)
|
||||
return git__throw(GIT_EOSERR, "Failed to create lock");
|
||||
return -1;
|
||||
|
||||
if ((flags & GIT_FILEBUF_APPEND) && git_path_exists(file->path_original) == 0) {
|
||||
file->fd_is_open = true;
|
||||
|
||||
if ((flags & GIT_FILEBUF_APPEND) && git_path_exists(file->path_original) == true) {
|
||||
git_file source;
|
||||
char buffer[2048];
|
||||
size_t read_bytes;
|
||||
|
||||
source = p_open(file->path_original, O_RDONLY);
|
||||
if (source < 0)
|
||||
return git__throw(GIT_EOSERR, "Failed to lock file. Could not open %s", file->path_original);
|
||||
if (source < 0) {
|
||||
giterr_set(GITERR_OS,
|
||||
"Failed to open file '%s' for reading",
|
||||
file->path_original);
|
||||
return -1;
|
||||
}
|
||||
|
||||
while ((read_bytes = p_read(source, buffer, 2048)) > 0) {
|
||||
p_write(file->fd, buffer, read_bytes);
|
||||
@ -52,15 +91,15 @@ static int lock_file(git_filebuf *file, int flags)
|
||||
p_close(source);
|
||||
}
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void git_filebuf_cleanup(git_filebuf *file)
|
||||
{
|
||||
if (file->fd >= 0)
|
||||
if (file->fd_is_open && file->fd >= 0)
|
||||
p_close(file->fd);
|
||||
|
||||
if (file->fd >= 0 && file->path_lock && git_path_exists(file->path_lock) == GIT_SUCCESS)
|
||||
if (file->fd_is_open && file->path_lock && git_path_exists(file->path_lock))
|
||||
p_unlink(file->path_lock);
|
||||
|
||||
if (file->digest)
|
||||
@ -93,20 +132,21 @@ GIT_INLINE(int) flush_buffer(git_filebuf *file)
|
||||
|
||||
static int write_normal(git_filebuf *file, void *source, size_t len)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (len > 0) {
|
||||
result = p_write(file->fd, (void *)source, len);
|
||||
if (p_write(file->fd, (void *)source, len) < 0) {
|
||||
file->last_error = BUFERR_WRITE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (file->digest)
|
||||
git_hash_update(file->digest, source, len);
|
||||
}
|
||||
|
||||
return result;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_deflate(git_filebuf *file, void *source, size_t len)
|
||||
{
|
||||
int result = Z_OK;
|
||||
z_stream *zs = &file->zs;
|
||||
|
||||
if (len > 0 || file->flush_mode == Z_FINISH) {
|
||||
@ -119,14 +159,17 @@ static int write_deflate(git_filebuf *file, void *source, size_t len)
|
||||
zs->next_out = file->z_buf;
|
||||
zs->avail_out = (uInt)file->buf_size;
|
||||
|
||||
result = deflate(zs, file->flush_mode);
|
||||
if (result == Z_STREAM_ERROR)
|
||||
return git__throw(GIT_ERROR, "Failed to deflate input");
|
||||
if (deflate(zs, file->flush_mode) == Z_STREAM_ERROR) {
|
||||
file->last_error = BUFERR_ZLIB;
|
||||
return -1;
|
||||
}
|
||||
|
||||
have = file->buf_size - (size_t)zs->avail_out;
|
||||
|
||||
if (p_write(file->fd, file->z_buf, have) < GIT_SUCCESS)
|
||||
return git__throw(GIT_EOSERR, "Failed to write to file");
|
||||
if (p_write(file->fd, file->z_buf, have) < 0) {
|
||||
file->last_error = BUFERR_WRITE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
} while (zs->avail_out == 0);
|
||||
|
||||
@ -136,38 +179,39 @@ static int write_deflate(git_filebuf *file, void *source, size_t len)
|
||||
git_hash_update(file->digest, source, len);
|
||||
}
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_filebuf_open(git_filebuf *file, const char *path, int flags)
|
||||
{
|
||||
int error, compression;
|
||||
int compression;
|
||||
size_t path_len;
|
||||
|
||||
assert(file && path);
|
||||
|
||||
if (file->buffer)
|
||||
return git__throw(GIT_EINVALIDARGS, "Tried to reopen an open filebuf");
|
||||
/* opening an already open buffer is a programming error;
|
||||
* assert that this never happens instead of returning
|
||||
* an error code */
|
||||
assert(file && path && file->buffer == NULL);
|
||||
|
||||
memset(file, 0x0, sizeof(git_filebuf));
|
||||
|
||||
if (flags & GIT_FILEBUF_DO_NOT_BUFFER)
|
||||
file->do_not_buffer = true;
|
||||
|
||||
file->buf_size = WRITE_BUFFER_SIZE;
|
||||
file->buf_pos = 0;
|
||||
file->fd = -1;
|
||||
file->last_error = BUFERR_OK;
|
||||
|
||||
/* Allocate the main cache buffer */
|
||||
file->buffer = git__malloc(file->buf_size);
|
||||
if (file->buffer == NULL){
|
||||
error = GIT_ENOMEM;
|
||||
goto cleanup;
|
||||
if (!file->do_not_buffer) {
|
||||
file->buffer = git__malloc(file->buf_size);
|
||||
GITERR_CHECK_ALLOC(file->buffer);
|
||||
}
|
||||
|
||||
/* If we are hashing on-write, allocate a new hash context */
|
||||
if (flags & GIT_FILEBUF_HASH_CONTENTS) {
|
||||
if ((file->digest = git_hash_new_ctx()) == NULL) {
|
||||
error = GIT_ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
file->digest = git_hash_new_ctx();
|
||||
GITERR_CHECK_ALLOC(file->digest);
|
||||
}
|
||||
|
||||
compression = flags >> GIT_FILEBUF_DEFLATE_SHIFT;
|
||||
@ -176,16 +220,13 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
|
||||
if (compression != 0) {
|
||||
/* Initialize the ZLib stream */
|
||||
if (deflateInit(&file->zs, compression) != Z_OK) {
|
||||
error = git__throw(GIT_EZLIB, "Failed to initialize zlib");
|
||||
giterr_set(GITERR_ZLIB, "Failed to initialize zlib");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* Allocate the Zlib cache buffer */
|
||||
file->z_buf = git__malloc(file->buf_size);
|
||||
if (file->z_buf == NULL){
|
||||
error = GIT_ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
GITERR_CHECK_ALLOC(file->z_buf);
|
||||
|
||||
/* Never flush */
|
||||
file->flush_mode = Z_NO_FLUSH;
|
||||
@ -200,104 +241,101 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags)
|
||||
|
||||
/* Open the file as temporary for locking */
|
||||
file->fd = git_futils_mktmp(&tmp_path, path);
|
||||
|
||||
if (file->fd < 0) {
|
||||
git_buf_free(&tmp_path);
|
||||
error = GIT_EOSERR;
|
||||
goto cleanup;
|
||||
}
|
||||
file->fd_is_open = true;
|
||||
|
||||
/* No original path */
|
||||
file->path_original = NULL;
|
||||
file->path_lock = git_buf_detach(&tmp_path);
|
||||
|
||||
if (file->path_lock == NULL) {
|
||||
error = GIT_ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
GITERR_CHECK_ALLOC(file->path_lock);
|
||||
} else {
|
||||
path_len = strlen(path);
|
||||
|
||||
/* Save the original path of the file */
|
||||
file->path_original = git__strdup(path);
|
||||
if (file->path_original == NULL) {
|
||||
error = GIT_ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
GITERR_CHECK_ALLOC(file->path_original);
|
||||
|
||||
/* create the locking path by appending ".lock" to the original */
|
||||
file->path_lock = git__malloc(path_len + GIT_FILELOCK_EXTLENGTH);
|
||||
if (file->path_lock == NULL) {
|
||||
error = GIT_ENOMEM;
|
||||
goto cleanup;
|
||||
}
|
||||
GITERR_CHECK_ALLOC(file->path_lock);
|
||||
|
||||
memcpy(file->path_lock, file->path_original, path_len);
|
||||
memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH);
|
||||
|
||||
/* open the file for locking */
|
||||
if ((error = lock_file(file, flags)) < GIT_SUCCESS)
|
||||
if (lock_file(file, flags) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
git_filebuf_cleanup(file);
|
||||
return git__rethrow(error, "Failed to open file buffer for '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_filebuf_hash(git_oid *oid, git_filebuf *file)
|
||||
{
|
||||
int error;
|
||||
|
||||
assert(oid && file && file->digest);
|
||||
|
||||
if ((error = flush_buffer(file)) < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Failed to get hash for file");
|
||||
flush_buffer(file);
|
||||
|
||||
if (verify_last_error(file) < 0)
|
||||
return -1;
|
||||
|
||||
git_hash_final(oid, file->digest);
|
||||
git_hash_free_ctx(file->digest);
|
||||
file->digest = NULL;
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_filebuf_commit_at(git_filebuf *file, const char *path, mode_t mode)
|
||||
{
|
||||
git__free(file->path_original);
|
||||
file->path_original = git__strdup(path);
|
||||
if (file->path_original == NULL)
|
||||
return GIT_ENOMEM;
|
||||
GITERR_CHECK_ALLOC(file->path_original);
|
||||
|
||||
return git_filebuf_commit(file, mode);
|
||||
}
|
||||
|
||||
int git_filebuf_commit(git_filebuf *file, mode_t mode)
|
||||
{
|
||||
int error;
|
||||
|
||||
/* temporary files cannot be committed */
|
||||
assert(file && file->path_original);
|
||||
|
||||
file->flush_mode = Z_FINISH;
|
||||
if ((error = flush_buffer(file)) < GIT_SUCCESS)
|
||||
goto cleanup;
|
||||
flush_buffer(file);
|
||||
|
||||
if (verify_last_error(file) < 0)
|
||||
goto on_error;
|
||||
|
||||
p_close(file->fd);
|
||||
file->fd = -1;
|
||||
file->fd_is_open = false;
|
||||
|
||||
if (p_chmod(file->path_lock, mode)) {
|
||||
error = git__throw(GIT_EOSERR, "Failed to chmod locked file before committing");
|
||||
goto cleanup;
|
||||
giterr_set(GITERR_OS, "Failed to set attributes for file at '%s'", file->path_lock);
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
error = p_rename(file->path_lock, file->path_original);
|
||||
p_unlink(file->path_original);
|
||||
|
||||
if (p_rename(file->path_lock, file->path_original) < 0) {
|
||||
giterr_set(GITERR_OS, "Failed to rename lockfile to '%s'", file->path_original);
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
git_filebuf_cleanup(file);
|
||||
if (error < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Failed to commit locked file from buffer");
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
|
||||
on_error:
|
||||
git_filebuf_cleanup(file);
|
||||
return -1;
|
||||
}
|
||||
|
||||
GIT_INLINE(void) add_to_cache(git_filebuf *file, const void *buf, size_t len)
|
||||
@ -308,22 +346,25 @@ GIT_INLINE(void) add_to_cache(git_filebuf *file, const void *buf, size_t len)
|
||||
|
||||
int git_filebuf_write(git_filebuf *file, const void *buff, size_t len)
|
||||
{
|
||||
int error;
|
||||
const unsigned char *buf = buff;
|
||||
|
||||
ENSURE_BUF_OK(file);
|
||||
|
||||
if (file->do_not_buffer)
|
||||
return file->write(file, (void *)buff, len);
|
||||
|
||||
for (;;) {
|
||||
size_t space_left = file->buf_size - file->buf_pos;
|
||||
|
||||
/* cache if it's small */
|
||||
if (space_left > len) {
|
||||
add_to_cache(file, buf, len);
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
add_to_cache(file, buf, space_left);
|
||||
|
||||
if ((error = flush_buffer(file)) < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Failed to write to buffer");
|
||||
if (flush_buffer(file) < 0)
|
||||
return -1;
|
||||
|
||||
len -= space_left;
|
||||
buf += space_left;
|
||||
@ -332,32 +373,37 @@ int git_filebuf_write(git_filebuf *file, const void *buff, size_t len)
|
||||
|
||||
int git_filebuf_reserve(git_filebuf *file, void **buffer, size_t len)
|
||||
{
|
||||
int error;
|
||||
size_t space_left = file->buf_size - file->buf_pos;
|
||||
|
||||
*buffer = NULL;
|
||||
|
||||
if (len > file->buf_size)
|
||||
return GIT_ENOMEM;
|
||||
ENSURE_BUF_OK(file);
|
||||
|
||||
if (len > file->buf_size) {
|
||||
file->last_error = BUFERR_MEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (space_left <= len) {
|
||||
if ((error = flush_buffer(file)) < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Failed to reserve buffer");
|
||||
if (flush_buffer(file) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
*buffer = (file->buffer + file->buf_pos);
|
||||
file->buf_pos += len;
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_filebuf_printf(git_filebuf *file, const char *format, ...)
|
||||
{
|
||||
va_list arglist;
|
||||
size_t space_left;
|
||||
int len, error;
|
||||
int len, res;
|
||||
char *tmp_buffer;
|
||||
|
||||
ENSURE_BUF_OK(file);
|
||||
|
||||
space_left = file->buf_size - file->buf_pos;
|
||||
|
||||
do {
|
||||
@ -365,24 +411,28 @@ int git_filebuf_printf(git_filebuf *file, const char *format, ...)
|
||||
len = p_vsnprintf((char *)file->buffer + file->buf_pos, space_left, format, arglist);
|
||||
va_end(arglist);
|
||||
|
||||
if (len < 0)
|
||||
return git__throw(GIT_EOSERR, "Failed to format string");
|
||||
if (len < 0) {
|
||||
file->last_error = BUFERR_MEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((size_t)len + 1 <= space_left) {
|
||||
file->buf_pos += len;
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((error = flush_buffer(file)) < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Failed to output to buffer");
|
||||
if (flush_buffer(file) < 0)
|
||||
return -1;
|
||||
|
||||
space_left = file->buf_size - file->buf_pos;
|
||||
|
||||
} while ((size_t)len + 1 <= space_left);
|
||||
|
||||
tmp_buffer = git__malloc(len + 1);
|
||||
if (!tmp_buffer)
|
||||
return GIT_ENOMEM;
|
||||
if (!tmp_buffer) {
|
||||
file->last_error = BUFERR_MEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
va_start(arglist, format);
|
||||
len = p_vsnprintf(tmp_buffer, len + 1, format, arglist);
|
||||
@ -390,12 +440,13 @@ int git_filebuf_printf(git_filebuf *file, const char *format, ...)
|
||||
|
||||
if (len < 0) {
|
||||
git__free(tmp_buffer);
|
||||
return git__throw(GIT_EOSERR, "Failed to format string");
|
||||
file->last_error = BUFERR_MEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = git_filebuf_write(file, tmp_buffer, len);
|
||||
res = git_filebuf_write(file, tmp_buffer, len);
|
||||
git__free(tmp_buffer);
|
||||
|
||||
return error;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -9,7 +9,7 @@
|
||||
|
||||
#include "fileops.h"
|
||||
#include "hash.h"
|
||||
#include "git2/zlib.h"
|
||||
#include <zlib.h>
|
||||
|
||||
#ifdef GIT_THREADS
|
||||
# define GIT_FILEBUF_THREADS
|
||||
@ -19,7 +19,8 @@
|
||||
#define GIT_FILEBUF_APPEND (1 << 2)
|
||||
#define GIT_FILEBUF_FORCE (1 << 3)
|
||||
#define GIT_FILEBUF_TEMPORARY (1 << 4)
|
||||
#define GIT_FILEBUF_DEFLATE_SHIFT (5)
|
||||
#define GIT_FILEBUF_DO_NOT_BUFFER (1 << 5)
|
||||
#define GIT_FILEBUF_DEFLATE_SHIFT (6)
|
||||
|
||||
#define GIT_FILELOCK_EXTENSION ".lock\0"
|
||||
#define GIT_FILELOCK_EXTLENGTH 6
|
||||
@ -40,25 +41,37 @@ struct git_filebuf {
|
||||
|
||||
size_t buf_size, buf_pos;
|
||||
git_file fd;
|
||||
bool fd_is_open;
|
||||
bool do_not_buffer;
|
||||
int last_error;
|
||||
};
|
||||
|
||||
typedef struct git_filebuf git_filebuf;
|
||||
|
||||
#define GIT_FILEBUF_INIT {0}
|
||||
|
||||
/* The git_filebuf object lifecycle is:
|
||||
/*
|
||||
* The git_filebuf object lifecycle is:
|
||||
* - Allocate git_filebuf, preferably using GIT_FILEBUF_INIT.
|
||||
*
|
||||
* - Call git_filebuf_open() to initialize the filebuf for use.
|
||||
*
|
||||
* - Make as many calls to git_filebuf_write(), git_filebuf_printf(),
|
||||
* git_filebuf_reserve() as you like.
|
||||
* git_filebuf_reserve() as you like. The error codes for these
|
||||
* functions don't need to be checked. They are stored internally
|
||||
* by the file buffer.
|
||||
*
|
||||
* - While you are writing, you may call git_filebuf_hash() to get
|
||||
* the hash of all you have written so far.
|
||||
* the hash of all you have written so far. This function will
|
||||
* fail if any of the previous writes to the buffer failed.
|
||||
*
|
||||
* - To close the git_filebuf, you may call git_filebuf_commit() or
|
||||
* git_filebuf_commit_at() to save the file, or
|
||||
* git_filebuf_cleanup() to abandon the file. All of these will
|
||||
* clear the git_filebuf object.
|
||||
* free the git_filebuf object. Likewise, all of these will fail
|
||||
* if any of the previous writes to the buffer failed, and set
|
||||
* an error code accordingly.
|
||||
*/
|
||||
|
||||
int git_filebuf_write(git_filebuf *lock, const void *buff, size_t len);
|
||||
int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len);
|
||||
int git_filebuf_printf(git_filebuf *file, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
|
||||
|
333
src/fileops.c
333
src/fileops.c
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -10,25 +10,19 @@
|
||||
|
||||
int git_futils_mkpath2file(const char *file_path, const mode_t mode)
|
||||
{
|
||||
int error;
|
||||
int result = 0;
|
||||
git_buf target_folder = GIT_BUF_INIT;
|
||||
|
||||
error = git_path_dirname_r(&target_folder, file_path);
|
||||
if (error < GIT_SUCCESS) {
|
||||
git_buf_free(&target_folder);
|
||||
return git__throw(GIT_EINVALIDPATH, "Failed to recursively build `%s` tree structure. Unable to parse parent folder name", file_path);
|
||||
} else {
|
||||
/* reset error */
|
||||
error = GIT_SUCCESS;
|
||||
}
|
||||
if (git_path_dirname_r(&target_folder, file_path) < 0)
|
||||
return -1;
|
||||
|
||||
/* Does the containing folder exist? */
|
||||
if (git_path_isdir(target_folder.ptr) != GIT_SUCCESS)
|
||||
if (git_path_isdir(target_folder.ptr) == false)
|
||||
/* Let's create the tree structure */
|
||||
error = git_futils_mkdir_r(target_folder.ptr, NULL, mode);
|
||||
result = git_futils_mkdir_r(target_folder.ptr, NULL, mode);
|
||||
|
||||
git_buf_free(&target_folder);
|
||||
return error;
|
||||
return result;
|
||||
}
|
||||
|
||||
int git_futils_mktmp(git_buf *path_out, const char *filename)
|
||||
@ -39,133 +33,180 @@ int git_futils_mktmp(git_buf *path_out, const char *filename)
|
||||
git_buf_puts(path_out, "_git2_XXXXXX");
|
||||
|
||||
if (git_buf_oom(path_out))
|
||||
return git__rethrow(git_buf_lasterror(path_out),
|
||||
"Failed to create temporary file for %s", filename);
|
||||
return -1;
|
||||
|
||||
if ((fd = p_mkstemp(path_out->ptr)) < 0)
|
||||
return git__throw(GIT_EOSERR, "Failed to create temporary file %s", path_out->ptr);
|
||||
if ((fd = p_mkstemp(path_out->ptr)) < 0) {
|
||||
giterr_set(GITERR_OS,
|
||||
"Failed to create temporary file '%s'", path_out->ptr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode)
|
||||
{
|
||||
if (git_futils_mkpath2file(path, dirmode) < GIT_SUCCESS)
|
||||
return git__throw(GIT_EOSERR, "Failed to create file %s", path);
|
||||
int fd;
|
||||
|
||||
return p_creat(path, mode);
|
||||
if (git_futils_mkpath2file(path, dirmode) < 0)
|
||||
return -1;
|
||||
|
||||
fd = p_creat(path, mode);
|
||||
if (fd < 0) {
|
||||
giterr_set(GITERR_OS, "Failed to create file '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int git_futils_creat_locked(const char *path, const mode_t mode)
|
||||
{
|
||||
int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
|
||||
return fd >= 0 ? fd : git__throw(GIT_EOSERR, "Failed to create locked file. Could not open %s", path);
|
||||
int fd;
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
wchar_t* buf;
|
||||
|
||||
buf = gitwin_to_utf16(path);
|
||||
fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
|
||||
git__free(buf);
|
||||
#else
|
||||
fd = open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_EXCL, mode);
|
||||
#endif
|
||||
|
||||
if (fd < 0) {
|
||||
giterr_set(GITERR_OS, "Failed to create locked file '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode)
|
||||
{
|
||||
if (git_futils_mkpath2file(path, dirmode) < GIT_SUCCESS)
|
||||
return git__throw(GIT_EOSERR, "Failed to create locked file %s", path);
|
||||
if (git_futils_mkpath2file(path, dirmode) < 0)
|
||||
return -1;
|
||||
|
||||
return git_futils_creat_locked(path, mode);
|
||||
}
|
||||
|
||||
int git_futils_open_ro(const char *path)
|
||||
{
|
||||
int fd = p_open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
if (errno == ENOENT)
|
||||
fd = GIT_ENOTFOUND;
|
||||
giterr_set(GITERR_OS, "Failed to open '%s'", path);
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
git_off_t git_futils_filesize(git_file fd)
|
||||
{
|
||||
struct stat sb;
|
||||
if (p_fstat(fd, &sb))
|
||||
return GIT_ERROR;
|
||||
|
||||
if (p_fstat(fd, &sb)) {
|
||||
giterr_set(GITERR_OS, "Failed to stat file descriptor");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sb.st_size;
|
||||
}
|
||||
|
||||
int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mtime, int *updated)
|
||||
mode_t git_futils_canonical_mode(mode_t raw_mode)
|
||||
{
|
||||
if (S_ISREG(raw_mode))
|
||||
return S_IFREG | GIT_CANONICAL_PERMS(raw_mode);
|
||||
else if (S_ISLNK(raw_mode))
|
||||
return S_IFLNK;
|
||||
else if (S_ISGITLINK(raw_mode))
|
||||
return S_IFGITLINK;
|
||||
else if (S_ISDIR(raw_mode))
|
||||
return S_IFDIR;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_futils_readbuffer_updated(git_buf *buf, const char *path, time_t *mtime, int *updated)
|
||||
{
|
||||
git_file fd;
|
||||
size_t len;
|
||||
struct stat st;
|
||||
unsigned char *buff;
|
||||
|
||||
assert(obj && path && *path);
|
||||
assert(buf && path && *path);
|
||||
|
||||
if (updated != NULL)
|
||||
*updated = 0;
|
||||
|
||||
if (p_stat(path, &st) < 0)
|
||||
return git__throw(GIT_ENOTFOUND, "Failed to stat file %s", path);
|
||||
if ((fd = git_futils_open_ro(path)) < 0)
|
||||
return fd;
|
||||
|
||||
if (S_ISDIR(st.st_mode))
|
||||
return git__throw(GIT_ERROR, "Can't read a dir into a buffer");
|
||||
if (p_fstat(fd, &st) < 0 || S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
|
||||
p_close(fd);
|
||||
giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we were given a time, we only want to read the file if it
|
||||
* has been modified.
|
||||
*/
|
||||
if (mtime != NULL && *mtime >= st.st_mtime)
|
||||
return GIT_SUCCESS;
|
||||
if (mtime != NULL && *mtime >= st.st_mtime) {
|
||||
p_close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mtime != NULL)
|
||||
*mtime = st.st_mtime;
|
||||
if (!git__is_sizet(st.st_size+1))
|
||||
return git__throw(GIT_ERROR, "Failed to read file `%s`. An error occured while calculating its size", path);
|
||||
|
||||
len = (size_t) st.st_size;
|
||||
|
||||
if ((fd = p_open(path, O_RDONLY)) < 0)
|
||||
return git__throw(GIT_EOSERR, "Failed to open %s for reading", path);
|
||||
git_buf_clear(buf);
|
||||
|
||||
if ((buff = git__malloc(len + 1)) == NULL) {
|
||||
if (git_buf_grow(buf, len + 1) < 0) {
|
||||
p_close(fd);
|
||||
return GIT_ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (p_read(fd, buff, len) < 0) {
|
||||
p_close(fd);
|
||||
git__free(buff);
|
||||
return git__throw(GIT_ERROR, "Failed to read file `%s`", path);
|
||||
buf->ptr[len] = '\0';
|
||||
|
||||
while (len > 0) {
|
||||
ssize_t read_size = p_read(fd, buf->ptr, len);
|
||||
|
||||
if (read_size < 0) {
|
||||
p_close(fd);
|
||||
giterr_set(GITERR_OS, "Failed to read descriptor for '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
len -= read_size;
|
||||
buf->size += read_size;
|
||||
}
|
||||
buff[len] = '\0';
|
||||
|
||||
p_close(fd);
|
||||
|
||||
if (mtime != NULL)
|
||||
*mtime = st.st_mtime;
|
||||
if (updated != NULL)
|
||||
*updated = 1;
|
||||
|
||||
obj->data = buff;
|
||||
obj->len = len;
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_futils_readbuffer(git_fbuffer *obj, const char *path)
|
||||
int git_futils_readbuffer(git_buf *buf, const char *path)
|
||||
{
|
||||
return git_futils_readbuffer_updated(obj, path, NULL, NULL);
|
||||
return git_futils_readbuffer_updated(buf, path, NULL, NULL);
|
||||
}
|
||||
|
||||
void git_futils_fbuffer_rtrim(git_fbuffer *obj)
|
||||
{
|
||||
unsigned char *buff = obj->data;
|
||||
while (obj->len > 0 && isspace(buff[obj->len - 1]))
|
||||
obj->len--;
|
||||
buff[obj->len] = '\0';
|
||||
}
|
||||
|
||||
void git_futils_freebuffer(git_fbuffer *obj)
|
||||
{
|
||||
assert(obj);
|
||||
git__free(obj->data);
|
||||
obj->data = NULL;
|
||||
}
|
||||
|
||||
|
||||
int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
|
||||
{
|
||||
if (git_futils_mkpath2file(to, dirmode) < GIT_SUCCESS)
|
||||
return GIT_EOSERR; /* The callee already takes care of setting the correct error message. */
|
||||
if (git_futils_mkpath2file(to, dirmode) < 0)
|
||||
return -1;
|
||||
|
||||
return p_rename(from, to); /* The callee already takes care of setting the correct error message. */
|
||||
if (p_rename(from, to) < 0) {
|
||||
giterr_set(GITERR_OS, "Failed to rename '%s' to '%s'", from, to);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len)
|
||||
@ -173,6 +214,26 @@ int git_futils_mmap_ro(git_map *out, git_file fd, git_off_t begin, size_t len)
|
||||
return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin);
|
||||
}
|
||||
|
||||
int git_futils_mmap_ro_file(git_map *out, const char *path)
|
||||
{
|
||||
git_file fd = git_futils_open_ro(path);
|
||||
git_off_t len;
|
||||
int result;
|
||||
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
|
||||
len = git_futils_filesize(fd);
|
||||
if (!git__is_sizet(len)) {
|
||||
giterr_set(GITERR_OS, "File `%s` too large to mmap", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
result = git_futils_mmap_ro(out, fd, 0, (size_t)len);
|
||||
p_close(fd);
|
||||
return result;
|
||||
}
|
||||
|
||||
void git_futils_mmap_free(git_map *out)
|
||||
{
|
||||
p_munmap(out);
|
||||
@ -180,20 +241,21 @@ void git_futils_mmap_free(git_map *out)
|
||||
|
||||
int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
|
||||
{
|
||||
int error, root_path_offset;
|
||||
int root_path_offset;
|
||||
git_buf make_path = GIT_BUF_INIT;
|
||||
size_t start;
|
||||
char *pp, *sp;
|
||||
bool failed = false;
|
||||
|
||||
if (base != NULL) {
|
||||
start = strlen(base);
|
||||
error = git_buf_joinpath(&make_path, base, path);
|
||||
if (git_buf_joinpath(&make_path, base, path) < 0)
|
||||
return -1;
|
||||
} else {
|
||||
start = 0;
|
||||
error = git_buf_puts(&make_path, path);
|
||||
if (git_buf_puts(&make_path, path) < 0)
|
||||
return -1;
|
||||
}
|
||||
if (error < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Failed to create `%s` tree structure", path);
|
||||
|
||||
pp = make_path.ptr + start;
|
||||
|
||||
@ -201,14 +263,13 @@ int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
|
||||
if (root_path_offset > 0)
|
||||
pp += root_path_offset; /* On Windows, will skip the drive name (eg. C: or D:) */
|
||||
|
||||
while (error == GIT_SUCCESS && (sp = strchr(pp, '/')) != NULL) {
|
||||
if (sp != pp && git_path_isdir(make_path.ptr) < GIT_SUCCESS) {
|
||||
while (!failed && (sp = strchr(pp, '/')) != NULL) {
|
||||
if (sp != pp && git_path_isdir(make_path.ptr) == false) {
|
||||
*sp = 0;
|
||||
error = p_mkdir(make_path.ptr, mode);
|
||||
|
||||
/* Do not choke while trying to recreate an existing directory */
|
||||
if (errno == EEXIST)
|
||||
error = GIT_SUCCESS;
|
||||
if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
|
||||
failed = true;
|
||||
|
||||
*sp = '/';
|
||||
}
|
||||
@ -216,53 +277,76 @@ int git_futils_mkdir_r(const char *path, const char *base, const mode_t mode)
|
||||
pp = sp + 1;
|
||||
}
|
||||
|
||||
if (*pp != '\0' && error == GIT_SUCCESS) {
|
||||
error = p_mkdir(make_path.ptr, mode);
|
||||
if (errno == EEXIST)
|
||||
error = GIT_SUCCESS;
|
||||
if (*pp != '\0' && !failed) {
|
||||
if (p_mkdir(make_path.ptr, mode) < 0 && errno != EEXIST)
|
||||
failed = true;
|
||||
}
|
||||
|
||||
git_buf_free(&make_path);
|
||||
|
||||
if (error < GIT_SUCCESS)
|
||||
return git__throw(error, "Failed to recursively create `%s` tree structure", path);
|
||||
if (failed) {
|
||||
giterr_set(GITERR_OS,
|
||||
"Failed to create directory structure at '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int _rmdir_recurs_foreach(void *opaque, git_buf *path)
|
||||
{
|
||||
int error = GIT_SUCCESS;
|
||||
int force = *(int *)opaque;
|
||||
git_directory_removal_type removal_type = *(git_directory_removal_type *)opaque;
|
||||
|
||||
if (git_path_isdir(path->ptr) == GIT_SUCCESS) {
|
||||
error = git_path_direach(path, _rmdir_recurs_foreach, opaque);
|
||||
if (error < GIT_SUCCESS)
|
||||
return git__rethrow(error, "Failed to remove directory `%s`", path->ptr);
|
||||
return p_rmdir(path->ptr);
|
||||
assert(removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY
|
||||
|| removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS
|
||||
|| removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS);
|
||||
|
||||
} else if (force) {
|
||||
return p_unlink(path->ptr);
|
||||
if (git_path_isdir(path->ptr) == true) {
|
||||
if (git_path_direach(path, _rmdir_recurs_foreach, opaque) < 0)
|
||||
return -1;
|
||||
|
||||
if (p_rmdir(path->ptr) < 0) {
|
||||
if (removal_type == GIT_DIRREMOVAL_ONLY_EMPTY_DIRS && (errno == ENOTEMPTY || errno == EEXIST))
|
||||
return 0;
|
||||
|
||||
giterr_set(GITERR_OS, "Could not remove directory '%s'", path->ptr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return git__rethrow(error, "Failed to remove directory. `%s` is not empty", path->ptr);
|
||||
if (removal_type == GIT_DIRREMOVAL_FILES_AND_DIRS) {
|
||||
if (p_unlink(path->ptr) < 0) {
|
||||
giterr_set(GITERR_OS, "Could not remove directory. File '%s' cannot be removed", path->ptr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (removal_type == GIT_DIRREMOVAL_EMPTY_HIERARCHY) {
|
||||
giterr_set(GITERR_OS, "Could not remove directory. File '%s' still present", path->ptr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_futils_rmdir_r(const char *path, int force)
|
||||
int git_futils_rmdir_r(const char *path, git_directory_removal_type removal_type)
|
||||
{
|
||||
int error;
|
||||
git_buf p = GIT_BUF_INIT;
|
||||
|
||||
error = git_buf_sets(&p, path);
|
||||
if (error == GIT_SUCCESS)
|
||||
error = _rmdir_recurs_foreach(&force, &p);
|
||||
if (!error)
|
||||
error = _rmdir_recurs_foreach(&removal_type, &p);
|
||||
git_buf_free(&p);
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_futils_find_global_file(git_buf *path, const char *filename)
|
||||
{
|
||||
int error;
|
||||
const char *home = getenv("HOME");
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
@ -270,19 +354,21 @@ int git_futils_find_global_file(git_buf *path, const char *filename)
|
||||
home = getenv("USERPROFILE");
|
||||
#endif
|
||||
|
||||
if (home == NULL)
|
||||
return git__throw(GIT_EOSERR, "Failed to open global %s file. "
|
||||
"Cannot locate the user's home directory.", filename);
|
||||
if (home == NULL) {
|
||||
giterr_set(GITERR_OS, "Global file lookup failed. "
|
||||
"Cannot locate the user's home directory");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((error = git_buf_joinpath(path, home, filename)) < GIT_SUCCESS)
|
||||
return error;
|
||||
if (git_buf_joinpath(path, home, filename) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_path_exists(path->ptr) < GIT_SUCCESS) {
|
||||
if (git_path_exists(path->ptr) == false) {
|
||||
git_buf_clear(path);
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
return GIT_SUCCESS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef GIT_WIN32
|
||||
@ -299,9 +385,8 @@ static const win32_path *win32_system_root(void)
|
||||
const wchar_t *root_tmpl = L"%PROGRAMFILES%\\Git\\etc\\";
|
||||
|
||||
s_root.len = ExpandEnvironmentStringsW(root_tmpl, NULL, 0);
|
||||
|
||||
if (s_root.len <= 0) {
|
||||
git__throw(GIT_EOSERR, "Failed to expand environment strings");
|
||||
giterr_set(GITERR_OS, "Failed to expand environment strings");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -310,7 +395,7 @@ static const win32_path *win32_system_root(void)
|
||||
return NULL;
|
||||
|
||||
if (ExpandEnvironmentStringsW(root_tmpl, s_root.path, s_root.len) != s_root.len) {
|
||||
git__throw(GIT_EOSERR, "Failed to expand environment strings");
|
||||
giterr_set(GITERR_OS, "Failed to expand environment strings");
|
||||
git__free(s_root.path);
|
||||
s_root.path = NULL;
|
||||
return NULL;
|
||||
@ -322,7 +407,7 @@ static const win32_path *win32_system_root(void)
|
||||
|
||||
static int win32_find_system_file(git_buf *path, const char *filename)
|
||||
{
|
||||
int error = GIT_SUCCESS;
|
||||
int error = 0;
|
||||
const win32_path *root = win32_system_root();
|
||||
size_t len;
|
||||
wchar_t *file_utf16 = NULL, *scan;
|
||||
@ -333,8 +418,7 @@ static int win32_find_system_file(git_buf *path, const char *filename)
|
||||
|
||||
/* allocate space for wchar_t path to file */
|
||||
file_utf16 = git__calloc(root->len + len + 2, sizeof(wchar_t));
|
||||
if (!file_utf16)
|
||||
return GIT_ENOMEM;
|
||||
GITERR_CHECK_ALLOC(file_utf16);
|
||||
|
||||
/* append root + '\\' + filename as wchar_t */
|
||||
memcpy(file_utf16, root->path, root->len * sizeof(wchar_t));
|
||||
@ -344,7 +428,7 @@ static int win32_find_system_file(git_buf *path, const char *filename)
|
||||
|
||||
if (gitwin_append_utf16(file_utf16 + root->len - 1, filename, len + 1) !=
|
||||
(int)len + 1) {
|
||||
error = git__throw(GIT_EOSERR, "Failed to build file path");
|
||||
error = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
@ -360,9 +444,8 @@ static int win32_find_system_file(git_buf *path, const char *filename)
|
||||
|
||||
/* convert to utf8 */
|
||||
if ((file_utf8 = gitwin_from_utf16(file_utf16)) == NULL)
|
||||
error = GIT_ENOMEM;
|
||||
|
||||
if (file_utf8) {
|
||||
error = -1;
|
||||
else {
|
||||
git_path_mkposix(file_utf8);
|
||||
git_buf_attach(path, file_utf8, 0);
|
||||
}
|
||||
@ -376,11 +459,11 @@ cleanup:
|
||||
|
||||
int git_futils_find_system_file(git_buf *path, const char *filename)
|
||||
{
|
||||
if (git_buf_joinpath(path, "/etc", filename) < GIT_SUCCESS)
|
||||
return git_buf_lasterror(path);
|
||||
if (git_buf_joinpath(path, "/etc", filename) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_path_exists(path->ptr) == GIT_SUCCESS)
|
||||
return GIT_SUCCESS;
|
||||
if (git_path_exists(path->ptr) == true)
|
||||
return 0;
|
||||
|
||||
git_buf_clear(path);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -17,17 +17,8 @@
|
||||
*
|
||||
* Read whole files into an in-memory buffer for processing
|
||||
*/
|
||||
#define GIT_FBUFFER_INIT {NULL, 0}
|
||||
|
||||
typedef struct { /* file io buffer */
|
||||
void *data; /* data bytes */
|
||||
size_t len; /* data length */
|
||||
} git_fbuffer;
|
||||
|
||||
extern int git_futils_readbuffer(git_fbuffer *obj, const char *path);
|
||||
extern int git_futils_readbuffer_updated(git_fbuffer *obj, const char *path, time_t *mtime, int *updated);
|
||||
extern void git_futils_freebuffer(git_fbuffer *obj);
|
||||
extern void git_futils_fbuffer_rtrim(git_fbuffer *obj);
|
||||
extern int git_futils_readbuffer(git_buf *obj, const char *path);
|
||||
extern int git_futils_readbuffer_updated(git_buf *obj, const char *path, time_t *mtime, int *updated);
|
||||
|
||||
/**
|
||||
* File utils
|
||||
@ -35,7 +26,7 @@ extern void git_futils_fbuffer_rtrim(git_fbuffer *obj);
|
||||
* These are custom filesystem-related helper methods. They are
|
||||
* rather high level, and wrap the underlying POSIX methods
|
||||
*
|
||||
* All these methods return GIT_SUCCESS on success,
|
||||
* All these methods return 0 on success,
|
||||
* or an error code on failure and an error message is set.
|
||||
*/
|
||||
|
||||
@ -67,10 +58,25 @@ extern int git_futils_mkdir_r(const char *path, const char *base, const mode_t m
|
||||
*/
|
||||
extern int git_futils_mkpath2file(const char *path, const mode_t mode);
|
||||
|
||||
typedef enum {
|
||||
GIT_DIRREMOVAL_EMPTY_HIERARCHY = 0,
|
||||
GIT_DIRREMOVAL_FILES_AND_DIRS = 1,
|
||||
GIT_DIRREMOVAL_ONLY_EMPTY_DIRS = 2,
|
||||
} git_directory_removal_type;
|
||||
|
||||
/**
|
||||
* Remove path and any files and directories beneath it.
|
||||
*
|
||||
* @param path Path to to top level directory to process.
|
||||
*
|
||||
* @param removal_type GIT_DIRREMOVAL_EMPTY_HIERARCHY to remove a hierarchy
|
||||
* of empty directories (will fail if any file is found), GIT_DIRREMOVAL_FILES_AND_DIRS
|
||||
* to remove a hierarchy of files and folders, GIT_DIRREMOVAL_ONLY_EMPTY_DIRS to only remove
|
||||
* empty directories (no failure on file encounter).
|
||||
*
|
||||
* @return 0 on success; -1 on error.
|
||||
*/
|
||||
extern int git_futils_rmdir_r(const char *path, int force);
|
||||
extern int git_futils_rmdir_r(const char *path, git_directory_removal_type removal_type);
|
||||
|
||||
/**
|
||||
* Create and open a temporary file with a `_git2_` suffix.
|
||||
@ -85,12 +91,26 @@ extern int git_futils_mktmp(git_buf *path_out, const char *filename);
|
||||
*/
|
||||
extern int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode);
|
||||
|
||||
/**
|
||||
* Open a file readonly and set error if needed.
|
||||
*/
|
||||
extern int git_futils_open_ro(const char *path);
|
||||
|
||||
/**
|
||||
* Get the filesize in bytes of a file
|
||||
*/
|
||||
extern git_off_t git_futils_filesize(git_file fd);
|
||||
|
||||
#define GIT_MODE_PERMS_MASK 0777
|
||||
#define GIT_CANONICAL_PERMS(MODE) (((MODE) & 0100) ? 0755 : 0644)
|
||||
#define GIT_MODE_TYPE(MODE) ((MODE) & ~GIT_MODE_PERMS_MASK)
|
||||
|
||||
/**
|
||||
* Convert a mode_t from the OS to a legal git mode_t value.
|
||||
*/
|
||||
extern mode_t git_futils_canonical_mode(mode_t raw_mode);
|
||||
|
||||
|
||||
/**
|
||||
* Read-only map all or part of a file into memory.
|
||||
* When possible this function should favor a virtual memory
|
||||
@ -103,8 +123,8 @@ extern git_off_t git_futils_filesize(git_file fd);
|
||||
* @param begin first byte to map, this should be page aligned.
|
||||
* @param end number of bytes to map.
|
||||
* @return
|
||||
* - GIT_SUCCESS on success;
|
||||
* - GIT_EOSERR on an unspecified OS related error.
|
||||
* - 0 on success;
|
||||
* - -1 on error.
|
||||
*/
|
||||
extern int git_futils_mmap_ro(
|
||||
git_map *out,
|
||||
@ -112,6 +132,20 @@ extern int git_futils_mmap_ro(
|
||||
git_off_t begin,
|
||||
size_t len);
|
||||
|
||||
/**
|
||||
* Read-only map an entire file.
|
||||
*
|
||||
* @param out buffer to populate with the mapping information.
|
||||
* @param path path to file to be opened.
|
||||
* @return
|
||||
* - 0 on success;
|
||||
* - GIT_ENOTFOUND if not found;
|
||||
* - -1 on an unspecified OS related error.
|
||||
*/
|
||||
extern int git_futils_mmap_ro_file(
|
||||
git_map *out,
|
||||
const char *path);
|
||||
|
||||
/**
|
||||
* Release the memory associated with a previous memory mapping.
|
||||
* @param map the mapping description previously configured.
|
||||
@ -124,9 +158,9 @@ extern void git_futils_mmap_free(git_map *map);
|
||||
* @param pathbuf buffer to write the full path into
|
||||
* @param filename name of file to find in the home directory
|
||||
* @return
|
||||
* - GIT_SUCCESS if found;
|
||||
* - 0 if found;
|
||||
* - GIT_ENOTFOUND if not found;
|
||||
* - GIT_EOSERR on an unspecified OS related error.
|
||||
* - -1 on an unspecified OS related error.
|
||||
*/
|
||||
extern int git_futils_find_global_file(git_buf *path, const char *filename);
|
||||
|
||||
@ -136,9 +170,9 @@ extern int git_futils_find_global_file(git_buf *path, const char *filename);
|
||||
* @param pathbuf buffer to write the full path into
|
||||
* @param filename name of file to find in the home directory
|
||||
* @return
|
||||
* - GIT_SUCCESS if found;
|
||||
* - 0 if found;
|
||||
* - GIT_ENOTFOUND if not found;
|
||||
* - GIT_EOSERR on an unspecified OS related error.
|
||||
* - -1 on an unspecified OS related error.
|
||||
*/
|
||||
extern int git_futils_find_system_file(git_buf *path, const char *filename);
|
||||
|
||||
|
165
src/filter.c
Normal file
165
src/filter.c
Normal file
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
|
||||
#include "common.h"
|
||||
#include "fileops.h"
|
||||
#include "hash.h"
|
||||
#include "filter.h"
|
||||
#include "repository.h"
|
||||
#include "git2/config.h"
|
||||
|
||||
/* Tweaked from Core Git. I wonder what we could use this for... */
|
||||
void git_text_gather_stats(git_text_stats *stats, const git_buf *text)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
memset(stats, 0, sizeof(*stats));
|
||||
|
||||
for (i = 0; i < git_buf_len(text); i++) {
|
||||
unsigned char c = text->ptr[i];
|
||||
|
||||
if (c == '\r') {
|
||||
stats->cr++;
|
||||
|
||||
if (i + 1 < git_buf_len(text) && text->ptr[i + 1] == '\n')
|
||||
stats->crlf++;
|
||||
}
|
||||
|
||||
else if (c == '\n')
|
||||
stats->lf++;
|
||||
|
||||
else if (c == 0x85)
|
||||
/* Unicode CR+LF */
|
||||
stats->crlf++;
|
||||
|
||||
else if (c == 127)
|
||||
/* DEL */
|
||||
stats->nonprintable++;
|
||||
|
||||
else if (c <= 0x1F || (c >= 0x80 && c <= 0x9F)) {
|
||||
switch (c) {
|
||||
/* BS, HT, ESC and FF */
|
||||
case '\b': case '\t': case '\033': case '\014':
|
||||
stats->printable++;
|
||||
break;
|
||||
case 0:
|
||||
stats->nul++;
|
||||
/* fall through */
|
||||
default:
|
||||
stats->nonprintable++;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
stats->printable++;
|
||||
}
|
||||
|
||||
/* If file ends with EOF then don't count this EOF as non-printable. */
|
||||
if (git_buf_len(text) >= 1 && text->ptr[text->size - 1] == '\032')
|
||||
stats->nonprintable--;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fresh from Core Git
|
||||
*/
|
||||
int git_text_is_binary(git_text_stats *stats)
|
||||
{
|
||||
if (stats->nul)
|
||||
return 1;
|
||||
|
||||
if ((stats->printable >> 7) < stats->nonprintable)
|
||||
return 1;
|
||||
/*
|
||||
* Other heuristics? Average line length might be relevant,
|
||||
* as might LF vs CR vs CRLF counts..
|
||||
*
|
||||
* NOTE! It might be normal to have a low ratio of CRLF to LF
|
||||
* (somebody starts with a LF-only file and edits it with an editor
|
||||
* that adds CRLF only to lines that are added..). But do we
|
||||
* want to support CR-only? Probably not.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (mode == GIT_FILTER_TO_ODB) {
|
||||
/* Load the CRLF cleanup filter when writing to the ODB */
|
||||
error = git_filter_add__crlf_to_odb(filters, repo, path);
|
||||
if (error < 0)
|
||||
return error;
|
||||
} else {
|
||||
giterr_set(GITERR_INVALID, "Worktree filters are not implemented yet");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)filters->length;
|
||||
}
|
||||
|
||||
void git_filters_free(git_vector *filters)
|
||||
{
|
||||
size_t i;
|
||||
git_filter *filter;
|
||||
|
||||
git_vector_foreach(filters, i, filter) {
|
||||
if (filter->do_free != NULL)
|
||||
filter->do_free(filter);
|
||||
else
|
||||
git__free(filter);
|
||||
}
|
||||
|
||||
git_vector_free(filters);
|
||||
}
|
||||
|
||||
int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters)
|
||||
{
|
||||
unsigned int i, src;
|
||||
git_buf *dbuffer[2];
|
||||
|
||||
dbuffer[0] = source;
|
||||
dbuffer[1] = dest;
|
||||
|
||||
src = 0;
|
||||
|
||||
if (git_buf_len(source) == 0) {
|
||||
git_buf_clear(dest);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Pre-grow the destination buffer to more or less the size
|
||||
* we expect it to have */
|
||||
if (git_buf_grow(dest, git_buf_len(source)) < 0)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < filters->length; ++i) {
|
||||
git_filter *filter = git_vector_get(filters, i);
|
||||
unsigned int dst = 1 - src;
|
||||
|
||||
git_buf_clear(dbuffer[dst]);
|
||||
|
||||
/* Apply the filter from dbuffer[src] to the other buffer;
|
||||
* if the filtering is canceled by the user mid-filter,
|
||||
* we skip to the next filter without changing the source
|
||||
* of the double buffering (so that the text goes through
|
||||
* cleanly).
|
||||
*/
|
||||
if (filter->apply(filter, dbuffer[dst], dbuffer[src]) == 0)
|
||||
src = dst;
|
||||
|
||||
if (git_buf_oom(dbuffer[dst]))
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Ensure that the output ends up in dbuffer[1] (i.e. the dest) */
|
||||
if (src != 1)
|
||||
git_buf_swap(dest, source);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
119
src/filter.h
Normal file
119
src/filter.h
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* 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_filter_h__
|
||||
#define INCLUDE_filter_h__
|
||||
|
||||
#include "common.h"
|
||||
#include "buffer.h"
|
||||
#include "git2/odb.h"
|
||||
#include "git2/repository.h"
|
||||
|
||||
typedef struct git_filter {
|
||||
int (*apply)(struct git_filter *self, git_buf *dest, const git_buf *source);
|
||||
void (*do_free)(struct git_filter *self);
|
||||
} git_filter;
|
||||
|
||||
typedef enum {
|
||||
GIT_FILTER_TO_WORKTREE,
|
||||
GIT_FILTER_TO_ODB
|
||||
} git_filter_mode;
|
||||
|
||||
typedef enum {
|
||||
GIT_CRLF_GUESS = -1,
|
||||
GIT_CRLF_BINARY = 0,
|
||||
GIT_CRLF_TEXT,
|
||||
GIT_CRLF_INPUT,
|
||||
GIT_CRLF_CRLF,
|
||||
GIT_CRLF_AUTO,
|
||||
} git_crlf_t;
|
||||
|
||||
typedef struct {
|
||||
/* NUL, CR, LF and CRLF counts */
|
||||
unsigned int nul, cr, lf, crlf;
|
||||
|
||||
/* These are just approximations! */
|
||||
unsigned int printable, nonprintable;
|
||||
} git_text_stats;
|
||||
|
||||
/*
|
||||
* FILTER API
|
||||
*/
|
||||
|
||||
/*
|
||||
* For any given path in the working directory, fill the `filters`
|
||||
* array with the relevant filters that need to be applied.
|
||||
*
|
||||
* Mode is either `GIT_FILTER_TO_WORKTREE` if you need to load the
|
||||
* filters that will be used when checking out a file to the working
|
||||
* directory, or `GIT_FILTER_TO_ODB` for the filters used when writing
|
||||
* a file to the ODB.
|
||||
*
|
||||
* @param filters Vector where to store all the loaded filters
|
||||
* @param repo Repository object that contains `path`
|
||||
* @param path Relative path of the file to be filtered
|
||||
* @param mode Filtering direction (WT->ODB or ODB->WT)
|
||||
* @return the number of filters loaded for the file (0 if the file
|
||||
* doesn't need filtering), or a negative error code
|
||||
*/
|
||||
extern int git_filters_load(git_vector *filters, git_repository *repo, const char *path, int mode);
|
||||
|
||||
/*
|
||||
* Apply one or more filters to a file.
|
||||
*
|
||||
* The file must have been loaded as a `git_buf` object. Both the `source`
|
||||
* and `dest` buffers are owned by the caller and must be freed once
|
||||
* they are no longer needed.
|
||||
*
|
||||
* NOTE: Because of the double-buffering schema, the `source` buffer that contains
|
||||
* the original file may be tampered once the filtering is complete. Regardless,
|
||||
* the `dest` buffer will always contain the final result of the filtering
|
||||
*
|
||||
* @param dest Buffer to store the result of the filtering
|
||||
* @param source Buffer containing the document to filter
|
||||
* @param filters A non-empty vector of filters as supplied by `git_filters_load`
|
||||
* @return 0 on success, an error code otherwise
|
||||
*/
|
||||
extern int git_filters_apply(git_buf *dest, git_buf *source, git_vector *filters);
|
||||
|
||||
/*
|
||||
* Free the `filters` array generated by `git_filters_load`.
|
||||
*
|
||||
* Note that this frees both the array and its contents. The array will
|
||||
* be clean/reusable after this call.
|
||||
*
|
||||
* @param filters A filters array as supplied by `git_filters_load`
|
||||
*/
|
||||
extern void git_filters_free(git_vector *filters);
|
||||
|
||||
/*
|
||||
* Available filters
|
||||
*/
|
||||
|
||||
/* Strip CRLF, from Worktree to ODB */
|
||||
extern int git_filter_add__crlf_to_odb(git_vector *filters, git_repository *repo, const char *path);
|
||||
|
||||
|
||||
/*
|
||||
* PLAINTEXT API
|
||||
*/
|
||||
|
||||
/*
|
||||
* Gather stats for a piece of text
|
||||
*
|
||||
* Fill the `stats` structure with information on the number of
|
||||
* unreadable characters, carriage returns, etc, so it can be
|
||||
* used in heuristics.
|
||||
*/
|
||||
extern void git_text_gather_stats(git_text_stats *stats, const git_buf *text);
|
||||
|
||||
/*
|
||||
* Process `git_text_stats` data generated by `git_text_stat` to see
|
||||
* if it qualifies as a binary file
|
||||
*/
|
||||
extern int git_text_is_binary(git_text_stats *stats);
|
||||
|
||||
#endif
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -62,7 +62,7 @@ git_global_st *git__global_state(void)
|
||||
if ((ptr = TlsGetValue(_tls_index)) != NULL)
|
||||
return ptr;
|
||||
|
||||
ptr = malloc(sizeof(git_global_st));
|
||||
ptr = git__malloc(sizeof(git_global_st));
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
|
||||
@ -78,7 +78,7 @@ static int _tls_init = 0;
|
||||
|
||||
static void cb__free_status(void *st)
|
||||
{
|
||||
free(st);
|
||||
git__free(st);
|
||||
}
|
||||
|
||||
void git_threads_init(void)
|
||||
@ -103,7 +103,7 @@ git_global_st *git__global_state(void)
|
||||
if ((ptr = pthread_getspecific(_tls_key)) != NULL)
|
||||
return ptr;
|
||||
|
||||
ptr = malloc(sizeof(git_global_st));
|
||||
ptr = git__malloc(sizeof(git_global_st));
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
@ -14,6 +14,9 @@ typedef struct {
|
||||
char last[1024];
|
||||
} error;
|
||||
|
||||
git_error *last_error;
|
||||
git_error error_t;
|
||||
|
||||
git_mwindow_ctl mem_ctl;
|
||||
} git_global_st;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2009-2011 the libgit2 contributors
|
||||
* Copyright (C) 2009-2012 the libgit2 contributors
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user