]> git.proxmox.com Git - libgit2.git/blame - src/repository.c
tests: plug a leak in the repo tests
[libgit2.git] / src / repository.c
CommitLineData
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>
7784bcbb 8#include <ctype.h>
3315782c 9
44908fe7 10#include "git2/object.h"
d12299fe 11
3315782c
VM
12#include "common.h"
13#include "repository.h"
14#include "commit.h"
15#include "tag.h"
237da401 16#include "blob.h"
6fd195d7 17#include "fileops.h"
b22d1479 18#include "config.h"
9282e921 19#include "refs.h"
20
f2d6a23a 21#define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/"
22#define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
40c44d2f 23
7784bcbb 24#define GIT_FILE_CONTENT_PREFIX "gitdir:"
6a01b6bd 25
28990938 26#define GIT_BRANCH_MASTER "master"
27
29e948de 28#define GIT_REPO_VERSION 0
691aa968 29
9462c471
VM
30static 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
39static 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 }
f2c25d18
VM
46
47 git_repository__cvar_cache_clear(repo);
691aa968
VM
48}
49
9462c471 50static void drop_index(git_repository *repo)
6fd195d7 51{
9462c471
VM
52 if (repo->_index != NULL) {
53 GIT_REFCOUNT_OWN(repo->_index, NULL);
54 git_index_free(repo->_index);
55 repo->_index = NULL;
56 }
e0011be3
VM
57}
58
9462c471 59void git_repository_free(git_repository *repo)
e0011be3 60{
9462c471
VM
61 if (repo == NULL)
62 return;
6fd195d7 63
9462c471
VM
64 git_cache_free(&repo->objects);
65 git_repository__refcache_free(&repo->references);
73b51450 66 git_attr_cache_flush(repo);
bfc9ca59 67 git_submodule_config_free(repo);
6fd195d7 68
9462c471
VM
69 git__free(repo->path_repository);
70 git__free(repo->workdir);
6fd195d7 71
9462c471
VM
72 drop_config(repo);
73 drop_index(repo);
74 drop_odb(repo);
75
76 git__free(repo);
6fd195d7
VM
77}
78
9462c471
VM
79/*
80 * Git repository open methods
81 *
82 * Open a repository object from its path
83 */
cb8a7961 84static bool valid_repository_path(git_buf *repository_path)
5ad739e8 85{
97769280 86 /* Check OBJECTS_DIR first, since it will generate the longest path name */
1a481123 87 if (git_path_contains_dir(repository_path, GIT_OBJECTS_DIR) == false)
cb8a7961 88 return false;
5ad739e8 89
97769280 90 /* Ensure HEAD file exists */
1a481123 91 if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false)
cb8a7961 92 return false;
5ad739e8 93
1a481123 94 if (git_path_contains_dir(repository_path, GIT_REFS_DIR) == false)
cb8a7961 95 return false;
5ad739e8 96
cb8a7961 97 return true;
5ad739e8
VM
98}
99
51d00446 100static git_repository *repository_alloc(void)
3315782c
VM
101{
102 git_repository *repo = git__malloc(sizeof(git_repository));
103 if (!repo)
104 return NULL;
105
106 memset(repo, 0x0, sizeof(git_repository));
107
cb8a7961 108 if (git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free) < 0) {
3286c408 109 git__free(repo);
81201a4c 110 return NULL;
111 }
3315782c 112
f2c25d18
VM
113 /* set all the entries in the cvar cache to `unset` */
114 git_repository__cvar_cache_clear(repo);
115
6fd195d7
VM
116 return repo;
117}
118
9462c471 119static int load_config_data(git_repository *repo)
ec3c7a16 120{
cb8a7961 121 int is_bare;
9462c471 122 git_config *config;
ec3c7a16 123
cb8a7961
VM
124 if (git_repository_config__weakptr(&config, repo) < 0)
125 return -1;
ec3c7a16 126
d3e9c4a5 127 /* Try to figure out if it's bare, default to non-bare if it's not set */
29e948de 128 if (git_config_get_bool(&is_bare, config, "core.bare") < 0)
d3e9c4a5
CMN
129 repo->is_bare = 0;
130 else
131 repo->is_bare = is_bare;
c94785a9 132
cb8a7961 133 return 0;
9462c471 134}
ec3c7a16 135
7784bcbb 136static int load_workdir(git_repository *repo, git_buf *parent_path)
9462c471 137{
7784bcbb
RB
138 int error;
139 git_config *config;
140 const char *worktree;
141 git_buf worktree_buf = GIT_BUF_INIT;
ec3c7a16 142
97769280 143 if (repo->is_bare)
cb8a7961 144 return 0;
ec3c7a16 145
7784bcbb 146 if (git_repository_config__weakptr(&config, repo) < 0)
cb8a7961 147 return -1;
ec3c7a16 148
29e948de 149 error = git_config_get_string(&worktree, config, "core.worktree");
7784bcbb
RB
150 if (!error && worktree != NULL)
151 repo->workdir = git__strdup(worktree);
904b67e6 152 else if (error != GIT_ENOTFOUND)
7784bcbb
RB
153 return error;
154 else {
155 giterr_clear();
156
157 if (parent_path && git_path_isdir(parent_path->ptr))
158 repo->workdir = git_buf_detach(parent_path);
159 else {
160 git_path_dirname_r(&worktree_buf, repo->path_repository);
161 git_path_to_dir(&worktree_buf);
162 repo->workdir = git_buf_detach(&worktree_buf);
163 }
164 }
165
166 GITERR_CHECK_ALLOC(repo->workdir);
97769280 167
cb8a7961 168 return 0;
ec3c7a16
VM
169}
170
7784bcbb
RB
171/*
172 * This function returns furthest offset into path where a ceiling dir
173 * is found, so we can stop processing the path at that point.
174 *
175 * Note: converting this to use git_bufs instead of GIT_PATH_MAX buffers on
176 * the stack could remove directories name limits, but at the cost of doing
177 * repeated malloc/frees inside the loop below, so let's not do it now.
178 */
179static int find_ceiling_dir_offset(
180 const char *path,
181 const char *ceiling_directories)
182{
183 char buf[GIT_PATH_MAX + 1];
184 char buf2[GIT_PATH_MAX + 1];
185 const char *ceil, *sep;
44ef8b1b 186 size_t len, max_len = 0, min_len;
7784bcbb
RB
187
188 assert(path);
189
44ef8b1b 190 min_len = (size_t)(git_path_root(path) + 1);
7784bcbb
RB
191
192 if (ceiling_directories == NULL || min_len == 0)
44ef8b1b 193 return (int)min_len;
7784bcbb
RB
194
195 for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) {
196 for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++);
197 len = sep - ceil;
198
44ef8b1b 199 if (len == 0 || len >= sizeof(buf) || git_path_root(ceil) == -1)
7784bcbb
RB
200 continue;
201
202 strncpy(buf, ceil, len);
203 buf[len] = '\0';
204
205 if (p_realpath(buf, buf2) == NULL)
206 continue;
207
208 len = strlen(buf2);
209 if (len > 0 && buf2[len-1] == '/')
210 buf[--len] = '\0';
211
212 if (!strncmp(path, buf2, len) &&
213 path[len] == '/' &&
214 len > max_len)
215 {
216 max_len = len;
217 }
218 }
219
44ef8b1b 220 return (int)(max_len <= min_len ? min_len : max_len);
7784bcbb
RB
221}
222
223/*
224 * Read the contents of `file_path` and set `path_out` to the repo dir that
225 * it points to. Before calling, set `path_out` to the base directory that
226 * should be used if the contents of `file_path` are a relative path.
227 */
228static int read_gitfile(git_buf *path_out, const char *file_path)
229{
230 int error = 0;
231 git_buf file = GIT_BUF_INIT;
232 size_t prefix_len = strlen(GIT_FILE_CONTENT_PREFIX);
233
234 assert(path_out && file_path);
235
236 if (git_futils_readbuffer(&file, file_path) < 0)
237 return -1;
238
239 git_buf_rtrim(&file);
240
241 if (file.size <= prefix_len ||
242 memcmp(file.ptr, GIT_FILE_CONTENT_PREFIX, prefix_len) != 0)
243 {
244 giterr_set(GITERR_REPOSITORY, "The `.git` file at '%s' is malformed", file_path);
245 error = -1;
246 }
247 else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) {
248 const char *gitlink = ((const char *)file.ptr) + prefix_len;
0f49200c 249 while (*gitlink && git__isspace(*gitlink)) gitlink++;
7784bcbb
RB
250 error = git_path_prettify_dir(path_out, gitlink, path_out->ptr);
251 }
252
253 git_buf_free(&file);
254 return error;
255}
256
257static int find_repo(
258 git_buf *repo_path,
259 git_buf *parent_path,
260 const char *start_path,
261 uint32_t flags,
262 const char *ceiling_dirs)
691aa968 263{
7784bcbb
RB
264 int error;
265 git_buf path = GIT_BUF_INIT;
266 struct stat st;
267 dev_t initial_device = 0;
268 bool try_with_dot_git = false;
269 int ceiling_offset;
270
271 git_buf_free(repo_path);
272
273 if ((error = git_path_prettify_dir(&path, start_path, NULL)) < 0)
274 return error;
275
276 ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
277
278 if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
279 return error;
280
fa6420f7 281 while (!error && !git_buf_len(repo_path)) {
7784bcbb
RB
282 if (p_stat(path.ptr, &st) == 0) {
283 /* check that we have not crossed device boundaries */
284 if (initial_device == 0)
285 initial_device = st.st_dev;
286 else if (st.st_dev != initial_device &&
287 (flags & GIT_REPOSITORY_OPEN_CROSS_FS) == 0)
288 break;
289
290 if (S_ISDIR(st.st_mode)) {
291 if (valid_repository_path(&path)) {
292 git_path_to_dir(&path);
293 git_buf_set(repo_path, path.ptr, path.size);
294 break;
295 }
296 }
297 else if (S_ISREG(st.st_mode)) {
298 git_buf repo_link = GIT_BUF_INIT;
299
300 if (!(error = read_gitfile(&repo_link, path.ptr))) {
301 if (valid_repository_path(&repo_link))
302 git_buf_swap(repo_path, &repo_link);
146f5c75
CMN
303
304 git_buf_free(&repo_link);
7784bcbb
RB
305 break;
306 }
307 git_buf_free(&repo_link);
308 }
309 }
310
311 /* move up one directory level */
312 if (git_path_dirname_r(&path, path.ptr) < 0) {
313 error = -1;
314 break;
315 }
316
317 if (try_with_dot_git) {
318 /* if we tried original dir with and without .git AND either hit
319 * directory ceiling or NO_SEARCH was requested, then be done.
320 */
321 if (path.ptr[ceiling_offset] == '\0' ||
322 (flags & GIT_REPOSITORY_OPEN_NO_SEARCH) != 0)
323 break;
324 /* otherwise look first for .git item */
325 error = git_buf_joinpath(&path, path.ptr, DOT_GIT);
326 }
327 try_with_dot_git = !try_with_dot_git;
328 }
329
330 if (!error && parent_path != NULL) {
fa6420f7 331 if (!git_buf_len(repo_path))
7784bcbb
RB
332 git_buf_clear(parent_path);
333 else {
334 git_path_dirname_r(parent_path, path.ptr);
335 git_path_to_dir(parent_path);
336 }
337 if (git_buf_oom(parent_path))
338 return -1;
339 }
340
341 git_buf_free(&path);
342
fa6420f7 343 if (!git_buf_len(repo_path) && !error) {
cb8a7961 344 giterr_set(GITERR_REPOSITORY,
7784bcbb 345 "Could not find repository from '%s'", start_path);
904b67e6 346 error = GIT_ENOTFOUND;
97769280 347 }
691aa968 348
7784bcbb
RB
349 return error;
350}
351
352int git_repository_open_ext(
353 git_repository **repo_ptr,
354 const char *start_path,
355 uint32_t flags,
356 const char *ceiling_dirs)
357{
358 int error;
359 git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT;
360 git_repository *repo;
361
362 *repo_ptr = NULL;
363
364 if ((error = find_repo(&path, &parent, start_path, flags, ceiling_dirs)) < 0)
365 return error;
366
e52ed7a5 367 repo = repository_alloc();
cb8a7961 368 GITERR_CHECK_ALLOC(repo);
691aa968 369
7784bcbb 370 repo->path_repository = git_buf_detach(&path);
cb8a7961 371 GITERR_CHECK_ALLOC(repo->path_repository);
691aa968 372
7784bcbb
RB
373 if ((error = load_config_data(repo)) < 0 ||
374 (error = load_workdir(repo, &parent)) < 0)
375 {
376 git_repository_free(repo);
377 return error;
378 }
691aa968 379
146f5c75 380 git_buf_free(&parent);
7784bcbb 381 *repo_ptr = repo;
cb8a7961 382 return 0;
7784bcbb 383}
97769280 384
7784bcbb
RB
385int git_repository_open(git_repository **repo_out, const char *path)
386{
387 return git_repository_open_ext(
388 repo_out, path, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL);
389}
390
391int git_repository_discover(
392 char *repository_path,
393 size_t size,
394 const char *start_path,
395 int across_fs,
396 const char *ceiling_dirs)
397{
398 git_buf path = GIT_BUF_INIT;
399 uint32_t flags = across_fs ? GIT_REPOSITORY_OPEN_CROSS_FS : 0;
464cf248 400 int error;
7784bcbb
RB
401
402 assert(start_path && repository_path && size > 0);
403
404 *repository_path = '\0';
405
464cf248 406 if ((error = find_repo(&path, NULL, start_path, flags, ceiling_dirs)) < 0)
904b67e6 407 return error != GIT_ENOTFOUND ? -1 : error;
7784bcbb
RB
408
409 if (size < (size_t)(path.size + 1)) {
410 giterr_set(GITERR_REPOSITORY,
411 "The given buffer is too long to store the discovered path");
412 git_buf_free(&path);
413 return -1;
414 }
415
416 /* success: we discovered a repository */
417 git_buf_copy_cstr(repository_path, size, &path);
418 git_buf_free(&path);
419 return 0;
691aa968
VM
420}
421
9462c471 422static int load_config(
7784bcbb
RB
423 git_config **out,
424 git_repository *repo,
425 const char *global_config_path,
426 const char *system_config_path)
b22d1479 427{
97769280 428 git_buf config_path = GIT_BUF_INIT;
9462c471 429 git_config *cfg = NULL;
b22d1479 430
9462c471 431 assert(repo && out);
07ff8817 432
cb8a7961
VM
433 if (git_config_new(&cfg) < 0)
434 return -1;
b22d1479 435
cb8a7961
VM
436 if (git_buf_joinpath(
437 &config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO) < 0)
438 goto on_error;
97769280 439
cb8a7961
VM
440 if (git_config_add_file_ondisk(cfg, config_path.ptr, 3) < 0)
441 goto on_error;
442
443 git_buf_free(&config_path);
b22d1479 444
40fe5fbe 445 if (global_config_path != NULL) {
cb8a7961
VM
446 if (git_config_add_file_ondisk(cfg, global_config_path, 2) < 0)
447 goto on_error;
b22d1479
CMN
448 }
449
07ff8817 450 if (system_config_path != NULL) {
cb8a7961
VM
451 if (git_config_add_file_ondisk(cfg, system_config_path, 1) < 0)
452 goto on_error;
9ba9e513
CMN
453 }
454
9462c471 455 *out = cfg;
cb8a7961 456 return 0;
b22d1479 457
cb8a7961
VM
458on_error:
459 git_buf_free(&config_path);
9462c471
VM
460 git_config_free(cfg);
461 *out = NULL;
cb8a7961 462 return -1;
b22d1479
CMN
463}
464
9462c471 465int git_repository_config__weakptr(git_config **out, git_repository *repo)
40fe5fbe 466{
9462c471 467 if (repo->_config == NULL) {
97769280 468 git_buf global_buf = GIT_BUF_INIT, system_buf = GIT_BUF_INIT;
cb8a7961 469 int res;
9462c471
VM
470
471 const char *global_config_path = NULL;
472 const char *system_config_path = NULL;
40fe5fbe 473
cb8a7961 474 if (git_config_find_global_r(&global_buf) == 0)
97769280 475 global_config_path = global_buf.ptr;
40fe5fbe 476
cb8a7961 477 if (git_config_find_system_r(&system_buf) == 0)
97769280 478 system_config_path = system_buf.ptr;
40fe5fbe 479
cb8a7961 480 res = load_config(&repo->_config, repo, global_config_path, system_config_path);
97769280
RB
481
482 git_buf_free(&global_buf);
483 git_buf_free(&system_buf);
484
cb8a7961
VM
485 if (res < 0)
486 return -1;
9462c471
VM
487
488 GIT_REFCOUNT_OWN(repo->_config, repo);
489 }
40fe5fbe 490
9462c471 491 *out = repo->_config;
cb8a7961 492 return 0;
40fe5fbe
CMN
493}
494
9462c471 495int git_repository_config(git_config **out, git_repository *repo)
fd0574e5 496{
cb8a7961
VM
497 if (git_repository_config__weakptr(out, repo) < 0)
498 return -1;
fd0574e5 499
cb8a7961
VM
500 GIT_REFCOUNT_INC(*out);
501 return 0;
9462c471
VM
502}
503
504void git_repository_set_config(git_repository *repo, git_config *config)
505{
506 assert(repo && config);
507
508 drop_config(repo);
509
510 repo->_config = config;
511 GIT_REFCOUNT_OWN(repo->_config, repo);
512}
513
514int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
515{
516 assert(repo && out);
517
518 if (repo->_odb == NULL) {
97769280 519 git_buf odb_path = GIT_BUF_INIT;
cb8a7961 520 int res;
9462c471 521
cb8a7961
VM
522 if (git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR) < 0)
523 return -1;
9462c471 524
cb8a7961 525 res = git_odb_open(&repo->_odb, odb_path.ptr);
97769280 526 git_buf_free(&odb_path); /* done with path */
cb8a7961
VM
527
528 if (res < 0)
529 return -1;
9462c471
VM
530
531 GIT_REFCOUNT_OWN(repo->_odb, repo);
532 }
fd0574e5 533
9462c471 534 *out = repo->_odb;
cb8a7961 535 return 0;
fd0574e5
RG
536}
537
9462c471 538int git_repository_odb(git_odb **out, git_repository *repo)
6fd195d7 539{
cb8a7961
VM
540 if (git_repository_odb__weakptr(out, repo) < 0)
541 return -1;
1795f879 542
cb8a7961
VM
543 GIT_REFCOUNT_INC(*out);
544 return 0;
9462c471 545}
6fd195d7 546
9462c471
VM
547void git_repository_set_odb(git_repository *repo, git_odb *odb)
548{
549 assert(repo && odb);
3315782c 550
9462c471 551 drop_odb(repo);
1795f879 552
9462c471
VM
553 repo->_odb = odb;
554 GIT_REFCOUNT_OWN(repo->_odb, repo);
baf861a5 555 GIT_REFCOUNT_INC(odb);
9462c471
VM
556}
557
558int git_repository_index__weakptr(git_index **out, git_repository *repo)
559{
560 assert(out && repo);
561
9462c471 562 if (repo->_index == NULL) {
cb8a7961 563 int res;
97769280 564 git_buf index_path = GIT_BUF_INIT;
9462c471 565
cb8a7961
VM
566 if (git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE) < 0)
567 return -1;
9462c471 568
cb8a7961 569 res = git_index_open(&repo->_index, index_path.ptr);
97769280 570 git_buf_free(&index_path); /* done with path */
cb8a7961
VM
571
572 if (res < 0)
573 return -1;
9462c471
VM
574
575 GIT_REFCOUNT_OWN(repo->_index, repo);
da825c92
RB
576
577 if (git_index_set_caps(repo->_index, GIT_INDEXCAP_FROM_OWNER) < 0)
578 return -1;
9462c471
VM
579 }
580
9462c471 581 *out = repo->_index;
cb8a7961 582 return 0;
9462c471 583}
1795f879 584
9462c471
VM
585int git_repository_index(git_index **out, git_repository *repo)
586{
cb8a7961
VM
587 if (git_repository_index__weakptr(out, repo) < 0)
588 return -1;
9462c471 589
cb8a7961
VM
590 GIT_REFCOUNT_INC(*out);
591 return 0;
3315782c
VM
592}
593
9462c471
VM
594void git_repository_set_index(git_repository *repo, git_index *index)
595{
596 assert(repo && index);
597
598 drop_index(repo);
599
600 repo->_index = index;
601 GIT_REFCOUNT_OWN(repo->_index, repo);
c1aefb35 602 GIT_REFCOUNT_INC(index);
9462c471
VM
603}
604
5663e61a 605static int check_repositoryformatversion(git_repository *repo)
40c44d2f 606{
5663e61a 607 git_config *config;
cb8a7961 608 int version;
5663e61a 609
cb8a7961
VM
610 if (git_repository_config__weakptr(&config, repo) < 0)
611 return -1;
5663e61a 612
29e948de 613 if (git_config_get_int32(&version, config, "core.repositoryformatversion") < 0)
cb8a7961 614 return -1;
5663e61a 615
29e948de 616 if (GIT_REPO_VERSION < version) {
cb8a7961
VM
617 giterr_set(GITERR_REPOSITORY,
618 "Unsupported repository version %d. Only versions up to %d are supported.",
29e948de 619 version, GIT_REPO_VERSION);
cb8a7961
VM
620 return -1;
621 }
5663e61a 622
cb8a7961 623 return 0;
5663e61a 624}
625
626static int repo_init_reinit(git_repository **repo_out, const char *repository_path, int is_bare)
627{
5663e61a 628 git_repository *repo = NULL;
629
cb8a7961 630 GIT_UNUSED(is_bare);
5663e61a 631
cb8a7961
VM
632 if (git_repository_open(&repo, repository_path) < 0)
633 return -1;
634
635 if (check_repositoryformatversion(repo) < 0) {
636 git_repository_free(repo);
637 return -1;
638 }
5663e61a 639
640 /* TODO: reinitialize the templates */
641
642 *repo_out = repo;
cb8a7961 643 return 0;
4b8e27c8 644}
645
9462c471 646static int repo_init_createhead(const char *git_dir)
e1f8cad0 647{
97769280 648 git_buf ref_path = GIT_BUF_INIT;
9462c471
VM
649 git_filebuf ref = GIT_FILEBUF_INIT;
650
cb8a7961
VM
651 if (git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE) < 0 ||
652 git_filebuf_open(&ref, ref_path.ptr, 0) < 0 ||
653 git_filebuf_printf(&ref, "ref: refs/heads/master\n") < 0 ||
654 git_filebuf_commit(&ref, GIT_REFS_FILE_MODE) < 0)
655 return -1;
9462c471 656
97769280 657 git_buf_free(&ref_path);
cb8a7961 658 return 0;
9462c471
VM
659}
660
fac66990 661static bool is_chmod_supported(const char *file_path)
662{
663 struct stat st1, st2;
664 static int _is_supported = -1;
665
666 if (_is_supported > -1)
667 return _is_supported;
668
669 if (p_stat(file_path, &st1) < 0)
670 return false;
671
672 if (p_chmod(file_path, st1.st_mode ^ S_IXUSR) < 0)
673 return false;
674
675 if (p_stat(file_path, &st2) < 0)
676 return false;
677
678 _is_supported = (st1.st_mode != st2.st_mode);
679 return _is_supported;
680}
681
693b23c0 682static bool is_filesystem_case_insensitive(const char *gitdir_path)
683{
684 git_buf path = GIT_BUF_INIT;
685 static int _is_insensitive = -1;
686
687 if (_is_insensitive > -1)
688 return _is_insensitive;
689
690 if (git_buf_joinpath(&path, gitdir_path, "CoNfIg") < 0)
691 goto cleanup;
692
693 _is_insensitive = git_path_exists(git_buf_cstr(&path));
694
695cleanup:
696 git_buf_free(&path);
697 return _is_insensitive;
698}
699
700static int repo_init_config(const char *git_dir, bool is_bare, bool is_reinit)
9462c471 701{
97769280
RB
702 git_buf cfg_path = GIT_BUF_INIT;
703 git_config *config = NULL;
9462c471
VM
704
705#define SET_REPO_CONFIG(type, name, val) {\
cb8a7961
VM
706 if (git_config_set_##type(config, name, val) < 0) { \
707 git_buf_free(&cfg_path); \
708 git_config_free(config); \
709 return -1; } \
9462c471
VM
710}
711
cb8a7961
VM
712 if (git_buf_joinpath(&cfg_path, git_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
713 return -1;
9462c471 714
fac66990 715 if (git_config_open_ondisk(&config, git_buf_cstr(&cfg_path)) < 0) {
cb8a7961
VM
716 git_buf_free(&cfg_path);
717 return -1;
718 }
9462c471
VM
719
720 SET_REPO_CONFIG(bool, "core.bare", is_bare);
29e948de 721 SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);
fac66990 722 SET_REPO_CONFIG(bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path)));
693b23c0 723
7623b1b6 724 if (!is_bare)
725 SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
726
693b23c0 727 if (!is_reinit && is_filesystem_case_insensitive(git_dir))
728 SET_REPO_CONFIG(bool, "core.ignorecase", true);
9462c471
VM
729 /* TODO: what other defaults? */
730
97769280 731 git_buf_free(&cfg_path);
9462c471 732 git_config_free(config);
cb8a7961 733 return 0;
d2d6912e 734}
e1f8cad0 735
dc34da6e
RB
736#define GIT_HOOKS_DIR "hooks/"
737#define GIT_HOOKS_DIR_MODE 0755
738
739#define GIT_HOOKS_README_FILE GIT_HOOKS_DIR "README.sample"
740#define GIT_HOOKS_README_MODE 0755
741#define GIT_HOOKS_README_CONTENT \
742"#!/bin/sh\n"\
743"#\n"\
744"# Place appropriately named executable hook scripts into this directory\n"\
745"# to intercept various actions that git takes. See `git help hooks` for\n"\
746"# more information.\n"
747
748#define GIT_INFO_DIR "info/"
749#define GIT_INFO_DIR_MODE 0755
750
751#define GIT_INFO_EXCLUDE_FILE GIT_INFO_DIR "exclude"
752#define GIT_INFO_EXCLUDE_MODE 0644
753#define GIT_INFO_EXCLUDE_CONTENT \
754"# File patterns to ignore; see `git help ignore` for more information.\n"\
755"# Lines that start with '#' are comments.\n"
756
757#define GIT_DESC_FILE "description"
758#define GIT_DESC_MODE 0644
759#define GIT_DESC_CONTENT "Unnamed repository; edit this file 'description' to name the repository.\n"
760
761static int repo_write_template(
762 const char *git_dir, const char *file, mode_t mode, const char *content)
763{
764 git_buf path = GIT_BUF_INIT;
db628072 765 int fd, error = 0;
dc34da6e
RB
766
767 if (git_buf_joinpath(&path, git_dir, file) < 0)
768 return -1;
769
770 fd = p_open(git_buf_cstr(&path), O_WRONLY | O_CREAT | O_EXCL, mode);
dc34da6e 771
db628072
RB
772 if (fd >= 0) {
773 error = p_write(fd, content, strlen(content));
dc34da6e 774
db628072
RB
775 p_close(fd);
776 }
777 else if (errno != EEXIST)
778 error = fd;
dc34da6e 779
dc34da6e 780 git_buf_free(&path);
db628072
RB
781
782 if (error)
783 giterr_set(GITERR_OS,
784 "Failed to initialize repository with template '%s'", file);
785
786 return error;
dc34da6e
RB
787}
788
ed72182b 789static int repo_init_structure(const char *git_dir, int is_bare)
4b8e27c8 790{
cb8a7961 791 int i;
97769280
RB
792 struct { const char *dir; mode_t mode; } dirs[] = {
793 { GIT_OBJECTS_INFO_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/info/' */
794 { GIT_OBJECTS_PACK_DIR, GIT_OBJECT_DIR_MODE }, /* '/objects/pack/' */
795 { GIT_REFS_HEADS_DIR, GIT_REFS_DIR_MODE }, /* '/refs/heads/' */
796 { GIT_REFS_TAGS_DIR, GIT_REFS_DIR_MODE }, /* '/refs/tags/' */
dc34da6e
RB
797 { GIT_HOOKS_DIR, GIT_HOOKS_DIR_MODE }, /* '/hooks/' */
798 { GIT_INFO_DIR, GIT_INFO_DIR_MODE }, /* '/info/' */
97769280
RB
799 { NULL, 0 }
800 };
dc34da6e
RB
801 struct { const char *file; mode_t mode; const char *content; } tmpl[] = {
802 { GIT_DESC_FILE, GIT_DESC_MODE, GIT_DESC_CONTENT },
803 { GIT_HOOKS_README_FILE, GIT_HOOKS_README_MODE, GIT_HOOKS_README_CONTENT },
804 { GIT_INFO_EXCLUDE_FILE, GIT_INFO_EXCLUDE_MODE, GIT_INFO_EXCLUDE_CONTENT },
805 { NULL, 0, NULL }
806 };
97769280
RB
807
808 /* Make the base directory */
cb8a7961
VM
809 if (git_futils_mkdir_r(git_dir, NULL, is_bare ? GIT_BARE_DIR_MODE : GIT_DIR_MODE) < 0)
810 return -1;
a67a096a 811
6ac91dfe 812 /* Hides the ".git" directory */
ed72182b 813 if (!is_bare) {
17837602 814#ifdef GIT_WIN32
cb8a7961
VM
815 if (p_hide_directory__w32(git_dir) < 0) {
816 giterr_set(GITERR_REPOSITORY,
817 "Failed to mark Git repository folder as hidden");
818 return -1;
819 }
6ac91dfe 820#endif
17837602 821 }
6ac91dfe 822
97769280
RB
823 /* Make subdirectories as needed */
824 for (i = 0; dirs[i].dir != NULL; ++i) {
cb8a7961
VM
825 if (git_futils_mkdir_r(dirs[i].dir, git_dir, dirs[i].mode) < 0)
826 return -1;
97769280 827 }
1c2c7c0d 828
dc34da6e
RB
829 /* Make template files as needed */
830 for (i = 0; tmpl[i].file != NULL; ++i) {
831 if (repo_write_template(
832 git_dir, tmpl[i].file, tmpl[i].mode, tmpl[i].content) < 0)
833 return -1;
834 }
835
cb8a7961 836 return 0;
4b8e27c8 837}
838
40c44d2f 839int git_repository_init(git_repository **repo_out, const char *path, unsigned is_bare)
4b8e27c8 840{
97769280 841 git_buf repository_path = GIT_BUF_INIT;
693b23c0 842 bool is_reinit;
843 int result = -1;
932d1baf 844
4b8e27c8 845 assert(repo_out && path);
846
cb8a7961 847 if (git_buf_joinpath(&repository_path, path, is_bare ? "" : GIT_DIR) < 0)
693b23c0 848 goto cleanup;
4b8e27c8 849
693b23c0 850 is_reinit = git_path_isdir(repository_path.ptr) && valid_repository_path(&repository_path);
851
852 if (is_reinit) {
853 if (repo_init_reinit(repo_out, repository_path.ptr, is_bare) < 0)
854 goto cleanup;
855
856 result = repo_init_config(repository_path.ptr, is_bare, is_reinit);
b3aa4406 857 goto cleanup;
1bc83ff1 858 }
d2d6912e 859
cb8a7961 860 if (repo_init_structure(repository_path.ptr, is_bare) < 0 ||
693b23c0 861 repo_init_config(repository_path.ptr, is_bare, is_reinit) < 0 ||
cb8a7961
VM
862 repo_init_createhead(repository_path.ptr) < 0 ||
863 git_repository_open(repo_out, repository_path.ptr) < 0) {
693b23c0 864 goto cleanup;
cb8a7961 865 }
d2d6912e 866
693b23c0 867 result = 0;
868
869cleanup:
97769280 870 git_buf_free(&repository_path);
693b23c0 871 return result;
40c44d2f 872}
35502d2e 873
c682886e 874int git_repository_head_detached(git_repository *repo)
35502d2e
CMN
875{
876 git_reference *ref;
9462c471 877 git_odb *odb = NULL;
cb8a7961 878 int exists;
9462c471 879
cb8a7961
VM
880 if (git_repository_odb__weakptr(&odb, repo) < 0)
881 return -1;
35502d2e 882
cb8a7961
VM
883 if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
884 return -1;
35502d2e 885
75abd2b9
MS
886 if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
887 git_reference_free(ref);
35502d2e 888 return 0;
75abd2b9 889 }
35502d2e 890
cb8a7961 891 exists = git_odb_exists(odb, git_reference_oid(ref));
75abd2b9
MS
892
893 git_reference_free(ref);
cb8a7961 894 return exists;
35502d2e
CMN
895}
896
3601c4bf 897int git_repository_head(git_reference **head_out, git_repository *repo)
35502d2e 898{
f201d613 899 return git_reference_lookup_resolved(head_out, repo, GIT_HEAD_FILE, -1);
3601c4bf 900}
901
902int git_repository_head_orphan(git_repository *repo)
903{
cb8a7961 904 git_reference *ref = NULL;
3601c4bf 905 int error;
906
907 error = git_repository_head(&ref, repo);
cb8a7961 908 git_reference_free(ref);
35502d2e 909
904b67e6 910 if (error == GIT_ENOTFOUND)
cb8a7961 911 return 1;
75abd2b9 912
cb8a7961
VM
913 if (error < 0)
914 return -1;
915
916 return 0;
35502d2e 917}
e0011be3 918
41233c40
VM
919int git_repository_is_empty(git_repository *repo)
920{
d4a0b124 921 git_reference *head = NULL, *branch = NULL;
41233c40
VM
922 int error;
923
cb8a7961
VM
924 if (git_reference_lookup(&head, repo, "HEAD") < 0)
925 return -1;
41233c40 926
75abd2b9
MS
927 if (git_reference_type(head) != GIT_REF_SYMBOLIC) {
928 git_reference_free(head);
0f489fb2 929 return 0;
75abd2b9 930 }
0f489fb2 931
75abd2b9
MS
932 if (strcmp(git_reference_target(head), "refs/heads/master") != 0) {
933 git_reference_free(head);
0f489fb2 934 return 0;
75abd2b9 935 }
41233c40 936
0f489fb2 937 error = git_reference_resolve(&branch, head);
75abd2b9
MS
938
939 git_reference_free(head);
940 git_reference_free(branch);
941
904b67e6 942 if (error == GIT_ENOTFOUND)
cb8a7961
VM
943 return 1;
944
945 if (error < 0)
946 return -1;
947
948 return 0;
41233c40
VM
949}
950
9462c471 951const char *git_repository_path(git_repository *repo)
4a34b3a9 952{
953 assert(repo);
9462c471
VM
954 return repo->path_repository;
955}
4a34b3a9 956
9462c471
VM
957const char *git_repository_workdir(git_repository *repo)
958{
959 assert(repo);
602ee38b 960
9462c471
VM
961 if (repo->is_bare)
962 return NULL;
602ee38b 963
9462c471
VM
964 return repo->workdir;
965}
602ee38b 966
9462c471
VM
967int git_repository_set_workdir(git_repository *repo, const char *workdir)
968{
b78fb64d 969 git_buf path = GIT_BUF_INIT;
970
9462c471 971 assert(repo && workdir);
602ee38b 972
b78fb64d 973 if (git_path_prettify_dir(&path, workdir, NULL) < 0)
974 return -1;
9462c471 975
2bc8fa02 976 git__free(repo->workdir);
9462c471 977
b78fb64d 978 repo->workdir = git_buf_detach(&path);
9462c471 979 repo->is_bare = 0;
0d0fa7c3 980 return 0;
4a34b3a9 981}
fa9bcd81 982
983int git_repository_is_bare(git_repository *repo)
984{
985 assert(repo);
986 return repo->is_bare;
987}
f917481e
RB
988
989int git_repository_head_tree(git_tree **tree, git_repository *repo)
990{
991 git_oid head_oid;
992 git_object *obj = NULL;
993
994 if (git_reference_name_to_oid(&head_oid, repo, GIT_HEAD_FILE) < 0) {
995 /* cannot resolve HEAD - probably brand new repo */
996 giterr_clear();
997 *tree = NULL;
998 return 0;
999 }
1000
1001 if (git_object_lookup(&obj, repo, &head_oid, GIT_OBJ_ANY) < 0 ||
1002 git_object__resolve_to_type(&obj, GIT_OBJ_TREE) < 0)
1003 return -1;
1004
1005 *tree = (git_tree *)obj;
1006 return 0;
1007}