]>
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 | 26 | |
44908fe7 | 27 | #include "git2/object.h" |
d12299fe | 28 | |
3315782c VM |
29 | #include "common.h" |
30 | #include "repository.h" | |
31 | #include "commit.h" | |
32 | #include "tag.h" | |
237da401 | 33 | #include "blob.h" |
6fd195d7 | 34 | #include "fileops.h" |
3315782c | 35 | |
9282e921 | 36 | #include "refs.h" |
37 | ||
f2d6a23a | 38 | #define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/" |
39 | #define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/" | |
40c44d2f | 40 | |
28990938 | 41 | #define GIT_BRANCH_MASTER "master" |
42 | ||
e52ed7a5 | 43 | static const int OBJECT_TABLE_SIZE = 32; |
d45b4a9a | 44 | |
40c44d2f | 45 | typedef struct { |
4b8e27c8 | 46 | char *path_repository; |
e52ed7a5 | 47 | unsigned is_bare:1, has_been_reinit:1; |
40c44d2f | 48 | } repo_init; |
4b8e27c8 | 49 | |
e52ed7a5 VM |
50 | /* |
51 | * Hash table methods | |
d12299fe | 52 | * |
e52ed7a5 VM |
53 | * Callbacks for the ODB cache, implemented |
54 | * as a hash table | |
55 | */ | |
fc658755 | 56 | uint32_t object_table_hash(const void *key, int hash_id) |
3315782c VM |
57 | { |
58 | uint32_t r; | |
59 | git_oid *id; | |
60 | ||
61 | id = (git_oid *)key; | |
fc658755 | 62 | memcpy(&r, id->id + (hash_id * sizeof(uint32_t)), sizeof(r)); |
3315782c VM |
63 | return r; |
64 | } | |
65 | ||
e52ed7a5 VM |
66 | /* |
67 | * Git repository open methods | |
68 | * | |
69 | * Open a repository object from its path | |
70 | */ | |
40c44d2f | 71 | static int assign_repository_DIRs(git_repository *repo, |
691aa968 VM |
72 | const char *git_dir, |
73 | const char *git_object_directory, | |
74 | const char *git_index_file, | |
d2d6912e | 75 | const char *git_work_tree, |
76 | int is_repo_being_created) | |
691aa968 VM |
77 | { |
78 | char path_aux[GIT_PATH_MAX]; | |
eb2f3b47 | 79 | size_t git_dir_path_len; |
80 | int error = GIT_SUCCESS; | |
691aa968 VM |
81 | |
82 | assert(repo); | |
83 | ||
eb2f3b47 | 84 | if (git_dir == NULL) |
691aa968 VM |
85 | return GIT_ENOTFOUND; |
86 | ||
2e6fd09c | 87 | error = gitfo_prettify_dir_path(path_aux, git_dir); |
eb2f3b47 | 88 | if (error < GIT_SUCCESS) |
89 | return error; | |
691aa968 | 90 | |
eb2f3b47 | 91 | git_dir_path_len = strlen(path_aux); |
691aa968 | 92 | |
eb2f3b47 | 93 | /* store GIT_DIR */ |
691aa968 | 94 | repo->path_repository = git__strdup(path_aux); |
8212e2d7 VM |
95 | if (repo->path_repository == NULL) |
96 | return GIT_ENOMEM; | |
691aa968 | 97 | |
d2d6912e | 98 | /* path to GIT_OBJECT_DIRECTORY */ |
691aa968 | 99 | if (git_object_directory == NULL) |
d2d6912e | 100 | git__joinpath(path_aux, repo->path_repository, GIT_OBJECTS_DIR); |
eb2f3b47 | 101 | else { |
2e6fd09c | 102 | error = gitfo_prettify_dir_path(path_aux, git_object_directory); |
eb2f3b47 | 103 | if (error < GIT_SUCCESS) |
104 | return error; | |
105 | } | |
691aa968 | 106 | |
d2d6912e | 107 | /* Ensure GIT_OBJECT_DIRECTORY exists */ |
6f02c3ba | 108 | if (gitfo_isdir(path_aux) < GIT_SUCCESS) |
691aa968 VM |
109 | return GIT_ENOTFOUND; |
110 | ||
d2d6912e | 111 | /* Store GIT_OBJECT_DIRECTORY */ |
691aa968 | 112 | repo->path_odb = git__strdup(path_aux); |
8212e2d7 VM |
113 | if (repo->path_odb == NULL) |
114 | return GIT_ENOMEM; | |
691aa968 | 115 | |
d2d6912e | 116 | if (!is_repo_being_created) { |
117 | /* Ensure HEAD file exists */ | |
118 | git__joinpath(path_aux, repo->path_repository, GIT_HEAD_FILE); | |
119 | if (gitfo_exists(path_aux) < 0) | |
120 | return GIT_ENOTAREPO; | |
eb2f3b47 | 121 | } |
691aa968 | 122 | |
d2d6912e | 123 | /* path to GIT_WORK_TREE */ |
691aa968 VM |
124 | if (git_work_tree == NULL) |
125 | repo->is_bare = 1; | |
eb2f3b47 | 126 | else { |
2e6fd09c | 127 | error = gitfo_prettify_dir_path(path_aux, git_work_tree); |
eb2f3b47 | 128 | if (error < GIT_SUCCESS) |
129 | return error; | |
d2d6912e | 130 | |
131 | /* Store GIT_WORK_TREE */ | |
eb2f3b47 | 132 | repo->path_workdir = git__strdup(path_aux); |
8212e2d7 VM |
133 | if (repo->path_workdir == NULL) |
134 | return GIT_ENOMEM; | |
d2d6912e | 135 | |
136 | /* Path to GIT_INDEX_FILE */ | |
137 | if (git_index_file == NULL) | |
138 | git__joinpath(path_aux, repo->path_repository, GIT_INDEX_FILE); | |
139 | else { | |
140 | error = gitfo_prettify_file_path(path_aux, git_index_file); | |
141 | if (error < GIT_SUCCESS) | |
142 | return error; | |
143 | } | |
144 | ||
145 | if (!is_repo_being_created) { | |
146 | /* Ensure GIT_INDEX_FILE exists */ | |
147 | if (gitfo_exists(path_aux) < 0) | |
148 | return GIT_ENOTAREPO; | |
149 | } | |
150 | ||
151 | /* store GIT_INDEX_FILE */ | |
152 | repo->path_index = git__strdup(path_aux); | |
153 | if (repo->path_index == NULL) | |
154 | return GIT_ENOMEM; | |
eb2f3b47 | 155 | } |
156 | ||
691aa968 VM |
157 | return GIT_SUCCESS; |
158 | } | |
159 | ||
d2d6912e | 160 | static int guess_repository_DIRs(git_repository *repo, const char *repository_path, int is_repo_being_created) |
6fd195d7 | 161 | { |
d2d6912e | 162 | char path_odb[GIT_PATH_MAX] = "\0", path_index[GIT_PATH_MAX] = "\0", path_work_tree[GIT_PATH_MAX] = "\0"; |
163 | char dir_name[MAX_GITDIR_TREE_STRUCTURE_PATH_LENGTH]; | |
f725931b | 164 | |
9dd34b1e | 165 | int error = GIT_SUCCESS; |
6fd195d7 | 166 | |
d2d6912e | 167 | /* Path to objects database */ |
168 | git__joinpath(path_odb, repository_path, GIT_OBJECTS_DIR); | |
6fd195d7 | 169 | |
d2d6912e | 170 | /* Git directory name */ |
171 | if (git__basename_r(dir_name, sizeof(dir_name), repository_path) < 0) | |
f725931b | 172 | return GIT_EINVALIDPATH; |
6fd195d7 | 173 | |
d2d6912e | 174 | if (strcmp(dir_name, DOT_GIT) == 0) { |
175 | ||
176 | /* Path to index file */ | |
177 | git__joinpath(path_index, repository_path, GIT_INDEX_FILE); | |
1544bc31 | 178 | |
d2d6912e | 179 | /* Path to working dir */ |
180 | if (git__dirname_r(path_work_tree, sizeof(path_work_tree), repository_path) < 0) | |
f725931b | 181 | return GIT_EINVALIDPATH; |
6fd195d7 VM |
182 | } |
183 | ||
d2d6912e | 184 | error = assign_repository_DIRs(repo, repository_path, path_odb, !*path_index ? NULL : path_index, !*path_work_tree ? NULL : path_work_tree, is_repo_being_created); |
185 | return error; | |
6fd195d7 VM |
186 | } |
187 | ||
e52ed7a5 | 188 | static git_repository *repository_alloc() |
3315782c VM |
189 | { |
190 | git_repository *repo = git__malloc(sizeof(git_repository)); | |
191 | if (!repo) | |
192 | return NULL; | |
193 | ||
194 | memset(repo, 0x0, sizeof(git_repository)); | |
195 | ||
196 | repo->objects = git_hashtable_alloc( | |
e52ed7a5 VM |
197 | OBJECT_TABLE_SIZE, |
198 | object_table_hash, | |
fc658755 | 199 | (git_hash_keyeq_ptr)git_oid_cmp); |
3315782c | 200 | |
fc658755 | 201 | if (repo->objects == NULL) { |
3315782c VM |
202 | free(repo); |
203 | return NULL; | |
204 | } | |
205 | ||
2f8a8ab2 | 206 | if (git_repository__refcache_init(&repo->references) < GIT_SUCCESS) { |
9282e921 | 207 | git_hashtable_free(repo->objects); |
208 | free(repo); | |
209 | return NULL; | |
210 | } | |
211 | ||
48c27f86 VM |
212 | if (git_vector_init(&repo->memory_objects, 16, NULL) < GIT_SUCCESS) { |
213 | git_hashtable_free(repo->objects); | |
214 | git_repository__refcache_free(&repo->references); | |
215 | free(repo); | |
216 | return NULL; | |
217 | } | |
218 | ||
f335b42c | 219 | repo->gc_enabled = 1; |
6fd195d7 VM |
220 | return repo; |
221 | } | |
222 | ||
ec3c7a16 VM |
223 | static int init_odb(git_repository *repo) |
224 | { | |
225 | return git_odb_open(&repo->db, repo->path_odb); | |
226 | } | |
227 | ||
228 | int git_repository_open3(git_repository **repo_out, | |
229 | const char *git_dir, | |
230 | git_odb *object_database, | |
231 | const char *git_index_file, | |
232 | const char *git_work_tree) | |
233 | { | |
234 | git_repository *repo; | |
235 | int error = GIT_SUCCESS; | |
236 | ||
237 | assert(repo_out); | |
238 | ||
239 | if (object_database == NULL) | |
240 | return GIT_ERROR; | |
241 | ||
242 | repo = repository_alloc(); | |
243 | if (repo == NULL) | |
244 | return GIT_ENOMEM; | |
245 | ||
246 | error = assign_repository_DIRs(repo, | |
247 | git_dir, | |
248 | NULL, | |
249 | git_index_file, | |
d2d6912e | 250 | git_work_tree, |
251 | 0); | |
ec3c7a16 VM |
252 | |
253 | if (error < GIT_SUCCESS) | |
254 | goto cleanup; | |
255 | ||
256 | repo->db = object_database; | |
257 | ||
258 | *repo_out = repo; | |
259 | return GIT_SUCCESS; | |
260 | ||
261 | cleanup: | |
262 | git_repository_free(repo); | |
263 | return error; | |
264 | } | |
265 | ||
266 | ||
691aa968 VM |
267 | int git_repository_open2(git_repository **repo_out, |
268 | const char *git_dir, | |
269 | const char *git_object_directory, | |
270 | const char *git_index_file, | |
271 | const char *git_work_tree) | |
272 | { | |
273 | git_repository *repo; | |
274 | int error = GIT_SUCCESS; | |
275 | ||
276 | assert(repo_out); | |
277 | ||
e52ed7a5 | 278 | repo = repository_alloc(); |
691aa968 VM |
279 | if (repo == NULL) |
280 | return GIT_ENOMEM; | |
281 | ||
40c44d2f | 282 | error = assign_repository_DIRs(repo, |
691aa968 VM |
283 | git_dir, |
284 | git_object_directory, | |
285 | git_index_file, | |
d2d6912e | 286 | git_work_tree, |
287 | 0); | |
691aa968 | 288 | |
6f02c3ba | 289 | if (error < GIT_SUCCESS) |
691aa968 VM |
290 | goto cleanup; |
291 | ||
ec3c7a16 | 292 | error = init_odb(repo); |
6f02c3ba | 293 | if (error < GIT_SUCCESS) |
691aa968 VM |
294 | goto cleanup; |
295 | ||
296 | *repo_out = repo; | |
297 | return GIT_SUCCESS; | |
298 | ||
299 | cleanup: | |
300 | git_repository_free(repo); | |
301 | return error; | |
302 | } | |
303 | ||
d2d6912e | 304 | static int repository_open_internal(git_repository **repo_out, const char *path, int is_repo_being_created) |
6fd195d7 VM |
305 | { |
306 | git_repository *repo; | |
1795f879 VM |
307 | int error = GIT_SUCCESS; |
308 | ||
309 | assert(repo_out && path); | |
6fd195d7 | 310 | |
e52ed7a5 | 311 | repo = repository_alloc(); |
6fd195d7 | 312 | if (repo == NULL) |
1795f879 | 313 | return GIT_ENOMEM; |
6fd195d7 | 314 | |
d2d6912e | 315 | error = guess_repository_DIRs(repo, path, is_repo_being_created); |
6f02c3ba | 316 | if (error < GIT_SUCCESS) |
1795f879 | 317 | goto cleanup; |
3315782c | 318 | |
ec3c7a16 | 319 | error = init_odb(repo); |
6f02c3ba | 320 | if (error < GIT_SUCCESS) |
1795f879 VM |
321 | goto cleanup; |
322 | ||
323 | *repo_out = repo; | |
324 | return GIT_SUCCESS; | |
325 | ||
326 | cleanup: | |
327 | git_repository_free(repo); | |
328 | return error; | |
3315782c VM |
329 | } |
330 | ||
d2d6912e | 331 | int git_repository_open(git_repository **repo_out, const char *path) |
332 | { | |
333 | return repository_open_internal(repo_out, path, 0); | |
334 | } | |
335 | ||
584f49a5 VM |
336 | static void repository_free(git_repository *repo) |
337 | { | |
338 | assert(repo); | |
339 | ||
340 | free(repo->path_workdir); | |
341 | free(repo->path_index); | |
342 | free(repo->path_repository); | |
343 | free(repo->path_odb); | |
344 | ||
345 | git_hashtable_free(repo->objects); | |
346 | git_vector_free(&repo->memory_objects); | |
347 | ||
348 | git_repository__refcache_free(&repo->references); | |
349 | ||
350 | if (repo->db != NULL) | |
351 | git_odb_close(repo->db); | |
352 | ||
353 | if (repo->index != NULL) | |
354 | git_index_free(repo->index); | |
355 | ||
356 | free(repo); | |
357 | } | |
358 | ||
359 | void git_repository_free__no_gc(git_repository *repo) | |
3315782c | 360 | { |
f49a2e49 | 361 | git_object *object; |
48c27f86 VM |
362 | const void *_unused; |
363 | unsigned int i; | |
3315782c | 364 | |
1f080e2d VM |
365 | if (repo == NULL) |
366 | return; | |
1795f879 | 367 | |
584f49a5 VM |
368 | GIT_HASHTABLE_FOREACH(repo->objects, _unused, object, |
369 | object->repo = NULL; | |
370 | object->refcount = 0; | |
371 | ); | |
372 | ||
373 | for (i = 0; i < repo->memory_objects.length; ++i) { | |
374 | object = git_vector_get(&repo->memory_objects, i); | |
375 | object->repo = NULL; | |
376 | object->refcount = 0; | |
377 | } | |
378 | ||
379 | repository_free(repo); | |
380 | } | |
381 | ||
382 | void git_repository_free(git_repository *repo) | |
383 | { | |
384 | git_object *object; | |
385 | const void *_unused; | |
386 | unsigned int i; | |
387 | ||
388 | if (repo == NULL) | |
389 | return; | |
6fd195d7 | 390 | |
f335b42c | 391 | repo->gc_enabled = 0; |
48c27f86 VM |
392 | |
393 | /* force free all the objects */ | |
394 | GIT_HASHTABLE_FOREACH(repo->objects, _unused, object, | |
395 | git_object__free(object); | |
396 | ); | |
397 | ||
398 | for (i = 0; i < repo->memory_objects.length; ++i) { | |
399 | object = git_vector_get(&repo->memory_objects, i); | |
400 | git_object__free(object); | |
401 | } | |
3315782c | 402 | |
584f49a5 | 403 | repository_free(repo); |
3315782c VM |
404 | } |
405 | ||
c836c332 | 406 | int git_repository_index(git_index **index_out, git_repository *repo) |
6fd195d7 | 407 | { |
c836c332 VM |
408 | int error; |
409 | ||
410 | assert(index_out && repo); | |
411 | ||
6fd195d7 | 412 | if (repo->index == NULL) { |
c836c332 VM |
413 | error = git_index_open_inrepo(&repo->index, repo); |
414 | if (error < GIT_SUCCESS) | |
415 | return error; | |
1795f879 | 416 | |
c836c332 | 417 | assert(repo->index != NULL); |
6fd195d7 VM |
418 | } |
419 | ||
c836c332 VM |
420 | *index_out = repo->index; |
421 | return GIT_SUCCESS; | |
6fd195d7 VM |
422 | } |
423 | ||
46f8566a VM |
424 | git_odb *git_repository_database(git_repository *repo) |
425 | { | |
426 | assert(repo); | |
427 | return repo->db; | |
428 | } | |
429 | ||
fb3cd6bc | 430 | static int repo_init_reinit(repo_init *results) |
40c44d2f VM |
431 | { |
432 | /* TODO: reinit the repository */ | |
4b8e27c8 | 433 | results->has_been_reinit = 1; |
434 | return GIT_SUCCESS; | |
435 | } | |
436 | ||
d2d6912e | 437 | static int repo_init_createhead(git_repository *repo) |
e1f8cad0 | 438 | { |
d2d6912e | 439 | git_reference *head_reference; |
440 | return git_reference_create_symbolic(&head_reference, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_MASTER_FILE); | |
441 | } | |
e1f8cad0 | 442 | |
d2d6912e | 443 | static int repo_init_check_head_existence(char * repository_path) |
444 | { | |
445 | char temp_path[GIT_PATH_MAX]; | |
e1f8cad0 | 446 | |
d2d6912e | 447 | git__joinpath(temp_path, repository_path, GIT_HEAD_FILE); |
448 | return gitfo_exists(temp_path); | |
e1f8cad0 | 449 | } |
450 | ||
fb3cd6bc | 451 | static int repo_init_structure(repo_init *results) |
4b8e27c8 | 452 | { |
a67a096a | 453 | const int mode = 0755; /* or 0777 ? */ |
4b8e27c8 | 454 | |
40c44d2f | 455 | char temp_path[GIT_PATH_MAX]; |
40c44d2f | 456 | char *git_dir = results->path_repository; |
4b8e27c8 | 457 | |
a67a096a | 458 | if (gitfo_mkdir_recurs(git_dir, mode)) |
459 | return GIT_ERROR; | |
460 | ||
8ea2c83b | 461 | /* Creates the '/objects/info/' directory */ |
874c3b6f VM |
462 | git__joinpath(temp_path, git_dir, GIT_OBJECTS_INFO_DIR); |
463 | if (gitfo_mkdir_recurs(temp_path, mode) < GIT_SUCCESS) | |
a67a096a | 464 | return GIT_ERROR; |
465 | ||
8ea2c83b | 466 | /* Creates the '/objects/pack/' directory */ |
874c3b6f | 467 | git__joinpath(temp_path, git_dir, GIT_OBJECTS_PACK_DIR); |
a67a096a | 468 | if (gitfo_mkdir(temp_path, mode)) |
469 | return GIT_ERROR; | |
470 | ||
1c2c7c0d | 471 | /* Creates the '/refs/heads/' directory */ |
874c3b6f | 472 | git__joinpath(temp_path, git_dir, GIT_REFS_HEADS_DIR); |
8ea2c83b | 473 | if (gitfo_mkdir_recurs(temp_path, mode)) |
1c2c7c0d | 474 | return GIT_ERROR; |
475 | ||
476 | /* Creates the '/refs/tags/' directory */ | |
874c3b6f | 477 | git__joinpath(temp_path, git_dir, GIT_REFS_TAGS_DIR); |
1c2c7c0d | 478 | if (gitfo_mkdir(temp_path, mode)) |
479 | return GIT_ERROR; | |
480 | ||
40c44d2f | 481 | /* TODO: what's left? templates? */ |
4b8e27c8 | 482 | |
483 | return GIT_SUCCESS; | |
484 | } | |
485 | ||
fb3cd6bc | 486 | static int repo_init_find_dir(repo_init *results, const char* path) |
4b8e27c8 | 487 | { |
4b8e27c8 | 488 | char temp_path[GIT_PATH_MAX]; |
9dd34b1e | 489 | int error = GIT_SUCCESS; |
4b8e27c8 | 490 | |
2e6fd09c | 491 | error = gitfo_prettify_dir_path(temp_path, path); |
9dd34b1e | 492 | if (error < GIT_SUCCESS) |
493 | return error; | |
4b8e27c8 | 494 | |
40c44d2f | 495 | if (!results->is_bare) { |
874c3b6f | 496 | git__joinpath(temp_path, temp_path, GIT_DIR); |
4b8e27c8 | 497 | } |
498 | ||
499 | results->path_repository = git__strdup(temp_path); | |
8212e2d7 VM |
500 | if (results->path_repository == NULL) |
501 | return GIT_ENOMEM; | |
4b8e27c8 | 502 | |
503 | return GIT_SUCCESS; | |
504 | } | |
505 | ||
40c44d2f | 506 | int git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare) |
4b8e27c8 | 507 | { |
4b8e27c8 | 508 | int error = GIT_SUCCESS; |
40c44d2f | 509 | repo_init results; |
4b8e27c8 | 510 | |
511 | assert(repo_out && path); | |
512 | ||
40c44d2f | 513 | results.path_repository = NULL; |
08190e2a | 514 | results.is_bare = is_bare; |
4b8e27c8 | 515 | |
40c44d2f | 516 | error = repo_init_find_dir(&results, path); |
4b8e27c8 | 517 | if (error < GIT_SUCCESS) |
518 | goto cleanup; | |
519 | ||
d2d6912e | 520 | if (!repo_init_check_head_existence(results.path_repository)) |
521 | return repo_init_reinit(&results); | |
522 | ||
40c44d2f | 523 | error = repo_init_structure(&results); |
4b8e27c8 | 524 | if (error < GIT_SUCCESS) |
525 | goto cleanup; | |
526 | ||
d2d6912e | 527 | error = repository_open_internal(repo_out, results.path_repository, 1); |
528 | if (error < GIT_SUCCESS) | |
529 | goto cleanup; | |
530 | ||
531 | assert((*repo_out)->is_bare == is_bare); | |
532 | ||
533 | error = repo_init_createhead(*repo_out); | |
4b8e27c8 | 534 | |
535 | cleanup: | |
58fcfc26 | 536 | free(results.path_repository); |
4b8e27c8 | 537 | return error; |
40c44d2f | 538 | } |