Normalize path facts once at the owning filesystem boundary, preserve caller spelling and unavailable inventory members, and retain typed read failures. Record exclusion entry kinds so only proven directories can establish descendant symlink targets; omitted legacy kinds stay unknown. Reviewed fixes include #1969, #2642, #2759, and #2994. Validation: 26 reference graph source tests passed; prior artifact checks passed 275 tests with one Windows-only skip. Updated compiled import workflows are included in final validation after regeneration.
135 lines
4.1 KiB
TypeScript
135 lines
4.1 KiB
TypeScript
import { chmod, mkdir, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { expect, it } from "vitest";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { withArtifactResourceScope } from "../../fixtures/artifactInventory.js";
|
|
import { execFileOutput } from "../../../src/process/ExecFileOutput.js";
|
|
import { readReferenceSourceVcs } from "../../../src/application/ReferenceSourceVcsAdapter.js";
|
|
|
|
const readVcs = (root: string, signal?: AbortSignal) =>
|
|
withArtifactResourceScope(async (resources) => {
|
|
const result = await readReferenceSourceVcs(root, resources, signal);
|
|
if (!result.ok) throw result.error;
|
|
return result.value;
|
|
});
|
|
|
|
it("reads a linked worktree's own branch and detached HEAD through shared Git refs", async () => {
|
|
const root = await createTestTempDirectory("rea-reference-worktree-");
|
|
const repository = join(root, "repository");
|
|
await mkdir(repository);
|
|
const git = (...args: string[]) =>
|
|
execFileOutput("git", args, { cwd: repository });
|
|
await git("init", "--initial-branch=main");
|
|
const commit = () =>
|
|
git(
|
|
"-c",
|
|
"user.name=REA fixture",
|
|
"-c",
|
|
"user.email=fixture@example.invalid",
|
|
"-c",
|
|
"commit.gpgsign=false",
|
|
"commit",
|
|
"--allow-empty",
|
|
"-m",
|
|
"fixture",
|
|
);
|
|
await commit();
|
|
const mainHead = (await git("rev-parse", "HEAD")).stdout.trim();
|
|
const linked = join(root, "linked");
|
|
await git("worktree", "add", "-b", "linked", linked);
|
|
await execFileOutput(
|
|
"git",
|
|
[
|
|
"-c",
|
|
"user.name=REA fixture",
|
|
"-c",
|
|
"user.email=fixture@example.invalid",
|
|
"-c",
|
|
"commit.gpgsign=false",
|
|
"commit",
|
|
"--allow-empty",
|
|
"-m",
|
|
"linked fixture",
|
|
],
|
|
{ cwd: linked },
|
|
);
|
|
const linkedHead = (
|
|
await execFileOutput("git", ["rev-parse", "HEAD"], { cwd: linked })
|
|
).stdout.trim();
|
|
expect(linkedHead).not.toBe(mainHead);
|
|
expect(await readVcs(repository)).toEqual({
|
|
kind: "git",
|
|
head: mainHead,
|
|
dirty: null,
|
|
});
|
|
expect(await readVcs(linked)).toEqual({
|
|
kind: "git",
|
|
head: linkedHead,
|
|
dirty: null,
|
|
});
|
|
await git("pack-refs", "--all", "--prune");
|
|
expect(await readVcs(linked)).toEqual({
|
|
kind: "git",
|
|
head: linkedHead,
|
|
dirty: null,
|
|
});
|
|
await execFileOutput("git", ["checkout", "--detach", mainHead], {
|
|
cwd: linked,
|
|
});
|
|
expect(await readVcs(linked)).toEqual({
|
|
kind: "git",
|
|
head: mainHead,
|
|
dirty: null,
|
|
});
|
|
const relativePointer = join(root, "relative");
|
|
await mkdir(relativePointer);
|
|
await writeFile(
|
|
join(relativePointer, ".git"),
|
|
"gitdir: ../repository/.git\r\n",
|
|
);
|
|
expect(await readVcs(relativePointer)).toEqual({
|
|
kind: "git",
|
|
head: mainHead,
|
|
dirty: null,
|
|
});
|
|
// A non-regular loose ref must remain unknown even when packed-refs has
|
|
// a readable value. The library's promise-API probe is not a source failure.
|
|
await mkdir(join(repository, ".git", "refs", "heads", "main"));
|
|
expect(await readVcs(repository)).toEqual({
|
|
kind: "unknown",
|
|
head: null,
|
|
dirty: null,
|
|
});
|
|
const controller = new AbortController();
|
|
controller.abort();
|
|
expect(await readVcs(linked, controller.signal)).toEqual({
|
|
kind: "unknown",
|
|
head: null,
|
|
dirty: null,
|
|
});
|
|
});
|
|
|
|
it.skipIf(process.platform === "win32" || process.getuid?.() === 0)(
|
|
"keeps unreadable loose refs unknown instead of falling back to packed refs",
|
|
async () => {
|
|
const root = await createTestTempDirectory("rea-reference-loose-ref-");
|
|
const gitdir = join(root, ".git");
|
|
const looseRef = join(gitdir, "refs", "heads", "main");
|
|
await mkdir(join(gitdir, "refs", "heads"), { recursive: true });
|
|
const head = "1234567890abcdef1234567890abcdef12345678";
|
|
await writeFile(join(gitdir, "HEAD"), "ref: refs/heads/main\n");
|
|
await writeFile(join(gitdir, "packed-refs"), `${head} refs/heads/main\n`);
|
|
await writeFile(looseRef, `${head}\n`);
|
|
await chmod(looseRef, 0);
|
|
try {
|
|
expect(await readVcs(root)).toEqual({
|
|
kind: "unknown",
|
|
head: null,
|
|
dirty: null,
|
|
});
|
|
} finally {
|
|
await chmod(looseRef, 0o600);
|
|
}
|
|
},
|
|
);
|