File Coverage

deps/libgit2/src/refs.c
Criterion Covered Total %
statement 430 579 74.2
branch 244 450 54.2
condition n/a
subroutine n/a
pod n/a
total 674 1029 65.5


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