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