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
- Push all ancestor branches of the stack: `git push <remote> <parent-branch>` for each link in the chain
- Run `but push` / full stack push instead of pushing a single branch
- 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
- Push whole stacks, not just the top branch
- Avoid force-pushing reviewed branches from other machines
- Fetch and reconcile remote state before creating reviews
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
- Could not restore all review targets after the push failed
- another pre-commit hook is already using the repository…
- askpass broker must be initialized
- Branch not found
- Branch ` ` is not part of the current workspace
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)