refs: RFC: Reftable support for git-core
Subsystems (2)
PATCH 1/1 refs: RFC: Reftable support for git-core
From: Han-Wen Nienhuys <[email protected]>
For background, see Documentation/technical/reftable.txt.
This introduces the file refs/reftable-backend.c containing a reftable-powered ref storage backend.
It can be activated by setting GIT_TEST_REFTABLE in the environment. When GIT_TEST_REFTABLE is set, the test prerequisite !REFFILES is set.
There is no option to git-init for now, as the test suite still shows failures with GIT_TEST_REFTABLE=1.
Example use: see t/t0031-reftable.sh
---
RFC: Reftable backend current version
In
https://lore.kernel.org/git/[email protected]/
Ævar considered the option of merging the reftable without 100% of tests passing. In my development branch, I am down to 26 test failures.
As input to this discussion, I'm proffering the latest version of the reftable support.
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1215%2Fhanwen%2Freftable-rfc-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1215/hanwen/reftable-rfc-v1
Pull-Request: https://github.com/git/git/pull/1215
Documentation/config/extensions.txt | 9 + .../technical/repository-version.txt | 7 + Makefile | 1 + builtin/clone.c | 5 +- builtin/init-db.c | 41 +- builtin/stash.c | 8 +- builtin/worktree.c | 27 +- cache.h | 8 +- config.mak.uname | 2 +- contrib/workdir/git-new-workdir | 2 +- refs.c | 29 +- refs.h | 3 + refs/refs-internal.h | 1 + refs/reftable-backend.c | 1898 +++++++++++++++++ repository.c | 2 + repository.h | 3 + setup.c | 8 + t/t0031-reftable.sh | 380 ++++ t/t1409-avoid-packing-refs.sh | 6 + t/t1450-fsck.sh | 6 + t/t3210-pack-refs.sh | 6 + t/test-lib.sh | 7 +- 22 files changed, 2429 insertions(+), 30 deletions(-) create mode 100644 refs/reftable-backend.c create mode 100755 t/t0031-reftable.sh
Documentation/config/extensions.txt +9 -0
@@ -37,3 +37,12 @@ details. + For historical reasons, `extensions.worktreeConfig` is respected regardless of the `core.repositoryFormatVersion` setting. ++ +extensions.refStorage:: + Specify the ref storage mechanism to use. The acceptable values are `files` and + `reftable`. If not specified, `files` is assumed. 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.
Documentation/technical/repository-version.txt +7 -0
@@ -100,3 +100,10 @@ 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. Values are `files` +(for the traditional packed + loose ref format) and `reftable` for the +binary reftable format. See https://github.com/google/reftable for +more information.
Makefile +1 -0
@@ -1000,6 +1000,7 @@ LIB_OBJS += reflog-walk.o LIB_OBJS += refs.o LIB_OBJS += refs/debug.o LIB_OBJS += refs/files-backend.o +LIB_OBJS += refs/reftable-backend.o LIB_OBJS += refs/iterator.o LIB_OBJS += refs/packed-backend.o LIB_OBJS += refs/ref-cache.o
builtin/clone.c +3 -2
@@ -1043,7 +1043,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) }init_db (git_dir, real_git_dir, option_template,GIT_HASH_UNKNOWN ,NULL , -INIT_DB_QUIET ); +default_ref_storage (),INIT_DB_QUIET );if (real_git_dir) {free ((char *)git_dir);
@@ -1199,7 +1199,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)* Now that we know what algorithm the remote side is using, * let's set ours to the same thing. */ -initialize_repository_version (hash_algo,1 ); +initialize_repository_version (hash_algo,1 , +default_ref_storage ());repo_set_hash_algo (the_repository, hash_algo);/* * transport_get_remote_refs() may return refs with null sha-1
builtin/init-db.c +33 -8
109 lines not on this page · raw message
builtin/stash.c +7 -1
@@ -207,10 +207,16 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)static int do_clear_stash (void ) {struct object_id obj; +int result;if (get_oid (ref_stash, &obj))return 0 ; -return delete_ref (NULL , ref_stash, &obj,0 ); + result =delete_ref (NULL , ref_stash, &obj,0 ); + +/* Ignore error; this is necessary for reftable, which keeps reflogs +* even when refs are deleted. */ +delete_reflog (ref_stash); +return result; }static int clear_stash (int argc,const char **argv,const char *prefix)
builtin/worktree.c +24 -3
@@ -14,6 +14,7 @@#include "utf8.h" #include "worktree.h" #include "quote.h" +#include "../refs/refs-internal.h" static const char *const worktree_usage[] = {N_ ("git worktree add [<options>] <path> [<commit-ish>]" ),
@@ -329,9 +330,29 @@ static int add_worktree(const char *path, const char *refname,* worktree. */ strbuf_reset (&sb); -strbuf_addf (&sb,"%s/HEAD" , sb_repo.buf); -write_file (sb.buf,"%s" ,oid_to_hex (null_oid ())); -strbuf_reset (&sb); +if (get_main_ref_store (the_repository)->be == &refs_be_reftable) { +/* XXX this is cut & paste from reftable_init_db. */ +strbuf_addf (&sb,"%s/HEAD" , sb_repo.buf); +write_file (sb.buf,"%s" ,"ref: refs/heads/.invalid\n" ); +strbuf_reset (&sb); + +strbuf_addf (&sb,"%s/refs" , sb_repo.buf); +safe_create_dir (sb.buf,1 ); +strbuf_reset (&sb); + +strbuf_addf (&sb,"%s/refs/heads" , sb_repo.buf); +write_file (sb.buf,"this repository uses the reftable format" ); +strbuf_reset (&sb); + +strbuf_addf (&sb,"%s/reftable" , sb_repo.buf); +safe_create_dir (sb.buf,1 ); +strbuf_reset (&sb); + }else { +strbuf_addf (&sb,"%s/HEAD" , sb_repo.buf); +write_file (sb.buf,"%s" ,oid_to_hex (null_oid ())); +strbuf_reset (&sb); + } +strbuf_addf (&sb,"%s/commondir" , sb_repo.buf);write_file (sb.buf,"../.." );
cache.h +5 -3
@@ -653,9 +653,10 @@ int path_inside_repo(const char *prefix, const char *path);#define INIT_DB_EXIST_OK 0x0002int init_db (const char *git_dir,const char *real_git_dir, -const char *template_dir,int hash_algo, -const char *initial_branch,unsigned int flags); -void initialize_repository_version (int hash_algo,int reinit); +const char *template_dir,int hash_algo,const char *initial_branch, +const char *ref_storage_format,unsigned int flags); +void initialize_repository_version (int hash_algo,int reinit, +const char *ref_storage_format);void sanitize_stdfds (void );int daemonize (void );
@@ -1076,6 +1077,7 @@ struct repository_format {int hash_algo;int sparse_index;char *work_tree; +char *ref_storage;struct string_list unknown_extensions;struct string_list v1_only_extensions; };
config.mak.uname +1 -1
@@ -749,7 +749,7 @@ vcxproj: # Make .vcxproj files and add them unset QUIET_GEN QUIET_BUILT_IN; \ perl contrib/buildsystems/generate -g Vcxproj - git add -f git.sln {*,*/lib,t/helper/*}/*.vcxproj + git add -f git.sln {*,*/lib,*/libreftable,t/helper/*}/*.vcxproj # Generate the LinkOrCopyBuiltins.targets and LinkOrCopyRemoteHttp.targets file (echo '<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">' && \
contrib/workdir/git-new-workdir +1 -1
@@ -79,7 +79,7 @@ trap cleanup $siglist # create the links to the original repo. explicitly exclude index, HEAD and # logs/HEAD from the list since they are purely related to the current working # directory, and should not be shared. -for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn +for x in config refs logs/refs objects info hooks packed-refs remotes rr-cache svn reftable do # create a containing directory if needed case $x in
refs.c +19 -10
@@ -20,10 +20,15 @@#include "repository.h" #include "sigchain.h" +const char *default_ref_storage (void ) +{ +return git_env_bool ("GIT_TEST_REFTABLE" ,0 ) ?"reftable" :"files" ; +} +/* * List of all available backends */ -static struct ref_storage_be *refs_backends = &refs_be_files; +static struct ref_storage_be *refs_backends = &refs_be_reftable;static struct ref_storage_be *find_ref_storage_backend (const char *name) {
@@ -1860,13 +1865,13 @@ static struct ref_store *lookup_ref_store_map(struct hashmap *map,* gitdir. */ static struct ref_store *ref_store_init (struct repository *repo, -const char *gitdir, +const char *gitdir,const char *be_name,unsigned int flags) { -const char *be_name ="files" ; -struct ref_storage_be *be =find_ref_storage_backend (be_name); +struct ref_storage_be *be;struct ref_store *refs; + be =find_ref_storage_backend (be_name);if (!be)BUG ("reference backend %s is unknown" , be_name);
@@ -1882,7 +1887,11 @@ struct ref_store *get_main_ref_store(struct repository *r)if (!r->gitdir)BUG ("attempting to get main_ref_store outside of repository" ); - r->refs_private =ref_store_init (r, r->gitdir,REF_STORE_ALL_CAPS ); + r->refs_private =ref_store_init (r, r->gitdir, + r->ref_storage_format ? + r->ref_storage_format : +default_ref_storage (), +REF_STORE_ALL_CAPS ); r->refs_private =maybe_debug_wrap_ref_store (r->gitdir, r->refs_private);return r->refs_private; }
@@ -1950,7 +1959,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule)free (subrepo); goto done; } - refs =ref_store_init (subrepo, submodule_sb.buf, + refs =ref_store_init (subrepo, submodule_sb.buf,default_ref_storage (),REF_STORE_READ |REF_STORE_ODB );register_ref_store_map (&submodule_ref_stores,"submodule" , refs, submodule);
@@ -1964,6 +1973,7 @@ done:struct ref_store *get_worktree_ref_store (const struct worktree *wt) { +const char *format =default_ref_storage ();struct ref_store *refs;const char *id;
@@ -1978,11 +1988,10 @@ struct ref_store *get_worktree_ref_store(const struct worktree *wt)if (wt->id) refs =ref_store_init (the_repository,git_common_path ("worktrees/%s" , wt->id), -REF_STORE_ALL_CAPS ); + format,REF_STORE_ALL_CAPS );else - refs =ref_store_init (the_repository, -get_git_common_dir (), -REF_STORE_ALL_CAPS ); + refs =ref_store_init (the_repository,get_git_common_dir (), + format,REF_STORE_ALL_CAPS );if (refs) register_ref_store_map(&worktree_ref_stores, "worktree",
refs.h +3 -0
@@ -11,6 +11,9 @@ struct string_list;struct string_list_item ;struct worktree ; +/* Returns the ref storage backend to use by default. */ +const char *default_ref_storage (void ); +/* * Resolve a reference, recursively following symbolic refererences. *
refs/refs-internal.h +1 -0
@@ -679,6 +679,7 @@ struct ref_storage_be { };extern struct ref_storage_be refs_be_files; +extern struct ref_storage_be refs_be_reftable;extern struct ref_storage_be refs_be_packed;/*
refs/reftable-backend.c new file +1898 -0
1,898 lines not on this page · raw message
repository.c +2 -0
@@ -185,6 +185,8 @@ int repo_init(struct repository *repo,if (worktree)repo_set_worktree (repo, worktree); + repo->ref_storage_format =xstrdup_or_null (format.ref_storage); +clear_repository_format (&format);return 0 ;
repository.h +3 -0
@@ -82,6 +82,9 @@ struct repository {*/ struct ref_store *refs_private; +/* The format to use for the ref database. */ +char *ref_storage_format; +/* * Contains path to often used file names. */
setup.c +8 -0
@@ -563,6 +563,9 @@ static enum extension_result handle_extension(const char *var, "extensions.objectformat", value); data->hash_algo = format;return EXTENSION_OK ; + }else if (!strcmp (ext,"refstorage" )) { + data->ref_storage =xstrdup (value); +return EXTENSION_OK ; }return EXTENSION_UNKNOWN ; }
@@ -713,6 +716,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);init_repository_format (format); }
@@ -1377,6 +1381,8 @@ const char *setup_git_directory_gently(int *nongit_ok) the_repository->repository_format_partial_clone = repo_fmt.partial_clone; repo_fmt.partial_clone =NULL ; + the_repository->ref_storage_format = +xstrdup_or_null (repo_fmt.ref_storage); } }/*
@@ -1465,6 +1471,8 @@ void check_repository_format(struct repository_format *fmt)repo_set_hash_algo (the_repository, fmt->hash_algo); the_repository->repository_format_partial_clone =xstrdup_or_null (fmt->partial_clone); +/* XXX why is repo->ref_storage_format set in multiple places?! */ + the_repository->ref_storage_format =xstrdup_or_null (fmt->ref_storage);clear_repository_format (&repo_fmt); }
t/t0031-reftable.sh new file +380 -0
380 lines not on this page · raw message
t/t1409-avoid-packing-refs.sh +6 -0
@@ -4,6 +4,12 @@ test_description='avoid rewriting packed-refs unnecessarily' . ./test-lib.sh +if test_have_prereq !REFFILES +then + skip_all='skipping pack-refs tests; need files backend' + test_done +fi + # Add an identifying mark to the packed-refs file header line. This # shouldn't upset readers, and it should be omitted if the file is # ever rewritten.
t/t1450-fsck.sh +6 -0
@@ -8,6 +8,12 @@ test_description='git fsck random collection of tests . ./test-lib.sh +if test_have_prereq !REFFILES +then + skip_all='skipping tests; incompatible with reftable' + test_done +fi + test_expect_success setup ' git config gc.auto 0 && git config i18n.commitencoding ISO-8859-1 &&
t/t3210-pack-refs.sh +6 -0
@@ -14,6 +14,12 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME . ./test-lib.sh +if test_have_prereq !REFFILES +then + skip_all='skipping pack-refs tests; requires files ref backend' + test_done +fi + test_expect_success 'enable reflogs' ' git config core.logallrefupdates true '
t/test-lib.sh +6 -1
@@ -1604,7 +1604,12 @@ parisc* | hppa*) ;; esac -test_set_prereq REFFILES +if test -n "$GIT_TEST_REFTABLE" +then + test_set_prereq !REFFILES +else + test_set_prereq REFFILES +fi ( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1 test -z "$NO_PERL" && test_set_prereq PERL
base-commit: a9cd6a0c5981a7d3c9865cf1966243c82f93e58e
Signature · 1 line
gitgitgadget