summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md25
-rw-r--r--CMakeLists.txt25
-rw-r--r--docs/error-handling.md2
-rw-r--r--examples/network/fetch.c1
-rw-r--r--include/git2/common.h17
-rw-r--r--include/git2/config.h24
-rw-r--r--include/git2/diff.h29
-rw-r--r--include/git2/errors.h1
-rw-r--r--include/git2/index.h28
-rw-r--r--include/git2/merge.h12
-rw-r--r--include/git2/sys/stream.h13
-rw-r--r--include/git2/sys/transport.h1
-rw-r--r--src/attr_file.c6
-rw-r--r--src/attrcache.c5
-rw-r--r--src/blame.c2
-rw-r--r--src/blame_git.c34
-rw-r--r--src/blame_git.h2
-rw-r--r--src/checkout.c10
-rw-r--r--src/commit.c35
-rw-r--r--src/commit_list.c8
-rw-r--r--src/commit_list.h3
-rw-r--r--src/common.h9
-rw-r--r--src/config.c10
-rw-r--r--src/config.h1
-rw-r--r--src/config_file.c15
-rw-r--r--src/diff.c24
-rw-r--r--src/diff_file.c25
-rw-r--r--src/diff_patch.c4
-rw-r--r--src/diff_tform.c6
-rw-r--r--src/diff_xdiff.c7
-rw-r--r--src/diff_xdiff.h5
-rw-r--r--src/filebuf.c6
-rw-r--r--src/fileops.c57
-rw-r--r--src/fileops.h3
-rw-r--r--src/global.c5
-rw-r--r--src/global.h2
-rw-r--r--src/index.c156
-rw-r--r--src/iterator.c7
-rw-r--r--src/merge.c342
-rw-r--r--src/merge_file.c98
-rw-r--r--src/merge_file.h14
-rw-r--r--src/odb.c243
-rw-r--r--src/odb_pack.c82
-rw-r--r--src/pack-objects.c3
-rw-r--r--src/pathspec.c12
-rw-r--r--src/pool.c239
-rw-r--r--src/pool.h54
-rw-r--r--src/refdb_fs.c27
-rw-r--r--src/repository.c15
-rw-r--r--src/revwalk.c6
-rw-r--r--src/settings.c16
-rw-r--r--src/signature.c18
-rw-r--r--src/sortedcache.c5
-rw-r--r--src/stream.h3
-rw-r--r--src/submodule.c42
-rw-r--r--src/sysdir.c19
-rw-r--r--src/sysdir.h14
-rw-r--r--src/tls_stream.c13
-rw-r--r--src/transaction.c3
-rw-r--r--src/transports/git.c11
-rw-r--r--src/transports/http.c13
-rw-r--r--src/transports/winhttp.c36
-rw-r--r--src/win32/findfile.c10
-rw-r--r--src/win32/findfile.h1
-rw-r--r--src/xdiff/xdiff.h8
-rw-r--r--src/xdiff/xdiffi.c12
-rw-r--r--src/xdiff/xhistogram.c6
-rw-r--r--src/xdiff/xmerge.c142
-rw-r--r--tests/commit/commit.c8
-rw-r--r--tests/commit/signature.c7
-rw-r--r--tests/commit/write.c2
-rw-r--r--tests/config/global.c40
-rw-r--r--tests/config/stress.c20
-rw-r--r--tests/core/filebuf.c9
-rw-r--r--tests/core/pool.c82
-rw-r--r--tests/core/stream.c47
-rw-r--r--tests/core/useragent.c11
-rw-r--r--tests/diff/notify.c29
-rw-r--r--tests/diff/tree.c2
-rw-r--r--tests/diff/workdir.c63
-rw-r--r--tests/index/bypath.c117
-rw-r--r--tests/index/conflicts.c91
-rw-r--r--tests/index/nsec.c78
-rw-r--r--tests/merge/files.c88
-rw-r--r--tests/merge/merge_helpers.c9
-rw-r--r--tests/merge/trees/commits.c18
-rw-r--r--tests/odb/sorting.c16
-rw-r--r--tests/online/badssl.c15
-rw-r--r--tests/refs/create.c17
-rw-r--r--tests/refs/reflog/reflog.c114
-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/resources/submodule_with_path/.gitmodules3
-rw-r--r--tests/resources/submodule_with_path/.gitted/HEAD1
-rw-r--r--tests/resources/submodule_with_path/.gitted/config8
-rw-r--r--tests/resources/submodule_with_path/.gitted/indexbin0 -> 253 bytes
-rw-r--r--tests/resources/submodule_with_path/.gitted/objects/18/372280a56a54340fa600aa91315065c6c4c693bin0 -> 85 bytes
-rw-r--r--tests/resources/submodule_with_path/.gitted/objects/36/683131578275f6a8fd1c539e0d5da0d8adff26bin0 -> 63 bytes
-rw-r--r--tests/resources/submodule_with_path/.gitted/objects/89/ca686bb21bfb75dda99a02313831a0c418f921bin0 -> 161 bytes
-rw-r--r--tests/resources/submodule_with_path/.gitted/objects/b1/620ef2628d10416a84d19c783e33dc4556c9c3bin0 -> 86 bytes
-rw-r--r--tests/resources/submodule_with_path/.gitted/objects/ba/34c47dc9d3d0b1bb335b45c9d26ba1f0fc90c7bin0 -> 68 bytes
-rw-r--r--tests/resources/submodule_with_path/.gitted/objects/c8/4bf57ba2254dba216ab5c6eb1a19fe8bd0e0d6bin0 -> 127 bytes
-rw-r--r--tests/resources/submodule_with_path/.gitted/objects/d5/45fc6b40ec9e67332b6a1d2dedcbdb1bffeb6bbin0 -> 51 bytes
-rw-r--r--tests/resources/submodule_with_path/.gitted/refs/heads/master1
-rw-r--r--tests/revwalk/basic.c36
-rw-r--r--tests/revwalk/mergebase.c20
-rw-r--r--tests/revwalk/signatureparsing.c2
-rw-r--r--tests/submodule/submodule_helpers.c15
-rw-r--r--tests/submodule/submodule_helpers.h1
-rw-r--r--tests/submodule/update.c48
127 files changed, 2252 insertions, 924 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6ade3e3b1..7d5a4166a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,9 @@ v0.23 + 1
* Symlinks are now followed when locking a file, which can be
necessary when multiple worktrees share a base repository.
+* You can now set your own user-agent to be sent for HTTP requests by
+ using the `GIT_OPT_SET_USER_AGENT` with `git_libgit2_opts()`.
+
### API additions
* `git_config_lock()` has been added, which allow for
@@ -17,6 +20,16 @@ v0.23 + 1
the opportunity for concurrent operations and not committing any
changes until the unlock.
+
+* `git_diff_options` added a new callback `progress_cb` to report on the
+ progress of the diff as files are being compared. The documentation of
+ the existing callback `notify_cb` was updated to reflect that it only
+ gets called when new deltas are added to the diff.
+
+* `git_stream_register_tls()` lets you register a callback to be used
+ as the constructor for a TLS stream instead of the libgit2 built-in
+ one.
+
### API removals
### Breaking API changes
@@ -31,10 +44,14 @@ v0.23 + 1
with which to implement the transactional/atomic semantics for the
configuration backend.
-* `git_index_add` will now use the case as provided by the caller on
- case insensitive systems. Previous versions would keep the case as
- it existed in the index. This does not affect the higher-level
- `git_index_add_bypath` or `git_index_add_frombuffer` functions.
+* `git_index_add` and `git_index_conflict_add()` will now use the case
+ as provided by the caller on case insensitive systems. Previous
+ versions would keep the case as it existed in the index. This does
+ not affect the higher-level `git_index_add_bypath` or
+ `git_index_add_frombuffer` functions.
+
+* The `notify_payload` field of `git_diff_options` was renamed to `payload`
+ to reflect that it's also the payload for the new progress callback.
v0.23
------
diff --git a/CMakeLists.txt b/CMakeLists.txt
index f15a6e623..0537b19d1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -144,13 +144,13 @@ FUNCTION(TARGET_OS_LIBRARIES target)
ENDIF()
ENDFUNCTION()
-# For the MSVC IDE, this function splits up the source files like windows
-# explorer does. This is esp. useful with the libgit2_clar project, were
-# usually 2 or more files share the same name. Sadly, this file grouping
-# is a per-directory option in cmake and not per-target, resulting in
-# empty virtual folders "tests" for the git2.dll
-FUNCTION(MSVC_SPLIT_SOURCES target)
- IF(MSVC_IDE)
+# This function splits the sources files up into their appropriate
+# subdirectories. This is especially useful for IDEs like Xcode and
+# Visual Studio, so that you can navigate into the libgit2_clar project,
+# and see the folders within the tests folder (instead of just seeing all
+# source and tests in a single folder.)
+FUNCTION(IDE_SPLIT_SOURCES target)
+ IF(MSVC_IDE OR CMAKE_GENERATOR STREQUAL Xcode)
GET_TARGET_PROPERTY(sources ${target} SOURCES)
FOREACH(source ${sources})
IF(source MATCHES ".*/")
@@ -571,7 +571,7 @@ IF(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS)
SET_TARGET_PROPERTIES(git2 PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64")
ENDIF()
-MSVC_SPLIT_SOURCES(git2)
+IDE_SPLIT_SOURCES(git2)
IF (SONAME)
SET_TARGET_PROPERTIES(git2 PROPERTIES VERSION ${LIBGIT2_VERSION_STRING})
@@ -602,7 +602,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")
@@ -635,7 +640,7 @@ IF (BUILD_CLAR)
TARGET_LINK_LIBRARIES(libgit2_clar ${GSSAPI_LIBRARIES})
TARGET_LINK_LIBRARIES(libgit2_clar ${ICONV_LIBRARIES})
TARGET_OS_LIBRARIES(libgit2_clar)
- MSVC_SPLIT_SOURCES(libgit2_clar)
+ IDE_SPLIT_SOURCES(libgit2_clar)
IF (MSVC_IDE)
# Precompiled headers
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/examples/network/fetch.c b/examples/network/fetch.c
index bc7a5a05e..177359b88 100644
--- a/examples/network/fetch.c
+++ b/examples/network/fetch.c
@@ -63,6 +63,7 @@ static int transfer_progress_cb(const git_transfer_progress *stats, void *payloa
stats->received_objects, stats->total_objects,
stats->indexed_objects, stats->received_bytes);
}
+ return 0;
}
/** Entry point for this command */
diff --git a/include/git2/common.h b/include/git2/common.h
index 1846e601a..e687977d5 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 \
@@ -144,6 +146,7 @@ typedef enum {
GIT_OPT_GET_TEMPLATE_PATH,
GIT_OPT_SET_TEMPLATE_PATH,
GIT_OPT_SET_SSL_CERT_LOCATIONS,
+ GIT_OPT_SET_USER_AGENT,
} git_libgit2_opt_t;
/**
@@ -239,6 +242,8 @@ typedef enum {
* >
* > Either parameter may be `NULL`, but not both.
*
+ * * opts(GIT_OPT_SET_USER_AGENT, const char *user_agent)
+ *
* @param option Option key
* @param ... value to set the option
* @return 0 on success, <0 on failure
diff --git a/include/git2/config.h b/include/git2/config.h
index 56b5431ac..d0f1ba1b3 100644
--- a/include/git2/config.h
+++ b/include/git2/config.h
@@ -29,25 +29,28 @@ GIT_BEGIN_DECL
* priority levels as well.
*/
typedef enum {
+ /** System-wide on Windows, for compatibility with portable git */
+ GIT_CONFIG_LEVEL_PROGRAMDATA = 1,
+
/** System-wide configuration file; /etc/gitconfig on Linux systems */
- GIT_CONFIG_LEVEL_SYSTEM = 1,
+ GIT_CONFIG_LEVEL_SYSTEM = 2,
/** XDG compatible configuration file; typically ~/.config/git/config */
- GIT_CONFIG_LEVEL_XDG = 2,
+ GIT_CONFIG_LEVEL_XDG = 3,
/** User-specific configuration file (also called Global configuration
* file); typically ~/.gitconfig
*/
- GIT_CONFIG_LEVEL_GLOBAL = 3,
+ GIT_CONFIG_LEVEL_GLOBAL = 4,
/** Repository specific configuration file; $WORK_DIR/.git/config on
* non-bare repos
*/
- GIT_CONFIG_LEVEL_LOCAL = 4,
+ GIT_CONFIG_LEVEL_LOCAL = 5,
/** Application specific configuration file; freely defined by applications
*/
- GIT_CONFIG_LEVEL_APP = 5,
+ GIT_CONFIG_LEVEL_APP = 6,
/** Represents the highest level available config file (i.e. the most
* specific config file available that actually is loaded)
@@ -142,6 +145,17 @@ GIT_EXTERN(int) git_config_find_xdg(git_buf *out);
GIT_EXTERN(int) git_config_find_system(git_buf *out);
/**
+ * Locate the path to the configuration file in ProgramData
+ *
+ * Look for the file in %PROGRAMDATA%\Git\config used by portable git.
+ *
+ * @param out Pointer to a user-allocated git_buf in which to store the path
+ * @return 0 if a ProgramData configuration file has been
+ * found. Its path will be stored in `out`.
+ */
+GIT_EXTERN(int) git_config_find_programdata(git_buf *out);
+
+/**
* Open the global, XDG and system configuration files
*
* Utility wrapper that finds the global, XDG and system configuration files
diff --git a/include/git2/diff.h b/include/git2/diff.h
index a0f6db350..cbffdb49a 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -351,6 +351,22 @@ typedef int (*git_diff_notify_cb)(
void *payload);
/**
+ * Diff progress callback.
+ *
+ * Called before each file comparison.
+ *
+ * @param diff_so_far The diff being generated.
+ * @param old_path The path to the old file or NULL.
+ * @param new_path The path to the new file or NULL.
+ * @return Non-zero to abort the diff.
+ */
+typedef int (*git_diff_progress_cb)(
+ const git_diff *diff_so_far,
+ const char *old_path,
+ const char *new_path,
+ void *payload);
+
+/**
* Structure describing options about how the diff should be executed.
*
* Setting all values of the structure to zero will yield the default
@@ -370,8 +386,10 @@ typedef int (*git_diff_notify_cb)(
* - `max_size` is a file size (in bytes) above which a blob will be marked
* as binary automatically; pass a negative value to disable.
* - `notify_cb` is an optional callback function, notifying the consumer of
- * which files are being examined as the diff is generated
- * - `notify_payload` is the payload data to pass to the `notify_cb` function
+ * changes to the diff as new deltas are added.
+ * - `progress_cb` is an optional callback function, notifying the consumer of
+ * which files are being examined as the diff is generated.
+ * - `payload` is the payload to pass to the callback functions.
* - `ignore_submodules` overrides the submodule ignore setting for all
* submodules in the diff.
*/
@@ -383,8 +401,9 @@ typedef struct {
git_submodule_ignore_t ignore_submodules; /**< submodule ignore rule */
git_strarray pathspec; /**< defaults to include all paths */
- git_diff_notify_cb notify_cb;
- void *notify_payload;
+ git_diff_notify_cb notify_cb;
+ git_diff_progress_cb progress_cb;
+ void *payload;
/* options controlling how to diff text is generated */
@@ -403,7 +422,7 @@ typedef struct {
* `git_diff_options_init` programmatic initialization.
*/
#define GIT_DIFF_OPTIONS_INIT \
- {GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, 3}
+ {GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, NULL, 3}
/**
* Initializes a `git_diff_options` with default values. Equivalent to
diff --git a/include/git2/errors.h b/include/git2/errors.h
index 4698366d8..1b528cf25 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -49,6 +49,7 @@ typedef enum {
GIT_EINVALID = -21, /**< Invalid operation or input */
GIT_EUNCOMMITTED = -22, /**< Uncommitted changes in index prevented operation */
GIT_EDIRECTORY = -23, /**< The operation is not valid for a directory */
+ GIT_EMERGECONFLICT = -24, /**< A merge conflict exists and cannot continue */
GIT_PASSTHROUGH = -30, /**< Internal only */
GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */
diff --git a/include/git2/index.h b/include/git2/index.h
index 176ba301e..466765be3 100644
--- a/include/git2/index.h
+++ b/include/git2/index.h
@@ -154,13 +154,27 @@ typedef enum {
GIT_INDEX_ADD_CHECK_PATHSPEC = (1u << 2),
} git_index_add_option_t;
-/**
- * Match any index stage.
- *
- * Some index APIs take a stage to match; pass this value to match
- * any entry matching the path regardless of stage.
- */
-#define GIT_INDEX_STAGE_ANY -1
+typedef enum {
+ /**
+ * Match any index stage.
+ *
+ * Some index APIs take a stage to match; pass this value to match
+ * any entry matching the path regardless of stage.
+ */
+ GIT_INDEX_STAGE_ANY = -1,
+
+ /** A normal staged file in the index. */
+ GIT_INDEX_STAGE_NORMAL = 0,
+
+ /** The ancestor side of a conflict. */
+ GIT_INDEX_STAGE_ANCESTOR = 1,
+
+ /** The "ours" side of a conflict. */
+ GIT_INDEX_STAGE_OURS = 2,
+
+ /** The "theirs" side of a conflict. */
+ GIT_INDEX_STAGE_THEIRS = 3,
+} git_index_stage_t;
/** @name Index File Functions
*
diff --git a/include/git2/merge.h b/include/git2/merge.h
index 5fef452b9..b7da63e0e 100644
--- a/include/git2/merge.h
+++ b/include/git2/merge.h
@@ -72,6 +72,18 @@ typedef enum {
* the ability to merge between a modified and renamed file.
*/
GIT_MERGE_TREE_FIND_RENAMES = (1 << 0),
+
+ /**
+ * If a conflict occurs, exit immediately instead of attempting to
+ * continue resolving conflicts. The merge operation will fail with
+ * GIT_EMERGECONFLICT and no index will be returned.
+ */
+ GIT_MERGE_TREE_FAIL_ON_CONFLICT = (1 << 1),
+
+ /**
+ * Do not write the REUC extension on the generated index
+ */
+ GIT_MERGE_TREE_SKIP_REUC = (1 << 2),
} git_merge_tree_flag_t;
/**
diff --git a/include/git2/sys/stream.h b/include/git2/sys/stream.h
index 55a714bbb..2b4ff7fd8 100644
--- a/include/git2/sys/stream.h
+++ b/include/git2/sys/stream.h
@@ -39,6 +39,19 @@ typedef struct git_stream {
void (*free)(struct git_stream *);
} git_stream;
+typedef int (*git_stream_cb)(git_stream **out, const char *host, const char *port);
+
+/**
+ * Register a TLS stream constructor for the library to use
+ *
+ * If a constructor is already set, it will be overwritten. Pass
+ * `NULL` in order to deregister the current constructor.
+ *
+ * @param ctor the constructor to use
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_stream_register_tls(git_stream_cb ctor);
+
GIT_END_DECL
#endif
diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h
index ca8617f3f..ce0234a18 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/src/attr_file.c b/src/attr_file.c
index 89706865a..500c99bd9 100644
--- a/src/attr_file.c
+++ b/src/attr_file.c
@@ -35,11 +35,7 @@ int git_attr_file__new(
return -1;
}
- if (git_pool_init(&attrs->pool, 1, 0) < 0) {
- attr_file_free(attrs);
- return -1;
- }
-
+ git_pool_init(&attrs->pool, 1);
GIT_REFCOUNT_INC(attrs);
attrs->entry = entry;
attrs->source = source;
diff --git a/src/attrcache.c b/src/attrcache.c
index 5bc260460..a57110684 100644
--- a/src/attrcache.c
+++ b/src/attrcache.c
@@ -388,10 +388,11 @@ int git_attr_cache__do_init(git_repository *repo)
* hashtable for attribute macros, and string pool
*/
if ((ret = git_strmap_alloc(&cache->files)) < 0 ||
- (ret = git_strmap_alloc(&cache->macros)) < 0 ||
- (ret = git_pool_init(&cache->pool, 1, 0)) < 0)
+ (ret = git_strmap_alloc(&cache->macros)) < 0)
goto cancel;
+ git_pool_init(&cache->pool, 1);
+
cache = git__compare_and_swap(&repo->attrcache, NULL, cache);
if (cache)
goto cancel; /* raced with another thread, free this but no error */
diff --git a/src/blame.c b/src/blame.c
index a52f9402b..08a90dcfd 100644
--- a/src/blame.c
+++ b/src/blame.c
@@ -331,7 +331,7 @@ static int blame_internal(git_blame *blame)
blame->ent = ent;
- git_blame__like_git(blame, blame->options.flags);
+ error = git_blame__like_git(blame, blame->options.flags);
cleanup:
for (ent = blame->ent; ent; ) {
diff --git a/src/blame_git.c b/src/blame_git.c
index f481426aa..67bae2384 100644
--- a/src/blame_git.c
+++ b/src/blame_git.c
@@ -9,6 +9,7 @@
#include "commit.h"
#include "blob.h"
#include "xdiff/xinclude.h"
+#include "diff_xdiff.h"
/*
* Origin is refcounted and usually we keep the blob contents to be
@@ -351,6 +352,13 @@ static int diff_hunks(mmfile_t file_a, mmfile_t file_b, void *cb_data)
ecb.priv = cb_data;
trim_common_tail(&file_a, &file_b, 0);
+
+ if (file_a.size > GIT_XDIFF_MAX_SIZE ||
+ file_b.size > GIT_XDIFF_MAX_SIZE) {
+ giterr_set(GITERR_INVALID, "file too large to blame");
+ return -1;
+ }
+
return xdl_diff(&file_a, &file_b, &xpp, &xecfg, &ecb);
}
@@ -379,7 +387,9 @@ static int pass_blame_to_parent(
fill_origin_blob(parent, &file_p);
fill_origin_blob(target, &file_o);
- diff_hunks(file_p, file_o, &d);
+ if (diff_hunks(file_p, file_o, &d) < 0)
+ return -1;
+
/* The reset (i.e. anything after tlno) are the same as the parent */
blame_chunk(blame, d.tlno, d.plno, last_in_target, target, parent);
@@ -477,12 +487,13 @@ static void pass_whole_blame(git_blame *blame,
}
}
-static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt)
+static int pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt)
{
git_commit *commit = origin->commit;
int i, num_parents;
git_blame__origin *sg_buf[16];
git_blame__origin *porigin, **sg_origin = sg_buf;
+ int ret, error = 0;
num_parents = git_commit_parentcount(commit);
if (!git_oid_cmp(git_commit_id(commit), &blame->options.oldest_commit))
@@ -540,8 +551,13 @@ static void pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt
origin_incref(porigin);
origin->previous = porigin;
}
- if (pass_blame_to_parent(blame, origin, porigin))
+
+ if ((ret = pass_blame_to_parent(blame, origin, porigin)) != 0) {
+ if (ret < 0)
+ error = -1;
+
goto finish;
+ }
}
/* TODO: optionally find moves in parents' files */
@@ -554,7 +570,7 @@ finish:
origin_decref(sg_origin[i]);
if (sg_origin != sg_buf)
git__free(sg_origin);
- return;
+ return error;
}
/*
@@ -583,7 +599,7 @@ static void coalesce(git_blame *blame)
}
}
-void git_blame__like_git(git_blame *blame, uint32_t opt)
+int git_blame__like_git(git_blame *blame, uint32_t opt)
{
while (true) {
git_blame__entry *ent;
@@ -594,11 +610,13 @@ void git_blame__like_git(git_blame *blame, uint32_t opt)
if (!ent->guilty)
suspect = ent->suspect;
if (!suspect)
- return; /* all done */
+ return 0; /* all done */
/* We'll use this suspect later in the loop, so hold on to it for now. */
origin_incref(suspect);
- pass_blame(blame, suspect, opt);
+
+ if (pass_blame(blame, suspect, opt) < 0)
+ return -1;
/* Take responsibility for the remaining entries */
for (ent = blame->ent; ent; ent = ent->next) {
@@ -613,6 +631,8 @@ void git_blame__like_git(git_blame *blame, uint32_t opt)
}
coalesce(blame);
+
+ return 0;
}
void git_blame__free_entry(git_blame__entry *ent)
diff --git a/src/blame_git.h b/src/blame_git.h
index 3ec2710b8..1891b0e1f 100644
--- a/src/blame_git.h
+++ b/src/blame_git.h
@@ -15,6 +15,6 @@ int git_blame__get_origin(
git_commit *commit,
const char *path);
void git_blame__free_entry(git_blame__entry *ent);
-void git_blame__like_git(git_blame *sb, uint32_t flags);
+int git_blame__like_git(git_blame *sb, uint32_t flags);
#endif
diff --git a/src/checkout.c b/src/checkout.c
index 2a8bfd558..d09357f2a 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -18,6 +18,7 @@
#include "git2/submodule.h"
#include "git2/sys/index.h"
#include "git2/sys/filter.h"
+#include "git2/merge.h"
#include "refs.h"
#include "repository.h"
@@ -27,7 +28,7 @@
#include "diff.h"
#include "pathspec.h"
#include "buf_text.h"
-#include "merge_file.h"
+#include "diff_xdiff.h"
#include "path.h"
#include "attr.h"
#include "pool.h"
@@ -1254,11 +1255,13 @@ static int checkout_get_actions(
int error = 0, act;
const git_index_entry *wditem;
git_vector pathspec = GIT_VECTOR_INIT, *deltas;
- git_pool pathpool = GIT_POOL_INIT_STRINGPOOL;
+ git_pool pathpool;
git_diff_delta *delta;
size_t i, *counts = NULL;
uint32_t *actions = NULL;
+ git_pool_init(&pathpool, 1);
+
if (data->opts.paths.count > 0 &&
git_pathspec__vinit(&pathspec, &data->opts.paths, &pathpool) < 0)
return -1;
@@ -2438,10 +2441,11 @@ static int checkout_data_init(
git_config_entry_free(conflict_style);
}
+ git_pool_init(&data->pool, 1);
+
if ((error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
(error = git_vector_init(&data->remove_conflicts, 0, NULL)) < 0 ||
(error = git_vector_init(&data->update_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 ||
(error = git_strmap_alloc(&data->mkdir_map)) < 0)
diff --git a/src/commit.c b/src/commit.c
index 616f947db..b42f9de28 100644
--- a/src/commit.c
+++ b/src/commit.c
@@ -431,22 +431,37 @@ const char *git_commit_summary(git_commit *commit)
{
git_buf summary = GIT_BUF_INIT;
const char *msg, *space;
+ bool space_contains_newline = false;
assert(commit);
if (!commit->summary) {
for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) {
- if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n'))
+ char next_character = msg[0];
+ /* stop processing at the end of the first paragraph */
+ if (next_character == '\n' && (!msg[1] || msg[1] == '\n'))
break;
- else if (msg[0] == '\n')
- git_buf_putc(&summary, ' ');
- else if (git__isspace(msg[0]))
- space = space ? space : msg;
- else if (space) {
- git_buf_put(&summary, space, (msg - space) + 1);
- space = NULL;
- } else
- git_buf_putc(&summary, *msg);
+ /* record the beginning of contiguous whitespace runs */
+ else if (git__isspace(next_character)) {
+ if(space == NULL) {
+ space = msg;
+ space_contains_newline = false;
+ }
+ space_contains_newline |= next_character == '\n';
+ }
+ /* the next character is non-space */
+ else {
+ /* process any recorded whitespace */
+ if (space) {
+ if(space_contains_newline)
+ git_buf_putc(&summary, ' '); /* if the space contains a newline, collapse to ' ' */
+ else
+ git_buf_put(&summary, space, (msg - space)); /* otherwise copy it */
+ space = NULL;
+ }
+ /* copy the next character */
+ git_buf_putc(&summary, next_character);
+ }
}
commit->summary = git_buf_detach(&summary);
diff --git a/src/commit_list.c b/src/commit_list.c
index 3054c18dd..28948c88b 100644
--- a/src/commit_list.c
+++ b/src/commit_list.c
@@ -47,7 +47,7 @@ git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_
git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk)
{
- return (git_commit_list_node *)git_pool_malloc(&walk->commit_pool, COMMIT_ALLOC);
+ return (git_commit_list_node *)git_pool_mallocz(&walk->commit_pool, 1);
}
static int commit_error(git_commit_list_node *commit, const char *msg)
@@ -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/common.h b/src/common.h
index c87343dda..2913baa92 100644
--- a/src/common.h
+++ b/src/common.h
@@ -209,6 +209,15 @@ GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int v
#define GITERR_CHECK_ALLOC_ADD(out, one, two) \
if (GIT_ADD_SIZET_OVERFLOW(out, one, two)) { return -1; }
+#define GITERR_CHECK_ALLOC_ADD3(out, one, two, three) \
+ if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \
+ GIT_ADD_SIZET_OVERFLOW(out, *(out), three)) { return -1; }
+
+#define GITERR_CHECK_ALLOC_ADD4(out, one, two, three, four) \
+ if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \
+ GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \
+ GIT_ADD_SIZET_OVERFLOW(out, *(out), four)) { return -1; }
+
/** Check for multiplicative overflow, failing if it would occur. */
#define GITERR_CHECK_ALLOC_MULTIPLY(out, nelem, elsize) \
if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { return -1; }
diff --git a/src/config.c b/src/config.c
index f0b2c3a61..f4d4cb2b9 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1086,6 +1086,12 @@ int git_config_find_system(git_buf *path)
return git_sysdir_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM);
}
+int git_config_find_programdata(git_buf *path)
+{
+ git_buf_sanitize(path);
+ return git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA);
+}
+
int git_config__global_location(git_buf *buf)
{
const git_buf *paths;
@@ -1133,6 +1139,10 @@ int git_config_open_default(git_config **out)
error = git_config_add_file_ondisk(cfg, buf.ptr,
GIT_CONFIG_LEVEL_SYSTEM, 0);
+ if (!error && !git_config_find_programdata(&buf))
+ error = git_config_add_file_ondisk(cfg, buf.ptr,
+ GIT_CONFIG_LEVEL_PROGRAMDATA, 0);
+
git_buf_free(&buf);
if (error) {
diff --git a/src/config.h b/src/config.h
index ba745331a..00c12b50d 100644
--- a/src/config.h
+++ b/src/config.h
@@ -12,6 +12,7 @@
#include "vector.h"
#include "repository.h"
+#define GIT_CONFIG_FILENAME_PROGRAMDATA "config"
#define GIT_CONFIG_FILENAME_SYSTEM "gitconfig"
#define GIT_CONFIG_FILENAME_GLOBAL ".gitconfig"
#define GIT_CONFIG_FILENAME_XDG "config"
diff --git a/src/config_file.c b/src/config_file.c
index 46f21c0f1..5f5e309e0 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -77,8 +77,7 @@ typedef struct git_config_file_iter {
(iter) = (tmp))
struct reader {
- time_t file_mtime;
- size_t file_size;
+ git_oid checksum;
char *file_path;
git_buf buffer;
char *read_ptr;
@@ -285,7 +284,7 @@ static int config_open(git_config_backend *cfg, git_config_level_t level)
git_buf_init(&reader->buffer, 0);
res = git_futils_readbuffer_updated(
- &reader->buffer, b->file_path, &reader->file_mtime, &reader->file_size, NULL);
+ &reader->buffer, b->file_path, &reader->checksum, NULL);
/* It's fine if the file doesn't exist */
if (res == GIT_ENOTFOUND)
@@ -345,7 +344,7 @@ static int config_refresh(git_config_backend *cfg)
reader = git_array_get(b->readers, i);
error = git_futils_readbuffer_updated(
&reader->buffer, reader->file_path,
- &reader->file_mtime, &reader->file_size, &updated);
+ &reader->checksum, &updated);
if (error < 0 && error != GIT_ENOTFOUND)
return error;
@@ -1618,7 +1617,7 @@ static int read_on_variable(
git_buf_init(&r->buffer, 0);
result = git_futils_readbuffer_updated(
- &r->buffer, r->file_path, &r->file_mtime, &r->file_size, NULL);
+ &r->buffer, r->file_path, &r->checksum, NULL);
if (result == 0) {
result = config_read(parse_data->values, parse_data->cfg_file, r, parse_data->level, parse_data->depth+1);
@@ -1894,7 +1893,7 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
} else {
/* Lock the file */
if ((result = git_filebuf_open(
- &file, cfg->file_path, 0, GIT_CONFIG_FILE_MODE)) < 0) {
+ &file, cfg->file_path, GIT_FILEBUF_HASH_CONTENTS, GIT_CONFIG_FILE_MODE)) < 0) {
git_buf_free(&reader->buffer);
return result;
}
@@ -1945,10 +1944,6 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p
git_buf_attach(&cfg->locked_content, git_buf_detach(&buf), len);
} else {
git_filebuf_write(&file, git_buf_cstr(&buf), git_buf_len(&buf));
-
- /* refresh stats - if this errors, then commit will error too */
- (void)git_filebuf_stats(&reader->file_mtime, &reader->file_size, &file);
-
result = git_filebuf_commit(&file);
}
diff --git a/src/diff.c b/src/diff.c
index 09f29fcee..0ab3b8d1f 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -56,7 +56,7 @@ static int diff_insert_delta(
if (diff->opts.notify_cb) {
error = diff->opts.notify_cb(
- diff, delta, matched_pathspec, diff->opts.notify_payload);
+ diff, delta, matched_pathspec, diff->opts.payload);
if (error) {
git__free(delta);
@@ -430,8 +430,9 @@ static git_diff *diff_list_alloc(
diff->new_src = new_iter->type;
memcpy(&diff->opts, &dflt, sizeof(diff->opts));
- if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0 ||
- git_pool_init(&diff->pool, 1, 0) < 0) {
+ git_pool_init(&diff->pool, 1);
+
+ if (git_vector_init(&diff->deltas, 0, git_diff_delta__cmp) < 0) {
git_diff_free(diff);
return NULL;
}
@@ -493,8 +494,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) {
@@ -1286,7 +1289,18 @@ int git_diff__from_iterators(
/* run iterators building diffs */
while (!error && (info.oitem || info.nitem)) {
- int cmp = info.oitem ?
+ int cmp;
+
+ /* report progress */
+ if (opts && opts->progress_cb) {
+ if ((error = opts->progress_cb(diff,
+ info.oitem ? info.oitem->path : NULL,
+ info.nitem ? info.nitem->path : NULL,
+ opts->payload)))
+ break;
+ }
+
+ cmp = info.oitem ?
(info.nitem ? diff->entrycomp(info.oitem, info.nitem) : -1) : 1;
/* create DELETED records for old items not matched in new */
diff --git a/src/diff_file.c b/src/diff_file.c
index c60362865..ecc34cf55 100644
--- a/src/diff_file.c
+++ b/src/diff_file.c
@@ -259,10 +259,35 @@ static int diff_file_content_load_blob(
return error;
}
+static int diff_file_content_load_workdir_symlink_fake(
+ git_diff_file_content *fc, git_buf *path)
+{
+ git_buf target = GIT_BUF_INIT;
+ int error;
+
+ if ((error = git_futils_readbuffer(&target, path->ptr)) < 0)
+ return error;
+
+ fc->map.len = git_buf_len(&target);
+ fc->map.data = git_buf_detach(&target);
+ fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
+
+ git_buf_free(&target);
+ return error;
+}
+
static int diff_file_content_load_workdir_symlink(
git_diff_file_content *fc, git_buf *path)
{
ssize_t alloc_len, read_len;
+ int symlink_supported, error;
+
+ if ((error = git_repository__cvar(
+ &symlink_supported, fc->repo, GIT_CVAR_SYMLINKS)) < 0)
+ return -1;
+
+ if (!symlink_supported)
+ return diff_file_content_load_workdir_symlink_fake(fc, path);
/* link path on disk could be UTF-16, so prepare a buffer that is
* big enough to handle some UTF-8 data expansion
diff --git a/src/diff_patch.c b/src/diff_patch.c
index 0628da6f2..50faa3b3f 100644
--- a/src/diff_patch.c
+++ b/src/diff_patch.c
@@ -30,6 +30,10 @@ static void diff_patch_update_binary(git_patch *patch)
(patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0)
patch->delta->flags |= GIT_DIFF_FLAG_BINARY;
+ else if (patch->ofile.file->size > GIT_XDIFF_MAX_SIZE ||
+ patch->nfile.file->size > GIT_XDIFF_MAX_SIZE)
+ patch->delta->flags |= GIT_DIFF_FLAG_BINARY;
+
else if ((patch->ofile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0 &&
(patch->nfile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0)
patch->delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
diff --git a/src/diff_tform.c b/src/diff_tform.c
index 92647e330..7cff34159 100644
--- a/src/diff_tform.c
+++ b/src/diff_tform.c
@@ -134,11 +134,11 @@ int git_diff__merge(
return -1;
}
- if (git_vector_init(
- &onto_new, onto->deltas.length, git_diff_delta__cmp) < 0 ||
- git_pool_init(&onto_pool, 1, 0) < 0)
+ if (git_vector_init(&onto_new, onto->deltas.length, git_diff_delta__cmp) < 0)
return -1;
+ git_pool_init(&onto_pool, 1);
+
for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
diff --git a/src/diff_xdiff.c b/src/diff_xdiff.c
index e5984f1c9..1057df3aa 100644
--- a/src/diff_xdiff.c
+++ b/src/diff_xdiff.c
@@ -4,6 +4,7 @@
* This file is part of libgit2, distributed under the GNU GPL v2 with
* a Linking Exception. For full terms see the included COPYING file.
*/
+#include "git2/errors.h"
#include "common.h"
#include "diff.h"
#include "diff_driver.h"
@@ -208,6 +209,12 @@ static int git_xdiff(git_diff_output *output, git_patch *patch)
git_patch__old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch);
git_patch__new_data(&info.xd_new_data.ptr, &info.xd_new_data.size, patch);
+ if (info.xd_old_data.size > GIT_XDIFF_MAX_SIZE ||
+ info.xd_new_data.size > GIT_XDIFF_MAX_SIZE) {
+ giterr_set(GITERR_INVALID, "files too large for diff");
+ return -1;
+ }
+
xdl_diff(&info.xd_old_data, &info.xd_new_data,
&xo->params, &xo->config, &xo->callback);
diff --git a/src/diff_xdiff.h b/src/diff_xdiff.h
index c547b00cf..98e11b2cb 100644
--- a/src/diff_xdiff.h
+++ b/src/diff_xdiff.h
@@ -11,6 +11,11 @@
#include "diff_patch.h"
#include "xdiff/xdiff.h"
+/* xdiff cannot cope with large files. these files should not be passed to
+ * xdiff. callers should treat these large files as binary.
+ */
+#define GIT_XDIFF_MAX_SIZE (1024LL * 1024 * 1023)
+
/* A git_xdiff_output is a git_diff_output with extra fields necessary
* to use libxdiff. Calling git_xdiff_init() will set the diff_cb field
* of the output to use xdiff to generate the diffs.
diff --git a/src/filebuf.c b/src/filebuf.c
index 2bbc210ba..17efe872e 100644
--- a/src/filebuf.c
+++ b/src/filebuf.c
@@ -357,6 +357,12 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode
memcpy(file->path_lock, file->path_original, path_len);
memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH);
+ if (git_path_isdir(file->path_original)) {
+ giterr_set(GITERR_FILESYSTEM, "path '%s' is a directory", file->path_original);
+ error = GIT_EDIRECTORY;
+ goto cleanup;
+ }
+
/* open the file for locking */
if ((error = lock_file(file, flags, mode)) < 0)
goto cleanup;
diff --git a/src/fileops.c b/src/fileops.c
index a13a40655..07eb504bd 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -153,13 +153,15 @@ 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)
+ git_buf *out, const char *path, git_oid *checksum, int *updated)
{
+ int error;
git_file fd;
struct stat st;
- bool changed = false;
+ git_buf buf = GIT_BUF_INIT;
+ git_oid checksum_new;
- assert(buf && path && *path);
+ assert(out && path && *path);
if (updated != NULL)
*updated = 0;
@@ -178,45 +180,50 @@ int git_futils_readbuffer_updated(
return -1;
}
- /*
- * If we were given a time and/or a size, we only want to read the file
- * if it has been modified.
- */
- if (size && *size != (size_t)st.st_size)
- changed = true;
- if (mtime && *mtime != (time_t)st.st_mtime)
- changed = true;
- if (!size && !mtime)
- changed = true;
-
- if (!changed) {
- return 0;
- }
-
- if (mtime != NULL)
- *mtime = st.st_mtime;
- if (size != NULL)
- *size = (size_t)st.st_size;
-
if ((fd = git_futils_open_ro(path)) < 0)
return fd;
- if (git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size) < 0) {
+ if (git_futils_readbuffer_fd(&buf, fd, (size_t)st.st_size) < 0) {
p_close(fd);
return -1;
}
p_close(fd);
+ if ((error = git_hash_buf(&checksum_new, buf.ptr, buf.size)) < 0) {
+ git_buf_free(&buf);
+ return error;
+ }
+
+ /*
+ * If we were given a checksum, we only want to use it if it's different
+ */
+ if (checksum && !git_oid__cmp(checksum, &checksum_new)) {
+ git_buf_free(&buf);
+ if (updated)
+ *updated = 0;
+
+ return 0;
+ }
+
+ /*
+ * If we're here, the file did change, or the user didn't have an old version
+ */
+ if (checksum)
+ git_oid_cpy(checksum, &checksum_new);
+
if (updated != NULL)
*updated = 1;
+ git_buf_swap(out, &buf);
+ git_buf_free(&buf);
+
return 0;
}
int git_futils_readbuffer(git_buf *buf, const char *path)
{
- return git_futils_readbuffer_updated(buf, path, NULL, NULL, NULL);
+ return git_futils_readbuffer_updated(buf, path, NULL, NULL);
}
int git_futils_writebuffer(
diff --git a/src/fileops.h b/src/fileops.h
index d806e83ba..6c6c49dcf 100644
--- a/src/fileops.h
+++ b/src/fileops.h
@@ -13,6 +13,7 @@
#include "path.h"
#include "pool.h"
#include "strmap.h"
+#include "oid.h"
/**
* Filebuffer methods
@@ -21,7 +22,7 @@
*/
extern int git_futils_readbuffer(git_buf *obj, const char *path);
extern int git_futils_readbuffer_updated(
- git_buf *obj, const char *path, time_t *mtime, size_t *size, int *updated);
+ git_buf *obj, const char *path, git_oid *checksum, int *updated);
extern int git_futils_readbuffer_fd(git_buf *obj, git_file fd, size_t len);
extern int git_futils_writebuffer(
diff --git a/src/global.c b/src/global.c
index 3d37ee4de..0eab8d552 100644
--- a/src/global.c
+++ b/src/global.c
@@ -31,6 +31,7 @@ static git_mutex *openssl_locks;
static git_global_shutdown_fn git__shutdown_callbacks[MAX_SHUTDOWN_CB];
static git_atomic git__n_shutdown_callbacks;
static git_atomic git__n_inits;
+char *git__user_agent;
void git__on_shutdown(git_global_shutdown_fn callback)
{
@@ -269,6 +270,8 @@ int git_libgit2_shutdown(void)
git_win32__crtdbg_stacktrace_cleanup();
git_win32__stack_cleanup();
#endif
+
+ git__free(git__user_agent);
}
/* Exit the lock */
@@ -369,6 +372,7 @@ int git_libgit2_shutdown(void)
git__global_state_cleanup(ptr);
git__free(ptr);
+ git__free(git__user_agent);
pthread_key_delete(_tls_key);
git_mutex_free(&git__mwindow_mutex);
@@ -423,6 +427,7 @@ int git_libgit2_shutdown(void)
git__shutdown();
git__global_state_cleanup(&__state);
uninit_ssl();
+ git__free(git__user_agent);
return 0;
}
diff --git a/src/global.h b/src/global.h
index 37e909ac6..9fdcee573 100644
--- a/src/global.h
+++ b/src/global.h
@@ -35,4 +35,6 @@ extern void git__on_shutdown(git_global_shutdown_fn callback);
extern void git__free_tls_data(void);
+extern const char *git_libgit2__user_agent(void);
+
#endif
diff --git a/src/index.c b/src/index.c
index 5311fe85f..0903770bb 100644
--- a/src/index.c
+++ b/src/index.c
@@ -439,7 +439,7 @@ int git_index_open(git_index **index_out, const char *index_path)
return -1;
}
- git_pool_init(&index->tree_pool, 1, 0);
+ git_pool_init(&index->tree_pool, 1);
if (index_path != NULL) {
index->index_file_path = git__strdup(index_path);
@@ -1124,7 +1124,9 @@ static int check_file_directory_collision(git_index *index,
}
static int canonicalize_directory_path(
- git_index *index, git_index_entry *entry)
+ git_index *index,
+ git_index_entry *entry,
+ git_index_entry *existing)
{
const git_index_entry *match, *best = NULL;
char *search, *sep;
@@ -1134,8 +1136,8 @@ static int canonicalize_directory_path(
return 0;
/* item already exists in the index, simply re-use the existing case */
- if ((match = git_index_get_bypath(index, entry->path, 0)) != NULL) {
- memcpy((char *)entry->path, match->path, strlen(entry->path));
+ if (existing) {
+ memcpy((char *)entry->path, existing->path, strlen(existing->path));
return 0;
}
@@ -1200,6 +1202,52 @@ static int index_no_dups(void **old, void *new)
return GIT_EEXISTS;
}
+static void index_existing_and_best(
+ git_index_entry **existing,
+ size_t *existing_position,
+ git_index_entry **best,
+ git_index *index,
+ const git_index_entry *entry)
+{
+ git_index_entry *e;
+ size_t pos;
+ int error;
+
+ error = index_find(&pos,
+ index, entry->path, 0, GIT_IDXENTRY_STAGE(entry), false);
+
+ if (error == 0) {
+ *existing = index->entries.contents[pos];
+ *existing_position = pos;
+ *best = index->entries.contents[pos];
+ return;
+ }
+
+ *existing = NULL;
+ *existing_position = 0;
+ *best = NULL;
+
+ if (GIT_IDXENTRY_STAGE(entry) == 0) {
+ for (; pos < index->entries.length; pos++) {
+ int (*strcomp)(const char *a, const char *b) =
+ index->ignore_case ? git__strcasecmp : git__strcmp;
+
+ e = index->entries.contents[pos];
+
+ if (strcomp(entry->path, e->path) != 0)
+ break;
+
+ if (GIT_IDXENTRY_STAGE(e) == GIT_INDEX_STAGE_ANCESTOR) {
+ *best = e;
+ continue;
+ } else {
+ *best = e;
+ break;
+ }
+ }
+ }
+}
+
/* index_insert takes ownership of the new entry - if it can't insert
* it, then it will return an error **and also free the entry**. When
* it replaces an existing entry, it will update the entry_ptr with the
@@ -1218,7 +1266,7 @@ static int index_insert(
{
int error = 0;
size_t path_length, position;
- git_index_entry *existing = NULL, *entry;
+ git_index_entry *existing, *best, *entry;
assert(index && entry_ptr);
@@ -1241,20 +1289,19 @@ static int index_insert(
git_vector_sort(&index->entries);
- /* look if an entry with this path already exists */
- if (!index_find(
- &position, index, entry->path, 0, GIT_IDXENTRY_STAGE(entry), false)) {
- existing = index->entries.contents[position];
- /* update filemode to existing values if stat is not trusted */
- if (trust_mode)
- entry->mode = git_index__create_mode(entry->mode);
- else
- entry->mode = index_merge_mode(index, existing, entry->mode);
- }
+ /* look if an entry with this path already exists, either staged, or (if
+ * this entry is a regular staged item) as the "ours" side of a conflict.
+ */
+ index_existing_and_best(&existing, &position, &best, index, entry);
+
+ /* update the file mode */
+ entry->mode = trust_mode ?
+ git_index__create_mode(entry->mode) :
+ index_merge_mode(index, best, entry->mode);
/* canonicalize the directory name */
if (!trust_path)
- error = canonicalize_directory_path(index, entry);
+ error = canonicalize_directory_path(index, entry, best);
/* look for tree / blob name collisions, removing conflicts if requested */
if (!error)
@@ -1654,7 +1701,7 @@ int git_index_conflict_add(git_index *index,
for (i = 0; i < 3; i++) {
if (entries[i] && !valid_filemode(entries[i]->mode)) {
giterr_set(GITERR_INDEX, "invalid filemode for stage %d entry",
- i);
+ i + 1);
return -1;
}
}
@@ -1681,7 +1728,7 @@ int git_index_conflict_add(git_index *index,
/* Make sure stage is correct */
GIT_IDXENTRY_STAGE_SET(entries[i], i + 1);
- if ((ret = index_insert(index, &entries[i], 0, true, true)) < 0)
+ if ((ret = index_insert(index, &entries[i], 1, true, true)) < 0)
goto on_error;
entries[i] = NULL; /* don't free if later entry fails */
@@ -1963,27 +2010,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,
@@ -1998,7 +2042,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;
@@ -2018,7 +2062,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;
@@ -2030,8 +2074,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);
}
@@ -2040,7 +2084,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);
@@ -2865,6 +2909,7 @@ int git_index_read_index(
{
git_vector new_entries = GIT_VECTOR_INIT,
remove_entries = GIT_VECTOR_INIT;
+ git_idxmap *new_entries_map = NULL;
git_iterator *index_iterator = NULL;
git_iterator *new_iterator = NULL;
git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT;
@@ -2874,9 +2919,15 @@ int git_index_read_index(
int error;
if ((error = git_vector_init(&new_entries, new_index->entries.length, index->entries._cmp)) < 0 ||
- (error = git_vector_init(&remove_entries, index->entries.length, NULL)) < 0)
+ (error = git_vector_init(&remove_entries, index->entries.length, NULL)) < 0 ||
+ (error = git_idxmap_alloc(&new_entries_map)) < 0)
goto done;
+ if (index->ignore_case)
+ kh_resize(idxicase, (khash_t(idxicase) *) new_entries_map, new_index->entries.length);
+ else
+ kh_resize(idx, new_entries_map, new_index->entries.length);
+
opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
if ((error = git_iterator_for_index(&index_iterator, index, &opts)) < 0 ||
@@ -2890,6 +2941,7 @@ int git_index_read_index(
goto done;
while (true) {
+ git_index_entry *add_entry = NULL, *remove_entry = NULL;
int diff;
if (old_entry && new_entry)
@@ -2902,27 +2954,37 @@ int git_index_read_index(
break;
if (diff < 0) {
- git_vector_insert(&remove_entries, (git_index_entry *)old_entry);
+ remove_entry = (git_index_entry *)old_entry;
} else if (diff > 0) {
- if ((error = index_entry_dup(&entry, index, new_entry)) < 0)
+ if ((error = index_entry_dup(&add_entry, index, new_entry)) < 0)
goto done;
-
- git_vector_insert(&new_entries, entry);
} else {
/* Path and stage are equal, if the OID is equal, keep it to
* keep the stat cache data.
*/
if (git_oid_equal(&old_entry->id, &new_entry->id)) {
- git_vector_insert(&new_entries, (git_index_entry *)old_entry);
+ add_entry = (git_index_entry *)old_entry;
} else {
- if ((error = index_entry_dup(&entry, index, new_entry)) < 0)
+ if ((error = index_entry_dup(&add_entry, index, new_entry)) < 0)
goto done;
- git_vector_insert(&new_entries, entry);
- git_vector_insert(&remove_entries, (git_index_entry *)old_entry);
+ remove_entry = (git_index_entry *)old_entry;
}
}
+ if (add_entry) {
+ if ((error = git_vector_insert(&new_entries, add_entry)) == 0)
+ INSERT_IN_MAP_EX(index, new_entries_map, add_entry, error);
+ }
+
+ if (remove_entry && !error)
+ error = git_vector_insert(&remove_entries, remove_entry);
+
+ if (error < 0) {
+ giterr_set(GITERR_INDEX, "failed to insert entry");
+ return error;
+ }
+
if (diff <= 0) {
if ((error = git_iterator_advance(&old_entry, index_iterator)) < 0 &&
error != GIT_ITEROVER)
@@ -2940,6 +3002,7 @@ int git_index_read_index(
git_index_reuc_clear(index);
git_vector_swap(&new_entries, &index->entries);
+ new_entries_map = git__swap(index->entries_map, new_entries_map);
git_vector_foreach(&remove_entries, i, entry) {
if (index->tree)
@@ -2951,6 +3014,7 @@ int git_index_read_index(
error = 0;
done:
+ git_idxmap_free(new_entries_map);
git_vector_free(&new_entries);
git_vector_free(&remove_entries);
git_iterator_free(index_iterator);
diff --git a/src/iterator.c b/src/iterator.c
index e3a2abf66..ee348de6e 100644
--- a/src/iterator.c
+++ b/src/iterator.c
@@ -567,7 +567,7 @@ static bool tree_iterator__pop_frame(tree_iterator *ti, bool final)
tree_iterator__move_to_next(ti, tf);
if (!final) { /* if final, don't bother to clean up */
- git_pool_free_array(&ti->pool, tf->n_entries, (void **)tf->entries);
+ // TODO: maybe free the pool so far?
git_buf_rtruncate_at_char(&ti->path, '/');
}
@@ -822,8 +822,9 @@ int git_iterator_for_tree(
if ((error = iterator__update_ignore_case((git_iterator *)ti, options ? options->flags : 0)) < 0)
goto fail;
- if ((error = git_pool_init(&ti->pool, sizeof(tree_iterator_entry),0)) < 0 ||
- (error = tree_iterator__create_root_frame(ti, tree)) < 0 ||
+ git_pool_init(&ti->pool, sizeof(tree_iterator_entry));
+
+ if ((error = tree_iterator__create_root_frame(ti, tree)) < 0 ||
(error = tree_iterator__push_frame(ti)) < 0) /* expand root now */
goto fail;
diff --git a/src/merge.c b/src/merge.c
index fc5088c82..bad5f9552 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -20,7 +20,6 @@
#include "diff.h"
#include "checkout.h"
#include "tree.h"
-#include "merge_file.h"
#include "blob.h"
#include "oid.h"
#include "index.h"
@@ -61,12 +60,6 @@ struct merge_diff_df_data {
git_merge_diff *prev_conflict;
};
-GIT_INLINE(int) merge_diff_detect_binary(
- bool *binary_out,
- git_repository *repo,
- const git_merge_diff *conflict);
-
-
/* Merge base computation */
int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, size_t length, const git_oid input_array[])
@@ -309,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;
@@ -383,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;
- git__free(tmp);
- tmp = next;
+ 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_vector_free(&redundant);
}
*out = result;
@@ -668,7 +809,6 @@ static int merge_conflict_resolve_automerge(
git_odb *odb = NULL;
git_oid automerge_oid;
int error = 0;
- bool binary = false;
assert(resolved && diff_list && conflict);
@@ -703,12 +843,6 @@ static int merge_conflict_resolve_automerge(
strcmp(conflict->ancestor_entry.path, conflict->their_entry.path) != 0)
return 0;
- /* Reject binary conflicts */
- if ((error = merge_diff_detect_binary(&binary, diff_list->repo, conflict)) < 0)
- return error;
- if (binary)
- return 0;
-
ancestor = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
&conflict->ancestor_entry : NULL;
ours = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
@@ -725,7 +859,7 @@ static int merge_conflict_resolve_automerge(
(error = git_odb_write(&automerge_oid, odb, result.ptr, result.len, GIT_OBJ_BLOB)) < 0)
goto done;
- if ((index_entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL)
+ if ((index_entry = git_pool_mallocz(&diff_list->pool, sizeof(git_index_entry))) == NULL)
GITERR_CHECK_ALLOC(index_entry);
index_entry->path = git_pool_strdup(&diff_list->pool, result.path);
@@ -1314,48 +1448,6 @@ GIT_INLINE(int) merge_diff_detect_type(
return 0;
}
-GIT_INLINE(int) merge_diff_detect_binary(
- bool *binary_out,
- git_repository *repo,
- const git_merge_diff *conflict)
-{
- git_blob *ancestor_blob = NULL, *our_blob = NULL, *their_blob = NULL;
- int error = 0;
- bool binary = false;
-
- if (GIT_MERGE_INDEX_ENTRY_ISFILE(conflict->ancestor_entry)) {
- if ((error = git_blob_lookup(&ancestor_blob, repo, &conflict->ancestor_entry.id)) < 0)
- goto done;
-
- binary = git_blob_is_binary(ancestor_blob);
- }
-
- if (!binary &&
- GIT_MERGE_INDEX_ENTRY_ISFILE(conflict->our_entry)) {
- if ((error = git_blob_lookup(&our_blob, repo, &conflict->our_entry.id)) < 0)
- goto done;
-
- binary = git_blob_is_binary(our_blob);
- }
-
- if (!binary &&
- GIT_MERGE_INDEX_ENTRY_ISFILE(conflict->their_entry)) {
- if ((error = git_blob_lookup(&their_blob, repo, &conflict->their_entry.id)) < 0)
- goto done;
-
- binary = git_blob_is_binary(their_blob);
- }
-
- *binary_out = binary;
-
-done:
- git_blob_free(ancestor_blob);
- git_blob_free(our_blob);
- git_blob_free(their_blob);
-
- return error;
-}
-
GIT_INLINE(int) index_entry_dup_pool(
git_index_entry *out,
git_pool *pool,
@@ -1363,7 +1455,6 @@ GIT_INLINE(int) index_entry_dup_pool(
{
if (src != NULL) {
memcpy(out, src, sizeof(git_index_entry));
-
if ((out->path = git_pool_strdup(pool, src->path)) == NULL)
return -1;
}
@@ -1399,7 +1490,7 @@ static git_merge_diff *merge_diff_from_index_entries(
git_merge_diff *conflict;
git_pool *pool = &diff_list->pool;
- if ((conflict = git_pool_malloc(pool, sizeof(git_merge_diff))) == NULL)
+ if ((conflict = git_pool_mallocz(pool, sizeof(git_merge_diff))) == NULL)
return NULL;
if (index_entry_dup_pool(&conflict->ancestor_entry, pool, entries[TREE_IDX_ANCESTOR]) < 0 ||
@@ -1498,10 +1589,11 @@ git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo)
diff_list->repo = repo;
+ git_pool_init(&diff_list->pool, 1);
+
if (git_vector_init(&diff_list->staged, 0, NULL) < 0 ||
git_vector_init(&diff_list->conflicts, 0, NULL) < 0 ||
- git_vector_init(&diff_list->resolved, 0, NULL) < 0 ||
- git_pool_init(&diff_list->pool, 1, 0) < 0) {
+ git_vector_init(&diff_list->resolved, 0, NULL) < 0) {
git_merge_diff_list__free(diff_list);
return NULL;
}
@@ -1597,7 +1689,45 @@ static int merge_index_insert_reuc(
mode[0], oid[0], mode[1], oid[1], mode[2], oid[2]);
}
-int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list)
+static int index_update_reuc(git_index *index, git_merge_diff_list *diff_list)
+{
+ int error;
+ size_t i;
+ git_merge_diff *conflict;
+
+ /* Add each entry in the resolved conflict to the REUC independently, since
+ * the paths may differ due to renames. */
+ git_vector_foreach(&diff_list->resolved, i, conflict) {
+ const git_index_entry *ancestor =
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
+ &conflict->ancestor_entry : NULL;
+
+ const git_index_entry *ours =
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
+ &conflict->our_entry : NULL;
+
+ const git_index_entry *theirs =
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
+ &conflict->their_entry : NULL;
+
+ if (ancestor != NULL &&
+ (error = merge_index_insert_reuc(index, TREE_IDX_ANCESTOR, ancestor)) < 0)
+ return error;
+
+ if (ours != NULL &&
+ (error = merge_index_insert_reuc(index, TREE_IDX_OURS, ours)) < 0)
+ return error;
+
+ if (theirs != NULL &&
+ (error = merge_index_insert_reuc(index, TREE_IDX_THEIRS, theirs)) < 0)
+ return error;
+ }
+
+ return 0;
+}
+
+static int index_from_diff_list(git_index **out,
+ git_merge_diff_list *diff_list, bool skip_reuc)
{
git_index *index;
size_t i;
@@ -1656,31 +1786,8 @@ int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list)
}
}
- /* Add each entry in the resolved conflict to the REUC independently, since
- * the paths may differ due to renames. */
- git_vector_foreach(&diff_list->resolved, i, conflict) {
- const git_index_entry *ancestor =
- GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
- &conflict->ancestor_entry : NULL;
-
- const git_index_entry *ours =
- GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
- &conflict->our_entry : NULL;
-
- const git_index_entry *theirs =
- GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
- &conflict->their_entry : NULL;
-
- if (ancestor != NULL &&
- (error = merge_index_insert_reuc(index, TREE_IDX_ANCESTOR, ancestor)) < 0)
- goto on_error;
-
- if (ours != NULL &&
- (error = merge_index_insert_reuc(index, TREE_IDX_OURS, ours)) < 0)
- goto on_error;
-
- if (theirs != NULL &&
- (error = merge_index_insert_reuc(index, TREE_IDX_THEIRS, theirs)) < 0)
+ if (!skip_reuc) {
+ if ((error = index_update_reuc(index, diff_list)) < 0)
goto on_error;
}
@@ -1689,7 +1796,6 @@ int index_from_diff_list(git_index **out, git_merge_diff_list *diff_list)
on_error:
git_index_free(index);
-
return error;
}
@@ -1757,16 +1863,24 @@ int git_merge__iterators(
if ((error = merge_conflict_resolve(&resolved, diff_list, conflict, opts.file_favor, opts.file_flags)) < 0)
goto done;
- if (!resolved)
+ if (!resolved) {
+ if ((opts.tree_flags & GIT_MERGE_TREE_FAIL_ON_CONFLICT)) {
+ giterr_set(GITERR_MERGE, "merge conflicts exist");
+ error = GIT_EMERGECONFLICT;
+ goto done;
+ }
+
git_vector_insert(&diff_list->conflicts, conflict);
+ }
}
+ error = index_from_diff_list(out, diff_list,
+ (opts.tree_flags & GIT_MERGE_TREE_SKIP_REUC));
+
+done:
if (!given_opts || !given_opts->metric)
git__free(opts.metric);
- error = index_from_diff_list(out, diff_list);
-
-done:
git_merge_diff_list__free(diff_list);
git_iterator_free(empty_ancestor);
git_iterator_free(empty_ours);
diff --git a/src/merge_file.c b/src/merge_file.c
index 6d89b089d..6d4738065 100644
--- a/src/merge_file.c
+++ b/src/merge_file.c
@@ -7,17 +7,23 @@
#include "common.h"
#include "repository.h"
-#include "merge_file.h"
#include "posix.h"
#include "fileops.h"
#include "index.h"
+#include "diff_xdiff.h"
#include "git2/repository.h"
#include "git2/object.h"
#include "git2/index.h"
+#include "git2/merge.h"
#include "xdiff/xdiff.h"
+/* only examine the first 8000 bytes for binaryness.
+ * https://github.com/git/git/blob/77bd3ea9f54f1584147b594abc04c26ca516d987/xdiff-interface.c#L197
+ */
+#define GIT_MERGE_FILE_BINARY_SIZE 8000
+
#define GIT_MERGE_FILE_SIDE_EXISTS(X) ((X)->mode != 0)
GIT_INLINE(const char *) merge_file_best_path(
@@ -100,7 +106,7 @@ static void merge_file_normalize_opts(
}
}
-static int git_merge_file__from_inputs(
+static int merge_file__xdiff(
git_merge_file_result *out,
const git_merge_file_input *ancestor,
const git_merge_file_input *ours,
@@ -189,6 +195,63 @@ done:
return error;
}
+static bool merge_file__is_binary(const git_merge_file_input *file)
+{
+ size_t len = file ? file->size : 0;
+
+ if (len > GIT_XDIFF_MAX_SIZE)
+ return true;
+ if (len > GIT_MERGE_FILE_BINARY_SIZE)
+ len = GIT_MERGE_FILE_BINARY_SIZE;
+
+ return len ? (memchr(file->ptr, 0, len) != NULL) : false;
+}
+
+static int merge_file__binary(
+ git_merge_file_result *out,
+ const git_merge_file_input *ours,
+ const git_merge_file_input *theirs,
+ const git_merge_file_options *given_opts)
+{
+ const git_merge_file_input *favored = NULL;
+
+ memset(out, 0x0, sizeof(git_merge_file_result));
+
+ if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_OURS)
+ favored = ours;
+ else if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_THEIRS)
+ favored = theirs;
+ else
+ goto done;
+
+ if ((out->path = git__strdup(favored->path)) == NULL ||
+ (out->ptr = git__malloc(favored->size)) == NULL)
+ goto done;
+
+ memcpy((char *)out->ptr, favored->ptr, favored->size);
+ out->len = favored->size;
+ out->mode = favored->mode;
+ out->automergeable = 1;
+
+done:
+ return 0;
+}
+
+static int merge_file__from_inputs(
+ git_merge_file_result *out,
+ const git_merge_file_input *ancestor,
+ const git_merge_file_input *ours,
+ const git_merge_file_input *theirs,
+ const git_merge_file_options *given_opts)
+{
+ if (merge_file__is_binary(ancestor) ||
+ merge_file__is_binary(ours) ||
+ merge_file__is_binary(theirs))
+ return merge_file__binary(out, ours, theirs, given_opts);
+
+ return merge_file__xdiff(out, ancestor, ours, theirs, given_opts);
+}
+
static git_merge_file_input *git_merge_file__normalize_inputs(
git_merge_file_input *out,
const git_merge_file_input *given)
@@ -223,7 +286,7 @@ int git_merge_file(
ours = git_merge_file__normalize_inputs(&inputs[1], ours);
theirs = git_merge_file__normalize_inputs(&inputs[2], theirs);
- return git_merge_file__from_inputs(out, ancestor, ours, theirs, options);
+ return merge_file__from_inputs(out, ancestor, ours, theirs, options);
}
int git_merge_file_from_index(
@@ -234,8 +297,8 @@ int git_merge_file_from_index(
const git_index_entry *theirs,
const git_merge_file_options *options)
{
- git_merge_file_input inputs[3] = { {0} },
- *ancestor_input = NULL, *our_input = NULL, *their_input = NULL;
+ git_merge_file_input *ancestor_ptr = NULL,
+ ancestor_input = {0}, our_input = {0}, their_input = {0};
git_odb *odb = NULL;
git_odb_object *odb_object[3] = { 0 };
int error = 0;
@@ -249,27 +312,20 @@ int git_merge_file_from_index(
if (ancestor) {
if ((error = git_merge_file__input_from_index(
- &inputs[0], &odb_object[0], odb, ancestor)) < 0)
+ &ancestor_input, &odb_object[0], odb, ancestor)) < 0)
goto done;
- ancestor_input = &inputs[0];
+ ancestor_ptr = &ancestor_input;
}
if ((error = git_merge_file__input_from_index(
- &inputs[1], &odb_object[1], odb, ours)) < 0)
- goto done;
-
- our_input = &inputs[1];
-
- if ((error = git_merge_file__input_from_index(
- &inputs[2], &odb_object[2], odb, theirs)) < 0)
+ &our_input, &odb_object[1], odb, ours)) < 0 ||
+ (error = git_merge_file__input_from_index(
+ &their_input, &odb_object[2], odb, theirs)) < 0)
goto done;
- their_input = &inputs[2];
-
- if ((error = git_merge_file__from_inputs(out,
- ancestor_input, our_input, their_input, options)) < 0)
- goto done;
+ error = merge_file__from_inputs(out,
+ ancestor_ptr, &our_input, &their_input, options);
done:
git_odb_object_free(odb_object[0]);
@@ -286,7 +342,5 @@ void git_merge_file_result_free(git_merge_file_result *result)
return;
git__free((char *)result->path);
-
- /* xdiff uses malloc() not git_malloc, so we use free(), not git_free() */
- free((char *)result->ptr);
+ git__free((char *)result->ptr);
}
diff --git a/src/merge_file.h b/src/merge_file.h
deleted file mode 100644
index 263391ee3..000000000
--- a/src/merge_file.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright (C) the libgit2 contributors. All rights reserved.
- *
- * This file is part of libgit2, distributed under the GNU GPL v2 with
- * a Linking Exception. For full terms see the included COPYING file.
- */
-#ifndef INCLUDE_filediff_h__
-#define INCLUDE_filediff_h__
-
-#include "xdiff/xdiff.h"
-
-#include "git2/merge.h"
-
-#endif
diff --git a/src/odb.c b/src/odb.c
index 2b2c35fe8..1c877c9fc 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)
@@ -620,23 +624,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);
}
@@ -644,43 +643,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)
@@ -697,13 +698,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;
@@ -783,36 +824,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)
@@ -822,42 +865,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;
@@ -878,7 +927,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;
@@ -887,6 +936,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_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/pack-objects.c b/src/pack-objects.c
index c4c061a3a..fd181fc5e 100644
--- a/src/pack-objects.c
+++ b/src/pack-objects.c
@@ -135,8 +135,7 @@ int git_packbuilder_new(git_packbuilder **out, git_repository *repo)
if (!pb->walk_objects)
goto on_error;
- if (git_pool_init(&pb->object_pool, sizeof(git_walk_object), 0) < 0)
- goto on_error;
+ git_pool_init(&pb->object_pool, sizeof(git_walk_object));
pb->repo = repo;
pb->nr_threads = 1; /* do not spawn any thread by default */
diff --git a/src/pathspec.c b/src/pathspec.c
index 9304da705..5bb69ec4b 100644
--- a/src/pathspec.c
+++ b/src/pathspec.c
@@ -237,9 +237,9 @@ int git_pathspec__init(git_pathspec *ps, const git_strarray *paths)
memset(ps, 0, sizeof(*ps));
ps->prefix = git_pathspec_prefix(paths);
+ git_pool_init(&ps->pool, 1);
- if ((error = git_pool_init(&ps->pool, 1, 0)) < 0 ||
- (error = git_pathspec__vinit(&ps->pathspec, paths, &ps->pool)) < 0)
+ if ((error = git_pathspec__vinit(&ps->pathspec, paths, &ps->pool)) < 0)
git_pathspec__clear(ps);
return error;
@@ -312,15 +312,11 @@ static git_pathspec_match_list *pathspec_match_alloc(
git_pathspec *ps, int datatype)
{
git_pathspec_match_list *m = git__calloc(1, sizeof(git_pathspec_match_list));
-
- if (m != NULL && git_pool_init(&m->pool, 1, 0) < 0) {
- pathspec_match_free(m);
- m = NULL;
- }
-
if (!m)
return NULL;
+ git_pool_init(&m->pool, 1);
+
/* need to keep reference to pathspec and increment refcount because
* failures array stores pointers to the pattern strings of the
* pathspec that had no matches
diff --git a/src/pool.c b/src/pool.c
index c93d78182..aab63beee 100644
--- a/src/pool.c
+++ b/src/pool.c
@@ -8,67 +8,49 @@ struct git_pool_page {
git_pool_page *next;
uint32_t size;
uint32_t avail;
- GIT_ALIGN(char data[GIT_FLEX_ARRAY], 8);
+ char data[GIT_FLEX_ARRAY];
};
-struct pool_freelist {
- struct pool_freelist *next;
-};
+static void *pool_alloc_page(git_pool *pool, uint32_t size);
-#define GIT_POOL_MIN_USABLE 4
-#define GIT_POOL_MIN_PAGESZ 2 * sizeof(void*)
+uint32_t git_pool__system_page_size(void)
+{
+ static uint32_t size = 0;
-static void *pool_alloc_page(git_pool *pool, uint32_t size);
-static void pool_insert_page(git_pool *pool, git_pool_page *page);
+ if (!size) {
+ size_t page_size;
+ if (git__page_size(&page_size) < 0)
+ page_size = 4096;
+ /* allow space for malloc overhead */
+ size = page_size - (2 * sizeof(void *)) - sizeof(git_pool_page);
+ }
-int git_pool_init(
- git_pool *pool, uint32_t item_size, uint32_t items_per_page)
+ return size;
+}
+
+void git_pool_init(git_pool *pool, uint32_t item_size)
{
+ const uint32_t align_size = sizeof(void *) - 1;
assert(pool);
- if (!item_size)
- item_size = 1;
- /* round up item_size for decent object alignment */
- if (item_size > 4)
- item_size = (item_size + 7) & ~7;
- else if (item_size == 3)
- item_size = 4;
-
- if (!items_per_page)
- items_per_page = git_pool__suggest_items_per_page(item_size);
- if (item_size * items_per_page < GIT_POOL_MIN_PAGESZ)
- items_per_page = (GIT_POOL_MIN_PAGESZ + item_size - 1) / item_size;
+ if (item_size > 1)
+ item_size = (item_size + align_size) & ~align_size;
memset(pool, 0, sizeof(git_pool));
pool->item_size = item_size;
- pool->page_size = item_size * items_per_page;
-
- return 0;
+ pool->page_size = git_pool__system_page_size();
}
void git_pool_clear(git_pool *pool)
{
git_pool_page *scan, *next;
- for (scan = pool->open; scan != NULL; scan = next) {
+ for (scan = pool->pages; scan != NULL; scan = next) {
next = scan->next;
git__free(scan);
}
- pool->open = NULL;
- for (scan = pool->full; scan != NULL; scan = next) {
- next = scan->next;
- git__free(scan);
- }
- pool->full = NULL;
-
- pool->free_list = NULL;
-
- pool->items = 0;
-
- pool->has_string_alloc = 0;
- pool->has_multi_item_alloc = 0;
- pool->has_large_page_alloc = 0;
+ pool->pages = NULL;
}
void git_pool_swap(git_pool *a, git_pool *b)
@@ -83,110 +65,51 @@ void git_pool_swap(git_pool *a, git_pool *b)
memcpy(b, &temp, sizeof(temp));
}
-static void pool_insert_page(git_pool *pool, git_pool_page *page)
-{
- git_pool_page *scan;
-
- /* If there are no open pages or this page has the most open space,
- * insert it at the beginning of the list. This is the common case.
- */
- if (pool->open == NULL || pool->open->avail < page->avail) {
- page->next = pool->open;
- pool->open = page;
- return;
- }
-
- /* Otherwise insert into sorted position. */
- for (scan = pool->open;
- scan->next && scan->next->avail > page->avail;
- scan = scan->next);
- page->next = scan->next;
- scan->next = page;
-}
-
static void *pool_alloc_page(git_pool *pool, uint32_t size)
{
git_pool_page *page;
- uint32_t new_page_size;
+ const uint32_t new_page_size = (size <= pool->page_size) ? pool->page_size : size;
size_t alloc_size;
- if (size <= pool->page_size)
- new_page_size = pool->page_size;
- else {
- new_page_size = size;
- pool->has_large_page_alloc = 1;
- }
-
if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, new_page_size, sizeof(git_pool_page)) ||
- !(page = git__calloc(1, alloc_size)))
+ !(page = git__malloc(alloc_size)))
return NULL;
- page->size = new_page_size;
+ page->size = new_page_size;
page->avail = new_page_size - size;
+ page->next = pool->pages;
- if (page->avail > 0)
- pool_insert_page(pool, page);
- else {
- page->next = pool->full;
- pool->full = page;
- }
-
- pool->items++;
+ pool->pages = page;
return page->data;
}
-GIT_INLINE(void) pool_remove_page(
- git_pool *pool, git_pool_page *page, git_pool_page *prev)
+static void *pool_alloc(git_pool *pool, uint32_t size)
{
- if (prev == NULL)
- pool->open = page->next;
- else
- prev->next = page->next;
-}
-
-void *git_pool_malloc(git_pool *pool, uint32_t items)
-{
- git_pool_page *scan = pool->open, *prev;
- uint32_t size = ((items * pool->item_size) + 7) & ~7;
+ git_pool_page *page = pool->pages;
void *ptr = NULL;
- pool->has_string_alloc = 0;
- if (items > 1)
- pool->has_multi_item_alloc = 1;
- else if (pool->free_list != NULL) {
- ptr = pool->free_list;
- pool->free_list = ((struct pool_freelist *)pool->free_list)->next;
- return ptr;
- }
-
- /* just add a block if there is no open one to accommodate this */
- if (size >= pool->page_size || !scan || scan->avail < size)
+ if (!page || page->avail < size)
return pool_alloc_page(pool, size);
- pool->items++;
-
- /* find smallest block in free list with space */
- for (scan = pool->open, prev = NULL;
- scan->next && scan->next->avail >= size;
- prev = scan, scan = scan->next);
+ ptr = &page->data[page->size - page->avail];
+ page->avail -= size;
- /* allocate space from the block */
- ptr = &scan->data[scan->size - scan->avail];
- scan->avail -= size;
+ return ptr;
+}
- /* move to full list if there is almost no space left */
- if (scan->avail < pool->item_size || scan->avail < GIT_POOL_MIN_USABLE) {
- pool_remove_page(pool, scan, prev);
- scan->next = pool->full;
- pool->full = scan;
- }
- /* reorder list if block is now smaller than the one after it */
- else if (scan->next != NULL && scan->next->avail > scan->avail) {
- pool_remove_page(pool, scan, prev);
- pool_insert_page(pool, scan);
- }
+void *git_pool_malloc(git_pool *pool, uint32_t items)
+{
+ const uint32_t size = items * pool->item_size;
+ return pool_alloc(pool, size);
+}
+void *git_pool_mallocz(git_pool *pool, uint32_t items)
+{
+ const uint32_t size = items * pool->item_size;
+ void *ptr = pool_alloc(pool, size);
+ if (ptr)
+ memset(ptr, 0x0, size);
return ptr;
}
@@ -204,15 +127,12 @@ char *git_pool_strndup(git_pool *pool, const char *str, size_t n)
ptr[n] = '\0';
}
- pool->has_string_alloc = 1;
-
return ptr;
}
char *git_pool_strdup(git_pool *pool, const char *str)
{
assert(pool && str && pool->item_size == sizeof(char));
-
return git_pool_strndup(pool, str, strlen(str));
}
@@ -238,88 +158,23 @@ char *git_pool_strcat(git_pool *pool, const char *a, const char *b)
memcpy(((char *)ptr) + len_a, b, len_b);
*(((char *)ptr) + len_a + len_b) = '\0';
}
- pool->has_string_alloc = 1;
-
return ptr;
}
-void git_pool_free(git_pool *pool, void *ptr)
-{
- struct pool_freelist *item = ptr;
-
- assert(pool && pool->item_size >= sizeof(void*));
-
- if (item) {
- item->next = pool->free_list;
- pool->free_list = item;
- }
-}
-
-void git_pool_free_array(git_pool *pool, size_t count, void **ptrs)
-{
- struct pool_freelist **items = (struct pool_freelist **)ptrs;
- size_t i;
-
- assert(pool && ptrs && pool->item_size >= sizeof(void*));
-
- if (!count)
- return;
-
- for (i = count - 1; i > 0; --i)
- items[i]->next = items[i - 1];
-
- items[i]->next = pool->free_list;
- pool->free_list = items[count - 1];
-}
-
uint32_t git_pool__open_pages(git_pool *pool)
{
uint32_t ct = 0;
git_pool_page *scan;
- for (scan = pool->open; scan != NULL; scan = scan->next) ct++;
- return ct;
-}
-
-uint32_t git_pool__full_pages(git_pool *pool)
-{
- uint32_t ct = 0;
- git_pool_page *scan;
- for (scan = pool->full; scan != NULL; scan = scan->next) ct++;
+ for (scan = pool->pages; scan != NULL; scan = scan->next) ct++;
return ct;
}
bool git_pool__ptr_in_pool(git_pool *pool, void *ptr)
{
git_pool_page *scan;
- for (scan = pool->open; scan != NULL; scan = scan->next)
- if ((void *)scan->data <= ptr &&
- (void *)(((char *)scan->data) + scan->size) > ptr)
- return true;
- for (scan = pool->full; scan != NULL; scan = scan->next)
+ for (scan = pool->pages; scan != NULL; scan = scan->next)
if ((void *)scan->data <= ptr &&
(void *)(((char *)scan->data) + scan->size) > ptr)
return true;
return false;
}
-
-uint32_t git_pool__system_page_size(void)
-{
- static uint32_t size = 0;
-
- if (!size) {
- size_t page_size;
- if (git__page_size(&page_size) < 0)
- page_size = 4096;
- size = page_size - 2 * sizeof(void *); /* allow space for malloc overhead */
- }
-
- return size;
-}
-
-uint32_t git_pool__suggest_items_per_page(uint32_t item_size)
-{
- uint32_t page_bytes =
- git_pool__system_page_size() - sizeof(git_pool_page);
- return page_bytes / item_size;
-}
-
diff --git a/src/pool.h b/src/pool.h
index b0007f315..d16bd349a 100644
--- a/src/pool.h
+++ b/src/pool.h
@@ -28,37 +28,28 @@ typedef struct git_pool_page git_pool_page;
* For examples of how to set up a `git_pool` see `git_pool_init`.
*/
typedef struct {
- git_pool_page *open; /* pages with space left */
- git_pool_page *full; /* pages with no space left */
- void *free_list; /* optional: list of freed blocks */
+ git_pool_page *pages; /* allocated pages */
uint32_t item_size; /* size of single alloc unit in bytes */
uint32_t page_size; /* size of page in bytes */
- uint32_t items;
- unsigned has_string_alloc : 1; /* was the strdup function used */
- unsigned has_multi_item_alloc : 1; /* was items ever > 1 in malloc */
- unsigned has_large_page_alloc : 1; /* are any pages > page_size */
} git_pool;
-#define GIT_POOL_INIT_STRINGPOOL { 0, 0, 0, 1, 4000, 0, 0, 0, 0 }
-
/**
* Initialize a pool.
*
* To allocation strings, use like this:
*
- * git_pool_init(&string_pool, 1, 0);
+ * git_pool_init(&string_pool, 1);
* my_string = git_pool_strdup(&string_pool, your_string);
*
* To allocate items of fixed size, use like this:
*
- * git_pool_init(&pool, sizeof(item), 0);
+ * git_pool_init(&pool, sizeof(item));
* my_item = git_pool_malloc(&pool, 1);
*
* Of course, you can use this in other ways, but those are the
* two most common patterns.
*/
-extern int git_pool_init(
- git_pool *pool, uint32_t item_size, uint32_t items_per_page);
+extern void git_pool_init(git_pool *pool, uint32_t item_size);
/**
* Free all items in pool
@@ -74,17 +65,7 @@ extern void git_pool_swap(git_pool *a, git_pool *b);
* Allocate space for one or more items from a pool.
*/
extern void *git_pool_malloc(git_pool *pool, uint32_t items);
-
-/**
- * Allocate space and zero it out.
- */
-GIT_INLINE(void *) git_pool_mallocz(git_pool *pool, uint32_t items)
-{
- void *ptr = git_pool_malloc(pool, items);
- if (ptr)
- memset(ptr, 0, (size_t)items * (size_t)pool->item_size);
- return ptr;
-}
+extern void *git_pool_mallocz(git_pool *pool, uint32_t items);
/**
* Allocate space and duplicate string data into it.
@@ -114,35 +95,10 @@ extern char *git_pool_strdup_safe(git_pool *pool, const char *str);
*/
extern char *git_pool_strcat(git_pool *pool, const char *a, const char *b);
-/**
- * Push a block back onto the free list for the pool.
- *
- * This is allowed only if the item_size is >= sizeof(void*).
- *
- * In some cases, it is helpful to "release" an allocated block
- * for reuse. Pools don't support a general purpose free, but
- * they will keep a simple free blocks linked list provided the
- * native block size is large enough to hold a void pointer
- */
-extern void git_pool_free(git_pool *pool, void *ptr);
-
-/**
- * Push an array of pool allocated blocks efficiently onto the free list.
- *
- * This has the same constraints as `git_pool_free()` above.
- */
-extern void git_pool_free_array(git_pool *pool, size_t count, void **ptrs);
-
/*
* Misc utilities
*/
-
extern uint32_t git_pool__open_pages(git_pool *pool);
-
-extern uint32_t git_pool__full_pages(git_pool *pool);
-
extern bool git_pool__ptr_in_pool(git_pool *pool, void *ptr);
-extern uint32_t git_pool__suggest_items_per_page(uint32_t item_size);
-
#endif
diff --git a/src/refdb_fs.c b/src/refdb_fs.c
index 921f7862b..85b5034d6 100644
--- a/src/refdb_fs.c
+++ b/src/refdb_fs.c
@@ -626,8 +626,9 @@ static int refdb_fs_backend__iterator(
iter = git__calloc(1, sizeof(refdb_fs_iter));
GITERR_CHECK_ALLOC(iter);
- if (git_pool_init(&iter->pool, 1, 0) < 0 ||
- git_vector_init(&iter->loose, 8, NULL) < 0)
+ git_pool_init(&iter->pool, 1);
+
+ if (git_vector_init(&iter->loose, 8, NULL) < 0)
goto fail;
if (glob != NULL &&
@@ -732,8 +733,11 @@ static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *
error = git_filebuf_open(file, ref_path.ptr, GIT_FILEBUF_FORCE, GIT_REFS_FILE_MODE);
+ if (error == GIT_EDIRECTORY)
+ giterr_set(GITERR_REFERENCE, "cannot lock ref '%s', there are refs beneath that folder", name);
+
git_buf_free(&ref_path);
- return error;
+ return error;
}
static int loose_commit(git_filebuf *file, const git_reference *ref)
@@ -1463,7 +1467,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)
@@ -1784,10 +1788,17 @@ static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, co
/* If the new branch matches part of the namespace of a previously deleted branch,
* there maybe an obsolete/unused directory (or directory hierarchy) in the way.
*/
- if (git_path_isdir(git_buf_cstr(&path)) &&
- (git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) {
- error = -1;
- goto cleanup;
+ if (git_path_isdir(git_buf_cstr(&path))) {
+ if ((git_futils_rmdir_r(git_buf_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0))
+ error = -1;
+ else if (git_path_isdir(git_buf_cstr(&path))) {
+ giterr_set(GITERR_REFERENCE, "cannot create reflog at '%s', there are reflogs beneath that folder",
+ ref->name);
+ error = GIT_EDIRECTORY;
+ }
+
+ if (error != 0)
+ goto cleanup;
}
error = git_futils_writebuffer(&buf, git_buf_cstr(&path), O_WRONLY|O_CREAT|O_APPEND, GIT_REFLOG_FILE_MODE);
diff --git a/src/repository.c b/src/repository.c
index 77145cfc8..c61d0e4f0 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -585,7 +585,8 @@ static int load_config(
git_repository *repo,
const char *global_config_path,
const char *xdg_config_path,
- const char *system_config_path)
+ const char *system_config_path,
+ const char *programdata_path)
{
int error;
git_buf config_path = GIT_BUF_INIT;
@@ -626,6 +627,12 @@ static int load_config(
error != GIT_ENOTFOUND)
goto on_error;
+ if (programdata_path != NULL &&
+ (error = git_config_add_file_ondisk(
+ cfg, programdata_path, GIT_CONFIG_LEVEL_PROGRAMDATA, 0)) < 0 &&
+ error != GIT_ENOTFOUND)
+ goto on_error;
+
giterr_clear(); /* clear any lingering ENOTFOUND errors */
*out = cfg;
@@ -651,11 +658,13 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo)
git_buf global_buf = GIT_BUF_INIT;
git_buf xdg_buf = GIT_BUF_INIT;
git_buf system_buf = GIT_BUF_INIT;
+ git_buf programdata_buf = GIT_BUF_INIT;
git_config *config;
git_config_find_global(&global_buf);
git_config_find_xdg(&xdg_buf);
git_config_find_system(&system_buf);
+ git_config_find_programdata(&programdata_buf);
/* If there is no global file, open a backend for it anyway */
if (git_buf_len(&global_buf) == 0)
@@ -665,7 +674,8 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo)
&config, repo,
path_unless_empty(&global_buf),
path_unless_empty(&xdg_buf),
- path_unless_empty(&system_buf));
+ path_unless_empty(&system_buf),
+ path_unless_empty(&programdata_buf));
if (!error) {
GIT_REFCOUNT_OWN(config, repo);
@@ -679,6 +689,7 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo)
git_buf_free(&global_buf);
git_buf_free(&xdg_buf);
git_buf_free(&system_buf);
+ git_buf_free(&programdata_buf);
}
*out = repo->_config;
diff --git a/src/revwalk.c b/src/revwalk.c
index dcdd97915..89279ed1f 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -535,12 +535,10 @@ int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo)
walk->commits = git_oidmap_alloc();
GITERR_CHECK_ALLOC(walk->commits);
- if (git_pqueue_init(
- &walk->iterator_time, 0, 8, git_commit_list_time_cmp) < 0 ||
- git_pool_init(&walk->commit_pool, 1,
- git_pool__suggest_items_per_page(COMMIT_ALLOC) * COMMIT_ALLOC) < 0)
+ if (git_pqueue_init(&walk->iterator_time, 0, 8, git_commit_list_time_cmp) < 0)
return -1;
+ git_pool_init(&walk->commit_pool, COMMIT_ALLOC);
walk->get_next = &revwalk_next_unsorted;
walk->enqueue = &revwalk_enqueue_unsorted;
diff --git a/src/settings.c b/src/settings.c
index 22e55953a..91bdfd520 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -60,6 +60,13 @@ static int config_level_to_sysdir(int config_level)
return val;
}
+extern char *git__user_agent;
+
+const char *git_libgit2__user_agent()
+{
+ return git__user_agent;
+}
+
int git_libgit2_opts(int key, ...)
{
int error = 0;
@@ -156,6 +163,15 @@ int git_libgit2_opts(int key, ...)
error = -1;
#endif
break;
+ case GIT_OPT_SET_USER_AGENT:
+ git__free(git__user_agent);
+ git__user_agent = git__strdup(va_arg(ap, const char *));
+ if (!git__user_agent) {
+ giterr_set_oom();
+ error = -1;
+ }
+
+ break;
}
va_end(ap);
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/sortedcache.c b/src/sortedcache.c
index 115175724..5c2a167a7 100644
--- a/src/sortedcache.c
+++ b/src/sortedcache.c
@@ -20,8 +20,9 @@ int git_sortedcache_new(
sc = git__calloc(1, alloclen);
GITERR_CHECK_ALLOC(sc);
- if (git_pool_init(&sc->pool, 1, 0) < 0 ||
- git_vector_init(&sc->items, 4, item_cmp) < 0 ||
+ git_pool_init(&sc->pool, 1);
+
+ if (git_vector_init(&sc->items, 4, item_cmp) < 0 ||
git_strmap_alloc(&sc->map) < 0)
goto fail;
diff --git a/src/stream.h b/src/stream.h
index 43fcc3045..4692c7115 100644
--- a/src/stream.h
+++ b/src/stream.h
@@ -62,6 +62,9 @@ GIT_INLINE(int) git_stream_close(git_stream *st)
GIT_INLINE(void) git_stream_free(git_stream *st)
{
+ if (!st)
+ return;
+
st->free(st);
}
diff --git a/src/submodule.c b/src/submodule.c
index 3fd338843..1148f8790 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -1423,7 +1423,6 @@ static int submodule_update_head(git_submodule *submodule)
return 0;
}
-
int git_submodule_reload(git_submodule *sm, int force)
{
int error = 0;
@@ -1433,35 +1432,30 @@ int git_submodule_reload(git_submodule *sm, int force)
assert(sm);
- /* refresh index data */
- if ((error = submodule_update_index(sm)) < 0)
- return error;
-
- /* refresh HEAD tree data */
- if ((error = submodule_update_head(sm)) < 0)
- return error;
+ if (!git_repository_is_bare(sm->repo)) {
+ /* refresh config data */
+ mods = gitmodules_snapshot(sm->repo);
+ if (mods != NULL) {
+ error = submodule_read_config(sm, mods);
+ git_config_free(mods);
- /* done if bare */
- if (git_repository_is_bare(sm->repo))
- return error;
+ if (error < 0)
+ return error;
+ }
- /* refresh config data */
- mods = gitmodules_snapshot(sm->repo);
- if (mods != NULL) {
- error = submodule_read_config(sm, mods);
- git_config_free(mods);
+ /* refresh wd data */
+ sm->flags &=
+ ~(GIT_SUBMODULE_STATUS_IN_WD |
+ GIT_SUBMODULE_STATUS__WD_OID_VALID |
+ GIT_SUBMODULE_STATUS__WD_FLAGS);
- if (error < 0) {
- return error;
- }
+ error = submodule_load_from_wd_lite(sm);
}
- /* refresh wd data */
- sm->flags &=
- ~(GIT_SUBMODULE_STATUS_IN_WD | GIT_SUBMODULE_STATUS__WD_OID_VALID |
- GIT_SUBMODULE_STATUS__WD_FLAGS);
+ if (error == 0 && (error = submodule_update_index(sm)) == 0)
+ error = submodule_update_head(sm);
- return submodule_load_from_wd_lite(sm);
+ return error;
}
static void submodule_copy_oid_maybe(
diff --git a/src/sysdir.c b/src/sysdir.c
index 2795de491..bf53d830f 100644
--- a/src/sysdir.c
+++ b/src/sysdir.c
@@ -15,6 +15,16 @@
#include "win32/findfile.h"
#endif
+static int git_sysdir_guess_programdata_dirs(git_buf *out)
+{
+#ifdef GIT_WIN32
+ return git_win32__find_programdata_dirs(out);
+#else
+ git_buf_clear(out);
+ return 0;
+#endif
+}
+
static int git_sysdir_guess_system_dirs(git_buf *out)
{
#ifdef GIT_WIN32
@@ -76,12 +86,13 @@ static int git_sysdir_guess_template_dirs(git_buf *out)
typedef int (*git_sysdir_guess_cb)(git_buf *out);
static git_buf git_sysdir__dirs[GIT_SYSDIR__MAX] =
- { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT };
+ { GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT, GIT_BUF_INIT };
static git_sysdir_guess_cb git_sysdir__dir_guess[GIT_SYSDIR__MAX] = {
git_sysdir_guess_system_dirs,
git_sysdir_guess_global_dirs,
git_sysdir_guess_xdg_dirs,
+ git_sysdir_guess_programdata_dirs,
git_sysdir_guess_template_dirs,
};
@@ -258,6 +269,12 @@ int git_sysdir_find_xdg_file(git_buf *path, const char *filename)
path, filename, GIT_SYSDIR_XDG, "global/xdg");
}
+int git_sysdir_find_programdata_file(git_buf *path, const char *filename)
+{
+ return git_sysdir_find_in_dirlist(
+ path, filename, GIT_SYSDIR_PROGRAMDATA, "ProgramData");
+}
+
int git_sysdir_find_template_dir(git_buf *path)
{
return git_sysdir_find_in_dirlist(
diff --git a/src/sysdir.h b/src/sysdir.h
index f1bbf0bae..12874fc85 100644
--- a/src/sysdir.h
+++ b/src/sysdir.h
@@ -39,6 +39,15 @@ extern int git_sysdir_find_xdg_file(git_buf *path, const char *filename);
extern int git_sysdir_find_system_file(git_buf *path, const char *filename);
/**
+ * Find a "ProgramData" file (i.e. one in %PROGRAMDATA%)
+ *
+ * @param path buffer to write the full path into
+ * @param filename name of file to find in the ProgramData directory
+ * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
+ */
+extern int git_sysdir_find_programdata_file(git_buf *path, const char *filename);
+
+/**
* Find template directory.
*
* @param path buffer to write the full path into
@@ -50,8 +59,9 @@ typedef enum {
GIT_SYSDIR_SYSTEM = 0,
GIT_SYSDIR_GLOBAL = 1,
GIT_SYSDIR_XDG = 2,
- GIT_SYSDIR_TEMPLATE = 3,
- GIT_SYSDIR__MAX = 4,
+ GIT_SYSDIR_PROGRAMDATA = 3,
+ GIT_SYSDIR_TEMPLATE = 4,
+ GIT_SYSDIR__MAX = 5,
} git_sysdir_t;
/**
diff --git a/src/tls_stream.c b/src/tls_stream.c
index 39a8ce343..83e2d064a 100644
--- a/src/tls_stream.c
+++ b/src/tls_stream.c
@@ -11,8 +11,21 @@
#include "openssl_stream.h"
#include "stransport_stream.h"
+static git_stream_cb tls_ctor;
+
+int git_stream_register_tls(git_stream_cb ctor)
+{
+ tls_ctor = ctor;
+
+ return 0;
+}
+
int git_tls_stream_new(git_stream **out, const char *host, const char *port)
{
+
+ if (tls_ctor)
+ return tls_ctor(out, host, port);
+
#ifdef GIT_SECURE_TRANSPORT
return git_stransport_stream_new(out, host, port);
#elif defined(GIT_OPENSSL)
diff --git a/src/transaction.c b/src/transaction.c
index 92e134e5b..2c8a1e8bd 100644
--- a/src/transaction.c
+++ b/src/transaction.c
@@ -77,8 +77,7 @@ int git_transaction_new(git_transaction **out, git_repository *repo)
assert(out && repo);
- if ((error = git_pool_init(&pool, 1, 0)) < 0)
- return error;
+ git_pool_init(&pool, 1);
tx = git_pool_mallocz(&pool, sizeof(git_transaction));
if (!tx) {
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/http.c b/src/transports/http.c
index e5f2b9f28..88b124bf7 100644
--- a/src/transports/http.c
+++ b/src/transports/http.c
@@ -10,6 +10,7 @@
#include "http_parser.h"
#include "buffer.h"
#include "netops.h"
+#include "global.h"
#include "remote.h"
#include "smart.h"
#include "auth.h"
@@ -186,6 +187,16 @@ static int apply_credentials(git_buf *buf, http_subtransport *t)
return context->next_token(buf, context, cred);
}
+static const char *user_agent(void)
+{
+ const char *custom = git_libgit2__user_agent();
+
+ if (custom)
+ return custom;
+
+ return "libgit2 " LIBGIT2_VERSION;
+}
+
static int gen_request(
git_buf *buf,
http_stream *s,
@@ -197,7 +208,7 @@ static int gen_request(
git_buf_printf(buf, "%s %s%s HTTP/1.1\r\n", s->verb, path, s->service_url);
- git_buf_puts(buf, "User-Agent: git/1.0 (libgit2 " LIBGIT2_VERSION ")\r\n");
+ git_buf_printf(buf, "User-Agent: git/1.0 (%s)\r\n", user_agent());
git_buf_printf(buf, "Host: %s\r\n", t->connection_data.host);
if (s->chunked || content_length > 0) {
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index b364e906e..77d939bd3 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -15,6 +15,7 @@
#include "smart.h"
#include "remote.h"
#include "repository.h"
+#include "global.h"
#include <wincrypt.h>
#include <winhttp.h>
@@ -567,12 +568,28 @@ static int winhttp_close_connection(winhttp_subtransport *t)
return ret;
}
+static int user_agent(git_buf *ua)
+{
+ const char *custom = git_libgit2__user_agent();
+
+ git_buf_clear(ua);
+ git_buf_PUTS(ua, "git/1.0 (");
+
+ if (custom)
+ git_buf_puts(ua, custom);
+ else
+ git_buf_PUTS(ua, "libgit2 " LIBGIT2_VERSION);
+
+ return git_buf_putc(ua, ')');
+}
+
static int winhttp_connect(
winhttp_subtransport *t)
{
- wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")";
wchar_t *wide_host;
int32_t port;
+ wchar_t *wide_ua;
+ git_buf ua = GIT_BUF_INIT;
int error = -1;
int default_timeout = TIMEOUT_INFINITE;
int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
@@ -590,9 +607,23 @@ static int winhttp_connect(
return -1;
}
+ if ((error = user_agent(&ua)) < 0) {
+ git__free(wide_host);
+ return error;
+ }
+
+ if (git__utf8_to_16_alloc(&wide_ua, git_buf_cstr(&ua)) < 0) {
+ giterr_set(GITERR_OS, "Unable to convert host to wide characters");
+ git__free(wide_host);
+ git_buf_free(&ua);
+ return -1;
+ }
+
+ git_buf_free(&ua);
+
/* Establish session */
t->session = WinHttpOpen(
- ua,
+ wide_ua,
WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
WINHTTP_NO_PROXY_NAME,
WINHTTP_NO_PROXY_BYPASS,
@@ -628,6 +659,7 @@ on_error:
winhttp_close_connection(t);
git__free(wide_host);
+ git__free(wide_ua);
return error;
}
diff --git a/src/win32/findfile.c b/src/win32/findfile.c
index de27dd060..58c22279e 100644
--- a/src/win32/findfile.c
+++ b/src/win32/findfile.c
@@ -215,3 +215,13 @@ int git_win32__find_xdg_dirs(git_buf *out)
return win32_find_existing_dirs(out, global_tmpls);
}
+
+int git_win32__find_programdata_dirs(git_buf *out)
+{
+ static const wchar_t *programdata_tmpls[2] = {
+ L"%PROGRAMDATA%\\Git",
+ NULL,
+ };
+
+ return win32_find_existing_dirs(out, programdata_tmpls);
+}
diff --git a/src/win32/findfile.h b/src/win32/findfile.h
index a50319b9a..3d5fff439 100644
--- a/src/win32/findfile.h
+++ b/src/win32/findfile.h
@@ -11,6 +11,7 @@
extern int git_win32__find_system_dirs(git_buf *out, const wchar_t *subpath);
extern int git_win32__find_global_dirs(git_buf *out);
extern int git_win32__find_xdg_dirs(git_buf *out);
+extern int git_win32__find_programdata_dirs(git_buf *out);
#endif
diff --git a/src/xdiff/xdiff.h b/src/xdiff/xdiff.h
index e2f1e892b..f08f72e16 100644
--- a/src/xdiff/xdiff.h
+++ b/src/xdiff/xdiff.h
@@ -20,6 +20,8 @@
*
*/
+#include "../util.h"
+
#if !defined(XDIFF_H)
#define XDIFF_H
@@ -106,9 +108,9 @@ typedef struct s_bdiffparam {
} bdiffparam_t;
-#define xdl_malloc(x) malloc(x)
-#define xdl_free(ptr) free(ptr)
-#define xdl_realloc(ptr,x) realloc(ptr,x)
+#define xdl_malloc(x) git__malloc(x)
+#define xdl_free(ptr) git__free(ptr)
+#define xdl_realloc(ptr,x) git__realloc(ptr,x)
void *xdl_mmfile_first(mmfile_t *mmf, long *size);
long xdl_mmfile_size(mmfile_t *mmf);
diff --git a/src/xdiff/xdiffi.c b/src/xdiff/xdiffi.c
index 0620e5fff..f4d01b48c 100644
--- a/src/xdiff/xdiffi.c
+++ b/src/xdiff/xdiffi.c
@@ -21,7 +21,8 @@
*/
#include "xinclude.h"
-
+#include "common.h"
+#include "integer.h"
#define XDL_MAX_COST_MIN 256
@@ -323,7 +324,7 @@ int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
xdfenv_t *xe) {
- long ndiags;
+ size_t ndiags, allocsize;
long *kvd, *kvdf, *kvdb;
xdalgoenv_t xenv;
diffdata_t dd1, dd2;
@@ -343,9 +344,12 @@ int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
* Allocate and setup K vectors to be used by the differential algorithm.
* One is to store the forward path and one to store the backward path.
*/
- ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3;
- if (!(kvd = (long *) xdl_malloc((2 * ndiags + 2) * sizeof(long)))) {
+ GITERR_CHECK_ALLOC_ADD3(&ndiags, xe->xdf1.nreff, xe->xdf2.nreff, 3);
+ GITERR_CHECK_ALLOC_MULTIPLY(&allocsize, ndiags, 2);
+ GITERR_CHECK_ALLOC_ADD(&allocsize, allocsize, 2);
+ GITERR_CHECK_ALLOC_MULTIPLY(&allocsize, allocsize, sizeof(long));
+ if (!(kvd = (long *) xdl_malloc(allocsize))) {
xdl_free_env(xe);
return -1;
}
diff --git a/src/xdiff/xhistogram.c b/src/xdiff/xhistogram.c
index 500d8112a..0c2edb89c 100644
--- a/src/xdiff/xhistogram.c
+++ b/src/xdiff/xhistogram.c
@@ -44,6 +44,7 @@
#include "xinclude.h"
#include "xtypes.h"
#include "xdiff.h"
+#include "common.h"
#define MAX_PTR UINT_MAX
#define MAX_CNT UINT_MAX
@@ -271,7 +272,7 @@ static int histogram_diff(
{
struct histindex index;
struct region lcs;
- unsigned int sz;
+ size_t sz;
int result = -1;
if (count1 <= 0 && count2 <= 0)
@@ -302,7 +303,8 @@ static int histogram_diff(
index.table_bits = xdl_hashbits(count1);
sz = index.records_size = 1 << index.table_bits;
- sz *= sizeof(struct record *);
+ GITERR_CHECK_ALLOC_MULTIPLY(&sz, sz, sizeof(struct record *));
+
if (!(index.records = (struct record **) xdl_malloc(sz)))
goto cleanup;
memset(index.records, 0, sz);
diff --git a/src/xdiff/xmerge.c b/src/xdiff/xmerge.c
index b11e59817..7b7e0e2d3 100644
--- a/src/xdiff/xmerge.c
+++ b/src/xdiff/xmerge.c
@@ -21,6 +21,7 @@
*/
#include "xinclude.h"
+#include "common.h"
typedef struct s_xdmerge {
struct s_xdmerge *next;
@@ -109,59 +110,74 @@ static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,
return 0;
}
-static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+static int xdl_recs_copy_0(size_t *out, int use_orig, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
{
xrecord_t **recs;
- int size = 0;
+ size_t size = 0;
+
+ *out = 0;
recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;
if (count < 1)
return 0;
- for (i = 0; i < count; size += recs[i++]->size)
+ for (i = 0; i < count; ) {
if (dest)
memcpy(dest + size, recs[i]->ptr, recs[i]->size);
+
+ GITERR_CHECK_ALLOC_ADD(&size, size, recs[i++]->size);
+ }
+
if (add_nl) {
i = recs[count - 1]->size;
if (i == 0 || recs[count - 1]->ptr[i - 1] != '\n') {
if (dest)
dest[size] = '\n';
- size++;
+
+ GITERR_CHECK_ALLOC_ADD(&size, size, 1);
}
}
- return size;
+
+ *out = size;
+ return 0;
}
-static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+static int xdl_recs_copy(size_t *out, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
{
- return xdl_recs_copy_0(0, xe, i, count, add_nl, dest);
+ return xdl_recs_copy_0(out, 0, xe, i, count, add_nl, dest);
}
-static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int add_nl, char *dest)
+static int xdl_orig_copy(size_t *out, xdfenv_t *xe, int i, int count, int add_nl, char *dest)
{
- return xdl_recs_copy_0(1, xe, i, count, add_nl, dest);
+ return xdl_recs_copy_0(out, 1, xe, i, count, add_nl, dest);
}
-static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
+static int fill_conflict_hunk(size_t *out, xdfenv_t *xe1, const char *name1,
xdfenv_t *xe2, const char *name2,
const char *name3,
- int size, int i, int style,
+ size_t size, int i, int style,
xdmerge_t *m, char *dest, int marker_size)
{
int marker1_size = (name1 ? (int)strlen(name1) + 1 : 0);
int marker2_size = (name2 ? (int)strlen(name2) + 1 : 0);
int marker3_size = (name3 ? (int)strlen(name3) + 1 : 0);
+ size_t copied;
+
+ *out = 0;
if (marker_size <= 0)
marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
/* Before conflicting part */
- size += xdl_recs_copy(xe1, i, m->i1 - i, 0,
- dest ? dest + size : NULL);
+ if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0,
+ dest ? dest + size : NULL) < 0)
+ return -1;
+
+ GITERR_CHECK_ALLOC_ADD(&size, size, copied);
if (!dest) {
- size += marker_size + 1 + marker1_size;
+ GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker1_size);
} else {
memset(dest + size, '<', marker_size);
size += marker_size;
@@ -174,13 +190,16 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
}
/* Postimage from side #1 */
- size += xdl_recs_copy(xe1, m->i1, m->chg1, 1,
- dest ? dest + size : NULL);
+ if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, 1,
+ dest ? dest + size : NULL) < 0)
+ return -1;
+
+ GITERR_CHECK_ALLOC_ADD(&size, size, copied);
if (style == XDL_MERGE_DIFF3) {
/* Shared preimage */
if (!dest) {
- size += marker_size + 1 + marker3_size;
+ GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker3_size);
} else {
memset(dest + size, '|', marker_size);
size += marker_size;
@@ -191,12 +210,15 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
}
dest[size++] = '\n';
}
- size += xdl_orig_copy(xe1, m->i0, m->chg0, 1,
- dest ? dest + size : NULL);
+
+ if (xdl_orig_copy(&copied, xe1, m->i0, m->chg0, 1,
+ dest ? dest + size : NULL) < 0)
+ return -1;
+ GITERR_CHECK_ALLOC_ADD(&size, size, copied);
}
if (!dest) {
- size += marker_size + 1;
+ GITERR_CHECK_ALLOC_ADD3(&size, size, marker_size, 1);
} else {
memset(dest + size, '=', marker_size);
size += marker_size;
@@ -204,10 +226,14 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
}
/* Postimage from side #2 */
- size += xdl_recs_copy(xe2, m->i2, m->chg2, 1,
- dest ? dest + size : NULL);
+
+ if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, 1,
+ dest ? dest + size : NULL) < 0)
+ return -1;
+ GITERR_CHECK_ALLOC_ADD(&size, size, copied);
+
if (!dest) {
- size += marker_size + 1 + marker2_size;
+ GITERR_CHECK_ALLOC_ADD4(&size, size, marker_size, 1, marker2_size);
} else {
memset(dest + size, '>', marker_size);
size += marker_size;
@@ -218,46 +244,69 @@ static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
}
dest[size++] = '\n';
}
- return size;
+
+ *out = size;
+ return 0;
}
-static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
+static int xdl_fill_merge_buffer(size_t *out,
+ xdfenv_t *xe1, const char *name1,
xdfenv_t *xe2, const char *name2,
const char *ancestor_name,
int favor,
xdmerge_t *m, char *dest, int style,
int marker_size)
{
- int size, i;
+ size_t size, copied;
+ int i;
+
+ *out = 0;
for (size = i = 0; m; m = m->next) {
if (favor && !m->mode)
m->mode = favor;
- if (m->mode == 0)
- size = fill_conflict_hunk(xe1, name1, xe2, name2,
+ if (m->mode == 0) {
+ if (fill_conflict_hunk(&size, xe1, name1, xe2, name2,
ancestor_name,
size, i, style, m, dest,
- marker_size);
+ marker_size) < 0)
+ return -1;
+ }
else if (m->mode & 3) {
/* Before conflicting part */
- size += xdl_recs_copy(xe1, i, m->i1 - i, 0,
- dest ? dest + size : NULL);
+ if (xdl_recs_copy(&copied, xe1, i, m->i1 - i, 0,
+ dest ? dest + size : NULL) < 0)
+ return -1;
+ GITERR_CHECK_ALLOC_ADD(&size, size, copied);
+
/* Postimage from side #1 */
- if (m->mode & 1)
- size += xdl_recs_copy(xe1, m->i1, m->chg1, (m->mode & 2),
- dest ? dest + size : NULL);
+ if (m->mode & 1) {
+ if (xdl_recs_copy(&copied, xe1, m->i1, m->chg1, (m->mode & 2),
+ dest ? dest + size : NULL) < 0)
+ return -1;
+ GITERR_CHECK_ALLOC_ADD(&size, size, copied);
+ }
+
/* Postimage from side #2 */
- if (m->mode & 2)
- size += xdl_recs_copy(xe2, m->i2, m->chg2, 0,
- dest ? dest + size : NULL);
+ if (m->mode & 2) {
+ if (xdl_recs_copy(&copied, xe2, m->i2, m->chg2, 0,
+ dest ? dest + size : NULL) < 0)
+ return -1;
+ GITERR_CHECK_ALLOC_ADD(&size, size, copied);
+ }
} else
continue;
i = m->i1 + m->chg1;
}
- size += xdl_recs_copy(xe1, i, xe1->xdf2.nrec - i, 0,
- dest ? dest + size : NULL);
- return size;
+
+ if (xdl_recs_copy(&copied, xe1, i, xe1->xdf2.nrec - i, 0,
+ dest ? dest + size : NULL) < 0)
+ return -1;
+ GITERR_CHECK_ALLOC_ADD(&size, size, copied);
+
+ *out = size;
+ return 0;
}
/*
@@ -551,19 +600,24 @@ static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
/* output */
if (result) {
int marker_size = xmp->marker_size;
- int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2,
+ size_t size;
+
+ if (xdl_fill_merge_buffer(&size, xe1, name1, xe2, name2,
ancestor_name,
favor, changes, NULL, style,
- marker_size);
+ marker_size) < 0)
+ return -1;
+
result->ptr = xdl_malloc(size);
if (!result->ptr) {
xdl_cleanup_merge(changes);
return -1;
}
result->size = size;
- xdl_fill_merge_buffer(xe1, name1, xe2, name2,
+ if (xdl_fill_merge_buffer(&size, xe1, name1, xe2, name2,
ancestor_name, favor, changes,
- result->ptr, style, marker_size);
+ result->ptr, style, marker_size) < 0)
+ return -1;
}
return xdl_cleanup_merge(changes);
}
diff --git a/tests/commit/commit.c b/tests/commit/commit.c
index f5461cfd3..81aaf80d3 100644
--- a/tests/commit/commit.c
+++ b/tests/commit/commit.c
@@ -70,12 +70,16 @@ void test_commit_commit__summary(void)
assert_commit_summary("Trimmed leading&trailing newlines", "\n\nTrimmed leading&trailing newlines\n\n");
assert_commit_summary("First paragraph only", "\nFirst paragraph only\n\n(There are more!)");
assert_commit_summary("First paragraph with unwrapped trailing\tlines", "\nFirst paragraph\nwith unwrapped\ntrailing\tlines\n\n(Yes, unwrapped!)");
- assert_commit_summary("\tLeading \ttabs", "\tLeading\n\ttabs\n\nis preserved");
- assert_commit_summary(" Leading Spaces", " Leading\n Spaces\n\nare preserved");
+ assert_commit_summary("\tLeading tabs", "\tLeading\n\ttabs\n\nare preserved"); /* tabs around newlines are collapsed down to a single space */
+ assert_commit_summary(" Leading Spaces", " Leading\n Spaces\n\nare preserved"); /* spaces around newlines are collapsed down to a single space */
assert_commit_summary("Trailing tabs\tare removed", "Trailing tabs\tare removed\t\t");
assert_commit_summary("Trailing spaces are removed", "Trailing spaces are removed ");
assert_commit_summary("Trailing tabs", "Trailing tabs\t\n\nare removed");
assert_commit_summary("Trailing spaces", "Trailing spaces \n\nare removed");
+ assert_commit_summary("Newlines are replaced by spaces", "Newlines\nare\nreplaced by spaces\n");
+ assert_commit_summary(" Spaces after newlines are collapsed", "\n Spaces after newlines\n are\n collapsed\n "); /* newlines at the very beginning are ignored and not collapsed */
+ assert_commit_summary(" Spaces before newlines are collapsed", " \nSpaces before newlines \nare \ncollapsed \n");
+ assert_commit_summary(" Spaces around newlines are collapsed", " \n Spaces around newlines \n are \n collapsed \n ");
assert_commit_summary("", "");
assert_commit_summary("", " ");
assert_commit_summary("", "\n");
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/commit/write.c b/tests/commit/write.c
index ee9eb8237..176965cbd 100644
--- a/tests/commit/write.c
+++ b/tests/commit/write.c
@@ -8,7 +8,7 @@ static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";
static const char *root_commit_message = "This is a root commit\n\
This is a root commit and should be the only one in this branch\n";
static const char *root_reflog_message = "commit (initial): This is a root commit \
- This is a root commit and should be the only one in this branch";
+This is a root commit and should be the only one in this branch";
static char *head_old;
static git_reference *head, *branch;
static git_commit *commit;
diff --git a/tests/config/global.c b/tests/config/global.c
index b5e83fec0..1336ef6e7 100644
--- a/tests/config/global.c
+++ b/tests/config/global.c
@@ -65,3 +65,43 @@ void test_config_global__open_xdg(void)
git_config_free(xdg);
git_config_free(cfg);
}
+
+void test_config_global__open_programdata(void)
+{
+ char *programdata;
+ git_config *cfg;
+ git_repository *repo;
+ git_buf config_path = GIT_BUF_INIT;
+ git_buf var_contents = GIT_BUF_INIT;
+
+ if (!cl_getenv("GITTEST_INVASIVE_FS_STRUCTURE"))
+ cl_skip();
+
+ programdata = cl_getenv("PROGRAMDATA");
+ cl_git_pass(git_buf_printf(&config_path, "%s/Git", programdata));
+ cl_git_pass(p_mkdir(config_path.ptr, 0777));
+ cl_git_pass(git_buf_puts(&config_path, "/config"));
+
+ cl_git_pass(git_config_open_ondisk(&cfg, config_path.ptr));
+ cl_git_pass(git_config_set_string(cfg, "programdata.var", "even higher level"));
+
+ git_buf_free(&config_path);
+ git_config_free(cfg);
+
+ git_config_open_default(&cfg);
+ cl_git_pass(git_config_get_string_buf(&var_contents, cfg, "programdata.var"));
+ cl_assert_equal_s("even higher level", var_contents.ptr);
+
+ git_config_free(cfg);
+ git_buf_free(&var_contents);
+
+ cl_git_pass(git_repository_init(&repo, "./foo.git", true));
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_get_string_buf(&var_contents, cfg, "programdata.var"));
+ cl_assert_equal_s("even higher level", var_contents.ptr);
+
+ git_config_free(cfg);
+ git_buf_free(&var_contents);
+ git_repository_free(repo);
+ cl_fixture_cleanup("./foo.git");
+}
diff --git a/tests/config/stress.c b/tests/config/stress.c
index 503f44f03..6e960425c 100644
--- a/tests/config/stress.c
+++ b/tests/config/stress.c
@@ -107,3 +107,23 @@ void test_config_stress__complex(void)
git_config_free(config);
}
+
+void test_config_stress__quick_write(void)
+{
+ git_config *config_w, *config_r;
+ const char *path = "./config-quick-write";
+ const char *key = "quick.write";
+ int32_t i;
+
+ /* Create an external writer for one instance with the other one */
+ cl_git_pass(git_config_open_ondisk(&config_w, path));
+ cl_git_pass(git_config_open_ondisk(&config_r, path));
+
+ /* Write and read in the same second (repeat to increase the chance of it happening) */
+ for (i = 0; i < 10; i++) {
+ int32_t val;
+ cl_git_pass(git_config_set_int32(config_w, key, i));
+ cl_git_pass(git_config_get_int32(&val, config_r, key));
+ cl_assert_equal_i(i, val);
+ }
+}
diff --git a/tests/core/filebuf.c b/tests/core/filebuf.c
index 915e3cc34..93aaed759 100644
--- a/tests/core/filebuf.c
+++ b/tests/core/filebuf.c
@@ -230,3 +230,12 @@ void test_core_filebuf__hidden_file(void)
git_filebuf_cleanup(&file);
#endif
}
+
+void test_core_filebuf__detects_directory(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT, fail = GIT_FILEBUF_INIT;
+
+ cl_must_pass(p_mkdir("foo", 0777));
+ cl_git_fail_with(GIT_EDIRECTORY, git_filebuf_open(&file, "foo", 0, 0666));
+ cl_must_pass(p_rmdir("foo"));
+}
diff --git a/tests/core/pool.c b/tests/core/pool.c
index a7ec8801b..f90adfbc3 100644
--- a/tests/core/pool.c
+++ b/tests/core/pool.c
@@ -8,7 +8,7 @@ void test_core_pool__0(void)
git_pool p;
void *ptr;
- cl_git_pass(git_pool_init(&p, 1, 4000));
+ git_pool_init(&p, 1);
for (i = 1; i < 10000; i *= 2) {
ptr = git_pool_malloc(&p, i);
@@ -17,13 +17,6 @@ void test_core_pool__0(void)
cl_assert(!git_pool__ptr_in_pool(&p, &i));
}
- /* 1+2+4+8+16+32+64+128+256+512+1024 -> original block */
- /* 2048 -> 1 block */
- /* 4096 -> 1 block */
- /* 8192 -> 1 block */
-
- cl_assert(git_pool__open_pages(&p) + git_pool__full_pages(&p) == 4);
-
git_pool_clear(&p);
}
@@ -32,26 +25,24 @@ void test_core_pool__1(void)
int i;
git_pool p;
- cl_git_pass(git_pool_init(&p, 1, 4000));
+ git_pool_init(&p, 1);
+ p.page_size = 4000;
for (i = 2010; i > 0; i--)
cl_assert(git_pool_malloc(&p, i) != NULL);
/* with fixed page size, allocation must end up with these values */
- cl_assert_equal_i(1, git_pool__open_pages(&p));
- cl_assert_equal_i(507, git_pool__full_pages(&p));
-
+ cl_assert_equal_i(590, git_pool__open_pages(&p));
git_pool_clear(&p);
- cl_git_pass(git_pool_init(&p, 1, 4120));
+ git_pool_init(&p, 1);
+ p.page_size = 4120;
for (i = 2010; i > 0; i--)
cl_assert(git_pool_malloc(&p, i) != NULL);
/* with fixed page size, allocation must end up with these values */
- cl_assert_equal_i(1, git_pool__open_pages(&p));
- cl_assert_equal_i(492, git_pool__full_pages(&p));
-
+ cl_assert_equal_i(573, git_pool__open_pages(&p));
git_pool_clear(&p);
}
@@ -66,7 +57,8 @@ void test_core_pool__2(void)
memset(oid_hex, '0', sizeof(oid_hex));
- cl_git_pass(git_pool_init(&p, sizeof(git_oid), 100));
+ git_pool_init(&p, sizeof(git_oid));
+ p.page_size = 4000;
for (i = 1000; i < 10000; i++) {
oid = git_pool_malloc(&p, 1);
@@ -78,59 +70,7 @@ void test_core_pool__2(void)
}
/* with fixed page size, allocation must end up with these values */
- cl_assert(git_pool__open_pages(&p) == 0);
- cl_assert(git_pool__full_pages(&p) == 90);
-
- git_pool_clear(&p);
-}
-
-void test_core_pool__free_list(void)
-{
- int i;
- git_pool p;
- void *ptr, *ptrs[50];
-
- cl_git_pass(git_pool_init(&p, 100, 100));
-
- for (i = 0; i < 10; ++i) {
- ptr = git_pool_malloc(&p, 1);
- cl_assert(ptr != NULL);
- }
- cl_assert_equal_i(10, (int)p.items);
-
- for (i = 0; i < 50; ++i) {
- ptrs[i] = git_pool_malloc(&p, 1);
- cl_assert(ptrs[i] != NULL);
- }
- cl_assert_equal_i(60, (int)p.items);
-
- git_pool_free(&p, ptr);
- cl_assert_equal_i(60, (int)p.items);
-
- git_pool_free_array(&p, 50, ptrs);
- cl_assert_equal_i(60, (int)p.items);
-
- for (i = 0; i < 50; ++i) {
- ptrs[i] = git_pool_malloc(&p, 1);
- cl_assert(ptrs[i] != NULL);
- }
- cl_assert_equal_i(60, (int)p.items);
-
- for (i = 0; i < 111; ++i) {
- ptr = git_pool_malloc(&p, 1);
- cl_assert(ptr != NULL);
- }
- cl_assert_equal_i(170, (int)p.items);
-
- git_pool_free_array(&p, 50, ptrs);
- cl_assert_equal_i(170, (int)p.items);
-
- for (i = 0; i < 50; ++i) {
- ptrs[i] = git_pool_malloc(&p, 1);
- cl_assert(ptrs[i] != NULL);
- }
- cl_assert_equal_i(170, (int)p.items);
-
+ cl_assert_equal_i(sizeof(void *) == 8 ? 55 : 45, git_pool__open_pages(&p));
git_pool_clear(&p);
}
@@ -138,7 +78,7 @@ void test_core_pool__strndup_limit(void)
{
git_pool p;
- cl_git_pass(git_pool_init(&p, 1, 100));
+ git_pool_init(&p, 1);
/* ensure 64 bit doesn't overflow */
cl_assert(git_pool_strndup(&p, "foo", (size_t)-1) == NULL);
git_pool_clear(&p);
diff --git a/tests/core/stream.c b/tests/core/stream.c
new file mode 100644
index 000000000..ace6a05da
--- /dev/null
+++ b/tests/core/stream.c
@@ -0,0 +1,47 @@
+#include "clar_libgit2.h"
+#include "git2/sys/stream.h"
+#include "tls_stream.h"
+#include "stream.h"
+
+static git_stream test_stream;
+static int ctor_called;
+
+static int test_ctor(git_stream **out, const char *host, const char *port)
+{
+ GIT_UNUSED(host);
+ GIT_UNUSED(port);
+
+ ctor_called = 1;
+ *out = &test_stream;
+
+ return 0;
+}
+
+void test_core_stream__register_tls(void)
+{
+ git_stream *stream;
+ int error;
+
+ ctor_called = 0;
+ cl_git_pass(git_stream_register_tls(test_ctor));
+ cl_git_pass(git_tls_stream_new(&stream, "localhost", "443"));
+ cl_assert_equal_i(1, ctor_called);
+ cl_assert_equal_p(&test_stream, stream);
+
+ ctor_called = 0;
+ stream = NULL;
+ cl_git_pass(git_stream_register_tls(NULL));
+ error = git_tls_stream_new(&stream, "localhost", "443");
+
+ /* We don't have arbitrary TLS stream support on Windows */
+#if GIT_WIN32
+ cl_git_fail_with(-1, error);
+#else
+ cl_git_pass(error);
+#endif
+
+ cl_assert_equal_i(0, ctor_called);
+ cl_assert(&test_stream != stream);
+
+ git_stream_free(stream);
+}
diff --git a/tests/core/useragent.c b/tests/core/useragent.c
new file mode 100644
index 000000000..6d06693a8
--- /dev/null
+++ b/tests/core/useragent.c
@@ -0,0 +1,11 @@
+#include "clar_libgit2.h"
+#include "global.h"
+
+void test_core_useragent__get(void)
+{
+ const char *custom_name = "super duper git";
+
+ cl_assert_equal_p(NULL, git_libgit2__user_agent());
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, custom_name));
+ cl_assert_equal_s(custom_name, git_libgit2__user_agent());
+}
diff --git a/tests/diff/notify.c b/tests/diff/notify.c
index 6ef4af573..74abbc93b 100644
--- a/tests/diff/notify.c
+++ b/tests/diff/notify.c
@@ -55,7 +55,7 @@ static void test_notify(
opts.pathspec.strings = searched_pathspecs;
opts.pathspec.count = pathspecs_count;
- opts.notify_payload = expected_matched_pathspecs;
+ opts.payload = expected_matched_pathspecs;
memset(&exp, 0, sizeof(exp));
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
@@ -228,3 +228,30 @@ void test_diff_notify__notify_cb_can_be_used_as_filtering_function(void)
git_diff_free(diff);
}
+
+static int progress_abort_diff(
+ const git_diff *diff_so_far,
+ const char *old_path,
+ const char *new_path,
+ void *payload)
+{
+ GIT_UNUSED(old_path);
+ GIT_UNUSED(new_path);
+ GIT_UNUSED(payload);
+
+ return -42;
+}
+
+void test_diff_notify__progress_cb_can_abort_diff(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.progress_cb = progress_abort_diff;
+
+ cl_git_fail_with(
+ git_diff_index_to_workdir(&diff, g_repo, NULL, &opts), -42);
+}
diff --git a/tests/diff/tree.c b/tests/diff/tree.c
index 2bc9e6a55..e4b2a8bbe 100644
--- a/tests/diff/tree.c
+++ b/tests/diff/tree.c
@@ -90,7 +90,7 @@ void test_diff_tree__0(void)
#define DIFF_OPTS(FLAGS, CTXT) \
{GIT_DIFF_OPTIONS_VERSION, (FLAGS), GIT_SUBMODULE_IGNORE_UNSPECIFIED, \
- {NULL,0}, NULL, NULL, (CTXT), 1}
+ {NULL,0}, NULL, NULL, NULL, (CTXT), 1}
void test_diff_tree__options(void)
{
diff --git a/tests/diff/workdir.c b/tests/diff/workdir.c
index 89f9483a6..4c782339d 100644
--- a/tests/diff/workdir.c
+++ b/tests/diff/workdir.c
@@ -2080,7 +2080,7 @@ void test_diff_workdir__to_index_pathlist(void)
cl_git_pass(git_repository_index(&index, g_repo));
opts.flags = GIT_DIFF_INCLUDE_IGNORED;
- opts.pathspec.strings = pathlist.contents;
+ opts.pathspec.strings = (char **)pathlist.contents;
opts.pathspec.count = pathlist.length;
cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &opts));
@@ -2097,3 +2097,64 @@ void test_diff_workdir__to_index_pathlist(void)
git_vector_free(&pathlist);
}
+void test_diff_workdir__symlink_changed_on_non_symlink_platform(void)
+{
+ git_tree *tree;
+ git_diff *diff;
+ diff_expects exp = {0};
+ const git_diff_delta *delta;
+ const char *commit = "7fccd7";
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_vector pathlist = GIT_VECTOR_INIT;
+ int symlinks;
+
+ g_repo = cl_git_sandbox_init("unsymlinked.git");
+
+ cl_git_pass(git_repository__cvar(&symlinks, g_repo, GIT_CVAR_SYMLINKS));
+
+ if (symlinks)
+ cl_skip();
+
+ cl_git_pass(git_vector_insert(&pathlist, "include/Nu/Nu.h"));
+
+ opts.pathspec.strings = (char **)pathlist.contents;
+ opts.pathspec.count = pathlist.length;
+
+ cl_must_pass(p_mkdir("symlink", 0777));
+ cl_git_pass(git_repository_set_workdir(g_repo, "symlink", false));
+
+ cl_assert((tree = resolve_commit_oid_to_tree(g_repo, commit)) != NULL);
+
+ /* first, do the diff with the original contents */
+
+ cl_git_pass(git_futils_mkpath2file("symlink/include/Nu/Nu.h", 0755));
+ cl_git_mkfile("symlink/include/Nu/Nu.h", "../../objc/Nu.h");
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
+ cl_assert_equal_i(0, git_diff_num_deltas(diff));
+ git_diff_free(diff);
+
+ /* now update the contents and expect a difference, but that the file
+ * mode has persisted as a symbolic link.
+ */
+
+ cl_git_rewritefile("symlink/include/Nu/Nu.h", "awesome content\n");
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(1, exp.files);
+
+ cl_assert_equal_i(1, git_diff_num_deltas(diff));
+ delta = git_diff_get_delta(diff, 0);
+ cl_assert_equal_i(GIT_FILEMODE_LINK, delta->old_file.mode);
+ cl_assert_equal_i(GIT_FILEMODE_LINK, delta->new_file.mode);
+
+ git_diff_free(diff);
+
+ cl_git_pass(git_futils_rmdir_r("symlink", NULL, GIT_RMDIR_REMOVE_FILES));
+
+ git_tree_free(tree);
+ git_vector_free(&pathlist);
+}
diff --git a/tests/index/bypath.c b/tests/index/bypath.c
index b152b0917..88a76178a 100644
--- a/tests/index/bypath.c
+++ b/tests/index/bypath.c
@@ -240,3 +240,120 @@ void test_index_bypath__add_honors_existing_case_4(void)
cl_assert_equal_s("just_a_dir/a/b/Z/y/X/foo.txt", entry->path);
}
+void test_index_bypath__add_honors_mode(void)
+{
+ const git_index_entry *entry;
+ git_index_entry new_entry;
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+
+ memcpy(&new_entry, entry, sizeof(git_index_entry));
+ new_entry.path = "README.txt";
+ new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE;
+
+ cl_must_pass(p_chmod("submod2/README.txt", GIT_FILEMODE_BLOB_EXECUTABLE));
+
+ cl_git_pass(git_index_add(g_idx, &new_entry));
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_git_rewritefile("submod2/README.txt", "Modified but still executable");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "README.txt"));
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+ cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, entry->mode);
+}
+
+void test_index_bypath__add_honors_conflict_mode(void)
+{
+ const git_index_entry *entry;
+ git_index_entry new_entry;
+ int stage = 0;
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+
+ memcpy(&new_entry, entry, sizeof(git_index_entry));
+ new_entry.path = "README.txt";
+ new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE;
+
+ cl_must_pass(p_chmod("submod2/README.txt", GIT_FILEMODE_BLOB_EXECUTABLE));
+
+ cl_git_pass(git_index_remove_bypath(g_idx, "README.txt"));
+
+ for (stage = 1; stage <= 3; stage++) {
+ new_entry.flags = stage << GIT_IDXENTRY_STAGESHIFT;
+ cl_git_pass(git_index_add(g_idx, &new_entry));
+ }
+
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_git_rewritefile("submod2/README.txt", "Modified but still executable");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "README.txt"));
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+ cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, entry->mode);
+}
+
+void test_index_bypath__add_honors_conflict_case(void)
+{
+ const git_index_entry *entry;
+ git_index_entry new_entry;
+ int stage = 0;
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+
+ memcpy(&new_entry, entry, sizeof(git_index_entry));
+ new_entry.path = "README.txt";
+ new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE;
+
+ cl_must_pass(p_chmod("submod2/README.txt", GIT_FILEMODE_BLOB_EXECUTABLE));
+
+ cl_git_pass(git_index_remove_bypath(g_idx, "README.txt"));
+
+ for (stage = 1; stage <= 3; stage++) {
+ new_entry.flags = stage << GIT_IDXENTRY_STAGESHIFT;
+ cl_git_pass(git_index_add(g_idx, &new_entry));
+ }
+
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_git_rewritefile("submod2/README.txt", "Modified but still executable");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "README.txt"));
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+ cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, entry->mode);
+}
+
+void test_index_bypath__add_honors_symlink(void)
+{
+ const git_index_entry *entry;
+ git_index_entry new_entry;
+ int symlinks;
+
+ cl_git_pass(git_repository__cvar(&symlinks, g_repo, GIT_CVAR_SYMLINKS));
+
+ if (symlinks)
+ cl_skip();
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+
+ memcpy(&new_entry, entry, sizeof(git_index_entry));
+ new_entry.path = "README.txt";
+ new_entry.mode = GIT_FILEMODE_LINK;
+
+ cl_git_pass(git_index_add(g_idx, &new_entry));
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_git_rewritefile("submod2/README.txt", "Modified but still a (fake) symlink");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "README.txt"));
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+ cl_assert_equal_i(GIT_FILEMODE_LINK, entry->mode);
+}
diff --git a/tests/index/conflicts.c b/tests/index/conflicts.c
index b7a2456eb..8e94cd441 100644
--- a/tests/index/conflicts.c
+++ b/tests/index/conflicts.c
@@ -342,3 +342,94 @@ void test_index_conflicts__partial(void)
cl_assert(conflict_entry[1] == NULL);
cl_assert(conflict_entry[2] == NULL);
}
+
+void test_index_conflicts__case_matters(void)
+{
+ const git_index_entry *conflict_entry[3];
+ git_oid oid;
+ const char *upper_case = "DIFFERS-IN-CASE.TXT";
+ const char *mixed_case = "Differs-In-Case.txt";
+ const char *correct_case;
+ bool ignorecase = cl_repo_get_bool(repo, "core.ignorecase");
+
+ git_index_entry ancestor_entry, our_entry, their_entry;
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = upper_case;
+ GIT_IDXENTRY_STAGE_SET(&ancestor_entry, GIT_INDEX_STAGE_ANCESTOR);
+ git_oid_fromstr(&ancestor_entry.id, CONFLICTS_ONE_ANCESTOR_OID);
+ ancestor_entry.mode = GIT_FILEMODE_BLOB;
+
+ our_entry.path = upper_case;
+ GIT_IDXENTRY_STAGE_SET(&our_entry, GIT_INDEX_STAGE_OURS);
+ git_oid_fromstr(&our_entry.id, CONFLICTS_ONE_OUR_OID);
+ our_entry.mode = GIT_FILEMODE_BLOB;
+
+ their_entry.path = upper_case;
+ GIT_IDXENTRY_STAGE_SET(&their_entry, GIT_INDEX_STAGE_THEIRS);
+ git_oid_fromstr(&their_entry.id, CONFLICTS_ONE_THEIR_OID);
+ their_entry.mode = GIT_FILEMODE_BLOB;
+
+ cl_git_pass(git_index_conflict_add(repo_index,
+ &ancestor_entry, &our_entry, &their_entry));
+
+ ancestor_entry.path = mixed_case;
+ GIT_IDXENTRY_STAGE_SET(&ancestor_entry, GIT_INDEX_STAGE_ANCESTOR);
+ git_oid_fromstr(&ancestor_entry.id, CONFLICTS_TWO_ANCESTOR_OID);
+ ancestor_entry.mode = GIT_FILEMODE_BLOB;
+
+ our_entry.path = mixed_case;
+ GIT_IDXENTRY_STAGE_SET(&ancestor_entry, GIT_INDEX_STAGE_ANCESTOR);
+ git_oid_fromstr(&our_entry.id, CONFLICTS_TWO_OUR_OID);
+ ancestor_entry.mode = GIT_FILEMODE_BLOB;
+
+ their_entry.path = mixed_case;
+ GIT_IDXENTRY_STAGE_SET(&their_entry, GIT_INDEX_STAGE_THEIRS);
+ git_oid_fromstr(&their_entry.id, CONFLICTS_TWO_THEIR_OID);
+ their_entry.mode = GIT_FILEMODE_BLOB;
+
+ cl_git_pass(git_index_conflict_add(repo_index,
+ &ancestor_entry, &our_entry, &their_entry));
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], repo_index, upper_case));
+
+ /*
+ * We inserted with mixed case last, so on a case-insensitive
+ * fs we should get the mixed case.
+ */
+ if (ignorecase)
+ correct_case = mixed_case;
+ else
+ correct_case = upper_case;
+
+ cl_assert_equal_s(correct_case, conflict_entry[0]->path);
+ git_oid_fromstr(&oid, ignorecase ? CONFLICTS_TWO_ANCESTOR_OID : CONFLICTS_ONE_ANCESTOR_OID);
+ cl_assert_equal_oid(&oid, &conflict_entry[0]->id);
+
+ cl_assert_equal_s(correct_case, conflict_entry[1]->path);
+ git_oid_fromstr(&oid, ignorecase ? CONFLICTS_TWO_OUR_OID : CONFLICTS_ONE_OUR_OID);
+ cl_assert_equal_oid(&oid, &conflict_entry[1]->id);
+
+ cl_assert_equal_s(correct_case, conflict_entry[2]->path);
+ git_oid_fromstr(&oid, ignorecase ? CONFLICTS_TWO_THEIR_OID : CONFLICTS_ONE_THEIR_OID);
+ cl_assert_equal_oid(&oid, &conflict_entry[2]->id);
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], repo_index, mixed_case));
+
+ cl_assert_equal_s(mixed_case, conflict_entry[0]->path);
+ git_oid_fromstr(&oid, CONFLICTS_TWO_ANCESTOR_OID);
+ cl_assert_equal_oid(&oid, &conflict_entry[0]->id);
+
+ cl_assert_equal_s(mixed_case, conflict_entry[1]->path);
+ git_oid_fromstr(&oid, CONFLICTS_TWO_OUR_OID);
+ cl_assert_equal_oid(&oid, &conflict_entry[1]->id);
+
+ cl_assert_equal_s(mixed_case, conflict_entry[2]->path);
+ git_oid_fromstr(&oid, CONFLICTS_TWO_THEIR_OID);
+ cl_assert_equal_oid(&oid, &conflict_entry[2]->id);
+}
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/merge/files.c b/tests/merge/files.c
index 2fd90d066..2d55df2b2 100644
--- a/tests/merge/files.c
+++ b/tests/merge/files.c
@@ -6,6 +6,7 @@
#include "merge_helpers.h"
#include "refs.h"
#include "fileops.h"
+#include "diff_xdiff.h"
#define TEST_REPO_PATH "merge-resolve"
#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
@@ -288,3 +289,90 @@ void test_merge_files__doesnt_add_newline(void)
git_merge_file_result_free(&result);
}
+void test_merge_files__skips_large_files(void)
+{
+ git_merge_file_input ours = GIT_MERGE_FILE_INPUT_INIT,
+ theirs = GIT_MERGE_FILE_INPUT_INIT;
+ git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
+ git_merge_file_result result = {0};
+
+ ours.size = GIT_XDIFF_MAX_SIZE + 1;
+ ours.path = "testfile.txt";
+ ours.mode = 0100755;
+
+ theirs.size = GIT_XDIFF_MAX_SIZE + 1;
+ theirs.path = "testfile.txt";
+ theirs.mode = 0100755;
+
+ cl_git_pass(git_merge_file(&result, NULL, &ours, &theirs, &opts));
+
+ cl_assert_equal_i(0, result.automergeable);
+
+ git_merge_file_result_free(&result);
+}
+
+void test_merge_files__skips_binaries(void)
+{
+ 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 = {0};
+
+ ancestor.ptr = "ance\0stor\0";
+ ancestor.size = 10;
+ ancestor.path = "ancestor.txt";
+ ancestor.mode = 0100755;
+
+ ours.ptr = "foo\0bar\0";
+ ours.size = 8;
+ ours.path = "ours.txt";
+ ours.mode = 0100755;
+
+ theirs.ptr = "bar\0foo\0";
+ theirs.size = 8;
+ theirs.path = "theirs.txt";
+ theirs.mode = 0100644;
+
+ cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, NULL));
+
+ cl_assert_equal_i(0, result.automergeable);
+
+ git_merge_file_result_free(&result);
+}
+
+void test_merge_files__handles_binaries_when_favored(void)
+{
+ 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_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
+ git_merge_file_result result = {0};
+
+ ancestor.ptr = "ance\0stor\0";
+ ancestor.size = 10;
+ ancestor.path = "ancestor.txt";
+ ancestor.mode = 0100755;
+
+ ours.ptr = "foo\0bar\0";
+ ours.size = 8;
+ ours.path = "ours.txt";
+ ours.mode = 0100755;
+
+ theirs.ptr = "bar\0foo\0";
+ theirs.size = 8;
+ theirs.path = "theirs.txt";
+ theirs.mode = 0100644;
+
+ opts.favor = GIT_MERGE_FILE_FAVOR_OURS;
+ cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, &opts));
+
+ cl_assert_equal_i(1, result.automergeable);
+
+ cl_assert_equal_s("ours.txt", result.path);
+ cl_assert_equal_i(0100755, result.mode);
+
+ cl_assert_equal_i(ours.size, result.len);
+ cl_assert(memcmp(result.ptr, ours.ptr, ours.size) == 0);
+
+ git_merge_file_result_free(&result);
+}
diff --git a/tests/merge/merge_helpers.c b/tests/merge/merge_helpers.c
index f81471424..986a365db 100644
--- a/tests/merge/merge_helpers.c
+++ b/tests/merge/merge_helpers.c
@@ -40,7 +40,7 @@ int merge_trees_from_branches(
cl_git_pass(git_commit_tree(&our_tree, our_commit));
cl_git_pass(git_commit_tree(&their_tree, their_commit));
- cl_git_pass(git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts));
+ error = git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts);
git_buf_free(&branch_buf);
git_tree_free(our_tree);
@@ -50,7 +50,7 @@ int merge_trees_from_branches(
git_commit_free(their_commit);
git_commit_free(ancestor_commit);
- return 0;
+ return error;
}
int merge_commits_from_branches(
@@ -61,6 +61,7 @@ int merge_commits_from_branches(
git_commit *our_commit, *their_commit;
git_oid our_oid, their_oid;
git_buf branch_buf = GIT_BUF_INIT;
+ int error;
git_buf_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name);
cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr));
@@ -71,13 +72,13 @@ int merge_commits_from_branches(
cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr));
cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid));
- cl_git_pass(git_merge_commits(index, repo, our_commit, their_commit, opts));
+ error = git_merge_commits(index, repo, our_commit, their_commit, opts);
git_buf_free(&branch_buf);
git_commit_free(our_commit);
git_commit_free(their_commit);
- return 0;
+ return error;
}
int merge_branches(git_repository *repo,
diff --git a/tests/merge/trees/commits.c b/tests/merge/trees/commits.c
index c4e470997..2e3c4578b 100644
--- a/tests/merge/trees/commits.c
+++ b/tests/merge/trees/commits.c
@@ -94,7 +94,6 @@ void test_merge_trees_commits__no_ancestor(void)
git_index_free(index);
}
-
void test_merge_trees_commits__df_conflict(void)
{
git_index *index;
@@ -129,3 +128,20 @@ void test_merge_trees_commits__df_conflict(void)
git_index_free(index);
}
+
+void test_merge_trees_commits__fail_on_conflict(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ opts.tree_flags |= GIT_MERGE_TREE_FAIL_ON_CONFLICT;
+
+ cl_git_fail_with(GIT_EMERGECONFLICT,
+ merge_trees_from_branches(&index, repo, "df_side1", "df_side2", &opts));
+
+ cl_git_fail_with(GIT_EMERGECONFLICT,
+ merge_commits_from_branches(&index, repo, "master", "unrelated", &opts));
+ cl_git_fail_with(GIT_EMERGECONFLICT,
+ merge_commits_from_branches(&index, repo, "master", "branch", &opts));
+}
+
diff --git a/tests/odb/sorting.c b/tests/odb/sorting.c
index d24c49c69..6af8b0d1b 100644
--- a/tests/odb/sorting.c
+++ b/tests/odb/sorting.c
@@ -57,14 +57,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/online/badssl.c b/tests/online/badssl.c
index 850468320..12badbda3 100644
--- a/tests/online/badssl.c
+++ b/tests/online/badssl.c
@@ -5,23 +5,34 @@
static git_repository *g_repo;
#if defined(GIT_OPENSSL) || defined(GIT_WINHTTP) || defined(GIT_SECURE_TRANSPORT)
+static bool g_has_ssl = true;
+#else
+static bool g_has_ssl = false;
+#endif
void test_online_badssl__expired(void)
{
+ if (!g_has_ssl)
+ cl_skip();
+
cl_git_fail_with(GIT_ECERTIFICATE,
git_clone(&g_repo, "https://expired.badssl.com/fake.git", "./fake", NULL));
}
void test_online_badssl__wrong_host(void)
{
+ if (!g_has_ssl)
+ cl_skip();
+
cl_git_fail_with(GIT_ECERTIFICATE,
git_clone(&g_repo, "https://wrong.host.badssl.com/fake.git", "./fake", NULL));
}
void test_online_badssl__self_signed(void)
{
+ if (!g_has_ssl)
+ cl_skip();
+
cl_git_fail_with(GIT_ECERTIFICATE,
git_clone(&g_repo, "https://self-signed.badssl.com/fake.git", "./fake", NULL));
}
-
-#endif
diff --git a/tests/refs/create.c b/tests/refs/create.c
index 192551dbd..48194ae3b 100644
--- a/tests/refs/create.c
+++ b/tests/refs/create.c
@@ -151,6 +151,23 @@ void test_refs_create__propagate_eexists(void)
cl_assert(error == GIT_EEXISTS);
}
+void test_refs_create__existing_dir_propagates_edirectory(void)
+{
+ git_reference *new_reference, *fail_reference;
+ git_oid id;
+ const char *dir_head = "refs/heads/new-dir/new-head",
+ *fail_head = "refs/heads/new-dir";
+
+ git_oid_fromstr(&id, current_master_tip);
+
+ /* Create and write the new object id reference */
+ cl_git_pass(git_reference_create(&new_reference, g_repo, dir_head, &id, 1, NULL));
+ cl_git_fail_with(GIT_EDIRECTORY,
+ git_reference_create(&fail_reference, g_repo, fail_head, &id, false, NULL));
+
+ git_reference_free(new_reference);
+}
+
static void test_invalid_name(const char *name)
{
git_reference *new_reference;
diff --git a/tests/refs/reflog/reflog.c b/tests/refs/reflog/reflog.c
index 56ec422c3..fdb15502c 100644
--- a/tests/refs/reflog/reflog.c
+++ b/tests/refs/reflog/reflog.c
@@ -125,6 +125,77 @@ void test_refs_reflog_reflog__renaming_the_reference_moves_the_reflog(void)
git_buf_free(&master_log_path);
}
+void test_refs_reflog_reflog__deleting_the_reference_deletes_the_reflog(void)
+{
+ git_reference *master;
+ git_buf master_log_path = GIT_BUF_INIT;
+
+ git_buf_joinpath(&master_log_path, git_repository_path(g_repo), GIT_REFLOG_DIR);
+ git_buf_joinpath(&master_log_path, git_buf_cstr(&master_log_path), "refs/heads/master");
+
+ cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&master_log_path)));
+
+ cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master"));
+ cl_git_pass(git_reference_delete(master));
+ git_reference_free(master);
+
+ cl_assert_equal_i(false, git_path_isfile(git_buf_cstr(&master_log_path)));
+ git_buf_free(&master_log_path);
+}
+
+void test_refs_reflog_reflog__removes_empty_reflog_dir(void)
+{
+ git_reference *ref;
+ git_buf log_path = GIT_BUF_INIT;
+ git_oid id;
+
+ /* Create a new branch pointing at the HEAD */
+ git_oid_fromstr(&id, current_master_tip);
+ cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir/new-head", &id, 0, NULL));
+
+ git_buf_joinpath(&log_path, git_repository_path(g_repo), GIT_REFLOG_DIR);
+ git_buf_joinpath(&log_path, git_buf_cstr(&log_path), "refs/heads/new-dir/new-head");
+
+ cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&log_path)));
+
+ cl_git_pass(git_reference_delete(ref));
+ git_reference_free(ref);
+
+ /* new ref creation should succeed since new-dir is empty */
+ git_oid_fromstr(&id, current_master_tip);
+ cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir", &id, 0, NULL));
+ git_reference_free(ref);
+
+ git_buf_free(&log_path);
+}
+
+void test_refs_reflog_reflog__fails_gracefully_on_nonempty_reflog_dir(void)
+{
+ git_reference *ref;
+ git_buf log_path = GIT_BUF_INIT;
+ git_oid id;
+
+ /* Create a new branch pointing at the HEAD */
+ git_oid_fromstr(&id, current_master_tip);
+ cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir/new-head", &id, 0, NULL));
+ git_reference_free(ref);
+
+ git_buf_joinpath(&log_path, git_repository_path(g_repo), GIT_REFLOG_DIR);
+ git_buf_joinpath(&log_path, git_buf_cstr(&log_path), "refs/heads/new-dir/new-head");
+
+ cl_assert_equal_i(true, git_path_isfile(git_buf_cstr(&log_path)));
+
+ /* delete the ref manually, leave the reflog */
+ cl_must_pass(p_unlink("testrepo.git/refs/heads/new-dir/new-head"));
+
+ /* new ref creation should fail since new-dir contains reflogs still */
+ git_oid_fromstr(&id, current_master_tip);
+ cl_git_fail_with(GIT_EDIRECTORY, git_reference_create(&ref, g_repo, "refs/heads/new-dir", &id, 0, NULL));
+ git_reference_free(ref);
+
+ git_buf_free(&log_path);
+}
+
static void assert_has_reflog(bool expected_result, const char *name)
{
cl_assert_equal_i(expected_result, git_reference_has_log(g_repo, name));
@@ -154,6 +225,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/resources/submodule_with_path/.gitmodules b/tests/resources/submodule_with_path/.gitmodules
new file mode 100644
index 000000000..ba34c47dc
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "testrepo"]
+ path = lib/testrepo
+ url = ../testrepo.git
diff --git a/tests/resources/submodule_with_path/.gitted/HEAD b/tests/resources/submodule_with_path/.gitted/HEAD
new file mode 100644
index 000000000..cb089cd89
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submodule_with_path/.gitted/config b/tests/resources/submodule_with_path/.gitted/config
new file mode 100644
index 000000000..78387c50b
--- /dev/null
+++ b/tests/resources/submodule_with_path/.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/submodule_with_path/.gitted/index b/tests/resources/submodule_with_path/.gitted/index
new file mode 100644
index 000000000..a740b4b91
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/index
Binary files differ
diff --git a/tests/resources/submodule_with_path/.gitted/objects/18/372280a56a54340fa600aa91315065c6c4c693 b/tests/resources/submodule_with_path/.gitted/objects/18/372280a56a54340fa600aa91315065c6c4c693
new file mode 100644
index 000000000..d9b4313e1
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/objects/18/372280a56a54340fa600aa91315065c6c4c693
Binary files differ
diff --git a/tests/resources/submodule_with_path/.gitted/objects/36/683131578275f6a8fd1c539e0d5da0d8adff26 b/tests/resources/submodule_with_path/.gitted/objects/36/683131578275f6a8fd1c539e0d5da0d8adff26
new file mode 100644
index 000000000..366c161c1
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/objects/36/683131578275f6a8fd1c539e0d5da0d8adff26
Binary files differ
diff --git a/tests/resources/submodule_with_path/.gitted/objects/89/ca686bb21bfb75dda99a02313831a0c418f921 b/tests/resources/submodule_with_path/.gitted/objects/89/ca686bb21bfb75dda99a02313831a0c418f921
new file mode 100644
index 000000000..7c1af6645
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/objects/89/ca686bb21bfb75dda99a02313831a0c418f921
Binary files differ
diff --git a/tests/resources/submodule_with_path/.gitted/objects/b1/620ef2628d10416a84d19c783e33dc4556c9c3 b/tests/resources/submodule_with_path/.gitted/objects/b1/620ef2628d10416a84d19c783e33dc4556c9c3
new file mode 100644
index 000000000..447558259
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/objects/b1/620ef2628d10416a84d19c783e33dc4556c9c3
Binary files differ
diff --git a/tests/resources/submodule_with_path/.gitted/objects/ba/34c47dc9d3d0b1bb335b45c9d26ba1f0fc90c7 b/tests/resources/submodule_with_path/.gitted/objects/ba/34c47dc9d3d0b1bb335b45c9d26ba1f0fc90c7
new file mode 100644
index 000000000..3069f151f
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/objects/ba/34c47dc9d3d0b1bb335b45c9d26ba1f0fc90c7
Binary files differ
diff --git a/tests/resources/submodule_with_path/.gitted/objects/c8/4bf57ba2254dba216ab5c6eb1a19fe8bd0e0d6 b/tests/resources/submodule_with_path/.gitted/objects/c8/4bf57ba2254dba216ab5c6eb1a19fe8bd0e0d6
new file mode 100644
index 000000000..9f664569c
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/objects/c8/4bf57ba2254dba216ab5c6eb1a19fe8bd0e0d6
Binary files differ
diff --git a/tests/resources/submodule_with_path/.gitted/objects/d5/45fc6b40ec9e67332b6a1d2dedcbdb1bffeb6b b/tests/resources/submodule_with_path/.gitted/objects/d5/45fc6b40ec9e67332b6a1d2dedcbdb1bffeb6b
new file mode 100644
index 000000000..56a844953
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/objects/d5/45fc6b40ec9e67332b6a1d2dedcbdb1bffeb6b
Binary files differ
diff --git a/tests/resources/submodule_with_path/.gitted/refs/heads/master b/tests/resources/submodule_with_path/.gitted/refs/heads/master
new file mode 100644
index 000000000..4b5a5a21d
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/refs/heads/master
@@ -0,0 +1 @@
+89ca686bb21bfb75dda99a02313831a0c418f921
diff --git a/tests/revwalk/basic.c b/tests/revwalk/basic.c
index 7e50452c9..5ed7da4eb 100644
--- a/tests/revwalk/basic.c
+++ b/tests/revwalk/basic.c
@@ -437,3 +437,39 @@ 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,
+ (const git_commit **)&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..ee078b3e7 100644
--- a/tests/revwalk/mergebase.c
+++ b/tests/revwalk/mergebase.c
@@ -492,3 +492,23 @@ 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_oidarray_free(&result);
+ 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/submodule/submodule_helpers.c b/tests/submodule/submodule_helpers.c
index cde69d92d..4ff4b4da7 100644
--- a/tests/submodule/submodule_helpers.c
+++ b/tests/submodule/submodule_helpers.c
@@ -156,6 +156,21 @@ git_repository *setup_fixture_submodule_simple(void)
return repo;
}
+git_repository *setup_fixture_submodule_with_path(void)
+{
+ git_repository *repo = cl_git_sandbox_init("submodule_with_path");
+
+ cl_fixture_sandbox("testrepo.git");
+ p_mkdir("submodule_with_path/lib", 0777);
+ p_mkdir("submodule_with_path/lib/testrepo", 0777);
+
+ cl_set_cleanup(cleanup_fixture_submodules, "testrepo.git");
+
+ cl_git_pass(git_repository_reinit_filesystem(repo, 1));
+
+ return repo;
+}
+
void assert__submodule_exists(
git_repository *repo, const char *name,
const char *msg, const char *file, int line)
diff --git a/tests/submodule/submodule_helpers.h b/tests/submodule/submodule_helpers.h
index 1191ab35b..42b14a7bc 100644
--- a/tests/submodule/submodule_helpers.h
+++ b/tests/submodule/submodule_helpers.h
@@ -5,6 +5,7 @@ extern git_repository *setup_fixture_submodules(void);
extern git_repository *setup_fixture_submod2(void);
extern git_repository *setup_fixture_submodule_simple(void);
extern git_repository *setup_fixture_super(void);
+extern git_repository *setup_fixture_submodule_with_path(void);
extern unsigned int get_submodule_status(git_repository *, const char *);
diff --git a/tests/submodule/update.c b/tests/submodule/update.c
index 40d24d0a7..cbd519d81 100644
--- a/tests/submodule/update.c
+++ b/tests/submodule/update.c
@@ -131,6 +131,53 @@ void test_submodule_update__update_submodule(void)
git_submodule_free(sm);
}
+void test_submodule_update__update_submodule_with_path(void)
+{
+ git_submodule *sm;
+ git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
+ unsigned int submodule_status = 0;
+ struct update_submodule_cb_payload update_payload = { 0 };
+
+ g_repo = setup_fixture_submodule_with_path();
+
+ update_options.checkout_opts.progress_cb = checkout_progress_cb;
+ update_options.checkout_opts.progress_payload = &update_payload;
+
+ update_options.fetch_opts.callbacks.update_tips = update_tips;
+ update_options.fetch_opts.callbacks.payload = &update_payload;
+
+ /* get the submodule */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+
+ /* verify the initial state of the submodule */
+ cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
+ cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
+
+ /* initialize and update the submodule */
+ cl_git_pass(git_submodule_init(sm, 0));
+ cl_git_pass(git_submodule_update(sm, 0, &update_options));
+
+ /* verify state */
+ cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
+ cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_IN_WD);
+
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+
+ /* verify that the expected callbacks have been called. */
+ cl_assert_equal_i(1, update_payload.checkout_progress_called);
+ cl_assert_equal_i(1, update_payload.update_tips_called);
+
+ git_submodule_free(sm);
+}
+
void test_submodule_update__update_and_init_submodule(void)
{
git_submodule *sm;
@@ -390,3 +437,4 @@ void test_submodule_update__can_force_update(void)
git_object_free(branch_commit);
git_reference_free(branch_reference);
}
+