GitoxideLabs/gitoxide · error
cannot split with unresolved conflicts
Error message
cannot split with unresolved conflicts
What it means
Before preparing a commit split, `prepare` loads the worktree changes and refuses to proceed if any path is in the `Unmerged` state. Splitting requires a clean, resolvable set of staged and unstaged hunks; unresolved merge conflicts would make the split ambiguous, so it is aborted up front (before the editor is launched).
Solutions
- Resolve the merge conflicts (`gix`/git status shows unmerged paths), then `git add` the resolved files.
- Abort the in-progress merge/rebase (`git merge --abort` / `git rebase --abort`) if it should not be continued.
- Run `gix tix split` again once `git status` reports no unmerged paths.
- Ensure both staged and worktree changes exist afterward, since the same function also requires both groups.
Example fix
// before: splitting during a conflicted merge
repo_split_prepare(&repo)?;
// after: guard on unmerged state first
if has_unmerged_paths(&repo)? {
anyhow::bail!("resolve conflicts before splitting");
}
repo_split_prepare(&repo)?; Defensive patterns
Strategy: validation
Validate before calling
fn has_unmerged_paths(repo: &gix::Repository) -> Result<bool> {
let changes = load_worktree_changes_without_lines(repo)?;
Ok(changes.paths.iter().any(|c| c.kind == ChangeKind::Unmerged))
} Try / catch
match prepare(&repo) {
Ok(p) => p,
Err(e) if e.to_string().contains("unresolved conflicts") => {
eprintln!("Resolve merge conflicts (git status) before running split.");
Err(e)
}
Err(e) => return Err(e),
} Prevention
- Check `git status`/unmerged entries before starting a split; finish or abort any in-progress merge or rebase first.
- Automate conflict detection (ChangeKind::Unmerged check) at the start of any stacked-edit workflow.
- Remember prepare also requires both staged and unstaged changes — verify groups beforehand.
When it happens
Trigger: Calling `prepare` (via `splits_staged_and_worktree_changes_without_touching_files_during_preparation` or `conflicting_staged_and_worktree_hunks_abort_before_the_editor`) when `load_worktree_changes_without_lines` reports at least one change with `kind == ChangeKind::Unmerged` in the repository.
Common situations: A merge or rebase left conflict markers and the user runs `gix tix split` before resolving; a cherry-pick conflict is still open; index contains unmerged entries from a conflicted operation.
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
- splitting requires both staged and worktree changes
- cannot time-travel with unresolved index conflicts
- Need a worktree to clean, this is a bare repository
- JSON output isn't implemented yet
- cannot create a commit with unresolved index conflicts
AI-assisted analysis of GitoxideLabs/gitoxide@e73179060b (2026-09-08).
Data as JSON: /api/errors/538b0ad6ea656f10.
Report an issue: GitHub.
Appendix: source
Thrown at gix-tix/src/edit/split.rs:24
pub(crate) struct Prepared {
pub editor: Option<gix::command::Prepare>,
pub document: Vec<u8>,
create: create::Prepared,
target: ObjectId,
source: gix::objs::Commit,
tree: ObjectId,
}
#[tracing::instrument(skip_all)]
pub(crate) fn prepare(mut repo: gix::Repository, todo: bool) -> Result<Prepared> {
let target = repo
.head_id()
.context("splitting requires an existing HEAD commit")?
.detach();
let changes = load_worktree_changes_without_lines(&repo)?;
if changes.paths.iter().any(|change| change.kind == ChangeKind::Unmerged) {
anyhow::bail!("cannot split with unresolved conflicts");
}
if !changes.paths.iter().any(|change| change.group == ChangeGroup::Staged)
|| !changes.paths.iter().any(|change| change.group == ChangeGroup::Unstaged)
{
anyhow::bail!("splitting requires both staged and worktree changes");
}
let mut source = repo
.find_commit(target)
.context("could not find HEAD commit")?
.decode()
.context("could not decode HEAD commit")?
.into_owned()
.context("could not own HEAD commit")?;
let mut create = create::prepare_from(repo.clone(), Some(target), create::Source::Default, None, todo)?;
repo.objects.set_object_memory(std::mem::take(&mut create.objects));
let head_tree = source.tree;View on GitHub (pinned to e73179060b)