@@ -206,15 +206,18 @@ impl Graph {
206
206
} ) ,
207
207
) ?;
208
208
let ( workspaces, target_refs, desired_refs) =
209
- obtain_workspace_infos ( ref_name. as_ref ( ) . map ( |rn| rn. as_ref ( ) ) , meta) ?;
209
+ obtain_workspace_infos ( repo , ref_name. as_ref ( ) . map ( |rn| rn. as_ref ( ) ) , meta) ?;
210
210
let mut seen = gix:: revwalk:: graph:: IdMap :: < SegmentIndex > :: default ( ) ;
211
- let tip_flags = CommitFlags :: NotInRemote ;
211
+ let mut goals = Goals :: default ( ) ;
212
+ let tip_limit_with_goal = limit. with_goal ( tip. detach ( ) , & mut goals) ;
213
+ // The tip transports itself.
214
+ let tip_flags = CommitFlags :: NotInRemote | tip_limit_with_goal. goal ;
212
215
213
216
let target_symbolic_remote_names = {
214
217
let remote_names = repo. remote_names ( ) ;
215
218
let mut v: Vec < _ > = workspaces
216
219
. iter ( )
217
- . filter_map ( |( _, data) | {
220
+ . filter_map ( |( _, _ , data) | {
218
221
let target_ref = data. target_ref . as_ref ( ) ?;
219
222
remotes:: extract_remote_name ( target_ref. as_ref ( ) , & remote_names)
220
223
} )
@@ -227,7 +230,7 @@ impl Graph {
227
230
let mut next = Queue :: new_with_limit ( hard_limit) ;
228
231
if !workspaces
229
232
. iter ( )
230
- . any ( |( wsrn, _) | Some ( wsrn) == ref_name. as_ref ( ) )
233
+ . any ( |( _ , wsrn, _) | Some ( wsrn) == ref_name. as_ref ( ) )
231
234
{
232
235
let current = graph. insert_root ( branch_segment_from_name_and_meta (
233
236
ref_name. clone ( ) ,
@@ -243,14 +246,7 @@ impl Graph {
243
246
return Ok ( graph. with_hard_limit ( ) ) ;
244
247
}
245
248
}
246
- for ( ws_ref, workspace_info) in workspaces {
247
- let Some ( ws_tip) = try_refname_to_id ( repo, ws_ref. as_ref ( ) ) ? else {
248
- tracing:: warn!(
249
- "Ignoring stale workspace ref '{ws_ref}', which didn't exist in Git but still had workspace data" ,
250
- ws_ref = ws_ref. as_bstr( )
251
- ) ;
252
- continue ;
253
- } ;
249
+ for ( ws_tip, ws_ref, workspace_info) in workspaces {
254
250
let target = workspace_info. target_ref . as_ref ( ) . and_then ( |trn| {
255
251
try_refname_to_id ( repo, trn. as_ref ( ) )
256
252
. map_err ( |err| {
@@ -265,15 +261,16 @@ impl Graph {
265
261
. map ( |tid| ( trn. clone ( ) , tid) )
266
262
} ) ;
267
263
268
- let add_flags = if Some ( & ws_ref) == ref_name. as_ref ( ) {
269
- tip_flags
264
+ let ( ws_flags , ws_limit ) = if Some ( & ws_ref) == ref_name. as_ref ( ) {
265
+ ( tip_flags, limit )
270
266
} else {
271
- CommitFlags :: empty ( )
267
+ ( CommitFlags :: empty ( ) , tip_limit_with_goal )
272
268
} ;
273
269
let mut ws_segment = branch_segment_from_name_and_meta ( Some ( ws_ref) , meta, None ) ?;
274
- // Drop the limit if we have a target ref
275
- let limit = if workspace_info. target_ref . is_some ( ) {
276
- limit. with_goal ( tip. detach ( ) )
270
+ // The limits for the target ref and the worktree ref are synced so they can always find each other,
271
+ // while being able to stop when the entrypoint is included.
272
+ let target_limit = if workspace_info. target_ref . is_some ( ) {
273
+ tip_limit_with_goal
277
274
} else {
278
275
limit
279
276
} ;
@@ -287,9 +284,9 @@ impl Graph {
287
284
// We only allow workspaces that are not remote, and that are not target refs.
288
285
// Theoretically they can still cross-reference each other, but then we'd simply ignore
289
286
// their status for now.
290
- CommitFlags :: NotInRemote | add_flags ,
287
+ CommitFlags :: NotInRemote | ws_flags ,
291
288
Instruction :: CollectCommit { into : ws_segment } ,
292
- limit ,
289
+ ws_limit ,
293
290
) ) {
294
291
return Ok ( graph. with_hard_limit ( ) ) ;
295
292
}
@@ -305,8 +302,7 @@ impl Graph {
305
302
Instruction :: CollectCommit {
306
303
into : target_segment,
307
304
} ,
308
- /* unlimited traversal for integrated commits */
309
- limit. with_goal ( tip. detach ( ) ) ,
305
+ target_limit,
310
306
) ) {
311
307
return Ok ( graph. with_hard_limit ( ) ) ;
312
308
}
@@ -319,7 +315,7 @@ impl Graph {
319
315
let max_limit = limit;
320
316
while let Some ( ( id, mut propagated_flags, instruction, mut limit) ) = next. pop_front ( ) {
321
317
if max_commits_recharge_location. binary_search ( & id) . is_ok ( ) {
322
- limit. inner = max_limit. inner ;
318
+ limit. set_but_keep_goal ( max_limit) ;
323
319
}
324
320
let info = find ( commit_graph. as_ref ( ) , repo, id, & mut buf) ?;
325
321
let src_flags = graph[ instruction. segment_idx ( ) ]
@@ -390,8 +386,23 @@ impl Graph {
390
386
} ,
391
387
} ;
392
388
389
+ let refs_at_commit_before_removal = refs_by_id. remove ( & id) . unwrap_or_default ( ) ;
390
+ let ( remote_items, maybe_goal_for_id) = try_queue_remote_tracking_branches (
391
+ repo,
392
+ & refs_at_commit_before_removal,
393
+ & mut graph,
394
+ & target_symbolic_remote_names,
395
+ & configured_remote_tracking_branches,
396
+ & target_refs,
397
+ meta,
398
+ id,
399
+ limit,
400
+ & mut goals,
401
+ ) ?;
402
+
393
403
let segment = & mut graph[ segment_idx_for_id] ;
394
404
let commit_idx_for_possible_fork = segment. commits . len ( ) ;
405
+ let propagated_flags = propagated_flags | maybe_goal_for_id;
395
406
let hard_limit_hit = queue_parents (
396
407
& mut next,
397
408
& info. parent_ids ,
@@ -404,7 +415,6 @@ impl Graph {
404
415
return Ok ( graph. with_hard_limit ( ) ) ;
405
416
}
406
417
407
- let refs_at_commit_before_removal = refs_by_id. remove ( & id) . unwrap_or_default ( ) ;
408
418
segment. commits . push (
409
419
info. into_commit (
410
420
repo,
@@ -422,20 +432,10 @@ impl Graph {
422
432
) ?,
423
433
) ;
424
434
425
- let hard_limit_hit = try_queue_remote_tracking_branches (
426
- repo,
427
- & refs_at_commit_before_removal,
428
- & mut next,
429
- & mut graph,
430
- & target_symbolic_remote_names,
431
- & configured_remote_tracking_branches,
432
- & target_refs,
433
- meta,
434
- id,
435
- limit,
436
- ) ?;
437
- if hard_limit_hit {
438
- return Ok ( graph. with_hard_limit ( ) ) ;
435
+ for item in remote_items {
436
+ if next. push_back_exhausted ( item) {
437
+ return Ok ( graph. with_hard_limit ( ) ) ;
438
+ }
439
439
}
440
440
441
441
prune_integrated_tips ( & mut graph, & mut next, & desired_refs, max_limit) ;
@@ -469,7 +469,39 @@ struct Queue {
469
469
struct Limit {
470
470
inner : Option < usize > ,
471
471
/// The commit we want to see to be able to assume normal limits. Until then there is no limit.
472
- goal : Option < gix:: ObjectId > ,
472
+ /// This is represented by bitflag, one for each goal.
473
+ /// The flag is empty if no goal is set.
474
+ goal : CommitFlags ,
475
+ }
476
+
477
+ /// A set of commits to keep track of in bitflags.
478
+ #[ derive( Default ) ]
479
+ struct Goals ( Vec < gix:: ObjectId > ) ;
480
+
481
+ impl Goals {
482
+ /// Return the bitflag for `goal`, or `None` if we can't track any more goals.
483
+ fn flag_for ( & mut self , goal : gix:: ObjectId ) -> Option < CommitFlags > {
484
+ let existing_flags = CommitFlags :: all ( ) . iter ( ) . count ( ) ;
485
+ let max_goals = size_of :: < CommitFlags > ( ) * 8 - existing_flags;
486
+
487
+ let goals = & mut self . 0 ;
488
+ let goal_index = match goals. iter ( ) . position ( |existing| existing == & goal) {
489
+ None => {
490
+ let idx = goals. len ( ) ;
491
+ goals. push ( goal) ;
492
+ idx
493
+ }
494
+ Some ( idx) => idx,
495
+ } ;
496
+ if goal_index >= max_goals {
497
+ tracing:: warn!( "Goals limit reached, cannot track {goal}" ) ;
498
+ None
499
+ } else {
500
+ Some ( CommitFlags :: from_bits_retain (
501
+ 1 << ( existing_flags + goal_index) ,
502
+ ) )
503
+ }
504
+ }
473
505
}
474
506
475
507
#[ derive( Debug , Copy , Clone ) ]
0 commit comments