GitoxideLabs/gitoxide · error
the squash target must be a strict ancestor of the source
Error message
the squash target must be a strict ancestor of the source
What it means
A squash in tix folds the source commit into a strict ancestor (the target). If the source equals the target, or the target is not an ancestor of the source in the loaded `HistoryGraph`, there is no valid fold direction, so the plan is rejected. This prevents creating nonsensical rewritten history.
Solutions
- Swap the arguments so the target is the older, ancestor commit and the source is the newer one.
- Choose a different target that is a strict ancestor of the source commit.
- If the commits are unrelated siblings, use a different operation (e.g. fixup via rebase) instead of squash.
Example fix
// before: wrong direction plan_squash(repo, &graph, older_commit, newer_commit)? // after: target must be the ancestor plan_squash(repo, &graph, newer_commit, older_commit)?
Defensive patterns
Strategy: validation
Validate before calling
// Rust: verify ancestry before planning a squash assert_ne!(source, target, "squash source and target must differ"); assert!(graph.is_ancestor(target, source), "target must be an ancestor of source");
Prevention
- Always pass the older commit as target and the newer as source.
- In UIs, disable squash options when the selected pair is not ancestor/descendant.
When it happens
Trigger: Calling the squash planning function (used by `perform_inner`) with `source == target`, or with a `target` that is a descendant of (or unrelated to) `source` according to `graph.is_ancestor(target, source)`.
Common situations: Selecting two sibling commits (neither is an ancestor of the other) for squash; accidentally passing the newer commit as the target; picking the same commit twice in the interactive UI.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- descendant merge commits cannot be squashed
- the squash source must have exactly one parent
- the squash target must have exactly one parent
- the copy source and target must differ
- the stack base must be an ancestor of HEAD
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/4626cd681358bf33.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/rebase.rs:446
new: Some(old),
follows_tip: tips.contains(&old),
editable: !reference.name().as_bstr().starts_with(crate::history::PIN_PREFIX)
&& !reference.name().as_bstr().starts_with(crate::history::REVIEW_PREFIX),
placement: None,
});
}
}
Ok(out)
}
pub(crate) fn squash_plan(
repo: &gix::Repository,
graph: &HistoryGraph,
source: ObjectId,
target: ObjectId,
) -> Result<Plan> {
if source == target || !graph.is_ancestor(target, source) {
anyhow::bail!("the squash target must be a strict ancestor of the source");
}
let source_parents = graph
.parents_of(source)
.context("the squash source is not in the loaded history")?;
let [source_parent] = source_parents.as_slice() else {
anyhow::bail!("the squash source must have exactly one parent");
};
let source_parent = *source_parent;
let target_parents = graph
.parents_of(target)
.context("the squash target is not in the loaded history")?;
let [base] = target_parents.as_slice() else {
anyhow::bail!("the squash target must have exactly one parent");
};
let base = *base;
let scope = graph
.descendants_in_parent_order(target)
.context("the squash target is not in the loaded history")?;View on GitHub (pinned to e73179060b)