authorgravatar for lucenz@proton.meSimone <lucenz@proton.me> 2026-08-14 23:56:12+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-14 17:56:12-04:00
logf0719072a1c6435b5a91243afc57bc8bf1f3e2b6
tree30f8fb5ffbfa4d31e8a5c7118738d70fa41c819a
parent9885a845c97325b1099b095011da8385485616f5
signature Signed by PGP key B5690EEEBB952194

fix(server): handle files named HEAD in git status (#6397)

Co-authored-by: Simone <185146821+Lucenx9@users.noreply.github.com>

2 files changed, 26 insertions(+), 4 deletions(-)

apps/server/src/vcs/GitVcsDriverCore.test.ts+21
...@@ -950,6 +950,27 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => {...@@ -950,6 +950,27 @@ it.layer(TestLayer)("GitVcsDriver core integration", (it) => {
950 }),950 }),
951 );951 );
952952
953 it.effect("reports changes to a file named HEAD", () =>
954 Effect.gen(function* () {
955 const cwd = yield* makeTmpDir();
956 yield* initRepoWithCommit(cwd);
957 yield* writeTextFile(cwd, "HEAD", "first line\n");
958 yield* git(cwd, ["add", "HEAD"]);
959 yield* git(cwd, ["commit", "-m", "add HEAD file"]);
960 yield* writeTextFile(cwd, "HEAD", "first line\nsecond line\n");
961
962 const status = yield* (yield* GitVcsDriver.GitVcsDriver).statusDetails(cwd);
963
964 assert.equal(status.isRepo, true);
965 assert.equal(status.hasWorkingTreeChanges, true);
966 assert.deepInclude(status.workingTree.files, {
967 path: "HEAD",
968 insertions: 1,
969 deletions: 0,
970 });
971 }),
972 );
973
953 it.effect("reports default-branch delta separately from upstream delta", () =>974 it.effect("reports default-branch delta separately from upstream delta", () =>
954 Effect.gen(function* () {975 Effect.gen(function* () {
955 const cwd = yield* makeTmpDir();976 const cwd = yield* makeTmpDir();
apps/server/src/vcs/GitVcsDriverCore.ts+5-4
...@@ -420,9 +420,10 @@ function isNonRepositoryGitStderr(stderr: string): boolean {...@@ -420,9 +420,10 @@ function isNonRepositoryGitStderr(stderr: string): boolean {
420 return stderr.toLowerCase().includes("not a git repository");420 return stderr.toLowerCase().includes("not a git repository");
421}421}
422function isUnbornHeadStderr(stderr: string): boolean {422function isUnbornHeadStderr(stderr: string): boolean {
423 const normalized = stderr.toLowerCase();
423 return (424 return (
424 stderr.toLowerCase().includes("unknown revision") &&425 normalized.includes("bad revision 'head'") ||
425 stderr.toLowerCase().includes("path not in the working tree")426 (normalized.includes("unknown revision") && normalized.includes("path not in the working tree"))
426 );427 );
427}428}
428429
...@@ -1600,7 +1601,7 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function*...@@ -1600,7 +1601,7 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function*
1600 executeGitWithStableDiagnostics(1601 executeGitWithStableDiagnostics(
1601 "GitVcsDriver.statusDetails.numstat",1602 "GitVcsDriver.statusDetails.numstat",
1602 cwd,1603 cwd,
1603 ["diff", "HEAD", "--numstat"],1604 ["diff", "HEAD", "--numstat", "--"],
1604 { allowNonZeroExit: true },1605 { allowNonZeroExit: true },
1605 ).pipe(1606 ).pipe(
1606 Effect.flatMap((result) => {1607 Effect.flatMap((result) => {
...@@ -1642,7 +1643,7 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function*...@@ -1642,7 +1643,7 @@ export const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function*
1642 ...gitCommandContext({1643 ...gitCommandContext({
1643 operation: "GitVcsDriver.statusDetails.numstat",1644 operation: "GitVcsDriver.statusDetails.numstat",
1644 cwd,1645 cwd,
1645 args: ["diff", "HEAD", "--numstat"],1646 args: ["diff", "HEAD", "--numstat", "--"],
1646 }),1647 }),
1647 detail: "git diff HEAD --numstat failed.",1648 detail: "git diff HEAD --numstat failed.",
1648 exitCode: result.exitCode,1649 exitCode: result.exitCode,