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