remote.c 35.9 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 "git2/config.h"
#include "git2/types.h"
10
#include "git2/oid.h"
11
#include "git2/net.h"
Carlos Martín Nieto committed
12

13
#include "common.h"
Carlos Martín Nieto committed
14 15 16
#include "config.h"
#include "repository.h"
#include "remote.h"
17
#include "fetch.h"
18
#include "refs.h"
19 20
#include "refspec.h"
#include "fetchhead.h"
Carlos Martín Nieto committed
21

22 23
static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs);

24
static int add_refspec(git_remote *remote, const char *string, bool is_fetch)
Carlos Martín Nieto committed
25
{
26
	git_refspec *spec;
Carlos Martín Nieto committed
27

28 29 30
	spec = git__calloc(1, sizeof(git_refspec));
	GITERR_CHECK_ALLOC(spec);

31 32 33 34
	if (git_refspec__parse(spec, string, is_fetch) < 0) {
		git__free(spec);
		return -1;
	}
35 36

	spec->push = !is_fetch;
37 38 39 40 41
	if (git_vector_insert(&remote->refspecs, spec) < 0) {
		git_refspec__free(spec);
		git__free(spec);
		return -1;
	}
Carlos Martín Nieto committed
42

43
	return 0;
Carlos Martín Nieto committed
44 45
}

46 47
static int download_tags_value(git_remote *remote, git_config *cfg)
{
48
	const git_config_entry *ce;
49 50 51
	git_buf buf = GIT_BUF_INIT;
	int error;

52
	/* The 0 value is the default (auto), let's see if we need to change it */
53 54 55
	if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0)
		return -1;

56
	error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false);
57 58
	git_buf_free(&buf);

59 60 61 62 63
	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;
64
	}
65 66 67 68

	return error;
}

69 70
static int ensure_remote_name_is_valid(const char *name)
{
71
	int error = 0;
72

73
	if (!git_remote_is_valid_name(name)) {
74 75 76 77 78 79 80 81 82
		giterr_set(
			GITERR_CONFIG,
			"'%s' is not a valid remote name.", name);
		error = GIT_EINVALIDSPEC;
	}

	return error;
}

83
static int get_check_cert(int *out, git_repository *repo)
84 85 86
{
	git_config *cfg;
	const char *val;
87 88 89
	int error = 0;

	assert(out && repo);
90

91 92
	/* By default, we *DO* want to verify the certificate. */
	*out = 1;
93 94 95 96 97

	/* Go through the possible sources for SSL verification settings, from
	 * most specific to least specific. */

	/* GIT_SSL_NO_VERIFY environment variable */
Russell Belfer committed
98
	if ((val = getenv("GIT_SSL_NO_VERIFY")) != NULL)
99
		return git_config_parse_bool(out, val);
100 101

	/* http.sslVerify config setting */
102 103
	if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
		return error;
104

105
	*out = git_config__get_bool_force(cfg, "http.sslverify", 1);
106
	return 0;
107 108
}

109
static int create_internal(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
110 111
{
	git_remote *remote;
112 113
	git_buf fetchbuf = GIT_BUF_INIT;
	int error = -1;
114

115
	/* name is optional */
116
	assert(out && repo && url);
117

118
	remote = git__calloc(1, sizeof(git_remote));
119
	GITERR_CHECK_ALLOC(remote);
120 121

	remote->repo = repo;
122
	remote->update_fetchhead = 1;
123

124 125 126
	if (get_check_cert(&remote->check_cert, repo) < 0)
		goto on_error;

127
	if (git_vector_init(&remote->refs, 32, NULL) < 0)
128
		goto on_error;
129

130
	remote->url = git__strdup(url);
131
	GITERR_CHECK_ALLOC(remote->url);
132

133 134
	if (name != NULL) {
		remote->name = git__strdup(name);
135
		GITERR_CHECK_ALLOC(remote->name);
136 137
	}

138
	if (fetch != NULL) {
139
		if (add_refspec(remote, fetch, true) < 0)
140 141 142
			goto on_error;
	}

143 144
	if (!name)
		/* A remote without a name doesn't download tags */
145 146
		remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;

147
	*out = remote;
148
	git_buf_free(&fetchbuf);
149
	return 0;
150 151 152

on_error:
	git_remote_free(remote);
153 154
	git_buf_free(&fetchbuf);
	return error;
155 156
}

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
static int ensure_remote_doesnot_exist(git_repository *repo, const char *name)
{
	int error;
	git_remote *remote;

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

	if (error == GIT_ENOTFOUND)
		return 0;

	if (error < 0)
		return error;

	git_remote_free(remote);

	giterr_set(
		GITERR_CONFIG,
		"Remote '%s' already exists.", name);

	return GIT_EEXISTS;
}

179

180 181 182
int git_remote_create(git_remote **out, git_repository *repo, const char *name, const char *url)
{
	git_buf buf = GIT_BUF_INIT;
183
	git_remote *remote = NULL;
184 185 186 187 188
	int error;

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

189 190 191
	if ((error = ensure_remote_doesnot_exist(repo, name)) < 0)
		return error;

192 193 194
	if (git_buf_printf(&buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0)
		return -1;

195
	if (create_internal(&remote, repo, name, url, git_buf_cstr(&buf)) < 0)
196 197 198 199
		goto on_error;

	git_buf_free(&buf);

200
	if (git_remote_save(remote) < 0)
201 202
		goto on_error;

203 204
	*out = remote;

205 206 207 208
	return 0;

on_error:
	git_buf_free(&buf);
209
	git_remote_free(remote);
210 211 212
	return -1;
}

213 214 215 216 217 218 219 220 221 222
int git_remote_create_with_fetchspec(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
{
	git_remote *remote = NULL;
	int error;

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

	if ((error = ensure_remote_doesnot_exist(repo, name)) < 0)
		return error;
223

224
	if (create_internal(&remote, repo, name, url, fetch) < 0)
225 226
		goto on_error;

227
	if (git_remote_save(remote) < 0)
228 229
		goto on_error;

230 231
	*out = remote;

232 233 234
	return 0;

on_error:
235
	git_remote_free(remote);
236 237 238
	return -1;
}

239
int git_remote_create_inmemory(git_remote **out, git_repository *repo, const char *fetch, const char *url)
240 241 242 243
{
	int error;
	git_remote *remote;

244
	if ((error = create_internal(&remote, repo, NULL, url, fetch)) < 0)
245 246 247 248 249 250
		return error;

	*out = remote;
	return 0;
}

251 252 253 254 255 256 257
struct refspec_cb_data {
	git_remote *remote;
	int fetch;
};

static int refspec_cb(const git_config_entry *entry, void *payload)
{
258
	struct refspec_cb_data *data = (struct refspec_cb_data *)payload;
259
	return add_refspec(data->remote, entry->value, data->fetch);
260 261
}

262
static int get_optional_config(
263 264
	bool *found, git_config *config, git_buf *buf,
	git_config_foreach_cb cb, void *payload)
265 266 267 268 269 270 271 272
{
	int error = 0;
	const char *key = git_buf_cstr(buf);

	if (git_buf_oom(buf))
		return -1;

	if (cb != NULL)
273
		error = git_config_get_multivar_foreach(config, key, NULL, cb, payload);
274 275 276
	else
		error = git_config_get_string(payload, config, key);

277 278 279
	if (found)
		*found = !error;

280 281 282 283 284 285 286 287
	if (error == GIT_ENOTFOUND) {
		giterr_clear();
		error = 0;
	}

	return error;
}

288
int git_remote_load(git_remote **out, git_repository *repo, const char *name)
Carlos Martín Nieto committed
289 290
{
	git_remote *remote;
291
	git_buf buf = GIT_BUF_INIT;
Carlos Martín Nieto committed
292
	const char *val;
293
	int error = 0;
294
	git_config *config;
295
	struct refspec_cb_data data = { NULL };
296
	bool optional_setting_found = false, found;
297

298 299
	assert(out && repo && name);

300 301 302
	if ((error = ensure_remote_name_is_valid(name)) < 0)
		return error;

303 304
	if (git_repository_config__weakptr(&config, repo) < 0)
		return -1;
305

Carlos Martín Nieto committed
306
	remote = git__malloc(sizeof(git_remote));
307
	GITERR_CHECK_ALLOC(remote);
Carlos Martín Nieto committed
308 309

	memset(remote, 0x0, sizeof(git_remote));
310
	remote->update_fetchhead = 1;
Carlos Martín Nieto committed
311
	remote->name = git__strdup(name);
312
	GITERR_CHECK_ALLOC(remote->name);
Carlos Martín Nieto committed
313

314 315 316
	if ((error = get_check_cert(&remote->check_cert, repo)) < 0)
		goto cleanup;

317 318 319
	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) {
320 321 322
		error = -1;
		goto cleanup;
	}
323

324
	if ((error = git_buf_printf(&buf, "remote.%s.url", name)) < 0)
325
		goto cleanup;
Carlos Martín Nieto committed
326

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

330 331
	optional_setting_found |= found;

332
	remote->repo = repo;
333 334 335 336 337

	if (found && strlen(val) > 0) {
		remote->url = git__strdup(val);
		GITERR_CHECK_ALLOC(remote->url);
	}
Carlos Martín Nieto committed
338

339
	val = NULL;
340
	git_buf_clear(&buf);
341
	git_buf_printf(&buf, "remote.%s.pushurl", name);
342

343
	if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
344 345
		goto cleanup;

346 347 348 349
	optional_setting_found |= found;

	if (!optional_setting_found) {
		error = GIT_ENOTFOUND;
350
		goto cleanup;
351
	}
352

353
	if (found && strlen(val) > 0) {
354 355 356 357
		remote->pushurl = git__strdup(val);
		GITERR_CHECK_ALLOC(remote->pushurl);
	}

358 359
	data.remote = remote;
	data.fetch = true;
360

361
	git_buf_clear(&buf);
362 363
	git_buf_printf(&buf, "remote.%s.fetch", name);

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

367
	data.fetch = false;
368 369
	git_buf_clear(&buf);
	git_buf_printf(&buf, "remote.%s.push", name);
Carlos Martín Nieto committed
370

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

374 375 376
	if (download_tags_value(remote, config) < 0)
		goto cleanup;

377 378 379 380
	/* Move the data over to where the matching functions can find them */
	if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0)
		goto cleanup;

Carlos Martín Nieto committed
381 382 383
	*out = remote;

cleanup:
384
	git_buf_free(&buf);
385

386
	if (error < 0)
Carlos Martín Nieto committed
387 388 389 390 391
		git_remote_free(remote);

	return error;
}

392
static int update_config_refspec(const git_remote *remote, git_config *config, int direction)
393
{
394
	git_buf name = GIT_BUF_INIT;
Linquize committed
395
	unsigned int push;
396 397
	const char *dir;
	size_t i;
398
	int error = 0;
399
	const char *cname;
400

401 402
	push = direction == GIT_DIRECTION_PUSH;
	dir = push ? "push" : "fetch";
403

404 405
	if (git_buf_printf(&name, "remote.%s.%s", remote->name, dir) < 0)
		return -1;
406
	cname = git_buf_cstr(&name);
407

408
	/* Clear out the existing config */
409
	while (!error)
410
		error = git_config_delete_multivar(config, cname, ".*");
411

412 413 414
	if (error != GIT_ENOTFOUND)
		return error;

415
	for (i = 0; i < remote->refspecs.length; i++) {
416 417 418 419 420
		git_refspec *spec = git_vector_get(&remote->refspecs, i);

		if (spec->push != push)
			continue;

421 422 423
		// "$^" is a unmatcheable regexp: it will not match anything at all, so
		// all values will be considered new and we will not replace any
		// present value.
424
		if ((error = git_config_set_multivar(
425
				config, cname, "$^", spec->string)) < 0) {
426 427 428 429 430 431
			goto cleanup;
		}
	}

	giterr_clear();
	error = 0;
432 433 434 435 436 437 438

cleanup:
	git_buf_free(&name);

	return error;
}

439 440
int git_remote_save(const git_remote *remote)
{
441
	int error;
442
	git_config *config;
443
	const char *tagopt = NULL;
444
	git_buf buf = GIT_BUF_INIT;
445

446 447
	assert(remote);

448
	if (!remote->name) {
449 450
		giterr_set(GITERR_INVALID, "Can't save an in-memory remote.");
		return GIT_EINVALIDSPEC;
451 452
	}

453 454
	if ((error = ensure_remote_name_is_valid(remote->name)) < 0)
		return error;
455

456 457
	if (git_repository_config__weakptr(&config, remote->repo) < 0)
		return -1;
458

459
	if (git_buf_printf(&buf, "remote.%s.url", remote->name) < 0)
460
		return -1;
461

462 463 464 465
	if (git_config_set_string(config, git_buf_cstr(&buf), remote->url) < 0) {
		git_buf_free(&buf);
		return -1;
	}
466

467 468 469
	git_buf_clear(&buf);
	if (git_buf_printf(&buf, "remote.%s.pushurl", remote->name) < 0)
		return -1;
470

471
	if (remote->pushurl) {
472 473 474 475
		if (git_config_set_string(config, git_buf_cstr(&buf), remote->pushurl) < 0) {
			git_buf_free(&buf);
			return -1;
		}
476
	} else {
Ben Straub committed
477
		int error = git_config_delete_entry(config, git_buf_cstr(&buf));
478 479
		if (error == GIT_ENOTFOUND) {
			error = 0;
480
			giterr_clear();
481 482 483
		}
		if (error < 0) {
			git_buf_free(&buf);
484
			return error;
485
		}
486 487
	}

488 489
	if (update_config_refspec(remote, config, GIT_DIRECTION_FETCH) < 0)
		goto on_error;
490

491 492
	if (update_config_refspec(remote, config, GIT_DIRECTION_PUSH) < 0)
		goto on_error;
493

494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521
	/*
	 * What action to take depends on the old and new values. This
	 * is describes by the table below. tagopt means whether the
	 * is already a value set in the config
	 *
	 *            AUTO     ALL or NONE
	 *         +-----------------------+
	 *  tagopt | remove  |     set     |
	 *         +---------+-------------|
	 * !tagopt | nothing |     set     |
	 *         +---------+-------------+
	 */

	git_buf_clear(&buf);
	if (git_buf_printf(&buf, "remote.%s.tagopt", remote->name) < 0)
		goto on_error;

	error = git_config_get_string(&tagopt, config, git_buf_cstr(&buf));
	if (error < 0 && error != GIT_ENOTFOUND)
		goto on_error;

	if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
		if (git_config_set_string(config, git_buf_cstr(&buf), "--tags") < 0)
			goto on_error;
	} else if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_NONE) {
		if (git_config_set_string(config, git_buf_cstr(&buf), "--no-tags") < 0)
			goto on_error;
	} else if (tagopt) {
Ben Straub committed
522
		if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
523 524 525
			goto on_error;
	}

526
	git_buf_free(&buf);
527 528 529 530 531 532

	return 0;

on_error:
	git_buf_free(&buf);
	return -1;
533 534
}

Ben Straub committed
535
const char *git_remote_name(const git_remote *remote)
Carlos Martín Nieto committed
536
{
537
	assert(remote);
Carlos Martín Nieto committed
538 539 540
	return remote->name;
}

Etienne Samson committed
541 542 543 544 545 546
git_repository *git_remote_owner(const git_remote *remote)
{
	assert(remote);
	return remote->repo;
}

Ben Straub committed
547
const char *git_remote_url(const git_remote *remote)
Carlos Martín Nieto committed
548
{
549
	assert(remote);
Carlos Martín Nieto committed
550 551 552
	return remote->url;
}

553 554 555 556 557 558 559 560 561 562 563 564
int git_remote_set_url(git_remote *remote, const char* url)
{
	assert(remote);
	assert(url);

	git__free(remote->url);
	remote->url = git__strdup(url);
	GITERR_CHECK_ALLOC(remote->url);

	return 0;
}

Ben Straub committed
565
const char *git_remote_pushurl(const git_remote *remote)
566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584
{
	assert(remote);
	return remote->pushurl;
}

int git_remote_set_pushurl(git_remote *remote, const char* url)
{
	assert(remote);

	git__free(remote->pushurl);
	if (url) {
		remote->pushurl = git__strdup(url);
		GITERR_CHECK_ALLOC(remote->pushurl);
	} else {
		remote->pushurl = NULL;
	}
	return 0;
}

585 586 587 588
const char* git_remote__urlfordirection(git_remote *remote, int direction)
{
	assert(remote);

589 590
	assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);

Ben Straub committed
591
	if (direction == GIT_DIRECTION_FETCH) {
592 593 594
		return remote->url;
	}

Ben Straub committed
595
	if (direction == GIT_DIRECTION_PUSH) {
596 597 598 599 600 601
		return remote->pushurl ? remote->pushurl : remote->url;
	}

	return NULL;
}

Ben Straub committed
602
int git_remote_connect(git_remote *remote, git_direction direction)
603 604
{
	git_transport *t;
605
	const char *url;
606
	int flags = GIT_TRANSPORTFLAGS_NONE;
607
	int error;
608

609 610
	assert(remote);

611 612
	t = remote->transport;

613
	url = git_remote__urlfordirection(remote, direction);
614
	if (url == NULL) {
615 616
		giterr_set(GITERR_INVALID,
			"Malformed remote '%s' - missing URL", remote->name);
617
		return -1;
618
	}
619

620 621
	/* A transport could have been supplied in advance with
	 * git_remote_set_transport */
622 623
	if (!t && (error = git_transport_new(&t, remote, url)) < 0)
		return error;
624

625
	if (t->set_callbacks &&
626
		(error = t->set_callbacks(t, remote->callbacks.progress, NULL, remote->callbacks.payload)) < 0)
627
		goto on_error;
628

629 630
	if (!remote->check_cert)
		flags |= GIT_TRANSPORTFLAGS_NO_CHECK_CERT;
631

632
	if ((error = t->connect(t, url, remote->callbacks.credentials, remote->callbacks.payload, direction, flags)) != 0)
633
		goto on_error;
634 635 636

	remote->transport = t;

637
	return 0;
638

639 640
on_error:
	t->free(t);
641 642 643 644

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

645
	return error;
646 647
}

648
int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote)
649
{
650 651
	assert(remote);

652
	return remote->transport->ls(out, size, remote->transport);
653 654
}

655 656 657
int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url)
{
	git_config *cfg;
658 659
	const git_config_entry *ce;
	const char *val = NULL;
660
	int error;
661 662 663

	assert(remote);

664
	if (!proxy_url || !remote->repo)
665 666 667 668
		return -1;

	*proxy_url = NULL;

669 670
	if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0)
		return error;
671 672 673 674 675

	/* Go through the possible sources for proxy configuration, from most specific
	 * to least specific. */

	/* remote.<name>.proxy config setting */
676
	if (remote->name && remote->name[0]) {
677 678
		git_buf buf = GIT_BUF_INIT;

679 680
		if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0)
			return error;
681

682 683
		error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false);
		git_buf_free(&buf);
684

685
		if (error < 0)
686
			return error;
687

688 689 690 691
		if (ce && ce->value) {
			val = ce->value;
			goto found;
		}
692 693 694
	}

	/* http.proxy config setting */
695
	if ((error = git_config__lookup_entry(&ce, cfg, "http.proxy", false)) < 0)
696
		return error;
697 698 699 700
	if (ce && ce->value) {
		val = ce->value;
		goto found;
	}
701 702 703 704

	/* HTTP_PROXY / HTTPS_PROXY environment variables */
	val = use_ssl ? getenv("HTTPS_PROXY") : getenv("HTTP_PROXY");

705 706
found:
	if (val && val[0]) {
707 708 709 710 711 712 713
		*proxy_url = git__strdup(val);
		GITERR_CHECK_ALLOC(*proxy_url);
	}

	return 0;
}

714 715
/* DWIM `refspecs` based on `refs` and append the output to `out` */
static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs)
716
{
717
	size_t i;
718 719 720
	git_refspec *spec;

	git_vector_foreach(refspecs, i, spec) {
721 722 723
		if (git_refspec__dwim_one(out, spec, refs) < 0)
			return -1;
	}
724

725 726
	return 0;
}
727

728 729 730 731
static void free_refspecs(git_vector *vec)
{
	size_t i;
	git_refspec *spec;
732

733 734 735
	git_vector_foreach(vec, i, spec) {
		git_refspec__free(spec);
		git__free(spec);
736 737
	}

738
	git_vector_clear(vec);
739 740 741 742 743 744 745 746 747 748
}

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);
}

749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767
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;
}

768
int git_remote_download(git_remote *remote)
769
{
770
	int error;
771
	git_vector refs;
772

773
	assert(remote);
774

775
	if (ls_to_vector(&refs, remote) < 0)
776 777
		return -1;

778 779
	free_refspecs(&remote->active_refspecs);

780 781 782 783
	error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &refs);
	git_vector_free(&refs);

	if (error < 0)
784
		return error;
785

786
	if ((error = git_fetch_negotiate(remote)) < 0)
787
		return error;
788

789
	return git_fetch_download_pack(remote);
790 791
}

792 793 794 795 796
int git_remote_fetch(git_remote *remote)
{
	int error;

	/* Connect and download everything */
797
	if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) != 0)
798 799
		return error;

800
	if ((error = git_remote_download(remote)) != 0)
801 802 803 804 805 806 807 808 809
		return error;

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

	/* Create "remote/foo" branches for all remote branches */
	return git_remote_update_tips(remote);
}

810 811 812 813 814 815 816 817
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;

	assert(update_heads && fetchspec_src);

	*out = NULL;
nulltoken committed
818 819 820 821 822

	git_vector_foreach(update_heads, i, remote_ref) {
		if (strcmp(remote_ref->name, fetchspec_src) == 0) {
			*out = remote_ref;
			break;
823 824 825 826 827 828
		}
	}

	return 0;
}

829
static int remote_head_for_ref(git_remote_head **out, git_refspec *spec, git_vector *update_heads, git_reference *ref)
830 831 832 833 834 835
{
	git_reference *resolved_ref = NULL;
	git_reference *tracking_ref = NULL;
	git_buf remote_name = GIT_BUF_INIT;
	int error = 0;

836
	assert(out && spec && ref);
837 838 839 840 841

	*out = NULL;

	if ((error = git_reference_resolve(&resolved_ref, ref)) < 0 ||
		(!git_reference_is_branch(resolved_ref)) ||
842
		(error = git_branch_upstream(&tracking_ref, resolved_ref)) < 0 ||
843
		(error = git_refspec_transform_l(&remote_name, spec, git_reference_name(tracking_ref))) < 0) {
844
		/* Not an error if HEAD is unborn or no tracking branch */
845 846 847 848 849 850 851 852 853 854 855 856 857 858 859
		if (error == GIT_ENOTFOUND)
			error = 0;

		goto cleanup;
	}

	error = remote_head_for_fetchspec_src(out, update_heads, git_buf_cstr(&remote_name));

cleanup:
	git_reference_free(tracking_ref);
	git_reference_free(resolved_ref);
	git_buf_free(&remote_name);
	return error;
}

860
static int git_remote_write_fetchhead(git_remote *remote, git_refspec *spec, git_vector *update_heads)
861 862 863 864 865 866 867 868 869 870 871
{
	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;

	assert(remote);

872 873 874 875
	/* no heads, nothing to do */
	if (update_heads->length == 0)
		return 0;

876 877 878 879 880 881 882 883 884
	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 ||
885
			(error = remote_head_for_ref(&merge_remote_ref, spec, update_heads, head_ref)) < 0)
886 887 888 889 890 891 892 893
				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
894
	git_vector_foreach(update_heads, i, remote_ref) {
895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924
		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;
}

925
static int update_tips_for_spec(git_remote *remote, git_refspec *spec, git_vector *refs)
926
{
927
	int error = 0, autotag;
928
	unsigned int i = 0;
929
	git_buf refname = GIT_BUF_INIT;
930
	git_oid old;
931
	git_odb *odb;
932 933
	git_remote_head *head;
	git_reference *ref;
934
	git_refspec tagspec;
935
	git_vector update_heads;
936

937 938
	assert(remote);

939
	if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
940 941
		return -1;

942
	if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
943 944
		return -1;

945
	/* Make a copy of the transport's refs */
946
	if (git_vector_init(&update_heads, 16, NULL) < 0)
947
		return -1;
948

949 950
	for (; i < refs->length; ++i) {
		head = git_vector_get(refs, i);
951
		autotag = 0;
952

953 954 955 956
		/* Ignore malformed ref names (which also saves us from tag^{} */
		if (!git_reference_is_valid_name(head->name))
			continue;

957
		if (git_refspec_src_matches(spec, head->name) && spec->dst) {
958 959 960
			if (git_refspec_transform_r(&refname, spec, head->name) < 0)
				goto on_error;
		} else if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_NONE) {
961 962 963

			if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_ALL)
				autotag = 1;
964 965 966 967 968 969 970 971 972 973 974 975 976

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

			git_buf_clear(&refname);
			if (git_buf_puts(&refname, head->name) < 0)
				goto on_error;
		} else {
			continue;
		}

		if (autotag && !git_odb_exists(odb, &head->oid))
			continue;
977

978 979 980
		if (git_vector_insert(&update_heads, head) < 0)
			goto on_error;

981
		error = git_reference_name_to_id(&old, remote->repo, refname.ptr);
982
		if (error < 0 && error != GIT_ENOTFOUND)
983 984
			goto on_error;

985
		if (error == GIT_ENOTFOUND)
986 987
			memset(&old, 0, GIT_OID_RAWSZ);

988
		if (!git_oid__cmp(&old, &head->oid))
989
			continue;
990

991
		/* In autotag mode, don't overwrite any locally-existing tags */
992
		error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag);
993
		if (error < 0 && error != GIT_EEXISTS)
994
			goto on_error;
995 996

		git_reference_free(ref);
997

998
		if (remote->callbacks.update_tips != NULL) {
Ben Straub committed
999
			if (remote->callbacks.update_tips(refname.ptr, &old, &head->oid, remote->callbacks.payload) < 0)
1000 1001
				goto on_error;
		}
1002 1003
	}

1004
	if (git_remote_update_fetchhead(remote) &&
1005
	    (error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0)
1006 1007 1008
		goto on_error;

	git_vector_free(&update_heads);
1009
	git_refspec__free(&tagspec);
1010
	git_buf_free(&refname);
1011 1012 1013
	return 0;

on_error:
1014
	git_vector_free(&update_heads);
1015
	git_refspec__free(&tagspec);
1016 1017
	git_buf_free(&refname);
	return -1;
1018

1019 1020
}

1021 1022
int git_remote_update_tips(git_remote *remote)
{
1023
	git_refspec *spec, tagspec;
1024
	git_vector refs;
1025
	int error;
1026 1027
	size_t i;

1028 1029 1030
	if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
		return -1;

1031 1032

	if ((error = ls_to_vector(&refs, remote)) < 0)
1033 1034 1035 1036 1037 1038
		goto out;

	if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
		error = update_tips_for_spec(remote, &tagspec, &refs);
		goto out;
	}
1039

1040
	git_vector_foreach(&remote->active_refspecs, i, spec) {
1041 1042 1043
		if (spec->push)
			continue;

1044 1045
		if ((error = update_tips_for_spec(remote, spec, &refs)) < 0)
			goto out;
1046 1047
	}

1048
out:
1049
	git_vector_free(&refs);
1050 1051
	git_refspec__free(&tagspec);
	return error;
1052 1053
}

1054 1055
int git_remote_connected(git_remote *remote)
{
1056
	assert(remote);
1057 1058 1059 1060 1061

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

	/* Ask the transport if it's connected. */
1062
	return remote->transport->is_connected(remote->transport);
1063 1064
}

1065 1066
void git_remote_stop(git_remote *remote)
{
1067 1068 1069
	assert(remote);

	if (remote->transport && remote->transport->cancel)
1070
		remote->transport->cancel(remote->transport);
1071 1072
}

1073 1074
void git_remote_disconnect(git_remote *remote)
{
1075 1076
	assert(remote);

1077 1078
	if (git_remote_connected(remote))
		remote->transport->close(remote->transport);
1079 1080
}

Carlos Martín Nieto committed
1081 1082
void git_remote_free(git_remote *remote)
{
1083 1084 1085
	if (remote == NULL)
		return;

1086 1087 1088 1089 1090 1091 1092 1093 1094
	if (remote->transport != NULL) {
		git_remote_disconnect(remote);

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

	git_vector_free(&remote->refs);

1095
	free_refspecs(&remote->refspecs);
1096 1097
	git_vector_free(&remote->refspecs);

1098 1099 1100
	free_refspecs(&remote->active_refspecs);
	git_vector_free(&remote->active_refspecs);

1101
	git__free(remote->url);
1102
	git__free(remote->pushurl);
1103 1104
	git__free(remote->name);
	git__free(remote);
Carlos Martín Nieto committed
1105
}
1106

1107
static int remote_list_cb(const git_config_entry *entry, void *payload)
1108
{
1109
	git_vector *list = payload;
1110 1111 1112
	const char *name = entry->name + strlen("remote.");
	size_t namelen = strlen(name);
	char *remote_name;
1113

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

1116 1117 1118 1119
	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" */
1120
	GITERR_CHECK_ALLOC(remote_name);
1121

1122
	return git_vector_insert(list, remote_name);
1123 1124 1125 1126 1127
}

int git_remote_list(git_strarray *remotes_list, git_repository *repo)
{
	int error;
1128
	git_config *cfg;
1129
	git_vector list = GIT_VECTOR_INIT;
1130

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

1134
	if ((error = git_vector_init(&list, 4, git__strcmp_cb)) < 0)
1135
		return error;
1136

1137
	error = git_config_foreach_match(
1138
		cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list);
1139

1140
	if (error < 0) {
1141
		git_vector_free_deep(&list);
1142 1143 1144
		return error;
	}

1145
	git_vector_uniq(&list, git__free);
1146

1147 1148
	remotes_list->strings =
		(char **)git_vector_detach(&remotes_list->count, NULL, &list);
1149

1150
	return 0;
1151
}
1152

1153 1154 1155 1156 1157 1158
void git_remote_check_cert(git_remote *remote, int check)
{
	assert(remote);

	remote->check_cert = check;
}
1159

1160
int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks)
1161 1162 1163
{
	assert(remote && callbacks);

Ben Straub committed
1164
	GITERR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
1165

1166
	memcpy(&remote->callbacks, callbacks, sizeof(git_remote_callbacks));
1167

1168
	if (remote->transport && remote->transport->set_callbacks)
1169
		return remote->transport->set_callbacks(remote->transport,
1170 1171
			remote->callbacks.progress,
			NULL,
Ben Straub committed
1172
			remote->callbacks.payload);
1173 1174

	return 0;
1175 1176 1177 1178 1179 1180
}

int git_remote_set_transport(git_remote *remote, git_transport *transport)
{
	assert(remote && transport);

Ben Straub committed
1181
	GITERR_CHECK_VERSION(transport, GIT_TRANSPORT_VERSION, "git_transport");
1182

1183
	if (remote->transport) {
1184 1185
		giterr_set(GITERR_NET, "A transport is already bound to this remote");
		return -1;
1186
	}
1187 1188 1189

	remote->transport = transport;
	return 0;
1190
}
1191

Ben Straub committed
1192
const git_transfer_progress* git_remote_stats(git_remote *remote)
1193 1194 1195 1196 1197
{
	assert(remote);
	return &remote->stats;
}

Ben Straub committed
1198
git_remote_autotag_option_t git_remote_autotag(git_remote *remote)
1199 1200 1201 1202
{
	return remote->download_tags;
}

Ben Straub committed
1203
void git_remote_set_autotag(git_remote *remote, git_remote_autotag_option_t value)
1204 1205 1206
{
	remote->download_tags = value;
}
1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234

static int rename_remote_config_section(
	git_repository *repo,
	const char *old_name,
	const char *new_name)
{
	git_buf old_section_name = GIT_BUF_INIT,
		new_section_name = GIT_BUF_INIT;
	int error = -1;

	if (git_buf_printf(&old_section_name, "remote.%s", old_name) < 0)
		goto cleanup;

	if (git_buf_printf(&new_section_name, "remote.%s", new_name) < 0)
		goto cleanup;

	error = git_config_rename_section(
		repo,
		git_buf_cstr(&old_section_name),
		git_buf_cstr(&new_section_name));

cleanup:
	git_buf_free(&old_section_name);
	git_buf_free(&new_section_name);

	return error;
}

1235
struct update_data {
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249
	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;

1250 1251
	return git_config_set_string(
		data->config, entry->name, data->new_remote_name);
1252 1253 1254 1255 1256 1257 1258
}

static int update_branch_remote_config_entry(
	git_repository *repo,
	const char *old_name,
	const char *new_name)
{
1259 1260
	int error;
	struct update_data data = { NULL };
1261

1262 1263
	if ((error = git_repository_config__weakptr(&data.config, repo)) < 0)
		return error;
1264 1265 1266 1267

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

1268
	return git_config_foreach_match(
1269
		data.config, "branch\\..+\\.remote", update_config_entries_cb, &data);
1270 1271 1272
}

static int rename_one_remote_reference(
Vicent Marti committed
1273
	git_reference *reference,
1274 1275 1276
	const char *old_remote_name,
	const char *new_remote_name)
{
1277
	int error;
1278 1279
	git_buf new_name = GIT_BUF_INIT;

1280
	error = git_buf_printf(
1281 1282 1283
		&new_name,
		GIT_REFS_REMOTES_DIR "%s%s",
		new_remote_name,
1284
		reference->name + strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name));
1285

1286 1287 1288 1289 1290
	if (!error) {
		error = git_reference_rename(
			NULL, reference, git_buf_cstr(&new_name), 0);
		git_reference_free(reference);
	}
1291 1292 1293 1294 1295 1296 1297 1298 1299 1300

	git_buf_free(&new_name);
	return error;
}

static int rename_remote_references(
	git_repository *repo,
	const char *old_name,
	const char *new_name)
{
1301
	int error;
Vicent Marti committed
1302
	git_reference *ref;
1303
	git_reference_iterator *iter;
1304

1305 1306
	if ((error = git_reference_iterator_new(&iter, repo)) < 0)
		return error;
1307

Vicent Marti committed
1308
	while ((error = git_reference_next(&ref, iter)) == 0) {
Russell Belfer committed
1309 1310
		if (git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) {
			git_reference_free(ref);
1311
			continue;
Russell Belfer committed
1312
		}
1313

1314 1315
		if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0)
			break;
1316 1317 1318
	}

	git_reference_iterator_free(iter);
1319

1320
	return (error == GIT_ITEROVER) ? 0 : error;
1321 1322 1323 1324 1325 1326 1327 1328 1329
}

static int rename_fetch_refspecs(
	git_remote *remote,
	const char *new_name,
	int (*callback)(const char *problematic_refspec, void *payload),
	void *payload)
{
	git_config *config;
1330
	git_buf base = GIT_BUF_INIT, var = GIT_BUF_INIT, val = GIT_BUF_INIT;
1331
	const git_refspec *spec;
1332
	size_t i;
1333
	int error = 0;
1334

1335
	if ((error = git_repository_config__weakptr(&config, remote->repo)) < 0)
1336 1337 1338 1339 1340
		return error;

	if ((error = git_buf_printf(
			&base, "+refs/heads/*:refs/remotes/%s/*", remote->name)) < 0)
		return error;
1341

1342
	git_vector_foreach(&remote->refspecs, i, spec) {
1343 1344
		if (spec->push)
			continue;
1345

1346 1347 1348 1349
		/* Every refspec is a problem refspec for an in-memory remote, OR */
		/* Does the dst part of the refspec follow the expected format? */
		if (!remote->name ||
			strcmp(git_buf_cstr(&base), spec->string)) {
1350

1351
			if ((error = callback(spec->string, payload)) != 0) {
1352
				giterr_set_after_callback(error);
1353
				break;
1354 1355
			}

1356 1357
			continue;
		}
1358

1359
		/* If we do want to move it to the new section */
1360

1361 1362
		git_buf_clear(&val);
		git_buf_clear(&var);
1363

1364 1365 1366 1367 1368
		if (git_buf_printf(
				&val, "+refs/heads/*:refs/remotes/%s/*", new_name) < 0 ||
			git_buf_printf(&var, "remote.%s.fetch", new_name) < 0)
		{
			error = -1;
1369
			break;
1370
		}
1371

1372 1373
		if ((error = git_config_set_string(
				config, git_buf_cstr(&var), git_buf_cstr(&val))) < 0)
1374
			break;
1375
	}
1376

1377 1378 1379
	git_buf_free(&base);
	git_buf_free(&var);
	git_buf_free(&val);
1380 1381 1382 1383 1384 1385
	return error;
}

int git_remote_rename(
	git_remote *remote,
	const char *new_name,
Ben Straub committed
1386
	git_remote_rename_problem_cb callback,
1387 1388 1389 1390 1391 1392
	void *payload)
{
	int error;

	assert(remote && new_name);

1393
	if (!remote->name) {
1394 1395 1396 1397
		giterr_set(GITERR_INVALID, "Can't rename an in-memory remote.");
		return GIT_EINVALIDSPEC;
	}

1398 1399 1400
	if ((error = ensure_remote_name_is_valid(new_name)) < 0)
		return error;

1401 1402
	if (remote->repo) {
		if ((error = ensure_remote_doesnot_exist(remote->repo, new_name)) < 0)
1403 1404
			return error;

1405 1406 1407 1408 1409 1410
		if (!remote->name) {
			if ((error = rename_fetch_refspecs(
				remote,
				new_name,
				callback,
				payload)) < 0)
1411
					return error;
1412

1413
			remote->name = git__strdup(new_name);
1414
			GITERR_CHECK_ALLOC(remote->name);
1415

1416 1417
			return git_remote_save(remote);
		}
1418

1419 1420 1421 1422 1423
		if ((error = rename_remote_config_section(
			remote->repo,
			remote->name,
			new_name)) < 0)
				return error;
1424

1425 1426 1427 1428 1429
		if ((error = update_branch_remote_config_entry(
			remote->repo,
			remote->name,
			new_name)) < 0)
				return error;
1430

1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441
		if ((error = rename_remote_references(
			remote->repo,
			remote->name,
			new_name)) < 0)
				return error;

		if ((error = rename_fetch_refspecs(
			remote,
			new_name,
			callback,
			payload)) < 0)
1442
				return error;
1443
	}
1444 1445

	git__free(remote->name);
1446

1447
	remote->name = git__strdup(new_name);
1448
	GITERR_CHECK_ALLOC(remote->name);
1449 1450 1451

	return 0;
}
1452 1453 1454

int git_remote_update_fetchhead(git_remote *remote)
{
Russell Belfer committed
1455
	return (remote->update_fetchhead != 0);
1456 1457 1458 1459
}

void git_remote_set_update_fetchhead(git_remote *remote, int value)
{
Russell Belfer committed
1460
	remote->update_fetchhead = (value != 0);
1461
}
1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481

int git_remote_is_valid_name(
	const char *remote_name)
{
	git_buf buf = GIT_BUF_INIT;
	git_refspec refspec;
	int error = -1;

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

	git_buf_printf(&buf, "refs/heads/test:refs/remotes/%s/test", remote_name);
	error = git_refspec__parse(&refspec, git_buf_cstr(&buf), true);

	git_buf_free(&buf);
	git_refspec__free(&refspec);

	giterr_clear();
	return error == 0;
}
1482 1483 1484 1485 1486 1487

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

1488
	git_vector_foreach(&remote->active_refspecs, i, spec) {
1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503
		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;

1504
	git_vector_foreach(&remote->active_refspecs, i, spec) {
1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521
		if (spec->push)
			continue;

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

	return NULL;
}

void git_remote_clear_refspecs(git_remote *remote)
{
	git_refspec *spec;
	size_t i;

	git_vector_foreach(&remote->refspecs, i, spec) {
		git_refspec__free(spec);
1522
		git__free(spec);
1523 1524 1525 1526
	}
	git_vector_clear(&remote->refspecs);
}

1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539
static int add_and_dwim(git_remote *remote, const char *str, int push)
{
	git_refspec *spec;
	git_vector *vec;

	if (add_refspec(remote, str, !push) < 0)
		return -1;

	vec = &remote->refspecs;
	spec = git_vector_get(vec, vec->length - 1);
	return git_refspec__dwim_one(&remote->active_refspecs, spec, &remote->refs);
}

1540
int git_remote_add_fetch(git_remote *remote, const char *refspec)
1541
{
1542
	return add_and_dwim(remote, refspec, false);
1543 1544
}

1545
int git_remote_add_push(git_remote *remote, const char *refspec)
1546
{
1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588
	return add_and_dwim(remote, refspec, true);
}

static int set_refspecs(git_remote *remote, git_strarray *array, int push)
{
	git_vector *vec = &remote->refspecs;
	git_refspec *spec;
	size_t i;

	/* Start by removing any refspecs of the same type */
	for (i = 0; i < vec->length; i++) {
		spec = git_vector_get(vec, i);
		if (spec->push != push)
			continue;

		git_refspec__free(spec);
		git__free(spec);
		git_vector_remove(vec, i);
		i--;
	}

	/* And now we add the new ones */

	for (i = 0; i < array->count; i++) {
		if (add_refspec(remote, array->strings[i], !push) < 0)
			return -1;
	}

	free_refspecs(&remote->active_refspecs);
	git_vector_clear(&remote->active_refspecs);

	return dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs);
}

int git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array)
{
	return set_refspecs(remote, array, false);
}

int git_remote_set_push_refspecs(git_remote *remote, git_strarray *array)
{
	return set_refspecs(remote, array, true);
1589
}
1590

Linquize committed
1591
static int copy_refspecs(git_strarray *array, git_remote *remote, unsigned int push)
1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604
{
	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;

1605
		if ((dup = git__strdup(spec->string)) == NULL)
1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619
			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:
1620
	git_vector_free_deep(&refspecs);
1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633

	return -1;
}

int git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote)
{
	return copy_refspecs(array, remote, false);
}

int git_remote_get_push_refspecs(git_strarray *array, git_remote *remote)
{
	return copy_refspecs(array, remote, true);
}
1634 1635 1636 1637 1638 1639 1640 1641 1642 1643

size_t git_remote_refspec_count(git_remote *remote)
{
	return remote->refspecs.length;
}

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