branch.c 15.6 KB
Newer Older
1
/*
Edward Thomson committed
2
 * Copyright (C) the libgit2 contributors. All rights reserved.
3 4 5 6 7 8 9 10
 *
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
 */

#include "common.h"
#include "commit.h"
#include "tag.h"
11 12
#include "config.h"
#include "refspec.h"
13
#include "refs.h"
14
#include "remote.h"
15
#include "annotated_commit.h"
16

17 18
#include "git2/branch.h"

19 20 21 22 23 24
static int retrieve_branch_reference(
	git_reference **branch_reference_out,
	git_repository *repo,
	const char *branch_name,
	int is_remote)
{
Russell Belfer committed
25 26
	git_reference *branch = NULL;
	int error = 0;
27 28 29 30 31
	char *prefix;
	git_buf ref_name = GIT_BUF_INIT;

	prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR;

Russell Belfer committed
32 33 34 35 36 37
	if ((error = git_buf_joinpath(&ref_name, prefix, branch_name)) < 0)
		/* OOM */;
	else if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0)
		giterr_set(
			GITERR_REFERENCE, "Cannot locate %s branch '%s'",
			is_remote ? "remote-tracking" : "local", branch_name);
38

Russell Belfer committed
39
	*branch_reference_out = branch; /* will be NULL on error */
40 41 42 43 44

	git_buf_free(&ref_name);
	return error;
}

45
static int not_a_local_branch(const char *reference_name)
46
{
47 48 49
	giterr_set(
		GITERR_INVALID,
		"Reference '%s' is not a local branch.", reference_name);
50 51 52
	return -1;
}

53
static int create_branch(
54 55 56 57
	git_reference **ref_out,
	git_repository *repository,
	const char *branch_name,
	const git_commit *commit,
58
	const char *from,
59
	int force)
60
{
61
	int is_head = 0;
62
	git_reference *branch = NULL;
63
	git_buf canonical_branch_name = GIT_BUF_INIT,
64
			  log_message = GIT_BUF_INIT;
65
	int error = -1;
66

Vicent Marti committed
67 68
	assert(branch_name && commit && ref_out);
	assert(git_object_owner((const git_object *)commit) == repository);
69 70 71 72 73 74 75

	if (force && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) {
		error = git_branch_is_head(branch);
		git_reference_free(branch);
		branch = NULL;

		if (error < 0)
76
			goto cleanup;
77 78

		is_head = error;
79
	}
80

81 82
	if (is_head && force) {
		giterr_set(GITERR_REFERENCE, "Cannot force update branch '%s' as it is "
83 84
			"the current HEAD of the repository.", branch_name);
		error = -1;
85 86
		goto cleanup;
	}
nulltoken committed
87

88 89
	if (git_buf_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)
		goto cleanup;
90

91
	if (git_buf_printf(&log_message, "branch: Created from %s", from) < 0)
92 93 94
		goto cleanup;

	error = git_reference_create(&branch, repository,
95
		git_buf_cstr(&canonical_branch_name), git_commit_id(commit), force,
96
		git_buf_cstr(&log_message));
97 98

	if (!error)
99
		*ref_out = branch;
100

101
cleanup:
102
	git_buf_free(&canonical_branch_name);
103
	git_buf_free(&log_message);
104 105 106
	return error;
}

107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
int git_branch_create(
	git_reference **ref_out,
	git_repository *repository,
	const char *branch_name,
	const git_commit *commit,
	int force)
{
	return create_branch(ref_out, repository, branch_name, commit, git_oid_tostr_s(git_commit_id(commit)), force);
}

int git_branch_create_from_annotated(
	git_reference **ref_out,
	git_repository *repository,
	const char *branch_name,
	const git_annotated_commit *commit,
	int force)
{
	return create_branch(ref_out, repository, branch_name, commit->commit, commit->ref_name, force);
}

127
int git_branch_delete(git_reference *branch)
128
{
129
	int is_head;
130 131
	git_buf config_section = GIT_BUF_INIT;
	int error = -1;
132

133
	assert(branch);
134

135 136 137 138
	if (!git_reference_is_branch(branch) && !git_reference_is_remote(branch)) {
		giterr_set(GITERR_INVALID, "Reference '%s' is not a valid branch.",
			git_reference_name(branch));
		return GIT_ENOTFOUND;
139
	}
140

141 142
	if ((is_head = git_branch_is_head(branch)) < 0)
		return is_head;
143

144
	if (is_head) {
145 146
		giterr_set(GITERR_REFERENCE, "Cannot delete branch '%s' as it is "
			"the current HEAD of the repository.", git_reference_name(branch));
147
		return -1;
148 149
	}

150 151
	if (git_buf_join(&config_section, '.', "branch",
			git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0)
152 153 154
		goto on_error;

	if (git_config_rename_section(
155 156
		git_reference_owner(branch), git_buf_cstr(&config_section), NULL) < 0)
		goto on_error;
157

158 159 160
	if (git_reference_delete(branch) < 0)
		goto on_error;

161
	if ((error = git_reflog_delete(git_reference_owner(branch), git_reference_name(branch))) < 0) {
162 163 164 165
		if (error == GIT_ENOTFOUND) {
			giterr_clear();
			error = 0;
		}
166
		goto on_error;
167
	}
168

169 170 171 172 173
	error = 0;

on_error:
	git_buf_free(&config_section);
	return error;
174 175
}

176
typedef struct {
177
	git_reference_iterator *iter;
178 179 180
	unsigned int flags;
} branch_iter;

181
int git_branch_next(git_reference **out, git_branch_t *out_type, git_branch_iterator *_iter)
182 183
{
	branch_iter *iter = (branch_iter *) _iter;
Vicent Marti committed
184
	git_reference *ref;
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
	int error;

	while ((error = git_reference_next(&ref, iter->iter)) == 0) {
		if ((iter->flags & GIT_BRANCH_LOCAL) &&
		    !git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR)) {
			*out = ref;
			*out_type = GIT_BRANCH_LOCAL;

			return 0;
		} else  if ((iter->flags & GIT_BRANCH_REMOTE) &&
			    !git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) {
			*out = ref;
			*out_type = GIT_BRANCH_REMOTE;

			return 0;
		} else {
			git_reference_free(ref);
		}
	}
204

205 206
	return error;
}
207

208 209 210
int git_branch_iterator_new(
	git_branch_iterator **out,
	git_repository *repo,
211
	git_branch_t list_flags)
212 213
{
	branch_iter *iter;
214

215 216
	iter = git__calloc(1, sizeof(branch_iter));
	GITERR_CHECK_ALLOC(iter);
Vicent Marti committed
217

218
	iter->flags = list_flags;
219

220 221 222
	if (git_reference_iterator_new(&iter->iter, repo) < 0) {
		git__free(iter);
		return -1;
223 224
	}

225
	*out = (git_branch_iterator *) iter;
226

227 228 229 230 231 232 233
	return 0;
}

void git_branch_iterator_free(git_branch_iterator *_iter)
{
	branch_iter *iter = (branch_iter *) _iter;

234 235 236
	if (iter == NULL)
		return;

237 238
	git_reference_iterator_free(iter->iter);
	git__free(iter);
239 240
}

241
int git_branch_move(
242
	git_reference **out,
243 244
	git_reference *branch,
	const char *new_branch_name,
245
	int force)
246
{
247
	git_buf new_reference_name = GIT_BUF_INIT,
248 249
	        old_config_section = GIT_BUF_INIT,
	        new_config_section = GIT_BUF_INIT,
250
	        log_message = GIT_BUF_INIT;
251
	int error;
252

253 254 255
	assert(branch && new_branch_name);

	if (!git_reference_is_branch(branch))
256
		return not_a_local_branch(git_reference_name(branch));
257

258
	if ((error = git_buf_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)
259
		goto done;
260

261
	if ((error = git_buf_printf(&log_message, "branch: renamed %s to %s",
262
				    git_reference_name(branch), git_buf_cstr(&new_reference_name))) < 0)
263 264
			goto done;

265
	/* first update ref then config so failure won't trash config */
Vicent Marti committed
266

267
	error = git_reference_rename(
268
		out, branch, git_buf_cstr(&new_reference_name), force,
269
		git_buf_cstr(&log_message));
270
	if (error < 0)
271
		goto done;
272

273 274 275 276 277 278 279 280
	git_buf_join(&old_config_section, '.', "branch",
		git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR));
	git_buf_join(&new_config_section, '.', "branch", new_branch_name);

	error = git_config_rename_section(
		git_reference_owner(branch),
		git_buf_cstr(&old_config_section),
		git_buf_cstr(&new_config_section));
281

282
done:
283
	git_buf_free(&new_reference_name);
284 285
	git_buf_free(&old_config_section);
	git_buf_free(&new_config_section);
286
	git_buf_free(&log_message);
287

288
	return error;
289
}
290 291

int git_branch_lookup(
292 293 294 295
	git_reference **ref_out,
	git_repository *repo,
	const char *branch_name,
	git_branch_t branch_type)
296 297 298 299 300
{
	assert(ref_out && repo && branch_name);

	return retrieve_branch_reference(ref_out, repo, branch_name, branch_type == GIT_BRANCH_REMOTE);
}
301

Jacques Germishuys committed
302 303 304
int git_branch_name(
	const char **out,
	const git_reference *ref)
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
{
	const char *branch_name;

	assert(out && ref);

	branch_name = ref->name;

	if (git_reference_is_branch(ref)) {
		branch_name += strlen(GIT_REFS_HEADS_DIR);
	} else if (git_reference_is_remote(ref)) {
		branch_name += strlen(GIT_REFS_REMOTES_DIR);
	} else {
		giterr_set(GITERR_INVALID,
				"Reference '%s' is neither a local nor a remote branch.", ref->name);
		return -1;
	}
	*out = branch_name;
	return 0;
}

325
static int retrieve_upstream_configuration(
326
	git_buf *out,
327
	const git_config *config,
328 329
	const char *canonical_branch_name,
	const char *format)
330 331 332 333 334
{
	git_buf buf = GIT_BUF_INIT;
	int error;

	if (git_buf_printf(&buf, format,
335
		canonical_branch_name + strlen(GIT_REFS_HEADS_DIR)) < 0)
336 337
			return -1;

338
	error = git_config_get_string_buf(out, config, git_buf_cstr(&buf));
339 340 341 342
	git_buf_free(&buf);
	return error;
}

343 344
int git_branch_upstream_name(
	git_buf *out,
345
	git_repository *repo,
346
	const char *refname)
347
{
348 349
	git_buf remote_name = GIT_BUF_INIT;
	git_buf merge_name = GIT_BUF_INIT;
350 351 352 353
	git_buf buf = GIT_BUF_INIT;
	int error = -1;
	git_remote *remote = NULL;
	const git_refspec *refspec;
354
	git_config *config;
355

356 357 358
	assert(out && refname);

	git_buf_sanitize(out);
359

360 361
	if (!git_reference__is_branch(refname))
		return not_a_local_branch(refname);
362

363
	if ((error = git_repository_config_snapshot(&config, repo)) < 0)
364 365
		return error;

366
	if ((error = retrieve_upstream_configuration(
367
		&remote_name, config, refname, "branch.%s.remote")) < 0)
368
			goto cleanup;
369

370
	if ((error = retrieve_upstream_configuration(
371
		&merge_name, config, refname, "branch.%s.merge")) < 0)
372
			goto cleanup;
373

374
	if (git_buf_len(&remote_name) == 0 || git_buf_len(&merge_name) == 0) {
375
		giterr_set(GITERR_REFERENCE,
376
			"branch '%s' does not have an upstream", refname);
nulltoken committed
377 378 379
		error = GIT_ENOTFOUND;
		goto cleanup;
	}
380

381 382
	if (strcmp(".", git_buf_cstr(&remote_name)) != 0) {
		if ((error = git_remote_lookup(&remote, repo, git_buf_cstr(&remote_name))) < 0)
383 384
			goto cleanup;

385
		refspec = git_remote__matching_refspec(remote, git_buf_cstr(&merge_name));
386 387 388
		if (!refspec) {
			error = GIT_ENOTFOUND;
			goto cleanup;
389 390
		}

391
		if (git_refspec_transform(&buf, refspec, git_buf_cstr(&merge_name)) < 0)
392 393
			goto cleanup;
	} else
394
		if (git_buf_set(&buf, git_buf_cstr(&merge_name), git_buf_len(&merge_name)) < 0)
395 396
			goto cleanup;

397
	error = git_buf_set(out, git_buf_cstr(&buf), git_buf_len(&buf));
398 399

cleanup:
400
	git_config_free(config);
401
	git_remote_free(remote);
402 403
	git_buf_free(&remote_name);
	git_buf_free(&merge_name);
404 405 406
	git_buf_free(&buf);
	return error;
}
407

408 409 410 411 412 413 414 415
int git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname)
{
	int error;
	git_config *cfg;

	if (!git_reference__is_branch(refname))
		return not_a_local_branch(refname);

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

419
	git_buf_sanitize(buf);
420

421 422 423 424
	if ((error = retrieve_upstream_configuration(buf, cfg, refname, "branch.%s.remote")) < 0)
		return error;

	if (git_buf_len(buf) == 0) {
425 426
		giterr_set(GITERR_REFERENCE, "branch '%s' does not have an upstream remote", refname);
		error = GIT_ENOTFOUND;
427
		git_buf_clear(buf);
428 429
	}

430 431 432
	return error;
}

433
int git_branch_remote_name(git_buf *buf, git_repository *repo, const char *refname)
434 435
{
	git_strarray remote_list = {0};
436
	size_t i;
437 438 439 440 441
	git_remote *remote;
	const git_refspec *fetchspec;
	int error = 0;
	char *remote_name = NULL;

442 443 444
	assert(buf && repo && refname);

	git_buf_sanitize(buf);
445 446

	/* Verify that this is a remote branch */
447
	if (!git_reference__is_remote(refname)) {
448
		giterr_set(GITERR_INVALID, "Reference '%s' is not a remote branch.",
449
			refname);
450 451 452 453 454 455 456 457 458 459
		error = GIT_ERROR;
		goto cleanup;
	}

	/* Get the remotes */
	if ((error = git_remote_list(&remote_list, repo)) < 0)
		goto cleanup;

	/* Find matching remotes */
	for (i = 0; i < remote_list.count; i++) {
460
		if ((error = git_remote_lookup(&remote, repo, remote_list.strings[i])) < 0)
461
			continue;
462

463
		fetchspec = git_remote__matching_dst_refspec(remote, refname);
464
		if (fetchspec) {
465 466 467 468 469 470 471 472
			/* If we have not already set out yet, then set
			 * it to the matching remote name. Otherwise
			 * multiple remotes match this reference, and it
			 * is ambiguous. */
			if (!remote_name) {
				remote_name = remote_list.strings[i];
			} else {
				git_remote_free(remote);
473 474

				giterr_set(GITERR_REFERENCE,
475
					"Reference '%s' is ambiguous", refname);
476 477 478 479 480 481 482 483 484
				error = GIT_EAMBIGUOUS;
				goto cleanup;
			}
		}

		git_remote_free(remote);
	}

	if (remote_name) {
485 486
		git_buf_clear(buf);
		error = git_buf_puts(buf, remote_name);
487
	} else {
488
		giterr_set(GITERR_REFERENCE,
489
			"Could not determine remote for '%s'", refname);
490 491 492 493
		error = GIT_ENOTFOUND;
	}

cleanup:
494 495 496
	if (error < 0)
		git_buf_free(buf);

497 498 499 500
	git_strarray_free(&remote_list);
	return error;
}

501
int git_branch_upstream(
Jacques Germishuys committed
502 503
	git_reference **tracking_out,
	const git_reference *branch)
504 505 506 507
{
	int error;
	git_buf tracking_name = GIT_BUF_INIT;

508
	if ((error = git_branch_upstream_name(&tracking_name,
509 510 511 512 513 514 515 516 517 518 519 520
		git_reference_owner(branch), git_reference_name(branch))) < 0)
			return error;

	error = git_reference_lookup(
		tracking_out,
		git_reference_owner(branch),
		git_buf_cstr(&tracking_name));

	git_buf_free(&tracking_name);
	return error;
}

521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
static int unset_upstream(git_config *config, const char *shortname)
{
	git_buf buf = GIT_BUF_INIT;

	if (git_buf_printf(&buf, "branch.%s.remote", shortname) < 0)
		return -1;

	if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
		goto on_error;

	git_buf_clear(&buf);
	if (git_buf_printf(&buf, "branch.%s.merge", shortname) < 0)
		goto on_error;

	if (git_config_delete_entry(config, git_buf_cstr(&buf)) < 0)
		goto on_error;

	git_buf_free(&buf);
	return 0;

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

int git_branch_set_upstream(git_reference *branch, const char *upstream_name)
{
	git_buf key = GIT_BUF_INIT, value = GIT_BUF_INIT;
	git_reference *upstream;
	git_repository *repo;
	git_remote *remote = NULL;
	git_config *config;
	const char *name, *shortname;
	int local;
	const git_refspec *fetchspec;

	name = git_reference_name(branch);
	if (!git_reference__is_branch(name))
		return not_a_local_branch(name);

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

	shortname = name + strlen(GIT_REFS_HEADS_DIR);

	if (upstream_name == NULL)
		return unset_upstream(config, shortname);

	repo = git_reference_owner(branch);

	/* First we need to figure out whether it's a branch or remote-tracking */
	if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_LOCAL) == 0)
		local = 1;
	else if (git_branch_lookup(&upstream, repo, upstream_name, GIT_BRANCH_REMOTE) == 0)
		local = 0;
576 577 578
	else {
		giterr_set(GITERR_REFERENCE,
			"Cannot set upstream for branch '%s'", shortname);
579
		return GIT_ENOTFOUND;
580
	}
581 582 583 584 585 586 587 588 589 590

	/*
	 * If it's local, the remote is "." and the branch name is
	 * simply the refname. Otherwise we need to figure out what
	 * the remote-tracking branch's name on the remote is and use
	 * that.
	 */
	if (local)
		git_buf_puts(&value, ".");
	else
591
		git_branch_remote_name(&value, repo, git_reference_name(upstream));
592 593 594 595 596 597 598 599

	if (git_buf_printf(&key, "branch.%s.remote", shortname) < 0)
		goto on_error;

	if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0)
		goto on_error;

	if (local) {
600 601
		git_buf_clear(&value);
		if (git_buf_puts(&value, git_reference_name(upstream)) < 0)
602 603 604
			goto on_error;
	} else {
		/* Get the remoe-tracking branch's refname in its repo */
605
		if (git_remote_lookup(&remote, repo, git_buf_cstr(&value)) < 0)
606 607
			goto on_error;

608
		fetchspec = git_remote__matching_dst_refspec(remote, git_reference_name(upstream));
609
		git_buf_clear(&value);
610
		if (!fetchspec || git_refspec_rtransform(&value, fetchspec, git_reference_name(upstream)) < 0)
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
			goto on_error;

		git_remote_free(remote);
		remote = NULL;
	}

	git_buf_clear(&key);
	if (git_buf_printf(&key, "branch.%s.merge", shortname) < 0)
		goto on_error;

	if (git_config_set_string(config, git_buf_cstr(&key), git_buf_cstr(&value)) < 0)
		goto on_error;

	git_reference_free(upstream);
	git_buf_free(&key);
	git_buf_free(&value);

	return 0;

on_error:
	git_reference_free(upstream);
	git_buf_free(&key);
	git_buf_free(&value);
	git_remote_free(remote);

	return -1;
}

639
int git_branch_is_head(
640
		const git_reference *branch)
641 642 643
{
	git_reference *head;
	bool is_same = false;
644
	int error;
645 646 647 648 649 650

	assert(branch);

	if (!git_reference_is_branch(branch))
		return false;

651 652
	error = git_repository_head(&head, git_reference_owner(branch));

653
	if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND)
654 655 656
		return false;

	if (error < 0)
657 658 659 660 661 662 663 664 665 666
		return -1;

	is_same = strcmp(
		git_reference_name(branch),
		git_reference_name(head)) == 0;

	git_reference_free(head);

	return is_same;
}