@@ -21,12 +21,56 @@ mod walk;
21
21
pub ( super ) type PetGraph = petgraph:: Graph < Segment , Edge > ;
22
22
23
23
/// Options for use in [`Graph::from_head()`] and [`Graph::from_commit_traversal()`].
24
- #[ derive( Default , Debug , Copy , Clone ) ]
24
+ #[ derive( Default , Debug , Clone ) ]
25
25
pub struct Options {
26
26
/// Associate tag references with commits.
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*.
31
+ /// Workspaces with a target branch automatically have unlimited traversals as they rely on the target
32
+ /// branch to eventually stop the traversal.
33
+ ///
34
+ /// If `None`, there is no limit, which typically means that when lacking a workspace, the traversal
35
+ /// will end only when no commit is left to traverse.
36
+ /// `Some(0)` means nothing is going to be returned.
37
+ ///
38
+ /// Note that this doesn't affect the traversal of integrated commits, which is always stopped once there
39
+ /// is nothing interesting left to traverse.
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.
43
+ ///
44
+ /// ### Tip Configuration
45
+ ///
46
+ /// * HEAD - uses the limit
47
+ /// * workspaces with target branch - no limit, but auto-stop if workspace is exhausted as everything is integrated.
48
+ /// - The target branch: no limit
49
+ /// * workspace without target branch - uses the limit
50
+ /// * remotes tracking branches - use the limit
51
+ pub max_commits_outside_of_workspace : Option < usize > ,
52
+ /// 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`.
54
+ /// 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 > ,
56
+ }
57
+
58
+ /// Builder
59
+ impl Options {
60
+ /// Set the maximum amount of commits that each lane in a tip may traverse.
61
+ pub fn with_limit ( mut self , limit : usize ) -> Self {
62
+ self . max_commits_outside_of_workspace = Some ( limit) ;
63
+ self
64
+ }
65
+
66
+ /// Keep track of commits at which the traversal limit should be reset to the [`limit`](Self::with_limit()).
67
+ pub fn with_limit_extension_at (
68
+ mut self ,
69
+ commits : impl IntoIterator < Item = gix:: ObjectId > ,
70
+ ) -> Self {
71
+ self . max_commits_recharge_location . extend ( commits) ;
72
+ self
73
+ }
30
74
}
31
75
32
76
/// Lifecycle
@@ -106,7 +150,11 @@ impl Graph {
106
150
tip : gix:: Id < ' _ > ,
107
151
ref_name : impl Into < Option < gix:: refs:: FullName > > ,
108
152
meta : & impl RefMetadata ,
109
- Options { collect_tags } : Options ,
153
+ Options {
154
+ collect_tags,
155
+ max_commits_outside_of_workspace : limit,
156
+ mut max_commits_recharge_location,
157
+ } : Options ,
110
158
) -> anyhow:: Result < Self > {
111
159
// TODO: also traverse (outside)-branches that ought to be in the workspace. That way we have the desired ones
112
160
// automatically and just have to find a way to prune the undesired ones.
@@ -168,6 +216,7 @@ impl Graph {
168
216
tip. detach ( ) ,
169
217
tip_flags,
170
218
Instruction :: CollectCommit { into : current } ,
219
+ limit,
171
220
) ) ;
172
221
}
173
222
for ( ws_ref, workspace_info) in workspaces {
@@ -198,6 +247,12 @@ impl Graph {
198
247
CommitFlags :: empty ( )
199
248
} ;
200
249
let mut ws_segment = branch_segment_from_name_and_meta ( Some ( ws_ref) , meta, None ) ?;
250
+ // Drop the limit if we have a target ref
251
+ let limit = if workspace_info. target_ref . is_some ( ) {
252
+ None
253
+ } else {
254
+ limit
255
+ } ;
201
256
ws_segment. metadata = Some ( SegmentMetadata :: Workspace ( workspace_info) ) ;
202
257
let ws_segment = graph. insert_root ( ws_segment) ;
203
258
// As workspaces typically have integration branches which can help us to stop the traversal,
@@ -210,6 +265,7 @@ impl Graph {
210
265
// their status for now.
211
266
CommitFlags :: NotInRemote | add_flags,
212
267
Instruction :: CollectCommit { into : ws_segment } ,
268
+ limit,
213
269
) ) ;
214
270
if let Some ( ( target_ref, target_ref_id) ) = target {
215
271
let target_segment = graph. insert_root ( branch_segment_from_name_and_meta (
@@ -223,11 +279,22 @@ impl Graph {
223
279
Instruction :: CollectCommit {
224
280
into : target_segment,
225
281
} ,
282
+ /* unlimited traversal for 'negative' commits */
283
+ None ,
226
284
) ) ;
227
285
}
228
286
}
229
287
230
- while let Some ( ( id, mut propagated_flags, instruction) ) = next. pop_front ( ) {
288
+ max_commits_recharge_location. sort ( ) ;
289
+ // Set max-limit so that we compensate for the way this is counted.
290
+ let max_limit = limit. map ( |l| l + 1 ) ;
291
+ while let Some ( ( id, mut propagated_flags, instruction, mut limit) ) = next. pop_front ( ) {
292
+ if max_commits_recharge_location. binary_search ( & id) . is_ok ( ) {
293
+ limit = max_limit;
294
+ }
295
+ if limit. is_some_and ( |l| l == 0 ) {
296
+ continue ;
297
+ }
231
298
let info = find ( commit_graph. as_ref ( ) , repo, id, & mut buf) ?;
232
299
let src_flags = graph[ instruction. segment_idx ( ) ]
233
300
. commits
@@ -363,6 +430,7 @@ impl Graph {
363
430
propagated_flags,
364
431
segment_idx_for_id,
365
432
commit_idx_for_possible_fork,
433
+ limit,
366
434
) ;
367
435
368
436
let refs_at_commit_before_removal = refs_by_id. remove ( & id) . unwrap_or_default ( ) ;
@@ -392,6 +460,7 @@ impl Graph {
392
460
& configured_remote_tracking_branches,
393
461
& target_refs,
394
462
meta,
463
+ limit,
395
464
) ?;
396
465
397
466
prune_integrated_tips ( & mut graph. inner , & mut next, & desired_refs) ;
@@ -442,7 +511,7 @@ impl Instruction {
442
511
}
443
512
}
444
513
445
- type QueueItem = ( ObjectId , CommitFlags , Instruction ) ;
514
+ type QueueItem = ( ObjectId , CommitFlags , Instruction , Option < usize > ) ;
446
515
447
516
#[ derive( Debug ) ]
448
517
pub ( crate ) struct EdgeOwned {
0 commit comments