Skip to content
Closed
114 changes: 91 additions & 23 deletions packages/opencode/src/snapshot/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,35 +298,103 @@ export namespace Snapshot {
)
})

const revert = Effect.fnUntraced(function* (patches: Snapshot.Patch[]) {
return yield* locked(
Effect.gen(function* () {
const seen = new Set<string>()
for (const item of patches) {
for (const file of item.files) {
if (seen.has(file)) continue
seen.add(file)
log.info("reverting", { file, hash: item.hash })
const result = yield* git([...core, ...args(["checkout", item.hash, "--", file])], {
const revert = Effect.fnUntraced(function* (patches: Snapshot.Patch[]) {
return yield* locked(
Effect.gen(function* () {
const groupByHash = new Map<string, string[]>()
const seen = new Set<string>()
for (const item of patches) {
const filesForHash = groupByHash.get(item.hash) ?? []
groupByHash.set(item.hash, filesForHash)
for (const file of item.files) {
if (seen.has(file)) continue
seen.add(file)
filesForHash.push(file)
}
}

const revertSingle = Effect.fnUntraced(function* (hash: string, file: string) {
log.info("reverting", { file, hash })
const result = yield* git([...core, ...args(["checkout", hash, "--", file])], {
cwd: state.worktree,
})
if (result.code === 0) return
const rel = path.relative(state.worktree, file).replaceAll("\\", "/")
const tree = yield* git([...core, ...args(["ls-tree", hash, "--", rel])], {
cwd: state.worktree,
})
if (result.code !== 0) {
const rel = path.relative(state.worktree, file)
const tree = yield* git([...core, ...args(["ls-tree", item.hash, "--", rel])], {
cwd: state.worktree,
})
if (tree.code === 0 && tree.text.trim()) {
log.info("file existed in snapshot but checkout failed, keeping", { file })
} else {
log.info("file did not exist in snapshot, deleting", { file })
if (tree.code === 0 && tree.text.trim()) {
log.info("file existed in snapshot but checkout failed, keeping", { file, hash })
return
}
log.info("file did not exist in snapshot, deleting", { file, hash })
yield* remove(file)
})

for (const [hash, filesByHash] of groupByHash) {
// run batched git commands in 100-file chunks to avoid oversized argv calls.
for (let i = 0; i < filesByHash.length; i += 100) {
const fileChunk = filesByHash.slice(i, i + 100)
if (fileChunk.length === 1) {
yield* revertSingle(hash, fileChunk[0]!)
continue
}

const filePaths = fileChunk.map((file) => ({
rel: path.relative(state.worktree, file).replaceAll("\\", "/"),
file,
}))
const tree = yield* git(
[...core, ...args(["ls-tree", "--name-only", hash, "--", ...filePaths.map((item) => item.rel)])],
{
cwd: state.worktree,
},
)

if (tree.code !== 0) {
log.info("batched ls-tree failed, falling back to single-file revert", { hash, files: fileChunk.length })
for (const file of fileChunk) {
yield* revertSingle(hash, file)
}
continue
}

const snapshotPaths = new Set(tree.text.trim().split("\n").map((item) => item.trim()).filter(Boolean))
const filesToCheckout: string[] = []
const missingFiles: string[] = []
for (const item of filePaths) {
if (snapshotPaths.has(item.rel)) {
filesToCheckout.push(item.file)
continue
}
missingFiles.push(item.file)
}

if (filesToCheckout.length) {
log.info("reverting", { hash, files: filesToCheckout.length })
const result = yield* git([...core, ...args(["checkout", hash, "--", ...filesToCheckout])], {
cwd: state.worktree,
})
if (result.code !== 0) {
log.info("batched checkout failed, falling back to single-file revert", {
hash,
files: filesToCheckout.length,
})
for (const file of filesToCheckout) {
yield* revertSingle(hash, file)
}
}
}

for (const file of missingFiles) {
log.info("file did not exist in snapshot, deleting", { file, hash })
yield* remove(file)
}
}
}
}
}),
)
})
}),
)
})

const diff = Effect.fnUntraced(function* (hash: string) {
return yield* locked(
Expand Down
Loading