@@ -12,18 +12,19 @@ use super::{
1212 state:: OplogHandle ,
1313} ;
1414use anyhow:: { anyhow, bail, Context , Result } ;
15- use git2:: { DiffOptions , FileMode } ;
15+ use git2:: FileMode ;
1616use gitbutler_command_context:: RepositoryExtLite ;
1717use gitbutler_diff:: { hunks_by_filepath, FileDiff } ;
18- use gitbutler_oxidize:: { git2_to_gix_object_id, gix_to_git2_oid} ;
18+ use gitbutler_oxidize:: { git2_to_gix_object_id, gix_time_to_git2 , gix_to_git2_oid} ;
1919use gitbutler_project:: {
2020 access:: { WorktreeReadPermission , WorktreeWritePermission } ,
2121 Project ,
2222} ;
2323use gitbutler_repo:: SignaturePurpose ;
2424use gitbutler_repo:: { GixRepositoryExt , RepositoryExt } ;
2525use gitbutler_stack:: { Stack , VirtualBranchesHandle , VirtualBranchesState } ;
26- use gix:: prelude:: Write ;
26+ use gix:: bstr:: ByteSlice ;
27+ use gix:: prelude:: { ObjectIdExt , Write } ;
2728use tracing:: instrument;
2829
2930const SNAPSHOT_FILE_LIMIT_BYTES : u64 = 32 * 1024 * 1024 ;
@@ -167,10 +168,9 @@ impl OplogExt for Project {
167168 oplog_commit_id : Option < git2:: Oid > ,
168169 ) -> Result < Vec < Snapshot > > {
169170 let worktree_dir = self . path . as_path ( ) ;
170- let repo = git2:: Repository :: open ( worktree_dir) ?;
171171 let gix_repo = gitbutler_command_context:: gix_repository_for_merging ( worktree_dir) ?;
172172
173- let traversal_root_id = match oplog_commit_id {
173+ let traversal_root_id = git2_to_gix_object_id ( match oplog_commit_id {
174174 Some ( id) => id,
175175 None => {
176176 let oplog_state = OplogHandle :: new ( & self . gb_dir ( ) ) ;
@@ -180,69 +180,79 @@ impl OplogExt for Project {
180180 return Ok ( vec ! [ ] ) ;
181181 }
182182 }
183- } ;
184-
185- let oplog_head_commit = repo. find_commit ( traversal_root_id) ?;
186-
187- let mut revwalk = repo. revwalk ( ) ?;
188- revwalk. push ( oplog_head_commit. id ( ) ) ?;
183+ } )
184+ . attach ( & gix_repo) ;
189185
190186 let mut snapshots = Vec :: new ( ) ;
187+ let mut wd_trees_cache: HashMap < gix:: ObjectId , gix:: ObjectId > = HashMap :: new ( ) ;
191188
192- let mut wd_trees_cache: HashMap < git2:: Oid , git2:: Oid > = HashMap :: new ( ) ;
193-
194- for commit_id in revwalk {
189+ for commit_info in traversal_root_id. ancestors ( ) . all ( ) ? {
195190 if snapshots. len ( ) == limit {
196191 break ;
197192 }
198- let commit_id = commit_id?;
199- let commit = repo. find_commit ( commit_id) ?;
200-
201- if commit. parent_count ( ) > 1 {
193+ let commit_id = commit_info?. id ( ) ;
194+ let commit = commit_id. object ( ) ?. into_commit ( ) ;
195+ let mut parents = commit. parent_ids ( ) ;
196+ let ( first_parent, second_parent) = ( parents. next ( ) , parents. next ( ) ) ;
197+ if second_parent. is_some ( ) {
202198 break ;
203199 }
204200
205201 let tree = commit. tree ( ) ?;
206- if tree. get_name ( "virtual_branches.toml" ) . is_none ( ) {
202+ if tree
203+ . lookup_entry_by_path ( "virtual_branches.toml" ) ?
204+ . is_none ( )
205+ {
207206 // We reached a tree that is not a snapshot
208207 tracing:: warn!( "Commit {commit_id} didn't seem to be an oplog commit - skipping" ) ;
209208 continue ;
210209 }
211210
212211 // Get tree id from cache or calculate it
213- let wd_tree = get_workdir_tree ( & mut wd_trees_cache, commit_id, & repo , & gix_repo) ?;
212+ let wd_tree = get_workdir_tree ( & mut wd_trees_cache, commit_id, & gix_repo) ?;
214213
214+ let commit_id = gix_to_git2_oid ( commit_id) ;
215215 let details = commit
216- . message ( )
216+ . message_raw ( ) ?
217+ . to_str ( )
218+ . ok ( )
217219 . and_then ( |msg| SnapshotDetails :: from_str ( msg) . ok ( ) ) ;
220+ let commit_time = gix_time_to_git2 ( commit. time ( ) ?) ;
218221
219- if let Ok ( parent ) = commit . parent ( 0 ) {
222+ if let Some ( parent_id ) = first_parent {
220223 // Get tree id from cache or calculate it
221- let parent_tree =
222- get_workdir_tree ( & mut wd_trees_cache, parent. id ( ) , & repo, & gix_repo) ?;
223-
224- let mut opts = DiffOptions :: new ( ) ;
225- opts. include_untracked ( true ) ;
226- opts. ignore_submodules ( true ) ;
227- let diff =
228- repo. diff_tree_to_tree ( Some ( & parent_tree) , Some ( & wd_tree) , Some ( & mut opts) ) ?;
229224
230225 let mut files_changed = Vec :: new ( ) ;
231- diff. print ( git2:: DiffFormat :: NameOnly , |delta, _, _| {
232- if let Some ( path) = delta. new_file ( ) . path ( ) {
233- files_changed. push ( path. to_path_buf ( ) ) ;
234- }
235- true
236- } ) ?;
226+ let mut resource_cache = gix_repo. diff_resource_cache_for_tree_diff ( ) ?;
227+ let ( mut lines_added, mut lines_removed) = ( 0 , 0 ) ;
228+ let parent_tree = get_workdir_tree ( & mut wd_trees_cache, parent_id, & gix_repo) ?;
229+ parent_tree
230+ . changes ( ) ?
231+ . options ( |opts| {
232+ opts. track_rewrites ( None ) . track_path ( ) ;
233+ } )
234+ . for_each_to_obtain_tree ( & wd_tree, |change| -> Result < _ > {
235+ files_changed. push ( gix:: path:: from_bstr ( change. location ( ) ) . into_owned ( ) ) ;
236+ if let Some ( counts) = change
237+ . diff ( & mut resource_cache)
238+ . ok ( )
239+ . and_then ( |mut platform| platform. line_counts ( ) . ok ( ) . flatten ( ) )
240+ {
241+ lines_added += u64:: from ( counts. insertions ) ;
242+ lines_removed += u64:: from ( counts. removals ) ;
243+ }
244+ resource_cache. clear_resource_cache_keep_allocation ( ) ;
245+
246+ Ok ( gix:: object:: tree:: diff:: Action :: Continue )
247+ } ) ?;
237248
238- let stats = diff. stats ( ) ?;
239249 snapshots. push ( Snapshot {
240250 commit_id,
241251 details,
242- lines_added : stats . insertions ( ) ,
243- lines_removed : stats . deletions ( ) ,
252+ lines_added : lines_added as usize ,
253+ lines_removed : lines_removed as usize ,
244254 files_changed,
245- created_at : commit . time ( ) ,
255+ created_at : commit_time ,
246256 } ) ;
247257 } else {
248258 // this is the very first snapshot
@@ -252,7 +262,7 @@ impl OplogExt for Project {
252262 lines_added : 0 ,
253263 lines_removed : 0 ,
254264 files_changed : Vec :: new ( ) ,
255- created_at : commit . time ( ) ,
265+ created_at : commit_time ,
256266 } ) ;
257267 break ;
258268 }
@@ -318,21 +328,20 @@ impl OplogExt for Project {
318328
319329/// Get a tree of the working dir (applied branches merged)
320330fn get_workdir_tree < ' a > (
321- wd_trees_cache : & mut HashMap < git2 :: Oid , git2 :: Oid > ,
322- commit_id : git2 :: Oid ,
323- repo : & ' a git2 :: Repository ,
324- gix_repo : & gix:: Repository ,
325- ) -> Result < git2 :: Tree < ' a > , anyhow :: Error > {
331+ wd_trees_cache : & mut HashMap < gix :: ObjectId , gix :: ObjectId > ,
332+ commit_id : impl Into < gix :: ObjectId > ,
333+ repo : & ' a gix :: Repository ,
334+ ) -> Result < gix:: Tree < ' a > , anyhow :: Error > {
335+ let commit_id = commit_id . into ( ) ;
326336 if let Entry :: Vacant ( e) = wd_trees_cache. entry ( commit_id) {
327- if let Ok ( wd_tree_id) = tree_from_applied_vbranches ( gix_repo , commit_id) {
328- e. insert ( wd_tree_id) ;
337+ if let Ok ( wd_tree_id) = tree_from_applied_vbranches ( repo , gix_to_git2_oid ( commit_id) ) {
338+ e. insert ( git2_to_gix_object_id ( wd_tree_id) ) ;
329339 }
330340 }
331- let wd_tree_id = wd_trees_cache. get ( & commit_id) . ok_or ( anyhow ! (
341+ let id = wd_trees_cache. get ( & commit_id) . copied ( ) . ok_or ( anyhow ! (
332342 "Could not get a tree of all applied virtual branches merged"
333343 ) ) ?;
334- let wd_tree = repo. find_tree ( wd_tree_id. to_owned ( ) ) ?;
335- Ok ( wd_tree)
344+ Ok ( repo. find_tree ( id) ?)
336345}
337346
338347fn prepare_snapshot ( ctx : & Project , _shared_access : & WorktreeReadPermission ) -> Result < git2:: Oid > {
0 commit comments