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