@@ -12,18 +12,20 @@ use super::{
12
12
state:: OplogHandle ,
13
13
} ;
14
14
use anyhow:: { anyhow, bail, Context , Result } ;
15
- use git2:: { DiffOptions , FileMode } ;
15
+ use git2:: FileMode ;
16
16
use gitbutler_command_context:: RepositoryExtLite ;
17
17
use 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} ;
19
19
use gitbutler_project:: {
20
20
access:: { WorktreeReadPermission , WorktreeWritePermission } ,
21
21
Project ,
22
22
} ;
23
23
use gitbutler_repo:: SignaturePurpose ;
24
24
use gitbutler_repo:: { GixRepositoryExt , RepositoryExt } ;
25
25
use 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 } ;
27
29
use tracing:: instrument;
28
30
29
31
const SNAPSHOT_FILE_LIMIT_BYTES : u64 = 32 * 1024 * 1024 ;
@@ -167,10 +169,9 @@ impl OplogExt for Project {
167
169
oplog_commit_id : Option < git2:: Oid > ,
168
170
) -> Result < Vec < Snapshot > > {
169
171
let worktree_dir = self . path . as_path ( ) ;
170
- let repo = git2:: Repository :: open ( worktree_dir) ?;
171
172
let gix_repo = gitbutler_command_context:: gix_repository_for_merging ( worktree_dir) ?;
172
173
173
- let traversal_root_id = match oplog_commit_id {
174
+ let traversal_root_id = git2_to_gix_object_id ( match oplog_commit_id {
174
175
Some ( id) => id,
175
176
None => {
176
177
let oplog_state = OplogHandle :: new ( & self . gb_dir ( ) ) ;
@@ -180,69 +181,86 @@ impl OplogExt for Project {
180
181
return Ok ( vec ! [ ] ) ;
181
182
}
182
183
}
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) ;
189
186
190
187
let mut snapshots = Vec :: new ( ) ;
188
+ let mut wd_trees_cache: HashMap < gix:: ObjectId , gix:: ObjectId > = HashMap :: new ( ) ;
191
189
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 ( ) ? {
195
191
if snapshots. len ( ) == limit {
196
192
break ;
197
193
}
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 ( ) {
202
199
break ;
203
200
}
204
201
205
202
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
+ {
207
207
// We reached a tree that is not a snapshot
208
208
tracing:: warn!( "Commit {commit_id} didn't seem to be an oplog commit - skipping" ) ;
209
209
continue ;
210
210
}
211
211
212
212
// 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) ?;
214
214
215
+ let commit_id = gix_to_git2_oid ( commit_id) ;
215
216
let details = commit
216
- . message ( )
217
+ . message_raw ( ) ?
218
+ . to_str ( )
219
+ . ok ( )
217
220
. and_then ( |msg| SnapshotDetails :: from_str ( msg) . ok ( ) ) ;
221
+ let commit_time = gix_time_to_git2 ( commit. time ( ) ?) ;
218
222
219
- if let Ok ( parent ) = commit . parent ( 0 ) {
223
+ if let Some ( parent_id ) = first_parent {
220
224
// 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) ) ?;
229
225
230
226
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
+ } ) ?;
237
256
238
- let stats = diff. stats ( ) ?;
239
257
snapshots. push ( Snapshot {
240
258
commit_id,
241
259
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 ,
244
262
files_changed,
245
- created_at : commit . time ( ) ,
263
+ created_at : commit_time ,
246
264
} ) ;
247
265
} else {
248
266
// this is the very first snapshot
@@ -252,7 +270,7 @@ impl OplogExt for Project {
252
270
lines_added : 0 ,
253
271
lines_removed : 0 ,
254
272
files_changed : Vec :: new ( ) ,
255
- created_at : commit . time ( ) ,
273
+ created_at : commit_time ,
256
274
} ) ;
257
275
break ;
258
276
}
@@ -318,21 +336,20 @@ impl OplogExt for Project {
318
336
319
337
/// Get a tree of the working dir (applied branches merged)
320
338
fn 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 ( ) ;
326
344
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) ) ;
329
347
}
330
348
}
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 ! (
332
350
"Could not get a tree of all applied virtual branches merged"
333
351
) ) ?;
334
- let wd_tree = repo. find_tree ( wd_tree_id. to_owned ( ) ) ?;
335
- Ok ( wd_tree)
352
+ Ok ( repo. find_tree ( id) ?)
336
353
}
337
354
338
355
fn prepare_snapshot ( ctx : & Project , _shared_access : & WorktreeReadPermission ) -> Result < git2:: Oid > {
0 commit comments