fix(projects): preserve SSH URL path semantics

This commit is contained in:
Mohamed Boudra
2026-07-29 01:20:04 +00:00
parent 29d3b2f76c
commit afbd527c8d
2 changed files with 33 additions and 12 deletions

View File

@@ -20,15 +20,17 @@ describe("deriveProjectGroupKey", () => {
test("normalizes the default SSH port", () => {
const rootPath = path.resolve("repo");
expect(
const derive = (remoteUrl: string) =>
deriveProjectGroupKey({
rootPath,
remoteUrl: "ssh://git@github.com:22/getpaseo/paseo.git",
remoteUrl,
worktreeRoot: rootPath,
mainRepoRoot: null,
}),
).toBe("remote:github.com/getpaseo/paseo");
});
expect(derive("ssh://git@github.com:22/getpaseo/paseo.git")).toBe(
derive("ssh://git@github.com/getpaseo/paseo.git"),
);
});
test("accepts a root-level remote repository path", () => {
@@ -70,6 +72,21 @@ describe("deriveProjectGroupKey", () => {
expect(derive("example.com:srv/repo.git")).not.toBe(derive("example.com:/srv/repo.git"));
});
test("distinguishes absolute SSH URL paths from home-relative SCP paths", () => {
const rootPath = path.resolve("repo");
const derive = (remoteUrl: string) =>
deriveProjectGroupKey({
rootPath,
remoteUrl,
worktreeRoot: rootPath,
mainRepoRoot: null,
});
expect(derive("ssh://git@example.com/srv/repo.git")).not.toBe(
derive("git@example.com:srv/repo.git"),
);
});
test("distinguishes SSH users for home-relative SCP paths", () => {
const rootPath = path.resolve("repo");
const derive = (remoteUrl: string) =>
@@ -115,15 +132,17 @@ describe("deriveProjectGroupKey", () => {
test.each(["git+ssh:", "ssh+git:"])("normalizes SSH alias default ports for %s", (scheme) => {
const rootPath = path.resolve("repo");
expect(
const derive = (remoteUrl: string) =>
deriveProjectGroupKey({
rootPath,
remoteUrl: `${scheme}//git@github.com:22/getpaseo/paseo.git`,
remoteUrl,
worktreeRoot: rootPath,
mainRepoRoot: null,
}),
).toBe("remote:github.com/getpaseo/paseo");
});
expect(derive(`${scheme}//git@github.com:22/getpaseo/paseo.git`)).toBe(
derive("ssh://git@github.com/getpaseo/paseo.git"),
);
});
test("accepts an SCP-style remote with a bracketed IPv6 host", () => {

View File

@@ -82,13 +82,15 @@ function parseUrlRemote(remoteUrl: string): RemoteLocation | null {
try {
const parsed = new URL(remoteUrl);
const host = deriveRemoteHost(parsed);
const remotePath = parsed.pathname ? parsed.pathname.replace(/^\/+/, "") : null;
const preserveLeadingSlash = ["git+ssh:", "ssh:", "ssh+git:"].includes(parsed.protocol);
let remotePath = parsed.pathname || null;
if (remotePath && !preserveLeadingSlash) remotePath = remotePath.replace(/^\/+/, "");
if (!host || !remotePath) return null;
return {
host,
path: remotePath,
relativePathUser: null,
preserveLeadingSlash: false,
preserveLeadingSlash,
decodePercentEncoding: true,
};
} catch {