]>
Commit | Line | Data |
---|---|---|
3315782c | 1 | /* |
359fc2d2 | 2 | * Copyright (C) the libgit2 contributors. All rights reserved. |
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" |
d00d5464 | 11 | #include "git2/refdb.h" |
1384b688 | 12 | #include "git2/sys/repository.h" |
d12299fe | 13 | |
3315782c VM |
14 | #include "common.h" |
15 | #include "repository.h" | |
16 | #include "commit.h" | |
17 | #include "tag.h" | |
237da401 | 18 | #include "blob.h" |
6fd195d7 | 19 | #include "fileops.h" |
114f5a6c RB |
20 | #include "filebuf.h" |
21 | #include "index.h" | |
b22d1479 | 22 | #include "config.h" |
9282e921 | 23 | #include "refs.h" |
47bfa0be RB |
24 | #include "filter.h" |
25 | #include "odb.h" | |
096d9e94 | 26 | #include "remote.h" |
632d8b23 | 27 | #include "merge.h" |
114f5a6c | 28 | #include "diff_driver.h" |
9282e921 | 29 | |
7784bcbb | 30 | #define GIT_FILE_CONTENT_PREFIX "gitdir:" |
6a01b6bd | 31 | |
28990938 | 32 | #define GIT_BRANCH_MASTER "master" |
33 | ||
29e948de | 34 | #define GIT_REPO_VERSION 0 |
691aa968 | 35 | |
e976b56d | 36 | static void set_odb(git_repository *repo, git_odb *odb) |
9462c471 | 37 | { |
e976b56d RB |
38 | if (odb) { |
39 | GIT_REFCOUNT_OWN(odb, repo); | |
40 | GIT_REFCOUNT_INC(odb); | |
41 | } | |
42 | ||
43 | if ((odb = git__swap(repo->_odb, odb)) != NULL) { | |
44 | GIT_REFCOUNT_OWN(odb, NULL); | |
45 | git_odb_free(odb); | |
eb2f3b47 | 46 | } |
9462c471 | 47 | } |
691aa968 | 48 | |
e976b56d | 49 | static void set_refdb(git_repository *repo, git_refdb *refdb) |
d00d5464 | 50 | { |
e976b56d RB |
51 | if (refdb) { |
52 | GIT_REFCOUNT_OWN(refdb, repo); | |
53 | GIT_REFCOUNT_INC(refdb); | |
54 | } | |
55 | ||
56 | if ((refdb = git__swap(repo->_refdb, refdb)) != NULL) { | |
57 | GIT_REFCOUNT_OWN(refdb, NULL); | |
58 | git_refdb_free(refdb); | |
d00d5464 ET |
59 | } |
60 | } | |
61 | ||
e976b56d | 62 | static void set_config(git_repository *repo, git_config *config) |
9462c471 | 63 | { |
e976b56d RB |
64 | if (config) { |
65 | GIT_REFCOUNT_OWN(config, repo); | |
66 | GIT_REFCOUNT_INC(config); | |
67 | } | |
68 | ||
69 | if ((config = git__swap(repo->_config, config)) != NULL) { | |
70 | GIT_REFCOUNT_OWN(config, NULL); | |
71 | git_config_free(config); | |
eb2f3b47 | 72 | } |
e976b56d RB |
73 | |
74 | git_repository__cvar_cache_clear(repo); | |
691aa968 VM |
75 | } |
76 | ||
e976b56d | 77 | static void set_index(git_repository *repo, git_index *index) |
6fd195d7 | 78 | { |
e976b56d RB |
79 | if (index) { |
80 | GIT_REFCOUNT_OWN(index, repo); | |
81 | GIT_REFCOUNT_INC(index); | |
82 | } | |
83 | ||
84 | if ((index = git__swap(repo->_index, index)) != NULL) { | |
85 | GIT_REFCOUNT_OWN(index, NULL); | |
86 | git_index_free(index); | |
9462c471 | 87 | } |
e0011be3 VM |
88 | } |
89 | ||
879458e7 | 90 | void git_repository__cleanup(git_repository *repo) |
e0011be3 | 91 | { |
879458e7 | 92 | assert(repo); |
6fd195d7 | 93 | |
879458e7 | 94 | git_cache_clear(&repo->objects); |
73b51450 | 95 | git_attr_cache_flush(repo); |
6fd195d7 | 96 | |
e976b56d RB |
97 | set_config(repo, NULL); |
98 | set_index(repo, NULL); | |
99 | set_odb(repo, NULL); | |
100 | set_refdb(repo, NULL); | |
879458e7 VM |
101 | } |
102 | ||
103 | void git_repository_free(git_repository *repo) | |
104 | { | |
105 | if (repo == NULL) | |
106 | return; | |
107 | ||
108 | git_repository__cleanup(repo); | |
109 | ||
110 | git_cache_free(&repo->objects); | |
111 | git_submodule_config_free(repo); | |
3eadfecd | 112 | |
114f5a6c | 113 | git_diff_driver_registry_free(repo->diff_drivers); |
3eadfecd | 114 | repo->diff_drivers = NULL; |
9462c471 | 115 | |
53607868 RB |
116 | git__free(repo->path_repository); |
117 | git__free(repo->workdir); | |
bade5194 | 118 | git__free(repo->namespace); |
53607868 | 119 | |
6de9b2ee | 120 | git__memzero(repo, sizeof(*repo)); |
9462c471 | 121 | git__free(repo); |
6fd195d7 VM |
122 | } |
123 | ||
9462c471 VM |
124 | /* |
125 | * Git repository open methods | |
126 | * | |
127 | * Open a repository object from its path | |
128 | */ | |
cb8a7961 | 129 | static bool valid_repository_path(git_buf *repository_path) |
5ad739e8 | 130 | { |
97769280 | 131 | /* Check OBJECTS_DIR first, since it will generate the longest path name */ |
1a481123 | 132 | if (git_path_contains_dir(repository_path, GIT_OBJECTS_DIR) == false) |
cb8a7961 | 133 | return false; |
5ad739e8 | 134 | |
97769280 | 135 | /* Ensure HEAD file exists */ |
1a481123 | 136 | if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false) |
cb8a7961 | 137 | return false; |
5ad739e8 | 138 | |
1a481123 | 139 | if (git_path_contains_dir(repository_path, GIT_REFS_DIR) == false) |
cb8a7961 | 140 | return false; |
5ad739e8 | 141 | |
cb8a7961 | 142 | return true; |
5ad739e8 VM |
143 | } |
144 | ||
51d00446 | 145 | static git_repository *repository_alloc(void) |
3315782c | 146 | { |
3e9e6cda | 147 | git_repository *repo = git__calloc(1, sizeof(git_repository)); |
3315782c VM |
148 | if (!repo) |
149 | return NULL; | |
150 | ||
5df18424 | 151 | if (git_cache_init(&repo->objects) < 0) { |
3286c408 | 152 | git__free(repo); |
81201a4c | 153 | return NULL; |
154 | } | |
3315782c | 155 | |
f2c25d18 VM |
156 | /* set all the entries in the cvar cache to `unset` */ |
157 | git_repository__cvar_cache_clear(repo); | |
158 | ||
6fd195d7 VM |
159 | return repo; |
160 | } | |
161 | ||
7cc3c920 JW |
162 | int git_repository_new(git_repository **out) |
163 | { | |
164 | *out = repository_alloc(); | |
165 | return 0; | |
166 | } | |
167 | ||
9462c471 | 168 | static int load_config_data(git_repository *repo) |
ec3c7a16 | 169 | { |
cb8a7961 | 170 | int is_bare; |
9462c471 | 171 | git_config *config; |
ec3c7a16 | 172 | |
cb8a7961 VM |
173 | if (git_repository_config__weakptr(&config, repo) < 0) |
174 | return -1; | |
ec3c7a16 | 175 | |
d3e9c4a5 | 176 | /* Try to figure out if it's bare, default to non-bare if it's not set */ |
29e948de | 177 | if (git_config_get_bool(&is_bare, config, "core.bare") < 0) |
d3e9c4a5 CMN |
178 | repo->is_bare = 0; |
179 | else | |
180 | repo->is_bare = is_bare; | |
c94785a9 | 181 | |
cb8a7961 | 182 | return 0; |
9462c471 | 183 | } |
ec3c7a16 | 184 | |
7784bcbb | 185 | static int load_workdir(git_repository *repo, git_buf *parent_path) |
9462c471 | 186 | { |
7784bcbb RB |
187 | int error; |
188 | git_config *config; | |
9f77b3f6 RB |
189 | const git_config_entry *ce; |
190 | git_buf worktree = GIT_BUF_INIT; | |
ec3c7a16 | 191 | |
97769280 | 192 | if (repo->is_bare) |
cb8a7961 | 193 | return 0; |
ec3c7a16 | 194 | |
9f77b3f6 RB |
195 | if ((error = git_repository_config__weakptr(&config, repo)) < 0) |
196 | return error; | |
197 | ||
198 | if ((error = git_config__lookup_entry( | |
199 | &ce, config, "core.worktree", false)) < 0) | |
200 | return error; | |
ec3c7a16 | 201 | |
9f77b3f6 RB |
202 | if (ce && ce->value) { |
203 | if ((error = git_path_prettify_dir( | |
204 | &worktree, ce->value, repo->path_repository)) < 0) | |
5f4a61ae | 205 | return error; |
9f77b3f6 RB |
206 | |
207 | repo->workdir = git_buf_detach(&worktree); | |
5f4a61ae | 208 | } |
9f77b3f6 RB |
209 | else if (parent_path && git_path_isdir(parent_path->ptr)) |
210 | repo->workdir = git_buf_detach(parent_path); | |
7784bcbb | 211 | else { |
9f77b3f6 RB |
212 | if (git_path_dirname_r(&worktree, repo->path_repository) < 0 || |
213 | git_path_to_dir(&worktree) < 0) | |
214 | return -1; | |
215 | ||
216 | repo->workdir = git_buf_detach(&worktree); | |
7784bcbb RB |
217 | } |
218 | ||
219 | GITERR_CHECK_ALLOC(repo->workdir); | |
cb8a7961 | 220 | return 0; |
ec3c7a16 VM |
221 | } |
222 | ||
7784bcbb RB |
223 | /* |
224 | * This function returns furthest offset into path where a ceiling dir | |
225 | * is found, so we can stop processing the path at that point. | |
226 | * | |
227 | * Note: converting this to use git_bufs instead of GIT_PATH_MAX buffers on | |
228 | * the stack could remove directories name limits, but at the cost of doing | |
229 | * repeated malloc/frees inside the loop below, so let's not do it now. | |
230 | */ | |
231 | static int find_ceiling_dir_offset( | |
232 | const char *path, | |
233 | const char *ceiling_directories) | |
234 | { | |
235 | char buf[GIT_PATH_MAX + 1]; | |
236 | char buf2[GIT_PATH_MAX + 1]; | |
237 | const char *ceil, *sep; | |
44ef8b1b | 238 | size_t len, max_len = 0, min_len; |
7784bcbb RB |
239 | |
240 | assert(path); | |
241 | ||
44ef8b1b | 242 | min_len = (size_t)(git_path_root(path) + 1); |
7784bcbb RB |
243 | |
244 | if (ceiling_directories == NULL || min_len == 0) | |
44ef8b1b | 245 | return (int)min_len; |
7784bcbb RB |
246 | |
247 | for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) { | |
248 | for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++); | |
249 | len = sep - ceil; | |
250 | ||
44ef8b1b | 251 | if (len == 0 || len >= sizeof(buf) || git_path_root(ceil) == -1) |
7784bcbb RB |
252 | continue; |
253 | ||
254 | strncpy(buf, ceil, len); | |
255 | buf[len] = '\0'; | |
256 | ||
257 | if (p_realpath(buf, buf2) == NULL) | |
258 | continue; | |
259 | ||
260 | len = strlen(buf2); | |
261 | if (len > 0 && buf2[len-1] == '/') | |
262 | buf[--len] = '\0'; | |
263 | ||
264 | if (!strncmp(path, buf2, len) && | |
3fe046cf | 265 | (path[len] == '/' || !path[len]) && |
7784bcbb RB |
266 | len > max_len) |
267 | { | |
268 | max_len = len; | |
269 | } | |
270 | } | |
271 | ||
44ef8b1b | 272 | return (int)(max_len <= min_len ? min_len : max_len); |
7784bcbb RB |
273 | } |
274 | ||
275 | /* | |
276 | * Read the contents of `file_path` and set `path_out` to the repo dir that | |
277 | * it points to. Before calling, set `path_out` to the base directory that | |
278 | * should be used if the contents of `file_path` are a relative path. | |
279 | */ | |
280 | static int read_gitfile(git_buf *path_out, const char *file_path) | |
281 | { | |
282 | int error = 0; | |
283 | git_buf file = GIT_BUF_INIT; | |
284 | size_t prefix_len = strlen(GIT_FILE_CONTENT_PREFIX); | |
285 | ||
286 | assert(path_out && file_path); | |
287 | ||
288 | if (git_futils_readbuffer(&file, file_path) < 0) | |
289 | return -1; | |
290 | ||
291 | git_buf_rtrim(&file); | |
6b415f62 RB |
292 | /* apparently on Windows, some people use backslashes in paths */ |
293 | git_path_mkposix(file.ptr); | |
7784bcbb | 294 | |
662880ca RB |
295 | if (git_buf_len(&file) <= prefix_len || |
296 | memcmp(git_buf_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0) | |
7784bcbb | 297 | { |
6b415f62 RB |
298 | giterr_set(GITERR_REPOSITORY, |
299 | "The `.git` file at '%s' is malformed", file_path); | |
7784bcbb RB |
300 | error = -1; |
301 | } | |
302 | else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) { | |
662880ca | 303 | const char *gitlink = git_buf_cstr(&file) + prefix_len; |
0f49200c | 304 | while (*gitlink && git__isspace(*gitlink)) gitlink++; |
6b415f62 | 305 | |
662880ca RB |
306 | error = git_path_prettify_dir( |
307 | path_out, gitlink, git_buf_cstr(path_out)); | |
7784bcbb RB |
308 | } |
309 | ||
310 | git_buf_free(&file); | |
311 | return error; | |
312 | } | |
313 | ||
314 | static int find_repo( | |
315 | git_buf *repo_path, | |
316 | git_buf *parent_path, | |
317 | const char *start_path, | |
318 | uint32_t flags, | |
319 | const char *ceiling_dirs) | |
691aa968 | 320 | { |
7784bcbb RB |
321 | int error; |
322 | git_buf path = GIT_BUF_INIT; | |
323 | struct stat st; | |
324 | dev_t initial_device = 0; | |
3fe046cf | 325 | bool try_with_dot_git = ((flags & GIT_REPOSITORY_OPEN_BARE) != 0); |
7784bcbb RB |
326 | int ceiling_offset; |
327 | ||
328 | git_buf_free(repo_path); | |
329 | ||
3fe046cf | 330 | if ((error = git_path_prettify(&path, start_path, NULL)) < 0) |
7784bcbb RB |
331 | return error; |
332 | ||
333 | ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs); | |
334 | ||
3fe046cf RB |
335 | if (!try_with_dot_git && |
336 | (error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0) | |
7784bcbb RB |
337 | return error; |
338 | ||
fa6420f7 | 339 | while (!error && !git_buf_len(repo_path)) { |
7784bcbb RB |
340 | if (p_stat(path.ptr, &st) == 0) { |
341 | /* check that we have not crossed device boundaries */ | |
342 | if (initial_device == 0) | |
343 | initial_device = st.st_dev; | |
344 | else if (st.st_dev != initial_device && | |
345 | (flags & GIT_REPOSITORY_OPEN_CROSS_FS) == 0) | |
346 | break; | |
347 | ||
348 | if (S_ISDIR(st.st_mode)) { | |
349 | if (valid_repository_path(&path)) { | |
350 | git_path_to_dir(&path); | |
351 | git_buf_set(repo_path, path.ptr, path.size); | |
352 | break; | |
353 | } | |
354 | } | |
355 | else if (S_ISREG(st.st_mode)) { | |
356 | git_buf repo_link = GIT_BUF_INIT; | |
357 | ||
358 | if (!(error = read_gitfile(&repo_link, path.ptr))) { | |
359 | if (valid_repository_path(&repo_link)) | |
360 | git_buf_swap(repo_path, &repo_link); | |
146f5c75 CMN |
361 | |
362 | git_buf_free(&repo_link); | |
7784bcbb RB |
363 | break; |
364 | } | |
365 | git_buf_free(&repo_link); | |
366 | } | |
367 | } | |
368 | ||
369 | /* move up one directory level */ | |
370 | if (git_path_dirname_r(&path, path.ptr) < 0) { | |
371 | error = -1; | |
372 | break; | |
373 | } | |
374 | ||
375 | if (try_with_dot_git) { | |
376 | /* if we tried original dir with and without .git AND either hit | |
377 | * directory ceiling or NO_SEARCH was requested, then be done. | |
378 | */ | |
379 | if (path.ptr[ceiling_offset] == '\0' || | |
380 | (flags & GIT_REPOSITORY_OPEN_NO_SEARCH) != 0) | |
381 | break; | |
382 | /* otherwise look first for .git item */ | |
383 | error = git_buf_joinpath(&path, path.ptr, DOT_GIT); | |
384 | } | |
385 | try_with_dot_git = !try_with_dot_git; | |
386 | } | |
387 | ||
3fe046cf | 388 | if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) { |
fa6420f7 | 389 | if (!git_buf_len(repo_path)) |
7784bcbb RB |
390 | git_buf_clear(parent_path); |
391 | else { | |
392 | git_path_dirname_r(parent_path, path.ptr); | |
393 | git_path_to_dir(parent_path); | |
394 | } | |
395 | if (git_buf_oom(parent_path)) | |
396 | return -1; | |
397 | } | |
398 | ||
399 | git_buf_free(&path); | |
400 | ||
fa6420f7 | 401 | if (!git_buf_len(repo_path) && !error) { |
cb8a7961 | 402 | giterr_set(GITERR_REPOSITORY, |
7784bcbb | 403 | "Could not find repository from '%s'", start_path); |
904b67e6 | 404 | error = GIT_ENOTFOUND; |
97769280 | 405 | } |
691aa968 | 406 | |
7784bcbb RB |
407 | return error; |
408 | } | |
409 | ||
a442ed68 VM |
410 | int git_repository_open_bare( |
411 | git_repository **repo_ptr, | |
412 | const char *bare_path) | |
413 | { | |
414 | int error; | |
415 | git_buf path = GIT_BUF_INIT; | |
416 | git_repository *repo = NULL; | |
417 | ||
418 | if ((error = git_path_prettify_dir(&path, bare_path, NULL)) < 0) | |
419 | return error; | |
420 | ||
421 | if (!valid_repository_path(&path)) { | |
422 | git_buf_free(&path); | |
423 | giterr_set(GITERR_REPOSITORY, "Path is not a repository: %s", bare_path); | |
424 | return GIT_ENOTFOUND; | |
425 | } | |
426 | ||
427 | repo = repository_alloc(); | |
428 | GITERR_CHECK_ALLOC(repo); | |
429 | ||
430 | repo->path_repository = git_buf_detach(&path); | |
431 | GITERR_CHECK_ALLOC(repo->path_repository); | |
432 | ||
433 | /* of course we're bare! */ | |
434 | repo->is_bare = 1; | |
435 | repo->workdir = NULL; | |
436 | ||
437 | *repo_ptr = repo; | |
438 | return 0; | |
439 | } | |
440 | ||
7784bcbb RB |
441 | int git_repository_open_ext( |
442 | git_repository **repo_ptr, | |
443 | const char *start_path, | |
c9fc4a6f | 444 | unsigned int flags, |
7784bcbb RB |
445 | const char *ceiling_dirs) |
446 | { | |
447 | int error; | |
448 | git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT; | |
449 | git_repository *repo; | |
450 | ||
662880ca RB |
451 | if (repo_ptr) |
452 | *repo_ptr = NULL; | |
7784bcbb | 453 | |
662880ca RB |
454 | error = find_repo(&path, &parent, start_path, flags, ceiling_dirs); |
455 | if (error < 0 || !repo_ptr) | |
7784bcbb RB |
456 | return error; |
457 | ||
e52ed7a5 | 458 | repo = repository_alloc(); |
cb8a7961 | 459 | GITERR_CHECK_ALLOC(repo); |
691aa968 | 460 | |
7784bcbb | 461 | repo->path_repository = git_buf_detach(&path); |
cb8a7961 | 462 | GITERR_CHECK_ALLOC(repo->path_repository); |
691aa968 | 463 | |
3fe046cf RB |
464 | if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0) |
465 | repo->is_bare = 1; | |
466 | else if ((error = load_config_data(repo)) < 0 || | |
7784bcbb RB |
467 | (error = load_workdir(repo, &parent)) < 0) |
468 | { | |
469 | git_repository_free(repo); | |
470 | return error; | |
471 | } | |
691aa968 | 472 | |
146f5c75 | 473 | git_buf_free(&parent); |
7784bcbb | 474 | *repo_ptr = repo; |
cb8a7961 | 475 | return 0; |
7784bcbb | 476 | } |
97769280 | 477 | |
7784bcbb RB |
478 | int git_repository_open(git_repository **repo_out, const char *path) |
479 | { | |
480 | return git_repository_open_ext( | |
481 | repo_out, path, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL); | |
482 | } | |
483 | ||
6782245e CMN |
484 | int git_repository_wrap_odb(git_repository **repo_out, git_odb *odb) |
485 | { | |
486 | git_repository *repo; | |
487 | ||
488 | repo = repository_alloc(); | |
489 | GITERR_CHECK_ALLOC(repo); | |
490 | ||
491 | git_repository_set_odb(repo, odb); | |
492 | *repo_out = repo; | |
493 | ||
494 | return 0; | |
495 | } | |
496 | ||
7784bcbb | 497 | int git_repository_discover( |
7a3bd1e7 | 498 | git_buf *out, |
7784bcbb RB |
499 | const char *start_path, |
500 | int across_fs, | |
501 | const char *ceiling_dirs) | |
502 | { | |
7784bcbb | 503 | uint32_t flags = across_fs ? GIT_REPOSITORY_OPEN_CROSS_FS : 0; |
7784bcbb | 504 | |
7a3bd1e7 | 505 | assert(start_path); |
7784bcbb | 506 | |
7a3bd1e7 | 507 | git_buf_sanitize(out); |
7784bcbb | 508 | |
7a3bd1e7 | 509 | return find_repo(out, NULL, start_path, flags, ceiling_dirs); |
691aa968 VM |
510 | } |
511 | ||
9462c471 | 512 | static int load_config( |
7784bcbb RB |
513 | git_config **out, |
514 | git_repository *repo, | |
515 | const char *global_config_path, | |
4258d483 | 516 | const char *xdg_config_path, |
7784bcbb | 517 | const char *system_config_path) |
b22d1479 | 518 | { |
cc6b4162 | 519 | int error; |
97769280 | 520 | git_buf config_path = GIT_BUF_INIT; |
9462c471 | 521 | git_config *cfg = NULL; |
b22d1479 | 522 | |
9462c471 | 523 | assert(repo && out); |
07ff8817 | 524 | |
cc6b4162 RB |
525 | if ((error = git_config_new(&cfg)) < 0) |
526 | return error; | |
b22d1479 | 527 | |
cc6b4162 RB |
528 | error = git_buf_joinpath( |
529 | &config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO); | |
530 | if (error < 0) | |
cb8a7961 | 531 | goto on_error; |
97769280 | 532 | |
cc6b4162 RB |
533 | if ((error = git_config_add_file_ondisk( |
534 | cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, 0)) < 0 && | |
535 | error != GIT_ENOTFOUND) | |
cb8a7961 VM |
536 | goto on_error; |
537 | ||
538 | git_buf_free(&config_path); | |
b22d1479 | 539 | |
cc6b4162 RB |
540 | if (global_config_path != NULL && |
541 | (error = git_config_add_file_ondisk( | |
542 | cfg, global_config_path, GIT_CONFIG_LEVEL_GLOBAL, 0)) < 0 && | |
543 | error != GIT_ENOTFOUND) | |
544 | goto on_error; | |
8b4f9b17 | 545 | |
cc6b4162 RB |
546 | if (xdg_config_path != NULL && |
547 | (error = git_config_add_file_ondisk( | |
548 | cfg, xdg_config_path, GIT_CONFIG_LEVEL_XDG, 0)) < 0 && | |
549 | error != GIT_ENOTFOUND) | |
550 | goto on_error; | |
b22d1479 | 551 | |
cc6b4162 RB |
552 | if (system_config_path != NULL && |
553 | (error = git_config_add_file_ondisk( | |
554 | cfg, system_config_path, GIT_CONFIG_LEVEL_SYSTEM, 0)) < 0 && | |
555 | error != GIT_ENOTFOUND) | |
556 | goto on_error; | |
9ba9e513 | 557 | |
38f7d026 RB |
558 | giterr_clear(); /* clear any lingering ENOTFOUND errors */ |
559 | ||
9462c471 | 560 | *out = cfg; |
cb8a7961 | 561 | return 0; |
b22d1479 | 562 | |
cb8a7961 VM |
563 | on_error: |
564 | git_buf_free(&config_path); | |
9462c471 VM |
565 | git_config_free(cfg); |
566 | *out = NULL; | |
cc6b4162 | 567 | return error; |
b22d1479 CMN |
568 | } |
569 | ||
53607868 | 570 | static const char *path_unless_empty(git_buf *buf) |
40fe5fbe | 571 | { |
53607868 RB |
572 | return git_buf_len(buf) > 0 ? git_buf_cstr(buf) : NULL; |
573 | } | |
8b4f9b17 | 574 | |
53607868 RB |
575 | int git_repository_config__weakptr(git_config **out, git_repository *repo) |
576 | { | |
577 | int error = 0; | |
40fe5fbe | 578 | |
53607868 RB |
579 | if (repo->_config == NULL) { |
580 | git_buf global_buf = GIT_BUF_INIT; | |
581 | git_buf xdg_buf = GIT_BUF_INIT; | |
582 | git_buf system_buf = GIT_BUF_INIT; | |
583 | git_config *config; | |
584 | ||
585 | git_config_find_global_r(&global_buf); | |
586 | git_config_find_xdg_r(&xdg_buf); | |
587 | git_config_find_system_r(&system_buf); | |
588 | ||
a4b75dcf CMN |
589 | /* If there is no global file, open a backend for it anyway */ |
590 | if (git_buf_len(&global_buf) == 0) | |
591 | git_config__global_location(&global_buf); | |
592 | ||
53607868 RB |
593 | error = load_config( |
594 | &config, repo, | |
595 | path_unless_empty(&global_buf), | |
596 | path_unless_empty(&xdg_buf), | |
597 | path_unless_empty(&system_buf)); | |
598 | if (!error) { | |
599 | GIT_REFCOUNT_OWN(config, repo); | |
600 | ||
e976b56d | 601 | config = git__compare_and_swap(&repo->_config, NULL, config); |
53607868 RB |
602 | if (config != NULL) { |
603 | GIT_REFCOUNT_OWN(config, NULL); | |
604 | git_config_free(config); | |
605 | } | |
606 | } | |
97769280 RB |
607 | |
608 | git_buf_free(&global_buf); | |
a8918418 | 609 | git_buf_free(&xdg_buf); |
97769280 | 610 | git_buf_free(&system_buf); |
9462c471 | 611 | } |
40fe5fbe | 612 | |
9462c471 | 613 | *out = repo->_config; |
53607868 | 614 | return error; |
40fe5fbe CMN |
615 | } |
616 | ||
9462c471 | 617 | int git_repository_config(git_config **out, git_repository *repo) |
fd0574e5 | 618 | { |
cb8a7961 VM |
619 | if (git_repository_config__weakptr(out, repo) < 0) |
620 | return -1; | |
fd0574e5 | 621 | |
cb8a7961 VM |
622 | GIT_REFCOUNT_INC(*out); |
623 | return 0; | |
9462c471 VM |
624 | } |
625 | ||
626 | void git_repository_set_config(git_repository *repo, git_config *config) | |
627 | { | |
628 | assert(repo && config); | |
e976b56d | 629 | set_config(repo, config); |
9462c471 VM |
630 | } |
631 | ||
632 | int git_repository_odb__weakptr(git_odb **out, git_repository *repo) | |
633 | { | |
53607868 RB |
634 | int error = 0; |
635 | ||
9462c471 VM |
636 | assert(repo && out); |
637 | ||
638 | if (repo->_odb == NULL) { | |
97769280 | 639 | git_buf odb_path = GIT_BUF_INIT; |
53607868 | 640 | git_odb *odb; |
9462c471 | 641 | |
53607868 | 642 | git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR); |
9462c471 | 643 | |
53607868 RB |
644 | error = git_odb_open(&odb, odb_path.ptr); |
645 | if (!error) { | |
646 | GIT_REFCOUNT_OWN(odb, repo); | |
cb8a7961 | 647 | |
e976b56d | 648 | odb = git__compare_and_swap(&repo->_odb, NULL, odb); |
53607868 RB |
649 | if (odb != NULL) { |
650 | GIT_REFCOUNT_OWN(odb, NULL); | |
651 | git_odb_free(odb); | |
652 | } | |
653 | } | |
9462c471 | 654 | |
53607868 | 655 | git_buf_free(&odb_path); |
9462c471 | 656 | } |
fd0574e5 | 657 | |
9462c471 | 658 | *out = repo->_odb; |
53607868 | 659 | return error; |
fd0574e5 RG |
660 | } |
661 | ||
9462c471 | 662 | int git_repository_odb(git_odb **out, git_repository *repo) |
6fd195d7 | 663 | { |
cb8a7961 VM |
664 | if (git_repository_odb__weakptr(out, repo) < 0) |
665 | return -1; | |
1795f879 | 666 | |
cb8a7961 VM |
667 | GIT_REFCOUNT_INC(*out); |
668 | return 0; | |
9462c471 | 669 | } |
6fd195d7 | 670 | |
9462c471 VM |
671 | void git_repository_set_odb(git_repository *repo, git_odb *odb) |
672 | { | |
673 | assert(repo && odb); | |
e976b56d | 674 | set_odb(repo, odb); |
9462c471 VM |
675 | } |
676 | ||
d00d5464 ET |
677 | int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo) |
678 | { | |
53607868 RB |
679 | int error = 0; |
680 | ||
d00d5464 ET |
681 | assert(out && repo); |
682 | ||
683 | if (repo->_refdb == NULL) { | |
53607868 | 684 | git_refdb *refdb; |
d00d5464 | 685 | |
53607868 RB |
686 | error = git_refdb_open(&refdb, repo); |
687 | if (!error) { | |
688 | GIT_REFCOUNT_OWN(refdb, repo); | |
d00d5464 | 689 | |
e976b56d | 690 | refdb = git__compare_and_swap(&repo->_refdb, NULL, refdb); |
53607868 RB |
691 | if (refdb != NULL) { |
692 | GIT_REFCOUNT_OWN(refdb, NULL); | |
693 | git_refdb_free(refdb); | |
694 | } | |
695 | } | |
d00d5464 ET |
696 | } |
697 | ||
698 | *out = repo->_refdb; | |
53607868 | 699 | return error; |
d00d5464 ET |
700 | } |
701 | ||
702 | int git_repository_refdb(git_refdb **out, git_repository *repo) | |
703 | { | |
704 | if (git_repository_refdb__weakptr(out, repo) < 0) | |
705 | return -1; | |
706 | ||
707 | GIT_REFCOUNT_INC(*out); | |
708 | return 0; | |
709 | } | |
710 | ||
711 | void git_repository_set_refdb(git_repository *repo, git_refdb *refdb) | |
712 | { | |
53607868 | 713 | assert(repo && refdb); |
e976b56d | 714 | set_refdb(repo, refdb); |
d00d5464 ET |
715 | } |
716 | ||
9462c471 VM |
717 | int git_repository_index__weakptr(git_index **out, git_repository *repo) |
718 | { | |
53607868 RB |
719 | int error = 0; |
720 | ||
9462c471 VM |
721 | assert(out && repo); |
722 | ||
9462c471 | 723 | if (repo->_index == NULL) { |
97769280 | 724 | git_buf index_path = GIT_BUF_INIT; |
53607868 | 725 | git_index *index; |
9462c471 | 726 | |
53607868 | 727 | git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE); |
9462c471 | 728 | |
53607868 RB |
729 | error = git_index_open(&index, index_path.ptr); |
730 | if (!error) { | |
731 | GIT_REFCOUNT_OWN(index, repo); | |
cb8a7961 | 732 | |
e976b56d | 733 | index = git__compare_and_swap(&repo->_index, NULL, index); |
53607868 RB |
734 | if (index != NULL) { |
735 | GIT_REFCOUNT_OWN(index, NULL); | |
736 | git_index_free(index); | |
737 | } | |
9462c471 | 738 | |
53607868 RB |
739 | error = git_index_set_caps(repo->_index, GIT_INDEXCAP_FROM_OWNER); |
740 | } | |
da825c92 | 741 | |
53607868 | 742 | git_buf_free(&index_path); |
9462c471 VM |
743 | } |
744 | ||
9462c471 | 745 | *out = repo->_index; |
53607868 | 746 | return error; |
9462c471 | 747 | } |
1795f879 | 748 | |
9462c471 VM |
749 | int git_repository_index(git_index **out, git_repository *repo) |
750 | { | |
cb8a7961 VM |
751 | if (git_repository_index__weakptr(out, repo) < 0) |
752 | return -1; | |
9462c471 | 753 | |
cb8a7961 VM |
754 | GIT_REFCOUNT_INC(*out); |
755 | return 0; | |
3315782c VM |
756 | } |
757 | ||
9462c471 VM |
758 | void git_repository_set_index(git_repository *repo, git_index *index) |
759 | { | |
760 | assert(repo && index); | |
e976b56d | 761 | set_index(repo, index); |
9462c471 VM |
762 | } |
763 | ||
bade5194 VM |
764 | int git_repository_set_namespace(git_repository *repo, const char *namespace) |
765 | { | |
766 | git__free(repo->namespace); | |
767 | ||
768 | if (namespace == NULL) { | |
769 | repo->namespace = NULL; | |
770 | return 0; | |
771 | } | |
772 | ||
773 | return (repo->namespace = git__strdup(namespace)) ? 0 : -1; | |
774 | } | |
775 | ||
776 | const char *git_repository_get_namespace(git_repository *repo) | |
777 | { | |
778 | return repo->namespace; | |
779 | } | |
780 | ||
2c227b8b | 781 | static int check_repositoryformatversion(git_config *config) |
40c44d2f | 782 | { |
cb8a7961 | 783 | int version; |
5663e61a | 784 | |
29e948de | 785 | if (git_config_get_int32(&version, config, "core.repositoryformatversion") < 0) |
cb8a7961 | 786 | return -1; |
5663e61a | 787 | |
29e948de | 788 | if (GIT_REPO_VERSION < version) { |
cb8a7961 VM |
789 | giterr_set(GITERR_REPOSITORY, |
790 | "Unsupported repository version %d. Only versions up to %d are supported.", | |
29e948de | 791 | version, GIT_REPO_VERSION); |
cb8a7961 VM |
792 | return -1; |
793 | } | |
5663e61a | 794 | |
cb8a7961 | 795 | return 0; |
5663e61a | 796 | } |
797 | ||
662880ca | 798 | static int repo_init_create_head(const char *git_dir, const char *ref_name) |
e1f8cad0 | 799 | { |
97769280 | 800 | git_buf ref_path = GIT_BUF_INIT; |
9462c471 | 801 | git_filebuf ref = GIT_FILEBUF_INIT; |
662880ca | 802 | const char *fmt; |
9462c471 | 803 | |
cb8a7961 | 804 | if (git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE) < 0 || |
1d3a8aeb | 805 | git_filebuf_open(&ref, ref_path.ptr, 0, GIT_REFS_FILE_MODE) < 0) |
662880ca RB |
806 | goto fail; |
807 | ||
808 | if (!ref_name) | |
809 | ref_name = GIT_BRANCH_MASTER; | |
810 | ||
74a24005 | 811 | if (git__prefixcmp(ref_name, GIT_REFS_DIR) == 0) |
662880ca RB |
812 | fmt = "ref: %s\n"; |
813 | else | |
74a24005 | 814 | fmt = "ref: " GIT_REFS_HEADS_DIR "%s\n"; |
662880ca RB |
815 | |
816 | if (git_filebuf_printf(&ref, fmt, ref_name) < 0 || | |
1d3a8aeb | 817 | git_filebuf_commit(&ref) < 0) |
662880ca | 818 | goto fail; |
9462c471 | 819 | |
97769280 | 820 | git_buf_free(&ref_path); |
cb8a7961 | 821 | return 0; |
662880ca RB |
822 | |
823 | fail: | |
824 | git_buf_free(&ref_path); | |
825 | git_filebuf_cleanup(&ref); | |
826 | return -1; | |
9462c471 VM |
827 | } |
828 | ||
fac66990 | 829 | static bool is_chmod_supported(const char *file_path) |
830 | { | |
831 | struct stat st1, st2; | |
fac66990 | 832 | |
833 | if (p_stat(file_path, &st1) < 0) | |
834 | return false; | |
835 | ||
836 | if (p_chmod(file_path, st1.st_mode ^ S_IXUSR) < 0) | |
837 | return false; | |
838 | ||
839 | if (p_stat(file_path, &st2) < 0) | |
840 | return false; | |
841 | ||
6b7991e2 | 842 | return (st1.st_mode != st2.st_mode); |
fac66990 | 843 | } |
844 | ||
693b23c0 | 845 | static bool is_filesystem_case_insensitive(const char *gitdir_path) |
846 | { | |
847 | git_buf path = GIT_BUF_INIT; | |
6b7991e2 | 848 | int is_insensitive = -1; |
693b23c0 | 849 | |
6b7991e2 RB |
850 | if (!git_buf_joinpath(&path, gitdir_path, "CoNfIg")) |
851 | is_insensitive = git_path_exists(git_buf_cstr(&path)); | |
693b23c0 | 852 | |
693b23c0 | 853 | git_buf_free(&path); |
6b7991e2 | 854 | return is_insensitive; |
693b23c0 | 855 | } |
856 | ||
ca1b6e54 RB |
857 | static bool are_symlinks_supported(const char *wd_path) |
858 | { | |
859 | git_buf path = GIT_BUF_INIT; | |
860 | int fd; | |
861 | struct stat st; | |
6b7991e2 | 862 | int symlinks_supported = -1; |
ca1b6e54 | 863 | |
1d3a8aeb | 864 | if ((fd = git_futils_mktmp(&path, wd_path, 0666)) < 0 || |
ca1b6e54 RB |
865 | p_close(fd) < 0 || |
866 | p_unlink(path.ptr) < 0 || | |
867 | p_symlink("testing", path.ptr) < 0 || | |
868 | p_lstat(path.ptr, &st) < 0) | |
6b7991e2 | 869 | symlinks_supported = false; |
ca1b6e54 | 870 | else |
6b7991e2 | 871 | symlinks_supported = (S_ISLNK(st.st_mode) != 0); |
ca1b6e54 RB |
872 | |
873 | (void)p_unlink(path.ptr); | |
874 | git_buf_free(&path); | |
875 | ||
6b7991e2 RB |
876 | return symlinks_supported; |
877 | } | |
878 | ||
af302aca RB |
879 | #ifdef GIT_USE_ICONV |
880 | ||
6b7991e2 RB |
881 | static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX"; |
882 | static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX"; | |
883 | ||
840fb4fc RB |
884 | /* Check if the platform is decomposing unicode data for us. We will |
885 | * emulate core Git and prefer to use precomposed unicode data internally | |
886 | * on these platforms, composing the decomposed unicode on the fly. | |
887 | * | |
888 | * This mainly happens on the Mac where HDFS stores filenames as | |
889 | * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will | |
890 | * return decomposed unicode from readdir() even when the actual | |
891 | * filesystem is storing precomposed unicode. | |
6b7991e2 | 892 | */ |
840fb4fc | 893 | static bool does_fs_decompose_unicode_paths(const char *wd_path) |
6b7991e2 RB |
894 | { |
895 | git_buf path = GIT_BUF_INIT; | |
896 | int fd; | |
840fb4fc | 897 | bool found_decomposed = false; |
6b7991e2 RB |
898 | char tmp[6]; |
899 | ||
900 | /* Create a file using a precomposed path and then try to find it | |
901 | * using the decomposed name. If the lookup fails, then we will mark | |
902 | * that we should precompose unicode for this repository. | |
903 | */ | |
904 | if (git_buf_joinpath(&path, wd_path, nfc_file) < 0 || | |
905 | (fd = p_mkstemp(path.ptr)) < 0) | |
840fb4fc | 906 | goto done; |
6b7991e2 RB |
907 | p_close(fd); |
908 | ||
909 | /* record trailing digits generated by mkstemp */ | |
910 | memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp)); | |
911 | ||
912 | /* try to look up as NFD path */ | |
913 | if (git_buf_joinpath(&path, wd_path, nfd_file) < 0) | |
840fb4fc | 914 | goto done; |
6b7991e2 RB |
915 | memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); |
916 | ||
840fb4fc | 917 | found_decomposed = git_path_exists(path.ptr); |
6b7991e2 | 918 | |
840fb4fc | 919 | /* remove temporary file (using original precomposed path) */ |
6b7991e2 | 920 | if (git_buf_joinpath(&path, wd_path, nfc_file) < 0) |
840fb4fc | 921 | goto done; |
6b7991e2 RB |
922 | memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); |
923 | ||
924 | (void)p_unlink(path.ptr); | |
925 | ||
840fb4fc | 926 | done: |
6b7991e2 | 927 | git_buf_free(&path); |
840fb4fc | 928 | return found_decomposed; |
ca1b6e54 RB |
929 | } |
930 | ||
af302aca RB |
931 | #endif |
932 | ||
270160b9 | 933 | static int create_empty_file(const char *path, mode_t mode) |
934 | { | |
935 | int fd; | |
936 | ||
937 | if ((fd = p_creat(path, mode)) < 0) { | |
938 | giterr_set(GITERR_OS, "Error while creating '%s'", path); | |
939 | return -1; | |
940 | } | |
941 | ||
942 | if (p_close(fd) < 0) { | |
943 | giterr_set(GITERR_OS, "Error while closing '%s'", path); | |
944 | return -1; | |
945 | } | |
946 | ||
947 | return 0; | |
948 | } | |
949 | ||
14997dc5 RB |
950 | static int repo_local_config( |
951 | git_config **out, | |
952 | git_buf *config_dir, | |
953 | git_repository *repo, | |
954 | const char *repo_dir) | |
9462c471 | 955 | { |
ca1b6e54 | 956 | int error = 0; |
14997dc5 RB |
957 | git_config *parent; |
958 | const char *cfg_path; | |
9462c471 | 959 | |
14997dc5 | 960 | if (git_buf_joinpath(config_dir, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0) |
cb8a7961 | 961 | return -1; |
14997dc5 | 962 | cfg_path = git_buf_cstr(config_dir); |
9462c471 | 963 | |
14997dc5 | 964 | /* make LOCAL config if missing */ |
5173ea92 RB |
965 | if (!git_path_isfile(cfg_path) && |
966 | (error = create_empty_file(cfg_path, GIT_CONFIG_FILE_MODE)) < 0) | |
14997dc5 | 967 | return error; |
270160b9 | 968 | |
14997dc5 RB |
969 | /* if no repo, just open that file directly */ |
970 | if (!repo) | |
971 | return git_config_open_ondisk(out, cfg_path); | |
972 | ||
973 | /* otherwise, open parent config and get that level */ | |
974 | if ((error = git_repository_config__weakptr(&parent, repo)) < 0) | |
975 | return error; | |
976 | ||
977 | if (git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL) < 0) { | |
5173ea92 RB |
978 | giterr_clear(); |
979 | ||
980 | if (!(error = git_config_add_file_ondisk( | |
981 | parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, false))) | |
14997dc5 | 982 | error = git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL); |
cb8a7961 | 983 | } |
9462c471 | 984 | |
14997dc5 RB |
985 | git_config_free(parent); |
986 | ||
987 | return error; | |
988 | } | |
989 | ||
990 | static int repo_init_fs_configs( | |
991 | git_config *cfg, | |
992 | const char *cfg_path, | |
993 | const char *repo_dir, | |
994 | const char *work_dir, | |
995 | bool update_ignorecase) | |
996 | { | |
997 | int error = 0; | |
998 | ||
999 | if (!work_dir) | |
1000 | work_dir = repo_dir; | |
1001 | ||
1002 | if ((error = git_config_set_bool( | |
1003 | cfg, "core.filemode", is_chmod_supported(cfg_path))) < 0) | |
1004 | return error; | |
1005 | ||
1006 | if (!are_symlinks_supported(work_dir)) { | |
1007 | if ((error = git_config_set_bool(cfg, "core.symlinks", false)) < 0) | |
1008 | return error; | |
1009 | } else if (git_config_delete_entry(cfg, "core.symlinks") < 0) | |
1010 | giterr_clear(); | |
2c227b8b | 1011 | |
14997dc5 RB |
1012 | if (update_ignorecase) { |
1013 | if (is_filesystem_case_insensitive(repo_dir)) { | |
1014 | if ((error = git_config_set_bool(cfg, "core.ignorecase", true)) < 0) | |
1015 | return error; | |
1016 | } else if (git_config_delete_entry(cfg, "core.ignorecase") < 0) | |
1017 | giterr_clear(); | |
1018 | } | |
662880ca | 1019 | |
af302aca | 1020 | #ifdef GIT_USE_ICONV |
14997dc5 RB |
1021 | if ((error = git_config_set_bool( |
1022 | cfg, "core.precomposeunicode", | |
1023 | does_fs_decompose_unicode_paths(work_dir))) < 0) | |
1024 | return error; | |
6b7991e2 RB |
1025 | #endif |
1026 | ||
14997dc5 RB |
1027 | return 0; |
1028 | } | |
7623b1b6 | 1029 | |
14997dc5 RB |
1030 | static int repo_init_config( |
1031 | const char *repo_dir, | |
1032 | const char *work_dir, | |
1033 | uint32_t flags, | |
1034 | uint32_t mode) | |
1035 | { | |
1036 | int error = 0; | |
1037 | git_buf cfg_path = GIT_BUF_INIT; | |
1038 | git_config *config = NULL; | |
1039 | bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0); | |
1040 | bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0); | |
1041 | ||
1042 | if ((error = repo_local_config(&config, &cfg_path, NULL, repo_dir)) < 0) | |
1043 | goto cleanup; | |
1044 | ||
1045 | if (is_reinit && (error = check_repositoryformatversion(config)) < 0) | |
1046 | goto cleanup; | |
1047 | ||
1048 | #define SET_REPO_CONFIG(TYPE, NAME, VAL) do { \ | |
1049 | if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \ | |
1050 | goto cleanup; } while (0) | |
1051 | ||
1052 | SET_REPO_CONFIG(bool, "core.bare", is_bare); | |
1053 | SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION); | |
1054 | ||
1055 | if ((error = repo_init_fs_configs( | |
1056 | config, cfg_path.ptr, repo_dir, work_dir, !is_reinit)) < 0) | |
1057 | goto cleanup; | |
5173ea92 | 1058 | |
6b7991e2 RB |
1059 | if (!is_bare) { |
1060 | SET_REPO_CONFIG(bool, "core.logallrefupdates", true); | |
ca1b6e54 | 1061 | |
14997dc5 | 1062 | if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD)) |
ca1b6e54 | 1063 | SET_REPO_CONFIG(string, "core.worktree", work_dir); |
14997dc5 | 1064 | else if (is_reinit) { |
54b2a37a | 1065 | if (git_config_delete_entry(config, "core.worktree") < 0) |
89cd5708 | 1066 | giterr_clear(); |
ca1b6e54 | 1067 | } |
ca1b6e54 RB |
1068 | } |
1069 | ||
5173ea92 | 1070 | if (mode == GIT_REPOSITORY_INIT_SHARED_GROUP) { |
662880ca RB |
1071 | SET_REPO_CONFIG(int32, "core.sharedrepository", 1); |
1072 | SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true); | |
ca1b6e54 | 1073 | } |
5173ea92 | 1074 | else if (mode == GIT_REPOSITORY_INIT_SHARED_ALL) { |
662880ca RB |
1075 | SET_REPO_CONFIG(int32, "core.sharedrepository", 2); |
1076 | SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true); | |
1077 | } | |
9462c471 | 1078 | |
ca1b6e54 | 1079 | cleanup: |
14997dc5 | 1080 | git_buf_free(&cfg_path); |
9462c471 | 1081 | git_config_free(config); |
662880ca | 1082 | |
ca1b6e54 | 1083 | return error; |
d2d6912e | 1084 | } |
e1f8cad0 | 1085 | |
867f7c9b | 1086 | static int repo_reinit_submodule_fs(git_submodule *sm, const char *n, void *p) |
5173ea92 | 1087 | { |
14997dc5 RB |
1088 | git_repository *smrepo = NULL; |
1089 | GIT_UNUSED(n); GIT_UNUSED(p); | |
5173ea92 | 1090 | |
14997dc5 | 1091 | if (git_submodule_open(&smrepo, sm) < 0 || |
867f7c9b | 1092 | git_repository_reinit_filesystem(smrepo, true) < 0) |
14997dc5 RB |
1093 | giterr_clear(); |
1094 | git_repository_free(smrepo); | |
5173ea92 | 1095 | |
14997dc5 RB |
1096 | return 0; |
1097 | } | |
5173ea92 | 1098 | |
867f7c9b | 1099 | int git_repository_reinit_filesystem(git_repository *repo, int recurse) |
14997dc5 RB |
1100 | { |
1101 | int error = 0; | |
1102 | git_buf path = GIT_BUF_INIT; | |
1103 | git_config *config = NULL; | |
1104 | const char *repo_dir = git_repository_path(repo); | |
5173ea92 | 1105 | |
14997dc5 RB |
1106 | if (!(error = repo_local_config(&config, &path, repo, repo_dir))) |
1107 | error = repo_init_fs_configs( | |
1108 | config, path.ptr, repo_dir, git_repository_workdir(repo), true); | |
5173ea92 | 1109 | |
14997dc5 RB |
1110 | git_config_free(config); |
1111 | git_buf_free(&path); | |
5173ea92 | 1112 | |
5173ea92 RB |
1113 | git_repository__cvar_cache_clear(repo); |
1114 | ||
14997dc5 | 1115 | if (!repo->is_bare && recurse) |
867f7c9b | 1116 | (void)git_submodule_foreach(repo, repo_reinit_submodule_fs, NULL); |
14997dc5 | 1117 | |
5173ea92 RB |
1118 | return error; |
1119 | } | |
1120 | ||
dc34da6e | 1121 | static int repo_write_template( |
991a56c7 RB |
1122 | const char *git_dir, |
1123 | bool allow_overwrite, | |
1124 | const char *file, | |
1125 | mode_t mode, | |
662880ca | 1126 | bool hidden, |
991a56c7 | 1127 | const char *content) |
dc34da6e RB |
1128 | { |
1129 | git_buf path = GIT_BUF_INIT; | |
991a56c7 | 1130 | int fd, error = 0, flags; |
dc34da6e RB |
1131 | |
1132 | if (git_buf_joinpath(&path, git_dir, file) < 0) | |
1133 | return -1; | |
1134 | ||
991a56c7 RB |
1135 | if (allow_overwrite) |
1136 | flags = O_WRONLY | O_CREAT | O_TRUNC; | |
1137 | else | |
1138 | flags = O_WRONLY | O_CREAT | O_EXCL; | |
1139 | ||
1140 | fd = p_open(git_buf_cstr(&path), flags, mode); | |
dc34da6e | 1141 | |
db628072 RB |
1142 | if (fd >= 0) { |
1143 | error = p_write(fd, content, strlen(content)); | |
dc34da6e | 1144 | |
db628072 RB |
1145 | p_close(fd); |
1146 | } | |
1147 | else if (errno != EEXIST) | |
1148 | error = fd; | |
dc34da6e | 1149 | |
662880ca RB |
1150 | #ifdef GIT_WIN32 |
1151 | if (!error && hidden) { | |
1152 | if (p_hide_directory__w32(path.ptr) < 0) | |
1153 | error = -1; | |
1154 | } | |
1155 | #else | |
1156 | GIT_UNUSED(hidden); | |
1157 | #endif | |
1158 | ||
dc34da6e | 1159 | git_buf_free(&path); |
db628072 RB |
1160 | |
1161 | if (error) | |
1162 | giterr_set(GITERR_OS, | |
1163 | "Failed to initialize repository with template '%s'", file); | |
1164 | ||
1165 | return error; | |
dc34da6e RB |
1166 | } |
1167 | ||
662880ca RB |
1168 | static int repo_write_gitlink( |
1169 | const char *in_dir, const char *to_repo) | |
4b8e27c8 | 1170 | { |
662880ca RB |
1171 | int error; |
1172 | git_buf buf = GIT_BUF_INIT; | |
1173 | struct stat st; | |
1174 | ||
1175 | git_path_dirname_r(&buf, to_repo); | |
1176 | git_path_to_dir(&buf); | |
1177 | if (git_buf_oom(&buf)) | |
cb8a7961 | 1178 | return -1; |
a67a096a | 1179 | |
662880ca RB |
1180 | /* don't write gitlink to natural workdir */ |
1181 | if (git__suffixcmp(to_repo, "/" DOT_GIT "/") == 0 && | |
1182 | strcmp(in_dir, buf.ptr) == 0) | |
1183 | { | |
1184 | error = GIT_PASSTHROUGH; | |
1185 | goto cleanup; | |
1186 | } | |
1187 | ||
1188 | if ((error = git_buf_joinpath(&buf, in_dir, DOT_GIT)) < 0) | |
1189 | goto cleanup; | |
1190 | ||
1191 | if (!p_stat(buf.ptr, &st) && !S_ISREG(st.st_mode)) { | |
1192 | giterr_set(GITERR_REPOSITORY, | |
1193 | "Cannot overwrite gitlink file into path '%s'", in_dir); | |
1194 | error = GIT_EEXISTS; | |
1195 | goto cleanup; | |
1196 | } | |
1197 | ||
1198 | git_buf_clear(&buf); | |
1199 | ||
1200 | error = git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo); | |
1201 | ||
1202 | if (!error) | |
18f08264 | 1203 | error = repo_write_template(in_dir, true, DOT_GIT, 0666, true, buf.ptr); |
662880ca RB |
1204 | |
1205 | cleanup: | |
1206 | git_buf_free(&buf); | |
1207 | return error; | |
1208 | } | |
1209 | ||
ca1b6e54 RB |
1210 | static mode_t pick_dir_mode(git_repository_init_options *opts) |
1211 | { | |
1212 | if (opts->mode == GIT_REPOSITORY_INIT_SHARED_UMASK) | |
18f08264 | 1213 | return 0777; |
ca1b6e54 RB |
1214 | if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP) |
1215 | return (0775 | S_ISGID); | |
1216 | if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL) | |
1217 | return (0777 | S_ISGID); | |
1218 | return opts->mode; | |
1219 | } | |
1220 | ||
662880ca RB |
1221 | #include "repo_template.h" |
1222 | ||
1223 | static int repo_init_structure( | |
1224 | const char *repo_dir, | |
1225 | const char *work_dir, | |
1226 | git_repository_init_options *opts) | |
1227 | { | |
ca1b6e54 | 1228 | int error = 0; |
662880ca | 1229 | repo_template_item *tpl; |
ca1b6e54 RB |
1230 | bool external_tpl = |
1231 | ((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0); | |
1232 | mode_t dmode = pick_dir_mode(opts); | |
662880ca RB |
1233 | |
1234 | /* Hide the ".git" directory */ | |
17837602 | 1235 | #ifdef GIT_WIN32 |
2eb4edf5 | 1236 | if ((opts->flags & GIT_REPOSITORY_INIT__HAS_DOTGIT) != 0) { |
662880ca | 1237 | if (p_hide_directory__w32(repo_dir) < 0) { |
cb8a7961 VM |
1238 | giterr_set(GITERR_REPOSITORY, |
1239 | "Failed to mark Git repository folder as hidden"); | |
1240 | return -1; | |
1241 | } | |
17837602 | 1242 | } |
2eb4edf5 RB |
1243 | #endif |
1244 | ||
1245 | /* Create the .git gitlink if appropriate */ | |
1246 | if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 && | |
1247 | (opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD) == 0) | |
1248 | { | |
662880ca | 1249 | if (repo_write_gitlink(work_dir, repo_dir) < 0) |
cb8a7961 | 1250 | return -1; |
97769280 | 1251 | } |
1c2c7c0d | 1252 | |
ca1b6e54 RB |
1253 | /* Copy external template if requested */ |
1254 | if (external_tpl) { | |
0cd1c3bb L |
1255 | git_config *cfg = NULL; |
1256 | const char *tdir = NULL; | |
1257 | bool default_template = false; | |
a025907e L |
1258 | git_buf template_buf = GIT_BUF_INIT; |
1259 | ||
ca1b6e54 RB |
1260 | if (opts->template_path) |
1261 | tdir = opts->template_path; | |
0cd1c3bb | 1262 | else if ((error = git_config_open_default(&cfg)) >= 0) { |
ca1b6e54 | 1263 | error = git_config_get_string(&tdir, cfg, "init.templatedir"); |
ca1b6e54 | 1264 | giterr_clear(); |
0cd1c3bb L |
1265 | } |
1266 | ||
1267 | if (!tdir) { | |
1ca3e49f | 1268 | if (!(error = git_futils_find_template_dir(&template_buf))) |
417472e3 | 1269 | tdir = template_buf.ptr; |
0cd1c3bb | 1270 | default_template = true; |
662880ca | 1271 | } |
ca1b6e54 | 1272 | |
1ca3e49f RB |
1273 | if (tdir) |
1274 | error = git_futils_cp_r(tdir, repo_dir, | |
1275 | GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS | | |
1276 | GIT_CPDIR_SIMPLE_TO_MODE, dmode); | |
ca1b6e54 | 1277 | |
0cd1c3bb L |
1278 | git_buf_free(&template_buf); |
1279 | git_config_free(cfg); | |
1ca3e49f | 1280 | |
ca1b6e54 | 1281 | if (error < 0) { |
0cd1c3bb | 1282 | if (!default_template) |
ca1b6e54 RB |
1283 | return error; |
1284 | ||
1285 | /* if template was default, ignore error and use internal */ | |
1286 | giterr_clear(); | |
1287 | external_tpl = false; | |
b7b1acfd | 1288 | error = 0; |
ca1b6e54 RB |
1289 | } |
1290 | } | |
1291 | ||
1292 | /* Copy internal template | |
1293 | * - always ensure existence of dirs | |
1294 | * - only create files if no external template was specified | |
1295 | */ | |
1296 | for (tpl = repo_template; !error && tpl->path; ++tpl) { | |
1297 | if (!tpl->content) | |
1298 | error = git_futils_mkdir( | |
1299 | tpl->path, repo_dir, dmode, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD); | |
1300 | else if (!external_tpl) { | |
662880ca RB |
1301 | const char *content = tpl->content; |
1302 | ||
1303 | if (opts->description && strcmp(tpl->path, GIT_DESC_FILE) == 0) | |
1304 | content = opts->description; | |
1305 | ||
ca1b6e54 RB |
1306 | error = repo_write_template( |
1307 | repo_dir, false, tpl->path, tpl->mode, false, content); | |
662880ca | 1308 | } |
dc34da6e RB |
1309 | } |
1310 | ||
ca1b6e54 | 1311 | return error; |
4b8e27c8 | 1312 | } |
1313 | ||
3c42e4ef RB |
1314 | static int mkdir_parent(git_buf *buf, uint32_t mode, bool skip2) |
1315 | { | |
0d1b094b RB |
1316 | /* When making parent directories during repository initialization |
1317 | * don't try to set gid or grant world write access | |
1318 | */ | |
3c42e4ef | 1319 | return git_futils_mkdir( |
0d1b094b | 1320 | buf->ptr, NULL, mode & ~(S_ISGID | 0002), |
3c42e4ef RB |
1321 | GIT_MKDIR_PATH | GIT_MKDIR_VERIFY_DIR | |
1322 | (skip2 ? GIT_MKDIR_SKIP_LAST2 : GIT_MKDIR_SKIP_LAST)); | |
1323 | } | |
1324 | ||
662880ca RB |
1325 | static int repo_init_directories( |
1326 | git_buf *repo_path, | |
1327 | git_buf *wd_path, | |
1328 | const char *given_repo, | |
1329 | git_repository_init_options *opts) | |
4b8e27c8 | 1330 | { |
662880ca | 1331 | int error = 0; |
3c42e4ef | 1332 | bool is_bare, add_dotgit, has_dotgit, natural_wd; |
ca1b6e54 | 1333 | mode_t dirmode; |
932d1baf | 1334 | |
3c42e4ef RB |
1335 | /* There are three possible rules for what we are allowed to create: |
1336 | * - MKPATH means anything we need | |
1337 | * - MKDIR means just the .git directory and its parent and the workdir | |
1338 | * - Neither means only the .git directory can be created | |
1339 | * | |
1340 | * There are 5 "segments" of path that we might need to deal with: | |
1341 | * 1. The .git directory | |
1342 | * 2. The parent of the .git directory | |
1343 | * 3. Everything above the parent of the .git directory | |
1344 | * 4. The working directory (often the same as #2) | |
1345 | * 5. Everything above the working directory (often the same as #3) | |
1346 | * | |
1347 | * For all directories created, we start with the init_mode value for | |
1348 | * permissions and then strip off bits in some cases: | |
1349 | * | |
1350 | * For MKPATH, we create #3 (and #5) paths without S_ISGID or S_IWOTH | |
1351 | * For MKPATH and MKDIR, we create #2 (and #4) without S_ISGID | |
1352 | * For all rules, we create #1 using the untouched init_mode | |
1353 | */ | |
1354 | ||
662880ca | 1355 | /* set up repo path */ |
4b8e27c8 | 1356 | |
3c42e4ef RB |
1357 | is_bare = ((opts->flags & GIT_REPOSITORY_INIT_BARE) != 0); |
1358 | ||
662880ca RB |
1359 | add_dotgit = |
1360 | (opts->flags & GIT_REPOSITORY_INIT_NO_DOTGIT_DIR) == 0 && | |
3c42e4ef | 1361 | !is_bare && |
662880ca RB |
1362 | git__suffixcmp(given_repo, "/" DOT_GIT) != 0 && |
1363 | git__suffixcmp(given_repo, "/" GIT_DIR) != 0; | |
4b8e27c8 | 1364 | |
662880ca RB |
1365 | if (git_buf_joinpath(repo_path, given_repo, add_dotgit ? GIT_DIR : "") < 0) |
1366 | return -1; | |
693b23c0 | 1367 | |
662880ca RB |
1368 | has_dotgit = (git__suffixcmp(repo_path->ptr, "/" GIT_DIR) == 0); |
1369 | if (has_dotgit) | |
1370 | opts->flags |= GIT_REPOSITORY_INIT__HAS_DOTGIT; | |
1371 | ||
1372 | /* set up workdir path */ | |
1373 | ||
3c42e4ef | 1374 | if (!is_bare) { |
662880ca | 1375 | if (opts->workdir_path) { |
ca1b6e54 RB |
1376 | if (git_path_join_unrooted( |
1377 | wd_path, opts->workdir_path, repo_path->ptr, NULL) < 0) | |
1378 | return -1; | |
662880ca RB |
1379 | } else if (has_dotgit) { |
1380 | if (git_path_dirname_r(wd_path, repo_path->ptr) < 0) | |
1381 | return -1; | |
1382 | } else { | |
1383 | giterr_set(GITERR_REPOSITORY, "Cannot pick working directory" | |
1384 | " for non-bare repository that isn't a '.git' directory"); | |
1385 | return -1; | |
1386 | } | |
693b23c0 | 1387 | |
662880ca RB |
1388 | if (git_path_to_dir(wd_path) < 0) |
1389 | return -1; | |
1390 | } else { | |
1391 | git_buf_clear(wd_path); | |
1392 | } | |
1393 | ||
1394 | natural_wd = | |
1395 | has_dotgit && | |
1396 | wd_path->size > 0 && | |
1397 | wd_path->size + strlen(GIT_DIR) == repo_path->size && | |
1398 | memcmp(repo_path->ptr, wd_path->ptr, wd_path->size) == 0; | |
1399 | if (natural_wd) | |
1400 | opts->flags |= GIT_REPOSITORY_INIT__NATURAL_WD; | |
1401 | ||
662880ca RB |
1402 | /* create directories as needed / requested */ |
1403 | ||
ca1b6e54 | 1404 | dirmode = pick_dir_mode(opts); |
662880ca | 1405 | |
3c42e4ef RB |
1406 | if ((opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0) { |
1407 | /* create path #5 */ | |
1408 | if (wd_path->size > 0 && | |
1409 | (error = mkdir_parent(wd_path, dirmode, false)) < 0) | |
1410 | return error; | |
1411 | ||
1412 | /* create path #3 (if not the same as #5) */ | |
1413 | if (!natural_wd && | |
1414 | (error = mkdir_parent(repo_path, dirmode, has_dotgit)) < 0) | |
1415 | return error; | |
1416 | } | |
1417 | ||
1418 | if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 || | |
1419 | (opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0) | |
1420 | { | |
1421 | /* create path #4 */ | |
1422 | if (wd_path->size > 0 && | |
1423 | (error = git_futils_mkdir( | |
1424 | wd_path->ptr, NULL, dirmode & ~S_ISGID, | |
1425 | GIT_MKDIR_VERIFY_DIR)) < 0) | |
1426 | return error; | |
1427 | ||
1428 | /* create path #2 (if not the same as #4) */ | |
1429 | if (!natural_wd && | |
1430 | (error = git_futils_mkdir( | |
1431 | repo_path->ptr, NULL, dirmode & ~S_ISGID, | |
1432 | GIT_MKDIR_VERIFY_DIR | GIT_MKDIR_SKIP_LAST)) < 0) | |
1433 | return error; | |
662880ca | 1434 | } |
662880ca | 1435 | |
ca1b6e54 RB |
1436 | if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 || |
1437 | (opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0 || | |
1438 | has_dotgit) | |
1439 | { | |
3c42e4ef | 1440 | /* create path #1 */ |
18f08264 RB |
1441 | error = git_futils_mkdir(repo_path->ptr, NULL, dirmode, |
1442 | GIT_MKDIR_VERIFY_DIR | ((dirmode & S_ISGID) ? GIT_MKDIR_CHMOD : 0)); | |
662880ca | 1443 | } |
ca1b6e54 | 1444 | |
662880ca RB |
1445 | /* prettify both directories now that they are created */ |
1446 | ||
1447 | if (!error) { | |
1448 | error = git_path_prettify_dir(repo_path, repo_path->ptr, NULL); | |
1449 | ||
1450 | if (!error && wd_path->size > 0) | |
1451 | error = git_path_prettify_dir(wd_path, wd_path->ptr, NULL); | |
1452 | } | |
1453 | ||
1454 | return error; | |
1455 | } | |
1456 | ||
1457 | static int repo_init_create_origin(git_repository *repo, const char *url) | |
1458 | { | |
1459 | int error; | |
1460 | git_remote *remote; | |
1461 | ||
29f27599 | 1462 | if (!(error = git_remote_create(&remote, repo, GIT_REMOTE_ORIGIN, url))) { |
662880ca RB |
1463 | git_remote_free(remote); |
1464 | } | |
1465 | ||
1466 | return error; | |
1467 | } | |
1468 | ||
1469 | int git_repository_init( | |
1470 | git_repository **repo_out, const char *path, unsigned is_bare) | |
1471 | { | |
b4d13652 | 1472 | git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT; |
662880ca | 1473 | |
662880ca RB |
1474 | opts.flags = GIT_REPOSITORY_INIT_MKPATH; /* don't love this default */ |
1475 | if (is_bare) | |
1476 | opts.flags |= GIT_REPOSITORY_INIT_BARE; | |
1477 | ||
1478 | return git_repository_init_ext(repo_out, path, &opts); | |
1479 | } | |
1480 | ||
1481 | int git_repository_init_ext( | |
c9fc4a6f | 1482 | git_repository **out, |
662880ca RB |
1483 | const char *given_repo, |
1484 | git_repository_init_options *opts) | |
1485 | { | |
1486 | int error; | |
1487 | git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT; | |
1488 | ||
c9fc4a6f | 1489 | assert(out && given_repo && opts); |
662880ca | 1490 | |
c7231c45 | 1491 | GITERR_CHECK_VERSION(opts, GIT_REPOSITORY_INIT_OPTIONS_VERSION, "git_repository_init_options"); |
b4d13652 | 1492 | |
662880ca RB |
1493 | error = repo_init_directories(&repo_path, &wd_path, given_repo, opts); |
1494 | if (error < 0) | |
693b23c0 | 1495 | goto cleanup; |
662880ca RB |
1496 | |
1497 | if (valid_repository_path(&repo_path)) { | |
1498 | ||
1499 | if ((opts->flags & GIT_REPOSITORY_INIT_NO_REINIT) != 0) { | |
1500 | giterr_set(GITERR_REPOSITORY, | |
1501 | "Attempt to reinitialize '%s'", given_repo); | |
1502 | error = GIT_EEXISTS; | |
1503 | goto cleanup; | |
1504 | } | |
1505 | ||
1506 | opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT; | |
1507 | ||
ca1b6e54 | 1508 | error = repo_init_config( |
14997dc5 | 1509 | repo_path.ptr, wd_path.ptr, opts->flags, opts->mode); |
662880ca RB |
1510 | |
1511 | /* TODO: reinitialize the templates */ | |
1512 | } | |
1513 | else { | |
1514 | if (!(error = repo_init_structure( | |
5173ea92 | 1515 | repo_path.ptr, wd_path.ptr, opts)) && |
ca1b6e54 | 1516 | !(error = repo_init_config( |
14997dc5 | 1517 | repo_path.ptr, wd_path.ptr, opts->flags, opts->mode))) |
662880ca | 1518 | error = repo_init_create_head( |
5173ea92 | 1519 | repo_path.ptr, opts->initial_head); |
cb8a7961 | 1520 | } |
662880ca RB |
1521 | if (error < 0) |
1522 | goto cleanup; | |
1523 | ||
5173ea92 | 1524 | error = git_repository_open(out, repo_path.ptr); |
d2d6912e | 1525 | |
662880ca | 1526 | if (!error && opts->origin_url) |
c9fc4a6f | 1527 | error = repo_init_create_origin(*out, opts->origin_url); |
693b23c0 | 1528 | |
1529 | cleanup: | |
662880ca RB |
1530 | git_buf_free(&repo_path); |
1531 | git_buf_free(&wd_path); | |
1532 | ||
1533 | return error; | |
40c44d2f | 1534 | } |
35502d2e | 1535 | |
c682886e | 1536 | int git_repository_head_detached(git_repository *repo) |
35502d2e CMN |
1537 | { |
1538 | git_reference *ref; | |
9462c471 | 1539 | git_odb *odb = NULL; |
cb8a7961 | 1540 | int exists; |
9462c471 | 1541 | |
cb8a7961 VM |
1542 | if (git_repository_odb__weakptr(&odb, repo) < 0) |
1543 | return -1; | |
35502d2e | 1544 | |
cb8a7961 VM |
1545 | if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0) |
1546 | return -1; | |
35502d2e | 1547 | |
75abd2b9 MS |
1548 | if (git_reference_type(ref) == GIT_REF_SYMBOLIC) { |
1549 | git_reference_free(ref); | |
35502d2e | 1550 | return 0; |
75abd2b9 | 1551 | } |
35502d2e | 1552 | |
2508cc66 | 1553 | exists = git_odb_exists(odb, git_reference_target(ref)); |
75abd2b9 MS |
1554 | |
1555 | git_reference_free(ref); | |
cb8a7961 | 1556 | return exists; |
35502d2e CMN |
1557 | } |
1558 | ||
3601c4bf | 1559 | int git_repository_head(git_reference **head_out, git_repository *repo) |
35502d2e | 1560 | { |
b1a3a70e | 1561 | git_reference *head; |
8b05bea8 | 1562 | int error; |
1563 | ||
b1a3a70e | 1564 | if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0) |
1565 | return error; | |
1566 | ||
1567 | if (git_reference_type(head) == GIT_REF_OID) { | |
1568 | *head_out = head; | |
1569 | return 0; | |
1570 | } | |
1571 | ||
2508cc66 | 1572 | error = git_reference_lookup_resolved(head_out, repo, git_reference_symbolic_target(head), -1); |
b1a3a70e | 1573 | git_reference_free(head); |
8b05bea8 | 1574 | |
605da51a | 1575 | return error == GIT_ENOTFOUND ? GIT_EUNBORNBRANCH : error; |
3601c4bf | 1576 | } |
1577 | ||
605da51a | 1578 | int git_repository_head_unborn(git_repository *repo) |
3601c4bf | 1579 | { |
cb8a7961 | 1580 | git_reference *ref = NULL; |
3601c4bf | 1581 | int error; |
1582 | ||
1583 | error = git_repository_head(&ref, repo); | |
cb8a7961 | 1584 | git_reference_free(ref); |
35502d2e | 1585 | |
605da51a | 1586 | if (error == GIT_EUNBORNBRANCH) |
cb8a7961 | 1587 | return 1; |
75abd2b9 | 1588 | |
cb8a7961 VM |
1589 | if (error < 0) |
1590 | return -1; | |
1591 | ||
1592 | return 0; | |
35502d2e | 1593 | } |
e0011be3 | 1594 | |
0066955d | 1595 | static int at_least_one_cb(const char *refname, void *payload) |
41233c40 | 1596 | { |
6091457e | 1597 | GIT_UNUSED(refname); |
1598 | GIT_UNUSED(payload); | |
25e0b157 | 1599 | return GIT_PASSTHROUGH; |
6091457e | 1600 | } |
41233c40 | 1601 | |
6091457e | 1602 | static int repo_contains_no_reference(git_repository *repo) |
1603 | { | |
ec24e542 | 1604 | int error = git_reference_foreach_name(repo, &at_least_one_cb, NULL); |
0f489fb2 | 1605 | |
25e0b157 | 1606 | if (error == GIT_PASSTHROUGH) |
0f489fb2 | 1607 | return 0; |
ec24e542 | 1608 | |
e976b56d RB |
1609 | if (!error) |
1610 | return 1; | |
ec24e542 | 1611 | |
e976b56d | 1612 | return error; |
6091457e | 1613 | } |
75abd2b9 | 1614 | |
6091457e | 1615 | int git_repository_is_empty(git_repository *repo) |
1616 | { | |
1617 | git_reference *head = NULL; | |
42181836 | 1618 | int is_empty = 0; |
cb8a7961 | 1619 | |
6091457e | 1620 | if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0) |
cb8a7961 VM |
1621 | return -1; |
1622 | ||
42181836 RB |
1623 | if (git_reference_type(head) == GIT_REF_SYMBOLIC) |
1624 | is_empty = | |
1625 | (strcmp(git_reference_symbolic_target(head), | |
1626 | GIT_REFS_HEADS_DIR "master") == 0) && | |
1627 | repo_contains_no_reference(repo); | |
6091457e | 1628 | |
6091457e | 1629 | git_reference_free(head); |
42181836 RB |
1630 | |
1631 | return is_empty; | |
41233c40 VM |
1632 | } |
1633 | ||
9462c471 | 1634 | const char *git_repository_path(git_repository *repo) |
4a34b3a9 | 1635 | { |
1636 | assert(repo); | |
9462c471 VM |
1637 | return repo->path_repository; |
1638 | } | |
4a34b3a9 | 1639 | |
9462c471 VM |
1640 | const char *git_repository_workdir(git_repository *repo) |
1641 | { | |
1642 | assert(repo); | |
602ee38b | 1643 | |
9462c471 VM |
1644 | if (repo->is_bare) |
1645 | return NULL; | |
602ee38b | 1646 | |
9462c471 VM |
1647 | return repo->workdir; |
1648 | } | |
602ee38b | 1649 | |
991a56c7 RB |
1650 | int git_repository_set_workdir( |
1651 | git_repository *repo, const char *workdir, int update_gitlink) | |
9462c471 | 1652 | { |
991a56c7 | 1653 | int error = 0; |
b78fb64d | 1654 | git_buf path = GIT_BUF_INIT; |
1655 | ||
9462c471 | 1656 | assert(repo && workdir); |
602ee38b | 1657 | |
b78fb64d | 1658 | if (git_path_prettify_dir(&path, workdir, NULL) < 0) |
1659 | return -1; | |
9462c471 | 1660 | |
991a56c7 RB |
1661 | if (repo->workdir && strcmp(repo->workdir, path.ptr) == 0) |
1662 | return 0; | |
9462c471 | 1663 | |
991a56c7 RB |
1664 | if (update_gitlink) { |
1665 | git_config *config; | |
1666 | ||
1667 | if (git_repository_config__weakptr(&config, repo) < 0) | |
1668 | return -1; | |
1669 | ||
662880ca | 1670 | error = repo_write_gitlink(path.ptr, git_repository_path(repo)); |
991a56c7 RB |
1671 | |
1672 | /* passthrough error means gitlink is unnecessary */ | |
1673 | if (error == GIT_PASSTHROUGH) | |
54b2a37a | 1674 | error = git_config_delete_entry(config, "core.worktree"); |
991a56c7 RB |
1675 | else if (!error) |
1676 | error = git_config_set_string(config, "core.worktree", path.ptr); | |
1677 | ||
1678 | if (!error) | |
1679 | error = git_config_set_bool(config, "core.bare", false); | |
1680 | } | |
1681 | ||
1682 | if (!error) { | |
1683 | char *old_workdir = repo->workdir; | |
1684 | ||
1685 | repo->workdir = git_buf_detach(&path); | |
1686 | repo->is_bare = 0; | |
1687 | ||
1688 | git__free(old_workdir); | |
1689 | } | |
1690 | ||
1691 | return error; | |
4a34b3a9 | 1692 | } |
fa9bcd81 | 1693 | |
1694 | int git_repository_is_bare(git_repository *repo) | |
1695 | { | |
1696 | assert(repo); | |
1697 | return repo->is_bare; | |
1698 | } | |
f917481e RB |
1699 | |
1700 | int git_repository_head_tree(git_tree **tree, git_repository *repo) | |
1701 | { | |
5cec896a | 1702 | git_reference *head; |
1703 | git_object *obj; | |
1704 | int error; | |
f917481e | 1705 | |
5cec896a | 1706 | if ((error = git_repository_head(&head, repo)) < 0) |
1707 | return error; | |
f917481e | 1708 | |
5cec896a | 1709 | if ((error = git_reference_peel(&obj, head, GIT_OBJ_TREE)) < 0) |
1710 | goto cleanup; | |
f917481e RB |
1711 | |
1712 | *tree = (git_tree *)obj; | |
5cec896a | 1713 | |
1714 | cleanup: | |
1715 | git_reference_free(head); | |
1716 | return error; | |
f917481e | 1717 | } |
074841ec | 1718 | |
7a3bd1e7 | 1719 | int git_repository_message(git_buf *out, git_repository *repo) |
074841ec | 1720 | { |
7a3bd1e7 | 1721 | git_buf path = GIT_BUF_INIT; |
0ac349a9 | 1722 | struct stat st; |
0ac349a9 | 1723 | int error; |
074841ec | 1724 | |
7a3bd1e7 | 1725 | git_buf_sanitize(out); |
3d1c9f61 | 1726 | |
632d8b23 | 1727 | if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0) |
0ac349a9 | 1728 | return -1; |
074841ec | 1729 | |
e9ca852e | 1730 | if ((error = p_stat(git_buf_cstr(&path), &st)) < 0) { |
074841ec CMN |
1731 | if (errno == ENOENT) |
1732 | error = GIT_ENOTFOUND; | |
1a9e406c | 1733 | giterr_set(GITERR_OS, "Could not access message file"); |
0ac349a9 | 1734 | } |
074841ec | 1735 | |
7a3bd1e7 | 1736 | error = git_futils_readbuffer(out, git_buf_cstr(&path)); |
074841ec | 1737 | |
7a3bd1e7 | 1738 | git_buf_free(&path); |
e9ca852e RB |
1739 | |
1740 | return error; | |
074841ec CMN |
1741 | } |
1742 | ||
1743 | int git_repository_message_remove(git_repository *repo) | |
1744 | { | |
0ac349a9 VM |
1745 | git_buf path = GIT_BUF_INIT; |
1746 | int error; | |
074841ec | 1747 | |
632d8b23 | 1748 | if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0) |
0ac349a9 | 1749 | return -1; |
074841ec CMN |
1750 | |
1751 | error = p_unlink(git_buf_cstr(&path)); | |
1752 | git_buf_free(&path); | |
1753 | ||
1754 | return error; | |
1755 | } | |
47bfa0be RB |
1756 | |
1757 | int git_repository_hashfile( | |
0cb16fe9 L |
1758 | git_oid *out, |
1759 | git_repository *repo, | |
1760 | const char *path, | |
1761 | git_otype type, | |
1762 | const char *as_path) | |
47bfa0be RB |
1763 | { |
1764 | int error; | |
85d54812 | 1765 | git_filter_list *fl = NULL; |
b1127a30 | 1766 | git_file fd = -1; |
47bfa0be RB |
1767 | git_off_t len; |
1768 | git_buf full_path = GIT_BUF_INIT; | |
1769 | ||
a13fb55a RB |
1770 | assert(out && path && repo); /* as_path can be NULL */ |
1771 | ||
1772 | /* At some point, it would be nice if repo could be NULL to just | |
1773 | * apply filter rules defined in system and global files, but for | |
1774 | * now that is not possible because git_filters_load() needs it. | |
1775 | */ | |
47bfa0be RB |
1776 | |
1777 | error = git_path_join_unrooted( | |
1778 | &full_path, path, repo ? git_repository_workdir(repo) : NULL, NULL); | |
1779 | if (error < 0) | |
1780 | return error; | |
1781 | ||
1782 | if (!as_path) | |
1783 | as_path = path; | |
1784 | ||
1785 | /* passing empty string for "as_path" indicated --no-filters */ | |
1786 | if (strlen(as_path) > 0) { | |
4b11f25a RB |
1787 | error = git_filter_list_load( |
1788 | &fl, repo, NULL, as_path, GIT_FILTER_TO_ODB); | |
47bfa0be RB |
1789 | if (error < 0) |
1790 | return error; | |
1791 | } else { | |
1792 | error = 0; | |
1793 | } | |
1794 | ||
1795 | /* at this point, error is a count of the number of loaded filters */ | |
1796 | ||
1797 | fd = git_futils_open_ro(full_path.ptr); | |
1798 | if (fd < 0) { | |
1799 | error = fd; | |
1800 | goto cleanup; | |
1801 | } | |
1802 | ||
1803 | len = git_futils_filesize(fd); | |
1804 | if (len < 0) { | |
75050223 | 1805 | error = (int)len; |
47bfa0be RB |
1806 | goto cleanup; |
1807 | } | |
1808 | ||
1809 | if (!git__is_sizet(len)) { | |
1810 | giterr_set(GITERR_OS, "File size overflow for 32-bit systems"); | |
1811 | error = -1; | |
1812 | goto cleanup; | |
1813 | } | |
1814 | ||
85d54812 | 1815 | error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, fl); |
47bfa0be RB |
1816 | |
1817 | cleanup: | |
b1127a30 SS |
1818 | if (fd >= 0) |
1819 | p_close(fd); | |
85d54812 | 1820 | git_filter_list_free(fl); |
47bfa0be RB |
1821 | git_buf_free(&full_path); |
1822 | ||
1823 | return error; | |
1824 | } | |
1825 | ||
44af67a8 | 1826 | static bool looks_like_a_branch(const char *refname) |
1827 | { | |
1828 | return git__prefixcmp(refname, GIT_REFS_HEADS_DIR) == 0; | |
1829 | } | |
1830 | ||
1831 | int git_repository_set_head( | |
1832 | git_repository* repo, | |
1833 | const char* refname) | |
1834 | { | |
1835 | git_reference *ref, | |
1836 | *new_head = NULL; | |
1837 | int error; | |
1838 | ||
1839 | assert(repo && refname); | |
1840 | ||
1841 | error = git_reference_lookup(&ref, repo, refname); | |
1842 | if (error < 0 && error != GIT_ENOTFOUND) | |
1843 | return error; | |
1844 | ||
1845 | if (!error) { | |
1846 | if (git_reference_is_branch(ref)) | |
0b28217b | 1847 | error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, git_reference_name(ref), 1, NULL, NULL); |
44af67a8 | 1848 | else |
2508cc66 | 1849 | error = git_repository_set_head_detached(repo, git_reference_target(ref)); |
44af67a8 | 1850 | } else if (looks_like_a_branch(refname)) |
0b28217b | 1851 | error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, refname, 1, NULL, NULL); |
44af67a8 | 1852 | |
1853 | git_reference_free(ref); | |
1854 | git_reference_free(new_head); | |
1855 | return error; | |
1856 | } | |
1857 | ||
4ebe38bd | 1858 | int git_repository_set_head_detached( |
1859 | git_repository* repo, | |
1860 | const git_oid* commitish) | |
1861 | { | |
1862 | int error; | |
1863 | git_object *object, | |
1864 | *peeled = NULL; | |
1865 | git_reference *new_head = NULL; | |
1866 | ||
1867 | assert(repo && commitish); | |
1868 | ||
1869 | if ((error = git_object_lookup(&object, repo, commitish, GIT_OBJ_ANY)) < 0) | |
1870 | return error; | |
1871 | ||
1872 | if ((error = git_object_peel(&peeled, object, GIT_OBJ_COMMIT)) < 0) | |
1873 | goto cleanup; | |
1874 | ||
0b28217b | 1875 | error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), 1, NULL, NULL); |
4ebe38bd | 1876 | |
1877 | cleanup: | |
1878 | git_object_free(object); | |
1879 | git_object_free(peeled); | |
1880 | git_reference_free(new_head); | |
1881 | return error; | |
1882 | } | |
1883 | ||
3f4c3072 | 1884 | int git_repository_detach_head( |
1885 | git_repository* repo) | |
1886 | { | |
1887 | git_reference *old_head = NULL, | |
1888 | *new_head = NULL; | |
1889 | git_object *object = NULL; | |
8b05bea8 | 1890 | int error; |
3f4c3072 | 1891 | |
1892 | assert(repo); | |
1893 | ||
8b05bea8 | 1894 | if ((error = git_repository_head(&old_head, repo)) < 0) |
1895 | return error; | |
3f4c3072 | 1896 | |
2508cc66 | 1897 | if ((error = git_object_lookup(&object, repo, git_reference_target(old_head), GIT_OBJ_COMMIT)) < 0) |
3f4c3072 | 1898 | goto cleanup; |
1899 | ||
0b28217b | 1900 | error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_reference_target(old_head), 1, NULL, NULL); |
3f4c3072 | 1901 | |
1902 | cleanup: | |
1903 | git_object_free(object); | |
1904 | git_reference_free(old_head); | |
1905 | git_reference_free(new_head); | |
1906 | return error; | |
1907 | } | |
632d8b23 | 1908 | |
31966d20 | 1909 | /** |
1910 | * Loosely ported from git.git | |
1911 | * https://github.com/git/git/blob/master/contrib/completion/git-prompt.sh#L198-289 | |
1912 | */ | |
632d8b23 ET |
1913 | int git_repository_state(git_repository *repo) |
1914 | { | |
1915 | git_buf repo_path = GIT_BUF_INIT; | |
1916 | int state = GIT_REPOSITORY_STATE_NONE; | |
1917 | ||
1918 | assert(repo); | |
1919 | ||
1920 | if (git_buf_puts(&repo_path, repo->path_repository) < 0) | |
1921 | return -1; | |
1922 | ||
31966d20 | 1923 | if (git_path_contains_file(&repo_path, GIT_REBASE_MERGE_INTERACTIVE_FILE)) |
1924 | state = GIT_REPOSITORY_STATE_REBASE_INTERACTIVE; | |
1925 | else if (git_path_contains_dir(&repo_path, GIT_REBASE_MERGE_DIR)) | |
1926 | state = GIT_REPOSITORY_STATE_REBASE_MERGE; | |
1927 | else if (git_path_contains_file(&repo_path, GIT_REBASE_APPLY_REBASING_FILE)) | |
1928 | state = GIT_REPOSITORY_STATE_REBASE; | |
1929 | else if (git_path_contains_file(&repo_path, GIT_REBASE_APPLY_APPLYING_FILE)) | |
1930 | state = GIT_REPOSITORY_STATE_APPLY_MAILBOX; | |
1931 | else if (git_path_contains_dir(&repo_path, GIT_REBASE_APPLY_DIR)) | |
1932 | state = GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE; | |
1933 | else if (git_path_contains_file(&repo_path, GIT_MERGE_HEAD_FILE)) | |
632d8b23 ET |
1934 | state = GIT_REPOSITORY_STATE_MERGE; |
1935 | else if(git_path_contains_file(&repo_path, GIT_REVERT_HEAD_FILE)) | |
1936 | state = GIT_REPOSITORY_STATE_REVERT; | |
1937 | else if(git_path_contains_file(&repo_path, GIT_CHERRY_PICK_HEAD_FILE)) | |
1938 | state = GIT_REPOSITORY_STATE_CHERRY_PICK; | |
31966d20 | 1939 | else if(git_path_contains_file(&repo_path, GIT_BISECT_LOG_FILE)) |
1940 | state = GIT_REPOSITORY_STATE_BISECT; | |
632d8b23 ET |
1941 | |
1942 | git_buf_free(&repo_path); | |
1943 | return state; | |
1944 | } | |
93d8f77f | 1945 | |
bab0b9f2 ET |
1946 | int git_repository__cleanup_files(git_repository *repo, const char *files[], size_t files_len) |
1947 | { | |
1948 | git_buf path = GIT_BUF_INIT; | |
1949 | size_t i; | |
1950 | int error = 0; | |
1951 | ||
1952 | for (i = 0; i < files_len; ++i) { | |
1953 | git_buf_clear(&path); | |
1954 | ||
1955 | if ((error = git_buf_joinpath(&path, repo->path_repository, files[i])) < 0 || | |
1956 | (git_path_isfile(git_buf_cstr(&path)) && | |
1957 | (error = p_unlink(git_buf_cstr(&path))) < 0)) | |
1958 | goto done; | |
1959 | } | |
1960 | ||
1961 | done: | |
1962 | git_buf_free(&path); | |
1963 | ||
1964 | return error; | |
1965 | } | |
1966 | ||
1967 | static const char *state_files[] = { | |
1968 | GIT_MERGE_HEAD_FILE, | |
1969 | GIT_MERGE_MODE_FILE, | |
1970 | GIT_MERGE_MSG_FILE, | |
1971 | GIT_REVERT_HEAD_FILE, | |
1972 | GIT_CHERRY_PICK_HEAD_FILE, | |
1973 | }; | |
1974 | ||
1975 | int git_repository_state_cleanup(git_repository *repo) | |
1976 | { | |
1977 | assert(repo); | |
1978 | ||
1979 | return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files)); | |
1980 | } | |
1981 | ||
93d8f77f BS |
1982 | int git_repository_is_shallow(git_repository *repo) |
1983 | { | |
1984 | git_buf path = GIT_BUF_INIT; | |
1985 | struct stat st; | |
6f0b8142 | 1986 | int error; |
93d8f77f BS |
1987 | |
1988 | git_buf_joinpath(&path, repo->path_repository, "shallow"); | |
6f0b8142 BS |
1989 | error = git_path_lstat(path.ptr, &st); |
1990 | git_buf_free(&path); | |
93d8f77f | 1991 | |
6f0b8142 | 1992 | if (error == GIT_ENOTFOUND) |
93d8f77f | 1993 | return 0; |
6f0b8142 BS |
1994 | if (error < 0) |
1995 | return -1; | |
93d8f77f BS |
1996 | return st.st_size == 0 ? 0 : 1; |
1997 | } |