From 361b833fcd9e0e1f7b58dd94d7ddcbc1901aba94 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Sat, 30 May 2026 12:20:33 -0700 Subject: [PATCH] fix: tolerate literal percent in bitbucket remotes (#3685) --- src/main/bitbucket/repository-ref.test.ts | 17 +++++++++++++++++ src/main/bitbucket/repository-ref.ts | 12 ++++++++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/main/bitbucket/repository-ref.test.ts b/src/main/bitbucket/repository-ref.test.ts index 6ea77cf41..a95bf8e85 100644 --- a/src/main/bitbucket/repository-ref.test.ts +++ b/src/main/bitbucket/repository-ref.test.ts @@ -36,6 +36,23 @@ describe('Bitbucket repository refs', () => { expect(parseBitbucketRepoRef('https://github.com/team/project.git')).toBeNull() }) + it('keeps malformed percent sequences as literal repo path text', async () => { + expect(parseBitbucketRepoRef('git@bitbucket.org:team/project%zz.git')).toEqual({ + workspace: 'team', + repoSlug: 'project%zz' + }) + + gitExecFileAsyncMock.mockResolvedValue({ + stdout: 'git@bitbucket.org:team/project%zz.git\n', + stderr: '' + }) + + await expect(getBitbucketRepoRef('/repo')).resolves.toEqual({ + workspace: 'team', + repoSlug: 'project%zz' + }) + }) + it('resolves origin through the WSL-aware git runner and caches the result', async () => { gitExecFileAsyncMock.mockResolvedValue({ stdout: 'git@bitbucket.org:team/project.git\n', diff --git a/src/main/bitbucket/repository-ref.ts b/src/main/bitbucket/repository-ref.ts index b51b7c137..4d17e0b63 100644 --- a/src/main/bitbucket/repository-ref.ts +++ b/src/main/bitbucket/repository-ref.ts @@ -12,6 +12,14 @@ export function _resetBitbucketRepoRefCache(): void { repoRefCache.clear() } +function decodeSegment(value: string): string { + try { + return decodeURIComponent(value) + } catch { + return value + } +} + function parseBitbucketPath(pathname: string): BitbucketRepoRef | null { const withoutSuffix = pathname.replace(/\.git$/i, '') const parts = withoutSuffix @@ -27,8 +35,8 @@ function parseBitbucketPath(pathname: string): BitbucketRepoRef | null { return null } return { - workspace: decodeURIComponent(workspace), - repoSlug: decodeURIComponent(repoSlug) + workspace: decodeSegment(workspace), + repoSlug: decodeSegment(repoSlug) } }