File Coverage

deps/libgit2/src/libgit2/refs.c
Criterion Covered Total %
statement 450 607 74.1
branch 250 460 54.3
condition n/a
subroutine n/a
pod n/a
total 700 1067 65.6


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