index.ts 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. import { App } from "../app/app"
  2. import { $ } from "bun"
  3. import path from "path"
  4. import fs from "fs/promises"
  5. import { Ripgrep } from "../file/ripgrep"
  6. import { Log } from "../util/log"
  7. export namespace Snapshot {
  8. const log = Log.create({ service: "snapshot" })
  9. export async function create(sessionID: string) {
  10. log.info("creating snapshot")
  11. const app = App.info()
  12. const git = gitdir(sessionID)
  13. // not a git repo, check if too big to snapshot
  14. if (!app.git) {
  15. const files = await Ripgrep.files({
  16. cwd: app.path.cwd,
  17. limit: 1000,
  18. })
  19. log.info("found files", { count: files.length })
  20. if (files.length > 1000) return
  21. }
  22. if (await fs.mkdir(git, { recursive: true })) {
  23. await $`git init`
  24. .env({
  25. ...process.env,
  26. GIT_DIR: git,
  27. GIT_WORK_TREE: app.path.root,
  28. })
  29. .quiet()
  30. .nothrow()
  31. log.info("initialized")
  32. }
  33. await $`git --git-dir ${git} add .`.quiet().cwd(app.path.cwd).nothrow()
  34. log.info("added files")
  35. const result =
  36. await $`git --git-dir ${git} commit --allow-empty -m "snapshot" --author="opencode <mail@opencode.ai>"`
  37. .quiet()
  38. .cwd(app.path.cwd)
  39. .nothrow()
  40. log.info("commit")
  41. // Extract commit hash from output like "[main abc1234] snapshot"
  42. const match = result.stdout.toString().match(/\[.+ ([a-f0-9]+)\]/)
  43. if (!match) throw new Error("Failed to extract commit hash")
  44. return match[1]
  45. }
  46. export async function restore(sessionID: string, commit: string) {
  47. log.info("restore", { commit })
  48. const app = App.info()
  49. const git = gitdir(sessionID)
  50. await $`git --git-dir=${git} checkout ${commit} --force`
  51. .quiet()
  52. .cwd(app.path.root)
  53. }
  54. function gitdir(sessionID: string) {
  55. const app = App.info()
  56. return path.join(app.path.data, "snapshot", sessionID)
  57. }
  58. }