Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/commands/compareFileWithRev.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as vscode from "vscode";
import { REV_KINDS, TITLE_PREFIX } from "../constants";
import type { GitRepo } from "../git/GitRepo";
import { toRepoRelPath } from "../git/relPath";
import type { Sha } from "../git/types";
import type { Result } from "../result";
import { err, ok } from "../result";
Expand Down Expand Up @@ -69,6 +70,7 @@ const handler = async ({
void vscode.window.showWarningMessage(NOT_IN_REPO);
return;
}
const repoRoot = vsRepo.rootUri.fsPath;
const repo = buildRepo(deps.runner, vsRepo);
const sha = await pickShaForFile({ repo, source, output: deps.output });
if (!sha.ok) {
Expand All @@ -77,8 +79,8 @@ const handler = async ({
await openDiff({
revA: sideAFromSha(sha.value),
revB: { kind: REV_KINDS.workingCopy },
repoRoot: vsRepo.rootUri.fsPath,
relPath: vscode.workspace.asRelativePath(target, false),
repoRoot,
relPath: toRepoRelPath({ repoRoot, fileFsPath: target.fsPath }),
});
};

Expand Down
18 changes: 18 additions & 0 deletions src/git/relPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as path from "node:path";

const GIT_PATH_SEP = "/";

/**
* File path relative to the git repository root, with forward slashes.
*
* Must NOT use workspace-folder-relative paths: the VS Code workspace folder
* can be a subdirectory of the git root (nested package / multi-root), and
* joining that with the git root produces a wrong absolute path.
*/
export const toRepoRelPath = ({
repoRoot,
fileFsPath,
}: {
repoRoot: string;
fileFsPath: string;
}): string => path.relative(repoRoot, fileFsPath).split(path.sep).join(GIT_PATH_SEP);
39 changes: 39 additions & 0 deletions src/test/unit/relPath.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { strict as assert } from "node:assert";
import * as path from "node:path";
import { toRepoRelPath } from "../../git/relPath";

describe("toRepoRelPath", () => {
it("keeps every segment under the git root when the workspace folder is a nested package", () => {
const repoRoot = "/repo";
const fileFsPath = "/repo/scripts/pkg/pkg/module.py";

// Reproduce the product bug: asRelativePath(workspace=.../scripts/pkg)
// yields "pkg/module.py", then path.join(gitRoot, that) is wrong.
const nestedWorkspace = "/repo/scripts/pkg";
const workspaceRel = path.relative(nestedWorkspace, fileFsPath);
assert.equal(workspaceRel, "pkg/module.py");
assert.equal(path.join(repoRoot, workspaceRel), "/repo/pkg/module.py");

assert.equal(toRepoRelPath({ repoRoot, fileFsPath }), "scripts/pkg/pkg/module.py");
});

it("returns a single-segment path for a file at the repo root", () => {
assert.equal(
toRepoRelPath({
repoRoot: "/repo",
fileFsPath: "/repo/README.md",
}),
"README.md"
);
});

it("preserves nested directories under the repo root", () => {
assert.equal(
toRepoRelPath({
repoRoot: "/repo",
fileFsPath: "/repo/dir/c.txt",
}),
"dir/c.txt"
);
});
});