sequencer: factor out parsing of todo commands

Move the code that parses todo commands into a separate function so that it can be shared with "git status" in the next commit. As we know the input is NUL terminated we do not pass a pointer to the end of the line and instead test for a blank line by looking for NUL, CR LF, or LF. We use starts_with() instead of starts_with_mem() for the same reason. This results in slightly different behavior when there a CR at the start of the line that is not followed by LF. Previously such a line was treated as a comment rather than an invalid line. Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Phillip Wood committed Jun 23, 2026 at 16:53 UTC 10c2678a2bfbd2a3e8d0a3623d1b71b6cc916253
2 files changed +35 -12
sequencer.c
+27 -12
@@ -2627,6 +2627,27 @@ static int is_command(enum todo_command command, const char **bol)
2627 return 0;
2628 }
2629
2630 +bool sequencer_parse_todo_command(const char **p, enum todo_command *cmd)
2631 +{
2632 + const char *s = *p;
2633 +
2634 + for (int i = 0; i < TODO_COMMENT; i++)
2635 + if (is_command(i, p)) {
2636 + *cmd = i;
2637 + return true;
2638 + }
2639 +
2640 + if (starts_with(s, comment_line_str)) {
2641 + *cmd = TODO_COMMENT;
2642 + return true;
2643 + } else if (s[0] == '\n' || (s[0] == '\r' && s[1] == '\n') || !s[0]) {
2644 + *cmd = TODO_COMMENT;
2645 + return true;
2646 + }
2647 +
2648 + return false;
2649 +}
2650 +
2651 static int check_label_or_ref_arg(enum todo_command command, const char *arg)
2652 {
2653 switch (command) {
@@ -2716,30 +2737,24 @@ static int parse_insn_line(struct repository *r, struct replay_opts *opts,
2737 {
2738 struct object_id commit_oid;
2739 char *end_of_object_name;
2719 - int i, saved, status, padding;
2740 + int saved, status, padding;
2741
2742 item->flags = 0;
2743
2744 /* left-trim */
2745 bol += strspn(bol, " \t");
2746
2726 - if (bol == eol || *bol == '\r' || starts_with_mem(bol, eol - bol, comment_line_str)) {
2727 - item->command = TODO_COMMENT;
2747 + if (!sequencer_parse_todo_command(&bol, &item->command))
2748 + return error(_("invalid command '%.*s'"),
2749 + (int)strcspn(bol, " \t\r\n"), bol);
2750 +
2751 + if (item->command == TODO_COMMENT) {
2752 item->commit = NULL;
2753 item->arg_offset = bol - buf;
2754 item->arg_len = eol - bol;
2755 return 0;
2756 }
2757
2734 - for (i = 0; i < TODO_COMMENT; i++)
2735 - if (is_command(i, &bol)) {
2736 - item->command = i;
2737 - break;
2738 - }
2739 - if (i >= TODO_COMMENT)
2740 - return error(_("invalid command '%.*s'"),
2741 - (int)strcspn(bol, " \t\r\n"), bol);
2742 -
2758 /* Eat up extra spaces/ tabs before object name */
2759 padding = strspn(bol, " \t");
2760 bol += padding;
sequencer.h
+8
@@ -262,6 +262,14 @@ int read_author_script(const char *path, char **name, char **email, char **date,
262 int write_basic_state(struct replay_opts *opts, const char *head_name,
263 struct commit *onto, const struct object_id *orig_head);
264 void sequencer_post_commit_cleanup(struct repository *r, int verbose);
265 +
266 +/*
267 + * Try to parse the todo command pointed to by *p. On success sets cmd,
268 + * advances p and returns true. On failure returns false, leaves p and
269 + * cmd unchanged.
270 + */
271 +bool sequencer_parse_todo_command(const char **p, enum todo_command *cmd);
272 +
273 int sequencer_get_last_command(struct repository* r,
274 enum replay_action *action);
275 int sequencer_determine_whence(struct repository *r, enum commit_whence *whence);