summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md8
-rw-r--r--examples/general.c55
-rw-r--r--include/git2/common.h10
-rw-r--r--include/git2/merge.h3
-rw-r--r--include/git2/submodule.h13
-rw-r--r--include/git2/sys/repository.h29
-rw-r--r--include/git2/tree.h13
-rw-r--r--src/checkout.c4
-rw-r--r--src/indexer.c39
-rw-r--r--src/merge.c8
-rw-r--r--src/pack.c17
-rw-r--r--src/pack.h1
-rw-r--r--src/refs.c29
-rw-r--r--src/refs.h3
-rw-r--r--src/repository.c33
-rw-r--r--src/repository.h1
-rw-r--r--src/settings.c5
-rw-r--r--src/submodule.c239
-rw-r--r--src/submodule.h3
-rw-r--r--src/transports/winhttp.c123
-rw-r--r--src/tree.c45
-rw-r--r--tests/checkout/tree.c38
-rw-r--r--tests/merge/trees/renames.c2
-rw-r--r--tests/pack/indexer.c41
-rw-r--r--tests/submodule/lookup.c25
25 files changed, 573 insertions, 214 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3a8db068f..0ae75e156 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,12 +3,20 @@ v0.25 + 1
### Changes or improvements
+* `GIT_MERGE_OPTIONS_INIT` now includes a setting to perform rename detection.
+ This aligns this structure with the default by `git_merge` and
+ `git_merge_trees` when `NULL` was provided for the options.
+
### API additions
### API removals
### Breaking API changes
+* `clone_checkout_strategy` has been removed from
+ `git_submodule_update_option`. The checkout strategy used to clone will
+ be the same strategy specified in `checkout_opts`.
+
v0.25
-------
diff --git a/examples/general.c b/examples/general.c
index cc0cf1b42..ff984a36c 100644
--- a/examples/general.c
+++ b/examples/general.c
@@ -247,6 +247,11 @@ static void object_database(git_repository *repo, git_oid *oid)
*/
git_oid_fmt(oid_hex, oid);
printf("Written Object: %s\n", oid_hex);
+
+ /**
+ * Free the object database after usage.
+ */
+ git_odb_free(odb);
}
/**
@@ -264,7 +269,7 @@ static void commit_writing(git_repository *repo)
git_oid tree_id, parent_id, commit_id;
git_tree *tree;
git_commit *parent;
- const git_signature *author, *cmtter;
+ git_signature *author, *committer;
char oid_hex[GIT_OID_HEXSZ+1] = { 0 };
printf("\n*Commit Writing*\n");
@@ -276,9 +281,9 @@ static void commit_writing(git_repository *repo)
* `user.email` configuration options. See the `config` section of this
* example file to see how to access config values.
*/
- git_signature_new((git_signature **)&author,
+ git_signature_new(&author,
"Scott Chacon", "schacon@gmail.com", 123456789, 60);
- git_signature_new((git_signature **)&cmtter,
+ git_signature_new(&committer,
"Scott A Chacon", "scott@github.com", 987654321, 90);
/**
@@ -301,7 +306,7 @@ static void commit_writing(git_repository *repo)
repo,
NULL, /* do not update the HEAD */
author,
- cmtter,
+ committer,
NULL, /* use default message encoding */
"example commit",
tree,
@@ -312,6 +317,14 @@ static void commit_writing(git_repository *repo)
*/
git_oid_fmt(oid_hex, &commit_id);
printf("New Commit: %s\n", oid_hex);
+
+ /**
+ * Free all objects used in the meanwhile.
+ */
+ git_tree_free(tree);
+ git_commit_free(parent);
+ git_signature_free(author);
+ git_signature_free(committer);
}
/**
@@ -431,7 +444,11 @@ static void tag_parsing(git_repository *repo)
printf("Tag Name: %s\nTag Type: %s\nTag Message: %s\n",
name, git_object_type2string(type), message);
+ /**
+ * Free both the commit and tag after usage.
+ */
git_commit_free(commit);
+ git_tag_free(tag);
}
/**
@@ -485,9 +502,10 @@ static void tree_parsing(git_repository *repo)
git_tree_entry_to_object(&obj, repo, entry); /* blob */
/**
- * Remember to close the looked-up object once you are done using it
+ * Remember to close the looked-up object and tree once you are done using it
*/
git_object_free(obj);
+ git_tree_free(tree);
}
/**
@@ -522,6 +540,11 @@ static void blob_parsing(git_repository *repo)
* */
printf("Blob Size: %ld\n", (long)git_blob_rawsize(blob)); /* 8 */
git_blob_rawcontent(blob); /* "content" */
+
+ /**
+ * Free the blob after usage.
+ */
+ git_blob_free(blob);
}
/**
@@ -644,10 +667,7 @@ static void index_walking(git_repository *repo)
static void reference_listing(git_repository *repo)
{
git_strarray ref_list;
- const char *refname;
- git_reference *ref;
unsigned i;
- char oid_hex[GIT_OID_HEXSZ+1];
printf("\n*Reference Listing*\n");
@@ -662,7 +682,10 @@ static void reference_listing(git_repository *repo)
git_reference_list(&ref_list, repo);
for (i = 0; i < ref_list.count; ++i) {
- memset(oid_hex, 0, sizeof(oid_hex));
+ git_reference *ref;
+ char oid_hex[GIT_OID_HEXSZ+1] = GIT_OID_HEX_ZERO;
+ const char *refname;
+
refname = ref_list.strings[i];
git_reference_lookup(&ref, repo, refname);
@@ -679,6 +702,8 @@ static void reference_listing(git_repository *repo)
fprintf(stderr, "Unexpected reference type\n");
exit(1);
}
+
+ git_reference_free(ref);
}
git_strarray_free(&ref_list);
@@ -696,7 +721,7 @@ static void config_files(const char *repo_path, git_repository* repo)
{
const char *email;
char config_path[256];
- int32_t j;
+ int32_t autocorrect;
git_config *cfg;
git_config *snap_cfg;
@@ -708,10 +733,16 @@ static void config_files(const char *repo_path, git_repository* repo)
sprintf(config_path, "%s/config", repo_path);
check_error(git_config_open_ondisk(&cfg, config_path), "opening config");
- git_config_get_int32(&j, cfg, "help.autocorrect");
- printf("Autocorrect: %d\n", j);
+ if (git_config_get_int32(&autocorrect, cfg, "help.autocorrect") == 0)
+ printf("Autocorrect: %d\n", autocorrect);
check_error(git_repository_config_snapshot(&snap_cfg, repo), "config snapshot");
git_config_get_string(&email, snap_cfg, "user.email");
printf("Email: %s\n", email);
+
+ /**
+ * Remember to free the configurations after usage.
+ */
+ git_config_free(cfg);
+ git_config_free(snap_cfg);
}
diff --git a/include/git2/common.h b/include/git2/common.h
index 99c99812b..5be7fb632 100644
--- a/include/git2/common.h
+++ b/include/git2/common.h
@@ -175,6 +175,7 @@ typedef enum {
GIT_OPT_SET_SSL_CERT_LOCATIONS,
GIT_OPT_SET_USER_AGENT,
GIT_OPT_ENABLE_STRICT_OBJECT_CREATION,
+ GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION,
GIT_OPT_SET_SSL_CIPHERS,
GIT_OPT_GET_USER_AGENT,
} git_libgit2_opt_t;
@@ -289,6 +290,15 @@ typedef enum {
* > will be validated when creating a new commit. This defaults
* > to enabled.
*
+ * * opts(GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, int enabled)
+ *
+ * > Validate the target of a symbolic ref when creating it. For
+ * > example, `foobar` is not a valid ref, therefore `foobar` is
+ * > not a valid target for a symbolic ref by default, whereas
+ * > `refs/heads/foobar` is. Disabling this bypasses validation
+ * > so that an arbitrary strings such as `foobar` can be used
+ * > for a symbolic ref target. This defaults to enabled.
+ *
* * opts(GIT_OPT_SET_SSL_CIPHERS, const char *ciphers)
*
* > Set the SSL ciphers use for HTTPS connections.
diff --git a/include/git2/merge.h b/include/git2/merge.h
index c6f6cba6c..94ac8b5c5 100644
--- a/include/git2/merge.h
+++ b/include/git2/merge.h
@@ -290,7 +290,8 @@ typedef struct {
} git_merge_options;
#define GIT_MERGE_OPTIONS_VERSION 1
-#define GIT_MERGE_OPTIONS_INIT {GIT_MERGE_OPTIONS_VERSION}
+#define GIT_MERGE_OPTIONS_INIT { \
+ GIT_MERGE_OPTIONS_VERSION, GIT_MERGE_FIND_RENAMES }
/**
* Initializes a `git_merge_options` with default values. Equivalent to
diff --git a/include/git2/submodule.h b/include/git2/submodule.h
index 540ecf5c7..b2b3039fe 100644
--- a/include/git2/submodule.h
+++ b/include/git2/submodule.h
@@ -134,9 +134,7 @@ typedef struct git_submodule_update_options {
* checkout, set the `checkout_strategy` to
* `GIT_CHECKOUT_NONE`. Generally you will want the use
* GIT_CHECKOUT_SAFE to update files in the working
- * directory. Use the `clone_checkout_strategy` field
- * to set the checkout strategy that will be used in
- * the case where update needs to clone the repository.
+ * directory.
*/
git_checkout_options checkout_opts;
@@ -149,13 +147,6 @@ typedef struct git_submodule_update_options {
git_fetch_options fetch_opts;
/**
- * The checkout strategy to use when the sub repository needs to
- * be cloned. Use GIT_CHECKOUT_SAFE to create all files
- * in the working directory for the newly cloned repository.
- */
- unsigned int clone_checkout_strategy;
-
- /**
* Allow fetching from the submodule's default remote if the target
* commit isn't found. Enabled by default.
*/
@@ -166,7 +157,7 @@ typedef struct git_submodule_update_options {
#define GIT_SUBMODULE_UPDATE_OPTIONS_INIT \
{ GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, \
{ GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE }, \
- GIT_FETCH_OPTIONS_INIT, GIT_CHECKOUT_SAFE, 1 }
+ GIT_FETCH_OPTIONS_INIT, 1 }
/**
* Initializes a `git_submodule_update_options` with default values.
diff --git a/include/git2/sys/repository.h b/include/git2/sys/repository.h
index 800396c86..0c9142143 100644
--- a/include/git2/sys/repository.h
+++ b/include/git2/sys/repository.h
@@ -135,6 +135,35 @@ GIT_EXTERN(void) git_repository_set_index(git_repository *repo, git_index *index
*/
GIT_EXTERN(int) git_repository_set_bare(git_repository *repo);
+/**
+ * Load and cache all submodules.
+ *
+ * Because the `.gitmodules` file is unstructured, loading submodules is an
+ * O(N) operation. Any operation (such as `git_rebase_init`) that requires
+ * accessing all submodules is O(N^2) in the number of submodules, if it
+ * has to look each one up individually. This function loads all submodules
+ * and caches them so that subsequent calls to `git_submodule_lookup` are O(1).
+ *
+ * @param repo the repository whose submodules will be cached.
+ */
+GIT_EXTERN(int) git_repository_submodule_cache_all(
+ git_repository *repo);
+
+/**
+ * Clear the submodule cache.
+ *
+ * Clear the submodule cache populated by `git_repository_submodule_cache_all`.
+ * If there is no cache, do nothing.
+ *
+ * The cache incorporates data from the repository's configuration, as well
+ * as the state of the working tree, the index, and HEAD. So any time any
+ * of these has changed, the cache might become invalid.
+ *
+ * @param repo the repository whose submodule cache will be cleared
+ */
+GIT_EXTERN(int) git_repository_submodule_cache_clear(
+ git_repository *repo);
+
/** @} */
GIT_END_DECL
#endif
diff --git a/include/git2/tree.h b/include/git2/tree.h
index 2e4735c4b..a38215f9f 100644
--- a/include/git2/tree.h
+++ b/include/git2/tree.h
@@ -375,6 +375,19 @@ GIT_EXTERN(void) git_treebuilder_filter(
GIT_EXTERN(int) git_treebuilder_write(
git_oid *id, git_treebuilder *bld);
+/**
+ * Write the contents of the tree builder as a tree object
+ * using a shared git_buf.
+ *
+ * @see git_treebuilder_write
+ *
+ * @param id Pointer to store the OID of the newly written tree
+ * @param bld Tree builder to write
+ * @param tree Shared buffer for writing the tree. Will be grown as necessary.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_treebuilder_write_with_buffer(
+ git_oid *oid, git_treebuilder *bld, git_buf *tree);
/** Callback for the tree traversal method */
typedef int (*git_treewalk_cb)(
diff --git a/src/checkout.c b/src/checkout.c
index 0cc29054d..b70d5ab35 100644
--- a/src/checkout.c
+++ b/src/checkout.c
@@ -2553,6 +2553,10 @@ int git_checkout_iterator(
GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE;
baseline_opts.start = data.pfx;
baseline_opts.end = data.pfx;
+ if (opts && (opts->checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH)) {
+ baseline_opts.pathlist.count = opts->paths.count;
+ baseline_opts.pathlist.strings = opts->paths.strings;
+ }
if (data.opts.baseline_index) {
if ((error = git_iterator_for_index(
diff --git a/src/indexer.c b/src/indexer.c
index 4e8919adb..606de2ef6 100644
--- a/src/indexer.c
+++ b/src/indexer.c
@@ -33,7 +33,7 @@ struct entry {
struct git_indexer {
unsigned int parsed_header :1,
- opened_pack :1,
+ pack_committed :1,
have_stream :1,
have_delta :1;
struct git_pack_header hdr;
@@ -151,6 +151,12 @@ cleanup:
if (fd != -1)
p_close(fd);
+ if (git_buf_len(&tmp_path) > 0)
+ p_unlink(git_buf_cstr(&tmp_path));
+
+ if (idx->pack != NULL)
+ p_unlink(idx->pack->pack_name);
+
git_buf_free(&path);
git_buf_free(&tmp_path);
git__free(idx);
@@ -477,13 +483,29 @@ static int write_at(git_indexer *idx, const void *data, git_off_t offset, size_t
static int append_to_pack(git_indexer *idx, const void *data, size_t size)
{
+ git_off_t new_size;
+ size_t mmap_alignment;
+ size_t page_offset;
+ git_off_t page_start;
git_off_t current_size = idx->pack->mwf.size;
int fd = idx->pack->mwf.fd;
+ int error;
if (!size)
return 0;
- if (p_lseek(fd, current_size + size - 1, SEEK_SET) < 0 ||
+ if ((error = git__mmap_alignment(&mmap_alignment)) < 0)
+ return error;
+
+ /* Write a single byte to force the file system to allocate space now or
+ * report an error, since we can't report errors when writing using mmap.
+ * Round the size up to the nearest page so that we only need to perform file
+ * I/O when we add a page, instead of whenever we write even a single byte. */
+ new_size = current_size + size;
+ page_offset = new_size % mmap_alignment;
+ page_start = new_size - page_offset;
+
+ if (p_lseek(fd, page_start + mmap_alignment - 1, SEEK_SET) < 0 ||
p_write(idx->pack->mwf.fd, data, 1) < 0) {
giterr_set(GITERR_OS, "cannot extend packfile '%s'", idx->pack->pack_name);
return -1;
@@ -1041,6 +1063,13 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
goto on_error;
git_mwindow_free_all(&idx->pack->mwf);
+
+ /* Truncate file to undo rounding up to next page_size in append_to_pack */
+ if (p_ftruncate(idx->pack->mwf.fd, idx->pack->mwf.size) < 0) {
+ giterr_set(GITERR_OS, "failed to truncate pack file '%s'", idx->pack->pack_name);
+ return -1;
+ }
+
/* We need to close the descriptor here so Windows doesn't choke on commit_at */
if (p_close(idx->pack->mwf.fd) < 0) {
giterr_set(GITERR_OS, "failed to close packfile");
@@ -1054,6 +1083,7 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats)
/* And don't forget to rename the packfile to its new place. */
p_rename(idx->pack->pack_name, git_buf_cstr(&filename));
+ idx->pack_committed = 1;
git_buf_free(&filename);
git_hash_ctx_cleanup(&ctx);
@@ -1074,7 +1104,7 @@ void git_indexer_free(git_indexer *idx)
git_vector_free_deep(&idx->objects);
- if (idx->pack && idx->pack->idx_cache) {
+ if (idx->pack->idx_cache) {
struct git_pack_entry *pentry;
kh_foreach_value(
idx->pack->idx_cache, pentry, { git__free(pentry); });
@@ -1085,6 +1115,9 @@ void git_indexer_free(git_indexer *idx)
git_vector_free_deep(&idx->deltas);
if (!git_mutex_lock(&git__mwindow_mutex)) {
+ if (!idx->pack_committed)
+ git_packfile_close(idx->pack, true);
+
git_packfile_free(idx->pack);
git_mutex_unlock(&git__mwindow_mutex);
}
diff --git a/src/merge.c b/src/merge.c
index 4d812da50..087178ace 100644
--- a/src/merge.c
+++ b/src/merge.c
@@ -1713,15 +1713,15 @@ static int merge_normalize_opts(
if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
return error;
- if (given != NULL)
+ if (given != NULL) {
memcpy(opts, given, sizeof(git_merge_options));
- else {
+ } else {
git_merge_options init = GIT_MERGE_OPTIONS_INIT;
memcpy(opts, &init, sizeof(init));
+ }
- opts->flags = GIT_MERGE_FIND_RENAMES;
+ if ((opts->flags & GIT_MERGE_FIND_RENAMES) && !opts->rename_threshold)
opts->rename_threshold = GIT_MERGE_DEFAULT_RENAME_THRESHOLD;
- }
if (given && given->default_driver) {
opts->default_driver = git__strdup(given->default_driver);
diff --git a/src/pack.c b/src/pack.c
index 345ff5259..243719d9c 100644
--- a/src/pack.c
+++ b/src/pack.c
@@ -991,6 +991,18 @@ git_off_t get_delta_base(
*
***********************************************************/
+void git_packfile_close(struct git_pack_file *p, bool unlink_packfile)
+{
+ if (p->mwf.fd >= 0) {
+ git_mwindow_free_all_locked(&p->mwf);
+ p_close(p->mwf.fd);
+ p->mwf.fd = -1;
+ }
+
+ if (unlink_packfile)
+ p_unlink(p->pack_name);
+}
+
void git_packfile_free(struct git_pack_file *p)
{
if (!p)
@@ -998,10 +1010,7 @@ void git_packfile_free(struct git_pack_file *p)
cache_free(&p->bases);
- if (p->mwf.fd >= 0) {
- git_mwindow_free_all_locked(&p->mwf);
- p_close(p->mwf.fd);
- }
+ git_packfile_close(p, false);
pack_index_free(p);
diff --git a/src/pack.h b/src/pack.h
index 5302db5b7..e2bf165f4 100644
--- a/src/pack.h
+++ b/src/pack.h
@@ -149,6 +149,7 @@ git_off_t get_delta_base(struct git_pack_file *p, git_mwindow **w_curs,
git_off_t *curpos, git_otype type,
git_off_t delta_obj_offset);
+void git_packfile_close(struct git_pack_file *p, bool unlink_packfile);
void git_packfile_free(struct git_pack_file *p);
int git_packfile_alloc(struct git_pack_file **pack_out, const char *path);
diff --git a/src/refs.c b/src/refs.c
index dbc7e5e8e..140b175af 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -24,6 +24,8 @@
#include <git2/signature.h>
#include <git2/commit.h>
+bool git_reference__enable_symbolic_ref_target_validation = true;
+
GIT__USE_STRMAP
#define DEFAULT_NESTING_LEVEL 5
@@ -178,7 +180,8 @@ int git_reference_name_to_id(
static int reference_normalize_for_repo(
git_refname_t out,
git_repository *repo,
- const char *name)
+ const char *name,
+ bool validate)
{
int precompose;
unsigned int flags = GIT_REF_FORMAT_ALLOW_ONELEVEL;
@@ -187,6 +190,9 @@ static int reference_normalize_for_repo(
precompose)
flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE;
+ if (!validate)
+ flags |= GIT_REF_FORMAT__VALIDATION_DISABLE;
+
return git_reference_normalize_name(out, GIT_REFNAME_MAX, name, flags);
}
@@ -213,7 +219,7 @@ int git_reference_lookup_resolved(
scan_type = GIT_REF_SYMBOLIC;
- if ((error = reference_normalize_for_repo(scan_name, repo, name)) < 0)
+ if ((error = reference_normalize_for_repo(scan_name, repo, name, true)) < 0)
return error;
if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
@@ -383,7 +389,7 @@ static int reference__create(
if (ref_out)
*ref_out = NULL;
- error = reference_normalize_for_repo(normalized, repo, name);
+ error = reference_normalize_for_repo(normalized, repo, name, true);
if (error < 0)
return error;
@@ -404,7 +410,10 @@ static int reference__create(
} else {
git_refname_t normalized_target;
- if ((error = reference_normalize_for_repo(normalized_target, repo, symbolic)) < 0)
+ error = reference_normalize_for_repo(normalized_target, repo,
+ symbolic, git_reference__enable_symbolic_ref_target_validation);
+
+ if (error < 0)
return error;
ref = git_reference__alloc_symbolic(normalized, normalized_target);
@@ -583,7 +592,7 @@ static int reference__rename(git_reference **out, git_reference *ref, const char
assert(ref && new_name && signature);
if ((error = reference_normalize_for_repo(
- normalized, git_reference_owner(ref), new_name)) < 0)
+ normalized, git_reference_owner(ref), new_name, true)) < 0)
return error;
@@ -876,6 +885,7 @@ int git_reference__normalize_name(
int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
unsigned int process_flags;
bool normalize = (buf != NULL);
+ bool validate = (flags & GIT_REF_FORMAT__VALIDATION_DISABLE) == 0;
#ifdef GIT_USE_ICONV
git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
@@ -886,7 +896,7 @@ int git_reference__normalize_name(
process_flags = flags;
current = (char *)name;
- if (*current == '/')
+ if (validate && *current == '/')
goto cleanup;
if (normalize)
@@ -902,6 +912,13 @@ int git_reference__normalize_name(
}
#endif
+ if (!validate) {
+ git_buf_sets(buf, current);
+
+ error = git_buf_oom(buf) ? -1 : 0;
+ goto cleanup;
+ }
+
while (true) {
segment_len = ensure_segment_validity(current);
if (segment_len < 0) {
diff --git a/src/refs.h b/src/refs.h
index fda9532de..80e655af7 100644
--- a/src/refs.h
+++ b/src/refs.h
@@ -15,6 +15,8 @@
#include "buffer.h"
#include "oid.h"
+extern bool git_reference__enable_symbolic_ref_target_validation;
+
#define GIT_REFS_DIR "refs/"
#define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
#define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/"
@@ -53,6 +55,7 @@
#define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE
#define GIT_REF_FORMAT__PRECOMPOSE_UNICODE (1u << 16)
+#define GIT_REF_FORMAT__VALIDATION_DISABLE (1u << 15)
#define GIT_REFNAME_MAX 1024
diff --git a/src/repository.c b/src/repository.c
index 60299193f..2185632bf 100644
--- a/src/repository.c
+++ b/src/repository.c
@@ -27,6 +27,10 @@
#include "merge.h"
#include "diff_driver.h"
#include "annotated_commit.h"
+#include "submodule.h"
+
+GIT__USE_STRMAP
+#include "strmap.h"
#ifdef GIT_WIN32
# include "win32/w32_util.h"
@@ -109,6 +113,7 @@ void git_repository__cleanup(git_repository *repo)
{
assert(repo);
+ git_repository_submodule_cache_clear(repo);
git_cache_clear(&repo->objects);
git_attr_cache_flush(repo);
@@ -2541,3 +2546,31 @@ int git_repository_set_ident(git_repository *repo, const char *name, const char
return 0;
}
+
+int git_repository_submodule_cache_all(git_repository *repo)
+{
+ int error;
+
+ assert(repo);
+
+ if ((error = git_strmap_alloc(&repo->submodule_cache)))
+ return error;
+
+ error = git_submodule__map(repo, repo->submodule_cache);
+ return error;
+}
+
+int git_repository_submodule_cache_clear(git_repository *repo)
+{
+ git_submodule *sm;
+ assert(repo);
+ if (repo->submodule_cache == NULL) {
+ return 0;
+ }
+ git_strmap_foreach_value(repo->submodule_cache, sm, {
+ git_submodule_free(sm);
+ });
+ git_strmap_free(repo->submodule_cache);
+ repo->submodule_cache = 0;
+ return 0;
+}
diff --git a/src/repository.h b/src/repository.h
index b259bea3b..9d276f376 100644
--- a/src/repository.h
+++ b/src/repository.h
@@ -143,6 +143,7 @@ struct git_repository {
git_atomic attr_session_key;
git_cvar_value cvar_cache[GIT_CVAR_CACHE_MAX];
+ git_strmap *submodule_cache;
};
GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo)
diff --git a/src/settings.c b/src/settings.c
index 980233d88..222bd6b8e 100644
--- a/src/settings.c
+++ b/src/settings.c
@@ -15,6 +15,7 @@
#include "cache.h"
#include "global.h"
#include "object.h"
+#include "refs.h"
void git_libgit2_version(int *major, int *minor, int *rev)
{
@@ -193,6 +194,10 @@ int git_libgit2_opts(int key, ...)
git_object__strict_input_validation = (va_arg(ap, int) != 0);
break;
+ case GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION:
+ git_reference__enable_symbolic_ref_target_validation = (va_arg(ap, int) != 0);
+ break;
+
case GIT_OPT_SET_SSL_CIPHERS:
#ifdef GIT_OPENSSL
{
diff --git a/src/submodule.c b/src/submodule.c
index 6d6b314d6..21e3d45e4 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -149,40 +149,53 @@ static int find_by_path(const git_config_entry *entry, void *payload)
}
/**
- * Find out the name of a submodule from its path
+ * Release the name map returned by 'load_submodule_names'.
*/
-static int name_from_path(git_buf *out, git_config *cfg, const char *path)
+static void free_submodule_names(git_strmap *names)
+{
+ git_buf *name = 0;
+ if (names == NULL)
+ return;
+ git_strmap_foreach_value(names, name, {
+ git__free(name);
+ });
+ git_strmap_free(names);
+ return;
+}
+
+/**
+ * Map submodule paths to names.
+ * TODO: for some use-cases, this might need case-folding on a
+ * case-insensitive filesystem
+ */
+static int load_submodule_names(git_strmap *out, git_config *cfg)
{
const char *key = "submodule\\..*\\.path";
git_config_iterator *iter;
git_config_entry *entry;
- int error;
+ git_buf buf = GIT_BUF_INIT;
+ int rval;
+ int error = 0;
if ((error = git_config_iterator_glob_new(&iter, cfg, key)) < 0)
return error;
- while ((error = git_config_next(&entry, iter)) == 0) {
+ while (git_config_next(&entry, iter) == 0) {
const char *fdot, *ldot;
- /* TODO: this should maybe be strcasecmp on a case-insensitive fs */
- if (strcmp(path, entry->value) != 0)
- continue;
-
fdot = strchr(entry->name, '.');
ldot = strrchr(entry->name, '.');
- git_buf_clear(out);
- git_buf_put(out, fdot + 1, ldot - fdot - 1);
- goto cleanup;
- }
-
- if (error == GIT_ITEROVER) {
- giterr_set(GITERR_SUBMODULE, "could not find a submodule name for '%s'", path);
- error = GIT_ENOTFOUND;
+ git_buf_put(&buf, fdot + 1, ldot - fdot - 1);
+ git_strmap_insert(out, entry->value, git_buf_detach(&buf), rval);
+ if (rval < 0) {
+ giterr_set(GITERR_NOMEMORY, "Error inserting submodule into hash table");
+ free_submodule_names(out);
+ return -1;
+ }
}
-cleanup:
git_config_iterator_free(iter);
- return error;
+ return 0;
}
int git_submodule_lookup(
@@ -196,6 +209,17 @@ int git_submodule_lookup(
assert(repo && name);
+ if (repo->submodule_cache != NULL) {
+ khiter_t pos = git_strmap_lookup_index(repo->submodule_cache, name);
+ if (git_strmap_valid_index(repo->submodule_cache, pos)) {
+ if (out) {
+ *out = git_strmap_value_at(repo->submodule_cache, pos);
+ GIT_REFCOUNT_INC(*out);
+ }
+ return 0;
+ }
+ }
+
if ((error = submodule_alloc(&sm, repo, name)) < 0)
return error;
@@ -324,89 +348,107 @@ done:
static int submodules_from_index(git_strmap *map, git_index *idx, git_config *cfg)
{
- int error;
- git_iterator *i;
- const git_index_entry *entry;
- git_buf name = GIT_BUF_INIT;
-
- if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0)
- return error;
-
- while (!(error = git_iterator_advance(&entry, i))) {
- khiter_t pos = git_strmap_lookup_index(map, entry->path);
- git_submodule *sm;
+ int error;
+ git_iterator *i;
+ const git_index_entry *entry;
+ git_strmap *names = 0;
+ git_strmap_alloc(&names);
+ if ((error = load_submodule_names(names, cfg)))
+ goto done;
- git_buf_clear(&name);
- if (!name_from_path(&name, cfg, entry->path)) {
- git_strmap_lookup_index(map, name.ptr);
- }
+ if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0)
+ goto done;
- if (git_strmap_valid_index(map, pos)) {
- sm = git_strmap_value_at(map, pos);
+ while (!(error = git_iterator_advance(&entry, i))) {
+ khiter_t pos = git_strmap_lookup_index(map, entry->path);
+ git_submodule *sm;
+
+ if (git_strmap_valid_index(map, pos)) {
+ sm = git_strmap_value_at(map, pos);
+
+ if (S_ISGITLINK(entry->mode))
+ submodule_update_from_index_entry(sm, entry);
+ else
+ sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
+ } else if (S_ISGITLINK(entry->mode)) {
+ khiter_t name_pos;
+ const char *name;
+
+ name_pos = git_strmap_lookup_index(names, entry->path);
+ if (git_strmap_valid_index(names, name_pos)) {
+ name = git_strmap_value_at(names, name_pos);
+ } else {
+ name = entry->path;
+ }
- if (S_ISGITLINK(entry->mode))
- submodule_update_from_index_entry(sm, entry);
- else
- sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
- } else if (S_ISGITLINK(entry->mode)) {
- if (!submodule_get_or_create(&sm, git_index_owner(idx), map, name.ptr ? name.ptr : entry->path)) {
- submodule_update_from_index_entry(sm, entry);
- git_submodule_free(sm);
- }
- }
- }
+ if (!submodule_get_or_create(&sm, git_index_owner(idx), map, name)) {
+ submodule_update_from_index_entry(sm, entry);
+ git_submodule_free(sm);
+ }
+ }
+ }
- if (error == GIT_ITEROVER)
- error = 0;
+ if (error == GIT_ITEROVER)
+ error = 0;
- git_buf_free(&name);
- git_iterator_free(i);
+done:
+ git_iterator_free(i);
+ free_submodule_names(names);
- return error;
+ return error;
}
static int submodules_from_head(git_strmap *map, git_tree *head, git_config *cfg)
{
- int error;
- git_iterator *i;
- const git_index_entry *entry;
- git_buf name = GIT_BUF_INIT;
-
- if ((error = git_iterator_for_tree(&i, head, NULL)) < 0)
- return error;
-
- while (!(error = git_iterator_advance(&entry, i))) {
- khiter_t pos = git_strmap_lookup_index(map, entry->path);
- git_submodule *sm;
+ int error;
+ git_iterator *i;
+ const git_index_entry *entry;
+ git_strmap *names = 0;
+ git_strmap_alloc(&names);
+ if ((error = load_submodule_names(names, cfg)))
+ goto done;
- git_buf_clear(&name);
- if (!name_from_path(&name, cfg, entry->path)) {
- git_strmap_lookup_index(map, name.ptr);
- }
+ if ((error = git_iterator_for_tree(&i, head, NULL)) < 0)
+ goto done;
- if (git_strmap_valid_index(map, pos)) {
- sm = git_strmap_value_at(map, pos);
+ while (!(error = git_iterator_advance(&entry, i))) {
+ khiter_t pos = git_strmap_lookup_index(map, entry->path);
+ git_submodule *sm;
+
+ if (git_strmap_valid_index(map, pos)) {
+ sm = git_strmap_value_at(map, pos);
+
+ if (S_ISGITLINK(entry->mode))
+ submodule_update_from_head_data(sm, entry->mode, &entry->id);
+ else
+ sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
+ } else if (S_ISGITLINK(entry->mode)) {
+ khiter_t name_pos;
+ const char *name;
+
+ name_pos = git_strmap_lookup_index(names, entry->path);
+ if (git_strmap_valid_index(names, name_pos)) {
+ name = git_strmap_value_at(names, name_pos);
+ } else {
+ name = entry->path;
+ }
- if (S_ISGITLINK(entry->mode))
- submodule_update_from_head_data(sm, entry->mode, &entry->id);
- else
- sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
- } else if (S_ISGITLINK(entry->mode)) {
- if (!submodule_get_or_create(&sm, git_tree_owner(head), map, name.ptr ? name.ptr : entry->path)) {
- submodule_update_from_head_data(
- sm, entry->mode, &entry->id);
- git_submodule_free(sm);
- }
- }
- }
+ if (!submodule_get_or_create(&sm, git_tree_owner(head), map, name)) {
+ submodule_update_from_head_data(
+ sm, entry->mode, &entry->id);
+ git_submodule_free(sm);
+ }
+ }
+ }
- if (error == GIT_ITEROVER)
- error = 0;
+ if (error == GIT_ITEROVER)
+ error = 0;
- git_buf_free(&name);
- git_iterator_free(i);
+done:
+ git_iterator_free(i);
+ free_submodule_names(names);
- return error;
+ return error;
}
/* If have_sm is true, sm is populated, otherwise map an repo are. */
@@ -416,7 +458,7 @@ typedef struct {
git_repository *repo;
} lfc_data;
-static int all_submodules(git_repository *repo, git_strmap *map)
+int git_submodule__map(git_repository *repo, git_strmap *map)
{
int error = 0;
git_index *idx = NULL;
@@ -509,7 +551,7 @@ int git_submodule_foreach(
if ((error = git_strmap_alloc(&submodules)) < 0)
return error;
- if ((error = all_submodules(repo, submodules)) < 0)
+ if ((error = git_submodule__map(repo, submodules)) < 0)
goto done;
if (!(error = git_vector_init(
@@ -1160,7 +1202,6 @@ int git_submodule_update(git_submodule *sm, int init, git_submodule_update_optio
* will checkout the specific commit manually.
*/
clone_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE;
- update_options.checkout_opts.checkout_strategy = update_options.clone_checkout_strategy;
if ((error = git_clone(&sub_repo, submodule_url, sm->path, &clone_options)) < 0 ||
(error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm))) < 0 ||
@@ -1502,13 +1543,22 @@ int git_submodule__status(
return 0;
}
- /* refresh the index OID */
- if (submodule_update_index(sm) < 0)
- return -1;
+ /* If the user has requested caching submodule state, performing these
+ * expensive operations (especially `submodule_update_head`, which is
+ * bottlenecked on `git_repository_head_tree`) eliminates much of the
+ * advantage. We will, therefore, interpret the request for caching to
+ * apply here to and skip them.
+ */
- /* refresh the HEAD OID */
- if (submodule_update_head(sm) < 0)
- return -1;
+ if (sm->repo->submodule_cache == NULL) {
+ /* refresh the index OID */
+ if (submodule_update_index(sm) < 0)
+ return -1;
+
+ /* refresh the HEAD OID */
+ if (submodule_update_head(sm) < 0)
+ return -1;
+ }
/* for ignore == dirty, don't scan the working directory */
if (ign == GIT_SUBMODULE_IGNORE_DIRTY) {
@@ -1566,7 +1616,6 @@ int git_submodule_location(unsigned int *location, git_submodule *sm)
location, NULL, NULL, NULL, sm, GIT_SUBMODULE_IGNORE_ALL);
}
-
/*
* INTERNAL FUNCTIONS
*/
diff --git a/src/submodule.h b/src/submodule.h
index 2ef2031b3..456a93979 100644
--- a/src/submodule.h
+++ b/src/submodule.h
@@ -143,4 +143,7 @@ extern int git_submodule_parse_ignore(
extern int git_submodule_parse_update(
git_submodule_update_t *out, const char *value);
+extern int git_submodule__map(
+ git_repository *repo,
+ git_strmap *map);
#endif
diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c
index 051340d87..79b3ac6b4 100644
--- a/src/transports/winhttp.c
+++ b/src/transports/winhttp.c
@@ -68,7 +68,8 @@ static const IID IID_IInternetSecurityManager_mingw =
typedef enum {
GIT_WINHTTP_AUTH_BASIC = 1,
- GIT_WINHTTP_AUTH_NEGOTIATE = 2,
+ GIT_WINHTTP_AUTH_NTLM = 2,
+ GIT_WINHTTP_AUTH_NEGOTIATE = 4,
} winhttp_authmechanism_t;
typedef struct {
@@ -95,79 +96,66 @@ typedef struct {
git_cred *cred;
git_cred *url_cred;
git_cred *proxy_cred;
- int auth_mechanism;
+ int auth_mechanisms;
HINTERNET session;
HINTERNET connection;
} winhttp_subtransport;
-static int apply_basic_credential_proxy(HINTERNET request, git_cred *cred)
+static int _apply_userpass_credential(HINTERNET request, DWORD target, DWORD scheme, git_cred *cred)
{
git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
wchar_t *user, *pass;
- int error;
+ int user_len = 0, pass_len = 0, error = 0;
- if ((error = git__utf8_to_16_alloc(&user, c->username)) < 0)
- return error;
+ if ((error = user_len = git__utf8_to_16_alloc(&user, c->username)) < 0)
+ goto done;
- if ((error = git__utf8_to_16_alloc(&pass, c->password)) < 0)
- return error;
+ if ((error = pass_len = git__utf8_to_16_alloc(&pass, c->password)) < 0)
+ goto done;
- if (!WinHttpSetCredentials(request, WINHTTP_AUTH_TARGET_PROXY, WINHTTP_AUTH_SCHEME_BASIC,
- user, pass, NULL)) {
- giterr_set(GITERR_OS, "failed to set proxy auth");
+ if (!WinHttpSetCredentials(request, target, scheme, user, pass, NULL)) {
+ giterr_set(GITERR_OS, "failed to set credentials");
error = -1;
}
+done:
+ if (user_len > 0)
+ git__memzero(user, user_len * sizeof(wchar_t));
+
+ if (pass_len > 0)
+ git__memzero(pass, pass_len * sizeof(wchar_t));
+
git__free(user);
git__free(pass);
return error;
}
-static int apply_basic_credential(HINTERNET request, git_cred *cred)
+static int apply_userpass_credential_proxy(HINTERNET request, git_cred *cred)
{
- git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred;
- git_buf buf = GIT_BUF_INIT, raw = GIT_BUF_INIT;
- wchar_t *wide = NULL;
- int error = -1, wide_len;
-
- git_buf_printf(&raw, "%s:%s", c->username, c->password);
-
- if (git_buf_oom(&raw) ||
- git_buf_puts(&buf, "Authorization: Basic ") < 0 ||
- git_buf_encode_base64(&buf, git_buf_cstr(&raw), raw.size) < 0)
- goto on_error;
+ return _apply_userpass_credential(request, WINHTTP_AUTH_TARGET_PROXY,
+ WINHTTP_AUTH_SCHEME_BASIC, cred);
+}
- if ((wide_len = git__utf8_to_16_alloc(&wide, git_buf_cstr(&buf))) < 0) {
- giterr_set(GITERR_OS, "failed to convert string to wide form");
- goto on_error;
- }
+static int apply_userpass_credential(HINTERNET request, int mechanisms, git_cred *cred)
+{
+ DWORD native_scheme;
- if (!WinHttpAddRequestHeaders(request, wide, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
- giterr_set(GITERR_OS, "failed to add a header to the request");
- goto on_error;
+ if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) ||
+ (mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE)) {
+ native_scheme = WINHTTP_AUTH_SCHEME_NTLM;
+ } else if (mechanisms & GIT_WINHTTP_AUTH_BASIC) {
+ native_scheme = WINHTTP_AUTH_SCHEME_BASIC;
+ } else {
+ giterr_set(GITERR_NET, "invalid authentication scheme");
+ return -1;
}
- error = 0;
-
-on_error:
- /* We were dealing with plaintext passwords, so clean up after ourselves a bit. */
- if (wide)
- memset(wide, 0x0, wide_len * sizeof(wchar_t));
-
- if (buf.size)
- memset(buf.ptr, 0x0, buf.size);
-
- if (raw.size)
- memset(raw.ptr, 0x0, raw.size);
-
- git__free(wide);
- git_buf_free(&buf);
- git_buf_free(&raw);
- return error;
+ return _apply_userpass_credential(request, WINHTTP_AUTH_TARGET_SERVER,
+ native_scheme, cred);
}
-static int apply_default_credentials(HINTERNET request)
+static int apply_default_credentials(HINTERNET request, int mechanisms)
{
/* Either the caller explicitly requested that default credentials be passed,
* or our fallback credential callback was invoked and checked that the target
@@ -177,6 +165,12 @@ static int apply_default_credentials(HINTERNET request)
* to Internet Explorer security zones, but in fact does not. */
DWORD data = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
+ if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) == 0 &&
+ (mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE) == 0) {
+ giterr_set(GITERR_NET, "invalid authentication scheme");
+ return -1;
+ }
+
if (!WinHttpSetOption(request, WINHTTP_OPTION_AUTOLOGON_POLICY, &data, sizeof(DWORD)))
return -1;
@@ -453,7 +447,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
if (t->proxy_cred) {
if (t->proxy_cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT) {
- if ((error = apply_basic_credential_proxy(s->request, t->proxy_cred)) < 0)
+ if ((error = apply_userpass_credential_proxy(s->request, t->proxy_cred)) < 0)
goto on_error;
}
}
@@ -550,13 +544,11 @@ static int winhttp_stream_connect(winhttp_stream *s)
/* If we have a credential on the subtransport, apply it to the request */
if (t->cred &&
t->cred->credtype == GIT_CREDTYPE_USERPASS_PLAINTEXT &&
- t->auth_mechanism == GIT_WINHTTP_AUTH_BASIC &&
- apply_basic_credential(s->request, t->cred) < 0)
+ apply_userpass_credential(s->request, t->auth_mechanisms, t->cred) < 0)
goto on_error;
else if (t->cred &&
t->cred->credtype == GIT_CREDTYPE_DEFAULT &&
- t->auth_mechanism == GIT_WINHTTP_AUTH_NEGOTIATE &&
- apply_default_credentials(s->request) < 0)
+ apply_default_credentials(s->request, t->auth_mechanisms) < 0)
goto on_error;
/* If no other credentials have been applied and the URL has username and
@@ -565,7 +557,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
if (!t->url_cred &&
git_cred_userpass_plaintext_new(&t->url_cred, t->connection_data.user, t->connection_data.pass) < 0)
goto on_error;
- if (apply_basic_credential(s->request, t->url_cred) < 0)
+ if (apply_userpass_credential(s->request, GIT_WINHTTP_AUTH_BASIC, t->url_cred) < 0)
goto on_error;
}
@@ -585,12 +577,12 @@ on_error:
static int parse_unauthorized_response(
HINTERNET request,
int *allowed_types,
- int *auth_mechanism)
+ int *allowed_mechanisms)
{
DWORD supported, first, target;
*allowed_types = 0;
- *auth_mechanism = 0;
+ *allowed_mechanisms = 0;
/* WinHttpQueryHeaders() must be called before WinHttpQueryAuthSchemes().
* We can assume this was already done, since we know we are unauthorized.
@@ -600,15 +592,20 @@ static int parse_unauthorized_response(
return -1;
}
- if (WINHTTP_AUTH_SCHEME_BASIC & supported) {
+ if (WINHTTP_AUTH_SCHEME_NTLM & supported) {
*allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
- *auth_mechanism = GIT_WINHTTP_AUTH_BASIC;
+ *allowed_types |= GIT_CREDTYPE_DEFAULT;
+ *allowed_mechanisms = GIT_WINHTTP_AUTH_NEGOTIATE;
}
- if ((WINHTTP_AUTH_SCHEME_NTLM & supported) ||
- (WINHTTP_AUTH_SCHEME_NEGOTIATE & supported)) {
+ if (WINHTTP_AUTH_SCHEME_NEGOTIATE & supported) {
*allowed_types |= GIT_CREDTYPE_DEFAULT;
- *auth_mechanism = GIT_WINHTTP_AUTH_NEGOTIATE;
+ *allowed_mechanisms = GIT_WINHTTP_AUTH_NEGOTIATE;
+ }
+
+ if (WINHTTP_AUTH_SCHEME_BASIC & supported) {
+ *allowed_types |= GIT_CREDTYPE_USERPASS_PLAINTEXT;
+ *allowed_mechanisms |= GIT_WINHTTP_AUTH_BASIC;
}
return 0;
@@ -1029,7 +1026,7 @@ replay:
if (status_code == HTTP_STATUS_PROXY_AUTH_REQ) {
int allowed_types;
- if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0)
+ if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanisms) < 0)
return -1;
/* TODO: extract the username from the url, no payload? */
@@ -1049,7 +1046,7 @@ replay:
if (HTTP_STATUS_DENIED == status_code && get_verb == s->verb) {
int allowed_types;
- if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanism) < 0)
+ if (parse_unauthorized_response(s->request, &allowed_types, &t->auth_mechanisms) < 0)
return -1;
if (allowed_types) {
diff --git a/src/tree.c b/src/tree.c
index 783dca41f..26da296d5 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -520,7 +520,8 @@ static int write_tree(
git_repository *repo,
git_index *index,
const char *dirname,
- size_t start)
+ size_t start,
+ git_buf *shared_buf)
{
git_treebuilder *bld = NULL;
size_t i, entries = git_index_entrycount(index);
@@ -573,7 +574,7 @@ static int write_tree(
GITERR_CHECK_ALLOC(subdir);
/* Write out the subtree */
- written = write_tree(&sub_oid, repo, index, subdir, i);
+ written = write_tree(&sub_oid, repo, index, subdir, i, shared_buf);
if (written < 0) {
git__free(subdir);
goto on_error;
@@ -605,7 +606,7 @@ static int write_tree(
}
}
- if (git_treebuilder_write(oid, bld) < 0)
+ if (git_treebuilder_write_with_buffer(oid, bld, shared_buf) < 0)
goto on_error;
git_treebuilder_free(bld);
@@ -621,6 +622,7 @@ int git_tree__write_index(
{
int ret;
git_tree *tree;
+ git_buf shared_buf = GIT_BUF_INIT;
bool old_ignore_case = false;
assert(oid && index && repo);
@@ -646,7 +648,8 @@ int git_tree__write_index(
git_index__set_ignore_case(index, false);
}
- ret = write_tree(oid, repo, index, "", 0);
+ ret = write_tree(oid, repo, index, "", 0, &shared_buf);
+ git_buf_free(&shared_buf);
if (old_ignore_case)
git_index__set_ignore_case(index, true);
@@ -802,19 +805,36 @@ int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
int git_treebuilder_write(git_oid *oid, git_treebuilder *bld)
{
+ int error;
+ git_buf buffer = GIT_BUF_INIT;
+
+ error = git_treebuilder_write_with_buffer(oid, bld, &buffer);
+
+ git_buf_free(&buffer);
+ return error;
+}
+
+int git_treebuilder_write_with_buffer(git_oid *oid, git_treebuilder *bld, git_buf *tree)
+{
int error = 0;
size_t i, entrycount;
- git_buf tree = GIT_BUF_INIT;
git_odb *odb;
git_tree_entry *entry;
git_vector entries;
assert(bld);
+ assert(tree);
+
+ git_buf_clear(tree);
entrycount = git_strmap_num_entries(bld->map);
if (git_vector_init(&entries, entrycount, entry_sort_cmp) < 0)
return -1;
+ if (tree->asize == 0 &&
+ (error = git_buf_grow(tree, entrycount * 72)) < 0)
+ return error;
+
git_strmap_foreach_value(bld->map, entry, {
if (git_vector_insert(&entries, entry) < 0)
return -1;
@@ -822,26 +842,21 @@ int git_treebuilder_write(git_oid *oid, git_treebuilder *bld)
git_vector_sort(&entries);
- /* Grow the buffer beforehand to an estimated size */
- error = git_buf_grow(&tree, entrycount * 72);
-
for (i = 0; i < entries.length && !error; ++i) {
git_tree_entry *entry = git_vector_get(&entries, i);
- git_buf_printf(&tree, "%o ", entry->attr);
- git_buf_put(&tree, entry->filename, entry->filename_len + 1);
- git_buf_put(&tree, (char *)entry->oid->id, GIT_OID_RAWSZ);
+ git_buf_printf(tree, "%o ", entry->attr);
+ git_buf_put(tree, entry->filename, entry->filename_len + 1);
+ git_buf_put(tree, (char *)entry->oid->id, GIT_OID_RAWSZ);
- if (git_buf_oom(&tree))
+ if (git_buf_oom(tree))
error = -1;
}
-
if (!error &&
!(error = git_repository_odb__weakptr(&odb, bld->repo)))
- error = git_odb_write(oid, odb, tree.ptr, tree.size, GIT_OBJ_TREE);
+ error = git_odb_write(oid, odb, tree->ptr, tree->size, GIT_OBJ_TREE);
- git_buf_free(&tree);
git_vector_free(&entries);
return error;
diff --git a/tests/checkout/tree.c b/tests/checkout/tree.c
index 4a0314a9e..c9581fdc5 100644
--- a/tests/checkout/tree.c
+++ b/tests/checkout/tree.c
@@ -422,6 +422,44 @@ void test_checkout_tree__can_checkout_with_pattern(void)
cl_assert(git_path_exists("testrepo/new.txt"));
}
+void test_checkout_tree__pathlist_checkout_ignores_non_matches(void)
+{
+ char *entries[] = { "branch_file.txt", "link_to_new.txt" };
+
+ /* reset to beginning of history (i.e. just a README file) */
+
+ g_opts.checkout_strategy =
+ GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "refs/heads/master"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
+
+ cl_assert(git_path_exists("testrepo/README"));
+ cl_assert(git_path_exists("testrepo/branch_file.txt"));
+ cl_assert(git_path_exists("testrepo/link_to_new.txt"));
+ cl_assert(git_path_exists("testrepo/new.txt"));
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "8496071c1b46c854b31185ea97743be6a8774479"));
+
+ g_opts.checkout_strategy =
+ GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
+ g_opts.paths.strings = entries;
+ g_opts.paths.count = 2;
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_assert(git_path_exists("testrepo/README"));
+ cl_assert(!git_path_exists("testrepo/branch_file.txt"));
+ cl_assert(!git_path_exists("testrepo/link_to_new.txt"));
+ cl_assert(git_path_exists("testrepo/new.txt"));
+
+ git_object_free(g_object);
+ g_object = NULL;
+
+}
+
void test_checkout_tree__can_disable_pattern_match(void)
{
char *entries[] = { "b*.txt" };
diff --git a/tests/merge/trees/renames.c b/tests/merge/trees/renames.c
index d7721c894..6f5c36b0a 100644
--- a/tests/merge/trees/renames.c
+++ b/tests/merge/trees/renames.c
@@ -242,6 +242,8 @@ void test_merge_trees_renames__no_rename_index(void)
{ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" },
};
+ opts.flags &= ~GIT_MERGE_FIND_RENAMES;
+
cl_git_pass(merge_trees_from_branches(&index, repo,
BRANCH_RENAME_OURS, BRANCH_RENAME_THEIRS,
&opts));
diff --git a/tests/pack/indexer.c b/tests/pack/indexer.c
index 49a106d98..1e514b2d6 100644
--- a/tests/pack/indexer.c
+++ b/tests/pack/indexer.c
@@ -125,3 +125,44 @@ void test_pack_indexer__fix_thin(void)
git_indexer_free(idx);
}
}
+
+static int find_tmp_file_recurs(void *opaque, git_buf *path)
+{
+ int error = 0;
+ git_buf *first_tmp_file = opaque;
+ struct stat st;
+
+ if ((error = p_lstat_posixly(path->ptr, &st)) < 0)
+ return error;
+
+ if (S_ISDIR(st.st_mode))
+ return git_path_direach(path, 0, find_tmp_file_recurs, opaque);
+
+ /* This is the template that's used in git_futils_mktmp. */
+ if (strstr(git_buf_cstr(path), "_git2_") != NULL)
+ return git_buf_sets(first_tmp_file, git_buf_cstr(path));
+
+ return 0;
+}
+
+void test_pack_indexer__no_tmp_files(void)
+{
+ git_indexer *idx = NULL;
+ git_buf path = GIT_BUF_INIT;
+ git_buf first_tmp_file = GIT_BUF_INIT;
+
+ /* Precondition: there are no temporary files. */
+ cl_git_pass(git_buf_sets(&path, clar_sandbox_path()));
+ cl_git_pass(find_tmp_file_recurs(&first_tmp_file, &path));
+ git_buf_free(&path);
+ cl_assert(git_buf_len(&first_tmp_file) == 0);
+
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL));
+ git_indexer_free(idx);
+
+ cl_git_pass(git_buf_sets(&path, clar_sandbox_path()));
+ cl_git_pass(find_tmp_file_recurs(&first_tmp_file, &path));
+ git_buf_free(&path);
+ cl_assert(git_buf_len(&first_tmp_file) == 0);
+ git_buf_free(&first_tmp_file);
+}
diff --git a/tests/submodule/lookup.c b/tests/submodule/lookup.c
index 148f9273e..e36fc44e0 100644
--- a/tests/submodule/lookup.c
+++ b/tests/submodule/lookup.c
@@ -388,3 +388,28 @@ void test_submodule_lookup__renamed(void)
cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
cl_assert_equal_i(8, data.count);
}
+
+void test_submodule_lookup_cached(void) {
+ git_submodule *sm;
+ git_submodule *sm2;
+ /* See that the simple tests still pass. */
+
+ git_repository_submodule_cache_all(g_repo);
+ test_submodule_lookup__simple_lookup();
+ git_repository_submodule_cache_clear(g_repo);
+ test_submodule_lookup__simple_lookup();
+
+ /* Check that subsequent calls return different objects when cached. */
+ git_repository_submodule_cache_all(g_repo);
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_lookup(&sm2, g_repo, "sm_unchanged"));
+ cl_assert_equal_p(sm, sm2);
+ git_submodule_free(sm2);
+
+ /* and that we get new objects again after clearing the cache. */
+ git_repository_submodule_cache_clear(g_repo);
+ cl_git_pass(git_submodule_lookup(&sm2, g_repo, "sm_unchanged"));
+ cl_assert(sm != sm2);
+ git_submodule_free(sm);
+ git_submodule_free(sm2);
+}