diff options
Diffstat (limited to 'src/repository.c')
-rw-r--r-- | src/repository.c | 328 |
1 files changed, 173 insertions, 155 deletions
diff --git a/src/repository.c b/src/repository.c index dcc02e4fb..e8d50aed3 100644 --- a/src/repository.c +++ b/src/repository.c @@ -4,7 +4,6 @@ * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ -#include <stdarg.h> #include <ctype.h> #include "git2/object.h" @@ -17,6 +16,7 @@ #include "tag.h" #include "blob.h" #include "fileops.h" +#include "sysdir.h" #include "filebuf.h" #include "index.h" #include "config.h" @@ -27,6 +27,10 @@ #include "merge.h" #include "diff_driver.h" +#ifdef GIT_WIN32 +# include "win32/w32_util.h" +#endif + #define GIT_FILE_CONTENT_PREFIX "gitdir:" #define GIT_BRANCH_MASTER "master" @@ -93,6 +97,7 @@ void git_repository__cleanup(git_repository *repo) git_cache_clear(&repo->objects); git_attr_cache_flush(repo); + git_submodule_cache_free(repo); set_config(repo, NULL); set_index(repo, NULL); @@ -108,7 +113,6 @@ void git_repository_free(git_repository *repo) git_repository__cleanup(repo); git_cache_free(&repo->objects); - git_submodule_config_free(repo); git_diff_driver_registry_free(repo->diff_drivers); repo->diff_drivers = NULL; @@ -165,13 +169,9 @@ int git_repository_new(git_repository **out) return 0; } -static int load_config_data(git_repository *repo) +static int load_config_data(git_repository *repo, const git_config *config) { int is_bare; - git_config *config; - - if (git_repository_config__weakptr(&config, repo) < 0) - return -1; /* Try to figure out if it's bare, default to non-bare if it's not set */ if (git_config_get_bool(&is_bare, config, "core.bare") < 0) @@ -182,43 +182,37 @@ static int load_config_data(git_repository *repo) return 0; } -static int load_workdir(git_repository *repo, git_buf *parent_path) +static int load_workdir(git_repository *repo, git_config *config, git_buf *parent_path) { int error; - git_config *config; - const char *worktree; - git_buf worktree_buf = GIT_BUF_INIT; + const git_config_entry *ce; + git_buf worktree = GIT_BUF_INIT; if (repo->is_bare) return 0; - if (git_repository_config__weakptr(&config, repo) < 0) - return -1; + if ((error = git_config__lookup_entry( + &ce, config, "core.worktree", false)) < 0) + return error; - error = git_config_get_string(&worktree, config, "core.worktree"); - if (!error && worktree != NULL) { - error = git_path_prettify_dir( - &worktree_buf, worktree, repo->path_repository); - if (error < 0) + if (ce && ce->value) { + if ((error = git_path_prettify_dir( + &worktree, ce->value, repo->path_repository)) < 0) return error; - repo->workdir = git_buf_detach(&worktree_buf); + + repo->workdir = git_buf_detach(&worktree); } - else if (error != GIT_ENOTFOUND) - return error; + else if (parent_path && git_path_isdir(parent_path->ptr)) + repo->workdir = git_buf_detach(parent_path); else { - giterr_clear(); + if (git_path_dirname_r(&worktree, repo->path_repository) < 0 || + git_path_to_dir(&worktree) < 0) + return -1; - if (parent_path && git_path_isdir(parent_path->ptr)) - repo->workdir = git_buf_detach(parent_path); - else { - git_path_dirname_r(&worktree_buf, repo->path_repository); - git_path_to_dir(&worktree_buf); - repo->workdir = git_buf_detach(&worktree_buf); - } + repo->workdir = git_buf_detach(&worktree); } GITERR_CHECK_ALLOC(repo->workdir); - return 0; } @@ -291,16 +285,20 @@ static int read_gitfile(git_buf *path_out, const char *file_path) return -1; git_buf_rtrim(&file); + /* apparently on Windows, some people use backslashes in paths */ + git_path_mkposix(file.ptr); if (git_buf_len(&file) <= prefix_len || memcmp(git_buf_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0) { - giterr_set(GITERR_REPOSITORY, "The `.git` file at '%s' is malformed", file_path); + giterr_set(GITERR_REPOSITORY, + "The `.git` file at '%s' is malformed", file_path); error = -1; } else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) { const char *gitlink = git_buf_cstr(&file) + prefix_len; while (*gitlink && git__isspace(*gitlink)) gitlink++; + error = git_path_prettify_dir( path_out, gitlink, git_buf_cstr(path_out)); } @@ -461,16 +459,22 @@ int git_repository_open_ext( if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0) repo->is_bare = 1; - else if ((error = load_config_data(repo)) < 0 || - (error = load_workdir(repo, &parent)) < 0) - { - git_repository_free(repo); - return error; + else { + git_config *config = NULL; + + if ((error = git_repository_config_snapshot(&config, repo)) < 0 || + (error = load_config_data(repo, config)) < 0 || + (error = load_workdir(repo, config, &parent)) < 0) + git_repository_free(repo); + + git_config_free(config); } + if (!error) + *repo_ptr = repo; git_buf_free(&parent); - *repo_ptr = repo; - return 0; + + return error; } int git_repository_open(git_repository **repo_out, const char *path) @@ -493,34 +497,18 @@ int git_repository_wrap_odb(git_repository **repo_out, git_odb *odb) } int git_repository_discover( - char *repository_path, - size_t size, + git_buf *out, const char *start_path, int across_fs, const char *ceiling_dirs) { - git_buf path = GIT_BUF_INIT; uint32_t flags = across_fs ? GIT_REPOSITORY_OPEN_CROSS_FS : 0; - int error; - - assert(start_path && repository_path && size > 0); - - *repository_path = '\0'; - if ((error = find_repo(&path, NULL, start_path, flags, ceiling_dirs)) < 0) - return error != GIT_ENOTFOUND ? -1 : error; + assert(start_path); - if (size < (size_t)(path.size + 1)) { - giterr_set(GITERR_REPOSITORY, - "The given buffer is too small to store the discovered path"); - git_buf_free(&path); - return -1; - } + git_buf_sanitize(out); - /* success: we discovered a repository */ - git_buf_copy_cstr(repository_path, size, &path); - git_buf_free(&path); - return 0; + return find_repo(out, NULL, start_path, flags, ceiling_dirs); } static int load_config( @@ -596,9 +584,9 @@ int git_repository_config__weakptr(git_config **out, git_repository *repo) git_buf system_buf = GIT_BUF_INIT; git_config *config; - git_config_find_global_r(&global_buf); - git_config_find_xdg_r(&xdg_buf); - git_config_find_system_r(&system_buf); + git_config_find_global(&global_buf); + git_config_find_xdg(&xdg_buf); + git_config_find_system(&system_buf); /* If there is no global file, open a backend for it anyway */ if (git_buf_len(&global_buf) == 0) @@ -637,6 +625,17 @@ int git_repository_config(git_config **out, git_repository *repo) return 0; } +int git_repository_config_snapshot(git_config **out, git_repository *repo) +{ + int error; + git_config *weak; + + if ((error = git_repository_config__weakptr(&weak, repo)) < 0) + return error; + + return git_config_snapshot(out, weak); +} + void git_repository_set_config(git_repository *repo, git_config *config) { assert(repo && config); @@ -890,60 +889,6 @@ static bool are_symlinks_supported(const char *wd_path) return symlinks_supported; } -#ifdef GIT_USE_ICONV - -static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D.XXXXXX"; -static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D.XXXXXX"; - -/* Check if the platform is decomposing unicode data for us. We will - * emulate core Git and prefer to use precomposed unicode data internally - * on these platforms, composing the decomposed unicode on the fly. - * - * This mainly happens on the Mac where HDFS stores filenames as - * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will - * return decomposed unicode from readdir() even when the actual - * filesystem is storing precomposed unicode. - */ -static bool does_fs_decompose_unicode_paths(const char *wd_path) -{ - git_buf path = GIT_BUF_INIT; - int fd; - bool found_decomposed = false; - char tmp[6]; - - /* Create a file using a precomposed path and then try to find it - * using the decomposed name. If the lookup fails, then we will mark - * that we should precompose unicode for this repository. - */ - if (git_buf_joinpath(&path, wd_path, nfc_file) < 0 || - (fd = p_mkstemp(path.ptr)) < 0) - goto done; - p_close(fd); - - /* record trailing digits generated by mkstemp */ - memcpy(tmp, path.ptr + path.size - sizeof(tmp), sizeof(tmp)); - - /* try to look up as NFD path */ - if (git_buf_joinpath(&path, wd_path, nfd_file) < 0) - goto done; - memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); - - found_decomposed = git_path_exists(path.ptr); - - /* remove temporary file (using original precomposed path) */ - if (git_buf_joinpath(&path, wd_path, nfc_file) < 0) - goto done; - memcpy(path.ptr + path.size - sizeof(tmp), tmp, sizeof(tmp)); - - (void)p_unlink(path.ptr); - -done: - git_buf_free(&path); - return found_decomposed; -} - -#endif - static int create_empty_file(const char *path, mode_t mode) { int fd; @@ -1034,8 +979,9 @@ static int repo_init_fs_configs( #ifdef GIT_USE_ICONV if ((error = git_config_set_bool( cfg, "core.precomposeunicode", - does_fs_decompose_unicode_paths(work_dir))) < 0) + git_path_does_fs_decompose_unicode(work_dir))) < 0) return error; + /* on non-iconv platforms, don't even set core.precomposeunicode */ #endif return 0; @@ -1163,7 +1109,7 @@ static int repo_write_template( #ifdef GIT_WIN32 if (!error && hidden) { - if (p_hide_directory__w32(path.ptr) < 0) + if (git_win32__sethidden(path.ptr) < 0) error = -1; } #else @@ -1244,12 +1190,13 @@ static int repo_init_structure( bool external_tpl = ((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0); mode_t dmode = pick_dir_mode(opts); + bool chmod = opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK; /* Hide the ".git" directory */ #ifdef GIT_WIN32 if ((opts->flags & GIT_REPOSITORY_INIT__HAS_DOTGIT) != 0) { - if (p_hide_directory__w32(repo_dir) < 0) { - giterr_set(GITERR_REPOSITORY, + if (git_win32__sethidden(repo_dir) < 0) { + giterr_set(GITERR_OS, "Failed to mark Git repository folder as hidden"); return -1; } @@ -1279,15 +1226,17 @@ static int repo_init_structure( } if (!tdir) { - if (!(error = git_futils_find_template_dir(&template_buf))) + if (!(error = git_sysdir_find_template_dir(&template_buf))) tdir = template_buf.ptr; default_template = true; } - if (tdir) - error = git_futils_cp_r(tdir, repo_dir, - GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS | - GIT_CPDIR_SIMPLE_TO_MODE, dmode); + if (tdir) { + uint32_t cpflags = GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_SIMPLE_TO_MODE; + if (opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK) + cpflags |= GIT_CPDIR_CHMOD_DIRS; + error = git_futils_cp_r(tdir, repo_dir, cpflags, dmode); + } git_buf_free(&template_buf); git_config_free(cfg); @@ -1308,9 +1257,14 @@ static int repo_init_structure( * - only create files if no external template was specified */ for (tpl = repo_template; !error && tpl->path; ++tpl) { - if (!tpl->content) + if (!tpl->content) { + uint32_t mkdir_flags = GIT_MKDIR_PATH; + if (chmod) + mkdir_flags |= GIT_MKDIR_CHMOD; + error = git_futils_mkdir( - tpl->path, repo_dir, dmode, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD); + tpl->path, repo_dir, dmode, mkdir_flags); + } else if (!external_tpl) { const char *content = tpl->content; @@ -1610,15 +1564,14 @@ static int at_least_one_cb(const char *refname, void *payload) { GIT_UNUSED(refname); GIT_UNUSED(payload); - - return GIT_EUSER; + return GIT_PASSTHROUGH; } static int repo_contains_no_reference(git_repository *repo) { int error = git_reference_foreach_name(repo, &at_least_one_cb, NULL); - if (error == GIT_EUSER) + if (error == GIT_PASSTHROUGH) return 0; if (!error) @@ -1731,14 +1684,13 @@ cleanup: return error; } -int git_repository_message(char *buffer, size_t len, git_repository *repo) +int git_repository_message(git_buf *out, git_repository *repo) { - git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT; + git_buf path = GIT_BUF_INIT; struct stat st; int error; - if (buffer != NULL) - *buffer = '\0'; + git_buf_sanitize(out); if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0) return -1; @@ -1747,17 +1699,11 @@ int git_repository_message(char *buffer, size_t len, git_repository *repo) if (errno == ENOENT) error = GIT_ENOTFOUND; giterr_set(GITERR_OS, "Could not access message file"); - } - else if (buffer != NULL) { - error = git_futils_readbuffer(&buf, git_buf_cstr(&path)); - git_buf_copy_cstr(buffer, len, &buf); + } else { + error = git_futils_readbuffer(out, git_buf_cstr(&path)); } git_buf_free(&path); - git_buf_free(&buf); - - if (!error) - error = (int)st.st_size + 1; /* add 1 for NUL byte */ return error; } @@ -1807,7 +1753,8 @@ int git_repository_hashfile( /* passing empty string for "as_path" indicated --no-filters */ if (strlen(as_path) > 0) { error = git_filter_list_load( - &fl, repo, NULL, as_path, GIT_FILTER_TO_ODB); + &fl, repo, NULL, as_path, + GIT_FILTER_TO_ODB, GIT_FILTER_OPT_DEFAULT); if (error < 0) return error; } else { @@ -1852,7 +1799,9 @@ static bool looks_like_a_branch(const char *refname) int git_repository_set_head( git_repository* repo, - const char* refname) + const char* refname, + const git_signature *signature, + const char *log_message) { git_reference *ref, *new_head = NULL; @@ -1865,12 +1814,17 @@ int git_repository_set_head( return error; if (!error) { - if (git_reference_is_branch(ref)) - error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, git_reference_name(ref), 1); - else - error = git_repository_set_head_detached(repo, git_reference_target(ref)); - } else if (looks_like_a_branch(refname)) - error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, refname, 1); + if (git_reference_is_branch(ref)) { + error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, + git_reference_name(ref), true, signature, log_message); + } else { + error = git_repository_set_head_detached(repo, git_reference_target(ref), + signature, log_message); + } + } else if (looks_like_a_branch(refname)) { + error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, refname, + true, signature, log_message); + } git_reference_free(ref); git_reference_free(new_head); @@ -1879,7 +1833,9 @@ int git_repository_set_head( int git_repository_set_head_detached( git_repository* repo, - const git_oid* commitish) + const git_oid* commitish, + const git_signature *signature, + const char *log_message) { int error; git_object *object, @@ -1894,7 +1850,7 @@ int git_repository_set_head_detached( if ((error = git_object_peel(&peeled, object, GIT_OBJ_COMMIT)) < 0) goto cleanup; - error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), 1); + error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), true, signature, log_message); cleanup: git_object_free(object); @@ -1904,7 +1860,9 @@ cleanup: } int git_repository_detach_head( - git_repository* repo) + git_repository* repo, + const git_signature *signature, + const char *reflog_message) { git_reference *old_head = NULL, *new_head = NULL; @@ -1919,7 +1877,8 @@ int git_repository_detach_head( if ((error = git_object_lookup(&object, repo, git_reference_target(old_head), GIT_OBJ_COMMIT)) < 0) goto cleanup; - error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_reference_target(old_head), 1); + error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_reference_target(old_head), + 1, signature, reflog_message); cleanup: git_object_free(object); @@ -1965,6 +1924,53 @@ int git_repository_state(git_repository *repo) return state; } +int git_repository__cleanup_files( + git_repository *repo, const char *files[], size_t files_len) +{ + git_buf buf = GIT_BUF_INIT; + size_t i; + int error; + + for (error = 0, i = 0; !error && i < files_len; ++i) { + const char *path; + + if (git_buf_joinpath(&buf, repo->path_repository, files[i]) < 0) + return -1; + + path = git_buf_cstr(&buf); + + if (git_path_isfile(path)) { + error = p_unlink(path); + } else if (git_path_isdir(path)) { + error = git_futils_rmdir_r(path, NULL, + GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS); + } + + git_buf_clear(&buf); + } + + git_buf_free(&buf); + return error; +} + +static const char *state_files[] = { + GIT_MERGE_HEAD_FILE, + GIT_MERGE_MODE_FILE, + GIT_MERGE_MSG_FILE, + GIT_REVERT_HEAD_FILE, + GIT_CHERRY_PICK_HEAD_FILE, + GIT_BISECT_LOG_FILE, + GIT_REBASE_MERGE_DIR, + GIT_REBASE_APPLY_DIR, +}; + +int git_repository_state_cleanup(git_repository *repo) +{ + assert(repo); + + return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files)); +} + int git_repository_is_shallow(git_repository *repo) { git_buf path = GIT_BUF_INIT; @@ -1975,9 +1981,21 @@ int git_repository_is_shallow(git_repository *repo) error = git_path_lstat(path.ptr, &st); git_buf_free(&path); - if (error == GIT_ENOTFOUND) + if (error == GIT_ENOTFOUND) { + giterr_clear(); return 0; + } + if (error < 0) - return -1; + return error; return st.st_size == 0 ? 0 : 1; } + +int git_repository_init_init_options( + git_repository_init_options *opts, unsigned int version) +{ + GIT_INIT_STRUCTURE_FROM_TEMPLATE( + opts, version, git_repository_init_options, + GIT_REPOSITORY_INIT_OPTIONS_INIT); + return 0; +} |