From: Junio C Hamano Date: Thu, 21 Mar 2024 21:55:13 +0000 (-0700) Subject: Merge branch 'jc/safe-implicit-bare' X-Git-Tag: v2.45.0-rc0~92 X-Git-Url: http://git.ipfire.org/?p=thirdparty%2Fgit.git;a=commitdiff_plain;h=dc97afdcb93ca683bf73b4ae2ff028c161206617;hp=-c Merge branch 'jc/safe-implicit-bare' Users with safe.bareRepository=explicit can still work from within $GIT_DIR of a seconary worktree (which resides at .git/worktrees/$name/) of the primary worktree without explicitly specifying the $GIT_DIR environment variable or the --git-dir= option. * jc/safe-implicit-bare: setup: notice more types of implicit bare repositories --- dc97afdcb93ca683bf73b4ae2ff028c161206617 diff --combined setup.c index b2d9371ddc,25d98ee6dd..0b798591c0 --- a/setup.c +++ b/setup.c @@@ -13,6 -13,7 +13,6 @@@ #include "string-list.h" #include "chdir-notify.h" #include "path.h" -#include "promisor-remote.h" #include "quote.h" #include "trace2.h" #include "worktree.h" @@@ -558,8 -559,6 +558,8 @@@ static enum extension_result handle_ext data->precious_objects = git_config_bool(var, value); return EXTENSION_OK; } else if (!strcmp(ext, "partialclone")) { + if (!value) + return config_error_nonbool(var); data->partial_clone = xstrdup(value); return EXTENSION_OK; } else if (!strcmp(ext, "worktreeconfig")) { @@@ -591,17 -590,6 +591,17 @@@ static enum extension_result handle_ext "extensions.objectformat", value); data->hash_algo = format; return EXTENSION_OK; + } else if (!strcmp(ext, "refstorage")) { + unsigned int format; + + if (!value) + return config_error_nonbool(var); + format = ref_storage_format_by_name(value); + if (format == REF_STORAGE_FORMAT_UNKNOWN) + return error(_("invalid value for '%s': '%s'"), + "extensions.refstorage", value); + data->ref_storage_format = format; + return EXTENSION_OK; } return EXTENSION_UNKNOWN; } @@@ -1243,6 -1231,32 +1243,32 @@@ static const char *allowed_bare_repo_to return NULL; } + static int is_implicit_bare_repo(const char *path) + { + /* + * what we found is a ".git" directory at the root of + * the working tree. + */ + if (ends_with_path_components(path, ".git")) + return 1; + + /* + * we are inside $GIT_DIR of a secondary worktree of a + * non-bare repository. + */ + if (strstr(path, "/.git/worktrees/")) + return 1; + + /* + * we are inside $GIT_DIR of a worktree of a non-embedded + * submodule, whose superproject is not a bare repository. + */ + if (strstr(path, "/.git/modules/")) + return 1; + + return 0; + } + /* * We cannot decide in this function whether we are in the work tree or * not, since the config can only be read _after_ this function was called. @@@ -1372,7 -1386,7 +1398,7 @@@ static enum discovery_result setup_git_ if (is_git_directory(dir->buf)) { trace2_data_string("setup", NULL, "implicit-bare-repository", dir->buf); if (get_allowed_bare_repo() == ALLOWED_BARE_REPO_EXPLICIT && - !ends_with_path_components(dir->buf, ".git")) + !is_implicit_bare_repo(dir->buf)) return GIT_DIR_DISALLOWED_BARE; if (!ensure_valid_ownership(NULL, NULL, dir->buf, report)) return GIT_DIR_INVALID_OWNERSHIP; @@@ -1577,8 -1591,6 +1603,8 @@@ const char *setup_git_directory_gently( } if (startup_info->have_repository) { repo_set_hash_algo(the_repository, repo_fmt.hash_algo); + repo_set_ref_storage_format(the_repository, + repo_fmt.ref_storage_format); the_repository->repository_format_worktree_config = repo_fmt.worktree_config; /* take ownership of repo_fmt.partial_clone */ @@@ -1672,8 -1684,6 +1698,8 @@@ void check_repository_format(struct rep check_repository_format_gently(get_git_dir(), fmt, NULL); startup_info->have_repository = 1; repo_set_hash_algo(the_repository, fmt->hash_algo); + repo_set_ref_storage_format(the_repository, + fmt->ref_storage_format); the_repository->repository_format_worktree_config = fmt->worktree_config; the_repository->repository_format_partial_clone = @@@ -1882,22 -1892,12 +1908,22 @@@ static int needs_work_tree_config(cons return 1; } -void initialize_repository_version(int hash_algo, int reinit) +void initialize_repository_version(int hash_algo, + unsigned int ref_storage_format, + int reinit) { char repo_version_string[10]; int repo_version = GIT_REPO_VERSION; - if (hash_algo != GIT_HASH_SHA1) + /* + * Note that we initialize the repository version to 1 when the ref + * storage format is unknown. This is on purpose so that we can add the + * correct object format to the config during git-clone(1). The format + * version will get adjusted by git-clone(1) once it has learned about + * the remote repository's format. + */ + if (hash_algo != GIT_HASH_SHA1 || + ref_storage_format != REF_STORAGE_FORMAT_FILES) repo_version = GIT_REPO_VERSION_READ; /* This forces creation of new config file */ @@@ -1905,76 -1905,28 +1931,76 @@@ "%d", repo_version); git_config_set("core.repositoryformatversion", repo_version_string); - if (hash_algo != GIT_HASH_SHA1) + if (hash_algo != GIT_HASH_SHA1 && hash_algo != GIT_HASH_UNKNOWN) git_config_set("extensions.objectformat", hash_algos[hash_algo].name); else if (reinit) git_config_set_gently("extensions.objectformat", NULL); + + if (ref_storage_format != REF_STORAGE_FORMAT_FILES) + git_config_set("extensions.refstorage", + ref_storage_format_to_name(ref_storage_format)); +} + +static int is_reinit(void) +{ + struct strbuf buf = STRBUF_INIT; + char junk[2]; + int ret; + + git_path_buf(&buf, "HEAD"); + ret = !access(buf.buf, R_OK) || readlink(buf.buf, junk, sizeof(junk) - 1) != -1; + strbuf_release(&buf); + return ret; +} + +void create_reference_database(unsigned int ref_storage_format, + const char *initial_branch, int quiet) +{ + struct strbuf err = STRBUF_INIT; + int reinit = is_reinit(); + + repo_set_ref_storage_format(the_repository, ref_storage_format); + if (refs_init_db(get_main_ref_store(the_repository), 0, &err)) + die("failed to set up refs db: %s", err.buf); + + /* + * Point the HEAD symref to the initial branch with if HEAD does + * not yet exist. + */ + if (!reinit) { + char *ref; + + if (!initial_branch) + initial_branch = git_default_branch_name(quiet); + + ref = xstrfmt("refs/heads/%s", initial_branch); + if (check_refname_format(ref, 0) < 0) + die(_("invalid initial branch name: '%s'"), + initial_branch); + + if (create_symref("HEAD", ref, NULL) < 0) + exit(1); + free(ref); + } + + if (reinit && initial_branch) + warning(_("re-init: ignored --initial-branch=%s"), + initial_branch); + + strbuf_release(&err); } static int create_default_files(const char *template_path, const char *original_git_dir, - const char *initial_branch, const struct repository_format *fmt, - int prev_bare_repository, - int init_shared_repository, - int quiet) + int init_shared_repository) { struct stat st1; struct strbuf buf = STRBUF_INIT; char *path; - char junk[2]; int reinit; int filemode; - struct strbuf err = STRBUF_INIT; const char *init_template_dir = NULL; const char *work_tree = get_git_work_tree(); @@@ -1994,16 -1946,40 +2020,16 @@@ reset_shared_repository(); git_config(git_default_config, NULL); + reinit = is_reinit(); + /* * We must make sure command-line options continue to override any * values we might have just re-read from the config. */ if (init_shared_repository != -1) set_shared_repository(init_shared_repository); - /* - * TODO: heed core.bare from config file in templates if no - * command-line override given - */ - is_bare_repository_cfg = prev_bare_repository || !work_tree; - /* TODO (continued): - * - * Unfortunately, the line above is equivalent to - * is_bare_repository_cfg = !work_tree; - * which ignores the config entirely even if no `--[no-]bare` - * command line option was present. - * - * To see why, note that before this function, there was this call: - * prev_bare_repository = is_bare_repository() - * expanding the right hand side: - * = is_bare_repository_cfg && !get_git_work_tree() - * = is_bare_repository_cfg && !work_tree - * note that the last simplification above is valid because nothing - * calls repo_init() or set_git_work_tree() between any of the - * relevant calls in the code, and thus the !get_git_work_tree() - * calls will return the same result each time. So, what we are - * interested in computing is the right hand side of the line of - * code just above this comment: - * prev_bare_repository || !work_tree - * = is_bare_repository_cfg && !work_tree || !work_tree - * = !work_tree - * because "A && !B || !B == !B" for all boolean values of A & B. - */ + + is_bare_repository_cfg = !work_tree; /* * We would have created the above under user's umask -- under @@@ -2013,7 -1989,40 +2039,7 @@@ adjust_shared_perm(get_git_dir()); } - /* - * We need to create a "refs" dir in any case so that older - * versions of git can tell that this is a repository. - */ - safe_create_dir(git_path("refs"), 1); - adjust_shared_perm(git_path("refs")); - - if (refs_init_db(&err)) - die("failed to set up refs db: %s", err.buf); - - /* - * Point the HEAD symref to the initial branch with if HEAD does - * not yet exist. - */ - path = git_path_buf(&buf, "HEAD"); - reinit = (!access(path, R_OK) - || readlink(path, junk, sizeof(junk)-1) != -1); - if (!reinit) { - char *ref; - - if (!initial_branch) - initial_branch = git_default_branch_name(quiet); - - ref = xstrfmt("refs/heads/%s", initial_branch); - if (check_refname_format(ref, 0) < 0) - die(_("invalid initial branch name: '%s'"), - initial_branch); - - if (create_symref("HEAD", ref, NULL) < 0) - exit(1); - free(ref); - } - - initialize_repository_version(fmt->hash_algo, 0); + initialize_repository_version(fmt->hash_algo, fmt->ref_storage_format, 0); /* Check filemode trustability */ path = git_path_buf(&buf, "config"); @@@ -2126,35 -2135,15 +2152,35 @@@ static void validate_hash_algorithm(str } } +static void validate_ref_storage_format(struct repository_format *repo_fmt, + unsigned int format) +{ + const char *name = getenv("GIT_DEFAULT_REF_FORMAT"); + + if (repo_fmt->version >= 0 && + format != REF_STORAGE_FORMAT_UNKNOWN && + format != repo_fmt->ref_storage_format) { + die(_("attempt to reinitialize repository with different reference storage format")); + } else if (format != REF_STORAGE_FORMAT_UNKNOWN) { + repo_fmt->ref_storage_format = format; + } else if (name) { + format = ref_storage_format_by_name(name); + if (format == REF_STORAGE_FORMAT_UNKNOWN) + die(_("unknown ref storage format '%s'"), name); + repo_fmt->ref_storage_format = format; + } +} + int init_db(const char *git_dir, const char *real_git_dir, - const char *template_dir, int hash, const char *initial_branch, + const char *template_dir, int hash, + unsigned int ref_storage_format, + const char *initial_branch, int init_shared_repository, unsigned int flags) { int reinit; int exist_ok = flags & INIT_DB_EXIST_OK; char *original_git_dir = real_pathdup(git_dir, 1); struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT; - int prev_bare_repository; if (real_git_dir) { struct stat st; @@@ -2180,6 -2169,7 +2206,6 @@@ safe_create_dir(git_dir, 0); - prev_bare_repository = is_bare_repository(); /* Check to see if the repository version is right. * Note that a newly created repository does not have @@@ -2189,21 -2179,16 +2215,21 @@@ check_repository_format(&repo_fmt); validate_hash_algorithm(&repo_fmt, hash); + validate_ref_storage_format(&repo_fmt, ref_storage_format); reinit = create_default_files(template_dir, original_git_dir, - initial_branch, &repo_fmt, - prev_bare_repository, - init_shared_repository, - flags & INIT_DB_QUIET); - if (reinit && initial_branch) - warning(_("re-init: ignored --initial-branch=%s"), - initial_branch); + &repo_fmt, init_shared_repository); + + /* + * Now that we have set up both the hash algorithm and the ref storage + * format we can update the repository's settings accordingly. + */ + repo_set_hash_algo(the_repository, repo_fmt.hash_algo); + repo_set_ref_storage_format(the_repository, repo_fmt.ref_storage_format); + if (!(flags & INIT_DB_SKIP_REFDB)) + create_reference_database(repo_fmt.ref_storage_format, + initial_branch, flags & INIT_DB_QUIET); create_object_directory(); if (get_shared_repository()) {