gitbutlerapp/gitbutler · error
this is a placeholder for where we will have to start…
Error message
this is a placeholder for where we will have to start handling this UnmergedTree
What it means
tips_from_metadata in crates/but-workspace/src/commit/mod.rs panics with a todo!() when it encounters a workspace commit whose relation to the workspace is WorkspaceCommitRelation::MergeFrom (an unmerged tree / merge source). Merged and Outside relations are handled; MergeFrom explicitly has no handling yet, so reaching it is an unconditional panic.
Solutions
- Upgrade/normalize the workspace metadata so affected commits are classified Merged (retry them via the documented path of passing them as Merged) or Outside before calling tips_from_metadata.
- Fix at the source: ensure merge-from commits are added to parents lists without being merged, as the code comment suggests, then implement the MergeFrom arm.
- Short-term, filter out MergeFrom relations in a pre-pass and handle them separately instead of feeding them to tips_from_metadata.
Defensive patterns
Strategy: validation
Validate before calling
// pre-check metadata for MergeFrom relations before calling tips_from_metadata
if segments.iter().any(|s| matches!(s.relation, WorkspaceCommitRelation::MergeFrom{..})) {
// handle separately
} Type guard
// Rust: match on the relation before passing through
match relation {
WorkspaceCommitRelation::MergeFrom{..} => None, // handled elsewhere
_ => Some((top_segment, relation)),
} Prevention
- Inspect workspace relations before deriving tips; never assume all commits are Merged.
- Add a test with a merge-from commit to catch this early.
- Normalize workspace metadata (re-classify MergeFrom commits) after pull/merge operations.
When it happens
Trigger: Calling but_workspace::commit::tips_from_metadata on workspace metadata that contains at least one top-segment commit classified as WorkspaceCommitRelation::MergeFrom{..} (a merge commit brought into the workspace but not merged into the workspace tree).
Common situations: A user pulled/merged external commits into a workspace branch so the workspace contains unmerged merge-commit trees; the app then derives commit tips from metadata and hits the placeholder.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- a committed transaction always materializes a workspace
- implement list and call recursively
- known statically
- not yet implemented
- statically known
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/020a1677ef4d1989.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-workspace/src/commit/mod.rs:254
/// the workspace projection, used to preserve anonymous parents not represented in metadata.
///
/// `graph` resolves metadata branch names to commit and segment ids.
pub fn tips_from_metadata<'a>(
stacks: impl IntoIterator<Item = &'a but_core::ref_metadata::WorkspaceStack>,
anon_stacks: impl IntoIterator<Item = (usize, Tip)>,
graph: &but_graph::Graph,
) -> ResolvedTips {
let mut missing_stacks = Vec::new();
let mut tips_with_metadata_slots: Vec<_> = stacks
.into_iter()
.filter_map(|s| s.branches.first().map(|b| (b, s.workspacecommit_relation)))
.map(|(top_segment, relation)| {
match relation {
WorkspaceCommitRelation::Merged => {}
WorkspaceCommitRelation::MergeFrom { .. } => {
// These need to be part of the parents list, but shouldn't be merged.
// If the caller wants to retry them, they can be passed here as "Merged".
todo!(
"this is a placeholder for where we will have to start handling this UnmergedTree"
)
}
WorkspaceCommitRelation::Outside => return None,
}
let stack_tip_name = top_segment.ref_name.as_ref();
match graph.segment_and_commit_by_ref_name(stack_tip_name) {
None => {
missing_stacks.push(top_segment.ref_name.to_owned());
None
}
Some((segment, commit)) => Some(Tip {
name: Some(stack_tip_name.to_owned()),
commit_id: commit.id,
segment_idx: segment.id,
}),
}
})View on GitHub (pinned to 58e5313667)