File Coverage

deps/libgit2/src/libgit2/remote.c
Criterion Covered Total %
statement 543 1519 35.7
branch 267 1110 24.0
condition n/a
subroutine n/a
pod n/a
total 810 2629 30.8


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