gitbutlerapp/gitbutler · error
No uncommitted changes assigned to branch
Error message
No uncommitted changes assigned to branch: {branch_name} What it means
`absorption_plan_with_perm` (exposed as `absorption_plan`) throws this when absorbing uncommitted changes into a branch (`AbsorptionTarget::Branch`-style target) but no uncommitted file changes in the worktree are assigned to the named branch/stack. Absorption moves worktree changes into commits of a stack; with an empty candidate set there is nothing to absorb, so the operation bails.
Solutions
- Check which branch the uncommitted changes are assigned to (`but status` / UI) and target that branch instead.
- Assign the worktree changes to the intended branch first (`but branch assign` / UI assignment), then retry absorb.
- Verify the branch name spelling; use unassigned-changes absorption if the changes are not assigned to any branch.
Example fix
// before
absorb("feature-x", files) // Err: No uncommitted changes assigned to branch: feature-x
// after
let target = changes.iter().find(|c| c.assigned_branch == "feature-x");
absorb(target.branch, files) // or assign the changes to feature-x first Defensive patterns
Strategy: validation
Validate before calling
// ensure the branch actually has assigned uncommitted changes before absorbing into it
const status = await api.workspaceState();
const hasChanges = status.fileChanges.some(f => f.assignedStack?.branches.includes(branchName));
if (!hasChanges) throw new Error(`No uncommitted changes assigned to ${branchName}; assign or pick another branch`); Type guard
function branchHasUncommittedChanges(branchName, files) {
return files.some(f => f.uncommitted && f.assignedBranch === branchName);
} Try / catch
try {
await api.absorb({ branch: branchName });
} catch (e) {
if (String(e).includes('No uncommitted changes assigned to branch')) {
const owner = findBranchOwningChanges(); // inspect assignments, then retarget
if (owner) await api.absorb({ branch: owner });
} else throw e;
} Prevention
- Check change-to-branch assignments in `but status` before absorbing.
- Assign worktree changes to the intended branch as part of your workflow, not after.
- Guard automation to skip absorb when the target branch has no assigned changes.
When it happens
Trigger: Calling `absorption_plan` targeting branch `branch_name` where the workspace's file-change assignments filtered by `stack_id == Some(stack_id)` yield an empty candidate list — the branch has no assigned uncommitted changes.
Common situations: Passing a branch name that exists but has no worktree changes assigned to it (changes belong to other branches or are unassigned); typos in the branch name that resolve to a different stack; running absorb after the changes were already committed or unassigned.
Understand the failure class
Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.
Related errors
- No uncommitted changes found for the selected files
- Aborting due to empty branch name
- Branch name ' ' collides with existing branch
- Branch not found
- Can only check out local branches under refs/heads, got
AI-assisted analysis of gitbutlerapp/gitbutler@58e5313667 (2026-09-18).
Data as JSON: /api/errors/97bef06ed45c80d2.
Report an issue: GitHub.
Appendix: source
Thrown at crates/but-api/src/legacy/absorb.rs:157
segment
.ref_info
.as_ref()
.is_some_and(|ri| ri.ref_name.shorten() == branch_name.as_bytes())
})
})
.ok_or_else(|| anyhow::anyhow!("Branch not found: {branch_name}"))?;
let stack_id = stack.id.ok_or_else(|| anyhow::anyhow!("Stack has no ID"))?;
// Filter assignments to just this stack
let candidates: Vec<AbsorbCandidate> = all_assignments
.into_iter()
.filter(|a| a.stack_id == Some(stack_id))
.map(Into::into)
.collect();
if candidates.is_empty() {
anyhow::bail!("No uncommitted changes assigned to branch: {branch_name}");
}
(candidates, dependencies)
}
AbsorptionTarget::TreeChanges {
changes,
assigned_stack_id,
} => {
// Get all worktree changes, assignments, and dependencies
let worktree_changes = crate::diff::changes_in_worktree_with_perm(
ctx,
ChangesSource::Head,
true,
perm.read_permission(),
)?;
let all_assignments = worktree_changes.assignments;
let dependencies = worktree_changes.dependencies;
View on GitHub (pinned to 58e5313667)