| author | |
| committer | |
| log | b441875731c2fb174adbb61e1d46121be363474e |
| tree | b3058ac095a17c9edc89b5fea5f2618c65aac316 |
| parent | 8e744269a2edb2e51d3fa81e576fc29e64d5b186 |
- avoid git reset when repository has no HEAD\n- use React Query isLoading for checkpoint diff spinner\n- remove unreachable provider manager guard\n- add no-HEAD restore regression test\n\nCo-authored-by: codex <codex@users.noreply.github.com>4 files changed, 33 insertions(+), 5 deletions(-)
apps/server/src/filesystemCheckpointStore.test.ts+29| ... | ... | @@ -35,6 +35,12 @@ async function createRepo(): Promise<string> { |
| 35 | 35 | return cwd; |
| 36 | 36 | } |
| 37 | 37 | |
| 38 | async function createRepoWithoutHead(): Promise<string> { | |
| 39 | const cwd = await mkdtemp(path.join(os.tmpdir(), "t3-fs-checkpoints-empty-test-")); | |
| 40 | await runGit(cwd, ["init", "--initial-branch=main"]); | |
| 41 | return cwd; | |
| 42 | } | |
| 43 | ||
| 38 | 44 | describe("FilesystemCheckpointStore", () => { |
| 39 | 45 | it("captures refs without mutating branch history and restores workspace state", async () => { |
| 40 | 46 | const cwd = await createRepo(); |
| ... | ... | @@ -211,4 +217,27 @@ describe("FilesystemCheckpointStore", () => { |
| 211 | 217 | |
| 212 | 218 | expect(await store.isGitRepository(cwd)).toBe(false); |
| 213 | 219 | }); |
| 220 | ||
| 221 | it("restores checkpoint without reset when repository has no HEAD commit", async () => { | |
| 222 | const cwd = await createRepoWithoutHead(); | |
| 223 | const store = new FilesystemCheckpointStore(); | |
| 224 | const threadId = "thr-no-head"; | |
| 225 | ||
| 226 | try { | |
| 227 | await writeFile(path.join(cwd, "README.md"), "first\n"); | |
| 228 | await store.captureCheckpoint({ cwd, threadId, turnCount: 0 }); | |
| 229 | ||
| 230 | await writeFile(path.join(cwd, "README.md"), "second\n"); | |
| 231 | const restored = await store.restoreCheckpoint({ | |
| 232 | cwd, | |
| 233 | threadId, | |
| 234 | turnCount: 0, | |
| 235 | }); | |
| 236 | ||
| 237 | expect(restored).toBe(true); | |
| 238 | expect(await readFile(path.join(cwd, "README.md"), "utf8")).toBe("first\n"); | |
| 239 | } finally { | |
| 240 | await rm(cwd, { recursive: true, force: true }); | |
| 241 | } | |
| 242 | }); | |
| 214 | 243 | }); |
apps/server/src/filesystemCheckpointStore.ts+3-1| ... | ... | @@ -136,7 +136,9 @@ export class FilesystemCheckpointStore { |
| 136 | 136 | |
| 137 | 137 | await this.runGit(cwd, ["restore", "--source", commitOid, "--worktree", "--staged", "--", "."]); |
| 138 | 138 | await this.runGit(cwd, ["clean", "-fd", "--", "."]); |
| 139 | await this.runGit(cwd, ["reset", "--quiet", "--", "."]); | |
| 139 | if (await this.hasHeadCommit(cwd)) { | |
| 140 | await this.runGit(cwd, ["reset", "--quiet", "--", "."]); | |
| 141 | } | |
| 140 | 142 | return true; |
| 141 | 143 | } |
| 142 | 144 |
apps/server/src/providerManager.ts-3| ... | ... | @@ -433,9 +433,6 @@ export class ProviderManager extends EventEmitter<ProviderManagerEvents> { |
| 433 | 433 | .listSessions() |
| 434 | 434 | .find((candidate) => candidate.sessionId === sessionId); |
| 435 | 435 | const candidateCwds = session?.cwd ? [session.cwd] : [process.cwd()]; |
| 436 | if (candidateCwds.length === 0) { | |
| 437 | return null; | |
| 438 | } | |
| 439 | 436 | |
| 440 | 437 | await this.withFilesystemLock(sessionId, async () => { |
| 441 | 438 | const currentCwd = this.sessionCheckpointCwds.get(sessionId); |
apps/web/src/components/DiffPanel.tsx+1-1| ... | ... | @@ -147,7 +147,7 @@ export default function DiffPanel({ mode = "inline" }: DiffPanelProps) { |
| 147 | 147 | const conversationCheckpointDiff = selectedTurn |
| 148 | 148 | ? undefined |
| 149 | 149 | : activeCheckpointDiffQuery.data?.diff; |
| 150 | const isLoadingCheckpointDiff = activeCheckpointDiffQuery.isPending; | |
| 150 | const isLoadingCheckpointDiff = activeCheckpointDiffQuery.isLoading; | |
| 151 | 151 | const checkpointDiffError = |
| 152 | 152 | activeCheckpointDiffQuery.error instanceof Error |
| 153 | 153 | ? activeCheckpointDiffQuery.error.message |