{"record":{"id":"97bef06ed45c80d2","repo":"gitbutlerapp/gitbutler","slug":"no-uncommitted-changes-assigned-to-branch-branch","errorCode":null,"errorMessage":"No uncommitted changes assigned to branch: {branch_name}","messagePattern":"No uncommitted changes assigned to branch: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-api/src/legacy/absorb.rs","lineNumber":157,"sourceCode":"                        segment\n                            .ref_info\n                            .as_ref()\n                            .is_some_and(|ri| ri.ref_name.shorten() == branch_name.as_bytes())\n                    })\n                })\n                .ok_or_else(|| anyhow::anyhow!(\"Branch not found: {branch_name}\"))?;\n\n            let stack_id = stack.id.ok_or_else(|| anyhow::anyhow!(\"Stack has no ID\"))?;\n\n            // Filter assignments to just this stack\n            let candidates: Vec<AbsorbCandidate> = all_assignments\n                .into_iter()\n                .filter(|a| a.stack_id == Some(stack_id))\n                .map(Into::into)\n                .collect();\n\n            if candidates.is_empty() {\n                anyhow::bail!(\"No uncommitted changes assigned to branch: {branch_name}\");\n            }\n\n            (candidates, dependencies)\n        }\n        AbsorptionTarget::TreeChanges {\n            changes,\n            assigned_stack_id,\n        } => {\n            // Get all worktree changes, assignments, and dependencies\n            let worktree_changes = crate::diff::changes_in_worktree_with_perm(\n                ctx,\n                ChangesSource::Head,\n                true,\n                perm.read_permission(),\n            )?;\n            let all_assignments = worktree_changes.assignments;\n            let dependencies = worktree_changes.dependencies;\n","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-api/src/legacy/absorb.rs#L139-L175","documentation":"`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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nabsorb(\"feature-x\", files)  // Err: No uncommitted changes assigned to branch: feature-x\n// after\nlet target = changes.iter().find(|c| c.assigned_branch == \"feature-x\");\nabsorb(target.branch, files)  // or assign the changes to feature-x first","handlingStrategy":"validation","validationCode":"// ensure the branch actually has assigned uncommitted changes before absorbing into it\nconst status = await api.workspaceState();\nconst hasChanges = status.fileChanges.some(f => f.assignedStack?.branches.includes(branchName));\nif (!hasChanges) throw new Error(`No uncommitted changes assigned to ${branchName}; assign or pick another branch`);","typeGuard":"function branchHasUncommittedChanges(branchName, files) {\n  return files.some(f => f.uncommitted && f.assignedBranch === branchName);\n}","tryCatchPattern":"try {\n  await api.absorb({ branch: branchName });\n} catch (e) {\n  if (String(e).includes('No uncommitted changes assigned to branch')) {\n    const owner = findBranchOwningChanges(); // inspect assignments, then retarget\n    if (owner) await api.absorb({ branch: owner });\n  } else throw e;\n}","preventionTips":["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."],"tags":["git","uncommitted-changes","absorb","validation"],"backgroundTag":"empty-result-set","analyzedSha":"58e5313667b857ef39a730e380af31816a7b1768","analyzedAt":"2026-09-18T06:50:32.052Z","contentChangedAt":"2026-09-18T06:50:32.052Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}