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