{"record":{"id":"c386ea5fe44fd01c","repo":"GitoxideLabs/gitoxide","slug":"a-fork-section-contains-no-commits","errorCode":null,"errorMessage":"a fork section contains no commits","messagePattern":"a fork section contains no commits","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gix-tix/src/edit/todo.rs","lineNumber":874,"sourceCode":"        if line.starts_with(\"<!--\") {\n            in_comment = !line.contains(\"-->\");\n            continue;\n        }\n        if line.is_empty() || line.starts_with(\"# \") {\n            continue;\n        }\n        editable.push(line);\n    }\n\n    for line in editable.into_iter().rev() {\n        if line.starts_with('─') && line.ends_with('─') {\n            let target = line\n                .trim_matches('─')\n                .trim()\n                .strip_prefix(\"fork \")\n                .context(\"a fork separator needs a fork ID\")?;\n            if sections > 0 && !section_has_commit {\n                anyhow::bail!(\"a fork section contains no commits\");\n            }\n            let id = resolve_commit(\n                repo,\n                target\n                    .split_whitespace()\n                    .next()\n                    .context(\"a fork heading needs a commit ID\")?,\n            )?;\n            cursor = Some(if let Some(index) = picked.get(&id) {\n                rebase::PlanParent::Step(*index)\n            } else if scope.contains(&id) {\n                anyhow::bail!(\"a fork target must be picked before it is used\");\n            } else {\n                rebase::PlanParent::Existing(id)\n            });\n            sections += 1;\n            section_has_commit = false;\n            section_last_step = None;","sourceCodeStart":856,"sourceCodeEnd":892,"githubUrl":"https://github.com/GitoxideLabs/gitoxide/blob/e73179060badf27222d790981fac3f84c1830a7e/gix-tix/src/edit/todo.rs#L856-L892","documentation":"When parsing an edited rebase todo, lines are processed bottom-up and each `──fork <id>──` separator starts a new fork section. Before opening a new section, the previous one must contain at least one line (a command or reference line). An empty fork section is rejected because it would produce no plan steps.","triggerScenarios":"`parse` bails when it encounters a fork separator while `sections > 0 && !section_has_commit` — i.e. a previous fork section contained only another fork heading (or nothing) with no commit-producing lines between them. Happens when a user deletes all lines within a fork section in the editor but leaves the heading.","commonSituations":"Manually cleaning up the todo file and deleting the contents of a fork section but not its fork separator; editor macros stripping lines; pasting malformed todo templates.","solutions":["Remove the leftover `──fork ...──` separator for the now-empty section","Add back at least one command or reference line under the fork heading","Reopen the todo editor and let tix regenerate the template before editing"],"exampleFix":"// before\n──fork abc1234──\n──fork def5678──\npick 1111111\n// after\n──fork abc1234──\npick abc1234\n──fork def5678──\npick 1111111","handlingStrategy":"validation","validationCode":"// check each fork section has content before saving the todo\nlet sections: Vec<&str> = text.split(\"──fork \").skip(1).collect();\nfor s in &sections {\n    let body = s.splitn(2, '\\n').nth(1).unwrap_or(\"\");\n    if body.trim().is_empty() {\n        return Err(\"fork section has no commands or references\");\n    }\n}","typeGuard":null,"tryCatchPattern":"if let Err(e) = parse(repo, edited) {\n    if e.to_string().contains(\"fork section contains no commits\") {\n        // reopen the editor or strip the empty fork heading automatically\n    }\n}","preventionTips":["Delete the fork separator when emptying a section","Preview the todo diff before saving","Regenerate the template instead of editing from memory"],"tags":["rebase","todo-parsing","fork-section"],"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-15T23:17:13.987Z"}