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