Commit fb585d01 by Edward Thomson

Merge branch '4233'

parents d55431eb 868ce84f
...@@ -22,6 +22,7 @@ Dmitry Kakurin ...@@ -22,6 +22,7 @@ Dmitry Kakurin
Dmitry Kovega Dmitry Kovega
Emeric Fermas Emeric Fermas
Emmanuel Rodriguez Emmanuel Rodriguez
Eric Myhre
Florian Forster Florian Forster
Holger Weiss Holger Weiss
Ingmar Vanhassel Ingmar Vanhassel
......
...@@ -8,6 +8,10 @@ v0.26 + 1 ...@@ -8,6 +8,10 @@ v0.26 + 1
### API additions ### 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 ### API removals
### Breaking API changes ### Breaking API changes
......
...@@ -76,6 +76,24 @@ GIT_EXTERN(int) git_remote_create_anonymous( ...@@ -76,6 +76,24 @@ GIT_EXTERN(int) git_remote_create_anonymous(
const char *url); 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 * Get the information for a particular remote
* *
* The name will be checked for validity. * The name will be checked for validity.
......
...@@ -197,10 +197,10 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n ...@@ -197,10 +197,10 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n
git_buf var = GIT_BUF_INIT; git_buf var = GIT_BUF_INIT;
int error = -1; int error = -1;
/* name is optional */ /* repo, name, and fetch are optional */
assert(out && repo && url); 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; return error;
remote = git__calloc(1, sizeof(git_remote)); remote = git__calloc(1, sizeof(git_remote));
...@@ -212,7 +212,11 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n ...@@ -212,7 +212,11 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n
(error = canonicalize_url(&canonical_url, url)) < 0) (error = canonicalize_url(&canonical_url, url)) < 0)
goto on_error; goto on_error;
if (repo) {
remote->url = apply_insteadof(config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH); 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); GITERR_CHECK_ALLOC(remote->url);
if (name != NULL) { if (name != NULL) {
...@@ -222,8 +226,9 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n ...@@ -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) if ((error = git_buf_printf(&var, CONFIG_URL_FMT, name)) < 0)
goto on_error; goto on_error;
if ((error = git_repository_config__weakptr(&config_rw, repo)) < 0 || if (repo &&
(error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0) ((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; goto on_error;
} }
...@@ -235,7 +240,7 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n ...@@ -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) if (name && (error = write_add_refspec(repo, name, fetch, true)) < 0)
goto on_error; 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; goto on_error;
/* Move the data over to where the matching functions can find them */ /* 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 ...@@ -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); 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) int git_remote_dup(git_remote **dest, git_remote *source)
{ {
size_t i; size_t i;
...@@ -674,7 +684,9 @@ int git_remote_connect(git_remote *remote, git_direction direction, const git_re ...@@ -674,7 +684,9 @@ int git_remote_connect(git_remote *remote, git_direction direction, const git_re
url = git_remote__urlfordirection(remote, direction); url = git_remote__urlfordirection(remote, direction);
if (url == NULL) { if (url == NULL) {
giterr_set(GITERR_INVALID, 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; return -1;
} }
...@@ -859,6 +871,11 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const ...@@ -859,6 +871,11 @@ int git_remote_download(git_remote *remote, const git_strarray *refspecs, const
assert(remote); assert(remote);
if (!remote->repo) {
giterr_set(GITERR_INVALID, "cannot download detached remote");
return -1;
}
if (opts) { if (opts) {
GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
cbs = &opts->callbacks; cbs = &opts->callbacks;
...@@ -2344,6 +2361,11 @@ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const gi ...@@ -2344,6 +2361,11 @@ int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const gi
assert(remote); assert(remote);
if (!remote->repo) {
giterr_set(GITERR_INVALID, "cannot download detached remote");
return -1;
}
if (opts) { if (opts) {
cbs = &opts->callbacks; cbs = &opts->callbacks;
custom_headers = &opts->custom_headers; custom_headers = &opts->custom_headers;
...@@ -2403,6 +2425,13 @@ int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_ ...@@ -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_strarray *custom_headers = NULL;
const git_proxy_options *proxy = NULL; const git_proxy_options *proxy = NULL;
assert(remote);
if (!remote->repo) {
giterr_set(GITERR_INVALID, "cannot download detached remote");
return -1;
}
if (opts) { if (opts) {
GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks"); GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
cbs = &opts->callbacks; cbs = &opts->callbacks;
......
#include "clar_libgit2.h" #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) static int remote_single_branch(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload)
{ {
GIT_UNUSED(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; return 0;
} }
...@@ -22,7 +23,7 @@ void test_online_remotes__single_branch(void) ...@@ -22,7 +23,7 @@ void test_online_remotes__single_branch(void)
opts.remote_cb = remote_single_branch; opts.remote_cb = remote_single_branch;
opts.checkout_branch = "first-merge"; 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)); cl_git_pass(git_reference_list(&refs, repo));
for (i = 0; i < refs.count; i++) { for (i = 0; i < refs.count; i++) {
...@@ -37,7 +38,7 @@ void test_online_remotes__single_branch(void) ...@@ -37,7 +38,7 @@ void test_online_remotes__single_branch(void)
cl_git_pass(git_remote_get_fetch_refspecs(&refs, remote)); cl_git_pass(git_remote_get_fetch_refspecs(&refs, remote));
cl_assert_equal_i(1, refs.count); 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_strarray_free(&refs);
git_remote_free(remote); git_remote_free(remote);
...@@ -51,5 +52,76 @@ void test_online_remotes__restricted_refspecs(void) ...@@ -51,5 +52,76 @@ void test_online_remotes__restricted_refspecs(void)
opts.remote_cb = remote_single_branch; 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);
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment