GitoxideLabs/gitoxide · error
cannot restore an unborn checkout after review setup failed
Error message
cannot restore an unborn checkout after review setup failed
What it means
If review setup fails midway, `restore_checkout` attempts to return the worktree to its previous position. When the repository had an unborn HEAD (a fresh repo with no commits — no branch target and no commit id recorded), there is nowhere to check out, so the restore is impossible and this error is raised during rollback.
Solutions
- Create an initial commit first (`git commit --allow-empty -m init`) so HEAD is born, then retry the review.
- Manually restore the unborn-HEAD state: delete the branch HEAD was moved to (`git update-ref -d <branch>`) if setup partially completed.
- Re-initialize the repository from scratch if it is disposable.
Example fix
// before: review in an empty repo fails and cannot roll back // shell: git commit --allow-empty -m "initial" // after: retry the review command
Defensive patterns
Strategy: validation
Validate before calling
let has_commits = repo.head_id().is_ok() || repo.head().ok().and_then(|h| h.try_into_referenced().ok()).is_some();
if !has_commits {
return Err(anyhow::anyhow!("create an initial commit before starting a review"));
} Try / catch
match review::start(repo, params) {
Err(e) if e.to_string().contains("unborn checkout") => {
eprintln!("repository has no commits; run: git commit --allow-empty -m init");
}
other => other?,
} Prevention
- Never run review workflows in freshly `git init`ed, commit-less repositories.
- Check `git rev-parse HEAD` succeeds before review operations.
- Require at least one commit in setup scripts or CI guards.
When it happens
Trigger: Review `start` failing on a repository with zero commits (HEAD points to a not-yet-created branch), triggering the rollback path in `restore_checkout` with `(None, None)`.
Common situations: Running `tix review` in a freshly `git init`ed repository before the first commit; a repo where all branches were deleted.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- cannot time-travel from an unborn HEAD
- Tried to use as tree, but was
- Tried to use as commit, but was
- Tried to use as tag, but was
- invalid mode change: can't flip executable bit of
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/dddbdc1ad9d08497.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/review.rs:377
fn remove_new_departure_pin(repository_path: &Path, bare: bool, pin: Option<&(history::Pin, bool)>) -> Result<()> {
let Some((pin, true)) = pin else { return Ok(()) };
let repo =
open_repository(repository_path, bare, false).context("could not reopen repository to remove review pin")?;
super::time_travel::delete_pin(&repo, pin).context("could not remove review departure pin")
}
fn restore_checkout(workdir: &Path, restore: &(Option<gix::refs::FullName>, Option<ObjectId>)) -> Result<()> {
let mut command = Command::new("git");
command.arg("-C").arg(workdir).args(["checkout", "--quiet", "--force"]);
match restore {
(Some(name), _) => {
let name = gix::path::from_bstr(name.as_bstr());
command.arg(name.as_ref());
}
(None, Some(id)) => {
command.args(["--detach", &id.to_string()]);
}
(None, None) => anyhow::bail!("cannot restore an unborn checkout after review setup failed"),
}
let output = command
.output()
.context("could not restore checkout after review setup failed")?;
if output.status.success() {
Ok(())
} else {
anyhow::bail!("{}", String::from_utf8_lossy(&output.stderr).trim())
}
}
#[cfg(test)]
mod tests {
use super::*;
fn run(path: &Path, args: &[&str]) -> gix_testtools::Result<Vec<u8>> {
let output = Command::new("git")
.arg("-C")View on GitHub (pinned to e73179060b)