GitoxideLabs/gitoxide · error
cannot time-travel with unresolved index conflicts
Error message
cannot time-travel with unresolved index conflicts
What it means
`perform_reporting_rebased` refuses to start a time-travel operation while the repository index contains entries that are not unconflicted (i.e. an unresolved merge conflict is staged or present in the index). Before moving HEAD or materializing a different review state, the index must be clean of conflict stages, otherwise the conflict state could be silently lost or mangled. It is a guard ensuring time-travel only happens from a coherent index state.
Solutions
- Resolve the conflicted files and stage them (`git add <files>`) so the index only holds unconflicted stage-0 entries, then retry.
- Abort the in-progress merge/rebase with `git merge --abort` or `git rebase --abort` to clear conflict stages.
- Inspect the index with `git ls-files -u` to list unmerged entries before re-running the operation.
- Commit or stash the current conflict resolution before invoking time-travel.
Example fix
// before (conflicted index)
// conflict markers still in file.rs, not staged
if repo.time_travel(target).is_err() { /* cannot time-travel with unresolved index conflicts */ }
// after
// resolve file.rs, then:
repo.git(&["add", "file.rs"]).expect("stage resolution");
repo.time_travel(target).expect("index is conflict-free"); Defensive patterns
Strategy: validation
Validate before calling
let has_conflicts = repo.index_or_empty()?.entries().iter()
.any(|e| e.stage() != gix::index::entry::Stage::Unconflicted);
if has_conflicts {
anyhow::bail!("resolve index conflicts before time-travel");
} Try / catch
match perform_reporting_rebased(&mut repo, target) {
Ok(report) => /* proceed */,
Err(e) if e.to_string().contains("unresolved index conflicts") => {
// prompt user to resolve + `git add` before retrying
}
Err(e) => return Err(e),
} Prevention
- Run `git ls-files -u` (empty output expected) before any tix time-travel
- Automate conflict resolution + staging before invoking time-travel
- Abort lingering merge/rebase states in CI before running tix commands
When it happens
Trigger: Calling tix time-travel (via `perform` or related entry points) while the index has entries whose stage is not `gix::index::entry::Stage::Unconflicted`, i.e. after a merge/rebase/cherry-pick conflict was resolved in the worktree but never `git add`ed, or not resolved at all.
Common situations: A user starts a tix time-travel right after a conflicted merge; an automated tool aborts mid-resolution leaving stage entries; a CI environment inherits a dirty repo with leftover conflict stages.
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 create a commit with unresolved index conflicts
- cannot spill an unmerged path
- could not apply conflict stages to the prepared index
- cannot split with unresolved conflicts
- the conflict index still has unresolved entries
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/b89f7b206faf4f18.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/time_travel.rs:762
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();
let mut ref_rewrites = Vec::new();
let mut ref_changes = Vec::new();
let mut pending = pending_base(&repository, selected)?;
while let Some(base) = pending {
let graph = completed_graph.as_ref().unwrap_or(graph);
let mut rebased = Vec::new();
let outcome = super::rebase::perform_reporting_rebased(
&repository,
graph,
super::rebase::Edit::Repeat {
base,View on GitHub (pinned to e73179060b)