Skip to content

Commit 78b47a7

Browse files
committed
move commit: Factor out the disconnection params
We can reuse the logic behind caculating the disconnect params.
1 parent 712d512 commit 78b47a7

3 files changed

Lines changed: 233 additions & 229 deletions

File tree

crates/but-workspace/src/branch/move_branch.rs

Lines changed: 24 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
1-
use anyhow::{Context, bail};
21
use but_core::RefMetadata;
3-
use but_graph::workspace::{Stack, StackSegment};
4-
use but_rebase::graph_rebase::{
5-
Editor, Selector, SuccessfulRebase,
6-
mutate::{SegmentDelimiter, SelectorSet, SomeSelectors},
7-
};
2+
use but_rebase::graph_rebase::SuccessfulRebase;
83

94
/// Outcome of moving branches between or out of stacks.
105
///
@@ -20,12 +15,11 @@ pub struct Outcome<'ws, 'meta, M: RefMetadata> {
2015

2116
pub(super) mod function {
2217

18+
use crate::branch::segment_disconnect::{DisconnectParameters, get_disconnect_parameters};
2319
use but_core::RefMetadata;
2420
use but_core::ref_metadata::StackId;
2521
use but_rebase::graph_rebase::mutate::SomeSelectors;
2622

27-
use super::get_disconnect_parameters;
28-
2923
use super::Outcome;
3024
use anyhow::Context;
3125
use anyhow::bail;
@@ -105,14 +99,17 @@ pub(super) mod function {
10599
.select_reference(lower_bound_ref)
106100
.context("Failed to find target reference in graph.")?;
107101

108-
let (subject_delimiter, children_to_disconnect, parents_to_disconnect) =
109-
get_disconnect_parameters(
110-
&editor,
111-
&workspace,
112-
source_stack,
113-
subject_segment,
114-
workspace_head,
115-
)?;
102+
let DisconnectParameters {
103+
delimiter: subject_delimiter,
104+
children_to_disconnect,
105+
parents_to_disconnect,
106+
} = get_disconnect_parameters(
107+
&editor,
108+
&workspace,
109+
source_stack,
110+
subject_segment,
111+
workspace_head,
112+
)?;
116113

117114
editor.disconnect_segment_from(
118115
subject_delimiter.clone(),
@@ -202,14 +199,17 @@ pub(super) mod function {
202199
.select_reference(target_segment_ref_name)
203200
.context("Failed to find target reference in graph.")?;
204201

205-
let (subject_delimiter, children_to_disconnect, parents_to_disconnect) =
206-
get_disconnect_parameters(
207-
&editor,
208-
&workspace,
209-
&source_stack,
210-
&subject_segment,
211-
workspace_head,
212-
)?;
202+
let DisconnectParameters {
203+
delimiter: subject_delimiter,
204+
children_to_disconnect,
205+
parents_to_disconnect,
206+
} = get_disconnect_parameters(
207+
&editor,
208+
&workspace,
209+
&source_stack,
210+
&subject_segment,
211+
workspace_head,
212+
)?;
213213

214214
let skip_reconnect_step = source_stack.segments.len() == 1;
215215
editor.disconnect_segment_from(
@@ -311,158 +311,3 @@ pub(super) mod function {
311311
Ok((own_context(source), own_context(destination)))
312312
}
313313
}
314-
315-
/// Get the right disconnect parameters for the given subject segment and source stack.
316-
///
317-
/// This function determines which are the right parents and children to disconnect,
318-
/// as well as the right segment delimiter to move.
319-
fn get_disconnect_parameters<'ws, 'meta, M: RefMetadata>(
320-
editor: &Editor<'ws, 'meta, M>,
321-
workspace: &but_graph::Workspace,
322-
source_stack: &Stack,
323-
subject_segment: &StackSegment,
324-
workspace_head: gix::ObjectId,
325-
) -> anyhow::Result<(
326-
SegmentDelimiter<Selector, Selector>,
327-
SelectorSet,
328-
SelectorSet,
329-
)> {
330-
let index_of_segment = source_stack
331-
.segments
332-
.iter()
333-
.position(|segment| segment.id == subject_segment.id)
334-
.context("BUG: Unable to find subject segment on source stack.")?;
335-
336-
let subject_segment_ref_name = subject_segment
337-
.ref_name()
338-
.context("Subject segment doesn't have a ref name.")?;
339-
let delimiter_child = editor
340-
.select_reference(subject_segment_ref_name)
341-
.context("Failed to find subject reference in graph.")?;
342-
let delimiter_parent = match subject_segment.commits.last() {
343-
Some(last_commit) => editor
344-
.select_commit(last_commit.id)
345-
.context("Failed to find last commit in subject segment in graph.")?,
346-
None => {
347-
// Subject segment is empty, move only the reference
348-
delimiter_child
349-
}
350-
};
351-
352-
// The delimiter for the segment we want to move, is the reference selector
353-
// as the child, and the last commit inside the branch as the parent.
354-
// If the branch is empty, we take the reference selector as the parent as well.
355-
let delimiter = SegmentDelimiter {
356-
child: delimiter_child,
357-
parent: delimiter_parent,
358-
};
359-
360-
// The parent segment in the stack if any.
361-
// This will be `None` if the branch we want to move is at the bottom of the stack.
362-
let stack_base_segment = subject_segment.base_segment_id.and_then(|base_segment_id| {
363-
source_stack
364-
.segments
365-
.iter()
366-
.find(|segment| segment.id == base_segment_id)
367-
});
368-
369-
// The parent segment in the graph.
370-
// If the `stack_base_segment` is `None` but there's a `base_segment_id` defined, it means we'll find it in the
371-
// graph data, and it's probably the target branch, which is not included in the workspace.
372-
let graph_base_segment = subject_segment
373-
.base_segment_id
374-
.map(|segment_idx| &workspace.graph[segment_idx]);
375-
376-
let parents_to_disconnect = if let Some(stack_base_segment) = stack_base_segment {
377-
// Base segment is part of the source stack.
378-
select_segment(editor, stack_base_segment)?
379-
} else if let Some(graph_base_segment) = graph_base_segment {
380-
// Base segment is outside of workspace (probably target branch).
381-
select_segment(editor, graph_base_segment)?
382-
} else if subject_segment.base_segment_id.is_some() {
383-
// Base segment could not be found, but there is an ID defined. Error out.
384-
bail!(
385-
"Failed to find the base segment of the subject we want to move, even if it seems to be defined"
386-
);
387-
} else {
388-
// Nothing found. Remove all parents.
389-
SelectorSet::All
390-
};
391-
392-
if index_of_segment == 0 {
393-
// This is the top-most segment in the stack, so the parent is the workspace commit.
394-
let workspace_head_selector = editor
395-
.select_commit(workspace_head)
396-
.context("Failed to find workspace head in graph.")?;
397-
let selectors = SomeSelectors::new(vec![workspace_head_selector])?;
398-
let children_to_disconnect = SelectorSet::Some(selectors);
399-
400-
return Ok((delimiter, children_to_disconnect, parents_to_disconnect));
401-
}
402-
403-
// Segment on top of the subject segment in the stack.
404-
let child_segment = source_stack.segments.get(index_of_segment - 1).context(
405-
"BUG: Unable to find child segment of subject segment but expected it to exist.",
406-
)?;
407-
408-
// If branch stacked on top of the branch we want to move is empty, we only need to disconnect
409-
// the reference from it.
410-
// Otherwise, disconnect the last commit on the segment.
411-
let child_selector = match child_segment.commits.last() {
412-
Some(last_commit) => editor
413-
.select_commit(last_commit.id)
414-
.context("Failed to find last commit of child segment in graph."),
415-
None => {
416-
// The segment on top of the subject segment is empty. Select the reference.
417-
let child_segment_ref_name = child_segment
418-
.ref_name()
419-
.context("Child segment doesn't have a ref name.")?;
420-
editor
421-
.select_reference(child_segment_ref_name)
422-
.context("Failed to find child segment reference in graph.")
423-
}
424-
}?;
425-
let selectors = SomeSelectors::new(vec![child_selector])?;
426-
let children_to_disconnect = SelectorSet::Some(selectors);
427-
428-
Ok((delimiter, children_to_disconnect, parents_to_disconnect))
429-
}
430-
431-
/// Select a segment by its ref name if available, otherwise fall back to its tip commit.
432-
fn select_segment<M: RefMetadata>(
433-
editor: &Editor<'_, '_, M>,
434-
segment: &impl SegmentLike,
435-
) -> anyhow::Result<SelectorSet> {
436-
let selector = if let Some(ref_name) = segment.ref_name() {
437-
editor.select_reference(ref_name)?
438-
} else if let Some(tip) = segment.tip() {
439-
editor.select_commit(tip)?
440-
} else {
441-
bail!("Base segment has neither a ref name nor any commits.");
442-
};
443-
let selectors = SomeSelectors::new(vec![selector])?;
444-
Ok(SelectorSet::Some(selectors))
445-
}
446-
447-
trait SegmentLike {
448-
fn ref_name(&self) -> Option<&gix::refs::FullNameRef>;
449-
fn tip(&self) -> Option<gix::ObjectId>;
450-
}
451-
452-
impl SegmentLike for StackSegment {
453-
fn ref_name(&self) -> Option<&gix::refs::FullNameRef> {
454-
self.ref_name()
455-
}
456-
fn tip(&self) -> Option<gix::ObjectId> {
457-
self.tip()
458-
}
459-
}
460-
461-
impl SegmentLike for but_graph::Segment {
462-
fn ref_name(&self) -> Option<&gix::refs::FullNameRef> {
463-
self.ref_name()
464-
}
465-
fn tip(&self) -> Option<gix::ObjectId> {
466-
self.tip()
467-
}
468-
}

0 commit comments

Comments
 (0)