1 /*
2  * Copyright (C) the libgit2 contributors. All rights reserved.
3  *
4  * This file is part of libgit2, distributed under the GNU GPL v2 with
5  * a Linking Exception. For full terms see the included COPYING file.
6  */
7 
8 #include "refs.h"
9 
10 #include "hash.h"
11 #include "repository.h"
12 #include "fileops.h"
13 #include "filebuf.h"
14 #include "pack.h"
15 #include "reflog.h"
16 #include "refdb.h"
17 
18 #include <git2/tag.h>
19 #include <git2/object.h>
20 #include <git2/oid.h>
21 #include <git2/branch.h>
22 #include <git2/refs.h>
23 #include <git2/refdb.h>
24 #include <git2/sys/refs.h>
25 #include <git2/signature.h>
26 #include <git2/commit.h>
27 
28 bool git_reference__enable_symbolic_ref_target_validation = true;
29 
30 #define DEFAULT_NESTING_LEVEL	5
31 #define MAX_NESTING_LEVEL		10
32 
33 enum {
34 	GIT_PACKREF_HAS_PEEL = 1,
35 	GIT_PACKREF_WAS_LOOSE = 2
36 };
37 
alloc_ref(const char * name)38 static git_reference *alloc_ref(const char *name)
39 {
40 	git_reference *ref = NULL;
41 	size_t namelen = strlen(name), reflen;
42 
43 	if (!GIT_ADD_SIZET_OVERFLOW(&reflen, sizeof(git_reference), namelen) &&
44 		!GIT_ADD_SIZET_OVERFLOW(&reflen, reflen, 1) &&
45 		(ref = git__calloc(1, reflen)) != NULL)
46 		memcpy(ref->name, name, namelen + 1);
47 
48 	return ref;
49 }
50 
git_reference__alloc_symbolic(const char * name,const char * target)51 git_reference *git_reference__alloc_symbolic(
52 	const char *name, const char *target)
53 {
54 	git_reference *ref;
55 
56 	assert(name && target);
57 
58 	ref = alloc_ref(name);
59 	if (!ref)
60 		return NULL;
61 
62 	ref->type = GIT_REFERENCE_SYMBOLIC;
63 
64 	if ((ref->target.symbolic = git__strdup(target)) == NULL) {
65 		git__free(ref);
66 		return NULL;
67 	}
68 
69 	return ref;
70 }
71 
git_reference__alloc(const char * name,const git_oid * oid,const git_oid * peel)72 git_reference *git_reference__alloc(
73 	const char *name,
74 	const git_oid *oid,
75 	const git_oid *peel)
76 {
77 	git_reference *ref;
78 
79 	assert(name && oid);
80 
81 	ref = alloc_ref(name);
82 	if (!ref)
83 		return NULL;
84 
85 	ref->type = GIT_REFERENCE_DIRECT;
86 	git_oid_cpy(&ref->target.oid, oid);
87 
88 	if (peel != NULL)
89 		git_oid_cpy(&ref->peel, peel);
90 
91 	return ref;
92 }
93 
git_reference__set_name(git_reference * ref,const char * name)94 git_reference *git_reference__set_name(
95 	git_reference *ref, const char *name)
96 {
97 	size_t namelen = strlen(name);
98 	size_t reflen;
99 	git_reference *rewrite = NULL;
100 
101 	if (!GIT_ADD_SIZET_OVERFLOW(&reflen, sizeof(git_reference), namelen) &&
102 		!GIT_ADD_SIZET_OVERFLOW(&reflen, reflen, 1) &&
103 		(rewrite = git__realloc(ref, reflen)) != NULL)
104 		memcpy(rewrite->name, name, namelen + 1);
105 
106 	return rewrite;
107 }
108 
git_reference_dup(git_reference ** dest,git_reference * source)109 int git_reference_dup(git_reference **dest, git_reference *source)
110 {
111 	if (source->type == GIT_REFERENCE_SYMBOLIC)
112 		*dest = git_reference__alloc_symbolic(source->name, source->target.symbolic);
113 	else
114 		*dest = git_reference__alloc(source->name, &source->target.oid, &source->peel);
115 
116 	GIT_ERROR_CHECK_ALLOC(*dest);
117 
118 	(*dest)->db = source->db;
119 	GIT_REFCOUNT_INC((*dest)->db);
120 
121 	return 0;
122 }
123 
git_reference_free(git_reference * reference)124 void git_reference_free(git_reference *reference)
125 {
126 	if (reference == NULL)
127 		return;
128 
129 	if (reference->type == GIT_REFERENCE_SYMBOLIC)
130 		git__free(reference->target.symbolic);
131 
132 	if (reference->db)
133 		GIT_REFCOUNT_DEC(reference->db, git_refdb__free);
134 
135 	git__free(reference);
136 }
137 
git_reference_delete(git_reference * ref)138 int git_reference_delete(git_reference *ref)
139 {
140 	const git_oid *old_id = NULL;
141 	const char *old_target = NULL;
142 
143 	if (!strcmp(ref->name, "HEAD")) {
144 		git_error_set(GIT_ERROR_REFERENCE, "cannot delete HEAD");
145 		return GIT_ERROR;
146 	}
147 
148 	if (ref->type == GIT_REFERENCE_DIRECT)
149 		old_id = &ref->target.oid;
150 	else
151 		old_target = ref->target.symbolic;
152 
153 	return git_refdb_delete(ref->db, ref->name, old_id, old_target);
154 }
155 
git_reference_remove(git_repository * repo,const char * name)156 int git_reference_remove(git_repository *repo, const char *name)
157 {
158 	git_refdb *db;
159 	int error;
160 
161 	if ((error = git_repository_refdb__weakptr(&db, repo)) < 0)
162 		return error;
163 
164 	return git_refdb_delete(db, name, NULL, NULL);
165 }
166 
git_reference_lookup(git_reference ** ref_out,git_repository * repo,const char * name)167 int git_reference_lookup(git_reference **ref_out,
168 	git_repository *repo, const char *name)
169 {
170 	return git_reference_lookup_resolved(ref_out, repo, name, 0);
171 }
172 
git_reference_name_to_id(git_oid * out,git_repository * repo,const char * name)173 int git_reference_name_to_id(
174 	git_oid *out, git_repository *repo, const char *name)
175 {
176 	int error;
177 	git_reference *ref;
178 
179 	if ((error = git_reference_lookup_resolved(&ref, repo, name, -1)) < 0)
180 		return error;
181 
182 	git_oid_cpy(out, git_reference_target(ref));
183 	git_reference_free(ref);
184 	return 0;
185 }
186 
reference_normalize_for_repo(git_refname_t out,git_repository * repo,const char * name,bool validate)187 static int reference_normalize_for_repo(
188 	git_refname_t out,
189 	git_repository *repo,
190 	const char *name,
191 	bool validate)
192 {
193 	int precompose;
194 	unsigned int flags = GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL;
195 
196 	if (!git_repository__cvar(&precompose, repo, GIT_CVAR_PRECOMPOSE) &&
197 		precompose)
198 		flags |= GIT_REFERENCE_FORMAT__PRECOMPOSE_UNICODE;
199 
200 	if (!validate)
201 		flags |= GIT_REFERENCE_FORMAT__VALIDATION_DISABLE;
202 
203 	return git_reference_normalize_name(out, GIT_REFNAME_MAX, name, flags);
204 }
205 
git_reference_lookup_resolved(git_reference ** ref_out,git_repository * repo,const char * name,int max_nesting)206 int git_reference_lookup_resolved(
207 	git_reference **ref_out,
208 	git_repository *repo,
209 	const char *name,
210 	int max_nesting)
211 {
212 	git_refname_t scan_name;
213 	git_reference_t scan_type;
214 	int error = 0, nesting;
215 	git_reference *ref = NULL;
216 	git_refdb *refdb;
217 
218 	assert(ref_out && repo && name);
219 
220 	*ref_out = NULL;
221 
222 	if (max_nesting > MAX_NESTING_LEVEL)
223 		max_nesting = MAX_NESTING_LEVEL;
224 	else if (max_nesting < 0)
225 		max_nesting = DEFAULT_NESTING_LEVEL;
226 
227 	scan_type = GIT_REFERENCE_SYMBOLIC;
228 
229 	if ((error = reference_normalize_for_repo(scan_name, repo, name, true)) < 0)
230 		return error;
231 
232 	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
233 		return error;
234 
235 	for (nesting = max_nesting;
236 		 nesting >= 0 && scan_type == GIT_REFERENCE_SYMBOLIC;
237 		 nesting--)
238 	{
239 		if (nesting != max_nesting) {
240                         /* Fix in git2r to handle a significant
241                            warning from 'R CMD check'
242                            libgit2/src/refs.c:235:4: warning:
243                            ‘strncpy’ specified bound 1024 equals
244                            destination size [-Wstringop-truncation] */
245 			strncpy(scan_name, ref->target.symbolic, sizeof(scan_name) - 1);
246 			/* strncpy(scan_name, ref->target.symbolic, sizeof(scan_name)); */
247 			git_reference_free(ref);
248 		}
249 
250 		if ((error = git_refdb_lookup(&ref, refdb, scan_name)) < 0)
251 			return error;
252 
253 		scan_type = ref->type;
254 	}
255 
256 	if (scan_type != GIT_REFERENCE_DIRECT && max_nesting != 0) {
257 		git_error_set(GIT_ERROR_REFERENCE,
258 			"cannot resolve reference (>%u levels deep)", max_nesting);
259 		git_reference_free(ref);
260 		return -1;
261 	}
262 
263 	*ref_out = ref;
264 	return 0;
265 }
266 
git_reference__read_head(git_reference ** out,git_repository * repo,const char * path)267 int git_reference__read_head(
268 	git_reference **out,
269 	git_repository *repo,
270 	const char *path)
271 {
272 	git_buf reference = GIT_BUF_INIT;
273 	char *name = NULL;
274 	int error;
275 
276 	if ((error = git_futils_readbuffer(&reference, path)) < 0)
277 		goto out;
278 	git_buf_rtrim(&reference);
279 
280 	if (git__strncmp(reference.ptr, GIT_SYMREF, strlen(GIT_SYMREF)) == 0) {
281 		git_buf_consume(&reference, reference.ptr + strlen(GIT_SYMREF));
282 
283 		name = git_path_basename(path);
284 
285 		if ((*out = git_reference__alloc_symbolic(name, reference.ptr)) == NULL) {
286 			error = -1;
287 			goto out;
288 		}
289 	} else {
290 		if ((error = git_reference_lookup(out, repo, reference.ptr)) < 0)
291 			goto out;
292 	}
293 
294 out:
295 	git__free(name);
296 	git_buf_dispose(&reference);
297 
298 	return error;
299 }
300 
git_reference_dwim(git_reference ** out,git_repository * repo,const char * refname)301 int git_reference_dwim(git_reference **out, git_repository *repo, const char *refname)
302 {
303 	int error = 0, i;
304 	bool fallbackmode = true, foundvalid = false;
305 	git_reference *ref;
306 	git_buf refnamebuf = GIT_BUF_INIT, name = GIT_BUF_INIT;
307 
308 	static const char* formatters[] = {
309 		"%s",
310 		GIT_REFS_DIR "%s",
311 		GIT_REFS_TAGS_DIR "%s",
312 		GIT_REFS_HEADS_DIR "%s",
313 		GIT_REFS_REMOTES_DIR "%s",
314 		GIT_REFS_REMOTES_DIR "%s/" GIT_HEAD_FILE,
315 		NULL
316 	};
317 
318 	if (*refname)
319 		git_buf_puts(&name, refname);
320 	else {
321 		git_buf_puts(&name, GIT_HEAD_FILE);
322 		fallbackmode = false;
323 	}
324 
325 	for (i = 0; formatters[i] && (fallbackmode || i == 0); i++) {
326 
327 		git_buf_clear(&refnamebuf);
328 
329 		if ((error = git_buf_printf(&refnamebuf, formatters[i], git_buf_cstr(&name))) < 0)
330 			goto cleanup;
331 
332 		if (!git_reference_is_valid_name(git_buf_cstr(&refnamebuf))) {
333 			error = GIT_EINVALIDSPEC;
334 			continue;
335 		}
336 		foundvalid = true;
337 
338 		error = git_reference_lookup_resolved(&ref, repo, git_buf_cstr(&refnamebuf), -1);
339 
340 		if (!error) {
341 			*out = ref;
342 			error = 0;
343 			goto cleanup;
344 		}
345 
346 		if (error != GIT_ENOTFOUND)
347 			goto cleanup;
348 	}
349 
350 cleanup:
351 	if (error && !foundvalid) {
352 		/* never found a valid reference name */
353 		git_error_set(GIT_ERROR_REFERENCE,
354 			"could not use '%s' as valid reference name", git_buf_cstr(&name));
355 	}
356 
357 	if (error == GIT_ENOTFOUND)
358 		git_error_set(GIT_ERROR_REFERENCE, "no reference found for shorthand '%s'", refname);
359 
360 	git_buf_dispose(&name);
361 	git_buf_dispose(&refnamebuf);
362 	return error;
363 }
364 
365 /**
366  * Getters
367  */
git_reference_type(const git_reference * ref)368 git_reference_t git_reference_type(const git_reference *ref)
369 {
370 	assert(ref);
371 	return ref->type;
372 }
373 
git_reference_name(const git_reference * ref)374 const char *git_reference_name(const git_reference *ref)
375 {
376 	assert(ref);
377 	return ref->name;
378 }
379 
git_reference_owner(const git_reference * ref)380 git_repository *git_reference_owner(const git_reference *ref)
381 {
382 	assert(ref);
383 	return ref->db->repo;
384 }
385 
git_reference_target(const git_reference * ref)386 const git_oid *git_reference_target(const git_reference *ref)
387 {
388 	assert(ref);
389 
390 	if (ref->type != GIT_REFERENCE_DIRECT)
391 		return NULL;
392 
393 	return &ref->target.oid;
394 }
395 
git_reference_target_peel(const git_reference * ref)396 const git_oid *git_reference_target_peel(const git_reference *ref)
397 {
398 	assert(ref);
399 
400 	if (ref->type != GIT_REFERENCE_DIRECT || git_oid_iszero(&ref->peel))
401 		return NULL;
402 
403 	return &ref->peel;
404 }
405 
git_reference_symbolic_target(const git_reference * ref)406 const char *git_reference_symbolic_target(const git_reference *ref)
407 {
408 	assert(ref);
409 
410 	if (ref->type != GIT_REFERENCE_SYMBOLIC)
411 		return NULL;
412 
413 	return ref->target.symbolic;
414 }
415 
reference__create(git_reference ** ref_out,git_repository * repo,const char * name,const git_oid * oid,const char * symbolic,int force,const git_signature * signature,const char * log_message,const git_oid * old_id,const char * old_target)416 static int reference__create(
417 	git_reference **ref_out,
418 	git_repository *repo,
419 	const char *name,
420 	const git_oid *oid,
421 	const char *symbolic,
422 	int force,
423 	const git_signature *signature,
424 	const char *log_message,
425 	const git_oid *old_id,
426 	const char *old_target)
427 {
428 	git_refname_t normalized;
429 	git_refdb *refdb;
430 	git_reference *ref = NULL;
431 	int error = 0;
432 
433 	assert(repo && name);
434 	assert(symbolic || signature);
435 
436 	if (ref_out)
437 		*ref_out = NULL;
438 
439 	error = reference_normalize_for_repo(normalized, repo, name, true);
440 	if (error < 0)
441 		return error;
442 
443 	error = git_repository_refdb__weakptr(&refdb, repo);
444 	if (error < 0)
445 		return error;
446 
447 	if (oid != NULL) {
448 		assert(symbolic == NULL);
449 
450 		if (!git_object__is_valid(repo, oid, GIT_OBJECT_ANY)) {
451 			git_error_set(GIT_ERROR_REFERENCE,
452 				"target OID for the reference doesn't exist on the repository");
453 			return -1;
454 		}
455 
456 		ref = git_reference__alloc(normalized, oid, NULL);
457 	} else {
458 		git_refname_t normalized_target;
459 
460 		error = reference_normalize_for_repo(normalized_target, repo,
461 			symbolic, git_reference__enable_symbolic_ref_target_validation);
462 
463 		if (error < 0)
464 			return error;
465 
466 		ref = git_reference__alloc_symbolic(normalized, normalized_target);
467 	}
468 
469 	GIT_ERROR_CHECK_ALLOC(ref);
470 
471 	if ((error = git_refdb_write(refdb, ref, force, signature, log_message, old_id, old_target)) < 0) {
472 		git_reference_free(ref);
473 		return error;
474 	}
475 
476 	if (ref_out == NULL)
477 		git_reference_free(ref);
478 	else
479 		*ref_out = ref;
480 
481 	return 0;
482 }
483 
configured_ident(git_signature ** out,const git_repository * repo)484 int configured_ident(git_signature **out, const git_repository *repo)
485 {
486 	if (repo->ident_name && repo->ident_email)
487 		return git_signature_now(out, repo->ident_name, repo->ident_email);
488 
489 	/* if not configured let us fall-through to the next method  */
490 	return -1;
491 }
492 
git_reference__log_signature(git_signature ** out,git_repository * repo)493 int git_reference__log_signature(git_signature **out, git_repository *repo)
494 {
495 	int error;
496 	git_signature *who;
497 
498 	if(((error = configured_ident(&who, repo)) < 0) &&
499 	   ((error = git_signature_default(&who, repo)) < 0) &&
500 	   ((error = git_signature_now(&who, "unknown", "unknown")) < 0))
501 		return error;
502 
503 	*out = who;
504 	return 0;
505 }
506 
git_reference_create_matching(git_reference ** ref_out,git_repository * repo,const char * name,const git_oid * id,int force,const git_oid * old_id,const char * log_message)507 int git_reference_create_matching(
508 	git_reference **ref_out,
509 	git_repository *repo,
510 	const char *name,
511 	const git_oid *id,
512 	int force,
513 	const git_oid *old_id,
514 	const char *log_message)
515 
516 {
517 	int error;
518 	git_signature *who = NULL;
519 
520 	assert(id);
521 
522 	if ((error = git_reference__log_signature(&who, repo)) < 0)
523 		return error;
524 
525 	error = reference__create(
526 		ref_out, repo, name, id, NULL, force, who, log_message, old_id, NULL);
527 
528 	git_signature_free(who);
529 	return error;
530 }
531 
git_reference_create(git_reference ** ref_out,git_repository * repo,const char * name,const git_oid * id,int force,const char * log_message)532 int git_reference_create(
533 	git_reference **ref_out,
534 	git_repository *repo,
535 	const char *name,
536 	const git_oid *id,
537 	int force,
538 	const char *log_message)
539 {
540         return git_reference_create_matching(ref_out, repo, name, id, force, NULL, log_message);
541 }
542 
git_reference_symbolic_create_matching(git_reference ** ref_out,git_repository * repo,const char * name,const char * target,int force,const char * old_target,const char * log_message)543 int git_reference_symbolic_create_matching(
544 	git_reference **ref_out,
545 	git_repository *repo,
546 	const char *name,
547 	const char *target,
548 	int force,
549 	const char *old_target,
550 	const char *log_message)
551 {
552 	int error;
553 	git_signature *who = NULL;
554 
555 	assert(target);
556 
557 	if ((error = git_reference__log_signature(&who, repo)) < 0)
558 		return error;
559 
560 	error = reference__create(
561 		ref_out, repo, name, NULL, target, force, who, log_message, NULL, old_target);
562 
563 	git_signature_free(who);
564 	return error;
565 }
566 
git_reference_symbolic_create(git_reference ** ref_out,git_repository * repo,const char * name,const char * target,int force,const char * log_message)567 int git_reference_symbolic_create(
568 	git_reference **ref_out,
569 	git_repository *repo,
570 	const char *name,
571 	const char *target,
572 	int force,
573 	const char *log_message)
574 {
575 	return git_reference_symbolic_create_matching(ref_out, repo, name, target, force, NULL, log_message);
576 }
577 
ensure_is_an_updatable_direct_reference(git_reference * ref)578 static int ensure_is_an_updatable_direct_reference(git_reference *ref)
579 {
580 	if (ref->type == GIT_REFERENCE_DIRECT)
581 		return 0;
582 
583 	git_error_set(GIT_ERROR_REFERENCE, "cannot set OID on symbolic reference");
584 	return -1;
585 }
586 
git_reference_set_target(git_reference ** out,git_reference * ref,const git_oid * id,const char * log_message)587 int git_reference_set_target(
588 	git_reference **out,
589 	git_reference *ref,
590 	const git_oid *id,
591 	const char *log_message)
592 {
593 	int error;
594 	git_repository *repo;
595 
596 	assert(out && ref && id);
597 
598 	repo = ref->db->repo;
599 
600 	if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0)
601 		return error;
602 
603 	return git_reference_create_matching(out, repo, ref->name, id, 1, &ref->target.oid, log_message);
604 }
605 
ensure_is_an_updatable_symbolic_reference(git_reference * ref)606 static int ensure_is_an_updatable_symbolic_reference(git_reference *ref)
607 {
608 	if (ref->type == GIT_REFERENCE_SYMBOLIC)
609 		return 0;
610 
611 	git_error_set(GIT_ERROR_REFERENCE, "cannot set symbolic target on a direct reference");
612 	return -1;
613 }
614 
git_reference_symbolic_set_target(git_reference ** out,git_reference * ref,const char * target,const char * log_message)615 int git_reference_symbolic_set_target(
616 	git_reference **out,
617 	git_reference *ref,
618 	const char *target,
619 	const char *log_message)
620 {
621 	int error;
622 
623 	assert(out && ref && target);
624 
625 	if ((error = ensure_is_an_updatable_symbolic_reference(ref)) < 0)
626 		return error;
627 
628 	return git_reference_symbolic_create_matching(
629 		out, ref->db->repo, ref->name, target, 1, ref->target.symbolic, log_message);
630 }
631 
632 typedef struct {
633     const char *old_name;
634     git_refname_t new_name;
635 } rename_cb_data;
636 
update_wt_heads(git_repository * repo,const char * path,void * payload)637 static int update_wt_heads(git_repository *repo, const char *path, void *payload)
638 {
639 	rename_cb_data *data = (rename_cb_data *) payload;
640 	git_reference *head = NULL;
641 	char *gitdir = NULL;
642 	int error;
643 
644 	if ((error = git_reference__read_head(&head, repo, path)) < 0) {
645 		git_error_set(GIT_ERROR_REFERENCE, "could not read HEAD when renaming references");
646 		goto out;
647 	}
648 
649 	if ((gitdir = git_path_dirname(path)) == NULL) {
650 		error = -1;
651 		goto out;
652 	}
653 
654 	if (git_reference_type(head) != GIT_REFERENCE_SYMBOLIC ||
655 	    git__strcmp(head->target.symbolic, data->old_name) != 0) {
656 		error = 0;
657 		goto out;
658 	}
659 
660 	/* Update HEAD it was pointing to the reference being renamed */
661 	if ((error = git_repository_create_head(gitdir, data->new_name)) < 0) {
662 		git_error_set(GIT_ERROR_REFERENCE, "failed to update HEAD after renaming reference");
663 		goto out;
664 	}
665 
666 out:
667 	git_reference_free(head);
668 	git__free(gitdir);
669 
670 	return error;
671 }
672 
reference__rename(git_reference ** out,git_reference * ref,const char * new_name,int force,const git_signature * signature,const char * message)673 static int reference__rename(git_reference **out, git_reference *ref, const char *new_name, int force,
674 				 const git_signature *signature, const char *message)
675 {
676 	git_repository *repo;
677 	git_refname_t normalized;
678 	bool should_head_be_updated = false;
679 	int error = 0;
680 
681 	assert(ref && new_name && signature);
682 
683 	repo = git_reference_owner(ref);
684 
685 	if ((error = reference_normalize_for_repo(
686 		normalized, repo, new_name, true)) < 0)
687 		return error;
688 
689 	/* Check if we have to update HEAD. */
690 	if ((error = git_branch_is_head(ref)) < 0)
691 		return error;
692 
693 	should_head_be_updated = (error > 0);
694 
695 	if ((error = git_refdb_rename(out, ref->db, ref->name, normalized, force, signature, message)) < 0)
696 		return error;
697 
698 	/* Update HEAD if it was pointing to the reference being renamed */
699 	if (should_head_be_updated) {
700 		error = git_repository_set_head(ref->db->repo, normalized);
701 	} else {
702 		rename_cb_data payload;
703 		payload.old_name = ref->name;
704 		memcpy(&payload.new_name, &normalized, sizeof(normalized));
705 
706 		error = git_repository_foreach_head(repo, update_wt_heads, &payload);
707 	}
708 
709 	return error;
710 }
711 
712 
git_reference_rename(git_reference ** out,git_reference * ref,const char * new_name,int force,const char * log_message)713 int git_reference_rename(
714 	git_reference **out,
715 	git_reference *ref,
716 	const char *new_name,
717 	int force,
718 	const char *log_message)
719 {
720 	git_signature *who;
721 	int error;
722 
723 	assert(out && ref);
724 
725 	if ((error = git_reference__log_signature(&who, ref->db->repo)) < 0)
726 		return error;
727 
728 	error = reference__rename(out, ref, new_name, force, who, log_message);
729 	git_signature_free(who);
730 
731 	return error;
732 }
733 
git_reference_resolve(git_reference ** ref_out,const git_reference * ref)734 int git_reference_resolve(git_reference **ref_out, const git_reference *ref)
735 {
736 	switch (git_reference_type(ref)) {
737 	case GIT_REFERENCE_DIRECT:
738 		return git_reference_lookup(ref_out, ref->db->repo, ref->name);
739 
740 	case GIT_REFERENCE_SYMBOLIC:
741 		return git_reference_lookup_resolved(ref_out, ref->db->repo, ref->target.symbolic, -1);
742 
743 	default:
744 		git_error_set(GIT_ERROR_REFERENCE, "invalid reference");
745 		return -1;
746 	}
747 }
748 
git_reference_foreach(git_repository * repo,git_reference_foreach_cb callback,void * payload)749 int git_reference_foreach(
750 	git_repository *repo,
751 	git_reference_foreach_cb callback,
752 	void *payload)
753 {
754 	git_reference_iterator *iter;
755 	git_reference *ref;
756 	int error;
757 
758 	if ((error = git_reference_iterator_new(&iter, repo)) < 0)
759 		return error;
760 
761 	while (!(error = git_reference_next(&ref, iter))) {
762 		if ((error = callback(ref, payload)) != 0) {
763 			git_error_set_after_callback(error);
764 			break;
765 		}
766 	}
767 
768 	if (error == GIT_ITEROVER)
769 		error = 0;
770 
771 	git_reference_iterator_free(iter);
772 	return error;
773 }
774 
git_reference_foreach_name(git_repository * repo,git_reference_foreach_name_cb callback,void * payload)775 int git_reference_foreach_name(
776 	git_repository *repo,
777 	git_reference_foreach_name_cb callback,
778 	void *payload)
779 {
780 	git_reference_iterator *iter;
781 	const char *refname;
782 	int error;
783 
784 	if ((error = git_reference_iterator_new(&iter, repo)) < 0)
785 		return error;
786 
787 	while (!(error = git_reference_next_name(&refname, iter))) {
788 		if ((error = callback(refname, payload)) != 0) {
789 			git_error_set_after_callback(error);
790 			break;
791 		}
792 	}
793 
794 	if (error == GIT_ITEROVER)
795 		error = 0;
796 
797 	git_reference_iterator_free(iter);
798 	return error;
799 }
800 
git_reference_foreach_glob(git_repository * repo,const char * glob,git_reference_foreach_name_cb callback,void * payload)801 int git_reference_foreach_glob(
802 	git_repository *repo,
803 	const char *glob,
804 	git_reference_foreach_name_cb callback,
805 	void *payload)
806 {
807 	git_reference_iterator *iter;
808 	const char *refname;
809 	int error;
810 
811 	if ((error = git_reference_iterator_glob_new(&iter, repo, glob)) < 0)
812 		return error;
813 
814 	while (!(error = git_reference_next_name(&refname, iter))) {
815 		if ((error = callback(refname, payload)) != 0) {
816 			git_error_set_after_callback(error);
817 			break;
818 		}
819 	}
820 
821 	if (error == GIT_ITEROVER)
822 		error = 0;
823 
824 	git_reference_iterator_free(iter);
825 	return error;
826 }
827 
git_reference_iterator_new(git_reference_iterator ** out,git_repository * repo)828 int git_reference_iterator_new(git_reference_iterator **out, git_repository *repo)
829 {
830 	git_refdb *refdb;
831 
832 	if (git_repository_refdb__weakptr(&refdb, repo) < 0)
833 		return -1;
834 
835 	return git_refdb_iterator(out, refdb, NULL);
836 }
837 
git_reference_iterator_glob_new(git_reference_iterator ** out,git_repository * repo,const char * glob)838 int git_reference_iterator_glob_new(
839 	git_reference_iterator **out, git_repository *repo, const char *glob)
840 {
841 	git_refdb *refdb;
842 
843 	if (git_repository_refdb__weakptr(&refdb, repo) < 0)
844 		return -1;
845 
846 	return git_refdb_iterator(out, refdb, glob);
847 }
848 
git_reference_next(git_reference ** out,git_reference_iterator * iter)849 int git_reference_next(git_reference **out, git_reference_iterator *iter)
850 {
851 	return git_refdb_iterator_next(out, iter);
852 }
853 
git_reference_next_name(const char ** out,git_reference_iterator * iter)854 int git_reference_next_name(const char **out, git_reference_iterator *iter)
855 {
856 	return git_refdb_iterator_next_name(out, iter);
857 }
858 
git_reference_iterator_free(git_reference_iterator * iter)859 void git_reference_iterator_free(git_reference_iterator *iter)
860 {
861 	if (iter == NULL)
862 		return;
863 
864 	git_refdb_iterator_free(iter);
865 }
866 
cb__reflist_add(const char * ref,void * data)867 static int cb__reflist_add(const char *ref, void *data)
868 {
869 	char *name = git__strdup(ref);
870 	GIT_ERROR_CHECK_ALLOC(name);
871 	return git_vector_insert((git_vector *)data, name);
872 }
873 
git_reference_list(git_strarray * array,git_repository * repo)874 int git_reference_list(
875 	git_strarray *array,
876 	git_repository *repo)
877 {
878 	git_vector ref_list;
879 
880 	assert(array && repo);
881 
882 	array->strings = NULL;
883 	array->count = 0;
884 
885 	if (git_vector_init(&ref_list, 8, NULL) < 0)
886 		return -1;
887 
888 	if (git_reference_foreach_name(
889 			repo, &cb__reflist_add, (void *)&ref_list) < 0) {
890 		git_vector_free(&ref_list);
891 		return -1;
892 	}
893 
894 	array->strings = (char **)git_vector_detach(&array->count, NULL, &ref_list);
895 
896 	return 0;
897 }
898 
is_valid_ref_char(char ch)899 static int is_valid_ref_char(char ch)
900 {
901 	if ((unsigned) ch <= ' ')
902 		return 0;
903 
904 	switch (ch) {
905 	case '~':
906 	case '^':
907 	case ':':
908 	case '\\':
909 	case '?':
910 	case '[':
911 	case '*':
912 		return 0;
913 	default:
914 		return 1;
915 	}
916 }
917 
ensure_segment_validity(const char * name)918 static int ensure_segment_validity(const char *name)
919 {
920 	const char *current = name;
921 	char prev = '\0';
922 	const int lock_len = (int)strlen(GIT_FILELOCK_EXTENSION);
923 	int segment_len;
924 
925 	if (*current == '.')
926 		return -1; /* Refname starts with "." */
927 
928 	for (current = name; ; current++) {
929 		if (*current == '\0' || *current == '/')
930 			break;
931 
932 		if (!is_valid_ref_char(*current))
933 			return -1; /* Illegal character in refname */
934 
935 		if (prev == '.' && *current == '.')
936 			return -1; /* Refname contains ".." */
937 
938 		if (prev == '@' && *current == '{')
939 			return -1; /* Refname contains "@{" */
940 
941 		prev = *current;
942 	}
943 
944 	segment_len = (int)(current - name);
945 
946 	/* A refname component can not end with ".lock" */
947 	if (segment_len >= lock_len &&
948 		!memcmp(current - lock_len, GIT_FILELOCK_EXTENSION, lock_len))
949 			return -1;
950 
951 	return segment_len;
952 }
953 
is_all_caps_and_underscore(const char * name,size_t len)954 static bool is_all_caps_and_underscore(const char *name, size_t len)
955 {
956 	size_t i;
957 	char c;
958 
959 	assert(name && len > 0);
960 
961 	for (i = 0; i < len; i++)
962 	{
963 		c = name[i];
964 		if ((c < 'A' || c > 'Z') && c != '_')
965 			return false;
966 	}
967 
968 	if (*name == '_' || name[len - 1] == '_')
969 		return false;
970 
971 	return true;
972 }
973 
974 /* Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100 */
git_reference__normalize_name(git_buf * buf,const char * name,unsigned int flags)975 int git_reference__normalize_name(
976 	git_buf *buf,
977 	const char *name,
978 	unsigned int flags)
979 {
980 	const char *current;
981 	int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
982 	unsigned int process_flags;
983 	bool normalize = (buf != NULL);
984 	bool validate = (flags & GIT_REFERENCE_FORMAT__VALIDATION_DISABLE) == 0;
985 
986 #ifdef GIT_USE_ICONV
987 	git_path_iconv_t ic = GIT_PATH_ICONV_INIT;
988 #endif
989 
990 	assert(name);
991 
992 	process_flags = flags;
993 	current = (char *)name;
994 
995 	if (validate && *current == '/')
996 		goto cleanup;
997 
998 	if (normalize)
999 		git_buf_clear(buf);
1000 
1001 #ifdef GIT_USE_ICONV
1002 	if ((flags & GIT_REFERENCE_FORMAT__PRECOMPOSE_UNICODE) != 0) {
1003 		size_t namelen = strlen(current);
1004 		if ((error = git_path_iconv_init_precompose(&ic)) < 0 ||
1005 			(error = git_path_iconv(&ic, &current, &namelen)) < 0)
1006 			goto cleanup;
1007 		error = GIT_EINVALIDSPEC;
1008 	}
1009 #endif
1010 
1011 	if (!validate) {
1012 		git_buf_sets(buf, current);
1013 
1014 		error = git_buf_oom(buf) ? -1 : 0;
1015 		goto cleanup;
1016 	}
1017 
1018 	while (true) {
1019 		segment_len = ensure_segment_validity(current);
1020 		if (segment_len < 0) {
1021 			if ((process_flags & GIT_REFERENCE_FORMAT_REFSPEC_PATTERN) &&
1022 					current[0] == '*' &&
1023 					(current[1] == '\0' || current[1] == '/')) {
1024 				/* Accept one wildcard as a full refname component. */
1025 				process_flags &= ~GIT_REFERENCE_FORMAT_REFSPEC_PATTERN;
1026 				segment_len = 1;
1027 			} else
1028 				goto cleanup;
1029 		}
1030 
1031 		if (segment_len > 0) {
1032 			if (normalize) {
1033 				size_t cur_len = git_buf_len(buf);
1034 
1035 				git_buf_joinpath(buf, git_buf_cstr(buf), current);
1036 				git_buf_truncate(buf,
1037 					cur_len + segment_len + (segments_count ? 1 : 0));
1038 
1039 				if (git_buf_oom(buf)) {
1040 					error = -1;
1041 					goto cleanup;
1042 				}
1043 			}
1044 
1045 			segments_count++;
1046 		}
1047 
1048 		/* No empty segment is allowed when not normalizing */
1049 		if (segment_len == 0 && !normalize)
1050 			goto cleanup;
1051 
1052 		if (current[segment_len] == '\0')
1053 			break;
1054 
1055 		current += segment_len + 1;
1056 	}
1057 
1058 	/* A refname can not be empty */
1059 	if (segment_len == 0 && segments_count == 0)
1060 		goto cleanup;
1061 
1062 	/* A refname can not end with "." */
1063 	if (current[segment_len - 1] == '.')
1064 		goto cleanup;
1065 
1066 	/* A refname can not end with "/" */
1067 	if (current[segment_len - 1] == '/')
1068 		goto cleanup;
1069 
1070 	if ((segments_count == 1 ) && !(flags & GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL))
1071 		goto cleanup;
1072 
1073 	if ((segments_count == 1 ) &&
1074 	    !(flags & GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND) &&
1075 		!(is_all_caps_and_underscore(name, (size_t)segment_len) ||
1076 			((flags & GIT_REFERENCE_FORMAT_REFSPEC_PATTERN) && !strcmp("*", name))))
1077 			goto cleanup;
1078 
1079 	if ((segments_count > 1)
1080 		&& (is_all_caps_and_underscore(name, strchr(name, '/') - name)))
1081 			goto cleanup;
1082 
1083 	error = 0;
1084 
1085 cleanup:
1086 	if (error == GIT_EINVALIDSPEC)
1087 		git_error_set(
1088 			GIT_ERROR_REFERENCE,
1089 			"the given reference name '%s' is not valid", name);
1090 
1091 	if (error && normalize)
1092 		git_buf_dispose(buf);
1093 
1094 #ifdef GIT_USE_ICONV
1095 	git_path_iconv_clear(&ic);
1096 #endif
1097 
1098 	return error;
1099 }
1100 
git_reference_normalize_name(char * buffer_out,size_t buffer_size,const char * name,unsigned int flags)1101 int git_reference_normalize_name(
1102 	char *buffer_out,
1103 	size_t buffer_size,
1104 	const char *name,
1105 	unsigned int flags)
1106 {
1107 	git_buf buf = GIT_BUF_INIT;
1108 	int error;
1109 
1110 	if ((error = git_reference__normalize_name(&buf, name, flags)) < 0)
1111 		goto cleanup;
1112 
1113 	if (git_buf_len(&buf) > buffer_size - 1) {
1114 		git_error_set(
1115 		GIT_ERROR_REFERENCE,
1116 		"the provided buffer is too short to hold the normalization of '%s'", name);
1117 		error = GIT_EBUFS;
1118 		goto cleanup;
1119 	}
1120 
1121 	git_buf_copy_cstr(buffer_out, buffer_size, &buf);
1122 
1123 	error = 0;
1124 
1125 cleanup:
1126 	git_buf_dispose(&buf);
1127 	return error;
1128 }
1129 
1130 #define GIT_REFERENCE_TYPEMASK (GIT_REFERENCE_DIRECT | GIT_REFERENCE_SYMBOLIC)
1131 
git_reference_cmp(const git_reference * ref1,const git_reference * ref2)1132 int git_reference_cmp(
1133 	const git_reference *ref1,
1134 	const git_reference *ref2)
1135 {
1136 	git_reference_t type1, type2;
1137 	assert(ref1 && ref2);
1138 
1139 	type1 = git_reference_type(ref1);
1140 	type2 = git_reference_type(ref2);
1141 
1142 	/* let's put symbolic refs before OIDs */
1143 	if (type1 != type2)
1144 		return (type1 == GIT_REFERENCE_SYMBOLIC) ? -1 : 1;
1145 
1146 	if (type1 == GIT_REFERENCE_SYMBOLIC)
1147 		return strcmp(ref1->target.symbolic, ref2->target.symbolic);
1148 
1149 	return git_oid__cmp(&ref1->target.oid, &ref2->target.oid);
1150 }
1151 
1152 /**
1153  * Get the end of a chain of references. If the final one is not
1154  * found, we return the reference just before that.
1155  */
get_terminal(git_reference ** out,git_repository * repo,const char * ref_name,int nesting)1156 static int get_terminal(git_reference **out, git_repository *repo, const char *ref_name, int nesting)
1157 {
1158 	git_reference *ref;
1159 	int error = 0;
1160 
1161 	if (nesting > MAX_NESTING_LEVEL) {
1162 		git_error_set(GIT_ERROR_REFERENCE, "reference chain too deep (%d)", nesting);
1163 		return GIT_ENOTFOUND;
1164 	}
1165 
1166 	/* set to NULL to let the caller know that they're at the end of the chain */
1167 	if ((error = git_reference_lookup(&ref, repo, ref_name)) < 0) {
1168 		*out = NULL;
1169 		return error;
1170 	}
1171 
1172 	if (git_reference_type(ref) == GIT_REFERENCE_DIRECT) {
1173 		*out = ref;
1174 		error = 0;
1175 	} else {
1176 		error = get_terminal(out, repo, git_reference_symbolic_target(ref), nesting + 1);
1177 		if (error == GIT_ENOTFOUND && !*out)
1178 			*out = ref;
1179 		else
1180 			git_reference_free(ref);
1181 	}
1182 
1183 	return error;
1184 }
1185 
1186 /*
1187  * Starting with the reference given by `ref_name`, follows symbolic
1188  * references until a direct reference is found and updated the OID
1189  * on that direct reference to `oid`.
1190  */
git_reference__update_terminal(git_repository * repo,const char * ref_name,const git_oid * oid,const git_signature * sig,const char * log_message)1191 int git_reference__update_terminal(
1192 	git_repository *repo,
1193 	const char *ref_name,
1194 	const git_oid *oid,
1195 	const git_signature *sig,
1196 	const char *log_message)
1197 {
1198 	git_reference *ref = NULL, *ref2 = NULL;
1199 	git_signature *who = NULL;
1200 	const git_signature *to_use;
1201 	int error = 0;
1202 
1203 	if (!sig && (error = git_reference__log_signature(&who, repo)) < 0)
1204 		return error;
1205 
1206 	to_use = sig ? sig : who;
1207 	error = get_terminal(&ref, repo, ref_name, 0);
1208 
1209 	/* found a dangling symref */
1210 	if (error == GIT_ENOTFOUND && ref) {
1211 		assert(git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC);
1212 		git_error_clear();
1213 		error = reference__create(&ref2, repo, ref->target.symbolic, oid, NULL, 0, to_use,
1214 					  log_message, NULL, NULL);
1215 	} else if (error == GIT_ENOTFOUND) {
1216 		git_error_clear();
1217 		error = reference__create(&ref2, repo, ref_name, oid, NULL, 0, to_use,
1218 					  log_message, NULL, NULL);
1219 	}  else if (error == 0) {
1220 		assert(git_reference_type(ref) == GIT_REFERENCE_DIRECT);
1221 		error = reference__create(&ref2, repo, ref->name, oid, NULL, 1, to_use,
1222 					  log_message, &ref->target.oid, NULL);
1223 	}
1224 
1225 	git_reference_free(ref2);
1226 	git_reference_free(ref);
1227 	git_signature_free(who);
1228 	return error;
1229 }
1230 
commit_type(const git_commit * commit)1231 static const char *commit_type(const git_commit *commit)
1232 {
1233 	unsigned int count = git_commit_parentcount(commit);
1234 
1235 	if (count >= 2)
1236 		return " (merge)";
1237 	else if (count == 0)
1238 		return " (initial)";
1239 	else
1240 		return "";
1241 }
1242 
git_reference__update_for_commit(git_repository * repo,git_reference * ref,const char * ref_name,const git_oid * id,const char * operation)1243 int git_reference__update_for_commit(
1244 	git_repository *repo,
1245 	git_reference *ref,
1246 	const char *ref_name,
1247 	const git_oid *id,
1248 	const char *operation)
1249 {
1250 	git_reference *ref_new = NULL;
1251 	git_commit *commit = NULL;
1252 	git_buf reflog_msg = GIT_BUF_INIT;
1253 	const git_signature *who;
1254 	int error;
1255 
1256 	if ((error = git_commit_lookup(&commit, repo, id)) < 0 ||
1257 		(error = git_buf_printf(&reflog_msg, "%s%s: %s",
1258 			operation ? operation : "commit",
1259 			commit_type(commit),
1260 			git_commit_summary(commit))) < 0)
1261 		goto done;
1262 
1263 	who = git_commit_committer(commit);
1264 
1265 	if (ref) {
1266 		if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0)
1267 			return error;
1268 
1269 		error = reference__create(&ref_new, repo, ref->name, id, NULL, 1, who,
1270 					  git_buf_cstr(&reflog_msg), &ref->target.oid, NULL);
1271 	}
1272 	else
1273 		error = git_reference__update_terminal(
1274 			repo, ref_name, id, who, git_buf_cstr(&reflog_msg));
1275 
1276 done:
1277 	git_reference_free(ref_new);
1278 	git_buf_dispose(&reflog_msg);
1279 	git_commit_free(commit);
1280 	return error;
1281 }
1282 
git_reference_has_log(git_repository * repo,const char * refname)1283 int git_reference_has_log(git_repository *repo, const char *refname)
1284 {
1285 	int error;
1286 	git_refdb *refdb;
1287 
1288 	assert(repo && refname);
1289 
1290 	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
1291 		return error;
1292 
1293 	return git_refdb_has_log(refdb, refname);
1294 }
1295 
git_reference_ensure_log(git_repository * repo,const char * refname)1296 int git_reference_ensure_log(git_repository *repo, const char *refname)
1297 {
1298 	int error;
1299 	git_refdb *refdb;
1300 
1301 	assert(repo && refname);
1302 
1303 	if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
1304 		return error;
1305 
1306 	return git_refdb_ensure_log(refdb, refname);
1307 }
1308 
git_reference__is_branch(const char * ref_name)1309 int git_reference__is_branch(const char *ref_name)
1310 {
1311 	return git__prefixcmp(ref_name, GIT_REFS_HEADS_DIR) == 0;
1312 }
1313 
git_reference_is_branch(const git_reference * ref)1314 int git_reference_is_branch(const git_reference *ref)
1315 {
1316 	assert(ref);
1317 	return git_reference__is_branch(ref->name);
1318 }
1319 
git_reference__is_remote(const char * ref_name)1320 int git_reference__is_remote(const char *ref_name)
1321 {
1322 	return git__prefixcmp(ref_name, GIT_REFS_REMOTES_DIR) == 0;
1323 }
1324 
git_reference_is_remote(const git_reference * ref)1325 int git_reference_is_remote(const git_reference *ref)
1326 {
1327 	assert(ref);
1328 	return git_reference__is_remote(ref->name);
1329 }
1330 
git_reference__is_tag(const char * ref_name)1331 int git_reference__is_tag(const char *ref_name)
1332 {
1333 	return git__prefixcmp(ref_name, GIT_REFS_TAGS_DIR) == 0;
1334 }
1335 
git_reference_is_tag(const git_reference * ref)1336 int git_reference_is_tag(const git_reference *ref)
1337 {
1338 	assert(ref);
1339 	return git_reference__is_tag(ref->name);
1340 }
1341 
git_reference__is_note(const char * ref_name)1342 int git_reference__is_note(const char *ref_name)
1343 {
1344 	return git__prefixcmp(ref_name, GIT_REFS_NOTES_DIR) == 0;
1345 }
1346 
git_reference_is_note(const git_reference * ref)1347 int git_reference_is_note(const git_reference *ref)
1348 {
1349 	assert(ref);
1350 	return git_reference__is_note(ref->name);
1351 }
1352 
peel_error(int error,const git_reference * ref,const char * msg)1353 static int peel_error(int error, const git_reference *ref, const char* msg)
1354 {
1355 	git_error_set(
1356 		GIT_ERROR_INVALID,
1357 		"the reference '%s' cannot be peeled - %s", git_reference_name(ref), msg);
1358 	return error;
1359 }
1360 
git_reference_peel(git_object ** peeled,const git_reference * ref,git_object_t target_type)1361 int git_reference_peel(
1362 	git_object **peeled,
1363 	const git_reference *ref,
1364 	git_object_t target_type)
1365 {
1366 	const git_reference *resolved = NULL;
1367 	git_reference *allocated = NULL;
1368 	git_object *target = NULL;
1369 	int error;
1370 
1371 	assert(ref);
1372 
1373 	if (ref->type == GIT_REFERENCE_DIRECT) {
1374 		resolved = ref;
1375 	} else {
1376 		if ((error = git_reference_resolve(&allocated, ref)) < 0)
1377 			return peel_error(error, ref, "Cannot resolve reference");
1378 
1379 		resolved = allocated;
1380 	}
1381 
1382 	/*
1383 	 * If we try to peel an object to a tag, we cannot use
1384 	 * the fully peeled object, as that will always resolve
1385 	 * to a commit. So we only want to use the peeled value
1386 	 * if it is not zero and the target is not a tag.
1387 	 */
1388 	if (target_type != GIT_OBJECT_TAG && !git_oid_iszero(&resolved->peel)) {
1389 		error = git_object_lookup(&target,
1390 			git_reference_owner(ref), &resolved->peel, GIT_OBJECT_ANY);
1391 	} else {
1392 		error = git_object_lookup(&target,
1393 			git_reference_owner(ref), &resolved->target.oid, GIT_OBJECT_ANY);
1394 	}
1395 
1396 	if (error < 0) {
1397 		peel_error(error, ref, "Cannot retrieve reference target");
1398 		goto cleanup;
1399 	}
1400 
1401 	if (target_type == GIT_OBJECT_ANY && git_object_type(target) != GIT_OBJECT_TAG)
1402 		error = git_object_dup(peeled, target);
1403 	else
1404 		error = git_object_peel(peeled, target, target_type);
1405 
1406 cleanup:
1407 	git_object_free(target);
1408 	git_reference_free(allocated);
1409 
1410 	return error;
1411 }
1412 
git_reference__is_valid_name(const char * refname,unsigned int flags)1413 int git_reference__is_valid_name(const char *refname, unsigned int flags)
1414 {
1415 	if (git_reference__normalize_name(NULL, refname, flags) < 0) {
1416 		git_error_clear();
1417 		return false;
1418 	}
1419 
1420 	return true;
1421 }
1422 
git_reference_is_valid_name(const char * refname)1423 int git_reference_is_valid_name(const char *refname)
1424 {
1425 	return git_reference__is_valid_name(refname, GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL);
1426 }
1427 
git_reference__shorthand(const char * name)1428 const char *git_reference__shorthand(const char *name)
1429 {
1430 	if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR))
1431 		return name + strlen(GIT_REFS_HEADS_DIR);
1432 	else if (!git__prefixcmp(name, GIT_REFS_TAGS_DIR))
1433 		return name + strlen(GIT_REFS_TAGS_DIR);
1434 	else if (!git__prefixcmp(name, GIT_REFS_REMOTES_DIR))
1435 		return name + strlen(GIT_REFS_REMOTES_DIR);
1436 	else if (!git__prefixcmp(name, GIT_REFS_DIR))
1437 		return name + strlen(GIT_REFS_DIR);
1438 
1439 	/* No shorthands are avaiable, so just return the name */
1440 	return name;
1441 }
1442 
git_reference_shorthand(const git_reference * ref)1443 const char *git_reference_shorthand(const git_reference *ref)
1444 {
1445 	return git_reference__shorthand(ref->name);
1446 }
1447 
git_reference__is_unborn_head(bool * unborn,const git_reference * ref,git_repository * repo)1448 int git_reference__is_unborn_head(bool *unborn, const git_reference *ref, git_repository *repo)
1449 {
1450 	int error;
1451 	git_reference *tmp_ref;
1452 	assert(unborn && ref && repo);
1453 
1454 	if (ref->type == GIT_REFERENCE_DIRECT) {
1455 		*unborn = 0;
1456 		return 0;
1457 	}
1458 
1459 	error = git_reference_lookup_resolved(&tmp_ref, repo, ref->name, -1);
1460 	git_reference_free(tmp_ref);
1461 
1462 	if (error != 0 && error != GIT_ENOTFOUND)
1463 		return error;
1464 	else if (error == GIT_ENOTFOUND && git__strcmp(ref->name, GIT_HEAD_FILE) == 0)
1465 		*unborn = true;
1466 	else
1467 		*unborn = false;
1468 
1469 	return 0;
1470 }
1471