diff options
-rw-r--r-- | CHANGELOG.md | 8 | ||||
-rw-r--r-- | examples/general.c | 55 | ||||
-rw-r--r-- | include/git2/common.h | 10 | ||||
-rw-r--r-- | include/git2/merge.h | 3 | ||||
-rw-r--r-- | include/git2/submodule.h | 13 | ||||
-rw-r--r-- | include/git2/sys/repository.h | 29 | ||||
-rw-r--r-- | include/git2/tree.h | 13 | ||||
-rw-r--r-- | src/checkout.c | 4 | ||||
-rw-r--r-- | src/indexer.c | 39 | ||||
-rw-r--r-- | src/merge.c | 8 | ||||
-rw-r--r-- | src/pack.c | 17 | ||||
-rw-r--r-- | src/pack.h | 1 | ||||
-rw-r--r-- | src/refs.c | 29 | ||||
-rw-r--r-- | src/refs.h | 3 | ||||
-rw-r--r-- | src/repository.c | 33 | ||||
-rw-r--r-- | src/repository.h | 1 | ||||
-rw-r--r-- | src/settings.c | 5 | ||||
-rw-r--r-- | src/submodule.c | 239 | ||||
-rw-r--r-- | src/submodule.h | 3 | ||||
-rw-r--r-- | src/transports/winhttp.c | 123 | ||||
-rw-r--r-- | src/tree.c | 45 | ||||
-rw-r--r-- | tests/checkout/tree.c | 38 | ||||
-rw-r--r-- | tests/merge/trees/renames.c | 2 | ||||
-rw-r--r-- | tests/pack/indexer.c | 41 | ||||
-rw-r--r-- | tests/submodule/lookup.c | 25 |
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); +} |