]> git.proxmox.com Git - libgit2.git/blame - src/libgit2/remote.c
Merge https://salsa.debian.org/debian/libgit2 into proxmox/bullseye
[libgit2.git] / src / libgit2 / remote.c
CommitLineData
9c82357b 1/*
359fc2d2 2 * Copyright (C) the libgit2 contributors. All rights reserved.
9c82357b 3 *
bb742ede
VM
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
9c82357b
CMN
6 */
7
eae0bfdc
PP
8#include "remote.h"
9
e579e0f7
MB
10#include "buf.h"
11#include "branch.h"
9c82357b
CMN
12#include "config.h"
13#include "repository.h"
e1d88030 14#include "fetch.h"
441f57c2 15#include "refs.h"
b0f6e45d
ET
16#include "refspec.h"
17#include "fetchhead.h"
fe794b2e 18#include "push.h"
e579e0f7
MB
19#include "proxy.h"
20
21#include "git2/config.h"
22#include "git2/types.h"
23#include "git2/oid.h"
24#include "git2/net.h"
9c82357b 25
22261344
CMN
26#define CONFIG_URL_FMT "remote.%s.url"
27#define CONFIG_PUSHURL_FMT "remote.%s.pushurl"
28#define CONFIG_FETCH_FMT "remote.%s.fetch"
77254990 29#define CONFIG_PUSH_FMT "remote.%s.push"
35a8a8c5 30#define CONFIG_TAGOPT_FMT "remote.%s.tagopt"
22261344 31
af613ecd 32static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs);
1ef3f0ce 33static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name);
e579e0f7 34static int apply_insteadof(char **out, git_config *config, const char *url, int direction, bool use_default_if_empty);
af613ecd 35
3f894205 36static int add_refspec_to(git_vector *vector, const char *string, bool is_fetch)
9c82357b 37{
4330ab26 38 git_refspec *spec;
9c82357b 39
4330ab26 40 spec = git__calloc(1, sizeof(git_refspec));
ac3d33df 41 GIT_ERROR_CHECK_ALLOC(spec);
4330ab26 42
1be680c4
CMN
43 if (git_refspec__parse(spec, string, is_fetch) < 0) {
44 git__free(spec);
45 return -1;
46 }
4330ab26
CMN
47
48 spec->push = !is_fetch;
3f894205 49 if (git_vector_insert(vector, spec) < 0) {
ac3d33df 50 git_refspec__dispose(spec);
1be680c4
CMN
51 git__free(spec);
52 return -1;
53 }
9c82357b 54
4330ab26 55 return 0;
9c82357b
CMN
56}
57
3f894205
CMN
58static int add_refspec(git_remote *remote, const char *string, bool is_fetch)
59{
60 return add_refspec_to(&remote->refspecs, string, is_fetch);
61}
62
24f2f94e
CMN
63static int download_tags_value(git_remote *remote, git_config *cfg)
64{
9a97f49e 65 git_config_entry *ce;
e579e0f7 66 git_str buf = GIT_STR_INIT;
24f2f94e
CMN
67 int error;
68
e579e0f7 69 if (git_str_printf(&buf, "remote.%s.tagopt", remote->name) < 0)
24f2f94e
CMN
70 return -1;
71
e579e0f7
MB
72 error = git_config__lookup_entry(&ce, cfg, git_str_cstr(&buf), false);
73 git_str_dispose(&buf);
24f2f94e 74
9f77b3f6
RB
75 if (!error && ce && ce->value) {
76 if (!strcmp(ce->value, "--no-tags"))
77 remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
78 else if (!strcmp(ce->value, "--tags"))
79 remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
bf6bebe2 80 }
24f2f94e 81
9a97f49e 82 git_config_entry_free(ce);
24f2f94e
CMN
83 return error;
84}
85
032ba9e4 86static int ensure_remote_name_is_valid(const char *name)
87{
c25aa7cd
PP
88 int valid, error;
89
90 error = git_remote_name_is_valid(&valid, name);
032ba9e4 91
c25aa7cd 92 if (!error && !valid) {
ac3d33df
JK
93 git_error_set(
94 GIT_ERROR_CONFIG,
183aa4f8 95 "'%s' is not a valid remote name.", name ? name : "(null)");
032ba9e4 96 error = GIT_EINVALIDSPEC;
97 }
98
99 return error;
100}
101
77254990
CMN
102static int write_add_refspec(git_repository *repo, const char *name, const char *refspec, bool fetch)
103{
104 git_config *cfg;
e579e0f7 105 git_str var = GIT_STR_INIT;
c6e942fb 106 git_refspec spec;
77254990
CMN
107 const char *fmt;
108 int error;
109
110 if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
111 return error;
112
113 fmt = fetch ? CONFIG_FETCH_FMT : CONFIG_PUSH_FMT;
114
115 if ((error = ensure_remote_name_is_valid(name)) < 0)
116 return error;
117
c25aa7cd 118 if ((error = git_refspec__parse(&spec, refspec, fetch)) < 0)
c6e942fb 119 return error;
c6e942fb 120
ac3d33df 121 git_refspec__dispose(&spec);
c6e942fb 122
e579e0f7 123 if ((error = git_str_printf(&var, fmt, name)) < 0)
77254990
CMN
124 return error;
125
126 /*
c25aa7cd 127 * "$^" is an unmatchable regexp: it will not match anything at all, so
77254990
CMN
128 * all values will be considered new and we will not replace any
129 * present value.
130 */
131 if ((error = git_config_set_multivar(cfg, var.ptr, "$^", refspec)) < 0) {
132 goto cleanup;
133 }
134
135cleanup:
e579e0f7 136 git_str_dispose(&var);
77254990
CMN
137 return 0;
138}
139
e579e0f7 140static int canonicalize_url(git_str *out, const char *in)
12f32d91 141{
47a40d1d 142 if (in == NULL || strlen(in) == 0) {
ac3d33df 143 git_error_set(GIT_ERROR_INVALID, "cannot set empty URL");
47a40d1d
CMN
144 return GIT_EINVALIDSPEC;
145 }
12f32d91 146
47a40d1d 147#ifdef GIT_WIN32
12f32d91
ET
148 /* Given a UNC path like \\server\path, we need to convert this
149 * to //server/path for compatibility with core git.
150 */
151 if (in[0] == '\\' && in[1] == '\\' &&
152 (git__isalpha(in[2]) || git__isdigit(in[2]))) {
47a40d1d 153 const char *c;
12f32d91 154 for (c = in; *c; c++)
e579e0f7 155 git_str_putc(out, *c == '\\' ? '/' : *c);
12f32d91 156
e579e0f7 157 return git_str_oom(out) ? -1 : 0;
12f32d91
ET
158 }
159#endif
160
e579e0f7 161 return git_str_puts(out, in);
12f32d91
ET
162}
163
e579e0f7 164static int default_fetchspec_for_name(git_str *buf, const char *name)
778e1c73 165{
e579e0f7 166 if (git_str_printf(buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0)
ac3d33df
JK
167 return -1;
168
169 return 0;
170}
171
172static int ensure_remote_doesnot_exist(git_repository *repo, const char *name)
173{
174 int error;
778e1c73 175 git_remote *remote;
ac3d33df
JK
176
177 error = git_remote_lookup(&remote, repo, name);
178
179 if (error == GIT_ENOTFOUND)
180 return 0;
181
182 if (error < 0)
183 return error;
184
185 git_remote_free(remote);
186
187 git_error_set(GIT_ERROR_CONFIG, "remote '%s' already exists", name);
188
189 return GIT_EEXISTS;
190}
191
22a2d3d5 192int git_remote_create_options_init(git_remote_create_options *opts, unsigned int version)
ac3d33df
JK
193{
194 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
195 opts, version, git_remote_create_options, GIT_REMOTE_CREATE_OPTIONS_INIT);
196 return 0;
197}
198
22a2d3d5
UG
199#ifndef GIT_DEPRECATE_HARD
200int git_remote_create_init_options(git_remote_create_options *opts, unsigned int version)
201{
202 return git_remote_create_options_init(opts, version);
203}
204#endif
205
ac3d33df
JK
206int git_remote_create_with_opts(git_remote **out, const char *url, const git_remote_create_options *opts)
207{
208 git_remote *remote = NULL;
4dbcf0e6 209 git_config *config_ro = NULL, *config_rw;
e579e0f7
MB
210 git_str canonical_url = GIT_STR_INIT;
211 git_str var = GIT_STR_INIT;
212 git_str specbuf = GIT_STR_INIT;
ac3d33df 213 const git_remote_create_options dummy_opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
44f36f6e 214 int error = -1;
778e1c73 215
c25aa7cd
PP
216 GIT_ASSERT_ARG(out);
217 GIT_ASSERT_ARG(url);
617bfdf4 218
ac3d33df
JK
219 if (!opts) {
220 opts = &dummy_opts;
221 }
222
223 GIT_ERROR_CHECK_VERSION(opts, GIT_REMOTE_CREATE_OPTIONS_VERSION, "git_remote_create_options");
224
225 if (opts->name != NULL) {
226 if ((error = ensure_remote_name_is_valid(opts->name)) < 0)
227 return error;
228
229 if (opts->repository &&
230 (error = ensure_remote_doesnot_exist(opts->repository, opts->name)) < 0)
231 return error;
232 }
233
234 if (opts->repository) {
235 if ((error = git_repository_config_snapshot(&config_ro, opts->repository)) < 0)
236 goto on_error;
237 }
22261344 238
59bccf33 239 remote = git__calloc(1, sizeof(git_remote));
ac3d33df 240 GIT_ERROR_CHECK_ALLOC(remote);
778e1c73 241
ac3d33df 242 remote->repo = opts->repository;
617bfdf4 243
ac3d33df 244 if ((error = git_vector_init(&remote->refs, 8, NULL)) < 0 ||
d0cb11e7 245 (error = canonicalize_url(&canonical_url, url)) < 0)
44f36f6e 246 goto on_error;
d88d4311 247
ac3d33df 248 if (opts->repository && !(opts->flags & GIT_REMOTE_CREATE_SKIP_INSTEADOF)) {
e579e0f7
MB
249 if ((error = apply_insteadof(&remote->url, config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH, true)) < 0 ||
250 (error = apply_insteadof(&remote->pushurl, config_ro, canonical_url.ptr, GIT_DIRECTION_PUSH, false)) < 0)
251 goto on_error;
eae0bfdc
PP
252 } else {
253 remote->url = git__strdup(canonical_url.ptr);
e579e0f7 254 GIT_ERROR_CHECK_ALLOC(remote->url);
eae0bfdc 255 }
22261344 256
ac3d33df
JK
257 if (opts->name != NULL) {
258 remote->name = git__strdup(opts->name);
259 GIT_ERROR_CHECK_ALLOC(remote->name);
22261344 260
ac3d33df 261 if (opts->repository &&
e579e0f7 262 ((error = git_str_printf(&var, CONFIG_URL_FMT, opts->name)) < 0 ||
ac3d33df
JK
263 (error = git_repository_config__weakptr(&config_rw, opts->repository)) < 0 ||
264 (error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0))
22261344 265 goto on_error;
617bfdf4
CMN
266 }
267
ac3d33df
JK
268 if (opts->fetchspec != NULL ||
269 (opts->name && !(opts->flags & GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC))) {
270 const char *fetch = NULL;
271 if (opts->fetchspec) {
272 fetch = opts->fetchspec;
273 } else {
274 if ((error = default_fetchspec_for_name(&specbuf, opts->name)) < 0)
275 goto on_error;
77254990 276
e579e0f7 277 fetch = git_str_cstr(&specbuf);
ac3d33df
JK
278 }
279
280 if ((error = add_refspec(remote, fetch, true)) < 0)
baaa8a44 281 goto on_error;
d3cd7da5 282
ac3d33df
JK
283 /* only write for named remotes with a repository */
284 if (opts->repository && opts->name &&
285 ((error = write_add_refspec(opts->repository, opts->name, fetch, true)) < 0 ||
286 (error = lookup_remote_prune_config(remote, config_ro, opts->name)) < 0))
1ef3f0ce
DC
287 goto on_error;
288
d3cd7da5 289 /* Move the data over to where the matching functions can find them */
69f0032b 290 if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
d3cd7da5 291 goto on_error;
baaa8a44
CMN
292 }
293
35a8a8c5 294 /* A remote without a name doesn't download tags */
ac3d33df 295 if (!opts->name)
c648d4a8 296 remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
35a8a8c5
CMN
297 else
298 remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
299
c648d4a8 300
e579e0f7 301 git_str_dispose(&var);
22261344 302
778e1c73 303 *out = remote;
a8846da7 304 error = 0;
baaa8a44
CMN
305
306on_error:
a8846da7
ET
307 if (error)
308 git_remote_free(remote);
309
4dbcf0e6 310 git_config_free(config_ro);
e579e0f7
MB
311 git_str_dispose(&specbuf);
312 git_str_dispose(&canonical_url);
313 git_str_dispose(&var);
44f36f6e 314 return error;
778e1c73
CMN
315}
316
ac3d33df 317int git_remote_create(git_remote **out, git_repository *repo, const char *name, const char *url)
592f466c 318{
e579e0f7 319 git_str buf = GIT_STR_INIT;
592f466c 320 int error;
ac3d33df 321 git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
592f466c 322
ac3d33df
JK
323 /* Those 2 tests are duplicated here because of backward-compatibility */
324 if ((error = ensure_remote_name_is_valid(name)) < 0)
592f466c
BS
325 return error;
326
ac3d33df
JK
327 if (canonicalize_url(&buf, url) < 0)
328 return GIT_ERROR;
592f466c 329
e579e0f7 330 git_str_clear(&buf);
f19304d2 331
ac3d33df
JK
332 opts.repository = repo;
333 opts.name = name;
874dcb25 334
ac3d33df 335 error = git_remote_create_with_opts(out, url, &opts);
874dcb25 336
e579e0f7 337 git_str_dispose(&buf);
874dcb25 338
a68e217f 339 return error;
874dcb25
BS
340}
341
40b99d05
VG
342int git_remote_create_with_fetchspec(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
343{
40b99d05 344 int error;
ac3d33df 345 git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
40b99d05
VG
346
347 if ((error = ensure_remote_name_is_valid(name)) < 0)
348 return error;
349
ac3d33df
JK
350 opts.repository = repo;
351 opts.name = name;
352 opts.fetchspec = fetch;
353 opts.flags = GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC;
c5193e3c 354
ac3d33df 355 return git_remote_create_with_opts(out, url, &opts);
874dcb25
BS
356}
357
ae5b9362 358int git_remote_create_anonymous(git_remote **out, git_repository *repo, const char *url)
874dcb25 359{
ac3d33df
JK
360 git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
361
362 opts.repository = repo;
363
364 return git_remote_create_with_opts(out, url, &opts);
874dcb25
BS
365}
366
eae0bfdc
PP
367int git_remote_create_detached(git_remote **out, const char *url)
368{
ac3d33df 369 return git_remote_create_with_opts(out, url, NULL);
eae0bfdc
PP
370}
371
991b2840 372int git_remote_dup(git_remote **dest, git_remote *source)
40ef47dd 373{
70f7484d 374 size_t i;
991b2840 375 int error = 0;
70f7484d 376 git_refspec *spec;
40ef47dd 377 git_remote *remote = git__calloc(1, sizeof(git_remote));
ac3d33df 378 GIT_ERROR_CHECK_ALLOC(remote);
40ef47dd 379
40ef47dd
AS
380 if (source->name != NULL) {
381 remote->name = git__strdup(source->name);
ac3d33df 382 GIT_ERROR_CHECK_ALLOC(remote->name);
40ef47dd
AS
383 }
384
385 if (source->url != NULL) {
386 remote->url = git__strdup(source->url);
ac3d33df 387 GIT_ERROR_CHECK_ALLOC(remote->url);
40ef47dd
AS
388 }
389
390 if (source->pushurl != NULL) {
391 remote->pushurl = git__strdup(source->pushurl);
ac3d33df 392 GIT_ERROR_CHECK_ALLOC(remote->pushurl);
40ef47dd
AS
393 }
394
395 remote->repo = source->repo;
40ef47dd 396 remote->download_tags = source->download_tags;
5f473947 397 remote->prune_refs = source->prune_refs;
40ef47dd 398
991b2840
AS
399 if (git_vector_init(&remote->refs, 32, NULL) < 0 ||
400 git_vector_init(&remote->refspecs, 2, NULL) < 0 ||
401 git_vector_init(&remote->active_refspecs, 2, NULL) < 0) {
402 error = -1;
403 goto cleanup;
40ef47dd
AS
404 }
405
70f7484d
CMN
406 git_vector_foreach(&source->refspecs, i, spec) {
407 if ((error = add_refspec(remote, spec->string, !spec->push)) < 0)
408 goto cleanup;
409 }
991b2840 410
40ef47dd
AS
411 *dest = remote;
412
991b2840
AS
413cleanup:
414
991b2840
AS
415 if (error < 0)
416 git__free(remote);
417
418 return error;
40ef47dd
AS
419}
420
4330ab26
CMN
421struct refspec_cb_data {
422 git_remote *remote;
423 int fetch;
424};
425
426static int refspec_cb(const git_config_entry *entry, void *payload)
427{
96869a4e 428 struct refspec_cb_data *data = (struct refspec_cb_data *)payload;
25e0b157 429 return add_refspec(data->remote, entry->value, data->fetch);
4330ab26
CMN
430}
431
bf6bebe2 432static int get_optional_config(
e579e0f7 433 bool *found, git_config *config, git_str *buf,
c9ffa84b 434 git_config_foreach_cb cb, void *payload)
bf6bebe2
RB
435{
436 int error = 0;
e579e0f7 437 const char *key = git_str_cstr(buf);
bf6bebe2 438
e579e0f7 439 if (git_str_oom(buf))
bf6bebe2
RB
440 return -1;
441
442 if (cb != NULL)
4efa3290 443 error = git_config_get_multivar_foreach(config, key, NULL, cb, payload);
bf6bebe2
RB
444 else
445 error = git_config_get_string(payload, config, key);
446
c9ffa84b 447 if (found)
448 *found = !error;
449
bf6bebe2 450 if (error == GIT_ENOTFOUND) {
ac3d33df 451 git_error_clear();
bf6bebe2
RB
452 error = 0;
453 }
454
bf6bebe2
RB
455 return error;
456}
457
209425ce 458int git_remote_lookup(git_remote **out, git_repository *repo, const char *name)
9c82357b 459{
6c7cee42 460 git_remote *remote = NULL;
e579e0f7 461 git_str buf = GIT_STR_INIT;
9c82357b 462 const char *val;
4376f7f6 463 int error = 0;
9462c471 464 git_config *config;
96869a4e 465 struct refspec_cb_data data = { NULL };
c9ffa84b 466 bool optional_setting_found = false, found;
4330ab26 467
c25aa7cd
PP
468 GIT_ASSERT_ARG(out);
469 GIT_ASSERT_ARG(repo);
470 GIT_ASSERT_ARG(name);
9462c471 471
032ba9e4 472 if ((error = ensure_remote_name_is_valid(name)) < 0)
473 return error;
474
ac99d86b 475 if ((error = git_repository_config_snapshot(&config, repo)) < 0)
29c4cb09 476 return error;
4bef3565 477
392702ee 478 remote = git__calloc(1, sizeof(git_remote));
ac3d33df 479 GIT_ERROR_CHECK_ALLOC(remote);
9c82357b 480
9c82357b 481 remote->name = git__strdup(name);
ac3d33df 482 GIT_ERROR_CHECK_ALLOC(remote->name);
9c82357b 483
25e0b157
RB
484 if (git_vector_init(&remote->refs, 32, NULL) < 0 ||
485 git_vector_init(&remote->refspecs, 2, NULL) < 0 ||
2cdd5c57 486 git_vector_init(&remote->passive_refspecs, 2, NULL) < 0 ||
25e0b157 487 git_vector_init(&remote->active_refspecs, 2, NULL) < 0) {
2fb9d6de 488 error = -1;
489 goto cleanup;
490 }
d88d4311 491
e579e0f7 492 if ((error = git_str_printf(&buf, "remote.%s.url", name)) < 0)
2fb9d6de 493 goto cleanup;
9c82357b 494
c9ffa84b 495 if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
9c82357b 496 goto cleanup;
bf6bebe2 497
c9ffa84b 498 optional_setting_found |= found;
499
9462c471 500 remote->repo = repo;
35a8a8c5 501 remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
c9ffa84b 502
503 if (found && strlen(val) > 0) {
e579e0f7
MB
504 if ((error = apply_insteadof(&remote->url, config, val, GIT_DIRECTION_FETCH, true)) < 0 ||
505 (error = apply_insteadof(&remote->pushurl, config, val, GIT_DIRECTION_PUSH, false)) < 0)
506 goto cleanup;
c9ffa84b 507 }
9c82357b 508
bf6bebe2 509 val = NULL;
e579e0f7
MB
510 git_str_clear(&buf);
511 git_str_printf(&buf, "remote.%s.pushurl", name);
3ed4b501 512
c9ffa84b 513 if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
3ed4b501 514 goto cleanup;
3ed4b501 515
c9ffa84b 516 optional_setting_found |= found;
517
518 if (!optional_setting_found) {
519 error = GIT_ENOTFOUND;
ac3d33df 520 git_error_set(GIT_ERROR_CONFIG, "remote '%s' does not exist", name);
3ed4b501 521 goto cleanup;
c9ffa84b 522 }
3ed4b501 523
c9ffa84b 524 if (found && strlen(val) > 0) {
e579e0f7
MB
525 if (remote->pushurl)
526 git__free(remote->pushurl);
527
528 if ((error = apply_insteadof(&remote->pushurl, config, val, GIT_DIRECTION_FETCH, true)) < 0)
529 goto cleanup;
3ed4b501
SC
530 }
531
4330ab26
CMN
532 data.remote = remote;
533 data.fetch = true;
96869a4e 534
e579e0f7
MB
535 git_str_clear(&buf);
536 git_str_printf(&buf, "remote.%s.fetch", name);
bf6bebe2 537
c9ffa84b 538 if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0)
2fb9d6de 539 goto cleanup;
9c82357b 540
4330ab26 541 data.fetch = false;
e579e0f7
MB
542 git_str_clear(&buf);
543 git_str_printf(&buf, "remote.%s.push", name);
9c82357b 544
c9ffa84b 545 if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0)
9c82357b
CMN
546 goto cleanup;
547
6c7cee42 548 if ((error = download_tags_value(remote, config)) < 0)
24f2f94e
CMN
549 goto cleanup;
550
1ef3f0ce
DC
551 if ((error = lookup_remote_prune_config(remote, config, name)) < 0)
552 goto cleanup;
553
554 /* Move the data over to where the matching functions can find them */
69f0032b 555 if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
1ef3f0ce
DC
556 goto cleanup;
557
558 *out = remote;
559
560cleanup:
561 git_config_free(config);
e579e0f7 562 git_str_dispose(&buf);
1ef3f0ce
DC
563
564 if (error < 0)
565 git_remote_free(remote);
566
567 return error;
568}
569
570static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name)
571{
e579e0f7 572 git_str buf = GIT_STR_INIT;
1ef3f0ce
DC
573 int error = 0;
574
e579e0f7 575 git_str_printf(&buf, "remote.%s.prune", name);
5f473947 576
e579e0f7 577 if ((error = git_config_get_bool(&remote->prune_refs, config, git_str_cstr(&buf))) < 0) {
5f473947 578 if (error == GIT_ENOTFOUND) {
ac3d33df 579 git_error_clear();
5f473947
L
580
581 if ((error = git_config_get_bool(&remote->prune_refs, config, "fetch.prune")) < 0) {
582 if (error == GIT_ENOTFOUND) {
ac3d33df 583 git_error_clear();
66b71ea5 584 error = 0;
5f473947
L
585 }
586 }
587 }
588 }
589
e579e0f7 590 git_str_dispose(&buf);
9c82357b
CMN
591 return error;
592}
593
df705148 594const char *git_remote_name(const git_remote *remote)
9c82357b 595{
c25aa7cd 596 GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
9c82357b
CMN
597 return remote->name;
598}
599
85e1eded
ES
600git_repository *git_remote_owner(const git_remote *remote)
601{
c25aa7cd 602 GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
85e1eded
ES
603 return remote->repo;
604}
605
df705148 606const char *git_remote_url(const git_remote *remote)
9c82357b 607{
c25aa7cd 608 GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
9c82357b
CMN
609 return remote->url;
610}
611
c25aa7cd
PP
612int git_remote_set_instance_url(git_remote *remote, const char *url)
613{
614 char *tmp;
615
616 GIT_ASSERT_ARG(remote);
617 GIT_ASSERT_ARG(url);
618
619 if ((tmp = git__strdup(url)) == NULL)
620 return -1;
621
622 git__free(remote->url);
623 remote->url = tmp;
624
625 return 0;
626}
627
22261344 628static int set_url(git_repository *repo, const char *remote, const char *pattern, const char *url)
76501590 629{
22261344 630 git_config *cfg;
e579e0f7 631 git_str buf = GIT_STR_INIT, canonical_url = GIT_STR_INIT;
22261344 632 int error;
76501590 633
c25aa7cd
PP
634 GIT_ASSERT_ARG(repo);
635 GIT_ASSERT_ARG(remote);
76501590 636
22261344
CMN
637 if ((error = ensure_remote_name_is_valid(remote)) < 0)
638 return error;
639
640 if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
641 return error;
642
e579e0f7 643 if ((error = git_str_printf(&buf, pattern, remote)) < 0)
22261344
CMN
644 return error;
645
646 if (url) {
647 if ((error = canonicalize_url(&canonical_url, url)) < 0)
648 goto cleanup;
649
650 error = git_config_set_string(cfg, buf.ptr, url);
651 } else {
652 error = git_config_delete_entry(cfg, buf.ptr);
653 }
654
655cleanup:
e579e0f7
MB
656 git_str_dispose(&canonical_url);
657 git_str_dispose(&buf);
22261344
CMN
658
659 return error;
660}
661
662int git_remote_set_url(git_repository *repo, const char *remote, const char *url)
663{
664 return set_url(repo, remote, CONFIG_URL_FMT, url);
76501590
SC
665}
666
df705148 667const char *git_remote_pushurl(const git_remote *remote)
76501590 668{
c25aa7cd 669 GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
76501590
SC
670 return remote->pushurl;
671}
672
c25aa7cd
PP
673int git_remote_set_instance_pushurl(git_remote *remote, const char *url)
674{
675 char *tmp;
676
677 GIT_ASSERT_ARG(remote);
678 GIT_ASSERT_ARG(url);
679
680 if ((tmp = git__strdup(url)) == NULL)
681 return -1;
682
683 git__free(remote->pushurl);
684 remote->pushurl = tmp;
685
686 return 0;
687}
688
689int git_remote_set_pushurl(git_repository *repo, const char *remote, const char *url)
76501590 690{
22261344 691 return set_url(repo, remote, CONFIG_PUSHURL_FMT, url);
76501590
SC
692}
693
c25aa7cd 694static int resolve_url(
e579e0f7 695 git_str *resolved_url,
c25aa7cd
PP
696 const char *url,
697 int direction,
698 const git_remote_callbacks *callbacks)
eff5b499 699{
c25aa7cd
PP
700#ifdef GIT_DEPRECATE_HARD
701 GIT_UNUSED(direction);
702 GIT_UNUSED(callbacks);
703#else
e579e0f7
MB
704 git_buf buf = GIT_BUF_INIT;
705 int error;
22a2d3d5
UG
706
707 if (callbacks && callbacks->resolve_url) {
e579e0f7 708 error = callbacks->resolve_url(&buf, url, direction, callbacks->payload);
c25aa7cd 709
e579e0f7
MB
710 if (error != GIT_PASSTHROUGH) {
711 git_error_set_after_callback_function(error, "git_resolve_url_cb");
c25aa7cd 712
e579e0f7
MB
713 git_str_set(resolved_url, buf.ptr, buf.size);
714 git_buf_dispose(&buf);
715
716 return error;
22a2d3d5
UG
717 }
718 }
c25aa7cd 719#endif
22a2d3d5 720
e579e0f7 721 return git_str_sets(resolved_url, url);
22a2d3d5 722}
eff5b499 723
c25aa7cd 724int git_remote__urlfordirection(
e579e0f7 725 git_str *url_out,
c25aa7cd
PP
726 struct git_remote *remote,
727 int direction,
728 const git_remote_callbacks *callbacks)
22a2d3d5
UG
729{
730 const char *url = NULL;
731
c25aa7cd
PP
732 GIT_ASSERT_ARG(remote);
733 GIT_ASSERT_ARG(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
b83c92dd 734
c25aa7cd
PP
735 if (callbacks && callbacks->remote_ready) {
736 int status = callbacks->remote_ready(remote, direction, callbacks->payload);
737
738 if (status != 0 && status != GIT_PASSTHROUGH) {
739 git_error_set_after_callback_function(status, "git_remote_ready_cb");
740 return status;
741 }
742 }
743
744 if (direction == GIT_DIRECTION_FETCH)
22a2d3d5 745 url = remote->url;
c25aa7cd 746 else if (direction == GIT_DIRECTION_PUSH)
22a2d3d5 747 url = remote->pushurl ? remote->pushurl : remote->url;
eff5b499 748
22a2d3d5
UG
749 if (!url) {
750 git_error_set(GIT_ERROR_INVALID,
751 "malformed remote '%s' - missing %s URL",
752 remote->name ? remote->name : "(anonymous)",
753 direction == GIT_DIRECTION_FETCH ? "fetch" : "push");
754 return GIT_EINVALID;
eff5b499 755 }
c25aa7cd 756
22a2d3d5 757 return resolve_url(url_out, url, direction, callbacks);
eff5b499
SC
758}
759
e579e0f7
MB
760int git_remote_connect_options_init(
761 git_remote_connect_options *opts,
762 unsigned int version)
763{
764 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
765 opts, version, git_remote_connect_options, GIT_REMOTE_CONNECT_OPTIONS_INIT);
766 return 0;
767}
768
769int git_remote_connect_options_dup(
770 git_remote_connect_options *dst,
771 const git_remote_connect_options *src)
8f0104ec 772{
e579e0f7
MB
773 memcpy(dst, src, sizeof(git_remote_connect_options));
774
775 if (git_proxy_options_dup(&dst->proxy_opts, &src->proxy_opts) < 0 ||
776 git_strarray_copy(&dst->custom_headers, &src->custom_headers) < 0)
777 return -1;
778
779 return 0;
780}
781
782void git_remote_connect_options_dispose(git_remote_connect_options *opts)
783{
784 if (!opts)
785 return;
786
787 git_strarray_dispose(&opts->custom_headers);
788 git_proxy_options_dispose(&opts->proxy_opts);
789}
790
791static size_t http_header_name_length(const char *http_header)
792{
793 const char *colon = strchr(http_header, ':');
794 if (!colon)
8f0104ec 795 return 0;
e579e0f7
MB
796 return colon - http_header;
797}
798
799static bool is_malformed_http_header(const char *http_header)
800{
801 const char *c;
802 size_t name_len;
8f0104ec 803
e579e0f7
MB
804 /* Disallow \r and \n */
805 if ((c = strchr(http_header, '\r')) != NULL)
806 return true;
807 if ((c = strchr(http_header, '\n')) != NULL)
808 return true;
809
810 /* Require a header name followed by : */
811 if ((name_len = http_header_name_length(http_header)) < 1)
812 return true;
813
814 return false;
815}
816
817static char *forbidden_custom_headers[] = {
818 "User-Agent",
819 "Host",
820 "Accept",
821 "Content-Type",
822 "Transfer-Encoding",
823 "Content-Length",
824};
825
826static bool is_forbidden_custom_header(const char *custom_header)
827{
828 unsigned long i;
829 size_t name_len = http_header_name_length(custom_header);
830
831 /* Disallow headers that we set */
832 for (i = 0; i < ARRAY_SIZE(forbidden_custom_headers); i++)
833 if (strncmp(forbidden_custom_headers[i], custom_header, name_len) == 0)
834 return true;
835
836 return false;
837}
838
839static int validate_custom_headers(const git_strarray *custom_headers)
840{
841 size_t i;
842
843 if (!custom_headers)
844 return 0;
845
846 for (i = 0; i < custom_headers->count; i++) {
847 if (is_malformed_http_header(custom_headers->strings[i])) {
848 git_error_set(GIT_ERROR_INVALID, "custom HTTP header '%s' is malformed", custom_headers->strings[i]);
849 return -1;
850 }
851
852 if (is_forbidden_custom_header(custom_headers->strings[i])) {
853 git_error_set(GIT_ERROR_INVALID, "custom HTTP header '%s' is already set by libgit2", custom_headers->strings[i]);
854 return -1;
855 }
856 }
857
858 return 0;
8f0104ec
CMN
859}
860
e579e0f7
MB
861static int lookup_redirect_config(
862 git_remote_redirect_t *out,
863 git_repository *repo)
4f2b6093 864{
e579e0f7
MB
865 git_config *config;
866 const char *value;
867 int bool_value, error = 0;
868
869 if (!repo) {
870 *out = GIT_REMOTE_REDIRECT_INITIAL;
4f2b6093 871 return 0;
e579e0f7
MB
872 }
873
874 if ((error = git_repository_config_snapshot(&config, repo)) < 0)
875 goto done;
876
877 if ((error = git_config_get_string(&value, config, "http.followRedirects")) < 0) {
878 if (error == GIT_ENOTFOUND) {
879 *out = GIT_REMOTE_REDIRECT_INITIAL;
880 error = 0;
881 }
4f2b6093 882
e579e0f7
MB
883 goto done;
884 }
885
886 if (git_config_parse_bool(&bool_value, value) == 0) {
887 *out = bool_value ? GIT_REMOTE_REDIRECT_ALL :
888 GIT_REMOTE_REDIRECT_NONE;
889 } else if (strcasecmp(value, "initial") == 0) {
890 *out = GIT_REMOTE_REDIRECT_INITIAL;
891 } else {
892 git_error_set(GIT_ERROR_CONFIG, "invalid configuration setting '%s' for 'http.followRedirects'", value);
893 error = -1;
894 }
895
896done:
897 git_config_free(config);
898 return error;
4f2b6093
MB
899}
900
e579e0f7
MB
901int git_remote_connect_options_normalize(
902 git_remote_connect_options *dst,
903 git_repository *repo,
904 const git_remote_connect_options *src)
9ba49bb5 905{
e579e0f7
MB
906 git_remote_connect_options_dispose(dst);
907 git_remote_connect_options_init(dst, GIT_REMOTE_CONNECT_OPTIONS_VERSION);
908
909 if (src) {
910 GIT_ERROR_CHECK_VERSION(src, GIT_REMOTE_CONNECT_OPTIONS_VERSION, "git_remote_connect_options");
911 GIT_ERROR_CHECK_VERSION(&src->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
912 GIT_ERROR_CHECK_VERSION(&src->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
913
914 if (validate_custom_headers(&src->custom_headers) < 0 ||
915 git_remote_connect_options_dup(dst, src) < 0)
916 return -1;
917 }
918
919 if (dst->follow_redirects == 0) {
920 if (lookup_redirect_config(&dst->follow_redirects, repo) < 0)
921 return -1;
922 }
923
924 return 0;
925}
926
927int git_remote_connect_ext(
928 git_remote *remote,
929 git_direction direction,
930 const git_remote_connect_options *given_opts)
931{
932 git_remote_connect_options opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
933 git_str url = GIT_STR_INIT;
9ba49bb5 934 git_transport *t;
80fc7d6b 935 int error;
9ba49bb5 936
c25aa7cd 937 GIT_ASSERT_ARG(remote);
4bef3565 938
e579e0f7
MB
939 if (given_opts)
940 memcpy(&opts, given_opts, sizeof(git_remote_connect_options));
8f0104ec 941
e579e0f7
MB
942 GIT_ERROR_CHECK_VERSION(&opts.callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
943 GIT_ERROR_CHECK_VERSION(&opts.proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
07bd3e57 944
41fb1ca0
PK
945 t = remote->transport;
946
e579e0f7 947 if ((error = git_remote__urlfordirection(&url, remote, direction, &opts.callbacks)) < 0)
22a2d3d5 948 goto on_error;
eff5b499 949
1697cd6f
PK
950 /* If we don't have a transport object yet, and the caller specified a
951 * custom transport factory, use that */
e579e0f7
MB
952 if (!t && opts.callbacks.transport &&
953 (error = opts.callbacks.transport(&t, remote, opts.callbacks.payload)) < 0)
22a2d3d5 954 goto on_error;
1697cd6f
PK
955
956 /* If we still don't have a transport, then use the global
957 * transport registrations which map URI schemes to transport factories */
22a2d3d5
UG
958 if (!t && (error = git_transport_new(&t, remote, url.ptr)) < 0)
959 goto on_error;
9ba49bb5 960
e579e0f7 961 if ((error = t->connect(t, url.ptr, direction, &opts)) != 0)
4376f7f6 962 goto on_error;
9ba49bb5
CMN
963
964 remote->transport = t;
965
e579e0f7 966 git_str_dispose(&url);
22a2d3d5 967
4376f7f6 968 return 0;
9ba49bb5 969
4376f7f6 970on_error:
22a2d3d5
UG
971 if (t)
972 t->free(t);
973
e579e0f7 974 git_str_dispose(&url);
613d5eb9
PK
975
976 if (t == remote->transport)
977 remote->transport = NULL;
978
80fc7d6b 979 return error;
9ba49bb5
CMN
980}
981
e579e0f7
MB
982int git_remote_connect(
983 git_remote *remote,
984 git_direction direction,
985 const git_remote_callbacks *callbacks,
986 const git_proxy_options *proxy,
987 const git_strarray *custom_headers)
ac3d33df 988{
e579e0f7
MB
989 git_remote_connect_options opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
990
991 if (callbacks)
992 memcpy(&opts.callbacks, callbacks, sizeof(git_remote_callbacks));
993
994 if (proxy)
995 memcpy(&opts.proxy_opts, proxy, sizeof(git_proxy_options));
ac3d33df 996
e579e0f7
MB
997 if (custom_headers)
998 memcpy(&opts.custom_headers, custom_headers, sizeof(git_strarray));
ac3d33df 999
e579e0f7 1000 return git_remote_connect_ext(remote, direction, &opts);
ac3d33df
JK
1001}
1002
359dce72 1003int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote)
9ba49bb5 1004{
c25aa7cd 1005 GIT_ASSERT_ARG(remote);
d88d4311 1006
dc8adda4 1007 if (!remote->transport) {
ac3d33df 1008 git_error_set(GIT_ERROR_NET, "this remote has never connected");
dc8adda4
JG
1009 return -1;
1010 }
1011
359dce72 1012 return remote->transport->ls(out, size, remote->transport);
9ba49bb5
CMN
1013}
1014
e579e0f7
MB
1015int git_remote_capabilities(unsigned int *out, git_remote *remote)
1016{
1017 GIT_ASSERT_ARG(remote);
1018
1019 *out = 0;
1020
1021 if (!remote->transport) {
1022 git_error_set(GIT_ERROR_NET, "this remote has never connected");
1023 return -1;
1024 }
1025
1026 return remote->transport->capabilities(out, remote->transport);
1027}
1028
c25aa7cd 1029static int lookup_config(char **out, git_config *cfg, const char *name)
613d5eb9 1030{
9a97f49e 1031 git_config_entry *ce = NULL;
80fc7d6b 1032 int error;
613d5eb9 1033
c25aa7cd
PP
1034 if ((error = git_config__lookup_entry(&ce, cfg, name, false)) < 0)
1035 return error;
1036
1037 if (ce && ce->value) {
1038 *out = git__strdup(ce->value);
1039 GIT_ERROR_CHECK_ALLOC(*out);
1040 } else {
1041 error = GIT_ENOTFOUND;
1042 }
613d5eb9 1043
c25aa7cd
PP
1044 git_config_entry_free(ce);
1045 return error;
1046}
613d5eb9 1047
c25aa7cd
PP
1048static void url_config_trim(git_net_url *url)
1049{
1050 size_t len = strlen(url->path);
613d5eb9 1051
c25aa7cd
PP
1052 if (url->path[len - 1] == '/') {
1053 len--;
1054 } else {
1055 while (len && url->path[len - 1] != '/')
1056 len--;
1057 }
613d5eb9 1058
c25aa7cd
PP
1059 url->path[len] = '\0';
1060}
1061
1062static int http_proxy_config(char **out, git_remote *remote, git_net_url *url)
1063{
1064 git_config *cfg = NULL;
e579e0f7 1065 git_str buf = GIT_STR_INIT;
c25aa7cd
PP
1066 git_net_url lookup_url = GIT_NET_URL_INIT;
1067 int error;
1068
1069 if ((error = git_net_url_dup(&lookup_url, url)) < 0)
1070 goto done;
1071
1072 if (remote->repo) {
1073 if ((error = git_repository_config(&cfg, remote->repo)) < 0)
1074 goto done;
1075 } else {
1076 if ((error = git_config_open_default(&cfg)) < 0)
1077 goto done;
1078 }
613d5eb9
PK
1079
1080 /* remote.<name>.proxy config setting */
9f77b3f6 1081 if (remote->name && remote->name[0]) {
e579e0f7 1082 git_str_clear(&buf);
613d5eb9 1083
e579e0f7 1084 if ((error = git_str_printf(&buf, "remote.%s.proxy", remote->name)) < 0 ||
c25aa7cd
PP
1085 (error = lookup_config(out, cfg, buf.ptr)) != GIT_ENOTFOUND)
1086 goto done;
1087 }
613d5eb9 1088
c25aa7cd 1089 while (true) {
e579e0f7 1090 git_str_clear(&buf);
613d5eb9 1091
e579e0f7 1092 if ((error = git_str_puts(&buf, "http.")) < 0 ||
c25aa7cd 1093 (error = git_net_url_fmt(&buf, &lookup_url)) < 0 ||
e579e0f7 1094 (error = git_str_puts(&buf, ".proxy")) < 0 ||
c25aa7cd
PP
1095 (error = lookup_config(out, cfg, buf.ptr)) != GIT_ENOTFOUND)
1096 goto done;
613d5eb9 1097
c25aa7cd
PP
1098 if (! lookup_url.path[0])
1099 break;
1100
1101 url_config_trim(&lookup_url);
613d5eb9
PK
1102 }
1103
e579e0f7 1104 git_str_clear(&buf);
e069c621 1105
c25aa7cd
PP
1106 error = lookup_config(out, cfg, "http.proxy");
1107
1108done:
1109 git_config_free(cfg);
e579e0f7 1110 git_str_dispose(&buf);
c25aa7cd
PP
1111 git_net_url_dispose(&lookup_url);
1112 return error;
1113}
1114
1115static int http_proxy_env(char **out, git_remote *remote, git_net_url *url)
1116{
e579e0f7 1117 git_str proxy_env = GIT_STR_INIT, no_proxy_env = GIT_STR_INIT;
c25aa7cd
PP
1118 bool use_ssl = (strcmp(url->scheme, "https") == 0);
1119 int error;
1120
1121 GIT_UNUSED(remote);
613d5eb9 1122
2af282d8 1123 /* http_proxy / https_proxy environment variables */
c25aa7cd 1124 error = git__getenv(&proxy_env, use_ssl ? "https_proxy" : "http_proxy");
5f3276c7 1125
61189a11
CB
1126 /* try uppercase environment variables */
1127 if (error == GIT_ENOTFOUND)
c25aa7cd 1128 error = git__getenv(&proxy_env, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY");
5f3276c7 1129
c25aa7cd
PP
1130 if (error)
1131 goto done;
e069c621 1132
c25aa7cd
PP
1133 /* no_proxy/NO_PROXY environment variables */
1134 error = git__getenv(&no_proxy_env, "no_proxy");
e069c621 1135
c25aa7cd
PP
1136 if (error == GIT_ENOTFOUND)
1137 error = git__getenv(&no_proxy_env, "NO_PROXY");
e069c621 1138
c25aa7cd
PP
1139 if (error && error != GIT_ENOTFOUND)
1140 goto done;
1141
1142 if (!git_net_url_matches_pattern_list(url, no_proxy_env.ptr))
e579e0f7 1143 *out = git_str_detach(&proxy_env);
c25aa7cd
PP
1144 else
1145 error = GIT_ENOTFOUND;
1146
1147done:
e579e0f7
MB
1148 git_str_dispose(&proxy_env);
1149 git_str_dispose(&no_proxy_env);
c25aa7cd
PP
1150 return error;
1151}
1152
1153int git_remote__http_proxy(char **out, git_remote *remote, git_net_url *url)
1154{
1155 int error;
1156
1157 GIT_ASSERT_ARG(out);
1158 GIT_ASSERT_ARG(remote);
1159
1160 *out = NULL;
1161
1162 /*
1163 * Go through the possible sources for proxy configuration,
1164 * Examine the various git config options first, then
1165 * consult environment variables.
1166 */
1167 if ((error = http_proxy_config(out, remote, url)) != GIT_ENOTFOUND ||
1168 (error = http_proxy_env(out, remote, url)) != GIT_ENOTFOUND)
1169 return error;
613d5eb9
PK
1170
1171 return 0;
1172}
1173
af613ecd
CMN
1174/* DWIM `refspecs` based on `refs` and append the output to `out` */
1175static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs)
d8488457 1176{
af613ecd 1177 size_t i;
d8488457 1178 git_refspec *spec;
d8488457
CMN
1179
1180 git_vector_foreach(refspecs, i, spec) {
af613ecd
CMN
1181 if (git_refspec__dwim_one(out, spec, refs) < 0)
1182 return -1;
1183 }
d8488457 1184
af613ecd
CMN
1185 return 0;
1186}
d8488457 1187
af613ecd
CMN
1188static void free_refspecs(git_vector *vec)
1189{
1190 size_t i;
1191 git_refspec *spec;
d8488457 1192
af613ecd 1193 git_vector_foreach(vec, i, spec) {
ac3d33df 1194 git_refspec__dispose(spec);
af613ecd 1195 git__free(spec);
d8488457
CMN
1196 }
1197
af613ecd 1198 git_vector_clear(vec);
d8488457
CMN
1199}
1200
1201static int remote_head_cmp(const void *_a, const void *_b)
1202{
1203 const git_remote_head *a = (git_remote_head *) _a;
1204 const git_remote_head *b = (git_remote_head *) _b;
1205
1206 return git__strcmp_cb(a->name, b->name);
1207}
1208
877cde76
CMN
1209static int ls_to_vector(git_vector *out, git_remote *remote)
1210{
1211 git_remote_head **heads;
1212 size_t heads_len, i;
1213
1214 if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0)
1215 return -1;
1216
1217 if (git_vector_init(out, heads_len, remote_head_cmp) < 0)
1218 return -1;
1219
1220 for (i = 0; i < heads_len; i++) {
1221 if (git_vector_insert(out, heads[i]) < 0)
1222 return -1;
1223 }
1224
1225 return 0;
1226}
1227
e579e0f7
MB
1228#define copy_opts(out, in) \
1229 if (in) { \
1230 (out)->callbacks = (in)->callbacks; \
1231 (out)->proxy_opts = (in)->proxy_opts; \
1232 (out)->custom_headers = (in)->custom_headers; \
1233 (out)->follow_redirects = (in)->follow_redirects; \
eae0bfdc
PP
1234 }
1235
e579e0f7
MB
1236GIT_INLINE(int) connect_opts_from_fetch_opts(
1237 git_remote_connect_options *out,
1238 git_remote *remote,
1239 const git_fetch_options *fetch_opts)
1240{
1241 git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
1242 copy_opts(&tmp, fetch_opts);
1243 return git_remote_connect_options_normalize(out, remote->repo, &tmp);
1244}
1245
1246static int connect_or_reset_options(
1247 git_remote *remote,
1248 int direction,
1249 git_remote_connect_options *opts)
1250{
1251 if (!git_remote_connected(remote)) {
1252 return git_remote_connect_ext(remote, direction, opts);
1253 } else {
1254 return remote->transport->set_connect_opts(remote->transport, opts);
8f0104ec 1255 }
e579e0f7 1256}
8f0104ec 1257
e579e0f7
MB
1258/* Download from an already connected remote. */
1259static int git_remote__download(
1260 git_remote *remote,
1261 const git_strarray *refspecs,
1262 const git_fetch_options *opts)
1263{
1264 git_vector *to_active, specs = GIT_VECTOR_INIT, refs = GIT_VECTOR_INIT;
1265 size_t i;
1266 int error;
8f0104ec 1267
877cde76 1268 if (ls_to_vector(&refs, remote) < 0)
d8488457 1269 return -1;
d8488457 1270
e579e0f7 1271 if ((error = git_vector_init(&specs, 0, NULL)) < 0)
3f894205
CMN
1272 goto on_error;
1273
c5837cad 1274 remote->passed_refspecs = 0;
8e398e4c 1275 if (!refspecs || !refspecs->count) {
3f894205
CMN
1276 to_active = &remote->refspecs;
1277 } else {
1278 for (i = 0; i < refspecs->count; i++) {
1279 if ((error = add_refspec_to(&specs, refspecs->strings[i], true)) < 0)
1280 goto on_error;
1281 }
1282
1283 to_active = &specs;
c5837cad 1284 remote->passed_refspecs = 1;
3f894205
CMN
1285 }
1286
2cdd5c57
CMN
1287 free_refspecs(&remote->passive_refspecs);
1288 if ((error = dwim_refspecs(&remote->passive_refspecs, &remote->refspecs, &refs)) < 0)
1289 goto on_error;
af613ecd 1290
2cdd5c57 1291 free_refspecs(&remote->active_refspecs);
3f894205 1292 error = dwim_refspecs(&remote->active_refspecs, to_active, &refs);
2cdd5c57 1293
877cde76 1294 git_vector_free(&refs);
3f894205
CMN
1295 free_refspecs(&specs);
1296 git_vector_free(&specs);
877cde76
CMN
1297
1298 if (error < 0)
e579e0f7 1299 goto on_error;
d8488457 1300
c070ac64
POL
1301 if (remote->push) {
1302 git_push_free(remote->push);
1303 remote->push = NULL;
1304 }
1305
35a8a8c5 1306 if ((error = git_fetch_negotiate(remote, opts)) < 0)
e579e0f7 1307 goto on_error;
95057b85 1308
e579e0f7 1309 error = git_fetch_download_pack(remote);
3f894205
CMN
1310
1311on_error:
1312 git_vector_free(&refs);
1313 free_refspecs(&specs);
1314 git_vector_free(&specs);
1315 return error;
48a65a07
CMN
1316}
1317
e579e0f7
MB
1318int git_remote_download(
1319 git_remote *remote,
1320 const git_strarray *refspecs,
1321 const git_fetch_options *opts)
1322{
1323 git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
1324 int error;
1325
1326 GIT_ASSERT_ARG(remote);
1327
1328 if (!remote->repo) {
1329 git_error_set(GIT_ERROR_INVALID, "cannot download detached remote");
1330 return -1;
1331 }
1332
1333 if (connect_opts_from_fetch_opts(&connect_opts, remote, opts) < 0)
1334 return -1;
1335
1336 if ((error = connect_or_reset_options(remote, GIT_DIRECTION_FETCH, &connect_opts)) < 0)
1337 return error;
1338
1339 return git_remote__download(remote, refspecs, opts);
1340}
1341
c3ab1e5a 1342int git_remote_fetch(
e579e0f7
MB
1343 git_remote *remote,
1344 const git_strarray *refspecs,
1345 const git_fetch_options *opts,
1346 const char *reflog_message)
fe3a40a4 1347{
3eff2a57 1348 int error, update_fetchhead = 1;
35a8a8c5 1349 git_remote_autotag_option_t tagopt = remote->download_tags;
6fb373a0 1350 bool prune = false;
e579e0f7
MB
1351 git_str reflog_msg_buf = GIT_STR_INIT;
1352 git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
1353
1354 GIT_ASSERT_ARG(remote);
1355
1356 if (!remote->repo) {
1357 git_error_set(GIT_ERROR_INVALID, "cannot download detached remote");
1358 return -1;
1359 }
1360
1361 if (connect_opts_from_fetch_opts(&connect_opts, remote, opts) < 0)
1362 return -1;
1363
1364 if ((error = connect_or_reset_options(remote, GIT_DIRECTION_FETCH, &connect_opts)) < 0)
1365 return error;
8f0104ec
CMN
1366
1367 if (opts) {
3eff2a57 1368 update_fetchhead = opts->update_fetchhead;
35a8a8c5 1369 tagopt = opts->download_tags;
8f0104ec 1370 }
fe3a40a4
CMN
1371
1372 /* Connect and download everything */
e579e0f7 1373 error = git_remote__download(remote, refspecs, opts);
fe3a40a4
CMN
1374
1375 /* We don't need to be connected anymore */
1376 git_remote_disconnect(remote);
1377
06a8f5c3
BL
1378 /* If the download failed, return the error */
1379 if (error != 0)
e579e0f7 1380 goto done;
06a8f5c3 1381
db55bb73
BS
1382 /* Default reflog message */
1383 if (reflog_message)
e579e0f7 1384 git_str_sets(&reflog_msg_buf, reflog_message);
db55bb73 1385 else {
e579e0f7 1386 git_str_printf(&reflog_msg_buf, "fetch %s",
db55bb73
BS
1387 remote->name ? remote->name : remote->url);
1388 }
1389
fe3a40a4 1390 /* Create "remote/foo" branches for all remote branches */
e579e0f7
MB
1391 error = git_remote_update_tips(remote, &connect_opts.callbacks, update_fetchhead, tagopt, git_str_cstr(&reflog_msg_buf));
1392 git_str_dispose(&reflog_msg_buf);
8c13eaed 1393 if (error < 0)
e579e0f7 1394 goto done;
8c13eaed 1395
6fb373a0
CMN
1396 if (opts && opts->prune == GIT_FETCH_PRUNE)
1397 prune = true;
c2418f46 1398 else if (opts && opts->prune == GIT_FETCH_PRUNE_UNSPECIFIED && remote->prune_refs)
6fb373a0
CMN
1399 prune = true;
1400 else if (opts && opts->prune == GIT_FETCH_NO_PRUNE)
1401 prune = false;
1402 else
1403 prune = remote->prune_refs;
1404
1405 if (prune)
e579e0f7 1406 error = git_remote_prune(remote, &connect_opts.callbacks);
8c13eaed 1407
e579e0f7
MB
1408done:
1409 git_remote_connect_options_dispose(&connect_opts);
db55bb73 1410 return error;
fe3a40a4
CMN
1411}
1412
b0f6e45d
ET
1413static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src)
1414{
1415 unsigned int i;
1416 git_remote_head *remote_ref;
1417
c25aa7cd
PP
1418 GIT_ASSERT_ARG(update_heads);
1419 GIT_ASSERT_ARG(fetchspec_src);
b0f6e45d
ET
1420
1421 *out = NULL;
7d4b65f6 1422
1423 git_vector_foreach(update_heads, i, remote_ref) {
1424 if (strcmp(remote_ref->name, fetchspec_src) == 0) {
1425 *out = remote_ref;
1426 break;
b0f6e45d
ET
1427 }
1428 }
1429
1430 return 0;
1431}
1432
e579e0f7 1433static int ref_to_update(int *update, git_str *remote_name, git_remote *remote, git_refspec *spec, const char *ref_name)
2c9b9c8b
CMN
1434{
1435 int error = 0;
1436 git_repository *repo;
e579e0f7
MB
1437 git_str upstream_remote = GIT_STR_INIT;
1438 git_str upstream_name = GIT_STR_INIT;
2c9b9c8b
CMN
1439
1440 repo = git_remote_owner(remote);
1441
1442 if ((!git_reference__is_branch(ref_name)) ||
1443 !git_remote_name(remote) ||
e579e0f7
MB
1444 (error = git_branch__upstream_remote(&upstream_remote, repo, ref_name) < 0) ||
1445 git__strcmp(git_remote_name(remote), git_str_cstr(&upstream_remote)) ||
1446 (error = git_branch__upstream_name(&upstream_name, repo, ref_name)) < 0 ||
1447 !git_refspec_dst_matches(spec, git_str_cstr(&upstream_name)) ||
1448 (error = git_refspec__rtransform(remote_name, spec, upstream_name.ptr)) < 0) {
2c9b9c8b
CMN
1449 /* Not an error if there is no upstream */
1450 if (error == GIT_ENOTFOUND) {
ac3d33df 1451 git_error_clear();
2c9b9c8b
CMN
1452 error = 0;
1453 }
1454
1455 *update = 0;
1456 } else {
1457 *update = 1;
1458 }
1459
e579e0f7
MB
1460 git_str_dispose(&upstream_remote);
1461 git_str_dispose(&upstream_name);
2c9b9c8b
CMN
1462 return error;
1463}
1464
f49819aa 1465static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_refspec *spec, git_vector *update_heads, git_reference *ref)
b0f6e45d
ET
1466{
1467 git_reference *resolved_ref = NULL;
e579e0f7 1468 git_str remote_name = GIT_STR_INIT;
f49819aa 1469 git_config *config = NULL;
e235db02 1470 const char *ref_name;
2c9b9c8b 1471 int error = 0, update;
b0f6e45d 1472
c25aa7cd
PP
1473 GIT_ASSERT_ARG(out);
1474 GIT_ASSERT_ARG(spec);
1475 GIT_ASSERT_ARG(ref);
b0f6e45d
ET
1476
1477 *out = NULL;
1478
67d4997a
CMN
1479 error = git_reference_resolve(&resolved_ref, ref);
1480
1481 /* If we're in an unborn branch, let's pretend nothing happened */
ac3d33df 1482 if (error == GIT_ENOTFOUND && git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) {
67d4997a
CMN
1483 ref_name = git_reference_symbolic_target(ref);
1484 error = 0;
1485 } else {
1486 ref_name = git_reference_name(resolved_ref);
1487 }
1488
c25aa7cd
PP
1489 /*
1490 * The ref name may be unresolvable - perhaps it's pointing to
1491 * something invalid. In this case, there is no remote head for
1492 * this ref.
1493 */
1494 if (!ref_name) {
1495 error = 0;
1496 goto cleanup;
1497 }
1498
2c9b9c8b 1499 if ((error = ref_to_update(&update, &remote_name, remote, spec, ref_name)) < 0)
b0f6e45d 1500 goto cleanup;
b0f6e45d 1501
2c9b9c8b 1502 if (update)
e579e0f7 1503 error = remote_head_for_fetchspec_src(out, update_heads, git_str_cstr(&remote_name));
b0f6e45d
ET
1504
1505cleanup:
e579e0f7 1506 git_str_dispose(&remote_name);
b0f6e45d 1507 git_reference_free(resolved_ref);
f49819aa 1508 git_config_free(config);
b0f6e45d
ET
1509 return error;
1510}
1511
4330ab26 1512static int git_remote_write_fetchhead(git_remote *remote, git_refspec *spec, git_vector *update_heads)
b0f6e45d 1513{
b0f6e45d
ET
1514 git_reference *head_ref = NULL;
1515 git_fetchhead_ref *fetchhead_ref;
1516 git_remote_head *remote_ref, *merge_remote_ref;
1517 git_vector fetchhead_refs;
1518 bool include_all_fetchheads;
1519 unsigned int i = 0;
1520 int error = 0;
1521
c25aa7cd 1522 GIT_ASSERT_ARG(remote);
b0f6e45d 1523
404eadb0
CMN
1524 /* no heads, nothing to do */
1525 if (update_heads->length == 0)
1526 return 0;
1527
b0f6e45d
ET
1528 if (git_vector_init(&fetchhead_refs, update_heads->length, git_fetchhead_ref_cmp) < 0)
1529 return -1;
1530
1531 /* Iff refspec is * (but not subdir slash star), include tags */
1532 include_all_fetchheads = (strcmp(GIT_REFS_HEADS_DIR "*", git_refspec_src(spec)) == 0);
1533
1534 /* Determine what to merge: if refspec was a wildcard, just use HEAD */
1535 if (git_refspec_is_wildcard(spec)) {
1536 if ((error = git_reference_lookup(&head_ref, remote->repo, GIT_HEAD_FILE)) < 0 ||
f49819aa 1537 (error = remote_head_for_ref(&merge_remote_ref, remote, spec, update_heads, head_ref)) < 0)
b0f6e45d
ET
1538 goto cleanup;
1539 } else {
1540 /* If we're fetching a single refspec, that's the only thing that should be in FETCH_HEAD. */
1541 if ((error = remote_head_for_fetchspec_src(&merge_remote_ref, update_heads, git_refspec_src(spec))) < 0)
1542 goto cleanup;
1543 }
1544
1545 /* Create the FETCH_HEAD file */
7d4b65f6 1546 git_vector_foreach(update_heads, i, remote_ref) {
b0f6e45d
ET
1547 int merge_this_fetchhead = (merge_remote_ref == remote_ref);
1548
1549 if (!include_all_fetchheads &&
1550 !git_refspec_src_matches(spec, remote_ref->name) &&
1551 !merge_this_fetchhead)
1552 continue;
1553
1554 if (git_fetchhead_ref_create(&fetchhead_ref,
1555 &remote_ref->oid,
1556 merge_this_fetchhead,
1557 remote_ref->name,
1558 git_remote_url(remote)) < 0)
1559 goto cleanup;
1560
1561 if (git_vector_insert(&fetchhead_refs, fetchhead_ref) < 0)
1562 goto cleanup;
1563 }
1564
1565 git_fetchhead_write(remote->repo, &fetchhead_refs);
1566
1567cleanup:
1568 for (i = 0; i < fetchhead_refs.length; ++i)
1569 git_fetchhead_ref_free(fetchhead_refs.contents[i]);
1570
1571 git_vector_free(&fetchhead_refs);
1572 git_reference_free(head_ref);
1573
1574 return error;
1575}
1576
59ff8b67
CMN
1577/**
1578 * Generate a list of candidates for pruning by getting a list of
1579 * references which match the rhs of an active refspec.
1580 */
1581static int prune_candidates(git_vector *candidates, git_remote *remote)
5f473947
L
1582{
1583 git_strarray arr = { 0 };
59ff8b67 1584 size_t i;
5f473947
L
1585 int error;
1586
1587 if ((error = git_reference_list(&arr, remote->repo)) < 0)
1588 return error;
1589
59ff8b67
CMN
1590 for (i = 0; i < arr.count; i++) {
1591 const char *refname = arr.strings[i];
1592 char *refname_dup;
1593
1594 if (!git_remote__matching_dst_refspec(remote, refname))
1595 continue;
1596
1597 refname_dup = git__strdup(refname);
ac3d33df 1598 GIT_ERROR_CHECK_ALLOC(refname_dup);
59ff8b67
CMN
1599
1600 if ((error = git_vector_insert(candidates, refname_dup)) < 0)
1601 goto out;
1602 }
1603
1604out:
22a2d3d5 1605 git_strarray_dispose(&arr);
59ff8b67
CMN
1606 return error;
1607}
1608
1609static int find_head(const void *_a, const void *_b)
1610{
1611 git_remote_head *a = (git_remote_head *) _a;
1612 git_remote_head *b = (git_remote_head *) _b;
1613
1614 return strcmp(a->name, b->name);
1615}
1616
8f0104ec 1617int git_remote_prune(git_remote *remote, const git_remote_callbacks *callbacks)
59ff8b67
CMN
1618{
1619 size_t i, j;
1620 git_vector remote_refs = GIT_VECTOR_INIT;
1621 git_vector candidates = GIT_VECTOR_INIT;
1622 const git_refspec *spec;
1623 const char *refname;
1624 int error;
1625 git_oid zero_id = {{ 0 }};
1626
8f0104ec 1627 if (callbacks)
ac3d33df 1628 GIT_ERROR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
8f0104ec 1629
5f473947
L
1630 if ((error = ls_to_vector(&remote_refs, remote)) < 0)
1631 goto cleanup;
1632
59ff8b67 1633 git_vector_set_cmp(&remote_refs, find_head);
5f473947 1634
59ff8b67
CMN
1635 if ((error = prune_candidates(&candidates, remote)) < 0)
1636 goto cleanup;
5f473947 1637
59ff8b67
CMN
1638 /*
1639 * Remove those entries from the candidate list for which we
1640 * can find a remote reference in at least one refspec.
1641 */
1642 git_vector_foreach(&candidates, i, refname) {
1643 git_vector_foreach(&remote->active_refspecs, j, spec) {
e579e0f7 1644 git_str buf = GIT_STR_INIT;
59ff8b67
CMN
1645 size_t pos;
1646 char *src_name;
1647 git_remote_head key = {0};
1648
1649 if (!git_refspec_dst_matches(spec, refname))
5f473947
L
1650 continue;
1651
e579e0f7 1652 if ((error = git_refspec__rtransform(&buf, spec, refname)) < 0)
59ff8b67 1653 goto cleanup;
5f473947 1654
e579e0f7 1655 key.name = (char *) git_str_cstr(&buf);
6c7cee42 1656 error = git_vector_bsearch(&pos, &remote_refs, &key);
e579e0f7 1657 git_str_dispose(&buf);
5f473947 1658
59ff8b67
CMN
1659 if (error < 0 && error != GIT_ENOTFOUND)
1660 goto cleanup;
5f473947 1661
59ff8b67 1662 if (error == GIT_ENOTFOUND)
5f473947
L
1663 continue;
1664
c25aa7cd 1665 /* If we did find a source, remove it from the candidates. */
59ff8b67
CMN
1666 if ((error = git_vector_set((void **) &src_name, &candidates, i, NULL)) < 0)
1667 goto cleanup;
1668
1669 git__free(src_name);
1670 break;
5f473947
L
1671 }
1672 }
1673
59ff8b67
CMN
1674 /*
1675 * For those candidates still left in the list, we need to
1676 * remove them. We do not remove symrefs, as those are for
1677 * stuff like origin/HEAD which will never match, but we do
1678 * not want to remove them.
1679 */
1680 git_vector_foreach(&candidates, i, refname) {
1681 git_reference *ref;
1682 git_oid id;
1683
1684 if (refname == NULL)
1685 continue;
1686
1687 error = git_reference_lookup(&ref, remote->repo, refname);
1688 /* as we want it gone, let's not consider this an error */
1689 if (error == GIT_ENOTFOUND)
1690 continue;
1691
1692 if (error < 0)
1693 goto cleanup;
1694
ac3d33df 1695 if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) {
59ff8b67
CMN
1696 git_reference_free(ref);
1697 continue;
1698 }
1699
1700 git_oid_cpy(&id, git_reference_target(ref));
1701 error = git_reference_delete(ref);
1702 git_reference_free(ref);
1703 if (error < 0)
1704 goto cleanup;
1705
8f0104ec
CMN
1706 if (callbacks && callbacks->update_tips)
1707 error = callbacks->update_tips(refname, &id, &zero_id, callbacks->payload);
59ff8b67
CMN
1708
1709 if (error < 0)
1710 goto cleanup;
1711 }
1712
5f473947 1713cleanup:
5f473947 1714 git_vector_free(&remote_refs);
59ff8b67 1715 git_vector_free_deep(&candidates);
5f473947
L
1716 return error;
1717}
1718
e579e0f7
MB
1719static int update_ref(
1720 const git_remote *remote,
1721 const char *ref_name,
1722 git_oid *id,
1723 const char *msg,
1724 const git_remote_callbacks *callbacks)
441f57c2 1725{
441f57c2 1726 git_reference *ref;
e579e0f7
MB
1727 git_oid old_id;
1728 int error;
441f57c2 1729
e579e0f7 1730 error = git_reference_name_to_id(&old_id, remote->repo, ref_name);
4bef3565 1731
e579e0f7
MB
1732 if (error < 0 && error != GIT_ENOTFOUND)
1733 return error;
1734 else if (error == 0 && git_oid_equal(&old_id, id))
1735 return 0;
a37ddf7e 1736
e579e0f7
MB
1737 /* If we did find a current reference, make sure we haven't lost a race */
1738 if (error)
1739 error = git_reference_create(&ref, remote->repo, ref_name, id, true, msg);
1740 else
1741 error = git_reference_create_matching(&ref, remote->repo, ref_name, id, true, &old_id, msg);
a37ddf7e 1742
e579e0f7 1743 git_reference_free(ref);
585a2eb7 1744
e579e0f7
MB
1745 if (error < 0)
1746 return error;
517bda19 1747
e579e0f7
MB
1748 if (callbacks && callbacks->update_tips &&
1749 (error = callbacks->update_tips(ref_name, &old_id, id, callbacks->payload)) < 0)
1750 return error;
c25aa7cd 1751
e579e0f7
MB
1752 return 0;
1753}
a37ddf7e 1754
e579e0f7
MB
1755static int update_one_tip(
1756 git_vector *update_heads,
1757 git_remote *remote,
1758 git_refspec *spec,
1759 git_remote_head *head,
1760 git_refspec *tagspec,
1761 git_remote_autotag_option_t tagopt,
1762 const char *log_message,
1763 const git_remote_callbacks *callbacks)
1764{
1765 git_odb *odb;
1766 git_str refname = GIT_STR_INIT;
1767 git_reference *ref = NULL;
1768 bool autotag = false;
1769 git_oid old;
1770 int valid;
1771 int error;
3230a44f 1772
e579e0f7
MB
1773 if ((error = git_repository_odb__weakptr(&odb, remote->repo)) < 0)
1774 goto done;
a37ddf7e 1775
e579e0f7
MB
1776 /* Ignore malformed ref names (which also saves us from tag^{} */
1777 if ((error = git_reference_name_is_valid(&valid, head->name)) < 0)
1778 goto done;
e3e017d4 1779
e579e0f7
MB
1780 if (!valid)
1781 goto done;
23aa7c90 1782
e579e0f7
MB
1783 /* If we have a tag, see if the auto-follow rules say to update it */
1784 if (git_refspec_src_matches(tagspec, head->name)) {
1785 if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_AUTO)
1786 autotag = true;
1787
1788 if (tagopt != GIT_REMOTE_DOWNLOAD_TAGS_NONE) {
1789 if (git_str_puts(&refname, head->name) < 0)
1790 goto done;
e3e017d4 1791 }
e579e0f7 1792 }
e3e017d4 1793
e579e0f7
MB
1794 /* If we didn't want to auto-follow the tag, check if the refspec matches */
1795 if (!autotag && git_refspec_src_matches(spec, head->name)) {
1796 if (spec->dst) {
1797 if ((error = git_refspec__transform(&refname, spec, head->name)) < 0)
1798 goto done;
1799 } else {
1800 /*
1801 * no rhs means store it in FETCH_HEAD, even if we don't
1802 * update anything else.
1803 */
1804 error = git_vector_insert(update_heads, head);
1805 goto done;
a37ddf7e 1806 }
e579e0f7 1807 }
a37ddf7e 1808
e579e0f7
MB
1809 /* If we still don't have a refname, we don't want it */
1810 if (git_str_len(&refname) == 0)
1811 goto done;
f184836b 1812
e579e0f7
MB
1813 /* In autotag mode, only create tags for objects already in db */
1814 if (autotag && !git_odb_exists(odb, &head->oid))
1815 goto done;
b0f6e45d 1816
e579e0f7
MB
1817 if (!autotag && (error = git_vector_insert(update_heads, head)) < 0)
1818 goto done;
f184836b 1819
e579e0f7 1820 error = git_reference_name_to_id(&old, remote->repo, refname.ptr);
c25aa7cd 1821
e579e0f7
MB
1822 if (error < 0 && error != GIT_ENOTFOUND)
1823 goto done;
f184836b 1824
e579e0f7
MB
1825 if (!(error || error == GIT_ENOTFOUND) &&
1826 !spec->force &&
1827 !git_graph_descendant_of(remote->repo, &head->oid, &old)) {
1828 error = 0;
1829 goto done;
1830 }
d908351a 1831
e579e0f7 1832 if (error == GIT_ENOTFOUND) {
ad5611d8 1833 memset(&old, 0, sizeof(git_oid));
e579e0f7
MB
1834 error = 0;
1835
1836 if (autotag && (error = git_vector_insert(update_heads, head)) < 0)
1837 goto done;
1838 }
441f57c2 1839
e579e0f7
MB
1840 if (!git_oid__cmp(&old, &head->oid))
1841 goto done;
1842
1843 /* In autotag mode, don't overwrite any locally-existing tags */
1844 error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag,
1845 log_message);
7f9673e4 1846
e579e0f7 1847 if (error < 0) {
7f9673e4 1848 if (error == GIT_EEXISTS)
e579e0f7 1849 error = 0;
7f9673e4 1850
e579e0f7
MB
1851 goto done;
1852 }
1853
1854 if (callbacks && callbacks->update_tips != NULL &&
1855 (error = callbacks->update_tips(refname.ptr, &old, &head->oid, callbacks->payload)) < 0)
1856 git_error_set_after_callback_function(error, "git_remote_fetch");
1857
1858done:
1859 git_reference_free(ref);
1860 git_str_dispose(&refname);
1861 return error;
1862}
1863
1864static int update_tips_for_spec(
1865 git_remote *remote,
1866 const git_remote_callbacks *callbacks,
1867 int update_fetchhead,
1868 git_remote_autotag_option_t tagopt,
1869 git_refspec *spec,
1870 git_vector *refs,
1871 const char *log_message)
1872{
1873 git_refspec tagspec;
1874 git_remote_head *head, oid_head;
1875 git_vector update_heads;
1876 int error = 0;
1877 size_t i;
1878
1879 GIT_ASSERT_ARG(remote);
1880
1881 if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
1882 return -1;
1883
1884 /* Make a copy of the transport's refs */
1885 if (git_vector_init(&update_heads, 16, NULL) < 0)
1886 return -1;
1887
1888 /* Update tips based on the remote heads */
1889 git_vector_foreach(refs, i, head) {
1890 if (update_one_tip(&update_heads, remote, spec, head, &tagspec, tagopt, log_message, callbacks) < 0)
944d250f 1891 goto on_error;
e579e0f7 1892 }
39157563 1893
e579e0f7
MB
1894 /* Handle specified oid sources */
1895 if (git_oid__is_hexstr(spec->src)) {
1896 git_oid id;
f184836b 1897
e579e0f7
MB
1898 if ((error = git_oid_fromstr(&id, spec->src)) < 0)
1899 goto on_error;
1900
1901 if (spec->dst &&
1902 (error = update_ref(remote, spec->dst, &id, log_message, callbacks)) < 0)
1903 goto on_error;
1904
1905 git_oid_cpy(&oid_head.oid, &id);
1906 oid_head.name = spec->src;
1907
1908 if ((error = git_vector_insert(&update_heads, &oid_head)) < 0)
1909 goto on_error;
441f57c2
CMN
1910 }
1911
3eff2a57 1912 if (update_fetchhead &&
4330ab26 1913 (error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0)
b0f6e45d
ET
1914 goto on_error;
1915
ac3d33df 1916 git_refspec__dispose(&tagspec);
e579e0f7 1917 git_vector_free(&update_heads);
f184836b
CMN
1918 return 0;
1919
1920on_error:
ac3d33df 1921 git_refspec__dispose(&tagspec);
e579e0f7 1922 git_vector_free(&update_heads);
f184836b 1923 return -1;
97769280 1924
441f57c2
CMN
1925}
1926
c5837cad
CMN
1927/**
1928 * Iteration over the three vectors, with a pause whenever we find a match
1929 *
1930 * On each stop, we store the iteration stat in the inout i,j,k
1931 * parameters, and return the currently matching passive refspec as
1932 * well as the head which we matched.
1933 */
1934static int next_head(const git_remote *remote, git_vector *refs,
1935 git_refspec **out_spec, git_remote_head **out_head,
1936 size_t *out_i, size_t *out_j, size_t *out_k)
1937{
1938 const git_vector *active, *passive;
1939 git_remote_head *head;
1940 git_refspec *spec, *passive_spec;
1941 size_t i, j, k;
c25aa7cd 1942 int valid;
c5837cad
CMN
1943
1944 active = &remote->active_refspecs;
1945 passive = &remote->passive_refspecs;
1946
1947 i = *out_i;
1948 j = *out_j;
1949 k = *out_k;
1950
1951 for (; i < refs->length; i++) {
1952 head = git_vector_get(refs, i);
1953
c25aa7cd
PP
1954 if (git_reference_name_is_valid(&valid, head->name) < 0)
1955 return -1;
1956
1957 if (!valid)
c5837cad
CMN
1958 continue;
1959
1960 for (; j < active->length; j++) {
1961 spec = git_vector_get(active, j);
1962
1963 if (!git_refspec_src_matches(spec, head->name))
1964 continue;
1965
1966 for (; k < passive->length; k++) {
1967 passive_spec = git_vector_get(passive, k);
1968
1969 if (!git_refspec_src_matches(passive_spec, head->name))
1970 continue;
1971
1972 *out_spec = passive_spec;
1973 *out_head = head;
1974 *out_i = i;
1975 *out_j = j;
1976 *out_k = k + 1;
1977 return 0;
1978
1979 }
1980 k = 0;
1981 }
1982 j = 0;
1983 }
1984
1985 return GIT_ITEROVER;
1986}
1987
e579e0f7
MB
1988static int opportunistic_updates(
1989 const git_remote *remote,
1990 const git_remote_callbacks *callbacks,
1991 git_vector *refs,
1992 const char *msg)
c5837cad
CMN
1993{
1994 size_t i, j, k;
1995 git_refspec *spec;
1996 git_remote_head *head;
e579e0f7 1997 git_str refname = GIT_STR_INIT;
9566ce43 1998 int error = 0;
c5837cad
CMN
1999
2000 i = j = k = 0;
2001
e579e0f7 2002 /* Handle refspecs matching remote heads */
c5837cad
CMN
2003 while ((error = next_head(remote, refs, &spec, &head, &i, &j, &k)) == 0) {
2004 /*
2005 * If we got here, there is a refspec which was used
2006 * for fetching which matches the source of one of the
2007 * passive refspecs, so we should update that
2008 * remote-tracking branch, but not add it to
2009 * FETCH_HEAD
2010 */
2011
e579e0f7
MB
2012 git_str_clear(&refname);
2013 if ((error = git_refspec__transform(&refname, spec, head->name)) < 0 ||
2014 (error = update_ref(remote, refname.ptr, &head->oid, msg, callbacks)) < 0)
9566ce43 2015 goto cleanup;
c5837cad
CMN
2016 }
2017
e579e0f7
MB
2018 if (error != GIT_ITEROVER)
2019 goto cleanup;
2020
2021 error = 0;
9566ce43
CMN
2022
2023cleanup:
e579e0f7 2024 git_str_dispose(&refname);
9566ce43 2025 return error;
c5837cad
CMN
2026}
2027
eae0bfdc
PP
2028static int truncate_fetch_head(const char *gitdir)
2029{
e579e0f7 2030 git_str path = GIT_STR_INIT;
eae0bfdc
PP
2031 int error;
2032
e579e0f7 2033 if ((error = git_str_joinpath(&path, gitdir, GIT_FETCH_HEAD_FILE)) < 0)
eae0bfdc
PP
2034 return error;
2035
2036 error = git_futils_truncate(path.ptr, GIT_REFS_FILE_MODE);
e579e0f7 2037 git_str_dispose(&path);
eae0bfdc
PP
2038
2039 return error;
2040}
2041
c3ab1e5a
BS
2042int git_remote_update_tips(
2043 git_remote *remote,
8f0104ec 2044 const git_remote_callbacks *callbacks,
3eff2a57 2045 int update_fetchhead,
35a8a8c5 2046 git_remote_autotag_option_t download_tags,
c3ab1e5a 2047 const char *reflog_message)
4330ab26 2048{
505b5d0c 2049 git_refspec *spec, tagspec;
cdedef40 2050 git_vector refs = GIT_VECTOR_INIT;
35a8a8c5 2051 git_remote_autotag_option_t tagopt;
505b5d0c 2052 int error;
4330ab26
CMN
2053 size_t i;
2054
fe794b2e
CMN
2055 /* push has its own logic hidden away in the push object */
2056 if (remote->push) {
8f0104ec 2057 return git_push_update_tips(remote->push, callbacks);
fe794b2e
CMN
2058 }
2059
505b5d0c
CMN
2060 if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
2061 return -1;
2062
877cde76
CMN
2063
2064 if ((error = ls_to_vector(&refs, remote)) < 0)
505b5d0c
CMN
2065 goto out;
2066
c2418f46 2067 if (download_tags == GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED)
35a8a8c5
CMN
2068 tagopt = remote->download_tags;
2069 else
2070 tagopt = download_tags;
2071
eae0bfdc
PP
2072 if ((error = truncate_fetch_head(git_repository_path(remote->repo))) < 0)
2073 goto out;
2074
35a8a8c5
CMN
2075 if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
2076 if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, &tagspec, &refs, reflog_message)) < 0)
e284c451 2077 goto out;
505b5d0c 2078 }
4330ab26 2079
af613ecd 2080 git_vector_foreach(&remote->active_refspecs, i, spec) {
4330ab26
CMN
2081 if (spec->push)
2082 continue;
2083
35a8a8c5 2084 if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, spec, &refs, reflog_message)) < 0)
505b5d0c 2085 goto out;
4330ab26
CMN
2086 }
2087
e579e0f7 2088 /* Only try to do opportunistic updates if the refspec lists differ. */
c5837cad 2089 if (remote->passed_refspecs)
9566ce43 2090 error = opportunistic_updates(remote, callbacks, &refs, reflog_message);
c5837cad 2091
505b5d0c 2092out:
877cde76 2093 git_vector_free(&refs);
ac3d33df 2094 git_refspec__dispose(&tagspec);
505b5d0c 2095 return error;
4330ab26
CMN
2096}
2097
11f6ad5f 2098int git_remote_connected(const git_remote *remote)
6ac3b707 2099{
c25aa7cd 2100 GIT_ASSERT_ARG(remote);
41fb1ca0
PK
2101
2102 if (!remote->transport || !remote->transport->is_connected)
2103 return 0;
2104
2105 /* Ask the transport if it's connected. */
613d5eb9 2106 return remote->transport->is_connected(remote->transport);
6ac3b707
CMN
2107}
2108
22a2d3d5 2109int git_remote_stop(git_remote *remote)
f0d2ddbb 2110{
c25aa7cd 2111 GIT_ASSERT_ARG(remote);
613d5eb9
PK
2112
2113 if (remote->transport && remote->transport->cancel)
41fb1ca0 2114 remote->transport->cancel(remote->transport);
22a2d3d5
UG
2115
2116 return 0;
f0d2ddbb
CMN
2117}
2118
22a2d3d5 2119int git_remote_disconnect(git_remote *remote)
4cf01e9a 2120{
c25aa7cd 2121 GIT_ASSERT_ARG(remote);
4bef3565 2122
41fb1ca0
PK
2123 if (git_remote_connected(remote))
2124 remote->transport->close(remote->transport);
22a2d3d5
UG
2125
2126 return 0;
4cf01e9a
CMN
2127}
2128
e579e0f7
MB
2129static void free_heads(git_vector *heads)
2130{
2131 git_remote_head *head;
2132 size_t i;
2133
2134 git_vector_foreach(heads, i, head) {
2135 git__free(head->name);
2136 git__free(head);
2137 }
2138}
2139
9c82357b
CMN
2140void git_remote_free(git_remote *remote)
2141{
2aae2188
CMN
2142 if (remote == NULL)
2143 return;
2144
42ea35c0
MS
2145 if (remote->transport != NULL) {
2146 git_remote_disconnect(remote);
2147
2148 remote->transport->free(remote->transport);
2149 remote->transport = NULL;
2150 }
2151
2152 git_vector_free(&remote->refs);
2153
af613ecd 2154 free_refspecs(&remote->refspecs);
4330ab26
CMN
2155 git_vector_free(&remote->refspecs);
2156
af613ecd
CMN
2157 free_refspecs(&remote->active_refspecs);
2158 git_vector_free(&remote->active_refspecs);
2159
2cdd5c57
CMN
2160 free_refspecs(&remote->passive_refspecs);
2161 git_vector_free(&remote->passive_refspecs);
2162
e579e0f7
MB
2163 free_heads(&remote->local_heads);
2164 git_vector_free(&remote->local_heads);
2165
fe794b2e 2166 git_push_free(remote->push);
3286c408 2167 git__free(remote->url);
3ed4b501 2168 git__free(remote->pushurl);
3286c408 2169 git__free(remote->name);
3286c408 2170 git__free(remote);
9c82357b 2171}
8171998f 2172
106c12f1 2173static int remote_list_cb(const git_config_entry *entry, void *payload)
8171998f 2174{
25e0b157 2175 git_vector *list = payload;
106c12f1
RB
2176 const char *name = entry->name + strlen("remote.");
2177 size_t namelen = strlen(name);
2178 char *remote_name;
8171998f 2179
106c12f1 2180 /* we know name matches "remote.<stuff>.(push)?url" */
8171998f 2181
106c12f1
RB
2182 if (!strcmp(&name[namelen - 4], ".url"))
2183 remote_name = git__strndup(name, namelen - 4); /* strip ".url" */
2184 else
2185 remote_name = git__strndup(name, namelen - 8); /* strip ".pushurl" */
ac3d33df 2186 GIT_ERROR_CHECK_ALLOC(remote_name);
8171998f 2187
25e0b157 2188 return git_vector_insert(list, remote_name);
8171998f
CMN
2189}
2190
2191int git_remote_list(git_strarray *remotes_list, git_repository *repo)
2192{
8171998f 2193 int error;
96869a4e 2194 git_config *cfg;
25e0b157 2195 git_vector list = GIT_VECTOR_INIT;
8171998f 2196
96869a4e
RB
2197 if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
2198 return error;
8171998f 2199
25e0b157 2200 if ((error = git_vector_init(&list, 4, git__strcmp_cb)) < 0)
96869a4e 2201 return error;
8171998f 2202
106c12f1 2203 error = git_config_foreach_match(
25e0b157 2204 cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list);
8171998f 2205
4376f7f6 2206 if (error < 0) {
9cfce273 2207 git_vector_free_deep(&list);
8171998f
CMN
2208 return error;
2209 }
2210
25e0b157 2211 git_vector_uniq(&list, git__free);
aec87f71 2212
25e0b157
RB
2213 remotes_list->strings =
2214 (char **)git_vector_detach(&remotes_list->count, NULL, &list);
8171998f 2215
4376f7f6 2216 return 0;
8171998f 2217}
a209a025 2218
22a2d3d5 2219const git_indexer_progress *git_remote_stats(git_remote *remote)
d57c47dc 2220{
c25aa7cd 2221 GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
d57c47dc
BS
2222 return &remote->stats;
2223}
2224
11f6ad5f 2225git_remote_autotag_option_t git_remote_autotag(const git_remote *remote)
f70e466f
CMN
2226{
2227 return remote->download_tags;
2228}
2229
35a8a8c5 2230int git_remote_set_autotag(git_repository *repo, const char *remote, git_remote_autotag_option_t value)
f70e466f 2231{
e579e0f7 2232 git_str var = GIT_STR_INIT;
35a8a8c5
CMN
2233 git_config *config;
2234 int error;
2235
c25aa7cd 2236 GIT_ASSERT_ARG(repo && remote);
35a8a8c5
CMN
2237
2238 if ((error = ensure_remote_name_is_valid(remote)) < 0)
2239 return error;
2240
2241 if ((error = git_repository_config__weakptr(&config, repo)) < 0)
2242 return error;
2243
e579e0f7 2244 if ((error = git_str_printf(&var, CONFIG_TAGOPT_FMT, remote)))
35a8a8c5
CMN
2245 return error;
2246
2247 switch (value) {
2248 case GIT_REMOTE_DOWNLOAD_TAGS_NONE:
2249 error = git_config_set_string(config, var.ptr, "--no-tags");
2250 break;
2251 case GIT_REMOTE_DOWNLOAD_TAGS_ALL:
2252 error = git_config_set_string(config, var.ptr, "--tags");
2253 break;
2254 case GIT_REMOTE_DOWNLOAD_TAGS_AUTO:
2255 error = git_config_delete_entry(config, var.ptr);
2256 if (error == GIT_ENOTFOUND)
2257 error = 0;
2258 break;
2259 default:
ac3d33df 2260 git_error_set(GIT_ERROR_INVALID, "invalid value for the tagopt setting");
35a8a8c5
CMN
2261 error = -1;
2262 }
2263
e579e0f7 2264 git_str_dispose(&var);
35a8a8c5 2265 return error;
f70e466f 2266}
fcccf304 2267
5f473947
L
2268int git_remote_prune_refs(const git_remote *remote)
2269{
2270 return remote->prune_refs;
2271}
2272
fcccf304 2273static int rename_remote_config_section(
2274 git_repository *repo,
2275 const char *old_name,
2276 const char *new_name)
2277{
e579e0f7
MB
2278 git_str old_section_name = GIT_STR_INIT,
2279 new_section_name = GIT_STR_INIT;
fcccf304 2280 int error = -1;
2281
e579e0f7 2282 if (git_str_printf(&old_section_name, "remote.%s", old_name) < 0)
fcccf304 2283 goto cleanup;
2284
40e48ea4 2285 if (new_name &&
e579e0f7 2286 (git_str_printf(&new_section_name, "remote.%s", new_name) < 0))
40e48ea4 2287 goto cleanup;
fcccf304 2288
2289 error = git_config_rename_section(
2290 repo,
e579e0f7
MB
2291 git_str_cstr(&old_section_name),
2292 new_name ? git_str_cstr(&new_section_name) : NULL);
fcccf304 2293
2294cleanup:
e579e0f7
MB
2295 git_str_dispose(&old_section_name);
2296 git_str_dispose(&new_section_name);
fcccf304 2297
2298 return error;
2299}
2300
96869a4e 2301struct update_data {
fcccf304 2302 git_config *config;
2303 const char *old_remote_name;
2304 const char *new_remote_name;
2305};
2306
2307static int update_config_entries_cb(
2308 const git_config_entry *entry,
2309 void *payload)
2310{
2311 struct update_data *data = (struct update_data *)payload;
2312
2313 if (strcmp(entry->value, data->old_remote_name))
2314 return 0;
2315
25e0b157
RB
2316 return git_config_set_string(
2317 data->config, entry->name, data->new_remote_name);
fcccf304 2318}
2319
2320static int update_branch_remote_config_entry(
2321 git_repository *repo,
2322 const char *old_name,
2323 const char *new_name)
2324{
96869a4e
RB
2325 int error;
2326 struct update_data data = { NULL };
fcccf304 2327
96869a4e
RB
2328 if ((error = git_repository_config__weakptr(&data.config, repo)) < 0)
2329 return error;
fcccf304 2330
fcccf304 2331 data.old_remote_name = old_name;
2332 data.new_remote_name = new_name;
2333
25e0b157 2334 return git_config_foreach_match(
96869a4e 2335 data.config, "branch\\..+\\.remote", update_config_entries_cb, &data);
fcccf304 2336}
2337
fcccf304 2338static int rename_one_remote_reference(
d1544564 2339 git_reference *reference_in,
fcccf304 2340 const char *old_remote_name,
2341 const char *new_remote_name)
2342{
96869a4e 2343 int error;
d1544564 2344 git_reference *ref = NULL, *dummy = NULL;
e579e0f7
MB
2345 git_str namespace = GIT_STR_INIT, old_namespace = GIT_STR_INIT;
2346 git_str new_name = GIT_STR_INIT;
2347 git_str log_message = GIT_STR_INIT;
d1544564
CMN
2348 size_t pfx_len;
2349 const char *target;
fcccf304 2350
e579e0f7 2351 if ((error = git_str_printf(&namespace, GIT_REFS_REMOTES_DIR "%s/", new_remote_name)) < 0)
d1544564
CMN
2352 return error;
2353
2354 pfx_len = strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name) + 1;
e579e0f7
MB
2355 git_str_puts(&new_name, namespace.ptr);
2356 if ((error = git_str_puts(&new_name, git_reference_name(reference_in) + pfx_len)) < 0)
ccf6ce5c 2357 goto cleanup;
fcccf304 2358
e579e0f7 2359 if ((error = git_str_printf(&log_message,
ccf6ce5c
BS
2360 "renamed remote %s to %s",
2361 old_remote_name, new_remote_name)) < 0)
2362 goto cleanup;
fcccf304 2363
e579e0f7
MB
2364 if ((error = git_reference_rename(&ref, reference_in, git_str_cstr(&new_name), 1,
2365 git_str_cstr(&log_message))) < 0)
d1544564
CMN
2366 goto cleanup;
2367
ac3d33df 2368 if (git_reference_type(ref) != GIT_REFERENCE_SYMBOLIC)
d1544564
CMN
2369 goto cleanup;
2370
2371 /* Handle refs like origin/HEAD -> origin/master */
2372 target = git_reference_symbolic_target(ref);
e579e0f7 2373 if ((error = git_str_printf(&old_namespace, GIT_REFS_REMOTES_DIR "%s/", old_remote_name)) < 0)
d1544564
CMN
2374 goto cleanup;
2375
2376 if (git__prefixcmp(target, old_namespace.ptr))
2377 goto cleanup;
2378
e579e0f7
MB
2379 git_str_clear(&new_name);
2380 git_str_puts(&new_name, namespace.ptr);
2381 if ((error = git_str_puts(&new_name, target + pfx_len)) < 0)
d1544564
CMN
2382 goto cleanup;
2383
e579e0f7
MB
2384 error = git_reference_symbolic_set_target(&dummy, ref, git_str_cstr(&new_name),
2385 git_str_cstr(&log_message));
d1544564
CMN
2386
2387 git_reference_free(dummy);
ccf6ce5c
BS
2388
2389cleanup:
d1544564
CMN
2390 git_reference_free(reference_in);
2391 git_reference_free(ref);
e579e0f7
MB
2392 git_str_dispose(&namespace);
2393 git_str_dispose(&old_namespace);
2394 git_str_dispose(&new_name);
2395 git_str_dispose(&log_message);
fcccf304 2396 return error;
2397}
2398
2399static int rename_remote_references(
2400 git_repository *repo,
2401 const char *old_name,
2402 const char *new_name)
2403{
96869a4e 2404 int error;
e579e0f7 2405 git_str buf = GIT_STR_INIT;
56960b83 2406 git_reference *ref;
9bd89d96 2407 git_reference_iterator *iter;
fcccf304 2408
e579e0f7 2409 if ((error = git_str_printf(&buf, GIT_REFS_REMOTES_DIR "%s/*", old_name)) < 0)
96869a4e 2410 return error;
fcccf304 2411
e579e0f7
MB
2412 error = git_reference_iterator_glob_new(&iter, repo, git_str_cstr(&buf));
2413 git_str_dispose(&buf);
9bd89d96 2414
a52ab4b8
CMN
2415 if (error < 0)
2416 return error;
2417
2418 while ((error = git_reference_next(&ref, iter)) == 0) {
96869a4e
RB
2419 if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0)
2420 break;
9bd89d96
CMN
2421 }
2422
2423 git_reference_iterator_free(iter);
fcccf304 2424
96869a4e 2425 return (error == GIT_ITEROVER) ? 0 : error;
fcccf304 2426}
2427
72bca13e 2428static int rename_fetch_refspecs(git_vector *problems, git_remote *remote, const char *new_name)
fcccf304 2429{
2430 git_config *config;
e579e0f7 2431 git_str base = GIT_STR_INIT, var = GIT_STR_INIT, val = GIT_STR_INIT;
1be680c4 2432 const git_refspec *spec;
4330ab26 2433 size_t i;
25e0b157 2434 int error = 0;
fcccf304 2435
dab89f9b 2436 if ((error = git_repository_config__weakptr(&config, remote->repo)) < 0)
25e0b157
RB
2437 return error;
2438
72bca13e
CMN
2439 if ((error = git_vector_init(problems, 1, NULL)) < 0)
2440 return error;
2441
ac3d33df 2442 if ((error = default_fetchspec_for_name(&base, remote->name)) < 0)
25e0b157 2443 return error;
dab89f9b 2444
1be680c4 2445 git_vector_foreach(&remote->refspecs, i, spec) {
4330ab26
CMN
2446 if (spec->push)
2447 continue;
fcccf304 2448
dab89f9b 2449 /* Does the dst part of the refspec follow the expected format? */
e579e0f7 2450 if (strcmp(git_str_cstr(&base), spec->string)) {
72bca13e 2451 char *dup;
fcccf304 2452
72bca13e 2453 dup = git__strdup(spec->string);
ac3d33df 2454 GIT_ERROR_CHECK_ALLOC(dup);
72bca13e
CMN
2455
2456 if ((error = git_vector_insert(problems, dup)) < 0)
25e0b157 2457 break;
c7b3e1b3 2458
4330ab26
CMN
2459 continue;
2460 }
fcccf304 2461
4330ab26 2462 /* If we do want to move it to the new section */
fcccf304 2463
e579e0f7
MB
2464 git_str_clear(&val);
2465 git_str_clear(&var);
fcccf304 2466
ac3d33df 2467 if (default_fetchspec_for_name(&val, new_name) < 0 ||
e579e0f7 2468 git_str_printf(&var, "remote.%s.fetch", new_name) < 0)
dab89f9b
RB
2469 {
2470 error = -1;
25e0b157 2471 break;
dab89f9b 2472 }
fcccf304 2473
dab89f9b 2474 if ((error = git_config_set_string(
e579e0f7 2475 config, git_str_cstr(&var), git_str_cstr(&val))) < 0)
25e0b157 2476 break;
4330ab26 2477 }
fcccf304 2478
e579e0f7
MB
2479 git_str_dispose(&base);
2480 git_str_dispose(&var);
2481 git_str_dispose(&val);
72bca13e
CMN
2482
2483 if (error < 0) {
2484 char *str;
2485 git_vector_foreach(problems, i, str)
2486 git__free(str);
2487
2488 git_vector_free(problems);
2489 }
2490
fcccf304 2491 return error;
2492}
2493
46c8f7f8 2494int git_remote_rename(git_strarray *out, git_repository *repo, const char *name, const char *new_name)
fcccf304 2495{
2496 int error;
46c8f7f8 2497 git_vector problem_refspecs = GIT_VECTOR_INIT;
64bcf567 2498 git_remote *remote = NULL;
fcccf304 2499
c25aa7cd 2500 GIT_ASSERT_ARG(out && repo && name && new_name);
fcccf304 2501
209425ce 2502 if ((error = git_remote_lookup(&remote, repo, name)) < 0)
cce27d82 2503 return error;
79000951 2504
fcccf304 2505 if ((error = ensure_remote_name_is_valid(new_name)) < 0)
46c8f7f8 2506 goto cleanup;
fcccf304 2507
46c8f7f8
CMN
2508 if ((error = ensure_remote_doesnot_exist(repo, new_name)) < 0)
2509 goto cleanup;
fcccf304 2510
46c8f7f8
CMN
2511 if ((error = rename_remote_config_section(repo, name, new_name)) < 0)
2512 goto cleanup;
fcccf304 2513
46c8f7f8
CMN
2514 if ((error = update_branch_remote_config_entry(repo, name, new_name)) < 0)
2515 goto cleanup;
fcccf304 2516
46c8f7f8
CMN
2517 if ((error = rename_remote_references(repo, name, new_name)) < 0)
2518 goto cleanup;
fcccf304 2519
72bca13e 2520 if ((error = rename_fetch_refspecs(&problem_refspecs, remote, new_name)) < 0)
46c8f7f8 2521 goto cleanup;
fcccf304 2522
72bca13e
CMN
2523 out->count = problem_refspecs.length;
2524 out->strings = (char **) problem_refspecs.contents;
2525
46c8f7f8
CMN
2526cleanup:
2527 if (error < 0)
2528 git_vector_free(&problem_refspecs);
fcccf304 2529
46c8f7f8
CMN
2530 git_remote_free(remote);
2531 return error;
fcccf304 2532}
b0f6e45d 2533
c25aa7cd 2534int git_remote_name_is_valid(int *valid, const char *remote_name)
2bca5b67 2535{
e579e0f7 2536 git_str buf = GIT_STR_INIT;
c25aa7cd
PP
2537 git_refspec refspec = {0};
2538 int error;
2539
2540 GIT_ASSERT(valid);
2541
2542 *valid = 0;
2bca5b67 2543
2544 if (!remote_name || *remote_name == '\0')
2545 return 0;
2546
e579e0f7 2547 if ((error = git_str_printf(&buf, "refs/heads/test:refs/remotes/%s/test", remote_name)) < 0)
c25aa7cd
PP
2548 goto done;
2549
e579e0f7 2550 error = git_refspec__parse(&refspec, git_str_cstr(&buf), true);
2bca5b67 2551
c25aa7cd
PP
2552 if (!error)
2553 *valid = 1;
2554 else if (error == GIT_EINVALIDSPEC)
2555 error = 0;
2556
2557done:
e579e0f7 2558 git_str_dispose(&buf);
ac3d33df 2559 git_refspec__dispose(&refspec);
2bca5b67 2560
c25aa7cd 2561 return error;
2bca5b67 2562}
4330ab26
CMN
2563
2564git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname)
2565{
2566 git_refspec *spec;
2567 size_t i;
2568
af613ecd 2569 git_vector_foreach(&remote->active_refspecs, i, spec) {
4330ab26
CMN
2570 if (spec->push)
2571 continue;
2572
2573 if (git_refspec_src_matches(spec, refname))
2574 return spec;
2575 }
2576
2577 return NULL;
2578}
2579
2580git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname)
2581{
2582 git_refspec *spec;
2583 size_t i;
2584
af613ecd 2585 git_vector_foreach(&remote->active_refspecs, i, spec) {
4330ab26
CMN
2586 if (spec->push)
2587 continue;
2588
2589 if (git_refspec_dst_matches(spec, refname))
2590 return spec;
2591 }
2592
2593 return NULL;
2594}
2595
77254990 2596int git_remote_add_fetch(git_repository *repo, const char *remote, const char *refspec)
4330ab26 2597{
77254990 2598 return write_add_refspec(repo, remote, refspec, true);
4330ab26
CMN
2599}
2600
77254990 2601int git_remote_add_push(git_repository *repo, const char *remote, const char *refspec)
4330ab26 2602{
77254990 2603 return write_add_refspec(repo, remote, refspec, false);
266af6d8
CMN
2604}
2605
11f6ad5f 2606static int copy_refspecs(git_strarray *array, const git_remote *remote, unsigned int push)
bc6374ea
CMN
2607{
2608 size_t i;
2609 git_vector refspecs;
2610 git_refspec *spec;
2611 char *dup;
2612
2613 if (git_vector_init(&refspecs, remote->refspecs.length, NULL) < 0)
2614 return -1;
2615
2616 git_vector_foreach(&remote->refspecs, i, spec) {
2617 if (spec->push != push)
2618 continue;
2619
1be680c4 2620 if ((dup = git__strdup(spec->string)) == NULL)
bc6374ea 2621 goto on_error;
bc6374ea
CMN
2622
2623 if (git_vector_insert(&refspecs, dup) < 0) {
2624 git__free(dup);
2625 goto on_error;
2626 }
2627 }
2628
2629 array->strings = (char **)refspecs.contents;
2630 array->count = refspecs.length;
2631
2632 return 0;
2633
2634on_error:
9cfce273 2635 git_vector_free_deep(&refspecs);
bc6374ea
CMN
2636
2637 return -1;
2638}
2639
11f6ad5f 2640int git_remote_get_fetch_refspecs(git_strarray *array, const git_remote *remote)
bc6374ea
CMN
2641{
2642 return copy_refspecs(array, remote, false);
2643}
2644
11f6ad5f 2645int git_remote_get_push_refspecs(git_strarray *array, const git_remote *remote)
bc6374ea
CMN
2646{
2647 return copy_refspecs(array, remote, true);
2648}
1ffd0806 2649
11f6ad5f 2650size_t git_remote_refspec_count(const git_remote *remote)
1ffd0806
CMN
2651{
2652 return remote->refspecs.length;
2653}
2654
11f6ad5f 2655const git_refspec *git_remote_get_refspec(const git_remote *remote, size_t n)
1ffd0806
CMN
2656{
2657 return git_vector_get(&remote->refspecs, n);
2658}
b9f81997 2659
bc91347b 2660int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version)
b9f81997 2661{
bc91347b
RB
2662 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
2663 opts, version, git_remote_callbacks, GIT_REMOTE_CALLBACKS_INIT);
2664 return 0;
b9f81997 2665}
40e48ea4 2666
5cdac19c
CMN
2667/* asserts a branch.<foo>.remote format */
2668static const char *name_offset(size_t *len_out, const char *name)
40e48ea4 2669{
5cdac19c
CMN
2670 size_t prefix_len;
2671 const char *dot;
40e48ea4 2672
5cdac19c
CMN
2673 prefix_len = strlen("remote.");
2674 dot = strchr(name + prefix_len, '.');
40e48ea4 2675
c25aa7cd 2676 GIT_ASSERT_ARG_WITH_RETVAL(dot, NULL);
40e48ea4 2677
5cdac19c
CMN
2678 *len_out = dot - name - prefix_len;
2679 return name + prefix_len;
40e48ea4 2680}
2681
2682static int remove_branch_config_related_entries(
2683 git_repository *repo,
2684 const char *remote_name)
2685{
2686 int error;
2687 git_config *config;
5cdac19c
CMN
2688 git_config_entry *entry;
2689 git_config_iterator *iter;
e579e0f7 2690 git_str buf = GIT_STR_INIT;
40e48ea4 2691
2692 if ((error = git_repository_config__weakptr(&config, repo)) < 0)
2693 return error;
2694
5cdac19c 2695 if ((error = git_config_iterator_glob_new(&iter, config, "branch\\..+\\.remote")) < 0)
40e48ea4 2696 return error;
2697
5cdac19c
CMN
2698 /* find any branches with us as upstream and remove that config */
2699 while ((error = git_config_next(&entry, iter)) == 0) {
2700 const char *branch;
2701 size_t branch_len;
40e48ea4 2702
5cdac19c
CMN
2703 if (strcmp(remote_name, entry->value))
2704 continue;
40e48ea4 2705
c25aa7cd
PP
2706 if ((branch = name_offset(&branch_len, entry->name)) == NULL) {
2707 error = -1;
2708 break;
2709 }
40e48ea4 2710
e579e0f7
MB
2711 git_str_clear(&buf);
2712 if ((error = git_str_printf(&buf, "branch.%.*s.merge", (int)branch_len, branch)) < 0)
5cdac19c
CMN
2713 break;
2714
e579e0f7 2715 if ((error = git_config_delete_entry(config, git_str_cstr(&buf))) < 0) {
d81cb2e4
DT
2716 if (error != GIT_ENOTFOUND)
2717 break;
ac3d33df 2718 git_error_clear();
d81cb2e4 2719 }
5cdac19c 2720
e579e0f7
MB
2721 git_str_clear(&buf);
2722 if ((error = git_str_printf(&buf, "branch.%.*s.remote", (int)branch_len, branch)) < 0)
5cdac19c
CMN
2723 break;
2724
e579e0f7 2725 if ((error = git_config_delete_entry(config, git_str_cstr(&buf))) < 0) {
d81cb2e4
DT
2726 if (error != GIT_ENOTFOUND)
2727 break;
ac3d33df 2728 git_error_clear();
d81cb2e4 2729 }
40e48ea4 2730 }
2731
5cdac19c
CMN
2732 if (error == GIT_ITEROVER)
2733 error = 0;
2734
e579e0f7 2735 git_str_dispose(&buf);
5cdac19c 2736 git_config_iterator_free(iter);
40e48ea4 2737 return error;
2738}
2739
8a9419aa 2740static int remove_refs(git_repository *repo, const git_refspec *spec)
ec8a949a 2741{
8a9419aa
CMN
2742 git_reference_iterator *iter = NULL;
2743 git_vector refs;
ec8a949a 2744 const char *name;
8a9419aa 2745 char *dup;
ec8a949a 2746 int error;
8a9419aa 2747 size_t i;
ec8a949a 2748
8a9419aa 2749 if ((error = git_vector_init(&refs, 8, NULL)) < 0)
ec8a949a
CMN
2750 return error;
2751
8a9419aa
CMN
2752 if ((error = git_reference_iterator_new(&iter, repo)) < 0)
2753 goto cleanup;
2754
ec8a949a 2755 while ((error = git_reference_next_name(&name, iter)) == 0) {
8a9419aa
CMN
2756 if (!git_refspec_dst_matches(spec, name))
2757 continue;
2758
2759 dup = git__strdup(name);
2760 if (!dup) {
2761 error = -1;
2762 goto cleanup;
2763 }
ec8a949a 2764
8a9419aa
CMN
2765 if ((error = git_vector_insert(&refs, dup)) < 0)
2766 goto cleanup;
2767 }
ec8a949a
CMN
2768 if (error == GIT_ITEROVER)
2769 error = 0;
8a9419aa
CMN
2770 if (error < 0)
2771 goto cleanup;
2772
2773 git_vector_foreach(&refs, i, name) {
2774 if ((error = git_reference_remove(repo, name)) < 0)
2775 break;
2776 }
ec8a949a 2777
8a9419aa
CMN
2778cleanup:
2779 git_reference_iterator_free(iter);
2780 git_vector_foreach(&refs, i, dup) {
2781 git__free(dup);
2782 }
2783 git_vector_free(&refs);
ec8a949a
CMN
2784 return error;
2785}
2786
2787static int remove_remote_tracking(git_repository *repo, const char *remote_name)
2788{
2789 git_remote *remote;
2790 int error;
2791 size_t i, count;
2792
2793 /* we want to use what's on the config, regardless of changes to the instance in memory */
209425ce 2794 if ((error = git_remote_lookup(&remote, repo, remote_name)) < 0)
ec8a949a
CMN
2795 return error;
2796
2797 count = git_remote_refspec_count(remote);
2798 for (i = 0; i < count; i++) {
2799 const git_refspec *refspec = git_remote_get_refspec(remote, i);
2800
2801 /* shouldn't ever actually happen */
2802 if (refspec == NULL)
2803 continue;
2804
8a9419aa 2805 if ((error = remove_refs(repo, refspec)) < 0)
ec8a949a
CMN
2806 break;
2807 }
2808
2809 git_remote_free(remote);
2810 return error;
2811}
2812
262eec23 2813int git_remote_delete(git_repository *repo, const char *name)
40e48ea4 2814{
2815 int error;
40e48ea4 2816
c25aa7cd
PP
2817 GIT_ASSERT_ARG(repo);
2818 GIT_ASSERT_ARG(name);
ec8a949a 2819
262eec23
CMN
2820 if ((error = remove_branch_config_related_entries(repo, name)) < 0 ||
2821 (error = remove_remote_tracking(repo, name)) < 0 ||
2822 (error = rename_remote_config_section(repo, name, NULL)) < 0)
ec8a949a
CMN
2823 return error;
2824
40e48ea4 2825 return 0;
2826}
d22db24f
CMN
2827
2828int git_remote_default_branch(git_buf *out, git_remote *remote)
e579e0f7
MB
2829{
2830 GIT_BUF_WRAP_PRIVATE(out, git_remote__default_branch, remote);
2831}
2832
2833int git_remote__default_branch(git_str *out, git_remote *remote)
d22db24f
CMN
2834{
2835 const git_remote_head **heads;
2836 const git_remote_head *guess = NULL;
2837 const git_oid *head_id;
2838 size_t heads_len, i;
e579e0f7 2839 git_str local_default = GIT_STR_INIT;
d22db24f
CMN
2840 int error;
2841
c25aa7cd 2842 GIT_ASSERT_ARG(out);
dc8adda4 2843
d22db24f 2844 if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0)
22a2d3d5 2845 goto done;
d22db24f 2846
22a2d3d5
UG
2847 if (heads_len == 0 || strcmp(heads[0]->name, GIT_HEAD_FILE)) {
2848 error = GIT_ENOTFOUND;
2849 goto done;
2850 }
0cdaa376 2851
d22db24f 2852 /* the first one must be HEAD so if that has the symref info, we're done */
22a2d3d5 2853 if (heads[0]->symref_target) {
e579e0f7 2854 error = git_str_puts(out, heads[0]->symref_target);
22a2d3d5
UG
2855 goto done;
2856 }
d22db24f
CMN
2857
2858 /*
2859 * If there's no symref information, we have to look over them
22a2d3d5
UG
2860 * and guess. We return the first match unless the default
2861 * branch is a candidate. Then we return the default branch.
d22db24f 2862 */
22a2d3d5
UG
2863
2864 if ((error = git_repository_initialbranch(&local_default, remote->repo)) < 0)
2865 goto done;
2866
d22db24f
CMN
2867 head_id = &heads[0]->oid;
2868
2869 for (i = 1; i < heads_len; i++) {
2870 if (git_oid_cmp(head_id, &heads[i]->oid))
2871 continue;
2872
38952604
CMN
2873 if (git__prefixcmp(heads[i]->name, GIT_REFS_HEADS_DIR))
2874 continue;
2875
d22db24f
CMN
2876 if (!guess) {
2877 guess = heads[i];
2878 continue;
2879 }
2880
22a2d3d5 2881 if (!git__strcmp(local_default.ptr, heads[i]->name)) {
d22db24f
CMN
2882 guess = heads[i];
2883 break;
2884 }
2885 }
2886
22a2d3d5
UG
2887 if (!guess) {
2888 error = GIT_ENOTFOUND;
2889 goto done;
2890 }
d22db24f 2891
e579e0f7 2892 error = git_str_puts(out, guess->name);
22a2d3d5
UG
2893
2894done:
e579e0f7 2895 git_str_dispose(&local_default);
22a2d3d5 2896 return error;
d22db24f 2897}
3149547b 2898
e579e0f7
MB
2899GIT_INLINE(int) connect_opts_from_push_opts(
2900 git_remote_connect_options *out,
2901 git_remote *remote,
2902 const git_push_options *push_opts)
3149547b 2903{
e579e0f7
MB
2904 git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
2905 copy_opts(&tmp, push_opts);
2906 return git_remote_connect_options_normalize(out, remote->repo, &tmp);
2907}
2908
2909int git_remote_upload(
2910 git_remote *remote,
2911 const git_strarray *refspecs,
2912 const git_push_options *opts)
2913{
2914 git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
fe794b2e 2915 git_push *push;
64e3e6d4 2916 git_refspec *spec;
e579e0f7
MB
2917 size_t i;
2918 int error;
3149547b 2919
c25aa7cd 2920 GIT_ASSERT_ARG(remote);
3149547b 2921
eae0bfdc 2922 if (!remote->repo) {
ac3d33df 2923 git_error_set(GIT_ERROR_INVALID, "cannot download detached remote");
eae0bfdc
PP
2924 return -1;
2925 }
2926
e579e0f7
MB
2927 if ((error = connect_opts_from_push_opts(&connect_opts, remote, opts)) < 0)
2928 goto cleanup;
8f0104ec 2929
e579e0f7 2930 if ((error = connect_or_reset_options(remote, GIT_DIRECTION_PUSH, &connect_opts)) < 0)
fe794b2e
CMN
2931 goto cleanup;
2932
1034f1b5 2933 free_refspecs(&remote->active_refspecs);
69f0032b 2934 if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
1034f1b5
POL
2935 goto cleanup;
2936
fe794b2e
CMN
2937 if (remote->push) {
2938 git_push_free(remote->push);
2939 remote->push = NULL;
2940 }
2941
e579e0f7
MB
2942 if ((error = git_push_new(&remote->push, remote, opts)) < 0)
2943 goto cleanup;
3149547b 2944
fe794b2e 2945 push = remote->push;
3149547b 2946
64e3e6d4
CMN
2947 if (refspecs && refspecs->count > 0) {
2948 for (i = 0; i < refspecs->count; i++) {
2949 if ((error = git_push_add_refspec(push, refspecs->strings[i])) < 0)
2950 goto cleanup;
2951 }
2952 } else {
2953 git_vector_foreach(&remote->refspecs, i, spec) {
2954 if (!spec->push)
2955 continue;
2956 if ((error = git_push_add_refspec(push, spec->string)) < 0)
2957 goto cleanup;
2958 }
3149547b
CMN
2959 }
2960
e579e0f7 2961 if ((error = git_push_finish(push)) < 0)
3149547b
CMN
2962 goto cleanup;
2963
e579e0f7
MB
2964 if (connect_opts.callbacks.push_update_reference &&
2965 (error = git_push_status_foreach(push, connect_opts.callbacks.push_update_reference, connect_opts.callbacks.payload)) < 0)
3149547b
CMN
2966 goto cleanup;
2967
3149547b 2968cleanup:
e579e0f7 2969 git_remote_connect_options_dispose(&connect_opts);
fe794b2e
CMN
2970 return error;
2971}
2972
e579e0f7
MB
2973int git_remote_push(
2974 git_remote *remote,
2975 const git_strarray *refspecs,
2976 const git_push_options *opts)
fe794b2e 2977{
e579e0f7 2978 git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
fe794b2e 2979 int error;
8f0104ec 2980
c25aa7cd 2981 GIT_ASSERT_ARG(remote);
eae0bfdc
PP
2982
2983 if (!remote->repo) {
ac3d33df 2984 git_error_set(GIT_ERROR_INVALID, "cannot download detached remote");
eae0bfdc
PP
2985 return -1;
2986 }
2987
e579e0f7
MB
2988 if (connect_opts_from_push_opts(&connect_opts, remote, opts) < 0)
2989 return -1;
fe794b2e
CMN
2990
2991 if ((error = git_remote_upload(remote, refspecs, opts)) < 0)
e579e0f7 2992 goto done;
fe794b2e 2993
e579e0f7 2994 error = git_remote_update_tips(remote, &connect_opts.callbacks, 0, 0, NULL);
fe794b2e 2995
e579e0f7 2996done:
3149547b 2997 git_remote_disconnect(remote);
e579e0f7 2998 git_remote_connect_options_dispose(&connect_opts);
3149547b
CMN
2999 return error;
3000}
ec0c4c40
PS
3001
3002#define PREFIX "url"
3003#define SUFFIX_FETCH "insteadof"
3004#define SUFFIX_PUSH "pushinsteadof"
3005
e579e0f7 3006static int apply_insteadof(char **out, git_config *config, const char *url, int direction, bool use_default_if_empty)
ec0c4c40
PS
3007{
3008 size_t match_length, prefix_length, suffix_length;
3009 char *replacement = NULL;
3010 const char *regexp;
3011
e579e0f7 3012 git_str result = GIT_STR_INIT;
ec0c4c40
PS
3013 git_config_entry *entry;
3014 git_config_iterator *iter;
3015
e579e0f7
MB
3016 GIT_ASSERT_ARG(out);
3017 GIT_ASSERT_ARG(config);
3018 GIT_ASSERT_ARG(url);
3019 GIT_ASSERT_ARG(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
ec0c4c40
PS
3020
3021 /* Add 1 to prefix/suffix length due to the additional escaped dot */
3022 prefix_length = strlen(PREFIX) + 1;
3023 if (direction == GIT_DIRECTION_FETCH) {
3024 regexp = PREFIX "\\..*\\." SUFFIX_FETCH;
3025 suffix_length = strlen(SUFFIX_FETCH) + 1;
3026 } else {
3027 regexp = PREFIX "\\..*\\." SUFFIX_PUSH;
3028 suffix_length = strlen(SUFFIX_PUSH) + 1;
3029 }
3030
2785544f 3031 if (git_config_iterator_glob_new(&iter, config, regexp) < 0)
e579e0f7 3032 return -1;
ec0c4c40
PS
3033
3034 match_length = 0;
3035 while (git_config_next(&entry, iter) == 0) {
3036 size_t n, replacement_length;
3037
3038 /* Check if entry value is a prefix of URL */
3039 if (git__prefixcmp(url, entry->value))
3040 continue;
e579e0f7 3041
ec0c4c40
PS
3042 /* Check if entry value is longer than previous
3043 * prefixes */
3044 if ((n = strlen(entry->value)) <= match_length)
3045 continue;
3046
3047 git__free(replacement);
3048 match_length = n;
3049
3050 /* Cut off prefix and suffix of the value */
3051 replacement_length =
3052 strlen(entry->name) - (prefix_length + suffix_length);
3053 replacement = git__strndup(entry->name + prefix_length,
3054 replacement_length);
3055 }
3056
3057 git_config_iterator_free(iter);
3058
e579e0f7
MB
3059 if (match_length == 0 && use_default_if_empty) {
3060 *out = git__strdup(url);
3061 return *out ? 0 : -1;
3062 } else if (match_length == 0) {
3063 *out = NULL;
3064 return 0;
3065 }
ec0c4c40 3066
e579e0f7 3067 git_str_printf(&result, "%s%s", replacement, url + match_length);
ec0c4c40
PS
3068
3069 git__free(replacement);
3070
e579e0f7
MB
3071 *out = git_str_detach(&result);
3072 return 0;
ec0c4c40 3073}
c25aa7cd
PP
3074
3075/* Deprecated functions */
3076
3077#ifndef GIT_DEPRECATE_HARD
3078
3079int git_remote_is_valid_name(const char *remote_name)
3080{
3081 int valid = 0;
3082
3083 git_remote_name_is_valid(&valid, remote_name);
3084 return valid;
3085}
3086
3087#endif