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