gitbutlerapp/gitbutler · error
' ' is ambiguous - it matches more than one uncommitted…
Error message
'{s}' is ambiguous - it matches more than one uncommitted change. Use more characters to disambiguate. What it means
In `but absorb`, each positional source argument is matched against the set of uncommitted changes/branches. If the argument matches exactly one item it is used; if it matches two or more, the CLI refuses to guess and bails, telling you to add characters to disambiguate. A related guard bails when nothing matches at all.
Solutions
- Extend the argument with more characters until it matches exactly one change (e.g. src/a instead of src)
- Use the full file path or exact branch name
- Run `but status` (or the absorb listing) to see candidate names and pick a unique one
Example fix
// before but absorb src # matches src/a.ts and src/b.ts // after but absorb src/a.ts
Defensive patterns
Strategy: validation
Validate before calling
function assertUniquePrefix(target, candidates) {
const hits = candidates.filter(c => c.startsWith(target));
if (hits.length > 1) throw new Error(`'${target}' is ambiguous; matches: ${hits.join(', ')}`);
if (hits.length === 0) throw new Error(`'${target}' matches no uncommitted change`);
return hits[0];
} Type guard
const isAmbiguous = (hits) => Array.isArray(hits) && hits.length > 1;
Try / catch
try {
await exec(`but absorb ${arg}`);
} catch (e) {
if (e.message.includes('is ambiguous')) {
console.error('Lengthen the prefix or use the full file/branch name');
} else throw e;
} Prevention
- Use full file paths or exact branch names in scripts instead of prefixes
- Check `but status` before absorbing in trees with many similarly named changes
- Re-verify prefixes after new files appear in the working tree
When it happens
Trigger: Running `but absorb <s>` where s is a prefix that uniquely-match-checks against more than one uncommitted hunk/file/branch name — e.g. `but absorb src` when both src/a.ts and src/b.ts are uncommitted.
Common situations: Using short prefixes for file paths in large diffs; two similarly named branches or files changed at once; muscle-memory abbreviations breaking after new files appear in the working tree.
Related errors
- ' ' does not name an uncommitted change or branch; refusing…
- Ambiguous branch ' ', matches multiple items
- Ambiguous branch identifier
- CLI id ' ' is ambiguous for in IdMap
- Commit ID ' ' is ambiguous. Please provide more characters…
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/80e2bf5cbceaae84.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but/src/command/legacy/absorb.rs:66
// so later commits cannot shadow them; branch selectors resolve in
// the full namespace. A selector that names neither is an error —
// silently falling back to absorbing everything is never intended.
let resolved =
match crate::id::parser::resolve_uncommitted_part(ctx, &id_map, s) {
Ok(ids) if !ids.is_empty() => ids,
_ => parse_sources(ctx, &id_map, s)?,
};
let mut acceptable = resolved.into_iter().filter(|id| {
matches!(id, CliId::UncommittedHunkOrFile { .. })
|| matches!(id, CliId::Branch(..))
});
let first = acceptable.next().ok_or_else(|| {
anyhow::anyhow!(
"'{s}' does not name an uncommitted change or branch; refusing to absorb everything"
)
})?;
if acceptable.next().is_some() {
anyhow::bail!(
"'{s}' is ambiguous - it matches more than one uncommitted change. Use more characters to disambiguate."
);
}
Ok(first)
})
.transpose()?;
let target = if let Some(source) = source {
match source {
CliId::UncommittedHunkOrFile(UncommittedHunkOrFile { hunks, .. }) => {
// Absorb this particular file
AbsorptionTarget::Hunks {
hunks: hunks.map(|id_and_hunk| id_and_hunk.hunk).into(),
}
}
CliId::Branch(branch) => {
// Absorb everything that is assigned to this lane
AbsorptionTarget::Branch {View on GitHub (pinned to 58e5313667)