11import { App } from "../app/app"
2- import {
3- add ,
4- commit ,
5- init ,
6- checkout ,
7- statusMatrix ,
8- remove ,
9- } from "isomorphic-git"
2+ import { $ } from "bun"
103import path from "path"
11- import fs from "fs"
4+ import fs from "fs/promises "
125import { Ripgrep } from "../file/ripgrep"
136import { Log } from "../util/log"
147
@@ -19,76 +12,52 @@ export namespace Snapshot {
1912 log . info ( "creating snapshot" )
2013 const app = App . info ( )
2114 const git = gitdir ( sessionID )
22- const files = await Ripgrep . files ( {
23- cwd : app . path . cwd ,
24- limit : app . git ? undefined : 1000 ,
25- } )
26- log . info ( "found files" , { count : files . length } )
27- // not a git repo and too big to snapshot
28- if ( ! app . git && files . length === 1000 ) return
29- await init ( {
30- dir : app . path . cwd ,
31- gitdir : git ,
32- fs,
33- } )
34- log . info ( "initialized" )
35- const status = await statusMatrix ( {
36- fs,
37- gitdir : git ,
38- dir : app . path . cwd ,
39- } )
40- log . info ( "matrix" , {
41- count : status . length ,
42- } )
43- const added = [ ]
44- for ( const [ file , head , workdir , stage ] of status ) {
45- if ( workdir === 0 && stage === 1 ) {
46- log . info ( "remove" , { file } )
47- await remove ( {
48- fs,
49- gitdir : git ,
50- dir : app . path . cwd ,
51- filepath : file ,
15+
16+ // not a git repo, check if too big to snapshot
17+ if ( ! app . git ) {
18+ const files = await Ripgrep . files ( {
19+ cwd : app . path . cwd ,
20+ limit : 1000 ,
21+ } )
22+ log . info ( "found files" , { count : files . length } )
23+ if ( files . length > 1000 ) return
24+ }
25+
26+ if ( await fs . mkdir ( git , { recursive : true } ) ) {
27+ await $ `git init`
28+ . env ( {
29+ ...process . env ,
30+ GIT_DIR : git ,
31+ GIT_WORK_TREE : app . path . root ,
5232 } )
53- continue
54- }
55- if ( workdir !== head ) {
56- added . push ( file )
57- }
33+ . quiet ( )
34+ . nothrow ( )
35+ log . info ( "initialized" )
5836 }
59- log . info ( "removed files" )
60- await add ( {
61- fs,
62- gitdir : git ,
63- parallel : true ,
64- dir : app . path . cwd ,
65- filepath : added ,
66- } )
37+
38+ await $ `git --git-dir ${ git } add .` . quiet ( ) . cwd ( app . path . cwd ) . nothrow ( )
6739 log . info ( "added files" )
68- const result = await commit ( {
69- fs ,
70- gitdir : git ,
71- dir : app . path . cwd ,
72- message : "snapshot" ,
73- author : {
74- name : "opencode" ,
75- email : "mail@opencode.ai" ,
76- } ,
77- } )
78- log . info ( "commit" , { result } )
79- return result
40+
41+ const result =
42+ await $ ` git --git-dir ${ git } commit --allow-empty -m "snapshot" --author="opencode <mail@opencode.ai>"`
43+ . quiet ( )
44+ . cwd ( app . path . cwd )
45+ . nothrow ( )
46+ log . info ( "commit" )
47+
48+ // Extract commit hash from output like "[main abc1234] snapshot"
49+ const match = result . stdout . toString ( ) . match ( / \[ . + ( [ a - f 0 - 9 ] + ) \] / )
50+ if ( ! match ) throw new Error ( "Failed to extract commit hash" )
51+ return match [ 1 ]
8052 }
8153
8254 export async function restore ( sessionID : string , commit : string ) {
8355 log . info ( "restore" , { commit } )
8456 const app = App . info ( )
85- await checkout ( {
86- fs,
87- gitdir : gitdir ( sessionID ) ,
88- dir : app . path . cwd ,
89- ref : commit ,
90- force : true ,
91- } )
57+ const git = gitdir ( sessionID )
58+ await $ `git --git-dir=${ git } checkout ${ commit } --force`
59+ . quiet ( )
60+ . cwd ( app . path . root )
9261 }
9362
9463 function gitdir ( sessionID : string ) {
0 commit comments