Skip to content

Commit 3cbaebd

Browse files
committed
remote: provide a generic API for creating remotes
This supersedes the functionality of remote_create_with_fetchspec, remote_create_anonymous and remote_create_detached.
1 parent 43b4b2f commit 3cbaebd

File tree

3 files changed

+286
-39
lines changed

3 files changed

+286
-39
lines changed

include/git2/remote.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,66 @@ GIT_EXTERN(int) git_remote_create(
4141
const char *name,
4242
const char *url);
4343

44+
/**
45+
* Remote creation options structure
46+
*
47+
* Initialize with `GIT_REMOTE_CREATE_OPTIONS_INIT`. Alternatively, you can
48+
* use `git_remote_create_init_options`.
49+
*
50+
*/
51+
typedef struct git_remote_create_options {
52+
unsigned int version;
53+
54+
/**
55+
* The repository that should own the remote.
56+
* Setting this to NULL results in a detached remote.
57+
*/
58+
git_repository *repository;
59+
60+
/**
61+
* The remote's name.
62+
* Setting this to NULL results in an in-memory/anonymous remote.
63+
*/
64+
const char *name;
65+
66+
/** The fetchspec the remote should use. */
67+
const char *fetchspec;
68+
} git_remote_create_options;
69+
70+
#define GIT_REMOTE_CREATE_OPTIONS_VERSION 1
71+
#define GIT_REMOTE_CREATE_OPTIONS_INIT {GIT_REMOTE_CREATE_OPTIONS_VERSION}
72+
73+
/**
74+
* Initialize git_remote_create_options structure
75+
*
76+
* Initializes a `git_remote_create_options` with default values. Equivalent to
77+
* creating an instance with `GIT_REMOTE_CREATE_OPTIONS_INIT`.
78+
*
79+
* @param opts The `git_remote_create_options` struct to initialize.
80+
* @param version The struct version; pass `GIT_REMOTE_CREATE_OPTIONS_VERSION`.
81+
* @return Zero on success; -1 on failure.
82+
*/
83+
GIT_EXTERN(int) git_remote_create_init_options(
84+
git_remote_create_options *opts,
85+
unsigned int version);
86+
87+
/**
88+
* Create a remote, with options.
89+
*
90+
* This function allows more fine-grained control over the remote creation.
91+
*
92+
* Passing NULL as the opts argument will result in a detached remote.
93+
*
94+
* @param out the resulting remote
95+
* @param url the remote's url
96+
* @param opts the remote creation options
97+
* @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
98+
*/
99+
GIT_EXTERN(int) git_remote_create_with_opts(
100+
git_remote **out,
101+
const char *url,
102+
const git_remote_create_options *opts);
103+
44104
/**
45105
* Add a remote with the provided fetch refspec (or default if NULL) to the repository's
46106
* configuration.

src/remote.c

Lines changed: 74 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -217,57 +217,79 @@ static int ensure_remote_doesnot_exist(git_repository *repo, const char *name)
217217
return GIT_EEXISTS;
218218
}
219219

220-
static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
220+
int git_remote_create_init_options(git_remote_create_options *opts, unsigned int version)
221221
{
222-
git_remote *remote;
222+
GIT_INIT_STRUCTURE_FROM_TEMPLATE(
223+
opts, version, git_remote_create_options, GIT_REMOTE_CREATE_OPTIONS_INIT);
224+
return 0;
225+
}
226+
227+
static int create_internal(git_remote **out, const char *url, const git_remote_create_options *opts)
228+
{
229+
git_remote *remote = NULL;
223230
git_config *config_ro = NULL, *config_rw;
224231
git_buf canonical_url = GIT_BUF_INIT;
225232
git_buf var = GIT_BUF_INIT;
233+
const git_remote_create_options dummy_opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
226234
int error = -1;
227235

228-
/* repo, name, and fetch are optional */
229236
assert(out && url);
230237

231-
if (repo && (error = git_repository_config_snapshot(&config_ro, repo)) < 0)
232-
return error;
238+
if (!opts) {
239+
opts = &dummy_opts;
240+
}
241+
242+
GITERR_CHECK_VERSION(opts, GIT_REMOTE_CREATE_OPTIONS_VERSION, "git_remote_create_options");
243+
244+
if (opts->name != NULL) {
245+
if ((error = ensure_remote_name_is_valid(opts->name)) < 0)
246+
return error;
247+
248+
if (opts->repository &&
249+
(error = ensure_remote_doesnot_exist(opts->repository, opts->name)) < 0)
250+
return error;
251+
}
252+
253+
if (opts->repository) {
254+
if ((error = git_repository_config_snapshot(&config_ro, opts->repository)) < 0)
255+
goto on_error;
256+
}
233257

234258
remote = git__calloc(1, sizeof(git_remote));
235259
GITERR_CHECK_ALLOC(remote);
236260

237-
remote->repo = repo;
261+
remote->repo = opts->repository;
238262

239263
if ((error = git_vector_init(&remote->refs, 32, NULL)) < 0 ||
240264
(error = canonicalize_url(&canonical_url, url)) < 0)
241265
goto on_error;
242266

243-
if (repo) {
267+
if (opts->repository) {
244268
remote->url = apply_insteadof(config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH);
245269
} else {
246270
remote->url = git__strdup(canonical_url.ptr);
247271
}
248272
GITERR_CHECK_ALLOC(remote->url);
249273

250-
if (name != NULL) {
251-
remote->name = git__strdup(name);
274+
if (opts->name != NULL) {
275+
remote->name = git__strdup(opts->name);
252276
GITERR_CHECK_ALLOC(remote->name);
253277

254-
if ((error = git_buf_printf(&var, CONFIG_URL_FMT, name)) < 0)
255-
goto on_error;
256-
257-
if (repo &&
258-
((error = git_repository_config__weakptr(&config_rw, repo)) < 0 ||
259-
(error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0))
278+
if (opts->repository &&
279+
((error = git_buf_printf(&var, CONFIG_URL_FMT, opts->name)) < 0 ||
280+
(error = git_repository_config__weakptr(&config_rw, opts->repository)) < 0 ||
281+
(error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0))
260282
goto on_error;
261283
}
262284

263-
if (fetch != NULL) {
264-
if ((error = add_refspec(remote, fetch, true)) < 0)
285+
if (opts->fetchspec != NULL) {
286+
if ((error = add_refspec(remote, opts->fetchspec, true)) < 0)
265287
goto on_error;
266288

267289
/* only write for named remotes with a repository */
268-
if (repo && name &&
269-
((error = write_add_refspec(repo, name, fetch, true)) < 0 ||
270-
(error = lookup_remote_prune_config(remote, config_ro, name)) < 0))
290+
if (opts->repository && opts->name &&
291+
((error = write_add_refspec(opts->repository, opts->name, opts->fetchspec, true)) < 0 ||
292+
(error = lookup_remote_prune_config(remote, config_ro, opts->name)) < 0))
271293
goto on_error;
272294

273295
/* Move the data over to where the matching functions can find them */
@@ -276,7 +298,7 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n
276298
}
277299

278300
/* A remote without a name doesn't download tags */
279-
if (!name)
301+
if (!opts->name)
280302
remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
281303
else
282304
remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
@@ -301,47 +323,60 @@ int git_remote_create(git_remote **out, git_repository *repo, const char *name,
301323
{
302324
git_buf buf = GIT_BUF_INIT;
303325
int error;
326+
git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
304327

305-
if (default_fetchspec_for_name(&buf, name) < 0)
306-
return -1;
328+
/* Those 2 tests are duplicated here because of backward-compatibility */
329+
if ((error = ensure_remote_name_is_valid(name)) < 0)
330+
return error;
331+
332+
if (canonicalize_url(&buf, url) < 0)
333+
return GIT_ERROR;
334+
335+
git_buf_clear(&buf);
336+
337+
opts.repository = repo;
338+
opts.name = name;
339+
opts.fetchspec = git_buf_cstr(&buf);
340+
341+
error = create_internal(out, url, &opts);
307342

308-
error = git_remote_create_with_fetchspec(out, repo, name, url, git_buf_cstr(&buf));
309343
git_buf_dispose(&buf);
310344

311345
return error;
312346
}
313347

348+
int git_remote_create_with_opts(git_remote **out, const char *url, const git_remote_create_options *opts)
349+
{
350+
return create_internal(out, url, opts);
351+
}
352+
314353
int git_remote_create_with_fetchspec(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
315354
{
316-
git_remote *remote = NULL;
317355
int error;
356+
git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
318357

319358
if ((error = ensure_remote_name_is_valid(name)) < 0)
320359
return error;
321360

322-
if ((error = ensure_remote_doesnot_exist(repo, name)) < 0)
323-
return error;
324-
325-
if (create_internal(&remote, repo, name, url, fetch) < 0)
326-
goto on_error;
327-
328-
*out = remote;
329-
330-
return 0;
361+
opts.repository = repo;
362+
opts.name = name;
363+
opts.fetchspec = fetch;
331364

332-
on_error:
333-
git_remote_free(remote);
334-
return -1;
365+
return create_internal(out, url, &opts);
335366
}
336367

337368
int git_remote_create_anonymous(git_remote **out, git_repository *repo, const char *url)
338369
{
339-
return create_internal(out, repo, NULL, url, NULL);
370+
git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
371+
372+
opts.repository = repo;
373+
374+
return create_internal(out, url, &opts);
340375
}
341376

342377
int git_remote_create_detached(git_remote **out, const char *url)
343378
{
344-
return create_internal(out, NULL, NULL, url, NULL);
379+
return create_internal(out, url, NULL);
345380
}
346381

347382
int git_remote_dup(git_remote **dest, git_remote *source)

0 commit comments

Comments
 (0)