]>
Commit | Line | Data |
---|---|---|
3315782c | 1 | /* |
5e0de328 | 2 | * Copyright (C) 2009-2012 the libgit2 contributors |
3315782c | 3 | * |
bb742ede VM |
4 | * This file is part of libgit2, distributed under the GNU GPL v2 with |
5 | * a Linking Exception. For full terms see the included COPYING file. | |
3315782c | 6 | */ |
e802d8cc | 7 | #include <stdarg.h> |
3315782c | 8 | |
44908fe7 | 9 | #include "git2/object.h" |
d12299fe | 10 | |
3315782c VM |
11 | #include "common.h" |
12 | #include "repository.h" | |
13 | #include "commit.h" | |
14 | #include "tag.h" | |
237da401 | 15 | #include "blob.h" |
6fd195d7 | 16 | #include "fileops.h" |
b22d1479 | 17 | #include "config.h" |
9282e921 | 18 | #include "refs.h" |
19 | ||
f2d6a23a | 20 | #define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/" |
21 | #define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/" | |
40c44d2f | 22 | |
6a01b6bd | 23 | #define GIT_FILE_CONTENT_PREFIX "gitdir: " |
6a01b6bd | 24 | |
28990938 | 25 | #define GIT_BRANCH_MASTER "master" |
26 | ||
5663e61a | 27 | #define GIT_CONFIG_CORE_REPOSITORYFORMATVERSION "core.repositoryformatversion" |
28 | #define GIT_REPOSITORYFORMATVERSION 0 | |
691aa968 | 29 | |
9462c471 VM |
30 | static void drop_odb(git_repository *repo) |
31 | { | |
32 | if (repo->_odb != NULL) { | |
33 | GIT_REFCOUNT_OWN(repo->_odb, NULL); | |
34 | git_odb_free(repo->_odb); | |
35 | repo->_odb = NULL; | |
eb2f3b47 | 36 | } |
9462c471 | 37 | } |
691aa968 | 38 | |
9462c471 VM |
39 | static void drop_config(git_repository *repo) |
40 | { | |
41 | if (repo->_config != NULL) { | |
42 | GIT_REFCOUNT_OWN(repo->_config, NULL); | |
43 | git_config_free(repo->_config); | |
44 | repo->_config = NULL; | |
eb2f3b47 | 45 | } |
691aa968 VM |
46 | } |
47 | ||
9462c471 | 48 | static void drop_index(git_repository *repo) |
6fd195d7 | 49 | { |
9462c471 VM |
50 | if (repo->_index != NULL) { |
51 | GIT_REFCOUNT_OWN(repo->_index, NULL); | |
52 | git_index_free(repo->_index); | |
53 | repo->_index = NULL; | |
54 | } | |
e0011be3 VM |
55 | } |
56 | ||
9462c471 | 57 | void git_repository_free(git_repository *repo) |
e0011be3 | 58 | { |
9462c471 VM |
59 | if (repo == NULL) |
60 | return; | |
6fd195d7 | 61 | |
9462c471 VM |
62 | git_cache_free(&repo->objects); |
63 | git_repository__refcache_free(&repo->references); | |
73b51450 | 64 | git_attr_cache_flush(repo); |
6fd195d7 | 65 | |
9462c471 VM |
66 | git__free(repo->path_repository); |
67 | git__free(repo->workdir); | |
6fd195d7 | 68 | |
9462c471 VM |
69 | drop_config(repo); |
70 | drop_index(repo); | |
71 | drop_odb(repo); | |
72 | ||
73 | git__free(repo); | |
6fd195d7 VM |
74 | } |
75 | ||
9462c471 VM |
76 | /* |
77 | * Git repository open methods | |
78 | * | |
79 | * Open a repository object from its path | |
80 | */ | |
97769280 | 81 | static int quickcheck_repository_dir(git_buf *repository_path) |
5ad739e8 | 82 | { |
97769280 | 83 | /* Check OBJECTS_DIR first, since it will generate the longest path name */ |
0534641d | 84 | if (git_path_contains_dir(repository_path, GIT_OBJECTS_DIR) < 0) |
5ad739e8 VM |
85 | return GIT_ERROR; |
86 | ||
97769280 | 87 | /* Ensure HEAD file exists */ |
0534641d | 88 | if (git_path_contains_file(repository_path, GIT_HEAD_FILE) < 0) |
5ad739e8 VM |
89 | return GIT_ERROR; |
90 | ||
0534641d | 91 | if (git_path_contains_dir(repository_path, GIT_REFS_DIR) < 0) |
5ad739e8 VM |
92 | return GIT_ERROR; |
93 | ||
94 | return GIT_SUCCESS; | |
95 | } | |
96 | ||
9462c471 | 97 | |
51d00446 | 98 | static git_repository *repository_alloc(void) |
3315782c | 99 | { |
81201a4c | 100 | int error; |
101 | ||
3315782c VM |
102 | git_repository *repo = git__malloc(sizeof(git_repository)); |
103 | if (!repo) | |
104 | return NULL; | |
105 | ||
106 | memset(repo, 0x0, sizeof(git_repository)); | |
107 | ||
81201a4c | 108 | error = git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free); |
109 | if (error < GIT_SUCCESS) { | |
3286c408 | 110 | git__free(repo); |
81201a4c | 111 | return NULL; |
112 | } | |
3315782c | 113 | |
6fd195d7 VM |
114 | return repo; |
115 | } | |
116 | ||
9462c471 | 117 | static int load_config_data(git_repository *repo) |
ec3c7a16 | 118 | { |
9462c471 VM |
119 | int error, is_bare; |
120 | git_config *config; | |
ec3c7a16 | 121 | |
9462c471 VM |
122 | error = git_repository_config__weakptr(&config, repo); |
123 | if (error < GIT_SUCCESS) | |
124 | return error; | |
ec3c7a16 | 125 | |
9462c471 VM |
126 | error = git_config_get_bool(config, "core.bare", &is_bare); |
127 | if (error == GIT_SUCCESS) | |
128 | repo->is_bare = is_bare; | |
ec3c7a16 | 129 | |
c94785a9 VM |
130 | /* TODO: what else can we load/cache here? */ |
131 | ||
9462c471 VM |
132 | return GIT_SUCCESS; |
133 | } | |
ec3c7a16 | 134 | |
9462c471 VM |
135 | static int load_workdir(git_repository *repo) |
136 | { | |
97769280 RB |
137 | int error; |
138 | git_buf workdir_buf = GIT_BUF_INIT; | |
ec3c7a16 | 139 | |
97769280 RB |
140 | if (repo->is_bare) |
141 | return GIT_SUCCESS; | |
ec3c7a16 | 142 | |
97769280 RB |
143 | git_path_dirname_r(&workdir_buf, repo->path_repository); |
144 | git_path_to_dir(&workdir_buf); | |
e0011be3 | 145 | |
97769280 RB |
146 | if ((error = git_buf_lasterror(&workdir_buf)) == GIT_SUCCESS) |
147 | repo->workdir = git_buf_detach(&workdir_buf); | |
ec3c7a16 | 148 | |
97769280 RB |
149 | git_buf_free(&workdir_buf); |
150 | ||
151 | return error; | |
ec3c7a16 VM |
152 | } |
153 | ||
9462c471 | 154 | int git_repository_open(git_repository **repo_out, const char *path) |
691aa968 | 155 | { |
691aa968 | 156 | int error = GIT_SUCCESS; |
97769280 | 157 | git_buf path_buf = GIT_BUF_INIT; |
9462c471 VM |
158 | git_repository *repo = NULL; |
159 | ||
97769280 | 160 | error = git_path_prettify_dir(&path_buf, path, NULL); |
9462c471 | 161 | if (error < GIT_SUCCESS) |
97769280 | 162 | goto cleanup; |
9462c471 VM |
163 | |
164 | /** | |
165 | * Check if the path we've been given is actually the path | |
166 | * of the working dir, by testing if it contains a `.git` | |
167 | * folder inside of it. | |
168 | */ | |
0534641d RB |
169 | if (git_path_contains_dir(&path_buf, GIT_DIR) == GIT_SUCCESS) |
170 | git_buf_joinpath(&path_buf, path_buf.ptr, GIT_DIR); | |
9462c471 | 171 | |
97769280 RB |
172 | if (quickcheck_repository_dir(&path_buf) < GIT_SUCCESS) { |
173 | error = git__throw(GIT_ENOTAREPO, | |
0b44c065 | 174 | "The given path (%s) is not a valid Git repository", git_buf_cstr(&path_buf)); |
97769280 RB |
175 | goto cleanup; |
176 | } | |
691aa968 | 177 | |
e52ed7a5 | 178 | repo = repository_alloc(); |
97769280 RB |
179 | if (repo == NULL) { |
180 | error = GIT_ENOMEM; | |
181 | goto cleanup; | |
182 | } | |
691aa968 | 183 | |
97769280 | 184 | repo->path_repository = git_buf_detach(&path_buf); |
9462c471 | 185 | if (repo->path_repository == NULL) { |
97769280 RB |
186 | error = GIT_ENOMEM; |
187 | goto cleanup; | |
9462c471 | 188 | } |
691aa968 | 189 | |
9462c471 | 190 | error = load_config_data(repo); |
97769280 RB |
191 | if (error < GIT_SUCCESS) |
192 | goto cleanup; | |
691aa968 | 193 | |
9462c471 | 194 | error = load_workdir(repo); |
97769280 RB |
195 | if (error < GIT_SUCCESS) |
196 | goto cleanup; | |
691aa968 VM |
197 | |
198 | *repo_out = repo; | |
199 | return GIT_SUCCESS; | |
97769280 RB |
200 | |
201 | cleanup: | |
202 | git_repository_free(repo); | |
203 | git_buf_free(&path_buf); | |
204 | return error; | |
691aa968 VM |
205 | } |
206 | ||
9462c471 | 207 | static int load_config( |
07ff8817 VM |
208 | git_config **out, |
209 | git_repository *repo, | |
40fe5fbe | 210 | const char *global_config_path, |
07ff8817 | 211 | const char *system_config_path) |
b22d1479 | 212 | { |
97769280 | 213 | git_buf config_path = GIT_BUF_INIT; |
07ff8817 | 214 | int error; |
9462c471 | 215 | git_config *cfg = NULL; |
b22d1479 | 216 | |
9462c471 | 217 | assert(repo && out); |
07ff8817 | 218 | |
9462c471 | 219 | error = git_config_new(&cfg); |
b22d1479 | 220 | if (error < GIT_SUCCESS) |
07ff8817 | 221 | return error; |
b22d1479 | 222 | |
97769280 RB |
223 | error = git_buf_joinpath(&config_path, repo->path_repository, |
224 | GIT_CONFIG_FILENAME_INREPO); | |
225 | if (error < GIT_SUCCESS) | |
226 | goto cleanup; | |
227 | ||
228 | error = git_config_add_file_ondisk(cfg, config_path.ptr, 3); | |
229 | git_buf_free(&config_path); /* done with config_path now */ | |
07ff8817 | 230 | if (error < GIT_SUCCESS) |
b22d1479 | 231 | goto cleanup; |
b22d1479 | 232 | |
40fe5fbe | 233 | if (global_config_path != NULL) { |
9462c471 | 234 | error = git_config_add_file_ondisk(cfg, global_config_path, 2); |
40fe5fbe CMN |
235 | if (error < GIT_SUCCESS) |
236 | goto cleanup; | |
b22d1479 CMN |
237 | } |
238 | ||
07ff8817 | 239 | if (system_config_path != NULL) { |
9462c471 | 240 | error = git_config_add_file_ondisk(cfg, system_config_path, 1); |
07ff8817 VM |
241 | if (error < GIT_SUCCESS) |
242 | goto cleanup; | |
9ba9e513 CMN |
243 | } |
244 | ||
9462c471 | 245 | *out = cfg; |
07ff8817 | 246 | return GIT_SUCCESS; |
b22d1479 CMN |
247 | |
248 | cleanup: | |
9462c471 VM |
249 | git_config_free(cfg); |
250 | *out = NULL; | |
b22d1479 CMN |
251 | return error; |
252 | } | |
253 | ||
9462c471 | 254 | int git_repository_config__weakptr(git_config **out, git_repository *repo) |
40fe5fbe | 255 | { |
9462c471 VM |
256 | if (repo->_config == NULL) { |
257 | int error; | |
97769280 | 258 | git_buf global_buf = GIT_BUF_INIT, system_buf = GIT_BUF_INIT; |
9462c471 VM |
259 | |
260 | const char *global_config_path = NULL; | |
261 | const char *system_config_path = NULL; | |
40fe5fbe | 262 | |
97769280 RB |
263 | if (git_config_find_global_r(&global_buf) == GIT_SUCCESS) |
264 | global_config_path = global_buf.ptr; | |
40fe5fbe | 265 | |
97769280 RB |
266 | if (git_config_find_system_r(&system_buf) == GIT_SUCCESS) |
267 | system_config_path = system_buf.ptr; | |
40fe5fbe | 268 | |
9462c471 | 269 | error = load_config(&repo->_config, repo, global_config_path, system_config_path); |
97769280 RB |
270 | |
271 | git_buf_free(&global_buf); | |
272 | git_buf_free(&system_buf); | |
273 | ||
9462c471 VM |
274 | if (error < GIT_SUCCESS) |
275 | return error; | |
276 | ||
277 | GIT_REFCOUNT_OWN(repo->_config, repo); | |
278 | } | |
40fe5fbe | 279 | |
9462c471 VM |
280 | *out = repo->_config; |
281 | return GIT_SUCCESS; | |
40fe5fbe CMN |
282 | } |
283 | ||
9462c471 | 284 | int git_repository_config(git_config **out, git_repository *repo) |
fd0574e5 | 285 | { |
9462c471 | 286 | int error = git_repository_config__weakptr(out, repo); |
fd0574e5 | 287 | |
9462c471 VM |
288 | if (error == GIT_SUCCESS) { |
289 | GIT_REFCOUNT_INC(*out); | |
290 | } | |
fd0574e5 | 291 | |
9462c471 VM |
292 | return error; |
293 | } | |
294 | ||
295 | void git_repository_set_config(git_repository *repo, git_config *config) | |
296 | { | |
297 | assert(repo && config); | |
298 | ||
299 | drop_config(repo); | |
300 | ||
301 | repo->_config = config; | |
302 | GIT_REFCOUNT_OWN(repo->_config, repo); | |
303 | } | |
304 | ||
305 | int git_repository_odb__weakptr(git_odb **out, git_repository *repo) | |
306 | { | |
307 | assert(repo && out); | |
308 | ||
309 | if (repo->_odb == NULL) { | |
310 | int error; | |
97769280 | 311 | git_buf odb_path = GIT_BUF_INIT; |
9462c471 | 312 | |
97769280 RB |
313 | error = git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR); |
314 | if (error < GIT_SUCCESS) | |
315 | return error; | |
9462c471 | 316 | |
97769280 RB |
317 | error = git_odb_open(&repo->_odb, odb_path.ptr); |
318 | git_buf_free(&odb_path); /* done with path */ | |
9462c471 VM |
319 | if (error < GIT_SUCCESS) |
320 | return error; | |
321 | ||
322 | GIT_REFCOUNT_OWN(repo->_odb, repo); | |
323 | } | |
fd0574e5 | 324 | |
9462c471 | 325 | *out = repo->_odb; |
fd0574e5 RG |
326 | return GIT_SUCCESS; |
327 | } | |
328 | ||
9462c471 | 329 | int git_repository_odb(git_odb **out, git_repository *repo) |
6fd195d7 | 330 | { |
9462c471 | 331 | int error = git_repository_odb__weakptr(out, repo); |
1795f879 | 332 | |
9462c471 VM |
333 | if (error == GIT_SUCCESS) { |
334 | GIT_REFCOUNT_INC(*out); | |
335 | } | |
6fd195d7 | 336 | |
9462c471 VM |
337 | return error; |
338 | } | |
6fd195d7 | 339 | |
9462c471 VM |
340 | void git_repository_set_odb(git_repository *repo, git_odb *odb) |
341 | { | |
342 | assert(repo && odb); | |
3315782c | 343 | |
9462c471 | 344 | drop_odb(repo); |
1795f879 | 345 | |
9462c471 VM |
346 | repo->_odb = odb; |
347 | GIT_REFCOUNT_OWN(repo->_odb, repo); | |
348 | } | |
349 | ||
350 | int git_repository_index__weakptr(git_index **out, git_repository *repo) | |
351 | { | |
352 | assert(out && repo); | |
353 | ||
9462c471 VM |
354 | if (repo->_index == NULL) { |
355 | int error; | |
97769280 | 356 | git_buf index_path = GIT_BUF_INIT; |
9462c471 | 357 | |
97769280 RB |
358 | error = git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE); |
359 | if (error < GIT_SUCCESS) | |
360 | return error; | |
9462c471 | 361 | |
97769280 RB |
362 | error = git_index_open(&repo->_index, index_path.ptr); |
363 | git_buf_free(&index_path); /* done with path */ | |
9462c471 VM |
364 | if (error < GIT_SUCCESS) |
365 | return error; | |
366 | ||
367 | GIT_REFCOUNT_OWN(repo->_index, repo); | |
368 | } | |
369 | ||
9462c471 | 370 | *out = repo->_index; |
1795f879 | 371 | return GIT_SUCCESS; |
9462c471 | 372 | } |
1795f879 | 373 | |
9462c471 VM |
374 | int git_repository_index(git_index **out, git_repository *repo) |
375 | { | |
376 | int error = git_repository_index__weakptr(out, repo); | |
377 | ||
378 | if (error == GIT_SUCCESS) { | |
379 | GIT_REFCOUNT_INC(*out); | |
380 | } | |
381 | ||
382 | return error; | |
3315782c VM |
383 | } |
384 | ||
9462c471 VM |
385 | void git_repository_set_index(git_repository *repo, git_index *index) |
386 | { | |
387 | assert(repo && index); | |
388 | ||
389 | drop_index(repo); | |
390 | ||
391 | repo->_index = index; | |
392 | GIT_REFCOUNT_OWN(repo->_index, repo); | |
393 | } | |
394 | ||
395 | ||
5ec05d07 | 396 | static int retrieve_device(dev_t *device_out, const char *path) |
f2e6b877 RG |
397 | { |
398 | struct stat path_info; | |
399 | ||
400 | assert(device_out); | |
401 | ||
19ac1ed7 | 402 | if (p_lstat(path, &path_info)) |
f2e6b877 RG |
403 | return git__throw(GIT_EOSERR, "Failed to get file informations: %s", path); |
404 | ||
405 | *device_out = path_info.st_dev; | |
406 | ||
407 | return GIT_SUCCESS; | |
408 | } | |
409 | ||
97769280 RB |
410 | /* |
411 | * This function returns furthest offset into path where a ceiling dir | |
412 | * is found, so we can stop processing the path at that point. | |
413 | * | |
414 | * Note: converting this to use git_bufs instead of GIT_PATH_MAX buffers on | |
415 | * the stack could remove directories name limits, but at the cost of doing | |
416 | * repeated malloc/frees inside the loop below, so let's not do it now. | |
417 | */ | |
418 | static int retrieve_ceiling_directories_offset( | |
419 | const char *path, | |
420 | const char *ceiling_directories) | |
f2e6b877 RG |
421 | { |
422 | char buf[GIT_PATH_MAX + 1]; | |
423 | char buf2[GIT_PATH_MAX + 1]; | |
424 | const char *ceil, *sep; | |
425 | int len, max_len = -1; | |
426 | int min_len; | |
427 | ||
428 | assert(path); | |
429 | ||
5ad739e8 | 430 | min_len = git_path_root(path) + 1; |
f2e6b877 RG |
431 | |
432 | if (ceiling_directories == NULL || min_len == 0) | |
433 | return min_len; | |
434 | ||
435 | for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) { | |
436 | for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++); | |
437 | len = sep - ceil; | |
438 | ||
97769280 | 439 | if (len == 0 || len >= (int)sizeof(buf) || git_path_root(ceil) == -1) |
f2e6b877 RG |
440 | continue; |
441 | ||
442 | strncpy(buf, ceil, len); | |
443 | buf[len] = '\0'; | |
444 | ||
5ad739e8 | 445 | if (p_realpath(buf, buf2) == NULL) |
f2e6b877 RG |
446 | continue; |
447 | ||
448 | len = strlen(buf2); | |
449 | if (len > 0 && buf2[len-1] == '/') | |
450 | buf[--len] = '\0'; | |
451 | ||
452 | if (!strncmp(path, buf2, len) && | |
453 | path[len] == '/' && | |
454 | len > max_len) | |
455 | { | |
456 | max_len = len; | |
457 | } | |
458 | } | |
459 | ||
460 | return max_len <= min_len ? min_len : max_len; | |
461 | } | |
462 | ||
97769280 RB |
463 | /* |
464 | * Read the contents of `file_path` and set `path_out` to the repo dir that | |
465 | * it points to. Before calling, set `path_out` to the base directory that | |
466 | * should be used if the contents of `file_path` are a relative path. | |
467 | */ | |
468 | static int read_gitfile(git_buf *path_out, const char *file_path, const char *base_path) | |
6a01b6bd | 469 | { |
13224ea4 | 470 | git_buf file = GIT_BUF_INIT; |
6f1d23b2 | 471 | int error; |
6a01b6bd | 472 | |
97769280 | 473 | assert(path_out && file_path); |
6a01b6bd | 474 | |
f79026b4 | 475 | error = git_futils_readbuffer(&file, file_path); |
6a01b6bd RG |
476 | if (error < GIT_SUCCESS) |
477 | return error; | |
478 | ||
13224ea4 VM |
479 | if (git__prefixcmp((char *)file.ptr, GIT_FILE_CONTENT_PREFIX)) { |
480 | git_buf_free(&file); | |
6a01b6bd RG |
481 | return git__throw(GIT_ENOTFOUND, "Invalid gitfile format `%s`", file_path); |
482 | } | |
483 | ||
13224ea4 | 484 | git_buf_rtrim(&file); |
6a01b6bd | 485 | |
13224ea4 VM |
486 | if (strlen(GIT_FILE_CONTENT_PREFIX) == file.size) { |
487 | git_buf_free(&file); | |
6a01b6bd RG |
488 | return git__throw(GIT_ENOTFOUND, "No path in git file `%s`", file_path); |
489 | } | |
490 | ||
97769280 | 491 | error = git_path_prettify_dir(path_out, |
13224ea4 | 492 | ((char *)file.ptr) + strlen(GIT_FILE_CONTENT_PREFIX), base_path); |
97769280 | 493 | |
13224ea4 | 494 | git_buf_free(&file); |
6a01b6bd | 495 | |
1744fafe | 496 | if (error == GIT_SUCCESS && git_path_exists(path_out->ptr) == 0) |
5ad739e8 VM |
497 | return GIT_SUCCESS; |
498 | ||
97769280 | 499 | return git__throw(GIT_EOBJCORRUPTED, "The `.git` file points to a nonexistent path"); |
6a01b6bd RG |
500 | } |
501 | ||
9462c471 VM |
502 | int git_repository_discover( |
503 | char *repository_path, | |
504 | size_t size, | |
505 | const char *start_path, | |
506 | int across_fs, | |
507 | const char *ceiling_dirs) | |
fd0574e5 | 508 | { |
fd0574e5 | 509 | int error, ceiling_offset; |
97769280 RB |
510 | git_buf bare_path = GIT_BUF_INIT; |
511 | git_buf normal_path = GIT_BUF_INIT; | |
512 | git_buf *found_path = NULL; | |
393a9f9e | 513 | dev_t current_device = 0; |
fd0574e5 RG |
514 | |
515 | assert(start_path && repository_path); | |
fd0574e5 | 516 | |
97769280 RB |
517 | *repository_path = '\0'; |
518 | ||
519 | error = git_path_prettify_dir(&bare_path, start_path, NULL); | |
fd0574e5 | 520 | if (error < GIT_SUCCESS) |
97769280 | 521 | goto cleanup; |
fd0574e5 RG |
522 | |
523 | if (!across_fs) { | |
97769280 | 524 | error = retrieve_device(¤t_device, bare_path.ptr); |
fd0574e5 | 525 | if (error < GIT_SUCCESS) |
97769280 | 526 | goto cleanup; |
fd0574e5 RG |
527 | } |
528 | ||
97769280 | 529 | ceiling_offset = retrieve_ceiling_directories_offset(bare_path.ptr, ceiling_dirs); |
fd0574e5 | 530 | |
5ad739e8 | 531 | while(1) { |
97769280 RB |
532 | error = git_buf_joinpath(&normal_path, bare_path.ptr, DOT_GIT); |
533 | if (error < GIT_SUCCESS) | |
534 | break; | |
535 | ||
5ad739e8 VM |
536 | /** |
537 | * If the `.git` file is regular instead of | |
538 | * a directory, it should contain the path of the actual git repository | |
539 | */ | |
1744fafe | 540 | if (git_path_isfile(normal_path.ptr) == GIT_SUCCESS) { |
97769280 | 541 | git_buf gitfile_path = GIT_BUF_INIT; |
fd0574e5 | 542 | |
97769280 | 543 | error = read_gitfile(&gitfile_path, normal_path.ptr, bare_path.ptr); |
fd0574e5 | 544 | if (error < GIT_SUCCESS) |
97769280 RB |
545 | git__rethrow(error, "Unable to read git file `%s`", normal_path.ptr); |
546 | else if ((error = quickcheck_repository_dir(&gitfile_path)) < GIT_SUCCESS) | |
547 | git__throw(GIT_ENOTFOUND, | |
548 | "The `.git` file found at '%s' points " | |
549 | "to a nonexistent git folder", normal_path.ptr); | |
550 | else { | |
551 | git_buf_swap(&normal_path, &gitfile_path); | |
552 | found_path = &normal_path; | |
553 | } | |
fd0574e5 | 554 | |
97769280 RB |
555 | git_buf_free(&gitfile_path); |
556 | break; | |
fd0574e5 RG |
557 | } |
558 | ||
5ad739e8 VM |
559 | /** |
560 | * If the `.git` file is a folder, we check inside of it | |
561 | */ | |
1744fafe | 562 | if (git_path_isdir(normal_path.ptr) == GIT_SUCCESS) { |
97769280 | 563 | error = quickcheck_repository_dir(&normal_path); |
5ad739e8 | 564 | if (error == GIT_SUCCESS) { |
97769280 | 565 | found_path = &normal_path; |
5ad739e8 VM |
566 | break; |
567 | } | |
fd0574e5 RG |
568 | } |
569 | ||
5ad739e8 VM |
570 | /** |
571 | * Otherwise, the repository may be bare, let's check | |
572 | * the root anyway | |
573 | */ | |
97769280 | 574 | error = quickcheck_repository_dir(&bare_path); |
fd0574e5 | 575 | if (error == GIT_SUCCESS) { |
97769280 | 576 | found_path = &bare_path; |
fd0574e5 RG |
577 | break; |
578 | } | |
579 | ||
97769280 RB |
580 | /** |
581 | * If we didn't find it, walk up the tree | |
582 | */ | |
583 | error = git_path_dirname_r(&normal_path, bare_path.ptr); | |
584 | if (error < GIT_SUCCESS) { | |
585 | git__rethrow(GIT_EOSERR, "Failed to dirname '%s'", bare_path.ptr); | |
586 | break; | |
587 | } | |
588 | ||
589 | git_buf_swap(&bare_path, &normal_path); | |
fd0574e5 RG |
590 | |
591 | if (!across_fs) { | |
592 | dev_t new_device; | |
97769280 | 593 | error = retrieve_device(&new_device, bare_path.ptr); |
fd0574e5 | 594 | |
5ad739e8 | 595 | if (error < GIT_SUCCESS || current_device != new_device) { |
97769280 | 596 | error = git__throw(GIT_ENOTAREPO, |
9462c471 | 597 | "Not a git repository (or any parent up to mount parent %s)\n" |
97769280 RB |
598 | "Stopping at filesystem boundary.", normal_path.ptr); |
599 | break; | |
fd0574e5 RG |
600 | } |
601 | current_device = new_device; | |
602 | } | |
603 | ||
97769280 RB |
604 | /* nothing has been found, lets try the parent directory |
605 | * but stop if we hit one of the ceiling directories | |
606 | */ | |
607 | if (bare_path.ptr[ceiling_offset] == '\0') { | |
608 | error = git__throw(GIT_ENOTAREPO, | |
9462c471 | 609 | "Not a git repository (or any of the parent directories): %s", start_path); |
97769280 | 610 | break; |
9d9bab5c | 611 | } |
fd0574e5 RG |
612 | } |
613 | ||
97769280 RB |
614 | assert(found_path || error != GIT_SUCCESS); |
615 | ||
616 | if (found_path) { | |
617 | if ((error = git_path_to_dir(found_path)) < GIT_SUCCESS) | |
618 | git__rethrow(error, "Could not convert git repository to directory"); | |
619 | else if (size < (size_t)(found_path->size + 1)) | |
620 | error = git__throw(GIT_ESHORTBUFFER, | |
621 | "The repository buffer is not long enough to " | |
622 | "handle the repository path `%s`", found_path->ptr); | |
623 | else | |
624 | git_buf_copy_cstr(repository_path, size, found_path); | |
fd0574e5 RG |
625 | } |
626 | ||
97769280 RB |
627 | cleanup: |
628 | git_buf_free(&bare_path); | |
629 | git_buf_free(&normal_path); | |
630 | return error; | |
fd0574e5 RG |
631 | } |
632 | ||
5663e61a | 633 | static int check_repositoryformatversion(git_repository *repo) |
40c44d2f | 634 | { |
5663e61a | 635 | git_config *config; |
636 | int version, error = GIT_SUCCESS; | |
637 | ||
638 | if ((error = git_repository_config(&config, repo)) < GIT_SUCCESS) | |
639 | return git__throw(error, "Failed to open config file."); | |
640 | ||
641 | error = git_config_get_int32(config, GIT_CONFIG_CORE_REPOSITORYFORMATVERSION, &version); | |
642 | ||
643 | if (GIT_REPOSITORYFORMATVERSION < version) | |
644 | error = git__throw(GIT_ERROR, "Unsupported git repository version (Expected version <= %d, found %d).", GIT_REPOSITORYFORMATVERSION, version); | |
645 | ||
646 | git_config_free(config); | |
647 | ||
648 | return error; | |
649 | } | |
650 | ||
651 | static int repo_init_reinit(git_repository **repo_out, const char *repository_path, int is_bare) | |
652 | { | |
653 | int error; | |
654 | git_repository *repo = NULL; | |
655 | ||
656 | if ((error = git_repository_open(&repo, repository_path)) < GIT_SUCCESS) | |
657 | goto error; | |
658 | ||
659 | if ((error = check_repositoryformatversion(repo)) < GIT_SUCCESS) | |
660 | goto error; | |
661 | ||
662 | /* TODO: reinitialize the templates */ | |
663 | ||
664 | *repo_out = repo; | |
665 | ||
666 | return GIT_SUCCESS; | |
667 | ||
668 | error: | |
669 | git_repository_free(repo); | |
670 | ||
671 | return git__rethrow(error, | |
672 | "Failed to reinitialize the %srepository at '%s'. ", | |
9462c471 | 673 | is_bare ? "bare " : "", repository_path); |
4b8e27c8 | 674 | } |
675 | ||
9462c471 | 676 | static int repo_init_createhead(const char *git_dir) |
e1f8cad0 | 677 | { |
97769280 RB |
678 | int error; |
679 | git_buf ref_path = GIT_BUF_INIT; | |
9462c471 VM |
680 | git_filebuf ref = GIT_FILEBUF_INIT; |
681 | ||
97769280 RB |
682 | if (!(error = git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE)) && |
683 | !(error = git_filebuf_open(&ref, ref_path.ptr, 0)) && | |
684 | !(error = git_filebuf_printf(&ref, "ref: refs/heads/master\n"))) | |
685 | error = git_filebuf_commit(&ref, GIT_REFS_FILE_MODE); | |
9462c471 | 686 | |
97769280 RB |
687 | git_buf_free(&ref_path); |
688 | return error; | |
9462c471 VM |
689 | } |
690 | ||
691 | static int repo_init_config(const char *git_dir, int is_bare) | |
692 | { | |
97769280 RB |
693 | git_buf cfg_path = GIT_BUF_INIT; |
694 | git_config *config = NULL; | |
9462c471 VM |
695 | int error = GIT_SUCCESS; |
696 | ||
697 | #define SET_REPO_CONFIG(type, name, val) {\ | |
698 | error = git_config_set_##type(config, name, val);\ | |
699 | if (error < GIT_SUCCESS)\ | |
700 | goto cleanup;\ | |
701 | } | |
702 | ||
97769280 RB |
703 | error = git_buf_joinpath(&cfg_path, git_dir, GIT_CONFIG_FILENAME_INREPO); |
704 | if (error < GIT_SUCCESS) | |
705 | goto cleanup; | |
9462c471 | 706 | |
97769280 | 707 | error = git_config_open_ondisk(&config, cfg_path.ptr); |
9462c471 | 708 | if (error < GIT_SUCCESS) |
97769280 | 709 | goto cleanup; |
9462c471 VM |
710 | |
711 | SET_REPO_CONFIG(bool, "core.bare", is_bare); | |
5663e61a | 712 | SET_REPO_CONFIG(int32, GIT_CONFIG_CORE_REPOSITORYFORMATVERSION, GIT_REPOSITORYFORMATVERSION); |
9462c471 VM |
713 | /* TODO: what other defaults? */ |
714 | ||
715 | cleanup: | |
97769280 | 716 | git_buf_free(&cfg_path); |
9462c471 | 717 | git_config_free(config); |
75abd2b9 | 718 | return error; |
d2d6912e | 719 | } |
e1f8cad0 | 720 | |
ed72182b | 721 | static int repo_init_structure(const char *git_dir, int is_bare) |
4b8e27c8 | 722 | { |
97769280 RB |
723 | int error, i; |
724 | struct { const char *dir; mode_t mode; } dirs[] = { | |
725 | { GIT_OBJECTS_INFO_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/info/' */ | |
726 | { GIT_OBJECTS_PACK_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/pack/' */ | |
727 | { GIT_REFS_HEADS_DIR, GIT_REFS_DIR_MODE }, /* '/refs/heads/' */ | |
728 | { GIT_REFS_TAGS_DIR, GIT_REFS_DIR_MODE }, /* '/refs/tags/' */ | |
729 | { NULL, 0 } | |
730 | }; | |
731 | ||
732 | /* Make the base directory */ | |
733 | error = git_futils_mkdir_r(git_dir, NULL, is_bare ? | |
734 | GIT_BARE_DIR_MODE : GIT_DIR_MODE); | |
735 | if (error < GIT_SUCCESS) | |
736 | return git__rethrow(error, "Failed to initialize repository structure. Could not mkdir"); | |
a67a096a | 737 | |
6ac91dfe | 738 | /* Hides the ".git" directory */ |
ed72182b | 739 | if (!is_bare) { |
17837602 | 740 | #ifdef GIT_WIN32 |
f79026b4 | 741 | error = p_hide_directory__w32(git_dir); |
6ac91dfe | 742 | if (error < GIT_SUCCESS) |
743 | return git__rethrow(error, "Failed to initialize repository structure"); | |
6ac91dfe | 744 | #endif |
17837602 | 745 | } |
6ac91dfe | 746 | |
97769280 RB |
747 | /* Make subdirectories as needed */ |
748 | for (i = 0; dirs[i].dir != NULL; ++i) { | |
749 | error = git_futils_mkdir_r(dirs[i].dir, git_dir, dirs[i].mode); | |
750 | if (error < GIT_SUCCESS) | |
751 | return git__rethrow(error, | |
752 | "Failed to create repository folder `%s`", dirs[i].dir); | |
753 | } | |
1c2c7c0d | 754 | |
40c44d2f | 755 | /* TODO: what's left? templates? */ |
4b8e27c8 | 756 | |
97769280 | 757 | return error; |
4b8e27c8 | 758 | } |
759 | ||
40c44d2f | 760 | int git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare) |
4b8e27c8 | 761 | { |
4b8e27c8 | 762 | int error = GIT_SUCCESS; |
e0011be3 | 763 | git_repository *repo = NULL; |
97769280 | 764 | git_buf repository_path = GIT_BUF_INIT; |
932d1baf | 765 | |
4b8e27c8 | 766 | assert(repo_out && path); |
767 | ||
97769280 RB |
768 | error = git_buf_joinpath(&repository_path, path, is_bare ? "" : GIT_DIR); |
769 | if (error < GIT_SUCCESS) | |
770 | return error; | |
4b8e27c8 | 771 | |
1744fafe | 772 | if (git_path_isdir(repository_path.ptr) == GIT_SUCCESS) { |
97769280 | 773 | if (quickcheck_repository_dir(&repository_path) == GIT_SUCCESS) { |
5663e61a | 774 | error = repo_init_reinit(repo_out, repository_path.ptr, is_bare); |
97769280 RB |
775 | git_buf_free(&repository_path); |
776 | return error; | |
777 | } | |
1bc83ff1 | 778 | } |
d2d6912e | 779 | |
97769280 RB |
780 | if (!(error = repo_init_structure(repository_path.ptr, is_bare)) && |
781 | !(error = repo_init_config(repository_path.ptr, is_bare)) && | |
782 | !(error = repo_init_createhead(repository_path.ptr))) | |
783 | error = git_repository_open(repo_out, repository_path.ptr); | |
784 | else | |
785 | git_repository_free(repo); | |
d2d6912e | 786 | |
97769280 | 787 | git_buf_free(&repository_path); |
e0011be3 | 788 | |
97769280 RB |
789 | if (error != GIT_SUCCESS) |
790 | git__rethrow(error, "Failed to (re)init the repository `%s`", path); | |
4b8e27c8 | 791 | |
97769280 | 792 | return error; |
40c44d2f | 793 | } |
35502d2e | 794 | |
c682886e | 795 | int git_repository_head_detached(git_repository *repo) |
35502d2e CMN |
796 | { |
797 | git_reference *ref; | |
798 | int error; | |
9462c471 | 799 | size_t _size; |
35502d2e | 800 | git_otype type; |
9462c471 VM |
801 | git_odb *odb = NULL; |
802 | ||
803 | error = git_repository_odb__weakptr(&odb, repo); | |
804 | if (error < GIT_SUCCESS) | |
805 | return error; | |
35502d2e CMN |
806 | |
807 | error = git_reference_lookup(&ref, repo, GIT_HEAD_FILE); | |
808 | if (error < GIT_SUCCESS) | |
809 | return error; | |
810 | ||
75abd2b9 MS |
811 | if (git_reference_type(ref) == GIT_REF_SYMBOLIC) { |
812 | git_reference_free(ref); | |
35502d2e | 813 | return 0; |
75abd2b9 | 814 | } |
35502d2e | 815 | |
9462c471 | 816 | error = git_odb_read_header(&_size, &type, odb, git_reference_oid(ref)); |
75abd2b9 MS |
817 | |
818 | git_reference_free(ref); | |
819 | ||
35502d2e CMN |
820 | if (error < GIT_SUCCESS) |
821 | return error; | |
822 | ||
823 | if (type != GIT_OBJ_COMMIT) | |
824 | return git__throw(GIT_EOBJCORRUPTED, "HEAD is not a commit"); | |
825 | ||
826 | return 1; | |
827 | } | |
828 | ||
3601c4bf | 829 | int git_repository_head(git_reference **head_out, git_repository *repo) |
35502d2e | 830 | { |
75abd2b9 | 831 | git_reference *ref, *resolved_ref; |
35502d2e CMN |
832 | int error; |
833 | ||
3601c4bf | 834 | *head_out = NULL; |
835 | ||
35502d2e CMN |
836 | error = git_reference_lookup(&ref, repo, GIT_HEAD_FILE); |
837 | if (error < GIT_SUCCESS) | |
3601c4bf | 838 | return git__rethrow(GIT_ENOTAREPO, "Failed to locate the HEAD"); |
35502d2e | 839 | |
75abd2b9 MS |
840 | error = git_reference_resolve(&resolved_ref, ref); |
841 | if (error < GIT_SUCCESS) { | |
842 | git_reference_free(ref); | |
3601c4bf | 843 | return git__rethrow(error, "Failed to resolve the HEAD"); |
75abd2b9 | 844 | } |
3601c4bf | 845 | |
75abd2b9 MS |
846 | git_reference_free(ref); |
847 | ||
848 | *head_out = resolved_ref; | |
3601c4bf | 849 | return GIT_SUCCESS; |
850 | } | |
851 | ||
852 | int git_repository_head_orphan(git_repository *repo) | |
853 | { | |
854 | git_reference *ref; | |
855 | int error; | |
856 | ||
857 | error = git_repository_head(&ref, repo); | |
35502d2e | 858 | |
75abd2b9 MS |
859 | if (error == GIT_SUCCESS) |
860 | git_reference_free(ref); | |
861 | ||
35502d2e CMN |
862 | return error == GIT_ENOTFOUND ? 1 : error; |
863 | } | |
e0011be3 | 864 | |
41233c40 VM |
865 | int git_repository_is_empty(git_repository *repo) |
866 | { | |
d4a0b124 | 867 | git_reference *head = NULL, *branch = NULL; |
41233c40 VM |
868 | int error; |
869 | ||
870 | error = git_reference_lookup(&head, repo, "HEAD"); | |
871 | if (error < GIT_SUCCESS) | |
0f489fb2 | 872 | return git__throw(error, "Corrupted repository. HEAD does not exist"); |
41233c40 | 873 | |
75abd2b9 MS |
874 | if (git_reference_type(head) != GIT_REF_SYMBOLIC) { |
875 | git_reference_free(head); | |
0f489fb2 | 876 | return 0; |
75abd2b9 | 877 | } |
0f489fb2 | 878 | |
75abd2b9 MS |
879 | if (strcmp(git_reference_target(head), "refs/heads/master") != 0) { |
880 | git_reference_free(head); | |
0f489fb2 | 881 | return 0; |
75abd2b9 | 882 | } |
41233c40 | 883 | |
0f489fb2 | 884 | error = git_reference_resolve(&branch, head); |
75abd2b9 MS |
885 | |
886 | git_reference_free(head); | |
887 | git_reference_free(branch); | |
888 | ||
0f489fb2 | 889 | return error == GIT_ENOTFOUND ? 1 : error; |
41233c40 VM |
890 | } |
891 | ||
9462c471 | 892 | const char *git_repository_path(git_repository *repo) |
4a34b3a9 | 893 | { |
894 | assert(repo); | |
9462c471 VM |
895 | return repo->path_repository; |
896 | } | |
4a34b3a9 | 897 | |
9462c471 VM |
898 | const char *git_repository_workdir(git_repository *repo) |
899 | { | |
900 | assert(repo); | |
602ee38b | 901 | |
9462c471 VM |
902 | if (repo->is_bare) |
903 | return NULL; | |
602ee38b | 904 | |
9462c471 VM |
905 | return repo->workdir; |
906 | } | |
602ee38b | 907 | |
9462c471 VM |
908 | int git_repository_set_workdir(git_repository *repo, const char *workdir) |
909 | { | |
910 | assert(repo && workdir); | |
602ee38b | 911 | |
9462c471 VM |
912 | free(repo->workdir); |
913 | ||
914 | repo->workdir = git__strdup(workdir); | |
915 | if (repo->workdir == NULL) | |
916 | return GIT_ENOMEM; | |
917 | ||
918 | repo->is_bare = 0; | |
919 | return GIT_SUCCESS; | |
4a34b3a9 | 920 | } |
fa9bcd81 | 921 | |
922 | int git_repository_is_bare(git_repository *repo) | |
923 | { | |
924 | assert(repo); | |
925 | return repo->is_bare; | |
926 | } |