core: prevent file deletion when reverting changes to existing files

This commit is contained in:
Dax Raad
2025-10-01 05:05:50 -04:00
parent 35a608cd53
commit 6a7eeb39c3
2 changed files with 63 additions and 2 deletions

View File

@@ -105,8 +105,17 @@ export namespace Snapshot {
.cwd(Instance.worktree)
.nothrow()
if (result.exitCode !== 0) {
log.info("file not found in history, deleting", { file })
await fs.unlink(file).catch(() => {})
const relativePath = path.relative(Instance.worktree, file)
const checkTree = await $`git --git-dir=${git} ls-tree ${item.hash} -- ${relativePath}`
.quiet()
.cwd(Instance.worktree)
.nothrow()
if (checkTree.exitCode === 0 && checkTree.text().trim()) {
log.info("file existed in snapshot but checkout failed, keeping", { file })
} else {
log.info("file did not exist in snapshot, deleting", { file })
await fs.unlink(file).catch(() => {})
}
}
files.add(file)
}

View File

@@ -532,3 +532,55 @@ test("restore function", async () => {
},
})
})
test("revert should not delete files that existed but were deleted in snapshot", async () => {
await using tmp = await bootstrap()
await Instance.provide({
directory: tmp.path,
fn: async () => {
const snapshot1 = await Snapshot.track()
expect(snapshot1).toBeTruthy()
await $`rm ${tmp.path}/a.txt`.quiet()
const snapshot2 = await Snapshot.track()
expect(snapshot2).toBeTruthy()
await Bun.write(`${tmp.path}/a.txt`, "recreated content")
const patch = await Snapshot.patch(snapshot2!)
expect(patch.files).toContain(`${tmp.path}/a.txt`)
await Snapshot.revert([patch])
expect(await Bun.file(`${tmp.path}/a.txt`).exists()).toBe(false)
},
})
})
test("revert preserves file that existed in snapshot when deleted then recreated", async () => {
await using tmp = await bootstrap()
await Instance.provide({
directory: tmp.path,
fn: async () => {
await Bun.write(`${tmp.path}/existing.txt`, "original content")
const snapshot = await Snapshot.track()
expect(snapshot).toBeTruthy()
await $`rm ${tmp.path}/existing.txt`.quiet()
await Bun.write(`${tmp.path}/existing.txt`, "recreated")
await Bun.write(`${tmp.path}/newfile.txt`, "new")
const patch = await Snapshot.patch(snapshot!)
expect(patch.files).toContain(`${tmp.path}/existing.txt`)
expect(patch.files).toContain(`${tmp.path}/newfile.txt`)
await Snapshot.revert([patch])
expect(await Bun.file(`${tmp.path}/newfile.txt`).exists()).toBe(false)
expect(await Bun.file(`${tmp.path}/existing.txt`).exists()).toBe(true)
expect(await Bun.file(`${tmp.path}/existing.txt`).text()).toBe("original content")
},
})
})