Skip to content

Commit 7eb5331

Browse files
authored
Merge pull request #10449 from Byron/fix
Ensure that the target branch isn't showing up as workspace segment.
2 parents e5f32bf + 4c35759 commit 7eb5331

File tree

3 files changed

+117
-0
lines changed

3 files changed

+117
-0
lines changed

crates/but-graph/src/init/post.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,61 @@ impl Graph {
410410
return Ok(());
411411
};
412412

413+
// If the target branch made it into a stack segment which can also be named differently by preferably a ref with metadata,
414+
// then do that instead. Let's prevent it from showing up as its own node as this leads to undesirable workspaces.
415+
if let Some((target_segment_id, local_tracking_segment_of_target)) =
416+
ws_target.as_ref().and_then(|t| {
417+
self[t.segment_index]
418+
.sibling_segment_id
419+
.map(|sidx| (t.segment_index, sidx))
420+
})
421+
&& let Some(segment_id) = ws_stacks.iter().find_map(|s| {
422+
s.segments.iter().find_map(|s| {
423+
if s.id != local_tracking_segment_of_target {
424+
return None;
425+
}
426+
let first_commit = s.commits.first()?;
427+
(!first_commit.refs.is_empty()).then_some(s.id)
428+
})
429+
})
430+
{
431+
let s = &mut self[segment_id];
432+
let c = s
433+
.commits
434+
.first_mut()
435+
.expect("segment was chosen because it has at least one commit");
436+
let mut candidates = c.refs.clone();
437+
candidates.sort_by(|a, b| {
438+
meta.branch_opt(a.as_ref())
439+
.ok()
440+
.map(|md| md.is_some())
441+
.cmp(&meta.branch_opt(b.as_ref()).ok().map(|md| md.is_some()))
442+
.then_with(|| a.cmp(b))
443+
});
444+
let candidate = candidates
445+
.pop()
446+
.expect("at least one ref or we wouldn't be here");
447+
let current_name = s.ref_name.take();
448+
let index_to_replace = c
449+
.refs
450+
.iter()
451+
.position(|rn| rn == &candidate)
452+
.expect("candidate is from a clone of 'refs', so must be contained");
453+
// effectively swap the names
454+
s.metadata = meta
455+
.branch_opt(candidate.as_ref())?
456+
.map(SegmentMetadata::Branch);
457+
s.ref_name = Some(candidate);
458+
if let Some(rn) = current_name {
459+
c.refs[index_to_replace] = rn;
460+
}
461+
462+
// Make sure we dissolve the sibling relationship for correctness, the node is now not related
463+
// to the target branch anymore.
464+
s.sibling_segment_id = None;
465+
self[target_segment_id].sibling_segment_id = None;
466+
}
467+
413468
// Setup independent stacks, first by looking at potential bases.
414469
let candidates = self.candidates_for_independent_branches_in_workspace(
415470
ws_sidx,

crates/but-graph/tests/fixtures/scenarios.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,5 +1100,14 @@ EOF
11001100
create_workspace_commit_once A
11011101
)
11021102
)
1103+
1104+
git init local-target-ahead-and-on-stack-tip
1105+
(cd local-target-ahead-and-on-stack-tip
1106+
commit init
1107+
setup_target_to_match_main
1108+
commit A
1109+
git checkout -b A
1110+
create_workspace_commit_once A
1111+
)
11031112
)
11041113

crates/but-graph/tests/graph/init/with_workspace.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,59 @@ fn stacked_rebased_remotes() -> anyhow::Result<()> {
14971497
Ok(())
14981498
}
14991499

1500+
#[test]
1501+
fn target_with_remote_on_stack_tip() -> anyhow::Result<()> {
1502+
let (repo, mut meta) = read_only_in_memory_scenario("ws/local-target-ahead-and-on-stack-tip")?;
1503+
insta::assert_snapshot!(visualize_commit_graph_all(&repo)?, @r"
1504+
* dd0cca8 (HEAD -> gitbutler/workspace) GitButler Workspace Commit
1505+
* e255adc (main, A) A
1506+
* fafd9d0 (origin/main) init
1507+
");
1508+
add_stack_with_segments(&mut meta, 1, "A", StackState::InWorkspace, &[]);
1509+
1510+
let graph = Graph::from_head(&repo, &*meta, standard_options())?.validated()?;
1511+
insta::assert_snapshot!(graph_tree(&graph), @r"
1512+
└── 👉📕►►►:0[0]:gitbutler/workspace
1513+
└── ·dd0cca8 (⌂|🏘|1)
1514+
└── 📙►:2[1]:A
1515+
└── ·e255adc (⌂|🏘|11) ►main
1516+
└── ►:1[2]:origin/main
1517+
└── ·fafd9d0 (⌂|🏘|✓|11)
1518+
");
1519+
1520+
// The main branch is not present, as it's the target.
1521+
insta::assert_snapshot!(graph_workspace(&graph.to_workspace()?), @r"
1522+
📕🏘️:0:gitbutler/workspace <> ✓refs/remotes/origin/main on fafd9d0
1523+
└── ≡📙:2:A on fafd9d0
1524+
└── 📙:2:A
1525+
└── ·e255adc (🏘️) ►main
1526+
");
1527+
1528+
// But mention it if it's in the workspace. It should retain order.
1529+
add_stack_with_segments(&mut meta, 1, "A", StackState::InWorkspace, &["main"]);
1530+
let graph = Graph::from_head(&repo, &*meta, standard_options())?.validated()?;
1531+
insta::assert_snapshot!(graph_workspace(&graph.to_workspace()?), @r"
1532+
📕🏘️:0:gitbutler/workspace <> ✓refs/remotes/origin/main on fafd9d0
1533+
└── ≡📙:2:A on fafd9d0
1534+
├── 📙:2:A
1535+
└── 📙:3:main <> origin/main →:1:⇡1
1536+
└── ·e255adc (🏘️)
1537+
");
1538+
1539+
// But mention it if it's in the workspace. It should retain order - inverting the order is fine.
1540+
add_stack_with_segments(&mut meta, 1, "main", StackState::InWorkspace, &["A"]);
1541+
let graph = Graph::from_head(&repo, &*meta, standard_options())?.validated()?;
1542+
// TODO: fix order, main should be first.
1543+
insta::assert_snapshot!(graph_workspace(&graph.to_workspace()?), @r"
1544+
📕🏘️:0:gitbutler/workspace <> ✓refs/remotes/origin/main on fafd9d0
1545+
└── ≡📙:2:A on fafd9d0
1546+
├── 📙:2:A
1547+
└── 📙:3:main <> origin/main →:1:⇡1
1548+
└── ·e255adc (🏘️)
1549+
");
1550+
Ok(())
1551+
}
1552+
15001553
#[test]
15011554
fn disambiguate_by_remote() -> anyhow::Result<()> {
15021555
let (repo, mut meta) = read_only_in_memory_scenario("ws/disambiguate-by-remote")?;

0 commit comments

Comments
 (0)