Create 'feature.*' config area and some centralized config parsing
Subsystems (2)
cover Create 'feature.*' config area and some centralized config parsing
This is a brand-new thread to replace ds/early-access. The discussion on that thread was very helpful [1].
With this in mind, I propose instead a set of "feature.*" config settings that form groups of "community recommended" settings (with some caveats). In the space below, I'll list a set of possible feature names and the implied config options.
First, the main two categories we've discussed so far: many commits and many files. These two feature sets are for when your repo is large in one of these dimensions. Perhaps there are other settings to include in these?
feature.manyFiles:
index.version = 4
core.untrackedCache = true
feature.manyCommits:
core.commitGraph = true
gc.writeCommitGraph = true
(future: fetch.writeSplitCommitGraph = true)
Note: the fetch.writeSplitCommitGraph does not exist yet, but could be introduced in a later release to write a new commit-graph (with --split) on fetch.
The other category that has been discussed already is that of "experimental features that we generally think are helpful but change behavior slightly in some cases".
feature.experimental:
pack.useSparse = true
merge.directoryRenames = true
fetch.negotiationAlgorithm = skipping
Specifically, this setting is for config values we are not sure will ever be on by default, but additional testing is needed to be sure. This is different than a possible 'feature.preview' setting that would include config settings that we are committed to updating the defaults in a future release. There are many ways we can take this idea in the future (including more additions to these categories).
Thanks, -Stolee
[1] https://public-inbox.org/git/[email protected]/
Derrick Stolee (5):
repo-settings: consolidate some config settings
repo-settings: add feature.manyCommits setting
repo-settings: parse core.untrackedCache
repo-settings: create feature.manyFiles setting
repo-settings: create feature.experimental setting
Documentation/config.txt | 2 + Documentation/config/core.txt | 7 +- Documentation/config/feature.txt | 42 +++++++++++ Documentation/config/fetch.txt | 3 +- Documentation/config/gc.txt | 4 +- Documentation/config/index.txt | 1 + Documentation/config/merge.txt | 3 +- Documentation/config/pack.txt | 3 +- Makefile | 1 + builtin/am.c | 4 +- builtin/gc.c | 13 ++-- builtin/pack-objects.c | 9 ++- builtin/update-index.c | 7 +- commit-graph.c | 7 +- config.c | 24 ------ fetch-negotiator.c | 26 ++++--- fetch-negotiator.h | 5 +- fetch-pack.c | 11 ++- merge-recursive.c | 32 ++++---- merge-recursive.h | 1 - read-cache.c | 31 ++++---- repo-settings.c | 108 +++++++++++++++++++++++++++ repo-settings.h | 29 +++++++ repository.h | 3 + t/t1600-index.sh | 31 ++++++-- t/t5552-skipping-fetch-negotiator.sh | 23 ------ 26 files changed, 303 insertions(+), 127 deletions(-) create mode 100644 Documentation/config/feature.txt create mode 100644 repo-settings.c create mode 100644 repo-settings.h
base-commit: 9c9b961d7eb15fb583a2a812088713a68a85f1c0
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-292%2Fderrickstolee%2Frepo-settings%2Fhead-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-292/derrickstolee/repo-settings/head-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/292
Signature · 1 line
gitgitgadget
PATCH 1/5 repo-settings: consolidate some config settings
From: Derrick Stolee <[email protected]>
There are a few important config settings that are not loaded during git_default_config. These are instead loaded on-demand.
Centralize these config options to a single scan, and store all of the values in a repo_settings struct. The values for each setting are initialized as negative to indicate "unset".
This centralization will be particularly important in a later change to introduce "meta" config settings that change the defaults for these config settings.
--- Makefile | 1 + builtin/gc.c | 13 ++++++------- builtin/pack-objects.c | 9 +++++---- commit-graph.c | 7 ++++--- read-cache.c | 12 +++++++----- repo-settings.c | 44 ++++++++++++++++++++++++++++++++++++++++++ repo-settings.h | 15 ++++++++++++++ repository.h | 3 +++ 8 files changed, 85 insertions(+), 19 deletions(-) create mode 100644 repo-settings.c create mode 100644 repo-settings.h
Makefile +1 -0
@@ -964,6 +964,7 @@ LIB_OBJS += refspec.o LIB_OBJS += ref-filter.o LIB_OBJS += remote.o LIB_OBJS += replace-object.o +LIB_OBJS += repo-settings.o LIB_OBJS += repository.o LIB_OBJS += rerere.o LIB_OBJS += resolve-undo.o
builtin/gc.c +6 -7
@@ -27,6 +27,7 @@#include "pack-objects.h" #include "blob.h" #include "tree.h" +#include "repo-settings.h" #define FAILED_RUN "failed to run %s"
@@ -41,7 +42,6 @@ static int aggressive_depth = 50;static int aggressive_window =250 ;static int gc_auto_threshold =6700 ;static int gc_auto_pack_limit =50 ; -static int gc_write_commit_graph;static int detach_auto =1 ;static timestamp_t gc_log_expire_time;static const char *gc_log_expire ="1.day.ago" ;
@@ -148,7 +148,6 @@ static void gc_config(void)git_config_get_int ("gc.aggressivedepth" , &aggressive_depth);git_config_get_int ("gc.auto" , &gc_auto_threshold);git_config_get_int ("gc.autopacklimit" , &gc_auto_pack_limit); -git_config_get_bool ("gc.writecommitgraph" , &gc_write_commit_graph);git_config_get_bool ("gc.autodetach" , &detach_auto);git_config_get_expiry ("gc.pruneexpire" , &prune_expire);git_config_get_expiry ("gc.worktreepruneexpire" , &prune_worktrees_expire);
@@ -685,11 +684,11 @@ int cmd_gc(int argc, const char **argv, const char *prefix)clean_pack_garbage (); } -if (gc_write_commit_graph && -write_commit_graph_reachable (get_object_directory (), - !quiet && !daemonized ?COMMIT_GRAPH_PROGRESS :0 , -NULL )) -return 1 ; +prepare_repo_settings (the_repository); +if (the_repository->settings->gc_write_commit_graph ==1 ) +write_commit_graph_reachable (get_object_directory (), + !quiet && !daemonized ?COMMIT_GRAPH_PROGRESS :0 , +NULL );if (auto_gc &&too_many_loose_objects ()) warning(_("There are too many unreachable loose objects; "
builtin/pack-objects.c +5 -4
@@ -34,6 +34,7 @@#include "dir.h" #include "midx.h" #include "trace2.h" +#include "repo-settings.h" #define IN_PACK (obj) oe_in_pack(&to_pack, obj)#define SIZE (obj) oe_size(&to_pack, obj)
@@ -2709,10 +2710,6 @@ static int git_pack_config(const char *k, const char *v, void *cb) use_bitmap_index_default =git_config_bool (k, v);return 0 ; } -if (!strcmp (k,"pack.usesparse" )) { - sparse =git_config_bool (k, v); -return 0 ; - }if (!strcmp (k,"pack.threads" )) { delta_search_threads =git_config_int (k, v);if (delta_search_threads <0 )
@@ -3332,6 +3329,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) read_replace_refs =0 ; sparse =git_env_bool ("GIT_TEST_PACK_SPARSE" ,0 ); +prepare_repo_settings (the_repository); +if (!sparse && the_repository->settings->pack_use_sparse !=-1 ) + sparse = the_repository->settings->pack_use_sparse; +reset_pack_idx_option (&pack_idx_opts);git_config (git_pack_config,NULL );
commit-graph.c +4 -3
@@ -16,6 +16,7 @@#include "hashmap.h" #include "replace-object.h" #include "progress.h" +#include "repo-settings.h" #define GRAPH_SIGNATURE 0x43475048/* "CGPH" */ #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446/* "OIDF" */
@@ -466,7 +467,6 @@ static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)static int prepare_commit_graph (struct repository *r) {struct object_directory *odb; -int config_value;if (git_env_bool (GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD ,0 )) die("dying as requested by the '%s' variable on commit-graph load!",
@@ -476,9 +476,10 @@ static int prepare_commit_graph(struct repository *r)return !!r->objects->commit_graph; r->objects->commit_graph_attempted =1 ; +prepare_repo_settings (r); +if (!git_env_bool (GIT_TEST_COMMIT_GRAPH ,0 ) && - (repo_config_get_bool (r,"core.commitgraph" , &config_value) || - !config_value)) + r->settings->core_commit_graph !=1 )/* * This repository is not configured to use commit graphs, so * do not load one. (But report commit_graph_attempted anyway
read-cache.c +7 -5
@@ -25,6 +25,7 @@#include "fsmonitor.h" #include "thread-utils.h" #include "progress.h" +#include "repo-settings.h" /* Mask for the name length in ce_flags in the on-disk index */
@@ -1599,16 +1600,17 @@ struct cache_entry *refresh_cache_entry(struct index_state *istate,#define INDEX_FORMAT_DEFAULT 3 -static unsigned int get_index_format_default (void ) +static unsigned int get_index_format_default (struct repository *r) {char *envversion =getenv ("GIT_INDEX_VERSION" );char *endp; -int value;unsigned int version =INDEX_FORMAT_DEFAULT ;if (!envversion) { -if (!git_config_get_int ("index.version" , &value)) - version = value; +prepare_repo_settings (r); + +if (r->settings->index_version >=0 ) + version = r->settings->index_version;if (version <INDEX_FORMAT_LB ||INDEX_FORMAT_UB < version) {warning (_ ("index.version set, but the value is invalid.\n" "Using version %i" ),INDEX_FORMAT_DEFAULT );
@@ -2765,7 +2767,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, }if (!istate->version) { - istate->version =get_index_format_default (); + istate->version =get_index_format_default (the_repository);if (git_env_bool ("GIT_TEST_SPLIT_INDEX" ,0 ))init_split_index (istate); }
repo-settings.c new file +44 -0
44 lines not on this page · raw message
repo-settings.h new file +15 -0
@@ -0,0 +1,15 @@ +#ifndef REPO_SETTINGS_H +#define REPO_SETTINGS_H + +struct repo_settings { +int core_commit_graph; +int gc_write_commit_graph; +int pack_use_sparse; +int index_version; +}; + +struct repository ; + +void prepare_repo_settings (struct repository *r); + +#endif /* REPO_SETTINGS_H */
repository.h +3 -0
@@ -4,6 +4,7 @@#include "path.h" struct config_set ; +struct repo_settings ;struct git_hash_algo ;struct index_state ;struct lock_file ;
@@ -72,6 +73,8 @@ struct repository {*/ char *submodule_prefix; +struct repo_settings *settings; +/* Subsystems */ /* * Repository's config which contains key-value pairs from the usual
Signature · 1 line
gitgitgadget
Re: [PATCH 1/5] repo-settings: consolidate some config settings
Hi Stolee,
Quoted 13 lines · Derrick Stolee via GitGitGadget
builtin/gc.c +1
-0@@ -27,6 +27,7 @@#include "pack-objects.h" #include "blob.h" #include "tree.h" +#include "repo-settings.h" #define FAILED_RUN "failed to run %s"
builtin/gc.c static int aggressive_depth = 50; · line 44
Quoted 4 lines
builtin/gc.c +0
-1@@ -41,7 +42,6 @@ static int aggressive_depth = 50;static int aggressive_window =250 ;static int gc_auto_threshold =6700 ;static int gc_auto_pack_limit =50 ; -static int gc_write_commit_graph;
I _really_ like that direction. Anything that removes global state will improve Git's source code.
read-cache.c static int do_write_index(struct index_state *istate, struct tempfile *tempfile, · line 2770
Quoted 12 lines
[...]
read-cache.c +1
-1@@ -2765,7 +2767,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, }if (!istate->version) { - istate->version =get_index_format_default (); + istate->version =get_index_format_default (the_repository);
It is too bad that read-cache.h is not the_repository-free at the moment...
Quoted 24 lines
read-cache.c +0
-0if (git_env_bool ("GIT_TEST_SPLIT_INDEX" ,0 ))init_split_index (istate); }repo-settings.c new file +15
-0@@ -0,0 +1,44 @@ +#include "cache.h" +#include "repository.h" +#include "config.h" +#include "repo-settings.h" + +static int git_repo_config (const char *key,const char *value,void *cb) +{ +struct repo_settings *rs = (struct repo_settings *)cb; + +if (!strcmp (key,"core.commitgraph" )) { + rs->core_commit_graph =git_config_bool (key, value); +return 0 ; + } +if (!strcmp (key,"gc.writecommitgraph" )) { + rs->gc_write_commit_graph =git_config_bool (key, value);
repo-settings.c line 25
Quoted 10 lines
repo-settings.c new file +10
-0+return 0 ; + } +if (!strcmp (key,"pack.usesparse" )) { + rs->pack_use_sparse =git_config_bool (key, value); +return 0 ; + } +if (!strcmp (key,"index.version" )) { + rs->index_version =git_config_int (key, value); +return 0 ; + }
I would actually prefer to use the repo_config_get_*() family here. That way, we really avoid re-parsing the config.
Quoted 28 lines
repo-settings.c new file +19
-0+ +return 1 ; +} + +void prepare_repo_settings (struct repository *r) +{ +if (r->settings) +return ; + + r->settings =xmalloc (sizeof (*r->settings)); + +/* Defaults */ + r->settings->core_commit_graph =-1 ; + r->settings->gc_write_commit_graph =-1 ; + r->settings->pack_use_sparse =-1 ; + r->settings->index_version =-1 ; + +repo_config (r, git_repo_config, r->settings); +}repo-settings.h new file +3
-0@@ -0,0 +1,15 @@ +#ifndef REPO_SETTINGS_H +#define REPO_SETTINGS_H +
repo-settings.h line 13
Quoted 10 lines
repo-settings.h new file +10
-0+struct repo_settings { +int core_commit_graph; +int gc_write_commit_graph; +int pack_use_sparse; +int index_version; +}; + +struct repository ; + +void prepare_repo_settings (struct repository *r);
Hmm. I can see that you wanted to encapsulate this, but I do not really agree that this needs to be encapsulated away from repository.h. I'd rather declare struct repo_settings in repository.h and then make the settings a field of that type (as opposed to a pointer to that type). In general, I like to avoid unnecessary malloc()s, and this here instance is one of them.
Thanks,
Dscho
Quoted 27 lines
repo-settings.h new file +2
-0+ +#endif /* REPO_SETTINGS_H */ repository.h +3
-1@@ -4,6 +4,7 @@#include "path.h" struct config_set ; +struct repo_settings ;struct git_hash_algo ;struct index_state ;struct lock_file ;@@ -72,6 +73,8 @@ struct repository {*/ char *submodule_prefix; +struct repo_settings *settings; +/* Subsystems */ /* * Repository's config which contains key-value pairs from the usual -- gitgitgadget
PATCH 2/5 repo-settings: add feature.manyCommits setting
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
When a repo has many commits, it is helpful to write and read the commit-graph file. Future changes to Git may include new config settings that are benefitial in this scenario.
Create the 'feature.manyCommits' config setting that changes the default values of 'core.commitGraph' and 'gc.writeCommitGraph' to true.
--- Documentation/config.txt | 2 ++ Documentation/config/core.txt | 3 ++- Documentation/config/feature.txt | 15 +++++++++++++++ Documentation/config/gc.txt | 4 ++-- repo-settings.c | 7 +++++++ 5 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 Documentation/config/feature.txt
Documentation/config.txt +2 -0
@@ -345,6 +345,8 @@ include::config/difftool.txt[] include::config/fastimport.txt[] +include::config/feature.txt[] + include::config/fetch.txt[] include::config/format.txt[]
Documentation/config/core.txt +2 -1
@@ -577,7 +577,8 @@ the `GIT_NOTES_REF` environment variable. See linkgit:git-notes[1]. core.commitGraph:: If true, then git will read the commit-graph file (if it exists) - to parse the graph structure of commits. Defaults to false. See + to parse the graph structure of commits. Defaults to false, unless + `feature.manyCommits` is enabled. See linkgit:git-commit-graph[1] for more information. core.useReplaceRefs::
Documentation/config/feature.txt new file +15 -0
@@ -0,0 +1,15 @@ +feature.*:: + The config settings that start with `feature.` modify the defaults of + a group of other config settings. These groups are created by the Git + developer community as recommended defaults and are subject to change. + In particular, new config options may be added with different defaults. + +feature.manyCommits:: + Enable config options that optimize for repos with many commits. This + setting is recommended for repos with at least 100,000 commits. The + new default values are: ++ +* `core.commitGraph=true` enables reading the commit-graph file. ++ +* `gc.writeCommitGraph=true` enables writing the commit-graph file during +garbage collection.
Documentation/config/gc.txt +2 -2
@@ -63,8 +63,8 @@ gc.writeCommitGraph:: If true, then gc will rewrite the commit-graph file when linkgit:git-gc[1] is run. When using `git gc --auto` the commit-graph will be updated if housekeeping is - required. Default is false. See linkgit:git-commit-graph[1] - for details. + required. Default is false, unless `feature.manyCommits` + is enabled. See linkgit:git-commit-graph[1] for details. gc.logExpiry:: If the file gc.log exists, then `git gc --auto` will print
repo-settings.c +7 -0
@@ -3,10 +3,17 @@#include "config.h" #include "repo-settings.h" +#define UPDATE_DEFAULT (s,v) do { if (s == -1) { s = v; } } while(0) +static int git_repo_config (const char *key,const char *value,void *cb) {struct repo_settings *rs = (struct repo_settings *)cb; +if (!strcmp (key,"feature.manycommits" )) { +UPDATE_DEFAULT (rs->core_commit_graph,1 ); +UPDATE_DEFAULT (rs->gc_write_commit_graph,1 ); +return 0 ; + }if (!strcmp (key,"core.commitgraph" )) { rs->core_commit_graph =git_config_bool (key, value);return 0 ;
Signature · 1 line
gitgitgadget
Re: [PATCH 2/5] repo-settings: add feature.manyCommits setting
Hi Stolee,
Quoted 5 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
When a repo has many commits, it is helpful to write and read the commit-graph file. Future changes to Git may include new config settings that are benefitial in this scenario.
s/benefitial/beneficial/
Quoted 4 lines
Create the 'feature.manyCommits' config setting that changes the default values of 'core.commitGraph' and 'gc.writeCommitGraph' to true.
Great!
Quoted 12 lines
repo-settings.c +2
-0@@ -3,10 +3,17 @@#include "config.h" #include "repo-settings.h" +#define UPDATE_DEFAULT (s,v) do { if (s == -1) { s = v; } } while(0) +static int git_repo_config (const char *key,const char *value,void *cb) {
repo-settings.c line 19
Quoted 10 lines
repo-settings.c +5
-0struct repo_settings *rs = (struct repo_settings *)cb; +if (!strcmp (key,"feature.manycommits" )) { +UPDATE_DEFAULT (rs->core_commit_graph,1 ); +UPDATE_DEFAULT (rs->gc_write_commit_graph,1 ); +return 0 ; + }if (!strcmp (key,"core.commitgraph" )) { rs->core_commit_graph =git_config_bool (key, value);return 0 ;
Okay, this one is tricky. The behavior I would want is for feature.manycommits to override the _default_. And if I set feature.manycommits = false (e.g. via git -c feature.manycommits=false ...), I would want the default to "go back".
So I'd really rather see this as
if (!repo_config_get_bool(r, "feature.manycommits", &b) && b) {
rs->core_commit_graph = 1;
rs->gc_write_commit_graph = 1;
}
[...]
repo_config_get_bool(r, "core.commitgraph", &rs->core_commit_graph);I.e. override the default only if feature.manyCommits was true *in the end*.
In any case, we really need to make sure to handle the = false case correctly here. We might want to override the setting from the command-line, or it might be set in ~/.gitconfig and need to be overridden in a local repository. We need to handle it.
Otherwise this looks good!
Dscho
Re: [PATCH 2/5] repo-settings: add feature.manyCommits setting
Quoted 58 lines · Johannes Schindelin
Hi Stolee,
Quoted 5 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
When a repo has many commits, it is helpful to write and read the commit-graph file. Future changes to Git may include new config settings that are benefitial in this scenario.
s/benefitial/beneficial/
Quoted 4 lines
Create the 'feature.manyCommits' config setting that changes the default values of 'core.commitGraph' and 'gc.writeCommitGraph' to true.
Great!
Quoted 12 lines
repo-settings.c +2
-0@@ -3,10 +3,17 @@#include "config.h" #include "repo-settings.h" +#define UPDATE_DEFAULT (s,v) do { if (s == -1) { s = v; } } while(0) +static int git_repo_config (const char *key,const char *value,void *cb) {repo-settings.c line 19
Quoted 10 lines
repo-settings.c +5
-0struct repo_settings *rs = (struct repo_settings *)cb; +if (!strcmp (key,"feature.manycommits" )) { +UPDATE_DEFAULT (rs->core_commit_graph,1 ); +UPDATE_DEFAULT (rs->gc_write_commit_graph,1 ); +return 0 ; + }if (!strcmp (key,"core.commitgraph" )) { rs->core_commit_graph =git_config_bool (key, value);return 0 ;Okay, this one is tricky. The behavior I would want is for
feature.manycommitsto override the _default_. And if I setfeature.manycommits = false(e.g. viagit -c feature.manycommits=false ...), I would want the default to "go back".So I'd really rather see this as
if (!repo_config_get_bool(r, "feature.manycommits", &b) && b) { rs->core_commit_graph = 1; rs->gc_write_commit_graph = 1; } [...] repo_config_get_bool(r, "core.commitgraph", &rs->core_commit_graph);
Quoted 7 lines
I.e. override the default only if
feature.manyCommitswas true *in the end*.In any case, we really need to make sure to handle the
= falsecase correctly here. We might want to override the setting from the command-line, or it might be set in~/.gitconfigand need to be overridden in a local repository. We need to handle it.
I had forgotten about this interaction. Thanks for finding it!
The way I would fix it is to add an "int feature_many_commits" to the repo_settings struct, then check its value at the very end of prepare_repo_settings() and then UPDATE_DEFAULT() for the settings we want.
Thanks!
-Stolee
PATCH 3/5 repo-settings: parse core.untrackedCache
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The core.untrackedCache config setting is slightly complicated, so clarify its use and centralize its parsing into the repo settings.
The default value is "keep" (returned as -1), which persists the untracked cache if it exists.
If the value is set as "false" (returned as 0), then remove the untracked cache if it exists.
If the value is set as "true" (returned as 1), then write the untracked cache and persist it.
Instead of relying on magic values of -1, 0, and 1, split these options into bitflags CORE_UNTRACKED_CACHE_KEEP and CORE_UNTRACKED_CACHE_WRITE. This allows the use of "-1" as a default value. After parsing the config options, if the value is unset we can initialize it to CORE_UNTRACKED_CACHE_KEEP.
--- builtin/update-index.c | 7 +++++-- config.c | 24 ------------------------ read-cache.c | 19 +++++++++---------- repo-settings.c | 21 +++++++++++++++++++++ repo-settings.h | 4 ++++ 5 files changed, 39 insertions(+), 36 deletions(-)
builtin/update-index.c +5 -2
@@ -18,6 +18,7 @@#include "dir.h" #include "split-index.h" #include "fsmonitor.h" +#include "repo-settings.h" /* * Default to not allowing changes to the list of files. The
@@ -966,6 +967,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)struct parse_opt_ctx_t ctx;strbuf_getline_fn getline_fn;int parseopt_state =PARSE_OPT_UNKNOWN ; +struct repository *r = the_repository;struct option options[] = { OPT_BIT('q', NULL, &refresh_args.flags, N_("continue refresh even when index needs update"),
@@ -1180,11 +1182,12 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)remove_split_index (&the_index); } +prepare_repo_settings (r);switch (untracked_cache) {case UC_UNSPECIFIED :break ;case UC_DISABLE : -if (git_config_get_untracked_cache () ==1 ) +if (r->settings->core_untracked_cache &CORE_UNTRACKED_CACHE_WRITE )warning (_ ("core.untrackedCache is set to true; " "remove or change it, if you really want to " "disable the untracked cache" ));
@@ -1196,7 +1199,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)return !test_if_untracked_cache_is_supported ();case UC_ENABLE :case UC_FORCE : -if (git_config_get_untracked_cache () ==0 ) +if (r->settings->core_untracked_cache ==0 )warning (_ ("core.untrackedCache is set to false; " "remove or change it, if you really want to " "enable the untracked cache" ));
config.c +0 -24
@@ -2277,30 +2277,6 @@ int git_config_get_expiry_in_days(const char *key, timestamp_t *expiry, timestamreturn -1 ;/* thing exists but cannot be parsed */ } -int git_config_get_untracked_cache (void ) -{ -int val =-1 ; -const char *v; - -/* Hack for test programs like test-dump-untracked-cache */ -if (ignore_untracked_cache_config) -return -1 ; - -if (!git_config_get_maybe_bool ("core.untrackedcache" , &val)) -return val; - -if (!git_config_get_value ("core.untrackedcache" , &v)) { -if (!strcasecmp (v,"keep" )) -return -1 ; - -error (_ ("unknown core.untrackedCache value '%s'; " -"using 'keep' default value" ), v); -return -1 ; - } - -return -1 ;/* default value */ -} -int git_config_get_split_index (void ) {int val;
read-cache.c +9 -10
@@ -1846,18 +1846,17 @@ static void check_ce_order(struct index_state *istate)static void tweak_untracked_cache (struct index_state *istate) { -switch (git_config_get_untracked_cache ()) { -case -1 :/* keep: do nothing */ -break ; -case 0 :/* false */ +struct repository *r = the_repository; + +prepare_repo_settings (r); + +if (!(r->settings->core_untracked_cache &CORE_UNTRACKED_CACHE_KEEP )) {remove_untracked_cache (istate); -break ; -case 1 :/* true */ -add_untracked_cache (istate); -break ; -default :/* unknown value: do nothing */ -break ; +return ; } + +if (r->settings->core_untracked_cache &CORE_UNTRACKED_CACHE_WRITE ) +add_untracked_cache (istate); }static void tweak_split_index (struct index_state *istate)
repo-settings.c +21 -0
@@ -30,6 +30,20 @@ static int git_repo_config(const char *key, const char *value, void *cb) rs->index_version =git_config_int (key, value);return 0 ; } +if (!strcmp (key,"core.untrackedcache" )) { +int bool_value =git_parse_maybe_bool (value); +if (bool_value ==0 ) + rs->core_untracked_cache =0 ; +else if (bool_value ==1 ) + rs->core_untracked_cache =CORE_UNTRACKED_CACHE_KEEP | +CORE_UNTRACKED_CACHE_WRITE ; +else if (!strcasecmp (value,"keep" )) + rs->core_untracked_cache =CORE_UNTRACKED_CACHE_KEEP ; +else +error (_ ("unknown core.untrackedCache value '%s'; " +"using 'keep' default value" ), value); +return 0 ; + }return 1 ; }
@@ -46,6 +60,13 @@ void prepare_repo_settings(struct repository *r) r->settings->gc_write_commit_graph =-1 ; r->settings->pack_use_sparse =-1 ; r->settings->index_version =-1 ; + r->settings->core_untracked_cache =-1 ;repo_config (r, git_repo_config, r->settings); + +/* Hack for test programs like test-dump-untracked-cache */ +if (ignore_untracked_cache_config) + r->settings->core_untracked_cache =CORE_UNTRACKED_CACHE_KEEP ; +else +UPDATE_DEFAULT (r->settings->core_untracked_cache,CORE_UNTRACKED_CACHE_KEEP ); }
repo-settings.h +4 -0
@@ -1,11 +1,15 @@#ifndef REPO_SETTINGS_H #define REPO_SETTINGS_H +#define CORE_UNTRACKED_CACHE_WRITE (1 << 0) +#define CORE_UNTRACKED_CACHE_KEEP (1 << 1) +struct repo_settings {int core_commit_graph;int gc_write_commit_graph;int pack_use_sparse;int index_version; +int core_untracked_cache; };struct repository ;
Signature · 1 line
gitgitgadget
Re: [PATCH 3/5] repo-settings: parse core.untrackedCache
Hi Stolee,
Quoted 12 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
The core.untrackedCache config setting is slightly complicated, so clarify its use and centralize its parsing into the repo settings.
The default value is "keep" (returned as -1), which persists the untracked cache if it exists.
If the value is set as "false" (returned as 0), then remove the untracked cache if it exists.
Quoted 8 lines
If the value is set as "true" (returned as 1), then write the untracked cache and persist it.
Instead of relying on magic values of -1, 0, and 1, split these options into bitflags
CORE_UNTRACKED_CACHE_KEEPandCORE_UNTRACKED_CACHE_WRITE. This allows the use of "-1" as a default value. After parsing the config options, if the value is unset we can initialize it toCORE_UNTRACKED_CACHE_KEEP.
Nice!
Quoted 20 lines
[...]
read-cache.c +5
-5@@ -1846,18 +1846,17 @@ static void check_ce_order(struct index_state *istate)static void tweak_untracked_cache (struct index_state *istate) { -switch (git_config_get_untracked_cache ()) { -case -1 :/* keep: do nothing */ -break ; -case 0 :/* false */ +struct repository *r = the_repository; + +prepare_repo_settings (r); + +if (!(r->settings->core_untracked_cache &CORE_UNTRACKED_CACHE_KEEP )) {remove_untracked_cache (istate); -break ;
read-cache.c static void check_ce_order(struct index_state *istate) · line 1859
Quoted 10 lines
read-cache.c +4
-5-case 1 :/* true */ -add_untracked_cache (istate); -break ; -default :/* unknown value: do nothing */ -break ; +return ; } + +if (r->settings->core_untracked_cache &CORE_UNTRACKED_CACHE_WRITE ) +add_untracked_cache (istate);
This changes the flow in a subtle way: in the CORE_UNTRACKED_CACHE_WRITE case, we used to _not_ remove the untracked cache, but now we do.
I _think_ what you would want to do is replace the !(..._KEEP) condition by ..._REMOVE.
Quoted 29 lines
read-cache.c +0
-0}static void tweak_split_index (struct index_state *istate)repo-settings.c +14
-0@@ -30,6 +30,20 @@ static int git_repo_config(const char *key, const char *value, void *cb) rs->index_version =git_config_int (key, value);return 0 ; } +if (!strcmp (key,"core.untrackedcache" )) { +int bool_value =git_parse_maybe_bool (value); +if (bool_value ==0 ) + rs->core_untracked_cache =0 ; +else if (bool_value ==1 ) + rs->core_untracked_cache =CORE_UNTRACKED_CACHE_KEEP | +CORE_UNTRACKED_CACHE_WRITE ; +else if (!strcasecmp (value,"keep" )) + rs->core_untracked_cache =CORE_UNTRACKED_CACHE_KEEP ; +else +error (_ ("unknown core.untrackedCache value '%s'; " +"using 'keep' default value" ), value); +return 0 ; + }return 1 ; }
repo-settings.c void prepare_repo_settings(struct repository *r) · line 63
Quoted 4 lines
repo-settings.c +1
-0@@ -46,6 +60,13 @@ void prepare_repo_settings(struct repository *r) r->settings->gc_write_commit_graph =-1 ; r->settings->pack_use_sparse =-1 ; r->settings->index_version =-1 ; + r->settings->core_untracked_cache =-1 ;
At this point at the latest, I am starting to wonder whether it would not make more sense to use memset(..., -1, sizeof(struct repo_settings) instead.
Quoted 14 lines
repo-settings.c +6
-0repo_config (r, git_repo_config, r->settings); + +/* Hack for test programs like test-dump-untracked-cache */ +if (ignore_untracked_cache_config) + r->settings->core_untracked_cache =CORE_UNTRACKED_CACHE_KEEP ; +else +UPDATE_DEFAULT (r->settings->core_untracked_cache,CORE_UNTRACKED_CACHE_KEEP ); }repo-settings.h +0
-0
repo-settings.h line 5
Quoted 5 lines
repo-settings.h +2
-0@@ -1,11 +1,15 @@#ifndef REPO_SETTINGS_H #define REPO_SETTINGS_H +#define CORE_UNTRACKED_CACHE_WRITE (1 << 0) +#define CORE_UNTRACKED_CACHE_KEEP (1 << 1)
I think it would read even nicer as an enum. In any case, using 1<<1 suggests that this is a bit field, but I don't think that is what we actually want here. Instead, what core_untracked_cache seems to be (at least from my point of view) is a mode, where any two modes are mutually exclusive.
For example, what is the difference between (_KEEP | _WRITE) and (_WRITE) supposed to be? That the latter writes a fresh untracked cache without looking at the previous one? That's not how the existing code behaves, though...
Ciao,
Dscho
Quoted 14 lines
repo-settings.h +2
-1+struct repo_settings {int core_commit_graph;int gc_write_commit_graph;int pack_use_sparse;int index_version; +int core_untracked_cache; };struct repository ; --gitgitgadget
Re: [PATCH 3/5] repo-settings: parse core.untrackedCache
Quoted 62 lines · Johannes Schindelin
Hi Stolee,
Quoted 12 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
The core.untrackedCache config setting is slightly complicated, so clarify its use and centralize its parsing into the repo settings.
The default value is "keep" (returned as -1), which persists the untracked cache if it exists.
If the value is set as "false" (returned as 0), then remove the untracked cache if it exists.
Quoted 8 lines
If the value is set as "true" (returned as 1), then write the untracked cache and persist it.
Instead of relying on magic values of -1, 0, and 1, split these options into bitflags
CORE_UNTRACKED_CACHE_KEEPandCORE_UNTRACKED_CACHE_WRITE. This allows the use of "-1" as a default value. After parsing the config options, if the value is unset we can initialize it toCORE_UNTRACKED_CACHE_KEEP.Nice!
Quoted 20 lines
[...]
read-cache.c +5
-5@@ -1846,18 +1846,17 @@ static void check_ce_order(struct index_state *istate)static void tweak_untracked_cache (struct index_state *istate) { -switch (git_config_get_untracked_cache ()) { -case -1 :/* keep: do nothing */ -break ; -case 0 :/* false */ +struct repository *r = the_repository; + +prepare_repo_settings (r); + +if (!(r->settings->core_untracked_cache &CORE_UNTRACKED_CACHE_KEEP )) {remove_untracked_cache (istate); -break ;read-cache.c static void check_ce_order(struct index_state *istate) · line 1859
Quoted 10 lines
read-cache.c +4
-5-case 1 :/* true */ -add_untracked_cache (istate); -break ; -default :/* unknown value: do nothing */ -break ; +return ; } + +if (r->settings->core_untracked_cache &CORE_UNTRACKED_CACHE_WRITE ) +add_untracked_cache (istate);This changes the flow in a subtle way: in the
CORE_UNTRACKED_CACHE_WRITEcase, we used to _not_ remove the untracked cache, but now we do.
Quoted 2 lines
I _think_ what you would want to do is replace the
!(..._KEEP)condition by..._REMOVE.
I believe the code as written is correct, but confusing. The value is not an enum, but instead a bitflag. When the config setting is given as "true", then both _KEEP and _WRITE are set, so the flow is identical.
However, you already suggested switching to an enum, in which case using _REMOVE would be clearer.
Quoted 65 lines
Quoted 30 lines
read-cache.c +0
-0}static void tweak_split_index (struct index_state *istate)repo-settings.c +14
-0@@ -30,6 +30,20 @@ static int git_repo_config(const char *key, const char *value, void *cb) rs->index_version =git_config_int (key, value);return 0 ; } +if (!strcmp (key,"core.untrackedcache" )) { +int bool_value =git_parse_maybe_bool (value); +if (bool_value ==0 ) + rs->core_untracked_cache =0 ; +else if (bool_value ==1 ) + rs->core_untracked_cache =CORE_UNTRACKED_CACHE_KEEP | +CORE_UNTRACKED_CACHE_WRITE ; +else if (!strcasecmp (value,"keep" )) + rs->core_untracked_cache =CORE_UNTRACKED_CACHE_KEEP ; +else +error (_ ("unknown core.untrackedCache value '%s'; " +"using 'keep' default value" ), value); +return 0 ; + }return 1 ; }repo-settings.c void prepare_repo_settings(struct repository *r) · line 63
Quoted 4 lines
repo-settings.c +1
-0@@ -46,6 +60,13 @@ void prepare_repo_settings(struct repository *r) r->settings->gc_write_commit_graph =-1 ; r->settings->pack_use_sparse =-1 ; r->settings->index_version =-1 ; + r->settings->core_untracked_cache =-1 ;At this point at the latest, I am starting to wonder whether it would not make more sense to use
memset(..., -1, sizeof(struct repo_settings)instead.Quoted 14 lines
repo-settings.c +6
-0repo_config (r, git_repo_config, r->settings); + +/* Hack for test programs like test-dump-untracked-cache */ +if (ignore_untracked_cache_config) + r->settings->core_untracked_cache =CORE_UNTRACKED_CACHE_KEEP ; +else +UPDATE_DEFAULT (r->settings->core_untracked_cache,CORE_UNTRACKED_CACHE_KEEP ); }repo-settings.h +0
-0repo-settings.h line 5
Quoted 5 lines
repo-settings.h +2
-0@@ -1,11 +1,15 @@#ifndef REPO_SETTINGS_H #define REPO_SETTINGS_H +#define CORE_UNTRACKED_CACHE_WRITE (1 << 0) +#define CORE_UNTRACKED_CACHE_KEEP (1 << 1)I think it would read even nicer as an enum. In any case, using
1<<1suggests that this is a bit field, but I don't think that is what we actually want here. Instead, whatcore_untracked_cacheseems to be (at least from my point of view) is a mode, where any two modes are mutually exclusive.
Quoted 4 lines
For example, what is the difference between
(_KEEP | _WRITE)and(_WRITE)supposed to be? That the latter writes a fresh untracked cache without looking at the previous one? That's not how the existing code behaves, though...
Yes, there is no reason to have _WRITE without also _KEEP. An enum is better. Thanks!
Quoted 18 lines
Ciao,
DschoQuoted 14 lines
repo-settings.h +2
-1+struct repo_settings {int core_commit_graph;int gc_write_commit_graph;int pack_use_sparse;int index_version; +int core_untracked_cache; };struct repository ; --gitgitgadget
PATCH 4/5 repo-settings: create feature.manyFiles setting
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The feature.manyFiles setting is suitable for repos with many files in the working directory. By setting index.version=4 and core.untrackedCache=true, commands such as 'git status' should improve.
While adding this setting, modify the index version precedence tests to check how this setting overrides the default for index.version is unset.
--- Documentation/config/core.txt | 4 +++- Documentation/config/feature.txt | 12 +++++++++++- Documentation/config/index.txt | 1 + repo-settings.c | 6 ++++++ t/t1600-index.sh | 31 ++++++++++++++++++++++++++----- 5 files changed, 47 insertions(+), 7 deletions(-)
Documentation/config/core.txt +3 -1
@@ -86,7 +86,9 @@ core.untrackedCache:: it will automatically be removed, if set to `false`. Before setting it to `true`, you should check that mtime is working properly on your system. - See linkgit:git-update-index[1]. `keep` by default. + See linkgit:git-update-index[1]. `keep` by default, unless + `feature.manyFiles` is enabled which sets this setting to + `true` by default. core.checkStat:: When missing or is set to `default`, many fields in the stat
Documentation/config/feature.txt +11 -1
@@ -12,4 +12,14 @@ feature.manyCommits:: * `core.commitGraph=true` enables reading the commit-graph file. + * `gc.writeCommitGraph=true` enables writing the commit-graph file during -garbage collection. +garbage collection. + +feature.manyFiles:: + Enable config options that optimize for repos with many files in the + working directory. With many files, commands such as `git status` and + `git checkout` may be slow and these new defaults improve performance: ++ +* `index.version=4` enables path-prefix compression in the index. ++ +* `core.untrackedCache=true` enables the untracked cache. This setting assumes +that mtime is working on your machine.
Documentation/config/index.txt +1 -0
@@ -24,3 +24,4 @@ index.threads:: index.version:: Specify the version with which new index files should be initialized. This does not affect existing repositories. + If `feature.manyFiles` is enabled, then the default is 4.
repo-settings.c +6 -0
@@ -14,6 +14,12 @@ static int git_repo_config(const char *key, const char *value, void *cb)UPDATE_DEFAULT (rs->gc_write_commit_graph,1 );return 0 ; } +if (!strcmp (key,"feature.manyfiles" )) { +UPDATE_DEFAULT (rs->index_version,4 ); +UPDATE_DEFAULT (rs->core_untracked_cache, +CORE_UNTRACKED_CACHE_KEEP |CORE_UNTRACKED_CACHE_WRITE ); +return 0 ; + }if (!strcmp (key,"core.commitgraph" )) { rs->core_commit_graph =git_config_bool (key, value);return 0 ;
t/t1600-index.sh +26 -5
@@ -59,17 +59,38 @@ test_expect_success 'out of bounds index.version issues warning' ' ) ' -test_expect_success 'GIT_INDEX_VERSION takes precedence over config' ' +test_index_version () { + INDEX_VERSION_CONFIG=$1 && + FEATURE_MANY_FILES=$2 && + ENV_VAR_VERSION=$3 + EXPECTED_OUTPUT_VERSION=$4 && ( rm -f .git/index && - GIT_INDEX_VERSION=4 && - export GIT_INDEX_VERSION && - git config --add index.version 2 && + rm -f .git/config && + if test "$INDEX_VERSION_CONFIG" -ne 0 + then + git config --add index.version $INDEX_VERSION_CONFIG + fi && + git config --add feature.manyFiles $FEATURE_MANY_FILES + if test "$ENV_VAR_VERSION" -ne 0 + then + GIT_INDEX_VERSION=$ENV_VAR_VERSION && + export GIT_INDEX_VERSION + else + unset GIT_INDEX_VERSION + fi && git add a 2>&1 && - echo 4 >expect && + echo $EXPECTED_OUTPUT_VERSION >expect && test-tool index-version <.git/index >actual && test_cmp expect actual ) +} + +test_expect_success 'index version config precedence' ' + test_index_version 2 false 4 4 && + test_index_version 2 true 0 2 && + test_index_version 0 true 0 4 && + test_index_version 0 true 2 2 ' test_done
Signature · 1 line
gitgitgadget
PATCH 5/5 repo-settings: create feature.experimental setting
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The 'feature.experimental' setting includes config options that are not committed to become defaults, but could use additional testing.
Update the following config settings to take new defaults, and to use the repo_settings struct if not already using them:
* 'pack.useSparse=true'
* 'merge.directoryRenames=true'
* 'fetch.negotiationAlgorithm=skipping'
In the case of fetch.negotiationAlgorithm, the existing logic would load the config option only when about to use the setting, so had a die() statement on an unknown string value. This is removed as now the config is parsed under prepare_repo_settings(). In general, this die() is probably misplaced and not valuable. A test was removed that checked this die() statement executed.
--- Documentation/config/feature.txt | 17 +++++++++++++++ Documentation/config/fetch.txt | 3 ++- Documentation/config/merge.txt | 3 ++- Documentation/config/pack.txt | 3 ++- builtin/am.c | 4 +++- fetch-negotiator.c | 26 +++++++++++----------- fetch-negotiator.h | 5 +++-- fetch-pack.c | 11 +++++----- merge-recursive.c | 32 +++++++++++++++------------- merge-recursive.h | 1 - repo-settings.c | 32 +++++++++++++++++++++++++++- repo-settings.h | 12 ++++++++++- t/t5552-skipping-fetch-negotiator.sh | 23 -------------------- 13 files changed, 107 insertions(+), 65 deletions(-)
Documentation/config/feature.txt +17 -0
@@ -4,6 +4,23 @@ feature.*:: developer community as recommended defaults and are subject to change. In particular, new config options may be added with different defaults. +feature.experimental:: + Enable config options that are new to Git, and are being considered for + future defaults. Config settings included here may be added or removed + with each release, including minor version updates. These settings may + have unintended interactions since they are so new. Please enable this + setting if you are interested in providing feedback on experimental + features. The new default values are: ++ +* `pack.useSparse=true` uses a new algorithm when constructing a pack-file +which can improve `git push` performance in repos with many files. ++ +* `merge.directoryRenames=true` uses a new algorithm for detecting renames by +using entire directories at a time instead of single files at a time. ++ +* `fetch.negotiationAlgorithm=skipping` may improve fetch negotiation times by +skipping more commits at a time, reducing the number of round trips. + feature.manyCommits:: Enable config options that optimize for repos with many commits. This setting is recommended for repos with at least 100,000 commits. The
Documentation/config/fetch.txt +2 -1
@@ -59,7 +59,8 @@ fetch.negotiationAlgorithm:: effort to converge faster, but may result in a larger-than-necessary packfile; The default is "default" which instructs Git to use the default algorithm that never skips commits (unless the server has acknowledged it or one - of its descendants). + of its descendants). If `feature.experimental` is enabled, then this + setting defaults to "skipping". Unknown values will cause 'git fetch' to error out. + See also the `--negotiation-tip` option for linkgit:git-fetch[1].
Documentation/config/merge.txt +2 -1
@@ -54,7 +54,8 @@ merge.directoryRenames:: moved into the new directory. If set to "conflict", a conflict will be reported for such paths. If merge.renames is false, merge.directoryRenames is ignored and treated as false. Defaults - to "conflict". + to "conflict" unless `feature.experimental` is enabled and the + default is "true". merge.renormalize:: Tell Git that canonical representation of files in the
Documentation/config/pack.txt +2 -1
@@ -112,7 +112,8 @@ pack.useSparse:: objects. This can have significant performance benefits when computing a pack to send a small change. However, it is possible that extra objects are added to the pack-file if the included - commits contain certain types of direct renames. + commits contain certain types of direct renames. Default is `false` + unless `feature.experimental` is enabled. pack.writeBitmaps (deprecated):: This is a deprecated synonym for `repack.writeBitmaps`.
builtin/am.c +3 -1
@@ -34,6 +34,7 @@#include "string-list.h" #include "packfile.h" #include "repository.h" +#include "repo-settings.h" /** * Returns the length of the first line of msg.
@@ -1538,7 +1539,8 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa o.branch1 ="HEAD" ; their_tree_name =xstrfmt ("%.*s" ,linelen (state->msg), state->msg); o.branch2 = their_tree_name; - o.detect_directory_renames =0 ; +prepare_repo_settings (the_repository); + the_repository->settings->merge_directory_renames =0 ;if (state->quiet) o.verbosity =0 ;
fetch-negotiator.c +14 -12
@@ -2,19 +2,21 @@#include "fetch-negotiator.h" #include "negotiator/default.h" #include "negotiator/skipping.h" +#include "repository.h" +#include "repo-settings.h" -void fetch_negotiator_init (struct fetch_negotiator *negotiator, -const char *algorithm) +void fetch_negotiator_init (struct repository *r, +struct fetch_negotiator *negotiator) { -if (algorithm) { -if (!strcmp (algorithm,"skipping" )) { -skipping_negotiator_init (negotiator); -return ; - }else if (!strcmp (algorithm,"default" )) { -/* Fall through to default initialization */ - }else { -die ("unknown fetch negotiation algorithm '%s'" , algorithm); - } +prepare_repo_settings (r); +switch (r->settings->fetch_negotiation_algorithm) { +case FETCH_NEGOTIATION_SKIPPING : +skipping_negotiator_init (negotiator); +return ; + +case FETCH_NEGOTIATION_DEFAULT : +default : +default_negotiator_init (negotiator); +return ; } -default_negotiator_init (negotiator); }
fetch-negotiator.h +3 -2
@@ -2,6 +2,7 @@#define FETCH_NEGOTIATOR_H struct commit ; +struct repository ;/* * An object that supplies the information needed to negotiate the contents of
@@ -52,7 +53,7 @@ struct fetch_negotiator { void *data; }; -void fetch_negotiator_init (struct fetch_negotiator *negotiator, -const char *algorithm); +void fetch_negotiator_init (struct repository *r, +struct fetch_negotiator *negotiator);#endif
fetch-pack.c +5 -6
@@ -36,7 +36,6 @@ static int agent_supported;static int server_supports_filtering;static struct lock_file shallow_lock;static const char *alternate_shallow_file; -static char *negotiation_algorithm;static struct strbuf fsck_msg_types =STRBUF_INIT ;/* Remember to update object flag allocation in object.h */
@@ -892,12 +891,13 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,struct shallow_info *si, char **pack_lockfile) { + struct repository *r = the_repository;struct ref *ref =copy_ref_list (orig_ref);struct object_id oid;const char *agent_feature;int agent_len;struct fetch_negotiator negotiator; -fetch_negotiator_init (&negotiator, negotiation_algorithm); +fetch_negotiator_init (r, &negotiator);sort_ref_list (&ref, ref_compare_name);QSORT (sought, nr_sought, cmp_ref_by_name);
@@ -911,7 +911,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,if (server_supports ("shallow" ))print_verbose (args,_ ("Server supports %s" ),"shallow" ); -else if (args->depth >0 ||is_repository_shallow (the_repository)) +else if (args->depth >0 ||is_repository_shallow (r))die (_ ("Server does not support shallow clients" ));if (args->depth >0 || args->deepen_since || args->deepen_not) args->deepen =1 ;
@@ -1379,6 +1379,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,struct shallow_info *si, char **pack_lockfile) { + struct repository *r = the_repository;struct ref *ref =copy_ref_list (orig_ref);enum fetch_state state =FETCH_CHECK_LOCAL ;struct oidset common =OIDSET_INIT ;
@@ -1386,7 +1387,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,int in_vain =0 ;int haves_to_send =INITIAL_FLUSH ;struct fetch_negotiator negotiator; -fetch_negotiator_init (&negotiator, negotiation_algorithm); +fetch_negotiator_init (r, &negotiator);packet_reader_init (&reader, fd[0 ],NULL ,0 ,PACKET_READ_CHOMP_NEWLINE |PACKET_READ_DIE_ON_ERR_PACKET );
@@ -1505,8 +1506,6 @@ static void fetch_pack_config(void)git_config_get_bool ("repack.usedeltabaseoffset" , &prefer_ofs_delta);git_config_get_bool ("fetch.fsckobjects" , &fetch_fsck_objects);git_config_get_bool ("transfer.fsckobjects" , &transfer_fsck_objects); -git_config_get_string ("fetch.negotiationalgorithm" , - &negotiation_algorithm);git_config (fetch_pack_config_cb,NULL ); }
merge-recursive.c +17 -15
@@ -28,6 +28,7 @@#include "submodule.h" #include "revision.h" #include "commit-reach.h" +#include "repo-settings.h" struct path_hashmap_entry {struct hashmap_entry e;
@@ -1375,10 +1376,14 @@ static int handle_rename_via_dir(struct merge_options *opt,* there is no content merge to do; just move the file into the * desired final location. */ +struct repository *r = the_repository;const struct rename *ren = ci->ren1;const struct diff_filespec *dest = ren->pair->two;char *file_path = dest->path; -int mark_conflicted = (opt->detect_directory_renames ==1 ); +int mark_conflicted; + +prepare_repo_settings (r); + mark_conflicted = (r->settings->merge_directory_renames ==MERGE_DIRECTORY_RENAMES_CONFLICT );assert (ren->dir_rename_original_dest);if (!opt->call_depth &&would_lose_untracked (opt, dest->path)) {
@@ -2850,6 +2855,7 @@ static int detect_and_process_renames(struct merge_options *opt,struct string_list *entries, struct rename_info *ri) { + struct repository *r = the_repository;struct diff_queue_struct *head_pairs, *merge_pairs;struct hashmap *dir_re_head, *dir_re_merge;int clean =1 ;
@@ -2863,7 +2869,8 @@ static int detect_and_process_renames(struct merge_options *opt, head_pairs =get_diffpairs (opt, common, head); merge_pairs =get_diffpairs (opt, common, merge); -if (opt->detect_directory_renames) { +prepare_repo_settings (r); +if (r->settings->merge_directory_renames) { dir_re_head =get_directory_renames (head_pairs); dir_re_merge =get_directory_renames (merge_pairs);
@@ -3112,6 +3119,7 @@ static int handle_rename_normal(struct merge_options *opt,const struct diff_filespec *b, struct rename_conflict_info *ci) { + struct repository *r = the_repository;struct rename *ren = ci->ren1;struct merge_file_info mfi;int clean;
@@ -3121,7 +3129,9 @@ static int handle_rename_normal(struct merge_options *opt, clean =handle_content_merge (&mfi, opt, path,was_dirty (opt, path), o, a, b, ci); -if (clean && opt->detect_directory_renames ==1 && +prepare_repo_settings (r); +if (clean && + r->settings->merge_directory_renames ==MERGE_DIRECTORY_RENAMES_CONFLICT && ren->dir_rename_original_dest) { if (update_stages(opt, path, NULL,
@@ -3155,6 +3165,7 @@ static void dir_rename_warning(const char *msg,static int warn_about_dir_renamed_entries (struct merge_options *opt,struct rename *ren) { +struct repository *r = the_repository;const char *msg;int clean =1 , is_add;
@@ -3166,12 +3177,13 @@ static int warn_about_dir_renamed_entries(struct merge_options *opt,return clean;/* Sanity checks */ -assert (opt->detect_directory_renames >0 ); +prepare_repo_settings (r); +assert (r->settings->merge_directory_renames >0 );assert (ren->dir_rename_original_type =='A' || ren->dir_rename_original_type =='R' );/* Check whether to treat directory renames as a conflict */ - clean = (opt->detect_directory_renames ==2 ); + clean = (r->settings->merge_directory_renames ==MERGE_DIRECTORY_RENAMES_TRUE ); is_add = (ren->dir_rename_original_type =='A' );if (ren->dir_rename_original_type =='A' && clean) {
@@ -3662,15 +3674,6 @@ static void merge_recursive_config(struct merge_options *opt) opt->merge_detect_rename =git_config_rename ("merge.renames" , value);free (value); } -if (!git_config_get_string ("merge.directoryrenames" , &value)) { -int boolval =git_parse_maybe_bool (value); -if (0 <= boolval) { - opt->detect_directory_renames = boolval ?2 :0 ; - }else if (!strcasecmp (value,"conflict" )) { - opt->detect_directory_renames =1 ; - }/* avoid erroring on values from future versions of git */ -free (value); - }git_config (git_xmerge_config,NULL ); }
@@ -3687,7 +3690,6 @@ void init_merge_options(struct merge_options *opt, opt->renormalize =0 ; opt->diff_detect_rename =-1 ; opt->merge_detect_rename =-1 ; - opt->detect_directory_renames =1 ;merge_recursive_config (opt); merge_verbosity =getenv ("GIT_MERGE_VERBOSITY" );if (merge_verbosity)
merge-recursive.h +0 -1
@@ -22,7 +22,6 @@ struct merge_options { unsigned renormalize : 1;long xdl_opts;int verbosity; -int detect_directory_renames;int diff_detect_rename;int merge_detect_rename;int diff_rename_limit;
repo-settings.c +31 -1
@@ -9,6 +9,12 @@ static int git_repo_config(const char *key, const char *value, void *cb) {struct repo_settings *rs = (struct repo_settings *)cb; +if (!strcmp (key,"feature.experimental" )) { +UPDATE_DEFAULT (rs->pack_use_sparse,1 ); +UPDATE_DEFAULT (rs->merge_directory_renames,MERGE_DIRECTORY_RENAMES_TRUE ); +UPDATE_DEFAULT (rs->fetch_negotiation_algorithm,FETCH_NEGOTIATION_SKIPPING ); +return 0 ; + }if (!strcmp (key,"feature.manycommits" )) {UPDATE_DEFAULT (rs->core_commit_graph,1 );UPDATE_DEFAULT (rs->gc_write_commit_graph,1 );
@@ -50,6 +56,23 @@ static int git_repo_config(const char *key, const char *value, void *cb) "using 'keep' default value"), value);return 0 ; } +if (!strcmp (key,"merge.directoryrenames" )) { +int bool_value =git_parse_maybe_bool (value); +if (0 <= bool_value) { + rs->merge_directory_renames = bool_value ?MERGE_DIRECTORY_RENAMES_TRUE :0 ; + }else if (!strcasecmp (value,"conflict" )) { + rs->merge_directory_renames =MERGE_DIRECTORY_RENAMES_CONFLICT ; + } +return 0 ; + } +if (!strcmp (key,"fetch.negotiationalgorithm" )) { +if (!strcasecmp (value,"skipping" )) { + rs->fetch_negotiation_algorithm =FETCH_NEGOTIATION_SKIPPING ; + }else { + rs->fetch_negotiation_algorithm =FETCH_NEGOTIATION_DEFAULT ; + } +return 0 ; + }return 1 ; }
@@ -64,10 +87,14 @@ void prepare_repo_settings(struct repository *r)/* Defaults */ r->settings->core_commit_graph =-1 ; r->settings->gc_write_commit_graph =-1 ; - r->settings->pack_use_sparse =-1 ; + r->settings->index_version =-1 ; r->settings->core_untracked_cache =-1 ; + r->settings->pack_use_sparse =-1 ; + r->settings->merge_directory_renames =-1 ; + r->settings->fetch_negotiation_algorithm =-1 ; +repo_config (r, git_repo_config, r->settings);/* Hack for test programs like test-dump-untracked-cache */
@@ -75,4 +102,7 @@ void prepare_repo_settings(struct repository *r) r->settings->core_untracked_cache =CORE_UNTRACKED_CACHE_KEEP ; else UPDATE_DEFAULT(r->settings->core_untracked_cache, CORE_UNTRACKED_CACHE_KEEP); + +UPDATE_DEFAULT (r->settings->merge_directory_renames,MERGE_DIRECTORY_RENAMES_CONFLICT ); +UPDATE_DEFAULT (r->settings->fetch_negotiation_algorithm,FETCH_NEGOTIATION_DEFAULT ); }
repo-settings.h +11 -1
@@ -4,12 +4,22 @@#define CORE_UNTRACKED_CACHE_WRITE (1 << 0)#define CORE_UNTRACKED_CACHE_KEEP (1 << 1) +#define MERGE_DIRECTORY_RENAMES_CONFLICT 1 +#define MERGE_DIRECTORY_RENAMES_TRUE 2 + +#define FETCH_NEGOTIATION_DEFAULT 1 +#define FETCH_NEGOTIATION_SKIPPING 2 +struct repo_settings {int core_commit_graph;int gc_write_commit_graph; -int pack_use_sparse; +int index_version;int core_untracked_cache; + +int pack_use_sparse; +int merge_directory_renames; +int fetch_negotiation_algorithm; };struct repository ;
t/t5552-skipping-fetch-negotiator.sh +0 -23
@@ -60,29 +60,6 @@ test_expect_success 'commits with no parents are sent regardless of skip distanc have_not_sent c6 c4 c3 ' -test_expect_success 'unknown fetch.negotiationAlgorithm values error out' ' - rm -rf server client trace && - git init server && - test_commit -C server to_fetch && - - git init client && - test_commit -C client on_client && - git -C client checkout on_client && - - test_config -C client fetch.negotiationAlgorithm invalid && - test_must_fail git -C client fetch "$(pwd)/server" 2>err && - test_i18ngrep "unknown fetch negotiation algorithm" err && - - # Explicit "default" value - test_config -C client fetch.negotiationAlgorithm default && - git -C client -c fetch.negotiationAlgorithm=default fetch "$(pwd)/server" && - - # Implementation detail: If there is nothing to fetch, we will not error out - test_config -C client fetch.negotiationAlgorithm invalid && - git -C client fetch "$(pwd)/server" 2>err && - test_i18ngrep ! "unknown fetch negotiation algorithm" err -' - test_expect_success 'when two skips collide, favor the larger one' ' rm -rf server client trace && git init server &&
Signature · 1 line
gitgitgadget
Re: [PATCH 5/5] repo-settings: create feature.experimental setting
Hi Stolee,
Quoted 12 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
The 'feature.experimental' setting includes config options that are not committed to become defaults, but could use additional testing.
Update the following config settings to take new defaults, and to use the
repo_settingsstruct if not already using them:* 'pack.useSparse=true'
* 'merge.directoryRenames=true'
Quoted 8 lines
* 'fetch.negotiationAlgorithm=skipping'
In the case of fetch.negotiationAlgorithm, the existing logic would load the config option only when about to use the setting, so had a
die()statement on an unknown string value. This is removed as now the config is parsed underprepare_repo_settings(). In general, thisdie()is probably misplaced and not valuable. A test was removed that checked thisdie()statement executed.
Good.
Quoted 14 lines
[...]
merge-recursive.c +1
-0@@ -28,6 +28,7 @@#include "submodule.h" #include "revision.h" #include "commit-reach.h" +#include "repo-settings.h" struct path_hashmap_entry {struct hashmap_entry e;
merge-recursive.c static int handle_rename_via_dir(struct merge_options *opt, · line 1381
Quoted 8 lines
merge-recursive.c +1
-1@@ -1375,10 +1376,14 @@ static int handle_rename_via_dir(struct merge_options *opt,* there is no content merge to do; just move the file into the * desired final location. */ +struct repository *r = the_repository;const struct rename *ren = ci->ren1;const struct diff_filespec *dest = ren->pair->two;char *file_path = dest->path; -int mark_conflicted = (opt->detect_directory_renames ==1 );
I actually don't think that we want to do that; the opt parameter is passed to the merge recursive algorithm specifically so that it can be overridden by the caller.
Instead, what we should do, I think, is to change init_merge_options() (which already gets a parameter of type struct repository *) so that it does not hard-code the detect_directory_renames value to 1 but to query the repo settings instead.
Quoted 85 lines
merge-recursive.c +15
-13+int mark_conflicted; + +prepare_repo_settings (r); + mark_conflicted = (r->settings->merge_directory_renames ==MERGE_DIRECTORY_RENAMES_CONFLICT );assert (ren->dir_rename_original_dest);if (!opt->call_depth &&would_lose_untracked (opt, dest->path)) {@@ -2850,6 +2855,7 @@ static int detect_and_process_renames(struct merge_options *opt,struct string_list *entries, struct rename_info *ri) { + struct repository *r = the_repository;struct diff_queue_struct *head_pairs, *merge_pairs;struct hashmap *dir_re_head, *dir_re_merge;int clean =1 ;@@ -2863,7 +2869,8 @@ static int detect_and_process_renames(struct merge_options *opt, head_pairs =get_diffpairs (opt, common, head); merge_pairs =get_diffpairs (opt, common, merge); -if (opt->detect_directory_renames) { +prepare_repo_settings (r); +if (r->settings->merge_directory_renames) { dir_re_head =get_directory_renames (head_pairs); dir_re_merge =get_directory_renames (merge_pairs);@@ -3112,6 +3119,7 @@ static int handle_rename_normal(struct merge_options *opt,const struct diff_filespec *b, struct rename_conflict_info *ci) { + struct repository *r = the_repository;struct rename *ren = ci->ren1;struct merge_file_info mfi;int clean;@@ -3121,7 +3129,9 @@ static int handle_rename_normal(struct merge_options *opt, clean =handle_content_merge (&mfi, opt, path,was_dirty (opt, path), o, a, b, ci); -if (clean && opt->detect_directory_renames ==1 && +prepare_repo_settings (r); +if (clean && + r->settings->merge_directory_renames ==MERGE_DIRECTORY_RENAMES_CONFLICT && ren->dir_rename_original_dest) { if (update_stages(opt, path, NULL,@@ -3155,6 +3165,7 @@ static void dir_rename_warning(const char *msg,static int warn_about_dir_renamed_entries (struct merge_options *opt,struct rename *ren) { +struct repository *r = the_repository;const char *msg;int clean =1 , is_add;@@ -3166,12 +3177,13 @@ static int warn_about_dir_renamed_entries(struct merge_options *opt,return clean;/* Sanity checks */ -assert (opt->detect_directory_renames >0 ); +prepare_repo_settings (r); +assert (r->settings->merge_directory_renames >0 );assert (ren->dir_rename_original_type =='A' || ren->dir_rename_original_type =='R' );/* Check whether to treat directory renames as a conflict */ - clean = (opt->detect_directory_renames ==2 ); + clean = (r->settings->merge_directory_renames ==MERGE_DIRECTORY_RENAMES_TRUE ); is_add = (ren->dir_rename_original_type =='A' );if (ren->dir_rename_original_type =='A' && clean) {@@ -3662,15 +3674,6 @@ static void merge_recursive_config(struct merge_options *opt) opt->merge_detect_rename =git_config_rename ("merge.renames" , value);free (value); } -if (!git_config_get_string ("merge.directoryrenames" , &value)) { -int boolval =git_parse_maybe_bool (value); -if (0 <= boolval) { - opt->detect_directory_renames = boolval ?2 :0 ; - }else if (!strcasecmp (value,"conflict" )) { - opt->detect_directory_renames =1 ; - }/* avoid erroring on values from future versions of git */ -free (value); - }git_config (git_xmerge_config,NULL ); }
merge-recursive.c void init_merge_options(struct merge_options *opt, · line 3690
Quoted 4 lines
merge-recursive.c +0
-1@@ -3687,7 +3690,6 @@ void init_merge_options(struct merge_options *opt, opt->renormalize =0 ; opt->diff_detect_rename =-1 ; opt->merge_detect_rename =-1 ; - opt->detect_directory_renames =1 ;
In other words: here.
Quoted 57 lines
merge-recursive.c +0
-0merge_recursive_config (opt); merge_verbosity =getenv ("GIT_MERGE_VERBOSITY" );if (merge_verbosity)merge-recursive.h +0
-1@@ -22,7 +22,6 @@ struct merge_options { unsigned renormalize : 1;long xdl_opts;int verbosity; -int detect_directory_renames;int diff_detect_rename;int merge_detect_rename;int diff_rename_limit;repo-settings.c +23
-0@@ -9,6 +9,12 @@ static int git_repo_config(const char *key, const char *value, void *cb) {struct repo_settings *rs = (struct repo_settings *)cb; +if (!strcmp (key,"feature.experimental" )) { +UPDATE_DEFAULT (rs->pack_use_sparse,1 ); +UPDATE_DEFAULT (rs->merge_directory_renames,MERGE_DIRECTORY_RENAMES_TRUE ); +UPDATE_DEFAULT (rs->fetch_negotiation_algorithm,FETCH_NEGOTIATION_SKIPPING ); +return 0 ; + }if (!strcmp (key,"feature.manycommits" )) {UPDATE_DEFAULT (rs->core_commit_graph,1 );UPDATE_DEFAULT (rs->gc_write_commit_graph,1 );@@ -50,6 +56,23 @@ static int git_repo_config(const char *key, const char *value, void *cb) "using 'keep' default value"), value);return 0 ; } +if (!strcmp (key,"merge.directoryrenames" )) { +int bool_value =git_parse_maybe_bool (value); +if (0 <= bool_value) { + rs->merge_directory_renames = bool_value ?MERGE_DIRECTORY_RENAMES_TRUE :0 ; + }else if (!strcasecmp (value,"conflict" )) { + rs->merge_directory_renames =MERGE_DIRECTORY_RENAMES_CONFLICT ; + } +return 0 ; + } +if (!strcmp (key,"fetch.negotiationalgorithm" )) { +if (!strcasecmp (value,"skipping" )) { + rs->fetch_negotiation_algorithm =FETCH_NEGOTIATION_SKIPPING ; + }else { + rs->fetch_negotiation_algorithm =FETCH_NEGOTIATION_DEFAULT ; + } +return 0 ; + }return 1 ; }
repo-settings.c void prepare_repo_settings(struct repository *r) · line 94
Quoted 9 lines
repo-settings.c +2
-1@@ -64,10 +87,14 @@ void prepare_repo_settings(struct repository *r)/* Defaults */ r->settings->core_commit_graph =-1 ; r->settings->gc_write_commit_graph =-1 ; - r->settings->pack_use_sparse =-1 ; + r->settings->index_version =-1 ; r->settings->core_untracked_cache =-1 ; + r->settings->pack_use_sparse =-1 ;
This reordering at this stage in the patch series is a bit confusing. Maybe add it at the location where you want it to end up to begin with?
Or use memset(..., -1, ...).
Quoted 19 lines
repo-settings.c +6
-0+ r->settings->merge_directory_renames =-1 ; + r->settings->fetch_negotiation_algorithm =-1 ; +repo_config (r, git_repo_config, r->settings);/* Hack for test programs like test-dump-untracked-cache */ @@ -75,4 +102,7 @@ void prepare_repo_settings(struct repository *r) r->settings->core_untracked_cache =CORE_UNTRACKED_CACHE_KEEP ; else UPDATE_DEFAULT(r->settings->core_untracked_cache, CORE_UNTRACKED_CACHE_KEEP); + +UPDATE_DEFAULT (r->settings->merge_directory_renames,MERGE_DIRECTORY_RENAMES_CONFLICT ); +UPDATE_DEFAULT (r->settings->fetch_negotiation_algorithm,FETCH_NEGOTIATION_DEFAULT ); }repo-settings.h +0
-0
repo-settings.h line 12
Quoted 9 lines
repo-settings.h +6
-0@@ -4,12 +4,22 @@#define CORE_UNTRACKED_CACHE_WRITE (1 << 0)#define CORE_UNTRACKED_CACHE_KEEP (1 << 1) +#define MERGE_DIRECTORY_RENAMES_CONFLICT 1 +#define MERGE_DIRECTORY_RENAMES_TRUE 2 + +#define FETCH_NEGOTIATION_DEFAULT 1 +#define FETCH_NEGOTIATION_SKIPPING 2 +
Again, I'd prefer enums.
The rest looks pretty fine to me (I have to admit that I only glanced over the tests, though).
Thanks!
Dscho
Quoted 51 lines
repo-settings.h +5
-1struct repo_settings {int core_commit_graph;int gc_write_commit_graph; -int pack_use_sparse; +int index_version;int core_untracked_cache; + +int pack_use_sparse; +int merge_directory_renames; +int fetch_negotiation_algorithm; };struct repository ;t/t5552-skipping-fetch-negotiator.sh +0
-24@@ -60,29 +60,6 @@ test_expect_success 'commits with no parents are sent regardless of skip distanc have_not_sent c6 c4 c3 ' -test_expect_success 'unknown fetch.negotiationAlgorithm values error out' ' - rm -rf server client trace && - git init server && - test_commit -C server to_fetch && - - git init client && - test_commit -C client on_client && - git -C client checkout on_client && - - test_config -C client fetch.negotiationAlgorithm invalid && - test_must_fail git -C client fetch "$(pwd)/server" 2>err && - test_i18ngrep "unknown fetch negotiation algorithm" err && - - # Explicit "default" value - test_config -C client fetch.negotiationAlgorithm default && - git -C client -c fetch.negotiationAlgorithm=default fetch "$(pwd)/server" && - - # Implementation detail: If there is nothing to fetch, we will not error out - test_config -C client fetch.negotiationAlgorithm invalid && - git -C client fetch "$(pwd)/server" 2>err && - test_i18ngrep ! "unknown fetch negotiation algorithm" err -' - test_expect_success 'when two skips collide, favor the larger one' ' rm -rf server client trace && git init server && --gitgitgadget
Re: [PATCH 5/5] repo-settings: create feature.experimental setting
Quoted 23 lines · Johannes Schindelin
Hi Stolee,
merge-recursive.c static int handle_rename_via_dir(struct merge_options *opt, · line 1381
Quoted 11 lines · Derrick Stolee via GitGitGadget
struct
path_hashmap_entry{merge-recursive.c +1
-1struct hashmap_entry e;@@ -1375,10 +1376,14 @@ static int handle_rename_via_dir(struct merge_options *opt,* there is no content merge to do; just move the file into the * desired final location. */ +struct repository *r = the_repository;const struct rename *ren = ci->ren1;const struct diff_filespec *dest = ren->pair->two;char *file_path = dest->path; -int mark_conflicted = (opt->detect_directory_renames ==1 );I actually don't think that we want to do that; the
optparameter is passed to the merge recursive algorithm specifically so that it can be overridden by the caller.Instead, what we should do, I think, is to change
init_merge_options()(which already gets a parameter of typestruct repository *) so that it does not hard-code thedetect_directory_renamesvalue to1but to query the repo settings instead.
[snip]
Quoted 7 lines
merge-recursive.c void init_merge_options(struct merge_options *opt, · line 3690
Quoted 5 lines
merge-recursive.c +0
-1@@ -3687,7 +3690,6 @@ void init_merge_options(struct merge_options *opt, opt->renormalize =0 ; opt->diff_detect_rename =-1 ; opt->merge_detect_rename =-1 ; - opt->detect_directory_renames =1 ;In other words: here.
Yes, thanks! Your suggestion makes much more sense and makes the diff much simpler. No reason to couple too closely to the config storage here.
I think you gave me enough feedback to merit a v2 before others chime in, so I'll send one after I validate my current version.
Thanks,
-Stolee
v2 cover Create 'feature.*' config area and some centralized config parsing
In reply to Derrick Stolee via GitGitGadgetThis is a brand-new thread to replace ds/early-access. The discussion on that thread was very helpful [1].
With this in mind, I propose instead a set of "feature.*" config settings that form groups of "community recommended" settings (with some caveats). In the space below, I'll list a set of possible feature names and the implied config options.
First, the main two categories we've discussed so far: many commits and many files. These two feature sets are for when your repo is large in one of these dimensions. Perhaps there are other settings to include in these?
feature.manyFiles:
index.version = 4
core.untrackedCache = true
feature.manyCommits:
core.commitGraph = true
gc.writeCommitGraph = true
(future: fetch.writeSplitCommitGraph = true)
Note: the fetch.writeSplitCommitGraph does not exist yet, but could be introduced in a later release to write a new commit-graph (with --split) on fetch.
The other category that has been discussed already is that of "experimental features that we generally think are helpful but change behavior slightly in some cases".
feature.experimental:
pack.useSparse = true
merge.directoryRenames = true
fetch.negotiationAlgorithm = skipping
Specifically, this setting is for config values we are not sure will ever be on by default, but additional testing is needed to be sure. This is different than a possible 'feature.preview' setting that would include config settings that we are committed to updating the defaults in a future release. There are many ways we can take this idea in the future (including more additions to these categories).
Thanks, -Stolee
Updates in V2: I'm responding to Dscho's comments early, as they were very valuable.
* Rearranged how we are checking for the feature.* settings, so if one layer turns the setting on and a later layer turns it off, we do not adjust the defaults.
* Switched to using enums for the non-boolean config options.
* Placed the repo_settings struct directly in the repository struct.
* All struct members are initialized to -1 using memset.
* The config changes around directory rename detection is greatly simplified.
[1] https://public-inbox.org/git/[email protected]/
Derrick Stolee (5):
repo-settings: consolidate some config settings
repo-settings: add feature.manyCommits setting
repo-settings: parse core.untrackedCache
repo-settings: create feature.manyFiles setting
repo-settings: create feature.experimental setting
Documentation/config.txt | 2 + Documentation/config/core.txt | 7 ++- Documentation/config/feature.txt | 42 +++++++++++++++ Documentation/config/fetch.txt | 3 +- Documentation/config/gc.txt | 4 +- Documentation/config/index.txt | 1 + Documentation/config/merge.txt | 3 +- Documentation/config/pack.txt | 3 +- Makefile | 1 + builtin/am.c | 1 + builtin/gc.c | 13 +++-- builtin/pack-objects.c | 9 ++-- builtin/update-index.c | 7 ++- commit-graph.c | 7 +-- config.c | 24 --------- fetch-negotiator.c | 26 +++++----- fetch-negotiator.h | 5 +- fetch-pack.c | 11 ++-- merge-recursive.c | 15 +++--- read-cache.c | 31 +++++------ repo-settings.c | 78 ++++++++++++++++++++++++++++ repo-settings.h | 41 +++++++++++++++ repository.h | 5 ++ t/t1600-index.sh | 31 +++++++++-- t/t5552-skipping-fetch-negotiator.sh | 23 -------- 25 files changed, 274 insertions(+), 119 deletions(-) create mode 100644 Documentation/config/feature.txt create mode 100644 repo-settings.c create mode 100644 repo-settings.h
base-commit: 9c9b961d7eb15fb583a2a812088713a68a85f1c0
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-292%2Fderrickstolee%2Frepo-settings%2Fhead-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-292/derrickstolee/repo-settings/head-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/292
Range-diff vs v1:
1: 72f652b89c ! 1: 597ab7d621 repo-settings: consolidate some config settings
@@ -64,7 +64,7 @@
- NULL))
- return 1;
+ prepare_repo_settings(the_repository);
-+ if (the_repository->settings->gc_write_commit_graph == 1)
++ if (the_repository->settings.gc_write_commit_graph == 1)
+ write_commit_graph_reachable(get_object_directory(),
+ !quiet && !daemonized ? COMMIT_GRAPH_PROGRESS : 0,
+ NULL);
@@ -99,8 +99,8 @@
sparse = git_env_bool("GIT_TEST_PACK_SPARSE", 0);
+ prepare_repo_settings(the_repository);
-+ if (!sparse && the_repository->settings->pack_use_sparse != -1)
-+ sparse = the_repository->settings->pack_use_sparse;
++ if (!sparse && the_repository->settings.pack_use_sparse != -1)
++ sparse = the_repository->settings.pack_use_sparse;
+
reset_pack_idx_option(&pack_idx_opts);
git_config(git_pack_config, NULL);
@@ -134,7 +134,7 @@
if (!git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
- (repo_config_get_bool(r, "core.commitgraph", &config_value) ||
- !config_value))
-+ r->settings->core_commit_graph != 1)
++ r->settings.core_commit_graph != 1)
/*
* This repository is not configured to use commit graphs, so
* do not load one. (But report commit_graph_attempted anyway
@@ -167,8 +167,8 @@
- version = value;
+ prepare_repo_settings(r);
+
-+ if (r->settings->index_version >= 0)
-+ version = r->settings->index_version;
++ if (r->settings.index_version >= 0)
++ version = r->settings.index_version;
if (version < INDEX_FORMAT_LB || INDEX_FORMAT_UB < version) {
warning(_("index.version set, but the value is invalid.\n"
"Using version %i"), INDEX_FORMAT_DEFAULT);
@@ -192,44 +192,28 @@
+#include "config.h"
+#include "repo-settings.h"
+
-+static int git_repo_config(const char *key, const char *value, void *cb)
++void prepare_repo_settings(struct repository *r)
+{
-+ struct repo_settings *rs = (struct repo_settings *)cb;
++ int value;
+
-+ if (!strcmp(key, "core.commitgraph")) {
-+ rs->core_commit_graph = git_config_bool(key, value);
-+ return 0;
-+ }
-+ if (!strcmp(key, "gc.writecommitgraph")) {
-+ rs->gc_write_commit_graph = git_config_bool(key, value);
-+ return 0;
-+ }
-+ if (!strcmp(key, "pack.usesparse")) {
-+ rs->pack_use_sparse = git_config_bool(key, value);
-+ return 0;
-+ }
-+ if (!strcmp(key, "index.version")) {
-+ rs->index_version = git_config_int(key, value);
-+ return 0;
-+ }
++ if (r->settings_initialized)
++ return;
+
-+ return 1;
-+}
++ /* Defaults */
++ memset(&r->settings, -1, sizeof(r->settings));
+
-+void prepare_repo_settings(struct repository *r)
-+{
-+ if (r->settings)
-+ return;
++ if (!repo_config_get_bool(r, "core.commitgraph", &value))
++ r->settings.core_commit_graph = value;
++ if (!repo_config_get_bool(r, "gc.writecommitgraph", &value))
++ r->settings.gc_write_commit_graph = value;
+
-+ r->settings = xmalloc(sizeof(*r->settings));
++ if (!repo_config_get_bool(r, "index.version", &value))
++ r->settings.index_version = value;
+
-+ /* Defaults */
-+ r->settings->core_commit_graph = -1;
-+ r->settings->gc_write_commit_graph = -1;
-+ r->settings->pack_use_sparse = -1;
-+ r->settings->index_version = -1;
++ if (!repo_config_get_bool(r, "pack.usesparse", &value))
++ r->settings.pack_use_sparse = value;
+
-+ repo_config(r, git_repo_config, r->settings);
++ r->settings_initialized = 1;
+}
diff --git a/repo-settings.h b/repo-settings.h
@@ -243,8 +227,10 @@
+struct repo_settings {
+ int core_commit_graph;
+ int gc_write_commit_graph;
-+ int pack_use_sparse;
++
+ int index_version;
++
++ int pack_use_sparse;
+};
+
+struct repository;
@@ -257,7 +243,10 @@
--- a/repository.h
+++ b/repository.h
@@
+ #define REPOSITORY_H
+
#include "path.h"
++#include "repo-settings.h"
struct config_set;
+struct repo_settings;
@@ -268,7 +257,8 @@
*/
char *submodule_prefix;
-+ struct repo_settings *settings;
++ int settings_initialized;
++ struct repo_settings settings;
+
/* Subsystems */
/* 2: e00a1be75b ! 2: 86380c7832 repo-settings: add feature.manyCommits setting
@@ -4,7 +4,7 @@
When a repo has many commits, it is helpful to write and read the commit-graph file. Future changes to Git may include new config
- settings that are benefitial in this scenario.
+ settings that are beneficial in this scenario.
Create the 'feature.manyCommits' config setting that changes the default values of 'core.commitGraph' and 'gc.writeCommitGraph' to
@@ -85,15 +85,17 @@
+#define UPDATE_DEFAULT(s,v) do { if (s == -1) { s = v; } } while(0) +
- static int git_repo_config(const char *key, const char *value, void *cb)
+ void prepare_repo_settings(struct repository *r)
{
- struct repo_settings *rs = (struct repo_settings *)cb;
+ int value;
+@@
+ if (!repo_config_get_bool(r, "pack.usesparse", &value))
+ r->settings.pack_use_sparse = value;
-+ if (!strcmp(key, "feature.manycommits")) {
-+ UPDATE_DEFAULT(rs->core_commit_graph, 1);
-+ UPDATE_DEFAULT(rs->gc_write_commit_graph, 1);
-+ return 0;
++ if (!repo_config_get_bool(r, "feature.manycommits", &value) && value) {
++ UPDATE_DEFAULT(r->settings.core_commit_graph, 1);
++ UPDATE_DEFAULT(r->settings.gc_write_commit_graph, 1);
+ }
- if (!strcmp(key, "core.commitgraph")) {
- rs->core_commit_graph = git_config_bool(key, value);
- return 0;
++
+ r->settings_initialized = 1;
+ }
3: 47ae3e7d4d ! 3: 49be7a7345 repo-settings: parse core.untrackedCache
@@ -16,10 +16,9 @@
untracked cache and persist it.
Instead of relying on magic values of -1, 0, and 1, split these
- options into bitflags CORE_UNTRACKED_CACHE_KEEP and
- CORE_UNTRACKED_CACHE_WRITE. This allows the use of "-1" as a
+ options into an enum. This allows the use of "-1" as a
default value. After parsing the config options, if the value is
- unset we can initialize it to CORE_UNTRACKED_CACHE_KEEP.
+ unset we can initialize it to UNTRACKED_CACHE_KEEP.
Signed-off-by: Derrick Stolee <[email protected]>
@@ -52,7 +51,7 @@
break;
case UC_DISABLE:
- if (git_config_get_untracked_cache() == 1)
-+ if (r->settings->core_untracked_cache & CORE_UNTRACKED_CACHE_WRITE)
++ if (r->settings.core_untracked_cache == UNTRACKED_CACHE_WRITE)
warning(_("core.untrackedCache is set to true; "
"remove or change it, if you really want to "
"disable the untracked cache"));
@@ -61,7 +60,7 @@
case UC_ENABLE:
case UC_FORCE:
- if (git_config_get_untracked_cache() == 0)
-+ if (r->settings->core_untracked_cache == 0)
++ if (r->settings.core_untracked_cache == UNTRACKED_CACHE_REMOVE)
warning(_("core.untrackedCache is set to false; "
"remove or change it, if you really want to "
"enable the untracked cache"));
@@ -116,7 +115,7 @@
+
+ prepare_repo_settings(r);
+
-+ if (!(r->settings->core_untracked_cache & CORE_UNTRACKED_CACHE_KEEP)) {
++ if (r->settings.core_untracked_cache == UNTRACKED_CACHE_REMOVE) {
remove_untracked_cache(istate);
- break;
- case 1: /* true */
@@ -127,7 +126,7 @@
+ return;
}
+
-+ if (r->settings->core_untracked_cache & CORE_UNTRACKED_CACHE_WRITE)
++ if (r->settings.core_untracked_cache == UNTRACKED_CACHE_WRITE)
+ add_untracked_cache(istate);
}
@@ -137,39 +136,46 @@
--- a/repo-settings.c
+++ b/repo-settings.c
@@
- rs->index_version = git_config_int(key, value);
- return 0;
- }
-+ if (!strcmp(key, "core.untrackedcache")) {
-+ int bool_value = git_parse_maybe_bool(value);
-+ if (bool_value == 0)
-+ rs->core_untracked_cache = 0;
-+ else if (bool_value == 1)
-+ rs->core_untracked_cache = CORE_UNTRACKED_CACHE_KEEP |
-+ CORE_UNTRACKED_CACHE_WRITE;
-+ else if (!strcasecmp(value, "keep"))
-+ rs->core_untracked_cache = CORE_UNTRACKED_CACHE_KEEP;
-+ else
-+ error(_("unknown core.untrackedCache value '%s'; "
-+ "using 'keep' default value"), value);
-+ return 0;
-+ }
+ void prepare_repo_settings(struct repository *r)
+ {
+ int value;
++ char *strval;
- return 1;
- }
+ if (r->settings_initialized)
+ return;
@@
- r->settings->gc_write_commit_graph = -1;
- r->settings->pack_use_sparse = -1;
- r->settings->index_version = -1;
-+ r->settings->core_untracked_cache = -1;
- repo_config(r, git_repo_config, r->settings);
+ if (!repo_config_get_bool(r, "index.version", &value))
+ r->settings.index_version = value;
++ if (!repo_config_get_maybe_bool(r, "core.untrackedcache", &value)) {
++ if (value == 0)
++ r->settings.core_untracked_cache = UNTRACKED_CACHE_REMOVE;
++ else
++ r->settings.core_untracked_cache = UNTRACKED_CACHE_WRITE;
++ } else if (!repo_config_get_string(r, "core.untrackedcache", &strval)) {
++ if (!strcasecmp(strval, "keep"))
++ r->settings.core_untracked_cache = UNTRACKED_CACHE_KEEP;
++
++ free(strval);
++ }
+
+
+ if (!repo_config_get_bool(r, "pack.usesparse", &value))
+ r->settings.pack_use_sparse = value;
+-
++
+ if (!repo_config_get_bool(r, "feature.manycommits", &value) && value) {
+ UPDATE_DEFAULT(r->settings.core_commit_graph, 1);
+ UPDATE_DEFAULT(r->settings.gc_write_commit_graph, 1);
+ }
+
+ /* Hack for test programs like test-dump-untracked-cache */
+ if (ignore_untracked_cache_config)
-+ r->settings->core_untracked_cache = CORE_UNTRACKED_CACHE_KEEP;
++ r->settings.core_untracked_cache = UNTRACKED_CACHE_KEEP;
+ else
-+ UPDATE_DEFAULT(r->settings->core_untracked_cache, CORE_UNTRACKED_CACHE_KEEP);
++ UPDATE_DEFAULT(r->settings.core_untracked_cache, UNTRACKED_CACHE_KEEP);
++
+ r->settings_initialized = 1;
}
diff --git a/repo-settings.h b/repo-settings.h
@@ -179,15 +185,19 @@
#ifndef REPO_SETTINGS_H
#define REPO_SETTINGS_H
-+#define CORE_UNTRACKED_CACHE_WRITE (1 << 0)
-+#define CORE_UNTRACKED_CACHE_KEEP (1 << 1)
++enum untracked_cache_setting {
++ UNTRACKED_CACHE_UNSET = -1,
++ UNTRACKED_CACHE_REMOVE = 0,
++ UNTRACKED_CACHE_KEEP = 1,
++ UNTRACKED_CACHE_WRITE = 2
++};
+
struct repo_settings {
int core_commit_graph;
int gc_write_commit_graph;
- int pack_use_sparse;
+
int index_version;
-+ int core_untracked_cache;
- };
++ enum untracked_cache_setting core_untracked_cache;
- struct repository;
+ int pack_use_sparse;
+ };
4: 63b522a858 ! 4: 86a5a0c589 repo-settings: create feature.manyFiles setting
@@ -63,18 +63,16 @@
--- a/repo-settings.c
+++ b/repo-settings.c
@@
- UPDATE_DEFAULT(rs->gc_write_commit_graph, 1);
- return 0;
+ UPDATE_DEFAULT(r->settings.core_commit_graph, 1);
+ UPDATE_DEFAULT(r->settings.gc_write_commit_graph, 1);
}
-+ if (!strcmp(key, "feature.manyfiles")) {
-+ UPDATE_DEFAULT(rs->index_version, 4);
-+ UPDATE_DEFAULT(rs->core_untracked_cache,
-+ CORE_UNTRACKED_CACHE_KEEP | CORE_UNTRACKED_CACHE_WRITE);
-+ return 0;
++ if (!repo_config_get_bool(r, "feature.manyfiles", &value) && value) {
++ UPDATE_DEFAULT(r->settings.index_version, 4);
++ UPDATE_DEFAULT(r->settings.core_untracked_cache, UNTRACKED_CACHE_WRITE);
+ }
- if (!strcmp(key, "core.commitgraph")) {
- rs->core_commit_graph = git_config_bool(key, value);
- return 0;
+
+ /* Hack for test programs like test-dump-untracked-cache */
+ if (ignore_untracked_cache_config)
diff --git a/t/t1600-index.sh b/t/t1600-index.sh
--- a/t/t1600-index.sh
5: da7685936f ! 5: b8a631683a repo-settings: create feature.experimental setting
@@ -104,16 +104,6 @@
/**
* Returns the length of the first line of msg.
-@@
- o.branch1 = "HEAD";
- their_tree_name = xstrfmt("%.*s", linelen(state->msg), state->msg);
- o.branch2 = their_tree_name;
-- o.detect_directory_renames = 0;
-+ prepare_repo_settings(the_repository);
-+ the_repository->settings->merge_directory_renames = 0;
-
- if (state->quiet)
- o.verbosity = 0;
diff --git a/fetch-negotiator.c b/fetch-negotiator.c
--- a/fetch-negotiator.c
@@ -140,7 +130,7 @@
- die("unknown fetch negotiation algorithm '%s'", algorithm);
- }
+ prepare_repo_settings(r);
-+ switch(r->settings->fetch_negotiation_algorithm) {
++ switch(r->settings.fetch_negotiation_algorithm) {
+ case FETCH_NEGOTIATION_SKIPPING:
+ skipping_negotiator_init(negotiator);
+ return;
@@ -248,83 +238,6 @@
struct path_hashmap_entry {
struct hashmap_entry e;
-@@
- * there is no content merge to do; just move the file into the
- * desired final location.
- */
-+ struct repository *r = the_repository;
- const struct rename *ren = ci->ren1;
- const struct diff_filespec *dest = ren->pair->two;
- char *file_path = dest->path;
-- int mark_conflicted = (opt->detect_directory_renames == 1);
-+ int mark_conflicted;
-+
-+ prepare_repo_settings(r);
-+ mark_conflicted = (r->settings->merge_directory_renames == MERGE_DIRECTORY_RENAMES_CONFLICT);
- assert(ren->dir_rename_original_dest);
-
- if (!opt->call_depth && would_lose_untracked(opt, dest->path)) {
-@@
- struct string_list *entries,
- struct rename_info *ri)
- {
-+ struct repository *r = the_repository;
- struct diff_queue_struct *head_pairs, *merge_pairs;
- struct hashmap *dir_re_head, *dir_re_merge;
- int clean = 1;
-@@
- head_pairs = get_diffpairs(opt, common, head);
- merge_pairs = get_diffpairs(opt, common, merge);
-
-- if (opt->detect_directory_renames) {
-+ prepare_repo_settings(r);
-+ if (r->settings->merge_directory_renames) {
- dir_re_head = get_directory_renames(head_pairs);
- dir_re_merge = get_directory_renames(merge_pairs);
-
-@@
- const struct diff_filespec *b,
- struct rename_conflict_info *ci)
- {
-+ struct repository *r = the_repository;
- struct rename *ren = ci->ren1;
- struct merge_file_info mfi;
- int clean;
-@@
- clean = handle_content_merge(&mfi, opt, path, was_dirty(opt, path),
- o, a, b, ci);
-
-- if (clean && opt->detect_directory_renames == 1 &&
-+ prepare_repo_settings(r);
-+ if (clean &&
-+ r->settings->merge_directory_renames == MERGE_DIRECTORY_RENAMES_CONFLICT &&
- ren->dir_rename_original_dest) {
- if (update_stages(opt, path,
- NULL,
-@@
- static int warn_about_dir_renamed_entries(struct merge_options *opt,
- struct rename *ren)
- {
-+ struct repository *r = the_repository;
- const char *msg;
- int clean = 1, is_add;
-
-@@
- return clean;
-
- /* Sanity checks */
-- assert(opt->detect_directory_renames > 0);
-+ prepare_repo_settings(r);
-+ assert(r->settings->merge_directory_renames > 0);
- assert(ren->dir_rename_original_type == 'A' ||
- ren->dir_rename_original_type == 'R');
-
- /* Check whether to treat directory renames as a conflict */
-- clean = (opt->detect_directory_renames == 2);
-+ clean = (r->settings->merge_directory_renames == MERGE_DIRECTORY_RENAMES_TRUE);
-
- is_add = (ren->dir_rename_original_type == 'A');
- if (ren->dir_rename_original_type == 'A' && clean) {
@@
opt->merge_detect_rename = git_config_rename("merge.renames", value);
free(value);
@@ -342,115 +255,92 @@
}
@@
- opt->renormalize = 0;
opt->diff_detect_rename = -1;
opt->merge_detect_rename = -1;
-- opt->detect_directory_renames = 1;
+ opt->detect_directory_renames = 1;
++
++ prepare_repo_settings(repo);
++ if (repo->settings.merge_directory_renames >= 0)
++ opt->detect_directory_renames = repo->settings.merge_directory_renames;
++
merge_recursive_config(opt);
merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
if (merge_verbosity)
- diff --git a/merge-recursive.h b/merge-recursive.h
- --- a/merge-recursive.h
- +++ b/merge-recursive.h
-@@
- unsigned renormalize : 1;
- long xdl_opts;
- int verbosity;
-- int detect_directory_renames;
- int diff_detect_rename;
- int merge_detect_rename;
- int diff_rename_limit;
-
diff --git a/repo-settings.c b/repo-settings.c
--- a/repo-settings.c
+++ b/repo-settings.c
@@
- {
- struct repo_settings *rs = (struct repo_settings *)cb;
-
-+ if (!strcmp(key, "feature.experimental")) {
-+ UPDATE_DEFAULT(rs->pack_use_sparse, 1);
-+ UPDATE_DEFAULT(rs->merge_directory_renames, MERGE_DIRECTORY_RENAMES_TRUE);
-+ UPDATE_DEFAULT(rs->fetch_negotiation_algorithm, FETCH_NEGOTIATION_SKIPPING);
-+ return 0;
-+ }
- if (!strcmp(key, "feature.manycommits")) {
- UPDATE_DEFAULT(rs->core_commit_graph, 1);
- UPDATE_DEFAULT(rs->gc_write_commit_graph, 1);
-@@
- "using 'keep' default value"), value);
- return 0;
+ free(strval);
}
-+ if (!strcmp(key, "merge.directoryrenames")) {
-+ int bool_value = git_parse_maybe_bool(value);
-+ if (0 <= bool_value) {
-+ rs->merge_directory_renames = bool_value ? MERGE_DIRECTORY_RENAMES_TRUE : 0;
-+ } else if (!strcasecmp(value, "conflict")) {
-+ rs->merge_directory_renames = MERGE_DIRECTORY_RENAMES_CONFLICT;
-+ }
-+ return 0;
+
++ if (!repo_config_get_maybe_bool(r, "merge.directoryrenames", &value))
++ r->settings.merge_directory_renames = value ? MERGE_DIRECTORY_RENAMES_TRUE : 0;
++ else if (!repo_config_get_string(r, "merge.directoryrenames", &strval)) {
++ if (!strcasecmp(strval, "conflict"))
++ r->settings.merge_directory_renames = MERGE_DIRECTORY_RENAMES_CONFLICT;
+ }
-+ if (!strcmp(key, "fetch.negotiationalgorithm")) {
-+ if (!strcasecmp(value, "skipping")) {
-+ rs->fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING;
-+ } else {
-+ rs->fetch_negotiation_algorithm = FETCH_NEGOTIATION_DEFAULT;
-+ }
-+ return 0;
++ if (!repo_config_get_string(r, "fetch.negotiationalgorithm", &strval)) {
++ if (!strcasecmp(strval, "skipping"))
++ r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING;
++ else
++ r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_DEFAULT;
+ }
- return 1;
- }
+ if (!repo_config_get_bool(r, "pack.usesparse", &value))
+ r->settings.pack_use_sparse = value;
@@
- /* Defaults */
- r->settings->core_commit_graph = -1;
- r->settings->gc_write_commit_graph = -1;
-- r->settings->pack_use_sparse = -1;
-+
- r->settings->index_version = -1;
- r->settings->core_untracked_cache = -1;
-
-+ r->settings->pack_use_sparse = -1;
-+ r->settings->merge_directory_renames = -1;
-+ r->settings->fetch_negotiation_algorithm = -1;
-+
- repo_config(r, git_repo_config, r->settings);
+ UPDATE_DEFAULT(r->settings.index_version, 4);
+ UPDATE_DEFAULT(r->settings.core_untracked_cache, UNTRACKED_CACHE_WRITE);
+ }
++ if (!repo_config_get_bool(r, "feature.experimental", &value) && value) {
++ UPDATE_DEFAULT(r->settings.pack_use_sparse, 1);
++ UPDATE_DEFAULT(r->settings.merge_directory_renames, MERGE_DIRECTORY_RENAMES_TRUE);
++ UPDATE_DEFAULT(r->settings.fetch_negotiation_algorithm, FETCH_NEGOTIATION_SKIPPING);
++ }
/* Hack for test programs like test-dump-untracked-cache */
+ if (ignore_untracked_cache_config)
@@
- r->settings->core_untracked_cache = CORE_UNTRACKED_CACHE_KEEP;
else
- UPDATE_DEFAULT(r->settings->core_untracked_cache, CORE_UNTRACKED_CACHE_KEEP);
+ UPDATE_DEFAULT(r->settings.core_untracked_cache, UNTRACKED_CACHE_KEEP);
+
++ UPDATE_DEFAULT(r->settings.merge_directory_renames, MERGE_DIRECTORY_RENAMES_CONFLICT);
++ UPDATE_DEFAULT(r->settings.fetch_negotiation_algorithm, FETCH_NEGOTIATION_DEFAULT);
+
-+ UPDATE_DEFAULT(r->settings->merge_directory_renames, MERGE_DIRECTORY_RENAMES_CONFLICT);
-+ UPDATE_DEFAULT(r->settings->fetch_negotiation_algorithm, FETCH_NEGOTIATION_DEFAULT);
+ r->settings_initialized = 1;
}
diff --git a/repo-settings.h b/repo-settings.h
--- a/repo-settings.h
+++ b/repo-settings.h
@@
- #define CORE_UNTRACKED_CACHE_WRITE (1 << 0)
- #define CORE_UNTRACKED_CACHE_KEEP (1 << 1)
+ UNTRACKED_CACHE_WRITE = 2
+ };
-+#define MERGE_DIRECTORY_RENAMES_CONFLICT 1
-+#define MERGE_DIRECTORY_RENAMES_TRUE 2
++enum merge_directory_renames_setting {
++ MERGE_DIRECTORY_RENAMES_UNSET = -1,
++ MERGE_DIRECTORY_RENAMES_NONE = 0,
++ MERGE_DIRECTORY_RENAMES_CONFLICT = 1,
++ MERGE_DIRECTORY_RENAMES_TRUE = 2,
++};
+
-+#define FETCH_NEGOTIATION_DEFAULT 1
-+#define FETCH_NEGOTIATION_SKIPPING 2
++enum fetch_negotiation_setting {
++ FETCH_NEGOTIATION_UNSET = -1,
++ FETCH_NEGOTIATION_NONE = 0,
++ FETCH_NEGOTIATION_DEFAULT = 1,
++ FETCH_NEGOTIATION_SKIPPING = 2,
++};
+
struct repo_settings {
int core_commit_graph;
int gc_write_commit_graph;
-- int pack_use_sparse;
-+
- int index_version;
- int core_untracked_cache;
-+
-+ int pack_use_sparse;
-+ int merge_directory_renames;
-+ int fetch_negotiation_algorithm;
+@@
+ enum untracked_cache_setting core_untracked_cache;
+
+ int pack_use_sparse;
++ enum merge_directory_renames_setting merge_directory_renames;
++ enum fetch_negotiation_setting fetch_negotiation_algorithm;
};
struct repository;Signature · 1 line
gitgitgadget
PATCH v2 1/5 repo-settings: consolidate some config settings
From: Derrick Stolee <[email protected]>
There are a few important config settings that are not loaded during git_default_config. These are instead loaded on-demand.
Centralize these config options to a single scan, and store all of the values in a repo_settings struct. The values for each setting are initialized as negative to indicate "unset".
This centralization will be particularly important in a later change to introduce "meta" config settings that change the defaults for these config settings.
--- Makefile | 1 + builtin/gc.c | 13 ++++++------- builtin/pack-objects.c | 9 +++++---- commit-graph.c | 7 ++++--- read-cache.c | 12 +++++++----- repo-settings.c | 28 ++++++++++++++++++++++++++++ repo-settings.h | 17 +++++++++++++++++ repository.h | 5 +++++ 8 files changed, 73 insertions(+), 19 deletions(-) create mode 100644 repo-settings.c create mode 100644 repo-settings.h
Makefile +1 -0
@@ -964,6 +964,7 @@ LIB_OBJS += refspec.o LIB_OBJS += ref-filter.o LIB_OBJS += remote.o LIB_OBJS += replace-object.o +LIB_OBJS += repo-settings.o LIB_OBJS += repository.o LIB_OBJS += rerere.o LIB_OBJS += resolve-undo.o
builtin/gc.c +6 -7
@@ -27,6 +27,7 @@#include "pack-objects.h" #include "blob.h" #include "tree.h" +#include "repo-settings.h" #define FAILED_RUN "failed to run %s"
@@ -41,7 +42,6 @@ static int aggressive_depth = 50;static int aggressive_window =250 ;static int gc_auto_threshold =6700 ;static int gc_auto_pack_limit =50 ; -static int gc_write_commit_graph;static int detach_auto =1 ;static timestamp_t gc_log_expire_time;static const char *gc_log_expire ="1.day.ago" ;
@@ -148,7 +148,6 @@ static void gc_config(void)git_config_get_int ("gc.aggressivedepth" , &aggressive_depth);git_config_get_int ("gc.auto" , &gc_auto_threshold);git_config_get_int ("gc.autopacklimit" , &gc_auto_pack_limit); -git_config_get_bool ("gc.writecommitgraph" , &gc_write_commit_graph);git_config_get_bool ("gc.autodetach" , &detach_auto);git_config_get_expiry ("gc.pruneexpire" , &prune_expire);git_config_get_expiry ("gc.worktreepruneexpire" , &prune_worktrees_expire);
@@ -685,11 +684,11 @@ int cmd_gc(int argc, const char **argv, const char *prefix)clean_pack_garbage (); } -if (gc_write_commit_graph && -write_commit_graph_reachable (get_object_directory (), - !quiet && !daemonized ?COMMIT_GRAPH_PROGRESS :0 , -NULL )) -return 1 ; +prepare_repo_settings (the_repository); +if (the_repository->settings.gc_write_commit_graph ==1 ) +write_commit_graph_reachable (get_object_directory (), + !quiet && !daemonized ?COMMIT_GRAPH_PROGRESS :0 , +NULL );if (auto_gc &&too_many_loose_objects ()) warning(_("There are too many unreachable loose objects; "
builtin/pack-objects.c +5 -4
@@ -34,6 +34,7 @@#include "dir.h" #include "midx.h" #include "trace2.h" +#include "repo-settings.h" #define IN_PACK (obj) oe_in_pack(&to_pack, obj)#define SIZE (obj) oe_size(&to_pack, obj)
@@ -2709,10 +2710,6 @@ static int git_pack_config(const char *k, const char *v, void *cb) use_bitmap_index_default =git_config_bool (k, v);return 0 ; } -if (!strcmp (k,"pack.usesparse" )) { - sparse =git_config_bool (k, v); -return 0 ; - }if (!strcmp (k,"pack.threads" )) { delta_search_threads =git_config_int (k, v);if (delta_search_threads <0 )
@@ -3332,6 +3329,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) read_replace_refs =0 ; sparse =git_env_bool ("GIT_TEST_PACK_SPARSE" ,0 ); +prepare_repo_settings (the_repository); +if (!sparse && the_repository->settings.pack_use_sparse !=-1 ) + sparse = the_repository->settings.pack_use_sparse; +reset_pack_idx_option (&pack_idx_opts);git_config (git_pack_config,NULL );
commit-graph.c +4 -3
@@ -16,6 +16,7 @@#include "hashmap.h" #include "replace-object.h" #include "progress.h" +#include "repo-settings.h" #define GRAPH_SIGNATURE 0x43475048/* "CGPH" */ #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446/* "OIDF" */
@@ -466,7 +467,6 @@ static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)static int prepare_commit_graph (struct repository *r) {struct object_directory *odb; -int config_value;if (git_env_bool (GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD ,0 )) die("dying as requested by the '%s' variable on commit-graph load!",
@@ -476,9 +476,10 @@ static int prepare_commit_graph(struct repository *r)return !!r->objects->commit_graph; r->objects->commit_graph_attempted =1 ; +prepare_repo_settings (r); +if (!git_env_bool (GIT_TEST_COMMIT_GRAPH ,0 ) && - (repo_config_get_bool (r,"core.commitgraph" , &config_value) || - !config_value)) + r->settings.core_commit_graph !=1 )/* * This repository is not configured to use commit graphs, so * do not load one. (But report commit_graph_attempted anyway
read-cache.c +7 -5
@@ -25,6 +25,7 @@#include "fsmonitor.h" #include "thread-utils.h" #include "progress.h" +#include "repo-settings.h" /* Mask for the name length in ce_flags in the on-disk index */
@@ -1599,16 +1600,17 @@ struct cache_entry *refresh_cache_entry(struct index_state *istate,#define INDEX_FORMAT_DEFAULT 3 -static unsigned int get_index_format_default (void ) +static unsigned int get_index_format_default (struct repository *r) {char *envversion =getenv ("GIT_INDEX_VERSION" );char *endp; -int value;unsigned int version =INDEX_FORMAT_DEFAULT ;if (!envversion) { -if (!git_config_get_int ("index.version" , &value)) - version = value; +prepare_repo_settings (r); + +if (r->settings.index_version >=0 ) + version = r->settings.index_version;if (version <INDEX_FORMAT_LB ||INDEX_FORMAT_UB < version) {warning (_ ("index.version set, but the value is invalid.\n" "Using version %i" ),INDEX_FORMAT_DEFAULT );
@@ -2765,7 +2767,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, }if (!istate->version) { - istate->version =get_index_format_default (); + istate->version =get_index_format_default (the_repository);if (git_env_bool ("GIT_TEST_SPLIT_INDEX" ,0 ))init_split_index (istate); }
repo-settings.c new file +28 -0
@@ -0,0 +1,28 @@ +#include "cache.h" +#include "repository.h" +#include "config.h" +#include "repo-settings.h" + +void prepare_repo_settings (struct repository *r) +{ +int value; + +if (r->settings_initialized) +return ; + +/* Defaults */ +memset (&r->settings,-1 ,sizeof (r->settings)); + +if (!repo_config_get_bool (r,"core.commitgraph" , &value)) + r->settings.core_commit_graph = value; +if (!repo_config_get_bool (r,"gc.writecommitgraph" , &value)) + r->settings.gc_write_commit_graph = value; + +if (!repo_config_get_bool (r,"index.version" , &value)) + r->settings.index_version = value; + +if (!repo_config_get_bool (r,"pack.usesparse" , &value)) + r->settings.pack_use_sparse = value; + + r->settings_initialized =1 ; +}
repo-settings.h new file +17 -0
@@ -0,0 +1,17 @@ +#ifndef REPO_SETTINGS_H +#define REPO_SETTINGS_H + +struct repo_settings { +int core_commit_graph; +int gc_write_commit_graph; + +int index_version; + +int pack_use_sparse; +}; + +struct repository ; + +void prepare_repo_settings (struct repository *r); + +#endif /* REPO_SETTINGS_H */
repository.h +5 -0
@@ -2,8 +2,10 @@#define REPOSITORY_H #include "path.h" +#include "repo-settings.h" struct config_set ; +struct repo_settings ;struct git_hash_algo ;struct index_state ;struct lock_file ;
@@ -72,6 +74,9 @@ struct repository {*/ char *submodule_prefix; +int settings_initialized; +struct repo_settings settings; +/* Subsystems */ /* * Repository's config which contains key-value pairs from the usual
Signature · 1 line
gitgitgadget
Re: [PATCH v2 1/5] repo-settings: consolidate some config settings
Hi Stolee,
Quoted 28 lines · Derrick Stolee via GitGitGadget
repo-settings.h new file +17
-0@@ -0,0 +1,17 @@ +#ifndef REPO_SETTINGS_H +#define REPO_SETTINGS_H + +struct repo_settings { +int core_commit_graph; +int gc_write_commit_graph; + +int index_version; + +int pack_use_sparse; +}; + +struct repository ; + +void prepare_repo_settings (struct repository *r); + +#endif /* REPO_SETTINGS_H */ repository.h +0
-0
repository.h line 5
Quoted 4 lines
repository.h +1
-0@@ -2,8 +2,10 @@#define REPOSITORY_H #include "path.h" +#include "repo-settings.h"
I still think that the repo_settings struct could just as easily be declared in repository.h. No need to invent a new header file.
repository.h line 8
Quoted 3 lines
repository.h +1
-0struct config_set ; +struct repo_settings ;
In any case, this is no longer necessary.
repository.h struct repository { · line 77
Quoted 8 lines
repository.h +1
-0struct git_hash_algo ;struct index_state ;struct lock_file ;@@ -72,6 +74,9 @@ struct repository {*/ char *submodule_prefix; +int settings_initialized;
Or maybe
unsigned settings_initialized:1;
?
repository.h struct repository { · line 79
Quoted 2 lines
repository.h +2
-0+struct repo_settings settings; +
Or maybe even fold the initialized flag into that struct?
Thanks,
Dscho
Quoted 7 lines
repository.h +0
-1/* Subsystems */ /* * Repository's config which contains key-value pairs from the usual -- gitgitgadget
PATCH v2 2/5 repo-settings: add feature.manyCommits setting
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
When a repo has many commits, it is helpful to write and read the commit-graph file. Future changes to Git may include new config settings that are beneficial in this scenario.
Create the 'feature.manyCommits' config setting that changes the default values of 'core.commitGraph' and 'gc.writeCommitGraph' to true.
--- Documentation/config.txt | 2 ++ Documentation/config/core.txt | 3 ++- Documentation/config/feature.txt | 15 +++++++++++++++ Documentation/config/gc.txt | 4 ++-- repo-settings.c | 7 +++++++ 5 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 Documentation/config/feature.txt
Documentation/config.txt +2 -0
@@ -345,6 +345,8 @@ include::config/difftool.txt[] include::config/fastimport.txt[] +include::config/feature.txt[] + include::config/fetch.txt[] include::config/format.txt[]
Documentation/config/core.txt +2 -1
@@ -577,7 +577,8 @@ the `GIT_NOTES_REF` environment variable. See linkgit:git-notes[1]. core.commitGraph:: If true, then git will read the commit-graph file (if it exists) - to parse the graph structure of commits. Defaults to false. See + to parse the graph structure of commits. Defaults to false, unless + `feature.manyCommits` is enabled. See linkgit:git-commit-graph[1] for more information. core.useReplaceRefs::
Documentation/config/feature.txt new file +15 -0
@@ -0,0 +1,15 @@ +feature.*:: + The config settings that start with `feature.` modify the defaults of + a group of other config settings. These groups are created by the Git + developer community as recommended defaults and are subject to change. + In particular, new config options may be added with different defaults. + +feature.manyCommits:: + Enable config options that optimize for repos with many commits. This + setting is recommended for repos with at least 100,000 commits. The + new default values are: ++ +* `core.commitGraph=true` enables reading the commit-graph file. ++ +* `gc.writeCommitGraph=true` enables writing the commit-graph file during +garbage collection.
Documentation/config/gc.txt +2 -2
@@ -63,8 +63,8 @@ gc.writeCommitGraph:: If true, then gc will rewrite the commit-graph file when linkgit:git-gc[1] is run. When using `git gc --auto` the commit-graph will be updated if housekeeping is - required. Default is false. See linkgit:git-commit-graph[1] - for details. + required. Default is false, unless `feature.manyCommits` + is enabled. See linkgit:git-commit-graph[1] for details. gc.logExpiry:: If the file gc.log exists, then `git gc --auto` will print
repo-settings.c +7 -0
@@ -3,6 +3,8 @@#include "config.h" #include "repo-settings.h" +#define UPDATE_DEFAULT (s,v) do { if (s == -1) { s = v; } } while(0) +void prepare_repo_settings (struct repository *r) {int value;
@@ -24,5 +26,10 @@ void prepare_repo_settings(struct repository *r)if (!repo_config_get_bool (r,"pack.usesparse" , &value)) r->settings.pack_use_sparse = value; +if (!repo_config_get_bool (r,"feature.manycommits" , &value) && value) { +UPDATE_DEFAULT (r->settings.core_commit_graph,1 ); +UPDATE_DEFAULT (r->settings.gc_write_commit_graph,1 ); + } + r->settings_initialized =1 ; }
Signature · 1 line
gitgitgadget
PATCH v2 3/5 repo-settings: parse core.untrackedCache
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The core.untrackedCache config setting is slightly complicated, so clarify its use and centralize its parsing into the repo settings.
The default value is "keep" (returned as -1), which persists the untracked cache if it exists.
If the value is set as "false" (returned as 0), then remove the untracked cache if it exists.
If the value is set as "true" (returned as 1), then write the untracked cache and persist it.
Instead of relying on magic values of -1, 0, and 1, split these options into an enum. This allows the use of "-1" as a default value. After parsing the config options, if the value is unset we can initialize it to UNTRACKED_CACHE_KEEP.
--- builtin/update-index.c | 7 +++++-- config.c | 24 ------------------------ read-cache.c | 19 +++++++++---------- repo-settings.c | 21 ++++++++++++++++++++- repo-settings.h | 8 ++++++++ 5 files changed, 42 insertions(+), 37 deletions(-)
builtin/update-index.c +5 -2
@@ -18,6 +18,7 @@#include "dir.h" #include "split-index.h" #include "fsmonitor.h" +#include "repo-settings.h" /* * Default to not allowing changes to the list of files. The
@@ -966,6 +967,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)struct parse_opt_ctx_t ctx;strbuf_getline_fn getline_fn;int parseopt_state =PARSE_OPT_UNKNOWN ; +struct repository *r = the_repository;struct option options[] = { OPT_BIT('q', NULL, &refresh_args.flags, N_("continue refresh even when index needs update"),
@@ -1180,11 +1182,12 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)remove_split_index (&the_index); } +prepare_repo_settings (r);switch (untracked_cache) {case UC_UNSPECIFIED :break ;case UC_DISABLE : -if (git_config_get_untracked_cache () ==1 ) +if (r->settings.core_untracked_cache ==UNTRACKED_CACHE_WRITE )warning (_ ("core.untrackedCache is set to true; " "remove or change it, if you really want to " "disable the untracked cache" ));
@@ -1196,7 +1199,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)return !test_if_untracked_cache_is_supported ();case UC_ENABLE :case UC_FORCE : -if (git_config_get_untracked_cache () ==0 ) +if (r->settings.core_untracked_cache ==UNTRACKED_CACHE_REMOVE )warning (_ ("core.untrackedCache is set to false; " "remove or change it, if you really want to " "enable the untracked cache" ));
config.c +0 -24
@@ -2277,30 +2277,6 @@ int git_config_get_expiry_in_days(const char *key, timestamp_t *expiry, timestamreturn -1 ;/* thing exists but cannot be parsed */ } -int git_config_get_untracked_cache (void ) -{ -int val =-1 ; -const char *v; - -/* Hack for test programs like test-dump-untracked-cache */ -if (ignore_untracked_cache_config) -return -1 ; - -if (!git_config_get_maybe_bool ("core.untrackedcache" , &val)) -return val; - -if (!git_config_get_value ("core.untrackedcache" , &v)) { -if (!strcasecmp (v,"keep" )) -return -1 ; - -error (_ ("unknown core.untrackedCache value '%s'; " -"using 'keep' default value" ), v); -return -1 ; - } - -return -1 ;/* default value */ -} -int git_config_get_split_index (void ) {int val;
read-cache.c +9 -10
@@ -1846,18 +1846,17 @@ static void check_ce_order(struct index_state *istate)static void tweak_untracked_cache (struct index_state *istate) { -switch (git_config_get_untracked_cache ()) { -case -1 :/* keep: do nothing */ -break ; -case 0 :/* false */ +struct repository *r = the_repository; + +prepare_repo_settings (r); + +if (r->settings.core_untracked_cache ==UNTRACKED_CACHE_REMOVE ) {remove_untracked_cache (istate); -break ; -case 1 :/* true */ -add_untracked_cache (istate); -break ; -default :/* unknown value: do nothing */ -break ; +return ; } + +if (r->settings.core_untracked_cache ==UNTRACKED_CACHE_WRITE ) +add_untracked_cache (istate); }static void tweak_split_index (struct index_state *istate)
repo-settings.c +20 -1
@@ -8,6 +8,7 @@void prepare_repo_settings (struct repository *r) {int value; +char *strval;if (r->settings_initialized)return ;
@@ -22,14 +23,32 @@ void prepare_repo_settings(struct repository *r)if (!repo_config_get_bool (r,"index.version" , &value)) r->settings.index_version = value; +if (!repo_config_get_maybe_bool (r,"core.untrackedcache" , &value)) { +if (value ==0 ) + r->settings.core_untracked_cache =UNTRACKED_CACHE_REMOVE ; +else + r->settings.core_untracked_cache =UNTRACKED_CACHE_WRITE ; + }else if (!repo_config_get_string (r,"core.untrackedcache" , &strval)) { +if (!strcasecmp (strval,"keep" )) + r->settings.core_untracked_cache =UNTRACKED_CACHE_KEEP ; + +free (strval); + } +if (!repo_config_get_bool (r,"pack.usesparse" , &value)) r->settings.pack_use_sparse = value; - +if (!repo_config_get_bool (r,"feature.manycommits" , &value) && value) {UPDATE_DEFAULT (r->settings.core_commit_graph,1 );UPDATE_DEFAULT (r->settings.gc_write_commit_graph,1 ); } +/* Hack for test programs like test-dump-untracked-cache */ +if (ignore_untracked_cache_config) + r->settings.core_untracked_cache =UNTRACKED_CACHE_KEEP ; +else +UPDATE_DEFAULT (r->settings.core_untracked_cache,UNTRACKED_CACHE_KEEP ); + r->settings_initialized =1 ; }
repo-settings.h +8 -0
@@ -1,11 +1,19 @@#ifndef REPO_SETTINGS_H #define REPO_SETTINGS_H +enum untracked_cache_setting { +UNTRACKED_CACHE_UNSET =-1 , +UNTRACKED_CACHE_REMOVE =0 , +UNTRACKED_CACHE_KEEP =1 , +UNTRACKED_CACHE_WRITE =2 +}; +struct repo_settings {int core_commit_graph;int gc_write_commit_graph;int index_version; +enum untracked_cache_setting core_untracked_cache;int pack_use_sparse; };
Signature · 1 line
gitgitgadget
Re: [PATCH v2 3/5] repo-settings: parse core.untrackedCache
Hi Stolee,
builtin/update-index.c line 21
Quoted 9 lines · Derrick Stolee via GitGitGadget
builtin/update-index.c +1
-0@@ -18,6 +18,7 @@#include "dir.h" #include "split-index.h" #include "fsmonitor.h" +#include "repo-settings.h"
Sorry, I only realized 10 seconds ago that these #includes are no longer necessary because repo-settings is already included from repository.h (and needs to be, because the repository struct now contains a full struct repo_settings field, no longer just a pointer).
But really, I would just fold the repo-settings.h declarations directly into repository.h (but keep repo-settings.c, of course!) as it make everything a bit simpler.
Thanks,
Dscho
PATCH v2 4/5 repo-settings: create feature.manyFiles setting
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The feature.manyFiles setting is suitable for repos with many files in the working directory. By setting index.version=4 and core.untrackedCache=true, commands such as 'git status' should improve.
While adding this setting, modify the index version precedence tests to check how this setting overrides the default for index.version is unset.
--- Documentation/config/core.txt | 4 +++- Documentation/config/feature.txt | 12 +++++++++++- Documentation/config/index.txt | 1 + repo-settings.c | 4 ++++ t/t1600-index.sh | 31 ++++++++++++++++++++++++++----- 5 files changed, 45 insertions(+), 7 deletions(-)
Documentation/config/core.txt +3 -1
@@ -86,7 +86,9 @@ core.untrackedCache:: it will automatically be removed, if set to `false`. Before setting it to `true`, you should check that mtime is working properly on your system. - See linkgit:git-update-index[1]. `keep` by default. + See linkgit:git-update-index[1]. `keep` by default, unless + `feature.manyFiles` is enabled which sets this setting to + `true` by default. core.checkStat:: When missing or is set to `default`, many fields in the stat
Documentation/config/feature.txt +11 -1
@@ -12,4 +12,14 @@ feature.manyCommits:: * `core.commitGraph=true` enables reading the commit-graph file. + * `gc.writeCommitGraph=true` enables writing the commit-graph file during -garbage collection. +garbage collection. + +feature.manyFiles:: + Enable config options that optimize for repos with many files in the + working directory. With many files, commands such as `git status` and + `git checkout` may be slow and these new defaults improve performance: ++ +* `index.version=4` enables path-prefix compression in the index. ++ +* `core.untrackedCache=true` enables the untracked cache. This setting assumes +that mtime is working on your machine.
Documentation/config/index.txt +1 -0
@@ -24,3 +24,4 @@ index.threads:: index.version:: Specify the version with which new index files should be initialized. This does not affect existing repositories. + If `feature.manyFiles` is enabled, then the default is 4.
repo-settings.c +4 -0
@@ -43,6 +43,10 @@ void prepare_repo_settings(struct repository *r)UPDATE_DEFAULT (r->settings.core_commit_graph,1 );UPDATE_DEFAULT (r->settings.gc_write_commit_graph,1 ); } +if (!repo_config_get_bool (r,"feature.manyfiles" , &value) && value) { +UPDATE_DEFAULT (r->settings.index_version,4 ); +UPDATE_DEFAULT (r->settings.core_untracked_cache,UNTRACKED_CACHE_WRITE ); + }/* Hack for test programs like test-dump-untracked-cache */ if (ignore_untracked_cache_config)
t/t1600-index.sh +26 -5
@@ -59,17 +59,38 @@ test_expect_success 'out of bounds index.version issues warning' ' ) ' -test_expect_success 'GIT_INDEX_VERSION takes precedence over config' ' +test_index_version () { + INDEX_VERSION_CONFIG=$1 && + FEATURE_MANY_FILES=$2 && + ENV_VAR_VERSION=$3 + EXPECTED_OUTPUT_VERSION=$4 && ( rm -f .git/index && - GIT_INDEX_VERSION=4 && - export GIT_INDEX_VERSION && - git config --add index.version 2 && + rm -f .git/config && + if test "$INDEX_VERSION_CONFIG" -ne 0 + then + git config --add index.version $INDEX_VERSION_CONFIG + fi && + git config --add feature.manyFiles $FEATURE_MANY_FILES + if test "$ENV_VAR_VERSION" -ne 0 + then + GIT_INDEX_VERSION=$ENV_VAR_VERSION && + export GIT_INDEX_VERSION + else + unset GIT_INDEX_VERSION + fi && git add a 2>&1 && - echo 4 >expect && + echo $EXPECTED_OUTPUT_VERSION >expect && test-tool index-version <.git/index >actual && test_cmp expect actual ) +} + +test_expect_success 'index version config precedence' ' + test_index_version 2 false 4 4 && + test_index_version 2 true 0 2 && + test_index_version 0 true 0 4 && + test_index_version 0 true 2 2 ' test_done
Signature · 1 line
gitgitgadget
PATCH v2 5/5 repo-settings: create feature.experimental setting
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The 'feature.experimental' setting includes config options that are not committed to become defaults, but could use additional testing.
Update the following config settings to take new defaults, and to use the repo_settings struct if not already using them:
* 'pack.useSparse=true'
* 'merge.directoryRenames=true'
* 'fetch.negotiationAlgorithm=skipping'
In the case of fetch.negotiationAlgorithm, the existing logic would load the config option only when about to use the setting, so had a die() statement on an unknown string value. This is removed as now the config is parsed under prepare_repo_settings(). In general, this die() is probably misplaced and not valuable. A test was removed that checked this die() statement executed.
--- Documentation/config/feature.txt | 17 +++++++++++++++++ Documentation/config/fetch.txt | 3 ++- Documentation/config/merge.txt | 3 ++- Documentation/config/pack.txt | 3 ++- builtin/am.c | 1 + fetch-negotiator.c | 26 ++++++++++++++------------ fetch-negotiator.h | 5 +++-- fetch-pack.c | 11 +++++------ merge-recursive.c | 15 ++++++--------- repo-settings.c | 20 ++++++++++++++++++++ repo-settings.h | 16 ++++++++++++++++ t/t5552-skipping-fetch-negotiator.sh | 23 ----------------------- 12 files changed, 88 insertions(+), 55 deletions(-)
Documentation/config/feature.txt +17 -0
@@ -4,6 +4,23 @@ feature.*:: developer community as recommended defaults and are subject to change. In particular, new config options may be added with different defaults. +feature.experimental:: + Enable config options that are new to Git, and are being considered for + future defaults. Config settings included here may be added or removed + with each release, including minor version updates. These settings may + have unintended interactions since they are so new. Please enable this + setting if you are interested in providing feedback on experimental + features. The new default values are: ++ +* `pack.useSparse=true` uses a new algorithm when constructing a pack-file +which can improve `git push` performance in repos with many files. ++ +* `merge.directoryRenames=true` uses a new algorithm for detecting renames by +using entire directories at a time instead of single files at a time. ++ +* `fetch.negotiationAlgorithm=skipping` may improve fetch negotiation times by +skipping more commits at a time, reducing the number of round trips. + feature.manyCommits:: Enable config options that optimize for repos with many commits. This setting is recommended for repos with at least 100,000 commits. The
Documentation/config/fetch.txt +2 -1
@@ -59,7 +59,8 @@ fetch.negotiationAlgorithm:: effort to converge faster, but may result in a larger-than-necessary packfile; The default is "default" which instructs Git to use the default algorithm that never skips commits (unless the server has acknowledged it or one - of its descendants). + of its descendants). If `feature.experimental` is enabled, then this + setting defaults to "skipping". Unknown values will cause 'git fetch' to error out. + See also the `--negotiation-tip` option for linkgit:git-fetch[1].
Documentation/config/merge.txt +2 -1
@@ -54,7 +54,8 @@ merge.directoryRenames:: moved into the new directory. If set to "conflict", a conflict will be reported for such paths. If merge.renames is false, merge.directoryRenames is ignored and treated as false. Defaults - to "conflict". + to "conflict" unless `feature.experimental` is enabled and the + default is "true". merge.renormalize:: Tell Git that canonical representation of files in the
Documentation/config/pack.txt +2 -1
@@ -112,7 +112,8 @@ pack.useSparse:: objects. This can have significant performance benefits when computing a pack to send a small change. However, it is possible that extra objects are added to the pack-file if the included - commits contain certain types of direct renames. + commits contain certain types of direct renames. Default is `false` + unless `feature.experimental` is enabled. pack.writeBitmaps (deprecated):: This is a deprecated synonym for `repack.writeBitmaps`.
builtin/am.c +1 -0
@@ -34,6 +34,7 @@#include "string-list.h" #include "packfile.h" #include "repository.h" +#include "repo-settings.h" /** * Returns the length of the first line of msg.
fetch-negotiator.c +14 -12
@@ -2,19 +2,21 @@#include "fetch-negotiator.h" #include "negotiator/default.h" #include "negotiator/skipping.h" +#include "repository.h" +#include "repo-settings.h" -void fetch_negotiator_init (struct fetch_negotiator *negotiator, -const char *algorithm) +void fetch_negotiator_init (struct repository *r, +struct fetch_negotiator *negotiator) { -if (algorithm) { -if (!strcmp (algorithm,"skipping" )) { -skipping_negotiator_init (negotiator); -return ; - }else if (!strcmp (algorithm,"default" )) { -/* Fall through to default initialization */ - }else { -die ("unknown fetch negotiation algorithm '%s'" , algorithm); - } +prepare_repo_settings (r); +switch (r->settings.fetch_negotiation_algorithm) { +case FETCH_NEGOTIATION_SKIPPING : +skipping_negotiator_init (negotiator); +return ; + +case FETCH_NEGOTIATION_DEFAULT : +default : +default_negotiator_init (negotiator); +return ; } -default_negotiator_init (negotiator); }
fetch-negotiator.h +3 -2
@@ -2,6 +2,7 @@#define FETCH_NEGOTIATOR_H struct commit ; +struct repository ;/* * An object that supplies the information needed to negotiate the contents of
@@ -52,7 +53,7 @@ struct fetch_negotiator { void *data; }; -void fetch_negotiator_init (struct fetch_negotiator *negotiator, -const char *algorithm); +void fetch_negotiator_init (struct repository *r, +struct fetch_negotiator *negotiator);#endif
fetch-pack.c +5 -6
@@ -36,7 +36,6 @@ static int agent_supported;static int server_supports_filtering;static struct lock_file shallow_lock;static const char *alternate_shallow_file; -static char *negotiation_algorithm;static struct strbuf fsck_msg_types =STRBUF_INIT ;/* Remember to update object flag allocation in object.h */
@@ -892,12 +891,13 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,struct shallow_info *si, char **pack_lockfile) { + struct repository *r = the_repository;struct ref *ref =copy_ref_list (orig_ref);struct object_id oid;const char *agent_feature;int agent_len;struct fetch_negotiator negotiator; -fetch_negotiator_init (&negotiator, negotiation_algorithm); +fetch_negotiator_init (r, &negotiator);sort_ref_list (&ref, ref_compare_name);QSORT (sought, nr_sought, cmp_ref_by_name);
@@ -911,7 +911,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,if (server_supports ("shallow" ))print_verbose (args,_ ("Server supports %s" ),"shallow" ); -else if (args->depth >0 ||is_repository_shallow (the_repository)) +else if (args->depth >0 ||is_repository_shallow (r))die (_ ("Server does not support shallow clients" ));if (args->depth >0 || args->deepen_since || args->deepen_not) args->deepen =1 ;
@@ -1379,6 +1379,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,struct shallow_info *si, char **pack_lockfile) { + struct repository *r = the_repository;struct ref *ref =copy_ref_list (orig_ref);enum fetch_state state =FETCH_CHECK_LOCAL ;struct oidset common =OIDSET_INIT ;
@@ -1386,7 +1387,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,int in_vain =0 ;int haves_to_send =INITIAL_FLUSH ;struct fetch_negotiator negotiator; -fetch_negotiator_init (&negotiator, negotiation_algorithm); +fetch_negotiator_init (r, &negotiator);packet_reader_init (&reader, fd[0 ],NULL ,0 ,PACKET_READ_CHOMP_NEWLINE |PACKET_READ_DIE_ON_ERR_PACKET );
@@ -1505,8 +1506,6 @@ static void fetch_pack_config(void)git_config_get_bool ("repack.usedeltabaseoffset" , &prefer_ofs_delta);git_config_get_bool ("fetch.fsckobjects" , &fetch_fsck_objects);git_config_get_bool ("transfer.fsckobjects" , &transfer_fsck_objects); -git_config_get_string ("fetch.negotiationalgorithm" , - &negotiation_algorithm);git_config (fetch_pack_config_cb,NULL ); }
merge-recursive.c +6 -9
@@ -28,6 +28,7 @@#include "submodule.h" #include "revision.h" #include "commit-reach.h" +#include "repo-settings.h" struct path_hashmap_entry {struct hashmap_entry e;
@@ -3662,15 +3663,6 @@ static void merge_recursive_config(struct merge_options *opt) opt->merge_detect_rename =git_config_rename ("merge.renames" , value);free (value); } -if (!git_config_get_string ("merge.directoryrenames" , &value)) { -int boolval =git_parse_maybe_bool (value); -if (0 <= boolval) { - opt->detect_directory_renames = boolval ?2 :0 ; - }else if (!strcasecmp (value,"conflict" )) { - opt->detect_directory_renames =1 ; - }/* avoid erroring on values from future versions of git */ -free (value); - }git_config (git_xmerge_config,NULL ); }
@@ -3688,6 +3680,11 @@ void init_merge_options(struct merge_options *opt, opt->diff_detect_rename =-1 ; opt->merge_detect_rename =-1 ; opt->detect_directory_renames =1 ; + +prepare_repo_settings (repo); +if (repo->settings.merge_directory_renames >=0 ) + opt->detect_directory_renames = repo->settings.merge_directory_renames; +merge_recursive_config (opt); merge_verbosity =getenv ("GIT_MERGE_VERBOSITY" );if (merge_verbosity)
repo-settings.c +20 -0
@@ -35,6 +35,18 @@ void prepare_repo_settings(struct repository *r)free (strval); } +if (!repo_config_get_maybe_bool (r,"merge.directoryrenames" , &value)) + r->settings.merge_directory_renames = value ?MERGE_DIRECTORY_RENAMES_TRUE :0 ; +else if (!repo_config_get_string (r,"merge.directoryrenames" , &strval)) { +if (!strcasecmp (strval,"conflict" )) + r->settings.merge_directory_renames =MERGE_DIRECTORY_RENAMES_CONFLICT ; + } +if (!repo_config_get_string (r,"fetch.negotiationalgorithm" , &strval)) { +if (!strcasecmp (strval,"skipping" )) + r->settings.fetch_negotiation_algorithm =FETCH_NEGOTIATION_SKIPPING ; +else + r->settings.fetch_negotiation_algorithm =FETCH_NEGOTIATION_DEFAULT ; + }if (!repo_config_get_bool (r,"pack.usesparse" , &value)) r->settings.pack_use_sparse = value;
@@ -47,6 +59,11 @@ void prepare_repo_settings(struct repository *r)UPDATE_DEFAULT (r->settings.index_version,4 );UPDATE_DEFAULT (r->settings.core_untracked_cache,UNTRACKED_CACHE_WRITE ); } +if (!repo_config_get_bool (r,"feature.experimental" , &value) && value) { +UPDATE_DEFAULT (r->settings.pack_use_sparse,1 ); +UPDATE_DEFAULT (r->settings.merge_directory_renames,MERGE_DIRECTORY_RENAMES_TRUE ); +UPDATE_DEFAULT (r->settings.fetch_negotiation_algorithm,FETCH_NEGOTIATION_SKIPPING ); + }/* Hack for test programs like test-dump-untracked-cache */ if (ignore_untracked_cache_config)
@@ -54,5 +71,8 @@ void prepare_repo_settings(struct repository *r)else UPDATE_DEFAULT (r->settings.core_untracked_cache,UNTRACKED_CACHE_KEEP ); +UPDATE_DEFAULT (r->settings.merge_directory_renames,MERGE_DIRECTORY_RENAMES_CONFLICT ); +UPDATE_DEFAULT (r->settings.fetch_negotiation_algorithm,FETCH_NEGOTIATION_DEFAULT ); + r->settings_initialized =1 ; }
repo-settings.h +16 -0
@@ -8,6 +8,20 @@ enum untracked_cache_setting {UNTRACKED_CACHE_WRITE =2 }; +enum merge_directory_renames_setting { +MERGE_DIRECTORY_RENAMES_UNSET =-1 , +MERGE_DIRECTORY_RENAMES_NONE =0 , +MERGE_DIRECTORY_RENAMES_CONFLICT =1 , +MERGE_DIRECTORY_RENAMES_TRUE =2 , +}; + +enum fetch_negotiation_setting { +FETCH_NEGOTIATION_UNSET =-1 , +FETCH_NEGOTIATION_NONE =0 , +FETCH_NEGOTIATION_DEFAULT =1 , +FETCH_NEGOTIATION_SKIPPING =2 , +}; +struct repo_settings {int core_commit_graph;int gc_write_commit_graph;
@@ -16,6 +30,8 @@ struct repo_settings {enum untracked_cache_setting core_untracked_cache;int pack_use_sparse; +enum merge_directory_renames_setting merge_directory_renames; +enum fetch_negotiation_setting fetch_negotiation_algorithm; };struct repository ;
t/t5552-skipping-fetch-negotiator.sh +0 -23
@@ -60,29 +60,6 @@ test_expect_success 'commits with no parents are sent regardless of skip distanc have_not_sent c6 c4 c3 ' -test_expect_success 'unknown fetch.negotiationAlgorithm values error out' ' - rm -rf server client trace && - git init server && - test_commit -C server to_fetch && - - git init client && - test_commit -C client on_client && - git -C client checkout on_client && - - test_config -C client fetch.negotiationAlgorithm invalid && - test_must_fail git -C client fetch "$(pwd)/server" 2>err && - test_i18ngrep "unknown fetch negotiation algorithm" err && - - # Explicit "default" value - test_config -C client fetch.negotiationAlgorithm default && - git -C client -c fetch.negotiationAlgorithm=default fetch "$(pwd)/server" && - - # Implementation detail: If there is nothing to fetch, we will not error out - test_config -C client fetch.negotiationAlgorithm invalid && - git -C client fetch "$(pwd)/server" 2>err && - test_i18ngrep ! "unknown fetch negotiation algorithm" err -' - test_expect_success 'when two skips collide, favor the larger one' ' rm -rf server client trace && git init server &&
Signature · 1 line
gitgitgadget
Hi Stolee,
Quoted 11 lines · Derrick Stolee via GitGitGadget
Updates in V2: I'm responding to Dscho's comments early, as they were very valuable.
* Rearranged how we are checking for the feature.* settings, so if one layer turns the setting on and a later layer turns it off, we do not adjust the defaults.
* Switched to using enums for the non-boolean config options.
Quoted 8 lines
* Placed the
repo_settingsstruct directly in the repository struct.* All struct members are initialized to -1 using memset.
* The config changes around directory rename detection is greatly simplified.
I left only a couple of comments, and quite honestly, I am really happy with the direction this is going.
It even leaves the door open to let users define their custom feature groups, at least I have a couple of ideas how that could be accomplished building on top of repo-settings.c.
Thanks!
Dscho
v3 cover Create 'feature.*' config area and some centralized config parsing
In reply to Derrick Stolee via GitGitGadgetThis is a brand-new thread to replace ds/early-access. The discussion on that thread was very helpful [1].
With this in mind, I propose instead a set of "feature.*" config settings that form groups of "community recommended" settings (with some caveats). In the space below, I'll list a set of possible feature names and the implied config options.
First, the main two categories we've discussed so far: many commits and many files. These two feature sets are for when your repo is large in one of these dimensions. Perhaps there are other settings to include in these?
feature.manyFiles:
index.version = 4
core.untrackedCache = true
feature.manyCommits:
core.commitGraph = true
gc.writeCommitGraph = true
(future: fetch.writeSplitCommitGraph = true)
Note: the fetch.writeSplitCommitGraph does not exist yet, but could be introduced in a later release to write a new commit-graph (with --split) on fetch.
The other category that has been discussed already is that of "experimental features that we generally think are helpful but change behavior slightly in some cases".
feature.experimental:
pack.useSparse = true
merge.directoryRenames = true
fetch.negotiationAlgorithm = skipping
Specifically, this setting is for config values we are not sure will ever be on by default, but additional testing is needed to be sure. This is different than a possible 'feature.preview' setting that would include config settings that we are committed to updating the defaults in a future release. There are many ways we can take this idea in the future (including more additions to these categories).
Thanks, -Stolee
Updates in V2: I'm responding to Dscho's comments early, as they were very valuable.
* Rearranged how we are checking for the feature.* settings, so if one layer turns the setting on and a later layer turns it off, we do not adjust the defaults.
* Switched to using enums for the non-boolean config options.
* Placed the repo_settings struct directly in the repository struct.
* All struct members are initialized to -1 using memset.
* The config changes around directory rename detection is greatly simplified.
Updates in V3: These are more Dscho comments, and are quite small.
* Remove repo-settings.h by placing the struct and function definitions in repository.h.
* Move the initialization int into "struct repo_settings".
[1] https://public-inbox.org/git/[email protected]/
Derrick Stolee (5):
repo-settings: consolidate some config settings
repo-settings: add feature.manyCommits setting
repo-settings: parse core.untrackedCache
repo-settings: create feature.manyFiles setting
repo-settings: create feature.experimental setting
Documentation/config.txt | 2 + Documentation/config/core.txt | 7 ++- Documentation/config/feature.txt | 42 ++++++++++++++++ Documentation/config/fetch.txt | 3 +- Documentation/config/gc.txt | 4 +- Documentation/config/index.txt | 1 + Documentation/config/merge.txt | 3 +- Documentation/config/pack.txt | 3 +- Makefile | 1 + builtin/gc.c | 12 ++--- builtin/pack-objects.c | 8 +-- builtin/update-index.c | 6 ++- commit-graph.c | 6 +-- config.c | 24 --------- fetch-negotiator.c | 25 +++++----- fetch-negotiator.h | 5 +- fetch-pack.c | 11 ++-- merge-recursive.c | 14 ++---- read-cache.c | 30 +++++------ repo-settings.c | 75 ++++++++++++++++++++++++++++ repository.h | 39 +++++++++++++++ t/t1600-index.sh | 31 ++++++++++-- t/t5552-skipping-fetch-negotiator.sh | 23 --------- 23 files changed, 256 insertions(+), 119 deletions(-) create mode 100644 Documentation/config/feature.txt create mode 100644 repo-settings.c
base-commit: 9c9b961d7eb15fb583a2a812088713a68a85f1c0
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-292%2Fderrickstolee%2Frepo-settings%2Fhead-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-292/derrickstolee/repo-settings/head-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/292
Range-diff vs v2:
1: 597ab7d621 ! 1: 4d0db57ecb repo-settings: consolidate some config settings
@@ -30,14 +30,6 @@
diff --git a/builtin/gc.c b/builtin/gc.c
--- a/builtin/gc.c
+++ b/builtin/gc.c
-@@
- #include "pack-objects.h"
- #include "blob.h"
- #include "tree.h"
-+#include "repo-settings.h"
-
- #define FAILED_RUN "failed to run %s"
-
@@
static int aggressive_window = 250;
static int gc_auto_threshold = 6700;
@@ -75,14 +67,6 @@
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
-@@
- #include "dir.h"
- #include "midx.h"
- #include "trace2.h"
-+#include "repo-settings.h"
-
- #define IN_PACK(obj) oe_in_pack(&to_pack, obj)
- #define SIZE(obj) oe_size(&to_pack, obj)
@@
use_bitmap_index_default = git_config_bool(k, v);
return 0;
@@ -109,14 +93,6 @@
diff --git a/commit-graph.c b/commit-graph.c
--- a/commit-graph.c
+++ b/commit-graph.c
-@@
- #include "hashmap.h"
- #include "replace-object.h"
- #include "progress.h"
-+#include "repo-settings.h"
-
- #define GRAPH_SIGNATURE 0x43475048 /* "CGPH" */
- #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
@@
static int prepare_commit_graph(struct repository *r)
{
@@ -142,14 +118,6 @@
diff --git a/read-cache.c b/read-cache.c
--- a/read-cache.c
+++ b/read-cache.c
-@@
- #include "fsmonitor.h"
- #include "thread-utils.h"
- #include "progress.h"
-+#include "repo-settings.h"
-
- /* Mask for the name length in ce_flags in the on-disk index */
-
@@
#define INDEX_FORMAT_DEFAULT 3
@@ -188,15 +156,14 @@
+++ b/repo-settings.c
@@
+#include "cache.h"
-+#include "repository.h"
+#include "config.h"
-+#include "repo-settings.h"
++#include "repository.h"
+
+void prepare_repo_settings(struct repository *r)
+{
+ int value;
+
-+ if (r->settings_initialized)
++ if (r->settings.initialized)
+ return;
+
+ /* Defaults */
@@ -212,54 +179,50 @@
+
+ if (!repo_config_get_bool(r, "pack.usesparse", &value))
+ r->settings.pack_use_sparse = value;
-+
-+ r->settings_initialized = 1;
+}
- diff --git a/repo-settings.h b/repo-settings.h
- new file mode 100644
- --- /dev/null
- +++ b/repo-settings.h
-@@
-+#ifndef REPO_SETTINGS_H
-+#define REPO_SETTINGS_H
-+
-+struct repo_settings {
-+ int core_commit_graph;
-+ int gc_write_commit_graph;
-+
-+ int index_version;
-+
-+ int pack_use_sparse;
-+};
-+
-+struct repository;
-+
-+void prepare_repo_settings(struct repository *r);
-+
-+#endif /* REPO_SETTINGS_H */
-
diff --git a/repository.h b/repository.h
--- a/repository.h
+++ b/repository.h
@@
- #define REPOSITORY_H
-
#include "path.h"
-+#include "repo-settings.h"
struct config_set;
+struct repo_settings;
struct git_hash_algo;
struct index_state;
struct lock_file;
+@@
+ struct raw_object_store;
+ struct submodule_cache;
+
++struct repo_settings {
++ int initialized;
++
++ int core_commit_graph;
++ int gc_write_commit_graph;
++
++ int index_version;
++
++ int pack_use_sparse;
++};
++
+ struct repository {
+ /* Environment */
+ /*
@@
*/
char *submodule_prefix;
-+ int settings_initialized;
+ struct repo_settings settings;
+
/* Subsystems */
/*
* Repository's config which contains key-value pairs from the usual
+@@
+ */
+ void repo_update_index_if_able(struct repository *, struct lock_file *);
+
++void prepare_repo_settings(struct repository *r);
+
+ #endif /* REPOSITORY_H */
2: 86380c7832 ! 2: c0129066a0 repo-settings: add feature.manyCommits setting
@@ -81,7 +81,7 @@
+++ b/repo-settings.c
@@
#include "config.h"
- #include "repo-settings.h"
+ #include "repository.h"
+#define UPDATE_DEFAULT(s,v) do { if (s == -1) { s = v; } } while(0)
+
@@ -89,13 +89,12 @@
{
int value;
@@
+
if (!repo_config_get_bool(r, "pack.usesparse", &value))
r->settings.pack_use_sparse = value;
-
++
+ if (!repo_config_get_bool(r, "feature.manycommits", &value) && value) {
+ UPDATE_DEFAULT(r->settings.core_commit_graph, 1);
+ UPDATE_DEFAULT(r->settings.gc_write_commit_graph, 1);
+ }
-+
- r->settings_initialized = 1;
}
3: 49be7a7345 ! 3: b0a2d0e188 repo-settings: parse core.untrackedCache
@@ -25,14 +25,6 @@
diff --git a/builtin/update-index.c b/builtin/update-index.c
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
-@@
- #include "dir.h"
- #include "split-index.h"
- #include "fsmonitor.h"
-+#include "repo-settings.h"
-
- /*
- * Default to not allowing changes to the list of files. The
@@
struct parse_opt_ctx_t ctx;
strbuf_getline_fn getline_fn;
@@ -141,7 +133,7 @@
int value;
+ char *strval;
- if (r->settings_initialized)
+ if (r->settings.initialized)
return;
@@
@@ -168,22 +160,21 @@
UPDATE_DEFAULT(r->settings.core_commit_graph, 1);
UPDATE_DEFAULT(r->settings.gc_write_commit_graph, 1);
}
-
++
+ /* Hack for test programs like test-dump-untracked-cache */
+ if (ignore_untracked_cache_config)
+ r->settings.core_untracked_cache = UNTRACKED_CACHE_KEEP;
+ else
+ UPDATE_DEFAULT(r->settings.core_untracked_cache, UNTRACKED_CACHE_KEEP);
+
- r->settings_initialized = 1;
}
- diff --git a/repo-settings.h b/repo-settings.h
- --- a/repo-settings.h
- +++ b/repo-settings.h
+ diff --git a/repository.h b/repository.h
+ --- a/repository.h
+ +++ b/repository.h
@@
- #ifndef REPO_SETTINGS_H
- #define REPO_SETTINGS_H
+ struct raw_object_store;
+ struct submodule_cache;
+enum untracked_cache_setting {
+ UNTRACKED_CACHE_UNSET = -1,
@@ -193,7 +184,9 @@
+};
+
struct repo_settings {
- int core_commit_graph;
+ int initialized;
+
+@@
int gc_write_commit_graph;
int index_version;
4: 86a5a0c589 = 4: 0d4774d45f repo-settings: create feature.manyFiles setting
5: b8a631683a ! 5: 2e153fac22 repo-settings: create feature.experimental setting
@@ -93,18 +93,6 @@
pack.writeBitmaps (deprecated)::
This is a deprecated synonym for `repack.writeBitmaps`.
- diff --git a/builtin/am.c b/builtin/am.c
- --- a/builtin/am.c
- +++ b/builtin/am.c
-@@
- #include "string-list.h"
- #include "packfile.h"
- #include "repository.h"
-+#include "repo-settings.h"
-
- /**
- * Returns the length of the first line of msg.
-
diff --git a/fetch-negotiator.c b/fetch-negotiator.c
--- a/fetch-negotiator.c
+++ b/fetch-negotiator.c
@@ -113,7 +101,6 @@
#include "negotiator/default.h"
#include "negotiator/skipping.h"
+#include "repository.h"
-+#include "repo-settings.h"
-void fetch_negotiator_init(struct fetch_negotiator *negotiator,
- const char *algorithm)
@@ -230,14 +217,6 @@
diff --git a/merge-recursive.c b/merge-recursive.c
--- a/merge-recursive.c
+++ b/merge-recursive.c
-@@
- #include "submodule.h"
- #include "revision.h"
- #include "commit-reach.h"
-+#include "repo-settings.h"
-
- struct path_hashmap_entry {
- struct hashmap_entry e;
@@
opt->merge_detect_rename = git_config_rename("merge.renames", value);
free(value);
@@ -307,13 +286,11 @@
+ UPDATE_DEFAULT(r->settings.merge_directory_renames, MERGE_DIRECTORY_RENAMES_CONFLICT);
+ UPDATE_DEFAULT(r->settings.fetch_negotiation_algorithm, FETCH_NEGOTIATION_DEFAULT);
-+
- r->settings_initialized = 1;
}
- diff --git a/repo-settings.h b/repo-settings.h
- --- a/repo-settings.h
- +++ b/repo-settings.h
+ diff --git a/repository.h b/repository.h
+ --- a/repository.h
+ +++ b/repository.h
@@
UNTRACKED_CACHE_WRITE = 2
};
@@ -333,8 +310,8 @@
+};
+
struct repo_settings {
- int core_commit_graph;
- int gc_write_commit_graph;
+ int initialized;
+
@@
enum untracked_cache_setting core_untracked_cache;
@@ -343,7 +320,7 @@
+ enum fetch_negotiation_setting fetch_negotiation_algorithm;
};
- struct repository;
+ struct repository {
diff --git a/t/t5552-skipping-fetch-negotiator.sh b/t/t5552-skipping-fetch-negotiator.sh
--- a/t/t5552-skipping-fetch-negotiator.shSignature · 1 line
gitgitgadget
PATCH v3 1/5 repo-settings: consolidate some config settings
From: Derrick Stolee <[email protected]>
There are a few important config settings that are not loaded during git_default_config. These are instead loaded on-demand.
Centralize these config options to a single scan, and store all of the values in a repo_settings struct. The values for each setting are initialized as negative to indicate "unset".
This centralization will be particularly important in a later change to introduce "meta" config settings that change the defaults for these config settings.
--- Makefile | 1 + builtin/gc.c | 12 +++++------- builtin/pack-objects.c | 8 ++++---- commit-graph.c | 6 +++--- read-cache.c | 11 ++++++----- repo-settings.c | 25 +++++++++++++++++++++++++ repository.h | 15 +++++++++++++++ 7 files changed, 59 insertions(+), 19 deletions(-) create mode 100644 repo-settings.c
Makefile +1 -0
@@ -964,6 +964,7 @@ LIB_OBJS += refspec.o LIB_OBJS += ref-filter.o LIB_OBJS += remote.o LIB_OBJS += replace-object.o +LIB_OBJS += repo-settings.o LIB_OBJS += repository.o LIB_OBJS += rerere.o LIB_OBJS += resolve-undo.o
builtin/gc.c +5 -7
@@ -41,7 +41,6 @@ static int aggressive_depth = 50;static int aggressive_window =250 ;static int gc_auto_threshold =6700 ;static int gc_auto_pack_limit =50 ; -static int gc_write_commit_graph;static int detach_auto =1 ;static timestamp_t gc_log_expire_time;static const char *gc_log_expire ="1.day.ago" ;
@@ -148,7 +147,6 @@ static void gc_config(void)git_config_get_int ("gc.aggressivedepth" , &aggressive_depth);git_config_get_int ("gc.auto" , &gc_auto_threshold);git_config_get_int ("gc.autopacklimit" , &gc_auto_pack_limit); -git_config_get_bool ("gc.writecommitgraph" , &gc_write_commit_graph);git_config_get_bool ("gc.autodetach" , &detach_auto);git_config_get_expiry ("gc.pruneexpire" , &prune_expire);git_config_get_expiry ("gc.worktreepruneexpire" , &prune_worktrees_expire);
@@ -685,11 +683,11 @@ int cmd_gc(int argc, const char **argv, const char *prefix)clean_pack_garbage (); } -if (gc_write_commit_graph && -write_commit_graph_reachable (get_object_directory (), - !quiet && !daemonized ?COMMIT_GRAPH_PROGRESS :0 , -NULL )) -return 1 ; +prepare_repo_settings (the_repository); +if (the_repository->settings.gc_write_commit_graph ==1 ) +write_commit_graph_reachable (get_object_directory (), + !quiet && !daemonized ?COMMIT_GRAPH_PROGRESS :0 , +NULL );if (auto_gc &&too_many_loose_objects ()) warning(_("There are too many unreachable loose objects; "
builtin/pack-objects.c +4 -4
@@ -2709,10 +2709,6 @@ static int git_pack_config(const char *k, const char *v, void *cb) use_bitmap_index_default =git_config_bool (k, v);return 0 ; } -if (!strcmp (k,"pack.usesparse" )) { - sparse =git_config_bool (k, v); -return 0 ; - }if (!strcmp (k,"pack.threads" )) { delta_search_threads =git_config_int (k, v);if (delta_search_threads <0 )
@@ -3332,6 +3328,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) read_replace_refs =0 ; sparse =git_env_bool ("GIT_TEST_PACK_SPARSE" ,0 ); +prepare_repo_settings (the_repository); +if (!sparse && the_repository->settings.pack_use_sparse !=-1 ) + sparse = the_repository->settings.pack_use_sparse; +reset_pack_idx_option (&pack_idx_opts);git_config (git_pack_config,NULL );
commit-graph.c +3 -3
@@ -466,7 +466,6 @@ static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)static int prepare_commit_graph (struct repository *r) {struct object_directory *odb; -int config_value;if (git_env_bool (GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD ,0 )) die("dying as requested by the '%s' variable on commit-graph load!",
@@ -476,9 +475,10 @@ static int prepare_commit_graph(struct repository *r)return !!r->objects->commit_graph; r->objects->commit_graph_attempted =1 ; +prepare_repo_settings (r); +if (!git_env_bool (GIT_TEST_COMMIT_GRAPH ,0 ) && - (repo_config_get_bool (r,"core.commitgraph" , &config_value) || - !config_value)) + r->settings.core_commit_graph !=1 )/* * This repository is not configured to use commit graphs, so * do not load one. (But report commit_graph_attempted anyway
read-cache.c +6 -5
@@ -1599,16 +1599,17 @@ struct cache_entry *refresh_cache_entry(struct index_state *istate,#define INDEX_FORMAT_DEFAULT 3 -static unsigned int get_index_format_default (void ) +static unsigned int get_index_format_default (struct repository *r) {char *envversion =getenv ("GIT_INDEX_VERSION" );char *endp; -int value;unsigned int version =INDEX_FORMAT_DEFAULT ;if (!envversion) { -if (!git_config_get_int ("index.version" , &value)) - version = value; +prepare_repo_settings (r); + +if (r->settings.index_version >=0 ) + version = r->settings.index_version;if (version <INDEX_FORMAT_LB ||INDEX_FORMAT_UB < version) {warning (_ ("index.version set, but the value is invalid.\n" "Using version %i" ),INDEX_FORMAT_DEFAULT );
@@ -2765,7 +2766,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, }if (!istate->version) { - istate->version =get_index_format_default (); + istate->version =get_index_format_default (the_repository);if (git_env_bool ("GIT_TEST_SPLIT_INDEX" ,0 ))init_split_index (istate); }
repo-settings.c new file +25 -0
@@ -0,0 +1,25 @@ +#include "cache.h" +#include "config.h" +#include "repository.h" + +void prepare_repo_settings (struct repository *r) +{ +int value; + +if (r->settings.initialized) +return ; + +/* Defaults */ +memset (&r->settings,-1 ,sizeof (r->settings)); + +if (!repo_config_get_bool (r,"core.commitgraph" , &value)) + r->settings.core_commit_graph = value; +if (!repo_config_get_bool (r,"gc.writecommitgraph" , &value)) + r->settings.gc_write_commit_graph = value; + +if (!repo_config_get_bool (r,"index.version" , &value)) + r->settings.index_version = value; + +if (!repo_config_get_bool (r,"pack.usesparse" , &value)) + r->settings.pack_use_sparse = value; +}
repository.h +15 -0
@@ -4,6 +4,7 @@#include "path.h" struct config_set ; +struct repo_settings ;struct git_hash_algo ;struct index_state ;struct lock_file ;
@@ -11,6 +12,17 @@ struct pathspec;struct raw_object_store ;struct submodule_cache ; +struct repo_settings { +int initialized; + +int core_commit_graph; +int gc_write_commit_graph; + +int index_version; + +int pack_use_sparse; +}; +struct repository {/* Environment */ /*
@@ -72,6 +84,8 @@ struct repository {*/ char *submodule_prefix; +struct repo_settings settings; +/* Subsystems */ /* * Repository's config which contains key-value pairs from the usual
@@ -157,5 +171,6 @@ int repo_read_index_unmerged(struct repository *);*/ void repo_update_index_if_able (struct repository *,struct lock_file *); +void prepare_repo_settings (struct repository *r);#endif /* REPOSITORY_H */
Signature · 1 line
gitgitgadget
Re: [PATCH v3 1/5] repo-settings: consolidate some config settings
repository.h line 7
Quoted 9 lines · Derrick Stolee via GitGitGadget
repository.h +1
-0@@ -4,6 +4,7 @@#include "path.h" struct config_set ; +struct repo_settings ;
Given that the next hunk you introduce the real thing, and nobody refers to it until then, I do not see why we want to have a forward declaration.
Quoted 37 lines
repository.h +14
-0struct git_hash_algo ;struct index_state ;struct lock_file ;@@ -11,6 +12,17 @@ struct pathspec;struct raw_object_store ;struct submodule_cache ; +struct repo_settings { +int initialized; + +int core_commit_graph; +int gc_write_commit_graph; + +int index_version; + +int pack_use_sparse; +}; +struct repository {/* Environment */ /* @@ -72,6 +84,8 @@ struct repository {*/ char *submodule_prefix; +struct repo_settings settings; +/* Subsystems */ /* * Repository's config which contains key-value pairs from the usual @@ -157,5 +171,6 @@ int repo_read_index_unmerged(struct repository *);*/ void repo_update_index_if_able (struct repository *,struct lock_file *); +void prepare_repo_settings (struct repository *r);#endif /* REPOSITORY_H */
PATCH v3 2/5 repo-settings: add feature.manyCommits setting
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
When a repo has many commits, it is helpful to write and read the commit-graph file. Future changes to Git may include new config settings that are beneficial in this scenario.
Create the 'feature.manyCommits' config setting that changes the default values of 'core.commitGraph' and 'gc.writeCommitGraph' to true.
--- Documentation/config.txt | 2 ++ Documentation/config/core.txt | 3 ++- Documentation/config/feature.txt | 15 +++++++++++++++ Documentation/config/gc.txt | 4 ++-- repo-settings.c | 7 +++++++ 5 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 Documentation/config/feature.txt
Documentation/config.txt +2 -0
@@ -345,6 +345,8 @@ include::config/difftool.txt[] include::config/fastimport.txt[] +include::config/feature.txt[] + include::config/fetch.txt[] include::config/format.txt[]
Documentation/config/core.txt +2 -1
@@ -577,7 +577,8 @@ the `GIT_NOTES_REF` environment variable. See linkgit:git-notes[1]. core.commitGraph:: If true, then git will read the commit-graph file (if it exists) - to parse the graph structure of commits. Defaults to false. See + to parse the graph structure of commits. Defaults to false, unless + `feature.manyCommits` is enabled. See linkgit:git-commit-graph[1] for more information. core.useReplaceRefs::
Documentation/config/feature.txt new file +15 -0
@@ -0,0 +1,15 @@ +feature.*:: + The config settings that start with `feature.` modify the defaults of + a group of other config settings. These groups are created by the Git + developer community as recommended defaults and are subject to change. + In particular, new config options may be added with different defaults. + +feature.manyCommits:: + Enable config options that optimize for repos with many commits. This + setting is recommended for repos with at least 100,000 commits. The + new default values are: ++ +* `core.commitGraph=true` enables reading the commit-graph file. ++ +* `gc.writeCommitGraph=true` enables writing the commit-graph file during +garbage collection.
Documentation/config/gc.txt +2 -2
@@ -63,8 +63,8 @@ gc.writeCommitGraph:: If true, then gc will rewrite the commit-graph file when linkgit:git-gc[1] is run. When using `git gc --auto` the commit-graph will be updated if housekeeping is - required. Default is false. See linkgit:git-commit-graph[1] - for details. + required. Default is false, unless `feature.manyCommits` + is enabled. See linkgit:git-commit-graph[1] for details. gc.logExpiry:: If the file gc.log exists, then `git gc --auto` will print
repo-settings.c +7 -0
@@ -2,6 +2,8 @@#include "config.h" #include "repository.h" +#define UPDATE_DEFAULT (s,v) do { if (s == -1) { s = v; } } while(0) +void prepare_repo_settings (struct repository *r) {int value;
@@ -22,4 +24,9 @@ void prepare_repo_settings(struct repository *r)if (!repo_config_get_bool (r,"pack.usesparse" , &value)) r->settings.pack_use_sparse = value; + +if (!repo_config_get_bool (r,"feature.manycommits" , &value) && value) { +UPDATE_DEFAULT (r->settings.core_commit_graph,1 ); +UPDATE_DEFAULT (r->settings.gc_write_commit_graph,1 ); + } }
Signature · 1 line
gitgitgadget
Re: [PATCH v3 2/5] repo-settings: add feature.manyCommits setting
Documentation/config/feature.txt line 4
Quoted 11 lines · Derrick Stolee via GitGitGadget
Documentation/config/feature.txt new file +4
-0@@ -0,0 +1,15 @@ +feature.*:: +... +* `gc.writeCommitGraph=true` enables writing the commit-graph file during +garbage collection.
No newline at end of file
Quoted 20 lines
Documentation/config/gc.txt +2
-2@@ -63,8 +63,8 @@ gc.writeCommitGraph:: If true, then gc will rewrite the commit-graph file when linkgit:git-gc[1] is run. When using `git gc --auto` the commit-graph will be updated if housekeeping is - required. Default is false. See linkgit:git-commit-graph[1] - for details. + required. Default is false, unless `feature.manyCommits` + is enabled. See linkgit:git-commit-graph[1] for details. gc.logExpiry:: If the file gc.log exists, then `git gc --auto` will printrepo-settings.c +0
-0
repo-settings.c line 5
Quoted 4 lines
repo-settings.c +1
-0@@ -2,6 +2,8 @@#include "config.h" #include "repository.h" +#define UPDATE_DEFAULT (s,v) do { if (s == -1) { s = v; } } while(0)
A few points:
1. give 's' and 'v' a bit better name, perhaps 'slot' and 'value'?
2. "do { if ((s) == -1) { (s) = (v); } } while(0)"
3. When we learn to set default values for variables that are not boolean in the future, we will regret that we did not name it UPDATE_DEFAULT_BOOL(slot, value).
Re: [PATCH v3 2/5] repo-settings: add feature.manyCommits setting
Hi Junio,
Quoted 8 lines · Junio C Hamano
Quoted 1 line · Derrick Stolee via GitGitGadget
+#define
UPDATE_DEFAULT(s,v)do { if (s == -1) { s = v; } } while(0)[...]
3. When we learn to set default values for variables that are not boolean in the future, we will regret that we did not name itUPDATE_DEFAULT_BOOL(slot, value).
On the other hand, as we never promised any kind of API (and this is not even an internal API to begin with), it will be _easy_ to rename it in the unlikely event that we would ever introduce non-boolean defaults to override, wouldn't you agree?
We have plenty of precedent where patch series start by refactoring, whether it is to rename functions or variables or files or extracting functions. Preparing for a future that might never come strikes me as premature optimization.
Ciao,
Dscho
Re: [PATCH v3 2/5] repo-settings: add feature.manyCommits setting
Quoted 11 lines · Johannes Schindelin
Quoted 8 lines · Junio C Hamano
Quoted 1 line · Derrick Stolee via GitGitGadget
+#define
UPDATE_DEFAULT(s,v)do { if (s == -1) { s = v; } } while(0)[...]
3. When we learn to set default values for variables that are not boolean in the future, we will regret that we did not name itUPDATE_DEFAULT_BOOL(slot, value).
Quoted 4 lines
On the other hand, as we never promised any kind of API (and this is not even an internal API to begin with), it will be _easy_ to rename it in the unlikely event that we would ever introduce non-boolean defaults to override, wouldn't you agree?
I agree that it is easy to say that it is easy to rename it later and burden somebody else with the task.
I know that the renaming itself is easy, when you limit yourself within the scope of a single topic, whether done now or later. I also know that having to worry about other topics in flight has non-zero cost. I also know that you are not the one who will bear it---I will be.
So from my point of view, if we can make a prediction, even with limited knowledge that a name may need to be renamed in the future, it is better not pick such a name and instead use one that we think it has a better chance of surviving without needing a rename.
Re: [PATCH v3 2/5] repo-settings: add feature.manyCommits setting
In reply to Derrick Stolee via GitGitGadgetQuoted 5 lines · Derrick Stolee via GitGitGadget
Documentation/config/feature.txt new file +5
-0+feature.*:: + The config settings that start with `feature.` modify the defaults of + a group of other config settings. These groups are created by the Git + developer community as recommended defaults and are subject to change. + In particular, new config options may be added with different defaults.
Documentation/config/feature.txt line 15
Quoted 10 lines
Documentation/config/feature.txt new file +10
-0+ +feature.manyCommits:: + Enable config options that optimize for repos with many commits. This + setting is recommended for repos with at least 100,000 commits. The + new default values are: ++ +* `core.commitGraph=true` enables reading the commit-graph file. ++ +* `gc.writeCommitGraph=true` enables writing the commit-graph file during +garbage collection.
During the whole new commit graph format discussion (which has now landed) we discussed just auto toggling this:
https://public-inbox.org/git/[email protected]/
This looks fine, but have we backed out of simply enabling this at this point? I don't see why not, regardless of commit count...
Re: [PATCH v3 2/5] repo-settings: add feature.manyCommits setting
Quoted 23 lines · Ævar Arnfjörð Bjarmason
Quoted 5 lines · Derrick Stolee via GitGitGadget
Documentation/config/feature.txt new file +5
-0+feature.*:: + The config settings that start with `feature.` modify the defaults of + a group of other config settings. These groups are created by the Git + developer community as recommended defaults and are subject to change. + In particular, new config options may be added with different defaults.Documentation/config/feature.txt line 15
Quoted 10 lines
Documentation/config/feature.txt new file +10
-0+ +feature.manyCommits:: + Enable config options that optimize for repos with many commits. This + setting is recommended for repos with at least 100,000 commits. The + new default values are: ++ +* `core.commitGraph=true` enables reading the commit-graph file. ++ +* `gc.writeCommitGraph=true` enables writing the commit-graph file during +garbage collection.During the whole new commit graph format discussion (which has now landed) we discussed just auto toggling this:
https://public-inbox.org/git/[email protected]/
Quoted 2 lines
This looks fine, but have we backed out of simply enabling this at this point? I don't see why not, regardless of commit count...
I would be happy to drop feature.manyCommits and instead swap the defaults of core.commitGraph and gc.writeCommitGraph to true if we think that is what we want to do for 2.24.0. We can use the repo settings and UPDATE_DEFAULT[_BOOL] for this purpose.
Thanks,
-Stolee
PATCH v3 3/5 repo-settings: parse core.untrackedCache
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The core.untrackedCache config setting is slightly complicated, so clarify its use and centralize its parsing into the repo settings.
The default value is "keep" (returned as -1), which persists the untracked cache if it exists.
If the value is set as "false" (returned as 0), then remove the untracked cache if it exists.
If the value is set as "true" (returned as 1), then write the untracked cache and persist it.
Instead of relying on magic values of -1, 0, and 1, split these options into an enum. This allows the use of "-1" as a default value. After parsing the config options, if the value is unset we can initialize it to UNTRACKED_CACHE_KEEP.
--- builtin/update-index.c | 6 ++++-- config.c | 24 ------------------------ read-cache.c | 19 +++++++++---------- repo-settings.c | 22 +++++++++++++++++++++- repository.h | 8 ++++++++ 5 files changed, 42 insertions(+), 37 deletions(-)
builtin/update-index.c +4 -2
@@ -966,6 +966,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)struct parse_opt_ctx_t ctx;strbuf_getline_fn getline_fn;int parseopt_state =PARSE_OPT_UNKNOWN ; +struct repository *r = the_repository;struct option options[] = { OPT_BIT('q', NULL, &refresh_args.flags, N_("continue refresh even when index needs update"),
@@ -1180,11 +1181,12 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)remove_split_index (&the_index); } +prepare_repo_settings (r);switch (untracked_cache) {case UC_UNSPECIFIED :break ;case UC_DISABLE : -if (git_config_get_untracked_cache () ==1 ) +if (r->settings.core_untracked_cache ==UNTRACKED_CACHE_WRITE )warning (_ ("core.untrackedCache is set to true; " "remove or change it, if you really want to " "disable the untracked cache" ));
@@ -1196,7 +1198,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)return !test_if_untracked_cache_is_supported ();case UC_ENABLE :case UC_FORCE : -if (git_config_get_untracked_cache () ==0 ) +if (r->settings.core_untracked_cache ==UNTRACKED_CACHE_REMOVE )warning (_ ("core.untrackedCache is set to false; " "remove or change it, if you really want to " "enable the untracked cache" ));
config.c +0 -24
@@ -2277,30 +2277,6 @@ int git_config_get_expiry_in_days(const char *key, timestamp_t *expiry, timestamreturn -1 ;/* thing exists but cannot be parsed */ } -int git_config_get_untracked_cache (void ) -{ -int val =-1 ; -const char *v; - -/* Hack for test programs like test-dump-untracked-cache */ -if (ignore_untracked_cache_config) -return -1 ; - -if (!git_config_get_maybe_bool ("core.untrackedcache" , &val)) -return val; - -if (!git_config_get_value ("core.untrackedcache" , &v)) { -if (!strcasecmp (v,"keep" )) -return -1 ; - -error (_ ("unknown core.untrackedCache value '%s'; " -"using 'keep' default value" ), v); -return -1 ; - } - -return -1 ;/* default value */ -} -int git_config_get_split_index (void ) {int val;
read-cache.c +9 -10
@@ -1845,18 +1845,17 @@ static void check_ce_order(struct index_state *istate)static void tweak_untracked_cache (struct index_state *istate) { -switch (git_config_get_untracked_cache ()) { -case -1 :/* keep: do nothing */ -break ; -case 0 :/* false */ +struct repository *r = the_repository; + +prepare_repo_settings (r); + +if (r->settings.core_untracked_cache ==UNTRACKED_CACHE_REMOVE ) {remove_untracked_cache (istate); -break ; -case 1 :/* true */ -add_untracked_cache (istate); -break ; -default :/* unknown value: do nothing */ -break ; +return ; } + +if (r->settings.core_untracked_cache ==UNTRACKED_CACHE_WRITE ) +add_untracked_cache (istate); }static void tweak_split_index (struct index_state *istate)
repo-settings.c +21 -1
@@ -7,6 +7,7 @@void prepare_repo_settings (struct repository *r) {int value; +char *strval;if (r->settings.initialized)return ;
@@ -21,12 +22,31 @@ void prepare_repo_settings(struct repository *r)if (!repo_config_get_bool (r,"index.version" , &value)) r->settings.index_version = value; +if (!repo_config_get_maybe_bool (r,"core.untrackedcache" , &value)) { +if (value ==0 ) + r->settings.core_untracked_cache =UNTRACKED_CACHE_REMOVE ; +else + r->settings.core_untracked_cache =UNTRACKED_CACHE_WRITE ; + }else if (!repo_config_get_string (r,"core.untrackedcache" , &strval)) { +if (!strcasecmp (strval,"keep" )) + r->settings.core_untracked_cache =UNTRACKED_CACHE_KEEP ; + +free (strval); + } +if (!repo_config_get_bool (r,"pack.usesparse" , &value)) r->settings.pack_use_sparse = value; - +if (!repo_config_get_bool (r,"feature.manycommits" , &value) && value) {UPDATE_DEFAULT (r->settings.core_commit_graph,1 );UPDATE_DEFAULT (r->settings.gc_write_commit_graph,1 ); } + +/* Hack for test programs like test-dump-untracked-cache */ +if (ignore_untracked_cache_config) + r->settings.core_untracked_cache =UNTRACKED_CACHE_KEEP ; +else +UPDATE_DEFAULT (r->settings.core_untracked_cache,UNTRACKED_CACHE_KEEP ); + }
repository.h +8 -0
@@ -12,6 +12,13 @@ struct pathspec;struct raw_object_store ;struct submodule_cache ; +enum untracked_cache_setting { +UNTRACKED_CACHE_UNSET =-1 , +UNTRACKED_CACHE_REMOVE =0 , +UNTRACKED_CACHE_KEEP =1 , +UNTRACKED_CACHE_WRITE =2 +}; +struct repo_settings {int initialized;
@@ -19,6 +26,7 @@ struct repo_settings {int gc_write_commit_graph;int index_version; +enum untracked_cache_setting core_untracked_cache;int pack_use_sparse; };
Signature · 1 line
gitgitgadget
PATCH v3 4/5 repo-settings: create feature.manyFiles setting
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The feature.manyFiles setting is suitable for repos with many files in the working directory. By setting index.version=4 and core.untrackedCache=true, commands such as 'git status' should improve.
While adding this setting, modify the index version precedence tests to check how this setting overrides the default for index.version is unset.
--- Documentation/config/core.txt | 4 +++- Documentation/config/feature.txt | 12 +++++++++++- Documentation/config/index.txt | 1 + repo-settings.c | 4 ++++ t/t1600-index.sh | 31 ++++++++++++++++++++++++++----- 5 files changed, 45 insertions(+), 7 deletions(-)
Documentation/config/core.txt +3 -1
@@ -86,7 +86,9 @@ core.untrackedCache:: it will automatically be removed, if set to `false`. Before setting it to `true`, you should check that mtime is working properly on your system. - See linkgit:git-update-index[1]. `keep` by default. + See linkgit:git-update-index[1]. `keep` by default, unless + `feature.manyFiles` is enabled which sets this setting to + `true` by default. core.checkStat:: When missing or is set to `default`, many fields in the stat
Documentation/config/feature.txt +11 -1
@@ -12,4 +12,14 @@ feature.manyCommits:: * `core.commitGraph=true` enables reading the commit-graph file. + * `gc.writeCommitGraph=true` enables writing the commit-graph file during -garbage collection. +garbage collection. + +feature.manyFiles:: + Enable config options that optimize for repos with many files in the + working directory. With many files, commands such as `git status` and + `git checkout` may be slow and these new defaults improve performance: ++ +* `index.version=4` enables path-prefix compression in the index. ++ +* `core.untrackedCache=true` enables the untracked cache. This setting assumes +that mtime is working on your machine.
Documentation/config/index.txt +1 -0
@@ -24,3 +24,4 @@ index.threads:: index.version:: Specify the version with which new index files should be initialized. This does not affect existing repositories. + If `feature.manyFiles` is enabled, then the default is 4.
repo-settings.c +4 -0
@@ -42,6 +42,10 @@ void prepare_repo_settings(struct repository *r)UPDATE_DEFAULT (r->settings.core_commit_graph,1 );UPDATE_DEFAULT (r->settings.gc_write_commit_graph,1 ); } +if (!repo_config_get_bool (r,"feature.manyfiles" , &value) && value) { +UPDATE_DEFAULT (r->settings.index_version,4 ); +UPDATE_DEFAULT (r->settings.core_untracked_cache,UNTRACKED_CACHE_WRITE ); + }/* Hack for test programs like test-dump-untracked-cache */ if (ignore_untracked_cache_config)
t/t1600-index.sh +26 -5
@@ -59,17 +59,38 @@ test_expect_success 'out of bounds index.version issues warning' ' ) ' -test_expect_success 'GIT_INDEX_VERSION takes precedence over config' ' +test_index_version () { + INDEX_VERSION_CONFIG=$1 && + FEATURE_MANY_FILES=$2 && + ENV_VAR_VERSION=$3 + EXPECTED_OUTPUT_VERSION=$4 && ( rm -f .git/index && - GIT_INDEX_VERSION=4 && - export GIT_INDEX_VERSION && - git config --add index.version 2 && + rm -f .git/config && + if test "$INDEX_VERSION_CONFIG" -ne 0 + then + git config --add index.version $INDEX_VERSION_CONFIG + fi && + git config --add feature.manyFiles $FEATURE_MANY_FILES + if test "$ENV_VAR_VERSION" -ne 0 + then + GIT_INDEX_VERSION=$ENV_VAR_VERSION && + export GIT_INDEX_VERSION + else + unset GIT_INDEX_VERSION + fi && git add a 2>&1 && - echo 4 >expect && + echo $EXPECTED_OUTPUT_VERSION >expect && test-tool index-version <.git/index >actual && test_cmp expect actual ) +} + +test_expect_success 'index version config precedence' ' + test_index_version 2 false 4 4 && + test_index_version 2 true 0 2 && + test_index_version 0 true 0 4 && + test_index_version 0 true 2 2 ' test_done
Signature · 1 line
gitgitgadget
PATCH v3 5/5 repo-settings: create feature.experimental setting
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The 'feature.experimental' setting includes config options that are not committed to become defaults, but could use additional testing.
Update the following config settings to take new defaults, and to use the repo_settings struct if not already using them:
* 'pack.useSparse=true'
* 'merge.directoryRenames=true'
* 'fetch.negotiationAlgorithm=skipping'
In the case of fetch.negotiationAlgorithm, the existing logic would load the config option only when about to use the setting, so had a die() statement on an unknown string value. This is removed as now the config is parsed under prepare_repo_settings(). In general, this die() is probably misplaced and not valuable. A test was removed that checked this die() statement executed.
--- Documentation/config/feature.txt | 17 +++++++++++++++++ Documentation/config/fetch.txt | 3 ++- Documentation/config/merge.txt | 3 ++- Documentation/config/pack.txt | 3 ++- fetch-negotiator.c | 25 +++++++++++++------------ fetch-negotiator.h | 5 +++-- fetch-pack.c | 11 +++++------ merge-recursive.c | 14 +++++--------- repo-settings.c | 19 +++++++++++++++++++ repository.h | 16 ++++++++++++++++ t/t5552-skipping-fetch-negotiator.sh | 23 ----------------------- 11 files changed, 84 insertions(+), 55 deletions(-)
Documentation/config/feature.txt +17 -0
@@ -4,6 +4,23 @@ feature.*:: developer community as recommended defaults and are subject to change. In particular, new config options may be added with different defaults. +feature.experimental:: + Enable config options that are new to Git, and are being considered for + future defaults. Config settings included here may be added or removed + with each release, including minor version updates. These settings may + have unintended interactions since they are so new. Please enable this + setting if you are interested in providing feedback on experimental + features. The new default values are: ++ +* `pack.useSparse=true` uses a new algorithm when constructing a pack-file +which can improve `git push` performance in repos with many files. ++ +* `merge.directoryRenames=true` uses a new algorithm for detecting renames by +using entire directories at a time instead of single files at a time. ++ +* `fetch.negotiationAlgorithm=skipping` may improve fetch negotiation times by +skipping more commits at a time, reducing the number of round trips. + feature.manyCommits:: Enable config options that optimize for repos with many commits. This setting is recommended for repos with at least 100,000 commits. The
Documentation/config/fetch.txt +2 -1
@@ -59,7 +59,8 @@ fetch.negotiationAlgorithm:: effort to converge faster, but may result in a larger-than-necessary packfile; The default is "default" which instructs Git to use the default algorithm that never skips commits (unless the server has acknowledged it or one - of its descendants). + of its descendants). If `feature.experimental` is enabled, then this + setting defaults to "skipping". Unknown values will cause 'git fetch' to error out. + See also the `--negotiation-tip` option for linkgit:git-fetch[1].
Documentation/config/merge.txt +2 -1
@@ -54,7 +54,8 @@ merge.directoryRenames:: moved into the new directory. If set to "conflict", a conflict will be reported for such paths. If merge.renames is false, merge.directoryRenames is ignored and treated as false. Defaults - to "conflict". + to "conflict" unless `feature.experimental` is enabled and the + default is "true". merge.renormalize:: Tell Git that canonical representation of files in the
Documentation/config/pack.txt +2 -1
@@ -112,7 +112,8 @@ pack.useSparse:: objects. This can have significant performance benefits when computing a pack to send a small change. However, it is possible that extra objects are added to the pack-file if the included - commits contain certain types of direct renames. + commits contain certain types of direct renames. Default is `false` + unless `feature.experimental` is enabled. pack.writeBitmaps (deprecated):: This is a deprecated synonym for `repack.writeBitmaps`.
fetch-negotiator.c +13 -12
@@ -2,19 +2,20 @@#include "fetch-negotiator.h" #include "negotiator/default.h" #include "negotiator/skipping.h" +#include "repository.h" -void fetch_negotiator_init (struct fetch_negotiator *negotiator, -const char *algorithm) +void fetch_negotiator_init (struct repository *r, +struct fetch_negotiator *negotiator) { -if (algorithm) { -if (!strcmp (algorithm,"skipping" )) { -skipping_negotiator_init (negotiator); -return ; - }else if (!strcmp (algorithm,"default" )) { -/* Fall through to default initialization */ - }else { -die ("unknown fetch negotiation algorithm '%s'" , algorithm); - } +prepare_repo_settings (r); +switch (r->settings.fetch_negotiation_algorithm) { +case FETCH_NEGOTIATION_SKIPPING : +skipping_negotiator_init (negotiator); +return ; + +case FETCH_NEGOTIATION_DEFAULT : +default : +default_negotiator_init (negotiator); +return ; } -default_negotiator_init (negotiator); }
fetch-negotiator.h +3 -2
@@ -2,6 +2,7 @@#define FETCH_NEGOTIATOR_H struct commit ; +struct repository ;/* * An object that supplies the information needed to negotiate the contents of
@@ -52,7 +53,7 @@ struct fetch_negotiator { void *data; }; -void fetch_negotiator_init (struct fetch_negotiator *negotiator, -const char *algorithm); +void fetch_negotiator_init (struct repository *r, +struct fetch_negotiator *negotiator);#endif
fetch-pack.c +5 -6
@@ -36,7 +36,6 @@ static int agent_supported;static int server_supports_filtering;static struct lock_file shallow_lock;static const char *alternate_shallow_file; -static char *negotiation_algorithm;static struct strbuf fsck_msg_types =STRBUF_INIT ;/* Remember to update object flag allocation in object.h */
@@ -892,12 +891,13 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,struct shallow_info *si, char **pack_lockfile) { + struct repository *r = the_repository;struct ref *ref =copy_ref_list (orig_ref);struct object_id oid;const char *agent_feature;int agent_len;struct fetch_negotiator negotiator; -fetch_negotiator_init (&negotiator, negotiation_algorithm); +fetch_negotiator_init (r, &negotiator);sort_ref_list (&ref, ref_compare_name);QSORT (sought, nr_sought, cmp_ref_by_name);
@@ -911,7 +911,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,if (server_supports ("shallow" ))print_verbose (args,_ ("Server supports %s" ),"shallow" ); -else if (args->depth >0 ||is_repository_shallow (the_repository)) +else if (args->depth >0 ||is_repository_shallow (r))die (_ ("Server does not support shallow clients" ));if (args->depth >0 || args->deepen_since || args->deepen_not) args->deepen =1 ;
@@ -1379,6 +1379,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,struct shallow_info *si, char **pack_lockfile) { + struct repository *r = the_repository;struct ref *ref =copy_ref_list (orig_ref);enum fetch_state state =FETCH_CHECK_LOCAL ;struct oidset common =OIDSET_INIT ;
@@ -1386,7 +1387,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,int in_vain =0 ;int haves_to_send =INITIAL_FLUSH ;struct fetch_negotiator negotiator; -fetch_negotiator_init (&negotiator, negotiation_algorithm); +fetch_negotiator_init (r, &negotiator);packet_reader_init (&reader, fd[0 ],NULL ,0 ,PACKET_READ_CHOMP_NEWLINE |PACKET_READ_DIE_ON_ERR_PACKET );
@@ -1505,8 +1506,6 @@ static void fetch_pack_config(void)git_config_get_bool ("repack.usedeltabaseoffset" , &prefer_ofs_delta);git_config_get_bool ("fetch.fsckobjects" , &fetch_fsck_objects);git_config_get_bool ("transfer.fsckobjects" , &transfer_fsck_objects); -git_config_get_string ("fetch.negotiationalgorithm" , - &negotiation_algorithm);git_config (fetch_pack_config_cb,NULL ); }
merge-recursive.c +5 -9
@@ -3662,15 +3662,6 @@ static void merge_recursive_config(struct merge_options *opt) opt->merge_detect_rename =git_config_rename ("merge.renames" , value);free (value); } -if (!git_config_get_string ("merge.directoryrenames" , &value)) { -int boolval =git_parse_maybe_bool (value); -if (0 <= boolval) { - opt->detect_directory_renames = boolval ?2 :0 ; - }else if (!strcasecmp (value,"conflict" )) { - opt->detect_directory_renames =1 ; - }/* avoid erroring on values from future versions of git */ -free (value); - }git_config (git_xmerge_config,NULL ); }
@@ -3688,6 +3679,11 @@ void init_merge_options(struct merge_options *opt, opt->diff_detect_rename =-1 ; opt->merge_detect_rename =-1 ; opt->detect_directory_renames =1 ; + +prepare_repo_settings (repo); +if (repo->settings.merge_directory_renames >=0 ) + opt->detect_directory_renames = repo->settings.merge_directory_renames; +merge_recursive_config (opt); merge_verbosity =getenv ("GIT_MERGE_VERBOSITY" );if (merge_verbosity)
repo-settings.c +19 -0
@@ -34,6 +34,18 @@ void prepare_repo_settings(struct repository *r)free (strval); } +if (!repo_config_get_maybe_bool (r,"merge.directoryrenames" , &value)) + r->settings.merge_directory_renames = value ?MERGE_DIRECTORY_RENAMES_TRUE :0 ; +else if (!repo_config_get_string (r,"merge.directoryrenames" , &strval)) { +if (!strcasecmp (strval,"conflict" )) + r->settings.merge_directory_renames =MERGE_DIRECTORY_RENAMES_CONFLICT ; + } +if (!repo_config_get_string (r,"fetch.negotiationalgorithm" , &strval)) { +if (!strcasecmp (strval,"skipping" )) + r->settings.fetch_negotiation_algorithm =FETCH_NEGOTIATION_SKIPPING ; +else + r->settings.fetch_negotiation_algorithm =FETCH_NEGOTIATION_DEFAULT ; + }if (!repo_config_get_bool (r,"pack.usesparse" , &value)) r->settings.pack_use_sparse = value;
@@ -46,6 +58,11 @@ void prepare_repo_settings(struct repository *r)UPDATE_DEFAULT (r->settings.index_version,4 );UPDATE_DEFAULT (r->settings.core_untracked_cache,UNTRACKED_CACHE_WRITE ); } +if (!repo_config_get_bool (r,"feature.experimental" , &value) && value) { +UPDATE_DEFAULT (r->settings.pack_use_sparse,1 ); +UPDATE_DEFAULT (r->settings.merge_directory_renames,MERGE_DIRECTORY_RENAMES_TRUE ); +UPDATE_DEFAULT (r->settings.fetch_negotiation_algorithm,FETCH_NEGOTIATION_SKIPPING ); + }/* Hack for test programs like test-dump-untracked-cache */ if (ignore_untracked_cache_config)
@@ -53,4 +70,6 @@ void prepare_repo_settings(struct repository *r)else UPDATE_DEFAULT (r->settings.core_untracked_cache,UNTRACKED_CACHE_KEEP ); +UPDATE_DEFAULT (r->settings.merge_directory_renames,MERGE_DIRECTORY_RENAMES_CONFLICT ); +UPDATE_DEFAULT (r->settings.fetch_negotiation_algorithm,FETCH_NEGOTIATION_DEFAULT ); }
repository.h +16 -0
@@ -19,6 +19,20 @@ enum untracked_cache_setting {UNTRACKED_CACHE_WRITE =2 }; +enum merge_directory_renames_setting { +MERGE_DIRECTORY_RENAMES_UNSET =-1 , +MERGE_DIRECTORY_RENAMES_NONE =0 , +MERGE_DIRECTORY_RENAMES_CONFLICT =1 , +MERGE_DIRECTORY_RENAMES_TRUE =2 , +}; + +enum fetch_negotiation_setting { +FETCH_NEGOTIATION_UNSET =-1 , +FETCH_NEGOTIATION_NONE =0 , +FETCH_NEGOTIATION_DEFAULT =1 , +FETCH_NEGOTIATION_SKIPPING =2 , +}; +struct repo_settings {int initialized;
@@ -29,6 +43,8 @@ struct repo_settings {enum untracked_cache_setting core_untracked_cache;int pack_use_sparse; +enum merge_directory_renames_setting merge_directory_renames; +enum fetch_negotiation_setting fetch_negotiation_algorithm; };struct repository {
t/t5552-skipping-fetch-negotiator.sh +0 -23
@@ -60,29 +60,6 @@ test_expect_success 'commits with no parents are sent regardless of skip distanc have_not_sent c6 c4 c3 ' -test_expect_success 'unknown fetch.negotiationAlgorithm values error out' ' - rm -rf server client trace && - git init server && - test_commit -C server to_fetch && - - git init client && - test_commit -C client on_client && - git -C client checkout on_client && - - test_config -C client fetch.negotiationAlgorithm invalid && - test_must_fail git -C client fetch "$(pwd)/server" 2>err && - test_i18ngrep "unknown fetch negotiation algorithm" err && - - # Explicit "default" value - test_config -C client fetch.negotiationAlgorithm default && - git -C client -c fetch.negotiationAlgorithm=default fetch "$(pwd)/server" && - - # Implementation detail: If there is nothing to fetch, we will not error out - test_config -C client fetch.negotiationAlgorithm invalid && - git -C client fetch "$(pwd)/server" 2>err && - test_i18ngrep ! "unknown fetch negotiation algorithm" err -' - test_expect_success 'when two skips collide, favor the larger one' ' rm -rf server client trace && git init server &&
Signature · 1 line
gitgitgadget
Re: [PATCH v3 5/5] repo-settings: create feature.experimental setting
Sorry for the late reply...
Documentation/config/merge.txt merge.directoryRenames:: · line 58
Quoted 10 lines · Derrick Stolee via GitGitGadget
Documentation/config/merge.txt +2
-1@@ -54,7 +54,8 @@ merge.directoryRenames:: moved into the new directory. If set to "conflict", a conflict will be reported for such paths. If merge.renames is false, merge.directoryRenames is ignored and treated as false. Defaults - to "conflict". + to "conflict" unless `feature.experimental` is enabled and the + default is "true".
I have a hard time parsing that changed sentence. Perhaps something like:
...unless feature.experimental is enabled in which case the
default is "true".
?
...
repo-settings.c void prepare_repo_settings(struct repository *r) · line 38
Quoted 10 lines
repo-settings.c +2
-0@@ -34,6 +34,18 @@ void prepare_repo_settings(struct repository *r)free (strval); } +if (!repo_config_get_maybe_bool (r,"merge.directoryrenames" , &value)) + r->settings.merge_directory_renames = value ?MERGE_DIRECTORY_RENAMES_TRUE :0 ;
Shouldn't that be "MERGE_DIRECTORY_RENAMES_NONE" instead of "0"?
Quoted 5 lines
repository.h +0
-0
repository.h enum untracked_cache_setting { · line 27
Quoted 9 lines
repository.h +6
-0@@ -19,6 +19,20 @@ enum untracked_cache_setting { UNTRACKED_CACHE_WRITE = 2 }; +enum merge_directory_renames_setting { +MERGE_DIRECTORY_RENAMES_UNSET =-1 , +MERGE_DIRECTORY_RENAMES_NONE =0 , +MERGE_DIRECTORY_RENAMES_CONFLICT =1 , +MERGE_DIRECTORY_RENAMES_TRUE =2 , +};
Thanks for adding these; makes things much nicer. :-)
Cheers,
Elijah
Re: [PATCH v3 5/5] repo-settings: create feature.experimental setting
Quoted 42 lines · Elijah Newren
Sorry for the late reply...
Documentation/config/merge.txt merge.directoryRenames:: · line 58
Quoted 10 lines · Derrick Stolee via GitGitGadget
Documentation/config/merge.txt +2
-1@@ -54,7 +54,8 @@ merge.directoryRenames:: moved into the new directory. If set to "conflict", a conflict will be reported for such paths. If merge.renames is false, merge.directoryRenames is ignored and treated as false. Defaults - to "conflict". + to "conflict" unless `feature.experimental` is enabled and the + default is "true".I have a hard time parsing that changed sentence. Perhaps something like:
...unlessfeature.experimentalis enabled in which case the
default is "true".
?...
repo-settings.c void prepare_repo_settings(struct repository *r) · line 38
Quoted 10 lines
repo-settings.c +2
-0@@ -34,6 +34,18 @@ void prepare_repo_settings(struct repository *r)free (strval); } +if (!repo_config_get_maybe_bool (r,"merge.directoryrenames" , &value)) + r->settings.merge_directory_renames = value ?MERGE_DIRECTORY_RENAMES_TRUE :0 ;Shouldn't that be "
MERGE_DIRECTORY_RENAMES_NONE" instead of "0"?Quoted 8 lines
repository.h +0
-0@@ -19,6 +19,20 @@ enum untracked_cache_setting { UNTRACKED_CACHE_WRITE = 2 };
Quoted 8 lines
repository.h enum untracked_cache_setting { · line 27
Quoted 6 lines
repository.h +6
-0+enum merge_directory_renames_setting { +MERGE_DIRECTORY_RENAMES_UNSET =-1 , +MERGE_DIRECTORY_RENAMES_NONE =0 , +MERGE_DIRECTORY_RENAMES_CONFLICT =1 , +MERGE_DIRECTORY_RENAMES_TRUE =2 , +};Thanks for adding these; makes things much nicer. :-)
Thanks for the feedback! It's just in time for me to send a v4.
-Stolee
Re: [PATCH v3 5/5] repo-settings: create feature.experimental setting
In reply to Elijah NewrenQuoted 6 lines · Elijah Newren
Quoted 6 lines
Documentation/config/merge.txt +0
-0@@ -54,7 +54,8 @@ merge.directoryRenames:: moved into the new directory. If set to "conflict", a conflict will be reported for such paths. If merge.renames is false, merge.directoryRenames is ignored and treated as false. Defaults
Quoted 8 lines
Documentation/config/merge.txt merge.directoryRenames:: · line 58
Quoted 3 lines
Documentation/config/merge.txt +2
-1- to "conflict". + to "conflict" unless `feature.experimental` is enabled and the + default is "true".I have a hard time parsing that changed sentence. Perhaps something like:
...unlessfeature.experimentalis enabled in which case the
default is "true".
?
That reads better.
But I am not sure about the wisdom of controlling between conflict and true with this feature macro in the first place.
Between "conflict" and "true", the former forces the end user to verify (or allows the end user to veto) the auto resolution by the heuristics and is always a safer if more cumbersome option. It's not like blindly trusting the directory rename heuristics is the bright future for all users, is it?
I would not set rerere.autoUpdate to true when feature.experimental is set; for exactly the same reason, I do not find it reasonable to set this to true with feature.experimental macro.
Quoted 34 lines
...
repo-settings.c void prepare_repo_settings(struct repository *r) · line 38
Quoted 10 lines
repo-settings.c +2
-0@@ -34,6 +34,18 @@ void prepare_repo_settings(struct repository *r)free (strval); } +if (!repo_config_get_maybe_bool (r,"merge.directoryrenames" , &value)) + r->settings.merge_directory_renames = value ?MERGE_DIRECTORY_RENAMES_TRUE :0 ;Shouldn't that be "
MERGE_DIRECTORY_RENAMES_NONE" instead of "0"?Quoted 5 lines
repository.h +0
-0repository.h enum untracked_cache_setting { · line 27
Quoted 9 lines
repository.h +6
-0@@ -19,6 +19,20 @@ enum untracked_cache_setting { UNTRACKED_CACHE_WRITE = 2 }; +enum merge_directory_renames_setting { +MERGE_DIRECTORY_RENAMES_UNSET =-1 , +MERGE_DIRECTORY_RENAMES_NONE =0 , +MERGE_DIRECTORY_RENAMES_CONFLICT =1 , +MERGE_DIRECTORY_RENAMES_TRUE =2 , +};Thanks for adding these; makes things much nicer. :-)
Cheers,
Elijah
Re: [PATCH v3 5/5] repo-settings: create feature.experimental setting
Quoted 28 lines · Junio C Hamano
Quoted 6 lines · Elijah Newren
Quoted 6 lines
Documentation/config/merge.txt +0
-0@@ -54,7 +54,8 @@ merge.directoryRenames:: moved into the new directory. If set to "conflict", a conflict will be reported for such paths. If merge.renames is false, merge.directoryRenames is ignored and treated as false. DefaultsQuoted 8 lines
Documentation/config/merge.txt merge.directoryRenames:: · line 58
Quoted 3 lines
Documentation/config/merge.txt +2
-1- to "conflict". + to "conflict" unless `feature.experimental` is enabled and the + default is "true".I have a hard time parsing that changed sentence. Perhaps something like:
...unlessfeature.experimentalis enabled in which case the
default is "true".
?That reads better.
But I am not sure about the wisdom of controlling between conflict and true with this feature macro in the first place.
Between "conflict" and "true", the former forces the end user to verify (or allows the end user to veto) the auto resolution by the heuristics and is always a safer if more cumbersome option. It's not like blindly trusting the directory rename heuristics is the bright future for all users, is it?
Quoted 3 lines
I would not set rerere.autoUpdate to true when feature.experimental is set; for exactly the same reason, I do not find it reasonable to set this to true with feature.experimental macro.
OK. I can remove it from the feature.experimental variable.
Shall I keep the enum logic and the use of repo-settings.c? I can split them out into a separate patch.
-Stolee
Re: [PATCH v3 5/5] repo-settings: create feature.experimental setting
Quoted 30 lines · Derrick Stolee
Quoted 28 lines · Junio C Hamano
Quoted 6 lines · Elijah Newren
Quoted 6 lines
Documentation/config/merge.txt +0
-0@@ -54,7 +54,8 @@ merge.directoryRenames:: moved into the new directory. If set to "conflict", a conflict will be reported for such paths. If merge.renames is false, merge.directoryRenames is ignored and treated as false. DefaultsQuoted 8 lines
Documentation/config/merge.txt merge.directoryRenames:: · line 58
Quoted 3 lines
Documentation/config/merge.txt +2
-1- to "conflict". + to "conflict" unless `feature.experimental` is enabled and the + default is "true".I have a hard time parsing that changed sentence. Perhaps something like:
...unlessfeature.experimentalis enabled in which case the
default is "true".
?That reads better.
But I am not sure about the wisdom of controlling between conflict and true with this feature macro in the first place.
Between "conflict" and "true", the former forces the end user to verify (or allows the end user to veto) the auto resolution by the heuristics and is always a safer if more cumbersome option. It's not like blindly trusting the directory rename heuristics is the bright future for all users, is it?
Quoted 8 lines
Quoted 3 lines
I would not set rerere.autoUpdate to true when feature.experimental is set; for exactly the same reason, I do not find it reasonable to set this to true with feature.experimental macro.
OK. I can remove it from the feature.experimental variable.
Shall I keep the enum logic and the use of
repo-settings.c? I can split them out into a separate patch.
Good question. In part, I was looking at this ds/feature-macros series because my cleanup-merge-api series[1] has some minor conflicts with it. I'm a little unsure what route I should take with my series now. Some choices:
* keep this logic in your series in a separate patch, and have me
rebase my series on yours.
* drop this logic from your series since it may not be needed
anymore, making our two series independent again.
* move this logic into a separate patch of yours but making that
patch part of my series instead; that'd be easy with the enum logic, but the repo-settings.c stuff appears to depend on your other patches...
Thoughts or preferences?
Re: [PATCH v3 5/5] repo-settings: create feature.experimental setting
Quoted 53 lines · Elijah Newren
Quoted 30 lines · Derrick Stolee
Quoted 28 lines · Junio C Hamano
Quoted 6 lines · Elijah Newren
Quoted 6 lines
Documentation/config/merge.txt +0
-0@@ -54,7 +54,8 @@ merge.directoryRenames:: moved into the new directory. If set to "conflict", a conflict will be reported for such paths. If merge.renames is false, merge.directoryRenames is ignored and treated as false. DefaultsQuoted 8 lines
Documentation/config/merge.txt merge.directoryRenames:: · line 58
Quoted 3 lines
Documentation/config/merge.txt +2
-1- to "conflict". + to "conflict" unless `feature.experimental` is enabled and the + default is "true".I have a hard time parsing that changed sentence. Perhaps something like:
...unlessfeature.experimentalis enabled in which case the
default is "true".
?That reads better.
But I am not sure about the wisdom of controlling between conflict and true with this feature macro in the first place.
Between "conflict" and "true", the former forces the end user to verify (or allows the end user to veto) the auto resolution by the heuristics and is always a safer if more cumbersome option. It's not like blindly trusting the directory rename heuristics is the bright future for all users, is it?
Quoted 8 lines
Quoted 3 lines
I would not set rerere.autoUpdate to true when feature.experimental is set; for exactly the same reason, I do not find it reasonable to set this to true with feature.experimental macro.
OK. I can remove it from the feature.experimental variable.
Shall I keep the enum logic and the use of
repo-settings.c? I can split them out into a separate patch.Good question. In part, I was looking at this ds/feature-macros series because my cleanup-merge-api series[1] has some minor conflicts with it. I'm a little unsure what route I should take with my series now. Some choices:
* keep this logic in your series in a separate patch, and have me
rebase my series on yours.
* drop this logic from your series since it may not be needed
anymore, making our two series independent again.
* move this logic into a separate patch of yours but making that
patch part of my series instead; that'd be easy with the enum logic, but therepo-settings.cstuff appears to depend on your other patches...
Quoted 1 line
Thoughts or preferences?
I've done the work in my GGG PR [1] to split the commit into two, with the merge.directoryRenames on top. I created a tag [2] at that commit and I'll send my v4 without changes to merge.directoryRenames.
I'll let you decide if you like placing the config in repo-settings or not (or want to adapt the enum logic in a different place).
Thanks,
-Stolee
[1] https://github.com/gitgitgadget/git/pull/292
[2] https://github.com/derrickstolee/git/releases/tag/merge.directoryRenames
Re: [PATCH v3 5/5] repo-settings: create feature.experimental setting
In reply to Junio C HamanoQuoted 18 lines · Junio C Hamano
Quoted 6 lines · Elijah Newren
Quoted 6 lines
Documentation/config/merge.txt +0
-0@@ -54,7 +54,8 @@ merge.directoryRenames:: moved into the new directory. If set to "conflict", a conflict will be reported for such paths. If merge.renames is false, merge.directoryRenames is ignored and treated as false. DefaultsQuoted 8 lines
Documentation/config/merge.txt merge.directoryRenames:: · line 58
Quoted 3 lines
Documentation/config/merge.txt +2
-1- to "conflict". + to "conflict" unless `feature.experimental` is enabled and the + default is "true".I have a hard time parsing that changed sentence. Perhaps something like:
...unlessfeature.experimentalis enabled in which case the
default is "true".
?
Quoted 1 line
That reads better.
:-)
Quoted 8 lines
But I am not sure about the wisdom of controlling between conflict and true with this feature macro in the first place.
Between "conflict" and "true", the former forces the end user to verify (or allows the end user to veto) the auto resolution by the heuristics and is always a safer if more cumbersome option. It's not like blindly trusting the directory rename heuristics is the bright future for all users, is it?
I'm afraid I don't quite know what the intended usecase for feature.experimental is, and I'm unfamiliar with both pack.useSparse and fetch.negotiationAlgorithm for comparison. If the intended usecase is stuff that is expected to become the future for all users, then I agree with you; "conflict" is the right default for both now and in the future so feature.experimental should not change it. However, Stollee's commit message started with "The 'feature.experimental' setting includes config options that are not committed to become defaults".
If these are settings that are "intended" but not "committed" to become defaults, then yes, you're absolutely right. (And in such a case, it might be nice to add that distinction to the commit message.)
If there is some other purpose, it depends on what that purpose is.
Re: [PATCH v3 5/5] repo-settings: create feature.experimental setting
Quoted 7 lines · Elijah Newren
However, Stollee's commit message started with "The 'feature.experimental' setting includes config options that are not committed to become defaults".
If these are settings that are "intended" but not "committed" to become defaults, then yes, you're absolutely right. (And in such a case, it might be nice to add that distinction to the commit message.)
My understanding is that the only purpose of feature.experimental
is: "we want to change the default of X this way, but we do not know if we missed an important use case that would be harmed by such a change, so let's see if those who volunteered to be guinea pigs by setting feature.experimental to true find glitches in our thinking that led us to think the change of the default is a good idea".
Re: [PATCH v3 5/5] repo-settings: create feature.experimental setting
Quoted 10 lines · Junio C Hamano
Quoted 7 lines · Elijah Newren
However, Stollee's commit message started with "The 'feature.experimental' setting includes config options that are not committed to become defaults".
If these are settings that are "intended" but not "committed" to become defaults, then yes, you're absolutely right. (And in such a case, it might be nice to add that distinction to the commit message.)
Quoted 6 lines
My understanding is that the only purpose of feature.experimental
is: "we want to change the default of X this way, but we do not know if we missed an important use case that would be harmed by such a change, so let's see if those who volunteered to be guinea pigs by setting feature.experimental to true find glitches in our thinking that led us to think the change of the default is a good idea".
This is the basic idea. "Here are some new features that we think are good, please opt-in to test them out and find corner cases."
Thanks,
-Stolee
v4 cover Create 'feature.*' config area and some centralized config parsing
In reply to Derrick Stolee via GitGitGadgetThis is a brand-new thread to replace ds/early-access. The discussion on that thread was very helpful [1].
With this in mind, I propose instead a set of "feature.*" config settings that form groups of "community recommended" settings (with some caveats). In the space below, I'll list a set of possible feature names and the implied config options.
First, the main two categories we've discussed so far: many commits and many files. These two feature sets are for when your repo is large in one of these dimensions. Perhaps there are other settings to include in these?
feature.manyFiles:
index.version = 4
core.untrackedCache = true
UPDATE: Instead of "feature.manyCommits", this series includes making core.commitGraph and gc.writeCommitGraph true by default!
The other category that has been discussed already is that of "experimental features that we generally think are helpful but change behavior slightly in some cases".
feature.experimental:
pack.useSparse = true
merge.directoryRenames = true
fetch.negotiationAlgorithm = skipping
Specifically, this setting is for config values we are not sure will ever be on by default, but additional testing is needed to be sure. This is different than a possible 'feature.preview' setting that would include config settings that we are committed to updating the defaults in a future release. There are many ways we can take this idea in the future (including more additions to these categories).
Thanks, -Stolee
Updates in V2: I'm responding to Dscho's comments early, as they were very valuable.
* Rearranged how we are checking for the feature.* settings, so if one layer turns the setting on and a later layer turns it off, we do not adjust the defaults.
* Switched to using enums for the non-boolean config options.
* Placed the repo_settings struct directly in the repository struct.
* All struct members are initialized to -1 using memset.
* The config changes around directory rename detection is greatly simplified.
Updates in V3: These are more Dscho comments, and are quite small.
* Remove repo-settings.h by placing the struct and function definitions in repository.h.
* Move the initialization int into "struct repo_settings".
Updates in V4:
* Remove feature.manyCommits in favor of turning commit-graph on by default.
* Update docs and 0 constant in feature.experimental around merge.directoryRenames.
[1] https://public-inbox.org/git/[email protected]/
Derrick Stolee (6):
repo-settings: consolidate some config settings
t6501: use 'git gc' in quiet mode
commit-graph: turn on commit-graph by default
repo-settings: parse core.untrackedCache
repo-settings: create feature.manyFiles setting
repo-settings: create feature.experimental setting
Documentation/config.txt | 2 + Documentation/config/core.txt | 6 ++- Documentation/config/feature.txt | 29 +++++++++++++ Documentation/config/fetch.txt | 3 +- Documentation/config/gc.txt | 2 +- Documentation/config/index.txt | 1 + Documentation/config/pack.txt | 3 +- Makefile | 1 + builtin/gc.c | 12 +++--- builtin/pack-objects.c | 8 ++-- builtin/update-index.c | 6 ++- commit-graph.c | 6 +-- config.c | 24 ----------- fetch-negotiator.c | 25 +++++------ fetch-negotiator.h | 5 ++- fetch-pack.c | 11 +++-- read-cache.c | 30 ++++++------- repo-settings.c | 64 ++++++++++++++++++++++++++++ repository.h | 30 +++++++++++++ t/t0410-partial-clone.sh | 2 +- t/t1600-index.sh | 31 +++++++++++--- t/t5307-pack-missing-commit.sh | 4 +- t/t5324-split-commit-graph.sh | 2 + t/t5552-skipping-fetch-negotiator.sh | 23 ---------- t/t6011-rev-list-with-bad-commit.sh | 2 +- t/t6501-freshen-objects.sh | 6 +-- 26 files changed, 223 insertions(+), 115 deletions(-) create mode 100644 Documentation/config/feature.txt create mode 100644 repo-settings.c
base-commit: 9c9b961d7eb15fb583a2a812088713a68a85f1c0
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-292%2Fderrickstolee%2Frepo-settings%2Fhead-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-292/derrickstolee/repo-settings/head-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/292
Range-diff vs v3:
1: 4d0db57ecb ! 1: 1a0b30b88a repo-settings: consolidate some config settings
@@ -184,14 +184,6 @@
diff --git a/repository.h b/repository.h
--- a/repository.h
+++ b/repository.h
-@@
- #include "path.h"
-
- struct config_set;
-+struct repo_settings;
- struct git_hash_algo;
- struct index_state;
- struct lock_file;
@@
struct raw_object_store;
struct submodule_cache;
2: c0129066a0 < -: ---------- repo-settings: add feature.manyCommits setting
-: ---------- > 2: 6bf3584a60 t6501: use 'git gc' in quiet mode
-: ---------- > 3: 651e2d526b commit-graph: turn on commit-graph by default
3: b0a2d0e188 ! 4: ec0abffd8b repo-settings: parse core.untrackedCache
@@ -154,19 +154,12 @@
if (!repo_config_get_bool(r, "pack.usesparse", &value))
r->settings.pack_use_sparse = value;
--
-+
- if (!repo_config_get_bool(r, "feature.manycommits", &value) && value) {
- UPDATE_DEFAULT(r->settings.core_commit_graph, 1);
- UPDATE_DEFAULT(r->settings.gc_write_commit_graph, 1);
- }
+
+ /* Hack for test programs like test-dump-untracked-cache */
+ if (ignore_untracked_cache_config)
+ r->settings.core_untracked_cache = UNTRACKED_CACHE_KEEP;
+ else
-+ UPDATE_DEFAULT(r->settings.core_untracked_cache, UNTRACKED_CACHE_KEEP);
-+
++ UPDATE_DEFAULT_BOOL(r->settings.core_untracked_cache, UNTRACKED_CACHE_KEEP);
}
diff --git a/repository.h b/repository.h
4: 0d4774d45f ! 5: 082fc57288 repo-settings: create feature.manyFiles setting
@@ -13,6 +13,19 @@
Signed-off-by: Derrick Stolee <[email protected]>
+ diff --git a/Documentation/config.txt b/Documentation/config.txt
+ --- a/Documentation/config.txt
+ +++ b/Documentation/config.txt
+@@
+
+ include::config/fastimport.txt[]
+
++include::config/feature.txt[]
++
+ include::config/fetch.txt[]
+
+ include::config/format.txt[]
+
diff --git a/Documentation/config/core.txt b/Documentation/config/core.txt
--- a/Documentation/config/core.txt
+++ b/Documentation/config/core.txt
@@ -29,15 +42,15 @@
When missing or is set to `default`, many fields in the stat
diff --git a/Documentation/config/feature.txt b/Documentation/config/feature.txt
- --- a/Documentation/config/feature.txt
+ new file mode 100644
+ --- /dev/null
+++ b/Documentation/config/feature.txt
@@
- * `core.commitGraph=true` enables reading the commit-graph file.
- +
- * `gc.writeCommitGraph=true` enables writing the commit-graph file during
--garbage collection.
- \ No newline at end of file
-+garbage collection.
++feature.*::
++ The config settings that start with `feature.` modify the defaults of
++ a group of other config settings. These groups are created by the Git
++ developer community as recommended defaults and are subject to change.
++ In particular, new config options may be added with different defaults.
+
+feature.manyFiles::
+ Enable config options that optimize for repos with many files in the
@@ -48,7 +61,6 @@
++
+* `core.untrackedCache=true` enables the untracked cache. This setting assumes
+that mtime is working on your machine.
- \ No newline at end of file
diff --git a/Documentation/config/index.txt b/Documentation/config/index.txt
--- a/Documentation/config/index.txt
@@ -63,12 +75,15 @@
--- a/repo-settings.c
+++ b/repo-settings.c
@@
- UPDATE_DEFAULT(r->settings.core_commit_graph, 1);
- UPDATE_DEFAULT(r->settings.gc_write_commit_graph, 1);
+ free(strval);
}
+
+-
+ if (!repo_config_get_bool(r, "pack.usesparse", &value))
+ r->settings.pack_use_sparse = value;
+ if (!repo_config_get_bool(r, "feature.manyfiles", &value) && value) {
-+ UPDATE_DEFAULT(r->settings.index_version, 4);
-+ UPDATE_DEFAULT(r->settings.core_untracked_cache, UNTRACKED_CACHE_WRITE);
++ UPDATE_DEFAULT_BOOL(r->settings.index_version, 4);
++ UPDATE_DEFAULT_BOOL(r->settings.core_untracked_cache, UNTRACKED_CACHE_WRITE);
+ }
/* Hack for test programs like test-dump-untracked-cache */
5: 2e153fac22 ! 6: f30e25fcd6 repo-settings: create feature.experimental setting
@@ -10,8 +10,6 @@
* 'pack.useSparse=true'
- * 'merge.directoryRenames=true'
-
* 'fetch.negotiationAlgorithm=skipping'
In the case of fetch.negotiationAlgorithm, the existing logic
@@ -41,15 +39,12 @@
+* `pack.useSparse=true` uses a new algorithm when constructing a pack-file
+which can improve `git push` performance in repos with many files.
++
-+* `merge.directoryRenames=true` uses a new algorithm for detecting renames by
-+using entire directories at a time instead of single files at a time.
-++
+* `fetch.negotiationAlgorithm=skipping` may improve fetch negotiation times by
+skipping more commits at a time, reducing the number of round trips.
+
- feature.manyCommits::
- Enable config options that optimize for repos with many commits. This
- setting is recommended for repos with at least 100,000 commits. The
+ feature.manyFiles::
+ Enable config options that optimize for repos with many files in the
+ working directory. With many files, commands such as `git status` and
diff --git a/Documentation/config/fetch.txt b/Documentation/config/fetch.txt
--- a/Documentation/config/fetch.txt
@@ -65,20 +60,6 @@
+
See also the `--negotiation-tip` option for linkgit:git-fetch[1].
- diff --git a/Documentation/config/merge.txt b/Documentation/config/merge.txt
- --- a/Documentation/config/merge.txt
- +++ b/Documentation/config/merge.txt
-@@
- moved into the new directory. If set to "conflict", a conflict
- will be reported for such paths. If merge.renames is false,
- merge.directoryRenames is ignored and treated as false. Defaults
-- to "conflict".
-+ to "conflict" unless `feature.experimental` is enabled and the
-+ default is "true".
-
- merge.renormalize::
- Tell Git that canonical representation of files in the
-
diff --git a/Documentation/config/pack.txt b/Documentation/config/pack.txt
--- a/Documentation/config/pack.txt
+++ b/Documentation/config/pack.txt
@@ -214,38 +195,6 @@
git_config(fetch_pack_config_cb, NULL);
}
- diff --git a/merge-recursive.c b/merge-recursive.c
- --- a/merge-recursive.c
- +++ b/merge-recursive.c
-@@
- opt->merge_detect_rename = git_config_rename("merge.renames", value);
- free(value);
- }
-- if (!git_config_get_string("merge.directoryrenames", &value)) {
-- int boolval = git_parse_maybe_bool(value);
-- if (0 <= boolval) {
-- opt->detect_directory_renames = boolval ? 2 : 0;
-- } else if (!strcasecmp(value, "conflict")) {
-- opt->detect_directory_renames = 1;
-- } /* avoid erroring on values from future versions of git */
-- free(value);
-- }
- git_config(git_xmerge_config, NULL);
- }
-
-@@
- opt->diff_detect_rename = -1;
- opt->merge_detect_rename = -1;
- opt->detect_directory_renames = 1;
-+
-+ prepare_repo_settings(repo);
-+ if (repo->settings.merge_directory_renames >= 0)
-+ opt->detect_directory_renames = repo->settings.merge_directory_renames;
-+
- merge_recursive_config(opt);
- merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
- if (merge_verbosity)
-
diff --git a/repo-settings.c b/repo-settings.c
--- a/repo-settings.c
+++ b/repo-settings.c
@@ -253,39 +202,31 @@
free(strval);
}
-+ if (!repo_config_get_maybe_bool(r, "merge.directoryrenames", &value))
-+ r->settings.merge_directory_renames = value ? MERGE_DIRECTORY_RENAMES_TRUE : 0;
-+ else if (!repo_config_get_string(r, "merge.directoryrenames", &strval)) {
-+ if (!strcasecmp(strval, "conflict"))
-+ r->settings.merge_directory_renames = MERGE_DIRECTORY_RENAMES_CONFLICT;
-+ }
+ if (!repo_config_get_string(r, "fetch.negotiationalgorithm", &strval)) {
+ if (!strcasecmp(strval, "skipping"))
+ r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING;
+ else
+ r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_DEFAULT;
+ }
-
++
if (!repo_config_get_bool(r, "pack.usesparse", &value))
r->settings.pack_use_sparse = value;
-@@
- UPDATE_DEFAULT(r->settings.index_version, 4);
- UPDATE_DEFAULT(r->settings.core_untracked_cache, UNTRACKED_CACHE_WRITE);
+ if (!repo_config_get_bool(r, "feature.manyfiles", &value) && value) {
+ UPDATE_DEFAULT_BOOL(r->settings.index_version, 4);
+ UPDATE_DEFAULT_BOOL(r->settings.core_untracked_cache, UNTRACKED_CACHE_WRITE);
}
+ if (!repo_config_get_bool(r, "feature.experimental", &value) && value) {
-+ UPDATE_DEFAULT(r->settings.pack_use_sparse, 1);
-+ UPDATE_DEFAULT(r->settings.merge_directory_renames, MERGE_DIRECTORY_RENAMES_TRUE);
-+ UPDATE_DEFAULT(r->settings.fetch_negotiation_algorithm, FETCH_NEGOTIATION_SKIPPING);
++ UPDATE_DEFAULT_BOOL(r->settings.pack_use_sparse, 1);
++ UPDATE_DEFAULT_BOOL(r->settings.fetch_negotiation_algorithm, FETCH_NEGOTIATION_SKIPPING);
+ }
/* Hack for test programs like test-dump-untracked-cache */
if (ignore_untracked_cache_config)
-@@
+ r->settings.core_untracked_cache = UNTRACKED_CACHE_KEEP;
else
- UPDATE_DEFAULT(r->settings.core_untracked_cache, UNTRACKED_CACHE_KEEP);
-
-+ UPDATE_DEFAULT(r->settings.merge_directory_renames, MERGE_DIRECTORY_RENAMES_CONFLICT);
-+ UPDATE_DEFAULT(r->settings.fetch_negotiation_algorithm, FETCH_NEGOTIATION_DEFAULT);
+ UPDATE_DEFAULT_BOOL(r->settings.core_untracked_cache, UNTRACKED_CACHE_KEEP);
++
++ UPDATE_DEFAULT_BOOL(r->settings.fetch_negotiation_algorithm, FETCH_NEGOTIATION_DEFAULT);
}
diff --git a/repository.h b/repository.h
@@ -295,13 +236,6 @@
UNTRACKED_CACHE_WRITE = 2
};
-+enum merge_directory_renames_setting {
-+ MERGE_DIRECTORY_RENAMES_UNSET = -1,
-+ MERGE_DIRECTORY_RENAMES_NONE = 0,
-+ MERGE_DIRECTORY_RENAMES_CONFLICT = 1,
-+ MERGE_DIRECTORY_RENAMES_TRUE = 2,
-+};
-+
+enum fetch_negotiation_setting {
+ FETCH_NEGOTIATION_UNSET = -1,
+ FETCH_NEGOTIATION_NONE = 0,
@@ -316,7 +250,6 @@
enum untracked_cache_setting core_untracked_cache;
int pack_use_sparse;
-+ enum merge_directory_renames_setting merge_directory_renames;
+ enum fetch_negotiation_setting fetch_negotiation_algorithm;
};Signature · 1 line
gitgitgadget
PATCH v4 1/6 repo-settings: consolidate some config settings
From: Derrick Stolee <[email protected]>
There are a few important config settings that are not loaded during git_default_config. These are instead loaded on-demand.
Centralize these config options to a single scan, and store all of the values in a repo_settings struct. The values for each setting are initialized as negative to indicate "unset".
This centralization will be particularly important in a later change to introduce "meta" config settings that change the defaults for these config settings.
--- Makefile | 1 + builtin/gc.c | 12 +++++------- builtin/pack-objects.c | 8 ++++---- commit-graph.c | 6 +++--- read-cache.c | 11 ++++++----- repo-settings.c | 25 +++++++++++++++++++++++++ repository.h | 14 ++++++++++++++ 7 files changed, 58 insertions(+), 19 deletions(-) create mode 100644 repo-settings.c
Makefile +1 -0
@@ -964,6 +964,7 @@ LIB_OBJS += refspec.o LIB_OBJS += ref-filter.o LIB_OBJS += remote.o LIB_OBJS += replace-object.o +LIB_OBJS += repo-settings.o LIB_OBJS += repository.o LIB_OBJS += rerere.o LIB_OBJS += resolve-undo.o
builtin/gc.c +5 -7
@@ -41,7 +41,6 @@ static int aggressive_depth = 50;static int aggressive_window =250 ;static int gc_auto_threshold =6700 ;static int gc_auto_pack_limit =50 ; -static int gc_write_commit_graph;static int detach_auto =1 ;static timestamp_t gc_log_expire_time;static const char *gc_log_expire ="1.day.ago" ;
@@ -148,7 +147,6 @@ static void gc_config(void)git_config_get_int ("gc.aggressivedepth" , &aggressive_depth);git_config_get_int ("gc.auto" , &gc_auto_threshold);git_config_get_int ("gc.autopacklimit" , &gc_auto_pack_limit); -git_config_get_bool ("gc.writecommitgraph" , &gc_write_commit_graph);git_config_get_bool ("gc.autodetach" , &detach_auto);git_config_get_expiry ("gc.pruneexpire" , &prune_expire);git_config_get_expiry ("gc.worktreepruneexpire" , &prune_worktrees_expire);
@@ -685,11 +683,11 @@ int cmd_gc(int argc, const char **argv, const char *prefix)clean_pack_garbage (); } -if (gc_write_commit_graph && -write_commit_graph_reachable (get_object_directory (), - !quiet && !daemonized ?COMMIT_GRAPH_PROGRESS :0 , -NULL )) -return 1 ; +prepare_repo_settings (the_repository); +if (the_repository->settings.gc_write_commit_graph ==1 ) +write_commit_graph_reachable (get_object_directory (), + !quiet && !daemonized ?COMMIT_GRAPH_PROGRESS :0 , +NULL );if (auto_gc &&too_many_loose_objects ()) warning(_("There are too many unreachable loose objects; "
builtin/pack-objects.c +4 -4
@@ -2709,10 +2709,6 @@ static int git_pack_config(const char *k, const char *v, void *cb) use_bitmap_index_default =git_config_bool (k, v);return 0 ; } -if (!strcmp (k,"pack.usesparse" )) { - sparse =git_config_bool (k, v); -return 0 ; - }if (!strcmp (k,"pack.threads" )) { delta_search_threads =git_config_int (k, v);if (delta_search_threads <0 )
@@ -3332,6 +3328,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix) read_replace_refs =0 ; sparse =git_env_bool ("GIT_TEST_PACK_SPARSE" ,0 ); +prepare_repo_settings (the_repository); +if (!sparse && the_repository->settings.pack_use_sparse !=-1 ) + sparse = the_repository->settings.pack_use_sparse; +reset_pack_idx_option (&pack_idx_opts);git_config (git_pack_config,NULL );
commit-graph.c +3 -3
@@ -466,7 +466,6 @@ static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)static int prepare_commit_graph (struct repository *r) {struct object_directory *odb; -int config_value;if (git_env_bool (GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD ,0 )) die("dying as requested by the '%s' variable on commit-graph load!",
@@ -476,9 +475,10 @@ static int prepare_commit_graph(struct repository *r)return !!r->objects->commit_graph; r->objects->commit_graph_attempted =1 ; +prepare_repo_settings (r); +if (!git_env_bool (GIT_TEST_COMMIT_GRAPH ,0 ) && - (repo_config_get_bool (r,"core.commitgraph" , &config_value) || - !config_value)) + r->settings.core_commit_graph !=1 )/* * This repository is not configured to use commit graphs, so * do not load one. (But report commit_graph_attempted anyway
read-cache.c +6 -5
@@ -1599,16 +1599,17 @@ struct cache_entry *refresh_cache_entry(struct index_state *istate,#define INDEX_FORMAT_DEFAULT 3 -static unsigned int get_index_format_default (void ) +static unsigned int get_index_format_default (struct repository *r) {char *envversion =getenv ("GIT_INDEX_VERSION" );char *endp; -int value;unsigned int version =INDEX_FORMAT_DEFAULT ;if (!envversion) { -if (!git_config_get_int ("index.version" , &value)) - version = value; +prepare_repo_settings (r); + +if (r->settings.index_version >=0 ) + version = r->settings.index_version;if (version <INDEX_FORMAT_LB ||INDEX_FORMAT_UB < version) {warning (_ ("index.version set, but the value is invalid.\n" "Using version %i" ),INDEX_FORMAT_DEFAULT );
@@ -2765,7 +2766,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile, }if (!istate->version) { - istate->version =get_index_format_default (); + istate->version =get_index_format_default (the_repository);if (git_env_bool ("GIT_TEST_SPLIT_INDEX" ,0 ))init_split_index (istate); }
repo-settings.c new file +25 -0
@@ -0,0 +1,25 @@ +#include "cache.h" +#include "config.h" +#include "repository.h" + +void prepare_repo_settings (struct repository *r) +{ +int value; + +if (r->settings.initialized) +return ; + +/* Defaults */ +memset (&r->settings,-1 ,sizeof (r->settings)); + +if (!repo_config_get_bool (r,"core.commitgraph" , &value)) + r->settings.core_commit_graph = value; +if (!repo_config_get_bool (r,"gc.writecommitgraph" , &value)) + r->settings.gc_write_commit_graph = value; + +if (!repo_config_get_bool (r,"index.version" , &value)) + r->settings.index_version = value; + +if (!repo_config_get_bool (r,"pack.usesparse" , &value)) + r->settings.pack_use_sparse = value; +}
repository.h +14 -0
@@ -11,6 +11,17 @@ struct pathspec;struct raw_object_store ;struct submodule_cache ; +struct repo_settings { +int initialized; + +int core_commit_graph; +int gc_write_commit_graph; + +int index_version; + +int pack_use_sparse; +}; +struct repository {/* Environment */ /*
@@ -72,6 +83,8 @@ struct repository {*/ char *submodule_prefix; +struct repo_settings settings; +/* Subsystems */ /* * Repository's config which contains key-value pairs from the usual
@@ -157,5 +170,6 @@ int repo_read_index_unmerged(struct repository *);*/ void repo_update_index_if_able (struct repository *,struct lock_file *); +void prepare_repo_settings (struct repository *r);#endif /* REPOSITORY_H */
Signature · 1 line
gitgitgadget
PATCH v4 2/6 t6501: use 'git gc' in quiet mode
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
t6501-freshen-objects.sh sends the standard error from 'git gc' to a file and verifies that it is empty. This is intended as a way to ensure no warnings are written during the operation. However, as the commit-graph is added as a step to 'git gc', its progress will appear in the output.
Pass the '-q' argument to avoid a failing test case when progress is written.
--- t/t6501-freshen-objects.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
t/t6501-freshen-objects.sh +3 -3
@@ -137,7 +137,7 @@ test_expect_success 'do not complain about existing broken links (commit)' ' some message EOF commit=$(git hash-object -t commit -w broken-commit) && - git gc 2>stderr && + git gc -q 2>stderr && verbose git cat-file -e $commit && test_must_be_empty stderr '
@@ -147,7 +147,7 @@ test_expect_success 'do not complain about existing broken links (tree)' ' 100644 blob 0000000000000000000000000000000000000003 foo EOF tree=$(git mktree --missing <broken-tree) && - git gc 2>stderr && + git gc -q 2>stderr && git cat-file -e $tree && test_must_be_empty stderr '
@@ -162,7 +162,7 @@ test_expect_success 'do not complain about existing broken links (tag)' ' this is a broken tag EOF tag=$(git hash-object -t tag -w broken-tag) && - git gc 2>stderr && + git gc -q 2>stderr && git cat-file -e $tag && test_must_be_empty stderr '
Signature · 1 line
gitgitgadget
PATCH v4 3/6 commit-graph: turn on commit-graph by default
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The commit-graph feature has seen a lot of activity in the past year or so since it was introduced. The feature is a critical performance enhancement for medium- to large-sized repos, and does not significantly hurt small repos.
Change the defaults for core.commitGraph and gc.writeCommitGraph to true so users benefit from this feature by default.
There are several places in the test suite where the environment variable GIT_TEST_COMMIT_GRAPH is disabled to avoid reading a commit-graph, if it exists. The config option overrides the environment, so swap these. Some GIT_TEST_COMMIT_GRAPH assignments remain, and those are to avoid writing a commit-graph when a new commit is created.
--- Documentation/config/core.txt | 2 +- Documentation/config/gc.txt | 2 +- repo-settings.c | 4 ++++ t/t0410-partial-clone.sh | 2 +- t/t5307-pack-missing-commit.sh | 4 ++-- t/t5324-split-commit-graph.sh | 2 ++ t/t6011-rev-list-with-bad-commit.sh | 2 +- 7 files changed, 12 insertions(+), 6 deletions(-)
Documentation/config/core.txt +1 -1
@@ -577,7 +577,7 @@ the `GIT_NOTES_REF` environment variable. See linkgit:git-notes[1]. core.commitGraph:: If true, then git will read the commit-graph file (if it exists) - to parse the graph structure of commits. Defaults to false. See + to parse the graph structure of commits. Defaults to true. See linkgit:git-commit-graph[1] for more information. core.useReplaceRefs::
Documentation/config/gc.txt +1 -1
@@ -63,7 +63,7 @@ gc.writeCommitGraph:: If true, then gc will rewrite the commit-graph file when linkgit:git-gc[1] is run. When using `git gc --auto` the commit-graph will be updated if housekeeping is - required. Default is false. See linkgit:git-commit-graph[1] + required. Default is true. See linkgit:git-commit-graph[1] for details. gc.logExpiry::
repo-settings.c +4 -0
@@ -2,6 +2,8 @@#include "config.h" #include "repository.h" +#define UPDATE_DEFAULT_BOOL (s,v) do { if (s == -1) { s = v; } } while(0) +void prepare_repo_settings (struct repository *r) {int value;
@@ -16,6 +18,8 @@ void prepare_repo_settings(struct repository *r) r->settings.core_commit_graph = value;if (!repo_config_get_bool (r,"gc.writecommitgraph" , &value)) r->settings.gc_write_commit_graph = value; +UPDATE_DEFAULT_BOOL (r->settings.core_commit_graph,1 ); +UPDATE_DEFAULT_BOOL (r->settings.gc_write_commit_graph,1 );if (!repo_config_get_bool (r,"index.version" , &value)) r->settings.index_version = value;
t/t0410-partial-clone.sh +1 -1
@@ -234,7 +234,7 @@ test_expect_success 'rev-list stops traversal at missing and promised commit' ' git -C repo config core.repositoryformatversion 1 && git -C repo config extensions.partialclone "arbitrary string" && - GIT_TEST_COMMIT_GRAPH=0 git -C repo rev-list --exclude-promisor-objects --objects bar >out && + GIT_TEST_COMMIT_GRAPH=0 git -C repo -c core.commitGraph=false rev-list --exclude-promisor-objects --objects bar >out && grep $(git -C repo rev-parse bar) out && ! grep $FOO out '
t/t5307-pack-missing-commit.sh +2 -2
@@ -24,11 +24,11 @@ test_expect_success 'check corruption' ' ' test_expect_success 'rev-list notices corruption (1)' ' - test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git rev-list HEAD + test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git -c core.commitGraph=false rev-list HEAD ' test_expect_success 'rev-list notices corruption (2)' ' - test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git rev-list --objects HEAD + test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git -c core.commitGraph=false rev-list --objects HEAD ' test_expect_success 'pack-objects notices corruption' '
t/t5324-split-commit-graph.sh +2 -0
@@ -8,6 +8,7 @@ GIT_TEST_COMMIT_GRAPH=0 test_expect_success 'setup repo' ' git init && git config core.commitGraph true && + git config gc.writeCommitGraph false && infodir=".git/objects/info" && graphdir="$infodir/commit-graphs" && test_oid_init
@@ -332,6 +333,7 @@ test_expect_success 'split across alternate where alternate is not split' ' git clone --no-hardlinks . alt-split && ( cd alt-split && + rm -f .git/objects/info/commit-graph && echo "$(pwd)"/../.git/objects >.git/objects/info/alternates && test_commit 18 && git commit-graph write --reachable --split &&
t/t6011-rev-list-with-bad-commit.sh +1 -1
@@ -42,7 +42,7 @@ test_expect_success 'corrupt second commit object' \ ' test_expect_success 'rev-list should fail' ' - test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git rev-list --all > /dev/null + test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git -c core.commitGraph=false rev-list --all > /dev/null ' test_expect_success 'git repack _MUST_ fail' \
Signature · 1 line
gitgitgadget
Re: [PATCH v4 3/6] commit-graph: turn on commit-graph by default
Quoted 14 lines · Derrick Stolee via GitGitGadget
repo-settings.c +2
-0@@ -2,6 +2,8 @@#include "config.h" #include "repository.h" +#define UPDATE_DEFAULT_BOOL (s,v) do { if (s == -1) { s = v; } } while(0) +void prepare_repo_settings (struct repository *r) {int value;
repo-settings.c void prepare_repo_settings(struct repository *r) · line 22
Quoted 5 lines
repo-settings.c +2
-0@@ -16,6 +18,8 @@ void prepare_repo_settings(struct repository *r) r->settings.core_commit_graph = value;if (!repo_config_get_bool (r,"gc.writecommitgraph" , &value)) r->settings.gc_write_commit_graph = value; +UPDATE_DEFAULT_BOOL (r->settings.core_commit_graph,1 ); +UPDATE_DEFAULT_BOOL (r->settings.gc_write_commit_graph,1 );
This is a "review comment" that is more than 2 years late X-<, but I noticed that this is used to muck with a structure that was initialized by filling it with \0377 bytes.
+ /* Defaults */
+ memset(&r->settings, -1, sizeof(r->settings));
but the structure is is full of "int" and "enum", so apparently this works only on 2's complement architecture.
struct repo_settings {
int initialized;
int core_commit_graph;
int commit_graph_read_changed_paths;
int gc_write_commit_graph;
int fetch_write_commit_graph;
int index_version;
enum untracked_cache_setting core_untracked_cache;
int pack_use_sparse;
enum fetch_negotiation_setting fetch_negotiation_algorithm;
int core_multi_pack_index;
unsigned command_requires_full_index:1,
sparse_index:1;
};I see that the earliest iteration of this series [*1*] set the default explicitly using assignments of the correct types, like this:
+void prepare_repo_settings(struct repository *r)
+{
+ if (r->settings)
+ return;
+
+ r->settings = xmalloc(sizeof(*r->settings));
+
+ /* Defaults */ + r->settings->core_commit_graph = -1; + r->settings->gc_write_commit_graph = -1; + r->settings->pack_use_sparse = -1; + r->settings->index_version = -1;
+ ...
which I think should be a reasonable starting point to fix the current code.
Another thing I noticed is that while it may have been only for setting the default value for a boolean variable initially, other changes abuse the macro to set an arbitrary integer values to integer members of the structure, e.g. c6cc4c5a (repo-settings:
create feature.manyFiles setting, 2019-08-13) sets 4 to the index_version (naturally, the choice between 0 and 1 does not make much sense for the member), and ad0fb659 (repo-settings: parse core.untrackedCache, 2019-08-13) stuffs UNTRACKED_CACHE_* enum to core_untracked_cache. The UPDATE_DEFAULT_BOOL() macro should be renamed to UPDATE_DEFAULT_INT() at least, I would think, to save readers from confusion.
Thanks.
[Reference]
Re: [PATCH v4 3/6] commit-graph: turn on commit-graph by default
Quoted 30 lines · Junio C Hamano
Quoted 14 lines · Derrick Stolee via GitGitGadget
repo-settings.c +2
-0@@ -2,6 +2,8 @@#include "config.h" #include "repository.h" +#define UPDATE_DEFAULT_BOOL (s,v) do { if (s == -1) { s = v; } } while(0) +void prepare_repo_settings (struct repository *r) {int value;repo-settings.c void prepare_repo_settings(struct repository *r) · line 22
Quoted 5 lines
repo-settings.c +2
-0@@ -16,6 +18,8 @@ void prepare_repo_settings(struct repository *r) r->settings.core_commit_graph = value;if (!repo_config_get_bool (r,"gc.writecommitgraph" , &value)) r->settings.gc_write_commit_graph = value; +UPDATE_DEFAULT_BOOL (r->settings.core_commit_graph,1 ); +UPDATE_DEFAULT_BOOL (r->settings.gc_write_commit_graph,1 );This is a "review comment" that is more than 2 years late X-<, but I noticed that this is used to muck with a structure that was initialized by filling it with \0377 bytes.
(quoted diff) +2
-0+ /* Defaults */ + memset(&r->settings, -1, sizeof(r->settings));
Quoted 2 lines
but the structure is is full of "int" and "enum", so apparently this works only on 2's complement architecture.
This statement is true, but are there systems capable of running Git which don't use two's complement? Rust requires two's complement signed integers, and there's a proposal[0] to the C++ working group to only support two's complement because "[t]o the author’s knowledge no modern machine uses both C++ and a signed integer representation other than two’s complement". That proposal goes on to note that none of MSVC, GCC, or LLVM support other options.
Personally I am not aware of any modern processor which provides signed integer types using other than two's complement.
[0] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r4.html
Signature · 2 lines
brian m. carlson (he/him or they/them) Toronto, Ontario, CA
Re: [PATCH v4 3/6] commit-graph: turn on commit-graph by default
Quoted 34 lines · brian m. carlson
Quoted 30 lines · Junio C Hamano
Quoted 14 lines · Derrick Stolee via GitGitGadget
repo-settings.c +2
-0@@ -2,6 +2,8 @@#include "config.h" #include "repository.h" +#define UPDATE_DEFAULT_BOOL (s,v) do { if (s == -1) { s = v; } } while(0) +void prepare_repo_settings (struct repository *r) {int value;repo-settings.c void prepare_repo_settings(struct repository *r) · line 22
Quoted 5 lines
repo-settings.c +2
-0@@ -16,6 +18,8 @@ void prepare_repo_settings(struct repository *r) r->settings.core_commit_graph = value;if (!repo_config_get_bool (r,"gc.writecommitgraph" , &value)) r->settings.gc_write_commit_graph = value; +UPDATE_DEFAULT_BOOL (r->settings.core_commit_graph,1 ); +UPDATE_DEFAULT_BOOL (r->settings.gc_write_commit_graph,1 );This is a "review comment" that is more than 2 years late X-<, but I noticed that this is used to muck with a structure that was initialized by filling it with \0377 bytes.
(quoted diff) +2
-0+ /* Defaults */ + memset(&r->settings, -1, sizeof(r->settings));Quoted 2 lines
but the structure is is full of "int" and "enum", so apparently this works only on 2's complement architecture.
Quoted 7 lines
This statement is true, but are there systems capable of running Git which don't use two's complement? Rust requires two's complement signed integers, and there's a proposal[0] to the C++ working group to only support two's complement because "[t]o the author’s knowledge no modern machine uses both C++ and a signed integer representation other than two’s complement". That proposal goes on to note that none of MSVC, GCC, or LLVM support other options.
A similar proposal [1] is included in the draft of the next C standard [2]. As integer representation is implementation defined I believe this code has well defined behavior on 2's complement implementations. If an enum has no negative members then the compiler may choose an unsigned representation but even then the comparison to -1 is well defined. In this case I'm pretty sure the enums all have -1 as a member so are signed. Using memset() to initialize the struct eases future maintenance when members are added or removed and seems to me to be a sensible design choice.
Best Wishes
Phillip
[1] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2412.pdf
[2] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2596.pdf
Quoted 5 lines
Personally I am not aware of any modern processor which provides signed integer types using other than two's complement.
[0] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r4.html
Re: [PATCH v4 3/6] commit-graph: turn on commit-graph by default
Quoted 42 lines · Phillip Wood
Quoted 33 lines · brian m. carlson
Quoted 30 lines · Junio C Hamano
Quoted 14 lines · Derrick Stolee via GitGitGadget
repo-settings.c +1
-0@@ -2,6 +2,8 @@#include "config.h" #include "repository.h" +#define UPDATE_DEFAULT_BOOL(s,v) do { if (s == -1) { s = v; } }while (0 ) + void prepare_repo_settings(struct repository *r) {int value;repo-settings.c void prepare_repo_settings(struct repository *r) · line 22
Quoted 5 lines
repo-settings.c +2
-0@@ -16,6 +18,8 @@ void prepare_repo_settings(struct repository *r) r->settings.core_commit_graph = value;if (!repo_config_get_bool (r,"gc.writecommitgraph" , &value)) r->settings.gc_write_commit_graph = value; +UPDATE_DEFAULT_BOOL (r->settings.core_commit_graph,1 ); +UPDATE_DEFAULT_BOOL (r->settings.gc_write_commit_graph,1 );This is a "review comment" that is more than 2 years late X-<, but I noticed that this is used to muck with a structure that was initialized by filling it with \0377 bytes.
(quoted diff) +2
-0+ /* Defaults */ + memset(&r->settings, -1, sizeof(r->settings));Quoted 2 lines
but the structure is is full of "int" and "enum", so apparently this works only on 2's complement architecture.
Quoted 7 lines
This statement is true, but are there systems capable of running Git which don't use two's complement? Rust requires two's complement signed integers, and there's a proposal[0] to the C++ working group to only support two's complement because "[t]o the author’s knowledge no modern machine uses both C++ and a signed integer representation other than two’s complement". That proposal goes on to note that none of MSVC, GCC, or LLVM support other options.
Quoted 6 lines
A similar proposal [1] is included in the draft of the next C standard [2]. As integer representation is implementation defined I believe this code has well defined behavior on 2's complement implementations. If an enum has no negative members then the compiler may choose an unsigned representation but even then the comparison to -1 is well defined.
That's informative, thanks.
Quoted 4 lines
In this case I'm pretty sure the enums all have -1
as a member so are signed. Usingmemset()to initialize the struct eases future maintenance when members are added or removed and seems to me to be a sensible design choice.
It's really not sensible at all in this particular case, as I think my
[1] which gets rid of the pattern convincingly argues.
I.e. the only reason it had a memset() of -1 after we'd already memset it to 0 was because the function was tripping over itself and setting defaults in the wrong order for no good reason.
I.e. it was doing things like (pseudocode);
memset(&data, -1, ...)
if_config_is_true_set("x.y", data.x_y);
if (data.x_y == -1)
data.x_y = x_y_default();When we can instead just do:
data.x_y = x_y_default();
set_if_cfg_key_exists("x.y", &data.x_y);Which is how we e.g. handle options parsing, we have hardcoded defaults, then read defaults from config, then set options, in that order.
We don't set options, then check if each value is still -1, if so read config etc. Just read them in priority order, doing it any other way is just make-work for something that's the equivalent of a simple short-circuit || operation.
Anyway, there are other cases where we need to read something and distinguish e.g. false/true from "unset", and there a -1,0,1 tri-state serves us well.
But even in those cases what repo-settings.c was doing of memsetting the entire struct to -1 (2's compliment aside) just makes for needlessly hard to read code.
If we've got some members that need -1 defaults we should instead have that in an *_INIT macro or equivalent. The pre-[1] repo-settings.c also has code like this pseudocode:
data.a_b = -1; /* default for a bi-state, not tri-state variable */
set_if_cfg_key_exists("a.b", &data.a_b);
if (data.a_b == -1)
data.a_b = 1; /* on by default */Which, urm, you can just do as:
data.a_b = 1; /* on by default */
set_if_cfg_key_exists("a.b", &data.a_b);I.e. the setup for things that never wanted or cared about being set to -1 was complicated by them needing to un-set themselves from a -1 default they never wanted.
Thus the anti-pattern, yes set defaults for some members to -1, but not the entire struct. The only value we should memset a whole bag-of-stuff config struct to is 0, as that's the only sensible default & plays well with other C semantics.
1. https://lore.kernel.org/git/[email protected]/
Quoted 13 lines
Best Wishes
Phillip
[1] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2412.pdf
[2] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2596.pdfQuoted 5 lines
Personally I am not aware of any modern processor which provides signed integer types using other than two's complement.
[0]
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r4.html
Re: [PATCH v4 3/6] commit-graph: turn on commit-graph by default
Quoted 4 lines · Ævar Arnfjörð Bjarmason
Thus the anti-pattern, yes set defaults for some members to -1, but not the entire struct. The only value we should memset a whole bag-of-stuff config struct to is 0, as that's the only sensible default & plays well with other C semantics.
FWIW, I agree. I had to scratch my head for a moment at why a memset of "-1" would work at all on multi-byte types. I think it's better avoided in the name of readability and obviousness, not to mention the trap it leaves for items that don't sensibly initialize with it (like, say, pointers).
As an aside, memset to 0 is _also_ undefined for pointers, but we long ago decided not to care, as no real-world systems have a problem with this.
-Peff
Re: [PATCH v4 3/6] commit-graph: turn on commit-graph by default
In reply to Ævar Arnfjörð BjarmasonQuoted 54 lines · Ævar Arnfjörð Bjarmason
Quoted 42 lines · Phillip Wood
Quoted 33 lines · brian m. carlson
Quoted 30 lines · Junio C Hamano
Quoted 14 lines · Derrick Stolee via GitGitGadget
repo-settings.c +1
-0@@ -2,6 +2,8 @@#include "config.h" #include "repository.h" +#define UPDATE_DEFAULT_BOOL(s,v) do { if (s == -1) { s = v; } }while (0 ) + void prepare_repo_settings(struct repository *r) {int value;repo-settings.c void prepare_repo_settings(struct repository *r) · line 22
Quoted 5 lines
repo-settings.c +2
-0@@ -16,6 +18,8 @@ void prepare_repo_settings(struct repository *r) r->settings.core_commit_graph = value;if (!repo_config_get_bool (r,"gc.writecommitgraph" , &value)) r->settings.gc_write_commit_graph = value; +UPDATE_DEFAULT_BOOL (r->settings.core_commit_graph,1 ); +UPDATE_DEFAULT_BOOL (r->settings.gc_write_commit_graph,1 );This is a "review comment" that is more than 2 years late X-<, but I noticed that this is used to muck with a structure that was initialized by filling it with \0377 bytes.
(quoted diff) +2
-0+ /* Defaults */ + memset(&r->settings, -1, sizeof(r->settings));Quoted 2 lines
but the structure is is full of "int" and "enum", so apparently this works only on 2's complement architecture.
Quoted 7 lines
This statement is true, but are there systems capable of running Git which don't use two's complement? Rust requires two's complement signed integers, and there's a proposal[0] to the C++ working group to only support two's complement because "[t]o the author’s knowledge no modern machine uses both C++ and a signed integer representation other than two’s complement". That proposal goes on to note that none of MSVC, GCC, or LLVM support other options.
Quoted 6 lines
A similar proposal [1] is included in the draft of the next C standard [2]. As integer representation is implementation defined I believe this code has well defined behavior on 2's complement implementations. If an enum has no negative members then the compiler may choose an unsigned representation but even then the comparison to -1 is well defined.
That's informative, thanks.
Quoted 7 lines
Quoted 4 lines
In this case I'm pretty sure the enums all have -1
as a member so are signed. Usingmemset()to initialize the struct eases future maintenance when members are added or removed and seems to me to be a sensible design choice.It's really not sensible at all in this particular case, as I think my
[1] which gets rid of the pattern convincingly argues.
I meant that it was a sensible way to initialize all the struct members to -1, I did not mean to comment either way on whether such an initialization was sensible. If we can just store the default and then update from the user's config that sounds sensible.
Best Wishes
Phillip
Quoted 71 lines
I.e. the only reason it had a
memset()of -1 after we'd already memset it to 0 was because the function was tripping over itself and setting defaults in the wrong order for no good reason.I.e. it was doing things like (pseudocode);
memset(&data, -1, ...) if_config_is_true_set("x.y", data.x_y); if (data.x_y == -1) data.x_y = x_y_default();When we can instead just do:
data.x_y = x_y_default(); set_if_cfg_key_exists("x.y", &data.x_y);Which is how we e.g. handle options parsing, we have hardcoded defaults, then read defaults from config, then set options, in that order.
We don't set options, then check if each value is still -1, if so read config etc. Just read them in priority order, doing it any other way is just make-work for something that's the equivalent of a simple short-circuit || operation.
Anyway, there are other cases where we need to read something and distinguish e.g. false/true from "unset", and there a -1,0,1 tri-state serves us well.
But even in those cases what
repo-settings.cwas doing of memsetting the entire struct to -1 (2's compliment aside) just makes for needlessly hard to read code.If we've got some members that need -1 defaults we should instead have that in an
*_INITmacro or equivalent. The pre-[1]repo-settings.calso has code like this pseudocode:data.a_b = -1; /* default for a bi-state, not tri-state variable */ set_if_cfg_key_exists("a.b", &data.a_b); if (data.a_b == -1) data.a_b = 1; /* on by default */Which, urm, you can just do as:
data.a_b = 1; /* on by default */ set_if_cfg_key_exists("a.b", &data.a_b);I.e. the setup for things that never wanted or cared about being set to -1 was complicated by them needing to un-set themselves from a -1 default they never wanted.
Thus the anti-pattern, yes set defaults for some members to -1, but not the entire struct. The only value we should memset a whole bag-of-stuff config struct to is 0, as that's the only sensible default & plays well with other C semantics.
1. https://lore.kernel.org/git/[email protected]/
Quoted 13 lines
Best Wishes
Phillip
[1] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2412.pdf
[2] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2596.pdfQuoted 5 lines
Personally I am not aware of any modern processor which provides signed integer types using other than two's complement.
[0]
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0907r4.html
Re: [PATCH v4 3/6] commit-graph: turn on commit-graph by default
In reply to Junio C HamanoQuoted 75 lines · Junio C Hamano
Quoted 14 lines · Derrick Stolee via GitGitGadget
repo-settings.c +2
-0@@ -2,6 +2,8 @@#include "config.h" #include "repository.h" +#define UPDATE_DEFAULT_BOOL (s,v) do { if (s == -1) { s = v; } } while(0) +void prepare_repo_settings (struct repository *r) {int value;repo-settings.c void prepare_repo_settings(struct repository *r) · line 22
Quoted 5 lines
repo-settings.c +2
-0@@ -16,6 +18,8 @@ void prepare_repo_settings(struct repository *r) r->settings.core_commit_graph = value;if (!repo_config_get_bool (r,"gc.writecommitgraph" , &value)) r->settings.gc_write_commit_graph = value; +UPDATE_DEFAULT_BOOL (r->settings.core_commit_graph,1 ); +UPDATE_DEFAULT_BOOL (r->settings.gc_write_commit_graph,1 );This is a "review comment" that is more than 2 years late X-<, but I noticed that this is used to muck with a structure that was initialized by filling it with \0377 bytes.
(quoted diff) +2
-0+ /* Defaults */ + memset(&r->settings, -1, sizeof(r->settings));but the structure is is full of "int" and "enum", so apparently this works only on 2's complement architecture.
struct repo_settings { int initialized; int core_commit_graph; int commit_graph_read_changed_paths; int gc_write_commit_graph; int fetch_write_commit_graph; int index_version; enum untracked_cache_setting core_untracked_cache; int pack_use_sparse; enum fetch_negotiation_setting fetch_negotiation_algorithm; int core_multi_pack_index; unsigned command_requires_full_index:1, sparse_index:1; };I see that the earliest iteration of this series [*1*] set the default explicitly using assignments of the correct types, like this:
(quoted diff) +13
-0+void prepare_repo_settings(struct repository *r) +{ + if (r->settings) + return; + + r->settings = xmalloc(sizeof(*r->settings)); + + /* Defaults */ + r->settings->core_commit_graph = -1; + r->settings->gc_write_commit_graph = -1; + r->settings->pack_use_sparse = -1; + r->settings->index_version = -1; + ...which I think should be a reasonable starting point to fix the current code.
Quoted 11 lines
Another thing I noticed is that while it may have been only for setting the default value for a boolean variable initially, other changes abuse the macro to set an arbitrary integer values to integer members of the structure, e.g.
c6cc4c5a(repo-settings:
create feature.manyFiles setting, 2019-08-13) sets 4 to theindex_version(naturally, the choice between 0 and 1 does not make much sense for the member), andad0fb659(repo-settings: parse core.untrackedCache, 2019-08-13) stuffs UNTRACKED_CACHE_* enum tocore_untracked_cache. TheUPDATE_DEFAULT_BOOL()macro should be renamed toUPDATE_DEFAULT_INT()at least, I would think, to save readers from confusion.
Yes this is all a bit weird and/or broken, but I'm a bit perplexed at this reply to a 2+ year old E-Mail given my outstanding series to fix all these issues you've noted here[1] posted in the last few days, and you having read (at least part of) it[1].
But then again, the last patch you left a comment on was 3/5. It's 4/5 that fixes all the issues you note above[2] :)
The macro is gone, so is the memset to -1 and other weird emergent behavior. We can rely on repo_init() having zero'd the structure for us, and we just proceed to set sensible defaults in a way that doesn't stomp over the types in the struct.
1. https://lore.kernel.org/git/[email protected]/
2. https://lore.kernel.org/git/[email protected]/
Quoted 3 lines
[Reference]
PATCH v4 4/6 repo-settings: parse core.untrackedCache
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The core.untrackedCache config setting is slightly complicated, so clarify its use and centralize its parsing into the repo settings.
The default value is "keep" (returned as -1), which persists the untracked cache if it exists.
If the value is set as "false" (returned as 0), then remove the untracked cache if it exists.
If the value is set as "true" (returned as 1), then write the untracked cache and persist it.
Instead of relying on magic values of -1, 0, and 1, split these options into an enum. This allows the use of "-1" as a default value. After parsing the config options, if the value is unset we can initialize it to UNTRACKED_CACHE_KEEP.
--- builtin/update-index.c | 6 ++++-- config.c | 24 ------------------------ read-cache.c | 19 +++++++++---------- repo-settings.c | 19 +++++++++++++++++++ repository.h | 8 ++++++++ 5 files changed, 40 insertions(+), 36 deletions(-)
builtin/update-index.c +4 -2
@@ -966,6 +966,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)struct parse_opt_ctx_t ctx;strbuf_getline_fn getline_fn;int parseopt_state =PARSE_OPT_UNKNOWN ; +struct repository *r = the_repository;struct option options[] = { OPT_BIT('q', NULL, &refresh_args.flags, N_("continue refresh even when index needs update"),
@@ -1180,11 +1181,12 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)remove_split_index (&the_index); } +prepare_repo_settings (r);switch (untracked_cache) {case UC_UNSPECIFIED :break ;case UC_DISABLE : -if (git_config_get_untracked_cache () ==1 ) +if (r->settings.core_untracked_cache ==UNTRACKED_CACHE_WRITE )warning (_ ("core.untrackedCache is set to true; " "remove or change it, if you really want to " "disable the untracked cache" ));
@@ -1196,7 +1198,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)return !test_if_untracked_cache_is_supported ();case UC_ENABLE :case UC_FORCE : -if (git_config_get_untracked_cache () ==0 ) +if (r->settings.core_untracked_cache ==UNTRACKED_CACHE_REMOVE )warning (_ ("core.untrackedCache is set to false; " "remove or change it, if you really want to " "enable the untracked cache" ));
config.c +0 -24
@@ -2277,30 +2277,6 @@ int git_config_get_expiry_in_days(const char *key, timestamp_t *expiry, timestamreturn -1 ;/* thing exists but cannot be parsed */ } -int git_config_get_untracked_cache (void ) -{ -int val =-1 ; -const char *v; - -/* Hack for test programs like test-dump-untracked-cache */ -if (ignore_untracked_cache_config) -return -1 ; - -if (!git_config_get_maybe_bool ("core.untrackedcache" , &val)) -return val; - -if (!git_config_get_value ("core.untrackedcache" , &v)) { -if (!strcasecmp (v,"keep" )) -return -1 ; - -error (_ ("unknown core.untrackedCache value '%s'; " -"using 'keep' default value" ), v); -return -1 ; - } - -return -1 ;/* default value */ -} -int git_config_get_split_index (void ) {int val;
read-cache.c +9 -10
@@ -1845,18 +1845,17 @@ static void check_ce_order(struct index_state *istate)static void tweak_untracked_cache (struct index_state *istate) { -switch (git_config_get_untracked_cache ()) { -case -1 :/* keep: do nothing */ -break ; -case 0 :/* false */ +struct repository *r = the_repository; + +prepare_repo_settings (r); + +if (r->settings.core_untracked_cache ==UNTRACKED_CACHE_REMOVE ) {remove_untracked_cache (istate); -break ; -case 1 :/* true */ -add_untracked_cache (istate); -break ; -default :/* unknown value: do nothing */ -break ; +return ; } + +if (r->settings.core_untracked_cache ==UNTRACKED_CACHE_WRITE ) +add_untracked_cache (istate); }static void tweak_split_index (struct index_state *istate)
repo-settings.c +19 -0
@@ -7,6 +7,7 @@void prepare_repo_settings (struct repository *r) {int value; +char *strval;if (r->settings.initialized)return ;
@@ -23,7 +24,25 @@ void prepare_repo_settings(struct repository *r)if (!repo_config_get_bool (r,"index.version" , &value)) r->settings.index_version = value; +if (!repo_config_get_maybe_bool (r,"core.untrackedcache" , &value)) { +if (value ==0 ) + r->settings.core_untracked_cache =UNTRACKED_CACHE_REMOVE ; +else + r->settings.core_untracked_cache =UNTRACKED_CACHE_WRITE ; + }else if (!repo_config_get_string (r,"core.untrackedcache" , &strval)) { +if (!strcasecmp (strval,"keep" )) + r->settings.core_untracked_cache =UNTRACKED_CACHE_KEEP ; + +free (strval); + } +if (!repo_config_get_bool (r,"pack.usesparse" , &value)) r->settings.pack_use_sparse = value; + +/* Hack for test programs like test-dump-untracked-cache */ +if (ignore_untracked_cache_config) + r->settings.core_untracked_cache =UNTRACKED_CACHE_KEEP ; +else +UPDATE_DEFAULT_BOOL (r->settings.core_untracked_cache,UNTRACKED_CACHE_KEEP ); }
repository.h +8 -0
@@ -11,6 +11,13 @@ struct pathspec;struct raw_object_store ;struct submodule_cache ; +enum untracked_cache_setting { +UNTRACKED_CACHE_UNSET =-1 , +UNTRACKED_CACHE_REMOVE =0 , +UNTRACKED_CACHE_KEEP =1 , +UNTRACKED_CACHE_WRITE =2 +}; +struct repo_settings {int initialized;
@@ -18,6 +25,7 @@ struct repo_settings {int gc_write_commit_graph;int index_version; +enum untracked_cache_setting core_untracked_cache;int pack_use_sparse; };
Signature · 1 line
gitgitgadget
PATCH v4 5/6 repo-settings: create feature.manyFiles setting
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The feature.manyFiles setting is suitable for repos with many files in the working directory. By setting index.version=4 and core.untrackedCache=true, commands such as 'git status' should improve.
While adding this setting, modify the index version precedence tests to check how this setting overrides the default for index.version is unset.
--- Documentation/config.txt | 2 ++ Documentation/config/core.txt | 4 +++- Documentation/config/feature.txt | 15 +++++++++++++++ Documentation/config/index.txt | 1 + repo-settings.c | 5 ++++- t/t1600-index.sh | 31 ++++++++++++++++++++++++++----- 6 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 Documentation/config/feature.txt
Documentation/config.txt +2 -0
@@ -345,6 +345,8 @@ include::config/difftool.txt[] include::config/fastimport.txt[] +include::config/feature.txt[] + include::config/fetch.txt[] include::config/format.txt[]
Documentation/config/core.txt +3 -1
@@ -86,7 +86,9 @@ core.untrackedCache:: it will automatically be removed, if set to `false`. Before setting it to `true`, you should check that mtime is working properly on your system. - See linkgit:git-update-index[1]. `keep` by default. + See linkgit:git-update-index[1]. `keep` by default, unless + `feature.manyFiles` is enabled which sets this setting to + `true` by default. core.checkStat:: When missing or is set to `default`, many fields in the stat
Documentation/config/feature.txt new file +15 -0
@@ -0,0 +1,15 @@ +feature.*:: + The config settings that start with `feature.` modify the defaults of + a group of other config settings. These groups are created by the Git + developer community as recommended defaults and are subject to change. + In particular, new config options may be added with different defaults. + +feature.manyFiles:: + Enable config options that optimize for repos with many files in the + working directory. With many files, commands such as `git status` and + `git checkout` may be slow and these new defaults improve performance: ++ +* `index.version=4` enables path-prefix compression in the index. ++ +* `core.untrackedCache=true` enables the untracked cache. This setting assumes +that mtime is working on your machine.
Documentation/config/index.txt +1 -0
@@ -24,3 +24,4 @@ index.threads:: index.version:: Specify the version with which new index files should be initialized. This does not affect existing repositories. + If `feature.manyFiles` is enabled, then the default is 4.
repo-settings.c +4 -1
@@ -36,9 +36,12 @@ void prepare_repo_settings(struct repository *r)free (strval); } -if (!repo_config_get_bool (r,"pack.usesparse" , &value)) r->settings.pack_use_sparse = value; +if (!repo_config_get_bool (r,"feature.manyfiles" , &value) && value) { +UPDATE_DEFAULT_BOOL (r->settings.index_version,4 ); +UPDATE_DEFAULT_BOOL (r->settings.core_untracked_cache,UNTRACKED_CACHE_WRITE ); + }/* Hack for test programs like test-dump-untracked-cache */ if (ignore_untracked_cache_config)
t/t1600-index.sh +26 -5
@@ -59,17 +59,38 @@ test_expect_success 'out of bounds index.version issues warning' ' ) ' -test_expect_success 'GIT_INDEX_VERSION takes precedence over config' ' +test_index_version () { + INDEX_VERSION_CONFIG=$1 && + FEATURE_MANY_FILES=$2 && + ENV_VAR_VERSION=$3 + EXPECTED_OUTPUT_VERSION=$4 && ( rm -f .git/index && - GIT_INDEX_VERSION=4 && - export GIT_INDEX_VERSION && - git config --add index.version 2 && + rm -f .git/config && + if test "$INDEX_VERSION_CONFIG" -ne 0 + then + git config --add index.version $INDEX_VERSION_CONFIG + fi && + git config --add feature.manyFiles $FEATURE_MANY_FILES + if test "$ENV_VAR_VERSION" -ne 0 + then + GIT_INDEX_VERSION=$ENV_VAR_VERSION && + export GIT_INDEX_VERSION + else + unset GIT_INDEX_VERSION + fi && git add a 2>&1 && - echo 4 >expect && + echo $EXPECTED_OUTPUT_VERSION >expect && test-tool index-version <.git/index >actual && test_cmp expect actual ) +} + +test_expect_success 'index version config precedence' ' + test_index_version 2 false 4 4 && + test_index_version 2 true 0 2 && + test_index_version 0 true 0 4 && + test_index_version 0 true 2 2 ' test_done
Signature · 1 line
gitgitgadget
PATCH v4 6/6 repo-settings: create feature.experimental setting
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The 'feature.experimental' setting includes config options that are not committed to become defaults, but could use additional testing.
Update the following config settings to take new defaults, and to use the repo_settings struct if not already using them:
* 'pack.useSparse=true'
* 'fetch.negotiationAlgorithm=skipping'
In the case of fetch.negotiationAlgorithm, the existing logic would load the config option only when about to use the setting, so had a die() statement on an unknown string value. This is removed as now the config is parsed under prepare_repo_settings(). In general, this die() is probably misplaced and not valuable. A test was removed that checked this die() statement executed.
--- Documentation/config/feature.txt | 14 ++++++++++++++ Documentation/config/fetch.txt | 3 ++- Documentation/config/pack.txt | 3 ++- fetch-negotiator.c | 25 +++++++++++++------------ fetch-negotiator.h | 5 +++-- fetch-pack.c | 11 +++++------ repo-settings.c | 13 +++++++++++++ repository.h | 8 ++++++++ t/t5552-skipping-fetch-negotiator.sh | 23 ----------------------- 9 files changed, 60 insertions(+), 45 deletions(-)
Documentation/config/feature.txt +14 -0
@@ -4,6 +4,20 @@ feature.*:: developer community as recommended defaults and are subject to change. In particular, new config options may be added with different defaults. +feature.experimental:: + Enable config options that are new to Git, and are being considered for + future defaults. Config settings included here may be added or removed + with each release, including minor version updates. These settings may + have unintended interactions since they are so new. Please enable this + setting if you are interested in providing feedback on experimental + features. The new default values are: ++ +* `pack.useSparse=true` uses a new algorithm when constructing a pack-file +which can improve `git push` performance in repos with many files. ++ +* `fetch.negotiationAlgorithm=skipping` may improve fetch negotiation times by +skipping more commits at a time, reducing the number of round trips. + feature.manyFiles:: Enable config options that optimize for repos with many files in the working directory. With many files, commands such as `git status` and
Documentation/config/fetch.txt +2 -1
@@ -59,7 +59,8 @@ fetch.negotiationAlgorithm:: effort to converge faster, but may result in a larger-than-necessary packfile; The default is "default" which instructs Git to use the default algorithm that never skips commits (unless the server has acknowledged it or one - of its descendants). + of its descendants). If `feature.experimental` is enabled, then this + setting defaults to "skipping". Unknown values will cause 'git fetch' to error out. + See also the `--negotiation-tip` option for linkgit:git-fetch[1].
Documentation/config/pack.txt +2 -1
@@ -112,7 +112,8 @@ pack.useSparse:: objects. This can have significant performance benefits when computing a pack to send a small change. However, it is possible that extra objects are added to the pack-file if the included - commits contain certain types of direct renames. + commits contain certain types of direct renames. Default is `false` + unless `feature.experimental` is enabled. pack.writeBitmaps (deprecated):: This is a deprecated synonym for `repack.writeBitmaps`.
fetch-negotiator.c +13 -12
@@ -2,19 +2,20 @@#include "fetch-negotiator.h" #include "negotiator/default.h" #include "negotiator/skipping.h" +#include "repository.h" -void fetch_negotiator_init (struct fetch_negotiator *negotiator, -const char *algorithm) +void fetch_negotiator_init (struct repository *r, +struct fetch_negotiator *negotiator) { -if (algorithm) { -if (!strcmp (algorithm,"skipping" )) { -skipping_negotiator_init (negotiator); -return ; - }else if (!strcmp (algorithm,"default" )) { -/* Fall through to default initialization */ - }else { -die ("unknown fetch negotiation algorithm '%s'" , algorithm); - } +prepare_repo_settings (r); +switch (r->settings.fetch_negotiation_algorithm) { +case FETCH_NEGOTIATION_SKIPPING : +skipping_negotiator_init (negotiator); +return ; + +case FETCH_NEGOTIATION_DEFAULT : +default : +default_negotiator_init (negotiator); +return ; } -default_negotiator_init (negotiator); }
fetch-negotiator.h +3 -2
@@ -2,6 +2,7 @@#define FETCH_NEGOTIATOR_H struct commit ; +struct repository ;/* * An object that supplies the information needed to negotiate the contents of
@@ -52,7 +53,7 @@ struct fetch_negotiator { void *data; }; -void fetch_negotiator_init (struct fetch_negotiator *negotiator, -const char *algorithm); +void fetch_negotiator_init (struct repository *r, +struct fetch_negotiator *negotiator);#endif
fetch-pack.c +5 -6
@@ -36,7 +36,6 @@ static int agent_supported;static int server_supports_filtering;static struct lock_file shallow_lock;static const char *alternate_shallow_file; -static char *negotiation_algorithm;static struct strbuf fsck_msg_types =STRBUF_INIT ;/* Remember to update object flag allocation in object.h */
@@ -892,12 +891,13 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,struct shallow_info *si, char **pack_lockfile) { + struct repository *r = the_repository;struct ref *ref =copy_ref_list (orig_ref);struct object_id oid;const char *agent_feature;int agent_len;struct fetch_negotiator negotiator; -fetch_negotiator_init (&negotiator, negotiation_algorithm); +fetch_negotiator_init (r, &negotiator);sort_ref_list (&ref, ref_compare_name);QSORT (sought, nr_sought, cmp_ref_by_name);
@@ -911,7 +911,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,if (server_supports ("shallow" ))print_verbose (args,_ ("Server supports %s" ),"shallow" ); -else if (args->depth >0 ||is_repository_shallow (the_repository)) +else if (args->depth >0 ||is_repository_shallow (r))die (_ ("Server does not support shallow clients" ));if (args->depth >0 || args->deepen_since || args->deepen_not) args->deepen =1 ;
@@ -1379,6 +1379,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,struct shallow_info *si, char **pack_lockfile) { + struct repository *r = the_repository;struct ref *ref =copy_ref_list (orig_ref);enum fetch_state state =FETCH_CHECK_LOCAL ;struct oidset common =OIDSET_INIT ;
@@ -1386,7 +1387,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,int in_vain =0 ;int haves_to_send =INITIAL_FLUSH ;struct fetch_negotiator negotiator; -fetch_negotiator_init (&negotiator, negotiation_algorithm); +fetch_negotiator_init (r, &negotiator);packet_reader_init (&reader, fd[0 ],NULL ,0 ,PACKET_READ_CHOMP_NEWLINE |PACKET_READ_DIE_ON_ERR_PACKET );
@@ -1505,8 +1506,6 @@ static void fetch_pack_config(void)git_config_get_bool ("repack.usedeltabaseoffset" , &prefer_ofs_delta);git_config_get_bool ("fetch.fsckobjects" , &fetch_fsck_objects);git_config_get_bool ("transfer.fsckobjects" , &transfer_fsck_objects); -git_config_get_string ("fetch.negotiationalgorithm" , - &negotiation_algorithm);git_config (fetch_pack_config_cb,NULL ); }
repo-settings.c +13 -0
@@ -36,16 +36,29 @@ void prepare_repo_settings(struct repository *r)free (strval); } +if (!repo_config_get_string (r,"fetch.negotiationalgorithm" , &strval)) { +if (!strcasecmp (strval,"skipping" )) + r->settings.fetch_negotiation_algorithm =FETCH_NEGOTIATION_SKIPPING ; +else + r->settings.fetch_negotiation_algorithm =FETCH_NEGOTIATION_DEFAULT ; + } +if (!repo_config_get_bool (r,"pack.usesparse" , &value)) r->settings.pack_use_sparse = value;if (!repo_config_get_bool (r,"feature.manyfiles" , &value) && value) {UPDATE_DEFAULT_BOOL (r->settings.index_version,4 );UPDATE_DEFAULT_BOOL (r->settings.core_untracked_cache,UNTRACKED_CACHE_WRITE ); } +if (!repo_config_get_bool (r,"feature.experimental" , &value) && value) { +UPDATE_DEFAULT_BOOL (r->settings.pack_use_sparse,1 ); +UPDATE_DEFAULT_BOOL (r->settings.fetch_negotiation_algorithm,FETCH_NEGOTIATION_SKIPPING ); + }/* Hack for test programs like test-dump-untracked-cache */ if (ignore_untracked_cache_config) r->settings.core_untracked_cache =UNTRACKED_CACHE_KEEP ;else UPDATE_DEFAULT_BOOL (r->settings.core_untracked_cache,UNTRACKED_CACHE_KEEP ); + +UPDATE_DEFAULT_BOOL (r->settings.fetch_negotiation_algorithm,FETCH_NEGOTIATION_DEFAULT ); }
repository.h +8 -0
@@ -18,6 +18,13 @@ enum untracked_cache_setting {UNTRACKED_CACHE_WRITE =2 }; +enum fetch_negotiation_setting { +FETCH_NEGOTIATION_UNSET =-1 , +FETCH_NEGOTIATION_NONE =0 , +FETCH_NEGOTIATION_DEFAULT =1 , +FETCH_NEGOTIATION_SKIPPING =2 , +}; +struct repo_settings {int initialized;
@@ -28,6 +35,7 @@ struct repo_settings {enum untracked_cache_setting core_untracked_cache;int pack_use_sparse; +enum fetch_negotiation_setting fetch_negotiation_algorithm; };struct repository {
t/t5552-skipping-fetch-negotiator.sh +0 -23
@@ -60,29 +60,6 @@ test_expect_success 'commits with no parents are sent regardless of skip distanc have_not_sent c6 c4 c3 ' -test_expect_success 'unknown fetch.negotiationAlgorithm values error out' ' - rm -rf server client trace && - git init server && - test_commit -C server to_fetch && - - git init client && - test_commit -C client on_client && - git -C client checkout on_client && - - test_config -C client fetch.negotiationAlgorithm invalid && - test_must_fail git -C client fetch "$(pwd)/server" 2>err && - test_i18ngrep "unknown fetch negotiation algorithm" err && - - # Explicit "default" value - test_config -C client fetch.negotiationAlgorithm default && - git -C client -c fetch.negotiationAlgorithm=default fetch "$(pwd)/server" && - - # Implementation detail: If there is nothing to fetch, we will not error out - test_config -C client fetch.negotiationAlgorithm invalid && - git -C client fetch "$(pwd)/server" 2>err && - test_i18ngrep ! "unknown fetch negotiation algorithm" err -' - test_expect_success 'when two skips collide, favor the larger one' ' rm -rf server client trace && git init server &&
Signature · 1 line
gitgitgadget
Quoted 2 lines · Derrick Stolee via GitGitGadget
* Update docs and 0 constant in feature.experimental around merge.directoryRenames.
I somehow thought that the concensus was that *.experimental is for things that should become the future default, and this one is not something we would want to make the default.
Quoted 8 lines · Junio C Hamano
Quoted 2 lines · Derrick Stolee via GitGitGadget
* Update docs and 0 constant in feature.experimental around merge.directoryRenames.
I somehow thought that the concensus was that *.experimental is for things that should become the future default, and this one is not something we would want to make the default.
Ah, I see merge.directoryRenames is gone. Sorry for a confusion.
Quoted 4 lines · Junio C Hamano
Quoted 2 lines · Junio C Hamano
"Derrick Stolee via GitGitGadget" <[email protected]> writes:
Quoted 8 lines
Quoted 6 lines
Quoted 2 lines
* Update docs and 0 constant in feature.experimental around merge.directoryRenames.
I somehow thought that the concensus was that *.experimental is for things that should become the future default, and this one is not something we would want to make the default.
Ah, I see merge.directoryRenames is gone. Sorry for a confusion.
Confusion is my fault. This was a terrible way to say, "I cleaned up the adjustments to merge.directoryRenames AND removed it from features.experimental"
Thanks,
-Stolee
Quoted 8 lines · Derrick Stolee
Quoted 7 lines · Junio C Hamano
Quoted 5 lines · Junio C Hamano
Quoted 2 lines · Derrick Stolee via GitGitGadget
* Update docs and 0 constant in feature.experimental around merge.directoryRenames.
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
I somehow thought that the concensus was that *.experimental is for things that should become the future default, and this one is not something we would want to make the default.
Ah, I see merge.directoryRenames is gone. Sorry for a confusion.
Confusion is my fault. This was a terrible way to say, "I cleaned up the adjustments to merge.directoryRenames AND removed it from features.experimental"
It's too early for me to say things... let's try this again.
1. merge.directoryRenames is removed from this series.
2. The commit where I did the cleanup according to Elijahs' recommendation is available as a tag, as mentioned in [1].