]> git.proxmox.com Git - libgit2.git/blame - src/remote.c
Add some missing const declarations.
[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"
9c82357b 21
af613ecd
CMN
22static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs);
23
4330ab26 24static int add_refspec(git_remote *remote, const char *string, bool is_fetch)
9c82357b 25{
4330ab26 26 git_refspec *spec;
9c82357b 27
4330ab26
CMN
28 spec = git__calloc(1, sizeof(git_refspec));
29 GITERR_CHECK_ALLOC(spec);
30
1be680c4
CMN
31 if (git_refspec__parse(spec, string, is_fetch) < 0) {
32 git__free(spec);
33 return -1;
34 }
4330ab26
CMN
35
36 spec->push = !is_fetch;
1be680c4
CMN
37 if (git_vector_insert(&remote->refspecs, spec) < 0) {
38 git_refspec__free(spec);
39 git__free(spec);
40 return -1;
41 }
9c82357b 42
4330ab26 43 return 0;
9c82357b
CMN
44}
45
24f2f94e
CMN
46static int download_tags_value(git_remote *remote, git_config *cfg)
47{
9f77b3f6 48 const git_config_entry *ce;
24f2f94e
CMN
49 git_buf buf = GIT_BUF_INIT;
50 int error;
51
df50512a 52 /* The 0 value is the default (auto), let's see if we need to change it */
24f2f94e
CMN
53 if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0)
54 return -1;
55
9f77b3f6 56 error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false);
24f2f94e 57 git_buf_free(&buf);
24f2f94e 58
9f77b3f6
RB
59 if (!error && ce && ce->value) {
60 if (!strcmp(ce->value, "--no-tags"))
61 remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
62 else if (!strcmp(ce->value, "--tags"))
63 remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
bf6bebe2 64 }
24f2f94e
CMN
65
66 return error;
67}
68
032ba9e4 69static int ensure_remote_name_is_valid(const char *name)
70{
2bca5b67 71 int error = 0;
032ba9e4 72
2bca5b67 73 if (!git_remote_is_valid_name(name)) {
032ba9e4 74 giterr_set(
75 GITERR_CONFIG,
76 "'%s' is not a valid remote name.", name);
77 error = GIT_EINVALIDSPEC;
78 }
79
80 return error;
81}
82
80fc7d6b 83static int get_check_cert(int *out, git_repository *repo)
af6dab7e
PK
84{
85 git_config *cfg;
86 const char *val;
80fc7d6b
ET
87 int error = 0;
88
89 assert(out && repo);
af6dab7e 90
80fc7d6b
ET
91 /* By default, we *DO* want to verify the certificate. */
92 *out = 1;
af6dab7e
PK
93
94 /* Go through the possible sources for SSL verification settings, from
95 * most specific to least specific. */
96
97 /* GIT_SSL_NO_VERIFY environment variable */
8f2a3d62 98 if ((val = getenv("GIT_SSL_NO_VERIFY")) != NULL)
80fc7d6b 99 return git_config_parse_bool(out, val);
af6dab7e
PK
100
101 /* http.sslVerify config setting */
80fc7d6b
ET
102 if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
103 return error;
af6dab7e 104
9f77b3f6 105 *out = git_config__get_bool_force(cfg, "http.sslverify", 1);
80fc7d6b 106 return 0;
af6dab7e
PK
107}
108
874dcb25 109static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
778e1c73
CMN
110{
111 git_remote *remote;
44f36f6e
BS
112 git_buf fetchbuf = GIT_BUF_INIT;
113 int error = -1;
778e1c73 114
4bef3565 115 /* name is optional */
874dcb25 116 assert(out && repo && url);
617bfdf4 117
59bccf33 118 remote = git__calloc(1, sizeof(git_remote));
4376f7f6 119 GITERR_CHECK_ALLOC(remote);
778e1c73 120
778e1c73 121 remote->repo = repo;
b0f6e45d 122 remote->update_fetchhead = 1;
617bfdf4 123
80fc7d6b
ET
124 if (get_check_cert(&remote->check_cert, repo) < 0)
125 goto on_error;
126
4376f7f6 127 if (git_vector_init(&remote->refs, 32, NULL) < 0)
44f36f6e 128 goto on_error;
d88d4311 129
778e1c73 130 remote->url = git__strdup(url);
4376f7f6 131 GITERR_CHECK_ALLOC(remote->url);
778e1c73 132
617bfdf4
CMN
133 if (name != NULL) {
134 remote->name = git__strdup(name);
4376f7f6 135 GITERR_CHECK_ALLOC(remote->name);
617bfdf4
CMN
136 }
137
baaa8a44 138 if (fetch != NULL) {
4330ab26 139 if (add_refspec(remote, fetch, true) < 0)
baaa8a44
CMN
140 goto on_error;
141 }
142
215af2cc 143 if (!name)
144 /* A remote without a name doesn't download tags */
c648d4a8 145 remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
c648d4a8 146
778e1c73 147 *out = remote;
44f36f6e 148 git_buf_free(&fetchbuf);
4376f7f6 149 return 0;
baaa8a44
CMN
150
151on_error:
152 git_remote_free(remote);
44f36f6e
BS
153 git_buf_free(&fetchbuf);
154 return error;
778e1c73
CMN
155}
156
592f466c
BS
157static int ensure_remote_doesnot_exist(git_repository *repo, const char *name)
158{
159 int error;
160 git_remote *remote;
161
162 error = git_remote_load(&remote, repo, name);
163
164 if (error == GIT_ENOTFOUND)
165 return 0;
166
167 if (error < 0)
168 return error;
169
170 git_remote_free(remote);
171
172 giterr_set(
173 GITERR_CONFIG,
174 "Remote '%s' already exists.", name);
175
176 return GIT_EEXISTS;
177}
178
f19304d2 179
874dcb25
BS
180int git_remote_create(git_remote **out, git_repository *repo, const char *name, const char *url)
181{
182 git_buf buf = GIT_BUF_INIT;
c5193e3c 183 git_remote *remote = NULL;
874dcb25
BS
184 int error;
185
186 if ((error = ensure_remote_name_is_valid(name)) < 0)
187 return error;
188
f19304d2 189 if ((error = ensure_remote_doesnot_exist(repo, name)) < 0)
190 return error;
191
874dcb25
BS
192 if (git_buf_printf(&buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0)
193 return -1;
194
c5193e3c 195 if (create_internal(&remote, repo, name, url, git_buf_cstr(&buf)) < 0)
874dcb25
BS
196 goto on_error;
197
198 git_buf_free(&buf);
199
c5193e3c 200 if (git_remote_save(remote) < 0)
874dcb25
BS
201 goto on_error;
202
c5193e3c 203 *out = remote;
204
874dcb25
BS
205 return 0;
206
207on_error:
208 git_buf_free(&buf);
c5193e3c 209 git_remote_free(remote);
874dcb25
BS
210 return -1;
211}
212
40b99d05
VG
213int git_remote_create_with_fetchspec(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
214{
40b99d05
VG
215 git_remote *remote = NULL;
216 int error;
217
218 if ((error = ensure_remote_name_is_valid(name)) < 0)
219 return error;
220
221 if ((error = ensure_remote_doesnot_exist(repo, name)) < 0)
222 return error;
874dcb25 223
0fe522d1 224 if (create_internal(&remote, repo, name, url, fetch) < 0)
874dcb25
BS
225 goto on_error;
226
c5193e3c 227 if (git_remote_save(remote) < 0)
874dcb25
BS
228 goto on_error;
229
c5193e3c 230 *out = remote;
231
874dcb25
BS
232 return 0;
233
234on_error:
c5193e3c 235 git_remote_free(remote);
874dcb25
BS
236 return -1;
237}
238
0642c143 239int git_remote_create_inmemory(git_remote **out, git_repository *repo, const char *fetch, const char *url)
874dcb25
BS
240{
241 int error;
242 git_remote *remote;
243
79000951 244 if ((error = create_internal(&remote, repo, NULL, url, fetch)) < 0)
874dcb25
BS
245 return error;
246
874dcb25
BS
247 *out = remote;
248 return 0;
249}
250
40ef47dd
AS
251int git_remote_dup(git_remote **dest, const git_remote *source)
252{
253 int error;
254 git_remote *remote = git__calloc(1, sizeof(git_remote));
255 GITERR_CHECK_ALLOC(remote);
256
40ef47dd
AS
257 if (source->name != NULL) {
258 remote->name = git__strdup(source->name);
259 GITERR_CHECK_ALLOC(remote->name);
260 }
261
262 if (source->url != NULL) {
263 remote->url = git__strdup(source->url);
264 GITERR_CHECK_ALLOC(remote->url);
265 }
266
267 if (source->pushurl != NULL) {
268 remote->pushurl = git__strdup(source->pushurl);
269 GITERR_CHECK_ALLOC(remote->pushurl);
270 }
271
272 remote->repo = source->repo;
40ef47dd
AS
273 remote->download_tags = source->download_tags;
274 remote->check_cert = source->check_cert;
275 remote->update_fetchhead = source->update_fetchhead;
276
277 if ((error = git_vector_dup(&remote->refs, &source->refs, NULL)) < 0 ||
278 (error = git_vector_dup(&remote->refspecs, &source->refspecs, NULL)) < 0 ||
279 (error = git_vector_dup(&remote->active_refspecs, &source->active_refspecs, NULL))) {
280 git__free(remote);
281 return error;
282 }
283
284 *dest = remote;
285
286 return 0;
287}
288
4330ab26
CMN
289struct refspec_cb_data {
290 git_remote *remote;
291 int fetch;
292};
293
294static int refspec_cb(const git_config_entry *entry, void *payload)
295{
96869a4e 296 struct refspec_cb_data *data = (struct refspec_cb_data *)payload;
25e0b157 297 return add_refspec(data->remote, entry->value, data->fetch);
4330ab26
CMN
298}
299
bf6bebe2 300static int get_optional_config(
c9ffa84b 301 bool *found, git_config *config, git_buf *buf,
302 git_config_foreach_cb cb, void *payload)
bf6bebe2
RB
303{
304 int error = 0;
305 const char *key = git_buf_cstr(buf);
306
307 if (git_buf_oom(buf))
308 return -1;
309
310 if (cb != NULL)
4efa3290 311 error = git_config_get_multivar_foreach(config, key, NULL, cb, payload);
bf6bebe2
RB
312 else
313 error = git_config_get_string(payload, config, key);
314
c9ffa84b 315 if (found)
316 *found = !error;
317
bf6bebe2
RB
318 if (error == GIT_ENOTFOUND) {
319 giterr_clear();
320 error = 0;
321 }
322
bf6bebe2
RB
323 return error;
324}
325
9462c471 326int git_remote_load(git_remote **out, git_repository *repo, const char *name)
9c82357b
CMN
327{
328 git_remote *remote;
f0f3a18a 329 git_buf buf = GIT_BUF_INIT;
9c82357b 330 const char *val;
4376f7f6 331 int error = 0;
9462c471 332 git_config *config;
96869a4e 333 struct refspec_cb_data data = { NULL };
c9ffa84b 334 bool optional_setting_found = false, found;
4330ab26 335
9462c471
VM
336 assert(out && repo && name);
337
032ba9e4 338 if ((error = ensure_remote_name_is_valid(name)) < 0)
339 return error;
340
4376f7f6
CMN
341 if (git_repository_config__weakptr(&config, repo) < 0)
342 return -1;
4bef3565 343
9c82357b 344 remote = git__malloc(sizeof(git_remote));
4376f7f6 345 GITERR_CHECK_ALLOC(remote);
9c82357b
CMN
346
347 memset(remote, 0x0, sizeof(git_remote));
b0f6e45d 348 remote->update_fetchhead = 1;
9c82357b 349 remote->name = git__strdup(name);
4376f7f6 350 GITERR_CHECK_ALLOC(remote->name);
9c82357b 351
80fc7d6b
ET
352 if ((error = get_check_cert(&remote->check_cert, repo)) < 0)
353 goto cleanup;
354
25e0b157
RB
355 if (git_vector_init(&remote->refs, 32, NULL) < 0 ||
356 git_vector_init(&remote->refspecs, 2, NULL) < 0 ||
357 git_vector_init(&remote->active_refspecs, 2, NULL) < 0) {
2fb9d6de 358 error = -1;
359 goto cleanup;
360 }
d88d4311 361
25e0b157 362 if ((error = git_buf_printf(&buf, "remote.%s.url", name)) < 0)
2fb9d6de 363 goto cleanup;
9c82357b 364
c9ffa84b 365 if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
9c82357b 366 goto cleanup;
bf6bebe2 367
c9ffa84b 368 optional_setting_found |= found;
369
9462c471 370 remote->repo = repo;
c9ffa84b 371
372 if (found && strlen(val) > 0) {
373 remote->url = git__strdup(val);
374 GITERR_CHECK_ALLOC(remote->url);
375 }
9c82357b 376
bf6bebe2 377 val = NULL;
3ed4b501 378 git_buf_clear(&buf);
bf6bebe2 379 git_buf_printf(&buf, "remote.%s.pushurl", name);
3ed4b501 380
c9ffa84b 381 if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
3ed4b501 382 goto cleanup;
3ed4b501 383
c9ffa84b 384 optional_setting_found |= found;
385
386 if (!optional_setting_found) {
387 error = GIT_ENOTFOUND;
3ed4b501 388 goto cleanup;
c9ffa84b 389 }
3ed4b501 390
c9ffa84b 391 if (found && strlen(val) > 0) {
3ed4b501
SC
392 remote->pushurl = git__strdup(val);
393 GITERR_CHECK_ALLOC(remote->pushurl);
394 }
395
4330ab26
CMN
396 data.remote = remote;
397 data.fetch = true;
96869a4e 398
f0f3a18a 399 git_buf_clear(&buf);
bf6bebe2
RB
400 git_buf_printf(&buf, "remote.%s.fetch", name);
401
c9ffa84b 402 if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0)
2fb9d6de 403 goto cleanup;
9c82357b 404
4330ab26 405 data.fetch = false;
bf6bebe2
RB
406 git_buf_clear(&buf);
407 git_buf_printf(&buf, "remote.%s.push", name);
9c82357b 408
c9ffa84b 409 if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0)
9c82357b
CMN
410 goto cleanup;
411
24f2f94e
CMN
412 if (download_tags_value(remote, config) < 0)
413 goto cleanup;
414
af613ecd
CMN
415 /* Move the data over to where the matching functions can find them */
416 if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0)
417 goto cleanup;
418
9c82357b
CMN
419 *out = remote;
420
421cleanup:
f0f3a18a 422 git_buf_free(&buf);
9462c471 423
4376f7f6 424 if (error < 0)
9c82357b
CMN
425 git_remote_free(remote);
426
427 return error;
428}
429
4330ab26 430static int update_config_refspec(const git_remote *remote, git_config *config, int direction)
4fe5520a 431{
4330ab26 432 git_buf name = GIT_BUF_INIT;
66566516 433 unsigned int push;
4330ab26
CMN
434 const char *dir;
435 size_t i;
bf6bebe2 436 int error = 0;
3793fa9b 437 const char *cname;
4fe5520a 438
4330ab26
CMN
439 push = direction == GIT_DIRECTION_PUSH;
440 dir = push ? "push" : "fetch";
4fe5520a 441
4330ab26
CMN
442 if (git_buf_printf(&name, "remote.%s.%s", remote->name, dir) < 0)
443 return -1;
3793fa9b 444 cname = git_buf_cstr(&name);
4fe5520a 445
4330ab26 446 /* Clear out the existing config */
bf6bebe2 447 while (!error)
3793fa9b 448 error = git_config_delete_multivar(config, cname, ".*");
4fe5520a 449
4330ab26
CMN
450 if (error != GIT_ENOTFOUND)
451 return error;
452
1be680c4 453 for (i = 0; i < remote->refspecs.length; i++) {
4330ab26 454 git_refspec *spec = git_vector_get(&remote->refspecs, i);
4330ab26
CMN
455
456 if (spec->push != push)
457 continue;
458
376454d0
DRT
459 // "$^" is a unmatcheable regexp: it will not match anything at all, so
460 // all values will be considered new and we will not replace any
461 // present value.
bf6bebe2 462 if ((error = git_config_set_multivar(
376454d0 463 config, cname, "$^", spec->string)) < 0) {
4330ab26
CMN
464 goto cleanup;
465 }
466 }
467
468 giterr_clear();
469 error = 0;
4fe5520a 470
471cleanup:
472 git_buf_free(&name);
4fe5520a 473
474 return error;
475}
476
89e5ed98
CMN
477int git_remote_save(const git_remote *remote)
478{
218c88a9 479 int error;
89e5ed98 480 git_config *config;
218c88a9 481 const char *tagopt = NULL;
4fe5520a 482 git_buf buf = GIT_BUF_INIT;
89e5ed98 483
e497b16c 484 assert(remote);
485
c07b52df 486 if (!remote->name) {
874dcb25
BS
487 giterr_set(GITERR_INVALID, "Can't save an in-memory remote.");
488 return GIT_EINVALIDSPEC;
a71c27cc
BS
489 }
490
032ba9e4 491 if ((error = ensure_remote_name_is_valid(remote->name)) < 0)
492 return error;
89e5ed98 493
4376f7f6
CMN
494 if (git_repository_config__weakptr(&config, remote->repo) < 0)
495 return -1;
89e5ed98 496
cb020f0d 497 if (git_buf_printf(&buf, "remote.%s.url", remote->name) < 0)
4376f7f6 498 return -1;
89e5ed98 499
4376f7f6
CMN
500 if (git_config_set_string(config, git_buf_cstr(&buf), remote->url) < 0) {
501 git_buf_free(&buf);
502 return -1;
503 }
89e5ed98 504
413d5563
SC
505 git_buf_clear(&buf);
506 if (git_buf_printf(&buf, "remote.%s.pushurl", remote->name) < 0)
507 return -1;
3ed4b501 508
413d5563 509 if (remote->pushurl) {
3ed4b501
SC
510 if (git_config_set_string(config, git_buf_cstr(&buf), remote->pushurl) < 0) {
511 git_buf_free(&buf);
512 return -1;
513 }
413d5563 514 } else {
54b2a37a 515 int error = git_config_delete_entry(config, git_buf_cstr(&buf));
413d5563
SC
516 if (error == GIT_ENOTFOUND) {
517 error = 0;
c48e8700 518 giterr_clear();
413d5563
SC
519 }
520 if (error < 0) {
521 git_buf_free(&buf);
9f77b3f6 522 return error;
413d5563 523 }
3ed4b501
SC
524 }
525
4330ab26
CMN
526 if (update_config_refspec(remote, config, GIT_DIRECTION_FETCH) < 0)
527 goto on_error;
89e5ed98 528
4330ab26
CMN
529 if (update_config_refspec(remote, config, GIT_DIRECTION_PUSH) < 0)
530 goto on_error;
89e5ed98 531
218c88a9
CMN
532 /*
533 * What action to take depends on the old and new values. This
534 * is describes by the table below. tagopt means whether the
535 * is already a value set in the config
536 *
537 * AUTO ALL or NONE
538 * +-----------------------+
539 * tagopt | remove | set |
540 * +---------+-------------|
541 * !tagopt | nothing | set |
542 * +---------+-------------+
543 */
544
545 git_buf_clear(&buf);
546 if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0)
547 goto on_error;
548
549 error = git_config_get_string(&tagopt, config, git_buf_cstr(&buf));
550 if (error < 0 && error != GIT_ENOTFOUND)
551 goto on_error;
552
553 if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
554 if (git_config_set_string(config, git_buf_cstr(&buf), "--tags") < 0)
555 goto on_error;
556 } else if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_NONE) {
557 if (git_config_set_string(config, git_buf_cstr(&buf), "--no-tags") < 0)
558 goto on_error;
559 } else if (tagopt) {
54b2a37a 560 if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
218c88a9
CMN
561 goto on_error;
562 }
563
89e5ed98 564 git_buf_free(&buf);
4376f7f6
CMN
565
566 return 0;
567
568on_error:
569 git_buf_free(&buf);
4376f7f6 570 return -1;
89e5ed98
CMN
571}
572
df705148 573const char *git_remote_name(const git_remote *remote)
9c82357b 574{
4bef3565 575 assert(remote);
9c82357b
CMN
576 return remote->name;
577}
578
85e1eded
ES
579git_repository *git_remote_owner(const git_remote *remote)
580{
581 assert(remote);
582 return remote->repo;
583}
584
df705148 585const char *git_remote_url(const git_remote *remote)
9c82357b 586{
4bef3565 587 assert(remote);
9c82357b
CMN
588 return remote->url;
589}
590
76501590
SC
591int git_remote_set_url(git_remote *remote, const char* url)
592{
593 assert(remote);
594 assert(url);
595
596 git__free(remote->url);
597 remote->url = git__strdup(url);
598 GITERR_CHECK_ALLOC(remote->url);
599
600 return 0;
601}
602
df705148 603const char *git_remote_pushurl(const git_remote *remote)
76501590
SC
604{
605 assert(remote);
606 return remote->pushurl;
607}
608
609int git_remote_set_pushurl(git_remote *remote, const char* url)
610{
611 assert(remote);
612
613 git__free(remote->pushurl);
614 if (url) {
615 remote->pushurl = git__strdup(url);
616 GITERR_CHECK_ALLOC(remote->pushurl);
617 } else {
618 remote->pushurl = NULL;
619 }
620 return 0;
621}
622
eff5b499
SC
623const char* git_remote__urlfordirection(git_remote *remote, int direction)
624{
625 assert(remote);
626
b83c92dd 627 assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
628
df705148 629 if (direction == GIT_DIRECTION_FETCH) {
eff5b499
SC
630 return remote->url;
631 }
632
df705148 633 if (direction == GIT_DIRECTION_PUSH) {
eff5b499
SC
634 return remote->pushurl ? remote->pushurl : remote->url;
635 }
636
637 return NULL;
638}
639
df705148 640int git_remote_connect(git_remote *remote, git_direction direction)
9ba49bb5 641{
9ba49bb5 642 git_transport *t;
c0c39025 643 const char *url;
41fb1ca0 644 int flags = GIT_TRANSPORTFLAGS_NONE;
80fc7d6b 645 int error;
9ba49bb5 646
4bef3565
VM
647 assert(remote);
648
41fb1ca0
PK
649 t = remote->transport;
650
c0c39025 651 url = git_remote__urlfordirection(remote, direction);
80fc7d6b 652 if (url == NULL) {
44bc0c6a 653 giterr_set(GITERR_INVALID,
654 "Malformed remote '%s' - missing URL", remote->name);
eff5b499 655 return -1;
44bc0c6a 656 }
eff5b499 657
41fb1ca0
PK
658 /* A transport could have been supplied in advance with
659 * git_remote_set_transport */
80fc7d6b
ET
660 if (!t && (error = git_transport_new(&t, remote, url)) < 0)
661 return error;
9ba49bb5 662
41fb1ca0 663 if (t->set_callbacks &&
80fc7d6b 664 (error = t->set_callbacks(t, remote->callbacks.progress, NULL, remote->callbacks.payload)) < 0)
41fb1ca0 665 goto on_error;
613d5eb9 666
41fb1ca0
PK
667 if (!remote->check_cert)
668 flags |= GIT_TRANSPORTFLAGS_NO_CHECK_CERT;
e03e71da 669
25e0b157 670 if ((error = t->connect(t, url, remote->callbacks.credentials, remote->callbacks.payload, direction, flags)) != 0)
4376f7f6 671 goto on_error;
9ba49bb5
CMN
672
673 remote->transport = t;
674
4376f7f6 675 return 0;
9ba49bb5 676
4376f7f6
CMN
677on_error:
678 t->free(t);
613d5eb9
PK
679
680 if (t == remote->transport)
681 remote->transport = NULL;
682
80fc7d6b 683 return error;
9ba49bb5
CMN
684}
685
359dce72 686int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote)
9ba49bb5 687{
d88d4311
VM
688 assert(remote);
689
359dce72 690 return remote->transport->ls(out, size, remote->transport);
9ba49bb5
CMN
691}
692
613d5eb9
PK
693int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url)
694{
695 git_config *cfg;
9f77b3f6
RB
696 const git_config_entry *ce;
697 const char *val = NULL;
80fc7d6b 698 int error;
613d5eb9
PK
699
700 assert(remote);
701
a71c27cc 702 if (!proxy_url || !remote->repo)
613d5eb9
PK
703 return -1;
704
705 *proxy_url = NULL;
706
80fc7d6b
ET
707 if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0)
708 return error;
613d5eb9
PK
709
710 /* Go through the possible sources for proxy configuration, from most specific
711 * to least specific. */
712
713 /* remote.<name>.proxy config setting */
9f77b3f6 714 if (remote->name && remote->name[0]) {
613d5eb9
PK
715 git_buf buf = GIT_BUF_INIT;
716
80fc7d6b
ET
717 if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0)
718 return error;
613d5eb9 719
9f77b3f6
RB
720 error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false);
721 git_buf_free(&buf);
613d5eb9 722
9f77b3f6 723 if (error < 0)
80fc7d6b 724 return error;
613d5eb9 725
9f77b3f6
RB
726 if (ce && ce->value) {
727 val = ce->value;
728 goto found;
729 }
613d5eb9
PK
730 }
731
732 /* http.proxy config setting */
9f77b3f6 733 if ((error = git_config__lookup_entry(&ce, cfg, "http.proxy", false)) < 0)
80fc7d6b 734 return error;
9f77b3f6
RB
735 if (ce && ce->value) {
736 val = ce->value;
737 goto found;
738 }
613d5eb9
PK
739
740 /* HTTP_PROXY / HTTPS_PROXY environment variables */
741 val = use_ssl ? getenv("HTTPS_PROXY") : getenv("HTTP_PROXY");
742
9f77b3f6
RB
743found:
744 if (val && val[0]) {
613d5eb9
PK
745 *proxy_url = git__strdup(val);
746 GITERR_CHECK_ALLOC(*proxy_url);
613d5eb9
PK
747 }
748
749 return 0;
750}
751
af613ecd
CMN
752/* DWIM `refspecs` based on `refs` and append the output to `out` */
753static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs)
d8488457 754{
af613ecd 755 size_t i;
d8488457 756 git_refspec *spec;
d8488457
CMN
757
758 git_vector_foreach(refspecs, i, spec) {
af613ecd
CMN
759 if (git_refspec__dwim_one(out, spec, refs) < 0)
760 return -1;
761 }
d8488457 762
af613ecd
CMN
763 return 0;
764}
d8488457 765
af613ecd
CMN
766static void free_refspecs(git_vector *vec)
767{
768 size_t i;
769 git_refspec *spec;
d8488457 770
af613ecd
CMN
771 git_vector_foreach(vec, i, spec) {
772 git_refspec__free(spec);
773 git__free(spec);
d8488457
CMN
774 }
775
af613ecd 776 git_vector_clear(vec);
d8488457
CMN
777}
778
779static int remote_head_cmp(const void *_a, const void *_b)
780{
781 const git_remote_head *a = (git_remote_head *) _a;
782 const git_remote_head *b = (git_remote_head *) _b;
783
784 return git__strcmp_cb(a->name, b->name);
785}
786
877cde76
CMN
787static int ls_to_vector(git_vector *out, git_remote *remote)
788{
789 git_remote_head **heads;
790 size_t heads_len, i;
791
792 if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0)
793 return -1;
794
795 if (git_vector_init(out, heads_len, remote_head_cmp) < 0)
796 return -1;
797
798 for (i = 0; i < heads_len; i++) {
799 if (git_vector_insert(out, heads[i]) < 0)
800 return -1;
801 }
802
803 return 0;
804}
805
d31402a3 806int git_remote_download(git_remote *remote)
48a65a07 807{
95057b85 808 int error;
d8488457 809 git_vector refs;
95057b85 810
1e3b8ed5 811 assert(remote);
4bef3565 812
877cde76 813 if (ls_to_vector(&refs, remote) < 0)
d8488457 814 return -1;
d8488457 815
af613ecd
CMN
816 free_refspecs(&remote->active_refspecs);
817
877cde76
CMN
818 error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &refs);
819 git_vector_free(&refs);
820
821 if (error < 0)
25e0b157 822 return error;
d8488457 823
95057b85 824 if ((error = git_fetch_negotiate(remote)) < 0)
4376f7f6 825 return error;
95057b85 826
d31402a3 827 return git_fetch_download_pack(remote);
48a65a07
CMN
828}
829
fe3a40a4
CMN
830int git_remote_fetch(git_remote *remote)
831{
832 int error;
833
834 /* Connect and download everything */
25e0b157 835 if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) != 0)
fe3a40a4
CMN
836 return error;
837
25e0b157 838 if ((error = git_remote_download(remote)) != 0)
fe3a40a4
CMN
839 return error;
840
841 /* We don't need to be connected anymore */
842 git_remote_disconnect(remote);
843
844 /* Create "remote/foo" branches for all remote branches */
845 return git_remote_update_tips(remote);
846}
847
b0f6e45d
ET
848static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src)
849{
850 unsigned int i;
851 git_remote_head *remote_ref;
852
853 assert(update_heads && fetchspec_src);
854
855 *out = NULL;
7d4b65f6 856
857 git_vector_foreach(update_heads, i, remote_ref) {
858 if (strcmp(remote_ref->name, fetchspec_src) == 0) {
859 *out = remote_ref;
860 break;
b0f6e45d
ET
861 }
862 }
863
864 return 0;
865}
866
4330ab26 867static int remote_head_for_ref(git_remote_head **out, git_refspec *spec, git_vector *update_heads, git_reference *ref)
b0f6e45d
ET
868{
869 git_reference *resolved_ref = NULL;
870 git_reference *tracking_ref = NULL;
871 git_buf remote_name = GIT_BUF_INIT;
872 int error = 0;
873
4330ab26 874 assert(out && spec && ref);
b0f6e45d
ET
875
876 *out = NULL;
877
878 if ((error = git_reference_resolve(&resolved_ref, ref)) < 0 ||
879 (!git_reference_is_branch(resolved_ref)) ||
a258d8e3 880 (error = git_branch_upstream(&tracking_ref, resolved_ref)) < 0 ||
4330ab26 881 (error = git_refspec_transform_l(&remote_name, spec, git_reference_name(tracking_ref))) < 0) {
605da51a 882 /* Not an error if HEAD is unborn or no tracking branch */
b0f6e45d
ET
883 if (error == GIT_ENOTFOUND)
884 error = 0;
885
886 goto cleanup;
887 }
888
889 error = remote_head_for_fetchspec_src(out, update_heads, git_buf_cstr(&remote_name));
890
891cleanup:
892 git_reference_free(tracking_ref);
893 git_reference_free(resolved_ref);
894 git_buf_free(&remote_name);
895 return error;
896}
897
4330ab26 898static int git_remote_write_fetchhead(git_remote *remote, git_refspec *spec, git_vector *update_heads)
b0f6e45d 899{
b0f6e45d
ET
900 git_reference *head_ref = NULL;
901 git_fetchhead_ref *fetchhead_ref;
902 git_remote_head *remote_ref, *merge_remote_ref;
903 git_vector fetchhead_refs;
904 bool include_all_fetchheads;
905 unsigned int i = 0;
906 int error = 0;
907
908 assert(remote);
909
404eadb0
CMN
910 /* no heads, nothing to do */
911 if (update_heads->length == 0)
912 return 0;
913
b0f6e45d
ET
914 if (git_vector_init(&fetchhead_refs, update_heads->length, git_fetchhead_ref_cmp) < 0)
915 return -1;
916
917 /* Iff refspec is * (but not subdir slash star), include tags */
918 include_all_fetchheads = (strcmp(GIT_REFS_HEADS_DIR "*", git_refspec_src(spec)) == 0);
919
920 /* Determine what to merge: if refspec was a wildcard, just use HEAD */
921 if (git_refspec_is_wildcard(spec)) {
922 if ((error = git_reference_lookup(&head_ref, remote->repo, GIT_HEAD_FILE)) < 0 ||
4330ab26 923 (error = remote_head_for_ref(&merge_remote_ref, spec, update_heads, head_ref)) < 0)
b0f6e45d
ET
924 goto cleanup;
925 } else {
926 /* If we're fetching a single refspec, that's the only thing that should be in FETCH_HEAD. */
927 if ((error = remote_head_for_fetchspec_src(&merge_remote_ref, update_heads, git_refspec_src(spec))) < 0)
928 goto cleanup;
929 }
930
931 /* Create the FETCH_HEAD file */
7d4b65f6 932 git_vector_foreach(update_heads, i, remote_ref) {
b0f6e45d
ET
933 int merge_this_fetchhead = (merge_remote_ref == remote_ref);
934
935 if (!include_all_fetchheads &&
936 !git_refspec_src_matches(spec, remote_ref->name) &&
937 !merge_this_fetchhead)
938 continue;
939
940 if (git_fetchhead_ref_create(&fetchhead_ref,
941 &remote_ref->oid,
942 merge_this_fetchhead,
943 remote_ref->name,
944 git_remote_url(remote)) < 0)
945 goto cleanup;
946
947 if (git_vector_insert(&fetchhead_refs, fetchhead_ref) < 0)
948 goto cleanup;
949 }
950
951 git_fetchhead_write(remote->repo, &fetchhead_refs);
952
953cleanup:
954 for (i = 0; i < fetchhead_refs.length; ++i)
955 git_fetchhead_ref_free(fetchhead_refs.contents[i]);
956
957 git_vector_free(&fetchhead_refs);
958 git_reference_free(head_ref);
959
960 return error;
961}
962
4330ab26 963static int update_tips_for_spec(git_remote *remote, git_refspec *spec, git_vector *refs)
441f57c2 964{
a37ddf7e 965 int error = 0, autotag;
517bda19 966 unsigned int i = 0;
97769280 967 git_buf refname = GIT_BUF_INIT;
f184836b 968 git_oid old;
a37ddf7e 969 git_odb *odb;
441f57c2
CMN
970 git_remote_head *head;
971 git_reference *ref;
a37ddf7e 972 git_refspec tagspec;
4330ab26 973 git_vector update_heads;
441f57c2 974
4bef3565
VM
975 assert(remote);
976
acd17006 977 if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
a37ddf7e
CMN
978 return -1;
979
3230a44f 980 if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
a37ddf7e
CMN
981 return -1;
982
41fb1ca0 983 /* Make a copy of the transport's refs */
4330ab26 984 if (git_vector_init(&update_heads, 16, NULL) < 0)
41fb1ca0 985 return -1;
585a2eb7 986
4330ab26
CMN
987 for (; i < refs->length; ++i) {
988 head = git_vector_get(refs, i);
9063be1f 989 autotag = 0;
517bda19 990
a37ddf7e
CMN
991 /* Ignore malformed ref names (which also saves us from tag^{} */
992 if (!git_reference_is_valid_name(head->name))
993 continue;
994
d8488457 995 if (git_refspec_src_matches(spec, head->name) && spec->dst) {
a37ddf7e
CMN
996 if (git_refspec_transform_r(&refname, spec, head->name) < 0)
997 goto on_error;
998 } else if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_NONE) {
3230a44f
CMN
999
1000 if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_ALL)
1001 autotag = 1;
a37ddf7e
CMN
1002
1003 if (!git_refspec_src_matches(&tagspec, head->name))
1004 continue;
1005
1006 git_buf_clear(&refname);
1007 if (git_buf_puts(&refname, head->name) < 0)
1008 goto on_error;
1009 } else {
1010 continue;
1011 }
1012
1013 if (autotag && !git_odb_exists(odb, &head->oid))
1014 continue;
f184836b 1015
b0f6e45d
ET
1016 if (git_vector_insert(&update_heads, head) < 0)
1017 goto on_error;
1018
2508cc66 1019 error = git_reference_name_to_id(&old, remote->repo, refname.ptr);
904b67e6 1020 if (error < 0 && error != GIT_ENOTFOUND)
f184836b
CMN
1021 goto on_error;
1022
904b67e6 1023 if (error == GIT_ENOTFOUND)
f184836b
CMN
1024 memset(&old, 0, GIT_OID_RAWSZ);
1025
b7f167da 1026 if (!git_oid__cmp(&old, &head->oid))
f184836b 1027 continue;
441f57c2 1028
a37ddf7e 1029 /* In autotag mode, don't overwrite any locally-existing tags */
0b28217b 1030 error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag, NULL, NULL);
a37ddf7e 1031 if (error < 0 && error != GIT_EEXISTS)
944d250f 1032 goto on_error;
39157563
CMN
1033
1034 git_reference_free(ref);
f184836b 1035
b3aaa7a7 1036 if (remote->callbacks.update_tips != NULL) {
df705148 1037 if (remote->callbacks.update_tips(refname.ptr, &old, &head->oid, remote->callbacks.payload) < 0)
f184836b
CMN
1038 goto on_error;
1039 }
441f57c2
CMN
1040 }
1041
b0f6e45d 1042 if (git_remote_update_fetchhead(remote) &&
4330ab26 1043 (error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0)
b0f6e45d
ET
1044 goto on_error;
1045
b0f6e45d 1046 git_vector_free(&update_heads);
a37ddf7e 1047 git_refspec__free(&tagspec);
97769280 1048 git_buf_free(&refname);
f184836b
CMN
1049 return 0;
1050
1051on_error:
b0f6e45d 1052 git_vector_free(&update_heads);
a37ddf7e 1053 git_refspec__free(&tagspec);
f184836b
CMN
1054 git_buf_free(&refname);
1055 return -1;
97769280 1056
441f57c2
CMN
1057}
1058
4330ab26
CMN
1059int git_remote_update_tips(git_remote *remote)
1060{
505b5d0c 1061 git_refspec *spec, tagspec;
4330ab26 1062 git_vector refs;
505b5d0c 1063 int error;
4330ab26
CMN
1064 size_t i;
1065
505b5d0c
CMN
1066 if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
1067 return -1;
1068
877cde76
CMN
1069
1070 if ((error = ls_to_vector(&refs, remote)) < 0)
505b5d0c
CMN
1071 goto out;
1072
1073 if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
1074 error = update_tips_for_spec(remote, &tagspec, &refs);
1075 goto out;
1076 }
4330ab26 1077
af613ecd 1078 git_vector_foreach(&remote->active_refspecs, i, spec) {
4330ab26
CMN
1079 if (spec->push)
1080 continue;
1081
505b5d0c
CMN
1082 if ((error = update_tips_for_spec(remote, spec, &refs)) < 0)
1083 goto out;
4330ab26
CMN
1084 }
1085
505b5d0c 1086out:
877cde76 1087 git_vector_free(&refs);
505b5d0c 1088 git_refspec__free(&tagspec);
505b5d0c 1089 return error;
4330ab26
CMN
1090}
1091
11f6ad5f 1092int git_remote_connected(const git_remote *remote)
6ac3b707 1093{
4bef3565 1094 assert(remote);
41fb1ca0
PK
1095
1096 if (!remote->transport || !remote->transport->is_connected)
1097 return 0;
1098
1099 /* Ask the transport if it's connected. */
613d5eb9 1100 return remote->transport->is_connected(remote->transport);
6ac3b707
CMN
1101}
1102
f0d2ddbb
CMN
1103void git_remote_stop(git_remote *remote)
1104{
613d5eb9
PK
1105 assert(remote);
1106
1107 if (remote->transport && remote->transport->cancel)
41fb1ca0 1108 remote->transport->cancel(remote->transport);
f0d2ddbb
CMN
1109}
1110
4cf01e9a
CMN
1111void git_remote_disconnect(git_remote *remote)
1112{
4bef3565
VM
1113 assert(remote);
1114
41fb1ca0
PK
1115 if (git_remote_connected(remote))
1116 remote->transport->close(remote->transport);
4cf01e9a
CMN
1117}
1118
9c82357b
CMN
1119void git_remote_free(git_remote *remote)
1120{
2aae2188
CMN
1121 if (remote == NULL)
1122 return;
1123
42ea35c0
MS
1124 if (remote->transport != NULL) {
1125 git_remote_disconnect(remote);
1126
1127 remote->transport->free(remote->transport);
1128 remote->transport = NULL;
1129 }
1130
1131 git_vector_free(&remote->refs);
1132
af613ecd 1133 free_refspecs(&remote->refspecs);
4330ab26
CMN
1134 git_vector_free(&remote->refspecs);
1135
af613ecd
CMN
1136 free_refspecs(&remote->active_refspecs);
1137 git_vector_free(&remote->active_refspecs);
1138
3286c408 1139 git__free(remote->url);
3ed4b501 1140 git__free(remote->pushurl);
3286c408 1141 git__free(remote->name);
3286c408 1142 git__free(remote);
9c82357b 1143}
8171998f 1144
106c12f1 1145static int remote_list_cb(const git_config_entry *entry, void *payload)
8171998f 1146{
25e0b157 1147 git_vector *list = payload;
106c12f1
RB
1148 const char *name = entry->name + strlen("remote.");
1149 size_t namelen = strlen(name);
1150 char *remote_name;
8171998f 1151
106c12f1 1152 /* we know name matches "remote.<stuff>.(push)?url" */
8171998f 1153
106c12f1
RB
1154 if (!strcmp(&name[namelen - 4], ".url"))
1155 remote_name = git__strndup(name, namelen - 4); /* strip ".url" */
1156 else
1157 remote_name = git__strndup(name, namelen - 8); /* strip ".pushurl" */
25e0b157 1158 GITERR_CHECK_ALLOC(remote_name);
8171998f 1159
25e0b157 1160 return git_vector_insert(list, remote_name);
8171998f
CMN
1161}
1162
1163int git_remote_list(git_strarray *remotes_list, git_repository *repo)
1164{
8171998f 1165 int error;
96869a4e 1166 git_config *cfg;
25e0b157 1167 git_vector list = GIT_VECTOR_INIT;
8171998f 1168
96869a4e
RB
1169 if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
1170 return error;
8171998f 1171
25e0b157 1172 if ((error = git_vector_init(&list, 4, git__strcmp_cb)) < 0)
96869a4e 1173 return error;
8171998f 1174
106c12f1 1175 error = git_config_foreach_match(
25e0b157 1176 cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list);
8171998f 1177
4376f7f6 1178 if (error < 0) {
9cfce273 1179 git_vector_free_deep(&list);
8171998f
CMN
1180 return error;
1181 }
1182
25e0b157 1183 git_vector_uniq(&list, git__free);
aec87f71 1184
25e0b157
RB
1185 remotes_list->strings =
1186 (char **)git_vector_detach(&remotes_list->count, NULL, &list);
8171998f 1187
4376f7f6 1188 return 0;
8171998f 1189}
a209a025 1190
250b95b2
CMN
1191void git_remote_check_cert(git_remote *remote, int check)
1192{
1193 assert(remote);
1194
1195 remote->check_cert = check;
1196}
b3aaa7a7 1197
0e0cf787 1198int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks)
b3aaa7a7
CMN
1199{
1200 assert(remote && callbacks);
1201
c7231c45 1202 GITERR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
9267ff58 1203
b3aaa7a7 1204 memcpy(&remote->callbacks, callbacks, sizeof(git_remote_callbacks));
e03e71da 1205
41fb1ca0 1206 if (remote->transport && remote->transport->set_callbacks)
0e0cf787 1207 return remote->transport->set_callbacks(remote->transport,
41fb1ca0
PK
1208 remote->callbacks.progress,
1209 NULL,
df705148 1210 remote->callbacks.payload);
9267ff58
BS
1211
1212 return 0;
41fb1ca0
PK
1213}
1214
1215int git_remote_set_transport(git_remote *remote, git_transport *transport)
1216{
1217 assert(remote && transport);
1218
c7231c45 1219 GITERR_CHECK_VERSION(transport, GIT_TRANSPORT_VERSION, "git_transport");
10711769 1220
e03e71da 1221 if (remote->transport) {
41fb1ca0
PK
1222 giterr_set(GITERR_NET, "A transport is already bound to this remote");
1223 return -1;
e03e71da 1224 }
41fb1ca0
PK
1225
1226 remote->transport = transport;
1227 return 0;
b3aaa7a7 1228}
f70e466f 1229
67dad09b 1230const git_transfer_progress* git_remote_stats(git_remote *remote)
d57c47dc
BS
1231{
1232 assert(remote);
1233 return &remote->stats;
1234}
1235
11f6ad5f 1236git_remote_autotag_option_t git_remote_autotag(const git_remote *remote)
f70e466f
CMN
1237{
1238 return remote->download_tags;
1239}
1240
f4a62c30 1241void git_remote_set_autotag(git_remote *remote, git_remote_autotag_option_t value)
f70e466f
CMN
1242{
1243 remote->download_tags = value;
1244}
fcccf304 1245
fcccf304 1246static int rename_remote_config_section(
1247 git_repository *repo,
1248 const char *old_name,
1249 const char *new_name)
1250{
1251 git_buf old_section_name = GIT_BUF_INIT,
1252 new_section_name = GIT_BUF_INIT;
1253 int error = -1;
1254
1255 if (git_buf_printf(&old_section_name, "remote.%s", old_name) < 0)
1256 goto cleanup;
1257
1258 if (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0)
1259 goto cleanup;
1260
1261 error = git_config_rename_section(
1262 repo,
1263 git_buf_cstr(&old_section_name),
1264 git_buf_cstr(&new_section_name));
1265
1266cleanup:
1267 git_buf_free(&old_section_name);
1268 git_buf_free(&new_section_name);
1269
1270 return error;
1271}
1272
96869a4e 1273struct update_data {
fcccf304 1274 git_config *config;
1275 const char *old_remote_name;
1276 const char *new_remote_name;
1277};
1278
1279static int update_config_entries_cb(
1280 const git_config_entry *entry,
1281 void *payload)
1282{
1283 struct update_data *data = (struct update_data *)payload;
1284
1285 if (strcmp(entry->value, data->old_remote_name))
1286 return 0;
1287
25e0b157
RB
1288 return git_config_set_string(
1289 data->config, entry->name, data->new_remote_name);
fcccf304 1290}
1291
1292static int update_branch_remote_config_entry(
1293 git_repository *repo,
1294 const char *old_name,
1295 const char *new_name)
1296{
96869a4e
RB
1297 int error;
1298 struct update_data data = { NULL };
fcccf304 1299
96869a4e
RB
1300 if ((error = git_repository_config__weakptr(&data.config, repo)) < 0)
1301 return error;
fcccf304 1302
fcccf304 1303 data.old_remote_name = old_name;
1304 data.new_remote_name = new_name;
1305
25e0b157 1306 return git_config_foreach_match(
96869a4e 1307 data.config, "branch\\..+\\.remote", update_config_entries_cb, &data);
fcccf304 1308}
1309
fcccf304 1310static int rename_one_remote_reference(
56960b83 1311 git_reference *reference,
fcccf304 1312 const char *old_remote_name,
1313 const char *new_remote_name)
1314{
96869a4e 1315 int error;
fcccf304 1316 git_buf new_name = GIT_BUF_INIT;
fcccf304 1317
96869a4e 1318 error = git_buf_printf(
fcccf304 1319 &new_name,
1320 GIT_REFS_REMOTES_DIR "%s%s",
1321 new_remote_name,
96869a4e 1322 reference->name + strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name));
fcccf304 1323
96869a4e
RB
1324 if (!error) {
1325 error = git_reference_rename(
1326 NULL, reference, git_buf_cstr(&new_name), 0);
1327 git_reference_free(reference);
1328 }
fcccf304 1329
fcccf304 1330 git_buf_free(&new_name);
1331 return error;
1332}
1333
1334static int rename_remote_references(
1335 git_repository *repo,
1336 const char *old_name,
1337 const char *new_name)
1338{
96869a4e 1339 int error;
56960b83 1340 git_reference *ref;
9bd89d96 1341 git_reference_iterator *iter;
fcccf304 1342
96869a4e
RB
1343 if ((error = git_reference_iterator_new(&iter, repo)) < 0)
1344 return error;
fcccf304 1345
56960b83 1346 while ((error = git_reference_next(&ref, iter)) == 0) {
2f77d8f1
RB
1347 if (git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) {
1348 git_reference_free(ref);
9bd89d96 1349 continue;
2f77d8f1 1350 }
9bd89d96 1351
96869a4e
RB
1352 if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0)
1353 break;
9bd89d96
CMN
1354 }
1355
1356 git_reference_iterator_free(iter);
fcccf304 1357
96869a4e 1358 return (error == GIT_ITEROVER) ? 0 : error;
fcccf304 1359}
1360
1361static int rename_fetch_refspecs(
1362 git_remote *remote,
1363 const char *new_name,
1364 int (*callback)(const char *problematic_refspec, void *payload),
1365 void *payload)
1366{
1367 git_config *config;
4330ab26 1368 git_buf base = GIT_BUF_INIT, var = GIT_BUF_INIT, val = GIT_BUF_INIT;
1be680c4 1369 const git_refspec *spec;
4330ab26 1370 size_t i;
25e0b157 1371 int error = 0;
fcccf304 1372
dab89f9b 1373 if ((error = git_repository_config__weakptr(&config, remote->repo)) < 0)
25e0b157
RB
1374 return error;
1375
1376 if ((error = git_buf_printf(
1377 &base, "+refs/heads/*:refs/remotes/%s/*", remote->name)) < 0)
1378 return error;
dab89f9b 1379
1be680c4 1380 git_vector_foreach(&remote->refspecs, i, spec) {
4330ab26
CMN
1381 if (spec->push)
1382 continue;
fcccf304 1383
dab89f9b
RB
1384 /* Every refspec is a problem refspec for an in-memory remote, OR */
1385 /* Does the dst part of the refspec follow the expected format? */
1386 if (!remote->name ||
1387 strcmp(git_buf_cstr(&base), spec->string)) {
fcccf304 1388
c7b3e1b3 1389 if ((error = callback(spec->string, payload)) != 0) {
26c1cb91 1390 giterr_set_after_callback(error);
25e0b157 1391 break;
c7b3e1b3
RB
1392 }
1393
4330ab26
CMN
1394 continue;
1395 }
fcccf304 1396
4330ab26 1397 /* If we do want to move it to the new section */
fcccf304 1398
dab89f9b
RB
1399 git_buf_clear(&val);
1400 git_buf_clear(&var);
fcccf304 1401
dab89f9b
RB
1402 if (git_buf_printf(
1403 &val, "+refs/heads/*:refs/remotes/%s/*", new_name) < 0 ||
1404 git_buf_printf(&var, "remote.%s.fetch", new_name) < 0)
1405 {
1406 error = -1;
25e0b157 1407 break;
dab89f9b 1408 }
fcccf304 1409
dab89f9b
RB
1410 if ((error = git_config_set_string(
1411 config, git_buf_cstr(&var), git_buf_cstr(&val))) < 0)
25e0b157 1412 break;
4330ab26 1413 }
fcccf304 1414
4330ab26
CMN
1415 git_buf_free(&base);
1416 git_buf_free(&var);
1417 git_buf_free(&val);
fcccf304 1418 return error;
1419}
1420
1421int git_remote_rename(
1422 git_remote *remote,
1423 const char *new_name,
df705148 1424 git_remote_rename_problem_cb callback,
fcccf304 1425 void *payload)
1426{
1427 int error;
1428
1429 assert(remote && new_name);
1430
c07b52df 1431 if (!remote->name) {
79000951
BS
1432 giterr_set(GITERR_INVALID, "Can't rename an in-memory remote.");
1433 return GIT_EINVALIDSPEC;
1434 }
1435
fcccf304 1436 if ((error = ensure_remote_name_is_valid(new_name)) < 0)
1437 return error;
1438
a71c27cc
BS
1439 if (remote->repo) {
1440 if ((error = ensure_remote_doesnot_exist(remote->repo, new_name)) < 0)
fcccf304 1441 return error;
1442
a71c27cc
BS
1443 if (!remote->name) {
1444 if ((error = rename_fetch_refspecs(
1445 remote,
1446 new_name,
1447 callback,
1448 payload)) < 0)
dab89f9b 1449 return error;
fcccf304 1450
a71c27cc 1451 remote->name = git__strdup(new_name);
dab89f9b 1452 GITERR_CHECK_ALLOC(remote->name);
fcccf304 1453
a71c27cc
BS
1454 return git_remote_save(remote);
1455 }
fcccf304 1456
a71c27cc
BS
1457 if ((error = rename_remote_config_section(
1458 remote->repo,
1459 remote->name,
1460 new_name)) < 0)
1461 return error;
fcccf304 1462
a71c27cc
BS
1463 if ((error = update_branch_remote_config_entry(
1464 remote->repo,
1465 remote->name,
1466 new_name)) < 0)
1467 return error;
fcccf304 1468
a71c27cc
BS
1469 if ((error = rename_remote_references(
1470 remote->repo,
1471 remote->name,
1472 new_name)) < 0)
1473 return error;
1474
1475 if ((error = rename_fetch_refspecs(
1476 remote,
1477 new_name,
1478 callback,
1479 payload)) < 0)
dab89f9b 1480 return error;
a71c27cc 1481 }
fcccf304 1482
1483 git__free(remote->name);
dab89f9b 1484
fcccf304 1485 remote->name = git__strdup(new_name);
dab89f9b 1486 GITERR_CHECK_ALLOC(remote->name);
fcccf304 1487
1488 return 0;
1489}
b0f6e45d
ET
1490
1491int git_remote_update_fetchhead(git_remote *remote)
1492{
8f2a3d62 1493 return (remote->update_fetchhead != 0);
b0f6e45d
ET
1494}
1495
1496void git_remote_set_update_fetchhead(git_remote *remote, int value)
1497{
8f2a3d62 1498 remote->update_fetchhead = (value != 0);
b0f6e45d 1499}
2bca5b67 1500
1501int git_remote_is_valid_name(
1502 const char *remote_name)
1503{
1504 git_buf buf = GIT_BUF_INIT;
1505 git_refspec refspec;
1506 int error = -1;
1507
1508 if (!remote_name || *remote_name == '\0')
1509 return 0;
1510
1511 git_buf_printf(&buf, "refs/heads/test:refs/remotes/%s/test", remote_name);
1512 error = git_refspec__parse(&refspec, git_buf_cstr(&buf), true);
1513
1514 git_buf_free(&buf);
1515 git_refspec__free(&refspec);
1516
1517 giterr_clear();
1518 return error == 0;
1519}
4330ab26
CMN
1520
1521git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname)
1522{
1523 git_refspec *spec;
1524 size_t i;
1525
af613ecd 1526 git_vector_foreach(&remote->active_refspecs, i, spec) {
4330ab26
CMN
1527 if (spec->push)
1528 continue;
1529
1530 if (git_refspec_src_matches(spec, refname))
1531 return spec;
1532 }
1533
1534 return NULL;
1535}
1536
1537git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname)
1538{
1539 git_refspec *spec;
1540 size_t i;
1541
af613ecd 1542 git_vector_foreach(&remote->active_refspecs, i, spec) {
4330ab26
CMN
1543 if (spec->push)
1544 continue;
1545
1546 if (git_refspec_dst_matches(spec, refname))
1547 return spec;
1548 }
1549
1550 return NULL;
1551}
1552
1553void git_remote_clear_refspecs(git_remote *remote)
1554{
1555 git_refspec *spec;
4330ab26
CMN
1556 size_t i;
1557
1558 git_vector_foreach(&remote->refspecs, i, spec) {
1559 git_refspec__free(spec);
1be680c4 1560 git__free(spec);
4330ab26
CMN
1561 }
1562 git_vector_clear(&remote->refspecs);
4330ab26
CMN
1563}
1564
266af6d8
CMN
1565static int add_and_dwim(git_remote *remote, const char *str, int push)
1566{
1567 git_refspec *spec;
1568 git_vector *vec;
1569
1570 if (add_refspec(remote, str, !push) < 0)
1571 return -1;
1572
1573 vec = &remote->refspecs;
1574 spec = git_vector_get(vec, vec->length - 1);
1575 return git_refspec__dwim_one(&remote->active_refspecs, spec, &remote->refs);
1576}
1577
bc6374ea 1578int git_remote_add_fetch(git_remote *remote, const char *refspec)
4330ab26 1579{
266af6d8 1580 return add_and_dwim(remote, refspec, false);
4330ab26
CMN
1581}
1582
bc6374ea 1583int git_remote_add_push(git_remote *remote, const char *refspec)
4330ab26 1584{
266af6d8
CMN
1585 return add_and_dwim(remote, refspec, true);
1586}
1587
1588static int set_refspecs(git_remote *remote, git_strarray *array, int push)
1589{
1590 git_vector *vec = &remote->refspecs;
1591 git_refspec *spec;
1592 size_t i;
1593
1594 /* Start by removing any refspecs of the same type */
1595 for (i = 0; i < vec->length; i++) {
1596 spec = git_vector_get(vec, i);
1597 if (spec->push != push)
1598 continue;
1599
1600 git_refspec__free(spec);
1601 git__free(spec);
1602 git_vector_remove(vec, i);
1603 i--;
1604 }
1605
1606 /* And now we add the new ones */
1607
1608 for (i = 0; i < array->count; i++) {
1609 if (add_refspec(remote, array->strings[i], !push) < 0)
1610 return -1;
1611 }
1612
1613 free_refspecs(&remote->active_refspecs);
1614 git_vector_clear(&remote->active_refspecs);
1615
1616 return dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs);
1617}
1618
1619int git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array)
1620{
1621 return set_refspecs(remote, array, false);
1622}
1623
1624int git_remote_set_push_refspecs(git_remote *remote, git_strarray *array)
1625{
1626 return set_refspecs(remote, array, true);
4330ab26 1627}
bc6374ea 1628
11f6ad5f 1629static int copy_refspecs(git_strarray *array, const git_remote *remote, unsigned int push)
bc6374ea
CMN
1630{
1631 size_t i;
1632 git_vector refspecs;
1633 git_refspec *spec;
1634 char *dup;
1635
1636 if (git_vector_init(&refspecs, remote->refspecs.length, NULL) < 0)
1637 return -1;
1638
1639 git_vector_foreach(&remote->refspecs, i, spec) {
1640 if (spec->push != push)
1641 continue;
1642
1be680c4 1643 if ((dup = git__strdup(spec->string)) == NULL)
bc6374ea 1644 goto on_error;
bc6374ea
CMN
1645
1646 if (git_vector_insert(&refspecs, dup) < 0) {
1647 git__free(dup);
1648 goto on_error;
1649 }
1650 }
1651
1652 array->strings = (char **)refspecs.contents;
1653 array->count = refspecs.length;
1654
1655 return 0;
1656
1657on_error:
9cfce273 1658 git_vector_free_deep(&refspecs);
bc6374ea
CMN
1659
1660 return -1;
1661}
1662
11f6ad5f 1663int git_remote_get_fetch_refspecs(git_strarray *array, const git_remote *remote)
bc6374ea
CMN
1664{
1665 return copy_refspecs(array, remote, false);
1666}
1667
11f6ad5f 1668int git_remote_get_push_refspecs(git_strarray *array, const git_remote *remote)
bc6374ea
CMN
1669{
1670 return copy_refspecs(array, remote, true);
1671}
1ffd0806 1672
11f6ad5f 1673size_t git_remote_refspec_count(const git_remote *remote)
1ffd0806
CMN
1674{
1675 return remote->refspecs.length;
1676}
1677
11f6ad5f 1678const git_refspec *git_remote_get_refspec(const git_remote *remote, size_t n)
1ffd0806
CMN
1679{
1680 return git_vector_get(&remote->refspecs, n);
1681}