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