]> git.proxmox.com Git - libgit2.git/blame - src/remote.c
index: fix contradicting comparison
[libgit2.git] / src / 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
9c82357b
CMN
8#include "git2/config.h"
9#include "git2/types.h"
c07d9c95 10#include "git2/oid.h"
1ffd0806 11#include "git2/net.h"
9c82357b 12
157cef10 13#include "common.h"
9c82357b
CMN
14#include "config.h"
15#include "repository.h"
16#include "remote.h"
e1d88030 17#include "fetch.h"
441f57c2 18#include "refs.h"
b0f6e45d
ET
19#include "refspec.h"
20#include "fetchhead.h"
fe794b2e 21#include "push.h"
9c82357b 22
22261344
CMN
23#define CONFIG_URL_FMT "remote.%s.url"
24#define CONFIG_PUSHURL_FMT "remote.%s.pushurl"
25#define CONFIG_FETCH_FMT "remote.%s.fetch"
77254990 26#define CONFIG_PUSH_FMT "remote.%s.push"
35a8a8c5 27#define CONFIG_TAGOPT_FMT "remote.%s.tagopt"
22261344 28
af613ecd 29static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs);
1ef3f0ce 30static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name);
ec0c4c40 31char *apply_insteadof(git_config *config, const char *url, int direction);
af613ecd 32
3f894205 33static int add_refspec_to(git_vector *vector, const char *string, bool is_fetch)
9c82357b 34{
4330ab26 35 git_refspec *spec;
9c82357b 36
4330ab26
CMN
37 spec = git__calloc(1, sizeof(git_refspec));
38 GITERR_CHECK_ALLOC(spec);
39
1be680c4
CMN
40 if (git_refspec__parse(spec, string, is_fetch) < 0) {
41 git__free(spec);
42 return -1;
43 }
4330ab26
CMN
44
45 spec->push = !is_fetch;
3f894205 46 if (git_vector_insert(vector, spec) < 0) {
1be680c4
CMN
47 git_refspec__free(spec);
48 git__free(spec);
49 return -1;
50 }
9c82357b 51
4330ab26 52 return 0;
9c82357b
CMN
53}
54
3f894205
CMN
55static int add_refspec(git_remote *remote, const char *string, bool is_fetch)
56{
57 return add_refspec_to(&remote->refspecs, string, is_fetch);
58}
59
24f2f94e
CMN
60static int download_tags_value(git_remote *remote, git_config *cfg)
61{
9a97f49e 62 git_config_entry *ce;
24f2f94e
CMN
63 git_buf buf = GIT_BUF_INIT;
64 int error;
65
24f2f94e
CMN
66 if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0)
67 return -1;
68
9f77b3f6 69 error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false);
24f2f94e 70 git_buf_free(&buf);
24f2f94e 71
9f77b3f6
RB
72 if (!error && ce && ce->value) {
73 if (!strcmp(ce->value, "--no-tags"))
74 remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
75 else if (!strcmp(ce->value, "--tags"))
76 remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
bf6bebe2 77 }
24f2f94e 78
9a97f49e 79 git_config_entry_free(ce);
24f2f94e
CMN
80 return error;
81}
82
032ba9e4 83static int ensure_remote_name_is_valid(const char *name)
84{
2bca5b67 85 int error = 0;
032ba9e4 86
2bca5b67 87 if (!git_remote_is_valid_name(name)) {
032ba9e4 88 giterr_set(
89 GITERR_CONFIG,
183aa4f8 90 "'%s' is not a valid remote name.", name ? name : "(null)");
032ba9e4 91 error = GIT_EINVALIDSPEC;
92 }
93
94 return error;
95}
96
77254990
CMN
97static int write_add_refspec(git_repository *repo, const char *name, const char *refspec, bool fetch)
98{
99 git_config *cfg;
100 git_buf var = GIT_BUF_INIT;
c6e942fb 101 git_refspec spec;
77254990
CMN
102 const char *fmt;
103 int error;
104
105 if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
106 return error;
107
108 fmt = fetch ? CONFIG_FETCH_FMT : CONFIG_PUSH_FMT;
109
110 if ((error = ensure_remote_name_is_valid(name)) < 0)
111 return error;
112
c6e942fb
CMN
113 if ((error = git_refspec__parse(&spec, refspec, fetch)) < 0) {
114 if (giterr_last()->klass != GITERR_NOMEMORY)
115 error = GIT_EINVALIDSPEC;
116
117 return error;
118 }
119
120 git_refspec__free(&spec);
121
77254990
CMN
122 if ((error = git_buf_printf(&var, fmt, name)) < 0)
123 return error;
124
125 /*
126 * "$^" is a unmatcheable regexp: it will not match anything at all, so
127 * all values will be considered new and we will not replace any
128 * present value.
129 */
130 if ((error = git_config_set_multivar(cfg, var.ptr, "$^", refspec)) < 0) {
131 goto cleanup;
132 }
133
134cleanup:
135 git_buf_free(&var);
136 return 0;
137}
138
41698f22
CMN
139#if 0
140/* We could export this as a helper */
80fc7d6b 141static int get_check_cert(int *out, git_repository *repo)
af6dab7e
PK
142{
143 git_config *cfg;
144 const char *val;
80fc7d6b
ET
145 int error = 0;
146
147 assert(out && repo);
af6dab7e 148
80fc7d6b
ET
149 /* By default, we *DO* want to verify the certificate. */
150 *out = 1;
af6dab7e
PK
151
152 /* Go through the possible sources for SSL verification settings, from
153 * most specific to least specific. */
154
155 /* GIT_SSL_NO_VERIFY environment variable */
e069c621 156 if ((val = p_getenv("GIT_SSL_NO_VERIFY")) != NULL)
80fc7d6b 157 return git_config_parse_bool(out, val);
af6dab7e
PK
158
159 /* http.sslVerify config setting */
80fc7d6b
ET
160 if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
161 return error;
af6dab7e 162
9f77b3f6 163 *out = git_config__get_bool_force(cfg, "http.sslverify", 1);
80fc7d6b 164 return 0;
af6dab7e 165}
41698f22 166#endif
af6dab7e 167
12f32d91
ET
168static int canonicalize_url(git_buf *out, const char *in)
169{
47a40d1d
CMN
170 if (in == NULL || strlen(in) == 0) {
171 giterr_set(GITERR_INVALID, "cannot set empty URL");
172 return GIT_EINVALIDSPEC;
173 }
12f32d91 174
47a40d1d 175#ifdef GIT_WIN32
12f32d91
ET
176 /* Given a UNC path like \\server\path, we need to convert this
177 * to //server/path for compatibility with core git.
178 */
179 if (in[0] == '\\' && in[1] == '\\' &&
180 (git__isalpha(in[2]) || git__isdigit(in[2]))) {
47a40d1d 181 const char *c;
12f32d91
ET
182 for (c = in; *c; c++)
183 git_buf_putc(out, *c == '\\' ? '/' : *c);
184
185 return git_buf_oom(out) ? -1 : 0;
186 }
187#endif
188
189 return git_buf_puts(out, in);
190}
191
874dcb25 192static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
778e1c73
CMN
193{
194 git_remote *remote;
a8846da7 195 git_config *config = NULL;
22261344
CMN
196 git_buf canonical_url = GIT_BUF_INIT;
197 git_buf var = GIT_BUF_INIT;
44f36f6e 198 int error = -1;
778e1c73 199
4bef3565 200 /* name is optional */
874dcb25 201 assert(out && repo && url);
617bfdf4 202
22261344
CMN
203 if ((error = git_repository_config__weakptr(&config, repo)) < 0)
204 return error;
205
59bccf33 206 remote = git__calloc(1, sizeof(git_remote));
4376f7f6 207 GITERR_CHECK_ALLOC(remote);
778e1c73 208
778e1c73 209 remote->repo = repo;
617bfdf4 210
12f32d91
ET
211 if (git_vector_init(&remote->refs, 32, NULL) < 0 ||
212 canonicalize_url(&canonical_url, url) < 0)
44f36f6e 213 goto on_error;
d88d4311 214
ec0c4c40 215 remote->url = apply_insteadof(repo->_config, canonical_url.ptr, GIT_DIRECTION_FETCH);
778e1c73 216
617bfdf4
CMN
217 if (name != NULL) {
218 remote->name = git__strdup(name);
4376f7f6 219 GITERR_CHECK_ALLOC(remote->name);
22261344
CMN
220
221 if ((error = git_buf_printf(&var, CONFIG_URL_FMT, name)) < 0)
222 goto on_error;
223
ec0c4c40 224 if ((error = git_config_set_string(config, var.ptr, canonical_url.ptr)) < 0)
22261344 225 goto on_error;
617bfdf4
CMN
226 }
227
baaa8a44 228 if (fetch != NULL) {
77254990
CMN
229 if ((error = add_refspec(remote, fetch, true)) < 0)
230 goto on_error;
231
232 /* only write for non-anonymous remotes */
233 if (name && (error = write_add_refspec(repo, name, fetch, true)) < 0)
baaa8a44 234 goto on_error;
d3cd7da5 235
1ef3f0ce
DC
236 if ((error = git_repository_config_snapshot(&config, repo)) < 0)
237 goto on_error;
238
69f0032b 239 if ((error = lookup_remote_prune_config(remote, config, name)) < 0)
1ef3f0ce
DC
240 goto on_error;
241
d3cd7da5 242 /* Move the data over to where the matching functions can find them */
69f0032b 243 if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
d3cd7da5 244 goto on_error;
baaa8a44
CMN
245 }
246
35a8a8c5 247 /* A remote without a name doesn't download tags */
215af2cc 248 if (!name)
c648d4a8 249 remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
35a8a8c5
CMN
250 else
251 remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
252
c648d4a8 253
22261344
CMN
254 git_buf_free(&var);
255
778e1c73 256 *out = remote;
a8846da7 257 error = 0;
baaa8a44
CMN
258
259on_error:
a8846da7
ET
260 if (error)
261 git_remote_free(remote);
262
1ef3f0ce 263 git_config_free(config);
12f32d91 264 git_buf_free(&canonical_url);
22261344 265 git_buf_free(&var);
44f36f6e 266 return error;
778e1c73
CMN
267}
268
592f466c
BS
269static int ensure_remote_doesnot_exist(git_repository *repo, const char *name)
270{
271 int error;
272 git_remote *remote;
273
209425ce 274 error = git_remote_lookup(&remote, repo, name);
592f466c
BS
275
276 if (error == GIT_ENOTFOUND)
277 return 0;
278
279 if (error < 0)
280 return error;
281
282 git_remote_free(remote);
283
284 giterr_set(
285 GITERR_CONFIG,
286 "Remote '%s' already exists.", name);
287
288 return GIT_EEXISTS;
289}
290
f19304d2 291
874dcb25
BS
292int git_remote_create(git_remote **out, git_repository *repo, const char *name, const char *url)
293{
294 git_buf buf = GIT_BUF_INIT;
295 int error;
296
874dcb25
BS
297 if (git_buf_printf(&buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0)
298 return -1;
299
a68e217f 300 error = git_remote_create_with_fetchspec(out, repo, name, url, git_buf_cstr(&buf));
874dcb25
BS
301 git_buf_free(&buf);
302
a68e217f 303 return error;
874dcb25
BS
304}
305
40b99d05
VG
306int git_remote_create_with_fetchspec(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
307{
40b99d05
VG
308 git_remote *remote = NULL;
309 int error;
310
311 if ((error = ensure_remote_name_is_valid(name)) < 0)
312 return error;
313
314 if ((error = ensure_remote_doesnot_exist(repo, name)) < 0)
315 return error;
874dcb25 316
0fe522d1 317 if (create_internal(&remote, repo, name, url, fetch) < 0)
874dcb25
BS
318 goto on_error;
319
c5193e3c 320 *out = remote;
321
874dcb25
BS
322 return 0;
323
324on_error:
c5193e3c 325 git_remote_free(remote);
874dcb25
BS
326 return -1;
327}
328
ae5b9362 329int git_remote_create_anonymous(git_remote **out, git_repository *repo, const char *url)
874dcb25 330{
ae5b9362 331 return create_internal(out, repo, NULL, url, NULL);
874dcb25
BS
332}
333
991b2840 334int git_remote_dup(git_remote **dest, git_remote *source)
40ef47dd 335{
70f7484d 336 size_t i;
991b2840 337 int error = 0;
70f7484d 338 git_refspec *spec;
40ef47dd
AS
339 git_remote *remote = git__calloc(1, sizeof(git_remote));
340 GITERR_CHECK_ALLOC(remote);
341
40ef47dd
AS
342 if (source->name != NULL) {
343 remote->name = git__strdup(source->name);
344 GITERR_CHECK_ALLOC(remote->name);
345 }
346
347 if (source->url != NULL) {
348 remote->url = git__strdup(source->url);
ec0c4c40 349 GITERR_CHECK_ALLOC(remote->url);
40ef47dd
AS
350 }
351
352 if (source->pushurl != NULL) {
353 remote->pushurl = git__strdup(source->pushurl);
1697cd6f 354 GITERR_CHECK_ALLOC(remote->pushurl);
40ef47dd
AS
355 }
356
357 remote->repo = source->repo;
40ef47dd 358 remote->download_tags = source->download_tags;
5f473947 359 remote->prune_refs = source->prune_refs;
40ef47dd 360
991b2840
AS
361 if (git_vector_init(&remote->refs, 32, NULL) < 0 ||
362 git_vector_init(&remote->refspecs, 2, NULL) < 0 ||
363 git_vector_init(&remote->active_refspecs, 2, NULL) < 0) {
364 error = -1;
365 goto cleanup;
40ef47dd
AS
366 }
367
70f7484d
CMN
368 git_vector_foreach(&source->refspecs, i, spec) {
369 if ((error = add_refspec(remote, spec->string, !spec->push)) < 0)
370 goto cleanup;
371 }
991b2840 372
40ef47dd
AS
373 *dest = remote;
374
991b2840
AS
375cleanup:
376
991b2840
AS
377 if (error < 0)
378 git__free(remote);
379
380 return error;
40ef47dd
AS
381}
382
4330ab26
CMN
383struct refspec_cb_data {
384 git_remote *remote;
385 int fetch;
386};
387
388static int refspec_cb(const git_config_entry *entry, void *payload)
389{
96869a4e 390 struct refspec_cb_data *data = (struct refspec_cb_data *)payload;
25e0b157 391 return add_refspec(data->remote, entry->value, data->fetch);
4330ab26
CMN
392}
393
bf6bebe2 394static int get_optional_config(
c9ffa84b 395 bool *found, git_config *config, git_buf *buf,
396 git_config_foreach_cb cb, void *payload)
bf6bebe2
RB
397{
398 int error = 0;
399 const char *key = git_buf_cstr(buf);
400
401 if (git_buf_oom(buf))
402 return -1;
403
404 if (cb != NULL)
4efa3290 405 error = git_config_get_multivar_foreach(config, key, NULL, cb, payload);
bf6bebe2
RB
406 else
407 error = git_config_get_string(payload, config, key);
408
c9ffa84b 409 if (found)
410 *found = !error;
411
bf6bebe2
RB
412 if (error == GIT_ENOTFOUND) {
413 giterr_clear();
414 error = 0;
415 }
416
bf6bebe2
RB
417 return error;
418}
419
209425ce 420int git_remote_lookup(git_remote **out, git_repository *repo, const char *name)
9c82357b
CMN
421{
422 git_remote *remote;
f0f3a18a 423 git_buf buf = GIT_BUF_INIT;
9c82357b 424 const char *val;
4376f7f6 425 int error = 0;
9462c471 426 git_config *config;
96869a4e 427 struct refspec_cb_data data = { NULL };
c9ffa84b 428 bool optional_setting_found = false, found;
4330ab26 429
9462c471
VM
430 assert(out && repo && name);
431
032ba9e4 432 if ((error = ensure_remote_name_is_valid(name)) < 0)
433 return error;
434
ac99d86b 435 if ((error = git_repository_config_snapshot(&config, repo)) < 0)
29c4cb09 436 return error;
4bef3565 437
392702ee 438 remote = git__calloc(1, sizeof(git_remote));
4376f7f6 439 GITERR_CHECK_ALLOC(remote);
9c82357b 440
9c82357b 441 remote->name = git__strdup(name);
4376f7f6 442 GITERR_CHECK_ALLOC(remote->name);
9c82357b 443
25e0b157
RB
444 if (git_vector_init(&remote->refs, 32, NULL) < 0 ||
445 git_vector_init(&remote->refspecs, 2, NULL) < 0 ||
2cdd5c57 446 git_vector_init(&remote->passive_refspecs, 2, NULL) < 0 ||
25e0b157 447 git_vector_init(&remote->active_refspecs, 2, NULL) < 0) {
2fb9d6de 448 error = -1;
449 goto cleanup;
450 }
d88d4311 451
25e0b157 452 if ((error = git_buf_printf(&buf, "remote.%s.url", name)) < 0)
2fb9d6de 453 goto cleanup;
9c82357b 454
c9ffa84b 455 if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
9c82357b 456 goto cleanup;
bf6bebe2 457
c9ffa84b 458 optional_setting_found |= found;
459
9462c471 460 remote->repo = repo;
35a8a8c5 461 remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
c9ffa84b 462
463 if (found && strlen(val) > 0) {
ec0c4c40 464 remote->url = apply_insteadof(config, val, GIT_DIRECTION_FETCH);
c9ffa84b 465 GITERR_CHECK_ALLOC(remote->url);
466 }
9c82357b 467
bf6bebe2 468 val = NULL;
3ed4b501 469 git_buf_clear(&buf);
bf6bebe2 470 git_buf_printf(&buf, "remote.%s.pushurl", name);
3ed4b501 471
c9ffa84b 472 if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
3ed4b501 473 goto cleanup;
3ed4b501 474
c9ffa84b 475 optional_setting_found |= found;
476
477 if (!optional_setting_found) {
478 error = GIT_ENOTFOUND;
d723dbed 479 giterr_set(GITERR_CONFIG, "Remote '%s' does not exist.", name);
3ed4b501 480 goto cleanup;
c9ffa84b 481 }
3ed4b501 482
c9ffa84b 483 if (found && strlen(val) > 0) {
ec0c4c40 484 remote->pushurl = apply_insteadof(config, val, GIT_DIRECTION_PUSH);
3ed4b501
SC
485 GITERR_CHECK_ALLOC(remote->pushurl);
486 }
487
4330ab26
CMN
488 data.remote = remote;
489 data.fetch = true;
96869a4e 490
f0f3a18a 491 git_buf_clear(&buf);
bf6bebe2
RB
492 git_buf_printf(&buf, "remote.%s.fetch", name);
493
c9ffa84b 494 if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0)
2fb9d6de 495 goto cleanup;
9c82357b 496
4330ab26 497 data.fetch = false;
bf6bebe2
RB
498 git_buf_clear(&buf);
499 git_buf_printf(&buf, "remote.%s.push", name);
9c82357b 500
c9ffa84b 501 if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0)
9c82357b
CMN
502 goto cleanup;
503
24f2f94e
CMN
504 if (download_tags_value(remote, config) < 0)
505 goto cleanup;
506
1ef3f0ce
DC
507 if ((error = lookup_remote_prune_config(remote, config, name)) < 0)
508 goto cleanup;
509
510 /* Move the data over to where the matching functions can find them */
69f0032b 511 if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
1ef3f0ce
DC
512 goto cleanup;
513
514 *out = remote;
515
516cleanup:
517 git_config_free(config);
518 git_buf_free(&buf);
519
520 if (error < 0)
521 git_remote_free(remote);
522
523 return error;
524}
525
526static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name)
527{
528 git_buf buf = GIT_BUF_INIT;
529 int error = 0;
530
5f473947
L
531 git_buf_printf(&buf, "remote.%s.prune", name);
532
533 if ((error = git_config_get_bool(&remote->prune_refs, config, git_buf_cstr(&buf))) < 0) {
534 if (error == GIT_ENOTFOUND) {
535 giterr_clear();
536
537 if ((error = git_config_get_bool(&remote->prune_refs, config, "fetch.prune")) < 0) {
538 if (error == GIT_ENOTFOUND) {
539 giterr_clear();
66b71ea5 540 error = 0;
5f473947
L
541 }
542 }
543 }
544 }
545
f0f3a18a 546 git_buf_free(&buf);
9c82357b
CMN
547 return error;
548}
549
4330ab26 550static int update_config_refspec(const git_remote *remote, git_config *config, int direction)
4fe5520a 551{
4330ab26 552 git_buf name = GIT_BUF_INIT;
66566516 553 unsigned int push;
4330ab26
CMN
554 const char *dir;
555 size_t i;
bf6bebe2 556 int error = 0;
3793fa9b 557 const char *cname;
4fe5520a 558
4330ab26
CMN
559 push = direction == GIT_DIRECTION_PUSH;
560 dir = push ? "push" : "fetch";
4fe5520a 561
4330ab26
CMN
562 if (git_buf_printf(&name, "remote.%s.%s", remote->name, dir) < 0)
563 return -1;
3793fa9b 564 cname = git_buf_cstr(&name);
4fe5520a 565
4330ab26 566 /* Clear out the existing config */
bf6bebe2 567 while (!error)
3793fa9b 568 error = git_config_delete_multivar(config, cname, ".*");
4fe5520a 569
4330ab26
CMN
570 if (error != GIT_ENOTFOUND)
571 return error;
572
1be680c4 573 for (i = 0; i < remote->refspecs.length; i++) {
4330ab26 574 git_refspec *spec = git_vector_get(&remote->refspecs, i);
4330ab26
CMN
575
576 if (spec->push != push)
577 continue;
578
376454d0
DRT
579 // "$^" is a unmatcheable regexp: it will not match anything at all, so
580 // all values will be considered new and we will not replace any
581 // present value.
bf6bebe2 582 if ((error = git_config_set_multivar(
376454d0 583 config, cname, "$^", spec->string)) < 0) {
4330ab26
CMN
584 goto cleanup;
585 }
586 }
587
588 giterr_clear();
589 error = 0;
4fe5520a 590
591cleanup:
592 git_buf_free(&name);
4fe5520a 593
594 return error;
595}
596
df705148 597const char *git_remote_name(const git_remote *remote)
9c82357b 598{
4bef3565 599 assert(remote);
9c82357b
CMN
600 return remote->name;
601}
602
85e1eded
ES
603git_repository *git_remote_owner(const git_remote *remote)
604{
605 assert(remote);
606 return remote->repo;
607}
608
df705148 609const char *git_remote_url(const git_remote *remote)
9c82357b 610{
4bef3565 611 assert(remote);
9c82357b
CMN
612 return remote->url;
613}
614
22261344 615static int set_url(git_repository *repo, const char *remote, const char *pattern, const char *url)
76501590 616{
22261344
CMN
617 git_config *cfg;
618 git_buf buf = GIT_BUF_INIT, canonical_url = GIT_BUF_INIT;
619 int error;
76501590 620
22261344 621 assert(repo && remote);
76501590 622
22261344
CMN
623 if ((error = ensure_remote_name_is_valid(remote)) < 0)
624 return error;
625
626 if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
627 return error;
628
629 if ((error = git_buf_printf(&buf, pattern, remote)) < 0)
630 return error;
631
632 if (url) {
633 if ((error = canonicalize_url(&canonical_url, url)) < 0)
634 goto cleanup;
635
636 error = git_config_set_string(cfg, buf.ptr, url);
637 } else {
638 error = git_config_delete_entry(cfg, buf.ptr);
639 }
640
641cleanup:
642 git_buf_free(&canonical_url);
643 git_buf_free(&buf);
644
645 return error;
646}
647
648int git_remote_set_url(git_repository *repo, const char *remote, const char *url)
649{
650 return set_url(repo, remote, CONFIG_URL_FMT, url);
76501590
SC
651}
652
df705148 653const char *git_remote_pushurl(const git_remote *remote)
76501590
SC
654{
655 assert(remote);
656 return remote->pushurl;
657}
658
22261344 659int git_remote_set_pushurl(git_repository *repo, const char *remote, const char* url)
76501590 660{
22261344 661 return set_url(repo, remote, CONFIG_PUSHURL_FMT, url);
76501590
SC
662}
663
eff5b499
SC
664const char* git_remote__urlfordirection(git_remote *remote, int direction)
665{
666 assert(remote);
667
b83c92dd 668 assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
669
df705148 670 if (direction == GIT_DIRECTION_FETCH) {
eff5b499
SC
671 return remote->url;
672 }
673
df705148 674 if (direction == GIT_DIRECTION_PUSH) {
eff5b499
SC
675 return remote->pushurl ? remote->pushurl : remote->url;
676 }
677
678 return NULL;
679}
680
8f0104ec
CMN
681int set_transport_callbacks(git_transport *t, const git_remote_callbacks *cbs)
682{
683 if (!t->set_callbacks || !cbs)
684 return 0;
685
686 return t->set_callbacks(t, cbs->sideband_progress, NULL,
687 cbs->certificate_check, cbs->payload);
688}
689
d16c1b97 690static int set_transport_custom_headers(git_transport *t, const git_strarray *custom_headers)
4f2b6093 691{
d7375662 692 if (!t->set_custom_headers)
4f2b6093
MB
693 return 0;
694
695 return t->set_custom_headers(t, custom_headers);
696}
697
698int git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_strarray *custom_headers)
9ba49bb5 699{
9ba49bb5 700 git_transport *t;
c0c39025 701 const char *url;
41fb1ca0 702 int flags = GIT_TRANSPORTFLAGS_NONE;
80fc7d6b 703 int error;
8f0104ec 704 void *payload = NULL;
058b753c
CMN
705 git_cred_acquire_cb credentials = NULL;
706 git_transport_cb transport = NULL;
9ba49bb5 707
4bef3565
VM
708 assert(remote);
709
8f0104ec
CMN
710 if (callbacks) {
711 GITERR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
712 credentials = callbacks->credentials;
058b753c 713 transport = callbacks->transport;
8f0104ec
CMN
714 payload = callbacks->payload;
715 }
716
41fb1ca0
PK
717 t = remote->transport;
718
c0c39025 719 url = git_remote__urlfordirection(remote, direction);
80fc7d6b 720 if (url == NULL) {
44bc0c6a 721 giterr_set(GITERR_INVALID,
722 "Malformed remote '%s' - missing URL", remote->name);
eff5b499 723 return -1;
44bc0c6a 724 }
eff5b499 725
1697cd6f
PK
726 /* If we don't have a transport object yet, and the caller specified a
727 * custom transport factory, use that */
058b753c
CMN
728 if (!t && transport &&
729 (error = transport(&t, remote, payload)) < 0)
1697cd6f
PK
730 return error;
731
732 /* If we still don't have a transport, then use the global
733 * transport registrations which map URI schemes to transport factories */
80fc7d6b
ET
734 if (!t && (error = git_transport_new(&t, remote, url)) < 0)
735 return error;
9ba49bb5 736
4f2b6093
MB
737 if ((error = set_transport_custom_headers(t, custom_headers)) != 0)
738 goto on_error;
739
8f0104ec
CMN
740 if ((error = set_transport_callbacks(t, callbacks)) < 0 ||
741 (error = t->connect(t, url, credentials, payload, direction, flags)) != 0)
4376f7f6 742 goto on_error;
9ba49bb5
CMN
743
744 remote->transport = t;
745
4376f7f6 746 return 0;
9ba49bb5 747
4376f7f6
CMN
748on_error:
749 t->free(t);
613d5eb9
PK
750
751 if (t == remote->transport)
752 remote->transport = NULL;
753
80fc7d6b 754 return error;
9ba49bb5
CMN
755}
756
359dce72 757int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote)
9ba49bb5 758{
d88d4311
VM
759 assert(remote);
760
dc8adda4 761 if (!remote->transport) {
699dfcc3 762 giterr_set(GITERR_NET, "this remote has never connected");
dc8adda4
JG
763 return -1;
764 }
765
359dce72 766 return remote->transport->ls(out, size, remote->transport);
9ba49bb5
CMN
767}
768
613d5eb9
PK
769int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url)
770{
771 git_config *cfg;
9a97f49e 772 git_config_entry *ce = NULL;
e069c621 773 git_buf val = GIT_BUF_INIT;
80fc7d6b 774 int error;
613d5eb9
PK
775
776 assert(remote);
777
a71c27cc 778 if (!proxy_url || !remote->repo)
613d5eb9
PK
779 return -1;
780
781 *proxy_url = NULL;
782
80fc7d6b
ET
783 if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0)
784 return error;
613d5eb9
PK
785
786 /* Go through the possible sources for proxy configuration, from most specific
787 * to least specific. */
788
789 /* remote.<name>.proxy config setting */
9f77b3f6 790 if (remote->name && remote->name[0]) {
613d5eb9
PK
791 git_buf buf = GIT_BUF_INIT;
792
80fc7d6b
ET
793 if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0)
794 return error;
613d5eb9 795
9f77b3f6
RB
796 error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false);
797 git_buf_free(&buf);
613d5eb9 798
9f77b3f6 799 if (error < 0)
80fc7d6b 800 return error;
613d5eb9 801
9f77b3f6 802 if (ce && ce->value) {
e069c621 803 *proxy_url = git__strdup(ce->value);
9f77b3f6
RB
804 goto found;
805 }
613d5eb9
PK
806 }
807
808 /* http.proxy config setting */
9f77b3f6 809 if ((error = git_config__lookup_entry(&ce, cfg, "http.proxy", false)) < 0)
80fc7d6b 810 return error;
e069c621 811
9f77b3f6 812 if (ce && ce->value) {
e069c621 813 *proxy_url = git__strdup(ce->value);
9f77b3f6
RB
814 goto found;
815 }
613d5eb9
PK
816
817 /* HTTP_PROXY / HTTPS_PROXY environment variables */
e069c621 818 error = git__getenv(&val, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY");
613d5eb9 819
e069c621
ET
820 if (error < 0) {
821 if (error == GIT_ENOTFOUND) {
822 giterr_clear();
823 error = 0;
824 }
825
826 return error;
613d5eb9 827 }
e069c621
ET
828
829 *proxy_url = git_buf_detach(&val);
830
831found:
832 GITERR_CHECK_ALLOC(*proxy_url);
9a97f49e 833 git_config_entry_free(ce);
613d5eb9
PK
834
835 return 0;
836}
837
af613ecd
CMN
838/* DWIM `refspecs` based on `refs` and append the output to `out` */
839static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs)
d8488457 840{
af613ecd 841 size_t i;
d8488457 842 git_refspec *spec;
d8488457
CMN
843
844 git_vector_foreach(refspecs, i, spec) {
af613ecd
CMN
845 if (git_refspec__dwim_one(out, spec, refs) < 0)
846 return -1;
847 }
d8488457 848
af613ecd
CMN
849 return 0;
850}
d8488457 851
af613ecd
CMN
852static void free_refspecs(git_vector *vec)
853{
854 size_t i;
855 git_refspec *spec;
d8488457 856
af613ecd
CMN
857 git_vector_foreach(vec, i, spec) {
858 git_refspec__free(spec);
859 git__free(spec);
d8488457
CMN
860 }
861
af613ecd 862 git_vector_clear(vec);
d8488457
CMN
863}
864
865static int remote_head_cmp(const void *_a, const void *_b)
866{
867 const git_remote_head *a = (git_remote_head *) _a;
868 const git_remote_head *b = (git_remote_head *) _b;
869
870 return git__strcmp_cb(a->name, b->name);
871}
872
877cde76
CMN
873static int ls_to_vector(git_vector *out, git_remote *remote)
874{
875 git_remote_head **heads;
876 size_t heads_len, i;
877
878 if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0)
879 return -1;
880
881 if (git_vector_init(out, heads_len, remote_head_cmp) < 0)
882 return -1;
883
884 for (i = 0; i < heads_len; i++) {
885 if (git_vector_insert(out, heads[i]) < 0)
886 return -1;
887 }
888
889 return 0;
890}
891
8f0104ec 892int git_remote_download(git_remote *remote, const git_strarray *refspecs, const git_fetch_options *opts)
48a65a07 893{
eecc1772 894 int error = -1;
3f894205 895 size_t i;
2785544f 896 git_vector *to_active, specs = GIT_VECTOR_INIT, refs = GIT_VECTOR_INIT;
8f0104ec 897 const git_remote_callbacks *cbs = NULL;
c49126c8 898 const git_strarray *custom_headers = NULL;
95057b85 899
1e3b8ed5 900 assert(remote);
4bef3565 901
8f0104ec
CMN
902 if (opts) {
903 GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
904 cbs = &opts->callbacks;
c49126c8 905 custom_headers = &opts->custom_headers;
8f0104ec
CMN
906 }
907
908 if (!git_remote_connected(remote) &&
c49126c8 909 (error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, custom_headers)) < 0)
8f0104ec
CMN
910 goto on_error;
911
877cde76 912 if (ls_to_vector(&refs, remote) < 0)
d8488457 913 return -1;
d8488457 914
3f894205
CMN
915 if ((git_vector_init(&specs, 0, NULL)) < 0)
916 goto on_error;
917
c5837cad 918 remote->passed_refspecs = 0;
8e398e4c 919 if (!refspecs || !refspecs->count) {
3f894205
CMN
920 to_active = &remote->refspecs;
921 } else {
922 for (i = 0; i < refspecs->count; i++) {
923 if ((error = add_refspec_to(&specs, refspecs->strings[i], true)) < 0)
924 goto on_error;
925 }
926
927 to_active = &specs;
c5837cad 928 remote->passed_refspecs = 1;
3f894205
CMN
929 }
930
2cdd5c57
CMN
931 free_refspecs(&remote->passive_refspecs);
932 if ((error = dwim_refspecs(&remote->passive_refspecs, &remote->refspecs, &refs)) < 0)
933 goto on_error;
af613ecd 934
2cdd5c57 935 free_refspecs(&remote->active_refspecs);
3f894205 936 error = dwim_refspecs(&remote->active_refspecs, to_active, &refs);
2cdd5c57 937
877cde76 938 git_vector_free(&refs);
3f894205
CMN
939 free_refspecs(&specs);
940 git_vector_free(&specs);
877cde76
CMN
941
942 if (error < 0)
25e0b157 943 return error;
d8488457 944
c070ac64
POL
945 if (remote->push) {
946 git_push_free(remote->push);
947 remote->push = NULL;
948 }
949
35a8a8c5 950 if ((error = git_fetch_negotiate(remote, opts)) < 0)
4376f7f6 951 return error;
95057b85 952
8f0104ec 953 return git_fetch_download_pack(remote, cbs);
3f894205
CMN
954
955on_error:
956 git_vector_free(&refs);
957 free_refspecs(&specs);
958 git_vector_free(&specs);
959 return error;
48a65a07
CMN
960}
961
c3ab1e5a
BS
962int git_remote_fetch(
963 git_remote *remote,
3f894205 964 const git_strarray *refspecs,
8f0104ec 965 const git_fetch_options *opts,
c3ab1e5a 966 const char *reflog_message)
fe3a40a4 967{
3eff2a57 968 int error, update_fetchhead = 1;
35a8a8c5 969 git_remote_autotag_option_t tagopt = remote->download_tags;
6fb373a0 970 bool prune = false;
db55bb73 971 git_buf reflog_msg_buf = GIT_BUF_INIT;
8f0104ec 972 const git_remote_callbacks *cbs = NULL;
c49126c8 973 const git_strarray *custom_headers = NULL;
8f0104ec
CMN
974
975 if (opts) {
976 GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
977 cbs = &opts->callbacks;
c49126c8 978 custom_headers = &opts->custom_headers;
3eff2a57 979 update_fetchhead = opts->update_fetchhead;
35a8a8c5 980 tagopt = opts->download_tags;
8f0104ec 981 }
fe3a40a4
CMN
982
983 /* Connect and download everything */
c49126c8 984 if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, custom_headers)) != 0)
fe3a40a4
CMN
985 return error;
986
8f0104ec 987 error = git_remote_download(remote, refspecs, opts);
fe3a40a4
CMN
988
989 /* We don't need to be connected anymore */
990 git_remote_disconnect(remote);
991
06a8f5c3
BL
992 /* If the download failed, return the error */
993 if (error != 0)
994 return error;
995
db55bb73
BS
996 /* Default reflog message */
997 if (reflog_message)
998 git_buf_sets(&reflog_msg_buf, reflog_message);
999 else {
1000 git_buf_printf(&reflog_msg_buf, "fetch %s",
1001 remote->name ? remote->name : remote->url);
1002 }
1003
fe3a40a4 1004 /* Create "remote/foo" branches for all remote branches */
35a8a8c5 1005 error = git_remote_update_tips(remote, cbs, update_fetchhead, tagopt, git_buf_cstr(&reflog_msg_buf));
db55bb73 1006 git_buf_free(&reflog_msg_buf);
8c13eaed
CMN
1007 if (error < 0)
1008 return error;
1009
6fb373a0
CMN
1010 if (opts && opts->prune == GIT_FETCH_PRUNE)
1011 prune = true;
c2418f46 1012 else if (opts && opts->prune == GIT_FETCH_PRUNE_UNSPECIFIED && remote->prune_refs)
6fb373a0
CMN
1013 prune = true;
1014 else if (opts && opts->prune == GIT_FETCH_NO_PRUNE)
1015 prune = false;
1016 else
1017 prune = remote->prune_refs;
1018
1019 if (prune)
8f0104ec 1020 error = git_remote_prune(remote, cbs);
8c13eaed 1021
db55bb73 1022 return error;
fe3a40a4
CMN
1023}
1024
b0f6e45d
ET
1025static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src)
1026{
1027 unsigned int i;
1028 git_remote_head *remote_ref;
1029
1030 assert(update_heads && fetchspec_src);
1031
1032 *out = NULL;
7d4b65f6 1033
1034 git_vector_foreach(update_heads, i, remote_ref) {
1035 if (strcmp(remote_ref->name, fetchspec_src) == 0) {
1036 *out = remote_ref;
1037 break;
b0f6e45d
ET
1038 }
1039 }
1040
1041 return 0;
1042}
1043
2c9b9c8b
CMN
1044static int ref_to_update(int *update, git_buf *remote_name, git_remote *remote, git_refspec *spec, const char *ref_name)
1045{
1046 int error = 0;
1047 git_repository *repo;
1048 git_buf upstream_remote = GIT_BUF_INIT;
1049 git_buf upstream_name = GIT_BUF_INIT;
1050
1051 repo = git_remote_owner(remote);
1052
1053 if ((!git_reference__is_branch(ref_name)) ||
1054 !git_remote_name(remote) ||
1055 (error = git_branch_upstream_remote(&upstream_remote, repo, ref_name) < 0) ||
1056 git__strcmp(git_remote_name(remote), git_buf_cstr(&upstream_remote)) ||
1057 (error = git_branch_upstream_name(&upstream_name, repo, ref_name)) < 0 ||
1058 !git_refspec_dst_matches(spec, git_buf_cstr(&upstream_name)) ||
1059 (error = git_refspec_rtransform(remote_name, spec, upstream_name.ptr)) < 0) {
1060 /* Not an error if there is no upstream */
1061 if (error == GIT_ENOTFOUND) {
1062 giterr_clear();
1063 error = 0;
1064 }
1065
1066 *update = 0;
1067 } else {
1068 *update = 1;
1069 }
1070
1071 git_buf_free(&upstream_remote);
1072 git_buf_free(&upstream_name);
1073 return error;
1074}
1075
f49819aa 1076static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_refspec *spec, git_vector *update_heads, git_reference *ref)
b0f6e45d
ET
1077{
1078 git_reference *resolved_ref = NULL;
b0f6e45d 1079 git_buf remote_name = GIT_BUF_INIT;
f49819aa 1080 git_config *config = NULL;
e235db02 1081 const char *ref_name;
2c9b9c8b 1082 int error = 0, update;
b0f6e45d 1083
4330ab26 1084 assert(out && spec && ref);
b0f6e45d
ET
1085
1086 *out = NULL;
1087
67d4997a
CMN
1088 error = git_reference_resolve(&resolved_ref, ref);
1089
1090 /* If we're in an unborn branch, let's pretend nothing happened */
1091 if (error == GIT_ENOTFOUND && git_reference_type(ref) == GIT_REF_SYMBOLIC) {
1092 ref_name = git_reference_symbolic_target(ref);
1093 error = 0;
1094 } else {
1095 ref_name = git_reference_name(resolved_ref);
1096 }
1097
2c9b9c8b 1098 if ((error = ref_to_update(&update, &remote_name, remote, spec, ref_name)) < 0)
b0f6e45d 1099 goto cleanup;
b0f6e45d 1100
2c9b9c8b
CMN
1101 if (update)
1102 error = remote_head_for_fetchspec_src(out, update_heads, git_buf_cstr(&remote_name));
b0f6e45d
ET
1103
1104cleanup:
f7fcb18f 1105 git_buf_free(&remote_name);
b0f6e45d 1106 git_reference_free(resolved_ref);
f49819aa 1107 git_config_free(config);
b0f6e45d
ET
1108 return error;
1109}
1110
4330ab26 1111static int git_remote_write_fetchhead(git_remote *remote, git_refspec *spec, git_vector *update_heads)
b0f6e45d 1112{
b0f6e45d
ET
1113 git_reference *head_ref = NULL;
1114 git_fetchhead_ref *fetchhead_ref;
1115 git_remote_head *remote_ref, *merge_remote_ref;
1116 git_vector fetchhead_refs;
1117 bool include_all_fetchheads;
1118 unsigned int i = 0;
1119 int error = 0;
1120
1121 assert(remote);
1122
404eadb0
CMN
1123 /* no heads, nothing to do */
1124 if (update_heads->length == 0)
1125 return 0;
1126
b0f6e45d
ET
1127 if (git_vector_init(&fetchhead_refs, update_heads->length, git_fetchhead_ref_cmp) < 0)
1128 return -1;
1129
1130 /* Iff refspec is * (but not subdir slash star), include tags */
1131 include_all_fetchheads = (strcmp(GIT_REFS_HEADS_DIR "*", git_refspec_src(spec)) == 0);
1132
1133 /* Determine what to merge: if refspec was a wildcard, just use HEAD */
1134 if (git_refspec_is_wildcard(spec)) {
1135 if ((error = git_reference_lookup(&head_ref, remote->repo, GIT_HEAD_FILE)) < 0 ||
f49819aa 1136 (error = remote_head_for_ref(&merge_remote_ref, remote, spec, update_heads, head_ref)) < 0)
b0f6e45d
ET
1137 goto cleanup;
1138 } else {
1139 /* If we're fetching a single refspec, that's the only thing that should be in FETCH_HEAD. */
1140 if ((error = remote_head_for_fetchspec_src(&merge_remote_ref, update_heads, git_refspec_src(spec))) < 0)
1141 goto cleanup;
1142 }
1143
1144 /* Create the FETCH_HEAD file */
7d4b65f6 1145 git_vector_foreach(update_heads, i, remote_ref) {
b0f6e45d
ET
1146 int merge_this_fetchhead = (merge_remote_ref == remote_ref);
1147
1148 if (!include_all_fetchheads &&
1149 !git_refspec_src_matches(spec, remote_ref->name) &&
1150 !merge_this_fetchhead)
1151 continue;
1152
1153 if (git_fetchhead_ref_create(&fetchhead_ref,
1154 &remote_ref->oid,
1155 merge_this_fetchhead,
1156 remote_ref->name,
1157 git_remote_url(remote)) < 0)
1158 goto cleanup;
1159
1160 if (git_vector_insert(&fetchhead_refs, fetchhead_ref) < 0)
1161 goto cleanup;
1162 }
1163
1164 git_fetchhead_write(remote->repo, &fetchhead_refs);
1165
1166cleanup:
1167 for (i = 0; i < fetchhead_refs.length; ++i)
1168 git_fetchhead_ref_free(fetchhead_refs.contents[i]);
1169
1170 git_vector_free(&fetchhead_refs);
1171 git_reference_free(head_ref);
1172
1173 return error;
1174}
1175
59ff8b67
CMN
1176/**
1177 * Generate a list of candidates for pruning by getting a list of
1178 * references which match the rhs of an active refspec.
1179 */
1180static int prune_candidates(git_vector *candidates, git_remote *remote)
5f473947
L
1181{
1182 git_strarray arr = { 0 };
59ff8b67 1183 size_t i;
5f473947
L
1184 int error;
1185
1186 if ((error = git_reference_list(&arr, remote->repo)) < 0)
1187 return error;
1188
59ff8b67
CMN
1189 for (i = 0; i < arr.count; i++) {
1190 const char *refname = arr.strings[i];
1191 char *refname_dup;
1192
1193 if (!git_remote__matching_dst_refspec(remote, refname))
1194 continue;
1195
1196 refname_dup = git__strdup(refname);
1197 GITERR_CHECK_ALLOC(refname_dup);
1198
1199 if ((error = git_vector_insert(candidates, refname_dup)) < 0)
1200 goto out;
1201 }
1202
1203out:
1204 git_strarray_free(&arr);
1205 return error;
1206}
1207
1208static int find_head(const void *_a, const void *_b)
1209{
1210 git_remote_head *a = (git_remote_head *) _a;
1211 git_remote_head *b = (git_remote_head *) _b;
1212
1213 return strcmp(a->name, b->name);
1214}
1215
8f0104ec 1216int git_remote_prune(git_remote *remote, const git_remote_callbacks *callbacks)
59ff8b67
CMN
1217{
1218 size_t i, j;
1219 git_vector remote_refs = GIT_VECTOR_INIT;
1220 git_vector candidates = GIT_VECTOR_INIT;
1221 const git_refspec *spec;
1222 const char *refname;
1223 int error;
1224 git_oid zero_id = {{ 0 }};
1225
8f0104ec
CMN
1226 if (callbacks)
1227 GITERR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
1228
5f473947
L
1229 if ((error = ls_to_vector(&remote_refs, remote)) < 0)
1230 goto cleanup;
1231
59ff8b67 1232 git_vector_set_cmp(&remote_refs, find_head);
5f473947 1233
59ff8b67
CMN
1234 if ((error = prune_candidates(&candidates, remote)) < 0)
1235 goto cleanup;
5f473947 1236
59ff8b67
CMN
1237 /*
1238 * Remove those entries from the candidate list for which we
1239 * can find a remote reference in at least one refspec.
1240 */
1241 git_vector_foreach(&candidates, i, refname) {
1242 git_vector_foreach(&remote->active_refspecs, j, spec) {
1243 git_buf buf = GIT_BUF_INIT;
1244 size_t pos;
1245 char *src_name;
1246 git_remote_head key = {0};
1247
1248 if (!git_refspec_dst_matches(spec, refname))
5f473947
L
1249 continue;
1250
59ff8b67
CMN
1251 if ((error = git_refspec_rtransform(&buf, spec, refname)) < 0)
1252 goto cleanup;
5f473947 1253
59ff8b67
CMN
1254 key.name = (char *) git_buf_cstr(&buf);
1255 error = git_vector_search(&pos, &remote_refs, &key);
1256 git_buf_free(&buf);
5f473947 1257
59ff8b67
CMN
1258 if (error < 0 && error != GIT_ENOTFOUND)
1259 goto cleanup;
5f473947 1260
59ff8b67 1261 if (error == GIT_ENOTFOUND)
5f473947
L
1262 continue;
1263
59ff8b67
CMN
1264 /* if we did find a source, remove it from the candiates */
1265 if ((error = git_vector_set((void **) &src_name, &candidates, i, NULL)) < 0)
1266 goto cleanup;
1267
1268 git__free(src_name);
1269 break;
5f473947
L
1270 }
1271 }
1272
59ff8b67
CMN
1273 /*
1274 * For those candidates still left in the list, we need to
1275 * remove them. We do not remove symrefs, as those are for
1276 * stuff like origin/HEAD which will never match, but we do
1277 * not want to remove them.
1278 */
1279 git_vector_foreach(&candidates, i, refname) {
1280 git_reference *ref;
1281 git_oid id;
1282
1283 if (refname == NULL)
1284 continue;
1285
1286 error = git_reference_lookup(&ref, remote->repo, refname);
1287 /* as we want it gone, let's not consider this an error */
1288 if (error == GIT_ENOTFOUND)
1289 continue;
1290
1291 if (error < 0)
1292 goto cleanup;
1293
1294 if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
1295 git_reference_free(ref);
1296 continue;
1297 }
1298
1299 git_oid_cpy(&id, git_reference_target(ref));
1300 error = git_reference_delete(ref);
1301 git_reference_free(ref);
1302 if (error < 0)
1303 goto cleanup;
1304
8f0104ec
CMN
1305 if (callbacks && callbacks->update_tips)
1306 error = callbacks->update_tips(refname, &id, &zero_id, callbacks->payload);
59ff8b67
CMN
1307
1308 if (error < 0)
1309 goto cleanup;
1310 }
1311
5f473947 1312cleanup:
5f473947 1313 git_vector_free(&remote_refs);
59ff8b67 1314 git_vector_free_deep(&candidates);
5f473947
L
1315 return error;
1316}
1317
c3ab1e5a
BS
1318static int update_tips_for_spec(
1319 git_remote *remote,
8f0104ec 1320 const git_remote_callbacks *callbacks,
3eff2a57 1321 int update_fetchhead,
35a8a8c5 1322 git_remote_autotag_option_t tagopt,
c3ab1e5a
BS
1323 git_refspec *spec,
1324 git_vector *refs,
c3ab1e5a 1325 const char *log_message)
441f57c2 1326{
a37ddf7e 1327 int error = 0, autotag;
517bda19 1328 unsigned int i = 0;
97769280 1329 git_buf refname = GIT_BUF_INIT;
f184836b 1330 git_oid old;
a37ddf7e 1331 git_odb *odb;
441f57c2
CMN
1332 git_remote_head *head;
1333 git_reference *ref;
a37ddf7e 1334 git_refspec tagspec;
4330ab26 1335 git_vector update_heads;
441f57c2 1336
4bef3565
VM
1337 assert(remote);
1338
acd17006 1339 if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
a37ddf7e
CMN
1340 return -1;
1341
3230a44f 1342 if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
a37ddf7e
CMN
1343 return -1;
1344
41fb1ca0 1345 /* Make a copy of the transport's refs */
4330ab26 1346 if (git_vector_init(&update_heads, 16, NULL) < 0)
41fb1ca0 1347 return -1;
585a2eb7 1348
4330ab26
CMN
1349 for (; i < refs->length; ++i) {
1350 head = git_vector_get(refs, i);
9063be1f 1351 autotag = 0;
e3e017d4 1352 git_buf_clear(&refname);
517bda19 1353
a37ddf7e
CMN
1354 /* Ignore malformed ref names (which also saves us from tag^{} */
1355 if (!git_reference_is_valid_name(head->name))
1356 continue;
1357
e3e017d4 1358 /* If we have a tag, see if the auto-follow rules say to update it */
e284c451 1359 if (git_refspec_src_matches(&tagspec, head->name)) {
35a8a8c5 1360 if (tagopt != GIT_REMOTE_DOWNLOAD_TAGS_NONE) {
3230a44f 1361
35a8a8c5 1362 if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_AUTO)
e284c451 1363 autotag = 1;
a37ddf7e 1364
e284c451
POL
1365 git_buf_clear(&refname);
1366 if (git_buf_puts(&refname, head->name) < 0)
1367 goto on_error;
e284c451 1368 }
e3e017d4
CMN
1369 }
1370
1371 /* If we didn't want to auto-follow the tag, check if the refspec matches */
1372 if (!autotag && git_refspec_src_matches(spec, head->name)) {
23aa7c90
CMN
1373 if (spec->dst) {
1374 if (git_refspec_transform(&refname, spec, head->name) < 0)
1375 goto on_error;
1376 } else {
1377 /*
1378 * no rhs mans store it in FETCH_HEAD, even if we don't
1379 update anything else.
1380 */
1381 if ((error = git_vector_insert(&update_heads, head)) < 0)
1382 goto on_error;
1383
1384 continue;
1385 }
e3e017d4
CMN
1386 }
1387
1388 /* If we still don't have a refname, we don't want it */
1389 if (git_buf_len(&refname) == 0) {
a37ddf7e
CMN
1390 continue;
1391 }
1392
e284c451 1393 /* In autotag mode, only create tags for objects already in db */
a37ddf7e
CMN
1394 if (autotag && !git_odb_exists(odb, &head->oid))
1395 continue;
f184836b 1396
d908351a 1397 if (!autotag && git_vector_insert(&update_heads, head) < 0)
b0f6e45d
ET
1398 goto on_error;
1399
2508cc66 1400 error = git_reference_name_to_id(&old, remote->repo, refname.ptr);
904b67e6 1401 if (error < 0 && error != GIT_ENOTFOUND)
f184836b
CMN
1402 goto on_error;
1403
d908351a 1404 if (error == GIT_ENOTFOUND) {
f184836b
CMN
1405 memset(&old, 0, GIT_OID_RAWSZ);
1406
d908351a
L
1407 if (autotag && git_vector_insert(&update_heads, head) < 0)
1408 goto on_error;
1409 }
1410
b7f167da 1411 if (!git_oid__cmp(&old, &head->oid))
f184836b 1412 continue;
441f57c2 1413
a37ddf7e 1414 /* In autotag mode, don't overwrite any locally-existing tags */
c3ab1e5a 1415 error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag,
659cf202 1416 log_message);
a37ddf7e 1417 if (error < 0 && error != GIT_EEXISTS)
944d250f 1418 goto on_error;
39157563
CMN
1419
1420 git_reference_free(ref);
f184836b 1421
8f0104ec
CMN
1422 if (callbacks && callbacks->update_tips != NULL) {
1423 if (callbacks->update_tips(refname.ptr, &old, &head->oid, callbacks->payload) < 0)
f184836b
CMN
1424 goto on_error;
1425 }
441f57c2
CMN
1426 }
1427
3eff2a57 1428 if (update_fetchhead &&
4330ab26 1429 (error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0)
b0f6e45d
ET
1430 goto on_error;
1431
b0f6e45d 1432 git_vector_free(&update_heads);
a37ddf7e 1433 git_refspec__free(&tagspec);
97769280 1434 git_buf_free(&refname);
f184836b
CMN
1435 return 0;
1436
1437on_error:
b0f6e45d 1438 git_vector_free(&update_heads);
a37ddf7e 1439 git_refspec__free(&tagspec);
f184836b
CMN
1440 git_buf_free(&refname);
1441 return -1;
97769280 1442
441f57c2
CMN
1443}
1444
c5837cad
CMN
1445/**
1446 * Iteration over the three vectors, with a pause whenever we find a match
1447 *
1448 * On each stop, we store the iteration stat in the inout i,j,k
1449 * parameters, and return the currently matching passive refspec as
1450 * well as the head which we matched.
1451 */
1452static int next_head(const git_remote *remote, git_vector *refs,
1453 git_refspec **out_spec, git_remote_head **out_head,
1454 size_t *out_i, size_t *out_j, size_t *out_k)
1455{
1456 const git_vector *active, *passive;
1457 git_remote_head *head;
1458 git_refspec *spec, *passive_spec;
1459 size_t i, j, k;
1460
1461 active = &remote->active_refspecs;
1462 passive = &remote->passive_refspecs;
1463
1464 i = *out_i;
1465 j = *out_j;
1466 k = *out_k;
1467
1468 for (; i < refs->length; i++) {
1469 head = git_vector_get(refs, i);
1470
1471 if (!git_reference_is_valid_name(head->name))
1472 continue;
1473
1474 for (; j < active->length; j++) {
1475 spec = git_vector_get(active, j);
1476
1477 if (!git_refspec_src_matches(spec, head->name))
1478 continue;
1479
1480 for (; k < passive->length; k++) {
1481 passive_spec = git_vector_get(passive, k);
1482
1483 if (!git_refspec_src_matches(passive_spec, head->name))
1484 continue;
1485
1486 *out_spec = passive_spec;
1487 *out_head = head;
1488 *out_i = i;
1489 *out_j = j;
1490 *out_k = k + 1;
1491 return 0;
1492
1493 }
1494 k = 0;
1495 }
1496 j = 0;
1497 }
1498
1499 return GIT_ITEROVER;
1500}
1501
9566ce43
CMN
1502static int opportunistic_updates(const git_remote *remote, const git_remote_callbacks *callbacks,
1503 git_vector *refs, const char *msg)
c5837cad
CMN
1504{
1505 size_t i, j, k;
1506 git_refspec *spec;
1507 git_remote_head *head;
1508 git_reference *ref;
1509 git_buf refname = GIT_BUF_INIT;
9566ce43 1510 int error = 0;
c5837cad
CMN
1511
1512 i = j = k = 0;
1513
1514 while ((error = next_head(remote, refs, &spec, &head, &i, &j, &k)) == 0) {
9566ce43 1515 git_oid old = {{ 0 }};
c5837cad
CMN
1516 /*
1517 * If we got here, there is a refspec which was used
1518 * for fetching which matches the source of one of the
1519 * passive refspecs, so we should update that
1520 * remote-tracking branch, but not add it to
1521 * FETCH_HEAD
1522 */
1523
9566ce43 1524 git_buf_clear(&refname);
c5837cad 1525 if ((error = git_refspec_transform(&refname, spec, head->name)) < 0)
9566ce43 1526 goto cleanup;
c5837cad 1527
9566ce43
CMN
1528 error = git_reference_name_to_id(&old, remote->repo, refname.ptr);
1529 if (error < 0 && error != GIT_ENOTFOUND)
1530 goto cleanup;
1531
1532 if (!git_oid_cmp(&old, &head->oid))
1533 continue;
c5837cad 1534
9566ce43
CMN
1535 /* If we did find a current reference, make sure we haven't lost a race */
1536 if (error)
1537 error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, true, msg);
1538 else
1539 error = git_reference_create_matching(&ref, remote->repo, refname.ptr, &head->oid, true, &old, msg);
1540 git_reference_free(ref);
c5837cad 1541 if (error < 0)
9566ce43
CMN
1542 goto cleanup;
1543
1544 if (callbacks && callbacks->update_tips != NULL) {
1545 if (callbacks->update_tips(refname.ptr, &old, &head->oid, callbacks->payload) < 0)
1546 goto cleanup;
1547 }
c5837cad
CMN
1548 }
1549
9566ce43
CMN
1550 if (error == GIT_ITEROVER)
1551 error = 0;
1552
1553cleanup:
1554 git_buf_free(&refname);
1555 return error;
c5837cad
CMN
1556}
1557
c3ab1e5a
BS
1558int git_remote_update_tips(
1559 git_remote *remote,
8f0104ec 1560 const git_remote_callbacks *callbacks,
3eff2a57 1561 int update_fetchhead,
35a8a8c5 1562 git_remote_autotag_option_t download_tags,
c3ab1e5a 1563 const char *reflog_message)
4330ab26 1564{
505b5d0c 1565 git_refspec *spec, tagspec;
cdedef40 1566 git_vector refs = GIT_VECTOR_INIT;
35a8a8c5 1567 git_remote_autotag_option_t tagopt;
505b5d0c 1568 int error;
4330ab26
CMN
1569 size_t i;
1570
fe794b2e
CMN
1571 /* push has its own logic hidden away in the push object */
1572 if (remote->push) {
8f0104ec 1573 return git_push_update_tips(remote->push, callbacks);
fe794b2e
CMN
1574 }
1575
505b5d0c
CMN
1576 if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
1577 return -1;
1578
877cde76
CMN
1579
1580 if ((error = ls_to_vector(&refs, remote)) < 0)
505b5d0c
CMN
1581 goto out;
1582
c2418f46 1583 if (download_tags == GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED)
35a8a8c5
CMN
1584 tagopt = remote->download_tags;
1585 else
1586 tagopt = download_tags;
1587
1588 if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
1589 if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, &tagspec, &refs, reflog_message)) < 0)
e284c451 1590 goto out;
505b5d0c 1591 }
4330ab26 1592
af613ecd 1593 git_vector_foreach(&remote->active_refspecs, i, spec) {
4330ab26
CMN
1594 if (spec->push)
1595 continue;
1596
35a8a8c5 1597 if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, spec, &refs, reflog_message)) < 0)
505b5d0c 1598 goto out;
4330ab26
CMN
1599 }
1600
c5837cad
CMN
1601 /* only try to do opportunisitic updates if the refpec lists differ */
1602 if (remote->passed_refspecs)
9566ce43 1603 error = opportunistic_updates(remote, callbacks, &refs, reflog_message);
c5837cad 1604
505b5d0c 1605out:
877cde76 1606 git_vector_free(&refs);
505b5d0c 1607 git_refspec__free(&tagspec);
505b5d0c 1608 return error;
4330ab26
CMN
1609}
1610
11f6ad5f 1611int git_remote_connected(const git_remote *remote)
6ac3b707 1612{
4bef3565 1613 assert(remote);
41fb1ca0
PK
1614
1615 if (!remote->transport || !remote->transport->is_connected)
1616 return 0;
1617
1618 /* Ask the transport if it's connected. */
613d5eb9 1619 return remote->transport->is_connected(remote->transport);
6ac3b707
CMN
1620}
1621
f0d2ddbb
CMN
1622void git_remote_stop(git_remote *remote)
1623{
613d5eb9
PK
1624 assert(remote);
1625
1626 if (remote->transport && remote->transport->cancel)
41fb1ca0 1627 remote->transport->cancel(remote->transport);
f0d2ddbb
CMN
1628}
1629
4cf01e9a
CMN
1630void git_remote_disconnect(git_remote *remote)
1631{
4bef3565
VM
1632 assert(remote);
1633
41fb1ca0
PK
1634 if (git_remote_connected(remote))
1635 remote->transport->close(remote->transport);
4cf01e9a
CMN
1636}
1637
9c82357b
CMN
1638void git_remote_free(git_remote *remote)
1639{
2aae2188
CMN
1640 if (remote == NULL)
1641 return;
1642
42ea35c0
MS
1643 if (remote->transport != NULL) {
1644 git_remote_disconnect(remote);
1645
1646 remote->transport->free(remote->transport);
1647 remote->transport = NULL;
1648 }
1649
1650 git_vector_free(&remote->refs);
1651
af613ecd 1652 free_refspecs(&remote->refspecs);
4330ab26
CMN
1653 git_vector_free(&remote->refspecs);
1654
af613ecd
CMN
1655 free_refspecs(&remote->active_refspecs);
1656 git_vector_free(&remote->active_refspecs);
1657
2cdd5c57
CMN
1658 free_refspecs(&remote->passive_refspecs);
1659 git_vector_free(&remote->passive_refspecs);
1660
fe794b2e 1661 git_push_free(remote->push);
3286c408 1662 git__free(remote->url);
3ed4b501 1663 git__free(remote->pushurl);
3286c408 1664 git__free(remote->name);
3286c408 1665 git__free(remote);
9c82357b 1666}
8171998f 1667
106c12f1 1668static int remote_list_cb(const git_config_entry *entry, void *payload)
8171998f 1669{
25e0b157 1670 git_vector *list = payload;
106c12f1
RB
1671 const char *name = entry->name + strlen("remote.");
1672 size_t namelen = strlen(name);
1673 char *remote_name;
8171998f 1674
106c12f1 1675 /* we know name matches "remote.<stuff>.(push)?url" */
8171998f 1676
106c12f1
RB
1677 if (!strcmp(&name[namelen - 4], ".url"))
1678 remote_name = git__strndup(name, namelen - 4); /* strip ".url" */
1679 else
1680 remote_name = git__strndup(name, namelen - 8); /* strip ".pushurl" */
25e0b157 1681 GITERR_CHECK_ALLOC(remote_name);
8171998f 1682
25e0b157 1683 return git_vector_insert(list, remote_name);
8171998f
CMN
1684}
1685
1686int git_remote_list(git_strarray *remotes_list, git_repository *repo)
1687{
8171998f 1688 int error;
96869a4e 1689 git_config *cfg;
25e0b157 1690 git_vector list = GIT_VECTOR_INIT;
8171998f 1691
96869a4e
RB
1692 if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
1693 return error;
8171998f 1694
25e0b157 1695 if ((error = git_vector_init(&list, 4, git__strcmp_cb)) < 0)
96869a4e 1696 return error;
8171998f 1697
106c12f1 1698 error = git_config_foreach_match(
25e0b157 1699 cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list);
8171998f 1700
4376f7f6 1701 if (error < 0) {
9cfce273 1702 git_vector_free_deep(&list);
8171998f
CMN
1703 return error;
1704 }
1705
25e0b157 1706 git_vector_uniq(&list, git__free);
aec87f71 1707
25e0b157
RB
1708 remotes_list->strings =
1709 (char **)git_vector_detach(&remotes_list->count, NULL, &list);
8171998f 1710
4376f7f6 1711 return 0;
8171998f 1712}
a209a025 1713
67dad09b 1714const git_transfer_progress* git_remote_stats(git_remote *remote)
d57c47dc
BS
1715{
1716 assert(remote);
1717 return &remote->stats;
1718}
1719
11f6ad5f 1720git_remote_autotag_option_t git_remote_autotag(const git_remote *remote)
f70e466f
CMN
1721{
1722 return remote->download_tags;
1723}
1724
35a8a8c5 1725int git_remote_set_autotag(git_repository *repo, const char *remote, git_remote_autotag_option_t value)
f70e466f 1726{
35a8a8c5
CMN
1727 git_buf var = GIT_BUF_INIT;
1728 git_config *config;
1729 int error;
1730
1731 assert(repo && remote);
1732
1733 if ((error = ensure_remote_name_is_valid(remote)) < 0)
1734 return error;
1735
1736 if ((error = git_repository_config__weakptr(&config, repo)) < 0)
1737 return error;
1738
1739 if ((error = git_buf_printf(&var, CONFIG_TAGOPT_FMT, remote)))
1740 return error;
1741
1742 switch (value) {
1743 case GIT_REMOTE_DOWNLOAD_TAGS_NONE:
1744 error = git_config_set_string(config, var.ptr, "--no-tags");
1745 break;
1746 case GIT_REMOTE_DOWNLOAD_TAGS_ALL:
1747 error = git_config_set_string(config, var.ptr, "--tags");
1748 break;
1749 case GIT_REMOTE_DOWNLOAD_TAGS_AUTO:
1750 error = git_config_delete_entry(config, var.ptr);
1751 if (error == GIT_ENOTFOUND)
1752 error = 0;
1753 break;
1754 default:
1755 giterr_set(GITERR_INVALID, "Invalid value for the tagopt setting");
1756 error = -1;
1757 }
1758
1759 git_buf_free(&var);
1760 return error;
f70e466f 1761}
fcccf304 1762
5f473947
L
1763int git_remote_prune_refs(const git_remote *remote)
1764{
1765 return remote->prune_refs;
1766}
1767
fcccf304 1768static int rename_remote_config_section(
1769 git_repository *repo,
1770 const char *old_name,
1771 const char *new_name)
1772{
1773 git_buf old_section_name = GIT_BUF_INIT,
1774 new_section_name = GIT_BUF_INIT;
1775 int error = -1;
1776
1777 if (git_buf_printf(&old_section_name, "remote.%s", old_name) < 0)
1778 goto cleanup;
1779
40e48ea4 1780 if (new_name &&
1781 (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0))
1782 goto cleanup;
fcccf304 1783
1784 error = git_config_rename_section(
1785 repo,
1786 git_buf_cstr(&old_section_name),
40e48ea4 1787 new_name ? git_buf_cstr(&new_section_name) : NULL);
fcccf304 1788
1789cleanup:
1790 git_buf_free(&old_section_name);
1791 git_buf_free(&new_section_name);
1792
1793 return error;
1794}
1795
96869a4e 1796struct update_data {
fcccf304 1797 git_config *config;
1798 const char *old_remote_name;
1799 const char *new_remote_name;
1800};
1801
1802static int update_config_entries_cb(
1803 const git_config_entry *entry,
1804 void *payload)
1805{
1806 struct update_data *data = (struct update_data *)payload;
1807
1808 if (strcmp(entry->value, data->old_remote_name))
1809 return 0;
1810
25e0b157
RB
1811 return git_config_set_string(
1812 data->config, entry->name, data->new_remote_name);
fcccf304 1813}
1814
1815static int update_branch_remote_config_entry(
1816 git_repository *repo,
1817 const char *old_name,
1818 const char *new_name)
1819{
96869a4e
RB
1820 int error;
1821 struct update_data data = { NULL };
fcccf304 1822
96869a4e
RB
1823 if ((error = git_repository_config__weakptr(&data.config, repo)) < 0)
1824 return error;
fcccf304 1825
fcccf304 1826 data.old_remote_name = old_name;
1827 data.new_remote_name = new_name;
1828
25e0b157 1829 return git_config_foreach_match(
96869a4e 1830 data.config, "branch\\..+\\.remote", update_config_entries_cb, &data);
fcccf304 1831}
1832
fcccf304 1833static int rename_one_remote_reference(
d1544564 1834 git_reference *reference_in,
fcccf304 1835 const char *old_remote_name,
1836 const char *new_remote_name)
1837{
96869a4e 1838 int error;
d1544564
CMN
1839 git_reference *ref = NULL, *dummy = NULL;
1840 git_buf namespace = GIT_BUF_INIT, old_namespace = GIT_BUF_INIT;
fcccf304 1841 git_buf new_name = GIT_BUF_INIT;
ccf6ce5c 1842 git_buf log_message = GIT_BUF_INIT;
d1544564
CMN
1843 size_t pfx_len;
1844 const char *target;
fcccf304 1845
d1544564
CMN
1846 if ((error = git_buf_printf(&namespace, GIT_REFS_REMOTES_DIR "%s/", new_remote_name)) < 0)
1847 return error;
1848
1849 pfx_len = strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name) + 1;
1850 git_buf_puts(&new_name, namespace.ptr);
1851 if ((error = git_buf_puts(&new_name, git_reference_name(reference_in) + pfx_len)) < 0)
ccf6ce5c 1852 goto cleanup;
fcccf304 1853
ccf6ce5c
BS
1854 if ((error = git_buf_printf(&log_message,
1855 "renamed remote %s to %s",
1856 old_remote_name, new_remote_name)) < 0)
1857 goto cleanup;
fcccf304 1858
d1544564 1859 if ((error = git_reference_rename(&ref, reference_in, git_buf_cstr(&new_name), 1,
659cf202 1860 git_buf_cstr(&log_message))) < 0)
d1544564
CMN
1861 goto cleanup;
1862
1863 if (git_reference_type(ref) != GIT_REF_SYMBOLIC)
1864 goto cleanup;
1865
1866 /* Handle refs like origin/HEAD -> origin/master */
1867 target = git_reference_symbolic_target(ref);
1868 if ((error = git_buf_printf(&old_namespace, GIT_REFS_REMOTES_DIR "%s/", old_remote_name)) < 0)
1869 goto cleanup;
1870
1871 if (git__prefixcmp(target, old_namespace.ptr))
1872 goto cleanup;
1873
1874 git_buf_clear(&new_name);
1875 git_buf_puts(&new_name, namespace.ptr);
1876 if ((error = git_buf_puts(&new_name, target + pfx_len)) < 0)
1877 goto cleanup;
1878
1879 error = git_reference_symbolic_set_target(&dummy, ref, git_buf_cstr(&new_name),
659cf202 1880 git_buf_cstr(&log_message));
d1544564
CMN
1881
1882 git_reference_free(dummy);
ccf6ce5c
BS
1883
1884cleanup:
d1544564
CMN
1885 git_reference_free(reference_in);
1886 git_reference_free(ref);
1887 git_buf_free(&namespace);
1888 git_buf_free(&old_namespace);
fcccf304 1889 git_buf_free(&new_name);
ccf6ce5c 1890 git_buf_free(&log_message);
fcccf304 1891 return error;
1892}
1893
1894static int rename_remote_references(
1895 git_repository *repo,
1896 const char *old_name,
1897 const char *new_name)
1898{
96869a4e 1899 int error;
a52ab4b8 1900 git_buf buf = GIT_BUF_INIT;
56960b83 1901 git_reference *ref;
9bd89d96 1902 git_reference_iterator *iter;
fcccf304 1903
a52ab4b8 1904 if ((error = git_buf_printf(&buf, GIT_REFS_REMOTES_DIR "%s/*", old_name)) < 0)
96869a4e 1905 return error;
fcccf304 1906
a52ab4b8
CMN
1907 error = git_reference_iterator_glob_new(&iter, repo, git_buf_cstr(&buf));
1908 git_buf_free(&buf);
9bd89d96 1909
a52ab4b8
CMN
1910 if (error < 0)
1911 return error;
1912
1913 while ((error = git_reference_next(&ref, iter)) == 0) {
96869a4e
RB
1914 if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0)
1915 break;
9bd89d96
CMN
1916 }
1917
1918 git_reference_iterator_free(iter);
fcccf304 1919
96869a4e 1920 return (error == GIT_ITEROVER) ? 0 : error;
fcccf304 1921}
1922
72bca13e 1923static int rename_fetch_refspecs(git_vector *problems, git_remote *remote, const char *new_name)
fcccf304 1924{
1925 git_config *config;
4330ab26 1926 git_buf base = GIT_BUF_INIT, var = GIT_BUF_INIT, val = GIT_BUF_INIT;
1be680c4 1927 const git_refspec *spec;
4330ab26 1928 size_t i;
25e0b157 1929 int error = 0;
fcccf304 1930
dab89f9b 1931 if ((error = git_repository_config__weakptr(&config, remote->repo)) < 0)
25e0b157
RB
1932 return error;
1933
72bca13e
CMN
1934 if ((error = git_vector_init(problems, 1, NULL)) < 0)
1935 return error;
1936
25e0b157
RB
1937 if ((error = git_buf_printf(
1938 &base, "+refs/heads/*:refs/remotes/%s/*", remote->name)) < 0)
1939 return error;
dab89f9b 1940
1be680c4 1941 git_vector_foreach(&remote->refspecs, i, spec) {
4330ab26
CMN
1942 if (spec->push)
1943 continue;
fcccf304 1944
dab89f9b 1945 /* Does the dst part of the refspec follow the expected format? */
5a49ff9f 1946 if (strcmp(git_buf_cstr(&base), spec->string)) {
72bca13e 1947 char *dup;
fcccf304 1948
72bca13e
CMN
1949 dup = git__strdup(spec->string);
1950 GITERR_CHECK_ALLOC(dup);
1951
1952 if ((error = git_vector_insert(problems, dup)) < 0)
25e0b157 1953 break;
c7b3e1b3 1954
4330ab26
CMN
1955 continue;
1956 }
fcccf304 1957
4330ab26 1958 /* If we do want to move it to the new section */
fcccf304 1959
dab89f9b
RB
1960 git_buf_clear(&val);
1961 git_buf_clear(&var);
fcccf304 1962
dab89f9b
RB
1963 if (git_buf_printf(
1964 &val, "+refs/heads/*:refs/remotes/%s/*", new_name) < 0 ||
1965 git_buf_printf(&var, "remote.%s.fetch", new_name) < 0)
1966 {
1967 error = -1;
25e0b157 1968 break;
dab89f9b 1969 }
fcccf304 1970
dab89f9b
RB
1971 if ((error = git_config_set_string(
1972 config, git_buf_cstr(&var), git_buf_cstr(&val))) < 0)
25e0b157 1973 break;
4330ab26 1974 }
fcccf304 1975
4330ab26
CMN
1976 git_buf_free(&base);
1977 git_buf_free(&var);
1978 git_buf_free(&val);
72bca13e
CMN
1979
1980 if (error < 0) {
1981 char *str;
1982 git_vector_foreach(problems, i, str)
1983 git__free(str);
1984
1985 git_vector_free(problems);
1986 }
1987
fcccf304 1988 return error;
1989}
1990
46c8f7f8 1991int git_remote_rename(git_strarray *out, git_repository *repo, const char *name, const char *new_name)
fcccf304 1992{
1993 int error;
46c8f7f8 1994 git_vector problem_refspecs = GIT_VECTOR_INIT;
64bcf567 1995 git_remote *remote = NULL;
fcccf304 1996
46c8f7f8 1997 assert(out && repo && name && new_name);
fcccf304 1998
209425ce 1999 if ((error = git_remote_lookup(&remote, repo, name)) < 0)
cce27d82 2000 return error;
79000951 2001
fcccf304 2002 if ((error = ensure_remote_name_is_valid(new_name)) < 0)
46c8f7f8 2003 goto cleanup;
fcccf304 2004
46c8f7f8
CMN
2005 if ((error = ensure_remote_doesnot_exist(repo, new_name)) < 0)
2006 goto cleanup;
fcccf304 2007
46c8f7f8
CMN
2008 if ((error = rename_remote_config_section(repo, name, new_name)) < 0)
2009 goto cleanup;
fcccf304 2010
46c8f7f8
CMN
2011 if ((error = update_branch_remote_config_entry(repo, name, new_name)) < 0)
2012 goto cleanup;
fcccf304 2013
46c8f7f8
CMN
2014 if ((error = rename_remote_references(repo, name, new_name)) < 0)
2015 goto cleanup;
fcccf304 2016
72bca13e 2017 if ((error = rename_fetch_refspecs(&problem_refspecs, remote, new_name)) < 0)
46c8f7f8 2018 goto cleanup;
fcccf304 2019
72bca13e
CMN
2020 out->count = problem_refspecs.length;
2021 out->strings = (char **) problem_refspecs.contents;
2022
46c8f7f8
CMN
2023cleanup:
2024 if (error < 0)
2025 git_vector_free(&problem_refspecs);
fcccf304 2026
46c8f7f8
CMN
2027 git_remote_free(remote);
2028 return error;
fcccf304 2029}
b0f6e45d 2030
2bca5b67 2031int git_remote_is_valid_name(
2032 const char *remote_name)
2033{
2034 git_buf buf = GIT_BUF_INIT;
2035 git_refspec refspec;
2036 int error = -1;
2037
2038 if (!remote_name || *remote_name == '\0')
2039 return 0;
2040
2041 git_buf_printf(&buf, "refs/heads/test:refs/remotes/%s/test", remote_name);
2042 error = git_refspec__parse(&refspec, git_buf_cstr(&buf), true);
2043
2044 git_buf_free(&buf);
2045 git_refspec__free(&refspec);
2046
2047 giterr_clear();
2048 return error == 0;
2049}
4330ab26
CMN
2050
2051git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname)
2052{
2053 git_refspec *spec;
2054 size_t i;
2055
af613ecd 2056 git_vector_foreach(&remote->active_refspecs, i, spec) {
4330ab26
CMN
2057 if (spec->push)
2058 continue;
2059
2060 if (git_refspec_src_matches(spec, refname))
2061 return spec;
2062 }
2063
2064 return NULL;
2065}
2066
2067git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname)
2068{
2069 git_refspec *spec;
2070 size_t i;
2071
af613ecd 2072 git_vector_foreach(&remote->active_refspecs, i, spec) {
4330ab26
CMN
2073 if (spec->push)
2074 continue;
2075
2076 if (git_refspec_dst_matches(spec, refname))
2077 return spec;
2078 }
2079
2080 return NULL;
2081}
2082
77254990 2083int git_remote_add_fetch(git_repository *repo, const char *remote, const char *refspec)
4330ab26 2084{
77254990 2085 return write_add_refspec(repo, remote, refspec, true);
4330ab26
CMN
2086}
2087
77254990 2088int git_remote_add_push(git_repository *repo, const char *remote, const char *refspec)
4330ab26 2089{
77254990 2090 return write_add_refspec(repo, remote, refspec, false);
266af6d8
CMN
2091}
2092
2093static int set_refspecs(git_remote *remote, git_strarray *array, int push)
2094{
2095 git_vector *vec = &remote->refspecs;
2096 git_refspec *spec;
2097 size_t i;
2098
2099 /* Start by removing any refspecs of the same type */
2100 for (i = 0; i < vec->length; i++) {
2101 spec = git_vector_get(vec, i);
2102 if (spec->push != push)
2103 continue;
2104
2105 git_refspec__free(spec);
2106 git__free(spec);
2107 git_vector_remove(vec, i);
2108 i--;
2109 }
2110
2111 /* And now we add the new ones */
2112
2113 for (i = 0; i < array->count; i++) {
2114 if (add_refspec(remote, array->strings[i], !push) < 0)
2115 return -1;
2116 }
2117
c300d84a 2118 return 0;
266af6d8
CMN
2119}
2120
11f6ad5f 2121static int copy_refspecs(git_strarray *array, const git_remote *remote, unsigned int push)
bc6374ea
CMN
2122{
2123 size_t i;
2124 git_vector refspecs;
2125 git_refspec *spec;
2126 char *dup;
2127
2128 if (git_vector_init(&refspecs, remote->refspecs.length, NULL) < 0)
2129 return -1;
2130
2131 git_vector_foreach(&remote->refspecs, i, spec) {
2132 if (spec->push != push)
2133 continue;
2134
1be680c4 2135 if ((dup = git__strdup(spec->string)) == NULL)
bc6374ea 2136 goto on_error;
bc6374ea
CMN
2137
2138 if (git_vector_insert(&refspecs, dup) < 0) {
2139 git__free(dup);
2140 goto on_error;
2141 }
2142 }
2143
2144 array->strings = (char **)refspecs.contents;
2145 array->count = refspecs.length;
2146
2147 return 0;
2148
2149on_error:
9cfce273 2150 git_vector_free_deep(&refspecs);
bc6374ea
CMN
2151
2152 return -1;
2153}
2154
11f6ad5f 2155int git_remote_get_fetch_refspecs(git_strarray *array, const git_remote *remote)
bc6374ea
CMN
2156{
2157 return copy_refspecs(array, remote, false);
2158}
2159
11f6ad5f 2160int git_remote_get_push_refspecs(git_strarray *array, const git_remote *remote)
bc6374ea
CMN
2161{
2162 return copy_refspecs(array, remote, true);
2163}
1ffd0806 2164
11f6ad5f 2165size_t git_remote_refspec_count(const git_remote *remote)
1ffd0806
CMN
2166{
2167 return remote->refspecs.length;
2168}
2169
11f6ad5f 2170const git_refspec *git_remote_get_refspec(const git_remote *remote, size_t n)
1ffd0806
CMN
2171{
2172 return git_vector_get(&remote->refspecs, n);
2173}
b9f81997 2174
bc91347b 2175int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version)
b9f81997 2176{
bc91347b
RB
2177 GIT_INIT_STRUCTURE_FROM_TEMPLATE(
2178 opts, version, git_remote_callbacks, GIT_REMOTE_CALLBACKS_INIT);
2179 return 0;
b9f81997 2180}
40e48ea4 2181
5cdac19c
CMN
2182/* asserts a branch.<foo>.remote format */
2183static const char *name_offset(size_t *len_out, const char *name)
40e48ea4 2184{
5cdac19c
CMN
2185 size_t prefix_len;
2186 const char *dot;
40e48ea4 2187
5cdac19c
CMN
2188 prefix_len = strlen("remote.");
2189 dot = strchr(name + prefix_len, '.');
40e48ea4 2190
5cdac19c 2191 assert(dot);
40e48ea4 2192
5cdac19c
CMN
2193 *len_out = dot - name - prefix_len;
2194 return name + prefix_len;
40e48ea4 2195}
2196
2197static int remove_branch_config_related_entries(
2198 git_repository *repo,
2199 const char *remote_name)
2200{
2201 int error;
2202 git_config *config;
5cdac19c
CMN
2203 git_config_entry *entry;
2204 git_config_iterator *iter;
2205 git_buf buf = GIT_BUF_INIT;
40e48ea4 2206
2207 if ((error = git_repository_config__weakptr(&config, repo)) < 0)
2208 return error;
2209
5cdac19c 2210 if ((error = git_config_iterator_glob_new(&iter, config, "branch\\..+\\.remote")) < 0)
40e48ea4 2211 return error;
2212
5cdac19c
CMN
2213 /* find any branches with us as upstream and remove that config */
2214 while ((error = git_config_next(&entry, iter)) == 0) {
2215 const char *branch;
2216 size_t branch_len;
40e48ea4 2217
5cdac19c
CMN
2218 if (strcmp(remote_name, entry->value))
2219 continue;
40e48ea4 2220
5cdac19c 2221 branch = name_offset(&branch_len, entry->name);
40e48ea4 2222
5cdac19c
CMN
2223 git_buf_clear(&buf);
2224 if (git_buf_printf(&buf, "branch.%.*s.merge", (int)branch_len, branch) < 0)
2225 break;
2226
2227 if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0)
2228 break;
2229
2230 git_buf_clear(&buf);
2231 if (git_buf_printf(&buf, "branch.%.*s.remote", (int)branch_len, branch) < 0)
2232 break;
2233
2234 if ((error = git_config_delete_entry(config, git_buf_cstr(&buf))) < 0)
2235 break;
40e48ea4 2236 }
2237
5cdac19c
CMN
2238 if (error == GIT_ITEROVER)
2239 error = 0;
2240
2241 git_buf_free(&buf);
2242 git_config_iterator_free(iter);
40e48ea4 2243 return error;
2244}
2245
8a9419aa 2246static int remove_refs(git_repository *repo, const git_refspec *spec)
ec8a949a 2247{
8a9419aa
CMN
2248 git_reference_iterator *iter = NULL;
2249 git_vector refs;
ec8a949a 2250 const char *name;
8a9419aa 2251 char *dup;
ec8a949a 2252 int error;
8a9419aa 2253 size_t i;
ec8a949a 2254
8a9419aa 2255 if ((error = git_vector_init(&refs, 8, NULL)) < 0)
ec8a949a
CMN
2256 return error;
2257
8a9419aa
CMN
2258 if ((error = git_reference_iterator_new(&iter, repo)) < 0)
2259 goto cleanup;
2260
ec8a949a 2261 while ((error = git_reference_next_name(&name, iter)) == 0) {
8a9419aa
CMN
2262 if (!git_refspec_dst_matches(spec, name))
2263 continue;
2264
2265 dup = git__strdup(name);
2266 if (!dup) {
2267 error = -1;
2268 goto cleanup;
2269 }
ec8a949a 2270
8a9419aa
CMN
2271 if ((error = git_vector_insert(&refs, dup)) < 0)
2272 goto cleanup;
2273 }
ec8a949a
CMN
2274 if (error == GIT_ITEROVER)
2275 error = 0;
8a9419aa
CMN
2276 if (error < 0)
2277 goto cleanup;
2278
2279 git_vector_foreach(&refs, i, name) {
2280 if ((error = git_reference_remove(repo, name)) < 0)
2281 break;
2282 }
ec8a949a 2283
8a9419aa
CMN
2284cleanup:
2285 git_reference_iterator_free(iter);
2286 git_vector_foreach(&refs, i, dup) {
2287 git__free(dup);
2288 }
2289 git_vector_free(&refs);
ec8a949a
CMN
2290 return error;
2291}
2292
2293static int remove_remote_tracking(git_repository *repo, const char *remote_name)
2294{
2295 git_remote *remote;
2296 int error;
2297 size_t i, count;
2298
2299 /* we want to use what's on the config, regardless of changes to the instance in memory */
209425ce 2300 if ((error = git_remote_lookup(&remote, repo, remote_name)) < 0)
ec8a949a
CMN
2301 return error;
2302
2303 count = git_remote_refspec_count(remote);
2304 for (i = 0; i < count; i++) {
2305 const git_refspec *refspec = git_remote_get_refspec(remote, i);
2306
2307 /* shouldn't ever actually happen */
2308 if (refspec == NULL)
2309 continue;
2310
8a9419aa 2311 if ((error = remove_refs(repo, refspec)) < 0)
ec8a949a
CMN
2312 break;
2313 }
2314
2315 git_remote_free(remote);
2316 return error;
2317}
2318
262eec23 2319int git_remote_delete(git_repository *repo, const char *name)
40e48ea4 2320{
2321 int error;
40e48ea4 2322
262eec23 2323 assert(repo && name);
ec8a949a 2324
262eec23
CMN
2325 if ((error = remove_branch_config_related_entries(repo, name)) < 0 ||
2326 (error = remove_remote_tracking(repo, name)) < 0 ||
2327 (error = rename_remote_config_section(repo, name, NULL)) < 0)
ec8a949a
CMN
2328 return error;
2329
40e48ea4 2330 return 0;
2331}
d22db24f
CMN
2332
2333int git_remote_default_branch(git_buf *out, git_remote *remote)
2334{
2335 const git_remote_head **heads;
2336 const git_remote_head *guess = NULL;
2337 const git_oid *head_id;
2338 size_t heads_len, i;
2339 int error;
2340
dc8adda4
JG
2341 assert(out);
2342
d22db24f
CMN
2343 if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0)
2344 return error;
2345
2346 if (heads_len == 0)
2347 return GIT_ENOTFOUND;
2348
0cdaa376
CMN
2349 if (strcmp(heads[0]->name, GIT_HEAD_FILE))
2350 return GIT_ENOTFOUND;
2351
d22db24f
CMN
2352 git_buf_sanitize(out);
2353 /* the first one must be HEAD so if that has the symref info, we're done */
2354 if (heads[0]->symref_target)
2355 return git_buf_puts(out, heads[0]->symref_target);
2356
2357 /*
2358 * If there's no symref information, we have to look over them
2359 * and guess. We return the first match unless the master
2360 * branch is a candidate. Then we return the master branch.
2361 */
2362 head_id = &heads[0]->oid;
2363
2364 for (i = 1; i < heads_len; i++) {
2365 if (git_oid_cmp(head_id, &heads[i]->oid))
2366 continue;
2367
38952604
CMN
2368 if (git__prefixcmp(heads[i]->name, GIT_REFS_HEADS_DIR))
2369 continue;
2370
d22db24f
CMN
2371 if (!guess) {
2372 guess = heads[i];
2373 continue;
2374 }
2375
2376 if (!git__strcmp(GIT_REFS_HEADS_MASTER_FILE, heads[i]->name)) {
2377 guess = heads[i];
2378 break;
2379 }
2380 }
2381
2382 if (!guess)
2383 return GIT_ENOTFOUND;
2384
2385 return git_buf_puts(out, guess->name);
2386}
3149547b 2387
fe794b2e 2388int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts)
3149547b 2389{
3149547b 2390 size_t i;
fe794b2e
CMN
2391 int error;
2392 git_push *push;
64e3e6d4 2393 git_refspec *spec;
8f0104ec 2394 const git_remote_callbacks *cbs = NULL;
d29c5412 2395 const git_strarray *custom_headers = NULL;
3149547b 2396
fe794b2e 2397 assert(remote);
3149547b 2398
d29c5412 2399 if (opts) {
8f0104ec 2400 cbs = &opts->callbacks;
d29c5412
MB
2401 custom_headers = &opts->custom_headers;
2402 }
8f0104ec 2403
fe794b2e 2404 if (!git_remote_connected(remote) &&
d29c5412 2405 (error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, custom_headers)) < 0)
fe794b2e
CMN
2406 goto cleanup;
2407
1034f1b5 2408 free_refspecs(&remote->active_refspecs);
69f0032b 2409 if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
1034f1b5
POL
2410 goto cleanup;
2411
fe794b2e
CMN
2412 if (remote->push) {
2413 git_push_free(remote->push);
2414 remote->push = NULL;
2415 }
2416
2417 if ((error = git_push_new(&remote->push, remote)) < 0)
3149547b
CMN
2418 return error;
2419
fe794b2e 2420 push = remote->push;
3149547b
CMN
2421
2422 if (opts && (error = git_push_set_options(push, opts)) < 0)
2423 goto cleanup;
2424
64e3e6d4
CMN
2425 if (refspecs && refspecs->count > 0) {
2426 for (i = 0; i < refspecs->count; i++) {
2427 if ((error = git_push_add_refspec(push, refspecs->strings[i])) < 0)
2428 goto cleanup;
2429 }
2430 } else {
2431 git_vector_foreach(&remote->refspecs, i, spec) {
2432 if (!spec->push)
2433 continue;
2434 if ((error = git_push_add_refspec(push, spec->string)) < 0)
2435 goto cleanup;
2436 }
3149547b
CMN
2437 }
2438
8f0104ec 2439 if ((error = git_push_finish(push, cbs)) < 0)
3149547b
CMN
2440 goto cleanup;
2441
8f0104ec 2442 if (cbs && cbs->push_update_reference &&
52ee0e8e 2443 (error = git_push_status_foreach(push, cbs->push_update_reference, cbs->payload)) < 0)
3149547b
CMN
2444 goto cleanup;
2445
3149547b 2446cleanup:
fe794b2e
CMN
2447 return error;
2448}
2449
412a3808 2450int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts)
fe794b2e
CMN
2451{
2452 int error;
8f0104ec 2453 const git_remote_callbacks *cbs = NULL;
d29c5412 2454 const git_strarray *custom_headers = NULL;
8f0104ec
CMN
2455
2456 if (opts) {
2457 GITERR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
2458 cbs = &opts->callbacks;
d29c5412 2459 custom_headers = &opts->custom_headers;
8f0104ec 2460 }
fe794b2e
CMN
2461
2462 assert(remote && refspecs);
2463
d29c5412 2464 if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, custom_headers)) < 0)
fe794b2e
CMN
2465 return error;
2466
2467 if ((error = git_remote_upload(remote, refspecs, opts)) < 0)
2468 return error;
2469
35a8a8c5 2470 error = git_remote_update_tips(remote, cbs, 0, 0, NULL);
fe794b2e 2471
3149547b 2472 git_remote_disconnect(remote);
3149547b
CMN
2473 return error;
2474}
ec0c4c40
PS
2475
2476#define PREFIX "url"
2477#define SUFFIX_FETCH "insteadof"
2478#define SUFFIX_PUSH "pushinsteadof"
2479
2480char *apply_insteadof(git_config *config, const char *url, int direction)
2481{
2482 size_t match_length, prefix_length, suffix_length;
2483 char *replacement = NULL;
2484 const char *regexp;
2485
2486 git_buf result = GIT_BUF_INIT;
2487 git_config_entry *entry;
2488 git_config_iterator *iter;
2489
2490 assert(config);
2491 assert(url);
2492 assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
2493
2494 /* Add 1 to prefix/suffix length due to the additional escaped dot */
2495 prefix_length = strlen(PREFIX) + 1;
2496 if (direction == GIT_DIRECTION_FETCH) {
2497 regexp = PREFIX "\\..*\\." SUFFIX_FETCH;
2498 suffix_length = strlen(SUFFIX_FETCH) + 1;
2499 } else {
2500 regexp = PREFIX "\\..*\\." SUFFIX_PUSH;
2501 suffix_length = strlen(SUFFIX_PUSH) + 1;
2502 }
2503
2785544f
CMN
2504 if (git_config_iterator_glob_new(&iter, config, regexp) < 0)
2505 return NULL;
ec0c4c40
PS
2506
2507 match_length = 0;
2508 while (git_config_next(&entry, iter) == 0) {
2509 size_t n, replacement_length;
2510
2511 /* Check if entry value is a prefix of URL */
2512 if (git__prefixcmp(url, entry->value))
2513 continue;
2514 /* Check if entry value is longer than previous
2515 * prefixes */
2516 if ((n = strlen(entry->value)) <= match_length)
2517 continue;
2518
2519 git__free(replacement);
2520 match_length = n;
2521
2522 /* Cut off prefix and suffix of the value */
2523 replacement_length =
2524 strlen(entry->name) - (prefix_length + suffix_length);
2525 replacement = git__strndup(entry->name + prefix_length,
2526 replacement_length);
2527 }
2528
2529 git_config_iterator_free(iter);
2530
2531 if (match_length == 0)
2532 return git__strdup(url);
2533
2534 git_buf_printf(&result, "%s%s", replacement, url + match_length);
2535
2536 git__free(replacement);
2537
2538 return result.ptr;
2539}