@@ -12,18 +12,20 @@ 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:: object:: tree:: diff:: Change ;
28+ use gix:: prelude:: { ObjectIdExt , Write } ;
2729use tracing:: instrument;
2830
2931const SNAPSHOT_FILE_LIMIT_BYTES : u64 = 32 * 1024 * 1024 ;
@@ -167,10 +169,9 @@ impl OplogExt for Project {
167169 oplog_commit_id : Option < git2:: Oid > ,
168170 ) -> Result < Vec < Snapshot > > {
169171 let worktree_dir = self . path . as_path ( ) ;
170- let repo = git2:: Repository :: open ( worktree_dir) ?;
171172 let gix_repo = gitbutler_command_context:: gix_repository_for_merging ( worktree_dir) ?;
172173
173- let traversal_root_id = match oplog_commit_id {
174+ let traversal_root_id = git2_to_gix_object_id ( match oplog_commit_id {
174175 Some ( id) => id,
175176 None => {
176177 let oplog_state = OplogHandle :: new ( & self . gb_dir ( ) ) ;
@@ -180,69 +181,86 @@ impl OplogExt for Project {
180181 return Ok ( vec ! [ ] ) ;
181182 }
182183 }
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 ( ) ) ?;
184+ } )
185+ . attach ( & gix_repo) ;
189186
190187 let mut snapshots = Vec :: new ( ) ;
188+ let mut wd_trees_cache: HashMap < gix:: ObjectId , gix:: ObjectId > = HashMap :: new ( ) ;
191189
192- let mut wd_trees_cache: HashMap < git2:: Oid , git2:: Oid > = HashMap :: new ( ) ;
193-
194- for commit_id in revwalk {
190+ for commit_info in traversal_root_id. ancestors ( ) . all ( ) ? {
195191 if snapshots. len ( ) == limit {
196192 break ;
197193 }
198- let commit_id = commit_id?;
199- let commit = repo. find_commit ( commit_id) ?;
200-
201- if commit. parent_count ( ) > 1 {
194+ let commit_id = commit_info?. id ( ) ;
195+ let commit = commit_id. object ( ) ?. into_commit ( ) ;
196+ let mut parents = commit. parent_ids ( ) ;
197+ let ( first_parent, second_parent) = ( parents. next ( ) , parents. next ( ) ) ;
198+ if second_parent. is_some ( ) {
202199 break ;
203200 }
204201
205202 let tree = commit. tree ( ) ?;
206- if tree. get_name ( "virtual_branches.toml" ) . is_none ( ) {
203+ if tree
204+ . lookup_entry_by_path ( "virtual_branches.toml" ) ?
205+ . is_none ( )
206+ {
207207 // We reached a tree that is not a snapshot
208208 tracing:: warn!( "Commit {commit_id} didn't seem to be an oplog commit - skipping" ) ;
209209 continue ;
210210 }
211211
212212 // Get tree id from cache or calculate it
213- let wd_tree = get_workdir_tree ( & mut wd_trees_cache, commit_id, & repo , & gix_repo) ?;
213+ let wd_tree = get_workdir_tree ( & mut wd_trees_cache, commit_id, & gix_repo) ?;
214214
215+ let commit_id = gix_to_git2_oid ( commit_id) ;
215216 let details = commit
216- . message ( )
217+ . message_raw ( ) ?
218+ . to_str ( )
219+ . ok ( )
217220 . and_then ( |msg| SnapshotDetails :: from_str ( msg) . ok ( ) ) ;
221+ let commit_time = gix_time_to_git2 ( commit. time ( ) ?) ;
218222
219- if let Ok ( parent ) = commit . parent ( 0 ) {
223+ if let Some ( parent_id ) = first_parent {
220224 // 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) ) ?;
229225
230226 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- } ) ?;
227+ let mut resource_cache = gix_repo. diff_resource_cache_for_tree_diff ( ) ?;
228+ let ( mut lines_added, mut lines_removed) = ( 0 , 0 ) ;
229+ let parent_tree = get_workdir_tree ( & mut wd_trees_cache, parent_id, & gix_repo) ?;
230+ parent_tree
231+ . changes ( ) ?
232+ . options ( |opts| {
233+ opts. track_rewrites ( None ) . track_path ( ) ;
234+ } )
235+ . for_each_to_obtain_tree ( & wd_tree, |change| -> Result < _ > {
236+ match change {
237+ Change :: Addition { location, .. } => {
238+ files_changed. push ( gix:: path:: from_bstr ( location) . into_owned ( ) ) ;
239+ }
240+ Change :: Deletion { .. }
241+ | Change :: Modification { .. }
242+ | Change :: Rewrite { .. } => { }
243+ }
244+ if let Some ( counts) = change
245+ . diff ( & mut resource_cache)
246+ . ok ( )
247+ . and_then ( |mut platform| platform. line_counts ( ) . ok ( ) . flatten ( ) )
248+ {
249+ lines_added += u64:: from ( counts. insertions ) ;
250+ lines_removed += u64:: from ( counts. removals ) ;
251+ }
252+ resource_cache. clear_resource_cache_keep_allocation ( ) ;
253+
254+ Ok ( gix:: object:: tree:: diff:: Action :: Continue )
255+ } ) ?;
237256
238- let stats = diff. stats ( ) ?;
239257 snapshots. push ( Snapshot {
240258 commit_id,
241259 details,
242- lines_added : stats . insertions ( ) ,
243- lines_removed : stats . deletions ( ) ,
260+ lines_added : lines_added as usize ,
261+ lines_removed : lines_removed as usize ,
244262 files_changed,
245- created_at : commit . time ( ) ,
263+ created_at : commit_time ,
246264 } ) ;
247265 } else {
248266 // this is the very first snapshot
@@ -252,7 +270,7 @@ impl OplogExt for Project {
252270 lines_added : 0 ,
253271 lines_removed : 0 ,
254272 files_changed : Vec :: new ( ) ,
255- created_at : commit . time ( ) ,
273+ created_at : commit_time ,
256274 } ) ;
257275 break ;
258276 }
@@ -318,21 +336,20 @@ impl OplogExt for Project {
318336
319337/// Get a tree of the working dir (applied branches merged)
320338fn 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 > {
339+ wd_trees_cache : & mut HashMap < gix :: ObjectId , gix :: ObjectId > ,
340+ commit_id : impl Into < gix :: ObjectId > ,
341+ repo : & ' a gix :: Repository ,
342+ ) -> Result < gix:: Tree < ' a > , anyhow :: Error > {
343+ let commit_id = commit_id . into ( ) ;
326344 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) ;
345+ if let Ok ( wd_tree_id) = tree_from_applied_vbranches ( repo , gix_to_git2_oid ( commit_id) ) {
346+ e. insert ( git2_to_gix_object_id ( wd_tree_id) ) ;
329347 }
330348 }
331- let wd_tree_id = wd_trees_cache. get ( & commit_id) . ok_or ( anyhow ! (
349+ let id = wd_trees_cache. get ( & commit_id) . copied ( ) . ok_or ( anyhow ! (
332350 "Could not get a tree of all applied virtual branches merged"
333351 ) ) ?;
334- let wd_tree = repo. find_tree ( wd_tree_id. to_owned ( ) ) ?;
335- Ok ( wd_tree)
352+ Ok ( repo. find_tree ( id) ?)
336353}
337354
338355fn prepare_snapshot ( ctx : & Project , _shared_access : & WorktreeReadPermission ) -> Result < git2:: Oid > {
0 commit comments