@@ -4,6 +4,23 @@ feature.*::
developer community as recommended defaults and are subject to change.
In particular, new config options may be added with different defaults.
+feature.experimental::
+ Enable config options that are new to Git, and are being considered for
+ future defaults. Config settings included here may be added or removed
+ with each release, including minor version updates. These settings may
+ have unintended interactions since they are so new. Please enable this
+ setting if you are interested in providing feedback on experimental
+ features. The new default values are:
++
+* `pack.useSparse=true` uses a new algorithm when constructing a pack-file
+which can improve `git push` performance in repos with many files.
++
+* `merge.directoryRenames=true` uses a new algorithm for detecting renames by
+using entire directories at a time instead of single files at a time.
++
+* `fetch.negotiationAlgorithm=skipping` may improve fetch negotiation times by
+skipping more commits at a time, reducing the number of round trips.
+
feature.manyCommits::
Enable config options that optimize for repos with many commits. This
setting is recommended for repos with at least 100,000 commits. The
@@ -59,7 +59,8 @@ fetch.negotiationAlgorithm::
effort to converge faster, but may result in a larger-than-necessary
packfile; The default is "default" which instructs Git to use the default algorithm
that never skips commits (unless the server has acknowledged it or one
- of its descendants).
+ of its descendants). If `feature.experimental` is enabled, then this
+ setting defaults to "skipping".
Unknown values will cause 'git fetch' to error out.
+
See also the `--negotiation-tip` option for linkgit:git-fetch[1].
@@ -54,7 +54,8 @@ merge.directoryRenames::
moved into the new directory. If set to "conflict", a conflict
will be reported for such paths. If merge.renames is false,
merge.directoryRenames is ignored and treated as false. Defaults
- to "conflict".
+ to "conflict" unless `feature.experimental` is enabled and the
+ default is "true".
merge.renormalize::
Tell Git that canonical representation of files in the
@@ -112,7 +112,8 @@ pack.useSparse::
objects. This can have significant performance benefits when
computing a pack to send a small change. However, it is possible
that extra objects are added to the pack-file if the included
- commits contain certain types of direct renames.
+ commits contain certain types of direct renames. Default is `false`
+ unless `feature.experimental` is enabled.
pack.writeBitmaps (deprecated)::
This is a deprecated synonym for `repack.writeBitmaps`.
@@ -34,6 +34,7 @@
#include "string-list.h"
#include "packfile.h"
#include "repository.h"
+#include "repo-settings.h"
/**
* Returns the length of the first line of msg.
@@ -2,19 +2,21 @@
#include "fetch-negotiator.h"
#include "negotiator/default.h"
#include "negotiator/skipping.h"
+#include "repository.h"
+#include "repo-settings.h"
-void fetch_negotiator_init(struct fetch_negotiator *negotiator,
- const char *algorithm)
+void fetch_negotiator_init(struct repository *r,
+ struct fetch_negotiator *negotiator)
{
- if (algorithm) {
- if (!strcmp(algorithm, "skipping")) {
- skipping_negotiator_init(negotiator);
- return;
- } else if (!strcmp(algorithm, "default")) {
- /* Fall through to default initialization */
- } else {
- die("unknown fetch negotiation algorithm '%s'", algorithm);
- }
+ prepare_repo_settings(r);
+ switch(r->settings.fetch_negotiation_algorithm) {
+ case FETCH_NEGOTIATION_SKIPPING:
+ skipping_negotiator_init(negotiator);
+ return;
+
+ case FETCH_NEGOTIATION_DEFAULT:
+ default:
+ default_negotiator_init(negotiator);
+ return;
}
- default_negotiator_init(negotiator);
}
@@ -2,6 +2,7 @@
#define FETCH_NEGOTIATOR_H
struct commit;
+struct repository;
/*
* An object that supplies the information needed to negotiate the contents of
@@ -52,7 +53,7 @@ struct fetch_negotiator {
void *data;
};
-void fetch_negotiator_init(struct fetch_negotiator *negotiator,
- const char *algorithm);
+void fetch_negotiator_init(struct repository *r,
+ struct fetch_negotiator *negotiator);
#endif
@@ -36,7 +36,6 @@ static int agent_supported;
static int server_supports_filtering;
static struct lock_file shallow_lock;
static const char *alternate_shallow_file;
-static char *negotiation_algorithm;
static struct strbuf fsck_msg_types = STRBUF_INIT;
/* Remember to update object flag allocation in object.h */
@@ -892,12 +891,13 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
struct shallow_info *si,
char **pack_lockfile)
{
+ struct repository *r = the_repository;
struct ref *ref = copy_ref_list(orig_ref);
struct object_id oid;
const char *agent_feature;
int agent_len;
struct fetch_negotiator negotiator;
- fetch_negotiator_init(&negotiator, negotiation_algorithm);
+ fetch_negotiator_init(r, &negotiator);
sort_ref_list(&ref, ref_compare_name);
QSORT(sought, nr_sought, cmp_ref_by_name);
@@ -911,7 +911,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
if (server_supports("shallow"))
print_verbose(args, _("Server supports %s"), "shallow");
- else if (args->depth > 0 || is_repository_shallow(the_repository))
+ else if (args->depth > 0 || is_repository_shallow(r))
die(_("Server does not support shallow clients"));
if (args->depth > 0 || args->deepen_since || args->deepen_not)
args->deepen = 1;
@@ -1379,6 +1379,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
struct shallow_info *si,
char **pack_lockfile)
{
+ struct repository *r = the_repository;
struct ref *ref = copy_ref_list(orig_ref);
enum fetch_state state = FETCH_CHECK_LOCAL;
struct oidset common = OIDSET_INIT;
@@ -1386,7 +1387,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
int in_vain = 0;
int haves_to_send = INITIAL_FLUSH;
struct fetch_negotiator negotiator;
- fetch_negotiator_init(&negotiator, negotiation_algorithm);
+ fetch_negotiator_init(r, &negotiator);
packet_reader_init(&reader, fd[0], NULL, 0,
PACKET_READ_CHOMP_NEWLINE |
PACKET_READ_DIE_ON_ERR_PACKET);
@@ -1505,8 +1506,6 @@ static void fetch_pack_config(void)
git_config_get_bool("repack.usedeltabaseoffset", &prefer_ofs_delta);
git_config_get_bool("fetch.fsckobjects", &fetch_fsck_objects);
git_config_get_bool("transfer.fsckobjects", &transfer_fsck_objects);
- git_config_get_string("fetch.negotiationalgorithm",
- &negotiation_algorithm);
git_config(fetch_pack_config_cb, NULL);
}
@@ -28,6 +28,7 @@
#include "submodule.h"
#include "revision.h"
#include "commit-reach.h"
+#include "repo-settings.h"
struct path_hashmap_entry {
struct hashmap_entry e;
@@ -3662,15 +3663,6 @@ static void merge_recursive_config(struct merge_options *opt)
opt->merge_detect_rename = git_config_rename("merge.renames", value);
free(value);
}
- if (!git_config_get_string("merge.directoryrenames", &value)) {
- int boolval = git_parse_maybe_bool(value);
- if (0 <= boolval) {
- opt->detect_directory_renames = boolval ? 2 : 0;
- } else if (!strcasecmp(value, "conflict")) {
- opt->detect_directory_renames = 1;
- } /* avoid erroring on values from future versions of git */
- free(value);
- }
git_config(git_xmerge_config, NULL);
}
@@ -3688,6 +3680,11 @@ void init_merge_options(struct merge_options *opt,
opt->diff_detect_rename = -1;
opt->merge_detect_rename = -1;
opt->detect_directory_renames = 1;
+
+ prepare_repo_settings(repo);
+ if (repo->settings.merge_directory_renames >= 0)
+ opt->detect_directory_renames = repo->settings.merge_directory_renames;
+
merge_recursive_config(opt);
merge_verbosity = getenv("GIT_MERGE_VERBOSITY");
if (merge_verbosity)
@@ -35,6 +35,18 @@ void prepare_repo_settings(struct repository *r)
free(strval);
}
+ if (!repo_config_get_maybe_bool(r, "merge.directoryrenames", &value))
+ r->settings.merge_directory_renames = value ? MERGE_DIRECTORY_RENAMES_TRUE : 0;
+ else if (!repo_config_get_string(r, "merge.directoryrenames", &strval)) {
+ if (!strcasecmp(strval, "conflict"))
+ r->settings.merge_directory_renames = MERGE_DIRECTORY_RENAMES_CONFLICT;
+ }
+ if (!repo_config_get_string(r, "fetch.negotiationalgorithm", &strval)) {
+ if (!strcasecmp(strval, "skipping"))
+ r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING;
+ else
+ r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_DEFAULT;
+ }
if (!repo_config_get_bool(r, "pack.usesparse", &value))
r->settings.pack_use_sparse = value;
@@ -47,6 +59,11 @@ void prepare_repo_settings(struct repository *r)
UPDATE_DEFAULT(r->settings.index_version, 4);
UPDATE_DEFAULT(r->settings.core_untracked_cache, UNTRACKED_CACHE_WRITE);
}
+ if (!repo_config_get_bool(r, "feature.experimental", &value) && value) {
+ UPDATE_DEFAULT(r->settings.pack_use_sparse, 1);
+ UPDATE_DEFAULT(r->settings.merge_directory_renames, MERGE_DIRECTORY_RENAMES_TRUE);
+ UPDATE_DEFAULT(r->settings.fetch_negotiation_algorithm, FETCH_NEGOTIATION_SKIPPING);
+ }
/* Hack for test programs like test-dump-untracked-cache */
if (ignore_untracked_cache_config)
@@ -54,5 +71,8 @@ void prepare_repo_settings(struct repository *r)
else
UPDATE_DEFAULT(r->settings.core_untracked_cache, UNTRACKED_CACHE_KEEP);
+ UPDATE_DEFAULT(r->settings.merge_directory_renames, MERGE_DIRECTORY_RENAMES_CONFLICT);
+ UPDATE_DEFAULT(r->settings.fetch_negotiation_algorithm, FETCH_NEGOTIATION_DEFAULT);
+
r->settings_initialized = 1;
}
@@ -8,6 +8,20 @@ enum untracked_cache_setting {
UNTRACKED_CACHE_WRITE = 2
};
+enum merge_directory_renames_setting {
+ MERGE_DIRECTORY_RENAMES_UNSET = -1,
+ MERGE_DIRECTORY_RENAMES_NONE = 0,
+ MERGE_DIRECTORY_RENAMES_CONFLICT = 1,
+ MERGE_DIRECTORY_RENAMES_TRUE = 2,
+};
+
+enum fetch_negotiation_setting {
+ FETCH_NEGOTIATION_UNSET = -1,
+ FETCH_NEGOTIATION_NONE = 0,
+ FETCH_NEGOTIATION_DEFAULT = 1,
+ FETCH_NEGOTIATION_SKIPPING = 2,
+};
+
struct repo_settings {
int core_commit_graph;
int gc_write_commit_graph;
@@ -16,6 +30,8 @@ struct repo_settings {
enum untracked_cache_setting core_untracked_cache;
int pack_use_sparse;
+ enum merge_directory_renames_setting merge_directory_renames;
+ enum fetch_negotiation_setting fetch_negotiation_algorithm;
};
struct repository;
@@ -60,29 +60,6 @@ test_expect_success 'commits with no parents are sent regardless of skip distanc
have_not_sent c6 c4 c3
'
-test_expect_success 'unknown fetch.negotiationAlgorithm values error out' '
- rm -rf server client trace &&
- git init server &&
- test_commit -C server to_fetch &&
-
- git init client &&
- test_commit -C client on_client &&
- git -C client checkout on_client &&
-
- test_config -C client fetch.negotiationAlgorithm invalid &&
- test_must_fail git -C client fetch "$(pwd)/server" 2>err &&
- test_i18ngrep "unknown fetch negotiation algorithm" err &&
-
- # Explicit "default" value
- test_config -C client fetch.negotiationAlgorithm default &&
- git -C client -c fetch.negotiationAlgorithm=default fetch "$(pwd)/server" &&
-
- # Implementation detail: If there is nothing to fetch, we will not error out
- test_config -C client fetch.negotiationAlgorithm invalid &&
- git -C client fetch "$(pwd)/server" 2>err &&
- test_i18ngrep ! "unknown fetch negotiation algorithm" err
-'
-
test_expect_success 'when two skips collide, favor the larger one' '
rm -rf server client trace &&
git init server &&