gitbutlerapp/gitbutler · error

Branch ` ` is pushed, but its remote ancestry does not…

Error message

Branch `{}` is pushed, but its remote ancestry does not match the reviewed workspace stack; push it and its ancestors before creating a review

What it means

Before creating a review, the code verifies that the remote history of the selected branch matches the reviewed workspace stack ancestry. For each consecutive pair of tips in the lane chain it checks remote_contains; if the remote does not contain the parent tip, the branch is pushed but its ancestors are missing/diverged on the remote, and this error is thrown.

Solutions

  1. Push all ancestor branches of the stack: `git push <remote> <parent-branch>` for each link in the chain
  2. Run `but push` / full stack push instead of pushing a single branch
  3. If the remote diverged, reconcile with a fetch and re-push the correct history

Example fix

// before
git push origin feature-top;
// after
git push origin base-branch feature-top
Defensive patterns

Strategy: validation

Validate before calling

const parents = await getStackAncestors(projectId, branch);
for (const p of parents) {
  if (!await isPushed(p)) await pushBranch(projectId, p);
}

Try / catch

try {
  await createReview(projectId, branch);
} catch (e) {
  if (String(e).includes('remote ancestry does not match')) {
    await pushEntireStack(projectId); // push branch + ancestors
    await createReview(projectId, branch);
  } else throw e;
}

Prevention

When it happens

Trigger: Creating a review for a branch that is pushed, but whose ancestor commits in the workspace stack are not present on the remote (or remote ancestry diverged).

Common situations: User force-pushed or pushed only the top branch; a stacked branch was pushed from another machine; remote branch was rewritten causing ancestry mismatch.

Understand the failure class

Background: "git command failed": what it means when a tool shells out to git and git exits non-zero — this error's family across 21 libraries.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18). Data as JSON: /api/errors/0d77c12b228cb937. Report an issue: GitHub.

Appendix: source

Thrown at crates/but-api/src/legacy/forge.rs:2226

        .expect("a non-empty chain starts with the branch's own lane");
    let mut reviewed_ancestors = std::iter::once((*lane, selected_index + 1))
        .chain(beneath.iter().copied())
        .flat_map(|(lane, index)| &lane.segments[index..])
        .filter(|segment| review_number(segment, &open_reviews).is_some())
        .map(|segment| remote_head(&repo, segment))
        .collect::<Result<Vec<_>>>()?;
    reviewed_ancestors.reverse();
    let selected = remote_head(&repo, &lane.segments[*selected_index])?;

    for pair in reviewed_ancestors
        .iter()
        .map(|head| head.remote_tip)
        .chain(std::iter::once(selected.remote_tip))
        .collect::<Vec<_>>()
        .windows(2)
    {
        if !remote_contains(&repo, pair[0], pair[1])? {
            anyhow::bail!(
                "Branch `{}` is pushed, but its remote ancestry does not match the reviewed workspace stack; push it and its ancestors before creating a review",
                branch.shorten()
            );
        }
    }

    match reviewed_ancestors.pop() {
        Some(nearest_reviewed_ancestor) => Ok(nearest_reviewed_ancestor.branch_name),
        None => target_short_name(&ctx.project_meta()?, &repo),
    }
}

#[derive(Debug)]
struct RemoteHead {
    branch_name: String,
    remote_tip: gix::ObjectId,
}

View on GitHub (pinned to 58e5313667)