{"record":{"id":"58dac021dd297d0f","repo":"GitoxideLabs/gitoxide","slug":"the-rebase-todo-has-no-fork-heading","errorCode":null,"errorMessage":"the rebase todo has no fork heading","messagePattern":"the rebase todo has no fork heading","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-tix/src/edit/todo.rs","lineNumber":1015,"sourceCode":"        }\n        steps.push(rebase::PlanStep {\n            parent,\n            commit,\n            squash: Vec::new(),\n        });\n        cursor = Some(rebase::PlanParent::Step(index));\n        section_last_step = Some(index);\n        if marked {\n            let target = rebase::PlanParent::Step(index);\n            if checkout_target.is_some_and(|checkout| checkout != target) {\n                anyhow::bail!(\"the @ command and @ reference point to different results\");\n            }\n            checkout_target = Some(target);\n        }\n        section_has_commit = true;\n    }\n    if sections == 0 {\n        anyhow::bail!(\"the rebase todo has no fork heading\");\n    }\n    if sections > 1 && !section_has_commit {\n        anyhow::bail!(\"the last fork section contains no commits\");\n    }\n    if state.marker_required && checkout_target.is_none() {\n        anyhow::bail!(\"the current checkout marker must be retained\");\n    }\n    let checkout_reference = match (checkout_target, explicit_checkout_reference) {\n        (Some(target), Some((name, reference_target))) => {\n            if target != reference_target {\n                anyhow::bail!(\"the @ command and @ reference point to different results\");\n            }\n            Some(name)\n        }\n        (None, Some(_)) => anyhow::bail!(\"an @ reference requires an @ command at the same result\"),\n        (Some(target), None) => state\n            .head_ref\n            .take()","sourceCodeStart":997,"sourceCodeEnd":1033,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-tix/src/edit/todo.rs#L997-L1033","documentation":"The interactive-rebase todo parser in gix-tix requires every todo to begin with a `fork <commit-id>` separator heading, which anchors the first section of the plan. If the edited todo text, after stripping comments and blank lines, contains no fork heading at all, `parse` (called via `parse_plan`) aborts with this error. This is a structural validation of user-edited todo content, since the tool only allows editing history between fork points.","triggerScenarios":"Calling `parse`/`parse_plan` on todo text where all `─ fork <id> ─` separator lines were deleted by the user in the editor, where the whole file was emptied, or where every remaining line is a comment or blank.","commonSituations":"A user clears the editor buffer or a misconfigured `$EDITOR`/script overwrites the todo file with empty or template-only content; commenting out everything including the fork heading with `#` or `<!-- -->`.","solutions":["Reopen the todo and keep at least one `───── fork <commit-id> ─────` heading line above the first command.","Ensure the fork heading is not commented out (it must not start with `# ` or be inside an `<!-- -->` block).","If the edit was accidental, abort/cancel the todo edit and restart the command to regenerate a fresh todo."],"exampleFix":"// before (todo file, no heading)\npick 1a2b3c4d\n\n// after\n────── fork 9f8e7d6c some base ──────\npick 1a2b3c4d","handlingStrategy":"validation","validationCode":"// Rust: check the edited todo keeps at least one fork heading\nlet has_fork = text.lines().any(|l| {\n    let l = l.trim();\n    !l.starts_with('#') && !l.is_empty() && l.starts_with('─') && l.trim_matches('─').trim().starts_with(\"fork \")\n});\nif !has_fork {\n    return Err(\"todo must keep at least one `fork <id>` heading\");\n}","typeGuard":null,"tryCatchPattern":"match parse_plan(&repo, text) {\n    Ok(plan) => apply(plan),\n    Err(e) if e.to_string().contains(\"no fork heading\") => {\n        eprintln!(\"Todo edit removed the required fork heading; regenerating todo.\");\n        regenerate_todo();\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Never clear the whole editor buffer; delete only the lines you intend to change.","Keep the `──── fork <id> ────` separators intact when pruning commands.","Abort and rerun the command instead of hand-reconstructing a todo from scratch."],"tags":["rebase","todo-parsing","validation"],"backgroundTag":"missing-required-argument","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"}