refs.c 28.3 KB
Newer Older
1
/*
Edward Thomson committed
2
 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
6 7 8 9 10 11
 */

#include "refs.h"
#include "hash.h"
#include "repository.h"
#include "fileops.h"
12
#include "filebuf.h"
13
#include "pack.h"
14
#include "reflog.h"
15
#include "refdb.h"
16

Vicent Marti committed
17 18
#include <git2/tag.h>
#include <git2/object.h>
19
#include <git2/oid.h>
20
#include <git2/branch.h>
21 22
#include <git2/refs.h>
#include <git2/refdb.h>
23
#include <git2/sys/refs.h>
24
#include <git2/signature.h>
25
#include <git2/commit.h>
Vicent Marti committed
26

27
GIT__USE_STRMAP
28

29 30
#define DEFAULT_NESTING_LEVEL	5
#define MAX_NESTING_LEVEL		10
31

32 33 34 35
enum {
	GIT_PACKREF_HAS_PEEL = 1,
	GIT_PACKREF_WAS_LOOSE = 2
};
36

37
static git_reference *alloc_ref(const char *name)
38
{
39 40
	git_reference *ref = NULL;
	size_t namelen = strlen(name), reflen;
41

42 43 44 45
	if (!GIT_ADD_SIZET_OVERFLOW(&reflen, sizeof(git_reference), namelen) &&
		!GIT_ADD_SIZET_OVERFLOW(&reflen, reflen, 1) &&
		(ref = git__calloc(1, reflen)) != NULL)
		memcpy(ref->name, name, namelen + 1);
46 47 48 49 50

	return ref;
}

git_reference *git_reference__alloc_symbolic(
51
	const char *name, const char *target)
52
{
53
	git_reference *ref;
54

55
	assert(name && target);
56

57
	ref = alloc_ref(name);
58
	if (!ref)
59
		return NULL;
Vicent Marti committed
60

61
	ref->type = GIT_REF_SYMBOLIC;
Vicent Marti committed
62

63 64 65
	if ((ref->target.symbolic = git__strdup(target)) == NULL) {
		git__free(ref);
		return NULL;
66
	}
67

68 69 70 71 72 73 74 75 76 77
	return ref;
}

git_reference *git_reference__alloc(
	const char *name,
	const git_oid *oid,
	const git_oid *peel)
{
	git_reference *ref;

78
	assert(name && oid);
79

80
	ref = alloc_ref(name);
81 82 83 84
	if (!ref)
		return NULL;

	ref->type = GIT_REF_OID;
85
	git_oid_cpy(&ref->target.oid, oid);
86 87

	if (peel != NULL)
88
		git_oid_cpy(&ref->peel, peel);
89

90
	return ref;
91
}
92

93 94 95 96
git_reference *git_reference__set_name(
	git_reference *ref, const char *name)
{
	size_t namelen = strlen(name);
97
	size_t reflen;
98 99
	git_reference *rewrite = NULL;

100 101 102
	if (!GIT_ADD_SIZET_OVERFLOW(&reflen, sizeof(git_reference), namelen) &&
		!GIT_ADD_SIZET_OVERFLOW(&reflen, reflen, 1) &&
		(rewrite = git__realloc(ref, reflen)) != NULL)
103
		memcpy(rewrite->name, name, namelen + 1);
104

105 106 107
	return rewrite;
}

108 109 110 111 112 113 114 115 116 117 118 119
int git_reference_dup(git_reference **dest, git_reference *source)
{
	if (source->type == GIT_REF_SYMBOLIC)
		*dest = git_reference__alloc_symbolic(source->name, source->target.symbolic);
	else
		*dest = git_reference__alloc(source->name, &source->target.oid, &source->peel);

	GITERR_CHECK_ALLOC(*dest);

	return 0;
}

120
void git_reference_free(git_reference *reference)
121
{
122 123
	if (reference == NULL)
		return;
124

125
	if (reference->type == GIT_REF_SYMBOLIC)
126
		git__free(reference->target.symbolic);
127

Vicent Marti committed
128 129
	if (reference->db)
		GIT_REFCOUNT_DEC(reference->db, git_refdb__free);
130 131

	git__free(reference);
132
}
133

134
int git_reference_delete(git_reference *ref)
135
{
136 137 138 139 140 141 142 143 144
	const git_oid *old_id = NULL;
	const char *old_target = NULL;

	if (ref->type == GIT_REF_OID)
		old_id = &ref->target.oid;
	else
		old_target = ref->target.symbolic;

	return git_refdb_delete(ref->db, ref->name, old_id, old_target);
145 146
}

147 148 149 150 151 152 153 154 155 156 157
int git_reference_remove(git_repository *repo, const char *name)
{
	git_refdb *db;
	int error;

	if ((error = git_repository_refdb__weakptr(&db, repo)) < 0)
		return error;

	return git_refdb_delete(db, name, NULL, NULL);
}

158
int git_reference_lookup(git_reference **ref_out,
159
	git_repository *repo, const char *name)
160
{
161 162 163
	return git_reference_lookup_resolved(ref_out, repo, name, 0);
}

164
int git_reference_name_to_id(
165 166 167 168 169 170 171 172
	git_oid *out, git_repository *repo, const char *name)
{
	int error;
	git_reference *ref;

	if ((error = git_reference_lookup_resolved(&ref, repo, name, -1)) < 0)
		return error;

173
	git_oid_cpy(out, git_reference_target(ref));
174 175 176 177
	git_reference_free(ref);
	return 0;
}

178
static int reference_normalize_for_repo(
179
	git_refname_t out,
180 181 182 183 184 185 186 187 188 189
	git_repository *repo,
	const char *name)
{
	int precompose;
	unsigned int flags = GIT_REF_FORMAT_ALLOW_ONELEVEL;

	if (!git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) &&
		precompose)
		flags |= GIT_REF_FORMAT__PRECOMPOSE_UNICODE;

190
	return git_reference_normalize_name(out, GIT_REFNAME_MAX, name, flags);
191 192
}

193 194 195 196 197 198
int git_reference_lookup_resolved(
	git_reference **ref_out,
	git_repository *repo,
	const char *name,
	int max_nesting)
{
199
	git_refname_t scan_name;
200 201 202 203
	git_ref_t scan_type;
	int error = 0, nesting;
	git_reference *ref = NULL;
	git_refdb *refdb;
204 205

	assert(ref_out && repo && name);
206

207
	*ref_out = NULL;
208

209 210 211 212
	if (max_nesting > MAX_NESTING_LEVEL)
		max_nesting = MAX_NESTING_LEVEL;
	else if (max_nesting < 0)
		max_nesting = DEFAULT_NESTING_LEVEL;
213

214
	scan_type = GIT_REF_SYMBOLIC;
215

216
	if ((error = reference_normalize_for_repo(scan_name, repo, name)) < 0)
217
		return error;
218

219
	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
220
		return error;
221 222

	for (nesting = max_nesting;
223
		 nesting >= 0 && scan_type == GIT_REF_SYMBOLIC;
224 225
		 nesting--)
	{
226
		if (nesting != max_nesting) {
227
			strncpy(scan_name, ref->target.symbolic, sizeof(scan_name));
228 229
			git_reference_free(ref);
		}
230

231 232
		if ((error = git_refdb_lookup(&ref, refdb, scan_name)) < 0)
			return error;
233

234
		scan_type = ref->type;
235 236
	}

237
	if (scan_type != GIT_REF_OID && max_nesting != 0) {
238 239
		giterr_set(GITERR_REFERENCE,
			"Cannot resolve reference (>%u levels deep)", max_nesting);
240
		git_reference_free(ref);
241 242 243
		return -1;
	}

244
	*ref_out = ref;
245
	return 0;
246 247
}

248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303
int git_reference_dwim(git_reference **out, git_repository *repo, const char *refname)
{
	int error = 0, i;
	bool fallbackmode = true, foundvalid = false;
	git_reference *ref;
	git_buf refnamebuf = GIT_BUF_INIT, name = GIT_BUF_INIT;

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

	if (*refname)
		git_buf_puts(&name, refname);
	else {
		git_buf_puts(&name, GIT_HEAD_FILE);
		fallbackmode = false;
	}

	for (i = 0; formatters[i] && (fallbackmode || i == 0); i++) {

		git_buf_clear(&refnamebuf);

		if ((error = git_buf_printf(&refnamebuf, formatters[i], git_buf_cstr(&name))) < 0)
			goto cleanup;

		if (!git_reference_is_valid_name(git_buf_cstr(&refnamebuf))) {
			error = GIT_EINVALIDSPEC;
			continue;
		}
		foundvalid = true;

		error = git_reference_lookup_resolved(&ref, repo, git_buf_cstr(&refnamebuf), -1);

		if (!error) {
			*out = ref;
			error = 0;
			goto cleanup;
		}

		if (error != GIT_ENOTFOUND)
			goto cleanup;
	}

cleanup:
	if (error && !foundvalid) {
		/* never found a valid reference name */
		giterr_set(GITERR_REFERENCE,
			"Could not use '%s' as valid reference name", git_buf_cstr(&name));
	}

304 305 306
	if (error == GIT_ENOTFOUND)
		giterr_set(GITERR_REFERENCE, "no reference found for shorthand '%s'", refname);

307 308 309 310 311
	git_buf_free(&name);
	git_buf_free(&refnamebuf);
	return error;
}

312 313 314
/**
 * Getters
 */
315
git_ref_t git_reference_type(const git_reference *ref)
Vicent Marti committed
316 317
{
	assert(ref);
318
	return ref->type;
Vicent Marti committed
319 320
}

321
const char *git_reference_name(const git_reference *ref)
Vicent Marti committed
322 323
{
	assert(ref);
324
	return ref->name;
Vicent Marti committed
325 326
}

327
git_repository *git_reference_owner(const git_reference *ref)
328
{
329
	assert(ref);
330
	return ref->db->repo;
331 332
}

333
const git_oid *git_reference_target(const git_reference *ref)
Vicent Marti committed
334 335 336
{
	assert(ref);

337
	if (ref->type != GIT_REF_OID)
Vicent Marti committed
338 339
		return NULL;

340
	return &ref->target.oid;
341 342 343 344 345 346
}

const git_oid *git_reference_target_peel(const git_reference *ref)
{
	assert(ref);

347
	if (ref->type != GIT_REF_OID || git_oid_iszero(&ref->peel))
348 349
		return NULL;

350
	return &ref->peel;
Vicent Marti committed
351 352
}

353
const char *git_reference_symbolic_target(const git_reference *ref)
354
{
355
	assert(ref);
356

357
	if (ref->type != GIT_REF_SYMBOLIC)
358 359
		return NULL;

360
	return ref->target.symbolic;
361 362
}

363
static int reference__create(
364 365 366
	git_reference **ref_out,
	git_repository *repo,
	const char *name,
367 368
	const git_oid *oid,
	const char *symbolic,
369 370
	int force,
	const git_signature *signature,
371
	const char *log_message,
372 373
	const git_oid *old_id,
	const char *old_target)
374
{
375
	git_refname_t normalized;
376
	git_refdb *refdb;
377
	git_reference *ref = NULL;
378
	int error = 0;
379

380
	assert(repo && name);
381
	assert(symbolic || signature);
382

383 384 385
	if (ref_out)
		*ref_out = NULL;

386
	error = reference_normalize_for_repo(normalized, repo, name);
Vicent Marti committed
387 388 389 390 391
	if (error < 0)
		return error;

	error = git_repository_refdb__weakptr(&refdb, repo);
	if (error < 0)
392
		return error;
393 394 395

	if (oid != NULL) {
		assert(symbolic == NULL);
396

397
		if (!git_object__is_valid(repo, oid, GIT_OBJ_ANY)) {
398 399 400 401 402
			giterr_set(GITERR_REFERENCE,
				"Target OID for the reference doesn't exist on the repository");
			return -1;
		}

403
		ref = git_reference__alloc(normalized, oid, NULL);
404
	} else {
405
		git_refname_t normalized_target;
406

407
		if ((error = reference_normalize_for_repo(normalized_target, repo, symbolic)) < 0)
408 409
			return error;

410
		ref = git_reference__alloc_symbolic(normalized, normalized_target);
411
	}
412

413
	GITERR_CHECK_ALLOC(ref);
414

415
	if ((error = git_refdb_write(refdb, ref, force, signature, log_message, old_id, old_target)) < 0) {
416
		git_reference_free(ref);
417
		return error;
418
	}
419

420
	if (ref_out == NULL)
421
		git_reference_free(ref);
422
	else
423
		*ref_out = ref;
424

425
	return 0;
426 427
}

428 429 430 431 432 433 434 435 436
int configured_ident(git_signature **out, const git_repository *repo)
{
	if (repo->ident_name && repo->ident_email)
		return git_signature_now(out, repo->ident_name, repo->ident_email);

	/* if not configured let us fall-through to the next method  */
	return -1;
}

437
int git_reference__log_signature(git_signature **out, git_repository *repo)
438
{
439
	int error;
440
	git_signature *who;
441

442 443
	if(((error = configured_ident(&who, repo)) < 0) &&
	   ((error = git_signature_default(&who, repo)) < 0) &&
444
	   ((error = git_signature_now(&who, "unknown", "unknown")) < 0))
445
		return error;
446

447 448
	*out = who;
	return 0;
449 450
}

451
int git_reference_create_matching(
452 453 454 455 456
	git_reference **ref_out,
	git_repository *repo,
	const char *name,
	const git_oid *id,
	int force,
457 458
	const git_oid *old_id,
	const char *log_message)
459

460
{
461 462
	int error;
	git_signature *who = NULL;
463

464
	assert(id);
465

466 467
	if ((error = git_reference__log_signature(&who, repo)) < 0)
		return error;
468 469

	error = reference__create(
470
		ref_out, repo, name, id, NULL, force, who, log_message, old_id, NULL);
Vicent Marti committed
471

472 473
	git_signature_free(who);
	return error;
474
}
475

476 477 478 479 480 481 482 483
int git_reference_create(
	git_reference **ref_out,
	git_repository *repo,
	const char *name,
	const git_oid *id,
	int force,
	const char *log_message)
{
484
        return git_reference_create_matching(ref_out, repo, name, id, force, NULL, log_message);
485 486
}

487
int git_reference_symbolic_create_matching(
488 489 490 491 492
	git_reference **ref_out,
	git_repository *repo,
	const char *name,
	const char *target,
	int force,
493 494
	const char *old_target,
	const char *log_message)
495
{
496 497 498 499 500
	int error;
	git_signature *who = NULL;

	assert(target);

501 502
	if ((error = git_reference__log_signature(&who, repo)) < 0)
		return error;
503

504
	error = reference__create(
505
		ref_out, repo, name, NULL, target, force, who, log_message, NULL, old_target);
506 507 508

	git_signature_free(who);
	return error;
509 510
}

511 512 513 514 515 516 517 518
int git_reference_symbolic_create(
	git_reference **ref_out,
	git_repository *repo,
	const char *name,
	const char *target,
	int force,
	const char *log_message)
{
519
	return git_reference_symbolic_create_matching(ref_out, repo, name, target, force, NULL, log_message);
520 521
}

522 523 524 525
static int ensure_is_an_updatable_direct_reference(git_reference *ref)
{
	if (ref->type == GIT_REF_OID)
		return 0;
526

527 528
	giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference");
	return -1;
529
}
530

531
int git_reference_set_target(
532 533
	git_reference **out,
	git_reference *ref,
534
	const git_oid *id,
535
	const char *log_message)
536 537
{
	int error;
538
	git_repository *repo;
539 540 541

	assert(out && ref && id);

542 543
	repo = ref->db->repo;

544 545 546
	if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0)
		return error;

547
	return git_reference_create_matching(out, repo, ref->name, id, 1, &ref->target.oid, log_message);
548 549
}

550 551 552 553 554 555 556 557 558
static int ensure_is_an_updatable_symbolic_reference(git_reference *ref)
{
	if (ref->type == GIT_REF_SYMBOLIC)
		return 0;

	giterr_set(GITERR_REFERENCE, "Cannot set symbolic target on a direct reference");
	return -1;
}

559 560 561
int git_reference_symbolic_set_target(
	git_reference **out,
	git_reference *ref,
562 563
	const char *target,
	const char *log_message)
Vicent Marti committed
564
{
565 566
	int error;

567
	assert(out && ref && target);
568

569 570
	if ((error = ensure_is_an_updatable_symbolic_reference(ref)) < 0)
		return error;
571

572
	return git_reference_symbolic_create_matching(
573
		out, ref->db->repo, ref->name, target, 1, ref->target.symbolic, log_message);
Vicent Marti committed
574 575
}

576 577
static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force,
				 const git_signature *signature, const char *message)
578
{
579
	git_refname_t normalized;
580
	bool should_head_be_updated = false;
581
	int error = 0;
582 583

	assert(ref && new_name && signature);
584

585
	if ((error = reference_normalize_for_repo(
586
			normalized, git_reference_owner(ref), new_name)) < 0)
587
		return error;
588

589

590
	/* Check if we have to update HEAD. */
591
	if ((error = git_branch_is_head(ref)) < 0)
Vicent Marti committed
592
		return error;
593

594 595
	should_head_be_updated = (error > 0);

596
	if ((error = git_refdb_rename(out, ref->db, ref->name, normalized, force, signature, message)) < 0)
Vicent Marti committed
597
		return error;
598

599
	/* Update HEAD it was pointing to the reference being renamed */
Vicent Marti committed
600
	if (should_head_be_updated &&
601
		(error = git_repository_set_head(ref->db->repo, normalized)) < 0) {
602
		giterr_set(GITERR_REFERENCE, "Failed to update HEAD after renaming reference");
Vicent Marti committed
603
		return error;
604 605
	}

606 607 608
	return 0;
}

609

610 611 612 613
int git_reference_rename(
	git_reference **out,
	git_reference *ref,
	const char *new_name,
614 615
	int force,
	const char *log_message)
616
{
617
	git_signature *who;
618
	int error;
619

620
	if ((error = git_reference__log_signature(&who, ref->db->repo)) < 0)
Vicent Marti committed
621
		return error;
622

623
	error = reference__rename(out, ref, new_name, force, who, log_message);
624
	git_signature_free(who);
625 626 627 628

	return error;
}

629
int git_reference_resolve(git_reference **ref_out, const git_reference *ref)
Vicent Marti committed
630
{
631 632
	switch (git_reference_type(ref)) {
	case GIT_REF_OID:
633
		return git_reference_lookup(ref_out, ref->db->repo, ref->name);
634

635 636 637 638 639 640 641
	case GIT_REF_SYMBOLIC:
		return git_reference_lookup_resolved(ref_out, ref->db->repo, ref->target.symbolic, -1);

	default:
		giterr_set(GITERR_REFERENCE, "Invalid reference");
		return -1;
	}
Vicent Marti committed
642 643
}

644 645
int git_reference_foreach(
	git_repository *repo,
646
	git_reference_foreach_cb callback,
647
	void *payload)
648
{
649
	git_reference_iterator *iter;
650
	git_reference *ref;
651 652
	int error;

653 654
	if ((error = git_reference_iterator_new(&iter, repo)) < 0)
		return error;
655

656 657
	while (!(error = git_reference_next(&ref, iter))) {
		if ((error = callback(ref, payload)) != 0) {
658
			giterr_set_after_callback(error);
659 660 661
			break;
		}
	}
662 663 664 665 666 667 668 669 670 671 672 673 674 675 676

	if (error == GIT_ITEROVER)
		error = 0;

	git_reference_iterator_free(iter);
	return error;
}

int git_reference_foreach_name(
	git_repository *repo,
	git_reference_foreach_name_cb callback,
	void *payload)
{
	git_reference_iterator *iter;
	const char *refname;
677 678
	int error;

679 680
	if ((error = git_reference_iterator_new(&iter, repo)) < 0)
		return error;
681

682 683
	while (!(error = git_reference_next_name(&refname, iter))) {
		if ((error = callback(refname, payload)) != 0) {
684
			giterr_set_after_callback(error);
685 686 687
			break;
		}
	}
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705

	if (error == GIT_ITEROVER)
		error = 0;

	git_reference_iterator_free(iter);
	return error;
}

int git_reference_foreach_glob(
	git_repository *repo,
	const char *glob,
	git_reference_foreach_name_cb callback,
	void *payload)
{
	git_reference_iterator *iter;
	const char *refname;
	int error;

706 707
	if ((error = git_reference_iterator_glob_new(&iter, repo, glob)) < 0)
		return error;
708

709 710
	while (!(error = git_reference_next_name(&refname, iter))) {
		if ((error = callback(refname, payload)) != 0) {
711
			giterr_set_after_callback(error);
712 713 714
			break;
		}
	}
715 716 717 718 719 720

	if (error == GIT_ITEROVER)
		error = 0;

	git_reference_iterator_free(iter);
	return error;
721 722
}

723 724 725 726 727 728 729
int git_reference_iterator_new(git_reference_iterator **out, git_repository *repo)
{
	git_refdb *refdb;

	if (git_repository_refdb__weakptr(&refdb, repo) < 0)
		return -1;

730
	return git_refdb_iterator(out, refdb, NULL);
731 732
}

733 734
int git_reference_iterator_glob_new(
	git_reference_iterator **out, git_repository *repo, const char *glob)
735
{
736
	git_refdb *refdb;
737

738 739 740
	if (git_repository_refdb__weakptr(&refdb, repo) < 0)
		return -1;

741
	return git_refdb_iterator(out, refdb, glob);
742 743
}

Vicent Marti committed
744
int git_reference_next(git_reference **out, git_reference_iterator *iter)
745
{
746 747 748 749 750 751
	return git_refdb_iterator_next(out, iter);
}

int git_reference_next_name(const char **out, git_reference_iterator *iter)
{
	return git_refdb_iterator_next_name(out, iter);
752 753 754 755
}

void git_reference_iterator_free(git_reference_iterator *iter)
{
756 757 758
	if (iter == NULL)
		return;

759
	git_refdb_iterator_free(iter);
760 761
}

762
static int cb__reflist_add(const char *ref, void *data)
763
{
764 765 766
	char *name = git__strdup(ref);
	GITERR_CHECK_ALLOC(name);
	return git_vector_insert((git_vector *)data, name);
767 768
}

769
int git_reference_list(
770
	git_strarray *array,
771
	git_repository *repo)
772 773 774 775 776 777 778 779
{
	git_vector ref_list;

	assert(array && repo);

	array->strings = NULL;
	array->count = 0;

780
	if (git_vector_init(&ref_list, 8, NULL) < 0)
781
		return -1;
782

783
	if (git_reference_foreach_name(
784
			repo, &cb__reflist_add, (void *)&ref_list) < 0) {
785
		git_vector_free(&ref_list);
786
		return -1;
787 788
	}

789 790
	array->strings = (char **)git_vector_detach(&array->count, NULL, &ref_list);

791
	return 0;
792
}
Vicent Marti committed
793

794
static int is_valid_ref_char(char ch)
795
{
796
	if ((unsigned) ch <= ' ')
797
		return 0;
798 799 800 801 802 803 804 805

	switch (ch) {
	case '~':
	case '^':
	case ':':
	case '\\':
	case '?':
	case '[':
806
	case '*':
807
		return 0;
808
	default:
809
		return 1;
810 811 812
	}
}

813
static int ensure_segment_validity(const char *name)
814
{
815 816
	const char *current = name;
	char prev = '\0';
817 818
	const int lock_len = (int)strlen(GIT_FILELOCK_EXTENSION);
	int segment_len;
819

820 821
	if (*current == '.')
		return -1; /* Refname starts with "." */
822

823 824 825
	for (current = name; ; current++) {
		if (*current == '\0' || *current == '/')
			break;
826

827 828
		if (!is_valid_ref_char(*current))
			return -1; /* Illegal character in refname */
829

830 831
		if (prev == '.' && *current == '.')
			return -1; /* Refname contains ".." */
832

833 834
		if (prev == '@' && *current == '{')
			return -1; /* Refname contains "@{" */
835

836 837
		prev = *current;
	}
838

839 840
	segment_len = (int)(current - name);

841
	/* A refname component can not end with ".lock" */
842
	if (segment_len >= lock_len &&
843
		!memcmp(current - lock_len, GIT_FILELOCK_EXTENSION, lock_len))
844 845
			return -1;

846
	return segment_len;
847
}
848

849
static bool is_all_caps_and_underscore(const char *name, size_t len)
850
{
851
	size_t i;
852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868
	char c;

	assert(name && len > 0);

	for (i = 0; i < len; i++)
	{
		c = name[i];
		if ((c < 'A' || c > 'Z') && c != '_')
			return false;
	}

	if (*name == '_' || name[len - 1] == '_')
		return false;

	return true;
}

869
/* Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100 */
870 871 872 873 874
int git_reference__normalize_name(
	git_buf *buf,
	const char *name,
	unsigned int flags)
{
875
	const char *current;
876
	int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
877 878
	unsigned int process_flags;
	bool normalize = (buf != NULL);
879 880

#ifdef GIT_USE_ICONV
881
	git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
882
#endif
883

884
	assert(name);
885

886
	process_flags = flags;
887 888
	current = (char *)name;

889 890 891
	if (*current == '/')
		goto cleanup;

892 893
	if (normalize)
		git_buf_clear(buf);
894

895
#ifdef GIT_USE_ICONV
896 897 898 899 900
	if ((flags & GIT_REF_FORMAT__PRECOMPOSE_UNICODE) != 0) {
		size_t namelen = strlen(current);
		if ((error = git_path_iconv_init_precompose(&ic)) < 0 ||
			(error = git_path_iconv(&ic, &current, &namelen)) < 0)
			goto cleanup;
901
		error = GIT_EINVALIDSPEC;
902
	}
903
#endif
904

905 906 907
	while (true) {
		segment_len = ensure_segment_validity(current);
		if (segment_len < 0) {
908
			if ((process_flags & GIT_REF_FORMAT_REFSPEC_PATTERN) &&
909 910 911
					current[0] == '*' &&
					(current[1] == '\0' || current[1] == '/')) {
				/* Accept one wildcard as a full refname component. */
912
				process_flags &= ~GIT_REF_FORMAT_REFSPEC_PATTERN;
913 914 915 916
				segment_len = 1;
			} else
				goto cleanup;
		}
917

918
		if (segment_len > 0) {
919
			if (normalize) {
920
				size_t cur_len = git_buf_len(buf);
921

922
				git_buf_joinpath(buf, git_buf_cstr(buf), current);
923
				git_buf_truncate(buf,
924
					cur_len + segment_len + (segments_count ? 1 : 0));
925

926 927
				if (git_buf_oom(buf)) {
					error = -1;
928
					goto cleanup;
929
				}
930
			}
931

932
			segments_count++;
933
		}
934

935 936
		/* No empty segment is allowed when not normalizing */
		if (segment_len == 0 && !normalize)
937
			goto cleanup;
938

939 940
		if (current[segment_len] == '\0')
			break;
941

942
		current += segment_len + 1;
943
	}
944

945
	/* A refname can not be empty */
946
	if (segment_len == 0 && segments_count == 0)
947 948 949 950 951 952 953 954 955 956
		goto cleanup;

	/* A refname can not end with "." */
	if (current[segment_len - 1] == '.')
		goto cleanup;

	/* A refname can not end with "/" */
	if (current[segment_len - 1] == '/')
		goto cleanup;

957 958 959 960
	if ((segments_count == 1 ) && !(flags & GIT_REF_FORMAT_ALLOW_ONELEVEL))
		goto cleanup;

	if ((segments_count == 1 ) &&
961
	    !(flags & GIT_REF_FORMAT_REFSPEC_SHORTHAND) &&
962
		!(is_all_caps_and_underscore(name, (size_t)segment_len) ||
963 964 965 966 967 968
			((flags & GIT_REF_FORMAT_REFSPEC_PATTERN) && !strcmp("*", name))))
			goto cleanup;

	if ((segments_count > 1)
		&& (is_all_caps_and_underscore(name, strchr(name, '/') - name)))
			goto cleanup;
969

970
	error = 0;
971

972
cleanup:
973
	if (error == GIT_EINVALIDSPEC)
974 975 976
		giterr_set(
			GITERR_REFERENCE,
			"The given reference name '%s' is not valid", name);
977

978 979 980
	if (error && normalize)
		git_buf_free(buf);

981
#ifdef GIT_USE_ICONV
982
	git_path_iconv_clear(&ic);
983
#endif
984

985 986
	return error;
}
987

988 989 990 991 992 993 994 995 996 997 998 999 1000 1001
int git_reference_normalize_name(
	char *buffer_out,
	size_t buffer_size,
	const char *name,
	unsigned int flags)
{
	git_buf buf = GIT_BUF_INIT;
	int error;

	if ((error = git_reference__normalize_name(&buf, name, flags)) < 0)
		goto cleanup;

	if (git_buf_len(&buf) > buffer_size - 1) {
		giterr_set(
1002
		GITERR_REFERENCE,
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014
		"The provided buffer is too short to hold the normalization of '%s'", name);
		error = GIT_EBUFS;
		goto cleanup;
	}

	git_buf_copy_cstr(buffer_out, buffer_size, &buf);

	error = 0;

cleanup:
	git_buf_free(&buf);
	return error;
1015
}
1016

1017 1018
#define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC)

Jacques Germishuys committed
1019 1020 1021
int git_reference_cmp(
	const git_reference *ref1,
	const git_reference *ref2)
1022
{
1023
	git_ref_t type1, type2;
1024 1025
	assert(ref1 && ref2);

1026 1027 1028
	type1 = git_reference_type(ref1);
	type2 = git_reference_type(ref2);

1029
	/* let's put symbolic refs before OIDs */
1030 1031
	if (type1 != type2)
		return (type1 == GIT_REF_SYMBOLIC) ? -1 : 1;
1032

1033
	if (type1 == GIT_REF_SYMBOLIC)
1034 1035
		return strcmp(ref1->target.symbolic, ref2->target.symbolic);

1036
	return git_oid__cmp(&ref1->target.oid, &ref2->target.oid);
1037 1038
}

1039 1040 1041 1042 1043
/**
 * Get the end of a chain of references. If the final one is not
 * found, we return the reference just before that.
 */
static int get_terminal(git_reference **out, git_repository *repo, const char *ref_name, int nesting)
nulltoken committed
1044 1045
{
	git_reference *ref;
1046
	int error = 0;
nulltoken committed
1047

1048 1049
	if (nesting > MAX_NESTING_LEVEL) {
		giterr_set(GITERR_REFERENCE, "Reference chain too deep (%d)", nesting);
1050
		return GIT_ENOTFOUND;
1051
	}
1052

1053 1054 1055
	/* set to NULL to let the caller know that they're at the end of the chain */
	if ((error = git_reference_lookup(&ref, repo, ref_name)) < 0) {
		*out = NULL;
1056
		return error;
1057
	}
1058

1059 1060 1061
	if (git_reference_type(ref) == GIT_REF_OID) {
		*out = ref;
		error = 0;
1062
	} else {
1063
		error = get_terminal(out, repo, git_reference_symbolic_target(ref), nesting + 1);
Edward Thomson committed
1064 1065 1066
		if (error == GIT_ENOTFOUND && !*out)
			*out = ref;
		else
1067
			git_reference_free(ref);
nulltoken committed
1068
	}
1069

1070
	return error;
nulltoken committed
1071
}
1072

1073 1074 1075 1076 1077 1078 1079 1080
/*
 * Starting with the reference given by `ref_name`, follows symbolic
 * references until a direct reference is found and updated the OID
 * on that direct reference to `oid`.
 */
int git_reference__update_terminal(
	git_repository *repo,
	const char *ref_name,
1081
	const git_oid *oid,
1082
	const git_signature *sig,
1083
	const char *log_message)
1084
{
1085
	git_reference *ref = NULL, *ref2 = NULL;
1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099
	git_signature *who = NULL;
	const git_signature *to_use;
	int error = 0;

	if (!sig && (error = git_reference__log_signature(&who, repo)) < 0)
		return error;

	to_use = sig ? sig : who;
	error = get_terminal(&ref, repo, ref_name, 0);

	/* found a dangling symref */
	if (error == GIT_ENOTFOUND && ref) {
		assert(git_reference_type(ref) == GIT_REF_SYMBOLIC);
		giterr_clear();
1100
		error = reference__create(&ref2, repo, ref->target.symbolic, oid, NULL, 0, to_use,
1101 1102 1103
					  log_message, NULL, NULL);
	} else if (error == GIT_ENOTFOUND) {
		giterr_clear();
1104
		error = reference__create(&ref2, repo, ref_name, oid, NULL, 0, to_use,
1105 1106 1107
					  log_message, NULL, NULL);
	}  else if (error == 0) {
		assert(git_reference_type(ref) == GIT_REF_OID);
1108
		error = reference__create(&ref2, repo, ref->name, oid, NULL, 1, to_use,
1109 1110 1111
					  log_message, &ref->target.oid, NULL);
	}

1112 1113
	git_reference_free(ref2);
	git_reference_free(ref);
1114 1115
	git_signature_free(who);
	return error;
1116 1117
}

1118 1119 1120 1121 1122 1123 1124 1125 1126 1127
int git_reference__update_for_commit(
	git_repository *repo,
	git_reference *ref,
	const char *ref_name,
	const git_oid *id,
	const char *operation)
{
	git_reference *ref_new = NULL;
	git_commit *commit = NULL;
	git_buf reflog_msg = GIT_BUF_INIT;
1128
	const git_signature *who;
1129 1130 1131 1132 1133 1134 1135 1136 1137
	int error;

	if ((error = git_commit_lookup(&commit, repo, id)) < 0 ||
		(error = git_buf_printf(&reflog_msg, "%s%s: %s",
			operation ? operation : "commit",
			git_commit_parentcount(commit) == 0 ? " (initial)" : "",
			git_commit_summary(commit))) < 0)
		goto done;

1138 1139 1140 1141 1142 1143 1144 1145 1146
	who = git_commit_committer(commit);

	if (ref) {
		if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0)
			return error;

		error = reference__create(&ref_new, repo, ref->name, id, NULL, 1, who,
					  git_buf_cstr(&reflog_msg), &ref->target.oid, NULL);
	}
1147 1148
	else
		error = git_reference__update_terminal(
1149
			repo, ref_name, id, who, git_buf_cstr(&reflog_msg));
1150 1151 1152 1153 1154 1155 1156 1157

done:
	git_reference_free(ref_new);
	git_buf_free(&reflog_msg);
	git_commit_free(commit);
	return error;
}

1158
int git_reference_has_log(git_repository *repo, const char *refname)
1159
{
1160 1161
	int error;
	git_refdb *refdb;
1162

1163
	assert(repo && refname);
1164

1165 1166
	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
		return error;
1167

1168
	return git_refdb_has_log(refdb, refname);
1169
}
1170

1171 1172 1173 1174 1175 1176 1177 1178 1179
int git_reference_ensure_log(git_repository *repo, const char *refname)
{
	int error;
	git_refdb *refdb;

	assert(repo && refname);

	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
		return error;
1180

1181
	return git_refdb_ensure_log(refdb, refname);
1182
}
1183

1184 1185 1186 1187 1188
int git_reference__is_branch(const char *ref_name)
{
	return git__prefixcmp(ref_name, GIT_REFS_HEADS_DIR) == 0;
}

1189
int git_reference_is_branch(const git_reference *ref)
1190 1191
{
	assert(ref);
1192
	return git_reference__is_branch(ref->name);
1193
}
1194

1195 1196 1197 1198 1199
int git_reference__is_remote(const char *ref_name)
{
	return git__prefixcmp(ref_name, GIT_REFS_REMOTES_DIR) == 0;
}

Jacques Germishuys committed
1200
int git_reference_is_remote(const git_reference *ref)
1201 1202
{
	assert(ref);
1203
	return git_reference__is_remote(ref->name);
1204
}
1205

1206 1207 1208 1209 1210
int git_reference__is_tag(const char *ref_name)
{
	return git__prefixcmp(ref_name, GIT_REFS_TAGS_DIR) == 0;
}

Jacques Germishuys committed
1211
int git_reference_is_tag(const git_reference *ref)
1212 1213 1214 1215 1216
{
	assert(ref);
	return git_reference__is_tag(ref->name);
}

1217 1218 1219 1220 1221
int git_reference__is_note(const char *ref_name)
{
	return git__prefixcmp(ref_name, GIT_REFS_NOTES_DIR) == 0;
}

Jacques Germishuys committed
1222
int git_reference_is_note(const git_reference *ref)
1223 1224 1225 1226 1227
{
	assert(ref);
	return git_reference__is_note(ref->name);
}

1228 1229 1230 1231 1232 1233 1234 1235 1236
static int peel_error(int error, git_reference *ref, const char* msg)
{
	giterr_set(
		GITERR_INVALID,
		"The reference '%s' cannot be peeled - %s", git_reference_name(ref), msg);
	return error;
}

int git_reference_peel(
1237 1238 1239
	git_object **peeled,
	git_reference *ref,
	git_otype target_type)
1240 1241 1242 1243 1244 1245 1246
{
	git_reference *resolved = NULL;
	git_object *target = NULL;
	int error;

	assert(ref);

1247 1248 1249 1250 1251 1252 1253
	if (ref->type == GIT_REF_OID) {
		resolved = ref;
	} else {
		if ((error = git_reference_resolve(&resolved, ref)) < 0)
			return peel_error(error, ref, "Cannot resolve reference");
	}

1254
	if (!git_oid_iszero(&resolved->peel)) {
1255
		error = git_object_lookup(&target,
1256
			git_reference_owner(ref), &resolved->peel, GIT_OBJ_ANY);
1257 1258
	} else {
		error = git_object_lookup(&target,
1259
			git_reference_owner(ref), &resolved->target.oid, GIT_OBJ_ANY);
1260
	}
1261

1262
	if (error < 0) {
1263 1264 1265
		peel_error(error, ref, "Cannot retrieve reference target");
		goto cleanup;
	}
1266

1267
	if (target_type == GIT_OBJ_ANY && git_object_type(target) != GIT_OBJ_TAG)
1268
		error = git_object_dup(peeled, target);
1269
	else
1270 1271 1272 1273
		error = git_object_peel(peeled, target, target_type);

cleanup:
	git_object_free(target);
1274 1275 1276 1277

	if (resolved != ref)
		git_reference_free(resolved);

1278 1279
	return error;
}
1280

1281
int git_reference__is_valid_name(const char *refname, unsigned int flags)
1282
{
1283 1284 1285 1286
	if (git_reference__normalize_name(NULL, refname, flags) < 0) {
		giterr_clear();
		return false;
	}
nulltoken committed
1287

1288
	return true;
1289 1290
}

1291
int git_reference_is_valid_name(const char *refname)
1292
{
1293
	return git_reference__is_valid_name(refname, GIT_REF_FORMAT_ALLOW_ONELEVEL);
1294
}
1295

1296
const char *git_reference__shorthand(const char *name)
1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309
{
	if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR))
		return name + strlen(GIT_REFS_HEADS_DIR);
	else if (!git__prefixcmp(name, GIT_REFS_TAGS_DIR))
		return name + strlen(GIT_REFS_TAGS_DIR);
	else if (!git__prefixcmp(name, GIT_REFS_REMOTES_DIR))
		return name + strlen(GIT_REFS_REMOTES_DIR);
	else if (!git__prefixcmp(name, GIT_REFS_DIR))
		return name + strlen(GIT_REFS_DIR);

	/* No shorthands are avaiable, so just return the name */
	return name;
}
1310 1311 1312 1313 1314

const char *git_reference_shorthand(const git_reference *ref)
{
	return git_reference__shorthand(ref->name);
}