]> git.ipfire.org Git - thirdparty/git.git/commitdiff
setup: introduce "extensions.refStorage" extension
authorPatrick Steinhardt <ps@pks.im>
Fri, 29 Dec 2023 07:26:47 +0000 (08:26 +0100)
committerJunio C Hamano <gitster@pobox.com>
Tue, 2 Jan 2024 17:24:48 +0000 (09:24 -0800)
Introduce a new "extensions.refStorage" extension that allows us to
specify the ref storage format used by a repository. For now, the only
supported format is the "files" format, but this list will likely soon
be extended to also support the upcoming "reftable" format.

There have been discussions on the Git mailing list in the past around
how exactly this extension should look like. One alternative [1] that
was discussed was whether it would make sense to model the extension in
such a way that backends are arbitrarily stackable. This would allow for
a combined value of e.g. "loose,packed-refs" or "loose,reftable", which
indicates that new refs would be written via "loose" files backend and
compressed into "packed-refs" or "reftable" backends, respectively.

It is arguable though whether this flexibility and the complexity that
it brings with it is really required for now. It is not foreseeable that
there will be a proliferation of backends in the near-term future, and
the current set of existing formats and formats which are on the horizon
can easily be configured with the much simpler proposal where we have a
single value, only.

Furthermore, if we ever see that we indeed want to gain the ability to
arbitrarily stack the ref formats, then we can adapt the current
extension rather easily. Given that Git clients will refuse any unknown
value for the "extensions.refStorage" extension they would also know to
ignore a stacked "loose,packed-refs" in the future.

So let's stick with the easy proposal for the time being and wire up the
extension.

[1]: <pull.1408.git.1667846164.gitgitgadget@gmail.com>

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/config/extensions.txt
Documentation/ref-storage-format.txt [new file with mode: 0644]
Documentation/technical/repository-version.txt
builtin/clone.c
setup.c
setup.h
t/t0001-init.sh
t/test-lib.sh

index bccaec7a963679f8262d7c8d056fa4429b9586d1..66db0e15da7db82819de941626b7abc5983862c5 100644 (file)
@@ -7,6 +7,17 @@ Note that this setting should only be set by linkgit:git-init[1] or
 linkgit:git-clone[1].  Trying to change it after initialization will not
 work and will produce hard-to-diagnose issues.
 
+extensions.refStorage::
+       Specify the ref storage format to use. The acceptable values are:
++
+include::../ref-storage-format.txt[]
++
+It is an error to specify this key unless `core.repositoryFormatVersion` is 1.
++
+Note that this setting should only be set by linkgit:git-init[1] or
+linkgit:git-clone[1]. Trying to change it after initialization will not
+work and will produce hard-to-diagnose issues.
+
 extensions.worktreeConfig::
        If enabled, then worktrees will load config settings from the
        `$GIT_DIR/config.worktree` file in addition to the
diff --git a/Documentation/ref-storage-format.txt b/Documentation/ref-storage-format.txt
new file mode 100644 (file)
index 0000000..1a65cac
--- /dev/null
@@ -0,0 +1 @@
+* `files` for loose files with packed-refs. This is the default.
index 045a76756fcf47401dc61cb55f37df1deff480d7..27be3741e6040ed9f9915f504793b4ad978d40ad 100644 (file)
@@ -100,3 +100,8 @@ If set, by default "git config" reads from both "config" and
 multiple working directory mode, "config" file is shared while
 "config.worktree" is per-working directory (i.e., it's in
 GIT_COMMON_DIR/worktrees/<id>/config.worktree)
+
+==== `refStorage`
+
+Specifies the file format for the ref database. The only valid value
+is `files` (loose references with a packed-refs file).
index 48aeb1b90beb68eab57ea6fb83196245bf8e9b9d..0fb3816d0c034268c4315a3d97836fe397bcd243 100644 (file)
@@ -1291,7 +1291,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
         * ours to the same thing.
         */
        hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
-       initialize_repository_version(hash_algo, 1);
+       initialize_repository_version(hash_algo, the_repository->ref_storage_format, 1);
        repo_set_hash_algo(the_repository, hash_algo);
        create_reference_database(the_repository->ref_storage_format, NULL, 1);
 
diff --git a/setup.c b/setup.c
index 49570e6b3a2816c8a4dd849fcb631c0275f35717..fb1413cabd2fea15261a1c1e867cf7f4dc5d6aba 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -592,6 +592,17 @@ static enum extension_result handle_extension(const char *var,
                                     "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;
 }
@@ -1871,12 +1882,15 @@ static int needs_work_tree_config(const char *git_dir, const char *work_tree)
        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)
+       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 */
@@ -1889,6 +1903,10 @@ void initialize_repository_version(int hash_algo, int reinit)
                               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)
@@ -2030,7 +2048,7 @@ static int create_default_files(const char *template_path,
                adjust_shared_perm(get_git_dir());
        }
 
-       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");
diff --git a/setup.h b/setup.h
index 3d3eda7967cd71f675580f958123476627f464c3..3599aec93c5ac0b72aafaf3cdcb030831cc53e3b 100644 (file)
--- a/setup.h
+++ b/setup.h
@@ -180,7 +180,9 @@ int init_db(const char *git_dir, const char *real_git_dir,
            unsigned int ref_storage_format,
            const char *initial_branch, int init_shared_repository,
            unsigned int flags);
-void initialize_repository_version(int hash_algo, int reinit);
+void initialize_repository_version(int hash_algo,
+                                  unsigned int ref_storage_format,
+                                  int reinit);
 void create_reference_database(unsigned int ref_storage_format,
                               const char *initial_branch, int quiet);
 
index 2b78e3be4795096aa3d9eeb5f7b5c3dc3a24f717..38b3e4c39e875bd881cb5a41d35a9d8b6eb2767a 100755 (executable)
@@ -532,6 +532,32 @@ test_expect_success 'init rejects attempts to initialize with different hash' '
        test_must_fail git -C sha256 init --object-format=sha1
 '
 
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage is not allowed with repo version 0' '
+       test_when_finished "rm -rf refstorage" &&
+       git init refstorage &&
+       git -C refstorage config extensions.refStorage files &&
+       test_must_fail git -C refstorage rev-parse 2>err &&
+       grep "repo version is 0, but v1-only extension found" err
+'
+
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with files backend' '
+       test_when_finished "rm -rf refstorage" &&
+       git init refstorage &&
+       git -C refstorage config core.repositoryformatversion 1 &&
+       git -C refstorage config extensions.refStorage files &&
+       test_commit -C refstorage A &&
+       git -C refstorage rev-parse --verify HEAD
+'
+
+test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with unknown backend' '
+       test_when_finished "rm -rf refstorage" &&
+       git init refstorage &&
+       git -C refstorage config core.repositoryformatversion 1 &&
+       git -C refstorage config extensions.refStorage garbage &&
+       test_must_fail git -C refstorage rev-parse 2>err &&
+       grep "invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}garbage${SQ}" err
+'
+
 test_expect_success MINGW 'core.hidedotfiles = false' '
        git config --global core.hidedotfiles false &&
        rm -rf newdir &&
index dc03f06b8e90440e4a79c0bdc465e3960a50d412..4685cc3d4805a214a6f8ecdc9fd0e744ec558199 100644 (file)
@@ -1937,7 +1937,7 @@ test_lazy_prereq SHA1 '
 '
 
 test_lazy_prereq DEFAULT_REPO_FORMAT '
-       test_have_prereq SHA1
+       test_have_prereq SHA1,REFFILES
 '
 
 # Ensure that no test accidentally triggers a Git command