File Coverage

deps/libgit2/src/remote.c
Criterion Covered Total %
statement 487 1323 36.8
branch 241 968 24.9
condition n/a
subroutine n/a
pod n/a
total 728 2291 31.7


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