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