Skip to content

Commit d509594

Browse files
committed
Make entrypoint handling clear instead of implying it with insert_root().
This already led to a possible bug when multiple workspaces are used, and makes it hard to understand how this is really working.
1 parent 0cdf461 commit d509594

File tree

6 files changed

+89
-61
lines changed

6 files changed

+89
-61
lines changed

crates/but-graph/src/api.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,24 @@ use std::ops::{Deref, Index, IndexMut};
1111
/// Mutation
1212
impl Graph {
1313
/// Insert `segment` to the graph so that it's not connected to any other segment, and return its index.
14+
///
1415
/// Note that as a side effect, the [entrypoint](Self::lookup_entrypoint()) will also be set if it's not
1516
/// set yet.
16-
pub fn insert_root(&mut self, segment: Segment) -> SegmentIndex {
17-
let index = self.inner.add_node(segment);
18-
self.inner[index].id = index;
17+
pub fn insert_segment_set_entrypoint(&mut self, segment: Segment) -> SegmentIndex {
18+
let index = self.insert_segment(segment);
1919
if self.entrypoint.is_none() {
2020
self.entrypoint = Some((index, None))
2121
}
2222
index
2323
}
2424

25+
/// Insert `segment` to the graph so that it's not connected to any other segment, and return its index.
26+
pub fn insert_segment(&mut self, segment: Segment) -> SegmentIndex {
27+
let index = self.inner.add_node(segment);
28+
self.inner[index].id = index;
29+
index
30+
}
31+
2532
/// Put `dst` on top of `src`, connecting it from the `src_commit` specifically,
2633
/// an index valid for [`Segment::commits_unique_from_tip`] in `src` to the commit at `dst_commit` in `dst`.
2734
///

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

Lines changed: 48 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ impl Graph {
145145
// It's OK to default-initialise this here as overlays are only used when redoing
146146
// the traversal.
147147
let (_repo, meta, _entrypoint) = Overlay::default().into_parts(repo, meta);
148-
graph.insert_root(branch_segment_from_name_and_meta(
148+
graph.insert_segment_set_entrypoint(branch_segment_from_name_and_meta(
149149
Some((ref_name, None)),
150150
&meta,
151151
None,
@@ -337,7 +337,7 @@ impl Graph {
337337
dangerously_skip_postprocessing_for_debugging,
338338
};
339339
if tip_is_not_workspace_commit {
340-
let current = graph.insert_root(branch_segment_from_name_and_meta(
340+
let current = graph.insert_segment_set_entrypoint(branch_segment_from_name_and_meta(
341341
None,
342342
meta,
343343
Some((&ctx.refs_by_id, tip)),
@@ -388,7 +388,16 @@ impl Graph {
388388
// The limits for the target ref and the worktree ref are synced so they can always find each other,
389389
// while being able to stop when the entrypoint is included.
390390
ws_segment.metadata = Some(SegmentMetadata::Workspace(ws_meta));
391-
let ws_segment = graph.insert_root(ws_segment);
391+
let ws_segment = graph.insert_segment(ws_segment);
392+
if graph.entrypoint.is_none()
393+
&& graph
394+
.entrypoint_ref
395+
.as_ref()
396+
.zip(ref_name.as_ref())
397+
.is_some_and(|(a, b)| a == b)
398+
{
399+
graph.entrypoint = Some((ws_segment, None));
400+
}
392401
// As workspaces typically have integration branches which can help us to stop the traversal,
393402
// pick these up first.
394403
_ = next.push_front_exhausted((
@@ -403,45 +412,45 @@ impl Graph {
403412
));
404413

405414
if let Some((target_ref, target_ref_id, local_tip_info)) = target {
406-
let target_segment = graph.insert_root(branch_segment_from_name_and_meta(
415+
let target_segment = graph.insert_segment(branch_segment_from_name_and_meta(
407416
Some((target_ref, None)),
408417
meta,
409418
None,
410419
)?);
411-
let (local_sidx, local_goal) = if let Some((local_ref_name, target_local_tip)) =
412-
local_tip_info
413-
{
414-
let local_sidx = graph.insert_root(branch_segment_from_name_and_meta_sibling(
415-
None,
416-
Some(target_segment),
417-
meta,
418-
Some((&ctx.refs_by_id, target_local_tip)),
419-
)?);
420-
// We use auto-naming based on ambiguity - if the name ends up something else,
421-
// remove the nodes sibling link.
422-
let has_sibling_link = {
423-
let s = &mut graph[local_sidx];
424-
if s.ref_name.as_ref().is_none_or(|rn| rn != &local_ref_name) {
425-
s.sibling_segment_id = None;
426-
false
427-
} else {
428-
true
429-
}
420+
let (local_sidx, local_goal) =
421+
if let Some((local_ref_name, target_local_tip)) = local_tip_info {
422+
let local_sidx =
423+
graph.insert_segment(branch_segment_from_name_and_meta_sibling(
424+
None,
425+
Some(target_segment),
426+
meta,
427+
Some((&ctx.refs_by_id, target_local_tip)),
428+
)?);
429+
// We use auto-naming based on ambiguity - if the name ends up something else,
430+
// remove the nodes sibling link.
431+
let has_sibling_link = {
432+
let s = &mut graph[local_sidx];
433+
if s.ref_name.as_ref().is_none_or(|rn| rn != &local_ref_name) {
434+
s.sibling_segment_id = None;
435+
false
436+
} else {
437+
true
438+
}
439+
};
440+
let goal = goals.flag_for(target_local_tip).unwrap_or_default();
441+
_ = next.push_front_exhausted((
442+
target_local_tip,
443+
CommitFlags::NotInRemote | goal,
444+
Instruction::CollectCommit { into: local_sidx },
445+
max_limit
446+
.with_indirect_goal(tip, &mut goals)
447+
.without_allowance(),
448+
));
449+
next.add_goal_to(tip, goal);
450+
(has_sibling_link.then_some(local_sidx), goal)
451+
} else {
452+
(None, CommitFlags::empty())
430453
};
431-
let goal = goals.flag_for(target_local_tip).unwrap_or_default();
432-
_ = next.push_front_exhausted((
433-
target_local_tip,
434-
CommitFlags::NotInRemote | goal,
435-
Instruction::CollectCommit { into: local_sidx },
436-
max_limit
437-
.with_indirect_goal(tip, &mut goals)
438-
.without_allowance(),
439-
));
440-
next.add_goal_to(tip, goal);
441-
(has_sibling_link.then_some(local_sidx), goal)
442-
} else {
443-
(None, CommitFlags::empty())
444-
};
445454
_ = next.push_front_exhausted((
446455
target_ref_id,
447456
CommitFlags::Integrated,
@@ -468,7 +477,7 @@ impl Graph {
468477
// have to adjust the existing queue item.
469478
existing_segment
470479
} else {
471-
let extra_target_sidx = graph.insert_root(branch_segment_from_name_and_meta(
480+
let extra_target_sidx = graph.insert_segment(branch_segment_from_name_and_meta(
472481
None,
473482
meta,
474483
Some((&ctx.refs_by_id, extra_target)),
@@ -517,7 +526,7 @@ impl Graph {
517526
meta,
518527
Some((&ctx.refs_by_id, segment_tip.detach())),
519528
)?;
520-
let segment = graph.insert_root(segment);
529+
let segment = graph.insert_segment(segment);
521530
_ = next.push_back_exhausted((
522531
segment_tip.detach(),
523532
CommitFlags::NotInRemote,

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

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::projection::workspace;
66
use crate::{Commit, CommitFlags, CommitIndex, Edge, Graph, SegmentIndex, SegmentMetadata};
77
use anyhow::{Context as _, bail};
88
use but_core::{RefMetadata, ref_metadata};
9+
use gix::ObjectId;
910
use gix::prelude::ObjectIdExt;
1011
use gix::reference::Category;
1112
use petgraph::Direction;
@@ -52,13 +53,8 @@ impl Graph {
5253
) -> anyhow::Result<Self> {
5354
self.hard_limit_hit = hard_limit;
5455

55-
// For the first id to be inserted into our entrypoint segment, set index.
56-
if let Some((segment, ep_commit)) = self.entrypoint.as_mut() {
57-
*ep_commit = self
58-
.inner
59-
.node_weight(*segment)
60-
.and_then(|s| s.commit_index_of(tip));
61-
}
56+
// For the first id to be inserted into our entrypoint segment, set commit index.
57+
self.update_entrypoint_commit_index(tip);
6258

6359
if dangerously_skip_postprocessing_for_debugging {
6460
return Ok(self);
@@ -79,6 +75,8 @@ impl Graph {
7975

8076
// Point entrypoint to the right spot after all the virtual branches were added.
8177
self.set_entrypoint_to_ref_name(meta)?;
78+
// After the entrypoint changed, also update its commit-index again, for good measure.
79+
self.update_entrypoint_commit_index(tip);
8280

8381
// However, when it comes to using remotes to disambiguate, it's better to
8482
// *not* do that before workspaces are sorted as it might incorrectly place
@@ -98,6 +96,18 @@ impl Graph {
9896
Ok(self)
9997
}
10098

99+
/// Ensure the entrypoint commit-index is updated to match the actual tip commit.
100+
/// This may get out of sync when we do our graph manipulations, and it's easier
101+
/// to set it right in post than to let everything deal with this.
102+
fn update_entrypoint_commit_index(&mut self, tip: ObjectId) {
103+
if let Some((segment, ep_commit)) = self.entrypoint.as_mut() {
104+
*ep_commit = self
105+
.inner
106+
.node_weight(*segment)
107+
.and_then(|s| s.commit_index_of(tip));
108+
}
109+
}
110+
101111
/// After everything, assure the entrypoint still points to a segment with the correct ref-name,
102112
/// if one was given when starting the traversal.
103113
/// If not, try to find a segment with the right ref-name.
@@ -161,7 +171,7 @@ impl Graph {
161171
);
162172
let entrypoint_segment =
163173
branch_segment_from_name_and_meta(Some((desired_ref_name, None)), meta, None)?;
164-
let entrypoint_sidx = self.insert_root(entrypoint_segment);
174+
let entrypoint_sidx = self.insert_segment_set_entrypoint(entrypoint_segment);
165175
self.connect_segments(
166176
entrypoint_sidx,
167177
None,

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,9 @@ pub fn prioritize_initial_tips_and_assure_ws_commit_ownership<T: RefMetadata>(
114114
.find(|t| t.0 == ws_tip)
115115
.cloned()
116116
.expect("each ws-tip has one entry on queue");
117-
let new_anon_segment =
118-
graph.insert_root(branch_segment_from_name_and_meta(None, meta, None)?);
117+
let new_anon_segment = graph.insert_segment_set_entrypoint(
118+
branch_segment_from_name_and_meta(None, meta, None)?,
119+
);
119120
// This segment acts as stand-in - always process it even if the queue says it's done.
120121
_ = next.push_front_exhausted((
121122
ws_tip,
@@ -728,11 +729,12 @@ pub fn try_queue_remote_tracking_branches<T: RefMetadata>(
728729
let Some(remote_tip) = try_refname_to_id(repo, remote_tracking_branch.as_ref())? else {
729730
continue;
730731
};
731-
let remote_segment = graph.insert_root(branch_segment_from_name_and_meta(
732-
Some((remote_tracking_branch.clone(), None)),
733-
meta,
734-
None,
735-
)?);
732+
let remote_segment =
733+
graph.insert_segment_set_entrypoint(branch_segment_from_name_and_meta(
734+
Some((remote_tracking_branch.clone(), None)),
735+
meta,
736+
None,
737+
)?);
736738

737739
let remote_limit = limit.with_indirect_goal(id, goals);
738740
// These flags are to be attached to `id` so it can propagate itself later.

crates/but-graph/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,8 @@ pub struct Graph {
230230
inner: init::PetGraph,
231231
/// From where the graph was created. This is useful if one wants to focus on a subset of the graph.
232232
///
233-
/// The [`CommitIndex`] is empty if the entry point is an empty segment, one that is supposed to receive
234-
/// commits later.
233+
/// The [`CommitIndex`] is empty if the entry point is an empty segment, to indicate that the traversal
234+
/// tip isn't contained in it.
235235
entrypoint: Option<(SegmentIndex, Option<CommitIndex>)>,
236236
/// The ref_name used when starting the graph traversal. It is set to help assure that the entrypoint stays
237237
/// on the correctly named segment, knowing that the post-process may alter segments quite substantially

crates/but-graph/tests/graph/vis.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ fn post_graph_traversal() -> anyhow::Result<()> {
2828
..Default::default()
2929
};
3030

31-
let local_target = graph.insert_root(local_target);
31+
let local_target = graph.insert_segment_set_entrypoint(local_target);
3232
graph.connect_new_segment(
3333
local_target,
3434
None,
@@ -96,7 +96,7 @@ fn post_graph_traversal() -> anyhow::Result<()> {
9696
#[test]
9797
fn detached_head() {
9898
let mut graph = Graph::default();
99-
graph.insert_root(Segment {
99+
graph.insert_segment_set_entrypoint(Segment {
100100
commits: vec![commit(id("a"), None, CommitFlags::empty())],
101101
..Default::default()
102102
});

0 commit comments

Comments
 (0)