From c3c1bb25ebd53e5a786c7a251900371bf8c576f8 Mon Sep 17 00:00:00 2001 From: trent Date: Thu, 11 Jun 2026 21:36:31 +0000 Subject: [PATCH 1/2] fix: add stall detection and faster retries to git fetch Add http.lowSpeedLimit (1000 bytes/sec) and http.lowSpeedTime (15s) git configs to the fetch command. When the transfer speed drops below 1KB/s for 15 consecutive seconds, git aborts the fetch attempt. This enables the retry mechanism to actually work when connections stall, rather than having a single hung fetch consume the entire step-level timeout. Also switch fetch retries from the default 10-20s inter-attempt sleep to 1-5s. With a typical 3-minute step timeout, every second counts; the old delays could waste 40+ seconds on sleeps alone. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- __test__/git-command-manager.test.ts | 28 ++++++++++++++++++++++++++++ src/git-command-manager.ts | 13 +++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/__test__/git-command-manager.test.ts b/__test__/git-command-manager.test.ts index cea73d4..30a44d2 100644 --- a/__test__/git-command-manager.test.ts +++ b/__test__/git-command-manager.test.ts @@ -133,6 +133,10 @@ describe('Test fetchDepth and fetchTags options', () => { [ '-c', 'protocol.version=2', + '-c', + 'http.lowSpeedLimit=1000', + '-c', + 'http.lowSpeedTime=15', 'fetch', '--prune', '--no-recurse-submodules', @@ -170,6 +174,10 @@ describe('Test fetchDepth and fetchTags options', () => { [ '-c', 'protocol.version=2', + '-c', + 'http.lowSpeedLimit=1000', + '-c', + 'http.lowSpeedTime=15', 'fetch', '--no-tags', '--prune', @@ -208,6 +216,10 @@ describe('Test fetchDepth and fetchTags options', () => { [ '-c', 'protocol.version=2', + '-c', + 'http.lowSpeedLimit=1000', + '-c', + 'http.lowSpeedTime=15', 'fetch', '--no-tags', '--prune', @@ -247,6 +259,10 @@ describe('Test fetchDepth and fetchTags options', () => { [ '-c', 'protocol.version=2', + '-c', + 'http.lowSpeedLimit=1000', + '-c', + 'http.lowSpeedTime=15', 'fetch', '--prune', '--no-recurse-submodules', @@ -284,6 +300,10 @@ describe('Test fetchDepth and fetchTags options', () => { [ '-c', 'protocol.version=2', + '-c', + 'http.lowSpeedLimit=1000', + '-c', + 'http.lowSpeedTime=15', 'fetch', '--no-tags', '--prune', @@ -323,6 +343,10 @@ describe('Test fetchDepth and fetchTags options', () => { [ '-c', 'protocol.version=2', + '-c', + 'http.lowSpeedLimit=1000', + '-c', + 'http.lowSpeedTime=15', 'fetch', '--no-tags', '--prune', @@ -363,6 +387,10 @@ describe('Test fetchDepth and fetchTags options', () => { [ '-c', 'protocol.version=2', + '-c', + 'http.lowSpeedLimit=1000', + '-c', + 'http.lowSpeedTime=15', 'fetch', '--prune', '--no-recurse-submodules', diff --git a/src/git-command-manager.ts b/src/git-command-manager.ts index a45e15a..58a8a3d 100644 --- a/src/git-command-manager.ts +++ b/src/git-command-manager.ts @@ -284,7 +284,15 @@ class GitCommandManager { showProgress?: boolean } ): Promise { - const args = ['-c', 'protocol.version=2', 'fetch'] + const args = [ + '-c', + 'protocol.version=2', + '-c', + 'http.lowSpeedLimit=1000', + '-c', + 'http.lowSpeedTime=15', + 'fetch' + ] if (!refSpec.some(x => x === refHelper.tagsRefSpec) && !options.fetchTags) { args.push('--no-tags') } @@ -314,7 +322,8 @@ class GitCommandManager { } const that = this - await retryHelper.execute(async () => { + const fetchRetryHelper = new retryHelper.RetryHelper(3, 1, 5) + await fetchRetryHelper.execute(async () => { await that.execGit(args) }) } From 65903c092087daeeb38489864a1aedce73691f7e Mon Sep 17 00:00:00 2001 From: trent Date: Thu, 11 Jun 2026 21:38:09 +0000 Subject: [PATCH 2/2] chore: rebuild dist Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- dist/index.js | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/dist/index.js b/dist/index.js index 46de4b6..74f505d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1559,7 +1559,15 @@ class GitCommandManager { } fetch(refSpec, options) { return __awaiter(this, void 0, void 0, function* () { - const args = ['-c', 'protocol.version=2', 'fetch']; + const args = [ + '-c', + 'protocol.version=2', + '-c', + 'http.lowSpeedLimit=1000', + '-c', + 'http.lowSpeedTime=15', + 'fetch' + ]; if (!refSpec.some(x => x === refHelper.tagsRefSpec) && !options.fetchTags) { args.push('--no-tags'); } @@ -1581,7 +1589,8 @@ class GitCommandManager { args.push(arg); } const that = this; - yield retryHelper.execute(() => __awaiter(this, void 0, void 0, function* () { + const fetchRetryHelper = new retryHelper.RetryHelper(3, 1, 5); + yield fetchRetryHelper.execute(() => __awaiter(this, void 0, void 0, function* () { yield that.execGit(args); })); });