@@ -21,12 +21,48 @@ 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
+ pub max_commits_outside_of_workspace : Option < usize > ,
44
+ /// A list of the last commits of partial segments previously returned that reset the amount of available
45
+ /// commits to traverse back to `max_commits_outside_of_workspace`.
46
+ /// Imagine it like a gas station that can be chosen to direct where the commit-budge should be spent.
47
+ pub max_commits_recharge_location : Vec < gix:: ObjectId > ,
48
+ }
49
+
50
+ /// Builder
51
+ impl Options {
52
+ /// Set the maximum amount of commits that each lane in a tip may traverse.
53
+ pub fn with_limit ( mut self , limit : usize ) -> Self {
54
+ self . max_commits_outside_of_workspace = Some ( limit) ;
55
+ self
56
+ }
57
+
58
+ /// Keep track of commits at which the traversal limit should be reset to the [`limit`](Self::with_limit()).
59
+ pub fn with_limit_extension_at (
60
+ mut self ,
61
+ commits : impl IntoIterator < Item = gix:: ObjectId > ,
62
+ ) -> Self {
63
+ self . max_commits_recharge_location . extend ( commits) ;
64
+ self
65
+ }
30
66
}
31
67
32
68
/// Lifecycle
@@ -106,7 +142,11 @@ impl Graph {
106
142
tip : gix:: Id < ' _ > ,
107
143
ref_name : impl Into < Option < gix:: refs:: FullName > > ,
108
144
meta : & impl RefMetadata ,
109
- Options { collect_tags } : Options ,
145
+ Options {
146
+ collect_tags,
147
+ max_commits_outside_of_workspace : limit,
148
+ mut max_commits_recharge_location,
149
+ } : Options ,
110
150
) -> anyhow:: Result < Self > {
111
151
// TODO: also traverse (outside)-branches that ought to be in the workspace. That way we have the desired ones
112
152
// automatically and just have to find a way to prune the undesired ones.
@@ -168,6 +208,7 @@ impl Graph {
168
208
tip. detach ( ) ,
169
209
tip_flags,
170
210
Instruction :: CollectCommit { into : current } ,
211
+ limit,
171
212
) ) ;
172
213
}
173
214
for ( ws_ref, workspace_info) in workspaces {
@@ -198,6 +239,12 @@ impl Graph {
198
239
CommitFlags :: empty ( )
199
240
} ;
200
241
let mut ws_segment = branch_segment_from_name_and_meta ( Some ( ws_ref) , meta, None ) ?;
242
+ // Drop the limit if we have a target ref
243
+ let limit = if workspace_info. target_ref . is_some ( ) {
244
+ None
245
+ } else {
246
+ limit
247
+ } ;
201
248
ws_segment. metadata = Some ( SegmentMetadata :: Workspace ( workspace_info) ) ;
202
249
let ws_segment = graph. insert_root ( ws_segment) ;
203
250
// As workspaces typically have integration branches which can help us to stop the traversal,
@@ -210,6 +257,7 @@ impl Graph {
210
257
// their status for now.
211
258
CommitFlags :: NotInRemote | add_flags,
212
259
Instruction :: CollectCommit { into : ws_segment } ,
260
+ limit,
213
261
) ) ;
214
262
if let Some ( ( target_ref, target_ref_id) ) = target {
215
263
let target_segment = graph. insert_root ( branch_segment_from_name_and_meta (
@@ -223,11 +271,22 @@ impl Graph {
223
271
Instruction :: CollectCommit {
224
272
into : target_segment,
225
273
} ,
274
+ /* unlimited traversal for 'negative' commits */
275
+ None ,
226
276
) ) ;
227
277
}
228
278
}
229
279
230
- while let Some ( ( id, mut propagated_flags, instruction) ) = next. pop_front ( ) {
280
+ max_commits_recharge_location. sort ( ) ;
281
+ // Set max-limit so that we compensate for the way this is counted.
282
+ let max_limit = limit. map ( |l| l + 1 ) ;
283
+ while let Some ( ( id, mut propagated_flags, instruction, mut limit) ) = next. pop_front ( ) {
284
+ if max_commits_recharge_location. binary_search ( & id) . is_ok ( ) {
285
+ limit = max_limit;
286
+ }
287
+ if limit. is_some_and ( |l| l == 0 ) {
288
+ continue ;
289
+ }
231
290
let info = find ( commit_graph. as_ref ( ) , repo, id, & mut buf) ?;
232
291
let src_flags = graph[ instruction. segment_idx ( ) ]
233
292
. commits
@@ -363,6 +422,7 @@ impl Graph {
363
422
propagated_flags,
364
423
segment_idx_for_id,
365
424
commit_idx_for_possible_fork,
425
+ limit,
366
426
) ;
367
427
368
428
let refs_at_commit_before_removal = refs_by_id. remove ( & id) . unwrap_or_default ( ) ;
@@ -392,6 +452,7 @@ impl Graph {
392
452
& configured_remote_tracking_branches,
393
453
& target_refs,
394
454
meta,
455
+ limit,
395
456
) ?;
396
457
397
458
prune_integrated_tips ( & mut graph. inner , & mut next, & desired_refs) ;
@@ -442,7 +503,7 @@ impl Instruction {
442
503
}
443
504
}
444
505
445
- type QueueItem = ( ObjectId , CommitFlags , Instruction ) ;
506
+ type QueueItem = ( ObjectId , CommitFlags , Instruction , Option < usize > ) ;
446
507
447
508
#[ derive( Debug ) ]
448
509
pub ( crate ) struct EdgeOwned {
0 commit comments