diff options
author | Patrick Steinhardt <ps@pks.im> | 2018-11-13 13:40:17 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-13 13:40:17 +0100 |
commit | 20cb30b6b8e269d2ce3474523562b2739a8efea2 (patch) | |
tree | e297534b5f126e583bd8420331001016d14bdabb | |
parent | 28239be33d4f55228c9088d070c7cd7b9b6628ad (diff) | |
parent | 666c7bd84547b8793967820339cf265664fd9a95 (diff) | |
download | libgit2-20cb30b6b8e269d2ce3474523562b2739a8efea2.tar.gz |
Merge pull request #4667 from tiennou/feature/remote-create-api
Remote creation API
-rw-r--r-- | include/git2/remote.h | 74 | ||||
-rw-r--r-- | src/config.c | 4 | ||||
-rw-r--r-- | src/remote.c | 173 | ||||
-rw-r--r-- | tests/network/remote/createthenload.c | 37 | ||||
-rw-r--r-- | tests/network/remote/remotes.c | 59 | ||||
-rw-r--r-- | tests/remote/create.c | 388 |
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); +} |