1
1
use crate :: { CommitFlags , Edge } ;
2
2
use crate :: { CommitIndex , Graph , Segment , SegmentIndex , SegmentMetadata } ;
3
- use anyhow:: { Context , bail} ;
3
+ use anyhow:: bail;
4
4
use but_core:: RefMetadata ;
5
- use gix:: ObjectId ;
6
5
use gix:: hashtable:: hash_map:: Entry ;
7
6
use gix:: prelude:: { ObjectIdExt , ReferenceExt } ;
8
7
use gix:: refs:: Category ;
9
8
use petgraph:: graph:: EdgeReference ;
10
9
use petgraph:: prelude:: EdgeRef ;
11
10
use std:: collections:: VecDeque ;
11
+ use tracing:: instrument;
12
12
13
13
mod utils;
14
14
use utils:: * ;
@@ -27,39 +27,41 @@ pub struct Options {
27
27
///
28
28
/// If `false`, tags are not collected.
29
29
pub collect_tags : bool ,
30
- /// The maximum number of commits we should traverse outside any workspace *with a target branch* .
30
+ /// The (soft) maximum number of commits we should traverse.
31
31
/// Workspaces with a target branch automatically have unlimited traversals as they rely on the target
32
32
/// branch to eventually stop the traversal.
33
33
///
34
34
/// If `None`, there is no limit, which typically means that when lacking a workspace, the traversal
35
35
/// will end only when no commit is left to traverse.
36
- /// `Some(0)` means nothing is going to be returned.
36
+ /// `Some(0)` means nothing but the first commit is going to be returned, but it should be avoided .
37
37
///
38
38
/// Note that this doesn't affect the traversal of integrated commits, which is always stopped once there
39
39
/// is nothing interesting left to traverse.
40
40
///
41
- /// Also note: This is not a perfectly exact measure, and it's always possible to receive a few more commits
42
- /// than the maximum as for simplicity, we assign each 'split' the same limit, effectively doubling it.
41
+ /// Also note: This is a hint and not an exact measure, and it's always possible to receive a more commits
42
+ /// for various reasons, for instance the need to let remote branches find their local brnach independently
43
+ /// of the limit.
43
44
///
44
45
/// ### Tip Configuration
45
46
///
46
47
/// * HEAD - uses the limit
47
48
/// * workspaces with target branch - no limit, but auto-stop if workspace is exhausted as everything is integrated.
48
49
/// - The target branch: no limit
50
+ /// - Integrated workspace branches: use the limit
49
51
/// * workspace without target branch - uses the limit
50
- /// * remotes tracking branches - use the limit
51
- pub max_commits_outside_of_workspace : Option < usize > ,
52
+ /// * remotes tracking branches - use the limit, but only once they have reached a local branch.
53
+ pub commits_limit_hint : Option < usize > ,
52
54
/// A list of the last commits of partial segments previously returned that reset the amount of available
53
- /// commits to traverse back to `max_commits_outside_of_workspace `.
55
+ /// commits to traverse back to `commit_limit_hint `.
54
56
/// Imagine it like a gas station that can be chosen to direct where the commit-budge should be spent.
55
- pub max_commits_recharge_location : Vec < gix:: ObjectId > ,
57
+ pub commits_limit_recharge_location : Vec < gix:: ObjectId > ,
56
58
}
57
59
58
60
/// Builder
59
61
impl Options {
60
62
/// Set the maximum amount of commits that each lane in a tip may traverse.
61
63
pub fn with_limit ( mut self , limit : usize ) -> Self {
62
- self . max_commits_outside_of_workspace = Some ( limit) ;
64
+ self . commits_limit_hint = Some ( limit) ;
63
65
self
64
66
}
65
67
@@ -68,7 +70,7 @@ impl Options {
68
70
mut self ,
69
71
commits : impl IntoIterator < Item = gix:: ObjectId > ,
70
72
) -> Self {
71
- self . max_commits_recharge_location . extend ( commits) ;
73
+ self . commits_limit_recharge_location . extend ( commits) ;
72
74
self
73
75
}
74
76
}
@@ -146,16 +148,18 @@ impl Graph {
146
148
/// * The traversal is cut short when there is only tips which are integrated, even though named segments that are
147
149
/// supposed to be in the workspace will be fully traversed (implying they will stop at the first anon segment
148
150
/// as will happen at merge commits).
151
+ #[ instrument( skip( meta, ref_name) , err( Debug ) ) ]
149
152
pub fn from_commit_traversal (
150
153
tip : gix:: Id < ' _ > ,
151
154
ref_name : impl Into < Option < gix:: refs:: FullName > > ,
152
155
meta : & impl RefMetadata ,
153
156
Options {
154
157
collect_tags,
155
- max_commits_outside_of_workspace : limit,
156
- mut max_commits_recharge_location,
158
+ commits_limit_hint : limit,
159
+ commits_limit_recharge_location : mut max_commits_recharge_location,
157
160
} : Options ,
158
161
) -> anyhow:: Result < Self > {
162
+ let limit = Limit ( limit) ;
159
163
// TODO: also traverse (outside)-branches that ought to be in the workspace. That way we have the desired ones
160
164
// automatically and just have to find a way to prune the undesired ones.
161
165
let repo = tip. repo ;
@@ -249,7 +253,7 @@ impl Graph {
249
253
let mut ws_segment = branch_segment_from_name_and_meta ( Some ( ws_ref) , meta, None ) ?;
250
254
// Drop the limit if we have a target ref
251
255
let limit = if workspace_info. target_ref . is_some ( ) {
252
- None
256
+ Limit :: unspecified ( )
253
257
} else {
254
258
limit
255
259
} ;
@@ -279,22 +283,23 @@ impl Graph {
279
283
Instruction :: CollectCommit {
280
284
into : target_segment,
281
285
} ,
282
- /* unlimited traversal for 'negative' commits */
283
- None ,
286
+ /* unlimited traversal for integrated commits */
287
+ Limit :: unspecified ( ) ,
284
288
) ) ;
285
289
}
286
290
}
287
291
288
292
max_commits_recharge_location. sort ( ) ;
289
293
// Set max-limit so that we compensate for the way this is counted.
290
- let max_limit = limit. map ( |l| l + 1 ) ;
294
+ // let max_limit = limit.incremented();
295
+ let max_limit = limit;
291
296
while let Some ( ( id, mut propagated_flags, instruction, mut limit) ) = next. pop_front ( ) {
292
297
if max_commits_recharge_location. binary_search ( & id) . is_ok ( ) {
293
298
limit = max_limit;
294
299
}
295
- if limit. is_some_and ( |l| l == 0 ) {
296
- continue ;
297
- }
300
+ // if limit.is_exhausted( ) {
301
+ // continue;
302
+ // }
298
303
let info = find ( commit_graph. as_ref ( ) , repo, id, & mut buf) ?;
299
304
let src_flags = graph[ instruction. segment_idx ( ) ]
300
305
. commits
@@ -307,67 +312,15 @@ impl Graph {
307
312
propagated_flags |= src_flags;
308
313
let segment_idx_for_id = match instruction {
309
314
Instruction :: CollectCommit { into : src_sidx } => match seen. entry ( id) {
310
- Entry :: Occupied ( mut existing_sidx) => {
311
- let dst_sidx = * existing_sidx. get ( ) ;
312
- let ( top_sidx, mut bottom_sidx) =
313
- // If a normal branch walks into a workspace branch, put the workspace branch on top.
314
- if graph[ dst_sidx] . workspace_metadata ( ) . is_some ( ) &&
315
- graph[ src_sidx] . ref_name . as_ref ( )
316
- . is_some_and ( |rn| rn. category ( ) . is_some_and ( |c| matches ! ( c, Category :: LocalBranch ) ) ) {
317
- // `dst` is basically swapping with `src`, so must swap commits and connections.
318
- swap_commits_and_connections ( & mut graph. inner , dst_sidx, src_sidx) ;
319
- swap_queued_segments ( & mut next, dst_sidx, src_sidx) ;
320
-
321
- // Assure the first commit doesn't name the new owner segment.
322
- {
323
- let s = & mut graph[ src_sidx] ;
324
- if let Some ( c) = s. commits . first_mut ( ) {
325
- c. refs . retain ( |rn| Some ( rn) != s. ref_name . as_ref ( ) )
326
- }
327
- // Update the commit-ownership of the connecting commit, but also
328
- // of all other commits in the segment.
329
- existing_sidx. insert ( src_sidx) ;
330
- for commit_id in s. commits . iter ( ) . skip ( 1 ) . map ( |c| c. id ) {
331
- seen. entry ( commit_id) . insert ( src_sidx) ;
332
- }
333
- }
334
- ( dst_sidx, src_sidx)
335
- } else {
336
- // `src` naturally runs into destination, so nothing needs to be done
337
- // except for connecting both. Commit ownership doesn't change.
338
- ( src_sidx, dst_sidx)
339
- } ;
340
- let top_cidx = graph[ top_sidx] . last_commit_index ( ) ;
341
- let mut bottom_cidx =
342
- graph[ bottom_sidx] . commit_index_of ( id) . with_context ( || {
343
- format ! (
344
- "BUG: Didn't find commit {id} in segment {bottom_sidx}" ,
345
- bottom_sidx = dst_sidx. index( ) ,
346
- )
347
- } ) ?;
348
-
349
- if bottom_cidx != 0 {
350
- let new_bottom_sidx = split_commit_into_segment (
351
- & mut graph,
352
- & mut next,
353
- & mut seen,
354
- bottom_sidx,
355
- bottom_cidx,
356
- ) ?;
357
- bottom_sidx = new_bottom_sidx;
358
- bottom_cidx = 0 ;
359
- }
360
- graph. connect_segments ( top_sidx, top_cidx, bottom_sidx, bottom_cidx) ;
361
- let top_flags = top_cidx
362
- . map ( |cidx| graph[ top_sidx] . commits [ cidx] . flags )
363
- . unwrap_or_default ( ) ;
364
- let bottom_flags = graph[ bottom_sidx] . commits [ bottom_cidx] . flags ;
365
- propagate_flags_downward (
366
- & mut graph. inner ,
367
- propagated_flags | top_flags | bottom_flags,
368
- bottom_sidx,
369
- Some ( bottom_cidx) ,
370
- ) ;
315
+ Entry :: Occupied ( _) => {
316
+ possibly_split_occupied_segment (
317
+ & mut graph,
318
+ & mut seen,
319
+ & mut next,
320
+ id,
321
+ propagated_flags,
322
+ src_sidx,
323
+ ) ?;
371
324
continue ;
372
325
}
373
326
Entry :: Vacant ( e) => {
@@ -387,23 +340,15 @@ impl Graph {
387
340
parent_above,
388
341
at_commit,
389
342
} => match seen. entry ( id) {
390
- Entry :: Occupied ( existing_sidx) => {
391
- let bottom_sidx = * existing_sidx. get ( ) ;
392
- let bottom = & graph[ bottom_sidx] ;
393
- let bottom_cidx = bottom. commit_index_of ( id) . context (
394
- "BUG: bottom segment must contain ID, `seen` seems out of date" ,
343
+ Entry :: Occupied ( _) => {
344
+ possibly_split_occupied_segment (
345
+ & mut graph,
346
+ & mut seen,
347
+ & mut next,
348
+ id,
349
+ propagated_flags,
350
+ parent_above,
395
351
) ?;
396
- if bottom_cidx != 0 {
397
- todo ! ( "split bottom segment at `at_commit`" ) ;
398
- }
399
- let bottom_flags = bottom. commits [ bottom_cidx] . flags ;
400
- graph. connect_segments ( parent_above, at_commit, bottom_sidx, bottom_cidx) ;
401
- propagate_flags_downward (
402
- & mut graph. inner ,
403
- propagated_flags | bottom_flags,
404
- bottom_sidx,
405
- Some ( bottom_cidx) ,
406
- ) ;
407
352
continue ;
408
353
}
409
354
Entry :: Vacant ( e) => {
@@ -463,7 +408,7 @@ impl Graph {
463
408
limit,
464
409
) ?;
465
410
466
- prune_integrated_tips ( & mut graph. inner , & mut next, & desired_refs) ;
411
+ prune_integrated_tips ( & mut graph. inner , & mut next, & desired_refs, max_limit ) ;
467
412
}
468
413
469
414
graph. post_processed (
@@ -476,6 +421,9 @@ impl Graph {
476
421
}
477
422
}
478
423
424
+ #[ derive( Debug , Copy , Clone ) ]
425
+ struct Limit ( Option < usize > ) ;
426
+
479
427
#[ derive( Debug , Copy , Clone ) ]
480
428
enum Instruction {
481
429
/// Contains the segment into which to place this commit.
@@ -511,7 +459,7 @@ impl Instruction {
511
459
}
512
460
}
513
461
514
- type QueueItem = ( ObjectId , CommitFlags , Instruction , Option < usize > ) ;
462
+ type QueueItem = ( gix :: ObjectId , CommitFlags , Instruction , Limit ) ;
515
463
516
464
#[ derive( Debug ) ]
517
465
pub ( crate ) struct EdgeOwned {
0 commit comments