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