summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2015-11-05 10:49:39 -0500
committerEdward Thomson <ethomson@edwardthomson.com>2015-11-05 10:49:39 -0500
commit41dc9f067d822c91c70fd9e241c4a89da7c897ba (patch)
tree22346591a32d0641f78f85935542a46496dad340
parentfe965028885fbd8c62dce08e3a86cd3cb3e3b320 (diff)
parent5b6b774533fe0973a652be296b774bcddd96e5fd (diff)
downloadlibgit2-41dc9f067d822c91c70fd9e241c4a89da7c897ba.tar.gz
Merge pull request #3501 from libgit2/cmn/for-v23
Backports for v0.23
-rw-r--r--CMakeLists.txt40
-rw-r--r--docs/error-handling.md2
-rw-r--r--include/git2/common.h14
-rw-r--r--include/git2/sys/transport.h1
-rw-r--r--libgit2.pc.in5
-rw-r--r--src/checkout.c15
-rw-r--r--src/commit_list.c6
-rw-r--r--src/commit_list.h3
-rw-r--r--src/diff.c4
-rw-r--r--src/diff_print.c2
-rw-r--r--src/filebuf.c84
-rw-r--r--src/index.c35
-rw-r--r--src/merge.c192
-rw-r--r--src/odb.c243
-rw-r--r--src/odb_mempack.c11
-rw-r--r--src/odb_pack.c82
-rw-r--r--src/openssl_stream.c7
-rw-r--r--src/refdb_fs.c2
-rw-r--r--src/repository.c24
-rw-r--r--src/signature.c18
-rw-r--r--src/transports/git.c11
-rw-r--r--src/transports/ssh.c4
-rw-r--r--src/unix/posix.h15
-rw-r--r--src/util.c21
-rw-r--r--src/win32/path_w32.c23
-rw-r--r--src/win32/posix_w32.c11
-rw-r--r--src/xdiff/xdiff.h2
-rw-r--r--tests/checkout/tree.c84
-rw-r--r--tests/commit/signature.c7
-rw-r--r--tests/core/filebuf.c53
-rw-r--r--tests/diff/binary.c21
-rw-r--r--tests/index/nsec.c78
-rw-r--r--tests/odb/sorting.c16
-rw-r--r--tests/refs/reflog/reflog.c43
-rw-r--r--tests/resources/nsecs/.gitted/HEAD1
-rw-r--r--tests/resources/nsecs/.gitted/config8
-rw-r--r--tests/resources/nsecs/.gitted/indexbin0 -> 281 bytes
-rw-r--r--tests/resources/nsecs/.gitted/objects/03/1986a8372d1442cfe9e3b54906a9aadc524a7e2
-rw-r--r--tests/resources/nsecs/.gitted/objects/03/9afd91c98f82c14e425bb6796d8ca98e9c8cacbin0 -> 102 bytes
-rw-r--r--tests/resources/nsecs/.gitted/objects/6d/8b18077cc99abd8dda05a6062c646406abb2d4bin0 -> 22 bytes
-rw-r--r--tests/resources/nsecs/.gitted/objects/c5/12b6c64656b87ea8caf37a32bc5a562d797745bin0 -> 22 bytes
-rw-r--r--tests/resources/nsecs/.gitted/objects/df/78d3d51c369e1d2f1eadb73464aadd931d56b4bin0 -> 22 bytes
-rw-r--r--tests/resources/nsecs/.gitted/refs/heads/master1
-rw-r--r--tests/resources/nsecs/a.txt1
-rw-r--r--tests/resources/nsecs/b.txt1
-rw-r--r--tests/resources/nsecs/c.txt1
-rw-r--r--tests/resources/redundant.git/HEAD1
-rwxr-xr-xtests/resources/redundant.git/config5
-rw-r--r--tests/resources/redundant.git/objects/info/packs2
-rw-r--r--tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.idxbin0 -> 121136 bytes
-rw-r--r--tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.packbin0 -> 309860 bytes
-rw-r--r--tests/resources/redundant.git/packed-refs3
-rw-r--r--tests/resources/redundant.git/refs/.gitkeep0
-rw-r--r--tests/revwalk/basic.c35
-rw-r--r--tests/revwalk/mergebase.c19
-rw-r--r--tests/revwalk/signatureparsing.c2
-rw-r--r--tests/win32/longpath.c60
57 files changed, 1035 insertions, 286 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 73c963016..714e188e9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,6 +19,7 @@ CMAKE_POLICY(SET CMP0015 NEW)
SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules/")
INCLUDE(CheckLibraryExists)
+INCLUDE(CheckFunctionExists)
INCLUDE(AddCFlagIfSupported)
INCLUDE(FindPkgConfig)
@@ -95,6 +96,23 @@ SET(BIN_INSTALL_DIR bin CACHE PATH "Where to install binaries to.")
SET(LIB_INSTALL_DIR lib CACHE PATH "Where to install libraries to.")
SET(INCLUDE_INSTALL_DIR include CACHE PATH "Where to install headers to.")
+# Set a couple variables to be substituted inside the .pc file.
+# We can't just use LIB_INSTALL_DIR in the .pc file, as passing them as absolue
+# or relative paths is both valid and supported by cmake.
+SET (PKGCONFIG_PREFIX ${CMAKE_INSTALL_PREFIX})
+
+IF(IS_ABSOLUTE ${LIB_INSTALL_DIR})
+ SET (PKGCONFIG_LIBDIR ${LIB_INSTALL_DIR})
+ELSE(IS_ABSOLUTE ${LIB_INSTALL_DIR})
+ SET (PKGCONFIG_LIBDIR "\${prefix}/${LIB_INSTALL_DIR}")
+ENDIF (IS_ABSOLUTE ${LIB_INSTALL_DIR})
+
+IF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
+ SET (PKGCONFIG_INCLUDEDIR ${INCLUDE_INSTALL_DIR})
+ELSE(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
+ SET (PKGCONFIG_INCLUDEDIR "\${prefix}/${INCLUDE_INSTALL_DIR}")
+ENDIF(IS_ABSOLUTE ${INCLUDE_INSTALL_DIR})
+
FUNCTION(TARGET_OS_LIBRARIES target)
IF(WIN32)
TARGET_LINK_LIBRARIES(${target} ws2_32)
@@ -440,6 +458,21 @@ ELSE ()
ENDIF ()
ENDIF()
+CHECK_FUNCTION_EXISTS(futimens HAVE_FUTIMENS)
+IF (HAVE_FUTIMENS)
+ ADD_DEFINITIONS(-DHAVE_FUTIMENS)
+ENDIF ()
+
+CHECK_FUNCTION_EXISTS(qsort_r HAVE_QSORT_R)
+IF (HAVE_QSORT_R)
+ ADD_DEFINITIONS(-DHAVE_QSORT_R)
+ENDIF ()
+
+CHECK_FUNCTION_EXISTS(qsort_s HAVE_QSORT_S)
+IF (HAVE_QSORT_S)
+ ADD_DEFINITIONS(-DHAVE_QSORT_S)
+ENDIF ()
+
IF( NOT CMAKE_CONFIGURATION_TYPES )
# Build Debug by default
IF (NOT CMAKE_BUILD_TYPE)
@@ -546,7 +579,12 @@ INSTALL(FILES include/git2.h DESTINATION ${INCLUDE_INSTALL_DIR} )
# Tests
IF (BUILD_CLAR)
- FIND_PACKAGE(PythonInterp REQUIRED)
+ FIND_PACKAGE(PythonInterp)
+
+ IF(NOT PYTHONINTERP_FOUND)
+ MESSAGE(FATAL_ERROR "Could not find a python interpeter, which is needed to build the tests. "
+ "Make sure python is available, or pass -DBUILD_CLAR=OFF to skip building the tests")
+ ENDIF()
SET(CLAR_FIXTURES "${CMAKE_CURRENT_SOURCE_DIR}/tests/resources/")
SET(CLAR_PATH "${CMAKE_CURRENT_SOURCE_DIR}/tests")
diff --git a/docs/error-handling.md b/docs/error-handling.md
index 2dbe64a71..719244d2f 100644
--- a/docs/error-handling.md
+++ b/docs/error-handling.md
@@ -128,7 +128,7 @@ The public error API
bugs, but in the meantime, please code defensively and check for NULL
when calling this function.
-- `void geterr_clear(void)`: This function clears the last error. The
+- `void giterr_clear(void)`: This function clears the last error. The
library will call this when an error is generated by low level function
and the higher level function handles the error.
diff --git a/include/git2/common.h b/include/git2/common.h
index d84a76512..577906115 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -10,12 +10,6 @@
#include <time.h>
#include <stdlib.h>
-#ifdef _MSC_VER
-# include "inttypes.h"
-#else
-# include <inttypes.h>
-#endif
-
#ifdef __cplusplus
# define GIT_BEGIN_DECL extern "C" {
# define GIT_END_DECL }
@@ -26,6 +20,14 @@
# define GIT_END_DECL /* empty */
#endif
+#if defined(_MSC_VER) && _MSC_VER < 1800
+ GIT_BEGIN_DECL
+# include "inttypes.h"
+ GIT_END_DECL
+#else
+# include <inttypes.h>
+#endif
+
/** Declare a public function exported for application use. */
#if __GNUC__ >= 4
# define GIT_EXTERN(type) extern \
diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h
index 867fbcbce..e6ee3c654 100644
--- a/include/git2/sys/transport.h
+++ b/include/git2/sys/transport.h
@@ -10,6 +10,7 @@
#include "git2/net.h"
#include "git2/types.h"
+#include "git2/strarray.h"
/**
* @file git2/sys/transport.h
diff --git a/libgit2.pc.in b/libgit2.pc.in
index 3d825a49f..880266a30 100644
--- a/libgit2.pc.in
+++ b/libgit2.pc.in
@@ -1,5 +1,6 @@
-libdir=@CMAKE_INSTALL_PREFIX@/@LIB_INSTALL_DIR@
-includedir=@CMAKE_INSTALL_PREFIX@/@INCLUDE_INSTALL_DIR@
+prefix=@PKGCONFIG_PREFIX@
+libdir=@PKGCONFIG_LIBDIR@
+includedir=@PKGCONFIG_INCLUDEDIR@
Name: libgit2
Description: The git library, take 2
diff --git a/src/checkout.c b/src/checkout.c
index e7699d95f..12e308257 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -244,6 +244,12 @@ static int checkout_action_common(
if (delta->new_file.mode == GIT_FILEMODE_LINK && wd != NULL)
*action |= CHECKOUT_ACTION__REMOVE;
+ /* if the file is on disk and doesn't match our mode, force update */
+ if (wd &&
+ GIT_PERMS_IS_EXEC(wd->mode) !=
+ GIT_PERMS_IS_EXEC(delta->new_file.mode))
+ *action |= CHECKOUT_ACTION__REMOVE;
+
notify = GIT_CHECKOUT_NOTIFY_UPDATED;
}
@@ -1501,15 +1507,6 @@ static int blob_content_to_file(
if (error < 0)
return error;
- if (GIT_PERMS_IS_EXEC(mode)) {
- data->perfdata.chmod_calls++;
-
- if ((error = p_chmod(path, mode)) < 0) {
- giterr_set(GITERR_OS, "Failed to set permissions on '%s'", path);
- return error;
- }
- }
-
if (st) {
data->perfdata.stat_calls++;
diff --git a/src/commit_list.c b/src/commit_list.c
index 3054c18dd..53612d514 100644
--- a/src/commit_list.c
+++ b/src/commit_list.c
@@ -110,7 +110,7 @@ static int commit_quick_parse(
const uint8_t *buffer_end = buffer + buffer_len;
const uint8_t *parents_start, *committer_start;
int i, parents = 0;
- int commit_time;
+ int64_t commit_time;
buffer += strlen("tree ") + GIT_OID_HEXSZ + 1;
@@ -166,10 +166,10 @@ static int commit_quick_parse(
buffer--;
}
- if ((buffer == committer_start) || (git__strtol32(&commit_time, (char *)(buffer + 1), NULL, 10) < 0))
+ if ((buffer == committer_start) || (git__strtol64(&commit_time, (char *)(buffer + 1), NULL, 10) < 0))
return commit_error(commit, "cannot parse commit time");
- commit->time = (time_t)commit_time;
+ commit->time = commit_time;
commit->parsed = 1;
return 0;
}
diff --git a/src/commit_list.h b/src/commit_list.h
index 6b3f473d3..a6967bcef 100644
--- a/src/commit_list.h
+++ b/src/commit_list.h
@@ -13,6 +13,7 @@
#define PARENT2 (1 << 1)
#define RESULT (1 << 2)
#define STALE (1 << 3)
+#define ALL_FLAGS (PARENT1 | PARENT2 | STALE | RESULT)
#define PARENTS_PER_COMMIT 2
#define COMMIT_ALLOC \
@@ -22,7 +23,7 @@
typedef struct git_commit_list_node {
git_oid oid;
- uint32_t time;
+ int64_t time;
unsigned int seen:1,
uninteresting:1,
topo_delay:1,
diff --git a/src/diff.c b/src/diff.c
index c1adcc662..9cde03e17 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -471,8 +471,10 @@ static int diff_list_apply_options(
/* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
- /* Set GIT_DIFFCAPS_TRUST_NANOSECS on a platform basis */
+ /* Don't trust nanoseconds; we do not load nanos from disk */
+#ifdef GIT_USE_NSEC
diff->diffcaps = diff->diffcaps | GIT_DIFFCAPS_TRUST_NANOSECS;
+#endif
/* If not given explicit `opts`, check `diff.xyz` configs */
if (!opts) {
diff --git a/src/diff_print.c b/src/diff_print.c
index d406a441a..bc2d6fab0 100644
--- a/src/diff_print.c
+++ b/src/diff_print.c
@@ -358,6 +358,7 @@ static int format_binary(
scan += chunk_len;
pi->line.num_lines++;
}
+ git_buf_putc(pi->buf, '\n');
return 0;
}
@@ -416,7 +417,6 @@ static int diff_print_patch_file_binary(
if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data,
binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 ||
- (error = git_buf_putc(pi->buf, '\n')) < 0 ||
(error = format_binary(pi, binary->old_file.type, binary->old_file.data,
binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) {
diff --git a/src/filebuf.c b/src/filebuf.c
index 838f4b4d2..2bbc210ba 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -191,6 +191,81 @@ static int write_deflate(git_filebuf *file, void *source, size_t len)
return 0;
}
+#define MAX_SYMLINK_DEPTH 5
+
+static int resolve_symlink(git_buf *out, const char *path)
+{
+ int i, error, root;
+ ssize_t ret;
+ struct stat st;
+ git_buf curpath = GIT_BUF_INIT, target = GIT_BUF_INIT;
+
+ if ((error = git_buf_grow(&target, GIT_PATH_MAX + 1)) < 0 ||
+ (error = git_buf_puts(&curpath, path)) < 0)
+ return error;
+
+ for (i = 0; i < MAX_SYMLINK_DEPTH; i++) {
+ error = p_lstat(curpath.ptr, &st);
+ if (error < 0 && errno == ENOENT) {
+ error = git_buf_puts(out, curpath.ptr);
+ goto cleanup;
+ }
+
+ if (error < 0) {
+ giterr_set(GITERR_OS, "failed to stat '%s'", curpath.ptr);
+ error = -1;
+ goto cleanup;
+ }
+
+ if (!S_ISLNK(st.st_mode)) {
+ error = git_buf_puts(out, curpath.ptr);
+ goto cleanup;
+ }
+
+ ret = p_readlink(curpath.ptr, target.ptr, GIT_PATH_MAX);
+ if (ret < 0) {
+ giterr_set(GITERR_OS, "failed to read symlink '%s'", curpath.ptr);
+ error = -1;
+ goto cleanup;
+ }
+
+ if (ret == GIT_PATH_MAX) {
+ giterr_set(GITERR_INVALID, "symlink target too long");
+ error = -1;
+ goto cleanup;
+ }
+
+ /* readlink(2) won't NUL-terminate for us */
+ target.ptr[ret] = '\0';
+ target.size = ret;
+
+ root = git_path_root(target.ptr);
+ if (root >= 0) {
+ if ((error = git_buf_puts(&curpath, target.ptr)) < 0)
+ goto cleanup;
+ } else {
+ git_buf dir = GIT_BUF_INIT;
+
+ if ((error = git_path_dirname_r(&dir, curpath.ptr)) < 0)
+ goto cleanup;
+
+ git_buf_swap(&curpath, &dir);
+ git_buf_free(&dir);
+
+ if ((error = git_path_apply_relative(&curpath, target.ptr)) < 0)
+ goto cleanup;
+ }
+ }
+
+ giterr_set(GITERR_INVALID, "maximum symlink depth reached");
+ error = -1;
+
+cleanup:
+ git_buf_free(&curpath);
+ git_buf_free(&target);
+ return error;
+}
+
int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode)
{
int compression, error = -1;
@@ -265,11 +340,14 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode
file->path_lock = git_buf_detach(&tmp_path);
GITERR_CHECK_ALLOC(file->path_lock);
} else {
- path_len = strlen(path);
+ git_buf resolved_path = GIT_BUF_INIT;
+
+ if ((error = resolve_symlink(&resolved_path, path)) < 0)
+ goto cleanup;
/* Save the original path of the file */
- file->path_original = git__strdup(path);
- GITERR_CHECK_ALLOC(file->path_original);
+ path_len = resolved_path.size;
+ file->path_original = git_buf_detach(&resolved_path);
/* create the locking path by appending ".lock" to the original */
GITERR_CHECK_ALLOC_ADD(&alloc_len, path_len, GIT_FILELOCK_EXTLENGTH);
diff --git a/src/index.c b/src/index.c
index 73f0b3d26..cb5902ea9 100644
--- a/src/index.c
+++ b/src/index.c
@@ -1790,27 +1790,24 @@ size_t git_index_reuc_entrycount(git_index *index)
return index->reuc.length;
}
+static int index_reuc_on_dup(void **old, void *new)
+{
+ index_entry_reuc_free(*old);
+ *old = new;
+ return GIT_EEXISTS;
+}
+
static int index_reuc_insert(
git_index *index,
- git_index_reuc_entry *reuc,
- int replace)
+ git_index_reuc_entry *reuc)
{
- git_index_reuc_entry **existing = NULL;
- size_t position;
+ int res;
assert(index && reuc && reuc->path != NULL);
+ assert(git_vector_is_sorted(&index->reuc));
- if (!git_index_reuc_find(&position, index, reuc->path))
- existing = (git_index_reuc_entry **)&index->reuc.contents[position];
-
- if (!replace || !existing)
- return git_vector_insert(&index->reuc, reuc);
-
- /* exists, replace it */
- git__free(*existing);
- *existing = reuc;
-
- return 0;
+ res = git_vector_insert_sorted(&index->reuc, reuc, &index_reuc_on_dup);
+ return res == GIT_EEXISTS ? 0 : res;
}
int git_index_reuc_add(git_index *index, const char *path,
@@ -1825,7 +1822,7 @@ int git_index_reuc_add(git_index *index, const char *path,
if ((error = index_entry_reuc_init(&reuc, path, ancestor_mode,
ancestor_oid, our_mode, our_oid, their_mode, their_oid)) < 0 ||
- (error = index_reuc_insert(index, reuc, 1)) < 0)
+ (error = index_reuc_insert(index, reuc)) < 0)
index_entry_reuc_free(reuc);
return error;
@@ -1845,7 +1842,7 @@ const git_index_reuc_entry *git_index_reuc_get_bypath(
if (!index->reuc.length)
return NULL;
- git_vector_sort(&index->reuc);
+ assert(git_vector_is_sorted(&index->reuc));
if (git_index_reuc_find(&pos, index, path) < 0)
return NULL;
@@ -1857,8 +1854,8 @@ const git_index_reuc_entry *git_index_reuc_get_byindex(
git_index *index, size_t n)
{
assert(index);
+ assert(git_vector_is_sorted(&index->reuc));
- git_vector_sort(&index->reuc);
return git_vector_get(&index->reuc, n);
}
@@ -1867,7 +1864,7 @@ int git_index_reuc_remove(git_index *index, size_t position)
int error;
git_index_reuc_entry *reuc;
- git_vector_sort(&index->reuc);
+ assert(git_vector_is_sorted(&index->reuc));
reuc = git_vector_get(&index->reuc, position);
error = git_vector_remove(&index->reuc, position);
diff --git a/src/merge.c b/src/merge.c
index 13b524b81..9799f935b 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -302,30 +302,59 @@ static int interesting(git_pqueue *list)
return 0;
}
-int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos)
+static void clear_commit_marks_1(git_commit_list **plist,
+ git_commit_list_node *commit, unsigned int mark)
{
- int error;
- unsigned int i;
- git_commit_list_node *two;
- git_commit_list *result = NULL, *tmp = NULL;
- git_pqueue list;
+ while (commit) {
+ unsigned int i;
- /* If there's only the one commit, there can be no merge bases */
- if (twos->length == 0) {
- *out = NULL;
- return 0;
+ if (!(mark & commit->flags))
+ return;
+
+ commit->flags &= ~mark;
+
+ for (i = 1; i < commit->out_degree; i++) {
+ git_commit_list_node *p = commit->parents[i];
+ git_commit_list_insert(p, plist);
+ }
+
+ commit = commit->out_degree ? commit->parents[0] : NULL;
}
+}
- /* if the commit is repeated, we have a our merge base already */
- git_vector_foreach(twos, i, two) {
- if (one == two)
- return git_commit_list_insert(one, out) ? 0 : -1;
+static void clear_commit_marks_many(git_vector *commits, unsigned int mark)
+{
+ git_commit_list *list = NULL;
+ git_commit_list_node *c;
+ unsigned int i;
+
+ git_vector_foreach(commits, i, c) {
+ git_commit_list_insert(c, &list);
}
- if (git_pqueue_init(&list, 0, twos->length * 2, git_commit_list_time_cmp) < 0)
- return -1;
+ while (list)
+ clear_commit_marks_1(&list, git_commit_list_pop(&list), mark);
+}
- if (git_commit_list_parse(walk, one) < 0)
+static void clear_commit_marks(git_commit_list_node *commit, unsigned int mark)
+{
+ git_commit_list *list = NULL;
+ git_commit_list_insert(commit, &list);
+ while (list)
+ clear_commit_marks_1(&list, git_commit_list_pop(&list), mark);
+}
+
+static int paint_down_to_common(
+ git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos)
+{
+ git_pqueue list;
+ git_commit_list *result = NULL;
+ git_commit_list_node *two;
+
+ int error;
+ unsigned int i;
+
+ if (git_pqueue_init(&list, 0, twos->length * 2, git_commit_list_time_cmp) < 0)
return -1;
one->flags |= PARENT1;
@@ -376,19 +405,138 @@ int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_l
}
git_pqueue_free(&list);
+ *out = result;
+ return 0;
+}
+
+static int remove_redundant(git_revwalk *walk, git_vector *commits)
+{
+ git_vector work = GIT_VECTOR_INIT;
+ unsigned char *redundant;
+ unsigned int *filled_index;
+ unsigned int i, j;
+ int error = 0;
+
+ redundant = git__calloc(commits->length, 1);
+ GITERR_CHECK_ALLOC(redundant);
+ filled_index = git__calloc((commits->length - 1), sizeof(unsigned int));
+ GITERR_CHECK_ALLOC(filled_index);
+
+ for (i = 0; i < commits->length; ++i) {
+ if ((error = git_commit_list_parse(walk, commits->contents[i])) < 0)
+ goto done;
+ }
+
+ for (i = 0; i < commits->length; ++i) {
+ git_commit_list *common = NULL;
+ git_commit_list_node *commit = commits->contents[i];
+
+ if (redundant[i])
+ continue;
+
+ git_vector_clear(&work);
+
+ for (j = 0; j < commits->length; j++) {
+ if (i == j || redundant[j])
+ continue;
+
+ filled_index[work.length] = j;
+ if ((error = git_vector_insert(&work, commits->contents[j])) < 0)
+ goto done;
+ }
+
+ error = paint_down_to_common(&common, walk, commit, &work);
+ if (error < 0)
+ goto done;
+
+ if (commit->flags & PARENT2)
+ redundant[i] = 1;
+
+ for (j = 0; j < work.length; j++) {
+ git_commit_list_node *w = work.contents[j];
+ if (w->flags & PARENT1)
+ redundant[filled_index[j]] = 1;
+ }
+
+ clear_commit_marks(commit, ALL_FLAGS);
+ clear_commit_marks_many(&work, ALL_FLAGS);
+
+ git_commit_list_free(&common);
+ }
+
+ for (i = 0; i < commits->length; ++i) {
+ if (redundant[i])
+ commits->contents[i] = NULL;
+ }
+
+done:
+ git__free(redundant);
+ git__free(filled_index);
+ git_vector_free(&work);
+ return error;
+}
+
+int git_merge__bases_many(git_commit_list **out, git_revwalk *walk, git_commit_list_node *one, git_vector *twos)
+{
+ int error;
+ unsigned int i;
+ git_commit_list_node *two;
+ git_commit_list *result = NULL, *tmp = NULL;
+
+ /* If there's only the one commit, there can be no merge bases */
+ if (twos->length == 0) {
+ *out = NULL;
+ return 0;
+ }
+
+ /* if the commit is repeated, we have a our merge base already */
+ git_vector_foreach(twos, i, two) {
+ if (one == two)
+ return git_commit_list_insert(one, out) ? 0 : -1;
+ }
+
+ if (git_commit_list_parse(walk, one) < 0)
+ return -1;
+
+ error = paint_down_to_common(&result, walk, one, twos);
+ if (error < 0)
+ return error;
/* filter out any stale commits in the results */
tmp = result;
result = NULL;
while (tmp) {
- struct git_commit_list *next = tmp->next;
- if (!(tmp->item->flags & STALE))
- if (git_commit_list_insert_by_date(tmp->item, &result) == NULL)
+ git_commit_list_node *c = git_commit_list_pop(&tmp);
+ if (!(c->flags & STALE))
+ if (git_commit_list_insert_by_date(c, &result) == NULL)
return -1;
+ }
+
+ /*
+ * more than one merge base -- see if there are redundant merge
+ * bases and remove them
+ */
+ if (result && result->next) {
+ git_vector redundant = GIT_VECTOR_INIT;
+
+ while (result)
+ git_vector_insert(&redundant, git_commit_list_pop(&result));
+
+ clear_commit_marks(one, ALL_FLAGS);
+ clear_commit_marks_many(twos, ALL_FLAGS);
+
+ if ((error = remove_redundant(walk, &redundant)) < 0) {
+ git_vector_free(&redundant);
+ return error;
+ }
+
+ git_vector_foreach(&redundant, i, two) {
+ if (two != NULL)
+ git_commit_list_insert_by_date(two, &result);
+ }
- git__free(tmp);
- tmp = next;
+ git_vector_free(&redundant);
}
*out = result;
diff --git a/src/odb.c b/src/odb.c
index b2d635109..805d2c333 100644
--- a/src/odb.c
+++ b/src/odb.c
@@ -374,10 +374,14 @@ static int backend_sort_cmp(const void *a, const void *b)
const backend_internal *backend_a = (const backend_internal *)(a);
const backend_internal *backend_b = (const backend_internal *)(b);
- if (backend_a->is_alternate == backend_b->is_alternate)
- return (backend_b->priority - backend_a->priority);
-
- return backend_a->is_alternate ? 1 : -1;
+ if (backend_b->priority == backend_a->priority) {
+ if (backend_a->is_alternate)
+ return -1;
+ if (backend_b->is_alternate)
+ return 1;
+ return 0;
+ }
+ return (backend_b->priority - backend_a->priority);
}
int git_odb_new(git_odb **out)
@@ -621,23 +625,18 @@ void git_odb_free(git_odb *db)
GIT_REFCOUNT_DEC(db, odb_free);
}
-int git_odb_exists(git_odb *db, const git_oid *id)
+static int odb_exists_1(git_odb *db, const git_oid *id, bool only_refreshed)
{
- git_odb_object *object;
size_t i;
bool found = false;
- assert(db && id);
-
- if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
- git_odb_object_free(object);
- return (int)true;
- }
-
for (i = 0; i < db->backends.length && !found; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
+ if (only_refreshed && !b->refresh)
+ continue;
+
if (b->exists != NULL)
found = (bool)b->exists(b, id);
}
@@ -645,43 +644,45 @@ int git_odb_exists(git_odb *db, const git_oid *id)
return (int)found;
}
-int git_odb_exists_prefix(
- git_oid *out, git_odb *db, const git_oid *short_id, size_t len)
+int git_odb_exists(git_odb *db, const git_oid *id)
{
- int error = GIT_ENOTFOUND, num_found = 0;
- size_t i;
- git_oid key = {{0}}, last_found = {{0}}, found;
-
- assert(db && short_id);
+ git_odb_object *object;
- if (len < GIT_OID_MINPREFIXLEN)
- return git_odb__error_ambiguous("prefix length too short");
- if (len > GIT_OID_HEXSZ)
- len = GIT_OID_HEXSZ;
+ assert(db && id);
- if (len == GIT_OID_HEXSZ) {
- if (git_odb_exists(db, short_id)) {
- if (out)
- git_oid_cpy(out, short_id);
- return 0;
- } else {
- return git_odb__error_notfound("no match for id prefix", short_id);
- }
+ if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
+ git_odb_object_free(object);
+ return (int)true;
}
- /* just copy valid part of short_id */
- memcpy(&key.id, short_id->id, (len + 1) / 2);
- if (len & 1)
- key.id[len / 2] &= 0xF0;
+ if (odb_exists_1(db, id, false))
+ return 1;
+
+ if (!git_odb_refresh(db))
+ return odb_exists_1(db, id, true);
+
+ /* Failed to refresh, hence not found */
+ return 0;
+}
+
+static int odb_exists_prefix_1(git_oid *out, git_odb *db,
+ const git_oid *key, size_t len, bool only_refreshed)
+{
+ size_t i;
+ int error = GIT_ENOTFOUND, num_found = 0;
+ git_oid last_found = {{0}}, found;
for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
+ if (only_refreshed && !b->refresh)
+ continue;
+
if (!b->exists_prefix)
continue;
- error = b->exists_prefix(&found, b, &key, len);
+ error = b->exists_prefix(&found, b, key, len);
if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
continue;
if (error)
@@ -698,13 +699,53 @@ int git_odb_exists_prefix(
}
if (!num_found)
- return git_odb__error_notfound("no match for id prefix", &key);
+ return GIT_ENOTFOUND;
+
if (out)
git_oid_cpy(out, &last_found);
return 0;
}
+int git_odb_exists_prefix(
+ git_oid *out, git_odb *db, const git_oid *short_id, size_t len)
+{
+ int error;
+ git_oid key = {{0}};
+
+ assert(db && short_id);
+
+ if (len < GIT_OID_MINPREFIXLEN)
+ return git_odb__error_ambiguous("prefix length too short");
+ if (len > GIT_OID_HEXSZ)
+ len = GIT_OID_HEXSZ;
+
+ if (len == GIT_OID_HEXSZ) {
+ if (git_odb_exists(db, short_id)) {
+ if (out)
+ git_oid_cpy(out, short_id);
+ return 0;
+ } else {
+ return git_odb__error_notfound("no match for id prefix", short_id);
+ }
+ }
+
+ /* just copy valid part of short_id */
+ memcpy(&key.id, short_id->id, (len + 1) / 2);
+ if (len & 1)
+ key.id[len / 2] &= 0xF0;
+
+ error = odb_exists_prefix_1(out, db, &key, len, false);
+
+ if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
+ error = odb_exists_prefix_1(out, db, &key, len, true);
+
+ if (error == GIT_ENOTFOUND)
+ return git_odb__error_notfound("no match for id prefix", &key);
+
+ return error;
+}
+
int git_odb_read_header(size_t *len_p, git_otype *type_p, git_odb *db, const git_oid *id)
{
int error;
@@ -784,36 +825,38 @@ static int hardcoded_objects(git_rawobj *raw, const git_oid *id)
}
}
-int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
+static int odb_read_1(git_odb_object **out, git_odb *db, const git_oid *id,
+ bool only_refreshed)
{
- size_t i, reads = 0;
- int error;
+ size_t i;
git_rawobj raw;
git_odb_object *object;
+ bool found = false;
- assert(out && db && id);
-
- *out = git_cache_get_raw(odb_cache(db), id);
- if (*out != NULL)
- return 0;
-
- error = hardcoded_objects(&raw, id);
+ if (!hardcoded_objects(&raw, id))
+ found = true;
- for (i = 0; i < db->backends.length && error < 0; ++i) {
+ for (i = 0; i < db->backends.length && !found; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
+ if (only_refreshed && !b->refresh)
+ continue;
+
if (b->read != NULL) {
- ++reads;
- error = b->read(&raw.data, &raw.len, &raw.type, b, id);
+ int error = b->read(&raw.data, &raw.len, &raw.type, b, id);
+ if (error == GIT_PASSTHROUGH || error == GIT_ENOTFOUND)
+ continue;
+
+ if (error < 0)
+ return error;
+
+ found = true;
}
}
- if (error && error != GIT_PASSTHROUGH) {
- if (!reads)
- return git_odb__error_notfound("no match for id", id);
- return error;
- }
+ if (!found)
+ return GIT_ENOTFOUND;
giterr_clear();
if ((object = odb_object__alloc(id, &raw)) == NULL)
@@ -823,42 +866,48 @@ int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
return 0;
}
-int git_odb_read_prefix(
- git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len)
+int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
+{
+ int error;
+
+ assert(out && db && id);
+
+ *out = git_cache_get_raw(odb_cache(db), id);
+ if (*out != NULL)
+ return 0;
+
+ error = odb_read_1(out, db, id, false);
+
+ if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
+ error = odb_read_1(out, db, id, true);
+
+ if (error == GIT_ENOTFOUND)
+ return git_odb__error_notfound("no match for id", id);
+
+ return error;
+}
+
+static int read_prefix_1(git_odb_object **out, git_odb *db,
+ const git_oid *key, size_t len, bool only_refreshed)
{
size_t i;
int error = GIT_ENOTFOUND;
- git_oid key = {{0}}, found_full_oid = {{0}};
+ git_oid found_full_oid = {{0}};
git_rawobj raw;
void *data = NULL;
bool found = false;
git_odb_object *object;
- assert(out && db);
-
- if (len < GIT_OID_MINPREFIXLEN)
- return git_odb__error_ambiguous("prefix length too short");
- if (len > GIT_OID_HEXSZ)
- len = GIT_OID_HEXSZ;
-
- if (len == GIT_OID_HEXSZ) {
- *out = git_cache_get_raw(odb_cache(db), short_id);
- if (*out != NULL)
- return 0;
- }
-
- /* just copy valid part of short_id */
- memcpy(&key.id, short_id->id, (len + 1) / 2);
- if (len & 1)
- key.id[len / 2] &= 0xF0;
-
for (i = 0; i < db->backends.length; ++i) {
backend_internal *internal = git_vector_get(&db->backends, i);
git_odb_backend *b = internal->backend;
+ if (only_refreshed && !b->refresh)
+ continue;
+
if (b->read_prefix != NULL) {
git_oid full_oid;
- error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, &key, len);
+ error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, key, len);
if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
continue;
@@ -879,7 +928,7 @@ int git_odb_read_prefix(
}
if (!found)
- return git_odb__error_notfound("no match for prefix", &key);
+ return GIT_ENOTFOUND;
if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL)
return -1;
@@ -888,6 +937,42 @@ int git_odb_read_prefix(
return 0;
}
+int git_odb_read_prefix(
+ git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len)
+{
+ git_oid key = {{0}};
+ int error;
+
+ assert(out && db);
+
+ if (len < GIT_OID_MINPREFIXLEN)
+ return git_odb__error_ambiguous("prefix length too short");
+
+ if (len > GIT_OID_HEXSZ)
+ len = GIT_OID_HEXSZ;
+
+ if (len == GIT_OID_HEXSZ) {
+ *out = git_cache_get_raw(odb_cache(db), short_id);
+ if (*out != NULL)
+ return 0;
+ }
+
+ /* just copy valid part of short_id */
+ memcpy(&key.id, short_id->id, (len + 1) / 2);
+ if (len & 1)
+ key.id[len / 2] &= 0xF0;
+
+ error = read_prefix_1(out, db, &key, len, false);
+
+ if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
+ error = read_prefix_1(out, db, &key, len, true);
+
+ if (error == GIT_ENOTFOUND)
+ return git_odb__error_notfound("no match for prefix", &key);
+
+ return error;
+}
+
int git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload)
{
unsigned int i;
diff --git a/src/odb_mempack.c b/src/odb_mempack.c
index 34355270f..25f30590c 100644
--- a/src/odb_mempack.c
+++ b/src/odb_mempack.c
@@ -154,12 +154,19 @@ void git_mempack_reset(git_odb_backend *_backend)
});
git_array_clear(db->commits);
+
+ git_oidmap_free(db->objects);
+ db->objects = git_oidmap_alloc();
}
static void impl__free(git_odb_backend *_backend)
{
- git_mempack_reset(_backend);
- git__free(_backend);
+ struct memory_packer_db *db = (struct memory_packer_db *)_backend;
+
+ git_mempack_reset(db);
+ git_oidmap_free(db->objects);
+
+ git__free(db);
}
int git_mempack_new(git_odb_backend **out)
diff --git a/src/odb_pack.c b/src/odb_pack.c
index 735158d96..77d2c75b9 100644
--- a/src/odb_pack.c
+++ b/src/odb_pack.c
@@ -346,7 +346,7 @@ static int pack_backend__refresh(git_odb_backend *backend_)
return error;
}
-static int pack_backend__read_header_internal(
+static int pack_backend__read_header(
size_t *len_p, git_otype *type_p,
struct git_odb_backend *backend, const git_oid *oid)
{
@@ -361,24 +361,7 @@ static int pack_backend__read_header_internal(
return git_packfile_resolve_header(len_p, type_p, e.p, e.offset);
}
-static int pack_backend__read_header(
- size_t *len_p, git_otype *type_p,
- struct git_odb_backend *backend, const git_oid *oid)
-{
- int error;
-
- error = pack_backend__read_header_internal(len_p, type_p, backend, oid);
-
- if (error != GIT_ENOTFOUND)
- return error;
-
- if ((error = pack_backend__refresh(backend)) < 0)
- return error;
-
- return pack_backend__read_header_internal(len_p, type_p, backend, oid);
-}
-
-static int pack_backend__read_internal(
+static int pack_backend__read(
void **buffer_p, size_t *len_p, git_otype *type_p,
git_odb_backend *backend, const git_oid *oid)
{
@@ -397,24 +380,7 @@ static int pack_backend__read_internal(
return 0;
}
-static int pack_backend__read(
- void **buffer_p, size_t *len_p, git_otype *type_p,
- git_odb_backend *backend, const git_oid *oid)
-{
- int error;
-
- error = pack_backend__read_internal(buffer_p, len_p, type_p, backend, oid);
-
- if (error != GIT_ENOTFOUND)
- return error;
-
- if ((error = pack_backend__refresh(backend)) < 0)
- return error;
-
- return pack_backend__read_internal(buffer_p, len_p, type_p, backend, oid);
-}
-
-static int pack_backend__read_prefix_internal(
+static int pack_backend__read_prefix(
git_oid *out_oid,
void **buffer_p,
size_t *len_p,
@@ -451,45 +417,9 @@ static int pack_backend__read_prefix_internal(
return error;
}
-static int pack_backend__read_prefix(
- git_oid *out_oid,
- void **buffer_p,
- size_t *len_p,
- git_otype *type_p,
- git_odb_backend *backend,
- const git_oid *short_oid,
- size_t len)
-{
- int error;
-
- error = pack_backend__read_prefix_internal(
- out_oid, buffer_p, len_p, type_p, backend, short_oid, len);
-
- if (error != GIT_ENOTFOUND)
- return error;
-
- if ((error = pack_backend__refresh(backend)) < 0)
- return error;
-
- return pack_backend__read_prefix_internal(
- out_oid, buffer_p, len_p, type_p, backend, short_oid, len);
-}
-
static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
{
struct git_pack_entry e;
- int error;
-
- error = pack_entry_find(&e, (struct pack_backend *)backend, oid);
-
- if (error != GIT_ENOTFOUND)
- return error == 0;
-
- if ((error = pack_backend__refresh(backend)) < 0) {
- giterr_clear();
- return (int)false;
- }
-
return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0;
}
@@ -501,12 +431,7 @@ static int pack_backend__exists_prefix(
struct git_pack_entry e = {0};
error = pack_entry_find_prefix(&e, pb, short_id, len);
-
- if (error == GIT_ENOTFOUND && !(error = pack_backend__refresh(backend)))
- error = pack_entry_find_prefix(&e, pb, short_id, len);
-
git_oid_cpy(out, &e.sha1);
-
return error;
}
@@ -674,7 +599,6 @@ int git_odb_backend_pack(git_odb_backend **backend_out, const char *objects_dir)
git_path_isdir(git_buf_cstr(&path)))
{
backend->pack_folder = git_buf_detach(&path);
-
error = pack_backend__refresh((git_odb_backend *)backend);
}
diff --git a/src/openssl_stream.c b/src/openssl_stream.c
index 4df7c6b7c..16ee78341 100644
--- a/src/openssl_stream.c
+++ b/src/openssl_stream.c
@@ -302,6 +302,7 @@ cert_fail_name:
typedef struct {
git_stream parent;
git_stream *io;
+ bool connected;
char *host;
SSL *ssl;
git_cert_x509 cert_info;
@@ -318,6 +319,8 @@ int openssl_connect(git_stream *stream)
if ((ret = git_stream_connect(st->io)) < 0)
return ret;
+ st->connected = true;
+
bio = BIO_new(&git_stream_bio_method);
GITERR_CHECK_ALLOC(bio);
bio->ptr = st->io;
@@ -405,9 +408,11 @@ int openssl_close(git_stream *stream)
openssl_stream *st = (openssl_stream *) stream;
int ret;
- if ((ret = ssl_teardown(st->ssl)) < 0)
+ if (st->connected && (ret = ssl_teardown(st->ssl)) < 0)
return -1;
+ st->connected = false;
+
return git_stream_close(st->io);
}
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index e1a77f3ff..792e4bb0a 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -1453,7 +1453,7 @@ static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
entry = git__calloc(1, sizeof(git_reflog_entry));
GITERR_CHECK_ALLOC(entry);
- entry->committer = git__malloc(sizeof(git_signature));
+ entry->committer = git__calloc(1, sizeof(git_signature));
GITERR_CHECK_ALLOC(entry->committer);
if (git_oid_fromstrn(&entry->oid_old, buf, GIT_OID_HEXSZ) < 0)
diff --git a/src/repository.c b/src/repository.c
index 08f4baa20..3476ccadc 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -908,12 +908,28 @@ bool git_repository__reserved_names(
buf->size = git_repository__reserved_names_win32[i].size;
}
- /* Try to add any repo-specific reserved names */
+ /* Try to add any repo-specific reserved names - the gitlink file
+ * within a submodule or the repository (if the repository directory
+ * is beneath the workdir). These are typically `.git`, but should
+ * be protected in case they are not. Note, repo and workdir paths
+ * are always prettified to end in `/`, so a prefixcmp is safe.
+ */
if (!repo->is_bare) {
- const char *reserved_path = repo->path_gitlink ?
- repo->path_gitlink : repo->path_repository;
+ int (*prefixcmp)(const char *, const char *);
+ int error, ignorecase;
- if (reserved_names_add8dot3(repo, reserved_path) < 0)
+ error = git_repository__cvar(
+ &ignorecase, repo, GIT_CVAR_IGNORECASE);
+ prefixcmp = (error || ignorecase) ? git__prefixcmp_icase :
+ git__prefixcmp;
+
+ if (repo->path_gitlink &&
+ reserved_names_add8dot3(repo, repo->path_gitlink) < 0)
+ goto on_error;
+
+ if (repo->path_repository &&
+ prefixcmp(repo->path_repository, repo->workdir) == 0 &&
+ reserved_names_add8dot3(repo, repo->path_repository) < 0)
goto on_error;
}
}
diff --git a/src/signature.c b/src/signature.c
index 818cd300e..109476efe 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -34,13 +34,27 @@ static bool contains_angle_brackets(const char *input)
return strchr(input, '<') != NULL || strchr(input, '>') != NULL;
}
+static bool is_crud(unsigned char c)
+{
+ return c <= 32 ||
+ c == '.' ||
+ c == ',' ||
+ c == ':' ||
+ c == ';' ||
+ c == '<' ||
+ c == '>' ||
+ c == '"' ||
+ c == '\\' ||
+ c == '\'';
+}
+
static char *extract_trimmed(const char *ptr, size_t len)
{
- while (len && git__isspace(ptr[0])) {
+ while (len && is_crud((unsigned char)ptr[0])) {
ptr++; len--;
}
- while (len && git__isspace(ptr[len - 1])) {
+ while (len && is_crud((unsigned char)ptr[len - 1])) {
len--;
}
diff --git a/src/transports/git.c b/src/transports/git.c
index 52de92d09..6c6acf9c5 100644
--- a/src/transports/git.c
+++ b/src/transports/git.c
@@ -130,11 +130,14 @@ static int git_proto_stream_write(
static void git_proto_stream_free(git_smart_subtransport_stream *stream)
{
- git_proto_stream *s = (git_proto_stream *)stream;
- git_subtransport *t = OWNING_SUBTRANSPORT(s);
- int ret;
+ git_proto_stream *s;
+ git_subtransport *t;
+
+ if (!stream)
+ return;
- GIT_UNUSED(ret);
+ s = (git_proto_stream *)stream;
+ t = OWNING_SUBTRANSPORT(s);
t->current_stream = NULL;
diff --git a/src/transports/ssh.c b/src/transports/ssh.c
index e3792ebb3..250e588e7 100644
--- a/src/transports/ssh.c
+++ b/src/transports/ssh.c
@@ -756,8 +756,10 @@ static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *use
list = libssh2_userauth_list(session, username, strlen(username));
/* either error, or the remote accepts NONE auth, which is bizarre, let's punt */
- if (list == NULL && !libssh2_userauth_authenticated(session))
+ if (list == NULL && !libssh2_userauth_authenticated(session)) {
+ ssh_error(session, "Failed to retrieve list of SSH authentication methods");
return -1;
+ }
ptr = list;
while (ptr) {
diff --git a/src/unix/posix.h b/src/unix/posix.h
index 777350990..6633689bc 100644
--- a/src/unix/posix.h
+++ b/src/unix/posix.h
@@ -22,7 +22,6 @@ typedef int GIT_SOCKET;
#define p_stat(p,b) stat(p, b)
#define p_utimes(f, t) utimes(f, t)
-#define p_futimes(f, t) futimes(f, t)
#define p_readlink(a, b, c) readlink(a, b, c)
#define p_symlink(o,n) symlink(o, n)
@@ -53,4 +52,18 @@ extern char *p_realpath(const char *, char *);
#define p_localtime_r(c, r) localtime_r(c, r)
#define p_gmtime_r(c, r) gmtime_r(c, r)
+#ifdef HAVE_FUTIMENS
+GIT_INLINE(int) p_futimes(int f, const struct timeval t[2])
+{
+ struct timespec s[2];
+ s[0].tv_sec = t[0].tv_sec;
+ s[0].tv_nsec = t[0].tv_usec * 1000;
+ s[1].tv_sec = t[1].tv_sec;
+ s[1].tv_nsec = t[1].tv_usec * 1000;
+ return futimens(f, s);
+}
+#else
+# define p_futimes futimes
+#endif
+
#endif
diff --git a/src/util.c b/src/util.c
index c62826420..49d491dd3 100644
--- a/src/util.c
+++ b/src/util.c
@@ -607,7 +607,7 @@ size_t git__unescape(char *str)
return (pos - str);
}
-#if defined(GIT_WIN32) || defined(BSD)
+#if defined(HAVE_QSORT_S) || (defined(HAVE_QSORT_R) && defined(BSD))
typedef struct {
git__sort_r_cmp cmp;
void *payload;
@@ -624,21 +624,16 @@ static int GIT_STDLIB_CALL git__qsort_r_glue_cmp(
void git__qsort_r(
void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload)
{
-#if defined(__MINGW32__) || defined(AMIGA) || \
- defined(__OpenBSD__) || defined(__NetBSD__) || \
- defined(__gnu_hurd__) || defined(__ANDROID_API__) || \
- defined(__sun) || defined(__CYGWIN__) || \
- (__GLIBC__ == 2 && __GLIBC_MINOR__ < 8) || \
- (defined(_MSC_VER) && _MSC_VER < 1500)
- git__insertsort_r(els, nel, elsize, NULL, cmp, payload);
-#elif defined(GIT_WIN32)
- git__qsort_r_glue glue = { cmp, payload };
- qsort_s(els, nel, elsize, git__qsort_r_glue_cmp, &glue);
-#elif defined(BSD)
+#if defined(HAVE_QSORT_R) && defined(BSD)
git__qsort_r_glue glue = { cmp, payload };
qsort_r(els, nel, elsize, &glue, git__qsort_r_glue_cmp);
-#else
+#elif defined(HAVE_QSORT_R) && defined(__GLIBC__)
qsort_r(els, nel, elsize, cmp, payload);
+#elif defined(HAVE_QSORT_S)
+ git__qsort_r_glue glue = { cmp, payload };
+ qsort_s(els, nel, elsize, git__qsort_r_glue_cmp, &glue);
+#else
+ git__insertsort_r(els, nel, elsize, NULL, cmp, payload);
#endif
}
diff --git a/src/win32/path_w32.c b/src/win32/path_w32.c
index 118e8bcc5..40b95c33b 100644
--- a/src/win32/path_w32.c
+++ b/src/win32/path_w32.c
@@ -198,13 +198,13 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
/* See if this is an absolute path (beginning with a drive letter) */
if (path__is_absolute(src)) {
if (git__utf8_to_16(dest, MAX_PATH, src) < 0)
- return -1;
+ goto on_error;
}
/* File-prefixed NT-style paths beginning with \\?\ */
else if (path__is_nt_namespace(src)) {
/* Skip the NT prefix, the destination already contains it */
if (git__utf8_to_16(dest, MAX_PATH, src + PATH__NT_NAMESPACE_LEN) < 0)
- return -1;
+ goto on_error;
}
/* UNC paths */
else if (path__is_unc(src)) {
@@ -213,36 +213,43 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
/* Skip the leading "\\" */
if (git__utf8_to_16(dest, MAX_PATH - 2, src + 2) < 0)
- return -1;
+ goto on_error;
}
/* Absolute paths omitting the drive letter */
else if (src[0] == '\\' || src[0] == '/') {
if (path__cwd(dest, MAX_PATH) < 0)
- return -1;
+ goto on_error;
if (!path__is_absolute(dest)) {
errno = ENOENT;
- return -1;
+ goto on_error;
}
/* Skip the drive letter specification ("C:") */
if (git__utf8_to_16(dest + 2, MAX_PATH - 2, src) < 0)
- return -1;
+ goto on_error;
}
/* Relative paths */
else {
int cwd_len;
if ((cwd_len = git_win32_path__cwd(dest, MAX_PATH)) < 0)
- return -1;
+ goto on_error;
dest[cwd_len++] = L'\\';
if (git__utf8_to_16(dest + cwd_len, MAX_PATH - cwd_len, src) < 0)
- return -1;
+ goto on_error;
}
return git_win32_path_canonicalize(out);
+
+on_error:
+ /* set windows error code so we can use its error message */
+ if (errno == ENAMETOOLONG)
+ SetLastError(ERROR_FILENAME_EXCED_RANGE);
+
+ return -1;
}
int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src)
diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c
index 504562b0e..e7aa6fc7c 100644
--- a/src/win32/posix_w32.c
+++ b/src/win32/posix_w32.c
@@ -146,12 +146,19 @@ static int lstat_w(
return git_win32__file_attribute_to_stat(buf, &fdata, path);
}
- errno = ENOENT;
+ switch (GetLastError()) {
+ case ERROR_ACCESS_DENIED:
+ errno = EACCES;
+ break;
+ default:
+ errno = ENOENT;
+ break;
+ }
/* 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) {
+ if (errno == ENOENT && posix_enotdir) {
size_t path_len = wcslen(path);
/* scan up path until we find an existing item */
diff --git a/src/xdiff/xdiff.h b/src/xdiff/xdiff.h
index db5d59884..f08f72e16 100644
--- a/src/xdiff/xdiff.h
+++ b/src/xdiff/xdiff.h
@@ -20,7 +20,7 @@
*
*/
-#include "util.h"
+#include "../util.h"
#if !defined(XDIFF_H)
#define XDIFF_H
diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c
index be4019822..5680b86df 100644
--- a/tests/checkout/tree.c
+++ b/tests/checkout/tree.c
@@ -946,7 +946,7 @@ void test_checkout_tree__filemode_preserved_in_index(void)
cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
cl_assert(entry = git_index_get_bypath(index, "executable.txt", 0));
- cl_assert_equal_i(0100755, entry->mode);
+ cl_assert(GIT_PERMS_IS_EXEC(entry->mode));
git_commit_free(commit);
@@ -957,7 +957,7 @@ void test_checkout_tree__filemode_preserved_in_index(void)
cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
- cl_assert_equal_i(0100644, entry->mode);
+ cl_assert(!GIT_PERMS_IS_EXEC(entry->mode));
git_commit_free(commit);
@@ -968,7 +968,18 @@ void test_checkout_tree__filemode_preserved_in_index(void)
cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
- cl_assert_equal_i(0100755, entry->mode);
+ cl_assert(GIT_PERMS_IS_EXEC(entry->mode));
+
+ git_commit_free(commit);
+
+
+ /* Finally, check out the text file again and check that the exec bit is cleared */
+ cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
+ cl_assert(!GIT_PERMS_IS_EXEC(entry->mode));
git_commit_free(commit);
@@ -976,6 +987,73 @@ void test_checkout_tree__filemode_preserved_in_index(void)
git_index_free(index);
}
+mode_t read_filemode(const char *path)
+{
+ git_buf fullpath = GIT_BUF_INIT;
+ struct stat st;
+ mode_t result;
+
+ git_buf_joinpath(&fullpath, "testrepo", path);
+ cl_must_pass(p_stat(fullpath.ptr, &st));
+
+ result = GIT_PERMS_IS_EXEC(st.st_mode) ?
+ GIT_FILEMODE_BLOB_EXECUTABLE : GIT_FILEMODE_BLOB;
+
+ git_buf_free(&fullpath);
+
+ return result;
+}
+
+void test_checkout_tree__filemode_preserved_in_workdir(void)
+{
+#ifndef GIT_WIN32
+ git_oid executable_oid;
+ git_commit *commit;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ /* test a freshly added executable */
+ cl_git_pass(git_oid_fromstr(&executable_oid, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(GIT_PERMS_IS_EXEC(read_filemode("executable.txt")));
+
+ git_commit_free(commit);
+
+
+ /* Now start with a commit which has a text file */
+ cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(!GIT_PERMS_IS_EXEC(read_filemode("a/b.txt")));
+
+ git_commit_free(commit);
+
+
+ /* And then check out to a commit which converts the text file to an executable */
+ cl_git_pass(git_oid_fromstr(&executable_oid, "144344043ba4d4a405da03de3844aa829ae8be0e"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(GIT_PERMS_IS_EXEC(read_filemode("a/b.txt")));
+
+ git_commit_free(commit);
+
+
+ /* Finally, check out the text file again and check that the exec bit is cleared */
+ cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(!GIT_PERMS_IS_EXEC(read_filemode("a/b.txt")));
+
+ git_commit_free(commit);
+#endif
+}
+
void test_checkout_tree__removes_conflicts(void)
{
git_oid commit_id;
diff --git a/tests/commit/signature.c b/tests/commit/signature.c
index 41a74b999..0070320ae 100644
--- a/tests/commit/signature.c
+++ b/tests/commit/signature.c
@@ -35,6 +35,13 @@ void test_commit_signature__leading_and_trailing_spaces_are_trimmed(void)
assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", " \t nulltoken \n", " \n emeric.fermas@gmail.com \n");
}
+void test_commit_signature__leading_and_trailing_crud_is_trimmed(void)
+{
+ assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", "\"nulltoken\"", "\"emeric.fermas@gmail.com\"");
+ assert_name_and_email("nulltoken w", "emeric.fermas@gmail.com", "nulltoken w.", "emeric.fermas@gmail.com");
+ assert_name_and_email("nulltoken \xe2\x98\xba", "emeric.fermas@gmail.com", "nulltoken \xe2\x98\xba", "emeric.fermas@gmail.com");
+}
+
void test_commit_signature__angle_brackets_in_names_are_not_supported(void)
{
cl_git_fail(try_build_signature("<Phil Haack", "phil@haack", 1234567890, 60));
diff --git a/tests/core/filebuf.c b/tests/core/filebuf.c
index 3f7dc8569..39d98ff7e 100644
--- a/tests/core/filebuf.c
+++ b/tests/core/filebuf.c
@@ -151,3 +151,56 @@ void test_core_filebuf__rename_error(void)
cl_assert_equal_i(false, git_path_exists(test_lock));
}
+
+void test_core_filebuf__symlink_follow(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ const char *dir = "linkdir", *source = "linkdir/link";
+
+#ifdef GIT_WIN32
+ cl_skip();
+#endif
+
+ cl_git_pass(p_mkdir(dir, 0777));
+ cl_git_pass(p_symlink("target", source));
+
+ cl_git_pass(git_filebuf_open(&file, source, 0, 0666));
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+
+ cl_assert_equal_i(true, git_path_exists("linkdir/target.lock"));
+
+ cl_git_pass(git_filebuf_commit(&file));
+ cl_assert_equal_i(true, git_path_exists("linkdir/target"));
+
+ git_filebuf_cleanup(&file);
+
+ /* The second time around, the target file does exist */
+ cl_git_pass(git_filebuf_open(&file, source, 0, 0666));
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+
+ cl_assert_equal_i(true, git_path_exists("linkdir/target.lock"));
+
+ cl_git_pass(git_filebuf_commit(&file));
+ cl_assert_equal_i(true, git_path_exists("linkdir/target"));
+
+ git_filebuf_cleanup(&file);
+ cl_git_pass(git_futils_rmdir_r(dir, NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_core_filebuf__symlink_depth(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ const char *dir = "linkdir", *source = "linkdir/link";
+
+#ifdef GIT_WIN32
+ cl_skip();
+#endif
+
+ cl_git_pass(p_mkdir(dir, 0777));
+ /* Endless loop */
+ cl_git_pass(p_symlink("link", source));
+
+ cl_git_fail(git_filebuf_open(&file, source, 0, 0666));
+
+ cl_git_pass(git_futils_rmdir_r(dir, NULL, GIT_RMDIR_REMOVE_FILES));
+}
diff --git a/tests/diff/binary.c b/tests/diff/binary.c
index 5298e9ebb..173a5994e 100644
--- a/tests/diff/binary.c
+++ b/tests/diff/binary.c
@@ -96,7 +96,8 @@ void test_diff_binary__add(void)
"Kc${Nk-~s>u4FC%O\n"
"\n" \
"literal 0\n" \
- "Hc$@<O00001\n";
+ "Hc$@<O00001\n" \
+ "\n";
opts.flags = GIT_DIFF_SHOW_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
@@ -136,7 +137,8 @@ void test_diff_binary__modify(void)
"Mc${NkU}WL~000&M4gdfE\n" \
"\n" \
"literal 3\n" \
- "Kc${Nk-~s>u4FC%O\n";
+ "Kc${Nk-~s>u4FC%O\n" \
+ "\n";
opts.flags = GIT_DIFF_SHOW_BINARY;
@@ -177,7 +179,8 @@ void test_diff_binary__delete(void)
"Hc$@<O00001\n" \
"\n" \
"literal 3\n" \
- "Kc${Nk-~s>u4FC%O\n";
+ "Kc${Nk-~s>u4FC%O\n" \
+ "\n";
opts.flags = GIT_DIFF_SHOW_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
@@ -208,7 +211,8 @@ void test_diff_binary__delta(void)
"delta 198\n" \
"zc$}LmI8{(0BqLQJI6p64AwNwaIJGP_Pr*5}Br~;mqJ$<Jl;sX*mF<MGCYv&*L7AHu\n" \
"zGA1*^gt?gYVN82wTbPO_W)+x<&1+cP;HrPHR>PQ;Y(X&QMK*C5^Br3bjG4d=XI^5@\n" \
- "JfH567LIF3FM2!Fd\n";
+ "JfH567LIF3FM2!Fd\n" \
+ "\n";
opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
@@ -249,7 +253,8 @@ void test_diff_binary__delta_append(void)
"nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \
"\n" \
"delta 7\n" \
- "Oc%18D`@*{63ljhg(E~C7\n";
+ "Oc%18D`@*{63ljhg(E~C7\n" \
+ "\n";
opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
@@ -314,7 +319,8 @@ void test_diff_binary__index_to_workdir(void)
"nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \
"\n" \
"delta 7\n" \
- "Oc%18D`@*{63ljhg(E~C7\n";
+ "Oc%18D`@*{63ljhg(E~C7\n" \
+ "\n";
opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
@@ -379,7 +385,8 @@ void test_diff_binary__print_patch_from_diff(void)
"nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \
"\n" \
"delta 7\n" \
- "Oc%18D`@*{63ljhg(E~C7\n";
+ "Oc%18D`@*{63ljhg(E~C7\n" \
+ "\n";
opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
opts.id_abbrev = GIT_OID_HEXSZ;
diff --git a/tests/index/nsec.c b/tests/index/nsec.c
new file mode 100644
index 000000000..5004339f0
--- /dev/null
+++ b/tests/index/nsec.c
@@ -0,0 +1,78 @@
+#include "clar_libgit2.h"
+#include "index.h"
+#include "git2/sys/index.h"
+#include "git2/repository.h"
+#include "../reset/reset_helpers.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "nsecs"
+
+// Fixture setup and teardown
+void test_index_nsec__initialize(void)
+{
+ repo = cl_git_sandbox_init("nsecs");
+ git_repository_index(&repo_index, repo);
+}
+
+void test_index_nsec__cleanup(void)
+{
+ git_index_free(repo_index);
+ repo_index = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+static bool has_nsecs(void)
+{
+ const git_index_entry *entry;
+ size_t i;
+ bool has_nsecs = false;
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ entry = git_index_get_byindex(repo_index, i);
+
+ if (entry->ctime.nanoseconds || entry->mtime.nanoseconds) {
+ has_nsecs = true;
+ break;
+ }
+ }
+
+ return has_nsecs;
+}
+
+void test_index_nsec__has_nanos(void)
+{
+ cl_assert_equal_b(true, has_nsecs());
+}
+
+void test_index_nsec__staging_maintains_other_nanos(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_rewritefile("nsecs/a.txt", "This is file A");
+ cl_git_pass(git_index_add_bypath(repo_index, "a.txt"));
+ cl_git_pass(git_index_write(repo_index));
+
+ cl_git_pass(git_index_write(repo_index));
+
+ git_index_read(repo_index, 1);
+ cl_assert_equal_b(true, has_nsecs());
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "a.txt", 0)));
+ cl_assert_equal_i(0, entry->ctime.nanoseconds);
+ cl_assert_equal_i(0, entry->mtime.nanoseconds);
+}
+
+void test_index_nsec__status_doesnt_clear_nsecs(void)
+{
+ git_status_list *statuslist;
+
+ cl_git_pass(git_status_list_new(&statuslist, repo, NULL));
+
+ git_index_read(repo_index, 1);
+ cl_assert_equal_b(true, has_nsecs());
+
+ git_status_list_free(statuslist);
+}
diff --git a/tests/odb/sorting.c b/tests/odb/sorting.c
index 147a160c8..22d057b3b 100644
--- a/tests/odb/sorting.c
+++ b/tests/odb/sorting.c
@@ -56,14 +56,14 @@ void test_odb_sorting__basic_backends_sorting(void)
void test_odb_sorting__alternate_backends_sorting(void)
{
- cl_git_pass(git_odb_add_backend(_odb, new_backend(0), 5));
- cl_git_pass(git_odb_add_backend(_odb, new_backend(2), 3));
- cl_git_pass(git_odb_add_backend(_odb, new_backend(1), 4));
- cl_git_pass(git_odb_add_backend(_odb, new_backend(3), 1));
- cl_git_pass(git_odb_add_alternate(_odb, new_backend(4), 5));
- cl_git_pass(git_odb_add_alternate(_odb, new_backend(6), 3));
- cl_git_pass(git_odb_add_alternate(_odb, new_backend(5), 4));
- cl_git_pass(git_odb_add_alternate(_odb, new_backend(7), 1));
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(1), 5));
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(5), 3));
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(3), 4));
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(7), 1));
+ cl_git_pass(git_odb_add_alternate(_odb, new_backend(0), 5));
+ cl_git_pass(git_odb_add_alternate(_odb, new_backend(4), 3));
+ cl_git_pass(git_odb_add_alternate(_odb, new_backend(2), 4));
+ cl_git_pass(git_odb_add_alternate(_odb, new_backend(6), 1));
check_backend_sorting(_odb);
}
diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c
index 56ec422c3..3fbf412e4 100644
--- a/tests/refs/reflog/reflog.c
+++ b/tests/refs/reflog/reflog.c
@@ -154,6 +154,49 @@ void test_refs_reflog_reflog__reading_the_reflog_from_a_reference_with_no_log_re
git_buf_free(&subtrees_log_path);
}
+void test_refs_reflog_reflog__reading_a_reflog_with_invalid_format_returns_error(void)
+{
+ git_reflog *reflog;
+ const git_error *error;
+ const char *refname = "refs/heads/newline";
+ const char *refmessage =
+ "Reflog*message with a newline and enough content after it to pass the GIT_REFLOG_SIZE_MIN check inside reflog_parse.";
+ git_reference *ref;
+ git_oid id;
+ git_buf logpath = GIT_BUF_INIT, logcontents = GIT_BUF_INIT;
+ char *star;
+
+ git_oid_fromstr(&id, current_master_tip);
+
+ /* create a new branch */
+ cl_git_pass(git_reference_create(&ref, g_repo, refname, &id, 1, refmessage));
+
+ /* corrupt the branch reflog by introducing a newline inside the reflog message (we replace '*' with '\n') */
+ git_buf_join_n(&logpath, '/', 3, git_repository_path(g_repo), GIT_REFLOG_DIR, refname);
+ cl_git_pass(git_futils_readbuffer(&logcontents, git_buf_cstr(&logpath)));
+ cl_assert((star = strchr(git_buf_cstr(&logcontents), '*')) != NULL);
+ *star = '\n';
+ cl_git_rewritefile(git_buf_cstr(&logpath), git_buf_cstr(&logcontents));
+
+ /* confirm that the file was rewritten successfully and now contains a '\n' in the expected location */
+ cl_git_pass(git_futils_readbuffer(&logcontents, git_buf_cstr(&logpath)));
+ cl_assert(strstr(git_buf_cstr(&logcontents), "Reflog\nmessage") != NULL);
+
+ /* clear the error state so we can capture the error generated by git_reflog_read */
+ giterr_clear();
+
+ cl_git_fail(git_reflog_read(&reflog, g_repo, refname));
+
+ error = giterr_last();
+
+ cl_assert(error != NULL);
+ cl_assert_equal_s("Unable to parse OID - contains invalid characters", error->message);
+
+ git_reference_free(ref);
+ git_buf_free(&logpath);
+ git_buf_free(&logcontents);
+}
+
void test_refs_reflog_reflog__cannot_write_a_moved_reflog(void)
{
git_reference *master, *new_master;
diff --git a/tests/resources/nsecs/.gitted/HEAD b/tests/resources/nsecs/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/nsecs/.gitted/config b/tests/resources/nsecs/.gitted/config
new file mode 100644
index 000000000..78387c50b
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
diff --git a/tests/resources/nsecs/.gitted/index b/tests/resources/nsecs/.gitted/index
new file mode 100644
index 000000000..9233f1b11
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/index
Binary files differ
diff --git a/tests/resources/nsecs/.gitted/objects/03/1986a8372d1442cfe9e3b54906a9aadc524a7e b/tests/resources/nsecs/.gitted/objects/03/1986a8372d1442cfe9e3b54906a9aadc524a7e
new file mode 100644
index 000000000..a813b7424
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/objects/03/1986a8372d1442cfe9e3b54906a9aadc524a7e
@@ -0,0 +1,2 @@
+xA
+0D]J4DMu-/"FHFf P aʠZ rnX*4kUixK-#%y Z20;Џ; ŰJZ7FRBy?g?<^@]f˔GvܵNUOKv \ No newline at end of file
diff --git a/tests/resources/nsecs/.gitted/objects/03/9afd91c98f82c14e425bb6796d8ca98e9c8cac b/tests/resources/nsecs/.gitted/objects/03/9afd91c98f82c14e425bb6796d8ca98e9c8cac
new file mode 100644
index 000000000..74bb7d3fe
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/objects/03/9afd91c98f82c14e425bb6796d8ca98e9c8cac
Binary files differ
diff --git a/tests/resources/nsecs/.gitted/objects/6d/8b18077cc99abd8dda05a6062c646406abb2d4 b/tests/resources/nsecs/.gitted/objects/6d/8b18077cc99abd8dda05a6062c646406abb2d4
new file mode 100644
index 000000000..7bf3a956c
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/objects/6d/8b18077cc99abd8dda05a6062c646406abb2d4
Binary files differ
diff --git a/tests/resources/nsecs/.gitted/objects/c5/12b6c64656b87ea8caf37a32bc5a562d797745 b/tests/resources/nsecs/.gitted/objects/c5/12b6c64656b87ea8caf37a32bc5a562d797745
new file mode 100644
index 000000000..dcf4c8ccb
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/objects/c5/12b6c64656b87ea8caf37a32bc5a562d797745
Binary files differ
diff --git a/tests/resources/nsecs/.gitted/objects/df/78d3d51c369e1d2f1eadb73464aadd931d56b4 b/tests/resources/nsecs/.gitted/objects/df/78d3d51c369e1d2f1eadb73464aadd931d56b4
new file mode 100644
index 000000000..df45d3314
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/objects/df/78d3d51c369e1d2f1eadb73464aadd931d56b4
Binary files differ
diff --git a/tests/resources/nsecs/.gitted/refs/heads/master b/tests/resources/nsecs/.gitted/refs/heads/master
new file mode 100644
index 000000000..3dda65b65
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/refs/heads/master
@@ -0,0 +1 @@
+031986a8372d1442cfe9e3b54906a9aadc524a7e
diff --git a/tests/resources/nsecs/a.txt b/tests/resources/nsecs/a.txt
new file mode 100644
index 000000000..be4c1ee68
--- /dev/null
+++ b/tests/resources/nsecs/a.txt
@@ -0,0 +1 @@
+File A
diff --git a/tests/resources/nsecs/b.txt b/tests/resources/nsecs/b.txt
new file mode 100644
index 000000000..19a0af40f
--- /dev/null
+++ b/tests/resources/nsecs/b.txt
@@ -0,0 +1 @@
+File B
diff --git a/tests/resources/nsecs/c.txt b/tests/resources/nsecs/c.txt
new file mode 100644
index 000000000..31d776008
--- /dev/null
+++ b/tests/resources/nsecs/c.txt
@@ -0,0 +1 @@
+File C
diff --git a/tests/resources/redundant.git/HEAD b/tests/resources/redundant.git/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/redundant.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/redundant.git/config b/tests/resources/redundant.git/config
new file mode 100755
index 000000000..2f8958058
--- /dev/null
+++ b/tests/resources/redundant.git/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ logallrefupdates = true
diff --git a/tests/resources/redundant.git/objects/info/packs b/tests/resources/redundant.git/objects/info/packs
new file mode 100644
index 000000000..fbf960f10
--- /dev/null
+++ b/tests/resources/redundant.git/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.pack
+
diff --git a/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.idx b/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.idx
new file mode 100644
index 000000000..d8e099a98
--- /dev/null
+++ b/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.idx
Binary files differ
diff --git a/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.pack b/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.pack
new file mode 100644
index 000000000..02ea49f4b
--- /dev/null
+++ b/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.pack
Binary files differ
diff --git a/tests/resources/redundant.git/packed-refs b/tests/resources/redundant.git/packed-refs
new file mode 100644
index 000000000..e8bf04d65
--- /dev/null
+++ b/tests/resources/redundant.git/packed-refs
@@ -0,0 +1,3 @@
+# pack-refs with: peeled fully-peeled
+e18fa2788e9c4e12d83150808a31dfbfb1ae364f refs/heads/master
+91f4b95df4a59504a9813ba66912562931d990e3 refs/heads/ref2/ref28
diff --git a/tests/resources/redundant.git/refs/.gitkeep b/tests/resources/redundant.git/refs/.gitkeep
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/resources/redundant.git/refs/.gitkeep
diff --git a/tests/revwalk/basic.c b/tests/revwalk/basic.c
index 7e50452c9..d8236ce72 100644
--- a/tests/revwalk/basic.c
+++ b/tests/revwalk/basic.c
@@ -437,3 +437,38 @@ void test_revwalk_basic__mimic_git_rev_list(void)
cl_git_fail_with(git_revwalk_next(&oid, _walk), GIT_ITEROVER);
}
+
+void test_revwalk_basic__big_timestamp(void)
+{
+ git_reference *head;
+ git_commit *tip;
+ git_signature *sig;
+ git_tree *tree;
+ git_oid id;
+ int error;
+
+ revwalk_basic_setup_walk("testrepo.git");
+
+ cl_git_pass(git_repository_head(&head, _repo));
+ cl_git_pass(git_reference_peel((git_object **) &tip, head, GIT_OBJ_COMMIT));
+
+ /* Commit with a far-ahead timestamp, we should be able to parse it in the revwalk */
+ cl_git_pass(git_signature_new(&sig, "Joe", "joe@example.com", 2399662595, 0));
+ cl_git_pass(git_commit_tree(&tree, tip));
+
+ cl_git_pass(git_commit_create(&id, _repo, "HEAD", sig, sig, NULL, "some message", tree, 1, &tip));
+
+ cl_git_pass(git_revwalk_push_head(_walk));
+
+ while ((error = git_revwalk_next(&id, _walk)) == 0) {
+ /* nothing */
+ }
+
+ cl_assert_equal_i(GIT_ITEROVER, error);
+
+ git_tree_free(tree);
+ git_commit_free(tip);
+ git_reference_free(head);
+ git_signature_free(sig);
+
+}
diff --git a/tests/revwalk/mergebase.c b/tests/revwalk/mergebase.c
index aafe97d71..d486bbaf3 100644
--- a/tests/revwalk/mergebase.c
+++ b/tests/revwalk/mergebase.c
@@ -492,3 +492,22 @@ void test_revwalk_mergebase__octopus_merge_branch(void)
*
* a
*/
+
+void test_revwalk_mergebase__remove_redundant(void)
+{
+ git_repository *repo;
+ git_oid one, two, base;
+ git_oidarray result = {NULL, 0};
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("redundant.git")));
+
+ cl_git_pass(git_oid_fromstr(&one, "d89137c93ba1ee749214ff4ce52ae9137bc833f9"));
+ cl_git_pass(git_oid_fromstr(&two, "91f4b95df4a59504a9813ba66912562931d990e3"));
+ cl_git_pass(git_oid_fromstr(&base, "6cb1f2352d974e1c5a776093017e8772416ac97a"));
+
+ cl_git_pass(git_merge_bases(&result, repo, &one, &two));
+ cl_assert_equal_i(1, result.count);
+ cl_assert_equal_oid(&base, &result.ids[0]);
+
+ git_repository_free(repo);
+}
diff --git a/tests/revwalk/signatureparsing.c b/tests/revwalk/signatureparsing.c
index 5c7d8813d..b312bad09 100644
--- a/tests/revwalk/signatureparsing.c
+++ b/tests/revwalk/signatureparsing.c
@@ -38,7 +38,7 @@ void test_revwalk_signatureparsing__do_not_choke_when_name_contains_angle_bracke
signature = git_commit_committer(commit);
cl_assert_equal_s("foo@example.com", signature->email);
- cl_assert_equal_s("<Yu V. Bin Haacked>", signature->name);
+ cl_assert_equal_s("Yu V. Bin Haacked", signature->name);
cl_assert_equal_i(1323847743, (int)signature->when.time);
cl_assert_equal_i(60, signature->when.offset);
diff --git a/tests/win32/longpath.c b/tests/win32/longpath.c
new file mode 100644
index 000000000..6de7d389a
--- /dev/null
+++ b/tests/win32/longpath.c
@@ -0,0 +1,60 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+#include "clone.h"
+#include "buffer.h"
+#include "fileops.h"
+
+static git_buf path = GIT_BUF_INIT;
+
+void test_win32_longpath__initialize(void)
+{
+#ifdef GIT_WIN32
+ const char *base = clar_sandbox_path();
+ size_t base_len = strlen(base);
+ size_t remain = MAX_PATH - base_len;
+ size_t i;
+
+ git_buf_clear(&path);
+ git_buf_puts(&path, base);
+ git_buf_putc(&path, '/');
+
+ cl_assert(remain < (MAX_PATH - 5));
+
+ for (i = 0; i < (remain - 5); i++)
+ git_buf_putc(&path, 'a');
+#endif
+}
+
+void test_win32_longpath__cleanup(void)
+{
+ git_buf_free(&path);
+}
+
+#ifdef GIT_WIN32
+void assert_name_too_long(void)
+{
+ const git_error *err;
+ size_t expected_len, actual_len;
+ const char *expected_msg;
+
+ err = giterr_last();
+ actual_len = strlen(err->message);
+
+ expected_msg = git_win32_get_error_message(ERROR_FILENAME_EXCED_RANGE);
+ expected_len = strlen(expected_msg);
+
+ /* check the suffix */
+ cl_assert_equal_s(expected_msg, err->message + (actual_len - expected_len));
+}
+#endif
+
+void test_win32_longpath__errmsg_on_checkout(void)
+{
+#ifdef GIT_WIN32
+ git_repository *repo;
+
+ cl_git_fail(git_clone(&repo, cl_fixture("testrepo.git"), path.ptr, NULL));
+ assert_name_too_long();
+#endif
+}