setup: deduplicate logic to apply repository format

After having discovered the repository format we then apply it to the repository so that it knows to use the proper repository extensions. The logic to apply the format is duplicated across three callsites, which makes it rather painfull to add new extensions. Introduce a new function `apply_repository_format()` that takes a repo and applies a given format to it and adapt all callsites to use it. This function is also the new caller of `verify_repository_format()` so that we can ensure that we never apply an invalid repository format. The verification we have in `read_and_verify_repository_format()` is thus redundant now and dropped. Rename `read_and_verify_repository_format()` accordingly. While at it, also rename `check_repository_format()` to clarify that it doesn't only _check_ the format, but that it also applies it. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Patrick Steinhardt committed Jun 4, 2026 at 09:46 UTC 3d884b0b5656fe012002edd6bb8f36a125e6c17e
3 files changed +71 -63
repository.c
+11 -20
@@ -262,8 +262,8 @@ void repo_set_worktree(struct repository *repo, const char *path)
262 trace2_def_repo(repo);
263 }
264
265 -static int read_and_verify_repository_format(struct repository_format *format,
266 - const char *commondir)
265 +static int read_repository_format_from_commondir(struct repository_format *format,
266 + const char *commondir)
267 {
268 int ret = 0;
269 struct strbuf sb = STRBUF_INIT;
@@ -272,11 +272,6 @@ static int read_and_verify_repository_format(struct repository_format *format,
272 read_repository_format(format, sb.buf);
273 strbuf_reset(&sb);
274
275 - if (verify_repository_format(format, &sb) < 0) {
276 - warning("%s", sb.buf);
277 - ret = -1;
278 - }
279 -
275 strbuf_release(&sb);
276 return ret;
277 }
@@ -290,6 +285,8 @@ int repo_init(struct repository *repo,
285 const char *worktree)
286 {
287 struct repository_format format = REPOSITORY_FORMAT_INIT;
288 + struct strbuf err = STRBUF_INIT;
289 +
290 memset(repo, 0, sizeof(*repo));
291
292 initialize_repository(repo);
@@ -297,21 +294,13 @@ int repo_init(struct repository *repo,
294 if (repo_init_gitdir(repo, gitdir))
295 goto error;
296
300 - if (read_and_verify_repository_format(&format, repo->commondir))
297 + if (read_repository_format_from_commondir(&format, repo->commondir))
298 goto error;
299
303 - repo_set_hash_algo(repo, format.hash_algo);
304 - repo_set_compat_hash_algo(repo, format.compat_hash_algo);
305 - repo_set_ref_storage_format(repo, format.ref_storage_format,
306 - format.ref_storage_payload);
307 - repo->repository_format_worktree_config = format.worktree_config;
308 - repo->repository_format_relative_worktrees = format.relative_worktrees;
309 - repo->repository_format_precious_objects = format.precious_objects;
310 - repo->repository_format_submodule_path_cfg = format.submodule_path_cfg;
311 -
312 - /* take ownership of format.partial_clone */
313 - repo->repository_format_partial_clone = format.partial_clone;
314 - format.partial_clone = NULL;
300 + if (apply_repository_format(repo, &format, &err) < 0) {
301 + warning("%s", err.buf);
302 + goto error;
303 + }
304
305 if (worktree)
306 repo_set_worktree(repo, worktree);
@@ -320,10 +309,12 @@ int repo_init(struct repository *repo,
309 repo_read_loose_object_map(repo);
310
311 clear_repository_format(&format);
312 + strbuf_release(&err);
313 return 0;
314
315 error:
316 clear_repository_format(&format);
317 + strbuf_release(&err);
318 repo_clear(repo);
319 return -1;
320 }
setup.c
+50 -43
@@ -750,8 +750,7 @@ static int check_repo_format(const char *var, const char *value,
750 return read_worktree_config(var, value, ctx, vdata);
751 }
752
753 -static int check_repository_format_gently(struct repository *repo,
754 - const char *gitdir,
753 +static int check_repository_format_gently(const char *gitdir,
754 struct repository_format *candidate,
755 int *nongit_ok)
756 {
@@ -765,7 +764,7 @@ static int check_repository_format_gently(struct repository *repo,
764 strbuf_release(&sb);
765
766 /*
768 - * For historical use of check_repository_format() in git-init,
767 + * For historical use of check_and_apply_repository_format() in git-init,
768 * we treat a missing config as a silent "ok", even when nongit_ok
769 * is unset.
770 */
@@ -782,8 +781,6 @@ static int check_repository_format_gently(struct repository *repo,
781 die("%s", err.buf);
782 }
783
785 - repo->repository_format_precious_objects = candidate->precious_objects;
786 -
784 string_list_clear(&candidate->unknown_extensions, 0);
785 string_list_clear(&candidate->v1_only_extensions, 0);
786
@@ -1140,7 +1137,7 @@ static const char *setup_explicit_git_dir(struct repository *repo,
1137 die(_("not a git repository: '%s'"), gitdirenv);
1138 }
1139
1143 - if (check_repository_format_gently(repo, gitdirenv, repo_fmt, nongit_ok)) {
1140 + if (check_repository_format_gently(gitdirenv, repo_fmt, nongit_ok)) {
1141 free(gitfile);
1142 return NULL;
1143 }
@@ -1217,7 +1214,7 @@ static const char *setup_discovered_git_dir(struct repository *repo,
1214 struct repository_format *repo_fmt,
1215 int *nongit_ok)
1216 {
1220 - if (check_repository_format_gently(repo, gitdir, repo_fmt, nongit_ok))
1217 + if (check_repository_format_gently(gitdir, repo_fmt, nongit_ok))
1218 return NULL;
1219
1220 /* --work-tree is set without --git-dir; use discovered one */
@@ -1265,7 +1262,7 @@ static const char *setup_bare_git_dir(struct repository *repo,
1262 {
1263 int root_len;
1264
1268 - if (check_repository_format_gently(repo, ".", repo_fmt, nongit_ok))
1265 + if (check_repository_format_gently(".", repo_fmt, nongit_ok))
1266 return NULL;
1267
1268 setenv(GIT_IMPLICIT_WORK_TREE_ENVIRONMENT, "0", 1);
@@ -1757,6 +1754,32 @@ enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
1754 return result;
1755 }
1756
1757 +int apply_repository_format(struct repository *repo,
1758 + const struct repository_format *format,
1759 + struct strbuf *err)
1760 +{
1761 + if (verify_repository_format(format, err) < 0)
1762 + return -1;
1763 +
1764 + repo_set_hash_algo(repo, format->hash_algo);
1765 + repo_set_compat_hash_algo(repo, format->compat_hash_algo);
1766 + repo_set_ref_storage_format(repo,
1767 + format->ref_storage_format,
1768 + format->ref_storage_payload);
1769 + repo->repository_format_worktree_config =
1770 + format->worktree_config;
1771 + repo->repository_format_submodule_path_cfg =
1772 + format->submodule_path_cfg;
1773 + repo->repository_format_relative_worktrees =
1774 + format->relative_worktrees;
1775 + repo->repository_format_partial_clone =
1776 + xstrdup_or_null(format->partial_clone);
1777 + repo->repository_format_precious_objects =
1778 + format->precious_objects;
1779 +
1780 + return 0;
1781 +}
1782 +
1783 /*
1784 * Check the repository format version in the path found in repo_get_git_dir(repo),
1785 * and die if it is a version we don't understand. Generally one would
@@ -1765,26 +1788,20 @@ enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
1788 *
1789 * If successful and fmt is not NULL, fill fmt with data.
1790 */
1768 -static void check_repository_format(struct repository *repo, struct repository_format *fmt)
1791 +static void check_and_apply_repository_format(struct repository *repo,
1792 + struct repository_format *fmt)
1793 {
1794 struct repository_format repo_fmt = REPOSITORY_FORMAT_INIT;
1795 + struct strbuf err = STRBUF_INIT;
1796 +
1797 if (!fmt)
1798 fmt = &repo_fmt;
1773 - check_repository_format_gently(repo, repo_get_git_dir(repo), fmt, NULL);
1799 +
1800 + check_repository_format_gently(repo_get_git_dir(repo), fmt, NULL);
1801 + if (apply_repository_format(repo, fmt, &err) < 0)
1802 + die("%s", err.buf);
1803 startup_info->have_repository = 1;
1775 - repo_set_hash_algo(repo, fmt->hash_algo);
1776 - repo_set_compat_hash_algo(repo, fmt->compat_hash_algo);
1777 - repo_set_ref_storage_format(repo,
1778 - fmt->ref_storage_format,
1779 - fmt->ref_storage_payload);
1780 - repo->repository_format_worktree_config =
1781 - fmt->worktree_config;
1782 - repo->repository_format_submodule_path_cfg =
1783 - fmt->submodule_path_cfg;
1784 - repo->repository_format_relative_worktrees =
1785 - fmt->relative_worktrees;
1786 - repo->repository_format_partial_clone =
1787 - xstrdup_or_null(fmt->partial_clone);
1804 +
1805 clear_repository_format(&repo_fmt);
1806 }
1807
@@ -1862,7 +1879,7 @@ const char *enter_repo(struct repository *repo, const char *path, unsigned flags
1879
1880 if (is_git_directory(".")) {
1881 set_git_dir(repo, ".", 0);
1865 - check_repository_format(repo, NULL);
1882 + check_and_apply_repository_format(repo, NULL);
1883 return path;
1884 }
1885
@@ -2020,25 +2037,15 @@ const char *setup_git_directory_gently(struct repository *repo, int *nongit_ok)
2037 gitdir = DEFAULT_GIT_DIR_ENVIRONMENT;
2038 setup_git_env_internal(repo, gitdir, false);
2039 }
2040 +
2041 if (startup_info->have_repository) {
2024 - repo_set_hash_algo(repo, repo_fmt.hash_algo);
2025 - repo_set_compat_hash_algo(repo,
2026 - repo_fmt.compat_hash_algo);
2027 - repo_set_ref_storage_format(repo,
2028 - repo_fmt.ref_storage_format,
2029 - repo_fmt.ref_storage_payload);
2030 - repo->repository_format_worktree_config =
2031 - repo_fmt.worktree_config;
2032 - repo->repository_format_relative_worktrees =
2033 - repo_fmt.relative_worktrees;
2034 - repo->repository_format_submodule_path_cfg =
2035 - repo_fmt.submodule_path_cfg;
2036 - /* take ownership of repo_fmt.partial_clone */
2037 - repo->repository_format_partial_clone =
2038 - repo_fmt.partial_clone;
2039 - repo_fmt.partial_clone = NULL;
2040 - repo->repository_format_precious_objects =
2041 - repo_fmt.precious_objects;
2042 + struct strbuf err = STRBUF_INIT;
2043 +
2044 + if (apply_repository_format(repo, &repo_fmt, &err) < 0)
2045 + die("%s", err.buf);
2046 +
2047 + clear_repository_format(&repo_fmt);
2048 + strbuf_release(&err);
2049 }
2050 }
2051 /*
@@ -2814,7 +2821,7 @@ int init_db(struct repository *repo,
2821 * config file, so this will not fail. What we are catching
2822 * is an attempt to reinitialize new repository with an old tool.
2823 */
2817 - check_repository_format(repo, &repo_fmt);
2824 + check_and_apply_repository_format(repo, &repo_fmt);
2825
2826 repository_format_configure(repo, &repo_fmt, hash, ref_storage_format);
2827
setup.h
+10
@@ -221,6 +221,16 @@ void clear_repository_format(struct repository_format *format);
221 int verify_repository_format(const struct repository_format *format,
222 struct strbuf *err);
223
224 +/*
225 + * Apply the given repository format to the repo. This initializes extensions
226 + * and basic data structures required for normal operation. Returns 0 on
227 + * success, a negative error code when the format is not valid as determined by
228 + * `verify_repository_format()`.
229 + */
230 +int apply_repository_format(struct repository *repo,
231 + const struct repository_format *format,
232 + struct strbuf *err);
233 +
234 const char *get_template_dir(const char *option_template);
235
236 #define INIT_DB_QUIET (1 << 0)