When cloning large repositories from a website with multiple Git servers, the client may be directed to a "turtle-slow" server, causing the transfer to proceed at an unusably slow speed. This can lead to stalled downloads, particularly problematic for automated scripts that clone many repositories. This option makes Git abort the clone if the download rate falls below 128 KiB/s while receiving objects, typically catching slow servers early in the transfer, enabling scripts to retry the clone until they obtain a fast connection.
Signed-off-by: Mike Banon <mikebdp2@gmail.com>
Show changes to 4 files +33 −3
builtin/clone.c, environment.h, git.c, progress.c
diff --git a/builtin/clone.c b/builtin/clone.c index fba3c9c508..a9ed2c193d 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -94,6 +94,13 @@ static int recurse_submodules_cb(const struct option *opt, return 0; } +static int set_turtle_speed_env(const struct option *opt UNUSED, + const char *arg UNUSED, int unset UNUSED) +{ + setenv(NO_TURTLE_SPEED_ENVIRONMENT, "1", 1); + return 0; +} + static const char *get_repo_path_1(struct strbuf *path, int *is_bundle) { static const char *suffix[] = { "/.git", "", ".git/.git", ".git" }; @@ -946,6 +953,16 @@ int cmd_clone(int argc, .callback = recurse_submodules_cb, .defval = (intptr_t)".", }, + { + .type = OPTION_CALLBACK, + .long_name = "no-turtle-speed", + .value = NULL, + .argh = NULL, + .help = N_("abort clone if speed drops below 128 KiB/s"), + .flags = PARSE_OPT_NOARG, + .callback = set_turtle_speed_env, + .defval = 0, + }, OPT_ALIAS(0, "recursive", "recurse-submodules"), OPT_INTEGER('j', "jobs", &max_jobs, N_("number of submodules cloned in parallel")), diff --git a/environment.h b/environment.h index 123a71cdc8..cb316cf321 100644 --- a/environment.h +++ b/environment.h @@ -24,6 +24,7 @@ #define NO_REPLACE_OBJECTS_ENVIRONMENT "GIT_NO_REPLACE_OBJECTS" #define GIT_REPLACE_REF_BASE_ENVIRONMENT "GIT_REPLACE_REF_BASE" #define NO_LAZY_FETCH_ENVIRONMENT "GIT_NO_LAZY_FETCH" +#define NO_TURTLE_SPEED_ENVIRONMENT "GIT_NO_TURTLE_SPEED" #define GITATTRIBUTES_FILE ".gitattributes" #define INFOATTRIBUTES_FILE "info/attributes" #define ATTRIBUTE_MACRO_PREFIX "[attr]" diff --git a/git.c b/git.c index 744cb6527e..d0c4909fef 100644 --- a/git.c +++ b/git.c @@ -40,9 +40,9 @@ const char git_usage_string[] = N_("git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n" " [--exec-path[=<path>]] [--html-path] [--man-path] [--info-path]\n" " [-p | --paginate | -P | --no-pager] [--no-replace-objects] [--no-lazy-fetch]\n" - " [--no-optional-locks] [--no-advice] [--bare] [--git-dir=<path>]\n" - " [--work-tree=<path>] [--namespace=<name>] [--config-env=<name>=<envvar>]\n" - " <command> [<args>]"); + " [--no-turtle-speed] [--no-optional-locks] [--no-advice] [--bare]\n" + " [--git-dir=<path>] [--work-tree=<path>] [--namespace=<name>]\n" + " [--config-env=<name>=<envvar>] <command> [<args>]"); const char git_more_info_string[] = N_("'git help -a' and 'git help -g' list available subcommands and some\n" @@ -206,6 +206,10 @@ static int handle_options(const char ***argv, int *argc, int *envchanged) setenv(NO_LAZY_FETCH_ENVIRONMENT, "1", 1); if (envchanged) *envchanged = 1; + } else if (!strcmp(cmd, "--no-turtle-speed")) { + setenv(NO_TURTLE_SPEED_ENVIRONMENT, "1", 1); + if (envchanged) + *envchanged = 1; } else if (!strcmp(cmd, "--no-replace-objects")) { disable_replace_refs(); setenv(NO_REPLACE_OBJECTS_ENVIRONMENT, "1", 1); diff --git a/progress.c b/progress.c index 8315bdc3d4..b30c9f11d3 100644 --- a/progress.c +++ b/progress.c @@ -195,6 +195,7 @@ void display_throughput(struct progress *progress, uint64_t total) struct throughput *tp; uint64_t now_ns; unsigned int misecs, count, rate; + const char *turtle_env = getenv(NO_TURTLE_SPEED_ENVIRONMENT); if (!progress) return; @@ -245,6 +246,13 @@ void display_throughput(struct progress *progress, uint64_t total) tp->last_misecs[tp->idx] = misecs; tp->idx = (tp->idx + 1) % TP_IDX_MAX; + if (turtle_env && + *turtle_env == '1' && + rate < 128 && + progress->title && + strstr(progress->title, "Receiving objects")) + die(_("stopping due to a <128 KiB/s turtle speed: %u KiB/s"), rate); + throughput_string(&tp->display, total, rate); if (progress->last_value != -1 && progress_update) display(progress, progress->last_value, NULL);