diff options
author | Carlos Martín Nieto <cmn@dwim.me> | 2014-09-16 01:25:53 +0200 |
---|---|---|
committer | Carlos Martín Nieto <cmn@dwim.me> | 2014-09-16 17:02:28 +0200 |
commit | 0fef38999abc74b8237971f96295f461631d9d1d (patch) | |
tree | 6f26d1bd650140f1c4add4bf944263cd8e058d70 | |
parent | bf8756d6a2c42dc77b8a2de814a12e2ceb4487fd (diff) | |
parent | 910cd2daa6af0f3af97d283eb4c6a0452688d067 (diff) | |
download | libgit2-0fef38999abc74b8237971f96295f461631d9d1d.tar.gz |
Merge remote-tracking branch 'upstream/master' into cmn/host-cert-info
39 files changed, 796 insertions, 159 deletions
diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..be59274e8 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +; Check http://editorconfig.org/ for more informations +; Top-most EditorConfig file +root = true + +; tab indentation +[*] +indent_style = tab +trim_trailing_whitespace = true +insert_final_newline = true + +; 4-column space indentation +[*.md] +indent_style = space +indent_size = 4 diff --git a/CHANGELOG.md b/CHANGELOG.md index 97c873dc2..4664c4d41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -51,3 +51,6 @@ v0.21 + 1 * Add support for refspecs with the asterisk in the middle of a pattern. + +* Introduce git_merge_bases() and the git_oidarray type to expose all + merge bases between two commits. @@ -195,6 +195,8 @@ Here are the bindings to libgit2 that are currently available: * git2r <https://github.com/ropensci/git2r> * Ruby * Rugged <https://github.com/libgit2/rugged> +* Rust + * git2-rs <https://github.com/alexcrichton/git2-rs> * Vala * libgit2.vapi <https://github.com/apmasell/vapis/blob/master/libgit2.vapi> diff --git a/examples/tag.c b/examples/tag.c index ebb8e37b2..1d254aee5 100644 --- a/examples/tag.c +++ b/examples/tag.c @@ -236,7 +236,7 @@ static void action_create_tag(tag_state *state) git_signature_free(tagger); } -static void print_usage() +static void print_usage(void) { fprintf(stderr, "usage: see `git help tag`\n"); exit(1); diff --git a/include/git2/merge.h b/include/git2/merge.h index 9eb14ccb1..bd5ebc1bd 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -10,6 +10,7 @@ #include "common.h" #include "types.h" #include "oid.h" +#include "oidarray.h" #include "checkout.h" #include "index.h" @@ -321,6 +322,21 @@ GIT_EXTERN(int) git_merge_base( const git_oid *two); /** + * Find merge bases between two commits + * + * @param out array in which to store the resulting ids + * @param repo the repository where the commits exist + * @param one one of the commits + * @param two the other commit + * @return 0 on success, GIT_ENOTFOUND if not found or error code + */ +GIT_EXTERN(int) git_merge_bases( + git_oidarray *out, + git_repository *repo, + const git_oid *one, + const git_oid *two); + +/** * Find a merge base given a list of commits * * @param out the OID of a merge base considering all the commits diff --git a/include/git2/oidarray.h b/include/git2/oidarray.h new file mode 100644 index 000000000..0b3204597 --- /dev/null +++ b/include/git2/oidarray.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_git_oidarray_h__ +#define INCLUDE_git_oidarray_h__ + +#include "common.h" +#include "oid.h" + +GIT_BEGIN_DECL + +/** Array of object ids */ +typedef struct git_oidarray { + git_oid *ids; + size_t count; +} git_oidarray; + +/** + * Free the OID array + * + * This method must (and must only) be called on `git_oidarray` + * objects where the array is allocated by the library. Not doing so, + * will result in a memory leak. + * + * This does not free the `git_oidarray` itself, since the library will + * never allocate that object directly itself (it is more commonly embedded + * inside another struct or created on the stack). + * + * @param array git_oidarray from which to free oid data + */ +GIT_EXTERN(void) git_oidarray_free(git_oidarray *array); + +/** @} */ +GIT_END_DECL + +#endif + diff --git a/include/git2/remote.h b/include/git2/remote.h index d2cc3e8e7..055f5e517 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -384,15 +384,12 @@ GIT_EXTERN(int) git_remote_fetch( const char *reflog_message); /** - * Return whether a string is a valid remote URL * - * @param url the url to check - * @return 1 if the url is valid, 0 otherwise - */ -GIT_EXTERN(int) git_remote_valid_url(const char *url); - -/** - * Return whether the passed URL is supported by this version of the library. + * Return whether the library supports a particular URL scheme + * + * Both the built-in and externally-registered transport lists are + * searched for a transport which supports the scheme of the given + * URL. * * @param url the url to check * @return 1 if the url is supported, 0 otherwise diff --git a/include/git2/repository.h b/include/git2/repository.h index 18e515cb0..268782648 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -196,6 +196,8 @@ GIT_EXTERN(int) git_repository_init( * looking the "template_path" from the options if set, or the * `init.templatedir` global config if not, or falling back on * "/usr/share/git-core/templates" if it exists. + * * GIT_REPOSITORY_INIT_RELATIVE_GITLINK - If an alternate workdir is + * specified, use relative paths for the gitdir and core.worktree. */ typedef enum { GIT_REPOSITORY_INIT_BARE = (1u << 0), @@ -204,6 +206,7 @@ typedef enum { GIT_REPOSITORY_INIT_MKDIR = (1u << 3), GIT_REPOSITORY_INIT_MKPATH = (1u << 4), GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE = (1u << 5), + GIT_REPOSITORY_INIT_RELATIVE_GITLINK = (1u << 6), } git_repository_init_flag_t; /** diff --git a/include/git2/submodule.h b/include/git2/submodule.h index 864d1c58c..616890df6 100644 --- a/include/git2/submodule.h +++ b/include/git2/submodule.h @@ -471,6 +471,24 @@ GIT_EXTERN(git_submodule_recurse_t) git_submodule_set_fetch_recurse_submodules( GIT_EXTERN(int) git_submodule_init(git_submodule *submodule, int overwrite); /** + * Set up the subrepository for a submodule in preparation for clone. + * + * This function can be called to init and set up a submodule + * repository from a submodule in preparation to clone it from + * its remote. + * + * @param out Output pointer to the created git repository. + * @param sm The submodule to create a new subrepository from. + * @param use_gitlink Should the workdir contain a gitlink to + * the repo in .git/modules vs. repo directly in workdir. + * @return 0 on success, <0 on failure. + */ +GIT_EXTERN(int) git_submodule_repo_init( + git_repository **out, + const git_submodule *sm, + int use_gitlink); + +/** * Copy submodule remote info into submodule repo. * * This copies the information about the submodules URL into the checked out diff --git a/script/cibuild.sh b/script/cibuild.sh index c7c341ce5..dda4ead4a 100755 --- a/script/cibuild.sh +++ b/script/cibuild.sh @@ -44,7 +44,5 @@ export GITTEST_REMOTE_SSH_PASSPHRASE="" if [ -e ./libgit2_clar ]; then ./libgit2_clar -sonline::push -sonline::clone::cred_callback -sonline::clone::ssh_cert && - rm -rf $HOME/_temp/test.git && - git init --bare $HOME/_temp/test.git && # create an empty one ./libgit2_clar -sonline::clone::ssh_with_paths fi diff --git a/src/blame.c b/src/blame.c index eb977c287..2cc5e552b 100644 --- a/src/blame.c +++ b/src/blame.c @@ -316,7 +316,6 @@ static int blame_internal(git_blame *blame) ent->suspect = o; blame->ent = ent; - blame->path = blame->path; git_blame__like_git(blame, blame->options.flags); diff --git a/src/clone.c b/src/clone.c index 7835be1b1..43b839003 100644 --- a/src/clone.c +++ b/src/clone.c @@ -144,9 +144,9 @@ static int update_head_to_remote( const git_signature *signature, const char *reflog_message) { - int error = 0, found_branch = 0; + int error = 0; size_t refs_len; - git_refspec dummy_spec, *refspec; + git_refspec *refspec; const git_remote_head *remote_head, **refs; const git_oid *remote_head_id; git_buf remote_master_name = GIT_BUF_INIT; @@ -155,28 +155,30 @@ static int update_head_to_remote( if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0) return error; - /* Did we just clone an empty repository? */ - if (refs_len == 0) + /* We cloned an empty repository or one with an unborn HEAD */ + if (refs_len == 0 || strcmp(refs[0]->name, GIT_HEAD_FILE)) return setup_tracking_config( repo, "master", GIT_REMOTE_ORIGIN, GIT_REFS_HEADS_MASTER_FILE); - error = git_remote_default_branch(&branch, remote); - if (error == GIT_ENOTFOUND) { - git_buf_puts(&branch, GIT_REFS_HEADS_MASTER_FILE); - } else { - found_branch = 1; - } - - /* Get the remote's HEAD. This is always the first ref in the list. */ + /* We know we have HEAD, let's see where it points */ remote_head = refs[0]; assert(remote_head); remote_head_id = &remote_head->oid; + + error = git_remote_default_branch(&branch, remote); + if (error == GIT_ENOTFOUND) { + error = git_repository_set_head_detached( + repo, remote_head_id, signature, reflog_message); + goto cleanup; + } + refspec = git_remote__matching_refspec(remote, git_buf_cstr(&branch)); if (refspec == NULL) { - memset(&dummy_spec, 0, sizeof(git_refspec)); - refspec = &dummy_spec; + giterr_set(GITERR_NET, "the remote's default branch does not fit the refspec configuration"); + error = GIT_EINVALIDSPEC; + goto cleanup; } /* Determine the remote tracking reference name from the local master */ @@ -184,21 +186,18 @@ static int update_head_to_remote( &remote_master_name, refspec, git_buf_cstr(&branch))) < 0) - return error; + goto cleanup; - if (found_branch) { - error = update_head_to_new_branch( - repo, - remote_head_id, - git_buf_cstr(&branch), - signature, reflog_message); - } else { - error = git_repository_set_head_detached( - repo, remote_head_id, signature, reflog_message); - } + error = update_head_to_new_branch( + repo, + remote_head_id, + git_buf_cstr(&branch), + signature, reflog_message); +cleanup: git_buf_free(&remote_master_name); git_buf_free(&branch); + return error; } diff --git a/src/merge.c b/src/merge.c index add7eab64..926a600cc 100644 --- a/src/merge.c +++ b/src/merge.c @@ -27,6 +27,7 @@ #include "index.h" #include "filebuf.h" #include "config.h" +#include "oidarray.h" #include "git2/types.h" #include "git2/repository.h" @@ -39,6 +40,7 @@ #include "git2/signature.h" #include "git2/config.h" #include "git2/tree.h" +#include "git2/oidarray.h" #include "git2/sys/index.h" #define GIT_MERGE_INDEX_ENTRY_EXISTS(X) ((X).mode != 0) @@ -139,7 +141,7 @@ int git_merge_base_octopus(git_oid *out, git_repository *repo, size_t length, co return 0; } -int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const git_oid *two) +static int merge_bases(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, const git_oid *one, const git_oid *two) { git_revwalk *walk; git_vector list; @@ -173,13 +175,63 @@ int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const return GIT_ENOTFOUND; } + *out = result; + *walk_out = walk; + + return 0; + +on_error: + git_revwalk_free(walk); + return -1; + +} + +int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const git_oid *two) +{ + int error; + git_revwalk *walk; + git_commit_list *result; + + if ((error = merge_bases(&result, &walk, repo, one, two)) < 0) + return error; + git_oid_cpy(out, &result->item->oid); git_commit_list_free(&result); git_revwalk_free(walk); return 0; +} + +int git_merge_bases(git_oidarray *out, git_repository *repo, const git_oid *one, const git_oid *two) +{ + int error; + git_revwalk *walk; + git_commit_list *result, *list; + git_array_oid_t array; + + git_array_init(array); + + if ((error = merge_bases(&result, &walk, repo, one, two)) < 0) + return error; + + list = result; + while (list) { + git_oid *id = git_array_alloc(array); + if (id == NULL) + goto on_error; + + git_oid_cpy(id, &list->item->oid); + list = list->next; + } + + git_oidarray__from_array(out, &array); + git_commit_list_free(&result); + git_revwalk_free(walk); + + return 0; on_error: + git_commit_list_free(&result); git_revwalk_free(walk); return -1; } diff --git a/src/oidarray.c b/src/oidarray.c new file mode 100644 index 000000000..1d51a2958 --- /dev/null +++ b/src/oidarray.c @@ -0,0 +1,21 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ + +#include "git2/oidarray.h" +#include "oidarray.h" +#include "array.h" + +void git_oidarray_free(git_oidarray *arr) +{ + git__free(arr->ids); +} + +void git_oidarray__from_array(git_oidarray *arr, git_array_oid_t *array) +{ + arr->count = array->size; + arr->ids = array->ptr; +} diff --git a/src/oidarray.h b/src/oidarray.h new file mode 100644 index 000000000..a7215ae6c --- /dev/null +++ b/src/oidarray.h @@ -0,0 +1,18 @@ +/* + * Copyright (C) the libgit2 contributors. All rights reserved. + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_oidarray_h__ +#define INCLUDE_oidarray_h__ + +#include "common.h" +#include "git2/oidarray.h" +#include "array.h" + +typedef git_array_t(git_oid) git_array_oid_t; + +extern void git_oidarray__from_array(git_oidarray *arr, git_array_oid_t *array); + +#endif diff --git a/src/pack.c b/src/pack.c index b05aa91f1..7c1cfe03e 100644 --- a/src/pack.c +++ b/src/pack.c @@ -648,9 +648,6 @@ int git_packfile_unpack( base_type = elem->type; } - if (error < 0) - goto cleanup; - switch (base_type) { case GIT_OBJ_COMMIT: case GIT_OBJ_TREE: diff --git a/src/path.c b/src/path.c index 77f8d8858..d29b992fe 100644 --- a/src/path.c +++ b/src/path.c @@ -750,6 +750,61 @@ int git_path_cmp( return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0; } +int git_path_make_relative(git_buf *path, const char *parent) +{ + const char *p, *q, *p_dirsep, *q_dirsep; + size_t plen = path->size, newlen, depth = 1, i; + + for (p_dirsep = p = path->ptr, q_dirsep = q = parent; *p && *q; p++, q++) { + if (*p == '/' && *q == '/') { + p_dirsep = p; + q_dirsep = q; + } + else if (*p != *q) + break; + } + + /* need at least 1 common path segment */ + if ((p_dirsep == path->ptr || q_dirsep == parent) && + (*p_dirsep != '/' || *q_dirsep != '/')) { + giterr_set(GITERR_INVALID, + "%s is not a parent of %s", parent, path->ptr); + return GIT_ENOTFOUND; + } + + if (*p == '/' && !*q) + p++; + else if (!*p && *q == '/') + q++; + else if (!*p && !*q) + return git_buf_clear(path), 0; + else { + p = p_dirsep + 1; + q = q_dirsep + 1; + } + + plen -= (p - path->ptr); + + if (!*q) + return git_buf_set(path, p, plen); + + for (; (q = strchr(q, '/')) && *(q + 1); q++) + depth++; + + newlen = (depth * 3) + plen; + + if (git_buf_try_grow(path, newlen + 1, 1, 0) < 0) + return -1; + + memmove(path->ptr + (depth * 3), p, plen + 1); + + for (i = 0; i < depth; i++) + memcpy(path->ptr + (i * 3), "../", 3); + + path->size = newlen; + return 0; +} + bool git_path_has_non_ascii(const char *path, size_t pathlen) { const uint8_t *scan = (const uint8_t *)path, *end; diff --git a/src/path.h b/src/path.h index 46d6efe93..d0a9de707 100644 --- a/src/path.h +++ b/src/path.h @@ -197,6 +197,17 @@ extern bool git_path_contains(git_buf *dir, const char *item); extern bool git_path_contains_dir(git_buf *parent, const char *subdir); /** + * Make the path relative to the given parent path. + * + * @param path The path to make relative + * @param parent The parent path to make path relative to + * @return 0 if path was made relative, GIT_ENOTFOUND + * if there was not common root between the paths, + * or <0. + */ +extern int git_path_make_relative(git_buf *path, const char *parent); + +/** * Check if the given path contains the given file. * * @param dir Directory path that might contain file diff --git a/src/remote.c b/src/remote.c index 46a610c3a..dfad946d5 100644 --- a/src/remote.c +++ b/src/remote.c @@ -1058,16 +1058,20 @@ static int update_tips_for_spec( if (autotag && !git_odb_exists(odb, &head->oid)) continue; - if (git_vector_insert(&update_heads, head) < 0) + if (!autotag && git_vector_insert(&update_heads, head) < 0) goto on_error; error = git_reference_name_to_id(&old, remote->repo, refname.ptr); if (error < 0 && error != GIT_ENOTFOUND) goto on_error; - if (error == GIT_ENOTFOUND) + if (error == GIT_ENOTFOUND) { memset(&old, 0, GIT_OID_RAWSZ); + if (autotag && git_vector_insert(&update_heads, head) < 0) + goto on_error; + } + if (!git_oid__cmp(&old, &head->oid)) continue; @@ -1942,6 +1946,9 @@ int git_remote_default_branch(git_buf *out, git_remote *remote) if (heads_len == 0) return GIT_ENOTFOUND; + if (strcmp(heads[0]->name, GIT_HEAD_FILE)) + return GIT_ENOTFOUND; + git_buf_sanitize(out); /* the first one must be HEAD so if that has the symref info, we're done */ if (heads[0]->symref_target) @@ -1958,6 +1965,9 @@ int git_remote_default_branch(git_buf *out, git_remote *remote) if (git_oid_cmp(head_id, &heads[i]->oid)) continue; + if (git__prefixcmp(heads[i]->name, GIT_REFS_HEADS_DIR)) + continue; + if (!guess) { guess = heads[i]; continue; diff --git a/src/repository.c b/src/repository.c index 4f7a5feab..d86d8905a 100644 --- a/src/repository.c +++ b/src/repository.c @@ -994,7 +994,7 @@ static int repo_init_config( uint32_t mode) { int error = 0; - git_buf cfg_path = GIT_BUF_INIT; + git_buf cfg_path = GIT_BUF_INIT, worktree_path = GIT_BUF_INIT; git_config *config = NULL; bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0); bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0); @@ -1019,9 +1019,16 @@ static int repo_init_config( if (!is_bare) { SET_REPO_CONFIG(bool, "core.logallrefupdates", true); - if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD)) - SET_REPO_CONFIG(string, "core.worktree", work_dir); - else if (is_reinit) { + if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD)) { + if ((error = git_buf_sets(&worktree_path, work_dir)) < 0) + goto cleanup; + + if ((flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK)) + if ((error = git_path_make_relative(&worktree_path, repo_dir)) < 0) + goto cleanup; + + SET_REPO_CONFIG(string, "core.worktree", worktree_path.ptr); + } else if (is_reinit) { if (git_config_delete_entry(config, "core.worktree") < 0) giterr_clear(); } @@ -1038,6 +1045,7 @@ static int repo_init_config( cleanup: git_buf_free(&cfg_path); + git_buf_free(&worktree_path); git_config_free(config); return error; @@ -1126,10 +1134,11 @@ static int repo_write_template( } static int repo_write_gitlink( - const char *in_dir, const char *to_repo) + const char *in_dir, const char *to_repo, bool use_relative_path) { int error; git_buf buf = GIT_BUF_INIT; + git_buf path_to_repo = GIT_BUF_INIT; struct stat st; git_path_dirname_r(&buf, to_repo); @@ -1157,13 +1166,20 @@ static int repo_write_gitlink( git_buf_clear(&buf); - error = git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo); + error = git_buf_sets(&path_to_repo, to_repo); + + if (!error && use_relative_path) + error = git_path_make_relative(&path_to_repo, in_dir); + + if (!error) + error = git_buf_join(&buf, ' ', GIT_FILE_CONTENT_PREFIX, path_to_repo.ptr); if (!error) error = repo_write_template(in_dir, true, DOT_GIT, 0666, true, buf.ptr); cleanup: git_buf_free(&buf); + git_buf_free(&path_to_repo); return error; } @@ -1207,7 +1223,7 @@ static int repo_init_structure( if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 && (opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD) == 0) { - if (repo_write_gitlink(work_dir, repo_dir) < 0) + if (repo_write_gitlink(work_dir, repo_dir, opts->flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK) < 0) return -1; } @@ -1635,7 +1651,7 @@ int git_repository_set_workdir( if (git_repository_config__weakptr(&config, repo) < 0) return -1; - error = repo_write_gitlink(path.ptr, git_repository_path(repo)); + error = repo_write_gitlink(path.ptr, git_repository_path(repo), false); /* passthrough error means gitlink is unnecessary */ if (error == GIT_PASSTHROUGH) diff --git a/src/revparse.c b/src/revparse.c index 60872e187..e0ec3941d 100644 --- a/src/revparse.c +++ b/src/revparse.c @@ -205,7 +205,6 @@ cleanup: static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t identifier) { git_reflog *reflog; - int error = -1; size_t numentries; const git_reflog_entry *entry; bool search_by_pos = (identifier <= 100000000); @@ -216,21 +215,11 @@ static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t ide numentries = git_reflog_entrycount(reflog); if (search_by_pos) { - if (numentries < identifier + 1) { - giterr_set( - GITERR_REFERENCE, - "Reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ, - git_reference_name(ref), numentries, identifier); - - error = GIT_ENOTFOUND; - goto cleanup; - } + if (numentries < identifier + 1) + goto notfound; entry = git_reflog_entry_byindex(reflog, identifier); git_oid_cpy(oid, git_reflog_entry_id_new(entry)); - error = 0; - goto cleanup; - } else { size_t i; git_time commit_time; @@ -243,16 +232,24 @@ static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t ide continue; git_oid_cpy(oid, git_reflog_entry_id_new(entry)); - error = 0; - goto cleanup; + break; } - error = GIT_ENOTFOUND; + if (i == numentries) + goto notfound; } -cleanup: git_reflog_free(reflog); - return error; + return 0; + +notfound: + giterr_set( + GITERR_REFERENCE, + "Reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ, + git_reference_name(ref), numentries, identifier); + + git_reflog_free(reflog); + return GIT_ENOTFOUND; } static int retrieve_revobject_from_reflog(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, size_t position) diff --git a/src/signature.c b/src/signature.c index 2545b7519..2a16b484a 100644 --- a/src/signature.c +++ b/src/signature.c @@ -70,9 +70,9 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema if (p->name == NULL || p->email == NULL) return -1; /* oom */ - if (p->name[0] == '\0') { + if (p->name[0] == '\0' || p->email[0] == '\0') { git_signature_free(p); - return signature_error("Signature cannot have an empty name"); + return signature_error("Signature cannot have an empty name or email"); } p->when.time = time; diff --git a/src/stash.c b/src/stash.c index 86e0a627c..22f756e35 100644 --- a/src/stash.c +++ b/src/stash.c @@ -634,7 +634,8 @@ int git_stash_drop( entry = git_reflog_entry_byindex(reflog, 0); git_reference_free(stash); - if ((error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1, NULL, NULL) < 0)) + error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, &entry->oid_cur, 1, NULL, NULL); + if (error < 0) goto cleanup; /* We need to undo the writing that we just did */ diff --git a/src/submodule.c b/src/submodule.c index b1291df8e..ccc8ad117 100644 --- a/src/submodule.c +++ b/src/submodule.c @@ -306,6 +306,56 @@ void git_submodule_cache_free(git_repository *repo) submodule_cache_free(cache); } +static int submodule_repo_init( + git_repository **out, + git_repository *parent_repo, + const char *path, + const char *url, + bool use_gitlink) +{ + int error = 0; + git_buf workdir = GIT_BUF_INIT, repodir = GIT_BUF_INIT; + git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT; + git_repository *subrepo = NULL; + + error = git_buf_joinpath(&workdir, git_repository_workdir(parent_repo), path); + if (error < 0) + goto cleanup; + + initopt.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_NO_REINIT; + initopt.origin_url = url; + + /* init submodule repository and add origin remote as needed */ + + /* New style: sub-repo goes in <repo-dir>/modules/<name>/ with a + * gitlink in the sub-repo workdir directory to that repository + * + * Old style: sub-repo goes directly into repo/<name>/.git/ + */ + if (use_gitlink) { + error = git_buf_join3( + &repodir, '/', git_repository_path(parent_repo), "modules", path); + if (error < 0) + goto cleanup; + + initopt.workdir_path = workdir.ptr; + initopt.flags |= + GIT_REPOSITORY_INIT_NO_DOTGIT_DIR | + GIT_REPOSITORY_INIT_RELATIVE_GITLINK; + + error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt); + } else + error = git_repository_init_ext(&subrepo, workdir.ptr, &initopt); + +cleanup: + git_buf_free(&workdir); + git_buf_free(&repodir); + + *out = subrepo; + + return error; +} + int git_submodule_add_setup( git_submodule **out, git_repository *repo, @@ -317,7 +367,6 @@ int git_submodule_add_setup( git_config_backend *mods = NULL; git_submodule *sm = NULL; git_buf name = GIT_BUF_INIT, real_url = GIT_BUF_INIT; - git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT; git_repository *subrepo = NULL; assert(repo && url && path); @@ -371,41 +420,14 @@ int git_submodule_add_setup( if (error < 0) goto cleanup; - /* New style: sub-repo goes in <repo-dir>/modules/<name>/ with a - * gitlink in the sub-repo workdir directory to that repository - * - * Old style: sub-repo goes directly into repo/<name>/.git/ + /* if the repo does not already exist, then init a new repo and add it. + * Otherwise, just add the existing repo. */ - - initopt.flags = GIT_REPOSITORY_INIT_MKPATH | - GIT_REPOSITORY_INIT_NO_REINIT; - initopt.origin_url = real_url.ptr; - - if (git_path_exists(name.ptr) && - git_path_contains(&name, DOT_GIT)) - { - /* repo appears to already exist - reinit? */ - } - else if (use_gitlink) { - git_buf repodir = GIT_BUF_INIT; - - error = git_buf_join3( - &repodir, '/', git_repository_path(repo), "modules", path); - if (error < 0) + if (!(git_path_exists(name.ptr) && + git_path_contains(&name, DOT_GIT))) { + if ((error = submodule_repo_init(&subrepo, repo, path, real_url.ptr, use_gitlink)) < 0) goto cleanup; - - initopt.workdir_path = name.ptr; - initopt.flags |= GIT_REPOSITORY_INIT_NO_DOTGIT_DIR; - - error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt); - - git_buf_free(&repodir); } - else { - error = git_repository_init_ext(&subrepo, name.ptr, &initopt); - } - if (error < 0) - goto cleanup; /* add submodule to hash and "reload" it */ @@ -437,6 +459,23 @@ cleanup: return error; } +int git_submodule_repo_init( + git_repository **out, + const git_submodule *sm, + int use_gitlink) +{ + int error; + git_repository *sub_repo = NULL; + + assert(out && sm); + + error = submodule_repo_init(&sub_repo, sm->repo, sm->path, sm->url, use_gitlink); + + *out = sub_repo; + + return error; +} + int git_submodule_add_finalize(git_submodule *sm) { int error; @@ -1897,6 +1936,7 @@ static void submodule_get_index_status(unsigned int *status, git_submodule *sm) *status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED; } + static void submodule_get_wd_status( unsigned int *status, git_submodule *sm, diff --git a/src/transport.c b/src/transport.c index d390e1422..d42c92684 100644 --- a/src/transport.c +++ b/src/transport.c @@ -25,16 +25,13 @@ static git_smart_subtransport_definition ssh_subtransport_definition = { git_sma #endif static transport_definition local_transport_definition = { "file://", git_transport_local, NULL }; -#ifdef GIT_SSH -static transport_definition ssh_transport_definition = { "ssh://", git_transport_smart, &ssh_subtransport_definition }; -#else -static transport_definition dummy_transport_definition = { NULL, git_transport_dummy, NULL }; -#endif static transport_definition transports[] = { { "git://", git_transport_smart, &git_subtransport_definition }, { "http://", git_transport_smart, &http_subtransport_definition }, +#if defined(GIT_SSL) || defined(GIT_WINHTTP) { "https://", git_transport_smart, &http_subtransport_definition }, +#endif { "file://", git_transport_local, NULL }, #ifdef GIT_SSH { "ssh://", git_transport_smart, &ssh_subtransport_definition }, @@ -95,11 +92,6 @@ static int transport_find_fn( if (!definition && strrchr(url, ':')) { // re-search transports again with ssh:// as url so that we can find a third party ssh transport definition = transport_find_by_url("ssh://"); -#ifndef GIT_SSH - if (!definition) { - definition = &dummy_transport_definition; - } -#endif } #ifndef GIT_WIN32 @@ -121,15 +113,6 @@ static int transport_find_fn( * Public API * **************/ -int git_transport_dummy(git_transport **transport, git_remote *owner, void *param) -{ - GIT_UNUSED(transport); - GIT_UNUSED(owner); - GIT_UNUSED(param); - giterr_set(GITERR_NET, "This transport isn't implemented. Sorry"); - return -1; -} - int git_transport_new(git_transport **out, git_remote *owner, const char *url) { git_transport_cb fn; @@ -229,24 +212,13 @@ done: return error; } -/* from remote.h */ -int git_remote_valid_url(const char *url) -{ - git_transport_cb fn; - void *param; - - return !transport_find_fn(&fn, url, ¶m); -} - int git_remote_supported_url(const char* url) { git_transport_cb fn; void *param; - if (transport_find_fn(&fn, url, ¶m) < 0) - return 0; - - return fn != &git_transport_dummy; + /* The only error we expect is ENOTFOUND */ + return !transport_find_fn(&fn, url, ¶m); } int git_transport_init(git_transport *opts, unsigned int version) diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index 82891165f..5ca5065ac 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -314,7 +314,7 @@ static int wait_while_ack(gitno_buffer *buf) break; if (pkt->type == GIT_PKT_ACK && - (pkt->status != GIT_ACK_CONTINUE || + (pkt->status != GIT_ACK_CONTINUE && pkt->status != GIT_ACK_COMMON)) { git__free(pkt); return 0; diff --git a/src/transports/ssh.c b/src/transports/ssh.c index 8ea4a25d7..a0f6dd722 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -292,6 +292,10 @@ static int ssh_agent_auth(LIBSSH2_SESSION *session, git_cred_ssh_key *c) { } shutdown: + + if (rc != LIBSSH2_ERROR_NONE) + ssh_error(session, "error authenticating"); + libssh2_agent_disconnect(agent); libssh2_agent_free(agent); @@ -305,6 +309,7 @@ static int _git_ssh_authenticate_session( int rc; do { + giterr_clear(); switch (cred->credtype) { case GIT_CREDTYPE_USERPASS_PLAINTEXT: { git_cred_userpass_plaintext *c = (git_cred_userpass_plaintext *)cred; @@ -361,7 +366,8 @@ static int _git_ssh_authenticate_session( return GIT_EAUTH; if (rc != LIBSSH2_ERROR_NONE) { - ssh_error(session, "Failed to authenticate SSH session"); + if (!giterr_last()) + ssh_error(session, "Failed to authenticate SSH session"); return -1; } diff --git a/tests/commit/signature.c b/tests/commit/signature.c index e9dcfab41..41a74b999 100644 --- a/tests/commit/signature.c +++ b/tests/commit/signature.c @@ -56,8 +56,8 @@ void test_commit_signature__create_empties(void) cl_git_fail(try_build_signature("", "emeric.fermas@gmail.com", 1234567890, 60)); cl_git_fail(try_build_signature(" ", "emeric.fermas@gmail.com", 1234567890, 60)); - cl_git_pass(try_build_signature("nulltoken", "", 1234567890, 60)); - cl_git_pass(try_build_signature("nulltoken", " ", 1234567890, 60)); + cl_git_fail(try_build_signature("nulltoken", "", 1234567890, 60)); + cl_git_fail(try_build_signature("nulltoken", " ", 1234567890, 60)); } void test_commit_signature__create_one_char(void) diff --git a/tests/fetchhead/fetchhead_data.h b/tests/fetchhead/fetchhead_data.h index 94402abd5..c75b65b90 100644 --- a/tests/fetchhead/fetchhead_data.h +++ b/tests/fetchhead/fetchhead_data.h @@ -16,6 +16,11 @@ "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" \ "6e0c7bdb9b4ed93212491ee778ca1c65047cab4e\tnot-for-merge\ttag 'nearly-dangling' of git://github.com/libgit2/TestGitRepository\n" +#define FETCH_HEAD_WILDCARD_DATA2 \ + "49322bb17d3acc9146f98c97d078513228bbf3c0\t\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \ + "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \ + "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \ + #define FETCH_HEAD_NO_MERGE_DATA \ "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \ "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \ @@ -25,6 +30,16 @@ "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" \ "6e0c7bdb9b4ed93212491ee778ca1c65047cab4e\tnot-for-merge\ttag 'nearly-dangling' of git://github.com/libgit2/TestGitRepository\n" +#define FETCH_HEAD_NO_MERGE_DATA2 \ + "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \ + "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \ + "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \ + +#define FETCH_HEAD_NO_MERGE_DATA3 \ + "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" \ + "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of git://github.com/libgit2/TestGitRepository\n" \ + "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of git://github.com/libgit2/TestGitRepository\n" \ + "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of git://github.com/libgit2/TestGitRepository\n" \ #define FETCH_HEAD_EXPLICIT_DATA \ "0966a434eb1a025db6b71485ab63a3bfbea520b6\t\tbranch 'first-merge' of git://github.com/libgit2/TestGitRepository\n" diff --git a/tests/network/remote/defaultbranch.c b/tests/network/remote/defaultbranch.c index fa3a329db..243369fa2 100644 --- a/tests/network/remote/defaultbranch.c +++ b/tests/network/remote/defaultbranch.c @@ -48,3 +48,61 @@ void test_network_remote_defaultbranch__master_on_detached(void) cl_git_pass(git_repository_detach_head(g_repo_a, NULL, NULL)); assert_default_branch("refs/heads/master"); } + +void test_network_remote_defaultbranch__no_default_branch(void) +{ + git_remote *remote_b; + const git_remote_head **heads; + size_t len; + git_buf buf = GIT_BUF_INIT; + + cl_git_pass(git_remote_create(&remote_b, g_repo_b, "self", git_repository_path(g_repo_b))); + cl_git_pass(git_remote_connect(remote_b, GIT_DIRECTION_FETCH)); + cl_git_pass(git_remote_ls(&heads, &len, remote_b)); + cl_assert_equal_i(0, len); + + cl_git_fail_with(GIT_ENOTFOUND, git_remote_default_branch(&buf, remote_b)); + + git_remote_free(remote_b); +} + +void test_network_remote_defaultbranch__detached_sharing_nonbranch_id(void) +{ + git_oid id, id_cloned; + git_reference *ref; + git_buf buf = GIT_BUF_INIT; + git_repository *cloned_repo; + + cl_git_pass(git_reference_name_to_id(&id, g_repo_a, "HEAD")); + cl_git_pass(git_repository_detach_head(g_repo_a, NULL, NULL)); + cl_git_pass(git_reference_remove(g_repo_a, "refs/heads/master")); + cl_git_pass(git_reference_remove(g_repo_a, "refs/heads/not-good")); + cl_git_pass(git_reference_create(&ref, g_repo_a, "refs/foo/bar", &id, 1, NULL, NULL)); + git_reference_free(ref); + + cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH)); + cl_git_fail_with(GIT_ENOTFOUND, git_remote_default_branch(&buf, g_remote)); + + cl_git_pass(git_clone(&cloned_repo, git_repository_path(g_repo_a), "./local-detached", NULL)); + + cl_assert(git_repository_head_detached(cloned_repo)); + cl_git_pass(git_reference_name_to_id(&id_cloned, g_repo_a, "HEAD")); + cl_assert(git_oid_equal(&id, &id_cloned)); + + git_repository_free(cloned_repo); +} + +void test_network_remote_defaultbranch__unborn_HEAD_with_branches(void) +{ + git_reference *ref; + git_repository *cloned_repo; + + cl_git_pass(git_reference_symbolic_create(&ref, g_repo_a, "HEAD", "refs/heads/i-dont-exist", 1, NULL, NULL)); + git_reference_free(ref); + + cl_git_pass(git_clone(&cloned_repo, git_repository_path(g_repo_a), "./semi-empty", NULL)); + + cl_assert(git_repository_head_unborn(cloned_repo)); + + git_repository_free(cloned_repo); +} diff --git a/tests/network/remote/remotes.c b/tests/network/remote/remotes.c index 21c57119a..d176774ea 100644 --- a/tests/network/remote/remotes.c +++ b/tests/network/remote/remotes.c @@ -91,26 +91,24 @@ void test_network_remote_remotes__error_when_no_push_available(void) git_remote_free(r); } -void test_network_remote_remotes__parsing_ssh_remote(void) +void test_network_remote_remotes__supported_urls(void) { - cl_assert( git_remote_valid_url("git@github.com:libgit2/libgit2.git") ); -} - -void test_network_remote_remotes__parsing_local_path_fails_if_path_not_found(void) -{ - cl_assert( !git_remote_valid_url("/home/git/repos/libgit2.git") ); -} + int ssh_supported = 0, https_supported = 0; -void test_network_remote_remotes__supported_transport_methods_are_supported(void) -{ - cl_assert( git_remote_supported_url("git://github.com/libgit2/libgit2") ); -} +#ifdef GIT_SSH + ssh_supported = 1; +#endif -void test_network_remote_remotes__unsupported_transport_methods_are_unsupported(void) -{ -#ifndef GIT_SSH - cl_assert( !git_remote_supported_url("git@github.com:libgit2/libgit2.git") ); +#if defined(GIT_SSL) || defined(GIT_WINHTTP) + https_supported = 1; #endif + + cl_assert(git_remote_supported_url("git://github.com/libgit2/libgit2")); + cl_assert(git_remote_supported_url("http://github.com/libgit2/libgit2")); + + cl_assert_equal_i(ssh_supported, git_remote_supported_url("git@github.com:libgit2/libgit2.git")); + cl_assert_equal_i(ssh_supported, git_remote_supported_url("ssh://git@github.com/libgit2/libgit2.git")); + cl_assert_equal_i(https_supported, git_remote_supported_url("https://github.com/libgit2/libgit2.git")); } void test_network_remote_remotes__refspec_parsing(void) @@ -511,3 +509,53 @@ void test_network_remote_remotes__query_refspecs(void) git_remote_free(remote); } + +static int remote_single_branch(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload) +{ + char *fetch_refspecs[] = { + "refs/heads/first-merge:refs/remotes/origin/first-merge", + }; + git_strarray fetch_refspecs_strarray = { + fetch_refspecs, + 1, + }; + + GIT_UNUSED(payload); + + cl_git_pass(git_remote_create(out, repo, name, url)); + cl_git_pass(git_remote_set_fetch_refspecs(*out, &fetch_refspecs_strarray)); + + return 0; +} + +void test_network_remote_remotes__single_branch(void) +{ + git_clone_options opts = GIT_CLONE_OPTIONS_INIT; + git_repository *repo; + git_strarray refs; + size_t i, count = 0; + + opts.remote_cb = remote_single_branch; + opts.checkout_branch = "first-merge"; + + cl_git_pass(git_clone(&repo, "git://github.com/libgit2/TestGitRepository", "./single-branch", &opts)); + cl_git_pass(git_reference_list(&refs, repo)); + + for (i = 0; i < refs.count; i++) { + if (!git__prefixcmp(refs.strings[i], "refs/heads/")) + count++; + } + cl_assert_equal_i(1, count); + + git_repository_free(repo); +} + +void test_network_remote_remotes__restricted_refspecs(void) +{ + git_clone_options opts = GIT_CLONE_OPTIONS_INIT; + git_repository *repo; + + opts.remote_cb = remote_single_branch; + + cl_git_fail_with(GIT_EINVALIDSPEC, git_clone(&repo, "git://github.com/libgit2/TestGitRepository", "./restrict-refspec", &opts)); +} diff --git a/tests/online/clone.c b/tests/online/clone.c index 2e51364f6..f88a4d611 100644 --- a/tests/online/clone.c +++ b/tests/online/clone.c @@ -384,6 +384,9 @@ void test_online_clone__ssh_auth_methods(void) { int with_user; +#ifndef GIT_SSH + clar__skip(); +#endif g_options.remote_callbacks.credentials = check_ssh_auth_methods; g_options.remote_callbacks.payload = &with_user; @@ -436,6 +439,9 @@ void test_online_clone__ssh_with_paths(void) const char *remote_url = cl_getenv("GITTEST_REMOTE_URL"); const char *remote_user = cl_getenv("GITTEST_REMOTE_USER"); +#ifndef GIT_SSH + clar__skip(); +#endif if (!remote_url || !remote_user || strncmp(remote_url, "ssh://", 5) != 0) clar__skip(); @@ -459,6 +465,9 @@ static int cred_foo_bar(git_cred **cred, const char *url, const char *username_f void test_online_clone__ssh_cannot_change_username(void) { +#ifndef GIT_SSH + clar__skip(); +#endif g_options.remote_callbacks.credentials = cred_foo_bar; cl_git_fail(git_clone(&g_repo, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options)); diff --git a/tests/online/fetchhead.c b/tests/online/fetchhead.c index 0b3f20db1..3f27e1331 100644 --- a/tests/online/fetchhead.c +++ b/tests/online/fetchhead.c @@ -67,6 +67,11 @@ static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fet void test_online_fetchhead__wildcard_spec(void) { fetchhead_test_clone(); + fetchhead_test_fetch(NULL, FETCH_HEAD_WILDCARD_DATA2); + cl_git_pass(git_tag_delete(g_repo, "annotated_tag")); + cl_git_pass(git_tag_delete(g_repo, "blob")); + cl_git_pass(git_tag_delete(g_repo, "commit_tree")); + cl_git_pass(git_tag_delete(g_repo, "nearly-dangling")); fetchhead_test_fetch(NULL, FETCH_HEAD_WILDCARD_DATA); } @@ -87,5 +92,12 @@ void test_online_fetchhead__no_merges(void) cl_git_pass(git_config_delete_entry(config, "branch.master.merge")); git_config_free(config); + fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA2); + cl_git_pass(git_tag_delete(g_repo, "annotated_tag")); + cl_git_pass(git_tag_delete(g_repo, "blob")); + cl_git_pass(git_tag_delete(g_repo, "commit_tree")); + cl_git_pass(git_tag_delete(g_repo, "nearly-dangling")); fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA); + cl_git_pass(git_tag_delete(g_repo, "commit_tree")); + fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA3); } diff --git a/tests/online/push.c b/tests/online/push.c index 4d88bdf7f..70ec705fe 100644 --- a/tests/online/push.c +++ b/tests/online/push.c @@ -864,7 +864,6 @@ void test_online_push__notes(void) push_status exp_stats[] = { { "refs/notes/commits", 1 } }; expected_ref exp_refs[] = { { "refs/notes/commits", &expected_oid } }; const char *specs_del[] = { ":refs/notes/commits" }; - expected_ref exp_refs_del[] = { }; git_oid_fromstr(&expected_oid, "8461a99b27b7043e58ff6e1f5d2cf07d282534fb"); @@ -882,7 +881,7 @@ void test_online_push__notes(void) do_push(specs_del, ARRAY_SIZE(specs_del), exp_stats, 1, - exp_refs_del, ARRAY_SIZE(exp_refs_del), 0, 0, 0); + NULL, 0, 0, 0, 0); git_signature_free(signature); } diff --git a/tests/path/core.c b/tests/path/core.c new file mode 100644 index 000000000..be63e309b --- /dev/null +++ b/tests/path/core.c @@ -0,0 +1,55 @@ +#include "clar_libgit2.h" +#include "path.h" + +static void test_make_relative( + const char *expected_path, + const char *path, + const char *parent, + int expected_status) +{ + git_buf buf = GIT_BUF_INIT; + git_buf_puts(&buf, path); + cl_assert_equal_i(expected_status, git_path_make_relative(&buf, parent)); + cl_assert_equal_s(expected_path, buf.ptr); + git_buf_free(&buf); +} + +void test_path_core__make_relative(void) +{ + git_buf buf = GIT_BUF_INIT; + + test_make_relative("foo.c", "/path/to/foo.c", "/path/to", 0); + test_make_relative("bar/foo.c", "/path/to/bar/foo.c", "/path/to", 0); + test_make_relative("foo.c", "/path/to/foo.c", "/path/to/", 0); + + test_make_relative("", "/path/to", "/path/to", 0); + test_make_relative("", "/path/to", "/path/to/", 0); + + test_make_relative("../", "/path/to", "/path/to/foo", 0); + + test_make_relative("../foo.c", "/path/to/foo.c", "/path/to/bar", 0); + test_make_relative("../bar/foo.c", "/path/to/bar/foo.c", "/path/to/baz", 0); + + test_make_relative("../../foo.c", "/path/to/foo.c", "/path/to/foo/bar", 0); + test_make_relative("../../foo/bar.c", "/path/to/foo/bar.c", "/path/to/bar/foo", 0); + + test_make_relative("../../foo.c", "/foo.c", "/bar/foo", 0); + + test_make_relative("foo.c", "/path/to/foo.c", "/path/to/", 0); + test_make_relative("../foo.c", "/path/to/foo.c", "/path/to/bar/", 0); + + test_make_relative("foo.c", "d:/path/to/foo.c", "d:/path/to", 0); + + test_make_relative("../foo", "/foo", "/bar", 0); + test_make_relative("path/to/foo.c", "/path/to/foo.c", "/", 0); + test_make_relative("../foo", "path/to/foo", "path/to/bar", 0); + + test_make_relative("/path/to/foo.c", "/path/to/foo.c", "d:/path/to", GIT_ENOTFOUND); + test_make_relative("d:/path/to/foo.c", "d:/path/to/foo.c", "/path/to", GIT_ENOTFOUND); + + test_make_relative("/path/to/foo.c", "/path/to/foo.c", "not-a-rooted-path", GIT_ENOTFOUND); + test_make_relative("not-a-rooted-path", "not-a-rooted-path", "/path/to", GIT_ENOTFOUND); + + test_make_relative("/path", "/path", "pathtofoo", GIT_ENOTFOUND); + test_make_relative("path", "path", "pathtofoo", GIT_ENOTFOUND); +} diff --git a/tests/repo/init.c b/tests/repo/init.c index aea383c64..999afd625 100644 --- a/tests/repo/init.c +++ b/tests/repo/init.c @@ -367,6 +367,84 @@ void test_repo_init__extended_1(void) cl_fixture_cleanup("root"); } +void test_repo_init__relative_gitdir(void) +{ + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + git_config *cfg; + const char *worktree_path; + git_buf dot_git_content = GIT_BUF_INIT; + + opts.workdir_path = "../c_wd"; + opts.flags = + GIT_REPOSITORY_INIT_MKPATH | + GIT_REPOSITORY_INIT_RELATIVE_GITLINK | + GIT_REPOSITORY_INIT_NO_DOTGIT_DIR; + + /* make the directory first, then it should succeed */ + cl_git_pass(git_repository_init_ext(&_repo, "root/b/my_repository", &opts)); + + cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "root/b/c_wd/")); + cl_assert(!git__suffixcmp(git_repository_path(_repo), "root/b/my_repository/")); + cl_assert(!git_repository_is_bare(_repo)); + cl_assert(git_repository_is_empty(_repo)); + + /* Verify that the gitlink and worktree entries are relative */ + + /* Verify worktree */ + cl_git_pass(git_repository_config(&cfg, _repo)); + cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree")); + cl_assert_equal_s("../c_wd/", worktree_path); + + /* Verify gitlink */ + cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git")); + cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr); + + git_buf_free(&dot_git_content); + git_config_free(cfg); + cleanup_repository("root"); +} + +void test_repo_init__relative_gitdir_2(void) +{ + git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; + git_config *cfg; + const char *worktree_path; + git_buf dot_git_content = GIT_BUF_INIT; + git_buf full_path = GIT_BUF_INIT; + + cl_git_pass(git_path_prettify(&full_path, ".", NULL)); + cl_git_pass(git_buf_joinpath(&full_path, full_path.ptr, "root/b/c_wd")); + + opts.workdir_path = full_path.ptr; + opts.flags = + GIT_REPOSITORY_INIT_MKPATH | + GIT_REPOSITORY_INIT_RELATIVE_GITLINK | + GIT_REPOSITORY_INIT_NO_DOTGIT_DIR; + + /* make the directory first, then it should succeed */ + cl_git_pass(git_repository_init_ext(&_repo, "root/b/my_repository", &opts)); + + cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "root/b/c_wd/")); + cl_assert(!git__suffixcmp(git_repository_path(_repo), "root/b/my_repository/")); + cl_assert(!git_repository_is_bare(_repo)); + cl_assert(git_repository_is_empty(_repo)); + + /* Verify that the gitlink and worktree entries are relative */ + + /* Verify worktree */ + cl_git_pass(git_repository_config(&cfg, _repo)); + cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree")); + cl_assert_equal_s("../c_wd/", worktree_path); + + /* Verify gitlink */ + cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git")); + cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr); + + git_buf_free(&dot_git_content); + git_config_free(cfg); + cleanup_repository("root"); +} + #define CLEAR_FOR_CORE_FILEMODE(M) ((M) &= ~0177) static void assert_hooks_match( diff --git a/tests/revwalk/mergebase.c b/tests/revwalk/mergebase.c index 8f1f6ef21..677e1a1b6 100644 --- a/tests/revwalk/mergebase.c +++ b/tests/revwalk/mergebase.c @@ -135,6 +135,24 @@ void test_revwalk_mergebase__prefer_youngest_merge_base(void) cl_assert_equal_oid(&expected, &result); } +void test_revwalk_mergebase__multiple_merge_bases(void) +{ + git_oid one, two, expected1, expected2; + git_oidarray result = {NULL, 0}; + + cl_git_pass(git_oid_fromstr(&one, "a4a7dce85cf63874e984719f4fdd239f5145052f ")); + cl_git_pass(git_oid_fromstr(&two, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644")); + cl_git_pass(git_oid_fromstr(&expected1, "c47800c7266a2be04c571c04d5a6614691ea99bd")); + cl_git_pass(git_oid_fromstr(&expected2, "9fd738e8f7967c078dceed8190330fc8648ee56a")); + + cl_git_pass(git_merge_bases(&result, _repo, &one, &two)); + cl_assert_equal_i(2, result.count); + cl_assert_equal_oid(&expected1, &result.ids[0]); + cl_assert_equal_oid(&expected2, &result.ids[1]); + + git_oidarray_free(&result); +} + void test_revwalk_mergebase__no_off_by_one_missing(void) { git_oid result, one, two; diff --git a/tests/submodule/add.c b/tests/submodule/add.c index 9fdc7cc57..10717809e 100644 --- a/tests/submodule/add.c +++ b/tests/submodule/add.c @@ -2,6 +2,7 @@ #include "posix.h" #include "path.h" #include "submodule_helpers.h" +#include "fileops.h" static git_repository *g_repo = NULL; @@ -29,6 +30,10 @@ static void assert_submodule_url(const char* name, const char *url) void test_submodule_add__url_absolute(void) { git_submodule *sm; + git_config *cfg; + git_repository *repo; + const char *worktree_path; + git_buf dot_git_content = GIT_BUF_INIT; g_repo = setup_fixture_submod2(); @@ -51,6 +56,21 @@ void test_submodule_add__url_absolute(void) cl_assert(git_path_isfile("submod2/.git/modules/" "sm_libgit2" "/HEAD")); assert_submodule_url("sm_libgit2", "https://github.com/libgit2/libgit2.git"); + cl_git_pass(git_repository_open(&repo, "submod2/" "sm_libgit2")); + + /* Verify worktree path is relative */ + cl_git_pass(git_repository_config(&cfg, repo)); + cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree")); + cl_assert_equal_s("../../../sm_libgit2/", worktree_path); + + /* Verify gitdir path is relative */ + cl_git_pass(git_futils_readbuffer(&dot_git_content, "submod2/" "sm_libgit2" "/.git")); + cl_assert_equal_s("gitdir: ../.git/modules/sm_libgit2/", dot_git_content.ptr); + + git_config_free(cfg); + git_repository_free(repo); + git_buf_free(&dot_git_content); + /* add a submodule not using a gitlink */ cl_git_pass( diff --git a/tests/submodule/repository_init.c b/tests/submodule/repository_init.c new file mode 100644 index 000000000..24b47aff8 --- /dev/null +++ b/tests/submodule/repository_init.c @@ -0,0 +1,40 @@ +#include "clar_libgit2.h" +#include "posix.h" +#include "path.h" +#include "submodule_helpers.h" +#include "fileops.h" + +static git_repository *g_repo = NULL; + +void test_submodule_repository_init__basic(void) +{ + git_submodule *sm; + git_repository *repo; + git_config *cfg; + const char *worktree_path; + git_buf dot_git_content = GIT_BUF_INIT; + + g_repo = setup_fixture_submod2(); + + cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only")); + cl_git_pass(git_submodule_repo_init(&repo, sm, 1)); + + /* Verify worktree */ + cl_git_pass(git_repository_config(&cfg, repo)); + cl_git_pass(git_config_get_string(&worktree_path, cfg, "core.worktree")); + cl_assert_equal_s("../../../sm_gitmodules_only/", worktree_path); + + /* Verify gitlink */ + cl_git_pass(git_futils_readbuffer(&dot_git_content, "submod2/" "sm_gitmodules_only" "/.git")); + cl_assert_equal_s("gitdir: ../.git/modules/sm_gitmodules_only/", dot_git_content.ptr); + + cl_assert(git_path_isfile("submod2/" "sm_gitmodules_only" "/.git")); + + cl_assert(git_path_isdir("submod2/.git/modules")); + cl_assert(git_path_isdir("submod2/.git/modules/" "sm_gitmodules_only")); + cl_assert(git_path_isfile("submod2/.git/modules/" "sm_gitmodules_only" "/HEAD")); + + git_config_free(cfg); + git_repository_free(repo); + git_buf_free(&dot_git_content); +} |