GitoxideLabs/gitoxide · error
the squash source must have exactly one parent
Error message
the squash source must have exactly one parent
What it means
Squashing a commit into its target is only defined when the source has exactly one parent: the rewritten history replaces the single parent link. Merge commits (two or more parents) or graph queries that fail to find the source's parents cannot be squashed this way, so tix rejects the plan.
Solutions
- Pick a non-merge commit as the squash source; squash its child or a leaf commit instead.
- First linearize history (rebase to drop the merge) if merging is not required, then squash.
- Use a different operation for merge commits (e.g. reword or drop) rather than squash.
Example fix
// before: squashing a merge commit plan_squash(repo, &graph, merge_commit, base)? // after: squash the linear child of the target instead plan_squash(repo, &graph, linear_commit_after_target, target)?
Defensive patterns
Strategy: validation
Validate before calling
// Rust: ensure the source is a non-merge commit
let parents = graph.parents_of(source)?;
if parents.len() != 1 { eprintln!("pick a non-merge commit as squash source"); } Type guard
fn is_linear(commit: ObjectId, graph: &HistoryGraph) -> bool {
graph.parents_of(commit).map(|p| p.len() == 1).unwrap_or(false)
} Prevention
- Exclude merge commits from squash-source selection in tooling/UIs.
- Linearize history before squashing around merges.
When it happens
Trigger: Calling squash planning with a `source` commit that is a merge commit, or whose parent lookup in the `HistoryGraph` returns more than one parent (`parents_of(source)` not matching `[one]`).
Common situations: Trying to squash a merge commit into its first parent; selecting a merge commit in the interactive history view as the squash source.
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
- the squash target must have exactly one parent
- descendant merge commits cannot be squashed
- the squash target must be a strict ancestor of the source
- copying a commit requires it to have exactly one parent
- copying a commit cannot rewrite root or merge commits
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/f5a137f7b13a0e99.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/rebase.rs:452
}
}
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")?;
let scope_set: HashSet<_> = scope.iter().copied().collect();
let mut non_leaves = HashSet::new();
for id in &scope {
let parents = graph
.parents_of(*id)
.context("an affected squash commit is incomplete")?;View on GitHub (pinned to e73179060b)