{"record":{"id":"f588a544ed0b444c","repo":"GitoxideLabs/gitoxide","slug":"the-last-fork-section-contains-no-commits","errorCode":null,"errorMessage":"the last fork section contains no commits","messagePattern":"the last fork section contains no commits","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-tix/src/edit/todo.rs","lineNumber":1018,"sourceCode":"            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()\n            .filter(|name| ref_targets.get(name) == Some(&target)),\n        (None, None) => None,\n    };","sourceCodeStart":1000,"sourceCodeEnd":1036,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-tix/src/edit/todo.rs#L1000-L1036","documentation":"When the parsed todo contains more than one fork section, the final section must contain at least one commit command (`pick`, `empty`, `squash`, or a reference line). `parse` throws this error when the todo ends with a `fork` heading that is never followed by any command, which would leave the plan with an unusable trailing section.","triggerScenarios":"Editing the todo so the last `──── fork <id> ────` heading has no `pick`/`empty`/`squash`/reference lines beneath it (e.g. deleting all trailing picks, or leaving a stray duplicated fork heading at the end).","commonSituations":"A user removes the trailing lines of the todo while pruning history but keeps the fork heading; a scripted todo generator appends a fork marker without emitting the section's commits; the editor truncated the file.","solutions":["Add at least one `pick`, `empty`, `squash`, or `(ref…)` line below the final fork heading.","Delete the trailing fork heading entirely if its section is intentionally empty and it is the last section.","Merge the trailing empty section into the previous section by removing the extra fork separator."],"exampleFix":"// before\n──── fork aaa1 base ────\npick bbb2 ...\n──── fork ccc3 tip ────\n\n// after (heading removed)\n──── fork aaa1 base ────\npick bbb2 ...","handlingStrategy":"validation","validationCode":"// Rust: ensure the last fork section is non-empty before submitting the todo\nlet editable: Vec<&str> = text.lines()\n    .filter(|l| !l.trim().is_empty() && !l.trim().starts_with(\"# \") && !l.contains(\"<!--\"))\n    .collect();\nlet last_is_bare_fork = editable.last()\n    .map(|l| l.starts_with('─') && l.trim_matches('─').trim().starts_with(\"fork \"))\n    .unwrap_or(false);\nif last_is_bare_fork {\n    return Err(\"last fork section contains no commits\");\n}","typeGuard":null,"tryCatchPattern":"match parse_plan(&repo, text) {\n    Ok(plan) => apply(plan),\n    Err(e) if e.to_string().contains(\"last fork section contains no commits\") => {\n        eprintln!(\"Add a command under the final fork heading or delete it.\");\n        Err(e)\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["When deleting trailing commits, delete their fork heading too.","Check the bottom of the todo before saving: the last line should be a command, not a fork heading.","Avoid duplicate fork separators at the end of the file."],"tags":["rebase","todo-parsing","validation"],"backgroundTag":"empty-required-field","analyzedSha":"e73179060badf27222d790981fac3f84c1830a7e","analyzedAt":"2026-09-08T11:26:50.865Z","contentChangedAt":"2026-09-08T11:26:50.865Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}