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

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

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

25 26 27
	spec = git__calloc(1, sizeof(git_refspec));
	GITERR_CHECK_ALLOC(spec);

28 29 30 31
	if (git_refspec__parse(spec, string, is_fetch) < 0) {
		git__free(spec);
		return -1;
	}
32 33

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

40
	return 0;
Carlos Martín Nieto committed
41 42
}

43 44 45 46 47 48
static int download_tags_value(git_remote *remote, git_config *cfg)
{
	const char *val;
	git_buf buf = GIT_BUF_INIT;
	int error;

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

	error = git_config_get_string(&val, cfg, git_buf_cstr(&buf));
	git_buf_free(&buf);
	if (!error && !strcmp(val, "--no-tags"))
		remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
57 58
	else if (!error && !strcmp(val, "--tags"))
		remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
59

60 61
	if (error == GIT_ENOTFOUND) {
		giterr_clear();
62
		error = 0;
63
	}
64 65 66 67

	return error;
}

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

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

	return error;
}

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
static int get_check_cert(git_repository *repo)
{
	git_config *cfg;
	const char *val;
	int check_cert;

	assert(repo);

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

	/* GIT_SSL_NO_VERIFY environment variable */
	if ((val = getenv("GIT_SSL_NO_VERIFY")) &&
		!git_config_parse_bool(&check_cert, val))
		return !check_cert;

	/* http.sslVerify config setting */
	if (!git_repository_config__weakptr(&cfg, repo) &&
		!git_config_get_bool(&check_cert, cfg, "http.sslVerify"))
		return check_cert;

	/* By default, we *DO* want to verify the certificate. */
	return 1;
}

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

113
	/* name is optional */
114
	assert(out && repo && url);
115

116
	remote = git__calloc(1, sizeof(git_remote));
117
	GITERR_CHECK_ALLOC(remote);
118 119

	remote->repo = repo;
120
	remote->check_cert = (unsigned)get_check_cert(repo);
121
	remote->update_fetchhead = 1;
122

123
	if (git_vector_init(&remote->refs, 32, NULL) < 0)
124
		goto on_error;
125

126
	remote->url = git__strdup(url);
127
	GITERR_CHECK_ALLOC(remote->url);
128

129 130
	if (name != NULL) {
		remote->name = git__strdup(name);
131
		GITERR_CHECK_ALLOC(remote->name);
132 133
	}

134
	if (fetch != NULL) {
135
		if (add_refspec(remote, fetch, true) < 0)
136 137 138
			goto on_error;
	}

139 140
	if (!name)
		/* A remote without a name doesn't download tags */
141 142
		remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;

143
	*out = remote;
144
	git_buf_free(&fetchbuf);
145
	return 0;
146 147 148

on_error:
	git_remote_free(remote);
149 150
	git_buf_free(&fetchbuf);
	return error;
151 152
}

153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
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;
}

175

176 177 178
int git_remote_create(git_remote **out, git_repository *repo, const char *name, const char *url)
{
	git_buf buf = GIT_BUF_INIT;
179
	git_remote *remote = NULL;
180 181 182 183 184
	int error;

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

185 186 187
	if ((error = ensure_remote_doesnot_exist(repo, name)) < 0)
		return error;

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

191
	if (create_internal(&remote, repo, name, url, git_buf_cstr(&buf)) < 0)
192 193 194 195
		goto on_error;

	git_buf_free(&buf);

196
	if (git_remote_save(remote) < 0)
197 198
		goto on_error;

199 200
	*out = remote;

201 202 203 204
	return 0;

on_error:
	git_buf_free(&buf);
205
	git_remote_free(remote);
206 207 208
	return -1;
}

209
int git_remote_create_inmemory(git_remote **out, git_repository *repo, const char *fetch, const char *url)
210 211 212 213
{
	int error;
	git_remote *remote;

214
	if ((error = create_internal(&remote, repo, NULL, url, fetch)) < 0)
215 216 217 218 219 220
		return error;

	*out = remote;
	return 0;
}

221 222 223 224 225 226 227 228 229 230 231 232
struct refspec_cb_data {
	git_remote *remote;
	int fetch;
};

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

	return add_refspec(data->remote, entry->value, data->fetch);
}

233
static int get_optional_config(
234 235
	bool *found, git_config *config, git_buf *buf,
	git_config_foreach_cb cb, void *payload)
236 237 238 239 240 241 242 243
{
	int error = 0;
	const char *key = git_buf_cstr(buf);

	if (git_buf_oom(buf))
		return -1;

	if (cb != NULL)
244
		error = git_config_get_multivar_foreach(config, key, NULL, cb, payload);
245 246 247
	else
		error = git_config_get_string(payload, config, key);

248 249 250
	if (found)
		*found = !error;

251 252 253 254 255 256 257 258 259 260 261
	if (error == GIT_ENOTFOUND) {
		giterr_clear();
		error = 0;
	}

	if (error < 0)
		error = -1;

	return error;
}

262
int git_remote_load(git_remote **out, git_repository *repo, const char *name)
Carlos Martín Nieto committed
263 264
{
	git_remote *remote;
265
	git_buf buf = GIT_BUF_INIT;
Carlos Martín Nieto committed
266
	const char *val;
267
	int error = 0;
268
	git_config *config;
269
	struct refspec_cb_data data;
270
	bool optional_setting_found = false, found;
271

272 273
	assert(out && repo && name);

274 275 276
	if ((error = ensure_remote_name_is_valid(name)) < 0)
		return error;

277 278
	if (git_repository_config__weakptr(&config, repo) < 0)
		return -1;
279

Carlos Martín Nieto committed
280
	remote = git__malloc(sizeof(git_remote));
281
	GITERR_CHECK_ALLOC(remote);
Carlos Martín Nieto committed
282 283

	memset(remote, 0x0, sizeof(git_remote));
284
	remote->check_cert = (unsigned)get_check_cert(repo);
285
	remote->update_fetchhead = 1;
Carlos Martín Nieto committed
286
	remote->name = git__strdup(name);
287
	GITERR_CHECK_ALLOC(remote->name);
Carlos Martín Nieto committed
288

289
	if ((git_vector_init(&remote->refs, 32, NULL) < 0) ||
290
	    (git_vector_init(&remote->refspecs, 2, NULL))) {
291 292 293
		error = -1;
		goto cleanup;
	}
294

295 296 297 298
	if (git_buf_printf(&buf, "remote.%s.url", name) < 0) {
		error = -1;
		goto cleanup;
	}
Carlos Martín Nieto committed
299

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

303 304
	optional_setting_found |= found;

305
	remote->repo = repo;
306 307 308 309 310

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

312
	val = NULL;
313
	git_buf_clear(&buf);
314
	git_buf_printf(&buf, "remote.%s.pushurl", name);
315

316
	if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
317 318
		goto cleanup;

319 320 321 322
	optional_setting_found |= found;

	if (!optional_setting_found) {
		error = GIT_ENOTFOUND;
323
		goto cleanup;
324
	}
325

326
	if (found && strlen(val) > 0) {
327 328 329 330
		remote->pushurl = git__strdup(val);
		GITERR_CHECK_ALLOC(remote->pushurl);
	}

331 332
	data.remote = remote;
	data.fetch = true;
333
	git_buf_clear(&buf);
334 335
	git_buf_printf(&buf, "remote.%s.fetch", name);

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

339
	data.fetch = false;
340 341
	git_buf_clear(&buf);
	git_buf_printf(&buf, "remote.%s.push", name);
Carlos Martín Nieto committed
342

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

346 347 348
	if (download_tags_value(remote, config) < 0)
		goto cleanup;

Carlos Martín Nieto committed
349 350 351
	*out = remote;

cleanup:
352
	git_buf_free(&buf);
353

354
	if (error < 0)
Carlos Martín Nieto committed
355 356 357 358 359
		git_remote_free(remote);

	return error;
}

360
static int update_config_refspec(const git_remote *remote, git_config *config, int direction)
361
{
362
	git_buf name = GIT_BUF_INIT;
Linquize committed
363
	unsigned int push;
364 365
	const char *dir;
	size_t i;
366
	int error = 0;
367

368 369
	push = direction == GIT_DIRECTION_PUSH;
	dir = push ? "push" : "fetch";
370

371 372
	if (git_buf_printf(&name, "remote.%s.%s", remote->name, dir) < 0)
		return -1;
373

374
	/* Clear out the existing config */
375
	while (!error)
376
		error = git_config_delete_entry(config, git_buf_cstr(&name));
377

378 379 380
	if (error != GIT_ENOTFOUND)
		return error;

381
	for (i = 0; i < remote->refspecs.length; i++) {
382 383 384 385 386
		git_refspec *spec = git_vector_get(&remote->refspecs, i);

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

387 388
		if ((error = git_config_set_multivar(
				config, git_buf_cstr(&name), "", spec->string)) < 0) {
389 390 391 392 393 394
			goto cleanup;
		}
	}

	giterr_clear();
	error = 0;
395 396 397 398 399 400 401

cleanup:
	git_buf_free(&name);

	return error;
}

402 403
int git_remote_save(const git_remote *remote)
{
404
	int error;
405
	git_config *config;
406
	const char *tagopt = NULL;
407
	git_buf buf = GIT_BUF_INIT;
408

409 410
	assert(remote);

411
	if (!remote->name) {
412 413
		giterr_set(GITERR_INVALID, "Can't save an in-memory remote.");
		return GIT_EINVALIDSPEC;
414 415
	}

416 417
	if ((error = ensure_remote_name_is_valid(remote->name)) < 0)
		return error;
418

419 420
	if (git_repository_config__weakptr(&config, remote->repo) < 0)
		return -1;
421

422
	if (git_buf_printf(&buf, "remote.%s.url", remote->name) < 0)
423
		return -1;
424

425 426 427 428
	if (git_config_set_string(config, git_buf_cstr(&buf), remote->url) < 0) {
		git_buf_free(&buf);
		return -1;
	}
429

430 431 432
	git_buf_clear(&buf);
	if (git_buf_printf(&buf, "remote.%s.pushurl", remote->name) < 0)
		return -1;
433

434
	if (remote->pushurl) {
435 436 437 438
		if (git_config_set_string(config, git_buf_cstr(&buf), remote->pushurl) < 0) {
			git_buf_free(&buf);
			return -1;
		}
439
	} else {
Ben Straub committed
440
		int error = git_config_delete_entry(config, git_buf_cstr(&buf));
441 442
		if (error == GIT_ENOTFOUND) {
			error = 0;
443
			giterr_clear();
444 445 446 447 448
		}
		if (error < 0) {
			git_buf_free(&buf);
			return -1;
		}
449 450
	}

451 452
	if (update_config_refspec(remote, config, GIT_DIRECTION_FETCH) < 0)
		goto on_error;
453

454 455
	if (update_config_refspec(remote, config, GIT_DIRECTION_PUSH) < 0)
		goto on_error;
456

457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
	/*
	 * 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
485
		if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
486 487 488
			goto on_error;
	}

489
	git_buf_free(&buf);
490 491 492 493 494 495

	return 0;

on_error:
	git_buf_free(&buf);
	return -1;
496 497
}

Ben Straub committed
498
const char *git_remote_name(const git_remote *remote)
Carlos Martín Nieto committed
499
{
500
	assert(remote);
Carlos Martín Nieto committed
501 502 503
	return remote->name;
}

Etienne Samson committed
504 505 506 507 508 509
git_repository *git_remote_owner(const git_remote *remote)
{
	assert(remote);
	return remote->repo;
}

Ben Straub committed
510
const char *git_remote_url(const git_remote *remote)
Carlos Martín Nieto committed
511
{
512
	assert(remote);
Carlos Martín Nieto committed
513 514 515
	return remote->url;
}

516 517 518 519 520 521 522 523 524 525 526 527
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
528
const char *git_remote_pushurl(const git_remote *remote)
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
{
	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;
}

548 549 550 551
const char* git_remote__urlfordirection(git_remote *remote, int direction)
{
	assert(remote);

552 553
	assert(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);

Ben Straub committed
554
	if (direction == GIT_DIRECTION_FETCH) {
555 556 557
		return remote->url;
	}

Ben Straub committed
558
	if (direction == GIT_DIRECTION_PUSH) {
559 560 561 562 563 564
		return remote->pushurl ? remote->pushurl : remote->url;
	}

	return NULL;
}

Ben Straub committed
565
int git_remote_connect(git_remote *remote, git_direction direction)
566 567
{
	git_transport *t;
568
	const char *url;
569
	int flags = GIT_TRANSPORTFLAGS_NONE;
570

571 572
	assert(remote);

573 574
	t = remote->transport;

575
	url = git_remote__urlfordirection(remote, direction);
576 577 578
	if (url == NULL ) {
		giterr_set(GITERR_INVALID,
			"Malformed remote '%s' - missing URL", remote->name);
579
		return -1;
580
	}
581

582 583
	/* A transport could have been supplied in advance with
	 * git_remote_set_transport */
584
	if (!t && git_transport_new(&t, remote, url) < 0)
585
		return -1;
586

587
	if (t->set_callbacks &&
Ben Straub committed
588
		t->set_callbacks(t, remote->callbacks.progress, NULL, remote->callbacks.payload) < 0)
589
		goto on_error;
590

591 592
	if (!remote->check_cert)
		flags |= GIT_TRANSPORTFLAGS_NO_CHECK_CERT;
593

594
	if (t->connect(t, url, remote->callbacks.credentials, remote->callbacks.payload, direction, flags) < 0)
595
		goto on_error;
596 597 598

	remote->transport = t;

599
	return 0;
600

601 602
on_error:
	t->free(t);
603 604 605 606

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

607
	return -1;
608 609
}

610
int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload)
611
{
612 613
	assert(remote);

614
	return remote->transport->ls(remote->transport, list_cb, payload);
615 616
}

617 618 619 620 621 622 623
int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url)
{
	git_config *cfg;
	const char *val;

	assert(remote);

624
	if (!proxy_url || !remote->repo)
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
		return -1;

	*proxy_url = NULL;

	if (git_repository_config__weakptr(&cfg, remote->repo) < 0)
		return -1;

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

	/* remote.<name>.proxy config setting */
	if (remote->name && 0 != *(remote->name)) {
		git_buf buf = GIT_BUF_INIT;

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

		if (!git_config_get_string(&val, cfg, git_buf_cstr(&buf)) &&
			val && ('\0' != *val)) {
			git_buf_free(&buf);

			*proxy_url = git__strdup(val);
			GITERR_CHECK_ALLOC(*proxy_url);
			return 0;
		}

		git_buf_free(&buf);
	}

	/* http.proxy config setting */
	if (!git_config_get_string(&val, cfg, "http.proxy") &&
		val && ('\0' != *val)) {
		*proxy_url = git__strdup(val);
		GITERR_CHECK_ALLOC(*proxy_url);
		return 0;
	}

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

	if (val && ('\0' != *val)) {
		*proxy_url = git__strdup(val);
		GITERR_CHECK_ALLOC(*proxy_url);
		return 0;
	}

	return 0;
}

674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
static int store_refs(git_remote_head *head, void *payload)
{
	git_vector *refs = (git_vector *)payload;

	return git_vector_insert(refs, head);
}

static int dwim_refspecs(git_vector *refspecs, git_vector *refs)
{
	git_buf buf = GIT_BUF_INIT;
	git_refspec *spec;
	size_t i, j, pos;
	git_remote_head key;

	const char* formatters[] = {
		GIT_REFS_DIR "%s",
		GIT_REFS_TAGS_DIR "%s",
		GIT_REFS_HEADS_DIR "%s",
		NULL
	};

	git_vector_foreach(refspecs, i, spec) {
		if (spec->dwim)
			continue;

		/* shorthand on the lhs */
		if (git__prefixcmp(spec->src, GIT_REFS_DIR)) {
			for (j = 0; formatters[j]; j++) {
				git_buf_clear(&buf);
				if (git_buf_printf(&buf, formatters[j], spec->src) < 0)
					return -1;

				key.name = (char *) git_buf_cstr(&buf);
				if (!git_vector_search(&pos, refs, &key)) {
					/* we found something to match the shorthand, set src to that */
					git__free(spec->src);
					spec->src = git_buf_detach(&buf);
				}
			}
		}

		if (spec->dst && git__prefixcmp(spec->dst, GIT_REFS_DIR)) {
			/* if it starts with "remotes" then we just prepend "refs/" */
			if (!git__prefixcmp(spec->dst, "remotes/")) {
				git_buf_puts(&buf, GIT_REFS_DIR);
			} else {
				git_buf_puts(&buf, GIT_REFS_HEADS_DIR);
			}

			if (git_buf_puts(&buf, spec->dst) < 0)
				return -1;

			git__free(spec->dst);
			spec->dst = git_buf_detach(&buf);
		}

		spec->dwim = 1;
	}

733
	git_buf_free(&buf);
734 735 736 737 738 739 740 741 742 743 744
	return 0;
}

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

745
int git_remote_download(git_remote *remote)
746
{
747
	int error;
748
	git_vector refs;
749

750
	assert(remote);
751

752 753 754 755 756 757 758 759 760 761 762 763
	if (git_vector_init(&refs, 16, remote_head_cmp) < 0)
		return -1;

	if (git_remote_ls(remote, store_refs, &refs) < 0) {
		return -1;
	}

	error = dwim_refspecs(&remote->refspecs, &refs);
	git_vector_free(&refs);
	if (error < 0)
		return -1;

764
	if ((error = git_fetch_negotiate(remote)) < 0)
765
		return error;
766

767
	return git_fetch_download_pack(remote);
768 769
}

770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787
int git_remote_fetch(git_remote *remote)
{
	int error;

	/* Connect and download everything */
	if ((error = git_remote_connect(remote, GIT_DIRECTION_FETCH)) < 0)
		return error;

	if ((error = git_remote_download(remote)) < 0)
		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);
}

788 789 790 791 792 793 794 795
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
796 797 798 799 800

	git_vector_foreach(update_heads, i, remote_ref) {
		if (strcmp(remote_ref->name, fetchspec_src) == 0) {
			*out = remote_ref;
			break;
801 802 803 804 805 806
		}
	}

	return 0;
}

807
static int remote_head_for_ref(git_remote_head **out, git_refspec *spec, git_vector *update_heads, git_reference *ref)
808 809 810 811 812 813
{
	git_reference *resolved_ref = NULL;
	git_reference *tracking_ref = NULL;
	git_buf remote_name = GIT_BUF_INIT;
	int error = 0;

814
	assert(out && spec && ref);
815 816 817 818 819

	*out = NULL;

	if ((error = git_reference_resolve(&resolved_ref, ref)) < 0 ||
		(!git_reference_is_branch(resolved_ref)) ||
820
		(error = git_branch_upstream(&tracking_ref, resolved_ref)) < 0 ||
821
		(error = git_refspec_transform_l(&remote_name, spec, git_reference_name(tracking_ref))) < 0) {
822
		/* Not an error if HEAD is unborn or no tracking branch */
823 824 825 826 827 828 829 830 831 832 833 834 835 836 837
		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;
}

838
static int git_remote_write_fetchhead(git_remote *remote, git_refspec *spec, git_vector *update_heads)
839 840 841 842 843 844 845 846 847 848 849
{
	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);

850 851 852 853
	/* no heads, nothing to do */
	if (update_heads->length == 0)
		return 0;

854 855 856 857 858 859 860 861 862
	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 ||
863
			(error = remote_head_for_ref(&merge_remote_ref, spec, update_heads, head_ref)) < 0)
864 865 866 867 868 869 870 871
				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
872
	git_vector_foreach(update_heads, i, remote_ref) {
873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
		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;
}

903
static int update_tips_for_spec(git_remote *remote, git_refspec *spec, git_vector *refs)
904
{
905
	int error = 0, autotag;
906
	unsigned int i = 0;
907
	git_buf refname = GIT_BUF_INIT;
908
	git_oid old;
909
	git_odb *odb;
910 911
	git_remote_head *head;
	git_reference *ref;
912
	git_refspec tagspec;
913
	git_vector update_heads;
914

915 916
	assert(remote);

917
	if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
918 919
		return -1;

920
	if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
921 922
		return -1;

923
	/* Make a copy of the transport's refs */
924
	if (git_vector_init(&update_heads, 16, NULL) < 0)
925
		return -1;
926

927 928
	for (; i < refs->length; ++i) {
		head = git_vector_get(refs, i);
929
		autotag = 0;
930

931 932 933 934
		/* Ignore malformed ref names (which also saves us from tag^{} */
		if (!git_reference_is_valid_name(head->name))
			continue;

935
		if (git_refspec_src_matches(spec, head->name) && spec->dst) {
936 937 938
			if (git_refspec_transform_r(&refname, spec, head->name) < 0)
				goto on_error;
		} else if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_NONE) {
939 940 941

			if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_ALL)
				autotag = 1;
942 943 944 945 946 947 948 949 950 951 952 953 954

			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;
955

956 957 958
		if (git_vector_insert(&update_heads, head) < 0)
			goto on_error;

959
		error = git_reference_name_to_id(&old, remote->repo, refname.ptr);
960
		if (error < 0 && error != GIT_ENOTFOUND)
961 962
			goto on_error;

963
		if (error == GIT_ENOTFOUND)
964 965
			memset(&old, 0, GIT_OID_RAWSZ);

966
		if (!git_oid__cmp(&old, &head->oid))
967
			continue;
968

969
		/* In autotag mode, don't overwrite any locally-existing tags */
970
		error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag);
971
		if (error < 0 && error != GIT_EEXISTS)
972
			goto on_error;
973 974

		git_reference_free(ref);
975

976
		if (remote->callbacks.update_tips != NULL) {
Ben Straub committed
977
			if (remote->callbacks.update_tips(refname.ptr, &old, &head->oid, remote->callbacks.payload) < 0)
978 979
				goto on_error;
		}
980 981
	}

982
	if (git_remote_update_fetchhead(remote) &&
983
	    (error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0)
984 985 986
		goto on_error;

	git_vector_free(&update_heads);
987
	git_refspec__free(&tagspec);
988
	git_buf_free(&refname);
989 990 991
	return 0;

on_error:
992
	git_vector_free(&update_heads);
993
	git_refspec__free(&tagspec);
994 995
	git_buf_free(&refname);
	return -1;
996

997 998
}

999 1000
int git_remote_update_tips(git_remote *remote)
{
1001
	git_refspec *spec, tagspec;
1002
	git_vector refs;
1003
	int error;
1004 1005
	size_t i;

1006 1007 1008 1009

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

1010 1011 1012
	if (git_vector_init(&refs, 16, NULL) < 0)
		return -1;

1013 1014 1015 1016 1017 1018 1019
	if ((error = git_remote_ls(remote, store_refs, &refs)) < 0)
		goto out;

	if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
		error = update_tips_for_spec(remote, &tagspec, &refs);
		goto out;
	}
1020 1021 1022 1023 1024

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

1025 1026
		if ((error = update_tips_for_spec(remote, spec, &refs)) < 0)
			goto out;
1027 1028
	}

1029 1030
out:
	git_refspec__free(&tagspec);
1031
	git_vector_free(&refs);
1032
	return error;
1033 1034
}

1035 1036
int git_remote_connected(git_remote *remote)
{
1037
	assert(remote);
1038 1039 1040 1041 1042

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

	/* Ask the transport if it's connected. */
1043
	return remote->transport->is_connected(remote->transport);
1044 1045
}

1046 1047
void git_remote_stop(git_remote *remote)
{
1048 1049 1050
	assert(remote);

	if (remote->transport && remote->transport->cancel)
1051
		remote->transport->cancel(remote->transport);
1052 1053
}

1054 1055
void git_remote_disconnect(git_remote *remote)
{
1056 1057
	assert(remote);

1058 1059
	if (git_remote_connected(remote))
		remote->transport->close(remote->transport);
1060 1061
}

Carlos Martín Nieto committed
1062 1063
void git_remote_free(git_remote *remote)
{
1064 1065 1066
	git_refspec *spec;
	size_t i;

1067 1068 1069
	if (remote == NULL)
		return;

1070 1071 1072 1073 1074 1075 1076 1077 1078
	if (remote->transport != NULL) {
		git_remote_disconnect(remote);

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

	git_vector_free(&remote->refs);

1079 1080 1081 1082 1083 1084
	git_vector_foreach(&remote->refspecs, i, spec) {
		git_refspec__free(spec);
		git__free(spec);
	}
	git_vector_free(&remote->refspecs);

1085
	git__free(remote->url);
1086
	git__free(remote->pushurl);
1087 1088
	git__free(remote->name);
	git__free(remote);
Carlos Martín Nieto committed
1089
}
1090

1091
static int remote_list_cb(const git_config_entry *entry, void *payload)
1092
{
1093 1094 1095 1096
	git_vector *list = payload;
	const char *name = entry->name + strlen("remote.");
	size_t namelen = strlen(name);
	char *remote_name;
1097

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

1100 1101 1102 1103 1104
	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" */
	GITERR_CHECK_ALLOC(remote_name);
1105

1106
	return git_vector_insert(list, remote_name);
1107 1108 1109 1110 1111 1112 1113 1114
}

int git_remote_list(git_strarray *remotes_list, git_repository *repo)
{
	git_config *cfg;
	git_vector list;
	int error;

1115 1116
	if (git_repository_config__weakptr(&cfg, repo) < 0)
		return -1;
1117

1118
	if (git_vector_init(&list, 4, git__strcmp_cb) < 0)
1119
		return -1;
1120

1121 1122
	error = git_config_foreach_match(
		cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list);
1123

1124
	if (error < 0) {
1125 1126
		size_t i;
		char *elem;
1127

1128
		git_vector_foreach(&list, i, elem) {
1129
			git__free(elem);
1130 1131 1132
		}

		git_vector_free(&list);
1133 1134 1135 1136 1137

		/* cb error is converted to GIT_EUSER by git_config_foreach */
		if (error == GIT_EUSER)
			error = -1;

1138 1139 1140
		return error;
	}

1141 1142
	git_vector_uniq(&list, git__free);

1143 1144 1145
	remotes_list->strings = (char **)list.contents;
	remotes_list->count = list.length;

1146
	return 0;
1147
}
1148

1149 1150 1151 1152 1153 1154
void git_remote_check_cert(git_remote *remote, int check)
{
	assert(remote);

	remote->check_cert = check;
}
1155

1156
int git_remote_set_callbacks(git_remote *remote, git_remote_callbacks *callbacks)
1157 1158 1159
{
	assert(remote && callbacks);

Ben Straub committed
1160
	GITERR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
1161

1162
	memcpy(&remote->callbacks, callbacks, sizeof(git_remote_callbacks));
1163

1164 1165 1166 1167
	if (remote->transport && remote->transport->set_callbacks)
		remote->transport->set_callbacks(remote->transport,
			remote->callbacks.progress,
			NULL,
Ben Straub committed
1168
			remote->callbacks.payload);
1169 1170

	return 0;
1171 1172 1173 1174 1175 1176
}

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

Ben Straub committed
1177
	GITERR_CHECK_VERSION(transport, GIT_TRANSPORT_VERSION, "git_transport");
1178

1179
	if (remote->transport) {
1180 1181
		giterr_set(GITERR_NET, "A transport is already bound to this remote");
		return -1;
1182
	}
1183 1184 1185

	remote->transport = transport;
	return 0;
1186
}
1187

Ben Straub committed
1188
const git_transfer_progress* git_remote_stats(git_remote *remote)
1189 1190 1191 1192 1193
{
	assert(remote);
	return &remote->stats;
}

Ben Straub committed
1194
git_remote_autotag_option_t git_remote_autotag(git_remote *remote)
1195 1196 1197 1198
{
	return remote->download_tags;
}

Ben Straub committed
1199
void git_remote_set_autotag(git_remote *remote, git_remote_autotag_option_t value)
1200 1201 1202
{
	remote->download_tags = value;
}
1203 1204 1205 1206 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 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274

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

struct update_data
{
	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;

	return git_config_set_string(
		data->config,
		entry->name,
		data->new_remote_name);
}

static int update_branch_remote_config_entry(
	git_repository *repo,
	const char *old_name,
	const char *new_name)
{
	git_config *config;
	struct update_data data;

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

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

	return git_config_foreach_match(
		config,
		"branch\\..+\\.remote",
		update_config_entries_cb, &data);
}

static int rename_one_remote_reference(
Vicent Marti committed
1275
	git_reference *reference,
1276 1277 1278
	const char *old_remote_name,
	const char *new_remote_name)
{
1279
	int error = -1;
1280 1281 1282 1283 1284 1285
	git_buf new_name = GIT_BUF_INIT;

	if (git_buf_printf(
		&new_name,
		GIT_REFS_REMOTES_DIR "%s%s",
		new_remote_name,
Vicent Marti committed
1286
		reference->name + strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name)) < 0)
1287 1288
			return -1;

Vicent Marti committed
1289
	error = git_reference_rename(NULL, reference, git_buf_cstr(&new_name), 0);
1290
	git_reference_free(reference);
1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301

	git_buf_free(&new_name);
	return error;
}

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

1305
	if (git_reference_iterator_new(&iter, repo) < 0)
Vicent Marti committed
1306
		return -1;
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

Vicent Marti committed
1314 1315 1316 1317
		if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0) {
			git_reference_iterator_free(iter);
			return error;
		}
1318 1319 1320
	}

	git_reference_iterator_free(iter);
1321

Vicent Marti committed
1322 1323
	if (error == GIT_ITEROVER)
		return 0;
1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334

	return error;
}

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;
1335
	git_buf base = GIT_BUF_INIT, var = GIT_BUF_INIT, val = GIT_BUF_INIT;
1336
	const git_refspec *spec;
1337
	size_t i;
1338 1339
	int error = -1;

1340 1341
	if (git_buf_printf(&base, "+refs/heads/*:refs/remotes/%s/*", remote->name) < 0)
		goto cleanup;
1342

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

1347 1348
		/* Every refspec is a problem refspec for an in-memory remote */
		if (!remote->name) {
1349
			if (callback(spec->string, payload) < 0) {
1350 1351 1352
				error = GIT_EUSER;
				goto cleanup;
			}
1353

1354 1355
			continue;
		}
1356

1357
		/* Does the dst part of the refspec follow the extected standard format? */
1358 1359
		if (strcmp(git_buf_cstr(&base), spec->string)) {
			if (callback(spec->string, payload) < 0) {
1360 1361 1362
				error = GIT_EUSER;
				goto cleanup;
			}
1363

1364 1365
			continue;
		}
1366

1367 1368
		/* If we do want to move it to the new section */
		if (git_buf_printf(&val, "+refs/heads/*:refs/remotes/%s/*", new_name) < 0)
1369 1370
			goto cleanup;

1371 1372
		if (git_buf_printf(&var, "remote.%s.fetch", new_name) < 0)
			goto cleanup;
1373

1374 1375
		if (git_repository_config__weakptr(&config, remote->repo) < 0)
			goto cleanup;
1376

1377 1378 1379
		if (git_config_set_string(config, git_buf_cstr(&var), git_buf_cstr(&val)) < 0)
			goto cleanup;
	}
1380

1381
	error = 0;
1382 1383

cleanup:
1384 1385 1386
	git_buf_free(&base);
	git_buf_free(&var);
	git_buf_free(&val);
1387 1388 1389 1390 1391 1392
	return error;
}

int git_remote_rename(
	git_remote *remote,
	const char *new_name,
Ben Straub committed
1393
	git_remote_rename_problem_cb callback,
1394 1395 1396 1397 1398 1399
	void *payload)
{
	int error;

	assert(remote && new_name);

1400
	if (!remote->name) {
1401 1402 1403 1404
		giterr_set(GITERR_INVALID, "Can't rename an in-memory remote.");
		return GIT_EINVALIDSPEC;
	}

1405 1406 1407
	if ((error = ensure_remote_name_is_valid(new_name)) < 0)
		return error;

1408 1409
	if (remote->repo) {
		if ((error = ensure_remote_doesnot_exist(remote->repo, new_name)) < 0)
1410 1411
			return error;

1412 1413 1414 1415 1416 1417 1418
		if (!remote->name) {
			if ((error = rename_fetch_refspecs(
				remote,
				new_name,
				callback,
				payload)) < 0)
				return error;
1419

1420
			remote->name = git__strdup(new_name);
1421

1422
			if (!remote->name) return 0;
1423 1424
			return git_remote_save(remote);
		}
1425

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

1432 1433 1434 1435 1436
		if ((error = update_branch_remote_config_entry(
			remote->repo,
			remote->name,
			new_name)) < 0)
				return error;
1437

1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450
		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)
			return error;
	}
1451 1452 1453 1454 1455 1456

	git__free(remote->name);
	remote->name = git__strdup(new_name);

	return 0;
}
1457 1458 1459 1460 1461 1462 1463 1464 1465 1466

int git_remote_update_fetchhead(git_remote *remote)
{
	return remote->update_fetchhead;
}

void git_remote_set_update_fetchhead(git_remote *remote, int value)
{
	remote->update_fetchhead = value;
}
1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486

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;
}
1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526

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

	git_vector_foreach(&remote->refspecs, i, spec) {
		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;

	git_vector_foreach(&remote->refspecs, i, spec) {
		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);
1527
		git__free(spec);
1528 1529 1530 1531
	}
	git_vector_clear(&remote->refspecs);
}

1532
int git_remote_add_fetch(git_remote *remote, const char *refspec)
1533 1534 1535 1536
{
	return add_refspec(remote, refspec, true);
}

1537
int git_remote_add_push(git_remote *remote, const char *refspec)
1538 1539 1540
{
	return add_refspec(remote, refspec, false);
}
1541

Linquize committed
1542
static int copy_refspecs(git_strarray *array, git_remote *remote, unsigned int push)
1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555
{
	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;

1556
		if ((dup = git__strdup(spec->string)) == NULL)
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
			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:
	git_vector_foreach(&refspecs, i, dup)
		git__free(dup);
	git_vector_free(&refspecs);

	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);
}
1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611

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

int git_remote_remove_refspec(git_remote *remote, size_t n)
{
	git_refspec *spec;

	assert(remote);

	spec = git_vector_get(&remote->refspecs, n);
	if (spec) {
		git_refspec__free(spec);
		git__free(spec);
	}

	return git_vector_remove(&remote->refspecs, n);
}