Skip to content

Commit 7056ed8

Browse files
committed
Incorporate worktree information into graph and workspace.
1 parent 3467de7 commit 7056ed8

File tree

36 files changed

+1938
-1242
lines changed

36 files changed

+1938
-1242
lines changed

Cargo.lock

Lines changed: 59 additions & 58 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/but-api/src/commands/stack.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,10 @@ pub fn create_branch(
135135
{
136136
let segment = stack.segments.first().context("BUG: no empty stacks")?;
137137
segment
138-
.ref_name
138+
.ref_info
139139
.as_ref()
140-
.map(|rn| but_workspace::branch::create_reference::Anchor::AtSegment {
141-
ref_name: Cow::Borrowed(rn.as_ref()),
140+
.map(|ri| but_workspace::branch::create_reference::Anchor::AtSegment {
141+
ref_name: Cow::Borrowed(ri.ref_name.as_ref()),
142142
position: Above,
143143
})
144144
.or_else(|| {

crates/but-graph/src/api.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,13 +133,13 @@ impl Graph {
133133
name: &gix::refs::FullNameRef,
134134
) -> Option<(&Segment, &Commit)> {
135135
self.inner.node_weights().find_map(|s| {
136-
if s.ref_name.as_ref().is_some_and(|rn| rn.as_ref() == name) {
136+
if s.ref_name().is_some_and(|rn| rn == name) {
137137
self.tip_skip_empty(s.id).map(|c| (s, c))
138138
} else {
139139
s.commits.iter().find_map(|c| {
140140
c.refs
141141
.iter()
142-
.any(|rn| rn.as_ref() == name)
142+
.any(|ri| ri.ref_name.as_ref() == name)
143143
.then_some((s, c))
144144
})
145145
}
@@ -156,7 +156,7 @@ impl Graph {
156156
pub fn named_segment_by_ref_name(&self, name: &gix::refs::FullNameRef) -> Option<&Segment> {
157157
self.inner
158158
.node_weights()
159-
.find(|s| s.ref_name.as_ref().is_some_and(|rn| rn.as_ref() == name))
159+
.find(|s| s.ref_name().is_some_and(|rn| rn == name))
160160
}
161161

162162
/// Starting a `segment`, ignore all segments that have no commit and return the first commit

crates/but-graph/src/debug.rs

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,14 @@ impl Graph {
9696
Ok(())
9797
};
9898
for node in self.inner.node_weights_mut() {
99-
if let Some(rn) = node.ref_name.as_mut() {
100-
anon(rn)?;
99+
if let Some(ri) = node.ref_info.as_mut() {
100+
anon(&mut ri.ref_name)?;
101101
}
102102
if let Some(rn) = node.remote_tracking_ref_name.as_mut() {
103103
anon(rn)?;
104104
}
105-
for rn in node.commits.iter_mut().flat_map(|c| c.refs.iter_mut()) {
106-
anon(rn)?;
105+
for ri in node.commits.iter_mut().flat_map(|c| c.refs.iter_mut()) {
106+
anon(&mut ri.ref_name)?;
107107
}
108108
if let Some(SegmentMetadata::Workspace(md)) = node.metadata.as_mut() {
109109
if let Some(rn) = md.target_ref.as_mut() {
@@ -154,7 +154,9 @@ impl Graph {
154154
commit
155155
.refs
156156
.iter()
157-
.map(|rn| format!("►{}", { Self::ref_debug_string(rn) }))
157+
.map(|ri| format!("►{}", {
158+
Self::ref_debug_string(ri.ref_name.as_ref(), ri.worktree.as_ref())
159+
}))
158160
.collect::<Vec<_>>()
159161
.join(", ")
160162
)
@@ -163,34 +165,43 @@ impl Graph {
163165
}
164166

165167
/// Shorten the given `name` so it's still clear if it is a special ref (like tag) or not.
166-
pub fn ref_debug_string(name: &gix::refs::FullName) -> String {
167-
let (cat, sn) = name.category_and_short_name().expect("valid refs");
168+
pub fn ref_debug_string(
169+
ref_name: &gix::refs::FullNameRef,
170+
worktree: Option<&crate::Worktree>,
171+
) -> String {
172+
let (cat, sn) = ref_name.category_and_short_name().expect("valid refs");
168173
// Only shorten those that look good and are unambiguous enough.
169-
if matches!(cat, Category::LocalBranch | Category::RemoteBranch) {
170-
sn
171-
} else {
172-
name.as_bstr()
173-
.strip_prefix(b"refs/")
174-
.map(|n| n.as_bstr())
175-
.unwrap_or(name.as_bstr())
176-
}
177-
.to_string()
174+
format!(
175+
"{}{ws}",
176+
if matches!(cat, Category::LocalBranch | Category::RemoteBranch) {
177+
sn
178+
} else {
179+
ref_name
180+
.as_bstr()
181+
.strip_prefix(b"refs/")
182+
.map(|n| n.as_bstr())
183+
.unwrap_or(ref_name.as_bstr())
184+
},
185+
ws = worktree
186+
.map(|ws| ws.debug_string(ref_name))
187+
.unwrap_or_default()
188+
)
178189
}
179190

180191
/// Return a useful one-line string showing the relationship between `ref_name`, `remote_ref_name` and how
181192
/// they are linked with `sibling_id`.
182193
pub fn ref_and_remote_debug_string(
183-
ref_name: Option<&gix::refs::FullName>,
194+
ref_info: Option<&crate::RefInfo>,
184195
remote_ref_name: Option<&gix::refs::FullName>,
185196
sibling_id: Option<SegmentIndex>,
186197
) -> String {
187198
format!(
188199
"{ref_name}{remote}",
189-
ref_name = ref_name
200+
ref_name = ref_info
190201
.as_ref()
191-
.map(|rn| format!(
202+
.map(|ri| format!(
192203
"{}{maybe_id}",
193-
Graph::ref_debug_string(rn),
204+
Graph::ref_debug_string(ri.ref_name.as_ref(), ri.worktree.as_ref()),
194205
maybe_id = sibling_id
195206
.filter(|_| remote_ref_name.is_none())
196207
.map(|id| format!(" →:{}:", id.index()))
@@ -206,7 +217,7 @@ impl Graph {
206217
.as_ref()
207218
.map(|remote_ref_name| format!(
208219
" <> {remote_name}{maybe_id}",
209-
remote_name = Graph::ref_debug_string(remote_ref_name),
220+
remote_name = Graph::ref_debug_string(remote_ref_name.as_ref(), None),
210221
maybe_id = sibling_id
211222
.map(|id| format!(" →:{}:", id.index()))
212223
.unwrap_or_default()
@@ -289,14 +300,14 @@ impl Graph {
289300
let name = format!(
290301
"{ref_name_and_remote}{maybe_centering_newline}",
291302
ref_name_and_remote = Self::ref_and_remote_debug_string(
292-
s.ref_name.as_ref(),
303+
s.ref_info.as_ref(),
293304
s.remote_tracking_ref_name.as_ref(),
294305
s.sibling_segment_id
295306
),
296307
maybe_centering_newline = if s.commits.is_empty() { "" } else { "\n" },
297308
);
298309
// Reduce noise by preferring ref-based entry-points.
299-
let show_segment_entrypoint = s.ref_name.is_some()
310+
let show_segment_entrypoint = s.ref_info.is_some()
300311
&& entrypoint.is_some_and(|(s, cidx)| s == sidx && matches!(cidx, None | Some(0)));
301312
let mut commits = s
302313
.commits

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

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use gix::{
66
prelude::{ObjectIdExt, ReferenceExt},
77
refs::Category,
88
};
9+
use std::collections::BTreeMap;
910
use tracing::instrument;
1011

1112
use crate::{CommitFlags, CommitIndex, Edge, Graph, Segment, SegmentIndex, SegmentMetadata};
@@ -154,10 +155,17 @@ impl Graph {
154155
// It's OK to default-initialise this here as overlays are only used when redoing
155156
// the traversal.
156157
let (_repo, meta, _entrypoint) = Overlay::default().into_parts(repo, meta);
158+
let wt_by_branch = {
159+
// Assume linked worktrees are never unborn!
160+
let mut m = BTreeMap::new();
161+
m.insert(ref_name.clone(), vec![crate::Worktree::Main]);
162+
m
163+
};
157164
graph.insert_segment_set_entrypoint(branch_segment_from_name_and_meta(
158165
Some((ref_name, None)),
159166
&meta,
160167
None,
168+
&wt_by_branch,
161169
)?);
162170
return Ok(graph);
163171
}
@@ -182,7 +190,7 @@ impl Graph {
182190
if let Some((rn, first_commit)) = s
183191
.commits
184192
.first_mut()
185-
.and_then(|first_commit| s.ref_name.take().map(|rn| (rn, first_commit)))
193+
.and_then(|first_commit| s.ref_info.take().map(|rn| (rn, first_commit)))
186194
{
187195
first_commit.refs.push(rn);
188196
}
@@ -350,6 +358,8 @@ impl Graph {
350358
let tip_is_not_workspace_commit = !workspaces
351359
.iter()
352360
.any(|(_, wsrn, _)| Some(wsrn) == ref_name.as_ref());
361+
let worktree_by_branch = worktree_branches(repo.for_worktree_only())?;
362+
353363
let mut ctx = post::Context {
354364
repo,
355365
symbolic_remote_names: &symbolic_remote_names,
@@ -358,12 +368,14 @@ impl Graph {
358368
refs_by_id,
359369
hard_limit: false,
360370
dangerously_skip_postprocessing_for_debugging,
371+
worktree_by_branch,
361372
};
362373
if tip_is_not_workspace_commit {
363374
let current = graph.insert_segment_set_entrypoint(branch_segment_from_name_and_meta(
364375
None,
365376
meta,
366377
Some((&ctx.refs_by_id, tip)),
378+
&ctx.worktree_by_branch,
367379
)?);
368380
_ = next.push_back_exhausted((
369381
tip,
@@ -406,8 +418,12 @@ impl Graph {
406418
max_limit.with_indirect_goal(tip, &mut goals),
407419
)
408420
};
409-
let mut ws_segment =
410-
branch_segment_from_name_and_meta(Some((ws_ref, None)), meta, None)?;
421+
let mut ws_segment = branch_segment_from_name_and_meta(
422+
Some((ws_ref, None)),
423+
meta,
424+
None,
425+
&ctx.worktree_by_branch,
426+
)?;
411427
// The limits for the target ref and the worktree ref are synced so they can always find each other,
412428
// while being able to stop when the entrypoint is included.
413429
ws_segment.metadata = Some(SegmentMetadata::Workspace(ws_meta));
@@ -439,6 +455,7 @@ impl Graph {
439455
Some((target_ref, None)),
440456
meta,
441457
None,
458+
&ctx.worktree_by_branch,
442459
)?);
443460
let (local_sidx, local_goal) =
444461
if let Some((local_ref_name, target_local_tip)) = local_tip_info {
@@ -448,12 +465,13 @@ impl Graph {
448465
Some(target_segment),
449466
meta,
450467
Some((&ctx.refs_by_id, target_local_tip)),
468+
&ctx.worktree_by_branch,
451469
)?);
452470
// We use auto-naming based on ambiguity - if the name ends up something else,
453471
// remove the nodes sibling link.
454472
let has_sibling_link = {
455473
let s = &mut graph[local_sidx];
456-
if s.ref_name.as_ref().is_none_or(|rn| rn != &local_ref_name) {
474+
if s.ref_name().is_none_or(|rn| rn != local_ref_name.as_ref()) {
457475
s.sibling_segment_id = None;
458476
false
459477
} else {
@@ -504,6 +522,7 @@ impl Graph {
504522
None,
505523
meta,
506524
Some((&ctx.refs_by_id, extra_target)),
525+
&ctx.worktree_by_branch,
507526
)?);
508527
_ = next.push_front_exhausted((
509528
extra_target,
@@ -550,15 +569,19 @@ impl Graph {
550569
None,
551570
meta,
552571
Some((&ctx.refs_by_id, segment_tip.detach())),
572+
&ctx.worktree_by_branch,
553573
)?;
554574

555575
// However, if this is a remote segment that is explicitly mentioned, and we couldn't name
556576
// it, then just fix it up here as we really want that name.
557577
let is_remote = segment_name
558578
.category()
559579
.is_some_and(|c| c == Category::RemoteBranch);
560-
if segment.ref_name.is_none() && is_remote {
561-
segment.ref_name = Some(segment_name.clone());
580+
if segment.ref_info.is_none() && is_remote {
581+
segment.ref_info = Some(crate::RefInfo::from_ref(
582+
segment_name.clone(),
583+
&ctx.worktree_by_branch,
584+
));
562585
segment.metadata = meta
563586
.branch_opt(segment_name.as_ref())?
564587
.map(SegmentMetadata::Branch);
@@ -577,6 +600,7 @@ impl Graph {
577600
&mut graph,
578601
&mut next,
579602
(ws_tips, repo, meta),
603+
&ctx.worktree_by_branch,
580604
)?;
581605
max_commits_recharge_location.sort();
582606
while let Some((id, mut propagated_flags, instruction, mut limit)) = next.pop_front() {
@@ -614,6 +638,7 @@ impl Graph {
614638
&info,
615639
&ctx.refs_by_id,
616640
meta,
641+
&ctx.worktree_by_branch,
617642
)?
618643
.unwrap_or(src_sidx);
619644
e.insert(src_sidx);
@@ -641,6 +666,7 @@ impl Graph {
641666
None,
642667
meta,
643668
Some((&ctx.refs_by_id, id)),
669+
&ctx.worktree_by_branch,
644670
)?;
645671
let segment_below = graph.connect_new_segment(
646672
parent_above,
@@ -672,6 +698,7 @@ impl Graph {
672698
limit,
673699
&mut goals,
674700
&next,
701+
&ctx.worktree_by_branch,
675702
)?;
676703

677704
let segment = &mut graph[segment_idx_for_id];
@@ -700,8 +727,9 @@ impl Graph {
700727
refs_at_commit_before_removal
701728
.clone()
702729
.into_iter()
703-
.filter(|rn| segment.ref_name.as_ref() != Some(rn))
730+
.filter(|rn| segment.ref_name() != Some(rn.as_ref()))
704731
.collect(),
732+
&ctx.worktree_by_branch,
705733
)?,
706734
);
707735

@@ -738,7 +766,7 @@ impl Graph {
738766
.tip_skip_empty(tip_sidx)
739767
.context("BUG: entrypoint must eventually point to a commit")?
740768
.id;
741-
let ref_name = self[tip_sidx].ref_name.clone();
769+
let ref_name = self[tip_sidx].ref_info.clone().map(|ri| ri.ref_name);
742770
(tip, ref_name)
743771
}
744772
};

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,10 @@ impl<'repo> OverlayRepo<'repo> {
182182
self.inner
183183
}
184184

185+
pub fn for_worktree_only(&self) -> &'repo gix::Repository {
186+
self.inner
187+
}
188+
185189
pub fn remote_names(&self) -> gix::remote::Names<'repo> {
186190
self.inner.remote_names()
187191
}

0 commit comments

Comments
 (0)