mirror of
https://git.proxmox.com/git/libgit2
synced 2025-12-30 19:51:27 +00:00
Merge remote-tracking branch 'libgit2/development' into blame
This commit is contained in:
commit
42c8f8f807
1
AUTHORS
1
AUTHORS
@ -68,5 +68,6 @@ Sven Strickroth
|
||||
Tim Branyen
|
||||
Tim Clem
|
||||
Tim Harder
|
||||
Torsten Bögershausen
|
||||
Trent Mick
|
||||
Vicent Marti
|
||||
|
||||
@ -27,9 +27,14 @@ OPTION( BUILD_EXAMPLES "Build library usage example apps" OFF )
|
||||
OPTION( TAGS "Generate tags" OFF )
|
||||
OPTION( PROFILE "Generate profiling information" OFF )
|
||||
OPTION( ENABLE_TRACE "Enables tracing support" OFF )
|
||||
OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF )
|
||||
OPTION( LIBGIT2_FILENAME "Name of the produced binary" OFF )
|
||||
|
||||
OPTION( ANDROID "Build for android NDK" OFF )
|
||||
OPTION( ANDROID "Build for android NDK" OFF )
|
||||
|
||||
OPTION( USE_ICONV "Link with and use iconv library" OFF )
|
||||
IF(APPLE)
|
||||
SET( USE_ICONV ON )
|
||||
ENDIF()
|
||||
|
||||
IF(MSVC)
|
||||
# This option is only available when building with MSVC. By default, libgit2
|
||||
@ -43,8 +48,21 @@ IF(MSVC)
|
||||
# This option must match the settings used in your program, in particular if you
|
||||
# are linking statically
|
||||
OPTION( STATIC_CRT "Link the static CRT libraries" ON )
|
||||
|
||||
# By default, libgit2 is built with WinHTTP. To use the built-in
|
||||
# HTTP transport, invoke CMake with the "-DWINHTTP=OFF" argument.
|
||||
OPTION( WINHTTP "Use Win32 WinHTTP routines" ON )
|
||||
ENDIF()
|
||||
|
||||
# This variable will contain the libraries we need to put into
|
||||
# libgit2.pc's Requires.private. That is, what we're linking to or
|
||||
# what someone who's statically linking us needs to link to.
|
||||
SET(LIBGIT2_PC_REQUIRES "")
|
||||
# This will be set later if we use the system's http-parser library or
|
||||
# use iconv (OSX) and will be written to the Libs.private field in the
|
||||
# pc file.
|
||||
SET(LIBGIT2_PC_LIBS "")
|
||||
|
||||
# Installation paths
|
||||
#
|
||||
SET(BIN_INSTALL_DIR bin CACHE PATH "Where to install binaries to.")
|
||||
@ -56,9 +74,18 @@ FUNCTION(TARGET_OS_LIBRARIES target)
|
||||
TARGET_LINK_LIBRARIES(${target} ws2_32)
|
||||
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
|
||||
TARGET_LINK_LIBRARIES(${target} socket nsl)
|
||||
SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lsocket -lnsl" PARENT_SCOPE)
|
||||
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
TARGET_LINK_LIBRARIES(${target} rt)
|
||||
ENDIF ()
|
||||
SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lrt" PARENT_SCOPE)
|
||||
ENDIF()
|
||||
|
||||
IF(USE_ICONV)
|
||||
TARGET_LINK_LIBRARIES(${target} iconv)
|
||||
ADD_DEFINITIONS(-DGIT_USE_ICONV)
|
||||
SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -liconv" PARENT_SCOPE)
|
||||
ENDIF()
|
||||
|
||||
IF(THREADSAFE)
|
||||
TARGET_LINK_LIBRARIES(${target} ${CMAKE_THREAD_LIBS_INIT})
|
||||
ENDIF()
|
||||
@ -97,7 +124,7 @@ SET(LIBGIT2_VERSION_STRING "${LIBGIT2_VERSION_MAJOR}.${LIBGIT2_VERSION_MINOR}.${
|
||||
# Find required dependencies
|
||||
INCLUDE_DIRECTORIES(src include)
|
||||
|
||||
IF (WIN32 AND NOT MINGW)
|
||||
IF (WIN32 AND WINHTTP AND NOT MINGW)
|
||||
ADD_DEFINITIONS(-DGIT_WINHTTP)
|
||||
ELSE ()
|
||||
IF (NOT AMIGA)
|
||||
@ -108,6 +135,7 @@ ELSE ()
|
||||
IF (HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2)
|
||||
INCLUDE_DIRECTORIES(${HTTP_PARSER_INCLUDE_DIRS})
|
||||
LINK_LIBRARIES(${HTTP_PARSER_LIBRARIES})
|
||||
SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lhttp_parser")
|
||||
ELSE()
|
||||
MESSAGE("http-parser was not found or is too old; using bundled 3rd-party sources.")
|
||||
INCLUDE_DIRECTORIES(deps/http-parser)
|
||||
@ -121,6 +149,7 @@ IF (WIN32 AND NOT MINGW AND NOT SHA1_TYPE STREQUAL "builtin")
|
||||
FILE(GLOB SRC_SHA1 src/hash/hash_win32.c)
|
||||
ELSEIF (OPENSSL_FOUND AND NOT SHA1_TYPE STREQUAL "builtin")
|
||||
ADD_DEFINITIONS(-DOPENSSL_SHA1)
|
||||
SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} openssl")
|
||||
ELSE()
|
||||
FILE(GLOB SRC_SHA1 src/hash/hash_generic.c)
|
||||
ENDIF()
|
||||
@ -143,6 +172,11 @@ FIND_PACKAGE(ZLIB QUIET)
|
||||
IF (ZLIB_FOUND)
|
||||
INCLUDE_DIRECTORIES(${ZLIB_INCLUDE_DIRS})
|
||||
LINK_LIBRARIES(${ZLIB_LIBRARIES})
|
||||
IF(APPLE)
|
||||
SET(LIBGIT2_PC_LIBS "${LIBGIT2_PC_LIBS} -lz")
|
||||
ELSE()
|
||||
SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} zlib")
|
||||
ENDIF()
|
||||
# Fake the message CMake would have shown
|
||||
MESSAGE("-- Found zlib: ${ZLIB_LIBRARY}")
|
||||
ELSE()
|
||||
@ -158,6 +192,7 @@ ENDIF()
|
||||
IF (LIBSSH2_FOUND)
|
||||
ADD_DEFINITIONS(-DGIT_SSH)
|
||||
INCLUDE_DIRECTORIES(${LIBSSH2_INCLUDE_DIR})
|
||||
SET(LIBGIT2_PC_REQUIRES "${LIBGIT2_PC_REQUIRES} libssh2")
|
||||
SET(SSH_LIBRARIES ${LIBSSH2_LIBRARIES})
|
||||
ENDIF()
|
||||
|
||||
|
||||
43
README.md
43
README.md
@ -1,21 +1,21 @@
|
||||
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
|
||||
`libgit2` is a portable, pure C implementation of the Git core methods provided as a
|
||||
re-entrant linkable library with a solid API, allowing you to write native
|
||||
speed custom Git applications in any language with bindings.
|
||||
|
||||
libgit2 is licensed under a **very permissive license** (GPLv2 with a special Linking Exception).
|
||||
`libgit2` is licensed under a **very permissive license** (GPLv2 with a special Linking Exception).
|
||||
This basically means that you can link it (unmodified) with any kind of software without having to
|
||||
release its source code.
|
||||
|
||||
* Website: <http://libgit2.github.com>
|
||||
* Website: [libgit2.github.com](http://libgit2.github.com)
|
||||
* StackOverflow Tag: [libgit2](http://stackoverflow.com/questions/tagged/libgit2)
|
||||
* Issues: <https://github.com/libgit2/libgit2/issues>
|
||||
* Issues: [GitHub Issues](https://github.com/libgit2/libgit2/issues) (Right here!)
|
||||
* API documentation: <http://libgit2.github.com/libgit2>
|
||||
* IRC: #libgit2 on irc.freenode.net.
|
||||
* IRC: [#libgit2](irc://irc.freenode.net/libgit2) on irc.freenode.net.
|
||||
* Mailing list: The libgit2 mailing list was
|
||||
traditionally hosted in Librelist but has been deprecated. We encourage you to
|
||||
[use StackOverflow](http://stackoverflow.com/questions/tagged/libgit2) instead for any questions regarding
|
||||
@ -25,9 +25,9 @@ release its source code.
|
||||
|
||||
|
||||
What It Can Do
|
||||
==================================
|
||||
==============
|
||||
|
||||
libgit2 is already very usable and is being used in production for many applications including the GitHub.com site, in Plastic SCM
|
||||
`libgit2` is already very usable and is being used in production for many applications including the GitHub.com site, in Plastic SCM
|
||||
and also powering Microsoft's Visual Studio tools for Git. The library provides:
|
||||
|
||||
* SHA conversions, formatting and shortening
|
||||
@ -43,15 +43,26 @@ and also powering Microsoft's Visual Studio tools for Git. The library provides
|
||||
* descriptive and detailed error messages
|
||||
* ...and more (over 175 different API calls)
|
||||
|
||||
Optional dependencies
|
||||
=====================
|
||||
|
||||
While the library provides git functionality without the need for
|
||||
dependencies, it can make use of a few libraries to add to it:
|
||||
|
||||
- pthreads (non-Windows) to enable threadsafe access as well as multi-threaded pack generation
|
||||
- OpenSSL (non-Windows) to talk over HTTPS and provide the SHA-1 functions
|
||||
- LibSSH2 to enable the ssh transport
|
||||
- iconv (OSX) to handle the HFS+ path encoding peculiarities
|
||||
|
||||
Building libgit2 - Using CMake
|
||||
==============================
|
||||
|
||||
libgit2 builds cleanly on most platforms without any external dependencies.
|
||||
`libgit2` builds cleanly on most platforms without any external dependencies.
|
||||
Under Unix-like systems, like Linux, \*BSD and Mac OS X, libgit2 expects `pthreads` to be available;
|
||||
they should be installed by default on all systems. Under Windows, libgit2 uses the native Windows API
|
||||
for threading.
|
||||
|
||||
The libgit2 library is built using CMake 2.6+ (<http://www.cmake.org>) on all platforms.
|
||||
The `libgit2` library is built using `CMake 2.6+` (<http://www.cmake.org>) on all platforms.
|
||||
|
||||
On most systems you can build the library using the following commands
|
||||
|
||||
@ -112,8 +123,8 @@ Android
|
||||
-------
|
||||
|
||||
Extract toolchain from NDK using, `make-standalone-toolchain.sh` script.
|
||||
Optionaly, crosscompile and install OpenSSL inside of it. Then create CMake
|
||||
toolchain file that configures paths to your crosscompiler (substitude `{PATH}`
|
||||
Optionally, crosscompile and install OpenSSL inside of it. Then create CMake
|
||||
toolchain file that configures paths to your crosscompiler (substitute `{PATH}`
|
||||
with full path to the toolchain):
|
||||
|
||||
SET(CMAKE_SYSTEM_NAME Linux)
|
||||
@ -155,7 +166,7 @@ Here are the bindings to libgit2 that are currently available:
|
||||
* luagit2 <https://github.com/libgit2/luagit2>
|
||||
* .NET
|
||||
* libgit2sharp <https://github.com/libgit2/libgit2sharp>
|
||||
* libgit2net, low level bindings superceeded by libgit2sharp <https://github.com/txdv/libgit2net>
|
||||
* libgit2net, low level bindings superseded by libgit2sharp <https://github.com/txdv/libgit2net>
|
||||
* Node.js
|
||||
* node-gitteh <https://github.com/libgit2/node-gitteh>
|
||||
* nodegit <https://github.com/tbranyen/nodegit>
|
||||
@ -187,9 +198,9 @@ Check the [contribution guidelines](CONTRIBUTING.md).
|
||||
|
||||
License
|
||||
==================================
|
||||
libgit2 is under GPL2 **with linking exemption**. This means you
|
||||
can link to the library with any program, commercial, open source or
|
||||
other. However, you cannot modify libgit2 and distribute it without
|
||||
`libgit2` is under GPL2 **with linking exemption**. This means you
|
||||
can link to and use the library from any program, proprietary or open source; paid
|
||||
or gratis. However, you cannot modify libgit2 and distribute it without
|
||||
supplying the source.
|
||||
|
||||
See the COPYING file for the full license text.
|
||||
|
||||
@ -45,25 +45,23 @@ char *colors[] = {
|
||||
|
||||
static int printer(
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_range *range,
|
||||
char usage,
|
||||
const char *line,
|
||||
size_t line_len,
|
||||
const git_diff_hunk *hunk,
|
||||
const git_diff_line *line,
|
||||
void *data)
|
||||
{
|
||||
int *last_color = data, color = 0;
|
||||
|
||||
(void)delta; (void)range; (void)line_len;
|
||||
(void)delta; (void)hunk;
|
||||
|
||||
if (*last_color >= 0) {
|
||||
switch (usage) {
|
||||
case GIT_DIFF_LINE_ADDITION: color = 3; break;
|
||||
case GIT_DIFF_LINE_DELETION: color = 2; break;
|
||||
switch (line->origin) {
|
||||
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;
|
||||
case GIT_DIFF_LINE_FILE_HDR: color = 1; break;
|
||||
case GIT_DIFF_LINE_HUNK_HDR: color = 4; break;
|
||||
default: break;
|
||||
}
|
||||
if (color != *last_color) {
|
||||
if (*last_color == 1 || color == 1)
|
||||
@ -73,7 +71,13 @@ static int printer(
|
||||
}
|
||||
}
|
||||
|
||||
fputs(line, stdout);
|
||||
if (line->origin == GIT_DIFF_LINE_CONTEXT ||
|
||||
line->origin == GIT_DIFF_LINE_ADDITION ||
|
||||
line->origin == GIT_DIFF_LINE_DELETION)
|
||||
fputc(line->origin, stdout);
|
||||
|
||||
fwrite(line->content, 1, line->content_len, stdout);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -114,20 +118,15 @@ static void usage(const char *message, const char *arg)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
enum {
|
||||
FORMAT_PATCH = 0,
|
||||
FORMAT_COMPACT = 1,
|
||||
FORMAT_RAW = 2
|
||||
};
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
git_repository *repo = NULL;
|
||||
git_tree *t1 = NULL, *t2 = NULL;
|
||||
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
||||
git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
|
||||
git_diff_list *diff;
|
||||
int i, color = -1, format = FORMAT_PATCH, cached = 0;
|
||||
git_diff *diff;
|
||||
int i, color = -1, cached = 0;
|
||||
git_diff_format_t format = GIT_DIFF_FORMAT_PATCH;
|
||||
char *a, *treeish1 = NULL, *treeish2 = NULL;
|
||||
const char *dir = ".";
|
||||
|
||||
@ -148,13 +147,15 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
else if (!strcmp(a, "-p") || !strcmp(a, "-u") ||
|
||||
!strcmp(a, "--patch"))
|
||||
format = FORMAT_PATCH;
|
||||
format = GIT_DIFF_FORMAT_PATCH;
|
||||
else if (!strcmp(a, "--cached"))
|
||||
cached = 1;
|
||||
else if (!strcmp(a, "--name-only"))
|
||||
format = GIT_DIFF_FORMAT_NAME_ONLY;
|
||||
else if (!strcmp(a, "--name-status"))
|
||||
format = FORMAT_COMPACT;
|
||||
format = GIT_DIFF_FORMAT_NAME_STATUS;
|
||||
else if (!strcmp(a, "--raw"))
|
||||
format = FORMAT_RAW;
|
||||
format = GIT_DIFF_FORMAT_RAW;
|
||||
else if (!strcmp(a, "--color"))
|
||||
color = 0;
|
||||
else if (!strcmp(a, "--no-color"))
|
||||
@ -218,11 +219,11 @@ int main(int argc, char *argv[])
|
||||
else if (t1 && cached)
|
||||
check(git_diff_tree_to_index(&diff, repo, t1, NULL, &opts), "Diff");
|
||||
else if (t1) {
|
||||
git_diff_list *diff2;
|
||||
git_diff *diff2;
|
||||
check(git_diff_tree_to_index(&diff, repo, t1, NULL, &opts), "Diff");
|
||||
check(git_diff_index_to_workdir(&diff2, repo, NULL, &opts), "Diff");
|
||||
check(git_diff_merge(diff, diff2), "Merge diffs");
|
||||
git_diff_list_free(diff2);
|
||||
git_diff_free(diff2);
|
||||
}
|
||||
else if (cached) {
|
||||
check(resolve_to_tree(repo, "HEAD", &t1), "looking up HEAD");
|
||||
@ -238,22 +239,12 @@ int main(int argc, char *argv[])
|
||||
if (color >= 0)
|
||||
fputs(colors[0], stdout);
|
||||
|
||||
switch (format) {
|
||||
case FORMAT_PATCH:
|
||||
check(git_diff_print_patch(diff, printer, &color), "Displaying diff");
|
||||
break;
|
||||
case FORMAT_COMPACT:
|
||||
check(git_diff_print_compact(diff, printer, &color), "Displaying diff");
|
||||
break;
|
||||
case FORMAT_RAW:
|
||||
check(git_diff_print_raw(diff, printer, &color), "Displaying diff");
|
||||
break;
|
||||
}
|
||||
check(git_diff_print(diff, format, printer, &color), "Displaying diff");
|
||||
|
||||
if (color >= 0)
|
||||
fputs(colors[0], stdout);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
git_diff_free(diff);
|
||||
git_tree_free(t1);
|
||||
git_tree_free(t2);
|
||||
git_repository_free(repo);
|
||||
|
||||
@ -184,14 +184,18 @@ static void print_commit(git_commit *commit)
|
||||
|
||||
static int print_diff(
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_range *range,
|
||||
char usage,
|
||||
const char *line,
|
||||
size_t line_len,
|
||||
const git_diff_hunk *hunk,
|
||||
const git_diff_line *line,
|
||||
void *data)
|
||||
{
|
||||
(void)delta; (void)range; (void)usage; (void)line_len; (void)data;
|
||||
fputs(line, stdout);
|
||||
(void)delta; (void)hunk; (void)data;
|
||||
|
||||
if (line->origin == GIT_DIFF_LINE_CONTEXT ||
|
||||
line->origin == GIT_DIFF_LINE_ADDITION ||
|
||||
line->origin == GIT_DIFF_LINE_DELETION)
|
||||
fputc(line->origin, stdout);
|
||||
|
||||
fwrite(line->content, 1, line->content_len, stdout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -218,7 +222,7 @@ static int match_with_parent(
|
||||
{
|
||||
git_commit *parent;
|
||||
git_tree *a, *b;
|
||||
git_diff_list *diff;
|
||||
git_diff *diff;
|
||||
int ndeltas;
|
||||
|
||||
check(git_commit_parent(&parent, commit, (size_t)i), "Get parent", NULL);
|
||||
@ -229,7 +233,7 @@ static int match_with_parent(
|
||||
|
||||
ndeltas = (int)git_diff_num_deltas(diff);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
git_diff_free(diff);
|
||||
git_tree_free(a);
|
||||
git_tree_free(b);
|
||||
git_commit_free(parent);
|
||||
@ -373,7 +377,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (opt.show_diff) {
|
||||
git_tree *a = NULL, *b = NULL;
|
||||
git_diff_list *diff = NULL;
|
||||
git_diff *diff = NULL;
|
||||
|
||||
if (parents > 1)
|
||||
continue;
|
||||
@ -388,10 +392,10 @@ int main(int argc, char *argv[])
|
||||
check(git_diff_tree_to_tree(
|
||||
&diff, git_commit_owner(commit), a, b, &diffopts),
|
||||
"Diff commit with parent", NULL);
|
||||
check(git_diff_print_patch(diff, print_diff, NULL),
|
||||
check(git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, print_diff, NULL),
|
||||
"Displaying diff", NULL);
|
||||
|
||||
git_diff_list_free(diff);
|
||||
git_diff_free(diff);
|
||||
git_tree_free(a);
|
||||
git_tree_free(b);
|
||||
}
|
||||
|
||||
@ -25,13 +25,19 @@ static void print_progress(const progress_data *pd)
|
||||
: 0.f;
|
||||
int kbytes = pd->fetch_progress.received_bytes / 1024;
|
||||
|
||||
printf("net %3d%% (%4d kb, %5d/%5d) / idx %3d%% (%5d/%5d) / chk %3d%% (%4" PRIuZ "/%4" PRIuZ ") %s\n",
|
||||
if (pd->fetch_progress.received_objects == pd->fetch_progress.total_objects) {
|
||||
printf("Resolving deltas %d/%d\r",
|
||||
pd->fetch_progress.indexed_deltas,
|
||||
pd->fetch_progress.total_deltas);
|
||||
} else {
|
||||
printf("net %3d%% (%4d kb, %5d/%5d) / idx %3d%% (%5d/%5d) / chk %3d%% (%4" PRIuZ "/%4" PRIuZ ") %s\n",
|
||||
network_percent, kbytes,
|
||||
pd->fetch_progress.received_objects, pd->fetch_progress.total_objects,
|
||||
index_percent, pd->fetch_progress.indexed_objects, pd->fetch_progress.total_objects,
|
||||
checkout_percent,
|
||||
pd->completed_steps, pd->total_steps,
|
||||
pd->path);
|
||||
}
|
||||
}
|
||||
|
||||
static int fetch_progress(const git_transfer_progress *stats, void *payload)
|
||||
|
||||
@ -72,6 +72,7 @@ int fetch(git_repository *repo, int argc, char **argv)
|
||||
const git_transfer_progress *stats;
|
||||
struct dl_data data;
|
||||
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
|
||||
int resolve_deltas_ln = 0;
|
||||
#ifndef _WIN32
|
||||
pthread_t worker;
|
||||
#endif
|
||||
@ -113,10 +114,14 @@ int fetch(git_repository *repo, int argc, char **argv)
|
||||
do {
|
||||
usleep(10000);
|
||||
|
||||
if (stats->total_objects > 0)
|
||||
if (stats->received_objects == stats->total_objects) {
|
||||
printf("Resolving deltas %d/%d\r",
|
||||
stats->indexed_deltas, stats->total_deltas);
|
||||
} else if (stats->total_objects > 0) {
|
||||
printf("Received %d/%d objects (%d) in %" PRIuZ " bytes\r",
|
||||
stats->received_objects, stats->total_objects,
|
||||
stats->indexed_objects, stats->received_bytes);
|
||||
}
|
||||
} while (!data.finished);
|
||||
|
||||
if (data.ret < 0)
|
||||
@ -125,8 +130,13 @@ int fetch(git_repository *repo, int argc, char **argv)
|
||||
pthread_join(worker, NULL);
|
||||
#endif
|
||||
|
||||
printf("\rReceived %d/%d objects in %zu bytes\n",
|
||||
if (stats->local_objects > 0) {
|
||||
printf("\rReceived %d/%d objects in %zu bytes (used %d local objects)\n",
|
||||
stats->indexed_objects, stats->total_objects, stats->received_bytes, stats->local_objects);
|
||||
} else{
|
||||
printf("\rReceived %d/%d objects in %zu bytes\n",
|
||||
stats->indexed_objects, stats->total_objects, stats->received_bytes);
|
||||
}
|
||||
|
||||
// Disconnect the underlying connection to prevent from idling.
|
||||
git_remote_disconnect(remote);
|
||||
|
||||
@ -46,7 +46,7 @@ int index_pack(git_repository *repo, int argc, char **argv)
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (git_indexer_stream_new(&idx, ".", NULL, NULL) < 0) {
|
||||
if (git_indexer_stream_new(&idx, ".", NULL, NULL, NULL) < 0) {
|
||||
puts("bad idx");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -68,3 +68,4 @@ ok Sebastian Schuberth <sschuberth@gmail.com>
|
||||
ok Shawn O. Pearce <spearce@spearce.org>
|
||||
ok Steffen Prohaska <prohaska@zib.de>
|
||||
ok Sven Verdoolaege <skimo@kotnet.org>
|
||||
ok Torsten Bögershausen <tboegi@web.de>
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
#include "git2/odb.h"
|
||||
#include "git2/oid.h"
|
||||
#include "git2/pack.h"
|
||||
#include "git2/patch.h"
|
||||
#include "git2/pathspec.h"
|
||||
#include "git2/push.h"
|
||||
#include "git2/refdb.h"
|
||||
|
||||
@ -131,6 +131,13 @@ typedef enum {
|
||||
/** Don't refresh index/config/etc before doing checkout */
|
||||
GIT_CHECKOUT_NO_REFRESH = (1u << 9),
|
||||
|
||||
/** Allow checkout to skip unmerged files */
|
||||
GIT_CHECKOUT_SKIP_UNMERGED = (1u << 10),
|
||||
/** For unmerged files, checkout stage 2 from index */
|
||||
GIT_CHECKOUT_USE_OURS = (1u << 11),
|
||||
/** For unmerged files, checkout stage 3 from index */
|
||||
GIT_CHECKOUT_USE_THEIRS = (1u << 12),
|
||||
|
||||
/** Treat pathspec as simple list of exact match file paths */
|
||||
GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH = (1u << 13),
|
||||
|
||||
@ -141,13 +148,6 @@ typedef enum {
|
||||
* THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
|
||||
*/
|
||||
|
||||
/** Allow checkout to skip unmerged files (NOT IMPLEMENTED) */
|
||||
GIT_CHECKOUT_SKIP_UNMERGED = (1u << 10),
|
||||
/** For unmerged files, checkout stage 2 from index (NOT IMPLEMENTED) */
|
||||
GIT_CHECKOUT_USE_OURS = (1u << 11),
|
||||
/** For unmerged files, checkout stage 3 from index (NOT IMPLEMENTED) */
|
||||
GIT_CHECKOUT_USE_THEIRS = (1u << 12),
|
||||
|
||||
/** Recursively checkout submodules with same options (NOT IMPLEMENTED) */
|
||||
GIT_CHECKOUT_UPDATE_SUBMODULES = (1u << 16),
|
||||
/** Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED) */
|
||||
@ -238,6 +238,9 @@ typedef struct git_checkout_opts {
|
||||
git_tree *baseline; /** expected content of workdir, defaults to HEAD */
|
||||
|
||||
const char *target_directory; /** alternative checkout path to workdir */
|
||||
|
||||
const char *our_label; /** the name of the "our" side of conflicts */
|
||||
const char *their_label; /** the name of the "their" side of conflicts */
|
||||
} git_checkout_opts;
|
||||
|
||||
#define GIT_CHECKOUT_OPTS_VERSION 1
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -225,6 +225,9 @@ GIT_EXTERN(int) git_index_set_caps(git_index *index, unsigned int caps);
|
||||
* Update the contents of an existing index object in memory
|
||||
* by reading from the hard disk.
|
||||
*
|
||||
* If the file doesn't exist on the filesystem, the index
|
||||
* will be cleared from its current content.
|
||||
*
|
||||
* @param index an existing index object
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
|
||||
@ -20,12 +20,16 @@ typedef struct git_indexer_stream git_indexer_stream;
|
||||
*
|
||||
* @param out where to store the indexer instance
|
||||
* @param path to the directory where the packfile should be stored
|
||||
* @param odb object database from which to read base objects when
|
||||
* fixing thin packs. Pass NULL if no thin pack is expected (an error
|
||||
* will be returned if there are bases missing)
|
||||
* @param progress_cb function to call with progress information
|
||||
* @param progress_cb_payload payload for the progress callback
|
||||
*/
|
||||
GIT_EXTERN(int) git_indexer_stream_new(
|
||||
git_indexer_stream **out,
|
||||
const char *path,
|
||||
git_odb *odb,
|
||||
git_transfer_progress_callback progress_cb,
|
||||
void *progress_cb_payload);
|
||||
|
||||
|
||||
@ -357,6 +357,20 @@ GIT_EXTERN(int) git_odb_hash(git_oid *out, const void *data, size_t len, git_oty
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_hashfile(git_oid *out, const char *path, git_otype type);
|
||||
|
||||
/**
|
||||
* Create a copy of an odb_object
|
||||
*
|
||||
* The returned copy must be manually freed with `git_odb_object_free`.
|
||||
* Note that because of an implementation detail, the returned copy will be
|
||||
* the same pointer as `source`: the object is internally refcounted, so the
|
||||
* copy still needs to be freed twice.
|
||||
*
|
||||
* @param dest pointer where to store the copy
|
||||
* @param source object to copy
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_odb_object_dup(git_odb_object **dest, git_odb_object *source);
|
||||
|
||||
/**
|
||||
* Close an ODB object
|
||||
*
|
||||
|
||||
@ -158,7 +158,7 @@ GIT_EXTERN(uint32_t) git_packbuilder_object_count(git_packbuilder *pb);
|
||||
GIT_EXTERN(uint32_t) git_packbuilder_written(git_packbuilder *pb);
|
||||
|
||||
/** Packbuilder progress notification function */
|
||||
typedef void (*git_packbuilder_progress)(
|
||||
typedef int (*git_packbuilder_progress)(
|
||||
int stage,
|
||||
unsigned int current,
|
||||
unsigned int total,
|
||||
|
||||
250
include/git2/patch.h
Normal file
250
include/git2/patch.h
Normal file
@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright (C) the libgit2 contributors. All rights reserved.
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
#ifndef INCLUDE_git_patch_h__
|
||||
#define INCLUDE_git_patch_h__
|
||||
|
||||
#include "common.h"
|
||||
#include "types.h"
|
||||
#include "oid.h"
|
||||
#include "diff.h"
|
||||
|
||||
/**
|
||||
* @file git2/patch.h
|
||||
* @brief Patch handling routines.
|
||||
* @ingroup Git
|
||||
* @{
|
||||
*/
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
/**
|
||||
* The diff patch is used to store all the text diffs for a delta.
|
||||
*
|
||||
* You can easily loop over the content of patches and get information about
|
||||
* them.
|
||||
*/
|
||||
typedef struct git_patch git_patch;
|
||||
|
||||
/**
|
||||
* Return the diff delta and patch for an entry in the diff list.
|
||||
*
|
||||
* The `git_patch` is a newly created object contains the text diffs
|
||||
* for the delta. You have to call `git_patch_free()` when you are
|
||||
* done with it. You can use the patch object to loop over all the hunks
|
||||
* and lines in the diff of the one delta.
|
||||
*
|
||||
* For an unchanged file or a binary file, no `git_patch` will be
|
||||
* created, the output will be set to NULL, and the `binary` flag will be
|
||||
* set true in the `git_diff_delta` structure.
|
||||
*
|
||||
* The `git_diff_delta` pointer points to internal data and you do not have
|
||||
* to release it when you are done with it. It will go away when the
|
||||
* `git_diff` and `git_patch` go away.
|
||||
*
|
||||
* It is okay to pass NULL for either of the output parameters; if you pass
|
||||
* NULL for the `git_patch`, then the text diff will not be calculated.
|
||||
*
|
||||
* @param out Output parameter for the delta patch object
|
||||
* @param diff Diff list object
|
||||
* @param idx Index into diff list
|
||||
* @return 0 on success, other value < 0 on error
|
||||
*/
|
||||
GIT_EXTERN(int) git_patch_from_diff(
|
||||
git_patch **out, git_diff *diff, size_t idx);
|
||||
|
||||
/**
|
||||
* Directly generate a patch from the difference between two blobs.
|
||||
*
|
||||
* This is just like `git_diff_blobs()` except it generates a patch object
|
||||
* for the difference instead of directly making callbacks. You can use the
|
||||
* standard `git_patch` accessor functions to read the patch data, and
|
||||
* you must call `git_patch_free()` on the patch when done.
|
||||
*
|
||||
* @param out The generated patch; NULL on error
|
||||
* @param old_blob Blob for old side of diff, or NULL for empty blob
|
||||
* @param old_as_path Treat old blob as if it had this filename; can be NULL
|
||||
* @param new_blob Blob for new side of diff, or NULL for empty blob
|
||||
* @param new_as_path Treat new blob as if it had this filename; can be NULL
|
||||
* @param opts Options for diff, or NULL for default options
|
||||
* @return 0 on success or error code < 0
|
||||
*/
|
||||
GIT_EXTERN(int) git_patch_from_blobs(
|
||||
git_patch **out,
|
||||
const git_blob *old_blob,
|
||||
const char *old_as_path,
|
||||
const git_blob *new_blob,
|
||||
const char *new_as_path,
|
||||
const git_diff_options *opts);
|
||||
|
||||
/**
|
||||
* Directly generate a patch from the difference between a blob and a buffer.
|
||||
*
|
||||
* This is just like `git_diff_blob_to_buffer()` except it generates a patch
|
||||
* object for the difference instead of directly making callbacks. You can
|
||||
* use the standard `git_patch` accessor functions to read the patch
|
||||
* data, and you must call `git_patch_free()` on the patch when done.
|
||||
*
|
||||
* @param out The generated patch; NULL on error
|
||||
* @param old_blob Blob for old side of diff, or NULL for empty blob
|
||||
* @param old_as_path Treat old blob as if it had this filename; can be NULL
|
||||
* @param buffer Raw data for new side of diff, or NULL for empty
|
||||
* @param buffer_len Length of raw data for new side of diff
|
||||
* @param buffer_as_path Treat buffer as if it had this filename; can be NULL
|
||||
* @param opts Options for diff, or NULL for default options
|
||||
* @return 0 on success or error code < 0
|
||||
*/
|
||||
GIT_EXTERN(int) git_patch_from_blob_and_buffer(
|
||||
git_patch **out,
|
||||
const git_blob *old_blob,
|
||||
const char *old_as_path,
|
||||
const char *buffer,
|
||||
size_t buffer_len,
|
||||
const char *buffer_as_path,
|
||||
const git_diff_options *opts);
|
||||
|
||||
/**
|
||||
* Free a git_patch object.
|
||||
*/
|
||||
GIT_EXTERN(void) git_patch_free(git_patch *patch);
|
||||
|
||||
/**
|
||||
* Get the delta associated with a patch
|
||||
*/
|
||||
GIT_EXTERN(const git_diff_delta *) git_patch_get_delta(git_patch *patch);
|
||||
|
||||
/**
|
||||
* Get the number of hunks in a patch
|
||||
*/
|
||||
GIT_EXTERN(size_t) git_patch_num_hunks(git_patch *patch);
|
||||
|
||||
/**
|
||||
* Get line counts of each type in a patch.
|
||||
*
|
||||
* This helps imitate a diff --numstat type of output. For that purpose,
|
||||
* you only need the `total_additions` and `total_deletions` values, but we
|
||||
* include the `total_context` line count in case you want the total number
|
||||
* of lines of diff output that will be generated.
|
||||
*
|
||||
* All outputs are optional. Pass NULL if you don't need a particular count.
|
||||
*
|
||||
* @param total_context Count of context lines in output, can be NULL.
|
||||
* @param total_additions Count of addition lines in output, can be NULL.
|
||||
* @param total_deletions Count of deletion lines in output, can be NULL.
|
||||
* @param patch The git_patch object
|
||||
* @return 0 on success, <0 on error
|
||||
*/
|
||||
GIT_EXTERN(int) git_patch_line_stats(
|
||||
size_t *total_context,
|
||||
size_t *total_additions,
|
||||
size_t *total_deletions,
|
||||
const git_patch *patch);
|
||||
|
||||
/**
|
||||
* Get the information about a hunk in a patch
|
||||
*
|
||||
* Given a patch and a hunk index into the patch, this returns detailed
|
||||
* information about that hunk. Any of the output pointers can be passed
|
||||
* as NULL if you don't care about that particular piece of information.
|
||||
*
|
||||
* @param out Output pointer to git_diff_hunk of hunk
|
||||
* @param lines_in_hunk Output count of total lines in this hunk
|
||||
* @param patch Input pointer to patch object
|
||||
* @param hunk_idx Input index of hunk to get information about
|
||||
* @return 0 on success, GIT_ENOTFOUND if hunk_idx out of range, <0 on error
|
||||
*/
|
||||
GIT_EXTERN(int) git_patch_get_hunk(
|
||||
const git_diff_hunk **out,
|
||||
size_t *lines_in_hunk,
|
||||
git_patch *patch,
|
||||
size_t hunk_idx);
|
||||
|
||||
/**
|
||||
* Get the number of lines in a hunk.
|
||||
*
|
||||
* @param patch The git_patch object
|
||||
* @param hunk_idx Index of the hunk
|
||||
* @return Number of lines in hunk or -1 if invalid hunk index
|
||||
*/
|
||||
GIT_EXTERN(int) git_patch_num_lines_in_hunk(
|
||||
git_patch *patch,
|
||||
size_t hunk_idx);
|
||||
|
||||
/**
|
||||
* Get data about a line in a hunk of a patch.
|
||||
*
|
||||
* Given a patch, a hunk index, and a line index in the hunk, this
|
||||
* will return a lot of details about that line. If you pass a hunk
|
||||
* index larger than the number of hunks or a line index larger than
|
||||
* the number of lines in the hunk, this will return -1.
|
||||
*
|
||||
* @param out The git_diff_line data for this line
|
||||
* @param patch The patch to look in
|
||||
* @param hunk_idx The index of the hunk
|
||||
* @param line_of_hunk The index of the line in the hunk
|
||||
* @return 0 on success, <0 on failure
|
||||
*/
|
||||
GIT_EXTERN(int) git_patch_get_line_in_hunk(
|
||||
const git_diff_line **out,
|
||||
git_patch *patch,
|
||||
size_t hunk_idx,
|
||||
size_t line_of_hunk);
|
||||
|
||||
/**
|
||||
* Look up size of patch diff data in bytes
|
||||
*
|
||||
* This returns the raw size of the patch data. This only includes the
|
||||
* actual data from the lines of the diff, not the file or hunk headers.
|
||||
*
|
||||
* If you pass `include_context` as true (non-zero), this will be the size
|
||||
* of all of the diff output; if you pass it as false (zero), this will
|
||||
* only include the actual changed lines (as if `context_lines` was 0).
|
||||
*
|
||||
* @param patch A git_patch representing changes to one file
|
||||
* @param include_context Include context lines in size if non-zero
|
||||
* @param include_hunk_headers Include hunk header lines if non-zero
|
||||
* @param include_file_headers Include file header lines if non-zero
|
||||
* @return The number of bytes of data
|
||||
*/
|
||||
GIT_EXTERN(size_t) git_patch_size(
|
||||
git_patch *patch,
|
||||
int include_context,
|
||||
int include_hunk_headers,
|
||||
int include_file_headers);
|
||||
|
||||
/**
|
||||
* Serialize the patch to text via callback.
|
||||
*
|
||||
* Returning a non-zero value from the callback will terminate the iteration
|
||||
* and cause this return `GIT_EUSER`.
|
||||
*
|
||||
* @param patch A git_patch representing changes to one file
|
||||
* @param print_cb Callback function to output lines of the patch. Will be
|
||||
* called for file headers, hunk headers, and diff lines.
|
||||
* @param payload Reference pointer that will be passed to your callbacks.
|
||||
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_patch_print(
|
||||
git_patch *patch,
|
||||
git_diff_line_cb print_cb,
|
||||
void *payload);
|
||||
|
||||
/**
|
||||
* Get the content of a patch as a single diff text.
|
||||
*
|
||||
* @param string Allocated string; caller must free.
|
||||
* @param patch A git_patch representing changes to one file
|
||||
* @return 0 on success, <0 on failure.
|
||||
*/
|
||||
GIT_EXTERN(int) git_patch_to_str(
|
||||
char **string,
|
||||
git_patch *patch);
|
||||
|
||||
|
||||
GIT_END_DECL
|
||||
|
||||
/**@}*/
|
||||
|
||||
#endif
|
||||
@ -187,7 +187,7 @@ GIT_EXTERN(int) git_pathspec_match_tree(
|
||||
*/
|
||||
GIT_EXTERN(int) git_pathspec_match_diff(
|
||||
git_pathspec_match_list **out,
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
uint32_t flags,
|
||||
git_pathspec *ps);
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@ typedef struct {
|
||||
#define GIT_PUSH_OPTIONS_INIT { GIT_PUSH_OPTIONS_VERSION }
|
||||
|
||||
/** Push network progress notification function */
|
||||
typedef void (*git_push_transfer_progress)(
|
||||
typedef int (*git_push_transfer_progress)(
|
||||
unsigned int current,
|
||||
unsigned int total,
|
||||
size_t bytes,
|
||||
|
||||
@ -31,10 +31,11 @@ GIT_BEGIN_DECL
|
||||
* git_reflog_free().
|
||||
*
|
||||
* @param out pointer to reflog
|
||||
* @param ref reference to read the reflog for
|
||||
* @param repo the repostiory
|
||||
* @param name reference to look up
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reflog_read(git_reflog **out, const git_reference *ref);
|
||||
GIT_EXTERN(int) git_reflog_read(git_reflog **out, git_repository *repo, const char *name);
|
||||
|
||||
/**
|
||||
* Write an existing in-memory reflog object back to disk
|
||||
@ -59,26 +60,45 @@ GIT_EXTERN(int) git_reflog_write(git_reflog *reflog);
|
||||
GIT_EXTERN(int) git_reflog_append(git_reflog *reflog, const git_oid *id, const git_signature *committer, const char *msg);
|
||||
|
||||
/**
|
||||
* Rename the reflog for the given reference
|
||||
* Add a new entry to the named reflog.
|
||||
*
|
||||
* This utility function loads the named reflog, appends to it and
|
||||
* writes it back out to the backend.
|
||||
*
|
||||
* `msg` is optional and can be NULL.
|
||||
*
|
||||
* @param repo the repository to act on
|
||||
* @param name the reflog's name
|
||||
* @param id the OID the reference is now pointing to
|
||||
* @param committer the signature of the committer
|
||||
* @param msg the reflog message
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reflog_append_to(git_repository *repo, const char *name, const git_oid *id, const git_signature *committer, const char *msg);
|
||||
|
||||
/**
|
||||
* Rename a reflog
|
||||
*
|
||||
* The reflog to be renamed is expected to already exist
|
||||
*
|
||||
* The new name will be checked for validity.
|
||||
* See `git_reference_create_symbolic()` for rules about valid names.
|
||||
*
|
||||
* @param ref the reference
|
||||
* @param name the new name of the reference
|
||||
* @param repo the repository
|
||||
* @param old_name the old name of the reference
|
||||
* @param new_name the new name of the reference
|
||||
* @return 0 on success, GIT_EINVALIDSPEC or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reflog_rename(git_reference *ref, const char *name);
|
||||
GIT_EXTERN(int) git_reflog_rename(git_repository *repo, const char *old_name, const char *name);
|
||||
|
||||
/**
|
||||
* Delete the reflog for the given reference
|
||||
*
|
||||
* @param ref the reference
|
||||
* @param repo the repository
|
||||
* @param name the reflog to delete
|
||||
* @return 0 or an error code
|
||||
*/
|
||||
GIT_EXTERN(int) git_reflog_delete(git_reference *ref);
|
||||
GIT_EXTERN(int) git_reflog_delete(git_repository *repo, const char *name);
|
||||
|
||||
/**
|
||||
* Get the number of log entries in a reflog
|
||||
|
||||
@ -453,7 +453,7 @@ GIT_EXTERN(int) git_reference_is_remote(git_reference *ref);
|
||||
GIT_EXTERN(int) git_reference_is_tag(git_reference *ref);
|
||||
|
||||
typedef enum {
|
||||
GIT_REF_FORMAT_NORMAL = 0,
|
||||
GIT_REF_FORMAT_NORMAL = 0u,
|
||||
|
||||
/**
|
||||
* Control whether one-level refnames are accepted
|
||||
@ -461,7 +461,7 @@ typedef enum {
|
||||
* components). Those are expected to be written only using
|
||||
* uppercase letters and underscore (FETCH_HEAD, ...)
|
||||
*/
|
||||
GIT_REF_FORMAT_ALLOW_ONELEVEL = (1 << 0),
|
||||
GIT_REF_FORMAT_ALLOW_ONELEVEL = (1u << 0),
|
||||
|
||||
/**
|
||||
* Interpret the provided name as a reference pattern for a
|
||||
@ -470,14 +470,14 @@ typedef enum {
|
||||
* in place of a one full pathname component
|
||||
* (e.g., foo/<star>/bar but not foo/bar<star>).
|
||||
*/
|
||||
GIT_REF_FORMAT_REFSPEC_PATTERN = (1 << 1),
|
||||
GIT_REF_FORMAT_REFSPEC_PATTERN = (1u << 1),
|
||||
|
||||
/**
|
||||
* Interpret the name as part of a refspec in shorthand form
|
||||
* so the `ONELEVEL` naming rules aren't enforced and 'master'
|
||||
* becomes a valid name.
|
||||
*/
|
||||
GIT_REF_FORMAT_REFSPEC_SHORTHAND = (1 << 2),
|
||||
GIT_REF_FORMAT_REFSPEC_SHORTHAND = (1u << 2),
|
||||
} git_reference_normalize_t;
|
||||
|
||||
/**
|
||||
|
||||
@ -410,7 +410,7 @@ struct git_remote_callbacks {
|
||||
* progress side-band will be passed to this function (this is
|
||||
* the 'counting objects' output.
|
||||
*/
|
||||
void (*progress)(const char *str, int len, void *data);
|
||||
int (*progress)(const char *str, int len, void *data);
|
||||
|
||||
/**
|
||||
* Completion is called when different parts of the download
|
||||
|
||||
@ -80,7 +80,7 @@ struct git_odb_backend {
|
||||
git_odb_backend *, git_odb_foreach_cb cb, void *payload);
|
||||
|
||||
int (* writepack)(
|
||||
git_odb_writepack **, git_odb_backend *,
|
||||
git_odb_writepack **, git_odb_backend *, git_odb *odb,
|
||||
git_transfer_progress_callback progress_cb, void *progress_payload);
|
||||
|
||||
void (* free)(git_odb_backend *);
|
||||
|
||||
@ -119,6 +119,26 @@ struct git_refdb_backend {
|
||||
* provide this function; if it is not provided, nothing will be done.
|
||||
*/
|
||||
void (*free)(git_refdb_backend *backend);
|
||||
|
||||
/**
|
||||
* Read the reflog for the given reference name.
|
||||
*/
|
||||
int (*reflog_read)(git_reflog **out, git_refdb_backend *backend, const char *name);
|
||||
|
||||
/**
|
||||
* Write a reflog to disk.
|
||||
*/
|
||||
int (*reflog_write)(git_refdb_backend *backend, git_reflog *reflog);
|
||||
|
||||
/**
|
||||
* Rename a reflog
|
||||
*/
|
||||
int (*reflog_rename)(git_refdb_backend *_backend, const char *old_name, const char *new_name);
|
||||
|
||||
/**
|
||||
* Remove a reflog.
|
||||
*/
|
||||
int (*reflog_delete)(git_refdb_backend *backend, const char *name);
|
||||
};
|
||||
|
||||
#define GIT_REFDB_BACKEND_VERSION 1
|
||||
|
||||
21
include/git2/sys/reflog.h
Normal file
21
include/git2/sys/reflog.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (C) the libgit2 contributors. All rights reserved.
|
||||
*
|
||||
* This file is part of libgit2, distributed under the GNU GPL v2 with
|
||||
* a Linking Exception. For full terms see the included COPYING file.
|
||||
*/
|
||||
#ifndef INCLUDE_sys_git_reflog_h__
|
||||
#define INCLUDE_sys_git_reflog_h__
|
||||
|
||||
#include "git2/common.h"
|
||||
#include "git2/types.h"
|
||||
#include "git2/oid.h"
|
||||
|
||||
GIT_BEGIN_DECL
|
||||
|
||||
GIT_EXTERN(git_reflog_entry *) git_reflog_entry__alloc(void);
|
||||
GIT_EXTERN(void) git_reflog_entry__free(git_reflog_entry *entry);
|
||||
|
||||
GIT_END_DECL
|
||||
|
||||
#endif
|
||||
@ -27,7 +27,6 @@ GIT_BEGIN_DECL
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_new(git_repository **out);
|
||||
|
||||
|
||||
/**
|
||||
* Reset all the internal state in a repository.
|
||||
*
|
||||
@ -41,6 +40,25 @@ GIT_EXTERN(int) git_repository_new(git_repository **out);
|
||||
*/
|
||||
GIT_EXTERN(void) git_repository__cleanup(git_repository *repo);
|
||||
|
||||
/**
|
||||
* Update the filesystem config settings for an open repository
|
||||
*
|
||||
* When a repository is initialized, config values are set based on the
|
||||
* properties of the filesystem that the repository is on, such as
|
||||
* "core.ignorecase", "core.filemode", "core.symlinks", etc. If the
|
||||
* repository is moved to a new filesystem, these properties may no
|
||||
* longer be correct and API calls may not behave as expected. This
|
||||
* call reruns the phase of repository initialization that sets those
|
||||
* properties to compensate for the current filesystem of the repo.
|
||||
*
|
||||
* @param repo A repository object
|
||||
* @param recurse_submodules Should submodules be updated recursively
|
||||
* @returrn 0 on success, < 0 on error
|
||||
*/
|
||||
GIT_EXTERN(int) git_repository_reinit_filesystem(
|
||||
git_repository *repo,
|
||||
int recurse_submodules);
|
||||
|
||||
/**
|
||||
* Set the configuration file for this repository
|
||||
*
|
||||
|
||||
@ -28,11 +28,16 @@ GIT_BEGIN_DECL
|
||||
*** Begin interface for credentials acquisition ***
|
||||
*/
|
||||
|
||||
/** Authentication type requested */
|
||||
typedef enum {
|
||||
/* git_cred_userpass_plaintext */
|
||||
GIT_CREDTYPE_USERPASS_PLAINTEXT = 1,
|
||||
GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE = 2,
|
||||
GIT_CREDTYPE_SSH_PUBLICKEY = 3,
|
||||
GIT_CREDTYPE_USERPASS_PLAINTEXT = (1u << 0),
|
||||
|
||||
/* git_cred_ssh_key */
|
||||
GIT_CREDTYPE_SSH_KEY = (1u << 1),
|
||||
|
||||
/* git_cred_ssh_custom */
|
||||
GIT_CREDTYPE_SSH_CUSTOM = (1u << 2),
|
||||
} git_credtype_t;
|
||||
|
||||
/* The base structure for all credential types */
|
||||
@ -56,24 +61,28 @@ typedef LIBSSH2_USERAUTH_PUBLICKEY_SIGN_FUNC((*git_cred_sign_callback));
|
||||
typedef int (*git_cred_sign_callback)(void *, ...);
|
||||
#endif
|
||||
|
||||
/* A ssh key file and passphrase */
|
||||
typedef struct git_cred_ssh_keyfile_passphrase {
|
||||
/**
|
||||
* A ssh key from disk
|
||||
*/
|
||||
typedef struct git_cred_ssh_key {
|
||||
git_cred parent;
|
||||
char *username;
|
||||
char *publickey;
|
||||
char *privatekey;
|
||||
char *passphrase;
|
||||
} git_cred_ssh_keyfile_passphrase;
|
||||
} git_cred_ssh_key;
|
||||
|
||||
/* A ssh public key and authentication callback */
|
||||
typedef struct git_cred_ssh_publickey {
|
||||
/**
|
||||
* A key with a custom signature function
|
||||
*/
|
||||
typedef struct git_cred_ssh_custom {
|
||||
git_cred parent;
|
||||
char *username;
|
||||
char *publickey;
|
||||
size_t publickey_len;
|
||||
void *sign_callback;
|
||||
void *sign_data;
|
||||
} git_cred_ssh_publickey;
|
||||
} git_cred_ssh_custom;
|
||||
|
||||
/**
|
||||
* Check whether a credential object contains username information.
|
||||
@ -84,7 +93,7 @@ typedef struct git_cred_ssh_publickey {
|
||||
GIT_EXTERN(int) git_cred_has_username(git_cred *cred);
|
||||
|
||||
/**
|
||||
* Creates a new plain-text username and password credential object.
|
||||
* Create a new plain-text username and password credential object.
|
||||
* The supplied credential parameter will be internally duplicated.
|
||||
*
|
||||
* @param out The newly created credential object.
|
||||
@ -98,7 +107,7 @@ GIT_EXTERN(int) git_cred_userpass_plaintext_new(
|
||||
const char *password);
|
||||
|
||||
/**
|
||||
* Creates a new ssh key file and passphrase credential object.
|
||||
* Create a new passphrase-protected ssh key credential object.
|
||||
* The supplied credential parameter will be internally duplicated.
|
||||
*
|
||||
* @param out The newly created credential object.
|
||||
@ -108,32 +117,38 @@ GIT_EXTERN(int) git_cred_userpass_plaintext_new(
|
||||
* @param passphrase The passphrase of the credential.
|
||||
* @return 0 for success or an error code for failure
|
||||
*/
|
||||
GIT_EXTERN(int) git_cred_ssh_keyfile_passphrase_new(
|
||||
GIT_EXTERN(int) git_cred_ssh_key_new(
|
||||
git_cred **out,
|
||||
const char *username,
|
||||
const char *publickey,
|
||||
const char *privatekey,
|
||||
const char *passphrase);
|
||||
const char *passphrase);
|
||||
|
||||
/**
|
||||
* Creates a new ssh public key credential object.
|
||||
* Create an ssh key credential with a custom signing function.
|
||||
*
|
||||
* This lets you use your own function to sign the challenge.
|
||||
*
|
||||
* This function and its credential type is provided for completeness
|
||||
* and wraps `libssh2_userauth_publickey()`, which is undocumented.
|
||||
*
|
||||
* The supplied credential parameter will be internally duplicated.
|
||||
*
|
||||
* @param out The newly created credential object.
|
||||
* @param username username to use to authenticate
|
||||
* @param publickey The bytes of the public key.
|
||||
* @param publickey_len The length of the public key in bytes.
|
||||
* @param sign_fn The callback method for authenticating.
|
||||
* @param sign_data The abstract data sent to the sign_callback method.
|
||||
* @param sign_fn The callback method to sign the data during the challenge.
|
||||
* @param sign_data The data to pass to the sign function.
|
||||
* @return 0 for success or an error code for failure
|
||||
*/
|
||||
GIT_EXTERN(int) git_cred_ssh_publickey_new(
|
||||
GIT_EXTERN(int) git_cred_ssh_custom_new(
|
||||
git_cred **out,
|
||||
const char *username,
|
||||
const char *publickey,
|
||||
size_t publickey_len,
|
||||
git_cred_sign_callback sign_fn,
|
||||
void *sign_data);
|
||||
size_t publickey_len,
|
||||
git_cred_sign_callback sign_fn,
|
||||
void *sign_data);
|
||||
|
||||
/**
|
||||
* Signature of a function which acquires a credential object.
|
||||
@ -165,7 +180,7 @@ typedef enum {
|
||||
GIT_TRANSPORTFLAGS_NO_CHECK_CERT = 1
|
||||
} git_transport_flags_t;
|
||||
|
||||
typedef void (*git_transport_message_cb)(const char *str, int len, void *data);
|
||||
typedef int (*git_transport_message_cb)(const char *str, int len, void *data);
|
||||
|
||||
typedef struct git_transport git_transport;
|
||||
|
||||
|
||||
@ -198,6 +198,17 @@ GIT_EXTERN(git_otype) git_tree_entry_type(const git_tree_entry *entry);
|
||||
*/
|
||||
GIT_EXTERN(git_filemode_t) git_tree_entry_filemode(const git_tree_entry *entry);
|
||||
|
||||
/**
|
||||
* Get the raw UNIX file attributes of a tree entry
|
||||
*
|
||||
* This function does not perform any normalization and is only useful
|
||||
* if you need to be able to recreate the original tree object.
|
||||
*
|
||||
* @param entry a tree entry
|
||||
* @return filemode as an integer
|
||||
*/
|
||||
|
||||
GIT_EXTERN(git_filemode_t) git_tree_entry_filemode_raw(const git_tree_entry *entry);
|
||||
/**
|
||||
* Compare two tree entries
|
||||
*
|
||||
|
||||
@ -212,11 +212,21 @@ typedef struct git_remote_callbacks git_remote_callbacks;
|
||||
/**
|
||||
* This is passed as the first argument to the callback to allow the
|
||||
* user to see the progress.
|
||||
*
|
||||
* - total_objects: number of objects in the packfile being downloaded
|
||||
* - indexed_objects: received objects that have been hashed
|
||||
* - received_objects: objects which have been downloaded
|
||||
* - local_objects: locally-available objects that have been injected
|
||||
* in order to fix a thin pack.
|
||||
* - received-bytes: size of the packfile received up to now
|
||||
*/
|
||||
typedef struct git_transfer_progress {
|
||||
unsigned int total_objects;
|
||||
unsigned int indexed_objects;
|
||||
unsigned int received_objects;
|
||||
unsigned int local_objects;
|
||||
unsigned int total_deltas;
|
||||
unsigned int indexed_deltas;
|
||||
size_t received_bytes;
|
||||
} git_transfer_progress;
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ includedir=@CMAKE_INSTALL_PREFIX@/@INCLUDE_INSTALL_DIR@
|
||||
Name: libgit2
|
||||
Description: The git library, take 2
|
||||
Version: @LIBGIT2_VERSION_STRING@
|
||||
Requires: libcrypto
|
||||
Libs: -L${libdir} -lgit2 -lz -lcrypto
|
||||
Requires.private: @LIBGIT2_PC_REQUIRES@
|
||||
Libs.private: @LIBGIT2_PC_LIBS@
|
||||
Libs: -L${libdir} -lgit2
|
||||
Cflags: -I${includedir}
|
||||
|
||||
@ -1,6 +0,0 @@
|
||||
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
|
||||
@ -1,106 +0,0 @@
|
||||
#
|
||||
# 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} \
|
||||
-DLIB_INSTALL_DIR:PATH=%{_libdir}S
|
||||
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
|
||||
@ -1,3 +1,4 @@
|
||||
#include "common.h"
|
||||
#include "repository.h"
|
||||
#include "fileops.h"
|
||||
#include "config.h"
|
||||
|
||||
741
src/checkout.c
741
src/checkout.c
@ -26,6 +26,8 @@
|
||||
#include "diff.h"
|
||||
#include "pathspec.h"
|
||||
#include "buf_text.h"
|
||||
#include "merge_file.h"
|
||||
#include "path.h"
|
||||
|
||||
/* See docs/checkout-internals.md for more information */
|
||||
|
||||
@ -35,21 +37,23 @@ enum {
|
||||
CHECKOUT_ACTION__UPDATE_BLOB = 2,
|
||||
CHECKOUT_ACTION__UPDATE_SUBMODULE = 4,
|
||||
CHECKOUT_ACTION__CONFLICT = 8,
|
||||
CHECKOUT_ACTION__MAX = 8,
|
||||
CHECKOUT_ACTION__DEFER_REMOVE = 16,
|
||||
CHECKOUT_ACTION__UPDATE_CONFLICT = 16,
|
||||
CHECKOUT_ACTION__MAX = 16,
|
||||
CHECKOUT_ACTION__DEFER_REMOVE = 32,
|
||||
CHECKOUT_ACTION__REMOVE_AND_UPDATE =
|
||||
(CHECKOUT_ACTION__UPDATE_BLOB | CHECKOUT_ACTION__REMOVE),
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
git_repository *repo;
|
||||
git_diff_list *diff;
|
||||
git_diff *diff;
|
||||
git_checkout_opts opts;
|
||||
bool opts_free_baseline;
|
||||
char *pfx;
|
||||
git_index *index;
|
||||
git_pool pool;
|
||||
git_vector removes;
|
||||
git_vector conflicts;
|
||||
git_buf path;
|
||||
size_t workdir_len;
|
||||
unsigned int strategy;
|
||||
@ -59,6 +63,16 @@ typedef struct {
|
||||
size_t completed_steps;
|
||||
} checkout_data;
|
||||
|
||||
typedef struct {
|
||||
const git_index_entry *ancestor;
|
||||
const git_index_entry *ours;
|
||||
const git_index_entry *theirs;
|
||||
|
||||
int name_collision:1,
|
||||
directoryfile:1,
|
||||
one_to_two:1;
|
||||
} checkout_conflictdata;
|
||||
|
||||
static int checkout_notify(
|
||||
checkout_data *data,
|
||||
git_checkout_notify_t why,
|
||||
@ -592,6 +606,359 @@ static int checkout_remaining_wd_items(
|
||||
return error;
|
||||
}
|
||||
|
||||
GIT_INLINE(int) checkout_idxentry_cmp(
|
||||
const git_index_entry *a,
|
||||
const git_index_entry *b)
|
||||
{
|
||||
if (!a && !b)
|
||||
return 0;
|
||||
else if (!a && b)
|
||||
return -1;
|
||||
else if(a && !b)
|
||||
return 1;
|
||||
else
|
||||
return strcmp(a->path, b->path);
|
||||
}
|
||||
|
||||
static int checkout_conflictdata_cmp(const void *a, const void *b)
|
||||
{
|
||||
const checkout_conflictdata *ca = a;
|
||||
const checkout_conflictdata *cb = b;
|
||||
int diff;
|
||||
|
||||
if ((diff = checkout_idxentry_cmp(ca->ancestor, cb->ancestor)) == 0 &&
|
||||
(diff = checkout_idxentry_cmp(ca->ours, cb->theirs)) == 0)
|
||||
diff = checkout_idxentry_cmp(ca->theirs, cb->theirs);
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
int checkout_conflictdata_empty(const git_vector *conflicts, size_t idx)
|
||||
{
|
||||
checkout_conflictdata *conflict;
|
||||
|
||||
if ((conflict = git_vector_get(conflicts, idx)) == NULL)
|
||||
return -1;
|
||||
|
||||
if (conflict->ancestor || conflict->ours || conflict->theirs)
|
||||
return 0;
|
||||
|
||||
git__free(conflict);
|
||||
return 1;
|
||||
}
|
||||
|
||||
GIT_INLINE(bool) conflict_pathspec_match(
|
||||
checkout_data *data,
|
||||
git_iterator *workdir,
|
||||
git_vector *pathspec,
|
||||
const git_index_entry *ancestor,
|
||||
const git_index_entry *ours,
|
||||
const git_index_entry *theirs)
|
||||
{
|
||||
/* if the pathspec matches ours *or* theirs, proceed */
|
||||
if (ours && git_pathspec__match(pathspec, ours->path,
|
||||
(data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
|
||||
git_iterator_ignore_case(workdir), NULL, NULL))
|
||||
return true;
|
||||
|
||||
if (theirs && git_pathspec__match(pathspec, theirs->path,
|
||||
(data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
|
||||
git_iterator_ignore_case(workdir), NULL, NULL))
|
||||
return true;
|
||||
|
||||
if (ancestor && git_pathspec__match(pathspec, ancestor->path,
|
||||
(data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
|
||||
git_iterator_ignore_case(workdir), NULL, NULL))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, git_vector *pathspec)
|
||||
{
|
||||
git_index_conflict_iterator *iterator = NULL;
|
||||
const git_index_entry *ancestor, *ours, *theirs;
|
||||
checkout_conflictdata *conflict;
|
||||
int error = 0;
|
||||
|
||||
if ((error = git_index_conflict_iterator_new(&iterator, data->index)) < 0)
|
||||
goto done;
|
||||
|
||||
data->conflicts._cmp = checkout_conflictdata_cmp;
|
||||
|
||||
/* Collect the conflicts */
|
||||
while ((error = git_index_conflict_next(&ancestor, &ours, &theirs, iterator)) == 0) {
|
||||
if (!conflict_pathspec_match(data, workdir, pathspec, ancestor, ours, theirs))
|
||||
continue;
|
||||
|
||||
conflict = git__calloc(1, sizeof(checkout_conflictdata));
|
||||
GITERR_CHECK_ALLOC(conflict);
|
||||
|
||||
conflict->ancestor = ancestor;
|
||||
conflict->ours = ours;
|
||||
conflict->theirs = theirs;
|
||||
|
||||
git_vector_insert(&data->conflicts, conflict);
|
||||
}
|
||||
|
||||
if (error == GIT_ITEROVER)
|
||||
error = 0;
|
||||
|
||||
done:
|
||||
git_index_conflict_iterator_free(iterator);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
GIT_INLINE(int) checkout_conflicts_cmp_entry(
|
||||
const char *path,
|
||||
const git_index_entry *entry)
|
||||
{
|
||||
return strcmp((const char *)path, entry->path);
|
||||
}
|
||||
|
||||
static int checkout_conflicts_cmp_ancestor(const void *p, const void *c)
|
||||
{
|
||||
const char *path = p;
|
||||
const checkout_conflictdata *conflict = c;
|
||||
|
||||
if (!conflict->ancestor)
|
||||
return 1;
|
||||
|
||||
return checkout_conflicts_cmp_entry(path, conflict->ancestor);
|
||||
}
|
||||
|
||||
static checkout_conflictdata *checkout_conflicts_search_ancestor(
|
||||
checkout_data *data,
|
||||
const char *path)
|
||||
{
|
||||
size_t pos;
|
||||
|
||||
if (git_vector_bsearch2(&pos, &data->conflicts, checkout_conflicts_cmp_ancestor, path) < 0)
|
||||
return NULL;
|
||||
|
||||
return git_vector_get(&data->conflicts, pos);
|
||||
}
|
||||
|
||||
static checkout_conflictdata *checkout_conflicts_search_branch(
|
||||
checkout_data *data,
|
||||
const char *path)
|
||||
{
|
||||
checkout_conflictdata *conflict;
|
||||
size_t i;
|
||||
|
||||
git_vector_foreach(&data->conflicts, i, conflict) {
|
||||
int cmp = -1;
|
||||
|
||||
if (conflict->ancestor)
|
||||
break;
|
||||
|
||||
if (conflict->ours)
|
||||
cmp = checkout_conflicts_cmp_entry(path, conflict->ours);
|
||||
else if (conflict->theirs)
|
||||
cmp = checkout_conflicts_cmp_entry(path, conflict->theirs);
|
||||
|
||||
if (cmp == 0)
|
||||
return conflict;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int checkout_conflicts_load_byname_entry(
|
||||
checkout_conflictdata **ancestor_out,
|
||||
checkout_conflictdata **ours_out,
|
||||
checkout_conflictdata **theirs_out,
|
||||
checkout_data *data,
|
||||
const git_index_name_entry *name_entry)
|
||||
{
|
||||
checkout_conflictdata *ancestor, *ours = NULL, *theirs = NULL;
|
||||
int error = 0;
|
||||
|
||||
*ancestor_out = NULL;
|
||||
*ours_out = NULL;
|
||||
*theirs_out = NULL;
|
||||
|
||||
if (!name_entry->ancestor) {
|
||||
giterr_set(GITERR_INDEX, "A NAME entry exists without an ancestor");
|
||||
error = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!name_entry->ours && !name_entry->theirs) {
|
||||
giterr_set(GITERR_INDEX, "A NAME entry exists without an ours or theirs");
|
||||
error = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((ancestor = checkout_conflicts_search_ancestor(data,
|
||||
name_entry->ancestor)) == NULL) {
|
||||
giterr_set(GITERR_INDEX,
|
||||
"A NAME entry referenced ancestor entry '%s' which does not exist in the main index",
|
||||
name_entry->ancestor);
|
||||
error = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (name_entry->ours) {
|
||||
if (strcmp(name_entry->ancestor, name_entry->ours) == 0)
|
||||
ours = ancestor;
|
||||
else if ((ours = checkout_conflicts_search_branch(data, name_entry->ours)) == NULL ||
|
||||
ours->ours == NULL) {
|
||||
giterr_set(GITERR_INDEX,
|
||||
"A NAME entry referenced our entry '%s' which does not exist in the main index",
|
||||
name_entry->ours);
|
||||
error = -1;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (name_entry->theirs) {
|
||||
if (strcmp(name_entry->ancestor, name_entry->theirs) == 0)
|
||||
theirs = ancestor;
|
||||
else if (name_entry->ours && strcmp(name_entry->ours, name_entry->theirs) == 0)
|
||||
theirs = ours;
|
||||
else if ((theirs = checkout_conflicts_search_branch(data, name_entry->theirs)) == NULL ||
|
||||
theirs->theirs == NULL) {
|
||||
giterr_set(GITERR_INDEX,
|
||||
"A NAME entry referenced their entry '%s' which does not exist in the main index",
|
||||
name_entry->theirs);
|
||||
error = -1;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
*ancestor_out = ancestor;
|
||||
*ours_out = ours;
|
||||
*theirs_out = theirs;
|
||||
|
||||
done:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int checkout_conflicts_coalesce_renames(
|
||||
checkout_data *data)
|
||||
{
|
||||
const git_index_name_entry *name_entry;
|
||||
checkout_conflictdata *ancestor_conflict, *our_conflict, *their_conflict;
|
||||
size_t i, names;
|
||||
int error = 0;
|
||||
|
||||
/* Juggle entries based on renames */
|
||||
names = git_index_name_entrycount(data->index);
|
||||
|
||||
for (i = 0; i < names; i++) {
|
||||
name_entry = git_index_name_get_byindex(data->index, i);
|
||||
|
||||
if ((error = checkout_conflicts_load_byname_entry(
|
||||
&ancestor_conflict, &our_conflict, &their_conflict,
|
||||
data, name_entry)) < 0)
|
||||
goto done;
|
||||
|
||||
if (our_conflict && our_conflict != ancestor_conflict) {
|
||||
ancestor_conflict->ours = our_conflict->ours;
|
||||
our_conflict->ours = NULL;
|
||||
|
||||
if (our_conflict->theirs)
|
||||
our_conflict->name_collision = 1;
|
||||
|
||||
if (our_conflict->name_collision)
|
||||
ancestor_conflict->name_collision = 1;
|
||||
}
|
||||
|
||||
if (their_conflict && their_conflict != ancestor_conflict) {
|
||||
ancestor_conflict->theirs = their_conflict->theirs;
|
||||
their_conflict->theirs = NULL;
|
||||
|
||||
if (their_conflict->ours)
|
||||
their_conflict->name_collision = 1;
|
||||
|
||||
if (their_conflict->name_collision)
|
||||
ancestor_conflict->name_collision = 1;
|
||||
}
|
||||
|
||||
if (our_conflict && our_conflict != ancestor_conflict &&
|
||||
their_conflict && their_conflict != ancestor_conflict)
|
||||
ancestor_conflict->one_to_two = 1;
|
||||
}
|
||||
|
||||
git_vector_remove_matching(&data->conflicts, checkout_conflictdata_empty);
|
||||
|
||||
done:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int checkout_conflicts_mark_directoryfile(
|
||||
checkout_data *data)
|
||||
{
|
||||
checkout_conflictdata *conflict;
|
||||
const git_index_entry *entry;
|
||||
size_t i, j, len;
|
||||
const char *path;
|
||||
int prefixed, error = 0;
|
||||
|
||||
len = git_index_entrycount(data->index);
|
||||
|
||||
/* Find d/f conflicts */
|
||||
git_vector_foreach(&data->conflicts, i, conflict) {
|
||||
if ((conflict->ours && conflict->theirs) ||
|
||||
(!conflict->ours && !conflict->theirs))
|
||||
continue;
|
||||
|
||||
path = conflict->ours ?
|
||||
conflict->ours->path : conflict->theirs->path;
|
||||
|
||||
if ((error = git_index_find(&j, data->index, path)) < 0) {
|
||||
if (error == GIT_ENOTFOUND)
|
||||
giterr_set(GITERR_INDEX,
|
||||
"Index inconsistency, could not find entry for expected conflict '%s'", path);
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
for (; j < len; j++) {
|
||||
if ((entry = git_index_get_byindex(data->index, j)) == NULL) {
|
||||
giterr_set(GITERR_INDEX,
|
||||
"Index inconsistency, truncated index while loading expected conflict '%s'", path);
|
||||
error = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
prefixed = git_path_equal_or_prefixed(path, entry->path);
|
||||
|
||||
if (prefixed == GIT_PATH_EQUAL)
|
||||
continue;
|
||||
|
||||
if (prefixed == GIT_PATH_PREFIX)
|
||||
conflict->directoryfile = 1;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int checkout_get_conflicts(
|
||||
checkout_data *data,
|
||||
git_iterator *workdir,
|
||||
git_vector *pathspec)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (data->strategy & GIT_CHECKOUT_SKIP_UNMERGED)
|
||||
return 0;
|
||||
|
||||
if ((error = checkout_conflicts_load(data, workdir, pathspec)) < 0 ||
|
||||
(error = checkout_conflicts_coalesce_renames(data)) < 0 ||
|
||||
(error = checkout_conflicts_mark_directoryfile(data)) < 0)
|
||||
goto done;
|
||||
|
||||
done:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int checkout_get_actions(
|
||||
uint32_t **actions_ptr,
|
||||
size_t **counts_ptr,
|
||||
@ -659,6 +1026,12 @@ static int checkout_get_actions(
|
||||
goto fail;
|
||||
}
|
||||
|
||||
|
||||
if ((error = checkout_get_conflicts(data, workdir, &pathspec)) < 0)
|
||||
goto fail;
|
||||
|
||||
counts[CHECKOUT_ACTION__UPDATE_CONFLICT] = git_vector_length(&data->conflicts);
|
||||
|
||||
git_pathspec__vfree(&pathspec);
|
||||
git_pool_clear(&pathpool);
|
||||
|
||||
@ -707,6 +1080,7 @@ static int blob_content_to_file(
|
||||
struct stat *st,
|
||||
git_blob *blob,
|
||||
const char *path,
|
||||
const char * hint_path,
|
||||
mode_t entry_filemode,
|
||||
git_checkout_opts *opts)
|
||||
{
|
||||
@ -715,9 +1089,12 @@ static int blob_content_to_file(
|
||||
git_buf out = GIT_BUF_INIT;
|
||||
git_filter_list *fl = NULL;
|
||||
|
||||
if (hint_path == NULL)
|
||||
hint_path = path;
|
||||
|
||||
if (!opts->disable_filters)
|
||||
error = git_filter_list_load(
|
||||
&fl, git_blob_owner(blob), blob, path, GIT_FILTER_TO_WORKTREE);
|
||||
&fl, git_blob_owner(blob), blob, hint_path, GIT_FILTER_TO_WORKTREE);
|
||||
|
||||
if (!error)
|
||||
error = git_filter_list_apply_to_blob(&out, fl, blob);
|
||||
@ -783,7 +1160,8 @@ static int checkout_update_index(
|
||||
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
entry.path = (char *)file->path; /* cast to prevent warning */
|
||||
git_index_entry__init_from_stat(&entry, st);
|
||||
git_index_entry__init_from_stat(
|
||||
&entry, st, !(git_index_caps(data->index) & GIT_INDEXCAP_NO_FILEMODE));
|
||||
git_oid_cpy(&entry.oid, &file->oid);
|
||||
|
||||
return git_index_add(data->index, &entry);
|
||||
@ -885,34 +1263,26 @@ static int checkout_safe_for_update_only(const char *path, mode_t expected_mode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int checkout_blob(
|
||||
static int checkout_write_content(
|
||||
checkout_data *data,
|
||||
const git_diff_file *file)
|
||||
const git_oid *oid,
|
||||
const char *full_path,
|
||||
const char *hint_path,
|
||||
unsigned int mode,
|
||||
struct stat *st)
|
||||
{
|
||||
int error = 0;
|
||||
git_blob *blob;
|
||||
struct stat st;
|
||||
|
||||
git_buf_truncate(&data->path, data->workdir_len);
|
||||
if (git_buf_puts(&data->path, file->path) < 0)
|
||||
return -1;
|
||||
|
||||
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) {
|
||||
int rval = checkout_safe_for_update_only(
|
||||
git_buf_cstr(&data->path), file->mode);
|
||||
if (rval <= 0)
|
||||
return rval;
|
||||
}
|
||||
|
||||
if ((error = git_blob_lookup(&blob, data->repo, &file->oid)) < 0)
|
||||
if ((error = git_blob_lookup(&blob, data->repo, oid)) < 0)
|
||||
return error;
|
||||
|
||||
if (S_ISLNK(file->mode))
|
||||
if (S_ISLNK(mode))
|
||||
error = blob_content_to_link(
|
||||
&st, blob, git_buf_cstr(&data->path), data->opts.dir_mode, data->can_symlink);
|
||||
st, blob, full_path, data->opts.dir_mode, data->can_symlink);
|
||||
else
|
||||
error = blob_content_to_file(
|
||||
&st, blob, git_buf_cstr(&data->path), file->mode, &data->opts);
|
||||
st, blob, full_path, hint_path, mode, &data->opts);
|
||||
|
||||
git_blob_free(blob);
|
||||
|
||||
@ -927,6 +1297,30 @@ static int checkout_blob(
|
||||
error = 0;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int checkout_blob(
|
||||
checkout_data *data,
|
||||
const git_diff_file *file)
|
||||
{
|
||||
int error = 0;
|
||||
struct stat st;
|
||||
|
||||
git_buf_truncate(&data->path, data->workdir_len);
|
||||
if (git_buf_puts(&data->path, file->path) < 0)
|
||||
return -1;
|
||||
|
||||
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) {
|
||||
int rval = checkout_safe_for_update_only(
|
||||
git_buf_cstr(&data->path), file->mode);
|
||||
if (rval <= 0)
|
||||
return rval;
|
||||
}
|
||||
|
||||
error = checkout_write_content(
|
||||
data, &file->oid, git_buf_cstr(&data->path), NULL, file->mode, &st);
|
||||
|
||||
/* update the index unless prevented */
|
||||
if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
|
||||
error = checkout_update_index(data, file, &st);
|
||||
@ -1097,8 +1491,279 @@ static int checkout_lookup_head_tree(git_tree **out, git_repository *repo)
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
static int conflict_entry_name(
|
||||
git_buf *out,
|
||||
const char *side_name,
|
||||
const char *filename)
|
||||
{
|
||||
if (git_buf_puts(out, side_name) < 0 ||
|
||||
git_buf_putc(out, ':') < 0 ||
|
||||
git_buf_puts(out, filename) < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int checkout_path_suffixed(git_buf *path, const char *suffix)
|
||||
{
|
||||
size_t path_len;
|
||||
int i = 0, error = 0;
|
||||
|
||||
if ((error = git_buf_putc(path, '~')) < 0 || (error = git_buf_puts(path, suffix)) < 0)
|
||||
return -1;
|
||||
|
||||
path_len = git_buf_len(path);
|
||||
|
||||
while (git_path_exists(git_buf_cstr(path)) && i < INT_MAX) {
|
||||
git_buf_truncate(path, path_len);
|
||||
|
||||
if ((error = git_buf_putc(path, '_')) < 0 ||
|
||||
(error = git_buf_printf(path, "%d", i)) < 0)
|
||||
return error;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i == INT_MAX) {
|
||||
git_buf_truncate(path, path_len);
|
||||
|
||||
giterr_set(GITERR_CHECKOUT, "Could not write '%s': working directory file exists", path);
|
||||
return GIT_EEXISTS;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int checkout_write_entry(
|
||||
checkout_data *data,
|
||||
checkout_conflictdata *conflict,
|
||||
const git_index_entry *side)
|
||||
{
|
||||
const char *hint_path = NULL, *suffix;
|
||||
struct stat st;
|
||||
int error;
|
||||
|
||||
assert (side == conflict->ours || side == conflict->theirs);
|
||||
|
||||
git_buf_truncate(&data->path, data->workdir_len);
|
||||
if (git_buf_puts(&data->path, side->path) < 0)
|
||||
return -1;
|
||||
|
||||
if ((conflict->name_collision || conflict->directoryfile) &&
|
||||
(data->strategy & GIT_CHECKOUT_USE_OURS) == 0 &&
|
||||
(data->strategy & GIT_CHECKOUT_USE_THEIRS) == 0) {
|
||||
|
||||
if (side == conflict->ours)
|
||||
suffix = data->opts.our_label ? data->opts.our_label :
|
||||
"ours";
|
||||
else
|
||||
suffix = data->opts.their_label ? data->opts.their_label :
|
||||
"theirs";
|
||||
|
||||
if (checkout_path_suffixed(&data->path, suffix) < 0)
|
||||
return -1;
|
||||
|
||||
hint_path = side->path;
|
||||
}
|
||||
|
||||
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
|
||||
(error = checkout_safe_for_update_only(git_buf_cstr(&data->path), side->mode)) <= 0)
|
||||
return error;
|
||||
|
||||
return checkout_write_content(data,
|
||||
&side->oid, git_buf_cstr(&data->path), hint_path, side->mode, &st);
|
||||
}
|
||||
|
||||
static int checkout_write_entries(
|
||||
checkout_data *data,
|
||||
checkout_conflictdata *conflict)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if ((error = checkout_write_entry(data, conflict, conflict->ours)) >= 0)
|
||||
error = checkout_write_entry(data, conflict, conflict->theirs);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int checkout_merge_path(
|
||||
git_buf *out,
|
||||
checkout_data *data,
|
||||
checkout_conflictdata *conflict,
|
||||
git_merge_file_result *result)
|
||||
{
|
||||
const char *our_label_raw, *their_label_raw, *suffix;
|
||||
int error = 0;
|
||||
|
||||
if ((error = git_buf_joinpath(out, git_repository_workdir(data->repo), result->path)) < 0)
|
||||
return error;
|
||||
|
||||
/* Most conflicts simply use the filename in the index */
|
||||
if (!conflict->name_collision)
|
||||
return 0;
|
||||
|
||||
/* Rename 2->1 conflicts need the branch name appended */
|
||||
our_label_raw = data->opts.our_label ? data->opts.our_label : "ours";
|
||||
their_label_raw = data->opts.their_label ? data->opts.their_label : "theirs";
|
||||
suffix = strcmp(result->path, conflict->ours->path) == 0 ? our_label_raw : their_label_raw;
|
||||
|
||||
if ((error = checkout_path_suffixed(out, suffix)) < 0)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int checkout_write_merge(
|
||||
checkout_data *data,
|
||||
checkout_conflictdata *conflict)
|
||||
{
|
||||
git_buf our_label = GIT_BUF_INIT, their_label = GIT_BUF_INIT,
|
||||
path_suffixed = GIT_BUF_INIT, path_workdir = GIT_BUF_INIT;
|
||||
git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
|
||||
ours = GIT_MERGE_FILE_INPUT_INIT,
|
||||
theirs = GIT_MERGE_FILE_INPUT_INIT;
|
||||
git_merge_file_result result = GIT_MERGE_FILE_RESULT_INIT;
|
||||
git_filebuf output = GIT_FILEBUF_INIT;
|
||||
int error = 0;
|
||||
|
||||
if ((conflict->ancestor &&
|
||||
(error = git_merge_file_input_from_index_entry(
|
||||
&ancestor, data->repo, conflict->ancestor)) < 0) ||
|
||||
(error = git_merge_file_input_from_index_entry(
|
||||
&ours, data->repo, conflict->ours)) < 0 ||
|
||||
(error = git_merge_file_input_from_index_entry(
|
||||
&theirs, data->repo, conflict->theirs)) < 0)
|
||||
goto done;
|
||||
|
||||
ancestor.label = NULL;
|
||||
ours.label = data->opts.our_label ? data->opts.our_label : "ours";
|
||||
theirs.label = data->opts.their_label ? data->opts.their_label : "theirs";
|
||||
|
||||
/* If all the paths are identical, decorate the diff3 file with the branch
|
||||
* names. Otherwise, append branch_name:path.
|
||||
*/
|
||||
if (conflict->ours && conflict->theirs &&
|
||||
strcmp(conflict->ours->path, conflict->theirs->path) != 0) {
|
||||
|
||||
if ((error = conflict_entry_name(
|
||||
&our_label, ours.label, conflict->ours->path)) < 0 ||
|
||||
(error = conflict_entry_name(
|
||||
&their_label, theirs.label, conflict->theirs->path)) < 0)
|
||||
goto done;
|
||||
|
||||
ours.label = git_buf_cstr(&our_label);
|
||||
theirs.label = git_buf_cstr(&their_label);
|
||||
}
|
||||
|
||||
if ((error = git_merge_files(&result, &ancestor, &ours, &theirs, 0)) < 0)
|
||||
goto done;
|
||||
|
||||
if (result.path == NULL || result.mode == 0) {
|
||||
giterr_set(GITERR_CHECKOUT, "Could not merge contents of file");
|
||||
error = GIT_EMERGECONFLICT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if ((error = checkout_merge_path(&path_workdir, data, conflict, &result)) < 0)
|
||||
goto done;
|
||||
|
||||
if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
|
||||
(error = checkout_safe_for_update_only(git_buf_cstr(&path_workdir), result.mode)) <= 0)
|
||||
goto done;
|
||||
|
||||
if ((error = git_futils_mkpath2file(path_workdir.ptr, 0755)) < 0 ||
|
||||
(error = git_filebuf_open(&output, path_workdir.ptr, GIT_FILEBUF_DO_NOT_BUFFER)) < 0 ||
|
||||
(error = git_filebuf_write(&output, result.data, result.len)) < 0 ||
|
||||
(error = git_filebuf_commit(&output, result.mode)) < 0)
|
||||
goto done;
|
||||
|
||||
done:
|
||||
git_buf_free(&our_label);
|
||||
git_buf_free(&their_label);
|
||||
|
||||
git_merge_file_input_free(&ancestor);
|
||||
git_merge_file_input_free(&ours);
|
||||
git_merge_file_input_free(&theirs);
|
||||
git_merge_file_result_free(&result);
|
||||
git_buf_free(&path_workdir);
|
||||
git_buf_free(&path_suffixed);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int checkout_create_conflicts(checkout_data *data)
|
||||
{
|
||||
git_vector conflicts = GIT_VECTOR_INIT;
|
||||
checkout_conflictdata *conflict;
|
||||
size_t i;
|
||||
int error = 0;
|
||||
|
||||
git_vector_foreach(&data->conflicts, i, conflict) {
|
||||
/* Both deleted: nothing to do */
|
||||
if (conflict->ours == NULL && conflict->theirs == NULL)
|
||||
error = 0;
|
||||
|
||||
else if ((data->strategy & GIT_CHECKOUT_USE_OURS) &&
|
||||
conflict->ours)
|
||||
error = checkout_write_entry(data, conflict, conflict->ours);
|
||||
else if ((data->strategy & GIT_CHECKOUT_USE_THEIRS) &&
|
||||
conflict->theirs)
|
||||
error = checkout_write_entry(data, conflict, conflict->theirs);
|
||||
|
||||
/* Ignore the other side of name collisions. */
|
||||
else if ((data->strategy & GIT_CHECKOUT_USE_OURS) &&
|
||||
!conflict->ours && conflict->name_collision)
|
||||
error = 0;
|
||||
else if ((data->strategy & GIT_CHECKOUT_USE_THEIRS) &&
|
||||
!conflict->theirs && conflict->name_collision)
|
||||
error = 0;
|
||||
|
||||
/* For modify/delete, name collisions and d/f conflicts, write
|
||||
* the file (potentially with the name mangled.
|
||||
*/
|
||||
else if (conflict->ours != NULL && conflict->theirs == NULL)
|
||||
error = checkout_write_entry(data, conflict, conflict->ours);
|
||||
else if (conflict->ours == NULL && conflict->theirs != NULL)
|
||||
error = checkout_write_entry(data, conflict, conflict->theirs);
|
||||
|
||||
/* Add/add conflicts and rename 1->2 conflicts, write the
|
||||
* ours/theirs sides (potentially name mangled).
|
||||
*/
|
||||
else if (conflict->one_to_two)
|
||||
error = checkout_write_entries(data, conflict);
|
||||
|
||||
/* If all sides are links, write the ours side */
|
||||
else if (S_ISLNK(conflict->ours->mode) &&
|
||||
S_ISLNK(conflict->theirs->mode))
|
||||
error = checkout_write_entry(data, conflict, conflict->ours);
|
||||
/* Link/file conflicts, write the file side */
|
||||
else if (S_ISLNK(conflict->ours->mode))
|
||||
error = checkout_write_entry(data, conflict, conflict->theirs);
|
||||
else if (S_ISLNK(conflict->theirs->mode))
|
||||
error = checkout_write_entry(data, conflict, conflict->ours);
|
||||
|
||||
else
|
||||
error = checkout_write_merge(data, conflict);
|
||||
|
||||
if (error)
|
||||
break;
|
||||
|
||||
data->completed_steps++;
|
||||
report_progress(data,
|
||||
conflict->ours ? conflict->ours->path :
|
||||
(conflict->theirs ? conflict->theirs->path : conflict->ancestor->path));
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
static void checkout_data_clear(checkout_data *data)
|
||||
{
|
||||
checkout_conflictdata *conflict;
|
||||
size_t i;
|
||||
|
||||
if (data->opts_free_baseline) {
|
||||
git_tree_free(data->opts.baseline);
|
||||
data->opts.baseline = NULL;
|
||||
@ -1107,6 +1772,11 @@ static void checkout_data_clear(checkout_data *data)
|
||||
git_vector_free(&data->removes);
|
||||
git_pool_clear(&data->pool);
|
||||
|
||||
git_vector_foreach(&data->conflicts, i, conflict)
|
||||
git__free(conflict);
|
||||
|
||||
git_vector_free(&data->conflicts);
|
||||
|
||||
git__free(data->pfx);
|
||||
data->pfx = NULL;
|
||||
|
||||
@ -1171,7 +1841,17 @@ static int checkout_data_init(
|
||||
(error = git_index_read(data->index)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* clear the REUC when doing a tree or commit checkout */
|
||||
/* cannot checkout if unresolved conflicts exist */
|
||||
if ((data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) == 0 &&
|
||||
git_index_has_conflicts(data->index)) {
|
||||
error = GIT_EMERGECONFLICT;
|
||||
giterr_set(GITERR_CHECKOUT,
|
||||
"unresolved conflicts exist in the index");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* clean conflict data when doing a tree or commit checkout */
|
||||
git_index_name_clear(data->index);
|
||||
git_index_reuc_clear(data->index);
|
||||
}
|
||||
}
|
||||
@ -1213,6 +1893,7 @@ static int checkout_data_init(
|
||||
}
|
||||
|
||||
if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
|
||||
(error = git_vector_init(&data->conflicts, 0, NULL)) < 0 ||
|
||||
(error = git_pool_init(&data->pool, 1, 0)) < 0 ||
|
||||
(error = git_buf_puts(&data->path, data->opts.target_directory)) < 0 ||
|
||||
(error = git_path_to_dir(&data->path)) < 0)
|
||||
@ -1283,14 +1964,16 @@ int git_checkout_iterator(
|
||||
goto cleanup;
|
||||
|
||||
/* Loop through diff (and working directory iterator) building a list of
|
||||
* actions to be taken, plus look for conflicts and send notifications.
|
||||
* actions to be taken, plus look for conflicts and send notifications,
|
||||
* then loop through conflicts.
|
||||
*/
|
||||
if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
data.total_steps = counts[CHECKOUT_ACTION__REMOVE] +
|
||||
counts[CHECKOUT_ACTION__UPDATE_BLOB] +
|
||||
counts[CHECKOUT_ACTION__UPDATE_SUBMODULE];
|
||||
counts[CHECKOUT_ACTION__UPDATE_SUBMODULE] +
|
||||
counts[CHECKOUT_ACTION__UPDATE_CONFLICT];
|
||||
|
||||
report_progress(&data, NULL); /* establish 0 baseline */
|
||||
|
||||
@ -1309,6 +1992,10 @@ int git_checkout_iterator(
|
||||
(error = checkout_create_submodules(actions, &data)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (counts[CHECKOUT_ACTION__UPDATE_CONFLICT] > 0 &&
|
||||
(error = checkout_create_conflicts(&data)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
assert(data.completed_steps == data.total_steps);
|
||||
|
||||
cleanup:
|
||||
@ -1319,7 +2006,7 @@ cleanup:
|
||||
(data.strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
|
||||
error = git_index_write(data.index);
|
||||
|
||||
git_diff_list_free(data.diff);
|
||||
git_diff_free(data.diff);
|
||||
git_iterator_free(workdir);
|
||||
git_iterator_free(baseline);
|
||||
git__free(actions);
|
||||
|
||||
42
src/clone.c
42
src/clone.c
@ -391,11 +391,11 @@ int git_clone(
|
||||
const char *local_path,
|
||||
const git_clone_options *_options)
|
||||
{
|
||||
int retcode = GIT_ERROR;
|
||||
int error = 0;
|
||||
git_repository *repo = NULL;
|
||||
git_remote *origin;
|
||||
git_clone_options options = GIT_CLONE_OPTIONS_INIT;
|
||||
int remove_directory_on_failure = 0;
|
||||
uint32_t rmdir_flags = GIT_RMDIR_REMOVE_FILES;
|
||||
|
||||
assert(out && url && local_path);
|
||||
|
||||
@ -408,33 +408,29 @@ int git_clone(
|
||||
if (git_path_exists(local_path) && !git_path_is_empty_dir(local_path)) {
|
||||
giterr_set(GITERR_INVALID,
|
||||
"'%s' exists and is not an empty directory", local_path);
|
||||
return GIT_ERROR;
|
||||
return GIT_EEXISTS;
|
||||
}
|
||||
|
||||
/* Only remove the directory on failure if we create it */
|
||||
remove_directory_on_failure = !git_path_exists(local_path);
|
||||
/* Only remove the root directory on failure if we create it */
|
||||
if (git_path_exists(local_path))
|
||||
rmdir_flags |= GIT_RMDIR_SKIP_ROOT;
|
||||
|
||||
if ((retcode = git_repository_init(&repo, local_path, options.bare)) < 0)
|
||||
return retcode;
|
||||
if ((error = git_repository_init(&repo, local_path, options.bare)) < 0)
|
||||
return error;
|
||||
|
||||
if ((retcode = create_and_configure_origin(&origin, repo, url, &options)) < 0)
|
||||
goto cleanup;
|
||||
if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
|
||||
error = git_clone_into(
|
||||
repo, origin, &options.checkout_opts, options.checkout_branch);
|
||||
|
||||
retcode = git_clone_into(repo, origin, &options.checkout_opts, options.checkout_branch);
|
||||
git_remote_free(origin);
|
||||
git_remote_free(origin);
|
||||
}
|
||||
|
||||
if (retcode < 0)
|
||||
goto cleanup;
|
||||
if (error < 0) {
|
||||
git_repository_free(repo);
|
||||
repo = NULL;
|
||||
(void)git_futils_rmdir_r(local_path, NULL, rmdir_flags);
|
||||
}
|
||||
|
||||
*out = repo;
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
git_repository_free(repo);
|
||||
if (remove_directory_on_failure)
|
||||
git_futils_rmdir_r(local_path, NULL, GIT_RMDIR_REMOVE_FILES);
|
||||
else
|
||||
git_futils_cleanupdir_r(local_path);
|
||||
|
||||
return retcode;
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@ extern int git_config_rename_section(
|
||||
* @param out the new backend
|
||||
* @param path where the config file is located
|
||||
*/
|
||||
extern int git_config_file__ondisk(struct git_config_backend **out, const char *path);
|
||||
extern int git_config_file__ondisk(git_config_backend **out, const char *path);
|
||||
|
||||
extern int git_config__normalize_name(const char *in, char **out);
|
||||
|
||||
|
||||
@ -67,6 +67,7 @@ static struct map_data _cvar_maps[] = {
|
||||
{"core.ignorestat", NULL, 0, GIT_IGNORESTAT_DEFAULT },
|
||||
{"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT },
|
||||
{"core.abbrev", _cvar_map_int, 1, GIT_ABBREV_DEFAULT },
|
||||
{"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT },
|
||||
};
|
||||
|
||||
int git_repository__cvar(int *out, git_repository *repo, git_cvar_cached cvar)
|
||||
|
||||
134
src/diff.c
134
src/diff.c
@ -21,7 +21,7 @@
|
||||
(VAL) ? ((DIFF)->opts.flags | (FLAG)) : ((DIFF)->opts.flags & ~(VAL))
|
||||
|
||||
static git_diff_delta *diff_delta__alloc(
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
git_delta_t status,
|
||||
const char *path)
|
||||
{
|
||||
@ -50,7 +50,7 @@ static git_diff_delta *diff_delta__alloc(
|
||||
}
|
||||
|
||||
static int diff_notify(
|
||||
const git_diff_list *diff,
|
||||
const git_diff *diff,
|
||||
const git_diff_delta *delta,
|
||||
const char *matched_pathspec)
|
||||
{
|
||||
@ -62,7 +62,7 @@ static int diff_notify(
|
||||
}
|
||||
|
||||
static int diff_delta__from_one(
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
git_delta_t status,
|
||||
const git_index_entry *entry)
|
||||
{
|
||||
@ -81,7 +81,7 @@ static int diff_delta__from_one(
|
||||
if (!git_pathspec__match(
|
||||
&diff->pathspec, entry->path,
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE),
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
|
||||
&matched_pathspec, NULL))
|
||||
return 0;
|
||||
|
||||
@ -90,6 +90,7 @@ static int diff_delta__from_one(
|
||||
|
||||
/* This fn is just for single-sided diffs */
|
||||
assert(status != GIT_DELTA_MODIFIED);
|
||||
delta->nfiles = 1;
|
||||
|
||||
if (delta->status == GIT_DELTA_DELETED) {
|
||||
delta->old_file.mode = entry->mode;
|
||||
@ -120,7 +121,7 @@ static int diff_delta__from_one(
|
||||
}
|
||||
|
||||
static int diff_delta__from_two(
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
git_delta_t status,
|
||||
const git_index_entry *old_entry,
|
||||
uint32_t old_mode,
|
||||
@ -148,6 +149,7 @@ static int diff_delta__from_two(
|
||||
|
||||
delta = diff_delta__alloc(diff, status, canonical_path);
|
||||
GITERR_CHECK_ALLOC(delta);
|
||||
delta->nfiles = 2;
|
||||
|
||||
git_oid_cpy(&delta->old_file.oid, &old_entry->oid);
|
||||
delta->old_file.size = old_entry->file_size;
|
||||
@ -181,7 +183,7 @@ static int diff_delta__from_two(
|
||||
}
|
||||
|
||||
static git_diff_delta *diff_delta__last_for_item(
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
const git_index_entry *item)
|
||||
{
|
||||
git_diff_delta *delta = git_vector_last(&diff->deltas);
|
||||
@ -340,13 +342,13 @@ static const char *diff_mnemonic_prefix(
|
||||
return pfx;
|
||||
}
|
||||
|
||||
static git_diff_list *diff_list_alloc(
|
||||
static git_diff *diff_list_alloc(
|
||||
git_repository *repo,
|
||||
git_iterator *old_iter,
|
||||
git_iterator *new_iter)
|
||||
{
|
||||
git_diff_options dflt = GIT_DIFF_OPTIONS_INIT;
|
||||
git_diff_list *diff = git__calloc(1, sizeof(git_diff_list));
|
||||
git_diff *diff = git__calloc(1, sizeof(git_diff));
|
||||
if (!diff)
|
||||
return NULL;
|
||||
|
||||
@ -360,7 +362,7 @@ static git_diff_list *diff_list_alloc(
|
||||
|
||||
if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0 ||
|
||||
git_pool_init(&diff->pool, 1, 0) < 0) {
|
||||
git_diff_list_free(diff);
|
||||
git_diff_free(diff);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -368,14 +370,14 @@ static git_diff_list *diff_list_alloc(
|
||||
* the ignore_case bit set */
|
||||
if (!git_iterator_ignore_case(old_iter) &&
|
||||
!git_iterator_ignore_case(new_iter)) {
|
||||
diff->opts.flags &= ~GIT_DIFF_DELTAS_ARE_ICASE;
|
||||
diff->opts.flags &= ~GIT_DIFF_IGNORE_CASE;
|
||||
|
||||
diff->strcomp = git__strcmp;
|
||||
diff->strncomp = git__strncmp;
|
||||
diff->pfxcomp = git__prefixcmp;
|
||||
diff->entrycomp = git_index_entry__cmp;
|
||||
} else {
|
||||
diff->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
|
||||
diff->opts.flags |= GIT_DIFF_IGNORE_CASE;
|
||||
|
||||
diff->strcomp = git__strcasecmp;
|
||||
diff->strncomp = git__strncasecmp;
|
||||
@ -389,7 +391,7 @@ static git_diff_list *diff_list_alloc(
|
||||
}
|
||||
|
||||
static int diff_list_apply_options(
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
const git_diff_options *opts)
|
||||
{
|
||||
git_config *cfg;
|
||||
@ -399,9 +401,9 @@ static int diff_list_apply_options(
|
||||
|
||||
if (opts) {
|
||||
/* copy user options (except case sensitivity info from iterators) */
|
||||
bool icase = DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE);
|
||||
bool icase = DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE);
|
||||
memcpy(&diff->opts, opts, sizeof(diff->opts));
|
||||
DIFF_FLAG_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE, icase);
|
||||
DIFF_FLAG_SET(diff, GIT_DIFF_IGNORE_CASE, icase);
|
||||
|
||||
/* initialize pathspec from options */
|
||||
if (git_pathspec__vinit(&diff->pathspec, &opts->pathspec, pool) < 0)
|
||||
@ -413,7 +415,7 @@ static int diff_list_apply_options(
|
||||
diff->opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
|
||||
|
||||
/* flag INCLUDE_UNTRACKED_CONTENT implies INCLUDE_UNTRACKED */
|
||||
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED_CONTENT))
|
||||
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_SHOW_UNTRACKED_CONTENT))
|
||||
diff->opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
|
||||
|
||||
/* load config values that affect diff behavior */
|
||||
@ -490,7 +492,7 @@ static int diff_list_apply_options(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void diff_list_free(git_diff_list *diff)
|
||||
static void diff_list_free(git_diff *diff)
|
||||
{
|
||||
git_diff_delta *delta;
|
||||
unsigned int i;
|
||||
@ -508,7 +510,7 @@ static void diff_list_free(git_diff_list *diff)
|
||||
git__free(diff);
|
||||
}
|
||||
|
||||
void git_diff_list_free(git_diff_list *diff)
|
||||
void git_diff_free(git_diff *diff)
|
||||
{
|
||||
if (!diff)
|
||||
return;
|
||||
@ -516,7 +518,7 @@ void git_diff_list_free(git_diff_list *diff)
|
||||
GIT_REFCOUNT_DEC(diff, diff_list_free);
|
||||
}
|
||||
|
||||
void git_diff_list_addref(git_diff_list *diff)
|
||||
void git_diff_addref(git_diff *diff)
|
||||
{
|
||||
GIT_REFCOUNT_INC(diff);
|
||||
}
|
||||
@ -612,7 +614,7 @@ typedef struct {
|
||||
static int maybe_modified_submodule(
|
||||
git_delta_t *status,
|
||||
git_oid *found_oid,
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
diff_in_progress *info)
|
||||
{
|
||||
int error = 0;
|
||||
@ -659,7 +661,7 @@ static int maybe_modified_submodule(
|
||||
}
|
||||
|
||||
static int maybe_modified(
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
diff_in_progress *info)
|
||||
{
|
||||
git_oid noid;
|
||||
@ -674,7 +676,7 @@ static int maybe_modified(
|
||||
if (!git_pathspec__match(
|
||||
&diff->pathspec, oitem->path,
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH),
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE),
|
||||
DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
|
||||
&matched_pathspec, NULL))
|
||||
return 0;
|
||||
|
||||
@ -778,7 +780,7 @@ static int maybe_modified(
|
||||
}
|
||||
|
||||
static bool entry_is_prefixed(
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
const git_index_entry *item,
|
||||
const git_index_entry *prefix_item)
|
||||
{
|
||||
@ -795,7 +797,7 @@ static bool entry_is_prefixed(
|
||||
}
|
||||
|
||||
static int diff_scan_inside_untracked_dir(
|
||||
git_diff_list *diff, diff_in_progress *info, git_delta_t *delta_type)
|
||||
git_diff *diff, diff_in_progress *info, git_delta_t *delta_type)
|
||||
{
|
||||
int error = 0;
|
||||
git_buf base = GIT_BUF_INIT;
|
||||
@ -861,7 +863,7 @@ done:
|
||||
}
|
||||
|
||||
static int handle_unmatched_new_item(
|
||||
git_diff_list *diff, diff_in_progress *info)
|
||||
git_diff *diff, diff_in_progress *info)
|
||||
{
|
||||
int error = 0;
|
||||
const git_index_entry *nitem = info->nitem;
|
||||
@ -910,7 +912,7 @@ static int handle_unmatched_new_item(
|
||||
*/
|
||||
if (!recurse_into_dir &&
|
||||
delta_type == GIT_DELTA_UNTRACKED &&
|
||||
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_FAST_UNTRACKED_DIRS))
|
||||
DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS))
|
||||
{
|
||||
git_diff_delta *last;
|
||||
|
||||
@ -1016,7 +1018,7 @@ static int handle_unmatched_new_item(
|
||||
}
|
||||
|
||||
static int handle_unmatched_old_item(
|
||||
git_diff_list *diff, diff_in_progress *info)
|
||||
git_diff *diff, diff_in_progress *info)
|
||||
{
|
||||
int error = diff_delta__from_one(diff, GIT_DELTA_DELETED, info->oitem);
|
||||
if (error < 0)
|
||||
@ -1048,7 +1050,7 @@ static int handle_unmatched_old_item(
|
||||
}
|
||||
|
||||
static int handle_matched_item(
|
||||
git_diff_list *diff, diff_in_progress *info)
|
||||
git_diff *diff, diff_in_progress *info)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
@ -1063,7 +1065,7 @@ static int handle_matched_item(
|
||||
}
|
||||
|
||||
int git_diff__from_iterators(
|
||||
git_diff_list **diff_ptr,
|
||||
git_diff **diff_ptr,
|
||||
git_repository *repo,
|
||||
git_iterator *old_iter,
|
||||
git_iterator *new_iter,
|
||||
@ -1071,7 +1073,7 @@ int git_diff__from_iterators(
|
||||
{
|
||||
int error = 0;
|
||||
diff_in_progress info;
|
||||
git_diff_list *diff;
|
||||
git_diff *diff;
|
||||
|
||||
*diff_ptr = NULL;
|
||||
|
||||
@ -1084,7 +1086,7 @@ int git_diff__from_iterators(
|
||||
git_buf_init(&info.ignore_prefix, 0);
|
||||
|
||||
/* make iterators have matching icase behavior */
|
||||
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_DELTAS_ARE_ICASE)) {
|
||||
if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE)) {
|
||||
if ((error = git_iterator_set_ignore_case(old_iter, true)) < 0 ||
|
||||
(error = git_iterator_set_ignore_case(new_iter, true)) < 0)
|
||||
goto cleanup;
|
||||
@ -1132,7 +1134,7 @@ cleanup:
|
||||
if (!error)
|
||||
*diff_ptr = diff;
|
||||
else
|
||||
git_diff_list_free(diff);
|
||||
git_diff_free(diff);
|
||||
|
||||
git_buf_free(&info.ignore_prefix);
|
||||
|
||||
@ -1149,7 +1151,7 @@ cleanup:
|
||||
} while (0)
|
||||
|
||||
int git_diff_tree_to_tree(
|
||||
git_diff_list **diff,
|
||||
git_diff **diff,
|
||||
git_repository *repo,
|
||||
git_tree *old_tree,
|
||||
git_tree *new_tree,
|
||||
@ -1164,7 +1166,7 @@ int git_diff_tree_to_tree(
|
||||
* currently case insensitive, unless the user explicitly asked
|
||||
* for case insensitivity
|
||||
*/
|
||||
if (opts && (opts->flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0)
|
||||
if (opts && (opts->flags & GIT_DIFF_IGNORE_CASE) != 0)
|
||||
iflag = GIT_ITERATOR_IGNORE_CASE;
|
||||
|
||||
DIFF_FROM_ITERATORS(
|
||||
@ -1176,7 +1178,7 @@ int git_diff_tree_to_tree(
|
||||
}
|
||||
|
||||
int git_diff_tree_to_index(
|
||||
git_diff_list **diff,
|
||||
git_diff **diff,
|
||||
git_repository *repo,
|
||||
git_tree *old_tree,
|
||||
git_index *index,
|
||||
@ -1204,9 +1206,9 @@ int git_diff_tree_to_index(
|
||||
git_index__set_ignore_case(index, true);
|
||||
|
||||
if (!error) {
|
||||
git_diff_list *d = *diff;
|
||||
git_diff *d = *diff;
|
||||
|
||||
d->opts.flags |= GIT_DIFF_DELTAS_ARE_ICASE;
|
||||
d->opts.flags |= GIT_DIFF_IGNORE_CASE;
|
||||
d->strcomp = git__strcasecmp;
|
||||
d->strncomp = git__strncasecmp;
|
||||
d->pfxcomp = git__prefixcmp_icase;
|
||||
@ -1221,7 +1223,7 @@ int git_diff_tree_to_index(
|
||||
}
|
||||
|
||||
int git_diff_index_to_workdir(
|
||||
git_diff_list **diff,
|
||||
git_diff **diff,
|
||||
git_repository *repo,
|
||||
git_index *index,
|
||||
const git_diff_options *opts)
|
||||
@ -1242,9 +1244,8 @@ int git_diff_index_to_workdir(
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
int git_diff_tree_to_workdir(
|
||||
git_diff_list **diff,
|
||||
git_diff **diff,
|
||||
git_repository *repo,
|
||||
git_tree *old_tree,
|
||||
const git_diff_options *opts)
|
||||
@ -1262,16 +1263,43 @@ int git_diff_tree_to_workdir(
|
||||
return error;
|
||||
}
|
||||
|
||||
size_t git_diff_num_deltas(git_diff_list *diff)
|
||||
int git_diff_tree_to_workdir_with_index(
|
||||
git_diff **diff,
|
||||
git_repository *repo,
|
||||
git_tree *old_tree,
|
||||
const git_diff_options *opts)
|
||||
{
|
||||
assert(diff);
|
||||
return (size_t)diff->deltas.length;
|
||||
int error = 0;
|
||||
git_diff *d1 = NULL, *d2 = NULL;
|
||||
|
||||
assert(diff && repo);
|
||||
|
||||
if (!(error = git_diff_tree_to_index(&d1, repo, old_tree, NULL, opts)) &&
|
||||
!(error = git_diff_index_to_workdir(&d2, repo, NULL, opts)))
|
||||
error = git_diff_merge(d1, d2);
|
||||
|
||||
git_diff_free(d2);
|
||||
|
||||
if (error) {
|
||||
git_diff_free(d1);
|
||||
d1 = NULL;
|
||||
}
|
||||
|
||||
*diff = d1;
|
||||
return error;
|
||||
}
|
||||
|
||||
size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type)
|
||||
|
||||
size_t git_diff_num_deltas(const git_diff *diff)
|
||||
{
|
||||
assert(diff);
|
||||
return diff->deltas.length;
|
||||
}
|
||||
|
||||
size_t git_diff_num_deltas_of_type(const git_diff *diff, git_delta_t type)
|
||||
{
|
||||
size_t i, count = 0;
|
||||
git_diff_delta *delta;
|
||||
const git_diff_delta *delta;
|
||||
|
||||
assert(diff);
|
||||
|
||||
@ -1282,14 +1310,20 @@ size_t git_diff_num_deltas_of_type(git_diff_list *diff, git_delta_t type)
|
||||
return count;
|
||||
}
|
||||
|
||||
int git_diff_is_sorted_icase(const git_diff_list *diff)
|
||||
const git_diff_delta *git_diff_get_delta(const git_diff *diff, size_t idx)
|
||||
{
|
||||
return (diff->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0;
|
||||
assert(diff);
|
||||
return git_vector_get(&diff->deltas, idx);
|
||||
}
|
||||
|
||||
int git_diff_is_sorted_icase(const git_diff *diff)
|
||||
{
|
||||
return (diff->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
|
||||
}
|
||||
|
||||
int git_diff__paired_foreach(
|
||||
git_diff_list *head2idx,
|
||||
git_diff_list *idx2wd,
|
||||
git_diff *head2idx,
|
||||
git_diff *idx2wd,
|
||||
int (*cb)(git_diff_delta *h2i, git_diff_delta *i2w, void *payload),
|
||||
void *payload)
|
||||
{
|
||||
@ -1318,10 +1352,10 @@ int git_diff__paired_foreach(
|
||||
* always sort by the old name in the i2w list.
|
||||
*/
|
||||
h2i_icase = head2idx != NULL &&
|
||||
(head2idx->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0;
|
||||
(head2idx->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
|
||||
|
||||
i2w_icase = idx2wd != NULL &&
|
||||
(idx2wd->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0;
|
||||
(idx2wd->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
|
||||
|
||||
icase_mismatch =
|
||||
(head2idx != NULL && idx2wd != NULL && h2i_icase != i2w_icase);
|
||||
|
||||
10
src/diff.h
10
src/diff.h
@ -52,7 +52,7 @@ enum {
|
||||
|
||||
#define GIT_DIFF__VERBOSE (1 << 30)
|
||||
|
||||
struct git_diff_list {
|
||||
struct git_diff {
|
||||
git_refcount rc;
|
||||
git_repository *repo;
|
||||
git_diff_options opts;
|
||||
@ -72,7 +72,7 @@ struct git_diff_list {
|
||||
extern void git_diff__cleanup_modes(
|
||||
uint32_t diffcaps, uint32_t *omode, uint32_t *nmode);
|
||||
|
||||
extern void git_diff_list_addref(git_diff_list *diff);
|
||||
extern void git_diff_addref(git_diff *diff);
|
||||
|
||||
extern int git_diff_delta__cmp(const void *a, const void *b);
|
||||
extern int git_diff_delta__casecmp(const void *a, const void *b);
|
||||
@ -93,15 +93,15 @@ extern int git_diff__oid_for_file(
|
||||
git_repository *, const char *, uint16_t, git_off_t, git_oid *);
|
||||
|
||||
extern int git_diff__from_iterators(
|
||||
git_diff_list **diff_ptr,
|
||||
git_diff **diff_ptr,
|
||||
git_repository *repo,
|
||||
git_iterator *old_iter,
|
||||
git_iterator *new_iter,
|
||||
const git_diff_options *opts);
|
||||
|
||||
extern int git_diff__paired_foreach(
|
||||
git_diff_list *idx2head,
|
||||
git_diff_list *wd2idx,
|
||||
git_diff *idx2head,
|
||||
git_diff *wd2idx,
|
||||
int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
|
||||
void *payload);
|
||||
|
||||
|
||||
@ -88,7 +88,7 @@ static int diff_file_content_init_common(
|
||||
|
||||
int git_diff_file_content__init_from_diff(
|
||||
git_diff_file_content *fc,
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
size_t delta_index,
|
||||
bool use_old)
|
||||
{
|
||||
@ -110,7 +110,7 @@ int git_diff_file_content__init_from_diff(
|
||||
has_data = use_old; break;
|
||||
case GIT_DELTA_UNTRACKED:
|
||||
has_data = !use_old &&
|
||||
(diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) != 0;
|
||||
(diff->opts.flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) != 0;
|
||||
break;
|
||||
case GIT_DELTA_MODIFIED:
|
||||
case GIT_DELTA_COPIED:
|
||||
|
||||
@ -27,7 +27,7 @@ typedef struct {
|
||||
|
||||
extern int git_diff_file_content__init_from_diff(
|
||||
git_diff_file_content *fc,
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
size_t delta_index,
|
||||
bool use_old);
|
||||
|
||||
|
||||
284
src/diff_patch.c
284
src/diff_patch.c
@ -12,36 +12,24 @@
|
||||
#include "diff_xdiff.h"
|
||||
#include "fileops.h"
|
||||
|
||||
/* cached information about a single span in a diff */
|
||||
typedef struct diff_patch_line diff_patch_line;
|
||||
struct diff_patch_line {
|
||||
const char *ptr;
|
||||
size_t len;
|
||||
size_t lines, oldno, newno;
|
||||
char origin;
|
||||
};
|
||||
|
||||
/* cached information about a hunk in a diff */
|
||||
typedef struct diff_patch_hunk diff_patch_hunk;
|
||||
struct diff_patch_hunk {
|
||||
git_diff_range range;
|
||||
char header[128];
|
||||
size_t header_len;
|
||||
git_diff_hunk hunk;
|
||||
size_t line_start;
|
||||
size_t line_count;
|
||||
};
|
||||
|
||||
struct git_diff_patch {
|
||||
struct git_patch {
|
||||
git_refcount rc;
|
||||
git_diff_list *diff; /* for refcount purposes, maybe NULL for blob diffs */
|
||||
git_diff *diff; /* for refcount purposes, maybe NULL for blob diffs */
|
||||
git_diff_delta *delta;
|
||||
size_t delta_index;
|
||||
git_diff_file_content ofile;
|
||||
git_diff_file_content nfile;
|
||||
uint32_t flags;
|
||||
git_array_t(diff_patch_hunk) hunks;
|
||||
git_array_t(diff_patch_line) lines;
|
||||
size_t oldno, newno;
|
||||
git_array_t(git_diff_line) lines;
|
||||
size_t content_size, context_size, header_size;
|
||||
git_pool flattened;
|
||||
};
|
||||
@ -55,12 +43,13 @@ enum {
|
||||
GIT_DIFF_PATCH_FLATTENED = (1 << 5),
|
||||
};
|
||||
|
||||
static void diff_output_init(git_diff_output*, const git_diff_options*,
|
||||
git_diff_file_cb, git_diff_hunk_cb, git_diff_data_cb, void*);
|
||||
static void diff_output_init(
|
||||
git_diff_output*, const git_diff_options*,
|
||||
git_diff_file_cb, git_diff_hunk_cb, git_diff_line_cb, void*);
|
||||
|
||||
static void diff_output_to_patch(git_diff_output *, git_diff_patch *);
|
||||
static void diff_output_to_patch(git_diff_output *, git_patch *);
|
||||
|
||||
static void diff_patch_update_binary(git_diff_patch *patch)
|
||||
static void diff_patch_update_binary(git_patch *patch)
|
||||
{
|
||||
if ((patch->delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0)
|
||||
return;
|
||||
@ -74,21 +63,21 @@ static void diff_patch_update_binary(git_diff_patch *patch)
|
||||
patch->delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
|
||||
}
|
||||
|
||||
static void diff_patch_init_common(git_diff_patch *patch)
|
||||
static void diff_patch_init_common(git_patch *patch)
|
||||
{
|
||||
diff_patch_update_binary(patch);
|
||||
|
||||
if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
|
||||
patch->flags |= GIT_DIFF_PATCH_LOADED; /* set LOADED but not DIFFABLE */
|
||||
patch->flags |= GIT_DIFF_PATCH_LOADED; /* LOADED but not DIFFABLE */
|
||||
|
||||
patch->flags |= GIT_DIFF_PATCH_INITIALIZED;
|
||||
|
||||
if (patch->diff)
|
||||
git_diff_list_addref(patch->diff);
|
||||
git_diff_addref(patch->diff);
|
||||
}
|
||||
|
||||
static int diff_patch_init_from_diff(
|
||||
git_diff_patch *patch, git_diff_list *diff, size_t delta_index)
|
||||
git_patch *patch, git_diff *diff, size_t delta_index)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
@ -109,12 +98,10 @@ static int diff_patch_init_from_diff(
|
||||
}
|
||||
|
||||
static int diff_patch_alloc_from_diff(
|
||||
git_diff_patch **out,
|
||||
git_diff_list *diff,
|
||||
size_t delta_index)
|
||||
git_patch **out, git_diff *diff, size_t delta_index)
|
||||
{
|
||||
int error;
|
||||
git_diff_patch *patch = git__calloc(1, sizeof(git_diff_patch));
|
||||
git_patch *patch = git__calloc(1, sizeof(git_patch));
|
||||
GITERR_CHECK_ALLOC(patch);
|
||||
|
||||
if (!(error = diff_patch_init_from_diff(patch, diff, delta_index))) {
|
||||
@ -129,7 +116,7 @@ static int diff_patch_alloc_from_diff(
|
||||
return error;
|
||||
}
|
||||
|
||||
static int diff_patch_load(git_diff_patch *patch, git_diff_output *output)
|
||||
static int diff_patch_load(git_patch *patch, git_diff_output *output)
|
||||
{
|
||||
int error = 0;
|
||||
bool incomplete_data;
|
||||
@ -207,7 +194,7 @@ cleanup:
|
||||
}
|
||||
|
||||
static int diff_patch_file_callback(
|
||||
git_diff_patch *patch, git_diff_output *output)
|
||||
git_patch *patch, git_diff_output *output)
|
||||
{
|
||||
float progress;
|
||||
|
||||
@ -223,7 +210,7 @@ static int diff_patch_file_callback(
|
||||
return output->error;
|
||||
}
|
||||
|
||||
static int diff_patch_generate(git_diff_patch *patch, git_diff_output *output)
|
||||
static int diff_patch_generate(git_patch *patch, git_diff_output *output)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
@ -248,7 +235,7 @@ static int diff_patch_generate(git_diff_patch *patch, git_diff_output *output)
|
||||
return error;
|
||||
}
|
||||
|
||||
static void diff_patch_free(git_diff_patch *patch)
|
||||
static void diff_patch_free(git_patch *patch)
|
||||
{
|
||||
git_diff_file_content__clear(&patch->ofile);
|
||||
git_diff_file_content__clear(&patch->nfile);
|
||||
@ -256,7 +243,7 @@ static void diff_patch_free(git_diff_patch *patch)
|
||||
git_array_clear(patch->lines);
|
||||
git_array_clear(patch->hunks);
|
||||
|
||||
git_diff_list_free(patch->diff); /* decrements refcount */
|
||||
git_diff_free(patch->diff); /* decrements refcount */
|
||||
patch->diff = NULL;
|
||||
|
||||
git_pool_clear(&patch->flattened);
|
||||
@ -265,7 +252,7 @@ static void diff_patch_free(git_diff_patch *patch)
|
||||
git__free(patch);
|
||||
}
|
||||
|
||||
static int diff_required(git_diff_list *diff, const char *action)
|
||||
static int diff_required(git_diff *diff, const char *action)
|
||||
{
|
||||
if (diff)
|
||||
return 0;
|
||||
@ -274,16 +261,16 @@ static int diff_required(git_diff_list *diff, const char *action)
|
||||
}
|
||||
|
||||
int git_diff_foreach(
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
git_diff_file_cb file_cb,
|
||||
git_diff_hunk_cb hunk_cb,
|
||||
git_diff_data_cb data_cb,
|
||||
git_diff_line_cb data_cb,
|
||||
void *payload)
|
||||
{
|
||||
int error = 0;
|
||||
git_xdiff_output xo;
|
||||
size_t idx;
|
||||
git_diff_patch patch;
|
||||
git_patch patch;
|
||||
|
||||
if (diff_required(diff, "git_diff_foreach") < 0)
|
||||
return -1;
|
||||
@ -305,7 +292,7 @@ int git_diff_foreach(
|
||||
if (!error)
|
||||
error = diff_patch_generate(&patch, &xo.output);
|
||||
|
||||
git_diff_patch_free(&patch);
|
||||
git_patch_free(&patch);
|
||||
}
|
||||
|
||||
if (error < 0)
|
||||
@ -318,7 +305,7 @@ int git_diff_foreach(
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
git_diff_patch patch;
|
||||
git_patch patch;
|
||||
git_diff_delta delta;
|
||||
char paths[GIT_FLEX_ARRAY];
|
||||
} diff_patch_with_delta;
|
||||
@ -326,7 +313,7 @@ typedef struct {
|
||||
static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo)
|
||||
{
|
||||
int error = 0;
|
||||
git_diff_patch *patch = &pd->patch;
|
||||
git_patch *patch = &pd->patch;
|
||||
bool has_old = ((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
|
||||
bool has_new = ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
|
||||
|
||||
@ -430,7 +417,7 @@ int git_diff_blobs(
|
||||
const git_diff_options *opts,
|
||||
git_diff_file_cb file_cb,
|
||||
git_diff_hunk_cb hunk_cb,
|
||||
git_diff_data_cb data_cb,
|
||||
git_diff_line_cb data_cb,
|
||||
void *payload)
|
||||
{
|
||||
int error = 0;
|
||||
@ -452,13 +439,13 @@ int git_diff_blobs(
|
||||
error = diff_patch_from_blobs(
|
||||
&pd, &xo, old_blob, old_path, new_blob, new_path, opts);
|
||||
|
||||
git_diff_patch_free(&pd.patch);
|
||||
git_patch_free(&pd.patch);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_diff_patch_from_blobs(
|
||||
git_diff_patch **out,
|
||||
int git_patch_from_blobs(
|
||||
git_patch **out,
|
||||
const git_blob *old_blob,
|
||||
const char *old_path,
|
||||
const git_blob *new_blob,
|
||||
@ -484,9 +471,9 @@ int git_diff_patch_from_blobs(
|
||||
pd, &xo, old_blob, old_path, new_blob, new_path, opts);
|
||||
|
||||
if (!error)
|
||||
*out = (git_diff_patch *)pd;
|
||||
*out = (git_patch *)pd;
|
||||
else
|
||||
git_diff_patch_free((git_diff_patch *)pd);
|
||||
git_patch_free((git_patch *)pd);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -542,7 +529,7 @@ int git_diff_blob_to_buffer(
|
||||
const git_diff_options *opts,
|
||||
git_diff_file_cb file_cb,
|
||||
git_diff_hunk_cb hunk_cb,
|
||||
git_diff_data_cb data_cb,
|
||||
git_diff_line_cb data_cb,
|
||||
void *payload)
|
||||
{
|
||||
int error = 0;
|
||||
@ -564,13 +551,13 @@ int git_diff_blob_to_buffer(
|
||||
error = diff_patch_from_blob_and_buffer(
|
||||
&pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts);
|
||||
|
||||
git_diff_patch_free(&pd.patch);
|
||||
git_patch_free(&pd.patch);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_diff_patch_from_blob_and_buffer(
|
||||
git_diff_patch **out,
|
||||
int git_patch_from_blob_and_buffer(
|
||||
git_patch **out,
|
||||
const git_blob *old_blob,
|
||||
const char *old_path,
|
||||
const char *buf,
|
||||
@ -597,28 +584,24 @@ int git_diff_patch_from_blob_and_buffer(
|
||||
pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts);
|
||||
|
||||
if (!error)
|
||||
*out = (git_diff_patch *)pd;
|
||||
*out = (git_patch *)pd;
|
||||
else
|
||||
git_diff_patch_free((git_diff_patch *)pd);
|
||||
git_patch_free((git_patch *)pd);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_diff_get_patch(
|
||||
git_diff_patch **patch_ptr,
|
||||
const git_diff_delta **delta_ptr,
|
||||
git_diff_list *diff,
|
||||
size_t idx)
|
||||
int git_patch_from_diff(
|
||||
git_patch **patch_ptr, git_diff *diff, size_t idx)
|
||||
{
|
||||
int error = 0;
|
||||
git_xdiff_output xo;
|
||||
git_diff_delta *delta = NULL;
|
||||
git_diff_patch *patch = NULL;
|
||||
git_patch *patch = NULL;
|
||||
|
||||
if (patch_ptr) *patch_ptr = NULL;
|
||||
if (delta_ptr) *delta_ptr = NULL;
|
||||
|
||||
if (diff_required(diff, "git_diff_get_patch") < 0)
|
||||
if (diff_required(diff, "git_patch_from_diff") < 0)
|
||||
return -1;
|
||||
|
||||
delta = git_vector_get(&diff->deltas, idx);
|
||||
@ -627,9 +610,6 @@ int git_diff_get_patch(
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
if (delta_ptr)
|
||||
*delta_ptr = delta;
|
||||
|
||||
if (git_diff_delta__should_skip(&diff->opts, delta))
|
||||
return 0;
|
||||
|
||||
@ -656,7 +636,7 @@ int git_diff_get_patch(
|
||||
}
|
||||
|
||||
if (error || !patch_ptr)
|
||||
git_diff_patch_free(patch);
|
||||
git_patch_free(patch);
|
||||
else
|
||||
*patch_ptr = patch;
|
||||
|
||||
@ -665,36 +645,36 @@ int git_diff_get_patch(
|
||||
return error;
|
||||
}
|
||||
|
||||
void git_diff_patch_free(git_diff_patch *patch)
|
||||
void git_patch_free(git_patch *patch)
|
||||
{
|
||||
if (patch)
|
||||
GIT_REFCOUNT_DEC(patch, diff_patch_free);
|
||||
}
|
||||
|
||||
const git_diff_delta *git_diff_patch_delta(git_diff_patch *patch)
|
||||
const git_diff_delta *git_patch_get_delta(git_patch *patch)
|
||||
{
|
||||
assert(patch);
|
||||
return patch->delta;
|
||||
}
|
||||
|
||||
size_t git_diff_patch_num_hunks(git_diff_patch *patch)
|
||||
size_t git_patch_num_hunks(git_patch *patch)
|
||||
{
|
||||
assert(patch);
|
||||
return git_array_size(patch->hunks);
|
||||
}
|
||||
|
||||
int git_diff_patch_line_stats(
|
||||
int git_patch_line_stats(
|
||||
size_t *total_ctxt,
|
||||
size_t *total_adds,
|
||||
size_t *total_dels,
|
||||
const git_diff_patch *patch)
|
||||
const git_patch *patch)
|
||||
{
|
||||
size_t totals[3], idx;
|
||||
|
||||
memset(totals, 0, sizeof(totals));
|
||||
|
||||
for (idx = 0; idx < git_array_size(patch->lines); ++idx) {
|
||||
diff_patch_line *line = git_array_get(patch->lines, idx);
|
||||
git_diff_line *line = git_array_get(patch->lines, idx);
|
||||
if (!line)
|
||||
continue;
|
||||
|
||||
@ -726,12 +706,10 @@ static int diff_error_outofrange(const char *thing)
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
int git_diff_patch_get_hunk(
|
||||
const git_diff_range **range,
|
||||
const char **header,
|
||||
size_t *header_len,
|
||||
int git_patch_get_hunk(
|
||||
const git_diff_hunk **out,
|
||||
size_t *lines_in_hunk,
|
||||
git_diff_patch *patch,
|
||||
git_patch *patch,
|
||||
size_t hunk_idx)
|
||||
{
|
||||
diff_patch_hunk *hunk;
|
||||
@ -740,21 +718,17 @@ int git_diff_patch_get_hunk(
|
||||
hunk = git_array_get(patch->hunks, hunk_idx);
|
||||
|
||||
if (!hunk) {
|
||||
if (range) *range = NULL;
|
||||
if (header) *header = NULL;
|
||||
if (header_len) *header_len = 0;
|
||||
if (out) *out = NULL;
|
||||
if (lines_in_hunk) *lines_in_hunk = 0;
|
||||
return diff_error_outofrange("hunk");
|
||||
}
|
||||
|
||||
if (range) *range = &hunk->range;
|
||||
if (header) *header = hunk->header;
|
||||
if (header_len) *header_len = hunk->header_len;
|
||||
if (out) *out = &hunk->hunk;
|
||||
if (lines_in_hunk) *lines_in_hunk = hunk->line_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_diff_patch_num_lines_in_hunk(git_diff_patch *patch, size_t hunk_idx)
|
||||
int git_patch_num_lines_in_hunk(git_patch *patch, size_t hunk_idx)
|
||||
{
|
||||
diff_patch_hunk *hunk;
|
||||
assert(patch);
|
||||
@ -764,54 +738,35 @@ int git_diff_patch_num_lines_in_hunk(git_diff_patch *patch, size_t hunk_idx)
|
||||
return (int)hunk->line_count;
|
||||
}
|
||||
|
||||
int git_diff_patch_get_line_in_hunk(
|
||||
char *line_origin,
|
||||
const char **content,
|
||||
size_t *content_len,
|
||||
int *old_lineno,
|
||||
int *new_lineno,
|
||||
git_diff_patch *patch,
|
||||
int git_patch_get_line_in_hunk(
|
||||
const git_diff_line **out,
|
||||
git_patch *patch,
|
||||
size_t hunk_idx,
|
||||
size_t line_of_hunk)
|
||||
{
|
||||
diff_patch_hunk *hunk;
|
||||
diff_patch_line *line;
|
||||
const char *thing;
|
||||
git_diff_line *line;
|
||||
|
||||
assert(patch);
|
||||
|
||||
if (!(hunk = git_array_get(patch->hunks, hunk_idx))) {
|
||||
thing = "hunk";
|
||||
goto notfound;
|
||||
if (out) *out = NULL;
|
||||
return diff_error_outofrange("hunk");
|
||||
}
|
||||
|
||||
if (line_of_hunk >= hunk->line_count ||
|
||||
!(line = git_array_get(
|
||||
patch->lines, hunk->line_start + line_of_hunk))) {
|
||||
thing = "line";
|
||||
goto notfound;
|
||||
if (out) *out = NULL;
|
||||
return diff_error_outofrange("line");
|
||||
}
|
||||
|
||||
if (line_origin) *line_origin = line->origin;
|
||||
if (content) *content = line->ptr;
|
||||
if (content_len) *content_len = line->len;
|
||||
if (old_lineno) *old_lineno = (int)line->oldno;
|
||||
if (new_lineno) *new_lineno = (int)line->newno;
|
||||
|
||||
if (out) *out = line;
|
||||
return 0;
|
||||
|
||||
notfound:
|
||||
if (line_origin) *line_origin = GIT_DIFF_LINE_CONTEXT;
|
||||
if (content) *content = NULL;
|
||||
if (content_len) *content_len = 0;
|
||||
if (old_lineno) *old_lineno = -1;
|
||||
if (new_lineno) *new_lineno = -1;
|
||||
|
||||
return diff_error_outofrange(thing);
|
||||
}
|
||||
|
||||
size_t git_diff_patch_size(
|
||||
git_diff_patch *patch,
|
||||
size_t git_patch_size(
|
||||
git_patch *patch,
|
||||
int include_context,
|
||||
int include_hunk_headers,
|
||||
int include_file_headers)
|
||||
@ -843,36 +798,36 @@ size_t git_diff_patch_size(
|
||||
return out;
|
||||
}
|
||||
|
||||
git_diff_list *git_diff_patch__diff(git_diff_patch *patch)
|
||||
git_diff *git_patch__diff(git_patch *patch)
|
||||
{
|
||||
return patch->diff;
|
||||
}
|
||||
|
||||
git_diff_driver *git_diff_patch__driver(git_diff_patch *patch)
|
||||
git_diff_driver *git_patch__driver(git_patch *patch)
|
||||
{
|
||||
/* ofile driver is representative for whole patch */
|
||||
return patch->ofile.driver;
|
||||
}
|
||||
|
||||
void git_diff_patch__old_data(
|
||||
char **ptr, size_t *len, git_diff_patch *patch)
|
||||
void git_patch__old_data(
|
||||
char **ptr, size_t *len, git_patch *patch)
|
||||
{
|
||||
*ptr = patch->ofile.map.data;
|
||||
*len = patch->ofile.map.len;
|
||||
}
|
||||
|
||||
void git_diff_patch__new_data(
|
||||
char **ptr, size_t *len, git_diff_patch *patch)
|
||||
void git_patch__new_data(
|
||||
char **ptr, size_t *len, git_patch *patch)
|
||||
{
|
||||
*ptr = patch->nfile.map.data;
|
||||
*len = patch->nfile.map.len;
|
||||
}
|
||||
|
||||
int git_diff_patch__invoke_callbacks(
|
||||
git_diff_patch *patch,
|
||||
int git_patch__invoke_callbacks(
|
||||
git_patch *patch,
|
||||
git_diff_file_cb file_cb,
|
||||
git_diff_hunk_cb hunk_cb,
|
||||
git_diff_data_cb line_cb,
|
||||
git_diff_line_cb line_cb,
|
||||
void *payload)
|
||||
{
|
||||
int error = 0;
|
||||
@ -887,18 +842,16 @@ int git_diff_patch__invoke_callbacks(
|
||||
for (i = 0; !error && i < git_array_size(patch->hunks); ++i) {
|
||||
diff_patch_hunk *h = git_array_get(patch->hunks, i);
|
||||
|
||||
error = hunk_cb(
|
||||
patch->delta, &h->range, h->header, h->header_len, payload);
|
||||
error = hunk_cb(patch->delta, &h->hunk, payload);
|
||||
|
||||
if (!line_cb)
|
||||
continue;
|
||||
|
||||
for (j = 0; !error && j < h->line_count; ++j) {
|
||||
diff_patch_line *l =
|
||||
git_diff_line *l =
|
||||
git_array_get(patch->lines, h->line_start + j);
|
||||
|
||||
error = line_cb(
|
||||
patch->delta, &h->range, l->origin, l->ptr, l->len, payload);
|
||||
error = line_cb(patch->delta, &h->hunk, l, payload);
|
||||
}
|
||||
}
|
||||
|
||||
@ -917,12 +870,10 @@ static int diff_patch_file_cb(
|
||||
|
||||
static int diff_patch_hunk_cb(
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_range *range,
|
||||
const char *header,
|
||||
size_t header_len,
|
||||
const git_diff_hunk *hunk_,
|
||||
void *payload)
|
||||
{
|
||||
git_diff_patch *patch = payload;
|
||||
git_patch *patch = payload;
|
||||
diff_patch_hunk *hunk;
|
||||
|
||||
GIT_UNUSED(delta);
|
||||
@ -930,39 +881,28 @@ static int diff_patch_hunk_cb(
|
||||
hunk = git_array_alloc(patch->hunks);
|
||||
GITERR_CHECK_ALLOC(hunk);
|
||||
|
||||
memcpy(&hunk->range, range, sizeof(hunk->range));
|
||||
memcpy(&hunk->hunk, hunk_, sizeof(hunk->hunk));
|
||||
|
||||
assert(header_len + 1 < sizeof(hunk->header));
|
||||
memcpy(&hunk->header, header, header_len);
|
||||
hunk->header[header_len] = '\0';
|
||||
hunk->header_len = header_len;
|
||||
|
||||
patch->header_size += header_len;
|
||||
patch->header_size += hunk_->header_len;
|
||||
|
||||
hunk->line_start = git_array_size(patch->lines);
|
||||
hunk->line_count = 0;
|
||||
|
||||
patch->oldno = range->old_start;
|
||||
patch->newno = range->new_start;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int diff_patch_line_cb(
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_range *range,
|
||||
char line_origin,
|
||||
const char *content,
|
||||
size_t content_len,
|
||||
const git_diff_hunk *hunk_,
|
||||
const git_diff_line *line_,
|
||||
void *payload)
|
||||
{
|
||||
git_diff_patch *patch = payload;
|
||||
git_patch *patch = payload;
|
||||
diff_patch_hunk *hunk;
|
||||
diff_patch_line *line;
|
||||
const char *content_end = content + content_len;
|
||||
git_diff_line *line;
|
||||
|
||||
GIT_UNUSED(delta);
|
||||
GIT_UNUSED(range);
|
||||
GIT_UNUSED(hunk_);
|
||||
|
||||
hunk = git_array_last(patch->hunks);
|
||||
GITERR_CHECK_ALLOC(hunk);
|
||||
@ -970,48 +910,20 @@ static int diff_patch_line_cb(
|
||||
line = git_array_alloc(patch->lines);
|
||||
GITERR_CHECK_ALLOC(line);
|
||||
|
||||
line->ptr = content;
|
||||
line->len = content_len;
|
||||
line->origin = line_origin;
|
||||
memcpy(line, line_, sizeof(*line));
|
||||
|
||||
/* do some bookkeeping so we can provide old/new line numbers */
|
||||
|
||||
line->lines = 0;
|
||||
while (content < content_end)
|
||||
if (*content++ == '\n')
|
||||
++line->lines;
|
||||
patch->content_size += line->content_len;
|
||||
|
||||
patch->content_size += content_len;
|
||||
|
||||
switch (line_origin) {
|
||||
case GIT_DIFF_LINE_ADDITION:
|
||||
if (line->origin == GIT_DIFF_LINE_ADDITION ||
|
||||
line->origin == GIT_DIFF_LINE_DELETION)
|
||||
patch->content_size += 1;
|
||||
case GIT_DIFF_LINE_DEL_EOFNL:
|
||||
line->oldno = -1;
|
||||
line->newno = patch->newno;
|
||||
patch->newno += line->lines;
|
||||
break;
|
||||
case GIT_DIFF_LINE_DELETION:
|
||||
else if (line->origin == GIT_DIFF_LINE_CONTEXT) {
|
||||
patch->content_size += 1;
|
||||
case GIT_DIFF_LINE_ADD_EOFNL:
|
||||
line->oldno = patch->oldno;
|
||||
line->newno = -1;
|
||||
patch->oldno += line->lines;
|
||||
break;
|
||||
case GIT_DIFF_LINE_CONTEXT:
|
||||
patch->content_size += 1;
|
||||
patch->context_size += 1;
|
||||
case GIT_DIFF_LINE_CONTEXT_EOFNL:
|
||||
patch->context_size += content_len;
|
||||
line->oldno = patch->oldno;
|
||||
line->newno = patch->newno;
|
||||
patch->oldno += line->lines;
|
||||
patch->newno += line->lines;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
patch->context_size += line->content_len + 1;
|
||||
} else if (line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL)
|
||||
patch->context_size += line->content_len;
|
||||
|
||||
hunk->line_count++;
|
||||
|
||||
@ -1023,7 +935,7 @@ static void diff_output_init(
|
||||
const git_diff_options *opts,
|
||||
git_diff_file_cb file_cb,
|
||||
git_diff_hunk_cb hunk_cb,
|
||||
git_diff_data_cb data_cb,
|
||||
git_diff_line_cb data_cb,
|
||||
void *payload)
|
||||
{
|
||||
GIT_UNUSED(opts);
|
||||
@ -1036,7 +948,7 @@ static void diff_output_init(
|
||||
out->payload = payload;
|
||||
}
|
||||
|
||||
static void diff_output_to_patch(git_diff_output *out, git_diff_patch *patch)
|
||||
static void diff_output_to_patch(git_diff_output *out, git_patch *patch)
|
||||
{
|
||||
diff_output_init(
|
||||
out, NULL,
|
||||
|
||||
@ -11,19 +11,20 @@
|
||||
#include "diff.h"
|
||||
#include "diff_file.h"
|
||||
#include "array.h"
|
||||
#include "git2/patch.h"
|
||||
|
||||
extern git_diff_list *git_diff_patch__diff(git_diff_patch *);
|
||||
extern git_diff *git_patch__diff(git_patch *);
|
||||
|
||||
extern git_diff_driver *git_diff_patch__driver(git_diff_patch *);
|
||||
extern git_diff_driver *git_patch__driver(git_patch *);
|
||||
|
||||
extern void git_diff_patch__old_data(char **, size_t *, git_diff_patch *);
|
||||
extern void git_diff_patch__new_data(char **, size_t *, git_diff_patch *);
|
||||
extern void git_patch__old_data(char **, size_t *, git_patch *);
|
||||
extern void git_patch__new_data(char **, size_t *, git_patch *);
|
||||
|
||||
extern int git_diff_patch__invoke_callbacks(
|
||||
git_diff_patch *patch,
|
||||
extern int git_patch__invoke_callbacks(
|
||||
git_patch *patch,
|
||||
git_diff_file_cb file_cb,
|
||||
git_diff_hunk_cb hunk_cb,
|
||||
git_diff_data_cb line_cb,
|
||||
git_diff_line_cb line_cb,
|
||||
void *payload);
|
||||
|
||||
typedef struct git_diff_output git_diff_output;
|
||||
@ -31,7 +32,7 @@ struct git_diff_output {
|
||||
/* these callbacks are issued with the diff data */
|
||||
git_diff_file_cb file_cb;
|
||||
git_diff_hunk_cb hunk_cb;
|
||||
git_diff_data_cb data_cb;
|
||||
git_diff_line_cb data_cb;
|
||||
void *payload;
|
||||
|
||||
/* this records the actual error in cases where it may be obscured */
|
||||
@ -40,7 +41,7 @@ struct git_diff_output {
|
||||
/* this callback is used to do the diff and drive the other callbacks.
|
||||
* see diff_xdiff.h for how to use this in practice for now.
|
||||
*/
|
||||
int (*diff_cb)(git_diff_output *output, git_diff_patch *patch);
|
||||
int (*diff_cb)(git_diff_output *output, git_patch *patch);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
241
src/diff_print.c
241
src/diff_print.c
@ -10,23 +10,36 @@
|
||||
#include "fileops.h"
|
||||
|
||||
typedef struct {
|
||||
git_diff_list *diff;
|
||||
git_diff_data_cb print_cb;
|
||||
git_diff *diff;
|
||||
git_diff_format_t format;
|
||||
git_diff_line_cb print_cb;
|
||||
void *payload;
|
||||
git_buf *buf;
|
||||
uint32_t flags;
|
||||
int oid_strlen;
|
||||
git_diff_line line;
|
||||
} diff_print_info;
|
||||
|
||||
static int diff_print_info_init(
|
||||
diff_print_info *pi,
|
||||
git_buf *out, git_diff_list *diff, git_diff_data_cb cb, void *payload)
|
||||
git_buf *out,
|
||||
git_diff *diff,
|
||||
git_diff_format_t format,
|
||||
git_diff_line_cb cb,
|
||||
void *payload)
|
||||
{
|
||||
pi->diff = diff;
|
||||
pi->format = format;
|
||||
pi->print_cb = cb;
|
||||
pi->payload = payload;
|
||||
pi->buf = out;
|
||||
|
||||
if (!diff || !diff->repo)
|
||||
if (diff)
|
||||
pi->flags = diff->opts.flags;
|
||||
|
||||
if (diff && diff->opts.oid_abbrev != 0)
|
||||
pi->oid_strlen = diff->opts.oid_abbrev;
|
||||
else if (!diff || !diff->repo)
|
||||
pi->oid_strlen = GIT_ABBREV_DEFAULT;
|
||||
else if (git_repository__cvar(
|
||||
&pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0)
|
||||
@ -39,6 +52,11 @@ static int diff_print_info_init(
|
||||
else if (pi->oid_strlen > GIT_OID_HEXSZ + 1)
|
||||
pi->oid_strlen = GIT_OID_HEXSZ + 1;
|
||||
|
||||
memset(&pi->line, 0, sizeof(pi->line));
|
||||
pi->line.old_lineno = -1;
|
||||
pi->line.new_lineno = -1;
|
||||
pi->line.num_lines = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -77,7 +95,35 @@ static int callback_error(void)
|
||||
return GIT_EUSER;
|
||||
}
|
||||
|
||||
static int diff_print_one_compact(
|
||||
static int diff_print_one_name_only(
|
||||
const git_diff_delta *delta, float progress, void *data)
|
||||
{
|
||||
diff_print_info *pi = data;
|
||||
git_buf *out = pi->buf;
|
||||
|
||||
GIT_UNUSED(progress);
|
||||
|
||||
if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 &&
|
||||
delta->status == GIT_DELTA_UNMODIFIED)
|
||||
return 0;
|
||||
|
||||
git_buf_clear(out);
|
||||
|
||||
if (git_buf_puts(out, delta->new_file.path) < 0 ||
|
||||
git_buf_putc(out, '\n'))
|
||||
return -1;
|
||||
|
||||
pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
|
||||
pi->line.content = git_buf_cstr(out);
|
||||
pi->line.content_len = git_buf_len(out);
|
||||
|
||||
if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
|
||||
return callback_error();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int diff_print_one_name_status(
|
||||
const git_diff_delta *delta, float progress, void *data)
|
||||
{
|
||||
diff_print_info *pi = data;
|
||||
@ -88,7 +134,7 @@ static int diff_print_one_compact(
|
||||
|
||||
GIT_UNUSED(progress);
|
||||
|
||||
if (code == ' ')
|
||||
if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
|
||||
return 0;
|
||||
|
||||
old_suffix = diff_pick_suffix(delta->old_file.mode);
|
||||
@ -112,31 +158,16 @@ static int diff_print_one_compact(
|
||||
if (git_buf_oom(out))
|
||||
return -1;
|
||||
|
||||
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
|
||||
git_buf_cstr(out), git_buf_len(out), pi->payload))
|
||||
pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
|
||||
pi->line.content = git_buf_cstr(out);
|
||||
pi->line.content_len = git_buf_len(out);
|
||||
|
||||
if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
|
||||
return callback_error();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* print a git_diff_list to a print callback in compact format */
|
||||
int git_diff_print_compact(
|
||||
git_diff_list *diff,
|
||||
git_diff_data_cb print_cb,
|
||||
void *payload)
|
||||
{
|
||||
int error;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
diff_print_info pi;
|
||||
|
||||
if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
|
||||
error = git_diff_foreach(diff, diff_print_one_compact, NULL, NULL, &pi);
|
||||
|
||||
git_buf_free(&buf);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int diff_print_one_raw(
|
||||
const git_diff_delta *delta, float progress, void *data)
|
||||
{
|
||||
@ -147,7 +178,7 @@ static int diff_print_one_raw(
|
||||
|
||||
GIT_UNUSED(progress);
|
||||
|
||||
if (code == ' ')
|
||||
if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
|
||||
return 0;
|
||||
|
||||
git_buf_clear(out);
|
||||
@ -173,31 +204,16 @@ static int diff_print_one_raw(
|
||||
if (git_buf_oom(out))
|
||||
return -1;
|
||||
|
||||
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
|
||||
git_buf_cstr(out), git_buf_len(out), pi->payload))
|
||||
pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
|
||||
pi->line.content = git_buf_cstr(out);
|
||||
pi->line.content_len = git_buf_len(out);
|
||||
|
||||
if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
|
||||
return callback_error();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* print a git_diff_list to a print callback in raw output format */
|
||||
int git_diff_print_raw(
|
||||
git_diff_list *diff,
|
||||
git_diff_data_cb print_cb,
|
||||
void *payload)
|
||||
{
|
||||
int error;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
diff_print_info pi;
|
||||
|
||||
if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
|
||||
error = git_diff_foreach(diff, diff_print_one_raw, NULL, NULL, &pi);
|
||||
|
||||
git_buf_free(&buf);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int diff_print_oid_range(
|
||||
git_buf *out, const git_diff_delta *delta, int oid_strlen)
|
||||
{
|
||||
@ -287,7 +303,6 @@ static int diff_print_patch_file(
|
||||
pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT;
|
||||
const char *newpfx =
|
||||
pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT;
|
||||
uint32_t opts_flags = pi->diff ? pi->diff->opts.flags : GIT_DIFF_NORMAL;
|
||||
|
||||
GIT_UNUSED(progress);
|
||||
|
||||
@ -295,15 +310,18 @@ static int diff_print_patch_file(
|
||||
delta->status == GIT_DELTA_UNMODIFIED ||
|
||||
delta->status == GIT_DELTA_IGNORED ||
|
||||
(delta->status == GIT_DELTA_UNTRACKED &&
|
||||
(opts_flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0))
|
||||
(pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
|
||||
return 0;
|
||||
|
||||
if (git_diff_delta__format_file_header(
|
||||
pi->buf, delta, oldpfx, newpfx, pi->oid_strlen) < 0)
|
||||
return -1;
|
||||
|
||||
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
|
||||
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||
pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
|
||||
pi->line.content = git_buf_cstr(pi->buf);
|
||||
pi->line.content_len = git_buf_len(pi->buf);
|
||||
|
||||
if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
|
||||
return callback_error();
|
||||
|
||||
if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
|
||||
@ -316,8 +334,12 @@ static int diff_print_patch_file(
|
||||
"Binary files %s%s and %s%s differ\n") < 0)
|
||||
return -1;
|
||||
|
||||
if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_BINARY,
|
||||
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||
pi->line.origin = GIT_DIFF_LINE_BINARY;
|
||||
pi->line.content = git_buf_cstr(pi->buf);
|
||||
pi->line.content_len = git_buf_len(pi->buf);
|
||||
pi->line.num_lines = 1;
|
||||
|
||||
if (pi->print_cb(delta, NULL, &pi->line, pi->payload))
|
||||
return callback_error();
|
||||
|
||||
return 0;
|
||||
@ -325,9 +347,7 @@ static int diff_print_patch_file(
|
||||
|
||||
static int diff_print_patch_hunk(
|
||||
const git_diff_delta *d,
|
||||
const git_diff_range *r,
|
||||
const char *header,
|
||||
size_t header_len,
|
||||
const git_diff_hunk *h,
|
||||
void *data)
|
||||
{
|
||||
diff_print_info *pi = data;
|
||||
@ -335,12 +355,11 @@ static int diff_print_patch_hunk(
|
||||
if (S_ISDIR(d->new_file.mode))
|
||||
return 0;
|
||||
|
||||
git_buf_clear(pi->buf);
|
||||
if (git_buf_put(pi->buf, header, header_len) < 0)
|
||||
return -1;
|
||||
pi->line.origin = GIT_DIFF_LINE_HUNK_HDR;
|
||||
pi->line.content = h->header;
|
||||
pi->line.content_len = h->header_len;
|
||||
|
||||
if (pi->print_cb(d, r, GIT_DIFF_LINE_HUNK_HDR,
|
||||
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||
if (pi->print_cb(d, h, &pi->line, pi->payload))
|
||||
return callback_error();
|
||||
|
||||
return 0;
|
||||
@ -348,10 +367,8 @@ static int diff_print_patch_hunk(
|
||||
|
||||
static int diff_print_patch_line(
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_range *range,
|
||||
char line_origin, /* GIT_DIFF_LINE value from above */
|
||||
const char *content,
|
||||
size_t content_len,
|
||||
const git_diff_hunk *hunk,
|
||||
const git_diff_line *line,
|
||||
void *data)
|
||||
{
|
||||
diff_print_info *pi = data;
|
||||
@ -359,50 +376,63 @@ static int diff_print_patch_line(
|
||||
if (S_ISDIR(delta->new_file.mode))
|
||||
return 0;
|
||||
|
||||
git_buf_clear(pi->buf);
|
||||
git_buf_grow(pi->buf, content_len + 2);
|
||||
|
||||
if (line_origin == GIT_DIFF_LINE_ADDITION ||
|
||||
line_origin == GIT_DIFF_LINE_DELETION ||
|
||||
line_origin == GIT_DIFF_LINE_CONTEXT)
|
||||
git_buf_putc(pi->buf, line_origin);
|
||||
|
||||
git_buf_put(pi->buf, content, content_len);
|
||||
|
||||
if (git_buf_oom(pi->buf))
|
||||
return -1;
|
||||
|
||||
if (pi->print_cb(delta, range, line_origin,
|
||||
git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
|
||||
if (pi->print_cb(delta, hunk, line, pi->payload))
|
||||
return callback_error();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* print a git_diff_list to an output callback in patch format */
|
||||
int git_diff_print_patch(
|
||||
git_diff_list *diff,
|
||||
git_diff_data_cb print_cb,
|
||||
/* print a git_diff to an output callback */
|
||||
int git_diff_print(
|
||||
git_diff *diff,
|
||||
git_diff_format_t format,
|
||||
git_diff_line_cb print_cb,
|
||||
void *payload)
|
||||
{
|
||||
int error;
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
diff_print_info pi;
|
||||
git_diff_file_cb print_file = NULL;
|
||||
git_diff_hunk_cb print_hunk = NULL;
|
||||
git_diff_line_cb print_line = NULL;
|
||||
|
||||
if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
|
||||
switch (format) {
|
||||
case GIT_DIFF_FORMAT_PATCH:
|
||||
print_file = diff_print_patch_file;
|
||||
print_hunk = diff_print_patch_hunk;
|
||||
print_line = diff_print_patch_line;
|
||||
break;
|
||||
case GIT_DIFF_FORMAT_PATCH_HEADER:
|
||||
print_file = diff_print_patch_file;
|
||||
break;
|
||||
case GIT_DIFF_FORMAT_RAW:
|
||||
print_file = diff_print_one_raw;
|
||||
break;
|
||||
case GIT_DIFF_FORMAT_NAME_ONLY:
|
||||
print_file = diff_print_one_name_only;
|
||||
break;
|
||||
case GIT_DIFF_FORMAT_NAME_STATUS:
|
||||
print_file = diff_print_one_name_status;
|
||||
break;
|
||||
default:
|
||||
giterr_set(GITERR_INVALID, "Unknown diff output format (%d)", format);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(error = diff_print_info_init(
|
||||
&pi, &buf, diff, format, print_cb, payload)))
|
||||
error = git_diff_foreach(
|
||||
diff, diff_print_patch_file, diff_print_patch_hunk,
|
||||
diff_print_patch_line, &pi);
|
||||
diff, print_file, print_hunk, print_line, &pi);
|
||||
|
||||
git_buf_free(&buf);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* print a git_diff_patch to an output callback */
|
||||
int git_diff_patch_print(
|
||||
git_diff_patch *patch,
|
||||
git_diff_data_cb print_cb,
|
||||
/* print a git_patch to an output callback */
|
||||
int git_patch_print(
|
||||
git_patch *patch,
|
||||
git_diff_line_cb print_cb,
|
||||
void *payload)
|
||||
{
|
||||
int error;
|
||||
@ -412,8 +442,9 @@ int git_diff_patch_print(
|
||||
assert(patch && print_cb);
|
||||
|
||||
if (!(error = diff_print_info_init(
|
||||
&pi, &temp, git_diff_patch__diff(patch), print_cb, payload)))
|
||||
error = git_diff_patch__invoke_callbacks(
|
||||
&pi, &temp, git_patch__diff(patch),
|
||||
GIT_DIFF_FORMAT_PATCH, print_cb, payload)))
|
||||
error = git_patch__invoke_callbacks(
|
||||
patch, diff_print_patch_file, diff_print_patch_hunk,
|
||||
diff_print_patch_line, &pi);
|
||||
|
||||
@ -424,26 +455,30 @@ int git_diff_patch_print(
|
||||
|
||||
static int diff_print_to_buffer_cb(
|
||||
const git_diff_delta *delta,
|
||||
const git_diff_range *range,
|
||||
char line_origin,
|
||||
const char *content,
|
||||
size_t content_len,
|
||||
const git_diff_hunk *hunk,
|
||||
const git_diff_line *line,
|
||||
void *payload)
|
||||
{
|
||||
git_buf *output = payload;
|
||||
GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(line_origin);
|
||||
return git_buf_put(output, content, content_len);
|
||||
GIT_UNUSED(delta); GIT_UNUSED(hunk);
|
||||
|
||||
if (line->origin == GIT_DIFF_LINE_ADDITION ||
|
||||
line->origin == GIT_DIFF_LINE_DELETION ||
|
||||
line->origin == GIT_DIFF_LINE_CONTEXT)
|
||||
git_buf_putc(output, line->origin);
|
||||
|
||||
return git_buf_put(output, line->content, line->content_len);
|
||||
}
|
||||
|
||||
/* print a git_diff_patch to a string buffer */
|
||||
int git_diff_patch_to_str(
|
||||
/* print a git_patch to a string buffer */
|
||||
int git_patch_to_str(
|
||||
char **string,
|
||||
git_diff_patch *patch)
|
||||
git_patch *patch)
|
||||
{
|
||||
int error;
|
||||
git_buf output = GIT_BUF_INIT;
|
||||
|
||||
error = git_diff_patch_print(patch, diff_print_to_buffer_cb, &output);
|
||||
error = git_patch_print(patch, diff_print_to_buffer_cb, &output);
|
||||
|
||||
/* GIT_EUSER means git_buf_put in print_to_buffer_cb returned -1,
|
||||
* meaning a memory allocation failure, so just map to -1...
|
||||
|
||||
@ -97,8 +97,8 @@ static git_diff_delta *diff_delta__merge_like_cgit(
|
||||
}
|
||||
|
||||
int git_diff_merge(
|
||||
git_diff_list *onto,
|
||||
const git_diff_list *from)
|
||||
git_diff *onto,
|
||||
const git_diff *from)
|
||||
{
|
||||
int error = 0;
|
||||
git_pool onto_pool;
|
||||
@ -117,15 +117,15 @@ int git_diff_merge(
|
||||
git_pool_init(&onto_pool, 1, 0) < 0)
|
||||
return -1;
|
||||
|
||||
if ((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 ||
|
||||
(from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0)
|
||||
if ((onto->opts.flags & GIT_DIFF_IGNORE_CASE) != 0 ||
|
||||
(from->opts.flags & GIT_DIFF_IGNORE_CASE) != 0)
|
||||
{
|
||||
ignore_case = true;
|
||||
|
||||
/* This function currently only supports merging diff lists that
|
||||
* are sorted identically. */
|
||||
assert((onto->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0 &&
|
||||
(from->opts.flags & GIT_DIFF_DELTAS_ARE_ICASE) != 0);
|
||||
assert((onto->opts.flags & GIT_DIFF_IGNORE_CASE) != 0 &&
|
||||
(from->opts.flags & GIT_DIFF_IGNORE_CASE) != 0);
|
||||
}
|
||||
|
||||
for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
|
||||
@ -230,9 +230,9 @@ int git_diff_find_similar__calc_similarity(
|
||||
#define DEFAULT_RENAME_LIMIT 200
|
||||
|
||||
static int normalize_find_opts(
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
git_diff_find_options *opts,
|
||||
git_diff_find_options *given)
|
||||
const git_diff_find_options *given)
|
||||
{
|
||||
git_config *cfg = NULL;
|
||||
|
||||
@ -328,7 +328,7 @@ static int normalize_find_opts(
|
||||
}
|
||||
|
||||
static int apply_splits_and_deletes(
|
||||
git_diff_list *diff, size_t expected_size, bool actually_split)
|
||||
git_diff *diff, size_t expected_size, bool actually_split)
|
||||
{
|
||||
git_vector onto = GIT_VECTOR_INIT;
|
||||
size_t i;
|
||||
@ -350,6 +350,7 @@ static int apply_splits_and_deletes(
|
||||
goto on_error;
|
||||
|
||||
deleted->status = GIT_DELTA_DELETED;
|
||||
deleted->nfiles = 1;
|
||||
memset(&deleted->new_file, 0, sizeof(deleted->new_file));
|
||||
deleted->new_file.path = deleted->old_file.path;
|
||||
deleted->new_file.flags |= GIT_DIFF_FLAG_VALID_OID;
|
||||
@ -361,6 +362,7 @@ static int apply_splits_and_deletes(
|
||||
delta->status = GIT_DELTA_UNTRACKED;
|
||||
else
|
||||
delta->status = GIT_DELTA_ADDED;
|
||||
delta->nfiles = 1;
|
||||
memset(&delta->old_file, 0, sizeof(delta->old_file));
|
||||
delta->old_file.path = delta->new_file.path;
|
||||
delta->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
|
||||
@ -402,7 +404,7 @@ on_error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
GIT_INLINE(git_diff_file *) similarity_get_file(git_diff_list *diff, size_t idx)
|
||||
GIT_INLINE(git_diff_file *) similarity_get_file(git_diff *diff, size_t idx)
|
||||
{
|
||||
git_diff_delta *delta = git_vector_get(&diff->deltas, idx / 2);
|
||||
return (idx & 1) ? &delta->new_file : &delta->old_file;
|
||||
@ -419,7 +421,7 @@ typedef struct {
|
||||
} similarity_info;
|
||||
|
||||
static int similarity_init(
|
||||
similarity_info *info, git_diff_list *diff, size_t file_idx)
|
||||
similarity_info *info, git_diff *diff, size_t file_idx)
|
||||
{
|
||||
info->idx = file_idx;
|
||||
info->src = (file_idx & 1) ? diff->new_src : diff->old_src;
|
||||
@ -509,7 +511,7 @@ static void similarity_unload(similarity_info *info)
|
||||
*/
|
||||
static int similarity_measure(
|
||||
int *score,
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
const git_diff_find_options *opts,
|
||||
void **cache,
|
||||
size_t a_idx,
|
||||
@ -595,7 +597,7 @@ cleanup:
|
||||
}
|
||||
|
||||
static int calc_self_similarity(
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
const git_diff_find_options *opts,
|
||||
size_t delta_idx,
|
||||
void **cache)
|
||||
@ -612,7 +614,7 @@ static int calc_self_similarity(
|
||||
return error;
|
||||
|
||||
if (similarity >= 0) {
|
||||
delta->similarity = (uint32_t)similarity;
|
||||
delta->similarity = (uint16_t)similarity;
|
||||
delta->flags |= GIT_DIFF_FLAG__HAS_SELF_SIMILARITY;
|
||||
}
|
||||
|
||||
@ -620,7 +622,7 @@ static int calc_self_similarity(
|
||||
}
|
||||
|
||||
static bool is_rename_target(
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
const git_diff_find_options *opts,
|
||||
size_t delta_idx,
|
||||
void **cache)
|
||||
@ -675,7 +677,7 @@ static bool is_rename_target(
|
||||
}
|
||||
|
||||
static bool is_rename_source(
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
const git_diff_find_options *opts,
|
||||
size_t delta_idx,
|
||||
void **cache)
|
||||
@ -745,25 +747,27 @@ GIT_INLINE(bool) delta_is_new_only(git_diff_delta *delta)
|
||||
}
|
||||
|
||||
GIT_INLINE(void) delta_make_rename(
|
||||
git_diff_delta *to, const git_diff_delta *from, uint32_t similarity)
|
||||
git_diff_delta *to, const git_diff_delta *from, uint16_t similarity)
|
||||
{
|
||||
to->status = GIT_DELTA_RENAMED;
|
||||
to->similarity = similarity;
|
||||
to->nfiles = 2;
|
||||
memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
|
||||
to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint32_t idx;
|
||||
uint32_t similarity;
|
||||
size_t idx;
|
||||
uint16_t similarity;
|
||||
} diff_find_match;
|
||||
|
||||
int git_diff_find_similar(
|
||||
git_diff_list *diff,
|
||||
git_diff_find_options *given_opts)
|
||||
git_diff *diff,
|
||||
const git_diff_find_options *given_opts)
|
||||
{
|
||||
size_t s, t;
|
||||
int error = 0, similarity;
|
||||
int error = 0, result;
|
||||
uint16_t similarity;
|
||||
git_diff_delta *src, *tgt;
|
||||
git_diff_find_options opts;
|
||||
size_t num_deltas, num_srcs = 0, num_tgts = 0;
|
||||
@ -839,17 +843,18 @@ find_best_matches:
|
||||
|
||||
/* calculate similarity for this pair and find best match */
|
||||
if (s == t)
|
||||
similarity = -1; /* don't measure self-similarity here */
|
||||
result = -1; /* don't measure self-similarity here */
|
||||
else if ((error = similarity_measure(
|
||||
&similarity, diff, &opts, sigcache, 2 * s, 2 * t + 1)) < 0)
|
||||
&result, diff, &opts, sigcache, 2 * s, 2 * t + 1)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (similarity < 0)
|
||||
if (result < 0)
|
||||
continue;
|
||||
similarity = (uint16_t)result;
|
||||
|
||||
/* is this a better rename? */
|
||||
if (tgt2src[t].similarity < (uint32_t)similarity &&
|
||||
src2tgt[s].similarity < (uint32_t)similarity)
|
||||
if (tgt2src[t].similarity < similarity &&
|
||||
src2tgt[s].similarity < similarity)
|
||||
{
|
||||
/* eject old mapping */
|
||||
if (src2tgt[s].similarity > 0) {
|
||||
@ -862,18 +867,18 @@ find_best_matches:
|
||||
}
|
||||
|
||||
/* write new mapping */
|
||||
tgt2src[t].idx = (uint32_t)s;
|
||||
tgt2src[t].similarity = (uint32_t)similarity;
|
||||
src2tgt[s].idx = (uint32_t)t;
|
||||
src2tgt[s].similarity = (uint32_t)similarity;
|
||||
tgt2src[t].idx = s;
|
||||
tgt2src[t].similarity = similarity;
|
||||
src2tgt[s].idx = t;
|
||||
src2tgt[s].similarity = similarity;
|
||||
}
|
||||
|
||||
/* keep best absolute match for copies */
|
||||
if (tgt2src_copy != NULL &&
|
||||
tgt2src_copy[t].similarity < (uint32_t)similarity)
|
||||
tgt2src_copy[t].similarity < similarity)
|
||||
{
|
||||
tgt2src_copy[t].idx = (uint32_t)s;
|
||||
tgt2src_copy[t].similarity = (uint32_t)similarity;
|
||||
tgt2src_copy[t].idx = s;
|
||||
tgt2src_copy[t].similarity = similarity;
|
||||
}
|
||||
|
||||
if (++tried_srcs >= num_srcs)
|
||||
@ -943,7 +948,7 @@ find_best_matches:
|
||||
delta_make_rename(tgt, src, best_match->similarity);
|
||||
num_rewrites--;
|
||||
|
||||
src->status = GIT_DELTA_DELETED;
|
||||
assert(src->status == GIT_DELTA_DELETED);
|
||||
memcpy(&src->old_file, &swap, sizeof(src->old_file));
|
||||
memset(&src->new_file, 0, sizeof(src->new_file));
|
||||
src->new_file.path = src->old_file.path;
|
||||
@ -953,7 +958,7 @@ find_best_matches:
|
||||
|
||||
if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) {
|
||||
/* what used to be at src t is now at src s */
|
||||
tgt2src[src2tgt[t].idx].idx = (uint32_t)s;
|
||||
tgt2src[src2tgt[t].idx].idx = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -969,6 +974,7 @@ find_best_matches:
|
||||
|
||||
src->status = (diff->new_src == GIT_ITERATOR_TYPE_WORKDIR) ?
|
||||
GIT_DELTA_UNTRACKED : GIT_DELTA_ADDED;
|
||||
src->nfiles = 1;
|
||||
memset(&src->old_file, 0, sizeof(src->old_file));
|
||||
src->old_file.path = src->new_file.path;
|
||||
src->old_file.flags |= GIT_DIFF_FLAG_VALID_OID;
|
||||
@ -1006,7 +1012,7 @@ find_best_matches:
|
||||
/* otherwise, if we just overwrote a source, update mapping */
|
||||
else if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) {
|
||||
/* what used to be at src t is now at src s */
|
||||
tgt2src[src2tgt[t].idx].idx = (uint32_t)s;
|
||||
tgt2src[src2tgt[t].idx].idx = s;
|
||||
}
|
||||
|
||||
num_updates++;
|
||||
@ -1026,6 +1032,7 @@ find_best_matches:
|
||||
|
||||
tgt->status = GIT_DELTA_COPIED;
|
||||
tgt->similarity = best_match->similarity;
|
||||
tgt->nfiles = 2;
|
||||
memcpy(&tgt->old_file, &src->old_file, sizeof(tgt->old_file));
|
||||
|
||||
num_updates++;
|
||||
|
||||
122
src/diff_xdiff.c
122
src/diff_xdiff.c
@ -24,26 +24,26 @@ static int git_xdiff_scan_int(const char **str, int *value)
|
||||
return (digits > 0) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int git_xdiff_parse_hunk(git_diff_range *range, const char *header)
|
||||
static int git_xdiff_parse_hunk(git_diff_hunk *hunk, const char *header)
|
||||
{
|
||||
/* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
|
||||
if (*header != '@')
|
||||
return -1;
|
||||
if (git_xdiff_scan_int(&header, &range->old_start) < 0)
|
||||
if (git_xdiff_scan_int(&header, &hunk->old_start) < 0)
|
||||
return -1;
|
||||
if (*header == ',') {
|
||||
if (git_xdiff_scan_int(&header, &range->old_lines) < 0)
|
||||
if (git_xdiff_scan_int(&header, &hunk->old_lines) < 0)
|
||||
return -1;
|
||||
} else
|
||||
range->old_lines = 1;
|
||||
if (git_xdiff_scan_int(&header, &range->new_start) < 0)
|
||||
hunk->old_lines = 1;
|
||||
if (git_xdiff_scan_int(&header, &hunk->new_start) < 0)
|
||||
return -1;
|
||||
if (*header == ',') {
|
||||
if (git_xdiff_scan_int(&header, &range->new_lines) < 0)
|
||||
if (git_xdiff_scan_int(&header, &hunk->new_lines) < 0)
|
||||
return -1;
|
||||
} else
|
||||
range->new_lines = 1;
|
||||
if (range->old_start < 0 || range->new_start < 0)
|
||||
hunk->new_lines = 1;
|
||||
if (hunk->old_start < 0 || hunk->new_start < 0)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
@ -51,38 +51,96 @@ static int git_xdiff_parse_hunk(git_diff_range *range, const char *header)
|
||||
|
||||
typedef struct {
|
||||
git_xdiff_output *xo;
|
||||
git_diff_patch *patch;
|
||||
git_diff_range range;
|
||||
git_patch *patch;
|
||||
git_diff_hunk hunk;
|
||||
int old_lineno, new_lineno;
|
||||
} git_xdiff_info;
|
||||
|
||||
static int diff_update_lines(
|
||||
git_xdiff_info *info,
|
||||
git_diff_line *line,
|
||||
const char *content,
|
||||
size_t content_len)
|
||||
{
|
||||
const char *scan = content, *scan_end = content + content_len;
|
||||
|
||||
for (line->num_lines = 0; scan < scan_end; ++scan)
|
||||
if (*scan == '\n')
|
||||
++line->num_lines;
|
||||
|
||||
line->content = content;
|
||||
line->content_len = content_len;
|
||||
|
||||
/* expect " "/"-"/"+", then data */
|
||||
switch (line->origin) {
|
||||
case GIT_DIFF_LINE_ADDITION:
|
||||
case GIT_DIFF_LINE_DEL_EOFNL:
|
||||
line->old_lineno = -1;
|
||||
line->new_lineno = info->new_lineno;
|
||||
info->new_lineno += (int)line->num_lines;
|
||||
break;
|
||||
case GIT_DIFF_LINE_DELETION:
|
||||
case GIT_DIFF_LINE_ADD_EOFNL:
|
||||
line->old_lineno = info->old_lineno;
|
||||
line->new_lineno = -1;
|
||||
info->old_lineno += (int)line->num_lines;
|
||||
break;
|
||||
case GIT_DIFF_LINE_CONTEXT:
|
||||
case GIT_DIFF_LINE_CONTEXT_EOFNL:
|
||||
line->old_lineno = info->old_lineno;
|
||||
line->new_lineno = info->new_lineno;
|
||||
info->old_lineno += (int)line->num_lines;
|
||||
info->new_lineno += (int)line->num_lines;
|
||||
break;
|
||||
default:
|
||||
giterr_set(GITERR_INVALID, "Unknown diff line origin %02x",
|
||||
(unsigned int)line->origin);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
|
||||
{
|
||||
git_xdiff_info *info = priv;
|
||||
git_diff_patch *patch = info->patch;
|
||||
const git_diff_delta *delta = git_diff_patch_delta(patch);
|
||||
git_patch *patch = info->patch;
|
||||
const git_diff_delta *delta = git_patch_get_delta(patch);
|
||||
git_diff_output *output = &info->xo->output;
|
||||
git_diff_line line;
|
||||
|
||||
if (len == 1) {
|
||||
output->error = git_xdiff_parse_hunk(&info->range, bufs[0].ptr);
|
||||
output->error = git_xdiff_parse_hunk(&info->hunk, bufs[0].ptr);
|
||||
if (output->error < 0)
|
||||
return output->error;
|
||||
|
||||
info->hunk.header_len = bufs[0].size;
|
||||
if (info->hunk.header_len >= sizeof(info->hunk.header))
|
||||
info->hunk.header_len = sizeof(info->hunk.header) - 1;
|
||||
memcpy(info->hunk.header, bufs[0].ptr, info->hunk.header_len);
|
||||
info->hunk.header[info->hunk.header_len] = '\0';
|
||||
|
||||
if (output->hunk_cb != NULL &&
|
||||
output->hunk_cb(delta, &info->range,
|
||||
bufs[0].ptr, bufs[0].size, output->payload))
|
||||
output->hunk_cb(delta, &info->hunk, output->payload))
|
||||
output->error = GIT_EUSER;
|
||||
|
||||
info->old_lineno = info->hunk.old_start;
|
||||
info->new_lineno = info->hunk.new_start;
|
||||
}
|
||||
|
||||
if (len == 2 || len == 3) {
|
||||
/* expect " "/"-"/"+", then data */
|
||||
char origin =
|
||||
line.origin =
|
||||
(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
|
||||
(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
|
||||
GIT_DIFF_LINE_CONTEXT;
|
||||
|
||||
if (output->data_cb != NULL &&
|
||||
output->data_cb(delta, &info->range,
|
||||
origin, bufs[1].ptr, bufs[1].size, output->payload))
|
||||
output->error = diff_update_lines(
|
||||
info, &line, bufs[1].ptr, bufs[1].size);
|
||||
|
||||
if (!output->error &&
|
||||
output->data_cb != NULL &&
|
||||
output->data_cb(delta, &info->hunk, &line, output->payload))
|
||||
output->error = GIT_EUSER;
|
||||
}
|
||||
|
||||
@ -92,21 +150,24 @@ static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
|
||||
* If we have a '-' and a third buf, then we have removed a line
|
||||
* with out a newline but added a blank line, so ADD_EOFNL.
|
||||
*/
|
||||
char origin =
|
||||
line.origin =
|
||||
(*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL :
|
||||
(*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL :
|
||||
GIT_DIFF_LINE_CONTEXT_EOFNL;
|
||||
|
||||
if (output->data_cb != NULL &&
|
||||
output->data_cb(delta, &info->range,
|
||||
origin, bufs[2].ptr, bufs[2].size, output->payload))
|
||||
output->error = diff_update_lines(
|
||||
info, &line, bufs[2].ptr, bufs[2].size);
|
||||
|
||||
if (!output->error &&
|
||||
output->data_cb != NULL &&
|
||||
output->data_cb(delta, &info->hunk, &line, output->payload))
|
||||
output->error = GIT_EUSER;
|
||||
}
|
||||
|
||||
return output->error;
|
||||
}
|
||||
|
||||
static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
|
||||
static int git_xdiff(git_diff_output *output, git_patch *patch)
|
||||
{
|
||||
git_xdiff_output *xo = (git_xdiff_output *)output;
|
||||
git_xdiff_info info;
|
||||
@ -120,7 +181,7 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
|
||||
xo->callback.priv = &info;
|
||||
|
||||
git_diff_find_context_init(
|
||||
&xo->config.find_func, &findctxt, git_diff_patch__driver(patch));
|
||||
&xo->config.find_func, &findctxt, git_patch__driver(patch));
|
||||
xo->config.find_func_priv = &findctxt;
|
||||
|
||||
if (xo->config.find_func != NULL)
|
||||
@ -132,8 +193,8 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
|
||||
* updates are needed to xo->params.flags
|
||||
*/
|
||||
|
||||
git_diff_patch__old_data(&xd_old_data.ptr, &xd_old_data.size, patch);
|
||||
git_diff_patch__new_data(&xd_new_data.ptr, &xd_new_data.size, patch);
|
||||
git_patch__old_data(&xd_old_data.ptr, &xd_old_data.size, patch);
|
||||
git_patch__new_data(&xd_new_data.ptr, &xd_new_data.size, patch);
|
||||
|
||||
xdl_diff(&xd_old_data, &xd_new_data,
|
||||
&xo->params, &xo->config, &xo->callback);
|
||||
@ -145,7 +206,7 @@ static int git_xdiff(git_diff_output *output, git_diff_patch *patch)
|
||||
|
||||
void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts)
|
||||
{
|
||||
uint32_t flags = opts ? opts->flags : GIT_DIFF_NORMAL;
|
||||
uint32_t flags = opts ? opts->flags : 0;
|
||||
|
||||
xo->output.diff_cb = git_xdiff;
|
||||
|
||||
@ -161,6 +222,11 @@ void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts)
|
||||
if (flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
|
||||
xo->params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
|
||||
|
||||
if (flags & GIT_DIFF_PATIENCE)
|
||||
xo->params.flags |= XDF_PATIENCE_DIFF;
|
||||
if (flags & GIT_DIFF_MINIMAL)
|
||||
xo->params.flags |= XDF_NEED_MINIMAL;
|
||||
|
||||
memset(&xo->callback, 0, sizeof(xo->callback));
|
||||
xo->callback.outf = git_xdiff_cb;
|
||||
}
|
||||
|
||||
@ -116,4 +116,3 @@ const git_error *giterr_last(void)
|
||||
{
|
||||
return GIT_GLOBAL->last_error;
|
||||
}
|
||||
|
||||
|
||||
136
src/fileops.c
136
src/fileops.c
@ -78,11 +78,8 @@ int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, con
|
||||
int git_futils_open_ro(const char *path)
|
||||
{
|
||||
int fd = p_open(path, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
if (errno == ENOENT || errno == ENOTDIR)
|
||||
fd = GIT_ENOTFOUND;
|
||||
giterr_set(GITERR_OS, "Failed to open '%s'", path);
|
||||
}
|
||||
if (fd < 0)
|
||||
return git_path_set_error(errno, path, "open");
|
||||
return fd;
|
||||
}
|
||||
|
||||
@ -138,7 +135,6 @@ int git_futils_readbuffer_fd(git_buf *buf, git_file fd, size_t len)
|
||||
int git_futils_readbuffer_updated(
|
||||
git_buf *buf, const char *path, time_t *mtime, size_t *size, int *updated)
|
||||
{
|
||||
int error = 0;
|
||||
git_file fd;
|
||||
struct stat st;
|
||||
bool changed = false;
|
||||
@ -148,15 +144,16 @@ int git_futils_readbuffer_updated(
|
||||
if (updated != NULL)
|
||||
*updated = 0;
|
||||
|
||||
if (p_stat(path, &st) < 0) {
|
||||
error = errno;
|
||||
giterr_set(GITERR_OS, "Failed to stat '%s'", path);
|
||||
if (error == ENOENT || error == ENOTDIR)
|
||||
return GIT_ENOTFOUND;
|
||||
return -1;
|
||||
if (p_stat(path, &st) < 0)
|
||||
return git_path_set_error(errno, path, "stat");
|
||||
|
||||
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
giterr_set(GITERR_INVALID, "requested file is a directory");
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
if (S_ISDIR(st.st_mode) || !git__is_sizet(st.st_size+1)) {
|
||||
if (!git__is_sizet(st.st_size+1)) {
|
||||
giterr_set(GITERR_OS, "Invalid regular file stat for '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
@ -398,8 +395,11 @@ typedef struct {
|
||||
size_t baselen;
|
||||
uint32_t flags;
|
||||
int error;
|
||||
int depth;
|
||||
} futils__rmdir_data;
|
||||
|
||||
#define FUTILS_MAX_DEPTH 100
|
||||
|
||||
static int futils__error_cannot_rmdir(const char *path, const char *filemsg)
|
||||
{
|
||||
if (filemsg)
|
||||
@ -438,51 +438,60 @@ static int futils__rm_first_parent(git_buf *path, const char *ceiling)
|
||||
|
||||
static int futils__rmdir_recurs_foreach(void *opaque, git_buf *path)
|
||||
{
|
||||
struct stat st;
|
||||
futils__rmdir_data *data = opaque;
|
||||
int error = data->error;
|
||||
struct stat st;
|
||||
|
||||
if ((data->error = p_lstat_posixly(path->ptr, &st)) < 0) {
|
||||
if (data->depth > FUTILS_MAX_DEPTH)
|
||||
error = futils__error_cannot_rmdir(
|
||||
path->ptr, "directory nesting too deep");
|
||||
|
||||
else if ((error = p_lstat_posixly(path->ptr, &st)) < 0) {
|
||||
if (errno == ENOENT)
|
||||
data->error = 0;
|
||||
error = 0;
|
||||
else if (errno == ENOTDIR) {
|
||||
/* asked to remove a/b/c/d/e and a/b is a normal file */
|
||||
if ((data->flags & GIT_RMDIR_REMOVE_BLOCKERS) != 0)
|
||||
data->error = futils__rm_first_parent(path, data->base);
|
||||
error = futils__rm_first_parent(path, data->base);
|
||||
else
|
||||
futils__error_cannot_rmdir(
|
||||
path->ptr, "parent is not directory");
|
||||
}
|
||||
else
|
||||
futils__error_cannot_rmdir(path->ptr, "cannot access");
|
||||
error = git_path_set_error(errno, path->ptr, "rmdir");
|
||||
}
|
||||
|
||||
else if (S_ISDIR(st.st_mode)) {
|
||||
int error = git_path_direach(path, futils__rmdir_recurs_foreach, data);
|
||||
data->depth++;
|
||||
|
||||
error = git_path_direach(path, 0, futils__rmdir_recurs_foreach, data);
|
||||
if (error < 0)
|
||||
return (error == GIT_EUSER) ? data->error : error;
|
||||
|
||||
data->error = p_rmdir(path->ptr);
|
||||
data->depth--;
|
||||
|
||||
if (data->error < 0) {
|
||||
if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0)
|
||||
return data->error;
|
||||
|
||||
if ((error = p_rmdir(path->ptr)) < 0) {
|
||||
if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 &&
|
||||
(errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY))
|
||||
data->error = 0;
|
||||
error = 0;
|
||||
else
|
||||
futils__error_cannot_rmdir(path->ptr, NULL);
|
||||
error = git_path_set_error(errno, path->ptr, "rmdir");
|
||||
}
|
||||
}
|
||||
|
||||
else if ((data->flags & GIT_RMDIR_REMOVE_FILES) != 0) {
|
||||
data->error = p_unlink(path->ptr);
|
||||
|
||||
if (data->error < 0)
|
||||
futils__error_cannot_rmdir(path->ptr, "cannot be removed");
|
||||
if (p_unlink(path->ptr) < 0)
|
||||
error = git_path_set_error(errno, path->ptr, "remove");
|
||||
}
|
||||
|
||||
else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0)
|
||||
data->error = futils__error_cannot_rmdir(path->ptr, "still present");
|
||||
error = futils__error_cannot_rmdir(path->ptr, "still present");
|
||||
|
||||
return data->error;
|
||||
data->error = error;
|
||||
return error;
|
||||
}
|
||||
|
||||
static int futils__rmdir_empty_parent(void *opaque, git_buf *path)
|
||||
@ -505,7 +514,7 @@ static int futils__rmdir_empty_parent(void *opaque, git_buf *path)
|
||||
giterr_clear();
|
||||
error = GIT_ITEROVER;
|
||||
} else {
|
||||
futils__error_cannot_rmdir(git_buf_cstr(path), NULL);
|
||||
error = git_path_set_error(errno, git_buf_cstr(path), "rmdir");
|
||||
}
|
||||
}
|
||||
|
||||
@ -517,7 +526,7 @@ int git_futils_rmdir_r(
|
||||
{
|
||||
int error;
|
||||
git_buf fullpath = GIT_BUF_INIT;
|
||||
futils__rmdir_data data;
|
||||
futils__rmdir_data data = { 0 };
|
||||
|
||||
/* build path and find "root" where we should start calling mkdir */
|
||||
if (git_path_join_unrooted(&fullpath, path, base, NULL) < 0)
|
||||
@ -526,7 +535,6 @@ int git_futils_rmdir_r(
|
||||
data.base = base ? base : "";
|
||||
data.baselen = base ? strlen(base) : 0;
|
||||
data.flags = flags;
|
||||
data.error = 0;
|
||||
|
||||
error = futils__rmdir_recurs_foreach(&data, &fullpath);
|
||||
|
||||
@ -544,41 +552,6 @@ int git_futils_rmdir_r(
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_futils_cleanupdir_r(const char *path)
|
||||
{
|
||||
int error;
|
||||
git_buf fullpath = GIT_BUF_INIT;
|
||||
futils__rmdir_data data;
|
||||
|
||||
if ((error = git_buf_put(&fullpath, path, strlen(path))) < 0)
|
||||
goto clean_up;
|
||||
|
||||
data.base = "";
|
||||
data.baselen = 0;
|
||||
data.flags = GIT_RMDIR_REMOVE_FILES;
|
||||
data.error = 0;
|
||||
|
||||
if (!git_path_exists(path)) {
|
||||
giterr_set(GITERR_OS, "Path does not exist: %s" , path);
|
||||
error = GIT_ERROR;
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
if (!git_path_isdir(path)) {
|
||||
giterr_set(GITERR_OS, "Path is not a directory: %s" , path);
|
||||
error = GIT_ERROR;
|
||||
goto clean_up;
|
||||
}
|
||||
|
||||
error = git_path_direach(&fullpath, futils__rmdir_recurs_foreach, &data);
|
||||
if (error == GIT_EUSER)
|
||||
error = data.error;
|
||||
|
||||
clean_up:
|
||||
git_buf_free(&fullpath);
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
static int git_futils_guess_system_dirs(git_buf *out)
|
||||
{
|
||||
@ -836,11 +809,8 @@ int git_futils_cp(const char *from, const char *to, mode_t filemode)
|
||||
return ifd;
|
||||
|
||||
if ((ofd = p_open(to, O_WRONLY | O_CREAT | O_EXCL, filemode)) < 0) {
|
||||
if (errno == ENOENT || errno == ENOTDIR)
|
||||
ofd = GIT_ENOTFOUND;
|
||||
giterr_set(GITERR_OS, "Failed to open '%s' for writing", to);
|
||||
p_close(ifd);
|
||||
return ofd;
|
||||
return git_path_set_error(errno, to, "open for writing");
|
||||
}
|
||||
|
||||
return cp_by_fd(ifd, ofd, true);
|
||||
@ -923,15 +893,14 @@ static int _cp_r_callback(void *ref, git_buf *from)
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (p_lstat(info->to.ptr, &to_st) < 0) {
|
||||
if (errno != ENOENT && errno != ENOTDIR) {
|
||||
giterr_set(GITERR_OS,
|
||||
"Could not access %s while copying files", info->to.ptr);
|
||||
error = -1;
|
||||
goto exit;
|
||||
}
|
||||
} else
|
||||
if (!(error = git_path_lstat(info->to.ptr, &to_st)))
|
||||
exists = true;
|
||||
else if (error != GIT_ENOTFOUND)
|
||||
goto exit;
|
||||
else {
|
||||
giterr_clear();
|
||||
error = 0;
|
||||
}
|
||||
|
||||
if ((error = git_path_lstat(from->ptr, &from_st)) < 0)
|
||||
goto exit;
|
||||
@ -948,9 +917,12 @@ static int _cp_r_callback(void *ref, git_buf *from)
|
||||
error = _cp_r_mkdir(info, from);
|
||||
|
||||
/* recurse onto target directory */
|
||||
if (!error && (!exists || S_ISDIR(to_st.st_mode)) &&
|
||||
((error = git_path_direach(from, _cp_r_callback, info)) == GIT_EUSER))
|
||||
error = info->error;
|
||||
if (!error && (!exists || S_ISDIR(to_st.st_mode))) {
|
||||
error = git_path_direach(from, 0, _cp_r_callback, info);
|
||||
|
||||
if (error == GIT_EUSER)
|
||||
error = info->error;
|
||||
}
|
||||
|
||||
if (oldmode != 0)
|
||||
info->dirmode = oldmode;
|
||||
|
||||
@ -115,12 +115,7 @@ extern int git_futils_mkpath2file(const char *path, const mode_t mode);
|
||||
* * GIT_RMDIR_EMPTY_PARENTS - remove containing directories up to base
|
||||
* if removing this item leaves them empty
|
||||
* * GIT_RMDIR_REMOVE_BLOCKERS - remove blocking file that causes ENOTDIR
|
||||
*
|
||||
* The old values translate into the new as follows:
|
||||
*
|
||||
* * GIT_DIRREMOVAL_EMPTY_HIERARCHY == GIT_RMDIR_EMPTY_HIERARCHY
|
||||
* * GIT_DIRREMOVAL_FILES_AND_DIRS ~= GIT_RMDIR_REMOVE_FILES
|
||||
* * GIT_DIRREMOVAL_ONLY_EMPTY_DIRS == GIT_RMDIR_SKIP_NONEMPTY
|
||||
* * GIT_RMDIR_SKIP_ROOT - don't remove root directory itself
|
||||
*/
|
||||
typedef enum {
|
||||
GIT_RMDIR_EMPTY_HIERARCHY = 0,
|
||||
@ -128,6 +123,7 @@ typedef enum {
|
||||
GIT_RMDIR_SKIP_NONEMPTY = (1 << 1),
|
||||
GIT_RMDIR_EMPTY_PARENTS = (1 << 2),
|
||||
GIT_RMDIR_REMOVE_BLOCKERS = (1 << 3),
|
||||
GIT_RMDIR_SKIP_ROOT = (1 << 4),
|
||||
} git_futils_rmdir_flags;
|
||||
|
||||
/**
|
||||
@ -140,14 +136,6 @@ typedef enum {
|
||||
*/
|
||||
extern int git_futils_rmdir_r(const char *path, const char *base, uint32_t flags);
|
||||
|
||||
/**
|
||||
* Remove all files and directories beneath the specified path.
|
||||
*
|
||||
* @param path Path to the top level directory to process.
|
||||
* @return 0 on success; -1 on error.
|
||||
*/
|
||||
extern int git_futils_cleanupdir_r(const char *path);
|
||||
|
||||
/**
|
||||
* Create and open a temporary file with a `_git2_` suffix.
|
||||
* Writes the filename into path_out.
|
||||
|
||||
95
src/global.c
95
src/global.c
@ -73,47 +73,69 @@ static void git__shutdown(void)
|
||||
#if defined(GIT_THREADS) && defined(GIT_WIN32)
|
||||
|
||||
static DWORD _tls_index;
|
||||
static int _tls_init = 0;
|
||||
static DWORD _mutex = 0;
|
||||
static DWORD _n_inits = 0;
|
||||
|
||||
int git_threads_init(void)
|
||||
static int synchronized_threads_init()
|
||||
{
|
||||
int error;
|
||||
|
||||
if (_tls_init)
|
||||
return 0;
|
||||
|
||||
_tls_index = TlsAlloc();
|
||||
if (git_mutex_init(&git__mwindow_mutex))
|
||||
return -1;
|
||||
|
||||
/* Initialize any other subsystems that have global state */
|
||||
if ((error = git_hash_global_init()) >= 0 &&
|
||||
(error = git_futils_dirs_global_init()) >= 0)
|
||||
_tls_init = 1;
|
||||
|
||||
GIT_MEMORY_BARRIER;
|
||||
if ((error = git_hash_global_init()) >= 0)
|
||||
error = git_futils_dirs_global_init();
|
||||
|
||||
win32_pthread_initialize();
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
void git_threads_shutdown(void)
|
||||
int git_threads_init(void)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
/* Enter the lock */
|
||||
while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
|
||||
|
||||
/* Only do work on a 0 -> 1 transition of the refcount */
|
||||
if (1 == ++_n_inits)
|
||||
error = synchronized_threads_init();
|
||||
|
||||
/* Exit the lock */
|
||||
InterlockedExchange(&_mutex, 0);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static void synchronized_threads_shutdown()
|
||||
{
|
||||
/* Shut down any subsystems that have global state */
|
||||
git__shutdown();
|
||||
|
||||
TlsFree(_tls_index);
|
||||
_tls_init = 0;
|
||||
|
||||
git_mutex_free(&git__mwindow_mutex);
|
||||
}
|
||||
|
||||
void git_threads_shutdown(void)
|
||||
{
|
||||
/* Enter the lock */
|
||||
while (InterlockedCompareExchange(&_mutex, 1, 0)) { Sleep(0); }
|
||||
|
||||
/* Only do work on a 1 -> 0 transition of the refcount */
|
||||
if (0 == --_n_inits)
|
||||
synchronized_threads_shutdown();
|
||||
|
||||
/* Exit the lock */
|
||||
InterlockedExchange(&_mutex, 0);
|
||||
}
|
||||
|
||||
git_global_st *git__global_state(void)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
assert(_tls_init);
|
||||
assert(_n_inits);
|
||||
|
||||
if ((ptr = TlsGetValue(_tls_index)) != NULL)
|
||||
return ptr;
|
||||
@ -130,55 +152,58 @@ git_global_st *git__global_state(void)
|
||||
#elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
|
||||
|
||||
static pthread_key_t _tls_key;
|
||||
static int _tls_init = 0;
|
||||
static pthread_once_t _once_init = PTHREAD_ONCE_INIT;
|
||||
static git_atomic git__n_inits;
|
||||
int init_error = 0;
|
||||
|
||||
static void cb__free_status(void *st)
|
||||
{
|
||||
git__free(st);
|
||||
}
|
||||
|
||||
int git_threads_init(void)
|
||||
static void init_once(void)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (_tls_init)
|
||||
return 0;
|
||||
|
||||
if (git_mutex_init(&git__mwindow_mutex))
|
||||
return -1;
|
||||
if ((init_error = git_mutex_init(&git__mwindow_mutex)) != 0)
|
||||
return;
|
||||
pthread_key_create(&_tls_key, &cb__free_status);
|
||||
|
||||
/* Initialize any other subsystems that have global state */
|
||||
if ((error = git_hash_global_init()) >= 0 &&
|
||||
(error = git_futils_dirs_global_init()) >= 0)
|
||||
_tls_init = 1;
|
||||
if ((init_error = git_hash_global_init()) >= 0)
|
||||
init_error = git_futils_dirs_global_init();
|
||||
|
||||
GIT_MEMORY_BARRIER;
|
||||
}
|
||||
|
||||
return error;
|
||||
int git_threads_init(void)
|
||||
{
|
||||
pthread_once(&_once_init, init_once);
|
||||
git_atomic_inc(&git__n_inits);
|
||||
return init_error;
|
||||
}
|
||||
|
||||
void git_threads_shutdown(void)
|
||||
{
|
||||
pthread_once_t new_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
if (git_atomic_dec(&git__n_inits) > 0) return;
|
||||
|
||||
/* Shut down any subsystems that have global state */
|
||||
git__shutdown();
|
||||
|
||||
if (_tls_init) {
|
||||
void *ptr = pthread_getspecific(_tls_key);
|
||||
pthread_setspecific(_tls_key, NULL);
|
||||
git__free(ptr);
|
||||
}
|
||||
void *ptr = pthread_getspecific(_tls_key);
|
||||
pthread_setspecific(_tls_key, NULL);
|
||||
git__free(ptr);
|
||||
|
||||
pthread_key_delete(_tls_key);
|
||||
_tls_init = 0;
|
||||
git_mutex_free(&git__mwindow_mutex);
|
||||
_once_init = new_once;
|
||||
}
|
||||
|
||||
git_global_st *git__global_state(void)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
assert(_tls_init);
|
||||
assert(git__n_inits.val);
|
||||
|
||||
if ((ptr = pthread_getspecific(_tls_key)) != NULL)
|
||||
return ptr;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "git2/ignore.h"
|
||||
#include "common.h"
|
||||
#include "ignore.h"
|
||||
#include "attr.h"
|
||||
#include "path.h"
|
||||
|
||||
13
src/index.c
13
src/index.c
@ -461,9 +461,10 @@ int git_index_read(git_index *index)
|
||||
return create_index_error(-1,
|
||||
"Failed to read index: The index is in-memory only");
|
||||
|
||||
if (!index->on_disk || git_path_exists(index->index_file_path) == false) {
|
||||
index->on_disk = git_path_exists(index->index_file_path);
|
||||
|
||||
if (!index->on_disk) {
|
||||
git_index_clear(index);
|
||||
index->on_disk = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -579,7 +580,8 @@ const git_index_entry *git_index_get_bypath(
|
||||
return git_index_get_byindex(index, pos);
|
||||
}
|
||||
|
||||
void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st)
|
||||
void git_index_entry__init_from_stat(
|
||||
git_index_entry *entry, struct stat *st, bool trust_mode)
|
||||
{
|
||||
entry->ctime.seconds = (git_time_t)st->st_ctime;
|
||||
entry->mtime.seconds = (git_time_t)st->st_mtime;
|
||||
@ -587,7 +589,8 @@ void git_index_entry__init_from_stat(git_index_entry *entry, struct stat *st)
|
||||
/* entry->ctime.nanoseconds = st->st_ctimensec; */
|
||||
entry->dev = st->st_rdev;
|
||||
entry->ino = st->st_ino;
|
||||
entry->mode = index_create_mode(st->st_mode);
|
||||
entry->mode = (!trust_mode && S_ISREG(st->st_mode)) ?
|
||||
index_create_mode(0666) : index_create_mode(st->st_mode);
|
||||
entry->uid = st->st_uid;
|
||||
entry->gid = st->st_gid;
|
||||
entry->file_size = st->st_size;
|
||||
@ -631,7 +634,7 @@ static int index_entry_init(
|
||||
entry = git__calloc(1, sizeof(git_index_entry));
|
||||
GITERR_CHECK_ALLOC(entry);
|
||||
|
||||
git_index_entry__init_from_stat(entry, &st);
|
||||
git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode);
|
||||
|
||||
entry->oid = oid;
|
||||
entry->path = git__strdup(rel_path);
|
||||
|
||||
@ -48,7 +48,7 @@ struct git_index_conflict_iterator {
|
||||
};
|
||||
|
||||
extern void git_index_entry__init_from_stat(
|
||||
git_index_entry *entry, struct stat *st);
|
||||
git_index_entry *entry, struct stat *st, bool trust_mode);
|
||||
|
||||
extern size_t git_index__prefix_position(git_index *index, const char *path);
|
||||
|
||||
|
||||
330
src/indexer.c
330
src/indexer.c
@ -18,6 +18,7 @@
|
||||
#include "filebuf.h"
|
||||
#include "oid.h"
|
||||
#include "oidmap.h"
|
||||
#include "compress.h"
|
||||
|
||||
#define UINT31_MAX (0x7FFFFFFF)
|
||||
|
||||
@ -33,6 +34,7 @@ struct git_indexer_stream {
|
||||
opened_pack :1,
|
||||
have_stream :1,
|
||||
have_delta :1;
|
||||
struct git_pack_header hdr;
|
||||
struct git_pack_file *pack;
|
||||
git_filebuf pack_file;
|
||||
git_off_t off;
|
||||
@ -48,9 +50,12 @@ struct git_indexer_stream {
|
||||
void *progress_payload;
|
||||
char objbuf[8*1024];
|
||||
|
||||
/* Needed to look up objects which we want to inject to fix a thin pack */
|
||||
git_odb *odb;
|
||||
|
||||
/* Fields for calculating the packfile trailer (hash of everything before it) */
|
||||
char inbuf[GIT_OID_RAWSZ];
|
||||
int inbuf_len;
|
||||
size_t inbuf_len;
|
||||
git_hash_ctx trailer;
|
||||
};
|
||||
|
||||
@ -114,6 +119,7 @@ static int objects_cmp(const void *a, const void *b)
|
||||
int git_indexer_stream_new(
|
||||
git_indexer_stream **out,
|
||||
const char *prefix,
|
||||
git_odb *odb,
|
||||
git_transfer_progress_callback progress_cb,
|
||||
void *progress_payload)
|
||||
{
|
||||
@ -124,6 +130,7 @@ int git_indexer_stream_new(
|
||||
|
||||
idx = git__calloc(1, sizeof(git_indexer_stream));
|
||||
GITERR_CHECK_ALLOC(idx);
|
||||
idx->odb = odb;
|
||||
idx->progress_cb = progress_cb;
|
||||
idx->progress_payload = progress_payload;
|
||||
git_hash_ctx_init(&idx->trailer);
|
||||
@ -309,17 +316,10 @@ on_error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t entry_start)
|
||||
static int save_entry(git_indexer_stream *idx, struct entry *entry, struct git_pack_entry *pentry, git_off_t entry_start)
|
||||
{
|
||||
int i, error;
|
||||
khiter_t k;
|
||||
git_oid oid;
|
||||
size_t entry_size;
|
||||
struct entry *entry;
|
||||
struct git_pack_entry *pentry;
|
||||
|
||||
entry = git__calloc(1, sizeof(*entry));
|
||||
GITERR_CHECK_ALLOC(entry);
|
||||
|
||||
if (entry_start > UINT31_MAX) {
|
||||
entry->offset = UINT32_MAX;
|
||||
@ -328,6 +328,34 @@ static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t ent
|
||||
entry->offset = (uint32_t)entry_start;
|
||||
}
|
||||
|
||||
pentry->offset = entry_start;
|
||||
k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error);
|
||||
if (!error)
|
||||
return -1;
|
||||
|
||||
kh_value(idx->pack->idx_cache, k) = pentry;
|
||||
|
||||
/* Add the object to the list */
|
||||
if (git_vector_insert(&idx->objects, entry) < 0)
|
||||
return -1;
|
||||
|
||||
for (i = entry->oid.id[0]; i < 256; ++i) {
|
||||
idx->fanout[i]++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t entry_start)
|
||||
{
|
||||
git_oid oid;
|
||||
size_t entry_size;
|
||||
struct entry *entry;
|
||||
struct git_pack_entry *pentry;
|
||||
|
||||
entry = git__calloc(1, sizeof(*entry));
|
||||
GITERR_CHECK_ALLOC(entry);
|
||||
|
||||
if (git_odb__hashobj(&oid, obj) < 0) {
|
||||
giterr_set(GITERR_INDEXER, "Failed to hash object");
|
||||
goto on_error;
|
||||
@ -337,15 +365,6 @@ static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t ent
|
||||
GITERR_CHECK_ALLOC(pentry);
|
||||
|
||||
git_oid_cpy(&pentry->sha1, &oid);
|
||||
pentry->offset = entry_start;
|
||||
k = kh_put(oid, idx->pack->idx_cache, &pentry->sha1, &error);
|
||||
if (!error) {
|
||||
git__free(pentry);
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
kh_value(idx->pack->idx_cache, k) = pentry;
|
||||
|
||||
git_oid_cpy(&entry->oid, &oid);
|
||||
entry->crc = crc32(0L, Z_NULL, 0);
|
||||
|
||||
@ -353,15 +372,7 @@ static int hash_and_save(git_indexer_stream *idx, git_rawobj *obj, git_off_t ent
|
||||
if (crc_object(&entry->crc, &idx->pack->mwf, entry_start, entry_size) < 0)
|
||||
goto on_error;
|
||||
|
||||
/* Add the object to the list */
|
||||
if (git_vector_insert(&idx->objects, entry) < 0)
|
||||
goto on_error;
|
||||
|
||||
for (i = oid.id[0]; i < 256; ++i) {
|
||||
idx->fanout[i]++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return save_entry(idx, entry, pentry, entry_start);
|
||||
|
||||
on_error:
|
||||
git__free(entry);
|
||||
@ -378,13 +389,13 @@ static int do_progress_callback(git_indexer_stream *idx, git_transfer_progress *
|
||||
/* Hash everything but the last 20B of input */
|
||||
static void hash_partially(git_indexer_stream *idx, const uint8_t *data, size_t size)
|
||||
{
|
||||
int to_expell, to_keep;
|
||||
size_t to_expell, to_keep;
|
||||
|
||||
if (size == 0)
|
||||
return;
|
||||
|
||||
/* Easy case, dump the buffer and the data minus the last 20 bytes */
|
||||
if (size >= 20) {
|
||||
if (size >= GIT_OID_RAWSZ) {
|
||||
git_hash_update(&idx->trailer, idx->inbuf, idx->inbuf_len);
|
||||
git_hash_update(&idx->trailer, data, size - GIT_OID_RAWSZ);
|
||||
|
||||
@ -402,8 +413,8 @@ static void hash_partially(git_indexer_stream *idx, const uint8_t *data, size_t
|
||||
}
|
||||
|
||||
/* We need to partially drain the buffer and then append */
|
||||
to_expell = abs(size - (GIT_OID_RAWSZ - idx->inbuf_len));
|
||||
to_keep = abs(idx->inbuf_len - to_expell);
|
||||
to_keep = GIT_OID_RAWSZ - size;
|
||||
to_expell = idx->inbuf_len - to_keep;
|
||||
|
||||
git_hash_update(&idx->trailer, idx->inbuf, to_expell);
|
||||
|
||||
@ -415,8 +426,8 @@ static void hash_partially(git_indexer_stream *idx, const uint8_t *data, size_t
|
||||
int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t size, git_transfer_progress *stats)
|
||||
{
|
||||
int error = -1;
|
||||
struct git_pack_header hdr;
|
||||
size_t processed;
|
||||
struct git_pack_header *hdr = &idx->hdr;
|
||||
git_mwindow_file *mwf = &idx->pack->mwf;
|
||||
|
||||
assert(idx && data && stats);
|
||||
@ -443,14 +454,14 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
|
||||
if (!idx->parsed_header) {
|
||||
unsigned int total_objects;
|
||||
|
||||
if ((unsigned)idx->pack->mwf.size < sizeof(hdr))
|
||||
if ((unsigned)idx->pack->mwf.size < sizeof(struct git_pack_header))
|
||||
return 0;
|
||||
|
||||
if (parse_header(&hdr, idx->pack) < 0)
|
||||
if (parse_header(&idx->hdr, idx->pack) < 0)
|
||||
return -1;
|
||||
|
||||
idx->parsed_header = 1;
|
||||
idx->nr_objects = ntohl(hdr.hdr_entries);
|
||||
idx->nr_objects = ntohl(hdr->hdr_entries);
|
||||
idx->off = sizeof(struct git_pack_header);
|
||||
|
||||
/* for now, limit to 2^32 objects */
|
||||
@ -471,6 +482,9 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
|
||||
return -1;
|
||||
|
||||
stats->received_objects = 0;
|
||||
stats->local_objects = 0;
|
||||
stats->total_deltas = 0;
|
||||
stats->indexed_deltas = 0;
|
||||
processed = stats->indexed_objects = 0;
|
||||
stats->total_objects = total_objects;
|
||||
do_progress_callback(idx, stats);
|
||||
@ -556,6 +570,7 @@ int git_indexer_stream_add(git_indexer_stream *idx, const void *data, size_t siz
|
||||
stats->received_objects++;
|
||||
|
||||
if (do_progress_callback(idx, stats) != 0) {
|
||||
giterr_clear();
|
||||
error = GIT_EUSER;
|
||||
goto on_error;
|
||||
}
|
||||
@ -590,24 +605,229 @@ static int index_path_stream(git_buf *path, git_indexer_stream *idx, const char
|
||||
return git_buf_oom(path) ? -1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewind the packfile by the trailer, as we might need to fix the
|
||||
* packfile by injecting objects at the tail and must overwrite it.
|
||||
*/
|
||||
static git_off_t seek_back_trailer(git_indexer_stream *idx)
|
||||
{
|
||||
git_off_t off;
|
||||
|
||||
if ((off = p_lseek(idx->pack_file.fd, -GIT_OID_RAWSZ, SEEK_CUR)) < 0)
|
||||
return -1;
|
||||
|
||||
idx->pack->mwf.size -= GIT_OID_RAWSZ;
|
||||
git_mwindow_free_all(&idx->pack->mwf);
|
||||
|
||||
return off;
|
||||
}
|
||||
|
||||
static int inject_object(git_indexer_stream *idx, git_oid *id)
|
||||
{
|
||||
git_odb_object *obj;
|
||||
struct entry *entry;
|
||||
struct git_pack_entry *pentry;
|
||||
git_oid foo = {{0}};
|
||||
unsigned char hdr[64];
|
||||
git_buf buf = GIT_BUF_INIT;
|
||||
git_off_t entry_start;
|
||||
const void *data;
|
||||
size_t len, hdr_len;
|
||||
int error;
|
||||
|
||||
entry = git__calloc(1, sizeof(*entry));
|
||||
GITERR_CHECK_ALLOC(entry);
|
||||
|
||||
entry_start = seek_back_trailer(idx);
|
||||
|
||||
if (git_odb_read(&obj, idx->odb, id) < 0)
|
||||
return -1;
|
||||
|
||||
data = git_odb_object_data(obj);
|
||||
len = git_odb_object_size(obj);
|
||||
|
||||
entry->crc = crc32(0L, Z_NULL, 0);
|
||||
|
||||
/* Write out the object header */
|
||||
hdr_len = git_packfile__object_header(hdr, len, git_odb_object_type(obj));
|
||||
git_filebuf_write(&idx->pack_file, hdr, hdr_len);
|
||||
idx->pack->mwf.size += hdr_len;
|
||||
entry->crc = crc32(entry->crc, hdr, hdr_len);
|
||||
|
||||
if ((error = git__compress(&buf, data, len)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
/* And then the compressed object */
|
||||
git_filebuf_write(&idx->pack_file, buf.ptr, buf.size);
|
||||
idx->pack->mwf.size += buf.size;
|
||||
entry->crc = htonl(crc32(entry->crc, (unsigned char *)buf.ptr, buf.size));
|
||||
git_buf_free(&buf);
|
||||
|
||||
/* Write a fake trailer so the pack functions play ball */
|
||||
if ((error = git_filebuf_write(&idx->pack_file, &foo, GIT_OID_RAWSZ)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
idx->pack->mwf.size += GIT_OID_RAWSZ;
|
||||
|
||||
pentry = git__calloc(1, sizeof(struct git_pack_entry));
|
||||
GITERR_CHECK_ALLOC(pentry);
|
||||
|
||||
git_oid_cpy(&pentry->sha1, id);
|
||||
git_oid_cpy(&entry->oid, id);
|
||||
idx->off = entry_start + hdr_len + len;
|
||||
|
||||
if ((error = save_entry(idx, entry, pentry, entry_start)) < 0)
|
||||
git__free(pentry);
|
||||
|
||||
cleanup:
|
||||
git_odb_object_free(obj);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int fix_thin_pack(git_indexer_stream *idx, git_transfer_progress *stats)
|
||||
{
|
||||
int error, found_ref_delta = 0;
|
||||
unsigned int i;
|
||||
struct delta_info *delta;
|
||||
size_t size;
|
||||
git_otype type;
|
||||
git_mwindow *w = NULL;
|
||||
git_off_t curpos;
|
||||
unsigned char *base_info;
|
||||
unsigned int left = 0;
|
||||
git_oid base;
|
||||
|
||||
assert(git_vector_length(&idx->deltas) > 0);
|
||||
|
||||
if (idx->odb == NULL) {
|
||||
giterr_set(GITERR_INDEXER, "cannot fix a thin pack without an ODB");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Loop until we find the first REF delta */
|
||||
git_vector_foreach(&idx->deltas, i, delta) {
|
||||
curpos = delta->delta_off;
|
||||
error = git_packfile_unpack_header(&size, &type, &idx->pack->mwf, &w, &curpos);
|
||||
git_mwindow_close(&w);
|
||||
if (error < 0)
|
||||
return error;
|
||||
|
||||
if (type == GIT_OBJ_REF_DELTA) {
|
||||
found_ref_delta = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_ref_delta) {
|
||||
giterr_set(GITERR_INDEXER, "no REF_DELTA found, cannot inject object");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* curpos now points to the base information, which is an OID */
|
||||
base_info = git_mwindow_open(&idx->pack->mwf, &w, curpos, GIT_OID_RAWSZ, &left);
|
||||
if (base_info == NULL) {
|
||||
giterr_set(GITERR_INDEXER, "failed to map delta information");
|
||||
return -1;
|
||||
}
|
||||
|
||||
git_oid_fromraw(&base, base_info);
|
||||
git_mwindow_close(&w);
|
||||
|
||||
if (inject_object(idx, &base) < 0)
|
||||
return -1;
|
||||
|
||||
stats->local_objects++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int resolve_deltas(git_indexer_stream *idx, git_transfer_progress *stats)
|
||||
{
|
||||
unsigned int i;
|
||||
struct delta_info *delta;
|
||||
int progressed = 0;
|
||||
|
||||
git_vector_foreach(&idx->deltas, i, delta) {
|
||||
git_rawobj obj;
|
||||
while (idx->deltas.length > 0) {
|
||||
progressed = 0;
|
||||
git_vector_foreach(&idx->deltas, i, delta) {
|
||||
git_rawobj obj;
|
||||
|
||||
idx->off = delta->delta_off;
|
||||
if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0)
|
||||
idx->off = delta->delta_off;
|
||||
if (git_packfile_unpack(&obj, idx->pack, &idx->off) < 0)
|
||||
continue;
|
||||
|
||||
if (hash_and_save(idx, &obj, delta->delta_off) < 0)
|
||||
continue;
|
||||
|
||||
git__free(obj.data);
|
||||
stats->indexed_objects++;
|
||||
stats->indexed_deltas++;
|
||||
progressed = 1;
|
||||
do_progress_callback(idx, stats);
|
||||
|
||||
/*
|
||||
* Remove this delta from the list and
|
||||
* decrease i so we don't skip over the next
|
||||
* delta.
|
||||
*/
|
||||
git_vector_remove(&idx->deltas, i);
|
||||
git__free(delta);
|
||||
i--;
|
||||
}
|
||||
|
||||
if (!progressed && (fix_thin_pack(idx, stats) < 0)) {
|
||||
giterr_set(GITERR_INDEXER, "missing delta bases");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int update_header_and_rehash(git_indexer_stream *idx, git_transfer_progress *stats)
|
||||
{
|
||||
void *ptr;
|
||||
size_t chunk = 1024*1024;
|
||||
git_off_t hashed = 0;
|
||||
git_mwindow *w = NULL;
|
||||
git_mwindow_file *mwf;
|
||||
unsigned int left;
|
||||
git_hash_ctx *ctx;
|
||||
|
||||
mwf = &idx->pack->mwf;
|
||||
ctx = &idx->trailer;
|
||||
|
||||
git_hash_ctx_init(ctx);
|
||||
git_mwindow_free_all(mwf);
|
||||
|
||||
/* Update the header to include the numer of local objects we injected */
|
||||
idx->hdr.hdr_entries = htonl(stats->total_objects + stats->local_objects);
|
||||
if (p_lseek(idx->pack_file.fd, 0, SEEK_SET) < 0) {
|
||||
giterr_set(GITERR_OS, "failed to seek to the beginning of the pack");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (p_write(idx->pack_file.fd, &idx->hdr, sizeof(struct git_pack_header)) < 0) {
|
||||
giterr_set(GITERR_OS, "failed to update the pack header");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* We now use the same technique as before to determine the
|
||||
* hash. We keep reading up to the end and let
|
||||
* hash_partially() keep the existing trailer out of the
|
||||
* calculation.
|
||||
*/
|
||||
idx->inbuf_len = 0;
|
||||
while (hashed < mwf->size) {
|
||||
ptr = git_mwindow_open(mwf, &w, hashed, chunk, &left);
|
||||
if (ptr == NULL)
|
||||
return -1;
|
||||
|
||||
if (hash_and_save(idx, &obj, delta->delta_off) < 0)
|
||||
return -1;
|
||||
hash_partially(idx, ptr, left);
|
||||
hashed += left;
|
||||
|
||||
git__free(obj.data);
|
||||
stats->indexed_objects++;
|
||||
do_progress_callback(idx, stats);
|
||||
git_mwindow_close(&w);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -650,15 +870,31 @@ int git_indexer_stream_finalize(git_indexer_stream *idx, git_transfer_progress *
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (idx->deltas.length > 0)
|
||||
if (resolve_deltas(idx, stats) < 0)
|
||||
return -1;
|
||||
/* Freeze the number of deltas */
|
||||
stats->total_deltas = stats->total_objects - stats->indexed_objects;
|
||||
|
||||
if (resolve_deltas(idx, stats) < 0)
|
||||
return -1;
|
||||
|
||||
if (stats->indexed_objects != stats->total_objects) {
|
||||
giterr_set(GITERR_INDEXER, "early EOF");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (stats->local_objects > 0) {
|
||||
if (update_header_and_rehash(idx, stats) < 0)
|
||||
return -1;
|
||||
|
||||
git_hash_final(&trailer_hash, &idx->trailer);
|
||||
if (p_lseek(idx->pack_file.fd, -GIT_OID_RAWSZ, SEEK_END) < 0)
|
||||
return -1;
|
||||
|
||||
if (p_write(idx->pack_file.fd, &trailer_hash, GIT_OID_RAWSZ) < 0) {
|
||||
giterr_set(GITERR_OS, "failed to update pack trailer");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
git_vector_sort(&idx->objects);
|
||||
|
||||
git_buf_sets(&filename, idx->pack->pack_name);
|
||||
|
||||
@ -893,6 +893,7 @@ struct fs_iterator {
|
||||
git_index_entry entry;
|
||||
git_buf path;
|
||||
size_t root_len;
|
||||
uint32_t dirload_flags;
|
||||
int depth;
|
||||
|
||||
int (*enter_dir_cb)(fs_iterator *self);
|
||||
@ -986,7 +987,7 @@ static int fs_iterator__expand_dir(fs_iterator *fi)
|
||||
GITERR_CHECK_ALLOC(ff);
|
||||
|
||||
error = git_path_dirload_with_stat(
|
||||
fi->path.ptr, fi->root_len, iterator__ignore_case(fi),
|
||||
fi->path.ptr, fi->root_len, fi->dirload_flags,
|
||||
fi->base.start, fi->base.end, &ff->entries);
|
||||
|
||||
if (error < 0) {
|
||||
@ -1174,7 +1175,7 @@ static int fs_iterator__update_entry(fs_iterator *fi)
|
||||
return GIT_ITEROVER;
|
||||
|
||||
fi->entry.path = ps->path;
|
||||
git_index_entry__init_from_stat(&fi->entry, &ps->st);
|
||||
git_index_entry__init_from_stat(&fi->entry, &ps->st, true);
|
||||
|
||||
/* need different mode here to keep directories during iteration */
|
||||
fi->entry.mode = git_futils_canonical_mode(ps->st.st_mode);
|
||||
@ -1207,6 +1208,11 @@ static int fs_iterator__initialize(
|
||||
}
|
||||
fi->root_len = fi->path.size;
|
||||
|
||||
fi->dirload_flags =
|
||||
(iterator__ignore_case(fi) ? GIT_PATH_DIR_IGNORE_CASE : 0) |
|
||||
(iterator__flag(fi, PRECOMPOSE_UNICODE) ?
|
||||
GIT_PATH_DIR_PRECOMPOSE_UNICODE : 0);
|
||||
|
||||
if ((error = fs_iterator__expand_dir(fi)) < 0) {
|
||||
if (error == GIT_ENOTFOUND || error == GIT_ITEROVER) {
|
||||
giterr_clear();
|
||||
@ -1329,7 +1335,7 @@ int git_iterator_for_workdir_ext(
|
||||
const char *start,
|
||||
const char *end)
|
||||
{
|
||||
int error;
|
||||
int error, precompose = 0;
|
||||
workdir_iterator *wi;
|
||||
|
||||
if (!repo_workdir) {
|
||||
@ -1356,6 +1362,12 @@ int git_iterator_for_workdir_ext(
|
||||
return error;
|
||||
}
|
||||
|
||||
/* try to look up precompose and set flag if appropriate */
|
||||
if (git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) < 0)
|
||||
giterr_clear();
|
||||
else if (precompose)
|
||||
wi->fi.base.flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
|
||||
|
||||
return fs_iterator__initialize(out, &wi->fi, repo_workdir);
|
||||
}
|
||||
|
||||
|
||||
@ -24,13 +24,15 @@ typedef enum {
|
||||
|
||||
typedef enum {
|
||||
/** ignore case for entry sort order */
|
||||
GIT_ITERATOR_IGNORE_CASE = (1 << 0),
|
||||
GIT_ITERATOR_IGNORE_CASE = (1u << 0),
|
||||
/** force case sensitivity for entry sort order */
|
||||
GIT_ITERATOR_DONT_IGNORE_CASE = (1 << 1),
|
||||
GIT_ITERATOR_DONT_IGNORE_CASE = (1u << 1),
|
||||
/** return tree items in addition to blob items */
|
||||
GIT_ITERATOR_INCLUDE_TREES = (1 << 2),
|
||||
GIT_ITERATOR_INCLUDE_TREES = (1u << 2),
|
||||
/** don't flatten trees, requiring advance_into (implies INCLUDE_TREES) */
|
||||
GIT_ITERATOR_DONT_AUTOEXPAND = (1 << 3),
|
||||
GIT_ITERATOR_DONT_AUTOEXPAND = (1u << 3),
|
||||
/** convert precomposed unicode to decomposed unicode */
|
||||
GIT_ITERATOR_PRECOMPOSE_UNICODE = (1u << 4),
|
||||
} git_iterator_flag_t;
|
||||
|
||||
typedef struct {
|
||||
|
||||
@ -47,7 +47,7 @@ GIT_INLINE(int) merge_file_best_mode(
|
||||
* assume executable. Otherwise, if any mode changed from the ancestor,
|
||||
* use that one.
|
||||
*/
|
||||
if (GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
|
||||
if (!GIT_MERGE_FILE_SIDE_EXISTS(ancestor)) {
|
||||
if (ours->mode == GIT_FILEMODE_BLOB_EXECUTABLE ||
|
||||
theirs->mode == GIT_FILEMODE_BLOB_EXECUTABLE)
|
||||
return GIT_FILEMODE_BLOB_EXECUTABLE;
|
||||
|
||||
@ -124,6 +124,13 @@ git_otype git_odb_object_type(git_odb_object *object)
|
||||
return object->cached.type;
|
||||
}
|
||||
|
||||
int git_odb_object_dup(git_odb_object **dest, git_odb_object *source)
|
||||
{
|
||||
git_cached_obj_incref(source);
|
||||
*dest = source;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void git_odb_object_free(git_odb_object *object)
|
||||
{
|
||||
if (object == NULL)
|
||||
@ -988,7 +995,7 @@ int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_transfer
|
||||
|
||||
if (b->writepack != NULL) {
|
||||
++writes;
|
||||
error = b->writepack(out, b, progress_cb, progress_payload);
|
||||
error = b->writepack(out, b, db, progress_cb, progress_payload);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -544,7 +544,7 @@ static int locate_object_short_oid(
|
||||
|
||||
/* Explore directory to find a unique object matching short_oid */
|
||||
error = git_path_direach(
|
||||
object_location, fn_locate_object_short_oid, &state);
|
||||
object_location, 0, fn_locate_object_short_oid, &state);
|
||||
|
||||
if (error && error != GIT_EUSER)
|
||||
return error;
|
||||
@ -745,7 +745,7 @@ static int foreach_cb(void *_state, git_buf *path)
|
||||
{
|
||||
struct foreach_state *state = (struct foreach_state *) _state;
|
||||
|
||||
return git_path_direach(path, foreach_object_dir_cb, state);
|
||||
return git_path_direach(path, 0, foreach_object_dir_cb, state);
|
||||
}
|
||||
|
||||
static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
|
||||
@ -768,7 +768,7 @@ static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb
|
||||
state.data = data;
|
||||
state.dir_len = git_buf_len(&buf);
|
||||
|
||||
error = git_path_direach(&buf, foreach_cb, &state);
|
||||
error = git_path_direach(&buf, 0, foreach_cb, &state);
|
||||
|
||||
git_buf_free(&buf);
|
||||
|
||||
|
||||
@ -331,7 +331,7 @@ static int pack_backend__refresh(git_odb_backend *_backend)
|
||||
git_buf_sets(&path, backend->pack_folder);
|
||||
|
||||
/* reload all packs */
|
||||
error = git_path_direach(&path, packfile_load__cb, (void *)backend);
|
||||
error = git_path_direach(&path, 0, packfile_load__cb, backend);
|
||||
|
||||
git_buf_free(&path);
|
||||
|
||||
@ -541,6 +541,7 @@ static void pack_backend__writepack_free(struct git_odb_writepack *_writepack)
|
||||
|
||||
static int pack_backend__writepack(struct git_odb_writepack **out,
|
||||
git_odb_backend *_backend,
|
||||
git_odb *odb,
|
||||
git_transfer_progress_callback progress_cb,
|
||||
void *progress_payload)
|
||||
{
|
||||
@ -557,7 +558,7 @@ static int pack_backend__writepack(struct git_odb_writepack **out,
|
||||
GITERR_CHECK_ALLOC(writepack);
|
||||
|
||||
if (git_indexer_stream_new(&writepack->indexer_stream,
|
||||
backend->pack_folder, progress_cb, progress_payload) < 0) {
|
||||
backend->pack_folder, odb, progress_cb, progress_payload) < 0) {
|
||||
git__free(writepack);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -216,52 +216,22 @@ int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid,
|
||||
assert(ret != 0);
|
||||
kh_value(pb->object_ix, pos) = po;
|
||||
|
||||
pb->done = false;
|
||||
|
||||
if (pb->progress_cb) {
|
||||
double current_time = git__timer();
|
||||
if ((current_time - pb->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
|
||||
pb->last_progress_report_time = current_time;
|
||||
pb->progress_cb(GIT_PACKBUILDER_ADDING_OBJECTS, pb->nr_objects, 0, pb->progress_cb_payload);
|
||||
if (pb->progress_cb(GIT_PACKBUILDER_ADDING_OBJECTS, pb->nr_objects, 0, pb->progress_cb_payload)) {
|
||||
giterr_clear();
|
||||
return GIT_EUSER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pb->done = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The per-object header is a pretty dense thing, which is
|
||||
* - first byte: low four bits are "size",
|
||||
* then three bits of "type",
|
||||
* with the high bit being "size continues".
|
||||
* - each byte afterwards: low seven bits are size continuation,
|
||||
* with the high bit being "size continues"
|
||||
*/
|
||||
static int gen_pack_object_header(
|
||||
unsigned char *hdr,
|
||||
unsigned long size,
|
||||
git_otype type)
|
||||
{
|
||||
unsigned char *hdr_base;
|
||||
unsigned char c;
|
||||
|
||||
assert(type >= GIT_OBJ_COMMIT && type <= GIT_OBJ_REF_DELTA);
|
||||
|
||||
/* TODO: add support for chunked objects; see git.git 6c0d19b1 */
|
||||
|
||||
c = (unsigned char)((type << 4) | (size & 15));
|
||||
size >>= 4;
|
||||
hdr_base = hdr;
|
||||
|
||||
while (size) {
|
||||
*hdr++ = c | 0x80;
|
||||
c = size & 0x7f;
|
||||
size >>= 7;
|
||||
}
|
||||
*hdr++ = c;
|
||||
|
||||
return (int)(hdr - hdr_base);
|
||||
}
|
||||
|
||||
static int get_delta(void **out, git_odb *odb, git_pobject *po)
|
||||
{
|
||||
git_odb_object *src = NULL, *trg = NULL;
|
||||
@ -323,7 +293,7 @@ static int write_object(git_buf *buf, git_packbuilder *pb, git_pobject *po)
|
||||
}
|
||||
|
||||
/* Write header */
|
||||
hdr_len = gen_pack_object_header(hdr, size, type);
|
||||
hdr_len = git_packfile__object_header(hdr, size, type);
|
||||
|
||||
if (git_buf_put(buf, (char *)hdr, hdr_len) < 0)
|
||||
goto on_error;
|
||||
@ -591,49 +561,50 @@ static int write_pack(git_packbuilder *pb,
|
||||
enum write_one_status status;
|
||||
struct git_pack_header ph;
|
||||
unsigned int i = 0;
|
||||
int error = 0;
|
||||
|
||||
write_order = compute_write_order(pb);
|
||||
if (write_order == NULL)
|
||||
goto on_error;
|
||||
if (write_order == NULL) {
|
||||
error = -1;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Write pack header */
|
||||
ph.hdr_signature = htonl(PACK_SIGNATURE);
|
||||
ph.hdr_version = htonl(PACK_VERSION);
|
||||
ph.hdr_entries = htonl(pb->nr_objects);
|
||||
|
||||
if (cb(&ph, sizeof(ph), data) < 0)
|
||||
goto on_error;
|
||||
if ((error = cb(&ph, sizeof(ph), data)) < 0)
|
||||
goto done;
|
||||
|
||||
if (git_hash_update(&pb->ctx, &ph, sizeof(ph)) < 0)
|
||||
goto on_error;
|
||||
if ((error = git_hash_update(&pb->ctx, &ph, sizeof(ph))) < 0)
|
||||
goto done;
|
||||
|
||||
pb->nr_remaining = pb->nr_objects;
|
||||
do {
|
||||
pb->nr_written = 0;
|
||||
for ( ; i < pb->nr_objects; ++i) {
|
||||
po = write_order[i];
|
||||
if (write_one(&buf, pb, po, &status) < 0)
|
||||
goto on_error;
|
||||
if (cb(buf.ptr, buf.size, data) < 0)
|
||||
goto on_error;
|
||||
if ((error = write_one(&buf, pb, po, &status)) < 0)
|
||||
goto done;
|
||||
if ((error = cb(buf.ptr, buf.size, data)) < 0)
|
||||
goto done;
|
||||
git_buf_clear(&buf);
|
||||
}
|
||||
|
||||
pb->nr_remaining -= pb->nr_written;
|
||||
} while (pb->nr_remaining && i < pb->nr_objects);
|
||||
|
||||
|
||||
if ((error = git_hash_final(&pb->pack_oid, &pb->ctx)) < 0)
|
||||
goto done;
|
||||
|
||||
error = cb(pb->pack_oid.id, GIT_OID_RAWSZ, data);
|
||||
|
||||
done:
|
||||
git__free(write_order);
|
||||
git_buf_free(&buf);
|
||||
|
||||
if (git_hash_final(&pb->pack_oid, &pb->ctx) < 0)
|
||||
goto on_error;
|
||||
|
||||
return cb(pb->pack_oid.id, GIT_OID_RAWSZ, data);
|
||||
|
||||
on_error:
|
||||
git__free(write_order);
|
||||
git_buf_free(&buf);
|
||||
return -1;
|
||||
return error;
|
||||
}
|
||||
|
||||
static int write_pack_buf(void *buf, size_t size, void *data)
|
||||
@ -1287,7 +1258,7 @@ int git_packbuilder_write(
|
||||
PREPARE_PACK;
|
||||
|
||||
if (git_indexer_stream_new(
|
||||
&indexer, path, progress_cb, progress_cb_payload) < 0)
|
||||
&indexer, path, pb->odb, progress_cb, progress_cb_payload) < 0)
|
||||
return -1;
|
||||
|
||||
ctx.indexer = indexer;
|
||||
|
||||
32
src/pack.c
32
src/pack.c
@ -364,6 +364,38 @@ static unsigned char *pack_window_open(
|
||||
return git_mwindow_open(&p->mwf, w_cursor, offset, 20, left);
|
||||
}
|
||||
|
||||
/*
|
||||
* The per-object header is a pretty dense thing, which is
|
||||
* - first byte: low four bits are "size",
|
||||
* then three bits of "type",
|
||||
* with the high bit being "size continues".
|
||||
* - each byte afterwards: low seven bits are size continuation,
|
||||
* with the high bit being "size continues"
|
||||
*/
|
||||
int git_packfile__object_header(unsigned char *hdr, unsigned long size, git_otype type)
|
||||
{
|
||||
unsigned char *hdr_base;
|
||||
unsigned char c;
|
||||
|
||||
assert(type >= GIT_OBJ_COMMIT && type <= GIT_OBJ_REF_DELTA);
|
||||
|
||||
/* TODO: add support for chunked objects; see git.git 6c0d19b1 */
|
||||
|
||||
c = (unsigned char)((type << 4) | (size & 15));
|
||||
size >>= 4;
|
||||
hdr_base = hdr;
|
||||
|
||||
while (size) {
|
||||
*hdr++ = c | 0x80;
|
||||
c = size & 0x7f;
|
||||
size >>= 7;
|
||||
}
|
||||
*hdr++ = c;
|
||||
|
||||
return (int)(hdr - hdr_base);
|
||||
}
|
||||
|
||||
|
||||
static int packfile_unpack_header1(
|
||||
unsigned long *usedp,
|
||||
size_t *sizep,
|
||||
|
||||
@ -112,6 +112,8 @@ typedef struct git_packfile_stream {
|
||||
git_mwindow *mw;
|
||||
} git_packfile_stream;
|
||||
|
||||
int git_packfile__object_header(unsigned char *hdr, unsigned long size, git_otype type);
|
||||
|
||||
int git_packfile_unpack_header(
|
||||
size_t *size_p,
|
||||
git_otype *type_p,
|
||||
|
||||
265
src/path.c
265
src/path.c
@ -483,23 +483,23 @@ bool git_path_isfile(const char *path)
|
||||
|
||||
bool git_path_is_empty_dir(const char *path)
|
||||
{
|
||||
git_buf pathbuf = GIT_BUF_INIT;
|
||||
HANDLE hFind = INVALID_HANDLE_VALUE;
|
||||
git_win32_path wbuf;
|
||||
int wbufsz;
|
||||
WIN32_FIND_DATAW ffd;
|
||||
bool retval = true;
|
||||
|
||||
if (!git_path_isdir(path)) return false;
|
||||
if (!git_path_isdir(path))
|
||||
return false;
|
||||
|
||||
git_buf_printf(&pathbuf, "%s\\*", path);
|
||||
git_win32_path_from_c(wbuf, git_buf_cstr(&pathbuf));
|
||||
wbufsz = git_win32_path_from_c(wbuf, path);
|
||||
if (!wbufsz || wbufsz + 2 > GIT_WIN_PATH_UTF16)
|
||||
return false;
|
||||
memcpy(&wbuf[wbufsz - 1], L"\\*", 3 * sizeof(wchar_t));
|
||||
|
||||
hFind = FindFirstFileW(wbuf, &ffd);
|
||||
if (INVALID_HANDLE_VALUE == hFind) {
|
||||
giterr_set(GITERR_OS, "Couldn't open '%s'", path);
|
||||
git_buf_free(&pathbuf);
|
||||
if (INVALID_HANDLE_VALUE == hFind)
|
||||
return false;
|
||||
}
|
||||
|
||||
do {
|
||||
if (!git_path_is_dot_or_dotdotW(ffd.cFileName)) {
|
||||
@ -509,50 +509,64 @@ bool git_path_is_empty_dir(const char *path)
|
||||
} while (FindNextFileW(hFind, &ffd) != 0);
|
||||
|
||||
FindClose(hFind);
|
||||
git_buf_free(&pathbuf);
|
||||
return retval;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static int path_found_entry(void *payload, git_buf *path)
|
||||
{
|
||||
GIT_UNUSED(payload);
|
||||
return !git_path_is_dot_or_dotdot(path->ptr);
|
||||
}
|
||||
|
||||
bool git_path_is_empty_dir(const char *path)
|
||||
{
|
||||
DIR *dir = NULL;
|
||||
struct dirent *e;
|
||||
bool retval = true;
|
||||
int error;
|
||||
git_buf dir = GIT_BUF_INIT;
|
||||
|
||||
if (!git_path_isdir(path)) return false;
|
||||
|
||||
dir = opendir(path);
|
||||
if (!dir) {
|
||||
giterr_set(GITERR_OS, "Couldn't open '%s'", path);
|
||||
if (!git_path_isdir(path))
|
||||
return false;
|
||||
}
|
||||
|
||||
while ((e = readdir(dir)) != NULL) {
|
||||
if (!git_path_is_dot_or_dotdot(e->d_name)) {
|
||||
giterr_set(GITERR_INVALID,
|
||||
"'%s' exists and is not an empty directory", path);
|
||||
retval = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
if (!(error = git_buf_sets(&dir, path)))
|
||||
error = git_path_direach(&dir, 0, path_found_entry, NULL);
|
||||
|
||||
return retval;
|
||||
git_buf_free(&dir);
|
||||
|
||||
return !error;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int git_path_set_error(int errno_value, const char *path, const char *action)
|
||||
{
|
||||
switch (errno_value) {
|
||||
case ENOENT:
|
||||
case ENOTDIR:
|
||||
giterr_set(GITERR_OS, "Could not find '%s' to %s", path, action);
|
||||
return GIT_ENOTFOUND;
|
||||
|
||||
case EINVAL:
|
||||
case ENAMETOOLONG:
|
||||
giterr_set(GITERR_OS, "Invalid path for filesystem '%s'", path);
|
||||
return GIT_EINVALIDSPEC;
|
||||
|
||||
case EEXIST:
|
||||
giterr_set(GITERR_OS, "Failed %s - '%s' already exists", action, path);
|
||||
return GIT_EEXISTS;
|
||||
|
||||
default:
|
||||
giterr_set(GITERR_OS, "Could not %s '%s'", action, path);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int git_path_lstat(const char *path, struct stat *st)
|
||||
{
|
||||
int err = 0;
|
||||
if (p_lstat(path, st) == 0)
|
||||
return 0;
|
||||
|
||||
if (p_lstat(path, st) < 0) {
|
||||
err = (errno == ENOENT) ? GIT_ENOTFOUND : -1;
|
||||
giterr_set(GITERR_OS, "Failed to stat file '%s'", path);
|
||||
}
|
||||
|
||||
return err;
|
||||
return git_path_set_error(errno, path, "stat");
|
||||
}
|
||||
|
||||
static bool _check_dir_contents(
|
||||
@ -724,14 +738,103 @@ int git_path_cmp(
|
||||
return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
|
||||
}
|
||||
|
||||
bool git_path_has_non_ascii(const char *path, size_t pathlen)
|
||||
{
|
||||
const uint8_t *scan = (const uint8_t *)path, *end;
|
||||
|
||||
for (end = scan + pathlen; scan < end; ++scan)
|
||||
if (*scan & 0x80)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef GIT_USE_ICONV
|
||||
|
||||
int git_path_iconv_init_precompose(git_path_iconv_t *ic)
|
||||
{
|
||||
git_buf_init(&ic->buf, 0);
|
||||
ic->map = iconv_open(GIT_PATH_REPO_ENCODING, GIT_PATH_NATIVE_ENCODING);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void git_path_iconv_clear(git_path_iconv_t *ic)
|
||||
{
|
||||
if (ic) {
|
||||
if (ic->map != (iconv_t)-1)
|
||||
iconv_close(ic->map);
|
||||
git_buf_free(&ic->buf);
|
||||
}
|
||||
}
|
||||
|
||||
int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen)
|
||||
{
|
||||
char *nfd = *in, *nfc;
|
||||
size_t nfdlen = *inlen, nfclen, wantlen = nfdlen, rv;
|
||||
int retry = 1;
|
||||
|
||||
if (!ic || ic->map == (iconv_t)-1 ||
|
||||
!git_path_has_non_ascii(*in, *inlen))
|
||||
return 0;
|
||||
|
||||
while (1) {
|
||||
if (git_buf_grow(&ic->buf, wantlen) < 0)
|
||||
return -1;
|
||||
|
||||
nfc = ic->buf.ptr + ic->buf.size;
|
||||
nfclen = ic->buf.asize - ic->buf.size;
|
||||
|
||||
rv = iconv(ic->map, &nfd, &nfdlen, &nfc, &nfclen);
|
||||
|
||||
ic->buf.size = (nfc - ic->buf.ptr);
|
||||
|
||||
if (rv != (size_t)-1)
|
||||
break;
|
||||
|
||||
if (errno != E2BIG)
|
||||
goto fail;
|
||||
|
||||
/* make space for 2x the remaining data to be converted
|
||||
* (with per retry overhead to avoid infinite loops)
|
||||
*/
|
||||
wantlen = ic->buf.size + max(nfclen, nfdlen) * 2 + (size_t)(retry * 4);
|
||||
|
||||
if (retry++ > 4)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ic->buf.ptr[ic->buf.size] = '\0';
|
||||
|
||||
*in = ic->buf.ptr;
|
||||
*inlen = ic->buf.size;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
giterr_set(GITERR_OS, "Unable to convert unicode path data");
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__sun) || defined(__GNU__)
|
||||
typedef char path_dirent_data[sizeof(struct dirent) + FILENAME_MAX + 1];
|
||||
#else
|
||||
typedef struct dirent path_dirent_data;
|
||||
#endif
|
||||
|
||||
int git_path_direach(
|
||||
git_buf *path,
|
||||
uint32_t flags,
|
||||
int (*fn)(void *, git_buf *),
|
||||
void *arg)
|
||||
{
|
||||
int error = 0;
|
||||
ssize_t wd_len;
|
||||
DIR *dir;
|
||||
struct dirent *de, *de_buf;
|
||||
path_dirent_data de_data;
|
||||
struct dirent *de, *de_buf = (struct dirent *)&de_data;
|
||||
git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
|
||||
|
||||
if (git_path_to_dir(path) < 0)
|
||||
return -1;
|
||||
@ -743,99 +846,98 @@ int git_path_direach(
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(__sun) || defined(__GNU__)
|
||||
de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
|
||||
#else
|
||||
de_buf = git__malloc(sizeof(struct dirent));
|
||||
#endif
|
||||
if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
|
||||
(void)git_path_iconv_init_precompose(&ic);
|
||||
|
||||
while (p_readdir_r(dir, de_buf, &de) == 0 && de != NULL) {
|
||||
int result;
|
||||
char *de_path = de->d_name;
|
||||
size_t de_len = strlen(de_path);
|
||||
|
||||
if (git_path_is_dot_or_dotdot(de->d_name))
|
||||
if (git_path_is_dot_or_dotdot(de_path))
|
||||
continue;
|
||||
|
||||
if (git_buf_puts(path, de->d_name) < 0) {
|
||||
closedir(dir);
|
||||
git__free(de_buf);
|
||||
return -1;
|
||||
}
|
||||
if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0 ||
|
||||
(error = git_buf_put(path, de_path, de_len)) < 0)
|
||||
break;
|
||||
|
||||
result = fn(arg, path);
|
||||
error = fn(arg, path);
|
||||
|
||||
git_buf_truncate(path, wd_len); /* restore path */
|
||||
|
||||
if (result) {
|
||||
closedir(dir);
|
||||
git__free(de_buf);
|
||||
return GIT_EUSER;
|
||||
if (error) {
|
||||
error = GIT_EUSER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
git__free(de_buf);
|
||||
return 0;
|
||||
git_path_iconv_clear(&ic);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_path_dirload(
|
||||
const char *path,
|
||||
size_t prefix_len,
|
||||
size_t alloc_extra,
|
||||
unsigned int flags,
|
||||
git_vector *contents)
|
||||
{
|
||||
int error, need_slash;
|
||||
DIR *dir;
|
||||
struct dirent *de, *de_buf;
|
||||
size_t path_len;
|
||||
path_dirent_data de_data;
|
||||
struct dirent *de, *de_buf = (struct dirent *)&de_data;
|
||||
git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
|
||||
|
||||
assert(path && contents);
|
||||
|
||||
assert(path != NULL && contents != NULL);
|
||||
path_len = strlen(path);
|
||||
assert(path_len > 0 && path_len >= prefix_len);
|
||||
|
||||
if (!path_len || path_len < prefix_len) {
|
||||
giterr_set(GITERR_INVALID, "Invalid directory path '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
if ((dir = opendir(path)) == NULL) {
|
||||
giterr_set(GITERR_OS, "Failed to open directory '%s'", path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if defined(__sun) || defined(__GNU__)
|
||||
de_buf = git__malloc(sizeof(struct dirent) + FILENAME_MAX + 1);
|
||||
#else
|
||||
de_buf = git__malloc(sizeof(struct dirent));
|
||||
#endif
|
||||
if ((flags & GIT_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
|
||||
(void)git_path_iconv_init_precompose(&ic);
|
||||
|
||||
path += prefix_len;
|
||||
path_len -= prefix_len;
|
||||
need_slash = (path_len > 0 && path[path_len-1] != '/') ? 1 : 0;
|
||||
|
||||
while ((error = p_readdir_r(dir, de_buf, &de)) == 0 && de != NULL) {
|
||||
char *entry_path;
|
||||
size_t entry_len;
|
||||
char *entry_path, *de_path = de->d_name;
|
||||
size_t alloc_size, de_len = strlen(de_path);
|
||||
|
||||
if (git_path_is_dot_or_dotdot(de->d_name))
|
||||
if (git_path_is_dot_or_dotdot(de_path))
|
||||
continue;
|
||||
|
||||
entry_len = strlen(de->d_name);
|
||||
if ((error = git_path_iconv(&ic, &de_path, &de_len)) < 0)
|
||||
break;
|
||||
|
||||
entry_path = git__malloc(
|
||||
path_len + need_slash + entry_len + 1 + alloc_extra);
|
||||
GITERR_CHECK_ALLOC(entry_path);
|
||||
alloc_size = path_len + need_slash + de_len + 1 + alloc_extra;
|
||||
if ((entry_path = git__calloc(alloc_size, 1)) == NULL) {
|
||||
error = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (path_len)
|
||||
memcpy(entry_path, path, path_len);
|
||||
if (need_slash)
|
||||
entry_path[path_len] = '/';
|
||||
memcpy(&entry_path[path_len + need_slash], de->d_name, entry_len);
|
||||
entry_path[path_len + need_slash + entry_len] = '\0';
|
||||
memcpy(&entry_path[path_len + need_slash], de_path, de_len);
|
||||
|
||||
if (git_vector_insert(contents, entry_path) < 0) {
|
||||
closedir(dir);
|
||||
git__free(de_buf);
|
||||
return -1;
|
||||
}
|
||||
if ((error = git_vector_insert(contents, entry_path)) < 0)
|
||||
break;
|
||||
}
|
||||
|
||||
closedir(dir);
|
||||
git__free(de_buf);
|
||||
git_path_iconv_clear(&ic);
|
||||
|
||||
if (error != 0)
|
||||
giterr_set(GITERR_OS, "Failed to process directory entry in '%s'", path);
|
||||
@ -858,7 +960,7 @@ int git_path_with_stat_cmp_icase(const void *a, const void *b)
|
||||
int git_path_dirload_with_stat(
|
||||
const char *path,
|
||||
size_t prefix_len,
|
||||
bool ignore_case,
|
||||
unsigned int flags,
|
||||
const char *start_stat,
|
||||
const char *end_stat,
|
||||
git_vector *contents)
|
||||
@ -875,13 +977,14 @@ int git_path_dirload_with_stat(
|
||||
return -1;
|
||||
|
||||
error = git_path_dirload(
|
||||
path, prefix_len, sizeof(git_path_with_stat) + 1, contents);
|
||||
path, prefix_len, sizeof(git_path_with_stat) + 1, flags, contents);
|
||||
if (error < 0) {
|
||||
git_buf_free(&full);
|
||||
return error;
|
||||
}
|
||||
|
||||
strncomp = ignore_case ? git__strncasecmp : git__strncmp;
|
||||
strncomp = (flags & GIT_PATH_DIR_IGNORE_CASE) != 0 ?
|
||||
git__strncasecmp : git__strncmp;
|
||||
|
||||
/* stat struct at start of git_path_with_stat, so shift path text */
|
||||
git_vector_foreach(contents, i, ps) {
|
||||
|
||||
122
src/path.h
122
src/path.h
@ -242,21 +242,28 @@ extern int git_path_resolve_relative(git_buf *path, size_t ceiling);
|
||||
*/
|
||||
extern int git_path_apply_relative(git_buf *target, const char *relpath);
|
||||
|
||||
enum {
|
||||
GIT_PATH_DIR_IGNORE_CASE = (1u << 0),
|
||||
GIT_PATH_DIR_PRECOMPOSE_UNICODE = (1u << 1),
|
||||
};
|
||||
|
||||
/**
|
||||
* Walk each directory entry, except '.' and '..', calling fn(state).
|
||||
*
|
||||
* @param pathbuf buffer the function reads the initial directory
|
||||
* @param pathbuf Buffer the function reads the initial directory
|
||||
* path from, and updates with each successive entry's name.
|
||||
* @param fn function to invoke with each entry. The first arg is
|
||||
* the input state and the second arg is pathbuf. The function
|
||||
* may modify the pathbuf, but only by appending new text.
|
||||
* @param state to pass to fn as the first arg.
|
||||
* @param flags Combination of GIT_PATH_DIR flags.
|
||||
* @param callback Callback for each entry. Passed the `payload` and each
|
||||
* successive path inside the directory as a full path. This may
|
||||
* safely append text to the pathbuf if needed.
|
||||
* @param payload Passed to callback as first argument.
|
||||
* @return 0 on success, GIT_EUSER on non-zero callback, or error code
|
||||
*/
|
||||
extern int git_path_direach(
|
||||
git_buf *pathbuf,
|
||||
int (*fn)(void *, git_buf *),
|
||||
void *state);
|
||||
uint32_t flags,
|
||||
int (*callback)(void *payload, git_buf *path),
|
||||
void *payload);
|
||||
|
||||
/**
|
||||
* Sort function to order two paths
|
||||
@ -276,19 +283,19 @@ extern int git_path_cmp(
|
||||
* @param pathbuf Buffer the function reads the directory from and
|
||||
* and updates with each successive name.
|
||||
* @param ceiling Prefix of path at which to stop walking up. If NULL,
|
||||
* this will walk all the way up to the root. If not a prefix of
|
||||
* pathbuf, the callback will be invoked a single time on the
|
||||
* original input path.
|
||||
* @param fn Function to invoke on each path. The first arg is the
|
||||
* input satte and the second arg is the pathbuf. The function
|
||||
* should not modify the pathbuf.
|
||||
* this will walk all the way up to the root. If not a prefix of
|
||||
* pathbuf, the callback will be invoked a single time on the
|
||||
* original input path.
|
||||
* @param callback Function to invoke on each path. Passed the `payload`
|
||||
* and the buffer containing the current path. The path should not
|
||||
* be modified in any way.
|
||||
* @param state Passed to fn as the first ath.
|
||||
*/
|
||||
extern int git_path_walk_up(
|
||||
git_buf *pathbuf,
|
||||
const char *ceiling,
|
||||
int (*fn)(void *state, git_buf *),
|
||||
void *state);
|
||||
int (*callback)(void *payload, git_buf *path),
|
||||
void *payload);
|
||||
|
||||
/**
|
||||
* Load all directory entries (except '.' and '..') into a vector.
|
||||
@ -304,12 +311,14 @@ extern int git_path_walk_up(
|
||||
* prefix_len 3, the entries will look like "b/e1", "b/e2", etc.
|
||||
* @param alloc_extra Extra bytes to add to each string allocation in
|
||||
* case you want to append anything funny.
|
||||
* @param flags Combination of GIT_PATH_DIR flags.
|
||||
* @param contents Vector to fill with directory entry names.
|
||||
*/
|
||||
extern int git_path_dirload(
|
||||
const char *path,
|
||||
size_t prefix_len,
|
||||
size_t alloc_extra,
|
||||
uint32_t flags,
|
||||
git_vector *contents);
|
||||
|
||||
|
||||
@ -336,7 +345,7 @@ extern int git_path_with_stat_cmp_icase(const void *a, const void *b);
|
||||
*
|
||||
* @param path The directory to read from
|
||||
* @param prefix_len The trailing part of path to prefix to entry paths
|
||||
* @param ignore_case How to sort and compare paths with start/end limits
|
||||
* @param flags GIT_PATH_DIR flags from above
|
||||
* @param start_stat As optimization, only stat values after this prefix
|
||||
* @param end_stat As optimization, only stat values before this prefix
|
||||
* @param contents Vector to fill with git_path_with_stat structures
|
||||
@ -344,9 +353,88 @@ extern int git_path_with_stat_cmp_icase(const void *a, const void *b);
|
||||
extern int git_path_dirload_with_stat(
|
||||
const char *path,
|
||||
size_t prefix_len,
|
||||
bool ignore_case,
|
||||
uint32_t flags,
|
||||
const char *start_stat,
|
||||
const char *end_stat,
|
||||
git_vector *contents);
|
||||
|
||||
enum { GIT_PATH_NOTEQUAL = 0, GIT_PATH_EQUAL = 1, GIT_PATH_PREFIX = 2 };
|
||||
|
||||
/*
|
||||
* Determines if a path is equal to or potentially a child of another.
|
||||
* @param parent The possible parent
|
||||
* @param child The possible child
|
||||
*/
|
||||
GIT_INLINE(int) git_path_equal_or_prefixed(
|
||||
const char *parent,
|
||||
const char *child)
|
||||
{
|
||||
const char *p = parent, *c = child;
|
||||
|
||||
while (*p && *c) {
|
||||
if (*p++ != *c++)
|
||||
return GIT_PATH_NOTEQUAL;
|
||||
}
|
||||
|
||||
if (*p != '\0')
|
||||
return GIT_PATH_NOTEQUAL;
|
||||
if (*c == '\0')
|
||||
return GIT_PATH_EQUAL;
|
||||
if (*c == '/')
|
||||
return GIT_PATH_PREFIX;
|
||||
|
||||
return GIT_PATH_NOTEQUAL;
|
||||
}
|
||||
|
||||
/* translate errno to libgit2 error code and set error message */
|
||||
extern int git_path_set_error(
|
||||
int errno_value, const char *path, const char *action);
|
||||
|
||||
/* check if non-ascii characters are present in filename */
|
||||
extern bool git_path_has_non_ascii(const char *path, size_t pathlen);
|
||||
|
||||
#define GIT_PATH_REPO_ENCODING "UTF-8"
|
||||
|
||||
#ifdef __APPLE__
|
||||
#define GIT_PATH_NATIVE_ENCODING "UTF-8-MAC"
|
||||
#else
|
||||
#define GIT_PATH_NATIVE_ENCODING "UTF-8"
|
||||
#endif
|
||||
|
||||
#ifdef GIT_USE_ICONV
|
||||
|
||||
#include <iconv.h>
|
||||
|
||||
typedef struct {
|
||||
iconv_t map;
|
||||
git_buf buf;
|
||||
} git_path_iconv_t;
|
||||
|
||||
#define GIT_PATH_ICONV_INIT { (iconv_t)-1, GIT_BUF_INIT }
|
||||
|
||||
/* Init iconv data for converting decomposed UTF-8 to precomposed */
|
||||
extern int git_path_iconv_init_precompose(git_path_iconv_t *ic);
|
||||
|
||||
/* Clear allocated iconv data */
|
||||
extern void git_path_iconv_clear(git_path_iconv_t *ic);
|
||||
|
||||
/*
|
||||
* Rewrite `in` buffer using iconv map if necessary, replacing `in`
|
||||
* pointer internal iconv buffer if rewrite happened. The `in` pointer
|
||||
* will be left unchanged if no rewrite was needed.
|
||||
*/
|
||||
extern int git_path_iconv(git_path_iconv_t *ic, char **in, size_t *inlen);
|
||||
|
||||
#else
|
||||
|
||||
typedef struct {
|
||||
int unused;
|
||||
} git_path_iconv_t;
|
||||
#define GIT_PATH_ICONV_INIT { 0 }
|
||||
#define git_path_iconv_init_precompose(X) 0
|
||||
#define git_path_iconv_clear(X) (void)(X)
|
||||
#define git_path_iconv(X,Y,Z) 0
|
||||
|
||||
#endif /* GIT_USE_ICONV */
|
||||
|
||||
#endif
|
||||
|
||||
@ -585,7 +585,7 @@ int git_pathspec_match_tree(
|
||||
|
||||
int git_pathspec_match_diff(
|
||||
git_pathspec_match_list **out,
|
||||
git_diff_list *diff,
|
||||
git_diff *diff,
|
||||
uint32_t flags,
|
||||
git_pathspec *ps)
|
||||
{
|
||||
|
||||
@ -582,7 +582,7 @@ static int calculate_work(git_push *push)
|
||||
|
||||
static int do_push(git_push *push)
|
||||
{
|
||||
int error;
|
||||
int error = 0;
|
||||
git_transport *transport = push->remote->transport;
|
||||
|
||||
if (!transport->push) {
|
||||
@ -611,8 +611,6 @@ static int do_push(git_push *push)
|
||||
(error = transport->push(transport, push)) < 0)
|
||||
goto on_error;
|
||||
|
||||
error = 0;
|
||||
|
||||
on_error:
|
||||
git_packbuilder_free(push->pb);
|
||||
return error;
|
||||
|
||||
16
src/refdb.c
16
src/refdb.c
@ -16,6 +16,7 @@
|
||||
#include "hash.h"
|
||||
#include "refdb.h"
|
||||
#include "refs.h"
|
||||
#include "reflog.h"
|
||||
|
||||
int git_refdb_new(git_refdb **out, git_repository *repo)
|
||||
{
|
||||
@ -203,3 +204,18 @@ int git_refdb_delete(struct git_refdb *db, const char *ref_name)
|
||||
assert(db && db->backend);
|
||||
return db->backend->del(db->backend, ref_name);
|
||||
}
|
||||
|
||||
int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name)
|
||||
{
|
||||
int error;
|
||||
|
||||
assert(db && db->backend);
|
||||
|
||||
if ((error = db->backend->reflog_read(out, db->backend, name)) < 0)
|
||||
return error;
|
||||
|
||||
GIT_REFCOUNT_INC(db);
|
||||
(*out)->db = db;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -43,4 +43,8 @@ void git_refdb_iterator_free(git_reference_iterator *iter);
|
||||
int git_refdb_write(git_refdb *refdb, git_reference *ref, int force);
|
||||
int git_refdb_delete(git_refdb *refdb, const char *ref_name);
|
||||
|
||||
int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name);
|
||||
int git_refdb_reflog_write(git_reflog *reflog);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
365
src/refdb_fs.c
365
src/refdb_fs.c
@ -16,12 +16,14 @@
|
||||
#include "refdb_fs.h"
|
||||
#include "iterator.h"
|
||||
#include "sortedcache.h"
|
||||
#include "signature.h"
|
||||
|
||||
#include <git2/tag.h>
|
||||
#include <git2/object.h>
|
||||
#include <git2/refdb.h>
|
||||
#include <git2/sys/refdb_backend.h>
|
||||
#include <git2/sys/refs.h>
|
||||
#include <git2/sys/reflog.h>
|
||||
|
||||
GIT__USE_STRMAP;
|
||||
|
||||
@ -56,6 +58,8 @@ typedef struct refdb_fs_backend {
|
||||
|
||||
git_sortedcache *refcache;
|
||||
int peeling_mode;
|
||||
git_iterator_flag_t iterator_flags;
|
||||
uint32_t direach_flags;
|
||||
} refdb_fs_backend;
|
||||
|
||||
static int packref_cmp(const void *a_, const void *b_)
|
||||
@ -269,7 +273,8 @@ static int _dirent_loose_load(void *data, git_buf *full_path)
|
||||
return 0;
|
||||
|
||||
if (git_path_isdir(full_path->ptr))
|
||||
return git_path_direach(full_path, _dirent_loose_load, backend);
|
||||
return git_path_direach(
|
||||
full_path, backend->direach_flags, _dirent_loose_load, backend);
|
||||
|
||||
file_path = full_path->ptr + strlen(backend->path);
|
||||
|
||||
@ -295,7 +300,8 @@ static int packed_loadloose(refdb_fs_backend *backend)
|
||||
* This will overwrite any old packed entries with their
|
||||
* updated loose versions
|
||||
*/
|
||||
error = git_path_direach(&refs_path, _dirent_loose_load, backend);
|
||||
error = git_path_direach(
|
||||
&refs_path, backend->direach_flags, _dirent_loose_load, backend);
|
||||
|
||||
git_buf_free(&refs_path);
|
||||
|
||||
@ -468,7 +474,7 @@ static int iter_load_loose_paths(refdb_fs_backend *backend, refdb_fs_iter *iter)
|
||||
|
||||
if ((error = git_buf_printf(&path, "%s/refs", backend->path)) < 0 ||
|
||||
(error = git_iterator_for_filesystem(
|
||||
&fsit, git_buf_cstr(&path), 0, NULL, NULL)) < 0) {
|
||||
&fsit, path.ptr, backend->iterator_flags, NULL, NULL)) < 0) {
|
||||
git_buf_free(&path);
|
||||
return error;
|
||||
}
|
||||
@ -1067,10 +1073,350 @@ static int setup_namespace(git_buf *path, git_repository *repo)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reflog_alloc(git_reflog **reflog, const char *name)
|
||||
{
|
||||
git_reflog *log;
|
||||
|
||||
*reflog = NULL;
|
||||
|
||||
log = git__calloc(1, sizeof(git_reflog));
|
||||
GITERR_CHECK_ALLOC(log);
|
||||
|
||||
log->ref_name = git__strdup(name);
|
||||
GITERR_CHECK_ALLOC(log->ref_name);
|
||||
|
||||
if (git_vector_init(&log->entries, 0, NULL) < 0) {
|
||||
git__free(log->ref_name);
|
||||
git__free(log);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*reflog = log;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
|
||||
{
|
||||
const char *ptr;
|
||||
git_reflog_entry *entry;
|
||||
|
||||
#define seek_forward(_increase) do { \
|
||||
if (_increase >= buf_size) { \
|
||||
giterr_set(GITERR_INVALID, "Ran out of data while parsing reflog"); \
|
||||
goto fail; \
|
||||
} \
|
||||
buf += _increase; \
|
||||
buf_size -= _increase; \
|
||||
} while (0)
|
||||
|
||||
while (buf_size > GIT_REFLOG_SIZE_MIN) {
|
||||
entry = git__calloc(1, sizeof(git_reflog_entry));
|
||||
GITERR_CHECK_ALLOC(entry);
|
||||
|
||||
entry->committer = git__malloc(sizeof(git_signature));
|
||||
GITERR_CHECK_ALLOC(entry->committer);
|
||||
|
||||
if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0)
|
||||
goto fail;
|
||||
seek_forward(GIT_OID_HEXSZ + 1);
|
||||
|
||||
if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < 0)
|
||||
goto fail;
|
||||
seek_forward(GIT_OID_HEXSZ + 1);
|
||||
|
||||
ptr = buf;
|
||||
|
||||
/* Seek forward to the end of the signature. */
|
||||
while (*buf && *buf != '\t' && *buf != '\n')
|
||||
seek_forward(1);
|
||||
|
||||
if (git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf) < 0)
|
||||
goto fail;
|
||||
|
||||
if (*buf == '\t') {
|
||||
/* We got a message. Read everything till we reach LF. */
|
||||
seek_forward(1);
|
||||
ptr = buf;
|
||||
|
||||
while (*buf && *buf != '\n')
|
||||
seek_forward(1);
|
||||
|
||||
entry->msg = git__strndup(ptr, buf - ptr);
|
||||
GITERR_CHECK_ALLOC(entry->msg);
|
||||
} else
|
||||
entry->msg = NULL;
|
||||
|
||||
while (*buf && *buf == '\n' && buf_size > 1)
|
||||
seek_forward(1);
|
||||
|
||||
if (git_vector_insert(&log->entries, entry) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
#undef seek_forward
|
||||
|
||||
fail:
|
||||
if (entry)
|
||||
git_reflog_entry__free(entry);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int create_new_reflog_file(const char *filepath)
|
||||
{
|
||||
int fd, error;
|
||||
|
||||
if ((error = git_futils_mkpath2file(filepath, GIT_REFLOG_DIR_MODE)) < 0)
|
||||
return error;
|
||||
|
||||
if ((fd = p_open(filepath,
|
||||
O_WRONLY | O_CREAT | O_TRUNC,
|
||||
GIT_REFLOG_FILE_MODE)) < 0)
|
||||
return -1;
|
||||
|
||||
return p_close(fd);
|
||||
}
|
||||
|
||||
GIT_INLINE(int) retrieve_reflog_path(git_buf *path, git_repository *repo, const char *name)
|
||||
{
|
||||
return git_buf_join_n(path, '/', 3, repo->path_repository, GIT_REFLOG_DIR, name);
|
||||
}
|
||||
|
||||
static int refdb_reflog_fs__read(git_reflog **out, git_refdb_backend *_backend, const char *name)
|
||||
{
|
||||
int error = -1;
|
||||
git_buf log_path = GIT_BUF_INIT;
|
||||
git_buf log_file = GIT_BUF_INIT;
|
||||
git_reflog *log = NULL;
|
||||
git_repository *repo;
|
||||
refdb_fs_backend *backend;
|
||||
|
||||
assert(out && _backend && name);
|
||||
|
||||
backend = (refdb_fs_backend *) _backend;
|
||||
repo = backend->repo;
|
||||
|
||||
if (reflog_alloc(&log, name) < 0)
|
||||
return -1;
|
||||
|
||||
if (retrieve_reflog_path(&log_path, repo, name) < 0)
|
||||
goto cleanup;
|
||||
|
||||
error = git_futils_readbuffer(&log_file, git_buf_cstr(&log_path));
|
||||
if (error < 0 && error != GIT_ENOTFOUND)
|
||||
goto cleanup;
|
||||
|
||||
if ((error == GIT_ENOTFOUND) &&
|
||||
((error = create_new_reflog_file(git_buf_cstr(&log_path))) < 0))
|
||||
goto cleanup;
|
||||
|
||||
if ((error = reflog_parse(log,
|
||||
git_buf_cstr(&log_file), git_buf_len(&log_file))) < 0)
|
||||
goto cleanup;
|
||||
|
||||
*out = log;
|
||||
goto success;
|
||||
|
||||
cleanup:
|
||||
git_reflog_free(log);
|
||||
|
||||
success:
|
||||
git_buf_free(&log_file);
|
||||
git_buf_free(&log_path);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int serialize_reflog_entry(
|
||||
git_buf *buf,
|
||||
const git_oid *oid_old,
|
||||
const git_oid *oid_new,
|
||||
const git_signature *committer,
|
||||
const char *msg)
|
||||
{
|
||||
char raw_old[GIT_OID_HEXSZ+1];
|
||||
char raw_new[GIT_OID_HEXSZ+1];
|
||||
|
||||
git_oid_tostr(raw_old, GIT_OID_HEXSZ+1, oid_old);
|
||||
git_oid_tostr(raw_new, GIT_OID_HEXSZ+1, oid_new);
|
||||
|
||||
git_buf_clear(buf);
|
||||
|
||||
git_buf_puts(buf, raw_old);
|
||||
git_buf_putc(buf, ' ');
|
||||
git_buf_puts(buf, raw_new);
|
||||
|
||||
git_signature__writebuf(buf, " ", committer);
|
||||
|
||||
/* drop trailing LF */
|
||||
git_buf_rtrim(buf);
|
||||
|
||||
if (msg) {
|
||||
git_buf_putc(buf, '\t');
|
||||
git_buf_puts(buf, msg);
|
||||
}
|
||||
|
||||
git_buf_putc(buf, '\n');
|
||||
|
||||
return git_buf_oom(buf);
|
||||
}
|
||||
|
||||
static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflog)
|
||||
{
|
||||
int error = -1;
|
||||
unsigned int i;
|
||||
git_reflog_entry *entry;
|
||||
git_repository *repo;
|
||||
refdb_fs_backend *backend;
|
||||
git_buf log_path = GIT_BUF_INIT;
|
||||
git_buf log = GIT_BUF_INIT;
|
||||
git_filebuf fbuf = GIT_FILEBUF_INIT;
|
||||
|
||||
assert(_backend && reflog);
|
||||
|
||||
backend = (refdb_fs_backend *) _backend;
|
||||
repo = backend->repo;
|
||||
|
||||
if (retrieve_reflog_path(&log_path, repo, reflog->ref_name) < 0)
|
||||
return -1;
|
||||
|
||||
if (!git_path_isfile(git_buf_cstr(&log_path))) {
|
||||
giterr_set(GITERR_INVALID,
|
||||
"Log file for reference '%s' doesn't exist.", reflog->ref_name);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((error = git_filebuf_open(&fbuf, git_buf_cstr(&log_path), 0)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
git_vector_foreach(&reflog->entries, i, entry) {
|
||||
if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE);
|
||||
goto success;
|
||||
|
||||
cleanup:
|
||||
git_filebuf_cleanup(&fbuf);
|
||||
|
||||
success:
|
||||
git_buf_free(&log);
|
||||
git_buf_free(&log_path);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name)
|
||||
{
|
||||
int error = 0, fd;
|
||||
git_buf old_path = GIT_BUF_INIT;
|
||||
git_buf new_path = GIT_BUF_INIT;
|
||||
git_buf temp_path = GIT_BUF_INIT;
|
||||
git_buf normalized = GIT_BUF_INIT;
|
||||
git_repository *repo;
|
||||
refdb_fs_backend *backend;
|
||||
|
||||
assert(_backend && old_name && new_name);
|
||||
|
||||
backend = (refdb_fs_backend *) _backend;
|
||||
repo = backend->repo;
|
||||
|
||||
if ((error = git_reference__normalize_name(
|
||||
&normalized, new_name, GIT_REF_FORMAT_ALLOW_ONELEVEL)) < 0)
|
||||
return error;
|
||||
|
||||
if (git_buf_joinpath(&temp_path, repo->path_repository, GIT_REFLOG_DIR) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), old_name) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_buf_joinpath(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized)) < 0)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Move the reflog to a temporary place. This two-phase renaming is required
|
||||
* in order to cope with funny renaming use cases when one tries to move a reference
|
||||
* to a partially colliding namespace:
|
||||
* - a/b -> a/b/c
|
||||
* - a/b/c/d -> a/b/c
|
||||
*/
|
||||
if (git_buf_joinpath(&temp_path, git_buf_cstr(&temp_path), "temp_reflog") < 0)
|
||||
return -1;
|
||||
|
||||
if ((fd = git_futils_mktmp(&temp_path, git_buf_cstr(&temp_path))) < 0) {
|
||||
error = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
p_close(fd);
|
||||
|
||||
if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) {
|
||||
giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
|
||||
error = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (git_path_isdir(git_buf_cstr(&new_path)) &&
|
||||
(git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) {
|
||||
error = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0) {
|
||||
error = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)) < 0) {
|
||||
giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
|
||||
error = -1;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&temp_path);
|
||||
git_buf_free(&old_path);
|
||||
git_buf_free(&new_path);
|
||||
git_buf_free(&normalized);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name)
|
||||
{
|
||||
int error;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
|
||||
git_repository *repo;
|
||||
refdb_fs_backend *backend;
|
||||
|
||||
assert(_backend && name);
|
||||
|
||||
backend = (refdb_fs_backend *) _backend;
|
||||
repo = backend->repo;
|
||||
|
||||
error = retrieve_reflog_path(&path, repo, name);
|
||||
|
||||
if (!error && git_path_exists(path.ptr))
|
||||
error = p_unlink(path.ptr);
|
||||
|
||||
git_buf_free(&path);
|
||||
|
||||
return error;
|
||||
|
||||
}
|
||||
|
||||
int git_refdb_backend_fs(
|
||||
git_refdb_backend **backend_out,
|
||||
git_repository *repository)
|
||||
{
|
||||
int t = 0;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
refdb_fs_backend *backend;
|
||||
|
||||
@ -1092,6 +1438,15 @@ int git_refdb_backend_fs(
|
||||
|
||||
git_buf_free(&path);
|
||||
|
||||
if (!git_repository__cvar(&t, backend->repo, GIT_CVAR_IGNORECASE) && t) {
|
||||
backend->iterator_flags |= GIT_ITERATOR_IGNORE_CASE;
|
||||
backend->direach_flags |= GIT_PATH_DIR_IGNORE_CASE;
|
||||
}
|
||||
if (!git_repository__cvar(&t, backend->repo, GIT_CVAR_PRECOMPOSE) && t) {
|
||||
backend->iterator_flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
|
||||
backend->direach_flags |= GIT_PATH_DIR_PRECOMPOSE_UNICODE;
|
||||
}
|
||||
|
||||
backend->parent.exists = &refdb_fs_backend__exists;
|
||||
backend->parent.lookup = &refdb_fs_backend__lookup;
|
||||
backend->parent.iterator = &refdb_fs_backend__iterator;
|
||||
@ -1100,6 +1455,10 @@ int git_refdb_backend_fs(
|
||||
backend->parent.rename = &refdb_fs_backend__rename;
|
||||
backend->parent.compress = &refdb_fs_backend__compress;
|
||||
backend->parent.free = &refdb_fs_backend__free;
|
||||
backend->parent.reflog_read = &refdb_reflog_fs__read;
|
||||
backend->parent.reflog_write = &refdb_reflog_fs__write;
|
||||
backend->parent.reflog_rename = &refdb_reflog_fs__rename;
|
||||
backend->parent.reflog_delete = &refdb_reflog_fs__delete;
|
||||
|
||||
*backend_out = (git_refdb_backend *)backend;
|
||||
return 0;
|
||||
|
||||
384
src/reflog.c
384
src/reflog.c
@ -9,82 +9,16 @@
|
||||
#include "repository.h"
|
||||
#include "filebuf.h"
|
||||
#include "signature.h"
|
||||
#include "refdb.h"
|
||||
|
||||
static int reflog_init(git_reflog **reflog, const git_reference *ref)
|
||||
#include <git2/sys/refdb_backend.h>
|
||||
|
||||
git_reflog_entry *git_reflog_entry__alloc(void)
|
||||
{
|
||||
git_reflog *log;
|
||||
|
||||
*reflog = NULL;
|
||||
|
||||
log = git__calloc(1, sizeof(git_reflog));
|
||||
GITERR_CHECK_ALLOC(log);
|
||||
|
||||
log->ref_name = git__strdup(ref->name);
|
||||
GITERR_CHECK_ALLOC(log->ref_name);
|
||||
|
||||
if (git_vector_init(&log->entries, 0, NULL) < 0) {
|
||||
git__free(log->ref_name);
|
||||
git__free(log);
|
||||
return -1;
|
||||
}
|
||||
|
||||
log->owner = git_reference_owner(ref);
|
||||
*reflog = log;
|
||||
|
||||
return 0;
|
||||
return git__calloc(1, sizeof(git_reflog_entry));
|
||||
}
|
||||
|
||||
static int serialize_reflog_entry(
|
||||
git_buf *buf,
|
||||
const git_oid *oid_old,
|
||||
const git_oid *oid_new,
|
||||
const git_signature *committer,
|
||||
const char *msg)
|
||||
{
|
||||
char raw_old[GIT_OID_HEXSZ+1];
|
||||
char raw_new[GIT_OID_HEXSZ+1];
|
||||
|
||||
git_oid_tostr(raw_old, GIT_OID_HEXSZ+1, oid_old);
|
||||
git_oid_tostr(raw_new, GIT_OID_HEXSZ+1, oid_new);
|
||||
|
||||
git_buf_clear(buf);
|
||||
|
||||
git_buf_puts(buf, raw_old);
|
||||
git_buf_putc(buf, ' ');
|
||||
git_buf_puts(buf, raw_new);
|
||||
|
||||
git_signature__writebuf(buf, " ", committer);
|
||||
|
||||
/* drop trailing LF */
|
||||
git_buf_rtrim(buf);
|
||||
|
||||
if (msg) {
|
||||
git_buf_putc(buf, '\t');
|
||||
git_buf_puts(buf, msg);
|
||||
}
|
||||
|
||||
git_buf_putc(buf, '\n');
|
||||
|
||||
return git_buf_oom(buf);
|
||||
}
|
||||
|
||||
static int reflog_entry_new(git_reflog_entry **entry)
|
||||
{
|
||||
git_reflog_entry *e;
|
||||
|
||||
assert(entry);
|
||||
|
||||
e = git__malloc(sizeof(git_reflog_entry));
|
||||
GITERR_CHECK_ALLOC(e);
|
||||
|
||||
memset(e, 0, sizeof(git_reflog_entry));
|
||||
|
||||
*entry = e;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void reflog_entry_free(git_reflog_entry *entry)
|
||||
void git_reflog_entry__free(git_reflog_entry *entry)
|
||||
{
|
||||
git_signature_free(entry->committer);
|
||||
|
||||
@ -92,75 +26,6 @@ static void reflog_entry_free(git_reflog_entry *entry)
|
||||
git__free(entry);
|
||||
}
|
||||
|
||||
static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
|
||||
{
|
||||
const char *ptr;
|
||||
git_reflog_entry *entry;
|
||||
|
||||
#define seek_forward(_increase) do { \
|
||||
if (_increase >= buf_size) { \
|
||||
giterr_set(GITERR_INVALID, "Ran out of data while parsing reflog"); \
|
||||
goto fail; \
|
||||
} \
|
||||
buf += _increase; \
|
||||
buf_size -= _increase; \
|
||||
} while (0)
|
||||
|
||||
while (buf_size > GIT_REFLOG_SIZE_MIN) {
|
||||
if (reflog_entry_new(&entry) < 0)
|
||||
return -1;
|
||||
|
||||
entry->committer = git__malloc(sizeof(git_signature));
|
||||
GITERR_CHECK_ALLOC(entry->committer);
|
||||
|
||||
if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0)
|
||||
goto fail;
|
||||
seek_forward(GIT_OID_HEXSZ + 1);
|
||||
|
||||
if (git_oid_fromstrn(&entry->oid_cur, buf, GIT_OID_HEXSZ) < 0)
|
||||
goto fail;
|
||||
seek_forward(GIT_OID_HEXSZ + 1);
|
||||
|
||||
ptr = buf;
|
||||
|
||||
/* Seek forward to the end of the signature. */
|
||||
while (*buf && *buf != '\t' && *buf != '\n')
|
||||
seek_forward(1);
|
||||
|
||||
if (git_signature__parse(entry->committer, &ptr, buf + 1, NULL, *buf) < 0)
|
||||
goto fail;
|
||||
|
||||
if (*buf == '\t') {
|
||||
/* We got a message. Read everything till we reach LF. */
|
||||
seek_forward(1);
|
||||
ptr = buf;
|
||||
|
||||
while (*buf && *buf != '\n')
|
||||
seek_forward(1);
|
||||
|
||||
entry->msg = git__strndup(ptr, buf - ptr);
|
||||
GITERR_CHECK_ALLOC(entry->msg);
|
||||
} else
|
||||
entry->msg = NULL;
|
||||
|
||||
while (*buf && *buf == '\n' && buf_size > 1)
|
||||
seek_forward(1);
|
||||
|
||||
if (git_vector_insert(&log->entries, entry) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
#undef seek_forward
|
||||
|
||||
fail:
|
||||
if (entry)
|
||||
reflog_entry_free(entry);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void git_reflog_free(git_reflog *reflog)
|
||||
{
|
||||
size_t i;
|
||||
@ -169,10 +34,13 @@ void git_reflog_free(git_reflog *reflog)
|
||||
if (reflog == NULL)
|
||||
return;
|
||||
|
||||
if (reflog->db)
|
||||
GIT_REFCOUNT_DEC(reflog->db, git_refdb__free);
|
||||
|
||||
for (i=0; i < reflog->entries.length; i++) {
|
||||
entry = git_vector_get(&reflog->entries, i);
|
||||
|
||||
reflog_entry_free(entry);
|
||||
git_reflog_entry__free(entry);
|
||||
}
|
||||
|
||||
git_vector_free(&reflog->entries);
|
||||
@ -180,115 +48,30 @@ void git_reflog_free(git_reflog *reflog)
|
||||
git__free(reflog);
|
||||
}
|
||||
|
||||
static int retrieve_reflog_path(git_buf *path, const git_reference *ref)
|
||||
int git_reflog_read(git_reflog **reflog, git_repository *repo, const char *name)
|
||||
{
|
||||
return git_buf_join_n(path, '/', 3,
|
||||
git_reference_owner(ref)->path_repository, GIT_REFLOG_DIR, ref->name);
|
||||
}
|
||||
git_refdb *refdb;
|
||||
int error;
|
||||
|
||||
static int create_new_reflog_file(const char *filepath)
|
||||
{
|
||||
int fd, error;
|
||||
assert(reflog && repo && name);
|
||||
|
||||
if ((error = git_futils_mkpath2file(filepath, GIT_REFLOG_DIR_MODE)) < 0)
|
||||
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
|
||||
return error;
|
||||
|
||||
if ((fd = p_open(filepath,
|
||||
O_WRONLY | O_CREAT | O_TRUNC,
|
||||
GIT_REFLOG_FILE_MODE)) < 0)
|
||||
return -1;
|
||||
|
||||
return p_close(fd);
|
||||
}
|
||||
|
||||
int git_reflog_read(git_reflog **reflog, const git_reference *ref)
|
||||
{
|
||||
int error = -1;
|
||||
git_buf log_path = GIT_BUF_INIT;
|
||||
git_buf log_file = GIT_BUF_INIT;
|
||||
git_reflog *log = NULL;
|
||||
|
||||
assert(reflog && ref);
|
||||
|
||||
*reflog = NULL;
|
||||
|
||||
if (reflog_init(&log, ref) < 0)
|
||||
return -1;
|
||||
|
||||
if (retrieve_reflog_path(&log_path, ref) < 0)
|
||||
goto cleanup;
|
||||
|
||||
error = git_futils_readbuffer(&log_file, git_buf_cstr(&log_path));
|
||||
if (error < 0 && error != GIT_ENOTFOUND)
|
||||
goto cleanup;
|
||||
|
||||
if ((error == GIT_ENOTFOUND) &&
|
||||
((error = create_new_reflog_file(git_buf_cstr(&log_path))) < 0))
|
||||
goto cleanup;
|
||||
|
||||
if ((error = reflog_parse(log,
|
||||
git_buf_cstr(&log_file), git_buf_len(&log_file))) < 0)
|
||||
goto cleanup;
|
||||
|
||||
*reflog = log;
|
||||
goto success;
|
||||
|
||||
cleanup:
|
||||
git_reflog_free(log);
|
||||
|
||||
success:
|
||||
git_buf_free(&log_file);
|
||||
git_buf_free(&log_path);
|
||||
|
||||
return error;
|
||||
return git_refdb_reflog_read(reflog, refdb, name);
|
||||
}
|
||||
|
||||
int git_reflog_write(git_reflog *reflog)
|
||||
{
|
||||
int error = -1;
|
||||
unsigned int i;
|
||||
git_reflog_entry *entry;
|
||||
git_buf log_path = GIT_BUF_INIT;
|
||||
git_buf log = GIT_BUF_INIT;
|
||||
git_filebuf fbuf = GIT_FILEBUF_INIT;
|
||||
git_refdb *db;
|
||||
|
||||
assert(reflog);
|
||||
assert(reflog && reflog->db);
|
||||
|
||||
if (git_buf_join_n(&log_path, '/', 3,
|
||||
git_repository_path(reflog->owner), GIT_REFLOG_DIR, reflog->ref_name) < 0)
|
||||
return -1;
|
||||
|
||||
if (!git_path_isfile(git_buf_cstr(&log_path))) {
|
||||
giterr_set(GITERR_INVALID,
|
||||
"Log file for reference '%s' doesn't exist.", reflog->ref_name);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if ((error = git_filebuf_open(&fbuf, git_buf_cstr(&log_path), 0)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
git_vector_foreach(&reflog->entries, i, entry) {
|
||||
if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
error = git_filebuf_commit(&fbuf, GIT_REFLOG_FILE_MODE);
|
||||
goto success;
|
||||
|
||||
cleanup:
|
||||
git_filebuf_cleanup(&fbuf);
|
||||
|
||||
success:
|
||||
git_buf_free(&log);
|
||||
git_buf_free(&log_path);
|
||||
return error;
|
||||
db = reflog->db;
|
||||
return db->backend->reflog_write(db->backend, reflog);
|
||||
}
|
||||
|
||||
int git_reflog_append(git_reflog *reflog, const git_oid *new_oid,
|
||||
const git_signature *committer, const char *msg)
|
||||
int git_reflog_append(git_reflog *reflog, const git_oid *new_oid, const git_signature *committer, const char *msg)
|
||||
{
|
||||
git_reflog_entry *entry;
|
||||
const git_reflog_entry *previous;
|
||||
@ -296,8 +79,8 @@ int git_reflog_append(git_reflog *reflog, const git_oid *new_oid,
|
||||
|
||||
assert(reflog && new_oid && committer);
|
||||
|
||||
if (reflog_entry_new(&entry) < 0)
|
||||
return -1;
|
||||
entry = git__calloc(1, sizeof(git_reflog_entry));
|
||||
GITERR_CHECK_ALLOC(entry);
|
||||
|
||||
if ((entry->committer = git_signature_dup(committer)) == NULL)
|
||||
goto cleanup;
|
||||
@ -333,94 +116,30 @@ int git_reflog_append(git_reflog *reflog, const git_oid *new_oid,
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
reflog_entry_free(entry);
|
||||
git_reflog_entry__free(entry);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int git_reflog_rename(git_reference *ref, const char *new_name)
|
||||
int git_reflog_rename(git_repository *repo, const char *old_name, const char *new_name)
|
||||
{
|
||||
int error = 0, fd;
|
||||
git_buf old_path = GIT_BUF_INIT;
|
||||
git_buf new_path = GIT_BUF_INIT;
|
||||
git_buf temp_path = GIT_BUF_INIT;
|
||||
git_buf normalized = GIT_BUF_INIT;
|
||||
git_refdb *refdb;
|
||||
int error;
|
||||
|
||||
assert(ref && new_name);
|
||||
|
||||
if ((error = git_reference__normalize_name(
|
||||
&normalized, new_name, GIT_REF_FORMAT_ALLOW_ONELEVEL)) < 0)
|
||||
return error;
|
||||
|
||||
if (git_buf_joinpath(&temp_path, git_reference_owner(ref)->path_repository, GIT_REFLOG_DIR) < 0)
|
||||
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_buf_joinpath(&old_path, git_buf_cstr(&temp_path), ref->name) < 0)
|
||||
return -1;
|
||||
|
||||
if (git_buf_joinpath(&new_path, git_buf_cstr(&temp_path), git_buf_cstr(&normalized)) < 0)
|
||||
return -1;
|
||||
|
||||
/*
|
||||
* Move the reflog to a temporary place. This two-phase renaming is required
|
||||
* in order to cope with funny renaming use cases when one tries to move a reference
|
||||
* to a partially colliding namespace:
|
||||
* - a/b -> a/b/c
|
||||
* - a/b/c/d -> a/b/c
|
||||
*/
|
||||
if (git_buf_joinpath(&temp_path, git_buf_cstr(&temp_path), "temp_reflog") < 0)
|
||||
return -1;
|
||||
|
||||
if ((fd = git_futils_mktmp(&temp_path, git_buf_cstr(&temp_path))) < 0) {
|
||||
error = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
p_close(fd);
|
||||
|
||||
if (p_rename(git_buf_cstr(&old_path), git_buf_cstr(&temp_path)) < 0) {
|
||||
giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
|
||||
error = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (git_path_isdir(git_buf_cstr(&new_path)) &&
|
||||
(git_futils_rmdir_r(git_buf_cstr(&new_path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) {
|
||||
error = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (git_futils_mkpath2file(git_buf_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0) {
|
||||
error = -1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (p_rename(git_buf_cstr(&temp_path), git_buf_cstr(&new_path)) < 0) {
|
||||
giterr_set(GITERR_OS, "Failed to rename reflog for %s", new_name);
|
||||
error = -1;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&temp_path);
|
||||
git_buf_free(&old_path);
|
||||
git_buf_free(&new_path);
|
||||
git_buf_free(&normalized);
|
||||
|
||||
return error;
|
||||
return refdb->backend->reflog_rename(refdb->backend, old_name, new_name);
|
||||
}
|
||||
|
||||
int git_reflog_delete(git_reference *ref)
|
||||
int git_reflog_delete(git_repository *repo, const char *name)
|
||||
{
|
||||
git_refdb *refdb;
|
||||
int error;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
|
||||
error = retrieve_reflog_path(&path, ref);
|
||||
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
|
||||
return -1;
|
||||
|
||||
if (!error && git_path_exists(path.ptr))
|
||||
error = p_unlink(path.ptr);
|
||||
|
||||
git_buf_free(&path);
|
||||
|
||||
return error;
|
||||
return refdb->backend->reflog_delete(refdb->backend, name);
|
||||
}
|
||||
|
||||
size_t git_reflog_entrycount(git_reflog *reflog)
|
||||
@ -429,11 +148,6 @@ size_t git_reflog_entrycount(git_reflog *reflog)
|
||||
return reflog->entries.length;
|
||||
}
|
||||
|
||||
GIT_INLINE(size_t) reflog_inverse_index(size_t idx, size_t total)
|
||||
{
|
||||
return (total - 1) - idx;
|
||||
}
|
||||
|
||||
const git_reflog_entry * git_reflog_entry_byindex(git_reflog *reflog, size_t idx)
|
||||
{
|
||||
assert(reflog);
|
||||
@ -469,26 +183,21 @@ const char * git_reflog_entry_message(const git_reflog_entry *entry)
|
||||
return entry->msg;
|
||||
}
|
||||
|
||||
int git_reflog_drop(
|
||||
git_reflog *reflog,
|
||||
size_t idx,
|
||||
int rewrite_previous_entry)
|
||||
int git_reflog_drop(git_reflog *reflog, size_t idx, int rewrite_previous_entry)
|
||||
{
|
||||
size_t entrycount;
|
||||
git_reflog_entry *entry, *previous;
|
||||
|
||||
assert(reflog);
|
||||
|
||||
entrycount = git_reflog_entrycount(reflog);
|
||||
|
||||
entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx);
|
||||
|
||||
if (entry == NULL) {
|
||||
giterr_set(GITERR_REFERENCE, "No reflog entry at index %"PRIuZ, idx);
|
||||
giterr_set(GITERR_REFERENCE, "No reflog entry at index "PRIuZ, idx);
|
||||
return GIT_ENOTFOUND;
|
||||
}
|
||||
|
||||
reflog_entry_free(entry);
|
||||
git_reflog_entry__free(entry);
|
||||
|
||||
if (git_vector_remove(
|
||||
&reflog->entries, reflog_inverse_index(idx, entrycount)) < 0)
|
||||
@ -521,3 +230,22 @@ int git_reflog_drop(
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_reflog_append_to(git_repository *repo, const char *name, const git_oid *id,
|
||||
const git_signature *committer, const char *msg)
|
||||
{
|
||||
int error;
|
||||
git_reflog *reflog;
|
||||
|
||||
if ((error = git_reflog_read(&reflog, repo, name)) < 0)
|
||||
return error;
|
||||
|
||||
if ((error = git_reflog_append(reflog, id, committer, msg)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
error = git_reflog_write(reflog);
|
||||
|
||||
cleanup:
|
||||
git_reflog_free(reflog);
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -27,9 +27,14 @@ struct git_reflog_entry {
|
||||
};
|
||||
|
||||
struct git_reflog {
|
||||
git_refdb *db;
|
||||
char *ref_name;
|
||||
git_repository *owner;
|
||||
git_vector entries;
|
||||
};
|
||||
|
||||
GIT_INLINE(size_t) reflog_inverse_index(size_t idx, size_t total)
|
||||
{
|
||||
return (total - 1) - idx;
|
||||
}
|
||||
|
||||
#endif /* INCLUDE_reflog_h__ */
|
||||
|
||||
70
src/refs.c
70
src/refs.c
@ -138,6 +138,22 @@ int git_reference_name_to_id(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int reference_normalize_for_repo(
|
||||
char *out,
|
||||
size_t out_size,
|
||||
git_repository *repo,
|
||||
const char *name)
|
||||
{
|
||||
int precompose;
|
||||
unsigned int flags = GIT_REF_FORMAT_ALLOW_ONELEVEL;
|
||||
|
||||
if (!git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) &&
|
||||
precompose)
|
||||
flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE;
|
||||
|
||||
return git_reference_normalize_name(out, out_size, name, flags);
|
||||
}
|
||||
|
||||
int git_reference_lookup_resolved(
|
||||
git_reference **ref_out,
|
||||
git_repository *repo,
|
||||
@ -159,13 +175,13 @@ int git_reference_lookup_resolved(
|
||||
else if (max_nesting < 0)
|
||||
max_nesting = DEFAULT_NESTING_LEVEL;
|
||||
|
||||
strncpy(scan_name, name, GIT_REFNAME_MAX);
|
||||
scan_type = GIT_REF_SYMBOLIC;
|
||||
|
||||
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
|
||||
return -1;
|
||||
if ((error = reference_normalize_for_repo(
|
||||
scan_name, sizeof(scan_name), repo, name)) < 0)
|
||||
return error;
|
||||
|
||||
if ((error = git_reference__normalize_name_lax(scan_name, GIT_REFNAME_MAX, name)) < 0)
|
||||
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
|
||||
return error;
|
||||
|
||||
for (nesting = max_nesting;
|
||||
@ -173,7 +189,7 @@ int git_reference_lookup_resolved(
|
||||
nesting--)
|
||||
{
|
||||
if (nesting != max_nesting) {
|
||||
strncpy(scan_name, ref->target.symbolic, GIT_REFNAME_MAX);
|
||||
strncpy(scan_name, ref->target.symbolic, sizeof(scan_name));
|
||||
git_reference_free(ref);
|
||||
}
|
||||
|
||||
@ -467,7 +483,7 @@ int git_reference_rename(
|
||||
if (reference_has_log < 0)
|
||||
return reference_has_log;
|
||||
|
||||
if (reference_has_log && (error = git_reflog_rename(ref, new_name)) < 0)
|
||||
if (reference_has_log && (error = git_reflog_rename(git_reference_owner(ref), git_reference_name(ref), new_name)) < 0)
|
||||
return error;
|
||||
|
||||
return 0;
|
||||
@ -711,17 +727,18 @@ static bool is_all_caps_and_underscore(const char *name, size_t len)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100 */
|
||||
int git_reference__normalize_name(
|
||||
git_buf *buf,
|
||||
const char *name,
|
||||
unsigned int flags)
|
||||
{
|
||||
// Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100
|
||||
|
||||
char *current;
|
||||
int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
|
||||
unsigned int process_flags;
|
||||
bool normalize = (buf != NULL);
|
||||
git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
|
||||
|
||||
assert(name);
|
||||
|
||||
process_flags = flags;
|
||||
@ -733,6 +750,14 @@ int git_reference__normalize_name(
|
||||
if (normalize)
|
||||
git_buf_clear(buf);
|
||||
|
||||
if ((flags & GIT_REF_FORMAT__PRECOMPOSE_UNICODE) != 0) {
|
||||
size_t namelen = strlen(current);
|
||||
if ((error = git_path_iconv_init_precompose(&ic)) < 0 ||
|
||||
(error = git_path_iconv(&ic, ¤t, &namelen)) < 0)
|
||||
goto cleanup;
|
||||
error = GIT_EINVALIDSPEC;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
segment_len = ensure_segment_validity(current);
|
||||
if (segment_len < 0) {
|
||||
@ -809,6 +834,8 @@ cleanup:
|
||||
if (error && normalize)
|
||||
git_buf_free(buf);
|
||||
|
||||
git_path_iconv_clear(&ic);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -983,9 +1010,9 @@ static int peel_error(int error, git_reference *ref, const char* msg)
|
||||
}
|
||||
|
||||
int git_reference_peel(
|
||||
git_object **peeled,
|
||||
git_reference *ref,
|
||||
git_otype target_type)
|
||||
git_object **peeled,
|
||||
git_reference *ref,
|
||||
git_otype target_type)
|
||||
{
|
||||
git_reference *resolved = NULL;
|
||||
git_object *target = NULL;
|
||||
@ -1027,24 +1054,19 @@ cleanup:
|
||||
return error;
|
||||
}
|
||||
|
||||
int git_reference__is_valid_name(
|
||||
const char *refname,
|
||||
unsigned int flags)
|
||||
int git_reference__is_valid_name(const char *refname, unsigned int flags)
|
||||
{
|
||||
int error;
|
||||
if (git_reference__normalize_name(NULL, refname, flags) < 0) {
|
||||
giterr_clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
error = git_reference__normalize_name(NULL, refname, flags) == 0;
|
||||
giterr_clear();
|
||||
|
||||
return error;
|
||||
return true;
|
||||
}
|
||||
|
||||
int git_reference_is_valid_name(
|
||||
const char *refname)
|
||||
int git_reference_is_valid_name(const char *refname)
|
||||
{
|
||||
return git_reference__is_valid_name(
|
||||
refname,
|
||||
GIT_REF_FORMAT_ALLOW_ONELEVEL);
|
||||
return git_reference__is_valid_name(refname, GIT_REF_FORMAT_ALLOW_ONELEVEL);
|
||||
}
|
||||
|
||||
const char *git_reference_shorthand(git_reference *ref)
|
||||
|
||||
@ -46,6 +46,8 @@
|
||||
#define GIT_STASH_FILE "stash"
|
||||
#define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE
|
||||
|
||||
#define GIT_REF_FORMAT__PRECOMPOSE_UNICODE (1u << 16)
|
||||
|
||||
#define GIT_REFNAME_MAX 1024
|
||||
|
||||
struct git_reference {
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include "git2/oid.h"
|
||||
#include "git2/net.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "config.h"
|
||||
#include "repository.h"
|
||||
#include "remote.h"
|
||||
|
||||
272
src/repository.c
272
src/repository.c
@ -843,10 +843,6 @@ fail:
|
||||
static bool is_chmod_supported(const char *file_path)
|
||||
{
|
||||
struct stat st1, st2;
|
||||
static int _is_supported = -1;
|
||||
|
||||
if (_is_supported > -1)
|
||||
return _is_supported;
|
||||
|
||||
if (p_stat(file_path, &st1) < 0)
|
||||
return false;
|
||||
@ -857,27 +853,19 @@ static bool is_chmod_supported(const char *file_path)
|
||||
if (p_stat(file_path, &st2) < 0)
|
||||
return false;
|
||||
|
||||
_is_supported = (st1.st_mode != st2.st_mode);
|
||||
|
||||
return _is_supported;
|
||||
return (st1.st_mode != st2.st_mode);
|
||||
}
|
||||
|
||||
static bool is_filesystem_case_insensitive(const char *gitdir_path)
|
||||
{
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
static int _is_insensitive = -1;
|
||||
int is_insensitive = -1;
|
||||
|
||||
if (_is_insensitive > -1)
|
||||
return _is_insensitive;
|
||||
if (!git_buf_joinpath(&path, gitdir_path, "CoNfIg"))
|
||||
is_insensitive = git_path_exists(git_buf_cstr(&path));
|
||||
|
||||
if (git_buf_joinpath(&path, gitdir_path, "CoNfIg") < 0)
|
||||
goto cleanup;
|
||||
|
||||
_is_insensitive = git_path_exists(git_buf_cstr(&path));
|
||||
|
||||
cleanup:
|
||||
git_buf_free(&path);
|
||||
return _is_insensitive;
|
||||
return is_insensitive;
|
||||
}
|
||||
|
||||
static bool are_symlinks_supported(const char *wd_path)
|
||||
@ -885,26 +873,77 @@ static bool are_symlinks_supported(const char *wd_path)
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
int fd;
|
||||
struct stat st;
|
||||
static int _symlinks_supported = -1;
|
||||
|
||||
if (_symlinks_supported > -1)
|
||||
return _symlinks_supported;
|
||||
int symlinks_supported = -1;
|
||||
|
||||
if ((fd = git_futils_mktmp(&path, wd_path)) < 0 ||
|
||||
p_close(fd) < 0 ||
|
||||
p_unlink(path.ptr) < 0 ||
|
||||
p_symlink("testing", path.ptr) < 0 ||
|
||||
p_lstat(path.ptr, &st) < 0)
|
||||
_symlinks_supported = false;
|
||||
symlinks_supported = false;
|
||||
else
|
||||
_symlinks_supported = (S_ISLNK(st.st_mode) != 0);
|
||||
symlinks_supported = (S_ISLNK(st.st_mode) != 0);
|
||||
|
||||
(void)p_unlink(path.ptr);
|
||||
git_buf_free(&path);
|
||||
|
||||
return _symlinks_supported;
|
||||
return symlinks_supported;
|
||||
}
|
||||
|
||||
#ifdef GIT_USE_ICONV
|
||||
|
||||
static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX";
|
||||
static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX";
|
||||
|
||||
/* Check if the platform is decomposing unicode data for us. We will
|
||||
* emulate core Git and prefer to use precomposed unicode data internally
|
||||
* on these platforms, composing the decomposed unicode on the fly.
|
||||
*
|
||||
* This mainly happens on the Mac where HDFS stores filenames as
|
||||
* decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will
|
||||
* return decomposed unicode from readdir() even when the actual
|
||||
* filesystem is storing precomposed unicode.
|
||||
*/
|
||||
static bool does_fs_decompose_unicode_paths(const char *wd_path)
|
||||
{
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
int fd;
|
||||
bool found_decomposed = false;
|
||||
char tmp[6];
|
||||
|
||||
/* Create a file using a precomposed path and then try to find it
|
||||
* using the decomposed name. If the lookup fails, then we will mark
|
||||
* that we should precompose unicode for this repository.
|
||||
*/
|
||||
if (git_buf_joinpath(&path, wd_path, nfc_file) < 0 ||
|
||||
(fd = p_mkstemp(path.ptr)) < 0)
|
||||
goto done;
|
||||
p_close(fd);
|
||||
|
||||
/* record trailing digits generated by mkstemp */
|
||||
memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp));
|
||||
|
||||
/* try to look up as NFD path */
|
||||
if (git_buf_joinpath(&path, wd_path, nfd_file) < 0)
|
||||
goto done;
|
||||
memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
|
||||
|
||||
found_decomposed = git_path_exists(path.ptr);
|
||||
|
||||
/* remove temporary file (using original precomposed path) */
|
||||
if (git_buf_joinpath(&path, wd_path, nfc_file) < 0)
|
||||
goto done;
|
||||
memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp));
|
||||
|
||||
(void)p_unlink(path.ptr);
|
||||
|
||||
done:
|
||||
git_buf_free(&path);
|
||||
return found_decomposed;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int create_empty_file(const char *path, mode_t mode)
|
||||
{
|
||||
int fd;
|
||||
@ -922,71 +961,131 @@ static int create_empty_file(const char *path, mode_t mode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int repo_local_config(
|
||||
git_config **out,
|
||||
git_buf *config_dir,
|
||||
git_repository *repo,
|
||||
const char *repo_dir)
|
||||
{
|
||||
int error = 0;
|
||||
git_config *parent;
|
||||
const char *cfg_path;
|
||||
|
||||
if (git_buf_joinpath(config_dir, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
|
||||
return -1;
|
||||
cfg_path = git_buf_cstr(config_dir);
|
||||
|
||||
/* make LOCAL config if missing */
|
||||
if (!git_path_isfile(cfg_path) &&
|
||||
(error = create_empty_file(cfg_path, GIT_CONFIG_FILE_MODE)) < 0)
|
||||
return error;
|
||||
|
||||
/* if no repo, just open that file directly */
|
||||
if (!repo)
|
||||
return git_config_open_ondisk(out, cfg_path);
|
||||
|
||||
/* otherwise, open parent config and get that level */
|
||||
if ((error = git_repository_config__weakptr(&parent, repo)) < 0)
|
||||
return error;
|
||||
|
||||
if (git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL) < 0) {
|
||||
giterr_clear();
|
||||
|
||||
if (!(error = git_config_add_file_ondisk(
|
||||
parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, false)))
|
||||
error = git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL);
|
||||
}
|
||||
|
||||
git_config_free(parent);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int repo_init_fs_configs(
|
||||
git_config *cfg,
|
||||
const char *cfg_path,
|
||||
const char *repo_dir,
|
||||
const char *work_dir,
|
||||
bool update_ignorecase)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (!work_dir)
|
||||
work_dir = repo_dir;
|
||||
|
||||
if ((error = git_config_set_bool(
|
||||
cfg, "core.filemode", is_chmod_supported(cfg_path))) < 0)
|
||||
return error;
|
||||
|
||||
if (!are_symlinks_supported(work_dir)) {
|
||||
if ((error = git_config_set_bool(cfg, "core.symlinks", false)) < 0)
|
||||
return error;
|
||||
} else if (git_config_delete_entry(cfg, "core.symlinks") < 0)
|
||||
giterr_clear();
|
||||
|
||||
if (update_ignorecase) {
|
||||
if (is_filesystem_case_insensitive(repo_dir)) {
|
||||
if ((error = git_config_set_bool(cfg, "core.ignorecase", true)) < 0)
|
||||
return error;
|
||||
} else if (git_config_delete_entry(cfg, "core.ignorecase") < 0)
|
||||
giterr_clear();
|
||||
}
|
||||
|
||||
#ifdef GIT_USE_ICONV
|
||||
if ((error = git_config_set_bool(
|
||||
cfg, "core.precomposeunicode",
|
||||
does_fs_decompose_unicode_paths(work_dir))) < 0)
|
||||
return error;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int repo_init_config(
|
||||
const char *repo_dir,
|
||||
const char *work_dir,
|
||||
git_repository_init_options *opts)
|
||||
uint32_t flags,
|
||||
uint32_t mode)
|
||||
{
|
||||
int error = 0;
|
||||
git_buf cfg_path = GIT_BUF_INIT;
|
||||
git_config *config = NULL;
|
||||
bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0);
|
||||
bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0);
|
||||
|
||||
#define SET_REPO_CONFIG(TYPE, NAME, VAL) do {\
|
||||
if ((error = repo_local_config(&config, &cfg_path, NULL, repo_dir)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (is_reinit && (error = check_repositoryformatversion(config)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
#define SET_REPO_CONFIG(TYPE, NAME, VAL) do { \
|
||||
if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \
|
||||
goto cleanup; } while (0)
|
||||
|
||||
if (git_buf_joinpath(&cfg_path, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
|
||||
return -1;
|
||||
SET_REPO_CONFIG(bool, "core.bare", is_bare);
|
||||
SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
|
||||
|
||||
if (!git_path_isfile(git_buf_cstr(&cfg_path)) &&
|
||||
create_empty_file(git_buf_cstr(&cfg_path), GIT_CONFIG_FILE_MODE) < 0) {
|
||||
git_buf_free(&cfg_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (git_config_open_ondisk(&config, git_buf_cstr(&cfg_path)) < 0) {
|
||||
git_buf_free(&cfg_path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0 &&
|
||||
(error = check_repositoryformatversion(config)) < 0)
|
||||
if ((error = repo_init_fs_configs(
|
||||
config, cfg_path.ptr, repo_dir, work_dir, !is_reinit)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
SET_REPO_CONFIG(
|
||||
bool, "core.bare", (opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);
|
||||
SET_REPO_CONFIG(
|
||||
int32, "core.repositoryformatversion", GIT_REPO_VERSION);
|
||||
SET_REPO_CONFIG(
|
||||
bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path)));
|
||||
|
||||
if (!(opts->flags & GIT_REPOSITORY_INIT_BARE)) {
|
||||
if (!is_bare) {
|
||||
SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
|
||||
|
||||
if (!are_symlinks_supported(work_dir))
|
||||
SET_REPO_CONFIG(bool, "core.symlinks", false);
|
||||
|
||||
if (!(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD)) {
|
||||
if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD))
|
||||
SET_REPO_CONFIG(string, "core.worktree", work_dir);
|
||||
}
|
||||
else if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0) {
|
||||
else if (is_reinit) {
|
||||
if (git_config_delete_entry(config, "core.worktree") < 0)
|
||||
giterr_clear();
|
||||
}
|
||||
} else {
|
||||
if (!are_symlinks_supported(repo_dir))
|
||||
SET_REPO_CONFIG(bool, "core.symlinks", false);
|
||||
}
|
||||
|
||||
if (!(opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) &&
|
||||
is_filesystem_case_insensitive(repo_dir))
|
||||
SET_REPO_CONFIG(bool, "core.ignorecase", true);
|
||||
|
||||
if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
|
||||
if (mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
|
||||
SET_REPO_CONFIG(int32, "core.sharedrepository", 1);
|
||||
SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
|
||||
}
|
||||
else if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
|
||||
else if (mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
|
||||
SET_REPO_CONFIG(int32, "core.sharedrepository", 2);
|
||||
SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
|
||||
}
|
||||
@ -998,6 +1097,41 @@ cleanup:
|
||||
return error;
|
||||
}
|
||||
|
||||
static int repo_reinit_submodule_fs(git_submodule *sm, const char *n, void *p)
|
||||
{
|
||||
git_repository *smrepo = NULL;
|
||||
GIT_UNUSED(n); GIT_UNUSED(p);
|
||||
|
||||
if (git_submodule_open(&smrepo, sm) < 0 ||
|
||||
git_repository_reinit_filesystem(smrepo, true) < 0)
|
||||
giterr_clear();
|
||||
git_repository_free(smrepo);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_repository_reinit_filesystem(git_repository *repo, int recurse)
|
||||
{
|
||||
int error = 0;
|
||||
git_buf path = GIT_BUF_INIT;
|
||||
git_config *config = NULL;
|
||||
const char *repo_dir = git_repository_path(repo);
|
||||
|
||||
if (!(error = repo_local_config(&config, &path, repo, repo_dir)))
|
||||
error = repo_init_fs_configs(
|
||||
config, path.ptr, repo_dir, git_repository_workdir(repo), true);
|
||||
|
||||
git_config_free(config);
|
||||
git_buf_free(&path);
|
||||
|
||||
git_repository__cvar_cache_clear(repo);
|
||||
|
||||
if (!repo->is_bare && recurse)
|
||||
(void)git_submodule_foreach(repo, repo_reinit_submodule_fs, NULL);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
static int repo_write_template(
|
||||
const char *git_dir,
|
||||
bool allow_overwrite,
|
||||
@ -1386,22 +1520,22 @@ int git_repository_init_ext(
|
||||
opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;
|
||||
|
||||
error = repo_init_config(
|
||||
git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts);
|
||||
repo_path.ptr, wd_path.ptr, opts->flags, opts->mode);
|
||||
|
||||
/* TODO: reinitialize the templates */
|
||||
}
|
||||
else {
|
||||
if (!(error = repo_init_structure(
|
||||
git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)) &&
|
||||
repo_path.ptr, wd_path.ptr, opts)) &&
|
||||
!(error = repo_init_config(
|
||||
git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)))
|
||||
repo_path.ptr, wd_path.ptr, opts->flags, opts->mode)))
|
||||
error = repo_init_create_head(
|
||||
git_buf_cstr(&repo_path), opts->initial_head);
|
||||
repo_path.ptr, opts->initial_head);
|
||||
}
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
error = git_repository_open(out, git_buf_cstr(&repo_path));
|
||||
error = git_repository_open(out, repo_path.ptr);
|
||||
|
||||
if (!error && opts->origin_url)
|
||||
error = repo_init_create_origin(*out, opts->origin_url);
|
||||
|
||||
@ -37,6 +37,7 @@ typedef enum {
|
||||
GIT_CVAR_IGNORESTAT, /* core.ignorestat */
|
||||
GIT_CVAR_TRUSTCTIME, /* core.trustctime */
|
||||
GIT_CVAR_ABBREV, /* core.abbrev */
|
||||
GIT_CVAR_PRECOMPOSE, /* core.precomposeunicode */
|
||||
GIT_CVAR_CACHE_MAX
|
||||
} git_cvar_cached;
|
||||
|
||||
@ -86,6 +87,8 @@ typedef enum {
|
||||
GIT_TRUSTCTIME_DEFAULT = GIT_CVAR_TRUE,
|
||||
/* core.abbrev */
|
||||
GIT_ABBREV_DEFAULT = 7,
|
||||
/* core.precomposeunicode */
|
||||
GIT_PRECOMPOSE_DEFAULT = GIT_CVAR_FALSE,
|
||||
|
||||
} git_cvar_value;
|
||||
|
||||
|
||||
13
src/reset.c
13
src/reset.c
@ -24,10 +24,9 @@ int git_reset_default(
|
||||
{
|
||||
git_object *commit = NULL;
|
||||
git_tree *tree = NULL;
|
||||
git_diff_list *diff = NULL;
|
||||
git_diff *diff = NULL;
|
||||
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
||||
size_t i;
|
||||
git_diff_delta *delta;
|
||||
size_t i, max_i;
|
||||
git_index_entry entry;
|
||||
int error;
|
||||
git_index *index = NULL;
|
||||
@ -58,7 +57,9 @@ int git_reset_default(
|
||||
&diff, repo, tree, index, &opts)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
git_vector_foreach(&diff->deltas, i, delta) {
|
||||
for (i = 0, max_i = git_diff_num_deltas(diff); i < max_i; ++i) {
|
||||
const git_diff_delta *delta = git_diff_get_delta(diff, i);
|
||||
|
||||
if ((error = git_index_conflict_remove(index, delta->old_file.path)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
@ -85,7 +86,7 @@ cleanup:
|
||||
git_object_free(commit);
|
||||
git_tree_free(tree);
|
||||
git_index_free(index);
|
||||
git_diff_list_free(diff);
|
||||
git_diff_free(diff);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -135,7 +136,7 @@ int git_reset(
|
||||
|
||||
if (reset_type == GIT_RESET_HARD) {
|
||||
/* overwrite working directory with HEAD */
|
||||
opts.checkout_strategy = GIT_CHECKOUT_FORCE;
|
||||
opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_SKIP_UNMERGED;
|
||||
|
||||
if ((error = git_checkout_tree(repo, (git_object *)tree, &opts)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
@ -160,7 +160,7 @@ static int retrieve_previously_checked_out_branch_or_revision(git_object **out,
|
||||
if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if (git_reflog_read(&reflog, ref) < 0)
|
||||
if (git_reflog_read(&reflog, repo, GIT_HEAD_FILE) < 0)
|
||||
goto cleanup;
|
||||
|
||||
numentries = git_reflog_entrycount(reflog);
|
||||
@ -208,7 +208,7 @@ static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t ide
|
||||
const git_reflog_entry *entry;
|
||||
bool search_by_pos = (identifier <= 100000000);
|
||||
|
||||
if (git_reflog_read(&reflog, ref) < 0)
|
||||
if (git_reflog_read(&reflog, git_reference_owner(ref), git_reference_name(ref)) < 0)
|
||||
return -1;
|
||||
|
||||
numentries = git_reflog_entrycount(reflog);
|
||||
|
||||
144
src/stash.c
144
src/stash.c
@ -153,65 +153,61 @@ cleanup:
|
||||
return error;
|
||||
}
|
||||
|
||||
struct cb_data {
|
||||
git_index *index;
|
||||
|
||||
int error;
|
||||
|
||||
struct stash_update_rules {
|
||||
bool include_changed;
|
||||
bool include_untracked;
|
||||
bool include_ignored;
|
||||
};
|
||||
|
||||
static int update_index_cb(
|
||||
const git_diff_delta *delta,
|
||||
float progress,
|
||||
void *payload)
|
||||
static int stash_update_index_from_diff(
|
||||
git_index *index,
|
||||
const git_diff *diff,
|
||||
struct stash_update_rules *data)
|
||||
{
|
||||
struct cb_data *data = (struct cb_data *)payload;
|
||||
const char *add_path = NULL;
|
||||
int error = 0;
|
||||
size_t d, max_d = git_diff_num_deltas(diff);
|
||||
|
||||
GIT_UNUSED(progress);
|
||||
for (d = 0; !error && d < max_d; ++d) {
|
||||
const char *add_path = NULL;
|
||||
const git_diff_delta *delta = git_diff_get_delta(diff, d);
|
||||
|
||||
switch (delta->status) {
|
||||
case GIT_DELTA_IGNORED:
|
||||
if (data->include_ignored)
|
||||
add_path = delta->new_file.path;
|
||||
break;
|
||||
|
||||
case GIT_DELTA_UNTRACKED:
|
||||
if (data->include_untracked)
|
||||
add_path = delta->new_file.path;
|
||||
break;
|
||||
|
||||
case GIT_DELTA_ADDED:
|
||||
case GIT_DELTA_MODIFIED:
|
||||
if (data->include_changed)
|
||||
add_path = delta->new_file.path;
|
||||
break;
|
||||
|
||||
case GIT_DELTA_DELETED:
|
||||
if (!data->include_changed)
|
||||
switch (delta->status) {
|
||||
case GIT_DELTA_IGNORED:
|
||||
if (data->include_ignored)
|
||||
add_path = delta->new_file.path;
|
||||
break;
|
||||
if (git_index_find(NULL, data->index, delta->old_file.path) == 0)
|
||||
data->error = git_index_remove(
|
||||
data->index, delta->old_file.path, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Unimplemented */
|
||||
giterr_set(
|
||||
GITERR_INVALID,
|
||||
"Cannot update index. Unimplemented status (%d)",
|
||||
delta->status);
|
||||
data->error = -1;
|
||||
break;
|
||||
case GIT_DELTA_UNTRACKED:
|
||||
if (data->include_untracked)
|
||||
add_path = delta->new_file.path;
|
||||
break;
|
||||
|
||||
case GIT_DELTA_ADDED:
|
||||
case GIT_DELTA_MODIFIED:
|
||||
if (data->include_changed)
|
||||
add_path = delta->new_file.path;
|
||||
break;
|
||||
|
||||
case GIT_DELTA_DELETED:
|
||||
if (data->include_changed &&
|
||||
!git_index_find(NULL, index, delta->old_file.path))
|
||||
error = git_index_remove(index, delta->old_file.path, 0);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Unimplemented */
|
||||
giterr_set(
|
||||
GITERR_INVALID,
|
||||
"Cannot update index. Unimplemented status (%d)",
|
||||
delta->status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (add_path != NULL)
|
||||
error = git_index_add_bypath(index, add_path);
|
||||
}
|
||||
|
||||
if (add_path != NULL)
|
||||
data->error = git_index_add_bypath(data->index, add_path);
|
||||
|
||||
return data->error;
|
||||
return error;
|
||||
}
|
||||
|
||||
static int build_untracked_tree(
|
||||
@ -221,15 +217,13 @@ static int build_untracked_tree(
|
||||
uint32_t flags)
|
||||
{
|
||||
git_tree *i_tree = NULL;
|
||||
git_diff_list *diff = NULL;
|
||||
git_diff *diff = NULL;
|
||||
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
||||
struct cb_data data = {0};
|
||||
struct stash_update_rules data = {0};
|
||||
int error;
|
||||
|
||||
git_index_clear(index);
|
||||
|
||||
data.index = index;
|
||||
|
||||
if (flags & GIT_STASH_INCLUDE_UNTRACKED) {
|
||||
opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED |
|
||||
GIT_DIFF_RECURSE_UNTRACKED_DIRS;
|
||||
@ -248,18 +242,13 @@ static int build_untracked_tree(
|
||||
&diff, git_index_owner(index), i_tree, &opts)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_diff_foreach(
|
||||
diff, update_index_cb, NULL, NULL, &data)) < 0)
|
||||
{
|
||||
if (error == GIT_EUSER)
|
||||
error = data.error;
|
||||
if ((error = stash_update_index_from_diff(index, diff, &data)) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
error = build_tree_from_index(tree_out, index);
|
||||
|
||||
cleanup:
|
||||
git_diff_list_free(diff);
|
||||
git_diff_free(diff);
|
||||
git_tree_free(i_tree);
|
||||
return error;
|
||||
}
|
||||
@ -311,9 +300,9 @@ static int build_workdir_tree(
|
||||
{
|
||||
git_repository *repo = git_index_owner(index);
|
||||
git_tree *b_tree = NULL;
|
||||
git_diff_list *diff = NULL, *diff2 = NULL;
|
||||
git_diff *diff = NULL;
|
||||
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
|
||||
struct cb_data data = {0};
|
||||
struct stash_update_rules data = {0};
|
||||
int error;
|
||||
|
||||
opts.flags = GIT_DIFF_IGNORE_SUBMODULES;
|
||||
@ -321,33 +310,19 @@ static int build_workdir_tree(
|
||||
if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_diff_tree_to_index(&diff, repo, b_tree, NULL, &opts)) < 0)
|
||||
if ((error = git_diff_tree_to_workdir_with_index(
|
||||
&diff, repo, b_tree, &opts)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_diff_index_to_workdir(&diff2, repo, NULL, &opts)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_diff_merge(diff, diff2)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
data.index = index;
|
||||
data.include_changed = true;
|
||||
|
||||
if ((error = git_diff_foreach(
|
||||
diff, update_index_cb, NULL, NULL, &data)) < 0)
|
||||
{
|
||||
if (error == GIT_EUSER)
|
||||
error = data.error;
|
||||
if ((error = stash_update_index_from_diff(index, diff, &data)) < 0)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
||||
if ((error = build_tree_from_index(tree_out, index)) < 0)
|
||||
goto cleanup;
|
||||
error = build_tree_from_index(tree_out, index);
|
||||
|
||||
cleanup:
|
||||
git_diff_list_free(diff);
|
||||
git_diff_list_free(diff2);
|
||||
git_diff_free(diff);
|
||||
git_tree_free(b_tree);
|
||||
|
||||
return error;
|
||||
@ -436,14 +411,16 @@ static int update_reflog(
|
||||
const git_signature *stasher,
|
||||
const char *message)
|
||||
{
|
||||
git_reference *stash = NULL;
|
||||
git_reference *stash;
|
||||
git_reflog *reflog = NULL;
|
||||
int error;
|
||||
|
||||
if ((error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_reflog_read(&reflog, stash)) < 0)
|
||||
git_reference_free(stash);
|
||||
|
||||
if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE) < 0))
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_reflog_append(reflog, w_commit_oid, stasher, message)) < 0)
|
||||
@ -453,7 +430,6 @@ static int update_reflog(
|
||||
goto cleanup;
|
||||
|
||||
cleanup:
|
||||
git_reference_free(stash);
|
||||
git_reflog_free(reflog);
|
||||
return error;
|
||||
}
|
||||
@ -599,7 +575,7 @@ int git_stash_foreach(
|
||||
if (error < 0)
|
||||
goto cleanup;
|
||||
|
||||
if ((error = git_reflog_read(&reflog, stash)) < 0)
|
||||
if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
max = git_reflog_entrycount(reflog);
|
||||
@ -633,7 +609,7 @@ int git_stash_drop(
|
||||
if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
|
||||
return error;
|
||||
|
||||
if ((error = git_reflog_read(&reflog, stash)) < 0)
|
||||
if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
|
||||
goto cleanup;
|
||||
|
||||
max = git_reflog_entrycount(reflog);
|
||||
|
||||
@ -52,7 +52,7 @@ static unsigned int index_delta2status(const git_diff_delta *head2idx)
|
||||
}
|
||||
|
||||
static unsigned int workdir_delta2status(
|
||||
git_diff_list *diff, git_diff_delta *idx2wd)
|
||||
git_diff *diff, git_diff_delta *idx2wd)
|
||||
{
|
||||
git_status_t st = GIT_STATUS_CURRENT;
|
||||
|
||||
@ -361,8 +361,8 @@ void git_status_list_free(git_status_list *status)
|
||||
if (status == NULL)
|
||||
return;
|
||||
|
||||
git_diff_list_free(status->head2idx);
|
||||
git_diff_list_free(status->idx2wd);
|
||||
git_diff_free(status->head2idx);
|
||||
git_diff_free(status->idx2wd);
|
||||
|
||||
git_vector_foreach(&status->paired, i, status_entry)
|
||||
git__free(status_entry);
|
||||
|
||||
@ -14,8 +14,8 @@
|
||||
struct git_status_list {
|
||||
git_status_options opts;
|
||||
|
||||
git_diff_list *head2idx;
|
||||
git_diff_list *idx2wd;
|
||||
git_diff *head2idx;
|
||||
git_diff *idx2wd;
|
||||
|
||||
git_vector paired;
|
||||
};
|
||||
|
||||
@ -372,7 +372,8 @@ int git_submodule_add_to_index(git_submodule *sm, int write_index)
|
||||
|
||||
memset(&entry, 0, sizeof(entry));
|
||||
entry.path = sm->path;
|
||||
git_index_entry__init_from_stat(&entry, &st);
|
||||
git_index_entry__init_from_stat(
|
||||
&entry, &st, !(git_index_caps(index) & GIT_INDEXCAP_NO_FILEMODE));
|
||||
|
||||
/* calling git_submodule_open will have set sm->wd_oid if possible */
|
||||
if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {
|
||||
@ -1527,7 +1528,7 @@ static void submodule_get_wd_status(
|
||||
(sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) ? &sm->wd_oid : NULL;
|
||||
git_tree *sm_head = NULL;
|
||||
git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
|
||||
git_diff_list *diff;
|
||||
git_diff *diff;
|
||||
|
||||
*status = *status & ~GIT_SUBMODULE_STATUS__WD_FLAGS;
|
||||
|
||||
@ -1567,7 +1568,7 @@ static void submodule_get_wd_status(
|
||||
else {
|
||||
if (git_diff_num_deltas(diff) > 0)
|
||||
*status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED;
|
||||
git_diff_list_free(diff);
|
||||
git_diff_free(diff);
|
||||
diff = NULL;
|
||||
}
|
||||
|
||||
@ -1587,7 +1588,7 @@ static void submodule_get_wd_status(
|
||||
if (git_diff_num_deltas(diff) != untracked)
|
||||
*status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
|
||||
|
||||
git_diff_list_free(diff);
|
||||
git_diff_free(diff);
|
||||
diff = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,13 +19,13 @@ int git_cred_has_username(git_cred *cred)
|
||||
ret = !!c->username;
|
||||
break;
|
||||
}
|
||||
case GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE: {
|
||||
git_cred_ssh_keyfile_passphrase *c = (git_cred_ssh_keyfile_passphrase *)cred;
|
||||
case GIT_CREDTYPE_SSH_KEY: {
|
||||
git_cred_ssh_key *c = (git_cred_ssh_key *)cred;
|
||||
ret = !!c->username;
|
||||
break;
|
||||
}
|
||||
case GIT_CREDTYPE_SSH_PUBLICKEY: {
|
||||
git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
|
||||
case GIT_CREDTYPE_SSH_CUSTOM: {
|
||||
git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred;
|
||||
ret = !!c->username;
|
||||
break;
|
||||
}
|
||||
@ -58,7 +58,7 @@ int git_cred_userpass_plaintext_new(
|
||||
{
|
||||
git_cred_userpass_plaintext *c;
|
||||
|
||||
assert(cred);
|
||||
assert(cred && username && password);
|
||||
|
||||
c = git__malloc(sizeof(git_cred_userpass_plaintext));
|
||||
GITERR_CHECK_ALLOC(c);
|
||||
@ -84,10 +84,10 @@ int git_cred_userpass_plaintext_new(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ssh_keyfile_passphrase_free(struct git_cred *cred)
|
||||
static void ssh_key_free(struct git_cred *cred)
|
||||
{
|
||||
git_cred_ssh_keyfile_passphrase *c =
|
||||
(git_cred_ssh_keyfile_passphrase *)cred;
|
||||
git_cred_ssh_key *c =
|
||||
(git_cred_ssh_key *)cred;
|
||||
|
||||
git__free(c->username);
|
||||
git__free(c->publickey);
|
||||
@ -104,9 +104,9 @@ static void ssh_keyfile_passphrase_free(struct git_cred *cred)
|
||||
git__free(c);
|
||||
}
|
||||
|
||||
static void ssh_publickey_free(struct git_cred *cred)
|
||||
static void ssh_custom_free(struct git_cred *cred)
|
||||
{
|
||||
git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
|
||||
git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred;
|
||||
|
||||
git__free(c->username);
|
||||
git__free(c->publickey);
|
||||
@ -115,22 +115,22 @@ static void ssh_publickey_free(struct git_cred *cred)
|
||||
git__free(c);
|
||||
}
|
||||
|
||||
int git_cred_ssh_keyfile_passphrase_new(
|
||||
int git_cred_ssh_key_new(
|
||||
git_cred **cred,
|
||||
const char *username,
|
||||
const char *publickey,
|
||||
const char *privatekey,
|
||||
const char *passphrase)
|
||||
{
|
||||
git_cred_ssh_keyfile_passphrase *c;
|
||||
git_cred_ssh_key *c;
|
||||
|
||||
assert(cred && privatekey);
|
||||
|
||||
c = git__calloc(1, sizeof(git_cred_ssh_keyfile_passphrase));
|
||||
c = git__calloc(1, sizeof(git_cred_ssh_key));
|
||||
GITERR_CHECK_ALLOC(c);
|
||||
|
||||
c->parent.credtype = GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE;
|
||||
c->parent.free = ssh_keyfile_passphrase_free;
|
||||
c->parent.credtype = GIT_CREDTYPE_SSH_KEY;
|
||||
c->parent.free = ssh_key_free;
|
||||
|
||||
if (username) {
|
||||
c->username = git__strdup(username);
|
||||
@ -154,7 +154,7 @@ int git_cred_ssh_keyfile_passphrase_new(
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_cred_ssh_publickey_new(
|
||||
int git_cred_ssh_custom_new(
|
||||
git_cred **cred,
|
||||
const char *username,
|
||||
const char *publickey,
|
||||
@ -162,15 +162,15 @@ int git_cred_ssh_publickey_new(
|
||||
git_cred_sign_callback sign_callback,
|
||||
void *sign_data)
|
||||
{
|
||||
git_cred_ssh_publickey *c;
|
||||
git_cred_ssh_custom *c;
|
||||
|
||||
assert(cred);
|
||||
|
||||
c = git__calloc(1, sizeof(git_cred_ssh_publickey));
|
||||
c = git__calloc(1, sizeof(git_cred_ssh_custom));
|
||||
GITERR_CHECK_ALLOC(c);
|
||||
|
||||
c->parent.credtype = GIT_CREDTYPE_SSH_PUBLICKEY;
|
||||
c->parent.free = ssh_publickey_free;
|
||||
c->parent.credtype = GIT_CREDTYPE_SSH_CUSTOM;
|
||||
c->parent.free = ssh_custom_free;
|
||||
|
||||
if (username) {
|
||||
c->username = git__strdup(username);
|
||||
|
||||
@ -23,8 +23,13 @@ static int git_smart__recv_cb(gitno_buffer *buf)
|
||||
|
||||
buf->offset += bytes_read;
|
||||
|
||||
if (t->packetsize_cb)
|
||||
t->packetsize_cb(bytes_read, t->packetsize_payload);
|
||||
if (t->packetsize_cb && !t->cancelled.val)
|
||||
if (t->packetsize_cb(bytes_read, t->packetsize_payload)) {
|
||||
git_atomic_set(&t->cancelled, 1);
|
||||
|
||||
giterr_clear();
|
||||
return GIT_EUSER;
|
||||
}
|
||||
|
||||
return (int)(buf->offset - old_len);
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
#define GIT_CAP_INCLUDE_TAG "include-tag"
|
||||
#define GIT_CAP_DELETE_REFS "delete-refs"
|
||||
#define GIT_CAP_REPORT_STATUS "report-status"
|
||||
#define GIT_CAP_THIN_PACK "thin-pack"
|
||||
|
||||
enum git_pkt_type {
|
||||
GIT_PKT_CMD,
|
||||
@ -116,10 +117,11 @@ typedef struct transport_smart_caps {
|
||||
side_band_64k:1,
|
||||
include_tag:1,
|
||||
delete_refs:1,
|
||||
report_status:1;
|
||||
report_status:1,
|
||||
thin_pack:1;
|
||||
} transport_smart_caps;
|
||||
|
||||
typedef void (*packetsize_cb)(size_t received, void *payload);
|
||||
typedef int (*packetsize_cb)(size_t received, void *payload);
|
||||
|
||||
typedef struct {
|
||||
git_transport parent;
|
||||
|
||||
@ -472,6 +472,9 @@ static int buffer_want_with_caps(const git_remote_head *head, transport_smart_ca
|
||||
if (caps->include_tag)
|
||||
git_buf_puts(&str, GIT_CAP_INCLUDE_TAG " ");
|
||||
|
||||
if (caps->thin_pack)
|
||||
git_buf_puts(&str, GIT_CAP_THIN_PACK " ");
|
||||
|
||||
if (git_buf_oom(&str))
|
||||
return -1;
|
||||
|
||||
|
||||
@ -128,6 +128,12 @@ int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!git__prefixcmp(ptr, GIT_CAP_THIN_PACK)) {
|
||||
caps->common = caps->thin_pack = 1;
|
||||
ptr += strlen(GIT_CAP_THIN_PACK);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We don't know this capability, so skip it */
|
||||
ptr = strchr(ptr, ' ');
|
||||
}
|
||||
@ -425,7 +431,7 @@ struct network_packetsize_payload
|
||||
size_t last_fired_bytes;
|
||||
};
|
||||
|
||||
static void network_packetsize(size_t received, void *payload)
|
||||
static int network_packetsize(size_t received, void *payload)
|
||||
{
|
||||
struct network_packetsize_payload *npp = (struct network_packetsize_payload*)payload;
|
||||
|
||||
@ -435,8 +441,12 @@ static void network_packetsize(size_t received, void *payload)
|
||||
/* Fire notification if the threshold is reached */
|
||||
if ((npp->stats->received_bytes - npp->last_fired_bytes) > NETWORK_XFER_THRESHOLD) {
|
||||
npp->last_fired_bytes = npp->stats->received_bytes;
|
||||
npp->callback(npp->stats, npp->payload);
|
||||
|
||||
if (npp->callback(npp->stats, npp->payload))
|
||||
return GIT_EUSER;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int git_smart__download_pack(
|
||||
@ -450,7 +460,7 @@ int git_smart__download_pack(
|
||||
gitno_buffer *buf = &t->buffer;
|
||||
git_odb *odb;
|
||||
struct git_odb_writepack *writepack = NULL;
|
||||
int error = -1;
|
||||
int error = 0;
|
||||
struct network_packetsize_payload npp = {0};
|
||||
|
||||
memset(stats, 0, sizeof(git_transfer_progress));
|
||||
@ -463,13 +473,14 @@ int git_smart__download_pack(
|
||||
t->packetsize_payload = &npp;
|
||||
|
||||
/* We might have something in the buffer already from negotiate_fetch */
|
||||
if (t->buffer.offset > 0)
|
||||
t->packetsize_cb(t->buffer.offset, t->packetsize_payload);
|
||||
if (t->buffer.offset > 0 && !t->cancelled.val)
|
||||
if (t->packetsize_cb(t->buffer.offset, t->packetsize_payload))
|
||||
git_atomic_set(&t->cancelled, 1);
|
||||
}
|
||||
|
||||
if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
|
||||
((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) < 0))
|
||||
goto on_error;
|
||||
goto done;
|
||||
|
||||
/*
|
||||
* If the remote doesn't support the side-band, we can feed
|
||||
@ -477,28 +488,37 @@ int git_smart__download_pack(
|
||||
* check which one belongs there.
|
||||
*/
|
||||
if (!t->caps.side_band && !t->caps.side_band_64k) {
|
||||
if (no_sideband(t, writepack, buf, stats) < 0)
|
||||
goto on_error;
|
||||
|
||||
goto on_success;
|
||||
error = no_sideband(t, writepack, buf, stats);
|
||||
goto done;
|
||||
}
|
||||
|
||||
do {
|
||||
git_pkt *pkt;
|
||||
|
||||
/* Check cancellation before network call */
|
||||
if (t->cancelled.val) {
|
||||
giterr_set(GITERR_NET, "The fetch was cancelled by the user");
|
||||
error = GIT_EUSER;
|
||||
goto on_error;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (recv_pkt(&pkt, buf) < 0)
|
||||
goto on_error;
|
||||
if ((error = recv_pkt(&pkt, buf)) < 0)
|
||||
goto done;
|
||||
|
||||
/* Check cancellation after network call */
|
||||
if (t->cancelled.val) {
|
||||
giterr_set(GITERR_NET, "The fetch was cancelled by the user");
|
||||
error = GIT_EUSER;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (pkt->type == GIT_PKT_PROGRESS) {
|
||||
if (t->progress_cb) {
|
||||
git_pkt_progress *p = (git_pkt_progress *) pkt;
|
||||
t->progress_cb(p->data, p->len, t->message_cb_payload);
|
||||
if (t->progress_cb(p->data, p->len, t->message_cb_payload)) {
|
||||
giterr_set(GITERR_NET, "The fetch was cancelled by the user");
|
||||
return GIT_EUSER;
|
||||
}
|
||||
}
|
||||
git__free(pkt);
|
||||
} else if (pkt->type == GIT_PKT_DATA) {
|
||||
@ -507,7 +527,7 @@ int git_smart__download_pack(
|
||||
|
||||
git__free(pkt);
|
||||
if (error < 0)
|
||||
goto on_error;
|
||||
goto done;
|
||||
} else if (pkt->type == GIT_PKT_FLUSH) {
|
||||
/* A flush indicates the end of the packfile */
|
||||
git__free(pkt);
|
||||
@ -515,13 +535,9 @@ int git_smart__download_pack(
|
||||
}
|
||||
} while (1);
|
||||
|
||||
if (writepack->commit(writepack, stats) < 0)
|
||||
goto on_error;
|
||||
error = writepack->commit(writepack, stats);
|
||||
|
||||
on_success:
|
||||
error = 0;
|
||||
|
||||
on_error:
|
||||
done:
|
||||
if (writepack)
|
||||
writepack->free(writepack);
|
||||
|
||||
@ -828,7 +844,10 @@ static int stream_thunk(void *buf, size_t size, void *data)
|
||||
|
||||
if ((current_time - payload->last_progress_report_time) >= MIN_PROGRESS_UPDATE_INTERVAL) {
|
||||
payload->last_progress_report_time = current_time;
|
||||
payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload);
|
||||
if (payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload)) {
|
||||
giterr_clear();
|
||||
error = GIT_EUSER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -840,7 +859,7 @@ int git_smart__push(git_transport *transport, git_push *push)
|
||||
transport_smart *t = (transport_smart *)transport;
|
||||
struct push_packbuilder_payload packbuilder_payload = {0};
|
||||
git_buf pktline = GIT_BUF_INIT;
|
||||
int error = -1, need_pack = 0;
|
||||
int error = 0, need_pack = 0;
|
||||
push_spec *spec;
|
||||
unsigned int i;
|
||||
|
||||
@ -882,34 +901,31 @@ int git_smart__push(git_transport *transport, git_push *push)
|
||||
}
|
||||
}
|
||||
|
||||
if (git_smart__get_push_stream(t, &packbuilder_payload.stream) < 0 ||
|
||||
gen_pktline(&pktline, push) < 0 ||
|
||||
packbuilder_payload.stream->write(packbuilder_payload.stream, git_buf_cstr(&pktline), git_buf_len(&pktline)) < 0)
|
||||
goto on_error;
|
||||
if ((error = git_smart__get_push_stream(t, &packbuilder_payload.stream)) < 0 ||
|
||||
(error = gen_pktline(&pktline, push)) < 0 ||
|
||||
(error = packbuilder_payload.stream->write(packbuilder_payload.stream, git_buf_cstr(&pktline), git_buf_len(&pktline))) < 0)
|
||||
goto done;
|
||||
|
||||
if (need_pack && git_packbuilder_foreach(push->pb, &stream_thunk, &packbuilder_payload) < 0)
|
||||
goto on_error;
|
||||
if (need_pack &&
|
||||
(error = git_packbuilder_foreach(push->pb, &stream_thunk, &packbuilder_payload)) < 0)
|
||||
goto done;
|
||||
|
||||
/* If we sent nothing or the server doesn't support report-status, then
|
||||
* we consider the pack to have been unpacked successfully */
|
||||
if (!push->specs.length || !push->report_status)
|
||||
push->unpack_ok = 1;
|
||||
else if (parse_report(&t->buffer, push) < 0)
|
||||
goto on_error;
|
||||
else if ((error = parse_report(&t->buffer, push)) < 0)
|
||||
goto done;
|
||||
|
||||
/* If progress is being reported write the final report */
|
||||
if (push->transfer_progress_cb) {
|
||||
push->transfer_progress_cb(push->pb->nr_written, push->pb->nr_objects, packbuilder_payload.last_bytes, push->transfer_progress_cb_payload);
|
||||
}
|
||||
|
||||
if (push->status.length &&
|
||||
update_refs_from_report(&t->refs, &push->specs, &push->status) < 0)
|
||||
goto on_error;
|
||||
if (push->status.length)
|
||||
error = update_refs_from_report(&t->refs, &push->specs, &push->status);
|
||||
|
||||
error = 0;
|
||||
|
||||
on_error:
|
||||
done:
|
||||
git_buf_free(&pktline);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@ -249,15 +249,15 @@ static int _git_ssh_authenticate_session(
|
||||
rc = libssh2_userauth_password(session, user, c->password);
|
||||
break;
|
||||
}
|
||||
case GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE: {
|
||||
git_cred_ssh_keyfile_passphrase *c = (git_cred_ssh_keyfile_passphrase *)cred;
|
||||
case GIT_CREDTYPE_SSH_KEY: {
|
||||
git_cred_ssh_key *c = (git_cred_ssh_key *)cred;
|
||||
user = c->username ? c->username : user;
|
||||
rc = libssh2_userauth_publickey_fromfile(
|
||||
session, c->username, c->publickey, c->privatekey, c->passphrase);
|
||||
break;
|
||||
}
|
||||
case GIT_CREDTYPE_SSH_PUBLICKEY: {
|
||||
git_cred_ssh_publickey *c = (git_cred_ssh_publickey *)cred;
|
||||
case GIT_CREDTYPE_SSH_CUSTOM: {
|
||||
git_cred_ssh_custom *c = (git_cred_ssh_custom *)cred;
|
||||
|
||||
user = c->username ? c->username : user;
|
||||
rc = libssh2_userauth_publickey(
|
||||
@ -349,7 +349,8 @@ static int _git_ssh_setup_conn(
|
||||
if (t->owner->cred_acquire_cb(
|
||||
&t->cred, t->owner->url, user,
|
||||
GIT_CREDTYPE_USERPASS_PLAINTEXT |
|
||||
GIT_CREDTYPE_SSH_KEYFILE_PASSPHRASE,
|
||||
GIT_CREDTYPE_SSH_KEY |
|
||||
GIT_CREDTYPE_SSH_CUSTOM,
|
||||
t->owner->cred_acquire_payload) < 0)
|
||||
goto on_error;
|
||||
|
||||
|
||||
@ -265,15 +265,32 @@ static int winhttp_stream_connect(winhttp_stream *s)
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
/* Send Content-Type header -- only necessary on a POST */
|
||||
if (post_verb == s->verb) {
|
||||
/* Send Content-Type and Accept headers -- only necessary on a POST */
|
||||
git_buf_clear(&buf);
|
||||
if (git_buf_printf(&buf, "Content-Type: application/x-git-%s-request", s->service) < 0)
|
||||
if (git_buf_printf(&buf,
|
||||
"Content-Type: application/x-git-%s-request",
|
||||
s->service) < 0)
|
||||
goto on_error;
|
||||
|
||||
git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf));
|
||||
|
||||
if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
|
||||
if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L,
|
||||
WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
|
||||
giterr_set(GITERR_OS, "Failed to add a header to the request");
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
git_buf_clear(&buf);
|
||||
if (git_buf_printf(&buf,
|
||||
"Accept: application/x-git-%s-result",
|
||||
s->service) < 0)
|
||||
goto on_error;
|
||||
|
||||
git__utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_buf_cstr(&buf));
|
||||
|
||||
if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L,
|
||||
WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
|
||||
giterr_set(GITERR_OS, "Failed to add a header to the request");
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
@ -237,7 +237,12 @@ void git_tree__free(void *_tree)
|
||||
|
||||
git_filemode_t git_tree_entry_filemode(const git_tree_entry *entry)
|
||||
{
|
||||
return (git_filemode_t)entry->attr;
|
||||
return normalize_filemode(entry->attr);
|
||||
}
|
||||
|
||||
git_filemode_t git_tree_entry_filemode_raw(const git_tree_entry *entry)
|
||||
{
|
||||
return entry->attr;
|
||||
}
|
||||
|
||||
const char *git_tree_entry_name(const git_tree_entry *entry)
|
||||
@ -386,8 +391,6 @@ int git_tree__parse(void *_tree, git_odb_object *odb_obj)
|
||||
if (git__strtol32(&attr, buffer, &buffer, 8) < 0 || !buffer)
|
||||
return tree_error("Failed to parse tree. Can't parse filemode", NULL);
|
||||
|
||||
attr = normalize_filemode(attr); /* make sure to normalize the filemode */
|
||||
|
||||
if (*buffer++ != ' ')
|
||||
return tree_error("Failed to parse tree. Object is corrupted", NULL);
|
||||
|
||||
|
||||
@ -125,8 +125,8 @@ static int do_lstat(
|
||||
|
||||
errno = ENOENT;
|
||||
|
||||
/* We need POSIX behavior, then ENOTDIR must set when any of the folders in the
|
||||
* file path is a regular file,otherwise ENOENT must be set.
|
||||
/* To match POSIX behavior, set ENOTDIR when any of the folders in the
|
||||
* file path is a regular file, otherwise set ENOENT.
|
||||
*/
|
||||
if (posix_enotdir) {
|
||||
/* scan up path until we find an existing item */
|
||||
|
||||
1124
tests-clar/checkout/conflict.c
Normal file
1124
tests-clar/checkout/conflict.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -224,13 +224,15 @@ void test_checkout_index__options_disable_filters(void)
|
||||
|
||||
void test_checkout_index__options_dir_modes(void)
|
||||
{
|
||||
#ifndef GIT_WIN32
|
||||
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
|
||||
struct stat st;
|
||||
git_oid oid;
|
||||
git_commit *commit;
|
||||
mode_t um;
|
||||
|
||||
if (!cl_is_chmod_supported())
|
||||
return;
|
||||
|
||||
cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
|
||||
cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
|
||||
|
||||
@ -252,15 +254,16 @@ void test_checkout_index__options_dir_modes(void)
|
||||
cl_assert_equal_i_fmt(st.st_mode, GIT_FILEMODE_BLOB_EXECUTABLE, "%07o");
|
||||
|
||||
git_commit_free(commit);
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_checkout_index__options_override_file_modes(void)
|
||||
{
|
||||
#ifndef GIT_WIN32
|
||||
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
|
||||
struct stat st;
|
||||
|
||||
if (!cl_is_chmod_supported())
|
||||
return;
|
||||
|
||||
opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
|
||||
opts.file_mode = 0700;
|
||||
|
||||
@ -268,7 +271,6 @@ void test_checkout_index__options_override_file_modes(void)
|
||||
|
||||
cl_git_pass(p_stat("./testrepo/new.txt", &st));
|
||||
cl_assert_equal_i_fmt(st.st_mode & GIT_MODE_PERMS_MASK, 0700, "%07o");
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_checkout_index__options_open_flags(void)
|
||||
|
||||
@ -696,3 +696,47 @@ void test_checkout_tree__extremely_long_file_name(void)
|
||||
cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
|
||||
cl_assert(!git_path_exists(path));
|
||||
}
|
||||
|
||||
static void create_conflict(void)
|
||||
{
|
||||
git_index *index;
|
||||
git_index_entry entry;
|
||||
|
||||
cl_git_pass(git_repository_index(&index, g_repo));
|
||||
|
||||
memset(&entry, 0x0, sizeof(git_index_entry));
|
||||
entry.mode = 0100644;
|
||||
entry.flags = 1 << GIT_IDXENTRY_STAGESHIFT;
|
||||
git_oid_fromstr(&entry.oid, "d427e0b2e138501a3d15cc376077a3631e15bd46");
|
||||
entry.path = "conflicts.txt";
|
||||
cl_git_pass(git_index_add(index, &entry));
|
||||
|
||||
entry.flags = 2 << GIT_IDXENTRY_STAGESHIFT;
|
||||
git_oid_fromstr(&entry.oid, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf");
|
||||
cl_git_pass(git_index_add(index, &entry));
|
||||
|
||||
entry.flags = 3 << GIT_IDXENTRY_STAGESHIFT;
|
||||
git_oid_fromstr(&entry.oid, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
|
||||
cl_git_pass(git_index_add(index, &entry));
|
||||
|
||||
git_index_write(index);
|
||||
git_index_free(index);
|
||||
}
|
||||
|
||||
void test_checkout_tree__fails_when_conflicts_exist_in_index(void)
|
||||
{
|
||||
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
|
||||
git_oid oid;
|
||||
git_object *obj = NULL;
|
||||
|
||||
opts.checkout_strategy = GIT_CHECKOUT_SAFE;
|
||||
|
||||
cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
|
||||
cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
|
||||
|
||||
create_conflict();
|
||||
|
||||
cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
|
||||
|
||||
git_object_free(obj);
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "posix.h"
|
||||
#include "path.h"
|
||||
#include "git2/sys/repository.h"
|
||||
|
||||
void cl_git_report_failure(
|
||||
int error, const char *file, int line, const char *fncall)
|
||||
@ -190,6 +191,9 @@ git_repository *cl_git_sandbox_init(const char *sandbox)
|
||||
/* Now open the sandbox repository and make it available for tests */
|
||||
cl_git_pass(git_repository_open(&_cl_repo, sandbox));
|
||||
|
||||
/* Adjust configs after copying to new filesystem */
|
||||
cl_git_pass(git_repository_reinit_filesystem(_cl_repo, 0));
|
||||
|
||||
return _cl_repo;
|
||||
}
|
||||
|
||||
@ -301,7 +305,7 @@ static int remove_placeholders_recurs(void *_data, git_buf *path)
|
||||
size_t pathlen;
|
||||
|
||||
if (git_path_isdir(path->ptr) == true)
|
||||
return git_path_direach(path, remove_placeholders_recurs, data);
|
||||
return git_path_direach(path, 0, remove_placeholders_recurs, data);
|
||||
|
||||
pathlen = path->size;
|
||||
|
||||
|
||||
@ -56,41 +56,18 @@ void test_clone_nonetwork__bad_url(void)
|
||||
cl_assert(!git_path_exists("./foo"));
|
||||
}
|
||||
|
||||
static int dont_call_me(void *state, git_buf *path)
|
||||
{
|
||||
GIT_UNUSED(state);
|
||||
GIT_UNUSED(path);
|
||||
return GIT_ERROR;
|
||||
}
|
||||
|
||||
void test_clone_nonetwork__do_not_clean_existing_directory(void)
|
||||
{
|
||||
git_buf path_buf = GIT_BUF_INIT;
|
||||
|
||||
git_buf_put(&path_buf, "./foo", 5);
|
||||
|
||||
/* Clone should not remove the directory if it already exists, but
|
||||
* Should clean up entries it creates. */
|
||||
p_mkdir("./foo", GIT_DIR_MODE);
|
||||
cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
|
||||
cl_assert(git_path_exists("./foo"));
|
||||
|
||||
/* Make sure the directory is empty. */
|
||||
cl_git_pass(git_path_direach(&path_buf,
|
||||
dont_call_me,
|
||||
NULL));
|
||||
cl_assert(git_path_is_empty_dir("./foo"));
|
||||
|
||||
/* Try again with a bare repository. */
|
||||
g_options.bare = true;
|
||||
cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
|
||||
cl_assert(git_path_exists("./foo"));
|
||||
|
||||
/* Make sure the directory is empty. */
|
||||
cl_git_pass(git_path_direach(&path_buf,
|
||||
dont_call_me,
|
||||
NULL));
|
||||
|
||||
git_buf_free(&path_buf);
|
||||
cl_assert(git_path_is_empty_dir("./foo"));
|
||||
}
|
||||
|
||||
void test_clone_nonetwork__local(void)
|
||||
|
||||
@ -88,14 +88,6 @@ static int one_entry(void *state, git_buf *path)
|
||||
return GIT_ERROR;
|
||||
}
|
||||
|
||||
static int dont_call_me(void *state, git_buf *path)
|
||||
{
|
||||
GIT_UNUSED(state);
|
||||
GIT_UNUSED(path);
|
||||
return GIT_ERROR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static name_data dot_names[] = {
|
||||
{ 0, "./a" },
|
||||
@ -115,9 +107,7 @@ void test_core_dirent__dont_traverse_dot(void)
|
||||
cl_set_cleanup(&dirent_cleanup__cb, &dot);
|
||||
setup(&dot);
|
||||
|
||||
cl_git_pass(git_path_direach(&dot.path,
|
||||
one_entry,
|
||||
&dot));
|
||||
cl_git_pass(git_path_direach(&dot.path, 0, one_entry, &dot));
|
||||
|
||||
check_counts(&dot);
|
||||
}
|
||||
@ -141,9 +131,7 @@ void test_core_dirent__traverse_subfolder(void)
|
||||
cl_set_cleanup(&dirent_cleanup__cb, &sub);
|
||||
setup(&sub);
|
||||
|
||||
cl_git_pass(git_path_direach(&sub.path,
|
||||
one_entry,
|
||||
&sub));
|
||||
cl_git_pass(git_path_direach(&sub.path, 0, one_entry, &sub));
|
||||
|
||||
check_counts(&sub);
|
||||
}
|
||||
@ -161,9 +149,7 @@ void test_core_dirent__traverse_slash_terminated_folder(void)
|
||||
cl_set_cleanup(&dirent_cleanup__cb, &sub_slash);
|
||||
setup(&sub_slash);
|
||||
|
||||
cl_git_pass(git_path_direach(&sub_slash.path,
|
||||
one_entry,
|
||||
&sub_slash));
|
||||
cl_git_pass(git_path_direach(&sub_slash.path, 0, one_entry, &sub_slash));
|
||||
|
||||
check_counts(&sub_slash);
|
||||
}
|
||||
@ -184,16 +170,12 @@ void test_core_dirent__dont_traverse_empty_folders(void)
|
||||
cl_set_cleanup(&dirent_cleanup__cb, &empty);
|
||||
setup(&empty);
|
||||
|
||||
cl_git_pass(git_path_direach(&empty.path,
|
||||
one_entry,
|
||||
&empty));
|
||||
cl_git_pass(git_path_direach(&empty.path, 0, one_entry, &empty));
|
||||
|
||||
check_counts(&empty);
|
||||
|
||||
/* make sure callback not called */
|
||||
cl_git_pass(git_path_direach(&empty.path,
|
||||
dont_call_me,
|
||||
&empty));
|
||||
cl_assert(git_path_is_empty_dir(empty.path.ptr));
|
||||
}
|
||||
|
||||
static name_data odd_names[] = {
|
||||
@ -216,9 +198,7 @@ void test_core_dirent__traverse_weird_filenames(void)
|
||||
cl_set_cleanup(&dirent_cleanup__cb, &odd);
|
||||
setup(&odd);
|
||||
|
||||
cl_git_pass(git_path_direach(&odd.path,
|
||||
one_entry,
|
||||
&odd));
|
||||
cl_git_pass(git_path_direach(&odd.path, 0, one_entry, &odd));
|
||||
|
||||
check_counts(&odd);
|
||||
}
|
||||
@ -231,5 +211,26 @@ void test_core_dirent__length_limits(void)
|
||||
big_filename[FILENAME_MAX] = 0;
|
||||
|
||||
cl_must_fail(p_creat(big_filename, 0666));
|
||||
|
||||
git__free(big_filename);
|
||||
}
|
||||
|
||||
void test_core_dirent__empty_dir(void)
|
||||
{
|
||||
cl_must_pass(p_mkdir("empty_dir", 0777));
|
||||
cl_assert(git_path_is_empty_dir("empty_dir"));
|
||||
|
||||
cl_git_mkfile("empty_dir/content", "whatever\n");
|
||||
cl_assert(!git_path_is_empty_dir("empty_dir"));
|
||||
cl_assert(!git_path_is_empty_dir("empty_dir/content"));
|
||||
|
||||
cl_must_pass(p_unlink("empty_dir/content"));
|
||||
|
||||
cl_must_pass(p_mkdir("empty_dir/content", 0777));
|
||||
cl_assert(!git_path_is_empty_dir("empty_dir"));
|
||||
cl_assert(git_path_is_empty_dir("empty_dir/content"));
|
||||
|
||||
cl_must_pass(p_rmdir("empty_dir/content"));
|
||||
|
||||
cl_must_pass(p_rmdir("empty_dir"));
|
||||
}
|
||||
|
||||
60
tests-clar/core/iconv.c
Normal file
60
tests-clar/core/iconv.c
Normal file
@ -0,0 +1,60 @@
|
||||
#include "clar_libgit2.h"
|
||||
#include "path.h"
|
||||
|
||||
static git_path_iconv_t ic;
|
||||
static char *nfc = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D";
|
||||
static char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
|
||||
|
||||
void test_core_iconv__initialize(void)
|
||||
{
|
||||
cl_git_pass(git_path_iconv_init_precompose(&ic));
|
||||
}
|
||||
|
||||
void test_core_iconv__cleanup(void)
|
||||
{
|
||||
git_path_iconv_clear(&ic);
|
||||
}
|
||||
|
||||
void test_core_iconv__unchanged(void)
|
||||
{
|
||||
char *data = "Ascii data", *original = data;
|
||||
size_t datalen = strlen(data);
|
||||
|
||||
cl_git_pass(git_path_iconv(&ic, &data, &datalen));
|
||||
GIT_UNUSED(datalen);
|
||||
|
||||
/* There are no high bits set, so this should leave data untouched */
|
||||
cl_assert(data == original);
|
||||
}
|
||||
|
||||
void test_core_iconv__decomposed_to_precomposed(void)
|
||||
{
|
||||
char *data = nfd;
|
||||
size_t datalen = strlen(nfd);
|
||||
|
||||
cl_git_pass(git_path_iconv(&ic, &data, &datalen));
|
||||
GIT_UNUSED(datalen);
|
||||
|
||||
/* The decomposed nfd string should be transformed to the nfc form
|
||||
* (on platforms where iconv is enabled, of course).
|
||||
*/
|
||||
#ifdef GIT_USE_ICONV
|
||||
cl_assert_equal_s(nfc, data);
|
||||
#else
|
||||
cl_assert_equal_s(nfd, data);
|
||||
#endif
|
||||
}
|
||||
|
||||
void test_core_iconv__precomposed_is_unmodified(void)
|
||||
{
|
||||
char *data = nfc;
|
||||
size_t datalen = strlen(nfc);
|
||||
|
||||
cl_git_pass(git_path_iconv(&ic, &data, &datalen));
|
||||
GIT_UNUSED(datalen);
|
||||
|
||||
/* data is already in precomposed form, so even though some bytes have
|
||||
* the high-bit set, the iconv transform should result in no change.
|
||||
*/
|
||||
cl_assert_equal_s(nfc, data);
|
||||
}
|
||||
@ -111,14 +111,20 @@ static void cleanup_chmod_root(void *ref)
|
||||
git_futils_rmdir_r("r", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
|
||||
}
|
||||
|
||||
static void check_mode(mode_t expected, mode_t actual)
|
||||
#define check_mode(X,A) check_mode_at_line((X), (A), __FILE__, __LINE__)
|
||||
|
||||
static void check_mode_at_line(
|
||||
mode_t expected, mode_t actual, const char *file, int line)
|
||||
{
|
||||
#ifdef GIT_WIN32
|
||||
/* chmod on Win32 doesn't support exec bit, not group/world bits */
|
||||
cl_assert_equal_i_fmt((expected & 0600), (actual & 0777), "%07o");
|
||||
#else
|
||||
cl_assert_equal_i_fmt(expected, (actual & 0777), "%07o");
|
||||
#endif
|
||||
/* FAT filesystems don't support exec bit, nor group/world bits */
|
||||
if (!cl_is_chmod_supported()) {
|
||||
expected &= 0600;
|
||||
actual &= 0600;
|
||||
}
|
||||
|
||||
clar__assert_equal(
|
||||
file, line, "expected_mode != actual_mode", 1,
|
||||
"%07o", (int)expected, (int)(actual & 0777));
|
||||
}
|
||||
|
||||
void test_core_mkdir__chmods(void)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user