gitbutlerapp/gitbutler · error · anyhow::Error

line {line_number}: invalid squash commit '{commit}': {err}

Error message

line {line_number}: invalid squash commit '{commit}': {err}

What it means

A commit inside a `squash <c1> <c2> [...]` line failed the same resolve_commit check against the editable divergence set. The message names the exact offending commit string, so multi-commit squash lines with one bad hash are easy to localize.

Source

Thrown at crates/but-workspace/src/branch/integrate_branch_upstream/parsing.rs:85

                let commit = arguments
                    .first()
                    .copied()
                    .expect("validated merge arity above");
                InteractiveIntegrationStep::Merge {
                    commit_id: resolve_commit(commit, &allowed_commits).map_err(|err| {
                        anyhow::anyhow!("line {line_number}: invalid merge commit: {err}")
                    })?,
                }
            }
            "squash" => {
                if arguments.len() < 2 {
                    bail!("line {line_number}: squash requires at least two commits");
                }
                let commits = arguments
                    .iter()
                    .map(|commit| {
                        resolve_commit(commit, &allowed_commits).map_err(|err| {
                            anyhow::anyhow!(
                                "line {line_number}: invalid squash commit '{commit}': {err}"
                            )
                        })
                    })
                    .collect::<Result<Vec<_>>>()?;
                InteractiveIntegrationStep::Squash {
                    commits,
                    message: message_part
                        .map(parse_message_clause)
                        .transpose()
                        .map_err(|err| {
                            anyhow::anyhow!("line {line_number}: invalid squash message: {err}")
                        })?,
                }
            }
            other => bail!("line {line_number}: unknown command '{other}'"),
        };
        steps.push(step);

View on GitHub (pinned to caf1f223d3)

Solutions

  1. Fix the specific commit token named in the error to a hash from the generated script
  2. Lengthen ambiguous prefixes
  3. Regenerate the integration script and re-apply the squash edits

Example fix

# before
squash 3333333 deadbee 4444444   # deadbee not in divergence
# after
squash 3333333 4444444
Defensive patterns

Strategy: validation

Validate before calling

// validate every token of each squash line (≥2 tokens, all resolvable)
for line in script.lines().filter(|l| l.trim_start().starts_with("squash")) {
    let toks: Vec<_> = line.split_whitespace().collect();
    ensure!(toks.len() >= 2, "squash needs ≥2 commits");
    for t in &toks[1..] { ensure!(commit_resolves(&candidate_strings, t), "squash token {t} invalid"); }
}

Type guard

fn squash_line_valid(line: &str, candidates: &HashSet<String>) -> bool {
    let toks: Vec<_> = line.split('|').next().unwrap_or("").split_whitespace().collect();
    toks.len() >= 2 && toks[1..].iter().all(|t| commit_resolves(candidates, t))
}

Try / catch

if let Err(e) = parse_integration_steps_script(&script, &divergence) {
    if let Some(rest) = e.to_string().split("invalid squash commit '").nth(1) { /* rest starts with the offending token — show it */ }
}

Prevention

When it happens

Trigger: A squash line listing ≥2 commits where at least one hash is not a not-integrated local commit or upstream-only commit, or one short prefix is ambiguous. Inner error text distinguishes 'not part of the editable divergence' from 'ambiguous'.

Common situations: Hand-editing squash lines and pasting hashes from another branch window; stale scripts reused after upstream force-push changed the candidate set.

Related errors


AI-assisted analysis of gitbutlerapp/gitbutler@caf1f223d3 (2026-08-20). Data as JSON: /api/errors/4cd4a5ee0b1eacc3. Report an issue: GitHub.