4
4
//! This property allows changeset IDs to be used to determine if two different commits, or sets of commits,
5
5
//! represent the same change.
6
6
7
- use std:: {
8
- borrow:: Borrow ,
9
- collections:: { HashMap , HashSet , hash_map:: Entry } ,
10
- } ;
11
-
12
7
use crate :: {
13
8
RefInfo ,
14
9
ref_info:: {
@@ -18,9 +13,15 @@ use crate::{
18
13
ui:: PushStatus ,
19
14
} ;
20
15
use bstr:: BString ;
21
- use but_core:: { ChangeState , TreeChange , TreeStatus , commit:: TreeKind } ;
16
+ use but_core:: { ChangeState , commit:: TreeKind } ;
17
+ use gix:: diff:: tree:: recorder:: Change ;
22
18
use gix:: { ObjectId , Repository , object:: tree:: EntryKind , prelude:: ObjectIdExt } ;
23
19
use itertools:: Itertools ;
20
+ use std:: ops:: Deref ;
21
+ use std:: {
22
+ borrow:: Borrow ,
23
+ collections:: { HashMap , HashSet , hash_map:: Entry } ,
24
+ } ;
24
25
25
26
/// The ID of a changeset, calculated as Git hash for convenience.
26
27
type ChangesetID = gix:: ObjectId ;
@@ -68,9 +69,12 @@ impl RefInfo {
68
69
self . compute_pushstatus ( ) ;
69
70
return Ok ( ( ) ) ;
70
71
} ;
72
+ let lower_bound_generation = self . lower_bound . map ( |sidx| graph[ sidx] . generation ) ;
71
73
graph. visit_all_segments_until ( target_tip, but_graph:: petgraph:: Direction :: Outgoing , |s| {
72
74
let prune = true ;
73
- if Some ( s. id ) == self . lower_bound {
75
+ if Some ( s. id ) == self . lower_bound
76
+ || lower_bound_generation. is_some_and ( |generation| s. generation > generation)
77
+ {
74
78
return prune;
75
79
}
76
80
for c in & s. commits {
@@ -348,15 +352,19 @@ fn id_for_tree_diff(
348
352
// TODO(perf): use plumbing directly to avoid resource-cache overhead.
349
353
// consider parallelization
350
354
// really needs caching to be practical, in-memory might suffice for now.
351
- let changes = repo. diff_tree_to_tree (
352
- lhs_tree. as_ref ( ) ,
353
- & rhs_tree,
354
- * gix:: diff:: Options :: default ( )
355
- . track_path ( )
356
- // Rewrite tracking isn't needed for unique IDs and doesn't alter the validity,
357
- // but would cost time, making it useless.
358
- . track_rewrites ( None ) ,
355
+
356
+ let empty_tree = repo. empty_tree ( ) ;
357
+ let mut state = Default :: default ( ) ;
358
+ let mut recorder = gix:: diff:: tree:: Recorder :: default ( )
359
+ . track_location ( Some ( gix:: diff:: tree:: recorder:: Location :: Path ) ) ;
360
+ gix:: diff:: tree (
361
+ gix:: objs:: TreeRefIter :: from_bytes ( & lhs_tree. unwrap_or ( empty_tree) . data ) ,
362
+ gix:: objs:: TreeRefIter :: from_bytes ( & rhs_tree. data ) ,
363
+ & mut state,
364
+ repo. objects . deref ( ) ,
365
+ & mut recorder,
359
366
) ?;
367
+ let changes = recorder. records ;
360
368
if changes. is_empty ( ) {
361
369
return Ok ( None ) ;
362
370
}
@@ -366,41 +374,73 @@ fn id_for_tree_diff(
366
374
367
375
// We rely on the diff order, it's consistent as rewrites are disabled.
368
376
for c in changes {
369
- if c. entry_mode ( ) . is_tree ( ) {
377
+ let ( entry_mode, location) = match & c {
378
+ Change :: Addition {
379
+ entry_mode, path, ..
380
+ }
381
+ | Change :: Deletion {
382
+ entry_mode, path, ..
383
+ }
384
+ | Change :: Modification {
385
+ entry_mode, path, ..
386
+ } => ( * entry_mode, path) ,
387
+ } ;
388
+ if entry_mode. is_tree ( ) {
370
389
continue ;
371
390
}
372
- // For simplicity, use this type.
373
- let c = TreeChange :: from ( c) ;
374
391
// must hash all fields, even if None for unambiguous hashes.
375
- hash. update ( & c. path ) ;
376
- match c. status {
377
- TreeStatus :: Addition {
378
- state,
379
- // Ignore as untracked files can't happen with tree/tree diffs
380
- is_untracked : _,
392
+ hash. update ( location) ;
393
+ match c {
394
+ Change :: Addition {
395
+ entry_mode, oid, ..
381
396
} => {
382
397
hash. update ( b"A" ) ;
383
- hash_change_state ( & mut hash, state)
398
+ hash_change_state (
399
+ & mut hash,
400
+ ChangeState {
401
+ id : oid,
402
+ kind : entry_mode. kind ( ) ,
403
+ } ,
404
+ )
384
405
}
385
- TreeStatus :: Deletion { previous_state } => {
406
+ Change :: Deletion {
407
+ entry_mode, oid, ..
408
+ } => {
386
409
hash. update ( b"D" ) ;
387
- hash_change_state ( & mut hash, previous_state)
410
+ hash_change_state (
411
+ & mut hash,
412
+ ChangeState {
413
+ id : oid,
414
+ kind : entry_mode. kind ( ) ,
415
+ } ,
416
+ ) ;
388
417
}
389
- TreeStatus :: Modification {
390
- previous_state,
391
- state,
392
- // Ignore as it's derived
393
- flags : _,
418
+ Change :: Modification {
419
+ previous_entry_mode,
420
+ previous_oid,
421
+ entry_mode,
422
+ oid,
423
+ ..
394
424
} => {
395
425
hash. update ( b"M" ) ;
396
- hash_change_state ( & mut hash, previous_state) ;
397
- hash_change_state ( & mut hash, state) ;
398
- }
399
- TreeStatus :: Rename { .. } => {
400
- unreachable ! ( "disabled in prior configuration" )
426
+ hash_change_state (
427
+ & mut hash,
428
+ ChangeState {
429
+ id : previous_oid,
430
+ kind : previous_entry_mode. kind ( ) ,
431
+ } ,
432
+ ) ;
433
+ hash_change_state (
434
+ & mut hash,
435
+ ChangeState {
436
+ id : oid,
437
+ kind : entry_mode. kind ( ) ,
438
+ } ,
439
+ ) ;
401
440
}
402
441
}
403
442
}
443
+
404
444
Ok ( Some ( hash. try_finalize ( ) ?) )
405
445
}
406
446
0 commit comments