]> git.proxmox.com Git - libgit2.git/blob - src/refs.c
trim whitespace when parsing loose refs
[libgit2.git] / src / refs.c
1 /*
2 * Copyright (C) 2009-2012 the libgit2 contributors
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 "pack.h"
13 #include "reflog.h"
14
15 #include <git2/tag.h>
16 #include <git2/object.h>
17
18 GIT__USE_STRMAP;
19
20 #define DEFAULT_NESTING_LEVEL 5
21 #define MAX_NESTING_LEVEL 10
22
23 enum {
24 GIT_PACKREF_HAS_PEEL = 1,
25 GIT_PACKREF_WAS_LOOSE = 2
26 };
27
28 struct packref {
29 git_oid oid;
30 git_oid peel;
31 char flags;
32 char name[GIT_FLEX_ARRAY];
33 };
34
35 static int reference_read(
36 git_buf *file_content,
37 time_t *mtime,
38 const char *repo_path,
39 const char *ref_name,
40 int *updated);
41
42 /* loose refs */
43 static int loose_parse_symbolic(git_reference *ref, git_buf *file_content);
44 static int loose_parse_oid(git_oid *ref, git_buf *file_content);
45 static int loose_lookup(git_reference *ref);
46 static int loose_lookup_to_packfile(struct packref **ref_out,
47 git_repository *repo, const char *name);
48 static int loose_write(git_reference *ref);
49
50 /* packed refs */
51 static int packed_parse_peel(struct packref *tag_ref,
52 const char **buffer_out, const char *buffer_end);
53 static int packed_parse_oid(struct packref **ref_out,
54 const char **buffer_out, const char *buffer_end);
55 static int packed_load(git_repository *repo);
56 static int packed_loadloose(git_repository *repository);
57 static int packed_write_ref(struct packref *ref, git_filebuf *file);
58 static int packed_find_peel(git_repository *repo, struct packref *ref);
59 static int packed_remove_loose(git_repository *repo, git_vector *packing_list);
60 static int packed_sort(const void *a, const void *b);
61 static int packed_lookup(git_reference *ref);
62 static int packed_write(git_repository *repo);
63
64 /* internal helpers */
65 static int reference_path_available(git_repository *repo,
66 const char *ref, const char *old_ref);
67 static int reference_delete(git_reference *ref);
68 static int reference_lookup(git_reference *ref);
69
70 /* name normalization */
71 static int normalize_name(char *buffer_out, size_t out_size,
72 const char *name, int is_oid_ref);
73
74
75 void git_reference_free(git_reference *reference)
76 {
77 if (reference == NULL)
78 return;
79
80 git__free(reference->name);
81 reference->name = NULL;
82
83 if (reference->flags & GIT_REF_SYMBOLIC) {
84 git__free(reference->target.symbolic);
85 reference->target.symbolic = NULL;
86 }
87
88 git__free(reference);
89 }
90
91 static int reference_alloc(
92 git_reference **ref_out,
93 git_repository *repo,
94 const char *name)
95 {
96 git_reference *reference = NULL;
97
98 assert(ref_out && repo && name);
99
100 reference = git__malloc(sizeof(git_reference));
101 GITERR_CHECK_ALLOC(reference);
102
103 memset(reference, 0x0, sizeof(git_reference));
104 reference->owner = repo;
105
106 reference->name = git__strdup(name);
107 GITERR_CHECK_ALLOC(reference->name);
108
109 *ref_out = reference;
110 return 0;
111 }
112
113 static int reference_read(
114 git_buf *file_content,
115 time_t *mtime,
116 const char *repo_path,
117 const char *ref_name,
118 int *updated)
119 {
120 git_buf path = GIT_BUF_INIT;
121 int result;
122
123 assert(file_content && repo_path && ref_name);
124
125 /* Determine the full path of the file */
126 if (git_buf_joinpath(&path, repo_path, ref_name) < 0)
127 return -1;
128
129 result = git_futils_readbuffer_updated(file_content, path.ptr, mtime, updated);
130 git_buf_free(&path);
131
132 return result;
133 }
134
135 static int loose_parse_symbolic(git_reference *ref, git_buf *file_content)
136 {
137 const unsigned int header_len = (unsigned int)strlen(GIT_SYMREF);
138 const char *refname_start;
139
140 refname_start = (const char *)file_content->ptr;
141
142 if (git_buf_len(file_content) < header_len + 1) {
143 giterr_set(GITERR_REFERENCE, "Corrupted loose reference file");
144 return -1;
145 }
146
147 /*
148 * Assume we have already checked for the header
149 * before calling this function
150 */
151 refname_start += header_len;
152
153 ref->target.symbolic = git__strdup(refname_start);
154 GITERR_CHECK_ALLOC(ref->target.symbolic);
155
156 return 0;
157 }
158
159 static int loose_parse_oid(git_oid *oid, git_buf *file_content)
160 {
161 /* File format: 40 chars (OID) */
162 if (git_buf_len(file_content) == GIT_OID_HEXSZ &&
163 git_oid_fromstr(oid, git_buf_cstr(file_content)) == 0)
164 return 0;
165
166 giterr_set(GITERR_REFERENCE, "Corrupted loose reference file");
167 return -1;
168 }
169
170 static git_ref_t loose_guess_rtype(const git_buf *full_path)
171 {
172 git_buf ref_file = GIT_BUF_INIT;
173 git_ref_t type;
174
175 type = GIT_REF_INVALID;
176
177 if (git_futils_readbuffer(&ref_file, full_path->ptr) == 0) {
178 if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0)
179 type = GIT_REF_SYMBOLIC;
180 else
181 type = GIT_REF_OID;
182 }
183
184 git_buf_free(&ref_file);
185 return type;
186 }
187
188 static int loose_lookup(git_reference *ref)
189 {
190 int result, updated;
191 git_buf ref_file = GIT_BUF_INIT;
192
193 result = reference_read(&ref_file, &ref->mtime,
194 ref->owner->path_repository, ref->name, &updated);
195
196 if (result < 0)
197 return result;
198
199 if (!updated)
200 return 0;
201
202 git_buf_rtrim(&ref_file);
203
204 if (ref->flags & GIT_REF_SYMBOLIC) {
205 git__free(ref->target.symbolic);
206 ref->target.symbolic = NULL;
207 }
208
209 ref->flags = 0;
210
211 if (git__prefixcmp((const char *)(ref_file.ptr), GIT_SYMREF) == 0) {
212 ref->flags |= GIT_REF_SYMBOLIC;
213 result = loose_parse_symbolic(ref, &ref_file);
214 } else {
215 ref->flags |= GIT_REF_OID;
216 result = loose_parse_oid(&ref->target.oid, &ref_file);
217 }
218
219 git_buf_free(&ref_file);
220 return result;
221 }
222
223 static int loose_lookup_to_packfile(
224 struct packref **ref_out,
225 git_repository *repo,
226 const char *name)
227 {
228 git_buf ref_file = GIT_BUF_INIT;
229 struct packref *ref = NULL;
230 size_t name_len;
231
232 *ref_out = NULL;
233
234 if (reference_read(&ref_file, NULL, repo->path_repository, name, NULL) < 0)
235 return -1;
236
237 git_buf_rtrim(&ref_file);
238
239 name_len = strlen(name);
240 ref = git__malloc(sizeof(struct packref) + name_len + 1);
241 GITERR_CHECK_ALLOC(ref);
242
243 memcpy(ref->name, name, name_len);
244 ref->name[name_len] = 0;
245
246 if (loose_parse_oid(&ref->oid, &ref_file) < 0) {
247 git_buf_free(&ref_file);
248 git__free(ref);
249 return -1;
250 }
251
252 ref->flags = GIT_PACKREF_WAS_LOOSE;
253
254 *ref_out = ref;
255 git_buf_free(&ref_file);
256 return 0;
257 }
258
259 static int loose_write(git_reference *ref)
260 {
261 git_filebuf file = GIT_FILEBUF_INIT;
262 git_buf ref_path = GIT_BUF_INIT;
263 struct stat st;
264
265 if (git_buf_joinpath(&ref_path, ref->owner->path_repository, ref->name) < 0)
266 return -1;
267
268 /* Remove a possibly existing empty directory hierarchy
269 * which name would collide with the reference name
270 */
271 if (git_path_isdir(git_buf_cstr(&ref_path)) &&
272 (git_futils_rmdir_r(git_buf_cstr(&ref_path), GIT_DIRREMOVAL_ONLY_EMPTY_DIRS) < 0)) {
273 git_buf_free(&ref_path);
274 return -1;
275 }
276
277 if (git_filebuf_open(&file, ref_path.ptr, GIT_FILEBUF_FORCE) < 0) {
278 git_buf_free(&ref_path);
279 return -1;
280 }
281
282 git_buf_free(&ref_path);
283
284 if (ref->flags & GIT_REF_OID) {
285 char oid[GIT_OID_HEXSZ + 1];
286
287 git_oid_fmt(oid, &ref->target.oid);
288 oid[GIT_OID_HEXSZ] = '\0';
289
290 git_filebuf_printf(&file, "%s\n", oid);
291
292 } else if (ref->flags & GIT_REF_SYMBOLIC) {
293 git_filebuf_printf(&file, GIT_SYMREF "%s\n", ref->target.symbolic);
294 } else {
295 assert(0); /* don't let this happen */
296 }
297
298 if (p_stat(ref_path.ptr, &st) == 0)
299 ref->mtime = st.st_mtime;
300
301 return git_filebuf_commit(&file, GIT_REFS_FILE_MODE);
302 }
303
304 static int packed_parse_peel(
305 struct packref *tag_ref,
306 const char **buffer_out,
307 const char *buffer_end)
308 {
309 const char *buffer = *buffer_out + 1;
310
311 assert(buffer[-1] == '^');
312
313 /* Ensure it's not the first entry of the file */
314 if (tag_ref == NULL)
315 goto corrupt;
316
317 /* Ensure reference is a tag */
318 if (git__prefixcmp(tag_ref->name, GIT_REFS_TAGS_DIR) != 0)
319 goto corrupt;
320
321 if (buffer + GIT_OID_HEXSZ >= buffer_end)
322 goto corrupt;
323
324 /* Is this a valid object id? */
325 if (git_oid_fromstr(&tag_ref->peel, buffer) < 0)
326 goto corrupt;
327
328 buffer = buffer + GIT_OID_HEXSZ;
329 if (*buffer == '\r')
330 buffer++;
331
332 if (*buffer != '\n')
333 goto corrupt;
334
335 *buffer_out = buffer + 1;
336 return 0;
337
338 corrupt:
339 giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");
340 return -1;
341 }
342
343 static int packed_parse_oid(
344 struct packref **ref_out,
345 const char **buffer_out,
346 const char *buffer_end)
347 {
348 struct packref *ref = NULL;
349
350 const char *buffer = *buffer_out;
351 const char *refname_begin, *refname_end;
352
353 size_t refname_len;
354 git_oid id;
355
356 refname_begin = (buffer + GIT_OID_HEXSZ + 1);
357 if (refname_begin >= buffer_end || refname_begin[-1] != ' ')
358 goto corrupt;
359
360 /* Is this a valid object id? */
361 if (git_oid_fromstr(&id, buffer) < 0)
362 goto corrupt;
363
364 refname_end = memchr(refname_begin, '\n', buffer_end - refname_begin);
365 if (refname_end == NULL)
366 goto corrupt;
367
368 if (refname_end[-1] == '\r')
369 refname_end--;
370
371 refname_len = refname_end - refname_begin;
372
373 ref = git__malloc(sizeof(struct packref) + refname_len + 1);
374 GITERR_CHECK_ALLOC(ref);
375
376 memcpy(ref->name, refname_begin, refname_len);
377 ref->name[refname_len] = 0;
378
379 git_oid_cpy(&ref->oid, &id);
380
381 ref->flags = 0;
382
383 *ref_out = ref;
384 *buffer_out = refname_end + 1;
385
386 return 0;
387
388 corrupt:
389 git__free(ref);
390 giterr_set(GITERR_REFERENCE, "The packed references file is corrupted");
391 return -1;
392 }
393
394 static int packed_load(git_repository *repo)
395 {
396 int result, updated;
397 git_buf packfile = GIT_BUF_INIT;
398 const char *buffer_start, *buffer_end;
399 git_refcache *ref_cache = &repo->references;
400
401 /* First we make sure we have allocated the hash table */
402 if (ref_cache->packfile == NULL) {
403 ref_cache->packfile = git_strmap_alloc();
404 GITERR_CHECK_ALLOC(ref_cache->packfile);
405 }
406
407 result = reference_read(&packfile, &ref_cache->packfile_time,
408 repo->path_repository, GIT_PACKEDREFS_FILE, &updated);
409
410 /*
411 * If we couldn't find the file, we need to clear the table and
412 * return. On any other error, we return that error. If everything
413 * went fine and the file wasn't updated, then there's nothing new
414 * for us here, so just return. Anything else means we need to
415 * refresh the packed refs.
416 */
417 if (result == GIT_ENOTFOUND) {
418 git_strmap_clear(ref_cache->packfile);
419 return 0;
420 }
421
422 if (result < 0)
423 return -1;
424
425 if (!updated)
426 return 0;
427
428 /*
429 * At this point, we want to refresh the packed refs. We already
430 * have the contents in our buffer.
431 */
432 git_strmap_clear(ref_cache->packfile);
433
434 buffer_start = (const char *)packfile.ptr;
435 buffer_end = (const char *)(buffer_start) + packfile.size;
436
437 while (buffer_start < buffer_end && buffer_start[0] == '#') {
438 buffer_start = strchr(buffer_start, '\n');
439 if (buffer_start == NULL)
440 goto parse_failed;
441
442 buffer_start++;
443 }
444
445 while (buffer_start < buffer_end) {
446 int err;
447 struct packref *ref = NULL;
448
449 if (packed_parse_oid(&ref, &buffer_start, buffer_end) < 0)
450 goto parse_failed;
451
452 if (buffer_start[0] == '^') {
453 if (packed_parse_peel(ref, &buffer_start, buffer_end) < 0)
454 goto parse_failed;
455 }
456
457 git_strmap_insert(ref_cache->packfile, ref->name, ref, err);
458 if (err < 0)
459 goto parse_failed;
460 }
461
462 git_buf_free(&packfile);
463 return 0;
464
465 parse_failed:
466 git_strmap_free(ref_cache->packfile);
467 ref_cache->packfile = NULL;
468 git_buf_free(&packfile);
469 return -1;
470 }
471
472
473 struct dirent_list_data {
474 git_repository *repo;
475 size_t repo_path_len;
476 unsigned int list_flags;
477
478 int (*callback)(const char *, void *);
479 void *callback_payload;
480 int callback_error;
481 };
482
483 static int _dirent_loose_listall(void *_data, git_buf *full_path)
484 {
485 struct dirent_list_data *data = (struct dirent_list_data *)_data;
486 const char *file_path = full_path->ptr + data->repo_path_len;
487
488 if (git_path_isdir(full_path->ptr) == true)
489 return git_path_direach(full_path, _dirent_loose_listall, _data);
490
491 /* do not add twice a reference that exists already in the packfile */
492 if ((data->list_flags & GIT_REF_PACKED) != 0 &&
493 git_strmap_exists(data->repo->references.packfile, file_path))
494 return 0;
495
496 if (data->list_flags != GIT_REF_LISTALL) {
497 if ((data->list_flags & loose_guess_rtype(full_path)) == 0)
498 return 0; /* we are filtering out this reference */
499 }
500
501 if (data->callback(file_path, data->callback_payload))
502 data->callback_error = GIT_EUSER;
503
504 return data->callback_error;
505 }
506
507 static int _dirent_loose_load(void *data, git_buf *full_path)
508 {
509 git_repository *repository = (git_repository *)data;
510 void *old_ref = NULL;
511 struct packref *ref;
512 const char *file_path;
513 int err;
514
515 if (git_path_isdir(full_path->ptr) == true)
516 return git_path_direach(full_path, _dirent_loose_load, repository);
517
518 file_path = full_path->ptr + strlen(repository->path_repository);
519
520 if (loose_lookup_to_packfile(&ref, repository, file_path) < 0)
521 return -1;
522
523 git_strmap_insert2(
524 repository->references.packfile, ref->name, ref, old_ref, err);
525 if (err < 0) {
526 git__free(ref);
527 return -1;
528 }
529
530 git__free(old_ref);
531 return 0;
532 }
533
534 /*
535 * Load all the loose references from the repository
536 * into the in-memory Packfile, and build a vector with
537 * all the references so it can be written back to
538 * disk.
539 */
540 static int packed_loadloose(git_repository *repository)
541 {
542 git_buf refs_path = GIT_BUF_INIT;
543 int result;
544
545 /* the packfile must have been previously loaded! */
546 assert(repository->references.packfile);
547
548 if (git_buf_joinpath(&refs_path, repository->path_repository, GIT_REFS_DIR) < 0)
549 return -1;
550
551 /*
552 * Load all the loose files from disk into the Packfile table.
553 * This will overwrite any old packed entries with their
554 * updated loose versions
555 */
556 result = git_path_direach(&refs_path, _dirent_loose_load, repository);
557 git_buf_free(&refs_path);
558
559 return result;
560 }
561
562 /*
563 * Write a single reference into a packfile
564 */
565 static int packed_write_ref(struct packref *ref, git_filebuf *file)
566 {
567 char oid[GIT_OID_HEXSZ + 1];
568
569 git_oid_fmt(oid, &ref->oid);
570 oid[GIT_OID_HEXSZ] = 0;
571
572 /*
573 * For references that peel to an object in the repo, we must
574 * write the resulting peel on a separate line, e.g.
575 *
576 * 6fa8a902cc1d18527e1355773c86721945475d37 refs/tags/libgit2-0.4
577 * ^2ec0cb7959b0bf965d54f95453f5b4b34e8d3100
578 *
579 * This obviously only applies to tags.
580 * The required peels have already been loaded into `ref->peel_target`.
581 */
582 if (ref->flags & GIT_PACKREF_HAS_PEEL) {
583 char peel[GIT_OID_HEXSZ + 1];
584 git_oid_fmt(peel, &ref->peel);
585 peel[GIT_OID_HEXSZ] = 0;
586
587 if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0)
588 return -1;
589 } else {
590 if (git_filebuf_printf(file, "%s %s\n", oid, ref->name) < 0)
591 return -1;
592 }
593
594 return 0;
595 }
596
597 /*
598 * Find out what object this reference resolves to.
599 *
600 * For references that point to a 'big' tag (e.g. an
601 * actual tag object on the repository), we need to
602 * cache on the packfile the OID of the object to
603 * which that 'big tag' is pointing to.
604 */
605 static int packed_find_peel(git_repository *repo, struct packref *ref)
606 {
607 git_object *object;
608
609 if (ref->flags & GIT_PACKREF_HAS_PEEL)
610 return 0;
611
612 /*
613 * Only applies to tags, i.e. references
614 * in the /refs/tags folder
615 */
616 if (git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) != 0)
617 return 0;
618
619 /*
620 * Find the tagged object in the repository
621 */
622 if (git_object_lookup(&object, repo, &ref->oid, GIT_OBJ_ANY) < 0)
623 return -1;
624
625 /*
626 * If the tagged object is a Tag object, we need to resolve it;
627 * if the ref is actually a 'weak' ref, we don't need to resolve
628 * anything.
629 */
630 if (git_object_type(object) == GIT_OBJ_TAG) {
631 git_tag *tag = (git_tag *)object;
632
633 /*
634 * Find the object pointed at by this tag
635 */
636 git_oid_cpy(&ref->peel, git_tag_target_oid(tag));
637 ref->flags |= GIT_PACKREF_HAS_PEEL;
638
639 /*
640 * The reference has now cached the resolved OID, and is
641 * marked at such. When written to the packfile, it'll be
642 * accompanied by this resolved oid
643 */
644 }
645
646 git_object_free(object);
647 return 0;
648 }
649
650 /*
651 * Remove all loose references
652 *
653 * Once we have successfully written a packfile,
654 * all the loose references that were packed must be
655 * removed from disk.
656 *
657 * This is a dangerous method; make sure the packfile
658 * is well-written, because we are destructing references
659 * here otherwise.
660 */
661 static int packed_remove_loose(git_repository *repo, git_vector *packing_list)
662 {
663 unsigned int i;
664 git_buf full_path = GIT_BUF_INIT;
665 int failed = 0;
666
667 for (i = 0; i < packing_list->length; ++i) {
668 struct packref *ref = git_vector_get(packing_list, i);
669
670 if ((ref->flags & GIT_PACKREF_WAS_LOOSE) == 0)
671 continue;
672
673 if (git_buf_joinpath(&full_path, repo->path_repository, ref->name) < 0)
674 return -1; /* critical; do not try to recover on oom */
675
676 if (git_path_exists(full_path.ptr) == true && p_unlink(full_path.ptr) < 0) {
677 if (failed)
678 continue;
679
680 giterr_set(GITERR_REFERENCE,
681 "Failed to remove loose reference '%s' after packing: %s",
682 full_path.ptr, strerror(errno));
683
684 failed = 1;
685 }
686
687 /*
688 * if we fail to remove a single file, this is *not* good,
689 * but we should keep going and remove as many as possible.
690 * After we've removed as many files as possible, we return
691 * the error code anyway.
692 */
693 }
694
695 git_buf_free(&full_path);
696 return failed ? -1 : 0;
697 }
698
699 static int packed_sort(const void *a, const void *b)
700 {
701 const struct packref *ref_a = (const struct packref *)a;
702 const struct packref *ref_b = (const struct packref *)b;
703
704 return strcmp(ref_a->name, ref_b->name);
705 }
706
707 /*
708 * Write all the contents in the in-memory packfile to disk.
709 */
710 static int packed_write(git_repository *repo)
711 {
712 git_filebuf pack_file = GIT_FILEBUF_INIT;
713 unsigned int i;
714 git_buf pack_file_path = GIT_BUF_INIT;
715 git_vector packing_list;
716 unsigned int total_refs;
717
718 assert(repo && repo->references.packfile);
719
720 total_refs =
721 (unsigned int)git_strmap_num_entries(repo->references.packfile);
722
723 if (git_vector_init(&packing_list, total_refs, packed_sort) < 0)
724 return -1;
725
726 /* Load all the packfile into a vector */
727 {
728 struct packref *reference;
729
730 /* cannot fail: vector already has the right size */
731 git_strmap_foreach_value(repo->references.packfile, reference, {
732 git_vector_insert(&packing_list, reference);
733 });
734 }
735
736 /* sort the vector so the entries appear sorted on the packfile */
737 git_vector_sort(&packing_list);
738
739 /* Now we can open the file! */
740 if (git_buf_joinpath(&pack_file_path, repo->path_repository, GIT_PACKEDREFS_FILE) < 0)
741 goto cleanup_memory;
742
743 if (git_filebuf_open(&pack_file, pack_file_path.ptr, 0) < 0)
744 goto cleanup_packfile;
745
746 /* Packfiles have a header... apparently
747 * This is in fact not required, but we might as well print it
748 * just for kicks */
749 if (git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER) < 0)
750 goto cleanup_packfile;
751
752 for (i = 0; i < packing_list.length; ++i) {
753 struct packref *ref = (struct packref *)git_vector_get(&packing_list, i);
754
755 if (packed_find_peel(repo, ref) < 0)
756 goto cleanup_packfile;
757
758 if (packed_write_ref(ref, &pack_file) < 0)
759 goto cleanup_packfile;
760 }
761
762 /* if we've written all the references properly, we can commit
763 * the packfile to make the changes effective */
764 if (git_filebuf_commit(&pack_file, GIT_PACKEDREFS_FILE_MODE) < 0)
765 goto cleanup_memory;
766
767 /* when and only when the packfile has been properly written,
768 * we can go ahead and remove the loose refs */
769 if (packed_remove_loose(repo, &packing_list) < 0)
770 goto cleanup_memory;
771
772 {
773 struct stat st;
774 if (p_stat(pack_file_path.ptr, &st) == 0)
775 repo->references.packfile_time = st.st_mtime;
776 }
777
778 git_vector_free(&packing_list);
779 git_buf_free(&pack_file_path);
780
781 /* we're good now */
782 return 0;
783
784 cleanup_packfile:
785 git_filebuf_cleanup(&pack_file);
786
787 cleanup_memory:
788 git_vector_free(&packing_list);
789 git_buf_free(&pack_file_path);
790
791 return -1;
792 }
793
794 struct reference_available_t {
795 const char *new_ref;
796 const char *old_ref;
797 int available;
798 };
799
800 static int _reference_available_cb(const char *ref, void *data)
801 {
802 struct reference_available_t *d;
803
804 assert(ref && data);
805 d = (struct reference_available_t *)data;
806
807 if (!d->old_ref || strcmp(d->old_ref, ref)) {
808 size_t reflen = strlen(ref);
809 size_t newlen = strlen(d->new_ref);
810 size_t cmplen = reflen < newlen ? reflen : newlen;
811 const char *lead = reflen < newlen ? d->new_ref : ref;
812
813 if (!strncmp(d->new_ref, ref, cmplen) && lead[cmplen] == '/') {
814 d->available = 0;
815 return -1;
816 }
817 }
818
819 return 0;
820 }
821
822 static int reference_path_available(
823 git_repository *repo,
824 const char *ref,
825 const char* old_ref)
826 {
827 int error;
828 struct reference_available_t data;
829
830 data.new_ref = ref;
831 data.old_ref = old_ref;
832 data.available = 1;
833
834 error = git_reference_foreach(
835 repo, GIT_REF_LISTALL, _reference_available_cb, (void *)&data);
836 if (error < 0)
837 return error;
838
839 if (!data.available) {
840 giterr_set(GITERR_REFERENCE,
841 "The path to reference '%s' collides with an existing one", ref);
842 return -1;
843 }
844
845 return 0;
846 }
847
848 static int reference_exists(int *exists, git_repository *repo, const char *ref_name)
849 {
850 git_buf ref_path = GIT_BUF_INIT;
851
852 if (packed_load(repo) < 0)
853 return -1;
854
855 if (git_buf_joinpath(&ref_path, repo->path_repository, ref_name) < 0)
856 return -1;
857
858 if (git_path_isfile(ref_path.ptr) == true ||
859 git_strmap_exists(repo->references.packfile, ref_path.ptr))
860 {
861 *exists = 1;
862 } else {
863 *exists = 0;
864 }
865
866 git_buf_free(&ref_path);
867 return 0;
868 }
869
870 /*
871 * Check if a reference could be written to disk, based on:
872 *
873 * - Whether a reference with the same name already exists,
874 * and we are allowing or disallowing overwrites
875 *
876 * - Whether the name of the reference would collide with
877 * an existing path
878 */
879 static int reference_can_write(
880 git_repository *repo,
881 const char *refname,
882 const char *previous_name,
883 int force)
884 {
885 /* see if the reference shares a path with an existing reference;
886 * if a path is shared, we cannot create the reference, even when forcing */
887 if (reference_path_available(repo, refname, previous_name) < 0)
888 return -1;
889
890 /* check if the reference actually exists, but only if we are not forcing
891 * the rename. If we are forcing, it's OK to overwrite */
892 if (!force) {
893 int exists;
894
895 if (reference_exists(&exists, repo, refname) < 0)
896 return -1;
897
898 /* We cannot proceed if the reference already exists and we're not forcing
899 * the rename; the existing one would be overwritten */
900 if (exists) {
901 giterr_set(GITERR_REFERENCE,
902 "A reference with that name (%s) already exists", refname);
903 return GIT_EEXISTS;
904 }
905 }
906
907 /* FIXME: if the reference exists and we are forcing, do we really need to
908 * remove the reference first?
909 *
910 * Two cases:
911 *
912 * - the reference already exists and is loose: not a problem, the file
913 * gets overwritten on disk
914 *
915 * - the reference already exists and is packed: we write a new one as
916 * loose, which by all means renders the packed one useless
917 */
918
919 return 0;
920 }
921
922
923 static int packed_lookup(git_reference *ref)
924 {
925 struct packref *pack_ref = NULL;
926 git_strmap *packfile_refs;
927 khiter_t pos;
928
929 if (packed_load(ref->owner) < 0)
930 return -1;
931
932 /* maybe the packfile hasn't changed at all, so we don't
933 * have to re-lookup the reference */
934 if ((ref->flags & GIT_REF_PACKED) &&
935 ref->mtime == ref->owner->references.packfile_time)
936 return 0;
937
938 if (ref->flags & GIT_REF_SYMBOLIC) {
939 git__free(ref->target.symbolic);
940 ref->target.symbolic = NULL;
941 }
942
943 /* Look up on the packfile */
944 packfile_refs = ref->owner->references.packfile;
945 pos = git_strmap_lookup_index(packfile_refs, ref->name);
946 if (!git_strmap_valid_index(packfile_refs, pos)) {
947 giterr_set(GITERR_REFERENCE, "Reference '%s' not found", ref->name);
948 return GIT_ENOTFOUND;
949 }
950
951 pack_ref = git_strmap_value_at(packfile_refs, pos);
952
953 ref->flags = GIT_REF_OID | GIT_REF_PACKED;
954 ref->mtime = ref->owner->references.packfile_time;
955 git_oid_cpy(&ref->target.oid, &pack_ref->oid);
956
957 return 0;
958 }
959
960 static int reference_lookup(git_reference *ref)
961 {
962 int result;
963
964 result = loose_lookup(ref);
965 if (result == 0)
966 return 0;
967
968 /* only try to lookup this reference on the packfile if it
969 * wasn't found on the loose refs; not if there was a critical error */
970 if (result == GIT_ENOTFOUND) {
971 giterr_clear();
972 result = packed_lookup(ref);
973 if (result == 0)
974 return 0;
975 }
976
977 /* unexpected error; free the reference */
978 git_reference_free(ref);
979 return result;
980 }
981
982 /*
983 * Delete a reference.
984 * This is an internal method; the reference is removed
985 * from disk or the packfile, but the pointer is not freed
986 */
987 static int reference_delete(git_reference *ref)
988 {
989 int result;
990
991 assert(ref);
992
993 /* If the reference is packed, this is an expensive operation.
994 * We need to reload the packfile, remove the reference from the
995 * packing list, and repack */
996 if (ref->flags & GIT_REF_PACKED) {
997 git_strmap *packfile_refs;
998 struct packref *packref;
999 khiter_t pos;
1000
1001 /* load the existing packfile */
1002 if (packed_load(ref->owner) < 0)
1003 return -1;
1004
1005 packfile_refs = ref->owner->references.packfile;
1006 pos = git_strmap_lookup_index(packfile_refs, ref->name);
1007 if (!git_strmap_valid_index(packfile_refs, pos)) {
1008 giterr_set(GITERR_REFERENCE,
1009 "Reference %s stopped existing in the packfile", ref->name);
1010 return -1;
1011 }
1012
1013 packref = git_strmap_value_at(packfile_refs, pos);
1014 git_strmap_delete_at(packfile_refs, pos);
1015
1016 git__free(packref);
1017 if (packed_write(ref->owner) < 0)
1018 return -1;
1019
1020 /* If the reference is loose, we can just remove the reference
1021 * from the filesystem */
1022 } else {
1023 git_reference *ref_in_pack;
1024 git_buf full_path = GIT_BUF_INIT;
1025
1026 if (git_buf_joinpath(&full_path, ref->owner->path_repository, ref->name) < 0)
1027 return -1;
1028
1029 result = p_unlink(full_path.ptr);
1030 git_buf_free(&full_path); /* done with path at this point */
1031
1032 if (result < 0) {
1033 giterr_set(GITERR_OS, "Failed to unlink '%s'", full_path.ptr);
1034 return -1;
1035 }
1036
1037 /* When deleting a loose reference, we have to ensure that an older
1038 * packed version of it doesn't exist */
1039 if (git_reference_lookup(&ref_in_pack, ref->owner, ref->name) == 0) {
1040 assert((ref_in_pack->flags & GIT_REF_PACKED) != 0);
1041 return git_reference_delete(ref_in_pack);
1042 }
1043
1044 giterr_clear();
1045 }
1046
1047 return 0;
1048 }
1049
1050 int git_reference_delete(git_reference *ref)
1051 {
1052 int result = reference_delete(ref);
1053 git_reference_free(ref);
1054 return result;
1055 }
1056
1057 int git_reference_lookup(git_reference **ref_out,
1058 git_repository *repo, const char *name)
1059 {
1060 return git_reference_lookup_resolved(ref_out, repo, name, 0);
1061 }
1062
1063 int git_reference_name_to_oid(
1064 git_oid *out, git_repository *repo, const char *name)
1065 {
1066 int error;
1067 git_reference *ref;
1068
1069 if ((error = git_reference_lookup_resolved(&ref, repo, name, -1)) < 0)
1070 return error;
1071
1072 git_oid_cpy(out, git_reference_oid(ref));
1073 git_reference_free(ref);
1074 return 0;
1075 }
1076
1077 int git_reference_lookup_resolved(
1078 git_reference **ref_out,
1079 git_repository *repo,
1080 const char *name,
1081 int max_nesting)
1082 {
1083 git_reference *scan;
1084 int result, nesting;
1085
1086 assert(ref_out && repo && name);
1087
1088 *ref_out = NULL;
1089
1090 if (max_nesting > MAX_NESTING_LEVEL)
1091 max_nesting = MAX_NESTING_LEVEL;
1092 else if (max_nesting < 0)
1093 max_nesting = DEFAULT_NESTING_LEVEL;
1094
1095 scan = git__calloc(1, sizeof(git_reference));
1096 GITERR_CHECK_ALLOC(scan);
1097
1098 scan->name = git__calloc(GIT_REFNAME_MAX + 1, sizeof(char));
1099 GITERR_CHECK_ALLOC(scan->name);
1100
1101 if ((result = normalize_name(scan->name, GIT_REFNAME_MAX, name, 0)) < 0) {
1102 git_reference_free(scan);
1103 return result;
1104 }
1105
1106 scan->target.symbolic = git__strdup(scan->name);
1107 GITERR_CHECK_ALLOC(scan->target.symbolic);
1108
1109 scan->owner = repo;
1110 scan->flags = GIT_REF_SYMBOLIC;
1111
1112 for (nesting = max_nesting;
1113 nesting >= 0 && (scan->flags & GIT_REF_SYMBOLIC) != 0;
1114 nesting--)
1115 {
1116 if (nesting != max_nesting)
1117 strncpy(scan->name, scan->target.symbolic, GIT_REFNAME_MAX);
1118
1119 scan->mtime = 0;
1120
1121 if ((result = reference_lookup(scan)) < 0)
1122 return result; /* lookup git_reference_free on scan already */
1123 }
1124
1125 if ((scan->flags & GIT_REF_OID) == 0 && max_nesting != 0) {
1126 giterr_set(GITERR_REFERENCE,
1127 "Cannot resolve reference (>%u levels deep)", max_nesting);
1128 git_reference_free(scan);
1129 return -1;
1130 }
1131
1132 *ref_out = scan;
1133 return 0;
1134 }
1135
1136 /**
1137 * Getters
1138 */
1139 git_ref_t git_reference_type(git_reference *ref)
1140 {
1141 assert(ref);
1142
1143 if (ref->flags & GIT_REF_OID)
1144 return GIT_REF_OID;
1145
1146 if (ref->flags & GIT_REF_SYMBOLIC)
1147 return GIT_REF_SYMBOLIC;
1148
1149 return GIT_REF_INVALID;
1150 }
1151
1152 int git_reference_is_packed(git_reference *ref)
1153 {
1154 assert(ref);
1155 return !!(ref->flags & GIT_REF_PACKED);
1156 }
1157
1158 const char *git_reference_name(git_reference *ref)
1159 {
1160 assert(ref);
1161 return ref->name;
1162 }
1163
1164 git_repository *git_reference_owner(git_reference *ref)
1165 {
1166 assert(ref);
1167 return ref->owner;
1168 }
1169
1170 const git_oid *git_reference_oid(git_reference *ref)
1171 {
1172 assert(ref);
1173
1174 if ((ref->flags & GIT_REF_OID) == 0)
1175 return NULL;
1176
1177 return &ref->target.oid;
1178 }
1179
1180 const char *git_reference_target(git_reference *ref)
1181 {
1182 assert(ref);
1183
1184 if ((ref->flags & GIT_REF_SYMBOLIC) == 0)
1185 return NULL;
1186
1187 return ref->target.symbolic;
1188 }
1189
1190 int git_reference_create_symbolic(
1191 git_reference **ref_out,
1192 git_repository *repo,
1193 const char *name,
1194 const char *target,
1195 int force)
1196 {
1197 char normalized[GIT_REFNAME_MAX];
1198 git_reference *ref = NULL;
1199
1200 if (normalize_name(normalized, sizeof(normalized), name, 0) < 0)
1201 return -1;
1202
1203 if (reference_can_write(repo, normalized, NULL, force) < 0)
1204 return -1;
1205
1206 if (reference_alloc(&ref, repo, normalized) < 0)
1207 return -1;
1208
1209 ref->flags |= GIT_REF_SYMBOLIC;
1210
1211 /* set the target; this will normalize the name automatically
1212 * and write the reference on disk */
1213 if (git_reference_set_target(ref, target) < 0) {
1214 git_reference_free(ref);
1215 return -1;
1216 }
1217 if (ref_out == NULL) {
1218 git_reference_free(ref);
1219 } else {
1220 *ref_out = ref;
1221 }
1222
1223 return 0;
1224 }
1225
1226 int git_reference_create_oid(
1227 git_reference **ref_out,
1228 git_repository *repo,
1229 const char *name,
1230 const git_oid *id,
1231 int force)
1232 {
1233 git_reference *ref = NULL;
1234 char normalized[GIT_REFNAME_MAX];
1235
1236 if (normalize_name(normalized, sizeof(normalized), name, 1) < 0)
1237 return -1;
1238
1239 if (reference_can_write(repo, normalized, NULL, force) < 0)
1240 return -1;
1241
1242 if (reference_alloc(&ref, repo, name) < 0)
1243 return -1;
1244
1245 ref->flags |= GIT_REF_OID;
1246
1247 /* set the oid; this will write the reference on disk */
1248 if (git_reference_set_oid(ref, id) < 0) {
1249 git_reference_free(ref);
1250 return -1;
1251 }
1252
1253 if (ref_out == NULL) {
1254 git_reference_free(ref);
1255 } else {
1256 *ref_out = ref;
1257 }
1258
1259 return 0;
1260 }
1261 /*
1262 * Change the OID target of a reference.
1263 *
1264 * For both loose and packed references, just change
1265 * the oid in memory and (over)write the file in disk.
1266 *
1267 * We do not repack packed references because of performance
1268 * reasons.
1269 */
1270 int git_reference_set_oid(git_reference *ref, const git_oid *id)
1271 {
1272 git_odb *odb = NULL;
1273
1274 if ((ref->flags & GIT_REF_OID) == 0) {
1275 giterr_set(GITERR_REFERENCE, "Cannot set OID on symbolic reference");
1276 return -1;
1277 }
1278
1279 assert(ref->owner);
1280
1281 if (git_repository_odb__weakptr(&odb, ref->owner) < 0)
1282 return -1;
1283
1284 /* Don't let the user create references to OIDs that
1285 * don't exist in the ODB */
1286 if (!git_odb_exists(odb, id)) {
1287 giterr_set(GITERR_REFERENCE,
1288 "Target OID for the reference doesn't exist on the repository");
1289 return -1;
1290 }
1291
1292 /* Update the OID value on `ref` */
1293 git_oid_cpy(&ref->target.oid, id);
1294
1295 /* Write back to disk */
1296 return loose_write(ref);
1297 }
1298
1299 /*
1300 * Change the target of a symbolic reference.
1301 *
1302 * This is easy because symrefs cannot be inside
1303 * a pack. We just change the target in memory
1304 * and overwrite the file on disk.
1305 */
1306 int git_reference_set_target(git_reference *ref, const char *target)
1307 {
1308 char normalized[GIT_REFNAME_MAX];
1309
1310 if ((ref->flags & GIT_REF_SYMBOLIC) == 0) {
1311 giterr_set(GITERR_REFERENCE,
1312 "Cannot set symbolic target on a direct reference");
1313 return -1;
1314 }
1315
1316 if (normalize_name(normalized, sizeof(normalized), target, 0))
1317 return -1;
1318
1319 git__free(ref->target.symbolic);
1320 ref->target.symbolic = git__strdup(normalized);
1321 GITERR_CHECK_ALLOC(ref->target.symbolic);
1322
1323 return loose_write(ref);
1324 }
1325
1326 int git_reference_rename(git_reference *ref, const char *new_name, int force)
1327 {
1328 int result;
1329 git_buf aux_path = GIT_BUF_INIT;
1330 char normalized[GIT_REFNAME_MAX];
1331
1332 const char *head_target = NULL;
1333 git_reference *head = NULL;
1334
1335 if (normalize_name(normalized, sizeof(normalized),
1336 new_name, ref->flags & GIT_REF_OID) < 0)
1337 return -1;
1338
1339 if (reference_can_write(ref->owner, normalized, ref->name, force) < 0)
1340 return -1;
1341
1342 /* Initialize path now so we won't get an allocation failure once
1343 * we actually start removing things. */
1344 if (git_buf_joinpath(&aux_path, ref->owner->path_repository, new_name) < 0)
1345 return -1;
1346
1347 /*
1348 * Now delete the old ref and remove an possibly existing directory
1349 * named `new_name`. Note that using the internal `reference_delete`
1350 * method deletes the ref from disk but doesn't free the pointer, so
1351 * we can still access the ref's attributes for creating the new one
1352 */
1353 if (reference_delete(ref) < 0)
1354 goto cleanup;
1355
1356 /*
1357 * Finally we can create the new reference.
1358 */
1359 if (ref->flags & GIT_REF_SYMBOLIC) {
1360 result = git_reference_create_symbolic(
1361 NULL, ref->owner, new_name, ref->target.symbolic, force);
1362 } else {
1363 result = git_reference_create_oid(
1364 NULL, ref->owner, new_name, &ref->target.oid, force);
1365 }
1366
1367 if (result < 0)
1368 goto rollback;
1369
1370 /*
1371 * Check if we have to update HEAD.
1372 */
1373 if (git_reference_lookup(&head, ref->owner, GIT_HEAD_FILE) < 0) {
1374 giterr_set(GITERR_REFERENCE,
1375 "Failed to update HEAD after renaming reference");
1376 goto cleanup;
1377 }
1378
1379 head_target = git_reference_target(head);
1380
1381 if (head_target && !strcmp(head_target, ref->name)) {
1382 git_reference_free(head);
1383 head = NULL;
1384
1385 if (git_reference_create_symbolic(&head, ref->owner, "HEAD", new_name, 1) < 0) {
1386 giterr_set(GITERR_REFERENCE,
1387 "Failed to update HEAD after renaming reference");
1388 goto cleanup;
1389 }
1390 }
1391
1392 /*
1393 * Rename the reflog file, if it exists.
1394 */
1395 if ((git_reference_has_log(ref)) && (git_reflog_rename(ref, new_name) < 0))
1396 goto cleanup;
1397
1398 /*
1399 * Change the name of the reference given by the user.
1400 */
1401 git__free(ref->name);
1402 ref->name = git__strdup(new_name);
1403
1404 /* The reference is no longer packed */
1405 ref->flags &= ~GIT_REF_PACKED;
1406
1407 git_reference_free(head);
1408 git_buf_free(&aux_path);
1409 return 0;
1410
1411 cleanup:
1412 git_reference_free(head);
1413 git_buf_free(&aux_path);
1414 return -1;
1415
1416 rollback:
1417 /*
1418 * Try to create the old reference again, ignore failures
1419 */
1420 if (ref->flags & GIT_REF_SYMBOLIC)
1421 git_reference_create_symbolic(
1422 NULL, ref->owner, ref->name, ref->target.symbolic, 0);
1423 else
1424 git_reference_create_oid(
1425 NULL, ref->owner, ref->name, &ref->target.oid, 0);
1426
1427 /* The reference is no longer packed */
1428 ref->flags &= ~GIT_REF_PACKED;
1429
1430 git_buf_free(&aux_path);
1431 return -1;
1432 }
1433
1434 int git_reference_resolve(git_reference **ref_out, git_reference *ref)
1435 {
1436 if (ref->flags & GIT_REF_OID)
1437 return git_reference_lookup(ref_out, ref->owner, ref->name);
1438 else
1439 return git_reference_lookup_resolved(ref_out, ref->owner, ref->target.symbolic, -1);
1440 }
1441
1442 int git_reference_packall(git_repository *repo)
1443 {
1444 if (packed_load(repo) < 0 || /* load the existing packfile */
1445 packed_loadloose(repo) < 0 || /* add all the loose refs */
1446 packed_write(repo) < 0) /* write back to disk */
1447 return -1;
1448
1449 return 0;
1450 }
1451
1452 int git_reference_foreach(
1453 git_repository *repo,
1454 unsigned int list_flags,
1455 int (*callback)(const char *, void *),
1456 void *payload)
1457 {
1458 int result;
1459 struct dirent_list_data data;
1460 git_buf refs_path = GIT_BUF_INIT;
1461
1462 /* list all the packed references first */
1463 if (list_flags & GIT_REF_PACKED) {
1464 const char *ref_name;
1465 void *ref;
1466 GIT_UNUSED(ref);
1467
1468 if (packed_load(repo) < 0)
1469 return -1;
1470
1471 git_strmap_foreach(repo->references.packfile, ref_name, ref, {
1472 if (callback(ref_name, payload))
1473 return GIT_EUSER;
1474 });
1475 }
1476
1477 /* now list the loose references, trying not to
1478 * duplicate the ref names already in the packed-refs file */
1479
1480 data.repo_path_len = strlen(repo->path_repository);
1481 data.list_flags = list_flags;
1482 data.repo = repo;
1483 data.callback = callback;
1484 data.callback_payload = payload;
1485 data.callback_error = 0;
1486
1487 if (git_buf_joinpath(&refs_path, repo->path_repository, GIT_REFS_DIR) < 0)
1488 return -1;
1489
1490 result = git_path_direach(&refs_path, _dirent_loose_listall, &data);
1491
1492 git_buf_free(&refs_path);
1493
1494 return data.callback_error ? GIT_EUSER : result;
1495 }
1496
1497 static int cb__reflist_add(const char *ref, void *data)
1498 {
1499 return git_vector_insert((git_vector *)data, git__strdup(ref));
1500 }
1501
1502 int git_reference_list(
1503 git_strarray *array,
1504 git_repository *repo,
1505 unsigned int list_flags)
1506 {
1507 git_vector ref_list;
1508
1509 assert(array && repo);
1510
1511 array->strings = NULL;
1512 array->count = 0;
1513
1514 if (git_vector_init(&ref_list, 8, NULL) < 0)
1515 return -1;
1516
1517 if (git_reference_foreach(
1518 repo, list_flags, &cb__reflist_add, (void *)&ref_list) < 0) {
1519 git_vector_free(&ref_list);
1520 return -1;
1521 }
1522
1523 array->strings = (char **)ref_list.contents;
1524 array->count = ref_list.length;
1525 return 0;
1526 }
1527
1528 int git_reference_reload(git_reference *ref)
1529 {
1530 return reference_lookup(ref);
1531 }
1532
1533 void git_repository__refcache_free(git_refcache *refs)
1534 {
1535 assert(refs);
1536
1537 if (refs->packfile) {
1538 struct packref *reference;
1539
1540 git_strmap_foreach_value(refs->packfile, reference, {
1541 git__free(reference);
1542 });
1543
1544 git_strmap_free(refs->packfile);
1545 }
1546 }
1547
1548 static int is_valid_ref_char(char ch)
1549 {
1550 if ((unsigned) ch <= ' ')
1551 return 0;
1552
1553 switch (ch) {
1554 case '~':
1555 case '^':
1556 case ':':
1557 case '\\':
1558 case '?':
1559 case '[':
1560 case '*':
1561 return 0;
1562 default:
1563 return 1;
1564 }
1565 }
1566
1567 static int normalize_name(
1568 char *buffer_out,
1569 size_t out_size,
1570 const char *name,
1571 int is_oid_ref)
1572 {
1573 const char *name_end, *buffer_out_start;
1574 const char *current;
1575 int contains_a_slash = 0;
1576
1577 assert(name && buffer_out);
1578
1579 buffer_out_start = buffer_out;
1580 current = name;
1581 name_end = name + strlen(name);
1582
1583 /* Terminating null byte */
1584 out_size--;
1585
1586 /* A refname can not be empty */
1587 if (name_end == name)
1588 goto invalid_name;
1589
1590 /* A refname can not end with a dot or a slash */
1591 if (*(name_end - 1) == '.' || *(name_end - 1) == '/')
1592 goto invalid_name;
1593
1594 while (current < name_end && out_size) {
1595 if (!is_valid_ref_char(*current))
1596 goto invalid_name;
1597
1598 if (buffer_out > buffer_out_start) {
1599 char prev = *(buffer_out - 1);
1600
1601 /* A refname can not start with a dot nor contain a double dot */
1602 if (*current == '.' && ((prev == '.') || (prev == '/')))
1603 goto invalid_name;
1604
1605 /* '@{' is forbidden within a refname */
1606 if (*current == '{' && prev == '@')
1607 goto invalid_name;
1608
1609 /* Prevent multiple slashes from being added to the output */
1610 if (*current == '/' && prev == '/') {
1611 current++;
1612 continue;
1613 }
1614 }
1615
1616 if (*current == '/')
1617 contains_a_slash = 1;
1618
1619 *buffer_out++ = *current++;
1620 out_size--;
1621 }
1622
1623 if (!out_size)
1624 goto invalid_name;
1625
1626 /* Object id refname have to contain at least one slash, except
1627 * for HEAD in a detached state or MERGE_HEAD if we're in the
1628 * middle of a merge */
1629 if (is_oid_ref &&
1630 !contains_a_slash &&
1631 strcmp(name, GIT_HEAD_FILE) != 0 &&
1632 strcmp(name, GIT_MERGE_HEAD_FILE) != 0 &&
1633 strcmp(name, GIT_FETCH_HEAD_FILE) != 0)
1634 goto invalid_name;
1635
1636 /* A refname can not end with ".lock" */
1637 if (!git__suffixcmp(name, GIT_FILELOCK_EXTENSION))
1638 goto invalid_name;
1639
1640 *buffer_out = '\0';
1641
1642 /*
1643 * For object id references, name has to start with refs/. Again,
1644 * we need to allow HEAD to be in a detached state.
1645 */
1646 if (is_oid_ref && !(git__prefixcmp(buffer_out_start, GIT_REFS_DIR) ||
1647 strcmp(buffer_out_start, GIT_HEAD_FILE)))
1648 goto invalid_name;
1649
1650 return 0;
1651
1652 invalid_name:
1653 giterr_set(GITERR_REFERENCE, "The given reference name is not valid");
1654 return -1;
1655 }
1656
1657 int git_reference__normalize_name(
1658 char *buffer_out,
1659 size_t out_size,
1660 const char *name)
1661 {
1662 return normalize_name(buffer_out, out_size, name, 0);
1663 }
1664
1665 int git_reference__normalize_name_oid(
1666 char *buffer_out,
1667 size_t out_size,
1668 const char *name)
1669 {
1670 return normalize_name(buffer_out, out_size, name, 1);
1671 }
1672
1673 #define GIT_REF_TYPEMASK (GIT_REF_OID | GIT_REF_SYMBOLIC)
1674
1675 int git_reference_cmp(git_reference *ref1, git_reference *ref2)
1676 {
1677 assert(ref1 && ref2);
1678
1679 /* let's put symbolic refs before OIDs */
1680 if ((ref1->flags & GIT_REF_TYPEMASK) != (ref2->flags & GIT_REF_TYPEMASK))
1681 return (ref1->flags & GIT_REF_SYMBOLIC) ? -1 : 1;
1682
1683 if (ref1->flags & GIT_REF_SYMBOLIC)
1684 return strcmp(ref1->target.symbolic, ref2->target.symbolic);
1685
1686 return git_oid_cmp(&ref1->target.oid, &ref2->target.oid);
1687 }
1688
1689 /* Update the reference named `ref_name` so it points to `oid` */
1690 int git_reference__update(git_repository *repo, const git_oid *oid, const char *ref_name)
1691 {
1692 git_reference *ref;
1693 int res;
1694
1695 res = git_reference_lookup(&ref, repo, ref_name);
1696
1697 /* If we haven't found the reference at all, we assume we need to create
1698 * a new reference and that's it */
1699 if (res == GIT_ENOTFOUND) {
1700 giterr_clear();
1701 return git_reference_create_oid(NULL, repo, ref_name, oid, 1);
1702 }
1703
1704 if (res < 0)
1705 return -1;
1706
1707 /* If we have found a reference, but it's symbolic, we need to update
1708 * the direct reference it points to */
1709 if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
1710 git_reference *aux;
1711 const char *sym_target;
1712
1713 /* The target pointed at by this reference */
1714 sym_target = git_reference_target(ref);
1715
1716 /* resolve the reference to the target it points to */
1717 res = git_reference_resolve(&aux, ref);
1718
1719 /*
1720 * if the symbolic reference pointed to an inexisting ref,
1721 * this is means we're creating a new branch, for example.
1722 * We need to create a new direct reference with that name
1723 */
1724 if (res == GIT_ENOTFOUND) {
1725 giterr_clear();
1726 res = git_reference_create_oid(NULL, repo, sym_target, oid, 1);
1727 git_reference_free(ref);
1728 return res;
1729 }
1730
1731 /* free the original symbolic reference now; not before because
1732 * we're using the `sym_target` pointer */
1733 git_reference_free(ref);
1734
1735 if (res < 0)
1736 return -1;
1737
1738 /* store the newly found direct reference in its place */
1739 ref = aux;
1740 }
1741
1742 /* ref is made to point to `oid`: ref is either the original reference,
1743 * or the target of the symbolic reference we've looked up */
1744 res = git_reference_set_oid(ref, oid);
1745 git_reference_free(ref);
1746 return res;
1747 }
1748
1749 struct glob_cb_data {
1750 const char *glob;
1751 int (*callback)(const char *, void *);
1752 void *payload;
1753 };
1754
1755 static int fromglob_cb(const char *reference_name, void *payload)
1756 {
1757 struct glob_cb_data *data = (struct glob_cb_data *)payload;
1758
1759 if (!p_fnmatch(data->glob, reference_name, 0))
1760 return data->callback(reference_name, data->payload);
1761
1762 return 0;
1763 }
1764
1765 int git_reference_foreach_glob(
1766 git_repository *repo,
1767 const char *glob,
1768 unsigned int list_flags,
1769 int (*callback)(
1770 const char *reference_name,
1771 void *payload),
1772 void *payload)
1773 {
1774 struct glob_cb_data data;
1775
1776 assert(repo && glob && callback);
1777
1778 data.glob = glob;
1779 data.callback = callback;
1780 data.payload = payload;
1781
1782 return git_reference_foreach(
1783 repo, list_flags, fromglob_cb, &data);
1784 }
1785
1786 int git_reference_has_log(
1787 git_reference *ref)
1788 {
1789 git_buf path = GIT_BUF_INIT;
1790 int result;
1791
1792 assert(ref);
1793
1794 if (git_buf_join_n(&path, '/', 3, ref->owner->path_repository, GIT_REFLOG_DIR, ref->name) < 0)
1795 return -1;
1796
1797 result = git_path_isfile(git_buf_cstr(&path));
1798 git_buf_free(&path);
1799
1800 return result;
1801 }
1802
1803 int git_reference_is_branch(git_reference *ref)
1804 {
1805 assert(ref);
1806
1807 return git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) == 0;
1808 }