public inbox for git@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH] clone, progress: add --no-turtle-speed option to abort slow clones
@ 2026-03-06 22:14 Mike Banon
  2026-03-06 23:29 ` brian m. carlson
  0 siblings, 1 reply; 5+ messages in thread
From: Mike Banon @ 2026-03-06 22:14 UTC (permalink / raw)
  To: git

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>

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);

^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2026-03-09  9:36 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-06 22:14 [PATCH] clone, progress: add --no-turtle-speed option to abort slow clones Mike Banon
2026-03-06 23:29 ` brian m. carlson
2026-03-07  0:29   ` Mike Banon
2026-03-07  1:34     ` brian m. carlson
2026-03-09  9:31   ` Richard Kerry

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox