diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2017-07-31 00:58:58 +0100 |
---|---|---|
committer | Edward Thomson <ethomson@edwardthomson.com> | 2017-07-31 00:59:27 +0100 |
commit | fb585d01205e1a0b580a39f9cbdc829ff63e02c9 (patch) | |
tree | 33f6f11be8a18afdd6ce3594e184dce9625638ea | |
parent | d55431ebfaf3958ca2030ac73a3caff9c8507b4c (diff) | |
parent | 868ce84f9ed21f8881b3c66b2363b29b219a99ab (diff) | |
download | libgit2-fb585d01205e1a0b580a39f9cbdc829ff63e02c9.tar.gz |
Merge branch '4233'
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | CHANGELOG.md | 4 | ||||
-rw-r--r-- | include/git2/remote.h | 18 | ||||
-rw-r--r-- | src/remote.c | 45 | ||||
-rw-r--r-- | tests/online/remotes.c | 82 |
5 files changed, 137 insertions, 13 deletions
@@ -22,6 +22,7 @@ Dmitry Kakurin Dmitry Kovega Emeric Fermas Emmanuel Rodriguez +Eric Myhre Florian Forster Holger Weiss Ingmar Vanhassel diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b2eba377..43724b5f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ v0.26 + 1 ### API additions +* `git_remote_create_detached()` creates a remote that is not associated + to any repository (and does not apply configuration like 'insteadof' rules). + This is mostly useful for e.g. emulating `git ls-remote` behavior. + ### API removals ### Breaking API changes diff --git a/include/git2/remote.h b/include/git2/remote.h index e9e4e5b65..b1e04d557 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -76,6 +76,24 @@ GIT_EXTERN(int) git_remote_create_anonymous( const char *url); /** + * Create a remote without a connected local repo + * + * Create a remote with the given url in-memory. You can use this when + * you have a URL instead of a remote's name. + * + * Contrasted with git_remote_create_anonymous, a detached remote + * will not consider any repo configuration values (such as insteadof url + * substitutions). + * + * @param out pointer to the new remote objects + * @param url the remote repository's URL + * @return 0 or an error code + */ +GIT_EXTERN(int) git_remote_create_detached( + git_remote **out, + const char *url); + +/** * Get the information for a particular remote * * The name will be checked for validity. diff --git a/src/remote.c b/src/remote.c index bd8b3cfbc..87fc17dda 100644 --- a/src/remote.c +++ b/src/remote.c @@ -197,10 +197,10 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n git_buf var = GIT_BUF_INIT; int error = -1; - /* name is optional */ - assert(out && repo && url); + /* repo, name, and fetch are optional */ + assert(out && url); - if ((error = git_repository_config_snapshot(&config_ro, repo)) < 0) + if (repo && (error = git_repository_config_snapshot(&config_ro, repo)) < 0) return error; remote = git__calloc(1, sizeof(git_remote)); @@ -212,7 +212,11 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n (error = canonicalize_url(&canonical_url, url)) < 0) goto on_error; - remote->url = apply_insteadof(config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH); + if (repo) { + remote->url = apply_insteadof(config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH); + } else { + remote->url = git__strdup(canonical_url.ptr); + } GITERR_CHECK_ALLOC(remote->url); if (name != NULL) { @@ -222,8 +226,9 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n if ((error = git_buf_printf(&var, CONFIG_URL_FMT, name)) < 0) goto on_error; - if ((error = git_repository_config__weakptr(&config_rw, repo)) < 0 || - (error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0) + if (repo && + ((error = git_repository_config__weakptr(&config_rw, repo)) < 0 || + (error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0)) goto on_error; } @@ -235,7 +240,7 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n if (name && (error = write_add_refspec(repo, name, fetch, true)) < 0) goto on_error; - if ((error = lookup_remote_prune_config(remote, config_ro, name)) < 0) + if (repo && (error = lookup_remote_prune_config(remote, config_ro, name)) < 0) goto on_error; /* Move the data over to where the matching functions can find them */ @@ -330,6 +335,11 @@ int git_remote_create_anonymous(git_remote **out, git_repository *repo, const ch return create_internal(out, repo, NULL, url, NULL); } +int git_remote_create_detached(git_remote **out, const char *url) +{ + return create_internal(out, NULL, NULL, url, NULL); +} + int git_remote_dup(git_remote **dest, git_remote *source) { size_t i; @@ -674,7 +684,9 @@ int git_remote_connect(git_remote *remote, git_direction direction, const git_re url = git_remote__urlfordirection(remote, direction); if (url == NULL) { giterr_set(GITERR_INVALID, - "Malformed remote '%s' - missing URL", remote->name); + "Malformed remote '%s' - missing %s URL", + remote->name ? remote->name : "(anonymous)", + direction == GIT_DIRECTION_FETCH ? "fetch" : "push"); return -1; } @@ -859,6 +871,11 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const assert(remote); + if (!remote->repo) { + giterr_set(GITERR_INVALID, "cannot download detached remote"); + return -1; + } + if (opts) { GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); cbs = &opts->callbacks; @@ -2344,6 +2361,11 @@ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const gi assert(remote); + if (!remote->repo) { + giterr_set(GITERR_INVALID, "cannot download detached remote"); + return -1; + } + if (opts) { cbs = &opts->callbacks; custom_headers = &opts->custom_headers; @@ -2403,6 +2425,13 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_ const git_strarray *custom_headers = NULL; const git_proxy_options *proxy = NULL; + assert(remote); + + if (!remote->repo) { + giterr_set(GITERR_INVALID, "cannot download detached remote"); + return -1; + } + if (opts) { GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); cbs = &opts->callbacks; diff --git a/tests/online/remotes.c b/tests/online/remotes.c index a86f2d9ae..d79eb1f59 100644 --- a/tests/online/remotes.c +++ b/tests/online/remotes.c @@ -1,12 +1,13 @@ #include "clar_libgit2.h" -static const char *refspec = "refs/heads/first-merge:refs/remotes/origin/first-merge"; +#define URL "git://github.com/libgit2/TestGitRepository" +#define REFSPEC "refs/heads/first-merge:refs/remotes/origin/first-merge" static int remote_single_branch(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload) { GIT_UNUSED(payload); - cl_git_pass(git_remote_create_with_fetchspec(out, repo, name, url, refspec)); + cl_git_pass(git_remote_create_with_fetchspec(out, repo, name, url, REFSPEC)); return 0; } @@ -22,7 +23,7 @@ void test_online_remotes__single_branch(void) 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_clone(&repo, URL, "./single-branch", &opts)); cl_git_pass(git_reference_list(&refs, repo)); for (i = 0; i < refs.count; i++) { @@ -37,7 +38,7 @@ void test_online_remotes__single_branch(void) cl_git_pass(git_remote_get_fetch_refspecs(&refs, remote)); cl_assert_equal_i(1, refs.count); - cl_assert_equal_s(refspec, refs.strings[0]); + cl_assert_equal_s(REFSPEC, refs.strings[0]); git_strarray_free(&refs); git_remote_free(remote); @@ -51,5 +52,76 @@ void test_online_remotes__restricted_refspecs(void) opts.remote_cb = remote_single_branch; - cl_git_fail_with(GIT_EINVALIDSPEC, git_clone(&repo, "git://github.com/libgit2/TestGitRepository", "./restrict-refspec", &opts)); + cl_git_fail_with(GIT_EINVALIDSPEC, git_clone(&repo, URL, "./restrict-refspec", &opts)); +} + +void test_online_remotes__detached_remote_fails_downloading(void) +{ + git_remote *remote; + + cl_git_pass(git_remote_create_detached(&remote, URL)); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL)); + cl_git_fail(git_remote_download(remote, NULL, NULL)); + + git_remote_free(remote); +} + +void test_online_remotes__detached_remote_fails_uploading(void) +{ + git_remote *remote; + + cl_git_pass(git_remote_create_detached(&remote, URL)); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL)); + cl_git_fail(git_remote_upload(remote, NULL, NULL)); + + git_remote_free(remote); +} + +void test_online_remotes__detached_remote_fails_pushing(void) +{ + git_remote *remote; + + cl_git_pass(git_remote_create_detached(&remote, URL)); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL)); + cl_git_fail(git_remote_push(remote, NULL, NULL)); + + git_remote_free(remote); +} + +void test_online_remotes__detached_remote_succeeds_ls(void) +{ + const char *refs[] = { + "HEAD", + "refs/heads/first-merge", + "refs/heads/master", + "refs/heads/no-parent", + "refs/tags/annotated_tag", + "refs/tags/annotated_tag^{}", + "refs/tags/blob", + "refs/tags/commit_tree", + "refs/tags/nearly-dangling", + }; + const git_remote_head **heads; + git_remote *remote; + size_t i, j, n; + + cl_git_pass(git_remote_create_detached(&remote, URL)); + cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL)); + cl_git_pass(git_remote_ls(&heads, &n, remote)); + + cl_assert_equal_sz(n, 9); + for (i = 0; i < n; i++) { + char found = false; + + for (j = 0; j < ARRAY_SIZE(refs); j++) { + if (!strcmp(heads[i]->name, refs[j])) { + found = true; + break; + } + } + + cl_assert_(found, heads[i]->name); + } + + git_remote_free(remote); } |