nikivdev/code · error
detached HEAD (checkout a branch first)
Error message
detached HEAD (checkout a branch first)
What it means
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.
Source
Thrown at src/push.rs:343
if !output.status.success() {
bail!("not inside a git repository");
}
let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
Ok(PathBuf::from(path))
}
fn current_branch(repo_root: &Path) -> Result<String> {
let output = Command::new("git")
.args(["rev-parse", "--abbrev-ref", "HEAD"])
.current_dir(repo_root)
.output()
.context("failed to read current branch")?;
if !output.status.success() {
bail!("git rev-parse --abbrev-ref HEAD failed");
}
let name = String::from_utf8_lossy(&output.stdout).trim().to_string();
if name.is_empty() || name == "HEAD" {
bail!("detached HEAD (checkout a branch first)");
}
Ok(name)
}
pub(crate) fn git_capture_in(repo_root: &Path, args: &[&str]) -> Result<String> {
let output = Command::new("git")
.args(args)
.current_dir(repo_root)
.output()
.with_context(|| format!("failed to run git {}", args.join(" ")))?;
if !output.status.success() {
bail!("git {} failed", args.join(" "));
}
Ok(String::from_utf8_lossy(&output.stdout).to_string())
}
pub(crate) fn git_run_in(repo_root: &Path, args: &[&str]) -> Result<()> {
git_run_in_with_env(repo_root, args, &[])View on GitHub (pinned to a747e741ae)
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`)
Example fix
// before HEAD detached at abc1234 $ f push detached HEAD (checkout a branch first) // after $ git checkout main $ f push
Defensive patterns
Strategy: validation
Validate before calling
let name = String::from_utf8(
std::process::Command::new("git")
.args(["rev-parse", "--abbrev-ref", "HEAD"])
.output()?
.stdout,
).unwrap().trim().to_string();
if name.is_empty() || name == "HEAD" { eprintln!("detached HEAD; checkout a branch first"); } Try / catch
match result {
Err(e) if e.to_string().contains("detached HEAD") => {
eprintln!("git checkout <branch> then retry");
}
Err(e) => return Err(e),
Ok(v) => Ok(v),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Specify branch with: f upstream setup --branch <branch-name>
- jj git export retry loop should always return
- Lin.app is not running
- No branches available to search
- No branches matched query '{}'.
AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01).
Data as JSON: /api/errors/7e1f5dd97c35cb66.
Report an issue: GitHub.