Skip to content
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Upgraded `engine.io` to `^6.6.11`, `source-map-js` to `^1.2.2`, `smol-toml` to `^1.9.0`, `fast-copy` to `^3.1.0`, and `dompurify` to `^3.4.16`. [#1714](https://github.com/sourcebot-dev/sourcebot/pull/1714)
- Upgraded `golang.org/x/crypto` to `v0.56.0` and OpenTelemetry-Go exporters and SDK to `v1.45.0` in Zoekt. [#1716](https://github.com/sourcebot-dev/sourcebot/pull/1716)
- Upgraded `@modelcontextprotocol/sdk` to `^1.32.1`. [#1719](https://github.com/sourcebot-dev/sourcebot/pull/1719)
- Decoded percent-encoded characters in repo names derived from direct generic git URLs. [#1666](https://github.com/sourcebot-dev/sourcebot/pull/1666)
- Fixed `.gitattributes` `linguist-language` overrides being ignored for files in subdirectories. [#1692](https://github.com/sourcebot-dev/sourcebot/pull/1692)

## [5.1.15] - 2026-09-29
Expand Down
20 changes: 20 additions & 0 deletions packages/backend/src/repoCompileUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -334,4 +334,24 @@ describe('compileGenericGitHostConfig_url', () => {
const metadata = result[0].metadata as { gitConfig?: Record<string, string> };
expect(metadata.gitConfig!['zoekt.name']).toBe('github.com/test/repo');
});

test('should decode percent-encoded characters in the repo name', async () => {
mockedIsUrlAValidGitRepo.mockResolvedValue(true);

const config = {
type: 'git' as const,
url: 'https://github.com/test/Project%20Name%20With%20Spaces.git',
};

const result = await compileGenericGitHostConfig_url(config, 1);

expect(result).toHaveLength(1);
// The repo name should have decoded spaces, not %20
expect(result[0].name).toBe('github.com/test/Project Name With Spaces');
expect(result[0].displayName).toBe('github.com/test/Project Name With Spaces');

const metadata = result[0].metadata as { gitConfig?: Record<string, string> };
expect(metadata.gitConfig!['zoekt.name']).toBe('github.com/test/Project Name With Spaces');
expect(metadata.gitConfig!['zoekt.display-name']).toBe('github.com/test/Project Name With Spaces');
});
});
23 changes: 22 additions & 1 deletion packages/backend/src/repoCompileUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,28 @@ export const compileGenericGitHostConfig_url = async (

// @note: matches the naming here:
// https://github.com/sourcebot-dev/zoekt/blob/main/gitindex/index.go#L293
const repoName = path.join(remoteUrl.host, remoteUrl.pathname.replace(/\.git$/, ''));
// Decode URL-encoded characters (e.g., %20 -> space) to ensure consistent repo names
let decodedPathname: string;
try {
decodedPathname = decodeURIComponent(remoteUrl.pathname);
} catch (e) {
if (e instanceof URIError) {
const warning = `Skipping ${remoteUrl.toString()} - malformed URL encoding.`;
logger.warn(warning);
reportRepositoryDiscoveryIssue({
code: "INVALID_REPOSITORY_SOURCE",
effect: "TARGET_SKIPPED",
subject: {
kind: "url",
value: remoteUrl.toString(),
},
message: warning,
});
return [];
}
throw e;
}
const repoName = path.join(remoteUrl.host, decodedPathname.replace(/\.git$/, ''));

const repo: RepoData = {
external_codeHostType: 'genericGitHost',
Expand Down
Loading