{"record":{"id":"acc7ea1fdc37f5c2","repo":"gitbutlerapp/gitbutler","slug":"refusing-to-land-branch-with-whole-stack-it","errorCode":null,"errorMessage":"Refusing to land `{branch}` with --whole-stack: it is not the top of its stack. --whole-stack lands the entire stack; name its top segment `{top}` instead.","messagePattern":"Refusing to land `(.+?)` with --whole-stack: it is not the top of its stack\\. --whole-stack lands the entire stack; name its top segment `(.+?)` instead\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/but-api/src/land/mod.rs","lineNumber":411,"sourceCode":"/// commits in any segment that would be published (the same guard `but push` applies before\n/// sending commits to a remote). All computed from the graph workspace, not stack projections.\n/// Returns the named lower segments that land together with `branch` — non-empty only for a\n/// validated `--whole-stack` land.\nfn validate_branch_landing(\n    ctx: &mut Context,\n    branch: &str,\n    target_display: &str,\n    whole_stack: bool,\n) -> anyhow::Result<Vec<String>> {\n    let Some(scan) = scan_stack(ctx, branch)? else {\n        return Ok(Vec::new());\n    };\n\n    if whole_stack && scan.has_upper {\n        // Judge \"top of the stack\" by position, not by names: a segment whose branch ref was\n        // deleted still holds commits that \"land the entire stack\" would have to include.\n        if let Some(top) = scan.upper_segments.first() {\n            bail!(\n                \"Refusing to land `{branch}` with --whole-stack: it is not the top of its stack. \\\n                 --whole-stack lands the entire stack; name its top segment `{top}` instead.\",\n            );\n        }\n        bail!(\n            \"Refusing to land `{branch}` with --whole-stack: it is not the top of its stack — \\\n             unnamed segment(s) with commits sit above it (their branch refs no longer exist), so \\\n             landing `{branch}` would not land the entire stack.\",\n        );\n    }\n    // Key off the commits below, not the segment names: segments whose branch ref was deleted are\n    // unnamed but their commits would be published all the same.\n    let publishes_below = scan.commits_below > 0 || !scan.lower_segments.is_empty();\n    if publishes_below && !whole_stack {\n        if scan.lower_segments.is_empty() {\n            bail!(\n                \"Refusing to land `{branch}`: {} commit(s) on unnamed segment(s) below it would \\\n                 also be published to {target_display}. Pass --whole-stack to land `{branch}` \\","sourceCodeStart":393,"sourceCodeEnd":429,"githubUrl":"https://github.com/gitbutlerapp/gitbutler/blob/58e5313667b857ef39a730e380af31816a7b1768/crates/but-api/src/land/mod.rs#L393-L429","documentation":"`validate_branch_landing` refuses a `--whole-stack` land when the target branch is not the topmost segment of its stack but a named segment sits above it. `--whole-stack` is defined to land the entire stack, and landing a mid-stack branch that way would strand the commits above it, so GitButler rejects the request and names the actual top segment to use instead.","triggerScenarios":"Calling `branch_land` with `whole_stack: true` (CLI `but land <branch> --whole-stack`) where `scan.has_upper` is true and `scan.upper_segments` has a named first segment — i.e. the branch has a segment above it in the same stack.","commonSituations":"Working in a stacked-branch workflow, forgetting that the stack has more segments on top; a branch ref above was renamed so the developer assumes their branch is the top; scripting lands on a fixed branch name that is no longer the stack top.","solutions":["Land the named top segment instead: `but land <top-segment> --whole-stack` (the error message names it).","Drop or rebase off the segments above first, then re-run `but land <branch> --whole-stack`.","Land without `--whole-stack` if you only intend to publish this branch's commits."],"exampleFix":"// before\nbut land middle-branch --whole-stack   // refused: not top of stack\n// after\nbut land top-branch --whole-stack      # lands the entire stack including middle-branch","handlingStrategy":"validation","validationCode":"// before landing with --whole-stack, verify the branch is the stack top\nconst stack = await api.listStacks();\nconst s = stack.find(s => s.branches.includes(branch));\nconst isTop = s.branches[s.branches.length - 1] === branch;\nif (!isTop) throw new Error(`Land the top segment ${s.branches.at(-1)} with --whole-stack instead`);","typeGuard":"function isStackTop(branch: string, stackBranches: string[]): boolean {\n  return stackBranches[stackBranches.length - 1] === branch;\n}","tryCatchPattern":"try {\n  await api.land(branch, { wholeStack: true });\n} catch (e) {\n  const m = String(e).match(/name its top segment `([^`]+)`/);\n  if (m) await api.land(m[1], { wholeStack: true }); // retry with the named top segment\n  else throw e;\n}","preventionTips":["Run `but status` (or list the stack) to confirm branch position before a whole-stack land.","Only use --whole-stack from the top of a stack; land bottom segments individually otherwise.","Automate land scripts to resolve the stack top dynamically rather than hardcoding a branch name."],"tags":["git","stacked-branches","validation","cli"],"backgroundTag":"invalid-state-transition","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"}