Introduce `refStorage` extension
cover Introduce `refStorage` extension
Hi,
this patch series introduces a new refStorage extension and related tooling. While there is only a single user-visible ref backend for now, this extension will eventually allow us to determine which backend shall be used for a repository. Furthermore, the series introduces tooling so that the ref storage format becomes more accessible.
The series is structured as follows:
- Patches 1 - 3: preliminary refactorings that prepare for the introduction of the ref storage format.
- Patches 4 - 6: wire up the ref format when setting up the repository and creating a new one.
- Patches 7 - 8: introduction of envvars to control the ref format globally.
- Patches 9 - 11: amend our tooling to know how to access and set the ref storage format.
- Patch 12: small patch for t9500 to make it handle ref storage extensions in the future.
I've been kind of torn regarding how to name this in the user-facing parts. Even though the extension is called "refStorage", I rather opted to use "ref format" in the user-facing parts, which is similar in nature to the "object format". Changing the extension to be called "refFormat" would cause issues for JGit though, so it's likely not an option to change it now.
Anyway, I'd appreciate your thoughts and proposals and am happy to rename the user-facing parts if we get to a preferable agreement.
This series depends on 18c9cb7524 (builtin/clone: create the refdb with the correct object format, 2023-12-12) because it relies on the more careful setup of the repository's refdb during clones.
Thanks!
Patrick
Patrick Steinhardt (12):
t: introduce DEFAULT_REPO_FORMAT prereq
worktree: skip reading HEAD when repairing worktrees
refs: refactor logic to look up storage backends
setup: start tracking ref storage format when
setup: set repository's formats on init
setup: introduce "extensions.refStorage" extension
setup: introduce GIT_DEFAULT_REF_FORMAT envvar
t: introduce GIT_TEST_DEFAULT_REF_FORMAT envvar builtin/rev-parse: introduce --show-ref-format flag builtin/init: introduce --ref-format= value flag builtin/clone: introduce --ref-format= value flag
t9500: write "extensions.refstorage" into config
Documentation/config/extensions.txt | 11 +++ Documentation/git-clone.txt | 6 ++ Documentation/git-init.txt | 7 ++ Documentation/git-rev-parse.txt | 3 + Documentation/git.txt | 5 ++ Documentation/ref-storage-format.txt | 1 + .../technical/repository-version.txt | 5 ++ builtin/clone.c | 17 ++++- builtin/init-db.c | 15 +++- builtin/rev-parse.c | 4 ++ refs.c | 34 ++++++--- refs.h | 4 ++ refs/debug.c | 1 - refs/files-backend.c | 1 - refs/packed-backend.c | 1 - refs/refs-internal.h | 1 - repository.c | 1 + repository.h | 6 ++ setup.c | 63 +++++++++++++++-- setup.h | 9 ++- t/README | 3 + t/t0001-init.sh | 70 +++++++++++++++++++ t/t1500-rev-parse.sh | 17 +++++ t/t3200-branch.sh | 2 +- t/t5601-clone.sh | 17 +++++ t/t9500-gitweb-standalone-no-errors.sh | 5 ++ t/test-lib-functions.sh | 5 ++ t/test-lib.sh | 15 +++- worktree.c | 24 ++++--- 29 files changed, 318 insertions(+), 35 deletions(-) create mode 100644 Documentation/ref-storage-format.txt
Signature · 1 line
2.43.GIT
PATCH 1/12 t: introduce DEFAULT_REPO_FORMAT prereq
A limited number of tests require repositories to have the default repository format or otherwise they would fail to run, e.g. because they fail to detect the correct hash function. While the hash function is the only extension right now that creates problems like this, we are about to add a second extensions for the ref format.
Introduce a new DEFAULT_REPO_FORMAT prereq that can easily be amended whenever we add new format extensions. Next to making any such changes easier on us, the prerequisite's name should also help to clarify the intent better.
--- t/t3200-branch.sh | 2 +- t/test-lib.sh | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-)
t/t3200-branch.sh +1 -1
@@ -519,7 +519,7 @@ EOF mv .git/config .git/config-saved -test_expect_success SHA1 'git branch -m q q2 without config should succeed' ' +test_expect_success DEFAULT_REPO_FORMAT 'git branch -m q q2 without config should succeed' ' git branch -m q q2 && git branch -m q2 q '
t/test-lib.sh +4 -0
@@ -1936,6 +1936,10 @@ test_lazy_prereq SHA1 ' esac ' +test_lazy_prereq DEFAULT_REPO_FORMAT ' + test_have_prereq SHA1 +' + # Ensure that no test accidentally triggers a Git command # that runs the actual maintenance scheduler, affecting a user's # system permanently.
Signature · 1 line
2.43.GIT
Re: [PATCH 01/12] t: introduce DEFAULT_REPO_FORMAT prereq
Quoted 6 lines · Patrick Steinhardt
A limited number of tests require repositories to have the default repository format or otherwise they would fail to run, e.g. because they fail to detect the correct hash function. While the hash function is the only extension right now that creates problems like this, we are about to add a second extensions for the ref format.
Nit: s/extensions/extension
t/t3200-branch.sh EOF · line 525
Quoted 5 lines
t/t3200-branch.sh +1
-1-test_expect_success SHA1 'git branch -m q q2 without config should succeed' ' +test_expect_success DEFAULT_REPO_FORMAT 'git branch -m q q2 without config should succeed' ' git branch -m q q2 && git branch -m q2 q '
My only concern is whether we'll end up blindly adding DEFAULT_REPO_FORMAT for tests where only SHA1 is a prereq or only where the new format extension is a prereq.
Re: [PATCH 01/12] t: introduce DEFAULT_REPO_FORMAT prereq
Quoted 1 line · Karthik Nayak
Patrick Steinhardt <[email protected]> writes:
[snip]
Quoted 9 lines
t/t3200-branch.sh EOF · line 525
Quoted 5 lines
t/t3200-branch.sh +1
-1-test_expect_success SHA1 'git branch -m q q2 without config should succeed' ' +test_expect_success DEFAULT_REPO_FORMAT 'git branch -m q q2 without config should succeed' ' git branch -m q q2 && git branch -m q2 q 'My only concern is whether we'll end up blindly adding
DEFAULT_REPO_FORMATfor tests where only SHA1 is a prereq or only where the new format extension is a prereq.
Yeah, this is of course a possibility with this new prereq. I don't really know what to do about it though, and think that it does serve a sensible purpose. So... dunno.
Patrick
PATCH 2/12 worktree: skip reading HEAD when repairing worktrees
In reply to Patrick SteinhardtWhen calling git init --separate-git-dir=<new-path> on a preexisting repository, then we move the Git directory of that repository to the new path specified by the user. If there are worktrees present in the Git repository, we need to repair the worktrees so that their gitlinks point to the new location of the repository.
This repair logic will load repositories via get_worktrees(), which will enumerate up and initialize all worktrees. Part of initialization is logic that we resolve their respective worktree HEADs, even though that information may not actually be needed in the end by all callers.
In the context of git-init(1) this is about to become a problem, because we do not have a repository that was set up via setup_git_directory() or friends. Consequentially, it is not yet fully initialized at the time of calling repair_worktrees(), and properly setting up all parts of the repository in init_db() before we repair worktrees is not an easy thing to do. While this is okay right now where we only have a single reference backend in Git, once we gain a second one we would be trying to look up the worktree HEADs before we have figured out the reference format, which does not work.
We do not require the worktree HEADs at all to repair worktrees. So let's fix issue this by skipping over the step that reads them.
--- worktree.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-)
worktree.c +16 -8
@@ -51,7 +51,7 @@ static void add_head_info(struct worktree *wt)/** * get the main worktree */ -static struct worktree *get_main_worktree (void ) +static struct worktree *get_main_worktree (int skip_reading_head) {struct worktree *worktree =NULL ;struct strbuf worktree_path =STRBUF_INIT ;
@@ -70,11 +70,13 @@ static struct worktree *get_main_worktree(void)*/ worktree->is_bare = (is_bare_repository_cfg ==1 ) ||is_bare_repository (); -add_head_info (worktree); +if (!skip_reading_head) +add_head_info (worktree);return worktree; } -static struct worktree *get_linked_worktree (const char *id) +static struct worktree *get_linked_worktree (const char *id, +int skip_reading_head) {struct worktree *worktree =NULL ;struct strbuf path =STRBUF_INIT ;
@@ -93,7 +95,8 @@ static struct worktree *get_linked_worktree(const char *id)CALLOC_ARRAY (worktree,1 ); worktree->path =strbuf_detach (&worktree_path,NULL ); worktree->id =xstrdup (id); -add_head_info (worktree); +if (!skip_reading_head) +add_head_info (worktree); done:strbuf_release (&path);
@@ -118,7 +121,7 @@ static void mark_current_worktree(struct worktree **worktrees)free (git_dir); } -struct worktree **get_worktrees (void ) +static struct worktree **get_worktrees_internal (int skip_reading_head) {struct worktree **list =NULL ;struct strbuf path =STRBUF_INIT ;
@@ -128,7 +131,7 @@ struct worktree **get_worktrees(void)ALLOC_ARRAY (list, alloc); - list[counter++] =get_main_worktree (); + list[counter++] =get_main_worktree (skip_reading_head);strbuf_addf (&path,"%s/worktrees" ,get_git_common_dir ()); dir =opendir (path.buf);
@@ -137,7 +140,7 @@ struct worktree **get_worktrees(void)while ((d =readdir_skip_dot_and_dotdot (dir)) !=NULL ) {struct worktree *linked =NULL ; -if ((linked =get_linked_worktree (d->d_name))) { +if ((linked =get_linked_worktree (d->d_name, skip_reading_head))) {ALLOC_GROW (list, counter +1 , alloc); list[counter++] = linked; }
@@ -151,6 +154,11 @@ struct worktree **get_worktrees(void)return list; } +struct worktree **get_worktrees (void ) +{ +return get_worktrees_internal (0 ); +} +const char *get_worktree_git_dir (const struct worktree *wt) {if (!wt)
@@ -591,7 +599,7 @@ static void repair_noop(int iserr UNUSED,void repair_worktrees (worktree_repair_fn fn,void *cb_data) { -struct worktree **worktrees =get_worktrees (); +struct worktree **worktrees =get_worktrees_internal (1 );struct worktree **wt = worktrees +1 ;/* +1 skips main worktree */ if (!fn)
Signature · 1 line
2.43.GIT
Re: [PATCH 02/12] worktree: skip reading HEAD when repairing worktrees
Quoted 6 lines · Patrick Steinhardt
When calling
git init --separate-git-dir=<new-path>on a preexisting repository, then we move the Git directory of that repository to the new path specified by the user. If there are worktrees present in the Git repository, we need to repair the worktrees so that their gitlinks point to the new location of the repository.
Nit: s/then we/we
PATCH 3/12 refs: refactor logic to look up storage backends
In reply to Patrick SteinhardtIn order to look up ref storage backends, we're currently using a linked list of backends, where each backend is expected to set up its next pointer to the next ref storage backend. This is kind of a weird setup as backends need to be aware of other backends without much of a reason.
Refactor the code so that the array of backends is centrally defined in "refs.c", where each backend is now identified by an integer constant. Expose functions to translate from those integer constants to the name and vice versa, which will be required by subsequent patches.
--- refs.c | 34 +++++++++++++++++++++++++--------- refs.h | 3 +++ refs/debug.c | 1 - refs/files-backend.c | 1 - refs/packed-backend.c | 1 - refs/refs-internal.h | 1 - repository.h | 3 +++ 7 files changed, 31 insertions(+), 13 deletions(-)
refs.c +25 -9
@@ -33,17 +33,33 @@/* * List of all available backends */ -static struct ref_storage_be *refs_backends = &refs_be_files; +static const struct ref_storage_be *refs_backends[] = { + [REF_STORAGE_FORMAT_FILES ] = &refs_be_files, +}; -static struct ref_storage_be *find_ref_storage_backend (const char *name) +static const struct ref_storage_be *find_ref_storage_backend (int ref_storage_format) { -struct ref_storage_be *be; -for (be = refs_backends; be; be = be->next) -if (!strcmp (be->name, name)) -return be; +if (ref_storage_format && ref_storage_format <ARRAY_SIZE (refs_backends)) +return refs_backends[ref_storage_format];return NULL ; } +int ref_storage_format_by_name (const char *name) +{ +for (int i =0 ; i <ARRAY_SIZE (refs_backends); i++) +if (refs_backends[i] && !strcmp (refs_backends[i]->name, name)) +return i; +return REF_STORAGE_FORMAT_UNKNOWN ; +} + +const char *ref_storage_format_to_name (int ref_storage_format) +{ +const struct ref_storage_be *be =find_ref_storage_backend (ref_storage_format); +if (!be) +return "unknown" ; +return be->name; +} +/* * How to handle various characters in refnames: * 0: An acceptable character for refs
@@ -2029,12 +2045,12 @@ static struct ref_store *ref_store_init(struct repository *repo,const char *gitdir, unsigned int flags) { - const char *be_name = "files"; -struct ref_storage_be *be =find_ref_storage_backend (be_name); + int format = REF_STORAGE_FORMAT_FILES; +const struct ref_storage_be *be =find_ref_storage_backend (format);struct ref_store *refs;if (!be) -BUG ("reference backend %s is unknown" , be_name); +BUG ("reference backend %s is unknown" ,ref_storage_format_to_name (format)); refs = be->init (repo, gitdir, flags);return refs;
refs.h +3 -0
@@ -11,6 +11,9 @@ struct string_list;struct string_list_item ;struct worktree ; +int ref_storage_format_by_name (const char *name); +const char *ref_storage_format_to_name (int ref_storage_format); +/* * Resolve a reference, recursively following symbolic refererences. *
refs/debug.c +0 -1
@@ -426,7 +426,6 @@ static int debug_reflog_expire(struct ref_store *ref_store, const char *refname, }struct ref_storage_be refs_be_debug = { - .next =NULL , .name ="debug" , .init =NULL , .init_db = debug_init_db,
refs/files-backend.c +0 -1
@@ -3241,7 +3241,6 @@ static int files_init_db(struct ref_store *ref_store, struct strbuf *err UNUSED) }struct ref_storage_be refs_be_files = { - .next =NULL , .name ="files" , .init = files_ref_store_create, .init_db = files_init_db,
refs/packed-backend.c +0 -1
@@ -1705,7 +1705,6 @@ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_s }struct ref_storage_be refs_be_packed = { - .next =NULL , .name ="packed" , .init = packed_ref_store_create, .init_db = packed_init_db,
refs/refs-internal.h +0 -1
@@ -663,7 +663,6 @@ typedef int read_symbolic_ref_fn(struct ref_store *ref_store, const char *refnam struct strbuf *referent);struct ref_storage_be { -struct ref_storage_be *next;const char *name;ref_store_init_fn *init;ref_init_db_fn *init_db;
repository.h +3 -0
@@ -24,6 +24,9 @@ enum fetch_negotiation_setting { FETCH_NEGOTIATION_NOOP, }; +#define REF_STORAGE_FORMAT_UNKNOWN 0 +#define REF_STORAGE_FORMAT_FILES 1 +struct repo_settings {int initialized;
Signature · 1 line
2.43.GIT
Re: [PATCH 03/12] refs: refactor logic to look up storage backends
refs.c line 61
Quoted 9 lines · Patrick Steinhardt
refs.c +8
-0+ +const char *ref_storage_format_to_name (int ref_storage_format) +{ +const struct ref_storage_be *be =find_ref_storage_backend (ref_storage_format); +if (!be) +return "unknown" ; +return be->name; +}
Would it make more sense to return NULL here?
Quoted 8 lines
refs.c +1
-0+/* * How to handle various characters in refnames: * 0: An acceptable character for refs @@ -2029,12 +2045,12 @@ static struct ref_store *ref_store_init(struct repository *repo,const char *gitdir, unsigned int flags) {
refs.c static struct ref_store *ref_store_init(struct repository *repo, · line 2054
Quoted 10 lines
refs.c +3
-3-const char *be_name ="files" ; -struct ref_storage_be *be =find_ref_storage_backend (be_name); +int format =REF_STORAGE_FORMAT_FILES ; +const struct ref_storage_be *be =find_ref_storage_backend (format);struct ref_store *refs;if (!be) -BUG ("reference backend %s is unknown" , be_name); +BUG ("reference backend %s is unknown" ,ref_storage_format_to_name (format));
This doesn't really get us more information, since be == NULL, calling ref_storage_format_to_name will again call find_ref_storage_backend, which will be NULL. I wonder if its better to:
@@ -2029,12 +2045,12 @@ static struct ref_store *ref_store_init(struct repository *repo,
const char *gitdir,
unsigned int flags)
{
- const char *be_name = "files";
- struct ref_storage_be *be = find_ref_storage_backend(be_name);
+ int format = REF_STORAGE_FORMAT_FILES;
+ const struct ref_storage_be *be = find_ref_storage_backend(format);
struct ref_store *refs;
if (!be) - BUG("reference backend %s is unknown", be_name);
+ BUG("reference backend is unknown");
Re: [PATCH 03/12] refs: refactor logic to look up storage backends
Quoted 13 lines · Karthik Nayak
refs.c line 61
Quoted 9 lines · Patrick Steinhardt
refs.c +8
-0+ +const char *ref_storage_format_to_name (int ref_storage_format) +{ +const struct ref_storage_be *be =find_ref_storage_backend (ref_storage_format); +if (!be) +return "unknown" ; +return be->name; +}Would it make more sense to return NULL here?
The only purpose of this function will be to print error messages, so "unknown" seems like the better choice.
Quoted 35 lines
Quoted 7 lines
/*
* How to handle various characters in refnames:
* 0: An acceptable character for refsrefs.c +0
-0@@ -2029,12 +2045,12 @@ static struct ref_store *ref_store_init(struct repository *repo,const char *gitdir, unsigned int flags) {refs.c static struct ref_store *ref_store_init(struct repository *repo, · line 2054
Quoted 10 lines
refs.c +3
-3-const char *be_name ="files" ; -struct ref_storage_be *be =find_ref_storage_backend (be_name); +int format =REF_STORAGE_FORMAT_FILES ; +const struct ref_storage_be *be =find_ref_storage_backend (format);struct ref_store *refs;if (!be) -BUG ("reference backend %s is unknown" , be_name); +BUG ("reference backend %s is unknown" ,ref_storage_format_to_name (format));This doesn't really get us more information, since be == NULL, calling
ref_storage_format_to_namewill again callfind_ref_storage_backend, which will be NULL. I wonder if its better to:refs.c +0
-0@@ -2029,12 +2045,12 @@ static struct ref_store *ref_store_init(structrepository *repo, const char *gitdir, unsigned int flags) { - const char *be_name = "files"; - struct ref_storage_be *be = find_ref_storage_backend(be_name); + int format = REF_STORAGE_FORMAT_FILES; + const struct ref_storage_be *be = find_ref_storage_backend(format);struct ref_store *refs;if (!be) -BUG ("reference backend %s is unknown" , be_name);
Quoted 1 line
+
BUG("reference backend is unknown");
Good point, will change.
Patrick
PATCH 4/12 setup: start tracking ref storage format when
In reply to Patrick SteinhardtIn order to discern which ref storage format a repository is supposed to use we need to start setting up and/or discovering the format. This needs to happen in two separate code paths.
- The first path is when we create a repository via init_db(). When we are re-initializing a preexisting repository we need to retain the previously used ref storage format -- if the user asked for a different format then this indicates an erorr and we error out. Otherwise we either initialize the repository with the format asked for by the user or the default format, which currently is the "files" backend.
- The second path is when discovering repositories, where we need to read the config of that repository. There is not yet any way to configure something other than the "files" backend, so we can just blindly set the ref storage format to this backend.
Wire up this logic so that we have the ref storage format always readily available when needed. As there is only a single backend and because it is not configurable we cannot yet verify that this tracking works as expected via tests, but tests will be added in subsequent commits. To countermand this ommission now though, raise a BUG() in case the ref storage format is not set up properly in ref_store_init().
--- builtin/clone.c | 5 +++-- builtin/init-db.c | 4 +++- refs.c | 6 +++--- refs.h | 1 + repository.c | 1 + repository.h | 3 +++ setup.c | 26 +++++++++++++++++++++++--- setup.h | 6 +++++- 8 files changed, 42 insertions(+), 10 deletions(-)
builtin/clone.c +3 -2
@@ -1105,7 +1105,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)* repository, and reference backends may persist that information into * their on-disk data structures. */ -init_db (git_dir, real_git_dir, option_template,GIT_HASH_UNKNOWN ,NULL , +init_db (git_dir, real_git_dir, option_template,GIT_HASH_UNKNOWN , +REF_STORAGE_FORMAT_UNKNOWN ,NULL , do_not_override_repo_unix_permissions,INIT_DB_QUIET |INIT_DB_SKIP_REFDB );if (real_git_dir) {
@@ -1290,7 +1291,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) hash_algo =hash_algo_by_ptr (transport_get_hash_algo (transport));initialize_repository_version (hash_algo,1 );repo_set_hash_algo (the_repository, hash_algo); -create_reference_database (NULL ,1 ); +create_reference_database (the_repository->ref_storage_format,NULL ,1 );/* * Before fetching from the remote, download and install bundle
builtin/init-db.c +3 -1
@@ -11,6 +11,7 @@#include "object-file.h" #include "parse-options.h" #include "path.h" +#include "refs.h" #include "setup.h" #include "strbuf.h"
@@ -236,5 +237,6 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) flags |=INIT_DB_EXIST_OK ;return init_db (git_dir, real_git_dir, template_dir, hash_algo, - initial_branch, init_shared_repository, flags); +REF_STORAGE_FORMAT_UNKNOWN , initial_branch, + init_shared_repository, flags); }
refs.c +3 -3
@@ -2045,12 +2045,12 @@ static struct ref_store *ref_store_init(struct repository *repo, const char *gitdir, unsigned int flags) { - int format = REF_STORAGE_FORMAT_FILES; -const struct ref_storage_be *be =find_ref_storage_backend (format); +const struct ref_storage_be *be;struct ref_store *refs; + be =find_ref_storage_backend (repo->ref_storage_format);if (!be) -BUG ("reference backend %s is unknown" ,ref_storage_format_to_name (format)); +BUG ("reference backend is unknown" ); refs = be->init (repo, gitdir, flags);return refs;
refs.h +1 -0
@@ -11,6 +11,7 @@ struct string_list;struct string_list_item ;struct worktree ; +int default_ref_storage_format (void );int ref_storage_format_by_name (const char *name);const char *ref_storage_format_to_name (int ref_storage_format);
repository.c +1 -0
@@ -184,6 +184,7 @@ int repo_init(struct repository *repo, goto error;repo_set_hash_algo (repo, format.hash_algo); + repo->ref_storage_format = format.ref_storage_format; repo->repository_format_worktree_config = format.worktree_config;/* take ownership of format.partial_clone */
repository.h +3 -0
@@ -163,6 +163,9 @@ struct repository {/* Repository's current hash algorithm, as serialized on disk. */ const struct git_hash_algo *hash_algo; +/* Repository's reference storage format, as serialized on disk. */ +int ref_storage_format; +/* A unique-id for tracing purposes. */ int trace2_repo_id;
setup.c +23 -3
@@ -1564,6 +1564,8 @@ const char *setup_git_directory_gently(int *nongit_ok) }if (startup_info->have_repository) {repo_set_hash_algo (the_repository, repo_fmt.hash_algo); + the_repository->ref_storage_format = + repo_fmt.ref_storage_format; the_repository->repository_format_worktree_config = repo_fmt.worktree_config;/* take ownership of repo_fmt.partial_clone */
@@ -1657,6 +1659,8 @@ void check_repository_format(struct repository_format *fmt)check_repository_format_gently (get_git_dir (), fmt,NULL ); startup_info->have_repository =1 ;repo_set_hash_algo (the_repository, fmt->hash_algo); + the_repository->ref_storage_format = + fmt->ref_storage_format; the_repository->repository_format_worktree_config = fmt->worktree_config; the_repository->repository_format_partial_clone =
@@ -1897,7 +1901,8 @@ static int is_reinit(void)return ret; } -void create_reference_database (const char *initial_branch,int quiet) +void create_reference_database (int ref_storage_format, +const char *initial_branch,int quiet) {struct strbuf err =STRBUF_INIT ;int reinit =is_reinit ();
@@ -1917,6 +1922,7 @@ void create_reference_database(const char *initial_branch, int quiet)safe_create_dir (git_path ("refs" ),1 );adjust_shared_perm (git_path ("refs" )); + the_repository->ref_storage_format = ref_storage_format;if (refs_init_db (&err))die ("failed to set up refs db: %s" , err.buf);
@@ -2135,8 +2141,20 @@ static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash } } +static void validate_ref_storage_format (struct repository_format *repo_fmt,int 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; + } +} +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, +int ref_storage_format,const char *initial_branch,int init_shared_repository,unsigned int flags) {int reinit;
@@ -2179,13 +2197,15 @@ int init_db(const char *git_dir, const char *real_git_dir,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, &repo_fmt, prev_bare_repository, init_shared_repository);if (!(flags &INIT_DB_SKIP_REFDB )) -create_reference_database (initial_branch, flags &INIT_DB_QUIET ); +create_reference_database (repo_fmt.ref_storage_format, + initial_branch, flags &INIT_DB_QUIET );create_object_directory ();if (get_shared_repository ()) {
setup.h +5 -1
@@ -115,6 +115,7 @@ struct repository_format {int worktree_config;int is_bare;int hash_algo; +int ref_storage_format;int sparse_index;char *work_tree;struct string_list unknown_extensions;
@@ -131,6 +132,7 @@ struct repository_format { .version = -1, \ .is_bare = -1, \ .hash_algo = GIT_HASH_SHA1, \ + .ref_storage_format = REF_STORAGE_FORMAT_FILES, \ .unknown_extensions = STRING_LIST_INIT_DUP, \ .v1_only_extensions = STRING_LIST_INIT_DUP, \ }
@@ -175,10 +177,12 @@ void check_repository_format(struct repository_format *fmt);int init_db (const char *git_dir,const char *real_git_dir,const char *template_dir,int hash_algo, +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 create_reference_database (const char *initial_branch,int quiet); +void create_reference_database (int ref_storage_format, +const char *initial_branch,int quiet);/* * NOTE NOTE NOTE!!
Signature · 1 line
2.43.GIT
Re: [PATCH 04/12] setup: start tracking ref storage format when
There is a topic in-flight that introduces the .compat_hash_algo member to the repo_fmt structure. Seeing a conflict resolution like the attached (there are many others that are similar in spirit), I have to wonder if we want to add repo_set_ref_storage_format() helper function. There are many assignments to .ref_storage_format member after this series is applied.
Note that I haven't read the series in full, so take the above with a grain of salt---it might turn out to be that direct assignment is more desirable, I dunno.
Thanks.
setup.c +0 -0
@@@ ... }if (startup_info->have_repository) {repo_set_hash_algo (the_repository, repo_fmt.hash_algo); +repo_set_compat_hash_algo (the_repository, + repo_fmt.compat_hash_algo); + the_repository->ref_storage_format = + repo_fmt.ref_storage_format; the_repository->repository_format_worktree_config = repo_fmt.worktree_config;/* take ownership of repo_fmt.partial_clone */
Re: [PATCH 04/12] setup: start tracking ref storage format when
Quoted 7 lines · Junio C Hamano
There is a topic in-flight that introduces the .compat_hash_algo member to the
repo_fmtstructure. Seeing a conflict resolution like the attached (there are many others that are similar in spirit), I have to wonder if we want to addrepo_set_ref_storage_format()helper function. There are many assignments to .ref_storage_format member after this series is applied.
Quoted 5 lines
Note that I haven't read the series in full, so take the above with a grain of salt---it might turn out to be that direct assignment is more desirable, I dunno.
Thanks.
Wrapping this in a repo_set_ref_storage_format() doesn't add much for now, but on the other hand it also doesn't hurt and is more in line with how we set the other formats. This could also be useful in the future to have a central place for additional sanity checks, if required. So yeah, let's do it.
Makes me wonder whether we should then also add the following diff to "setup: set repository's format on init" when both topics are being merged together:
setup.c +1 -0
@@ -2210,6 +2210,7 @@ int init_db(const char *git_dir, const char *real_git_dir,* format we can update the repository's settings accordingly. */ repo_set_hash_algo (the_repository, repo_fmt.hash_algo); +repo_set_compat_hash_algo (the_repository, repo_fmt.compat_hash_algo);repo_set_ref_storage_format (the_repository, repo_fmt.ref_storage_format);if (!(flags &INIT_DB_SKIP_REFDB ))
Patrick
Re: [PATCH 04/12] setup: start tracking ref storage format when
Quoted 9 lines · Patrick Steinhardt
Makes me wonder whether we should then also add the following diff to "setup: set repository's format on init" when both topics are being merged together:
setup.c +0
-0
setup.c int init_db(const char *git_dir, const char *real_git_dir, · line 2216
Quoted 7 lines
setup.c +1
-0@@ -2210,6 +2210,7 @@ int init_db(const char *git_dir, const char *real_git_dir,* format we can update the repository's settings accordingly. */ repo_set_hash_algo (the_repository, repo_fmt.hash_algo); +repo_set_compat_hash_algo (the_repository, repo_fmt.compat_hash_algo);repo_set_ref_storage_format (the_repository, repo_fmt.ref_storage_format);if (!(flags &INIT_DB_SKIP_REFDB ))
Shouldn't that come from the series that wants .compat_hash_algo in the repo_fmt structure, whichever it is, not added by an evil merge?
Re: [PATCH 04/12] setup: start tracking ref storage format when
Quoted 17 lines · Junio C Hamano
Quoted 15 lines · Patrick Steinhardt
Makes me wonder whether we should then also add the following diff to "setup: set repository's format on init" when both topics are being merged together:
setup.c +1
-0@@ -2210,6 +2210,7 @@ int init_db(const char *git_dir, const char *real_git_dir,* format we can update the repository's settings accordingly. */ repo_set_hash_algo (the_repository, repo_fmt.hash_algo); +repo_set_compat_hash_algo (the_repository, repo_fmt.compat_hash_algo);repo_set_ref_storage_format (the_repository, repo_fmt.ref_storage_format);
Quoted 4 lines
setup.c int init_db(const char *git_dir, const char *real_git_dir, · line 2216
Quoted 1 line
setup.c +0
-0if (!(flags &INIT_DB_SKIP_REFDB ))Shouldn't that come from the series that wants .compat_hash_algo in the
repo_fmtstructure, whichever it is, not added by an evil merge?
Well, the above code is newly added by my series to ensure that init_db() results in a properly initialized repo upon return. So the compat hash algo series cannot yet call repo_set_compat_hash_algo() because the code site doesn't exist, whereas my series cannot yet add the call because there is no compat hash algo yet.
So depending on which series lands first we'll either have to adapt the respective other series or do an evil merge.
Patrick
Re: [PATCH 04/12] setup: start tracking ref storage format when
In reply to Patrick SteinhardtQuoted 8 lines · Patrick Steinhardt
In order to discern which ref storage format a repository is supposed to use we need to start setting up and/or discovering the format. This needs to happen in two separate code paths.
- The first path is when we create a repository via
init_db(). When we are re-initializing a preexisting repository we need to retain the previously used ref storage format -- if the user asked for a different format then this indicates an erorr and we error out.
Nit: s/erorr/error
Quoted 25 lines
refs.c +3
-3@@ -2045,12 +2045,12 @@ static struct ref_store *ref_store_init(struct repository *repo, const char *gitdir, unsigned int flags) { - int format = REF_STORAGE_FORMAT_FILES; -const struct ref_storage_be *be =find_ref_storage_backend (format); +const struct ref_storage_be *be;struct ref_store *refs; + be =find_ref_storage_backend (repo->ref_storage_format);if (!be) -BUG ("reference backend %s is unknown" ,ref_storage_format_to_name (format)); +BUG ("reference backend is unknown" ); refs = be->init (repo, gitdir, flags);return refs;refs.h +0
-0
refs.h struct string_list; · line 15
Quoted 5 lines
refs.h +1
-0@@ -11,6 +11,7 @@ struct string_list;struct string_list_item ;struct worktree ; +int default_ref_storage_format (void );
Is this used/defined somewhere?
Re: [PATCH 04/12] setup: start tracking ref storage format when
Quoted 29 lines · Karthik Nayak
Quoted 28 lines · Patrick Steinhardt
refs.c +3
-3@@ -2045,12 +2045,12 @@ static struct ref_store *ref_store_init(struct repository *repo, const char *gitdir, unsigned int flags) { - int format = REF_STORAGE_FORMAT_FILES; -const struct ref_storage_be *be =find_ref_storage_backend (format); +const struct ref_storage_be *be;struct ref_store *refs; + be =find_ref_storage_backend (repo->ref_storage_format);if (!be) -BUG ("reference backend %s is unknown" ,ref_storage_format_to_name (format)); +BUG ("reference backend is unknown" ); refs = be->init (repo, gitdir, flags);return refs;refs.h +0
-0@@ -11,6 +11,7 @@ struct string_list;struct string_list_item ;struct worktree ;
Quoted 4 lines
refs.h struct string_list; · line 17
Quoted 2 lines
refs.h +1
-0+int default_ref_storage_format (void );Is this used/defined somewhere?
No, it's a leftover from a previous iteration. Will drop.
Patrick
PATCH 5/12 setup: set repository's formats on init
In reply to Patrick SteinhardtThe proper hash algorithm and ref storage format that will be used for a newly initialized repository will be figured out in init_db() via validate_hash_algorithm() and validate_ref_storage_format(). Until now though, we never set up the hash algorithm or ref storage format of the_repository accordingly.
There are only two callsites of init_db(), one in git-init(1) and one in git-clone(1). The former function doesn't care for the formats to be set up properly because it never access the repository after calling the function in the first place.
For git-clone(1) it's a different story though, as we call init_db() before listing remote refs. While we do indeed have the wrong hash function in the_repository when init_db() sets up a non-default object format for the repository, it never mattered because we adjust the hash after learning about the remote's hash function via the listed refs.
So the current state is correct for the hash algo, but it's not for the ref storage format because git-clone(1) wouldn't know to set it up properly. But instead of adjusting only the ref_storage_format, set both the hash algo and the ref storage format so that the_repository is in the correct state when init_db() exits. This is fine as we will adjust the hash later on anyway and makes it easier to reason about the end state of the_repository.
--- setup.c | 7 +++++++ 1 file changed, 7 insertions(+)
setup.c +7 -0
@@ -2203,6 +2203,13 @@ int init_db(const char *git_dir, const char *real_git_dir, &repo_fmt, prev_bare_repository, 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. +*/ + the_repository->hash_algo = &hash_algos[repo_fmt.hash_algo]; + the_repository->ref_storage_format = 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 );
Signature · 1 line
2.43.GIT
PATCH 6/12 setup: introduce "extensions.refStorage" extension
In reply to Patrick SteinhardtIntroduce 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]: <[email protected]>
--- Documentation/config/extensions.txt | 11 ++++++++ Documentation/ref-storage-format.txt | 1 + .../technical/repository-version.txt | 5 ++++ builtin/clone.c | 2 +- setup.c | 23 +++++++++++++--- setup.h | 3 ++- t/t0001-init.sh | 26 +++++++++++++++++++ t/test-lib.sh | 2 +- 8 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 Documentation/ref-storage-format.txt
Documentation/config/extensions.txt +11 -0
@@ -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
Documentation/ref-storage-format.txt new file +1 -0
@@ -0,0 +1 @@ +* `files` for loose files with packed-refs. This is the default.
Documentation/technical/repository-version.txt +5 -0
@@ -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).
builtin/clone.c +1 -1
@@ -1289,7 +1289,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 );
setup.c +20 -3
@@ -590,6 +590,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" )) { +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 ; }
@@ -1869,12 +1880,14 @@ 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,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 */
@@ -1887,6 +1900,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 )
@@ -2028,7 +2045,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" );
setup.h +2 -1
@@ -180,7 +180,8 @@ int init_db(const char *git_dir, const char *real_git_dir,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,int ref_storage_format, +int reinit);void create_reference_database (int ref_storage_format,const char *initial_branch,int quiet);
t/t0001-init.sh +26 -0
@@ -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 &&
t/test-lib.sh +1 -1
@@ -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
Signature · 1 line
2.43.GIT
PATCH 7/12 setup: introduce GIT_DEFAULT_REF_FORMAT envvar
In reply to Patrick SteinhardtIntroduce a new GIT_DEFAULT_REF_FORMAT environment variable that lets users control the default ref format used by both git-init(1) and git-clone(1). This is modeled after GIT_DEFAULT_OBJECT_FORMAT, which does the same thing for the repository's object format.
--- Documentation/git.txt | 5 +++++ setup.c | 7 +++++++ t/t0001-init.sh | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+)
Documentation/git.txt +5 -0
@@ -556,6 +556,11 @@ double-quotes and respecting backslash escapes. E.g., the value is always used. The default is "sha1". See `--object-format` in linkgit:git-init[1]. +`GIT_DEFAULT_REF_FORMAT`:: + If this variable is set, the default reference backend format for new + repositories will be set to this value. The default is "files". + See `--ref-format` in linkgit:git-init[1]. + Git Commits ~~~~~~~~~~~ `GIT_AUTHOR_NAME`::
setup.c +7 -0
@@ -2160,12 +2160,19 @@ static void validate_hash_algorithm(struct repository_format *repo_fmt, int hashstatic void validate_ref_storage_format (struct repository_format *repo_fmt,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; } }
t/t0001-init.sh +18 -0
@@ -558,6 +558,24 @@ test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with unknown back grep "invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}garbage${SQ}" err ' +test_expect_success DEFAULT_REPO_FORMAT 'init with GIT_DEFAULT_REF_FORMAT=files' ' + test_when_finished "rm -rf refformat" && + GIT_DEFAULT_REF_FORMAT=files git init refformat && + echo 0 >expect && + git -C refformat config core.repositoryformatversion >actual && + test_cmp expect actual && + test_must_fail git -C refformat config extensions.refstorage +' + +test_expect_success 'init with GIT_DEFAULT_REF_FORMAT=garbage' ' + test_when_finished "rm -rf refformat" && + cat >expect <<-EOF && + fatal: unknown ref storage format ${SQ}garbage${SQ} + EOF + test_must_fail env GIT_DEFAULT_REF_FORMAT=garbage git init refformat 2>err && + test_cmp expect err +' + test_expect_success MINGW 'core.hidedotfiles = false' ' git config --global core.hidedotfiles false && rm -rf newdir &&
Signature · 1 line
2.43.GIT
PATCH 8/12 t: introduce GIT_TEST_DEFAULT_REF_FORMAT envvar
In reply to Patrick SteinhardtIntroduce a new GIT_TEST_DEFAULT_REF_FORMAT environment variable that lets developers run the test suite with a different default ref format without impacting the ref format used by non-test Git invocations. This is modeled after GIT_TEST_DEFAULT_OBJECT_FORMAT, which does the same thing for the repository's object format.
Adapt the setup of the REFFILES test prerequisite to be conditionally set based on the default ref format.
--- t/README | 3 +++ t/test-lib-functions.sh | 5 +++++ t/test-lib.sh | 11 ++++++++++- 3 files changed, 18 insertions(+), 1 deletion(-)
t/README +3 -0
@@ -479,6 +479,9 @@ GIT_TEST_DEFAULT_HASH=<hash-algo> specifies which hash algorithm to use in the test scripts. Recognized values for <hash-algo> are "sha1" and "sha256". +GIT_TEST_DEFAULT_REF_FORMAT=<format> specifies which ref storage format +to use in the test scripts. Recognized values for <format> are "files". + GIT_TEST_NO_WRITE_REV_INDEX=<boolean>, when true disables the 'pack.writeReverseIndex' setting.
t/test-lib-functions.sh +5 -0
@@ -1659,6 +1659,11 @@ test_detect_hash () { test_hash_algo="${GIT_TEST_DEFAULT_HASH:-sha1}" } +# Detect the hash algorithm in use. +test_detect_ref_format () { + echo "${GIT_TEST_DEFAULT_REF_FORMAT:-files}" +} + # Load common hash metadata and common placeholder object IDs for use with # test_oid. test_oid_init () {
t/test-lib.sh +10 -1
@@ -542,6 +542,8 @@ export EDITOR GIT_DEFAULT_HASH="${GIT_TEST_DEFAULT_HASH:-sha1}" export GIT_DEFAULT_HASH +GIT_DEFAULT_REF_FORMAT="${GIT_TEST_DEFAULT_REF_FORMAT:-files}" +export GIT_DEFAULT_REF_FORMAT GIT_TEST_MERGE_ALGORITHM="${GIT_TEST_MERGE_ALGORITHM:-ort}" export GIT_TEST_MERGE_ALGORITHM
@@ -1745,7 +1747,14 @@ parisc* | hppa*) ;; esac -test_set_prereq REFFILES +case "$GIT_DEFAULT_REF_FORMAT" in +files) + test_set_prereq REFFILES;; +*) + echo 2>&1 "error: unknown ref format $GIT_DEFAULT_REF_FORMAT" + exit 1 + ;; +esac ( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1 test -z "$NO_CURL" && test_set_prereq LIBCURL
Signature · 1 line
2.43.GIT
PATCH 9/12 builtin/rev-parse: introduce `--show-ref-format` flag
In reply to Patrick SteinhardtIntroduce a new --show-ref-format to git-rev-parse(1) that causes it to print the ref format used by a repository.
--- Documentation/git-rev-parse.txt | 3 +++ builtin/rev-parse.c | 4 ++++ t/t1500-rev-parse.sh | 17 +++++++++++++++++ 3 files changed, 24 insertions(+)
Documentation/git-rev-parse.txt +3 -0
@@ -307,6 +307,9 @@ The following options are unaffected by `--path-format`: input, multiple algorithms may be printed, space-separated. If not specified, the default is "storage". +--show-ref-format:: + Show the reference storage format used for the repository. + Other Options ~~~~~~~~~~~~~
builtin/rev-parse.c +4 -0
@@ -1059,6 +1059,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)puts (the_hash_algo->name);continue ; } +if (!strcmp (arg,"--show-ref-format" )) { +puts (ref_storage_format_to_name (the_repository->ref_storage_format)); +continue ; + }if (!strcmp (arg,"--end-of-options" )) { seen_end_of_options =1 ;if (filter & (DO_FLAGS |DO_REVS ))
t/t1500-rev-parse.sh +17 -0
@@ -208,6 +208,23 @@ test_expect_success 'rev-parse --show-object-format in repo' ' grep "unknown mode for --show-object-format: squeamish-ossifrage" err ' +test_expect_success 'rev-parse --show-ref-format' ' + test_detect_ref_format >expect && + git rev-parse --show-ref-format >actual && + test_cmp expect actual +' + +test_expect_success 'rev-parse --show-ref-format with invalid storage' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + git config extensions.refstorage broken && + test_must_fail git rev-parse --show-ref-format 2>err && + grep "error: invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}broken${SQ}" err + ) +' + test_expect_success '--show-toplevel from subdir of working tree' ' pwd >expect && git -C sub/dir rev-parse --show-toplevel >actual &&
Signature · 1 line
2.43.GIT
PATCH 10/12 builtin/init: introduce `--ref-format=` value flag
In reply to Patrick SteinhardtIntroduce a new --ref-format value flag for git-init(1) that allows the user to specify the ref format that is to be used for a newly initialized repository.
--- Documentation/git-init.txt | 7 +++++++ builtin/init-db.c | 13 ++++++++++++- t/t0001-init.sh | 26 ++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-)
Documentation/git-init.txt +7 -0
@@ -11,6 +11,7 @@ SYNOPSIS [verse] 'git init' [-q | --quiet] [--bare] [--template=<template-directory>] [--separate-git-dir <git-dir>] [--object-format=<format>] + [--ref-format=<format>] [-b <branch-name> | --initial-branch=<branch-name>] [--shared[=<permissions>]] [<directory>]
@@ -57,6 +58,12 @@ values are 'sha1' and (if enabled) 'sha256'. 'sha1' is the default. + include::object-format-disclaimer.txt[] +--ref-format=<format>:: + +Specify the given ref storage format for the repository. The valid values are: ++ +include::ref-storage-format.txt[] + --template=<template-directory>:: Specify the directory from which templates will be used. (See the "TEMPLATE
builtin/init-db.c +12 -1
@@ -58,6 +58,7 @@ static int shared_callback(const struct option *opt, const char *arg, int unset)static const char *const init_db_usage[] = {N_ ("git init [-q | --quiet] [--bare] [--template=<template-directory>]\n" " [--separate-git-dir <git-dir>] [--object-format=<format>]\n" +" [--ref-format=<format>]\n" " [-b <branch-name> | --initial-branch=<branch-name>]\n" " [--shared[=<permissions>]] [<directory>]" ),NULL
@@ -77,8 +78,10 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)const char *template_dir =NULL ;unsigned int flags =0 ;const char *object_format =NULL ; +const char *ref_format =NULL ;const char *initial_branch =NULL ;int hash_algo =GIT_HASH_UNKNOWN ; +int ref_storage_format =REF_STORAGE_FORMAT_UNKNOWN ;int init_shared_repository =-1 ;const struct option init_db_options[] = { OPT_STRING(0, "template", &template_dir, N_("template-directory"),
@@ -96,6 +99,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) N_("override the name of the initial branch")),OPT_STRING (0 ,"object-format" , &object_format,N_ ("hash" ),N_ ("specify the hash algorithm to use" )), +OPT_STRING (0 ,"ref-format" , &ref_format,N_ ("format" ), +N_ ("specify the reference format to use" )), OPT_END() };
@@ -159,6 +164,12 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)die (_ ("unknown hash algorithm '%s'" ), object_format); } +if (ref_format) { + ref_storage_format =ref_storage_format_by_name (ref_format); +if (ref_storage_format ==REF_STORAGE_FORMAT_UNKNOWN ) +die (_ ("unknown ref storage format '%s'" ), ref_format); + } +if (init_shared_repository !=-1 )set_shared_repository (init_shared_repository);
@@ -237,6 +248,6 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) flags |=INIT_DB_EXIST_OK ;return init_db (git_dir, real_git_dir, template_dir, hash_algo, -REF_STORAGE_FORMAT_UNKNOWN , initial_branch, + ref_storage_format, initial_branch, init_shared_repository, flags); }
t/t0001-init.sh +26 -0
@@ -576,6 +576,32 @@ test_expect_success 'init with GIT_DEFAULT_REF_FORMAT=garbage' ' test_cmp expect err ' +test_expect_success 'init with --ref-format=files' ' + test_when_finished "rm -rf refformat" && + git init --ref-format=files refformat && + echo files >expect && + git -C refformat rev-parse --show-ref-format >actual && + test_cmp expect actual +' + +test_expect_success 're-init with same format' ' + test_when_finished "rm -rf refformat" && + git init --ref-format=files refformat && + git init --ref-format=files refformat && + echo files >expect && + git -C refformat rev-parse --show-ref-format >actual && + test_cmp expect actual +' + +test_expect_success 'init with --ref-format=garbage' ' + test_when_finished "rm -rf refformat" && + cat >expect <<-EOF && + fatal: unknown ref storage format ${SQ}garbage${SQ} + EOF + test_must_fail git init --ref-format=garbage refformat 2>err && + test_cmp expect err +' + test_expect_success MINGW 'core.hidedotfiles = false' ' git config --global core.hidedotfiles false && rm -rf newdir &&
Signature · 1 line
2.43.GIT
PATCH 11/12 builtin/clone: introduce `--ref-format=` value flag
In reply to Patrick SteinhardtIntroduce a new --ref-format value flag for git-clone(1) that allows the user to specify the ref format that is to be used for a newly initialized repository.
--- Documentation/git-clone.txt | 6 ++++++ builtin/clone.c | 12 +++++++++++- t/t5601-clone.sh | 17 +++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-)
Documentation/git-clone.txt +6 -0
@@ -311,6 +311,12 @@ or `--mirror` is given) The result is Git repository can be separated from working tree. +--ref-format=<ref-format:: + +Specify the given ref storage format for the repository. The valid values are: ++ +include::ref-storage-format.txt[] + -j <n>:: --jobs <n>:: The number of submodules fetched at the same time.
builtin/clone.c +11 -1
@@ -72,6 +72,7 @@ static char *remote_name = NULL;static char *option_branch =NULL ;static struct string_list option_not =STRING_LIST_INIT_NODUP ;static const char *real_git_dir; +static const char *ref_format;static char *option_upload_pack ="git-upload-pack" ;static int option_verbosity;static int option_progress =-1 ;
@@ -157,6 +158,8 @@ static struct option builtin_clone_options[] = { N_("any cloned submodules will be shallow")),OPT_STRING (0 ,"separate-git-dir" , &real_git_dir,N_ ("gitdir" ),N_ ("separate git dir from working tree" )), +OPT_STRING (0 ,"ref-format" , &ref_format,N_ ("format" ), +N_ ("specify the reference format to use" )),OPT_STRING_LIST ('c' ,"config" , &option_config,N_ ("key=value" ),N_ ("set config inside the new repository" )), OPT_STRING_LIST(0, "server-option", &server_options,
@@ -930,6 +933,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)int submodule_progress;int filter_submodules =0 ;int hash_algo; +int ref_storage_format =REF_STORAGE_FORMAT_UNKNOWN ;const int do_not_override_repo_unix_permissions =-1 ; struct transport_ls_refs_options transport_ls_refs_options =
@@ -955,6 +959,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)if (option_single_branch ==-1 ) option_single_branch = deepen ?1 :0 ; +if (ref_format) { + ref_storage_format =ref_storage_format_by_name (ref_format); +if (ref_storage_format ==REF_STORAGE_FORMAT_UNKNOWN ) +die (_ ("unknown ref storage format '%s'" ), ref_format); + } +if (option_mirror) option_bare =1 ;
@@ -1106,7 +1116,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)* their on-disk data structures. */ init_db (git_dir, real_git_dir, option_template,GIT_HASH_UNKNOWN , -REF_STORAGE_FORMAT_UNKNOWN ,NULL , + ref_storage_format,NULL , do_not_override_repo_unix_permissions,INIT_DB_QUIET |INIT_DB_SKIP_REFDB );if (real_git_dir) {
t/t5601-clone.sh +17 -0
@@ -157,6 +157,23 @@ test_expect_success 'clone --mirror does not repeat tags' ' ' +test_expect_success 'clone with files ref format' ' + test_when_finished "rm -rf ref-storage" && + git clone --ref-format=files --mirror src ref-storage && + echo files >expect && + git -C ref-storage rev-parse --show-ref-format >actual && + test_cmp expect actual +' + +test_expect_success 'clone with garbage ref format' ' + cat >expect <<-EOF && + fatal: unknown ref storage format ${SQ}garbage${SQ} + EOF + test_must_fail git clone --ref-format=garbage --mirror src ref-storage 2>err && + test_cmp expect err && + test_path_is_missing ref-storage +' + test_expect_success 'clone to destination with trailing /' ' git clone src target-1/ &&
Signature · 1 line
2.43.GIT
PATCH 12/12 t9500: write "extensions.refstorage" into config
In reply to Patrick SteinhardtIn t9500 we're writing a custom configuration that sets up gitweb. This requires us manually ensure that the repository format is configured as required, including both the repository format version and extensions. With the introduction of the "extensions.refStorage" extension we need to update the test to also write this new one.
--- t/t9500-gitweb-standalone-no-errors.sh | 5 +++++ 1 file changed, 5 insertions(+)
t/t9500-gitweb-standalone-no-errors.sh +5 -0
@@ -627,6 +627,7 @@ test_expect_success \ test_expect_success 'setup' ' version=$(git config core.repositoryformatversion) && algo=$(test_might_fail git config extensions.objectformat) && + refstorage=$(test_might_fail git config extensions.refstorage) && cat >.git/config <<-\EOF && # testing noval and alternate separator [gitweb]
@@ -637,6 +638,10 @@ test_expect_success 'setup' ' if test -n "$algo" then git config extensions.objectformat "$algo" + fi && + if test -n "$refstorage" + then + git config extensions.refstorage "$refstorage" fi '
Signature · 1 line
2.43.GIT
Re: [PATCH 12/12] t9500: write "extensions.refstorage" into config
Quoted 3 lines · Patrick Steinhardt
In t9500 we're writing a custom configuration that sets up gitweb. This requires us manually ensure that the repository format is configured as
Nit: s/requires us/requires us to/
Hello,
Quoted 7 lines · Patrick Steinhardt
Hi,
this patch series introduces a new
refStorageextension and related tooling. While there is only a single user-visible ref backend for now, this extension will eventually allow us to determine which backend shall be used for a repository. Furthermore, the series introduces tooling so that the ref storage format becomes more accessible.
Apart from small nits, the series looks good to me, thanks!
Quoted 5 lines · Karthik Nayak
Hello,
Quoted 2 lines · Patrick Steinhardt
Hi,
Quoted 7 lines
Quoted 5 lines
this patch series introduces a new
refStorageextension and related tooling. While there is only a single user-visible ref backend for now, this extension will eventually allow us to determine which backend shall be used for a repository. Furthermore, the series introduces tooling so that the ref storage format becomes more accessible.Apart from small nits, the series looks good to me, thanks!
Thanks. Hopefully we can merge it down after a quick, small, and final reroll?
v2 cover Introduce `refStorage` extension
In reply to Patrick SteinhardtHi,
this is the second version of my patch series that introduces the new refStorage extension. This extension will be used for the upcoming reftable backend.
Changes compared to v2:
- Fixed various typos in commit messages.
- Fixed redundant information when the refstorage extension's value isn't understood.
- Introduced repo_set_ref_storage_format().
Thanks for the feedback so far!
Patrick
Patrick Steinhardt (12):
t: introduce DEFAULT_REPO_FORMAT prereq
worktree: skip reading HEAD when repairing worktrees
refs: refactor logic to look up storage backends
setup: start tracking ref storage format
setup: set repository's formats on init
setup: introduce "extensions.refStorage" extension
setup: introduce GIT_DEFAULT_REF_FORMAT envvar
t: introduce GIT_TEST_DEFAULT_REF_FORMAT envvar builtin/rev-parse: introduce --show-ref-format flag builtin/init: introduce --ref-format= value flag builtin/clone: introduce --ref-format= value flag
t9500: write "extensions.refstorage" into config
Documentation/config/extensions.txt | 11 +++ Documentation/git-clone.txt | 6 ++ Documentation/git-init.txt | 7 ++ Documentation/git-rev-parse.txt | 3 + Documentation/git.txt | 5 ++ Documentation/ref-storage-format.txt | 1 + .../technical/repository-version.txt | 5 ++ builtin/clone.c | 17 ++++- builtin/init-db.c | 15 +++- builtin/rev-parse.c | 4 ++ refs.c | 34 ++++++--- refs.h | 3 + refs/debug.c | 1 - refs/files-backend.c | 1 - refs/packed-backend.c | 1 - refs/refs-internal.h | 1 - repository.c | 6 ++ repository.h | 7 ++ setup.c | 63 +++++++++++++++-- setup.h | 9 ++- t/README | 3 + t/t0001-init.sh | 70 +++++++++++++++++++ t/t1500-rev-parse.sh | 17 +++++ t/t3200-branch.sh | 2 +- t/t5601-clone.sh | 17 +++++ t/t9500-gitweb-standalone-no-errors.sh | 5 ++ t/test-lib-functions.sh | 5 ++ t/test-lib.sh | 15 +++- worktree.c | 24 ++++--- 29 files changed, 323 insertions(+), 35 deletions(-) create mode 100644 Documentation/ref-storage-format.txt
Range-diff against v1:
1: 239ca38efd ! 1: 3613439cb7 t: introduce DEFAULT_REPO_FORMAT prereq
@@ Commit message
repository format or otherwise they would fail to run, e.g. because they fail to detect the correct hash function. While the hash function is the only extension right now that creates problems like this, we are about
- to add a second extensions for the ref format.
+ to add a second extension for the ref format.
Introduce a new DEFAULT_REPO_FORMAT prereq that can easily be amended whenever we add new format extensions. Next to making any such changes
2: e895091025 ! 2: ecf4f1ddee worktree: skip reading HEAD when repairing worktrees
@@ Commit message
worktree: skip reading HEAD when repairing worktrees
When calling git init --separate-git-dir=<new-path> on a preexisting
- repository, then we move the Git directory of that repository to the new
- path specified by the user. If there are worktrees present in the Git
- repository, we need to repair the worktrees so that their gitlinks point
- to the new location of the repository.
+ repository, we move the Git directory of that repository to the new path
+ specified by the user. If there are worktrees present in the repository,
+ we need to repair the worktrees so that their gitlinks point to the new
+ location of the repository.
This repair logic will load repositories via get_worktrees(), which will enumerate up and initialize all worktrees. Part of initialization
@@ Commit message
format, which does not work.
We do not require the worktree HEADs at all to repair worktrees. So
- let's fix issue this by skipping over the step that reads them.
+ let's fix this issue by skipping over the step that reads them.
3: f712d5ef5b ! 3: 12329b99b7 refs: refactor logic to look up storage backends
@@ refs.c
- for (be = refs_backends; be; be = be->next)
- if (!strcmp(be->name, name))
- return be;
-+ if (ref_storage_format && ref_storage_format < ARRAY_SIZE(refs_backends))
++ if (ref_storage_format < ARRAY_SIZE(refs_backends))
+ return refs_backends[ref_storage_format];
return NULL;
}
@@ refs.c: static struct ref_store *ref_store_init(struct repository *repo,
if (!be)
- BUG("reference backend %s is unknown", be_name);
-+ BUG("reference backend %s is unknown", ref_storage_format_to_name(format));
++ BUG("reference backend is unknown");
refs = be->init(repo, gitdir, flags);
return refs;
4: 6564659d40 ! 4: ddd099fbaf setup: start tracking ref storage format when
@@ Metadata
Author: Patrick Steinhardt <[email protected]>
## Commit message ##
- setup: start tracking ref storage format when
+ setup: start tracking ref storage format
In order to discern which ref storage format a repository is supposed to
use we need to start setting up and/or discovering the format. This
@@ Commit message
- The first path is when we create a repository via `init_db()`. When
we are re-initializing a preexisting repository we need to retain
the previously used ref storage format -- if the user asked for a
- different format then this indicates an erorr and we error out.
+ different format then this indicates an error and we error out.
Otherwise we either initialize the repository with the format asked
for by the user or the default format, which currently is the
"files" backend.
@@ refs.c: static struct ref_store *ref_store_init(struct repository *repo,
+ be = find_ref_storage_backend(repo->ref_storage_format);
if (!be)
-- BUG("reference backend %s is unknown", ref_storage_format_to_name(format));
-+ BUG("reference backend is unknown");
-
- refs = be->init(repo, gitdir, flags);
- return refs;
-
- ## refs.h ##
-@@ refs.h: struct string_list;
- struct string_list_item;
- struct worktree;
-
-+int default_ref_storage_format(void);
- int ref_storage_format_by_name(const char *name);
- const char *ref_storage_format_to_name(int ref_storage_format);
+ BUG("reference backend is unknown");
## repository.c ##
+@@ repository.c: void repo_set_hash_algo(struct repository *repo, int hash_algo)
+ repo->hash_algo = &hash_algos[hash_algo];
+ }
+
++void repo_set_ref_storage_format(struct repository *repo, int format)
++{
++ repo->ref_storage_format = format;
++}
++
+ /*
+ * Attempt to resolve and set the provided 'gitdir' for repository 'repo'.
+ * Return 0 upon success and a non-zero value upon failure.
@@ repository.c: int repo_init(struct repository *repo,
goto error;
repo_set_hash_algo(repo, format.hash_algo);
-+ repo->ref_storage_format = format.ref_storage_format;
++ repo_set_ref_storage_format(repo, format.ref_storage_format);
repo->repository_format_worktree_config = format.worktree_config;
/* take ownership of format.partial_clone */
@@ repository.h: struct repository {
/* A unique-id for tracing purposes. */
int trace2_repo_id;
+@@ repository.h: void repo_set_gitdir(struct repository *repo, const char *root,
+ const struct set_gitdir_args *extra_args);
+ void repo_set_worktree(struct repository *repo, const char *path);
+ void repo_set_hash_algo(struct repository *repo, int algo);
++void repo_set_ref_storage_format(struct repository *repo, int format);
+ void initialize_the_repository(void);
+ RESULT_MUST_BE_USED
+ int repo_init(struct repository *r, const char *gitdir, const char *worktree);
## setup.c ##
@@ setup.c: const char *setup_git_directory_gently(int *nongit_ok)
}
if (startup_info->have_repository) {
repo_set_hash_algo(the_repository, repo_fmt.hash_algo);
-+ the_repository->ref_storage_format =
-+ repo_fmt.ref_storage_format;
++ 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 */
@@ setup.c: void check_repository_format(struct repository_format *fmt)
check_repository_format_gently(get_git_dir(), fmt, NULL);
startup_info->have_repository = 1;
repo_set_hash_algo(the_repository, fmt->hash_algo);
-+ the_repository->ref_storage_format =
-+ fmt->ref_storage_format;
++ 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 =
@@ setup.c: void create_reference_database(const char *initial_branch, int quiet)
safe_create_dir(git_path("refs"), 1);
adjust_shared_perm(git_path("refs"));
-+ the_repository->ref_storage_format = ref_storage_format;
++ repo_set_ref_storage_format(the_repository, ref_storage_format);
if (refs_init_db(&err))
die("failed to set up refs db: %s", err.buf); 5: f90a63d63c ! 5: 01a1e58a97 setup: set repository's formats on init
@@ setup.c: int init_db(const char *git_dir, const char *real_git_dir,
+ * Now that we have set up both the hash algorithm and the ref storage
+ * format we can update the repository's settings accordingly.
+ */
-+ the_repository->hash_algo = &hash_algos[repo_fmt.hash_algo];
-+ the_repository->ref_storage_format = repo_fmt.ref_storage_format;
++ 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,
6: beeb182f28 = 6: 0a586fa648 setup: introduce "extensions.refStorage" extension
7: dd91a75da4 = 7: 6d8754f73a setup: introduce GIT_DEFAULT_REF_FORMAT envvar
8: ed3bf008cd = 8: c645932f3d t: introduce GIT_TEST_DEFAULT_REF_FORMAT envvar
9: 8a3d950d69 = 9: 761d647770 builtin/rev-parse: introduce `--show-ref-format` flag
10: 4d98b53553 = 10: e382b5bf08 builtin/init: introduce `--ref-format=` value flag
11: 71cf0ce827 = 11: 257233658d builtin/clone: introduce `--ref-format=` value flag
12: bbe2fbb154 ! 12: b8cd06ec53 t9500: write "extensions.refstorage" into config @@ Commit message
t9500: write "extensions.refstorage" into config
In t9500 we're writing a custom configuration that sets up gitweb. This
- requires us manually ensure that the repository format is configured as
- required, including both the repository format version and extensions.
- With the introduction of the "extensions.refStorage" extension we need
- to update the test to also write this new one.
+ requires us to manually ensure that the repository format is configured
+ as required, including both the repository format version and
+ extensions. With the introduction of the "extensions.refStorage"
+ extension we need to update the test to also write this new one.
base-commit: e79552d19784ee7f4bbce278fe25f93fbda196fa
Signature · 1 line
2.43.GIT
PATCH v2 1/12 t: introduce DEFAULT_REPO_FORMAT prereq
A limited number of tests require repositories to have the default repository format or otherwise they would fail to run, e.g. because they fail to detect the correct hash function. While the hash function is the only extension right now that creates problems like this, we are about to add a second extension for the ref format.
Introduce a new DEFAULT_REPO_FORMAT prereq that can easily be amended whenever we add new format extensions. Next to making any such changes easier on us, the prerequisite's name should also help to clarify the intent better.
--- t/t3200-branch.sh | 2 +- t/test-lib.sh | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-)
t/t3200-branch.sh +1 -1
@@ -519,7 +519,7 @@ EOF mv .git/config .git/config-saved -test_expect_success SHA1 'git branch -m q q2 without config should succeed' ' +test_expect_success DEFAULT_REPO_FORMAT 'git branch -m q q2 without config should succeed' ' git branch -m q q2 && git branch -m q2 q '
t/test-lib.sh +4 -0
@@ -1936,6 +1936,10 @@ test_lazy_prereq SHA1 ' esac ' +test_lazy_prereq DEFAULT_REPO_FORMAT ' + test_have_prereq SHA1 +' + # Ensure that no test accidentally triggers a Git command # that runs the actual maintenance scheduler, affecting a user's # system permanently.
Signature · 1 line
2.43.GIT
PATCH v2 2/12 worktree: skip reading HEAD when repairing worktrees
In reply to Patrick SteinhardtWhen calling git init --separate-git-dir=<new-path> on a preexisting repository, we move the Git directory of that repository to the new path specified by the user. If there are worktrees present in the repository, we need to repair the worktrees so that their gitlinks point to the new location of the repository.
This repair logic will load repositories via get_worktrees(), which will enumerate up and initialize all worktrees. Part of initialization is logic that we resolve their respective worktree HEADs, even though that information may not actually be needed in the end by all callers.
In the context of git-init(1) this is about to become a problem, because we do not have a repository that was set up via setup_git_directory() or friends. Consequentially, it is not yet fully initialized at the time of calling repair_worktrees(), and properly setting up all parts of the repository in init_db() before we repair worktrees is not an easy thing to do. While this is okay right now where we only have a single reference backend in Git, once we gain a second one we would be trying to look up the worktree HEADs before we have figured out the reference format, which does not work.
We do not require the worktree HEADs at all to repair worktrees. So let's fix this issue by skipping over the step that reads them.
--- worktree.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-)
worktree.c +16 -8
@@ -51,7 +51,7 @@ static void add_head_info(struct worktree *wt)/** * get the main worktree */ -static struct worktree *get_main_worktree (void ) +static struct worktree *get_main_worktree (int skip_reading_head) {struct worktree *worktree =NULL ;struct strbuf worktree_path =STRBUF_INIT ;
@@ -70,11 +70,13 @@ static struct worktree *get_main_worktree(void)*/ worktree->is_bare = (is_bare_repository_cfg ==1 ) ||is_bare_repository (); -add_head_info (worktree); +if (!skip_reading_head) +add_head_info (worktree);return worktree; } -static struct worktree *get_linked_worktree (const char *id) +static struct worktree *get_linked_worktree (const char *id, +int skip_reading_head) {struct worktree *worktree =NULL ;struct strbuf path =STRBUF_INIT ;
@@ -93,7 +95,8 @@ static struct worktree *get_linked_worktree(const char *id)CALLOC_ARRAY (worktree,1 ); worktree->path =strbuf_detach (&worktree_path,NULL ); worktree->id =xstrdup (id); -add_head_info (worktree); +if (!skip_reading_head) +add_head_info (worktree); done:strbuf_release (&path);
@@ -118,7 +121,7 @@ static void mark_current_worktree(struct worktree **worktrees)free (git_dir); } -struct worktree **get_worktrees (void ) +static struct worktree **get_worktrees_internal (int skip_reading_head) {struct worktree **list =NULL ;struct strbuf path =STRBUF_INIT ;
@@ -128,7 +131,7 @@ struct worktree **get_worktrees(void)ALLOC_ARRAY (list, alloc); - list[counter++] =get_main_worktree (); + list[counter++] =get_main_worktree (skip_reading_head);strbuf_addf (&path,"%s/worktrees" ,get_git_common_dir ()); dir =opendir (path.buf);
@@ -137,7 +140,7 @@ struct worktree **get_worktrees(void)while ((d =readdir_skip_dot_and_dotdot (dir)) !=NULL ) {struct worktree *linked =NULL ; -if ((linked =get_linked_worktree (d->d_name))) { +if ((linked =get_linked_worktree (d->d_name, skip_reading_head))) {ALLOC_GROW (list, counter +1 , alloc); list[counter++] = linked; }
@@ -151,6 +154,11 @@ struct worktree **get_worktrees(void)return list; } +struct worktree **get_worktrees (void ) +{ +return get_worktrees_internal (0 ); +} +const char *get_worktree_git_dir (const struct worktree *wt) {if (!wt)
@@ -591,7 +599,7 @@ static void repair_noop(int iserr UNUSED,void repair_worktrees (worktree_repair_fn fn,void *cb_data) { -struct worktree **worktrees =get_worktrees (); +struct worktree **worktrees =get_worktrees_internal (1 );struct worktree **wt = worktrees +1 ;/* +1 skips main worktree */ if (!fn)
Signature · 1 line
2.43.GIT
Re: [PATCH v2 02/12] worktree: skip reading HEAD when repairing worktrees
Quoted 11 lines · Patrick Steinhardt
When calling
git init --separate-git-dir=<new-path>on a preexisting repository, we move the Git directory of that repository to the new path specified by the user. If there are worktrees present in the repository, we need to repair the worktrees so that their gitlinks point to the new location of the repository.This repair logic will load repositories via
get_worktrees(), which will enumerate up and initialize all worktrees. Part of initialization is logic that we resolve their respective worktree HEADs, even though that information may not actually be needed in the end by all callers.
Quoted 9 lines
In the context of git-init(1) this is about to become a problem, because we do not have a repository that was set up via
setup_git_directory()or friends. Consequentially, it is not yet fully initialized at the time of callingrepair_worktrees(), and properly setting up all parts of the repository ininit_db()before we repair worktrees is not an easy thing to do. While this is okay right now where we only have a single reference backend in Git, once we gain a second one we would be trying to look up the worktree HEADs before we have figured out the reference format, which does not work.
s/Consequentially/Consequently/
I found it difficult to digest this paragraph with its foreshadowing phrase "about to become a problem" since it wasn't apparent until the very final sentence in the paragraph what the actual problem would be. Perhaps if you mention early on that the reftable backend will have trouble with the current code, it would be easier to grasp. Maybe something like this:
Although not a problem presently with the file-based reference backend, it will become a problem with the upcoming reftable backend. In the context of git-init(1) a fully-materialized repository set up via setup_git_directory() or friends is not yet present. Consequently, it is not yet fully initialized at the time repair_worktrees() is called, and properly setting up all parts of the repository in init_db() before we repair worktrees is not an easy task. With introduction of the reftable backend, it would try to look up the worktree HEADs before we have figured out the reference format, thus would not work.
worktree.c static void add_head_info(struct worktree *wt) · line 54
Quoted 13 lines
We do not require the worktree HEADs at all to repair worktrees. So let's fix this issue by skipping over the step that reads them.
Signed-off-by Patrick Steinhardt <[email protected]>---
worktree.c +3
-2@@ -51,7 +51,7 @@ static void add_head_info(struct worktree *wt) -static struct worktree *get_main_worktree (void ) +static struct worktree *get_main_worktree (int skip_reading_head) { -add_head_info (worktree); +if (!skip_reading_head) +add_head_info (worktree);
This is so special-case that it feels more than a little dirty.
worktree.c static void repair_noop(int iserr UNUSED, · line 601
Quoted 5 lines
worktree.c +1
-1@@ -591,7 +599,7 @@ static void repair_noop(int iserr UNUSED,void repair_worktrees (worktree_repair_fn fn,void *cb_data) { -struct worktree **worktrees =get_worktrees (); +struct worktree **worktrees =get_worktrees_internal (1 );
In an ideal world, a repair function should not be calling get_worktrees() at all since get_worktrees() is not tolerant of corruption of the worktree administrative files. (Plus, as you note, it does more work than necessary for the current set of repairs performed by git worktree repair.)
Even as I was implementing the worktree repair code, I wavered back and forth multiple times between calling get_worktrees() and writing a custom corruption-tolerant function to retrieve worktree administrative information. In the end, I opted for get_worktrees() for the pragmatic reason that it allowed me to narrow the scope of the patches to the types of repairs which were the current focus without getting mired down in the involved details of writing a corruption-tolerant function for retrieving worktree metadata. However, that decision was made with the understanding that the pragmatic choice of the moment would not rule out the possibility of returning later and implementing the more correct approach of having a corruption-tolerant function for retrieving worktree metadata.
The special-case ugliness of this patch suggests strongly in favor of implementing the earlier-envisioned corruption-tolerant function for retrieving worktree metadata rather than the band-aid approach taken by this patch. The generic name get_worktrees_internal() isn't helpful either; it doesn't do a good job of conveying any particular meaning to the reader.
Having said all that, I'm not overly opposed to this patch, especially since your main focus is on getting the reftable backend integrated, and because the changes (and ugliness) introduced by this patch are entirely self-contained and private to worktree.c, so are not a show-stopper by any means. Rather, I wanted to get down to writing what I think would be a better future approach if someone gets around to tackling it. (There is no pressing need at the moment, and that someone doesn't have to be you.)
Re: [PATCH v2 02/12] worktree: skip reading HEAD when repairing worktrees
Quoted 8 lines · Eric Sunshine
Having said all that, I'm not overly opposed to this patch, especially since your main focus is on getting the reftable backend integrated, and because the changes (and ugliness) introduced by this patch are entirely self-contained and private to
worktree.c, so are not a show-stopper by any means. Rather, I wanted to get down to writing what I think would be a better future approach if someone gets around to tackling it. (There is no pressing need at the moment, and that someone doesn't have to be you.)
I forgot to mention that, if you reroll for some reason, the get_worktrees()/get_worktrees_internal() dance might deserve an in-source NEEDSWORK comment explaining that get_worktrees_internal() exists to work around the shortcoming that a corruption-tolerant function for retrieving worktree metadata (for use by the "repair" function) does not yet exist.
Re: [PATCH v2 02/12] worktree: skip reading HEAD when repairing worktrees
Quoted 10 lines · Eric Sunshine
Quoted 8 lines · Eric Sunshine
Having said all that, I'm not overly opposed to this patch, especially since your main focus is on getting the reftable backend integrated, and because the changes (and ugliness) introduced by this patch are entirely self-contained and private to
worktree.c, so are not a show-stopper by any means. Rather, I wanted to get down to writing what I think would be a better future approach if someone gets around to tackling it. (There is no pressing need at the moment, and that someone doesn't have to be you.)
Quoted 6 lines
I forgot to mention that, if you reroll for some reason, the get_worktrees()/get_worktrees_internal() dance might deserve an in-source NEEDSWORK comment explaining that
get_worktrees_internal()exists to work around the shortcoming that a corruption-tolerant function for retrieving worktree metadata (for use by the "repair" function) does not yet exist.
Thanks for sharing your thoughts, will do.
Patrick
PATCH v2 3/12 refs: refactor logic to look up storage backends
In reply to Patrick SteinhardtIn order to look up ref storage backends, we're currently using a linked list of backends, where each backend is expected to set up its next pointer to the next ref storage backend. This is kind of a weird setup as backends need to be aware of other backends without much of a reason.
Refactor the code so that the array of backends is centrally defined in "refs.c", where each backend is now identified by an integer constant. Expose functions to translate from those integer constants to the name and vice versa, which will be required by subsequent patches.
--- refs.c | 34 +++++++++++++++++++++++++--------- refs.h | 3 +++ refs/debug.c | 1 - refs/files-backend.c | 1 - refs/packed-backend.c | 1 - refs/refs-internal.h | 1 - repository.h | 3 +++ 7 files changed, 31 insertions(+), 13 deletions(-)
refs.c +25 -9
@@ -33,17 +33,33 @@/* * List of all available backends */ -static struct ref_storage_be *refs_backends = &refs_be_files; +static const struct ref_storage_be *refs_backends[] = { + [REF_STORAGE_FORMAT_FILES ] = &refs_be_files, +}; -static struct ref_storage_be *find_ref_storage_backend (const char *name) +static const struct ref_storage_be *find_ref_storage_backend (int ref_storage_format) { -struct ref_storage_be *be; -for (be = refs_backends; be; be = be->next) -if (!strcmp (be->name, name)) -return be; +if (ref_storage_format <ARRAY_SIZE (refs_backends)) +return refs_backends[ref_storage_format];return NULL ; } +int ref_storage_format_by_name (const char *name) +{ +for (int i =0 ; i <ARRAY_SIZE (refs_backends); i++) +if (refs_backends[i] && !strcmp (refs_backends[i]->name, name)) +return i; +return REF_STORAGE_FORMAT_UNKNOWN ; +} + +const char *ref_storage_format_to_name (int ref_storage_format) +{ +const struct ref_storage_be *be =find_ref_storage_backend (ref_storage_format); +if (!be) +return "unknown" ; +return be->name; +} +/* * How to handle various characters in refnames: * 0: An acceptable character for refs
@@ -2029,12 +2045,12 @@ static struct ref_store *ref_store_init(struct repository *repo,const char *gitdir, unsigned int flags) { - const char *be_name = "files"; -struct ref_storage_be *be =find_ref_storage_backend (be_name); + int format = REF_STORAGE_FORMAT_FILES; +const struct ref_storage_be *be =find_ref_storage_backend (format);struct ref_store *refs;if (!be) -BUG ("reference backend %s is unknown" , be_name); +BUG ("reference backend is unknown" ); refs = be->init (repo, gitdir, flags);return refs;
refs.h +3 -0
@@ -11,6 +11,9 @@ struct string_list;struct string_list_item ;struct worktree ; +int ref_storage_format_by_name (const char *name); +const char *ref_storage_format_to_name (int ref_storage_format); +/* * Resolve a reference, recursively following symbolic refererences. *
refs/debug.c +0 -1
@@ -426,7 +426,6 @@ static int debug_reflog_expire(struct ref_store *ref_store, const char *refname, }struct ref_storage_be refs_be_debug = { - .next =NULL , .name ="debug" , .init =NULL , .init_db = debug_init_db,
refs/files-backend.c +0 -1
@@ -3241,7 +3241,6 @@ static int files_init_db(struct ref_store *ref_store, struct strbuf *err UNUSED) }struct ref_storage_be refs_be_files = { - .next =NULL , .name ="files" , .init = files_ref_store_create, .init_db = files_init_db,
refs/packed-backend.c +0 -1
@@ -1705,7 +1705,6 @@ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_s }struct ref_storage_be refs_be_packed = { - .next =NULL , .name ="packed" , .init = packed_ref_store_create, .init_db = packed_init_db,
refs/refs-internal.h +0 -1
@@ -663,7 +663,6 @@ typedef int read_symbolic_ref_fn(struct ref_store *ref_store, const char *refnam struct strbuf *referent);struct ref_storage_be { -struct ref_storage_be *next;const char *name;ref_store_init_fn *init;ref_init_db_fn *init_db;
repository.h +3 -0
@@ -24,6 +24,9 @@ enum fetch_negotiation_setting { FETCH_NEGOTIATION_NOOP, }; +#define REF_STORAGE_FORMAT_UNKNOWN 0 +#define REF_STORAGE_FORMAT_FILES 1 +struct repo_settings {int initialized;
Signature · 1 line
2.43.GIT
Re: [PATCH v2 03/12] refs: refactor logic to look up storage backends
Quoted 9 lines · Patrick Steinhardt
In order to look up ref storage backends, we're currently using a linked list of backends, where each backend is expected to set up its
nextpointer to the next ref storage backend. This is kind of a weird setup as backends need to be aware of other backends without much of a reason.Refactor the code so that the array of backends is centrally defined in "
refs.c", where each backend is now identified by an integer constant. Expose functions to translate from those integer constants to the name and vice versa, which will be required by subsequent patches.
A small question. Does this have to be "int", or is "unsigned" (or even an enum, rewrittenfrom the "REF_STORAGE_FORMAT_*" family of CPP macro constants) good enough? I am only wondering what happens when you clal find_ref_storage_backend() with a negative index.
For that matter, how REF_STORAGE_FORMAT_UNKNOWN (whose value is 0) is handled by the function also gets curious. The caller may have to find that the backend hasn't been specified by receiving an element in the refs_backends[] array that corresponds to it, but the error behaviour of this function is also to return NULL, so it has to be prepared to handle both cases?
Quoted 10 lines
refs.c +6
-0+static const struct ref_storage_be *refs_backends[] = { + [REF_STORAGE_FORMAT_FILES ] = &refs_be_files, +}; +static const struct ref_storage_be *find_ref_storage_backend (int ref_storage_format) { +if (ref_storage_format <ARRAY_SIZE (refs_backends)) +return refs_backends[ref_storage_format];return NULL ; }
Re: [PATCH v2 03/12] refs: refactor logic to look up storage backends
Quoted 12 lines · Junio C Hamano
Quoted 9 lines · Patrick Steinhardt
In order to look up ref storage backends, we're currently using a linked list of backends, where each backend is expected to set up its
nextpointer to the next ref storage backend. This is kind of a weird setup as backends need to be aware of other backends without much of a reason.Refactor the code so that the array of backends is centrally defined in "
refs.c", where each backend is now identified by an integer constant. Expose functions to translate from those integer constants to the name and vice versa, which will be required by subsequent patches.
Quoted 4 lines
A small question. Does this have to be "int", or is "unsigned" (or even an enum, rewrittenfrom the "REF_STORAGE_FORMAT_*" family of CPP macro constants) good enough? I am only wondering what happens when you clal
find_ref_storage_backend()with a negative index.
No, it does not have to be an int, and handling a negative index would be a bug. I tried to stick to what we have with GIT_HASH_UNKNOWN, GIT_HASH_SHA1 etc, which is exactly similar in spirit. Whether it's the perfect way to handle this... probably not. Without the context I would've used an enum, but instead I opted for consistency.
Quoted 6 lines
For that matter, how
REF_STORAGE_FORMAT_UNKNOWN(whose value is 0) is handled by the function also gets curious. The caller may have to find that the backend hasn't been specified by receiving an element in the refs_backends[] array that corresponds to it, but the error behaviour of this function is also to return NULL, so it has to be prepared to handle both cases?
Yeah, we do not really discern those two cases for now and instead just return NULL both for any unknown ref storage format. All callers know to handle NULL, but the error handling will only report a generic "unknown" backend error.
The easiest way to discern those cases would be to BUG() when being passed an invalid ref storage format smaller than 0 or larger than the number of known backends. Because ultimately it is just that, a bug that shouldn't ever occur.
Not sure whether this is worth a reroll?
Patrick
Quoted 10 lines
Quoted 10 lines
refs.c +6
-0+static const struct ref_storage_be *refs_backends[] = { + [REF_STORAGE_FORMAT_FILES ] = &refs_be_files, +}; +static const struct ref_storage_be *find_ref_storage_backend (int ref_storage_format) { +if (ref_storage_format <ARRAY_SIZE (refs_backends)) +return refs_backends[ref_storage_format];return NULL ; }
Re: [PATCH v2 03/12] refs: refactor logic to look up storage backends
Quoted 11 lines · Patrick Steinhardt
Yeah, we do not really discern those two cases for now and instead just return
NULLboth for any unknown ref storage format. All callers know to handleNULL, but the error handling will only report a generic "unknown" backend error.The easiest way to discern those cases would be to
BUG()when being passed an invalid ref storage format smaller than 0 or larger than the number of known backends. Because ultimately it is just that, a bug that shouldn't ever occur.Not sure whether this is worth a reroll?
By using an unsigned type, you no longer have to worry about getting handed a negative index, as the "must be smaller than ARRAY_SIZE()" check will be sufficient to catch anybody who passes "-1" (casted to unsigned by parameter passing). So I would say that would be a good enough reason to reroll, whether we differentiate 0 and an index that is larger than refs_backends[] (or a negative one) with an explicit BUG(), or just leave it to the caller by returning NULL. As to the error handling, I suspect it is sufficient to return NULL and let the caller handle it.
Thanks.
Quoted 13 lines
Patrick
Quoted 10 lines
Quoted 10 lines
refs.c +6
-0+static const struct ref_storage_be *refs_backends[] = { + [REF_STORAGE_FORMAT_FILES ] = &refs_be_files, +}; +static const struct ref_storage_be *find_ref_storage_backend (int ref_storage_format) { +if (ref_storage_format <ARRAY_SIZE (refs_backends)) +return refs_backends[ref_storage_format];return NULL ; }
PATCH v2 4/12 setup: start tracking ref storage format
In reply to Patrick SteinhardtIn order to discern which ref storage format a repository is supposed to use we need to start setting up and/or discovering the format. This needs to happen in two separate code paths.
- The first path is when we create a repository via init_db(). When we are re-initializing a preexisting repository we need to retain the previously used ref storage format -- if the user asked for a different format then this indicates an error and we error out. Otherwise we either initialize the repository with the format asked for by the user or the default format, which currently is the "files" backend.
- The second path is when discovering repositories, where we need to read the config of that repository. There is not yet any way to configure something other than the "files" backend, so we can just blindly set the ref storage format to this backend.
Wire up this logic so that we have the ref storage format always readily available when needed. As there is only a single backend and because it is not configurable we cannot yet verify that this tracking works as expected via tests, but tests will be added in subsequent commits. To countermand this ommission now though, raise a BUG() in case the ref storage format is not set up properly in ref_store_init().
--- builtin/clone.c | 5 +++-- builtin/init-db.c | 4 +++- refs.c | 4 ++-- repository.c | 6 ++++++ repository.h | 4 ++++ setup.c | 26 +++++++++++++++++++++++--- setup.h | 6 +++++- 7 files changed, 46 insertions(+), 9 deletions(-)
builtin/clone.c +3 -2
@@ -1107,7 +1107,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)* repository, and reference backends may persist that information into * their on-disk data structures. */ -init_db (git_dir, real_git_dir, option_template,GIT_HASH_UNKNOWN ,NULL , +init_db (git_dir, real_git_dir, option_template,GIT_HASH_UNKNOWN , +REF_STORAGE_FORMAT_UNKNOWN ,NULL , do_not_override_repo_unix_permissions,INIT_DB_QUIET |INIT_DB_SKIP_REFDB );if (real_git_dir) {
@@ -1292,7 +1293,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) hash_algo =hash_algo_by_ptr (transport_get_hash_algo (transport));initialize_repository_version (hash_algo,1 );repo_set_hash_algo (the_repository, hash_algo); -create_reference_database (NULL ,1 ); +create_reference_database (the_repository->ref_storage_format,NULL ,1 );/* * Before fetching from the remote, download and install bundle
builtin/init-db.c +3 -1
@@ -11,6 +11,7 @@#include "object-file.h" #include "parse-options.h" #include "path.h" +#include "refs.h" #include "setup.h" #include "strbuf.h"
@@ -236,5 +237,6 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) flags |=INIT_DB_EXIST_OK ;return init_db (git_dir, real_git_dir, template_dir, hash_algo, - initial_branch, init_shared_repository, flags); +REF_STORAGE_FORMAT_UNKNOWN , initial_branch, + init_shared_repository, flags); }
refs.c +2 -2
@@ -2045,10 +2045,10 @@ static struct ref_store *ref_store_init(struct repository *repo, const char *gitdir, unsigned int flags) { - int format = REF_STORAGE_FORMAT_FILES; -const struct ref_storage_be *be =find_ref_storage_backend (format); +const struct ref_storage_be *be;struct ref_store *refs; + be =find_ref_storage_backend (repo->ref_storage_format);if (!be)BUG ("reference backend is unknown" );
repository.c +6 -0
@@ -104,6 +104,11 @@ void repo_set_hash_algo(struct repository *repo, int hash_algo) repo->hash_algo = &hash_algos[hash_algo]; } +void repo_set_ref_storage_format (struct repository *repo,int format) +{ + repo->ref_storage_format = format; +} +/* * Attempt to resolve and set the provided 'gitdir' for repository 'repo'. * Return 0 upon success and a non-zero value upon failure.
@@ -184,6 +189,7 @@ int repo_init(struct repository *repo, goto error;repo_set_hash_algo (repo, format.hash_algo); +repo_set_ref_storage_format (repo, format.ref_storage_format); repo->repository_format_worktree_config = format.worktree_config;/* take ownership of format.partial_clone */
repository.h +4 -0
@@ -163,6 +163,9 @@ struct repository {/* Repository's current hash algorithm, as serialized on disk. */ const struct git_hash_algo *hash_algo; +/* Repository's reference storage format, as serialized on disk. */ +int ref_storage_format; +/* A unique-id for tracing purposes. */ int trace2_repo_id;
@@ -202,6 +205,7 @@ void repo_set_gitdir(struct repository *repo, const char *root, const struct set_gitdir_args *extra_args);void repo_set_worktree (struct repository *repo,const char *path);void repo_set_hash_algo (struct repository *repo,int algo); +void repo_set_ref_storage_format (struct repository *repo,int format);void initialize_the_repository (void );RESULT_MUST_BE_USED int repo_init(struct repository *r, const char *gitdir, const char *worktree);
setup.c +23 -3
@@ -1566,6 +1566,8 @@ const char *setup_git_directory_gently(int *nongit_ok) }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 */
@@ -1659,6 +1661,8 @@ void check_repository_format(struct repository_format *fmt)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 =
@@ -1899,7 +1903,8 @@ static int is_reinit(void)return ret; } -void create_reference_database (const char *initial_branch,int quiet) +void create_reference_database (int ref_storage_format, +const char *initial_branch,int quiet) {struct strbuf err =STRBUF_INIT ;int reinit =is_reinit ();
@@ -1919,6 +1924,7 @@ void create_reference_database(const char *initial_branch, int quiet)safe_create_dir (git_path ("refs" ),1 );adjust_shared_perm (git_path ("refs" )); +repo_set_ref_storage_format (the_repository, ref_storage_format);if (refs_init_db (&err))die ("failed to set up refs db: %s" , err.buf);
@@ -2137,8 +2143,20 @@ static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash } } +static void validate_ref_storage_format (struct repository_format *repo_fmt,int 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; + } +} +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, +int ref_storage_format,const char *initial_branch,int init_shared_repository,unsigned int flags) {int reinit;
@@ -2181,13 +2199,15 @@ int init_db(const char *git_dir, const char *real_git_dir,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, &repo_fmt, prev_bare_repository, init_shared_repository);if (!(flags &INIT_DB_SKIP_REFDB )) -create_reference_database (initial_branch, flags &INIT_DB_QUIET ); +create_reference_database (repo_fmt.ref_storage_format, + initial_branch, flags &INIT_DB_QUIET );create_object_directory ();if (get_shared_repository ()) {
setup.h +5 -1
@@ -115,6 +115,7 @@ struct repository_format {int worktree_config;int is_bare;int hash_algo; +int ref_storage_format;int sparse_index;char *work_tree;struct string_list unknown_extensions;
@@ -131,6 +132,7 @@ struct repository_format { .version = -1, \ .is_bare = -1, \ .hash_algo = GIT_HASH_SHA1, \ + .ref_storage_format = REF_STORAGE_FORMAT_FILES, \ .unknown_extensions = STRING_LIST_INIT_DUP, \ .v1_only_extensions = STRING_LIST_INIT_DUP, \ }
@@ -175,10 +177,12 @@ void check_repository_format(struct repository_format *fmt);int init_db (const char *git_dir,const char *real_git_dir,const char *template_dir,int hash_algo, +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 create_reference_database (const char *initial_branch,int quiet); +void create_reference_database (int ref_storage_format, +const char *initial_branch,int quiet);/* * NOTE NOTE NOTE!!
Signature · 1 line
2.43.GIT
PATCH v2 5/12 setup: set repository's formats on init
In reply to Patrick SteinhardtThe proper hash algorithm and ref storage format that will be used for a newly initialized repository will be figured out in init_db() via validate_hash_algorithm() and validate_ref_storage_format(). Until now though, we never set up the hash algorithm or ref storage format of the_repository accordingly.
There are only two callsites of init_db(), one in git-init(1) and one in git-clone(1). The former function doesn't care for the formats to be set up properly because it never access the repository after calling the function in the first place.
For git-clone(1) it's a different story though, as we call init_db() before listing remote refs. While we do indeed have the wrong hash function in the_repository when init_db() sets up a non-default object format for the repository, it never mattered because we adjust the hash after learning about the remote's hash function via the listed refs.
So the current state is correct for the hash algo, but it's not for the ref storage format because git-clone(1) wouldn't know to set it up properly. But instead of adjusting only the ref_storage_format, set both the hash algo and the ref storage format so that the_repository is in the correct state when init_db() exits. This is fine as we will adjust the hash later on anyway and makes it easier to reason about the end state of the_repository.
--- setup.c | 7 +++++++ 1 file changed, 7 insertions(+)
setup.c +7 -0
@@ -2205,6 +2205,13 @@ int init_db(const char *git_dir, const char *real_git_dir, &repo_fmt, prev_bare_repository, 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 );
Signature · 1 line
2.43.GIT
PATCH v2 6/12 setup: introduce "extensions.refStorage" extension
In reply to Patrick SteinhardtIntroduce 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]: <[email protected]>
--- Documentation/config/extensions.txt | 11 ++++++++ Documentation/ref-storage-format.txt | 1 + .../technical/repository-version.txt | 5 ++++ builtin/clone.c | 2 +- setup.c | 23 +++++++++++++--- setup.h | 3 ++- t/t0001-init.sh | 26 +++++++++++++++++++ t/test-lib.sh | 2 +- 8 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 Documentation/ref-storage-format.txt
Documentation/config/extensions.txt +11 -0
@@ -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
Documentation/ref-storage-format.txt new file +1 -0
@@ -0,0 +1 @@ +* `files` for loose files with packed-refs. This is the default.
Documentation/technical/repository-version.txt +5 -0
@@ -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).
builtin/clone.c +1 -1
@@ -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 );
setup.c +20 -3
@@ -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" )) { +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,14 @@ 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,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 +1902,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 +2047,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" );
setup.h +2 -1
@@ -180,7 +180,8 @@ int init_db(const char *git_dir, const char *real_git_dir,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,int ref_storage_format, +int reinit);void create_reference_database (int ref_storage_format,const char *initial_branch,int quiet);
t/t0001-init.sh +26 -0
@@ -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 &&
t/test-lib.sh +1 -1
@@ -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
Signature · 1 line
2.43.GIT
PATCH v2 7/12 setup: introduce GIT_DEFAULT_REF_FORMAT envvar
In reply to Patrick SteinhardtIntroduce a new GIT_DEFAULT_REF_FORMAT environment variable that lets users control the default ref format used by both git-init(1) and git-clone(1). This is modeled after GIT_DEFAULT_OBJECT_FORMAT, which does the same thing for the repository's object format.
--- Documentation/git.txt | 5 +++++ setup.c | 7 +++++++ t/t0001-init.sh | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+)
Documentation/git.txt +5 -0
@@ -556,6 +556,11 @@ double-quotes and respecting backslash escapes. E.g., the value is always used. The default is "sha1". See `--object-format` in linkgit:git-init[1]. +`GIT_DEFAULT_REF_FORMAT`:: + If this variable is set, the default reference backend format for new + repositories will be set to this value. The default is "files". + See `--ref-format` in linkgit:git-init[1]. + Git Commits ~~~~~~~~~~~ `GIT_AUTHOR_NAME`::
setup.c +7 -0
@@ -2162,12 +2162,19 @@ static void validate_hash_algorithm(struct repository_format *repo_fmt, int hashstatic void validate_ref_storage_format (struct repository_format *repo_fmt,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; } }
t/t0001-init.sh +18 -0
@@ -558,6 +558,24 @@ test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with unknown back grep "invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}garbage${SQ}" err ' +test_expect_success DEFAULT_REPO_FORMAT 'init with GIT_DEFAULT_REF_FORMAT=files' ' + test_when_finished "rm -rf refformat" && + GIT_DEFAULT_REF_FORMAT=files git init refformat && + echo 0 >expect && + git -C refformat config core.repositoryformatversion >actual && + test_cmp expect actual && + test_must_fail git -C refformat config extensions.refstorage +' + +test_expect_success 'init with GIT_DEFAULT_REF_FORMAT=garbage' ' + test_when_finished "rm -rf refformat" && + cat >expect <<-EOF && + fatal: unknown ref storage format ${SQ}garbage${SQ} + EOF + test_must_fail env GIT_DEFAULT_REF_FORMAT=garbage git init refformat 2>err && + test_cmp expect err +' + test_expect_success MINGW 'core.hidedotfiles = false' ' git config --global core.hidedotfiles false && rm -rf newdir &&
Signature · 1 line
2.43.GIT
PATCH v2 8/12 t: introduce GIT_TEST_DEFAULT_REF_FORMAT envvar
In reply to Patrick SteinhardtIntroduce a new GIT_TEST_DEFAULT_REF_FORMAT environment variable that lets developers run the test suite with a different default ref format without impacting the ref format used by non-test Git invocations. This is modeled after GIT_TEST_DEFAULT_OBJECT_FORMAT, which does the same thing for the repository's object format.
Adapt the setup of the REFFILES test prerequisite to be conditionally set based on the default ref format.
--- t/README | 3 +++ t/test-lib-functions.sh | 5 +++++ t/test-lib.sh | 11 ++++++++++- 3 files changed, 18 insertions(+), 1 deletion(-)
t/README +3 -0
@@ -479,6 +479,9 @@ GIT_TEST_DEFAULT_HASH=<hash-algo> specifies which hash algorithm to use in the test scripts. Recognized values for <hash-algo> are "sha1" and "sha256". +GIT_TEST_DEFAULT_REF_FORMAT=<format> specifies which ref storage format +to use in the test scripts. Recognized values for <format> are "files". + GIT_TEST_NO_WRITE_REV_INDEX=<boolean>, when true disables the 'pack.writeReverseIndex' setting.
t/test-lib-functions.sh +5 -0
@@ -1659,6 +1659,11 @@ test_detect_hash () { test_hash_algo="${GIT_TEST_DEFAULT_HASH:-sha1}" } +# Detect the hash algorithm in use. +test_detect_ref_format () { + echo "${GIT_TEST_DEFAULT_REF_FORMAT:-files}" +} + # Load common hash metadata and common placeholder object IDs for use with # test_oid. test_oid_init () {
t/test-lib.sh +10 -1
@@ -542,6 +542,8 @@ export EDITOR GIT_DEFAULT_HASH="${GIT_TEST_DEFAULT_HASH:-sha1}" export GIT_DEFAULT_HASH +GIT_DEFAULT_REF_FORMAT="${GIT_TEST_DEFAULT_REF_FORMAT:-files}" +export GIT_DEFAULT_REF_FORMAT GIT_TEST_MERGE_ALGORITHM="${GIT_TEST_MERGE_ALGORITHM:-ort}" export GIT_TEST_MERGE_ALGORITHM
@@ -1745,7 +1747,14 @@ parisc* | hppa*) ;; esac -test_set_prereq REFFILES +case "$GIT_DEFAULT_REF_FORMAT" in +files) + test_set_prereq REFFILES;; +*) + echo 2>&1 "error: unknown ref format $GIT_DEFAULT_REF_FORMAT" + exit 1 + ;; +esac ( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1 test -z "$NO_CURL" && test_set_prereq LIBCURL
Signature · 1 line
2.43.GIT
PATCH v2 9/12 builtin/rev-parse: introduce `--show-ref-format` flag
In reply to Patrick SteinhardtIntroduce a new --show-ref-format to git-rev-parse(1) that causes it to print the ref format used by a repository.
--- Documentation/git-rev-parse.txt | 3 +++ builtin/rev-parse.c | 4 ++++ t/t1500-rev-parse.sh | 17 +++++++++++++++++ 3 files changed, 24 insertions(+)
Documentation/git-rev-parse.txt +3 -0
@@ -307,6 +307,9 @@ The following options are unaffected by `--path-format`: input, multiple algorithms may be printed, space-separated. If not specified, the default is "storage". +--show-ref-format:: + Show the reference storage format used for the repository. + Other Options ~~~~~~~~~~~~~
builtin/rev-parse.c +4 -0
@@ -1062,6 +1062,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)puts (the_hash_algo->name);continue ; } +if (!strcmp (arg,"--show-ref-format" )) { +puts (ref_storage_format_to_name (the_repository->ref_storage_format)); +continue ; + }if (!strcmp (arg,"--end-of-options" )) { seen_end_of_options =1 ;if (filter & (DO_FLAGS |DO_REVS ))
t/t1500-rev-parse.sh +17 -0
@@ -208,6 +208,23 @@ test_expect_success 'rev-parse --show-object-format in repo' ' grep "unknown mode for --show-object-format: squeamish-ossifrage" err ' +test_expect_success 'rev-parse --show-ref-format' ' + test_detect_ref_format >expect && + git rev-parse --show-ref-format >actual && + test_cmp expect actual +' + +test_expect_success 'rev-parse --show-ref-format with invalid storage' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + git config extensions.refstorage broken && + test_must_fail git rev-parse --show-ref-format 2>err && + grep "error: invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}broken${SQ}" err + ) +' + test_expect_success '--show-toplevel from subdir of working tree' ' pwd >expect && git -C sub/dir rev-parse --show-toplevel >actual &&
Signature · 1 line
2.43.GIT
PATCH v2 10/12 builtin/init: introduce `--ref-format=` value flag
In reply to Patrick SteinhardtIntroduce a new --ref-format value flag for git-init(1) that allows the user to specify the ref format that is to be used for a newly initialized repository.
--- Documentation/git-init.txt | 7 +++++++ builtin/init-db.c | 13 ++++++++++++- t/t0001-init.sh | 26 ++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-)
Documentation/git-init.txt +7 -0
@@ -11,6 +11,7 @@ SYNOPSIS [verse] 'git init' [-q | --quiet] [--bare] [--template=<template-directory>] [--separate-git-dir <git-dir>] [--object-format=<format>] + [--ref-format=<format>] [-b <branch-name> | --initial-branch=<branch-name>] [--shared[=<permissions>]] [<directory>]
@@ -57,6 +58,12 @@ values are 'sha1' and (if enabled) 'sha256'. 'sha1' is the default. + include::object-format-disclaimer.txt[] +--ref-format=<format>:: + +Specify the given ref storage format for the repository. The valid values are: ++ +include::ref-storage-format.txt[] + --template=<template-directory>:: Specify the directory from which templates will be used. (See the "TEMPLATE
builtin/init-db.c +12 -1
@@ -58,6 +58,7 @@ static int shared_callback(const struct option *opt, const char *arg, int unset)static const char *const init_db_usage[] = {N_ ("git init [-q | --quiet] [--bare] [--template=<template-directory>]\n" " [--separate-git-dir <git-dir>] [--object-format=<format>]\n" +" [--ref-format=<format>]\n" " [-b <branch-name> | --initial-branch=<branch-name>]\n" " [--shared[=<permissions>]] [<directory>]" ),NULL
@@ -77,8 +78,10 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)const char *template_dir =NULL ;unsigned int flags =0 ;const char *object_format =NULL ; +const char *ref_format =NULL ;const char *initial_branch =NULL ;int hash_algo =GIT_HASH_UNKNOWN ; +int ref_storage_format =REF_STORAGE_FORMAT_UNKNOWN ;int init_shared_repository =-1 ;const struct option init_db_options[] = { OPT_STRING(0, "template", &template_dir, N_("template-directory"),
@@ -96,6 +99,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) N_("override the name of the initial branch")),OPT_STRING (0 ,"object-format" , &object_format,N_ ("hash" ),N_ ("specify the hash algorithm to use" )), +OPT_STRING (0 ,"ref-format" , &ref_format,N_ ("format" ), +N_ ("specify the reference format to use" )), OPT_END() };
@@ -159,6 +164,12 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)die (_ ("unknown hash algorithm '%s'" ), object_format); } +if (ref_format) { + ref_storage_format =ref_storage_format_by_name (ref_format); +if (ref_storage_format ==REF_STORAGE_FORMAT_UNKNOWN ) +die (_ ("unknown ref storage format '%s'" ), ref_format); + } +if (init_shared_repository !=-1 )set_shared_repository (init_shared_repository);
@@ -237,6 +248,6 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) flags |=INIT_DB_EXIST_OK ;return init_db (git_dir, real_git_dir, template_dir, hash_algo, -REF_STORAGE_FORMAT_UNKNOWN , initial_branch, + ref_storage_format, initial_branch, init_shared_repository, flags); }
t/t0001-init.sh +26 -0
@@ -576,6 +576,32 @@ test_expect_success 'init with GIT_DEFAULT_REF_FORMAT=garbage' ' test_cmp expect err ' +test_expect_success 'init with --ref-format=files' ' + test_when_finished "rm -rf refformat" && + git init --ref-format=files refformat && + echo files >expect && + git -C refformat rev-parse --show-ref-format >actual && + test_cmp expect actual +' + +test_expect_success 're-init with same format' ' + test_when_finished "rm -rf refformat" && + git init --ref-format=files refformat && + git init --ref-format=files refformat && + echo files >expect && + git -C refformat rev-parse --show-ref-format >actual && + test_cmp expect actual +' + +test_expect_success 'init with --ref-format=garbage' ' + test_when_finished "rm -rf refformat" && + cat >expect <<-EOF && + fatal: unknown ref storage format ${SQ}garbage${SQ} + EOF + test_must_fail git init --ref-format=garbage refformat 2>err && + test_cmp expect err +' + test_expect_success MINGW 'core.hidedotfiles = false' ' git config --global core.hidedotfiles false && rm -rf newdir &&
Signature · 1 line
2.43.GIT
PATCH v2 11/12 builtin/clone: introduce `--ref-format=` value flag
In reply to Patrick SteinhardtIntroduce a new --ref-format value flag for git-clone(1) that allows the user to specify the ref format that is to be used for a newly initialized repository.
--- Documentation/git-clone.txt | 6 ++++++ builtin/clone.c | 12 +++++++++++- t/t5601-clone.sh | 17 +++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-)
Documentation/git-clone.txt +6 -0
@@ -311,6 +311,12 @@ or `--mirror` is given) The result is Git repository can be separated from working tree. +--ref-format=<ref-format:: + +Specify the given ref storage format for the repository. The valid values are: ++ +include::ref-storage-format.txt[] + -j <n>:: --jobs <n>:: The number of submodules fetched at the same time.
builtin/clone.c +11 -1
@@ -72,6 +72,7 @@ static char *remote_name = NULL;static char *option_branch =NULL ;static struct string_list option_not =STRING_LIST_INIT_NODUP ;static const char *real_git_dir; +static const char *ref_format;static char *option_upload_pack ="git-upload-pack" ;static int option_verbosity;static int option_progress =-1 ;
@@ -157,6 +158,8 @@ static struct option builtin_clone_options[] = { N_("any cloned submodules will be shallow")),OPT_STRING (0 ,"separate-git-dir" , &real_git_dir,N_ ("gitdir" ),N_ ("separate git dir from working tree" )), +OPT_STRING (0 ,"ref-format" , &ref_format,N_ ("format" ), +N_ ("specify the reference format to use" )),OPT_STRING_LIST ('c' ,"config" , &option_config,N_ ("key=value" ),N_ ("set config inside the new repository" )), OPT_STRING_LIST(0, "server-option", &server_options,
@@ -932,6 +935,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)int submodule_progress;int filter_submodules =0 ;int hash_algo; +int ref_storage_format =REF_STORAGE_FORMAT_UNKNOWN ;const int do_not_override_repo_unix_permissions =-1 ; struct transport_ls_refs_options transport_ls_refs_options =
@@ -957,6 +961,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)if (option_single_branch ==-1 ) option_single_branch = deepen ?1 :0 ; +if (ref_format) { + ref_storage_format =ref_storage_format_by_name (ref_format); +if (ref_storage_format ==REF_STORAGE_FORMAT_UNKNOWN ) +die (_ ("unknown ref storage format '%s'" ), ref_format); + } +if (option_mirror) option_bare =1 ;
@@ -1108,7 +1118,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)* their on-disk data structures. */ init_db (git_dir, real_git_dir, option_template,GIT_HASH_UNKNOWN , -REF_STORAGE_FORMAT_UNKNOWN ,NULL , + ref_storage_format,NULL , do_not_override_repo_unix_permissions,INIT_DB_QUIET |INIT_DB_SKIP_REFDB );if (real_git_dir) {
t/t5601-clone.sh +17 -0
@@ -157,6 +157,23 @@ test_expect_success 'clone --mirror does not repeat tags' ' ' +test_expect_success 'clone with files ref format' ' + test_when_finished "rm -rf ref-storage" && + git clone --ref-format=files --mirror src ref-storage && + echo files >expect && + git -C ref-storage rev-parse --show-ref-format >actual && + test_cmp expect actual +' + +test_expect_success 'clone with garbage ref format' ' + cat >expect <<-EOF && + fatal: unknown ref storage format ${SQ}garbage${SQ} + EOF + test_must_fail git clone --ref-format=garbage --mirror src ref-storage 2>err && + test_cmp expect err && + test_path_is_missing ref-storage +' + test_expect_success 'clone to destination with trailing /' ' git clone src target-1/ &&
Signature · 1 line
2.43.GIT
PATCH v2 12/12 t9500: write "extensions.refstorage" into config
In reply to Patrick SteinhardtIn t9500 we're writing a custom configuration that sets up gitweb. This requires us to manually ensure that the repository format is configured as required, including both the repository format version and extensions. With the introduction of the "extensions.refStorage" extension we need to update the test to also write this new one.
--- t/t9500-gitweb-standalone-no-errors.sh | 5 +++++ 1 file changed, 5 insertions(+)
t/t9500-gitweb-standalone-no-errors.sh +5 -0
@@ -627,6 +627,7 @@ test_expect_success \ test_expect_success 'setup' ' version=$(git config core.repositoryformatversion) && algo=$(test_might_fail git config extensions.objectformat) && + refstorage=$(test_might_fail git config extensions.refstorage) && cat >.git/config <<-\EOF && # testing noval and alternate separator [gitweb]
@@ -637,6 +638,10 @@ test_expect_success 'setup' ' if test -n "$algo" then git config extensions.objectformat "$algo" + fi && + if test -n "$refstorage" + then + git config extensions.refstorage "$refstorage" fi '
Signature · 1 line
2.43.GIT
v3 cover Introduce `refStorage` extension
In reply to Patrick SteinhardtHi,
this is the third version of my patch series that introduces the new refStorage extension. This extension will be used for the upcoming reftable backend.
Changes compared to v3:
- The ref_storage_format is now tracked as an unsigned int, proposed by Junio.
- Reworded the commit message in patch 2, proposed by Eric.
- Added a NEEDSWORK comment to get_worktrees_internal(), propose by Eric.
Thanks for your reviews!
Patrick
Patrick Steinhardt (12):
t: introduce DEFAULT_REPO_FORMAT prereq
worktree: skip reading HEAD when repairing worktrees
refs: refactor logic to look up storage backends
setup: start tracking ref storage format
setup: set repository's formats on init
setup: introduce "extensions.refStorage" extension
setup: introduce GIT_DEFAULT_REF_FORMAT envvar
t: introduce GIT_TEST_DEFAULT_REF_FORMAT envvar builtin/rev-parse: introduce --show-ref-format flag builtin/init: introduce --ref-format= value flag builtin/clone: introduce --ref-format= value flag
t9500: write "extensions.refstorage" into config
Documentation/config/extensions.txt | 11 +++ Documentation/git-clone.txt | 6 ++ Documentation/git-init.txt | 7 ++ Documentation/git-rev-parse.txt | 3 + Documentation/git.txt | 5 ++ Documentation/ref-storage-format.txt | 1 + .../technical/repository-version.txt | 5 ++ builtin/clone.c | 17 ++++- builtin/init-db.c | 15 +++- builtin/rev-parse.c | 4 ++ refs.c | 34 ++++++--- refs.h | 3 + refs/debug.c | 1 - refs/files-backend.c | 1 - refs/packed-backend.c | 1 - refs/refs-internal.h | 1 - repository.c | 6 ++ repository.h | 7 ++ setup.c | 66 +++++++++++++++-- setup.h | 10 ++- t/README | 3 + t/t0001-init.sh | 70 +++++++++++++++++++ t/t1500-rev-parse.sh | 17 +++++ t/t3200-branch.sh | 2 +- t/t5601-clone.sh | 17 +++++ t/t9500-gitweb-standalone-no-errors.sh | 5 ++ t/test-lib-functions.sh | 5 ++ t/test-lib.sh | 15 +++- worktree.c | 31 +++++--- 29 files changed, 334 insertions(+), 35 deletions(-) create mode 100644 Documentation/ref-storage-format.txt
Range-diff against v2:
1: 3613439cb7 = 1: 578deaabcf t: introduce DEFAULT_REPO_FORMAT prereq 2: ecf4f1ddee ! 2: 77c7213c66 worktree: skip reading HEAD when repairing worktrees
@@ Commit message
is logic that we resolve their respective worktree HEADs, even though that information may not actually be needed in the end by all callers.
- In the context of git-init(1) this is about to become a problem, because
- we do not have a repository that was set up via setup_git_directory()
- or friends. Consequentially, it is not yet fully initialized at the time
- of calling repair_worktrees(), and properly setting up all parts of
- the repository in init_db() before we repair worktrees is not an easy
- thing to do. While this is okay right now where we only have a single
- reference backend in Git, once we gain a second one we would be trying
- to look up the worktree HEADs before we have figured out the reference
- format, which does not work.
+ Although not a problem presently with the file-based reference backend,
+ it will become a problem with the upcoming reftable backend. In the
+ context of git-init(1) we do not have a fully-initialized repository set
+ up via setup_git_directory() or friends. Consequently, we do not know
+ about the repository format when repair_worktrees() is called, and
+ properly setting up all parts of the repositroy in init_db() before we
+ try to repair worktrees is not an easy task. With the introduction of
+ the reftable backend, we would ultimately try to look up the worktree
+ HEADs before we have figured out the reference format, which does not
+ work.
We do not require the worktree HEADs at all to repair worktrees. So let's fix this issue by skipping over the step that reads them.
@@ worktree.c: static void mark_current_worktree(struct worktree **worktrees)
}
-struct worktree **get_worktrees(void)
++/*
++ * NEEDSWORK: This function exists so that we can look up metadata of a ++ * worktree without trying to access any of its internals like the refdb. It ++ * would be preferable to instead have a corruption-tolerant function for ++ * retrieving worktree metadata that could be used when the worktree is known ++ * to not be in a healthy state, e.g. when creating or repairing it. ++ */
+static struct worktree **get_worktrees_internal(int skip_reading_head)
{
struct worktree **list = NULL;
3: 12329b99b7 ! 3: 47649570bf refs: refactor logic to look up storage backends
@@ refs.c
+};
-static struct ref_storage_be *find_ref_storage_backend(const char *name)
-+static const struct ref_storage_be *find_ref_storage_backend(int ref_storage_format)
++static const struct ref_storage_be *find_ref_storage_backend(unsigned int ref_storage_format)
{
- struct ref_storage_be *be;
- for (be = refs_backends; be; be = be->next)
@@ refs.c
return NULL;
}
-+int ref_storage_format_by_name(const char *name)
++unsigned int ref_storage_format_by_name(const char *name)
+{
-+ for (int i = 0; i < ARRAY_SIZE(refs_backends); i++)
++ for (unsigned int i = 0; i < ARRAY_SIZE(refs_backends); i++)
+ if (refs_backends[i] && !strcmp(refs_backends[i]->name, name))
+ return i;
+ return REF_STORAGE_FORMAT_UNKNOWN;
+}
+
-+const char *ref_storage_format_to_name(int ref_storage_format)
++const char *ref_storage_format_to_name(unsigned int ref_storage_format)
+{
+ const struct ref_storage_be *be = find_ref_storage_backend(ref_storage_format);
+ if (!be)
@@ refs.c: static struct ref_store *ref_store_init(struct repository *repo,
{
- const char *be_name = "files";
- struct ref_storage_be *be = find_ref_storage_backend(be_name);
-+ int format = REF_STORAGE_FORMAT_FILES;
++ unsigned int format = REF_STORAGE_FORMAT_FILES;
+ const struct ref_storage_be *be = find_ref_storage_backend(format);
struct ref_store *refs;
@@ refs.h: struct string_list;
struct string_list_item;
struct worktree;
-+int ref_storage_format_by_name(const char *name);
-+const char *ref_storage_format_to_name(int ref_storage_format);
++unsigned int ref_storage_format_by_name(const char *name);
++const char *ref_storage_format_to_name(unsigned int ref_storage_format);
+
/*
* Resolve a reference, recursively following symbolic refererences.
4: ddd099fbaf ! 4: 837764d0b5 setup: start tracking ref storage format
@@ refs.c: static struct ref_store *ref_store_init(struct repository *repo,
const char *gitdir,
unsigned int flags)
{
-- int format = REF_STORAGE_FORMAT_FILES;
+- unsigned int format = REF_STORAGE_FORMAT_FILES;
- const struct ref_storage_be *be = find_ref_storage_backend(format);
+ const struct ref_storage_be *be;
struct ref_store *refs;
@@ repository.c: void repo_set_hash_algo(struct repository *repo, int hash_algo)
repo->hash_algo = &hash_algos[hash_algo];
}
-+void repo_set_ref_storage_format(struct repository *repo, int format)
++void repo_set_ref_storage_format(struct repository *repo, unsigned int format)
+{
+ repo->ref_storage_format = format;
+}
@@ repository.h: struct repository {
const struct git_hash_algo *hash_algo;
+ /* Repository's reference storage format, as serialized on disk. */
-+ int ref_storage_format;
++ unsigned int ref_storage_format;
+
/* A unique-id for tracing purposes. */
int trace2_repo_id;
@@ repository.h: void repo_set_gitdir(struct repository *repo, const char *root,
const struct set_gitdir_args *extra_args);
void repo_set_worktree(struct repository *repo, const char *path);
void repo_set_hash_algo(struct repository *repo, int algo);
-+void repo_set_ref_storage_format(struct repository *repo, int format);
++void repo_set_ref_storage_format(struct repository *repo, unsigned int format);
void initialize_the_repository(void);
RESULT_MUST_BE_USED
int repo_init(struct repository *r, const char *gitdir, const char *worktree);
@@ setup.c: static int is_reinit(void)
}
-void create_reference_database(const char *initial_branch, int quiet)
-+void create_reference_database(int ref_storage_format,
++void create_reference_database(unsigned int ref_storage_format,
+ const char *initial_branch, int quiet)
{
struct strbuf err = STRBUF_INIT;
@@ setup.c: static void validate_hash_algorithm(struct repository_format *repo_fmt,
}
}
-+static void validate_ref_storage_format(struct repository_format *repo_fmt, int format)
++static void validate_ref_storage_format(struct repository_format *repo_fmt,
++ unsigned int format)
+{
+ if (repo_fmt->version >= 0 &&
+ format != REF_STORAGE_FORMAT_UNKNOWN &&
@@ setup.c: static void validate_hash_algorithm(struct repository_format *repo_fmt,
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,
-+ int ref_storage_format, const char *initial_branch,
++ unsigned int ref_storage_format,
++ const char *initial_branch,
int init_shared_repository, unsigned int flags)
{
int reinit;
@@ setup.h: struct repository_format {
int worktree_config;
int is_bare;
int hash_algo;
-+ int ref_storage_format;
++ unsigned int ref_storage_format;
int sparse_index;
char *work_tree;
struct string_list unknown_extensions;
@@ setup.h: void check_repository_format(struct repository_format *fmt);
int init_db(const char *git_dir, const char *real_git_dir,
const char *template_dir, int hash_algo,
-+ int ref_storage_format,
++ 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 create_reference_database(const char *initial_branch, int quiet);
-+void create_reference_database(int ref_storage_format,
++void create_reference_database(unsigned int ref_storage_format,
+ const char *initial_branch, int quiet);
/*
5: 01a1e58a97 = 5: a51da56d9b setup: set repository's formats on init
6: 0a586fa648 ! 6: a1e03e4392 setup: introduce "extensions.refStorage" extension
@@ setup.c: static enum extension_result handle_extension(const char *var,
data->hash_algo = format;
return EXTENSION_OK;
+ } else if (!strcmp(ext, "refstorage")) {
-+ int format;
++ unsigned int format;
+
+ if (!value)
+ return config_error_nonbool(var);
@@ setup.c: static int needs_work_tree_config(const char *git_dir, const char *work
}
-void initialize_repository_version(int hash_algo, int reinit)
-+void initialize_repository_version(int hash_algo, int ref_storage_format,
++void initialize_repository_version(int hash_algo,
++ unsigned int ref_storage_format,
+ int reinit)
{
char repo_version_string[10];
@@ setup.c: static int create_default_files(const char *template_path,
## setup.h ##
@@ setup.h: int init_db(const char *git_dir, const char *real_git_dir,
- int ref_storage_format,
+ 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, int ref_storage_format,
++void initialize_repository_version(int hash_algo,
++ unsigned int ref_storage_format,
+ int reinit);
- void create_reference_database(int ref_storage_format,
+ void create_reference_database(unsigned int ref_storage_format,
const char *initial_branch, int quiet); 7: 6d8754f73a ! 7: 5ffc70e9be setup: introduce GIT_DEFAULT_REF_FORMAT envvar
@@ Documentation/git.txt: double-quotes and respecting backslash escapes. E.g., the
## setup.c ##
@@ setup.c: static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash
-
- static void validate_ref_storage_format(struct repository_format *repo_fmt, int format)
+ static void validate_ref_storage_format(struct repository_format *repo_fmt,
+ unsigned int format)
{
+ const char *name = getenv("GIT_DEFAULT_REF_FORMAT");
+
8: c645932f3d = 8: 13c074acdf t: introduce GIT_TEST_DEFAULT_REF_FORMAT envvar
9: 761d647770 = 9: 4ee3c9a2d1 builtin/rev-parse: introduce `--show-ref-format` flag
10: e382b5bf08 ! 10: 25773e3560 builtin/init: introduce `--ref-format=` value flag
@@ builtin/init-db.c: int cmd_init_db(int argc, const char **argv, const char *pref
+ const char *ref_format = NULL;
const char *initial_branch = NULL;
int hash_algo = GIT_HASH_UNKNOWN;
-+ int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
++ unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
int init_shared_repository = -1;
const struct option init_db_options[] = {
OPT_STRING(0, "template", &template_dir, N_("template-directory"),
11: 257233658d ! 11: 3f1cb6b9e5 builtin/clone: introduce `--ref-format=` value flag
@@ builtin/clone.c: int cmd_clone(int argc, const char **argv, const char *prefix)
int submodule_progress;
int filter_submodules = 0;
int hash_algo;
-+ int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
++ unsigned int ref_storage_format = REF_STORAGE_FORMAT_UNKNOWN;
const int do_not_override_repo_unix_permissions = -1;
struct transport_ls_refs_options transport_ls_refs_options =12: b8cd06ec53 = 12: 2e7682b2f3 t9500: write "extensions.refstorage" into config
base-commit: e79552d19784ee7f4bbce278fe25f93fbda196fa
Signature · 1 line
2.43.GIT
PATCH v3 1/12 t: introduce DEFAULT_REPO_FORMAT prereq
A limited number of tests require repositories to have the default repository format or otherwise they would fail to run, e.g. because they fail to detect the correct hash function. While the hash function is the only extension right now that creates problems like this, we are about to add a second extension for the ref format.
Introduce a new DEFAULT_REPO_FORMAT prereq that can easily be amended whenever we add new format extensions. Next to making any such changes easier on us, the prerequisite's name should also help to clarify the intent better.
--- t/t3200-branch.sh | 2 +- t/test-lib.sh | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-)
t/t3200-branch.sh +1 -1
@@ -519,7 +519,7 @@ EOF mv .git/config .git/config-saved -test_expect_success SHA1 'git branch -m q q2 without config should succeed' ' +test_expect_success DEFAULT_REPO_FORMAT 'git branch -m q q2 without config should succeed' ' git branch -m q q2 && git branch -m q2 q '
t/test-lib.sh +4 -0
@@ -1936,6 +1936,10 @@ test_lazy_prereq SHA1 ' esac ' +test_lazy_prereq DEFAULT_REPO_FORMAT ' + test_have_prereq SHA1 +' + # Ensure that no test accidentally triggers a Git command # that runs the actual maintenance scheduler, affecting a user's # system permanently.
Signature · 1 line
2.43.GIT
PATCH v3 2/12 worktree: skip reading HEAD when repairing worktrees
In reply to Patrick SteinhardtWhen calling git init --separate-git-dir=<new-path> on a preexisting repository, we move the Git directory of that repository to the new path specified by the user. If there are worktrees present in the repository, we need to repair the worktrees so that their gitlinks point to the new location of the repository.
This repair logic will load repositories via get_worktrees(), which will enumerate up and initialize all worktrees. Part of initialization is logic that we resolve their respective worktree HEADs, even though that information may not actually be needed in the end by all callers.
Although not a problem presently with the file-based reference backend, it will become a problem with the upcoming reftable backend. In the context of git-init(1) we do not have a fully-initialized repository set up via setup_git_directory() or friends. Consequently, we do not know about the repository format when repair_worktrees() is called, and properly setting up all parts of the repositroy in init_db() before we try to repair worktrees is not an easy task. With the introduction of the reftable backend, we would ultimately try to look up the worktree HEADs before we have figured out the reference format, which does not work.
We do not require the worktree HEADs at all to repair worktrees. So let's fix this issue by skipping over the step that reads them.
--- worktree.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-)
worktree.c +23 -8
@@ -51,7 +51,7 @@ static void add_head_info(struct worktree *wt)/** * get the main worktree */ -static struct worktree *get_main_worktree (void ) +static struct worktree *get_main_worktree (int skip_reading_head) {struct worktree *worktree =NULL ;struct strbuf worktree_path =STRBUF_INIT ;
@@ -70,11 +70,13 @@ static struct worktree *get_main_worktree(void)*/ worktree->is_bare = (is_bare_repository_cfg ==1 ) ||is_bare_repository (); -add_head_info (worktree); +if (!skip_reading_head) +add_head_info (worktree);return worktree; } -static struct worktree *get_linked_worktree (const char *id) +static struct worktree *get_linked_worktree (const char *id, +int skip_reading_head) {struct worktree *worktree =NULL ;struct strbuf path =STRBUF_INIT ;
@@ -93,7 +95,8 @@ static struct worktree *get_linked_worktree(const char *id)CALLOC_ARRAY (worktree,1 ); worktree->path =strbuf_detach (&worktree_path,NULL ); worktree->id =xstrdup (id); -add_head_info (worktree); +if (!skip_reading_head) +add_head_info (worktree); done:strbuf_release (&path);
@@ -118,7 +121,14 @@ static void mark_current_worktree(struct worktree **worktrees)free (git_dir); } -struct worktree **get_worktrees (void ) +/* +* NEEDSWORK: This function exists so that we can look up metadata of a +* worktree without trying to access any of its internals like the refdb. It +* would be preferable to instead have a corruption-tolerant function for +* retrieving worktree metadata that could be used when the worktree is known +* to not be in a healthy state, e.g. when creating or repairing it. +*/ +static struct worktree **get_worktrees_internal (int skip_reading_head) {struct worktree **list =NULL ;struct strbuf path =STRBUF_INIT ;
@@ -128,7 +138,7 @@ struct worktree **get_worktrees(void)ALLOC_ARRAY (list, alloc); - list[counter++] =get_main_worktree (); + list[counter++] =get_main_worktree (skip_reading_head);strbuf_addf (&path,"%s/worktrees" ,get_git_common_dir ()); dir =opendir (path.buf);
@@ -137,7 +147,7 @@ struct worktree **get_worktrees(void)while ((d =readdir_skip_dot_and_dotdot (dir)) !=NULL ) {struct worktree *linked =NULL ; -if ((linked =get_linked_worktree (d->d_name))) { +if ((linked =get_linked_worktree (d->d_name, skip_reading_head))) {ALLOC_GROW (list, counter +1 , alloc); list[counter++] = linked; }
@@ -151,6 +161,11 @@ struct worktree **get_worktrees(void)return list; } +struct worktree **get_worktrees (void ) +{ +return get_worktrees_internal (0 ); +} +const char *get_worktree_git_dir (const struct worktree *wt) {if (!wt)
@@ -591,7 +606,7 @@ static void repair_noop(int iserr UNUSED,void repair_worktrees (worktree_repair_fn fn,void *cb_data) { -struct worktree **worktrees =get_worktrees (); +struct worktree **worktrees =get_worktrees_internal (1 );struct worktree **wt = worktrees +1 ;/* +1 skips main worktree */ if (!fn)
Signature · 1 line
2.43.GIT
PATCH v3 3/12 refs: refactor logic to look up storage backends
In reply to Patrick SteinhardtIn order to look up ref storage backends, we're currently using a linked list of backends, where each backend is expected to set up its next pointer to the next ref storage backend. This is kind of a weird setup as backends need to be aware of other backends without much of a reason.
Refactor the code so that the array of backends is centrally defined in "refs.c", where each backend is now identified by an integer constant. Expose functions to translate from those integer constants to the name and vice versa, which will be required by subsequent patches.
--- refs.c | 34 +++++++++++++++++++++++++--------- refs.h | 3 +++ refs/debug.c | 1 - refs/files-backend.c | 1 - refs/packed-backend.c | 1 - refs/refs-internal.h | 1 - repository.h | 3 +++ 7 files changed, 31 insertions(+), 13 deletions(-)
refs.c +25 -9
@@ -33,17 +33,33 @@/* * List of all available backends */ -static struct ref_storage_be *refs_backends = &refs_be_files; +static const struct ref_storage_be *refs_backends[] = { + [REF_STORAGE_FORMAT_FILES ] = &refs_be_files, +}; -static struct ref_storage_be *find_ref_storage_backend (const char *name) +static const struct ref_storage_be *find_ref_storage_backend (unsigned int ref_storage_format) { -struct ref_storage_be *be; -for (be = refs_backends; be; be = be->next) -if (!strcmp (be->name, name)) -return be; +if (ref_storage_format <ARRAY_SIZE (refs_backends)) +return refs_backends[ref_storage_format];return NULL ; } +unsigned int ref_storage_format_by_name (const char *name) +{ +for (unsigned int i =0 ; i <ARRAY_SIZE (refs_backends); i++) +if (refs_backends[i] && !strcmp (refs_backends[i]->name, name)) +return i; +return REF_STORAGE_FORMAT_UNKNOWN ; +} + +const char *ref_storage_format_to_name (unsigned int ref_storage_format) +{ +const struct ref_storage_be *be =find_ref_storage_backend (ref_storage_format); +if (!be) +return "unknown" ; +return be->name; +} +/* * How to handle various characters in refnames: * 0: An acceptable character for refs
@@ -2029,12 +2045,12 @@ static struct ref_store *ref_store_init(struct repository *repo,const char *gitdir, unsigned int flags) { - const char *be_name = "files"; -struct ref_storage_be *be =find_ref_storage_backend (be_name); + unsigned int format = REF_STORAGE_FORMAT_FILES; +const struct ref_storage_be *be =find_ref_storage_backend (format);struct ref_store *refs;if (!be) -BUG ("reference backend %s is unknown" , be_name); +BUG ("reference backend is unknown" ); refs = be->init (repo, gitdir, flags);return refs;
refs.h +3 -0
@@ -11,6 +11,9 @@ struct string_list;struct string_list_item ;struct worktree ; +unsigned int ref_storage_format_by_name (const char *name); +const char *ref_storage_format_to_name (unsigned int ref_storage_format); +/* * Resolve a reference, recursively following symbolic refererences. *
refs/debug.c +0 -1
@@ -426,7 +426,6 @@ static int debug_reflog_expire(struct ref_store *ref_store, const char *refname, }struct ref_storage_be refs_be_debug = { - .next =NULL , .name ="debug" , .init =NULL , .init_db = debug_init_db,
refs/files-backend.c +0 -1
@@ -3241,7 +3241,6 @@ static int files_init_db(struct ref_store *ref_store, struct strbuf *err UNUSED) }struct ref_storage_be refs_be_files = { - .next =NULL , .name ="files" , .init = files_ref_store_create, .init_db = files_init_db,
refs/packed-backend.c +0 -1
@@ -1705,7 +1705,6 @@ static struct ref_iterator *packed_reflog_iterator_begin(struct ref_store *ref_s }struct ref_storage_be refs_be_packed = { - .next =NULL , .name ="packed" , .init = packed_ref_store_create, .init_db = packed_init_db,
refs/refs-internal.h +0 -1
@@ -663,7 +663,6 @@ typedef int read_symbolic_ref_fn(struct ref_store *ref_store, const char *refnam struct strbuf *referent);struct ref_storage_be { -struct ref_storage_be *next;const char *name;ref_store_init_fn *init;ref_init_db_fn *init_db;
repository.h +3 -0
@@ -24,6 +24,9 @@ enum fetch_negotiation_setting { FETCH_NEGOTIATION_NOOP, }; +#define REF_STORAGE_FORMAT_UNKNOWN 0 +#define REF_STORAGE_FORMAT_FILES 1 +struct repo_settings {int initialized;
Signature · 1 line
2.43.GIT
PATCH v3 4/12 setup: start tracking ref storage format
In reply to Patrick SteinhardtIn order to discern which ref storage format a repository is supposed to use we need to start setting up and/or discovering the format. This needs to happen in two separate code paths.
- The first path is when we create a repository via init_db(). When we are re-initializing a preexisting repository we need to retain the previously used ref storage format -- if the user asked for a different format then this indicates an error and we error out. Otherwise we either initialize the repository with the format asked for by the user or the default format, which currently is the "files" backend.
- The second path is when discovering repositories, where we need to read the config of that repository. There is not yet any way to configure something other than the "files" backend, so we can just blindly set the ref storage format to this backend.
Wire up this logic so that we have the ref storage format always readily available when needed. As there is only a single backend and because it is not configurable we cannot yet verify that this tracking works as expected via tests, but tests will be added in subsequent commits. To countermand this ommission now though, raise a BUG() in case the ref storage format is not set up properly in ref_store_init().
--- builtin/clone.c | 5 +++-- builtin/init-db.c | 4 +++- refs.c | 4 ++-- repository.c | 6 ++++++ repository.h | 4 ++++ setup.c | 28 +++++++++++++++++++++++++--- setup.h | 6 +++++- 7 files changed, 48 insertions(+), 9 deletions(-)
builtin/clone.c +3 -2
@@ -1107,7 +1107,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)* repository, and reference backends may persist that information into * their on-disk data structures. */ -init_db (git_dir, real_git_dir, option_template,GIT_HASH_UNKNOWN ,NULL , +init_db (git_dir, real_git_dir, option_template,GIT_HASH_UNKNOWN , +REF_STORAGE_FORMAT_UNKNOWN ,NULL , do_not_override_repo_unix_permissions,INIT_DB_QUIET |INIT_DB_SKIP_REFDB );if (real_git_dir) {
@@ -1292,7 +1293,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix) hash_algo =hash_algo_by_ptr (transport_get_hash_algo (transport));initialize_repository_version (hash_algo,1 );repo_set_hash_algo (the_repository, hash_algo); -create_reference_database (NULL ,1 ); +create_reference_database (the_repository->ref_storage_format,NULL ,1 );/* * Before fetching from the remote, download and install bundle
builtin/init-db.c +3 -1
@@ -11,6 +11,7 @@#include "object-file.h" #include "parse-options.h" #include "path.h" +#include "refs.h" #include "setup.h" #include "strbuf.h"
@@ -236,5 +237,6 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) flags |=INIT_DB_EXIST_OK ;return init_db (git_dir, real_git_dir, template_dir, hash_algo, - initial_branch, init_shared_repository, flags); +REF_STORAGE_FORMAT_UNKNOWN , initial_branch, + init_shared_repository, flags); }
refs.c +2 -2
@@ -2045,10 +2045,10 @@ static struct ref_store *ref_store_init(struct repository *repo, const char *gitdir, unsigned int flags) { - unsigned int format = REF_STORAGE_FORMAT_FILES; -const struct ref_storage_be *be =find_ref_storage_backend (format); +const struct ref_storage_be *be;struct ref_store *refs; + be =find_ref_storage_backend (repo->ref_storage_format);if (!be)BUG ("reference backend is unknown" );
repository.c +6 -0
@@ -104,6 +104,11 @@ void repo_set_hash_algo(struct repository *repo, int hash_algo) repo->hash_algo = &hash_algos[hash_algo]; } +void repo_set_ref_storage_format (struct repository *repo,unsigned int format) +{ + repo->ref_storage_format = format; +} +/* * Attempt to resolve and set the provided 'gitdir' for repository 'repo'. * Return 0 upon success and a non-zero value upon failure.
@@ -184,6 +189,7 @@ int repo_init(struct repository *repo, goto error;repo_set_hash_algo (repo, format.hash_algo); +repo_set_ref_storage_format (repo, format.ref_storage_format); repo->repository_format_worktree_config = format.worktree_config;/* take ownership of format.partial_clone */
repository.h +4 -0
@@ -163,6 +163,9 @@ struct repository {/* Repository's current hash algorithm, as serialized on disk. */ const struct git_hash_algo *hash_algo; +/* Repository's reference storage format, as serialized on disk. */ +unsigned int ref_storage_format; +/* A unique-id for tracing purposes. */ int trace2_repo_id;
@@ -202,6 +205,7 @@ void repo_set_gitdir(struct repository *repo, const char *root, const struct set_gitdir_args *extra_args);void repo_set_worktree (struct repository *repo,const char *path);void repo_set_hash_algo (struct repository *repo,int algo); +void repo_set_ref_storage_format (struct repository *repo,unsigned int format);void initialize_the_repository (void );RESULT_MUST_BE_USED int repo_init(struct repository *r, const char *gitdir, const char *worktree);
setup.c +25 -3
@@ -1566,6 +1566,8 @@ const char *setup_git_directory_gently(int *nongit_ok) }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 */
@@ -1659,6 +1661,8 @@ void check_repository_format(struct repository_format *fmt)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 =
@@ -1899,7 +1903,8 @@ static int is_reinit(void)return ret; } -void create_reference_database (const char *initial_branch,int quiet) +void create_reference_database (unsigned int ref_storage_format, +const char *initial_branch,int quiet) {struct strbuf err =STRBUF_INIT ;int reinit =is_reinit ();
@@ -1919,6 +1924,7 @@ void create_reference_database(const char *initial_branch, int quiet)safe_create_dir (git_path ("refs" ),1 );adjust_shared_perm (git_path ("refs" )); +repo_set_ref_storage_format (the_repository, ref_storage_format);if (refs_init_db (&err))die ("failed to set up refs db: %s" , err.buf);
@@ -2137,8 +2143,22 @@ static void validate_hash_algorithm(struct repository_format *repo_fmt, int hash } } +static void validate_ref_storage_format (struct repository_format *repo_fmt, +unsigned int 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; + } +} +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;
@@ -2181,13 +2201,15 @@ int init_db(const char *git_dir, const char *real_git_dir,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, &repo_fmt, prev_bare_repository, init_shared_repository);if (!(flags &INIT_DB_SKIP_REFDB )) -create_reference_database (initial_branch, flags &INIT_DB_QUIET ); +create_reference_database (repo_fmt.ref_storage_format, + initial_branch, flags &INIT_DB_QUIET );create_object_directory ();if (get_shared_repository ()) {
setup.h +5 -1
@@ -115,6 +115,7 @@ struct repository_format {int worktree_config;int is_bare;int hash_algo; +unsigned int ref_storage_format;int sparse_index;char *work_tree;struct string_list unknown_extensions;
@@ -131,6 +132,7 @@ struct repository_format { .version = -1, \ .is_bare = -1, \ .hash_algo = GIT_HASH_SHA1, \ + .ref_storage_format = REF_STORAGE_FORMAT_FILES, \ .unknown_extensions = STRING_LIST_INIT_DUP, \ .v1_only_extensions = STRING_LIST_INIT_DUP, \ }
@@ -175,10 +177,12 @@ void check_repository_format(struct repository_format *fmt);int init_db (const char *git_dir,const char *real_git_dir,const char *template_dir,int hash_algo, +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 create_reference_database (const char *initial_branch,int quiet); +void create_reference_database (unsigned int ref_storage_format, +const char *initial_branch,int quiet);/* * NOTE NOTE NOTE!!
Signature · 1 line
2.43.GIT
PATCH v3 5/12 setup: set repository's formats on init
In reply to Patrick SteinhardtThe proper hash algorithm and ref storage format that will be used for a newly initialized repository will be figured out in init_db() via validate_hash_algorithm() and validate_ref_storage_format(). Until now though, we never set up the hash algorithm or ref storage format of the_repository accordingly.
There are only two callsites of init_db(), one in git-init(1) and one in git-clone(1). The former function doesn't care for the formats to be set up properly because it never access the repository after calling the function in the first place.
For git-clone(1) it's a different story though, as we call init_db() before listing remote refs. While we do indeed have the wrong hash function in the_repository when init_db() sets up a non-default object format for the repository, it never mattered because we adjust the hash after learning about the remote's hash function via the listed refs.
So the current state is correct for the hash algo, but it's not for the ref storage format because git-clone(1) wouldn't know to set it up properly. But instead of adjusting only the ref_storage_format, set both the hash algo and the ref storage format so that the_repository is in the correct state when init_db() exits. This is fine as we will adjust the hash later on anyway and makes it easier to reason about the end state of the_repository.
--- setup.c | 7 +++++++ 1 file changed, 7 insertions(+)
setup.c +7 -0
@@ -2207,6 +2207,13 @@ int init_db(const char *git_dir, const char *real_git_dir, &repo_fmt, prev_bare_repository, 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 );
Signature · 1 line
2.43.GIT
PATCH v3 6/12 setup: introduce "extensions.refStorage" extension
In reply to Patrick SteinhardtIntroduce 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]: <[email protected]>
--- Documentation/config/extensions.txt | 11 ++++++++ Documentation/ref-storage-format.txt | 1 + .../technical/repository-version.txt | 5 ++++ builtin/clone.c | 2 +- setup.c | 24 ++++++++++++++--- setup.h | 4 ++- t/t0001-init.sh | 26 +++++++++++++++++++ t/test-lib.sh | 2 +- 8 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 Documentation/ref-storage-format.txt
Documentation/config/extensions.txt +11 -0
@@ -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
Documentation/ref-storage-format.txt new file +1 -0
@@ -0,0 +1 @@ +* `files` for loose files with packed-refs. This is the default.
Documentation/technical/repository-version.txt +5 -0
@@ -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).
builtin/clone.c +1 -1
@@ -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 );
setup.c +21 -3
@@ -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" );
setup.h +3 -1
@@ -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);
t/t0001-init.sh +26 -0
@@ -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 &&
t/test-lib.sh +1 -1
@@ -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
Signature · 1 line
2.43.GIT
PATCH v3 7/12 setup: introduce GIT_DEFAULT_REF_FORMAT envvar
In reply to Patrick SteinhardtIntroduce a new GIT_DEFAULT_REF_FORMAT environment variable that lets users control the default ref format used by both git-init(1) and git-clone(1). This is modeled after GIT_DEFAULT_OBJECT_FORMAT, which does the same thing for the repository's object format.
--- Documentation/git.txt | 5 +++++ setup.c | 7 +++++++ t/t0001-init.sh | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+)
Documentation/git.txt +5 -0
@@ -556,6 +556,11 @@ double-quotes and respecting backslash escapes. E.g., the value is always used. The default is "sha1". See `--object-format` in linkgit:git-init[1]. +`GIT_DEFAULT_REF_FORMAT`:: + If this variable is set, the default reference backend format for new + repositories will be set to this value. The default is "files". + See `--ref-format` in linkgit:git-init[1]. + Git Commits ~~~~~~~~~~~ `GIT_AUTHOR_NAME`::
setup.c +7 -0
@@ -2164,12 +2164,19 @@ static void validate_hash_algorithm(struct repository_format *repo_fmt, int hashstatic 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; } }
t/t0001-init.sh +18 -0
@@ -558,6 +558,24 @@ test_expect_success DEFAULT_REPO_FORMAT 'extensions.refStorage with unknown back grep "invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}garbage${SQ}" err ' +test_expect_success DEFAULT_REPO_FORMAT 'init with GIT_DEFAULT_REF_FORMAT=files' ' + test_when_finished "rm -rf refformat" && + GIT_DEFAULT_REF_FORMAT=files git init refformat && + echo 0 >expect && + git -C refformat config core.repositoryformatversion >actual && + test_cmp expect actual && + test_must_fail git -C refformat config extensions.refstorage +' + +test_expect_success 'init with GIT_DEFAULT_REF_FORMAT=garbage' ' + test_when_finished "rm -rf refformat" && + cat >expect <<-EOF && + fatal: unknown ref storage format ${SQ}garbage${SQ} + EOF + test_must_fail env GIT_DEFAULT_REF_FORMAT=garbage git init refformat 2>err && + test_cmp expect err +' + test_expect_success MINGW 'core.hidedotfiles = false' ' git config --global core.hidedotfiles false && rm -rf newdir &&
Signature · 1 line
2.43.GIT
PATCH v3 8/12 t: introduce GIT_TEST_DEFAULT_REF_FORMAT envvar
In reply to Patrick SteinhardtIntroduce a new GIT_TEST_DEFAULT_REF_FORMAT environment variable that lets developers run the test suite with a different default ref format without impacting the ref format used by non-test Git invocations. This is modeled after GIT_TEST_DEFAULT_OBJECT_FORMAT, which does the same thing for the repository's object format.
Adapt the setup of the REFFILES test prerequisite to be conditionally set based on the default ref format.
--- t/README | 3 +++ t/test-lib-functions.sh | 5 +++++ t/test-lib.sh | 11 ++++++++++- 3 files changed, 18 insertions(+), 1 deletion(-)
t/README +3 -0
@@ -479,6 +479,9 @@ GIT_TEST_DEFAULT_HASH=<hash-algo> specifies which hash algorithm to use in the test scripts. Recognized values for <hash-algo> are "sha1" and "sha256". +GIT_TEST_DEFAULT_REF_FORMAT=<format> specifies which ref storage format +to use in the test scripts. Recognized values for <format> are "files". + GIT_TEST_NO_WRITE_REV_INDEX=<boolean>, when true disables the 'pack.writeReverseIndex' setting.
t/test-lib-functions.sh +5 -0
@@ -1659,6 +1659,11 @@ test_detect_hash () { test_hash_algo="${GIT_TEST_DEFAULT_HASH:-sha1}" } +# Detect the hash algorithm in use. +test_detect_ref_format () { + echo "${GIT_TEST_DEFAULT_REF_FORMAT:-files}" +} + # Load common hash metadata and common placeholder object IDs for use with # test_oid. test_oid_init () {
t/test-lib.sh +10 -1
@@ -542,6 +542,8 @@ export EDITOR GIT_DEFAULT_HASH="${GIT_TEST_DEFAULT_HASH:-sha1}" export GIT_DEFAULT_HASH +GIT_DEFAULT_REF_FORMAT="${GIT_TEST_DEFAULT_REF_FORMAT:-files}" +export GIT_DEFAULT_REF_FORMAT GIT_TEST_MERGE_ALGORITHM="${GIT_TEST_MERGE_ALGORITHM:-ort}" export GIT_TEST_MERGE_ALGORITHM
@@ -1745,7 +1747,14 @@ parisc* | hppa*) ;; esac -test_set_prereq REFFILES +case "$GIT_DEFAULT_REF_FORMAT" in +files) + test_set_prereq REFFILES;; +*) + echo 2>&1 "error: unknown ref format $GIT_DEFAULT_REF_FORMAT" + exit 1 + ;; +esac ( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1 test -z "$NO_CURL" && test_set_prereq LIBCURL
Signature · 1 line
2.43.GIT
PATCH v3 9/12 builtin/rev-parse: introduce `--show-ref-format` flag
In reply to Patrick SteinhardtIntroduce a new --show-ref-format to git-rev-parse(1) that causes it to print the ref format used by a repository.
--- Documentation/git-rev-parse.txt | 3 +++ builtin/rev-parse.c | 4 ++++ t/t1500-rev-parse.sh | 17 +++++++++++++++++ 3 files changed, 24 insertions(+)
Documentation/git-rev-parse.txt +3 -0
@@ -307,6 +307,9 @@ The following options are unaffected by `--path-format`: input, multiple algorithms may be printed, space-separated. If not specified, the default is "storage". +--show-ref-format:: + Show the reference storage format used for the repository. + Other Options ~~~~~~~~~~~~~
builtin/rev-parse.c +4 -0
@@ -1062,6 +1062,10 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)puts (the_hash_algo->name);continue ; } +if (!strcmp (arg,"--show-ref-format" )) { +puts (ref_storage_format_to_name (the_repository->ref_storage_format)); +continue ; + }if (!strcmp (arg,"--end-of-options" )) { seen_end_of_options =1 ;if (filter & (DO_FLAGS |DO_REVS ))
t/t1500-rev-parse.sh +17 -0
@@ -208,6 +208,23 @@ test_expect_success 'rev-parse --show-object-format in repo' ' grep "unknown mode for --show-object-format: squeamish-ossifrage" err ' +test_expect_success 'rev-parse --show-ref-format' ' + test_detect_ref_format >expect && + git rev-parse --show-ref-format >actual && + test_cmp expect actual +' + +test_expect_success 'rev-parse --show-ref-format with invalid storage' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + git config extensions.refstorage broken && + test_must_fail git rev-parse --show-ref-format 2>err && + grep "error: invalid value for ${SQ}extensions.refstorage${SQ}: ${SQ}broken${SQ}" err + ) +' + test_expect_success '--show-toplevel from subdir of working tree' ' pwd >expect && git -C sub/dir rev-parse --show-toplevel >actual &&
Signature · 1 line
2.43.GIT
PATCH v3 10/12 builtin/init: introduce `--ref-format=` value flag
In reply to Patrick SteinhardtIntroduce a new --ref-format value flag for git-init(1) that allows the user to specify the ref format that is to be used for a newly initialized repository.
--- Documentation/git-init.txt | 7 +++++++ builtin/init-db.c | 13 ++++++++++++- t/t0001-init.sh | 26 ++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-)
Documentation/git-init.txt +7 -0
@@ -11,6 +11,7 @@ SYNOPSIS [verse] 'git init' [-q | --quiet] [--bare] [--template=<template-directory>] [--separate-git-dir <git-dir>] [--object-format=<format>] + [--ref-format=<format>] [-b <branch-name> | --initial-branch=<branch-name>] [--shared[=<permissions>]] [<directory>]
@@ -57,6 +58,12 @@ values are 'sha1' and (if enabled) 'sha256'. 'sha1' is the default. + include::object-format-disclaimer.txt[] +--ref-format=<format>:: + +Specify the given ref storage format for the repository. The valid values are: ++ +include::ref-storage-format.txt[] + --template=<template-directory>:: Specify the directory from which templates will be used. (See the "TEMPLATE
builtin/init-db.c +12 -1
@@ -58,6 +58,7 @@ static int shared_callback(const struct option *opt, const char *arg, int unset)static const char *const init_db_usage[] = {N_ ("git init [-q | --quiet] [--bare] [--template=<template-directory>]\n" " [--separate-git-dir <git-dir>] [--object-format=<format>]\n" +" [--ref-format=<format>]\n" " [-b <branch-name> | --initial-branch=<branch-name>]\n" " [--shared[=<permissions>]] [<directory>]" ),NULL
@@ -77,8 +78,10 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)const char *template_dir =NULL ;unsigned int flags =0 ;const char *object_format =NULL ; +const char *ref_format =NULL ;const char *initial_branch =NULL ;int hash_algo =GIT_HASH_UNKNOWN ; +unsigned int ref_storage_format =REF_STORAGE_FORMAT_UNKNOWN ;int init_shared_repository =-1 ;const struct option init_db_options[] = { OPT_STRING(0, "template", &template_dir, N_("template-directory"),
@@ -96,6 +99,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) N_("override the name of the initial branch")),OPT_STRING (0 ,"object-format" , &object_format,N_ ("hash" ),N_ ("specify the hash algorithm to use" )), +OPT_STRING (0 ,"ref-format" , &ref_format,N_ ("format" ), +N_ ("specify the reference format to use" )), OPT_END() };
@@ -159,6 +164,12 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)die (_ ("unknown hash algorithm '%s'" ), object_format); } +if (ref_format) { + ref_storage_format =ref_storage_format_by_name (ref_format); +if (ref_storage_format ==REF_STORAGE_FORMAT_UNKNOWN ) +die (_ ("unknown ref storage format '%s'" ), ref_format); + } +if (init_shared_repository !=-1 )set_shared_repository (init_shared_repository);
@@ -237,6 +248,6 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) flags |=INIT_DB_EXIST_OK ;return init_db (git_dir, real_git_dir, template_dir, hash_algo, -REF_STORAGE_FORMAT_UNKNOWN , initial_branch, + ref_storage_format, initial_branch, init_shared_repository, flags); }
t/t0001-init.sh +26 -0
@@ -576,6 +576,32 @@ test_expect_success 'init with GIT_DEFAULT_REF_FORMAT=garbage' ' test_cmp expect err ' +test_expect_success 'init with --ref-format=files' ' + test_when_finished "rm -rf refformat" && + git init --ref-format=files refformat && + echo files >expect && + git -C refformat rev-parse --show-ref-format >actual && + test_cmp expect actual +' + +test_expect_success 're-init with same format' ' + test_when_finished "rm -rf refformat" && + git init --ref-format=files refformat && + git init --ref-format=files refformat && + echo files >expect && + git -C refformat rev-parse --show-ref-format >actual && + test_cmp expect actual +' + +test_expect_success 'init with --ref-format=garbage' ' + test_when_finished "rm -rf refformat" && + cat >expect <<-EOF && + fatal: unknown ref storage format ${SQ}garbage${SQ} + EOF + test_must_fail git init --ref-format=garbage refformat 2>err && + test_cmp expect err +' + test_expect_success MINGW 'core.hidedotfiles = false' ' git config --global core.hidedotfiles false && rm -rf newdir &&
Signature · 1 line
2.43.GIT
PATCH v3 11/12 builtin/clone: introduce `--ref-format=` value flag
In reply to Patrick SteinhardtIntroduce a new --ref-format value flag for git-clone(1) that allows the user to specify the ref format that is to be used for a newly initialized repository.
--- Documentation/git-clone.txt | 6 ++++++ builtin/clone.c | 12 +++++++++++- t/t5601-clone.sh | 17 +++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-)
Documentation/git-clone.txt +6 -0
@@ -311,6 +311,12 @@ or `--mirror` is given) The result is Git repository can be separated from working tree. +--ref-format=<ref-format:: + +Specify the given ref storage format for the repository. The valid values are: ++ +include::ref-storage-format.txt[] + -j <n>:: --jobs <n>:: The number of submodules fetched at the same time.
builtin/clone.c +11 -1
@@ -72,6 +72,7 @@ static char *remote_name = NULL;static char *option_branch =NULL ;static struct string_list option_not =STRING_LIST_INIT_NODUP ;static const char *real_git_dir; +static const char *ref_format;static char *option_upload_pack ="git-upload-pack" ;static int option_verbosity;static int option_progress =-1 ;
@@ -157,6 +158,8 @@ static struct option builtin_clone_options[] = { N_("any cloned submodules will be shallow")),OPT_STRING (0 ,"separate-git-dir" , &real_git_dir,N_ ("gitdir" ),N_ ("separate git dir from working tree" )), +OPT_STRING (0 ,"ref-format" , &ref_format,N_ ("format" ), +N_ ("specify the reference format to use" )),OPT_STRING_LIST ('c' ,"config" , &option_config,N_ ("key=value" ),N_ ("set config inside the new repository" )), OPT_STRING_LIST(0, "server-option", &server_options,
@@ -932,6 +935,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)int submodule_progress;int filter_submodules =0 ;int hash_algo; +unsigned int ref_storage_format =REF_STORAGE_FORMAT_UNKNOWN ;const int do_not_override_repo_unix_permissions =-1 ; struct transport_ls_refs_options transport_ls_refs_options =
@@ -957,6 +961,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)if (option_single_branch ==-1 ) option_single_branch = deepen ?1 :0 ; +if (ref_format) { + ref_storage_format =ref_storage_format_by_name (ref_format); +if (ref_storage_format ==REF_STORAGE_FORMAT_UNKNOWN ) +die (_ ("unknown ref storage format '%s'" ), ref_format); + } +if (option_mirror) option_bare =1 ;
@@ -1108,7 +1118,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)* their on-disk data structures. */ init_db (git_dir, real_git_dir, option_template,GIT_HASH_UNKNOWN , -REF_STORAGE_FORMAT_UNKNOWN ,NULL , + ref_storage_format,NULL , do_not_override_repo_unix_permissions,INIT_DB_QUIET |INIT_DB_SKIP_REFDB );if (real_git_dir) {
t/t5601-clone.sh +17 -0
@@ -157,6 +157,23 @@ test_expect_success 'clone --mirror does not repeat tags' ' ' +test_expect_success 'clone with files ref format' ' + test_when_finished "rm -rf ref-storage" && + git clone --ref-format=files --mirror src ref-storage && + echo files >expect && + git -C ref-storage rev-parse --show-ref-format >actual && + test_cmp expect actual +' + +test_expect_success 'clone with garbage ref format' ' + cat >expect <<-EOF && + fatal: unknown ref storage format ${SQ}garbage${SQ} + EOF + test_must_fail git clone --ref-format=garbage --mirror src ref-storage 2>err && + test_cmp expect err && + test_path_is_missing ref-storage +' + test_expect_success 'clone to destination with trailing /' ' git clone src target-1/ &&
Signature · 1 line
2.43.GIT
PATCH v3 12/12 t9500: write "extensions.refstorage" into config
In reply to Patrick SteinhardtIn t9500 we're writing a custom configuration that sets up gitweb. This requires us to manually ensure that the repository format is configured as required, including both the repository format version and extensions. With the introduction of the "extensions.refStorage" extension we need to update the test to also write this new one.
--- t/t9500-gitweb-standalone-no-errors.sh | 5 +++++ 1 file changed, 5 insertions(+)
t/t9500-gitweb-standalone-no-errors.sh +5 -0
@@ -627,6 +627,7 @@ test_expect_success \ test_expect_success 'setup' ' version=$(git config core.repositoryformatversion) && algo=$(test_might_fail git config extensions.objectformat) && + refstorage=$(test_might_fail git config extensions.refstorage) && cat >.git/config <<-\EOF && # testing noval and alternate separator [gitweb]
@@ -637,6 +638,10 @@ test_expect_success 'setup' ' if test -n "$algo" then git config extensions.objectformat "$algo" + fi && + if test -n "$refstorage" + then + git config extensions.refstorage "$refstorage" fi '
Signature · 1 line
2.43.GIT
Quoted 8 lines · Patrick Steinhardt
this is the third version of my patch series that introduces the new
refStorageextension. This extension will be used for the upcoming reftable backend.Changes compared to v3:
- Reworded the commit message in patch 2, proposed by Eric.
- Added a NEEDSWORK comment toget_worktrees_internal(), propose by Eric.
Thanks for addressing my minor review comments.