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 {
@@ -101,7 +105,17 @@ impl RefInfo {
101
105
// top-to-bottom
102
106
. commits
103
107
. iter_mut ( )
104
- . take_while ( |c| c. relation == LocalCommitRelation :: LocalOnly )
108
+ . take_while ( |c| {
109
+ matches ! (
110
+ c. relation,
111
+ // This happens when the identity match with the remote didn't work.
112
+ LocalCommitRelation :: LocalOnly |
113
+ // This would be expected to be a remote-match by identity (we don't check for this),
114
+ // something that is determined during graph traversal time. But we want ot see
115
+ // if any of these is also integrated.
116
+ LocalCommitRelation :: LocalAndRemote ( _)
117
+ )
118
+ } )
105
119
{
106
120
let expensive = changeset_identifier ( repo, expensive. then_some ( local) ) ?;
107
121
if let Some ( upstream_commit_id) =
@@ -338,15 +352,19 @@ fn id_for_tree_diff(
338
352
// TODO(perf): use plumbing directly to avoid resource-cache overhead.
339
353
// consider parallelization
340
354
// really needs caching to be practical, in-memory might suffice for now.
341
- let changes = repo. diff_tree_to_tree (
342
- lhs_tree. as_ref ( ) ,
343
- & rhs_tree,
344
- * gix:: diff:: Options :: default ( )
345
- . track_path ( )
346
- // Rewrite tracking isn't needed for unique IDs and doesn't alter the validity,
347
- // but would cost time, making it useless.
348
- . 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,
349
366
) ?;
367
+ let changes = recorder. records ;
350
368
if changes. is_empty ( ) {
351
369
return Ok ( None ) ;
352
370
}
@@ -356,41 +374,73 @@ fn id_for_tree_diff(
356
374
357
375
// We rely on the diff order, it's consistent as rewrites are disabled.
358
376
for c in changes {
359
- 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 ( ) {
360
389
continue ;
361
390
}
362
- // For simplicity, use this type.
363
- let c = TreeChange :: from ( c) ;
364
391
// must hash all fields, even if None for unambiguous hashes.
365
- hash. update ( & c. path ) ;
366
- match c. status {
367
- TreeStatus :: Addition {
368
- state,
369
- // Ignore as untracked files can't happen with tree/tree diffs
370
- is_untracked : _,
392
+ hash. update ( location) ;
393
+ match c {
394
+ Change :: Addition {
395
+ entry_mode, oid, ..
371
396
} => {
372
397
hash. update ( b"A" ) ;
373
- 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
+ )
374
405
}
375
- TreeStatus :: Deletion { previous_state } => {
406
+ Change :: Deletion {
407
+ entry_mode, oid, ..
408
+ } => {
376
409
hash. update ( b"D" ) ;
377
- 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
+ ) ;
378
417
}
379
- TreeStatus :: Modification {
380
- previous_state,
381
- state,
382
- // Ignore as it's derived
383
- flags : _,
418
+ Change :: Modification {
419
+ previous_entry_mode,
420
+ previous_oid,
421
+ entry_mode,
422
+ oid,
423
+ ..
384
424
} => {
385
425
hash. update ( b"M" ) ;
386
- hash_change_state ( & mut hash, previous_state) ;
387
- hash_change_state ( & mut hash, state) ;
388
- }
389
- TreeStatus :: Rename { .. } => {
390
- 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
+ ) ;
391
440
}
392
441
}
393
442
}
443
+
394
444
Ok ( Some ( hash. try_finalize ( ) ?) )
395
445
}
396
446
0 commit comments