{"thread":{"id":"51228","subject":"[PATCH 00/11] [RFC] Create 'core.size=large' setting to update config defaults","startedAt":"2019-06-03T20:18:22Z","lastAt":"2019-07-22T12:11:03Z","messageCount":48,"participants":["Derrick Stolee via GitGitGadget","Jeff Hostetler via GitGitGadget","Derrick Stolee","Jeff Hostetler","Johannes Schindelin","Junio C Hamano","Carlo Arenas","Duy Nguyen","Ævar Arnfjörð Bjarmason","Jakub Narebski","Taylor Blau"],"isPatch":true,"patchVersion":1,"patchTotal":11},"messages":[{"id":"376599","messageId":"pull.254.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":null,"subject":"[PATCH 00/11] [RFC] Create 'core.size=large' setting to update config defaults","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-06-03T20:18:18Z","receivedAt":"2019-06-03T20:18:22Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"This patch series includes a few new config options we created to speed up\ncertain critical commands in VFS for Git. On their own, they would\ncontribute little value as it is hard to discover new config variables.\nInstead, I've created this RFC as a goal for probably three sequential patch\nseries:\n\n 1. (Patches 1-3) Introduce a new 'core.size' config setting that takes\n    'large' as a value. This enables several config values that are\n    beneficial for large repos. We use a certain set in VFS for Git (see\n    [1]), and most of those are applicable to any repo. This 'core.size'\n    setting is intended for users to automatically receive performance\n    updates as soon as they are stable, but they must opt-in to the setting\n    and can always explicitly set their own config values. The settings to\n    include here are core.commitGraph=true, gc.writeCommitGraph=true,\n    index.version=4, pack.useSparse=true.\n    \n    \n 2. (Patches 4-8) Introduce 'status.aheadBehind' to dictate if we use\n    '--[no-]ahead-behind' during 'git status' calls. Also do some cleanup on\n    the feature around porcelain formats. I adapted Jeff Hostetler's commits\n    from microsoft/git for this section.\n    \n    \n 3. (Patches 9-12) Introduce 'fetch.showForcedUpdates' and the associated\n    '--[no-]show-forced-updates' option for 'git fetch' and 'git pull'\n    calls. When fetching from a remote with many branches that move quickly,\n    the check for forced updates can be expensive. Further, the only effects\n    are a \"(forced update)\" indicator to stdout and a single bit in the\n    reflog. The reflog bit is unfortunate to lose, but it is never trusted\n    for important actions. These changes are likely to be more controversial\n    than the others.\n    \n    \n\nHopefully this direction is amenable to allow \"early adopters\" gain access\nto new performance features even if they are not necessary reading every\nline of the release notes.\n\nThanks, -Stolee\n\n[1] \nhttps://github.com/microsoft/VFSForGit/blob/6a7fd2ff50056b73b347b882d2b8d52939bd6419/GVFS/GVFS/CommandLine/GVFSVerb.cs#L122-L152\nThis code includes the settings we enable by default in VFS for Git\nenlistments.\n\nDerrick Stolee (8):\n  repo-settings: create repo.size=large setting\n  repo-settings: use index.version=4 by default\n  repo-settings: pack.useSparse=true\n  repo-settings: status.aheadBehind=false\n  fetch: add --[no-]show-forced-updates argument\n  fetch: warn about forced updates after branch list\n  pull: add --[no-]show-forced-updates passthrough to fetch\n  repo-settings: fetch.showForcedUpdates=false\n\nJeff Hostetler (3):\n  status: add status.aheadbehind setting\n  status: add warning when a/b calculation takes too long for\n    long/normal format\n  status: ignore status.aheadbehind in porcelain formats\n\n Documentation/config/core.txt   | 31 ++++++++++++++-\n Documentation/config/fetch.txt  |  5 +++\n Documentation/config/gc.txt     |  4 +-\n Documentation/config/index.txt  |  1 +\n Documentation/config/pack.txt   |  3 +-\n Documentation/config/status.txt |  6 +++\n Documentation/fetch-options.txt | 13 +++++++\n Makefile                        |  1 +\n advice.c                        |  2 +\n advice.h                        |  1 +\n builtin/commit.c                | 19 ++++++++-\n builtin/fetch.c                 | 34 ++++++++++++++++-\n builtin/gc.c                    |  6 +--\n builtin/pack-objects.c          |  9 +++--\n builtin/pull.c                  |  7 ++++\n commit-graph.c                  |  7 ++--\n read-cache.c                    | 12 +++---\n repo-settings.c                 | 68 +++++++++++++++++++++++++++++++++\n repo-settings.h                 | 17 +++++++++\n repository.h                    |  3 ++\n t/t6040-tracking-info.sh        | 31 +++++++++++++++\n t/t7064-wtstatus-pv2.sh         |  8 ++++\n wt-status.c                     | 17 +++++++++\n 23 files changed, 283 insertions(+), 22 deletions(-)\n create mode 100644 repo-settings.c\n create mode 100644 repo-settings.h\n\n\nbase-commit: aa25c82427ae70aebf3b8f970f2afd54e9a2a8c6\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-254%2Fderrickstolee%2Fconfig-large%2Fupstream-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-254/derrickstolee/config-large/upstream-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/254\n-- \ngitgitgadget\n"},{"id":"376600","messageId":"704613f4480e3b9aacab91d2241247791f34ce22.1559593097.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.git.gitgitgadget@gmail.com","subject":"[PATCH 01/11] repo-settings: create repo.size=large setting","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-06-03T20:18:19Z","receivedAt":"2019-06-03T20:18:23Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nSeveral advanced config settings are highly recommended for clients\nusing large repositories. Power users learn these one-by-one and\nenable them as they see fit. This could be made simpler, to allow\nmore users to have access to these almost-always beneficial features\n(and more beneficial in larger repos).\n\nCreate a 'repo.size' config setting whose only accepted value is\n'large'. When a repo.size=large is given, change the default values\nof some config settings. If the setting is given explicitly, then\ntake the explicit value.\n\nThis change adds these two defaults to the repo.size=large setting:\n\n * core.commitGraph=true\n * gc.writeCommitGraph=true\n\nTo centralize these config options and properly set the defaults,\ncreate a repo_settings that contains chars for each config variable.\nUse -1 as \"unset\", with 0 for false and 1 for true.\n\nThe prepare_repo_settings() method ensures that this settings\nstruct has been initialized, and avoids double-scanning the config\nsettings.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/config/core.txt | 16 +++++++++++--\n Documentation/config/gc.txt   |  4 ++--\n Makefile                      |  1 +\n builtin/gc.c                  |  6 ++---\n commit-graph.c                |  7 +++---\n repo-settings.c               | 44 +++++++++++++++++++++++++++++++++++\n repo-settings.h               | 13 +++++++++++\n repository.h                  |  3 +++\n 8 files changed, 84 insertions(+), 10 deletions(-)\n create mode 100644 repo-settings.c\n create mode 100644 repo-settings.h\n\ndiff --git a/Documentation/config/core.txt b/Documentation/config/core.txt\nindex 75538d27e7..1a188db620 100644\n--- a/Documentation/config/core.txt\n+++ b/Documentation/config/core.txt\n@@ -577,8 +577,9 @@ the `GIT_NOTES_REF` environment variable.  See linkgit:git-notes[1].\n \n core.commitGraph::\n \tIf true, then git will read the commit-graph file (if it exists)\n-\tto parse the graph structure of commits. Defaults to false. See\n-\tlinkgit:git-commit-graph[1] for more information.\n+\tto parse the graph structure of commits. Defaults to false, unless\n+\t`core.size=large`. See linkgit:git-commit-graph[1] for more\n+\tinformation.\n \n core.useReplaceRefs::\n \tIf set to `false`, behave as if the `--no-replace-objects`\n@@ -601,3 +602,14 @@ core.abbrev::\n \tin your repository, which hopefully is enough for\n \tabbreviated object names to stay unique for some time.\n \tThe minimum length is 4.\n+\n+core.size::\n+\tWhen specified as \"large\", change the default values of some config\n+\tvariables to improve performance in a large repository. If a variable\n+\tis specified explicitly, the explicit value will override these\n+\tdefaults:\n++\n+* `core.commitGraph=true` enables reading commit-graph files.\n++\n+* `gc.writeCommitGraph=true` eneables writing commit-graph files during\n+`git gc`.\ndiff --git a/Documentation/config/gc.txt b/Documentation/config/gc.txt\nindex 02b92b18b5..680721ebbb 100644\n--- a/Documentation/config/gc.txt\n+++ b/Documentation/config/gc.txt\n@@ -63,8 +63,8 @@ gc.writeCommitGraph::\n \tIf true, then gc will rewrite the commit-graph file when\n \tlinkgit:git-gc[1] is run. When using `git gc --auto`\n \tthe commit-graph will be updated if housekeeping is\n-\trequired. Default is false. See linkgit:git-commit-graph[1]\n-\tfor details.\n+\trequired. Default is false, unless `core.size=large`.\n+\tSee linkgit:git-commit-graph[1] for details.\n \n gc.logExpiry::\n \tIf the file gc.log exists, then `git gc --auto` will print\ndiff --git a/Makefile b/Makefile\nindex 8a7e235352..2d3499d7ac 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -967,6 +967,7 @@ LIB_OBJS += refspec.o\n LIB_OBJS += ref-filter.o\n LIB_OBJS += remote.o\n LIB_OBJS += replace-object.o\n+LIB_OBJS += repo-settings.o\n LIB_OBJS += repository.o\n LIB_OBJS += rerere.o\n LIB_OBJS += resolve-undo.o\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex 8943bcc300..6281aad961 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -27,6 +27,7 @@\n #include \"pack-objects.h\"\n #include \"blob.h\"\n #include \"tree.h\"\n+#include \"repo-settings.h\"\n \n #define FAILED_RUN \"failed to run %s\"\n \n@@ -41,7 +42,6 @@ static int aggressive_depth = 50;\n static int aggressive_window = 250;\n static int gc_auto_threshold = 6700;\n static int gc_auto_pack_limit = 50;\n-static int gc_write_commit_graph;\n static int detach_auto = 1;\n static timestamp_t gc_log_expire_time;\n static const char *gc_log_expire = \"1.day.ago\";\n@@ -148,7 +148,6 @@ static void gc_config(void)\n \tgit_config_get_int(\"gc.aggressivedepth\", &aggressive_depth);\n \tgit_config_get_int(\"gc.auto\", &gc_auto_threshold);\n \tgit_config_get_int(\"gc.autopacklimit\", &gc_auto_pack_limit);\n-\tgit_config_get_bool(\"gc.writecommitgraph\", &gc_write_commit_graph);\n \tgit_config_get_bool(\"gc.autodetach\", &detach_auto);\n \tgit_config_get_expiry(\"gc.pruneexpire\", &prune_expire);\n \tgit_config_get_expiry(\"gc.worktreepruneexpire\", &prune_worktrees_expire);\n@@ -685,7 +684,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)\n \t\tclean_pack_garbage();\n \t}\n \n-\tif (gc_write_commit_graph)\n+\tprepare_repo_settings(the_repository);\n+\tif (the_repository->settings->gc_write_commit_graph == 1)\n \t\twrite_commit_graph_reachable(get_object_directory(), 0,\n \t\t\t\t\t     !quiet && !daemonized);\n \ndiff --git a/commit-graph.c b/commit-graph.c\nindex 7c5e54875f..b09c465a7a 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -16,6 +16,7 @@\n #include \"hashmap.h\"\n #include \"replace-object.h\"\n #include \"progress.h\"\n+#include \"repo-settings.h\"\n \n #define GRAPH_SIGNATURE 0x43475048 /* \"CGPH\" */\n #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* \"OIDF\" */\n@@ -311,7 +312,6 @@ static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)\n static int prepare_commit_graph(struct repository *r)\n {\n \tstruct object_directory *odb;\n-\tint config_value;\n \n \tif (git_env_bool(GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD, 0))\n \t\tdie(\"dying as requested by the '%s' variable on commit-graph load!\",\n@@ -321,9 +321,10 @@ static int prepare_commit_graph(struct repository *r)\n \t\treturn !!r->objects->commit_graph;\n \tr->objects->commit_graph_attempted = 1;\n \n+\tprepare_repo_settings(r);\n+\n \tif (!git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&\n-\t    (repo_config_get_bool(r, \"core.commitgraph\", &config_value) ||\n-\t    !config_value))\n+\t    r->settings->core_commit_graph != 1)\n \t\t/*\n \t\t * This repository is not configured to use commit graphs, so\n \t\t * do not load one. (But report commit_graph_attempted anyway\ndiff --git a/repo-settings.c b/repo-settings.c\nnew file mode 100644\nindex 0000000000..6f5e18d92e\n--- /dev/null\n+++ b/repo-settings.c\n@@ -0,0 +1,44 @@\n+#include \"cache.h\"\n+#include \"repository.h\"\n+#include \"config.h\"\n+#include \"repo-settings.h\"\n+\n+\n+#define UPDATE_DEFAULT(s,v) if (s != -1) { s = v; }\n+\n+static int git_repo_config(const char *key, const char *value, void *cb)\n+{\n+\tstruct repo_settings *rs = (struct repo_settings *)cb;\n+\n+\tif (!strcmp(key, \"core.size\")) {\n+\t\tif (!strcmp(value, \"large\")) {\n+\t\t\tUPDATE_DEFAULT(rs->core_commit_graph, 1);\n+\t\t\tUPDATE_DEFAULT(rs->gc_write_commit_graph, 1);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(key, \"core.commitgraph\")) {\n+\t\trs->core_commit_graph = git_config_bool(key, value);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(key, \"gc.writecommitgraph\")) {\n+\t\trs->gc_write_commit_graph = git_config_bool(key, value);\n+\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+void prepare_repo_settings(struct repository *r)\n+{\n+\tif (r->settings)\n+\t\treturn;\n+\n+\tr->settings = xmalloc(sizeof(*r->settings));\n+\n+\t/* Defaults */\n+\tr->settings->core_commit_graph = -1;\n+\tr->settings->gc_write_commit_graph = -1;\n+\n+\trepo_config(r, git_repo_config, r->settings);\n+}\ndiff --git a/repo-settings.h b/repo-settings.h\nnew file mode 100644\nindex 0000000000..11d08648e1\n--- /dev/null\n+++ b/repo-settings.h\n@@ -0,0 +1,13 @@\n+#ifndef REPO_SETTINGS_H\n+#define REPO_SETTINGS_H\n+\n+struct repo_settings {\n+\tchar core_commit_graph;\n+\tchar gc_write_commit_graph;\n+};\n+\n+struct repository;\n+\n+void prepare_repo_settings(struct repository *r);\n+\n+#endif /* REPO_SETTINGS_H */\ndiff --git a/repository.h b/repository.h\nindex 4fb6a5885f..352afc9cd8 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -4,6 +4,7 @@\n #include \"path.h\"\n \n struct config_set;\n+struct repo_settings;\n struct git_hash_algo;\n struct index_state;\n struct lock_file;\n@@ -72,6 +73,8 @@ struct repository {\n \t */\n \tchar *submodule_prefix;\n \n+\tstruct repo_settings *settings;\n+\n \t/* Subsystems */\n \t/*\n \t * Repository's config which contains key-value pairs from the usual\n-- \ngitgitgadget\n\n"},{"id":"376601","messageId":"d5f5d7453c8135cf0c1186447de4323d31d4eca8.1559593097.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.git.gitgitgadget@gmail.com","subject":"[PATCH 02/11] repo-settings: use index.version=4 by default","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-06-03T20:18:20Z","receivedAt":"2019-06-03T20:18:27Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nIf a repo is large, it likely has many paths in its working directory.\nThis means the index could be compressed using version 4. Set this as\na default when core.size=large.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/config/core.txt  |  3 +++\n Documentation/config/index.txt |  1 +\n read-cache.c                   | 12 +++++++-----\n repo-settings.c                |  6 ++++++\n repo-settings.h                |  1 +\n 5 files changed, 18 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config/core.txt b/Documentation/config/core.txt\nindex 1a188db620..ea64f675fa 100644\n--- a/Documentation/config/core.txt\n+++ b/Documentation/config/core.txt\n@@ -613,3 +613,6 @@ core.size::\n +\n * `gc.writeCommitGraph=true` eneables writing commit-graph files during\n `git gc`.\n++\n+* `index.version=4` uses prefix-compression to reduce the size of the\n+.git/index file.\ndiff --git a/Documentation/config/index.txt b/Documentation/config/index.txt\nindex f181503041..d4b56925c4 100644\n--- a/Documentation/config/index.txt\n+++ b/Documentation/config/index.txt\n@@ -24,3 +24,4 @@ index.threads::\n index.version::\n \tSpecify the version with which new index files should be\n \tinitialized.  This does not affect existing repositories.\n+\tIf `core.size=large`, then the default value is 4.\ndiff --git a/read-cache.c b/read-cache.c\nindex 22e7b9944e..7fab8ff748 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -25,6 +25,7 @@\n #include \"fsmonitor.h\"\n #include \"thread-utils.h\"\n #include \"progress.h\"\n+#include \"repo-settings.h\"\n \n /* Mask for the name length in ce_flags in the on-disk index */\n \n@@ -1599,16 +1600,17 @@ struct cache_entry *refresh_cache_entry(struct index_state *istate,\n \n #define INDEX_FORMAT_DEFAULT 3\n \n-static unsigned int get_index_format_default(void)\n+static unsigned int get_index_format_default(struct repository *r)\n {\n \tchar *envversion = getenv(\"GIT_INDEX_VERSION\");\n \tchar *endp;\n-\tint value;\n \tunsigned int version = INDEX_FORMAT_DEFAULT;\n \n \tif (!envversion) {\n-\t\tif (!git_config_get_int(\"index.version\", &value))\n-\t\t\tversion = value;\n+\t\tprepare_repo_settings(r);\n+\n+\t\tif (r->settings->index_version >= 0)\n+\t\t\tversion = r->settings->index_version;\n \t\tif (version < INDEX_FORMAT_LB || INDEX_FORMAT_UB < version) {\n \t\t\twarning(_(\"index.version set, but the value is invalid.\\n\"\n \t\t\t\t  \"Using version %i\"), INDEX_FORMAT_DEFAULT);\n@@ -2765,7 +2767,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,\n \t}\n \n \tif (!istate->version) {\n-\t\tistate->version = get_index_format_default();\n+\t\tistate->version = get_index_format_default(the_repository);\n \t\tif (git_env_bool(\"GIT_TEST_SPLIT_INDEX\", 0))\n \t\t\tinit_split_index(istate);\n \t}\ndiff --git a/repo-settings.c b/repo-settings.c\nindex 6f5e18d92e..7e6e65d60c 100644\n--- a/repo-settings.c\n+++ b/repo-settings.c\n@@ -14,6 +14,7 @@ static int git_repo_config(const char *key, const char *value, void *cb)\n \t\tif (!strcmp(value, \"large\")) {\n \t\t\tUPDATE_DEFAULT(rs->core_commit_graph, 1);\n \t\t\tUPDATE_DEFAULT(rs->gc_write_commit_graph, 1);\n+\t\t\tUPDATE_DEFAULT(rs->index_version, 4);\n \t\t}\n \t\treturn 0;\n \t}\n@@ -25,6 +26,10 @@ static int git_repo_config(const char *key, const char *value, void *cb)\n \t\trs->gc_write_commit_graph = git_config_bool(key, value);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(key, \"index.version\")) {\n+\t\trs->index_version = git_config_int(key, value);\n+\t\treturn 0;\n+\t}\n \n \treturn 1;\n }\n@@ -39,6 +44,7 @@ void prepare_repo_settings(struct repository *r)\n \t/* Defaults */\n \tr->settings->core_commit_graph = -1;\n \tr->settings->gc_write_commit_graph = -1;\n+\tr->settings->index_version = -1;\n \n \trepo_config(r, git_repo_config, r->settings);\n }\ndiff --git a/repo-settings.h b/repo-settings.h\nindex 11d08648e1..9b8104042e 100644\n--- a/repo-settings.h\n+++ b/repo-settings.h\n@@ -4,6 +4,7 @@\n struct repo_settings {\n \tchar core_commit_graph;\n \tchar gc_write_commit_graph;\n+\tint index_version;\n };\n \n struct repository;\n-- \ngitgitgadget\n\n"},{"id":"376602","messageId":"d2e5cf185711385643999493fcfbcf03afe13e34.1559593097.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.git.gitgitgadget@gmail.com","subject":"[PATCH 05/11] status: add warning when a/b calculation takes too long for long/normal format","fromName":"Jeff Hostetler via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-06-03T20:18:22Z","receivedAt":"2019-06-03T20:18:27Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n advice.c    |  2 ++\n advice.h    |  1 +\n wt-status.c | 17 +++++++++++++++++\n 3 files changed, 20 insertions(+)\n\ndiff --git a/advice.c b/advice.c\nindex ce5f374ecd..54f8dea30c 100644\n--- a/advice.c\n+++ b/advice.c\n@@ -12,6 +12,7 @@ int advice_push_needs_force = 1;\n int advice_push_unqualified_ref_name = 1;\n int advice_status_hints = 1;\n int advice_status_u_option = 1;\n+int advice_status_ahead_behind_warning = 1;\n int advice_commit_before_merge = 1;\n int advice_reset_quiet_warning = 1;\n int advice_resolve_conflict = 1;\n@@ -68,6 +69,7 @@ static struct {\n \t{ \"pushUnqualifiedRefName\", &advice_push_unqualified_ref_name },\n \t{ \"statusHints\", &advice_status_hints },\n \t{ \"statusUoption\", &advice_status_u_option },\n+\t{ \"statusAheadBehindWarning\", &advice_status_ahead_behind_warning },\n \t{ \"commitBeforeMerge\", &advice_commit_before_merge },\n \t{ \"resetQuiet\", &advice_reset_quiet_warning },\n \t{ \"resolveConflict\", &advice_resolve_conflict },\ndiff --git a/advice.h b/advice.h\nindex e50f02cdfe..c86de9b9b8 100644\n--- a/advice.h\n+++ b/advice.h\n@@ -12,6 +12,7 @@ extern int advice_push_needs_force;\n extern int advice_push_unqualified_ref_name;\n extern int advice_status_hints;\n extern int advice_status_u_option;\n+extern int advice_status_ahead_behind_warning;\n extern int advice_commit_before_merge;\n extern int advice_reset_quiet_warning;\n extern int advice_resolve_conflict;\ndiff --git a/wt-status.c b/wt-status.c\nindex d2a1bec226..c94d43879a 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -19,6 +19,8 @@\n #include \"lockfile.h\"\n #include \"sequencer.h\"\n \n+#define AB_DELAY_WARNING_IN_MS (2 * 1000)\n+\n static const char cut_line[] =\n \"------------------------ >8 ------------------------\\n\";\n \n@@ -1085,14 +1087,29 @@ static void wt_longstatus_print_tracking(struct wt_status *s)\n \tstruct branch *branch;\n \tchar comment_line_string[3];\n \tint i;\n+\tuint64_t t_begin = 0;\n \n \tassert(s->branch && !s->is_initial);\n \tif (!skip_prefix(s->branch, \"refs/heads/\", &branch_name))\n \t\treturn;\n \tbranch = branch_get(branch_name);\n+\n+\tt_begin = getnanotime();\n+\n \tif (!format_tracking_info(branch, &sb, s->ahead_behind_flags))\n \t\treturn;\n \n+\tif (advice_status_ahead_behind_warning &&\n+\t    s->ahead_behind_flags == AHEAD_BEHIND_FULL) {\n+\t\tuint64_t t_delta_in_ms = (getnanotime() - t_begin) / 1000000;\n+\t\tif (t_delta_in_ms > AB_DELAY_WARNING_IN_MS) {\n+\t\t\tstrbuf_addf(&sb, _(\"\\n\"\n+\t\t\t\t\t   \"It took %.2f seconds to compute the branch ahead/behind values.\\n\"\n+\t\t\t\t\t   \"You can use '--no-ahead-behind' to avoid this.\\n\"),\n+\t\t\t\t    t_delta_in_ms / 1000.0);\n+\t\t}\n+\t}\n+\n \ti = 0;\n \tif (s->display_comment_prefix) {\n \t\tcomment_line_string[i++] = comment_line_char;\n-- \ngitgitgadget\n\n"},{"id":"376603","messageId":"6a80cfa5a5604eb4560e8ad7254a982caa894678.1559593097.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.git.gitgitgadget@gmail.com","subject":"[PATCH 09/11] fetch: warn about forced updates after branch list","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-06-03T20:18:25Z","receivedAt":"2019-06-03T20:18:29Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/fetch.c | 25 ++++++++++++++++++++++++-\n 1 file changed, 24 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 571c255218..f8ff98fdaf 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -24,6 +24,8 @@\n #include \"list-objects-filter-options.h\"\n #include \"commit-reach.h\"\n \n+#define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)\n+\n static const char * const builtin_fetch_usage[] = {\n \tN_(\"git fetch [<options>] [<repository> [<refspec>...]]\"),\n \tN_(\"git fetch [<options>] <group>\"),\n@@ -40,6 +42,7 @@ enum {\n \n static int fetch_prune_config = -1; /* unspecified */\n static int fetch_show_forced_updates = 1;\n+static uint64_t forced_updates_ms = 0;\n static int prune = -1; /* unspecified */\n #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */\n \n@@ -707,6 +710,7 @@ static int update_local_ref(struct ref *ref,\n \tenum object_type type;\n \tstruct branch *current_branch = branch_get(NULL);\n \tconst char *pretty_ref = prettify_refname(ref->name);\n+\tint fast_forward = 0;\n \n \ttype = oid_object_info(the_repository, &ref->new_oid, NULL);\n \tif (type < 0)\n@@ -781,7 +785,15 @@ static int update_local_ref(struct ref *ref,\n \t\treturn r;\n \t}\n \n-\tif (!fetch_show_forced_updates || in_merge_bases(current, updated)) {\n+\tif (fetch_show_forced_updates) {\n+\t\tuint64_t t_before = getnanotime();\n+\t\tfast_forward = in_merge_bases(current, updated);\n+\t\tforced_updates_ms += (getnanotime() - t_before) / 1000000;\n+\t} else {\n+\t\tfast_forward = 1;\n+\t}\n+\n+\tif (fast_forward) {\n \t\tstruct strbuf quickref = STRBUF_INIT;\n \t\tint r;\n \n@@ -980,6 +992,17 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,\n \t\t      \" 'git remote prune %s' to remove any old, conflicting \"\n \t\t      \"branches\"), remote_name);\n \n+\tif (!fetch_show_forced_updates) {\n+\t\twarning(_(\"Fetch normally indicates which branches had a forced update, but that check has been disabled.\"));\n+\t\twarning(_(\"To re-enable, use '--show-forced-updates' flag or run 'git config fetch.showForcedUpdates true'.\"));\n+\t}\n+\tif (fetch_show_forced_updates &&\n+\t    forced_updates_ms > FORCED_UPDATES_DELAY_WARNING_IN_MS) {\n+\t\twarning(_(\"It took %.2f seconds to check forced updates. You can use '--no-show-forced-updates'\\n\"),\n+\t\t\tforced_updates_ms / 1000.0);\n+\t\twarning(_(\"or run 'git config fetch.showForcedUpdates false' to avoid this check.\\n\"));\n+\t}\n+\n  abort:\n \tstrbuf_release(&note);\n \tfree(url);\n-- \ngitgitgadget\n\n"},{"id":"376604","messageId":"936fae31b712f5cce52258619b07d35356903bc6.1559593097.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.git.gitgitgadget@gmail.com","subject":"[PATCH 07/11] repo-settings: status.aheadBehind=false","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-06-03T20:18:23Z","receivedAt":"2019-06-03T20:18:30Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nWhen a repo has many active developers, the commit history can grow\nvery quickly. This can lead remote branches from being very far from\ntheir local copies.\n\nSet stats.aheadBehind=false by default when core.size=large, so all\n'git status' calls have an implied '--no-ahead-behind' argument.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/config/core.txt   |  3 +++\n Documentation/config/status.txt |  3 ++-\n builtin/commit.c                | 12 ++++++------\n repo-settings.c                 |  6 ++++++\n repo-settings.h                 |  1 +\n 5 files changed, 18 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/config/core.txt b/Documentation/config/core.txt\nindex df357f5af5..6bed956a08 100644\n--- a/Documentation/config/core.txt\n+++ b/Documentation/config/core.txt\n@@ -620,3 +620,6 @@ core.size::\n * `pack.useSparse=true` uses the sparse tree-walk algorithm, which is\n optimized for enumerating objects during linkgit:git-push[1] from a\n client machine.\n++\n+* `status.aheadBehind=false` enables `--no-ahead-behind` by default during\n+linkgit:git-status[1] calls, saving time in a fast-moving commit history.\ndiff --git a/Documentation/config/status.txt b/Documentation/config/status.txt\nindex 0fc704ab80..3e39019810 100644\n--- a/Documentation/config/status.txt\n+++ b/Documentation/config/status.txt\n@@ -15,7 +15,8 @@ status.branch::\n status.aheadBehind::\n \tSet to true to enable `--ahead-behind` and false to enable\n \t`--no-ahead-behind` by default in linkgit:git-status[1] for\n-\tnon-porcelain status formats.  Defaults to true.\n+\tnon-porcelain status formats.  Defaults to true, unless\n+\t`core.size=large`.\n \n status.displayCommentPrefix::\n \tIf set to true, linkgit:git-status[1] will insert a comment\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 79cb238d87..246a802167 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -36,6 +36,7 @@\n #include \"help.h\"\n #include \"commit-reach.h\"\n #include \"commit-graph.h\"\n+#include \"repo-settings.h\"\n \n static const char * const builtin_commit_usage[] = {\n \tN_(\"git commit [<options>] [--] <pathspec>...\"),\n@@ -1117,8 +1118,11 @@ static void finalize_deferred_config(struct wt_status *s)\n \t * in particular), we inherit _FULL for backwards compatibility.\n \t */\n \tif (use_deferred_config &&\n-\t    s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)\n-\t\ts->ahead_behind_flags = status_deferred_config.ahead_behind;\n+\t    s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED) {\n+\t\tprepare_repo_settings(the_repository);\n+\t\tif (the_repository->settings->status_ahead_behind != -1)\n+\t\t\ts->ahead_behind_flags = the_repository->settings->status_ahead_behind;\n+\t}\n \n \tif (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)\n \t\ts->ahead_behind_flags = AHEAD_BEHIND_FULL;\n@@ -1259,10 +1263,6 @@ static int git_status_config(const char *k, const char *v, void *cb)\n \t\tstatus_deferred_config.show_branch = git_config_bool(k, v);\n \t\treturn 0;\n \t}\n-\tif (!strcmp(k, \"status.aheadbehind\")) {\n-\t\tstatus_deferred_config.ahead_behind = git_config_bool(k, v);\n-\t\treturn 0;\n-\t}\n \tif (!strcmp(k, \"status.showstash\")) {\n \t\ts->show_stash = git_config_bool(k, v);\n \t\treturn 0;\ndiff --git a/repo-settings.c b/repo-settings.c\nindex 026ab9c1a0..b3d4b50b72 100644\n--- a/repo-settings.c\n+++ b/repo-settings.c\n@@ -15,6 +15,7 @@ static int git_repo_config(const char *key, const char *value, void *cb)\n \t\t\tUPDATE_DEFAULT(rs->core_commit_graph, 1);\n \t\t\tUPDATE_DEFAULT(rs->gc_write_commit_graph, 1);\n \t\t\tUPDATE_DEFAULT(rs->pack_use_sparse, 1);\n+\t\t\tUPDATE_DEFAULT(rs->status_ahead_behind, 1);\n \t\t\tUPDATE_DEFAULT(rs->index_version, 4);\n \t\t}\n \t\treturn 0;\n@@ -31,6 +32,10 @@ static int git_repo_config(const char *key, const char *value, void *cb)\n \t\trs->pack_use_sparse = git_config_bool(key, value);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(key, \"status.aheadbehind\")) {\n+\t\trs->status_ahead_behind = git_config_bool(key, value);\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(key, \"index.version\")) {\n \t\trs->index_version = git_config_int(key, value);\n \t\treturn 0;\n@@ -50,6 +55,7 @@ void prepare_repo_settings(struct repository *r)\n \tr->settings->core_commit_graph = -1;\n \tr->settings->gc_write_commit_graph = -1;\n \tr->settings->pack_use_sparse = -1;\n+\tr->settings->status_ahead_behind = -1;\n \tr->settings->index_version = -1;\n \n \trepo_config(r, git_repo_config, r->settings);\ndiff --git a/repo-settings.h b/repo-settings.h\nindex b50228f992..cc358a083a 100644\n--- a/repo-settings.h\n+++ b/repo-settings.h\n@@ -5,6 +5,7 @@ struct repo_settings {\n \tchar core_commit_graph;\n \tchar gc_write_commit_graph;\n \tchar pack_use_sparse;\n+\tchar status_ahead_behind;\n \tint index_version;\n };\n \n-- \ngitgitgadget\n\n"},{"id":"376605","messageId":"d4ff987ad950325ccae7b32571c4a66d5eecc851.1559593097.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.git.gitgitgadget@gmail.com","subject":"[PATCH 11/11] repo-settings: fetch.showForcedUpdates=false","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-06-03T20:18:26Z","receivedAt":"2019-06-03T20:18:31Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nWhen a repo has many active contributors, the commit history can\ngrow very quickly and there can be many branches. During 'git fetch',\nafter downloading the pack from the remote, the default behavior\nchecks each remote ref for a forced update. This presents a\nsomewhat quadratic performance hit, as the time it takes to walk these\ncommits can be on the order of the number of branches times the\n\"commit velocity\" per branch.\n\nSet fetch.showForcedUpdates=false when core.size=large to enable\nthe --no-show-forced-updates option by default for fetch and pull\ncommands.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/config/core.txt  |  5 +++++\n Documentation/config/fetch.txt |  2 +-\n builtin/fetch.c                | 10 +++++-----\n repo-settings.c                |  6 ++++++\n repo-settings.h                |  1 +\n 5 files changed, 18 insertions(+), 6 deletions(-)\n\ndiff --git a/Documentation/config/core.txt b/Documentation/config/core.txt\nindex 6bed956a08..5733128d48 100644\n--- a/Documentation/config/core.txt\n+++ b/Documentation/config/core.txt\n@@ -623,3 +623,8 @@ client machine.\n +\n * `status.aheadBehind=false` enables `--no-ahead-behind` by default during\n linkgit:git-status[1] calls, saving time in a fast-moving commit history.\n++\n+* `fetch.showForcedUpdates=false` enables `--no-show-forced-updates` by\n+default during linkgit:git-fetch[1] and linkgit:git-pull[1] calls, saving\n+time in a fast-moving commit history. This has a small side-effect of not\n+updating the forced-update bit in the reflog.\ndiff --git a/Documentation/config/fetch.txt b/Documentation/config/fetch.txt\nindex ba890b5884..b7a3b08854 100644\n--- a/Documentation/config/fetch.txt\n+++ b/Documentation/config/fetch.txt\n@@ -67,4 +67,4 @@ See also the `--negotiation-tip` option for linkgit:git-fetch[1].\n fetch.showForcedUpdates::\n \tSet to false to enable `--no-show-forced-updates` in\n \tlinkgit:git-fetch[1] and linkgit:git-pull[1] commands.\n-\tDefaults to true.\n+\tDefaults to true, unless `core.size=large`.\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex f8ff98fdaf..56683ece17 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -23,6 +23,7 @@\n #include \"packfile.h\"\n #include \"list-objects-filter-options.h\"\n #include \"commit-reach.h\"\n+#include \"repo-settings.h\"\n \n #define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)\n \n@@ -83,11 +84,6 @@ static int git_fetch_config(const char *k, const char *v, void *cb)\n \t\treturn 0;\n \t}\n \n-\tif (!strcmp(k, \"fetch.showforcedupdates\")) {\n-\t\tfetch_show_forced_updates = git_config_bool(k, v);\n-\t\treturn 0;\n-\t}\n-\n \tif (!strcmp(k, \"submodule.recurse\")) {\n \t\tint r = git_config_bool(k, v) ?\n \t\t\tRECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;\n@@ -1618,6 +1614,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)\n \tfetch_config_from_gitmodules(&max_children, &recurse_submodules);\n \tgit_config(git_fetch_config, NULL);\n \n+\tprepare_repo_settings(the_repository);\n+\tif (the_repository->settings->fetch_show_forced_updates != -1)\n+\t\tfetch_show_forced_updates = the_repository->settings->fetch_show_forced_updates;\n+\n \targc = parse_options(argc, argv, prefix,\n \t\t\t     builtin_fetch_options, builtin_fetch_usage, 0);\n \ndiff --git a/repo-settings.c b/repo-settings.c\nindex b3d4b50b72..84e87913a5 100644\n--- a/repo-settings.c\n+++ b/repo-settings.c\n@@ -16,6 +16,7 @@ static int git_repo_config(const char *key, const char *value, void *cb)\n \t\t\tUPDATE_DEFAULT(rs->gc_write_commit_graph, 1);\n \t\t\tUPDATE_DEFAULT(rs->pack_use_sparse, 1);\n \t\t\tUPDATE_DEFAULT(rs->status_ahead_behind, 1);\n+\t\t\tUPDATE_DEFAULT(rs->fetch_show_forced_updates, 1);\n \t\t\tUPDATE_DEFAULT(rs->index_version, 4);\n \t\t}\n \t\treturn 0;\n@@ -36,6 +37,10 @@ static int git_repo_config(const char *key, const char *value, void *cb)\n \t\trs->status_ahead_behind = git_config_bool(key, value);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(key, \"fetch.showforcedupdates\")) {\n+\t\trs->fetch_show_forced_updates = git_config_bool(key, value);\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(key, \"index.version\")) {\n \t\trs->index_version = git_config_int(key, value);\n \t\treturn 0;\n@@ -56,6 +61,7 @@ void prepare_repo_settings(struct repository *r)\n \tr->settings->gc_write_commit_graph = -1;\n \tr->settings->pack_use_sparse = -1;\n \tr->settings->status_ahead_behind = -1;\n+\tr->settings->fetch_show_forced_updates = -1;\n \tr->settings->index_version = -1;\n \n \trepo_config(r, git_repo_config, r->settings);\ndiff --git a/repo-settings.h b/repo-settings.h\nindex cc358a083a..ea7d27138f 100644\n--- a/repo-settings.h\n+++ b/repo-settings.h\n@@ -6,6 +6,7 @@ struct repo_settings {\n \tchar gc_write_commit_graph;\n \tchar pack_use_sparse;\n \tchar status_ahead_behind;\n+\tchar fetch_show_forced_updates;\n \tint index_version;\n };\n \n-- \ngitgitgadget\n"},{"id":"376606","messageId":"637aebc8ac1b90a3c7eed859c5d2c054f637a4b6.1559593097.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.git.gitgitgadget@gmail.com","subject":"[PATCH 10/11] pull: add --[no-]show-forced-updates passthrough to fetch","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-06-03T20:18:25Z","receivedAt":"2019-06-03T20:18:32Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/pull.c | 7 +++++++\n 1 file changed, 7 insertions(+)\n\ndiff --git a/builtin/pull.c b/builtin/pull.c\nindex 9dd32a115b..f1eaf6e6ed 100644\n--- a/builtin/pull.c\n+++ b/builtin/pull.c\n@@ -128,6 +128,7 @@ static char *opt_update_shallow;\n static char *opt_refmap;\n static char *opt_ipv4;\n static char *opt_ipv6;\n+static int opt_show_forced_updates = -1;\n \n static struct option pull_options[] = {\n \t/* Shared options */\n@@ -240,6 +241,8 @@ static struct option pull_options[] = {\n \tOPT_PASSTHRU('6',  \"ipv6\", &opt_ipv6, NULL,\n \t\tN_(\"use IPv6 addresses only\"),\n \t\tPARSE_OPT_NOARG),\n+\tOPT_BOOL(0, \"show-forced-updates\", &opt_show_forced_updates,\n+\t\t N_(\"check for forced-updates on all updated branches\")),\n \n \tOPT_END()\n };\n@@ -549,6 +552,10 @@ static int run_fetch(const char *repo, const char **refspecs)\n \t\targv_array_push(&args, opt_ipv4);\n \tif (opt_ipv6)\n \t\targv_array_push(&args, opt_ipv6);\n+\tif (opt_show_forced_updates > 0)\n+\t\targv_array_push(&args, \"--show-forced-updates\");\n+\telse if (opt_show_forced_updates == 0)\n+\t\targv_array_push(&args, \"--no-show-forced-updates\");\n \n \tif (repo) {\n \t\targv_array_push(&args, repo);\n-- \ngitgitgadget\n\n"},{"id":"376607","messageId":"2d6bf8513d928765d7411a28c526d9c77237b3fe.1559593097.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.git.gitgitgadget@gmail.com","subject":"[PATCH 08/11] fetch: add --[no-]show-forced-updates argument","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-06-03T20:18:24Z","receivedAt":"2019-06-03T20:18:34Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nAfter updating a set of remove refs during a 'git fetch', we walk the\ncommits in the new ref value and not in the old ref value to discover\nif the update was a forced update. This results in two things happening\nduring the command:\n\n 1. The line including the ref update has an additional \"(forced-update)\"\n    marker at the end.\n\n 2. The ref log for that remote branch includes a bit saying that update\n    is a forced update.\n\nFor many situations, this forced-update message happens infrequently, or\nis a small bit of information among many ref updates. Many users ignore\nthese messages, but the calculation required here slows down their fetches\nsignificantly. Keep in mind that they do not have the opportunity to\ncalculate a commit-graph file containing the newly-fetched commits, so\nthese comparisons can be very slow.\n\nAdd a '--[no-]show-forced-updates' option that allows a user to skip this\ncalculation. The only permanent result is dropping the forced-update bit\nin the reflog.\n\nInclude a new fetch.showForcedUpdates config setting that allows this\nbehavior without including the argument in every command. The config\nsetting is overridden by the command-line arguments.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/config/fetch.txt  |  5 +++++\n Documentation/fetch-options.txt | 13 +++++++++++++\n builtin/fetch.c                 | 11 ++++++++++-\n 3 files changed, 28 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config/fetch.txt b/Documentation/config/fetch.txt\nindex cbfad6cdbb..ba890b5884 100644\n--- a/Documentation/config/fetch.txt\n+++ b/Documentation/config/fetch.txt\n@@ -63,3 +63,8 @@ fetch.negotiationAlgorithm::\n \tUnknown values will cause 'git fetch' to error out.\n +\n See also the `--negotiation-tip` option for linkgit:git-fetch[1].\n+\n+fetch.showForcedUpdates::\n+\tSet to false to enable `--no-show-forced-updates` in\n+\tlinkgit:git-fetch[1] and linkgit:git-pull[1] commands.\n+\tDefaults to true.\ndiff --git a/Documentation/fetch-options.txt b/Documentation/fetch-options.txt\nindex 91c47752ec..5801d23ae4 100644\n--- a/Documentation/fetch-options.txt\n+++ b/Documentation/fetch-options.txt\n@@ -221,6 +221,19 @@ endif::git-pull[]\n \tWhen multiple `--server-option=<option>` are given, they are all\n \tsent to the other side in the order listed on the command line.\n \n+--show-forced-updates::\n+\tBy default, git checks if a branch is force-updated during\n+\tfetch. This can be disabled through fetch.showForcedUpdates, but\n+\tthe --show-forced-updates option guarantees this check occurs.\n+\tSee linkgit:git-config[1].\n+\n+--no-show-forced-updates::\n+\tBy default, git checks if a branch is force-updated during\n+\tfetch. Pass --no-show-forced-updates or set fetch.showForcedUpdates\n+\tto false to skip this check for performance reasons. If used during\n+\t'git-pull' the --ff-only option will still check for forced updates\n+\tbefore attempting a fast-forward update. See linkgit:git-config[1].\n+\n -4::\n --ipv4::\n \tUse IPv4 addresses only, ignoring IPv6 addresses.\ndiff --git a/builtin/fetch.c b/builtin/fetch.c\nindex 4ba63d5ac6..571c255218 100644\n--- a/builtin/fetch.c\n+++ b/builtin/fetch.c\n@@ -39,6 +39,7 @@ enum {\n };\n \n static int fetch_prune_config = -1; /* unspecified */\n+static int fetch_show_forced_updates = 1;\n static int prune = -1; /* unspecified */\n #define PRUNE_BY_DEFAULT 0 /* do we prune by default? */\n \n@@ -79,6 +80,11 @@ static int git_fetch_config(const char *k, const char *v, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(k, \"fetch.showforcedupdates\")) {\n+\t\tfetch_show_forced_updates = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(k, \"submodule.recurse\")) {\n \t\tint r = git_config_bool(k, v) ?\n \t\t\tRECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;\n@@ -169,6 +175,8 @@ static struct option builtin_fetch_options[] = {\n \tOPT_STRING_LIST(0, \"negotiation-tip\", &negotiation_tip, N_(\"revision\"),\n \t\t\tN_(\"report that we have only objects reachable from this object\")),\n \tOPT_PARSE_LIST_OBJECTS_FILTER(&filter_options),\n+\tOPT_BOOL(0, \"show-forced-updates\", &fetch_show_forced_updates,\n+\t\t N_(\"check for forced-updates on all updated branches\")),\n \tOPT_END()\n };\n \n@@ -773,9 +781,10 @@ static int update_local_ref(struct ref *ref,\n \t\treturn r;\n \t}\n \n-\tif (in_merge_bases(current, updated)) {\n+\tif (!fetch_show_forced_updates || in_merge_bases(current, updated)) {\n \t\tstruct strbuf quickref = STRBUF_INIT;\n \t\tint r;\n+\n \t\tstrbuf_add_unique_abbrev(&quickref, &current->object.oid, DEFAULT_ABBREV);\n \t\tstrbuf_addstr(&quickref, \"..\");\n \t\tstrbuf_add_unique_abbrev(&quickref, &ref->new_oid, DEFAULT_ABBREV);\n-- \ngitgitgadget\n\n"},{"id":"376608","messageId":"f3ea4e3f270939b9fca69aa4fd4da8fc8559057c.1559593097.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.git.gitgitgadget@gmail.com","subject":"[PATCH 03/11] repo-settings: pack.useSparse=true","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-06-03T20:18:20Z","receivedAt":"2019-06-03T20:18:35Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nIf a repo is large, then it probably has a very large working\ndirectory. In this case, a typical developer's edits usually impact\nmany fewer paths than the full path set. The sparse treewalk\nalgorithm is optimized for this case, speeding up 'git push' calls.\n\nUse pack.useSparse=true when core.size=large.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/config/core.txt | 4 ++++\n Documentation/config/pack.txt | 3 ++-\n builtin/pack-objects.c        | 9 +++++----\n repo-settings.c               | 6 ++++++\n repo-settings.h               | 1 +\n 5 files changed, 18 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config/core.txt b/Documentation/config/core.txt\nindex ea64f675fa..df357f5af5 100644\n--- a/Documentation/config/core.txt\n+++ b/Documentation/config/core.txt\n@@ -616,3 +616,7 @@ core.size::\n +\n * `index.version=4` uses prefix-compression to reduce the size of the\n .git/index file.\n++\n+* `pack.useSparse=true` uses the sparse tree-walk algorithm, which is\n+optimized for enumerating objects during linkgit:git-push[1] from a\n+client machine.\ndiff --git a/Documentation/config/pack.txt b/Documentation/config/pack.txt\nindex 9cdcfa7324..e6f44de104 100644\n--- a/Documentation/config/pack.txt\n+++ b/Documentation/config/pack.txt\n@@ -112,7 +112,8 @@ pack.useSparse::\n \tobjects. This can have significant performance benefits when\n \tcomputing a pack to send a small change. However, it is possible\n \tthat extra objects are added to the pack-file if the included\n-\tcommits contain certain types of direct renames.\n+\tcommits contain certain types of direct renames. Defaults to\n+\tfalse, unless `core.size=large`.\n \n pack.writeBitmaps (deprecated)::\n \tThis is a deprecated synonym for `repack.writeBitmaps`.\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 41d7fc5983..f26b3f2892 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -34,6 +34,7 @@\n #include \"dir.h\"\n #include \"midx.h\"\n #include \"trace2.h\"\n+#include \"repo-settings.h\"\n \n #define IN_PACK(obj) oe_in_pack(&to_pack, obj)\n #define SIZE(obj) oe_size(&to_pack, obj)\n@@ -2707,10 +2708,6 @@ static int git_pack_config(const char *k, const char *v, void *cb)\n \t\tuse_bitmap_index_default = git_config_bool(k, v);\n \t\treturn 0;\n \t}\n-\tif (!strcmp(k, \"pack.usesparse\")) {\n-\t\tsparse = git_config_bool(k, v);\n-\t\treturn 0;\n-\t}\n \tif (!strcmp(k, \"pack.threads\")) {\n \t\tdelta_search_threads = git_config_int(k, v);\n \t\tif (delta_search_threads < 0)\n@@ -3330,6 +3327,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \tread_replace_refs = 0;\n \n \tsparse = git_env_bool(\"GIT_TEST_PACK_SPARSE\", 0);\n+\tprepare_repo_settings(the_repository);\n+\tif (!sparse && the_repository->settings->pack_use_sparse != -1)\n+\t\tsparse = the_repository->settings->pack_use_sparse;\n+\n \treset_pack_idx_option(&pack_idx_opts);\n \tgit_config(git_pack_config, NULL);\n \ndiff --git a/repo-settings.c b/repo-settings.c\nindex 7e6e65d60c..026ab9c1a0 100644\n--- a/repo-settings.c\n+++ b/repo-settings.c\n@@ -14,6 +14,7 @@ static int git_repo_config(const char *key, const char *value, void *cb)\n \t\tif (!strcmp(value, \"large\")) {\n \t\t\tUPDATE_DEFAULT(rs->core_commit_graph, 1);\n \t\t\tUPDATE_DEFAULT(rs->gc_write_commit_graph, 1);\n+\t\t\tUPDATE_DEFAULT(rs->pack_use_sparse, 1);\n \t\t\tUPDATE_DEFAULT(rs->index_version, 4);\n \t\t}\n \t\treturn 0;\n@@ -26,6 +27,10 @@ static int git_repo_config(const char *key, const char *value, void *cb)\n \t\trs->gc_write_commit_graph = git_config_bool(key, value);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(key, \"pack.usesparse\")) {\n+\t\trs->pack_use_sparse = git_config_bool(key, value);\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(key, \"index.version\")) {\n \t\trs->index_version = git_config_int(key, value);\n \t\treturn 0;\n@@ -44,6 +49,7 @@ void prepare_repo_settings(struct repository *r)\n \t/* Defaults */\n \tr->settings->core_commit_graph = -1;\n \tr->settings->gc_write_commit_graph = -1;\n+\tr->settings->pack_use_sparse = -1;\n \tr->settings->index_version = -1;\n \n \trepo_config(r, git_repo_config, r->settings);\ndiff --git a/repo-settings.h b/repo-settings.h\nindex 9b8104042e..b50228f992 100644\n--- a/repo-settings.h\n+++ b/repo-settings.h\n@@ -4,6 +4,7 @@\n struct repo_settings {\n \tchar core_commit_graph;\n \tchar gc_write_commit_graph;\n+\tchar pack_use_sparse;\n \tint index_version;\n };\n \n-- \ngitgitgadget\n\n"},{"id":"376609","messageId":"82ae00e49571f2621769c3cea07f806b89142efb.1559593097.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.git.gitgitgadget@gmail.com","subject":"[PATCH 06/11] status: ignore status.aheadbehind in porcelain formats","fromName":"Jeff Hostetler via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-06-03T20:18:23Z","receivedAt":"2019-06-03T20:18:36Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nTeach porcelain V[12] formats to ignore the status.aheadbehind\nconfig setting. They only respect the --[no-]ahead-behind\ncommand line argument.  This is for backwards compatibility\nwith existing scripts.\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n builtin/commit.c        | 10 ++++++----\n t/t7064-wtstatus-pv2.sh | 12 ++++++++----\n 2 files changed, 14 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 71305073ad..79cb238d87 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1111,11 +1111,13 @@ static void finalize_deferred_config(struct wt_status *s)\n \n \t/*\n \t * If the user did not give a \"--[no]-ahead-behind\" command\n-\t * line argument, then we inherit the a/b config setting.\n-\t * If is not set, then we inherit _FULL for backwards\n-\t * compatibility.\n+\t * line argument *AND* we will print in a human-readable format\n+\t * (short, long etc.) then we inherit from the status.aheadbehind\n+\t * config setting.  In all other cases (and porcelain V[12] formats\n+\t * in particular), we inherit _FULL for backwards compatibility.\n \t */\n-\tif (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)\n+\tif (use_deferred_config &&\n+\t    s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)\n \t\ts->ahead_behind_flags = status_deferred_config.ahead_behind;\n \n \tif (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)\ndiff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh\nindex a0baf6e8b0..537787e598 100755\n--- a/t/t7064-wtstatus-pv2.sh\n+++ b/t/t7064-wtstatus-pv2.sh\n@@ -436,10 +436,6 @@ test_expect_success 'verify --[no-]ahead-behind with V2 format' '\n \t\tgit status --no-ahead-behind --porcelain=v2 --branch --untracked-files=all >actual &&\n \t\ttest_cmp expect actual &&\n \n-\t\t# Confirmat that \"status.aheadbehind\" works on V2 format.\n-\t\tgit -c status.aheadbehind=false status --porcelain=v2 --branch --untracked-files=all >actual &&\n-\t\ttest_cmp expect actual &&\n-\n \t\t# Confirm --ahead-behind reports traditional branch.ab with 1/0.\n \t\tcat >expect <<-EOF &&\n \t\t# branch.oid $HUF\n@@ -449,6 +445,14 @@ test_expect_success 'verify --[no-]ahead-behind with V2 format' '\n \t\tEOF\n \n \t\tgit status --ahead-behind --porcelain=v2 --branch --untracked-files=all >actual &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\t# Confirm that \"status.aheadbehind\" DOES NOT work on V2 format.\n+\t\tgit -c status.aheadbehind=false status --porcelain=v2 --branch --untracked-files=all >actual &&\n+\t\ttest_cmp expect actual &&\n+\n+\t\t# Confirm that \"status.aheadbehind\" DOES NOT work on V2 format.\n+\t\tgit -c status.aheadbehind=true status --porcelain=v2 --branch --untracked-files=all >actual &&\n \t\ttest_cmp expect actual\n \t)\n '\n-- \ngitgitgadget\n\n"},{"id":"376610","messageId":"671cf092fd549486f668b9f0f353dd595c0e3afc.1559593097.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.git.gitgitgadget@gmail.com","subject":"[PATCH 04/11] status: add status.aheadbehind setting","fromName":"Jeff Hostetler via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-06-03T20:18:21Z","receivedAt":"2019-06-03T20:18:37Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"From: Jeff Hostetler <jeffhost@microsoft.com>\n\nAdd \"status.aheadbehind\" config setting to change the default\nbehavior of ALL git status formats.\n\nSigned-off-by: Jeff Hostetler <jeffhost@microsoft.com>\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/config/status.txt |  5 +++++\n builtin/commit.c                | 17 ++++++++++++++++-\n t/t6040-tracking-info.sh        | 31 +++++++++++++++++++++++++++++++\n t/t7064-wtstatus-pv2.sh         |  4 ++++\n 4 files changed, 56 insertions(+), 1 deletion(-)\n\ndiff --git a/Documentation/config/status.txt b/Documentation/config/status.txt\nindex ed72fa7dae..0fc704ab80 100644\n--- a/Documentation/config/status.txt\n+++ b/Documentation/config/status.txt\n@@ -12,6 +12,11 @@ status.branch::\n \tSet to true to enable --branch by default in linkgit:git-status[1].\n \tThe option --no-branch takes precedence over this variable.\n \n+status.aheadBehind::\n+\tSet to true to enable `--ahead-behind` and false to enable\n+\t`--no-ahead-behind` by default in linkgit:git-status[1] for\n+\tnon-porcelain status formats.  Defaults to true.\n+\n status.displayCommentPrefix::\n \tIf set to true, linkgit:git-status[1] will insert a comment\n \tprefix before each output line (starting with\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 1c9e8e2228..71305073ad 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1078,9 +1078,11 @@ static const char *read_commit_message(const char *name)\n static struct status_deferred_config {\n \tenum wt_status_format status_format;\n \tint show_branch;\n+\tenum ahead_behind_flags ahead_behind;\n } status_deferred_config = {\n \tSTATUS_FORMAT_UNSPECIFIED,\n-\t-1 /* unspecified */\n+\t-1, /* unspecified */\n+\tAHEAD_BEHIND_UNSPECIFIED,\n };\n \n static void finalize_deferred_config(struct wt_status *s)\n@@ -1107,6 +1109,15 @@ static void finalize_deferred_config(struct wt_status *s)\n \tif (s->show_branch < 0)\n \t\ts->show_branch = 0;\n \n+\t/*\n+\t * If the user did not give a \"--[no]-ahead-behind\" command\n+\t * line argument, then we inherit the a/b config setting.\n+\t * If is not set, then we inherit _FULL for backwards\n+\t * compatibility.\n+\t */\n+\tif (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)\n+\t\ts->ahead_behind_flags = status_deferred_config.ahead_behind;\n+\n \tif (s->ahead_behind_flags == AHEAD_BEHIND_UNSPECIFIED)\n \t\ts->ahead_behind_flags = AHEAD_BEHIND_FULL;\n }\n@@ -1246,6 +1257,10 @@ static int git_status_config(const char *k, const char *v, void *cb)\n \t\tstatus_deferred_config.show_branch = git_config_bool(k, v);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(k, \"status.aheadbehind\")) {\n+\t\tstatus_deferred_config.ahead_behind = git_config_bool(k, v);\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(k, \"status.showstash\")) {\n \t\ts->show_stash = git_config_bool(k, v);\n \t\treturn 0;\ndiff --git a/t/t6040-tracking-info.sh b/t/t6040-tracking-info.sh\nindex 716283b274..febf63f28a 100755\n--- a/t/t6040-tracking-info.sh\n+++ b/t/t6040-tracking-info.sh\n@@ -159,6 +159,19 @@ test_expect_success 'status -s -b --no-ahead-behind (diverged from upstream)' '\n \ttest_i18ncmp expect actual\n '\n \n+cat >expect <<\\EOF\n+## b1...origin/master [different]\n+EOF\n+\n+test_expect_success 'status.aheadbehind=false status -s -b (diverged from upstream)' '\n+\t(\n+\t\tcd test &&\n+\t\tgit checkout b1 >/dev/null &&\n+\t\tgit -c status.aheadbehind=false status -s -b | head -1\n+\t) >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n cat >expect <<\\EOF\n On branch b1\n Your branch and 'origin/master' have diverged,\n@@ -174,6 +187,15 @@ test_expect_success 'status --long --branch' '\n \ttest_i18ncmp expect actual\n '\n \n+test_expect_success 'status --long --branch' '\n+\t(\n+\t\tcd test &&\n+\t\tgit checkout b1 >/dev/null &&\n+\t\tgit -c status.aheadbehind=true status --long -b | head -3\n+\t) >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n cat >expect <<\\EOF\n On branch b1\n Your branch and 'origin/master' refer to different commits.\n@@ -188,6 +210,15 @@ test_expect_success 'status --long --branch --no-ahead-behind' '\n \ttest_i18ncmp expect actual\n '\n \n+test_expect_success 'status.aheadbehind=false status --long --branch' '\n+\t(\n+\t\tcd test &&\n+\t\tgit checkout b1 >/dev/null &&\n+\t\tgit -c status.aheadbehind=false status --long -b | head -2\n+\t) >actual &&\n+\ttest_i18ncmp expect actual\n+'\n+\n cat >expect <<\\EOF\n ## b5...brokenbase [gone]\n EOF\ndiff --git a/t/t7064-wtstatus-pv2.sh b/t/t7064-wtstatus-pv2.sh\nindex 11eccc231a..a0baf6e8b0 100755\n--- a/t/t7064-wtstatus-pv2.sh\n+++ b/t/t7064-wtstatus-pv2.sh\n@@ -436,6 +436,10 @@ test_expect_success 'verify --[no-]ahead-behind with V2 format' '\n \t\tgit status --no-ahead-behind --porcelain=v2 --branch --untracked-files=all >actual &&\n \t\ttest_cmp expect actual &&\n \n+\t\t# Confirmat that \"status.aheadbehind\" works on V2 format.\n+\t\tgit -c status.aheadbehind=false status --porcelain=v2 --branch --untracked-files=all >actual &&\n+\t\ttest_cmp expect actual &&\n+\n \t\t# Confirm --ahead-behind reports traditional branch.ab with 1/0.\n \t\tcat >expect <<-EOF &&\n \t\t# branch.oid $HUF\n-- \ngitgitgadget\n\n"},{"id":"376620","messageId":"c8f4d8d6-d8b6-efad-4bfd-472af6617123@gmail.com","threadId":"51228","inReplyTo":"pull.254.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 00/11] [RFC] Create 'core.size=large' setting to update config defaults","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2019-06-03T20:55:48Z","receivedAt":"2019-06-03T21:55:40Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 6/3/2019 4:18 PM, Derrick Stolee via GitGitGadget wrote:\n>  1. (Patches 1-3) Introduce a new 'core.size' config setting that takes\n>     'large' as a value. \n\nI do want to point out that this \"core.size=large\" option is probably a\nterrible name and could easily be replaced with something better. Please\nconsider alternatives that better describe the goals at hand (helping users\nget performance boosts on upgrade without needing to pay close attention).\n\nThanks,\n-Stolee\n"},{"id":"376622","messageId":"9a41f574-c476-1c70-ae11-577cbe1ec00b@jeffhostetler.com","threadId":"51228","inReplyTo":"704613f4480e3b9aacab91d2241247791f34ce22.1559593097.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 01/11] repo-settings: create repo.size=large setting","fromName":"Jeff Hostetler","fromEmail":"git@jeffhostetler.com","sentAt":"2019-06-03T20:42:36Z","receivedAt":"2019-06-03T21:58:56Z","isPatch":true,"sender":{"key":"git@jeffhostetler.com","avatar":null},"body":"\n\nOn 6/3/2019 4:18 PM, Derrick Stolee via GitGitGadget wrote:\n> From: Derrick Stolee <dstolee@microsoft.com>\n> \n> Several advanced config settings are highly recommended for clients\n> using large repositories. Power users learn these one-by-one and\n> enable them as they see fit. This could be made simpler, to allow\n> more users to have access to these almost-always beneficial features\n> (and more beneficial in larger repos).\n> \n> Create a 'repo.size' config setting whose only accepted value is\n> 'large'. When a repo.size=large is given, change the default values\n> of some config settings. If the setting is given explicitly, then\n> take the explicit value.\n> \n> This change adds these two defaults to the repo.size=large setting:\n> \n>   * core.commitGraph=true\n>   * gc.writeCommitGraph=true\n> \n> To centralize these config options and properly set the defaults,\n> create a repo_settings that contains chars for each config variable.\n> Use -1 as \"unset\", with 0 for false and 1 for true.\n> \n> The prepare_repo_settings() method ensures that this settings\n> struct has been initialized, and avoids double-scanning the config\n> settings.\n> \n> Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n[...]\n> diff --git a/repo-settings.c b/repo-settings.c\n> new file mode 100644\n> index 0000000000..6f5e18d92e\n> --- /dev/null\n> +++ b/repo-settings.c\n> @@ -0,0 +1,44 @@\n> +#include \"cache.h\"\n> +#include \"repository.h\"\n> +#include \"config.h\"\n> +#include \"repo-settings.h\"\n> +\n> +\n> +#define UPDATE_DEFAULT(s,v) if (s != -1) { s = v; }\n\nWe should guard this with a \"do { ... } while (0)\"\n\n> +\n> +static int git_repo_config(const char *key, const char *value, void *cb)\n> +{\n> +\tstruct repo_settings *rs = (struct repo_settings *)cb;\n> +\n> +\tif (!strcmp(key, \"core.size\")) {\n> +\t\tif (!strcmp(value, \"large\")) {\n> +\t\t\tUPDATE_DEFAULT(rs->core_commit_graph, 1);\n> +\t\t\tUPDATE_DEFAULT(rs->gc_write_commit_graph, 1);\n> +\t\t}\n> +\t\treturn 0;\n> +\t}\n> +\tif (!strcmp(key, \"core.commitgraph\")) {\n> +\t\trs->core_commit_graph = git_config_bool(key, value);\n> +\t\treturn 0;\n> +\t}\n> +\tif (!strcmp(key, \"gc.writecommitgraph\")) {\n> +\t\trs->gc_write_commit_graph = git_config_bool(key, value);\n> +\t\treturn 0;\n> +\t}\n> +\n> +\treturn 1;\n> +}\n[...]\n\nJeff\n"},{"id":"376665","messageId":"nycvar.QRO.7.76.6.1906041641280.1775@tvgsbejvaqbjf.bet","threadId":"51228","inReplyTo":"pull.254.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 00/11] [RFC] Create 'core.size=large' setting to update config defaults","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-06-04T14:43:12Z","receivedAt":"2019-06-04T14:43:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Stolee,\n\nOn Mon, 3 Jun 2019, Derrick Stolee via GitGitGadget wrote:\n\n>  1. (Patches 1-3) Introduce a new 'core.size' config setting that takes\n>     'large' as a value. This enables several config values that are\n>     beneficial for large repos.\n\nI find `core.size` a bit non-descriptive. Maybe `repository.size` instead?\n\nCiao,\nDscho\n"},{"id":"376668","messageId":"6a89e2c3-cbca-176c-1964-7e25d90c0ff7@gmail.com","threadId":"51228","inReplyTo":"nycvar.QRO.7.76.6.1906041641280.1775@tvgsbejvaqbjf.bet","subject":"Re: [PATCH 00/11] [RFC] Create 'core.size=large' setting to update config defaults","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2019-06-04T14:56:25Z","receivedAt":"2019-06-04T14:56:29Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 6/4/2019 10:43 AM, Johannes Schindelin wrote:\n> Hi Stolee,\n> \n> On Mon, 3 Jun 2019, Derrick Stolee via GitGitGadget wrote:\n> \n>>  1. (Patches 1-3) Introduce a new 'core.size' config setting that takes\n>>     'large' as a value. This enables several config values that are\n>>     beneficial for large repos.\n> \n> I find `core.size` a bit non-descriptive. Maybe `repository.size` instead?\n\nThanks for the suggestion! If the \"repository.\" doesn't make sense as a top-\nlevel category, then maybe \"core.repositorySize\" would work?\n\nA thought I had overnight that may broaden our options would be to think of\nthis as a tolerance for experimental features. Maybe \"core.adoptionRing\" with\noptions for \"slow\" and \"fast\", where \"slow\" takes things that have been cooking\na long while (index.version=4, core.commitGraph=gc.writeCommitGraph=1) and the\n\"fast\" option gets all of those values plus the more experimental options\n(status.aheadBehind=false, fetch.showForcedUpdates=false).\n\nAlternate names with this slow/fast idea could be:\n\n* core.experimentTolerance={none,low,high}\n\n* core.autoConfig={none,some,all}\n\nHopefully these options can trigger some creativity to decide on a good\nname that an experienced Git user could understand.\n\nThanks,\n-Stolee\n"},{"id":"376733","messageId":"xmqqftonsr6a.fsf@gitster-ct.c.googlers.com","threadId":"51228","inReplyTo":"pull.254.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 00/11] [RFC] Create 'core.size=large' setting to update config defaults","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-06-05T20:39:09Z","receivedAt":"2019-06-05T20:39:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> This patch series includes a few new config options we created to speed up\n> certain critical commands in VFS for Git. On their own, they would\n> contribute little value as it is hard to discover new config variables.\n> Instead, I've created this RFC as a goal for probably three sequential patch\n> series:\n>\n>  1. (Patches 1-3) Introduce a new 'core.size' config setting that takes\n>     'large' as a value. This enables several config values that are\n>     beneficial for large repos. We use a certain set in VFS for Git (see\n>     [1]), and most of those are applicable to any repo. This 'core.size'\n>     setting is intended for users to automatically receive performance\n>     updates as soon as they are stable, but they must opt-in to the setting\n>     and can always explicitly set their own config values. The settings to\n>     include here are core.commitGraph=true, gc.writeCommitGraph=true,\n>     index.version=4, pack.useSparse=true.\n\n... and not the configuration introduced by the other two points in\nthis list?\n\n\"If you set this, these other configuration variables are set to\nthese default values\" is a very valuable usability feature.  It\nlooks a lot more \"meta\" or \"macro\", and certainly is not a good idea\nto call it as if it sits next to variables in any existing hierarchy.\n\nI also wonder if this is something we would want to support in\ngeneral; random things that come to mind are:\n\n - should such a \"macro\" configuration be limited to boolean\n   (e.g. the above core.size that takes 'large' is a boolean between\n   'large' and 'not large'), or can it be an enum (e.g. choose among\n   'large', 'medium' and 'small', and core.bigFileThreshold will be\n   set to 1G, 512M and 128M respectively---this silly example is for\n   illustration purposes only), and if so, can we express what these\n   default values are for each choice without writing a lot of code?\n\n - if we were to have more than just this 'core.size' macro, can two\n   otherwise orthogonal macros both control the same underlying\n   variable, and if so, how do we express their interactions?\n   \"using these two at the same time is forbidden\" is a perfectly\n   acceptable answer for the first round until we figure out the\n   desired semantics, of course.\n\n - perhaps we may eventually want to allow end users (via their\n   ~/.gitconfig) and system administrators (via /etc/gitconfig)\n   define such a macro setting (e.g. setting macro.largeRepoSetting\n   sets pack.usebitmaps=true, pack.useSpars=true, etc.) *after* we\n   figure out what we want to do to the other points in this list.\n\n - even if we do not allow end users and system administrators futz\n   with custom macros, can we specify the macros we ship without\n   casting them in code?\n\n"},{"id":"376745","messageId":"741a4e37-e5d6-829a-75ee-b9bc3f3b17b2@gmail.com","threadId":"51228","inReplyTo":"xmqqftonsr6a.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 00/11] [RFC] Create 'core.size=large' setting to update config defaults","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2019-06-06T12:23:45Z","receivedAt":"2019-06-06T12:23:50Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 6/5/2019 4:39 PM, Junio C Hamano wrote:\n> \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> \n>> This patch series includes a few new config options we created to speed up\n>> certain critical commands in VFS for Git. On their own, they would\n>> contribute little value as it is hard to discover new config variables.\n>> Instead, I've created this RFC as a goal for probably three sequential patch\n>> series:\n>>\n>>  1. (Patches 1-3) Introduce a new 'core.size' config setting that takes\n>>     'large' as a value. This enables several config values that are\n>>     beneficial for large repos. We use a certain set in VFS for Git (see\n>>     [1]), and most of those are applicable to any repo. This 'core.size'\n>>     setting is intended for users to automatically receive performance\n>>     updates as soon as they are stable, but they must opt-in to the setting\n>>     and can always explicitly set their own config values. The settings to\n>>     include here are core.commitGraph=true, gc.writeCommitGraph=true,\n>>     index.version=4, pack.useSparse=true.\n> \n> ... and not the configuration introduced by the other two points in\n> this list?\n\nThey are added to the config setting after they are introduced. See\npatches 7 (status.aheadBehind) and 11 (fetch.showForcedUpdates).\n\n> \"If you set this, these other configuration variables are set to\n> these default values\" is a very valuable usability feature.  It\n> looks a lot more \"meta\" or \"macro\", and certainly is not a good idea\n> to call it as if it sits next to variables in any existing hierarchy.\n> \n> I also wonder if this is something we would want to support in\n> general; random things that come to mind are:\n> \n>  - should such a \"macro\" configuration be limited to boolean\n>    (e.g. the above core.size that takes 'large' is a boolean between\n>    'large' and 'not large'), or can it be an enum (e.g. choose among\n>    'large', 'medium' and 'small', and core.bigFileThreshold will be\n>    set to 1G, 512M and 128M respectively---this silly example is for\n>    illustration purposes only), and if so, can we express what these\n>    default values are for each choice without writing a lot of code?\n\nThat's a good point that we could include recommended values for\nother non-boolean variables if our \"meta\" config setting is also\nnon-boolean. This fits in with the \"ring\" ideas discussed earlier [1].\nTaking in a few ideas from your message, perhaps we create a new \"meta\"\ncategory for this setting and use an integer value for \"how big do I\nthink my repo is?\" and we can apply different settings based on thresholds:\n\n 0: no config defaults changed\n 3: safe defaults (core.commitGraph, index.version=4)\n 6: behavior-modifying defaults (status.aheadBehind, fetch.noShowForcedUpdates)\n\nUsing 3 and 6 here to allow for finer gradients at a later date.\n\n[1] https://public-inbox.org/git/xmqqftonsr6a.fsf@gitster-ct.c.googlers.com/T/#m8dbaedc016ce7301b9d80e5ceb6a82edfa7bafac\n\n>  - if we were to have more than just this 'core.size' macro, can two\n>    otherwise orthogonal macros both control the same underlying\n>    variable, and if so, how do we express their interactions?\n>    \"using these two at the same time is forbidden\" is a perfectly\n>    acceptable answer for the first round until we figure out the\n>    desired semantics, of course.\n\nTo borrow from linear algebra, I would recommend that two orthogonal\nconfig settings have disjoint _bases_ (i.e. the set of config settings\nthey use are disjoint). Of course, this can be discussed in more\ndetail when someone suggests a second meta-config setting. Such a\nsecond setting would need justification for why it doesn't work with\nour first setting.\n \n>  - perhaps we may eventually want to allow end users (via their\n>    ~/.gitconfig) and system administrators (via /etc/gitconfig)\n>    define such a macro setting (e.g. setting macro.largeRepoSetting\n>    sets pack.usebitmaps=true, pack.useSpars=true, etc.) *after* we\n>    figure out what we want to do to the other points in this list.\n>\n>  - even if we do not allow end users and system administrators futz\n>    with custom macros, can we specify the macros we ship without\n>    casting them in code?\n\nAre you suggesting that we allow some config values to be pulled from\nthe repo contents? If we could identify some config options as \"safe\"\nto include in the Git data, then a repo administrator could commit a\n\"/.gitconfig\" file _and_ some existing config option says \"look at the\nconfig in the repo\".\n\nI see value in making some \"safe\" settings available in the repo, but\nalso see that it can be very tricky to get right. Further, I think it\nis independent of the current direction. In fact, I would imagine the\nmeta-config setting be one of the \"safe\" settings that we could put in\nthis committed config file.\n\nThanks,\n-Stolee\n \n\n"},{"id":"376768","messageId":"xmqqv9xir92l.fsf@gitster-ct.c.googlers.com","threadId":"51228","inReplyTo":"741a4e37-e5d6-829a-75ee-b9bc3f3b17b2@gmail.com","subject":"Re: [PATCH 00/11] [RFC] Create 'core.size=large' setting to update config defaults","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-06-06T16:07:46Z","receivedAt":"2019-06-06T16:07:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <stolee@gmail.com> writes:\n\n>>  - perhaps we may eventually want to allow end users (via their\n>>    ~/.gitconfig) and system administrators (via /etc/gitconfig)\n>>    define such a macro setting (e.g. setting macro.largeRepoSetting\n>>    sets pack.usebitmaps=true, pack.useSpars=true, etc.) *after* we\n>>    figure out what we want to do to the other points in this list.\n>>\n>>  - even if we do not allow end users and system administrators futz\n>>    with custom macros, can we specify the macros we ship without\n>>    casting them in code?\n>\n> Are you suggesting that we allow some config values to be pulled from\n> the repo contents?\n\nNot at all.  As far as the configuration is concerned, what project\nships is tainted data that should not be used blindly.\n\nWhat I had in mind is parallel to the idea of pushing \"static struct\nuserdiff_driver builtin_drivers[]\" out of the compiled-in code and\ninstead have a text file shipped in /usr/share/git/ somewhere.  So,\ninstead of having \"core.size==large means these other four variables\nare set to these values\" in the code, we invent a general mechanism\nto read such \"macro\" specification out of a text file, and that\nwould be the only code change---the specific \"core.size==large\naffects X, Y and Z\" would not be in the code, but would be in the\ntext file we ship and read by the mechanism.\n\nIf the list of allowed \"meta\" configuration variables and the\nconfiguration variables whose default each of them affects can be\nexpressed in our usual \".gitconfig\" file format, then the system\nadministrators can add their own in /etc/gitconfig, too, to help\ntheir users.\n\nThat is what I meant by the last item.  Note that I was \"wondering\nif it makes sense\" and what I wrote above in this message is merely\nclarifying what I meant---I am not making further/more arguments to\nclaim it is a good idea (at least not yet).\n\nThanks.\n"},{"id":"377530","messageId":"pull.254.v2.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.git.gitgitgadget@gmail.com","subject":"[PATCH v2 0/3] [RFC] Create 'core.featureAdoptionRate' setting to update config defaults","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-06-19T15:11:59Z","receivedAt":"2019-06-19T15:12:03Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"Here is a second run at this RFC, which aims to create a \"meta\" config\nsetting that automatically turns on other settings according to a user's\nwillingness to trade new Git behavior or new feature risk for performance\nbenefits. The new name for the setting is \"core.featureAdoptionRate\" and is\nan integer scale from 0 to 10. There will be multiple \"categories\" of\nsettings, and the intention is to allow more granular levels as necessary.\n\nThe first category is \"3 or higher\" which means that the user is willing to\nadopt features that have been tested in multiple major releases. The\nsettings to include here are core.commitGraph=true,\ngc.writeCommitGraph=true, and index.version=4.\n\nThe second category is \"5 or higher\" which means the user is willing to\nadopt features that have not been out for multiple major releases. The\nsetting included here is pack.useSparse=true.\n\nIn the future, I would add a \"7 or higher\" setting which means the user is\nwilling to have a change of behavior in exchange for performance benefits.\nThe two settings to place here are 'status.aheadBehind=false' and\n'fetch.showForcedUpdates=false'. Instead of including these settings in the\ncurrent series, I've submitted them independently for full review [1, 2].\n\nHopefully this direction is amenable to allow \"early adopters\" gain access\nto new performance features even if they are not necessary reading every\nline of the release notes.\n\nThanks, -Stolee\n\n[1] https://public-inbox.org/git/pull.272.git.gitgitgadget@gmail.com/\n\n[2] https://public-inbox.org/git/pull.273.git.gitgitgadget@gmail.com/\n\nDerrick Stolee (3):\n  repo-settings: create core.featureAdoptionRate setting\n  repo-settings: use index.version=4 by default\n  repo-settings: pack.useSparse=true\n\n Documentation/config/core.txt  | 33 ++++++++++++++++++-\n Documentation/config/gc.txt    |  4 +--\n Documentation/config/index.txt |  2 ++\n Documentation/config/pack.txt  |  3 +-\n Makefile                       |  1 +\n builtin/gc.c                   |  6 ++--\n builtin/pack-objects.c         |  9 +++---\n commit-graph.c                 |  7 ++--\n read-cache.c                   | 12 ++++---\n repo-settings.c                | 58 ++++++++++++++++++++++++++++++++++\n repo-settings.h                | 15 +++++++++\n repository.h                   |  3 ++\n 12 files changed, 134 insertions(+), 19 deletions(-)\n create mode 100644 repo-settings.c\n create mode 100644 repo-settings.h\n\n\nbase-commit: aa25c82427ae70aebf3b8f970f2afd54e9a2a8c6\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-254%2Fderrickstolee%2Fconfig-large%2Fupstream-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-254/derrickstolee/config-large/upstream-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/254\n\nRange-diff vs v1:\n\n  1:  704613f448 !  1:  bdaee3ea9d repo-settings: create repo.size=large setting\n     @@ -1,6 +1,6 @@\n      Author: Derrick Stolee <dstolee@microsoft.com>\n      \n     -    repo-settings: create repo.size=large setting\n     +    repo-settings: create core.featureAdoptionRate setting\n      \n          Several advanced config settings are highly recommended for clients\n          using large repositories. Power users learn these one-by-one and\n     @@ -8,16 +8,31 @@\n          more users to have access to these almost-always beneficial features\n          (and more beneficial in larger repos).\n      \n     -    Create a 'repo.size' config setting whose only accepted value is\n     -    'large'. When a repo.size=large is given, change the default values\n     -    of some config settings. If the setting is given explicitly, then\n     -    take the explicit value.\n     +    Create a 'core.featureAdoptionRate' config setting that allows integer\n     +    values. This is a rating from 0 to 10 for the user's willingness to\n     +    adopt new or experimental features that improve Git performance.\n     +    The default is 0, meaning \"don't change anything!\" A value of 10\n     +    would mean \"I'm willing for some behavior to change to get the\n     +    best performance I can get, and can take experimental features\n     +    in their first release.\" As we integrate this with more config\n     +    settings, we will make this scale more clear.\n      \n     -    This change adds these two defaults to the repo.size=large setting:\n     +    This config setting only changes the default values of other config\n     +    settings. If the setting is given explicitly, then take the\n     +    explicit value.\n     +\n     +    This change adds these two defaults when core.featureAdoptionRate\n     +    is at least three:\n      \n           * core.commitGraph=true\n           * gc.writeCommitGraph=true\n      \n     +    The use of \"three or higher\" for these settings means that a value\n     +    of 3 means \"I'm willing to add optional features that can augment\n     +    the data on disk in favor of improved performance, but those\n     +    features should be stable after being included in multiple major\n     +    releases.\"\n     +\n          To centralize these config options and properly set the defaults,\n          create a repo_settings that contains chars for each config variable.\n          Use -1 as \"unset\", with 0 for false and 1 for true.\n     @@ -36,24 +51,29 @@\n       core.commitGraph::\n       \tIf true, then git will read the commit-graph file (if it exists)\n      -\tto parse the graph structure of commits. Defaults to false. See\n     --\tlinkgit:git-commit-graph[1] for more information.\n      +\tto parse the graph structure of commits. Defaults to false, unless\n     -+\t`core.size=large`. See linkgit:git-commit-graph[1] for more\n     -+\tinformation.\n     ++\t`core.featureAdoptionRate` is at least three. See\n     + \tlinkgit:git-commit-graph[1] for more information.\n       \n       core.useReplaceRefs::\n     - \tIf set to `false`, behave as if the `--no-replace-objects`\n      @@\n       \tin your repository, which hopefully is enough for\n       \tabbreviated object names to stay unique for some time.\n       \tThe minimum length is 4.\n      +\n     -+core.size::\n     -+\tWhen specified as \"large\", change the default values of some config\n     -+\tvariables to improve performance in a large repository. If a variable\n     ++core.featureAdoptionRate::\n     ++\tSet an integer value on a scale from 0 to 10 describing your\n     ++\tdesire to adopt new performance features. Defaults to 0. As\n     ++\tthe value increases, features are enabled by changing the\n     ++\tdefault values of other config settings. If a config variable\n      +\tis specified explicitly, the explicit value will override these\n      +\tdefaults:\n      ++\n     ++If the value is at least 3, then the following defaults are modified.\n     ++These represent relatively new features that have existed for multiple\n     ++major releases, and present significant performance benefits. They do\n     ++not modify the user-facing output of porcelain commands.\n     +++\n      +* `core.commitGraph=true` enables reading commit-graph files.\n      ++\n      +* `gc.writeCommitGraph=true` eneables writing commit-graph files during\n     @@ -68,8 +88,8 @@\n       \tthe commit-graph will be updated if housekeeping is\n      -\trequired. Default is false. See linkgit:git-commit-graph[1]\n      -\tfor details.\n     -+\trequired. Default is false, unless `core.size=large`.\n     -+\tSee linkgit:git-commit-graph[1] for details.\n     ++\trequired. Default is false, unless `core.featureAdoptionRage`\n     ++\tis at least three. See linkgit:git-commit-graph[1] for details.\n       \n       gc.logExpiry::\n       \tIf the file gc.log exists, then `git gc --auto` will print\n     @@ -167,15 +187,15 @@\n      +#include \"config.h\"\n      +#include \"repo-settings.h\"\n      +\n     -+\n     -+#define UPDATE_DEFAULT(s,v) if (s != -1) { s = v; }\n     ++#define UPDATE_DEFAULT(s,v) do { if (s == -1) { s = v; } } while(0)\n      +\n      +static int git_repo_config(const char *key, const char *value, void *cb)\n      +{\n      +\tstruct repo_settings *rs = (struct repo_settings *)cb;\n      +\n     -+\tif (!strcmp(key, \"core.size\")) {\n     -+\t\tif (!strcmp(value, \"large\")) {\n     ++\tif (!strcmp(key, \"core.featureadoptionrate\")) {\n     ++\t\tint rate = git_config_int(key, value);\n     ++\t\tif (rate >= 3) {\n      +\t\t\tUPDATE_DEFAULT(rs->core_commit_graph, 1);\n      +\t\t\tUPDATE_DEFAULT(rs->gc_write_commit_graph, 1);\n      +\t\t}\n  2:  d5f5d7453c !  2:  02c89415fe repo-settings: use index.version=4 by default\n     @@ -4,7 +4,7 @@\n      \n          If a repo is large, it likely has many paths in its working directory.\n          This means the index could be compressed using version 4. Set this as\n     -    a default when core.size=large.\n     +    a default when core.featureAdoptionRate is at least three.\n      \n          Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n      \n     @@ -26,7 +26,8 @@\n       index.version::\n       \tSpecify the version with which new index files should be\n       \tinitialized.  This does not affect existing repositories.\n     -+\tIf `core.size=large`, then the default value is 4.\n     ++\tIf `core.featureAdoptionRate` is at least three, then the\n     ++\tdefault value is 4.\n      \n       diff --git a/read-cache.c b/read-cache.c\n       --- a/read-cache.c\n     @@ -75,7 +76,7 @@\n       --- a/repo-settings.c\n       +++ b/repo-settings.c\n      @@\n     - \t\tif (!strcmp(value, \"large\")) {\n     + \t\tif (rate >= 3) {\n       \t\t\tUPDATE_DEFAULT(rs->core_commit_graph, 1);\n       \t\t\tUPDATE_DEFAULT(rs->gc_write_commit_graph, 1);\n      +\t\t\tUPDATE_DEFAULT(rs->index_version, 4);\n  3:  f3ea4e3f27 !  3:  5bba9062f4 repo-settings: pack.useSparse=true\n     @@ -7,7 +7,10 @@\n          many fewer paths than the full path set. The sparse treewalk\n          algorithm is optimized for this case, speeding up 'git push' calls.\n      \n     -    Use pack.useSparse=true when core.size=large.\n     +    Use pack.useSparse=true when core.featureAdoptionRate is at least\n     +    five. This is the first setting where the feature has only been\n     +    out for a single major version. This could be moved to the \"at\n     +    least three\" category after another major version.\n      \n          Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n      \n     @@ -19,6 +22,11 @@\n       * `index.version=4` uses prefix-compression to reduce the size of the\n       .git/index file.\n      ++\n     ++If the value is at least 5, then all of the defaults above are included,\n     ++plus the defaults below. These represent new features that present\n     ++significant performance benefits, but may not have been released for\n     ++multiple major versions.\n     +++\n      +* `pack.useSparse=true` uses the sparse tree-walk algorithm, which is\n      +optimized for enumerating objects during linkgit:git-push[1] from a\n      +client machine.\n     @@ -32,7 +40,7 @@\n       \tthat extra objects are added to the pack-file if the included\n      -\tcommits contain certain types of direct renames.\n      +\tcommits contain certain types of direct renames. Defaults to\n     -+\tfalse, unless `core.size=large`.\n     ++\tfalse, unless `core.featureAdoptionRate` is at least five.\n       \n       pack.writeBitmaps (deprecated)::\n       \tThis is a deprecated synonym for `repack.writeBitmaps`.\n     @@ -75,13 +83,15 @@\n       --- a/repo-settings.c\n       +++ b/repo-settings.c\n      @@\n     - \t\tif (!strcmp(value, \"large\")) {\n     - \t\t\tUPDATE_DEFAULT(rs->core_commit_graph, 1);\n       \t\t\tUPDATE_DEFAULT(rs->gc_write_commit_graph, 1);\n     -+\t\t\tUPDATE_DEFAULT(rs->pack_use_sparse, 1);\n       \t\t\tUPDATE_DEFAULT(rs->index_version, 4);\n       \t\t}\n     ++\t\tif (rate >= 5) {\n     ++\t\t\tUPDATE_DEFAULT(rs->pack_use_sparse, 1);\n     ++\t\t}\n       \t\treturn 0;\n     + \t}\n     + \tif (!strcmp(key, \"core.commitgraph\")) {\n      @@\n       \t\trs->gc_write_commit_graph = git_config_bool(key, value);\n       \t\treturn 0;\n  4:  671cf092fd <  -:  ---------- status: add status.aheadbehind setting\n  5:  d2e5cf1857 <  -:  ---------- status: add warning when a/b calculation takes too long for long/normal format\n  6:  82ae00e495 <  -:  ---------- status: ignore status.aheadbehind in porcelain formats\n  7:  936fae31b7 <  -:  ---------- repo-settings: status.aheadBehind=false\n  8:  2d6bf8513d <  -:  ---------- fetch: add --[no-]show-forced-updates argument\n  9:  6a80cfa5a5 <  -:  ---------- fetch: warn about forced updates after branch list\n 10:  637aebc8ac <  -:  ---------- pull: add --[no-]show-forced-updates passthrough to fetch\n 11:  d4ff987ad9 <  -:  ---------- repo-settings: fetch.showForcedUpdates=false\n\n-- \ngitgitgadget\n"},{"id":"377531","messageId":"02c89415fe11f14a65f2e6ee94e9d0de5a727e33.1560957119.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.v2.git.gitgitgadget@gmail.com","subject":"[PATCH v2 2/3] repo-settings: use index.version=4 by default","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-06-19T15:12:01Z","receivedAt":"2019-06-19T15:12:05Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nIf a repo is large, it likely has many paths in its working directory.\nThis means the index could be compressed using version 4. Set this as\na default when core.featureAdoptionRate is at least three.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/config/core.txt  |  3 +++\n Documentation/config/index.txt |  2 ++\n read-cache.c                   | 12 +++++++-----\n repo-settings.c                |  6 ++++++\n repo-settings.h                |  1 +\n 5 files changed, 19 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config/core.txt b/Documentation/config/core.txt\nindex 6a9f707815..d16503a9d7 100644\n--- a/Documentation/config/core.txt\n+++ b/Documentation/config/core.txt\n@@ -620,3 +620,6 @@ not modify the user-facing output of porcelain commands.\n +\n * `gc.writeCommitGraph=true` eneables writing commit-graph files during\n `git gc`.\n++\n+* `index.version=4` uses prefix-compression to reduce the size of the\n+.git/index file.\ndiff --git a/Documentation/config/index.txt b/Documentation/config/index.txt\nindex f181503041..98a88c30be 100644\n--- a/Documentation/config/index.txt\n+++ b/Documentation/config/index.txt\n@@ -24,3 +24,5 @@ index.threads::\n index.version::\n \tSpecify the version with which new index files should be\n \tinitialized.  This does not affect existing repositories.\n+\tIf `core.featureAdoptionRate` is at least three, then the\n+\tdefault value is 4.\ndiff --git a/read-cache.c b/read-cache.c\nindex 22e7b9944e..7fab8ff748 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -25,6 +25,7 @@\n #include \"fsmonitor.h\"\n #include \"thread-utils.h\"\n #include \"progress.h\"\n+#include \"repo-settings.h\"\n \n /* Mask for the name length in ce_flags in the on-disk index */\n \n@@ -1599,16 +1600,17 @@ struct cache_entry *refresh_cache_entry(struct index_state *istate,\n \n #define INDEX_FORMAT_DEFAULT 3\n \n-static unsigned int get_index_format_default(void)\n+static unsigned int get_index_format_default(struct repository *r)\n {\n \tchar *envversion = getenv(\"GIT_INDEX_VERSION\");\n \tchar *endp;\n-\tint value;\n \tunsigned int version = INDEX_FORMAT_DEFAULT;\n \n \tif (!envversion) {\n-\t\tif (!git_config_get_int(\"index.version\", &value))\n-\t\t\tversion = value;\n+\t\tprepare_repo_settings(r);\n+\n+\t\tif (r->settings->index_version >= 0)\n+\t\t\tversion = r->settings->index_version;\n \t\tif (version < INDEX_FORMAT_LB || INDEX_FORMAT_UB < version) {\n \t\t\twarning(_(\"index.version set, but the value is invalid.\\n\"\n \t\t\t\t  \"Using version %i\"), INDEX_FORMAT_DEFAULT);\n@@ -2765,7 +2767,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,\n \t}\n \n \tif (!istate->version) {\n-\t\tistate->version = get_index_format_default();\n+\t\tistate->version = get_index_format_default(the_repository);\n \t\tif (git_env_bool(\"GIT_TEST_SPLIT_INDEX\", 0))\n \t\t\tinit_split_index(istate);\n \t}\ndiff --git a/repo-settings.c b/repo-settings.c\nindex f7fc2a1959..5753153a84 100644\n--- a/repo-settings.c\n+++ b/repo-settings.c\n@@ -14,6 +14,7 @@ static int git_repo_config(const char *key, const char *value, void *cb)\n \t\tif (rate >= 3) {\n \t\t\tUPDATE_DEFAULT(rs->core_commit_graph, 1);\n \t\t\tUPDATE_DEFAULT(rs->gc_write_commit_graph, 1);\n+\t\t\tUPDATE_DEFAULT(rs->index_version, 4);\n \t\t}\n \t\treturn 0;\n \t}\n@@ -25,6 +26,10 @@ static int git_repo_config(const char *key, const char *value, void *cb)\n \t\trs->gc_write_commit_graph = git_config_bool(key, value);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(key, \"index.version\")) {\n+\t\trs->index_version = git_config_int(key, value);\n+\t\treturn 0;\n+\t}\n \n \treturn 1;\n }\n@@ -39,6 +44,7 @@ void prepare_repo_settings(struct repository *r)\n \t/* Defaults */\n \tr->settings->core_commit_graph = -1;\n \tr->settings->gc_write_commit_graph = -1;\n+\tr->settings->index_version = -1;\n \n \trepo_config(r, git_repo_config, r->settings);\n }\ndiff --git a/repo-settings.h b/repo-settings.h\nindex 11d08648e1..9b8104042e 100644\n--- a/repo-settings.h\n+++ b/repo-settings.h\n@@ -4,6 +4,7 @@\n struct repo_settings {\n \tchar core_commit_graph;\n \tchar gc_write_commit_graph;\n+\tint index_version;\n };\n \n struct repository;\n-- \ngitgitgadget\n\n"},{"id":"377533","messageId":"5bba9062f417e71d5dbb4192421b61cdde311c81.1560957119.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.v2.git.gitgitgadget@gmail.com","subject":"[PATCH v2 3/3] repo-settings: pack.useSparse=true","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-06-19T15:12:02Z","receivedAt":"2019-06-19T15:12:06Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nIf a repo is large, then it probably has a very large working\ndirectory. In this case, a typical developer's edits usually impact\nmany fewer paths than the full path set. The sparse treewalk\nalgorithm is optimized for this case, speeding up 'git push' calls.\n\nUse pack.useSparse=true when core.featureAdoptionRate is at least\nfive. This is the first setting where the feature has only been\nout for a single major version. This could be moved to the \"at\nleast three\" category after another major version.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/config/core.txt | 9 +++++++++\n Documentation/config/pack.txt | 3 ++-\n builtin/pack-objects.c        | 9 +++++----\n repo-settings.c               | 8 ++++++++\n repo-settings.h               | 1 +\n 5 files changed, 25 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config/core.txt b/Documentation/config/core.txt\nindex d16503a9d7..77fdd02660 100644\n--- a/Documentation/config/core.txt\n+++ b/Documentation/config/core.txt\n@@ -623,3 +623,12 @@ not modify the user-facing output of porcelain commands.\n +\n * `index.version=4` uses prefix-compression to reduce the size of the\n .git/index file.\n++\n+If the value is at least 5, then all of the defaults above are included,\n+plus the defaults below. These represent new features that present\n+significant performance benefits, but may not have been released for\n+multiple major versions.\n++\n+* `pack.useSparse=true` uses the sparse tree-walk algorithm, which is\n+optimized for enumerating objects during linkgit:git-push[1] from a\n+client machine.\ndiff --git a/Documentation/config/pack.txt b/Documentation/config/pack.txt\nindex 9cdcfa7324..9c4f8ea9ff 100644\n--- a/Documentation/config/pack.txt\n+++ b/Documentation/config/pack.txt\n@@ -112,7 +112,8 @@ pack.useSparse::\n \tobjects. This can have significant performance benefits when\n \tcomputing a pack to send a small change. However, it is possible\n \tthat extra objects are added to the pack-file if the included\n-\tcommits contain certain types of direct renames.\n+\tcommits contain certain types of direct renames. Defaults to\n+\tfalse, unless `core.featureAdoptionRate` is at least five.\n \n pack.writeBitmaps (deprecated)::\n \tThis is a deprecated synonym for `repack.writeBitmaps`.\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 41d7fc5983..f26b3f2892 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -34,6 +34,7 @@\n #include \"dir.h\"\n #include \"midx.h\"\n #include \"trace2.h\"\n+#include \"repo-settings.h\"\n \n #define IN_PACK(obj) oe_in_pack(&to_pack, obj)\n #define SIZE(obj) oe_size(&to_pack, obj)\n@@ -2707,10 +2708,6 @@ static int git_pack_config(const char *k, const char *v, void *cb)\n \t\tuse_bitmap_index_default = git_config_bool(k, v);\n \t\treturn 0;\n \t}\n-\tif (!strcmp(k, \"pack.usesparse\")) {\n-\t\tsparse = git_config_bool(k, v);\n-\t\treturn 0;\n-\t}\n \tif (!strcmp(k, \"pack.threads\")) {\n \t\tdelta_search_threads = git_config_int(k, v);\n \t\tif (delta_search_threads < 0)\n@@ -3330,6 +3327,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \tread_replace_refs = 0;\n \n \tsparse = git_env_bool(\"GIT_TEST_PACK_SPARSE\", 0);\n+\tprepare_repo_settings(the_repository);\n+\tif (!sparse && the_repository->settings->pack_use_sparse != -1)\n+\t\tsparse = the_repository->settings->pack_use_sparse;\n+\n \treset_pack_idx_option(&pack_idx_opts);\n \tgit_config(git_pack_config, NULL);\n \ndiff --git a/repo-settings.c b/repo-settings.c\nindex 5753153a84..c700edc286 100644\n--- a/repo-settings.c\n+++ b/repo-settings.c\n@@ -16,6 +16,9 @@ static int git_repo_config(const char *key, const char *value, void *cb)\n \t\t\tUPDATE_DEFAULT(rs->gc_write_commit_graph, 1);\n \t\t\tUPDATE_DEFAULT(rs->index_version, 4);\n \t\t}\n+\t\tif (rate >= 5) {\n+\t\t\tUPDATE_DEFAULT(rs->pack_use_sparse, 1);\n+\t\t}\n \t\treturn 0;\n \t}\n \tif (!strcmp(key, \"core.commitgraph\")) {\n@@ -26,6 +29,10 @@ static int git_repo_config(const char *key, const char *value, void *cb)\n \t\trs->gc_write_commit_graph = git_config_bool(key, value);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(key, \"pack.usesparse\")) {\n+\t\trs->pack_use_sparse = git_config_bool(key, value);\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(key, \"index.version\")) {\n \t\trs->index_version = git_config_int(key, value);\n \t\treturn 0;\n@@ -44,6 +51,7 @@ void prepare_repo_settings(struct repository *r)\n \t/* Defaults */\n \tr->settings->core_commit_graph = -1;\n \tr->settings->gc_write_commit_graph = -1;\n+\tr->settings->pack_use_sparse = -1;\n \tr->settings->index_version = -1;\n \n \trepo_config(r, git_repo_config, r->settings);\ndiff --git a/repo-settings.h b/repo-settings.h\nindex 9b8104042e..b50228f992 100644\n--- a/repo-settings.h\n+++ b/repo-settings.h\n@@ -4,6 +4,7 @@\n struct repo_settings {\n \tchar core_commit_graph;\n \tchar gc_write_commit_graph;\n+\tchar pack_use_sparse;\n \tint index_version;\n };\n \n-- \ngitgitgadget\n"},{"id":"377532","messageId":"bdaee3ea9df0533c268d6bebbd252c00cfbaccd6.1560957119.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.v2.git.gitgitgadget@gmail.com","subject":"[PATCH v2 1/3] repo-settings: create core.featureAdoptionRate setting","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-06-19T15:12:00Z","receivedAt":"2019-06-19T15:12:07Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nSeveral advanced config settings are highly recommended for clients\nusing large repositories. Power users learn these one-by-one and\nenable them as they see fit. This could be made simpler, to allow\nmore users to have access to these almost-always beneficial features\n(and more beneficial in larger repos).\n\nCreate a 'core.featureAdoptionRate' config setting that allows integer\nvalues. This is a rating from 0 to 10 for the user's willingness to\nadopt new or experimental features that improve Git performance.\nThe default is 0, meaning \"don't change anything!\" A value of 10\nwould mean \"I'm willing for some behavior to change to get the\nbest performance I can get, and can take experimental features\nin their first release.\" As we integrate this with more config\nsettings, we will make this scale more clear.\n\nThis config setting only changes the default values of other config\nsettings. If the setting is given explicitly, then take the\nexplicit value.\n\nThis change adds these two defaults when core.featureAdoptionRate\nis at least three:\n\n * core.commitGraph=true\n * gc.writeCommitGraph=true\n\nThe use of \"three or higher\" for these settings means that a value\nof 3 means \"I'm willing to add optional features that can augment\nthe data on disk in favor of improved performance, but those\nfeatures should be stable after being included in multiple major\nreleases.\"\n\nTo centralize these config options and properly set the defaults,\ncreate a repo_settings that contains chars for each config variable.\nUse -1 as \"unset\", with 0 for false and 1 for true.\n\nThe prepare_repo_settings() method ensures that this settings\nstruct has been initialized, and avoids double-scanning the config\nsettings.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/config/core.txt | 21 ++++++++++++++++-\n Documentation/config/gc.txt   |  4 ++--\n Makefile                      |  1 +\n builtin/gc.c                  |  6 ++---\n commit-graph.c                |  7 +++---\n repo-settings.c               | 44 +++++++++++++++++++++++++++++++++++\n repo-settings.h               | 13 +++++++++++\n repository.h                  |  3 +++\n 8 files changed, 90 insertions(+), 9 deletions(-)\n create mode 100644 repo-settings.c\n create mode 100644 repo-settings.h\n\ndiff --git a/Documentation/config/core.txt b/Documentation/config/core.txt\nindex 75538d27e7..6a9f707815 100644\n--- a/Documentation/config/core.txt\n+++ b/Documentation/config/core.txt\n@@ -577,7 +577,8 @@ the `GIT_NOTES_REF` environment variable.  See linkgit:git-notes[1].\n \n core.commitGraph::\n \tIf true, then git will read the commit-graph file (if it exists)\n-\tto parse the graph structure of commits. Defaults to false. See\n+\tto parse the graph structure of commits. Defaults to false, unless\n+\t`core.featureAdoptionRate` is at least three. See\n \tlinkgit:git-commit-graph[1] for more information.\n \n core.useReplaceRefs::\n@@ -601,3 +602,21 @@ core.abbrev::\n \tin your repository, which hopefully is enough for\n \tabbreviated object names to stay unique for some time.\n \tThe minimum length is 4.\n+\n+core.featureAdoptionRate::\n+\tSet an integer value on a scale from 0 to 10 describing your\n+\tdesire to adopt new performance features. Defaults to 0. As\n+\tthe value increases, features are enabled by changing the\n+\tdefault values of other config settings. If a config variable\n+\tis specified explicitly, the explicit value will override these\n+\tdefaults:\n++\n+If the value is at least 3, then the following defaults are modified.\n+These represent relatively new features that have existed for multiple\n+major releases, and present significant performance benefits. They do\n+not modify the user-facing output of porcelain commands.\n++\n+* `core.commitGraph=true` enables reading commit-graph files.\n++\n+* `gc.writeCommitGraph=true` eneables writing commit-graph files during\n+`git gc`.\ndiff --git a/Documentation/config/gc.txt b/Documentation/config/gc.txt\nindex 02b92b18b5..898263209c 100644\n--- a/Documentation/config/gc.txt\n+++ b/Documentation/config/gc.txt\n@@ -63,8 +63,8 @@ gc.writeCommitGraph::\n \tIf true, then gc will rewrite the commit-graph file when\n \tlinkgit:git-gc[1] is run. When using `git gc --auto`\n \tthe commit-graph will be updated if housekeeping is\n-\trequired. Default is false. See linkgit:git-commit-graph[1]\n-\tfor details.\n+\trequired. Default is false, unless `core.featureAdoptionRage`\n+\tis at least three. See linkgit:git-commit-graph[1] for details.\n \n gc.logExpiry::\n \tIf the file gc.log exists, then `git gc --auto` will print\ndiff --git a/Makefile b/Makefile\nindex 8a7e235352..2d3499d7ac 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -967,6 +967,7 @@ LIB_OBJS += refspec.o\n LIB_OBJS += ref-filter.o\n LIB_OBJS += remote.o\n LIB_OBJS += replace-object.o\n+LIB_OBJS += repo-settings.o\n LIB_OBJS += repository.o\n LIB_OBJS += rerere.o\n LIB_OBJS += resolve-undo.o\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex 8943bcc300..6281aad961 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -27,6 +27,7 @@\n #include \"pack-objects.h\"\n #include \"blob.h\"\n #include \"tree.h\"\n+#include \"repo-settings.h\"\n \n #define FAILED_RUN \"failed to run %s\"\n \n@@ -41,7 +42,6 @@ static int aggressive_depth = 50;\n static int aggressive_window = 250;\n static int gc_auto_threshold = 6700;\n static int gc_auto_pack_limit = 50;\n-static int gc_write_commit_graph;\n static int detach_auto = 1;\n static timestamp_t gc_log_expire_time;\n static const char *gc_log_expire = \"1.day.ago\";\n@@ -148,7 +148,6 @@ static void gc_config(void)\n \tgit_config_get_int(\"gc.aggressivedepth\", &aggressive_depth);\n \tgit_config_get_int(\"gc.auto\", &gc_auto_threshold);\n \tgit_config_get_int(\"gc.autopacklimit\", &gc_auto_pack_limit);\n-\tgit_config_get_bool(\"gc.writecommitgraph\", &gc_write_commit_graph);\n \tgit_config_get_bool(\"gc.autodetach\", &detach_auto);\n \tgit_config_get_expiry(\"gc.pruneexpire\", &prune_expire);\n \tgit_config_get_expiry(\"gc.worktreepruneexpire\", &prune_worktrees_expire);\n@@ -685,7 +684,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)\n \t\tclean_pack_garbage();\n \t}\n \n-\tif (gc_write_commit_graph)\n+\tprepare_repo_settings(the_repository);\n+\tif (the_repository->settings->gc_write_commit_graph == 1)\n \t\twrite_commit_graph_reachable(get_object_directory(), 0,\n \t\t\t\t\t     !quiet && !daemonized);\n \ndiff --git a/commit-graph.c b/commit-graph.c\nindex 7c5e54875f..b09c465a7a 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -16,6 +16,7 @@\n #include \"hashmap.h\"\n #include \"replace-object.h\"\n #include \"progress.h\"\n+#include \"repo-settings.h\"\n \n #define GRAPH_SIGNATURE 0x43475048 /* \"CGPH\" */\n #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* \"OIDF\" */\n@@ -311,7 +312,6 @@ static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)\n static int prepare_commit_graph(struct repository *r)\n {\n \tstruct object_directory *odb;\n-\tint config_value;\n \n \tif (git_env_bool(GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD, 0))\n \t\tdie(\"dying as requested by the '%s' variable on commit-graph load!\",\n@@ -321,9 +321,10 @@ static int prepare_commit_graph(struct repository *r)\n \t\treturn !!r->objects->commit_graph;\n \tr->objects->commit_graph_attempted = 1;\n \n+\tprepare_repo_settings(r);\n+\n \tif (!git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&\n-\t    (repo_config_get_bool(r, \"core.commitgraph\", &config_value) ||\n-\t    !config_value))\n+\t    r->settings->core_commit_graph != 1)\n \t\t/*\n \t\t * This repository is not configured to use commit graphs, so\n \t\t * do not load one. (But report commit_graph_attempted anyway\ndiff --git a/repo-settings.c b/repo-settings.c\nnew file mode 100644\nindex 0000000000..f7fc2a1959\n--- /dev/null\n+++ b/repo-settings.c\n@@ -0,0 +1,44 @@\n+#include \"cache.h\"\n+#include \"repository.h\"\n+#include \"config.h\"\n+#include \"repo-settings.h\"\n+\n+#define UPDATE_DEFAULT(s,v) do { if (s == -1) { s = v; } } while(0)\n+\n+static int git_repo_config(const char *key, const char *value, void *cb)\n+{\n+\tstruct repo_settings *rs = (struct repo_settings *)cb;\n+\n+\tif (!strcmp(key, \"core.featureadoptionrate\")) {\n+\t\tint rate = git_config_int(key, value);\n+\t\tif (rate >= 3) {\n+\t\t\tUPDATE_DEFAULT(rs->core_commit_graph, 1);\n+\t\t\tUPDATE_DEFAULT(rs->gc_write_commit_graph, 1);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(key, \"core.commitgraph\")) {\n+\t\trs->core_commit_graph = git_config_bool(key, value);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(key, \"gc.writecommitgraph\")) {\n+\t\trs->gc_write_commit_graph = git_config_bool(key, value);\n+\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+void prepare_repo_settings(struct repository *r)\n+{\n+\tif (r->settings)\n+\t\treturn;\n+\n+\tr->settings = xmalloc(sizeof(*r->settings));\n+\n+\t/* Defaults */\n+\tr->settings->core_commit_graph = -1;\n+\tr->settings->gc_write_commit_graph = -1;\n+\n+\trepo_config(r, git_repo_config, r->settings);\n+}\ndiff --git a/repo-settings.h b/repo-settings.h\nnew file mode 100644\nindex 0000000000..11d08648e1\n--- /dev/null\n+++ b/repo-settings.h\n@@ -0,0 +1,13 @@\n+#ifndef REPO_SETTINGS_H\n+#define REPO_SETTINGS_H\n+\n+struct repo_settings {\n+\tchar core_commit_graph;\n+\tchar gc_write_commit_graph;\n+};\n+\n+struct repository;\n+\n+void prepare_repo_settings(struct repository *r);\n+\n+#endif /* REPO_SETTINGS_H */\ndiff --git a/repository.h b/repository.h\nindex 4fb6a5885f..352afc9cd8 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -4,6 +4,7 @@\n #include \"path.h\"\n \n struct config_set;\n+struct repo_settings;\n struct git_hash_algo;\n struct index_state;\n struct lock_file;\n@@ -72,6 +73,8 @@ struct repository {\n \t */\n \tchar *submodule_prefix;\n \n+\tstruct repo_settings *settings;\n+\n \t/* Subsystems */\n \t/*\n \t * Repository's config which contains key-value pairs from the usual\n-- \ngitgitgadget\n\n"},{"id":"378283","messageId":"xmqqd0ix8me1.fsf@gitster-ct.c.googlers.com","threadId":"51228","inReplyTo":"bdaee3ea9df0533c268d6bebbd252c00cfbaccd6.1560957119.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 1/3] repo-settings: create core.featureAdoptionRate setting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-06-28T20:50:46Z","receivedAt":"2019-06-28T20:50:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> +core.featureAdoptionRate::\n> +\tSet an integer value on a scale from 0 to 10 describing your\n> +\tdesire to adopt new performance features. Defaults to 0. As\n> +\tthe value increases, features are enabled by changing the\n> +\tdefault values of other config settings. If a config variable\n> +\tis specified explicitly, the explicit value will override these\n> +\tdefaults:\n> ++\n> +If the value is at least 3, then the following defaults are modified.\n> +These represent relatively new features that have existed for multiple\n> +major releases, and present significant performance benefits. They do\n> +not modify the user-facing output of porcelain commands.\n> ++\n> +* `core.commitGraph=true` enables reading commit-graph files.\n> ++\n> +* `gc.writeCommitGraph=true` eneables writing commit-graph files during\n> +`git gc`.\n\nI was re-reading the whole series, and found that the phrase\n\"present significant benefits\" was somewhat overselling.  Wouldn't\nthat claim largely depend on the end-user's workflow?  The same\ncomment applies to the description of \"at least 5\" level, too.\n\nI would not mind if we say \"enabling this may present performance\nbenefits\", with or without \"significant\" before \"performance\nbenefits\", and with or without \", depending how your repository is\nused\" at the end.\n\nThanks.\n"},{"id":"378285","messageId":"de9f547a-ac01-29c4-a330-1a7e7a7b1c20@gmail.com","threadId":"51228","inReplyTo":"xmqqd0ix8me1.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 1/3] repo-settings: create core.featureAdoptionRate setting","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2019-06-28T21:08:19Z","receivedAt":"2019-06-28T21:08:24Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 6/28/2019 4:50 PM, Junio C Hamano wrote:\n> \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> \n>> +core.featureAdoptionRate::\n>> +\tSet an integer value on a scale from 0 to 10 describing your\n>> +\tdesire to adopt new performance features. Defaults to 0. As\n>> +\tthe value increases, features are enabled by changing the\n>> +\tdefault values of other config settings. If a config variable\n>> +\tis specified explicitly, the explicit value will override these\n>> +\tdefaults:\n>> ++\n>> +If the value is at least 3, then the following defaults are modified.\n>> +These represent relatively new features that have existed for multiple\n>> +major releases, and present significant performance benefits. They do\n>> +not modify the user-facing output of porcelain commands.\n>> ++\n>> +* `core.commitGraph=true` enables reading commit-graph files.\n>> ++\n>> +* `gc.writeCommitGraph=true` eneables writing commit-graph files during\n>> +`git gc`.\n> \n> I was re-reading the whole series, and found that the phrase\n> \"present significant benefits\" was somewhat overselling.  Wouldn't\n> that claim largely depend on the end-user's workflow?  The same\n> comment applies to the description of \"at least 5\" level, too.\n> \n> I would not mind if we say \"enabling this may present performance\n> benefits\", with or without \"significant\" before \"performance\n> benefits\", and with or without \", depending how your repository is\n> used\" at the end.\n\nThanks for taking such a close look. Indeed, it is not appropriate\nto over-sell here. I will take another stab at this documentation\nnext week.\n\n-Stolee\n"},{"id":"378287","messageId":"xmqq8stl8k09.fsf@gitster-ct.c.googlers.com","threadId":"51228","inReplyTo":"bdaee3ea9df0533c268d6bebbd252c00cfbaccd6.1560957119.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 1/3] repo-settings: create core.featureAdoptionRate setting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-06-28T21:42:14Z","receivedAt":"2019-06-28T21:42:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n\n> @@ -41,7 +42,6 @@ static int aggressive_depth = 50;\n>  static int aggressive_window = 250;\n>  static int gc_auto_threshold = 6700;\n>  static int gc_auto_pack_limit = 50;\n> -static int gc_write_commit_graph;\n>  static int detach_auto = 1;\n>  static timestamp_t gc_log_expire_time;\n>  static const char *gc_log_expire = \"1.day.ago\";\n> @@ -148,7 +148,6 @@ static void gc_config(void)\n>  \tgit_config_get_int(\"gc.aggressivedepth\", &aggressive_depth);\n>  \tgit_config_get_int(\"gc.auto\", &gc_auto_threshold);\n>  \tgit_config_get_int(\"gc.autopacklimit\", &gc_auto_pack_limit);\n> -\tgit_config_get_bool(\"gc.writecommitgraph\", &gc_write_commit_graph);\n>  \tgit_config_get_bool(\"gc.autodetach\", &detach_auto);\n>  \tgit_config_get_expiry(\"gc.pruneexpire\", &prune_expire);\n>  \tgit_config_get_expiry(\"gc.worktreepruneexpire\", &prune_worktrees_expire);\n> @@ -685,7 +684,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)\n>  \t\tclean_pack_garbage();\n>  \t}\n>  \n> -\tif (gc_write_commit_graph)\n> +\tprepare_repo_settings(the_repository);\n> +\tif (the_repository->settings->gc_write_commit_graph == 1)\n>  \t\twrite_commit_graph_reachable(get_object_directory(), 0,\n>  \t\t\t\t\t     !quiet && !daemonized);\n\nOK, so the general idea is to move per-subsystem local variables to\nnew fields in the repository structure, stop parsing the configuration\nin per-subsystem config callback, which is how the configuration for\nthe writing side is done above, and ...\n\n>  \n> diff --git a/commit-graph.c b/commit-graph.c\n> index 7c5e54875f..b09c465a7a 100644\n> --- a/commit-graph.c\n> +++ b/commit-graph.c\n> @@ -16,6 +16,7 @@\n>  #include \"hashmap.h\"\n>  #include \"replace-object.h\"\n>  #include \"progress.h\"\n> +#include \"repo-settings.h\"\n>  \n>  #define GRAPH_SIGNATURE 0x43475048 /* \"CGPH\" */\n>  #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* \"OIDF\" */\n> @@ -311,7 +312,6 @@ static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)\n>  static int prepare_commit_graph(struct repository *r)\n>  {\n>  \tstruct object_directory *odb;\n> -\tint config_value;\n>  \n>  \tif (git_env_bool(GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD, 0))\n>  \t\tdie(\"dying as requested by the '%s' variable on commit-graph load!\",\n> @@ -321,9 +321,10 @@ static int prepare_commit_graph(struct repository *r)\n>  \t\treturn !!r->objects->commit_graph;\n>  \tr->objects->commit_graph_attempted = 1;\n>  \n> +\tprepare_repo_settings(r);\n> +\n>  \tif (!git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&\n> -\t    (repo_config_get_bool(r, \"core.commitgraph\", &config_value) ||\n> -\t    !config_value))\n> +\t    r->settings->core_commit_graph != 1)\n>  \t\t/*\n>  \t\t * This repository is not configured to use commit graphs, so\n>  \t\t * do not load one. (But report commit_graph_attempted anyway\n\n... how the reading side is done here.  And then instead let the new\nrepo-settings module read them ...\n\n> diff --git a/repo-settings.c b/repo-settings.c\n> new file mode 100644\n> index 0000000000..f7fc2a1959\n> --- /dev/null\n> +++ b/repo-settings.c\n> @@ -0,0 +1,44 @@\n> +#include \"cache.h\"\n> +#include \"repository.h\"\n> +#include \"config.h\"\n> +#include \"repo-settings.h\"\n> +\n> +#define UPDATE_DEFAULT(s,v) do { if (s == -1) { s = v; } } while(0)\n> +\n> +static int git_repo_config(const char *key, const char *value, void *cb)\n> +{\n> +\tstruct repo_settings *rs = (struct repo_settings *)cb;\n> +\n> +\tif (!strcmp(key, \"core.featureadoptionrate\")) {\n> +\t\tint rate = git_config_int(key, value);\n> +\t\tif (rate >= 3) {\n> +\t\t\tUPDATE_DEFAULT(rs->core_commit_graph, 1);\n> +\t\t\tUPDATE_DEFAULT(rs->gc_write_commit_graph, 1);\n> +\t\t}\n> +\t\treturn 0;\n> +\t}\n> +\tif (!strcmp(key, \"core.commitgraph\")) {\n> +\t\trs->core_commit_graph = git_config_bool(key, value);\n> +\t\treturn 0;\n> +\t}\n> +\tif (!strcmp(key, \"gc.writecommitgraph\")) {\n> +\t\trs->gc_write_commit_graph = git_config_bool(key, value);\n> +\t\treturn 0;\n> +\t}\n> +\n> +\treturn 1;\n> +}\n\n... like this.  If a concrete/underlying configuration variable\nappears in the configuration data stream, the fields would get their\nvalues overwritten, and if the adoption rate setting appears\n_before_ concreate ones, they affect the value in the fields.  So\nthe assignment to these fields when the adoption rate setting is\nseen survives only when the underlying variable does not appear\nanywhere in the configuration dasta stream at all.\n\nWhich is exactly what we want.  Nicely written.\n\n> +void prepare_repo_settings(struct repository *r)\n> +{\n> +\tif (r->settings)\n> +\t\treturn;\n> +\n> +\tr->settings = xmalloc(sizeof(*r->settings));\n> +\n> +\t/* Defaults */\n> +\tr->settings->core_commit_graph = -1;\n> +\tr->settings->gc_write_commit_graph = -1;\n> +\n> +\trepo_config(r, git_repo_config, r->settings);\n> +}\n> diff --git a/repo-settings.h b/repo-settings.h\n> new file mode 100644\n> index 0000000000..11d08648e1\n> --- /dev/null\n> +++ b/repo-settings.h\n> @@ -0,0 +1,13 @@\n> +#ifndef REPO_SETTINGS_H\n> +#define REPO_SETTINGS_H\n> +\n> +struct repo_settings {\n> +\tchar core_commit_graph;\n> +\tchar gc_write_commit_graph;\n> +};\n\nI do not see a particular reason to favor type \"char\" here. \"char\"\nis wider than e.g. \"signed int :2\", if you wanted to save space\ncompared to a more obvious and naive type, e.g. \"int\".  More\nimportantly, the language does not guarantee signedness of \"char\",\nso the sentinel value of \"-1\" is a bit tricky to use, as you have to\nbe prepared to see your code used on an \"unsigned char\" platform.\n\nUse of \"signed char\" would be OK, but this is a singleton instance\nper repository, so I am not sure how much it matters to save a few\nwords here by not using the most natural \"int\" type.\n"},{"id":"378314","messageId":"e684ad41-ca93-bad5-cf39-9dcf578a04ae@gmail.com","threadId":"51228","inReplyTo":"xmqq8stl8k09.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 1/3] repo-settings: create core.featureAdoptionRate setting","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2019-06-29T01:43:05Z","receivedAt":"2019-06-29T01:43:09Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 6/28/2019 5:42 PM, Junio C Hamano wrote:\n> \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>\n>> +struct repo_settings {\n>> +\tchar core_commit_graph;\n>> +\tchar gc_write_commit_graph;\n>> +};\n> \n> I do not see a particular reason to favor type \"char\" here. \"char\"\n> is wider than e.g. \"signed int :2\", if you wanted to save space\n> compared to a more obvious and naive type, e.g. \"int\".  More\n> importantly, the language does not guarantee signedness of \"char\",\n> so the sentinel value of \"-1\" is a bit tricky to use, as you have to\n> be prepared to see your code used on an \"unsigned char\" platform.\n\nI was unaware that platforms could change the signedness of \"char\".\nThanks for guarding against it.\n\nYou're right that it probably isn't worth saving space here, as these\nvalues are replacing existing globals somewhere anyway. If we start\nworrying about these being present for each of thousands of submodules\nthen we probably have bigger problems.\n\n> Use of \"signed char\" would be OK, but this is a singleton instance\n> per repository, so I am not sure how much it matters to save a few\n> words here by not using the most natural \"int\" type.\n\nI'll use 'int' in v2.\n\nThanks,\n-Stolee\n"},{"id":"378348","messageId":"CAPUEspg8neAfmKUV3U2DWPnDHOawcbGMNNm_n1MCD7KtLNymLQ@mail.gmail.com","threadId":"51228","inReplyTo":"e684ad41-ca93-bad5-cf39-9dcf578a04ae@gmail.com","subject":"Re: [PATCH v2 1/3] repo-settings: create core.featureAdoptionRate setting","fromName":"Carlo Arenas","fromEmail":"carenas@gmail.com","sentAt":"2019-06-30T18:35:29Z","receivedAt":"2019-06-30T18:35:42Z","isPatch":true,"sender":{"key":"carenas@gmail.com","avatar":"https://avatars.githubusercontent.com/u/76036?v=4"},"body":"On Fri, Jun 28, 2019 at 6:44 PM Derrick Stolee <stolee@gmail.com> wrote:\n>\n> On 6/28/2019 5:42 PM, Junio C Hamano wrote:\n> > \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n> >\n> > Use of \"signed char\" would be OK, but this is a singleton instance\n> > per repository, so I am not sure how much it matters to save a few\n> > words here by not using the most natural \"int\" type.\n>\n> I'll use 'int' in v2.\n\nFWIW, this broke the build in (at least) Linux AArch64, unless the following\nis applied on top of ds/early-access\n\nCarlo\n-- >8 --\nSubject: [PATCH] repo-settings: explicitly make flags using char as signed\n\nused as a three state boolean and therefore could also be set/compared\nwith -1, which will break in architectures that use unsigned char by\ndefault (ex: ARM)\n\nSigned-off-by: Carlo Marcelo Arenas Belón <carenas@gmail.com>\n---\n repo-settings.h | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/repo-settings.h b/repo-settings.h\nindex b50228f992..544873fff4 100644\n--- a/repo-settings.h\n+++ b/repo-settings.h\n@@ -2,9 +2,9 @@\n #define REPO_SETTINGS_H\n\n struct repo_settings {\n-       char core_commit_graph;\n-       char gc_write_commit_graph;\n-       char pack_use_sparse;\n+       signed char core_commit_graph;\n+       signed char gc_write_commit_graph;\n+       signed char pack_use_sparse;\n        int index_version;\n };\n\n-- \n2.22.0\n"},{"id":"378374","messageId":"6cf903a0-03d8-2c18-639e-a89b953eceab@gmail.com","threadId":"51228","inReplyTo":"CAPUEspg8neAfmKUV3U2DWPnDHOawcbGMNNm_n1MCD7KtLNymLQ@mail.gmail.com","subject":"Re: [PATCH v2 1/3] repo-settings: create core.featureAdoptionRate setting","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2019-07-01T12:45:28Z","receivedAt":"2019-07-01T12:45:31Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 6/30/2019 2:35 PM, Carlo Arenas wrote:\n> On Fri, Jun 28, 2019 at 6:44 PM Derrick Stolee <stolee@gmail.com> wrote:\n>>\n>> On 6/28/2019 5:42 PM, Junio C Hamano wrote:\n>>> \"Derrick Stolee via GitGitGadget\" <gitgitgadget@gmail.com> writes:\n>>>\n>>> Use of \"signed char\" would be OK, but this is a singleton instance\n>>> per repository, so I am not sure how much it matters to save a few\n>>> words here by not using the most natural \"int\" type.\n>>\n>> I'll use 'int' in v2.\n> \n> FWIW, this broke the build in (at least) Linux AArch64, unless the following\n> is applied on top of ds/early-access\n\nThanks, Carlo, for reporting this. I'm glad we have someone running early\nbuilds on platforms with these special cases!\n\nI'm working on rerolling to use int.\n\n-Stolee\n\n"},{"id":"378390","messageId":"pull.254.v3.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.v2.git.gitgitgadget@gmail.com","subject":"[PATCH v3 0/3] [RFC] Create 'core.featureAdoptionRate' setting to update config defaults","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-07-01T14:29:08Z","receivedAt":"2019-07-01T14:29:12Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"Here is a second run at this RFC, which aims to create a \"meta\" config\nsetting that automatically turns on other settings according to a user's\nwillingness to trade new Git behavior or new feature risk for performance\nbenefits. The new name for the setting is \"core.featureAdoptionRate\" and is\nan integer scale from 0 to 10. There will be multiple \"categories\" of\nsettings, and the intention is to allow more granular levels as necessary.\n\nThe first category is \"3 or higher\" which means that the user is willing to\nadopt features that have been tested in multiple major releases. The\nsettings to include here are core.commitGraph=true,\ngc.writeCommitGraph=true, and index.version=4.\n\nThe second category is \"5 or higher\" which means the user is willing to\nadopt features that have not been out for multiple major releases. The\nsetting included here is pack.useSparse=true.\n\nIn the future, I would add a \"7 or higher\" setting which means the user is\nwilling to have a change of behavior in exchange for performance benefits.\nThe two settings to place here are 'status.aheadBehind=false' and\n'fetch.showForcedUpdates=false'. Instead of including these settings in the\ncurrent series, I've submitted them independently for full review [1, 2].\n\nHopefully this direction is amenable to allow \"early adopters\" gain access\nto new performance features even if they are not necessary reading every\nline of the release notes.\n\nThanks, -Stolee\n\n[1] https://public-inbox.org/git/pull.272.git.gitgitgadget@gmail.com/\n\n[2] https://public-inbox.org/git/pull.273.git.gitgitgadget@gmail.com/\n\nDerrick Stolee (3):\n  repo-settings: create core.featureAdoptionRate setting\n  repo-settings: use index.version=4 by default\n  repo-settings: pack.useSparse=true\n\n Documentation/config/core.txt  | 34 +++++++++++++++++++-\n Documentation/config/gc.txt    |  4 +--\n Documentation/config/index.txt |  2 ++\n Documentation/config/pack.txt  |  3 +-\n Makefile                       |  1 +\n builtin/gc.c                   |  6 ++--\n builtin/pack-objects.c         |  9 +++---\n commit-graph.c                 |  7 ++--\n read-cache.c                   | 12 ++++---\n repo-settings.c                | 58 ++++++++++++++++++++++++++++++++++\n repo-settings.h                | 15 +++++++++\n repository.h                   |  3 ++\n t/t1600-index.sh               | 34 +++++++++++++++++---\n 13 files changed, 164 insertions(+), 24 deletions(-)\n create mode 100644 repo-settings.c\n create mode 100644 repo-settings.h\n\n\nbase-commit: aa25c82427ae70aebf3b8f970f2afd54e9a2a8c6\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-254%2Fderrickstolee%2Fconfig-large%2Fupstream-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-254/derrickstolee/config-large/upstream-v3\nPull-Request: https://github.com/gitgitgadget/git/pull/254\n\nRange-diff vs v2:\n\n 1:  bdaee3ea9d ! 1:  13b9e71b38 repo-settings: create core.featureAdoptionRate setting\n     @@ -71,8 +71,9 @@\n      ++\n      +If the value is at least 3, then the following defaults are modified.\n      +These represent relatively new features that have existed for multiple\n     -+major releases, and present significant performance benefits. They do\n     -+not modify the user-facing output of porcelain commands.\n     ++major releases, and may present performance benefits. These benefits\n     ++depend on the amount and kind of data in your repo and how you use it.\n     ++The settings do not modify the user-facing output of porcelain commands.\n      ++\n      +* `core.commitGraph=true` enables reading commit-graph files.\n      ++\n     @@ -236,8 +237,8 @@\n      +#define REPO_SETTINGS_H\n      +\n      +struct repo_settings {\n     -+\tchar core_commit_graph;\n     -+\tchar gc_write_commit_graph;\n     ++\tint core_commit_graph;\n     ++\tint gc_write_commit_graph;\n      +};\n      +\n      +struct repository;\n 2:  02c89415fe ! 2:  4fe896e423 repo-settings: use index.version=4 by default\n     @@ -6,6 +6,12 @@\n          This means the index could be compressed using version 4. Set this as\n          a default when core.featureAdoptionRate is at least three.\n      \n     +    Since the index version is written to a file, this is an excellent\n     +    opportunity to test that the config settings are working correctly\n     +    with the different precedence rules. Adapt a test from t1600-index.sh\n     +    to verify the version is set properly with different values of\n     +    index.version config, core.featureAdoptionRate, and GIT_INDEX_VERSION.\n     +\n          Signed-off-by: Derrick Stolee <dstolee@microsoft.com>\n      \n       diff --git a/Documentation/config/core.txt b/Documentation/config/core.txt\n     @@ -108,9 +114,60 @@\n       +++ b/repo-settings.h\n      @@\n       struct repo_settings {\n     - \tchar core_commit_graph;\n     - \tchar gc_write_commit_graph;\n     + \tint core_commit_graph;\n     + \tint gc_write_commit_graph;\n      +\tint index_version;\n       };\n       \n       struct repository;\n     +\n     + diff --git a/t/t1600-index.sh b/t/t1600-index.sh\n     + --- a/t/t1600-index.sh\n     + +++ b/t/t1600-index.sh\n     +@@\n     + \t)\n     + '\n     + \n     +-test_expect_success 'GIT_INDEX_VERSION takes precedence over config' '\n     ++test_index_version () {\n     ++\tINDEX_VERSION_CONFIG=$1 &&\n     ++\tREPO_ADOPTION_RATE=$2 &&\n     ++\tENV_VAR_VERSION=$3\n     ++\tEXPECTED_OUTPUT_VERSION=$4 &&\n     + \t(\n     + \t\trm -f .git/index &&\n     +-\t\tGIT_INDEX_VERSION=4 &&\n     +-\t\texport GIT_INDEX_VERSION &&\n     +-\t\tgit config --add index.version 2 &&\n     ++\t\trm -f .git/config &&\n     ++\t\tif test \"$INDEX_VERSION_CONFIG\" -ne 0\n     ++\t\tthen\n     ++\t\t\tgit config --add index.version $INDEX_VERSION_CONFIG\n     ++\t\tfi &&\n     ++\t\tif test \"$REPO_ADOPTION_RATE\" -ne 0\n     ++\t\tthen\n     ++\t\t\tgit config --add core.featureAdoptionRate $REPO_ADOPTION_RATE\n     ++\t\tfi &&\n     ++\t\tif test \"$ENV_VAR_VERSION\" -ne 0\n     ++\t\tthen\n     ++\t\t\tGIT_INDEX_VERSION=$ENV_VAR_VERSION &&\n     ++\t\t\texport GIT_INDEX_VERSION\n     ++\t\telse\n     ++\t\t\tunset GIT_INDEX_VERSION\n     ++\t\tfi &&\n     + \t\tgit add a 2>&1 &&\n     +-\t\techo 4 >expect &&\n     ++\t\techo $EXPECTED_OUTPUT_VERSION >expect &&\n     + \t\ttest-tool index-version <.git/index >actual &&\n     + \t\ttest_cmp expect actual\n     + \t)\n     ++}\n     ++\n     ++test_expect_success 'index version config precedence' '\n     ++\ttest_index_version 2 0 4 4 &&\n     ++\ttest_index_version 2 3 0 2 &&\n     ++\ttest_index_version 0 3 0 4 &&\n     ++\ttest_index_version 0 3 2 2\n     + '\n     + \n     + test_done\n 3:  5bba9062f4 ! 3:  d080065a92 repo-settings: pack.useSparse=true\n     @@ -117,9 +117,9 @@\n       +++ b/repo-settings.h\n      @@\n       struct repo_settings {\n     - \tchar core_commit_graph;\n     - \tchar gc_write_commit_graph;\n     -+\tchar pack_use_sparse;\n     + \tint core_commit_graph;\n     + \tint gc_write_commit_graph;\n     ++\tint pack_use_sparse;\n       \tint index_version;\n       };\n       \n\n-- \ngitgitgadget\n"},{"id":"378391","messageId":"4fe896e423b698ef60b208c613252b4847b6cd0a.1561991348.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.v3.git.gitgitgadget@gmail.com","subject":"[PATCH v3 2/3] repo-settings: use index.version=4 by default","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-07-01T14:29:10Z","receivedAt":"2019-07-01T14:29:14Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nIf a repo is large, it likely has many paths in its working directory.\nThis means the index could be compressed using version 4. Set this as\na default when core.featureAdoptionRate is at least three.\n\nSince the index version is written to a file, this is an excellent\nopportunity to test that the config settings are working correctly\nwith the different precedence rules. Adapt a test from t1600-index.sh\nto verify the version is set properly with different values of\nindex.version config, core.featureAdoptionRate, and GIT_INDEX_VERSION.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/config/core.txt  |  3 +++\n Documentation/config/index.txt |  2 ++\n read-cache.c                   | 12 +++++++-----\n repo-settings.c                |  6 ++++++\n repo-settings.h                |  1 +\n t/t1600-index.sh               | 34 +++++++++++++++++++++++++++++-----\n 6 files changed, 48 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/config/core.txt b/Documentation/config/core.txt\nindex bfe647c76f..865252aba9 100644\n--- a/Documentation/config/core.txt\n+++ b/Documentation/config/core.txt\n@@ -621,3 +621,6 @@ The settings do not modify the user-facing output of porcelain commands.\n +\n * `gc.writeCommitGraph=true` eneables writing commit-graph files during\n `git gc`.\n++\n+* `index.version=4` uses prefix-compression to reduce the size of the\n+.git/index file.\ndiff --git a/Documentation/config/index.txt b/Documentation/config/index.txt\nindex f181503041..98a88c30be 100644\n--- a/Documentation/config/index.txt\n+++ b/Documentation/config/index.txt\n@@ -24,3 +24,5 @@ index.threads::\n index.version::\n \tSpecify the version with which new index files should be\n \tinitialized.  This does not affect existing repositories.\n+\tIf `core.featureAdoptionRate` is at least three, then the\n+\tdefault value is 4.\ndiff --git a/read-cache.c b/read-cache.c\nindex 22e7b9944e..7fab8ff748 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -25,6 +25,7 @@\n #include \"fsmonitor.h\"\n #include \"thread-utils.h\"\n #include \"progress.h\"\n+#include \"repo-settings.h\"\n \n /* Mask for the name length in ce_flags in the on-disk index */\n \n@@ -1599,16 +1600,17 @@ struct cache_entry *refresh_cache_entry(struct index_state *istate,\n \n #define INDEX_FORMAT_DEFAULT 3\n \n-static unsigned int get_index_format_default(void)\n+static unsigned int get_index_format_default(struct repository *r)\n {\n \tchar *envversion = getenv(\"GIT_INDEX_VERSION\");\n \tchar *endp;\n-\tint value;\n \tunsigned int version = INDEX_FORMAT_DEFAULT;\n \n \tif (!envversion) {\n-\t\tif (!git_config_get_int(\"index.version\", &value))\n-\t\t\tversion = value;\n+\t\tprepare_repo_settings(r);\n+\n+\t\tif (r->settings->index_version >= 0)\n+\t\t\tversion = r->settings->index_version;\n \t\tif (version < INDEX_FORMAT_LB || INDEX_FORMAT_UB < version) {\n \t\t\twarning(_(\"index.version set, but the value is invalid.\\n\"\n \t\t\t\t  \"Using version %i\"), INDEX_FORMAT_DEFAULT);\n@@ -2765,7 +2767,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,\n \t}\n \n \tif (!istate->version) {\n-\t\tistate->version = get_index_format_default();\n+\t\tistate->version = get_index_format_default(the_repository);\n \t\tif (git_env_bool(\"GIT_TEST_SPLIT_INDEX\", 0))\n \t\t\tinit_split_index(istate);\n \t}\ndiff --git a/repo-settings.c b/repo-settings.c\nindex f7fc2a1959..5753153a84 100644\n--- a/repo-settings.c\n+++ b/repo-settings.c\n@@ -14,6 +14,7 @@ static int git_repo_config(const char *key, const char *value, void *cb)\n \t\tif (rate >= 3) {\n \t\t\tUPDATE_DEFAULT(rs->core_commit_graph, 1);\n \t\t\tUPDATE_DEFAULT(rs->gc_write_commit_graph, 1);\n+\t\t\tUPDATE_DEFAULT(rs->index_version, 4);\n \t\t}\n \t\treturn 0;\n \t}\n@@ -25,6 +26,10 @@ static int git_repo_config(const char *key, const char *value, void *cb)\n \t\trs->gc_write_commit_graph = git_config_bool(key, value);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(key, \"index.version\")) {\n+\t\trs->index_version = git_config_int(key, value);\n+\t\treturn 0;\n+\t}\n \n \treturn 1;\n }\n@@ -39,6 +44,7 @@ void prepare_repo_settings(struct repository *r)\n \t/* Defaults */\n \tr->settings->core_commit_graph = -1;\n \tr->settings->gc_write_commit_graph = -1;\n+\tr->settings->index_version = -1;\n \n \trepo_config(r, git_repo_config, r->settings);\n }\ndiff --git a/repo-settings.h b/repo-settings.h\nindex 7d44627bf0..b752dfe8b4 100644\n--- a/repo-settings.h\n+++ b/repo-settings.h\n@@ -4,6 +4,7 @@\n struct repo_settings {\n \tint core_commit_graph;\n \tint gc_write_commit_graph;\n+\tint index_version;\n };\n \n struct repository;\ndiff --git a/t/t1600-index.sh b/t/t1600-index.sh\nindex 42962ed7d4..74f56e2769 100755\n--- a/t/t1600-index.sh\n+++ b/t/t1600-index.sh\n@@ -59,17 +59,41 @@ test_expect_success 'out of bounds index.version issues warning' '\n \t)\n '\n \n-test_expect_success 'GIT_INDEX_VERSION takes precedence over config' '\n+test_index_version () {\n+\tINDEX_VERSION_CONFIG=$1 &&\n+\tREPO_ADOPTION_RATE=$2 &&\n+\tENV_VAR_VERSION=$3\n+\tEXPECTED_OUTPUT_VERSION=$4 &&\n \t(\n \t\trm -f .git/index &&\n-\t\tGIT_INDEX_VERSION=4 &&\n-\t\texport GIT_INDEX_VERSION &&\n-\t\tgit config --add index.version 2 &&\n+\t\trm -f .git/config &&\n+\t\tif test \"$INDEX_VERSION_CONFIG\" -ne 0\n+\t\tthen\n+\t\t\tgit config --add index.version $INDEX_VERSION_CONFIG\n+\t\tfi &&\n+\t\tif test \"$REPO_ADOPTION_RATE\" -ne 0\n+\t\tthen\n+\t\t\tgit config --add core.featureAdoptionRate $REPO_ADOPTION_RATE\n+\t\tfi &&\n+\t\tif test \"$ENV_VAR_VERSION\" -ne 0\n+\t\tthen\n+\t\t\tGIT_INDEX_VERSION=$ENV_VAR_VERSION &&\n+\t\t\texport GIT_INDEX_VERSION\n+\t\telse\n+\t\t\tunset GIT_INDEX_VERSION\n+\t\tfi &&\n \t\tgit add a 2>&1 &&\n-\t\techo 4 >expect &&\n+\t\techo $EXPECTED_OUTPUT_VERSION >expect &&\n \t\ttest-tool index-version <.git/index >actual &&\n \t\ttest_cmp expect actual\n \t)\n+}\n+\n+test_expect_success 'index version config precedence' '\n+\ttest_index_version 2 0 4 4 &&\n+\ttest_index_version 2 3 0 2 &&\n+\ttest_index_version 0 3 0 4 &&\n+\ttest_index_version 0 3 2 2\n '\n \n test_done\n-- \ngitgitgadget\n\n"},{"id":"378392","messageId":"13b9e71b383485885c4823baa466c32511fd20bc.1561991348.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.v3.git.gitgitgadget@gmail.com","subject":"[PATCH v3 1/3] repo-settings: create core.featureAdoptionRate setting","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-07-01T14:29:09Z","receivedAt":"2019-07-01T14:29:15Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nSeveral advanced config settings are highly recommended for clients\nusing large repositories. Power users learn these one-by-one and\nenable them as they see fit. This could be made simpler, to allow\nmore users to have access to these almost-always beneficial features\n(and more beneficial in larger repos).\n\nCreate a 'core.featureAdoptionRate' config setting that allows integer\nvalues. This is a rating from 0 to 10 for the user's willingness to\nadopt new or experimental features that improve Git performance.\nThe default is 0, meaning \"don't change anything!\" A value of 10\nwould mean \"I'm willing for some behavior to change to get the\nbest performance I can get, and can take experimental features\nin their first release.\" As we integrate this with more config\nsettings, we will make this scale more clear.\n\nThis config setting only changes the default values of other config\nsettings. If the setting is given explicitly, then take the\nexplicit value.\n\nThis change adds these two defaults when core.featureAdoptionRate\nis at least three:\n\n * core.commitGraph=true\n * gc.writeCommitGraph=true\n\nThe use of \"three or higher\" for these settings means that a value\nof 3 means \"I'm willing to add optional features that can augment\nthe data on disk in favor of improved performance, but those\nfeatures should be stable after being included in multiple major\nreleases.\"\n\nTo centralize these config options and properly set the defaults,\ncreate a repo_settings that contains chars for each config variable.\nUse -1 as \"unset\", with 0 for false and 1 for true.\n\nThe prepare_repo_settings() method ensures that this settings\nstruct has been initialized, and avoids double-scanning the config\nsettings.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/config/core.txt | 22 +++++++++++++++++-\n Documentation/config/gc.txt   |  4 ++--\n Makefile                      |  1 +\n builtin/gc.c                  |  6 ++---\n commit-graph.c                |  7 +++---\n repo-settings.c               | 44 +++++++++++++++++++++++++++++++++++\n repo-settings.h               | 13 +++++++++++\n repository.h                  |  3 +++\n 8 files changed, 91 insertions(+), 9 deletions(-)\n create mode 100644 repo-settings.c\n create mode 100644 repo-settings.h\n\ndiff --git a/Documentation/config/core.txt b/Documentation/config/core.txt\nindex 75538d27e7..bfe647c76f 100644\n--- a/Documentation/config/core.txt\n+++ b/Documentation/config/core.txt\n@@ -577,7 +577,8 @@ the `GIT_NOTES_REF` environment variable.  See linkgit:git-notes[1].\n \n core.commitGraph::\n \tIf true, then git will read the commit-graph file (if it exists)\n-\tto parse the graph structure of commits. Defaults to false. See\n+\tto parse the graph structure of commits. Defaults to false, unless\n+\t`core.featureAdoptionRate` is at least three. See\n \tlinkgit:git-commit-graph[1] for more information.\n \n core.useReplaceRefs::\n@@ -601,3 +602,22 @@ core.abbrev::\n \tin your repository, which hopefully is enough for\n \tabbreviated object names to stay unique for some time.\n \tThe minimum length is 4.\n+\n+core.featureAdoptionRate::\n+\tSet an integer value on a scale from 0 to 10 describing your\n+\tdesire to adopt new performance features. Defaults to 0. As\n+\tthe value increases, features are enabled by changing the\n+\tdefault values of other config settings. If a config variable\n+\tis specified explicitly, the explicit value will override these\n+\tdefaults:\n++\n+If the value is at least 3, then the following defaults are modified.\n+These represent relatively new features that have existed for multiple\n+major releases, and may present performance benefits. These benefits\n+depend on the amount and kind of data in your repo and how you use it.\n+The settings do not modify the user-facing output of porcelain commands.\n++\n+* `core.commitGraph=true` enables reading commit-graph files.\n++\n+* `gc.writeCommitGraph=true` eneables writing commit-graph files during\n+`git gc`.\ndiff --git a/Documentation/config/gc.txt b/Documentation/config/gc.txt\nindex 02b92b18b5..898263209c 100644\n--- a/Documentation/config/gc.txt\n+++ b/Documentation/config/gc.txt\n@@ -63,8 +63,8 @@ gc.writeCommitGraph::\n \tIf true, then gc will rewrite the commit-graph file when\n \tlinkgit:git-gc[1] is run. When using `git gc --auto`\n \tthe commit-graph will be updated if housekeeping is\n-\trequired. Default is false. See linkgit:git-commit-graph[1]\n-\tfor details.\n+\trequired. Default is false, unless `core.featureAdoptionRage`\n+\tis at least three. See linkgit:git-commit-graph[1] for details.\n \n gc.logExpiry::\n \tIf the file gc.log exists, then `git gc --auto` will print\ndiff --git a/Makefile b/Makefile\nindex 8a7e235352..2d3499d7ac 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -967,6 +967,7 @@ LIB_OBJS += refspec.o\n LIB_OBJS += ref-filter.o\n LIB_OBJS += remote.o\n LIB_OBJS += replace-object.o\n+LIB_OBJS += repo-settings.o\n LIB_OBJS += repository.o\n LIB_OBJS += rerere.o\n LIB_OBJS += resolve-undo.o\ndiff --git a/builtin/gc.c b/builtin/gc.c\nindex 8943bcc300..6281aad961 100644\n--- a/builtin/gc.c\n+++ b/builtin/gc.c\n@@ -27,6 +27,7 @@\n #include \"pack-objects.h\"\n #include \"blob.h\"\n #include \"tree.h\"\n+#include \"repo-settings.h\"\n \n #define FAILED_RUN \"failed to run %s\"\n \n@@ -41,7 +42,6 @@ static int aggressive_depth = 50;\n static int aggressive_window = 250;\n static int gc_auto_threshold = 6700;\n static int gc_auto_pack_limit = 50;\n-static int gc_write_commit_graph;\n static int detach_auto = 1;\n static timestamp_t gc_log_expire_time;\n static const char *gc_log_expire = \"1.day.ago\";\n@@ -148,7 +148,6 @@ static void gc_config(void)\n \tgit_config_get_int(\"gc.aggressivedepth\", &aggressive_depth);\n \tgit_config_get_int(\"gc.auto\", &gc_auto_threshold);\n \tgit_config_get_int(\"gc.autopacklimit\", &gc_auto_pack_limit);\n-\tgit_config_get_bool(\"gc.writecommitgraph\", &gc_write_commit_graph);\n \tgit_config_get_bool(\"gc.autodetach\", &detach_auto);\n \tgit_config_get_expiry(\"gc.pruneexpire\", &prune_expire);\n \tgit_config_get_expiry(\"gc.worktreepruneexpire\", &prune_worktrees_expire);\n@@ -685,7 +684,8 @@ int cmd_gc(int argc, const char **argv, const char *prefix)\n \t\tclean_pack_garbage();\n \t}\n \n-\tif (gc_write_commit_graph)\n+\tprepare_repo_settings(the_repository);\n+\tif (the_repository->settings->gc_write_commit_graph == 1)\n \t\twrite_commit_graph_reachable(get_object_directory(), 0,\n \t\t\t\t\t     !quiet && !daemonized);\n \ndiff --git a/commit-graph.c b/commit-graph.c\nindex 7c5e54875f..b09c465a7a 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -16,6 +16,7 @@\n #include \"hashmap.h\"\n #include \"replace-object.h\"\n #include \"progress.h\"\n+#include \"repo-settings.h\"\n \n #define GRAPH_SIGNATURE 0x43475048 /* \"CGPH\" */\n #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* \"OIDF\" */\n@@ -311,7 +312,6 @@ static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)\n static int prepare_commit_graph(struct repository *r)\n {\n \tstruct object_directory *odb;\n-\tint config_value;\n \n \tif (git_env_bool(GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD, 0))\n \t\tdie(\"dying as requested by the '%s' variable on commit-graph load!\",\n@@ -321,9 +321,10 @@ static int prepare_commit_graph(struct repository *r)\n \t\treturn !!r->objects->commit_graph;\n \tr->objects->commit_graph_attempted = 1;\n \n+\tprepare_repo_settings(r);\n+\n \tif (!git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&\n-\t    (repo_config_get_bool(r, \"core.commitgraph\", &config_value) ||\n-\t    !config_value))\n+\t    r->settings->core_commit_graph != 1)\n \t\t/*\n \t\t * This repository is not configured to use commit graphs, so\n \t\t * do not load one. (But report commit_graph_attempted anyway\ndiff --git a/repo-settings.c b/repo-settings.c\nnew file mode 100644\nindex 0000000000..f7fc2a1959\n--- /dev/null\n+++ b/repo-settings.c\n@@ -0,0 +1,44 @@\n+#include \"cache.h\"\n+#include \"repository.h\"\n+#include \"config.h\"\n+#include \"repo-settings.h\"\n+\n+#define UPDATE_DEFAULT(s,v) do { if (s == -1) { s = v; } } while(0)\n+\n+static int git_repo_config(const char *key, const char *value, void *cb)\n+{\n+\tstruct repo_settings *rs = (struct repo_settings *)cb;\n+\n+\tif (!strcmp(key, \"core.featureadoptionrate\")) {\n+\t\tint rate = git_config_int(key, value);\n+\t\tif (rate >= 3) {\n+\t\t\tUPDATE_DEFAULT(rs->core_commit_graph, 1);\n+\t\t\tUPDATE_DEFAULT(rs->gc_write_commit_graph, 1);\n+\t\t}\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(key, \"core.commitgraph\")) {\n+\t\trs->core_commit_graph = git_config_bool(key, value);\n+\t\treturn 0;\n+\t}\n+\tif (!strcmp(key, \"gc.writecommitgraph\")) {\n+\t\trs->gc_write_commit_graph = git_config_bool(key, value);\n+\t\treturn 0;\n+\t}\n+\n+\treturn 1;\n+}\n+\n+void prepare_repo_settings(struct repository *r)\n+{\n+\tif (r->settings)\n+\t\treturn;\n+\n+\tr->settings = xmalloc(sizeof(*r->settings));\n+\n+\t/* Defaults */\n+\tr->settings->core_commit_graph = -1;\n+\tr->settings->gc_write_commit_graph = -1;\n+\n+\trepo_config(r, git_repo_config, r->settings);\n+}\ndiff --git a/repo-settings.h b/repo-settings.h\nnew file mode 100644\nindex 0000000000..7d44627bf0\n--- /dev/null\n+++ b/repo-settings.h\n@@ -0,0 +1,13 @@\n+#ifndef REPO_SETTINGS_H\n+#define REPO_SETTINGS_H\n+\n+struct repo_settings {\n+\tint core_commit_graph;\n+\tint gc_write_commit_graph;\n+};\n+\n+struct repository;\n+\n+void prepare_repo_settings(struct repository *r);\n+\n+#endif /* REPO_SETTINGS_H */\ndiff --git a/repository.h b/repository.h\nindex 4fb6a5885f..352afc9cd8 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -4,6 +4,7 @@\n #include \"path.h\"\n \n struct config_set;\n+struct repo_settings;\n struct git_hash_algo;\n struct index_state;\n struct lock_file;\n@@ -72,6 +73,8 @@ struct repository {\n \t */\n \tchar *submodule_prefix;\n \n+\tstruct repo_settings *settings;\n+\n \t/* Subsystems */\n \t/*\n \t * Repository's config which contains key-value pairs from the usual\n-- \ngitgitgadget\n\n"},{"id":"378393","messageId":"d080065a9208852a7e551cc8bef7d326576c076d.1561991348.git.gitgitgadget@gmail.com","threadId":"51228","inReplyTo":"pull.254.v3.git.gitgitgadget@gmail.com","subject":"[PATCH v3 3/3] repo-settings: pack.useSparse=true","fromName":"Derrick Stolee via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2019-07-01T14:29:11Z","receivedAt":"2019-07-01T14:29:16Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"From: Derrick Stolee <dstolee@microsoft.com>\n\nIf a repo is large, then it probably has a very large working\ndirectory. In this case, a typical developer's edits usually impact\nmany fewer paths than the full path set. The sparse treewalk\nalgorithm is optimized for this case, speeding up 'git push' calls.\n\nUse pack.useSparse=true when core.featureAdoptionRate is at least\nfive. This is the first setting where the feature has only been\nout for a single major version. This could be moved to the \"at\nleast three\" category after another major version.\n\nSigned-off-by: Derrick Stolee <dstolee@microsoft.com>\n---\n Documentation/config/core.txt | 9 +++++++++\n Documentation/config/pack.txt | 3 ++-\n builtin/pack-objects.c        | 9 +++++----\n repo-settings.c               | 8 ++++++++\n repo-settings.h               | 1 +\n 5 files changed, 25 insertions(+), 5 deletions(-)\n\ndiff --git a/Documentation/config/core.txt b/Documentation/config/core.txt\nindex 865252aba9..60356102a8 100644\n--- a/Documentation/config/core.txt\n+++ b/Documentation/config/core.txt\n@@ -624,3 +624,12 @@ The settings do not modify the user-facing output of porcelain commands.\n +\n * `index.version=4` uses prefix-compression to reduce the size of the\n .git/index file.\n++\n+If the value is at least 5, then all of the defaults above are included,\n+plus the defaults below. These represent new features that present\n+significant performance benefits, but may not have been released for\n+multiple major versions.\n++\n+* `pack.useSparse=true` uses the sparse tree-walk algorithm, which is\n+optimized for enumerating objects during linkgit:git-push[1] from a\n+client machine.\ndiff --git a/Documentation/config/pack.txt b/Documentation/config/pack.txt\nindex 9cdcfa7324..9c4f8ea9ff 100644\n--- a/Documentation/config/pack.txt\n+++ b/Documentation/config/pack.txt\n@@ -112,7 +112,8 @@ pack.useSparse::\n \tobjects. This can have significant performance benefits when\n \tcomputing a pack to send a small change. However, it is possible\n \tthat extra objects are added to the pack-file if the included\n-\tcommits contain certain types of direct renames.\n+\tcommits contain certain types of direct renames. Defaults to\n+\tfalse, unless `core.featureAdoptionRate` is at least five.\n \n pack.writeBitmaps (deprecated)::\n \tThis is a deprecated synonym for `repack.writeBitmaps`.\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 41d7fc5983..f26b3f2892 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -34,6 +34,7 @@\n #include \"dir.h\"\n #include \"midx.h\"\n #include \"trace2.h\"\n+#include \"repo-settings.h\"\n \n #define IN_PACK(obj) oe_in_pack(&to_pack, obj)\n #define SIZE(obj) oe_size(&to_pack, obj)\n@@ -2707,10 +2708,6 @@ static int git_pack_config(const char *k, const char *v, void *cb)\n \t\tuse_bitmap_index_default = git_config_bool(k, v);\n \t\treturn 0;\n \t}\n-\tif (!strcmp(k, \"pack.usesparse\")) {\n-\t\tsparse = git_config_bool(k, v);\n-\t\treturn 0;\n-\t}\n \tif (!strcmp(k, \"pack.threads\")) {\n \t\tdelta_search_threads = git_config_int(k, v);\n \t\tif (delta_search_threads < 0)\n@@ -3330,6 +3327,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \tread_replace_refs = 0;\n \n \tsparse = git_env_bool(\"GIT_TEST_PACK_SPARSE\", 0);\n+\tprepare_repo_settings(the_repository);\n+\tif (!sparse && the_repository->settings->pack_use_sparse != -1)\n+\t\tsparse = the_repository->settings->pack_use_sparse;\n+\n \treset_pack_idx_option(&pack_idx_opts);\n \tgit_config(git_pack_config, NULL);\n \ndiff --git a/repo-settings.c b/repo-settings.c\nindex 5753153a84..c700edc286 100644\n--- a/repo-settings.c\n+++ b/repo-settings.c\n@@ -16,6 +16,9 @@ static int git_repo_config(const char *key, const char *value, void *cb)\n \t\t\tUPDATE_DEFAULT(rs->gc_write_commit_graph, 1);\n \t\t\tUPDATE_DEFAULT(rs->index_version, 4);\n \t\t}\n+\t\tif (rate >= 5) {\n+\t\t\tUPDATE_DEFAULT(rs->pack_use_sparse, 1);\n+\t\t}\n \t\treturn 0;\n \t}\n \tif (!strcmp(key, \"core.commitgraph\")) {\n@@ -26,6 +29,10 @@ static int git_repo_config(const char *key, const char *value, void *cb)\n \t\trs->gc_write_commit_graph = git_config_bool(key, value);\n \t\treturn 0;\n \t}\n+\tif (!strcmp(key, \"pack.usesparse\")) {\n+\t\trs->pack_use_sparse = git_config_bool(key, value);\n+\t\treturn 0;\n+\t}\n \tif (!strcmp(key, \"index.version\")) {\n \t\trs->index_version = git_config_int(key, value);\n \t\treturn 0;\n@@ -44,6 +51,7 @@ void prepare_repo_settings(struct repository *r)\n \t/* Defaults */\n \tr->settings->core_commit_graph = -1;\n \tr->settings->gc_write_commit_graph = -1;\n+\tr->settings->pack_use_sparse = -1;\n \tr->settings->index_version = -1;\n \n \trepo_config(r, git_repo_config, r->settings);\ndiff --git a/repo-settings.h b/repo-settings.h\nindex b752dfe8b4..1151c2193a 100644\n--- a/repo-settings.h\n+++ b/repo-settings.h\n@@ -4,6 +4,7 @@\n struct repo_settings {\n \tint core_commit_graph;\n \tint gc_write_commit_graph;\n+\tint pack_use_sparse;\n \tint index_version;\n };\n \n-- \ngitgitgadget\n"},{"id":"378466","messageId":"CAPUEspi_c9P=1LNEu2Oej3d5wcpYVVxys=aOT6Ow47vDY+0M8A@mail.gmail.com","threadId":"51228","inReplyTo":"13b9e71b383485885c4823baa466c32511fd20bc.1561991348.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 1/3] repo-settings: create core.featureAdoptionRate setting","fromName":"Carlo Arenas","fromEmail":"carenas@gmail.com","sentAt":"2019-07-01T23:27:01Z","receivedAt":"2019-07-01T23:27:18Z","isPatch":true,"sender":{"key":"carenas@gmail.com","avatar":"https://avatars.githubusercontent.com/u/76036?v=4"},"body":"On Mon, Jul 1, 2019 at 8:32 AM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n>\n> To centralize these config options and properly set the defaults,\n> create a repo_settings that contains chars for each config variable.\n> Use -1 as \"unset\", with 0 for false and 1 for true.\n\nminor nitpick that hopefully Junio can fix: s/chars/ints\n\n> +* `gc.writeCommitGraph=true` eneables writing commit-graph files during\n\ntypo: s/eneables/enable\n\nCarlo\n"},{"id":"378483","messageId":"CACsJy8Cwxov9VWq_MpeWstGtMB-rTy6LYyFj_PF9oSP0kqcDXQ@mail.gmail.com","threadId":"51228","inReplyTo":"13b9e71b383485885c4823baa466c32511fd20bc.1561991348.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 1/3] repo-settings: create core.featureAdoptionRate setting","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-07-02T09:20:21Z","receivedAt":"2019-07-02T09:20:49Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Mon, Jul 1, 2019 at 10:32 PM Derrick Stolee via GitGitGadget\n<gitgitgadget@gmail.com> wrote:\n> @@ -601,3 +602,22 @@ core.abbrev::\n>         in your repository, which hopefully is enough for\n>         abbreviated object names to stay unique for some time.\n>         The minimum length is 4.\n> +\n> +core.featureAdoptionRate::\n> +       Set an integer value on a scale from 0 to 10 describing your\n> +       desire to adopt new performance features. Defaults to 0. As\n> +       the value increases, features are enabled by changing the\n> +       default values of other config settings. If a config variable\n> +       is specified explicitly, the explicit value will override these\n> +       defaults:\n\nThis is because I'd like to keep core.* from growing too big (it's\nalready big), hard to read, search and maintain. Perhaps this should\nbelong to a separate group? Something like tuning.something or\ndefaults.something.\n\n> +If the value is at least 3, then the following defaults are modified.\n> +These represent relatively new features that have existed for multiple\n> +major releases, and may present performance benefits. These benefits\n> +depend on the amount and kind of data in your repo and how you use it.\n\nThen instead of numeric values, maybe the user should write some sort\ndescription about the repo and we optimize for that, similar to gcc\n-Os optimized for size, -Ofast for compiler speed (-O<n> is all about\nexecution speed).\n\nWe could write, for example, tuning.commitHistory = {small, medium,\nlarge} and tuning.worktree = {small, large, medium} and maybe\ntuning.refSize and use that to optimize. We can still have different\noptimization levels (probably just \"none\", \"recommended\" vs\n\"aggressive\" where agressive enables most new stuff),\n-- \nDuy\n"},{"id":"378486","messageId":"87sgro7lxo.fsf@evledraar.gmail.com","threadId":"51228","inReplyTo":"bdaee3ea9df0533c268d6bebbd252c00cfbaccd6.1560957119.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 1/3] repo-settings: create core.featureAdoptionRate setting","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2019-07-02T10:47:15Z","receivedAt":"2019-07-02T10:47:20Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Jun 19 2019, Derrick Stolee via GitGitGadget wrote:\n\n>  core.commitGraph::\n>  \tIf true, then git will read the commit-graph file (if it exists)\n> -\tto parse the graph structure of commits. Defaults to false. See\n> +\tto parse the graph structure of commits. Defaults to false, unless\n> +\t`core.featureAdoptionRate` is at least three. See\n>  \tlinkgit:git-commit-graph[1] for more information.\n>\n>  core.useReplaceRefs::\n> @@ -601,3 +602,21 @@ core.abbrev::\n>  \tin your repository, which hopefully is enough for\n>  \tabbreviated object names to stay unique for some time.\n>  \tThe minimum length is 4.\n> +\n> +core.featureAdoptionRate::\n> +\tSet an integer value on a scale from 0 to 10 describing your\n> +\tdesire to adopt new performance features. Defaults to 0. As\n> +\tthe value increases, features are enabled by changing the\n> +\tdefault values of other config settings. If a config variable\n> +\tis specified explicitly, the explicit value will override these\n> +\tdefaults:\n> ++\n> +If the value is at least 3, then the following defaults are modified.\n> +These represent relatively new features that have existed for multiple\n> +major releases, and present significant performance benefits. They do\n> +not modify the user-facing output of porcelain commands.\n> ++\n> +* `core.commitGraph=true` enables reading commit-graph files.\n> ++\n> +* `gc.writeCommitGraph=true` eneables writing commit-graph files during\n\nI barked up a similar tree in\nhttps://public-inbox.org/git/CACBZZX5SbYo5fVPtK6LW1FF96nR5591RHHC-5wdjW-fmg1R0EQ@mail.gmail.com/\n\nI wonder if you've seen that & what you think about that\napproach. I.e. have a core.version=2.28 (or core.version=+6) or whatever\nto opt-in to features we'd make default in 2.28. Would that be your\ncore.featureAdoptionRate=6 (28-28 = 6)?\n\nI admit that question is partly rhetorical, because I think it suggests\nhow hard it would be for users to reason about this.\n\nThe \"core.version\" idea also sucks, but at least it's bound to our\nadvertised version number, so it's obvious if you set it to e.g. +2 what\nfeature track you're on, and furthermore when we'd commit to making that\nthe default for users who don't set core.version (although we could of\ncourse always change our minds...). It's also something that mirrors how\ne.g. Perl, C compilers (with --std=*) treat this sort of thing.\n\nSo I'm all for a facility to have a setting to collectively opt-in to\nnew things early. But I think for such a thing we really should a) at\nleast in principle commit to making those things the default eventually\n(if they don't suck) b) it needs to be obvious to the user how the\n\"rate\" relates to git releases.\n\nThis \"core.featureAdoptionRate\" value seems more like zlib compression\nvalues & unrelated to release numbers. It's also for \"performance\nfeatures\" only but squats a more general name. I suggested\n\"core.version\" & then \"core.uiVersion\" (in\nhttps://public-inbox.org/git/87pnunxz5i.fsf@evledraar.gmail.com/).\n\nRegardless of whether we want to pin opt-in early-bird features to\nversion numbers in some way, which I think is a good idea, but maybe\nothers disagree. I think if it's \"just performance\" it's good to put\nthat in the key name in such a way that we can have \"early UI\" features,\nor other non-UI non-performance.\n\nThanks for working on this!\n"},{"id":"378487","messageId":"87r2787lms.fsf@evledraar.gmail.com","threadId":"51228","inReplyTo":"CACsJy8Cwxov9VWq_MpeWstGtMB-rTy6LYyFj_PF9oSP0kqcDXQ@mail.gmail.com","subject":"Re: [PATCH v3 1/3] repo-settings: create core.featureAdoptionRate setting","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2019-07-02T10:53:47Z","receivedAt":"2019-07-02T10:53:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Tue, Jul 02 2019, Duy Nguyen wrote:\n\n> On Mon, Jul 1, 2019 at 10:32 PM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n>> @@ -601,3 +602,22 @@ core.abbrev::\n>>         in your repository, which hopefully is enough for\n>>         abbreviated object names to stay unique for some time.\n>>         The minimum length is 4.\n>> +\n>> +core.featureAdoptionRate::\n>> +       Set an integer value on a scale from 0 to 10 describing your\n>> +       desire to adopt new performance features. Defaults to 0. As\n>> +       the value increases, features are enabled by changing the\n>> +       default values of other config settings. If a config variable\n>> +       is specified explicitly, the explicit value will override these\n>> +       defaults:\n>\n> This is because I'd like to keep core.* from growing too big (it's\n> already big), hard to read, search and maintain. Perhaps this should\n> belong to a separate group? Something like tuning.something or\n> defaults.something.\n\nThe main thing users look at is \"man git-config\" (or its web rendering)\nwhich renders it all in one page anyway.\n\nI think in general adding more things to core.* sucks less than\nexplaining the special-case that \"tuning.*\" isn't a config for\ngit-tuning(1) (although we have some of that already, e.g. with\ntrace2.*).\n\nDocumentation/config/core.txt is ~600 lines. Maybe it would be a good\nidea to split it up, similar to your split of\nDocumentation/config/*.txt, but let's not conflate how we'd like to\nmaintain stuff in git.git with a config interface we expose externally.\n\nIt's going to be very confusing for users if some settings that\notherwise would be in core aren't there because a file in git.git was\n\"too big\" at the time. Users (mostly) aren't going to know/care in what\nchronological order we added config keys.\n"},{"id":"378488","messageId":"CACsJy8Aqdb_-5ituTQMNjacHiJbw4abV=HsH9s6PoAGKyuwdJg@mail.gmail.com","threadId":"51228","inReplyTo":"87sgro7lxo.fsf@evledraar.gmail.com","subject":"Re: [PATCH v2 1/3] repo-settings: create core.featureAdoptionRate setting","fromName":"Duy Nguyen","fromEmail":"pclouds@gmail.com","sentAt":"2019-07-02T11:09:41Z","receivedAt":"2019-07-02T11:10:10Z","isPatch":true,"sender":{"key":"pclouds@gmail.com","avatar":"https://avatars.githubusercontent.com/u/720?v=4"},"body":"On Tue, Jul 2, 2019 at 5:47 PM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>\n>\n> On Wed, Jun 19 2019, Derrick Stolee via GitGitGadget wrote:\n>\n> >  core.commitGraph::\n> >       If true, then git will read the commit-graph file (if it exists)\n> > -     to parse the graph structure of commits. Defaults to false. See\n> > +     to parse the graph structure of commits. Defaults to false, unless\n> > +     `core.featureAdoptionRate` is at least three. See\n> >       linkgit:git-commit-graph[1] for more information.\n> >\n> >  core.useReplaceRefs::\n> > @@ -601,3 +602,21 @@ core.abbrev::\n> >       in your repository, which hopefully is enough for\n> >       abbreviated object names to stay unique for some time.\n> >       The minimum length is 4.\n> > +\n> > +core.featureAdoptionRate::\n> > +     Set an integer value on a scale from 0 to 10 describing your\n> > +     desire to adopt new performance features. Defaults to 0. As\n> > +     the value increases, features are enabled by changing the\n> > +     default values of other config settings. If a config variable\n> > +     is specified explicitly, the explicit value will override these\n> > +     defaults:\n> > ++\n> > +If the value is at least 3, then the following defaults are modified.\n> > +These represent relatively new features that have existed for multiple\n> > +major releases, and present significant performance benefits. They do\n> > +not modify the user-facing output of porcelain commands.\n> > ++\n> > +* `core.commitGraph=true` enables reading commit-graph files.\n> > ++\n> > +* `gc.writeCommitGraph=true` eneables writing commit-graph files during\n>\n> I barked up a similar tree in\n> https://public-inbox.org/git/CACBZZX5SbYo5fVPtK6LW1FF96nR5591RHHC-5wdjW-fmg1R0EQ@mail.gmail.com/\n>\n> I wonder if you've seen that & what you think about that\n> approach. I.e. have a core.version=2.28 (or core.version=+6) or whatever\n> to opt-in to features we'd make default in 2.28. Would that be your\n> core.featureAdoptionRate=6 (28-28 = 6)?\n>\n> I admit that question is partly rhetorical, because I think it suggests\n> how hard it would be for users to reason about this.\n>\n> The \"core.version\" idea also sucks, but at least it's bound to our\n> advertised version number, so it's obvious if you set it to e.g. +2 what\n> feature track you're on, and furthermore when we'd commit to making that\n> the default for users who don't set core.version (although we could of\n> course always change our minds...). It's also something that mirrors how\n> e.g. Perl, C compilers (with --std=*) treat this sort of thing.\n>\n> So I'm all for a facility to have a setting to collectively opt-in to\n> new things early. But I think for such a thing we really should a) at\n> least in principle commit to making those things the default eventually\n\nSome features may be best enabled for certain setups. This is why I\nset configuration variables repo size, worktree size.. instead of just\none number.\n\n> (if they don't suck) b) it needs to be obvious to the user how the\n> \"rate\" relates to git releases.\n\nI see this more like gcc =O options. And for those options, the\ndevelopers decide what to include. If you know what you want already,\nyou can just turn specific keys on. Otherwise you count on devs to do\nthe right things.\n\nIt would help if we have something like \"gcc -Q -O2 --help=optimizers\"\nso you can see exactly what you need to turn on to achieve the same\nthing. Then you can just set those have the same \"per release\"\nsettings.\n\nWhich makes me think about a slightly different implementation detail\n(which I ignored because I didn't think further about per-release\nstuff): since these are basically meta config to change defaults, we\ncan just implement them as a (builtin, or bundled) config file. The\nuser can see what are included much easier we have several different\nconfig \"profiles\" (deep history, large worktree, bleeding-edge...) and\nthe user can include one or all [1].\n\n[1] it also opens up the opportunity to have a standard (but optional)\nset of aliases. But that's a touchy topic.\n-- \nDuy\n"},{"id":"378498","messageId":"8720cee9-bd50-6641-c029-234a9b00e1ea@gmail.com","threadId":"51228","inReplyTo":"CACsJy8Aqdb_-5ituTQMNjacHiJbw4abV=HsH9s6PoAGKyuwdJg@mail.gmail.com","subject":"Re: [PATCH v2 1/3] repo-settings: create core.featureAdoptionRate setting","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2019-07-02T14:54:45Z","receivedAt":"2019-07-02T14:54:49Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 7/2/2019 7:09 AM, Duy Nguyen wrote:\n> On Tue, Jul 2, 2019 at 5:47 PM Ævar Arnfjörð Bjarmason <avarab@gmail.com> wrote:\n>>\n>>\n>> On Wed, Jun 19 2019, Derrick Stolee via GitGitGadget wrote:\n>>\n>>>  core.commitGraph::\n>>>       If true, then git will read the commit-graph file (if it exists)\n>>> -     to parse the graph structure of commits. Defaults to false. See\n>>> +     to parse the graph structure of commits. Defaults to false, unless\n>>> +     `core.featureAdoptionRate` is at least three. See\n>>>       linkgit:git-commit-graph[1] for more information.\n>>>\n>>>  core.useReplaceRefs::\n>>> @@ -601,3 +602,21 @@ core.abbrev::\n>>>       in your repository, which hopefully is enough for\n>>>       abbreviated object names to stay unique for some time.\n>>>       The minimum length is 4.\n>>> +\n>>> +core.featureAdoptionRate::\n>>> +     Set an integer value on a scale from 0 to 10 describing your\n>>> +     desire to adopt new performance features. Defaults to 0. As\n>>> +     the value increases, features are enabled by changing the\n>>> +     default values of other config settings. If a config variable\n>>> +     is specified explicitly, the explicit value will override these\n>>> +     defaults:\n>>> ++\n>>> +If the value is at least 3, then the following defaults are modified.\n>>> +These represent relatively new features that have existed for multiple\n>>> +major releases, and present significant performance benefits. They do\n>>> +not modify the user-facing output of porcelain commands.\n>>> ++\n>>> +* `core.commitGraph=true` enables reading commit-graph files.\n>>> ++\n>>> +* `gc.writeCommitGraph=true` eneables writing commit-graph files during\n>>\n>> I barked up a similar tree in\n>> https://public-inbox.org/git/CACBZZX5SbYo5fVPtK6LW1FF96nR5591RHHC-5wdjW-fmg1R0EQ@mail.gmail.com/\n>>\n>> I wonder if you've seen that & what you think about that\n>> approach. I.e. have a core.version=2.28 (or core.version=+6) or whatever\n>> to opt-in to features we'd make default in 2.28. Would that be your\n>> core.featureAdoptionRate=6 (28-28 = 6)?\n\nI had not seen that message. Thanks for the link.\n\nHowever, I don't think that your idea of \"give me features that will be\ndefault soon\" is enough, as some of these features will _never_ be\nturned on by default. It's more about how much a user is willing to have\nsome slight changes (i.e. extra files during maintenance [commit-graph],\ndifferent index format, changed ahead/behind messages in status) in\nexchange for an overall \"better\" experience. Here \"better\" is decided by\nthe community members adjusting the values on this feature.\n>> I admit that question is partly rhetorical, because I think it suggests\n>> how hard it would be for users to reason about this.\n\nThe intention here is to make this as simple for users as possible. I want\nto be able to say \"I highly recommend you set core.featureAdoptionRate=5\"\nand for them to not need to know what is happening. Of course, they can\nlearn more about the details and opt-out as things change.\n\n>> The \"core.version\" idea also sucks, but at least it's bound to our\n>> advertised version number, so it's obvious if you set it to e.g. +2 what\n>> feature track you're on, and furthermore when we'd commit to making that\n>> the default for users who don't set core.version (although we could of\n>> course always change our minds...). It's also something that mirrors how\n>> e.g. Perl, C compilers (with --std=*) treat this sort of thing.\n>>\n>> So I'm all for a facility to have a setting to collectively opt-in to\n>> new things early. But I think for such a thing we really should a) at\n>> least in principle commit to making those things the default eventually\n> \n> Some features may be best enabled for certain setups. This is why I\n> set configuration variables repo size, worktree size.. instead of just\n> one number.\n\nYou are right that some features are best for different scale factors:\n\n * commit-graph is best with a deep history.\n * index version 4 is best with a large working directory.\n\nThese things are usually correlated, and users don't always know what\nexactly is \"large\" for any of these variables.\n\nFurther, these features actually don't have much downside even if\na user is legitimately struggling with one of these scale factors and\nnot another.\n\n>> (if they don't suck) b) it needs to be obvious to the user how the\n>> \"rate\" relates to git releases.\n> \n> I see this more like gcc =O options. And for those options, the\n> developers decide what to include. If you know what you want already,\n> you can just turn specific keys on. Otherwise you count on devs to do\n> the right things.\n> \n> It would help if we have something like \"gcc -Q -O2 --help=optimizers\"\n> so you can see exactly what you need to turn on to achieve the same\n> thing. Then you can just set those have the same \"per release\"\n> settings.\n> \n> Which makes me think about a slightly different implementation detail\n> (which I ignored because I didn't think further about per-release\n> stuff): since these are basically meta config to change defaults, we\n> can just implement them as a (builtin, or bundled) config file. The\n> user can see what are included much easier we have several different\n> config \"profiles\" (deep history, large worktree, bleeding-edge...) and\n> the user can include one or all [1].\n\nIn some sense, this is creating a list of growing config profiles that\neach include the previous one:\n\n  config-3 (core.commitGraph, gc.writeCommitGraph, index.version)\n  config-5 (config-3 + pack.useSparse)\n  config-7 (config-5 + status.aheadBehind, fetch.showForcedUpdates)\n\nThe issue you seem to have is that we are not creating multiple\ndimensions of options. I think simplicity is key here. Anyone with\nthe knowledge to deeply understand multiple dimensions can just\nassign the config options as they see fit. This is _not_ a feature\nfor power users.\n \n> [1] it also opens up the opportunity to have a standard (but optional)\n> set of aliases. But that's a touchy topic.\n\nStandard aliases is an interesting topic, but tangential to the\ntopic at hand and I'd prefer to leave it out of the current\ndiscussion.\n\n-Stolee\n"},{"id":"378501","messageId":"xmqqv9wk4bkn.fsf@gitster-ct.c.googlers.com","threadId":"51228","inReplyTo":"CACsJy8Aqdb_-5ituTQMNjacHiJbw4abV=HsH9s6PoAGKyuwdJg@mail.gmail.com","subject":"Re: [PATCH v2 1/3] repo-settings: create core.featureAdoptionRate setting","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-07-02T16:59:20Z","receivedAt":"2019-07-02T16:59:30Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n\n>> So I'm all for a facility to have a setting to collectively opt-in to\n>> new things early. But I think for such a thing we really should a) at\n>> least in principle commit to making those things the default eventually\n>\n> Some features may be best enabled for certain setups. This is why I\n> set configuration variables repo size, worktree size.. instead of just\n> one number.\n\nYeah, I think the concept of core.fetureAdoptionRate is faulty at\nmultiple counts, and I admit I am guilty of making at least one\naspect worse by giving the topic branch to queue these patches a\nmistaken name of \"early-adoption\".\n\nSome tweaks, like the use of index version 4, may be something we\nstrive to make it eventually suitable for _all_ users.  \n\nThe effort may involve multiple iterations of things like \"gee, the\nprefix-compression works very well for really big tree, but sucks\nfor a project of medium size; lets tweak to automatically\nenable/disable it based on the size of the tree\", but the main point\nis that we want to eventually make it good for projects of all sizes\nand different access patterns.  While we do the treaking, the user\nexperience may be rocky, and \"early adoption\" model is perfectly\nsuitable for a thing like this.\n\nBut some other tweaks, like the ahead-behind thing, are what we\nwould never make it the default for everybody.  They are \"Git is\nnever designed to be used like this, but if we disable small things\nlike this and that, the end user experience for those who used to\nhave them might suffer, but other aspect of the system becomes\nusable\" tradeoffs.  When we are done experimenting and know what\nkind of system castration may give acceptable trade off, we know the\nsubset of users to whom these tweaks give benefit (and others to whom\nthese are not improvements).  Opting into these things is not about\n\"early adoption\".\n\nAlso as raised in another message in this thread, I do agree that\nthe configuration does not belong to the \"core.\" hierarchy.  It is\nmore like a macro, that flips individual configuration based on a\nhigher level \"grouping\" (e.g. my project falls into \"large but\ninfrequently updated\" category) to suit the access pattern.\n\n> I see this more like gcc =O options. And for those options, the\n> developers decide what to include. If you know what you want already,\n> you can just turn specific keys on. Otherwise you count on devs to do\n> the right things.\n\nYup.  Sorry for backing a wrong model.  And I kind of like the word\n\"bundled\" you mention below, not as in \"bundled with Git\", but more\nas in \"these configuration settings are bundled together to serve\nusers of this kind of project\".\n\n> Which makes me think about a slightly different implementation detail\n> (which I ignored because I didn't think further about per-release\n> stuff): since these are basically meta config to change defaults, we\n> can just implement them as a (builtin, or bundled) config file. The\n> user can see what are included much easier we have several different\n> config \"profiles\" (deep history, large worktree, bleeding-edge...) and\n> the user can include one or all [1].\n>\n> [1] it also opens up the opportunity to have a standard (but optional)\n> set of aliases. But that's a touchy topic.\n"},{"id":"378611","messageId":"86ftnl1kod.fsf@gmail.com","threadId":"51228","inReplyTo":"CACsJy8Cwxov9VWq_MpeWstGtMB-rTy6LYyFj_PF9oSP0kqcDXQ@mail.gmail.com","subject":"Re: [PATCH v3 1/3] repo-settings: create core.featureAdoptionRate setting","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2019-07-04T22:47:46Z","receivedAt":"2019-07-04T22:50:54Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Duy Nguyen <pclouds@gmail.com> writes:\n> On Mon, Jul 1, 2019 at 10:32 PM Derrick Stolee via GitGitGadget\n> <gitgitgadget@gmail.com> wrote:\n>> @@ -601,3 +602,22 @@ core.abbrev::\n>>         in your repository, which hopefully is enough for\n>>         abbreviated object names to stay unique for some time.\n>>         The minimum length is 4.\n>> +\n>> +core.featureAdoptionRate::\n>> +       Set an integer value on a scale from 0 to 10 describing your\n>> +       desire to adopt new performance features. Defaults to 0. As\n>> +       the value increases, features are enabled by changing the\n>> +       default values of other config settings. If a config variable\n>> +       is specified explicitly, the explicit value will override these\n>> +       defaults:\n>\n> This is because I'd like to keep core.* from growing too big (it's\n> already big), hard to read, search and maintain. Perhaps this should\n> belong to a separate group? Something like tuning.something or\n> defaults.something.\n\nI'm not sure if I consider core.* too big.  Well, there are 55 or more\nentries in this namespace.\n\n>> +If the value is at least 3, then the following defaults are modified.\n>> +These represent relatively new features that have existed for multiple\n>> +major releases, and may present performance benefits. These benefits\n>> +depend on the amount and kind of data in your repo and how you use it.\n>\n> Then instead of numeric values, maybe the user should write some sort\n> description about the repo and we optimize for that, similar to gcc\n> -Os optimized for size, -Ofast for compiler speed (-O<n> is all about\n> execution speed).\n\nI also do not like those magic numbers.\n\n>\n> We could write, for example, tuning.commitHistory = {small, medium,\n> large} and tuning.worktree = {small, large, medium} and maybe\n> tuning.refSize and use that to optimize. We can still have different\n> optimization levels (probably just \"none\", \"recommended\" vs\n> \"aggressive\" where agressive enables most new stuff),\n\nI think we have three different things that are currently conflated in\none config variable and one value.\n\nFirst is what we want to optimize for; is it on-disk repository size,\ncommand performance / execution speed, or maybe convenient information.\n\nSecond is what type of repository we are dealing with.  Is there a\nproblem with long history, large number of files in checkout, large\nand/or binary files, or all together?  The original `core.size=large`\n(or proposed core.repositorySize) was all about this issue.  Another\nissue that might be important is that if it is leaf developer\nrepository, or is it maintainer repository, etc. (which affects for\nexample how the push looks like).\n\nThird is what tradeoffs we are willing to accept to get required\nperformance.  Are we willing to use additional stable optional features;\nare we willing to use new experimental optional features; are we\nwilling; are we willing to sacrifice convenience (ahead/behind\ninformation in status, information bout forced updates in push output,\netc.) for performance?  This what current proposal is about.\n\nIt may not nnned to be a separate confi variable for a separate aspect;\nit may be enough to have value that is space-separated list, or\nsomething like that.\n\nBest,\n--\nJakub Narębski\n"},{"id":"378699","messageId":"50955e76-8b61-8ffd-b8ee-3621ecbd912b@gmail.com","threadId":"51228","inReplyTo":"pull.254.v3.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 0/3] [RFC] Create 'core.featureAdoptionRate' setting to update config defaults","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2019-07-08T19:22:49Z","receivedAt":"2019-07-08T19:22:55Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 7/1/2019 10:29 AM, Derrick Stolee via GitGitGadget wrote:\n> Here is a second run at this RFC, which aims to create a \"meta\" config\n> setting that automatically turns on other settings according to a user's\n> willingness to trade new Git behavior or new feature risk for performance\n> benefits. The new name for the setting is \"core.featureAdoptionRate\" and is\n> an integer scale from 0 to 10. There will be multiple \"categories\" of\n> settings, and the intention is to allow more granular levels as necessary.\n\n(Adding people who contributed feedback to CC line.)\n\nIt seems that this \"Feature Adoption Rate\" idea was too simplistic, and\nhad several issues. Time to take a different stab at this direction, but\nwith these clear goals in mind:\n\n 1. We want intermediate users to be able to take advantage of new config\n    options without watching every release for new config options.\n\n 2. The config name should match the general effect of the implied\n    settings.\n\n 3. There are orthogonal settings that may not apply beneficially to\n    all repos.\n\nWith this in mind, I propose instead a set of \"feature.*\" config settings\nthat form groups of \"community recommended\" settings (with some caveats).\nIn the space below, I'll list a set of possible feature names and the\nimplied config options.\n\nFirst, the main two categories we've discussed so far: many commits and\nmany files. These two feature sets are for when your repo is large in\none of these dimensions. Perhaps there are other settings to include\nin these?\n\n\tfeature.manyFiles:\n\t\tindex.version = 4\n\t\tindex.threads = true\n\t\tcore.untrackedCache = true\n\n\tfeature.manyCommits:\n\t\tcore.commitGraph = true\n\t\tgc.writeCommitGraph = true\n\t\t(future: fetch.writeSplitCommitGraph = true)\n\nNote: the `fetch.writeSplitCommitGraph` does not exist yet, but could\nbe introduced in a later release to write a new commit-graph (with --split)\non fetch.\n\nThe other category that has been discussed already is that of \"experimental\nfeatures that we generally think are helpful but change behavior slightly in\nsome cases\".\n\n\tfeature.experimental:\n\t\tpack.useSparse = true\n\t\tstatus.aheadBehind = false\n\t\tfetch.showForcedUpdates = false\n\t\tmerge.directoryRenames = true\n\t\tprotocol.version = 2\n\t\tfetch.negotiationAlgorithm = skipping\n\nWe have not discussed anything like the next category, but Dscho thought\na set of configs to make pretty diffs could be a fun \"meta-config\" setting:\n\n\tfeature.prettyDiff:\n\t\tdiff.color = auto\n\t\tui.color = auto\n\t\tdiff.context = 5\n\t\tdiff.colorMoved = true\n\t\tdiff.colorMovedWs = allow-indentation-change\n\t\tdiff.algorithm = minimal\n\nThese are just a first round of suggestions. I'm sure we would enjoy a\ndebate around an optimal set of diff settings.\n\nFinally, here is a kind of feature that I could imagine being helpful\nin the future, but maybe is not a good idea to pursue right now. In\nsome cases users use \"gc.auto = 0\" to prevent all user-time blocking\nmaintenance. This can degrade performance over time as loose objects\nand pack-files accumulate. The performance could mostly be recovered\nby using a multi-pack-index, but there is not current way to automatically\nwrite the file. This would not solve the space issues that happen here.\n\n\tfeature.noGC:\n\t\tgc.auto = 0\n\t\tcore.multiPackIndex = true\n\t\t(future: fetch.writeMultiPackIndex = true)\n\nWhat do people think about this general idea? Are there any other\nfeature.* settings that could be useful? Any additional settings\nto add to these groups?\n\nThanks,\n-Stolee\n"},{"id":"378729","messageId":"20190709185552.GA84865@TaylorsMBP6986.attlocal.net","threadId":"51228","inReplyTo":"50955e76-8b61-8ffd-b8ee-3621ecbd912b@gmail.com","subject":"Re: [PATCH v3 0/3] [RFC] Create 'core.featureAdoptionRate' setting to update config defaults","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2019-07-09T18:55:52Z","receivedAt":"2019-07-09T18:55:56Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"Hi Derrick,\n\nI'm a little bit late to the part, but I think that this is a really\ninteresting feature with a lot of really interesting discussion so far.\n\nI hope you don't mind me throwing in my $.02 as well :-).\n\nOn Mon, Jul 08, 2019 at 03:22:49PM -0400, Derrick Stolee wrote:\n> On 7/1/2019 10:29 AM, Derrick Stolee via GitGitGadget wrote:\n> > Here is a second run at this RFC, which aims to create a \"meta\" config\n> > setting that automatically turns on other settings according to a user's\n> > willingness to trade new Git behavior or new feature risk for performance\n> > benefits. The new name for the setting is \"core.featureAdoptionRate\" and is\n> > an integer scale from 0 to 10. There will be multiple \"categories\" of\n> > settings, and the intention is to allow more granular levels as necessary.\n>\n> (Adding people who contributed feedback to CC line.)\n>\n> It seems that this \"Feature Adoption Rate\" idea was too simplistic, and\n> had several issues. Time to take a different stab at this direction, but\n> with these clear goals in mind:\n>\n>  1. We want intermediate users to be able to take advantage of new config\n>     options without watching every release for new config options.\n>\n>  2. The config name should match the general effect of the implied\n>     settings.\n>\n>  3. There are orthogonal settings that may not apply beneficially to\n>     all repos.\n\nI think that this is a clear representation of the initial reaction I\nhad to the 'core.featureAdoptionRate' idea. I had drafted a response to\nadvance these concerns before realizing that this subsequent RFC\nexisted, which does a nice job highlighting the concerns that I had.\n\n> With this in mind, I propose instead a set of \"feature.*\" config settings\n> that form groups of \"community recommended\" settings (with some caveats).\n> In the space below, I'll list a set of possible feature names and the\n> implied config options.\n\nI think that 'feature.*' configuration settings are a good idea. They\naddress each of the above (3) concerns, since they are:\n\n  1. Can be easily adopted by even novice-level users. Perhaps\n     novice-users will not be setting 'feature.manyFiles = 1', but they\n     can easily opt-in to organization-level features that have been\n     defined to handle organization-specific concerns.\n\n  2. This one is straightforward: I think that setting\n     'feature.manyFiles = 1' is clearer than 'feature.adoptionRate = 3'.\n\n  3. Right. Windows developers may have a different set of what features\n     are interesting to adopt than, say, every-day users, and likewise\n     for kernel developers, too.\n\n> First, the main two categories we've discussed so far: many commits and\n> many files. These two feature sets are for when your repo is large in\n> one of these dimensions. Perhaps there are other settings to include\n> in these?\n>\n> \tfeature.manyFiles:\n> \t\tindex.version = 4\n> \t\tindex.threads = true\n> \t\tcore.untrackedCache = true\n>\n> \tfeature.manyCommits:\n> \t\tcore.commitGraph = true\n> \t\tgc.writeCommitGraph = true\n> \t\t(future: fetch.writeSplitCommitGraph = true)\n\nI think that for this *feature* (pun mostly unintended) to really shine,\nwe ought to adopt Junio's suggestion in [1] that we allow users to:\n\n  * use pre-baked features that are defined within and shipped with\n    Git itself.\n\n  * define their own features and second-order features that can\n    reference both pre-baked and user-defined feature groups.\n\nI think that this will let, say, folks at Microsoft to define a set of\nfeatures that are interesting to Windows developers, that are separate\nfrom the features that core Git thinks will be interesting to every-day\nusers.\n\n>\n> <snip>\n>\n> Thanks,\n> -Stolee\n\nThanks,\nTaylor\n\n[1]: https://public-inbox.org/git/xmqqftonsr6a.fsf@gitster-ct.c.googlers.com/\n"},{"id":"378730","messageId":"xmqqo923ui7x.fsf@gitster-ct.c.googlers.com","threadId":"51228","inReplyTo":"50955e76-8b61-8ffd-b8ee-3621ecbd912b@gmail.com","subject":"Re: [PATCH v3 0/3] [RFC] Create 'core.featureAdoptionRate' setting to update config defaults","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-07-09T19:21:38Z","receivedAt":"2019-07-09T19:21:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <stolee@gmail.com> writes:\n\n> The other category that has been discussed already is that of \"experimental\n> features that we generally think are helpful but change behavior slightly in\n> some cases\".\n>\n> \tfeature.experimental:\n> \t\tpack.useSparse = true\n> \t\tstatus.aheadBehind = false\n> \t\tfetch.showForcedUpdates = false\n> \t\tmerge.directoryRenames = true\n> \t\tprotocol.version = 2\n> \t\tfetch.negotiationAlgorithm = skipping\n\nOther classes you listed I can easily support, but I have trouble\ndeciding if this concept itself is bad, or merely that some/many of\nthe sample knobs you listed above are not exactly appropriate.\nEither way, I have hard time swallowing this one as-is.  You may\nthink aheadBehind==false is helpful, but I don't, for example, and\nthere may be people for and against each of the experimental knobs.\n\nBut there may be a clear set of \"this is agreed to be the way to the\nfuture, but the implementation currently is too convoluted and\nsuspected of bugs, so we'll let early adoptors opt into the feature,\nand when that happens, eventually this knob will go away (i.e. you\nwon't be able to turn it off)\" type of knobs.  Or it may change the\nbehaviour drastically, but as long as it is agreed that the future\nlies in that direction, I think it is OK to throw such a knob into\nthis class.  The key points are (1) we are committed that in the\nfuture everybody will be forced to have it and (2) it is not merely\n\"we generally think\", but \"the decision about the future has been\nmade---there won't be any other way\".  The feature.experimental\nbecomes merely a way to let early adoptors in.  If you limit the\nindividual features governed by feature.experimental to that kind of\nknobs, I can be easily convinced that this class is a good idea.\n"},{"id":"378732","messageId":"afdaaa93-8769-c859-e957-e61d27b6d5a9@gmail.com","threadId":"51228","inReplyTo":"xmqqo923ui7x.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v3 0/3] [RFC] Create 'core.featureAdoptionRate' setting to update config defaults","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2019-07-09T19:45:40Z","receivedAt":"2019-07-09T19:45:44Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 7/9/2019 3:21 PM, Junio C Hamano wrote:\n> Derrick Stolee <stolee@gmail.com> writes:\n> \n>> The other category that has been discussed already is that of \"experimental\n>> features that we generally think are helpful but change behavior slightly in\n>> some cases\".\n>>\n>> \tfeature.experimental:\n>> \t\tpack.useSparse = true\n>> \t\tstatus.aheadBehind = false\n>> \t\tfetch.showForcedUpdates = false\n>> \t\tmerge.directoryRenames = true\n>> \t\tprotocol.version = 2\n>> \t\tfetch.negotiationAlgorithm = skipping\n> \n> Other classes you listed I can easily support, but I have trouble\n> deciding if this concept itself is bad, or merely that some/many of\n> the sample knobs you listed above are not exactly appropriate.\n> Either way, I have hard time swallowing this one as-is.  You may\n> think aheadBehind==false is helpful, but I don't, for example, and\n> there may be people for and against each of the experimental knobs.\n\nThanks for the specific note about aheadBehind. I'll drop that one\nfrom consideration.\n\nI suppose that fetch.showForcedUpdates is in the same category, and\nit has a self-discovery mechanism (a warning message) for users who\nfeel the pain of checking for forced updates (i.e. it takes >10s).\n\n> But there may be a clear set of \"this is agreed to be the way to the\n> future, but the implementation currently is too convoluted and\n> suspected of bugs, so we'll let early adoptors opt into the feature,\n> and when that happens, eventually this knob will go away (i.e. you\n> won't be able to turn it off)\" type of knobs.  Or it may change the\n> behaviour drastically, but as long as it is agreed that the future\n> lies in that direction, I think it is OK to throw such a knob into\n> this class.  The key points are (1) we are committed that in the\n> future everybody will be forced to have it and (2) it is not merely\n> \"we generally think\", but \"the decision about the future has been\n> made---there won't be any other way\".  The feature.experimental\n> becomes merely a way to let early adoptors in.  If you limit the\n> individual features governed by feature.experimental to that kind of\n> knobs, I can be easily convinced that this class is a good idea.\n\nFrom this list, do you think any of these settings are likely to\nbecome defaults? It seems that protocol.version = 2 may be a default\nnow that _most_ services have an implementation, and it always falls\nback to protocol v1 without extra cost.\n\nWhen pack.useSparse was first introduced, I considered making it true\nby default after a while. But you protested, saying you want people\nknocking at the door saying it is useful. What if it lived here?\n\nfetch.negotiationAlgorithm and merge.directoryRenames seem like\nvaluable features and maybe just need more time out in the world\nbefore they could be considered defaults.\n\nI appreciate all of the feedback, and to drive the discussion forward\nI'm trying to tease out very specific opinions.\n\nThanks,\n-Stolee\n"},{"id":"378740","messageId":"xmqqef2yvp7v.fsf@gitster-ct.c.googlers.com","threadId":"51228","inReplyTo":"afdaaa93-8769-c859-e957-e61d27b6d5a9@gmail.com","subject":"Re: [PATCH v3 0/3] [RFC] Create 'core.featureAdoptionRate' setting to update config defaults","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-07-09T22:05:08Z","receivedAt":"2019-07-09T22:05:18Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <stolee@gmail.com> writes:\n\n> From this list, do you think any of these settings are likely to\n> become defaults? It seems that protocol.version = 2 may be a default\n> now that _most_ services have an implementation, and it always falls\n> back to protocol v1 without extra cost.\n>\n> When pack.useSparse was first introduced, I considered making it true\n> by default after a while. But you protested, saying you want people\n> knocking at the door saying it is useful. What if it lived here?\n>\n> fetch.negotiationAlgorithm and merge.directoryRenames seem like\n> valuable features and maybe just need more time out in the world\n> before they could be considered defaults.\n\nI mostly agree with the categorization you gave above.\n\nI think it is perfectly fine for a knob, after proving its worth by\nexisting in the world without being a part of any feature.* set, to\nbecome part of feature.experimental, and then later be ejected\nwithout ever becoming the default in response to reactions by real\nworld users.  This would be easier to arrange if we had at least two\nexperiment levels.  One class would be \"we are firmly committed to\nmake these default in the future and ironing kinks out---please help\nby setting feature.experimental on\" and is more for early adopter\ntesting.  The other class may be \"we try this on users to see if\nthere are some populations of them with usage patterns we did not\nanticipate, and will yank it out if it turns out to be problematic\nto some users.\"  The more guinea pig users opt into the latter\n\"Highly Experimental\" category, the more help they can give us to\nprevent an ill-thought-out feature that does not universally help to\nbecome a new default.\n"},{"id":"378871","messageId":"867e8o9qzt.fsf@gmail.com","threadId":"51228","inReplyTo":"50955e76-8b61-8ffd-b8ee-3621ecbd912b@gmail.com","subject":"Re: [PATCH v3 0/3] [RFC] Create 'core.featureAdoptionRate' setting to update config defaults","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2019-07-11T21:54:30Z","receivedAt":"2019-07-11T21:54:40Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Derrick Stolee <stolee@gmail.com> writes:\n> On 7/1/2019 10:29 AM, Derrick Stolee via GitGitGadget wrote:\n>>\n>> Here is a second run at this RFC, which aims to create a \"meta\" config\n>> setting that automatically turns on other settings according to a user's\n>> willingness to trade new Git behavior or new feature risk for performance\n>> benefits. The new name for the setting is \"core.featureAdoptionRate\" and is\n>> an integer scale from 0 to 10. There will be multiple \"categories\" of\n>> settings, and the intention is to allow more granular levels as necessary.\n>\n> (Adding people who contributed feedback to CC line.)\n>\n> It seems that this \"Feature Adoption Rate\" idea was too simplistic, and\n> had several issues. Time to take a different stab at this direction, but\n> with these clear goals in mind:\n>\n>  1. We want intermediate users to be able to take advantage of new config\n>     options without watching every release for new config options.\n>\n>  2. The config name should match the general effect of the implied\n>     settings.\n>\n>  3. There are orthogonal settings that may not apply beneficially to\n>     all repos.\n>\n> With this in mind, I propose instead a set of \"feature.*\" config settings\n> that form groups of \"community recommended\" settings (with some caveats).\n> In the space below, I'll list a set of possible feature names and the\n> implied config options.\n\nA bit of bikeshed painting: I am unsure if \"feature.*\" is the best name\nfor this category of config (meta)settings.  Perhaps \"defaults.*\" or\n\"presets.*\" would be a better name -- they would certainly be more\nindicative of what setting this config variable actually *does*.\n\n> First, the main two categories we've discussed so far: many commits and\n> many files. These two feature sets are for when your repo is large in\n> one of these dimensions. Perhaps there are other settings to include\n> in these?\n>\n> \tfeature.manyFiles:\n> \t\tindex.version = 4\n> \t\tindex.threads = true\n> \t\tcore.untrackedCache = true\n>\n> \tfeature.manyCommits:\n> \t\tcore.commitGraph = true\n> \t\tgc.writeCommitGraph = true\n> \t\t(future: fetch.writeSplitCommitGraph = true)\n>\n> Note: the `fetch.writeSplitCommitGraph` does not exist yet, but could\n> be introduced in a later release to write a new commit-graph (with --split)\n> on fetch.\n\nThat looks really nice (for a built-in set of defaults).\n\nIt would be good if the above format, or something like it, could be\nused as a source of truth for this feature.\n\n> The other category that has been discussed already is that of \"experimental\n> features that we generally think are helpful but change behavior slightly in\n> some cases\".\n>\n> \tfeature.experimental:\n> \t\tpack.useSparse = true\n> \t\tstatus.aheadBehind = false\n> \t\tfetch.showForcedUpdates = false\n> \t\tmerge.directoryRenames = true\n> \t\tprotocol.version = 2\n> \t\tfetch.negotiationAlgorithm = skipping\n\nWell... turning off by default status.aheadBehind and\nfetch.showForcedUpdates makes sense only if also repository is large.\nOtherwise it is not useful, and even a bad thing.\n\nBoth of status.aheadBehind and fetch.showForcedUpdates are discoverable;\nas far as I remember Git would show a hint about those config options\nwhen the related task takes too long (either 'git status' in case of\nstatus.aheadBehind, or 'git fetch' in the latter case).\n\nI don't know if we have discoverability in the opposite direction: do we\nshow some advice (which as all advice can be turned off in the config)\nif either of status.aheadBehind or fetch.showForcedUpdates is false?\n\n> We have not discussed anything like the next category, but Dscho thought\n> a set of configs to make pretty diffs could be a fun \"meta-config\" setting:\n>\n> \tfeature.prettyDiff:\n> \t\tdiff.color = auto\n> \t\tui.color = auto\n> \t\tdiff.context = 5\n> \t\tdiff.colorMoved = true\n> \t\tdiff.colorMovedWs = allow-indentation-change\n> \t\tdiff.algorithm = minimal\n>\n> These are just a first round of suggestions. I'm sure we would enjoy a\n> debate around an optimal set of diff settings.\n\nMaybe Git for Windows defaults would be shipped in similar form; though\nI wonder if in this case it is better from simple system-wide settings.\n\n> Finally, here is a kind of feature that I could imagine being helpful\n> in the future, but maybe is not a good idea to pursue right now. In\n> some cases users use \"gc.auto = 0\" to prevent all user-time blocking\n> maintenance.\n\nDon't we have a hook for that?\n\n>              This can degrade performance over time as loose objects\n> and pack-files accumulate. The performance could mostly be recovered\n> by using a multi-pack-index, but there is not current way to automatically\n> write the file. This would not solve the space issues that happen here.\n>\n> \tfeature.noGC:\n> \t\tgc.auto = 0\n> \t\tcore.multiPackIndex = true\n> \t\t(future: fetch.writeMultiPackIndex = true)\n>\n> What do people think about this general idea? Are there any other\n> feature.* settings that could be useful? Any additional settings\n> to add to these groups?\n\nMaybe feature.slowFilesystem / defaults.slowFilesystem?  Or maybe\nfeature.server?\n\nBest regards,\n-- \nJakub Narębski\n"},{"id":"379144","messageId":"be4f9cc9-ee20-8e4f-c45a-26e93b51c4f3@gmail.com","threadId":"51228","inReplyTo":"xmqqef2yvp7v.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v3 0/3] [RFC] Create 'core.featureAdoptionRate' setting to update config defaults","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2019-07-22T12:10:58Z","receivedAt":"2019-07-22T12:11:03Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 7/9/2019 6:05 PM, Junio C Hamano wrote:\n> Derrick Stolee <stolee@gmail.com> writes:\n> \n>> From this list, do you think any of these settings are likely to\n>> become defaults? It seems that protocol.version = 2 may be a default\n>> now that _most_ services have an implementation, and it always falls\n>> back to protocol v1 without extra cost.\n>>\n>> When pack.useSparse was first introduced, I considered making it true\n>> by default after a while. But you protested, saying you want people\n>> knocking at the door saying it is useful. What if it lived here?\n>>\n>> fetch.negotiationAlgorithm and merge.directoryRenames seem like\n>> valuable features and maybe just need more time out in the world\n>> before they could be considered defaults.\n> \n> I mostly agree with the categorization you gave above.\n> \n> I think it is perfectly fine for a knob, after proving its worth by\n> existing in the world without being a part of any feature.* set, to\n> become part of feature.experimental, and then later be ejected\n> without ever becoming the default in response to reactions by real\n> world users.  This would be easier to arrange if we had at least two\n> experiment levels.  One class would be \"we are firmly committed to\n> make these default in the future and ironing kinks out---please help\n> by setting feature.experimental on\" and is more for early adopter\n> testing.  The other class may be \"we try this on users to see if\n> there are some populations of them with usage patterns we did not\n> anticipate, and will yank it out if it turns out to be problematic\n> to some users.\"  The more guinea pig users opt into the latter\n> \"Highly Experimental\" category, the more help they can give us to\n> prevent an ill-thought-out feature that does not universally help to\n> become a new default.\n\nHow about \"feature.preview\" for defaults we expect to change in a later\nversion, while \"feature.experimental\" is for defaults we are not sure\nabout?\n\n-Stolee\n\n"}]}