@@ -157,6 +157,7 @@ pub(crate) mod function {
157
157
repo,
158
158
checkout:: Options {
159
159
uncommitted_changes,
160
+ skip_head_update : false ,
160
161
} ,
161
162
) ?;
162
163
let graph = workspace. graph . redo_traversal_with_overlay (
@@ -335,8 +336,9 @@ pub(crate) mod function {
335
336
) ;
336
337
}
337
338
// At this point, the workspace-metadata already knows the new branch(es), but the workspace itself
338
- // doesn't see one or more of to-be-applied branches. These are, however, part of the graph by now,
339
- // and we want to try to create a workspace merge.
339
+ // doesn't see one or more of to-be-applied branches (to become stacks).
340
+ // These are, however, part of the graph by now, and we want to try to create a workspace
341
+ // merge.
340
342
let mut in_memory_repo = repo. clone ( ) . for_tree_diffing ( ) ?. with_object_memory ( ) ;
341
343
let merge_result = WorkspaceCommit :: from_new_merge_with_metadata (
342
344
& ws_md. stacks ,
@@ -353,6 +355,110 @@ pub(crate) mod function {
353
355
} ) ;
354
356
}
355
357
358
+ let prev_head_id = graph
359
+ . entrypoint_commit ( )
360
+ . context ( "BUG: how is it possible that there is no head commit?" ) ?
361
+ . id ;
362
+ let mut new_head_id = merge_result. workspace_commit_id ;
363
+ let overlay =
364
+ overlay. with_entrypoint ( new_head_id, Some ( workspace_ref_name_to_update. clone ( ) ) ) ;
365
+ let mut graph =
366
+ graph. redo_traversal_with_overlay ( & in_memory_repo, meta, overlay. clone ( ) ) ?;
367
+
368
+ let workspace = graph. to_workspace ( ) ?;
369
+ let collect_unapplied_branches = |workspace : & but_graph:: projection:: Workspace | {
370
+ branches_to_apply
371
+ . iter ( )
372
+ . filter ( |rn| workspace. refname_is_segment ( rn) )
373
+ . collect :: < Vec < _ > > ( )
374
+ } ;
375
+ let unapplied_branches = collect_unapplied_branches ( & workspace) ;
376
+ if !unapplied_branches. is_empty ( ) {
377
+ // Now that the merge is done, try to redo the operation one last time with dependent branches instead.
378
+ // Only do that for the still unapplied branches, which should always find some sort of anchor.
379
+ let ws_mut: & mut Workspace = & mut ws_md;
380
+ for branch_to_remove in & unapplied_branches {
381
+ ws_mut. remove_segment ( branch_to_remove) ;
382
+ }
383
+ for rn in & unapplied_branches {
384
+ // Here we have to check if the new ref would be able to become its own stack,
385
+ // or if it has to be a dependent branch. Stacks only work if the ref rests on a base
386
+ // outside the workspace, so if we find it in the workspace (in an ambiguous spot) it must be
387
+ // a dependent branch
388
+ if let Some ( segment_to_insert_above) = workspace
389
+ . stacks
390
+ . iter ( )
391
+ . flat_map ( |stack| stack. segments . iter ( ) )
392
+ . find_map ( |segment| {
393
+ segment. commits . iter ( ) . flat_map ( |c| c. refs . iter ( ) ) . find_map (
394
+ |ambiguous_rn| {
395
+ ( ambiguous_rn. as_ref ( ) == * * rn)
396
+ . then_some ( segment. ref_name . as_ref ( ) )
397
+ . flatten ( )
398
+ } ,
399
+ )
400
+ } )
401
+ {
402
+ if ws_mut
403
+ . insert_new_segment_above_anchor_if_not_present (
404
+ rn,
405
+ segment_to_insert_above. as_ref ( ) ,
406
+ )
407
+ . is_none ( )
408
+ {
409
+ // For now bail, until we know it's worth fixing this case automatically.
410
+ bail ! (
411
+ "Missing reference {segment_to_insert_above} which should be known to workspace metadata to serve as insertion position for {rn}"
412
+ ) ;
413
+ }
414
+ } else {
415
+ bail ! (
416
+ "Unexpectedly failed to find anchor for {rn} to make it a dependent branch"
417
+ )
418
+ }
419
+ }
420
+
421
+ // Redo the merge, with the different stack configuration.
422
+ // Note that this is the exception, typically using stacks will be fine.
423
+ let merge_result = WorkspaceCommit :: from_new_merge_with_metadata (
424
+ & ws_md. stacks ,
425
+ workspace. graph ,
426
+ & in_memory_repo,
427
+ Some ( branch) ,
428
+ ) ?;
429
+
430
+ if merge_result. has_conflicts ( ) && on_workspace_conflict. should_abort ( ) {
431
+ return Ok ( Outcome {
432
+ graph : Cow :: Owned ( graph) ,
433
+ workspace_ref_created : needs_ws_ref_creation,
434
+ workspace_merge : Some ( merge_result) ,
435
+ } ) ;
436
+ }
437
+ new_head_id = merge_result. workspace_commit_id ;
438
+ let ws_md_override = Some ( ( workspace_ref_name_to_update. clone ( ) , ( * ws_md) . clone ( ) ) ) ;
439
+
440
+ graph = graph. redo_traversal_with_overlay (
441
+ & in_memory_repo,
442
+ meta,
443
+ overlay
444
+ . with_entrypoint ( new_head_id, Some ( workspace_ref_name_to_update. clone ( ) ) )
445
+ . with_workspace_metadata_override ( ws_md_override) ,
446
+ ) ?;
447
+ let workspace = graph. to_workspace ( ) ?;
448
+ let unapplied_branches = collect_unapplied_branches ( & workspace) ;
449
+
450
+ if !unapplied_branches. is_empty ( ) {
451
+ bail ! (
452
+ "Unexpectedly failed to apply {branches} which is/are still not in the workspace" ,
453
+ branches = unapplied_branches
454
+ . iter( )
455
+ . map( |rn| rn. shorten( ) . to_string( ) )
456
+ . collect:: <Vec <_>>( )
457
+ . join( ", " )
458
+ )
459
+ }
460
+ }
461
+
356
462
// All work is done, persist and exit.
357
463
// Note that it could be that some stacks aren't merged in,
358
464
// while being present in the workspace metadata.
@@ -361,23 +467,14 @@ pub(crate) mod function {
361
467
storage. persist ( repo) ?;
362
468
drop ( in_memory_repo) ;
363
469
}
364
- let prev_head_id = graph
365
- . entrypoint_commit ( )
366
- . context ( "BUG: how is it possible that there is no head commit?" ) ?
367
- . id ;
368
- let new_head_id = merge_result. workspace_commit_id ;
369
- let graph = graph. redo_traversal_with_overlay (
370
- repo,
371
- meta,
372
- overlay. with_entrypoint ( new_head_id, Some ( workspace_ref_name_to_update. clone ( ) ) ) ,
373
- ) ?;
374
470
persist_metadata ( meta, & branches_to_apply, & ws_md) ?;
375
471
crate :: branch:: safe_checkout (
376
472
prev_head_id,
377
473
new_head_id,
378
474
repo,
379
475
checkout:: Options {
380
476
uncommitted_changes,
477
+ skip_head_update : true ,
381
478
} ,
382
479
) ?;
383
480
@@ -416,10 +513,9 @@ pub(crate) mod function {
416
513
new_ref_target : gix:: ObjectId ,
417
514
new_ref : Option < & gix:: refs:: FullNameRef > ,
418
515
) -> anyhow:: Result < ( ) > {
419
- // This also means we want HEAD to point to it.
420
- let head_message = "GitButler switch to workspace during apply-branch" . into ( ) ;
421
516
let edits = match new_ref {
422
517
None => {
518
+ let head_message = "GitButler checkout workspace during apply-branch" . into ( ) ;
423
519
vec ! [ RefEdit {
424
520
change: Change :: Update {
425
521
log: LogChange {
@@ -435,6 +531,8 @@ pub(crate) mod function {
435
531
} ]
436
532
}
437
533
Some ( new_ref) => {
534
+ // This also means we want HEAD to point to it.
535
+ let head_message = "GitButler switch to workspace during apply-branch" . into ( ) ;
438
536
vec ! [
439
537
RefEdit {
440
538
change: Change :: Update {
0 commit comments