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