GitoxideLabs/gitoxide · error

cannot time-travel from an unborn HEAD

Error message

cannot time-travel from an unborn HEAD

What it means

Time travel cannot start because HEAD is unborn: the repository has no commits (HEAD points at a branch that does not exist yet), so `head.id()` returns None. The library bails since there is no commit to travel from. Thrown at the start of `perform_reporting_rebased`.

Solutions

  1. Create an initial commit before time-traveling (`git commit --allow-empty` if appropriate).
  2. Check out a branch that has at least one commit.
  3. Verify with `git rev-parse HEAD` (must not error) before invoking the operation.

Example fix

// before (fresh repo)
$ gix time-travel ...  # unborn HEAD
// after
$ git commit --allow-empty -m "initial"
$ gix time-travel ...
Defensive patterns

Strategy: validation

Validate before calling

let repo = gix::open(repo_path)?;
let head = repo.head()?;
if head.is_unborn() || head.id().is_none() {
    return Err(anyhow!("repository has no commits; create an initial commit first"));
}

Type guard

fn head_is_born(repo: &gix::Repository) -> bool {
    repo.head().ok().and_then(|h| h.id()).is_some()
}

Try / catch

match result {
    Err(e) if e.to_string().contains("cannot time-travel from an unborn HEAD") => {
        // create an initial commit, then retry
    }
    other => other?,
}

Prevention

When it happens

Trigger: Calling perform (or the listed tests) on a freshly `git init`-ed repository before the first commit, or on a repo whose current branch has no commits, so `head.id()` is None and the `let ... else` bails.

Common situations: Running the tool right after initializing a repo; an orphan branch checkout with no commits; a repo where the initial commit failed and the branch never materialized.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08). Data as JSON: /api/errors/55a74494acb5df31. Report an issue: GitHub.

Appendix: source

Thrown at gix-tix/src/edit/time_travel.rs:751

    clippy::too_many_arguments,
    reason = "time travel context plus rebased-commit reporting"
)]
pub(crate) fn perform_reporting_rebased(
    repository_path: &Path,
    bare: bool,
    mut selected: ObjectId,
    graph: &history::HistoryGraph,
    review_roots: &[ObjectId],
    revisions: &[OsString],
    include_worktrees: bool,
    mut report: impl FnMut(ObjectId),
) -> Result<Perform> {
    let mut repository =
        open_repository(repository_path, bare, false).context("could not open repository for time-travel")?;
    repository.workdir().context("time-travel requires a worktree")?;
    let head = repository.head().context("could not read HEAD before time-travel")?;
    let Some(mut head_id) = head.id().map(gix::Id::detach) else {
        anyhow::bail!("cannot time-travel from an unborn HEAD");
    };
    let head_was_detached = head.is_detached();
    drop(head);
    if repository
        .index_or_empty()
        .context("could not inspect the index before time-travel")?
        .entries()
        .iter()
        .any(|entry| entry.stage() != gix::index::entry::Stage::Unconflicted)
    {
        anyhow::bail!("cannot time-travel with unresolved index conflicts");
    }
    let source_review = review_tree(&repository, graph, review_roots, head_id)?;
    let destination_review = review_tree(&repository, graph, review_roots, selected)?;
    let crosses_review_boundary =
        source_review.as_ref().map(|review| review.root) != destination_review.as_ref().map(|review| review.root);
    let mut completed_graph = None;
    let mut original_ids = HashMap::new();

View on GitHub (pinned to e73179060b)