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