{"record":{"id":"3c3615538adda321","repo":"gastownhall/beads","slug":"cannot-verify-unpushed-commits-target-is-detached","errorCode":null,"errorMessage":"cannot verify unpushed commits: target is detached; use --merged-into <ref>","messagePattern":"cannot verify unpushed commits: target is detached; use --merged-into <ref>","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/worktree_cmd.go","lineNumber":1967,"sourceCode":"\toid := strings.TrimSpace(string(output))\n\thashLength, err := repositoryObjectIDLength(ctx, git, executionRoot)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif !isHexObjectID(oid, hashLength) {\n\t\treturn \"\", fmt.Errorf(\"git returned invalid commit object ID %q\", oid)\n\t}\n\treturn strings.ToLower(oid), nil\n}\n\nfunc resolveUpstreamWorktreeComparator(\n\tctx context.Context,\n\tgit *worktreeRemovalGit,\n\texecutionRoot string,\n\ttarget pinnedWorktreeTarget,\n) (pinnedWorktreeComparator, error) {\n\tif target.branch == \"\" || target.detached {\n\t\treturn pinnedWorktreeComparator{}, fmt.Errorf(\n\t\t\t\"cannot verify unpushed commits: target is detached; use --merged-into <ref>\",\n\t\t)\n\t}\n\toutput, err := git.output(\n\t\tctx,\n\t\texecutionRoot,\n\t\t\"for-each-ref\",\n\t\t\"--format=%(upstream)\",\n\t\t\"--\",\n\t\ttarget.branch,\n\t)\n\tif err != nil {\n\t\treturn pinnedWorktreeComparator{}, fmt.Errorf(\"failed to inspect configured upstream: %w\", err)\n\t}\n\tupstreamRef := strings.TrimSpace(string(output))\n\tif upstreamRef == \"\" || strings.Contains(upstreamRef, \"\\n\") {\n\t\treturn pinnedWorktreeComparator{}, fmt.Errorf(\n\t\t\t\"cannot verify unpushed commits: the target branch has no single resolvable upstream; configure an upstream or use --merged-into <ref>\",","sourceCodeStart":1949,"sourceCodeEnd":1985,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/worktree_cmd.go#L1949-L1985","documentation":"This error is returned when the pinned-worktree verification needs to determine which commits on a worktree's branch are unpushed, but the target worktree is in a detached-HEAD state (or has no branch name). Without a branch there is no upstream or merge-base to compare against, so the library refuses to guess and asks the caller to supply an explicit reference via --merged-into <ref>.","triggerScenarios":"Calling the pinned-worktree comparison flow (newPinnedWorktreeComparator path in cmd/bd/worktree_cmd.go) when target.branch is empty or target.detached is true — i.e. the worktree was created with a detached HEAD (e.g. `git worktree add --detach`, checking out a raw commit SHA, or a rebase/checkout left HEAD detached).","commonSituations":"CI pipelines that check out commits by SHA into worktrees; a user who ran `git checkout <sha>` inside a linked worktree; worktrees created by tooling that pins an exact revision; a branch that was deleted leaving the worktree detached.","solutions":["Pass --merged-into <ref> to give the verifier an explicit reference to compare against (e.g. the base branch like main).","Re-attach the worktree to a branch: from inside the worktree run `git switch -c <branch>` (or `git switch <existing-branch>`).","If the detached state is unintended, recreate the worktree with `git worktree add <path> <branch>` instead of a SHA."],"exampleFix":"// before\nbd worktree remove mytask   # worktree is detached; verification fails\n// after\nbd worktree remove mytask --merged-into main","handlingStrategy":"validation","validationCode":"branch := execGit(\"git\", \"rev-parse\", \"--abbrev-ref\", \"HEAD\")\nif branch == \"HEAD\" || branch == \"\" {\n    // detached: supply --merged-into explicitly\n    args = append(args, \"--merged-into\", \"main\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always create worktrees bound to a branch (`git worktree add <path> <branch>`), never a raw SHA.","Before running worktree verification, check `git symbolic-ref -q HEAD` for emptiness (detached).","In CI, prefer `actions/checkout` with ref=<branch> over SHA checkouts when worktree tooling will run.","Adopt the habit of passing --merged-into explicitly in scripts so detached states never break them."],"tags":["git","worktree","detached-head","cli-flag-required"],"backgroundTag":"git-detached-head","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}