]> git.proxmox.com Git - libgit2.git/blob - src/repository.c
4e5e7e6992c4900cc94453cccdff789617099ac3
[libgit2.git] / src / repository.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 #include <stdarg.h>
26
27 #include "git2/object.h"
28
29 #include "common.h"
30 #include "repository.h"
31 #include "commit.h"
32 #include "tag.h"
33 #include "blob.h"
34 #include "fileops.h"
35
36 #define GIT_FOLDER "/.git/"
37 #define GIT_OBJECTS_FOLDER "objects/"
38 #define GIT_REFS_FOLDER "refs/"
39 #define GIT_INDEX_FILE "index"
40 #define GIT_HEAD_FILE "HEAD"
41
42 static const int default_table_size = 32;
43 static const double max_load_factor = 0.65;
44
45 static const int OBJECT_BASE_SIZE = 4096;
46
47 static struct {
48 const char *str; /* type name string */
49 int loose; /* valid loose object type flag */
50 size_t size; /* size in bytes of the object structure */
51 } git_objects_table[] = {
52 { "", 0, 0 }, /* 0 = GIT_OBJ__EXT1 */
53 { "commit", 1, sizeof(git_commit)}, /* 1 = GIT_OBJ_COMMIT */
54 { "tree", 1, sizeof(git_tree) }, /* 2 = GIT_OBJ_TREE */
55 { "blob", 1, sizeof(git_blob) }, /* 3 = GIT_OBJ_BLOB */
56 { "tag", 1, sizeof(git_tag) }, /* 4 = GIT_OBJ_TAG */
57 { "", 0, 0 }, /* 5 = GIT_OBJ__EXT2 */
58 { "OFS_DELTA", 0, 0 }, /* 6 = GIT_OBJ_OFS_DELTA */
59 { "REF_DELTA", 0, 0 } /* 7 = GIT_OBJ_REF_DELTA */
60 };
61
62 typedef struct git_repository_init_results {
63 char *path_repository;
64
65 unsigned is_bare:1;
66 unsigned has_been_reinit:1;
67 } git_repository_init_results;
68
69 /***********************************************************
70 *
71 * MISCELANEOUS HELPER FUNCTIONS
72 *
73 ***********************************************************/
74
75
76
77 uint32_t git__objtable_hash(const void *key)
78 {
79 uint32_t r;
80 git_oid *id;
81
82 id = (git_oid *)key;
83 memcpy(&r, id->id, sizeof(r));
84 return r;
85 }
86
87 int git__objtable_haskey(void *object, const void *key)
88 {
89 git_object *obj;
90 git_oid *oid;
91
92 obj = (git_object *)object;
93 oid = (git_oid *)key;
94
95 return (git_oid_cmp(oid, &obj->id) == 0);
96 }
97
98
99
100 static int assign_repository_folders(git_repository *repo,
101 const char *git_dir,
102 const char *git_object_directory,
103 const char *git_index_file,
104 const char *git_work_tree)
105 {
106 char path_aux[GIT_PATH_MAX];
107 size_t path_len;
108
109 assert(repo);
110
111 if (git_dir == NULL || gitfo_isdir(git_dir) < GIT_SUCCESS)
112 return GIT_ENOTFOUND;
113
114 /* store GIT_DIR */
115 path_len = strlen(git_dir);
116 strcpy(path_aux, git_dir);
117
118 if (path_aux[path_len - 1] != '/') {
119 path_aux[path_len] = '/';
120 path_aux[path_len + 1] = 0;
121
122 path_len = path_len + 1;
123 }
124
125 repo->path_repository = git__strdup(path_aux);
126
127 /* store GIT_OBJECT_DIRECTORY */
128 if (git_object_directory == NULL)
129 strcpy(path_aux + path_len, GIT_OBJECTS_FOLDER);
130 else
131 strcpy(path_aux, git_object_directory);
132
133 if (gitfo_isdir(path_aux) < GIT_SUCCESS)
134 return GIT_ENOTFOUND;
135
136 repo->path_odb = git__strdup(path_aux);
137
138
139 /* store GIT_INDEX_FILE */
140 if (git_index_file == NULL)
141 strcpy(path_aux + path_len, GIT_INDEX_FILE);
142 else
143 strcpy(path_aux, git_index_file);
144
145 if (gitfo_exists(path_aux) < 0)
146 return GIT_ENOTFOUND;
147
148 repo->path_index = git__strdup(path_aux);
149
150
151 /* store GIT_WORK_TREE */
152 if (git_work_tree == NULL)
153 repo->is_bare = 1;
154 else
155 repo->path_workdir = git__strdup(git_work_tree);
156
157 return GIT_SUCCESS;
158 }
159
160 static int guess_repository_folders(git_repository *repo, const char *repository_path)
161 {
162 char path_aux[GIT_PATH_MAX], *last_folder;
163 int path_len;
164
165 if (gitfo_isdir(repository_path) < GIT_SUCCESS)
166 return GIT_ENOTAREPO;
167
168 path_len = strlen(repository_path);
169 strcpy(path_aux, repository_path);
170
171 if (path_aux[path_len - 1] != '/') {
172 path_aux[path_len] = '/';
173 path_aux[path_len + 1] = 0;
174
175 path_len = path_len + 1;
176 }
177
178 repo->path_repository = git__strdup(path_aux);
179
180 /* objects database */
181 strcpy(path_aux + path_len, GIT_OBJECTS_FOLDER);
182 if (gitfo_isdir(path_aux) < GIT_SUCCESS)
183 return GIT_ENOTAREPO;
184 repo->path_odb = git__strdup(path_aux);
185
186 /* HEAD file */
187 strcpy(path_aux + path_len, GIT_HEAD_FILE);
188 if (gitfo_exists(path_aux) < 0)
189 return GIT_ENOTAREPO;
190
191 path_aux[path_len] = 0;
192
193 last_folder = (path_aux + path_len - 2);
194
195 while (*last_folder != '/')
196 last_folder--;
197
198 if (strcmp(last_folder, GIT_FOLDER) == 0) {
199 repo->is_bare = 0;
200
201 /* index file */
202 strcpy(path_aux + path_len, GIT_INDEX_FILE);
203 repo->path_index = git__strdup(path_aux);
204
205 /* working dir */
206 *(last_folder + 1) = 0;
207 repo->path_workdir = git__strdup(path_aux);
208
209 } else {
210 repo->is_bare = 1;
211 repo->path_workdir = NULL;
212 }
213
214 return GIT_SUCCESS;
215 }
216
217 git_repository *git_repository__alloc()
218 {
219 git_repository *repo = git__malloc(sizeof(git_repository));
220 if (!repo)
221 return NULL;
222
223 memset(repo, 0x0, sizeof(git_repository));
224
225 repo->objects = git_hashtable_alloc(
226 default_table_size,
227 git__objtable_hash,
228 git__objtable_haskey);
229
230 if (repo->objects == NULL) {
231 free(repo);
232 return NULL;
233 }
234
235 return repo;
236 }
237
238 int git_repository_open2(git_repository **repo_out,
239 const char *git_dir,
240 const char *git_object_directory,
241 const char *git_index_file,
242 const char *git_work_tree)
243 {
244 git_repository *repo;
245 int error = GIT_SUCCESS;
246
247 assert(repo_out);
248
249 repo = git_repository__alloc();
250 if (repo == NULL)
251 return GIT_ENOMEM;
252
253 error = assign_repository_folders(repo,
254 git_dir,
255 git_object_directory,
256 git_index_file,
257 git_work_tree);
258
259 if (error < GIT_SUCCESS)
260 goto cleanup;
261
262 error = git_odb_open(&repo->db, repo->path_odb);
263 if (error < GIT_SUCCESS)
264 goto cleanup;
265
266 *repo_out = repo;
267 return GIT_SUCCESS;
268
269 cleanup:
270 git_repository_free(repo);
271 return error;
272 }
273
274 int git_repository_open(git_repository **repo_out, const char *path)
275 {
276 git_repository *repo;
277 int error = GIT_SUCCESS;
278
279 assert(repo_out && path);
280
281 repo = git_repository__alloc();
282 if (repo == NULL)
283 return GIT_ENOMEM;
284
285 error = guess_repository_folders(repo, path);
286 if (error < GIT_SUCCESS)
287 goto cleanup;
288
289
290 error = git_odb_open(&repo->db, repo->path_odb);
291 if (error < GIT_SUCCESS)
292 goto cleanup;
293
294 *repo_out = repo;
295 return GIT_SUCCESS;
296
297 cleanup:
298 git_repository_free(repo);
299 return error;
300 }
301
302 void git_repository_free(git_repository *repo)
303 {
304 git_hashtable_iterator it;
305 git_object *object;
306
307 if (repo == NULL)
308 return;
309
310 free(repo->path_workdir);
311 free(repo->path_index);
312 free(repo->path_repository);
313 free(repo->path_odb);
314
315 git_hashtable_iterator_init(repo->objects, &it);
316
317 while ((object = (git_object *)
318 git_hashtable_iterator_next(&it)) != NULL)
319 git_object_free(object);
320
321 git_hashtable_free(repo->objects);
322
323 if (repo->db != NULL)
324 git_odb_close(repo->db);
325
326 if (repo->index != NULL)
327 git_index_free(repo->index);
328
329 free(repo);
330 }
331
332 git_index *git_repository_index(git_repository *repo)
333 {
334 if (repo->index == NULL) {
335 if (git_index_open_inrepo(&repo->index, repo) < GIT_SUCCESS)
336 return NULL;
337
338 assert(repo->index);
339 }
340
341 return repo->index;
342 }
343
344 static int source_resize(git_odb_source *src)
345 {
346 size_t write_offset, new_size;
347 void *new_data;
348
349 write_offset = (size_t)((char *)src->write_ptr - (char *)src->raw.data);
350
351 new_size = src->raw.len * 2;
352 if ((new_data = git__malloc(new_size)) == NULL)
353 return GIT_ENOMEM;
354
355 memcpy(new_data, src->raw.data, src->written_bytes);
356 free(src->raw.data);
357
358 src->raw.data = new_data;
359 src->raw.len = new_size;
360 src->write_ptr = (char *)new_data + write_offset;
361
362 return GIT_SUCCESS;
363 }
364
365 int git__source_printf(git_odb_source *source, const char *format, ...)
366 {
367 va_list arglist;
368 int len, did_resize = 0;
369
370 assert(source->open && source->write_ptr);
371
372 va_start(arglist, format);
373
374 len = vsnprintf(source->write_ptr, source->raw.len - source->written_bytes, format, arglist);
375
376 while (source->written_bytes + len >= source->raw.len) {
377 if (source_resize(source) < GIT_SUCCESS)
378 return GIT_ENOMEM;
379
380 did_resize = 1;
381 }
382
383 if (did_resize)
384 vsnprintf(source->write_ptr, source->raw.len - source->written_bytes, format, arglist);
385
386 source->write_ptr = (char *)source->write_ptr + len;
387 source->written_bytes += len;
388
389 return GIT_SUCCESS;
390 }
391
392 int git__source_write(git_odb_source *source, const void *bytes, size_t len)
393 {
394 assert(source);
395
396 assert(source->open && source->write_ptr);
397
398 while (source->written_bytes + len >= source->raw.len) {
399 if (source_resize(source) < GIT_SUCCESS)
400 return GIT_ENOMEM;
401 }
402
403 memcpy(source->write_ptr, bytes, len);
404 source->write_ptr = (char *)source->write_ptr + len;
405 source->written_bytes += len;
406
407 return GIT_SUCCESS;
408 }
409
410 static void prepare_write(git_object *object)
411 {
412 if (object->source.write_ptr != NULL || object->source.open)
413 git_object__source_close(object);
414
415 /* TODO: proper size calculation */
416 object->source.raw.data = git__malloc(OBJECT_BASE_SIZE);
417 object->source.raw.len = OBJECT_BASE_SIZE;
418
419 object->source.write_ptr = object->source.raw.data;
420 object->source.written_bytes = 0;
421
422 object->source.open = 1;
423 }
424
425 static int write_back(git_object *object)
426 {
427 int error;
428 git_oid new_id;
429
430 assert(object);
431
432 assert(object->source.open);
433 assert(object->modified);
434
435 object->source.raw.len = object->source.written_bytes;
436
437 if ((error = git_odb_write(&new_id, object->repo->db, &object->source.raw)) < GIT_SUCCESS)
438 return error;
439
440 if (!object->in_memory)
441 git_hashtable_remove(object->repo->objects, &object->id);
442
443 git_oid_cpy(&object->id, &new_id);
444 git_hashtable_insert(object->repo->objects, &object->id, object);
445
446 object->source.write_ptr = NULL;
447 object->source.written_bytes = 0;
448
449 object->modified = 0;
450 object->in_memory = 0;
451
452 git_object__source_close(object);
453 return GIT_SUCCESS;
454 }
455
456 int git_object__source_open(git_object *object)
457 {
458 int error;
459
460 assert(object && !object->in_memory);
461
462 if (object->source.open)
463 git_object__source_close(object);
464
465 error = git_odb_read(&object->source.raw, object->repo->db, &object->id);
466 if (error < GIT_SUCCESS)
467 return error;
468
469 object->source.open = 1;
470 return GIT_SUCCESS;
471 }
472
473 void git_object__source_close(git_object *object)
474 {
475 assert(object);
476
477 if (object->source.open) {
478 git_rawobj_close(&object->source.raw);
479 object->source.open = 0;
480 }
481 }
482
483 int git_object_write(git_object *object)
484 {
485 int error;
486 git_odb_source *source;
487
488 assert(object);
489
490 if (object->modified == 0)
491 return GIT_SUCCESS;
492
493 prepare_write(object);
494 source = &object->source;
495
496 switch (source->raw.type) {
497 case GIT_OBJ_COMMIT:
498 error = git_commit__writeback((git_commit *)object, source);
499 break;
500
501 case GIT_OBJ_TREE:
502 error = git_tree__writeback((git_tree *)object, source);
503 break;
504
505 case GIT_OBJ_TAG:
506 error = git_tag__writeback((git_tag *)object, source);
507 break;
508
509 case GIT_OBJ_BLOB:
510 error = git_blob__writeback((git_blob *)object, source);
511 break;
512
513 default:
514 error = GIT_ERROR;
515 break;
516 }
517
518 if (error < GIT_SUCCESS) {
519 git_object__source_close(object);
520 return error;
521 }
522
523 return write_back(object);
524 }
525
526 void git_object_free(git_object *object)
527 {
528 if (object == NULL)
529 return;
530
531 git_object__source_close(object);
532 git_hashtable_remove(object->repo->objects, &object->id);
533
534 switch (object->source.raw.type) {
535 case GIT_OBJ_COMMIT:
536 git_commit__free((git_commit *)object);
537 break;
538
539 case GIT_OBJ_TREE:
540 git_tree__free((git_tree *)object);
541 break;
542
543 case GIT_OBJ_TAG:
544 git_tag__free((git_tag *)object);
545 break;
546
547 case GIT_OBJ_BLOB:
548 git_blob__free((git_blob *)object);
549 break;
550
551 default:
552 free(object);
553 break;
554 }
555 }
556
557 git_odb *git_repository_database(git_repository *repo)
558 {
559 assert(repo);
560 return repo->db;
561 }
562
563 const git_oid *git_object_id(git_object *obj)
564 {
565 assert(obj);
566
567 if (obj->in_memory)
568 return NULL;
569
570 return &obj->id;
571 }
572
573 git_otype git_object_type(git_object *obj)
574 {
575 assert(obj);
576 return obj->source.raw.type;
577 }
578
579 git_repository *git_object_owner(git_object *obj)
580 {
581 assert(obj);
582 return obj->repo;
583 }
584
585 int git_repository_newobject(git_object **object_out, git_repository *repo, git_otype type)
586 {
587 git_object *object = NULL;
588
589 assert(object_out && repo);
590
591 *object_out = NULL;
592
593 switch (type) {
594 case GIT_OBJ_COMMIT:
595 case GIT_OBJ_TAG:
596 case GIT_OBJ_TREE:
597 case GIT_OBJ_BLOB:
598 break;
599
600 default:
601 return GIT_EINVALIDTYPE;
602 }
603
604 object = git__malloc(git_objects_table[type].size);
605
606 if (object == NULL)
607 return GIT_ENOMEM;
608
609 memset(object, 0x0, git_objects_table[type].size);
610 object->repo = repo;
611 object->in_memory = 1;
612 object->modified = 1;
613
614 object->source.raw.type = type;
615
616 *object_out = object;
617 return GIT_SUCCESS;
618 }
619
620 int git_repository_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type)
621 {
622 git_object *object = NULL;
623 git_rawobj obj_file;
624 int error = GIT_SUCCESS;
625
626 assert(repo && object_out && id);
627
628 object = git_hashtable_lookup(repo->objects, id);
629 if (object != NULL) {
630 *object_out = object;
631 return GIT_SUCCESS;
632 }
633
634 error = git_odb_read(&obj_file, repo->db, id);
635 if (error < GIT_SUCCESS)
636 return error;
637
638 if (type != GIT_OBJ_ANY && type != obj_file.type) {
639 git_rawobj_close(&obj_file);
640 return GIT_EINVALIDTYPE;
641 }
642
643 type = obj_file.type;
644
645 object = git__malloc(git_objects_table[type].size);
646
647 if (object == NULL)
648 return GIT_ENOMEM;
649
650 memset(object, 0x0, git_objects_table[type].size);
651
652 /* Initialize parent object */
653 git_oid_cpy(&object->id, id);
654 object->repo = repo;
655 memcpy(&object->source.raw, &obj_file, sizeof(git_rawobj));
656 object->source.open = 1;
657
658 switch (type) {
659
660 case GIT_OBJ_COMMIT:
661 error = git_commit__parse((git_commit *)object);
662 break;
663
664 case GIT_OBJ_TREE:
665 error = git_tree__parse((git_tree *)object);
666 break;
667
668 case GIT_OBJ_TAG:
669 error = git_tag__parse((git_tag *)object);
670 break;
671
672 case GIT_OBJ_BLOB:
673 error = git_blob__parse((git_blob *)object);
674 break;
675
676 default:
677 break;
678 }
679
680 if (error < GIT_SUCCESS) {
681 git_object_free(object);
682 return error;
683 }
684
685 git_object__source_close(object);
686 git_hashtable_insert(repo->objects, &object->id, object);
687
688 *object_out = object;
689 return GIT_SUCCESS;
690 }
691
692 #define GIT_NEWOBJECT_TEMPLATE(obj, tp) \
693 int git_##obj##_new(git_##obj **o, git_repository *repo) {\
694 return git_repository_newobject((git_object **)o, repo, GIT_OBJ_##tp); } \
695 int git_##obj##_lookup(git_##obj **o, git_repository *repo, const git_oid *id) { \
696 return git_repository_lookup((git_object **)o, repo, id, GIT_OBJ_##tp); }
697
698 GIT_NEWOBJECT_TEMPLATE(commit, COMMIT)
699 GIT_NEWOBJECT_TEMPLATE(tag, TAG)
700 GIT_NEWOBJECT_TEMPLATE(tree, TREE)
701 GIT_NEWOBJECT_TEMPLATE(blob, BLOB)
702
703
704 const char *git_object_type2string(git_otype type)
705 {
706 if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
707 return "";
708
709 return git_objects_table[type].str;
710 }
711
712 git_otype git_object_string2type(const char *str)
713 {
714 size_t i;
715
716 if (!str || !*str)
717 return GIT_OBJ_BAD;
718
719 for (i = 0; i < ARRAY_SIZE(git_objects_table); i++)
720 if (!strcmp(str, git_objects_table[i].str))
721 return (git_otype)i;
722
723 return GIT_OBJ_BAD;
724 }
725
726 int git_object_typeisloose(git_otype type)
727 {
728 if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
729 return 0;
730
731 return git_objects_table[type].loose;
732 }
733
734 int git_repository_init__reinit(git_repository_init_results* results)
735 {
736 /* To be implemented */
737
738 results->has_been_reinit = 1;
739 return GIT_SUCCESS;
740 }
741
742 int git_repository_init__create_structure(git_repository_init_results* results)
743 {
744 char temp_path[GIT_PATH_MAX];
745 int path_len;
746 const int mode = 0755; /* or 0777 ? */
747
748 char* git_dir = results->path_repository;
749
750 //TODO : Ensure the parent tree structure already exists by recursively building it through gitfo_mkdir_recurs().
751 if (gitfo_mkdir_recurs(git_dir, mode))
752 return GIT_ERROR;
753
754 path_len = strlen(git_dir);
755 strcpy(temp_path, git_dir);
756
757 /* Does HEAD file already exist ? */
758 strcpy(temp_path + path_len, GIT_HEAD_FILE);
759 if (!gitfo_exists(temp_path))
760 {
761 return git_repository_init__reinit(results);
762 }
763
764 strcpy(temp_path + path_len, GIT_OBJECTS_FOLDER);
765 if (gitfo_mkdir(temp_path, mode))
766 return GIT_ERROR;
767
768 strcpy(temp_path + path_len, GIT_REFS_FOLDER);
769 if (gitfo_mkdir(temp_path, mode))
770 return GIT_ERROR;
771
772
773 /* To be implemented */
774
775 return GIT_SUCCESS;
776 }
777
778 int git_repository_init__assign_git_directory(git_repository_init_results* results, const char* path)
779 {
780 const int MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH = 66; // TODO: How many ?
781 char temp_path[GIT_PATH_MAX];
782 int path_len;
783
784 path_len = strlen(path);
785 strcpy(temp_path, path);
786
787 /* Ensure path has a trailing slash */
788 if (temp_path[path_len - 1] != '/') {
789 temp_path[path_len] = '/';
790 temp_path[path_len + 1] = 0;
791
792 path_len = path_len + 1;
793 }
794
795 assert(path_len < GIT_PATH_MAX - MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH);
796
797 if (!results->is_bare)
798 {
799 strcpy(temp_path + path_len - 1, GIT_FOLDER);
800 path_len = path_len + strlen(GIT_FOLDER) - 1; /* Skip the leading slash from the constant */
801 }
802
803 results->path_repository = git__strdup(temp_path);
804
805 return GIT_SUCCESS;
806 }
807
808 int git_repository_init(git_repository** repo_out, const char* path, unsigned is_bare)
809 {
810 git_repository_init_results results;
811 int error = GIT_SUCCESS;
812
813 assert(repo_out && path);
814
815 results.is_bare = is_bare;
816
817 error = git_repository_init__assign_git_directory(&results, path);
818 if (error < GIT_SUCCESS)
819 goto cleanup;
820
821 error = git_repository_init__create_structure(&results);
822 if (error < GIT_SUCCESS)
823 goto cleanup;
824
825 //TODO: Uncomment when the structure has been properly created
826 //error = git_repository_open(repo_out, results->path_repository);
827 //if (error < GIT_SUCCESS)
828 // goto cleanup;
829
830 cleanup:
831 free(results.path_repository);
832 return error;
833 }