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

  1. Check which branch the uncommitted changes are assigned to (`but status` / UI) and target that branch instead.
  2. Assign the worktree changes to the intended branch first (`but branch assign` / UI assignment), then retry absorb.
  3. 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

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


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)