{"record":{"id":"7e1f5dd97c35cb66","repo":"nikivdev/code","slug":"detached-head-checkout-a-branch-first","errorCode":null,"errorMessage":"detached HEAD (checkout a branch first)","messagePattern":"detached HEAD \\(checkout a branch first\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/push.rs","lineNumber":343,"sourceCode":"    if !output.status.success() {\n        bail!(\"not inside a git repository\");\n    }\n    let path = String::from_utf8_lossy(&output.stdout).trim().to_string();\n    Ok(PathBuf::from(path))\n}\n\nfn current_branch(repo_root: &Path) -> Result<String> {\n    let output = Command::new(\"git\")\n        .args([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"])\n        .current_dir(repo_root)\n        .output()\n        .context(\"failed to read current branch\")?;\n    if !output.status.success() {\n        bail!(\"git rev-parse --abbrev-ref HEAD failed\");\n    }\n    let name = String::from_utf8_lossy(&output.stdout).trim().to_string();\n    if name.is_empty() || name == \"HEAD\" {\n        bail!(\"detached HEAD (checkout a branch first)\");\n    }\n    Ok(name)\n}\n\npub(crate) fn git_capture_in(repo_root: &Path, args: &[&str]) -> Result<String> {\n    let output = Command::new(\"git\")\n        .args(args)\n        .current_dir(repo_root)\n        .output()\n        .with_context(|| format!(\"failed to run git {}\", args.join(\" \")))?;\n    if !output.status.success() {\n        bail!(\"git {} failed\", args.join(\" \"));\n    }\n    Ok(String::from_utf8_lossy(&output.stdout).to_string())\n}\n\npub(crate) fn git_run_in(repo_root: &Path, args: &[&str]) -> Result<()> {\n    git_run_in_with_env(repo_root, args, &[])","sourceCodeStart":325,"sourceCodeEnd":361,"githubUrl":"https://github.com/nikivdev/code/blob/a747e741ae92c09071d0ae946ab48488adcff1ce/src/push.rs#L325-L361","documentation":"Raised by current_branch in src/push.rs when `git rev-parse --abbrev-ref HEAD` succeeds but returns empty or the literal string \"HEAD\", which git does when the repository is in detached HEAD state. The mirror-push flow requires a named branch to push and refuse to guess one, so it aborts asking the user to check out a branch.","triggerScenarios":"The repository is in detached HEAD state (checked out a commit/tag directly, or a rebase/checkout of a remote ref) and the push command is run.","commonSituations":"Checking out a specific commit SHA or tag to inspect history, then forgetting to return to a branch; CI checkouts that default to detached HEAD; mid-rebase states.","solutions":["Run `git checkout <branch>` (e.g. `git checkout main`) to attach HEAD to a branch","If you made commits while detached, create a branch first: `git switch -c my-branch` then push","Finish or abort any in-progress rebase (`git rebase --continue` / `git rebase --abort`)"],"exampleFix":"// before\nHEAD detached at abc1234\n$ f push\ndetached HEAD (checkout a branch first)\n// after\n$ git checkout main\n$ f push","handlingStrategy":"validation","validationCode":"let name = String::from_utf8(\n    std::process::Command::new(\"git\")\n        .args([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"])\n        .output()?\n        .stdout,\n).unwrap().trim().to_string();\nif name.is_empty() || name == \"HEAD\" { eprintln!(\"detached HEAD; checkout a branch first\"); }","typeGuard":null,"tryCatchPattern":"match result {\n    Err(e) if e.to_string().contains(\"detached HEAD\") => {\n        eprintln!(\"git checkout <branch> then retry\");\n    }\n    Err(e) => return Err(e),\n    Ok(v) => Ok(v),\n}","preventionTips":["Avoid `git checkout <sha>/<tag>` right before pushing","Return to a named branch after inspecting history","In CI, create/checkout a branch instead of relying on detached checkouts"],"tags":["git","detached-head","branch"],"backgroundTag":"git-detached-head","analyzedSha":"a747e741ae92c09071d0ae946ab48488adcff1ce","analyzedAt":"2026-09-01T22:43:55.719Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}