File Coverage

deps/libgit2/src/remote.c
Criterion Covered Total %
statement 495 1323 37.4
branch 244 968 25.2
condition n/a
subroutine n/a
pod n/a
total 739 2291 32.2


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