GitoxideLabs/gitoxide · error

review finish cannot rewrite merge descendants

Error message

review finish cannot rewrite merge descendants

What it means

`review finish` rewrites the ancestry of the finished review's commits, which the rebase machinery can only do for linear history. When any descendant commit of the review has more than one parent (a merge commit), the rewrite cannot be expressed, so the library bails with this message.

Solutions

  1. Rebase the merge descendants onto a linearized version of the review branch first (flatten the merges), then run review finish
  2. Finish the review before downstream merges are created
  3. Ask downstream authors to rebase instead of merging onto the review branch

Example fix

// before: merge commit M(=parents[A_review, X]) on top of review
repo.review_finish(review_id)?; // bails
// after: linearize descendants first
rebase_descendants_onto_linearized(&repo, review_tip)?;
repo.review_finish(review_id)?;
Defensive patterns

Strategy: validation

Validate before calling

fn has_merge_descendants(graph: &Graph, tips: &[ObjectId]) -> bool {
    tips.iter().all(|id| graph.parents_of(id).unwrap().len() <= 1)
}

Try / catch

match result {
    Err(e) if e.to_string().contains("merge descendants") => {
        eprintln!("linearize downstream merges before finishing the review");
    }
    r => r?,
}

Prevention

When it happens

Trigger: Finishing a review whose merged/branch-derived commits have descendants that are merge commits — detected via `graph.parents_of(id).len() > 1` for every review descendant.

Common situations: A developer branched off a review branch and merged it back before the review was finished; CI automation created merge commits on top of the review stack; a team policy of merge-instead-of-rebase downstream of review branches.

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 GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/debc9e97affd4255. Report an issue: GitHub.

Appendix: source

Thrown at gix-tix/src/edit/rebase.rs:1427

            })
            .unwrap_or_default()
    } else {
        HashSet::new()
    };
    if !checkout_path.is_empty() {
        reject_pending_checkout_path(
            &repo,
            checkout.as_ref().expect("a non-empty checkout path has a checkout").0,
        )?;
    }
    for id in review_ids.iter().chain(&natural_ids) {
        if graph
            .parents_of(*id)
            .context("a review descendant is incomplete")?
            .len()
            > 1
        {
            anyhow::bail!("review finish cannot rewrite merge descendants");
        }
    }

    let mut rewritten = HashMap::<ObjectId, Option<ObjectId>>::new();
    let mut note_rewrites = Vec::new();
    let mut finished_review = None;
    let mut conflict = None;
    for old in &review_ids {
        let old_parents = graph.parents_of(*old).context("a review descendant is incomplete")?;
        let mut commit = repo.find_commit(*old)?.decode()?.into_owned()?;
        let new_parents = if *old == review {
            vec![tip]
        } else {
            old_parents
                .iter()
                .filter_map(|parent| rewritten.get(parent).copied().unwrap_or(Some(*parent)))
                .collect()
        };

View on GitHub (pinned to e73179060b)