{"record":{"id":"8a350f810c57facf","repo":"GitoxideLabs/gitoxide","slug":"splitting-requires-both-staged-and-worktree-change","errorCode":null,"errorMessage":"splitting requires both staged and worktree changes","messagePattern":"splitting requires both staged and worktree changes","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-tix/src/edit/split.rs","lineNumber":29,"sourceCode":"    target: ObjectId,\n    source: gix::objs::Commit,\n    tree: ObjectId,\n}\n\n#[tracing::instrument(skip_all)]\npub(crate) fn prepare(mut repo: gix::Repository, todo: bool) -> Result<Prepared> {\n    let target = repo\n        .head_id()\n        .context(\"splitting requires an existing HEAD commit\")?\n        .detach();\n    let changes = load_worktree_changes_without_lines(&repo)?;\n    if changes.paths.iter().any(|change| change.kind == ChangeKind::Unmerged) {\n        anyhow::bail!(\"cannot split with unresolved conflicts\");\n    }\n    if !changes.paths.iter().any(|change| change.group == ChangeGroup::Staged)\n        || !changes.paths.iter().any(|change| change.group == ChangeGroup::Unstaged)\n    {\n        anyhow::bail!(\"splitting requires both staged and worktree changes\");\n    }\n\n    let mut source = repo\n        .find_commit(target)\n        .context(\"could not find HEAD commit\")?\n        .decode()\n        .context(\"could not decode HEAD commit\")?\n        .into_owned()\n        .context(\"could not own HEAD commit\")?;\n    let mut create = create::prepare_from(repo.clone(), Some(target), create::Source::Default, None, todo)?;\n    repo.objects.set_object_memory(std::mem::take(&mut create.objects));\n\n    let head_tree = source.tree;\n    let index_tree = create.tree;\n    let index = repo\n        .find_tree(index_tree)\n        .context(\"could not load the prepared index tree\")?;\n    let worktree_tree = create::worktree_tree_with_changes(&repo, &index, &changes)?;","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-tix/src/edit/split.rs#L11-L47","documentation":"This error means `gix tix edit split` was asked to split a commit, but the working tree does not have changes in both the staged (index) and unstaged (worktree) change groups. The split operation partitions the diff of a commit by interacting with staged and worktree state, so both must be present in the change list. The library checks `changes.paths` for at least one path in `ChangeGroup::Staged` and at least one in `ChangeGroup::Unstaged` and bails if either group is empty.","triggerScenarios":"Calling the split preparation (via the CLI `tix edit split` flow) when the index matches HEAD (nothing staged) or when the worktree is clean relative to the index (nothing unstaged), so `changes.paths` lacks one of the two groups.","commonSituations":"Running `split` right after committing (staged group empty); running `split` with only `git add`ed changes but no worktree edits; running split twice, the second time after all hunks were consumed into splits.","solutions":["Stage some changes (e.g. `git add -p`) if only worktree changes exist, or make additional worktree edits, so both groups are non-empty","Unstage some changes (`git restore --staged <path>`) if everything is staged","Verify with `git status` / the tix review listing that both staged and unstaged entries exist before invoking split","If split is not the intended operation, use a different edit command that tolerates a single change group"],"exampleFix":"// before: invoking split with only staged changes\nedit_split(&repo, target)?;\n// after: guard before invoking\nlet changes = collect_changes(&repo, target)?;\nlet has_staged = changes.paths.iter().any(|c| c.group == ChangeGroup::Staged);\nlet has_unstaged = changes.paths.iter().any(|c| c.group == ChangeGroup::Unstaged);\nif has_staged && has_unstaged {\n    edit_split(&repo, target)?;\n}","handlingStrategy":"validation","validationCode":"let changes = collect_changes(&repo, target)?;\nlet has_staged = changes.paths.iter().any(|c| c.group == ChangeGroup::Staged);\nlet has_unstaged = changes.paths.iter().any(|c| c.group == ChangeGroup::Unstaged);\nif !has_staged || !has_unstaged {\n    anyhow::bail!(\"split needs both staged and worktree changes; run `git status` first\");\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run `git status` (or the tix review listing) before split to confirm both staged and unstaged entries exist","Check for unresolved conflicts first — split aborts on unmerged paths too","Avoid scripted splits immediately after commit or full staging operations"],"tags":["git","precondition","split","staged-changes"],"backgroundTag":"invalid-state-transition","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}