@neuralnomads/codenomad-dev 0.20.0-dev-20260922-e68ac524 → 0.20.0-dev-20260922-5650ed9d
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/workspaces/__tests__/git-common-directory.test.js +11 -0
- package/dist/workspaces/__tests__/git-common-directory.test.js.map +1 -1
- package/dist/workspaces/git-process.js +16 -6
- package/dist/workspaces/git-process.js.map +1 -1
- package/dist/workspaces/git-status.js +32 -38
- package/dist/workspaces/git-status.js.map +1 -1
- package/dist/workspaces/git-status.test.js +28 -1
- package/dist/workspaces/git-status.test.js.map +1 -1
- package/package.json +1 -1
- package/public/assets/App-BInh9z8B.js +51 -0
- package/public/assets/App-BInh9z8B.js.map +1 -0
- package/public/assets/{FilesTab-CznSFRW_.js → FilesTab-Bz3ekii-.js} +3 -3
- package/public/assets/{FilesTab-CznSFRW_.js.map → FilesTab-Bz3ekii-.js.map} +1 -1
- package/public/assets/{GitChangesTab-DLtyzpX0.js → GitChangesTab-DEmFm9X9.js} +3 -3
- package/public/assets/{GitChangesTab-DLtyzpX0.js.map → GitChangesTab-DEmFm9X9.js.map} +1 -1
- package/public/assets/{SplitFilePanel-BHkyIER4.js → SplitFilePanel-D9KFC8Yo.js} +2 -2
- package/public/assets/{SplitFilePanel-BHkyIER4.js.map → SplitFilePanel-D9KFC8Yo.js.map} +1 -1
- package/public/assets/{StatusTab-BB3L4UEF.js → StatusTab-V8ZSDdnZ.js} +2 -2
- package/public/assets/{StatusTab-BB3L4UEF.js.map → StatusTab-V8ZSDdnZ.js.map} +1 -1
- package/public/assets/{align-justify-BGcr8Y3G.js → align-justify-Bi3yHu8i.js} +2 -2
- package/public/assets/{align-justify-BGcr8Y3G.js.map → align-justify-Bi3yHu8i.js.map} +1 -1
- package/public/assets/{bundle-full-CwHbX2Tz.js → bundle-full-CSLya0fX.js} +2 -2
- package/public/assets/{bundle-full-CwHbX2Tz.js.map → bundle-full-CSLya0fX.js.map} +1 -1
- package/public/assets/{client-state-CjVIIkWh.js → client-state-BZr92Odb.js} +2 -2
- package/public/assets/{client-state-CjVIIkWh.js.map → client-state-BZr92Odb.js.map} +1 -1
- package/public/assets/{diff-viewer-FNBCogHl.js → diff-viewer-DHy_0NiU.js} +2 -2
- package/public/assets/{diff-viewer-FNBCogHl.js.map → diff-viewer-DHy_0NiU.js.map} +1 -1
- package/public/assets/{index-AeE49W2c.js → index-B80YtNDR.js} +2 -2
- package/public/assets/{index-AeE49W2c.js.map → index-B80YtNDR.js.map} +1 -1
- package/public/assets/{index-D292-iMl.js → index-BF4aeRZL.js} +2 -2
- package/public/assets/{index-D292-iMl.js.map → index-BF4aeRZL.js.map} +1 -1
- package/public/assets/{index-Dk3HFhZs.js → index-BMM7DX4E.js} +2 -2
- package/public/assets/{index-Dk3HFhZs.js.map → index-BMM7DX4E.js.map} +1 -1
- package/public/assets/{index-CaYFZAN4.js → index-C5fAhE7q.js} +2 -2
- package/public/assets/{index-CaYFZAN4.js.map → index-C5fAhE7q.js.map} +1 -1
- package/public/assets/{index-CLdQSSfU.js → index-Cr0fq_1m.js} +2 -2
- package/public/assets/{index-CLdQSSfU.js.map → index-Cr0fq_1m.js.map} +1 -1
- package/public/assets/{index-BhInW6hR.js → index-DTM4JCOg.js} +3 -3
- package/public/assets/{index-BhInW6hR.js.map → index-DTM4JCOg.js.map} +1 -1
- package/public/assets/{index-C9vJvaFa.js → index-DUSHdfNt.js} +2 -2
- package/public/assets/{index-C9vJvaFa.js.map → index-DUSHdfNt.js.map} +1 -1
- package/public/assets/{index-ChudAs0f.js → index-DvzNUKxQ.js} +2 -2
- package/public/assets/{index-ChudAs0f.js.map → index-DvzNUKxQ.js.map} +1 -1
- package/public/assets/index-DyV5PLqt.js +2 -0
- package/public/assets/{index-C2jBtFkI.js.map → index-DyV5PLqt.js.map} +1 -1
- package/public/assets/{index-CLgpBTF6.js → index-aeY5PFwo.js} +2 -2
- package/public/assets/{index-CLgpBTF6.js.map → index-aeY5PFwo.js.map} +1 -1
- package/public/assets/{index-B875QQLi.js → index-je4g4OjU.js} +2 -2
- package/public/assets/{index-B875QQLi.js.map → index-je4g4OjU.js.map} +1 -1
- package/public/assets/{index-C8zCyB6Z.js → index-vOjZjElj.js} +2 -2
- package/public/assets/{index-C8zCyB6Z.js.map → index-vOjZjElj.js.map} +1 -1
- package/public/assets/{loading-D8BcUQaM.js → loading-XXcoiSPg.js} +2 -2
- package/public/assets/{loading-D8BcUQaM.js.map → loading-XXcoiSPg.js.map} +1 -1
- package/public/assets/main-C_TdncJ5.js +42 -0
- package/public/assets/main-C_TdncJ5.js.map +1 -0
- package/public/assets/{markdown-CRPEbAqL.js → markdown-DcIkkDVW.js} +4 -4
- package/public/assets/{markdown-CRPEbAqL.js.map → markdown-DcIkkDVW.js.map} +1 -1
- package/public/assets/{monaco-viewer-CG0aPZ-r.js → monaco-viewer-DWKuMLsd.js} +2 -2
- package/public/assets/monaco-viewer-DWKuMLsd.js.map +1 -0
- package/public/assets/{startup-state-settings-card-CHqS2Pmg.js → startup-state-settings-card-DsQ6ZLN9.js} +2 -2
- package/public/assets/{startup-state-settings-card-CHqS2Pmg.js.map → startup-state-settings-card-DsQ6ZLN9.js.map} +1 -1
- package/public/assets/{terminal-square-DhmNJqNG.js → terminal-square-CvdvCOmv.js} +2 -2
- package/public/assets/{terminal-square-DhmNJqNG.js.map → terminal-square-CvdvCOmv.js.map} +1 -1
- package/public/assets/{todo-DXJ5kP_Z.js → todo-DZCMHJBr.js} +2 -2
- package/public/assets/{todo-DXJ5kP_Z.js.map → todo-DZCMHJBr.js.map} +1 -1
- package/public/assets/{tool-call-Bjo9DalZ.js → tool-call-Bw7CQyE9.js} +17 -17
- package/public/assets/{tool-call-Bjo9DalZ.js.map → tool-call-Bw7CQyE9.js.map} +1 -1
- package/public/assets/{unified-picker-CxrTM_tY.js → unified-picker-D3FIJGsM.js} +2 -2
- package/public/assets/{unified-picker-CxrTM_tY.js.map → unified-picker-D3FIJGsM.js.map} +1 -1
- package/public/assets/{wrap-text-BznZeD4J.js → wrap-text-ma0EMBIk.js} +2 -2
- package/public/assets/{wrap-text-BznZeD4J.js.map → wrap-text-ma0EMBIk.js.map} +1 -1
- package/public/index.html +3 -3
- package/public/loading.html +3 -3
- package/public/sw.js +1 -1
- package/public/sw.js.map +1 -1
- package/public/assets/App-BSkmbNUC.js +0 -51
- package/public/assets/App-BSkmbNUC.js.map +0 -1
- package/public/assets/index-C2jBtFkI.js +0 -2
- package/public/assets/main-DRbjgAo8.js +0 -42
- package/public/assets/main-DRbjgAo8.js.map +0 -1
- package/public/assets/monaco-viewer-CG0aPZ-r.js.map +0 -1
|
@@ -6,6 +6,7 @@ import path from "node:path";
|
|
|
6
6
|
import { afterEach, beforeEach, describe, it } from "node:test";
|
|
7
7
|
import { readGitCommonDirectory } from "../git-common-directory";
|
|
8
8
|
import { sharesGitCommonDirectory } from "../git-worktrees";
|
|
9
|
+
import { runGitProcess } from "../git-process";
|
|
9
10
|
const git = (directory, ...args) => execFileSync("git", ["-C", directory, ...args], {
|
|
10
11
|
encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], windowsHide: true,
|
|
11
12
|
}).trim();
|
|
@@ -82,5 +83,15 @@ describe("Git ownership preflight admission", () => {
|
|
|
82
83
|
git(temp, "init", missing);
|
|
83
84
|
assert.equal(await readGitCommonDirectory(missing), realpathSync(path.join(missing, ".git")));
|
|
84
85
|
});
|
|
86
|
+
it("admits a newly selected worktree before the pending Git display batch", async () => {
|
|
87
|
+
const blockers = Array.from({ length: 2 }, () => runGitProcess(repo, ["-c", "alias.pause=!sleep 0.2", "pause"]));
|
|
88
|
+
const display = Array.from({ length: 24 }, () => runGitProcess(repo, ["diff", "--numstat"]));
|
|
89
|
+
const ownership = readGitCommonDirectory(linked);
|
|
90
|
+
await Promise.all([...blockers, ...display, ownership]);
|
|
91
|
+
const starts = events(trace).filter(event => event.event === "start");
|
|
92
|
+
const foregroundIndex = starts.findIndex(event => event.argv.includes("--git-common-dir"));
|
|
93
|
+
const lastDisplayIndex = starts.map(event => event.argv.includes("--numstat")).lastIndexOf(true);
|
|
94
|
+
assert.ok(foregroundIndex >= 0 && foregroundIndex < lastDisplayIndex, `ownership at ${foregroundIndex} waited behind all display reads (last at ${lastDisplayIndex})`);
|
|
95
|
+
});
|
|
85
96
|
});
|
|
86
97
|
//# sourceMappingURL=git-common-directory.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-common-directory.test.js","sourceRoot":"","sources":["../../../src/workspaces/__tests__/git-common-directory.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\"\nimport { execFileSync } from \"node:child_process\"\nimport { mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from \"node:fs\"\nimport { tmpdir } from \"node:os\"\nimport path from \"node:path\"\nimport { afterEach, beforeEach, describe, it } from \"node:test\"\nimport { readGitCommonDirectory } from \"../git-common-directory\"\nimport { sharesGitCommonDirectory } from \"../git-worktrees\"\n\nconst git = (directory: string, ...args: string[]) => execFileSync(\"git\", [\"-C\", directory, ...args], {\n encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"pipe\"], windowsHide: true,\n}).trim()\n\ndescribe(\"Git ownership preflight admission\", () => {\n let temp: string\n let repo: string\n let linked: string\n let trace: string\n let environment: Record<string, string | undefined>\n\n beforeEach(() => {\n environment = Object.fromEntries(Object.entries(process.env).filter(([key]) => key.startsWith(\"GIT_\")))\n for (const key of Object.keys(environment)) delete process.env[key]\n temp = mkdtempSync(path.join(tmpdir(), \"codenomad-git-preflight-\"))\n process.env.GIT_CONFIG_NOSYSTEM = \"1\"\n process.env.GIT_CONFIG_GLOBAL = path.join(temp, \"empty-global-config\")\n writeFileSync(process.env.GIT_CONFIG_GLOBAL, \"\")\n repo = path.join(temp, \"main repo\")\n linked = path.join(temp, \"linked checkout\")\n git(temp, \"init\", \"--initial-branch=main\", repo)\n git(repo, \"-c\", \"user.name=Test\", \"-c\", \"user.email=test@example.com\", \"commit\", \"--allow-empty\", \"-m\", \"initial\")\n git(repo, \"worktree\", \"add\", \"-b\", \"feature\", linked)\n trace = path.join(temp, \"trace.json\")\n process.env.GIT_TRACE2_EVENT = trace.replaceAll(\"\\\\\", \"/\")\n })\n\n afterEach(() => {\n for (const key of Object.keys(process.env)) if (key.startsWith(\"GIT_\")) delete process.env[key]\n Object.assign(process.env, environment)\n rmSync(temp, { recursive: true, force: true })\n })\n\n const events = (file: string) => readFileSync(file, \"utf8\").trim().split(\"\\n\").map(line => JSON.parse(line))\n\n it(\"coalesces a concurrent session/project ownership fan into one command per directory\", async () => {\n const reads = Array.from({ length: 48 }, () => sharesGitCommonDirectory(repo, linked))\n assert.deepEqual(await Promise.all(reads), Array(48).fill(true))\n const starts = events(trace).filter(event => event.event === \"start\")\n assert.equal(starts.length, 2)\n assert.deepEqual(starts.map(event => event.argv[2]).sort(), [repo, linked].sort())\n })\n\n it(\"bounds distinct Git processes without holding the calling event loop\", async () => {\n const directories = Array.from({ length: 10 }, (_, index) => path.join(temp, `repo-${index}`))\n for (const directory of directories) git(temp, \"init\", directory)\n writeFileSync(trace, \"\")\n const reads = directories.map(readGitCommonDirectory)\n let settled = false\n void Promise.all(reads).then(() => { settled = true })\n await new Promise<void>(resolve => setImmediate(resolve))\n assert.equal(settled, false)\n assert.deepEqual(await Promise.all(reads), directories.map(directory => realpathSync(path.join(directory, \".git\"))))\n const timeline = events(trace).filter(event => event.event === \"start\" || event.event === \"exit\")\n .sort((a, b) => a.time.localeCompare(b.time))\n let running = 0\n let starts = 0\n for (const event of timeline) {\n running += event.event === \"start\" ? 1 : -1\n if (event.event === \"start\") starts += 1\n assert.ok(running >= 0 && running <= 2, `concurrent Git processes: ${running}`)\n }\n assert.equal(starts, directories.length)\n assert.equal(running, 0)\n })\n\n it(\"rechecks changed Git identity and does not retain failed reads\", async () => {\n assert.equal(await sharesGitCommonDirectory(repo, linked), true)\n const other = path.join(temp, \"independent clone\")\n git(temp, \"clone\", repo, other)\n rmSync(path.join(linked, \".git\"))\n writeFileSync(path.join(linked, \".git\"), `gitdir: ${path.join(other, \".git\").replaceAll(\"\\\\\", \"/\")}\\n`)\n assert.equal(await sharesGitCommonDirectory(repo, linked), false)\n const missing = path.join(temp, \"not-yet-created\")\n await assert.rejects(readGitCommonDirectory(missing))\n git(temp, \"init\", missing)\n assert.equal(await readGitCommonDirectory(missing), realpathSync(path.join(missing, \".git\")))\n })\n})\n"]}
|
|
1
|
+
{"version":3,"file":"git-common-directory.test.js","sourceRoot":"","sources":["../../../src/workspaces/__tests__/git-common-directory.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAA;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACxF,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAChC,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAA;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAA;AAChE,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAA;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAE9C,MAAM,GAAG,GAAG,CAAC,SAAiB,EAAE,GAAG,IAAc,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,EAAE;IACpG,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,EAAE,IAAI;CACvE,CAAC,CAAC,IAAI,EAAE,CAAA;AAET,QAAQ,CAAC,mCAAmC,EAAE,GAAG,EAAE;IACjD,IAAI,IAAY,CAAA;IAChB,IAAI,IAAY,CAAA;IAChB,IAAI,MAAc,CAAA;IAClB,IAAI,KAAa,CAAA;IACjB,IAAI,WAA+C,CAAA;IAEnD,UAAU,CAAC,GAAG,EAAE;QACd,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;QACvG,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC;YAAE,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACnE,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,0BAA0B,CAAC,CAAC,CAAA;QACnE,OAAO,CAAC,GAAG,CAAC,mBAAmB,GAAG,GAAG,CAAA;QACrC,OAAO,CAAC,GAAG,CAAC,iBAAiB,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,qBAAqB,CAAC,CAAA;QACtE,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,EAAE,EAAE,CAAC,CAAA;QAChD,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAA;QACnC,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAA;QAC3C,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,uBAAuB,EAAE,IAAI,CAAC,CAAA;QAChD,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE,6BAA6B,EAAE,QAAQ,EAAE,eAAe,EAAE,IAAI,EAAE,SAAS,CAAC,CAAA;QAClH,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;QACrD,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;QACrC,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;IAC5D,CAAC,CAAC,CAAA;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC;YAAE,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,OAAO,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC/F,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,WAAW,CAAC,CAAA;QACvC,MAAM,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;IAChD,CAAC,CAAC,CAAA;IAEF,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;IAE5G,EAAE,CAAC,qFAAqF,EAAE,KAAK,IAAI,EAAE;QACnG,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,wBAAwB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAA;QACtF,MAAM,CAAC,SAAS,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;QAChE,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC,CAAA;QACrE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAA;QAC9B,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;IACpF,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,KAAK,EAAE,CAAC,CAAC,CAAA;QAC9F,KAAK,MAAM,SAAS,IAAI,WAAW;YAAE,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,CAAA;QACjE,aAAa,CAAC,KAAK,EAAE,EAAE,CAAC,CAAA;QACxB,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAA;QACrD,IAAI,OAAO,GAAG,KAAK,CAAA;QACnB,KAAK,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,IAAI,CAAA,CAAC,CAAC,CAAC,CAAA;QACtD,MAAM,IAAI,OAAO,CAAO,OAAO,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAA;QACzD,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QAC5B,MAAM,CAAC,SAAS,CAAC,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;QACpH,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,OAAO,IAAI,KAAK,CAAC,KAAK,KAAK,MAAM,CAAC;aAC9F,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;QAC/C,IAAI,OAAO,GAAG,CAAC,CAAA;QACf,IAAI,MAAM,GAAG,CAAC,CAAA;QACd,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;YAC7B,OAAO,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;YAC3C,IAAI,KAAK,CAAC,KAAK,KAAK,OAAO;gBAAE,MAAM,IAAI,CAAC,CAAA;YACxC,MAAM,CAAC,EAAE,CAAC,OAAO,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,EAAE,6BAA6B,OAAO,EAAE,CAAC,CAAA;QACjF,CAAC;QACD,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;QACxC,MAAM,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAA;IAC1B,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;QAC9E,MAAM,CAAC,KAAK,CAAC,MAAM,wBAAwB,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,CAAA;QAChE,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAA;QAClD,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAA;QAC/B,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;QACjC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,UAAU,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAA;QACvG,MAAM,CAAC,KAAK,CAAC,MAAM,wBAAwB,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,KAAK,CAAC,CAAA;QACjE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAA;QAClD,MAAM,MAAM,CAAC,OAAO,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAA;QACrD,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;QAC1B,MAAM,CAAC,KAAK,CAAC,MAAM,sBAAsB,CAAC,OAAO,CAAC,EAAE,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;IAC/F,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,uEAAuE,EAAE,KAAK,IAAI,EAAE;QACrF,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,wBAAwB,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;QAChH,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;QAC5F,MAAM,SAAS,GAAG,sBAAsB,CAAC,MAAM,CAAC,CAAA;QAChD,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,QAAQ,EAAE,GAAG,OAAO,EAAE,SAAS,CAAC,CAAC,CAAA;QACvD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,KAAK,OAAO,CAAC,CAAA;QACrE,MAAM,eAAe,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC,CAAA;QAC1F,MAAM,gBAAgB,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAA;QAChG,MAAM,CAAC,EAAE,CAAC,eAAe,IAAI,CAAC,IAAI,eAAe,GAAG,gBAAgB,EAClE,gBAAgB,eAAe,6CAA6C,gBAAgB,GAAG,CAAC,CAAA;IACpG,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA","sourcesContent":["import assert from \"node:assert/strict\"\nimport { execFileSync } from \"node:child_process\"\nimport { mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSync } from \"node:fs\"\nimport { tmpdir } from \"node:os\"\nimport path from \"node:path\"\nimport { afterEach, beforeEach, describe, it } from \"node:test\"\nimport { readGitCommonDirectory } from \"../git-common-directory\"\nimport { sharesGitCommonDirectory } from \"../git-worktrees\"\nimport { runGitProcess } from \"../git-process\"\n\nconst git = (directory: string, ...args: string[]) => execFileSync(\"git\", [\"-C\", directory, ...args], {\n encoding: \"utf8\", stdio: [\"ignore\", \"pipe\", \"pipe\"], windowsHide: true,\n}).trim()\n\ndescribe(\"Git ownership preflight admission\", () => {\n let temp: string\n let repo: string\n let linked: string\n let trace: string\n let environment: Record<string, string | undefined>\n\n beforeEach(() => {\n environment = Object.fromEntries(Object.entries(process.env).filter(([key]) => key.startsWith(\"GIT_\")))\n for (const key of Object.keys(environment)) delete process.env[key]\n temp = mkdtempSync(path.join(tmpdir(), \"codenomad-git-preflight-\"))\n process.env.GIT_CONFIG_NOSYSTEM = \"1\"\n process.env.GIT_CONFIG_GLOBAL = path.join(temp, \"empty-global-config\")\n writeFileSync(process.env.GIT_CONFIG_GLOBAL, \"\")\n repo = path.join(temp, \"main repo\")\n linked = path.join(temp, \"linked checkout\")\n git(temp, \"init\", \"--initial-branch=main\", repo)\n git(repo, \"-c\", \"user.name=Test\", \"-c\", \"user.email=test@example.com\", \"commit\", \"--allow-empty\", \"-m\", \"initial\")\n git(repo, \"worktree\", \"add\", \"-b\", \"feature\", linked)\n trace = path.join(temp, \"trace.json\")\n process.env.GIT_TRACE2_EVENT = trace.replaceAll(\"\\\\\", \"/\")\n })\n\n afterEach(() => {\n for (const key of Object.keys(process.env)) if (key.startsWith(\"GIT_\")) delete process.env[key]\n Object.assign(process.env, environment)\n rmSync(temp, { recursive: true, force: true })\n })\n\n const events = (file: string) => readFileSync(file, \"utf8\").trim().split(\"\\n\").map(line => JSON.parse(line))\n\n it(\"coalesces a concurrent session/project ownership fan into one command per directory\", async () => {\n const reads = Array.from({ length: 48 }, () => sharesGitCommonDirectory(repo, linked))\n assert.deepEqual(await Promise.all(reads), Array(48).fill(true))\n const starts = events(trace).filter(event => event.event === \"start\")\n assert.equal(starts.length, 2)\n assert.deepEqual(starts.map(event => event.argv[2]).sort(), [repo, linked].sort())\n })\n\n it(\"bounds distinct Git processes without holding the calling event loop\", async () => {\n const directories = Array.from({ length: 10 }, (_, index) => path.join(temp, `repo-${index}`))\n for (const directory of directories) git(temp, \"init\", directory)\n writeFileSync(trace, \"\")\n const reads = directories.map(readGitCommonDirectory)\n let settled = false\n void Promise.all(reads).then(() => { settled = true })\n await new Promise<void>(resolve => setImmediate(resolve))\n assert.equal(settled, false)\n assert.deepEqual(await Promise.all(reads), directories.map(directory => realpathSync(path.join(directory, \".git\"))))\n const timeline = events(trace).filter(event => event.event === \"start\" || event.event === \"exit\")\n .sort((a, b) => a.time.localeCompare(b.time))\n let running = 0\n let starts = 0\n for (const event of timeline) {\n running += event.event === \"start\" ? 1 : -1\n if (event.event === \"start\") starts += 1\n assert.ok(running >= 0 && running <= 2, `concurrent Git processes: ${running}`)\n }\n assert.equal(starts, directories.length)\n assert.equal(running, 0)\n })\n\n it(\"rechecks changed Git identity and does not retain failed reads\", async () => {\n assert.equal(await sharesGitCommonDirectory(repo, linked), true)\n const other = path.join(temp, \"independent clone\")\n git(temp, \"clone\", repo, other)\n rmSync(path.join(linked, \".git\"))\n writeFileSync(path.join(linked, \".git\"), `gitdir: ${path.join(other, \".git\").replaceAll(\"\\\\\", \"/\")}\\n`)\n assert.equal(await sharesGitCommonDirectory(repo, linked), false)\n const missing = path.join(temp, \"not-yet-created\")\n await assert.rejects(readGitCommonDirectory(missing))\n git(temp, \"init\", missing)\n assert.equal(await readGitCommonDirectory(missing), realpathSync(path.join(missing, \".git\")))\n })\n\n it(\"admits a newly selected worktree before the pending Git display batch\", async () => {\n const blockers = Array.from({ length: 2 }, () => runGitProcess(repo, [\"-c\", \"alias.pause=!sleep 0.2\", \"pause\"]))\n const display = Array.from({ length: 24 }, () => runGitProcess(repo, [\"diff\", \"--numstat\"]))\n const ownership = readGitCommonDirectory(linked)\n await Promise.all([...blockers, ...display, ownership])\n const starts = events(trace).filter(event => event.event === \"start\")\n const foregroundIndex = starts.findIndex(event => event.argv.includes(\"--git-common-dir\"))\n const lastDisplayIndex = starts.map(event => event.argv.includes(\"--numstat\")).lastIndexOf(true)\n assert.ok(foregroundIndex >= 0 && foregroundIndex < lastDisplayIndex,\n `ownership at ${foregroundIndex} waited behind all display reads (last at ${lastDisplayIndex})`)\n })\n})\n"]}
|
|
@@ -6,13 +6,14 @@ function workerMain() {
|
|
|
6
6
|
const { parentPort } = require("node:worker_threads");
|
|
7
7
|
const { execFile } = require("node:child_process");
|
|
8
8
|
const queue = [];
|
|
9
|
+
const foreground = [];
|
|
9
10
|
let running = 0;
|
|
10
11
|
const scheduler = { pump() {
|
|
11
|
-
while (running < 2 && queue.length) {
|
|
12
|
-
const request = queue.shift();
|
|
12
|
+
while (running < 2 && (foreground.length || queue.length)) {
|
|
13
|
+
const request = (foreground.shift() ?? queue.shift());
|
|
13
14
|
running += 1;
|
|
14
15
|
execFile("git", ["-C", request.directory, ...request.args], {
|
|
15
|
-
encoding: "utf8", windowsHide: true, maxBuffer: 1024 * 1024,
|
|
16
|
+
encoding: "utf8", windowsHide: true, maxBuffer: request.maxBuffer ?? 1024 * 1024,
|
|
16
17
|
env: request.env, timeout: request.timeout,
|
|
17
18
|
}, (error, stdout, stderr) => {
|
|
18
19
|
parentPort.postMessage({ id: request.id, stdout, stderr,
|
|
@@ -23,7 +24,11 @@ function workerMain() {
|
|
|
23
24
|
});
|
|
24
25
|
}
|
|
25
26
|
} };
|
|
26
|
-
parentPort.on("message", (request) => {
|
|
27
|
+
parentPort.on("message", (request) => {
|
|
28
|
+
const target = request.priority === "foreground" ? foreground : queue;
|
|
29
|
+
target.push(request);
|
|
30
|
+
scheduler.pump();
|
|
31
|
+
});
|
|
27
32
|
}
|
|
28
33
|
let worker;
|
|
29
34
|
let sequence = 0;
|
|
@@ -53,7 +58,7 @@ function getWorker() {
|
|
|
53
58
|
code: response.error.code, stdout: response.stdout, stderr: response.stderr,
|
|
54
59
|
}));
|
|
55
60
|
else
|
|
56
|
-
task.resolve(response.stdout
|
|
61
|
+
task.resolve(response.stdout);
|
|
57
62
|
if (pending.size === 0)
|
|
58
63
|
created.unref();
|
|
59
64
|
});
|
|
@@ -61,13 +66,18 @@ function getWorker() {
|
|
|
61
66
|
return created;
|
|
62
67
|
}
|
|
63
68
|
export function runWorktreeGit(directory, args, timeout) {
|
|
69
|
+
// Ownership and inventory reads must not wait behind an entire display batch.
|
|
70
|
+
return runGitProcess(directory, args, { timeout, priority: "foreground" }).then(stdout => stdout.replace(/\r?\n$/, ""));
|
|
71
|
+
}
|
|
72
|
+
// Content/diff readers need exact bytes decoded as UTF-8, including the final newline.
|
|
73
|
+
export function runGitProcess(directory, args, options = {}) {
|
|
64
74
|
return new Promise((resolve, reject) => {
|
|
65
75
|
const current = getWorker();
|
|
66
76
|
const id = ++sequence;
|
|
67
77
|
pending.set(id, { resolve, reject });
|
|
68
78
|
current.ref();
|
|
69
79
|
try {
|
|
70
|
-
current.postMessage({ id, directory, args, env: { ...process.env },
|
|
80
|
+
current.postMessage({ id, directory, args, env: { ...process.env }, ...options });
|
|
71
81
|
}
|
|
72
82
|
catch (error) {
|
|
73
83
|
pending.delete(id);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-process.js","sourceRoot":"","sources":["../../src/workspaces/git-process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"git-process.js","sourceRoot":"","sources":["../../src/workspaces/git-process.ts"],"names":[],"mappings":"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","sourcesContent":["import { Worker } from \"node:worker_threads\"\n\ninterface GitProcessOptions { timeout?: number; maxBuffer?: number; priority?: \"foreground\" | \"background\" }\ninterface GitRequest extends GitProcessOptions { id: number; directory: string; args: string[]; env: NodeJS.ProcessEnv }\ninterface GitResponse { id: number; stdout: string; stderr: string; error?: { message: string; code?: string | number | null } }\n\n// Even asynchronous execFile spends synchronous time creating a Windows process.\n// Keep that work off the HTTP/SSE thread. The worker is self-contained so the same\n// entry works in compiled desktop resources and TypeScript test runners.\nfunction workerMain() {\n const { parentPort } = require(\"node:worker_threads\") as typeof import(\"node:worker_threads\")\n const { execFile } = require(\"node:child_process\") as typeof import(\"node:child_process\")\n const queue: GitRequest[] = []\n const foreground: GitRequest[] = []\n let running = 0\n const scheduler = { pump() {\n while (running < 2 && (foreground.length || queue.length)) {\n const request = (foreground.shift() ?? queue.shift())!\n running += 1\n execFile(\"git\", [\"-C\", request.directory, ...request.args], {\n encoding: \"utf8\", windowsHide: true, maxBuffer: request.maxBuffer ?? 1024 * 1024,\n env: request.env, timeout: request.timeout,\n }, (error, stdout, stderr) => {\n parentPort!.postMessage({ id: request.id, stdout, stderr,\n ...(error ? { error: { message: error.message, code: error.code } } : {}),\n } satisfies GitResponse)\n running -= 1\n scheduler.pump()\n })\n }\n } }\n parentPort!.on(\"message\", (request: GitRequest) => {\n const target = request.priority === \"foreground\" ? foreground : queue\n target.push(request)\n scheduler.pump()\n })\n}\n\nlet worker: Worker | undefined\nlet sequence = 0\nconst pending = new Map<number, { resolve: (stdout: string) => void; reject: (error: Error) => void }>()\n\nfunction getWorker(): Worker {\n if (worker) return worker\n const created = new Worker(`(${workerMain.toString()})()`, { eval: true, execArgv: [] })\n worker = created\n const failed = (error: Error) => {\n if (worker !== created) return\n worker = undefined\n for (const task of pending.values()) task.reject(error)\n pending.clear()\n }\n created.on(\"error\", failed)\n created.on(\"exit\", code => failed(new Error(`Git process worker exited (${code})`)))\n created.on(\"message\", (response: GitResponse) => {\n const task = pending.get(response.id)\n if (!task) return\n pending.delete(response.id)\n if (response.error) task.reject(Object.assign(new Error(response.error.message), {\n code: response.error.code, stdout: response.stdout, stderr: response.stderr,\n }))\n else task.resolve(response.stdout)\n if (pending.size === 0) created.unref()\n })\n created.unref()\n return created\n}\n\nexport function runWorktreeGit(directory: string, args: string[], timeout?: number): Promise<string> {\n // Ownership and inventory reads must not wait behind an entire display batch.\n return runGitProcess(directory, args, { timeout, priority: \"foreground\" }).then(stdout => stdout.replace(/\\r?\\n$/, \"\"))\n}\n\n// Content/diff readers need exact bytes decoded as UTF-8, including the final newline.\nexport function runGitProcess(directory: string, args: string[], options: GitProcessOptions = {}): Promise<string> {\n return new Promise((resolve, reject) => {\n const current = getWorker()\n const id = ++sequence\n pending.set(id, { resolve, reject })\n current.ref()\n try {\n current.postMessage({ id, directory, args, env: { ...process.env }, ...options } satisfies GitRequest)\n } catch (error) {\n pending.delete(id)\n if (pending.size === 0) current.unref()\n reject(error)\n }\n })\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { spawn } from "child_process";
|
|
2
1
|
import { readFile, realpath } from "fs/promises";
|
|
3
2
|
import path from "path";
|
|
4
3
|
import { normalizeGitWorktreeRelativePath } from "./git-mutations";
|
|
4
|
+
import { runGitProcess } from "./git-process";
|
|
5
5
|
const gitStatusGenerations = new Map();
|
|
6
6
|
const gitStatusRequests = new Map();
|
|
7
7
|
async function readFileAsDiffText(filePath) {
|
|
@@ -14,30 +14,21 @@ async function readGitBlobAsDiffText(resultPromise, missingOk = false) {
|
|
|
14
14
|
}
|
|
15
15
|
return result.stdout;
|
|
16
16
|
}
|
|
17
|
-
function runGit(args, cwd, acceptedExitCodes = [0]) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (acceptedExitCodes.includes(code ?? 0)) {
|
|
33
|
-
resolve({ ok: true, stdout });
|
|
34
|
-
}
|
|
35
|
-
else {
|
|
36
|
-
const error = new Error(stderr.trim() || `git ${args.join(" ")} failed with code ${code}`);
|
|
37
|
-
resolve({ ok: false, error, stdout, stderr });
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
});
|
|
17
|
+
async function runGit(args, cwd, acceptedExitCodes = [0]) {
|
|
18
|
+
try {
|
|
19
|
+
// Preserve the previous streaming reader's unrestricted content size. Process
|
|
20
|
+
// creation belongs to the worker, including per-untracked-file numstat reads.
|
|
21
|
+
return { ok: true, stdout: await runGitProcess(cwd, args, { maxBuffer: Infinity }) };
|
|
22
|
+
}
|
|
23
|
+
catch (cause) {
|
|
24
|
+
const result = cause;
|
|
25
|
+
const stdout = result.stdout ?? "", stderr = result.stderr ?? "";
|
|
26
|
+
if (typeof result.code === "number" && acceptedExitCodes.includes(result.code))
|
|
27
|
+
return { ok: true, stdout };
|
|
28
|
+
const error = typeof result.code === "number"
|
|
29
|
+
? new Error(stderr.trim() || `git ${args.join(" ")} failed with code ${result.code}`) : result;
|
|
30
|
+
return { ok: false, error, stdout, stderr };
|
|
31
|
+
}
|
|
41
32
|
}
|
|
42
33
|
function ensureEntry(map, path) {
|
|
43
34
|
const existing = map.get(path);
|
|
@@ -137,20 +128,23 @@ async function getUntrackedFileNumstat(workspaceFolder, relativePath) {
|
|
|
137
128
|
return { additions: parsed.additions, deletions: parsed.deletions };
|
|
138
129
|
}
|
|
139
130
|
async function applyUntrackedFileStats(map, workspaceFolder) {
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
131
|
+
const entries = Array.from(map.values()).filter((entry) => entry.unstagedStatus === "untracked");
|
|
132
|
+
const pending = entries.values();
|
|
133
|
+
// Bound submission as well as execution: a large untracked directory must not
|
|
134
|
+
// fill the shared worker mailbox ahead of worktree authorization requests.
|
|
135
|
+
await Promise.all(Array.from({ length: Math.min(2, entries.length) }, async () => {
|
|
136
|
+
for (const entry of pending) {
|
|
137
|
+
try {
|
|
138
|
+
const stats = await getUntrackedFileNumstat(workspaceFolder, entry.path);
|
|
139
|
+
entry.unstagedAdditions = stats.additions;
|
|
140
|
+
entry.unstagedDeletions = stats.deletions;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
entry.unstagedAdditions = 0;
|
|
144
|
+
entry.unstagedDeletions = 0;
|
|
145
|
+
}
|
|
151
146
|
}
|
|
152
|
-
});
|
|
153
|
-
await Promise.all(pending);
|
|
147
|
+
}));
|
|
154
148
|
}
|
|
155
149
|
function applyNumstatOutput(map, output, target) {
|
|
156
150
|
const tokens = output.split("\0");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-status.js","sourceRoot":"","sources":["../../src/workspaces/git-status.ts"],"names":[],"mappings":"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","sourcesContent":["import { spawn } from \"child_process\"\nimport { readFile, realpath } from \"fs/promises\"\nimport path from \"path\"\n\nimport type { GitChangeKind, WorktreeGitDiffResponse, WorktreeGitDiffScope, WorktreeGitStatusEntry } from \"../api-types\"\nimport type { LogLike } from \"./git-worktrees\"\nimport { normalizeGitWorktreeRelativePath } from \"./git-mutations\"\n\ntype GitResult = { ok: true; stdout: string } | { ok: false; error: Error; stdout?: string; stderr?: string }\ntype GitSuccessResult = Extract<GitResult, { ok: true }>\ntype RunGit = typeof runGit\nconst gitStatusGenerations = new Map<string, number>()\nconst gitStatusRequests = new Map<string, { generation: number; request: Promise<WorktreeGitStatusEntry[]> }>()\n\nasync function readFileAsDiffText(filePath: string): Promise<string> {\n return readFile(filePath, \"utf-8\")\n}\n\nasync function readGitBlobAsDiffText(resultPromise: Promise<GitResult>, missingOk = false): Promise<string> {\n const result = await resultPromise\n if (!result.ok) {\n return decodeGitShowResult(result, missingOk)\n }\n return result.stdout\n}\n\nfunction runGit(args: string[], cwd: string, acceptedExitCodes: number[] = [0]): Promise<GitResult> {\n return new Promise((resolve) => {\n const child = spawn(\"git\", args, { cwd, stdio: [\"ignore\", \"pipe\", \"pipe\"] })\n let stdout = \"\"\n let stderr = \"\"\n\n child.stdout?.on(\"data\", (chunk) => {\n stdout += chunk.toString()\n })\n child.stderr?.on(\"data\", (chunk) => {\n stderr += chunk.toString()\n })\n child.once(\"error\", (error) => {\n resolve({ ok: false, error, stdout, stderr })\n })\n child.once(\"close\", (code) => {\n if (acceptedExitCodes.includes(code ?? 0)) {\n resolve({ ok: true, stdout })\n } else {\n const error = new Error(stderr.trim() || `git ${args.join(\" \")} failed with code ${code}`)\n resolve({ ok: false, error, stdout, stderr })\n }\n })\n })\n}\n\nfunction ensureEntry(map: Map<string, WorktreeGitStatusEntry>, path: string): WorktreeGitStatusEntry {\n const existing = map.get(path)\n if (existing) return existing\n const next: WorktreeGitStatusEntry = {\n path,\n originalPath: null,\n stagedStatus: null,\n stagedAdditions: 0,\n stagedDeletions: 0,\n unstagedStatus: null,\n unstagedAdditions: 0,\n unstagedDeletions: 0,\n }\n map.set(path, next)\n return next\n}\n\nfunction normalizeGitStatusPath(value: string): string {\n return value.trim().replace(/\\\\+/g, \"/\")\n}\n\nfunction parseGitChangeKind(code: string): GitChangeKind | null {\n const normalized = code.trim().toUpperCase()\n if (!normalized) return null\n if (normalized === \"A\") return \"added\"\n if (normalized === \"M\") return \"modified\"\n if (normalized === \"D\") return \"deleted\"\n if (normalized.startsWith(\"R\")) return \"renamed\"\n if (normalized.startsWith(\"C\")) return \"copied\"\n if (normalized === \"U\") return \"unmerged\"\n return null\n}\n\nfunction applyNameStatusOutput(\n map: Map<string, WorktreeGitStatusEntry>,\n output: string,\n target: \"stagedStatus\" | \"unstagedStatus\",\n) {\n const tokens = output.split(\"\\0\")\n let index = 0\n\n while (index < tokens.length) {\n const record = tokens[index++] ?? \"\"\n if (!record) continue\n\n const parts = record.split(\"\\t\")\n const statusCode = parseGitChangeKind(parts[0] ?? \"\")\n if (!statusCode) continue\n\n const inlinePath = parts.slice(1).join(\"\\t\")\n const firstPath = inlinePath || tokens[index++] || \"\"\n const secondPath = statusCode === \"renamed\" || statusCode === \"copied\" ? tokens[index++] || \"\" : \"\"\n const path = statusCode === \"renamed\" || statusCode === \"copied\" ? secondPath || firstPath : firstPath\n const normalizedPath = normalizeGitStatusPath(path)\n if (!normalizedPath) continue\n const entry = ensureEntry(map, normalizedPath)\n entry[target] = statusCode\n if (statusCode === \"renamed\" || statusCode === \"copied\") {\n const originalPath = normalizeGitStatusPath(firstPath)\n entry.originalPath = originalPath || entry.originalPath || null\n }\n }\n}\n\nfunction applyUntrackedOutput(map: Map<string, WorktreeGitStatusEntry>, output: string) {\n for (const rawLine of output.split(/\\r?\\n/)) {\n const path = normalizeGitStatusPath(rawLine)\n if (!path) continue\n ensureEntry(map, path).unstagedStatus = \"untracked\"\n }\n}\n\nfunction parseSingleNumstat(output: string): { additions: number; deletions: number; isBinary: boolean; found: boolean } {\n for (const rawLine of output.split(/\\r?\\n/)) {\n const line = rawLine.trim()\n if (!line) continue\n const parts = rawLine.split(\"\\t\")\n const isBinary = parts[0] === \"-\" || parts[1] === \"-\"\n return {\n additions: isBinary ? 0 : Number.parseInt(parts[0] ?? \"0\", 10) || 0,\n deletions: isBinary ? 0 : Number.parseInt(parts[1] ?? \"0\", 10) || 0,\n isBinary,\n found: true,\n }\n }\n\n return { additions: 0, deletions: 0, isBinary: false, found: false }\n}\n\nasync function getUntrackedFileNumstat(workspaceFolder: string, relativePath: string): Promise<{ additions: number; deletions: number }> {\n const absolutePath = path.join(workspaceFolder, relativePath)\n const result = await runGit([\"diff\", \"--numstat\", \"--no-index\", \"--\", \"/dev/null\", absolutePath], workspaceFolder, [0, 1])\n if (!result.ok) {\n throw result.error\n }\n\n const parsed = parseSingleNumstat(result.stdout)\n return { additions: parsed.additions, deletions: parsed.deletions }\n}\n\nasync function applyUntrackedFileStats(map: Map<string, WorktreeGitStatusEntry>, workspaceFolder: string) {\n const pending = Array.from(map.values())\n .filter((entry) => entry.unstagedStatus === \"untracked\")\n .map(async (entry) => {\n try {\n const stats = await getUntrackedFileNumstat(workspaceFolder, entry.path)\n entry.unstagedAdditions = stats.additions\n entry.unstagedDeletions = stats.deletions\n } catch {\n entry.unstagedAdditions = 0\n entry.unstagedDeletions = 0\n }\n })\n await Promise.all(pending)\n}\n\nfunction applyNumstatOutput(\n map: Map<string, WorktreeGitStatusEntry>,\n output: string,\n target: \"staged\" | \"unstaged\",\n) {\n const tokens = output.split(\"\\0\")\n let index = 0\n\n while (index < tokens.length) {\n const record = tokens[index++] ?? \"\"\n if (!record) continue\n\n const parts = record.split(\"\\t\")\n if (parts.length < 3) continue\n\n const additions = parts[0] === \"-\" ? 0 : Number.parseInt(parts[0] ?? \"0\", 10)\n const deletions = parts[1] === \"-\" ? 0 : Number.parseInt(parts[1] ?? \"0\", 10)\n const inlinePath = parts.slice(2).join(\"\\t\")\n const isRenameLike = inlinePath === \"\"\n const originalPath = isRenameLike ? normalizeGitStatusPath(tokens[index++] ?? \"\") : null\n const normalizedPath = normalizeGitStatusPath(isRenameLike ? tokens[index++] ?? \"\" : inlinePath)\n if (!normalizedPath) continue\n\n const entry = ensureEntry(map, normalizedPath)\n if (originalPath) {\n entry.originalPath = originalPath\n }\n\n if (target === \"staged\") {\n entry.stagedAdditions = Number.isFinite(additions) ? additions : 0\n entry.stagedDeletions = Number.isFinite(deletions) ? deletions : 0\n } else {\n entry.unstagedAdditions = Number.isFinite(additions) ? additions : 0\n entry.unstagedDeletions = Number.isFinite(deletions) ? deletions : 0\n }\n }\n}\n\nexport async function getWorktreeGitStatus(params: {\n workspaceFolder: string\n logger?: LogLike\n}, run: RunGit = runGit): Promise<WorktreeGitStatusEntry[]> {\n const workspaceFolder = await realpath(params.workspaceFolder)\n const generation = gitStatusGenerations.get(workspaceFolder) ?? 0\n const pending = gitStatusRequests.get(workspaceFolder)\n if (pending?.generation === generation) return pending.request\n\n const request = readWorktreeGitStatus({ ...params, workspaceFolder }, run).finally(() => {\n if (gitStatusRequests.get(workspaceFolder)?.request === request) gitStatusRequests.delete(workspaceFolder)\n if (!gitStatusRequests.has(workspaceFolder)) gitStatusGenerations.delete(workspaceFolder)\n })\n gitStatusRequests.set(workspaceFolder, { generation, request })\n return request\n}\n\nexport async function invalidateWorktreeGitStatus(workspaceFolder: string): Promise<void> {\n const canonicalDirectory = await realpath(workspaceFolder)\n gitStatusGenerations.set(canonicalDirectory, (gitStatusGenerations.get(canonicalDirectory) ?? 0) + 1)\n}\n\nasync function readWorktreeGitStatus(params: {\n workspaceFolder: string\n logger?: LogLike\n}, run: RunGit): Promise<WorktreeGitStatusEntry[]> {\n const { workspaceFolder, logger } = params\n const [stagedResult, unstagedResult, untrackedResult, stagedNumstatResult, unstagedNumstatResult] = await Promise.all([\n run([\"diff\", \"--name-status\", \"-z\", \"--cached\", \"--find-renames\", \"--find-copies\"], workspaceFolder),\n run([\"diff\", \"--name-status\", \"-z\", \"--find-renames\", \"--find-copies\"], workspaceFolder),\n run([\"ls-files\", \"--others\", \"--exclude-standard\"], workspaceFolder),\n run([\"diff\", \"--numstat\", \"-z\", \"--cached\", \"--find-renames\", \"--find-copies\"], workspaceFolder),\n run([\"diff\", \"--numstat\", \"-z\", \"--find-renames\", \"--find-copies\"], workspaceFolder),\n ])\n\n for (const result of [stagedResult, unstagedResult, untrackedResult, stagedNumstatResult, unstagedNumstatResult]) {\n if (!result.ok) {\n logger?.warn?.({ workspaceFolder, err: result.error }, \"Failed to read git status for worktree\")\n throw result.error\n }\n }\n\n const stagedOutput = (stagedResult as GitSuccessResult).stdout\n const unstagedOutput = (unstagedResult as GitSuccessResult).stdout\n const untrackedOutput = (untrackedResult as GitSuccessResult).stdout\n const stagedNumstatOutput = (stagedNumstatResult as GitSuccessResult).stdout\n const unstagedNumstatOutput = (unstagedNumstatResult as GitSuccessResult).stdout\n\n const entries = new Map<string, WorktreeGitStatusEntry>()\n applyNameStatusOutput(entries, stagedOutput, \"stagedStatus\")\n applyNameStatusOutput(entries, unstagedOutput, \"unstagedStatus\")\n applyUntrackedOutput(entries, untrackedOutput)\n applyNumstatOutput(entries, stagedNumstatOutput, \"staged\")\n applyNumstatOutput(entries, unstagedNumstatOutput, \"unstaged\")\n await applyUntrackedFileStats(entries, workspaceFolder)\n\n return Array.from(entries.values()).sort((a, b) => a.path.localeCompare(b.path))\n}\n\nfunction decodeGitShowResult(result: GitResult, missingOk = false): string {\n if (result.ok) return result.stdout\n const message = result.stderr?.trim() || result.error.message || \"\"\n if (\n missingOk &&\n (message.includes(\"exists on disk, but not in\") ||\n message.includes(\"Path '\") ||\n message.includes(\"does not exist\") ||\n message.includes(\"unknown revision or path not in the working tree\"))\n ) {\n return \"\"\n }\n throw result.error\n}\n\nasync function readGitIndexBlob(workspaceFolder: string, normalizedPath: string): Promise<GitResult> {\n return runGit([\"cat-file\", \"-p\", `:${normalizedPath}`], workspaceFolder)\n}\n\nasync function getTrackedDiffMetadata(params: {\n workspaceFolder: string\n scope: WorktreeGitDiffScope\n normalizedPath: string\n normalizedOriginalPath: string | null\n}): Promise<{ isBinary: boolean; found: boolean }> {\n const args = [\"diff\", \"--numstat\"]\n if (params.scope === \"staged\") {\n args.push(\"--cached\")\n }\n args.push(\"--find-renames\", \"--find-copies\", \"--\")\n args.push(params.normalizedPath)\n if (params.normalizedOriginalPath && params.normalizedOriginalPath !== params.normalizedPath) {\n args.push(params.normalizedOriginalPath)\n }\n\n const result = await runGit(args, params.workspaceFolder)\n if (!result.ok) {\n throw result.error\n }\n\n const parsed = parseSingleNumstat(result.stdout)\n return { isBinary: parsed.isBinary, found: parsed.found }\n}\n\nasync function getUntrackedDiffMetadata(params: {\n workspaceFolder: string\n normalizedPath: string\n}): Promise<{ isBinary: boolean }> {\n const absolutePath = path.join(params.workspaceFolder, params.normalizedPath)\n const result = await runGit([\"diff\", \"--numstat\", \"--no-index\", \"--\", \"/dev/null\", absolutePath], params.workspaceFolder, [0, 1])\n if (!result.ok) {\n throw result.error\n }\n\n return { isBinary: parseSingleNumstat(result.stdout).isBinary }\n}\n\nasync function resolveUnstagedBeforePath(params: {\n workspaceFolder: string\n normalizedPath: string\n normalizedOriginalPath: string | null\n}): Promise<GitResult> {\n const currentPathResult = await readGitIndexBlob(params.workspaceFolder, params.normalizedPath)\n if (currentPathResult.ok || !params.normalizedOriginalPath || params.normalizedOriginalPath === params.normalizedPath) {\n return currentPathResult\n }\n return readGitIndexBlob(params.workspaceFolder, params.normalizedOriginalPath)\n}\n\nexport async function getWorktreeGitDiff(params: {\n workspaceFolder: string\n path: string\n originalPath?: string | null\n scope: WorktreeGitDiffScope\n}): Promise<WorktreeGitDiffResponse> {\n const normalizedPath = normalizeGitWorktreeRelativePath(params.path)\n const normalizedOriginalPath = params.originalPath ? normalizeGitWorktreeRelativePath(params.originalPath) : null\n\n const trackedMetadata = await getTrackedDiffMetadata({\n workspaceFolder: params.workspaceFolder,\n scope: params.scope,\n normalizedPath,\n normalizedOriginalPath,\n })\n\n const diffMetadata =\n params.scope === \"unstaged\" && !trackedMetadata.found\n ? await getUntrackedDiffMetadata({\n workspaceFolder: params.workspaceFolder,\n normalizedPath,\n })\n : trackedMetadata\n\n if (diffMetadata.isBinary) {\n return {\n path: normalizedPath,\n originalPath: normalizedOriginalPath,\n scope: params.scope,\n before: \"\",\n after: \"\",\n isBinary: true,\n }\n }\n\n if (params.scope === \"staged\") {\n const [beforeResult, afterResult] = await Promise.all([\n readGitBlobAsDiffText(runGit([\"show\", `HEAD:${normalizedOriginalPath ?? normalizedPath}`], params.workspaceFolder), true),\n readGitBlobAsDiffText(readGitIndexBlob(params.workspaceFolder, normalizedPath), true),\n ])\n\n return {\n path: normalizedPath,\n originalPath: normalizedOriginalPath,\n scope: params.scope,\n before: beforeResult,\n after: afterResult,\n isBinary: false,\n }\n }\n\n const indexResult = await resolveUnstagedBeforePath({\n workspaceFolder: params.workspaceFolder,\n normalizedPath,\n normalizedOriginalPath,\n })\n\n const beforeResult = await readGitBlobAsDiffText(Promise.resolve(indexResult), true)\n let after = beforeResult\n\n const fsPath = path.join(params.workspaceFolder, normalizedPath)\n try {\n after = await readFileAsDiffText(fsPath)\n } catch {\n after = \"\"\n }\n\n return {\n path: normalizedPath,\n originalPath: normalizedOriginalPath,\n scope: params.scope,\n before: beforeResult,\n after,\n isBinary: false,\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"git-status.js","sourceRoot":"","sources":["../../src/workspaces/git-status.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile, realpath } from \"fs/promises\"\nimport path from \"path\"\n\nimport type { GitChangeKind, WorktreeGitDiffResponse, WorktreeGitDiffScope, WorktreeGitStatusEntry } from \"../api-types\"\nimport type { LogLike } from \"./git-worktrees\"\nimport { normalizeGitWorktreeRelativePath } from \"./git-mutations\"\nimport { runGitProcess } from \"./git-process\"\n\ntype GitResult = { ok: true; stdout: string } | { ok: false; error: Error; stdout?: string; stderr?: string }\ntype GitSuccessResult = Extract<GitResult, { ok: true }>\ntype RunGit = typeof runGit\nconst gitStatusGenerations = new Map<string, number>()\nconst gitStatusRequests = new Map<string, { generation: number; request: Promise<WorktreeGitStatusEntry[]> }>()\n\nasync function readFileAsDiffText(filePath: string): Promise<string> {\n return readFile(filePath, \"utf-8\")\n}\n\nasync function readGitBlobAsDiffText(resultPromise: Promise<GitResult>, missingOk = false): Promise<string> {\n const result = await resultPromise\n if (!result.ok) {\n return decodeGitShowResult(result, missingOk)\n }\n return result.stdout\n}\n\nasync function runGit(args: string[], cwd: string, acceptedExitCodes: number[] = [0]): Promise<GitResult> {\n try {\n // Preserve the previous streaming reader's unrestricted content size. Process\n // creation belongs to the worker, including per-untracked-file numstat reads.\n return { ok: true, stdout: await runGitProcess(cwd, args, { maxBuffer: Infinity }) }\n } catch (cause) {\n const result = cause as Error & { code?: string | number; stdout?: string; stderr?: string }\n const stdout = result.stdout ?? \"\", stderr = result.stderr ?? \"\"\n if (typeof result.code === \"number\" && acceptedExitCodes.includes(result.code)) return { ok: true, stdout }\n const error = typeof result.code === \"number\"\n ? new Error(stderr.trim() || `git ${args.join(\" \")} failed with code ${result.code}`) : result\n return { ok: false, error, stdout, stderr }\n }\n}\n\nfunction ensureEntry(map: Map<string, WorktreeGitStatusEntry>, path: string): WorktreeGitStatusEntry {\n const existing = map.get(path)\n if (existing) return existing\n const next: WorktreeGitStatusEntry = {\n path,\n originalPath: null,\n stagedStatus: null,\n stagedAdditions: 0,\n stagedDeletions: 0,\n unstagedStatus: null,\n unstagedAdditions: 0,\n unstagedDeletions: 0,\n }\n map.set(path, next)\n return next\n}\n\nfunction normalizeGitStatusPath(value: string): string {\n return value.trim().replace(/\\\\+/g, \"/\")\n}\n\nfunction parseGitChangeKind(code: string): GitChangeKind | null {\n const normalized = code.trim().toUpperCase()\n if (!normalized) return null\n if (normalized === \"A\") return \"added\"\n if (normalized === \"M\") return \"modified\"\n if (normalized === \"D\") return \"deleted\"\n if (normalized.startsWith(\"R\")) return \"renamed\"\n if (normalized.startsWith(\"C\")) return \"copied\"\n if (normalized === \"U\") return \"unmerged\"\n return null\n}\n\nfunction applyNameStatusOutput(\n map: Map<string, WorktreeGitStatusEntry>,\n output: string,\n target: \"stagedStatus\" | \"unstagedStatus\",\n) {\n const tokens = output.split(\"\\0\")\n let index = 0\n\n while (index < tokens.length) {\n const record = tokens[index++] ?? \"\"\n if (!record) continue\n\n const parts = record.split(\"\\t\")\n const statusCode = parseGitChangeKind(parts[0] ?? \"\")\n if (!statusCode) continue\n\n const inlinePath = parts.slice(1).join(\"\\t\")\n const firstPath = inlinePath || tokens[index++] || \"\"\n const secondPath = statusCode === \"renamed\" || statusCode === \"copied\" ? tokens[index++] || \"\" : \"\"\n const path = statusCode === \"renamed\" || statusCode === \"copied\" ? secondPath || firstPath : firstPath\n const normalizedPath = normalizeGitStatusPath(path)\n if (!normalizedPath) continue\n const entry = ensureEntry(map, normalizedPath)\n entry[target] = statusCode\n if (statusCode === \"renamed\" || statusCode === \"copied\") {\n const originalPath = normalizeGitStatusPath(firstPath)\n entry.originalPath = originalPath || entry.originalPath || null\n }\n }\n}\n\nfunction applyUntrackedOutput(map: Map<string, WorktreeGitStatusEntry>, output: string) {\n for (const rawLine of output.split(/\\r?\\n/)) {\n const path = normalizeGitStatusPath(rawLine)\n if (!path) continue\n ensureEntry(map, path).unstagedStatus = \"untracked\"\n }\n}\n\nfunction parseSingleNumstat(output: string): { additions: number; deletions: number; isBinary: boolean; found: boolean } {\n for (const rawLine of output.split(/\\r?\\n/)) {\n const line = rawLine.trim()\n if (!line) continue\n const parts = rawLine.split(\"\\t\")\n const isBinary = parts[0] === \"-\" || parts[1] === \"-\"\n return {\n additions: isBinary ? 0 : Number.parseInt(parts[0] ?? \"0\", 10) || 0,\n deletions: isBinary ? 0 : Number.parseInt(parts[1] ?? \"0\", 10) || 0,\n isBinary,\n found: true,\n }\n }\n\n return { additions: 0, deletions: 0, isBinary: false, found: false }\n}\n\nasync function getUntrackedFileNumstat(workspaceFolder: string, relativePath: string): Promise<{ additions: number; deletions: number }> {\n const absolutePath = path.join(workspaceFolder, relativePath)\n const result = await runGit([\"diff\", \"--numstat\", \"--no-index\", \"--\", \"/dev/null\", absolutePath], workspaceFolder, [0, 1])\n if (!result.ok) {\n throw result.error\n }\n\n const parsed = parseSingleNumstat(result.stdout)\n return { additions: parsed.additions, deletions: parsed.deletions }\n}\n\nasync function applyUntrackedFileStats(map: Map<string, WorktreeGitStatusEntry>, workspaceFolder: string) {\n const entries = Array.from(map.values()).filter((entry) => entry.unstagedStatus === \"untracked\")\n const pending = entries.values()\n // Bound submission as well as execution: a large untracked directory must not\n // fill the shared worker mailbox ahead of worktree authorization requests.\n await Promise.all(Array.from({ length: Math.min(2, entries.length) }, async () => {\n for (const entry of pending) {\n try {\n const stats = await getUntrackedFileNumstat(workspaceFolder, entry.path)\n entry.unstagedAdditions = stats.additions\n entry.unstagedDeletions = stats.deletions\n } catch {\n entry.unstagedAdditions = 0\n entry.unstagedDeletions = 0\n }\n }\n }))\n}\n\nfunction applyNumstatOutput(\n map: Map<string, WorktreeGitStatusEntry>,\n output: string,\n target: \"staged\" | \"unstaged\",\n) {\n const tokens = output.split(\"\\0\")\n let index = 0\n\n while (index < tokens.length) {\n const record = tokens[index++] ?? \"\"\n if (!record) continue\n\n const parts = record.split(\"\\t\")\n if (parts.length < 3) continue\n\n const additions = parts[0] === \"-\" ? 0 : Number.parseInt(parts[0] ?? \"0\", 10)\n const deletions = parts[1] === \"-\" ? 0 : Number.parseInt(parts[1] ?? \"0\", 10)\n const inlinePath = parts.slice(2).join(\"\\t\")\n const isRenameLike = inlinePath === \"\"\n const originalPath = isRenameLike ? normalizeGitStatusPath(tokens[index++] ?? \"\") : null\n const normalizedPath = normalizeGitStatusPath(isRenameLike ? tokens[index++] ?? \"\" : inlinePath)\n if (!normalizedPath) continue\n\n const entry = ensureEntry(map, normalizedPath)\n if (originalPath) {\n entry.originalPath = originalPath\n }\n\n if (target === \"staged\") {\n entry.stagedAdditions = Number.isFinite(additions) ? additions : 0\n entry.stagedDeletions = Number.isFinite(deletions) ? deletions : 0\n } else {\n entry.unstagedAdditions = Number.isFinite(additions) ? additions : 0\n entry.unstagedDeletions = Number.isFinite(deletions) ? deletions : 0\n }\n }\n}\n\nexport async function getWorktreeGitStatus(params: {\n workspaceFolder: string\n logger?: LogLike\n}, run: RunGit = runGit): Promise<WorktreeGitStatusEntry[]> {\n const workspaceFolder = await realpath(params.workspaceFolder)\n const generation = gitStatusGenerations.get(workspaceFolder) ?? 0\n const pending = gitStatusRequests.get(workspaceFolder)\n if (pending?.generation === generation) return pending.request\n\n const request = readWorktreeGitStatus({ ...params, workspaceFolder }, run).finally(() => {\n if (gitStatusRequests.get(workspaceFolder)?.request === request) gitStatusRequests.delete(workspaceFolder)\n if (!gitStatusRequests.has(workspaceFolder)) gitStatusGenerations.delete(workspaceFolder)\n })\n gitStatusRequests.set(workspaceFolder, { generation, request })\n return request\n}\n\nexport async function invalidateWorktreeGitStatus(workspaceFolder: string): Promise<void> {\n const canonicalDirectory = await realpath(workspaceFolder)\n gitStatusGenerations.set(canonicalDirectory, (gitStatusGenerations.get(canonicalDirectory) ?? 0) + 1)\n}\n\nasync function readWorktreeGitStatus(params: {\n workspaceFolder: string\n logger?: LogLike\n}, run: RunGit): Promise<WorktreeGitStatusEntry[]> {\n const { workspaceFolder, logger } = params\n const [stagedResult, unstagedResult, untrackedResult, stagedNumstatResult, unstagedNumstatResult] = await Promise.all([\n run([\"diff\", \"--name-status\", \"-z\", \"--cached\", \"--find-renames\", \"--find-copies\"], workspaceFolder),\n run([\"diff\", \"--name-status\", \"-z\", \"--find-renames\", \"--find-copies\"], workspaceFolder),\n run([\"ls-files\", \"--others\", \"--exclude-standard\"], workspaceFolder),\n run([\"diff\", \"--numstat\", \"-z\", \"--cached\", \"--find-renames\", \"--find-copies\"], workspaceFolder),\n run([\"diff\", \"--numstat\", \"-z\", \"--find-renames\", \"--find-copies\"], workspaceFolder),\n ])\n\n for (const result of [stagedResult, unstagedResult, untrackedResult, stagedNumstatResult, unstagedNumstatResult]) {\n if (!result.ok) {\n logger?.warn?.({ workspaceFolder, err: result.error }, \"Failed to read git status for worktree\")\n throw result.error\n }\n }\n\n const stagedOutput = (stagedResult as GitSuccessResult).stdout\n const unstagedOutput = (unstagedResult as GitSuccessResult).stdout\n const untrackedOutput = (untrackedResult as GitSuccessResult).stdout\n const stagedNumstatOutput = (stagedNumstatResult as GitSuccessResult).stdout\n const unstagedNumstatOutput = (unstagedNumstatResult as GitSuccessResult).stdout\n\n const entries = new Map<string, WorktreeGitStatusEntry>()\n applyNameStatusOutput(entries, stagedOutput, \"stagedStatus\")\n applyNameStatusOutput(entries, unstagedOutput, \"unstagedStatus\")\n applyUntrackedOutput(entries, untrackedOutput)\n applyNumstatOutput(entries, stagedNumstatOutput, \"staged\")\n applyNumstatOutput(entries, unstagedNumstatOutput, \"unstaged\")\n await applyUntrackedFileStats(entries, workspaceFolder)\n\n return Array.from(entries.values()).sort((a, b) => a.path.localeCompare(b.path))\n}\n\nfunction decodeGitShowResult(result: GitResult, missingOk = false): string {\n if (result.ok) return result.stdout\n const message = result.stderr?.trim() || result.error.message || \"\"\n if (\n missingOk &&\n (message.includes(\"exists on disk, but not in\") ||\n message.includes(\"Path '\") ||\n message.includes(\"does not exist\") ||\n message.includes(\"unknown revision or path not in the working tree\"))\n ) {\n return \"\"\n }\n throw result.error\n}\n\nasync function readGitIndexBlob(workspaceFolder: string, normalizedPath: string): Promise<GitResult> {\n return runGit([\"cat-file\", \"-p\", `:${normalizedPath}`], workspaceFolder)\n}\n\nasync function getTrackedDiffMetadata(params: {\n workspaceFolder: string\n scope: WorktreeGitDiffScope\n normalizedPath: string\n normalizedOriginalPath: string | null\n}): Promise<{ isBinary: boolean; found: boolean }> {\n const args = [\"diff\", \"--numstat\"]\n if (params.scope === \"staged\") {\n args.push(\"--cached\")\n }\n args.push(\"--find-renames\", \"--find-copies\", \"--\")\n args.push(params.normalizedPath)\n if (params.normalizedOriginalPath && params.normalizedOriginalPath !== params.normalizedPath) {\n args.push(params.normalizedOriginalPath)\n }\n\n const result = await runGit(args, params.workspaceFolder)\n if (!result.ok) {\n throw result.error\n }\n\n const parsed = parseSingleNumstat(result.stdout)\n return { isBinary: parsed.isBinary, found: parsed.found }\n}\n\nasync function getUntrackedDiffMetadata(params: {\n workspaceFolder: string\n normalizedPath: string\n}): Promise<{ isBinary: boolean }> {\n const absolutePath = path.join(params.workspaceFolder, params.normalizedPath)\n const result = await runGit([\"diff\", \"--numstat\", \"--no-index\", \"--\", \"/dev/null\", absolutePath], params.workspaceFolder, [0, 1])\n if (!result.ok) {\n throw result.error\n }\n\n return { isBinary: parseSingleNumstat(result.stdout).isBinary }\n}\n\nasync function resolveUnstagedBeforePath(params: {\n workspaceFolder: string\n normalizedPath: string\n normalizedOriginalPath: string | null\n}): Promise<GitResult> {\n const currentPathResult = await readGitIndexBlob(params.workspaceFolder, params.normalizedPath)\n if (currentPathResult.ok || !params.normalizedOriginalPath || params.normalizedOriginalPath === params.normalizedPath) {\n return currentPathResult\n }\n return readGitIndexBlob(params.workspaceFolder, params.normalizedOriginalPath)\n}\n\nexport async function getWorktreeGitDiff(params: {\n workspaceFolder: string\n path: string\n originalPath?: string | null\n scope: WorktreeGitDiffScope\n}): Promise<WorktreeGitDiffResponse> {\n const normalizedPath = normalizeGitWorktreeRelativePath(params.path)\n const normalizedOriginalPath = params.originalPath ? normalizeGitWorktreeRelativePath(params.originalPath) : null\n\n const trackedMetadata = await getTrackedDiffMetadata({\n workspaceFolder: params.workspaceFolder,\n scope: params.scope,\n normalizedPath,\n normalizedOriginalPath,\n })\n\n const diffMetadata =\n params.scope === \"unstaged\" && !trackedMetadata.found\n ? await getUntrackedDiffMetadata({\n workspaceFolder: params.workspaceFolder,\n normalizedPath,\n })\n : trackedMetadata\n\n if (diffMetadata.isBinary) {\n return {\n path: normalizedPath,\n originalPath: normalizedOriginalPath,\n scope: params.scope,\n before: \"\",\n after: \"\",\n isBinary: true,\n }\n }\n\n if (params.scope === \"staged\") {\n const [beforeResult, afterResult] = await Promise.all([\n readGitBlobAsDiffText(runGit([\"show\", `HEAD:${normalizedOriginalPath ?? normalizedPath}`], params.workspaceFolder), true),\n readGitBlobAsDiffText(readGitIndexBlob(params.workspaceFolder, normalizedPath), true),\n ])\n\n return {\n path: normalizedPath,\n originalPath: normalizedOriginalPath,\n scope: params.scope,\n before: beforeResult,\n after: afterResult,\n isBinary: false,\n }\n }\n\n const indexResult = await resolveUnstagedBeforePath({\n workspaceFolder: params.workspaceFolder,\n normalizedPath,\n normalizedOriginalPath,\n })\n\n const beforeResult = await readGitBlobAsDiffText(Promise.resolve(indexResult), true)\n let after = beforeResult\n\n const fsPath = path.join(params.workspaceFolder, normalizedPath)\n try {\n after = await readFileAsDiffText(fsPath)\n } catch {\n after = \"\"\n }\n\n return {\n path: normalizedPath,\n originalPath: normalizedOriginalPath,\n scope: params.scope,\n before: beforeResult,\n after,\n isBinary: false,\n }\n}\n"]}
|
|
@@ -4,9 +4,36 @@ import fs from "node:fs/promises";
|
|
|
4
4
|
import { tmpdir } from "node:os";
|
|
5
5
|
import { syncBuiltinESMExports } from "node:module";
|
|
6
6
|
import path from "node:path";
|
|
7
|
+
import { execFileSync } from "node:child_process";
|
|
7
8
|
import { describe, it } from "node:test";
|
|
8
|
-
import { getWorktreeGitStatus, invalidateWorktreeGitStatus } from "./git-status";
|
|
9
|
+
import { getWorktreeGitDiff, getWorktreeGitStatus, invalidateWorktreeGitStatus } from "./git-status";
|
|
9
10
|
describe("worktree git status singleflight", () => {
|
|
11
|
+
it("reads real Git status and large UTF-8 blobs through the worker without losing final newlines", async () => {
|
|
12
|
+
const directory = await mkdtemp(path.join(tmpdir(), "codenomad-git-content-"));
|
|
13
|
+
const git = (...args) => execFileSync("git", ["-C", directory, ...args], { stdio: "pipe" });
|
|
14
|
+
try {
|
|
15
|
+
git("init");
|
|
16
|
+
git("config", "user.name", "Fixture");
|
|
17
|
+
git("config", "user.email", "fixture@example.test");
|
|
18
|
+
git("config", "core.autocrlf", "false");
|
|
19
|
+
const before = "é漢字 — content\n".repeat(100_000);
|
|
20
|
+
await fs.writeFile(path.join(directory, "large.txt"), before);
|
|
21
|
+
git("add", ".");
|
|
22
|
+
git("commit", "-m", "fixture");
|
|
23
|
+
await fs.writeFile(path.join(directory, "large.txt"), before + "changed\n");
|
|
24
|
+
await fs.writeFile(path.join(directory, "untracked.txt"), "one\ntwo\n");
|
|
25
|
+
const status = await getWorktreeGitStatus({ workspaceFolder: directory });
|
|
26
|
+
assert.equal(status.find(entry => entry.path === "untracked.txt")?.unstagedAdditions, 2, "git diff --no-index exit 1 is a successful numstat result");
|
|
27
|
+
const diff = await getWorktreeGitDiff({ workspaceFolder: directory, path: "large.txt", scope: "unstaged" });
|
|
28
|
+
assert.equal(diff.before, before);
|
|
29
|
+
assert.equal(diff.after, before + "changed\n");
|
|
30
|
+
await assert.rejects(getWorktreeGitStatus({ workspaceFolder: path.join(directory, "missing") }));
|
|
31
|
+
assert.equal((await getWorktreeGitStatus({ workspaceFolder: directory })).length, 2);
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
await rm(directory, { recursive: true, force: true });
|
|
35
|
+
}
|
|
36
|
+
});
|
|
10
37
|
it("coalesces concurrent requests and runs again after settlement", async (t) => {
|
|
11
38
|
const directory = await mkdtemp(path.join(tmpdir(), "codenomad-git-status-"));
|
|
12
39
|
let calls = 0;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-status.test.js","sourceRoot":"","sources":["../../src/workspaces/git-status.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\"\nimport { mkdtemp, rm } from \"node:fs/promises\"\nimport fs from \"node:fs/promises\"\nimport { tmpdir } from \"node:os\"\nimport { syncBuiltinESMExports } from \"node:module\"\nimport path from \"node:path\"\nimport { describe, it } from \"node:test\"\n\nimport { getWorktreeGitStatus, invalidateWorktreeGitStatus } from \"./git-status\"\n\ndescribe(\"worktree git status singleflight\", () => {\n it(\"coalesces concurrent requests and runs again after settlement\", async (t) => {\n const directory = await mkdtemp(path.join(tmpdir(), \"codenomad-git-status-\"))\n let calls = 0\n let release!: () => void\n const blocked = new Promise<void>((resolve) => { release = resolve })\n // Both callers must finish canonicalization before releasing the Git work.\n // Otherwise a fast first read can settle while the second realpath is still\n // in the filesystem queue, legitimately starting a separate flight.\n const realpath = fs.realpath\n let canonicalized = 0\n let ready!: () => void\n const bothCanonicalized = new Promise<void>((resolve) => { ready = resolve })\n const canonicalization = t.mock.method(fs, \"realpath\", async (value: string) => {\n const result = await realpath(value)\n canonicalized += 1\n if (canonicalized === 2) ready()\n await bothCanonicalized\n return result\n })\n // Native ESM named imports do not see default-export monkey patches until\n // synchronized. The CI runner uses ESM even when a local tsx run uses CJS.\n syncBuiltinESMExports()\n const run = async () => {\n calls += 1\n await blocked\n return { ok: true as const, stdout: \"\" }\n }\n\n try {\n const first = getWorktreeGitStatus({ workspaceFolder: directory }, run)\n const second = getWorktreeGitStatus({ workspaceFolder: path.join(directory, \".\") }, run)\n const deadline = Date.now() + 1_000\n while (calls === 0 && Date.now() < deadline) {\n await new Promise<void>((resolve) => setImmediate(resolve))\n }\n assert.equal(calls, 5)\n release()\n assert.deepEqual(await Promise.all([first, second]), [[], []])\n assert.equal(calls, 5)\n\n await getWorktreeGitStatus({ workspaceFolder: directory }, run)\n assert.equal(calls, 10)\n } finally {\n release()\n canonicalization.mock.restore()\n syncBuiltinESMExports()\n await rm(directory, { recursive: true, force: true })\n }\n })\n\n it(\"starts a new generation after a successful mutation while an older read is blocked\", async () => {\n const directory = await mkdtemp(path.join(tmpdir(), \"codenomad-git-status-\"))\n let calls = 0\n let releaseOld!: () => void\n const oldBlocked = new Promise<void>((resolve) => { releaseOld = resolve })\n const run = async () => {\n calls += 1\n if (calls <= 5) await oldBlocked\n return { ok: true as const, stdout: \"\" }\n }\n\n try {\n const oldRead = getWorktreeGitStatus({ workspaceFolder: directory }, run)\n while (calls < 5) await new Promise<void>((resolve) => setImmediate(resolve))\n await invalidateWorktreeGitStatus(path.join(directory, \".\"))\n const newRead = getWorktreeGitStatus({ workspaceFolder: directory }, run)\n assert.deepEqual(await newRead, [])\n assert.equal(calls, 10)\n releaseOld()\n assert.deepEqual(await oldRead, [])\n\n await getWorktreeGitStatus({ workspaceFolder: directory }, run)\n assert.equal(calls, 15)\n } finally {\n await rm(directory, { recursive: true, force: true })\n }\n })\n\n it(\"cleans up a rejected flight so the next read can retry\", async () => {\n const directory = await mkdtemp(path.join(tmpdir(), \"codenomad-git-status-\"))\n let fail = true\n let calls = 0\n const run = async () => {\n calls += 1\n return fail\n ? { ok: false as const, error: new Error(\"status failed\") }\n : { ok: true as const, stdout: \"\" }\n }\n\n try {\n await assert.rejects(getWorktreeGitStatus({ workspaceFolder: directory }, run), /status failed/)\n fail = false\n assert.deepEqual(await getWorktreeGitStatus({ workspaceFolder: directory }, run), [])\n assert.equal(calls, 10)\n } finally {\n await rm(directory, { recursive: true, force: true })\n }\n })\n})\n"]}
|
|
1
|
+
{"version":3,"file":"git-status.test.js","sourceRoot":"","sources":["../../src/workspaces/git-status.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\"\nimport { mkdtemp, rm } from \"node:fs/promises\"\nimport fs from \"node:fs/promises\"\nimport { tmpdir } from \"node:os\"\nimport { syncBuiltinESMExports } from \"node:module\"\nimport path from \"node:path\"\nimport { execFileSync } from \"node:child_process\"\nimport { describe, it } from \"node:test\"\n\nimport { getWorktreeGitDiff, getWorktreeGitStatus, invalidateWorktreeGitStatus } from \"./git-status\"\n\ndescribe(\"worktree git status singleflight\", () => {\n it(\"reads real Git status and large UTF-8 blobs through the worker without losing final newlines\", async () => {\n const directory = await mkdtemp(path.join(tmpdir(), \"codenomad-git-content-\"))\n const git = (...args: string[]) => execFileSync(\"git\", [\"-C\", directory, ...args], { stdio: \"pipe\" })\n try {\n git(\"init\")\n git(\"config\", \"user.name\", \"Fixture\")\n git(\"config\", \"user.email\", \"fixture@example.test\")\n git(\"config\", \"core.autocrlf\", \"false\")\n const before = \"é漢字 — content\\n\".repeat(100_000)\n await fs.writeFile(path.join(directory, \"large.txt\"), before)\n git(\"add\", \".\")\n git(\"commit\", \"-m\", \"fixture\")\n await fs.writeFile(path.join(directory, \"large.txt\"), before + \"changed\\n\")\n await fs.writeFile(path.join(directory, \"untracked.txt\"), \"one\\ntwo\\n\")\n const status = await getWorktreeGitStatus({ workspaceFolder: directory })\n assert.equal(status.find(entry => entry.path === \"untracked.txt\")?.unstagedAdditions, 2,\n \"git diff --no-index exit 1 is a successful numstat result\")\n const diff = await getWorktreeGitDiff({ workspaceFolder: directory, path: \"large.txt\", scope: \"unstaged\" })\n assert.equal(diff.before, before)\n assert.equal(diff.after, before + \"changed\\n\")\n await assert.rejects(getWorktreeGitStatus({ workspaceFolder: path.join(directory, \"missing\") }))\n assert.equal((await getWorktreeGitStatus({ workspaceFolder: directory })).length, 2)\n } finally { await rm(directory, { recursive: true, force: true }) }\n })\n it(\"coalesces concurrent requests and runs again after settlement\", async (t) => {\n const directory = await mkdtemp(path.join(tmpdir(), \"codenomad-git-status-\"))\n let calls = 0\n let release!: () => void\n const blocked = new Promise<void>((resolve) => { release = resolve })\n // Both callers must finish canonicalization before releasing the Git work.\n // Otherwise a fast first read can settle while the second realpath is still\n // in the filesystem queue, legitimately starting a separate flight.\n const realpath = fs.realpath\n let canonicalized = 0\n let ready!: () => void\n const bothCanonicalized = new Promise<void>((resolve) => { ready = resolve })\n const canonicalization = t.mock.method(fs, \"realpath\", async (value: string) => {\n const result = await realpath(value)\n canonicalized += 1\n if (canonicalized === 2) ready()\n await bothCanonicalized\n return result\n })\n // Native ESM named imports do not see default-export monkey patches until\n // synchronized. The CI runner uses ESM even when a local tsx run uses CJS.\n syncBuiltinESMExports()\n const run = async () => {\n calls += 1\n await blocked\n return { ok: true as const, stdout: \"\" }\n }\n\n try {\n const first = getWorktreeGitStatus({ workspaceFolder: directory }, run)\n const second = getWorktreeGitStatus({ workspaceFolder: path.join(directory, \".\") }, run)\n const deadline = Date.now() + 1_000\n while (calls === 0 && Date.now() < deadline) {\n await new Promise<void>((resolve) => setImmediate(resolve))\n }\n assert.equal(calls, 5)\n release()\n assert.deepEqual(await Promise.all([first, second]), [[], []])\n assert.equal(calls, 5)\n\n await getWorktreeGitStatus({ workspaceFolder: directory }, run)\n assert.equal(calls, 10)\n } finally {\n release()\n canonicalization.mock.restore()\n syncBuiltinESMExports()\n await rm(directory, { recursive: true, force: true })\n }\n })\n\n it(\"starts a new generation after a successful mutation while an older read is blocked\", async () => {\n const directory = await mkdtemp(path.join(tmpdir(), \"codenomad-git-status-\"))\n let calls = 0\n let releaseOld!: () => void\n const oldBlocked = new Promise<void>((resolve) => { releaseOld = resolve })\n const run = async () => {\n calls += 1\n if (calls <= 5) await oldBlocked\n return { ok: true as const, stdout: \"\" }\n }\n\n try {\n const oldRead = getWorktreeGitStatus({ workspaceFolder: directory }, run)\n while (calls < 5) await new Promise<void>((resolve) => setImmediate(resolve))\n await invalidateWorktreeGitStatus(path.join(directory, \".\"))\n const newRead = getWorktreeGitStatus({ workspaceFolder: directory }, run)\n assert.deepEqual(await newRead, [])\n assert.equal(calls, 10)\n releaseOld()\n assert.deepEqual(await oldRead, [])\n\n await getWorktreeGitStatus({ workspaceFolder: directory }, run)\n assert.equal(calls, 15)\n } finally {\n await rm(directory, { recursive: true, force: true })\n }\n })\n\n it(\"cleans up a rejected flight so the next read can retry\", async () => {\n const directory = await mkdtemp(path.join(tmpdir(), \"codenomad-git-status-\"))\n let fail = true\n let calls = 0\n const run = async () => {\n calls += 1\n return fail\n ? { ok: false as const, error: new Error(\"status failed\") }\n : { ok: true as const, stdout: \"\" }\n }\n\n try {\n await assert.rejects(getWorktreeGitStatus({ workspaceFolder: directory }, run), /status failed/)\n fail = false\n assert.deepEqual(await getWorktreeGitStatus({ workspaceFolder: directory }, run), [])\n assert.equal(calls, 10)\n } finally {\n await rm(directory, { recursive: true, force: true })\n }\n })\n})\n"]}
|