summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Steinhardt <ps@pks.im>2018-11-13 13:40:17 +0100
committerGitHub <noreply@github.com>2018-11-13 13:40:17 +0100
commit20cb30b6b8e269d2ce3474523562b2739a8efea2 (patch)
treee297534b5f126e583bd8420331001016d14bdabb
parent28239be33d4f55228c9088d070c7cd7b9b6628ad (diff)
parent666c7bd84547b8793967820339cf265664fd9a95 (diff)
downloadlibgit2-20cb30b6b8e269d2ce3474523562b2739a8efea2.tar.gz
Merge pull request #4667 from tiennou/feature/remote-create-api
Remote creation API
-rw-r--r--include/git2/remote.h74
-rw-r--r--src/config.c4
-rw-r--r--src/remote.c173
-rw-r--r--tests/network/remote/createthenload.c37
-rw-r--r--tests/network/remote/remotes.c59
-rw-r--r--tests/remote/create.c388
6 files changed, 575 insertions, 160 deletions
diff --git a/include/git2/remote.h b/include/git2/remote.h
index f521fab78..3e8292fe4 100644
--- a/include/git2/remote.h
+++ b/include/git2/remote.h
@@ -42,6 +42,80 @@ GIT_EXTERN(int) git_remote_create(
const char *url);
/**
+ * Remote creation options flags
+ */
+typedef enum {
+ /** Ignore the repository apply.insteadOf configuration */
+ GIT_REMOTE_CREATE_SKIP_INSTEADOF = (1 << 0),
+
+ /** Don't build a fetchspec from the name if none is set */
+ GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC = (1 << 1),
+} git_remote_create_flags;
+
+/**
+ * Remote creation options structure
+ *
+ * Initialize with `GIT_REMOTE_CREATE_OPTIONS_INIT`. Alternatively, you can
+ * use `git_remote_create_init_options`.
+ *
+ */
+typedef struct git_remote_create_options {
+ unsigned int version;
+
+ /**
+ * The repository that should own the remote.
+ * Setting this to NULL results in a detached remote.
+ */
+ git_repository *repository;
+
+ /**
+ * The remote's name.
+ * Setting this to NULL results in an in-memory/anonymous remote.
+ */
+ const char *name;
+
+ /** The fetchspec the remote should use. */
+ const char *fetchspec;
+
+ /** Additional flags for the remote. See git_remote_create_flags. */
+ unsigned int flags;
+} git_remote_create_options;
+
+#define GIT_REMOTE_CREATE_OPTIONS_VERSION 1
+#define GIT_REMOTE_CREATE_OPTIONS_INIT {GIT_REMOTE_CREATE_OPTIONS_VERSION}
+
+/**
+ * Initialize git_remote_create_options structure
+ *
+ * Initializes a `git_remote_create_options` with default values. Equivalent to
+ * creating an instance with `GIT_REMOTE_CREATE_OPTIONS_INIT`.
+ *
+ * @param opts The `git_remote_create_options` struct to initialize.
+ * @param version The struct version; pass `GIT_REMOTE_CREATE_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_remote_create_init_options(
+ git_remote_create_options *opts,
+ unsigned int version);
+
+/**
+ * Create a remote, with options.
+ *
+ * This function allows more fine-grained control over the remote creation.
+ *
+ * Passing NULL as the opts argument will result in a detached remote.
+ *
+ * @param out the resulting remote
+ * @param url the remote's url
+ * @param opts the remote creation options
+ * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
+ */
+GIT_EXTERN(int) git_remote_create_with_opts(
+ git_remote **out,
+ const char *url,
+ const git_remote_create_options *opts);
+
+/**
* Add a remote with the provided fetch refspec (or default if NULL) to the repository's
* configuration.
*
diff --git a/src/config.c b/src/config.c
index 0837500f5..042805e95 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1182,6 +1182,8 @@ int git_config_lock(git_transaction **out, git_config *cfg)
git_config_backend *backend;
backend_internal *internal;
+ assert(cfg);
+
internal = git_vector_get(&cfg->backends, 0);
if (!internal || !internal->backend) {
giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends");
@@ -1200,6 +1202,8 @@ int git_config_unlock(git_config *cfg, int commit)
git_config_backend *backend;
backend_internal *internal;
+ assert(cfg);
+
internal = git_vector_get(&cfg->backends, 0);
if (!internal || !internal->backend) {
giterr_set(GITERR_CONFIG, "cannot lock; the config has no backends");
diff --git a/src/remote.c b/src/remote.c
index 7a07c0138..393068b31 100644
--- a/src/remote.c
+++ b/src/remote.c
@@ -189,58 +189,119 @@ static int canonicalize_url(git_buf *out, const char *in)
return git_buf_puts(out, in);
}
-static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
+static int default_fetchspec_for_name(git_buf *buf, const char *name)
{
+ if (git_buf_printf(buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int ensure_remote_doesnot_exist(git_repository *repo, const char *name)
+{
+ int error;
git_remote *remote;
+
+ error = git_remote_lookup(&remote, repo, name);
+
+ if (error == GIT_ENOTFOUND)
+ return 0;
+
+ if (error < 0)
+ return error;
+
+ git_remote_free(remote);
+
+ giterr_set(GITERR_CONFIG, "remote '%s' already exists", name);
+
+ return GIT_EEXISTS;
+}
+
+int git_remote_create_init_options(git_remote_create_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_remote_create_options, GIT_REMOTE_CREATE_OPTIONS_INIT);
+ return 0;
+}
+
+int git_remote_create_with_opts(git_remote **out, const char *url, const git_remote_create_options *opts)
+{
+ git_remote *remote = NULL;
git_config *config_ro = NULL, *config_rw;
git_buf canonical_url = GIT_BUF_INIT;
git_buf var = GIT_BUF_INIT;
+ git_buf specbuf = GIT_BUF_INIT;
+ const git_remote_create_options dummy_opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
int error = -1;
- /* repo, name, and fetch are optional */
assert(out && url);
- if (repo && (error = git_repository_config_snapshot(&config_ro, repo)) < 0)
- return error;
+ if (!opts) {
+ opts = &dummy_opts;
+ }
+
+ GITERR_CHECK_VERSION(opts, GIT_REMOTE_CREATE_OPTIONS_VERSION, "git_remote_create_options");
+
+ if (opts->name != NULL) {
+ if ((error = ensure_remote_name_is_valid(opts->name)) < 0)
+ return error;
+
+ if (opts->repository &&
+ (error = ensure_remote_doesnot_exist(opts->repository, opts->name)) < 0)
+ return error;
+ }
+
+ if (opts->repository) {
+ if ((error = git_repository_config_snapshot(&config_ro, opts->repository)) < 0)
+ goto on_error;
+ }
remote = git__calloc(1, sizeof(git_remote));
GITERR_CHECK_ALLOC(remote);
- remote->repo = repo;
+ remote->repo = opts->repository;
- if ((error = git_vector_init(&remote->refs, 32, NULL)) < 0 ||
+ if ((error = git_vector_init(&remote->refs, 8, NULL)) < 0 ||
(error = canonicalize_url(&canonical_url, url)) < 0)
goto on_error;
- if (repo) {
+ if (opts->repository && !(opts->flags & GIT_REMOTE_CREATE_SKIP_INSTEADOF)) {
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) {
- remote->name = git__strdup(name);
+ if (opts->name != NULL) {
+ remote->name = git__strdup(opts->name);
GITERR_CHECK_ALLOC(remote->name);
- if ((error = git_buf_printf(&var, CONFIG_URL_FMT, name)) < 0)
- goto on_error;
-
- if (repo &&
- ((error = git_repository_config__weakptr(&config_rw, repo)) < 0 ||
- (error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0))
+ if (opts->repository &&
+ ((error = git_buf_printf(&var, CONFIG_URL_FMT, opts->name)) < 0 ||
+ (error = git_repository_config__weakptr(&config_rw, opts->repository)) < 0 ||
+ (error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0))
goto on_error;
}
- if (fetch != NULL) {
- if ((error = add_refspec(remote, fetch, true)) < 0)
- goto on_error;
+ if (opts->fetchspec != NULL ||
+ (opts->name && !(opts->flags & GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC))) {
+ const char *fetch = NULL;
+ if (opts->fetchspec) {
+ fetch = opts->fetchspec;
+ } else {
+ if ((error = default_fetchspec_for_name(&specbuf, opts->name)) < 0)
+ goto on_error;
+
+ fetch = git_buf_cstr(&specbuf);
+ }
- /* only write for non-anonymous remotes */
- if (repo && name && (error = write_add_refspec(repo, name, fetch, true)) < 0)
+ if ((error = add_refspec(remote, fetch, true)) < 0)
goto on_error;
- if (repo && (error = lookup_remote_prune_config(remote, config_ro, name)) < 0)
+ /* only write for named remotes with a repository */
+ if (opts->repository && opts->name &&
+ ((error = write_add_refspec(opts->repository, opts->name, fetch, true)) < 0 ||
+ (error = lookup_remote_prune_config(remote, config_ro, opts->name)) < 0))
goto on_error;
/* Move the data over to where the matching functions can find them */
@@ -249,7 +310,7 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n
}
/* A remote without a name doesn't download tags */
- if (!name)
+ if (!opts->name)
remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
else
remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
@@ -265,43 +326,32 @@ on_error:
git_remote_free(remote);
git_config_free(config_ro);
+ git_buf_dispose(&specbuf);
git_buf_dispose(&canonical_url);
git_buf_dispose(&var);
return error;
}
-static int ensure_remote_doesnot_exist(git_repository *repo, const char *name)
+int git_remote_create(git_remote **out, git_repository *repo, const char *name, const char *url)
{
+ git_buf buf = GIT_BUF_INIT;
int error;
- git_remote *remote;
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
- error = git_remote_lookup(&remote, repo, name);
-
- if (error == GIT_ENOTFOUND)
- return 0;
-
- if (error < 0)
+ /* Those 2 tests are duplicated here because of backward-compatibility */
+ if ((error = ensure_remote_name_is_valid(name)) < 0)
return error;
- git_remote_free(remote);
-
- giterr_set(
- GITERR_CONFIG,
- "remote '%s' already exists", name);
-
- return GIT_EEXISTS;
-}
+ if (canonicalize_url(&buf, url) < 0)
+ return GIT_ERROR;
+ git_buf_clear(&buf);
-int git_remote_create(git_remote **out, git_repository *repo, const char *name, const char *url)
-{
- git_buf buf = GIT_BUF_INIT;
- int error;
+ opts.repository = repo;
+ opts.name = name;
- if (git_buf_printf(&buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0)
- return -1;
+ error = git_remote_create_with_opts(out, url, &opts);
- error = git_remote_create_with_fetchspec(out, repo, name, url, git_buf_cstr(&buf));
git_buf_dispose(&buf);
return error;
@@ -309,35 +359,32 @@ int git_remote_create(git_remote **out, git_repository *repo, const char *name,
int git_remote_create_with_fetchspec(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
{
- git_remote *remote = NULL;
int error;
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
if ((error = ensure_remote_name_is_valid(name)) < 0)
return error;
- if ((error = ensure_remote_doesnot_exist(repo, name)) < 0)
- return error;
+ opts.repository = repo;
+ opts.name = name;
+ opts.fetchspec = fetch;
+ opts.flags = GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC;
- if (create_internal(&remote, repo, name, url, fetch) < 0)
- goto on_error;
-
- *out = remote;
-
- return 0;
-
-on_error:
- git_remote_free(remote);
- return -1;
+ return git_remote_create_with_opts(out, url, &opts);
}
int git_remote_create_anonymous(git_remote **out, git_repository *repo, const char *url)
{
- return create_internal(out, repo, NULL, url, NULL);
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ opts.repository = repo;
+
+ return git_remote_create_with_opts(out, url, &opts);
}
int git_remote_create_detached(git_remote **out, const char *url)
{
- return create_internal(out, NULL, NULL, url, NULL);
+ return git_remote_create_with_opts(out, url, NULL);
}
int git_remote_dup(git_remote **dest, git_remote *source)
@@ -1946,8 +1993,7 @@ static int rename_fetch_refspecs(git_vector *problems, git_remote *remote, const
if ((error = git_vector_init(problems, 1, NULL)) < 0)
return error;
- if ((error = git_buf_printf(
- &base, "+refs/heads/*:refs/remotes/%s/*", remote->name)) < 0)
+ if ((error = default_fetchspec_for_name(&base, remote->name)) < 0)
return error;
git_vector_foreach(&remote->refspecs, i, spec) {
@@ -1972,8 +2018,7 @@ static int rename_fetch_refspecs(git_vector *problems, git_remote *remote, const
git_buf_clear(&val);
git_buf_clear(&var);
- if (git_buf_printf(
- &val, "+refs/heads/*:refs/remotes/%s/*", new_name) < 0 ||
+ if (default_fetchspec_for_name(&val, new_name) < 0 ||
git_buf_printf(&var, "remote.%s.fetch", new_name) < 0)
{
error = -1;
diff --git a/tests/network/remote/createthenload.c b/tests/network/remote/createthenload.c
deleted file mode 100644
index f811f3c4c..000000000
--- a/tests/network/remote/createthenload.c
+++ /dev/null
@@ -1,37 +0,0 @@
-#include "clar_libgit2.h"
-
-static git_remote *_remote;
-static git_repository *_repo;
-static git_config *_config;
-static char url[] = "http://github.com/libgit2/libgit2.git";
-
-void test_network_remote_createthenload__initialize(void)
-{
- cl_fixture_sandbox("testrepo.git");
-
- cl_git_pass(git_repository_open(&_repo, "testrepo.git"));
-
- cl_git_pass(git_repository_config(&_config, _repo));
- cl_git_pass(git_config_set_string(_config, "remote.origin.fetch", "+refs/heads/*:refs/remotes/origin/*"));
- cl_git_pass(git_config_set_string(_config, "remote.origin.url", url));
- git_config_free(_config);
-
- cl_git_pass(git_remote_lookup(&_remote, _repo, "origin"));
-}
-
-void test_network_remote_createthenload__cleanup(void)
-{
- git_remote_free(_remote);
- _remote = NULL;
-
- git_repository_free(_repo);
- _repo = NULL;
-
- cl_fixture_cleanup("testrepo.git");
-}
-
-void test_network_remote_createthenload__parsing(void)
-{
- cl_assert_equal_s(git_remote_name(_remote), "origin");
- cl_assert_equal_s(git_remote_url(_remote), url);
-}
diff --git a/tests/network/remote/remotes.c b/tests/network/remote/remotes.c
index 8bbc5c7c9..ff11a3ea6 100644
--- a/tests/network/remote/remotes.c
+++ b/tests/network/remote/remotes.c
@@ -312,30 +312,6 @@ void test_network_remote_remotes__add(void)
cl_assert_equal_s(git_remote_url(_remote), "http://github.com/libgit2/libgit2");
}
-void test_network_remote_remotes__cannot_add_a_nameless_remote(void)
-{
- git_remote *remote;
-
- cl_assert_equal_i(
- GIT_EINVALIDSPEC,
- git_remote_create(&remote, _repo, NULL, "git://github.com/libgit2/libgit2"));
-}
-
-void test_network_remote_remotes__cannot_add_a_remote_with_an_invalid_name(void)
-{
- git_remote *remote = NULL;
-
- cl_assert_equal_i(
- GIT_EINVALIDSPEC,
- git_remote_create(&remote, _repo, "Inv@{id", "git://github.com/libgit2/libgit2"));
- cl_assert_equal_p(remote, NULL);
-
- cl_assert_equal_i(
- GIT_EINVALIDSPEC,
- git_remote_create(&remote, _repo, "", "git://github.com/libgit2/libgit2"));
- cl_assert_equal_p(remote, NULL);
-}
-
void test_network_remote_remotes__tagopt(void)
{
const char *name = git_remote_name(_remote);
@@ -389,41 +365,6 @@ void test_network_remote_remotes__returns_ENOTFOUND_when_neither_url_nor_pushurl
git_remote_lookup(&remote, _repo, "no-remote-url"), GIT_ENOTFOUND);
}
-void assert_cannot_create_remote(const char *name, int expected_error)
-{
- git_remote *remote = NULL;
-
- cl_git_fail_with(
- git_remote_create(&remote, _repo, name, "git://github.com/libgit2/libgit2"),
- expected_error);
-
- cl_assert_equal_p(remote, NULL);
-}
-
-void test_network_remote_remotes__cannot_create_a_remote_which_name_conflicts_with_an_existing_remote(void)
-{
- assert_cannot_create_remote("test", GIT_EEXISTS);
-}
-
-void test_network_remote_remotes__cannot_create_a_remote_which_name_is_invalid(void)
-{
- assert_cannot_create_remote("/", GIT_EINVALIDSPEC);
- assert_cannot_create_remote("//", GIT_EINVALIDSPEC);
- assert_cannot_create_remote(".lock", GIT_EINVALIDSPEC);
- assert_cannot_create_remote("a.lock", GIT_EINVALIDSPEC);
-}
-
-void test_network_remote_remote__git_remote_create_with_fetchspec(void)
-{
- git_remote *remote;
- git_strarray array;
-
- cl_git_pass(git_remote_create_with_fetchspec(&remote, _repo, "test-new", "git://github.com/libgit2/libgit2", "+refs/*:refs/*"));
- git_remote_get_fetch_refspecs(&array, remote);
- cl_assert_equal_s("+refs/*:refs/*", array.strings[0]);
- git_remote_free(remote);
-}
-
static const char *fetch_refspecs[] = {
"+refs/heads/*:refs/remotes/origin/*",
"refs/tags/*:refs/tags/*",
diff --git a/tests/remote/create.c b/tests/remote/create.c
new file mode 100644
index 000000000..510962314
--- /dev/null
+++ b/tests/remote/create.c
@@ -0,0 +1,388 @@
+#include "clar_libgit2.h"
+#include "config/config_helpers.h"
+
+static git_repository *_repo;
+static git_config *_config;
+
+#define TEST_URL "http://github.com/libgit2/libgit2.git"
+
+void test_remote_create__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+
+ cl_git_pass(git_repository_open(&_repo, "testrepo.git"));
+
+ cl_git_pass(git_repository_config(&_config, _repo));
+}
+
+void test_remote_create__cleanup(void)
+{
+ git_config_free(_config);
+
+ git_repository_free(_repo);
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+void test_remote_create__manual(void)
+{
+ git_remote *remote;
+ cl_git_pass(git_config_set_string(_config, "remote.origin.fetch", "+refs/heads/*:refs/remotes/origin/*"));
+ cl_git_pass(git_config_set_string(_config, "remote.origin.url", TEST_URL));
+
+ cl_git_pass(git_remote_lookup(&remote, _repo, "origin"));
+ cl_assert_equal_s(git_remote_name(remote), "origin");
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+
+ git_remote_free(remote);
+}
+
+void test_remote_create__named(void)
+{
+ git_remote *remote;
+ git_config *cfg;
+ const char *cfg_val;
+
+ size_t section_count = count_config_entries_match(_repo, "remote\\.");
+
+ cl_git_pass(git_remote_create(&remote, _repo, "valid-name", TEST_URL));
+
+ cl_assert_equal_s(git_remote_name(remote), "valid-name");
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), _repo);
+
+ cl_git_pass(git_repository_config_snapshot(&cfg, _repo));
+
+ cl_git_pass(git_config_get_string(&cfg_val, cfg, "remote.valid-name.fetch"));
+ cl_assert_equal_s(cfg_val, "+refs/heads/*:refs/remotes/valid-name/*");
+
+ cl_git_pass(git_config_get_string(&cfg_val, cfg, "remote.valid-name.url"));
+ cl_assert_equal_s(cfg_val, TEST_URL);
+
+ cl_assert_equal_i(section_count + 2, count_config_entries_match(_repo, "remote\\."));
+
+ git_config_free(cfg);
+ git_remote_free(remote);
+}
+
+void test_remote_create__named_fail_on_invalid_name(void)
+{
+ const char *names[] = {
+ NULL,
+ "Inv@{id",
+ "",
+ "/",
+ "//",
+ ".lock",
+ "a.lock",
+ };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(names); i++) {
+ git_remote *remote = NULL;
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_remote_create(&remote, _repo, names[i], TEST_URL));
+ cl_assert_equal_p(remote, NULL);
+ }
+}
+
+void test_remote_create__named_fail_on_invalid_url(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(GIT_ERROR, git_remote_create(&remote, _repo, "bad-url", ""));
+ cl_assert_equal_p(remote, NULL);
+}
+
+void test_remote_create__named_fail_on_conflicting_name(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(GIT_EEXISTS, git_remote_create(&remote, _repo, "test", TEST_URL));
+ cl_assert_equal_p(remote, NULL);
+}
+
+void test_remote_create__with_fetchspec(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ size_t section_count = count_config_entries_match(_repo, "remote\\.");
+
+ cl_git_pass(git_remote_create_with_fetchspec(&remote, _repo, "test-new", "git://github.com/libgit2/libgit2", "+refs/*:refs/*"));
+ cl_assert_equal_s(git_remote_name(remote), "test-new");
+ cl_assert_equal_s(git_remote_url(remote), "git://github.com/libgit2/libgit2");
+ cl_assert_equal_p(git_remote_owner(remote), _repo);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_s("+refs/*:refs/*", array.strings[0]);
+ cl_assert_equal_i(1, array.count);
+ cl_assert_equal_i(section_count + 2, count_config_entries_match(_repo, "remote\\."));
+
+ git_strarray_free(&array);
+ git_remote_free(remote);
+}
+
+void test_remote_create__with_empty_fetchspec(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ size_t section_count = count_config_entries_match(_repo, "remote\\.");
+
+ cl_git_pass(git_remote_create_with_fetchspec(&remote, _repo, "test-new", "git://github.com/libgit2/libgit2", NULL));
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(0, array.count);
+ cl_assert_equal_i(section_count + 1, count_config_entries_match(_repo, "remote\\."));
+
+ git_strarray_free(&array);
+ git_remote_free(remote);
+}
+
+void test_remote_create__with_fetchspec_invalid_name(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_remote_create_with_fetchspec(&remote, _repo, NULL, TEST_URL, NULL));
+ cl_assert_equal_p(remote, NULL);
+}
+
+void test_remote_create__with_fetchspec_invalid_url(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_remote_create_with_fetchspec(&remote, _repo, NULL, "", NULL));
+ cl_assert_equal_p(remote, NULL);
+}
+
+void test_remote_create__anonymous(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ size_t section_count = count_config_entries_match(_repo, "remote\\.");
+
+ cl_git_pass(git_remote_create_anonymous(&remote, _repo, TEST_URL));
+ cl_assert_equal_s(git_remote_name(remote), NULL);
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), _repo);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(0, array.count);
+ cl_assert_equal_i(section_count, count_config_entries_match(_repo, "remote\\."));
+
+ git_strarray_free(&array);
+ git_remote_free(remote);
+}
+
+void test_remote_create__anonymous_invalid_url(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_remote_create_anonymous(&remote, _repo, ""));
+ cl_assert_equal_p(remote, NULL);
+}
+
+void test_remote_create__detached(void)
+{
+ git_remote *remote;
+ git_strarray array;
+
+ size_t section_count = count_config_entries_match(_repo, "remote\\.");
+
+ cl_git_pass(git_remote_create_detached(&remote, TEST_URL));
+ cl_assert_equal_s(git_remote_name(remote), NULL);
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), NULL);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(0, array.count);
+ cl_assert_equal_i(section_count, count_config_entries_match(_repo, "remote\\."));
+
+ git_strarray_free(&array);
+ git_remote_free(remote);
+}
+
+void test_remote_create__detached_invalid_url(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_remote_create_detached(&remote, ""));
+ cl_assert_equal_p(remote, NULL);
+}
+
+void test_remote_create__with_opts_named(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ opts.name = "test-new";
+ opts.repository = _repo;
+
+ cl_git_pass(git_remote_create_with_opts(&remote, TEST_URL, &opts));
+ cl_assert_equal_s(git_remote_name(remote), "test-new");
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), _repo);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(1, array.count);
+ cl_assert_equal_s("+refs/heads/*:refs/remotes/test-new/*", array.strings[0]);
+
+ git_strarray_free(&array);
+ git_remote_free(remote);
+}
+
+void test_remote_create__with_opts_named_and_fetchspec(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ opts.name = "test-new";
+ opts.repository = _repo;
+ opts.fetchspec = "+refs/*:refs/*";
+
+ cl_git_pass(git_remote_create_with_opts(&remote, TEST_URL, &opts));
+ cl_assert_equal_s(git_remote_name(remote), "test-new");
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), _repo);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(1, array.count);
+ cl_assert_equal_s("+refs/*:refs/*", array.strings[0]);
+
+ git_strarray_free(&array);
+ git_remote_free(remote);
+}
+
+void test_remote_create__with_opts_named_no_fetchspec(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ opts.name = "test-new";
+ opts.repository = _repo;
+ opts.flags = GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC;
+
+ cl_git_pass(git_remote_create_with_opts(&remote, TEST_URL, &opts));
+ cl_assert_equal_s(git_remote_name(remote), "test-new");
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), _repo);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(0, array.count);
+
+ git_strarray_free(&array);
+ git_remote_free(remote);
+}
+
+void test_remote_create__with_opts_anonymous(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ opts.repository = _repo;
+
+ cl_git_pass(git_remote_create_with_opts(&remote, TEST_URL, &opts));
+ cl_assert_equal_s(git_remote_name(remote), NULL);
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), _repo);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(0, array.count);
+
+ git_strarray_free(&array);
+ git_remote_free(remote);
+}
+
+void test_remote_create__with_opts_detached(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ cl_git_pass(git_remote_create_with_opts(&remote, TEST_URL, &opts));
+ cl_assert_equal_s(git_remote_name(remote), NULL);
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), NULL);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(0, array.count);
+
+ git_strarray_free(&array);
+
+ git_remote_free(remote);
+
+ cl_git_pass(git_remote_create_with_opts(&remote, TEST_URL, NULL));
+ cl_assert_equal_s(git_remote_name(remote), NULL);
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), NULL);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(0, array.count);
+
+ git_strarray_free(&array);
+
+ git_remote_free(remote);
+}
+
+
+void test_remote_create__with_opts_insteadof_disabled(void)
+{
+ git_remote *remote;
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ opts.repository = _repo;
+ opts.flags = GIT_REMOTE_CREATE_SKIP_INSTEADOF;
+
+ cl_git_pass(git_remote_create_with_opts(&remote, "http://example.com/libgit2/libgit2", &opts));
+
+ cl_assert_equal_s(git_remote_url(remote), "http://example.com/libgit2/libgit2");
+ cl_assert_equal_p(git_remote_pushurl(remote), NULL);
+
+ git_remote_free(remote);
+}
+
+static int create_with_name(git_remote **remote, git_repository *repo, const char *name, const char *url)
+{
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ opts.repository = repo;
+ opts.name = name;
+
+ return git_remote_create_with_opts(remote, url, &opts);
+}
+
+void test_remote_create__with_opts_invalid_name(void)
+{
+ const char *names[] = {
+ "Inv@{id",
+ "",
+ "/",
+ "//",
+ ".lock",
+ "a.lock",
+ };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(names); i++) {
+ git_remote *remote = NULL;
+ cl_git_fail_with(GIT_EINVALIDSPEC, create_with_name(&remote, _repo, names[i], TEST_URL));
+ cl_assert_equal_p(remote, NULL);
+ }
+}
+
+void test_remote_create__with_opts_conflicting_name(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(GIT_EEXISTS, create_with_name(&remote, _repo, "test", TEST_URL));
+ cl_assert_equal_p(remote, NULL);
+}
+
+void test_remote_create__with_opts_invalid_url(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(GIT_EINVALIDSPEC, create_with_name(&remote, _repo, "test-new", ""));
+ cl_assert_equal_p(remote, NULL);
+}