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