remote.c 66.6 KB
Newer Older
Carlos Martín Nieto committed
1
/*
Edward Thomson committed
2
 * Copyright (C) the libgit2 contributors. All rights reserved.
Carlos Martín Nieto committed
3
 *
Vicent Marti committed
4 5
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
Carlos Martín Nieto committed
6 7
 */

8 9
#include "remote.h"

10 11
#include "buf.h"
#include "branch.h"
Carlos Martín Nieto committed
12 13
#include "config.h"
#include "repository.h"
14
#include "fetch.h"
15
#include "refs.h"
16 17
#include "refspec.h"
#include "fetchhead.h"
18
#include "push.h"
Carlos Martín Nieto committed
19

20 21 22 23 24
#include "git2/config.h"
#include "git2/types.h"
#include "git2/oid.h"
#include "git2/net.h"

25 26 27
#define CONFIG_URL_FMT "remote.%s.url"
#define CONFIG_PUSHURL_FMT "remote.%s.pushurl"
#define CONFIG_FETCH_FMT "remote.%s.fetch"
28
#define CONFIG_PUSH_FMT "remote.%s.push"
29
#define CONFIG_TAGOPT_FMT "remote.%s.tagopt"
30

31
static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs);
32
static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name);
33
char *apply_insteadof(git_config *config, const char *url, int direction);
34

35
static int add_refspec_to(git_vector *vector, const char *string, bool is_fetch)
Carlos Martín Nieto committed
36
{
37
	git_refspec *spec;
Carlos Martín Nieto committed
38

39
	spec = git__calloc(1, sizeof(git_refspec));
40
	GIT_ERROR_CHECK_ALLOC(spec);
41

42 43 44 45
	if (git_refspec__parse(spec, string, is_fetch) < 0) {
		git__free(spec);
		return -1;
	}
46 47

	spec->push = !is_fetch;
48
	if (git_vector_insert(vector, spec) < 0) {
49
		git_refspec__dispose(spec);
50 51 52
		git__free(spec);
		return -1;
	}
Carlos Martín Nieto committed
53

54
	return 0;
Carlos Martín Nieto committed
55 56
}

57 58 59 60 61
static int add_refspec(git_remote *remote, const char *string, bool is_fetch)
{
	return add_refspec_to(&remote->refspecs, string, is_fetch);
}

62 63
static int download_tags_value(git_remote *remote, git_config *cfg)
{
64
	git_config_entry *ce;
65
	git_str buf = GIT_STR_INIT;
66 67
	int error;

68
	if (git_str_printf(&buf, "remote.%s.tagopt", remote->name) < 0)
69 70
		return -1;

71 72
	error = git_config__lookup_entry(&ce, cfg, git_str_cstr(&buf), false);
	git_str_dispose(&buf);
73

74 75 76 77 78
	if (!error && ce && ce->value) {
		if (!strcmp(ce->value, "--no-tags"))
			remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
		else if (!strcmp(ce->value, "--tags"))
			remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
79
	}
80

81
	git_config_entry_free(ce);
82 83 84
	return error;
}

85 86
static int ensure_remote_name_is_valid(const char *name)
{
87
	int valid, error;
88

89 90 91
	error = git_remote_name_is_valid(&valid, name);

	if (!error && !valid) {
92 93
		git_error_set(
			GIT_ERROR_CONFIG,
94
			"'%s' is not a valid remote name.", name ? name : "(null)");
95 96 97 98 99 100
		error = GIT_EINVALIDSPEC;
	}

	return error;
}

101 102 103
static int write_add_refspec(git_repository *repo, const char *name, const char *refspec, bool fetch)
{
	git_config *cfg;
104
	git_str var = GIT_STR_INIT;
105
	git_refspec spec;
106 107 108 109 110 111 112 113 114 115 116
	const char *fmt;
	int error;

	if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
	    return error;

	fmt = fetch ? CONFIG_FETCH_FMT : CONFIG_PUSH_FMT;

	if ((error = ensure_remote_name_is_valid(name)) < 0)
		return error;

117
	if ((error = git_refspec__parse(&spec, refspec, fetch)) < 0)
118 119
		return error;

120
	git_refspec__dispose(&spec);
121

122
	if ((error = git_str_printf(&var, fmt, name)) < 0)
123 124 125
		return error;

	/*
Aaron Franke committed
126
	 * "$^" is an unmatchable regexp: it will not match anything at all, so
127 128 129 130 131 132 133 134
	 * all values will be considered new and we will not replace any
	 * present value.
	 */
	if ((error = git_config_set_multivar(cfg, var.ptr, "$^", refspec)) < 0) {
		goto cleanup;
	}

cleanup:
135
	git_str_dispose(&var);
136 137 138
	return 0;
}

139
static int canonicalize_url(git_str *out, const char *in)
140
{
141
	if (in == NULL || strlen(in) == 0) {
142
		git_error_set(GIT_ERROR_INVALID, "cannot set empty URL");
143 144
		return GIT_EINVALIDSPEC;
	}
145

146
#ifdef GIT_WIN32
147 148 149 150 151
	/* Given a UNC path like \\server\path, we need to convert this
	 * to //server/path for compatibility with core git.
	 */
	if (in[0] == '\\' && in[1] == '\\' &&
		(git__isalpha(in[2]) || git__isdigit(in[2]))) {
152
		const char *c;
153
		for (c = in; *c; c++)
154
			git_str_putc(out, *c == '\\' ? '/' : *c);
155

156
		return git_str_oom(out) ? -1 : 0;
157 158 159
	}
#endif

160
	return git_str_puts(out, in);
161 162
}

163
static int default_fetchspec_for_name(git_str *buf, const char *name)
164
{
165
	if (git_str_printf(buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0)
166 167 168 169 170
		return -1;

	return 0;
}

171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
static int ensure_remote_doesnot_exist(git_repository *repo, const char *name)
{
	int error;
	git_remote *remote;

	error = git_remote_lookup(&remote, repo, name);

	if (error == GIT_ENOTFOUND)
		return 0;

	if (error < 0)
		return error;

	git_remote_free(remote);

186
	git_error_set(GIT_ERROR_CONFIG, "remote '%s' already exists", name);
187 188 189 190

	return GIT_EEXISTS;
}

191
int git_remote_create_options_init(git_remote_create_options *opts, unsigned int version)
192
{
193 194 195 196 197
	GIT_INIT_STRUCTURE_FROM_TEMPLATE(
		opts, version, git_remote_create_options, GIT_REMOTE_CREATE_OPTIONS_INIT);
	return 0;
}

198
#ifndef GIT_DEPRECATE_HARD
199 200 201 202
int git_remote_create_init_options(git_remote_create_options *opts, unsigned int version)
{
	return git_remote_create_options_init(opts, version);
}
203
#endif
204

205
int git_remote_create_with_opts(git_remote **out, const char *url, const git_remote_create_options *opts)
206 207
{
	git_remote *remote = NULL;
208
	git_config *config_ro = NULL, *config_rw;
209 210 211
	git_str canonical_url = GIT_STR_INIT;
	git_str var = GIT_STR_INIT;
	git_str specbuf = GIT_STR_INIT;
212
	const git_remote_create_options dummy_opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
213
	int error = -1;
214

215 216
	GIT_ASSERT_ARG(out);
	GIT_ASSERT_ARG(url);
217

218 219 220 221
	if (!opts) {
		opts = &dummy_opts;
	}

222
	GIT_ERROR_CHECK_VERSION(opts, GIT_REMOTE_CREATE_OPTIONS_VERSION, "git_remote_create_options");
223 224 225 226 227 228 229 230 231 232 233 234 235 236

	if (opts->name != NULL) {
		if ((error = ensure_remote_name_is_valid(opts->name)) < 0)
			return error;

		if (opts->repository &&
		    (error = ensure_remote_doesnot_exist(opts->repository, opts->name)) < 0)
			return error;
	}

	if (opts->repository) {
		if ((error = git_repository_config_snapshot(&config_ro, opts->repository)) < 0)
			goto on_error;
	}
237

238
	remote = git__calloc(1, sizeof(git_remote));
239
	GIT_ERROR_CHECK_ALLOC(remote);
240

241
	remote->repo = opts->repository;
242

243
	if ((error = git_vector_init(&remote->refs, 8, NULL)) < 0 ||
244
		(error = canonicalize_url(&canonical_url, url)) < 0)
245
		goto on_error;
246

247
	if (opts->repository && !(opts->flags & GIT_REMOTE_CREATE_SKIP_INSTEADOF)) {
248 249 250 251
		remote->url = apply_insteadof(config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH);
	} else {
		remote->url = git__strdup(canonical_url.ptr);
	}
252
	GIT_ERROR_CHECK_ALLOC(remote->url);
253

254 255
	if (opts->name != NULL) {
		remote->name = git__strdup(opts->name);
256
		GIT_ERROR_CHECK_ALLOC(remote->name);
257

258
		if (opts->repository &&
259
		    ((error = git_str_printf(&var, CONFIG_URL_FMT, opts->name)) < 0 ||
260 261
		    (error = git_repository_config__weakptr(&config_rw, opts->repository)) < 0 ||
		    (error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0))
262
			goto on_error;
263 264
	}

265 266 267 268 269 270 271 272 273
	if (opts->fetchspec != NULL ||
	    (opts->name && !(opts->flags & GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC))) {
		const char *fetch = NULL;
		if (opts->fetchspec) {
			fetch = opts->fetchspec;
		} else {
			if ((error = default_fetchspec_for_name(&specbuf, opts->name)) < 0)
				goto on_error;

274
			fetch = git_str_cstr(&specbuf);
275 276 277
		}

		if ((error = add_refspec(remote, fetch, true)) < 0)
278 279
			goto on_error;

280
		/* only write for named remotes with a repository */
281
		if (opts->repository && opts->name &&
282
		    ((error = write_add_refspec(opts->repository, opts->name, fetch, true)) < 0 ||
283
		    (error = lookup_remote_prune_config(remote, config_ro, opts->name)) < 0))
284 285
			goto on_error;

286
		/* Move the data over to where the matching functions can find them */
Leo Yang committed
287
		if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
288
			goto on_error;
289 290
	}

291
	/* A remote without a name doesn't download tags */
292
	if (!opts->name)
293
		remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
294 295 296
	else
		remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;

297

298
	git_str_dispose(&var);
299

300
	*out = remote;
Edward Thomson committed
301
	error = 0;
302 303

on_error:
Edward Thomson committed
304 305 306
	if (error)
		git_remote_free(remote);

307
	git_config_free(config_ro);
308 309 310
	git_str_dispose(&specbuf);
	git_str_dispose(&canonical_url);
	git_str_dispose(&var);
311
	return error;
312 313
}

314 315
int git_remote_create(git_remote **out, git_repository *repo, const char *name, const char *url)
{
316
	git_str buf = GIT_STR_INIT;
317
	int error;
318
	git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
319

320 321 322 323 324 325 326
	/* Those 2 tests are duplicated here because of backward-compatibility */
	if ((error = ensure_remote_name_is_valid(name)) < 0)
		return error;

	if (canonicalize_url(&buf, url) < 0)
		return GIT_ERROR;

327
	git_str_clear(&buf);
328 329 330 331

	opts.repository = repo;
	opts.name = name;

332
	error = git_remote_create_with_opts(out, url, &opts);
333

334
	git_str_dispose(&buf);
335

336
	return error;
337 338
}

339 340 341
int git_remote_create_with_fetchspec(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
{
	int error;
342
	git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
343 344 345 346

	if ((error = ensure_remote_name_is_valid(name)) < 0)
		return error;

347 348 349
	opts.repository = repo;
	opts.name = name;
	opts.fetchspec = fetch;
350
	opts.flags = GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC;
351

352
	return git_remote_create_with_opts(out, url, &opts);
353 354
}

355
int git_remote_create_anonymous(git_remote **out, git_repository *repo, const char *url)
356
{
357 358 359 360
	git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;

	opts.repository = repo;

361
	return git_remote_create_with_opts(out, url, &opts);
362 363
}

364 365
int git_remote_create_detached(git_remote **out, const char *url)
{
366
	return git_remote_create_with_opts(out, url, NULL);
367 368
}

369
int git_remote_dup(git_remote **dest, git_remote *source)
Arthur Schreiber committed
370
{
371
	size_t i;
372
	int error = 0;
373
	git_refspec *spec;
Arthur Schreiber committed
374
	git_remote *remote = git__calloc(1, sizeof(git_remote));
375
	GIT_ERROR_CHECK_ALLOC(remote);
Arthur Schreiber committed
376 377 378

	if (source->name != NULL) {
		remote->name = git__strdup(source->name);
379
		GIT_ERROR_CHECK_ALLOC(remote->name);
Arthur Schreiber committed
380 381 382 383
	}

	if (source->url != NULL) {
		remote->url = git__strdup(source->url);
384
		GIT_ERROR_CHECK_ALLOC(remote->url);
Arthur Schreiber committed
385 386 387 388
	}

	if (source->pushurl != NULL) {
		remote->pushurl = git__strdup(source->pushurl);
389
		GIT_ERROR_CHECK_ALLOC(remote->pushurl);
Arthur Schreiber committed
390 391 392 393
	}

	remote->repo = source->repo;
	remote->download_tags = source->download_tags;
394
	remote->prune_refs = source->prune_refs;
Arthur Schreiber committed
395

396 397 398 399 400
	if (git_vector_init(&remote->refs, 32, NULL) < 0 ||
	    git_vector_init(&remote->refspecs, 2, NULL) < 0 ||
	    git_vector_init(&remote->active_refspecs, 2, NULL) < 0) {
		error = -1;
		goto cleanup;
Arthur Schreiber committed
401 402
	}

403 404 405 406
	git_vector_foreach(&source->refspecs, i, spec) {
		if ((error = add_refspec(remote, spec->string, !spec->push)) < 0)
			goto cleanup;
	}
407

Arthur Schreiber committed
408 409
	*dest = remote;

410 411 412 413 414 415
cleanup:

	if (error < 0)
		git__free(remote);

	return error;
Arthur Schreiber committed
416 417
}

418 419 420 421 422 423 424
struct refspec_cb_data {
	git_remote *remote;
	int fetch;
};

static int refspec_cb(const git_config_entry *entry, void *payload)
{
425
	struct refspec_cb_data *data = (struct refspec_cb_data *)payload;
426
	return add_refspec(data->remote, entry->value, data->fetch);
427 428
}

429
static int get_optional_config(
430
	bool *found, git_config *config, git_str *buf,
431
	git_config_foreach_cb cb, void *payload)
432 433
{
	int error = 0;
434
	const char *key = git_str_cstr(buf);
435

436
	if (git_str_oom(buf))
437 438 439
		return -1;

	if (cb != NULL)
440
		error = git_config_get_multivar_foreach(config, key, NULL, cb, payload);
441 442 443
	else
		error = git_config_get_string(payload, config, key);

444 445 446
	if (found)
		*found = !error;

447
	if (error == GIT_ENOTFOUND) {
448
		git_error_clear();
449 450 451 452 453 454
		error = 0;
	}

	return error;
}

455
int git_remote_lookup(git_remote **out, git_repository *repo, const char *name)
Carlos Martín Nieto committed
456
{
457
	git_remote *remote = NULL;
458
	git_str buf = GIT_STR_INIT;
Carlos Martín Nieto committed
459
	const char *val;
460
	int error = 0;
461
	git_config *config;
462
	struct refspec_cb_data data = { NULL };
463
	bool optional_setting_found = false, found;
464

465 466 467
	GIT_ASSERT_ARG(out);
	GIT_ASSERT_ARG(repo);
	GIT_ASSERT_ARG(name);
468

469 470 471
	if ((error = ensure_remote_name_is_valid(name)) < 0)
		return error;

472
	if ((error = git_repository_config_snapshot(&config, repo)) < 0)
473
		return error;
474

475
	remote = git__calloc(1, sizeof(git_remote));
476
	GIT_ERROR_CHECK_ALLOC(remote);
Carlos Martín Nieto committed
477 478

	remote->name = git__strdup(name);
479
	GIT_ERROR_CHECK_ALLOC(remote->name);
Carlos Martín Nieto committed
480

481 482
	if (git_vector_init(&remote->refs, 32, NULL) < 0 ||
	    git_vector_init(&remote->refspecs, 2, NULL) < 0 ||
483
	    git_vector_init(&remote->passive_refspecs, 2, NULL) < 0 ||
484
	    git_vector_init(&remote->active_refspecs, 2, NULL) < 0) {
485 486 487
		error = -1;
		goto cleanup;
	}
488

489
	if ((error = git_str_printf(&buf, "remote.%s.url", name)) < 0)
490
		goto cleanup;
Carlos Martín Nieto committed
491

492
	if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
Carlos Martín Nieto committed
493
		goto cleanup;
494

495 496
	optional_setting_found |= found;

497
	remote->repo = repo;
498
	remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
499 500

	if (found && strlen(val) > 0) {
501
		remote->url = apply_insteadof(config, val, GIT_DIRECTION_FETCH);
502
		GIT_ERROR_CHECK_ALLOC(remote->url);
503
	}
Carlos Martín Nieto committed
504

505
	val = NULL;
506 507
	git_str_clear(&buf);
	git_str_printf(&buf, "remote.%s.pushurl", name);
508

509
	if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
510 511
		goto cleanup;

512 513 514 515
	optional_setting_found |= found;

	if (!optional_setting_found) {
		error = GIT_ENOTFOUND;
516
		git_error_set(GIT_ERROR_CONFIG, "remote '%s' does not exist", name);
517
		goto cleanup;
518
	}
519

520
	if (found && strlen(val) > 0) {
521
		remote->pushurl = apply_insteadof(config, val, GIT_DIRECTION_PUSH);
522
		GIT_ERROR_CHECK_ALLOC(remote->pushurl);
523 524
	}

525 526
	data.remote = remote;
	data.fetch = true;
527

528 529
	git_str_clear(&buf);
	git_str_printf(&buf, "remote.%s.fetch", name);
530

531
	if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0)
532
		goto cleanup;
Carlos Martín Nieto committed
533

534
	data.fetch = false;
535 536
	git_str_clear(&buf);
	git_str_printf(&buf, "remote.%s.push", name);
Carlos Martín Nieto committed
537

538
	if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0)
Carlos Martín Nieto committed
539 540
		goto cleanup;

541
	if ((error = download_tags_value(remote, config)) < 0)
542 543
		goto cleanup;

544 545 546 547
	if ((error = lookup_remote_prune_config(remote, config, name)) < 0)
		goto cleanup;

	/* Move the data over to where the matching functions can find them */
Leo Yang committed
548
	if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
549 550 551 552 553 554
		goto cleanup;

	*out = remote;

cleanup:
	git_config_free(config);
555
	git_str_dispose(&buf);
556 557 558 559 560 561 562 563 564

	if (error < 0)
		git_remote_free(remote);

	return error;
}

static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name)
{
565
	git_str buf = GIT_STR_INIT;
566 567
	int error = 0;

568
	git_str_printf(&buf, "remote.%s.prune", name);
569

570
	if ((error = git_config_get_bool(&remote->prune_refs, config, git_str_cstr(&buf))) < 0) {
571
		if (error == GIT_ENOTFOUND) {
572
			git_error_clear();
573 574 575

			if ((error = git_config_get_bool(&remote->prune_refs, config, "fetch.prune")) < 0) {
				if (error == GIT_ENOTFOUND) {
576
					git_error_clear();
David Calavera committed
577
					error = 0;
578 579 580 581 582
				}
			}
		}
	}

583
	git_str_dispose(&buf);
Carlos Martín Nieto committed
584 585 586
	return error;
}

Ben Straub committed
587
const char *git_remote_name(const git_remote *remote)
Carlos Martín Nieto committed
588
{
589
	GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
Carlos Martín Nieto committed
590 591 592
	return remote->name;
}

Etienne Samson committed
593 594
git_repository *git_remote_owner(const git_remote *remote)
{
595
	GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
Etienne Samson committed
596 597 598
	return remote->repo;
}

Ben Straub committed
599
const char *git_remote_url(const git_remote *remote)
Carlos Martín Nieto committed
600
{
601
	GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
Carlos Martín Nieto committed
602 603 604
	return remote->url;
}

605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620
int git_remote_set_instance_url(git_remote *remote, const char *url)
{
	char *tmp;

	GIT_ASSERT_ARG(remote);
	GIT_ASSERT_ARG(url);

	if ((tmp = git__strdup(url)) == NULL)
		return -1;

	git__free(remote->url);
	remote->url = tmp;

	return 0;
}

621
static int set_url(git_repository *repo, const char *remote, const char *pattern, const char *url)
622
{
623
	git_config *cfg;
624
	git_str buf = GIT_STR_INIT, canonical_url = GIT_STR_INIT;
625
	int error;
626

627 628
	GIT_ASSERT_ARG(repo);
	GIT_ASSERT_ARG(remote);
629

630 631 632 633 634 635
	if ((error = ensure_remote_name_is_valid(remote)) < 0)
		return error;

	if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
		return error;

636
	if ((error = git_str_printf(&buf, pattern, remote)) < 0)
637 638 639 640 641 642 643 644 645 646 647 648
		return error;

	if (url) {
		if ((error = canonicalize_url(&canonical_url, url)) < 0)
			goto cleanup;

		error = git_config_set_string(cfg, buf.ptr, url);
	} else {
		error = git_config_delete_entry(cfg, buf.ptr);
	}

cleanup:
649 650
	git_str_dispose(&canonical_url);
	git_str_dispose(&buf);
651 652 653 654 655 656 657

	return error;
}

int git_remote_set_url(git_repository *repo, const char *remote, const char *url)
{
	return set_url(repo, remote, CONFIG_URL_FMT, url);
658 659
}

Ben Straub committed
660
const char *git_remote_pushurl(const git_remote *remote)
661
{
662
	GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
663 664 665
	return remote->pushurl;
}

666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681
int git_remote_set_instance_pushurl(git_remote *remote, const char *url)
{
	char *tmp;

	GIT_ASSERT_ARG(remote);
	GIT_ASSERT_ARG(url);

	if ((tmp = git__strdup(url)) == NULL)
		return -1;

	git__free(remote->pushurl);
	remote->pushurl = tmp;

	return 0;
}

682
int git_remote_set_pushurl(git_repository *repo, const char *remote, const char *url)
683
{
684
	return set_url(repo, remote, CONFIG_PUSHURL_FMT, url);
685 686
}

687
static int resolve_url(
688
	git_str *resolved_url,
689 690 691
	const char *url,
	int direction,
	const git_remote_callbacks *callbacks)
692
{
693 694 695 696
#ifdef GIT_DEPRECATE_HARD
	GIT_UNUSED(direction);
	GIT_UNUSED(callbacks);
#else
697 698
	git_buf buf = GIT_BUF_INIT;
	int error;
699 700

	if (callbacks && callbacks->resolve_url) {
701
		error = callbacks->resolve_url(&buf, url, direction, callbacks->payload);
702

703 704
		if (error != GIT_PASSTHROUGH) {
			git_error_set_after_callback_function(error, "git_resolve_url_cb");
705

706 707 708 709
			git_str_set(resolved_url, buf.ptr, buf.size);
			git_buf_dispose(&buf);

			return error;
710 711
		}
	}
712
#endif
713

714
	return git_str_sets(resolved_url, url);
715 716
}

717
int git_remote__urlfordirection(
718
	git_str *url_out,
719 720 721
	struct git_remote *remote,
	int direction,
	const git_remote_callbacks *callbacks)
722 723 724
{
	const char *url = NULL;

725 726
	GIT_ASSERT_ARG(remote);
	GIT_ASSERT_ARG(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
727

728 729 730 731 732 733 734 735 736 737
	if (callbacks && callbacks->remote_ready) {
		int status = callbacks->remote_ready(remote, direction, callbacks->payload);

		if (status != 0 && status != GIT_PASSTHROUGH) {
			git_error_set_after_callback_function(status, "git_remote_ready_cb");
			return status;
		}
	}

	if (direction == GIT_DIRECTION_FETCH)
738
		url = remote->url;
739
	else if (direction == GIT_DIRECTION_PUSH)
740
		url = remote->pushurl ? remote->pushurl : remote->url;
741

742 743 744 745 746 747
	if (!url) {
		git_error_set(GIT_ERROR_INVALID,
			"malformed remote '%s' - missing %s URL",
			remote->name ? remote->name : "(anonymous)",
			direction == GIT_DIRECTION_FETCH ? "fetch" : "push");
		return GIT_EINVALID;
748
	}
749

750
	return resolve_url(url_out, url, direction, callbacks);
751 752
}

753
static int remote_transport_set_callbacks(git_transport *t, const git_remote_callbacks *cbs)
754 755 756 757 758 759 760 761
{
	if (!t->set_callbacks || !cbs)
		return 0;

	return t->set_callbacks(t, cbs->sideband_progress, NULL,
				cbs->certificate_check, cbs->payload);
}

Matt Burke committed
762
static int set_transport_custom_headers(git_transport *t, const git_strarray *custom_headers)
763
{
764
	if (!t->set_custom_headers)
765 766 767 768 769
		return 0;

	return t->set_custom_headers(t, custom_headers);
}

770
int git_remote__connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_remote_connection_opts *conn)
771 772
{
	git_transport *t;
773
	git_str url = GIT_STR_INIT;
774
	int flags = GIT_TRANSPORTFLAGS_NONE;
775
	int error;
776
	void *payload = NULL;
777
	git_credential_acquire_cb credentials = NULL;
778
	git_transport_cb transport = NULL;
779

780
	GIT_ASSERT_ARG(remote);
781

782
	if (callbacks) {
783
		GIT_ERROR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
784
		credentials = callbacks->credentials;
785
		transport   = callbacks->transport;
786 787 788
		payload     = callbacks->payload;
	}

789
	if (conn->proxy)
790
		GIT_ERROR_CHECK_VERSION(conn->proxy, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
791

792 793
	t = remote->transport;

794 795
	if ((error = git_remote__urlfordirection(&url, remote, direction, callbacks)) < 0)
		goto on_error;
796

797 798
	/* If we don't have a transport object yet, and the caller specified a
	 * custom transport factory, use that */
799 800
	if (!t && transport &&
		(error = transport(&t, remote, payload)) < 0)
801
		goto on_error;
802 803 804

	/* If we still don't have a transport, then use the global
	 * transport registrations which map URI schemes to transport factories */
805 806
	if (!t && (error = git_transport_new(&t, remote, url.ptr)) < 0)
		goto on_error;
807

808
	if ((error = set_transport_custom_headers(t, conn->custom_headers)) != 0)
809 810
		goto on_error;

811
	if ((error = remote_transport_set_callbacks(t, callbacks)) < 0 ||
812
	    (error = t->connect(t, url.ptr, credentials, payload, conn->proxy, direction, flags)) != 0)
813
		goto on_error;
814 815 816

	remote->transport = t;

817
	git_str_dispose(&url);
818

819
	return 0;
820

821
on_error:
822 823 824
	if (t)
		t->free(t);

825
	git_str_dispose(&url);
826 827 828 829

	if (t == remote->transport)
		remote->transport = NULL;

830
	return error;
831 832
}

833 834 835 836 837 838 839 840 841 842
int git_remote_connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_proxy_options *proxy, const git_strarray *custom_headers)
{
	git_remote_connection_opts conn;

	conn.proxy = proxy;
	conn.custom_headers = custom_headers;

	return git_remote__connect(remote, direction, callbacks, &conn);
}

843
int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote)
844
{
845
	GIT_ASSERT_ARG(remote);
846

847
	if (!remote->transport) {
848
		git_error_set(GIT_ERROR_NET, "this remote has never connected");
849 850 851
		return -1;
	}

852
	return remote->transport->ls(out, size, remote->transport);
853 854
}

855
static int lookup_config(char **out, git_config *cfg, const char *name)
856
{
857 858
	git_config_entry *ce = NULL;
	int error;
859

860
	if ((error = git_config__lookup_entry(&ce, cfg, name, false)) < 0)
861 862
		return error;

863 864 865 866 867 868 869 870 871 872
	if (ce && ce->value) {
		*out = git__strdup(ce->value);
		GIT_ERROR_CHECK_ALLOC(*out);
	} else {
		error = GIT_ENOTFOUND;
	}

	git_config_entry_free(ce);
	return error;
}
873

874 875 876
static void url_config_trim(git_net_url *url)
{
	size_t len = strlen(url->path);
877

878 879 880 881 882 883
	if (url->path[len - 1] == '/') {
		len--;
	} else {
		while (len && url->path[len - 1] != '/')
			len--;
	}
884

885
	url->path[len] = '\0';
886 887
}

888
static int http_proxy_config(char **out, git_remote *remote, git_net_url *url)
889
{
Laurence McGlashan committed
890
	git_config *cfg = NULL;
891
	git_str buf = GIT_STR_INIT;
892
	git_net_url lookup_url = GIT_NET_URL_INIT;
893
	int error;
894

895
	if ((error = git_net_url_dup(&lookup_url, url)) < 0)
896
		goto done;
897

898
	if (remote->repo) {
899
		if ((error = git_repository_config(&cfg, remote->repo)) < 0)
900 901 902 903 904 905
			goto done;
	} else {
		if ((error = git_config_open_default(&cfg)) < 0)
			goto done;
	}

906 907
	/* remote.<name>.proxy config setting */
	if (remote->name && remote->name[0]) {
908
		git_str_clear(&buf);
909

910
		if ((error = git_str_printf(&buf, "remote.%s.proxy", remote->name)) < 0 ||
911 912 913
		    (error = lookup_config(out, cfg, buf.ptr)) != GIT_ENOTFOUND)
			goto done;
	}
914

915
	while (true) {
916
		git_str_clear(&buf);
917

918
		if ((error = git_str_puts(&buf, "http.")) < 0 ||
919
		    (error = git_net_url_fmt(&buf, &lookup_url)) < 0 ||
920
		    (error = git_str_puts(&buf, ".proxy")) < 0 ||
921 922
		    (error = lookup_config(out, cfg, buf.ptr)) != GIT_ENOTFOUND)
			goto done;
923

924 925
		if (! lookup_url.path[0])
			break;
926

927 928
		url_config_trim(&lookup_url);
	}
929

930
	git_str_clear(&buf);
931

932
	error = lookup_config(out, cfg, "http.proxy");
933

934
done:
935
	git_config_free(cfg);
936
	git_str_dispose(&buf);
937 938 939
	git_net_url_dispose(&lookup_url);
	return error;
}
940

941 942
static int http_proxy_env(char **out, git_remote *remote, git_net_url *url)
{
943
	git_str proxy_env = GIT_STR_INIT, no_proxy_env = GIT_STR_INIT;
944 945
	bool use_ssl = (strcmp(url->scheme, "https") == 0);
	int error;
946

947
	GIT_UNUSED(remote);
948

949
	/* http_proxy / https_proxy environment variables */
950
	error = git__getenv(&proxy_env, use_ssl ? "https_proxy" : "http_proxy");
951

952 953
	/* try uppercase environment variables */
	if (error == GIT_ENOTFOUND)
954
		error = git__getenv(&proxy_env, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY");
955

956 957
	if (error)
		goto done;
958

959 960
	/* no_proxy/NO_PROXY environment variables */
	error = git__getenv(&no_proxy_env, "no_proxy");
961

962 963 964
	if (error == GIT_ENOTFOUND)
		error = git__getenv(&no_proxy_env, "NO_PROXY");

965 966
	if (error && error != GIT_ENOTFOUND)
		goto done;
967

968
	if (!git_net_url_matches_pattern_list(url, no_proxy_env.ptr))
969
		*out = git_str_detach(&proxy_env);
970 971
	else
		error = GIT_ENOTFOUND;
972

973
done:
974 975
	git_str_dispose(&proxy_env);
	git_str_dispose(&no_proxy_env);
976
	return error;
977 978
}

979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999
int git_remote__http_proxy(char **out, git_remote *remote, git_net_url *url)
{
	int error;

	GIT_ASSERT_ARG(out);
	GIT_ASSERT_ARG(remote);

	*out = NULL;

	/*
	 * Go through the possible sources for proxy configuration,
	 * Examine the various git config options first, then
	 * consult environment variables.
	 */
	if ((error = http_proxy_config(out, remote, url)) != GIT_ENOTFOUND ||
	    (error = http_proxy_env(out, remote, url)) != GIT_ENOTFOUND)
		return error;

	return 0;
}

1000 1001
/* DWIM `refspecs` based on `refs` and append the output to `out` */
static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs)
1002
{
1003
	size_t i;
1004 1005 1006
	git_refspec *spec;

	git_vector_foreach(refspecs, i, spec) {
1007 1008 1009
		if (git_refspec__dwim_one(out, spec, refs) < 0)
			return -1;
	}
1010

1011 1012
	return 0;
}
1013

1014 1015 1016 1017
static void free_refspecs(git_vector *vec)
{
	size_t i;
	git_refspec *spec;
1018

1019
	git_vector_foreach(vec, i, spec) {
1020
		git_refspec__dispose(spec);
1021
		git__free(spec);
1022 1023
	}

1024
	git_vector_clear(vec);
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034
}

static int remote_head_cmp(const void *_a, const void *_b)
{
	const git_remote_head *a = (git_remote_head *) _a;
	const git_remote_head *b = (git_remote_head *) _b;

	return git__strcmp_cb(a->name, b->name);
}

1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053
static int ls_to_vector(git_vector *out, git_remote *remote)
{
	git_remote_head **heads;
	size_t heads_len, i;

	if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0)
		return -1;

	if (git_vector_init(out, heads_len, remote_head_cmp) < 0)
		return -1;

	for (i = 0; i < heads_len; i++) {
		if (git_vector_insert(out, heads[i]) < 0)
			return -1;
	}

	return 0;
}

1054
int git_remote_download(git_remote *remote, const git_strarray *refspecs, const git_fetch_options *opts)
1055
{
1056
	int error = -1;
1057
	size_t i;
1058
	git_vector *to_active, specs = GIT_VECTOR_INIT, refs = GIT_VECTOR_INIT;
1059
	const git_remote_callbacks *cbs = NULL;
1060
	const git_strarray *custom_headers = NULL;
1061
	const git_proxy_options *proxy = NULL;
1062

1063
	GIT_ASSERT_ARG(remote);
1064

1065
	if (!remote->repo) {
1066
		git_error_set(GIT_ERROR_INVALID, "cannot download detached remote");
1067 1068 1069
		return -1;
	}

1070
	if (opts) {
1071
		GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
1072
		cbs = &opts->callbacks;
1073
		custom_headers = &opts->custom_headers;
1074
		GIT_ERROR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
1075
		proxy = &opts->proxy_opts;
1076 1077 1078
	}

	if (!git_remote_connected(remote) &&
1079
	    (error = git_remote_connect(remote, GIT_DIRECTION_FETCH, cbs, proxy, custom_headers)) < 0)
1080 1081
		goto on_error;

1082
	if (ls_to_vector(&refs, remote) < 0)
1083 1084
		return -1;

1085 1086 1087
	if ((git_vector_init(&specs, 0, NULL)) < 0)
		goto on_error;

1088
	remote->passed_refspecs = 0;
1089
	if (!refspecs || !refspecs->count) {
1090 1091 1092 1093 1094 1095 1096 1097
		to_active = &remote->refspecs;
	} else {
		for (i = 0; i < refspecs->count; i++) {
			if ((error = add_refspec_to(&specs, refspecs->strings[i], true)) < 0)
				goto on_error;
		}

		to_active = &specs;
1098
		remote->passed_refspecs = 1;
1099 1100
	}

1101 1102 1103
	free_refspecs(&remote->passive_refspecs);
	if ((error = dwim_refspecs(&remote->passive_refspecs, &remote->refspecs, &refs)) < 0)
		goto on_error;
1104

1105
	free_refspecs(&remote->active_refspecs);
1106
	error = dwim_refspecs(&remote->active_refspecs, to_active, &refs);
1107

1108
	git_vector_free(&refs);
1109 1110
	free_refspecs(&specs);
	git_vector_free(&specs);
1111 1112

	if (error < 0)
1113
		return error;
1114

1115 1116 1117 1118 1119
	if (remote->push) {
		git_push_free(remote->push);
		remote->push = NULL;
	}

1120
	if ((error = git_fetch_negotiate(remote, opts)) < 0)
1121
		return error;
1122

1123
	return git_fetch_download_pack(remote, cbs);
1124 1125 1126 1127 1128 1129

on_error:
	git_vector_free(&refs);
	free_refspecs(&specs);
	git_vector_free(&specs);
	return error;
1130 1131
}

1132 1133
int git_remote_fetch(
		git_remote *remote,
1134
		const git_strarray *refspecs,
1135
		const git_fetch_options *opts,
1136
		const char *reflog_message)
1137
{
1138
	int error, update_fetchhead = 1;
1139
	git_remote_autotag_option_t tagopt = remote->download_tags;
1140
	bool prune = false;
1141
	git_str reflog_msg_buf = GIT_STR_INIT;
1142
	const git_remote_callbacks *cbs = NULL;
1143
	git_remote_connection_opts conn = GIT_REMOTE_CONNECTION_OPTIONS_INIT;
1144 1145

	if (opts) {
1146
		GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
1147
		cbs = &opts->callbacks;
1148
		conn.custom_headers = &opts->custom_headers;
1149
		update_fetchhead = opts->update_fetchhead;
1150
		tagopt = opts->download_tags;
1151
		GIT_ERROR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
1152
		conn.proxy = &opts->proxy_opts;
1153
	}
1154 1155

	/* Connect and download everything */
1156
	if ((error = git_remote__connect(remote, GIT_DIRECTION_FETCH, cbs, &conn)) != 0)
1157 1158
		return error;

1159
	error = git_remote_download(remote, refspecs, opts);
1160 1161 1162 1163

	/* We don't need to be connected anymore */
	git_remote_disconnect(remote);

1164 1165 1166 1167
	/* If the download failed, return the error */
	if (error != 0)
		return error;

1168 1169
	/* Default reflog message */
	if (reflog_message)
1170
		git_str_sets(&reflog_msg_buf, reflog_message);
1171
	else {
1172
		git_str_printf(&reflog_msg_buf, "fetch %s",
1173 1174 1175
				remote->name ? remote->name : remote->url);
	}

1176
	/* Create "remote/foo" branches for all remote branches */
1177 1178
	error = git_remote_update_tips(remote, cbs, update_fetchhead, tagopt, git_str_cstr(&reflog_msg_buf));
	git_str_dispose(&reflog_msg_buf);
1179 1180 1181
	if (error < 0)
		return error;

1182 1183
	if (opts && opts->prune == GIT_FETCH_PRUNE)
		prune = true;
1184
	else if (opts && opts->prune == GIT_FETCH_PRUNE_UNSPECIFIED && remote->prune_refs)
1185 1186 1187 1188 1189 1190 1191
		prune = true;
	else if (opts && opts->prune == GIT_FETCH_NO_PRUNE)
		prune = false;
	else
		prune = remote->prune_refs;

	if (prune)
1192
		error = git_remote_prune(remote, cbs);
1193

1194
	return error;
1195 1196
}

1197 1198 1199 1200 1201
static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src)
{
	unsigned int i;
	git_remote_head *remote_ref;

1202 1203
	GIT_ASSERT_ARG(update_heads);
	GIT_ASSERT_ARG(fetchspec_src);
1204 1205

	*out = NULL;
nulltoken committed
1206 1207 1208 1209 1210

	git_vector_foreach(update_heads, i, remote_ref) {
		if (strcmp(remote_ref->name, fetchspec_src) == 0) {
			*out = remote_ref;
			break;
1211 1212 1213 1214 1215 1216
		}
	}

	return 0;
}

1217
static int ref_to_update(int *update, git_str *remote_name, git_remote *remote, git_refspec *spec, const char *ref_name)
1218 1219 1220
{
	int error = 0;
	git_repository *repo;
1221 1222
	git_str upstream_remote = GIT_STR_INIT;
	git_str upstream_name = GIT_STR_INIT;
1223 1224 1225 1226 1227

	repo = git_remote_owner(remote);

	if ((!git_reference__is_branch(ref_name)) ||
	    !git_remote_name(remote) ||
1228 1229 1230 1231 1232
	    (error = git_branch__upstream_remote(&upstream_remote, repo, ref_name) < 0) ||
	    git__strcmp(git_remote_name(remote), git_str_cstr(&upstream_remote)) ||
	    (error = git_branch__upstream_name(&upstream_name, repo, ref_name)) < 0 ||
	    !git_refspec_dst_matches(spec, git_str_cstr(&upstream_name)) ||
	    (error = git_refspec__rtransform(remote_name, spec, upstream_name.ptr)) < 0) {
1233 1234
		/* Not an error if there is no upstream */
		if (error == GIT_ENOTFOUND) {
1235
			git_error_clear();
1236 1237 1238 1239 1240 1241 1242 1243
			error = 0;
		}

		*update = 0;
	} else {
		*update = 1;
	}

1244 1245
	git_str_dispose(&upstream_remote);
	git_str_dispose(&upstream_name);
1246 1247 1248
	return error;
}

1249
static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_refspec *spec, git_vector *update_heads, git_reference *ref)
1250 1251
{
	git_reference *resolved_ref = NULL;
1252
	git_str remote_name = GIT_STR_INIT;
1253
	git_config *config = NULL;
1254
	const char *ref_name;
1255
	int error = 0, update;
1256

1257 1258 1259
	GIT_ASSERT_ARG(out);
	GIT_ASSERT_ARG(spec);
	GIT_ASSERT_ARG(ref);
1260 1261 1262

	*out = NULL;

1263 1264 1265
	error = git_reference_resolve(&resolved_ref, ref);

	/* If we're in an unborn branch, let's pretend nothing happened */
1266
	if (error == GIT_ENOTFOUND && git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) {
1267 1268 1269 1270 1271 1272
		ref_name = git_reference_symbolic_target(ref);
		error = 0;
	} else {
		ref_name = git_reference_name(resolved_ref);
	}

1273 1274 1275 1276 1277 1278 1279 1280 1281 1282
	/*
	 * The ref name may be unresolvable - perhaps it's pointing to
	 * something invalid.  In this case, there is no remote head for
	 * this ref.
	 */
	if (!ref_name) {
		error = 0;
		goto cleanup;
	}

1283
	if ((error = ref_to_update(&update, &remote_name, remote, spec, ref_name)) < 0)
1284 1285
		goto cleanup;

1286
	if (update)
1287
		error = remote_head_for_fetchspec_src(out, update_heads, git_str_cstr(&remote_name));
1288 1289

cleanup:
1290
	git_str_dispose(&remote_name);
1291
	git_reference_free(resolved_ref);
1292
	git_config_free(config);
1293 1294 1295
	return error;
}

1296
static int git_remote_write_fetchhead(git_remote *remote, git_refspec *spec, git_vector *update_heads)
1297 1298 1299 1300 1301 1302 1303 1304 1305
{
	git_reference *head_ref = NULL;
	git_fetchhead_ref *fetchhead_ref;
	git_remote_head *remote_ref, *merge_remote_ref;
	git_vector fetchhead_refs;
	bool include_all_fetchheads;
	unsigned int i = 0;
	int error = 0;

1306
	GIT_ASSERT_ARG(remote);
1307

1308 1309 1310 1311
	/* no heads, nothing to do */
	if (update_heads->length == 0)
		return 0;

1312 1313 1314 1315 1316 1317 1318 1319 1320
	if (git_vector_init(&fetchhead_refs, update_heads->length, git_fetchhead_ref_cmp) < 0)
		return -1;

	/* Iff refspec is * (but not subdir slash star), include tags */
	include_all_fetchheads = (strcmp(GIT_REFS_HEADS_DIR "*", git_refspec_src(spec)) == 0);

	/* Determine what to merge: if refspec was a wildcard, just use HEAD */
	if (git_refspec_is_wildcard(spec)) {
		if ((error = git_reference_lookup(&head_ref, remote->repo, GIT_HEAD_FILE)) < 0 ||
1321
			(error = remote_head_for_ref(&merge_remote_ref, remote, spec, update_heads, head_ref)) < 0)
1322 1323 1324 1325 1326 1327 1328 1329
				goto cleanup;
	} else {
		/* If we're fetching a single refspec, that's the only thing that should be in FETCH_HEAD. */
		if ((error = remote_head_for_fetchspec_src(&merge_remote_ref, update_heads, git_refspec_src(spec))) < 0)
			goto cleanup;
	}

	/* Create the FETCH_HEAD file */
nulltoken committed
1330
	git_vector_foreach(update_heads, i, remote_ref) {
1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360
		int merge_this_fetchhead = (merge_remote_ref == remote_ref);

		if (!include_all_fetchheads &&
			!git_refspec_src_matches(spec, remote_ref->name) &&
			!merge_this_fetchhead)
			continue;

		if (git_fetchhead_ref_create(&fetchhead_ref,
			&remote_ref->oid,
			merge_this_fetchhead,
			remote_ref->name,
			git_remote_url(remote)) < 0)
			goto cleanup;

		if (git_vector_insert(&fetchhead_refs, fetchhead_ref) < 0)
			goto cleanup;
	}

	git_fetchhead_write(remote->repo, &fetchhead_refs);

cleanup:
	for (i = 0; i < fetchhead_refs.length; ++i)
		git_fetchhead_ref_free(fetchhead_refs.contents[i]);

	git_vector_free(&fetchhead_refs);
	git_reference_free(head_ref);

	return error;
}

1361 1362 1363 1364 1365
/**
 * Generate a list of candidates for pruning by getting a list of
 * references which match the rhs of an active refspec.
 */
static int prune_candidates(git_vector *candidates, git_remote *remote)
1366 1367
{
	git_strarray arr = { 0 };
1368
	size_t i;
1369 1370 1371 1372 1373
	int error;

	if ((error = git_reference_list(&arr, remote->repo)) < 0)
		return error;

1374 1375 1376 1377 1378 1379 1380 1381
	for (i = 0; i < arr.count; i++) {
		const char *refname = arr.strings[i];
		char *refname_dup;

		if (!git_remote__matching_dst_refspec(remote, refname))
			continue;

		refname_dup = git__strdup(refname);
1382
		GIT_ERROR_CHECK_ALLOC(refname_dup);
1383 1384 1385 1386 1387 1388

		if ((error = git_vector_insert(candidates, refname_dup)) < 0)
			goto out;
	}

out:
1389
	git_strarray_dispose(&arr);
1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400
	return error;
}

static int find_head(const void *_a, const void *_b)
{
	git_remote_head *a = (git_remote_head *) _a;
	git_remote_head *b = (git_remote_head *) _b;

	return strcmp(a->name, b->name);
}

1401
int git_remote_prune(git_remote *remote, const git_remote_callbacks *callbacks)
1402 1403 1404 1405 1406 1407 1408 1409 1410
{
	size_t i, j;
	git_vector remote_refs = GIT_VECTOR_INIT;
	git_vector candidates = GIT_VECTOR_INIT;
	const git_refspec *spec;
	const char *refname;
	int error;
	git_oid zero_id = {{ 0 }};

1411
	if (callbacks)
1412
		GIT_ERROR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
1413

1414 1415 1416
	if ((error = ls_to_vector(&remote_refs, remote)) < 0)
		goto cleanup;

1417
	git_vector_set_cmp(&remote_refs, find_head);
1418

1419 1420
	if ((error = prune_candidates(&candidates, remote)) < 0)
		goto cleanup;
1421

1422 1423 1424 1425 1426 1427
	/*
	 * Remove those entries from the candidate list for which we
	 * can find a remote reference in at least one refspec.
	 */
	git_vector_foreach(&candidates, i, refname) {
		git_vector_foreach(&remote->active_refspecs, j, spec) {
1428
			git_str buf = GIT_STR_INIT;
1429 1430 1431 1432 1433
			size_t pos;
			char *src_name;
			git_remote_head key = {0};

			if (!git_refspec_dst_matches(spec, refname))
1434 1435
				continue;

1436
			if ((error = git_refspec__rtransform(&buf, spec, refname)) < 0)
1437
				goto cleanup;
1438

1439
			key.name = (char *) git_str_cstr(&buf);
1440
			error = git_vector_bsearch(&pos, &remote_refs, &key);
1441
			git_str_dispose(&buf);
1442

1443 1444
			if (error < 0 && error != GIT_ENOTFOUND)
				goto cleanup;
1445

1446
			if (error == GIT_ENOTFOUND)
1447 1448
				continue;

Aaron Franke committed
1449
			/* If we did find a source, remove it from the candidates. */
1450 1451 1452 1453 1454
			if ((error = git_vector_set((void **) &src_name, &candidates, i, NULL)) < 0)
				goto cleanup;

			git__free(src_name);
			break;
1455 1456 1457
		}
	}

1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478
	/*
	 * For those candidates still left in the list, we need to
	 * remove them. We do not remove symrefs, as those are for
	 * stuff like origin/HEAD which will never match, but we do
	 * not want to remove them.
	 */
	git_vector_foreach(&candidates, i, refname) {
		git_reference *ref;
		git_oid id;

		if (refname == NULL)
			continue;

		error = git_reference_lookup(&ref, remote->repo, refname);
		/* as we want it gone, let's not consider this an error */
		if (error == GIT_ENOTFOUND)
			continue;

		if (error < 0)
			goto cleanup;

1479
		if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) {
1480 1481 1482 1483 1484 1485 1486 1487 1488 1489
			git_reference_free(ref);
			continue;
		}

		git_oid_cpy(&id, git_reference_target(ref));
		error = git_reference_delete(ref);
		git_reference_free(ref);
		if (error < 0)
			goto cleanup;

1490 1491
		if (callbacks && callbacks->update_tips)
			error = callbacks->update_tips(refname, &id, &zero_id, callbacks->payload);
1492 1493 1494 1495 1496

		if (error < 0)
			goto cleanup;
	}

1497 1498
cleanup:
	git_vector_free(&remote_refs);
1499
	git_vector_free_deep(&candidates);
1500 1501 1502
	return error;
}

1503 1504
static int update_tips_for_spec(
		git_remote *remote,
1505
		const git_remote_callbacks *callbacks,
1506
		int update_fetchhead,
1507
		git_remote_autotag_option_t tagopt,
1508 1509 1510
		git_refspec *spec,
		git_vector *refs,
		const char *log_message)
1511
{
1512
	int error = 0, autotag, valid;
1513
	unsigned int i = 0;
1514
	git_str refname = GIT_STR_INIT;
1515
	git_oid old;
1516
	git_odb *odb;
1517 1518
	git_remote_head *head;
	git_reference *ref;
1519
	git_refspec tagspec;
1520
	git_vector update_heads;
1521

1522
	GIT_ASSERT_ARG(remote);
1523

1524
	if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
1525 1526
		return -1;

1527
	if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
1528 1529
		return -1;

1530
	/* Make a copy of the transport's refs */
1531
	if (git_vector_init(&update_heads, 16, NULL) < 0)
1532
		return -1;
1533

1534 1535
	for (; i < refs->length; ++i) {
		head = git_vector_get(refs, i);
1536
		autotag = 0;
1537
		git_str_clear(&refname);
1538

1539
		/* Ignore malformed ref names (which also saves us from tag^{} */
1540 1541 1542 1543
		if (git_reference_name_is_valid(&valid, head->name) < 0)
			goto on_error;

		if (!valid)
1544 1545
			continue;

1546
		/* If we have a tag, see if the auto-follow rules say to update it */
1547
		if (git_refspec_src_matches(&tagspec, head->name)) {
1548
			if (tagopt != GIT_REMOTE_DOWNLOAD_TAGS_NONE) {
1549

1550
				if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_AUTO)
1551
					autotag = 1;
1552

1553 1554
				git_str_clear(&refname);
				if (git_str_puts(&refname, head->name) < 0)
1555 1556
					goto on_error;
			}
1557 1558 1559 1560
		}

		/* If we didn't want to auto-follow the tag, check if the refspec matches */
		if (!autotag && git_refspec_src_matches(spec, head->name)) {
1561
			if (spec->dst) {
1562
				if (git_refspec__transform(&refname, spec, head->name) < 0)
1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573
					goto on_error;
			} else {
				/*
				 * no rhs mans store it in FETCH_HEAD, even if we don't
				 update anything else.
				 */
				if ((error = git_vector_insert(&update_heads, head)) < 0)
					goto on_error;

				continue;
			}
1574 1575 1576
		}

		/* If we still don't have a refname, we don't want it */
1577
		if (git_str_len(&refname) == 0) {
1578 1579 1580
			continue;
		}

1581
		/* In autotag mode, only create tags for objects already in db */
1582 1583
		if (autotag && !git_odb_exists(odb, &head->oid))
			continue;
1584

1585
		if (!autotag && git_vector_insert(&update_heads, head) < 0)
1586 1587
			goto on_error;

1588
		error = git_reference_name_to_id(&old, remote->repo, refname.ptr);
1589
		if (error < 0 && error != GIT_ENOTFOUND)
1590 1591
			goto on_error;

1592 1593 1594
		if (!(error || error == GIT_ENOTFOUND)
				&& !spec->force
				&& !git_graph_descendant_of(remote->repo, &head->oid, &old))
1595 1596
			continue;

1597
		if (error == GIT_ENOTFOUND) {
1598 1599
			memset(&old, 0, GIT_OID_RAWSZ);

1600 1601 1602 1603
			if (autotag && git_vector_insert(&update_heads, head) < 0)
				goto on_error;
		}

1604
		if (!git_oid__cmp(&old, &head->oid))
1605
			continue;
1606

1607
		/* In autotag mode, don't overwrite any locally-existing tags */
1608
		error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag,
1609
				log_message);
1610 1611 1612 1613 1614

		if (error == GIT_EEXISTS)
			continue;

		if (error < 0)
1615
			goto on_error;
1616 1617

		git_reference_free(ref);
1618

1619 1620
		if (callbacks && callbacks->update_tips != NULL) {
			if (callbacks->update_tips(refname.ptr, &old, &head->oid, callbacks->payload) < 0)
1621 1622
				goto on_error;
		}
1623 1624
	}

1625
	if (update_fetchhead &&
1626
	    (error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0)
1627 1628 1629
		goto on_error;

	git_vector_free(&update_heads);
1630
	git_refspec__dispose(&tagspec);
1631
	git_str_dispose(&refname);
1632 1633 1634
	return 0;

on_error:
1635
	git_vector_free(&update_heads);
1636
	git_refspec__dispose(&tagspec);
1637
	git_str_dispose(&refname);
1638
	return -1;
1639

1640 1641
}

1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656
/**
 * Iteration over the three vectors, with a pause whenever we find a match
 *
 * On each stop, we store the iteration stat in the inout i,j,k
 * parameters, and return the currently matching passive refspec as
 * well as the head which we matched.
 */
static int next_head(const git_remote *remote, git_vector *refs,
		     git_refspec **out_spec, git_remote_head **out_head,
		     size_t *out_i, size_t *out_j, size_t *out_k)
{
	const git_vector *active, *passive;
	git_remote_head *head;
	git_refspec *spec, *passive_spec;
	size_t i, j, k;
1657
	int valid;
1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668

	active = &remote->active_refspecs;
	passive = &remote->passive_refspecs;

	i = *out_i;
	j = *out_j;
	k = *out_k;

	for (; i < refs->length; i++) {
		head = git_vector_get(refs, i);

1669 1670 1671 1672
		if (git_reference_name_is_valid(&valid, head->name) < 0)
			return -1;

		if (!valid)
1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702
			continue;

		for (; j < active->length; j++) {
			spec = git_vector_get(active, j);

			if (!git_refspec_src_matches(spec, head->name))
				continue;

			for (; k < passive->length; k++) {
				passive_spec = git_vector_get(passive, k);

				if (!git_refspec_src_matches(passive_spec, head->name))
				    continue;

				*out_spec = passive_spec;
				*out_head = head;
				*out_i = i;
				*out_j = j;
				*out_k = k + 1;
				return 0;

			}
			k = 0;
		}
		j = 0;
	}

	return GIT_ITEROVER;
}

1703 1704
static int opportunistic_updates(const git_remote *remote, const git_remote_callbacks *callbacks,
				 git_vector *refs, const char *msg)
1705 1706 1707 1708 1709
{
	size_t i, j, k;
	git_refspec *spec;
	git_remote_head *head;
	git_reference *ref;
1710
	git_str refname = GIT_STR_INIT;
1711
	int error = 0;
1712 1713 1714 1715

	i = j = k = 0;

	while ((error = next_head(remote, refs, &spec, &head, &i, &j, &k)) == 0) {
1716
		git_oid old = {{ 0 }};
1717 1718 1719 1720 1721 1722 1723 1724
		/*
		 * If we got here, there is a refspec which was used
		 * for fetching which matches the source of one of the
		 * passive refspecs, so we should update that
		 * remote-tracking branch, but not add it to
		 * FETCH_HEAD
		 */

1725 1726
		git_str_clear(&refname);
		if ((error = git_refspec__transform(&refname, spec, head->name)) < 0)
1727
			goto cleanup;
1728

1729 1730 1731 1732 1733 1734
		error = git_reference_name_to_id(&old, remote->repo, refname.ptr);
		if (error < 0 && error != GIT_ENOTFOUND)
			goto cleanup;

		if (!git_oid_cmp(&old, &head->oid))
			continue;
1735

1736 1737 1738 1739 1740 1741
		/* If we did find a current reference, make sure we haven't lost a race */
		if (error)
			error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, true, msg);
		else
			error = git_reference_create_matching(&ref, remote->repo, refname.ptr, &head->oid, true, &old, msg);
		git_reference_free(ref);
1742
		if (error < 0)
1743 1744 1745 1746 1747 1748
			goto cleanup;

		if (callbacks && callbacks->update_tips != NULL) {
			if (callbacks->update_tips(refname.ptr, &old, &head->oid, callbacks->payload) < 0)
				goto cleanup;
		}
1749 1750
	}

1751 1752 1753 1754
	if (error == GIT_ITEROVER)
		error = 0;

cleanup:
1755
	git_str_dispose(&refname);
1756
	return error;
1757 1758
}

1759 1760
static int truncate_fetch_head(const char *gitdir)
{
1761
	git_str path = GIT_STR_INIT;
1762 1763
	int error;

1764
	if ((error = git_str_joinpath(&path, gitdir, GIT_FETCH_HEAD_FILE)) < 0)
1765 1766 1767
		return error;

	error = git_futils_truncate(path.ptr, GIT_REFS_FILE_MODE);
1768
	git_str_dispose(&path);
1769 1770 1771 1772

	return error;
}

1773 1774
int git_remote_update_tips(
		git_remote *remote,
1775
		const git_remote_callbacks *callbacks,
1776
		int update_fetchhead,
1777
		git_remote_autotag_option_t download_tags,
1778
		const char *reflog_message)
1779
{
1780
	git_refspec *spec, tagspec;
1781
	git_vector refs = GIT_VECTOR_INIT;
1782
	git_remote_autotag_option_t tagopt;
1783
	int error;
1784 1785
	size_t i;

1786 1787
	/* push has its own logic hidden away in the push object */
	if (remote->push) {
1788
		return git_push_update_tips(remote->push, callbacks);
1789 1790
	}

1791 1792 1793
	if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
		return -1;

1794 1795

	if ((error = ls_to_vector(&refs, remote)) < 0)
1796 1797
		goto out;

1798
	if (download_tags == GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED)
1799 1800 1801 1802
		tagopt = remote->download_tags;
	else
		tagopt = download_tags;

1803 1804 1805
	if ((error = truncate_fetch_head(git_repository_path(remote->repo))) < 0)
		goto out;

1806 1807
	if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
		if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, &tagspec, &refs, reflog_message)) < 0)
1808
			goto out;
1809
	}
1810

1811
	git_vector_foreach(&remote->active_refspecs, i, spec) {
1812 1813 1814
		if (spec->push)
			continue;

1815
		if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, spec, &refs, reflog_message)) < 0)
1816
			goto out;
1817 1818
	}

Aaron Franke committed
1819
	/* Only try to do opportunistic updates if the refpec lists differ. */
1820
	if (remote->passed_refspecs)
1821
		error = opportunistic_updates(remote, callbacks, &refs, reflog_message);
1822

1823
out:
1824
	git_vector_free(&refs);
1825
	git_refspec__dispose(&tagspec);
1826
	return error;
1827 1828
}

1829
int git_remote_connected(const git_remote *remote)
1830
{
1831
	GIT_ASSERT_ARG(remote);
1832 1833 1834 1835 1836

	if (!remote->transport || !remote->transport->is_connected)
		return 0;

	/* Ask the transport if it's connected. */
1837
	return remote->transport->is_connected(remote->transport);
1838 1839
}

1840
int git_remote_stop(git_remote *remote)
1841
{
1842
	GIT_ASSERT_ARG(remote);
1843 1844

	if (remote->transport && remote->transport->cancel)
1845
		remote->transport->cancel(remote->transport);
1846 1847

	return 0;
1848 1849
}

1850
int git_remote_disconnect(git_remote *remote)
1851
{
1852
	GIT_ASSERT_ARG(remote);
1853

1854 1855
	if (git_remote_connected(remote))
		remote->transport->close(remote->transport);
1856 1857

	return 0;
1858 1859
}

Carlos Martín Nieto committed
1860 1861
void git_remote_free(git_remote *remote)
{
1862 1863 1864
	if (remote == NULL)
		return;

1865 1866 1867 1868 1869 1870 1871 1872 1873
	if (remote->transport != NULL) {
		git_remote_disconnect(remote);

		remote->transport->free(remote->transport);
		remote->transport = NULL;
	}

	git_vector_free(&remote->refs);

1874
	free_refspecs(&remote->refspecs);
1875 1876
	git_vector_free(&remote->refspecs);

1877 1878 1879
	free_refspecs(&remote->active_refspecs);
	git_vector_free(&remote->active_refspecs);

1880 1881 1882
	free_refspecs(&remote->passive_refspecs);
	git_vector_free(&remote->passive_refspecs);

1883
	git_push_free(remote->push);
1884
	git__free(remote->url);
1885
	git__free(remote->pushurl);
1886 1887
	git__free(remote->name);
	git__free(remote);
Carlos Martín Nieto committed
1888
}
1889

1890
static int remote_list_cb(const git_config_entry *entry, void *payload)
1891
{
1892
	git_vector *list = payload;
1893 1894 1895
	const char *name = entry->name + strlen("remote.");
	size_t namelen = strlen(name);
	char *remote_name;
1896

1897
	/* we know name matches "remote.<stuff>.(push)?url" */
1898

1899 1900 1901 1902
	if (!strcmp(&name[namelen - 4], ".url"))
		remote_name = git__strndup(name, namelen - 4); /* strip ".url" */
	else
		remote_name = git__strndup(name, namelen - 8); /* strip ".pushurl" */
1903
	GIT_ERROR_CHECK_ALLOC(remote_name);
1904

1905
	return git_vector_insert(list, remote_name);
1906 1907 1908 1909 1910
}

int git_remote_list(git_strarray *remotes_list, git_repository *repo)
{
	int error;
1911
	git_config *cfg;
1912
	git_vector list = GIT_VECTOR_INIT;
1913

1914 1915
	if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
		return error;
1916

1917
	if ((error = git_vector_init(&list, 4, git__strcmp_cb)) < 0)
1918
		return error;
1919

1920
	error = git_config_foreach_match(
1921
		cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list);
1922

1923
	if (error < 0) {
1924
		git_vector_free_deep(&list);
1925 1926 1927
		return error;
	}

1928
	git_vector_uniq(&list, git__free);
1929

1930 1931
	remotes_list->strings =
		(char **)git_vector_detach(&remotes_list->count, NULL, &list);
1932

1933
	return 0;
1934
}
1935

1936
const git_indexer_progress *git_remote_stats(git_remote *remote)
1937
{
1938
	GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
1939 1940 1941
	return &remote->stats;
}

1942
git_remote_autotag_option_t git_remote_autotag(const git_remote *remote)
1943 1944 1945 1946
{
	return remote->download_tags;
}

1947
int git_remote_set_autotag(git_repository *repo, const char *remote, git_remote_autotag_option_t value)
1948
{
1949
	git_str var = GIT_STR_INIT;
1950 1951 1952
	git_config *config;
	int error;

1953
	GIT_ASSERT_ARG(repo && remote);
1954 1955 1956 1957 1958 1959 1960

	if ((error = ensure_remote_name_is_valid(remote)) < 0)
		return error;

	if ((error = git_repository_config__weakptr(&config, repo)) < 0)
		return error;

1961
	if ((error = git_str_printf(&var, CONFIG_TAGOPT_FMT, remote)))
1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976
		return error;

	switch (value) {
	case GIT_REMOTE_DOWNLOAD_TAGS_NONE:
		error = git_config_set_string(config, var.ptr, "--no-tags");
		break;
	case GIT_REMOTE_DOWNLOAD_TAGS_ALL:
		error = git_config_set_string(config, var.ptr, "--tags");
		break;
	case GIT_REMOTE_DOWNLOAD_TAGS_AUTO:
		error = git_config_delete_entry(config, var.ptr);
		if (error == GIT_ENOTFOUND)
			error = 0;
		break;
	default:
1977
		git_error_set(GIT_ERROR_INVALID, "invalid value for the tagopt setting");
1978 1979 1980
		error = -1;
	}

1981
	git_str_dispose(&var);
1982
	return error;
1983
}
1984

1985 1986 1987 1988 1989
int git_remote_prune_refs(const git_remote *remote)
{
	return remote->prune_refs;
}

1990 1991 1992 1993 1994
static int rename_remote_config_section(
	git_repository *repo,
	const char *old_name,
	const char *new_name)
{
1995 1996
	git_str old_section_name = GIT_STR_INIT,
		new_section_name = GIT_STR_INIT;
1997 1998
	int error = -1;

1999
	if (git_str_printf(&old_section_name, "remote.%s", old_name) < 0)
2000 2001
		goto cleanup;

2002
	if (new_name &&
2003
		(git_str_printf(&new_section_name, "remote.%s", new_name) < 0))
2004
			goto cleanup;
2005 2006 2007

	error = git_config_rename_section(
		repo,
2008 2009
		git_str_cstr(&old_section_name),
		new_name ? git_str_cstr(&new_section_name) : NULL);
2010 2011

cleanup:
2012 2013
	git_str_dispose(&old_section_name);
	git_str_dispose(&new_section_name);
2014 2015 2016 2017

	return error;
}

2018
struct update_data {
2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032
	git_config *config;
	const char *old_remote_name;
	const char *new_remote_name;
};

static int update_config_entries_cb(
	const git_config_entry *entry,
	void *payload)
{
	struct update_data *data = (struct update_data *)payload;

	if (strcmp(entry->value, data->old_remote_name))
		return 0;

2033 2034
	return git_config_set_string(
		data->config, entry->name, data->new_remote_name);
2035 2036 2037 2038 2039 2040 2041
}

static int update_branch_remote_config_entry(
	git_repository *repo,
	const char *old_name,
	const char *new_name)
{
2042 2043
	int error;
	struct update_data data = { NULL };
2044

2045 2046
	if ((error = git_repository_config__weakptr(&data.config, repo)) < 0)
		return error;
2047 2048 2049 2050

	data.old_remote_name = old_name;
	data.new_remote_name = new_name;

2051
	return git_config_foreach_match(
2052
		data.config, "branch\\..+\\.remote", update_config_entries_cb, &data);
2053 2054 2055
}

static int rename_one_remote_reference(
2056
	git_reference *reference_in,
2057 2058 2059
	const char *old_remote_name,
	const char *new_remote_name)
{
2060
	int error;
2061
	git_reference *ref = NULL, *dummy = NULL;
2062 2063 2064
	git_str namespace = GIT_STR_INIT, old_namespace = GIT_STR_INIT;
	git_str new_name = GIT_STR_INIT;
	git_str log_message = GIT_STR_INIT;
2065 2066
	size_t pfx_len;
	const char *target;
2067

2068
	if ((error = git_str_printf(&namespace, GIT_REFS_REMOTES_DIR "%s/", new_remote_name)) < 0)
2069 2070 2071
		return error;

	pfx_len = strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name) + 1;
2072 2073
	git_str_puts(&new_name, namespace.ptr);
	if ((error = git_str_puts(&new_name, git_reference_name(reference_in) + pfx_len)) < 0)
2074
		goto cleanup;
2075

2076
	if ((error = git_str_printf(&log_message,
2077 2078 2079
					"renamed remote %s to %s",
					old_remote_name, new_remote_name)) < 0)
		goto cleanup;
2080

2081 2082
	if ((error = git_reference_rename(&ref, reference_in, git_str_cstr(&new_name), 1,
					  git_str_cstr(&log_message))) < 0)
2083 2084
		goto cleanup;

2085
	if (git_reference_type(ref) != GIT_REFERENCE_SYMBOLIC)
2086 2087 2088 2089
		goto cleanup;

	/* Handle refs like origin/HEAD -> origin/master */
	target = git_reference_symbolic_target(ref);
2090
	if ((error = git_str_printf(&old_namespace, GIT_REFS_REMOTES_DIR "%s/", old_remote_name)) < 0)
2091 2092 2093 2094 2095
		goto cleanup;

	if (git__prefixcmp(target, old_namespace.ptr))
		goto cleanup;

2096 2097 2098
	git_str_clear(&new_name);
	git_str_puts(&new_name, namespace.ptr);
	if ((error = git_str_puts(&new_name, target + pfx_len)) < 0)
2099 2100
		goto cleanup;

2101 2102
	error = git_reference_symbolic_set_target(&dummy, ref, git_str_cstr(&new_name),
						  git_str_cstr(&log_message));
2103 2104

	git_reference_free(dummy);
2105 2106

cleanup:
2107 2108
	git_reference_free(reference_in);
	git_reference_free(ref);
2109 2110 2111 2112
	git_str_dispose(&namespace);
	git_str_dispose(&old_namespace);
	git_str_dispose(&new_name);
	git_str_dispose(&log_message);
2113 2114 2115 2116 2117 2118 2119 2120
	return error;
}

static int rename_remote_references(
	git_repository *repo,
	const char *old_name,
	const char *new_name)
{
2121
	int error;
2122
	git_str buf = GIT_STR_INIT;
Vicent Marti committed
2123
	git_reference *ref;
2124
	git_reference_iterator *iter;
2125

2126
	if ((error = git_str_printf(&buf, GIT_REFS_REMOTES_DIR "%s/*", old_name)) < 0)
2127
		return error;
2128

2129 2130
	error = git_reference_iterator_glob_new(&iter, repo, git_str_cstr(&buf));
	git_str_dispose(&buf);
2131

2132 2133 2134 2135
	if (error < 0)
		return error;

	while ((error = git_reference_next(&ref, iter)) == 0) {
2136 2137
		if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0)
			break;
2138 2139 2140
	}

	git_reference_iterator_free(iter);
2141

2142
	return (error == GIT_ITEROVER) ? 0 : error;
2143 2144
}

2145
static int rename_fetch_refspecs(git_vector *problems, git_remote *remote, const char *new_name)
2146 2147
{
	git_config *config;
2148
	git_str base = GIT_STR_INIT, var = GIT_STR_INIT, val = GIT_STR_INIT;
2149
	const git_refspec *spec;
2150
	size_t i;
2151
	int error = 0;
2152

2153
	if ((error = git_repository_config__weakptr(&config, remote->repo)) < 0)
2154 2155
		return error;

2156 2157 2158
	if ((error = git_vector_init(problems, 1, NULL)) < 0)
		return error;

2159
	if ((error = default_fetchspec_for_name(&base, remote->name)) < 0)
2160
		return error;
2161

2162
	git_vector_foreach(&remote->refspecs, i, spec) {
2163 2164
		if (spec->push)
			continue;
2165

2166
		/* Does the dst part of the refspec follow the expected format? */
2167
		if (strcmp(git_str_cstr(&base), spec->string)) {
2168
			char *dup;
2169

2170
			dup = git__strdup(spec->string);
2171
			GIT_ERROR_CHECK_ALLOC(dup);
2172 2173

			if ((error = git_vector_insert(problems, dup)) < 0)
2174
				break;
2175

2176 2177
			continue;
		}
2178

2179
		/* If we do want to move it to the new section */
2180

2181 2182
		git_str_clear(&val);
		git_str_clear(&var);
2183

2184
		if (default_fetchspec_for_name(&val, new_name) < 0 ||
2185
			git_str_printf(&var, "remote.%s.fetch", new_name) < 0)
2186 2187
		{
			error = -1;
2188
			break;
2189
		}
2190

2191
		if ((error = git_config_set_string(
2192
				config, git_str_cstr(&var), git_str_cstr(&val))) < 0)
2193
			break;
2194
	}
2195

2196 2197 2198
	git_str_dispose(&base);
	git_str_dispose(&var);
	git_str_dispose(&val);
2199 2200 2201 2202 2203 2204 2205 2206 2207

	if (error < 0) {
		char *str;
		git_vector_foreach(problems, i, str)
			git__free(str);

		git_vector_free(problems);
	}

2208 2209 2210
	return error;
}

2211
int git_remote_rename(git_strarray *out, git_repository *repo, const char *name, const char *new_name)
2212 2213
{
	int error;
2214
	git_vector problem_refspecs = GIT_VECTOR_INIT;
2215
	git_remote *remote = NULL;
2216

2217
	GIT_ASSERT_ARG(out && repo && name && new_name);
2218

2219
	if ((error = git_remote_lookup(&remote, repo, name)) < 0)
2220
		return error;
2221

2222
	if ((error = ensure_remote_name_is_valid(new_name)) < 0)
2223
		goto cleanup;
2224

2225 2226
	if ((error = ensure_remote_doesnot_exist(repo, new_name)) < 0)
		goto cleanup;
2227

2228 2229
	if ((error = rename_remote_config_section(repo, name, new_name)) < 0)
		goto cleanup;
2230

2231 2232
	if ((error = update_branch_remote_config_entry(repo, name, new_name)) < 0)
		goto cleanup;
2233

2234 2235
	if ((error = rename_remote_references(repo, name, new_name)) < 0)
		goto cleanup;
2236

2237
	if ((error = rename_fetch_refspecs(&problem_refspecs, remote, new_name)) < 0)
2238
		goto cleanup;
2239

2240 2241 2242
	out->count = problem_refspecs.length;
	out->strings = (char **) problem_refspecs.contents;

2243 2244 2245
cleanup:
	if (error < 0)
		git_vector_free(&problem_refspecs);
2246

2247 2248
	git_remote_free(remote);
	return error;
2249
}
2250

2251
int git_remote_name_is_valid(int *valid, const char *remote_name)
2252
{
2253
	git_str buf = GIT_STR_INIT;
2254 2255 2256 2257 2258 2259
	git_refspec refspec = {0};
	int error;

	GIT_ASSERT(valid);

	*valid = 0;
2260 2261 2262 2263

	if (!remote_name || *remote_name == '\0')
		return 0;

2264
	if ((error = git_str_printf(&buf, "refs/heads/test:refs/remotes/%s/test", remote_name)) < 0)
2265 2266
		goto done;

2267
	error = git_refspec__parse(&refspec, git_str_cstr(&buf), true);
2268

2269 2270 2271 2272 2273 2274
	if (!error)
		*valid = 1;
	else if (error == GIT_EINVALIDSPEC)
		error = 0;

done:
2275
	git_str_dispose(&buf);
2276
	git_refspec__dispose(&refspec);
2277

2278 2279 2280
	return error;
}

2281 2282 2283 2284 2285
git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname)
{
	git_refspec *spec;
	size_t i;

2286
	git_vector_foreach(&remote->active_refspecs, i, spec) {
2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301
		if (spec->push)
			continue;

		if (git_refspec_src_matches(spec, refname))
			return spec;
	}

	return NULL;
}

git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname)
{
	git_refspec *spec;
	size_t i;

2302
	git_vector_foreach(&remote->active_refspecs, i, spec) {
2303 2304 2305 2306 2307 2308 2309 2310 2311 2312
		if (spec->push)
			continue;

		if (git_refspec_dst_matches(spec, refname))
			return spec;
	}

	return NULL;
}

2313
int git_remote_add_fetch(git_repository *repo, const char *remote, const char *refspec)
2314
{
2315
	return write_add_refspec(repo, remote, refspec, true);
2316 2317
}

2318
int git_remote_add_push(git_repository *repo, const char *remote, const char *refspec)
2319
{
2320
	return write_add_refspec(repo, remote, refspec, false);
2321 2322
}

2323
static int copy_refspecs(git_strarray *array, const git_remote *remote, unsigned int push)
2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336
{
	size_t i;
	git_vector refspecs;
	git_refspec *spec;
	char *dup;

	if (git_vector_init(&refspecs, remote->refspecs.length, NULL) < 0)
		return -1;

	git_vector_foreach(&remote->refspecs, i, spec) {
		if (spec->push != push)
			continue;

2337
		if ((dup = git__strdup(spec->string)) == NULL)
2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351
			goto on_error;

		if (git_vector_insert(&refspecs, dup) < 0) {
			git__free(dup);
			goto on_error;
		}
	}

	array->strings = (char **)refspecs.contents;
	array->count = refspecs.length;

	return 0;

on_error:
2352
	git_vector_free_deep(&refspecs);
2353 2354 2355 2356

	return -1;
}

2357
int git_remote_get_fetch_refspecs(git_strarray *array, const git_remote *remote)
2358 2359 2360 2361
{
	return copy_refspecs(array, remote, false);
}

2362
int git_remote_get_push_refspecs(git_strarray *array, const git_remote *remote)
2363 2364 2365
{
	return copy_refspecs(array, remote, true);
}
2366

2367
size_t git_remote_refspec_count(const git_remote *remote)
2368 2369 2370 2371
{
	return remote->refspecs.length;
}

2372
const git_refspec *git_remote_get_refspec(const git_remote *remote, size_t n)
2373 2374 2375
{
	return git_vector_get(&remote->refspecs, n);
}
2376

2377
int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version)
2378
{
2379 2380 2381
	GIT_INIT_STRUCTURE_FROM_TEMPLATE(
		opts, version, git_remote_callbacks, GIT_REMOTE_CALLBACKS_INIT);
	return 0;
2382
}
2383

2384 2385
/* asserts a branch.<foo>.remote format */
static const char *name_offset(size_t *len_out, const char *name)
2386
{
2387 2388
	size_t prefix_len;
	const char *dot;
2389

2390 2391
	prefix_len = strlen("remote.");
	dot = strchr(name + prefix_len, '.');
2392

2393
	GIT_ASSERT_ARG_WITH_RETVAL(dot, NULL);
2394

2395 2396
	*len_out = dot - name - prefix_len;
	return name + prefix_len;
2397 2398 2399 2400 2401 2402 2403 2404
}

static int remove_branch_config_related_entries(
	git_repository *repo,
	const char *remote_name)
{
	int error;
	git_config *config;
2405 2406
	git_config_entry *entry;
	git_config_iterator *iter;
2407
	git_str buf = GIT_STR_INIT;
2408 2409 2410 2411

	if ((error = git_repository_config__weakptr(&config, repo)) < 0)
		return error;

2412
	if ((error = git_config_iterator_glob_new(&iter, config, "branch\\..+\\.remote")) < 0)
2413 2414
		return error;

2415 2416 2417 2418
	/* find any branches with us as upstream and remove that config */
	while ((error = git_config_next(&entry, iter)) == 0) {
		const char *branch;
		size_t branch_len;
2419

2420 2421
		if (strcmp(remote_name, entry->value))
			continue;
2422

2423 2424 2425 2426
		if ((branch = name_offset(&branch_len, entry->name)) == NULL) {
			error = -1;
			break;
		}
2427

2428 2429
		git_str_clear(&buf);
		if ((error = git_str_printf(&buf, "branch.%.*s.merge", (int)branch_len, branch)) < 0)
2430 2431
			break;

2432
		if ((error = git_config_delete_entry(config, git_str_cstr(&buf))) < 0) {
2433 2434
			if (error != GIT_ENOTFOUND)
				break;
2435
			git_error_clear();
2436
		}
2437

2438 2439
		git_str_clear(&buf);
		if ((error = git_str_printf(&buf, "branch.%.*s.remote", (int)branch_len, branch)) < 0)
2440 2441
			break;

2442
		if ((error = git_config_delete_entry(config, git_str_cstr(&buf))) < 0) {
2443 2444
			if (error != GIT_ENOTFOUND)
				break;
2445
			git_error_clear();
2446
		}
2447 2448
	}

2449 2450 2451
	if (error == GIT_ITEROVER)
		error = 0;

2452
	git_str_dispose(&buf);
2453
	git_config_iterator_free(iter);
2454 2455 2456
	return error;
}

2457
static int remove_refs(git_repository *repo, const git_refspec *spec)
2458
{
2459 2460
	git_reference_iterator *iter = NULL;
	git_vector refs;
2461
	const char *name;
2462
	char *dup;
2463
	int error;
2464
	size_t i;
2465

2466
	if ((error = git_vector_init(&refs, 8, NULL)) < 0)
2467 2468
		return error;

2469 2470 2471
	if ((error = git_reference_iterator_new(&iter, repo)) < 0)
		goto cleanup;

2472
	while ((error = git_reference_next_name(&name, iter)) == 0) {
2473 2474 2475 2476 2477 2478 2479 2480
		if (!git_refspec_dst_matches(spec, name))
			continue;

		dup = git__strdup(name);
		if (!dup) {
			error = -1;
			goto cleanup;
		}
2481

2482 2483 2484
		if ((error = git_vector_insert(&refs, dup)) < 0)
			goto cleanup;
	}
2485 2486
	if (error == GIT_ITEROVER)
		error = 0;
2487 2488 2489 2490 2491 2492 2493
	if (error < 0)
		goto cleanup;

	git_vector_foreach(&refs, i, name) {
		if ((error = git_reference_remove(repo, name)) < 0)
			break;
	}
2494

2495 2496 2497 2498 2499 2500
cleanup:
	git_reference_iterator_free(iter);
	git_vector_foreach(&refs, i, dup) {
		git__free(dup);
	}
	git_vector_free(&refs);
2501 2502 2503 2504 2505 2506 2507 2508 2509 2510
	return error;
}

static int remove_remote_tracking(git_repository *repo, const char *remote_name)
{
	git_remote *remote;
	int error;
	size_t i, count;

	/* we want to use what's on the config, regardless of changes to the instance in memory */
2511
	if ((error = git_remote_lookup(&remote, repo, remote_name)) < 0)
2512 2513 2514 2515 2516 2517 2518 2519 2520 2521
		return error;

	count = git_remote_refspec_count(remote);
	for (i = 0; i < count; i++) {
		const git_refspec *refspec = git_remote_get_refspec(remote, i);

		/* shouldn't ever actually happen */
		if (refspec == NULL)
			continue;

2522
		if ((error = remove_refs(repo, refspec)) < 0)
2523 2524 2525 2526 2527 2528 2529
			break;
	}

	git_remote_free(remote);
	return error;
}

2530
int git_remote_delete(git_repository *repo, const char *name)
2531 2532 2533
{
	int error;

2534 2535
	GIT_ASSERT_ARG(repo);
	GIT_ASSERT_ARG(name);
2536

2537 2538 2539
	if ((error = remove_branch_config_related_entries(repo, name)) < 0 ||
	    (error = remove_remote_tracking(repo, name)) < 0 ||
	    (error = rename_remote_config_section(repo, name, NULL)) < 0)
2540 2541
		return error;

2542 2543
	return 0;
}
2544 2545 2546

int git_remote_default_branch(git_buf *out, git_remote *remote)
{
2547 2548 2549 2550 2551
	GIT_BUF_WRAP_PRIVATE(out, git_remote__default_branch, remote);
}

int git_remote__default_branch(git_str *out, git_remote *remote)
{
2552 2553 2554 2555
	const git_remote_head **heads;
	const git_remote_head *guess = NULL;
	const git_oid *head_id;
	size_t heads_len, i;
2556
	git_str local_default = GIT_STR_INIT;
2557 2558
	int error;

2559
	GIT_ASSERT_ARG(out);
2560

2561
	if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0)
2562
		goto done;
2563

2564 2565 2566 2567
	if (heads_len == 0 || strcmp(heads[0]->name, GIT_HEAD_FILE)) {
		error = GIT_ENOTFOUND;
		goto done;
	}
2568

2569
	/* the first one must be HEAD so if that has the symref info, we're done */
2570
	if (heads[0]->symref_target) {
2571
		error = git_str_puts(out, heads[0]->symref_target);
2572 2573
		goto done;
	}
2574 2575 2576

	/*
	 * If there's no symref information, we have to look over them
2577 2578
	 * and guess. We return the first match unless the default
	 * branch is a candidate. Then we return the default branch.
2579
	 */
2580 2581 2582 2583

	if ((error = git_repository_initialbranch(&local_default, remote->repo)) < 0)
		goto done;

2584 2585 2586 2587 2588 2589
	head_id = &heads[0]->oid;

	for (i = 1; i < heads_len; i++) {
		if (git_oid_cmp(head_id, &heads[i]->oid))
			continue;

2590 2591 2592
		if (git__prefixcmp(heads[i]->name, GIT_REFS_HEADS_DIR))
			continue;

2593 2594 2595 2596 2597
		if (!guess) {
			guess = heads[i];
			continue;
		}

2598
		if (!git__strcmp(local_default.ptr, heads[i]->name)) {
2599 2600 2601 2602 2603
			guess = heads[i];
			break;
		}
	}

2604 2605 2606 2607 2608
	if (!guess) {
		error = GIT_ENOTFOUND;
		goto done;
	}

2609
	error = git_str_puts(out, guess->name);
2610

2611
done:
2612
	git_str_dispose(&local_default);
2613
	return error;
2614
}
2615

2616
int git_remote_upload(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts)
2617 2618
{
	size_t i;
2619 2620
	int error;
	git_push *push;
2621
	git_refspec *spec;
2622
	const git_remote_callbacks *cbs = NULL;
2623
	git_remote_connection_opts conn = GIT_REMOTE_CONNECTION_OPTIONS_INIT;
2624

2625
	GIT_ASSERT_ARG(remote);
2626

2627
	if (!remote->repo) {
2628
		git_error_set(GIT_ERROR_INVALID, "cannot download detached remote");
2629 2630 2631
		return -1;
	}

2632
	if (opts) {
2633
		cbs = &opts->callbacks;
2634 2635
		conn.custom_headers = &opts->custom_headers;
		conn.proxy = &opts->proxy_opts;
2636
	}
2637

2638
	if (!git_remote_connected(remote) &&
2639
	    (error = git_remote__connect(remote, GIT_DIRECTION_PUSH, cbs, &conn)) < 0)
2640 2641
		goto cleanup;

2642
	free_refspecs(&remote->active_refspecs);
Leo Yang committed
2643
	if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
2644 2645
		goto cleanup;

2646 2647 2648 2649 2650 2651
	if (remote->push) {
		git_push_free(remote->push);
		remote->push = NULL;
	}

	if ((error = git_push_new(&remote->push, remote)) < 0)
2652 2653
		return error;

2654
	push = remote->push;
2655 2656 2657 2658

	if (opts && (error = git_push_set_options(push, opts)) < 0)
		goto cleanup;

2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670
	if (refspecs && refspecs->count > 0) {
		for (i = 0; i < refspecs->count; i++) {
			if ((error = git_push_add_refspec(push, refspecs->strings[i])) < 0)
				goto cleanup;
		}
	} else {
		git_vector_foreach(&remote->refspecs, i, spec) {
			if (!spec->push)
				continue;
			if ((error = git_push_add_refspec(push, spec->string)) < 0)
				goto cleanup;
		}
2671 2672
	}

2673
	if ((error = git_push_finish(push, cbs)) < 0)
2674 2675
		goto cleanup;

2676
	if (cbs && cbs->push_update_reference &&
2677
	    (error = git_push_status_foreach(push, cbs->push_update_reference, cbs->payload)) < 0)
2678 2679 2680
		goto cleanup;

cleanup:
2681 2682 2683
	return error;
}

2684
int git_remote_push(git_remote *remote, const git_strarray *refspecs, const git_push_options *opts)
2685 2686
{
	int error;
2687
	const git_remote_callbacks *cbs = NULL;
2688
	const git_strarray *custom_headers = NULL;
2689
	const git_proxy_options *proxy = NULL;
2690

2691
	GIT_ASSERT_ARG(remote);
2692 2693

	if (!remote->repo) {
2694
		git_error_set(GIT_ERROR_INVALID, "cannot download detached remote");
2695 2696 2697
		return -1;
	}

2698
	if (opts) {
2699
		GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
2700
		cbs = &opts->callbacks;
2701
		custom_headers = &opts->custom_headers;
2702
		GIT_ERROR_CHECK_VERSION(&opts->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
2703
		proxy = &opts->proxy_opts;
2704
	}
2705

2706
	if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH, cbs, proxy, custom_headers)) < 0)
2707 2708 2709 2710 2711
		return error;

	if ((error = git_remote_upload(remote, refspecs, opts)) < 0)
		return error;

2712
	error = git_remote_update_tips(remote, cbs, 0, 0, NULL);
2713

2714 2715 2716
	git_remote_disconnect(remote);
	return error;
}
2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727

#define PREFIX "url"
#define SUFFIX_FETCH "insteadof"
#define SUFFIX_PUSH "pushinsteadof"

char *apply_insteadof(git_config *config, const char *url, int direction)
{
	size_t match_length, prefix_length, suffix_length;
	char *replacement = NULL;
	const char *regexp;

2728
	git_str result = GIT_STR_INIT;
2729 2730 2731
	git_config_entry *entry;
	git_config_iterator *iter;

2732 2733 2734
	GIT_ASSERT_ARG_WITH_RETVAL(config, NULL);
	GIT_ASSERT_ARG_WITH_RETVAL(url, NULL);
	GIT_ASSERT_ARG_WITH_RETVAL(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH, NULL);
2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745

	/* Add 1 to prefix/suffix length due to the additional escaped dot */
	prefix_length = strlen(PREFIX) + 1;
	if (direction == GIT_DIRECTION_FETCH) {
		regexp = PREFIX "\\..*\\." SUFFIX_FETCH;
		suffix_length = strlen(SUFFIX_FETCH) + 1;
	} else {
		regexp = PREFIX "\\..*\\." SUFFIX_PUSH;
		suffix_length = strlen(SUFFIX_PUSH) + 1;
	}

2746 2747
	if (git_config_iterator_glob_new(&iter, config, regexp) < 0)
		return NULL;
2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775

	match_length = 0;
	while (git_config_next(&entry, iter) == 0) {
		size_t n, replacement_length;

		/* Check if entry value is a prefix of URL */
		if (git__prefixcmp(url, entry->value))
			continue;
		/* Check if entry value is longer than previous
		 * prefixes */
		if ((n = strlen(entry->value)) <= match_length)
			continue;

		git__free(replacement);
		match_length = n;

		/* Cut off prefix and suffix of the value */
		replacement_length =
		    strlen(entry->name) - (prefix_length + suffix_length);
		replacement = git__strndup(entry->name + prefix_length,
				replacement_length);
	}

	git_config_iterator_free(iter);

	if (match_length == 0)
		return git__strdup(url);

2776
	git_str_printf(&result, "%s%s", replacement, url + match_length);
2777 2778 2779 2780 2781

	git__free(replacement);

	return result.ptr;
}
2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795

/* Deprecated functions */

#ifndef GIT_DEPRECATE_HARD

int git_remote_is_valid_name(const char *remote_name)
{
	int valid = 0;

	git_remote_name_is_valid(&valid, remote_name);
	return valid;
}

#endif