@@ -12,18 +12,19 @@ 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:: prelude:: { ObjectIdExt , Write } ;
27
28
use tracing:: instrument;
28
29
29
30
const SNAPSHOT_FILE_LIMIT_BYTES : u64 = 32 * 1024 * 1024 ;
@@ -167,10 +168,9 @@ impl OplogExt for Project {
167
168
oplog_commit_id : Option < git2:: Oid > ,
168
169
) -> Result < Vec < Snapshot > > {
169
170
let worktree_dir = self . path . as_path ( ) ;
170
- let repo = git2:: Repository :: open ( worktree_dir) ?;
171
171
let gix_repo = gitbutler_command_context:: gix_repository_for_merging ( worktree_dir) ?;
172
172
173
- let traversal_root_id = match oplog_commit_id {
173
+ let traversal_root_id = git2_to_gix_object_id ( match oplog_commit_id {
174
174
Some ( id) => id,
175
175
None => {
176
176
let oplog_state = OplogHandle :: new ( & self . gb_dir ( ) ) ;
@@ -180,69 +180,79 @@ impl OplogExt for Project {
180
180
return Ok ( vec ! [ ] ) ;
181
181
}
182
182
}
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) ;
189
185
190
186
let mut snapshots = Vec :: new ( ) ;
187
+ let mut wd_trees_cache: HashMap < gix:: ObjectId , gix:: ObjectId > = HashMap :: new ( ) ;
191
188
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 ( ) ? {
195
190
if snapshots. len ( ) == limit {
196
191
break ;
197
192
}
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 ( ) {
202
198
break ;
203
199
}
204
200
205
201
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
+ {
207
206
// We reached a tree that is not a snapshot
208
207
tracing:: warn!( "Commit {commit_id} didn't seem to be an oplog commit - skipping" ) ;
209
208
continue ;
210
209
}
211
210
212
211
// 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) ?;
214
213
214
+ let commit_id = gix_to_git2_oid ( commit_id) ;
215
215
let details = commit
216
- . message ( )
216
+ . message_raw ( ) ?
217
+ . to_str ( )
218
+ . ok ( )
217
219
. and_then ( |msg| SnapshotDetails :: from_str ( msg) . ok ( ) ) ;
220
+ let commit_time = gix_time_to_git2 ( commit. time ( ) ?) ;
218
221
219
- if let Ok ( parent ) = commit . parent ( 0 ) {
222
+ if let Some ( parent_id ) = first_parent {
220
223
// 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
224
230
225
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
+ } ) ?;
237
248
238
- let stats = diff. stats ( ) ?;
239
249
snapshots. push ( Snapshot {
240
250
commit_id,
241
251
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 ,
244
254
files_changed,
245
- created_at : commit . time ( ) ,
255
+ created_at : commit_time ,
246
256
} ) ;
247
257
} else {
248
258
// this is the very first snapshot
@@ -252,7 +262,7 @@ impl OplogExt for Project {
252
262
lines_added : 0 ,
253
263
lines_removed : 0 ,
254
264
files_changed : Vec :: new ( ) ,
255
- created_at : commit . time ( ) ,
265
+ created_at : commit_time ,
256
266
} ) ;
257
267
break ;
258
268
}
@@ -318,21 +328,20 @@ impl OplogExt for Project {
318
328
319
329
/// Get a tree of the working dir (applied branches merged)
320
330
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 > {
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 ( ) ;
326
336
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) ) ;
329
339
}
330
340
}
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 ! (
332
342
"Could not get a tree of all applied virtual branches merged"
333
343
) ) ?;
334
- let wd_tree = repo. find_tree ( wd_tree_id. to_owned ( ) ) ?;
335
- Ok ( wd_tree)
344
+ Ok ( repo. find_tree ( id) ?)
336
345
}
337
346
338
347
fn prepare_snapshot ( ctx : & Project , _shared_access : & WorktreeReadPermission ) -> Result < git2:: Oid > {
0 commit comments