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

  1. 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.
  2. 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.
  3. 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

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


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)