]> git.proxmox.com Git - libgit2.git/blob - src/repository.c
Add 'git_repository_open2' to customize repo folders
[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 "common.h"
28 #include "repository.h"
29 #include "commit.h"
30 #include "tag.h"
31 #include "blob.h"
32 #include "fileops.h"
33
34 #define GIT_FOLDER "/.git/"
35 #define GIT_OBJECTS_FOLDER "objects/"
36 #define GIT_INDEX_FILE "index"
37 #define GIT_HEAD_FILE "HEAD"
38
39 static const int default_table_size = 32;
40 static const double max_load_factor = 0.65;
41
42 static const int OBJECT_BASE_SIZE = 4096;
43
44 static const size_t object_sizes[] = {
45 0,
46 sizeof(git_commit),
47 sizeof(git_tree),
48 sizeof(git_blob),
49 sizeof(git_tag)
50 };
51
52
53 uint32_t git__objtable_hash(const void *key)
54 {
55 uint32_t r;
56 git_oid *id;
57
58 id = (git_oid *)key;
59 memcpy(&r, id->id, sizeof(r));
60 return r;
61 }
62
63 int git__objtable_haskey(void *object, const void *key)
64 {
65 git_object *obj;
66 git_oid *oid;
67
68 obj = (git_object *)object;
69 oid = (git_oid *)key;
70
71 return (git_oid_cmp(oid, &obj->id) == 0);
72 }
73
74
75
76 static int assign_repository_folders(git_repository *repo,
77 const char *git_dir,
78 const char *git_object_directory,
79 const char *git_index_file,
80 const char *git_work_tree)
81 {
82 char path_aux[GIT_PATH_MAX];
83 size_t path_len;
84
85 assert(repo);
86
87 if (git_dir == NULL || gitfo_isdir(git_dir) < 0)
88 return GIT_ENOTFOUND;
89
90
91 /* store GIT_DIR */
92 path_len = strlen(git_dir);
93 strcpy(path_aux, git_dir);
94
95 if (path_aux[path_len - 1] != '/') {
96 path_aux[path_len] = '/';
97 path_aux[path_len + 1] = 0;
98
99 path_len = path_len + 1;
100 }
101
102 repo->path_repository = git__strdup(path_aux);
103
104 /* store GIT_OBJECT_DIRECTORY */
105 if (git_object_directory == NULL)
106 strcpy(path_aux + path_len, GIT_OBJECTS_FOLDER);
107 else
108 strcpy(path_aux, git_object_directory);
109
110 if (gitfo_isdir(path_aux) < 0)
111 return GIT_ENOTFOUND;
112
113 repo->path_odb = git__strdup(path_aux);
114
115
116 /* store GIT_INDEX_FILE */
117 if (git_index_file == NULL)
118 strcpy(path_aux + path_len, GIT_INDEX_FILE);
119 else
120 strcpy(path_aux, git_index_file);
121
122 if (gitfo_exists(path_aux) < 0)
123 return GIT_ENOTFOUND;
124
125 repo->path_index = git__strdup(path_aux);
126
127
128 /* store GIT_WORK_TREE */
129 if (git_work_tree == NULL)
130 repo->is_bare = 1;
131 else
132 repo->path_workdir = git__strdup(git_work_tree);
133
134 return GIT_SUCCESS;
135 }
136
137 static int guess_repository_folders(git_repository *repo, const char *repository_path)
138 {
139 char path_aux[GIT_PATH_MAX], *last_folder;
140 int path_len;
141
142 if (gitfo_isdir(repository_path) < 0)
143 return GIT_ENOTAREPO;
144
145 path_len = strlen(repository_path);
146 strcpy(path_aux, repository_path);
147
148 if (path_aux[path_len - 1] != '/') {
149 path_aux[path_len] = '/';
150 path_aux[path_len + 1] = 0;
151
152 path_len = path_len + 1;
153 }
154
155 repo->path_repository = git__strdup(path_aux);
156
157 /* objects database */
158 strcpy(path_aux + path_len, GIT_OBJECTS_FOLDER);
159 if (gitfo_isdir(path_aux) < 0)
160 return GIT_ENOTAREPO;
161 repo->path_odb = git__strdup(path_aux);
162
163 /* HEAD file */
164 strcpy(path_aux + path_len, GIT_HEAD_FILE);
165 if (gitfo_exists(path_aux) < 0)
166 return GIT_ENOTAREPO;
167
168 path_aux[path_len] = 0;
169
170 last_folder = (path_aux + path_len - 2);
171
172 while (*last_folder != '/')
173 last_folder--;
174
175 if (strcmp(last_folder, GIT_FOLDER) == 0) {
176 repo->is_bare = 0;
177
178 /* index file */
179 strcpy(path_aux + path_len, GIT_INDEX_FILE);
180 repo->path_index = git__strdup(path_aux);
181
182 /* working dir */
183 *(last_folder + 1) = 0;
184 repo->path_workdir = git__strdup(path_aux);
185
186 } else {
187 repo->is_bare = 1;
188 repo->path_workdir = NULL;
189 }
190
191 return GIT_SUCCESS;
192 }
193
194 git_repository *git_repository__alloc()
195 {
196 git_repository *repo = git__malloc(sizeof(git_repository));
197 if (!repo)
198 return NULL;
199
200 memset(repo, 0x0, sizeof(git_repository));
201
202 repo->objects = git_hashtable_alloc(
203 default_table_size,
204 git__objtable_hash,
205 git__objtable_haskey);
206
207 if (repo->objects == NULL) {
208 free(repo);
209 return NULL;
210 }
211
212 return repo;
213 }
214
215 int git_repository_open2(git_repository **repo_out,
216 const char *git_dir,
217 const char *git_object_directory,
218 const char *git_index_file,
219 const char *git_work_tree)
220 {
221 git_repository *repo;
222 int error = GIT_SUCCESS;
223
224 assert(repo_out);
225
226 repo = git_repository__alloc();
227 if (repo == NULL)
228 return GIT_ENOMEM;
229
230 error = assign_repository_folders(repo,
231 git_dir,
232 git_object_directory,
233 git_index_file,
234 git_work_tree);
235
236 if (error < 0)
237 goto cleanup;
238
239 error = git_odb_open(&repo->db, repo->path_odb);
240 if (error < 0)
241 goto cleanup;
242
243 *repo_out = repo;
244 return GIT_SUCCESS;
245
246 cleanup:
247 git_repository_free(repo);
248 return error;
249 }
250
251 int git_repository_open(git_repository **repo_out, const char *path)
252 {
253 git_repository *repo;
254 int error = GIT_SUCCESS;
255
256 assert(repo_out && path);
257
258 repo = git_repository__alloc();
259 if (repo == NULL)
260 return GIT_ENOMEM;
261
262 error = guess_repository_folders(repo, path);
263 if (error < 0)
264 goto cleanup;
265
266 error = git_odb_open(&repo->db, repo->path_odb);
267 if (error < 0)
268 goto cleanup;
269
270 *repo_out = repo;
271 return GIT_SUCCESS;
272
273 cleanup:
274 git_repository_free(repo);
275 return error;
276 }
277
278 void git_repository_free(git_repository *repo)
279 {
280 git_hashtable_iterator it;
281 git_object *object;
282
283 assert(repo);
284
285 free(repo->path_workdir);
286 free(repo->path_index);
287 free(repo->path_repository);
288 free(repo->path_odb);
289
290 git_hashtable_iterator_init(repo->objects, &it);
291
292 while ((object = (git_object *)
293 git_hashtable_iterator_next(&it)) != NULL)
294 git_object_free(object);
295
296 git_hashtable_free(repo->objects);
297 git_odb_close(repo->db);
298 git_index_free(repo->index);
299 free(repo);
300 }
301
302 git_index *git_repository_index(git_repository *repo)
303 {
304 if (repo->index == NULL) {
305 if (git_index_open_inrepo(&repo->index, repo) < 0)
306 return NULL;
307
308 assert(repo->index);
309 }
310
311 return repo->index;
312 }
313
314 static int source_resize(git_odb_source *src)
315 {
316 size_t write_offset, new_size;
317 void *new_data;
318
319 write_offset = (size_t)((char *)src->write_ptr - (char *)src->raw.data);
320
321 new_size = src->raw.len * 2;
322 if ((new_data = git__malloc(new_size)) == NULL)
323 return GIT_ENOMEM;
324
325 memcpy(new_data, src->raw.data, src->written_bytes);
326 free(src->raw.data);
327
328 src->raw.data = new_data;
329 src->raw.len = new_size;
330 src->write_ptr = (char *)new_data + write_offset;
331
332 return GIT_SUCCESS;
333 }
334
335 int git__source_printf(git_odb_source *source, const char *format, ...)
336 {
337 va_list arglist;
338 int len, did_resize = 0;
339
340 assert(source->open && source->write_ptr);
341
342 va_start(arglist, format);
343
344 len = vsnprintf(source->write_ptr, source->raw.len - source->written_bytes, format, arglist);
345
346 while (source->written_bytes + len >= source->raw.len) {
347 if (source_resize(source) < 0)
348 return GIT_ENOMEM;
349
350 did_resize = 1;
351 }
352
353 if (did_resize)
354 vsnprintf(source->write_ptr, source->raw.len - source->written_bytes, format, arglist);
355
356 source->write_ptr = (char *)source->write_ptr + len;
357 source->written_bytes += len;
358
359 return GIT_SUCCESS;
360 }
361
362 int git__source_write(git_odb_source *source, const void *bytes, size_t len)
363 {
364 assert(source);
365
366 assert(source->open && source->write_ptr);
367
368 while (source->written_bytes + len >= source->raw.len) {
369 if (source_resize(source) < 0)
370 return GIT_ENOMEM;
371 }
372
373 memcpy(source->write_ptr, bytes, len);
374 source->write_ptr = (char *)source->write_ptr + len;
375 source->written_bytes += len;
376
377 return GIT_SUCCESS;
378 }
379
380 static void prepare_write(git_object *object)
381 {
382 if (object->source.write_ptr != NULL || object->source.open)
383 git_object__source_close(object);
384
385 /* TODO: proper size calculation */
386 object->source.raw.data = git__malloc(OBJECT_BASE_SIZE);
387 object->source.raw.len = OBJECT_BASE_SIZE;
388
389 object->source.write_ptr = object->source.raw.data;
390 object->source.written_bytes = 0;
391
392 object->source.open = 1;
393 }
394
395 static int write_back(git_object *object)
396 {
397 int error;
398 git_oid new_id;
399
400 assert(object);
401
402 assert(object->source.open);
403 assert(object->modified);
404
405 object->source.raw.len = object->source.written_bytes;
406
407 if ((error = git_odb_write(&new_id, object->repo->db, &object->source.raw)) < 0)
408 return error;
409
410 if (!object->in_memory)
411 git_hashtable_remove(object->repo->objects, &object->id);
412
413 git_oid_cpy(&object->id, &new_id);
414 git_hashtable_insert(object->repo->objects, &object->id, object);
415
416 object->source.write_ptr = NULL;
417 object->source.written_bytes = 0;
418
419 object->modified = 0;
420 object->in_memory = 0;
421
422 git_object__source_close(object);
423 return GIT_SUCCESS;
424 }
425
426 int git_object__source_open(git_object *object)
427 {
428 int error;
429
430 assert(object && !object->in_memory);
431
432 if (object->source.open)
433 git_object__source_close(object);
434
435 error = git_odb_read(&object->source.raw, object->repo->db, &object->id);
436 if (error < 0)
437 return error;
438
439 object->source.open = 1;
440 return GIT_SUCCESS;
441 }
442
443 void git_object__source_close(git_object *object)
444 {
445 assert(object);
446
447 if (object->source.open) {
448 git_obj_close(&object->source.raw);
449 object->source.open = 0;
450 }
451 }
452
453 int git_object_write(git_object *object)
454 {
455 int error;
456 git_odb_source *source;
457
458 assert(object);
459
460 if (object->modified == 0)
461 return GIT_SUCCESS;
462
463 prepare_write(object);
464 source = &object->source;
465
466 switch (source->raw.type) {
467 case GIT_OBJ_COMMIT:
468 error = git_commit__writeback((git_commit *)object, source);
469 break;
470
471 case GIT_OBJ_TREE:
472 error = git_tree__writeback((git_tree *)object, source);
473 break;
474
475 case GIT_OBJ_TAG:
476 error = git_tag__writeback((git_tag *)object, source);
477 break;
478
479 case GIT_OBJ_BLOB:
480 error = git_blob__writeback((git_blob *)object, source);
481 break;
482
483 default:
484 error = GIT_ERROR;
485 break;
486 }
487
488 if (error < 0) {
489 git_object__source_close(object);
490 return error;
491 }
492
493 return write_back(object);
494 }
495
496 void git_object_free(git_object *object)
497 {
498 assert(object);
499
500 git_object__source_close(object);
501 git_hashtable_remove(object->repo->objects, &object->id);
502
503 switch (object->source.raw.type) {
504 case GIT_OBJ_COMMIT:
505 git_commit__free((git_commit *)object);
506 break;
507
508 case GIT_OBJ_TREE:
509 git_tree__free((git_tree *)object);
510 break;
511
512 case GIT_OBJ_TAG:
513 git_tag__free((git_tag *)object);
514 break;
515
516 case GIT_OBJ_BLOB:
517 git_blob__free((git_blob *)object);
518 break;
519
520 default:
521 free(object);
522 break;
523 }
524 }
525
526 git_odb *git_repository_database(git_repository *repo)
527 {
528 assert(repo);
529 return repo->db;
530 }
531
532 const git_oid *git_object_id(git_object *obj)
533 {
534 assert(obj);
535
536 if (obj->in_memory)
537 return NULL;
538
539 return &obj->id;
540 }
541
542 git_otype git_object_type(git_object *obj)
543 {
544 assert(obj);
545 return obj->source.raw.type;
546 }
547
548 git_repository *git_object_owner(git_object *obj)
549 {
550 assert(obj);
551 return obj->repo;
552 }
553
554 int git_repository_newobject(git_object **object_out, git_repository *repo, git_otype type)
555 {
556 git_object *object = NULL;
557
558 assert(object_out && repo);
559
560 *object_out = NULL;
561
562 switch (type) {
563 case GIT_OBJ_COMMIT:
564 case GIT_OBJ_TAG:
565 case GIT_OBJ_TREE:
566 case GIT_OBJ_BLOB:
567 break;
568
569 default:
570 return GIT_EINVALIDTYPE;
571 }
572
573 object = git__malloc(object_sizes[type]);
574
575 if (object == NULL)
576 return GIT_ENOMEM;
577
578 memset(object, 0x0, object_sizes[type]);
579 object->repo = repo;
580 object->in_memory = 1;
581 object->modified = 1;
582
583 object->source.raw.type = type;
584
585 *object_out = object;
586 return GIT_SUCCESS;
587 }
588
589 int git_repository_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_otype type)
590 {
591 git_object *object = NULL;
592 git_rawobj obj_file;
593 int error = 0;
594
595 assert(repo && object_out && id);
596
597 object = git_hashtable_lookup(repo->objects, id);
598 if (object != NULL) {
599 *object_out = object;
600 return GIT_SUCCESS;
601 }
602
603 error = git_odb_read(&obj_file, repo->db, id);
604 if (error < 0)
605 return error;
606
607 if (type != GIT_OBJ_ANY && type != obj_file.type)
608 return GIT_EINVALIDTYPE;
609
610 type = obj_file.type;
611
612 object = git__malloc(object_sizes[type]);
613
614 if (object == NULL)
615 return GIT_ENOMEM;
616
617 memset(object, 0x0, object_sizes[type]);
618
619 /* Initialize parent object */
620 git_oid_cpy(&object->id, id);
621 object->repo = repo;
622 memcpy(&object->source.raw, &obj_file, sizeof(git_rawobj));
623 object->source.open = 1;
624
625 switch (type) {
626
627 case GIT_OBJ_COMMIT:
628 error = git_commit__parse((git_commit *)object);
629 break;
630
631 case GIT_OBJ_TREE:
632 error = git_tree__parse((git_tree *)object);
633 break;
634
635 case GIT_OBJ_TAG:
636 error = git_tag__parse((git_tag *)object);
637 break;
638
639 case GIT_OBJ_BLOB:
640 error = git_blob__parse((git_blob *)object);
641 break;
642
643 default:
644 break;
645 }
646
647 if (error < 0) {
648 git_object_free(object);
649 return error;
650 }
651
652 git_object__source_close(object);
653 git_hashtable_insert(repo->objects, &object->id, object);
654
655 *object_out = object;
656 return GIT_SUCCESS;
657 }
658
659 #define GIT_NEWOBJECT_TEMPLATE(obj, tp) \
660 int git_##obj##_new(git_##obj **o, git_repository *repo) {\
661 return git_repository_newobject((git_object **)o, repo, GIT_OBJ_##tp); } \
662 int git_##obj##_lookup(git_##obj **o, git_repository *repo, const git_oid *id) { \
663 return git_repository_lookup((git_object **)o, repo, id, GIT_OBJ_##tp); }
664
665 GIT_NEWOBJECT_TEMPLATE(commit, COMMIT)
666 GIT_NEWOBJECT_TEMPLATE(tag, TAG)
667 GIT_NEWOBJECT_TEMPLATE(tree, TREE)
668 GIT_NEWOBJECT_TEMPLATE(blob, BLOB)
669
670