diff --git a/Documentation/config/extensions.adoc b/Documentation/config/extensions.adoc index 532456644b770e7f9a8d15b5eaac9eef143a5315..ab4d336ad941e14a5936eae90b5b5c1cf5e585ef 100644 --- a/Documentation/config/extensions.adoc +++ b/Documentation/config/extensions.adoc @@ -57,10 +57,25 @@ For historical reasons, this extension is respected regardless of the `core.repositoryFormatVersion` setting. refStorage::: - Specify the ref storage format to use. The acceptable values are: + Specify the ref storage format and location to use. The value can be + either a format name or a URI: + -- +* A format name alone (e.g., `reftable` or `files`) uses the default + location (the repository's common directory). + +* A URI format `://` explicitly specifies both the + format and location (e.g., `reftable:///foo/bar`). + +Supported format names are: ++ include::../ref-storage-format.adoc[] ++ +The location part of the URI is treated as an opaque string and interpreted by +the reference backend itself. For the `files` and `reftable` backends, this is +a filesystem path. Future backends may support other location schemes (e.g., +`postgres://127.0.0.1:5432?database=myrepo`). Currently custom locations do not +work with worktrees. -- + Note that this setting should only be set by linkgit:git-init[1] or diff --git a/Documentation/git.adoc b/Documentation/git.adoc index ce099e78b8023e90353ffda4fae43a5469fad2b9..ed3191e8f6983d422434e7ab601a77b519d31472 100644 --- a/Documentation/git.adoc +++ b/Documentation/git.adoc @@ -584,6 +584,11 @@ double-quotes and respecting backslash escapes. E.g., the value repositories will be set to this value. The default is "files". See `--ref-format` in linkgit:git-init[1]. +`GIT_REFERENCE_BACKEND`:: + Specify which reference backend to be used along with its URI. + See `extensions.refStorage` option in linkgit:git-config[1] for more + description. Overrides the config variable when used. + Git Commits ~~~~~~~~~~~ `GIT_AUTHOR_NAME`:: diff --git a/environment.h b/environment.h index 51898c99cd1e451a47c1a4aae32869cfbddbce45..73c3137fe12cf494ba247acf3f7b51903a18f337 100644 --- a/environment.h +++ b/environment.h @@ -42,6 +42,7 @@ #define GIT_OPTIONAL_LOCKS_ENVIRONMENT "GIT_OPTIONAL_LOCKS" #define GIT_TEXT_DOMAIN_DIR_ENVIRONMENT "GIT_TEXTDOMAINDIR" #define GIT_ATTR_SOURCE_ENVIRONMENT "GIT_ATTR_SOURCE" +#define GIT_REFERENCE_BACKEND_ENVIRONMENT "GIT_REFERENCE_BACKEND" /* * Environment variable used to propagate the --no-advice global option to the diff --git a/refs.c b/refs.c index 5583f6e09d7c76c132b91d3211d09d0838180c03..a7978bbc93738017f365b51381b1648d1f6ec3c8 100644 --- a/refs.c +++ b/refs.c @@ -2193,15 +2193,21 @@ void ref_store_release(struct ref_store *ref_store) struct ref_store *get_main_ref_store(struct repository *r) { + const char *location; + if (r->refs_private) return r->refs_private; if (!r->gitdir) BUG("attempting to get main_ref_store outside of repository"); + location = r->ref_storage_location; + if (!location) + location = r->gitdir; + r->refs_private = ref_store_init(r, r->ref_storage_format, - r->gitdir, REF_STORE_ALL_CAPS); - r->refs_private = maybe_debug_wrap_ref_store(r->gitdir, r->refs_private); + location, REF_STORE_ALL_CAPS); + r->refs_private = maybe_debug_wrap_ref_store(location, r->refs_private); return r->refs_private; } diff --git a/repository.c b/repository.c index 6aaa7ba00869bf6aa0450f3fbdf27590f7439228..7a3d97d35b2f25b2e4345a305c449546751812b3 100644 --- a/repository.c +++ b/repository.c @@ -204,9 +204,12 @@ void repo_set_compat_hash_algo(struct repository *repo, int algo) } void repo_set_ref_storage_format(struct repository *repo, - enum ref_storage_format format) + enum ref_storage_format format, + const char *location) { repo->ref_storage_format = format; + if (location) + repo->ref_storage_location = xstrdup(location); } /* @@ -288,7 +291,8 @@ int repo_init(struct repository *repo, repo_set_hash_algo(repo, format.hash_algo); repo_set_compat_hash_algo(repo, format.compat_hash_algo); - repo_set_ref_storage_format(repo, format.ref_storage_format); + repo_set_ref_storage_format(repo, format.ref_storage_format, + format.ref_storage_location); repo->repository_format_worktree_config = format.worktree_config; repo->repository_format_relative_worktrees = format.relative_worktrees; repo->repository_format_precious_objects = format.precious_objects; @@ -381,6 +385,7 @@ void repo_clear(struct repository *repo) FREE_AND_NULL(repo->index_file); FREE_AND_NULL(repo->worktree); FREE_AND_NULL(repo->submodule_prefix); + FREE_AND_NULL(repo->ref_storage_location); odb_clear(repo->objects); FREE_AND_NULL(repo->objects); diff --git a/repository.h b/repository.h index 5808a5d610846a0e42233f66e56dcbcebbd3ecd0..430ffc4b0357090d14370f145bb39945fc638262 100644 --- a/repository.h +++ b/repository.h @@ -143,6 +143,8 @@ struct repository { /* Repository's reference storage format, as serialized on disk. */ enum ref_storage_format ref_storage_format; + /* Reference storage location information as needed for the backend. */ + char *ref_storage_location; /* A unique-id for tracing purposes. */ int trace2_repo_id; @@ -196,7 +198,8 @@ void repo_set_worktree(struct repository *repo, const char *path); void repo_set_hash_algo(struct repository *repo, int algo); void repo_set_compat_hash_algo(struct repository *repo, int compat_algo); void repo_set_ref_storage_format(struct repository *repo, - enum ref_storage_format format); + enum ref_storage_format format, + const char *location); void initialize_repository(struct repository *repo); RESULT_MUST_BE_USED int repo_init(struct repository *r, const char *gitdir, const char *worktree); diff --git a/setup.c b/setup.c index 7086741e6c2d1f5c73e484b76e1a1272f7d0697d..00251bf0de95242426ef3f9fb1cf9eda302ccad8 100644 --- a/setup.c +++ b/setup.c @@ -224,8 +224,8 @@ static void NORETURN die_verify_filename(struct repository *r, /* ... or fall back the most general message. */ die(_("ambiguous argument '%s': unknown revision or path not in the working tree.\n" "Use '--' to separate paths from revisions, like this:\n" - "'git [...] -- [...]'"), arg); - + "'git [...] -- [...]'"), + arg); } /* @@ -309,7 +309,8 @@ void verify_non_filename(const char *prefix, const char *arg) return; die(_("ambiguous argument '%s': both revision and filename\n" "Use '--' to separate paths from revisions, like this:\n" - "'git [...] -- [...]'"), arg); + "'git [...] -- [...]'"), + arg); } int get_common_dir(struct strbuf *sb, const char *gitdir) @@ -366,7 +367,7 @@ static int validate_headref(const char *path) /* Make sure it is a "refs/.." symlink */ if (S_ISLNK(st.st_mode)) { - len = readlink(path, buffer, sizeof(buffer)-1); + len = readlink(path, buffer, sizeof(buffer) - 1); if (len >= 5 && !memcmp("refs/", buffer, 5)) return 0; return -1; @@ -378,7 +379,7 @@ static int validate_headref(const char *path) fd = open(path, O_RDONLY); if (fd < 0) return -1; - len = read_in_full(fd, buffer, sizeof(buffer)-1); + len = read_in_full(fd, buffer, sizeof(buffer) - 1); close(fd); if (len < 0) @@ -436,8 +437,7 @@ int is_git_directory(const char *suspect) if (getenv(DB_ENVIRONMENT)) { if (access(getenv(DB_ENVIRONMENT), X_OK)) goto done; - } - else { + } else { strbuf_setlen(&path, len); strbuf_addstr(&path, "/objects"); if (access(path.buf, X_OK)) @@ -541,12 +541,12 @@ static void setup_original_cwd(void) "realpath-path", tmp_original_cwd); trace2_data_string("setup", the_repository, "realpath-failure", strerror(errno)); - free((char*)tmp_original_cwd); + free((char *)tmp_original_cwd); tmp_original_cwd = NULL; return; } - free((char*)tmp_original_cwd); + free((char *)tmp_original_cwd); tmp_original_cwd = NULL; startup_info->original_cwd = strbuf_detach(&tmp, NULL); @@ -572,14 +572,13 @@ static void setup_original_cwd(void) * original_cwd was inside worktree; precompose it just as * we do prefix so that built up paths will match */ - startup_info->original_cwd = \ - precompose_string_if_needed(startup_info->original_cwd - + offset); + startup_info->original_cwd = + precompose_string_if_needed(startup_info->original_cwd + offset); return; } no_prevention_needed: - free((char*)startup_info->original_cwd); + free((char *)startup_info->original_cwd); startup_info->original_cwd = NULL; } @@ -615,22 +614,37 @@ static enum extension_result handle_extension_v0(const char *var, const char *ext, struct repository_format *data) { - if (!strcmp(ext, "noop")) { - return EXTENSION_OK; - } else if (!strcmp(ext, "preciousobjects")) { - 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")) { - data->worktree_config = git_config_bool(var, value); - return EXTENSION_OK; - } + if (!strcmp(ext, "noop")) { + return EXTENSION_OK; + } else if (!strcmp(ext, "preciousobjects")) { + 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")) { + data->worktree_config = git_config_bool(var, value); + return EXTENSION_OK; + } - return EXTENSION_UNKNOWN; + return EXTENSION_UNKNOWN; +} + +static void parse_reference_uri(const char *value, char **format, + char **location) +{ + char *schema_end; + + schema_end = strstr(value, "://"); + if (!schema_end) { + *format = xstrdup(value); + *location = NULL; + } else { + *format = xstrndup(value, schema_end - value); + *location = xstrdup(schema_end + 3); + } } /* @@ -668,21 +682,29 @@ static enum extension_result handle_extension(const char *var, for_each_string_list_item(item, &data->v1_only_extensions) { if (!strcmp(item->string, "compatobjectformat")) return error(_("'%s' already specified as '%s'"), - "extensions.compatobjectformat", - hash_algos[data->compat_hash_algo].name); + "extensions.compatobjectformat", + hash_algos[data->compat_hash_algo].name); } data->compat_hash_algo = format; return EXTENSION_OK; } else if (!strcmp(ext, "refstorage")) { unsigned int format; + char *backend; if (!value) return config_error_nonbool(var); - format = ref_storage_format_by_name(value); + + parse_reference_uri(value, &backend, + &data->ref_storage_location); + + format = ref_storage_format_by_name(backend); + free(backend); + if (format == REF_STORAGE_FORMAT_UNKNOWN) return error(_("invalid value for '%s': '%s'"), "extensions.refstorage", value); data->ref_storage_format = format; + return EXTENSION_OK; } else if (!strcmp(ext, "relativeworktrees")) { data->relative_worktrees = git_config_bool(var, value); @@ -851,6 +873,7 @@ void clear_repository_format(struct repository_format *format) string_list_clear(&format->v1_only_extensions, 0); free(format->work_tree); free(format->partial_clone); + free(format->ref_storage_location); init_repository_format(format); } @@ -929,7 +952,7 @@ void read_gitfile_error_die(int error_code, const char *path, const char *dir) */ const char *read_gitfile_gently(const char *path, int *return_error_code) { - const int max_file_size = 1 << 20; /* 1MB */ + const int max_file_size = 1 << 20; /* 1MB */ int error_code = 0; char *buf = NULL; char *dir = NULL; @@ -978,7 +1001,7 @@ const char *read_gitfile_gently(const char *path, int *return_error_code) dir = buf + 8; if (!is_absolute_path(dir) && (slash = strrchr(path, '/'))) { - size_t pathlen = slash+1 - path; + size_t pathlen = slash + 1 - path; dir = xstrfmt("%.*s%.*s", (int)pathlen, path, (int)(len - 8), buf + 8); free(buf); @@ -1015,7 +1038,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv, if (PATH_MAX - 40 < strlen(gitdirenv)) die(_("'$%s' too big"), GIT_DIR_ENVIRONMENT); - gitfile = (char*)read_gitfile(gitdirenv); + gitfile = (char *)read_gitfile(gitdirenv); if (gitfile) { gitfile = xstrdup(gitfile); gitdirenv = gitfile; @@ -1049,8 +1072,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv, set_git_dir(gitdirenv, 0); free(gitfile); return NULL; - } - else if (git_work_tree_cfg) { /* #6, #14 */ + } else if (git_work_tree_cfg) { /* #6, #14 */ if (is_absolute_path(git_work_tree_cfg)) set_git_work_tree(git_work_tree_cfg); else { @@ -1065,14 +1087,12 @@ static const char *setup_explicit_git_dir(const char *gitdirenv, set_git_work_tree(core_worktree); free(core_worktree); } - } - else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) { + } else if (!git_env_bool(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, 1)) { /* #16d */ set_git_dir(gitdirenv, 0); free(gitfile); return NULL; - } - else /* #2, #10 */ + } else /* #2, #10 */ set_git_work_tree("."); /* set_git_work_tree() must have been called by now */ @@ -1086,7 +1106,7 @@ static const char *setup_explicit_git_dir(const char *gitdirenv, } offset = dir_inside_of(cwd->buf, worktree); - if (offset >= 0) { /* cwd inside worktree? */ + if (offset >= 0) { /* cwd inside worktree? */ set_git_dir(gitdirenv, 1); if (chdir(worktree)) die_errno(_("cannot chdir to '%s'"), worktree); @@ -1178,8 +1198,7 @@ static const char *setup_bare_git_dir(struct strbuf *cwd, int offset, root_len = offset_1st_component(cwd->buf); strbuf_setlen(cwd, offset > root_len ? offset : root_len); set_git_dir(cwd->buf, 0); - } - else + } else set_git_dir(".", 0); return NULL; } @@ -1187,12 +1206,11 @@ static const char *setup_bare_git_dir(struct strbuf *cwd, int offset, static dev_t get_device_or_die(const char *path, const char *prefix, int prefix_len) { struct stat buf; - if (stat(path, &buf)) { + if (stat(path, &buf)) die_errno(_("failed to stat '%*s%s%s'"), - prefix_len, - prefix ? prefix : "", - prefix ? "/" : "", path); - } + prefix_len, + prefix ? prefix : "", + prefix ? "/" : "", path); return buf.st_dev; } @@ -1219,9 +1237,8 @@ static int canonicalize_ceiling_entry(struct string_list_item *item, return 1; } else { char *real_path = real_pathdup(ceil, 0); - if (!real_path) { + if (!real_path) return 0; - } free(item->string); item->string = real_path; return 1; @@ -1501,7 +1518,8 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, strbuf_addch(dir, '/'); strbuf_addstr(dir, DEFAULT_GIT_DIR_ENVIRONMENT); gitdirenv = read_gitfile_gently(dir->buf, die_on_error ? - NULL : &error_code); + NULL : + &error_code); if (!gitdirenv) { if (die_on_error || error_code == READ_GITFILE_ERR_NOT_A_FILE) { @@ -1568,7 +1586,7 @@ static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir, if (offset <= ceil_offset) return GIT_DIR_HIT_CEILING; - strbuf_setlen(dir, offset > min_offset ? offset : min_offset); + strbuf_setlen(dir, offset > min_offset ? offset : min_offset); if (one_filesystem && current_device != get_device_or_die(dir->buf, NULL, offset)) return GIT_DIR_HIT_MOUNT_POINT; @@ -1641,9 +1659,8 @@ void setup_git_env(const char *git_dir) args.graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT); args.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT); args.alternate_db = getenv_safe(&to_free, ALTERNATE_DB_ENVIRONMENT); - if (getenv(GIT_QUARANTINE_ENVIRONMENT)) { + if (getenv(GIT_QUARANTINE_ENVIRONMENT)) args.disable_ref_updates = 1; - } repo_set_gitdir(the_repository, git_dir, &args); strvec_clear(&to_free); @@ -1651,8 +1668,7 @@ void setup_git_env(const char *git_dir) if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT)) disable_replace_refs(); replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT); - git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base - : "refs/replace/"); + git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base : "refs/replace/"); update_ref_namespace(NAMESPACE_REPLACE, git_replace_ref_base); shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT); @@ -1733,6 +1749,7 @@ const char *setup_git_directory_gently(int *nongit_ok) static struct strbuf cwd = STRBUF_INIT; struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT, report = STRBUF_INIT; const char *prefix = NULL; + const char *ref_backend_uri; struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT; /* @@ -1800,11 +1817,10 @@ const char *setup_git_directory_gently(int *nongit_ok) *nongit_ok = 1; break; case GIT_DIR_DISALLOWED_BARE: - if (!nongit_ok) { + if (!nongit_ok) die(_("cannot use bare repository '%s' (safe.bareRepository is '%s')"), dir.buf, allowed_bare_repo_to_string(get_allowed_bare_repo())); - } *nongit_ok = 1; break; case GIT_DIR_CWD_FAILURE: @@ -1860,7 +1876,8 @@ const char *setup_git_directory_gently(int *nongit_ok) repo_set_compat_hash_algo(the_repository, repo_fmt.compat_hash_algo); repo_set_ref_storage_format(the_repository, - repo_fmt.ref_storage_format); + repo_fmt.ref_storage_format, + repo_fmt.ref_storage_location); the_repository->repository_format_worktree_config = repo_fmt.worktree_config; the_repository->repository_format_relative_worktrees = @@ -1889,6 +1906,25 @@ const char *setup_git_directory_gently(int *nongit_ok) setenv(GIT_PREFIX_ENVIRONMENT, "", 1); } + /* + * The env variable should override the repository config + * for 'extensions.refStorage'. + */ + ref_backend_uri = getenv(GIT_REFERENCE_BACKEND_ENVIRONMENT); + if (ref_backend_uri) { + char *backend, *location; + enum ref_storage_format format; + + parse_reference_uri(ref_backend_uri, &backend, &location); + format = ref_storage_format_by_name(backend); + if (format == REF_STORAGE_FORMAT_UNKNOWN) + die(_("unknown ref storage format: '%s'"), backend); + repo_set_ref_storage_format(the_repository, format, location); + + free(backend); + free(location); + } + setup_original_cwd(); strbuf_release(&dir); @@ -1928,11 +1964,11 @@ int git_config_perm(const char *var, const char *value) * a chmod value to restrict to. */ switch (i) { - case PERM_UMASK: /* 0 */ + case PERM_UMASK: /* 0 */ return PERM_UMASK; - case OLD_PERM_GROUP: /* 1 */ + case OLD_PERM_GROUP: /* 1 */ return PERM_GROUP; - case OLD_PERM_EVERYBODY: /* 2 */ + case OLD_PERM_EVERYBODY: /* 2 */ return PERM_EVERYBODY; } @@ -1940,8 +1976,9 @@ int git_config_perm(const char *var, const char *value) if ((i & 0600) != 0600) die(_("problem with core.sharedRepository filemode value " - "(0%.3o).\nThe owner of files must always have " - "read and write permissions."), i); + "(0%.3o).\nThe owner of files must always have " + "read and write permissions."), + i); /* * Mask filemode value. Others can not get write permission. @@ -1960,7 +1997,8 @@ void check_repository_format(struct repository_format *fmt) repo_set_hash_algo(the_repository, fmt->hash_algo); repo_set_compat_hash_algo(the_repository, fmt->compat_hash_algo); repo_set_ref_storage_format(the_repository, - fmt->ref_storage_format); + fmt->ref_storage_format, + fmt->ref_storage_location); the_repository->repository_format_worktree_config = fmt->worktree_config; the_repository->repository_format_relative_worktrees = @@ -2005,12 +2043,12 @@ int daemonize(void) return -1; #else switch (fork()) { - case 0: - break; - case -1: - die_errno(_("fork failed")); - default: - exit(0); + case 0: + break; + case -1: + die_errno(_("fork failed")); + default: + exit(0); } if (setsid() == -1) die_errno(_("setsid failed")); @@ -2074,9 +2112,9 @@ const char *get_template_dir(const char *option_template) } #ifdef NO_TRUSTABLE_FILEMODE -#define TEST_FILEMODE 0 +# define TEST_FILEMODE 0 #else -#define TEST_FILEMODE 1 +# define TEST_FILEMODE 1 #endif #define GIT_DEFAULT_HASH_ENVIRONMENT "GIT_DEFAULT_HASH" @@ -2110,8 +2148,7 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template_path, if (lstat(path->buf, &st_git)) { if (errno != ENOENT) die_errno(_("cannot stat '%s'"), path->buf); - } - else + } else exists = 1; if (lstat(template_path->buf, &st_template)) @@ -2125,8 +2162,7 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template_path, strbuf_addch(template_path, '/'); copy_templates_1(path, template_path, subdir); closedir(subdir); - } - else if (exists) + } else if (exists) continue; else if (S_ISLNK(st_template.st_mode)) { struct strbuf lnk = STRBUF_INIT; @@ -2137,13 +2173,11 @@ static void copy_templates_1(struct strbuf *path, struct strbuf *template_path, die_errno(_("cannot symlink '%s' '%s'"), lnk.buf, path->buf); strbuf_release(&lnk); - } - else if (S_ISREG(st_template.st_mode)) { + } else if (S_ISREG(st_template.st_mode)) { if (copy_file(path->buf, template_path->buf, st_template.st_mode)) die_errno(_("cannot copy '%s' to '%s'"), template_path->buf, path->buf); - } - else + } else error(_("ignoring template %s"), template_path->buf); } } @@ -2184,7 +2218,7 @@ static void copy_templates(const char *option_template) if (template_format.version >= 0 && verify_repository_format(&template_format, &err) < 0) { warning(_("not copying templates from '%s': %s"), - template_dir, err.buf); + template_dir, err.buf); strbuf_release(&err); goto close_free_return; } @@ -2284,7 +2318,7 @@ void create_reference_database(enum ref_storage_format ref_storage_format, char *to_free = NULL; int reinit = is_reinit(); - repo_set_ref_storage_format(the_repository, ref_storage_format); + repo_set_ref_storage_format(the_repository, ref_storage_format, NULL); if (ref_store_create_on_disk(get_main_ref_store(the_repository), 0, &err)) die("failed to set up refs db: %s", err.buf); @@ -2358,9 +2392,8 @@ static int create_default_files(const char *template_path, * We would have created the above under user's umask -- under * shared-repository settings, we would need to fix them up. */ - if (repo_settings_get_shared_repository(the_repository)) { + if (repo_settings_get_shared_repository(the_repository)) adjust_shared_perm(the_repository, repo_get_git_dir(the_repository)); - } initialize_repository_version(fmt->hash_algo, fmt->ref_storage_format, reinit); @@ -2370,9 +2403,9 @@ static int create_default_files(const char *template_path, if (TEST_FILEMODE && !lstat(path.buf, &st1)) { struct stat st2; filemode = (!chmod(path.buf, st1.st_mode ^ S_IXUSR) && - !lstat(path.buf, &st2) && - st1.st_mode != st2.st_mode && - !chmod(path.buf, st1.st_mode)); + !lstat(path.buf, &st2) && + st1.st_mode != st2.st_mode && + !chmod(path.buf, st1.st_mode)); if (filemode && !reinit && (st1.st_mode & S_IXUSR)) filemode = 0; } @@ -2563,7 +2596,8 @@ static void repository_format_configure(struct repository_format *repo_fmt, } else { repo_fmt->ref_storage_format = REF_STORAGE_FORMAT_DEFAULT; } - repo_set_ref_storage_format(the_repository, repo_fmt->ref_storage_format); + repo_set_ref_storage_format(the_repository, repo_fmt->ref_storage_format, + repo_fmt->ref_storage_location); } int init_db(const char *git_dir, const char *real_git_dir, @@ -2589,8 +2623,7 @@ int init_db(const char *git_dir, const char *real_git_dir, set_git_dir(real_git_dir, 1); git_dir = repo_get_git_dir(the_repository); separate_git_dir(git_dir, original_git_dir); - } - else { + } else { set_git_dir(git_dir, 1); git_dir = repo_get_git_dir(the_repository); } @@ -2648,15 +2681,11 @@ int init_db(const char *git_dir, const char *real_git_dir, int len = strlen(git_dir); if (reinit) - printf(repo_settings_get_shared_repository(the_repository) - ? _("Reinitialized existing shared Git repository in %s%s\n") - : _("Reinitialized existing Git repository in %s%s\n"), - git_dir, len && git_dir[len-1] != '/' ? "/" : ""); + printf(repo_settings_get_shared_repository(the_repository) ? _("Reinitialized existing shared Git repository in %s%s\n") : _("Reinitialized existing Git repository in %s%s\n"), + git_dir, len && git_dir[len - 1] != '/' ? "/" : ""); else - printf(repo_settings_get_shared_repository(the_repository) - ? _("Initialized empty shared Git repository in %s%s\n") - : _("Initialized empty Git repository in %s%s\n"), - git_dir, len && git_dir[len-1] != '/' ? "/" : ""); + printf(repo_settings_get_shared_repository(the_repository) ? _("Initialized empty shared Git repository in %s%s\n") : _("Initialized empty Git repository in %s%s\n"), + git_dir, len && git_dir[len - 1] != '/' ? "/" : ""); } clear_repository_format(&repo_fmt); diff --git a/setup.h b/setup.h index 8522fa8575da7123d0a9dc57dbabf08677a58eaa..918d99ae2a371f1b4bcf0553a8e5637631500f8f 100644 --- a/setup.h +++ b/setup.h @@ -134,6 +134,7 @@ struct repository_format { int hash_algo; int compat_hash_algo; enum ref_storage_format ref_storage_format; + char *ref_storage_location; int sparse_index; char *work_tree; struct string_list unknown_extensions; diff --git a/t/meson.build b/t/meson.build index 7c994d4643efe29bc70377300fd2eed7b5994923..fd47a69b06067b2110b7f612aa26d62acc0c530f 100644 --- a/t/meson.build +++ b/t/meson.build @@ -209,6 +209,7 @@ integration_tests = [ 't1420-lost-found.sh', 't1421-reflog-write.sh', 't1422-show-ref-exists.sh', + 't1423-ref-backend.sh', 't1430-bad-ref-name.sh', 't1450-fsck.sh', 't1451-fsck-buffer.sh', diff --git a/t/t1423-ref-backend.sh b/t/t1423-ref-backend.sh new file mode 100755 index 0000000000000000000000000000000000000000..11ed8eba138f19c5aaa4646f35ac0800ca0590ff --- /dev/null +++ b/t/t1423-ref-backend.sh @@ -0,0 +1,158 @@ +#!/bin/sh + +test_description='Test reference backend URIs' + +. ./test-lib.sh + +# Run a git command with the provided reference storage. Reset the backend +# post running the command. +# Usage: run_with_uri +# is the relative path to the repo to run the command in. +# is the original ref storage of the repo. +# is the new URI to be set for the ref storage. +# is the git subcommand to be run in the repository. +# if 'config', set the backend via the 'extensions.refStorage' config. +# if 'env', set the backend via the 'GIT_REFERENCE_BACKEND' env. +run_with_uri() { + repo=$1 && + backend=$2 && + uri=$3 && + cmd=$4 && + via=$5 && + + if test "$via" = "env" + then + test_env GIT_REFERENCE_BACKEND="$uri" git -C "$repo" $cmd + elif test "$via" = "config" + then + git -C "$repo" config set extensions.refStorage "$uri" && + git -C "$repo" $cmd && + git -C "$repo" config set extensions.refStorage "$backend" + fi +} + +# Test a repository with a given reference storage by running and comparing +# 'git refs list' before and after setting the new reference backend. If +# err_msg is set, expect the command to fail and grep for the provided err_msg. +# Usage: run_with_uri +# is the relative path to the repo to run the command in. +# is the original ref storage of the repo. +# is the new URI to be set for the ref storage. +# if 'config', set the backend via the 'extensions.refStorage' config. +# if 'env', set the backend via the 'GIT_REFERENCE_BACKEND' env. +# (optional) if set, check if 'git-refs(1)' failed with the provided msg. +test_refs_backend() { + repo=$1 && + backend=$2 && + uri=$3 && + via=$4 && + err_msg=$5 && + + git -C "$repo" config set core.repositoryformatversion 1 && + if test -n "$err_msg"; + then + if test "$via" = "env" + then + test_env GIT_REFERENCE_BACKEND="$uri" test_must_fail git -C "$repo" refs list 2>err && + test_grep "unknown ref storage format" err + elif test "$via" = "config" + then + git -C "$repo" config set extensions.refStorage "$uri" && + test_must_fail git -C "$repo" refs list 2>err && + test_grep "$err_msg" err + fi + else + git -C "$repo" refs list >expect && + run_with_uri "$repo" "$backend" "$uri" "refs list" "$via" >actual && + test_cmp expect actual + fi +} + +methods="env config" +for method in $methods +do + +test_expect_success "$method: URI is invalid" ' + test_when_finished "rm -rf repo" && + git init --ref-format=files repo && + test_refs_backend repo files "reftable@/home/reftable" "$method" \ + "invalid value for ${SQ}extensions.refstorage${SQ}" +' + +test_expect_success "$method: URI ends with colon" ' + test_when_finished "rm -rf repo" && + git init --ref-format=files repo && + test_refs_backend repo files "reftable:" "$method" \ + "invalid value for ${SQ}extensions.refstorage${SQ}" +' + +test_expect_success "$method: unknown reference backend" ' + test_when_finished "rm -rf repo" && + git init --ref-format=reftable repo && + test_refs_backend repo files "db://.git" "$method" \ + "invalid value for ${SQ}extensions.refstorage${SQ}" +' + +test_expect_success "$method: URI with empty path" ' + test_when_finished "rm -rf repo" && + git init --ref-format=reftable repo && + test_refs_backend repo files "reftable://" "$method" +' + +ref_formats="files reftable" +for from_format in $ref_formats +do + for to_format in $ref_formats + do + if test "$from_format" = "$to_format" + then + continue + fi + + test_expect_success "$method: read from $to_format backend" ' + test_when_finished "rm -rf repo" && + git init --ref-format=$from_format repo && + ( + cd repo && + test_commit 1 && + test_commit 2 && + test_commit 3 && + + git refs migrate --dry-run --ref-format=$to_format >out && + BACKEND_PATH=$(sed "s/.* ${SQ}\(.*\)${SQ}/\1/" out) && + test_refs_backend . $from_format "$to_format://$BACKEND_PATH" "$method" + ) + ' + + test_expect_success "$method: write to $to_format backend" ' + test_when_finished "rm -rf repo" && + git init --ref-format=$from_format repo && + ( + cd repo && + test_commit 1 && + test_commit 2 && + test_commit 3 && + + git refs migrate --dry-run --ref-format=$to_format >out && + BACKEND_PATH=$(sed "s/.* ${SQ}\(.*\)${SQ}/\1/" out) && + + test_refs_backend . $from_format "$to_format://$BACKEND_PATH" "$method" && + + git refs list >expect && + run_with_uri . "$from_format" "$to_format://$BACKEND_PATH" \ + "tag -d 1" "$method" && + git refs list >actual && + test_cmp expect actual && + + git refs list | grep -v "refs/tags/1" >expect && + run_with_uri . "$from_format" "$to_format://$BACKEND_PATH" \ + "refs list" "$method" >actual && + test_cmp expect actual + ) + ' + done # closes to_format +done # closes from_format + +done # closes method + +test_done