{"thread":{"id":"49504","subject":"[PATCH 00/15] sequencer: refactor functions working on a todo_list","startedAt":"2018-10-07T20:00:11Z","lastAt":"2019-03-13T10:45:45Z","messageCount":190,"participants":["Alban Gruin","Phillip Wood","SZEDER Gábor","Junio C Hamano","Johannes Schindelin"],"isPatch":true,"patchVersion":1,"patchTotal":15},"messages":[{"id":"359796","messageId":"20181007195418.25752-1-alban.gruin@gmail.com","threadId":"49504","inReplyTo":null,"subject":"[PATCH 00/15] sequencer: refactor functions working on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-07T19:54:03Z","receivedAt":"2018-10-07T20:00:11Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"At the center of the \"interactive\" part of the interactive rebase lies\nthe todo list.  When the user starts an interactive rebase, a todo list\nis generated, presented to the user (who then edits it using a text\neditor), read back, and then is checked and processed before the actual\nrebase takes place.\n\nSome of this processing includes adding execs commands, reordering\nfixup! and squash! commits, and checking if no commits were accidentally\ndropped by the user.\n\nBefore I converted the interactive rebase in C, these functions were\ncalled by git-rebase--interactive.sh through git-rebase--helper.  Since\nthe only way to pass around a large amount of data between a shell\nscript and a C program is to use a file (or any declination of a file),\nthe functions that checked and processed the todo list were directly\nworking on a file, the same file that the user edited.\n\nDuring the conversion, I did not address this issue, which lead to a\ncomplete_action() that reads the todo list file, does some computation\nbased on its content, and writes it back to the disk, several times in\nthe same function.\n\nAs it is not an efficient way to handle a data structure, this patch\nseries refactor the functions that processes the todo list to work on a\ntodo_list structure instead of reading it from the disk.\n\nSome commits consists in modifying edit_todo_list() (initially used by\n--edit-todo) to handle the initial edition of the todo list, to increase\ncode sharing.\n\nAlban Gruin (15):\n  sequencer: clear the number of items of a todo_list before parsing\n  sequencer: make the todo_list structure public\n  sequencer: refactor check_todo_list() to work on a todo_list\n  sequencer: refactor sequencer_add_exec_commands() to work on a\n    todo_list\n  sequencer: refactor rearrange_squash() to work on a todo_list\n  sequencer: refactor transform_todos() to work on a todo_list\n  sequencer: make sequencer_make_script() write its script to a strbuf\n  sequencer: change complete_action() to use the refactored functions\n  sequencer: refactor skip_unnecessary_picks() to work on a todo_list\n  rebase-interactive: use todo_list_transform() in edit_todo_list()\n  rebase-interactive: append_todo_help() changes\n  rebase-interactive: rewrite edit_todo_list() to handle the initial\n    edit\n  sequencer: use edit_todo_list() in complete_action()\n  sequencer: fix a call to error() in transform_todo_file()\n  rebase--interactive: move transform_todo_file() to\n    rebase--interactive.c\n\n builtin/rebase--interactive.c |  65 +++--\n rebase-interactive.c          | 161 ++++++++++--\n rebase-interactive.h          |   8 +-\n sequencer.c                   | 479 ++++++++++++----------------------\n sequencer.h                   |  66 ++++-\n 5 files changed, 406 insertions(+), 373 deletions(-)\n\n-- \n2.19.1\n\n"},{"id":"359797","messageId":"20181007195418.25752-3-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181007195418.25752-1-alban.gruin@gmail.com","subject":"[PATCH 02/15] sequencer: make the todo_list structure public","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-07T19:54:05Z","receivedAt":"2018-10-07T20:00:13Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This makes the structures todo_list and todo_item, and the functions\ntodo_list_release() and parse_insn_buffer(), accessible outside of\nsequencer.c.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 66 +++++++++--------------------------------------------\n sequencer.h | 48 ++++++++++++++++++++++++++++++++++++++\n 2 files changed, 59 insertions(+), 55 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex ed798b95d1..bb8ca2477f 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1443,31 +1443,6 @@ static int allow_empty(struct replay_opts *opts, struct commit *commit)\n \t\treturn 1;\n }\n \n-/*\n- * Note that ordering matters in this enum. Not only must it match the mapping\n- * below, it is also divided into several sections that matter.  When adding\n- * new commands, make sure you add it in the right section.\n- */\n-enum todo_command {\n-\t/* commands that handle commits */\n-\tTODO_PICK = 0,\n-\tTODO_REVERT,\n-\tTODO_EDIT,\n-\tTODO_REWORD,\n-\tTODO_FIXUP,\n-\tTODO_SQUASH,\n-\t/* commands that do something else than handling a single commit */\n-\tTODO_EXEC,\n-\tTODO_LABEL,\n-\tTODO_RESET,\n-\tTODO_MERGE,\n-\t/* commands that do nothing but are counted for reporting progress */\n-\tTODO_NOOP,\n-\tTODO_DROP,\n-\t/* comments (not counted for reporting progress) */\n-\tTODO_COMMENT\n-};\n-\n static struct {\n \tchar c;\n \tconst char *str;\n@@ -1937,26 +1912,7 @@ enum todo_item_flags {\n \tTODO_EDIT_MERGE_MSG = 1\n };\n \n-struct todo_item {\n-\tenum todo_command command;\n-\tstruct commit *commit;\n-\tunsigned int flags;\n-\tconst char *arg;\n-\tint arg_len;\n-\tsize_t offset_in_buf;\n-};\n-\n-struct todo_list {\n-\tstruct strbuf buf;\n-\tstruct todo_item *items;\n-\tint nr, alloc, current;\n-\tint done_nr, total_nr;\n-\tstruct stat_data stat;\n-};\n-\n-#define TODO_LIST_INIT { STRBUF_INIT }\n-\n-static void todo_list_release(struct todo_list *todo_list)\n+void todo_list_release(struct todo_list *todo_list)\n {\n \tstrbuf_release(&todo_list->buf);\n \tFREE_AND_NULL(todo_list->items);\n@@ -2057,7 +2013,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \treturn !item->commit;\n }\n \n-static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n+int todo_list_parse_insn_buffer(char *buf, struct todo_list *todo_list)\n {\n \tstruct todo_item *item;\n \tchar *p = buf, *next_p;\n@@ -2152,7 +2108,7 @@ static int read_populate_todo(struct todo_list *todo_list,\n \t\treturn error(_(\"could not stat '%s'\"), todo_file);\n \tfill_stat_data(&todo_list->stat, &st);\n \n-\tres = parse_insn_buffer(todo_list->buf.buf, todo_list);\n+\tres = todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list);\n \tif (res) {\n \t\tif (is_rebase_i(opts))\n \t\t\treturn error(_(\"please fix this using \"\n@@ -2183,7 +2139,7 @@ static int read_populate_todo(struct todo_list *todo_list,\n \t\tFILE *f = fopen_or_warn(rebase_path_msgtotal(), \"w\");\n \n \t\tif (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&\n-\t\t\t\t!parse_insn_buffer(done.buf.buf, &done))\n+\t\t\t\t!todo_list_parse_insn_buffer(done.buf.buf, &done))\n \t\t\ttodo_list->done_nr = count_commands(&done);\n \t\telse\n \t\t\ttodo_list->done_nr = 0;\n@@ -4429,7 +4385,7 @@ int sequencer_add_exec_commands(const char *commands)\n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4485,7 +4441,7 @@ int transform_todos(unsigned flags)\n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4571,7 +4527,7 @@ int check_todo_list(void)\n \t\tgoto leave_check;\n \t}\n \tadvise_to_edit_todo = res =\n-\t\tparse_insn_buffer(todo_list.buf.buf, &todo_list);\n+\t\ttodo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n \n \tif (res || check_level == MISSING_COMMIT_CHECK_IGNORE)\n \t\tgoto leave_check;\n@@ -4590,7 +4546,7 @@ int check_todo_list(void)\n \t\tgoto leave_check;\n \t}\n \tstrbuf_release(&todo_file);\n-\tres = !!parse_insn_buffer(todo_list.buf.buf, &todo_list);\n+\tres = !!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n \n \t/* Find commits in git-rebase-todo.backup yet unseen */\n \tfor (i = todo_list.nr - 1; i >= 0; i--) {\n@@ -4672,7 +4628,7 @@ static int skip_unnecessary_picks(struct object_id *output_oid)\n \n \tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n \t\treturn -1;\n-\tif (parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n@@ -4760,7 +4716,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(buf->buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(buf->buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4868,7 +4824,7 @@ int rearrange_squash(void)\n \n \tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n \t\treturn -1;\n-\tif (parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex 660cff5050..c786dee543 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -72,6 +72,54 @@ enum missing_commit_check_level {\n int write_message(const void *buf, size_t len, const char *filename,\n \t\t  int append_eol);\n \n+/*\n+ * Note that ordering matters in this enum. Not only must it match the mapping\n+ * of todo_command_info (in sequencer.c), it is also divided into several\n+ * sections that matter.  When adding new commands, make sure you add it in the\n+ * right section.\n+ */\n+enum todo_command {\n+\t/* commands that handle commits */\n+\tTODO_PICK = 0,\n+\tTODO_REVERT,\n+\tTODO_EDIT,\n+\tTODO_REWORD,\n+\tTODO_FIXUP,\n+\tTODO_SQUASH,\n+\t/* commands that do something else than handling a single commit */\n+\tTODO_EXEC,\n+\tTODO_LABEL,\n+\tTODO_RESET,\n+\tTODO_MERGE,\n+\t/* commands that do nothing but are counted for reporting progress */\n+\tTODO_NOOP,\n+\tTODO_DROP,\n+\t/* comments (not counted for reporting progress) */\n+\tTODO_COMMENT\n+};\n+\n+struct todo_item {\n+\tenum todo_command command;\n+\tstruct commit *commit;\n+\tunsigned int flags;\n+\tconst char *arg;\n+\tint arg_len;\n+\tsize_t offset_in_buf;\n+};\n+\n+struct todo_list {\n+\tstruct strbuf buf;\n+\tstruct todo_item *items;\n+\tint nr, alloc, current;\n+\tint done_nr, total_nr;\n+\tstruct stat_data stat;\n+};\n+\n+#define TODO_LIST_INIT { STRBUF_INIT }\n+\n+int todo_list_parse_insn_buffer(char *buf, struct todo_list *todo_list);\n+void todo_list_release(struct todo_list *todo_list);\n+\n /* Call this to setup defaults before parsing command line options */\n void sequencer_init_config(struct replay_opts *opts);\n int sequencer_pick_revisions(struct replay_opts *opts);\n-- \n2.19.1\n\n"},{"id":"359798","messageId":"20181007195418.25752-2-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181007195418.25752-1-alban.gruin@gmail.com","subject":"[PATCH 01/15] sequencer: clear the number of items of a todo_list before parsing","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-07T19:54:04Z","receivedAt":"2018-10-07T20:00:13Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This clears the number of items of a todo_list before parsing it to\nallow to parse the same list multiple times without issues.\n\nAs its items are not dynamically allocated, or don’t need to allocate\nmemory, no additionnal memory management is required here.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 0a3292d5c4..ed798b95d1 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2063,6 +2063,8 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n \tchar *p = buf, *next_p;\n \tint i, res = 0, fixup_okay = file_exists(rebase_path_done());\n \n+\ttodo_list->current = todo_list->nr = 0;\n+\n \tfor (i = 1; *p; i++, p = next_p) {\n \t\tchar *eol = strchrnul(p, '\\n');\n \n-- \n2.19.1\n\n"},{"id":"359799","messageId":"20181007195418.25752-4-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181007195418.25752-1-alban.gruin@gmail.com","subject":"[PATCH 03/15] sequencer: refactor check_todo_list() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-07T19:54:06Z","receivedAt":"2018-10-07T20:00:15Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors check_todo_list() to work on a todo_list to avoid\nredundant reads and writes to the disk.  The function is renamed\ntodo_list_check().\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, check_todo_list_from_file().  It reads the file\nfrom the disk, parses it, pass the todo_list to todo_list_check(), and\nwrites it back to the disk.\n\nAs get_missing_commit_check_level() and the enum\nmissing_commit_check_level are no longer needed inside of sequencer.c,\nthey are moved to rebase-interactive.c, and made static again.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |   2 +-\n rebase-interactive.c          | 106 ++++++++++++++++++++++++++++++++-\n rebase-interactive.h          |   1 +\n sequencer.c                   | 109 ++++------------------------------\n sequencer.h                   |   9 +--\n 5 files changed, 120 insertions(+), 107 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex a2ab68ed06..ea1f93ccb6 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -255,7 +255,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = transform_todos(flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n-\t\tret = check_todo_list();\n+\t\tret = check_todo_list_from_file();\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n \t\tret = rearrange_squash();\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 0f4119cbae..ef8540245d 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -1,8 +1,32 @@\n #include \"cache.h\"\n #include \"commit.h\"\n-#include \"rebase-interactive.h\"\n #include \"sequencer.h\"\n+#include \"rebase-interactive.h\"\n #include \"strbuf.h\"\n+#include \"commit-slab.h\"\n+#include \"config.h\"\n+\n+enum missing_commit_check_level {\n+\tMISSING_COMMIT_CHECK_IGNORE = 0,\n+\tMISSING_COMMIT_CHECK_WARN,\n+\tMISSING_COMMIT_CHECK_ERROR\n+};\n+\n+static enum missing_commit_check_level get_missing_commit_check_level(void)\n+{\n+\tconst char *value;\n+\n+\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n+\t\t\t!strcasecmp(\"ignore\", value))\n+\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n+\tif (!strcasecmp(\"warn\", value))\n+\t\treturn MISSING_COMMIT_CHECK_WARN;\n+\tif (!strcasecmp(\"error\", value))\n+\t\treturn MISSING_COMMIT_CHECK_ERROR;\n+\twarning(_(\"unrecognized setting %s for option \"\n+\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n+\treturn MISSING_COMMIT_CHECK_IGNORE;\n+}\n \n void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \t\t      struct strbuf *buf)\n@@ -88,3 +112,83 @@ int edit_todo_list(unsigned flags)\n \n \treturn 0;\n }\n+\n+define_commit_slab(commit_seen, unsigned char);\n+/*\n+ * Check if the user dropped some commits by mistake\n+ * Behaviour determined by rebase.missingCommitsCheck.\n+ * Check if there is an unrecognized command or a\n+ * bad SHA-1 in a command.\n+ */\n+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo)\n+{\n+\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n+\tstruct strbuf missing = STRBUF_INIT;\n+\tint advise_to_edit_todo = 0, res = 0, i;\n+\tstruct commit_seen commit_seen;\n+\n+\tinit_commit_seen(&commit_seen);\n+\n+\tres = todo_list_parse_insn_buffer(old_todo->buf.buf, old_todo);\n+\tif (!res)\n+\t\tres = todo_list_parse_insn_buffer(new_todo->buf.buf, new_todo);\n+\tif (res) {\n+\t\tadvise_to_edit_todo = res;\n+\t\tgoto leave_check;\n+\t}\n+\n+\tif (check_level == MISSING_COMMIT_CHECK_IGNORE)\n+\t\tgoto leave_check;\n+\n+\t/* Mark the commits in git-rebase-todo as seen */\n+\tfor (i = 0; i < new_todo->nr; i++) {\n+\t\tstruct commit *commit = new_todo->items[i].commit;\n+\t\tif (commit)\n+\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t}\n+\n+\t/* Find commits in git-rebase-todo.backup yet unseen */\n+\tfor (i = old_todo->nr - 1; i >= 0; i--) {\n+\t\tstruct todo_item *item = old_todo->items + i;\n+\t\tstruct commit *commit = item->commit;\n+\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n+\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n+\t\t\t\t    find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV),\n+\t\t\t\t    item->arg_len, item->arg);\n+\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t\t}\n+\t}\n+\n+\t/* Warn about missing commits */\n+\tif (!missing.len)\n+\t\tgoto leave_check;\n+\n+\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n+\t\tadvise_to_edit_todo = res = 1;\n+\n+\tfprintf(stderr,\n+\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n+\t\t\"Dropped commits (newer to older):\\n\"));\n+\n+\t/* Make the list user-friendly and display */\n+\tfputs(missing.buf, stderr);\n+\tstrbuf_release(&missing);\n+\n+\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n+\t\t\"explicitly remove a commit.\\n\\n\"\n+\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n+\t\t\"the level of warnings.\\n\"\n+\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n+\n+leave_check:\n+\tclear_commit_seen(&commit_seen);\n+\n+\tif (advise_to_edit_todo)\n+\t\tfprintf(stderr,\n+\t\t\t_(\"You can fix this with 'git rebase --edit-todo' \"\n+\t\t\t  \"and then run 'git rebase --continue'.\\n\"\n+\t\t\t  \"Or you can abort the rebase with 'git rebase\"\n+\t\t\t  \" --abort'.\\n\"));\n+\n+\treturn res;\n+}\ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 971da03776..6bc7bc315d 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -4,5 +4,6 @@\n void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \t\t      struct strbuf *buf);\n int edit_todo_list(unsigned flags);\n+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n \n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex bb8ca2477f..8dda61927c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4487,111 +4487,26 @@ int transform_todos(unsigned flags)\n \treturn i;\n }\n \n-enum missing_commit_check_level get_missing_commit_check_level(void)\n+int check_todo_list_from_file(void)\n {\n-\tconst char *value;\n-\n-\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n-\t\t\t!strcasecmp(\"ignore\", value))\n-\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n-\tif (!strcasecmp(\"warn\", value))\n-\t\treturn MISSING_COMMIT_CHECK_WARN;\n-\tif (!strcasecmp(\"error\", value))\n-\t\treturn MISSING_COMMIT_CHECK_ERROR;\n-\twarning(_(\"unrecognized setting %s for option \"\n-\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n-\treturn MISSING_COMMIT_CHECK_IGNORE;\n-}\n-\n-define_commit_slab(commit_seen, unsigned char);\n-/*\n- * Check if the user dropped some commits by mistake\n- * Behaviour determined by rebase.missingCommitsCheck.\n- * Check if there is an unrecognized command or a\n- * bad SHA-1 in a command.\n- */\n-int check_todo_list(void)\n-{\n-\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n-\tstruct strbuf todo_file = STRBUF_INIT;\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf missing = STRBUF_INIT;\n-\tint advise_to_edit_todo = 0, res = 0, i;\n-\tstruct commit_seen commit_seen;\n-\n-\tinit_commit_seen(&commit_seen);\n+\tstruct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;\n+\tint res = 0;\n \n-\tstrbuf_addstr(&todo_file, rebase_path_todo());\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n+\tif (strbuf_read_file_or_whine(&new_todo.buf, rebase_path_todo()) < 0) {\n \t\tres = -1;\n-\t\tgoto leave_check;\n-\t}\n-\tadvise_to_edit_todo = res =\n-\t\ttodo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n-\n-\tif (res || check_level == MISSING_COMMIT_CHECK_IGNORE)\n-\t\tgoto leave_check;\n-\n-\t/* Mark the commits in git-rebase-todo as seen */\n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tstruct commit *commit = todo_list.items[i].commit;\n-\t\tif (commit)\n-\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t\tgoto out;\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\tstrbuf_addstr(&todo_file, \".backup\");\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n+\tif (strbuf_read_file_or_whine(&old_todo.buf, rebase_path_todo_backup()) < 0) {\n \t\tres = -1;\n-\t\tgoto leave_check;\n-\t}\n-\tstrbuf_release(&todo_file);\n-\tres = !!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n-\n-\t/* Find commits in git-rebase-todo.backup yet unseen */\n-\tfor (i = todo_list.nr - 1; i >= 0; i--) {\n-\t\tstruct todo_item *item = todo_list.items + i;\n-\t\tstruct commit *commit = item->commit;\n-\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n-\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n-\t\t\t\t    short_commit_name(commit),\n-\t\t\t\t    item->arg_len, item->arg);\n-\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n-\t\t}\n+\t\tgoto out;\n \t}\n \n-\t/* Warn about missing commits */\n-\tif (!missing.len)\n-\t\tgoto leave_check;\n-\n-\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n-\t\tadvise_to_edit_todo = res = 1;\n+\tres = todo_list_check(&old_todo, &new_todo);\n \n-\tfprintf(stderr,\n-\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n-\t\t\"Dropped commits (newer to older):\\n\"));\n-\n-\t/* Make the list user-friendly and display */\n-\tfputs(missing.buf, stderr);\n-\tstrbuf_release(&missing);\n-\n-\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n-\t\t\"explicitly remove a commit.\\n\\n\"\n-\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n-\t\t\"the level of warnings.\\n\"\n-\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n-\n-leave_check:\n-\tclear_commit_seen(&commit_seen);\n-\tstrbuf_release(&todo_file);\n-\ttodo_list_release(&todo_list);\n-\n-\tif (advise_to_edit_todo)\n-\t\tfprintf(stderr,\n-\t\t\t_(\"You can fix this with 'git rebase --edit-todo' \"\n-\t\t\t  \"and then run 'git rebase --continue'.\\n\"\n-\t\t\t  \"Or you can abort the rebase with 'git rebase\"\n-\t\t\t  \" --abort'.\\n\"));\n+out:\n+\ttodo_list_release(&old_todo);\n+\ttodo_list_release(&new_todo);\n \n \treturn res;\n }\n@@ -4769,7 +4684,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \n \ttodo_list_release(&todo_list);\n \n-\tif (check_todo_list()) {\n+\tif (check_todo_list_from_file()) {\n \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n \t\treturn -1;\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex c786dee543..fb8b85bf9e 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -63,12 +63,6 @@ struct replay_opts {\n };\n #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }\n \n-enum missing_commit_check_level {\n-\tMISSING_COMMIT_CHECK_IGNORE = 0,\n-\tMISSING_COMMIT_CHECK_WARN,\n-\tMISSING_COMMIT_CHECK_ERROR\n-};\n-\n int write_message(const void *buf, size_t len, const char *filename,\n \t\t  int append_eol);\n \n@@ -142,8 +136,7 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,\n \n int sequencer_add_exec_commands(const char *command);\n int transform_todos(unsigned flags);\n-enum missing_commit_check_level get_missing_commit_check_level(void);\n-int check_todo_list(void);\n+int check_todo_list_from_file(void);\n int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, const char *cmd,\n-- \n2.19.1\n\n"},{"id":"359800","messageId":"20181007195418.25752-5-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181007195418.25752-1-alban.gruin@gmail.com","subject":"[PATCH 04/15] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-07T19:54:07Z","receivedAt":"2018-10-07T20:00:16Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors sequencer_add_exec_commands() to work on a todo_list to\navoid redundant reads and writes to the disk.\n\nsequencer_add_exec_commands() still reads the todo list from the disk,\nas it is needed by rebase -p.  todo_list_add_exec_commands() works on a\ntodo_list structure, and reparses it at the end.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 56 +++++++++++++++++++++++++++++++----------------------\n 1 file changed, 33 insertions(+), 23 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 8dda61927c..6d998f21a4 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4370,34 +4370,21 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,\n \treturn 0;\n }\n \n-/*\n- * Add commands after pick and (series of) squash/fixup commands\n- * in the todo list.\n- */\n-int sequencer_add_exec_commands(const char *commands)\n+static void todo_list_add_exec_commands(struct todo_list *todo_list,\n+\t\t\t\t\tconst char *commands)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf *buf = &todo_list.buf;\n+\tstruct strbuf *buf = &todo_list->buf;\n \tsize_t offset = 0, commands_len = strlen(commands);\n \tint i, insert;\n \n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n \t/*\n \t * Insert <commands> after every pick. Here, fixup/squash chains\n \t * are considered part of the pick, so we insert the commands *after*\n \t * those chains if there are any.\n \t */\n \tinsert = -1;\n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tenum todo_command command = todo_list.items[i].command;\n+\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\tenum todo_command command = todo_list->items[i].command;\n \n \t\tif (insert >= 0) {\n \t\t\t/* skip fixup/squash chains */\n@@ -4408,7 +4395,7 @@ int sequencer_add_exec_commands(const char *commands)\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tstrbuf_insert(buf,\n-\t\t\t\t      todo_list.items[insert].offset_in_buf +\n+\t\t\t\t      todo_list->items[insert].offset_in_buf +\n \t\t\t\t      offset, commands, commands_len);\n \t\t\toffset += commands_len;\n \t\t\tinsert = -1;\n@@ -4419,15 +4406,38 @@ int sequencer_add_exec_commands(const char *commands)\n \t}\n \n \t/* insert or append final <commands> */\n-\tif (insert >= 0 && insert < todo_list.nr)\n-\t\tstrbuf_insert(buf, todo_list.items[insert].offset_in_buf +\n+\tif (insert >= 0 && insert < todo_list->nr)\n+\t\tstrbuf_insert(buf, todo_list->items[insert].offset_in_buf +\n \t\t\t      offset, commands, commands_len);\n \telse if (insert >= 0 || !offset)\n \t\tstrbuf_add(buf, commands, commands_len);\n \n-\ti = write_message(buf->buf, buf->len, todo_file, 0);\n+\tif (todo_list_parse_insn_buffer(buf->buf, todo_list))\n+\t\tBUG(\"unusable todo list\");}\n+\n+/*\n+ * Add commands after pick and (series of) squash/fixup commands\n+ * in the todo list.\n+ */\n+int sequencer_add_exec_commands(const char *commands)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_add_exec_commands(&todo_list, commands);\n+\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n \ttodo_list_release(&todo_list);\n-\treturn i;\n+\n+\treturn res;\n }\n \n int transform_todos(unsigned flags)\n-- \n2.19.1\n\n"},{"id":"359802","messageId":"20181007195418.25752-9-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181007195418.25752-1-alban.gruin@gmail.com","subject":"[PATCH 08/15] sequencer: change complete_action() to use the refactored functions","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-07T19:54:11Z","receivedAt":"2018-10-07T20:00:22Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"complete_action() used functions that read the todo-list file, made some\nchanges to it, and wrote it back to the disk.\n\nThe previous commits were dedicated to separate the part that deals with\nthe file from the actual logic of these functions.  Now that this is\ndone, we can call directly the \"logic\" functions to avoid useless file\naccess.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 13 +-----\n sequencer.c                   | 76 +++++++++++++++++------------------\n sequencer.h                   |  2 +-\n 3 files changed, 38 insertions(+), 53 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex eef1ff2e83..0700339f90 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -71,7 +71,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tconst char *head_hash = NULL;\n \tchar *revisions = NULL, *shortrevisions = NULL;\n \tstruct argv_array make_script_args = ARGV_ARRAY_INIT;\n-\tFILE *todo_list_file;\n \tstruct todo_list todo_list = TODO_LIST_INIT;\n \n \tif (prepare_branch_to_be_rebased(opts, switch_to))\n@@ -94,14 +93,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (!upstream && squash_onto)\n \t\twrite_file(path_squash_onto(), \"%s\\n\", squash_onto);\n \n-\ttodo_list_file = fopen(rebase_path_todo(), \"w\");\n-\tif (!todo_list_file) {\n-\t\tfree(revisions);\n-\t\tfree(shortrevisions);\n-\n-\t\treturn error_errno(_(\"could not open %s\"), rebase_path_todo());\n-\t}\n-\n \targv_array_pushl(&make_script_args, \"\", revisions, NULL);\n \tif (restrict_revision)\n \t\targv_array_push(&make_script_args, restrict_revision);\n@@ -109,15 +100,13 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tret = sequencer_make_script(&todo_list.buf,\n \t\t\t\t    make_script_args.argc, make_script_args.argv,\n \t\t\t\t    flags);\n-\tfputs(todo_list.buf.buf, todo_list_file);\n-\tfclose(todo_list_file);\n \n \tif (ret)\n \t\terror(_(\"could not generate todo list\"));\n \telse {\n \t\tdiscard_cache();\n \t\tret = complete_action(opts, flags, shortrevisions, onto_name, onto,\n-\t\t\t\t      head_hash, cmd, autosquash);\n+\t\t\t\t      head_hash, cmd, autosquash, &todo_list);\n \t}\n \n \tfree(revisions);\ndiff --git a/sequencer.c b/sequencer.c\nindex dfb8d1c974..b37935e5ab 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4624,93 +4624,89 @@ static int skip_unnecessary_picks(struct object_id *output_oid)\n \treturn 0;\n }\n \n+static int todo_list_rearrange_squash(struct todo_list *todo_list);\n+\n int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, const char *cmd,\n-\t\t    unsigned autosquash)\n+\t\t    unsigned autosquash, struct todo_list *todo_list)\n {\n \tconst char *shortonto, *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf *buf = &(todo_list.buf);\n+\tstruct todo_list new_todo = TODO_LIST_INIT;\n+\tstruct strbuf *buf = &todo_list->buf;\n \tstruct object_id oid;\n-\tstruct stat st;\n+\tint command_count;\n \n \tget_oid(onto, &oid);\n \tshortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);\n \n-\tif (!lstat(todo_file, &st) && st.st_size == 0 &&\n-\t    write_message(\"noop\\n\", 5, todo_file, 0))\n-\t\treturn -1;\n+\tif (buf->len == 0)\n+\t\tstrbuf_add(buf, \"noop\\n\", 5);\n+\n+\tif (todo_list_parse_insn_buffer(buf->buf, todo_list))\n+\t\tBUG(\"unusable todo list\");\n \n-\tif (autosquash && rearrange_squash_in_todo_file())\n+\tif (autosquash && todo_list_rearrange_squash(todo_list))\n \t\treturn -1;\n \n \tif (cmd && *cmd)\n-\t\tsequencer_add_exec_commands(cmd);\n+\t\ttodo_list_add_exec_commands(todo_list, cmd);\n \n-\tif (strbuf_read_file(buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(buf->buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tif (count_commands(&todo_list) == 0) {\n+\tcommand_count = count_commands(todo_list);\n+\tif (command_count == 0) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n \n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n+\ttodo_list_transform(todo_list, flags | TODO_LIST_SHORTEN_IDS);\n+\n \tstrbuf_addch(buf, '\\n');\n \tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n \t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n-\t\t\t\t      count_commands(&todo_list)),\n-\t\t\t      shortrevisions, shortonto, count_commands(&todo_list));\n+\t\t\t\t      command_count),\n+\t\t\t      shortrevisions, shortonto, command_count);\n \tappend_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);\n \n-\tif (write_message(buf->buf, buf->len, todo_file, 0)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tif (write_message(buf->buf, buf->len, todo_file, 0))\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n \n \tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n \t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n \t\t\t     rebase_path_todo_backup());\n \n-\tif (transform_todo_file(flags | TODO_LIST_SHORTEN_IDS))\n-\t\treturn error(_(\"could not transform the todo list\"));\n-\n-\tstrbuf_reset(buf);\n-\n-\tif (launch_sequence_editor(todo_file, buf, NULL)) {\n+\tif (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n \n \t\treturn -1;\n \t}\n \n-\tstrbuf_stripspace(buf, 1);\n-\tif (buf->len == 0) {\n+\tstrbuf_stripspace(&new_todo.buf, 1);\n+\tif (new_todo.buf.len == 0) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n+\t\ttodo_list_release(&new_todo);\n \n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\n-\tif (check_todo_list_from_file()) {\n+\tif (todo_list_check(todo_list, &new_todo)) {\n \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n+\t\ttodo_list_release(&new_todo);\n+\n \t\treturn -1;\n \t}\n \n-\tif (transform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS)))\n-\t\treturn error(_(\"could not transform the todo list\"));\n+\ttodo_list_transform(&new_todo, flags & ~(TODO_LIST_SHORTEN_IDS));\n+\n+\tif (rewrite_file(todo_file, new_todo.buf.buf, new_todo.buf.len) < 0) {\n+\t\ttodo_list_release(&new_todo);\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_release(&new_todo);\n \n \tif (opts->allow_ff && skip_unnecessary_picks(&oid))\n \t\treturn error(_(\"could not skip unnecessary pick commands\"));\ndiff --git a/sequencer.h b/sequencer.h\nindex 21d9ba09ab..5bd3b79282 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -141,7 +141,7 @@ int check_todo_list_from_file(void);\n int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, const char *cmd,\n-\t\t    unsigned autosquash);\n+\t\t    unsigned autosquash, struct todo_list *todo_list);\n int rearrange_squash_in_todo_file(void);\n \n extern const char sign_off_header[];\n-- \n2.19.1\n\n"},{"id":"359801","messageId":"20181007195418.25752-11-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181007195418.25752-1-alban.gruin@gmail.com","subject":"[PATCH 10/15] rebase-interactive: use todo_list_transform() in edit_todo_list()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-07T19:54:13Z","receivedAt":"2018-10-07T20:00:23Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Just like complete_action(), edit_todo_list() used a\nfunction (transform_todo_file()) that read the todo-list from the disk\nand wrote it back, resulting in useless disk accesses.\n\nThis changes edit_todo_list() to call directly todo_list_transform()\ninstead.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n rebase-interactive.c | 40 +++++++++++++++++++---------------------\n 1 file changed, 19 insertions(+), 21 deletions(-)\n\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 7c7f720a3d..f42d48e192 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -78,39 +78,37 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \n int edit_todo_list(unsigned flags)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n \tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n \n-\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n-\tstrbuf_stripspace(&buf, 1);\n-\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n-\t\tstrbuf_release(&buf);\n-\t\treturn -1;\n-\t}\n-\n-\tstrbuf_release(&buf);\n+\tstrbuf_stripspace(&todo_list.buf, 1);\n+\tif (!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list))\n+\t\ttodo_list_transform(&todo_list, flags | TODO_LIST_SHORTEN_IDS);\n \n-\ttransform_todo_file(flags | TODO_LIST_SHORTEN_IDS);\n-\n-\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\tappend_todo_help(1, 0, &todo_list.buf);\n \n-\tappend_todo_help(1, 0, &buf);\n-\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n-\t\tstrbuf_release(&buf);\n+\tif (write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0)) {\n+\t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n \n-\tstrbuf_release(&buf);\n-\n-\tif (launch_sequence_editor(todo_file, NULL, NULL))\n+\tstrbuf_reset(&todo_list.buf);\n+\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n+\t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n+\t}\n \n-\ttransform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS));\n+\tif (!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_transform(&todo_list, flags & ~(TODO_LIST_SHORTEN_IDS));\n+\t\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n+\t}\n \n-\treturn 0;\n+\ttodo_list_release(&todo_list);\n+\treturn res;\n }\n \n define_commit_slab(commit_seen, unsigned char);\n-- \n2.19.1\n\n"},{"id":"359803","messageId":"20181007195418.25752-12-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181007195418.25752-1-alban.gruin@gmail.com","subject":"[PATCH 11/15] rebase-interactive: append_todo_help() changes","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-07T19:54:14Z","receivedAt":"2018-10-07T20:00:24Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This moves the writing of the comment \"Rebase $shortrevisions onto\n$shortonto ($command_count commands)\" from complete_action() to\nappend_todo_help().\n\nshortrevisions, shortonto, and command_count are passed as parameters to\nappend_todo_help().\n\nDuring the initial edit of the todo list, shortrevisions and shortonto\nare not NULL.  Therefore, if shortrevisions or shortonto is NULL, then\nedit_todo would be true, otherwise it would be false.  Thus, edit_todo\nis removed from the parameters of append_todo_help().\n\nedit_todo_list() and complete_action() are modified to fit these\nchanges.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n rebase-interactive.c | 14 ++++++++++++--\n rebase-interactive.h |  3 ++-\n sequencer.c          |  8 ++------\n 3 files changed, 16 insertions(+), 9 deletions(-)\n\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex f42d48e192..7168d56d17 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -28,7 +28,8 @@ static enum missing_commit_check_level get_missing_commit_check_level(void)\n \treturn MISSING_COMMIT_CHECK_IGNORE;\n }\n \n-void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n+void append_todo_help(unsigned keep_empty, int command_count,\n+\t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf)\n {\n \tconst char *msg = _(\"\\nCommands:\\n\"\n@@ -47,6 +48,15 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \".       specified). Use -c <commit> to reword the commit message.\\n\"\n \"\\n\"\n \"These lines can be re-ordered; they are executed from top to bottom.\\n\");\n+\tunsigned edit_todo = !(shortrevisions && shortonto);\n+\n+\tif (!edit_todo) {\n+\t\tstrbuf_addch(buf, '\\n');\n+\t\tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n+\t\t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n+\t\t\t\t\t      command_count),\n+\t\t\t\t      shortrevisions, shortonto, command_count);\n+\t}\n \n \tstrbuf_add_commented_lines(buf, msg, strlen(msg));\n \n@@ -89,7 +99,7 @@ int edit_todo_list(unsigned flags)\n \tif (!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list))\n \t\ttodo_list_transform(&todo_list, flags | TODO_LIST_SHORTEN_IDS);\n \n-\tappend_todo_help(1, 0, &todo_list.buf);\n+\tappend_todo_help(flags, 0, NULL, NULL, &todo_list.buf);\n \n \tif (write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0)) {\n \t\ttodo_list_release(&todo_list);\ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 6bc7bc315d..61858f3a02 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -1,7 +1,8 @@\n #ifndef REBASE_INTERACTIVE_H\n #define REBASE_INTERACTIVE_H\n \n-void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n+void append_todo_help(unsigned keep_empty, int command_count,\n+\t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf);\n int edit_todo_list(unsigned flags);\n int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\ndiff --git a/sequencer.c b/sequencer.c\nindex a432b64048..94d3058359 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4636,12 +4636,8 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \n \ttodo_list_transform(todo_list, flags | TODO_LIST_SHORTEN_IDS);\n \n-\tstrbuf_addch(buf, '\\n');\n-\tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n-\t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n-\t\t\t\t      command_count),\n-\t\t\t      shortrevisions, shortonto, command_count);\n-\tappend_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);\n+\tappend_todo_help(flags & TODO_LIST_KEEP_EMPTY, command_count,\n+\t\t\t shortrevisions, shortonto, buf);\n \n \tif (write_message(buf->buf, buf->len, todo_file, 0))\n \t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n-- \n2.19.1\n\n"},{"id":"359804","messageId":"20181007195418.25752-13-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181007195418.25752-1-alban.gruin@gmail.com","subject":"[PATCH 12/15] rebase-interactive: rewrite edit_todo_list() to handle the initial edit","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-07T19:54:15Z","receivedAt":"2018-10-07T20:00:25Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"edit_todo_list() is changed to work on a todo_list, and to handle the\ninitial edition of the todo list (ie. making a backup of the todo\nlist).\n\nIt does not check for dropped commits yet, as todo_list_check() does not\nwork if the old todo list has invalid commands.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 22 +++++++++++++++-\n rebase-interactive.c          | 49 ++++++++++++++++++-----------------\n rebase-interactive.h          |  4 ++-\n sequencer.c                   |  3 +--\n sequencer.h                   |  1 +\n 5 files changed, 51 insertions(+), 28 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 0700339f90..264e940b47 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -13,6 +13,26 @@ static GIT_PATH_FUNC(path_state_dir, \"rebase-merge/\")\n static GIT_PATH_FUNC(path_squash_onto, \"rebase-merge/squash-onto\")\n static GIT_PATH_FUNC(path_interactive, \"rebase-merge/interactive\")\n \n+static int edit_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT,\n+\t\tnew_todo = TODO_LIST_INIT;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tstrbuf_stripspace(&todo_list.buf, 1);\n+\tif (!edit_todo_list(&todo_list, &new_todo, flags, 0, NULL, NULL) &&\n+\t    write_message(new_todo.buf.buf, new_todo.buf.len, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n+\n+\ttodo_list_release(&todo_list);\n+\ttodo_list_release(&new_todo);\n+\n+\treturn 0;\n+}\n+\n static int get_revision_ranges(const char *upstream, const char *onto,\n \t\t\t       const char **head_hash,\n \t\t\t       char **revisions, char **shortrevisions)\n@@ -231,7 +251,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tbreak;\n \t}\n \tcase EDIT_TODO:\n-\t\tret = edit_todo_list(flags);\n+\t\tret = edit_todo_file(flags);\n \t\tbreak;\n \tcase SHOW_CURRENT_PATCH: {\n \t\tstruct child_process cmd = CHILD_PROCESS_INIT;\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 7168d56d17..6ee60ac03f 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -86,39 +86,40 @@ void append_todo_help(unsigned keep_empty, int command_count,\n \t}\n }\n \n-int edit_todo_list(unsigned flags)\n+int edit_todo_list(struct todo_list *todo_list, struct todo_list *new_todo,\n+\t\t   unsigned flags, int command_count,\n+\t\t   const char *shortrevisions, const char *shortonto)\n {\n \tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res = 0;\n+\tunsigned initial = shortrevisions && shortonto;\n \n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\tif (initial || !todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list))\n+\t\ttodo_list_transform(todo_list, flags | TODO_LIST_SHORTEN_IDS);\n \n-\tstrbuf_stripspace(&todo_list.buf, 1);\n-\tif (!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list))\n-\t\ttodo_list_transform(&todo_list, flags | TODO_LIST_SHORTEN_IDS);\n+\tif (initial)\n+\t\tappend_todo_help(flags & TODO_LIST_KEEP_EMPTY, command_count,\n+\t\t\t\t shortrevisions, shortonto, &todo_list->buf);\n+\telse\n+\t\tappend_todo_help(flags, 0, NULL, NULL, &todo_list->buf);\n \n-\tappend_todo_help(flags, 0, NULL, NULL, &todo_list.buf);\n+\tif (write_message(todo_list->buf.buf, todo_list->buf.len, todo_file, 0))\n+\t\treturn error_errno(_(\"could not write '%s''\"), todo_file);\n \n-\tif (write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tif (initial && copy_file(rebase_path_todo_backup(), todo_file, 0666))\n+\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n+\t\t\t     rebase_path_todo_backup());\n \n-\tstrbuf_reset(&todo_list.buf);\n-\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tif (launch_sequence_editor(todo_file, &new_todo->buf, NULL))\n+\t\treturn -2;\n \n-\tif (!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_transform(&todo_list, flags & ~(TODO_LIST_SHORTEN_IDS));\n-\t\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n-\t}\n+\tstrbuf_stripspace(&new_todo->buf, 1);\n+\tif (initial && new_todo->buf.len == 0)\n+\t\treturn -3;\n \n-\ttodo_list_release(&todo_list);\n-\treturn res;\n+\tif (!initial && !todo_list_parse_insn_buffer(new_todo->buf.buf, new_todo))\n+\t\ttodo_list_transform(new_todo, flags & ~(TODO_LIST_SHORTEN_IDS));\n+\n+\treturn 0;\n }\n \n define_commit_slab(commit_seen, unsigned char);\ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 61858f3a02..83cde455e6 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -4,7 +4,9 @@\n void append_todo_help(unsigned keep_empty, int command_count,\n \t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf);\n-int edit_todo_list(unsigned flags);\n+int edit_todo_list(struct todo_list *todo_list, struct todo_list *new_todo,\n+\t\t   unsigned flags, int command_count,\n+\t\t   const char *shortrevisions, const char *shortonto);\n int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n \n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex 94d3058359..bfcbe8239b 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -55,8 +55,7 @@ static GIT_PATH_FUNC(rebase_path, \"rebase-merge\")\n  * file and written to the tail of 'done'.\n  */\n GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n-static GIT_PATH_FUNC(rebase_path_todo_backup,\n-\t\t     \"rebase-merge/git-rebase-todo.backup\")\n+GIT_PATH_FUNC(rebase_path_todo_backup, \"rebase-merge/git-rebase-todo.backup\")\n \n /*\n  * The rebase command lines that have already been processed. A line\ndiff --git a/sequencer.h b/sequencer.h\nindex 5bd3b79282..fa84918c55 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -9,6 +9,7 @@ struct commit;\n const char *git_path_commit_editmsg(void);\n const char *git_path_seq_dir(void);\n const char *rebase_path_todo(void);\n+const char *rebase_path_todo_backup(void);\n \n #define APPEND_SIGNOFF_DEDUP (1u << 0)\n \n-- \n2.19.1\n\n"},{"id":"359805","messageId":"20181007195418.25752-14-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181007195418.25752-1-alban.gruin@gmail.com","subject":"[PATCH 13/15] sequencer: use edit_todo_list() in complete_action()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-07T19:54:16Z","receivedAt":"2018-10-07T20:00:26Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This changes complete_action() to use edit_todo_list(), now that it can\nhandle the initial edit of the todo list.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 27 +++++++--------------------\n 1 file changed, 7 insertions(+), 20 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex bfcbe8239b..93b9b40f66 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4608,7 +4608,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \tstruct todo_list new_todo = TODO_LIST_INIT;\n \tstruct strbuf *buf = &todo_list->buf;\n \tstruct object_id oid;\n-\tint command_count;\n+\tint command_count, res;\n \n \tget_oid(onto, &oid);\n \tshortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);\n@@ -4633,27 +4633,16 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\ttodo_list_transform(todo_list, flags | TODO_LIST_SHORTEN_IDS);\n-\n-\tappend_todo_help(flags & TODO_LIST_KEEP_EMPTY, command_count,\n-\t\t\t shortrevisions, shortonto, buf);\n-\n-\tif (write_message(buf->buf, buf->len, todo_file, 0))\n-\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n-\n-\tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n-\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n-\t\t\t     rebase_path_todo_backup());\n-\n-\tif (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {\n+\tres = edit_todo_list(todo_list, &new_todo, flags,\n+\t\t\t     command_count, shortrevisions, shortonto);\n+\tif (res == -1)\n+\t\treturn -1;\n+\telse if (res == -2) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n \n \t\treturn -1;\n-\t}\n-\n-\tstrbuf_stripspace(&new_todo.buf, 1);\n-\tif (new_todo.buf.len == 0) {\n+\t} else if (res == -3) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n \t\ttodo_list_release(&new_todo);\n@@ -4668,8 +4657,6 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t\treturn -1;\n \t}\n \n-\ttodo_list_transform(&new_todo, flags & ~(TODO_LIST_SHORTEN_IDS));\n-\n \tif (opts->allow_ff && skip_unnecessary_picks(&new_todo, &oid)) {\n \t\ttodo_list_release(&new_todo);\n \t\treturn error(_(\"could not skip unnecessary pick commands\"));\n-- \n2.19.1\n\n"},{"id":"359806","messageId":"20181007195418.25752-15-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181007195418.25752-1-alban.gruin@gmail.com","subject":"[PATCH 14/15] sequencer: fix a call to error() in transform_todo_file()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-07T19:54:17Z","receivedAt":"2018-10-07T20:00:28Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This replaces a call to error() by a call to error_errno() after writing\nthe content of the todo list to the disk in transform_todo_file().\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 93b9b40f66..65bf251ba5 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4421,7 +4421,7 @@ int sequencer_add_exec_commands(const char *commands)\n \tint res;\n \n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n \tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n-- \n2.19.1\n\n"},{"id":"359807","messageId":"20181007195418.25752-16-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181007195418.25752-1-alban.gruin@gmail.com","subject":"[PATCH 15/15] rebase--interactive: move transform_todo_file() to rebase--interactive.c","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-07T19:54:18Z","receivedAt":"2018-10-07T20:00:29Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"As transform_todo_file() is only needed inside of rebase--interactive.c,\nit is moved there from sequencer.c.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 21 +++++++++++++++++++++\n sequencer.c                   | 21 ---------------------\n sequencer.h                   |  1 -\n 3 files changed, 21 insertions(+), 22 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 264e940b47..50b5c25402 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -33,6 +33,27 @@ static int edit_todo_file(unsigned flags)\n \treturn 0;\n }\n \n+static int transform_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_transform(&todo_list, flags);\n+\n+\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n+\ttodo_list_release(&todo_list);\n+\treturn res;\n+}\n+\n static int get_revision_ranges(const char *upstream, const char *onto,\n \t\t\t       const char **head_hash,\n \t\t\t       char **revisions, char **shortrevisions)\ndiff --git a/sequencer.c b/sequencer.c\nindex 65bf251ba5..e837e52b64 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4485,27 +4485,6 @@ void todo_list_transform(struct todo_list *todo_list, unsigned flags)\n \t\tBUG(\"unusable todo list\");\n }\n \n-int transform_todo_file(unsigned flags)\n-{\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res;\n-\n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\ttodo_list_transform(&todo_list, flags);\n-\n-\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n-\ttodo_list_release(&todo_list);\n-\treturn res;\n-}\n-\n int check_todo_list_from_file(void)\n {\n \tstruct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;\ndiff --git a/sequencer.h b/sequencer.h\nindex fa84918c55..a4b0113206 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -137,7 +137,6 @@ int sequencer_make_script(struct strbuf *out, int argc, const char **argv,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(const char *command);\n-int transform_todo_file(unsigned flags);\n int check_todo_list_from_file(void);\n int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-- \n2.19.1\n\n"},{"id":"359808","messageId":"20181007195418.25752-8-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181007195418.25752-1-alban.gruin@gmail.com","subject":"[PATCH 07/15] sequencer: make sequencer_make_script() write its script to a strbuf","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-07T19:54:10Z","receivedAt":"2018-10-07T20:00:30Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This makes sequencer_make_script() write its script to a strbuf (ie. the\nbuffer of a todo_list) instead of a FILE.  This reduce the amount of\nread/write made by rebase interactive.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 13 +++++++-----\n sequencer.c                   | 38 ++++++++++++++++-------------------\n sequencer.h                   |  2 +-\n 3 files changed, 26 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex f827e53f05..eef1ff2e83 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -71,7 +71,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tconst char *head_hash = NULL;\n \tchar *revisions = NULL, *shortrevisions = NULL;\n \tstruct argv_array make_script_args = ARGV_ARRAY_INIT;\n-\tFILE *todo_list;\n+\tFILE *todo_list_file;\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n \n \tif (prepare_branch_to_be_rebased(opts, switch_to))\n \t\treturn -1;\n@@ -93,8 +94,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (!upstream && squash_onto)\n \t\twrite_file(path_squash_onto(), \"%s\\n\", squash_onto);\n \n-\ttodo_list = fopen(rebase_path_todo(), \"w\");\n-\tif (!todo_list) {\n+\ttodo_list_file = fopen(rebase_path_todo(), \"w\");\n+\tif (!todo_list_file) {\n \t\tfree(revisions);\n \t\tfree(shortrevisions);\n \n@@ -105,10 +106,11 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (restrict_revision)\n \t\targv_array_push(&make_script_args, restrict_revision);\n \n-\tret = sequencer_make_script(todo_list,\n+\tret = sequencer_make_script(&todo_list.buf,\n \t\t\t\t    make_script_args.argc, make_script_args.argv,\n \t\t\t\t    flags);\n-\tfclose(todo_list);\n+\tfputs(todo_list.buf.buf, todo_list_file);\n+\tfclose(todo_list_file);\n \n \tif (ret)\n \t\terror(_(\"could not generate todo list\"));\n@@ -120,6 +122,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \n \tfree(revisions);\n \tfree(shortrevisions);\n+\ttodo_list_release(&todo_list);\n \targv_array_clear(&make_script_args);\n \n \treturn ret;\ndiff --git a/sequencer.c b/sequencer.c\nindex 30a7fe3958..dfb8d1c974 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4083,7 +4083,7 @@ static const char *label_oid(struct object_id *oid, const char *label,\n }\n \n static int make_script_with_merges(struct pretty_print_context *pp,\n-\t\t\t\t   struct rev_info *revs, FILE *out,\n+\t\t\t\t   struct rev_info *revs, struct strbuf *out,\n \t\t\t\t   unsigned flags)\n {\n \tint keep_empty = flags & TODO_LIST_KEEP_EMPTY;\n@@ -4230,7 +4230,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t * gathering commits not yet shown, reversing the list on the fly,\n \t * then outputting that list (labeling revisions as needed).\n \t */\n-\tfprintf(out, \"%s onto\\n\", cmd_label);\n+\tstrbuf_addf(out, \"%s onto\\n\", cmd_label);\n \tfor (iter = tips; iter; iter = iter->next) {\n \t\tstruct commit_list *list = NULL, *iter2;\n \n@@ -4240,9 +4240,9 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\tentry = oidmap_get(&state.commit2label, &commit->object.oid);\n \n \t\tif (entry)\n-\t\t\tfprintf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n+\t\t\tstrbuf_addf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n \t\telse\n-\t\t\tfprintf(out, \"\\n\");\n+\t\t\tstrbuf_addf(out, \"\\n\");\n \n \t\twhile (oidset_contains(&interesting, &commit->object.oid) &&\n \t\t       !oidset_contains(&shown, &commit->object.oid)) {\n@@ -4255,8 +4255,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t}\n \n \t\tif (!commit)\n-\t\t\tfprintf(out, \"%s %s\\n\", cmd_reset,\n-\t\t\t\trebase_cousins ? \"onto\" : \"[new root]\");\n+\t\t\tstrbuf_addf(out, \"%s %s\\n\", cmd_reset,\n+\t\t\t\t    rebase_cousins ? \"onto\" : \"[new root]\");\n \t\telse {\n \t\t\tconst char *to = NULL;\n \n@@ -4269,12 +4269,12 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t\t\t\t       &state);\n \n \t\t\tif (!to || !strcmp(to, \"onto\"))\n-\t\t\t\tfprintf(out, \"%s onto\\n\", cmd_reset);\n+\t\t\t\tstrbuf_addf(out, \"%s onto\\n\", cmd_reset);\n \t\t\telse {\n \t\t\t\tstrbuf_reset(&oneline);\n \t\t\t\tpretty_print_commit(pp, commit, &oneline);\n-\t\t\t\tfprintf(out, \"%s %s # %s\\n\",\n-\t\t\t\t\tcmd_reset, to, oneline.buf);\n+\t\t\t\tstrbuf_addf(out, \"%s %s # %s\\n\",\n+\t\t\t\t\t    cmd_reset, to, oneline.buf);\n \t\t\t}\n \t\t}\n \n@@ -4283,11 +4283,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t\tentry = oidmap_get(&commit2todo, oid);\n \t\t\t/* only show if not already upstream */\n \t\t\tif (entry)\n-\t\t\t\tfprintf(out, \"%s\\n\", entry->string);\n+\t\t\t\tstrbuf_addf(out, \"%s\\n\", entry->string);\n \t\t\tentry = oidmap_get(&state.commit2label, oid);\n \t\t\tif (entry)\n-\t\t\t\tfprintf(out, \"%s %s\\n\",\n-\t\t\t\t\tcmd_label, entry->string);\n+\t\t\t\tstrbuf_addf(out, \"%s %s\\n\",\n+\t\t\t\t\t    cmd_label, entry->string);\n \t\t\toidset_insert(&shown, oid);\n \t\t}\n \n@@ -4309,12 +4309,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \treturn 0;\n }\n \n-int sequencer_make_script(FILE *out, int argc, const char **argv,\n+int sequencer_make_script(struct strbuf *out, int argc, const char **argv,\n \t\t\t  unsigned flags)\n {\n \tchar *format = NULL;\n \tstruct pretty_print_context pp = {0};\n-\tstruct strbuf buf = STRBUF_INIT;\n \tstruct rev_info revs;\n \tstruct commit *commit;\n \tint keep_empty = flags & TODO_LIST_KEEP_EMPTY;\n@@ -4357,16 +4356,13 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,\n \n \t\tif (!is_empty && (commit->object.flags & PATCHSAME))\n \t\t\tcontinue;\n-\t\tstrbuf_reset(&buf);\n \t\tif (!keep_empty && is_empty)\n-\t\t\tstrbuf_addf(&buf, \"%c \", comment_line_char);\n-\t\tstrbuf_addf(&buf, \"%s %s \", insn,\n+\t\t\tstrbuf_addf(out, \"%c \", comment_line_char);\n+\t\tstrbuf_addf(out, \"%s %s \", insn,\n \t\t\t    oid_to_hex(&commit->object.oid));\n-\t\tpretty_print_commit(&pp, commit, &buf);\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t\tfputs(buf.buf, out);\n+\t\tpretty_print_commit(&pp, commit, out);\n+\t\tstrbuf_addch(out, '\\n');\n \t}\n-\tstrbuf_release(&buf);\n \treturn 0;\n }\n \ndiff --git a/sequencer.h b/sequencer.h\nindex e1faca7884..21d9ba09ab 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -132,7 +132,7 @@ int sequencer_remove_state(struct replay_opts *opts);\n  * commits should be rebased onto the new base, this flag needs to be passed.\n  */\n #define TODO_LIST_REBASE_COUSINS (1U << 4)\n-int sequencer_make_script(FILE *out, int argc, const char **argv,\n+int sequencer_make_script(struct strbuf *out, int argc, const char **argv,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(const char *command);\n-- \n2.19.1\n\n"},{"id":"359810","messageId":"20181007195418.25752-10-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181007195418.25752-1-alban.gruin@gmail.com","subject":"[PATCH 09/15] sequencer: refactor skip_unnecessary_picks() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-07T19:54:12Z","receivedAt":"2018-10-07T20:00:32Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors skip_unnecessary_picks() to work on a todo_list.  The\nfile-handling logic is completely dropped here, as its only usage is\nmade by complete_action().\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 56 +++++++++++++++--------------------------------------\n 1 file changed, 16 insertions(+), 40 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex b37935e5ab..a432b64048 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4545,38 +4545,20 @@ static int rewrite_file(const char *path, const char *buf, size_t len)\n }\n \n /* skip picking commits whose parents are unchanged */\n-static int skip_unnecessary_picks(struct object_id *output_oid)\n+static int skip_unnecessary_picks(struct todo_list *todo_list,\n+\t\t\t\t  struct object_id *output_oid)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct object_id *parent_oid;\n \tint fd, i;\n \n-\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n-\t\treturn error(_(\"could not read 'onto'\"));\n-\tif (get_oid(buf.buf, output_oid)) {\n-\t\tstrbuf_release(&buf);\n-\t\treturn error(_(\"need a HEAD to fixup\"));\n-\t}\n-\tstrbuf_release(&buf);\n-\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n-\n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tstruct todo_item *item = todo_list.items + i;\n+\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\tstruct todo_item *item = todo_list->items + i;\n \n \t\tif (item->command >= TODO_NOOP)\n \t\t\tcontinue;\n \t\tif (item->command != TODO_PICK)\n \t\t\tbreak;\n \t\tif (parse_commit(item->commit)) {\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\treturn error(_(\"could not parse commit '%s'\"),\n \t\t\t\toid_to_hex(&item->commit->object.oid));\n \t\t}\n@@ -4590,37 +4572,29 @@ static int skip_unnecessary_picks(struct object_id *output_oid)\n \t\toidcpy(output_oid, &item->commit->object.oid);\n \t}\n \tif (i > 0) {\n-\t\tint offset = get_item_line_offset(&todo_list, i);\n+\t\tint offset = get_item_line_offset(todo_list, i);\n \t\tconst char *done_path = rebase_path_done();\n \n \t\tfd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);\n \t\tif (fd < 0) {\n \t\t\terror_errno(_(\"could not open '%s' for writing\"),\n \t\t\t\t    done_path);\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\treturn -1;\n \t\t}\n-\t\tif (write_in_full(fd, todo_list.buf.buf, offset) < 0) {\n+\t\tif (write_in_full(fd, todo_list->buf.buf, offset) < 0) {\n \t\t\terror_errno(_(\"could not write to '%s'\"), done_path);\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\tclose(fd);\n \t\t\treturn -1;\n \t\t}\n \t\tclose(fd);\n \n-\t\tif (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,\n-\t\t\t\t todo_list.buf.len - offset) < 0) {\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\treturn -1;\n-\t\t}\n+\t\tstrbuf_splice(&todo_list->buf, 0, offset, NULL, 0);\n \n-\t\ttodo_list.current = i;\n-\t\tif (is_fixup(peek_command(&todo_list, 0)))\n-\t\t\trecord_in_rewritten(output_oid, peek_command(&todo_list, 0));\n+\t\ttodo_list->current = i;\n+\t\tif (is_fixup(peek_command(todo_list, 0)))\n+\t\t\trecord_in_rewritten(output_oid, peek_command(todo_list, 0));\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\n \treturn 0;\n }\n \n@@ -4701,6 +4675,11 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \n \ttodo_list_transform(&new_todo, flags & ~(TODO_LIST_SHORTEN_IDS));\n \n+\tif (opts->allow_ff && skip_unnecessary_picks(&new_todo, &oid)) {\n+\t\ttodo_list_release(&new_todo);\n+\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n+\t}\n+\n \tif (rewrite_file(todo_file, new_todo.buf.buf, new_todo.buf.len) < 0) {\n \t\ttodo_list_release(&new_todo);\n \t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n@@ -4708,12 +4687,9 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \n \ttodo_list_release(&new_todo);\n \n-\tif (opts->allow_ff && skip_unnecessary_picks(&oid))\n-\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n-\n \tif (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))\n \t\treturn -1;\n-;\n+\n \tif (require_clean_work_tree(\"rebase\", \"\", 1, 1))\n \t\treturn -1;\n \n-- \n2.19.1\n\n"},{"id":"359809","messageId":"20181007195418.25752-7-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181007195418.25752-1-alban.gruin@gmail.com","subject":"[PATCH 06/15] sequencer: refactor transform_todos() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-07T19:54:09Z","receivedAt":"2018-10-07T20:00:33Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors transform_todos() to work on a todo_list.  The function\nis renamed todo_list_transform().\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, transform_todo_file().\n\ntodo_list_transform() is not a static function, because it will be used\nby edit_todo_list() from rebase-interactive.c in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |  2 +-\n rebase-interactive.c          |  4 +--\n sequencer.c                   | 46 +++++++++++++++++++++++------------\n sequencer.h                   |  3 ++-\n 4 files changed, 35 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 8deef126d1..f827e53f05 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -252,7 +252,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t}\n \tcase SHORTEN_OIDS:\n \tcase EXPAND_OIDS:\n-\t\tret = transform_todos(flags);\n+\t\tret = transform_todo_file(flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n \t\tret = check_todo_list_from_file();\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex ef8540245d..7c7f720a3d 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -92,7 +92,7 @@ int edit_todo_list(unsigned flags)\n \n \tstrbuf_release(&buf);\n \n-\ttransform_todos(flags | TODO_LIST_SHORTEN_IDS);\n+\ttransform_todo_file(flags | TODO_LIST_SHORTEN_IDS);\n \n \tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n@@ -108,7 +108,7 @@ int edit_todo_list(unsigned flags)\n \tif (launch_sequence_editor(todo_file, NULL, NULL))\n \t\treturn -1;\n \n-\ttransform_todos(flags & ~(TODO_LIST_SHORTEN_IDS));\n+\ttransform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS));\n \n \treturn 0;\n }\ndiff --git a/sequencer.c b/sequencer.c\nindex 8a6176b3d0..30a7fe3958 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4440,23 +4440,13 @@ int sequencer_add_exec_commands(const char *commands)\n \treturn res;\n }\n \n-int transform_todos(unsigned flags)\n+void todo_list_transform(struct todo_list *todo_list, unsigned flags)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct todo_item *item;\n \tint i;\n \n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tfor (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {\n+\tfor (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n \t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len, item->arg);\n@@ -4492,9 +4482,33 @@ int transform_todos(unsigned flags)\n \t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len, item->arg);\n \t}\n \n-\ti = write_message(buf.buf, buf.len, todo_file, 0);\n+\tstrbuf_reset(&todo_list->buf);\n+\tstrbuf_add(&todo_list->buf, buf.buf, buf.len);\n+\tstrbuf_release(&buf);\n+\n+\tif (todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list))\n+\t\tBUG(\"unusable todo list\");\n+}\n+\n+int transform_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_transform(&todo_list, flags);\n+\n+\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n \ttodo_list_release(&todo_list);\n-\treturn i;\n+\treturn res;\n }\n \n int check_todo_list_from_file(void)\n@@ -4670,7 +4684,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n \t\t\t     rebase_path_todo_backup());\n \n-\tif (transform_todos(flags | TODO_LIST_SHORTEN_IDS))\n+\tif (transform_todo_file(flags | TODO_LIST_SHORTEN_IDS))\n \t\treturn error(_(\"could not transform the todo list\"));\n \n \tstrbuf_reset(buf);\n@@ -4699,7 +4713,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t\treturn -1;\n \t}\n \n-\tif (transform_todos(flags & ~(TODO_LIST_SHORTEN_IDS)))\n+\tif (transform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS)))\n \t\treturn error(_(\"could not transform the todo list\"));\n \n \tif (opts->allow_ff && skip_unnecessary_picks(&oid))\ndiff --git a/sequencer.h b/sequencer.h\nindex 7f5668500f..e1faca7884 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -112,6 +112,7 @@ struct todo_list {\n #define TODO_LIST_INIT { STRBUF_INIT }\n \n int todo_list_parse_insn_buffer(char *buf, struct todo_list *todo_list);\n+void todo_list_transform(struct todo_list *todo_list, unsigned flags);\n void todo_list_release(struct todo_list *todo_list);\n \n /* Call this to setup defaults before parsing command line options */\n@@ -135,7 +136,7 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(const char *command);\n-int transform_todos(unsigned flags);\n+int transform_todo_file(unsigned flags);\n int check_todo_list_from_file(void);\n int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-- \n2.19.1\n\n"},{"id":"359811","messageId":"20181007195418.25752-6-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181007195418.25752-1-alban.gruin@gmail.com","subject":"[PATCH 05/15] sequencer: refactor rearrange_squash() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-07T19:54:08Z","receivedAt":"2018-10-07T20:00:36Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors rearrange_squash() to work on a todo_list to avoid\nredundant reads and writes.  The function is renamed\ntodo_list_rearrange_squash().\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, rearrange_squash_in_todo_file().\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |  2 +-\n sequencer.c                   | 73 +++++++++++++++++++++--------------\n sequencer.h                   |  2 +-\n 3 files changed, 46 insertions(+), 31 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex ea1f93ccb6..8deef126d1 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -258,7 +258,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = check_todo_list_from_file();\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n-\t\tret = rearrange_squash();\n+\t\tret = rearrange_squash_in_todo_file();\n \t\tbreak;\n \tcase ADD_EXEC:\n \t\tret = sequencer_add_exec_commands(cmd);\ndiff --git a/sequencer.c b/sequencer.c\nindex 6d998f21a4..8a6176b3d0 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4632,7 +4632,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t    write_message(\"noop\\n\", 5, todo_file, 0))\n \t\treturn -1;\n \n-\tif (autosquash && rearrange_squash())\n+\tif (autosquash && rearrange_squash_in_todo_file())\n \t\treturn -1;\n \n \tif (cmd && *cmd)\n@@ -4738,22 +4738,13 @@ define_commit_slab(commit_todo_item, struct todo_item *);\n  * message will have to be retrieved from the commit (as the oneline in the\n  * script cannot be trusted) in order to normalize the autosquash arrangement.\n  */\n-int rearrange_squash(void)\n+static int todo_list_rearrange_squash(struct todo_list *todo_list)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct hashmap subject2item;\n-\tint res = 0, rearranged = 0, *next, *tail, i;\n+\tint rearranged = 0, *next, *tail, i;\n \tchar **subjects;\n \tstruct commit_todo_item commit_todo;\n \n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n-\n \tinit_commit_todo_item(&commit_todo);\n \t/*\n \t * The hashmap maps onelines to the respective todo list index.\n@@ -4765,13 +4756,13 @@ int rearrange_squash(void)\n \t * be moved to appear after the i'th.\n \t */\n \thashmap_init(&subject2item, (hashmap_cmp_fn) subject2item_cmp,\n-\t\t     NULL, todo_list.nr);\n-\tALLOC_ARRAY(next, todo_list.nr);\n-\tALLOC_ARRAY(tail, todo_list.nr);\n-\tALLOC_ARRAY(subjects, todo_list.nr);\n-\tfor (i = 0; i < todo_list.nr; i++) {\n+\t\t     NULL, todo_list->nr);\n+\tALLOC_ARRAY(next, todo_list->nr);\n+\tALLOC_ARRAY(tail, todo_list->nr);\n+\tALLOC_ARRAY(subjects, todo_list->nr);\n+\tfor (i = 0; i < todo_list->nr; i++) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n-\t\tstruct todo_item *item = todo_list.items + i;\n+\t\tstruct todo_item *item = todo_list->items + i;\n \t\tconst char *commit_buffer, *subject, *p;\n \t\tsize_t subject_len;\n \t\tint i2 = -1;\n@@ -4784,7 +4775,6 @@ int rearrange_squash(void)\n \t\t}\n \n \t\tif (is_fixup(item->command)) {\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\tclear_commit_todo_item(&commit_todo);\n \t\t\treturn error(_(\"the script was already rearranged.\"));\n \t\t}\n@@ -4819,7 +4809,7 @@ int rearrange_squash(void)\n \t\t\t\t *commit_todo_item_at(&commit_todo, commit2))\n \t\t\t\t/* found by commit name */\n \t\t\t\ti2 = *commit_todo_item_at(&commit_todo, commit2)\n-\t\t\t\t\t- todo_list.items;\n+\t\t\t\t\t- todo_list->items;\n \t\t\telse {\n \t\t\t\t/* copy can be a prefix of the commit subject */\n \t\t\t\tfor (i2 = 0; i2 < i; i2++)\n@@ -4832,7 +4822,7 @@ int rearrange_squash(void)\n \t\t}\n \t\tif (i2 >= 0) {\n \t\t\trearranged = 1;\n-\t\t\ttodo_list.items[i].command =\n+\t\t\ttodo_list->items[i].command =\n \t\t\t\tstarts_with(subject, \"fixup!\") ?\n \t\t\t\tTODO_FIXUP : TODO_SQUASH;\n \t\t\tif (next[i2] < 0)\n@@ -4852,8 +4842,8 @@ int rearrange_squash(void)\n \tif (rearranged) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n \n-\t\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\t\tenum todo_command command = todo_list.items[i].command;\n+\t\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\t\tenum todo_command command = todo_list->items[i].command;\n \t\t\tint cur = i;\n \n \t\t\t/*\n@@ -4865,12 +4855,12 @@ int rearrange_squash(void)\n \n \t\t\twhile (cur >= 0) {\n \t\t\t\tconst char *bol =\n-\t\t\t\t\tget_item_line(&todo_list, cur);\n+\t\t\t\t\tget_item_line(todo_list, cur);\n \t\t\t\tconst char *eol =\n-\t\t\t\t\tget_item_line(&todo_list, cur + 1);\n+\t\t\t\t\tget_item_line(todo_list, cur + 1);\n \n \t\t\t\t/* replace 'pick', by 'fixup' or 'squash' */\n-\t\t\t\tcommand = todo_list.items[cur].command;\n+\t\t\t\tcommand = todo_list->items[cur].command;\n \t\t\t\tif (is_fixup(command)) {\n \t\t\t\t\tstrbuf_addstr(&buf,\n \t\t\t\t\t\ttodo_command_info[command].str);\n@@ -4883,18 +4873,43 @@ int rearrange_squash(void)\n \t\t\t}\n \t\t}\n \n-\t\tres = rewrite_file(todo_file, buf.buf, buf.len);\n+\t\tstrbuf_reset(&todo_list->buf);\n+\t\tstrbuf_add(&todo_list->buf, buf.buf, buf.len);\n \t\tstrbuf_release(&buf);\n \t}\n \n \tfree(next);\n \tfree(tail);\n-\tfor (i = 0; i < todo_list.nr; i++)\n+\tfor (i = 0; i < todo_list->nr; i++)\n \t\tfree(subjects[i]);\n \tfree(subjects);\n \thashmap_free(&subject2item, 1);\n-\ttodo_list_release(&todo_list);\n \n \tclear_commit_todo_item(&commit_todo);\n+\n+\tif (todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list))\n+\t\tBUG(\"unusable todo list\");\n+\n+\treturn 0;\n+}\n+\n+int rearrange_squash_in_todo_file(void)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n+\n+\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n+\t\treturn -1;\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn -1;\n+\t}\n+\n+\tres = todo_list_rearrange_squash(&todo_list);\n+\tif (!res)\n+\t\tres = rewrite_file(todo_file, todo_list.buf.buf, todo_list.buf.len);\n+\n+\ttodo_list_release(&todo_list);\n \treturn res;\n }\ndiff --git a/sequencer.h b/sequencer.h\nindex fb8b85bf9e..7f5668500f 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -141,7 +141,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, const char *cmd,\n \t\t    unsigned autosquash);\n-int rearrange_squash(void);\n+int rearrange_squash_in_todo_file(void);\n \n extern const char sign_off_header[];\n \n-- \n2.19.1\n\n"},{"id":"359813","messageId":"63f7c10f-9379-06d8-6b40-c527b2ec8504@gmail.com","threadId":"49504","inReplyTo":"20181007195418.25752-1-alban.gruin@gmail.com","subject":"Re: [PATCH 00/15] sequencer: refactor functions working on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-07T20:51:10Z","receivedAt":"2018-10-07T20:51:19Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Le 07/10/2018 à 21:54, Alban Gruin a écrit :\n> At the center of the \"interactive\" part of the interactive rebase lies\n> the todo list.  When the user starts an interactive rebase, a todo list\n> is generated, presented to the user (who then edits it using a text\n> editor), read back, and then is checked and processed before the actual\n> rebase takes place.\n> \n> Some of this processing includes adding execs commands, reordering\n> fixup! and squash! commits, and checking if no commits were accidentally\n> dropped by the user.\n> \n> Before I converted the interactive rebase in C, these functions were\n> called by git-rebase--interactive.sh through git-rebase--helper.  Since\n> the only way to pass around a large amount of data between a shell\n> script and a C program is to use a file (or any declination of a file),\n> the functions that checked and processed the todo list were directly\n> working on a file, the same file that the user edited.\n> \n> During the conversion, I did not address this issue, which lead to a\n> complete_action() that reads the todo list file, does some computation\n> based on its content, and writes it back to the disk, several times in\n> the same function.\n> \n> As it is not an efficient way to handle a data structure, this patch\n> series refactor the functions that processes the todo list to work on a\n> todo_list structure instead of reading it from the disk.\n> \n> Some commits consists in modifying edit_todo_list() (initially used by\n> --edit-todo) to handle the initial edition of the todo list, to increase\n> code sharing.\n> \n\nAnd it’s based on the 8th version of my patch series “rebase -i: rewrite\nin C”.\n\n"},{"id":"360175","messageId":"b87d71e4-7fa9-fc8b-90af-7baed2671aaa@talktalk.net","threadId":"49504","inReplyTo":"20181007195418.25752-4-alban.gruin@gmail.com","subject":"Re: [PATCH 03/15] sequencer: refactor check_todo_list() to work on a todo_list","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2018-10-11T11:23:02Z","receivedAt":"2018-10-11T11:23:06Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nI like the direction that this series is going\n\nOn 07/10/2018 20:54, Alban Gruin wrote:\n> This refactors check_todo_list() to work on a todo_list to avoid\n> redundant reads and writes to the disk.  The function is renamed\n> todo_list_check().\n> \n> As rebase -p still need to check the todo list from the disk, a new\n> function is introduced, check_todo_list_from_file().  It reads the file\n> from the disk, parses it, pass the todo_list to todo_list_check(), and\n> writes it back to the disk.\n\nAfter this commit we still use check_todo_list_from_file() even without \n'-p', it would be clearer if this (and the following commits) mentioned \nthat the new function will be wired up later.\n\n> As get_missing_commit_check_level() and the enum\n> missing_commit_check_level are no longer needed inside of sequencer.c,\n> they are moved to rebase-interactive.c, and made static again.\n> \n> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n> ---\n>   builtin/rebase--interactive.c |   2 +-\n>   rebase-interactive.c          | 106 ++++++++++++++++++++++++++++++++-\n>   rebase-interactive.h          |   1 +\n>   sequencer.c                   | 109 ++++------------------------------\n>   sequencer.h                   |   9 +--\n>   5 files changed, 120 insertions(+), 107 deletions(-)\n> \n> diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\n> index a2ab68ed06..ea1f93ccb6 100644\n> --- a/builtin/rebase--interactive.c\n> +++ b/builtin/rebase--interactive.c\n> @@ -255,7 +255,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>   \t\tret = transform_todos(flags);\n>   \t\tbreak;\n>   \tcase CHECK_TODO_LIST:\n> -\t\tret = check_todo_list();\n> +\t\tret = check_todo_list_from_file();\n>   \t\tbreak;\n>   \tcase REARRANGE_SQUASH:\n>   \t\tret = rearrange_squash();\n> diff --git a/rebase-interactive.c b/rebase-interactive.c\n> index 0f4119cbae..ef8540245d 100644\n> --- a/rebase-interactive.c\n> +++ b/rebase-interactive.c\n> @@ -1,8 +1,32 @@\n>   #include \"cache.h\"\n>   #include \"commit.h\"\n> -#include \"rebase-interactive.h\"\n>   #include \"sequencer.h\"\n> +#include \"rebase-interactive.h\"\n>   #include \"strbuf.h\"\n> +#include \"commit-slab.h\"\n> +#include \"config.h\"\n> +\n> +enum missing_commit_check_level {\n> +\tMISSING_COMMIT_CHECK_IGNORE = 0,\n> +\tMISSING_COMMIT_CHECK_WARN,\n> +\tMISSING_COMMIT_CHECK_ERROR\n> +};\n> +\n> +static enum missing_commit_check_level get_missing_commit_check_level(void)\n> +{\n> +\tconst char *value;\n> +\n> +\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n> +\t\t\t!strcasecmp(\"ignore\", value))\n> +\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n> +\tif (!strcasecmp(\"warn\", value))\n> +\t\treturn MISSING_COMMIT_CHECK_WARN;\n> +\tif (!strcasecmp(\"error\", value))\n> +\t\treturn MISSING_COMMIT_CHECK_ERROR;\n> +\twarning(_(\"unrecognized setting %s for option \"\n> +\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n> +\treturn MISSING_COMMIT_CHECK_IGNORE;\n> +}\n>   \n>   void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n>   \t\t      struct strbuf *buf)\n> @@ -88,3 +112,83 @@ int edit_todo_list(unsigned flags)\n>   \n>   \treturn 0;\n>   }\n> +\n> +define_commit_slab(commit_seen, unsigned char);\n> +/*\n> + * Check if the user dropped some commits by mistake\n> + * Behaviour determined by rebase.missingCommitsCheck.\n> + * Check if there is an unrecognized command or a\n> + * bad SHA-1 in a command.\n> + */\n> +int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo)\n> +{\n> +\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n> +\tstruct strbuf missing = STRBUF_INIT;\n> +\tint advise_to_edit_todo = 0, res = 0, i;\n> +\tstruct commit_seen commit_seen;\n> +\n> +\tinit_commit_seen(&commit_seen);\n> +\n> +\tres = todo_list_parse_insn_buffer(old_todo->buf.buf, old_todo);\n\nWhile it made sense to parse the old and new todo lists here when they \nwere being loaded from a file, I think it is confusing to keep that \nbehavior now it is being passed struct todo_lists. When we get to patch \n8 the newly read commit list is passed to this function without being \nparsed which confused me until I came back and check what was going on here.\n\nBest Wishes\n\nPhillip\n\n> +\tif (!res)\n> +\t\tres = todo_list_parse_insn_buffer(new_todo->buf.buf, new_todo);\n> +\tif (res) {\n> +\t\tadvise_to_edit_todo = res;\n> +\t\tgoto leave_check;\n> +\t}\n> +\n> +\tif (check_level == MISSING_COMMIT_CHECK_IGNORE)\n> +\t\tgoto leave_check;\n> +\n> +\t/* Mark the commits in git-rebase-todo as seen */\n> +\tfor (i = 0; i < new_todo->nr; i++) {\n> +\t\tstruct commit *commit = new_todo->items[i].commit;\n> +\t\tif (commit)\n> +\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n> +\t}\n> +\n> +\t/* Find commits in git-rebase-todo.backup yet unseen */\n> +\tfor (i = old_todo->nr - 1; i >= 0; i--) {\n> +\t\tstruct todo_item *item = old_todo->items + i;\n> +\t\tstruct commit *commit = item->commit;\n> +\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n> +\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n> +\t\t\t\t    find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV),\n> +\t\t\t\t    item->arg_len, item->arg);\n> +\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n> +\t\t}\n> +\t}\n> +\n> +\t/* Warn about missing commits */\n> +\tif (!missing.len)\n> +\t\tgoto leave_check;\n> +\n> +\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n> +\t\tadvise_to_edit_todo = res = 1;\n> +\n> +\tfprintf(stderr,\n> +\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n> +\t\t\"Dropped commits (newer to older):\\n\"));\n> +\n> +\t/* Make the list user-friendly and display */\n> +\tfputs(missing.buf, stderr);\n> +\tstrbuf_release(&missing);\n> +\n> +\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n> +\t\t\"explicitly remove a commit.\\n\\n\"\n> +\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n> +\t\t\"the level of warnings.\\n\"\n> +\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n> +\n> +leave_check:\n> +\tclear_commit_seen(&commit_seen);\n> +\n> +\tif (advise_to_edit_todo)\n> +\t\tfprintf(stderr,\n> +\t\t\t_(\"You can fix this with 'git rebase --edit-todo' \"\n> +\t\t\t  \"and then run 'git rebase --continue'.\\n\"\n> +\t\t\t  \"Or you can abort the rebase with 'git rebase\"\n> +\t\t\t  \" --abort'.\\n\"));\n> +\n> +\treturn res;\n> +}\n> diff --git a/rebase-interactive.h b/rebase-interactive.h\n> index 971da03776..6bc7bc315d 100644\n> --- a/rebase-interactive.h\n> +++ b/rebase-interactive.h\n> @@ -4,5 +4,6 @@\n>   void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n>   \t\t      struct strbuf *buf);\n>   int edit_todo_list(unsigned flags);\n> +int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n>   \n>   #endif\n> diff --git a/sequencer.c b/sequencer.c\n> index bb8ca2477f..8dda61927c 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -4487,111 +4487,26 @@ int transform_todos(unsigned flags)\n>   \treturn i;\n>   }\n>   \n> -enum missing_commit_check_level get_missing_commit_check_level(void)\n> +int check_todo_list_from_file(void)\n>   {\n> -\tconst char *value;\n> -\n> -\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n> -\t\t\t!strcasecmp(\"ignore\", value))\n> -\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n> -\tif (!strcasecmp(\"warn\", value))\n> -\t\treturn MISSING_COMMIT_CHECK_WARN;\n> -\tif (!strcasecmp(\"error\", value))\n> -\t\treturn MISSING_COMMIT_CHECK_ERROR;\n> -\twarning(_(\"unrecognized setting %s for option \"\n> -\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n> -\treturn MISSING_COMMIT_CHECK_IGNORE;\n> -}\n> -\n> -define_commit_slab(commit_seen, unsigned char);\n> -/*\n> - * Check if the user dropped some commits by mistake\n> - * Behaviour determined by rebase.missingCommitsCheck.\n> - * Check if there is an unrecognized command or a\n> - * bad SHA-1 in a command.\n> - */\n> -int check_todo_list(void)\n> -{\n> -\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n> -\tstruct strbuf todo_file = STRBUF_INIT;\n> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n> -\tstruct strbuf missing = STRBUF_INIT;\n> -\tint advise_to_edit_todo = 0, res = 0, i;\n> -\tstruct commit_seen commit_seen;\n> -\n> -\tinit_commit_seen(&commit_seen);\n> +\tstruct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;\n> +\tint res = 0;\n>   \n> -\tstrbuf_addstr(&todo_file, rebase_path_todo());\n> -\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n> +\tif (strbuf_read_file_or_whine(&new_todo.buf, rebase_path_todo()) < 0) {\n>   \t\tres = -1;\n> -\t\tgoto leave_check;\n> -\t}\n> -\tadvise_to_edit_todo = res =\n> -\t\ttodo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n> -\n> -\tif (res || check_level == MISSING_COMMIT_CHECK_IGNORE)\n> -\t\tgoto leave_check;\n> -\n> -\t/* Mark the commits in git-rebase-todo as seen */\n> -\tfor (i = 0; i < todo_list.nr; i++) {\n> -\t\tstruct commit *commit = todo_list.items[i].commit;\n> -\t\tif (commit)\n> -\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n> +\t\tgoto out;\n>   \t}\n>   \n> -\ttodo_list_release(&todo_list);\n> -\tstrbuf_addstr(&todo_file, \".backup\");\n> -\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n> +\tif (strbuf_read_file_or_whine(&old_todo.buf, rebase_path_todo_backup()) < 0) {\n>   \t\tres = -1;\n> -\t\tgoto leave_check;\n> -\t}\n> -\tstrbuf_release(&todo_file);\n> -\tres = !!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n> -\n> -\t/* Find commits in git-rebase-todo.backup yet unseen */\n> -\tfor (i = todo_list.nr - 1; i >= 0; i--) {\n> -\t\tstruct todo_item *item = todo_list.items + i;\n> -\t\tstruct commit *commit = item->commit;\n> -\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n> -\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n> -\t\t\t\t    short_commit_name(commit),\n> -\t\t\t\t    item->arg_len, item->arg);\n> -\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n> -\t\t}\n> +\t\tgoto out;\n>   \t}\n>   \n> -\t/* Warn about missing commits */\n> -\tif (!missing.len)\n> -\t\tgoto leave_check;\n> -\n> -\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n> -\t\tadvise_to_edit_todo = res = 1;\n> +\tres = todo_list_check(&old_todo, &new_todo);\n>   \n> -\tfprintf(stderr,\n> -\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n> -\t\t\"Dropped commits (newer to older):\\n\"));\n> -\n> -\t/* Make the list user-friendly and display */\n> -\tfputs(missing.buf, stderr);\n> -\tstrbuf_release(&missing);\n> -\n> -\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n> -\t\t\"explicitly remove a commit.\\n\\n\"\n> -\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n> -\t\t\"the level of warnings.\\n\"\n> -\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n> -\n> -leave_check:\n> -\tclear_commit_seen(&commit_seen);\n> -\tstrbuf_release(&todo_file);\n> -\ttodo_list_release(&todo_list);\n> -\n> -\tif (advise_to_edit_todo)\n> -\t\tfprintf(stderr,\n> -\t\t\t_(\"You can fix this with 'git rebase --edit-todo' \"\n> -\t\t\t  \"and then run 'git rebase --continue'.\\n\"\n> -\t\t\t  \"Or you can abort the rebase with 'git rebase\"\n> -\t\t\t  \" --abort'.\\n\"));\n> +out:\n> +\ttodo_list_release(&old_todo);\n> +\ttodo_list_release(&new_todo);\n>   \n>   \treturn res;\n>   }\n> @@ -4769,7 +4684,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n>   \n>   \ttodo_list_release(&todo_list);\n>   \n> -\tif (check_todo_list()) {\n> +\tif (check_todo_list_from_file()) {\n>   \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n>   \t\treturn -1;\n>   \t}\n> diff --git a/sequencer.h b/sequencer.h\n> index c786dee543..fb8b85bf9e 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -63,12 +63,6 @@ struct replay_opts {\n>   };\n>   #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }\n>   \n> -enum missing_commit_check_level {\n> -\tMISSING_COMMIT_CHECK_IGNORE = 0,\n> -\tMISSING_COMMIT_CHECK_WARN,\n> -\tMISSING_COMMIT_CHECK_ERROR\n> -};\n> -\n>   int write_message(const void *buf, size_t len, const char *filename,\n>   \t\t  int append_eol);\n>   \n> @@ -142,8 +136,7 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,\n>   \n>   int sequencer_add_exec_commands(const char *command);\n>   int transform_todos(unsigned flags);\n> -enum missing_commit_check_level get_missing_commit_check_level(void);\n> -int check_todo_list(void);\n> +int check_todo_list_from_file(void);\n>   int complete_action(struct replay_opts *opts, unsigned flags,\n>   \t\t    const char *shortrevisions, const char *onto_name,\n>   \t\t    const char *onto, const char *orig_head, const char *cmd,\n> \n\n"},{"id":"360177","messageId":"6188911e-fd21-4c7c-885d-a15d2b46aa86@talktalk.net","threadId":"49504","inReplyTo":"20181007195418.25752-5-alban.gruin@gmail.com","subject":"Re: [PATCH 04/15] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2018-10-11T11:25:34Z","receivedAt":"2018-10-11T11:25:40Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 07/10/2018 20:54, Alban Gruin wrote:\n> This refactors sequencer_add_exec_commands() to work on a todo_list to\n> avoid redundant reads and writes to the disk.\n> \n> sequencer_add_exec_commands() still reads the todo list from the disk,\n> as it is needed by rebase -p.  todo_list_add_exec_commands() works on a\n> todo_list structure, and reparses it at the end.\n> \n> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n> ---\n>   sequencer.c | 56 +++++++++++++++++++++++++++++++----------------------\n>   1 file changed, 33 insertions(+), 23 deletions(-)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index 8dda61927c..6d998f21a4 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -4370,34 +4370,21 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,\n>   \treturn 0;\n>   }\n>   \n> -/*\n> - * Add commands after pick and (series of) squash/fixup commands\n> - * in the todo list.\n> - */\n> -int sequencer_add_exec_commands(const char *commands)\n> +static void todo_list_add_exec_commands(struct todo_list *todo_list,\n> +\t\t\t\t\tconst char *commands)\n>   {\n> -\tconst char *todo_file = rebase_path_todo();\n> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n> -\tstruct strbuf *buf = &todo_list.buf;\n> +\tstruct strbuf *buf = &todo_list->buf;\n>   \tsize_t offset = 0, commands_len = strlen(commands);\n>   \tint i, insert;\n>   \n> -\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> -\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n> -\n> -\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n> -\t\ttodo_list_release(&todo_list);\n> -\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> -\t}\n> -\n>   \t/*\n>   \t * Insert <commands> after every pick. Here, fixup/squash chains\n>   \t * are considered part of the pick, so we insert the commands *after*\n>   \t * those chains if there are any.\n>   \t */\n>   \tinsert = -1;\n> -\tfor (i = 0; i < todo_list.nr; i++) {\n> -\t\tenum todo_command command = todo_list.items[i].command;\n> +\tfor (i = 0; i < todo_list->nr; i++) {\n> +\t\tenum todo_command command = todo_list->items[i].command;\n>   \n>   \t\tif (insert >= 0) {\n>   \t\t\t/* skip fixup/squash chains */\n> @@ -4408,7 +4395,7 @@ int sequencer_add_exec_commands(const char *commands)\n>   \t\t\t\tcontinue;\n>   \t\t\t}\n>   \t\t\tstrbuf_insert(buf,\n> -\t\t\t\t      todo_list.items[insert].offset_in_buf +\n> +\t\t\t\t      todo_list->items[insert].offset_in_buf +\n>   \t\t\t\t      offset, commands, commands_len);\n>   \t\t\toffset += commands_len;\n>   \t\t\tinsert = -1;\n> @@ -4419,15 +4406,38 @@ int sequencer_add_exec_commands(const char *commands)\n>   \t}\n>   \n>   \t/* insert or append final <commands> */\n> -\tif (insert >= 0 && insert < todo_list.nr)\n> -\t\tstrbuf_insert(buf, todo_list.items[insert].offset_in_buf +\n> +\tif (insert >= 0 && insert < todo_list->nr)\n> +\t\tstrbuf_insert(buf, todo_list->items[insert].offset_in_buf +\n>   \t\t\t      offset, commands, commands_len);\n>   \telse if (insert >= 0 || !offset)\n>   \t\tstrbuf_add(buf, commands, commands_len);\n>   \n> -\ti = write_message(buf->buf, buf->len, todo_file, 0);\n> +\tif (todo_list_parse_insn_buffer(buf->buf, todo_list))\n> +\t\tBUG(\"unusable todo list\");}\n\nIt is a shame to have to re-parse the todo list, I wonder how difficult \nit would be to adjust the todo_list item array as the exec commands are \ninserted. The same applies to the next couple of patches\n\nBest Wishes\n\nPhillip\n\n> +\n> +/*\n> + * Add commands after pick and (series of) squash/fixup commands\n> + * in the todo list.\n> + */\n> +int sequencer_add_exec_commands(const char *commands)\n> +{\n> +\tconst char *todo_file = rebase_path_todo();\n> +\tstruct todo_list todo_list = TODO_LIST_INIT;\n> +\tint res;\n> +\n> +\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> +\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n> +\n> +\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n> +\t\ttodo_list_release(&todo_list);\n> +\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> +\t}\n> +\n> +\ttodo_list_add_exec_commands(&todo_list, commands);\n> +\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n>   \ttodo_list_release(&todo_list);\n> -\treturn i;\n> +\n> +\treturn res;\n>   }\n>   \n>   int transform_todos(unsigned flags)\n> \n\n"},{"id":"360182","messageId":"d0adf861-3046-f0b6-3217-c89d92319e43@talktalk.net","threadId":"49504","inReplyTo":"20181007195418.25752-9-alban.gruin@gmail.com","subject":"Re: [PATCH 08/15] sequencer: change complete_action() to use the refactored functions","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2018-10-11T13:51:11Z","receivedAt":"2018-10-11T13:51:19Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 07/10/2018 20:54, Alban Gruin wrote:\n> complete_action() used functions that read the todo-list file, made some\n> changes to it, and wrote it back to the disk.\n> \n> The previous commits were dedicated to separate the part that deals with\n> the file from the actual logic of these functions.  Now that this is\n> done, we can call directly the \"logic\" functions to avoid useless file\n> access.\n> \n> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n> ---\n>   builtin/rebase--interactive.c | 13 +-----\n>   sequencer.c                   | 76 +++++++++++++++++------------------\n>   sequencer.h                   |  2 +-\n>   3 files changed, 38 insertions(+), 53 deletions(-)\n> \n> diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\n> index eef1ff2e83..0700339f90 100644\n> --- a/builtin/rebase--interactive.c\n> +++ b/builtin/rebase--interactive.c\n> @@ -71,7 +71,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n>   \tconst char *head_hash = NULL;\n>   \tchar *revisions = NULL, *shortrevisions = NULL;\n>   \tstruct argv_array make_script_args = ARGV_ARRAY_INIT;\n> -\tFILE *todo_list_file;\n>   \tstruct todo_list todo_list = TODO_LIST_INIT;\n>   \n>   \tif (prepare_branch_to_be_rebased(opts, switch_to))\n> @@ -94,14 +93,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n>   \tif (!upstream && squash_onto)\n>   \t\twrite_file(path_squash_onto(), \"%s\\n\", squash_onto);\n>   \n> -\ttodo_list_file = fopen(rebase_path_todo(), \"w\");\n> -\tif (!todo_list_file) {\n> -\t\tfree(revisions);\n> -\t\tfree(shortrevisions);\n> -\n> -\t\treturn error_errno(_(\"could not open %s\"), rebase_path_todo());\n> -\t}\n> -\n>   \targv_array_pushl(&make_script_args, \"\", revisions, NULL);\n>   \tif (restrict_revision)\n>   \t\targv_array_push(&make_script_args, restrict_revision);\n> @@ -109,15 +100,13 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n>   \tret = sequencer_make_script(&todo_list.buf,\n>   \t\t\t\t    make_script_args.argc, make_script_args.argv,\n>   \t\t\t\t    flags);\n\nI think it would be clearer to parse the todo list here explicitly \nrather than doing it implicitly in complete_action()\n\n> -\tfputs(todo_list.buf.buf, todo_list_file);\n> -\tfclose(todo_list_file);\n>   \n>   \tif (ret)\n>   \t\terror(_(\"could not generate todo list\"));\n>   \telse {\n>   \t\tdiscard_cache() >   \t\tret = complete_action(opts, flags, shortrevisions, onto_name, onto,\n> -\t\t\t\t      head_hash, cmd, autosquash);\n> +\t\t\t\t      head_hash, cmd, autosquash, &todo_list);\n>   \t}\n>   \n>   \tfree(revisions);\n> diff --git a/sequencer.c b/sequencer.c\n> index dfb8d1c974..b37935e5ab 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -4624,93 +4624,89 @@ static int skip_unnecessary_picks(struct object_id *output_oid)\n>   \treturn 0;\n>   }\n>   \n> +static int todo_list_rearrange_squash(struct todo_list *todo_list);\n> +\n>   int complete_action(struct replay_opts *opts, unsigned flags,\n>   \t\t    const char *shortrevisions, const char *onto_name,\n>   \t\t    const char *onto, const char *orig_head, const char *cmd,\n> -\t\t    unsigned autosquash)\n> +\t\t    unsigned autosquash, struct todo_list *todo_list)\n>   {\n>   \tconst char *shortonto, *todo_file = rebase_path_todo();\n> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n> -\tstruct strbuf *buf = &(todo_list.buf);\n> +\tstruct todo_list new_todo = TODO_LIST_INIT;\n> +\tstruct strbuf *buf = &todo_list->buf;\n>   \tstruct object_id oid;\n> -\tstruct stat st;\n> +\tint command_count;\n>   \n>   \tget_oid(onto, &oid);\n>   \tshortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);\n>   \n> -\tif (!lstat(todo_file, &st) && st.st_size == 0 &&\n> -\t    write_message(\"noop\\n\", 5, todo_file, 0))\n> -\t\treturn -1;\n> +\tif (buf->len == 0)\n> +\t\tstrbuf_add(buf, \"noop\\n\", 5);\n> +\n> +\tif (todo_list_parse_insn_buffer(buf->buf, todo_list))\n> +\t\tBUG(\"unusable todo list\");\n>   \n> -\tif (autosquash && rearrange_squash_in_todo_file())\n> +\tif (autosquash && todo_list_rearrange_squash(todo_list))\n>   \t\treturn -1;\n>   \n>   \tif (cmd && *cmd)\n> -\t\tsequencer_add_exec_commands(cmd);\n> +\t\ttodo_list_add_exec_commands(todo_list, cmd);\n>   \n> -\tif (strbuf_read_file(buf, todo_file, 0) < 0)\n> -\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> -\n> -\tif (todo_list_parse_insn_buffer(buf->buf, &todo_list)) {\n> -\t\ttodo_list_release(&todo_list);\n> -\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> -\t}\n> -\n> -\tif (count_commands(&todo_list) == 0) {\n> +\tcommand_count = count_commands(todo_list);\n> +\tif (command_count == 0) {\n>   \t\tapply_autostash(opts);\n>   \t\tsequencer_remove_state(opts);\n> -\t\ttodo_list_release(&todo_list);\n>   \n>   \t\treturn error(_(\"nothing to do\"));\n>   \t}\n>   \n> +\ttodo_list_transform(todo_list, flags | TODO_LIST_SHORTEN_IDS);\n> +\n>   \tstrbuf_addch(buf, '\\n');\n>   \tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n>   \t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n> -\t\t\t\t      count_commands(&todo_list)),\n> -\t\t\t      shortrevisions, shortonto, count_commands(&todo_list));\n> +\t\t\t\t      command_count),\n> +\t\t\t      shortrevisions, shortonto, command_count);\n>   \tappend_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);\n>   \n> -\tif (write_message(buf->buf, buf->len, todo_file, 0)) {\n> -\t\ttodo_list_release(&todo_list);\n> -\t\treturn -1;\n> -\t}\n> +\tif (write_message(buf->buf, buf->len, todo_file, 0))\n> +\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n>   \n>   \tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n>   \t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n>   \t\t\t     rebase_path_todo_backup());\n>   \n> -\tif (transform_todo_file(flags | TODO_LIST_SHORTEN_IDS))\n> -\t\treturn error(_(\"could not transform the todo list\"));\n> -\n> -\tstrbuf_reset(buf);\n> -\n> -\tif (launch_sequence_editor(todo_file, buf, NULL)) {\n> +\tif (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {\n>   \t\tapply_autostash(opts);\n>   \t\tsequencer_remove_state(opts);\n> -\t\ttodo_list_release(&todo_list);\n>   \n>   \t\treturn -1;\n>   \t}\n>   \n> -\tstrbuf_stripspace(buf, 1);\n> -\tif (buf->len == 0) {\n> +\tstrbuf_stripspace(&new_todo.buf, 1);\n> +\tif (new_todo.buf.len == 0) {\n>   \t\tapply_autostash(opts);\n>   \t\tsequencer_remove_state(opts);\n> -\t\ttodo_list_release(&todo_list);\n> +\t\ttodo_list_release(&new_todo);\n>   \n>   \t\treturn error(_(\"nothing to do\"));\n>   \t}\n>   \n> -\ttodo_list_release(&todo_list);\n> -\n> -\tif (check_todo_list_from_file()) {\n> +\tif (todo_list_check(todo_list, &new_todo)) {\n>   \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n> +\t\ttodo_list_release(&new_todo);\n> +\n>   \t\treturn -1;\n>   \t}\n>   \n> -\tif (transform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS)))\n> -\t\treturn error(_(\"could not transform the todo list\"));\n> +\ttodo_list_transform(&new_todo, flags & ~(TODO_LIST_SHORTEN_IDS));\n> +\n> +\tif (rewrite_file(todo_file, new_todo.buf.buf, new_todo.buf.len) < 0) {\n> +\t\ttodo_list_release(&new_todo);\n> +\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n> +\t}\n\nrewrite_file() can truncate the old version of the file if there is an \nerror when writing the new version, I think it would be better to use \nwrite_message() instead as that atomically updates the file. The same \napplies to patch 5 (refactor rearrange_squash()) after which I think \nthere will be no callers to rewrite_file() so it can be deleted.\n\nBest Wishes\n\nPhillip\n\n> +\n> +\ttodo_list_release(&new_todo);\n>   \n>   \tif (opts->allow_ff && skip_unnecessary_picks(&oid))\n>   \t\treturn error(_(\"could not skip unnecessary pick commands\"));\n> diff --git a/sequencer.h b/sequencer.h\n> index 21d9ba09ab..5bd3b79282 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -141,7 +141,7 @@ int check_todo_list_from_file(void);\n>   int complete_action(struct replay_opts *opts, unsigned flags,\n>   \t\t    const char *shortrevisions, const char *onto_name,\n>   \t\t    const char *onto, const char *orig_head, const char *cmd,\n> -\t\t    unsigned autosquash);\n> +\t\t    unsigned autosquash, struct todo_list *todo_list);\n>   int rearrange_squash_in_todo_file(void);\n>   \n>   extern const char sign_off_header[];\n> \n\n"},{"id":"360188","messageId":"f95102e3-3968-361f-2e2a-a22989519480@talktalk.net","threadId":"49504","inReplyTo":"20181007195418.25752-11-alban.gruin@gmail.com","subject":"Re: [PATCH 10/15] rebase-interactive: use todo_list_transform() in edit_todo_list()","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2018-10-11T15:16:07Z","receivedAt":"2018-10-11T15:16:13Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 07/10/2018 20:54, Alban Gruin wrote:\n> Just like complete_action(), edit_todo_list() used a\n> function (transform_todo_file()) that read the todo-list from the disk\n> and wrote it back, resulting in useless disk accesses.\n> \n> This changes edit_todo_list() to call directly todo_list_transform()\n> instead.\n> \n> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n> ---\n>   rebase-interactive.c | 40 +++++++++++++++++++---------------------\n>   1 file changed, 19 insertions(+), 21 deletions(-)\n> \n> diff --git a/rebase-interactive.c b/rebase-interactive.c\n> index 7c7f720a3d..f42d48e192 100644\n> --- a/rebase-interactive.c\n> +++ b/rebase-interactive.c\n> @@ -78,39 +78,37 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n>   \n>   int edit_todo_list(unsigned flags)\n>   {\n> -\tstruct strbuf buf = STRBUF_INIT;\n>   \tconst char *todo_file = rebase_path_todo();\n> +\tstruct todo_list todo_list = TODO_LIST_INIT;\n> +\tint res = 0;\n>   \n> -\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n> +\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>   \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n>   \n> -\tstrbuf_stripspace(&buf, 1);\n> -\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n> -\t\tstrbuf_release(&buf);\n> -\t\treturn -1;\n> -\t}\n> -\n> -\tstrbuf_release(&buf);\n> +\tstrbuf_stripspace(&todo_list.buf, 1);\n> +\tif (!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list))\n> +\t\ttodo_list_transform(&todo_list, flags | TODO_LIST_SHORTEN_IDS);\n>   \n> -\ttransform_todo_file(flags | TODO_LIST_SHORTEN_IDS);\n> -\n> -\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n> -\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> +\tappend_todo_help(1, 0, &todo_list.buf);\n>   \n> -\tappend_todo_help(1, 0, &buf);\n\nI think this patch is fine, I was just wondering if you meant to move \nthe call to append_todo_help() above the blank line?\n\nBest Wishes\n\nPhillip\n\n> -\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n> -\t\tstrbuf_release(&buf);\n> +\tif (write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0)) {\n> +\t\ttodo_list_release(&todo_list);\n>   \t\treturn -1;\n>   \t}\n>   \n> -\tstrbuf_release(&buf);\n> -\n> -\tif (launch_sequence_editor(todo_file, NULL, NULL))\n> +\tstrbuf_reset(&todo_list.buf);\n> +\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n> +\t\ttodo_list_release(&todo_list);\n>   \t\treturn -1;\n> +\t}\n>   \n> -\ttransform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS));\n> +\tif (!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n> +\t\ttodo_list_transform(&todo_list, flags & ~(TODO_LIST_SHORTEN_IDS));\n> +\t\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n> +\t}\n>   \n> -\treturn 0;\n> +\ttodo_list_release(&todo_list);\n> +\treturn res;\n>   }\n>   \n>   define_commit_slab(commit_seen, unsigned char);\n> \n\n"},{"id":"360193","messageId":"6c55857a-ce3c-f9fc-18c6-d7cef2976931@gmail.com","threadId":"49504","inReplyTo":"6188911e-fd21-4c7c-885d-a15d2b46aa86@talktalk.net","subject":"Re: [PATCH 04/15] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-11T16:57:27Z","receivedAt":"2018-10-11T16:57:40Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Hi Phillip,\n\nthanks for taking the time to review my patches.\n\nLe 11/10/2018 à 13:25, Phillip Wood a écrit :\n> On 07/10/2018 20:54, Alban Gruin wrote:\n>> @@ -4419,15 +4406,38 @@ int sequencer_add_exec_commands(const char\n>> *commands)\n>>       }\n>>         /* insert or append final <commands> */\n>> -    if (insert >= 0 && insert < todo_list.nr)\n>> -        strbuf_insert(buf, todo_list.items[insert].offset_in_buf +\n>> +    if (insert >= 0 && insert < todo_list->nr)\n>> +        strbuf_insert(buf, todo_list->items[insert].offset_in_buf +\n>>                     offset, commands, commands_len);\n>>       else if (insert >= 0 || !offset)\n>>           strbuf_add(buf, commands, commands_len);\n>>   -    i = write_message(buf->buf, buf->len, todo_file, 0);\n>> +    if (todo_list_parse_insn_buffer(buf->buf, todo_list))\n>> +        BUG(\"unusable todo list\");}\n> \n> It is a shame to have to re-parse the todo list, I wonder how difficult\n> it would be to adjust the todo_list item array as the exec commands are\n> inserted. The same applies to the next couple of patches\n> \n\nGood question.\n\nThis function inserts an `exec' command after every `pick' command.\nThese commands are stored in a dynamically allocated list, grew with\nALLOW_GROW().\n\nIf we want to keep the current structure, we would have to grow the size\nof the list by 1 and move several element to the end every time we want\nto add an `exec' command.  It would not be very effective.  Perhaps I\nshould use a linked list here, instead.  It may also work well with\nrearrange_squash() and skip_unnecessary_picks().\n\nMaybe we could even get rid of the strbuf at some point.\n\n> Best Wishes\n> \n> Phillip\n> \n\nCheers,\nAlban\n\n"},{"id":"360194","messageId":"d14281c1-6beb-50fe-e9aa-3d8555aa307d@gmail.com","threadId":"49504","inReplyTo":"d0adf861-3046-f0b6-3217-c89d92319e43@talktalk.net","subject":"Re: [PATCH 08/15] sequencer: change complete_action() to use the refactored functions","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-11T17:06:22Z","receivedAt":"2018-10-11T17:06:27Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Le 11/10/2018 à 15:51, Phillip Wood a écrit :\n> On 07/10/2018 20:54, Alban Gruin wrote:\n>> +    if (rewrite_file(todo_file, new_todo.buf.buf, new_todo.buf.len) <\n>> 0) {\n>> +        todo_list_release(&new_todo);\n>> +        return error_errno(_(\"could not write '%s'\"), todo_file);\n>> +    }\n> \n> rewrite_file() can truncate the old version of the file if there is an\n> error when writing the new version, I think it would be better to use\n> write_message() instead as that atomically updates the file. The same\n> applies to patch 5 (refactor rearrange_squash()) after which I think\n> there will be no callers to rewrite_file() so it can be deleted.\n\nYou’re right, I didn’t notice that.\n\n> \n> Best Wishes\n> \n> Phillip\n> \n\nCheers,\nAlban\n\n"},{"id":"360202","messageId":"60846277-93f6-1514-26d8-2878be7b8f1c@gmail.com","threadId":"49504","inReplyTo":"f95102e3-3968-361f-2e2a-a22989519480@talktalk.net","subject":"Re: [PATCH 10/15] rebase-interactive: use todo_list_transform() in edit_todo_list()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-11T19:58:49Z","receivedAt":"2018-10-11T19:58:55Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Le 11/10/2018 à 17:16, Phillip Wood a écrit :\n> On 07/10/2018 20:54, Alban Gruin wrote:\n>> Just like complete_action(), edit_todo_list() used a\n>> function (transform_todo_file()) that read the todo-list from the disk\n>> and wrote it back, resulting in useless disk accesses.\n>>\n>> This changes edit_todo_list() to call directly todo_list_transform()\n>> instead.\n>>\n>> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n>> ---\n>>   rebase-interactive.c | 40 +++++++++++++++++++---------------------\n>>   1 file changed, 19 insertions(+), 21 deletions(-)\n>>\n>> diff --git a/rebase-interactive.c b/rebase-interactive.c\n>> index 7c7f720a3d..f42d48e192 100644\n>> --- a/rebase-interactive.c\n>> +++ b/rebase-interactive.c\n>> @@ -78,39 +78,37 @@ void append_todo_help(unsigned edit_todo, unsigned\n>> keep_empty,\n>>     int edit_todo_list(unsigned flags)\n>>   {\n>> -    struct strbuf buf = STRBUF_INIT;\n>>       const char *todo_file = rebase_path_todo();\n>> +    struct todo_list todo_list = TODO_LIST_INIT;\n>> +    int res = 0;\n>>   -    if (strbuf_read_file(&buf, todo_file, 0) < 0)\n>> +    if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>>           return error_errno(_(\"could not read '%s'.\"), todo_file);\n>>   -    strbuf_stripspace(&buf, 1);\n>> -    if (write_message(buf.buf, buf.len, todo_file, 0)) {\n>> -        strbuf_release(&buf);\n>> -        return -1;\n>> -    }\n>> -\n>> -    strbuf_release(&buf);\n>> +    strbuf_stripspace(&todo_list.buf, 1);\n>> +    if (!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list))\n>> +        todo_list_transform(&todo_list, flags | TODO_LIST_SHORTEN_IDS);\n>>   -    transform_todo_file(flags | TODO_LIST_SHORTEN_IDS);\n>> -\n>> -    if (strbuf_read_file(&buf, todo_file, 0) < 0)\n>> -        return error_errno(_(\"could not read '%s'.\"), todo_file);\n>> +    append_todo_help(1, 0, &todo_list.buf);\n>>   -    append_todo_help(1, 0, &buf);\n> \n> I think this patch is fine, I was just wondering if you meant to move\n> the call to append_todo_help() above the blank line?\n> \n\nNo\n\n> Best Wishes\n> \n> Phillip\n> \n\nCheers,\nAlban\n\n"},{"id":"360291","messageId":"10482114-7954-2d1d-1612-f2b559d4ca8f@talktalk.net","threadId":"49504","inReplyTo":"6c55857a-ce3c-f9fc-18c6-d7cef2976931@gmail.com","subject":"Re: [PATCH 04/15] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2018-10-12T09:54:15Z","receivedAt":"2018-10-12T09:54:21Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 11/10/2018 17:57, Alban Gruin wrote:\n> Hi Phillip,\n> \n> thanks for taking the time to review my patches.\n> \n> Le 11/10/2018 à 13:25, Phillip Wood a écrit :\n>> On 07/10/2018 20:54, Alban Gruin wrote:\n>>> @@ -4419,15 +4406,38 @@ int sequencer_add_exec_commands(const char\n>>> *commands)\n>>>       }\n>>>         /* insert or append final <commands> */\n>>> -    if (insert >= 0 && insert < todo_list.nr)\n>>> -        strbuf_insert(buf, todo_list.items[insert].offset_in_buf +\n>>> +    if (insert >= 0 && insert < todo_list->nr)\n>>> +        strbuf_insert(buf, todo_list->items[insert].offset_in_buf +\n>>>                     offset, commands, commands_len);\n>>>       else if (insert >= 0 || !offset)\n>>>           strbuf_add(buf, commands, commands_len);\n>>>   -    i = write_message(buf->buf, buf->len, todo_file, 0);\n>>> +    if (todo_list_parse_insn_buffer(buf->buf, todo_list))\n>>> +        BUG(\"unusable todo list\");}\n>>\n>> It is a shame to have to re-parse the todo list, I wonder how difficult\n>> it would be to adjust the todo_list item array as the exec commands are\n>> inserted. The same applies to the next couple of patches\n>>\n> \n> Good question.\n> \n> This function inserts an `exec' command after every `pick' command.\n> These commands are stored in a dynamically allocated list, grew with\n> ALLOW_GROW().\n> \n> If we want to keep the current structure, we would have to grow the size\n> of the list by 1 and move several element to the end every time we want\n> to add an `exec' command.  It would not be very effective.  Perhaps I\n> should use a linked list here, instead.  It may also work well with\n> rearrange_squash() and skip_unnecessary_picks().\n> \n> Maybe we could even get rid of the strbuf at some point.\n\nAnother way would be to use the strbuf as a string pool rather than a\ncopy of the text of the file. There could be a write_todo_list()\nfunction that takes a todo list and some flags, iterates over the items\nin the todo list and writes the file. The flags would specify whether to\nappend the todo help and whether to abbreviate the object ids (so there\nis no need for a separate call to transform_todos()). Then\nadd_exec_commands() could allocate a new array of todo items which it\nbuilds up as it works through the original list and replaces the\noriginal list with the new one at the end. The text of the exec items\ncan be added to the end of the strbuf (we only need one copy of the exec\ntext with this scheme). rearrange_squash() can use a temporary array to\nbuild a new list as well or just memmove() things but that might be\nslower if there is a lot of rearrangement to do. skip_unecessary_picks()\ncould just set the current item to the one we want to start with.\n\nBest Wishes\n\nPhillip\n\n> \n>> Best Wishes\n>>\n>> Phillip\n>>\n> \n> Cheers,\n> Alban\n> \n\n"},{"id":"360294","messageId":"20181012100101.GE19800@szeder.dev","threadId":"49504","inReplyTo":"20181007195418.25752-8-alban.gruin@gmail.com","subject":"Re: [PATCH 07/15] sequencer: make sequencer_make_script() write its script to a strbuf","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2018-10-12T10:01:01Z","receivedAt":"2018-10-12T10:01:10Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Sun, Oct 07, 2018 at 09:54:10PM +0200, Alban Gruin wrote:\n\n> diff --git a/sequencer.c b/sequencer.c\n> index 30a7fe3958..dfb8d1c974 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -4083,7 +4083,7 @@ static const char *label_oid(struct object_id *oid, const char *label,\n>  }\n>  \n>  static int make_script_with_merges(struct pretty_print_context *pp,\n> -\t\t\t\t   struct rev_info *revs, FILE *out,\n> +\t\t\t\t   struct rev_info *revs, struct strbuf *out,\n>  \t\t\t\t   unsigned flags)\n>  {\n>  \tint keep_empty = flags & TODO_LIST_KEEP_EMPTY;\n> @@ -4230,7 +4230,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n>  \t * gathering commits not yet shown, reversing the list on the fly,\n>  \t * then outputting that list (labeling revisions as needed).\n>  \t */\n> -\tfprintf(out, \"%s onto\\n\", cmd_label);\n> +\tstrbuf_addf(out, \"%s onto\\n\", cmd_label);\n>  \tfor (iter = tips; iter; iter = iter->next) {\n>  \t\tstruct commit_list *list = NULL, *iter2;\n>  \n> @@ -4240,9 +4240,9 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n>  \t\tentry = oidmap_get(&state.commit2label, &commit->object.oid);\n>  \n>  \t\tif (entry)\n> -\t\t\tfprintf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n> +\t\t\tstrbuf_addf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n>  \t\telse\n> -\t\t\tfprintf(out, \"\\n\");\n> +\t\t\tstrbuf_addf(out, \"\\n\");\n\nPlease use plain strbuf_add() here.\n\nOr strbuf_complete_line()?  Dunno, as seen in the previous hunk, 'out'\nwon't be empty at this point.\n\n"},{"id":"360299","messageId":"1861868.piMsbFIpbn@andromeda","threadId":"49504","inReplyTo":"10482114-7954-2d1d-1612-f2b559d4ca8f@talktalk.net","subject":"Re: [PATCH 04/15] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-12T12:23:32Z","receivedAt":"2018-10-12T12:23:44Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Le 12/10/2018 à 11:54, Phillip Wood a écrit :\n> On 11/10/2018 17:57, Alban Gruin wrote:\n> > Hi Phillip,\n> > \n> > thanks for taking the time to review my patches.\n> > \n> > Le 11/10/2018 à 13:25, Phillip Wood a écrit :\n> >> On 07/10/2018 20:54, Alban Gruin wrote:\n> >>> @@ -4419,15 +4406,38 @@ int sequencer_add_exec_commands(const char\n> >>> *commands)\n> >>>       }\n> >>>         /* insert or append final <commands> */\n> >>> -    if (insert >= 0 && insert < todo_list.nr)\n> >>> -        strbuf_insert(buf, todo_list.items[insert].offset_in_buf +\n> >>> +    if (insert >= 0 && insert < todo_list->nr)\n> >>> +        strbuf_insert(buf, todo_list->items[insert].offset_in_buf +\n> >>>                     offset, commands, commands_len);\n> >>>       else if (insert >= 0 || !offset)\n> >>>           strbuf_add(buf, commands, commands_len);\n> >>>   -    i = write_message(buf->buf, buf->len, todo_file, 0);\n> >>> +    if (todo_list_parse_insn_buffer(buf->buf, todo_list))\n> >>> +        BUG(\"unusable todo list\");}\n> >> \n> >> It is a shame to have to re-parse the todo list, I wonder how difficult\n> >> it would be to adjust the todo_list item array as the exec commands are\n> >> inserted. The same applies to the next couple of patches\n> > \n> > Good question.\n> > \n> > This function inserts an `exec' command after every `pick' command.\n> > These commands are stored in a dynamically allocated list, grew with\n> > ALLOW_GROW().\n> > \n> > If we want to keep the current structure, we would have to grow the size\n> > of the list by 1 and move several element to the end every time we want\n> > to add an `exec' command.  It would not be very effective.  Perhaps I\n> > should use a linked list here, instead.  It may also work well with\n> > rearrange_squash() and skip_unnecessary_picks().\n> > \n> > Maybe we could even get rid of the strbuf at some point.\n> \n> Another way would be to use the strbuf as a string pool rather than a\n> copy of the text of the file. There could be a write_todo_list()\n> function that takes a todo list and some flags, iterates over the items\n> in the todo list and writes the file. The flags would specify whether to\n> append the todo help and whether to abbreviate the object ids (so there\n> is no need for a separate call to transform_todos()). Then\n> add_exec_commands() could allocate a new array of todo items which it\n> builds up as it works through the original list and replaces the\n> original list with the new one at the end. The text of the exec items\n> can be added to the end of the strbuf (we only need one copy of the exec\n> text with this scheme). rearrange_squash() can use a temporary array to\n> build a new list as well or just memmove() things but that might be\n> slower if there is a lot of rearrangement to do. skip_unecessary_picks()\n> could just set the current item to the one we want to start with.\n> \n\nThis sounds good, and it looks like the solution dscho proposed on IRC a few \nhours ago[0].  I will try to do this.\n\n[0] http://colabti.org/irclogger/irclogger_log/git-devel?date=2018-10-12#l46\n\nCheers,\nAlban\n\n\n\n\n"},{"id":"360904","messageId":"xmqqzhvaz6z5.fsf@gitster-ct.c.googlers.com","threadId":"49504","inReplyTo":"20181012100101.GE19800@szeder.dev","subject":"Re: [PATCH 07/15] sequencer: make sequencer_make_script() write its script to a strbuf","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-10-19T08:16:46Z","receivedAt":"2018-10-19T08:16:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"SZEDER Gábor <szeder.dev@gmail.com> writes:\n\n>>  \t\tif (entry)\n>> -\t\t\tfprintf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n>> +\t\t\tstrbuf_addf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n>>  \t\telse\n>> -\t\t\tfprintf(out, \"\\n\");\n>> +\t\t\tstrbuf_addf(out, \"\\n\");\n>\n> Please use plain strbuf_add() here.\n\nFWIW, contrib/coccinelle/strbuf.cocci.patch gave us this:\n\ndiff -u -p a/sequencer.c b/sequencer.c\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4311,7 +4311,7 @@ static int make_script_with_merges(struc\n \t\tif (entry)\n \t\t\tstrbuf_addf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n \t\telse\n-\t\t\tstrbuf_addf(out, \"\\n\");\n+\t\t\tstrbuf_addstr(out, \"\\n\");\n \n \t\twhile (oidset_contains(&interesting, &commit->object.oid) &&\n \t\t       !oidset_contains(&shown, &commit->object.oid)) {\n"},{"id":"360909","messageId":"20181019092704.GW19800@szeder.dev","threadId":"49504","inReplyTo":"xmqqzhvaz6z5.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 07/15] sequencer: make sequencer_make_script() write its script to a strbuf","fromName":"SZEDER Gábor","fromEmail":"szeder.dev@gmail.com","sentAt":"2018-10-19T09:27:04Z","receivedAt":"2018-10-19T09:27:10Z","isPatch":true,"sender":{"key":"szeder.dev@gmail.com","avatar":"https://avatars.githubusercontent.com/u/116324?v=4"},"body":"On Fri, Oct 19, 2018 at 05:16:46PM +0900, Junio C Hamano wrote:\n> SZEDER Gábor <szeder.dev@gmail.com> writes:\n> \n> >>  \t\tif (entry)\n> >> -\t\t\tfprintf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n> >> +\t\t\tstrbuf_addf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n> >>  \t\telse\n> >> -\t\t\tfprintf(out, \"\\n\");\n> >> +\t\t\tstrbuf_addf(out, \"\\n\");\n> >\n> > Please use plain strbuf_add() here.\n> \n> FWIW, contrib/coccinelle/strbuf.cocci.patch gave us this:\n> \n> diff -u -p a/sequencer.c b/sequencer.c\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -4311,7 +4311,7 @@ static int make_script_with_merges(struc\n>  \t\tif (entry)\n>  \t\t\tstrbuf_addf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n>  \t\telse\n> -\t\t\tstrbuf_addf(out, \"\\n\");\n> +\t\t\tstrbuf_addstr(out, \"\\n\");\n>  \n>  \t\twhile (oidset_contains(&interesting, &commit->object.oid) &&\n>  \t\t       !oidset_contains(&shown, &commit->object.oid)) {\n\nUh, right.  I didn't want to copy-paste a patch with too long lines\ninto my mailer, as it usually doesn't end too good, so I just typed\nthe function name.  Evidently I couldn't quite manage.\n\n"},{"id":"361804","messageId":"20181027212930.9303-1-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181007195418.25752-1-alban.gruin@gmail.com","subject":"[PATCH v2 00/16] sequencer: refactor functions working on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-27T21:29:14Z","receivedAt":"2018-10-27T21:30:04Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"At the center of the \"interactive\" part of the interactive rebase lies\nthe todo list.  When the user starts an interactive rebase, a todo list\nis generated, presented to the user (who then edits it using a text\neditor), read back, and then is checked and processed before the actual\nrebase takes place.\n\nSome of this processing includes adding execs commands, reordering\nfixup! and squash! commits, and checking if no commits were accidentally\ndropped by the user.\n\nBefore I converted the interactive rebase in C, these functions were\ncalled by git-rebase--interactive.sh through git-rebase--helper.  Since\nthe only way to pass around a large amount of data between a shell\nscript and a C program is to use a file (or any declination of a file),\nthe functions that checked and processed the todo list were directly\nworking on a file, the same file that the user edited.\n\nDuring the conversion, I did not address this issue, which lead to a\ncomplete_action() that reads the todo list file, does some computation\nbased on its content, and writes it back to the disk, several times in\nthe same function.\n\nAs it is not an efficient way to handle a data structure, this patch\nseries refactor the functions that processes the todo list to work on a\ntodo_list structure instead of reading it from the disk.\n\nSome commits consists in modifying edit_todo_list() (initially used by\n--edit-todo) to handle the initial edition of the todo list, to increase\ncode sharing.\n\nIt is based onto ag/rebase-i-in-c (34b4731, \"rebase -i: move\nrebase--helper modes to rebase--interactive\").\n\nChanges since v1:\n\n - When a line is invalid, parse_insn_buffer() sets the type of the\n   corresponding command to a garbage value instead of `noop', and its\n   argument is defined properly.\n\n - todo_list_add_exec_commands(), todo_list_rearrange_squash(),\n   skip_unnecessary_picks() don’t reparse the todo list after processing\n   them.  Instead, they recreate a new item list.\n\n - Due to the previous change, a todo list buffer can’t directly be\n   written to the disk.  A new function, todo_list_write_to_disk(), is\n   introduced to take care of this task.\n\n - rewrite_file() has been deleted.\n\n - A call to strbuf_addf(&buf, \"\\n\"); has been replaced by strbuf_addch(…).\n\n - complete_action() and todo_list_check() expect that their input todo list\n   have already been parsed.\n\n - complete_action() no longer writes \"noop\\n\" to the todo list buffer\n   if it is empty.  Instead, it appends a `noop' command to the item\n   list.\n\nAlban Gruin (16):\n  sequencer: changes in parse_insn_buffer()\n  sequencer: make the todo_list structure public\n  sequencer: refactor transform_todos() to work on a todo_list\n  sequencer: introduce todo_list_write_to_file()\n  sequencer: refactor check_todo_list() to work on a todo_list\n  sequencer: refactor sequencer_add_exec_commands() to work on a\n    todo_list\n  sequencer: refactor rearrange_squash() to work on a todo_list\n  sequencer: make sequencer_make_script() write its script to a strbuf\n  sequencer: change complete_action() to use the refactored functions\n  sequencer: refactor skip_unnecessary_picks() to work on a todo_list\n  rebase-interactive: use todo_list_write_to_file() in edit_todo_list()\n  rebase-interactive: append_todo_help() changes\n  rebase-interactive: rewrite edit_todo_list() to handle the initial\n    edit\n  sequencer: use edit_todo_list() in complete_action()\n  sequencer: fix a call to error() in transform_todo_file()\n  rebase--interactive: move transform_todo_file() to\n    rebase--interactive.c\n\n builtin/rebase--interactive.c |  68 +++-\n rebase-interactive.c          | 142 ++++++--\n rebase-interactive.h          |   8 +-\n sequencer.c                   | 589 +++++++++++++---------------------\n sequencer.h                   |  68 +++-\n 5 files changed, 455 insertions(+), 420 deletions(-)\n\n-- \n2.19.1\n\n"},{"id":"361805","messageId":"20181027212930.9303-2-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"[PATCH v2 01/16] sequencer: changes in parse_insn_buffer()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-27T21:29:15Z","receivedAt":"2018-10-27T21:30:04Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This clears the number of items of a todo_list before parsing it to\nallow to parse the same list multiple times without issues.  As its\nitems are not dynamically allocated, or don’t need to allocate memory,\nno additionnal memory management is required here.\n\nFurthermore, if a line is invalid, the type of the corresponding\ncommand is set to a garbage value, and its argument is defined properly.\nThis will allow to recreate the text of a todo list from its commands,\neven if one of them is incorrect.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 8dd6db5a01..9c8bd3f632 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2023,6 +2023,8 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n \tchar *p = buf, *next_p;\n \tint i, res = 0, fixup_okay = file_exists(rebase_path_done());\n \n+\ttodo_list->current = todo_list->nr = 0;\n+\n \tfor (i = 1; *p; i++, p = next_p) {\n \t\tchar *eol = strchrnul(p, '\\n');\n \n@@ -2036,7 +2038,10 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n \t\tif (parse_insn_line(item, p, eol)) {\n \t\t\tres = error(_(\"invalid line %d: %.*s\"),\n \t\t\t\ti, (int)(eol - p), p);\n-\t\t\titem->command = TODO_NOOP;\n+\t\t\titem->command = TODO_COMMENT + 1;\n+\t\t\titem->arg = p;\n+\t\t\titem->arg_len = (int)(eol - p);\n+\t\t\titem->commit = NULL;\n \t\t}\n \n \t\tif (fixup_okay)\n-- \n2.19.1\n\n"},{"id":"361806","messageId":"20181027212930.9303-3-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"[PATCH v2 02/16] sequencer: make the todo_list structure public","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-27T21:29:16Z","receivedAt":"2018-10-27T21:30:04Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This makes the structures todo_list and todo_item, and the functions\ntodo_list_release() and parse_insn_buffer(), accessible outside of\nsequencer.c.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v1.\n\n sequencer.c | 66 +++++++++--------------------------------------------\n sequencer.h | 48 ++++++++++++++++++++++++++++++++++++++\n 2 files changed, 59 insertions(+), 55 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 9c8bd3f632..f791729271 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1401,31 +1401,6 @@ static int allow_empty(struct replay_opts *opts, struct commit *commit)\n \t\treturn 1;\n }\n \n-/*\n- * Note that ordering matters in this enum. Not only must it match the mapping\n- * below, it is also divided into several sections that matter.  When adding\n- * new commands, make sure you add it in the right section.\n- */\n-enum todo_command {\n-\t/* commands that handle commits */\n-\tTODO_PICK = 0,\n-\tTODO_REVERT,\n-\tTODO_EDIT,\n-\tTODO_REWORD,\n-\tTODO_FIXUP,\n-\tTODO_SQUASH,\n-\t/* commands that do something else than handling a single commit */\n-\tTODO_EXEC,\n-\tTODO_LABEL,\n-\tTODO_RESET,\n-\tTODO_MERGE,\n-\t/* commands that do nothing but are counted for reporting progress */\n-\tTODO_NOOP,\n-\tTODO_DROP,\n-\t/* comments (not counted for reporting progress) */\n-\tTODO_COMMENT\n-};\n-\n static struct {\n \tchar c;\n \tconst char *str;\n@@ -1897,26 +1872,7 @@ enum todo_item_flags {\n \tTODO_EDIT_MERGE_MSG = 1\n };\n \n-struct todo_item {\n-\tenum todo_command command;\n-\tstruct commit *commit;\n-\tunsigned int flags;\n-\tconst char *arg;\n-\tint arg_len;\n-\tsize_t offset_in_buf;\n-};\n-\n-struct todo_list {\n-\tstruct strbuf buf;\n-\tstruct todo_item *items;\n-\tint nr, alloc, current;\n-\tint done_nr, total_nr;\n-\tstruct stat_data stat;\n-};\n-\n-#define TODO_LIST_INIT { STRBUF_INIT }\n-\n-static void todo_list_release(struct todo_list *todo_list)\n+void todo_list_release(struct todo_list *todo_list)\n {\n \tstrbuf_release(&todo_list->buf);\n \tFREE_AND_NULL(todo_list->items);\n@@ -2017,7 +1973,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \treturn !item->commit;\n }\n \n-static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n+int todo_list_parse_insn_buffer(char *buf, struct todo_list *todo_list)\n {\n \tstruct todo_item *item;\n \tchar *p = buf, *next_p;\n@@ -2115,7 +2071,7 @@ static int read_populate_todo(struct todo_list *todo_list,\n \t\treturn error(_(\"could not stat '%s'\"), todo_file);\n \tfill_stat_data(&todo_list->stat, &st);\n \n-\tres = parse_insn_buffer(todo_list->buf.buf, todo_list);\n+\tres = todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list);\n \tif (res) {\n \t\tif (is_rebase_i(opts))\n \t\t\treturn error(_(\"please fix this using \"\n@@ -2146,7 +2102,7 @@ static int read_populate_todo(struct todo_list *todo_list,\n \t\tFILE *f = fopen_or_warn(rebase_path_msgtotal(), \"w\");\n \n \t\tif (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&\n-\t\t\t\t!parse_insn_buffer(done.buf.buf, &done))\n+\t\t\t\t!todo_list_parse_insn_buffer(done.buf.buf, &done))\n \t\t\ttodo_list->done_nr = count_commands(&done);\n \t\telse\n \t\t\ttodo_list->done_nr = 0;\n@@ -4276,7 +4232,7 @@ int sequencer_add_exec_commands(const char *commands)\n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4311,7 +4267,7 @@ int transform_todos(unsigned flags)\n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4397,7 +4353,7 @@ int check_todo_list(void)\n \t\tgoto leave_check;\n \t}\n \tadvise_to_edit_todo = res =\n-\t\tparse_insn_buffer(todo_list.buf.buf, &todo_list);\n+\t\ttodo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n \n \tif (res || check_level == MISSING_COMMIT_CHECK_IGNORE)\n \t\tgoto leave_check;\n@@ -4416,7 +4372,7 @@ int check_todo_list(void)\n \t\tgoto leave_check;\n \t}\n \tstrbuf_release(&todo_file);\n-\tres = !!parse_insn_buffer(todo_list.buf.buf, &todo_list);\n+\tres = !!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n \n \t/* Find commits in git-rebase-todo.backup yet unseen */\n \tfor (i = todo_list.nr - 1; i >= 0; i--) {\n@@ -4498,7 +4454,7 @@ static int skip_unnecessary_picks(struct object_id *output_oid)\n \n \tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n \t\treturn -1;\n-\tif (parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n@@ -4586,7 +4542,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(buf->buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(buf->buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4694,7 +4650,7 @@ int rearrange_squash(void)\n \n \tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n \t\treturn -1;\n-\tif (parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex aab280f276..cc69473bc2 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -67,6 +67,54 @@ enum missing_commit_check_level {\n int write_message(const void *buf, size_t len, const char *filename,\n \t\t  int append_eol);\n \n+/*\n+ * Note that ordering matters in this enum. Not only must it match the mapping\n+ * of todo_command_info (in sequencer.c), it is also divided into several\n+ * sections that matter.  When adding new commands, make sure you add it in the\n+ * right section.\n+ */\n+enum todo_command {\n+\t/* commands that handle commits */\n+\tTODO_PICK = 0,\n+\tTODO_REVERT,\n+\tTODO_EDIT,\n+\tTODO_REWORD,\n+\tTODO_FIXUP,\n+\tTODO_SQUASH,\n+\t/* commands that do something else than handling a single commit */\n+\tTODO_EXEC,\n+\tTODO_LABEL,\n+\tTODO_RESET,\n+\tTODO_MERGE,\n+\t/* commands that do nothing but are counted for reporting progress */\n+\tTODO_NOOP,\n+\tTODO_DROP,\n+\t/* comments (not counted for reporting progress) */\n+\tTODO_COMMENT\n+};\n+\n+struct todo_item {\n+\tenum todo_command command;\n+\tstruct commit *commit;\n+\tunsigned int flags;\n+\tconst char *arg;\n+\tint arg_len;\n+\tsize_t offset_in_buf;\n+};\n+\n+struct todo_list {\n+\tstruct strbuf buf;\n+\tstruct todo_item *items;\n+\tint nr, alloc, current;\n+\tint done_nr, total_nr;\n+\tstruct stat_data stat;\n+};\n+\n+#define TODO_LIST_INIT { STRBUF_INIT }\n+\n+int todo_list_parse_insn_buffer(char *buf, struct todo_list *todo_list);\n+void todo_list_release(struct todo_list *todo_list);\n+\n /* Call this to setup defaults before parsing command line options */\n void sequencer_init_config(struct replay_opts *opts);\n int sequencer_pick_revisions(struct replay_opts *opts);\n-- \n2.19.1\n\n"},{"id":"361807","messageId":"20181027212930.9303-4-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"[PATCH v2 03/16] sequencer: refactor transform_todos() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-27T21:29:17Z","receivedAt":"2018-10-27T21:30:04Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors transform_todos() to work on a todo_list.  The function\nis renamed todo_list_transform().\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, transform_todo_file().  It is still used by\ncomplete_action() and edit_todo_list() for now, but they will be\nreplaced in a future commit.\n\ntodo_list_transform() is not a static function, because it will be used\nby edit_todo_list() from rebase-interactive.c in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |  2 +-\n rebase-interactive.c          |  4 +--\n sequencer.c                   | 46 +++++++++++++++++++++++------------\n sequencer.h                   |  3 ++-\n 4 files changed, 35 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex a2ab68ed06..abdf6126df 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -252,7 +252,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t}\n \tcase SHORTEN_OIDS:\n \tcase EXPAND_OIDS:\n-\t\tret = transform_todos(flags);\n+\t\tret = transform_todo_file(flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n \t\tret = check_todo_list();\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 0f4119cbae..49f2f549e1 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -68,7 +68,7 @@ int edit_todo_list(unsigned flags)\n \n \tstrbuf_release(&buf);\n \n-\ttransform_todos(flags | TODO_LIST_SHORTEN_IDS);\n+\ttransform_todo_file(flags | TODO_LIST_SHORTEN_IDS);\n \n \tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n@@ -84,7 +84,7 @@ int edit_todo_list(unsigned flags)\n \tif (launch_sequence_editor(todo_file, NULL, NULL))\n \t\treturn -1;\n \n-\ttransform_todos(flags & ~(TODO_LIST_SHORTEN_IDS));\n+\ttransform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS));\n \n \treturn 0;\n }\ndiff --git a/sequencer.c b/sequencer.c\nindex f791729271..07296f1f57 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4256,23 +4256,13 @@ int sequencer_add_exec_commands(const char *commands)\n \treturn i;\n }\n \n-int transform_todos(unsigned flags)\n+void todo_list_transform(struct todo_list *todo_list, unsigned flags)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct todo_item *item;\n \tint i;\n \n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tfor (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {\n+\tfor (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n \t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len, item->arg);\n@@ -4308,9 +4298,33 @@ int transform_todos(unsigned flags)\n \t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len, item->arg);\n \t}\n \n-\ti = write_message(buf.buf, buf.len, todo_file, 0);\n+\tstrbuf_reset(&todo_list->buf);\n+\tstrbuf_add(&todo_list->buf, buf.buf, buf.len);\n+\tstrbuf_release(&buf);\n+\n+\tif (todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list))\n+\t\tBUG(\"unusable todo list\");\n+}\n+\n+int transform_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_transform(&todo_list, flags);\n+\n+\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n \ttodo_list_release(&todo_list);\n-\treturn i;\n+\treturn res;\n }\n \n enum missing_commit_check_level get_missing_commit_check_level(void)\n@@ -4571,7 +4585,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n \t\t\t     rebase_path_todo_backup());\n \n-\tif (transform_todos(flags | TODO_LIST_SHORTEN_IDS))\n+\tif (transform_todo_file(flags | TODO_LIST_SHORTEN_IDS))\n \t\treturn error(_(\"could not transform the todo list\"));\n \n \tstrbuf_reset(buf);\n@@ -4600,7 +4614,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t\treturn -1;\n \t}\n \n-\tif (transform_todos(flags & ~(TODO_LIST_SHORTEN_IDS)))\n+\tif (transform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS)))\n \t\treturn error(_(\"could not transform the todo list\"));\n \n \tif (opts->allow_ff && skip_unnecessary_picks(&oid))\ndiff --git a/sequencer.h b/sequencer.h\nindex cc69473bc2..029d842ac5 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -113,6 +113,7 @@ struct todo_list {\n #define TODO_LIST_INIT { STRBUF_INIT }\n \n int todo_list_parse_insn_buffer(char *buf, struct todo_list *todo_list);\n+void todo_list_transform(struct todo_list *todo_list, unsigned flags);\n void todo_list_release(struct todo_list *todo_list);\n \n /* Call this to setup defaults before parsing command line options */\n@@ -136,7 +137,7 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(const char *command);\n-int transform_todos(unsigned flags);\n+int transform_todo_file(unsigned flags);\n enum missing_commit_check_level get_missing_commit_check_level(void);\n int check_todo_list(void);\n int complete_action(struct replay_opts *opts, unsigned flags,\n-- \n2.19.1\n\n"},{"id":"361808","messageId":"20181027212930.9303-5-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"[PATCH v2 04/16] sequencer: introduce todo_list_write_to_file()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-27T21:29:18Z","receivedAt":"2018-10-27T21:30:04Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This introduce a new function to recreate the text of a todo list from\nits commands, and then to write it to the disk.  This will be useful in\nthe future, the buffer of a todo list won’t be treated as a strict\nmirror of the todo file by some of its functions once they will be\nrefactored.\n\nThis functionnality can already be found in todo_list_transform(), but\nit is specifically made to replace the buffer of a todo list, which is\nnot the desired behaviour.  Thus, the part of todo_list_transform() that\nactually creates the buffer is moved to a new function,\ntodo_list_to_strbuf().  The rest is unused, and so is dropped.\n\ntodo_list_write_to_file() can also take care to append the help text to\nthe buffer before writing it to the disk, or to write only the first n\nitems of the list.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 59 ++++++++++++++++++++++++++++++++++++-----------------\n sequencer.h |  4 +++-\n 2 files changed, 43 insertions(+), 20 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 07296f1f57..0dd45677b1 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4256,24 +4256,27 @@ int sequencer_add_exec_commands(const char *commands)\n \treturn i;\n }\n \n-void todo_list_transform(struct todo_list *todo_list, unsigned flags)\n+static void todo_list_to_strbuf(struct todo_list *todo_list, struct strbuf *buf,\n+\t\t\t\tint num, unsigned flags)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n \tstruct todo_item *item;\n-\tint i;\n+\tint i, max = todo_list->nr;\n \n-\tfor (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {\n+\tif (num > 0 && num < max)\n+\t\tmax = num;\n+\n+\tfor (item = todo_list->items, i = 0; i < max; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n-\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len, item->arg);\n+\t\t\tstrbuf_addf(buf, \"%.*s\\n\", item->arg_len, item->arg);\n \t\t\tcontinue;\n \t\t}\n \n \t\t/* add command to the buffer */\n \t\tif (flags & TODO_LIST_ABBREVIATE_CMDS)\n-\t\t\tstrbuf_addch(&buf, command_to_char(item->command));\n+\t\t\tstrbuf_addch(buf, command_to_char(item->command));\n \t\telse\n-\t\t\tstrbuf_addstr(&buf, command_to_string(item->command));\n+\t\t\tstrbuf_addstr(buf, command_to_string(item->command));\n \n \t\t/* add commit id */\n \t\tif (item->commit) {\n@@ -4283,27 +4286,46 @@ void todo_list_transform(struct todo_list *todo_list, unsigned flags)\n \n \t\t\tif (item->command == TODO_MERGE) {\n \t\t\t\tif (item->flags & TODO_EDIT_MERGE_MSG)\n-\t\t\t\t\tstrbuf_addstr(&buf, \" -c\");\n+\t\t\t\t\tstrbuf_addstr(buf, \" -c\");\n \t\t\t\telse\n-\t\t\t\t\tstrbuf_addstr(&buf, \" -C\");\n+\t\t\t\t\tstrbuf_addstr(buf, \" -C\");\n \t\t\t}\n \n-\t\t\tstrbuf_addf(&buf, \" %s\", oid);\n+\t\t\tstrbuf_addf(buf, \" %s\", oid);\n \t\t}\n \n \t\t/* add all the rest */\n \t\tif (!item->arg_len)\n-\t\t\tstrbuf_addch(&buf, '\\n');\n+\t\t\tstrbuf_addch(buf, '\\n');\n \t\telse\n-\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len, item->arg);\n+\t\t\tstrbuf_addf(buf, \" %.*s\\n\", item->arg_len, item->arg);\n \t}\n+}\n \n-\tstrbuf_reset(&todo_list->buf);\n-\tstrbuf_add(&todo_list->buf, buf.buf, buf.len);\n+int todo_list_write_to_file(struct todo_list *todo_list, const char *file,\n+\t\t\t    const char *shortrevisions, const char *shortonto,\n+\t\t\t    int command_count, int append_help, int num, unsigned flags)\n+{\n+\tint edit_todo = !(shortrevisions && shortonto), res;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\ttodo_list_to_strbuf(todo_list, &buf, num, flags);\n+\n+\tif (append_help) {\n+\t\tif (!edit_todo) {\n+\t\t\tstrbuf_addch(&buf, '\\n');\n+\t\t\tstrbuf_commented_addf(&buf, Q_(\"Rebase %s onto %s (%d command)\",\n+\t\t\t\t\t\t       \"Rebase %s onto %s (%d commands)\",\n+\t\t\t\t\t\t       command_count),\n+\t\t\t\t\t      shortrevisions, shortonto, command_count);\n+\t\t}\n+\t\tappend_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, &buf);\n+\t}\n+\n+\tres = write_message(buf.buf, buf.len, file, 0);\n \tstrbuf_release(&buf);\n \n-\tif (todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list))\n-\t\tBUG(\"unusable todo list\");\n+\treturn res;\n }\n \n int transform_todo_file(unsigned flags)\n@@ -4320,9 +4342,8 @@ int transform_todo_file(unsigned flags)\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n \n-\ttodo_list_transform(&todo_list, flags);\n-\n-\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n+\tres = todo_list_write_to_file(&todo_list, todo_file,\n+\t\t\t\t      NULL, NULL, 0, 0, -1, flags);\n \ttodo_list_release(&todo_list);\n \treturn res;\n }\ndiff --git a/sequencer.h b/sequencer.h\nindex 029d842ac5..a299c977fe 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -113,7 +113,9 @@ struct todo_list {\n #define TODO_LIST_INIT { STRBUF_INIT }\n \n int todo_list_parse_insn_buffer(char *buf, struct todo_list *todo_list);\n-void todo_list_transform(struct todo_list *todo_list, unsigned flags);\n+int todo_list_write_to_file(struct todo_list *todo_list, const char *file,\n+\t\t\t    const char *shortrevisions, const char *shortonto,\n+\t\t\t    int command_count, int append_help, int num, unsigned flags);\n void todo_list_release(struct todo_list *todo_list);\n \n /* Call this to setup defaults before parsing command line options */\n-- \n2.19.1\n\n"},{"id":"361809","messageId":"20181027212930.9303-6-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"[PATCH v2 05/16] sequencer: refactor check_todo_list() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-27T21:29:19Z","receivedAt":"2018-10-27T21:30:04Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors check_todo_list() to work on a todo_list to avoid\nredundant reads and writes to the disk.  The function is renamed\ntodo_list_check().  The parsing of the two todo lists is left to the\ncaller.\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, check_todo_list_from_file().  It reads the file\nfrom the disk, parses it, pass the todo_list to todo_list_check(), and\nwrites it back to the disk.\n\nAs get_missing_commit_check_level() and the enum\nmissing_commit_check_level are no longer needed inside of sequencer.c,\nthey are moved to rebase-interactive.c, and made static again.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |   2 +-\n rebase-interactive.c          |  90 ++++++++++++++++++++++++-\n rebase-interactive.h          |   1 +\n sequencer.c                   | 120 +++++++---------------------------\n sequencer.h                   |   9 +--\n 5 files changed, 115 insertions(+), 107 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex abdf6126df..c1d87c0fe6 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -255,7 +255,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = transform_todo_file(flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n-\t\tret = check_todo_list();\n+\t\tret = check_todo_list_from_file();\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n \t\tret = rearrange_squash();\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 49f2f549e1..9e81ecfe9f 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -1,8 +1,32 @@\n #include \"cache.h\"\n #include \"commit.h\"\n-#include \"rebase-interactive.h\"\n #include \"sequencer.h\"\n+#include \"rebase-interactive.h\"\n #include \"strbuf.h\"\n+#include \"commit-slab.h\"\n+#include \"config.h\"\n+\n+enum missing_commit_check_level {\n+\tMISSING_COMMIT_CHECK_IGNORE = 0,\n+\tMISSING_COMMIT_CHECK_WARN,\n+\tMISSING_COMMIT_CHECK_ERROR\n+};\n+\n+static enum missing_commit_check_level get_missing_commit_check_level(void)\n+{\n+\tconst char *value;\n+\n+\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n+\t\t\t!strcasecmp(\"ignore\", value))\n+\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n+\tif (!strcasecmp(\"warn\", value))\n+\t\treturn MISSING_COMMIT_CHECK_WARN;\n+\tif (!strcasecmp(\"error\", value))\n+\t\treturn MISSING_COMMIT_CHECK_ERROR;\n+\twarning(_(\"unrecognized setting %s for option \"\n+\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n+\treturn MISSING_COMMIT_CHECK_IGNORE;\n+}\n \n void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \t\t      struct strbuf *buf)\n@@ -88,3 +112,67 @@ int edit_todo_list(unsigned flags)\n \n \treturn 0;\n }\n+\n+define_commit_slab(commit_seen, unsigned char);\n+/*\n+ * Check if the user dropped some commits by mistake\n+ * Behaviour determined by rebase.missingCommitsCheck.\n+ * Check if there is an unrecognized command or a\n+ * bad SHA-1 in a command.\n+ */\n+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo)\n+{\n+\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n+\tstruct strbuf missing = STRBUF_INIT;\n+\tint res = 0, i;\n+\tstruct commit_seen commit_seen;\n+\n+\tinit_commit_seen(&commit_seen);\n+\n+\tif (check_level == MISSING_COMMIT_CHECK_IGNORE)\n+\t\tgoto leave_check;\n+\n+\t/* Mark the commits in git-rebase-todo as seen */\n+\tfor (i = 0; i < new_todo->nr; i++) {\n+\t\tstruct commit *commit = new_todo->items[i].commit;\n+\t\tif (commit)\n+\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t}\n+\n+\t/* Find commits in git-rebase-todo.backup yet unseen */\n+\tfor (i = old_todo->nr - 1; i >= 0; i--) {\n+\t\tstruct todo_item *item = old_todo->items + i;\n+\t\tstruct commit *commit = item->commit;\n+\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n+\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n+\t\t\t\t    find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV),\n+\t\t\t\t    item->arg_len, item->arg);\n+\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t\t}\n+\t}\n+\n+\t/* Warn about missing commits */\n+\tif (!missing.len)\n+\t\tgoto leave_check;\n+\n+\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n+\t\tres = 1;\n+\n+\tfprintf(stderr,\n+\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n+\t\t\"Dropped commits (newer to older):\\n\"));\n+\n+\t/* Make the list user-friendly and display */\n+\tfputs(missing.buf, stderr);\n+\tstrbuf_release(&missing);\n+\n+\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n+\t\t\"explicitly remove a commit.\\n\\n\"\n+\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n+\t\t\"the level of warnings.\\n\"\n+\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n+\n+leave_check:\n+\tclear_commit_seen(&commit_seen);\n+\treturn res;\n+}\ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 971da03776..6bc7bc315d 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -4,5 +4,6 @@\n void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \t\t      struct strbuf *buf);\n int edit_todo_list(unsigned flags);\n+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n \n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex 0dd45677b1..e12860c047 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4348,111 +4348,37 @@ int transform_todo_file(unsigned flags)\n \treturn res;\n }\n \n-enum missing_commit_check_level get_missing_commit_check_level(void)\n-{\n-\tconst char *value;\n-\n-\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n-\t\t\t!strcasecmp(\"ignore\", value))\n-\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n-\tif (!strcasecmp(\"warn\", value))\n-\t\treturn MISSING_COMMIT_CHECK_WARN;\n-\tif (!strcasecmp(\"error\", value))\n-\t\treturn MISSING_COMMIT_CHECK_ERROR;\n-\twarning(_(\"unrecognized setting %s for option \"\n-\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n-\treturn MISSING_COMMIT_CHECK_IGNORE;\n-}\n+static const char edit_todo_list_advice[] =\n+N_(\"You can fix this with 'git rebase --edit-todo' \"\n+\"and then run 'git rebase --continue'.\\n\"\n+\"Or you can abort the rebase with 'git rebase\"\n+\" --abort'.\\n\");\n \n-define_commit_slab(commit_seen, unsigned char);\n-/*\n- * Check if the user dropped some commits by mistake\n- * Behaviour determined by rebase.missingCommitsCheck.\n- * Check if there is an unrecognized command or a\n- * bad SHA-1 in a command.\n- */\n-int check_todo_list(void)\n+int check_todo_list_from_file(void)\n {\n-\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n-\tstruct strbuf todo_file = STRBUF_INIT;\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf missing = STRBUF_INIT;\n-\tint advise_to_edit_todo = 0, res = 0, i;\n-\tstruct commit_seen commit_seen;\n-\n-\tinit_commit_seen(&commit_seen);\n+\tstruct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;\n+\tint res = 0;\n \n-\tstrbuf_addstr(&todo_file, rebase_path_todo());\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n+\tif (strbuf_read_file_or_whine(&new_todo.buf, rebase_path_todo()) < 0) {\n \t\tres = -1;\n-\t\tgoto leave_check;\n-\t}\n-\tadvise_to_edit_todo = res =\n-\t\ttodo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n-\n-\tif (res || check_level == MISSING_COMMIT_CHECK_IGNORE)\n-\t\tgoto leave_check;\n-\n-\t/* Mark the commits in git-rebase-todo as seen */\n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tstruct commit *commit = todo_list.items[i].commit;\n-\t\tif (commit)\n-\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t\tgoto out;\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\tstrbuf_addstr(&todo_file, \".backup\");\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n+\tif (strbuf_read_file_or_whine(&old_todo.buf, rebase_path_todo_backup()) < 0) {\n \t\tres = -1;\n-\t\tgoto leave_check;\n-\t}\n-\tstrbuf_release(&todo_file);\n-\tres = !!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n-\n-\t/* Find commits in git-rebase-todo.backup yet unseen */\n-\tfor (i = todo_list.nr - 1; i >= 0; i--) {\n-\t\tstruct todo_item *item = todo_list.items + i;\n-\t\tstruct commit *commit = item->commit;\n-\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n-\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n-\t\t\t\t    short_commit_name(commit),\n-\t\t\t\t    item->arg_len, item->arg);\n-\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n-\t\t}\n+\t\tgoto out;\n \t}\n \n-\t/* Warn about missing commits */\n-\tif (!missing.len)\n-\t\tgoto leave_check;\n-\n-\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n-\t\tadvise_to_edit_todo = res = 1;\n-\n-\tfprintf(stderr,\n-\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n-\t\t\"Dropped commits (newer to older):\\n\"));\n-\n-\t/* Make the list user-friendly and display */\n-\tfputs(missing.buf, stderr);\n-\tstrbuf_release(&missing);\n-\n-\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n-\t\t\"explicitly remove a commit.\\n\\n\"\n-\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n-\t\t\"the level of warnings.\\n\"\n-\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n-\n-leave_check:\n-\tclear_commit_seen(&commit_seen);\n-\tstrbuf_release(&todo_file);\n-\ttodo_list_release(&todo_list);\n-\n-\tif (advise_to_edit_todo)\n-\t\tfprintf(stderr,\n-\t\t\t_(\"You can fix this with 'git rebase --edit-todo' \"\n-\t\t\t  \"and then run 'git rebase --continue'.\\n\"\n-\t\t\t  \"Or you can abort the rebase with 'git rebase\"\n-\t\t\t  \" --abort'.\\n\"));\n+\tres = todo_list_parse_insn_buffer(old_todo.buf.buf, &old_todo);\n+\tif (!res)\n+\t\tres = todo_list_parse_insn_buffer(new_todo.buf.buf, &new_todo);\n+\tif (!res)\n+\t\tres = todo_list_check(&old_todo, &new_todo);\n+\tif (res)\n+\t\tfprintf(stderr, _(edit_todo_list_advice));\n+out:\n+\ttodo_list_release(&old_todo);\n+\ttodo_list_release(&new_todo);\n \n \treturn res;\n }\n@@ -4630,7 +4556,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \n \ttodo_list_release(&todo_list);\n \n-\tif (check_todo_list()) {\n+\tif (check_todo_list_from_file()) {\n \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n \t\treturn -1;\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex a299c977fe..e203930084 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -58,12 +58,6 @@ struct replay_opts {\n };\n #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }\n \n-enum missing_commit_check_level {\n-\tMISSING_COMMIT_CHECK_IGNORE = 0,\n-\tMISSING_COMMIT_CHECK_WARN,\n-\tMISSING_COMMIT_CHECK_ERROR\n-};\n-\n int write_message(const void *buf, size_t len, const char *filename,\n \t\t  int append_eol);\n \n@@ -140,8 +134,7 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,\n \n int sequencer_add_exec_commands(const char *command);\n int transform_todo_file(unsigned flags);\n-enum missing_commit_check_level get_missing_commit_check_level(void);\n-int check_todo_list(void);\n+int check_todo_list_from_file(void);\n int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, const char *cmd,\n-- \n2.19.1\n\n"},{"id":"361810","messageId":"20181027212930.9303-7-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"[PATCH v2 06/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-27T21:29:20Z","receivedAt":"2018-10-27T21:30:04Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors sequencer_add_exec_commands() to work on a todo_list to\navoid redundant reads and writes to the disk.\n\nAn obvious way to do this would be to insert the `exec' command between\nthe other commands, and reparse it once this is done.  This is not what\nis done here.  Instead, the command is appended to the buffer once, and\na new list of items is created.  Items from the old list are copied to\nit, and new `exec' items are appended when necessary.  This eliminates\nthe need to reparse the todo list, but this also means its buffer cannot\nbe directly written to the disk, hence todo_list_write_to_disk().\n\nsequencer_add_exec_commands() still reads the todo list from the disk,\nas it is needed by rebase -p.  todo_list_add_exec_commands() works on a\ntodo_list structure, and reparses it at the end.\n\ncomplete_action() still uses sequencer_add_exec_commands() for now.\nThis will be changed in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 69 +++++++++++++++++++++++++++++++++++++----------------\n 1 file changed, 49 insertions(+), 20 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex e12860c047..12a3efeca8 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4216,6 +4216,50 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,\n \treturn 0;\n }\n \n+static void todo_list_add_exec_commands(struct todo_list *todo_list,\n+\t\t\t\t\tconst char *commands)\n+{\n+\tstruct strbuf *buf = &todo_list->buf;\n+\tconst char *old_buf = buf->buf;\n+\tsize_t commands_len = strlen(commands + strlen(\"exec \")) - 1;\n+\tint i, first = 1, nr = 0, alloc = 0;\n+\tstruct todo_item *items = NULL,\n+\t\tbase_item = {TODO_EXEC, NULL, 0, 0, commands_len, 0};\n+\n+\tstrbuf_addstr(buf, commands);\n+\tbase_item.offset_in_buf = buf->len - commands_len - 1;\n+\tbase_item.arg = buf->buf + base_item.offset_in_buf;\n+\n+\t/*\n+\t * Insert <commands> after every pick. Here, fixup/squash chains\n+\t * are considered part of the pick, so we insert the commands *after*\n+\t * those chains if there are any.\n+\t */\n+\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\tenum todo_command command = todo_list->items[i].command;\n+\t\tif (todo_list->items[i].arg)\n+\t\t\ttodo_list->items[i].arg = todo_list->items[i].arg - old_buf + buf->buf;\n+\n+\t\tif (command == TODO_PICK && !first) {\n+\t\t\tALLOC_GROW(items, nr + 1, alloc);\n+\t\t\tmemcpy(items + nr++, &base_item, sizeof(struct todo_item));\n+\t\t}\n+\n+\t\tALLOC_GROW(items, nr + 1, alloc);\n+\t\tmemcpy(items + nr++, todo_list->items + i, sizeof(struct todo_item));\n+\t\tfirst = 0;\n+\t}\n+\n+\t/* insert or append final <commands> */\n+\tALLOC_GROW(items, nr + 1, alloc);\n+\tmemcpy(items + nr++, &base_item, sizeof(struct todo_item));\n+\n+\tFREE_AND_NULL(todo_list->items);\n+\ttodo_list->items = items;\n+\ttodo_list->nr = nr;\n+\ttodo_list->alloc = alloc;\n+}\n+\n /*\n  * Add commands after pick and (series of) squash/fixup commands\n  * in the todo list.\n@@ -4224,10 +4268,7 @@ int sequencer_add_exec_commands(const char *commands)\n {\n \tconst char *todo_file = rebase_path_todo();\n \tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct todo_item *item;\n-\tstruct strbuf *buf = &todo_list.buf;\n-\tsize_t offset = 0, commands_len = strlen(commands);\n-\tint i, first;\n+\tint res;\n \n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n@@ -4237,23 +4278,11 @@ int sequencer_add_exec_commands(const char *commands)\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n \n-\tfirst = 1;\n-\t/* insert <commands> before every pick except the first one */\n-\tfor (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {\n-\t\tif (item->command == TODO_PICK && !first) {\n-\t\t\tstrbuf_insert(buf, item->offset_in_buf + offset,\n-\t\t\t\t      commands, commands_len);\n-\t\t\toffset += commands_len;\n-\t\t}\n-\t\tfirst = 0;\n-\t}\n-\n-\t/* append final <commands> */\n-\tstrbuf_add(buf, commands, commands_len);\n-\n-\ti = write_message(buf->buf, buf->len, todo_file, 0);\n+\ttodo_list_add_exec_commands(&todo_list, commands);\n+\tres = todo_list_write_to_file(&todo_list, todo_file, NULL, NULL, 0, 0, -1, 0);\n \ttodo_list_release(&todo_list);\n-\treturn i;\n+\n+\treturn res;\n }\n \n static void todo_list_to_strbuf(struct todo_list *todo_list, struct strbuf *buf,\n-- \n2.19.1\n\n"},{"id":"361811","messageId":"20181027212930.9303-8-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"[PATCH v2 07/16] sequencer: refactor rearrange_squash() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-27T21:29:21Z","receivedAt":"2018-10-27T21:30:05Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors rearrange_squash() to work on a todo_list to avoid\nredundant reads and writes.  The function is renamed\ntodo_list_rearrange_squash().\n\nThe old version created a new buffer, which was directly written to the\ndisk.  This new version creates a new item list by just memcpying items\nfrom the old item list, without creating a new buffer.  This eliminates\nthe need to reparse the todo list, but this also means its buffer cannot\nbe directly written to the disk.\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, rearrange_squash_in_todo_file().\n\ncomplete_action() still uses rearrange_squash_in_todo_file() for now.\nThis will be changed in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |  2 +-\n sequencer.c                   | 87 +++++++++++++++++------------------\n sequencer.h                   |  2 +-\n 3 files changed, 45 insertions(+), 46 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex c1d87c0fe6..f827e53f05 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -258,7 +258,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = check_todo_list_from_file();\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n-\t\tret = rearrange_squash();\n+\t\tret = rearrange_squash_in_todo_file();\n \t\tbreak;\n \tcase ADD_EXEC:\n \t\tret = sequencer_add_exec_commands(cmd);\ndiff --git a/sequencer.c b/sequencer.c\nindex 12a3efeca8..09e32f3e5a 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4523,7 +4523,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t    write_message(\"noop\\n\", 5, todo_file, 0))\n \t\treturn -1;\n \n-\tif (autosquash && rearrange_squash())\n+\tif (autosquash && rearrange_squash_in_todo_file())\n \t\treturn -1;\n \n \tif (cmd && *cmd)\n@@ -4629,21 +4629,13 @@ define_commit_slab(commit_todo_item, struct todo_item *);\n  * message will have to be retrieved from the commit (as the oneline in the\n  * script cannot be trusted) in order to normalize the autosquash arrangement.\n  */\n-int rearrange_squash(void)\n+static int todo_list_rearrange_squash(struct todo_list *todo_list)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct hashmap subject2item;\n-\tint res = 0, rearranged = 0, *next, *tail, i;\n+\tint rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;\n \tchar **subjects;\n \tstruct commit_todo_item commit_todo;\n-\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tstruct todo_item *items = NULL;\n \n \tinit_commit_todo_item(&commit_todo);\n \t/*\n@@ -4656,13 +4648,13 @@ int rearrange_squash(void)\n \t * be moved to appear after the i'th.\n \t */\n \thashmap_init(&subject2item, (hashmap_cmp_fn) subject2item_cmp,\n-\t\t     NULL, todo_list.nr);\n-\tALLOC_ARRAY(next, todo_list.nr);\n-\tALLOC_ARRAY(tail, todo_list.nr);\n-\tALLOC_ARRAY(subjects, todo_list.nr);\n-\tfor (i = 0; i < todo_list.nr; i++) {\n+\t\t     NULL, todo_list->nr);\n+\tALLOC_ARRAY(next, todo_list->nr);\n+\tALLOC_ARRAY(tail, todo_list->nr);\n+\tALLOC_ARRAY(subjects, todo_list->nr);\n+\tfor (i = 0; i < todo_list->nr; i++) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n-\t\tstruct todo_item *item = todo_list.items + i;\n+\t\tstruct todo_item *item = todo_list->items + i;\n \t\tconst char *commit_buffer, *subject, *p;\n \t\tsize_t subject_len;\n \t\tint i2 = -1;\n@@ -4675,7 +4667,6 @@ int rearrange_squash(void)\n \t\t}\n \n \t\tif (is_fixup(item->command)) {\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\tclear_commit_todo_item(&commit_todo);\n \t\t\treturn error(_(\"the script was already rearranged.\"));\n \t\t}\n@@ -4710,7 +4701,7 @@ int rearrange_squash(void)\n \t\t\t\t *commit_todo_item_at(&commit_todo, commit2))\n \t\t\t\t/* found by commit name */\n \t\t\t\ti2 = *commit_todo_item_at(&commit_todo, commit2)\n-\t\t\t\t\t- todo_list.items;\n+\t\t\t\t\t- todo_list->items;\n \t\t\telse {\n \t\t\t\t/* copy can be a prefix of the commit subject */\n \t\t\t\tfor (i2 = 0; i2 < i; i2++)\n@@ -4723,7 +4714,7 @@ int rearrange_squash(void)\n \t\t}\n \t\tif (i2 >= 0) {\n \t\t\trearranged = 1;\n-\t\t\ttodo_list.items[i].command =\n+\t\t\ttodo_list->items[i].command =\n \t\t\t\tstarts_with(subject, \"fixup!\") ?\n \t\t\t\tTODO_FIXUP : TODO_SQUASH;\n \t\t\tif (next[i2] < 0)\n@@ -4741,10 +4732,8 @@ int rearrange_squash(void)\n \t}\n \n \tif (rearranged) {\n-\t\tstruct strbuf buf = STRBUF_INIT;\n-\n-\t\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\t\tenum todo_command command = todo_list.items[i].command;\n+\t\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\t\tenum todo_command command = todo_list->items[i].command;\n \t\t\tint cur = i;\n \n \t\t\t/*\n@@ -4755,37 +4744,47 @@ int rearrange_squash(void)\n \t\t\t\tcontinue;\n \n \t\t\twhile (cur >= 0) {\n-\t\t\t\tconst char *bol =\n-\t\t\t\t\tget_item_line(&todo_list, cur);\n-\t\t\t\tconst char *eol =\n-\t\t\t\t\tget_item_line(&todo_list, cur + 1);\n-\n-\t\t\t\t/* replace 'pick', by 'fixup' or 'squash' */\n-\t\t\t\tcommand = todo_list.items[cur].command;\n-\t\t\t\tif (is_fixup(command)) {\n-\t\t\t\t\tstrbuf_addstr(&buf,\n-\t\t\t\t\t\ttodo_command_info[command].str);\n-\t\t\t\t\tbol += strcspn(bol, \" \\t\");\n-\t\t\t\t}\n-\n-\t\t\t\tstrbuf_add(&buf, bol, eol - bol);\n-\n+\t\t\t\tALLOC_GROW(items, nr + 1, alloc);\n+\t\t\t\tmemcpy(items + nr++, todo_list->items + cur, sizeof(struct todo_item));\n \t\t\t\tcur = next[cur];\n \t\t\t}\n \t\t}\n \n-\t\tres = rewrite_file(todo_file, buf.buf, buf.len);\n-\t\tstrbuf_release(&buf);\n+\t\tFREE_AND_NULL(todo_list->items);\n+\t\ttodo_list->items = items;\n+\t\ttodo_list->nr = nr;\n+\t\ttodo_list->alloc = alloc;\n \t}\n \n \tfree(next);\n \tfree(tail);\n-\tfor (i = 0; i < todo_list.nr; i++)\n+\tfor (i = 0; i < todo_list->nr; i++)\n \t\tfree(subjects[i]);\n \tfree(subjects);\n \thashmap_free(&subject2item, 1);\n-\ttodo_list_release(&todo_list);\n \n \tclear_commit_todo_item(&commit_todo);\n+\n+\treturn 0;\n+}\n+\n+int rearrange_squash_in_todo_file(void)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n+\n+\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n+\t\treturn -1;\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn -1;\n+\t}\n+\n+\tres = todo_list_rearrange_squash(&todo_list);\n+\tif (!res)\n+\t\tres = todo_list_write_to_file(&todo_list, todo_file, NULL, NULL, 0, 0, -1, 0);\n+\n+\ttodo_list_release(&todo_list);\n \treturn res;\n }\ndiff --git a/sequencer.h b/sequencer.h\nindex e203930084..0c8074cab0 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -139,7 +139,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, const char *cmd,\n \t\t    unsigned autosquash);\n-int rearrange_squash(void);\n+int rearrange_squash_in_todo_file(void);\n \n extern const char sign_off_header[];\n \n-- \n2.19.1\n\n"},{"id":"361812","messageId":"20181027212930.9303-9-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"[PATCH v2 08/16] sequencer: make sequencer_make_script() write its script to a strbuf","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-27T21:29:22Z","receivedAt":"2018-10-27T21:30:07Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This makes sequencer_make_script() write its script to a strbuf (ie. the\nbuffer of a todo_list) instead of a FILE.  This reduce the amount of\nread/write made by rebase interactive.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 13 +++++++-----\n sequencer.c                   | 38 ++++++++++++++++-------------------\n sequencer.h                   |  2 +-\n 3 files changed, 26 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex f827e53f05..eef1ff2e83 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -71,7 +71,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tconst char *head_hash = NULL;\n \tchar *revisions = NULL, *shortrevisions = NULL;\n \tstruct argv_array make_script_args = ARGV_ARRAY_INIT;\n-\tFILE *todo_list;\n+\tFILE *todo_list_file;\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n \n \tif (prepare_branch_to_be_rebased(opts, switch_to))\n \t\treturn -1;\n@@ -93,8 +94,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (!upstream && squash_onto)\n \t\twrite_file(path_squash_onto(), \"%s\\n\", squash_onto);\n \n-\ttodo_list = fopen(rebase_path_todo(), \"w\");\n-\tif (!todo_list) {\n+\ttodo_list_file = fopen(rebase_path_todo(), \"w\");\n+\tif (!todo_list_file) {\n \t\tfree(revisions);\n \t\tfree(shortrevisions);\n \n@@ -105,10 +106,11 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (restrict_revision)\n \t\targv_array_push(&make_script_args, restrict_revision);\n \n-\tret = sequencer_make_script(todo_list,\n+\tret = sequencer_make_script(&todo_list.buf,\n \t\t\t\t    make_script_args.argc, make_script_args.argv,\n \t\t\t\t    flags);\n-\tfclose(todo_list);\n+\tfputs(todo_list.buf.buf, todo_list_file);\n+\tfclose(todo_list_file);\n \n \tif (ret)\n \t\terror(_(\"could not generate todo list\"));\n@@ -120,6 +122,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \n \tfree(revisions);\n \tfree(shortrevisions);\n+\ttodo_list_release(&todo_list);\n \targv_array_clear(&make_script_args);\n \n \treturn ret;\ndiff --git a/sequencer.c b/sequencer.c\nindex 09e32f3e5a..94167588a2 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -3927,7 +3927,7 @@ static const char *label_oid(struct object_id *oid, const char *label,\n }\n \n static int make_script_with_merges(struct pretty_print_context *pp,\n-\t\t\t\t   struct rev_info *revs, FILE *out,\n+\t\t\t\t   struct rev_info *revs, struct strbuf *out,\n \t\t\t\t   unsigned flags)\n {\n \tint keep_empty = flags & TODO_LIST_KEEP_EMPTY;\n@@ -4076,7 +4076,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t * gathering commits not yet shown, reversing the list on the fly,\n \t * then outputting that list (labeling revisions as needed).\n \t */\n-\tfprintf(out, \"%s onto\\n\", cmd_label);\n+\tstrbuf_addf(out, \"%s onto\\n\", cmd_label);\n \tfor (iter = tips; iter; iter = iter->next) {\n \t\tstruct commit_list *list = NULL, *iter2;\n \n@@ -4086,9 +4086,9 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\tentry = oidmap_get(&state.commit2label, &commit->object.oid);\n \n \t\tif (entry)\n-\t\t\tfprintf(out, \"\\n# Branch %s\\n\", entry->string);\n+\t\t\tstrbuf_addf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n \t\telse\n-\t\t\tfprintf(out, \"\\n\");\n+\t\t\tstrbuf_addch(out, '\\n');\n \n \t\twhile (oidset_contains(&interesting, &commit->object.oid) &&\n \t\t       !oidset_contains(&shown, &commit->object.oid)) {\n@@ -4101,8 +4101,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t}\n \n \t\tif (!commit)\n-\t\t\tfprintf(out, \"%s %s\\n\", cmd_reset,\n-\t\t\t\trebase_cousins ? \"onto\" : \"[new root]\");\n+\t\t\tstrbuf_addf(out, \"%s %s\\n\", cmd_reset,\n+\t\t\t\t    rebase_cousins ? \"onto\" : \"[new root]\");\n \t\telse {\n \t\t\tconst char *to = NULL;\n \n@@ -4115,12 +4115,12 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t\t\t\t       &state);\n \n \t\t\tif (!to || !strcmp(to, \"onto\"))\n-\t\t\t\tfprintf(out, \"%s onto\\n\", cmd_reset);\n+\t\t\t\tstrbuf_addf(out, \"%s onto\\n\", cmd_reset);\n \t\t\telse {\n \t\t\t\tstrbuf_reset(&oneline);\n \t\t\t\tpretty_print_commit(pp, commit, &oneline);\n-\t\t\t\tfprintf(out, \"%s %s # %s\\n\",\n-\t\t\t\t\tcmd_reset, to, oneline.buf);\n+\t\t\t\tstrbuf_addf(out, \"%s %s # %s\\n\",\n+\t\t\t\t\t    cmd_reset, to, oneline.buf);\n \t\t\t}\n \t\t}\n \n@@ -4129,11 +4129,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t\tentry = oidmap_get(&commit2todo, oid);\n \t\t\t/* only show if not already upstream */\n \t\t\tif (entry)\n-\t\t\t\tfprintf(out, \"%s\\n\", entry->string);\n+\t\t\t\tstrbuf_addf(out, \"%s\\n\", entry->string);\n \t\t\tentry = oidmap_get(&state.commit2label, oid);\n \t\t\tif (entry)\n-\t\t\t\tfprintf(out, \"%s %s\\n\",\n-\t\t\t\t\tcmd_label, entry->string);\n+\t\t\t\tstrbuf_addf(out, \"%s %s\\n\",\n+\t\t\t\t\t    cmd_label, entry->string);\n \t\t\toidset_insert(&shown, oid);\n \t\t}\n \n@@ -4155,12 +4155,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \treturn 0;\n }\n \n-int sequencer_make_script(FILE *out, int argc, const char **argv,\n+int sequencer_make_script(struct strbuf *out, int argc, const char **argv,\n \t\t\t  unsigned flags)\n {\n \tchar *format = NULL;\n \tstruct pretty_print_context pp = {0};\n-\tstruct strbuf buf = STRBUF_INIT;\n \tstruct rev_info revs;\n \tstruct commit *commit;\n \tint keep_empty = flags & TODO_LIST_KEEP_EMPTY;\n@@ -4203,16 +4202,13 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,\n \n \t\tif (!is_empty && (commit->object.flags & PATCHSAME))\n \t\t\tcontinue;\n-\t\tstrbuf_reset(&buf);\n \t\tif (!keep_empty && is_empty)\n-\t\t\tstrbuf_addf(&buf, \"%c \", comment_line_char);\n-\t\tstrbuf_addf(&buf, \"%s %s \", insn,\n+\t\t\tstrbuf_addf(out, \"%c \", comment_line_char);\n+\t\tstrbuf_addf(out, \"%s %s \", insn,\n \t\t\t    oid_to_hex(&commit->object.oid));\n-\t\tpretty_print_commit(&pp, commit, &buf);\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t\tfputs(buf.buf, out);\n+\t\tpretty_print_commit(&pp, commit, out);\n+\t\tstrbuf_addch(out, '\\n');\n \t}\n-\tstrbuf_release(&buf);\n \treturn 0;\n }\n \ndiff --git a/sequencer.h b/sequencer.h\nindex 0c8074cab0..5af96723e6 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -129,7 +129,7 @@ int sequencer_remove_state(struct replay_opts *opts);\n  * commits should be rebased onto the new base, this flag needs to be passed.\n  */\n #define TODO_LIST_REBASE_COUSINS (1U << 4)\n-int sequencer_make_script(FILE *out, int argc, const char **argv,\n+int sequencer_make_script(struct strbuf *out, int argc, const char **argv,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(const char *command);\n-- \n2.19.1\n\n"},{"id":"361814","messageId":"20181027212930.9303-10-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"[PATCH v2 09/16] sequencer: change complete_action() to use the refactored functions","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-27T21:29:23Z","receivedAt":"2018-10-27T21:30:07Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"complete_action() used functions that read the todo-list file, made some\nchanges to it, and wrote it back to the disk.\n\nThe previous commits were dedicated to separate the part that deals with\nthe file from the actual logic of these functions.  Now that this is\ndone, we can call directly the \"logic\" functions to avoid useless file\naccess.\n\nThe parsing of the list has to be done by the caller.  If the buffer of\nthe todo list provided by the caller is empty, a `noop' command is\ndirectly added to the todo list, without touching to the buffer.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 16 ++-----\n sequencer.c                   | 82 ++++++++++++++++-------------------\n sequencer.h                   |  2 +-\n 3 files changed, 42 insertions(+), 58 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex eef1ff2e83..c07330bdff 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -71,7 +71,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tconst char *head_hash = NULL;\n \tchar *revisions = NULL, *shortrevisions = NULL;\n \tstruct argv_array make_script_args = ARGV_ARRAY_INIT;\n-\tFILE *todo_list_file;\n \tstruct todo_list todo_list = TODO_LIST_INIT;\n \n \tif (prepare_branch_to_be_rebased(opts, switch_to))\n@@ -94,14 +93,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (!upstream && squash_onto)\n \t\twrite_file(path_squash_onto(), \"%s\\n\", squash_onto);\n \n-\ttodo_list_file = fopen(rebase_path_todo(), \"w\");\n-\tif (!todo_list_file) {\n-\t\tfree(revisions);\n-\t\tfree(shortrevisions);\n-\n-\t\treturn error_errno(_(\"could not open %s\"), rebase_path_todo());\n-\t}\n-\n \targv_array_pushl(&make_script_args, \"\", revisions, NULL);\n \tif (restrict_revision)\n \t\targv_array_push(&make_script_args, restrict_revision);\n@@ -109,15 +100,16 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tret = sequencer_make_script(&todo_list.buf,\n \t\t\t\t    make_script_args.argc, make_script_args.argv,\n \t\t\t\t    flags);\n-\tfputs(todo_list.buf.buf, todo_list_file);\n-\tfclose(todo_list_file);\n \n \tif (ret)\n \t\terror(_(\"could not generate todo list\"));\n \telse {\n \t\tdiscard_cache();\n+\t\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list))\n+\t\t\tBUG(\"unusable todo list\");\n+\n \t\tret = complete_action(opts, flags, shortrevisions, onto_name, onto,\n-\t\t\t\t      head_hash, cmd, autosquash);\n+\t\t\t\t      head_hash, cmd, autosquash, &todo_list);\n \t}\n \n \tfree(revisions);\ndiff --git a/sequencer.c b/sequencer.c\nindex 94167588a2..1f7579328b 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4501,93 +4501,85 @@ static int skip_unnecessary_picks(struct object_id *output_oid)\n \treturn 0;\n }\n \n+static int todo_list_rearrange_squash(struct todo_list *todo_list);\n+\n int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, const char *cmd,\n-\t\t    unsigned autosquash)\n+\t\t    unsigned autosquash, struct todo_list *todo_list)\n {\n \tconst char *shortonto, *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf *buf = &(todo_list.buf);\n+\tstruct todo_list new_todo = TODO_LIST_INIT;\n+\tstruct strbuf *buf = &todo_list->buf;\n \tstruct object_id oid;\n-\tstruct stat st;\n+\tint command_count;\n \n \tget_oid(onto, &oid);\n \tshortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);\n \n-\tif (!lstat(todo_file, &st) && st.st_size == 0 &&\n-\t    write_message(\"noop\\n\", 5, todo_file, 0))\n-\t\treturn -1;\n+\tif (buf->len == 0) {\n+\t\tstruct todo_item *item = append_new_todo(todo_list);\n+\t\titem->command = TODO_NOOP;\n+\t\titem->commit = NULL;\n+\t\titem->arg = NULL;\n+\t\titem->arg_len = item->flags = item->offset_in_buf = 0;\n+\t}\n \n-\tif (autosquash && rearrange_squash_in_todo_file())\n+\tif (autosquash && todo_list_rearrange_squash(todo_list))\n \t\treturn -1;\n \n \tif (cmd && *cmd)\n-\t\tsequencer_add_exec_commands(cmd);\n-\n-\tif (strbuf_read_file(buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(buf->buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n+\t\ttodo_list_add_exec_commands(todo_list, cmd);\n \n-\tif (count_commands(&todo_list) == 0) {\n+\tcommand_count = count_commands(todo_list);\n+\tif (command_count == 0) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n \n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\tstrbuf_addch(buf, '\\n');\n-\tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n-\t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n-\t\t\t\t      count_commands(&todo_list)),\n-\t\t\t      shortrevisions, shortonto, count_commands(&todo_list));\n-\tappend_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);\n-\n-\tif (write_message(buf->buf, buf->len, todo_file, 0)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tif (todo_list_write_to_file(todo_list, todo_file,\n+\t\t\t\t    shortrevisions, shortonto, command_count,\n+\t\t\t\t    1, -1, flags | TODO_LIST_SHORTEN_IDS))\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n \n \tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n \t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n \t\t\t     rebase_path_todo_backup());\n \n-\tif (transform_todo_file(flags | TODO_LIST_SHORTEN_IDS))\n-\t\treturn error(_(\"could not transform the todo list\"));\n-\n-\tstrbuf_reset(buf);\n-\n-\tif (launch_sequence_editor(todo_file, buf, NULL)) {\n+\tif (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n \n \t\treturn -1;\n \t}\n \n-\tstrbuf_stripspace(buf, 1);\n-\tif (buf->len == 0) {\n+\tstrbuf_stripspace(&new_todo.buf, 1);\n+\tif (new_todo.buf.len == 0) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n+\t\ttodo_list_release(&new_todo);\n \n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\n-\tif (check_todo_list_from_file()) {\n+\tif (todo_list_parse_insn_buffer(new_todo.buf.buf, &new_todo) ||\n+\t    todo_list_check(todo_list, &new_todo)) {\n+\t\tfprintf(stderr, _(edit_todo_list_advice));\n \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n+\t\ttodo_list_release(&new_todo);\n+\n \t\treturn -1;\n \t}\n \n-\tif (transform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS)))\n-\t\treturn error(_(\"could not transform the todo list\"));\n+\tif (todo_list_write_to_file(&new_todo, todo_file, NULL, NULL, 0, 0, -1,\n+\t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS))) {\n+\t\ttodo_list_release(&new_todo);\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_release(&new_todo);\n \n \tif (opts->allow_ff && skip_unnecessary_picks(&oid))\n \t\treturn error(_(\"could not skip unnecessary pick commands\"));\ndiff --git a/sequencer.h b/sequencer.h\nindex 5af96723e6..4a051e5dc9 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -138,7 +138,7 @@ int check_todo_list_from_file(void);\n int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, const char *cmd,\n-\t\t    unsigned autosquash);\n+\t\t    unsigned autosquash, struct todo_list *todo_list);\n int rearrange_squash_in_todo_file(void);\n \n extern const char sign_off_header[];\n-- \n2.19.1\n\n"},{"id":"361817","messageId":"20181027212930.9303-11-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"[PATCH v2 10/16] sequencer: refactor skip_unnecessary_picks() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-27T21:29:24Z","receivedAt":"2018-10-27T21:30:09Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors skip_unnecessary_picks() to work on a todo_list.  The\nfile-handling logic is completely dropped here, as its only usage is\nmade by complete_action().\n\nInstead of truncating the todo list’s buffer, the items are moved to\nthe beginning of the list, eliminating the need to reparse the list.\nThis also means its buffer cannot be directly written to the disk.\n\nrewrite_file() is then removed, as it is now unused.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 80 ++++++++++++-----------------------------------------\n 1 file changed, 18 insertions(+), 62 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 1f7579328b..7286498572 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4408,52 +4408,21 @@ int check_todo_list_from_file(void)\n \treturn res;\n }\n \n-static int rewrite_file(const char *path, const char *buf, size_t len)\n-{\n-\tint rc = 0;\n-\tint fd = open(path, O_WRONLY | O_TRUNC);\n-\tif (fd < 0)\n-\t\treturn error_errno(_(\"could not open '%s' for writing\"), path);\n-\tif (write_in_full(fd, buf, len) < 0)\n-\t\trc = error_errno(_(\"could not write to '%s'\"), path);\n-\tif (close(fd) && !rc)\n-\t\trc = error_errno(_(\"could not close '%s'\"), path);\n-\treturn rc;\n-}\n-\n /* skip picking commits whose parents are unchanged */\n-static int skip_unnecessary_picks(struct object_id *output_oid)\n+static int skip_unnecessary_picks(struct todo_list *todo_list,\n+\t\t\t\t  struct object_id *output_oid)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct object_id *parent_oid;\n-\tint fd, i;\n-\n-\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n-\t\treturn error(_(\"could not read 'onto'\"));\n-\tif (get_oid(buf.buf, output_oid)) {\n-\t\tstrbuf_release(&buf);\n-\t\treturn error(_(\"need a HEAD to fixup\"));\n-\t}\n-\tstrbuf_release(&buf);\n-\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tint i;\n \n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tstruct todo_item *item = todo_list.items + i;\n+\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\tstruct todo_item *item = todo_list->items + i;\n \n \t\tif (item->command >= TODO_NOOP)\n \t\t\tcontinue;\n \t\tif (item->command != TODO_PICK)\n \t\t\tbreak;\n \t\tif (parse_commit(item->commit)) {\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\treturn error(_(\"could not parse commit '%s'\"),\n \t\t\t\toid_to_hex(&item->commit->object.oid));\n \t\t}\n@@ -4467,37 +4436,22 @@ static int skip_unnecessary_picks(struct object_id *output_oid)\n \t\toidcpy(output_oid, &item->commit->object.oid);\n \t}\n \tif (i > 0) {\n-\t\tint offset = get_item_line_offset(&todo_list, i);\n \t\tconst char *done_path = rebase_path_done();\n \n-\t\tfd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);\n-\t\tif (fd < 0) {\n-\t\t\terror_errno(_(\"could not open '%s' for writing\"),\n-\t\t\t\t    done_path);\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\treturn -1;\n-\t\t}\n-\t\tif (write_in_full(fd, todo_list.buf.buf, offset) < 0) {\n+\t\tif (todo_list_write_to_file(todo_list, done_path, NULL, NULL, 0, 0, i, 0)) {\n \t\t\terror_errno(_(\"could not write to '%s'\"), done_path);\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\tclose(fd);\n \t\t\treturn -1;\n \t\t}\n-\t\tclose(fd);\n \n-\t\tif (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,\n-\t\t\t\t todo_list.buf.len - offset) < 0) {\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\treturn -1;\n-\t\t}\n+\t\tmemmove(todo_list->items, todo_list->items + i,\n+\t\t\tsizeof(struct todo_item) * (todo_list->nr - i));\n+\t\ttodo_list->nr -= i;\n+\t\ttodo_list->current = 0;\n \n-\t\ttodo_list.current = i;\n-\t\tif (is_fixup(peek_command(&todo_list, 0)))\n-\t\t\trecord_in_rewritten(output_oid, peek_command(&todo_list, 0));\n+\t\tif (is_fixup(peek_command(todo_list, 0)))\n+\t\t\trecord_in_rewritten(output_oid, peek_command(todo_list, 0));\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\n \treturn 0;\n }\n \n@@ -4573,6 +4527,11 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t\treturn -1;\n \t}\n \n+\tif (opts->allow_ff && skip_unnecessary_picks(&new_todo, &oid)) {\n+\t\ttodo_list_release(&new_todo);\n+\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n+\t}\n+\n \tif (todo_list_write_to_file(&new_todo, todo_file, NULL, NULL, 0, 0, -1,\n \t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS))) {\n \t\ttodo_list_release(&new_todo);\n@@ -4581,12 +4540,9 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \n \ttodo_list_release(&new_todo);\n \n-\tif (opts->allow_ff && skip_unnecessary_picks(&oid))\n-\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n-\n \tif (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))\n \t\treturn -1;\n-;\n+\n \tif (require_clean_work_tree(\"rebase\", \"\", 1, 1))\n \t\treturn -1;\n \n-- \n2.19.1\n\n"},{"id":"361818","messageId":"20181027212930.9303-12-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"[PATCH v2 11/16] rebase-interactive: use todo_list_write_to_file() in edit_todo_list()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-27T21:29:25Z","receivedAt":"2018-10-27T21:30:10Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Just like complete_action(), edit_todo_list() used a\nfunction (transform_todo_file()) that read the todo list from the disk\nand wrote it back, resulting in useless disk accesses.\n\nThis changes edit_todo_list() to call directly todo_list_write_to_file()\ninstead.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n rebase-interactive.c | 38 ++++++++++++++++----------------------\n 1 file changed, 16 insertions(+), 22 deletions(-)\n\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 9e81ecfe9f..b2be99a900 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -78,39 +78,33 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \n int edit_todo_list(unsigned flags)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n \tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n \n-\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n-\tstrbuf_stripspace(&buf, 1);\n-\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n-\t\tstrbuf_release(&buf);\n+\tstrbuf_stripspace(&todo_list.buf, 1);\n+\ttodo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n+\tif (todo_list_write_to_file(&todo_list, todo_file, NULL, NULL, 0, 1, -1,\n+\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS)) {\n+\t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n \n-\tstrbuf_release(&buf);\n-\n-\ttransform_todo_file(flags | TODO_LIST_SHORTEN_IDS);\n-\n-\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tappend_todo_help(1, 0, &buf);\n-\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n-\t\tstrbuf_release(&buf);\n+\tstrbuf_reset(&todo_list.buf);\n+\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n+\t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n \n-\tstrbuf_release(&buf);\n+\tif (!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list))\n+\t\tres = todo_list_write_to_file(&todo_list, todo_file, NULL, NULL, 0, 0, -1,\n+\t\t\t\t\t      flags & ~(TODO_LIST_SHORTEN_IDS));\n \n-\tif (launch_sequence_editor(todo_file, NULL, NULL))\n-\t\treturn -1;\n-\n-\ttransform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS));\n-\n-\treturn 0;\n+\ttodo_list_release(&todo_list);\n+\treturn res;\n }\n \n define_commit_slab(commit_seen, unsigned char);\n-- \n2.19.1\n\n"},{"id":"361820","messageId":"20181027212930.9303-13-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"[PATCH v2 12/16] rebase-interactive: append_todo_help() changes","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-27T21:29:26Z","receivedAt":"2018-10-27T21:30:11Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This moves the writing of the comment \"Rebase $shortrevisions onto\n$shortonto ($command_count commands)\" from complete_action() to\nappend_todo_help().\n\nshortrevisions, shortonto, and command_count are passed as parameters to\nappend_todo_help().\n\nDuring the initial edit of the todo list, shortrevisions and shortonto\nare not NULL.  Therefore, if shortrevisions or shortonto is NULL, then\nedit_todo would be true, otherwise it would be false.  Thus, edit_todo\nis removed from the parameters of append_todo_help().\n\nedit_todo_list() and complete_action() are modified to fit these\nchanges.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n rebase-interactive.c | 12 +++++++++++-\n rebase-interactive.h |  3 ++-\n sequencer.c          | 16 ++++------------\n 3 files changed, 17 insertions(+), 14 deletions(-)\n\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex b2be99a900..0643d54b1b 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -28,7 +28,8 @@ static enum missing_commit_check_level get_missing_commit_check_level(void)\n \treturn MISSING_COMMIT_CHECK_IGNORE;\n }\n \n-void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n+void append_todo_help(unsigned keep_empty, int command_count,\n+\t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf)\n {\n \tconst char *msg = _(\"\\nCommands:\\n\"\n@@ -47,6 +48,15 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \".       specified). Use -c <commit> to reword the commit message.\\n\"\n \"\\n\"\n \"These lines can be re-ordered; they are executed from top to bottom.\\n\");\n+\tunsigned edit_todo = !(shortrevisions && shortonto);\n+\n+\tif (!edit_todo) {\n+\t\tstrbuf_addch(buf, '\\n');\n+\t\tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n+\t\t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n+\t\t\t\t\t      command_count),\n+\t\t\t\t      shortrevisions, shortonto, command_count);\n+\t}\n \n \tstrbuf_add_commented_lines(buf, msg, strlen(msg));\n \ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 6bc7bc315d..61858f3a02 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -1,7 +1,8 @@\n #ifndef REBASE_INTERACTIVE_H\n #define REBASE_INTERACTIVE_H\n \n-void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n+void append_todo_help(unsigned keep_empty, int command_count,\n+\t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf);\n int edit_todo_list(unsigned flags);\n int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\ndiff --git a/sequencer.c b/sequencer.c\nindex 7286498572..4c664d6388 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4331,21 +4331,13 @@ int todo_list_write_to_file(struct todo_list *todo_list, const char *file,\n \t\t\t    const char *shortrevisions, const char *shortonto,\n \t\t\t    int command_count, int append_help, int num, unsigned flags)\n {\n-\tint edit_todo = !(shortrevisions && shortonto), res;\n+\tint res;\n \tstruct strbuf buf = STRBUF_INIT;\n \n \ttodo_list_to_strbuf(todo_list, &buf, num, flags);\n-\n-\tif (append_help) {\n-\t\tif (!edit_todo) {\n-\t\t\tstrbuf_addch(&buf, '\\n');\n-\t\t\tstrbuf_commented_addf(&buf, Q_(\"Rebase %s onto %s (%d command)\",\n-\t\t\t\t\t\t       \"Rebase %s onto %s (%d commands)\",\n-\t\t\t\t\t\t       command_count),\n-\t\t\t\t\t      shortrevisions, shortonto, command_count);\n-\t\t}\n-\t\tappend_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, &buf);\n-\t}\n+\tif (append_help)\n+\t\tappend_todo_help(flags & TODO_LIST_KEEP_EMPTY, command_count,\n+\t\t\t\t shortrevisions, shortonto, &buf);\n \n \tres = write_message(buf.buf, buf.len, file, 0);\n \tstrbuf_release(&buf);\n-- \n2.19.1\n\n"},{"id":"361815","messageId":"20181027212930.9303-14-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"[PATCH v2 13/16] rebase-interactive: rewrite edit_todo_list() to handle the initial edit","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-27T21:29:27Z","receivedAt":"2018-10-27T21:30:13Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"edit_todo_list() is changed to work on a todo_list, and to handle the\ninitial edition of the todo list (ie. making a backup of the todo\nlist).\n\nIt does not check for dropped commits yet, as todo_list_check() does not\ntake the commits that have already been processed by the rebase (ie. the\ntodo list is edited in the middle of a rebase session).\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 23 ++++++++++++++++-\n rebase-interactive.c          | 48 ++++++++++++++++++-----------------\n rebase-interactive.h          |  4 ++-\n sequencer.c                   |  3 +--\n sequencer.h                   |  1 +\n 5 files changed, 52 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex c07330bdff..0f83422aa0 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -13,6 +13,27 @@ static GIT_PATH_FUNC(path_state_dir, \"rebase-merge/\")\n static GIT_PATH_FUNC(path_squash_onto, \"rebase-merge/squash-onto\")\n static GIT_PATH_FUNC(path_interactive, \"rebase-merge/interactive\")\n \n+static int edit_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT,\n+\t\tnew_todo = TODO_LIST_INIT;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tstrbuf_stripspace(&todo_list.buf, 1);\n+\tif (!edit_todo_list(&todo_list, &new_todo, flags, 0, NULL, NULL) &&\n+\t    todo_list_write_to_file(&new_todo, todo_file, NULL, NULL, 0, 0, -1,\n+\t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS)) < 0)\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n+\n+\ttodo_list_release(&todo_list);\n+\ttodo_list_release(&new_todo);\n+\n+\treturn 0;\n+}\n+\n static int get_revision_ranges(const char *upstream, const char *onto,\n \t\t\t       const char **head_hash,\n \t\t\t       char **revisions, char **shortrevisions)\n@@ -234,7 +255,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tbreak;\n \t}\n \tcase EDIT_TODO:\n-\t\tret = edit_todo_list(flags);\n+\t\tret = edit_todo_file(flags);\n \t\tbreak;\n \tcase SHOW_CURRENT_PATCH: {\n \t\tstruct child_process cmd = CHILD_PROCESS_INIT;\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 0643d54b1b..d7a0818ac9 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -86,35 +86,37 @@ void append_todo_help(unsigned keep_empty, int command_count,\n \t}\n }\n \n-int edit_todo_list(unsigned flags)\n+int edit_todo_list(struct todo_list *todo_list, struct todo_list *new_todo,\n+\t\t   unsigned flags, int command_count,\n+\t\t   const char *shortrevisions, const char *shortonto)\n {\n \tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res = 0;\n-\n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tstrbuf_stripspace(&todo_list.buf, 1);\n-\ttodo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n-\tif (todo_list_write_to_file(&todo_list, todo_file, NULL, NULL, 0, 1, -1,\n-\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n+\tunsigned initial = shortrevisions && shortonto;\n+\n+\tif (initial) {\n+\t\ttodo_list_write_to_file(todo_list, todo_file, shortrevisions, shortonto,\n+\t\t\t\t\t0, 1, -1, flags | TODO_LIST_SHORTEN_IDS);\n+\n+\t\tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n+\t\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n+\t\t\t\t     rebase_path_todo_backup());\n+\t} else {\n+\t\ttodo_list_parse_insn_buffer(todo_list->buf.buf, todo_list);\n+\t\ttodo_list_write_to_file(todo_list, todo_file, NULL, NULL, 0, 1, -1,\n+\t\t\t\t\tflags | TODO_LIST_SHORTEN_IDS);\n \t}\n \n-\tstrbuf_reset(&todo_list.buf);\n-\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tif (launch_sequence_editor(todo_file, &new_todo->buf, NULL))\n+\t\treturn -2;\n \n-\tif (!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list))\n-\t\tres = todo_list_write_to_file(&todo_list, todo_file, NULL, NULL, 0, 0, -1,\n-\t\t\t\t\t      flags & ~(TODO_LIST_SHORTEN_IDS));\n+\tstrbuf_stripspace(&new_todo->buf, 1);\n+\tif (initial && new_todo->buf.len == 0)\n+\t\treturn -3;\n \n-\ttodo_list_release(&todo_list);\n-\treturn res;\n+\tif (!initial)\n+\t\ttodo_list_parse_insn_buffer(new_todo->buf.buf, new_todo);\n+\n+\treturn 0;\n }\n \n define_commit_slab(commit_seen, unsigned char);\ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 61858f3a02..83cde455e6 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -4,7 +4,9 @@\n void append_todo_help(unsigned keep_empty, int command_count,\n \t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf);\n-int edit_todo_list(unsigned flags);\n+int edit_todo_list(struct todo_list *todo_list, struct todo_list *new_todo,\n+\t\t   unsigned flags, int command_count,\n+\t\t   const char *shortrevisions, const char *shortonto);\n int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n \n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex 4c664d6388..a03505f582 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -54,8 +54,7 @@ static GIT_PATH_FUNC(rebase_path, \"rebase-merge\")\n  * file and written to the tail of 'done'.\n  */\n GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n-static GIT_PATH_FUNC(rebase_path_todo_backup,\n-\t\t     \"rebase-merge/git-rebase-todo.backup\")\n+GIT_PATH_FUNC(rebase_path_todo_backup, \"rebase-merge/git-rebase-todo.backup\")\n \n /*\n  * The rebase command lines that have already been processed. A line\ndiff --git a/sequencer.h b/sequencer.h\nindex 4a051e5dc9..6a1a23541f 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -4,6 +4,7 @@\n const char *git_path_commit_editmsg(void);\n const char *git_path_seq_dir(void);\n const char *rebase_path_todo(void);\n+const char *rebase_path_todo_backup(void);\n \n #define APPEND_SIGNOFF_DEDUP (1u << 0)\n \n-- \n2.19.1\n\n"},{"id":"361813","messageId":"20181027212930.9303-16-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"[PATCH v2 15/16] sequencer: fix a call to error() in transform_todo_file()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-27T21:29:29Z","receivedAt":"2018-10-27T21:30:14Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This replaces a call to error() by a call to error_errno() after writing\nthe content of the todo list to the disk in transform_todo_file().\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v1, was commit 14/15.\n\n sequencer.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 0763eeae25..3069d4c6a2 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4351,7 +4351,7 @@ int transform_todo_file(unsigned flags)\n \tint res;\n \n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n \tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n-- \n2.19.1\n\n"},{"id":"361816","messageId":"20181027212930.9303-15-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"[PATCH v2 14/16] sequencer: use edit_todo_list() in complete_action()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-27T21:29:28Z","receivedAt":"2018-10-27T21:30:14Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This changes complete_action() to use edit_todo_list(), now that it can\nhandle the initial edit of the todo list.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 22 +++++++---------------\n 1 file changed, 7 insertions(+), 15 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a03505f582..0763eeae25 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4457,7 +4457,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \tstruct todo_list new_todo = TODO_LIST_INIT;\n \tstruct strbuf *buf = &todo_list->buf;\n \tstruct object_id oid;\n-\tint command_count;\n+\tint command_count, res;\n \n \tget_oid(onto, &oid);\n \tshortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);\n@@ -4484,24 +4484,16 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\tif (todo_list_write_to_file(todo_list, todo_file,\n-\t\t\t\t    shortrevisions, shortonto, command_count,\n-\t\t\t\t    1, -1, flags | TODO_LIST_SHORTEN_IDS))\n-\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n-\n-\tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n-\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n-\t\t\t     rebase_path_todo_backup());\n-\n-\tif (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {\n+\tres = edit_todo_list(todo_list, &new_todo, flags,\n+\t\t\t     command_count, shortrevisions, shortonto);\n+\tif (res == -1)\n+\t\treturn -1;\n+\telse if (res == -2) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n \n \t\treturn -1;\n-\t}\n-\n-\tstrbuf_stripspace(&new_todo.buf, 1);\n-\tif (new_todo.buf.len == 0) {\n+\t} else if (res == -3) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n \t\ttodo_list_release(&new_todo);\n-- \n2.19.1\n\n"},{"id":"361819","messageId":"20181027212930.9303-17-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"[PATCH v2 16/16] rebase--interactive: move transform_todo_file() to rebase--interactive.c","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-27T21:29:30Z","receivedAt":"2018-10-27T21:30:16Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"As transform_todo_file() is only needed inside of rebase--interactive.c,\nit is moved there from sequencer.c.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 20 ++++++++++++++++++++\n sequencer.c                   | 20 --------------------\n sequencer.h                   |  1 -\n 3 files changed, 20 insertions(+), 21 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 0f83422aa0..28e6947ecb 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -34,6 +34,26 @@ static int edit_todo_file(unsigned flags)\n \treturn 0;\n }\n \n+static int transform_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\tres = todo_list_write_to_file(&todo_list, todo_file,\n+\t\t\t\t      NULL, NULL, 0, 0, -1, flags);\n+\ttodo_list_release(&todo_list);\n+\treturn res;\n+}\n+\n static int get_revision_ranges(const char *upstream, const char *onto,\n \t\t\t       const char **head_hash,\n \t\t\t       char **revisions, char **shortrevisions)\ndiff --git a/sequencer.c b/sequencer.c\nindex 3069d4c6a2..a1b81bd544 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4344,26 +4344,6 @@ int todo_list_write_to_file(struct todo_list *todo_list, const char *file,\n \treturn res;\n }\n \n-int transform_todo_file(unsigned flags)\n-{\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res;\n-\n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tres = todo_list_write_to_file(&todo_list, todo_file,\n-\t\t\t\t      NULL, NULL, 0, 0, -1, flags);\n-\ttodo_list_release(&todo_list);\n-\treturn res;\n-}\n-\n static const char edit_todo_list_advice[] =\n N_(\"You can fix this with 'git rebase --edit-todo' \"\n \"and then run 'git rebase --continue'.\\n\"\ndiff --git a/sequencer.h b/sequencer.h\nindex 6a1a23541f..d35ca61d5d 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -134,7 +134,6 @@ int sequencer_make_script(struct strbuf *out, int argc, const char **argv,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(const char *command);\n-int transform_todo_file(unsigned flags);\n int check_todo_list_from_file(void);\n int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-- \n2.19.1\n\n"},{"id":"361862","messageId":"xmqqd0rt8nao.fsf@gitster-ct.c.googlers.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"Re: [PATCH v2 00/16] sequencer: refactor functions working on a todo_list","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2018-10-29T03:05:51Z","receivedAt":"2018-10-29T03:05:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Alban Gruin <alban.gruin@gmail.com> writes:\n\n> At the center of the \"interactive\" part of the interactive rebase lies\n> the todo list.  When the user starts an interactive rebase, a todo list\n> is generated, presented to the user (who then edits it using a text\n> editor), read back, and then is checked and processed before the actual\n> rebase takes place.\n>\n> Some of this processing includes adding execs commands, reordering\n> fixup! and squash! commits, and checking if no commits were accidentally\n> dropped by the user.\n>\n> Before I converted the interactive rebase in C, these functions were\n> called by git-rebase--interactive.sh through git-rebase--helper.  Since\n> the only way to pass around a large amount of data between a shell\n> script and a C program is to use a file (or any declination of a file),\n> the functions that checked and processed the todo list were directly\n> working on a file, the same file that the user edited.\n>\n> During the conversion, I did not address this issue, which lead to a\n> complete_action() that reads the todo list file, does some computation\n> based on its content, and writes it back to the disk, several times in\n> the same function.\n>\n> As it is not an efficient way to handle a data structure, this patch\n> series refactor the functions that processes the todo list to work on a\n> todo_list structure instead of reading it from the disk.\n>\n> Some commits consists in modifying edit_todo_list() (initially used by\n> --edit-todo) to handle the initial edition of the todo list, to increase\n> code sharing.\n>\n> It is based onto ag/rebase-i-in-c (34b4731, \"rebase -i: move\n> rebase--helper modes to rebase--interactive\").\n\nAs there are quite a lot of fixes to the sequencer machinery since\nthat topic forked from the mainline.  For example, [06/16] has\nunpleasant merge conflicts with 1ace63bc (\"rebase --exec: make it\nwork with --rebase-merges\", 2018-08-09) that has been in master for\nthe past couple of months.  IOW, the tip of ag/rebase-i-in-c is a\nbit too old a base to work on by now.  \n\nI think I queued the previous round on the result of merging\nag/rebase-i-in-c into master, i.e. 61dc7b24 (\"Merge branch\n'ag/rebase-i-in-c' into ag/sequencer-reduce-rewriting-todo\",\n2018-10-09).  That may be a more reasonable place to start this\nupdate on.\n\n"},{"id":"361909","messageId":"ea891987-1030-2208-e76c-567e6c9777d0@gmail.com","threadId":"49504","inReplyTo":"xmqqd0rt8nao.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 00/16] sequencer: refactor functions working on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-10-29T15:34:18Z","receivedAt":"2018-10-29T15:34:29Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Hi Junio,\n\nLe 29/10/2018 à 04:05, Junio C Hamano a écrit :\n> Alban Gruin <alban.gruin@gmail.com> writes:\n[…]\n>> It is based onto ag/rebase-i-in-c (34b4731, \"rebase -i: move\n>> rebase--helper modes to rebase--interactive\").\n> \n> As there are quite a lot of fixes to the sequencer machinery since\n> that topic forked from the mainline.  For example, [06/16] has\n> unpleasant merge conflicts with 1ace63bc (\"rebase --exec: make it\n> work with --rebase-merges\", 2018-08-09) that has been in master for\n> the past couple of months.  IOW, the tip of ag/rebase-i-in-c is a\n> bit too old a base to work on by now.  \n> \n> I think I queued the previous round on the result of merging\n> ag/rebase-i-in-c into master, i.e. 61dc7b24 (\"Merge branch\n> 'ag/rebase-i-in-c' into ag/sequencer-reduce-rewriting-todo\",\n> 2018-10-09).  That may be a more reasonable place to start this\n> update on.\n> \n\nRight.\n\nMy next iteration will be based on 61dc7b24, I just rebased my branch\nonto it.\n\n"},{"id":"361996","messageId":"03475c29-5317-b105-6102-5cae3a5ae926@talktalk.net","threadId":"49504","inReplyTo":"20181027212930.9303-5-alban.gruin@gmail.com","subject":"Re: [PATCH v2 04/16] sequencer: introduce todo_list_write_to_file()","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2018-10-30T16:28:53Z","receivedAt":"2018-10-30T16:28:59Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nI like the direction this is going, it is an improvement on re-scanning \nthe list at the end of each function.\n\nOn 27/10/2018 22:29, Alban Gruin wrote:\n> This introduce a new function to recreate the text of a todo list from\n> its commands, and then to write it to the disk.  This will be useful in\n> the future, the buffer of a todo list won’t be treated as a strict\n> mirror of the todo file by some of its functions once they will be\n> refactored.\n\nI'd suggest rewording this slightly, maybe something like\n\nThis introduces a new function to recreate the text of a todo list from\nits commands and write it to a file. This will be useful as the next few \ncommits will change the use of the buffer in struct todo_list so it will \nno-longer be a mirror of the file on disk.\n\n> This functionnality can already be found in todo_list_transform(), but\n\ns/functionnality/functionality/\n\n> it is specifically made to replace the buffer of a todo list, which is\n> not the desired behaviour.  Thus, the part of todo_list_transform() that\n> actually creates the buffer is moved to a new function,\n> todo_list_to_strbuf().  The rest is unused, and so is dropped.\n> \n> todo_list_write_to_file() can also take care to append the help text to\n\ns/care to append/care of appending/\n\n> the buffer before writing it to the disk, or to write only the first n\n> items of the list.\n\nWhy/when do we only want to write a subset of the items?\n\n> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n> ---\n>   sequencer.c | 59 ++++++++++++++++++++++++++++++++++++-----------------\n>   sequencer.h |  4 +++-\n>   2 files changed, 43 insertions(+), 20 deletions(-)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index 07296f1f57..0dd45677b1 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -4256,24 +4256,27 @@ int sequencer_add_exec_commands(const char *commands)\n>   \treturn i;\n>   }\n>   \n> -void todo_list_transform(struct todo_list *todo_list, unsigned flags)\n> +static void todo_list_to_strbuf(struct todo_list *todo_list, struct strbuf *buf,\n> +\t\t\t\tint num, unsigned flags)\n>   {\n> -\tstruct strbuf buf = STRBUF_INIT;\n>   \tstruct todo_item *item;\n> -\tint i;\n> +\tint i, max = todo_list->nr;\n>   \n> -\tfor (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {\n> +\tif (num > 0 && num < max)\n> +\t\tmax = num;\n> +\n> +\tfor (item = todo_list->items, i = 0; i < max; i++, item++) {\n>   \t\t/* if the item is not a command write it and continue */\n>   \t\tif (item->command >= TODO_COMMENT) {\n> -\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len, item->arg);\n> +\t\t\tstrbuf_addf(buf, \"%.*s\\n\", item->arg_len, item->arg);\n>   \t\t\tcontinue;\n>   \t\t}\n>   \n>   \t\t/* add command to the buffer */\n>   \t\tif (flags & TODO_LIST_ABBREVIATE_CMDS)\n> -\t\t\tstrbuf_addch(&buf, command_to_char(item->command));\n> +\t\t\tstrbuf_addch(buf, command_to_char(item->command));\n>   \t\telse\n> -\t\t\tstrbuf_addstr(&buf, command_to_string(item->command));\n> +\t\t\tstrbuf_addstr(buf, command_to_string(item->command));\n>   \n>   \t\t/* add commit id */\n>   \t\tif (item->commit) {\n> @@ -4283,27 +4286,46 @@ void todo_list_transform(struct todo_list *todo_list, unsigned flags)\n>   \n>   \t\t\tif (item->command == TODO_MERGE) {\n>   \t\t\t\tif (item->flags & TODO_EDIT_MERGE_MSG)\n> -\t\t\t\t\tstrbuf_addstr(&buf, \" -c\");\n> +\t\t\t\t\tstrbuf_addstr(buf, \" -c\");\n>   \t\t\t\telse\n> -\t\t\t\t\tstrbuf_addstr(&buf, \" -C\");\n> +\t\t\t\t\tstrbuf_addstr(buf, \" -C\");\n>   \t\t\t}\n>   \n> -\t\t\tstrbuf_addf(&buf, \" %s\", oid);\n> +\t\t\tstrbuf_addf(buf, \" %s\", oid);\n>   \t\t}\n>   \n>   \t\t/* add all the rest */\n>   \t\tif (!item->arg_len)\n> -\t\t\tstrbuf_addch(&buf, '\\n');\n> +\t\t\tstrbuf_addch(buf, '\\n');\n>   \t\telse\n> -\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len, item->arg);\n> +\t\t\tstrbuf_addf(buf, \" %.*s\\n\", item->arg_len, item->arg);\n>   \t}\n> +}\n>   \n> -\tstrbuf_reset(&todo_list->buf);\n> -\tstrbuf_add(&todo_list->buf, buf.buf, buf.len);\n> +int todo_list_write_to_file(struct todo_list *todo_list, const char *file,\n> +\t\t\t    const char *shortrevisions, const char *shortonto,\n> +\t\t\t    int command_count, int append_help, int num, unsigned flags)\n\nThis is a really long argument list which makes it easy for callers to \nget the parameters in the wrong order. I think append_help could \nprobably be folded into the flags, I'm not sure what the command_count \nis used for but I've only read the first few patches. Maybe it would be \nbetter to pass a struct so we have named fields.\n\nBest Wishes\n\nPhillip\n\n> +{\n> +\tint edit_todo = !(shortrevisions && shortonto), res;\n> +\tstruct strbuf buf = STRBUF_INIT;\n> +\n> +\ttodo_list_to_strbuf(todo_list, &buf, num, flags);\n> +\n> +\tif (append_help) {\n> +\t\tif (!edit_todo) {\n> +\t\t\tstrbuf_addch(&buf, '\\n');\n> +\t\t\tstrbuf_commented_addf(&buf, Q_(\"Rebase %s onto %s (%d command)\",\n> +\t\t\t\t\t\t       \"Rebase %s onto %s (%d commands)\",\n> +\t\t\t\t\t\t       command_count),\n> +\t\t\t\t\t      shortrevisions, shortonto, command_count);\n> +\t\t}\n> +\t\tappend_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, &buf);\n> +\t}\n> +\n> +\tres = write_message(buf.buf, buf.len, file, 0);\n>   \tstrbuf_release(&buf);\n>   \n> -\tif (todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list))\n> -\t\tBUG(\"unusable todo list\");\n> +\treturn res;\n>   }\n>   \n>   int transform_todo_file(unsigned flags)\n> @@ -4320,9 +4342,8 @@ int transform_todo_file(unsigned flags)\n>   \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n>   \t}\n>   \n> -\ttodo_list_transform(&todo_list, flags);\n> -\n> -\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n> +\tres = todo_list_write_to_file(&todo_list, todo_file,\n> +\t\t\t\t      NULL, NULL, 0, 0, -1, flags);\n>   \ttodo_list_release(&todo_list);\n>   \treturn res;\n>   }\n> diff --git a/sequencer.h b/sequencer.h\n> index 029d842ac5..a299c977fe 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -113,7 +113,9 @@ struct todo_list {\n>   #define TODO_LIST_INIT { STRBUF_INIT }\n>   \n>   int todo_list_parse_insn_buffer(char *buf, struct todo_list *todo_list);\n> -void todo_list_transform(struct todo_list *todo_list, unsigned flags);\n> +int todo_list_write_to_file(struct todo_list *todo_list, const char *file,\n> +\t\t\t    const char *shortrevisions, const char *shortonto,\n> +\t\t\t    int command_count, int append_help, int num, unsigned flags);\n>   void todo_list_release(struct todo_list *todo_list);\n>   \n>   /* Call this to setup defaults before parsing command line options */\n> \n\n"},{"id":"361997","messageId":"12e849d3-351b-7673-1f14-742d0fbb1ac1@talktalk.net","threadId":"49504","inReplyTo":"20181027212930.9303-7-alban.gruin@gmail.com","subject":"Re: [PATCH v2 06/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2018-10-30T16:47:21Z","receivedAt":"2018-10-30T16:47:26Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 27/10/2018 22:29, Alban Gruin wrote:\n> This refactors sequencer_add_exec_commands() to work on a todo_list to\n> avoid redundant reads and writes to the disk.\n> \n> An obvious way to do this would be to insert the `exec' command between\n> the other commands, and reparse it once this is done.  This is not what\n> is done here.  Instead, the command is appended to the buffer once, and\n> a new list of items is created.  Items from the old list are copied to\n> it, and new `exec' items are appended when necessary.  This eliminates\n> the need to reparse the todo list, but this also means its buffer cannot\n> be directly written to the disk, hence todo_list_write_to_disk().\n\nI'd reword this slightly, maybe\n\nInstead of just inserting the `exec' command between the other commands, \nand re-parsing the buffer at the end the exec command is appended to the \nbuffer once, and a new list of items is created.  Items from the old \nlist are copied across and new `exec' items are appended when necessary. \n  This eliminates the need to reparse the buffer, but this also means we \nhave to use todo_list_write_to_disk() to write the file.\n\n> sequencer_add_exec_commands() still reads the todo list from the disk,\n> as it is needed by rebase -p.  todo_list_add_exec_commands() works on a\n> todo_list structure, and reparses it at the end.\n\nI think the saying 'reparses' is confusing as that is what we're trying \nto avoid.\n\n> complete_action() still uses sequencer_add_exec_commands() for now.\n> This will be changed in a future commit.\n> \n> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n> ---\n>   sequencer.c | 69 +++++++++++++++++++++++++++++++++++++----------------\n>   1 file changed, 49 insertions(+), 20 deletions(-)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index e12860c047..12a3efeca8 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -4216,6 +4216,50 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,\n>   \treturn 0;\n>   }\n>   \n> +static void todo_list_add_exec_commands(struct todo_list *todo_list,\n> +\t\t\t\t\tconst char *commands)\n> +{\n> +\tstruct strbuf *buf = &todo_list->buf;\n> +\tconst char *old_buf = buf->buf;\n> +\tsize_t commands_len = strlen(commands + strlen(\"exec \")) - 1;\n> +\tint i, first = 1, nr = 0, alloc = 0;\n\nMinor nit pick, I think it is clearer if first is initialized just \nbefore the loop as it is in the deleted code below.\n\n> +\tstruct todo_item *items = NULL,\n> +\t\tbase_item = {TODO_EXEC, NULL, 0, 0, commands_len, 0};\n> +\n> +\tstrbuf_addstr(buf, commands);\n> +\tbase_item.offset_in_buf = buf->len - commands_len - 1;\n> +\tbase_item.arg = buf->buf + base_item.offset_in_buf;\n\nI think if the user gives --exec more than once on the command line then \ncommands will contain more than one exec command so this needs to parse \ncommands and create one todo_item for each command.\n\n> +\n> +\t/*\n> +\t * Insert <commands> after every pick. Here, fixup/squash chains\n> +\t * are considered part of the pick, so we insert the commands *after*\n> +\t * those chains if there are any.\n> +\t */\n> +\tfor (i = 0; i < todo_list->nr; i++) {\n> +\t\tenum todo_command command = todo_list->items[i].command;\n> +\t\tif (todo_list->items[i].arg)\n> +\t\t\ttodo_list->items[i].arg = todo_list->items[i].arg - old_buf + buf->buf;\n> +\n> +\t\tif (command == TODO_PICK && !first) {\n> +\t\t\tALLOC_GROW(items, nr + 1, alloc);\n> +\t\t\tmemcpy(items + nr++, &base_item, sizeof(struct todo_item));\n\nI think it would be clearer to say\n\titems[nr++] = base_item;\nrather than using memcpy. This applies below and to some of the other \npatches as well. Also this needs to loop over all the base_items if the \nuser gave --exec more than once on the command line.\n\nBest Wishes\n\nPhillip\n\n> +\t\t}\n> +\n> +\t\tALLOC_GROW(items, nr + 1, alloc);\n> +\t\tmemcpy(items + nr++, todo_list->items + i, sizeof(struct todo_item));\n> +\t\tfirst = 0;\n> +\t}\n> +\n> +\t/* insert or append final <commands> */\n> +\tALLOC_GROW(items, nr + 1, alloc);\n> +\tmemcpy(items + nr++, &base_item, sizeof(struct todo_item));\n> +\n> +\tFREE_AND_NULL(todo_list->items);\n> +\ttodo_list->items = items;\n> +\ttodo_list->nr = nr;\n> +\ttodo_list->alloc = alloc;\n> +}\n> +\n>   /*\n>    * Add commands after pick and (series of) squash/fixup commands\n>    * in the todo list.\n> @@ -4224,10 +4268,7 @@ int sequencer_add_exec_commands(const char *commands)\n>   {\n>   \tconst char *todo_file = rebase_path_todo();\n>   \tstruct todo_list todo_list = TODO_LIST_INIT;\n> -\tstruct todo_item *item;\n> -\tstruct strbuf *buf = &todo_list.buf;\n> -\tsize_t offset = 0, commands_len = strlen(commands);\n> -\tint i, first;\n> +\tint res;\n>   \n>   \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>   \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n> @@ -4237,23 +4278,11 @@ int sequencer_add_exec_commands(const char *commands)\n>   \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n>   \t}\n>   \n> -\tfirst = 1;\n> -\t/* insert <commands> before every pick except the first one */\n> -\tfor (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {\n> -\t\tif (item->command == TODO_PICK && !first) {\n> -\t\t\tstrbuf_insert(buf, item->offset_in_buf + offset,\n> -\t\t\t\t      commands, commands_len);\n> -\t\t\toffset += commands_len;\n> -\t\t}\n> -\t\tfirst = 0;\n> -\t}\n> -\n> -\t/* append final <commands> */\n> -\tstrbuf_add(buf, commands, commands_len);\n> -\n> -\ti = write_message(buf->buf, buf->len, todo_file, 0);\n> +\ttodo_list_add_exec_commands(&todo_list, commands);\n> +\tres = todo_list_write_to_file(&todo_list, todo_file, NULL, NULL, 0, 0, -1, 0);\n>   \ttodo_list_release(&todo_list);\n> -\treturn i;\n> +\n> +\treturn res;\n>   }\n>   \n>   static void todo_list_to_strbuf(struct todo_list *todo_list, struct strbuf *buf,\n> \n\n"},{"id":"362215","messageId":"8b235228-fa53-cf5d-6ece-b28d030e1751@gmail.com","threadId":"49504","inReplyTo":"03475c29-5317-b105-6102-5cae3a5ae926@talktalk.net","subject":"Re: [PATCH v2 04/16] sequencer: introduce todo_list_write_to_file()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-01T23:31:34Z","receivedAt":"2018-11-01T23:31:48Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Hi Phillip,\n\nLe 30/10/2018 à 17:28, Phillip Wood a écrit :\n> Hi Alban\n> \n> I like the direction this is going, it is an improvement on re-scanning\n> the list at the end of each function.\n> \n> On 27/10/2018 22:29, Alban Gruin wrote:\n>> This introduce a new function to recreate the text of a todo list from\n>> its commands, and then to write it to the disk.  This will be useful in\n>> the future, the buffer of a todo list won’t be treated as a strict\n>> mirror of the todo file by some of its functions once they will be\n>> refactored.\n> \n> I'd suggest rewording this slightly, maybe something like\n> \n> This introduces a new function to recreate the text of a todo list from\n> its commands and write it to a file. This will be useful as the next few\n> commits will change the use of the buffer in struct todo_list so it will\n> no-longer be a mirror of the file on disk.\n> \n>> This functionnality can already be found in todo_list_transform(), but\n> \n> s/functionnality/functionality/\n> \n>> it is specifically made to replace the buffer of a todo list, which is\n>> not the desired behaviour.  Thus, the part of todo_list_transform() that\n>> actually creates the buffer is moved to a new function,\n>> todo_list_to_strbuf().  The rest is unused, and so is dropped.\n>>\n>> todo_list_write_to_file() can also take care to append the help text to\n> \n> s/care to append/care of appending/\n> \n>> the buffer before writing it to the disk, or to write only the first n\n>> items of the list.\n> \n> Why/when do we only want to write a subset of the items?\n>\n\nIn skip_unnecessary_picks(), in patch [10/16].  It needs to write the\nelements of the todo list that were already done in the `done' file.\n\n> […]\n>> +int todo_list_write_to_file(struct todo_list *todo_list, const char\n>> *file,\n>> +                const char *shortrevisions, const char *shortonto,\n>> +                int command_count, int append_help, int num, unsigned\n>> flags)\n> \n> This is a really long argument list which makes it easy for callers to\n> get the parameters in the wrong order. I think append_help could\n> probably be folded into the flags, I'm not sure what the command_count\n> is used for but I've only read the first few patches. Maybe it would be\n> better to pass a struct so we have named fields.\n> \n\nYou’re right, command_count is not really needed since we pass the\ncomplete todo list.\n\nThe only bit that irks me is that, if I stop passing command_count, I\nwould have to call count_commands() twice in complete_action(): once to\ncheck if there are any commands in the todo list, and again inside of\ntodo_list_write_to_file() (see [09/16].)\n\nPerhaps I could move this check before calling todo_list_rearrange_squash()?\n\nAs a sidenote, this is not why I added command_count to the parameters\nof todo_list_write_to_file().  It was a confusion of my part.\n\n> Best Wishes\n> \n> Phillip\n> \n\nCheers,\nAlban\n\n"},{"id":"362216","messageId":"c57384de-0996-2294-db8f-0204f9c16226@gmail.com","threadId":"49504","inReplyTo":"12e849d3-351b-7673-1f14-742d0fbb1ac1@talktalk.net","subject":"Re: [PATCH v2 06/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-01T23:31:49Z","receivedAt":"2018-11-01T23:31:55Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Le 30/10/2018 à 17:47, Phillip Wood a écrit :\n> On 27/10/2018 22:29, Alban Gruin wrote:\n>> This refactors sequencer_add_exec_commands() to work on a todo_list to\n>> avoid redundant reads and writes to the disk.\n>>\n>> An obvious way to do this would be to insert the `exec' command between\n>> the other commands, and reparse it once this is done.  This is not what\n>> is done here.  Instead, the command is appended to the buffer once, and\n>> a new list of items is created.  Items from the old list are copied to\n>> it, and new `exec' items are appended when necessary.  This eliminates\n>> the need to reparse the todo list, but this also means its buffer cannot\n>> be directly written to the disk, hence todo_list_write_to_disk().\n> \n> I'd reword this slightly, maybe\n> \n> Instead of just inserting the `exec' command between the other commands,\n> and re-parsing the buffer at the end the exec command is appended to the\n> buffer once, and a new list of items is created.  Items from the old\n> list are copied across and new `exec' items are appended when necessary.\n>  This eliminates the need to reparse the buffer, but this also means we\n> have to use todo_list_write_to_disk() to write the file.\n> \n>> sequencer_add_exec_commands() still reads the todo list from the disk,\n>> as it is needed by rebase -p.  todo_list_add_exec_commands() works on a\n>> todo_list structure, and reparses it at the end.\n> \n> I think the saying 'reparses' is confusing as that is what we're trying\n> to avoid.\n> \n>> complete_action() still uses sequencer_add_exec_commands() for now.\n>> This will be changed in a future commit.\n>>\n>> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n>> ---\n>>   sequencer.c | 69 +++++++++++++++++++++++++++++++++++++----------------\n>>   1 file changed, 49 insertions(+), 20 deletions(-)\n>>\n>> diff --git a/sequencer.c b/sequencer.c\n>> index e12860c047..12a3efeca8 100644\n>> --- a/sequencer.c\n>> +++ b/sequencer.c\n>> @@ -4216,6 +4216,50 @@ int sequencer_make_script(FILE *out, int argc,\n>> const char **argv,\n>>       return 0;\n>>   }\n>>   +static void todo_list_add_exec_commands(struct todo_list *todo_list,\n>> +                    const char *commands)\n>> +{\n>> +    struct strbuf *buf = &todo_list->buf;\n>> +    const char *old_buf = buf->buf;\n>> +    size_t commands_len = strlen(commands + strlen(\"exec \")) - 1;\n>> +    int i, first = 1, nr = 0, alloc = 0;\n> \n> Minor nit pick, I think it is clearer if first is initialized just\n> before the loop as it is in the deleted code below.\n> \n>> +    struct todo_item *items = NULL,\n>> +        base_item = {TODO_EXEC, NULL, 0, 0, commands_len, 0};\n>> +\n>> +    strbuf_addstr(buf, commands);\n>> +    base_item.offset_in_buf = buf->len - commands_len - 1;\n>> +    base_item.arg = buf->buf + base_item.offset_in_buf;\n> \n> I think if the user gives --exec more than once on the command line then\n> commands will contain more than one exec command so this needs to parse\n> commands and create one todo_item for each command.\n> \n\nOuch, you’re right.  Thanks for the heads up.\n\n>> +\n>> +    /*\n>> +     * Insert <commands> after every pick. Here, fixup/squash chains\n>> +     * are considered part of the pick, so we insert the commands\n>> *after*\n>> +     * those chains if there are any.\n>> +     */\n>> +    for (i = 0; i < todo_list->nr; i++) {\n>> +        enum todo_command command = todo_list->items[i].command;\n>> +        if (todo_list->items[i].arg)\n>> +            todo_list->items[i].arg = todo_list->items[i].arg -\n>> old_buf + buf->buf;\n>> +\n>> +        if (command == TODO_PICK && !first) {\n>> +            ALLOC_GROW(items, nr + 1, alloc);\n>> +            memcpy(items + nr++, &base_item, sizeof(struct todo_item));\n> \n> I think it would be clearer to say\n>     items[nr++] = base_item;\n> rather than using memcpy. This applies below and to some of the other\n> patches as well. Also this needs to loop over all the base_items if the\n> user gave --exec more than once on the command line.\n> \n\nI agree with you, it’s way more readable, IMO.  But for some reason, I\nthought it was not possible to assign a struct to another in C.\n\n> Best Wishes\n> \n> Phillip\n> \n\nCheers,\nAlban\n\n"},{"id":"362261","messageId":"351e3398-8239-5348-2880-d253d8a125d7@talktalk.net","threadId":"49504","inReplyTo":"c57384de-0996-2294-db8f-0204f9c16226@gmail.com","subject":"Re: [PATCH v2 06/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2018-11-02T10:09:52Z","receivedAt":"2018-11-02T10:10:11Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nOn 01/11/2018 23:31, Alban Gruin wrote:\n> Le 30/10/2018 à 17:47, Phillip Wood a écrit :\n>> On 27/10/2018 22:29, Alban Gruin wrote:\n>>> This refactors sequencer_add_exec_commands() to work on a todo_list to\n>>> avoid redundant reads and writes to the disk.\n>>>\n>>> An obvious way to do this would be to insert the `exec' command between\n>>> the other commands, and reparse it once this is done.  This is not what\n>>> is done here.  Instead, the command is appended to the buffer once, and\n>>> a new list of items is created.  Items from the old list are copied to\n>>> it, and new `exec' items are appended when necessary.  This eliminates\n>>> the need to reparse the todo list, but this also means its buffer cannot\n>>> be directly written to the disk, hence todo_list_write_to_disk().\n>>\n>> I'd reword this slightly, maybe\n>>\n>> Instead of just inserting the `exec' command between the other commands,\n>> and re-parsing the buffer at the end the exec command is appended to the\n>> buffer once, and a new list of items is created.  Items from the old\n>> list are copied across and new `exec' items are appended when necessary.\n>>  This eliminates the need to reparse the buffer, but this also means we\n>> have to use todo_list_write_to_disk() to write the file.\n>>\n>>> sequencer_add_exec_commands() still reads the todo list from the disk,\n>>> as it is needed by rebase -p.  todo_list_add_exec_commands() works on a\n>>> todo_list structure, and reparses it at the end.\n>>\n>> I think the saying 'reparses' is confusing as that is what we're trying\n>> to avoid.\n>>\n>>> complete_action() still uses sequencer_add_exec_commands() for now.\n>>> This will be changed in a future commit.\n>>>\n>>> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n>>> ---\n>>>   sequencer.c | 69 +++++++++++++++++++++++++++++++++++++----------------\n>>>   1 file changed, 49 insertions(+), 20 deletions(-)\n>>>\n>>> diff --git a/sequencer.c b/sequencer.c\n>>> index e12860c047..12a3efeca8 100644\n>>> --- a/sequencer.c\n>>> +++ b/sequencer.c\n>>> @@ -4216,6 +4216,50 @@ int sequencer_make_script(FILE *out, int argc,\n>>> const char **argv,\n>>>       return 0;\n>>>   }\n>>>   +static void todo_list_add_exec_commands(struct todo_list *todo_list,\n>>> +                    const char *commands)\n>>> +{\n>>> +    struct strbuf *buf = &todo_list->buf;\n>>> +    const char *old_buf = buf->buf;\n>>> +    size_t commands_len = strlen(commands + strlen(\"exec \")) - 1;\n>>> +    int i, first = 1, nr = 0, alloc = 0;\n>>\n>> Minor nit pick, I think it is clearer if first is initialized just\n>> before the loop as it is in the deleted code below.\n>>\n>>> +    struct todo_item *items = NULL,\n>>> +        base_item = {TODO_EXEC, NULL, 0, 0, commands_len, 0};\n>>> +\n>>> +    strbuf_addstr(buf, commands);\n>>> +    base_item.offset_in_buf = buf->len - commands_len - 1;\n>>> +    base_item.arg = buf->buf + base_item.offset_in_buf;\n>>\n>> I think if the user gives --exec more than once on the command line then\n>> commands will contain more than one exec command so this needs to parse\n>> commands and create one todo_item for each command.\n>>\n> \n> Ouch, you’re right.  Thanks for the heads up.\n\nI haven't looked how difficult it would be but it might be best to\nchange the option parsing to pass an array of strings containing the\nexec commands rather than one long string so we can just loop over the\narray here.\n\n> \n>>> +\n>>> +    /*\n>>> +     * Insert <commands> after every pick. Here, fixup/squash chains\n>>> +     * are considered part of the pick, so we insert the commands\n>>> *after*\n>>> +     * those chains if there are any.\n>>> +     */\n>>> +    for (i = 0; i < todo_list->nr; i++) {\n>>> +        enum todo_command command = todo_list->items[i].command;\n>>> +        if (todo_list->items[i].arg)\n>>> +            todo_list->items[i].arg = todo_list->items[i].arg -\n>>> old_buf + buf->buf;\n>>> +\n>>> +        if (command == TODO_PICK && !first) {\n>>> +            ALLOC_GROW(items, nr + 1, alloc);\n>>> +            memcpy(items + nr++, &base_item, sizeof(struct todo_item));\n>>\n>> I think it would be clearer to say\n>>     items[nr++] = base_item;\n>> rather than using memcpy. This applies below and to some of the other\n>> patches as well. Also this needs to loop over all the base_items if the\n>> user gave --exec more than once on the command line.\n>>\n> \n> I agree with you, it’s way more readable, IMO.  But for some reason, I\n> thought it was not possible to assign a struct to another in C.\n\nIn general one needs to be careful as it is only does a shallow copy but\nthat is exactly what we want here. Having said that if we have an array\nof exec commands to insert then it might be worth sticking with memcpy()\nhere so the whole array can be copied in one go.\n\nBest Wishes\n\nPhillip\n\n>> Best Wishes\n>>\n>> Phillip\n>>\n> \n> Cheers,\n> Alban\n> \n\n"},{"id":"362282","messageId":"878848f4-f8f6-25cb-061e-5f7aef9ca1a9@gmail.com","threadId":"49504","inReplyTo":"351e3398-8239-5348-2880-d253d8a125d7@talktalk.net","subject":"Re: [PATCH v2 06/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-02T16:26:53Z","receivedAt":"2018-11-02T16:27:16Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Hi Phillip,\n\nLe 02/11/2018 à 11:09, Phillip Wood a écrit :\n>>>> +    struct todo_item *items = NULL,\n>>>> +        base_item = {TODO_EXEC, NULL, 0, 0, commands_len, 0};\n>>>> +\n>>>> +    strbuf_addstr(buf, commands);\n>>>> +    base_item.offset_in_buf = buf->len - commands_len - 1;\n>>>> +    base_item.arg = buf->buf + base_item.offset_in_buf;\n>>>\n>>> I think if the user gives --exec more than once on the command line then\n>>> commands will contain more than one exec command so this needs to parse\n>>> commands and create one todo_item for each command.\n>>>\n>>\n>> Ouch, you’re right.  Thanks for the heads up.\n> \n> I haven't looked how difficult it would be but it might be best to\n> change the option parsing to pass an array of strings containing the\n> exec commands rather than one long string so we can just loop over the\n> array here.\n> \n\nIt would be the best way to do so.  This string comes from git-rebase.sh\n(or builtin/rebase.c) -- they format it this way before invoking\ngit-rebase--interactive.  So either I modify both of them (for this, I\nwould need to rebase my branch on master), or I can split this string in\nbuiltin/rebase--interactive.c.  I prefer the first option, but maybe\nchanging the base of this series will not please Junio.\n\nCheers,\nAlban\n\n"},{"id":"362287","messageId":"1f16fa71-2ff0-d0b3-0d90-f330788351d8@talktalk.net","threadId":"49504","inReplyTo":"878848f4-f8f6-25cb-061e-5f7aef9ca1a9@gmail.com","subject":"Re: [PATCH v2 06/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2018-11-02T17:09:18Z","receivedAt":"2018-11-02T17:09:22Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nOn 02/11/2018 16:26, Alban Gruin wrote:\n> Hi Phillip,\n> \n> Le 02/11/2018 à 11:09, Phillip Wood a écrit :\n>>>>> +    struct todo_item *items = NULL,\n>>>>> +        base_item = {TODO_EXEC, NULL, 0, 0, commands_len, 0};\n>>>>> +\n>>>>> +    strbuf_addstr(buf, commands);\n>>>>> +    base_item.offset_in_buf = buf->len - commands_len - 1;\n>>>>> +    base_item.arg = buf->buf + base_item.offset_in_buf;\n>>>>\n>>>> I think if the user gives --exec more than once on the command line then\n>>>> commands will contain more than one exec command so this needs to parse\n>>>> commands and create one todo_item for each command.\n>>>>\n>>>\n>>> Ouch, you’re right.  Thanks for the heads up.\n>>\n>> I haven't looked how difficult it would be but it might be best to\n>> change the option parsing to pass an array of strings containing the\n>> exec commands rather than one long string so we can just loop over the\n>> array here.\n>>\n> \n> It would be the best way to do so.  This string comes from git-rebase.sh\n> (or builtin/rebase.c) -- they format it this way before invoking\n> git-rebase--interactive.  So either I modify both of them (for this, I\n> would need to rebase my branch on master), or I can split this string in\n> builtin/rebase--interactive.c.  I prefer the first option, but maybe\n> changing the base of this series will not please Junio.\n\nI think in the last 'what's cooking' email Junio said he was going to \nmerge all the builtin/rebase.c stuff to master so there may not be a \nproblem if you wait a couple of days.\n\nBest Wishes\n\nPhillip\n\n> Cheers,\n> Alban\n> \n\n"},{"id":"362786","messageId":"20181109080805.6350-1-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181027212930.9303-1-alban.gruin@gmail.com","subject":"[PATCH v3 00/16] sequencer: refactor functions working on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-09T08:07:49Z","receivedAt":"2018-11-09T08:08:34Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"At the center of the \"interactive\" part of the interactive rebase lies\nthe todo list.  When the user starts an interactive rebase, a todo list\nis generated, presented to the user (who then edits it using a text\neditor), read back, and then is checked and processed before the actual\nrebase takes place.\n\nSome of this processing includes adding execs commands, reordering\nfixup! and squash! commits, and checking if no commits were accidentally\ndropped by the user.\n\nBefore I converted the interactive rebase in C, these functions were\ncalled by git-rebase--interactive.sh through git-rebase--helper.  Since\nthe only way to pass around a large amount of data between a shell\nscript and a C program is to use a file (or any declination of a file),\nthe functions that checked and processed the todo list were directly\nworking on a file, the same file that the user edited.\n\nDuring the conversion, I did not address this issue, which lead to a\ncomplete_action() that reads the todo list file, does some computation\nbased on its content, and writes it back to the disk, several times in\nthe same function.\n\nAs it is not an efficient way to handle a data structure, this patch\nseries refactor the functions that processes the todo list to work on a\ntodo_list structure instead of reading it from the disk.\n\nSome commits consists in modifying edit_todo_list() (initially used by\n--edit-todo) to handle the initial edition of the todo list, to increase\ncode sharing.\n\nThis is based on 8858448bb4 (\"Ninth batch for 2.20\", 2018-11-06) to\navoid conflicts with 'js/rebase-i-break'.\n\nChanges since v2:\n\n - Clarifying some commit messages\n\n - Reducing the number of parameters in todo_list_write_to_file()\n\n - sequencer_add_exec_commands() now requests a string list instead of a\n   string.\n\n - Replacing calls to memcpy() by shallow copies\n\n - Applying array.cocci.patch\n\nAlban Gruin (16):\n  sequencer: changes in parse_insn_buffer()\n  sequencer: make the todo_list structure public\n  sequencer: refactor transform_todos() to work on a todo_list\n  sequencer: introduce todo_list_write_to_file()\n  sequencer: refactor check_todo_list() to work on a todo_list\n  sequencer: refactor sequencer_add_exec_commands() to work on a\n    todo_list\n  sequencer: refactor rearrange_squash() to work on a todo_list\n  sequencer: make sequencer_make_script() write its script to a strbuf\n  sequencer: change complete_action() to use the refactored functions\n  sequencer: refactor skip_unnecessary_picks() to work on a todo_list\n  rebase-interactive: use todo_list_write_to_file() in edit_todo_list()\n  rebase-interactive: append_todo_help() changes\n  rebase-interactive: rewrite edit_todo_list() to handle the initial\n    edit\n  sequencer: use edit_todo_list() in complete_action()\n  sequencer: fix a call to error() in transform_todo_file()\n  rebase--interactive: move transform_todo_file() to\n    rebase--interactive.c\n\n builtin/rebase--interactive.c |  81 +++--\n rebase-interactive.c          | 142 ++++++--\n rebase-interactive.h          |   8 +-\n sequencer.c                   | 615 +++++++++++++---------------------\n sequencer.h                   |  75 ++++-\n 5 files changed, 481 insertions(+), 440 deletions(-)\n\nRange-diff against v2:\n 1:  55fa1fff03 =  1:  9ae965b73e sequencer: changes in parse_insn_buffer()\n 2:  192fb771ed <  -:  ---------- sequencer: make the todo_list structure public\n -:  ---------- >  2:  9c15cdede4 sequencer: make the todo_list structure public\n 3:  e2f821ffbe =  3:  a5c01d5a95 sequencer: refactor transform_todos() to work on a todo_list\n 4:  0001e8dbde <  -:  ---------- sequencer: introduce todo_list_write_to_file()\n -:  ---------- >  4:  f2781fe4c3 sequencer: introduce todo_list_write_to_file()\n 5:  aa9554d81d =  5:  337e0dce57 sequencer: refactor check_todo_list() to work on a todo_list\n 6:  8bd793caf5 <  -:  ---------- sequencer: refactor sequencer_add_exec_commands() to work on a todo_list\n 7:  e984946cef <  -:  ---------- sequencer: refactor rearrange_squash() to work on a todo_list\n 8:  cf05254acf <  -:  ---------- sequencer: make sequencer_make_script() write its script to a strbuf\n 9:  ac79151792 <  -:  ---------- sequencer: change complete_action() to use the refactored functions\n10:  e227e38b24 <  -:  ---------- sequencer: refactor skip_unnecessary_picks() to work on a todo_list\n11:  5bfd9d3460 <  -:  ---------- rebase-interactive: use todo_list_write_to_file() in edit_todo_list()\n12:  f4d7578a77 <  -:  ---------- rebase-interactive: append_todo_help() changes\n13:  11c43f1dfe <  -:  ---------- rebase-interactive: rewrite edit_todo_list() to handle the initial edit\n14:  e9a6396c26 <  -:  ---------- sequencer: use edit_todo_list() in complete_action()\n -:  ---------- >  6:  2c75eed153 sequencer: refactor sequencer_add_exec_commands() to work on a todo_list\n -:  ---------- >  7:  90e01bc713 sequencer: refactor rearrange_squash() to work on a todo_list\n -:  ---------- >  8:  850261fbf7 sequencer: make sequencer_make_script() write its script to a strbuf\n -:  ---------- >  9:  7bcfe98241 sequencer: change complete_action() to use the refactored functions\n -:  ---------- > 10:  50cdea7ef7 sequencer: refactor skip_unnecessary_picks() to work on a todo_list\n -:  ---------- > 11:  6d4fcf96e0 rebase-interactive: use todo_list_write_to_file() in edit_todo_list()\n -:  ---------- > 12:  3bea4eb9ba rebase-interactive: append_todo_help() changes\n -:  ---------- > 13:  bfe8f568ee rebase-interactive: rewrite edit_todo_list() to handle the initial edit\n -:  ---------- > 14:  0d6499b7c8 sequencer: use edit_todo_list() in complete_action()\n15:  a36d094610 = 15:  21d3d1abd7 sequencer: fix a call to error() in transform_todo_file()\n16:  344e8ceeed <  -:  ---------- rebase--interactive: move transform_todo_file() to rebase--interactive.c\n -:  ---------- > 16:  22fd27fa2f rebase--interactive: move transform_todo_file() to rebase--interactive.c\n-- \n2.19.1.872.ga867da739e\n\n"},{"id":"362787","messageId":"20181109080805.6350-2-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181109080805.6350-1-alban.gruin@gmail.com","subject":"[PATCH v3 01/16] sequencer: changes in parse_insn_buffer()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-09T08:07:50Z","receivedAt":"2018-11-09T08:08:35Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This clears the number of items of a todo_list before parsing it to\nallow to parse the same list multiple times without issues.  As its\nitems are not dynamically allocated, or don’t need to allocate memory,\nno additionnal memory management is required here.\n\nFurthermore, if a line is invalid, the type of the corresponding\ncommand is set to a garbage value, and its argument is defined properly.\nThis will allow to recreate the text of a todo list from its commands,\neven if one of them is incorrect.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v2.\n\n sequencer.c | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 9e1ab3a2a7..07cc91d8db 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2066,6 +2066,8 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n \tchar *p = buf, *next_p;\n \tint i, res = 0, fixup_okay = file_exists(rebase_path_done());\n \n+\ttodo_list->current = todo_list->nr = 0;\n+\n \tfor (i = 1; *p; i++, p = next_p) {\n \t\tchar *eol = strchrnul(p, '\\n');\n \n@@ -2079,7 +2081,10 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n \t\tif (parse_insn_line(item, p, eol)) {\n \t\t\tres = error(_(\"invalid line %d: %.*s\"),\n \t\t\t\ti, (int)(eol - p), p);\n-\t\t\titem->command = TODO_NOOP;\n+\t\t\titem->command = TODO_COMMENT + 1;\n+\t\t\titem->arg = p;\n+\t\t\titem->arg_len = (int)(eol - p);\n+\t\t\titem->commit = NULL;\n \t\t}\n \n \t\tif (fixup_okay)\n-- \n2.19.1.872.ga867da739e\n\n"},{"id":"362788","messageId":"20181109080805.6350-4-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181109080805.6350-1-alban.gruin@gmail.com","subject":"[PATCH v3 03/16] sequencer: refactor transform_todos() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-09T08:07:52Z","receivedAt":"2018-11-09T08:08:38Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors transform_todos() to work on a todo_list.  The function\nis renamed todo_list_transform().\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, transform_todo_file().  It is still used by\ncomplete_action() and edit_todo_list() for now, but they will be\nreplaced in a future commit.\n\ntodo_list_transform() is not a static function, because it will be used\nby edit_todo_list() from rebase-interactive.c in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v2.\n\n builtin/rebase--interactive.c |  2 +-\n rebase-interactive.c          |  4 +--\n sequencer.c                   | 46 +++++++++++++++++++++++------------\n sequencer.h                   |  3 ++-\n 4 files changed, 35 insertions(+), 20 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex a2ab68ed06..abdf6126df 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -252,7 +252,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t}\n \tcase SHORTEN_OIDS:\n \tcase EXPAND_OIDS:\n-\t\tret = transform_todos(flags);\n+\t\tret = transform_todo_file(flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n \t\tret = check_todo_list();\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 78f3263fc1..4cd487a450 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -69,7 +69,7 @@ int edit_todo_list(unsigned flags)\n \n \tstrbuf_release(&buf);\n \n-\ttransform_todos(flags | TODO_LIST_SHORTEN_IDS);\n+\ttransform_todo_file(flags | TODO_LIST_SHORTEN_IDS);\n \n \tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n@@ -85,7 +85,7 @@ int edit_todo_list(unsigned flags)\n \tif (launch_sequence_editor(todo_file, NULL, NULL))\n \t\treturn -1;\n \n-\ttransform_todos(flags & ~(TODO_LIST_SHORTEN_IDS));\n+\ttransform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS));\n \n \treturn 0;\n }\ndiff --git a/sequencer.c b/sequencer.c\nindex 7adbeaa27d..e1097660ed 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4454,23 +4454,13 @@ int sequencer_add_exec_commands(const char *commands)\n \treturn i;\n }\n \n-int transform_todos(unsigned flags)\n+void todo_list_transform(struct todo_list *todo_list, unsigned flags)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct todo_item *item;\n \tint i;\n \n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tfor (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {\n+\tfor (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n \t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len, item->arg);\n@@ -4506,9 +4496,33 @@ int transform_todos(unsigned flags)\n \t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len, item->arg);\n \t}\n \n-\ti = write_message(buf.buf, buf.len, todo_file, 0);\n+\tstrbuf_reset(&todo_list->buf);\n+\tstrbuf_add(&todo_list->buf, buf.buf, buf.len);\n+\tstrbuf_release(&buf);\n+\n+\tif (todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list))\n+\t\tBUG(\"unusable todo list\");\n+}\n+\n+int transform_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_transform(&todo_list, flags);\n+\n+\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n \ttodo_list_release(&todo_list);\n-\treturn i;\n+\treturn res;\n }\n \n enum missing_commit_check_level get_missing_commit_check_level(void)\n@@ -4769,7 +4783,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n \t\t\t     rebase_path_todo_backup());\n \n-\tif (transform_todos(flags | TODO_LIST_SHORTEN_IDS))\n+\tif (transform_todo_file(flags | TODO_LIST_SHORTEN_IDS))\n \t\treturn error(_(\"could not transform the todo list\"));\n \n \tstrbuf_reset(buf);\n@@ -4798,7 +4812,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t\treturn -1;\n \t}\n \n-\tif (transform_todos(flags & ~(TODO_LIST_SHORTEN_IDS)))\n+\tif (transform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS)))\n \t\treturn error(_(\"could not transform the todo list\"));\n \n \tif (opts->allow_ff && skip_unnecessary_picks(&oid))\ndiff --git a/sequencer.h b/sequencer.h\nindex 84f813305f..708d042f0e 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -119,6 +119,7 @@ struct todo_list {\n #define TODO_LIST_INIT { STRBUF_INIT }\n \n int todo_list_parse_insn_buffer(char *buf, struct todo_list *todo_list);\n+void todo_list_transform(struct todo_list *todo_list, unsigned flags);\n void todo_list_release(struct todo_list *todo_list);\n \n /* Call this to setup defaults before parsing command line options */\n@@ -142,7 +143,7 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(const char *command);\n-int transform_todos(unsigned flags);\n+int transform_todo_file(unsigned flags);\n enum missing_commit_check_level get_missing_commit_check_level(void);\n int check_todo_list(void);\n int complete_action(struct replay_opts *opts, unsigned flags,\n-- \n2.19.1.872.ga867da739e\n\n"},{"id":"362789","messageId":"20181109080805.6350-5-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181109080805.6350-1-alban.gruin@gmail.com","subject":"[PATCH v3 04/16] sequencer: introduce todo_list_write_to_file()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-09T08:07:53Z","receivedAt":"2018-11-09T08:08:39Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This introduces a new function to recreate the text of a todo list from\nits commands and write it to a file.  This will be useful as the next\nfew commits will change the use of the buffer in struct todo_list so it\nwill no longer be a mirror of the file on disk.\n\nThis functionality can already be found in todo_list_transform(), but it\nis specifically made to replace the buffer of a todo list, which is not\nthe desired behaviour.  Thus, the part of todo_list_transform() that\nactually creates the buffer is moved to a new function,\ntodo_list_to_strbuf().  The rest is unused, and so is dropped.\n\ntodo_list_write_to_file() can also take care of appending the help text\nto the buffer before writing it to the disk, or to write only the first\nn items of the list.  This feature will be used by\nskip_unnecessary_picks(), which has to write done commands in a file.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 60 ++++++++++++++++++++++++++++++++++++-----------------\n sequencer.h |  6 +++++-\n 2 files changed, 46 insertions(+), 20 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex e1097660ed..19f24825d4 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4454,24 +4454,27 @@ int sequencer_add_exec_commands(const char *commands)\n \treturn i;\n }\n \n-void todo_list_transform(struct todo_list *todo_list, unsigned flags)\n+static void todo_list_to_strbuf(struct todo_list *todo_list, struct strbuf *buf,\n+\t\t\t\tint num, unsigned flags)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n \tstruct todo_item *item;\n-\tint i;\n+\tint i, max = todo_list->nr;\n \n-\tfor (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {\n+\tif (num > 0 && num < max)\n+\t\tmax = num;\n+\n+\tfor (item = todo_list->items, i = 0; i < max; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n-\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len, item->arg);\n+\t\t\tstrbuf_addf(buf, \"%.*s\\n\", item->arg_len, item->arg);\n \t\t\tcontinue;\n \t\t}\n \n \t\t/* add command to the buffer */\n \t\tif (flags & TODO_LIST_ABBREVIATE_CMDS)\n-\t\t\tstrbuf_addch(&buf, command_to_char(item->command));\n+\t\t\tstrbuf_addch(buf, command_to_char(item->command));\n \t\telse\n-\t\t\tstrbuf_addstr(&buf, command_to_string(item->command));\n+\t\t\tstrbuf_addstr(buf, command_to_string(item->command));\n \n \t\t/* add commit id */\n \t\tif (item->commit) {\n@@ -4481,27 +4484,47 @@ void todo_list_transform(struct todo_list *todo_list, unsigned flags)\n \n \t\t\tif (item->command == TODO_MERGE) {\n \t\t\t\tif (item->flags & TODO_EDIT_MERGE_MSG)\n-\t\t\t\t\tstrbuf_addstr(&buf, \" -c\");\n+\t\t\t\t\tstrbuf_addstr(buf, \" -c\");\n \t\t\t\telse\n-\t\t\t\t\tstrbuf_addstr(&buf, \" -C\");\n+\t\t\t\t\tstrbuf_addstr(buf, \" -C\");\n \t\t\t}\n \n-\t\t\tstrbuf_addf(&buf, \" %s\", oid);\n+\t\t\tstrbuf_addf(buf, \" %s\", oid);\n \t\t}\n \n \t\t/* add all the rest */\n \t\tif (!item->arg_len)\n-\t\t\tstrbuf_addch(&buf, '\\n');\n+\t\t\tstrbuf_addch(buf, '\\n');\n \t\telse\n-\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len, item->arg);\n+\t\t\tstrbuf_addf(buf, \" %.*s\\n\", item->arg_len, item->arg);\n \t}\n+}\n \n-\tstrbuf_reset(&todo_list->buf);\n-\tstrbuf_add(&todo_list->buf, buf.buf, buf.len);\n+int todo_list_write_to_file(struct todo_list *todo_list, const char *file,\n+\t\t\t    const char *shortrevisions, const char *shortonto,\n+\t\t\t    int num, unsigned flags)\n+{\n+\tint edit_todo = !(shortrevisions && shortonto), res;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\ttodo_list_to_strbuf(todo_list, &buf, num, flags);\n+\n+\tif (flags & TODO_LIST_APPEND_TODO_HELP) {\n+\t\tint command_count = count_commands(todo_list);\n+\t\tif (!edit_todo) {\n+\t\t\tstrbuf_addch(&buf, '\\n');\n+\t\t\tstrbuf_commented_addf(&buf, Q_(\"Rebase %s onto %s (%d command)\",\n+\t\t\t\t\t\t       \"Rebase %s onto %s (%d commands)\",\n+\t\t\t\t\t\t       command_count),\n+\t\t\t\t\t      shortrevisions, shortonto, command_count);\n+\t\t}\n+\t\tappend_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, &buf);\n+\t}\n+\n+\tres = write_message(buf.buf, buf.len, file, 0);\n \tstrbuf_release(&buf);\n \n-\tif (todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list))\n-\t\tBUG(\"unusable todo list\");\n+\treturn res;\n }\n \n int transform_todo_file(unsigned flags)\n@@ -4518,9 +4541,8 @@ int transform_todo_file(unsigned flags)\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n \n-\ttodo_list_transform(&todo_list, flags);\n-\n-\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n+\tres = todo_list_write_to_file(&todo_list, todo_file,\n+\t\t\t\t      NULL, NULL, -1, flags);\n \ttodo_list_release(&todo_list);\n \treturn res;\n }\ndiff --git a/sequencer.h b/sequencer.h\nindex 708d042f0e..e69357c95b 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -119,7 +119,9 @@ struct todo_list {\n #define TODO_LIST_INIT { STRBUF_INIT }\n \n int todo_list_parse_insn_buffer(char *buf, struct todo_list *todo_list);\n-void todo_list_transform(struct todo_list *todo_list, unsigned flags);\n+int todo_list_write_to_file(struct todo_list *todo_list, const char *file,\n+\t\t\t    const char *shortrevisions, const char *shortonto,\n+\t\t\t    int num, unsigned flags);\n void todo_list_release(struct todo_list *todo_list);\n \n /* Call this to setup defaults before parsing command line options */\n@@ -139,6 +141,8 @@ int sequencer_remove_state(struct replay_opts *opts);\n  * commits should be rebased onto the new base, this flag needs to be passed.\n  */\n #define TODO_LIST_REBASE_COUSINS (1U << 4)\n+#define TODO_LIST_APPEND_TODO_HELP (1U << 5)\n+\n int sequencer_make_script(FILE *out, int argc, const char **argv,\n \t\t\t  unsigned flags);\n \n-- \n2.19.1.872.ga867da739e\n\n"},{"id":"362790","messageId":"20181109080805.6350-6-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181109080805.6350-1-alban.gruin@gmail.com","subject":"[PATCH v3 05/16] sequencer: refactor check_todo_list() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-09T08:07:54Z","receivedAt":"2018-11-09T08:08:40Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors check_todo_list() to work on a todo_list to avoid\nredundant reads and writes to the disk.  The function is renamed\ntodo_list_check().  The parsing of the two todo lists is left to the\ncaller.\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, check_todo_list_from_file().  It reads the file\nfrom the disk, parses it, pass the todo_list to todo_list_check(), and\nwrites it back to the disk.\n\nAs get_missing_commit_check_level() and the enum\nmissing_commit_check_level are no longer needed inside of sequencer.c,\nthey are moved to rebase-interactive.c, and made static again.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v2.\n\n builtin/rebase--interactive.c |   2 +-\n rebase-interactive.c          |  90 ++++++++++++++++++++++++-\n rebase-interactive.h          |   1 +\n sequencer.c                   | 120 +++++++---------------------------\n sequencer.h                   |   9 +--\n 5 files changed, 115 insertions(+), 107 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex abdf6126df..c1d87c0fe6 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -255,7 +255,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = transform_todo_file(flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n-\t\tret = check_todo_list();\n+\t\tret = check_todo_list_from_file();\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n \t\tret = rearrange_squash();\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 4cd487a450..3adcf39e07 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -1,8 +1,32 @@\n #include \"cache.h\"\n #include \"commit.h\"\n-#include \"rebase-interactive.h\"\n #include \"sequencer.h\"\n+#include \"rebase-interactive.h\"\n #include \"strbuf.h\"\n+#include \"commit-slab.h\"\n+#include \"config.h\"\n+\n+enum missing_commit_check_level {\n+\tMISSING_COMMIT_CHECK_IGNORE = 0,\n+\tMISSING_COMMIT_CHECK_WARN,\n+\tMISSING_COMMIT_CHECK_ERROR\n+};\n+\n+static enum missing_commit_check_level get_missing_commit_check_level(void)\n+{\n+\tconst char *value;\n+\n+\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n+\t\t\t!strcasecmp(\"ignore\", value))\n+\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n+\tif (!strcasecmp(\"warn\", value))\n+\t\treturn MISSING_COMMIT_CHECK_WARN;\n+\tif (!strcasecmp(\"error\", value))\n+\t\treturn MISSING_COMMIT_CHECK_ERROR;\n+\twarning(_(\"unrecognized setting %s for option \"\n+\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n+\treturn MISSING_COMMIT_CHECK_IGNORE;\n+}\n \n void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \t\t      struct strbuf *buf)\n@@ -89,3 +113,67 @@ int edit_todo_list(unsigned flags)\n \n \treturn 0;\n }\n+\n+define_commit_slab(commit_seen, unsigned char);\n+/*\n+ * Check if the user dropped some commits by mistake\n+ * Behaviour determined by rebase.missingCommitsCheck.\n+ * Check if there is an unrecognized command or a\n+ * bad SHA-1 in a command.\n+ */\n+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo)\n+{\n+\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n+\tstruct strbuf missing = STRBUF_INIT;\n+\tint res = 0, i;\n+\tstruct commit_seen commit_seen;\n+\n+\tinit_commit_seen(&commit_seen);\n+\n+\tif (check_level == MISSING_COMMIT_CHECK_IGNORE)\n+\t\tgoto leave_check;\n+\n+\t/* Mark the commits in git-rebase-todo as seen */\n+\tfor (i = 0; i < new_todo->nr; i++) {\n+\t\tstruct commit *commit = new_todo->items[i].commit;\n+\t\tif (commit)\n+\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t}\n+\n+\t/* Find commits in git-rebase-todo.backup yet unseen */\n+\tfor (i = old_todo->nr - 1; i >= 0; i--) {\n+\t\tstruct todo_item *item = old_todo->items + i;\n+\t\tstruct commit *commit = item->commit;\n+\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n+\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n+\t\t\t\t    find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV),\n+\t\t\t\t    item->arg_len, item->arg);\n+\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t\t}\n+\t}\n+\n+\t/* Warn about missing commits */\n+\tif (!missing.len)\n+\t\tgoto leave_check;\n+\n+\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n+\t\tres = 1;\n+\n+\tfprintf(stderr,\n+\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n+\t\t\"Dropped commits (newer to older):\\n\"));\n+\n+\t/* Make the list user-friendly and display */\n+\tfputs(missing.buf, stderr);\n+\tstrbuf_release(&missing);\n+\n+\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n+\t\t\"explicitly remove a commit.\\n\\n\"\n+\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n+\t\t\"the level of warnings.\\n\"\n+\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n+\n+leave_check:\n+\tclear_commit_seen(&commit_seen);\n+\treturn res;\n+}\ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 971da03776..6bc7bc315d 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -4,5 +4,6 @@\n void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \t\t      struct strbuf *buf);\n int edit_todo_list(unsigned flags);\n+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n \n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex 19f24825d4..900899ef20 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4547,111 +4547,37 @@ int transform_todo_file(unsigned flags)\n \treturn res;\n }\n \n-enum missing_commit_check_level get_missing_commit_check_level(void)\n-{\n-\tconst char *value;\n-\n-\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n-\t\t\t!strcasecmp(\"ignore\", value))\n-\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n-\tif (!strcasecmp(\"warn\", value))\n-\t\treturn MISSING_COMMIT_CHECK_WARN;\n-\tif (!strcasecmp(\"error\", value))\n-\t\treturn MISSING_COMMIT_CHECK_ERROR;\n-\twarning(_(\"unrecognized setting %s for option \"\n-\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n-\treturn MISSING_COMMIT_CHECK_IGNORE;\n-}\n+static const char edit_todo_list_advice[] =\n+N_(\"You can fix this with 'git rebase --edit-todo' \"\n+\"and then run 'git rebase --continue'.\\n\"\n+\"Or you can abort the rebase with 'git rebase\"\n+\" --abort'.\\n\");\n \n-define_commit_slab(commit_seen, unsigned char);\n-/*\n- * Check if the user dropped some commits by mistake\n- * Behaviour determined by rebase.missingCommitsCheck.\n- * Check if there is an unrecognized command or a\n- * bad SHA-1 in a command.\n- */\n-int check_todo_list(void)\n+int check_todo_list_from_file(void)\n {\n-\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n-\tstruct strbuf todo_file = STRBUF_INIT;\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf missing = STRBUF_INIT;\n-\tint advise_to_edit_todo = 0, res = 0, i;\n-\tstruct commit_seen commit_seen;\n-\n-\tinit_commit_seen(&commit_seen);\n+\tstruct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;\n+\tint res = 0;\n \n-\tstrbuf_addstr(&todo_file, rebase_path_todo());\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n+\tif (strbuf_read_file_or_whine(&new_todo.buf, rebase_path_todo()) < 0) {\n \t\tres = -1;\n-\t\tgoto leave_check;\n-\t}\n-\tadvise_to_edit_todo = res =\n-\t\ttodo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n-\n-\tif (res || check_level == MISSING_COMMIT_CHECK_IGNORE)\n-\t\tgoto leave_check;\n-\n-\t/* Mark the commits in git-rebase-todo as seen */\n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tstruct commit *commit = todo_list.items[i].commit;\n-\t\tif (commit)\n-\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t\tgoto out;\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\tstrbuf_addstr(&todo_file, \".backup\");\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n+\tif (strbuf_read_file_or_whine(&old_todo.buf, rebase_path_todo_backup()) < 0) {\n \t\tres = -1;\n-\t\tgoto leave_check;\n-\t}\n-\tstrbuf_release(&todo_file);\n-\tres = !!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n-\n-\t/* Find commits in git-rebase-todo.backup yet unseen */\n-\tfor (i = todo_list.nr - 1; i >= 0; i--) {\n-\t\tstruct todo_item *item = todo_list.items + i;\n-\t\tstruct commit *commit = item->commit;\n-\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n-\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n-\t\t\t\t    short_commit_name(commit),\n-\t\t\t\t    item->arg_len, item->arg);\n-\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n-\t\t}\n+\t\tgoto out;\n \t}\n \n-\t/* Warn about missing commits */\n-\tif (!missing.len)\n-\t\tgoto leave_check;\n-\n-\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n-\t\tadvise_to_edit_todo = res = 1;\n-\n-\tfprintf(stderr,\n-\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n-\t\t\"Dropped commits (newer to older):\\n\"));\n-\n-\t/* Make the list user-friendly and display */\n-\tfputs(missing.buf, stderr);\n-\tstrbuf_release(&missing);\n-\n-\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n-\t\t\"explicitly remove a commit.\\n\\n\"\n-\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n-\t\t\"the level of warnings.\\n\"\n-\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n-\n-leave_check:\n-\tclear_commit_seen(&commit_seen);\n-\tstrbuf_release(&todo_file);\n-\ttodo_list_release(&todo_list);\n-\n-\tif (advise_to_edit_todo)\n-\t\tfprintf(stderr,\n-\t\t\t_(\"You can fix this with 'git rebase --edit-todo' \"\n-\t\t\t  \"and then run 'git rebase --continue'.\\n\"\n-\t\t\t  \"Or you can abort the rebase with 'git rebase\"\n-\t\t\t  \" --abort'.\\n\"));\n+\tres = todo_list_parse_insn_buffer(old_todo.buf.buf, &old_todo);\n+\tif (!res)\n+\t\tres = todo_list_parse_insn_buffer(new_todo.buf.buf, &new_todo);\n+\tif (!res)\n+\t\tres = todo_list_check(&old_todo, &new_todo);\n+\tif (res)\n+\t\tfprintf(stderr, _(edit_todo_list_advice));\n+out:\n+\ttodo_list_release(&old_todo);\n+\ttodo_list_release(&new_todo);\n \n \treturn res;\n }\n@@ -4829,7 +4755,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \n \ttodo_list_release(&todo_list);\n \n-\tif (check_todo_list()) {\n+\tif (check_todo_list_from_file()) {\n \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n \t\treturn -1;\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex e69357c95b..c78f00aadd 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -63,12 +63,6 @@ struct replay_opts {\n };\n #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }\n \n-enum missing_commit_check_level {\n-\tMISSING_COMMIT_CHECK_IGNORE = 0,\n-\tMISSING_COMMIT_CHECK_WARN,\n-\tMISSING_COMMIT_CHECK_ERROR\n-};\n-\n int write_message(const void *buf, size_t len, const char *filename,\n \t\t  int append_eol);\n \n@@ -148,8 +142,7 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,\n \n int sequencer_add_exec_commands(const char *command);\n int transform_todo_file(unsigned flags);\n-enum missing_commit_check_level get_missing_commit_check_level(void);\n-int check_todo_list(void);\n+int check_todo_list_from_file(void);\n int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, const char *cmd,\n-- \n2.19.1.872.ga867da739e\n\n"},{"id":"362791","messageId":"20181109080805.6350-7-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181109080805.6350-1-alban.gruin@gmail.com","subject":"[PATCH v3 06/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-09T08:07:55Z","receivedAt":"2018-11-09T08:08:41Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors sequencer_add_exec_commands() to work on a todo_list to\navoid redundant reads and writes to the disk.\n\nInstead of just inserting the `exec' command between the other commands,\nand re-parsing the buffer at the end the exec command is appended to the\nbuffer once, and a new list of items is created.  Items from the old\nlist are copied across and new `exec' items are appended when\nnecessary.  This eliminates the need to reparse the buffer, but this\nalso means we have to use todo_list_write_to_disk() to write the file().\n\ntodo_list_add_exec_commands() and sequencer_add_exec_commands() are\nmodified to take a string list instead of a string -- one item for each\ncommand.  This makes it easier to insert a new command to the todo list\nfor each command to execute.\n\nsequencer_add_exec_commands() still reads the todo list from the disk,\nas it is needed by rebase -p.\n\ncomplete_action() still uses sequencer_add_exec_commands() for now.\nThis will be changed in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |  15 +++--\n sequencer.c                   | 111 +++++++++++++++++++++-------------\n sequencer.h                   |   4 +-\n 3 files changed, 83 insertions(+), 47 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex c1d87c0fe6..1fb5a43c0d 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -65,7 +65,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \t\t\t\t const char *onto, const char *onto_name,\n \t\t\t\t const char *squash_onto, const char *head_name,\n \t\t\t\t const char *restrict_revision, char *raw_strategies,\n-\t\t\t\t const char *cmd, unsigned autosquash)\n+\t\t\t\t struct string_list *commands, unsigned autosquash)\n {\n \tint ret;\n \tconst char *head_hash = NULL;\n@@ -115,7 +115,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \telse {\n \t\tdiscard_cache();\n \t\tret = complete_action(opts, flags, shortrevisions, onto_name, onto,\n-\t\t\t\t      head_hash, cmd, autosquash);\n+\t\t\t\t      head_hash, commands, autosquash);\n \t}\n \n \tfree(revisions);\n@@ -138,6 +138,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \tconst char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,\n \t\t*squash_onto = NULL, *upstream = NULL, *head_name = NULL,\n \t\t*switch_to = NULL, *cmd = NULL;\n+\tstruct string_list commands = STRING_LIST_INIT_DUP;\n \tchar *raw_strategies = NULL;\n \tenum {\n \t\tNONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,\n@@ -220,6 +221,12 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\twarning(_(\"--[no-]rebase-cousins has no effect without \"\n \t\t\t  \"--rebase-merges\"));\n \n+\tif (cmd && *cmd) {\n+\t\tstring_list_split(&commands, cmd, '\\n', -1);\n+\t\tif (strlen(commands.items[commands.nr - 1].string) == 0)\n+\t\t\t--commands.nr;\n+\t}\n+\n \tswitch (command) {\n \tcase NONE:\n \t\tif (!onto && !upstream)\n@@ -227,7 +234,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \n \t\tret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,\n \t\t\t\t\t    onto_name, squash_onto, head_name, restrict_revision,\n-\t\t\t\t\t    raw_strategies, cmd, autosquash);\n+\t\t\t\t\t    raw_strategies, &commands, autosquash);\n \t\tbreak;\n \tcase SKIP: {\n \t\tstruct string_list merge_rr = STRING_LIST_INIT_DUP;\n@@ -261,7 +268,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = rearrange_squash();\n \t\tbreak;\n \tcase ADD_EXEC:\n-\t\tret = sequencer_add_exec_commands(cmd);\n+\t\tret = sequencer_add_exec_commands(&commands);\n \t\tbreak;\n \tdefault:\n \t\tBUG(\"invalid command '%d'\", command);\ndiff --git a/sequencer.c b/sequencer.c\nindex 900899ef20..11692d0b98 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4394,24 +4394,29 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,\n \treturn 0;\n }\n \n-/*\n- * Add commands after pick and (series of) squash/fixup commands\n- * in the todo list.\n- */\n-int sequencer_add_exec_commands(const char *commands)\n+static void todo_list_add_exec_commands(struct todo_list *todo_list,\n+\t\t\t\t\tstruct string_list *commands)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf *buf = &todo_list.buf;\n-\tsize_t offset = 0, commands_len = strlen(commands);\n-\tint i, insert;\n+\tstruct strbuf *buf = &todo_list->buf;\n+\tconst char *old_buf = buf->buf;\n+\tsize_t base_length = buf->len;\n+\tint i, insert, nr = 0, alloc = 0;\n+\tstruct todo_item *items = NULL, *base_items = NULL;\n \n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n+\tfor (i = 0; i < commands->nr; ++i) {\n+\t\tstrbuf_addstr(buf, commands->items[i].string);\n+\t\tstrbuf_addch(buf, '\\n');\n+\t}\n \n-\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\tbase_items = xcalloc(commands->nr, sizeof(struct todo_item));\n+\tfor (i = 0; i < commands->nr; ++i) {\n+\t\tsize_t command_len = strlen(commands->items[i].string) - strlen(\"exec \");\n+\n+\t\tbase_items[i].command = TODO_EXEC;\n+\t\tbase_items[i].offset_in_buf = base_length + strlen(\"exec \");\n+\t\tbase_items[i].arg = buf->buf + base_items[i].offset_in_buf;\n+\t\tbase_items[i].arg_len = command_len;\n+\t\tbase_length = base_items[i].offset_in_buf + base_items[i].arg_len + 1;\n \t}\n \n \t/*\n@@ -4420,38 +4425,62 @@ int sequencer_add_exec_commands(const char *commands)\n \t * those chains if there are any.\n \t */\n \tinsert = -1;\n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tenum todo_command command = todo_list.items[i].command;\n-\n-\t\tif (insert >= 0) {\n-\t\t\t/* skip fixup/squash chains */\n-\t\t\tif (command == TODO_COMMENT)\n-\t\t\t\tcontinue;\n-\t\t\telse if (is_fixup(command)) {\n-\t\t\t\tinsert = i + 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tstrbuf_insert(buf,\n-\t\t\t\t      todo_list.items[insert].offset_in_buf +\n-\t\t\t\t      offset, commands, commands_len);\n-\t\t\toffset += commands_len;\n+\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\tenum todo_command command = todo_list->items[i].command;\n+\t\tif (todo_list->items[i].arg)\n+\t\t\ttodo_list->items[i].arg = todo_list->items[i].arg - old_buf + buf->buf;\n+\n+\t\tif (insert >= 0 && command != TODO_COMMENT && !is_fixup(command)) {\n+\t\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n+\t\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n+\t\t\tnr += commands->nr;\n \t\t\tinsert = -1;\n \t\t}\n \n-\t\tif (command == TODO_PICK || command == TODO_MERGE)\n+\t\tALLOC_GROW(items, nr + 1, alloc);\n+\t\titems[nr++] = todo_list->items[i];\n+\n+\t\tif (command == TODO_PICK || command == TODO_MERGE || is_fixup(command))\n \t\t\tinsert = i + 1;\n \t}\n \n \t/* insert or append final <commands> */\n-\tif (insert >= 0 && insert < todo_list.nr)\n-\t\tstrbuf_insert(buf, todo_list.items[insert].offset_in_buf +\n-\t\t\t      offset, commands, commands_len);\n-\telse if (insert >= 0 || !offset)\n-\t\tstrbuf_add(buf, commands, commands_len);\n+\tif (insert >= 0 || nr == todo_list->nr) {\n+\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n+\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n+\t\tnr += commands->nr;\n+\t}\n+\n+\tfree(base_items);\n+\tFREE_AND_NULL(todo_list->items);\n+\ttodo_list->items = items;\n+\ttodo_list->nr = nr;\n+\ttodo_list->alloc = alloc;\n+}\n+\n+/*\n+ * Add commands after pick and (series of) squash/fixup commands\n+ * in the todo list.\n+ */\n+int sequencer_add_exec_commands(struct string_list *commands)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n \n-\ti = write_message(buf->buf, buf->len, todo_file, 0);\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_add_exec_commands(&todo_list, commands);\n+\tres = todo_list_write_to_file(&todo_list, todo_file, NULL, NULL, -1, 0);\n \ttodo_list_release(&todo_list);\n-\treturn i;\n+\n+\treturn res;\n }\n \n static void todo_list_to_strbuf(struct todo_list *todo_list, struct strbuf *buf,\n@@ -4677,7 +4706,7 @@ static int skip_unnecessary_picks(struct object_id *output_oid)\n \n int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-\t\t    const char *onto, const char *orig_head, const char *cmd,\n+\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash)\n {\n \tconst char *shortonto, *todo_file = rebase_path_todo();\n@@ -4696,8 +4725,8 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \tif (autosquash && rearrange_squash())\n \t\treturn -1;\n \n-\tif (cmd && *cmd)\n-\t\tsequencer_add_exec_commands(cmd);\n+\tif (commands->nr)\n+\t\tsequencer_add_exec_commands(commands);\n \n \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\ndiff --git a/sequencer.h b/sequencer.h\nindex c78f00aadd..d7b52044bd 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -140,12 +140,12 @@ int sequencer_remove_state(struct replay_opts *opts);\n int sequencer_make_script(FILE *out, int argc, const char **argv,\n \t\t\t  unsigned flags);\n \n-int sequencer_add_exec_commands(const char *command);\n+int sequencer_add_exec_commands(struct string_list *commands);\n int transform_todo_file(unsigned flags);\n int check_todo_list_from_file(void);\n int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-\t\t    const char *onto, const char *orig_head, const char *cmd,\n+\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash);\n int rearrange_squash(void);\n \n-- \n2.19.1.872.ga867da739e\n\n"},{"id":"362792","messageId":"20181109080805.6350-8-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181109080805.6350-1-alban.gruin@gmail.com","subject":"[PATCH v3 07/16] sequencer: refactor rearrange_squash() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-09T08:07:56Z","receivedAt":"2018-11-09T08:08:43Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors rearrange_squash() to work on a todo_list to avoid\nredundant reads and writes.  The function is renamed\ntodo_list_rearrange_squash().\n\nThe old version created a new buffer, which was directly written to the\ndisk.  This new version creates a new item list by just memcpying items\nfrom the old item list, without creating a new buffer.  This eliminates\nthe need to reparse the todo list, but this also means its buffer cannot\nbe directly written to the disk.\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, rearrange_squash_in_todo_file().\n\ncomplete_action() still uses rearrange_squash_in_todo_file() for now.\nThis will be changed in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |  2 +-\n sequencer.c                   | 87 +++++++++++++++++------------------\n sequencer.h                   |  2 +-\n 3 files changed, 45 insertions(+), 46 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 1fb5a43c0d..a6d83a684e 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -265,7 +265,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = check_todo_list_from_file();\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n-\t\tret = rearrange_squash();\n+\t\tret = rearrange_squash_in_todo_file();\n \t\tbreak;\n \tcase ADD_EXEC:\n \t\tret = sequencer_add_exec_commands(&commands);\ndiff --git a/sequencer.c b/sequencer.c\nindex 11692d0b98..fce97e5f11 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4722,7 +4722,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t    write_message(\"noop\\n\", 5, todo_file, 0))\n \t\treturn -1;\n \n-\tif (autosquash && rearrange_squash())\n+\tif (autosquash && rearrange_squash_in_todo_file())\n \t\treturn -1;\n \n \tif (commands->nr)\n@@ -4828,21 +4828,13 @@ define_commit_slab(commit_todo_item, struct todo_item *);\n  * message will have to be retrieved from the commit (as the oneline in the\n  * script cannot be trusted) in order to normalize the autosquash arrangement.\n  */\n-int rearrange_squash(void)\n+static int todo_list_rearrange_squash(struct todo_list *todo_list)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct hashmap subject2item;\n-\tint res = 0, rearranged = 0, *next, *tail, i;\n+\tint rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;\n \tchar **subjects;\n \tstruct commit_todo_item commit_todo;\n-\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tstruct todo_item *items = NULL;\n \n \tinit_commit_todo_item(&commit_todo);\n \t/*\n@@ -4855,13 +4847,13 @@ int rearrange_squash(void)\n \t * be moved to appear after the i'th.\n \t */\n \thashmap_init(&subject2item, (hashmap_cmp_fn) subject2item_cmp,\n-\t\t     NULL, todo_list.nr);\n-\tALLOC_ARRAY(next, todo_list.nr);\n-\tALLOC_ARRAY(tail, todo_list.nr);\n-\tALLOC_ARRAY(subjects, todo_list.nr);\n-\tfor (i = 0; i < todo_list.nr; i++) {\n+\t\t     NULL, todo_list->nr);\n+\tALLOC_ARRAY(next, todo_list->nr);\n+\tALLOC_ARRAY(tail, todo_list->nr);\n+\tALLOC_ARRAY(subjects, todo_list->nr);\n+\tfor (i = 0; i < todo_list->nr; i++) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n-\t\tstruct todo_item *item = todo_list.items + i;\n+\t\tstruct todo_item *item = todo_list->items + i;\n \t\tconst char *commit_buffer, *subject, *p;\n \t\tsize_t subject_len;\n \t\tint i2 = -1;\n@@ -4874,7 +4866,6 @@ int rearrange_squash(void)\n \t\t}\n \n \t\tif (is_fixup(item->command)) {\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\tclear_commit_todo_item(&commit_todo);\n \t\t\treturn error(_(\"the script was already rearranged.\"));\n \t\t}\n@@ -4909,7 +4900,7 @@ int rearrange_squash(void)\n \t\t\t\t *commit_todo_item_at(&commit_todo, commit2))\n \t\t\t\t/* found by commit name */\n \t\t\t\ti2 = *commit_todo_item_at(&commit_todo, commit2)\n-\t\t\t\t\t- todo_list.items;\n+\t\t\t\t\t- todo_list->items;\n \t\t\telse {\n \t\t\t\t/* copy can be a prefix of the commit subject */\n \t\t\t\tfor (i2 = 0; i2 < i; i2++)\n@@ -4922,7 +4913,7 @@ int rearrange_squash(void)\n \t\t}\n \t\tif (i2 >= 0) {\n \t\t\trearranged = 1;\n-\t\t\ttodo_list.items[i].command =\n+\t\t\ttodo_list->items[i].command =\n \t\t\t\tstarts_with(subject, \"fixup!\") ?\n \t\t\t\tTODO_FIXUP : TODO_SQUASH;\n \t\t\tif (next[i2] < 0)\n@@ -4940,10 +4931,8 @@ int rearrange_squash(void)\n \t}\n \n \tif (rearranged) {\n-\t\tstruct strbuf buf = STRBUF_INIT;\n-\n-\t\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\t\tenum todo_command command = todo_list.items[i].command;\n+\t\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\t\tenum todo_command command = todo_list->items[i].command;\n \t\t\tint cur = i;\n \n \t\t\t/*\n@@ -4954,37 +4943,47 @@ int rearrange_squash(void)\n \t\t\t\tcontinue;\n \n \t\t\twhile (cur >= 0) {\n-\t\t\t\tconst char *bol =\n-\t\t\t\t\tget_item_line(&todo_list, cur);\n-\t\t\t\tconst char *eol =\n-\t\t\t\t\tget_item_line(&todo_list, cur + 1);\n-\n-\t\t\t\t/* replace 'pick', by 'fixup' or 'squash' */\n-\t\t\t\tcommand = todo_list.items[cur].command;\n-\t\t\t\tif (is_fixup(command)) {\n-\t\t\t\t\tstrbuf_addstr(&buf,\n-\t\t\t\t\t\ttodo_command_info[command].str);\n-\t\t\t\t\tbol += strcspn(bol, \" \\t\");\n-\t\t\t\t}\n-\n-\t\t\t\tstrbuf_add(&buf, bol, eol - bol);\n-\n+\t\t\t\tALLOC_GROW(items, nr + 1, alloc);\n+\t\t\t\titems[nr++] = todo_list->items[cur];\n \t\t\t\tcur = next[cur];\n \t\t\t}\n \t\t}\n \n-\t\tres = rewrite_file(todo_file, buf.buf, buf.len);\n-\t\tstrbuf_release(&buf);\n+\t\tFREE_AND_NULL(todo_list->items);\n+\t\ttodo_list->items = items;\n+\t\ttodo_list->nr = nr;\n+\t\ttodo_list->alloc = alloc;\n \t}\n \n \tfree(next);\n \tfree(tail);\n-\tfor (i = 0; i < todo_list.nr; i++)\n+\tfor (i = 0; i < todo_list->nr; i++)\n \t\tfree(subjects[i]);\n \tfree(subjects);\n \thashmap_free(&subject2item, 1);\n-\ttodo_list_release(&todo_list);\n \n \tclear_commit_todo_item(&commit_todo);\n+\n+\treturn 0;\n+}\n+\n+int rearrange_squash_in_todo_file(void)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n+\n+\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n+\t\treturn -1;\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn -1;\n+\t}\n+\n+\tres = todo_list_rearrange_squash(&todo_list);\n+\tif (!res)\n+\t\tres = todo_list_write_to_file(&todo_list, todo_file, NULL, NULL, -1, 0);\n+\n+\ttodo_list_release(&todo_list);\n \treturn res;\n }\ndiff --git a/sequencer.h b/sequencer.h\nindex d7b52044bd..1cd9d7f1e2 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -147,7 +147,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash);\n-int rearrange_squash(void);\n+int rearrange_squash_in_todo_file(void);\n \n extern const char sign_off_header[];\n \n-- \n2.19.1.872.ga867da739e\n\n"},{"id":"362793","messageId":"20181109080805.6350-9-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181109080805.6350-1-alban.gruin@gmail.com","subject":"[PATCH v3 08/16] sequencer: make sequencer_make_script() write its script to a strbuf","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-09T08:07:57Z","receivedAt":"2018-11-09T08:08:44Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This makes sequencer_make_script() write its script to a strbuf (ie. the\nbuffer of a todo_list) instead of a FILE.  This reduce the amount of\nread/write made by rebase interactive.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 13 +++++++-----\n sequencer.c                   | 38 ++++++++++++++++-------------------\n sequencer.h                   |  2 +-\n 3 files changed, 26 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex a6d83a684e..c740a7dd5d 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -71,7 +71,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tconst char *head_hash = NULL;\n \tchar *revisions = NULL, *shortrevisions = NULL;\n \tstruct argv_array make_script_args = ARGV_ARRAY_INIT;\n-\tFILE *todo_list;\n+\tFILE *todo_list_file;\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n \n \tif (prepare_branch_to_be_rebased(opts, switch_to))\n \t\treturn -1;\n@@ -93,8 +94,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (!upstream && squash_onto)\n \t\twrite_file(path_squash_onto(), \"%s\\n\", squash_onto);\n \n-\ttodo_list = fopen(rebase_path_todo(), \"w\");\n-\tif (!todo_list) {\n+\ttodo_list_file = fopen(rebase_path_todo(), \"w\");\n+\tif (!todo_list_file) {\n \t\tfree(revisions);\n \t\tfree(shortrevisions);\n \n@@ -105,10 +106,11 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (restrict_revision)\n \t\targv_array_push(&make_script_args, restrict_revision);\n \n-\tret = sequencer_make_script(todo_list,\n+\tret = sequencer_make_script(&todo_list.buf,\n \t\t\t\t    make_script_args.argc, make_script_args.argv,\n \t\t\t\t    flags);\n-\tfclose(todo_list);\n+\tfputs(todo_list.buf.buf, todo_list_file);\n+\tfclose(todo_list_file);\n \n \tif (ret)\n \t\terror(_(\"could not generate todo list\"));\n@@ -120,6 +122,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \n \tfree(revisions);\n \tfree(shortrevisions);\n+\ttodo_list_release(&todo_list);\n \targv_array_clear(&make_script_args);\n \n \treturn ret;\ndiff --git a/sequencer.c b/sequencer.c\nindex fce97e5f11..3389a753b6 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4109,7 +4109,7 @@ static const char *label_oid(struct object_id *oid, const char *label,\n }\n \n static int make_script_with_merges(struct pretty_print_context *pp,\n-\t\t\t\t   struct rev_info *revs, FILE *out,\n+\t\t\t\t   struct rev_info *revs, struct strbuf *out,\n \t\t\t\t   unsigned flags)\n {\n \tint keep_empty = flags & TODO_LIST_KEEP_EMPTY;\n@@ -4254,7 +4254,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t * gathering commits not yet shown, reversing the list on the fly,\n \t * then outputting that list (labeling revisions as needed).\n \t */\n-\tfprintf(out, \"%s onto\\n\", cmd_label);\n+\tstrbuf_addf(out, \"%s onto\\n\", cmd_label);\n \tfor (iter = tips; iter; iter = iter->next) {\n \t\tstruct commit_list *list = NULL, *iter2;\n \n@@ -4264,9 +4264,9 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\tentry = oidmap_get(&state.commit2label, &commit->object.oid);\n \n \t\tif (entry)\n-\t\t\tfprintf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n+\t\t\tstrbuf_addf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n \t\telse\n-\t\t\tfprintf(out, \"\\n\");\n+\t\t\tstrbuf_addch(out, '\\n');\n \n \t\twhile (oidset_contains(&interesting, &commit->object.oid) &&\n \t\t       !oidset_contains(&shown, &commit->object.oid)) {\n@@ -4279,8 +4279,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t}\n \n \t\tif (!commit)\n-\t\t\tfprintf(out, \"%s %s\\n\", cmd_reset,\n-\t\t\t\trebase_cousins ? \"onto\" : \"[new root]\");\n+\t\t\tstrbuf_addf(out, \"%s %s\\n\", cmd_reset,\n+\t\t\t\t    rebase_cousins ? \"onto\" : \"[new root]\");\n \t\telse {\n \t\t\tconst char *to = NULL;\n \n@@ -4293,12 +4293,12 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t\t\t\t       &state);\n \n \t\t\tif (!to || !strcmp(to, \"onto\"))\n-\t\t\t\tfprintf(out, \"%s onto\\n\", cmd_reset);\n+\t\t\t\tstrbuf_addf(out, \"%s onto\\n\", cmd_reset);\n \t\t\telse {\n \t\t\t\tstrbuf_reset(&oneline);\n \t\t\t\tpretty_print_commit(pp, commit, &oneline);\n-\t\t\t\tfprintf(out, \"%s %s # %s\\n\",\n-\t\t\t\t\tcmd_reset, to, oneline.buf);\n+\t\t\t\tstrbuf_addf(out, \"%s %s # %s\\n\",\n+\t\t\t\t\t    cmd_reset, to, oneline.buf);\n \t\t\t}\n \t\t}\n \n@@ -4307,11 +4307,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t\tentry = oidmap_get(&commit2todo, oid);\n \t\t\t/* only show if not already upstream */\n \t\t\tif (entry)\n-\t\t\t\tfprintf(out, \"%s\\n\", entry->string);\n+\t\t\t\tstrbuf_addf(out, \"%s\\n\", entry->string);\n \t\t\tentry = oidmap_get(&state.commit2label, oid);\n \t\t\tif (entry)\n-\t\t\t\tfprintf(out, \"%s %s\\n\",\n-\t\t\t\t\tcmd_label, entry->string);\n+\t\t\t\tstrbuf_addf(out, \"%s %s\\n\",\n+\t\t\t\t\t    cmd_label, entry->string);\n \t\t\toidset_insert(&shown, oid);\n \t\t}\n \n@@ -4333,12 +4333,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \treturn 0;\n }\n \n-int sequencer_make_script(FILE *out, int argc, const char **argv,\n+int sequencer_make_script(struct strbuf *out, int argc, const char **argv,\n \t\t\t  unsigned flags)\n {\n \tchar *format = NULL;\n \tstruct pretty_print_context pp = {0};\n-\tstruct strbuf buf = STRBUF_INIT;\n \tstruct rev_info revs;\n \tstruct commit *commit;\n \tint keep_empty = flags & TODO_LIST_KEEP_EMPTY;\n@@ -4381,16 +4380,13 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,\n \n \t\tif (!is_empty && (commit->object.flags & PATCHSAME))\n \t\t\tcontinue;\n-\t\tstrbuf_reset(&buf);\n \t\tif (!keep_empty && is_empty)\n-\t\t\tstrbuf_addf(&buf, \"%c \", comment_line_char);\n-\t\tstrbuf_addf(&buf, \"%s %s \", insn,\n+\t\t\tstrbuf_addf(out, \"%c \", comment_line_char);\n+\t\tstrbuf_addf(out, \"%s %s \", insn,\n \t\t\t    oid_to_hex(&commit->object.oid));\n-\t\tpretty_print_commit(&pp, commit, &buf);\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t\tfputs(buf.buf, out);\n+\t\tpretty_print_commit(&pp, commit, out);\n+\t\tstrbuf_addch(out, '\\n');\n \t}\n-\tstrbuf_release(&buf);\n \treturn 0;\n }\n \ndiff --git a/sequencer.h b/sequencer.h\nindex 1cd9d7f1e2..fcde6e739d 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -137,7 +137,7 @@ int sequencer_remove_state(struct replay_opts *opts);\n #define TODO_LIST_REBASE_COUSINS (1U << 4)\n #define TODO_LIST_APPEND_TODO_HELP (1U << 5)\n \n-int sequencer_make_script(FILE *out, int argc, const char **argv,\n+int sequencer_make_script(struct strbuf *out, int argc, const char **argv,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(struct string_list *commands);\n-- \n2.19.1.872.ga867da739e\n\n"},{"id":"362794","messageId":"20181109080805.6350-10-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181109080805.6350-1-alban.gruin@gmail.com","subject":"[PATCH v3 09/16] sequencer: change complete_action() to use the refactored functions","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-09T08:07:58Z","receivedAt":"2018-11-09T08:08:46Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"complete_action() used functions that read the todo-list file, made some\nchanges to it, and wrote it back to the disk.\n\nThe previous commits were dedicated to separate the part that deals with\nthe file from the actual logic of these functions.  Now that this is\ndone, we can call directly the \"logic\" functions to avoid useless file\naccess.\n\nThe parsing of the list has to be done by the caller.  If the buffer of\nthe todo list provided by the caller is empty, a `noop' command is\ndirectly added to the todo list, without touching to the buffer.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 16 ++-----\n sequencer.c                   | 80 +++++++++++++++--------------------\n sequencer.h                   |  2 +-\n 3 files changed, 40 insertions(+), 58 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex c740a7dd5d..99cbd1e8e3 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -71,7 +71,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tconst char *head_hash = NULL;\n \tchar *revisions = NULL, *shortrevisions = NULL;\n \tstruct argv_array make_script_args = ARGV_ARRAY_INIT;\n-\tFILE *todo_list_file;\n \tstruct todo_list todo_list = TODO_LIST_INIT;\n \n \tif (prepare_branch_to_be_rebased(opts, switch_to))\n@@ -94,14 +93,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (!upstream && squash_onto)\n \t\twrite_file(path_squash_onto(), \"%s\\n\", squash_onto);\n \n-\ttodo_list_file = fopen(rebase_path_todo(), \"w\");\n-\tif (!todo_list_file) {\n-\t\tfree(revisions);\n-\t\tfree(shortrevisions);\n-\n-\t\treturn error_errno(_(\"could not open %s\"), rebase_path_todo());\n-\t}\n-\n \targv_array_pushl(&make_script_args, \"\", revisions, NULL);\n \tif (restrict_revision)\n \t\targv_array_push(&make_script_args, restrict_revision);\n@@ -109,15 +100,16 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tret = sequencer_make_script(&todo_list.buf,\n \t\t\t\t    make_script_args.argc, make_script_args.argv,\n \t\t\t\t    flags);\n-\tfputs(todo_list.buf.buf, todo_list_file);\n-\tfclose(todo_list_file);\n \n \tif (ret)\n \t\terror(_(\"could not generate todo list\"));\n \telse {\n \t\tdiscard_cache();\n+\t\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list))\n+\t\t\tBUG(\"unusable todo list\");\n+\n \t\tret = complete_action(opts, flags, shortrevisions, onto_name, onto,\n-\t\t\t\t      head_hash, commands, autosquash);\n+\t\t\t\t      head_hash, commands, autosquash, &todo_list);\n \t}\n \n \tfree(revisions);\ndiff --git a/sequencer.c b/sequencer.c\nindex 3389a753b6..64a99ab84f 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4700,93 +4700,83 @@ static int skip_unnecessary_picks(struct object_id *output_oid)\n \treturn 0;\n }\n \n+static int todo_list_rearrange_squash(struct todo_list *todo_list);\n+\n int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n-\t\t    unsigned autosquash)\n+\t\t    unsigned autosquash, struct todo_list *todo_list)\n {\n \tconst char *shortonto, *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf *buf = &(todo_list.buf);\n+\tstruct todo_list new_todo = TODO_LIST_INIT;\n+\tstruct strbuf *buf = &todo_list->buf;\n \tstruct object_id oid;\n-\tstruct stat st;\n \n \tget_oid(onto, &oid);\n \tshortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);\n \n-\tif (!lstat(todo_file, &st) && st.st_size == 0 &&\n-\t    write_message(\"noop\\n\", 5, todo_file, 0))\n-\t\treturn -1;\n+\tif (buf->len == 0) {\n+\t\tstruct todo_item *item = append_new_todo(todo_list);\n+\t\titem->command = TODO_NOOP;\n+\t\titem->commit = NULL;\n+\t\titem->arg = NULL;\n+\t\titem->arg_len = item->flags = item->offset_in_buf = 0;\n+\t}\n \n-\tif (autosquash && rearrange_squash_in_todo_file())\n+\tif (autosquash && todo_list_rearrange_squash(todo_list))\n \t\treturn -1;\n \n \tif (commands->nr)\n-\t\tsequencer_add_exec_commands(commands);\n-\n-\tif (strbuf_read_file(buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(buf->buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n+\t\ttodo_list_add_exec_commands(todo_list, commands);\n \n-\tif (count_commands(&todo_list) == 0) {\n+\tif (count_commands(todo_list) == 0) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n \n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\tstrbuf_addch(buf, '\\n');\n-\tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n-\t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n-\t\t\t\t      count_commands(&todo_list)),\n-\t\t\t      shortrevisions, shortonto, count_commands(&todo_list));\n-\tappend_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);\n-\n-\tif (write_message(buf->buf, buf->len, todo_file, 0)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tif (todo_list_write_to_file(todo_list, todo_file,\n+\t\t\t\t    shortrevisions, shortonto, -1,\n+\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n \n \tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n \t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n \t\t\t     rebase_path_todo_backup());\n \n-\tif (transform_todo_file(flags | TODO_LIST_SHORTEN_IDS))\n-\t\treturn error(_(\"could not transform the todo list\"));\n-\n-\tstrbuf_reset(buf);\n-\n-\tif (launch_sequence_editor(todo_file, buf, NULL)) {\n+\tif (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n \n \t\treturn -1;\n \t}\n \n-\tstrbuf_stripspace(buf, 1);\n-\tif (buf->len == 0) {\n+\tstrbuf_stripspace(&new_todo.buf, 1);\n+\tif (new_todo.buf.len == 0) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n+\t\ttodo_list_release(&new_todo);\n \n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\n-\tif (check_todo_list_from_file()) {\n+\tif (todo_list_parse_insn_buffer(new_todo.buf.buf, &new_todo) ||\n+\t    todo_list_check(todo_list, &new_todo)) {\n+\t\tfprintf(stderr, _(edit_todo_list_advice));\n \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n+\t\ttodo_list_release(&new_todo);\n+\n \t\treturn -1;\n \t}\n \n-\tif (transform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS)))\n-\t\treturn error(_(\"could not transform the todo list\"));\n+\tif (todo_list_write_to_file(&new_todo, todo_file, NULL, NULL, -1,\n+\t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS))) {\n+\t\ttodo_list_release(&new_todo);\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_release(&new_todo);\n \n \tif (opts->allow_ff && skip_unnecessary_picks(&oid))\n \t\treturn error(_(\"could not skip unnecessary pick commands\"));\ndiff --git a/sequencer.h b/sequencer.h\nindex fcde6e739d..996e8ea2a6 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -146,7 +146,7 @@ int check_todo_list_from_file(void);\n int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n-\t\t    unsigned autosquash);\n+\t\t    unsigned autosquash, struct todo_list *todo_list);\n int rearrange_squash_in_todo_file(void);\n \n extern const char sign_off_header[];\n-- \n2.19.1.872.ga867da739e\n\n"},{"id":"362795","messageId":"20181109080805.6350-13-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181109080805.6350-1-alban.gruin@gmail.com","subject":"[PATCH v3 12/16] rebase-interactive: append_todo_help() changes","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-09T08:08:01Z","receivedAt":"2018-11-09T08:08:47Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This moves the writing of the comment \"Rebase $shortrevisions onto\n$shortonto ($command_count commands)\" from complete_action() to\nappend_todo_help().\n\nshortrevisions, shortonto, and command_count are passed as parameters to\nappend_todo_help().\n\nDuring the initial edit of the todo list, shortrevisions and shortonto\nare not NULL.  Therefore, if shortrevisions or shortonto is NULL, then\nedit_todo would be true, otherwise it would be false.  Thus, edit_todo\nis removed from the parameters of append_todo_help().\n\nedit_todo_list() and complete_action() are modified to fit these\nchanges.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n rebase-interactive.c | 12 +++++++++++-\n rebase-interactive.h |  3 ++-\n sequencer.c          | 17 ++++-------------\n 3 files changed, 17 insertions(+), 15 deletions(-)\n\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 23569cfa3c..3b7b5e3382 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -28,7 +28,8 @@ static enum missing_commit_check_level get_missing_commit_check_level(void)\n \treturn MISSING_COMMIT_CHECK_IGNORE;\n }\n \n-void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n+void append_todo_help(unsigned keep_empty, int command_count,\n+\t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf)\n {\n \tconst char *msg = _(\"\\nCommands:\\n\"\n@@ -48,6 +49,15 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \".       specified). Use -c <commit> to reword the commit message.\\n\"\n \"\\n\"\n \"These lines can be re-ordered; they are executed from top to bottom.\\n\");\n+\tunsigned edit_todo = !(shortrevisions && shortonto);\n+\n+\tif (!edit_todo) {\n+\t\tstrbuf_addch(buf, '\\n');\n+\t\tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n+\t\t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n+\t\t\t\t\t      command_count),\n+\t\t\t\t      shortrevisions, shortonto, command_count);\n+\t}\n \n \tstrbuf_add_commented_lines(buf, msg, strlen(msg));\n \ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 6bc7bc315d..61858f3a02 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -1,7 +1,8 @@\n #ifndef REBASE_INTERACTIVE_H\n #define REBASE_INTERACTIVE_H\n \n-void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n+void append_todo_help(unsigned keep_empty, int command_count,\n+\t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf);\n int edit_todo_list(unsigned flags);\n int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\ndiff --git a/sequencer.c b/sequencer.c\nindex 1c405763c3..cf6f69c93e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4529,22 +4529,13 @@ int todo_list_write_to_file(struct todo_list *todo_list, const char *file,\n \t\t\t    const char *shortrevisions, const char *shortonto,\n \t\t\t    int num, unsigned flags)\n {\n-\tint edit_todo = !(shortrevisions && shortonto), res;\n+\tint res;\n \tstruct strbuf buf = STRBUF_INIT;\n \n \ttodo_list_to_strbuf(todo_list, &buf, num, flags);\n-\n-\tif (flags & TODO_LIST_APPEND_TODO_HELP) {\n-\t\tint command_count = count_commands(todo_list);\n-\t\tif (!edit_todo) {\n-\t\t\tstrbuf_addch(&buf, '\\n');\n-\t\t\tstrbuf_commented_addf(&buf, Q_(\"Rebase %s onto %s (%d command)\",\n-\t\t\t\t\t\t       \"Rebase %s onto %s (%d commands)\",\n-\t\t\t\t\t\t       command_count),\n-\t\t\t\t\t      shortrevisions, shortonto, command_count);\n-\t\t}\n-\t\tappend_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, &buf);\n-\t}\n+\tif (flags & TODO_LIST_APPEND_TODO_HELP)\n+\t\tappend_todo_help(flags & TODO_LIST_KEEP_EMPTY, count_commands(todo_list),\n+\t\t\t\t shortrevisions, shortonto, &buf);\n \n \tres = write_message(buf.buf, buf.len, file, 0);\n \tstrbuf_release(&buf);\n-- \n2.19.1.872.ga867da739e\n\n"},{"id":"362796","messageId":"20181109080805.6350-12-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181109080805.6350-1-alban.gruin@gmail.com","subject":"[PATCH v3 11/16] rebase-interactive: use todo_list_write_to_file() in edit_todo_list()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-09T08:08:00Z","receivedAt":"2018-11-09T08:08:48Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Just like complete_action(), edit_todo_list() used a\nfunction (transform_todo_file()) that read the todo list from the disk\nand wrote it back, resulting in useless disk accesses.\n\nThis changes edit_todo_list() to call directly todo_list_write_to_file()\ninstead.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n rebase-interactive.c | 38 ++++++++++++++++----------------------\n 1 file changed, 16 insertions(+), 22 deletions(-)\n\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 3adcf39e07..23569cfa3c 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -79,39 +79,33 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \n int edit_todo_list(unsigned flags)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n \tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n \n-\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n-\tstrbuf_stripspace(&buf, 1);\n-\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n-\t\tstrbuf_release(&buf);\n+\tstrbuf_stripspace(&todo_list.buf, 1);\n+\ttodo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n+\tif (todo_list_write_to_file(&todo_list, todo_file, NULL, NULL, -1,\n+\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP)) {\n+\t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n \n-\tstrbuf_release(&buf);\n-\n-\ttransform_todo_file(flags | TODO_LIST_SHORTEN_IDS);\n-\n-\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tappend_todo_help(1, 0, &buf);\n-\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n-\t\tstrbuf_release(&buf);\n+\tstrbuf_reset(&todo_list.buf);\n+\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n+\t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n \n-\tstrbuf_release(&buf);\n+\tif (!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list))\n+\t\tres = todo_list_write_to_file(&todo_list, todo_file, NULL, NULL, -1,\n+\t\t\t\t\t      flags & ~(TODO_LIST_SHORTEN_IDS));\n \n-\tif (launch_sequence_editor(todo_file, NULL, NULL))\n-\t\treturn -1;\n-\n-\ttransform_todo_file(flags & ~(TODO_LIST_SHORTEN_IDS));\n-\n-\treturn 0;\n+\ttodo_list_release(&todo_list);\n+\treturn res;\n }\n \n define_commit_slab(commit_seen, unsigned char);\n-- \n2.19.1.872.ga867da739e\n\n"},{"id":"362797","messageId":"20181109080805.6350-15-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181109080805.6350-1-alban.gruin@gmail.com","subject":"[PATCH v3 14/16] sequencer: use edit_todo_list() in complete_action()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-09T08:08:03Z","receivedAt":"2018-11-09T08:08:49Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This changes complete_action() to use edit_todo_list(), now that it can\nhandle the initial edit of the todo list.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 20 ++++++--------------\n 1 file changed, 6 insertions(+), 14 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex f04b002e37..02afd2f5cd 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4654,6 +4654,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \tstruct todo_list new_todo = TODO_LIST_INIT;\n \tstruct strbuf *buf = &todo_list->buf;\n \tstruct object_id oid;\n+\tint res;\n \n \tget_oid(onto, &oid);\n \tshortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);\n@@ -4679,24 +4680,15 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\tif (todo_list_write_to_file(todo_list, todo_file,\n-\t\t\t\t    shortrevisions, shortonto, -1,\n-\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))\n-\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n-\n-\tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n-\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n-\t\t\t     rebase_path_todo_backup());\n-\n-\tif (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {\n+\tres = edit_todo_list(todo_list, &new_todo, shortrevisions, shortonto, flags);\n+\tif (res == -1)\n+\t\treturn -1;\n+\telse if (res == -2) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n \n \t\treturn -1;\n-\t}\n-\n-\tstrbuf_stripspace(&new_todo.buf, 1);\n-\tif (new_todo.buf.len == 0) {\n+\t} else if (res == -3) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n \t\ttodo_list_release(&new_todo);\n-- \n2.19.1.872.ga867da739e\n\n"},{"id":"362798","messageId":"20181109080805.6350-14-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181109080805.6350-1-alban.gruin@gmail.com","subject":"[PATCH v3 13/16] rebase-interactive: rewrite edit_todo_list() to handle the initial edit","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-09T08:08:02Z","receivedAt":"2018-11-09T08:08:50Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"edit_todo_list() is changed to work on a todo_list, and to handle the\ninitial edition of the todo list (ie. making a backup of the todo\nlist).\n\nIt does not check for dropped commits yet, as todo_list_check() does not\ntake the commits that have already been processed by the rebase (ie. the\ntodo list is edited in the middle of a rebase session).\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 23 ++++++++++++++++-\n rebase-interactive.c          | 48 ++++++++++++++++++-----------------\n rebase-interactive.h          |  4 ++-\n sequencer.c                   |  3 +--\n sequencer.h                   |  1 +\n 5 files changed, 52 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 99cbd1e8e3..6871990544 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -13,6 +13,27 @@ static GIT_PATH_FUNC(path_state_dir, \"rebase-merge/\")\n static GIT_PATH_FUNC(path_squash_onto, \"rebase-merge/squash-onto\")\n static GIT_PATH_FUNC(path_interactive, \"rebase-merge/interactive\")\n \n+static int edit_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT,\n+\t\tnew_todo = TODO_LIST_INIT;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tstrbuf_stripspace(&todo_list.buf, 1);\n+\tif (!edit_todo_list(&todo_list, &new_todo, NULL, NULL, flags) &&\n+\t    todo_list_write_to_file(&new_todo, todo_file, NULL, NULL, -1,\n+\t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS)) < 0)\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n+\n+\ttodo_list_release(&todo_list);\n+\ttodo_list_release(&new_todo);\n+\n+\treturn 0;\n+}\n+\n static int get_revision_ranges(const char *upstream, const char *onto,\n \t\t\t       const char **head_hash,\n \t\t\t       char **revisions, char **shortrevisions)\n@@ -241,7 +262,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tbreak;\n \t}\n \tcase EDIT_TODO:\n-\t\tret = edit_todo_list(flags);\n+\t\tret = edit_todo_file(flags);\n \t\tbreak;\n \tcase SHOW_CURRENT_PATCH: {\n \t\tstruct child_process cmd = CHILD_PROCESS_INIT;\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 3b7b5e3382..e0fa88b90e 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -87,35 +87,37 @@ void append_todo_help(unsigned keep_empty, int command_count,\n \t}\n }\n \n-int edit_todo_list(unsigned flags)\n+int edit_todo_list(struct todo_list *todo_list, struct todo_list *new_todo,\n+\t\t   const char *shortrevisions, const char *shortonto,\n+\t\t   unsigned flags)\n {\n \tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res = 0;\n-\n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tstrbuf_stripspace(&todo_list.buf, 1);\n-\ttodo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n-\tif (todo_list_write_to_file(&todo_list, todo_file, NULL, NULL, -1,\n-\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n+\tunsigned initial = shortrevisions && shortonto;\n+\n+\tif (initial) {\n+\t\ttodo_list_write_to_file(todo_list, todo_file, shortrevisions, shortonto, -1,\n+\t\t\t\t\tflags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP);\n+\n+\t\tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n+\t\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n+\t\t\t\t     rebase_path_todo_backup());\n+\t} else {\n+\t\ttodo_list_parse_insn_buffer(todo_list->buf.buf, todo_list);\n+\t\ttodo_list_write_to_file(todo_list, todo_file, NULL, NULL, -1,\n+\t\t\t\t\tflags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP);\n \t}\n \n-\tstrbuf_reset(&todo_list.buf);\n-\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tif (launch_sequence_editor(todo_file, &new_todo->buf, NULL))\n+\t\treturn -2;\n \n-\tif (!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list))\n-\t\tres = todo_list_write_to_file(&todo_list, todo_file, NULL, NULL, -1,\n-\t\t\t\t\t      flags & ~(TODO_LIST_SHORTEN_IDS));\n+\tstrbuf_stripspace(&new_todo->buf, 1);\n+\tif (initial && new_todo->buf.len == 0)\n+\t\treturn -3;\n \n-\ttodo_list_release(&todo_list);\n-\treturn res;\n+\tif (!initial)\n+\t\ttodo_list_parse_insn_buffer(new_todo->buf.buf, new_todo);\n+\n+\treturn 0;\n }\n \n define_commit_slab(commit_seen, unsigned char);\ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 61858f3a02..8d5c04521e 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -4,7 +4,9 @@\n void append_todo_help(unsigned keep_empty, int command_count,\n \t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf);\n-int edit_todo_list(unsigned flags);\n+int edit_todo_list(struct todo_list *todo_list, struct todo_list *new_todo,\n+\t\t   const char *shortrevisions, const char *shortonto,\n+\t\t   unsigned flags);\n int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n \n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex cf6f69c93e..f04b002e37 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -55,8 +55,7 @@ static GIT_PATH_FUNC(rebase_path, \"rebase-merge\")\n  * file and written to the tail of 'done'.\n  */\n GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n-static GIT_PATH_FUNC(rebase_path_todo_backup,\n-\t\t     \"rebase-merge/git-rebase-todo.backup\")\n+GIT_PATH_FUNC(rebase_path_todo_backup, \"rebase-merge/git-rebase-todo.backup\")\n \n /*\n  * The rebase command lines that have already been processed. A line\ndiff --git a/sequencer.h b/sequencer.h\nindex 996e8ea2a6..f6751d53b9 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -9,6 +9,7 @@ struct commit;\n const char *git_path_commit_editmsg(void);\n const char *git_path_seq_dir(void);\n const char *rebase_path_todo(void);\n+const char *rebase_path_todo_backup(void);\n \n #define APPEND_SIGNOFF_DEDUP (1u << 0)\n \n-- \n2.19.1.872.ga867da739e\n\n"},{"id":"362799","messageId":"20181109080805.6350-16-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181109080805.6350-1-alban.gruin@gmail.com","subject":"[PATCH v3 15/16] sequencer: fix a call to error() in transform_todo_file()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-09T08:08:04Z","receivedAt":"2018-11-09T08:08:51Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This replaces a call to error() by a call to error_errno() after writing\nthe content of the todo list to the disk in transform_todo_file().\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v2.\n\n sequencer.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 02afd2f5cd..a55df3526f 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4464,7 +4464,7 @@ int sequencer_add_exec_commands(struct string_list *commands)\n \tint res;\n \n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n \tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n-- \n2.19.1.872.ga867da739e\n\n"},{"id":"362800","messageId":"20181109080805.6350-17-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181109080805.6350-1-alban.gruin@gmail.com","subject":"[PATCH v3 16/16] rebase--interactive: move transform_todo_file() to rebase--interactive.c","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-09T08:08:05Z","receivedAt":"2018-11-09T08:08:53Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"As transform_todo_file() is only needed inside of rebase--interactive.c,\nit is moved there from sequencer.c.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 20 ++++++++++++++++++++\n sequencer.c                   | 20 --------------------\n sequencer.h                   |  1 -\n 3 files changed, 20 insertions(+), 21 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 6871990544..580c6a3822 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -34,6 +34,26 @@ static int edit_todo_file(unsigned flags)\n \treturn 0;\n }\n \n+static int transform_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\tres = todo_list_write_to_file(&todo_list, todo_file,\n+\t\t\t\t      NULL, NULL, -1, flags);\n+\ttodo_list_release(&todo_list);\n+\treturn res;\n+}\n+\n static int get_revision_ranges(const char *upstream, const char *onto,\n \t\t\t       const char **head_hash,\n \t\t\t       char **revisions, char **shortrevisions)\ndiff --git a/sequencer.c b/sequencer.c\nindex a55df3526f..896dd04150 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4542,26 +4542,6 @@ int todo_list_write_to_file(struct todo_list *todo_list, const char *file,\n \treturn res;\n }\n \n-int transform_todo_file(unsigned flags)\n-{\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res;\n-\n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tres = todo_list_write_to_file(&todo_list, todo_file,\n-\t\t\t\t      NULL, NULL, -1, flags);\n-\ttodo_list_release(&todo_list);\n-\treturn res;\n-}\n-\n static const char edit_todo_list_advice[] =\n N_(\"You can fix this with 'git rebase --edit-todo' \"\n \"and then run 'git rebase --continue'.\\n\"\ndiff --git a/sequencer.h b/sequencer.h\nindex f6751d53b9..ee59233344 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -142,7 +142,6 @@ int sequencer_make_script(struct strbuf *out, int argc, const char **argv,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(struct string_list *commands);\n-int transform_todo_file(unsigned flags);\n int check_todo_list_from_file(void);\n int complete_action(struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-- \n2.19.1.872.ga867da739e\n\n"},{"id":"362801","messageId":"20181109080805.6350-11-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181109080805.6350-1-alban.gruin@gmail.com","subject":"[PATCH v3 10/16] sequencer: refactor skip_unnecessary_picks() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-09T08:07:59Z","receivedAt":"2018-11-09T08:08:54Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors skip_unnecessary_picks() to work on a todo_list.  The\nfile-handling logic is completely dropped here, as its only usage is\nmade by complete_action().\n\nInstead of truncating the todo list’s buffer, the items are moved to\nthe beginning of the list, eliminating the need to reparse the list.\nThis also means its buffer cannot be directly written to the disk.\n\nrewrite_file() is then removed, as it is now unused.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 79 ++++++++++++-----------------------------------------\n 1 file changed, 17 insertions(+), 62 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 64a99ab84f..1c405763c3 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4607,52 +4607,21 @@ int check_todo_list_from_file(void)\n \treturn res;\n }\n \n-static int rewrite_file(const char *path, const char *buf, size_t len)\n-{\n-\tint rc = 0;\n-\tint fd = open(path, O_WRONLY | O_TRUNC);\n-\tif (fd < 0)\n-\t\treturn error_errno(_(\"could not open '%s' for writing\"), path);\n-\tif (write_in_full(fd, buf, len) < 0)\n-\t\trc = error_errno(_(\"could not write to '%s'\"), path);\n-\tif (close(fd) && !rc)\n-\t\trc = error_errno(_(\"could not close '%s'\"), path);\n-\treturn rc;\n-}\n-\n /* skip picking commits whose parents are unchanged */\n-static int skip_unnecessary_picks(struct object_id *output_oid)\n+static int skip_unnecessary_picks(struct todo_list *todo_list,\n+\t\t\t\t  struct object_id *output_oid)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct object_id *parent_oid;\n-\tint fd, i;\n-\n-\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n-\t\treturn error(_(\"could not read 'onto'\"));\n-\tif (get_oid(buf.buf, output_oid)) {\n-\t\tstrbuf_release(&buf);\n-\t\treturn error(_(\"need a HEAD to fixup\"));\n-\t}\n-\tstrbuf_release(&buf);\n-\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tint i;\n \n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tstruct todo_item *item = todo_list.items + i;\n+\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\tstruct todo_item *item = todo_list->items + i;\n \n \t\tif (item->command >= TODO_NOOP)\n \t\t\tcontinue;\n \t\tif (item->command != TODO_PICK)\n \t\t\tbreak;\n \t\tif (parse_commit(item->commit)) {\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\treturn error(_(\"could not parse commit '%s'\"),\n \t\t\t\toid_to_hex(&item->commit->object.oid));\n \t\t}\n@@ -4666,37 +4635,21 @@ static int skip_unnecessary_picks(struct object_id *output_oid)\n \t\toidcpy(output_oid, &item->commit->object.oid);\n \t}\n \tif (i > 0) {\n-\t\tint offset = get_item_line_offset(&todo_list, i);\n \t\tconst char *done_path = rebase_path_done();\n \n-\t\tfd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);\n-\t\tif (fd < 0) {\n-\t\t\terror_errno(_(\"could not open '%s' for writing\"),\n-\t\t\t\t    done_path);\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\treturn -1;\n-\t\t}\n-\t\tif (write_in_full(fd, todo_list.buf.buf, offset) < 0) {\n+\t\tif (todo_list_write_to_file(todo_list, done_path, NULL, NULL, i, 0)) {\n \t\t\terror_errno(_(\"could not write to '%s'\"), done_path);\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\tclose(fd);\n \t\t\treturn -1;\n \t\t}\n-\t\tclose(fd);\n \n-\t\tif (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,\n-\t\t\t\t todo_list.buf.len - offset) < 0) {\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\treturn -1;\n-\t\t}\n+\t\tMOVE_ARRAY(todo_list->items, todo_list->items + i, todo_list->nr - i);\n+\t\ttodo_list->nr -= i;\n+\t\ttodo_list->current = 0;\n \n-\t\ttodo_list.current = i;\n-\t\tif (is_fixup(peek_command(&todo_list, 0)))\n-\t\t\trecord_in_rewritten(output_oid, peek_command(&todo_list, 0));\n+\t\tif (is_fixup(peek_command(todo_list, 0)))\n+\t\t\trecord_in_rewritten(output_oid, peek_command(todo_list, 0));\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\n \treturn 0;\n }\n \n@@ -4770,6 +4723,11 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \t\treturn -1;\n \t}\n \n+\tif (opts->allow_ff && skip_unnecessary_picks(&new_todo, &oid)) {\n+\t\ttodo_list_release(&new_todo);\n+\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n+\t}\n+\n \tif (todo_list_write_to_file(&new_todo, todo_file, NULL, NULL, -1,\n \t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS))) {\n \t\ttodo_list_release(&new_todo);\n@@ -4778,12 +4736,9 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \n \ttodo_list_release(&new_todo);\n \n-\tif (opts->allow_ff && skip_unnecessary_picks(&oid))\n-\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n-\n \tif (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))\n \t\treturn -1;\n-;\n+\n \tif (require_clean_work_tree(\"rebase\", \"\", 1, 1))\n \t\treturn -1;\n \n-- \n2.19.1.872.ga867da739e\n\n"},{"id":"362802","messageId":"20181109080805.6350-3-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181109080805.6350-1-alban.gruin@gmail.com","subject":"[PATCH v3 02/16] sequencer: make the todo_list structure public","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-11-09T08:07:51Z","receivedAt":"2018-11-09T08:08:59Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This makes the structures todo_list and todo_item, and the functions\ntodo_list_release() and parse_insn_buffer(), accessible outside of\nsequencer.c.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 67 +++++++++--------------------------------------------\n sequencer.h | 49 +++++++++++++++++++++++++++++++++++++++\n 2 files changed, 60 insertions(+), 56 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 07cc91d8db..7adbeaa27d 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1443,32 +1443,6 @@ static int allow_empty(struct replay_opts *opts, struct commit *commit)\n \t\treturn 1;\n }\n \n-/*\n- * Note that ordering matters in this enum. Not only must it match the mapping\n- * below, it is also divided into several sections that matter.  When adding\n- * new commands, make sure you add it in the right section.\n- */\n-enum todo_command {\n-\t/* commands that handle commits */\n-\tTODO_PICK = 0,\n-\tTODO_REVERT,\n-\tTODO_EDIT,\n-\tTODO_REWORD,\n-\tTODO_FIXUP,\n-\tTODO_SQUASH,\n-\t/* commands that do something else than handling a single commit */\n-\tTODO_EXEC,\n-\tTODO_BREAK,\n-\tTODO_LABEL,\n-\tTODO_RESET,\n-\tTODO_MERGE,\n-\t/* commands that do nothing but are counted for reporting progress */\n-\tTODO_NOOP,\n-\tTODO_DROP,\n-\t/* comments (not counted for reporting progress) */\n-\tTODO_COMMENT\n-};\n-\n static struct {\n \tchar c;\n \tconst char *str;\n@@ -1939,26 +1913,7 @@ enum todo_item_flags {\n \tTODO_EDIT_MERGE_MSG = 1\n };\n \n-struct todo_item {\n-\tenum todo_command command;\n-\tstruct commit *commit;\n-\tunsigned int flags;\n-\tconst char *arg;\n-\tint arg_len;\n-\tsize_t offset_in_buf;\n-};\n-\n-struct todo_list {\n-\tstruct strbuf buf;\n-\tstruct todo_item *items;\n-\tint nr, alloc, current;\n-\tint done_nr, total_nr;\n-\tstruct stat_data stat;\n-};\n-\n-#define TODO_LIST_INIT { STRBUF_INIT }\n-\n-static void todo_list_release(struct todo_list *todo_list)\n+void todo_list_release(struct todo_list *todo_list)\n {\n \tstrbuf_release(&todo_list->buf);\n \tFREE_AND_NULL(todo_list->items);\n@@ -2060,7 +2015,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \treturn !item->commit;\n }\n \n-static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n+int todo_list_parse_insn_buffer(char *buf, struct todo_list *todo_list)\n {\n \tstruct todo_item *item;\n \tchar *p = buf, *next_p;\n@@ -2158,7 +2113,7 @@ static int read_populate_todo(struct todo_list *todo_list,\n \t\treturn error(_(\"could not stat '%s'\"), todo_file);\n \tfill_stat_data(&todo_list->stat, &st);\n \n-\tres = parse_insn_buffer(todo_list->buf.buf, todo_list);\n+\tres = todo_list_parse_insn_buffer(todo_list->buf.buf, todo_list);\n \tif (res) {\n \t\tif (is_rebase_i(opts))\n \t\t\treturn error(_(\"please fix this using \"\n@@ -2189,7 +2144,7 @@ static int read_populate_todo(struct todo_list *todo_list,\n \t\tFILE *f = fopen_or_warn(rebase_path_msgtotal(), \"w\");\n \n \t\tif (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&\n-\t\t\t\t!parse_insn_buffer(done.buf.buf, &done))\n+\t\t\t\t!todo_list_parse_insn_buffer(done.buf.buf, &done))\n \t\t\ttodo_list->done_nr = count_commands(&done);\n \t\telse\n \t\t\ttodo_list->done_nr = 0;\n@@ -4454,7 +4409,7 @@ int sequencer_add_exec_commands(const char *commands)\n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4510,7 +4465,7 @@ int transform_todos(unsigned flags)\n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4596,7 +4551,7 @@ int check_todo_list(void)\n \t\tgoto leave_check;\n \t}\n \tadvise_to_edit_todo = res =\n-\t\tparse_insn_buffer(todo_list.buf.buf, &todo_list);\n+\t\ttodo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n \n \tif (res || check_level == MISSING_COMMIT_CHECK_IGNORE)\n \t\tgoto leave_check;\n@@ -4615,7 +4570,7 @@ int check_todo_list(void)\n \t\tgoto leave_check;\n \t}\n \tstrbuf_release(&todo_file);\n-\tres = !!parse_insn_buffer(todo_list.buf.buf, &todo_list);\n+\tres = !!todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list);\n \n \t/* Find commits in git-rebase-todo.backup yet unseen */\n \tfor (i = todo_list.nr - 1; i >= 0; i--) {\n@@ -4697,7 +4652,7 @@ static int skip_unnecessary_picks(struct object_id *output_oid)\n \n \tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n \t\treturn -1;\n-\tif (parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n@@ -4785,7 +4740,7 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(buf->buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(buf->buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4893,7 +4848,7 @@ int rearrange_squash(void)\n \n \tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n \t\treturn -1;\n-\tif (parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n+\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list) < 0) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex 660cff5050..84f813305f 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -72,6 +72,55 @@ enum missing_commit_check_level {\n int write_message(const void *buf, size_t len, const char *filename,\n \t\t  int append_eol);\n \n+/*\n+ * Note that ordering matters in this enum. Not only must it match the mapping\n+ * of todo_command_info (in sequencer.c), it is also divided into several\n+ * sections that matter.  When adding new commands, make sure you add it in the\n+ * right section.\n+ */\n+enum todo_command {\n+\t/* commands that handle commits */\n+\tTODO_PICK = 0,\n+\tTODO_REVERT,\n+\tTODO_EDIT,\n+\tTODO_REWORD,\n+\tTODO_FIXUP,\n+\tTODO_SQUASH,\n+\t/* commands that do something else than handling a single commit */\n+\tTODO_EXEC,\n+\tTODO_BREAK,\n+\tTODO_LABEL,\n+\tTODO_RESET,\n+\tTODO_MERGE,\n+\t/* commands that do nothing but are counted for reporting progress */\n+\tTODO_NOOP,\n+\tTODO_DROP,\n+\t/* comments (not counted for reporting progress) */\n+\tTODO_COMMENT\n+};\n+\n+struct todo_item {\n+\tenum todo_command command;\n+\tstruct commit *commit;\n+\tunsigned int flags;\n+\tconst char *arg;\n+\tint arg_len;\n+\tsize_t offset_in_buf;\n+};\n+\n+struct todo_list {\n+\tstruct strbuf buf;\n+\tstruct todo_item *items;\n+\tint nr, alloc, current;\n+\tint done_nr, total_nr;\n+\tstruct stat_data stat;\n+};\n+\n+#define TODO_LIST_INIT { STRBUF_INIT }\n+\n+int todo_list_parse_insn_buffer(char *buf, struct todo_list *todo_list);\n+void todo_list_release(struct todo_list *todo_list);\n+\n /* Call this to setup defaults before parsing command line options */\n void sequencer_init_config(struct replay_opts *opts);\n int sequencer_pick_revisions(struct replay_opts *opts);\n-- \n2.19.1.872.ga867da739e\n\n"},{"id":"364400","messageId":"0e00acd9-5b09-b712-2e21-550e348ea0df@talktalk.net","threadId":"49504","inReplyTo":"20181109080805.6350-7-alban.gruin@gmail.com","subject":"Re: [PATCH v3 06/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2018-11-30T17:02:05Z","receivedAt":"2018-11-30T17:02:13Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nSorry it has taken me a while to look at the latest iteration. I like\nthe changes to pass a list of strings for the exec commands. I've only\nhad a chance to take a quick look, but I've got a couple of comments below\n\nOn 09/11/2018 08:07, Alban Gruin wrote:\n> This refactors sequencer_add_exec_commands() to work on a todo_list to\n> avoid redundant reads and writes to the disk.\n> \n> Instead of just inserting the `exec' command between the other commands,\n> and re-parsing the buffer at the end the exec command is appended to the\n> buffer once, and a new list of items is created.  Items from the old\n> list are copied across and new `exec' items are appended when\n> necessary.  This eliminates the need to reparse the buffer, but this\n> also means we have to use todo_list_write_to_disk() to write the file().\n> \n> todo_list_add_exec_commands() and sequencer_add_exec_commands() are\n> modified to take a string list instead of a string -- one item for each\n> command.  This makes it easier to insert a new command to the todo list\n> for each command to execute.\n> \n> sequencer_add_exec_commands() still reads the todo list from the disk,\n> as it is needed by rebase -p.\n> \n> complete_action() still uses sequencer_add_exec_commands() for now.\n> This will be changed in a future commit.\n> \n> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n> ---\n>  builtin/rebase--interactive.c |  15 +++--\n>  sequencer.c                   | 111 +++++++++++++++++++++-------------\n>  sequencer.h                   |   4 +-\n>  3 files changed, 83 insertions(+), 47 deletions(-)\n> \n> diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\n> index c1d87c0fe6..1fb5a43c0d 100644\n> --- a/builtin/rebase--interactive.c\n> +++ b/builtin/rebase--interactive.c\n> @@ -65,7 +65,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n>  \t\t\t\t const char *onto, const char *onto_name,\n>  \t\t\t\t const char *squash_onto, const char *head_name,\n>  \t\t\t\t const char *restrict_revision, char *raw_strategies,\n> -\t\t\t\t const char *cmd, unsigned autosquash)\n> +\t\t\t\t struct string_list *commands, unsigned autosquash)\n>  {\n>  \tint ret;\n>  \tconst char *head_hash = NULL;\n> @@ -115,7 +115,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n>  \telse {\n>  \t\tdiscard_cache();\n>  \t\tret = complete_action(opts, flags, shortrevisions, onto_name, onto,\n> -\t\t\t\t      head_hash, cmd, autosquash);\n> +\t\t\t\t      head_hash, commands, autosquash);\n>  \t}\n>  \n>  \tfree(revisions);\n> @@ -138,6 +138,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \tconst char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,\n>  \t\t*squash_onto = NULL, *upstream = NULL, *head_name = NULL,\n>  \t\t*switch_to = NULL, *cmd = NULL;\n> +\tstruct string_list commands = STRING_LIST_INIT_DUP;\n>  \tchar *raw_strategies = NULL;\n>  \tenum {\n>  \t\tNONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,\n> @@ -220,6 +221,12 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \t\twarning(_(\"--[no-]rebase-cousins has no effect without \"\n>  \t\t\t  \"--rebase-merges\"));\n>  \n> +\tif (cmd && *cmd) {\n> +\t\tstring_list_split(&commands, cmd, '\\n', -1);\n> +\t\tif (strlen(commands.items[commands.nr - 1].string) == 0)\n> +\t\t\t--commands.nr;\n> +\t}\n> +\n>  \tswitch (command) {\n>  \tcase NONE:\n>  \t\tif (!onto && !upstream)\n> @@ -227,7 +234,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \n>  \t\tret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,\n>  \t\t\t\t\t    onto_name, squash_onto, head_name, restrict_revision,\n> -\t\t\t\t\t    raw_strategies, cmd, autosquash);\n> +\t\t\t\t\t    raw_strategies, &commands, autosquash);\n>  \t\tbreak;\n>  \tcase SKIP: {\n>  \t\tstruct string_list merge_rr = STRING_LIST_INIT_DUP;\n> @@ -261,7 +268,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \t\tret = rearrange_squash();\n>  \t\tbreak;\n>  \tcase ADD_EXEC:\n> -\t\tret = sequencer_add_exec_commands(cmd);\n> +\t\tret = sequencer_add_exec_commands(&commands);\n>  \t\tbreak;\n>  \tdefault:\n>  \t\tBUG(\"invalid command '%d'\", command);\n> diff --git a/sequencer.c b/sequencer.c\n> index 900899ef20..11692d0b98 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -4394,24 +4394,29 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,\n>  \treturn 0;\n>  }\n>  \n> -/*\n> - * Add commands after pick and (series of) squash/fixup commands\n> - * in the todo list.\n> - */\n> -int sequencer_add_exec_commands(const char *commands)\n> +static void todo_list_add_exec_commands(struct todo_list *todo_list,\n> +\t\t\t\t\tstruct string_list *commands)\n>  {\n> -\tconst char *todo_file = rebase_path_todo();\n> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n> -\tstruct strbuf *buf = &todo_list.buf;\n> -\tsize_t offset = 0, commands_len = strlen(commands);\n> -\tint i, insert;\n> +\tstruct strbuf *buf = &todo_list->buf;\n> +\tconst char *old_buf = buf->buf;\n> +\tsize_t base_length = buf->len;\n> +\tint i, insert, nr = 0, alloc = 0;\n> +\tstruct todo_item *items = NULL, *base_items = NULL;\n>  \n> -\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> -\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n> +\tfor (i = 0; i < commands->nr; ++i) {\n> +\t\tstrbuf_addstr(buf, commands->items[i].string);\n> +\t\tstrbuf_addch(buf, '\\n');\n> +\t}\n\nThis could cause buf->buf to be reallocated in which case all the \ntodo_list_item.arg pointers will be invalidated. I think you want to \ncovert all those pointers to offsets stored in a temporary array and \nthen recreate the correct pointers from the offsets after appending to buf.\n\n> -\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n> -\t\ttodo_list_release(&todo_list);\n> -\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> +\tbase_items = xcalloc(commands->nr, sizeof(struct todo_item));\n> +\tfor (i = 0; i < commands->nr; ++i) {\n> +\t\tsize_t command_len = strlen(commands->items[i].string) - strlen(\"exec \");\n> +\n> +\t\tbase_items[i].command = TODO_EXEC;\n> +\t\tbase_items[i].offset_in_buf = base_length + strlen(\"exec \");\n\nCurrently parse_insn_buffer() sets offset_in_buf to the beginning of the\nline, it does not skip the command name I think it would be good to keep \nthis consistent with all the other commands.\n\n> +\t\tbase_items[i].arg = buf->buf + base_items[i].offset_in_buf;\n> +\t\tbase_items[i].arg_len = command_len;\n> +\t\tbase_length = base_items[i].offset_in_buf + base_items[i].arg_len + 1;\n\nbase_length is actually on offset into the buffer, it took me a while to \nfigure out that the calculation is correct. It might be simpler to do\n\nbase_offset += strlen(commands->items[i].string) + 1;\n\n>  \t}\n>  \n>  \t/*\n> @@ -4420,38 +4425,62 @@ int sequencer_add_exec_commands(const char *commands)\n>  \t * those chains if there are any.\n>  \t */\n>  \tinsert = -1;\n> -\tfor (i = 0; i < todo_list.nr; i++) {\n> -\t\tenum todo_command command = todo_list.items[i].command;\n> -\n> -\t\tif (insert >= 0) {\n> -\t\t\t/* skip fixup/squash chains */\n> -\t\t\tif (command == TODO_COMMENT)\n> -\t\t\t\tcontinue;\n> -\t\t\telse if (is_fixup(command)) {\n> -\t\t\t\tinsert = i + 1;\n> -\t\t\t\tcontinue;\n> -\t\t\t}\n> -\t\t\tstrbuf_insert(buf,\n> -\t\t\t\t      todo_list.items[insert].offset_in_buf +\n> -\t\t\t\t      offset, commands, commands_len);\n> -\t\t\toffset += commands_len;\n> +\tfor (i = 0; i < todo_list->nr; i++) {\n> +\t\tenum todo_command command = todo_list->items[i].command;\n> +\t\tif (todo_list->items[i].arg)\n> +\t\t\ttodo_list->items[i].arg = todo_list->items[i].arg - old_buf + buf->buf;\n\nI think that is undefined behavior the old value of a pointer passed \nrealloc() is undefined so cannot be used for pointer arithmetic. Using a \ntemporary array of offsets as suggested above would avoid this.\n\nBest Wishes\n\nPhillip\n\n> +\n> +\t\tif (insert >= 0 && command != TODO_COMMENT && !is_fixup(command)) {\n> +\t\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n> +\t\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n> +\t\t\tnr += commands->nr;\n>  \t\t\tinsert = -1;\n>  \t\t}\n>  \n> -\t\tif (command == TODO_PICK || command == TODO_MERGE)\n> +\t\tALLOC_GROW(items, nr + 1, alloc);\n> +\t\titems[nr++] = todo_list->items[i];\n> +\n> +\t\tif (command == TODO_PICK || command == TODO_MERGE || is_fixup(command))\n>  \t\t\tinsert = i + 1;\n>  \t}\n>  \n>  \t/* insert or append final <commands> */\n> -\tif (insert >= 0 && insert < todo_list.nr)\n> -\t\tstrbuf_insert(buf, todo_list.items[insert].offset_in_buf +\n> -\t\t\t      offset, commands, commands_len);\n> -\telse if (insert >= 0 || !offset)\n> -\t\tstrbuf_add(buf, commands, commands_len);\n> +\tif (insert >= 0 || nr == todo_list->nr) {\n> +\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n> +\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n> +\t\tnr += commands->nr;\n> +\t}\n> +\n> +\tfree(base_items);\n> +\tFREE_AND_NULL(todo_list->items);\n> +\ttodo_list->items = items;\n> +\ttodo_list->nr = nr;\n> +\ttodo_list->alloc = alloc;\n> +}\n> +\n> +/*\n> + * Add commands after pick and (series of) squash/fixup commands\n> + * in the todo list.\n> + */\n> +int sequencer_add_exec_commands(struct string_list *commands)\n> +{\n> +\tconst char *todo_file = rebase_path_todo();\n> +\tstruct todo_list todo_list = TODO_LIST_INIT;\n> +\tint res;\n>  \n> -\ti = write_message(buf->buf, buf->len, todo_file, 0);\n> +\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> +\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n> +\n> +\tif (todo_list_parse_insn_buffer(todo_list.buf.buf, &todo_list)) {\n> +\t\ttodo_list_release(&todo_list);\n> +\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> +\t}\n> +\n> +\ttodo_list_add_exec_commands(&todo_list, commands);\n> +\tres = todo_list_write_to_file(&todo_list, todo_file, NULL, NULL, -1, 0);\n>  \ttodo_list_release(&todo_list);\n> -\treturn i;\n> +\n> +\treturn res;\n>  }\n>  \n>  static void todo_list_to_strbuf(struct todo_list *todo_list, struct strbuf *buf,\n> @@ -4677,7 +4706,7 @@ static int skip_unnecessary_picks(struct object_id *output_oid)\n>  \n>  int complete_action(struct replay_opts *opts, unsigned flags,\n>  \t\t    const char *shortrevisions, const char *onto_name,\n> -\t\t    const char *onto, const char *orig_head, const char *cmd,\n> +\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n>  \t\t    unsigned autosquash)\n>  {\n>  \tconst char *shortonto, *todo_file = rebase_path_todo();\n> @@ -4696,8 +4725,8 @@ int complete_action(struct replay_opts *opts, unsigned flags,\n>  \tif (autosquash && rearrange_squash())\n>  \t\treturn -1;\n>  \n> -\tif (cmd && *cmd)\n> -\t\tsequencer_add_exec_commands(cmd);\n> +\tif (commands->nr)\n> +\t\tsequencer_add_exec_commands(commands);\n>  \n>  \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n>  \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> diff --git a/sequencer.h b/sequencer.h\n> index c78f00aadd..d7b52044bd 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -140,12 +140,12 @@ int sequencer_remove_state(struct replay_opts *opts);\n>  int sequencer_make_script(FILE *out, int argc, const char **argv,\n>  \t\t\t  unsigned flags);\n>  \n> -int sequencer_add_exec_commands(const char *command);\n> +int sequencer_add_exec_commands(struct string_list *commands);\n>  int transform_todo_file(unsigned flags);\n>  int check_todo_list_from_file(void);\n>  int complete_action(struct replay_opts *opts, unsigned flags,\n>  \t\t    const char *shortrevisions, const char *onto_name,\n> -\t\t    const char *onto, const char *orig_head, const char *cmd,\n> +\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n>  \t\t    unsigned autosquash);\n>  int rearrange_squash(void);\n>  \n> \n\n"},{"id":"364405","messageId":"nycvar.QRO.7.76.6.1811302002340.41@tvgsbejvaqbjf.bet","threadId":"49504","inReplyTo":"0e00acd9-5b09-b712-2e21-550e348ea0df@talktalk.net","subject":"Re: [PATCH v3 06/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2018-11-30T19:06:33Z","receivedAt":"2018-11-30T19:06:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 30 Nov 2018, Phillip Wood wrote:\n\n> > diff --git a/sequencer.c b/sequencer.c\n> > index 900899ef20..11692d0b98 100644\n> > --- a/sequencer.c\n> > +++ b/sequencer.c\n> > @@ -4394,24 +4394,29 @@ int sequencer_make_script(FILE *out, int argc, const\n> > char **argv,\n> >  \treturn 0;\n> >  }\n> >  \n> > -/*\n> > - * Add commands after pick and (series of) squash/fixup commands\n> > - * in the todo list.\n> > - */\n> > -int sequencer_add_exec_commands(const char *commands)\n> > +static void todo_list_add_exec_commands(struct todo_list *todo_list,\n> > +\t\t\t\t\tstruct string_list *commands)\n> > {\n> > -\tconst char *todo_file = rebase_path_todo();\n> > -\tstruct todo_list todo_list = TODO_LIST_INIT;\n> > -\tstruct strbuf *buf = &todo_list.buf;\n> > -\tsize_t offset = 0, commands_len = strlen(commands);\n> > -\tint i, insert;\n> > +\tstruct strbuf *buf = &todo_list->buf;\n> > +\tconst char *old_buf = buf->buf;\n> > +\tsize_t base_length = buf->len;\n> > +\tint i, insert, nr = 0, alloc = 0;\n> > +\tstruct todo_item *items = NULL, *base_items = NULL;\n> > \n> > -\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> > -\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n> > +\tfor (i = 0; i < commands->nr; ++i) {\n> > +\t\tstrbuf_addstr(buf, commands->items[i].string);\n> > +\t\tstrbuf_addch(buf, '\\n');\n> > +\t}\n> \n> This could cause buf->buf to be reallocated in which case all the\n> todo_list_item.arg pointers will be invalidated.\n\nI guess it is a good time for me to admit that the `arg` idea was not my\nbest. Maybe it would be good to convert that from a pointer to an offset,\ne.g. `size_t arg_offset_in_buf;`? Or maybe we should just drop the\n`_in_buf` suffix and clarify in a comment next to the declaration of the\nfields that they are offsets in the strbuf?\n\nCiao,\nDscho\n"},{"id":"364921","messageId":"a1a828e5-6faa-b1f2-20c3-a53b1a3d779a@talktalk.net","threadId":"49504","inReplyTo":"nycvar.QRO.7.76.6.1811302002340.41@tvgsbejvaqbjf.bet","subject":"Re: [PATCH v3 06/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2018-12-10T14:33:32Z","receivedAt":"2018-12-10T14:33:38Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 30/11/2018 19:06, Johannes Schindelin wrote:\n> Hi,\n> \n> On Fri, 30 Nov 2018, Phillip Wood wrote:\n> \n>>> diff --git a/sequencer.c b/sequencer.c\n>>> index 900899ef20..11692d0b98 100644\n>>> --- a/sequencer.c\n>>> +++ b/sequencer.c\n>>> @@ -4394,24 +4394,29 @@ int sequencer_make_script(FILE *out, int argc, const\n>>> char **argv,\n>>>   \treturn 0;\n>>>   }\n>>>   \n>>> -/*\n>>> - * Add commands after pick and (series of) squash/fixup commands\n>>> - * in the todo list.\n>>> - */\n>>> -int sequencer_add_exec_commands(const char *commands)\n>>> +static void todo_list_add_exec_commands(struct todo_list *todo_list,\n>>> +\t\t\t\t\tstruct string_list *commands)\n>>> {\n>>> -\tconst char *todo_file = rebase_path_todo();\n>>> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n>>> -\tstruct strbuf *buf = &todo_list.buf;\n>>> -\tsize_t offset = 0, commands_len = strlen(commands);\n>>> -\tint i, insert;\n>>> +\tstruct strbuf *buf = &todo_list->buf;\n>>> +\tconst char *old_buf = buf->buf;\n>>> +\tsize_t base_length = buf->len;\n>>> +\tint i, insert, nr = 0, alloc = 0;\n>>> +\tstruct todo_item *items = NULL, *base_items = NULL;\n>>>\n>>> -\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>>> -\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n>>> +\tfor (i = 0; i < commands->nr; ++i) {\n>>> +\t\tstrbuf_addstr(buf, commands->items[i].string);\n>>> +\t\tstrbuf_addch(buf, '\\n');\n>>> +\t}\n>>\n>> This could cause buf->buf to be reallocated in which case all the\n>> todo_list_item.arg pointers will be invalidated.\n> \n> I guess it is a good time for me to admit that the `arg` idea was not my\n> best. Maybe it would be good to convert that from a pointer to an offset,\n> e.g. `size_t arg_offset_in_buf;`? Or maybe we should just drop the\n> `_in_buf` suffix and clarify in a comment next to the declaration of the\n> fields that they are offsets in the strbuf?\n\nI think that sounds sensible (though I haven't looked at how much work \nit would be), having a comment and calling it arg_offset would be my \npreference.\n\nBest Wishes\n\nPhillip\n> \n> Ciao,\n> Dscho\n> \n\n"},{"id":"365910","messageId":"58d2a1e9-e798-acb6-d8af-3e67e18df057@gmail.com","threadId":"49504","inReplyTo":"a1a828e5-6faa-b1f2-20c3-a53b1a3d779a@talktalk.net","subject":"Re: [PATCH v3 06/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-28T19:42:18Z","receivedAt":"2018-12-28T19:42:26Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Hi Phillip and Johannes,\n\nmany thanks for your suggestions and feedback, I really appreciate it.\n\nLe 10/12/2018 à 15:33, Phillip Wood a écrit :\n> On 30/11/2018 19:06, Johannes Schindelin wrote:\n>> Hi,\n>>\n>> On Fri, 30 Nov 2018, Phillip Wood wrote:\n>>\n>>>> diff --git a/sequencer.c b/sequencer.c\n>>>> index 900899ef20..11692d0b98 100644\n>>>> --- a/sequencer.c\n>>>> +++ b/sequencer.c\n>>>> @@ -4394,24 +4394,29 @@ int sequencer_make_script(FILE *out, int\n>>>> argc, const\n>>>> char **argv,\n>>>>       return 0;\n>>>>   }\n>>>>   -/*\n>>>> - * Add commands after pick and (series of) squash/fixup commands\n>>>> - * in the todo list.\n>>>> - */\n>>>> -int sequencer_add_exec_commands(const char *commands)\n>>>> +static void todo_list_add_exec_commands(struct todo_list *todo_list,\n>>>> +                    struct string_list *commands)\n>>>> {\n>>>> -    const char *todo_file = rebase_path_todo();\n>>>> -    struct todo_list todo_list = TODO_LIST_INIT;\n>>>> -    struct strbuf *buf = &todo_list.buf;\n>>>> -    size_t offset = 0, commands_len = strlen(commands);\n>>>> -    int i, insert;\n>>>> +    struct strbuf *buf = &todo_list->buf;\n>>>> +    const char *old_buf = buf->buf;\n>>>> +    size_t base_length = buf->len;\n>>>> +    int i, insert, nr = 0, alloc = 0;\n>>>> +    struct todo_item *items = NULL, *base_items = NULL;\n>>>>\n>>>> -    if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>>>> -        return error(_(\"could not read '%s'.\"), todo_file);\n>>>> +    for (i = 0; i < commands->nr; ++i) {\n>>>> +        strbuf_addstr(buf, commands->items[i].string);\n>>>> +        strbuf_addch(buf, '\\n');\n>>>> +    }\n>>>\n>>> This could cause buf->buf to be reallocated in which case all the\n>>> todo_list_item.arg pointers will be invalidated.\n>>\n>> I guess it is a good time for me to admit that the `arg` idea was not my\n>> best. Maybe it would be good to convert that from a pointer to an offset,\n>> e.g. `size_t arg_offset_in_buf;`? Or maybe we should just drop the\n>> `_in_buf` suffix and clarify in a comment next to the declaration of the\n>> fields that they are offsets in the strbuf?\n> \n> I think that sounds sensible (though I haven't looked at how much work\n> it would be), having a comment and calling it arg_offset would be my\n> preference.\n> \n\nIt’s not a lot of work, actually.  Most usages of 'arg' are concentrated\nin two functions (parse_insn_line() and pick_commits()).  Some of the\nsubsequent patches of this series also use 'arg', and adapting them is\ntrivial.\n\nIn the end, most of the work went into todo_list_add_exec_commands(),\nand the result is pretty clean.\n\nCheers,\nAlban\n\n"},{"id":"365950","messageId":"20181229160413.19333-1-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181109080805.6350-1-alban.gruin@gmail.com","subject":"[PATCH v4 00/16] sequencer: refactor functions working on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-29T16:03:57Z","receivedAt":"2018-12-29T16:05:03Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"At the center of the \"interactive\" part of the interactive rebase lies\nthe todo list.  When the user starts an interactive rebase, a todo list\nis generated, presented to the user (who then edits it using a text\neditor), read back, and then is checked and processed before the actual\nrebase takes place.\n\nSome of this processing includes adding execs commands, reordering\nfixup! and squash! commits, and checking if no commits were accidentally\ndropped by the user.\n\nBefore I converted the interactive rebase in C, these functions were\ncalled by git-rebase--interactive.sh through git-rebase--helper.  Since\nthe only way to pass around a large amount of data between a shell\nscript and a C program is to use a file (or any declination of a file),\nthe functions that checked and processed the todo list were directly\nworking on a file, the same file that the user edited.\n\nDuring the conversion, I did not address this issue, which lead to a\ncomplete_action() that reads the todo list file, does some computation\nbased on its content, and writes it back to the disk, several times in\nthe same function.\n\nAs it is not an efficient way to handle a data structure, this patch\nseries refactor the functions that processes the todo list to work on a\ntodo_list structure instead of reading it from the disk.\n\nSome commits consists in modifying edit_todo_list() (initially used by\n--edit-todo) to handle the initial edition of the todo list, to increase\ncode sharing.\n\nThis is based on nd/the-index (36e7ed69de, \"rebase-interactive.c: remove\nthe_repository references\"), as it introduced a lot of conflicts.  The\nresult does not conflict with pu (e31bc98f4b, \"Merge branch\n'md/list-objects-filter-by-depth' into pu\").\n\nChanges since v3:\n\n - Replacing the 'arg' field from todo_item by 'arg_offset' to avoid\n   dealing with pointers on the todo list buffer in\n   todo_list_add_exec_commands().  This has led to some additionnal\n   changes.\n\n - Rewording some commits.\n\n - Dropping the commit \"sequencer: fix a call to error() in\n   transform_todo_file()\".  The call to error() after reading the todo\n   file is replaced by error_errno() in \"sequencer: refactor\n   transform_todos() to work on a todo_list\".  The same change has been\n   applied to sequencer_add_exec_commands() in \"sequencer: refactor\n   sequencer_add_exec_commands() to work on a todo_list\".\n\n - transform_todo_file(), sequencer_add_exec_commands() and\n   rearrange_squash_in_todo_file() now print an error if they fail to\n   write to the todo file.\n\n - A lot of changes were introduced by the conflict resolution with\n   nd/the-index.\n\nAlban Gruin (16):\n  sequencer: changes in parse_insn_buffer()\n  sequencer: make the todo_list structure public\n  sequencer: remove the 'arg' field from todo_item\n  sequencer: refactor transform_todos() to work on a todo_list\n  sequencer: introduce todo_list_write_to_file()\n  sequencer: refactor check_todo_list() to work on a todo_list\n  sequencer: refactor sequencer_add_exec_commands() to work on a\n    todo_list\n  sequencer: refactor rearrange_squash() to work on a todo_list\n  sequencer: make sequencer_make_script() write its script to a strbuf\n  sequencer: change complete_action() to use the refactored functions\n  sequencer: refactor skip_unnecessary_picks() to work on a todo_list\n  rebase-interactive: use todo_list_write_to_file() in edit_todo_list()\n  rebase-interactive: append_todo_help() changes\n  rebase-interactive: rewrite edit_todo_list() to handle the initial\n    edit\n  sequencer: use edit_todo_list() in complete_action()\n  rebase--interactive: move transform_todo_file() to\n    rebase--interactive.c\n\n builtin/rebase--interactive.c |  90 +++--\n rebase-interactive.c          | 143 +++++--\n rebase-interactive.h          |   8 +-\n sequencer.c                   | 677 +++++++++++++---------------------\n sequencer.h                   |  82 +++-\n 5 files changed, 525 insertions(+), 475 deletions(-)\n\n-- \n2.20.1\n\n"},{"id":"365951","messageId":"20181229160413.19333-2-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181229160413.19333-1-alban.gruin@gmail.com","subject":"[PATCH v4 01/16] sequencer: changes in parse_insn_buffer()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-29T16:03:58Z","receivedAt":"2018-12-29T16:05:03Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This clears the number of items of a todo_list before parsing it to\nallow to parse the same list multiple times without issues.  As its\nitems are not dynamically allocated, or don’t need to allocate memory,\nno additionnal memory management is required here.\n\nFurthermore, if a line is invalid, the type of the corresponding\ncommand is set to a garbage value, and its argument is defined properly.\nThis will allow to recreate the text of a todo list from its commands,\neven if one of them is incorrect.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nThe full diff changed due to the conflict resolution with nd/the-index,\nbut the \"core change\" is the same as in the v3.\n\n sequencer.c | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex d726f77e11..a7afaf6882 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2091,6 +2091,8 @@ static int parse_insn_buffer(struct repository *r, char *buf,\n \tchar *p = buf, *next_p;\n \tint i, res = 0, fixup_okay = file_exists(rebase_path_done());\n \n+\ttodo_list->current = todo_list->nr = 0;\n+\n \tfor (i = 1; *p; i++, p = next_p) {\n \t\tchar *eol = strchrnul(p, '\\n');\n \n@@ -2104,7 +2106,10 @@ static int parse_insn_buffer(struct repository *r, char *buf,\n \t\tif (parse_insn_line(r, item, p, eol)) {\n \t\t\tres = error(_(\"invalid line %d: %.*s\"),\n \t\t\t\ti, (int)(eol - p), p);\n-\t\t\titem->command = TODO_NOOP;\n+\t\t\titem->command = TODO_COMMENT + 1;\n+\t\t\titem->arg = p;\n+\t\t\titem->arg_len = (int)(eol - p);\n+\t\t\titem->commit = NULL;\n \t\t}\n \n \t\tif (fixup_okay)\n-- \n2.20.1\n\n"},{"id":"365952","messageId":"20181229160413.19333-3-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181229160413.19333-1-alban.gruin@gmail.com","subject":"[PATCH v4 02/16] sequencer: make the todo_list structure public","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-29T16:03:59Z","receivedAt":"2018-12-29T16:05:07Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This makes the structures todo_list and todo_item, and the functions\ntodo_list_release() and parse_insn_buffer(), accessible outside of\nsequencer.c.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nSome changes were introduced because of the conflict resolution with\nnd/the-index.  They mostly consist of adding a parameter (\"r\") when\ncalling todo_list_parse_insn_buffer.\n\n sequencer.c | 69 ++++++++++-------------------------------------------\n sequencer.h | 50 ++++++++++++++++++++++++++++++++++++++\n 2 files changed, 62 insertions(+), 57 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a7afaf6882..5b84a20532 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1460,32 +1460,6 @@ static int allow_empty(struct repository *r,\n \t\treturn 1;\n }\n \n-/*\n- * Note that ordering matters in this enum. Not only must it match the mapping\n- * below, it is also divided into several sections that matter.  When adding\n- * new commands, make sure you add it in the right section.\n- */\n-enum todo_command {\n-\t/* commands that handle commits */\n-\tTODO_PICK = 0,\n-\tTODO_REVERT,\n-\tTODO_EDIT,\n-\tTODO_REWORD,\n-\tTODO_FIXUP,\n-\tTODO_SQUASH,\n-\t/* commands that do something else than handling a single commit */\n-\tTODO_EXEC,\n-\tTODO_BREAK,\n-\tTODO_LABEL,\n-\tTODO_RESET,\n-\tTODO_MERGE,\n-\t/* commands that do nothing but are counted for reporting progress */\n-\tTODO_NOOP,\n-\tTODO_DROP,\n-\t/* comments (not counted for reporting progress) */\n-\tTODO_COMMENT\n-};\n-\n static struct {\n \tchar c;\n \tconst char *str;\n@@ -1962,26 +1936,7 @@ enum todo_item_flags {\n \tTODO_EDIT_MERGE_MSG = 1\n };\n \n-struct todo_item {\n-\tenum todo_command command;\n-\tstruct commit *commit;\n-\tunsigned int flags;\n-\tconst char *arg;\n-\tint arg_len;\n-\tsize_t offset_in_buf;\n-};\n-\n-struct todo_list {\n-\tstruct strbuf buf;\n-\tstruct todo_item *items;\n-\tint nr, alloc, current;\n-\tint done_nr, total_nr;\n-\tstruct stat_data stat;\n-};\n-\n-#define TODO_LIST_INIT { STRBUF_INIT }\n-\n-static void todo_list_release(struct todo_list *todo_list)\n+void todo_list_release(struct todo_list *todo_list)\n {\n \tstrbuf_release(&todo_list->buf);\n \tFREE_AND_NULL(todo_list->items);\n@@ -2084,8 +2039,8 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \treturn !item->commit;\n }\n \n-static int parse_insn_buffer(struct repository *r, char *buf,\n-\t\t\t     struct todo_list *todo_list)\n+int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n+\t\t\t\tstruct todo_list *todo_list)\n {\n \tstruct todo_item *item;\n \tchar *p = buf, *next_p;\n@@ -2184,7 +2139,7 @@ static int read_populate_todo(struct repository *r,\n \t\treturn error(_(\"could not stat '%s'\"), todo_file);\n \tfill_stat_data(&todo_list->stat, &st);\n \n-\tres = parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n+\tres = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n \tif (res) {\n \t\tif (is_rebase_i(opts))\n \t\t\treturn error(_(\"please fix this using \"\n@@ -2215,7 +2170,7 @@ static int read_populate_todo(struct repository *r,\n \t\tFILE *f = fopen_or_warn(rebase_path_msgtotal(), \"w\");\n \n \t\tif (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&\n-\t\t    !parse_insn_buffer(r, done.buf.buf, &done))\n+\t\t    !todo_list_parse_insn_buffer(r, done.buf.buf, &done))\n \t\t\ttodo_list->done_nr = count_commands(&done);\n \t\telse\n \t\t\ttodo_list->done_nr = 0;\n@@ -4501,7 +4456,7 @@ int sequencer_add_exec_commands(struct repository *r,\n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4557,7 +4512,7 @@ int transform_todos(struct repository *r, unsigned flags)\n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4643,7 +4598,7 @@ int check_todo_list(struct repository *r)\n \t\tgoto leave_check;\n \t}\n \tadvise_to_edit_todo = res =\n-\t\tparse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n+\t\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n \n \tif (res || check_level == MISSING_COMMIT_CHECK_IGNORE)\n \t\tgoto leave_check;\n@@ -4662,7 +4617,7 @@ int check_todo_list(struct repository *r)\n \t\tgoto leave_check;\n \t}\n \tstrbuf_release(&todo_file);\n-\tres = !!parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n+\tres = !!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n \n \t/* Find commits in git-rebase-todo.backup yet unseen */\n \tfor (i = todo_list.nr - 1; i >= 0; i--) {\n@@ -4744,7 +4699,7 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \n \tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n \t\treturn -1;\n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n@@ -4832,7 +4787,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(r, buf->buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(r, buf->buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4940,7 +4895,7 @@ int rearrange_squash(struct repository *r)\n \n \tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n \t\treturn -1;\n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex d2c18edd3a..7dc4d8946b 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -73,6 +73,56 @@ enum missing_commit_check_level {\n int write_message(const void *buf, size_t len, const char *filename,\n \t\t  int append_eol);\n \n+/*\n+ * Note that ordering matters in this enum. Not only must it match the mapping\n+ * of todo_command_info (in sequencer.c), it is also divided into several\n+ * sections that matter.  When adding new commands, make sure you add it in the\n+ * right section.\n+ */\n+enum todo_command {\n+\t/* commands that handle commits */\n+\tTODO_PICK = 0,\n+\tTODO_REVERT,\n+\tTODO_EDIT,\n+\tTODO_REWORD,\n+\tTODO_FIXUP,\n+\tTODO_SQUASH,\n+\t/* commands that do something else than handling a single commit */\n+\tTODO_EXEC,\n+\tTODO_BREAK,\n+\tTODO_LABEL,\n+\tTODO_RESET,\n+\tTODO_MERGE,\n+\t/* commands that do nothing but are counted for reporting progress */\n+\tTODO_NOOP,\n+\tTODO_DROP,\n+\t/* comments (not counted for reporting progress) */\n+\tTODO_COMMENT\n+};\n+\n+struct todo_item {\n+\tenum todo_command command;\n+\tstruct commit *commit;\n+\tunsigned int flags;\n+\tconst char *arg;\n+\tint arg_len;\n+\tsize_t offset_in_buf;\n+};\n+\n+struct todo_list {\n+\tstruct strbuf buf;\n+\tstruct todo_item *items;\n+\tint nr, alloc, current;\n+\tint done_nr, total_nr;\n+\tstruct stat_data stat;\n+};\n+\n+#define TODO_LIST_INIT { STRBUF_INIT }\n+\n+int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n+\t\t\t\tstruct todo_list *todo_list);\n+void todo_list_release(struct todo_list *todo_list);\n+\n /* Call this to setup defaults before parsing command line options */\n void sequencer_init_config(struct replay_opts *opts);\n int sequencer_pick_revisions(struct repository *repo,\n-- \n2.20.1\n\n"},{"id":"365953","messageId":"20181229160413.19333-4-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181229160413.19333-1-alban.gruin@gmail.com","subject":"[PATCH v4 03/16] sequencer: remove the 'arg' field from todo_item","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-29T16:04:00Z","receivedAt":"2018-12-29T16:05:09Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"The 'arg' field of todo_item used to store the address of the first byte\nof the parameter of a command in a todo list.  It was associated with\nthe length of the parameter (the 'arg_len' field).\n\nThis replaces the 'arg' field by 'arg_offset'.  This new field does not\nstore the address of the parameter, but the position of the first\ncharacter of the parameter in the buffer.\n\nThis will prevent todo_list_add_exec_commands() from having to do awful\npointer arithmetics when growing the todo list buffer.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nThis is a new commit.\n\n sequencer.c | 61 ++++++++++++++++++++++++++++-------------------------\n sequencer.h |  4 ++--\n 2 files changed, 34 insertions(+), 31 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 5b84a20532..61be04ccc5 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1950,7 +1950,7 @@ static struct todo_item *append_new_todo(struct todo_list *todo_list)\n }\n \n static int parse_insn_line(struct repository *r, struct todo_item *item,\n-\t\t\t   const char *bol, char *eol)\n+\t\t\t   const char *buf, const char *bol, char *eol)\n {\n \tstruct object_id commit_oid;\n \tchar *end_of_object_name;\n@@ -1964,7 +1964,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \tif (bol == eol || *bol == '\\r' || *bol == comment_line_char) {\n \t\titem->command = TODO_COMMENT;\n \t\titem->commit = NULL;\n-\t\titem->arg = bol;\n+\t\titem->arg_offset = bol - buf;\n \t\titem->arg_len = eol - bol;\n \t\treturn 0;\n \t}\n@@ -1991,7 +1991,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \t\t\treturn error(_(\"%s does not accept arguments: '%s'\"),\n \t\t\t\t     command_to_string(item->command), bol);\n \t\titem->commit = NULL;\n-\t\titem->arg = bol;\n+\t\titem->arg_offset = bol - buf;\n \t\titem->arg_len = eol - bol;\n \t\treturn 0;\n \t}\n@@ -2003,7 +2003,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \tif (item->command == TODO_EXEC || item->command == TODO_LABEL ||\n \t    item->command == TODO_RESET) {\n \t\titem->commit = NULL;\n-\t\titem->arg = bol;\n+\t\titem->arg_offset = bol - buf;\n \t\titem->arg_len = (int)(eol - bol);\n \t\treturn 0;\n \t}\n@@ -2017,7 +2017,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \t\t} else {\n \t\t\titem->flags |= TODO_EDIT_MERGE_MSG;\n \t\t\titem->commit = NULL;\n-\t\t\titem->arg = bol;\n+\t\t\titem->arg_offset = bol - buf;\n \t\t\titem->arg_len = (int)(eol - bol);\n \t\t\treturn 0;\n \t\t}\n@@ -2029,8 +2029,9 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \tstatus = get_oid(bol, &commit_oid);\n \t*end_of_object_name = saved;\n \n-\titem->arg = end_of_object_name + strspn(end_of_object_name, \" \\t\");\n-\titem->arg_len = (int)(eol - item->arg);\n+\tbol = end_of_object_name + strspn(end_of_object_name, \" \\t\");\n+\titem->arg_offset = bol - buf;\n+\titem->arg_len = (int)(eol - bol);\n \n \tif (status < 0)\n \t\treturn -1;\n@@ -2058,11 +2059,11 @@ int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \n \t\titem = append_new_todo(todo_list);\n \t\titem->offset_in_buf = p - todo_list->buf.buf;\n-\t\tif (parse_insn_line(r, item, p, eol)) {\n+\t\tif (parse_insn_line(r, item, buf, p, eol)) {\n \t\t\tres = error(_(\"invalid line %d: %.*s\"),\n \t\t\t\ti, (int)(eol - p), p);\n \t\t\titem->command = TODO_COMMENT + 1;\n-\t\t\titem->arg = p;\n+\t\t\titem->arg_offset = p - buf;\n \t\t\titem->arg_len = (int)(eol - p);\n \t\t\titem->commit = NULL;\n \t\t}\n@@ -2402,7 +2403,7 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,\n \n \t\titem->command = command;\n \t\titem->commit = commit;\n-\t\titem->arg = NULL;\n+\t\titem->arg_offset = 0;\n \t\titem->arg_len = 0;\n \t\titem->offset_in_buf = todo_list->buf.len;\n \t\tsubject_len = find_commit_subject(commit_buffer, &subject);\n@@ -3438,6 +3439,8 @@ static int pick_commits(struct repository *r,\n \n \twhile (todo_list->current < todo_list->nr) {\n \t\tstruct todo_item *item = todo_list->items + todo_list->current;\n+\t\tconst char *arg = todo_list->buf.buf + item->arg_offset;\n+\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n \t\tif (is_rebase_i(opts)) {\n@@ -3488,10 +3491,9 @@ static int pick_commits(struct repository *r,\n \t\t\t\t\tfprintf(stderr,\n \t\t\t\t\t\t_(\"Stopped at %s...  %.*s\\n\"),\n \t\t\t\t\t\tshort_commit_name(commit),\n-\t\t\t\t\t\titem->arg_len, item->arg);\n+\t\t\t\t\t\titem->arg_len, arg);\n \t\t\t\treturn error_with_patch(r, commit,\n-\t\t\t\t\titem->arg, item->arg_len, opts, res,\n-\t\t\t\t\t!res);\n+\t\t\t\t\targ, item->arg_len, opts, res, !res);\n \t\t\t}\n \t\t\tif (is_rebase_i(opts) && !res)\n \t\t\t\trecord_in_rewritten(&item->commit->object.oid,\n@@ -3500,7 +3502,7 @@ static int pick_commits(struct repository *r,\n \t\t\t\tif (res == 1)\n \t\t\t\t\tintend_to_amend();\n \t\t\t\treturn error_failed_squash(r, item->commit, opts,\n-\t\t\t\t\titem->arg_len, item->arg);\n+\t\t\t\t\titem->arg_len, arg);\n \t\t\t} else if (res && is_rebase_i(opts) && item->commit) {\n \t\t\t\tint to_amend = 0;\n \t\t\t\tstruct object_id oid;\n@@ -3519,16 +3521,16 @@ static int pick_commits(struct repository *r,\n \t\t\t\t\tto_amend = 1;\n \n \t\t\t\treturn res | error_with_patch(r, item->commit,\n-\t\t\t\t\t\titem->arg, item->arg_len, opts,\n+\t\t\t\t\t\targ, item->arg_len, opts,\n \t\t\t\t\t\tres, to_amend);\n \t\t\t}\n \t\t} else if (item->command == TODO_EXEC) {\n-\t\t\tchar *end_of_arg = (char *)(item->arg + item->arg_len);\n+\t\t\tchar *end_of_arg = (char *)(arg + item->arg_len);\n \t\t\tint saved = *end_of_arg;\n \t\t\tstruct stat st;\n \n \t\t\t*end_of_arg = '\\0';\n-\t\t\tres = do_exec(r, item->arg);\n+\t\t\tres = do_exec(r, arg);\n \t\t\t*end_of_arg = saved;\n \n \t\t\t/* Reread the todo file if it has changed. */\n@@ -3545,14 +3547,14 @@ static int pick_commits(struct repository *r,\n \t\t\t\ttodo_list->current = -1;\n \t\t\t}\n \t\t} else if (item->command == TODO_LABEL) {\n-\t\t\tif ((res = do_label(r, item->arg, item->arg_len)))\n+\t\t\tif ((res = do_label(r, arg, item->arg_len)))\n \t\t\t\treschedule = 1;\n \t\t} else if (item->command == TODO_RESET) {\n-\t\t\tif ((res = do_reset(r, item->arg, item->arg_len, opts)))\n+\t\t\tif ((res = do_reset(r, arg, item->arg_len, opts)))\n \t\t\t\treschedule = 1;\n \t\t} else if (item->command == TODO_MERGE) {\n \t\t\tif ((res = do_merge(r, item->commit,\n-\t\t\t\t\t    item->arg, item->arg_len,\n+\t\t\t\t\t    arg, item->arg_len,\n \t\t\t\t\t    item->flags, opts)) < 0)\n \t\t\t\treschedule = 1;\n \t\t\telse if (item->commit)\n@@ -3561,9 +3563,8 @@ static int pick_commits(struct repository *r,\n \t\t\tif (res > 0)\n \t\t\t\t/* failed with merge conflicts */\n \t\t\t\treturn error_with_patch(r, item->commit,\n-\t\t\t\t\t\t\titem->arg,\n-\t\t\t\t\t\t\titem->arg_len, opts,\n-\t\t\t\t\t\t\tres, 0);\n+\t\t\t\t\t\t\targ, item->arg_len,\n+\t\t\t\t\t\t\topts, res, 0);\n \t\t} else if (!is_noop(item->command))\n \t\t\treturn error(_(\"unknown command %d\"), item->command);\n \n@@ -3578,9 +3579,8 @@ static int pick_commits(struct repository *r,\n \t\t\tif (item->commit)\n \t\t\t\treturn error_with_patch(r,\n \t\t\t\t\t\t\titem->commit,\n-\t\t\t\t\t\t\titem->arg,\n-\t\t\t\t\t\t\titem->arg_len, opts,\n-\t\t\t\t\t\t\tres, 0);\n+\t\t\t\t\t\t\targ, item->arg_len,\n+\t\t\t\t\t\t\topts, res, 0);\n \t\t}\n \n \t\ttodo_list->current++;\n@@ -4520,7 +4520,8 @@ int transform_todos(struct repository *r, unsigned flags)\n \tfor (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n-\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len, item->arg);\n+\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len,\n+\t\t\t\t    todo_list.buf.buf + item->arg_offset);\n \t\t\tcontinue;\n \t\t}\n \n@@ -4550,7 +4551,8 @@ int transform_todos(struct repository *r, unsigned flags)\n \t\tif (!item->arg_len)\n \t\t\tstrbuf_addch(&buf, '\\n');\n \t\telse\n-\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len, item->arg);\n+\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len,\n+\t\t\t\t    todo_list.buf.buf + item->arg_offset);\n \t}\n \n \ti = write_message(buf.buf, buf.len, todo_file, 0);\n@@ -4626,7 +4628,8 @@ int check_todo_list(struct repository *r)\n \t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n \t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n \t\t\t\t    short_commit_name(commit),\n-\t\t\t\t    item->arg_len, item->arg);\n+\t\t\t\t    item->arg_len,\n+\t\t\t\t    todo_list.buf.buf + item->arg_offset);\n \t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n \t\t}\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex 7dc4d8946b..70a896e756 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -104,9 +104,9 @@ struct todo_item {\n \tenum todo_command command;\n \tstruct commit *commit;\n \tunsigned int flags;\n-\tconst char *arg;\n \tint arg_len;\n-\tsize_t offset_in_buf;\n+\t/* The offset of the command and its argument in the strbuf */\n+\tsize_t offset_in_buf, arg_offset;\n };\n \n struct todo_list {\n-- \n2.20.1\n\n"},{"id":"365954","messageId":"20181229160413.19333-5-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181229160413.19333-1-alban.gruin@gmail.com","subject":"[PATCH v4 04/16] sequencer: refactor transform_todos() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-29T16:04:01Z","receivedAt":"2018-12-29T16:05:12Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors transform_todos() to work on a todo_list.  The function\nis renamed todo_list_transform().\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, transform_todo_file().  It is still used by\ncomplete_action() and edit_todo_list() for now, but they will be\nreplaced in a future commit.\n\ntodo_list_transform() is not a static function, because it will be used\nby edit_todo_list() from rebase-interactive.c in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |  2 +-\n rebase-interactive.c          |  4 +--\n sequencer.c                   | 54 +++++++++++++++++++++++------------\n sequencer.h                   |  4 ++-\n 4 files changed, 42 insertions(+), 22 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex dd2a55ab1d..0898eb4c59 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -253,7 +253,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t}\n \tcase SHORTEN_OIDS:\n \tcase EXPAND_OIDS:\n-\t\tret = transform_todos(the_repository, flags);\n+\t\tret = transform_todo_file(the_repository, flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n \t\tret = check_todo_list(the_repository);\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 68aff1dac2..842fa07e7e 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -69,7 +69,7 @@ int edit_todo_list(struct repository *r, unsigned flags)\n \n \tstrbuf_release(&buf);\n \n-\ttransform_todos(r, flags | TODO_LIST_SHORTEN_IDS);\n+\ttransform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS);\n \n \tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n@@ -85,7 +85,7 @@ int edit_todo_list(struct repository *r, unsigned flags)\n \tif (launch_sequence_editor(todo_file, NULL, NULL))\n \t\treturn -1;\n \n-\ttransform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS));\n+\ttransform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS));\n \n \treturn 0;\n }\ndiff --git a/sequencer.c b/sequencer.c\nindex 61be04ccc5..6b4eb7a9ba 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4501,27 +4501,18 @@ int sequencer_add_exec_commands(struct repository *r,\n \treturn i;\n }\n \n-int transform_todos(struct repository *r, unsigned flags)\n+void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n+\t\t\t unsigned flags)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct todo_item *item;\n \tint i;\n \n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tfor (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {\n+\tfor (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n \t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len,\n-\t\t\t\t    todo_list.buf.buf + item->arg_offset);\n+\t\t\t\t    todo_list->buf.buf + item->arg_offset);\n \t\t\tcontinue;\n \t\t}\n \n@@ -4552,12 +4543,39 @@ int transform_todos(struct repository *r, unsigned flags)\n \t\t\tstrbuf_addch(&buf, '\\n');\n \t\telse\n \t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len,\n-\t\t\t\t    todo_list.buf.buf + item->arg_offset);\n+\t\t\t\t    todo_list->buf.buf + item->arg_offset);\n \t}\n \n-\ti = write_message(buf.buf, buf.len, todo_file, 0);\n+\tstrbuf_reset(&todo_list->buf);\n+\tstrbuf_add(&todo_list->buf, buf.buf, buf.len);\n+\tstrbuf_release(&buf);\n+\n+\tif (todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list))\n+\t\tBUG(\"unusable todo list\");\n+}\n+\n+int transform_todo_file(struct repository *r, unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_transform(r, &todo_list, flags);\n+\n+\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n \ttodo_list_release(&todo_list);\n-\treturn i;\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n }\n \n enum missing_commit_check_level get_missing_commit_check_level(void)\n@@ -4819,7 +4837,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n \t\t\t     rebase_path_todo_backup());\n \n-\tif (transform_todos(r, flags | TODO_LIST_SHORTEN_IDS))\n+\tif (transform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS))\n \t\treturn error(_(\"could not transform the todo list\"));\n \n \tstrbuf_reset(buf);\n@@ -4848,7 +4866,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn -1;\n \t}\n \n-\tif (transform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS)))\n+\tif (transform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS)))\n \t\treturn error(_(\"could not transform the todo list\"));\n \n \tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\ndiff --git a/sequencer.h b/sequencer.h\nindex 70a896e756..53ffe7371c 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -121,6 +121,8 @@ struct todo_list {\n \n int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \t\t\t\tstruct todo_list *todo_list);\n+void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n+\t\t\t unsigned flags);\n void todo_list_release(struct todo_list *todo_list);\n \n /* Call this to setup defaults before parsing command line options */\n@@ -146,7 +148,7 @@ int sequencer_make_script(struct repository *repo, FILE *out,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(struct repository *r, const char *command);\n-int transform_todos(struct repository *r, unsigned flags);\n+int transform_todo_file(struct repository *r, unsigned flags);\n enum missing_commit_check_level get_missing_commit_check_level(void);\n int check_todo_list(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n-- \n2.20.1\n\n"},{"id":"365955","messageId":"20181229160413.19333-6-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181229160413.19333-1-alban.gruin@gmail.com","subject":"[PATCH v4 05/16] sequencer: introduce todo_list_write_to_file()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-29T16:04:02Z","receivedAt":"2018-12-29T16:05:12Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This introduces a new function to recreate the text of a todo list from\nits commands and write it to a file.  This will be useful as the next\nfew commits will change the use of the buffer in struct todo_list so it\nwill no longer be a mirror of the file on disk.\n\nThis functionality already exists in todo_list_transform(), but this\nfunction was made to replace the buffer of a todo list, which is not\nwhat we want here.  Thus, the part of todo_list_transform() that\nreplaces the buffer is dropped, and the function is renamed\ntodo_list_to_strbuf().  It is called by todo_list_write_to_file() to\nfill the buffer to write to the disk.\n\ntodo_list_write_to_file() can also take care of appending the help text\nto the buffer before writing it to the disk, or to write only the first\nn items of the list.  This feature will be used by\nskip_unnecessary_picks(), which has to write done commands in a file.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 61 +++++++++++++++++++++++++++++++++++------------------\n sequencer.h | 11 ++++++----\n 2 files changed, 48 insertions(+), 24 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 6b4eb7a9ba..b932ca34f2 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4501,26 +4501,28 @@ int sequencer_add_exec_commands(struct repository *r,\n \treturn i;\n }\n \n-void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n-\t\t\t unsigned flags)\n+static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n+\t\t\t\tstruct strbuf *buf, int num, unsigned flags)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n \tstruct todo_item *item;\n-\tint i;\n+\tint i, max = todo_list->nr;\n \n-\tfor (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {\n+\tif (num > 0 && num < max)\n+\t\tmax = num;\n+\n+\tfor (item = todo_list->items, i = 0; i < max; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n-\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len,\n+\t\t\tstrbuf_addf(buf, \"%.*s\\n\", item->arg_len,\n \t\t\t\t    todo_list->buf.buf + item->arg_offset);\n \t\t\tcontinue;\n \t\t}\n \n \t\t/* add command to the buffer */\n \t\tif (flags & TODO_LIST_ABBREVIATE_CMDS)\n-\t\t\tstrbuf_addch(&buf, command_to_char(item->command));\n+\t\t\tstrbuf_addch(buf, command_to_char(item->command));\n \t\telse\n-\t\t\tstrbuf_addstr(&buf, command_to_string(item->command));\n+\t\t\tstrbuf_addstr(buf, command_to_string(item->command));\n \n \t\t/* add commit id */\n \t\tif (item->commit) {\n@@ -4530,28 +4532,48 @@ void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n \n \t\t\tif (item->command == TODO_MERGE) {\n \t\t\t\tif (item->flags & TODO_EDIT_MERGE_MSG)\n-\t\t\t\t\tstrbuf_addstr(&buf, \" -c\");\n+\t\t\t\t\tstrbuf_addstr(buf, \" -c\");\n \t\t\t\telse\n-\t\t\t\t\tstrbuf_addstr(&buf, \" -C\");\n+\t\t\t\t\tstrbuf_addstr(buf, \" -C\");\n \t\t\t}\n \n-\t\t\tstrbuf_addf(&buf, \" %s\", oid);\n+\t\t\tstrbuf_addf(buf, \" %s\", oid);\n \t\t}\n \n \t\t/* add all the rest */\n \t\tif (!item->arg_len)\n-\t\t\tstrbuf_addch(&buf, '\\n');\n+\t\t\tstrbuf_addch(buf, '\\n');\n \t\telse\n-\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len,\n+\t\t\tstrbuf_addf(buf, \" %.*s\\n\", item->arg_len,\n \t\t\t\t    todo_list->buf.buf + item->arg_offset);\n \t}\n+}\n \n-\tstrbuf_reset(&todo_list->buf);\n-\tstrbuf_add(&todo_list->buf, buf.buf, buf.len);\n+int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n+\t\t\t    const char *file, const char *shortrevisions,\n+\t\t\t    const char *shortonto, int num, unsigned flags)\n+{\n+\tint edit_todo = !(shortrevisions && shortonto), res;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\ttodo_list_to_strbuf(r, todo_list, &buf, num, flags);\n+\n+\tif (flags & TODO_LIST_APPEND_TODO_HELP) {\n+\t\tint command_count = count_commands(todo_list);\n+\t\tif (!edit_todo) {\n+\t\t\tstrbuf_addch(&buf, '\\n');\n+\t\t\tstrbuf_commented_addf(&buf, Q_(\"Rebase %s onto %s (%d command)\",\n+\t\t\t\t\t\t       \"Rebase %s onto %s (%d commands)\",\n+\t\t\t\t\t\t       command_count),\n+\t\t\t\t\t      shortrevisions, shortonto, command_count);\n+\t\t}\n+\t\tappend_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, &buf);\n+\t}\n+\n+\tres = write_message(buf.buf, buf.len, file, 0);\n \tstrbuf_release(&buf);\n \n-\tif (todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list))\n-\t\tBUG(\"unusable todo list\");\n+\treturn res;\n }\n \n int transform_todo_file(struct repository *r, unsigned flags)\n@@ -4568,9 +4590,8 @@ int transform_todo_file(struct repository *r, unsigned flags)\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n \n-\ttodo_list_transform(r, &todo_list, flags);\n-\n-\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n+\tres = todo_list_write_to_file(r, &todo_list, todo_file,\n+\t\t\t\t      NULL, NULL, -1, flags);\n \ttodo_list_release(&todo_list);\n \n \tif (res)\ndiff --git a/sequencer.h b/sequencer.h\nindex 53ffe7371c..4643fb3b69 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -121,8 +121,9 @@ struct todo_list {\n \n int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \t\t\t\tstruct todo_list *todo_list);\n-void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n-\t\t\t unsigned flags);\n+int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n+\t\t\t    const char *file, const char *shortrevisions,\n+\t\t\t    const char *shortonto, int num, unsigned flags);\n void todo_list_release(struct todo_list *todo_list);\n \n /* Call this to setup defaults before parsing command line options */\n@@ -143,8 +144,10 @@ int sequencer_remove_state(struct replay_opts *opts);\n  * commits should be rebased onto the new base, this flag needs to be passed.\n  */\n #define TODO_LIST_REBASE_COUSINS (1U << 4)\n-int sequencer_make_script(struct repository *repo, FILE *out,\n-\t\t\t  int argc, const char **argv,\n+#define TODO_LIST_APPEND_TODO_HELP (1U << 5)\n+\n+int sequencer_make_script(struct repository *r, FILE *out, int argc,\n+\t\t\t  const char **argv,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(struct repository *r, const char *command);\n-- \n2.20.1\n\n"},{"id":"365956","messageId":"20181229160413.19333-8-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181229160413.19333-1-alban.gruin@gmail.com","subject":"[PATCH v4 07/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-29T16:04:04Z","receivedAt":"2018-12-29T16:05:16Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors sequencer_add_exec_commands() to work on a todo_list to\navoid redundant reads and writes to the disk.\n\nInstead of inserting the `exec' commands between the other commands and\nre-parsing the buffer at the end, they are appended to the buffer once,\nand a new list of items is created.  Items from the old list are copied\nacross and new `exec' items are appended when necessary.  This\neliminates the need to reparse the buffer, but this also means we have\nto use todo_list_write_to_disk() to write the file.\n\ntodo_list_add_exec_commands() and sequencer_add_exec_commands() are\nmodified to take a string list instead of a string -- one item for each\ncommand.  This makes it easier to insert a new command to the todo list\nfor each command to execute.\n\nsequencer_add_exec_commands() still reads the todo list from the disk,\nas it is needed by rebase -p.\n\ncomplete_action() still uses sequencer_add_exec_commands() for now.\nThis will be changed in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |  15 +++--\n sequencer.c                   | 110 +++++++++++++++++++++-------------\n sequencer.h                   |   5 +-\n 3 files changed, 82 insertions(+), 48 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex df19ccaeb9..53056ee713 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -65,7 +65,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \t\t\t\t const char *onto, const char *onto_name,\n \t\t\t\t const char *squash_onto, const char *head_name,\n \t\t\t\t const char *restrict_revision, char *raw_strategies,\n-\t\t\t\t const char *cmd, unsigned autosquash)\n+\t\t\t\t struct string_list *commands, unsigned autosquash)\n {\n \tint ret;\n \tconst char *head_hash = NULL;\n@@ -116,7 +116,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \t\tdiscard_cache();\n \t\tret = complete_action(the_repository, opts, flags,\n \t\t\t\t      shortrevisions, onto_name, onto,\n-\t\t\t\t      head_hash, cmd, autosquash);\n+\t\t\t\t      head_hash, commands, autosquash);\n \t}\n \n \tfree(revisions);\n@@ -139,6 +139,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \tconst char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,\n \t\t*squash_onto = NULL, *upstream = NULL, *head_name = NULL,\n \t\t*switch_to = NULL, *cmd = NULL;\n+\tstruct string_list commands = STRING_LIST_INIT_DUP;\n \tchar *raw_strategies = NULL;\n \tenum {\n \t\tNONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,\n@@ -221,6 +222,12 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\twarning(_(\"--[no-]rebase-cousins has no effect without \"\n \t\t\t  \"--rebase-merges\"));\n \n+\tif (cmd && *cmd) {\n+\t\tstring_list_split(&commands, cmd, '\\n', -1);\n+\t\tif (strlen(commands.items[commands.nr - 1].string) == 0)\n+\t\t\t--commands.nr;\n+\t}\n+\n \tswitch (command) {\n \tcase NONE:\n \t\tif (!onto && !upstream)\n@@ -228,7 +235,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \n \t\tret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,\n \t\t\t\t\t    onto_name, squash_onto, head_name, restrict_revision,\n-\t\t\t\t\t    raw_strategies, cmd, autosquash);\n+\t\t\t\t\t    raw_strategies, &commands, autosquash);\n \t\tbreak;\n \tcase SKIP: {\n \t\tstruct string_list merge_rr = STRING_LIST_INIT_DUP;\n@@ -262,7 +269,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = rearrange_squash(the_repository);\n \t\tbreak;\n \tcase ADD_EXEC:\n-\t\tret = sequencer_add_exec_commands(the_repository, cmd);\n+\t\tret = sequencer_add_exec_commands(the_repository, &commands);\n \t\tbreak;\n \tdefault:\n \t\tBUG(\"invalid command '%d'\", command);\ndiff --git a/sequencer.c b/sequencer.c\nindex 347a1a701f..2df64b3677 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4440,25 +4440,27 @@ int sequencer_make_script(struct repository *r, FILE *out,\n \treturn 0;\n }\n \n-/*\n- * Add commands after pick and (series of) squash/fixup commands\n- * in the todo list.\n- */\n-int sequencer_add_exec_commands(struct repository *r,\n-\t\t\t\tconst char *commands)\n+static void todo_list_add_exec_commands(struct todo_list *todo_list,\n+\t\t\t\t\tstruct string_list *commands)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf *buf = &todo_list.buf;\n-\tsize_t offset = 0, commands_len = strlen(commands);\n-\tint i, insert;\n+\tstruct strbuf *buf = &todo_list->buf;\n+\tsize_t base_offset = buf->len;\n+\tint i, insert, nr = 0, alloc = 0;\n+\tstruct todo_item *items = NULL, *base_items = NULL;\n \n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n+\tbase_items = xcalloc(commands->nr, sizeof(struct todo_item));\n+\tfor (i = 0; i < commands->nr; ++i) {\n+\t\tsize_t command_len = strlen(commands->items[i].string);\n \n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t\tstrbuf_addstr(buf, commands->items[i].string);\n+\t\tstrbuf_addch(buf, '\\n');\n+\n+\t\tbase_items[i].command = TODO_EXEC;\n+\t\tbase_items[i].offset_in_buf = base_offset;\n+\t\tbase_items[i].arg_offset = base_offset + strlen(\"exec \");\n+\t\tbase_items[i].arg_len = command_len - strlen(\"exec \");\n+\n+\t\tbase_offset += command_len + 1;\n \t}\n \n \t/*\n@@ -4467,38 +4469,62 @@ int sequencer_add_exec_commands(struct repository *r,\n \t * those chains if there are any.\n \t */\n \tinsert = -1;\n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tenum todo_command command = todo_list.items[i].command;\n-\n-\t\tif (insert >= 0) {\n-\t\t\t/* skip fixup/squash chains */\n-\t\t\tif (command == TODO_COMMENT)\n-\t\t\t\tcontinue;\n-\t\t\telse if (is_fixup(command)) {\n-\t\t\t\tinsert = i + 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tstrbuf_insert(buf,\n-\t\t\t\t      todo_list.items[insert].offset_in_buf +\n-\t\t\t\t      offset, commands, commands_len);\n-\t\t\toffset += commands_len;\n+\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\tenum todo_command command = todo_list->items[i].command;\n+\t\tif (insert >= 0 && command != TODO_COMMENT && !is_fixup(command)) {\n+\t\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n+\t\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n+\t\t\tnr += commands->nr;\n \t\t\tinsert = -1;\n \t\t}\n \n-\t\tif (command == TODO_PICK || command == TODO_MERGE)\n+\t\tALLOC_GROW(items, nr + 1, alloc);\n+\t\titems[nr++] = todo_list->items[i];\n+\n+\t\tif (command == TODO_PICK || command == TODO_MERGE || is_fixup(command))\n \t\t\tinsert = i + 1;\n \t}\n \n \t/* insert or append final <commands> */\n-\tif (insert >= 0 && insert < todo_list.nr)\n-\t\tstrbuf_insert(buf, todo_list.items[insert].offset_in_buf +\n-\t\t\t      offset, commands, commands_len);\n-\telse if (insert >= 0 || !offset)\n-\t\tstrbuf_add(buf, commands, commands_len);\n+\tif (insert >= 0 || nr == todo_list->nr) {\n+\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n+\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n+\t\tnr += commands->nr;\n+\t}\n+\n+\tfree(base_items);\n+\tFREE_AND_NULL(todo_list->items);\n+\ttodo_list->items = items;\n+\ttodo_list->nr = nr;\n+\ttodo_list->alloc = alloc;\n+}\n \n-\ti = write_message(buf->buf, buf->len, todo_file, 0);\n+/*\n+ * Add commands after pick and (series of) squash/fixup commands\n+ * in the todo list.\n+ */\n+int sequencer_add_exec_commands(struct repository *r,\n+\t\t\t\tstruct string_list *commands)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_add_exec_commands(&todo_list, commands);\n+\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n \ttodo_list_release(&todo_list);\n-\treturn i;\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n }\n \n static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n@@ -4729,7 +4755,7 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-\t\t    const char *onto, const char *orig_head, const char *cmd,\n+\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash)\n {\n \tconst char *shortonto, *todo_file = rebase_path_todo();\n@@ -4748,8 +4774,8 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tif (autosquash && rearrange_squash(r))\n \t\treturn -1;\n \n-\tif (cmd && *cmd)\n-\t\tsequencer_add_exec_commands(r, cmd);\n+\tif (commands->nr)\n+\t\tsequencer_add_exec_commands(r, commands);\n \n \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\ndiff --git a/sequencer.h b/sequencer.h\nindex 07a8418b0d..1d9d66b0cd 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -144,12 +144,13 @@ int sequencer_make_script(struct repository *r, FILE *out, int argc,\n \t\t\t  const char **argv,\n \t\t\t  unsigned flags);\n \n-int sequencer_add_exec_commands(struct repository *r, const char *command);\n+int sequencer_add_exec_commands(struct repository *r,\n+\t\t\t\tstruct string_list *commands);\n int transform_todo_file(struct repository *r, unsigned flags);\n int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-\t\t    const char *onto, const char *orig_head, const char *cmd,\n+\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash);\n int rearrange_squash(struct repository *r);\n \n-- \n2.20.1\n\n"},{"id":"365957","messageId":"20181229160413.19333-9-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181229160413.19333-1-alban.gruin@gmail.com","subject":"[PATCH v4 08/16] sequencer: refactor rearrange_squash() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-29T16:04:05Z","receivedAt":"2018-12-29T16:05:17Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors rearrange_squash() to work on a todo_list to avoid\nredundant reads and writes.  The function is renamed\ntodo_list_rearrange_squash().\n\nThe old version created a new buffer, which was directly written to the\ndisk.  This new version creates a new item list by just copying items\nfrom the old item list, without creating a new buffer.  This eliminates\nthe need to reparse the todo list, but this also means its buffer cannot\nbe directly written to the disk.\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, rearrange_squash_in_todo_file().\n\ncomplete_action() still uses rearrange_squash_in_todo_file() for now.\nThis will be changed in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nSlight rewording of the commit message and changes due to conflicts with\nnd/the-index.\n\n builtin/rebase--interactive.c |  2 +-\n sequencer.c                   | 92 ++++++++++++++++++-----------------\n sequencer.h                   |  2 +-\n 3 files changed, 49 insertions(+), 47 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 53056ee713..4f0eae9239 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -266,7 +266,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = check_todo_list_from_file(the_repository);\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n-\t\tret = rearrange_squash(the_repository);\n+\t\tret = rearrange_squash_in_todo_file(the_repository);\n \t\tbreak;\n \tcase ADD_EXEC:\n \t\tret = sequencer_add_exec_commands(the_repository, &commands);\ndiff --git a/sequencer.c b/sequencer.c\nindex 2df64b3677..931ad9848e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4771,7 +4771,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t    write_message(\"noop\\n\", 5, todo_file, 0))\n \t\treturn -1;\n \n-\tif (autosquash && rearrange_squash(r))\n+\tif (autosquash && rearrange_squash_in_todo_file(r))\n \t\treturn -1;\n \n \tif (commands->nr)\n@@ -4877,21 +4877,13 @@ define_commit_slab(commit_todo_item, struct todo_item *);\n  * message will have to be retrieved from the commit (as the oneline in the\n  * script cannot be trusted) in order to normalize the autosquash arrangement.\n  */\n-int rearrange_squash(struct repository *r)\n+static int todo_list_rearrange_squash(struct todo_list *todo_list)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct hashmap subject2item;\n-\tint res = 0, rearranged = 0, *next, *tail, i;\n+\tint rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;\n \tchar **subjects;\n \tstruct commit_todo_item commit_todo;\n-\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tstruct todo_item *items = NULL;\n \n \tinit_commit_todo_item(&commit_todo);\n \t/*\n@@ -4904,13 +4896,13 @@ int rearrange_squash(struct repository *r)\n \t * be moved to appear after the i'th.\n \t */\n \thashmap_init(&subject2item, (hashmap_cmp_fn) subject2item_cmp,\n-\t\t     NULL, todo_list.nr);\n-\tALLOC_ARRAY(next, todo_list.nr);\n-\tALLOC_ARRAY(tail, todo_list.nr);\n-\tALLOC_ARRAY(subjects, todo_list.nr);\n-\tfor (i = 0; i < todo_list.nr; i++) {\n+\t\t     NULL, todo_list->nr);\n+\tALLOC_ARRAY(next, todo_list->nr);\n+\tALLOC_ARRAY(tail, todo_list->nr);\n+\tALLOC_ARRAY(subjects, todo_list->nr);\n+\tfor (i = 0; i < todo_list->nr; i++) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n-\t\tstruct todo_item *item = todo_list.items + i;\n+\t\tstruct todo_item *item = todo_list->items + i;\n \t\tconst char *commit_buffer, *subject, *p;\n \t\tsize_t subject_len;\n \t\tint i2 = -1;\n@@ -4923,7 +4915,6 @@ int rearrange_squash(struct repository *r)\n \t\t}\n \n \t\tif (is_fixup(item->command)) {\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\tclear_commit_todo_item(&commit_todo);\n \t\t\treturn error(_(\"the script was already rearranged.\"));\n \t\t}\n@@ -4958,7 +4949,7 @@ int rearrange_squash(struct repository *r)\n \t\t\t\t *commit_todo_item_at(&commit_todo, commit2))\n \t\t\t\t/* found by commit name */\n \t\t\t\ti2 = *commit_todo_item_at(&commit_todo, commit2)\n-\t\t\t\t\t- todo_list.items;\n+\t\t\t\t\t- todo_list->items;\n \t\t\telse {\n \t\t\t\t/* copy can be a prefix of the commit subject */\n \t\t\t\tfor (i2 = 0; i2 < i; i2++)\n@@ -4971,7 +4962,7 @@ int rearrange_squash(struct repository *r)\n \t\t}\n \t\tif (i2 >= 0) {\n \t\t\trearranged = 1;\n-\t\t\ttodo_list.items[i].command =\n+\t\t\ttodo_list->items[i].command =\n \t\t\t\tstarts_with(subject, \"fixup!\") ?\n \t\t\t\tTODO_FIXUP : TODO_SQUASH;\n \t\t\tif (next[i2] < 0)\n@@ -4989,10 +4980,8 @@ int rearrange_squash(struct repository *r)\n \t}\n \n \tif (rearranged) {\n-\t\tstruct strbuf buf = STRBUF_INIT;\n-\n-\t\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\t\tenum todo_command command = todo_list.items[i].command;\n+\t\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\t\tenum todo_command command = todo_list->items[i].command;\n \t\t\tint cur = i;\n \n \t\t\t/*\n@@ -5003,37 +4992,50 @@ int rearrange_squash(struct repository *r)\n \t\t\t\tcontinue;\n \n \t\t\twhile (cur >= 0) {\n-\t\t\t\tconst char *bol =\n-\t\t\t\t\tget_item_line(&todo_list, cur);\n-\t\t\t\tconst char *eol =\n-\t\t\t\t\tget_item_line(&todo_list, cur + 1);\n-\n-\t\t\t\t/* replace 'pick', by 'fixup' or 'squash' */\n-\t\t\t\tcommand = todo_list.items[cur].command;\n-\t\t\t\tif (is_fixup(command)) {\n-\t\t\t\t\tstrbuf_addstr(&buf,\n-\t\t\t\t\t\ttodo_command_info[command].str);\n-\t\t\t\t\tbol += strcspn(bol, \" \\t\");\n-\t\t\t\t}\n-\n-\t\t\t\tstrbuf_add(&buf, bol, eol - bol);\n-\n+\t\t\t\tALLOC_GROW(items, nr + 1, alloc);\n+\t\t\t\titems[nr++] = todo_list->items[cur];\n \t\t\t\tcur = next[cur];\n \t\t\t}\n \t\t}\n \n-\t\tres = rewrite_file(todo_file, buf.buf, buf.len);\n-\t\tstrbuf_release(&buf);\n+\t\tFREE_AND_NULL(todo_list->items);\n+\t\ttodo_list->items = items;\n+\t\ttodo_list->nr = nr;\n+\t\ttodo_list->alloc = alloc;\n \t}\n \n \tfree(next);\n \tfree(tail);\n-\tfor (i = 0; i < todo_list.nr; i++)\n+\tfor (i = 0; i < todo_list->nr; i++)\n \t\tfree(subjects[i]);\n \tfree(subjects);\n \thashmap_free(&subject2item, 1);\n-\ttodo_list_release(&todo_list);\n \n \tclear_commit_todo_item(&commit_todo);\n-\treturn res;\n+\n+\treturn 0;\n+}\n+\n+int rearrange_squash_in_todo_file(struct repository *r)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n+\n+\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n+\t\treturn -1;\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn -1;\n+\t}\n+\n+\tres = todo_list_rearrange_squash(&todo_list);\n+\tif (!res)\n+\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n+\n+\ttodo_list_release(&todo_list);\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n }\ndiff --git a/sequencer.h b/sequencer.h\nindex 1d9d66b0cd..e680fe2daa 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -152,7 +152,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash);\n-int rearrange_squash(struct repository *r);\n+int rearrange_squash_in_todo_file(struct repository *r);\n \n extern const char sign_off_header[];\n \n-- \n2.20.1\n\n"},{"id":"365959","messageId":"20181229160413.19333-10-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181229160413.19333-1-alban.gruin@gmail.com","subject":"[PATCH v4 09/16] sequencer: make sequencer_make_script() write its script to a strbuf","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-29T16:04:06Z","receivedAt":"2018-12-29T16:05:20Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This makes sequencer_make_script() write its script to a strbuf (ie. the\nbuffer of a todo_list) instead of a FILE.  This reduce the amount of\nread/write made by rebase interactive.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nChanges due to conflicts with nd/the-index.\n\n builtin/rebase--interactive.c | 13 ++++++-----\n sequencer.c                   | 41 +++++++++++++++--------------------\n sequencer.h                   |  5 ++---\n 3 files changed, 28 insertions(+), 31 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 4f0eae9239..92026739c9 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -71,7 +71,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tconst char *head_hash = NULL;\n \tchar *revisions = NULL, *shortrevisions = NULL;\n \tstruct argv_array make_script_args = ARGV_ARRAY_INIT;\n-\tFILE *todo_list;\n+\tFILE *todo_list_file;\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n \n \tif (prepare_branch_to_be_rebased(opts, switch_to))\n \t\treturn -1;\n@@ -93,8 +94,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (!upstream && squash_onto)\n \t\twrite_file(path_squash_onto(), \"%s\\n\", squash_onto);\n \n-\ttodo_list = fopen(rebase_path_todo(), \"w\");\n-\tif (!todo_list) {\n+\ttodo_list_file = fopen(rebase_path_todo(), \"w\");\n+\tif (!todo_list_file) {\n \t\tfree(revisions);\n \t\tfree(shortrevisions);\n \n@@ -105,10 +106,11 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (restrict_revision)\n \t\targv_array_push(&make_script_args, restrict_revision);\n \n-\tret = sequencer_make_script(the_repository, todo_list,\n+\tret = sequencer_make_script(the_repository, &todo_list.buf,\n \t\t\t\t    make_script_args.argc, make_script_args.argv,\n \t\t\t\t    flags);\n-\tfclose(todo_list);\n+\tfputs(todo_list.buf.buf, todo_list_file);\n+\tfclose(todo_list_file);\n \n \tif (ret)\n \t\terror(_(\"could not generate todo list\"));\n@@ -121,6 +123,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \n \tfree(revisions);\n \tfree(shortrevisions);\n+\ttodo_list_release(&todo_list);\n \targv_array_clear(&make_script_args);\n \n \treturn ret;\ndiff --git a/sequencer.c b/sequencer.c\nindex 931ad9848e..f6527fb418 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4154,7 +4154,7 @@ static const char *label_oid(struct object_id *oid, const char *label,\n }\n \n static int make_script_with_merges(struct pretty_print_context *pp,\n-\t\t\t\t   struct rev_info *revs, FILE *out,\n+\t\t\t\t   struct rev_info *revs, struct strbuf *out,\n \t\t\t\t   unsigned flags)\n {\n \tint keep_empty = flags & TODO_LIST_KEEP_EMPTY;\n@@ -4299,7 +4299,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t * gathering commits not yet shown, reversing the list on the fly,\n \t * then outputting that list (labeling revisions as needed).\n \t */\n-\tfprintf(out, \"%s onto\\n\", cmd_label);\n+\tstrbuf_addf(out, \"%s onto\\n\", cmd_label);\n \tfor (iter = tips; iter; iter = iter->next) {\n \t\tstruct commit_list *list = NULL, *iter2;\n \n@@ -4309,9 +4309,9 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\tentry = oidmap_get(&state.commit2label, &commit->object.oid);\n \n \t\tif (entry)\n-\t\t\tfprintf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n+\t\t\tstrbuf_addf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n \t\telse\n-\t\t\tfprintf(out, \"\\n\");\n+\t\t\tstrbuf_addch(out, '\\n');\n \n \t\twhile (oidset_contains(&interesting, &commit->object.oid) &&\n \t\t       !oidset_contains(&shown, &commit->object.oid)) {\n@@ -4324,8 +4324,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t}\n \n \t\tif (!commit)\n-\t\t\tfprintf(out, \"%s %s\\n\", cmd_reset,\n-\t\t\t\trebase_cousins ? \"onto\" : \"[new root]\");\n+\t\t\tstrbuf_addf(out, \"%s %s\\n\", cmd_reset,\n+\t\t\t\t    rebase_cousins ? \"onto\" : \"[new root]\");\n \t\telse {\n \t\t\tconst char *to = NULL;\n \n@@ -4338,12 +4338,12 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t\t\t\t       &state);\n \n \t\t\tif (!to || !strcmp(to, \"onto\"))\n-\t\t\t\tfprintf(out, \"%s onto\\n\", cmd_reset);\n+\t\t\t\tstrbuf_addf(out, \"%s onto\\n\", cmd_reset);\n \t\t\telse {\n \t\t\t\tstrbuf_reset(&oneline);\n \t\t\t\tpretty_print_commit(pp, commit, &oneline);\n-\t\t\t\tfprintf(out, \"%s %s # %s\\n\",\n-\t\t\t\t\tcmd_reset, to, oneline.buf);\n+\t\t\t\tstrbuf_addf(out, \"%s %s # %s\\n\",\n+\t\t\t\t\t    cmd_reset, to, oneline.buf);\n \t\t\t}\n \t\t}\n \n@@ -4352,11 +4352,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t\tentry = oidmap_get(&commit2todo, oid);\n \t\t\t/* only show if not already upstream */\n \t\t\tif (entry)\n-\t\t\t\tfprintf(out, \"%s\\n\", entry->string);\n+\t\t\t\tstrbuf_addf(out, \"%s\\n\", entry->string);\n \t\t\tentry = oidmap_get(&state.commit2label, oid);\n \t\t\tif (entry)\n-\t\t\t\tfprintf(out, \"%s %s\\n\",\n-\t\t\t\t\tcmd_label, entry->string);\n+\t\t\t\tstrbuf_addf(out, \"%s %s\\n\",\n+\t\t\t\t\t    cmd_label, entry->string);\n \t\t\toidset_insert(&shown, oid);\n \t\t}\n \n@@ -4378,13 +4378,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \treturn 0;\n }\n \n-int sequencer_make_script(struct repository *r, FILE *out,\n-\t\t\t  int argc, const char **argv,\n-\t\t\t  unsigned flags)\n+int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n+\t\t\t  const char **argv, unsigned flags)\n {\n \tchar *format = NULL;\n \tstruct pretty_print_context pp = {0};\n-\tstruct strbuf buf = STRBUF_INIT;\n \tstruct rev_info revs;\n \tstruct commit *commit;\n \tint keep_empty = flags & TODO_LIST_KEEP_EMPTY;\n@@ -4427,16 +4425,13 @@ int sequencer_make_script(struct repository *r, FILE *out,\n \n \t\tif (!is_empty && (commit->object.flags & PATCHSAME))\n \t\t\tcontinue;\n-\t\tstrbuf_reset(&buf);\n \t\tif (!keep_empty && is_empty)\n-\t\t\tstrbuf_addf(&buf, \"%c \", comment_line_char);\n-\t\tstrbuf_addf(&buf, \"%s %s \", insn,\n+\t\t\tstrbuf_addf(out, \"%c \", comment_line_char);\n+\t\tstrbuf_addf(out, \"%s %s \", insn,\n \t\t\t    oid_to_hex(&commit->object.oid));\n-\t\tpretty_print_commit(&pp, commit, &buf);\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t\tfputs(buf.buf, out);\n+\t\tpretty_print_commit(&pp, commit, out);\n+\t\tstrbuf_addch(out, '\\n');\n \t}\n-\tstrbuf_release(&buf);\n \treturn 0;\n }\n \ndiff --git a/sequencer.h b/sequencer.h\nindex e680fe2daa..d75049c15f 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -140,9 +140,8 @@ int sequencer_remove_state(struct replay_opts *opts);\n #define TODO_LIST_REBASE_COUSINS (1U << 4)\n #define TODO_LIST_APPEND_TODO_HELP (1U << 5)\n \n-int sequencer_make_script(struct repository *r, FILE *out, int argc,\n-\t\t\t  const char **argv,\n-\t\t\t  unsigned flags);\n+int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n+\t\t\t  const char **argv, unsigned flags);\n \n int sequencer_add_exec_commands(struct repository *r,\n \t\t\t\tstruct string_list *commands);\n-- \n2.20.1\n\n"},{"id":"365958","messageId":"20181229160413.19333-11-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181229160413.19333-1-alban.gruin@gmail.com","subject":"[PATCH v4 10/16] sequencer: change complete_action() to use the refactored functions","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-29T16:04:07Z","receivedAt":"2018-12-29T16:05:21Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"complete_action() used functions that read the todo-list file, made some\nchanges to it, and wrote it back to the disk.\n\nThe previous commits were dedicated to separate the part that deals with\nthe file from the actual logic of these functions.  Now that this is\ndone, we can call directly the \"logic\" functions to avoid useless file\naccess.\n\nThe parsing of the list has to be done by the caller.  If the buffer of\nthe todo list provided by the caller is empty, a `noop' command is\ndirectly added to the todo list, without touching the buffer.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 20 +++------\n sequencer.c                   | 81 +++++++++++++++--------------------\n sequencer.h                   |  2 +-\n 3 files changed, 42 insertions(+), 61 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 92026739c9..2dbf8fc08b 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -71,7 +71,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tconst char *head_hash = NULL;\n \tchar *revisions = NULL, *shortrevisions = NULL;\n \tstruct argv_array make_script_args = ARGV_ARRAY_INIT;\n-\tFILE *todo_list_file;\n \tstruct todo_list todo_list = TODO_LIST_INIT;\n \n \tif (prepare_branch_to_be_rebased(opts, switch_to))\n@@ -94,14 +93,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (!upstream && squash_onto)\n \t\twrite_file(path_squash_onto(), \"%s\\n\", squash_onto);\n \n-\ttodo_list_file = fopen(rebase_path_todo(), \"w\");\n-\tif (!todo_list_file) {\n-\t\tfree(revisions);\n-\t\tfree(shortrevisions);\n-\n-\t\treturn error_errno(_(\"could not open %s\"), rebase_path_todo());\n-\t}\n-\n \targv_array_pushl(&make_script_args, \"\", revisions, NULL);\n \tif (restrict_revision)\n \t\targv_array_push(&make_script_args, restrict_revision);\n@@ -109,16 +100,17 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tret = sequencer_make_script(the_repository, &todo_list.buf,\n \t\t\t\t    make_script_args.argc, make_script_args.argv,\n \t\t\t\t    flags);\n-\tfputs(todo_list.buf.buf, todo_list_file);\n-\tfclose(todo_list_file);\n \n \tif (ret)\n \t\terror(_(\"could not generate todo list\"));\n \telse {\n \t\tdiscard_cache();\n-\t\tret = complete_action(the_repository, opts, flags,\n-\t\t\t\t      shortrevisions, onto_name, onto,\n-\t\t\t\t      head_hash, commands, autosquash);\n+\t\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n+\t\t\t\t\t\t&todo_list))\n+\t\t\tBUG(\"unusable todo list\");\n+\n+\t\tret = complete_action(the_repository, opts, flags, shortrevisions, onto_name,\n+\t\t\t\t      onto, head_hash, commands, autosquash, &todo_list);\n \t}\n \n \tfree(revisions);\ndiff --git a/sequencer.c b/sequencer.c\nindex f6527fb418..ea5cea81a8 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4748,100 +4748,89 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \treturn 0;\n }\n \n+static int todo_list_rearrange_squash(struct todo_list *todo_list);\n+\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n-\t\t    unsigned autosquash)\n+\t\t    unsigned autosquash, struct todo_list *todo_list)\n {\n \tconst char *shortonto, *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf *buf = &(todo_list.buf);\n+\tstruct todo_list new_todo = TODO_LIST_INIT;\n+\tstruct strbuf *buf = &todo_list->buf;\n \tstruct object_id oid;\n-\tstruct stat st;\n \n \tget_oid(onto, &oid);\n \tshortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);\n \n-\tif (!lstat(todo_file, &st) && st.st_size == 0 &&\n-\t    write_message(\"noop\\n\", 5, todo_file, 0))\n-\t\treturn -1;\n+\tif (buf->len == 0) {\n+\t\tstruct todo_item *item = append_new_todo(todo_list);\n+\t\titem->command = TODO_NOOP;\n+\t\titem->commit = NULL;\n+\t\titem->arg_len = item->arg_offset = item->flags = item->offset_in_buf = 0;\n+\t}\n \n-\tif (autosquash && rearrange_squash_in_todo_file(r))\n+\tif (autosquash && todo_list_rearrange_squash(todo_list))\n \t\treturn -1;\n \n \tif (commands->nr)\n-\t\tsequencer_add_exec_commands(r, commands);\n+\t\ttodo_list_add_exec_commands(todo_list, commands);\n \n-\tif (strbuf_read_file(buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(r, buf->buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tif (count_commands(&todo_list) == 0) {\n+\tif (count_commands(todo_list) == 0) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n \n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\tstrbuf_addch(buf, '\\n');\n-\tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n-\t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n-\t\t\t\t      count_commands(&todo_list)),\n-\t\t\t      shortrevisions, shortonto, count_commands(&todo_list));\n-\tappend_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);\n-\n-\tif (write_message(buf->buf, buf->len, todo_file, 0)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tif (todo_list_write_to_file(r, todo_list, todo_file,\n+\t\t\t\t    shortrevisions, shortonto, -1,\n+\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n \n \tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n \t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n \t\t\t     rebase_path_todo_backup());\n \n-\tif (transform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS))\n-\t\treturn error(_(\"could not transform the todo list\"));\n-\n-\tstrbuf_reset(buf);\n-\n-\tif (launch_sequence_editor(todo_file, buf, NULL)) {\n+\tif (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n \n \t\treturn -1;\n \t}\n \n-\tstrbuf_stripspace(buf, 1);\n-\tif (buf->len == 0) {\n+\tstrbuf_stripspace(&new_todo.buf, 1);\n+\tif (new_todo.buf.len == 0) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n+\t\ttodo_list_release(&new_todo);\n \n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\n-\tif (check_todo_list_from_file(r)) {\n+\tif (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) ||\n+\t    todo_list_check(todo_list, &new_todo)) {\n+\t\tfprintf(stderr, _(edit_todo_list_advice));\n \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n+\t\ttodo_list_release(&new_todo);\n+\n \t\treturn -1;\n \t}\n \n-\tif (transform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS)))\n-\t\treturn error(_(\"could not transform the todo list\"));\n+\tif (todo_list_write_to_file(r, &new_todo, todo_file, NULL, NULL, -1,\n+\t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS))) {\n+\t\ttodo_list_release(&new_todo);\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_release(&new_todo);\n \n \tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\n \t\treturn error(_(\"could not skip unnecessary pick commands\"));\n \n \tif (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))\n \t\treturn -1;\n-;\n+\n \tif (require_clean_work_tree(r, \"rebase\", \"\", 1, 1))\n \t\treturn -1;\n \ndiff --git a/sequencer.h b/sequencer.h\nindex d75049c15f..dcbd682e63 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -150,7 +150,7 @@ int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n-\t\t    unsigned autosquash);\n+\t\t    unsigned autosquash, struct todo_list *todo_list);\n int rearrange_squash_in_todo_file(struct repository *r);\n \n extern const char sign_off_header[];\n-- \n2.20.1\n\n"},{"id":"365960","messageId":"20181229160413.19333-13-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181229160413.19333-1-alban.gruin@gmail.com","subject":"[PATCH v4 12/16] rebase-interactive: use todo_list_write_to_file() in edit_todo_list()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-29T16:04:09Z","receivedAt":"2018-12-29T16:05:22Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Just like complete_action(), edit_todo_list() used a\nfunction (transform_todo_file()) that read the todo list from the disk\nand wrote it back, resulting in useless disk accesses.\n\nThis changes edit_todo_list() to call directly todo_list_write_to_file()\ninstead.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nChanges due to conflicts with nd/the-index.\n\n rebase-interactive.c | 38 ++++++++++++++++----------------------\n 1 file changed, 16 insertions(+), 22 deletions(-)\n\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 6157247e1f..994f0f9753 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -79,39 +79,33 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \n int edit_todo_list(struct repository *r, unsigned flags)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n \tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n \n-\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n-\tstrbuf_stripspace(&buf, 1);\n-\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n-\t\tstrbuf_release(&buf);\n+\tstrbuf_stripspace(&todo_list.buf, 1);\n+\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n+\tif (todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n+\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP)) {\n+\t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n \n-\tstrbuf_release(&buf);\n-\n-\ttransform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS);\n-\n-\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tappend_todo_help(1, 0, &buf);\n-\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n-\t\tstrbuf_release(&buf);\n+\tstrbuf_reset(&todo_list.buf);\n+\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n+\t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n \n-\tstrbuf_release(&buf);\n+\tif (!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list))\n+\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n+\t\t\t\t\t      flags & ~(TODO_LIST_SHORTEN_IDS));\n \n-\tif (launch_sequence_editor(todo_file, NULL, NULL))\n-\t\treturn -1;\n-\n-\ttransform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS));\n-\n-\treturn 0;\n+\ttodo_list_release(&todo_list);\n+\treturn res;\n }\n \n define_commit_slab(commit_seen, unsigned char);\n-- \n2.20.1\n\n"},{"id":"365961","messageId":"20181229160413.19333-12-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181229160413.19333-1-alban.gruin@gmail.com","subject":"[PATCH v4 11/16] sequencer: refactor skip_unnecessary_picks() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-29T16:04:08Z","receivedAt":"2018-12-29T16:05:24Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors skip_unnecessary_picks() to work on a todo_list.  As this\nfunction is only called by complete_action() (and thus is not used by\nrebase -p), the file-handling logic is completely dropped here.\n\nInstead of truncating the todo list’s buffer, the items are moved to\nthe beginning of the list, eliminating the need to reparse the list.\nThis also means its buffer cannot be directly written to the disk.\n\nrewrite_file() is then removed, as it is now unused.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 78 ++++++++++++-----------------------------------------\n 1 file changed, 17 insertions(+), 61 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex ea5cea81a8..dfdba5cec0 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4655,52 +4655,22 @@ int check_todo_list_from_file(struct repository *r)\n \treturn res;\n }\n \n-static int rewrite_file(const char *path, const char *buf, size_t len)\n-{\n-\tint rc = 0;\n-\tint fd = open(path, O_WRONLY | O_TRUNC);\n-\tif (fd < 0)\n-\t\treturn error_errno(_(\"could not open '%s' for writing\"), path);\n-\tif (write_in_full(fd, buf, len) < 0)\n-\t\trc = error_errno(_(\"could not write to '%s'\"), path);\n-\tif (close(fd) && !rc)\n-\t\trc = error_errno(_(\"could not close '%s'\"), path);\n-\treturn rc;\n-}\n-\n /* skip picking commits whose parents are unchanged */\n-static int skip_unnecessary_picks(struct repository *r, struct object_id *output_oid)\n+static int skip_unnecessary_picks(struct repository *r,\n+\t\t\t\t  struct todo_list *todo_list,\n+\t\t\t\t  struct object_id *output_oid)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct object_id *parent_oid;\n-\tint fd, i;\n-\n-\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n-\t\treturn error(_(\"could not read 'onto'\"));\n-\tif (get_oid(buf.buf, output_oid)) {\n-\t\tstrbuf_release(&buf);\n-\t\treturn error(_(\"need a HEAD to fixup\"));\n-\t}\n-\tstrbuf_release(&buf);\n-\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tint i;\n \n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tstruct todo_item *item = todo_list.items + i;\n+\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\tstruct todo_item *item = todo_list->items + i;\n \n \t\tif (item->command >= TODO_NOOP)\n \t\t\tcontinue;\n \t\tif (item->command != TODO_PICK)\n \t\t\tbreak;\n \t\tif (parse_commit(item->commit)) {\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\treturn error(_(\"could not parse commit '%s'\"),\n \t\t\t\toid_to_hex(&item->commit->object.oid));\n \t\t}\n@@ -4714,37 +4684,21 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \t\toidcpy(output_oid, &item->commit->object.oid);\n \t}\n \tif (i > 0) {\n-\t\tint offset = get_item_line_offset(&todo_list, i);\n \t\tconst char *done_path = rebase_path_done();\n \n-\t\tfd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);\n-\t\tif (fd < 0) {\n-\t\t\terror_errno(_(\"could not open '%s' for writing\"),\n-\t\t\t\t    done_path);\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\treturn -1;\n-\t\t}\n-\t\tif (write_in_full(fd, todo_list.buf.buf, offset) < 0) {\n+\t\tif (todo_list_write_to_file(r, todo_list, done_path, NULL, NULL, i, 0)) {\n \t\t\terror_errno(_(\"could not write to '%s'\"), done_path);\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\tclose(fd);\n \t\t\treturn -1;\n \t\t}\n-\t\tclose(fd);\n \n-\t\tif (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,\n-\t\t\t\t todo_list.buf.len - offset) < 0) {\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\treturn -1;\n-\t\t}\n+\t\tMOVE_ARRAY(todo_list->items, todo_list->items + i, todo_list->nr - i);\n+\t\ttodo_list->nr -= i;\n+\t\ttodo_list->current = 0;\n \n-\t\ttodo_list.current = i;\n-\t\tif (is_fixup(peek_command(&todo_list, 0)))\n-\t\t\trecord_in_rewritten(output_oid, peek_command(&todo_list, 0));\n+\t\tif (is_fixup(peek_command(todo_list, 0)))\n+\t\t\trecord_in_rewritten(output_oid, peek_command(todo_list, 0));\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\n \treturn 0;\n }\n \n@@ -4817,6 +4771,11 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn -1;\n \t}\n \n+\tif (opts->allow_ff && skip_unnecessary_picks(r, &new_todo, &oid)) {\n+\t\ttodo_list_release(&new_todo);\n+\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n+\t}\n+\n \tif (todo_list_write_to_file(r, &new_todo, todo_file, NULL, NULL, -1,\n \t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS))) {\n \t\ttodo_list_release(&new_todo);\n@@ -4825,9 +4784,6 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \n \ttodo_list_release(&new_todo);\n \n-\tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\n-\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n-\n \tif (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))\n \t\treturn -1;\n \n-- \n2.20.1\n\n"},{"id":"365963","messageId":"20181229160413.19333-7-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181229160413.19333-1-alban.gruin@gmail.com","subject":"[PATCH v4 06/16] sequencer: refactor check_todo_list() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-29T16:04:03Z","receivedAt":"2018-12-29T16:05:25Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors check_todo_list() to work on a todo_list to avoid\nredundant reads and writes to the disk.  The function is renamed\ntodo_list_check().  The parsing of the two todo lists is left to the\ncaller.\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, check_todo_list_from_file().  It reads the file\nfrom the disk, parses it, pass the todo_list to todo_list_check(), and\nwrites it back to the disk.\n\nAs get_missing_commit_check_level() and the enum\nmissing_commit_check_level are no longer needed inside of sequencer.c,\nthey are moved to rebase-interactive.c, and made static again.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |   2 +-\n rebase-interactive.c          |  91 ++++++++++++++++++++++++-\n rebase-interactive.h          |   1 +\n sequencer.c                   | 121 +++++++---------------------------\n sequencer.h                   |   9 +--\n 5 files changed, 116 insertions(+), 108 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 0898eb4c59..df19ccaeb9 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -256,7 +256,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = transform_todo_file(the_repository, flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n-\t\tret = check_todo_list(the_repository);\n+\t\tret = check_todo_list_from_file(the_repository);\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n \t\tret = rearrange_squash(the_repository);\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 842fa07e7e..6157247e1f 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -1,8 +1,32 @@\n #include \"cache.h\"\n #include \"commit.h\"\n-#include \"rebase-interactive.h\"\n #include \"sequencer.h\"\n+#include \"rebase-interactive.h\"\n #include \"strbuf.h\"\n+#include \"commit-slab.h\"\n+#include \"config.h\"\n+\n+enum missing_commit_check_level {\n+\tMISSING_COMMIT_CHECK_IGNORE = 0,\n+\tMISSING_COMMIT_CHECK_WARN,\n+\tMISSING_COMMIT_CHECK_ERROR\n+};\n+\n+static enum missing_commit_check_level get_missing_commit_check_level(void)\n+{\n+\tconst char *value;\n+\n+\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n+\t\t\t!strcasecmp(\"ignore\", value))\n+\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n+\tif (!strcasecmp(\"warn\", value))\n+\t\treturn MISSING_COMMIT_CHECK_WARN;\n+\tif (!strcasecmp(\"error\", value))\n+\t\treturn MISSING_COMMIT_CHECK_ERROR;\n+\twarning(_(\"unrecognized setting %s for option \"\n+\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n+\treturn MISSING_COMMIT_CHECK_IGNORE;\n+}\n \n void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \t\t      struct strbuf *buf)\n@@ -89,3 +113,68 @@ int edit_todo_list(struct repository *r, unsigned flags)\n \n \treturn 0;\n }\n+\n+define_commit_slab(commit_seen, unsigned char);\n+/*\n+ * Check if the user dropped some commits by mistake\n+ * Behaviour determined by rebase.missingCommitsCheck.\n+ * Check if there is an unrecognized command or a\n+ * bad SHA-1 in a command.\n+ */\n+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo)\n+{\n+\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n+\tstruct strbuf missing = STRBUF_INIT;\n+\tint res = 0, i;\n+\tstruct commit_seen commit_seen;\n+\n+\tinit_commit_seen(&commit_seen);\n+\n+\tif (check_level == MISSING_COMMIT_CHECK_IGNORE)\n+\t\tgoto leave_check;\n+\n+\t/* Mark the commits in git-rebase-todo as seen */\n+\tfor (i = 0; i < new_todo->nr; i++) {\n+\t\tstruct commit *commit = new_todo->items[i].commit;\n+\t\tif (commit)\n+\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t}\n+\n+\t/* Find commits in git-rebase-todo.backup yet unseen */\n+\tfor (i = old_todo->nr - 1; i >= 0; i--) {\n+\t\tstruct todo_item *item = old_todo->items + i;\n+\t\tstruct commit *commit = item->commit;\n+\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n+\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n+\t\t\t\t    find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV),\n+\t\t\t\t    item->arg_len,\n+\t\t\t\t    old_todo->buf.buf + item->arg_offset);\n+\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t\t}\n+\t}\n+\n+\t/* Warn about missing commits */\n+\tif (!missing.len)\n+\t\tgoto leave_check;\n+\n+\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n+\t\tres = 1;\n+\n+\tfprintf(stderr,\n+\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n+\t\t\"Dropped commits (newer to older):\\n\"));\n+\n+\t/* Make the list user-friendly and display */\n+\tfputs(missing.buf, stderr);\n+\tstrbuf_release(&missing);\n+\n+\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n+\t\t\"explicitly remove a commit.\\n\\n\"\n+\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n+\t\t\"the level of warnings.\\n\"\n+\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n+\n+leave_check:\n+\tclear_commit_seen(&commit_seen);\n+\treturn res;\n+}\ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 17b6c9f6d0..dd0d717bc1 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -7,5 +7,6 @@ struct repository;\n void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \t\t      struct strbuf *buf);\n int edit_todo_list(struct repository *r, unsigned flags);\n+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n \n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex b932ca34f2..347a1a701f 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4599,112 +4599,37 @@ int transform_todo_file(struct repository *r, unsigned flags)\n \treturn 0;\n }\n \n-enum missing_commit_check_level get_missing_commit_check_level(void)\n-{\n-\tconst char *value;\n-\n-\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n-\t\t\t!strcasecmp(\"ignore\", value))\n-\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n-\tif (!strcasecmp(\"warn\", value))\n-\t\treturn MISSING_COMMIT_CHECK_WARN;\n-\tif (!strcasecmp(\"error\", value))\n-\t\treturn MISSING_COMMIT_CHECK_ERROR;\n-\twarning(_(\"unrecognized setting %s for option \"\n-\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n-\treturn MISSING_COMMIT_CHECK_IGNORE;\n-}\n+static const char edit_todo_list_advice[] =\n+N_(\"You can fix this with 'git rebase --edit-todo' \"\n+\"and then run 'git rebase --continue'.\\n\"\n+\"Or you can abort the rebase with 'git rebase\"\n+\" --abort'.\\n\");\n \n-define_commit_slab(commit_seen, unsigned char);\n-/*\n- * Check if the user dropped some commits by mistake\n- * Behaviour determined by rebase.missingCommitsCheck.\n- * Check if there is an unrecognized command or a\n- * bad SHA-1 in a command.\n- */\n-int check_todo_list(struct repository *r)\n+int check_todo_list_from_file(struct repository *r)\n {\n-\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n-\tstruct strbuf todo_file = STRBUF_INIT;\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf missing = STRBUF_INIT;\n-\tint advise_to_edit_todo = 0, res = 0, i;\n-\tstruct commit_seen commit_seen;\n-\n-\tinit_commit_seen(&commit_seen);\n+\tstruct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;\n+\tint res = 0;\n \n-\tstrbuf_addstr(&todo_file, rebase_path_todo());\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n+\tif (strbuf_read_file_or_whine(&new_todo.buf, rebase_path_todo()) < 0) {\n \t\tres = -1;\n-\t\tgoto leave_check;\n-\t}\n-\tadvise_to_edit_todo = res =\n-\t\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n-\n-\tif (res || check_level == MISSING_COMMIT_CHECK_IGNORE)\n-\t\tgoto leave_check;\n-\n-\t/* Mark the commits in git-rebase-todo as seen */\n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tstruct commit *commit = todo_list.items[i].commit;\n-\t\tif (commit)\n-\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t\tgoto out;\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\tstrbuf_addstr(&todo_file, \".backup\");\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n+\tif (strbuf_read_file_or_whine(&old_todo.buf, rebase_path_todo_backup()) < 0) {\n \t\tres = -1;\n-\t\tgoto leave_check;\n-\t}\n-\tstrbuf_release(&todo_file);\n-\tres = !!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n-\n-\t/* Find commits in git-rebase-todo.backup yet unseen */\n-\tfor (i = todo_list.nr - 1; i >= 0; i--) {\n-\t\tstruct todo_item *item = todo_list.items + i;\n-\t\tstruct commit *commit = item->commit;\n-\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n-\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n-\t\t\t\t    short_commit_name(commit),\n-\t\t\t\t    item->arg_len,\n-\t\t\t\t    todo_list.buf.buf + item->arg_offset);\n-\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n-\t\t}\n+\t\tgoto out;\n \t}\n \n-\t/* Warn about missing commits */\n-\tif (!missing.len)\n-\t\tgoto leave_check;\n-\n-\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n-\t\tadvise_to_edit_todo = res = 1;\n-\n-\tfprintf(stderr,\n-\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n-\t\t\"Dropped commits (newer to older):\\n\"));\n-\n-\t/* Make the list user-friendly and display */\n-\tfputs(missing.buf, stderr);\n-\tstrbuf_release(&missing);\n-\n-\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n-\t\t\"explicitly remove a commit.\\n\\n\"\n-\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n-\t\t\"the level of warnings.\\n\"\n-\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n-\n-leave_check:\n-\tclear_commit_seen(&commit_seen);\n-\tstrbuf_release(&todo_file);\n-\ttodo_list_release(&todo_list);\n-\n-\tif (advise_to_edit_todo)\n-\t\tfprintf(stderr,\n-\t\t\t_(\"You can fix this with 'git rebase --edit-todo' \"\n-\t\t\t  \"and then run 'git rebase --continue'.\\n\"\n-\t\t\t  \"Or you can abort the rebase with 'git rebase\"\n-\t\t\t  \" --abort'.\\n\"));\n+\tres = todo_list_parse_insn_buffer(r, old_todo.buf.buf, &old_todo);\n+\tif (!res)\n+\t\tres = todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo);\n+\tif (!res)\n+\t\tres = todo_list_check(&old_todo, &new_todo);\n+\tif (res)\n+\t\tfprintf(stderr, _(edit_todo_list_advice));\n+out:\n+\ttodo_list_release(&old_todo);\n+\ttodo_list_release(&new_todo);\n \n \treturn res;\n }\n@@ -4882,7 +4807,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \n \ttodo_list_release(&todo_list);\n \n-\tif (check_todo_list(r)) {\n+\tif (check_todo_list_from_file(r)) {\n \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n \t\treturn -1;\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex 4643fb3b69..07a8418b0d 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -64,12 +64,6 @@ struct replay_opts {\n };\n #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }\n \n-enum missing_commit_check_level {\n-\tMISSING_COMMIT_CHECK_IGNORE = 0,\n-\tMISSING_COMMIT_CHECK_WARN,\n-\tMISSING_COMMIT_CHECK_ERROR\n-};\n-\n int write_message(const void *buf, size_t len, const char *filename,\n \t\t  int append_eol);\n \n@@ -152,8 +146,7 @@ int sequencer_make_script(struct repository *r, FILE *out, int argc,\n \n int sequencer_add_exec_commands(struct repository *r, const char *command);\n int transform_todo_file(struct repository *r, unsigned flags);\n-enum missing_commit_check_level get_missing_commit_check_level(void);\n-int check_todo_list(struct repository *r);\n+int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, const char *cmd,\n-- \n2.20.1\n\n"},{"id":"365962","messageId":"20181229160413.19333-14-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181229160413.19333-1-alban.gruin@gmail.com","subject":"[PATCH v4 13/16] rebase-interactive: append_todo_help() changes","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-29T16:04:10Z","receivedAt":"2018-12-29T16:05:26Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This moves the writing of the comment \"Rebase $shortrevisions onto\n$shortonto ($command_count commands)\" from todo_list_write_to_file() to\nappend_todo_help().\n\nshortrevisions, shortonto, and command_count are passed as parameters to\nappend_todo_help().\n\nDuring the initial edit of the todo list, shortrevisions and shortonto\nare not NULL.  Therefore, if shortrevisions or shortonto is NULL, then\nedit_todo would be true, otherwise it would be false.  Thus, edit_todo\nis removed from the parameters of append_todo_help().\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nSlight rewording of the message and changes due to conflicts with\nnd/the-index.\n\n rebase-interactive.c | 12 +++++++++++-\n rebase-interactive.h |  3 ++-\n sequencer.c          | 17 ++++-------------\n 3 files changed, 17 insertions(+), 15 deletions(-)\n\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 994f0f9753..32f95002df 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -28,7 +28,8 @@ static enum missing_commit_check_level get_missing_commit_check_level(void)\n \treturn MISSING_COMMIT_CHECK_IGNORE;\n }\n \n-void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n+void append_todo_help(unsigned keep_empty, int command_count,\n+\t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf)\n {\n \tconst char *msg = _(\"\\nCommands:\\n\"\n@@ -48,6 +49,15 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \".       specified). Use -c <commit> to reword the commit message.\\n\"\n \"\\n\"\n \"These lines can be re-ordered; they are executed from top to bottom.\\n\");\n+\tunsigned edit_todo = !(shortrevisions && shortonto);\n+\n+\tif (!edit_todo) {\n+\t\tstrbuf_addch(buf, '\\n');\n+\t\tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n+\t\t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n+\t\t\t\t\t      command_count),\n+\t\t\t\t      shortrevisions, shortonto, command_count);\n+\t}\n \n \tstrbuf_add_commented_lines(buf, msg, strlen(msg));\n \ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex dd0d717bc1..2f6675eabd 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -4,7 +4,8 @@\n struct strbuf;\n struct repository;\n \n-void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n+void append_todo_help(unsigned keep_empty, int command_count,\n+\t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf);\n int edit_todo_list(struct repository *r, unsigned flags);\n int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\ndiff --git a/sequencer.c b/sequencer.c\nindex dfdba5cec0..dfc705291d 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4574,22 +4574,13 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n \t\t\t    const char *file, const char *shortrevisions,\n \t\t\t    const char *shortonto, int num, unsigned flags)\n {\n-\tint edit_todo = !(shortrevisions && shortonto), res;\n+\tint res;\n \tstruct strbuf buf = STRBUF_INIT;\n \n \ttodo_list_to_strbuf(r, todo_list, &buf, num, flags);\n-\n-\tif (flags & TODO_LIST_APPEND_TODO_HELP) {\n-\t\tint command_count = count_commands(todo_list);\n-\t\tif (!edit_todo) {\n-\t\t\tstrbuf_addch(&buf, '\\n');\n-\t\t\tstrbuf_commented_addf(&buf, Q_(\"Rebase %s onto %s (%d command)\",\n-\t\t\t\t\t\t       \"Rebase %s onto %s (%d commands)\",\n-\t\t\t\t\t\t       command_count),\n-\t\t\t\t\t      shortrevisions, shortonto, command_count);\n-\t\t}\n-\t\tappend_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, &buf);\n-\t}\n+\tif (flags & TODO_LIST_APPEND_TODO_HELP)\n+\t\tappend_todo_help(flags & TODO_LIST_KEEP_EMPTY, count_commands(todo_list),\n+\t\t\t\t shortrevisions, shortonto, &buf);\n \n \tres = write_message(buf.buf, buf.len, file, 0);\n \tstrbuf_release(&buf);\n-- \n2.20.1\n\n"},{"id":"365964","messageId":"20181229160413.19333-17-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181229160413.19333-1-alban.gruin@gmail.com","subject":"[PATCH v4 16/16] rebase--interactive: move transform_todo_file() to rebase--interactive.c","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-29T16:04:13Z","receivedAt":"2018-12-29T16:05:30Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"As transform_todo_file() is only needed inside of rebase--interactive.c,\nit is moved there from sequencer.c.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 26 +++++++++++++++++++++++++-\n sequencer.c                   | 23 -----------------------\n sequencer.h                   |  1 -\n 3 files changed, 25 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 645ac587f7..7f1e88a087 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -35,6 +35,30 @@ static int edit_todo_file(unsigned flags)\n \treturn 0;\n }\n \n+static int transform_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n+\t\t\t\t\t&todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\tres = todo_list_write_to_file(the_repository, &todo_list, todo_file,\n+\t\t\t\t      NULL, NULL, -1, flags);\n+\ttodo_list_release(&todo_list);\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n+}\n+\n static int get_revision_ranges(const char *upstream, const char *onto,\n \t\t\t       const char **head_hash,\n \t\t\t       char **revisions, char **shortrevisions)\n@@ -277,7 +301,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t}\n \tcase SHORTEN_OIDS:\n \tcase EXPAND_OIDS:\n-\t\tret = transform_todo_file(the_repository, flags);\n+\t\tret = transform_todo_file(flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n \t\tret = check_todo_list_from_file(the_repository);\ndiff --git a/sequencer.c b/sequencer.c\nindex 127bb0b68e..6566247a7c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4587,29 +4587,6 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n \treturn res;\n }\n \n-int transform_todo_file(struct repository *r, unsigned flags)\n-{\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res;\n-\n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tres = todo_list_write_to_file(r, &todo_list, todo_file,\n-\t\t\t\t      NULL, NULL, -1, flags);\n-\ttodo_list_release(&todo_list);\n-\n-\tif (res)\n-\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n-\treturn 0;\n-}\n-\n static const char edit_todo_list_advice[] =\n N_(\"You can fix this with 'git rebase --edit-todo' \"\n \"and then run 'git rebase --continue'.\\n\"\ndiff --git a/sequencer.h b/sequencer.h\nindex 7dd85bb399..bb3ca5783b 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -146,7 +146,6 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \n int sequencer_add_exec_commands(struct repository *r,\n \t\t\t\tstruct string_list *commands);\n-int transform_todo_file(struct repository *r, unsigned flags);\n int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-- \n2.20.1\n\n"},{"id":"365966","messageId":"20181229160413.19333-15-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181229160413.19333-1-alban.gruin@gmail.com","subject":"[PATCH v4 14/16] rebase-interactive: rewrite edit_todo_list() to handle the initial edit","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-29T16:04:11Z","receivedAt":"2018-12-29T16:05:30Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"edit_todo_list() is changed to work on a todo_list, and to handle the\ninitial edition of the todo list (ie. making a backup of the todo\nlist).\n\nIt does not check for dropped commits yet, as todo_list_check() does not\ntake the commits that have already been processed by the rebase (ie. the\ntodo list is edited in the middle of a rebase session).\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nChanges due to conflicts with nd/the-index.\n\n builtin/rebase--interactive.c | 24 +++++++++++++++++-\n rebase-interactive.c          | 48 ++++++++++++++++++-----------------\n rebase-interactive.h          |  4 ++-\n sequencer.c                   |  3 +--\n sequencer.h                   |  1 +\n 5 files changed, 53 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 2dbf8fc08b..645ac587f7 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -13,6 +13,28 @@ static GIT_PATH_FUNC(path_state_dir, \"rebase-merge/\")\n static GIT_PATH_FUNC(path_squash_onto, \"rebase-merge/squash-onto\")\n static GIT_PATH_FUNC(path_interactive, \"rebase-merge/interactive\")\n \n+static int edit_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT,\n+\t\tnew_todo = TODO_LIST_INIT;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tstrbuf_stripspace(&todo_list.buf, 1);\n+\tif (!edit_todo_list(the_repository, &todo_list,\n+\t\t\t    &new_todo, NULL, NULL, flags) &&\n+\t    todo_list_write_to_file(the_repository, &new_todo, todo_file, NULL, NULL,\n+\t\t\t\t    -1, flags & ~(TODO_LIST_SHORTEN_IDS)) < 0)\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n+\n+\ttodo_list_release(&todo_list);\n+\ttodo_list_release(&new_todo);\n+\n+\treturn 0;\n+}\n+\n static int get_revision_ranges(const char *upstream, const char *onto,\n \t\t\t       const char **head_hash,\n \t\t\t       char **revisions, char **shortrevisions)\n@@ -242,7 +264,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tbreak;\n \t}\n \tcase EDIT_TODO:\n-\t\tret = edit_todo_list(the_repository, flags);\n+\t\tret = edit_todo_file(flags);\n \t\tbreak;\n \tcase SHOW_CURRENT_PATCH: {\n \t\tstruct child_process cmd = CHILD_PROCESS_INIT;\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 32f95002df..7542e70a55 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -87,35 +87,37 @@ void append_todo_help(unsigned keep_empty, int command_count,\n \t}\n }\n \n-int edit_todo_list(struct repository *r, unsigned flags)\n+int edit_todo_list(struct repository *r, struct todo_list *todo_list,\n+\t\t   struct todo_list *new_todo, const char *shortrevisions,\n+\t\t   const char *shortonto, unsigned flags)\n {\n \tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res = 0;\n-\n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tstrbuf_stripspace(&todo_list.buf, 1);\n-\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n-\tif (todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n-\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n+\tunsigned initial = shortrevisions && shortonto;\n+\n+\tif (initial) {\n+\t\ttodo_list_write_to_file(r, todo_list, todo_file, shortrevisions, shortonto,\n+\t\t\t\t\t-1, flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP);\n+\n+\t\tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n+\t\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n+\t\t\t\t     rebase_path_todo_backup());\n+\t} else {\n+\t\ttodo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n+\t\ttodo_list_write_to_file(r, todo_list, todo_file, NULL, NULL, -1,\n+\t\t\t\t\tflags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP);\n \t}\n \n-\tstrbuf_reset(&todo_list.buf);\n-\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tif (launch_sequence_editor(todo_file, &new_todo->buf, NULL))\n+\t\treturn -2;\n \n-\tif (!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list))\n-\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n-\t\t\t\t\t      flags & ~(TODO_LIST_SHORTEN_IDS));\n+\tstrbuf_stripspace(&new_todo->buf, 1);\n+\tif (initial && new_todo->buf.len == 0)\n+\t\treturn -3;\n \n-\ttodo_list_release(&todo_list);\n-\treturn res;\n+\tif (!initial)\n+\t\ttodo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo);\n+\n+\treturn 0;\n }\n \n define_commit_slab(commit_seen, unsigned char);\ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 2f6675eabd..42cc3f865d 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -7,7 +7,9 @@ struct repository;\n void append_todo_help(unsigned keep_empty, int command_count,\n \t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf);\n-int edit_todo_list(struct repository *r, unsigned flags);\n+int edit_todo_list(struct repository *r, struct todo_list *todo_list,\n+\t\t   struct todo_list *new_todo, const char *shortrevisions,\n+\t\t   const char *shortonto, unsigned flags);\n int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n \n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex dfc705291d..0500a32e80 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -55,8 +55,7 @@ static GIT_PATH_FUNC(rebase_path, \"rebase-merge\")\n  * file and written to the tail of 'done'.\n  */\n GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n-static GIT_PATH_FUNC(rebase_path_todo_backup,\n-\t\t     \"rebase-merge/git-rebase-todo.backup\")\n+GIT_PATH_FUNC(rebase_path_todo_backup, \"rebase-merge/git-rebase-todo.backup\")\n \n /*\n  * The rebase command lines that have already been processed. A line\ndiff --git a/sequencer.h b/sequencer.h\nindex dcbd682e63..7dd85bb399 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -10,6 +10,7 @@ struct repository;\n const char *git_path_commit_editmsg(void);\n const char *git_path_seq_dir(void);\n const char *rebase_path_todo(void);\n+const char *rebase_path_todo_backup(void);\n \n #define APPEND_SIGNOFF_DEDUP (1u << 0)\n \n-- \n2.20.1\n\n"},{"id":"365965","messageId":"20181229160413.19333-16-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181229160413.19333-1-alban.gruin@gmail.com","subject":"[PATCH v4 15/16] sequencer: use edit_todo_list() in complete_action()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2018-12-29T16:04:12Z","receivedAt":"2018-12-29T16:05:31Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This changes complete_action() to use edit_todo_list(), now that it can\nhandle the initial edit of the todo list.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nChanges due to conflicts with nd/the-index.\n\n sequencer.c | 21 +++++++--------------\n 1 file changed, 7 insertions(+), 14 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 0500a32e80..127bb0b68e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4703,6 +4703,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tstruct todo_list new_todo = TODO_LIST_INIT;\n \tstruct strbuf *buf = &todo_list->buf;\n \tstruct object_id oid;\n+\tint res;\n \n \tget_oid(onto, &oid);\n \tshortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);\n@@ -4727,24 +4728,16 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\tif (todo_list_write_to_file(r, todo_list, todo_file,\n-\t\t\t\t    shortrevisions, shortonto, -1,\n-\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))\n-\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n-\n-\tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n-\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n-\t\t\t     rebase_path_todo_backup());\n-\n-\tif (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {\n+\tres = edit_todo_list(r, todo_list, &new_todo, shortrevisions,\n+\t\t\t     shortonto, flags);\n+\tif (res == -1)\n+\t\treturn -1;\n+\telse if (res == -2) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n \n \t\treturn -1;\n-\t}\n-\n-\tstrbuf_stripspace(&new_todo.buf, 1);\n-\tif (new_todo.buf.len == 0) {\n+\t} else if (res == -3) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n \t\ttodo_list_release(&new_todo);\n-- \n2.20.1\n\n"},{"id":"367236","messageId":"6bc7f154-a18a-3311-cbd0-c2a578931c4b@talktalk.net","threadId":"49504","inReplyTo":"20181229160413.19333-4-alban.gruin@gmail.com","subject":"Re: [PATCH v4 03/16] sequencer: remove the 'arg' field from todo_item","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-01-21T14:59:56Z","receivedAt":"2019-01-21T15:00:04Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nI think this is a good change, I just wonder if it would be better to \nhave an accessor function to get the arg pointer from a todo list item \nsee below\n\nOn 29/12/2018 16:04, Alban Gruin wrote:\n> The 'arg' field of todo_item used to store the address of the first byte\n> of the parameter of a command in a todo list.  It was associated with\n> the length of the parameter (the 'arg_len' field).\n> \n> This replaces the 'arg' field by 'arg_offset'.  This new field does not\n> store the address of the parameter, but the position of the first\n> character of the parameter in the buffer.\n> \n> This will prevent todo_list_add_exec_commands() from having to do awful\n> pointer arithmetics when growing the todo list buffer.\n> \n> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n> ---\n> This is a new commit.\n> \n>   sequencer.c | 61 ++++++++++++++++++++++++++++-------------------------\n>   sequencer.h |  4 ++--\n>   2 files changed, 34 insertions(+), 31 deletions(-)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index 5b84a20532..61be04ccc5 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -1950,7 +1950,7 @@ static struct todo_item *append_new_todo(struct todo_list *todo_list)\n>   }\n>   \n>   static int parse_insn_line(struct repository *r, struct todo_item *item,\n> -\t\t\t   const char *bol, char *eol)\n> +\t\t\t   const char *buf, const char *bol, char *eol)\n>   {\n>   \tstruct object_id commit_oid;\n>   \tchar *end_of_object_name;\n> @@ -1964,7 +1964,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n>   \tif (bol == eol || *bol == '\\r' || *bol == comment_line_char) {\n>   \t\titem->command = TODO_COMMENT;\n>   \t\titem->commit = NULL;\n> -\t\titem->arg = bol;\n> +\t\titem->arg_offset = bol - buf;\n>   \t\titem->arg_len = eol - bol;\n>   \t\treturn 0;\n>   \t}\n> @@ -1991,7 +1991,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n>   \t\t\treturn error(_(\"%s does not accept arguments: '%s'\"),\n>   \t\t\t\t     command_to_string(item->command), bol);\n>   \t\titem->commit = NULL;\n> -\t\titem->arg = bol;\n> +\t\titem->arg_offset = bol - buf;\n>   \t\titem->arg_len = eol - bol;\n>   \t\treturn 0;\n>   \t}\n> @@ -2003,7 +2003,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n>   \tif (item->command == TODO_EXEC || item->command == TODO_LABEL ||\n>   \t    item->command == TODO_RESET) {\n>   \t\titem->commit = NULL;\n> -\t\titem->arg = bol;\n> +\t\titem->arg_offset = bol - buf;\n>   \t\titem->arg_len = (int)(eol - bol);\n>   \t\treturn 0;\n>   \t}\n> @@ -2017,7 +2017,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n>   \t\t} else {\n>   \t\t\titem->flags |= TODO_EDIT_MERGE_MSG;\n>   \t\t\titem->commit = NULL;\n> -\t\t\titem->arg = bol;\n> +\t\t\titem->arg_offset = bol - buf;\n>   \t\t\titem->arg_len = (int)(eol - bol);\n>   \t\t\treturn 0;\n>   \t\t}\n> @@ -2029,8 +2029,9 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n>   \tstatus = get_oid(bol, &commit_oid);\n>   \t*end_of_object_name = saved;\n>   \n> -\titem->arg = end_of_object_name + strspn(end_of_object_name, \" \\t\");\n> -\titem->arg_len = (int)(eol - item->arg);\n> +\tbol = end_of_object_name + strspn(end_of_object_name, \" \\t\");\n> +\titem->arg_offset = bol - buf;\n> +\titem->arg_len = (int)(eol - bol);\n>   \n>   \tif (status < 0)\n>   \t\treturn -1;\n> @@ -2058,11 +2059,11 @@ int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n>   \n>   \t\titem = append_new_todo(todo_list);\n>   \t\titem->offset_in_buf = p - todo_list->buf.buf;\n> -\t\tif (parse_insn_line(r, item, p, eol)) {\n> +\t\tif (parse_insn_line(r, item, buf, p, eol)) {\n>   \t\t\tres = error(_(\"invalid line %d: %.*s\"),\n>   \t\t\t\ti, (int)(eol - p), p);\n>   \t\t\titem->command = TODO_COMMENT + 1;\n> -\t\t\titem->arg = p;\n> +\t\t\titem->arg_offset = p - buf;\n>   \t\t\titem->arg_len = (int)(eol - p);\n>   \t\t\titem->commit = NULL;\n>   \t\t}\n> @@ -2402,7 +2403,7 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,\n>   \n>   \t\titem->command = command;\n>   \t\titem->commit = commit;\n> -\t\titem->arg = NULL;\n> +\t\titem->arg_offset = 0;\n>   \t\titem->arg_len = 0;\n>   \t\titem->offset_in_buf = todo_list->buf.len;\n>   \t\tsubject_len = find_commit_subject(commit_buffer, &subject);\n> @@ -3438,6 +3439,8 @@ static int pick_commits(struct repository *r,\n>   \n>   \twhile (todo_list->current < todo_list->nr) {\n>   \t\tstruct todo_item *item = todo_list->items + todo_list->current;\n> +\t\tconst char *arg = todo_list->buf.buf + item->arg_offset;\n> +\n>   \t\tif (save_todo(todo_list, opts))\n>   \t\t\treturn -1;\n>   \t\tif (is_rebase_i(opts)) {\n> @@ -3488,10 +3491,9 @@ static int pick_commits(struct repository *r,\n>   \t\t\t\t\tfprintf(stderr,\n>   \t\t\t\t\t\t_(\"Stopped at %s...  %.*s\\n\"),\n>   \t\t\t\t\t\tshort_commit_name(commit),\n> -\t\t\t\t\t\titem->arg_len, item->arg);\n> +\t\t\t\t\t\titem->arg_len, arg);\n>   \t\t\t\treturn error_with_patch(r, commit,\n> -\t\t\t\t\titem->arg, item->arg_len, opts, res,\n> -\t\t\t\t\t!res);\n> +\t\t\t\t\targ, item->arg_len, opts, res, !res);\n>   \t\t\t}\n>   \t\t\tif (is_rebase_i(opts) && !res)\n>   \t\t\t\trecord_in_rewritten(&item->commit->object.oid,\n> @@ -3500,7 +3502,7 @@ static int pick_commits(struct repository *r,\n>   \t\t\t\tif (res == 1)\n>   \t\t\t\t\tintend_to_amend();\n>   \t\t\t\treturn error_failed_squash(r, item->commit, opts,\n> -\t\t\t\t\titem->arg_len, item->arg);\n> +\t\t\t\t\titem->arg_len, arg);\n>   \t\t\t} else if (res && is_rebase_i(opts) && item->commit) {\n>   \t\t\t\tint to_amend = 0;\n>   \t\t\t\tstruct object_id oid;\n> @@ -3519,16 +3521,16 @@ static int pick_commits(struct repository *r,\n>   \t\t\t\t\tto_amend = 1;\n>   \n>   \t\t\t\treturn res | error_with_patch(r, item->commit,\n> -\t\t\t\t\t\titem->arg, item->arg_len, opts,\n> +\t\t\t\t\t\targ, item->arg_len, opts,\n>   \t\t\t\t\t\tres, to_amend);\n>   \t\t\t}\n>   \t\t} else if (item->command == TODO_EXEC) {\n> -\t\t\tchar *end_of_arg = (char *)(item->arg + item->arg_len);\n> +\t\t\tchar *end_of_arg = (char *)(arg + item->arg_len);\n>   \t\t\tint saved = *end_of_arg;\n>   \t\t\tstruct stat st;\n>   \n>   \t\t\t*end_of_arg = '\\0';\n> -\t\t\tres = do_exec(r, item->arg);\n> +\t\t\tres = do_exec(r, arg);\n>   \t\t\t*end_of_arg = saved;\n>   \n>   \t\t\t/* Reread the todo file if it has changed. */\n> @@ -3545,14 +3547,14 @@ static int pick_commits(struct repository *r,\n>   \t\t\t\ttodo_list->current = -1;\n>   \t\t\t}\n>   \t\t} else if (item->command == TODO_LABEL) {\n> -\t\t\tif ((res = do_label(r, item->arg, item->arg_len)))\n> +\t\t\tif ((res = do_label(r, arg, item->arg_len)))\n>   \t\t\t\treschedule = 1;\n>   \t\t} else if (item->command == TODO_RESET) {\n> -\t\t\tif ((res = do_reset(r, item->arg, item->arg_len, opts)))\n> +\t\t\tif ((res = do_reset(r, arg, item->arg_len, opts)))\n>   \t\t\t\treschedule = 1;\n>   \t\t} else if (item->command == TODO_MERGE) {\n>   \t\t\tif ((res = do_merge(r, item->commit,\n> -\t\t\t\t\t    item->arg, item->arg_len,\n> +\t\t\t\t\t    arg, item->arg_len,\n>   \t\t\t\t\t    item->flags, opts)) < 0)\n>   \t\t\t\treschedule = 1;\n>   \t\t\telse if (item->commit)\n> @@ -3561,9 +3563,8 @@ static int pick_commits(struct repository *r,\n>   \t\t\tif (res > 0)\n>   \t\t\t\t/* failed with merge conflicts */\n>   \t\t\t\treturn error_with_patch(r, item->commit,\n> -\t\t\t\t\t\t\titem->arg,\n> -\t\t\t\t\t\t\titem->arg_len, opts,\n> -\t\t\t\t\t\t\tres, 0);\n> +\t\t\t\t\t\t\targ, item->arg_len,\n> +\t\t\t\t\t\t\topts, res, 0);\n>   \t\t} else if (!is_noop(item->command))\n>   \t\t\treturn error(_(\"unknown command %d\"), item->command);\n>   \n> @@ -3578,9 +3579,8 @@ static int pick_commits(struct repository *r,\n>   \t\t\tif (item->commit)\n>   \t\t\t\treturn error_with_patch(r,\n>   \t\t\t\t\t\t\titem->commit,\n> -\t\t\t\t\t\t\titem->arg,\n> -\t\t\t\t\t\t\titem->arg_len, opts,\n> -\t\t\t\t\t\t\tres, 0);\n> +\t\t\t\t\t\t\targ, item->arg_len,\n> +\t\t\t\t\t\t\topts, res, 0);\n>   \t\t}\n>   \n>   \t\ttodo_list->current++;\n> @@ -4520,7 +4520,8 @@ int transform_todos(struct repository *r, unsigned flags)\n>   \tfor (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {\n\nI think the changes to this function would be clearer with\n+\t\tconst char *arg = todo_list.buf.buf + item->arg_offset\nhere and then used arg instead of item->arg rather than calculating arg \ntwice. Given that we are going to need to calculate arg again in \ncheck_todo_list() maybe it would be better to have a function\n\nconst char *get_item_arg(struct todo_list *todo, struct todo_list_item \n*item) {\n\treturn todo->buf.buf + item->arg_offset;\n}\n\nand do\n+\t\tconst char *arg = get_item_arg(&todo_list, item);\nto avoid any accidents when calculating arg in future.\n\nBest Wishes\n\nPhillip\n\n>   \t\t/* if the item is not a command write it and continue */\n>   \t\tif (item->command >= TODO_COMMENT) {\n> -\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len, item->arg);\n> +\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len,\n> +\t\t\t\t    todo_list.buf.buf + item->arg_offset);\n>   \t\t\tcontinue;\n>   \t\t}\n>   \n> @@ -4550,7 +4551,8 @@ int transform_todos(struct repository *r, unsigned flags)\n>   \t\tif (!item->arg_len)\n>   \t\t\tstrbuf_addch(&buf, '\\n');\n>   \t\telse\n> -\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len, item->arg);\n> +\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len,\n> +\t\t\t\t    todo_list.buf.buf + item->arg_offset);\n>   \t}\n>   \n>   \ti = write_message(buf.buf, buf.len, todo_file, 0);\n> @@ -4626,7 +4628,8 @@ int check_todo_list(struct repository *r)\n>   \t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n>   \t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n>   \t\t\t\t    short_commit_name(commit),\n> -\t\t\t\t    item->arg_len, item->arg);\n> +\t\t\t\t    item->arg_len,\n> +\t\t\t\t    todo_list.buf.buf + item->arg_offset);\n>   \t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n>   \t\t}\n>   \t}\n> diff --git a/sequencer.h b/sequencer.h\n> index 7dc4d8946b..70a896e756 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -104,9 +104,9 @@ struct todo_item {\n>   \tenum todo_command command;\n>   \tstruct commit *commit;\n>   \tunsigned int flags;\n> -\tconst char *arg;\n>   \tint arg_len;\n> -\tsize_t offset_in_buf;\n> +\t/* The offset of the command and its argument in the strbuf */\n> +\tsize_t offset_in_buf, arg_offset;\n>   };\n>   \n>   struct todo_list {\n> \n\n"},{"id":"367320","messageId":"nycvar.QRO.7.76.6.1901221627190.41@tvgsbejvaqbjf.bet","threadId":"49504","inReplyTo":"6bc7f154-a18a-3311-cbd0-c2a578931c4b@talktalk.net","subject":"Re: [PATCH v4 03/16] sequencer: remove the 'arg' field from todo_item","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2019-01-22T15:27:41Z","receivedAt":"2019-01-22T15:28:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Phillip,\n\nOn Mon, 21 Jan 2019, Phillip Wood wrote:\n\n> const char *get_item_arg(struct todo_list *todo, struct todo_list_item *item)\n> {\n> \treturn todo->buf.buf + item->arg_offset;\n> }\n\nI like that a lot.\n\nThanks,\nDscho\n"},{"id":"367463","messageId":"20190123205821.27459-1-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20181229160413.19333-1-alban.gruin@gmail.com","subject":"[PATCH v5 00/16] sequencer: refactor functions working on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-23T20:58:05Z","receivedAt":"2019-01-23T20:59:19Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"At the center of the \"interactive\" part of the interactive rebase lies\nthe todo list.  When the user starts an interactive rebase, a todo list\nis generated, presented to the user (who then edits it using a text\neditor), read back, and then is checked and processed before the actual\nrebase takes place.\n\nSome of this processing includes adding execs commands, reordering\nfixup! and squash! commits, and checking if no commits were accidentally\ndropped by the user.\n\nBefore I converted the interactive rebase in C, these functions were\ncalled by git-rebase--interactive.sh through git-rebase--helper.  Since\nthe only way to pass around a large amount of data between a shell\nscript and a C program is to use a file (or any declination of a file),\nthe functions that checked and processed the todo list were directly\nworking on a file, the same file that the user edited.\n\nDuring the conversion, I did not address this issue, which lead to a\ncomplete_action() that reads the todo list file, does some computation\nbased on its content, and writes it back to the disk, several times in\nthe same function.\n\nAs it is not an efficient way to handle a data structure, this patch\nseries refactor the functions that processes the todo list to work on a\ntodo_list structure instead of reading it from the disk.\n\nSome commits consists in modifying edit_todo_list() (initially used by\n--edit-todo) to handle the initial edition of the todo list, to increase\ncode sharing.\n\nThis is based on nd/the-index (36e7ed69de, \"rebase-interactive.c: remove\nthe_repository references\").\n\nChanges since v4:\n\n - Adding todo_item_get_arg() to return the address of the parameter of\n   an item to avoid confusion.\n\n - Squashed <d414e1a2-e5a1-ce15-96b5-cf294c7f3c92@ramsayjones.plus.com>\n   and <5440ddf5-0b80-3d00-7daf-133a8611efa8@ramsayjones.plus.com> from\n   Ramsay Jones.\n\nAlban Gruin (16):\n  sequencer: changes in parse_insn_buffer()\n  sequencer: make the todo_list structure public\n  sequencer: remove the 'arg' field from todo_item\n  sequencer: refactor transform_todos() to work on a todo_list\n  sequencer: introduce todo_list_write_to_file()\n  sequencer: refactor check_todo_list() to work on a todo_list\n  sequencer: refactor sequencer_add_exec_commands() to work on a\n    todo_list\n  sequencer: refactor rearrange_squash() to work on a todo_list\n  sequencer: make sequencer_make_script() write its script to a strbuf\n  sequencer: change complete_action() to use the refactored functions\n  sequencer: refactor skip_unnecessary_picks() to work on a todo_list\n  rebase-interactive: use todo_list_write_to_file() in edit_todo_list()\n  rebase-interactive: append_todo_help() changes\n  rebase-interactive: rewrite edit_todo_list() to handle the initial\n    edit\n  sequencer: use edit_todo_list() in complete_action()\n  rebase--interactive: move transform_todo_file() to\n    rebase--interactive.c\n\n builtin/rebase--interactive.c |  90 +++--\n rebase-interactive.c          | 143 +++++--\n rebase-interactive.h          |   9 +-\n sequencer.c                   | 687 ++++++++++++++--------------------\n sequencer.h                   |  81 +++-\n 5 files changed, 533 insertions(+), 477 deletions(-)\n\n-- \n2.20.1\n\n"},{"id":"367464","messageId":"20190123205821.27459-2-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"[PATCH v5 01/16] sequencer: changes in parse_insn_buffer()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-23T20:58:06Z","receivedAt":"2019-01-23T20:59:23Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This clears the number of items of a todo_list before parsing it to\nallow to parse the same list multiple times without issues.  As its\nitems are not dynamically allocated, or don’t need to allocate memory,\nno additionnal memory management is required here.\n\nFurthermore, if a line is invalid, the type of the corresponding\ncommand is set to a garbage value, and its argument is defined properly.\nThis will allow to recreate the text of a todo list from its commands,\neven if one of them is incorrect.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nUnchanged since v4.\n\n sequencer.c | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex d726f77e11..a7afaf6882 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2091,6 +2091,8 @@ static int parse_insn_buffer(struct repository *r, char *buf,\n \tchar *p = buf, *next_p;\n \tint i, res = 0, fixup_okay = file_exists(rebase_path_done());\n \n+\ttodo_list->current = todo_list->nr = 0;\n+\n \tfor (i = 1; *p; i++, p = next_p) {\n \t\tchar *eol = strchrnul(p, '\\n');\n \n@@ -2104,7 +2106,10 @@ static int parse_insn_buffer(struct repository *r, char *buf,\n \t\tif (parse_insn_line(r, item, p, eol)) {\n \t\t\tres = error(_(\"invalid line %d: %.*s\"),\n \t\t\t\ti, (int)(eol - p), p);\n-\t\t\titem->command = TODO_NOOP;\n+\t\t\titem->command = TODO_COMMENT + 1;\n+\t\t\titem->arg = p;\n+\t\t\titem->arg_len = (int)(eol - p);\n+\t\t\titem->commit = NULL;\n \t\t}\n \n \t\tif (fixup_okay)\n-- \n2.20.1\n\n"},{"id":"367465","messageId":"20190123205821.27459-3-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"[PATCH v5 02/16] sequencer: make the todo_list structure public","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-23T20:58:07Z","receivedAt":"2019-01-23T20:59:25Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This makes the structures todo_list and todo_item, and the functions\ntodo_list_release() and parse_insn_buffer(), accessible outside of\nsequencer.c.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nUnchanged since v4.\n\n sequencer.c | 69 ++++++++++-------------------------------------------\n sequencer.h | 50 ++++++++++++++++++++++++++++++++++++++\n 2 files changed, 62 insertions(+), 57 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a7afaf6882..5b84a20532 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1460,32 +1460,6 @@ static int allow_empty(struct repository *r,\n \t\treturn 1;\n }\n \n-/*\n- * Note that ordering matters in this enum. Not only must it match the mapping\n- * below, it is also divided into several sections that matter.  When adding\n- * new commands, make sure you add it in the right section.\n- */\n-enum todo_command {\n-\t/* commands that handle commits */\n-\tTODO_PICK = 0,\n-\tTODO_REVERT,\n-\tTODO_EDIT,\n-\tTODO_REWORD,\n-\tTODO_FIXUP,\n-\tTODO_SQUASH,\n-\t/* commands that do something else than handling a single commit */\n-\tTODO_EXEC,\n-\tTODO_BREAK,\n-\tTODO_LABEL,\n-\tTODO_RESET,\n-\tTODO_MERGE,\n-\t/* commands that do nothing but are counted for reporting progress */\n-\tTODO_NOOP,\n-\tTODO_DROP,\n-\t/* comments (not counted for reporting progress) */\n-\tTODO_COMMENT\n-};\n-\n static struct {\n \tchar c;\n \tconst char *str;\n@@ -1962,26 +1936,7 @@ enum todo_item_flags {\n \tTODO_EDIT_MERGE_MSG = 1\n };\n \n-struct todo_item {\n-\tenum todo_command command;\n-\tstruct commit *commit;\n-\tunsigned int flags;\n-\tconst char *arg;\n-\tint arg_len;\n-\tsize_t offset_in_buf;\n-};\n-\n-struct todo_list {\n-\tstruct strbuf buf;\n-\tstruct todo_item *items;\n-\tint nr, alloc, current;\n-\tint done_nr, total_nr;\n-\tstruct stat_data stat;\n-};\n-\n-#define TODO_LIST_INIT { STRBUF_INIT }\n-\n-static void todo_list_release(struct todo_list *todo_list)\n+void todo_list_release(struct todo_list *todo_list)\n {\n \tstrbuf_release(&todo_list->buf);\n \tFREE_AND_NULL(todo_list->items);\n@@ -2084,8 +2039,8 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \treturn !item->commit;\n }\n \n-static int parse_insn_buffer(struct repository *r, char *buf,\n-\t\t\t     struct todo_list *todo_list)\n+int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n+\t\t\t\tstruct todo_list *todo_list)\n {\n \tstruct todo_item *item;\n \tchar *p = buf, *next_p;\n@@ -2184,7 +2139,7 @@ static int read_populate_todo(struct repository *r,\n \t\treturn error(_(\"could not stat '%s'\"), todo_file);\n \tfill_stat_data(&todo_list->stat, &st);\n \n-\tres = parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n+\tres = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n \tif (res) {\n \t\tif (is_rebase_i(opts))\n \t\t\treturn error(_(\"please fix this using \"\n@@ -2215,7 +2170,7 @@ static int read_populate_todo(struct repository *r,\n \t\tFILE *f = fopen_or_warn(rebase_path_msgtotal(), \"w\");\n \n \t\tif (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&\n-\t\t    !parse_insn_buffer(r, done.buf.buf, &done))\n+\t\t    !todo_list_parse_insn_buffer(r, done.buf.buf, &done))\n \t\t\ttodo_list->done_nr = count_commands(&done);\n \t\telse\n \t\t\ttodo_list->done_nr = 0;\n@@ -4501,7 +4456,7 @@ int sequencer_add_exec_commands(struct repository *r,\n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4557,7 +4512,7 @@ int transform_todos(struct repository *r, unsigned flags)\n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4643,7 +4598,7 @@ int check_todo_list(struct repository *r)\n \t\tgoto leave_check;\n \t}\n \tadvise_to_edit_todo = res =\n-\t\tparse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n+\t\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n \n \tif (res || check_level == MISSING_COMMIT_CHECK_IGNORE)\n \t\tgoto leave_check;\n@@ -4662,7 +4617,7 @@ int check_todo_list(struct repository *r)\n \t\tgoto leave_check;\n \t}\n \tstrbuf_release(&todo_file);\n-\tres = !!parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n+\tres = !!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n \n \t/* Find commits in git-rebase-todo.backup yet unseen */\n \tfor (i = todo_list.nr - 1; i >= 0; i--) {\n@@ -4744,7 +4699,7 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \n \tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n \t\treturn -1;\n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n@@ -4832,7 +4787,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(r, buf->buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(r, buf->buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4940,7 +4895,7 @@ int rearrange_squash(struct repository *r)\n \n \tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n \t\treturn -1;\n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex d2c18edd3a..7dc4d8946b 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -73,6 +73,56 @@ enum missing_commit_check_level {\n int write_message(const void *buf, size_t len, const char *filename,\n \t\t  int append_eol);\n \n+/*\n+ * Note that ordering matters in this enum. Not only must it match the mapping\n+ * of todo_command_info (in sequencer.c), it is also divided into several\n+ * sections that matter.  When adding new commands, make sure you add it in the\n+ * right section.\n+ */\n+enum todo_command {\n+\t/* commands that handle commits */\n+\tTODO_PICK = 0,\n+\tTODO_REVERT,\n+\tTODO_EDIT,\n+\tTODO_REWORD,\n+\tTODO_FIXUP,\n+\tTODO_SQUASH,\n+\t/* commands that do something else than handling a single commit */\n+\tTODO_EXEC,\n+\tTODO_BREAK,\n+\tTODO_LABEL,\n+\tTODO_RESET,\n+\tTODO_MERGE,\n+\t/* commands that do nothing but are counted for reporting progress */\n+\tTODO_NOOP,\n+\tTODO_DROP,\n+\t/* comments (not counted for reporting progress) */\n+\tTODO_COMMENT\n+};\n+\n+struct todo_item {\n+\tenum todo_command command;\n+\tstruct commit *commit;\n+\tunsigned int flags;\n+\tconst char *arg;\n+\tint arg_len;\n+\tsize_t offset_in_buf;\n+};\n+\n+struct todo_list {\n+\tstruct strbuf buf;\n+\tstruct todo_item *items;\n+\tint nr, alloc, current;\n+\tint done_nr, total_nr;\n+\tstruct stat_data stat;\n+};\n+\n+#define TODO_LIST_INIT { STRBUF_INIT }\n+\n+int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n+\t\t\t\tstruct todo_list *todo_list);\n+void todo_list_release(struct todo_list *todo_list);\n+\n /* Call this to setup defaults before parsing command line options */\n void sequencer_init_config(struct replay_opts *opts);\n int sequencer_pick_revisions(struct repository *repo,\n-- \n2.20.1\n\n"},{"id":"367466","messageId":"20190123205821.27459-4-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"[PATCH v5 03/16] sequencer: remove the 'arg' field from todo_item","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-23T20:58:08Z","receivedAt":"2019-01-23T20:59:29Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"The 'arg' field of todo_item used to store the address of the first byte\nof the parameter of a command in a todo list.  It was associated with\nthe length of the parameter (the 'arg_len' field).\n\nThis replaces the 'arg' field by 'arg_offset'.  This new field does not\nstore the address of the parameter, but the position of the first\ncharacter of the parameter in the buffer.  todo_item_get_arg() is added\nto return the address of the parameter of an item.\n\nThis will prevent todo_list_add_exec_commands() from having to do awful\npointer arithmetics when growing the todo list buffer.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nIntroduction and use of todo_item_get_arg().\n\n sequencer.c | 67 ++++++++++++++++++++++++++++++-----------------------\n sequencer.h |  6 +++--\n 2 files changed, 42 insertions(+), 31 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 5b84a20532..0e7ab16a05 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1949,8 +1949,14 @@ static struct todo_item *append_new_todo(struct todo_list *todo_list)\n \treturn todo_list->items + todo_list->nr++;\n }\n \n+const char *todo_item_get_arg(struct todo_list *todo_list,\n+\t\t\t      struct todo_item *item)\n+{\n+\treturn todo_list->buf.buf + item->arg_offset;\n+}\n+\n static int parse_insn_line(struct repository *r, struct todo_item *item,\n-\t\t\t   const char *bol, char *eol)\n+\t\t\t   const char *buf, const char *bol, char *eol)\n {\n \tstruct object_id commit_oid;\n \tchar *end_of_object_name;\n@@ -1964,7 +1970,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \tif (bol == eol || *bol == '\\r' || *bol == comment_line_char) {\n \t\titem->command = TODO_COMMENT;\n \t\titem->commit = NULL;\n-\t\titem->arg = bol;\n+\t\titem->arg_offset = bol - buf;\n \t\titem->arg_len = eol - bol;\n \t\treturn 0;\n \t}\n@@ -1991,7 +1997,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \t\t\treturn error(_(\"%s does not accept arguments: '%s'\"),\n \t\t\t\t     command_to_string(item->command), bol);\n \t\titem->commit = NULL;\n-\t\titem->arg = bol;\n+\t\titem->arg_offset = bol - buf;\n \t\titem->arg_len = eol - bol;\n \t\treturn 0;\n \t}\n@@ -2003,7 +2009,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \tif (item->command == TODO_EXEC || item->command == TODO_LABEL ||\n \t    item->command == TODO_RESET) {\n \t\titem->commit = NULL;\n-\t\titem->arg = bol;\n+\t\titem->arg_offset = bol - buf;\n \t\titem->arg_len = (int)(eol - bol);\n \t\treturn 0;\n \t}\n@@ -2017,7 +2023,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \t\t} else {\n \t\t\titem->flags |= TODO_EDIT_MERGE_MSG;\n \t\t\titem->commit = NULL;\n-\t\t\titem->arg = bol;\n+\t\t\titem->arg_offset = bol - buf;\n \t\t\titem->arg_len = (int)(eol - bol);\n \t\t\treturn 0;\n \t\t}\n@@ -2029,8 +2035,9 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \tstatus = get_oid(bol, &commit_oid);\n \t*end_of_object_name = saved;\n \n-\titem->arg = end_of_object_name + strspn(end_of_object_name, \" \\t\");\n-\titem->arg_len = (int)(eol - item->arg);\n+\tbol = end_of_object_name + strspn(end_of_object_name, \" \\t\");\n+\titem->arg_offset = bol - buf;\n+\titem->arg_len = (int)(eol - bol);\n \n \tif (status < 0)\n \t\treturn -1;\n@@ -2058,11 +2065,11 @@ int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \n \t\titem = append_new_todo(todo_list);\n \t\titem->offset_in_buf = p - todo_list->buf.buf;\n-\t\tif (parse_insn_line(r, item, p, eol)) {\n+\t\tif (parse_insn_line(r, item, buf, p, eol)) {\n \t\t\tres = error(_(\"invalid line %d: %.*s\"),\n \t\t\t\ti, (int)(eol - p), p);\n \t\t\titem->command = TODO_COMMENT + 1;\n-\t\t\titem->arg = p;\n+\t\t\titem->arg_offset = p - buf;\n \t\t\titem->arg_len = (int)(eol - p);\n \t\t\titem->commit = NULL;\n \t\t}\n@@ -2402,7 +2409,7 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,\n \n \t\titem->command = command;\n \t\titem->commit = commit;\n-\t\titem->arg = NULL;\n+\t\titem->arg_offset = 0;\n \t\titem->arg_len = 0;\n \t\titem->offset_in_buf = todo_list->buf.len;\n \t\tsubject_len = find_commit_subject(commit_buffer, &subject);\n@@ -3438,6 +3445,8 @@ static int pick_commits(struct repository *r,\n \n \twhile (todo_list->current < todo_list->nr) {\n \t\tstruct todo_item *item = todo_list->items + todo_list->current;\n+\t\tconst char *arg = todo_item_get_arg(todo_list, item);\n+\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n \t\tif (is_rebase_i(opts)) {\n@@ -3488,10 +3497,9 @@ static int pick_commits(struct repository *r,\n \t\t\t\t\tfprintf(stderr,\n \t\t\t\t\t\t_(\"Stopped at %s...  %.*s\\n\"),\n \t\t\t\t\t\tshort_commit_name(commit),\n-\t\t\t\t\t\titem->arg_len, item->arg);\n+\t\t\t\t\t\titem->arg_len, arg);\n \t\t\t\treturn error_with_patch(r, commit,\n-\t\t\t\t\titem->arg, item->arg_len, opts, res,\n-\t\t\t\t\t!res);\n+\t\t\t\t\targ, item->arg_len, opts, res, !res);\n \t\t\t}\n \t\t\tif (is_rebase_i(opts) && !res)\n \t\t\t\trecord_in_rewritten(&item->commit->object.oid,\n@@ -3500,7 +3508,7 @@ static int pick_commits(struct repository *r,\n \t\t\t\tif (res == 1)\n \t\t\t\t\tintend_to_amend();\n \t\t\t\treturn error_failed_squash(r, item->commit, opts,\n-\t\t\t\t\titem->arg_len, item->arg);\n+\t\t\t\t\titem->arg_len, arg);\n \t\t\t} else if (res && is_rebase_i(opts) && item->commit) {\n \t\t\t\tint to_amend = 0;\n \t\t\t\tstruct object_id oid;\n@@ -3519,16 +3527,16 @@ static int pick_commits(struct repository *r,\n \t\t\t\t\tto_amend = 1;\n \n \t\t\t\treturn res | error_with_patch(r, item->commit,\n-\t\t\t\t\t\titem->arg, item->arg_len, opts,\n+\t\t\t\t\t\targ, item->arg_len, opts,\n \t\t\t\t\t\tres, to_amend);\n \t\t\t}\n \t\t} else if (item->command == TODO_EXEC) {\n-\t\t\tchar *end_of_arg = (char *)(item->arg + item->arg_len);\n+\t\t\tchar *end_of_arg = (char *)(arg + item->arg_len);\n \t\t\tint saved = *end_of_arg;\n \t\t\tstruct stat st;\n \n \t\t\t*end_of_arg = '\\0';\n-\t\t\tres = do_exec(r, item->arg);\n+\t\t\tres = do_exec(r, arg);\n \t\t\t*end_of_arg = saved;\n \n \t\t\t/* Reread the todo file if it has changed. */\n@@ -3545,14 +3553,14 @@ static int pick_commits(struct repository *r,\n \t\t\t\ttodo_list->current = -1;\n \t\t\t}\n \t\t} else if (item->command == TODO_LABEL) {\n-\t\t\tif ((res = do_label(r, item->arg, item->arg_len)))\n+\t\t\tif ((res = do_label(r, arg, item->arg_len)))\n \t\t\t\treschedule = 1;\n \t\t} else if (item->command == TODO_RESET) {\n-\t\t\tif ((res = do_reset(r, item->arg, item->arg_len, opts)))\n+\t\t\tif ((res = do_reset(r, arg, item->arg_len, opts)))\n \t\t\t\treschedule = 1;\n \t\t} else if (item->command == TODO_MERGE) {\n \t\t\tif ((res = do_merge(r, item->commit,\n-\t\t\t\t\t    item->arg, item->arg_len,\n+\t\t\t\t\t    arg, item->arg_len,\n \t\t\t\t\t    item->flags, opts)) < 0)\n \t\t\t\treschedule = 1;\n \t\t\telse if (item->commit)\n@@ -3561,9 +3569,8 @@ static int pick_commits(struct repository *r,\n \t\t\tif (res > 0)\n \t\t\t\t/* failed with merge conflicts */\n \t\t\t\treturn error_with_patch(r, item->commit,\n-\t\t\t\t\t\t\titem->arg,\n-\t\t\t\t\t\t\titem->arg_len, opts,\n-\t\t\t\t\t\t\tres, 0);\n+\t\t\t\t\t\t\targ, item->arg_len,\n+\t\t\t\t\t\t\topts, res, 0);\n \t\t} else if (!is_noop(item->command))\n \t\t\treturn error(_(\"unknown command %d\"), item->command);\n \n@@ -3578,9 +3585,8 @@ static int pick_commits(struct repository *r,\n \t\t\tif (item->commit)\n \t\t\t\treturn error_with_patch(r,\n \t\t\t\t\t\t\titem->commit,\n-\t\t\t\t\t\t\titem->arg,\n-\t\t\t\t\t\t\titem->arg_len, opts,\n-\t\t\t\t\t\t\tres, 0);\n+\t\t\t\t\t\t\targ, item->arg_len,\n+\t\t\t\t\t\t\topts, res, 0);\n \t\t}\n \n \t\ttodo_list->current++;\n@@ -4520,7 +4526,8 @@ int transform_todos(struct repository *r, unsigned flags)\n \tfor (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n-\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len, item->arg);\n+\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len,\n+\t\t\t\t    todo_item_get_arg(&todo_list, item));\n \t\t\tcontinue;\n \t\t}\n \n@@ -4550,7 +4557,8 @@ int transform_todos(struct repository *r, unsigned flags)\n \t\tif (!item->arg_len)\n \t\t\tstrbuf_addch(&buf, '\\n');\n \t\telse\n-\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len, item->arg);\n+\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len,\n+\t\t\t\t    todo_item_get_arg(&todo_list, item));\n \t}\n \n \ti = write_message(buf.buf, buf.len, todo_file, 0);\n@@ -4626,7 +4634,8 @@ int check_todo_list(struct repository *r)\n \t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n \t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n \t\t\t\t    short_commit_name(commit),\n-\t\t\t\t    item->arg_len, item->arg);\n+\t\t\t\t    item->arg_len,\n+\t\t\t\t    todo_item_get_arg(&todo_list, item));\n \t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n \t\t}\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex 7dc4d8946b..cdb9ed7a2d 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -104,9 +104,9 @@ struct todo_item {\n \tenum todo_command command;\n \tstruct commit *commit;\n \tunsigned int flags;\n-\tconst char *arg;\n \tint arg_len;\n-\tsize_t offset_in_buf;\n+\t/* The offset of the command and its argument in the strbuf */\n+\tsize_t offset_in_buf, arg_offset;\n };\n \n struct todo_list {\n@@ -122,6 +122,8 @@ struct todo_list {\n int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \t\t\t\tstruct todo_list *todo_list);\n void todo_list_release(struct todo_list *todo_list);\n+const char *todo_item_get_arg(struct todo_list *todo_list,\n+\t\t\t      struct todo_item *item);\n \n /* Call this to setup defaults before parsing command line options */\n void sequencer_init_config(struct replay_opts *opts);\n-- \n2.20.1\n\n"},{"id":"367467","messageId":"20190123205821.27459-5-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"[PATCH v5 04/16] sequencer: refactor transform_todos() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-23T20:58:09Z","receivedAt":"2019-01-23T20:59:30Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors transform_todos() to work on a todo_list.  The function\nis renamed todo_list_transform().\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, transform_todo_file().  It is still used by\ncomplete_action() and edit_todo_list() for now, but they will be\nreplaced in a future commit.\n\ntodo_list_transform() is not a static function, because it will be used\nby edit_todo_list() from rebase-interactive.c in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |  2 +-\n rebase-interactive.c          |  4 +--\n sequencer.c                   | 54 +++++++++++++++++++++++------------\n sequencer.h                   |  4 ++-\n 4 files changed, 42 insertions(+), 22 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex dd2a55ab1d..0898eb4c59 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -253,7 +253,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t}\n \tcase SHORTEN_OIDS:\n \tcase EXPAND_OIDS:\n-\t\tret = transform_todos(the_repository, flags);\n+\t\tret = transform_todo_file(the_repository, flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n \t\tret = check_todo_list(the_repository);\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 68aff1dac2..842fa07e7e 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -69,7 +69,7 @@ int edit_todo_list(struct repository *r, unsigned flags)\n \n \tstrbuf_release(&buf);\n \n-\ttransform_todos(r, flags | TODO_LIST_SHORTEN_IDS);\n+\ttransform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS);\n \n \tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n@@ -85,7 +85,7 @@ int edit_todo_list(struct repository *r, unsigned flags)\n \tif (launch_sequence_editor(todo_file, NULL, NULL))\n \t\treturn -1;\n \n-\ttransform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS));\n+\ttransform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS));\n \n \treturn 0;\n }\ndiff --git a/sequencer.c b/sequencer.c\nindex 0e7ab16a05..7a295cbd3f 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4507,27 +4507,18 @@ int sequencer_add_exec_commands(struct repository *r,\n \treturn i;\n }\n \n-int transform_todos(struct repository *r, unsigned flags)\n+void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n+\t\t\t unsigned flags)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct todo_item *item;\n \tint i;\n \n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tfor (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {\n+\tfor (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n \t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len,\n-\t\t\t\t    todo_item_get_arg(&todo_list, item));\n+\t\t\t\t    todo_item_get_arg(todo_list, item));\n \t\t\tcontinue;\n \t\t}\n \n@@ -4558,12 +4549,39 @@ int transform_todos(struct repository *r, unsigned flags)\n \t\t\tstrbuf_addch(&buf, '\\n');\n \t\telse\n \t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len,\n-\t\t\t\t    todo_item_get_arg(&todo_list, item));\n+\t\t\t\t    todo_item_get_arg(todo_list, item));\n \t}\n \n-\ti = write_message(buf.buf, buf.len, todo_file, 0);\n+\tstrbuf_reset(&todo_list->buf);\n+\tstrbuf_add(&todo_list->buf, buf.buf, buf.len);\n+\tstrbuf_release(&buf);\n+\n+\tif (todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list))\n+\t\tBUG(\"unusable todo list\");\n+}\n+\n+int transform_todo_file(struct repository *r, unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_transform(r, &todo_list, flags);\n+\n+\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n \ttodo_list_release(&todo_list);\n-\treturn i;\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n }\n \n enum missing_commit_check_level get_missing_commit_check_level(void)\n@@ -4825,7 +4843,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n \t\t\t     rebase_path_todo_backup());\n \n-\tif (transform_todos(r, flags | TODO_LIST_SHORTEN_IDS))\n+\tif (transform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS))\n \t\treturn error(_(\"could not transform the todo list\"));\n \n \tstrbuf_reset(buf);\n@@ -4854,7 +4872,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn -1;\n \t}\n \n-\tif (transform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS)))\n+\tif (transform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS)))\n \t\treturn error(_(\"could not transform the todo list\"));\n \n \tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\ndiff --git a/sequencer.h b/sequencer.h\nindex cdb9ed7a2d..35472c137b 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -121,6 +121,8 @@ struct todo_list {\n \n int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \t\t\t\tstruct todo_list *todo_list);\n+void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n+\t\t\t unsigned flags);\n void todo_list_release(struct todo_list *todo_list);\n const char *todo_item_get_arg(struct todo_list *todo_list,\n \t\t\t      struct todo_item *item);\n@@ -148,7 +150,7 @@ int sequencer_make_script(struct repository *repo, FILE *out,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(struct repository *r, const char *command);\n-int transform_todos(struct repository *r, unsigned flags);\n+int transform_todo_file(struct repository *r, unsigned flags);\n enum missing_commit_check_level get_missing_commit_check_level(void);\n int check_todo_list(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n-- \n2.20.1\n\n"},{"id":"367468","messageId":"20190123205821.27459-6-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"[PATCH v5 05/16] sequencer: introduce todo_list_write_to_file()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-23T20:58:10Z","receivedAt":"2019-01-23T20:59:32Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This introduces a new function to recreate the text of a todo list from\nits commands and write it to a file.  This will be useful as the next\nfew commits will change the use of the buffer in struct todo_list so it\nwill no longer be a mirror of the file on disk.\n\nThis functionality already exists in todo_list_transform(), but this\nfunction was made to replace the buffer of a todo list, which is not\nwhat we want here.  Thus, the part of todo_list_transform() that\nreplaces the buffer is dropped, and the function is renamed\ntodo_list_to_strbuf().  It is called by todo_list_write_to_file() to\nfill the buffer to write to the disk.\n\ntodo_list_write_to_file() can also take care of appending the help text\nto the buffer before writing it to the disk, or to write only the first\nn items of the list.  This feature will be used by\nskip_unnecessary_picks(), which has to write done commands in a file.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 61 +++++++++++++++++++++++++++++++++++------------------\n sequencer.h | 11 ++++++----\n 2 files changed, 48 insertions(+), 24 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 7a295cbd3f..87b43994ff 100644\n--- a/sequencer.c\n\n+++ b/sequencer.c\n@@ -4507,26 +4507,28 @@ int sequencer_add_exec_commands(struct repository *r,\n \treturn i;\n }\n \n-void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n-\t\t\t unsigned flags)\n+static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n+\t\t\t\tstruct strbuf *buf, int num, unsigned flags)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n \tstruct todo_item *item;\n-\tint i;\n+\tint i, max = todo_list->nr;\n \n-\tfor (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {\n+\tif (num > 0 && num < max)\n+\t\tmax = num;\n+\n+\tfor (item = todo_list->items, i = 0; i < max; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n-\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len,\n+\t\t\tstrbuf_addf(buf, \"%.*s\\n\", item->arg_len,\n \t\t\t\t    todo_item_get_arg(todo_list, item));\n \t\t\tcontinue;\n \t\t}\n \n \t\t/* add command to the buffer */\n \t\tif (flags & TODO_LIST_ABBREVIATE_CMDS)\n-\t\t\tstrbuf_addch(&buf, command_to_char(item->command));\n+\t\t\tstrbuf_addch(buf, command_to_char(item->command));\n \t\telse\n-\t\t\tstrbuf_addstr(&buf, command_to_string(item->command));\n+\t\t\tstrbuf_addstr(buf, command_to_string(item->command));\n \n \t\t/* add commit id */\n \t\tif (item->commit) {\n@@ -4536,28 +4538,48 @@ void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n \n \t\t\tif (item->command == TODO_MERGE) {\n \t\t\t\tif (item->flags & TODO_EDIT_MERGE_MSG)\n-\t\t\t\t\tstrbuf_addstr(&buf, \" -c\");\n+\t\t\t\t\tstrbuf_addstr(buf, \" -c\");\n \t\t\t\telse\n-\t\t\t\t\tstrbuf_addstr(&buf, \" -C\");\n+\t\t\t\t\tstrbuf_addstr(buf, \" -C\");\n \t\t\t}\n \n-\t\t\tstrbuf_addf(&buf, \" %s\", oid);\n+\t\t\tstrbuf_addf(buf, \" %s\", oid);\n \t\t}\n \n \t\t/* add all the rest */\n \t\tif (!item->arg_len)\n-\t\t\tstrbuf_addch(&buf, '\\n');\n+\t\t\tstrbuf_addch(buf, '\\n');\n \t\telse\n-\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len,\n+\t\t\tstrbuf_addf(buf, \" %.*s\\n\", item->arg_len,\n \t\t\t\t    todo_item_get_arg(todo_list, item));\n \t}\n+}\n \n-\tstrbuf_reset(&todo_list->buf);\n-\tstrbuf_add(&todo_list->buf, buf.buf, buf.len);\n+int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n+\t\t\t    const char *file, const char *shortrevisions,\n+\t\t\t    const char *shortonto, int num, unsigned flags)\n+{\n+\tint edit_todo = !(shortrevisions && shortonto), res;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\ttodo_list_to_strbuf(r, todo_list, &buf, num, flags);\n+\n+\tif (flags & TODO_LIST_APPEND_TODO_HELP) {\n+\t\tint command_count = count_commands(todo_list);\n+\t\tif (!edit_todo) {\n+\t\t\tstrbuf_addch(&buf, '\\n');\n+\t\t\tstrbuf_commented_addf(&buf, Q_(\"Rebase %s onto %s (%d command)\",\n+\t\t\t\t\t\t       \"Rebase %s onto %s (%d commands)\",\n+\t\t\t\t\t\t       command_count),\n+\t\t\t\t\t      shortrevisions, shortonto, command_count);\n+\t\t}\n+\t\tappend_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, &buf);\n+\t}\n+\n+\tres = write_message(buf.buf, buf.len, file, 0);\n \tstrbuf_release(&buf);\n \n-\tif (todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list))\n-\t\tBUG(\"unusable todo list\");\n+\treturn res;\n }\n \n int transform_todo_file(struct repository *r, unsigned flags)\n@@ -4574,9 +4596,8 @@ int transform_todo_file(struct repository *r, unsigned flags)\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n \n-\ttodo_list_transform(r, &todo_list, flags);\n-\n-\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n+\tres = todo_list_write_to_file(r, &todo_list, todo_file,\n+\t\t\t\t      NULL, NULL, -1, flags);\n \ttodo_list_release(&todo_list);\n \n \tif (res)\ndiff --git a/sequencer.h b/sequencer.h\nindex 35472c137b..dc3d8f76f7 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -121,8 +121,9 @@ struct todo_list {\n \n int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \t\t\t\tstruct todo_list *todo_list);\n-void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n-\t\t\t unsigned flags);\n+int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n+\t\t\t    const char *file, const char *shortrevisions,\n+\t\t\t    const char *shortonto, int num, unsigned flags);\n void todo_list_release(struct todo_list *todo_list);\n const char *todo_item_get_arg(struct todo_list *todo_list,\n \t\t\t      struct todo_item *item);\n@@ -145,8 +146,10 @@ int sequencer_remove_state(struct replay_opts *opts);\n  * commits should be rebased onto the new base, this flag needs to be passed.\n  */\n #define TODO_LIST_REBASE_COUSINS (1U << 4)\n-int sequencer_make_script(struct repository *repo, FILE *out,\n-\t\t\t  int argc, const char **argv,\n+#define TODO_LIST_APPEND_TODO_HELP (1U << 5)\n+\n+int sequencer_make_script(struct repository *r, FILE *out, int argc,\n+\t\t\t  const char **argv,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(struct repository *r, const char *command);\n-- \n2.20.1\n\n"},{"id":"367469","messageId":"20190123205821.27459-7-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"[PATCH v5 06/16] sequencer: refactor check_todo_list() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-23T20:58:11Z","receivedAt":"2019-01-23T20:59:33Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors check_todo_list() to work on a todo_list to avoid\nredundant reads and writes to the disk.  The function is renamed\ntodo_list_check().  The parsing of the two todo lists is left to the\ncaller.\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, check_todo_list_from_file().  It reads the file\nfrom the disk, parses it, pass the todo_list to todo_list_check(), and\nwrites it back to the disk.\n\nAs get_missing_commit_check_level() and the enum\nmissing_commit_check_level are no longer needed inside of sequencer.c,\nthey are moved to rebase-interactive.c, and made static again.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nSquashed <d414e1a2-e5a1-ce15-96b5-cf294c7f3c92@ramsayjones.plus.com>\nfrom Ramsay Jones.\n\n builtin/rebase--interactive.c |   2 +-\n rebase-interactive.c          |  91 ++++++++++++++++++++++++-\n rebase-interactive.h          |   2 +\n sequencer.c                   | 121 +++++++---------------------------\n sequencer.h                   |   9 +--\n 5 files changed, 117 insertions(+), 108 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 0898eb4c59..df19ccaeb9 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -256,7 +256,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = transform_todo_file(the_repository, flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n-\t\tret = check_todo_list(the_repository);\n+\t\tret = check_todo_list_from_file(the_repository);\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n \t\tret = rearrange_squash(the_repository);\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 842fa07e7e..dfa6dd530f 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -1,8 +1,32 @@\n #include \"cache.h\"\n #include \"commit.h\"\n-#include \"rebase-interactive.h\"\n #include \"sequencer.h\"\n+#include \"rebase-interactive.h\"\n #include \"strbuf.h\"\n+#include \"commit-slab.h\"\n+#include \"config.h\"\n+\n+enum missing_commit_check_level {\n+\tMISSING_COMMIT_CHECK_IGNORE = 0,\n+\tMISSING_COMMIT_CHECK_WARN,\n+\tMISSING_COMMIT_CHECK_ERROR\n+};\n+\n+static enum missing_commit_check_level get_missing_commit_check_level(void)\n+{\n+\tconst char *value;\n+\n+\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n+\t\t\t!strcasecmp(\"ignore\", value))\n+\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n+\tif (!strcasecmp(\"warn\", value))\n+\t\treturn MISSING_COMMIT_CHECK_WARN;\n+\tif (!strcasecmp(\"error\", value))\n+\t\treturn MISSING_COMMIT_CHECK_ERROR;\n+\twarning(_(\"unrecognized setting %s for option \"\n+\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n+\treturn MISSING_COMMIT_CHECK_IGNORE;\n+}\n \n void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \t\t      struct strbuf *buf)\n@@ -89,3 +113,68 @@ int edit_todo_list(struct repository *r, unsigned flags)\n \n \treturn 0;\n }\n+\n+define_commit_slab(commit_seen, unsigned char);\n+/*\n+ * Check if the user dropped some commits by mistake\n+ * Behaviour determined by rebase.missingCommitsCheck.\n+ * Check if there is an unrecognized command or a\n+ * bad SHA-1 in a command.\n+ */\n+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo)\n+{\n+\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n+\tstruct strbuf missing = STRBUF_INIT;\n+\tint res = 0, i;\n+\tstruct commit_seen commit_seen;\n+\n+\tinit_commit_seen(&commit_seen);\n+\n+\tif (check_level == MISSING_COMMIT_CHECK_IGNORE)\n+\t\tgoto leave_check;\n+\n+\t/* Mark the commits in git-rebase-todo as seen */\n+\tfor (i = 0; i < new_todo->nr; i++) {\n+\t\tstruct commit *commit = new_todo->items[i].commit;\n+\t\tif (commit)\n+\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t}\n+\n+\t/* Find commits in git-rebase-todo.backup yet unseen */\n+\tfor (i = old_todo->nr - 1; i >= 0; i--) {\n+\t\tstruct todo_item *item = old_todo->items + i;\n+\t\tstruct commit *commit = item->commit;\n+\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n+\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n+\t\t\t\t    find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV),\n+\t\t\t\t    item->arg_len,\n+\t\t\t\t    todo_item_get_arg(old_todo, item));\n+\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t\t}\n+\t}\n+\n+\t/* Warn about missing commits */\n+\tif (!missing.len)\n+\t\tgoto leave_check;\n+\n+\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n+\t\tres = 1;\n+\n+\tfprintf(stderr,\n+\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n+\t\t\"Dropped commits (newer to older):\\n\"));\n+\n+\t/* Make the list user-friendly and display */\n+\tfputs(missing.buf, stderr);\n+\tstrbuf_release(&missing);\n+\n+\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n+\t\t\"explicitly remove a commit.\\n\\n\"\n+\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n+\t\t\"the level of warnings.\\n\"\n+\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n+\n+leave_check:\n+\tclear_commit_seen(&commit_seen);\n+\treturn res;\n+}\ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 17b6c9f6d0..187b5032d6 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -3,9 +3,11 @@\n \n struct strbuf;\n struct repository;\n+struct todo_list;\n \n void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \t\t      struct strbuf *buf);\n int edit_todo_list(struct repository *r, unsigned flags);\n+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n \n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex 87b43994ff..266f80d704 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4605,112 +4605,37 @@ int transform_todo_file(struct repository *r, unsigned flags)\n \treturn 0;\n }\n \n-enum missing_commit_check_level get_missing_commit_check_level(void)\n-{\n-\tconst char *value;\n-\n-\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n-\t\t\t!strcasecmp(\"ignore\", value))\n-\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n-\tif (!strcasecmp(\"warn\", value))\n-\t\treturn MISSING_COMMIT_CHECK_WARN;\n-\tif (!strcasecmp(\"error\", value))\n-\t\treturn MISSING_COMMIT_CHECK_ERROR;\n-\twarning(_(\"unrecognized setting %s for option \"\n-\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n-\treturn MISSING_COMMIT_CHECK_IGNORE;\n-}\n+static const char edit_todo_list_advice[] =\n+N_(\"You can fix this with 'git rebase --edit-todo' \"\n+\"and then run 'git rebase --continue'.\\n\"\n+\"Or you can abort the rebase with 'git rebase\"\n+\" --abort'.\\n\");\n \n-define_commit_slab(commit_seen, unsigned char);\n-/*\n- * Check if the user dropped some commits by mistake\n- * Behaviour determined by rebase.missingCommitsCheck.\n- * Check if there is an unrecognized command or a\n- * bad SHA-1 in a command.\n- */\n-int check_todo_list(struct repository *r)\n+int check_todo_list_from_file(struct repository *r)\n {\n-\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n-\tstruct strbuf todo_file = STRBUF_INIT;\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf missing = STRBUF_INIT;\n-\tint advise_to_edit_todo = 0, res = 0, i;\n-\tstruct commit_seen commit_seen;\n-\n-\tinit_commit_seen(&commit_seen);\n+\tstruct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;\n+\tint res = 0;\n \n-\tstrbuf_addstr(&todo_file, rebase_path_todo());\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n+\tif (strbuf_read_file_or_whine(&new_todo.buf, rebase_path_todo()) < 0) {\n \t\tres = -1;\n-\t\tgoto leave_check;\n-\t}\n-\tadvise_to_edit_todo = res =\n-\t\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n-\n-\tif (res || check_level == MISSING_COMMIT_CHECK_IGNORE)\n-\t\tgoto leave_check;\n-\n-\t/* Mark the commits in git-rebase-todo as seen */\n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tstruct commit *commit = todo_list.items[i].commit;\n-\t\tif (commit)\n-\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t\tgoto out;\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\tstrbuf_addstr(&todo_file, \".backup\");\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n+\tif (strbuf_read_file_or_whine(&old_todo.buf, rebase_path_todo_backup()) < 0) {\n \t\tres = -1;\n-\t\tgoto leave_check;\n-\t}\n-\tstrbuf_release(&todo_file);\n-\tres = !!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n-\n-\t/* Find commits in git-rebase-todo.backup yet unseen */\n-\tfor (i = todo_list.nr - 1; i >= 0; i--) {\n-\t\tstruct todo_item *item = todo_list.items + i;\n-\t\tstruct commit *commit = item->commit;\n-\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n-\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n-\t\t\t\t    short_commit_name(commit),\n-\t\t\t\t    item->arg_len,\n-\t\t\t\t    todo_item_get_arg(&todo_list, item));\n-\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n-\t\t}\n+\t\tgoto out;\n \t}\n \n-\t/* Warn about missing commits */\n-\tif (!missing.len)\n-\t\tgoto leave_check;\n-\n-\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n-\t\tadvise_to_edit_todo = res = 1;\n-\n-\tfprintf(stderr,\n-\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n-\t\t\"Dropped commits (newer to older):\\n\"));\n-\n-\t/* Make the list user-friendly and display */\n-\tfputs(missing.buf, stderr);\n-\tstrbuf_release(&missing);\n-\n-\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n-\t\t\"explicitly remove a commit.\\n\\n\"\n-\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n-\t\t\"the level of warnings.\\n\"\n-\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n-\n-leave_check:\n-\tclear_commit_seen(&commit_seen);\n-\tstrbuf_release(&todo_file);\n-\ttodo_list_release(&todo_list);\n-\n-\tif (advise_to_edit_todo)\n-\t\tfprintf(stderr,\n-\t\t\t_(\"You can fix this with 'git rebase --edit-todo' \"\n-\t\t\t  \"and then run 'git rebase --continue'.\\n\"\n-\t\t\t  \"Or you can abort the rebase with 'git rebase\"\n-\t\t\t  \" --abort'.\\n\"));\n+\tres = todo_list_parse_insn_buffer(r, old_todo.buf.buf, &old_todo);\n+\tif (!res)\n+\t\tres = todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo);\n+\tif (!res)\n+\t\tres = todo_list_check(&old_todo, &new_todo);\n+\tif (res)\n+\t\tfprintf(stderr, _(edit_todo_list_advice));\n+out:\n+\ttodo_list_release(&old_todo);\n+\ttodo_list_release(&new_todo);\n \n \treturn res;\n }\n@@ -4888,7 +4813,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \n \ttodo_list_release(&todo_list);\n \n-\tif (check_todo_list(r)) {\n+\tif (check_todo_list_from_file(r)) {\n \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n \t\treturn -1;\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex dc3d8f76f7..1de97f188d 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -64,12 +64,6 @@ struct replay_opts {\n };\n #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }\n \n-enum missing_commit_check_level {\n-\tMISSING_COMMIT_CHECK_IGNORE = 0,\n-\tMISSING_COMMIT_CHECK_WARN,\n-\tMISSING_COMMIT_CHECK_ERROR\n-};\n-\n int write_message(const void *buf, size_t len, const char *filename,\n \t\t  int append_eol);\n \n@@ -154,8 +148,7 @@ int sequencer_make_script(struct repository *r, FILE *out, int argc,\n \n int sequencer_add_exec_commands(struct repository *r, const char *command);\n int transform_todo_file(struct repository *r, unsigned flags);\n-enum missing_commit_check_level get_missing_commit_check_level(void);\n-int check_todo_list(struct repository *r);\n+int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, const char *cmd,\n-- \n2.20.1\n\n"},{"id":"367470","messageId":"20190123205821.27459-8-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"[PATCH v5 07/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-23T20:58:12Z","receivedAt":"2019-01-23T20:59:35Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors sequencer_add_exec_commands() to work on a todo_list to\navoid redundant reads and writes to the disk.\n\nInstead of inserting the `exec' commands between the other commands and\nre-parsing the buffer at the end, they are appended to the buffer once,\nand a new list of items is created.  Items from the old list are copied\nacross and new `exec' items are appended when necessary.  This\neliminates the need to reparse the buffer, but this also means we have\nto use todo_list_write_to_disk() to write the file.\n\ntodo_list_add_exec_commands() and sequencer_add_exec_commands() are\nmodified to take a string list instead of a string -- one item for each\ncommand.  This makes it easier to insert a new command to the todo list\nfor each command to execute.\n\nsequencer_add_exec_commands() still reads the todo list from the disk,\nas it is needed by rebase -p.\n\ncomplete_action() still uses sequencer_add_exec_commands() for now.\nThis will be changed in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nUnchanged since v4.\n\n builtin/rebase--interactive.c |  15 +++--\n sequencer.c                   | 110 +++++++++++++++++++++-------------\n sequencer.h                   |   5 +-\n 3 files changed, 82 insertions(+), 48 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex df19ccaeb9..53056ee713 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -65,7 +65,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \t\t\t\t const char *onto, const char *onto_name,\n \t\t\t\t const char *squash_onto, const char *head_name,\n \t\t\t\t const char *restrict_revision, char *raw_strategies,\n-\t\t\t\t const char *cmd, unsigned autosquash)\n+\t\t\t\t struct string_list *commands, unsigned autosquash)\n {\n \tint ret;\n \tconst char *head_hash = NULL;\n@@ -116,7 +116,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \t\tdiscard_cache();\n \t\tret = complete_action(the_repository, opts, flags,\n \t\t\t\t      shortrevisions, onto_name, onto,\n-\t\t\t\t      head_hash, cmd, autosquash);\n+\t\t\t\t      head_hash, commands, autosquash);\n \t}\n \n \tfree(revisions);\n@@ -139,6 +139,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \tconst char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,\n \t\t*squash_onto = NULL, *upstream = NULL, *head_name = NULL,\n \t\t*switch_to = NULL, *cmd = NULL;\n+\tstruct string_list commands = STRING_LIST_INIT_DUP;\n \tchar *raw_strategies = NULL;\n \tenum {\n \t\tNONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,\n@@ -221,6 +222,12 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\twarning(_(\"--[no-]rebase-cousins has no effect without \"\n \t\t\t  \"--rebase-merges\"));\n \n+\tif (cmd && *cmd) {\n+\t\tstring_list_split(&commands, cmd, '\\n', -1);\n+\t\tif (strlen(commands.items[commands.nr - 1].string) == 0)\n+\t\t\t--commands.nr;\n+\t}\n+\n \tswitch (command) {\n \tcase NONE:\n \t\tif (!onto && !upstream)\n@@ -228,7 +235,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \n \t\tret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,\n \t\t\t\t\t    onto_name, squash_onto, head_name, restrict_revision,\n-\t\t\t\t\t    raw_strategies, cmd, autosquash);\n+\t\t\t\t\t    raw_strategies, &commands, autosquash);\n \t\tbreak;\n \tcase SKIP: {\n \t\tstruct string_list merge_rr = STRING_LIST_INIT_DUP;\n@@ -262,7 +269,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = rearrange_squash(the_repository);\n \t\tbreak;\n \tcase ADD_EXEC:\n-\t\tret = sequencer_add_exec_commands(the_repository, cmd);\n+\t\tret = sequencer_add_exec_commands(the_repository, &commands);\n \t\tbreak;\n \tdefault:\n \t\tBUG(\"invalid command '%d'\", command);\ndiff --git a/sequencer.c b/sequencer.c\nindex 266f80d704..3a90b419d7 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4446,25 +4446,27 @@ int sequencer_make_script(struct repository *r, FILE *out,\n \treturn 0;\n }\n \n-/*\n- * Add commands after pick and (series of) squash/fixup commands\n- * in the todo list.\n- */\n-int sequencer_add_exec_commands(struct repository *r,\n-\t\t\t\tconst char *commands)\n+static void todo_list_add_exec_commands(struct todo_list *todo_list,\n+\t\t\t\t\tstruct string_list *commands)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf *buf = &todo_list.buf;\n-\tsize_t offset = 0, commands_len = strlen(commands);\n-\tint i, insert;\n+\tstruct strbuf *buf = &todo_list->buf;\n+\tsize_t base_offset = buf->len;\n+\tint i, insert, nr = 0, alloc = 0;\n+\tstruct todo_item *items = NULL, *base_items = NULL;\n \n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n+\tbase_items = xcalloc(commands->nr, sizeof(struct todo_item));\n+\tfor (i = 0; i < commands->nr; ++i) {\n+\t\tsize_t command_len = strlen(commands->items[i].string);\n \n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t\tstrbuf_addstr(buf, commands->items[i].string);\n+\t\tstrbuf_addch(buf, '\\n');\n+\n+\t\tbase_items[i].command = TODO_EXEC;\n+\t\tbase_items[i].offset_in_buf = base_offset;\n+\t\tbase_items[i].arg_offset = base_offset + strlen(\"exec \");\n+\t\tbase_items[i].arg_len = command_len - strlen(\"exec \");\n+\n+\t\tbase_offset += command_len + 1;\n \t}\n \n \t/*\n@@ -4473,38 +4475,62 @@ int sequencer_add_exec_commands(struct repository *r,\n \t * those chains if there are any.\n \t */\n \tinsert = -1;\n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tenum todo_command command = todo_list.items[i].command;\n-\n-\t\tif (insert >= 0) {\n-\t\t\t/* skip fixup/squash chains */\n-\t\t\tif (command == TODO_COMMENT)\n-\t\t\t\tcontinue;\n-\t\t\telse if (is_fixup(command)) {\n-\t\t\t\tinsert = i + 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tstrbuf_insert(buf,\n-\t\t\t\t      todo_list.items[insert].offset_in_buf +\n-\t\t\t\t      offset, commands, commands_len);\n-\t\t\toffset += commands_len;\n+\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\tenum todo_command command = todo_list->items[i].command;\n+\t\tif (insert >= 0 && command != TODO_COMMENT && !is_fixup(command)) {\n+\t\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n+\t\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n+\t\t\tnr += commands->nr;\n \t\t\tinsert = -1;\n \t\t}\n \n-\t\tif (command == TODO_PICK || command == TODO_MERGE)\n+\t\tALLOC_GROW(items, nr + 1, alloc);\n+\t\titems[nr++] = todo_list->items[i];\n+\n+\t\tif (command == TODO_PICK || command == TODO_MERGE || is_fixup(command))\n \t\t\tinsert = i + 1;\n \t}\n \n \t/* insert or append final <commands> */\n-\tif (insert >= 0 && insert < todo_list.nr)\n-\t\tstrbuf_insert(buf, todo_list.items[insert].offset_in_buf +\n-\t\t\t      offset, commands, commands_len);\n-\telse if (insert >= 0 || !offset)\n-\t\tstrbuf_add(buf, commands, commands_len);\n+\tif (insert >= 0 || nr == todo_list->nr) {\n+\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n+\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n+\t\tnr += commands->nr;\n+\t}\n+\n+\tfree(base_items);\n+\tFREE_AND_NULL(todo_list->items);\n+\ttodo_list->items = items;\n+\ttodo_list->nr = nr;\n+\ttodo_list->alloc = alloc;\n+}\n \n-\ti = write_message(buf->buf, buf->len, todo_file, 0);\n+/*\n+ * Add commands after pick and (series of) squash/fixup commands\n+ * in the todo list.\n+ */\n+int sequencer_add_exec_commands(struct repository *r,\n+\t\t\t\tstruct string_list *commands)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_add_exec_commands(&todo_list, commands);\n+\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n \ttodo_list_release(&todo_list);\n-\treturn i;\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n }\n \n static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n@@ -4735,7 +4761,7 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-\t\t    const char *onto, const char *orig_head, const char *cmd,\n+\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash)\n {\n \tconst char *shortonto, *todo_file = rebase_path_todo();\n@@ -4754,8 +4780,8 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tif (autosquash && rearrange_squash(r))\n \t\treturn -1;\n \n-\tif (cmd && *cmd)\n-\t\tsequencer_add_exec_commands(r, cmd);\n+\tif (commands->nr)\n+\t\tsequencer_add_exec_commands(r, commands);\n \n \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\ndiff --git a/sequencer.h b/sequencer.h\nindex 1de97f188d..e79f03e213 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -146,12 +146,13 @@ int sequencer_make_script(struct repository *r, FILE *out, int argc,\n \t\t\t  const char **argv,\n \t\t\t  unsigned flags);\n \n-int sequencer_add_exec_commands(struct repository *r, const char *command);\n+int sequencer_add_exec_commands(struct repository *r,\n+\t\t\t\tstruct string_list *commands);\n int transform_todo_file(struct repository *r, unsigned flags);\n int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-\t\t    const char *onto, const char *orig_head, const char *cmd,\n+\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash);\n int rearrange_squash(struct repository *r);\n \n-- \n2.20.1\n\n"},{"id":"367471","messageId":"20190123205821.27459-9-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"[PATCH v5 08/16] sequencer: refactor rearrange_squash() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-23T20:58:13Z","receivedAt":"2019-01-23T20:59:37Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors rearrange_squash() to work on a todo_list to avoid\nredundant reads and writes.  The function is renamed\ntodo_list_rearrange_squash().\n\nThe old version created a new buffer, which was directly written to the\ndisk.  This new version creates a new item list by just copying items\nfrom the old item list, without creating a new buffer.  This eliminates\nthe need to reparse the todo list, but this also means its buffer cannot\nbe directly written to the disk.\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, rearrange_squash_in_todo_file().\n\ncomplete_action() still uses rearrange_squash_in_todo_file() for now.\nThis will be changed in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nUnchanged since v4.\n\n builtin/rebase--interactive.c |  2 +-\n sequencer.c                   | 92 ++++++++++++++++++-----------------\n sequencer.h                   |  2 +-\n 3 files changed, 49 insertions(+), 47 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 53056ee713..4f0eae9239 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -266,7 +266,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = check_todo_list_from_file(the_repository);\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n-\t\tret = rearrange_squash(the_repository);\n+\t\tret = rearrange_squash_in_todo_file(the_repository);\n \t\tbreak;\n \tcase ADD_EXEC:\n \t\tret = sequencer_add_exec_commands(the_repository, &commands);\ndiff --git a/sequencer.c b/sequencer.c\nindex 3a90b419d7..11456be5cc 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4777,7 +4777,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t    write_message(\"noop\\n\", 5, todo_file, 0))\n \t\treturn -1;\n \n-\tif (autosquash && rearrange_squash(r))\n+\tif (autosquash && rearrange_squash_in_todo_file(r))\n \t\treturn -1;\n \n \tif (commands->nr)\n@@ -4883,21 +4883,13 @@ define_commit_slab(commit_todo_item, struct todo_item *);\n  * message will have to be retrieved from the commit (as the oneline in the\n  * script cannot be trusted) in order to normalize the autosquash arrangement.\n  */\n-int rearrange_squash(struct repository *r)\n+static int todo_list_rearrange_squash(struct todo_list *todo_list)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct hashmap subject2item;\n-\tint res = 0, rearranged = 0, *next, *tail, i;\n+\tint rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;\n \tchar **subjects;\n \tstruct commit_todo_item commit_todo;\n-\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tstruct todo_item *items = NULL;\n \n \tinit_commit_todo_item(&commit_todo);\n \t/*\n@@ -4910,13 +4902,13 @@ int rearrange_squash(struct repository *r)\n \t * be moved to appear after the i'th.\n \t */\n \thashmap_init(&subject2item, (hashmap_cmp_fn) subject2item_cmp,\n-\t\t     NULL, todo_list.nr);\n-\tALLOC_ARRAY(next, todo_list.nr);\n-\tALLOC_ARRAY(tail, todo_list.nr);\n-\tALLOC_ARRAY(subjects, todo_list.nr);\n-\tfor (i = 0; i < todo_list.nr; i++) {\n+\t\t     NULL, todo_list->nr);\n+\tALLOC_ARRAY(next, todo_list->nr);\n+\tALLOC_ARRAY(tail, todo_list->nr);\n+\tALLOC_ARRAY(subjects, todo_list->nr);\n+\tfor (i = 0; i < todo_list->nr; i++) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n-\t\tstruct todo_item *item = todo_list.items + i;\n+\t\tstruct todo_item *item = todo_list->items + i;\n \t\tconst char *commit_buffer, *subject, *p;\n \t\tsize_t subject_len;\n \t\tint i2 = -1;\n@@ -4929,7 +4921,6 @@ int rearrange_squash(struct repository *r)\n \t\t}\n \n \t\tif (is_fixup(item->command)) {\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\tclear_commit_todo_item(&commit_todo);\n \t\t\treturn error(_(\"the script was already rearranged.\"));\n \t\t}\n@@ -4964,7 +4955,7 @@ int rearrange_squash(struct repository *r)\n \t\t\t\t *commit_todo_item_at(&commit_todo, commit2))\n \t\t\t\t/* found by commit name */\n \t\t\t\ti2 = *commit_todo_item_at(&commit_todo, commit2)\n-\t\t\t\t\t- todo_list.items;\n+\t\t\t\t\t- todo_list->items;\n \t\t\telse {\n \t\t\t\t/* copy can be a prefix of the commit subject */\n \t\t\t\tfor (i2 = 0; i2 < i; i2++)\n@@ -4977,7 +4968,7 @@ int rearrange_squash(struct repository *r)\n \t\t}\n \t\tif (i2 >= 0) {\n \t\t\trearranged = 1;\n-\t\t\ttodo_list.items[i].command =\n+\t\t\ttodo_list->items[i].command =\n \t\t\t\tstarts_with(subject, \"fixup!\") ?\n \t\t\t\tTODO_FIXUP : TODO_SQUASH;\n \t\t\tif (next[i2] < 0)\n@@ -4995,10 +4986,8 @@ int rearrange_squash(struct repository *r)\n \t}\n \n \tif (rearranged) {\n-\t\tstruct strbuf buf = STRBUF_INIT;\n-\n-\t\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\t\tenum todo_command command = todo_list.items[i].command;\n+\t\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\t\tenum todo_command command = todo_list->items[i].command;\n \t\t\tint cur = i;\n \n \t\t\t/*\n@@ -5009,37 +4998,50 @@ int rearrange_squash(struct repository *r)\n \t\t\t\tcontinue;\n \n \t\t\twhile (cur >= 0) {\n-\t\t\t\tconst char *bol =\n-\t\t\t\t\tget_item_line(&todo_list, cur);\n-\t\t\t\tconst char *eol =\n-\t\t\t\t\tget_item_line(&todo_list, cur + 1);\n-\n-\t\t\t\t/* replace 'pick', by 'fixup' or 'squash' */\n-\t\t\t\tcommand = todo_list.items[cur].command;\n-\t\t\t\tif (is_fixup(command)) {\n-\t\t\t\t\tstrbuf_addstr(&buf,\n-\t\t\t\t\t\ttodo_command_info[command].str);\n-\t\t\t\t\tbol += strcspn(bol, \" \\t\");\n-\t\t\t\t}\n-\n-\t\t\t\tstrbuf_add(&buf, bol, eol - bol);\n-\n+\t\t\t\tALLOC_GROW(items, nr + 1, alloc);\n+\t\t\t\titems[nr++] = todo_list->items[cur];\n \t\t\t\tcur = next[cur];\n \t\t\t}\n \t\t}\n \n-\t\tres = rewrite_file(todo_file, buf.buf, buf.len);\n-\t\tstrbuf_release(&buf);\n+\t\tFREE_AND_NULL(todo_list->items);\n+\t\ttodo_list->items = items;\n+\t\ttodo_list->nr = nr;\n+\t\ttodo_list->alloc = alloc;\n \t}\n \n \tfree(next);\n \tfree(tail);\n-\tfor (i = 0; i < todo_list.nr; i++)\n+\tfor (i = 0; i < todo_list->nr; i++)\n \t\tfree(subjects[i]);\n \tfree(subjects);\n \thashmap_free(&subject2item, 1);\n-\ttodo_list_release(&todo_list);\n \n \tclear_commit_todo_item(&commit_todo);\n-\treturn res;\n+\n+\treturn 0;\n+}\n+\n+int rearrange_squash_in_todo_file(struct repository *r)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n+\n+\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n+\t\treturn -1;\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn -1;\n+\t}\n+\n+\tres = todo_list_rearrange_squash(&todo_list);\n+\tif (!res)\n+\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n+\n+\ttodo_list_release(&todo_list);\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n }\ndiff --git a/sequencer.h b/sequencer.h\nindex e79f03e213..c7bb38d6df 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -154,7 +154,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash);\n-int rearrange_squash(struct repository *r);\n+int rearrange_squash_in_todo_file(struct repository *r);\n \n extern const char sign_off_header[];\n \n-- \n2.20.1\n\n"},{"id":"367472","messageId":"20190123205821.27459-11-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"[PATCH v5 10/16] sequencer: change complete_action() to use the refactored functions","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-23T20:58:15Z","receivedAt":"2019-01-23T20:59:40Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"complete_action() used functions that read the todo-list file, made some\nchanges to it, and wrote it back to the disk.\n\nThe previous commits were dedicated to separate the part that deals with\nthe file from the actual logic of these functions.  Now that this is\ndone, we can call directly the \"logic\" functions to avoid useless file\naccess.\n\nThe parsing of the list has to be done by the caller.  If the buffer of\nthe todo list provided by the caller is empty, a `noop' command is\ndirectly added to the todo list, without touching the buffer.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nUnchanged since v4.\n\n builtin/rebase--interactive.c | 20 +++------\n sequencer.c                   | 81 +++++++++++++++--------------------\n sequencer.h                   |  2 +-\n 3 files changed, 42 insertions(+), 61 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 92026739c9..2dbf8fc08b 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -71,7 +71,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tconst char *head_hash = NULL;\n \tchar *revisions = NULL, *shortrevisions = NULL;\n \tstruct argv_array make_script_args = ARGV_ARRAY_INIT;\n-\tFILE *todo_list_file;\n \tstruct todo_list todo_list = TODO_LIST_INIT;\n \n \tif (prepare_branch_to_be_rebased(opts, switch_to))\n@@ -94,14 +93,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (!upstream && squash_onto)\n \t\twrite_file(path_squash_onto(), \"%s\\n\", squash_onto);\n \n-\ttodo_list_file = fopen(rebase_path_todo(), \"w\");\n-\tif (!todo_list_file) {\n-\t\tfree(revisions);\n-\t\tfree(shortrevisions);\n-\n-\t\treturn error_errno(_(\"could not open %s\"), rebase_path_todo());\n-\t}\n-\n \targv_array_pushl(&make_script_args, \"\", revisions, NULL);\n \tif (restrict_revision)\n \t\targv_array_push(&make_script_args, restrict_revision);\n@@ -109,16 +100,17 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tret = sequencer_make_script(the_repository, &todo_list.buf,\n \t\t\t\t    make_script_args.argc, make_script_args.argv,\n \t\t\t\t    flags);\n-\tfputs(todo_list.buf.buf, todo_list_file);\n-\tfclose(todo_list_file);\n \n \tif (ret)\n \t\terror(_(\"could not generate todo list\"));\n \telse {\n \t\tdiscard_cache();\n-\t\tret = complete_action(the_repository, opts, flags,\n-\t\t\t\t      shortrevisions, onto_name, onto,\n-\t\t\t\t      head_hash, commands, autosquash);\n+\t\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n+\t\t\t\t\t\t&todo_list))\n+\t\t\tBUG(\"unusable todo list\");\n+\n+\t\tret = complete_action(the_repository, opts, flags, shortrevisions, onto_name,\n+\t\t\t\t      onto, head_hash, commands, autosquash, &todo_list);\n \t}\n \n \tfree(revisions);\ndiff --git a/sequencer.c b/sequencer.c\nindex f1c62c5960..2a43ca685b 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4754,100 +4754,89 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \treturn 0;\n }\n \n+static int todo_list_rearrange_squash(struct todo_list *todo_list);\n+\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n-\t\t    unsigned autosquash)\n+\t\t    unsigned autosquash, struct todo_list *todo_list)\n {\n \tconst char *shortonto, *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf *buf = &(todo_list.buf);\n+\tstruct todo_list new_todo = TODO_LIST_INIT;\n+\tstruct strbuf *buf = &todo_list->buf;\n \tstruct object_id oid;\n-\tstruct stat st;\n \n \tget_oid(onto, &oid);\n \tshortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);\n \n-\tif (!lstat(todo_file, &st) && st.st_size == 0 &&\n-\t    write_message(\"noop\\n\", 5, todo_file, 0))\n-\t\treturn -1;\n+\tif (buf->len == 0) {\n+\t\tstruct todo_item *item = append_new_todo(todo_list);\n+\t\titem->command = TODO_NOOP;\n+\t\titem->commit = NULL;\n+\t\titem->arg_len = item->arg_offset = item->flags = item->offset_in_buf = 0;\n+\t}\n \n-\tif (autosquash && rearrange_squash_in_todo_file(r))\n+\tif (autosquash && todo_list_rearrange_squash(todo_list))\n \t\treturn -1;\n \n \tif (commands->nr)\n-\t\tsequencer_add_exec_commands(r, commands);\n+\t\ttodo_list_add_exec_commands(todo_list, commands);\n \n-\tif (strbuf_read_file(buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(r, buf->buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tif (count_commands(&todo_list) == 0) {\n+\tif (count_commands(todo_list) == 0) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n \n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\tstrbuf_addch(buf, '\\n');\n-\tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n-\t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n-\t\t\t\t      count_commands(&todo_list)),\n-\t\t\t      shortrevisions, shortonto, count_commands(&todo_list));\n-\tappend_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);\n-\n-\tif (write_message(buf->buf, buf->len, todo_file, 0)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tif (todo_list_write_to_file(r, todo_list, todo_file,\n+\t\t\t\t    shortrevisions, shortonto, -1,\n+\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n \n \tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n \t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n \t\t\t     rebase_path_todo_backup());\n \n-\tif (transform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS))\n-\t\treturn error(_(\"could not transform the todo list\"));\n-\n-\tstrbuf_reset(buf);\n-\n-\tif (launch_sequence_editor(todo_file, buf, NULL)) {\n+\tif (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n \n \t\treturn -1;\n \t}\n \n-\tstrbuf_stripspace(buf, 1);\n-\tif (buf->len == 0) {\n+\tstrbuf_stripspace(&new_todo.buf, 1);\n+\tif (new_todo.buf.len == 0) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n+\t\ttodo_list_release(&new_todo);\n \n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\n-\tif (check_todo_list_from_file(r)) {\n+\tif (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) ||\n+\t    todo_list_check(todo_list, &new_todo)) {\n+\t\tfprintf(stderr, _(edit_todo_list_advice));\n \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n+\t\ttodo_list_release(&new_todo);\n+\n \t\treturn -1;\n \t}\n \n-\tif (transform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS)))\n-\t\treturn error(_(\"could not transform the todo list\"));\n+\tif (todo_list_write_to_file(r, &new_todo, todo_file, NULL, NULL, -1,\n+\t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS))) {\n+\t\ttodo_list_release(&new_todo);\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_release(&new_todo);\n \n \tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\n \t\treturn error(_(\"could not skip unnecessary pick commands\"));\n \n \tif (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))\n \t\treturn -1;\n-;\n+\n \tif (require_clean_work_tree(r, \"rebase\", \"\", 1, 1))\n \t\treturn -1;\n \ndiff --git a/sequencer.h b/sequencer.h\nindex 9b8edd7df6..0c30e43f0a 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -152,7 +152,7 @@ int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n-\t\t    unsigned autosquash);\n+\t\t    unsigned autosquash, struct todo_list *todo_list);\n int rearrange_squash_in_todo_file(struct repository *r);\n \n extern const char sign_off_header[];\n-- \n2.20.1\n\n"},{"id":"367473","messageId":"20190123205821.27459-12-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"[PATCH v5 11/16] sequencer: refactor skip_unnecessary_picks() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-23T20:58:16Z","receivedAt":"2019-01-23T20:59:41Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors skip_unnecessary_picks() to work on a todo_list.  As this\nfunction is only called by complete_action() (and thus is not used by\nrebase -p), the file-handling logic is completely dropped here.\n\nInstead of truncating the todo list’s buffer, the items are moved to\nthe beginning of the list, eliminating the need to reparse the list.\nThis also means its buffer cannot be directly written to the disk.\n\nrewrite_file() is then removed, as it is now unused.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nUnchanged since v4.\n\n sequencer.c | 78 ++++++++++++-----------------------------------------\n 1 file changed, 17 insertions(+), 61 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 2a43ca685b..a817afffa9 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4661,52 +4661,22 @@ int check_todo_list_from_file(struct repository *r)\n \treturn res;\n }\n \n-static int rewrite_file(const char *path, const char *buf, size_t len)\n-{\n-\tint rc = 0;\n-\tint fd = open(path, O_WRONLY | O_TRUNC);\n-\tif (fd < 0)\n-\t\treturn error_errno(_(\"could not open '%s' for writing\"), path);\n-\tif (write_in_full(fd, buf, len) < 0)\n-\t\trc = error_errno(_(\"could not write to '%s'\"), path);\n-\tif (close(fd) && !rc)\n-\t\trc = error_errno(_(\"could not close '%s'\"), path);\n-\treturn rc;\n-}\n-\n /* skip picking commits whose parents are unchanged */\n-static int skip_unnecessary_picks(struct repository *r, struct object_id *output_oid)\n+static int skip_unnecessary_picks(struct repository *r,\n+\t\t\t\t  struct todo_list *todo_list,\n+\t\t\t\t  struct object_id *output_oid)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct object_id *parent_oid;\n-\tint fd, i;\n-\n-\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n-\t\treturn error(_(\"could not read 'onto'\"));\n-\tif (get_oid(buf.buf, output_oid)) {\n-\t\tstrbuf_release(&buf);\n-\t\treturn error(_(\"need a HEAD to fixup\"));\n-\t}\n-\tstrbuf_release(&buf);\n-\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tint i;\n \n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tstruct todo_item *item = todo_list.items + i;\n+\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\tstruct todo_item *item = todo_list->items + i;\n \n \t\tif (item->command >= TODO_NOOP)\n \t\t\tcontinue;\n \t\tif (item->command != TODO_PICK)\n \t\t\tbreak;\n \t\tif (parse_commit(item->commit)) {\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\treturn error(_(\"could not parse commit '%s'\"),\n \t\t\t\toid_to_hex(&item->commit->object.oid));\n \t\t}\n@@ -4720,37 +4690,21 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \t\toidcpy(output_oid, &item->commit->object.oid);\n \t}\n \tif (i > 0) {\n-\t\tint offset = get_item_line_offset(&todo_list, i);\n \t\tconst char *done_path = rebase_path_done();\n \n-\t\tfd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);\n-\t\tif (fd < 0) {\n-\t\t\terror_errno(_(\"could not open '%s' for writing\"),\n-\t\t\t\t    done_path);\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\treturn -1;\n-\t\t}\n-\t\tif (write_in_full(fd, todo_list.buf.buf, offset) < 0) {\n+\t\tif (todo_list_write_to_file(r, todo_list, done_path, NULL, NULL, i, 0)) {\n \t\t\terror_errno(_(\"could not write to '%s'\"), done_path);\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\tclose(fd);\n \t\t\treturn -1;\n \t\t}\n-\t\tclose(fd);\n \n-\t\tif (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,\n-\t\t\t\t todo_list.buf.len - offset) < 0) {\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\treturn -1;\n-\t\t}\n+\t\tMOVE_ARRAY(todo_list->items, todo_list->items + i, todo_list->nr - i);\n+\t\ttodo_list->nr -= i;\n+\t\ttodo_list->current = 0;\n \n-\t\ttodo_list.current = i;\n-\t\tif (is_fixup(peek_command(&todo_list, 0)))\n-\t\t\trecord_in_rewritten(output_oid, peek_command(&todo_list, 0));\n+\t\tif (is_fixup(peek_command(todo_list, 0)))\n+\t\t\trecord_in_rewritten(output_oid, peek_command(todo_list, 0));\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\n \treturn 0;\n }\n \n@@ -4823,6 +4777,11 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn -1;\n \t}\n \n+\tif (opts->allow_ff && skip_unnecessary_picks(r, &new_todo, &oid)) {\n+\t\ttodo_list_release(&new_todo);\n+\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n+\t}\n+\n \tif (todo_list_write_to_file(r, &new_todo, todo_file, NULL, NULL, -1,\n \t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS))) {\n \t\ttodo_list_release(&new_todo);\n@@ -4831,9 +4790,6 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \n \ttodo_list_release(&new_todo);\n \n-\tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\n-\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n-\n \tif (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))\n \t\treturn -1;\n \n-- \n2.20.1\n\n"},{"id":"367474","messageId":"20190123205821.27459-10-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"[PATCH v5 09/16] sequencer: make sequencer_make_script() write its script to a strbuf","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-23T20:58:14Z","receivedAt":"2019-01-23T20:59:42Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This makes sequencer_make_script() write its script to a strbuf (ie. the\nbuffer of a todo_list) instead of a FILE.  This reduce the amount of\nread/write made by rebase interactive.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nUnchanged since v4.\n\n builtin/rebase--interactive.c | 13 ++++++-----\n sequencer.c                   | 41 +++++++++++++++--------------------\n sequencer.h                   |  5 ++---\n 3 files changed, 28 insertions(+), 31 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 4f0eae9239..92026739c9 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -71,7 +71,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tconst char *head_hash = NULL;\n \tchar *revisions = NULL, *shortrevisions = NULL;\n \tstruct argv_array make_script_args = ARGV_ARRAY_INIT;\n-\tFILE *todo_list;\n+\tFILE *todo_list_file;\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n \n \tif (prepare_branch_to_be_rebased(opts, switch_to))\n \t\treturn -1;\n@@ -93,8 +94,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (!upstream && squash_onto)\n \t\twrite_file(path_squash_onto(), \"%s\\n\", squash_onto);\n \n-\ttodo_list = fopen(rebase_path_todo(), \"w\");\n-\tif (!todo_list) {\n+\ttodo_list_file = fopen(rebase_path_todo(), \"w\");\n+\tif (!todo_list_file) {\n \t\tfree(revisions);\n \t\tfree(shortrevisions);\n \n@@ -105,10 +106,11 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (restrict_revision)\n \t\targv_array_push(&make_script_args, restrict_revision);\n \n-\tret = sequencer_make_script(the_repository, todo_list,\n+\tret = sequencer_make_script(the_repository, &todo_list.buf,\n \t\t\t\t    make_script_args.argc, make_script_args.argv,\n \t\t\t\t    flags);\n-\tfclose(todo_list);\n+\tfputs(todo_list.buf.buf, todo_list_file);\n+\tfclose(todo_list_file);\n \n \tif (ret)\n \t\terror(_(\"could not generate todo list\"));\n@@ -121,6 +123,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \n \tfree(revisions);\n \tfree(shortrevisions);\n+\ttodo_list_release(&todo_list);\n \targv_array_clear(&make_script_args);\n \n \treturn ret;\ndiff --git a/sequencer.c b/sequencer.c\nindex 11456be5cc..f1c62c5960 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4160,7 +4160,7 @@ static const char *label_oid(struct object_id *oid, const char *label,\n }\n \n static int make_script_with_merges(struct pretty_print_context *pp,\n-\t\t\t\t   struct rev_info *revs, FILE *out,\n+\t\t\t\t   struct rev_info *revs, struct strbuf *out,\n \t\t\t\t   unsigned flags)\n {\n \tint keep_empty = flags & TODO_LIST_KEEP_EMPTY;\n@@ -4305,7 +4305,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t * gathering commits not yet shown, reversing the list on the fly,\n \t * then outputting that list (labeling revisions as needed).\n \t */\n-\tfprintf(out, \"%s onto\\n\", cmd_label);\n+\tstrbuf_addf(out, \"%s onto\\n\", cmd_label);\n \tfor (iter = tips; iter; iter = iter->next) {\n \t\tstruct commit_list *list = NULL, *iter2;\n \n@@ -4315,9 +4315,9 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\tentry = oidmap_get(&state.commit2label, &commit->object.oid);\n \n \t\tif (entry)\n-\t\t\tfprintf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n+\t\t\tstrbuf_addf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n \t\telse\n-\t\t\tfprintf(out, \"\\n\");\n+\t\t\tstrbuf_addch(out, '\\n');\n \n \t\twhile (oidset_contains(&interesting, &commit->object.oid) &&\n \t\t       !oidset_contains(&shown, &commit->object.oid)) {\n@@ -4330,8 +4330,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t}\n \n \t\tif (!commit)\n-\t\t\tfprintf(out, \"%s %s\\n\", cmd_reset,\n-\t\t\t\trebase_cousins ? \"onto\" : \"[new root]\");\n+\t\t\tstrbuf_addf(out, \"%s %s\\n\", cmd_reset,\n+\t\t\t\t    rebase_cousins ? \"onto\" : \"[new root]\");\n \t\telse {\n \t\t\tconst char *to = NULL;\n \n@@ -4344,12 +4344,12 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t\t\t\t       &state);\n \n \t\t\tif (!to || !strcmp(to, \"onto\"))\n-\t\t\t\tfprintf(out, \"%s onto\\n\", cmd_reset);\n+\t\t\t\tstrbuf_addf(out, \"%s onto\\n\", cmd_reset);\n \t\t\telse {\n \t\t\t\tstrbuf_reset(&oneline);\n \t\t\t\tpretty_print_commit(pp, commit, &oneline);\n-\t\t\t\tfprintf(out, \"%s %s # %s\\n\",\n-\t\t\t\t\tcmd_reset, to, oneline.buf);\n+\t\t\t\tstrbuf_addf(out, \"%s %s # %s\\n\",\n+\t\t\t\t\t    cmd_reset, to, oneline.buf);\n \t\t\t}\n \t\t}\n \n@@ -4358,11 +4358,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t\tentry = oidmap_get(&commit2todo, oid);\n \t\t\t/* only show if not already upstream */\n \t\t\tif (entry)\n-\t\t\t\tfprintf(out, \"%s\\n\", entry->string);\n+\t\t\t\tstrbuf_addf(out, \"%s\\n\", entry->string);\n \t\t\tentry = oidmap_get(&state.commit2label, oid);\n \t\t\tif (entry)\n-\t\t\t\tfprintf(out, \"%s %s\\n\",\n-\t\t\t\t\tcmd_label, entry->string);\n+\t\t\t\tstrbuf_addf(out, \"%s %s\\n\",\n+\t\t\t\t\t    cmd_label, entry->string);\n \t\t\toidset_insert(&shown, oid);\n \t\t}\n \n@@ -4384,13 +4384,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \treturn 0;\n }\n \n-int sequencer_make_script(struct repository *r, FILE *out,\n-\t\t\t  int argc, const char **argv,\n-\t\t\t  unsigned flags)\n+int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n+\t\t\t  const char **argv, unsigned flags)\n {\n \tchar *format = NULL;\n \tstruct pretty_print_context pp = {0};\n-\tstruct strbuf buf = STRBUF_INIT;\n \tstruct rev_info revs;\n \tstruct commit *commit;\n \tint keep_empty = flags & TODO_LIST_KEEP_EMPTY;\n@@ -4433,16 +4431,13 @@ int sequencer_make_script(struct repository *r, FILE *out,\n \n \t\tif (!is_empty && (commit->object.flags & PATCHSAME))\n \t\t\tcontinue;\n-\t\tstrbuf_reset(&buf);\n \t\tif (!keep_empty && is_empty)\n-\t\t\tstrbuf_addf(&buf, \"%c \", comment_line_char);\n-\t\tstrbuf_addf(&buf, \"%s %s \", insn,\n+\t\t\tstrbuf_addf(out, \"%c \", comment_line_char);\n+\t\tstrbuf_addf(out, \"%s %s \", insn,\n \t\t\t    oid_to_hex(&commit->object.oid));\n-\t\tpretty_print_commit(&pp, commit, &buf);\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t\tfputs(buf.buf, out);\n+\t\tpretty_print_commit(&pp, commit, out);\n+\t\tstrbuf_addch(out, '\\n');\n \t}\n-\tstrbuf_release(&buf);\n \treturn 0;\n }\n \ndiff --git a/sequencer.h b/sequencer.h\nindex c7bb38d6df..9b8edd7df6 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -142,9 +142,8 @@ int sequencer_remove_state(struct replay_opts *opts);\n #define TODO_LIST_REBASE_COUSINS (1U << 4)\n #define TODO_LIST_APPEND_TODO_HELP (1U << 5)\n \n-int sequencer_make_script(struct repository *r, FILE *out, int argc,\n-\t\t\t  const char **argv,\n-\t\t\t  unsigned flags);\n+int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n+\t\t\t  const char **argv, unsigned flags);\n \n int sequencer_add_exec_commands(struct repository *r,\n \t\t\t\tstruct string_list *commands);\n-- \n2.20.1\n\n"},{"id":"367475","messageId":"20190123205821.27459-14-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"[PATCH v5 13/16] rebase-interactive: append_todo_help() changes","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-23T20:58:18Z","receivedAt":"2019-01-23T20:59:46Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This moves the writing of the comment \"Rebase $shortrevisions onto\n$shortonto ($command_count commands)\" from todo_list_write_to_file() to\nappend_todo_help().\n\nshortrevisions, shortonto, and command_count are passed as parameters to\nappend_todo_help().\n\nDuring the initial edit of the todo list, shortrevisions and shortonto\nare not NULL.  Therefore, if shortrevisions or shortonto is NULL, then\nedit_todo would be true, otherwise it would be false.  Thus, edit_todo\nis removed from the parameters of append_todo_help().\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n rebase-interactive.c | 12 +++++++++++-\n rebase-interactive.h |  3 ++-\n sequencer.c          | 17 ++++-------------\n 3 files changed, 17 insertions(+), 15 deletions(-)\n\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex d396ecc599..807f8370db 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -28,7 +28,8 @@ static enum missing_commit_check_level get_missing_commit_check_level(void)\n \treturn MISSING_COMMIT_CHECK_IGNORE;\n }\n \n-void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n+void append_todo_help(unsigned keep_empty, int command_count,\n+\t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf)\n {\n \tconst char *msg = _(\"\\nCommands:\\n\"\n@@ -48,6 +49,15 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \".       specified). Use -c <commit> to reword the commit message.\\n\"\n \"\\n\"\n \"These lines can be re-ordered; they are executed from top to bottom.\\n\");\n+\tunsigned edit_todo = !(shortrevisions && shortonto);\n+\n+\tif (!edit_todo) {\n+\t\tstrbuf_addch(buf, '\\n');\n+\t\tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n+\t\t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n+\t\t\t\t\t      command_count),\n+\t\t\t\t      shortrevisions, shortonto, command_count);\n+\t}\n \n \tstrbuf_add_commented_lines(buf, msg, strlen(msg));\n \ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 187b5032d6..0e5925e3aa 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -5,7 +5,8 @@ struct strbuf;\n struct repository;\n struct todo_list;\n \n-void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n+void append_todo_help(unsigned keep_empty, int command_count,\n+\t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf);\n int edit_todo_list(struct repository *r, unsigned flags);\n int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\ndiff --git a/sequencer.c b/sequencer.c\nindex d8d045067c..92de982bc4 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4580,22 +4580,13 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n \t\t\t    const char *file, const char *shortrevisions,\n \t\t\t    const char *shortonto, int num, unsigned flags)\n {\n-\tint edit_todo = !(shortrevisions && shortonto), res;\n+\tint res;\n \tstruct strbuf buf = STRBUF_INIT;\n \n \ttodo_list_to_strbuf(r, todo_list, &buf, num, flags);\n-\n-\tif (flags & TODO_LIST_APPEND_TODO_HELP) {\n-\t\tint command_count = count_commands(todo_list);\n-\t\tif (!edit_todo) {\n-\t\t\tstrbuf_addch(&buf, '\\n');\n-\t\t\tstrbuf_commented_addf(&buf, Q_(\"Rebase %s onto %s (%d command)\",\n-\t\t\t\t\t\t       \"Rebase %s onto %s (%d commands)\",\n-\t\t\t\t\t\t       command_count),\n-\t\t\t\t\t      shortrevisions, shortonto, command_count);\n-\t\t}\n-\t\tappend_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, &buf);\n-\t}\n+\tif (flags & TODO_LIST_APPEND_TODO_HELP)\n+\t\tappend_todo_help(flags & TODO_LIST_KEEP_EMPTY, count_commands(todo_list),\n+\t\t\t\t shortrevisions, shortonto, &buf);\n \n \tres = write_message(buf.buf, buf.len, file, 0);\n \tstrbuf_release(&buf);\n-- \n2.20.1\n\n"},{"id":"367476","messageId":"20190123205821.27459-15-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"[PATCH v5 14/16] rebase-interactive: rewrite edit_todo_list() to handle the initial edit","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-23T20:58:19Z","receivedAt":"2019-01-23T20:59:47Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"edit_todo_list() is changed to work on a todo_list, and to handle the\ninitial edition of the todo list (ie. making a backup of the todo\nlist).\n\nIt does not check for dropped commits yet, as todo_list_check() does not\ntake the commits that have already been processed by the rebase (ie. the\ntodo list is edited in the middle of a rebase session).\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nUnchanged since v4.\n\n builtin/rebase--interactive.c | 24 +++++++++++++++++-\n rebase-interactive.c          | 48 ++++++++++++++++++-----------------\n rebase-interactive.h          |  4 ++-\n sequencer.c                   |  3 +--\n sequencer.h                   |  1 +\n 5 files changed, 53 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 2dbf8fc08b..645ac587f7 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -13,6 +13,28 @@ static GIT_PATH_FUNC(path_state_dir, \"rebase-merge/\")\n static GIT_PATH_FUNC(path_squash_onto, \"rebase-merge/squash-onto\")\n static GIT_PATH_FUNC(path_interactive, \"rebase-merge/interactive\")\n \n+static int edit_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT,\n+\t\tnew_todo = TODO_LIST_INIT;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tstrbuf_stripspace(&todo_list.buf, 1);\n+\tif (!edit_todo_list(the_repository, &todo_list,\n+\t\t\t    &new_todo, NULL, NULL, flags) &&\n+\t    todo_list_write_to_file(the_repository, &new_todo, todo_file, NULL, NULL,\n+\t\t\t\t    -1, flags & ~(TODO_LIST_SHORTEN_IDS)) < 0)\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n+\n+\ttodo_list_release(&todo_list);\n+\ttodo_list_release(&new_todo);\n+\n+\treturn 0;\n+}\n+\n static int get_revision_ranges(const char *upstream, const char *onto,\n \t\t\t       const char **head_hash,\n \t\t\t       char **revisions, char **shortrevisions)\n@@ -242,7 +264,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tbreak;\n \t}\n \tcase EDIT_TODO:\n-\t\tret = edit_todo_list(the_repository, flags);\n+\t\tret = edit_todo_file(flags);\n \t\tbreak;\n \tcase SHOW_CURRENT_PATCH: {\n \t\tstruct child_process cmd = CHILD_PROCESS_INIT;\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 807f8370db..3301efbe52 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -87,35 +87,37 @@ void append_todo_help(unsigned keep_empty, int command_count,\n \t}\n }\n \n-int edit_todo_list(struct repository *r, unsigned flags)\n+int edit_todo_list(struct repository *r, struct todo_list *todo_list,\n+\t\t   struct todo_list *new_todo, const char *shortrevisions,\n+\t\t   const char *shortonto, unsigned flags)\n {\n \tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res = 0;\n-\n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tstrbuf_stripspace(&todo_list.buf, 1);\n-\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n-\tif (todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n-\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n+\tunsigned initial = shortrevisions && shortonto;\n+\n+\tif (initial) {\n+\t\ttodo_list_write_to_file(r, todo_list, todo_file, shortrevisions, shortonto,\n+\t\t\t\t\t-1, flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP);\n+\n+\t\tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n+\t\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n+\t\t\t\t     rebase_path_todo_backup());\n+\t} else {\n+\t\ttodo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n+\t\ttodo_list_write_to_file(r, todo_list, todo_file, NULL, NULL, -1,\n+\t\t\t\t\tflags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP);\n \t}\n \n-\tstrbuf_reset(&todo_list.buf);\n-\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tif (launch_sequence_editor(todo_file, &new_todo->buf, NULL))\n+\t\treturn -2;\n \n-\tif (!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list))\n-\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n-\t\t\t\t\t      flags & ~(TODO_LIST_SHORTEN_IDS));\n+\tstrbuf_stripspace(&new_todo->buf, 1);\n+\tif (initial && new_todo->buf.len == 0)\n+\t\treturn -3;\n \n-\ttodo_list_release(&todo_list);\n-\treturn res;\n+\tif (!initial)\n+\t\ttodo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo);\n+\n+\treturn 0;\n }\n \n define_commit_slab(commit_seen, unsigned char);\ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 0e5925e3aa..44dbb06311 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -8,7 +8,9 @@ struct todo_list;\n void append_todo_help(unsigned keep_empty, int command_count,\n \t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf);\n-int edit_todo_list(struct repository *r, unsigned flags);\n+int edit_todo_list(struct repository *r, struct todo_list *todo_list,\n+\t\t   struct todo_list *new_todo, const char *shortrevisions,\n+\t\t   const char *shortonto, unsigned flags);\n int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n \n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex 92de982bc4..8f47f0cf39 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -55,8 +55,7 @@ static GIT_PATH_FUNC(rebase_path, \"rebase-merge\")\n  * file and written to the tail of 'done'.\n  */\n GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n-static GIT_PATH_FUNC(rebase_path_todo_backup,\n-\t\t     \"rebase-merge/git-rebase-todo.backup\")\n+GIT_PATH_FUNC(rebase_path_todo_backup, \"rebase-merge/git-rebase-todo.backup\")\n \n /*\n  * The rebase command lines that have already been processed. A line\ndiff --git a/sequencer.h b/sequencer.h\nindex c5bee8124c..68acab980b 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -10,6 +10,7 @@ struct repository;\n const char *git_path_commit_editmsg(void);\n const char *git_path_seq_dir(void);\n const char *rebase_path_todo(void);\n+const char *rebase_path_todo_backup(void);\n \n #define APPEND_SIGNOFF_DEDUP (1u << 0)\n \n-- \n2.20.1\n\n"},{"id":"367477","messageId":"20190123205821.27459-16-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"[PATCH v5 15/16] sequencer: use edit_todo_list() in complete_action()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-23T20:58:20Z","receivedAt":"2019-01-23T20:59:48Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This changes complete_action() to use edit_todo_list(), now that it can\nhandle the initial edit of the todo list.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nUnchanged since v4.\n\n sequencer.c | 21 +++++++--------------\n 1 file changed, 7 insertions(+), 14 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 8f47f0cf39..21b04e0642 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4709,6 +4709,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tstruct todo_list new_todo = TODO_LIST_INIT;\n \tstruct strbuf *buf = &todo_list->buf;\n \tstruct object_id oid;\n+\tint res;\n \n \tget_oid(onto, &oid);\n \tshortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);\n@@ -4733,24 +4734,16 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\tif (todo_list_write_to_file(r, todo_list, todo_file,\n-\t\t\t\t    shortrevisions, shortonto, -1,\n-\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))\n-\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n-\n-\tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n-\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n-\t\t\t     rebase_path_todo_backup());\n-\n-\tif (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {\n+\tres = edit_todo_list(r, todo_list, &new_todo, shortrevisions,\n+\t\t\t     shortonto, flags);\n+\tif (res == -1)\n+\t\treturn -1;\n+\telse if (res == -2) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n \n \t\treturn -1;\n-\t}\n-\n-\tstrbuf_stripspace(&new_todo.buf, 1);\n-\tif (new_todo.buf.len == 0) {\n+\t} else if (res == -3) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n \t\ttodo_list_release(&new_todo);\n-- \n2.20.1\n\n"},{"id":"367478","messageId":"20190123205821.27459-17-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"[PATCH v5 16/16] rebase--interactive: move transform_todo_file() to rebase--interactive.c","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-23T20:58:21Z","receivedAt":"2019-01-23T20:59:50Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"As transform_todo_file() is only needed inside of rebase--interactive.c,\nit is moved there from sequencer.c.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nUnchanged since v4.\n\n builtin/rebase--interactive.c | 26 +++++++++++++++++++++++++-\n sequencer.c                   | 23 -----------------------\n sequencer.h                   |  1 -\n 3 files changed, 25 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 645ac587f7..7f1e88a087 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -35,6 +35,30 @@ static int edit_todo_file(unsigned flags)\n \treturn 0;\n }\n \n+static int transform_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n+\t\t\t\t\t&todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\tres = todo_list_write_to_file(the_repository, &todo_list, todo_file,\n+\t\t\t\t      NULL, NULL, -1, flags);\n+\ttodo_list_release(&todo_list);\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n+}\n+\n static int get_revision_ranges(const char *upstream, const char *onto,\n \t\t\t       const char **head_hash,\n \t\t\t       char **revisions, char **shortrevisions)\n@@ -277,7 +301,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t}\n \tcase SHORTEN_OIDS:\n \tcase EXPAND_OIDS:\n-\t\tret = transform_todo_file(the_repository, flags);\n+\t\tret = transform_todo_file(flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n \t\tret = check_todo_list_from_file(the_repository);\ndiff --git a/sequencer.c b/sequencer.c\nindex 21b04e0642..5239700efc 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4593,29 +4593,6 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n \treturn res;\n }\n \n-int transform_todo_file(struct repository *r, unsigned flags)\n-{\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res;\n-\n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tres = todo_list_write_to_file(r, &todo_list, todo_file,\n-\t\t\t\t      NULL, NULL, -1, flags);\n-\ttodo_list_release(&todo_list);\n-\n-\tif (res)\n-\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n-\treturn 0;\n-}\n-\n static const char edit_todo_list_advice[] =\n N_(\"You can fix this with 'git rebase --edit-todo' \"\n \"and then run 'git rebase --continue'.\\n\"\ndiff --git a/sequencer.h b/sequencer.h\nindex 68acab980b..11afd47aa9 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -145,7 +145,6 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \n int sequencer_add_exec_commands(struct repository *r,\n \t\t\t\tstruct string_list *commands);\n-int transform_todo_file(struct repository *r, unsigned flags);\n int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-- \n2.20.1\n\n"},{"id":"367479","messageId":"20190123205821.27459-13-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"[PATCH v5 12/16] rebase-interactive: use todo_list_write_to_file() in edit_todo_list()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-23T20:58:17Z","receivedAt":"2019-01-23T20:59:50Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Just like complete_action(), edit_todo_list() used a\nfunction (transform_todo_file()) that read the todo list from the disk\nand wrote it back, resulting in useless disk accesses.\n\nThis changes edit_todo_list() to call directly todo_list_write_to_file()\ninstead.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nSquashed <5440ddf5-0b80-3d00-7daf-133a8611efa8@ramsayjones.plus.com>\nfrom Ramsay Jones.\n\n rebase-interactive.c | 38 ++++++++++++++++----------------------\n sequencer.c          |  4 ++--\n sequencer.h          |  3 ---\n 3 files changed, 18 insertions(+), 27 deletions(-)\n\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex dfa6dd530f..d396ecc599 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -79,39 +79,33 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \n int edit_todo_list(struct repository *r, unsigned flags)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n \tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n \n-\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n-\tstrbuf_stripspace(&buf, 1);\n-\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n-\t\tstrbuf_release(&buf);\n+\tstrbuf_stripspace(&todo_list.buf, 1);\n+\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n+\tif (todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n+\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP)) {\n+\t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n \n-\tstrbuf_release(&buf);\n-\n-\ttransform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS);\n-\n-\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tappend_todo_help(1, 0, &buf);\n-\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n-\t\tstrbuf_release(&buf);\n+\tstrbuf_reset(&todo_list.buf);\n+\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n+\t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n \n-\tstrbuf_release(&buf);\n+\tif (!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list))\n+\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n+\t\t\t\t\t      flags & ~(TODO_LIST_SHORTEN_IDS));\n \n-\tif (launch_sequence_editor(todo_file, NULL, NULL))\n-\t\treturn -1;\n-\n-\ttransform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS));\n-\n-\treturn 0;\n+\ttodo_list_release(&todo_list);\n+\treturn res;\n }\n \n define_commit_slab(commit_seen, unsigned char);\ndiff --git a/sequencer.c b/sequencer.c\nindex a817afffa9..d8d045067c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -383,8 +383,8 @@ static void print_advice(struct repository *r, int show_hint,\n \t}\n }\n \n-int write_message(const void *buf, size_t len, const char *filename,\n-\t\t  int append_eol)\n+static int write_message(const void *buf, size_t len, const char *filename,\n+\t\t\t int append_eol)\n {\n \tstruct lock_file msg_file = LOCK_INIT;\n \ndiff --git a/sequencer.h b/sequencer.h\nindex 0c30e43f0a..c5bee8124c 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -64,9 +64,6 @@ struct replay_opts {\n };\n #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }\n \n-int write_message(const void *buf, size_t len, const char *filename,\n-\t\t  int append_eol);\n-\n /*\n  * Note that ordering matters in this enum. Not only must it match the mapping\n  * of todo_command_info (in sequencer.c), it is also divided into several\n-- \n2.20.1\n\n"},{"id":"367621","messageId":"xmqqd0olhgb2.fsf@gitster-ct.c.googlers.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"Re: [PATCH v5 00/16] sequencer: refactor functions working on a todo_list","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-01-24T21:54:57Z","receivedAt":"2019-01-24T21:55:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Alban Gruin <alban.gruin@gmail.com> writes:\n\nBefore I comment on anything else.\n\n> This is based on nd/the-index (36e7ed69de, \"rebase-interactive.c: remove\n> the_repository references\").\n\nMy attempt to apply these in order on top of that commit seems to\nstop at step 5/16.  Are you sure you based them on it?\n\nThanks.\n"},{"id":"367625","messageId":"2759286.8cvUeAOlzz@andromeda","threadId":"49504","inReplyTo":"xmqqd0olhgb2.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v5 00/16] sequencer: refactor functions working on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-24T22:43:09Z","receivedAt":"2019-01-24T22:43:24Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"[I’m resending this as I clicked on the wrong button…]\n\nHi,\n\nLe 24/01/2019 à 22:54, Junio C Hamano a écrit :\n> Alban Gruin <alban.gruin@gmail.com> writes:\n> \n> Before I comment on anything else.\n> \n> > This is based on nd/the-index (36e7ed69de, \"rebase-interactive.c: remove\n> > the_repository references\").\n> \n> My attempt to apply these in order on top of that commit seems to\n> stop at step 5/16.  Are you sure you based them on it?\n> \n> Thanks.\n\nIt is based on that commit, but I mistakenly added a newline between `--- a/\nsequencer.c` and `+++ b/sequencer.c` before sending this series.  Sorry about \nthis.\n\nI just reapplied all the patches I sent, and there is no other problem.  \nShould I send this back?\n\n-- Alban\n\n\n\n\n"},{"id":"367987","messageId":"20190129150159.10588-1-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190123205821.27459-1-alban.gruin@gmail.com","subject":"[PATCH v6 00/16] sequencer: refactor functions working on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T15:01:43Z","receivedAt":"2019-01-29T15:03:02Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"At the center of the \"interactive\" part of the interactive rebase lies\nthe todo list.  When the user starts an interactive rebase, a todo list\nis generated, presented to the user (who then edits it using a text\neditor), read back, and then is checked and processed before the actual\nrebase takes place.\n\nSome of this processing includes adding execs commands, reordering\nfixup! and squash! commits, and checking if no commits were accidentally\ndropped by the user.\n\nBefore I converted the interactive rebase in C, these functions were\ncalled by git-rebase--interactive.sh through git-rebase--helper.  Since\nthe only way to pass around a large amount of data between a shell\nscript and a C program is to use a file (or any declination of a file),\nthe functions that checked and processed the todo list were directly\nworking on a file, the same file that the user edited.\n\nDuring the conversion, I did not address this issue, which lead to a\ncomplete_action() that reads the todo list file, does some computation\nbased on its content, and writes it back to the disk, several times in\nthe same function.\n\nAs it is not an efficient way to handle a data structure, this patch\nseries refactor the functions that processes the todo list to work on a\ntodo_list structure instead of reading it from the disk.\n\nSome commits consists in modifying edit_todo_list() (initially used by\n--edit-todo) to handle the initial edition of the todo list, to increase\ncode sharing.\n\nThis is based on nd/the-index (36e7ed69de, \"rebase-interactive.c: remove\nthe_repository references\").\n\nI am rerolling this because I accidentally added a newline to a place I\nshouldn’t in the patch 05/16 of the v5.  There is no change in this\nversion.\n\nAlban Gruin (16):\n  sequencer: changes in parse_insn_buffer()\n  sequencer: make the todo_list structure public\n  sequencer: remove the 'arg' field from todo_item\n  sequencer: refactor transform_todos() to work on a todo_list\n  sequencer: introduce todo_list_write_to_file()\n  sequencer: refactor check_todo_list() to work on a todo_list\n  sequencer: refactor sequencer_add_exec_commands() to work on a\n    todo_list\n  sequencer: refactor rearrange_squash() to work on a todo_list\n  sequencer: make sequencer_make_script() write its script to a strbuf\n  sequencer: change complete_action() to use the refactored functions\n  sequencer: refactor skip_unnecessary_picks() to work on a todo_list\n  rebase-interactive: use todo_list_write_to_file() in edit_todo_list()\n  rebase-interactive: append_todo_help() changes\n  rebase-interactive: rewrite edit_todo_list() to handle the initial\n    edit\n  sequencer: use edit_todo_list() in complete_action()\n  rebase--interactive: move transform_todo_file() to\n    rebase--interactive.c\n\n builtin/rebase--interactive.c |  90 +++--\n rebase-interactive.c          | 143 +++++--\n rebase-interactive.h          |   9 +-\n sequencer.c                   | 687 ++++++++++++++--------------------\n sequencer.h                   |  81 +++-\n 5 files changed, 533 insertions(+), 477 deletions(-)\n\n-- \n2.20.1\n\n"},{"id":"367988","messageId":"20190129150159.10588-2-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190129150159.10588-1-alban.gruin@gmail.com","subject":"[PATCH v6 01/16] sequencer: changes in parse_insn_buffer()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T15:01:44Z","receivedAt":"2019-01-29T15:03:03Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This clears the number of items of a todo_list before parsing it to\nallow to parse the same list multiple times without issues.  As its\nitems are not dynamically allocated, or don’t need to allocate memory,\nno additionnal memory management is required here.\n\nFurthermore, if a line is invalid, the type of the corresponding\ncommand is set to a garbage value, and its argument is defined properly.\nThis will allow to recreate the text of a todo list from its commands,\neven if one of them is incorrect.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex d726f77e11..a7afaf6882 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2091,6 +2091,8 @@ static int parse_insn_buffer(struct repository *r, char *buf,\n \tchar *p = buf, *next_p;\n \tint i, res = 0, fixup_okay = file_exists(rebase_path_done());\n \n+\ttodo_list->current = todo_list->nr = 0;\n+\n \tfor (i = 1; *p; i++, p = next_p) {\n \t\tchar *eol = strchrnul(p, '\\n');\n \n@@ -2104,7 +2106,10 @@ static int parse_insn_buffer(struct repository *r, char *buf,\n \t\tif (parse_insn_line(r, item, p, eol)) {\n \t\t\tres = error(_(\"invalid line %d: %.*s\"),\n \t\t\t\ti, (int)(eol - p), p);\n-\t\t\titem->command = TODO_NOOP;\n+\t\t\titem->command = TODO_COMMENT + 1;\n+\t\t\titem->arg = p;\n+\t\t\titem->arg_len = (int)(eol - p);\n+\t\t\titem->commit = NULL;\n \t\t}\n \n \t\tif (fixup_okay)\n-- \n2.20.1\n\n"},{"id":"367989","messageId":"20190129150159.10588-3-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190129150159.10588-1-alban.gruin@gmail.com","subject":"[PATCH v6 02/16] sequencer: make the todo_list structure public","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T15:01:45Z","receivedAt":"2019-01-29T15:03:05Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This makes the structures todo_list and todo_item, and the functions\ntodo_list_release() and parse_insn_buffer(), accessible outside of\nsequencer.c.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 69 ++++++++++-------------------------------------------\n sequencer.h | 50 ++++++++++++++++++++++++++++++++++++++\n 2 files changed, 62 insertions(+), 57 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a7afaf6882..5b84a20532 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1460,32 +1460,6 @@ static int allow_empty(struct repository *r,\n \t\treturn 1;\n }\n \n-/*\n- * Note that ordering matters in this enum. Not only must it match the mapping\n- * below, it is also divided into several sections that matter.  When adding\n- * new commands, make sure you add it in the right section.\n- */\n-enum todo_command {\n-\t/* commands that handle commits */\n-\tTODO_PICK = 0,\n-\tTODO_REVERT,\n-\tTODO_EDIT,\n-\tTODO_REWORD,\n-\tTODO_FIXUP,\n-\tTODO_SQUASH,\n-\t/* commands that do something else than handling a single commit */\n-\tTODO_EXEC,\n-\tTODO_BREAK,\n-\tTODO_LABEL,\n-\tTODO_RESET,\n-\tTODO_MERGE,\n-\t/* commands that do nothing but are counted for reporting progress */\n-\tTODO_NOOP,\n-\tTODO_DROP,\n-\t/* comments (not counted for reporting progress) */\n-\tTODO_COMMENT\n-};\n-\n static struct {\n \tchar c;\n \tconst char *str;\n@@ -1962,26 +1936,7 @@ enum todo_item_flags {\n \tTODO_EDIT_MERGE_MSG = 1\n };\n \n-struct todo_item {\n-\tenum todo_command command;\n-\tstruct commit *commit;\n-\tunsigned int flags;\n-\tconst char *arg;\n-\tint arg_len;\n-\tsize_t offset_in_buf;\n-};\n-\n-struct todo_list {\n-\tstruct strbuf buf;\n-\tstruct todo_item *items;\n-\tint nr, alloc, current;\n-\tint done_nr, total_nr;\n-\tstruct stat_data stat;\n-};\n-\n-#define TODO_LIST_INIT { STRBUF_INIT }\n-\n-static void todo_list_release(struct todo_list *todo_list)\n+void todo_list_release(struct todo_list *todo_list)\n {\n \tstrbuf_release(&todo_list->buf);\n \tFREE_AND_NULL(todo_list->items);\n@@ -2084,8 +2039,8 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \treturn !item->commit;\n }\n \n-static int parse_insn_buffer(struct repository *r, char *buf,\n-\t\t\t     struct todo_list *todo_list)\n+int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n+\t\t\t\tstruct todo_list *todo_list)\n {\n \tstruct todo_item *item;\n \tchar *p = buf, *next_p;\n@@ -2184,7 +2139,7 @@ static int read_populate_todo(struct repository *r,\n \t\treturn error(_(\"could not stat '%s'\"), todo_file);\n \tfill_stat_data(&todo_list->stat, &st);\n \n-\tres = parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n+\tres = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n \tif (res) {\n \t\tif (is_rebase_i(opts))\n \t\t\treturn error(_(\"please fix this using \"\n@@ -2215,7 +2170,7 @@ static int read_populate_todo(struct repository *r,\n \t\tFILE *f = fopen_or_warn(rebase_path_msgtotal(), \"w\");\n \n \t\tif (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&\n-\t\t    !parse_insn_buffer(r, done.buf.buf, &done))\n+\t\t    !todo_list_parse_insn_buffer(r, done.buf.buf, &done))\n \t\t\ttodo_list->done_nr = count_commands(&done);\n \t\telse\n \t\t\ttodo_list->done_nr = 0;\n@@ -4501,7 +4456,7 @@ int sequencer_add_exec_commands(struct repository *r,\n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4557,7 +4512,7 @@ int transform_todos(struct repository *r, unsigned flags)\n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4643,7 +4598,7 @@ int check_todo_list(struct repository *r)\n \t\tgoto leave_check;\n \t}\n \tadvise_to_edit_todo = res =\n-\t\tparse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n+\t\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n \n \tif (res || check_level == MISSING_COMMIT_CHECK_IGNORE)\n \t\tgoto leave_check;\n@@ -4662,7 +4617,7 @@ int check_todo_list(struct repository *r)\n \t\tgoto leave_check;\n \t}\n \tstrbuf_release(&todo_file);\n-\tres = !!parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n+\tres = !!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n \n \t/* Find commits in git-rebase-todo.backup yet unseen */\n \tfor (i = todo_list.nr - 1; i >= 0; i--) {\n@@ -4744,7 +4699,7 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \n \tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n \t\treturn -1;\n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n@@ -4832,7 +4787,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(r, buf->buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(r, buf->buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4940,7 +4895,7 @@ int rearrange_squash(struct repository *r)\n \n \tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n \t\treturn -1;\n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex d2c18edd3a..7dc4d8946b 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -73,6 +73,56 @@ enum missing_commit_check_level {\n int write_message(const void *buf, size_t len, const char *filename,\n \t\t  int append_eol);\n \n+/*\n+ * Note that ordering matters in this enum. Not only must it match the mapping\n+ * of todo_command_info (in sequencer.c), it is also divided into several\n+ * sections that matter.  When adding new commands, make sure you add it in the\n+ * right section.\n+ */\n+enum todo_command {\n+\t/* commands that handle commits */\n+\tTODO_PICK = 0,\n+\tTODO_REVERT,\n+\tTODO_EDIT,\n+\tTODO_REWORD,\n+\tTODO_FIXUP,\n+\tTODO_SQUASH,\n+\t/* commands that do something else than handling a single commit */\n+\tTODO_EXEC,\n+\tTODO_BREAK,\n+\tTODO_LABEL,\n+\tTODO_RESET,\n+\tTODO_MERGE,\n+\t/* commands that do nothing but are counted for reporting progress */\n+\tTODO_NOOP,\n+\tTODO_DROP,\n+\t/* comments (not counted for reporting progress) */\n+\tTODO_COMMENT\n+};\n+\n+struct todo_item {\n+\tenum todo_command command;\n+\tstruct commit *commit;\n+\tunsigned int flags;\n+\tconst char *arg;\n+\tint arg_len;\n+\tsize_t offset_in_buf;\n+};\n+\n+struct todo_list {\n+\tstruct strbuf buf;\n+\tstruct todo_item *items;\n+\tint nr, alloc, current;\n+\tint done_nr, total_nr;\n+\tstruct stat_data stat;\n+};\n+\n+#define TODO_LIST_INIT { STRBUF_INIT }\n+\n+int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n+\t\t\t\tstruct todo_list *todo_list);\n+void todo_list_release(struct todo_list *todo_list);\n+\n /* Call this to setup defaults before parsing command line options */\n void sequencer_init_config(struct replay_opts *opts);\n int sequencer_pick_revisions(struct repository *repo,\n-- \n2.20.1\n\n"},{"id":"367990","messageId":"20190129150159.10588-4-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190129150159.10588-1-alban.gruin@gmail.com","subject":"[PATCH v6 03/16] sequencer: remove the 'arg' field from todo_item","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T15:01:46Z","receivedAt":"2019-01-29T15:03:08Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"The 'arg' field of todo_item used to store the address of the first byte\nof the parameter of a command in a todo list.  It was associated with\nthe length of the parameter (the 'arg_len' field).\n\nThis replaces the 'arg' field by 'arg_offset'.  This new field does not\nstore the address of the parameter, but the position of the first\ncharacter of the parameter in the buffer.  todo_item_get_arg() is added\nto return the address of the parameter of an item.\n\nThis will prevent todo_list_add_exec_commands() from having to do awful\npointer arithmetics when growing the todo list buffer.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 67 ++++++++++++++++++++++++++++++-----------------------\n sequencer.h |  6 +++--\n 2 files changed, 42 insertions(+), 31 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 5b84a20532..0e7ab16a05 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1949,8 +1949,14 @@ static struct todo_item *append_new_todo(struct todo_list *todo_list)\n \treturn todo_list->items + todo_list->nr++;\n }\n \n+const char *todo_item_get_arg(struct todo_list *todo_list,\n+\t\t\t      struct todo_item *item)\n+{\n+\treturn todo_list->buf.buf + item->arg_offset;\n+}\n+\n static int parse_insn_line(struct repository *r, struct todo_item *item,\n-\t\t\t   const char *bol, char *eol)\n+\t\t\t   const char *buf, const char *bol, char *eol)\n {\n \tstruct object_id commit_oid;\n \tchar *end_of_object_name;\n@@ -1964,7 +1970,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \tif (bol == eol || *bol == '\\r' || *bol == comment_line_char) {\n \t\titem->command = TODO_COMMENT;\n \t\titem->commit = NULL;\n-\t\titem->arg = bol;\n+\t\titem->arg_offset = bol - buf;\n \t\titem->arg_len = eol - bol;\n \t\treturn 0;\n \t}\n@@ -1991,7 +1997,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \t\t\treturn error(_(\"%s does not accept arguments: '%s'\"),\n \t\t\t\t     command_to_string(item->command), bol);\n \t\titem->commit = NULL;\n-\t\titem->arg = bol;\n+\t\titem->arg_offset = bol - buf;\n \t\titem->arg_len = eol - bol;\n \t\treturn 0;\n \t}\n@@ -2003,7 +2009,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \tif (item->command == TODO_EXEC || item->command == TODO_LABEL ||\n \t    item->command == TODO_RESET) {\n \t\titem->commit = NULL;\n-\t\titem->arg = bol;\n+\t\titem->arg_offset = bol - buf;\n \t\titem->arg_len = (int)(eol - bol);\n \t\treturn 0;\n \t}\n@@ -2017,7 +2023,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \t\t} else {\n \t\t\titem->flags |= TODO_EDIT_MERGE_MSG;\n \t\t\titem->commit = NULL;\n-\t\t\titem->arg = bol;\n+\t\t\titem->arg_offset = bol - buf;\n \t\t\titem->arg_len = (int)(eol - bol);\n \t\t\treturn 0;\n \t\t}\n@@ -2029,8 +2035,9 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \tstatus = get_oid(bol, &commit_oid);\n \t*end_of_object_name = saved;\n \n-\titem->arg = end_of_object_name + strspn(end_of_object_name, \" \\t\");\n-\titem->arg_len = (int)(eol - item->arg);\n+\tbol = end_of_object_name + strspn(end_of_object_name, \" \\t\");\n+\titem->arg_offset = bol - buf;\n+\titem->arg_len = (int)(eol - bol);\n \n \tif (status < 0)\n \t\treturn -1;\n@@ -2058,11 +2065,11 @@ int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \n \t\titem = append_new_todo(todo_list);\n \t\titem->offset_in_buf = p - todo_list->buf.buf;\n-\t\tif (parse_insn_line(r, item, p, eol)) {\n+\t\tif (parse_insn_line(r, item, buf, p, eol)) {\n \t\t\tres = error(_(\"invalid line %d: %.*s\"),\n \t\t\t\ti, (int)(eol - p), p);\n \t\t\titem->command = TODO_COMMENT + 1;\n-\t\t\titem->arg = p;\n+\t\t\titem->arg_offset = p - buf;\n \t\t\titem->arg_len = (int)(eol - p);\n \t\t\titem->commit = NULL;\n \t\t}\n@@ -2402,7 +2409,7 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,\n \n \t\titem->command = command;\n \t\titem->commit = commit;\n-\t\titem->arg = NULL;\n+\t\titem->arg_offset = 0;\n \t\titem->arg_len = 0;\n \t\titem->offset_in_buf = todo_list->buf.len;\n \t\tsubject_len = find_commit_subject(commit_buffer, &subject);\n@@ -3438,6 +3445,8 @@ static int pick_commits(struct repository *r,\n \n \twhile (todo_list->current < todo_list->nr) {\n \t\tstruct todo_item *item = todo_list->items + todo_list->current;\n+\t\tconst char *arg = todo_item_get_arg(todo_list, item);\n+\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n \t\tif (is_rebase_i(opts)) {\n@@ -3488,10 +3497,9 @@ static int pick_commits(struct repository *r,\n \t\t\t\t\tfprintf(stderr,\n \t\t\t\t\t\t_(\"Stopped at %s...  %.*s\\n\"),\n \t\t\t\t\t\tshort_commit_name(commit),\n-\t\t\t\t\t\titem->arg_len, item->arg);\n+\t\t\t\t\t\titem->arg_len, arg);\n \t\t\t\treturn error_with_patch(r, commit,\n-\t\t\t\t\titem->arg, item->arg_len, opts, res,\n-\t\t\t\t\t!res);\n+\t\t\t\t\targ, item->arg_len, opts, res, !res);\n \t\t\t}\n \t\t\tif (is_rebase_i(opts) && !res)\n \t\t\t\trecord_in_rewritten(&item->commit->object.oid,\n@@ -3500,7 +3508,7 @@ static int pick_commits(struct repository *r,\n \t\t\t\tif (res == 1)\n \t\t\t\t\tintend_to_amend();\n \t\t\t\treturn error_failed_squash(r, item->commit, opts,\n-\t\t\t\t\titem->arg_len, item->arg);\n+\t\t\t\t\titem->arg_len, arg);\n \t\t\t} else if (res && is_rebase_i(opts) && item->commit) {\n \t\t\t\tint to_amend = 0;\n \t\t\t\tstruct object_id oid;\n@@ -3519,16 +3527,16 @@ static int pick_commits(struct repository *r,\n \t\t\t\t\tto_amend = 1;\n \n \t\t\t\treturn res | error_with_patch(r, item->commit,\n-\t\t\t\t\t\titem->arg, item->arg_len, opts,\n+\t\t\t\t\t\targ, item->arg_len, opts,\n \t\t\t\t\t\tres, to_amend);\n \t\t\t}\n \t\t} else if (item->command == TODO_EXEC) {\n-\t\t\tchar *end_of_arg = (char *)(item->arg + item->arg_len);\n+\t\t\tchar *end_of_arg = (char *)(arg + item->arg_len);\n \t\t\tint saved = *end_of_arg;\n \t\t\tstruct stat st;\n \n \t\t\t*end_of_arg = '\\0';\n-\t\t\tres = do_exec(r, item->arg);\n+\t\t\tres = do_exec(r, arg);\n \t\t\t*end_of_arg = saved;\n \n \t\t\t/* Reread the todo file if it has changed. */\n@@ -3545,14 +3553,14 @@ static int pick_commits(struct repository *r,\n \t\t\t\ttodo_list->current = -1;\n \t\t\t}\n \t\t} else if (item->command == TODO_LABEL) {\n-\t\t\tif ((res = do_label(r, item->arg, item->arg_len)))\n+\t\t\tif ((res = do_label(r, arg, item->arg_len)))\n \t\t\t\treschedule = 1;\n \t\t} else if (item->command == TODO_RESET) {\n-\t\t\tif ((res = do_reset(r, item->arg, item->arg_len, opts)))\n+\t\t\tif ((res = do_reset(r, arg, item->arg_len, opts)))\n \t\t\t\treschedule = 1;\n \t\t} else if (item->command == TODO_MERGE) {\n \t\t\tif ((res = do_merge(r, item->commit,\n-\t\t\t\t\t    item->arg, item->arg_len,\n+\t\t\t\t\t    arg, item->arg_len,\n \t\t\t\t\t    item->flags, opts)) < 0)\n \t\t\t\treschedule = 1;\n \t\t\telse if (item->commit)\n@@ -3561,9 +3569,8 @@ static int pick_commits(struct repository *r,\n \t\t\tif (res > 0)\n \t\t\t\t/* failed with merge conflicts */\n \t\t\t\treturn error_with_patch(r, item->commit,\n-\t\t\t\t\t\t\titem->arg,\n-\t\t\t\t\t\t\titem->arg_len, opts,\n-\t\t\t\t\t\t\tres, 0);\n+\t\t\t\t\t\t\targ, item->arg_len,\n+\t\t\t\t\t\t\topts, res, 0);\n \t\t} else if (!is_noop(item->command))\n \t\t\treturn error(_(\"unknown command %d\"), item->command);\n \n@@ -3578,9 +3585,8 @@ static int pick_commits(struct repository *r,\n \t\t\tif (item->commit)\n \t\t\t\treturn error_with_patch(r,\n \t\t\t\t\t\t\titem->commit,\n-\t\t\t\t\t\t\titem->arg,\n-\t\t\t\t\t\t\titem->arg_len, opts,\n-\t\t\t\t\t\t\tres, 0);\n+\t\t\t\t\t\t\targ, item->arg_len,\n+\t\t\t\t\t\t\topts, res, 0);\n \t\t}\n \n \t\ttodo_list->current++;\n@@ -4520,7 +4526,8 @@ int transform_todos(struct repository *r, unsigned flags)\n \tfor (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n-\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len, item->arg);\n+\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len,\n+\t\t\t\t    todo_item_get_arg(&todo_list, item));\n \t\t\tcontinue;\n \t\t}\n \n@@ -4550,7 +4557,8 @@ int transform_todos(struct repository *r, unsigned flags)\n \t\tif (!item->arg_len)\n \t\t\tstrbuf_addch(&buf, '\\n');\n \t\telse\n-\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len, item->arg);\n+\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len,\n+\t\t\t\t    todo_item_get_arg(&todo_list, item));\n \t}\n \n \ti = write_message(buf.buf, buf.len, todo_file, 0);\n@@ -4626,7 +4634,8 @@ int check_todo_list(struct repository *r)\n \t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n \t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n \t\t\t\t    short_commit_name(commit),\n-\t\t\t\t    item->arg_len, item->arg);\n+\t\t\t\t    item->arg_len,\n+\t\t\t\t    todo_item_get_arg(&todo_list, item));\n \t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n \t\t}\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex 7dc4d8946b..cdb9ed7a2d 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -104,9 +104,9 @@ struct todo_item {\n \tenum todo_command command;\n \tstruct commit *commit;\n \tunsigned int flags;\n-\tconst char *arg;\n \tint arg_len;\n-\tsize_t offset_in_buf;\n+\t/* The offset of the command and its argument in the strbuf */\n+\tsize_t offset_in_buf, arg_offset;\n };\n \n struct todo_list {\n@@ -122,6 +122,8 @@ struct todo_list {\n int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \t\t\t\tstruct todo_list *todo_list);\n void todo_list_release(struct todo_list *todo_list);\n+const char *todo_item_get_arg(struct todo_list *todo_list,\n+\t\t\t      struct todo_item *item);\n \n /* Call this to setup defaults before parsing command line options */\n void sequencer_init_config(struct replay_opts *opts);\n-- \n2.20.1\n\n"},{"id":"367991","messageId":"20190129150159.10588-5-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190129150159.10588-1-alban.gruin@gmail.com","subject":"[PATCH v6 04/16] sequencer: refactor transform_todos() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T15:01:47Z","receivedAt":"2019-01-29T15:03:09Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors transform_todos() to work on a todo_list.  The function\nis renamed todo_list_transform().\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, transform_todo_file().  It is still used by\ncomplete_action() and edit_todo_list() for now, but they will be\nreplaced in a future commit.\n\ntodo_list_transform() is not a static function, because it will be used\nby edit_todo_list() from rebase-interactive.c in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |  2 +-\n rebase-interactive.c          |  4 +--\n sequencer.c                   | 54 +++++++++++++++++++++++------------\n sequencer.h                   |  4 ++-\n 4 files changed, 42 insertions(+), 22 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex dd2a55ab1d..0898eb4c59 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -253,7 +253,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t}\n \tcase SHORTEN_OIDS:\n \tcase EXPAND_OIDS:\n-\t\tret = transform_todos(the_repository, flags);\n+\t\tret = transform_todo_file(the_repository, flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n \t\tret = check_todo_list(the_repository);\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 68aff1dac2..842fa07e7e 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -69,7 +69,7 @@ int edit_todo_list(struct repository *r, unsigned flags)\n \n \tstrbuf_release(&buf);\n \n-\ttransform_todos(r, flags | TODO_LIST_SHORTEN_IDS);\n+\ttransform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS);\n \n \tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n@@ -85,7 +85,7 @@ int edit_todo_list(struct repository *r, unsigned flags)\n \tif (launch_sequence_editor(todo_file, NULL, NULL))\n \t\treturn -1;\n \n-\ttransform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS));\n+\ttransform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS));\n \n \treturn 0;\n }\ndiff --git a/sequencer.c b/sequencer.c\nindex 0e7ab16a05..7a295cbd3f 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4507,27 +4507,18 @@ int sequencer_add_exec_commands(struct repository *r,\n \treturn i;\n }\n \n-int transform_todos(struct repository *r, unsigned flags)\n+void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n+\t\t\t unsigned flags)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct todo_item *item;\n \tint i;\n \n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tfor (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {\n+\tfor (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n \t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len,\n-\t\t\t\t    todo_item_get_arg(&todo_list, item));\n+\t\t\t\t    todo_item_get_arg(todo_list, item));\n \t\t\tcontinue;\n \t\t}\n \n@@ -4558,12 +4549,39 @@ int transform_todos(struct repository *r, unsigned flags)\n \t\t\tstrbuf_addch(&buf, '\\n');\n \t\telse\n \t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len,\n-\t\t\t\t    todo_item_get_arg(&todo_list, item));\n+\t\t\t\t    todo_item_get_arg(todo_list, item));\n \t}\n \n-\ti = write_message(buf.buf, buf.len, todo_file, 0);\n+\tstrbuf_reset(&todo_list->buf);\n+\tstrbuf_add(&todo_list->buf, buf.buf, buf.len);\n+\tstrbuf_release(&buf);\n+\n+\tif (todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list))\n+\t\tBUG(\"unusable todo list\");\n+}\n+\n+int transform_todo_file(struct repository *r, unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_transform(r, &todo_list, flags);\n+\n+\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n \ttodo_list_release(&todo_list);\n-\treturn i;\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n }\n \n enum missing_commit_check_level get_missing_commit_check_level(void)\n@@ -4825,7 +4843,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n \t\t\t     rebase_path_todo_backup());\n \n-\tif (transform_todos(r, flags | TODO_LIST_SHORTEN_IDS))\n+\tif (transform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS))\n \t\treturn error(_(\"could not transform the todo list\"));\n \n \tstrbuf_reset(buf);\n@@ -4854,7 +4872,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn -1;\n \t}\n \n-\tif (transform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS)))\n+\tif (transform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS)))\n \t\treturn error(_(\"could not transform the todo list\"));\n \n \tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\ndiff --git a/sequencer.h b/sequencer.h\nindex cdb9ed7a2d..35472c137b 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -121,6 +121,8 @@ struct todo_list {\n \n int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \t\t\t\tstruct todo_list *todo_list);\n+void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n+\t\t\t unsigned flags);\n void todo_list_release(struct todo_list *todo_list);\n const char *todo_item_get_arg(struct todo_list *todo_list,\n \t\t\t      struct todo_item *item);\n@@ -148,7 +150,7 @@ int sequencer_make_script(struct repository *repo, FILE *out,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(struct repository *r, const char *command);\n-int transform_todos(struct repository *r, unsigned flags);\n+int transform_todo_file(struct repository *r, unsigned flags);\n enum missing_commit_check_level get_missing_commit_check_level(void);\n int check_todo_list(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n-- \n2.20.1\n\n"},{"id":"367992","messageId":"20190129150159.10588-6-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190129150159.10588-1-alban.gruin@gmail.com","subject":"[PATCH v6 05/16] sequencer: introduce todo_list_write_to_file()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T15:01:48Z","receivedAt":"2019-01-29T15:03:12Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This introduces a new function to recreate the text of a todo list from\nits commands and write it to a file.  This will be useful as the next\nfew commits will change the use of the buffer in struct todo_list so it\nwill no longer be a mirror of the file on disk.\n\nThis functionality already exists in todo_list_transform(), but this\nfunction was made to replace the buffer of a todo list, which is not\nwhat we want here.  Thus, the part of todo_list_transform() that\nreplaces the buffer is dropped, and the function is renamed\ntodo_list_to_strbuf().  It is called by todo_list_write_to_file() to\nfill the buffer to write to the disk.\n\ntodo_list_write_to_file() can also take care of appending the help text\nto the buffer before writing it to the disk, or to write only the first\nn items of the list.  This feature will be used by\nskip_unnecessary_picks(), which has to write done commands in a file.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 61 +++++++++++++++++++++++++++++++++++------------------\n sequencer.h | 11 ++++++----\n 2 files changed, 48 insertions(+), 24 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 7a295cbd3f..87b43994ff 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4507,26 +4507,28 @@ int sequencer_add_exec_commands(struct repository *r,\n \treturn i;\n }\n \n-void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n-\t\t\t unsigned flags)\n+static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n+\t\t\t\tstruct strbuf *buf, int num, unsigned flags)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n \tstruct todo_item *item;\n-\tint i;\n+\tint i, max = todo_list->nr;\n \n-\tfor (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {\n+\tif (num > 0 && num < max)\n+\t\tmax = num;\n+\n+\tfor (item = todo_list->items, i = 0; i < max; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n-\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len,\n+\t\t\tstrbuf_addf(buf, \"%.*s\\n\", item->arg_len,\n \t\t\t\t    todo_item_get_arg(todo_list, item));\n \t\t\tcontinue;\n \t\t}\n \n \t\t/* add command to the buffer */\n \t\tif (flags & TODO_LIST_ABBREVIATE_CMDS)\n-\t\t\tstrbuf_addch(&buf, command_to_char(item->command));\n+\t\t\tstrbuf_addch(buf, command_to_char(item->command));\n \t\telse\n-\t\t\tstrbuf_addstr(&buf, command_to_string(item->command));\n+\t\t\tstrbuf_addstr(buf, command_to_string(item->command));\n \n \t\t/* add commit id */\n \t\tif (item->commit) {\n@@ -4536,28 +4538,48 @@ void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n \n \t\t\tif (item->command == TODO_MERGE) {\n \t\t\t\tif (item->flags & TODO_EDIT_MERGE_MSG)\n-\t\t\t\t\tstrbuf_addstr(&buf, \" -c\");\n+\t\t\t\t\tstrbuf_addstr(buf, \" -c\");\n \t\t\t\telse\n-\t\t\t\t\tstrbuf_addstr(&buf, \" -C\");\n+\t\t\t\t\tstrbuf_addstr(buf, \" -C\");\n \t\t\t}\n \n-\t\t\tstrbuf_addf(&buf, \" %s\", oid);\n+\t\t\tstrbuf_addf(buf, \" %s\", oid);\n \t\t}\n \n \t\t/* add all the rest */\n \t\tif (!item->arg_len)\n-\t\t\tstrbuf_addch(&buf, '\\n');\n+\t\t\tstrbuf_addch(buf, '\\n');\n \t\telse\n-\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len,\n+\t\t\tstrbuf_addf(buf, \" %.*s\\n\", item->arg_len,\n \t\t\t\t    todo_item_get_arg(todo_list, item));\n \t}\n+}\n \n-\tstrbuf_reset(&todo_list->buf);\n-\tstrbuf_add(&todo_list->buf, buf.buf, buf.len);\n+int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n+\t\t\t    const char *file, const char *shortrevisions,\n+\t\t\t    const char *shortonto, int num, unsigned flags)\n+{\n+\tint edit_todo = !(shortrevisions && shortonto), res;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\ttodo_list_to_strbuf(r, todo_list, &buf, num, flags);\n+\n+\tif (flags & TODO_LIST_APPEND_TODO_HELP) {\n+\t\tint command_count = count_commands(todo_list);\n+\t\tif (!edit_todo) {\n+\t\t\tstrbuf_addch(&buf, '\\n');\n+\t\t\tstrbuf_commented_addf(&buf, Q_(\"Rebase %s onto %s (%d command)\",\n+\t\t\t\t\t\t       \"Rebase %s onto %s (%d commands)\",\n+\t\t\t\t\t\t       command_count),\n+\t\t\t\t\t      shortrevisions, shortonto, command_count);\n+\t\t}\n+\t\tappend_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, &buf);\n+\t}\n+\n+\tres = write_message(buf.buf, buf.len, file, 0);\n \tstrbuf_release(&buf);\n \n-\tif (todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list))\n-\t\tBUG(\"unusable todo list\");\n+\treturn res;\n }\n \n int transform_todo_file(struct repository *r, unsigned flags)\n@@ -4574,9 +4596,8 @@ int transform_todo_file(struct repository *r, unsigned flags)\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n \n-\ttodo_list_transform(r, &todo_list, flags);\n-\n-\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n+\tres = todo_list_write_to_file(r, &todo_list, todo_file,\n+\t\t\t\t      NULL, NULL, -1, flags);\n \ttodo_list_release(&todo_list);\n \n \tif (res)\ndiff --git a/sequencer.h b/sequencer.h\nindex 35472c137b..dc3d8f76f7 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -121,8 +121,9 @@ struct todo_list {\n \n int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \t\t\t\tstruct todo_list *todo_list);\n-void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n-\t\t\t unsigned flags);\n+int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n+\t\t\t    const char *file, const char *shortrevisions,\n+\t\t\t    const char *shortonto, int num, unsigned flags);\n void todo_list_release(struct todo_list *todo_list);\n const char *todo_item_get_arg(struct todo_list *todo_list,\n \t\t\t      struct todo_item *item);\n@@ -145,8 +146,10 @@ int sequencer_remove_state(struct replay_opts *opts);\n  * commits should be rebased onto the new base, this flag needs to be passed.\n  */\n #define TODO_LIST_REBASE_COUSINS (1U << 4)\n-int sequencer_make_script(struct repository *repo, FILE *out,\n-\t\t\t  int argc, const char **argv,\n+#define TODO_LIST_APPEND_TODO_HELP (1U << 5)\n+\n+int sequencer_make_script(struct repository *r, FILE *out, int argc,\n+\t\t\t  const char **argv,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(struct repository *r, const char *command);\n-- \n2.20.1\n\n"},{"id":"367993","messageId":"20190129150159.10588-8-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190129150159.10588-1-alban.gruin@gmail.com","subject":"[PATCH v6 07/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T15:01:50Z","receivedAt":"2019-01-29T15:03:13Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors sequencer_add_exec_commands() to work on a todo_list to\navoid redundant reads and writes to the disk.\n\nInstead of inserting the `exec' commands between the other commands and\nre-parsing the buffer at the end, they are appended to the buffer once,\nand a new list of items is created.  Items from the old list are copied\nacross and new `exec' items are appended when necessary.  This\neliminates the need to reparse the buffer, but this also means we have\nto use todo_list_write_to_disk() to write the file.\n\ntodo_list_add_exec_commands() and sequencer_add_exec_commands() are\nmodified to take a string list instead of a string -- one item for each\ncommand.  This makes it easier to insert a new command to the todo list\nfor each command to execute.\n\nsequencer_add_exec_commands() still reads the todo list from the disk,\nas it is needed by rebase -p.\n\ncomplete_action() still uses sequencer_add_exec_commands() for now.\nThis will be changed in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |  15 +++--\n sequencer.c                   | 110 +++++++++++++++++++++-------------\n sequencer.h                   |   5 +-\n 3 files changed, 82 insertions(+), 48 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex df19ccaeb9..53056ee713 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -65,7 +65,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \t\t\t\t const char *onto, const char *onto_name,\n \t\t\t\t const char *squash_onto, const char *head_name,\n \t\t\t\t const char *restrict_revision, char *raw_strategies,\n-\t\t\t\t const char *cmd, unsigned autosquash)\n+\t\t\t\t struct string_list *commands, unsigned autosquash)\n {\n \tint ret;\n \tconst char *head_hash = NULL;\n@@ -116,7 +116,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \t\tdiscard_cache();\n \t\tret = complete_action(the_repository, opts, flags,\n \t\t\t\t      shortrevisions, onto_name, onto,\n-\t\t\t\t      head_hash, cmd, autosquash);\n+\t\t\t\t      head_hash, commands, autosquash);\n \t}\n \n \tfree(revisions);\n@@ -139,6 +139,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \tconst char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,\n \t\t*squash_onto = NULL, *upstream = NULL, *head_name = NULL,\n \t\t*switch_to = NULL, *cmd = NULL;\n+\tstruct string_list commands = STRING_LIST_INIT_DUP;\n \tchar *raw_strategies = NULL;\n \tenum {\n \t\tNONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,\n@@ -221,6 +222,12 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\twarning(_(\"--[no-]rebase-cousins has no effect without \"\n \t\t\t  \"--rebase-merges\"));\n \n+\tif (cmd && *cmd) {\n+\t\tstring_list_split(&commands, cmd, '\\n', -1);\n+\t\tif (strlen(commands.items[commands.nr - 1].string) == 0)\n+\t\t\t--commands.nr;\n+\t}\n+\n \tswitch (command) {\n \tcase NONE:\n \t\tif (!onto && !upstream)\n@@ -228,7 +235,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \n \t\tret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,\n \t\t\t\t\t    onto_name, squash_onto, head_name, restrict_revision,\n-\t\t\t\t\t    raw_strategies, cmd, autosquash);\n+\t\t\t\t\t    raw_strategies, &commands, autosquash);\n \t\tbreak;\n \tcase SKIP: {\n \t\tstruct string_list merge_rr = STRING_LIST_INIT_DUP;\n@@ -262,7 +269,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = rearrange_squash(the_repository);\n \t\tbreak;\n \tcase ADD_EXEC:\n-\t\tret = sequencer_add_exec_commands(the_repository, cmd);\n+\t\tret = sequencer_add_exec_commands(the_repository, &commands);\n \t\tbreak;\n \tdefault:\n \t\tBUG(\"invalid command '%d'\", command);\ndiff --git a/sequencer.c b/sequencer.c\nindex 266f80d704..3a90b419d7 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4446,25 +4446,27 @@ int sequencer_make_script(struct repository *r, FILE *out,\n \treturn 0;\n }\n \n-/*\n- * Add commands after pick and (series of) squash/fixup commands\n- * in the todo list.\n- */\n-int sequencer_add_exec_commands(struct repository *r,\n-\t\t\t\tconst char *commands)\n+static void todo_list_add_exec_commands(struct todo_list *todo_list,\n+\t\t\t\t\tstruct string_list *commands)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf *buf = &todo_list.buf;\n-\tsize_t offset = 0, commands_len = strlen(commands);\n-\tint i, insert;\n+\tstruct strbuf *buf = &todo_list->buf;\n+\tsize_t base_offset = buf->len;\n+\tint i, insert, nr = 0, alloc = 0;\n+\tstruct todo_item *items = NULL, *base_items = NULL;\n \n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n+\tbase_items = xcalloc(commands->nr, sizeof(struct todo_item));\n+\tfor (i = 0; i < commands->nr; ++i) {\n+\t\tsize_t command_len = strlen(commands->items[i].string);\n \n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t\tstrbuf_addstr(buf, commands->items[i].string);\n+\t\tstrbuf_addch(buf, '\\n');\n+\n+\t\tbase_items[i].command = TODO_EXEC;\n+\t\tbase_items[i].offset_in_buf = base_offset;\n+\t\tbase_items[i].arg_offset = base_offset + strlen(\"exec \");\n+\t\tbase_items[i].arg_len = command_len - strlen(\"exec \");\n+\n+\t\tbase_offset += command_len + 1;\n \t}\n \n \t/*\n@@ -4473,38 +4475,62 @@ int sequencer_add_exec_commands(struct repository *r,\n \t * those chains if there are any.\n \t */\n \tinsert = -1;\n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tenum todo_command command = todo_list.items[i].command;\n-\n-\t\tif (insert >= 0) {\n-\t\t\t/* skip fixup/squash chains */\n-\t\t\tif (command == TODO_COMMENT)\n-\t\t\t\tcontinue;\n-\t\t\telse if (is_fixup(command)) {\n-\t\t\t\tinsert = i + 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tstrbuf_insert(buf,\n-\t\t\t\t      todo_list.items[insert].offset_in_buf +\n-\t\t\t\t      offset, commands, commands_len);\n-\t\t\toffset += commands_len;\n+\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\tenum todo_command command = todo_list->items[i].command;\n+\t\tif (insert >= 0 && command != TODO_COMMENT && !is_fixup(command)) {\n+\t\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n+\t\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n+\t\t\tnr += commands->nr;\n \t\t\tinsert = -1;\n \t\t}\n \n-\t\tif (command == TODO_PICK || command == TODO_MERGE)\n+\t\tALLOC_GROW(items, nr + 1, alloc);\n+\t\titems[nr++] = todo_list->items[i];\n+\n+\t\tif (command == TODO_PICK || command == TODO_MERGE || is_fixup(command))\n \t\t\tinsert = i + 1;\n \t}\n \n \t/* insert or append final <commands> */\n-\tif (insert >= 0 && insert < todo_list.nr)\n-\t\tstrbuf_insert(buf, todo_list.items[insert].offset_in_buf +\n-\t\t\t      offset, commands, commands_len);\n-\telse if (insert >= 0 || !offset)\n-\t\tstrbuf_add(buf, commands, commands_len);\n+\tif (insert >= 0 || nr == todo_list->nr) {\n+\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n+\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n+\t\tnr += commands->nr;\n+\t}\n+\n+\tfree(base_items);\n+\tFREE_AND_NULL(todo_list->items);\n+\ttodo_list->items = items;\n+\ttodo_list->nr = nr;\n+\ttodo_list->alloc = alloc;\n+}\n \n-\ti = write_message(buf->buf, buf->len, todo_file, 0);\n+/*\n+ * Add commands after pick and (series of) squash/fixup commands\n+ * in the todo list.\n+ */\n+int sequencer_add_exec_commands(struct repository *r,\n+\t\t\t\tstruct string_list *commands)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_add_exec_commands(&todo_list, commands);\n+\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n \ttodo_list_release(&todo_list);\n-\treturn i;\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n }\n \n static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n@@ -4735,7 +4761,7 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-\t\t    const char *onto, const char *orig_head, const char *cmd,\n+\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash)\n {\n \tconst char *shortonto, *todo_file = rebase_path_todo();\n@@ -4754,8 +4780,8 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tif (autosquash && rearrange_squash(r))\n \t\treturn -1;\n \n-\tif (cmd && *cmd)\n-\t\tsequencer_add_exec_commands(r, cmd);\n+\tif (commands->nr)\n+\t\tsequencer_add_exec_commands(r, commands);\n \n \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\ndiff --git a/sequencer.h b/sequencer.h\nindex 1de97f188d..e79f03e213 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -146,12 +146,13 @@ int sequencer_make_script(struct repository *r, FILE *out, int argc,\n \t\t\t  const char **argv,\n \t\t\t  unsigned flags);\n \n-int sequencer_add_exec_commands(struct repository *r, const char *command);\n+int sequencer_add_exec_commands(struct repository *r,\n+\t\t\t\tstruct string_list *commands);\n int transform_todo_file(struct repository *r, unsigned flags);\n int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-\t\t    const char *onto, const char *orig_head, const char *cmd,\n+\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash);\n int rearrange_squash(struct repository *r);\n \n-- \n2.20.1\n\n"},{"id":"367994","messageId":"20190129150159.10588-9-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190129150159.10588-1-alban.gruin@gmail.com","subject":"[PATCH v6 08/16] sequencer: refactor rearrange_squash() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T15:01:51Z","receivedAt":"2019-01-29T15:03:15Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors rearrange_squash() to work on a todo_list to avoid\nredundant reads and writes.  The function is renamed\ntodo_list_rearrange_squash().\n\nThe old version created a new buffer, which was directly written to the\ndisk.  This new version creates a new item list by just copying items\nfrom the old item list, without creating a new buffer.  This eliminates\nthe need to reparse the todo list, but this also means its buffer cannot\nbe directly written to the disk.\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, rearrange_squash_in_todo_file().\n\ncomplete_action() still uses rearrange_squash_in_todo_file() for now.\nThis will be changed in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |  2 +-\n sequencer.c                   | 92 ++++++++++++++++++-----------------\n sequencer.h                   |  2 +-\n 3 files changed, 49 insertions(+), 47 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 53056ee713..4f0eae9239 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -266,7 +266,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = check_todo_list_from_file(the_repository);\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n-\t\tret = rearrange_squash(the_repository);\n+\t\tret = rearrange_squash_in_todo_file(the_repository);\n \t\tbreak;\n \tcase ADD_EXEC:\n \t\tret = sequencer_add_exec_commands(the_repository, &commands);\ndiff --git a/sequencer.c b/sequencer.c\nindex 3a90b419d7..11456be5cc 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4777,7 +4777,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t    write_message(\"noop\\n\", 5, todo_file, 0))\n \t\treturn -1;\n \n-\tif (autosquash && rearrange_squash(r))\n+\tif (autosquash && rearrange_squash_in_todo_file(r))\n \t\treturn -1;\n \n \tif (commands->nr)\n@@ -4883,21 +4883,13 @@ define_commit_slab(commit_todo_item, struct todo_item *);\n  * message will have to be retrieved from the commit (as the oneline in the\n  * script cannot be trusted) in order to normalize the autosquash arrangement.\n  */\n-int rearrange_squash(struct repository *r)\n+static int todo_list_rearrange_squash(struct todo_list *todo_list)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct hashmap subject2item;\n-\tint res = 0, rearranged = 0, *next, *tail, i;\n+\tint rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;\n \tchar **subjects;\n \tstruct commit_todo_item commit_todo;\n-\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tstruct todo_item *items = NULL;\n \n \tinit_commit_todo_item(&commit_todo);\n \t/*\n@@ -4910,13 +4902,13 @@ int rearrange_squash(struct repository *r)\n \t * be moved to appear after the i'th.\n \t */\n \thashmap_init(&subject2item, (hashmap_cmp_fn) subject2item_cmp,\n-\t\t     NULL, todo_list.nr);\n-\tALLOC_ARRAY(next, todo_list.nr);\n-\tALLOC_ARRAY(tail, todo_list.nr);\n-\tALLOC_ARRAY(subjects, todo_list.nr);\n-\tfor (i = 0; i < todo_list.nr; i++) {\n+\t\t     NULL, todo_list->nr);\n+\tALLOC_ARRAY(next, todo_list->nr);\n+\tALLOC_ARRAY(tail, todo_list->nr);\n+\tALLOC_ARRAY(subjects, todo_list->nr);\n+\tfor (i = 0; i < todo_list->nr; i++) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n-\t\tstruct todo_item *item = todo_list.items + i;\n+\t\tstruct todo_item *item = todo_list->items + i;\n \t\tconst char *commit_buffer, *subject, *p;\n \t\tsize_t subject_len;\n \t\tint i2 = -1;\n@@ -4929,7 +4921,6 @@ int rearrange_squash(struct repository *r)\n \t\t}\n \n \t\tif (is_fixup(item->command)) {\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\tclear_commit_todo_item(&commit_todo);\n \t\t\treturn error(_(\"the script was already rearranged.\"));\n \t\t}\n@@ -4964,7 +4955,7 @@ int rearrange_squash(struct repository *r)\n \t\t\t\t *commit_todo_item_at(&commit_todo, commit2))\n \t\t\t\t/* found by commit name */\n \t\t\t\ti2 = *commit_todo_item_at(&commit_todo, commit2)\n-\t\t\t\t\t- todo_list.items;\n+\t\t\t\t\t- todo_list->items;\n \t\t\telse {\n \t\t\t\t/* copy can be a prefix of the commit subject */\n \t\t\t\tfor (i2 = 0; i2 < i; i2++)\n@@ -4977,7 +4968,7 @@ int rearrange_squash(struct repository *r)\n \t\t}\n \t\tif (i2 >= 0) {\n \t\t\trearranged = 1;\n-\t\t\ttodo_list.items[i].command =\n+\t\t\ttodo_list->items[i].command =\n \t\t\t\tstarts_with(subject, \"fixup!\") ?\n \t\t\t\tTODO_FIXUP : TODO_SQUASH;\n \t\t\tif (next[i2] < 0)\n@@ -4995,10 +4986,8 @@ int rearrange_squash(struct repository *r)\n \t}\n \n \tif (rearranged) {\n-\t\tstruct strbuf buf = STRBUF_INIT;\n-\n-\t\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\t\tenum todo_command command = todo_list.items[i].command;\n+\t\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\t\tenum todo_command command = todo_list->items[i].command;\n \t\t\tint cur = i;\n \n \t\t\t/*\n@@ -5009,37 +4998,50 @@ int rearrange_squash(struct repository *r)\n \t\t\t\tcontinue;\n \n \t\t\twhile (cur >= 0) {\n-\t\t\t\tconst char *bol =\n-\t\t\t\t\tget_item_line(&todo_list, cur);\n-\t\t\t\tconst char *eol =\n-\t\t\t\t\tget_item_line(&todo_list, cur + 1);\n-\n-\t\t\t\t/* replace 'pick', by 'fixup' or 'squash' */\n-\t\t\t\tcommand = todo_list.items[cur].command;\n-\t\t\t\tif (is_fixup(command)) {\n-\t\t\t\t\tstrbuf_addstr(&buf,\n-\t\t\t\t\t\ttodo_command_info[command].str);\n-\t\t\t\t\tbol += strcspn(bol, \" \\t\");\n-\t\t\t\t}\n-\n-\t\t\t\tstrbuf_add(&buf, bol, eol - bol);\n-\n+\t\t\t\tALLOC_GROW(items, nr + 1, alloc);\n+\t\t\t\titems[nr++] = todo_list->items[cur];\n \t\t\t\tcur = next[cur];\n \t\t\t}\n \t\t}\n \n-\t\tres = rewrite_file(todo_file, buf.buf, buf.len);\n-\t\tstrbuf_release(&buf);\n+\t\tFREE_AND_NULL(todo_list->items);\n+\t\ttodo_list->items = items;\n+\t\ttodo_list->nr = nr;\n+\t\ttodo_list->alloc = alloc;\n \t}\n \n \tfree(next);\n \tfree(tail);\n-\tfor (i = 0; i < todo_list.nr; i++)\n+\tfor (i = 0; i < todo_list->nr; i++)\n \t\tfree(subjects[i]);\n \tfree(subjects);\n \thashmap_free(&subject2item, 1);\n-\ttodo_list_release(&todo_list);\n \n \tclear_commit_todo_item(&commit_todo);\n-\treturn res;\n+\n+\treturn 0;\n+}\n+\n+int rearrange_squash_in_todo_file(struct repository *r)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n+\n+\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n+\t\treturn -1;\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn -1;\n+\t}\n+\n+\tres = todo_list_rearrange_squash(&todo_list);\n+\tif (!res)\n+\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n+\n+\ttodo_list_release(&todo_list);\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n }\ndiff --git a/sequencer.h b/sequencer.h\nindex e79f03e213..c7bb38d6df 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -154,7 +154,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash);\n-int rearrange_squash(struct repository *r);\n+int rearrange_squash_in_todo_file(struct repository *r);\n \n extern const char sign_off_header[];\n \n-- \n2.20.1\n\n"},{"id":"367995","messageId":"20190129150159.10588-12-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190129150159.10588-1-alban.gruin@gmail.com","subject":"[PATCH v6 11/16] sequencer: refactor skip_unnecessary_picks() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T15:01:54Z","receivedAt":"2019-01-29T15:03:17Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors skip_unnecessary_picks() to work on a todo_list.  As this\nfunction is only called by complete_action() (and thus is not used by\nrebase -p), the file-handling logic is completely dropped here.\n\nInstead of truncating the todo list’s buffer, the items are moved to\nthe beginning of the list, eliminating the need to reparse the list.\nThis also means its buffer cannot be directly written to the disk.\n\nrewrite_file() is then removed, as it is now unused.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 78 ++++++++++++-----------------------------------------\n 1 file changed, 17 insertions(+), 61 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 2a43ca685b..a817afffa9 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4661,52 +4661,22 @@ int check_todo_list_from_file(struct repository *r)\n \treturn res;\n }\n \n-static int rewrite_file(const char *path, const char *buf, size_t len)\n-{\n-\tint rc = 0;\n-\tint fd = open(path, O_WRONLY | O_TRUNC);\n-\tif (fd < 0)\n-\t\treturn error_errno(_(\"could not open '%s' for writing\"), path);\n-\tif (write_in_full(fd, buf, len) < 0)\n-\t\trc = error_errno(_(\"could not write to '%s'\"), path);\n-\tif (close(fd) && !rc)\n-\t\trc = error_errno(_(\"could not close '%s'\"), path);\n-\treturn rc;\n-}\n-\n /* skip picking commits whose parents are unchanged */\n-static int skip_unnecessary_picks(struct repository *r, struct object_id *output_oid)\n+static int skip_unnecessary_picks(struct repository *r,\n+\t\t\t\t  struct todo_list *todo_list,\n+\t\t\t\t  struct object_id *output_oid)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct object_id *parent_oid;\n-\tint fd, i;\n-\n-\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n-\t\treturn error(_(\"could not read 'onto'\"));\n-\tif (get_oid(buf.buf, output_oid)) {\n-\t\tstrbuf_release(&buf);\n-\t\treturn error(_(\"need a HEAD to fixup\"));\n-\t}\n-\tstrbuf_release(&buf);\n-\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tint i;\n \n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tstruct todo_item *item = todo_list.items + i;\n+\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\tstruct todo_item *item = todo_list->items + i;\n \n \t\tif (item->command >= TODO_NOOP)\n \t\t\tcontinue;\n \t\tif (item->command != TODO_PICK)\n \t\t\tbreak;\n \t\tif (parse_commit(item->commit)) {\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\treturn error(_(\"could not parse commit '%s'\"),\n \t\t\t\toid_to_hex(&item->commit->object.oid));\n \t\t}\n@@ -4720,37 +4690,21 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \t\toidcpy(output_oid, &item->commit->object.oid);\n \t}\n \tif (i > 0) {\n-\t\tint offset = get_item_line_offset(&todo_list, i);\n \t\tconst char *done_path = rebase_path_done();\n \n-\t\tfd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);\n-\t\tif (fd < 0) {\n-\t\t\terror_errno(_(\"could not open '%s' for writing\"),\n-\t\t\t\t    done_path);\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\treturn -1;\n-\t\t}\n-\t\tif (write_in_full(fd, todo_list.buf.buf, offset) < 0) {\n+\t\tif (todo_list_write_to_file(r, todo_list, done_path, NULL, NULL, i, 0)) {\n \t\t\terror_errno(_(\"could not write to '%s'\"), done_path);\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\tclose(fd);\n \t\t\treturn -1;\n \t\t}\n-\t\tclose(fd);\n \n-\t\tif (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,\n-\t\t\t\t todo_list.buf.len - offset) < 0) {\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\treturn -1;\n-\t\t}\n+\t\tMOVE_ARRAY(todo_list->items, todo_list->items + i, todo_list->nr - i);\n+\t\ttodo_list->nr -= i;\n+\t\ttodo_list->current = 0;\n \n-\t\ttodo_list.current = i;\n-\t\tif (is_fixup(peek_command(&todo_list, 0)))\n-\t\t\trecord_in_rewritten(output_oid, peek_command(&todo_list, 0));\n+\t\tif (is_fixup(peek_command(todo_list, 0)))\n+\t\t\trecord_in_rewritten(output_oid, peek_command(todo_list, 0));\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\n \treturn 0;\n }\n \n@@ -4823,6 +4777,11 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn -1;\n \t}\n \n+\tif (opts->allow_ff && skip_unnecessary_picks(r, &new_todo, &oid)) {\n+\t\ttodo_list_release(&new_todo);\n+\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n+\t}\n+\n \tif (todo_list_write_to_file(r, &new_todo, todo_file, NULL, NULL, -1,\n \t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS))) {\n \t\ttodo_list_release(&new_todo);\n@@ -4831,9 +4790,6 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \n \ttodo_list_release(&new_todo);\n \n-\tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\n-\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n-\n \tif (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))\n \t\treturn -1;\n \n-- \n2.20.1\n\n"},{"id":"367996","messageId":"20190129150159.10588-11-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190129150159.10588-1-alban.gruin@gmail.com","subject":"[PATCH v6 10/16] sequencer: change complete_action() to use the refactored functions","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T15:01:53Z","receivedAt":"2019-01-29T15:03:18Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"complete_action() used functions that read the todo-list file, made some\nchanges to it, and wrote it back to the disk.\n\nThe previous commits were dedicated to separate the part that deals with\nthe file from the actual logic of these functions.  Now that this is\ndone, we can call directly the \"logic\" functions to avoid useless file\naccess.\n\nThe parsing of the list has to be done by the caller.  If the buffer of\nthe todo list provided by the caller is empty, a `noop' command is\ndirectly added to the todo list, without touching the buffer.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 20 +++------\n sequencer.c                   | 81 +++++++++++++++--------------------\n sequencer.h                   |  2 +-\n 3 files changed, 42 insertions(+), 61 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 92026739c9..2dbf8fc08b 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -71,7 +71,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tconst char *head_hash = NULL;\n \tchar *revisions = NULL, *shortrevisions = NULL;\n \tstruct argv_array make_script_args = ARGV_ARRAY_INIT;\n-\tFILE *todo_list_file;\n \tstruct todo_list todo_list = TODO_LIST_INIT;\n \n \tif (prepare_branch_to_be_rebased(opts, switch_to))\n@@ -94,14 +93,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (!upstream && squash_onto)\n \t\twrite_file(path_squash_onto(), \"%s\\n\", squash_onto);\n \n-\ttodo_list_file = fopen(rebase_path_todo(), \"w\");\n-\tif (!todo_list_file) {\n-\t\tfree(revisions);\n-\t\tfree(shortrevisions);\n-\n-\t\treturn error_errno(_(\"could not open %s\"), rebase_path_todo());\n-\t}\n-\n \targv_array_pushl(&make_script_args, \"\", revisions, NULL);\n \tif (restrict_revision)\n \t\targv_array_push(&make_script_args, restrict_revision);\n@@ -109,16 +100,17 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tret = sequencer_make_script(the_repository, &todo_list.buf,\n \t\t\t\t    make_script_args.argc, make_script_args.argv,\n \t\t\t\t    flags);\n-\tfputs(todo_list.buf.buf, todo_list_file);\n-\tfclose(todo_list_file);\n \n \tif (ret)\n \t\terror(_(\"could not generate todo list\"));\n \telse {\n \t\tdiscard_cache();\n-\t\tret = complete_action(the_repository, opts, flags,\n-\t\t\t\t      shortrevisions, onto_name, onto,\n-\t\t\t\t      head_hash, commands, autosquash);\n+\t\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n+\t\t\t\t\t\t&todo_list))\n+\t\t\tBUG(\"unusable todo list\");\n+\n+\t\tret = complete_action(the_repository, opts, flags, shortrevisions, onto_name,\n+\t\t\t\t      onto, head_hash, commands, autosquash, &todo_list);\n \t}\n \n \tfree(revisions);\ndiff --git a/sequencer.c b/sequencer.c\nindex f1c62c5960..2a43ca685b 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4754,100 +4754,89 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \treturn 0;\n }\n \n+static int todo_list_rearrange_squash(struct todo_list *todo_list);\n+\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n-\t\t    unsigned autosquash)\n+\t\t    unsigned autosquash, struct todo_list *todo_list)\n {\n \tconst char *shortonto, *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf *buf = &(todo_list.buf);\n+\tstruct todo_list new_todo = TODO_LIST_INIT;\n+\tstruct strbuf *buf = &todo_list->buf;\n \tstruct object_id oid;\n-\tstruct stat st;\n \n \tget_oid(onto, &oid);\n \tshortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);\n \n-\tif (!lstat(todo_file, &st) && st.st_size == 0 &&\n-\t    write_message(\"noop\\n\", 5, todo_file, 0))\n-\t\treturn -1;\n+\tif (buf->len == 0) {\n+\t\tstruct todo_item *item = append_new_todo(todo_list);\n+\t\titem->command = TODO_NOOP;\n+\t\titem->commit = NULL;\n+\t\titem->arg_len = item->arg_offset = item->flags = item->offset_in_buf = 0;\n+\t}\n \n-\tif (autosquash && rearrange_squash_in_todo_file(r))\n+\tif (autosquash && todo_list_rearrange_squash(todo_list))\n \t\treturn -1;\n \n \tif (commands->nr)\n-\t\tsequencer_add_exec_commands(r, commands);\n+\t\ttodo_list_add_exec_commands(todo_list, commands);\n \n-\tif (strbuf_read_file(buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(r, buf->buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tif (count_commands(&todo_list) == 0) {\n+\tif (count_commands(todo_list) == 0) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n \n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\tstrbuf_addch(buf, '\\n');\n-\tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n-\t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n-\t\t\t\t      count_commands(&todo_list)),\n-\t\t\t      shortrevisions, shortonto, count_commands(&todo_list));\n-\tappend_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);\n-\n-\tif (write_message(buf->buf, buf->len, todo_file, 0)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tif (todo_list_write_to_file(r, todo_list, todo_file,\n+\t\t\t\t    shortrevisions, shortonto, -1,\n+\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n \n \tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n \t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n \t\t\t     rebase_path_todo_backup());\n \n-\tif (transform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS))\n-\t\treturn error(_(\"could not transform the todo list\"));\n-\n-\tstrbuf_reset(buf);\n-\n-\tif (launch_sequence_editor(todo_file, buf, NULL)) {\n+\tif (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n \n \t\treturn -1;\n \t}\n \n-\tstrbuf_stripspace(buf, 1);\n-\tif (buf->len == 0) {\n+\tstrbuf_stripspace(&new_todo.buf, 1);\n+\tif (new_todo.buf.len == 0) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n+\t\ttodo_list_release(&new_todo);\n \n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\n-\tif (check_todo_list_from_file(r)) {\n+\tif (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) ||\n+\t    todo_list_check(todo_list, &new_todo)) {\n+\t\tfprintf(stderr, _(edit_todo_list_advice));\n \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n+\t\ttodo_list_release(&new_todo);\n+\n \t\treturn -1;\n \t}\n \n-\tif (transform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS)))\n-\t\treturn error(_(\"could not transform the todo list\"));\n+\tif (todo_list_write_to_file(r, &new_todo, todo_file, NULL, NULL, -1,\n+\t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS))) {\n+\t\ttodo_list_release(&new_todo);\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_release(&new_todo);\n \n \tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\n \t\treturn error(_(\"could not skip unnecessary pick commands\"));\n \n \tif (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))\n \t\treturn -1;\n-;\n+\n \tif (require_clean_work_tree(r, \"rebase\", \"\", 1, 1))\n \t\treturn -1;\n \ndiff --git a/sequencer.h b/sequencer.h\nindex 9b8edd7df6..0c30e43f0a 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -152,7 +152,7 @@ int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n-\t\t    unsigned autosquash);\n+\t\t    unsigned autosquash, struct todo_list *todo_list);\n int rearrange_squash_in_todo_file(struct repository *r);\n \n extern const char sign_off_header[];\n-- \n2.20.1\n\n"},{"id":"367997","messageId":"20190129150159.10588-14-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190129150159.10588-1-alban.gruin@gmail.com","subject":"[PATCH v6 13/16] rebase-interactive: append_todo_help() changes","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T15:01:56Z","receivedAt":"2019-01-29T15:03:20Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This moves the writing of the comment \"Rebase $shortrevisions onto\n$shortonto ($command_count commands)\" from todo_list_write_to_file() to\nappend_todo_help().\n\nshortrevisions, shortonto, and command_count are passed as parameters to\nappend_todo_help().\n\nDuring the initial edit of the todo list, shortrevisions and shortonto\nare not NULL.  Therefore, if shortrevisions or shortonto is NULL, then\nedit_todo would be true, otherwise it would be false.  Thus, edit_todo\nis removed from the parameters of append_todo_help().\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n rebase-interactive.c | 12 +++++++++++-\n rebase-interactive.h |  3 ++-\n sequencer.c          | 17 ++++-------------\n 3 files changed, 17 insertions(+), 15 deletions(-)\n\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex d396ecc599..807f8370db 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -28,7 +28,8 @@ static enum missing_commit_check_level get_missing_commit_check_level(void)\n \treturn MISSING_COMMIT_CHECK_IGNORE;\n }\n \n-void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n+void append_todo_help(unsigned keep_empty, int command_count,\n+\t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf)\n {\n \tconst char *msg = _(\"\\nCommands:\\n\"\n@@ -48,6 +49,15 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \".       specified). Use -c <commit> to reword the commit message.\\n\"\n \"\\n\"\n \"These lines can be re-ordered; they are executed from top to bottom.\\n\");\n+\tunsigned edit_todo = !(shortrevisions && shortonto);\n+\n+\tif (!edit_todo) {\n+\t\tstrbuf_addch(buf, '\\n');\n+\t\tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n+\t\t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n+\t\t\t\t\t      command_count),\n+\t\t\t\t      shortrevisions, shortonto, command_count);\n+\t}\n \n \tstrbuf_add_commented_lines(buf, msg, strlen(msg));\n \ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 187b5032d6..0e5925e3aa 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -5,7 +5,8 @@ struct strbuf;\n struct repository;\n struct todo_list;\n \n-void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n+void append_todo_help(unsigned keep_empty, int command_count,\n+\t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf);\n int edit_todo_list(struct repository *r, unsigned flags);\n int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\ndiff --git a/sequencer.c b/sequencer.c\nindex d8d045067c..92de982bc4 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4580,22 +4580,13 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n \t\t\t    const char *file, const char *shortrevisions,\n \t\t\t    const char *shortonto, int num, unsigned flags)\n {\n-\tint edit_todo = !(shortrevisions && shortonto), res;\n+\tint res;\n \tstruct strbuf buf = STRBUF_INIT;\n \n \ttodo_list_to_strbuf(r, todo_list, &buf, num, flags);\n-\n-\tif (flags & TODO_LIST_APPEND_TODO_HELP) {\n-\t\tint command_count = count_commands(todo_list);\n-\t\tif (!edit_todo) {\n-\t\t\tstrbuf_addch(&buf, '\\n');\n-\t\t\tstrbuf_commented_addf(&buf, Q_(\"Rebase %s onto %s (%d command)\",\n-\t\t\t\t\t\t       \"Rebase %s onto %s (%d commands)\",\n-\t\t\t\t\t\t       command_count),\n-\t\t\t\t\t      shortrevisions, shortonto, command_count);\n-\t\t}\n-\t\tappend_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, &buf);\n-\t}\n+\tif (flags & TODO_LIST_APPEND_TODO_HELP)\n+\t\tappend_todo_help(flags & TODO_LIST_KEEP_EMPTY, count_commands(todo_list),\n+\t\t\t\t shortrevisions, shortonto, &buf);\n \n \tres = write_message(buf.buf, buf.len, file, 0);\n \tstrbuf_release(&buf);\n-- \n2.20.1\n\n"},{"id":"367998","messageId":"20190129150159.10588-10-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190129150159.10588-1-alban.gruin@gmail.com","subject":"[PATCH v6 09/16] sequencer: make sequencer_make_script() write its script to a strbuf","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T15:01:52Z","receivedAt":"2019-01-29T15:03:21Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This makes sequencer_make_script() write its script to a strbuf (ie. the\nbuffer of a todo_list) instead of a FILE.  This reduce the amount of\nread/write made by rebase interactive.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 13 ++++++-----\n sequencer.c                   | 41 +++++++++++++++--------------------\n sequencer.h                   |  5 ++---\n 3 files changed, 28 insertions(+), 31 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 4f0eae9239..92026739c9 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -71,7 +71,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tconst char *head_hash = NULL;\n \tchar *revisions = NULL, *shortrevisions = NULL;\n \tstruct argv_array make_script_args = ARGV_ARRAY_INIT;\n-\tFILE *todo_list;\n+\tFILE *todo_list_file;\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n \n \tif (prepare_branch_to_be_rebased(opts, switch_to))\n \t\treturn -1;\n@@ -93,8 +94,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (!upstream && squash_onto)\n \t\twrite_file(path_squash_onto(), \"%s\\n\", squash_onto);\n \n-\ttodo_list = fopen(rebase_path_todo(), \"w\");\n-\tif (!todo_list) {\n+\ttodo_list_file = fopen(rebase_path_todo(), \"w\");\n+\tif (!todo_list_file) {\n \t\tfree(revisions);\n \t\tfree(shortrevisions);\n \n@@ -105,10 +106,11 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (restrict_revision)\n \t\targv_array_push(&make_script_args, restrict_revision);\n \n-\tret = sequencer_make_script(the_repository, todo_list,\n+\tret = sequencer_make_script(the_repository, &todo_list.buf,\n \t\t\t\t    make_script_args.argc, make_script_args.argv,\n \t\t\t\t    flags);\n-\tfclose(todo_list);\n+\tfputs(todo_list.buf.buf, todo_list_file);\n+\tfclose(todo_list_file);\n \n \tif (ret)\n \t\terror(_(\"could not generate todo list\"));\n@@ -121,6 +123,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \n \tfree(revisions);\n \tfree(shortrevisions);\n+\ttodo_list_release(&todo_list);\n \targv_array_clear(&make_script_args);\n \n \treturn ret;\ndiff --git a/sequencer.c b/sequencer.c\nindex 11456be5cc..f1c62c5960 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4160,7 +4160,7 @@ static const char *label_oid(struct object_id *oid, const char *label,\n }\n \n static int make_script_with_merges(struct pretty_print_context *pp,\n-\t\t\t\t   struct rev_info *revs, FILE *out,\n+\t\t\t\t   struct rev_info *revs, struct strbuf *out,\n \t\t\t\t   unsigned flags)\n {\n \tint keep_empty = flags & TODO_LIST_KEEP_EMPTY;\n@@ -4305,7 +4305,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t * gathering commits not yet shown, reversing the list on the fly,\n \t * then outputting that list (labeling revisions as needed).\n \t */\n-\tfprintf(out, \"%s onto\\n\", cmd_label);\n+\tstrbuf_addf(out, \"%s onto\\n\", cmd_label);\n \tfor (iter = tips; iter; iter = iter->next) {\n \t\tstruct commit_list *list = NULL, *iter2;\n \n@@ -4315,9 +4315,9 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\tentry = oidmap_get(&state.commit2label, &commit->object.oid);\n \n \t\tif (entry)\n-\t\t\tfprintf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n+\t\t\tstrbuf_addf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n \t\telse\n-\t\t\tfprintf(out, \"\\n\");\n+\t\t\tstrbuf_addch(out, '\\n');\n \n \t\twhile (oidset_contains(&interesting, &commit->object.oid) &&\n \t\t       !oidset_contains(&shown, &commit->object.oid)) {\n@@ -4330,8 +4330,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t}\n \n \t\tif (!commit)\n-\t\t\tfprintf(out, \"%s %s\\n\", cmd_reset,\n-\t\t\t\trebase_cousins ? \"onto\" : \"[new root]\");\n+\t\t\tstrbuf_addf(out, \"%s %s\\n\", cmd_reset,\n+\t\t\t\t    rebase_cousins ? \"onto\" : \"[new root]\");\n \t\telse {\n \t\t\tconst char *to = NULL;\n \n@@ -4344,12 +4344,12 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t\t\t\t       &state);\n \n \t\t\tif (!to || !strcmp(to, \"onto\"))\n-\t\t\t\tfprintf(out, \"%s onto\\n\", cmd_reset);\n+\t\t\t\tstrbuf_addf(out, \"%s onto\\n\", cmd_reset);\n \t\t\telse {\n \t\t\t\tstrbuf_reset(&oneline);\n \t\t\t\tpretty_print_commit(pp, commit, &oneline);\n-\t\t\t\tfprintf(out, \"%s %s # %s\\n\",\n-\t\t\t\t\tcmd_reset, to, oneline.buf);\n+\t\t\t\tstrbuf_addf(out, \"%s %s # %s\\n\",\n+\t\t\t\t\t    cmd_reset, to, oneline.buf);\n \t\t\t}\n \t\t}\n \n@@ -4358,11 +4358,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t\tentry = oidmap_get(&commit2todo, oid);\n \t\t\t/* only show if not already upstream */\n \t\t\tif (entry)\n-\t\t\t\tfprintf(out, \"%s\\n\", entry->string);\n+\t\t\t\tstrbuf_addf(out, \"%s\\n\", entry->string);\n \t\t\tentry = oidmap_get(&state.commit2label, oid);\n \t\t\tif (entry)\n-\t\t\t\tfprintf(out, \"%s %s\\n\",\n-\t\t\t\t\tcmd_label, entry->string);\n+\t\t\t\tstrbuf_addf(out, \"%s %s\\n\",\n+\t\t\t\t\t    cmd_label, entry->string);\n \t\t\toidset_insert(&shown, oid);\n \t\t}\n \n@@ -4384,13 +4384,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \treturn 0;\n }\n \n-int sequencer_make_script(struct repository *r, FILE *out,\n-\t\t\t  int argc, const char **argv,\n-\t\t\t  unsigned flags)\n+int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n+\t\t\t  const char **argv, unsigned flags)\n {\n \tchar *format = NULL;\n \tstruct pretty_print_context pp = {0};\n-\tstruct strbuf buf = STRBUF_INIT;\n \tstruct rev_info revs;\n \tstruct commit *commit;\n \tint keep_empty = flags & TODO_LIST_KEEP_EMPTY;\n@@ -4433,16 +4431,13 @@ int sequencer_make_script(struct repository *r, FILE *out,\n \n \t\tif (!is_empty && (commit->object.flags & PATCHSAME))\n \t\t\tcontinue;\n-\t\tstrbuf_reset(&buf);\n \t\tif (!keep_empty && is_empty)\n-\t\t\tstrbuf_addf(&buf, \"%c \", comment_line_char);\n-\t\tstrbuf_addf(&buf, \"%s %s \", insn,\n+\t\t\tstrbuf_addf(out, \"%c \", comment_line_char);\n+\t\tstrbuf_addf(out, \"%s %s \", insn,\n \t\t\t    oid_to_hex(&commit->object.oid));\n-\t\tpretty_print_commit(&pp, commit, &buf);\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t\tfputs(buf.buf, out);\n+\t\tpretty_print_commit(&pp, commit, out);\n+\t\tstrbuf_addch(out, '\\n');\n \t}\n-\tstrbuf_release(&buf);\n \treturn 0;\n }\n \ndiff --git a/sequencer.h b/sequencer.h\nindex c7bb38d6df..9b8edd7df6 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -142,9 +142,8 @@ int sequencer_remove_state(struct replay_opts *opts);\n #define TODO_LIST_REBASE_COUSINS (1U << 4)\n #define TODO_LIST_APPEND_TODO_HELP (1U << 5)\n \n-int sequencer_make_script(struct repository *r, FILE *out, int argc,\n-\t\t\t  const char **argv,\n-\t\t\t  unsigned flags);\n+int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n+\t\t\t  const char **argv, unsigned flags);\n \n int sequencer_add_exec_commands(struct repository *r,\n \t\t\t\tstruct string_list *commands);\n-- \n2.20.1\n\n"},{"id":"367999","messageId":"20190129150159.10588-13-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190129150159.10588-1-alban.gruin@gmail.com","subject":"[PATCH v6 12/16] rebase-interactive: use todo_list_write_to_file() in edit_todo_list()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T15:01:55Z","receivedAt":"2019-01-29T15:03:26Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Just like complete_action(), edit_todo_list() used a\nfunction (transform_todo_file()) that read the todo list from the disk\nand wrote it back, resulting in useless disk accesses.\n\nThis changes edit_todo_list() to call directly todo_list_write_to_file()\ninstead.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n rebase-interactive.c | 38 ++++++++++++++++----------------------\n sequencer.c          |  4 ++--\n sequencer.h          |  3 ---\n 3 files changed, 18 insertions(+), 27 deletions(-)\n\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex dfa6dd530f..d396ecc599 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -79,39 +79,33 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \n int edit_todo_list(struct repository *r, unsigned flags)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n \tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n \n-\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n-\tstrbuf_stripspace(&buf, 1);\n-\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n-\t\tstrbuf_release(&buf);\n+\tstrbuf_stripspace(&todo_list.buf, 1);\n+\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n+\tif (todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n+\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP)) {\n+\t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n \n-\tstrbuf_release(&buf);\n-\n-\ttransform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS);\n-\n-\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tappend_todo_help(1, 0, &buf);\n-\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n-\t\tstrbuf_release(&buf);\n+\tstrbuf_reset(&todo_list.buf);\n+\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n+\t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n \n-\tstrbuf_release(&buf);\n+\tif (!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list))\n+\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n+\t\t\t\t\t      flags & ~(TODO_LIST_SHORTEN_IDS));\n \n-\tif (launch_sequence_editor(todo_file, NULL, NULL))\n-\t\treturn -1;\n-\n-\ttransform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS));\n-\n-\treturn 0;\n+\ttodo_list_release(&todo_list);\n+\treturn res;\n }\n \n define_commit_slab(commit_seen, unsigned char);\ndiff --git a/sequencer.c b/sequencer.c\nindex a817afffa9..d8d045067c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -383,8 +383,8 @@ static void print_advice(struct repository *r, int show_hint,\n \t}\n }\n \n-int write_message(const void *buf, size_t len, const char *filename,\n-\t\t  int append_eol)\n+static int write_message(const void *buf, size_t len, const char *filename,\n+\t\t\t int append_eol)\n {\n \tstruct lock_file msg_file = LOCK_INIT;\n \ndiff --git a/sequencer.h b/sequencer.h\nindex 0c30e43f0a..c5bee8124c 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -64,9 +64,6 @@ struct replay_opts {\n };\n #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }\n \n-int write_message(const void *buf, size_t len, const char *filename,\n-\t\t  int append_eol);\n-\n /*\n  * Note that ordering matters in this enum. Not only must it match the mapping\n  * of todo_command_info (in sequencer.c), it is also divided into several\n-- \n2.20.1\n\n"},{"id":"368000","messageId":"20190129150159.10588-17-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190129150159.10588-1-alban.gruin@gmail.com","subject":"[PATCH v6 16/16] rebase--interactive: move transform_todo_file() to rebase--interactive.c","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T15:01:59Z","receivedAt":"2019-01-29T15:03:28Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"As transform_todo_file() is only needed inside of rebase--interactive.c,\nit is moved there from sequencer.c.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 26 +++++++++++++++++++++++++-\n sequencer.c                   | 23 -----------------------\n sequencer.h                   |  1 -\n 3 files changed, 25 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 645ac587f7..7f1e88a087 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -35,6 +35,30 @@ static int edit_todo_file(unsigned flags)\n \treturn 0;\n }\n \n+static int transform_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n+\t\t\t\t\t&todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\tres = todo_list_write_to_file(the_repository, &todo_list, todo_file,\n+\t\t\t\t      NULL, NULL, -1, flags);\n+\ttodo_list_release(&todo_list);\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n+}\n+\n static int get_revision_ranges(const char *upstream, const char *onto,\n \t\t\t       const char **head_hash,\n \t\t\t       char **revisions, char **shortrevisions)\n@@ -277,7 +301,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t}\n \tcase SHORTEN_OIDS:\n \tcase EXPAND_OIDS:\n-\t\tret = transform_todo_file(the_repository, flags);\n+\t\tret = transform_todo_file(flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n \t\tret = check_todo_list_from_file(the_repository);\ndiff --git a/sequencer.c b/sequencer.c\nindex 21b04e0642..5239700efc 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4593,29 +4593,6 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n \treturn res;\n }\n \n-int transform_todo_file(struct repository *r, unsigned flags)\n-{\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res;\n-\n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tres = todo_list_write_to_file(r, &todo_list, todo_file,\n-\t\t\t\t      NULL, NULL, -1, flags);\n-\ttodo_list_release(&todo_list);\n-\n-\tif (res)\n-\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n-\treturn 0;\n-}\n-\n static const char edit_todo_list_advice[] =\n N_(\"You can fix this with 'git rebase --edit-todo' \"\n \"and then run 'git rebase --continue'.\\n\"\ndiff --git a/sequencer.h b/sequencer.h\nindex 68acab980b..11afd47aa9 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -145,7 +145,6 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \n int sequencer_add_exec_commands(struct repository *r,\n \t\t\t\tstruct string_list *commands);\n-int transform_todo_file(struct repository *r, unsigned flags);\n int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-- \n2.20.1\n\n"},{"id":"368002","messageId":"20190129150159.10588-16-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190129150159.10588-1-alban.gruin@gmail.com","subject":"[PATCH v6 15/16] sequencer: use edit_todo_list() in complete_action()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T15:01:58Z","receivedAt":"2019-01-29T15:03:30Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This changes complete_action() to use edit_todo_list(), now that it can\nhandle the initial edit of the todo list.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 21 +++++++--------------\n 1 file changed, 7 insertions(+), 14 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 8f47f0cf39..21b04e0642 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4709,6 +4709,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tstruct todo_list new_todo = TODO_LIST_INIT;\n \tstruct strbuf *buf = &todo_list->buf;\n \tstruct object_id oid;\n+\tint res;\n \n \tget_oid(onto, &oid);\n \tshortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);\n@@ -4733,24 +4734,16 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\tif (todo_list_write_to_file(r, todo_list, todo_file,\n-\t\t\t\t    shortrevisions, shortonto, -1,\n-\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))\n-\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n-\n-\tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n-\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n-\t\t\t     rebase_path_todo_backup());\n-\n-\tif (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {\n+\tres = edit_todo_list(r, todo_list, &new_todo, shortrevisions,\n+\t\t\t     shortonto, flags);\n+\tif (res == -1)\n+\t\treturn -1;\n+\telse if (res == -2) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n \n \t\treturn -1;\n-\t}\n-\n-\tstrbuf_stripspace(&new_todo.buf, 1);\n-\tif (new_todo.buf.len == 0) {\n+\t} else if (res == -3) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n \t\ttodo_list_release(&new_todo);\n-- \n2.20.1\n\n"},{"id":"368001","messageId":"20190129150159.10588-15-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190129150159.10588-1-alban.gruin@gmail.com","subject":"[PATCH v6 14/16] rebase-interactive: rewrite edit_todo_list() to handle the initial edit","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T15:01:57Z","receivedAt":"2019-01-29T15:03:31Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"edit_todo_list() is changed to work on a todo_list, and to handle the\ninitial edition of the todo list (ie. making a backup of the todo\nlist).\n\nIt does not check for dropped commits yet, as todo_list_check() does not\ntake the commits that have already been processed by the rebase (ie. the\ntodo list is edited in the middle of a rebase session).\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 24 +++++++++++++++++-\n rebase-interactive.c          | 48 ++++++++++++++++++-----------------\n rebase-interactive.h          |  4 ++-\n sequencer.c                   |  3 +--\n sequencer.h                   |  1 +\n 5 files changed, 53 insertions(+), 27 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 2dbf8fc08b..645ac587f7 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -13,6 +13,28 @@ static GIT_PATH_FUNC(path_state_dir, \"rebase-merge/\")\n static GIT_PATH_FUNC(path_squash_onto, \"rebase-merge/squash-onto\")\n static GIT_PATH_FUNC(path_interactive, \"rebase-merge/interactive\")\n \n+static int edit_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT,\n+\t\tnew_todo = TODO_LIST_INIT;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tstrbuf_stripspace(&todo_list.buf, 1);\n+\tif (!edit_todo_list(the_repository, &todo_list,\n+\t\t\t    &new_todo, NULL, NULL, flags) &&\n+\t    todo_list_write_to_file(the_repository, &new_todo, todo_file, NULL, NULL,\n+\t\t\t\t    -1, flags & ~(TODO_LIST_SHORTEN_IDS)) < 0)\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n+\n+\ttodo_list_release(&todo_list);\n+\ttodo_list_release(&new_todo);\n+\n+\treturn 0;\n+}\n+\n static int get_revision_ranges(const char *upstream, const char *onto,\n \t\t\t       const char **head_hash,\n \t\t\t       char **revisions, char **shortrevisions)\n@@ -242,7 +264,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tbreak;\n \t}\n \tcase EDIT_TODO:\n-\t\tret = edit_todo_list(the_repository, flags);\n+\t\tret = edit_todo_file(flags);\n \t\tbreak;\n \tcase SHOW_CURRENT_PATCH: {\n \t\tstruct child_process cmd = CHILD_PROCESS_INIT;\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 807f8370db..3301efbe52 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -87,35 +87,37 @@ void append_todo_help(unsigned keep_empty, int command_count,\n \t}\n }\n \n-int edit_todo_list(struct repository *r, unsigned flags)\n+int edit_todo_list(struct repository *r, struct todo_list *todo_list,\n+\t\t   struct todo_list *new_todo, const char *shortrevisions,\n+\t\t   const char *shortonto, unsigned flags)\n {\n \tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res = 0;\n-\n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tstrbuf_stripspace(&todo_list.buf, 1);\n-\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n-\tif (todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n-\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n+\tunsigned initial = shortrevisions && shortonto;\n+\n+\tif (initial) {\n+\t\ttodo_list_write_to_file(r, todo_list, todo_file, shortrevisions, shortonto,\n+\t\t\t\t\t-1, flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP);\n+\n+\t\tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n+\t\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n+\t\t\t\t     rebase_path_todo_backup());\n+\t} else {\n+\t\ttodo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n+\t\ttodo_list_write_to_file(r, todo_list, todo_file, NULL, NULL, -1,\n+\t\t\t\t\tflags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP);\n \t}\n \n-\tstrbuf_reset(&todo_list.buf);\n-\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tif (launch_sequence_editor(todo_file, &new_todo->buf, NULL))\n+\t\treturn -2;\n \n-\tif (!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list))\n-\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n-\t\t\t\t\t      flags & ~(TODO_LIST_SHORTEN_IDS));\n+\tstrbuf_stripspace(&new_todo->buf, 1);\n+\tif (initial && new_todo->buf.len == 0)\n+\t\treturn -3;\n \n-\ttodo_list_release(&todo_list);\n-\treturn res;\n+\tif (!initial)\n+\t\ttodo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo);\n+\n+\treturn 0;\n }\n \n define_commit_slab(commit_seen, unsigned char);\ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 0e5925e3aa..44dbb06311 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -8,7 +8,9 @@ struct todo_list;\n void append_todo_help(unsigned keep_empty, int command_count,\n \t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf);\n-int edit_todo_list(struct repository *r, unsigned flags);\n+int edit_todo_list(struct repository *r, struct todo_list *todo_list,\n+\t\t   struct todo_list *new_todo, const char *shortrevisions,\n+\t\t   const char *shortonto, unsigned flags);\n int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n \n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex 92de982bc4..8f47f0cf39 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -55,8 +55,7 @@ static GIT_PATH_FUNC(rebase_path, \"rebase-merge\")\n  * file and written to the tail of 'done'.\n  */\n GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n-static GIT_PATH_FUNC(rebase_path_todo_backup,\n-\t\t     \"rebase-merge/git-rebase-todo.backup\")\n+GIT_PATH_FUNC(rebase_path_todo_backup, \"rebase-merge/git-rebase-todo.backup\")\n \n /*\n  * The rebase command lines that have already been processed. A line\ndiff --git a/sequencer.h b/sequencer.h\nindex c5bee8124c..68acab980b 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -10,6 +10,7 @@ struct repository;\n const char *git_path_commit_editmsg(void);\n const char *git_path_seq_dir(void);\n const char *rebase_path_todo(void);\n+const char *rebase_path_todo_backup(void);\n \n #define APPEND_SIGNOFF_DEDUP (1u << 0)\n \n-- \n2.20.1\n\n"},{"id":"368003","messageId":"20190129150159.10588-7-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190129150159.10588-1-alban.gruin@gmail.com","subject":"[PATCH v6 06/16] sequencer: refactor check_todo_list() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T15:01:49Z","receivedAt":"2019-01-29T15:03:32Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors check_todo_list() to work on a todo_list to avoid\nredundant reads and writes to the disk.  The function is renamed\ntodo_list_check().  The parsing of the two todo lists is left to the\ncaller.\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, check_todo_list_from_file().  It reads the file\nfrom the disk, parses it, pass the todo_list to todo_list_check(), and\nwrites it back to the disk.\n\nAs get_missing_commit_check_level() and the enum\nmissing_commit_check_level are no longer needed inside of sequencer.c,\nthey are moved to rebase-interactive.c, and made static again.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |   2 +-\n rebase-interactive.c          |  91 ++++++++++++++++++++++++-\n rebase-interactive.h          |   2 +\n sequencer.c                   | 121 +++++++---------------------------\n sequencer.h                   |   9 +--\n 5 files changed, 117 insertions(+), 108 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 0898eb4c59..df19ccaeb9 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -256,7 +256,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = transform_todo_file(the_repository, flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n-\t\tret = check_todo_list(the_repository);\n+\t\tret = check_todo_list_from_file(the_repository);\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n \t\tret = rearrange_squash(the_repository);\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 842fa07e7e..dfa6dd530f 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -1,8 +1,32 @@\n #include \"cache.h\"\n #include \"commit.h\"\n-#include \"rebase-interactive.h\"\n #include \"sequencer.h\"\n+#include \"rebase-interactive.h\"\n #include \"strbuf.h\"\n+#include \"commit-slab.h\"\n+#include \"config.h\"\n+\n+enum missing_commit_check_level {\n+\tMISSING_COMMIT_CHECK_IGNORE = 0,\n+\tMISSING_COMMIT_CHECK_WARN,\n+\tMISSING_COMMIT_CHECK_ERROR\n+};\n+\n+static enum missing_commit_check_level get_missing_commit_check_level(void)\n+{\n+\tconst char *value;\n+\n+\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n+\t\t\t!strcasecmp(\"ignore\", value))\n+\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n+\tif (!strcasecmp(\"warn\", value))\n+\t\treturn MISSING_COMMIT_CHECK_WARN;\n+\tif (!strcasecmp(\"error\", value))\n+\t\treturn MISSING_COMMIT_CHECK_ERROR;\n+\twarning(_(\"unrecognized setting %s for option \"\n+\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n+\treturn MISSING_COMMIT_CHECK_IGNORE;\n+}\n \n void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \t\t      struct strbuf *buf)\n@@ -89,3 +113,68 @@ int edit_todo_list(struct repository *r, unsigned flags)\n \n \treturn 0;\n }\n+\n+define_commit_slab(commit_seen, unsigned char);\n+/*\n+ * Check if the user dropped some commits by mistake\n+ * Behaviour determined by rebase.missingCommitsCheck.\n+ * Check if there is an unrecognized command or a\n+ * bad SHA-1 in a command.\n+ */\n+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo)\n+{\n+\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n+\tstruct strbuf missing = STRBUF_INIT;\n+\tint res = 0, i;\n+\tstruct commit_seen commit_seen;\n+\n+\tinit_commit_seen(&commit_seen);\n+\n+\tif (check_level == MISSING_COMMIT_CHECK_IGNORE)\n+\t\tgoto leave_check;\n+\n+\t/* Mark the commits in git-rebase-todo as seen */\n+\tfor (i = 0; i < new_todo->nr; i++) {\n+\t\tstruct commit *commit = new_todo->items[i].commit;\n+\t\tif (commit)\n+\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t}\n+\n+\t/* Find commits in git-rebase-todo.backup yet unseen */\n+\tfor (i = old_todo->nr - 1; i >= 0; i--) {\n+\t\tstruct todo_item *item = old_todo->items + i;\n+\t\tstruct commit *commit = item->commit;\n+\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n+\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n+\t\t\t\t    find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV),\n+\t\t\t\t    item->arg_len,\n+\t\t\t\t    todo_item_get_arg(old_todo, item));\n+\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t\t}\n+\t}\n+\n+\t/* Warn about missing commits */\n+\tif (!missing.len)\n+\t\tgoto leave_check;\n+\n+\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n+\t\tres = 1;\n+\n+\tfprintf(stderr,\n+\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n+\t\t\"Dropped commits (newer to older):\\n\"));\n+\n+\t/* Make the list user-friendly and display */\n+\tfputs(missing.buf, stderr);\n+\tstrbuf_release(&missing);\n+\n+\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n+\t\t\"explicitly remove a commit.\\n\\n\"\n+\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n+\t\t\"the level of warnings.\\n\"\n+\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n+\n+leave_check:\n+\tclear_commit_seen(&commit_seen);\n+\treturn res;\n+}\ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 17b6c9f6d0..187b5032d6 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -3,9 +3,11 @@\n \n struct strbuf;\n struct repository;\n+struct todo_list;\n \n void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \t\t      struct strbuf *buf);\n int edit_todo_list(struct repository *r, unsigned flags);\n+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n \n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex 87b43994ff..266f80d704 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4605,112 +4605,37 @@ int transform_todo_file(struct repository *r, unsigned flags)\n \treturn 0;\n }\n \n-enum missing_commit_check_level get_missing_commit_check_level(void)\n-{\n-\tconst char *value;\n-\n-\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n-\t\t\t!strcasecmp(\"ignore\", value))\n-\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n-\tif (!strcasecmp(\"warn\", value))\n-\t\treturn MISSING_COMMIT_CHECK_WARN;\n-\tif (!strcasecmp(\"error\", value))\n-\t\treturn MISSING_COMMIT_CHECK_ERROR;\n-\twarning(_(\"unrecognized setting %s for option \"\n-\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n-\treturn MISSING_COMMIT_CHECK_IGNORE;\n-}\n+static const char edit_todo_list_advice[] =\n+N_(\"You can fix this with 'git rebase --edit-todo' \"\n+\"and then run 'git rebase --continue'.\\n\"\n+\"Or you can abort the rebase with 'git rebase\"\n+\" --abort'.\\n\");\n \n-define_commit_slab(commit_seen, unsigned char);\n-/*\n- * Check if the user dropped some commits by mistake\n- * Behaviour determined by rebase.missingCommitsCheck.\n- * Check if there is an unrecognized command or a\n- * bad SHA-1 in a command.\n- */\n-int check_todo_list(struct repository *r)\n+int check_todo_list_from_file(struct repository *r)\n {\n-\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n-\tstruct strbuf todo_file = STRBUF_INIT;\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf missing = STRBUF_INIT;\n-\tint advise_to_edit_todo = 0, res = 0, i;\n-\tstruct commit_seen commit_seen;\n-\n-\tinit_commit_seen(&commit_seen);\n+\tstruct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;\n+\tint res = 0;\n \n-\tstrbuf_addstr(&todo_file, rebase_path_todo());\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n+\tif (strbuf_read_file_or_whine(&new_todo.buf, rebase_path_todo()) < 0) {\n \t\tres = -1;\n-\t\tgoto leave_check;\n-\t}\n-\tadvise_to_edit_todo = res =\n-\t\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n-\n-\tif (res || check_level == MISSING_COMMIT_CHECK_IGNORE)\n-\t\tgoto leave_check;\n-\n-\t/* Mark the commits in git-rebase-todo as seen */\n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tstruct commit *commit = todo_list.items[i].commit;\n-\t\tif (commit)\n-\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t\tgoto out;\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\tstrbuf_addstr(&todo_file, \".backup\");\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n+\tif (strbuf_read_file_or_whine(&old_todo.buf, rebase_path_todo_backup()) < 0) {\n \t\tres = -1;\n-\t\tgoto leave_check;\n-\t}\n-\tstrbuf_release(&todo_file);\n-\tres = !!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n-\n-\t/* Find commits in git-rebase-todo.backup yet unseen */\n-\tfor (i = todo_list.nr - 1; i >= 0; i--) {\n-\t\tstruct todo_item *item = todo_list.items + i;\n-\t\tstruct commit *commit = item->commit;\n-\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n-\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n-\t\t\t\t    short_commit_name(commit),\n-\t\t\t\t    item->arg_len,\n-\t\t\t\t    todo_item_get_arg(&todo_list, item));\n-\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n-\t\t}\n+\t\tgoto out;\n \t}\n \n-\t/* Warn about missing commits */\n-\tif (!missing.len)\n-\t\tgoto leave_check;\n-\n-\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n-\t\tadvise_to_edit_todo = res = 1;\n-\n-\tfprintf(stderr,\n-\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n-\t\t\"Dropped commits (newer to older):\\n\"));\n-\n-\t/* Make the list user-friendly and display */\n-\tfputs(missing.buf, stderr);\n-\tstrbuf_release(&missing);\n-\n-\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n-\t\t\"explicitly remove a commit.\\n\\n\"\n-\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n-\t\t\"the level of warnings.\\n\"\n-\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n-\n-leave_check:\n-\tclear_commit_seen(&commit_seen);\n-\tstrbuf_release(&todo_file);\n-\ttodo_list_release(&todo_list);\n-\n-\tif (advise_to_edit_todo)\n-\t\tfprintf(stderr,\n-\t\t\t_(\"You can fix this with 'git rebase --edit-todo' \"\n-\t\t\t  \"and then run 'git rebase --continue'.\\n\"\n-\t\t\t  \"Or you can abort the rebase with 'git rebase\"\n-\t\t\t  \" --abort'.\\n\"));\n+\tres = todo_list_parse_insn_buffer(r, old_todo.buf.buf, &old_todo);\n+\tif (!res)\n+\t\tres = todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo);\n+\tif (!res)\n+\t\tres = todo_list_check(&old_todo, &new_todo);\n+\tif (res)\n+\t\tfprintf(stderr, _(edit_todo_list_advice));\n+out:\n+\ttodo_list_release(&old_todo);\n+\ttodo_list_release(&new_todo);\n \n \treturn res;\n }\n@@ -4888,7 +4813,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \n \ttodo_list_release(&todo_list);\n \n-\tif (check_todo_list(r)) {\n+\tif (check_todo_list_from_file(r)) {\n \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n \t\treturn -1;\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex dc3d8f76f7..1de97f188d 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -64,12 +64,6 @@ struct replay_opts {\n };\n #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }\n \n-enum missing_commit_check_level {\n-\tMISSING_COMMIT_CHECK_IGNORE = 0,\n-\tMISSING_COMMIT_CHECK_WARN,\n-\tMISSING_COMMIT_CHECK_ERROR\n-};\n-\n int write_message(const void *buf, size_t len, const char *filename,\n \t\t  int append_eol);\n \n@@ -154,8 +148,7 @@ int sequencer_make_script(struct repository *r, FILE *out, int argc,\n \n int sequencer_add_exec_commands(struct repository *r, const char *command);\n int transform_todo_file(struct repository *r, unsigned flags);\n-enum missing_commit_check_level get_missing_commit_check_level(void);\n-int check_todo_list(struct repository *r);\n+int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, const char *cmd,\n-- \n2.20.1\n\n"},{"id":"368038","messageId":"xmqqo97z6x2v.fsf@gitster-ct.c.googlers.com","threadId":"49504","inReplyTo":"20190129150159.10588-11-alban.gruin@gmail.com","subject":"Re: [PATCH v6 10/16] sequencer: change complete_action() to use the refactored functions","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-01-29T20:14:00Z","receivedAt":"2019-01-29T20:14:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Alban Gruin <alban.gruin@gmail.com> writes:\n\n>  \tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\n>  \t\treturn error(_(\"could not skip unnecessary pick commands\"));\n>  \n>  \tif (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))\n>  \t\treturn -1;\n> -;\n> +\n>  \tif (require_clean_work_tree(r, \"rebase\", \"\", 1, 1))\n>  \t\treturn -1;\n\nThis hunk was fishy (in my tree, there is no such line with ';'\nalone, as I believe that we've already fixed it) and made me spend\nsome time to figure out why the patch does not apply, but once the\ncause was known it was trivial to fix.\n\nI've looked at the difference since the previous version, but in\nessence, the only change I saw was that four instances of similar\nexpressions are replaced with calls to a new todo_item_get_arg()\nhelper and no other changes.\n\nThanks, will replace.\n\n"},{"id":"368039","messageId":"822aa023-1603-a5c4-7693-9653a492b9a0@gmail.com","threadId":"49504","inReplyTo":"xmqqo97z6x2v.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v6 10/16] sequencer: change complete_action() to use the refactored functions","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-29T20:33:48Z","receivedAt":"2019-01-29T20:34:05Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Hi Junio,\n\nLe 29/01/2019 à 21:14, Junio C Hamano a écrit :\n> Alban Gruin <alban.gruin@gmail.com> writes:\n> \n>>  \tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\n>>  \t\treturn error(_(\"could not skip unnecessary pick commands\"));\n>>  \n>>  \tif (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))\n>>  \t\treturn -1;\n>> -;\n>> +\n>>  \tif (require_clean_work_tree(r, \"rebase\", \"\", 1, 1))\n>>  \t\treturn -1;\n> \n> This hunk was fishy (in my tree, there is no such line with ';'\n> alone, as I believe that we've already fixed it) and made me spend\n> some time to figure out why the patch does not apply, but once the\n> cause was known it was trivial to fix.\n> \n\nIndeed, this semicolon was removed by 29d51e214c (\"sequencer.c: remove a\nstray semicolon\", 2018-11-03).  But this commit isn’t part of nd/the-index.\n\n> I've looked at the difference since the previous version, but in\n> essence, the only change I saw was that four instances of similar\n> expressions are replaced with calls to a new todo_item_get_arg()\n> helper and no other changes.\n> \n> Thanks, will replace.\n> \n\nCheers,\nAlban\n\n"},{"id":"368053","messageId":"xmqqfttb6sdc.fsf@gitster-ct.c.googlers.com","threadId":"49504","inReplyTo":"822aa023-1603-a5c4-7693-9653a492b9a0@gmail.com","subject":"Re: [PATCH v6 10/16] sequencer: change complete_action() to use the refactored functions","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2019-01-29T21:55:43Z","receivedAt":"2019-01-29T21:55:48Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Alban Gruin <alban.gruin@gmail.com> writes:\n\n> Indeed, this semicolon was removed by 29d51e214c (\"sequencer.c: remove a\n> stray semicolon\", 2018-11-03).  But this commit isn’t part of nd/the-index.\n\nI thought you said it was based on nd/the-index, so I've been\nqueuing the topic since earlier iterations on top of cde55548\nwhich is where nd/the-index was merged to 'master'.\n\n"},{"id":"368239","messageId":"434cf0f7-1df6-7966-b460-e69e8f8b5e99@talktalk.net","threadId":"49504","inReplyTo":"20190129150159.10588-8-alban.gruin@gmail.com","subject":"Re: [PATCH v6 07/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-01-31T14:30:19Z","receivedAt":"2019-01-31T14:30:26Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nOn 29/01/2019 15:01, Alban Gruin wrote:\n> This refactors sequencer_add_exec_commands() to work on a todo_list to\n> avoid redundant reads and writes to the disk.\n> \n> Instead of inserting the `exec' commands between the other commands and\n> re-parsing the buffer at the end, they are appended to the buffer once,\n> and a new list of items is created.  Items from the old list are copied\n> across and new `exec' items are appended when necessary.  This\n> eliminates the need to reparse the buffer, but this also means we have\n> to use todo_list_write_to_disk() to write the file.\n> \n> todo_list_add_exec_commands() and sequencer_add_exec_commands() are\n> modified to take a string list instead of a string -- one item for each\n> command.  This makes it easier to insert a new command to the todo list\n> for each command to execute.\n> \n> sequencer_add_exec_commands() still reads the todo list from the disk,\n> as it is needed by rebase -p.\n> \n> complete_action() still uses sequencer_add_exec_commands() for now.\n> This will be changed in a future commit.\n> \n> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n> ---\n>  builtin/rebase--interactive.c |  15 +++--\n>  sequencer.c                   | 110 +++++++++++++++++++++-------------\n>  sequencer.h                   |   5 +-\n>  3 files changed, 82 insertions(+), 48 deletions(-)\n> \n> diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\n> index df19ccaeb9..53056ee713 100644\n> --- a/builtin/rebase--interactive.c\n> +++ b/builtin/rebase--interactive.c\n> @@ -65,7 +65,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n>  \t\t\t\t const char *onto, const char *onto_name,\n>  \t\t\t\t const char *squash_onto, const char *head_name,\n>  \t\t\t\t const char *restrict_revision, char *raw_strategies,\n> -\t\t\t\t const char *cmd, unsigned autosquash)\n> +\t\t\t\t struct string_list *commands, unsigned autosquash)\n>  {\n>  \tint ret;\n>  \tconst char *head_hash = NULL;\n> @@ -116,7 +116,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n>  \t\tdiscard_cache();\n>  \t\tret = complete_action(the_repository, opts, flags,\n>  \t\t\t\t      shortrevisions, onto_name, onto,\n> -\t\t\t\t      head_hash, cmd, autosquash);\n> +\t\t\t\t      head_hash, commands, autosquash);\n>  \t}\n>  \n>  \tfree(revisions);\n> @@ -139,6 +139,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \tconst char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,\n>  \t\t*squash_onto = NULL, *upstream = NULL, *head_name = NULL,\n>  \t\t*switch_to = NULL, *cmd = NULL;\n> +\tstruct string_list commands = STRING_LIST_INIT_DUP;\n>  \tchar *raw_strategies = NULL;\n>  \tenum {\n>  \t\tNONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,\n> @@ -221,6 +222,12 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \t\twarning(_(\"--[no-]rebase-cousins has no effect without \"\n>  \t\t\t  \"--rebase-merges\"));\n>  \n> +\tif (cmd && *cmd) {\n> +\t\tstring_list_split(&commands, cmd, '\\n', -1);\n\nThis whole splitting and later skipping 'exec ' is a bit of a shame - it \nwould be much nicer if we could just have one exec command per -x option \nbut I think that is outside the scope of this series (If I have time I'd \nlike to look at calling do_interactive_rebase() directly from \nbuiltin/rebase.c without forking rebase--interactive).\n\n> +\t\tif (strlen(commands.items[commands.nr - 1].string) == 0)\n\nI'd be tempted just to test the string using !* rather than calling \nstrlen. Also is there ever a case where the last string isn't empty?\n\n> +\t\t\t--commands.nr;\n> +\t}\n> +\n>  \tswitch (command) {\n>  \tcase NONE:\n>  \t\tif (!onto && !upstream)\n> @@ -228,7 +235,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \n>  \t\tret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,\n>  \t\t\t\t\t    onto_name, squash_onto, head_name, restrict_revision,\n> -\t\t\t\t\t    raw_strategies, cmd, autosquash);\n> +\t\t\t\t\t    raw_strategies, &commands, autosquash);\n>  \t\tbreak;\n>  \tcase SKIP: {\n>  \t\tstruct string_list merge_rr = STRING_LIST_INIT_DUP;\n> @@ -262,7 +269,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \t\tret = rearrange_squash(the_repository);\n>  \t\tbreak;\n>  \tcase ADD_EXEC:\n> -\t\tret = sequencer_add_exec_commands(the_repository, cmd);\n> +\t\tret = sequencer_add_exec_commands(the_repository, &commands);\n>  \t\tbreak;\n>  \tdefault:\n>  \t\tBUG(\"invalid command '%d'\", command);\n> diff --git a/sequencer.c b/sequencer.c\n> index 266f80d704..3a90b419d7 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -4446,25 +4446,27 @@ int sequencer_make_script(struct repository *r, FILE *out,\n>  \treturn 0;\n>  }\n>  \n> -/*\n> - * Add commands after pick and (series of) squash/fixup commands\n> - * in the todo list.\n> - */\n> -int sequencer_add_exec_commands(struct repository *r,\n> -\t\t\t\tconst char *commands)\n> +static void todo_list_add_exec_commands(struct todo_list *todo_list,\n> +\t\t\t\t\tstruct string_list *commands)\n>  {\n> -\tconst char *todo_file = rebase_path_todo();\n> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n> -\tstruct strbuf *buf = &todo_list.buf;\n> -\tsize_t offset = 0, commands_len = strlen(commands);\n> -\tint i, insert;\n> +\tstruct strbuf *buf = &todo_list->buf;\n> +\tsize_t base_offset = buf->len;\n> +\tint i, insert, nr = 0, alloc = 0;\n> +\tstruct todo_item *items = NULL, *base_items = NULL;\n>  \n> -\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> -\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n> +\tbase_items = xcalloc(commands->nr, sizeof(struct todo_item));\n> +\tfor (i = 0; i < commands->nr; ++i) {\n> +\t\tsize_t command_len = strlen(commands->items[i].string);\n>  \n> -\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n> -\t\ttodo_list_release(&todo_list);\n> -\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> +\t\tstrbuf_addstr(buf, commands->items[i].string);\n> +\t\tstrbuf_addch(buf, '\\n');\n> +\n> +\t\tbase_items[i].command = TODO_EXEC;\n> +\t\tbase_items[i].offset_in_buf = base_offset;\n> +\t\tbase_items[i].arg_offset = base_offset + strlen(\"exec \");\n> +\t\tbase_items[i].arg_len = command_len - strlen(\"exec \");\n> +\n> +\t\tbase_offset += command_len + 1;\n>  \t}\n>  \n>  \t/*\n> @@ -4473,38 +4475,62 @@ int sequencer_add_exec_commands(struct repository *r,\n>  \t * those chains if there are any.\n>  \t */\n>  \tinsert = -1;\n> -\tfor (i = 0; i < todo_list.nr; i++) {\n> -\t\tenum todo_command command = todo_list.items[i].command;\n> -\n> -\t\tif (insert >= 0) {\n> -\t\t\t/* skip fixup/squash chains */\n> -\t\t\tif (command == TODO_COMMENT)\n> -\t\t\t\tcontinue;\n> -\t\t\telse if (is_fixup(command)) {\n> -\t\t\t\tinsert = i + 1;\n> -\t\t\t\tcontinue;\n> -\t\t\t}\n> -\t\t\tstrbuf_insert(buf,\n> -\t\t\t\t      todo_list.items[insert].offset_in_buf +\n> -\t\t\t\t      offset, commands, commands_len);\n\nIn a todo list that looks like\npick abc message\n#pick cde empty commit\nThis inserts the exec command for the first pick above the commented out \npick. I think your translation puts it below the commented out pick as \nit ignores the value of insert. I think it's probably easiest to add an \nINSERT_ARRAY macro to insert it in the right place. An alternative might \nbe to track the last insert position and only copy commands across when \nthere is another exec to insert but that might get complicated in cases \nsuch as\n\npick abc message\n#squash cde squash! message //empty commit for rewording\nfixup 123 fixup! message\n#pick 456 empty commit\n\nBest Wishes\n\nPhillip\n\n> -\t\t\toffset += commands_len;\n> +\tfor (i = 0; i < todo_list->nr; i++) {\n> +\t\tenum todo_command command = todo_list->items[i].command;\n> +\t\tif (insert >= 0 && command != TODO_COMMENT && !is_fixup(command)) {\n> +\t\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n> +\t\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n> +\t\t\tnr += commands->nr;\n>  \t\t\tinsert = -1;\n>  \t\t}\n>  \n> -\t\tif (command == TODO_PICK || command == TODO_MERGE)\n> +\t\tALLOC_GROW(items, nr + 1, alloc);\n> +\t\titems[nr++] = todo_list->items[i];\n> +\n> +\t\tif (command == TODO_PICK || command == TODO_MERGE || is_fixup(command))\n>  \t\t\tinsert = i + 1;\n>  \t}\n>  \n>  \t/* insert or append final <commands> */\n> -\tif (insert >= 0 && insert < todo_list.nr)\n> -\t\tstrbuf_insert(buf, todo_list.items[insert].offset_in_buf +\n> -\t\t\t      offset, commands, commands_len);\n> -\telse if (insert >= 0 || !offset)\n> -\t\tstrbuf_add(buf, commands, commands_len);\n> +\tif (insert >= 0 || nr == todo_list->nr) {\n> +\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n> +\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n> +\t\tnr += commands->nr;\n> +\t}\n> +\n> +\tfree(base_items);\n> +\tFREE_AND_NULL(todo_list->items);\n> +\ttodo_list->items = items;\n> +\ttodo_list->nr = nr;\n> +\ttodo_list->alloc = alloc;> +}\n>  \n> -\ti = write_message(buf->buf, buf->len, todo_file, 0);\n> +/*\n> + * Add commands after pick and (series of) squash/fixup commands\n> + * in the todo list.\n> + */\n> +int sequencer_add_exec_commands(struct repository *r,\n> +\t\t\t\tstruct string_list *commands)\n> +{\n> +\tconst char *todo_file = rebase_path_todo();\n> +\tstruct todo_list todo_list = TODO_LIST_INIT;\n> +\tint res;\n> +\n> +\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> +\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> +\n> +\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n> +\t\ttodo_list_release(&todo_list);\n> +\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> +\t}\n> +\n> +\ttodo_list_add_exec_commands(&todo_list, commands);\n> +\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n>  \ttodo_list_release(&todo_list);\n> -\treturn i;\n> +\n> +\tif (res)\n> +\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n> +\treturn 0;\n>  }\n>  \n>  static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n> @@ -4735,7 +4761,7 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n>  \n>  int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n>  \t\t    const char *shortrevisions, const char *onto_name,\n> -\t\t    const char *onto, const char *orig_head, const char *cmd,\n> +\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n>  \t\t    unsigned autosquash)\n>  {\n>  \tconst char *shortonto, *todo_file = rebase_path_todo();\n> @@ -4754,8 +4780,8 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n>  \tif (autosquash && rearrange_squash(r))\n>  \t\treturn -1;\n>  \n> -\tif (cmd && *cmd)\n> -\t\tsequencer_add_exec_commands(r, cmd);\n> +\tif (commands->nr)\n> +\t\tsequencer_add_exec_commands(r, commands);\n>  \n>  \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n>  \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> diff --git a/sequencer.h b/sequencer.h\n> index 1de97f188d..e79f03e213 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -146,12 +146,13 @@ int sequencer_make_script(struct repository *r, FILE *out, int argc,\n>  \t\t\t  const char **argv,\n>  \t\t\t  unsigned flags);\n>  \n> -int sequencer_add_exec_commands(struct repository *r, const char *command);\n> +int sequencer_add_exec_commands(struct repository *r,\n> +\t\t\t\tstruct string_list *commands);\n>  int transform_todo_file(struct repository *r, unsigned flags);\n>  int check_todo_list_from_file(struct repository *r);\n>  int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n>  \t\t    const char *shortrevisions, const char *onto_name,\n> -\t\t    const char *onto, const char *orig_head, const char *cmd,\n> +\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n>  \t\t    unsigned autosquash);\n>  int rearrange_squash(struct repository *r);\n>  \n> \n\n"},{"id":"368264","messageId":"c5e3c1cc-12fa-ddf6-7008-ae47659ddc19@gmail.com","threadId":"49504","inReplyTo":"434cf0f7-1df6-7966-b460-e69e8f8b5e99@talktalk.net","subject":"Re: [PATCH v6 07/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-01-31T20:37:52Z","receivedAt":"2019-01-31T20:38:00Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Hi Phillip,\n\nLe 31/01/2019 à 15:30, Phillip Wood a écrit :\n> Hi Alban\n> \n> On 29/01/2019 15:01, Alban Gruin wrote:\n>> This refactors sequencer_add_exec_commands() to work on a todo_list to\n>> avoid redundant reads and writes to the disk.\n>>\n>> Instead of inserting the `exec' commands between the other commands and\n>> re-parsing the buffer at the end, they are appended to the buffer once,\n>> and a new list of items is created.  Items from the old list are copied\n>> across and new `exec' items are appended when necessary.  This\n>> eliminates the need to reparse the buffer, but this also means we have\n>> to use todo_list_write_to_disk() to write the file.\n>>\n>> todo_list_add_exec_commands() and sequencer_add_exec_commands() are\n>> modified to take a string list instead of a string -- one item for each\n>> command.  This makes it easier to insert a new command to the todo list\n>> for each command to execute.\n>>\n>> sequencer_add_exec_commands() still reads the todo list from the disk,\n>> as it is needed by rebase -p.\n>>\n>> complete_action() still uses sequencer_add_exec_commands() for now.\n>> This will be changed in a future commit.\n>>\n>> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n>> ---\n>>  builtin/rebase--interactive.c |  15 +++--\n>>  sequencer.c                   | 110 +++++++++++++++++++++-------------\n>>  sequencer.h                   |   5 +-\n>>  3 files changed, 82 insertions(+), 48 deletions(-)\n>>\n>> diff --git a/builtin/rebase--interactive.c\n>> b/builtin/rebase--interactive.c\n>> index df19ccaeb9..53056ee713 100644\n>> --- a/builtin/rebase--interactive.c\n>> +++ b/builtin/rebase--interactive.c\n>> @@ -65,7 +65,7 @@ static int do_interactive_rebase(struct replay_opts\n>> *opts, unsigned flags,\n>>                   const char *onto, const char *onto_name,\n>>                   const char *squash_onto, const char *head_name,\n>>                   const char *restrict_revision, char *raw_strategies,\n>> -                 const char *cmd, unsigned autosquash)\n>> +                 struct string_list *commands, unsigned autosquash)\n>>  {\n>>      int ret;\n>>      const char *head_hash = NULL;\n>> @@ -116,7 +116,7 @@ static int do_interactive_rebase(struct\n>> replay_opts *opts, unsigned flags,\n>>          discard_cache();\n>>          ret = complete_action(the_repository, opts, flags,\n>>                        shortrevisions, onto_name, onto,\n>> -                      head_hash, cmd, autosquash);\n>> +                      head_hash, commands, autosquash);\n>>      }\n>>  \n>>      free(revisions);\n>> @@ -139,6 +139,7 @@ int cmd_rebase__interactive(int argc, const char\n>> **argv, const char *prefix)\n>>      const char *onto = NULL, *onto_name = NULL, *restrict_revision =\n>> NULL,\n>>          *squash_onto = NULL, *upstream = NULL, *head_name = NULL,\n>>          *switch_to = NULL, *cmd = NULL;\n>> +    struct string_list commands = STRING_LIST_INIT_DUP;\n>>      char *raw_strategies = NULL;\n>>      enum {\n>>          NONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,\n>> @@ -221,6 +222,12 @@ int cmd_rebase__interactive(int argc, const char\n>> **argv, const char *prefix)\n>>          warning(_(\"--[no-]rebase-cousins has no effect without \"\n>>                \"--rebase-merges\"));\n>>  \n>> +    if (cmd && *cmd) {\n>> +        string_list_split(&commands, cmd, '\\n', -1);\n> \n> This whole splitting and later skipping 'exec ' is a bit of a shame - it\n> would be much nicer if we could just have one exec command per -x option\n> but I think that is outside the scope of this series (If I have time I'd\n> like to look at calling do_interactive_rebase() directly from\n> builtin/rebase.c without forking rebase--interactive).\n> \n\nYes, I completely agree with you.  I thought to do this in preparation\nto drop rebase -r.\n\n>> +        if (strlen(commands.items[commands.nr - 1].string) == 0)\n> \n> I'd be tempted just to test the string using !* rather than calling\n> strlen. \n>\n\nRight.  I’m still not used to this pattern.\n\n> Also is there ever a case where the last string isn't empty?\n\nI don’t think so.  When rebase.c prepares the arguments for\nrebase--interactive, it always add a newline at the end[1].  Do you want\nme to drop this check?\n\n\n>> +            --commands.nr;\n>> +    }\n>> +\n>>      switch (command) {\n>>      case NONE:\n>>          if (!onto && !upstream)\n>> @@ -228,7 +235,7 @@ int cmd_rebase__interactive(int argc, const char\n>> **argv, const char *prefix)\n>>  \n>>          ret = do_interactive_rebase(&opts, flags, switch_to,\n>> upstream, onto,\n>>                          onto_name, squash_onto, head_name,\n>> restrict_revision,\n>> -                        raw_strategies, cmd, autosquash);\n>> +                        raw_strategies, &commands, autosquash);\n>>          break;\n>>      case SKIP: {\n>>          struct string_list merge_rr = STRING_LIST_INIT_DUP;\n>> @@ -262,7 +269,7 @@ int cmd_rebase__interactive(int argc, const char\n>> **argv, const char *prefix)\n>>          ret = rearrange_squash(the_repository);\n>>          break;\n>>      case ADD_EXEC:\n>> -        ret = sequencer_add_exec_commands(the_repository, cmd);\n>> +        ret = sequencer_add_exec_commands(the_repository, &commands);\n>>          break;\n>>      default:\n>>          BUG(\"invalid command '%d'\", command);\n>> diff --git a/sequencer.c b/sequencer.c\n>> index 266f80d704..3a90b419d7 100644\n>> --- a/sequencer.c\n>> +++ b/sequencer.c\n>> @@ -4446,25 +4446,27 @@ int sequencer_make_script(struct repository\n>> *r, FILE *out,\n>>      return 0;\n>>  }\n>>  \n>> -/*\n>> - * Add commands after pick and (series of) squash/fixup commands\n>> - * in the todo list.\n>> - */\n>> -int sequencer_add_exec_commands(struct repository *r,\n>> -                const char *commands)\n>> +static void todo_list_add_exec_commands(struct todo_list *todo_list,\n>> +                    struct string_list *commands)\n>>  {\n>> -    const char *todo_file = rebase_path_todo();\n>> -    struct todo_list todo_list = TODO_LIST_INIT;\n>> -    struct strbuf *buf = &todo_list.buf;\n>> -    size_t offset = 0, commands_len = strlen(commands);\n>> -    int i, insert;\n>> +    struct strbuf *buf = &todo_list->buf;\n>> +    size_t base_offset = buf->len;\n>> +    int i, insert, nr = 0, alloc = 0;\n>> +    struct todo_item *items = NULL, *base_items = NULL;\n>>  \n>> -    if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>> -        return error(_(\"could not read '%s'.\"), todo_file);\n>> +    base_items = xcalloc(commands->nr, sizeof(struct todo_item));\n>> +    for (i = 0; i < commands->nr; ++i) {\n>> +        size_t command_len = strlen(commands->items[i].string);\n>>  \n>> -    if (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n>> -        todo_list_release(&todo_list);\n>> -        return error(_(\"unusable todo list: '%s'\"), todo_file);\n>> +        strbuf_addstr(buf, commands->items[i].string);\n>> +        strbuf_addch(buf, '\\n');\n>> +\n>> +        base_items[i].command = TODO_EXEC;\n>> +        base_items[i].offset_in_buf = base_offset;\n>> +        base_items[i].arg_offset = base_offset + strlen(\"exec \");\n>> +        base_items[i].arg_len = command_len - strlen(\"exec \");\n>> +\n>> +        base_offset += command_len + 1;\n>>      }\n>>  \n>>      /*\n>> @@ -4473,38 +4475,62 @@ int sequencer_add_exec_commands(struct\n>> repository *r,\n>>       * those chains if there are any.\n>>       */\n>>      insert = -1;\n>> -    for (i = 0; i < todo_list.nr; i++) {\n>> -        enum todo_command command = todo_list.items[i].command;\n>> -\n>> -        if (insert >= 0) {\n>> -            /* skip fixup/squash chains */\n>> -            if (command == TODO_COMMENT)\n>> -                continue;\n>> -            else if (is_fixup(command)) {\n>> -                insert = i + 1;\n>> -                continue;\n>> -            }\n>> -            strbuf_insert(buf,\n>> -                      todo_list.items[insert].offset_in_buf +\n>> -                      offset, commands, commands_len);\n> \n> In a todo list that looks like\n> pick abc message\n> #pick cde empty commit\n> This inserts the exec command for the first pick above the commented out\n> pick. I think your translation puts it below the commented out pick as\n> it ignores the value of insert. I think it's probably easiest to add an\n> INSERT_ARRAY macro to insert it in the right place. An alternative might\n> be to track the last insert position and only copy commands across when\n> there is another exec to insert but that might get complicated in cases\n> such as\n> \n> pick abc message\n> #squash cde squash! message //empty commit for rewording\n> fixup 123 fixup! message\n> #pick 456 empty commit\n> \n\nI could do this with MOVE_ARRAY(), no?\n\n> Best Wishes\n> \n> Phillip\n> \n\n[1] https://github.com/git/git/blob/master/builtin/rebase.c#L1182-L1191\n\nCheers,\nAlban\n\n\n"},{"id":"368268","messageId":"5f3c9739-8b12-bf27-681d-5b3563f1c75f@talktalk.net","threadId":"49504","inReplyTo":"c5e3c1cc-12fa-ddf6-7008-ae47659ddc19@gmail.com","subject":"Re: [PATCH v6 07/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-01-31T20:46:25Z","receivedAt":"2019-01-31T20:46:30Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nOn 31/01/2019 20:37, Alban Gruin wrote:\n> Hi Phillip,\n> \n> Le 31/01/2019 à 15:30, Phillip Wood a écrit :\n>> Hi Alban\n>>\n>> On 29/01/2019 15:01, Alban Gruin wrote:\n>>> This refactors sequencer_add_exec_commands() to work on a todo_list to\n>>> avoid redundant reads and writes to the disk.\n>>>\n>>> Instead of inserting the `exec' commands between the other commands and\n>>> re-parsing the buffer at the end, they are appended to the buffer once,\n>>> and a new list of items is created.  Items from the old list are copied\n>>> across and new `exec' items are appended when necessary.  This\n>>> eliminates the need to reparse the buffer, but this also means we have\n>>> to use todo_list_write_to_disk() to write the file.\n>>>\n>>> todo_list_add_exec_commands() and sequencer_add_exec_commands() are\n>>> modified to take a string list instead of a string -- one item for each\n>>> command.  This makes it easier to insert a new command to the todo list\n>>> for each command to execute.\n>>>\n>>> sequencer_add_exec_commands() still reads the todo list from the disk,\n>>> as it is needed by rebase -p.\n>>>\n>>> complete_action() still uses sequencer_add_exec_commands() for now.\n>>> This will be changed in a future commit.\n>>>\n>>> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n>>> ---\n>>>   builtin/rebase--interactive.c |  15 +++--\n>>>   sequencer.c                   | 110 +++++++++++++++++++++-------------\n>>>   sequencer.h                   |   5 +-\n>>>   3 files changed, 82 insertions(+), 48 deletions(-)\n>>>\n>>> diff --git a/builtin/rebase--interactive.c\n>>> b/builtin/rebase--interactive.c\n>>> index df19ccaeb9..53056ee713 100644\n>>> --- a/builtin/rebase--interactive.c\n>>> +++ b/builtin/rebase--interactive.c\n>>> @@ -65,7 +65,7 @@ static int do_interactive_rebase(struct replay_opts\n>>> *opts, unsigned flags,\n>>>                    const char *onto, const char *onto_name,\n>>>                    const char *squash_onto, const char *head_name,\n>>>                    const char *restrict_revision, char *raw_strategies,\n>>> -                 const char *cmd, unsigned autosquash)\n>>> +                 struct string_list *commands, unsigned autosquash)\n>>>   {\n>>>       int ret;\n>>>       const char *head_hash = NULL;\n>>> @@ -116,7 +116,7 @@ static int do_interactive_rebase(struct\n>>> replay_opts *opts, unsigned flags,\n>>>           discard_cache();\n>>>           ret = complete_action(the_repository, opts, flags,\n>>>                         shortrevisions, onto_name, onto,\n>>> -                      head_hash, cmd, autosquash);\n>>> +                      head_hash, commands, autosquash);\n>>>       }\n>>>   \n>>>       free(revisions);\n>>> @@ -139,6 +139,7 @@ int cmd_rebase__interactive(int argc, const char\n>>> **argv, const char *prefix)\n>>>       const char *onto = NULL, *onto_name = NULL, *restrict_revision =\n>>> NULL,\n>>>           *squash_onto = NULL, *upstream = NULL, *head_name = NULL,\n>>>           *switch_to = NULL, *cmd = NULL;\n>>> +    struct string_list commands = STRING_LIST_INIT_DUP;\n>>>       char *raw_strategies = NULL;\n>>>       enum {\n>>>           NONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,\n>>> @@ -221,6 +222,12 @@ int cmd_rebase__interactive(int argc, const char\n>>> **argv, const char *prefix)\n>>>           warning(_(\"--[no-]rebase-cousins has no effect without \"\n>>>                 \"--rebase-merges\"));\n>>>   \n>>> +    if (cmd && *cmd) {\n>>> +        string_list_split(&commands, cmd, '\\n', -1);\n>>\n>> This whole splitting and later skipping 'exec ' is a bit of a shame - it\n>> would be much nicer if we could just have one exec command per -x option\n>> but I think that is outside the scope of this series (If I have time I'd\n>> like to look at calling do_interactive_rebase() directly from\n>> builtin/rebase.c without forking rebase--interactive).\n>>\n> \n> Yes, I completely agree with you.  I thought to do this in preparation\n> to drop rebase -r.\n> \n>>> +        if (strlen(commands.items[commands.nr - 1].string) == 0)\n>>\n>> I'd be tempted just to test the string using !* rather than calling\n>> strlen.\n>>\n> \n> Right.  I’m still not used to this pattern.\n> \n>> Also is there ever a case where the last string isn't empty?\n> \n> I don’t think so.  When rebase.c prepares the arguments for\n> rebase--interactive, it always add a newline at the end[1].  Do you want\n> me to drop this check?\n\nI think that would be clearer\n\n> \n> \n>>> +            --commands.nr;\n>>> +    }\n>>> +\n>>>       switch (command) {\n>>>       case NONE:\n>>>           if (!onto && !upstream)\n>>> @@ -228,7 +235,7 @@ int cmd_rebase__interactive(int argc, const char\n>>> **argv, const char *prefix)\n>>>   \n>>>           ret = do_interactive_rebase(&opts, flags, switch_to,\n>>> upstream, onto,\n>>>                           onto_name, squash_onto, head_name,\n>>> restrict_revision,\n>>> -                        raw_strategies, cmd, autosquash);\n>>> +                        raw_strategies, &commands, autosquash);\n>>>           break;\n>>>       case SKIP: {\n>>>           struct string_list merge_rr = STRING_LIST_INIT_DUP;\n>>> @@ -262,7 +269,7 @@ int cmd_rebase__interactive(int argc, const char\n>>> **argv, const char *prefix)\n>>>           ret = rearrange_squash(the_repository);\n>>>           break;\n>>>       case ADD_EXEC:\n>>> -        ret = sequencer_add_exec_commands(the_repository, cmd);\n>>> +        ret = sequencer_add_exec_commands(the_repository, &commands);\n>>>           break;\n>>>       default:\n>>>           BUG(\"invalid command '%d'\", command);\n>>> diff --git a/sequencer.c b/sequencer.c\n>>> index 266f80d704..3a90b419d7 100644\n>>> --- a/sequencer.c\n>>> +++ b/sequencer.c\n>>> @@ -4446,25 +4446,27 @@ int sequencer_make_script(struct repository\n>>> *r, FILE *out,\n>>>       return 0;\n>>>   }\n>>>   \n>>> -/*\n>>> - * Add commands after pick and (series of) squash/fixup commands\n>>> - * in the todo list.\n>>> - */\n>>> -int sequencer_add_exec_commands(struct repository *r,\n>>> -                const char *commands)\n>>> +static void todo_list_add_exec_commands(struct todo_list *todo_list,\n>>> +                    struct string_list *commands)\n>>>   {\n>>> -    const char *todo_file = rebase_path_todo();\n>>> -    struct todo_list todo_list = TODO_LIST_INIT;\n>>> -    struct strbuf *buf = &todo_list.buf;\n>>> -    size_t offset = 0, commands_len = strlen(commands);\n>>> -    int i, insert;\n>>> +    struct strbuf *buf = &todo_list->buf;\n>>> +    size_t base_offset = buf->len;\n>>> +    int i, insert, nr = 0, alloc = 0;\n>>> +    struct todo_item *items = NULL, *base_items = NULL;\n>>>   \n>>> -    if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>>> -        return error(_(\"could not read '%s'.\"), todo_file);\n>>> +    base_items = xcalloc(commands->nr, sizeof(struct todo_item));\n>>> +    for (i = 0; i < commands->nr; ++i) {\n>>> +        size_t command_len = strlen(commands->items[i].string);\n>>>   \n>>> -    if (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n>>> -        todo_list_release(&todo_list);\n>>> -        return error(_(\"unusable todo list: '%s'\"), todo_file);\n>>> +        strbuf_addstr(buf, commands->items[i].string);\n>>> +        strbuf_addch(buf, '\\n');\n>>> +\n>>> +        base_items[i].command = TODO_EXEC;\n>>> +        base_items[i].offset_in_buf = base_offset;\n>>> +        base_items[i].arg_offset = base_offset + strlen(\"exec \");\n>>> +        base_items[i].arg_len = command_len - strlen(\"exec \");\n>>> +\n>>> +        base_offset += command_len + 1;\n>>>       }\n>>>   \n>>>       /*\n>>> @@ -4473,38 +4475,62 @@ int sequencer_add_exec_commands(struct\n>>> repository *r,\n>>>        * those chains if there are any.\n>>>        */\n>>>       insert = -1;\n>>> -    for (i = 0; i < todo_list.nr; i++) {\n>>> -        enum todo_command command = todo_list.items[i].command;\n>>> -\n>>> -        if (insert >= 0) {\n>>> -            /* skip fixup/squash chains */\n>>> -            if (command == TODO_COMMENT)\n>>> -                continue;\n>>> -            else if (is_fixup(command)) {\n>>> -                insert = i + 1;\n>>> -                continue;\n>>> -            }\n>>> -            strbuf_insert(buf,\n>>> -                      todo_list.items[insert].offset_in_buf +\n>>> -                      offset, commands, commands_len);\n>>\n>> In a todo list that looks like\n>> pick abc message\n>> #pick cde empty commit\n>> This inserts the exec command for the first pick above the commented out\n>> pick. I think your translation puts it below the commented out pick as\n>> it ignores the value of insert. I think it's probably easiest to add an\n>> INSERT_ARRAY macro to insert it in the right place. An alternative might\n>> be to track the last insert position and only copy commands across when\n>> there is another exec to insert but that might get complicated in cases\n>> such as\n>>\n>> pick abc message\n>> #squash cde squash! message //empty commit for rewording\n>> fixup 123 fixup! message\n>> #pick 456 empty commit\n>>\n> \n> I could do this with MOVE_ARRAY(), no?\n\nYes, if you extend the array first then you could use MOVE_ARRAY() and \nCOPY_ARRAY() to move the comment down and then insert the exec commands \nso maybe we don't need a new macro after all.\n\nI've looked through most of the rest of this series (I think I've got \nthree patches left to check) and they all look fine, I'll try and look \nat the rest tomorrow, but if not I'll get round to it next week.\n\nBest Wishes\n\nPhillip\n\n>> Best Wishes\n>>\n>> Phillip\n>>\n> \n> [1] https://github.com/git/git/blob/master/builtin/rebase.c#L1182-L1191\n> \n> Cheers,\n> Alban\n> \n> \n\n"},{"id":"368321","messageId":"f3652f60-dde4-0e5a-50ab-81536af9bae0@talktalk.net","threadId":"49504","inReplyTo":"20190129150159.10588-15-alban.gruin@gmail.com","subject":"Re: [PATCH v6 14/16] rebase-interactive: rewrite edit_todo_list() to handle the initial edit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-02-01T11:03:41Z","receivedAt":"2019-02-01T11:04:04Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nThis looks good apart from some missing error handling.\n\nOn 29/01/2019 15:01, Alban Gruin wrote:\n> edit_todo_list() is changed to work on a todo_list, and to handle the\n> initial edition of the todo list (ie. making a backup of the todo\n> list).\n> \n> It does not check for dropped commits yet, as todo_list_check() does not\n> take the commits that have already been processed by the rebase (ie. the\n> todo list is edited in the middle of a rebase session).\n> \n> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n> ---\n>   builtin/rebase--interactive.c | 24 +++++++++++++++++-\n>   rebase-interactive.c          | 48 ++++++++++++++++++-----------------\n>   rebase-interactive.h          |  4 ++-\n>   sequencer.c                   |  3 +--\n>   sequencer.h                   |  1 +\n>   5 files changed, 53 insertions(+), 27 deletions(-)\n> \n> diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\n> index 2dbf8fc08b..645ac587f7 100644\n> --- a/builtin/rebase--interactive.c\n> +++ b/builtin/rebase--interactive.c\n> @@ -13,6 +13,28 @@ static GIT_PATH_FUNC(path_state_dir, \"rebase-merge/\")\n>   static GIT_PATH_FUNC(path_squash_onto, \"rebase-merge/squash-onto\")\n>   static GIT_PATH_FUNC(path_interactive, \"rebase-merge/interactive\")\n>   \n> +static int edit_todo_file(unsigned flags)\n> +{\n> +\tconst char *todo_file = rebase_path_todo();\n> +\tstruct todo_list todo_list = TODO_LIST_INIT,\n> +\t\tnew_todo = TODO_LIST_INIT;\n> +\n> +\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> +\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> +\n> +\tstrbuf_stripspace(&todo_list.buf, 1);\n> +\tif (!edit_todo_list(the_repository, &todo_list,\n> +\t\t\t    &new_todo, NULL, NULL, flags) &&\n> +\t    todo_list_write_to_file(the_repository, &new_todo, todo_file, NULL, NULL,\n> +\t\t\t\t    -1, flags & ~(TODO_LIST_SHORTEN_IDS)) < 0)\n> +\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n\nIf edit_todo_list() fails then the function returns 0. I think you need \nto do\n\nif (edit_todo_list() || todo_list_write_file())\n\treturn error...\n\ntodo_list_write_file() forwards the return value of write_message() \nwhich is 0/-1 so there is no need for the '< 0'\n\n> +\n> +\ttodo_list_release(&todo_list);\n> +\ttodo_list_release(&new_todo);\n> +\n> +\treturn 0;\n> +}\n> +\n>   static int get_revision_ranges(const char *upstream, const char *onto,\n>   \t\t\t       const char **head_hash,\n>   \t\t\t       char **revisions, char **shortrevisions)\n> @@ -242,7 +264,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>   \t\tbreak;\n>   \t}\n>   \tcase EDIT_TODO:\n> -\t\tret = edit_todo_list(the_repository, flags);\n> +\t\tret = edit_todo_file(flags);\n>   \t\tbreak;\n>   \tcase SHOW_CURRENT_PATCH: {\n>   \t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n> diff --git a/rebase-interactive.c b/rebase-interactive.c\n> index 807f8370db..3301efbe52 100644\n> --- a/rebase-interactive.c\n> +++ b/rebase-interactive.c\n> @@ -87,35 +87,37 @@ void append_todo_help(unsigned keep_empty, int command_count,\n>   \t}\n>   }\n>   \n> -int edit_todo_list(struct repository *r, unsigned flags)\n> +int edit_todo_list(struct repository *r, struct todo_list *todo_list,\n> +\t\t   struct todo_list *new_todo, const char *shortrevisions,\n> +\t\t   const char *shortonto, unsigned flags)\n>   {\n>   \tconst char *todo_file = rebase_path_todo();\n> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n> -\tint res = 0;\n> -\n> -\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> -\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> -\n> -\tstrbuf_stripspace(&todo_list.buf, 1);\n> -\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n> -\tif (todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n> -\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP)) {\n> -\t\ttodo_list_release(&todo_list);\n> -\t\treturn -1;\n> +\tunsigned initial = shortrevisions && shortonto;\n> +\n> +\tif (initial) {\n> +\t\ttodo_list_write_to_file(r, todo_list, todo_file, shortrevisions, shortonto,\n> +\t\t\t\t\t-1, flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP);\n\nThis has lost the error handling when we cannot write the file\n\n> +\n> +\t\tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n> +\t\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n> +\t\t\t\t     rebase_path_todo_backup());\n> +\t} else {\n> +\t\ttodo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n> +\t\ttodo_list_write_to_file(r, todo_list, todo_file, NULL, NULL, -1,\n> +\t\t\t\t\tflags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP);\n\nerror handling again\n\n>   \t}\n>   \n> -\tstrbuf_reset(&todo_list.buf);\n> -\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n> -\t\ttodo_list_release(&todo_list);\n> -\t\treturn -1;\n> -\t}\n> +\tif (launch_sequence_editor(todo_file, &new_todo->buf, NULL))\n> +\t\treturn -2;\n>   \n> -\tif (!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list))\n> -\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n> -\t\t\t\t\t      flags & ~(TODO_LIST_SHORTEN_IDS));\n> +\tstrbuf_stripspace(&new_todo->buf, 1);\n> +\tif (initial && new_todo->buf.len == 0)\n> +\t\treturn -3;\n>   \n> -\ttodo_list_release(&todo_list);\n> -\treturn res;\n> +\tif (!initial)\n> +\t\ttodo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo);\n\nerror handling. Also why don't we try parse the file for the initial \nedit - is it done somewhere else?\n\nBest Wishes\n\nPhillip\n\n> +\n> +\treturn 0;\n>   }\n>   \n>   define_commit_slab(commit_seen, unsigned char);\n> diff --git a/rebase-interactive.h b/rebase-interactive.h\n> index 0e5925e3aa..44dbb06311 100644\n> --- a/rebase-interactive.h\n> +++ b/rebase-interactive.h\n> @@ -8,7 +8,9 @@ struct todo_list;\n>   void append_todo_help(unsigned keep_empty, int command_count,\n>   \t\t      const char *shortrevisions, const char *shortonto,\n>   \t\t      struct strbuf *buf);\n> -int edit_todo_list(struct repository *r, unsigned flags);\n> +int edit_todo_list(struct repository *r, struct todo_list *todo_list,\n> +\t\t   struct todo_list *new_todo, const char *shortrevisions,\n> +\t\t   const char *shortonto, unsigned flags);\n>   int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n>   \n>   #endif\n> diff --git a/sequencer.c b/sequencer.c\n> index 92de982bc4..8f47f0cf39 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -55,8 +55,7 @@ static GIT_PATH_FUNC(rebase_path, \"rebase-merge\")\n>    * file and written to the tail of 'done'.\n>    */\n>   GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n> -static GIT_PATH_FUNC(rebase_path_todo_backup,\n> -\t\t     \"rebase-merge/git-rebase-todo.backup\")\n> +GIT_PATH_FUNC(rebase_path_todo_backup, \"rebase-merge/git-rebase-todo.backup\")\n>   \n>   /*\n>    * The rebase command lines that have already been processed. A line\n> diff --git a/sequencer.h b/sequencer.h\n> index c5bee8124c..68acab980b 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -10,6 +10,7 @@ struct repository;\n>   const char *git_path_commit_editmsg(void);\n>   const char *git_path_seq_dir(void);\n>   const char *rebase_path_todo(void);\n> +const char *rebase_path_todo_backup(void);\n>   \n>   #define APPEND_SIGNOFF_DEDUP (1u << 0)\n>   \n> \n\n"},{"id":"368324","messageId":"d00c11bc-013e-5839-9766-6999e99edf34@talktalk.net","threadId":"49504","inReplyTo":"20190129150159.10588-17-alban.gruin@gmail.com","subject":"Re: [PATCH v6 16/16] rebase--interactive: move transform_todo_file() to rebase--interactive.c","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-02-01T11:15:23Z","receivedAt":"2019-02-01T11:15:36Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nOn 29/01/2019 15:01, Alban Gruin wrote:\n> As transform_todo_file() is only needed inside of rebase--interactive.c,\n> it is moved there from sequencer.c.\n\nI think I'd prefer to minimize the code under builtin and move this to \nrebase-interactive.c when it is modified earlier in the series. (I'd be \nquite happy if all the files in builtin just consisted of some option \nparsing followed by a call to run_git_foo() which resides in libgit)\n\nAlso I wonder if we should be moving more functions (e.g. \ntodo_list_write_file() and possibly add_exec_commands(), \nrearrange_squash() and the script generation) from sequencer.c to \nrebase-interactive.c when they're rewritten (possibly in a separate \ncommit for ease of review) but I haven't looked if this is practical or \nif there are some dependencies that make that tricky. Unless there are \nsome simple cases it should probably be a separate series.\n\nThanks for working on this series, it's great to see the todo list \nhandling becoming more efficient.\n\nBest Wishes\n\nPhillip\n\n> \n> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n> ---\n>   builtin/rebase--interactive.c | 26 +++++++++++++++++++++++++-\n>   sequencer.c                   | 23 -----------------------\n>   sequencer.h                   |  1 -\n>   3 files changed, 25 insertions(+), 25 deletions(-)\n> \n> diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\n> index 645ac587f7..7f1e88a087 100644\n> --- a/builtin/rebase--interactive.c\n> +++ b/builtin/rebase--interactive.c\n> @@ -35,6 +35,30 @@ static int edit_todo_file(unsigned flags)\n>   \treturn 0;\n>   }\n>   \n> +static int transform_todo_file(unsigned flags)\n> +{\n> +\tconst char *todo_file = rebase_path_todo();\n> +\tstruct todo_list todo_list = TODO_LIST_INIT;\n> +\tint res;\n> +\n> +\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> +\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> +\n> +\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n> +\t\t\t\t\t&todo_list)) {\n> +\t\ttodo_list_release(&todo_list);\n> +\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> +\t}\n> +\n> +\tres = todo_list_write_to_file(the_repository, &todo_list, todo_file,\n> +\t\t\t\t      NULL, NULL, -1, flags);\n> +\ttodo_list_release(&todo_list);\n> +\n> +\tif (res)\n> +\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n> +\treturn 0;\n> +}\n> +\n>   static int get_revision_ranges(const char *upstream, const char *onto,\n>   \t\t\t       const char **head_hash,\n>   \t\t\t       char **revisions, char **shortrevisions)\n> @@ -277,7 +301,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>   \t}\n>   \tcase SHORTEN_OIDS:\n>   \tcase EXPAND_OIDS:\n> -\t\tret = transform_todo_file(the_repository, flags);\n> +\t\tret = transform_todo_file(flags);\n>   \t\tbreak;\n>   \tcase CHECK_TODO_LIST:\n>   \t\tret = check_todo_list_from_file(the_repository);\n> diff --git a/sequencer.c b/sequencer.c\n> index 21b04e0642..5239700efc 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -4593,29 +4593,6 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n>   \treturn res;\n>   }\n>   \n> -int transform_todo_file(struct repository *r, unsigned flags)\n> -{\n> -\tconst char *todo_file = rebase_path_todo();\n> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n> -\tint res;\n> -\n> -\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> -\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> -\n> -\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n> -\t\ttodo_list_release(&todo_list);\n> -\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> -\t}\n> -\n> -\tres = todo_list_write_to_file(r, &todo_list, todo_file,\n> -\t\t\t\t      NULL, NULL, -1, flags);\n> -\ttodo_list_release(&todo_list);\n> -\n> -\tif (res)\n> -\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n> -\treturn 0;\n> -}\n> -\n>   static const char edit_todo_list_advice[] =\n>   N_(\"You can fix this with 'git rebase --edit-todo' \"\n>   \"and then run 'git rebase --continue'.\\n\"\n> diff --git a/sequencer.h b/sequencer.h\n> index 68acab980b..11afd47aa9 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -145,7 +145,6 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n>   \n>   int sequencer_add_exec_commands(struct repository *r,\n>   \t\t\t\tstruct string_list *commands);\n> -int transform_todo_file(struct repository *r, unsigned flags);\n>   int check_todo_list_from_file(struct repository *r);\n>   int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n>   \t\t    const char *shortrevisions, const char *onto_name,\n> \n\n"},{"id":"368331","messageId":"b95aab1f-25e4-be81-e8c3-5521fe68ef62@talktalk.net","threadId":"49504","inReplyTo":"434cf0f7-1df6-7966-b460-e69e8f8b5e99@talktalk.net","subject":"Re: [PATCH v6 07/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-02-01T14:51:33Z","receivedAt":"2019-02-01T14:51:38Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"On 31/01/2019 14:30, Phillip Wood wrote:\n> Hi Alban\n> \n> On 29/01/2019 15:01, Alban Gruin wrote:\n>> This refactors sequencer_add_exec_commands() to work on a todo_list to\n>> avoid redundant reads and writes to the disk.\n>>\n>> Instead of inserting the `exec' commands between the other commands and\n>> re-parsing the buffer at the end, they are appended to the buffer once,\n>> and a new list of items is created.  Items from the old list are copied\n>> across and new `exec' items are appended when necessary.  This\n>> eliminates the need to reparse the buffer, but this also means we have\n>> to use todo_list_write_to_disk() to write the file.\n>>\n>> todo_list_add_exec_commands() and sequencer_add_exec_commands() are\n>> modified to take a string list instead of a string -- one item for each\n>> command.  This makes it easier to insert a new command to the todo list\n>> for each command to execute.\n>>\n>> sequencer_add_exec_commands() still reads the todo list from the disk,\n>> as it is needed by rebase -p.\n>>\n>> complete_action() still uses sequencer_add_exec_commands() for now.\n>> This will be changed in a future commit.\n>>\n>> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n>> ---\n>>  builtin/rebase--interactive.c |  15 +++--\n>>  sequencer.c                   | 110 +++++++++++++++++++++-------------\n>>  sequencer.h                   |   5 +-\n>>  3 files changed, 82 insertions(+), 48 deletions(-)\n>>\n>> diff --git a/builtin/rebase--interactive.c \n>> b/builtin/rebase--interactive.c\n>> index df19ccaeb9..53056ee713 100644\n>> --- a/builtin/rebase--interactive.c\n>> +++ b/builtin/rebase--interactive.c\n>> @@ -65,7 +65,7 @@ static int do_interactive_rebase(struct replay_opts \n>> *opts, unsigned flags,\n>>                   const char *onto, const char *onto_name,\n>>                   const char *squash_onto, const char *head_name,\n>>                   const char *restrict_revision, char *raw_strategies,\n>> -                 const char *cmd, unsigned autosquash)\n>> +                 struct string_list *commands, unsigned autosquash)\n>>  {\n>>      int ret;\n>>      const char *head_hash = NULL;\n>> @@ -116,7 +116,7 @@ static int do_interactive_rebase(struct \n>> replay_opts *opts, unsigned flags,\n>>          discard_cache();\n>>          ret = complete_action(the_repository, opts, flags,\n>>                        shortrevisions, onto_name, onto,\n>> -                      head_hash, cmd, autosquash);\n>> +                      head_hash, commands, autosquash);\n>>      }\n>>\n>>      free(revisions);\n>> @@ -139,6 +139,7 @@ int cmd_rebase__interactive(int argc, const char \n>> **argv, const char *prefix)\n>>      const char *onto = NULL, *onto_name = NULL, *restrict_revision = \n>> NULL,\n>>          *squash_onto = NULL, *upstream = NULL, *head_name = NULL,\n>>          *switch_to = NULL, *cmd = NULL;\n>> +    struct string_list commands = STRING_LIST_INIT_DUP;\n>>      char *raw_strategies = NULL;\n>>      enum {\n>>          NONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,\n>> @@ -221,6 +222,12 @@ int cmd_rebase__interactive(int argc, const char \n>> **argv, const char *prefix)\n>>          warning(_(\"--[no-]rebase-cousins has no effect without \"\n>>                \"--rebase-merges\"));\n>>\n>> +    if (cmd && *cmd) {\n>> +        string_list_split(&commands, cmd, '\\n', -1);\n> \n> This whole splitting and later skipping 'exec ' is a bit of a shame - it \n> would be much nicer if we could just have one exec command per -x option \n> but I think that is outside the scope of this series (If I have time I'd \n> like to look at calling do_interactive_rebase() directly from \n> builtin/rebase.c without forking rebase--interactive).\n> \n>> +        if (strlen(commands.items[commands.nr - 1].string) == 0)\n> \n> I'd be tempted just to test the string using !* rather than calling \n> strlen. Also is there ever a case where the last string isn't empty?\n> \n>> +            --commands.nr;\n>> +    }\n>> +\n>>      switch (command) {\n>>      case NONE:\n>>          if (!onto && !upstream)\n>> @@ -228,7 +235,7 @@ int cmd_rebase__interactive(int argc, const char \n>> **argv, const char *prefix)\n>>\n>>          ret = do_interactive_rebase(&opts, flags, switch_to, \n>> upstream, onto,\n>>                          onto_name, squash_onto, head_name, \n>> restrict_revision,\n>> -                        raw_strategies, cmd, autosquash);\n>> +                        raw_strategies, &commands, autosquash);\n>>          break;\n>>      case SKIP: {\n>>          struct string_list merge_rr = STRING_LIST_INIT_DUP;\n>> @@ -262,7 +269,7 @@ int cmd_rebase__interactive(int argc, const char \n>> **argv, const char *prefix)\n>>          ret = rearrange_squash(the_repository);\n>>          break;\n>>      case ADD_EXEC:\n>> -        ret = sequencer_add_exec_commands(the_repository, cmd);\n>> +        ret = sequencer_add_exec_commands(the_repository, &commands);\n>>          break;\n>>      default:\n>>          BUG(\"invalid command '%d'\", command);\n>> diff --git a/sequencer.c b/sequencer.c\n>> index 266f80d704..3a90b419d7 100644\n>> --- a/sequencer.c\n>> +++ b/sequencer.c\n>> @@ -4446,25 +4446,27 @@ int sequencer_make_script(struct repository \n>> *r, FILE *out,\n>>      return 0;\n>>  }\n>>\n>> -/*\n>> - * Add commands after pick and (series of) squash/fixup commands\n>> - * in the todo list.\n>> - */\n>> -int sequencer_add_exec_commands(struct repository *r,\n>> -                const char *commands)\n>> +static void todo_list_add_exec_commands(struct todo_list *todo_list,\n>> +                    struct string_list *commands)\n>>  {\n>> -    const char *todo_file = rebase_path_todo();\n>> -    struct todo_list todo_list = TODO_LIST_INIT;\n>> -    struct strbuf *buf = &todo_list.buf;\n>> -    size_t offset = 0, commands_len = strlen(commands);\n>> -    int i, insert;\n>> +    struct strbuf *buf = &todo_list->buf;\n>> +    size_t base_offset = buf->len;\n>> +    int i, insert, nr = 0, alloc = 0;\n>> +    struct todo_item *items = NULL, *base_items = NULL;\n>>\n>> -    if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>> -        return error(_(\"could not read '%s'.\"), todo_file);\n>> +    base_items = xcalloc(commands->nr, sizeof(struct todo_item));\n>> +    for (i = 0; i < commands->nr; ++i) {\n>> +        size_t command_len = strlen(commands->items[i].string);\n>>\n>> -    if (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n>> -        todo_list_release(&todo_list);\n>> -        return error(_(\"unusable todo list: '%s'\"), todo_file);\n>> +        strbuf_addstr(buf, commands->items[i].string);\n>> +        strbuf_addch(buf, '\\n');\n>> +\n>> +        base_items[i].command = TODO_EXEC;\n>> +        base_items[i].offset_in_buf = base_offset;\n>> +        base_items[i].arg_offset = base_offset + strlen(\"exec \");\n>> +        base_items[i].arg_len = command_len - strlen(\"exec \");\n>> +\n>> +        base_offset += command_len + 1;\n>>      }\n>>\n>>      /*\n>> @@ -4473,38 +4475,62 @@ int sequencer_add_exec_commands(struct \n>> repository *r,\n>>       * those chains if there are any.\n>>       */\n>>      insert = -1;\n>> -    for (i = 0; i < todo_list.nr; i++) {\n>> -        enum todo_command command = todo_list.items[i].command;\n>> -\n>> -        if (insert >= 0) {\n>> -            /* skip fixup/squash chains */\n>> -            if (command == TODO_COMMENT)\n>> -                continue;\n>> -            else if (is_fixup(command)) {\n>> -                insert = i + 1;\n>> -                continue;\n>> -            }\n>> -            strbuf_insert(buf,\n>> -                      todo_list.items[insert].offset_in_buf +\n>> -                      offset, commands, commands_len);\n> \n> In a todo list that looks like\n> pick abc message\n> #pick cde empty commit\n> This inserts the exec command for the first pick above the commented out \n> pick. I think your translation puts it below the commented out pick as \n> it ignores the value of insert. I think it's probably easiest to add an \n> INSERT_ARRAY macro to insert it in the right place. An alternative might \n> be to track the last insert position and only copy commands across when \n> there is another exec to insert but that might get complicated in cases \n> such as\n> \n> pick abc message\n> #squash cde squash! message //empty commit for rewording\n> fixup 123 fixup! message\n> #pick 456 empty commit\n\nThinking about this, I'm not sure it can happen as the empty squash \ncommit will be commented out before rearrange_squash() is called so I \nthink it would actually look like\n\npick abc message\nfixup 123 fixup! message\n#pick cde squash! message\n#pick 456 empty commit\n\nSo I wonder if we can get away with treating insert as a flag and \ninserting exec commands when\ninsert && command == TODO_COMMENT\n\nWe could probably do with some tests for this to be sure.\n\nBest Wishes\n\nPhillip\n\n> \n> Best Wishes\n> \n> Phillip\n> \n>> -            offset += commands_len;\n>> +    for (i = 0; i < todo_list->nr; i++) {\n>> +        enum todo_command command = todo_list->items[i].command;\n>> +        if (insert >= 0 && command != TODO_COMMENT && \n>> !is_fixup(command)) {\n>> +            ALLOC_GROW(items, nr + commands->nr, alloc);\n>> +            COPY_ARRAY(items + nr, base_items, commands->nr);\n>> +            nr += commands->nr;\n>>              insert = -1;\n>>          }\n>>\n>> -        if (command == TODO_PICK || command == TODO_MERGE)\n>> +        ALLOC_GROW(items, nr + 1, alloc);\n>> +        items[nr++] = todo_list->items[i];\n>> +\n>> +        if (command == TODO_PICK || command == TODO_MERGE || \n>> is_fixup(command))\n>>              insert = i + 1;\n>>      }\n>>\n>>      /* insert or append final <commands> */\n>> -    if (insert >= 0 && insert < todo_list.nr)\n>> -        strbuf_insert(buf, todo_list.items[insert].offset_in_buf +\n>> -                  offset, commands, commands_len);\n>> -    else if (insert >= 0 || !offset)\n>> -        strbuf_add(buf, commands, commands_len);\n>> +    if (insert >= 0 || nr == todo_list->nr) {\n>> +        ALLOC_GROW(items, nr + commands->nr, alloc);\n>> +        COPY_ARRAY(items + nr, base_items, commands->nr);\n>> +        nr += commands->nr;\n>> +    }\n>> +\n>> +    free(base_items);\n>> +    FREE_AND_NULL(todo_list->items);\n>> +    todo_list->items = items;\n>> +    todo_list->nr = nr;\n>> +    todo_list->alloc = alloc;> +}\n>>\n>> -    i = write_message(buf->buf, buf->len, todo_file, 0);\n>> +/*\n>> + * Add commands after pick and (series of) squash/fixup commands\n>> + * in the todo list.\n>> + */\n>> +int sequencer_add_exec_commands(struct repository *r,\n>> +                struct string_list *commands)\n>> +{\n>> +    const char *todo_file = rebase_path_todo();\n>> +    struct todo_list todo_list = TODO_LIST_INIT;\n>> +    int res;\n>> +\n>> +    if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>> +        return error_errno(_(\"could not read '%s'.\"), todo_file);\n>> +\n>> +    if (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n>> +        todo_list_release(&todo_list);\n>> +        return error(_(\"unusable todo list: '%s'\"), todo_file);\n>> +    }\n>> +\n>> +    todo_list_add_exec_commands(&todo_list, commands);\n>> +    res = todo_list_write_to_file(r, &todo_list, todo_file, NULL, \n>> NULL, -1, 0);\n>>      todo_list_release(&todo_list);\n>> -    return i;\n>> +\n>> +    if (res)\n>> +        return error_errno(_(\"could not write '%s'.\"), todo_file);\n>> +    return 0;\n>>  }\n>>\n>>  static void todo_list_to_strbuf(struct repository *r, struct \n>> todo_list *todo_list,\n>> @@ -4735,7 +4761,7 @@ static int skip_unnecessary_picks(struct \n>> repository *r, struct object_id *output\n>>\n>>  int complete_action(struct repository *r, struct replay_opts *opts, \n>> unsigned flags,\n>>              const char *shortrevisions, const char *onto_name,\n>> -            const char *onto, const char *orig_head, const char *cmd,\n>> +            const char *onto, const char *orig_head, struct \n>> string_list *commands,\n>>              unsigned autosquash)\n>>  {\n>>      const char *shortonto, *todo_file = rebase_path_todo();\n>> @@ -4754,8 +4780,8 @@ int complete_action(struct repository *r, struct \n>> replay_opts *opts, unsigned fla\n>>      if (autosquash && rearrange_squash(r))\n>>          return -1;\n>>\n>> -    if (cmd && *cmd)\n>> -        sequencer_add_exec_commands(r, cmd);\n>> +    if (commands->nr)\n>> +        sequencer_add_exec_commands(r, commands);\n>>\n>>      if (strbuf_read_file(buf, todo_file, 0) < 0)\n>>          return error_errno(_(\"could not read '%s'.\"), todo_file);\n>> diff --git a/sequencer.h b/sequencer.h\n>> index 1de97f188d..e79f03e213 100644\n>> --- a/sequencer.h\n>> +++ b/sequencer.h\n>> @@ -146,12 +146,13 @@ int sequencer_make_script(struct repository *r, \n>> FILE *out, int argc,\n>>                const char **argv,\n>>                unsigned flags);\n>>\n>> -int sequencer_add_exec_commands(struct repository *r, const char \n>> *command);\n>> +int sequencer_add_exec_commands(struct repository *r,\n>> +                struct string_list *commands);\n>>  int transform_todo_file(struct repository *r, unsigned flags);\n>>  int check_todo_list_from_file(struct repository *r);\n>>  int complete_action(struct repository *r, struct replay_opts *opts, \n>> unsigned flags,\n>>              const char *shortrevisions, const char *onto_name,\n>> -            const char *onto, const char *orig_head, const char *cmd,\n>> +            const char *onto, const char *orig_head, struct \n>> string_list *commands,\n>>              unsigned autosquash);\n>>  int rearrange_squash(struct repository *r);\n>>\n>>\n> \n\n"},{"id":"368414","messageId":"66a61731-d794-8704-5246-0e906055e47b@gmail.com","threadId":"49504","inReplyTo":"f3652f60-dde4-0e5a-50ab-81536af9bae0@talktalk.net","subject":"Re: [PATCH v6 14/16] rebase-interactive: rewrite edit_todo_list() to handle the initial edit","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-02T14:40:51Z","receivedAt":"2019-02-02T14:41:50Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Hi Phillip,\n\nLe 01/02/2019 à 12:03, Phillip Wood a écrit :\n>>       }\n>>   -    strbuf_reset(&todo_list.buf);\n>> -    if (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n>> -        todo_list_release(&todo_list);\n>> -        return -1;\n>> -    }\n>> +    if (launch_sequence_editor(todo_file, &new_todo->buf, NULL))\n>> +        return -2;\n>>   -    if (!todo_list_parse_insn_buffer(r, todo_list.buf.buf,\n>> &todo_list))\n>> -        res = todo_list_write_to_file(r, &todo_list, todo_file, NULL,\n>> NULL, -1,\n>> -                          flags & ~(TODO_LIST_SHORTEN_IDS));\n>> +    strbuf_stripspace(&new_todo->buf, 1);\n>> +    if (initial && new_todo->buf.len == 0)\n>> +        return -3;\n>>   -    todo_list_release(&todo_list);\n>> -    return res;\n>> +    if (!initial)\n>> +        todo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo);\n> \n> error handling. Also why don't we try parse the file for the initial\n> edit - is it done somewhere else?\n> \n\nYes, it’s done in complete_action().\n\n-- Alban\n\n> Best Wishes\n> \n> Phillip\n> \n>> +\n>> +    return 0;\n>>   }\n>>     define_commit_slab(commit_seen, unsigned char);\n>> diff --git a/rebase-interactive.h b/rebase-interactive.h\n>> index 0e5925e3aa..44dbb06311 100644\n>> --- a/rebase-interactive.h\n>> +++ b/rebase-interactive.h\n>> @@ -8,7 +8,9 @@ struct todo_list;\n>>   void append_todo_help(unsigned keep_empty, int command_count,\n>>                 const char *shortrevisions, const char *shortonto,\n>>                 struct strbuf *buf);\n>> -int edit_todo_list(struct repository *r, unsigned flags);\n>> +int edit_todo_list(struct repository *r, struct todo_list *todo_list,\n>> +           struct todo_list *new_todo, const char *shortrevisions,\n>> +           const char *shortonto, unsigned flags);\n>>   int todo_list_check(struct todo_list *old_todo, struct todo_list\n>> *new_todo);\n>>     #endif\n>> diff --git a/sequencer.c b/sequencer.c\n>> index 92de982bc4..8f47f0cf39 100644\n>> --- a/sequencer.c\n>> +++ b/sequencer.c\n>> @@ -55,8 +55,7 @@ static GIT_PATH_FUNC(rebase_path, \"rebase-merge\")\n>>    * file and written to the tail of 'done'.\n>>    */\n>>   GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n>> -static GIT_PATH_FUNC(rebase_path_todo_backup,\n>> -             \"rebase-merge/git-rebase-todo.backup\")\n>> +GIT_PATH_FUNC(rebase_path_todo_backup,\n>> \"rebase-merge/git-rebase-todo.backup\")\n>>     /*\n>>    * The rebase command lines that have already been processed. A line\n>> diff --git a/sequencer.h b/sequencer.h\n>> index c5bee8124c..68acab980b 100644\n>> --- a/sequencer.h\n>> +++ b/sequencer.h\n>> @@ -10,6 +10,7 @@ struct repository;\n>>   const char *git_path_commit_editmsg(void);\n>>   const char *git_path_seq_dir(void);\n>>   const char *rebase_path_todo(void);\n>> +const char *rebase_path_todo_backup(void);\n>>     #define APPEND_SIGNOFF_DEDUP (1u << 0)\n>>  \n> \n\n"},{"id":"368415","messageId":"fd7ab34c-3a3f-57e3-0d90-0e97fbbffd3b@gmail.com","threadId":"49504","inReplyTo":"d00c11bc-013e-5839-9766-6999e99edf34@talktalk.net","subject":"Re: [PATCH v6 16/16] rebase--interactive: move transform_todo_file() to rebase--interactive.c","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-02T15:05:15Z","receivedAt":"2019-02-02T15:05:21Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Hi Phillip,\n\nLe 01/02/2019 à 12:15, Phillip Wood a écrit :\n> Hi Alban\n> \n> On 29/01/2019 15:01, Alban Gruin wrote:\n>> As transform_todo_file() is only needed inside of rebase--interactive.c,\n>> it is moved there from sequencer.c.\n> \n> I think I'd prefer to minimize the code under builtin and move this to\n> rebase-interactive.c when it is modified earlier in the series. (I'd be\n> quite happy if all the files in builtin just consisted of some option\n> parsing followed by a call to run_git_foo() which resides in libgit)\n> \n\nI do agree, but transform_todo_file() is only used by rebase -p.  Once\nit is deprecated, it’s much easier to see this function is no longer\nused if it’s marked as static thanks to -Werror=unused-function.\n\nNow that I think about it, check_todo_list_from_file(),\nrearrange_squash_in_todo_list(), and sequencer_add_exec_commands() are\nonly used by rebase -p, but I left them in sequencer.c.\n\n> Also I wonder if we should be moving more functions (e.g.\n> todo_list_write_file() and possibly add_exec_commands(),\n> rearrange_squash() and the script generation) from sequencer.c to\n> rebase-interactive.c when they're rewritten (possibly in a separate\n> commit for ease of review) but I haven't looked if this is practical or\n> if there are some dependencies that make that tricky. Unless there are\n> some simple cases it should probably be a separate series.\n> \n\nIt might be doable, but I think it’s a bit more difficult with\nsequencer_make_script() (and especially\nsequencer_make_script_with_merges()).  I will explore this after this\nseries.\n\n> Thanks for working on this series, it's great to see the todo list\n> handling becoming more efficient.\n> \n\nYou’re welcome ;-)\n\n-- Alban\n\n> Best Wishes\n> \n> Phillip\n> \n"},{"id":"368416","messageId":"2e8bb8b1-5a74-d363-5976-b5fc310b5200@gmail.com","threadId":"49504","inReplyTo":"b95aab1f-25e4-be81-e8c3-5521fe68ef62@talktalk.net","subject":"Re: [PATCH v6 07/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-02T15:09:39Z","receivedAt":"2019-02-02T15:09:44Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Le 01/02/2019 à 15:51, Phillip Wood a écrit :\n>>> @@ -4473,38 +4475,62 @@ int sequencer_add_exec_commands(struct\n>>> repository *r,\n>>>       * those chains if there are any.\n>>>       */\n>>>      insert = -1;\n>>> -    for (i = 0; i < todo_list.nr; i++) {\n>>> -        enum todo_command command = todo_list.items[i].command;\n>>> -\n>>> -        if (insert >= 0) {\n>>> -            /* skip fixup/squash chains */\n>>> -            if (command == TODO_COMMENT)\n>>> -                continue;\n>>> -            else if (is_fixup(command)) {\n>>> -                insert = i + 1;\n>>> -                continue;\n>>> -            }\n>>> -            strbuf_insert(buf,\n>>> -                      todo_list.items[insert].offset_in_buf +\n>>> -                      offset, commands, commands_len);\n>>\n>> In a todo list that looks like\n>> pick abc message\n>> #pick cde empty commit\n>> This inserts the exec command for the first pick above the commented\n>> out pick. I think your translation puts it below the commented out\n>> pick as it ignores the value of insert. I think it's probably easiest\n>> to add an INSERT_ARRAY macro to insert it in the right place. An\n>> alternative might be to track the last insert position and only copy\n>> commands across when there is another exec to insert but that might\n>> get complicated in cases such as\n>>\n>> pick abc message\n>> #squash cde squash! message //empty commit for rewording\n>> fixup 123 fixup! message\n>> #pick 456 empty commit\n> \n> Thinking about this, I'm not sure it can happen as the empty squash\n> commit will be commented out before rearrange_squash() is called so I\n> think it would actually look like\n> \n> pick abc message\n> fixup 123 fixup! message\n> #pick cde squash! message\n> #pick 456 empty commit\n> \n> So I wonder if we can get away with treating insert as a flag and\n> inserting exec commands when\n> insert && command == TODO_COMMENT\n> \n> We could probably do with some tests for this to be sure.\n> \n\nI will try this approach.\n\n-- Alban\n\n> Best Wishes\n> \n> Phillip\n> \n\n"},{"id":"368640","messageId":"571e5823-32a0-930c-909d-bb18a3b70a71@gmail.com","threadId":"49504","inReplyTo":"f3652f60-dde4-0e5a-50ab-81536af9bae0@talktalk.net","subject":"Re: [PATCH v6 14/16] rebase-interactive: rewrite edit_todo_list() to handle the initial edit","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-06T21:11:30Z","receivedAt":"2019-02-06T21:11:41Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Hi Phillip,\n\nI’ve just reread this message and have a couple of additionnal comments.\n\nLe 01/02/2019 à 12:03, Phillip Wood a écrit :\n> Hi Alban\n> \n> This looks good apart from some missing error handling.\n> \n> On 29/01/2019 15:01, Alban Gruin wrote:\n>> edit_todo_list() is changed to work on a todo_list, and to handle the\n>> initial edition of the todo list (ie. making a backup of the todo\n>> list).\n>>\n>> It does not check for dropped commits yet, as todo_list_check() does not\n>> take the commits that have already been processed by the rebase (ie. the\n>> todo list is edited in the middle of a rebase session).\n>>\n>> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n>> ---\n>>   builtin/rebase--interactive.c | 24 +++++++++++++++++-\n>>   rebase-interactive.c          | 48 ++++++++++++++++++-----------------\n>>   rebase-interactive.h          |  4 ++-\n>>   sequencer.c                   |  3 +--\n>>   sequencer.h                   |  1 +\n>>   5 files changed, 53 insertions(+), 27 deletions(-)\n>>\n>> diff --git a/builtin/rebase--interactive.c\n>> b/builtin/rebase--interactive.c\n>> index 2dbf8fc08b..645ac587f7 100644\n>> --- a/builtin/rebase--interactive.c\n>> +++ b/builtin/rebase--interactive.c\n>> @@ -13,6 +13,28 @@ static GIT_PATH_FUNC(path_state_dir, \"rebase-merge/\")\n>>   static GIT_PATH_FUNC(path_squash_onto, \"rebase-merge/squash-onto\")\n>>   static GIT_PATH_FUNC(path_interactive, \"rebase-merge/interactive\")\n>>   +static int edit_todo_file(unsigned flags)\n>> +{\n>> +    const char *todo_file = rebase_path_todo();\n>> +    struct todo_list todo_list = TODO_LIST_INIT,\n>> +        new_todo = TODO_LIST_INIT;\n>> +\n>> +    if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>> +        return error_errno(_(\"could not read '%s'.\"), todo_file);\n>> +\n>> +    strbuf_stripspace(&todo_list.buf, 1);\n>> +    if (!edit_todo_list(the_repository, &todo_list,\n>> +                &new_todo, NULL, NULL, flags) &&\n>> +        todo_list_write_to_file(the_repository, &new_todo, todo_file,\n>> NULL, NULL,\n>> +                    -1, flags & ~(TODO_LIST_SHORTEN_IDS)) < 0)\n>> +        return error_errno(_(\"could not write '%s'\"), todo_file);\n> \n> If edit_todo_list() fails then the function returns 0. I think you need\n> to do\n> \n> if (edit_todo_list() || todo_list_write_file())\n>     return error...\n> \n> todo_list_write_file() forwards the return value of write_message()\n> which is 0/-1 so there is no need for the '< 0'\n> \n\nWith your proposed condition, if edit_todo_list() fails, the error\n\"could not write '%s'\" will be shown, if I’m not mistaken.  But in my\nversion, if edit_todo_list() fails, the return value is 0.  Perhaps I\nshould write something like this instead:\n\n    int res = 0;\n    …\n    res = edit_todo_list();\n    if (!res && todo_list_write_to_file())\n        return error;\n    …\n    return res;\n\n>> +\n>> +    todo_list_release(&todo_list);\n>> +    todo_list_release(&new_todo);\n>> +\n>> +    return 0;\n>> +}\n>> +\n>>   static int get_revision_ranges(const char *upstream, const char *onto,\n>>                      const char **head_hash,\n>>                      char **revisions, char **shortrevisions)\n>> @@ -242,7 +264,7 @@ int cmd_rebase__interactive(int argc, const char\n>> **argv, const char *prefix)\n>>           break;\n>>       }\n>>       case EDIT_TODO:\n>> -        ret = edit_todo_list(the_repository, flags);\n>> +        ret = edit_todo_file(flags);\n>>           break;\n>>       case SHOW_CURRENT_PATCH: {\n>>           struct child_process cmd = CHILD_PROCESS_INIT;\n>> diff --git a/rebase-interactive.c b/rebase-interactive.c\n>> index 807f8370db..3301efbe52 100644\n>> --- a/rebase-interactive.c\n>> +++ b/rebase-interactive.c\n>> @@ -87,35 +87,37 @@ void append_todo_help(unsigned keep_empty, int\n>> command_count,\n>>       }\n>>   }\n>>   -int edit_todo_list(struct repository *r, unsigned flags)\n>> +int edit_todo_list(struct repository *r, struct todo_list *todo_list,\n>> +           struct todo_list *new_todo, const char *shortrevisions,\n>> +           const char *shortonto, unsigned flags)\n>>   {\n>>       const char *todo_file = rebase_path_todo();\n>> -    struct todo_list todo_list = TODO_LIST_INIT;\n>> -    int res = 0;\n>> -\n>> -    if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>> -        return error_errno(_(\"could not read '%s'.\"), todo_file);\n>> -\n>> -    strbuf_stripspace(&todo_list.buf, 1);\n>> -    todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n>> -    if (todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL,\n>> -1,\n>> -                    flags | TODO_LIST_SHORTEN_IDS |\n>> TODO_LIST_APPEND_TODO_HELP)) {\n>> -        todo_list_release(&todo_list);\n>> -        return -1;\n>> +    unsigned initial = shortrevisions && shortonto;\n>> +\n>> +    if (initial) {\n>> +        todo_list_write_to_file(r, todo_list, todo_file,\n>> shortrevisions, shortonto,\n>> +                    -1, flags | TODO_LIST_SHORTEN_IDS |\n>> TODO_LIST_APPEND_TODO_HELP);\n> \n> This has lost the error handling when we cannot write the file\n> \n>> +\n>> +        if (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n>> +            return error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n>> +                     rebase_path_todo_backup());\n>> +    } else {\n>> +        todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n>> +        todo_list_write_to_file(r, todo_list, todo_file, NULL, NULL, -1,\n>> +                    flags | TODO_LIST_SHORTEN_IDS |\n>> TODO_LIST_APPEND_TODO_HELP);\n> \n> error handling again\n> \n\nI agree for todo_list_write_to_file(), but todo_list_parse_insn_buffer()\nalready shows an error, and here it should not return -- we want to edit\nthe todo list to remove an error, but git would fail because the todo\nlist has an error.\n\n-- Alban\n\n"},{"id":"368702","messageId":"046f8a1d-4e2d-b968-2ee7-5291a59c13b6@talktalk.net","threadId":"49504","inReplyTo":"20190129150159.10588-12-alban.gruin@gmail.com","subject":"Re: [PATCH v6 11/16] sequencer: refactor skip_unnecessary_picks() to work on a todo_list","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-02-07T11:06:32Z","receivedAt":"2019-02-07T11:06:38Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nOn 29/01/2019 15:01, Alban Gruin wrote:\n> This refactors skip_unnecessary_picks() to work on a todo_list.  As this\n> function is only called by complete_action() (and thus is not used by\n> rebase -p), the file-handling logic is completely dropped here.\n> \n> Instead of truncating the todo list’s buffer, the items are moved to\n> the beginning of the list, eliminating the need to reparse the list.\n> This also means its buffer cannot be directly written to the disk.\n> \n> rewrite_file() is then removed, as it is now unused.\n> \n> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n> ---\n>  sequencer.c | 78 ++++++++++++-----------------------------------------\n>  1 file changed, 17 insertions(+), 61 deletions(-)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index 2a43ca685b..a817afffa9 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -4661,52 +4661,22 @@ int check_todo_list_from_file(struct repository *r)\n>  \treturn res;\n>  }\n>  \n> -static int rewrite_file(const char *path, const char *buf, size_t len)\n> -{\n> -\tint rc = 0;\n> -\tint fd = open(path, O_WRONLY | O_TRUNC);\n> -\tif (fd < 0)\n> -\t\treturn error_errno(_(\"could not open '%s' for writing\"), path);\n> -\tif (write_in_full(fd, buf, len) < 0)\n> -\t\trc = error_errno(_(\"could not write to '%s'\"), path);\n> -\tif (close(fd) && !rc)\n> -\t\trc = error_errno(_(\"could not close '%s'\"), path);\n> -\treturn rc;\n> -}\n> -\n\nIt's great to see that going\n\n>  /* skip picking commits whose parents are unchanged */\n> -static int skip_unnecessary_picks(struct repository *r, struct object_id *output_oid)\n> +static int skip_unnecessary_picks(struct repository *r,\n> +\t\t\t\t  struct todo_list *todo_list,\n> +\t\t\t\t  struct object_id *output_oid)\n\noutput_oid is a bit misleading now as we now feed the function the onto\ncommit with that parameter. Perhaps we could rename it to base_oid or\nsomething like that (I've been working on getting rebase -i to start\nwithout forking rebase--interactive and as part of that re-factoring\nI've changed the caller of this function to take a struct commit* rather\nthan a string I got tripped up by this)\n\nBest Wishes\n\nPhillip\n\n>  {\n> -\tconst char *todo_file = rebase_path_todo();\n> -\tstruct strbuf buf = STRBUF_INIT;\n> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n>  \tstruct object_id *parent_oid;\n> -\tint fd, i;\n> -\n> -\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n> -\t\treturn error(_(\"could not read 'onto'\"));\n> -\tif (get_oid(buf.buf, output_oid)) {\n> -\t\tstrbuf_release(&buf);\n> -\t\treturn error(_(\"need a HEAD to fixup\"));\n> -\t}\n> -\tstrbuf_release(&buf);\n> -\n> -\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n> -\t\treturn -1;\n> -\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n> -\t\ttodo_list_release(&todo_list);\n> -\t\treturn -1;\n> -\t}\n> +\tint i;\n>  \n> -\tfor (i = 0; i < todo_list.nr; i++) {\n> -\t\tstruct todo_item *item = todo_list.items + i;\n> +\tfor (i = 0; i < todo_list->nr; i++) {\n> +\t\tstruct todo_item *item = todo_list->items + i;\n>  \n>  \t\tif (item->command >= TODO_NOOP)\n>  \t\t\tcontinue;\n>  \t\tif (item->command != TODO_PICK)\n>  \t\t\tbreak;\n>  \t\tif (parse_commit(item->commit)) {\n> -\t\t\ttodo_list_release(&todo_list);\n>  \t\t\treturn error(_(\"could not parse commit '%s'\"),\n>  \t\t\t\toid_to_hex(&item->commit->object.oid));\n>  \t\t}\n> @@ -4720,37 +4690,21 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n>  \t\toidcpy(output_oid, &item->commit->object.oid);\n>  \t}\n>  \tif (i > 0) {\n> -\t\tint offset = get_item_line_offset(&todo_list, i);\n>  \t\tconst char *done_path = rebase_path_done();\n>  \n> -\t\tfd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);\n> -\t\tif (fd < 0) {\n> -\t\t\terror_errno(_(\"could not open '%s' for writing\"),\n> -\t\t\t\t    done_path);\n> -\t\t\ttodo_list_release(&todo_list);\n> -\t\t\treturn -1;\n> -\t\t}\n> -\t\tif (write_in_full(fd, todo_list.buf.buf, offset) < 0) {\n> +\t\tif (todo_list_write_to_file(r, todo_list, done_path, NULL, NULL, i, 0)) {\n>  \t\t\terror_errno(_(\"could not write to '%s'\"), done_path);\n> -\t\t\ttodo_list_release(&todo_list);\n> -\t\t\tclose(fd);\n>  \t\t\treturn -1;\n>  \t\t}\n> -\t\tclose(fd);\n>  \n> -\t\tif (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,\n> -\t\t\t\t todo_list.buf.len - offset) < 0) {\n> -\t\t\ttodo_list_release(&todo_list);\n> -\t\t\treturn -1;\n> -\t\t}\n> +\t\tMOVE_ARRAY(todo_list->items, todo_list->items + i, todo_list->nr - i);\n> +\t\ttodo_list->nr -= i;\n> +\t\ttodo_list->current = 0;\n>  \n> -\t\ttodo_list.current = i;\n> -\t\tif (is_fixup(peek_command(&todo_list, 0)))\n> -\t\t\trecord_in_rewritten(output_oid, peek_command(&todo_list, 0));\n> +\t\tif (is_fixup(peek_command(todo_list, 0)))\n> +\t\t\trecord_in_rewritten(output_oid, peek_command(todo_list, 0));\n>  \t}\n>  \n> -\ttodo_list_release(&todo_list);\n> -\n>  \treturn 0;\n>  }\n>  \n> @@ -4823,6 +4777,11 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n>  \t\treturn -1;\n>  \t}\n>  \n> +\tif (opts->allow_ff && skip_unnecessary_picks(r, &new_todo, &oid)) {\n> +\t\ttodo_list_release(&new_todo);\n> +\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n> +\t}\n> +\n>  \tif (todo_list_write_to_file(r, &new_todo, todo_file, NULL, NULL, -1,\n>  \t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS))) {\n>  \t\ttodo_list_release(&new_todo);\n> @@ -4831,9 +4790,6 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n>  \n>  \ttodo_list_release(&new_todo);\n>  \n> -\tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\n> -\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n> -\n>  \tif (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))\n>  \t\treturn -1;\n>  \n> \n\n"},{"id":"368703","messageId":"97f77aca-bd19-f763-349a-de40c4b94161@talktalk.net","threadId":"49504","inReplyTo":"20190129150159.10588-8-alban.gruin@gmail.com","subject":"Re: [PATCH v6 07/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-02-07T11:09:03Z","receivedAt":"2019-02-07T11:09:08Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nOn 29/01/2019 15:01, Alban Gruin wrote:\n> This refactors sequencer_add_exec_commands() to work on a todo_list to\n> avoid redundant reads and writes to the disk.\n> \n> Instead of inserting the `exec' commands between the other commands and\n> re-parsing the buffer at the end, they are appended to the buffer once,\n> and a new list of items is created.  Items from the old list are copied\n> across and new `exec' items are appended when necessary.  This\n> eliminates the need to reparse the buffer, but this also means we have\n> to use todo_list_write_to_disk() to write the file.\n> \n> todo_list_add_exec_commands() and sequencer_add_exec_commands() are\n> modified to take a string list instead of a string -- one item for each\n> command.  This makes it easier to insert a new command to the todo list\n> for each command to execute.\n> \n> sequencer_add_exec_commands() still reads the todo list from the disk,\n> as it is needed by rebase -p.\n> \n> complete_action() still uses sequencer_add_exec_commands() for now.\n> This will be changed in a future commit.\n> \n> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n> ---\n>  builtin/rebase--interactive.c |  15 +++--\n>  sequencer.c                   | 110 +++++++++++++++++++++-------------\n>  sequencer.h                   |   5 +-\n>  3 files changed, 82 insertions(+), 48 deletions(-)\n> \n> diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\n> index df19ccaeb9..53056ee713 100644\n> --- a/builtin/rebase--interactive.c\n> +++ b/builtin/rebase--interactive.c\n> @@ -65,7 +65,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n>  \t\t\t\t const char *onto, const char *onto_name,\n>  \t\t\t\t const char *squash_onto, const char *head_name,\n>  \t\t\t\t const char *restrict_revision, char *raw_strategies,\n> -\t\t\t\t const char *cmd, unsigned autosquash)\n> +\t\t\t\t struct string_list *commands, unsigned autosquash)\n>  {\n>  \tint ret;\n>  \tconst char *head_hash = NULL;\n> @@ -116,7 +116,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n>  \t\tdiscard_cache();\n>  \t\tret = complete_action(the_repository, opts, flags,\n>  \t\t\t\t      shortrevisions, onto_name, onto,\n> -\t\t\t\t      head_hash, cmd, autosquash);\n> +\t\t\t\t      head_hash, commands, autosquash);\n>  \t}\n>  \n>  \tfree(revisions);\n> @@ -139,6 +139,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \tconst char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,\n>  \t\t*squash_onto = NULL, *upstream = NULL, *head_name = NULL,\n>  \t\t*switch_to = NULL, *cmd = NULL;\n> +\tstruct string_list commands = STRING_LIST_INIT_DUP;\n\nThis is leaked, I think we should call string_list_clear() at the end.\n\nBest Wishes\n\nPhillip\n\n>  \tchar *raw_strategies = NULL;\n>  \tenum {\n>  \t\tNONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,\n> @@ -221,6 +222,12 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \t\twarning(_(\"--[no-]rebase-cousins has no effect without \"\n>  \t\t\t  \"--rebase-merges\"));\n>  \n> +\tif (cmd && *cmd) {\n> +\t\tstring_list_split(&commands, cmd, '\\n', -1);\n> +\t\tif (strlen(commands.items[commands.nr - 1].string) == 0)\n> +\t\t\t--commands.nr;\n> +\t}\n> +\n>  \tswitch (command) {\n>  \tcase NONE:\n>  \t\tif (!onto && !upstream)\n> @@ -228,7 +235,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \n>  \t\tret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,\n>  \t\t\t\t\t    onto_name, squash_onto, head_name, restrict_revision,\n> -\t\t\t\t\t    raw_strategies, cmd, autosquash);\n> +\t\t\t\t\t    raw_strategies, &commands, autosquash);\n>  \t\tbreak;\n>  \tcase SKIP: {\n>  \t\tstruct string_list merge_rr = STRING_LIST_INIT_DUP;\n> @@ -262,7 +269,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \t\tret = rearrange_squash(the_repository);\n>  \t\tbreak;\n>  \tcase ADD_EXEC:\n> -\t\tret = sequencer_add_exec_commands(the_repository, cmd);\n> +\t\tret = sequencer_add_exec_commands(the_repository, &commands);\n>  \t\tbreak;\n>  \tdefault:\n>  \t\tBUG(\"invalid command '%d'\", command);\n> diff --git a/sequencer.c b/sequencer.c\n> index 266f80d704..3a90b419d7 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -4446,25 +4446,27 @@ int sequencer_make_script(struct repository *r, FILE *out,\n>  \treturn 0;\n>  }\n>  \n> -/*\n> - * Add commands after pick and (series of) squash/fixup commands\n> - * in the todo list.\n> - */\n> -int sequencer_add_exec_commands(struct repository *r,\n> -\t\t\t\tconst char *commands)\n> +static void todo_list_add_exec_commands(struct todo_list *todo_list,\n> +\t\t\t\t\tstruct string_list *commands)\n>  {\n> -\tconst char *todo_file = rebase_path_todo();\n> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n> -\tstruct strbuf *buf = &todo_list.buf;\n> -\tsize_t offset = 0, commands_len = strlen(commands);\n> -\tint i, insert;\n> +\tstruct strbuf *buf = &todo_list->buf;\n> +\tsize_t base_offset = buf->len;\n> +\tint i, insert, nr = 0, alloc = 0;\n> +\tstruct todo_item *items = NULL, *base_items = NULL;\n>  \n> -\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> -\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n> +\tbase_items = xcalloc(commands->nr, sizeof(struct todo_item));\n> +\tfor (i = 0; i < commands->nr; ++i) {\n> +\t\tsize_t command_len = strlen(commands->items[i].string);\n>  \n> -\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n> -\t\ttodo_list_release(&todo_list);\n> -\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> +\t\tstrbuf_addstr(buf, commands->items[i].string);\n> +\t\tstrbuf_addch(buf, '\\n');\n> +\n> +\t\tbase_items[i].command = TODO_EXEC;\n> +\t\tbase_items[i].offset_in_buf = base_offset;\n> +\t\tbase_items[i].arg_offset = base_offset + strlen(\"exec \");\n> +\t\tbase_items[i].arg_len = command_len - strlen(\"exec \");\n> +\n> +\t\tbase_offset += command_len + 1;\n>  \t}\n>  \n>  \t/*\n> @@ -4473,38 +4475,62 @@ int sequencer_add_exec_commands(struct repository *r,\n>  \t * those chains if there are any.\n>  \t */\n>  \tinsert = -1;\n> -\tfor (i = 0; i < todo_list.nr; i++) {\n> -\t\tenum todo_command command = todo_list.items[i].command;\n> -\n> -\t\tif (insert >= 0) {\n> -\t\t\t/* skip fixup/squash chains */\n> -\t\t\tif (command == TODO_COMMENT)\n> -\t\t\t\tcontinue;\n> -\t\t\telse if (is_fixup(command)) {\n> -\t\t\t\tinsert = i + 1;\n> -\t\t\t\tcontinue;\n> -\t\t\t}\n> -\t\t\tstrbuf_insert(buf,\n> -\t\t\t\t      todo_list.items[insert].offset_in_buf +\n> -\t\t\t\t      offset, commands, commands_len);\n> -\t\t\toffset += commands_len;\n> +\tfor (i = 0; i < todo_list->nr; i++) {\n> +\t\tenum todo_command command = todo_list->items[i].command;\n> +\t\tif (insert >= 0 && command != TODO_COMMENT && !is_fixup(command)) {\n> +\t\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n> +\t\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n> +\t\t\tnr += commands->nr;\n>  \t\t\tinsert = -1;\n>  \t\t}\n>  \n> -\t\tif (command == TODO_PICK || command == TODO_MERGE)\n> +\t\tALLOC_GROW(items, nr + 1, alloc);\n> +\t\titems[nr++] = todo_list->items[i];\n> +\n> +\t\tif (command == TODO_PICK || command == TODO_MERGE || is_fixup(command))\n>  \t\t\tinsert = i + 1;\n>  \t}\n>  \n>  \t/* insert or append final <commands> */\n> -\tif (insert >= 0 && insert < todo_list.nr)\n> -\t\tstrbuf_insert(buf, todo_list.items[insert].offset_in_buf +\n> -\t\t\t      offset, commands, commands_len);\n> -\telse if (insert >= 0 || !offset)\n> -\t\tstrbuf_add(buf, commands, commands_len);\n> +\tif (insert >= 0 || nr == todo_list->nr) {\n> +\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n> +\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n> +\t\tnr += commands->nr;\n> +\t}\n> +\n> +\tfree(base_items);\n> +\tFREE_AND_NULL(todo_list->items);\n> +\ttodo_list->items = items;\n> +\ttodo_list->nr = nr;\n> +\ttodo_list->alloc = alloc;\n> +}\n>  \n> -\ti = write_message(buf->buf, buf->len, todo_file, 0);\n> +/*\n> + * Add commands after pick and (series of) squash/fixup commands\n> + * in the todo list.\n> + */\n> +int sequencer_add_exec_commands(struct repository *r,\n> +\t\t\t\tstruct string_list *commands)\n> +{\n> +\tconst char *todo_file = rebase_path_todo();\n> +\tstruct todo_list todo_list = TODO_LIST_INIT;\n> +\tint res;\n> +\n> +\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> +\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> +\n> +\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n> +\t\ttodo_list_release(&todo_list);\n> +\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> +\t}\n> +\n> +\ttodo_list_add_exec_commands(&todo_list, commands);\n> +\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n>  \ttodo_list_release(&todo_list);\n> -\treturn i;\n> +\n> +\tif (res)\n> +\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n> +\treturn 0;\n>  }\n>  \n>  static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n> @@ -4735,7 +4761,7 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n>  \n>  int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n>  \t\t    const char *shortrevisions, const char *onto_name,\n> -\t\t    const char *onto, const char *orig_head, const char *cmd,\n> +\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n>  \t\t    unsigned autosquash)\n>  {\n>  \tconst char *shortonto, *todo_file = rebase_path_todo();\n> @@ -4754,8 +4780,8 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n>  \tif (autosquash && rearrange_squash(r))\n>  \t\treturn -1;\n>  \n> -\tif (cmd && *cmd)\n> -\t\tsequencer_add_exec_commands(r, cmd);\n> +\tif (commands->nr)\n> +\t\tsequencer_add_exec_commands(r, commands);\n>  \n>  \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n>  \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> diff --git a/sequencer.h b/sequencer.h\n> index 1de97f188d..e79f03e213 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -146,12 +146,13 @@ int sequencer_make_script(struct repository *r, FILE *out, int argc,\n>  \t\t\t  const char **argv,\n>  \t\t\t  unsigned flags);\n>  \n> -int sequencer_add_exec_commands(struct repository *r, const char *command);\n> +int sequencer_add_exec_commands(struct repository *r,\n> +\t\t\t\tstruct string_list *commands);\n>  int transform_todo_file(struct repository *r, unsigned flags);\n>  int check_todo_list_from_file(struct repository *r);\n>  int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n>  \t\t    const char *shortrevisions, const char *onto_name,\n> -\t\t    const char *onto, const char *orig_head, const char *cmd,\n> +\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n>  \t\t    unsigned autosquash);\n>  int rearrange_squash(struct repository *r);\n>  \n> \n\n"},{"id":"368819","messageId":"32bf73bb-7650-0906-093e-d20e22a81d95@talktalk.net","threadId":"49504","inReplyTo":"571e5823-32a0-930c-909d-bb18a3b70a71@gmail.com","subject":"Re: [PATCH v6 14/16] rebase-interactive: rewrite edit_todo_list() to handle the initial edit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-02-08T10:56:50Z","receivedAt":"2019-02-08T10:56:56Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nOn 06/02/2019 21:11, Alban Gruin wrote:\n> Hi Phillip,\n> \n> I’ve just reread this message and have a couple of additionnal comments.\n> \n> Le 01/02/2019 à 12:03, Phillip Wood a écrit :\n>> Hi Alban\n>>\n>> This looks good apart from some missing error handling.\n>>\n>> On 29/01/2019 15:01, Alban Gruin wrote:\n>>> edit_todo_list() is changed to work on a todo_list, and to handle the\n>>> initial edition of the todo list (ie. making a backup of the todo\n>>> list).\n>>>\n>>> It does not check for dropped commits yet, as todo_list_check() does not\n>>> take the commits that have already been processed by the rebase (ie. the\n>>> todo list is edited in the middle of a rebase session).\n>>>\n>>> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n>>> ---\n>>>   builtin/rebase--interactive.c | 24 +++++++++++++++++-\n>>>   rebase-interactive.c          | 48 ++++++++++++++++++-----------------\n>>>   rebase-interactive.h          |  4 ++-\n>>>   sequencer.c                   |  3 +--\n>>>   sequencer.h                   |  1 +\n>>>   5 files changed, 53 insertions(+), 27 deletions(-)\n>>>\n>>> diff --git a/builtin/rebase--interactive.c\n>>> b/builtin/rebase--interactive.c\n>>> index 2dbf8fc08b..645ac587f7 100644\n>>> --- a/builtin/rebase--interactive.c\n>>> +++ b/builtin/rebase--interactive.c\n>>> @@ -13,6 +13,28 @@ static GIT_PATH_FUNC(path_state_dir, \"rebase-merge/\")\n>>>   static GIT_PATH_FUNC(path_squash_onto, \"rebase-merge/squash-onto\")\n>>>   static GIT_PATH_FUNC(path_interactive, \"rebase-merge/interactive\")\n>>>   +static int edit_todo_file(unsigned flags)\n>>> +{\n>>> +    const char *todo_file = rebase_path_todo();\n>>> +    struct todo_list todo_list = TODO_LIST_INIT,\n>>> +        new_todo = TODO_LIST_INIT;\n>>> +\n>>> +    if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>>> +        return error_errno(_(\"could not read '%s'.\"), todo_file);\n>>> +\n>>> +    strbuf_stripspace(&todo_list.buf, 1);\n>>> +    if (!edit_todo_list(the_repository, &todo_list,\n>>> +                &new_todo, NULL, NULL, flags) &&\n>>> +        todo_list_write_to_file(the_repository, &new_todo, todo_file,\n>>> NULL, NULL,\n>>> +                    -1, flags & ~(TODO_LIST_SHORTEN_IDS)) < 0)\n>>> +        return error_errno(_(\"could not write '%s'\"), todo_file);\n>>\n>> If edit_todo_list() fails then the function returns 0. I think you need\n>> to do\n>>\n>> if (edit_todo_list() || todo_list_write_file())\n>>     return error...\n>>\n>> todo_list_write_file() forwards the return value of write_message()\n>> which is 0/-1 so there is no need for the '< 0'\n>>\n> \n> With your proposed condition, if edit_todo_list() fails, the error\n> \"could not write '%s'\" will be shown, if I’m not mistaken.\n\nYes, you're right but as edit_todo_list() will have already printed an\nerror I decided it didn't matter too much, but it would be better to\navoid it as you suggest.\n\n> But in my\n> version, if edit_todo_list() fails, the return value is 0.  Perhaps I\n> should write something like this instead:\n> \n>     int res = 0;\n>     …\n>     res = edit_todo_list();\n>     if (!res && todo_list_write_to_file())\n>         return error;\n\nIf you did\n\tret = error...;\ninstead then we always free the todo lists before exiting the function.\n\n>     …\n>     return res;\n>>> +\n>>> +    todo_list_release(&todo_list);\n>>> +    todo_list_release(&new_todo);\n>>> +\n>>> +    return 0;\n>>> +}\n>>> +\n>>>   static int get_revision_ranges(const char *upstream, const char *onto,\n>>>                      const char **head_hash,\n>>>                      char **revisions, char **shortrevisions)\n>>> @@ -242,7 +264,7 @@ int cmd_rebase__interactive(int argc, const char\n>>> **argv, const char *prefix)\n>>>           break;\n>>>       }\n>>>       case EDIT_TODO:\n>>> -        ret = edit_todo_list(the_repository, flags);\n>>> +        ret = edit_todo_file(flags);\n>>>           break;\n>>>       case SHOW_CURRENT_PATCH: {\n>>>           struct child_process cmd = CHILD_PROCESS_INIT;\n>>> diff --git a/rebase-interactive.c b/rebase-interactive.c\n>>> index 807f8370db..3301efbe52 100644\n>>> --- a/rebase-interactive.c\n>>> +++ b/rebase-interactive.c\n>>> @@ -87,35 +87,37 @@ void append_todo_help(unsigned keep_empty, int\n>>> command_count,\n>>>       }\n>>>   }\n>>>   -int edit_todo_list(struct repository *r, unsigned flags)\n>>> +int edit_todo_list(struct repository *r, struct todo_list *todo_list,\n>>> +           struct todo_list *new_todo, const char *shortrevisions,\n>>> +           const char *shortonto, unsigned flags)\n>>>   {\n>>>       const char *todo_file = rebase_path_todo();\n>>> -    struct todo_list todo_list = TODO_LIST_INIT;\n>>> -    int res = 0;\n>>> -\n>>> -    if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>>> -        return error_errno(_(\"could not read '%s'.\"), todo_file);\n>>> -\n>>> -    strbuf_stripspace(&todo_list.buf, 1);\n>>> -    todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n>>> -    if (todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL,\n>>> -1,\n>>> -                    flags | TODO_LIST_SHORTEN_IDS |\n>>> TODO_LIST_APPEND_TODO_HELP)) {\n>>> -        todo_list_release(&todo_list);\n>>> -        return -1;\n>>> +    unsigned initial = shortrevisions && shortonto;\n>>> +\n>>> +    if (initial) {\n>>> +        todo_list_write_to_file(r, todo_list, todo_file,\n>>> shortrevisions, shortonto,\n>>> +                    -1, flags | TODO_LIST_SHORTEN_IDS |\n>>> TODO_LIST_APPEND_TODO_HELP);\n>>\n>> This has lost the error handling when we cannot write the file\n>>\n>>> +\n>>> +        if (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n>>> +            return error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n>>> +                     rebase_path_todo_backup());\n>>> +    } else {\n>>> +        todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n>>> +        todo_list_write_to_file(r, todo_list, todo_file, NULL, NULL, -1,\n>>> +                    flags | TODO_LIST_SHORTEN_IDS |\n>>> TODO_LIST_APPEND_TODO_HELP);\n>>\n>> error handling again\n>>\n> \n> I agree for todo_list_write_to_file(), but todo_list_parse_insn_buffer()\n> already shows an error, and here it should not return -- we want to edit\n> the todo list to remove an error, but git would fail because the todo\n> list has an error.\n\nAh yes, that is what the original was doing\n\n\nBest Wishes\n\nPhillip\n\n> \n> -- Alban\n> \n\n"},{"id":"368961","messageId":"20190210132648.12821-2-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190210132648.12821-1-alban.gruin@gmail.com","subject":"[PATCH v7 01/16] sequencer: changes in parse_insn_buffer()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-10T13:26:33Z","receivedAt":"2019-02-10T13:27:11Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This clears the number of items of a todo_list before parsing it to\nallow to parse the same list multiple times without issues.  As its\nitems are not dynamically allocated, or don’t need to allocate memory,\nno additionnal memory management is required here.\n\nFurthermore, if a line is invalid, the type of the corresponding\ncommand is set to a garbage value, and its argument is defined properly.\nThis will allow to recreate the text of a todo list from its commands,\neven if one of them is incorrect.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v6.\n\n sequencer.c | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex b68bca0bef..d605199a54 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2141,6 +2141,8 @@ static int parse_insn_buffer(struct repository *r, char *buf,\n \tchar *p = buf, *next_p;\n \tint i, res = 0, fixup_okay = file_exists(rebase_path_done());\n \n+\ttodo_list->current = todo_list->nr = 0;\n+\n \tfor (i = 1; *p; i++, p = next_p) {\n \t\tchar *eol = strchrnul(p, '\\n');\n \n@@ -2154,7 +2156,10 @@ static int parse_insn_buffer(struct repository *r, char *buf,\n \t\tif (parse_insn_line(r, item, p, eol)) {\n \t\t\tres = error(_(\"invalid line %d: %.*s\"),\n \t\t\t\ti, (int)(eol - p), p);\n-\t\t\titem->command = TODO_NOOP;\n+\t\t\titem->command = TODO_COMMENT + 1;\n+\t\t\titem->arg = p;\n+\t\t\titem->arg_len = (int)(eol - p);\n+\t\t\titem->commit = NULL;\n \t\t}\n \n \t\tif (fixup_okay)\n-- \n2.20.1\n\n"},{"id":"368962","messageId":"20190210132648.12821-1-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190129150159.10588-1-alban.gruin@gmail.com","subject":"[PATCH v7 00/16] sequencer: refactor functions working on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-10T13:26:32Z","receivedAt":"2019-02-10T13:27:12Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"At the center of the \"interactive\" part of the interactive rebase lies\nthe todo list.  When the user starts an interactive rebase, a todo list\nis generated, presented to the user (who then edits it using a text\neditor), read back, and then is checked and processed before the actual\nrebase takes place.\n\nSome of this processing includes adding execs commands, reordering\nfixup! and squash! commits, and checking if no commits were accidentally\ndropped by the user.\n\nBefore I converted the interactive rebase in C, these functions were\ncalled by git-rebase--interactive.sh through git-rebase--helper.  Since\nthe only way to pass around a large amount of data between a shell\nscript and a C program is to use a file (or any declination of a file),\nthe functions that checked and processed the todo list were directly\nworking on a file, the same file that the user edited.\n\nDuring the conversion, I did not address this issue, which lead to a\ncomplete_action() that reads the todo list file, does some computation\nbased on its content, and writes it back to the disk, several times in\nthe same function.\n\nAs it is not an efficient way to handle a data structure, this patch\nseries refactor the functions that processes the todo list to work on a\ntodo_list structure instead of reading it from the disk.\n\nSome commits consists in modifying edit_todo_list() (initially used by\n--edit-todo) to handle the initial edition of the todo list, to increase\ncode sharing.\n\nThis is based on nd/the-index (cde555480b, \"Merge branch\n'nd/the-index'\").\n\nChanges since v6:\n\n - The series has been rebased onto cde555480b (this affects the patch\n   10/16).\n\n - [07/16] rebase--interactive does no longer check if the last command\n   is empty before decrementing the size of the command list, and frees\n   the list before exiting.\n\n - [07/16] `insert' (in todo_list_add_exec_commands()) is treated as a\n   flag instead of an integer.\n\n - [07/16] Styling change (++i -> i++).\n\n - [07/16] `exec' commands are inserted before comments, not after.\n\n - [11/16] Renaming `output_oid' to `base_oid'.\n\n - [14/16] Better error handling in edit_todo_list() and\n   edit_todo_file().\n\n - [16/16] Move sequencer_add_exec_commands() and\n   rearrange_squash_in_todo_file() to rebase--interactive.c.\n\nThe tip of this series is tagged as \"refactor-todo-list-v7\" in\nhttps://github.com/agrn/git.\n\nThe range diff is included below.\n\nAlban Gruin (16):\n  sequencer: changes in parse_insn_buffer()\n  sequencer: make the todo_list structure public\n  sequencer: remove the 'arg' field from todo_item\n  sequencer: refactor transform_todos() to work on a todo_list\n  sequencer: introduce todo_list_write_to_file()\n  sequencer: refactor check_todo_list() to work on a todo_list\n  sequencer: refactor sequencer_add_exec_commands() to work on a\n    todo_list\n  sequencer: refactor rearrange_squash() to work on a todo_list\n  sequencer: make sequencer_make_script() write its script to a strbuf\n  sequencer: change complete_action() to use the refactored functions\n  sequencer: refactor skip_unnecessary_picks() to work on a todo_list\n  rebase-interactive: use todo_list_write_to_file() in edit_todo_list()\n  rebase-interactive: append_todo_help() changes\n  rebase-interactive: rewrite edit_todo_list() to handle the initial\n    edit\n  sequencer: use edit_todo_list() in complete_action()\n  rebase--interactive: move several functions to rebase--interactive.c\n\n builtin/rebase--interactive.c | 141 ++++++--\n rebase-interactive.c          | 141 ++++++--\n rebase-interactive.h          |   9 +-\n sequencer.c                   | 650 ++++++++++++----------------------\n sequencer.h                   |  81 ++++-\n 5 files changed, 539 insertions(+), 483 deletions(-)\n\nRange-diff against v6:\n 1:  f5d6bb72fa =  1:  0bc5f714e5 sequencer: changes in parse_insn_buffer()\n 2:  68dcdd233b =  2:  34d149ff25 sequencer: make the todo_list structure public\n 3:  74152c4d60 =  3:  8200f7a6be sequencer: remove the 'arg' field from todo_item\n 4:  aff98ea435 =  4:  ce8ca23ee0 sequencer: refactor transform_todos() to work on a todo_list\n 5:  e4c85dd743 =  5:  67ebea475e sequencer: introduce todo_list_write_to_file()\n 6:  5b1735671b =  6:  370c153ebe sequencer: refactor check_todo_list() to work on a todo_list\n 7:  f0734897af !  7:  66e7b65509 sequencer: refactor sequencer_add_exec_commands() to work on a todo_list\n    @@ -60,8 +60,7 @@\n      \n     +\tif (cmd && *cmd) {\n     +\t\tstring_list_split(&commands, cmd, '\\n', -1);\n    -+\t\tif (strlen(commands.items[commands.nr - 1].string) == 0)\n    -+\t\t\t--commands.nr;\n    ++\t\t--commands.nr;\n     +\t}\n     +\n      \tswitch (command) {\n    @@ -85,18 +84,19 @@\n      \t\tbreak;\n      \tdefault:\n      \t\tBUG(\"invalid command '%d'\", command);\n    + \t}\n    + \n    ++\tstring_list_clear(&commands, 1);\n    + \treturn !!ret;\n    + }\n     \n      diff --git a/sequencer.c b/sequencer.c\n      --- a/sequencer.c\n      +++ b/sequencer.c\n     @@\n    - \treturn 0;\n    - }\n    - \n    --/*\n    -- * Add commands after pick and (series of) squash/fixup commands\n    -- * in the todo list.\n    -- */\n    +  * Add commands after pick and (series of) squash/fixup commands\n    +  * in the todo list.\n    +  */\n     -int sequencer_add_exec_commands(struct repository *r,\n     -\t\t\t\tconst char *commands)\n     +static void todo_list_add_exec_commands(struct todo_list *todo_list,\n    @@ -115,7 +115,7 @@\n     -\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n     -\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n     +\tbase_items = xcalloc(commands->nr, sizeof(struct todo_item));\n    -+\tfor (i = 0; i < commands->nr; ++i) {\n    ++\tfor (i = 0; i < commands->nr; i++) {\n     +\t\tsize_t command_len = strlen(commands->items[i].string);\n      \n     -\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n    @@ -134,9 +134,10 @@\n      \n      \t/*\n     @@\n    + \t * are considered part of the pick, so we insert the commands *after*\n      \t * those chains if there are any.\n      \t */\n    - \tinsert = -1;\n    +-\tinsert = -1;\n     -\tfor (i = 0; i < todo_list.nr; i++) {\n     -\t\tenum todo_command command = todo_list.items[i].command;\n     -\n    @@ -152,21 +153,25 @@\n     -\t\t\t\t      todo_list.items[insert].offset_in_buf +\n     -\t\t\t\t      offset, commands, commands_len);\n     -\t\t\toffset += commands_len;\n    +-\t\t\tinsert = -1;\n    ++\tinsert = 0;\n     +\tfor (i = 0; i < todo_list->nr; i++) {\n     +\t\tenum todo_command command = todo_list->items[i].command;\n    -+\t\tif (insert >= 0 && command != TODO_COMMENT && !is_fixup(command)) {\n    ++\t\tif (insert && !is_fixup(command)) {\n     +\t\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n     +\t\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n     +\t\t\tnr += commands->nr;\n    - \t\t\tinsert = -1;\n    ++\n    ++\t\t\tinsert = 0;\n      \t\t}\n      \n     -\t\tif (command == TODO_PICK || command == TODO_MERGE)\n    +-\t\t\tinsert = i + 1;\n     +\t\tALLOC_GROW(items, nr + 1, alloc);\n     +\t\titems[nr++] = todo_list->items[i];\n     +\n     +\t\tif (command == TODO_PICK || command == TODO_MERGE || is_fixup(command))\n    - \t\t\tinsert = i + 1;\n    ++\t\t\tinsert = 1;\n      \t}\n      \n      \t/* insert or append final <commands> */\n    @@ -175,7 +180,7 @@\n     -\t\t\t      offset, commands, commands_len);\n     -\telse if (insert >= 0 || !offset)\n     -\t\tstrbuf_add(buf, commands, commands_len);\n    -+\tif (insert >= 0 || nr == todo_list->nr) {\n    ++\tif (insert || nr == todo_list->nr) {\n     +\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n     +\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n     +\t\tnr += commands->nr;\n    @@ -189,10 +194,6 @@\n     +}\n      \n     -\ti = write_message(buf->buf, buf->len, todo_file, 0);\n    -+/*\n    -+ * Add commands after pick and (series of) squash/fixup commands\n    -+ * in the todo list.\n    -+ */\n     +int sequencer_add_exec_commands(struct repository *r,\n     +\t\t\t\tstruct string_list *commands)\n     +{\n 8:  ecc684c833 =  8:  640cb7aa54 sequencer: refactor rearrange_squash() to work on a todo_list\n 9:  4de36e9e76 =  9:  bef1970c88 sequencer: make sequencer_make_script() write its script to a strbuf\n10:  d5af79282c ! 10:  48ee37a32a sequencer: change complete_action() to use the refactored functions\n    @@ -197,14 +197,6 @@\n      \n      \tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\n      \t\treturn error(_(\"could not skip unnecessary pick commands\"));\n    - \n    - \tif (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))\n    - \t\treturn -1;\n    --;\n    -+\n    - \tif (require_clean_work_tree(r, \"rebase\", \"\", 1, 1))\n    - \t\treturn -1;\n    - \n     \n      diff --git a/sequencer.h b/sequencer.h\n      --- a/sequencer.h\n11:  b04e1fc692 ! 11:  bc89fbfea6 sequencer: refactor skip_unnecessary_picks() to work on a todo_list\n    @@ -38,7 +38,7 @@\n     -static int skip_unnecessary_picks(struct repository *r, struct object_id *output_oid)\n     +static int skip_unnecessary_picks(struct repository *r,\n     +\t\t\t\t  struct todo_list *todo_list,\n    -+\t\t\t\t  struct object_id *output_oid)\n    ++\t\t\t\t  struct object_id *base_oid)\n      {\n     -\tconst char *todo_file = rebase_path_todo();\n     -\tstruct strbuf buf = STRBUF_INIT;\n    @@ -77,7 +77,14 @@\n      \t\t\t\toid_to_hex(&item->commit->object.oid));\n      \t\t}\n     @@\n    - \t\toidcpy(output_oid, &item->commit->object.oid);\n    + \t\tif (item->commit->parents->next)\n    + \t\t\tbreak; /* merge commit */\n    + \t\tparent_oid = &item->commit->parents->item->object.oid;\n    +-\t\tif (!oideq(parent_oid, output_oid))\n    ++\t\tif (!oideq(parent_oid, base_oid))\n    + \t\t\tbreak;\n    +-\t\toidcpy(output_oid, &item->commit->object.oid);\n    ++\t\toidcpy(base_oid, &item->commit->object.oid);\n      \t}\n      \tif (i > 0) {\n     -\t\tint offset = get_item_line_offset(&todo_list, i);\n    @@ -112,7 +119,7 @@\n     -\t\tif (is_fixup(peek_command(&todo_list, 0)))\n     -\t\t\trecord_in_rewritten(output_oid, peek_command(&todo_list, 0));\n     +\t\tif (is_fixup(peek_command(todo_list, 0)))\n    -+\t\t\trecord_in_rewritten(output_oid, peek_command(todo_list, 0));\n    ++\t\t\trecord_in_rewritten(base_oid, peek_command(todo_list, 0));\n      \t}\n      \n     -\ttodo_list_release(&todo_list);\n12:  7cc97f77a8 = 12:  a754d0da96 rebase-interactive: use todo_list_write_to_file() in edit_todo_list()\n13:  77056f58a2 = 13:  031e4de856 rebase-interactive: append_todo_help() changes\n14:  3591e06b7d ! 14:  268998924b rebase-interactive: rewrite edit_todo_list() to handle the initial edit\n    @@ -24,21 +24,21 @@\n     +\tconst char *todo_file = rebase_path_todo();\n     +\tstruct todo_list todo_list = TODO_LIST_INIT,\n     +\t\tnew_todo = TODO_LIST_INIT;\n    ++\tint res = 0;\n     +\n     +\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n     +\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n     +\n     +\tstrbuf_stripspace(&todo_list.buf, 1);\n    -+\tif (!edit_todo_list(the_repository, &todo_list,\n    -+\t\t\t    &new_todo, NULL, NULL, flags) &&\n    -+\t    todo_list_write_to_file(the_repository, &new_todo, todo_file, NULL, NULL,\n    -+\t\t\t\t    -1, flags & ~(TODO_LIST_SHORTEN_IDS)) < 0)\n    -+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n    ++\tres = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);\n    ++\tif (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,\n    ++\t\t\t\t\t    NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))\n    ++\t\tres = error_errno(_(\"could not write '%s'\"), todo_file);\n     +\n     +\ttodo_list_release(&todo_list);\n     +\ttodo_list_release(&new_todo);\n     +\n    -+\treturn 0;\n    ++\treturn res;\n     +}\n     +\n      static int get_revision_ranges(const char *upstream, const char *onto,\n    @@ -79,40 +79,39 @@\n     -\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP)) {\n     -\t\ttodo_list_release(&todo_list);\n     -\t\treturn -1;\n    +-\t}\n     +\tunsigned initial = shortrevisions && shortonto;\n    -+\n    -+\tif (initial) {\n    -+\t\ttodo_list_write_to_file(r, todo_list, todo_file, shortrevisions, shortonto,\n    -+\t\t\t\t\t-1, flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP);\n    -+\n    -+\t\tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n    -+\t\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n    -+\t\t\t\t     rebase_path_todo_backup());\n    -+\t} else {\n    -+\t\ttodo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n    -+\t\ttodo_list_write_to_file(r, todo_list, todo_file, NULL, NULL, -1,\n    -+\t\t\t\t\tflags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP);\n    - \t}\n      \n     -\tstrbuf_reset(&todo_list.buf);\n     -\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n     -\t\ttodo_list_release(&todo_list);\n     -\t\treturn -1;\n     -\t}\n    -+\tif (launch_sequence_editor(todo_file, &new_todo->buf, NULL))\n    -+\t\treturn -2;\n    ++\tif (!initial)\n    ++\t\ttodo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n      \n     -\tif (!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list))\n     -\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n     -\t\t\t\t\t      flags & ~(TODO_LIST_SHORTEN_IDS));\n    -+\tstrbuf_stripspace(&new_todo->buf, 1);\n    -+\tif (initial && new_todo->buf.len == 0)\n    -+\t\treturn -3;\n    ++\tif (todo_list_write_to_file(r, todo_list, todo_file, shortrevisions, shortonto,\n    ++\t\t\t\t    -1, flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))\n    ++\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n      \n     -\ttodo_list_release(&todo_list);\n     -\treturn res;\n    ++\tif (initial && copy_file(rebase_path_todo_backup(), todo_file, 0666))\n    ++\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n    ++\t\t\t     rebase_path_todo_backup());\n    ++\n    ++\tif (launch_sequence_editor(todo_file, &new_todo->buf, NULL))\n    ++\t\treturn -2;\n    ++\n    ++\tstrbuf_stripspace(&new_todo->buf, 1);\n    ++\tif (initial && new_todo->buf.len == 0)\n    ++\t\treturn -3;\n    ++\n     +\tif (!initial)\n    -+\t\ttodo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo);\n    ++\t\treturn todo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo);\n     +\n     +\treturn 0;\n      }\n15:  0b29fca27f = 15:  6f1274a33c sequencer: use edit_todo_list() in complete_action()\n16:  5ab34416af <  -:  ---------- rebase--interactive: move transform_todo_file() to rebase--interactive.c\n -:  ---------- > 16:  f57a7405d0 rebase--interactive: move several functions to rebase--interactive.c\n-- \n2.20.1\n\n"},{"id":"368963","messageId":"20190210132648.12821-4-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190210132648.12821-1-alban.gruin@gmail.com","subject":"[PATCH v7 03/16] sequencer: remove the 'arg' field from todo_item","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-10T13:26:35Z","receivedAt":"2019-02-10T13:27:15Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"The 'arg' field of todo_item used to store the address of the first byte\nof the parameter of a command in a todo list.  It was associated with\nthe length of the parameter (the 'arg_len' field).\n\nThis replaces the 'arg' field by 'arg_offset'.  This new field does not\nstore the address of the parameter, but the position of the first\ncharacter of the parameter in the buffer.  todo_item_get_arg() is added\nto return the address of the parameter of an item.\n\nThis will prevent todo_list_add_exec_commands() from having to do awful\npointer arithmetics when growing the todo list buffer.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v6.\n\n sequencer.c | 67 ++++++++++++++++++++++++++++++-----------------------\n sequencer.h |  6 +++--\n 2 files changed, 42 insertions(+), 31 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 25cc7a9a91..c844a9b7f3 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1999,8 +1999,14 @@ static struct todo_item *append_new_todo(struct todo_list *todo_list)\n \treturn todo_list->items + todo_list->nr++;\n }\n \n+const char *todo_item_get_arg(struct todo_list *todo_list,\n+\t\t\t      struct todo_item *item)\n+{\n+\treturn todo_list->buf.buf + item->arg_offset;\n+}\n+\n static int parse_insn_line(struct repository *r, struct todo_item *item,\n-\t\t\t   const char *bol, char *eol)\n+\t\t\t   const char *buf, const char *bol, char *eol)\n {\n \tstruct object_id commit_oid;\n \tchar *end_of_object_name;\n@@ -2014,7 +2020,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \tif (bol == eol || *bol == '\\r' || *bol == comment_line_char) {\n \t\titem->command = TODO_COMMENT;\n \t\titem->commit = NULL;\n-\t\titem->arg = bol;\n+\t\titem->arg_offset = bol - buf;\n \t\titem->arg_len = eol - bol;\n \t\treturn 0;\n \t}\n@@ -2041,7 +2047,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \t\t\treturn error(_(\"%s does not accept arguments: '%s'\"),\n \t\t\t\t     command_to_string(item->command), bol);\n \t\titem->commit = NULL;\n-\t\titem->arg = bol;\n+\t\titem->arg_offset = bol - buf;\n \t\titem->arg_len = eol - bol;\n \t\treturn 0;\n \t}\n@@ -2053,7 +2059,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \tif (item->command == TODO_EXEC || item->command == TODO_LABEL ||\n \t    item->command == TODO_RESET) {\n \t\titem->commit = NULL;\n-\t\titem->arg = bol;\n+\t\titem->arg_offset = bol - buf;\n \t\titem->arg_len = (int)(eol - bol);\n \t\treturn 0;\n \t}\n@@ -2067,7 +2073,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \t\t} else {\n \t\t\titem->flags |= TODO_EDIT_MERGE_MSG;\n \t\t\titem->commit = NULL;\n-\t\t\titem->arg = bol;\n+\t\t\titem->arg_offset = bol - buf;\n \t\t\titem->arg_len = (int)(eol - bol);\n \t\t\treturn 0;\n \t\t}\n@@ -2079,8 +2085,9 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \tstatus = get_oid(bol, &commit_oid);\n \t*end_of_object_name = saved;\n \n-\titem->arg = end_of_object_name + strspn(end_of_object_name, \" \\t\");\n-\titem->arg_len = (int)(eol - item->arg);\n+\tbol = end_of_object_name + strspn(end_of_object_name, \" \\t\");\n+\titem->arg_offset = bol - buf;\n+\titem->arg_len = (int)(eol - bol);\n \n \tif (status < 0)\n \t\treturn -1;\n@@ -2108,11 +2115,11 @@ int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \n \t\titem = append_new_todo(todo_list);\n \t\titem->offset_in_buf = p - todo_list->buf.buf;\n-\t\tif (parse_insn_line(r, item, p, eol)) {\n+\t\tif (parse_insn_line(r, item, buf, p, eol)) {\n \t\t\tres = error(_(\"invalid line %d: %.*s\"),\n \t\t\t\ti, (int)(eol - p), p);\n \t\t\titem->command = TODO_COMMENT + 1;\n-\t\t\titem->arg = p;\n+\t\t\titem->arg_offset = p - buf;\n \t\t\titem->arg_len = (int)(eol - p);\n \t\t\titem->commit = NULL;\n \t\t}\n@@ -2452,7 +2459,7 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,\n \n \t\titem->command = command;\n \t\titem->commit = commit;\n-\t\titem->arg = NULL;\n+\t\titem->arg_offset = 0;\n \t\titem->arg_len = 0;\n \t\titem->offset_in_buf = todo_list->buf.len;\n \t\tsubject_len = find_commit_subject(commit_buffer, &subject);\n@@ -3491,6 +3498,8 @@ static int pick_commits(struct repository *r,\n \n \twhile (todo_list->current < todo_list->nr) {\n \t\tstruct todo_item *item = todo_list->items + todo_list->current;\n+\t\tconst char *arg = todo_item_get_arg(todo_list, item);\n+\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n \t\tif (is_rebase_i(opts)) {\n@@ -3542,10 +3551,9 @@ static int pick_commits(struct repository *r,\n \t\t\t\t\tfprintf(stderr,\n \t\t\t\t\t\t_(\"Stopped at %s...  %.*s\\n\"),\n \t\t\t\t\t\tshort_commit_name(commit),\n-\t\t\t\t\t\titem->arg_len, item->arg);\n+\t\t\t\t\t\titem->arg_len, arg);\n \t\t\t\treturn error_with_patch(r, commit,\n-\t\t\t\t\titem->arg, item->arg_len, opts, res,\n-\t\t\t\t\t!res);\n+\t\t\t\t\targ, item->arg_len, opts, res, !res);\n \t\t\t}\n \t\t\tif (is_rebase_i(opts) && !res)\n \t\t\t\trecord_in_rewritten(&item->commit->object.oid,\n@@ -3554,7 +3562,7 @@ static int pick_commits(struct repository *r,\n \t\t\t\tif (res == 1)\n \t\t\t\t\tintend_to_amend();\n \t\t\t\treturn error_failed_squash(r, item->commit, opts,\n-\t\t\t\t\titem->arg_len, item->arg);\n+\t\t\t\t\titem->arg_len, arg);\n \t\t\t} else if (res && is_rebase_i(opts) && item->commit) {\n \t\t\t\tint to_amend = 0;\n \t\t\t\tstruct object_id oid;\n@@ -3573,16 +3581,16 @@ static int pick_commits(struct repository *r,\n \t\t\t\t\tto_amend = 1;\n \n \t\t\t\treturn res | error_with_patch(r, item->commit,\n-\t\t\t\t\t\titem->arg, item->arg_len, opts,\n+\t\t\t\t\t\targ, item->arg_len, opts,\n \t\t\t\t\t\tres, to_amend);\n \t\t\t}\n \t\t} else if (item->command == TODO_EXEC) {\n-\t\t\tchar *end_of_arg = (char *)(item->arg + item->arg_len);\n+\t\t\tchar *end_of_arg = (char *)(arg + item->arg_len);\n \t\t\tint saved = *end_of_arg;\n \t\t\tstruct stat st;\n \n \t\t\t*end_of_arg = '\\0';\n-\t\t\tres = do_exec(r, item->arg);\n+\t\t\tres = do_exec(r, arg);\n \t\t\t*end_of_arg = saved;\n \n \t\t\t/* Reread the todo file if it has changed. */\n@@ -3599,14 +3607,14 @@ static int pick_commits(struct repository *r,\n \t\t\t\ttodo_list->current = -1;\n \t\t\t}\n \t\t} else if (item->command == TODO_LABEL) {\n-\t\t\tif ((res = do_label(r, item->arg, item->arg_len)))\n+\t\t\tif ((res = do_label(r, arg, item->arg_len)))\n \t\t\t\treschedule = 1;\n \t\t} else if (item->command == TODO_RESET) {\n-\t\t\tif ((res = do_reset(r, item->arg, item->arg_len, opts)))\n+\t\t\tif ((res = do_reset(r, arg, item->arg_len, opts)))\n \t\t\t\treschedule = 1;\n \t\t} else if (item->command == TODO_MERGE) {\n \t\t\tif ((res = do_merge(r, item->commit,\n-\t\t\t\t\t    item->arg, item->arg_len,\n+\t\t\t\t\t    arg, item->arg_len,\n \t\t\t\t\t    item->flags, opts)) < 0)\n \t\t\t\treschedule = 1;\n \t\t\telse if (item->commit)\n@@ -3615,9 +3623,8 @@ static int pick_commits(struct repository *r,\n \t\t\tif (res > 0)\n \t\t\t\t/* failed with merge conflicts */\n \t\t\t\treturn error_with_patch(r, item->commit,\n-\t\t\t\t\t\t\titem->arg,\n-\t\t\t\t\t\t\titem->arg_len, opts,\n-\t\t\t\t\t\t\tres, 0);\n+\t\t\t\t\t\t\targ, item->arg_len,\n+\t\t\t\t\t\t\topts, res, 0);\n \t\t} else if (!is_noop(item->command))\n \t\t\treturn error(_(\"unknown command %d\"), item->command);\n \n@@ -3632,9 +3639,8 @@ static int pick_commits(struct repository *r,\n \t\t\tif (item->commit)\n \t\t\t\treturn error_with_patch(r,\n \t\t\t\t\t\t\titem->commit,\n-\t\t\t\t\t\t\titem->arg,\n-\t\t\t\t\t\t\titem->arg_len, opts,\n-\t\t\t\t\t\t\tres, 0);\n+\t\t\t\t\t\t\targ, item->arg_len,\n+\t\t\t\t\t\t\topts, res, 0);\n \t\t}\n \n \t\ttodo_list->current++;\n@@ -4575,7 +4581,8 @@ int transform_todos(struct repository *r, unsigned flags)\n \tfor (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n-\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len, item->arg);\n+\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len,\n+\t\t\t\t    todo_item_get_arg(&todo_list, item));\n \t\t\tcontinue;\n \t\t}\n \n@@ -4605,7 +4612,8 @@ int transform_todos(struct repository *r, unsigned flags)\n \t\tif (!item->arg_len)\n \t\t\tstrbuf_addch(&buf, '\\n');\n \t\telse\n-\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len, item->arg);\n+\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len,\n+\t\t\t\t    todo_item_get_arg(&todo_list, item));\n \t}\n \n \ti = write_message(buf.buf, buf.len, todo_file, 0);\n@@ -4681,7 +4689,8 @@ int check_todo_list(struct repository *r)\n \t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n \t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n \t\t\t\t    short_commit_name(commit),\n-\t\t\t\t    item->arg_len, item->arg);\n+\t\t\t\t    item->arg_len,\n+\t\t\t\t    todo_item_get_arg(&todo_list, item));\n \t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n \t\t}\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex c6360bac66..50d552429c 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -104,9 +104,9 @@ struct todo_item {\n \tenum todo_command command;\n \tstruct commit *commit;\n \tunsigned int flags;\n-\tconst char *arg;\n \tint arg_len;\n-\tsize_t offset_in_buf;\n+\t/* The offset of the command and its argument in the strbuf */\n+\tsize_t offset_in_buf, arg_offset;\n };\n \n struct todo_list {\n@@ -122,6 +122,8 @@ struct todo_list {\n int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \t\t\t\tstruct todo_list *todo_list);\n void todo_list_release(struct todo_list *todo_list);\n+const char *todo_item_get_arg(struct todo_list *todo_list,\n+\t\t\t      struct todo_item *item);\n \n /* Call this to setup defaults before parsing command line options */\n void sequencer_init_config(struct replay_opts *opts);\n-- \n2.20.1\n\n"},{"id":"368964","messageId":"20190210132648.12821-3-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190210132648.12821-1-alban.gruin@gmail.com","subject":"[PATCH v7 02/16] sequencer: make the todo_list structure public","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-10T13:26:34Z","receivedAt":"2019-02-10T13:27:15Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This makes the structures todo_list and todo_item, and the functions\ntodo_list_release() and parse_insn_buffer(), accessible outside of\nsequencer.c.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v6.\n\n sequencer.c | 69 ++++++++++-------------------------------------------\n sequencer.h | 50 ++++++++++++++++++++++++++++++++++++++\n 2 files changed, 62 insertions(+), 57 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex d605199a54..25cc7a9a91 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1510,32 +1510,6 @@ static int allow_empty(struct repository *r,\n \t\treturn 1;\n }\n \n-/*\n- * Note that ordering matters in this enum. Not only must it match the mapping\n- * below, it is also divided into several sections that matter.  When adding\n- * new commands, make sure you add it in the right section.\n- */\n-enum todo_command {\n-\t/* commands that handle commits */\n-\tTODO_PICK = 0,\n-\tTODO_REVERT,\n-\tTODO_EDIT,\n-\tTODO_REWORD,\n-\tTODO_FIXUP,\n-\tTODO_SQUASH,\n-\t/* commands that do something else than handling a single commit */\n-\tTODO_EXEC,\n-\tTODO_BREAK,\n-\tTODO_LABEL,\n-\tTODO_RESET,\n-\tTODO_MERGE,\n-\t/* commands that do nothing but are counted for reporting progress */\n-\tTODO_NOOP,\n-\tTODO_DROP,\n-\t/* comments (not counted for reporting progress) */\n-\tTODO_COMMENT\n-};\n-\n static struct {\n \tchar c;\n \tconst char *str;\n@@ -2012,26 +1986,7 @@ enum todo_item_flags {\n \tTODO_EDIT_MERGE_MSG = 1\n };\n \n-struct todo_item {\n-\tenum todo_command command;\n-\tstruct commit *commit;\n-\tunsigned int flags;\n-\tconst char *arg;\n-\tint arg_len;\n-\tsize_t offset_in_buf;\n-};\n-\n-struct todo_list {\n-\tstruct strbuf buf;\n-\tstruct todo_item *items;\n-\tint nr, alloc, current;\n-\tint done_nr, total_nr;\n-\tstruct stat_data stat;\n-};\n-\n-#define TODO_LIST_INIT { STRBUF_INIT }\n-\n-static void todo_list_release(struct todo_list *todo_list)\n+void todo_list_release(struct todo_list *todo_list)\n {\n \tstrbuf_release(&todo_list->buf);\n \tFREE_AND_NULL(todo_list->items);\n@@ -2134,8 +2089,8 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \treturn !item->commit;\n }\n \n-static int parse_insn_buffer(struct repository *r, char *buf,\n-\t\t\t     struct todo_list *todo_list)\n+int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n+\t\t\t\tstruct todo_list *todo_list)\n {\n \tstruct todo_item *item;\n \tchar *p = buf, *next_p;\n@@ -2234,7 +2189,7 @@ static int read_populate_todo(struct repository *r,\n \t\treturn error(_(\"could not stat '%s'\"), todo_file);\n \tfill_stat_data(&todo_list->stat, &st);\n \n-\tres = parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n+\tres = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n \tif (res) {\n \t\tif (is_rebase_i(opts))\n \t\t\treturn error(_(\"please fix this using \"\n@@ -2265,7 +2220,7 @@ static int read_populate_todo(struct repository *r,\n \t\tFILE *f = fopen_or_warn(rebase_path_msgtotal(), \"w\");\n \n \t\tif (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&\n-\t\t    !parse_insn_buffer(r, done.buf.buf, &done))\n+\t\t    !todo_list_parse_insn_buffer(r, done.buf.buf, &done))\n \t\t\ttodo_list->done_nr = count_commands(&done);\n \t\telse\n \t\t\ttodo_list->done_nr = 0;\n@@ -4556,7 +4511,7 @@ int sequencer_add_exec_commands(struct repository *r,\n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4612,7 +4567,7 @@ int transform_todos(struct repository *r, unsigned flags)\n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4698,7 +4653,7 @@ int check_todo_list(struct repository *r)\n \t\tgoto leave_check;\n \t}\n \tadvise_to_edit_todo = res =\n-\t\tparse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n+\t\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n \n \tif (res || check_level == MISSING_COMMIT_CHECK_IGNORE)\n \t\tgoto leave_check;\n@@ -4717,7 +4672,7 @@ int check_todo_list(struct repository *r)\n \t\tgoto leave_check;\n \t}\n \tstrbuf_release(&todo_file);\n-\tres = !!parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n+\tres = !!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n \n \t/* Find commits in git-rebase-todo.backup yet unseen */\n \tfor (i = todo_list.nr - 1; i >= 0; i--) {\n@@ -4799,7 +4754,7 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \n \tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n \t\treturn -1;\n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n@@ -4887,7 +4842,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(r, buf->buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(r, buf->buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4995,7 +4950,7 @@ int rearrange_squash(struct repository *r)\n \n \tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n \t\treturn -1;\n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex 9d83f0f3e9..c6360bac66 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -73,6 +73,56 @@ enum missing_commit_check_level {\n int write_message(const void *buf, size_t len, const char *filename,\n \t\t  int append_eol);\n \n+/*\n+ * Note that ordering matters in this enum. Not only must it match the mapping\n+ * of todo_command_info (in sequencer.c), it is also divided into several\n+ * sections that matter.  When adding new commands, make sure you add it in the\n+ * right section.\n+ */\n+enum todo_command {\n+\t/* commands that handle commits */\n+\tTODO_PICK = 0,\n+\tTODO_REVERT,\n+\tTODO_EDIT,\n+\tTODO_REWORD,\n+\tTODO_FIXUP,\n+\tTODO_SQUASH,\n+\t/* commands that do something else than handling a single commit */\n+\tTODO_EXEC,\n+\tTODO_BREAK,\n+\tTODO_LABEL,\n+\tTODO_RESET,\n+\tTODO_MERGE,\n+\t/* commands that do nothing but are counted for reporting progress */\n+\tTODO_NOOP,\n+\tTODO_DROP,\n+\t/* comments (not counted for reporting progress) */\n+\tTODO_COMMENT\n+};\n+\n+struct todo_item {\n+\tenum todo_command command;\n+\tstruct commit *commit;\n+\tunsigned int flags;\n+\tconst char *arg;\n+\tint arg_len;\n+\tsize_t offset_in_buf;\n+};\n+\n+struct todo_list {\n+\tstruct strbuf buf;\n+\tstruct todo_item *items;\n+\tint nr, alloc, current;\n+\tint done_nr, total_nr;\n+\tstruct stat_data stat;\n+};\n+\n+#define TODO_LIST_INIT { STRBUF_INIT }\n+\n+int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n+\t\t\t\tstruct todo_list *todo_list);\n+void todo_list_release(struct todo_list *todo_list);\n+\n /* Call this to setup defaults before parsing command line options */\n void sequencer_init_config(struct replay_opts *opts);\n int sequencer_pick_revisions(struct repository *repo,\n-- \n2.20.1\n\n"},{"id":"368965","messageId":"20190210132648.12821-5-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190210132648.12821-1-alban.gruin@gmail.com","subject":"[PATCH v7 04/16] sequencer: refactor transform_todos() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-10T13:26:36Z","receivedAt":"2019-02-10T13:27:17Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors transform_todos() to work on a todo_list.  The function\nis renamed todo_list_transform().\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, transform_todo_file().  It is still used by\ncomplete_action() and edit_todo_list() for now, but they will be\nreplaced in a future commit.\n\ntodo_list_transform() is not a static function, because it will be used\nby edit_todo_list() from rebase-interactive.c in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v6.\n\n builtin/rebase--interactive.c |  2 +-\n rebase-interactive.c          |  4 +--\n sequencer.c                   | 54 +++++++++++++++++++++++------------\n sequencer.h                   |  4 ++-\n 4 files changed, 42 insertions(+), 22 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex dd2a55ab1d..0898eb4c59 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -253,7 +253,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t}\n \tcase SHORTEN_OIDS:\n \tcase EXPAND_OIDS:\n-\t\tret = transform_todos(the_repository, flags);\n+\t\tret = transform_todo_file(the_repository, flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n \t\tret = check_todo_list(the_repository);\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 68aff1dac2..842fa07e7e 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -69,7 +69,7 @@ int edit_todo_list(struct repository *r, unsigned flags)\n \n \tstrbuf_release(&buf);\n \n-\ttransform_todos(r, flags | TODO_LIST_SHORTEN_IDS);\n+\ttransform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS);\n \n \tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n@@ -85,7 +85,7 @@ int edit_todo_list(struct repository *r, unsigned flags)\n \tif (launch_sequence_editor(todo_file, NULL, NULL))\n \t\treturn -1;\n \n-\ttransform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS));\n+\ttransform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS));\n \n \treturn 0;\n }\ndiff --git a/sequencer.c b/sequencer.c\nindex c844a9b7f3..346706029c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4562,27 +4562,18 @@ int sequencer_add_exec_commands(struct repository *r,\n \treturn i;\n }\n \n-int transform_todos(struct repository *r, unsigned flags)\n+void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n+\t\t\t unsigned flags)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct todo_item *item;\n \tint i;\n \n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tfor (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {\n+\tfor (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n \t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len,\n-\t\t\t\t    todo_item_get_arg(&todo_list, item));\n+\t\t\t\t    todo_item_get_arg(todo_list, item));\n \t\t\tcontinue;\n \t\t}\n \n@@ -4613,12 +4604,39 @@ int transform_todos(struct repository *r, unsigned flags)\n \t\t\tstrbuf_addch(&buf, '\\n');\n \t\telse\n \t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len,\n-\t\t\t\t    todo_item_get_arg(&todo_list, item));\n+\t\t\t\t    todo_item_get_arg(todo_list, item));\n \t}\n \n-\ti = write_message(buf.buf, buf.len, todo_file, 0);\n+\tstrbuf_reset(&todo_list->buf);\n+\tstrbuf_add(&todo_list->buf, buf.buf, buf.len);\n+\tstrbuf_release(&buf);\n+\n+\tif (todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list))\n+\t\tBUG(\"unusable todo list\");\n+}\n+\n+int transform_todo_file(struct repository *r, unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_transform(r, &todo_list, flags);\n+\n+\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n \ttodo_list_release(&todo_list);\n-\treturn i;\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n }\n \n enum missing_commit_check_level get_missing_commit_check_level(void)\n@@ -4880,7 +4898,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n \t\t\t     rebase_path_todo_backup());\n \n-\tif (transform_todos(r, flags | TODO_LIST_SHORTEN_IDS))\n+\tif (transform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS))\n \t\treturn error(_(\"could not transform the todo list\"));\n \n \tstrbuf_reset(buf);\n@@ -4909,7 +4927,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn -1;\n \t}\n \n-\tif (transform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS)))\n+\tif (transform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS)))\n \t\treturn error(_(\"could not transform the todo list\"));\n \n \tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\ndiff --git a/sequencer.h b/sequencer.h\nindex 50d552429c..2ddb20cbc1 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -121,6 +121,8 @@ struct todo_list {\n \n int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \t\t\t\tstruct todo_list *todo_list);\n+void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n+\t\t\t unsigned flags);\n void todo_list_release(struct todo_list *todo_list);\n const char *todo_item_get_arg(struct todo_list *todo_list,\n \t\t\t      struct todo_item *item);\n@@ -148,7 +150,7 @@ int sequencer_make_script(struct repository *repo, FILE *out,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(struct repository *r, const char *command);\n-int transform_todos(struct repository *r, unsigned flags);\n+int transform_todo_file(struct repository *r, unsigned flags);\n enum missing_commit_check_level get_missing_commit_check_level(void);\n int check_todo_list(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n-- \n2.20.1\n\n"},{"id":"368967","messageId":"20190210132648.12821-7-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190210132648.12821-1-alban.gruin@gmail.com","subject":"[PATCH v7 06/16] sequencer: refactor check_todo_list() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-10T13:26:38Z","receivedAt":"2019-02-10T13:27:19Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors check_todo_list() to work on a todo_list to avoid\nredundant reads and writes to the disk.  The function is renamed\ntodo_list_check().  The parsing of the two todo lists is left to the\ncaller.\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, check_todo_list_from_file().  It reads the file\nfrom the disk, parses it, pass the todo_list to todo_list_check(), and\nwrites it back to the disk.\n\nAs get_missing_commit_check_level() and the enum\nmissing_commit_check_level are no longer needed inside of sequencer.c,\nthey are moved to rebase-interactive.c, and made static again.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v6.\n\n builtin/rebase--interactive.c |   2 +-\n rebase-interactive.c          |  91 ++++++++++++++++++++++++-\n rebase-interactive.h          |   2 +\n sequencer.c                   | 121 +++++++---------------------------\n sequencer.h                   |   9 +--\n 5 files changed, 117 insertions(+), 108 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 0898eb4c59..df19ccaeb9 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -256,7 +256,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = transform_todo_file(the_repository, flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n-\t\tret = check_todo_list(the_repository);\n+\t\tret = check_todo_list_from_file(the_repository);\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n \t\tret = rearrange_squash(the_repository);\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 842fa07e7e..dfa6dd530f 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -1,8 +1,32 @@\n #include \"cache.h\"\n #include \"commit.h\"\n-#include \"rebase-interactive.h\"\n #include \"sequencer.h\"\n+#include \"rebase-interactive.h\"\n #include \"strbuf.h\"\n+#include \"commit-slab.h\"\n+#include \"config.h\"\n+\n+enum missing_commit_check_level {\n+\tMISSING_COMMIT_CHECK_IGNORE = 0,\n+\tMISSING_COMMIT_CHECK_WARN,\n+\tMISSING_COMMIT_CHECK_ERROR\n+};\n+\n+static enum missing_commit_check_level get_missing_commit_check_level(void)\n+{\n+\tconst char *value;\n+\n+\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n+\t\t\t!strcasecmp(\"ignore\", value))\n+\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n+\tif (!strcasecmp(\"warn\", value))\n+\t\treturn MISSING_COMMIT_CHECK_WARN;\n+\tif (!strcasecmp(\"error\", value))\n+\t\treturn MISSING_COMMIT_CHECK_ERROR;\n+\twarning(_(\"unrecognized setting %s for option \"\n+\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n+\treturn MISSING_COMMIT_CHECK_IGNORE;\n+}\n \n void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \t\t      struct strbuf *buf)\n@@ -89,3 +113,68 @@ int edit_todo_list(struct repository *r, unsigned flags)\n \n \treturn 0;\n }\n+\n+define_commit_slab(commit_seen, unsigned char);\n+/*\n+ * Check if the user dropped some commits by mistake\n+ * Behaviour determined by rebase.missingCommitsCheck.\n+ * Check if there is an unrecognized command or a\n+ * bad SHA-1 in a command.\n+ */\n+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo)\n+{\n+\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n+\tstruct strbuf missing = STRBUF_INIT;\n+\tint res = 0, i;\n+\tstruct commit_seen commit_seen;\n+\n+\tinit_commit_seen(&commit_seen);\n+\n+\tif (check_level == MISSING_COMMIT_CHECK_IGNORE)\n+\t\tgoto leave_check;\n+\n+\t/* Mark the commits in git-rebase-todo as seen */\n+\tfor (i = 0; i < new_todo->nr; i++) {\n+\t\tstruct commit *commit = new_todo->items[i].commit;\n+\t\tif (commit)\n+\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t}\n+\n+\t/* Find commits in git-rebase-todo.backup yet unseen */\n+\tfor (i = old_todo->nr - 1; i >= 0; i--) {\n+\t\tstruct todo_item *item = old_todo->items + i;\n+\t\tstruct commit *commit = item->commit;\n+\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n+\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n+\t\t\t\t    find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV),\n+\t\t\t\t    item->arg_len,\n+\t\t\t\t    todo_item_get_arg(old_todo, item));\n+\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t\t}\n+\t}\n+\n+\t/* Warn about missing commits */\n+\tif (!missing.len)\n+\t\tgoto leave_check;\n+\n+\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n+\t\tres = 1;\n+\n+\tfprintf(stderr,\n+\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n+\t\t\"Dropped commits (newer to older):\\n\"));\n+\n+\t/* Make the list user-friendly and display */\n+\tfputs(missing.buf, stderr);\n+\tstrbuf_release(&missing);\n+\n+\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n+\t\t\"explicitly remove a commit.\\n\\n\"\n+\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n+\t\t\"the level of warnings.\\n\"\n+\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n+\n+leave_check:\n+\tclear_commit_seen(&commit_seen);\n+\treturn res;\n+}\ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 17b6c9f6d0..187b5032d6 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -3,9 +3,11 @@\n \n struct strbuf;\n struct repository;\n+struct todo_list;\n \n void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \t\t      struct strbuf *buf);\n int edit_todo_list(struct repository *r, unsigned flags);\n+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n \n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex 4809b22ce4..99e12c751e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4660,112 +4660,37 @@ int transform_todo_file(struct repository *r, unsigned flags)\n \treturn 0;\n }\n \n-enum missing_commit_check_level get_missing_commit_check_level(void)\n-{\n-\tconst char *value;\n-\n-\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n-\t\t\t!strcasecmp(\"ignore\", value))\n-\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n-\tif (!strcasecmp(\"warn\", value))\n-\t\treturn MISSING_COMMIT_CHECK_WARN;\n-\tif (!strcasecmp(\"error\", value))\n-\t\treturn MISSING_COMMIT_CHECK_ERROR;\n-\twarning(_(\"unrecognized setting %s for option \"\n-\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n-\treturn MISSING_COMMIT_CHECK_IGNORE;\n-}\n+static const char edit_todo_list_advice[] =\n+N_(\"You can fix this with 'git rebase --edit-todo' \"\n+\"and then run 'git rebase --continue'.\\n\"\n+\"Or you can abort the rebase with 'git rebase\"\n+\" --abort'.\\n\");\n \n-define_commit_slab(commit_seen, unsigned char);\n-/*\n- * Check if the user dropped some commits by mistake\n- * Behaviour determined by rebase.missingCommitsCheck.\n- * Check if there is an unrecognized command or a\n- * bad SHA-1 in a command.\n- */\n-int check_todo_list(struct repository *r)\n+int check_todo_list_from_file(struct repository *r)\n {\n-\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n-\tstruct strbuf todo_file = STRBUF_INIT;\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf missing = STRBUF_INIT;\n-\tint advise_to_edit_todo = 0, res = 0, i;\n-\tstruct commit_seen commit_seen;\n-\n-\tinit_commit_seen(&commit_seen);\n+\tstruct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;\n+\tint res = 0;\n \n-\tstrbuf_addstr(&todo_file, rebase_path_todo());\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n+\tif (strbuf_read_file_or_whine(&new_todo.buf, rebase_path_todo()) < 0) {\n \t\tres = -1;\n-\t\tgoto leave_check;\n-\t}\n-\tadvise_to_edit_todo = res =\n-\t\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n-\n-\tif (res || check_level == MISSING_COMMIT_CHECK_IGNORE)\n-\t\tgoto leave_check;\n-\n-\t/* Mark the commits in git-rebase-todo as seen */\n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tstruct commit *commit = todo_list.items[i].commit;\n-\t\tif (commit)\n-\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t\tgoto out;\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\tstrbuf_addstr(&todo_file, \".backup\");\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n+\tif (strbuf_read_file_or_whine(&old_todo.buf, rebase_path_todo_backup()) < 0) {\n \t\tres = -1;\n-\t\tgoto leave_check;\n-\t}\n-\tstrbuf_release(&todo_file);\n-\tres = !!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n-\n-\t/* Find commits in git-rebase-todo.backup yet unseen */\n-\tfor (i = todo_list.nr - 1; i >= 0; i--) {\n-\t\tstruct todo_item *item = todo_list.items + i;\n-\t\tstruct commit *commit = item->commit;\n-\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n-\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n-\t\t\t\t    short_commit_name(commit),\n-\t\t\t\t    item->arg_len,\n-\t\t\t\t    todo_item_get_arg(&todo_list, item));\n-\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n-\t\t}\n+\t\tgoto out;\n \t}\n \n-\t/* Warn about missing commits */\n-\tif (!missing.len)\n-\t\tgoto leave_check;\n-\n-\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n-\t\tadvise_to_edit_todo = res = 1;\n-\n-\tfprintf(stderr,\n-\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n-\t\t\"Dropped commits (newer to older):\\n\"));\n-\n-\t/* Make the list user-friendly and display */\n-\tfputs(missing.buf, stderr);\n-\tstrbuf_release(&missing);\n-\n-\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n-\t\t\"explicitly remove a commit.\\n\\n\"\n-\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n-\t\t\"the level of warnings.\\n\"\n-\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n-\n-leave_check:\n-\tclear_commit_seen(&commit_seen);\n-\tstrbuf_release(&todo_file);\n-\ttodo_list_release(&todo_list);\n-\n-\tif (advise_to_edit_todo)\n-\t\tfprintf(stderr,\n-\t\t\t_(\"You can fix this with 'git rebase --edit-todo' \"\n-\t\t\t  \"and then run 'git rebase --continue'.\\n\"\n-\t\t\t  \"Or you can abort the rebase with 'git rebase\"\n-\t\t\t  \" --abort'.\\n\"));\n+\tres = todo_list_parse_insn_buffer(r, old_todo.buf.buf, &old_todo);\n+\tif (!res)\n+\t\tres = todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo);\n+\tif (!res)\n+\t\tres = todo_list_check(&old_todo, &new_todo);\n+\tif (res)\n+\t\tfprintf(stderr, _(edit_todo_list_advice));\n+out:\n+\ttodo_list_release(&old_todo);\n+\ttodo_list_release(&new_todo);\n \n \treturn res;\n }\n@@ -4943,7 +4868,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \n \ttodo_list_release(&todo_list);\n \n-\tif (check_todo_list(r)) {\n+\tif (check_todo_list_from_file(r)) {\n \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n \t\treturn -1;\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex 7278f9675b..217353e9f0 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -64,12 +64,6 @@ struct replay_opts {\n };\n #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }\n \n-enum missing_commit_check_level {\n-\tMISSING_COMMIT_CHECK_IGNORE = 0,\n-\tMISSING_COMMIT_CHECK_WARN,\n-\tMISSING_COMMIT_CHECK_ERROR\n-};\n-\n int write_message(const void *buf, size_t len, const char *filename,\n \t\t  int append_eol);\n \n@@ -154,8 +148,7 @@ int sequencer_make_script(struct repository *r, FILE *out, int argc,\n \n int sequencer_add_exec_commands(struct repository *r, const char *command);\n int transform_todo_file(struct repository *r, unsigned flags);\n-enum missing_commit_check_level get_missing_commit_check_level(void);\n-int check_todo_list(struct repository *r);\n+int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, const char *cmd,\n-- \n2.20.1\n\n"},{"id":"368966","messageId":"20190210132648.12821-6-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190210132648.12821-1-alban.gruin@gmail.com","subject":"[PATCH v7 05/16] sequencer: introduce todo_list_write_to_file()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-10T13:26:37Z","receivedAt":"2019-02-10T13:27:20Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This introduces a new function to recreate the text of a todo list from\nits commands and write it to a file.  This will be useful as the next\nfew commits will change the use of the buffer in struct todo_list so it\nwill no longer be a mirror of the file on disk.\n\nThis functionality already exists in todo_list_transform(), but this\nfunction was made to replace the buffer of a todo list, which is not\nwhat we want here.  Thus, the part of todo_list_transform() that\nreplaces the buffer is dropped, and the function is renamed\ntodo_list_to_strbuf().  It is called by todo_list_write_to_file() to\nfill the buffer to write to the disk.\n\ntodo_list_write_to_file() can also take care of appending the help text\nto the buffer before writing it to the disk, or to write only the first\nn items of the list.  This feature will be used by\nskip_unnecessary_picks(), which has to write done commands in a file.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v6.\n\n sequencer.c | 61 +++++++++++++++++++++++++++++++++++------------------\n sequencer.h | 11 ++++++----\n 2 files changed, 48 insertions(+), 24 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 346706029c..4809b22ce4 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4562,26 +4562,28 @@ int sequencer_add_exec_commands(struct repository *r,\n \treturn i;\n }\n \n-void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n-\t\t\t unsigned flags)\n+static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n+\t\t\t\tstruct strbuf *buf, int num, unsigned flags)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n \tstruct todo_item *item;\n-\tint i;\n+\tint i, max = todo_list->nr;\n \n-\tfor (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {\n+\tif (num > 0 && num < max)\n+\t\tmax = num;\n+\n+\tfor (item = todo_list->items, i = 0; i < max; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n-\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len,\n+\t\t\tstrbuf_addf(buf, \"%.*s\\n\", item->arg_len,\n \t\t\t\t    todo_item_get_arg(todo_list, item));\n \t\t\tcontinue;\n \t\t}\n \n \t\t/* add command to the buffer */\n \t\tif (flags & TODO_LIST_ABBREVIATE_CMDS)\n-\t\t\tstrbuf_addch(&buf, command_to_char(item->command));\n+\t\t\tstrbuf_addch(buf, command_to_char(item->command));\n \t\telse\n-\t\t\tstrbuf_addstr(&buf, command_to_string(item->command));\n+\t\t\tstrbuf_addstr(buf, command_to_string(item->command));\n \n \t\t/* add commit id */\n \t\tif (item->commit) {\n@@ -4591,28 +4593,48 @@ void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n \n \t\t\tif (item->command == TODO_MERGE) {\n \t\t\t\tif (item->flags & TODO_EDIT_MERGE_MSG)\n-\t\t\t\t\tstrbuf_addstr(&buf, \" -c\");\n+\t\t\t\t\tstrbuf_addstr(buf, \" -c\");\n \t\t\t\telse\n-\t\t\t\t\tstrbuf_addstr(&buf, \" -C\");\n+\t\t\t\t\tstrbuf_addstr(buf, \" -C\");\n \t\t\t}\n \n-\t\t\tstrbuf_addf(&buf, \" %s\", oid);\n+\t\t\tstrbuf_addf(buf, \" %s\", oid);\n \t\t}\n \n \t\t/* add all the rest */\n \t\tif (!item->arg_len)\n-\t\t\tstrbuf_addch(&buf, '\\n');\n+\t\t\tstrbuf_addch(buf, '\\n');\n \t\telse\n-\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len,\n+\t\t\tstrbuf_addf(buf, \" %.*s\\n\", item->arg_len,\n \t\t\t\t    todo_item_get_arg(todo_list, item));\n \t}\n+}\n \n-\tstrbuf_reset(&todo_list->buf);\n-\tstrbuf_add(&todo_list->buf, buf.buf, buf.len);\n+int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n+\t\t\t    const char *file, const char *shortrevisions,\n+\t\t\t    const char *shortonto, int num, unsigned flags)\n+{\n+\tint edit_todo = !(shortrevisions && shortonto), res;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\ttodo_list_to_strbuf(r, todo_list, &buf, num, flags);\n+\n+\tif (flags & TODO_LIST_APPEND_TODO_HELP) {\n+\t\tint command_count = count_commands(todo_list);\n+\t\tif (!edit_todo) {\n+\t\t\tstrbuf_addch(&buf, '\\n');\n+\t\t\tstrbuf_commented_addf(&buf, Q_(\"Rebase %s onto %s (%d command)\",\n+\t\t\t\t\t\t       \"Rebase %s onto %s (%d commands)\",\n+\t\t\t\t\t\t       command_count),\n+\t\t\t\t\t      shortrevisions, shortonto, command_count);\n+\t\t}\n+\t\tappend_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, &buf);\n+\t}\n+\n+\tres = write_message(buf.buf, buf.len, file, 0);\n \tstrbuf_release(&buf);\n \n-\tif (todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list))\n-\t\tBUG(\"unusable todo list\");\n+\treturn res;\n }\n \n int transform_todo_file(struct repository *r, unsigned flags)\n@@ -4629,9 +4651,8 @@ int transform_todo_file(struct repository *r, unsigned flags)\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n \n-\ttodo_list_transform(r, &todo_list, flags);\n-\n-\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n+\tres = todo_list_write_to_file(r, &todo_list, todo_file,\n+\t\t\t\t      NULL, NULL, -1, flags);\n \ttodo_list_release(&todo_list);\n \n \tif (res)\ndiff --git a/sequencer.h b/sequencer.h\nindex 2ddb20cbc1..7278f9675b 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -121,8 +121,9 @@ struct todo_list {\n \n int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \t\t\t\tstruct todo_list *todo_list);\n-void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n-\t\t\t unsigned flags);\n+int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n+\t\t\t    const char *file, const char *shortrevisions,\n+\t\t\t    const char *shortonto, int num, unsigned flags);\n void todo_list_release(struct todo_list *todo_list);\n const char *todo_item_get_arg(struct todo_list *todo_list,\n \t\t\t      struct todo_item *item);\n@@ -145,8 +146,10 @@ int sequencer_remove_state(struct replay_opts *opts);\n  * commits should be rebased onto the new base, this flag needs to be passed.\n  */\n #define TODO_LIST_REBASE_COUSINS (1U << 4)\n-int sequencer_make_script(struct repository *repo, FILE *out,\n-\t\t\t  int argc, const char **argv,\n+#define TODO_LIST_APPEND_TODO_HELP (1U << 5)\n+\n+int sequencer_make_script(struct repository *r, FILE *out, int argc,\n+\t\t\t  const char **argv,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(struct repository *r, const char *command);\n-- \n2.20.1\n\n"},{"id":"368968","messageId":"20190210132648.12821-8-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190210132648.12821-1-alban.gruin@gmail.com","subject":"[PATCH v7 07/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-10T13:26:39Z","receivedAt":"2019-02-10T13:27:22Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors sequencer_add_exec_commands() to work on a todo_list to\navoid redundant reads and writes to the disk.\n\nInstead of inserting the `exec' commands between the other commands and\nre-parsing the buffer at the end, they are appended to the buffer once,\nand a new list of items is created.  Items from the old list are copied\nacross and new `exec' items are appended when necessary.  This\neliminates the need to reparse the buffer, but this also means we have\nto use todo_list_write_to_disk() to write the file.\n\ntodo_list_add_exec_commands() and sequencer_add_exec_commands() are\nmodified to take a string list instead of a string -- one item for each\ncommand.  This makes it easier to insert a new command to the todo list\nfor each command to execute.\n\nsequencer_add_exec_commands() still reads the todo list from the disk,\nas it is needed by rebase -p.\n\ncomplete_action() still uses sequencer_add_exec_commands() for now.\nThis will be changed in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |  15 +++--\n sequencer.c                   | 109 +++++++++++++++++++++-------------\n sequencer.h                   |   5 +-\n 3 files changed, 82 insertions(+), 47 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex df19ccaeb9..c131fd4a27 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -65,7 +65,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \t\t\t\t const char *onto, const char *onto_name,\n \t\t\t\t const char *squash_onto, const char *head_name,\n \t\t\t\t const char *restrict_revision, char *raw_strategies,\n-\t\t\t\t const char *cmd, unsigned autosquash)\n+\t\t\t\t struct string_list *commands, unsigned autosquash)\n {\n \tint ret;\n \tconst char *head_hash = NULL;\n@@ -116,7 +116,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \t\tdiscard_cache();\n \t\tret = complete_action(the_repository, opts, flags,\n \t\t\t\t      shortrevisions, onto_name, onto,\n-\t\t\t\t      head_hash, cmd, autosquash);\n+\t\t\t\t      head_hash, commands, autosquash);\n \t}\n \n \tfree(revisions);\n@@ -139,6 +139,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \tconst char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,\n \t\t*squash_onto = NULL, *upstream = NULL, *head_name = NULL,\n \t\t*switch_to = NULL, *cmd = NULL;\n+\tstruct string_list commands = STRING_LIST_INIT_DUP;\n \tchar *raw_strategies = NULL;\n \tenum {\n \t\tNONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,\n@@ -221,6 +222,11 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\twarning(_(\"--[no-]rebase-cousins has no effect without \"\n \t\t\t  \"--rebase-merges\"));\n \n+\tif (cmd && *cmd) {\n+\t\tstring_list_split(&commands, cmd, '\\n', -1);\n+\t\t--commands.nr;\n+\t}\n+\n \tswitch (command) {\n \tcase NONE:\n \t\tif (!onto && !upstream)\n@@ -228,7 +234,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \n \t\tret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,\n \t\t\t\t\t    onto_name, squash_onto, head_name, restrict_revision,\n-\t\t\t\t\t    raw_strategies, cmd, autosquash);\n+\t\t\t\t\t    raw_strategies, &commands, autosquash);\n \t\tbreak;\n \tcase SKIP: {\n \t\tstruct string_list merge_rr = STRING_LIST_INIT_DUP;\n@@ -262,11 +268,12 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = rearrange_squash(the_repository);\n \t\tbreak;\n \tcase ADD_EXEC:\n-\t\tret = sequencer_add_exec_commands(the_repository, cmd);\n+\t\tret = sequencer_add_exec_commands(the_repository, &commands);\n \t\tbreak;\n \tdefault:\n \t\tBUG(\"invalid command '%d'\", command);\n \t}\n \n+\tstring_list_clear(&commands, 1);\n \treturn !!ret;\n }\ndiff --git a/sequencer.c b/sequencer.c\nindex 99e12c751e..82ca3432cd 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4505,21 +4505,27 @@ int sequencer_make_script(struct repository *r, FILE *out,\n  * Add commands after pick and (series of) squash/fixup commands\n  * in the todo list.\n  */\n-int sequencer_add_exec_commands(struct repository *r,\n-\t\t\t\tconst char *commands)\n+static void todo_list_add_exec_commands(struct todo_list *todo_list,\n+\t\t\t\t\tstruct string_list *commands)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf *buf = &todo_list.buf;\n-\tsize_t offset = 0, commands_len = strlen(commands);\n-\tint i, insert;\n+\tstruct strbuf *buf = &todo_list->buf;\n+\tsize_t base_offset = buf->len;\n+\tint i, insert, nr = 0, alloc = 0;\n+\tstruct todo_item *items = NULL, *base_items = NULL;\n \n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n+\tbase_items = xcalloc(commands->nr, sizeof(struct todo_item));\n+\tfor (i = 0; i < commands->nr; i++) {\n+\t\tsize_t command_len = strlen(commands->items[i].string);\n \n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t\tstrbuf_addstr(buf, commands->items[i].string);\n+\t\tstrbuf_addch(buf, '\\n');\n+\n+\t\tbase_items[i].command = TODO_EXEC;\n+\t\tbase_items[i].offset_in_buf = base_offset;\n+\t\tbase_items[i].arg_offset = base_offset + strlen(\"exec \");\n+\t\tbase_items[i].arg_len = command_len - strlen(\"exec \");\n+\n+\t\tbase_offset += command_len + 1;\n \t}\n \n \t/*\n@@ -4527,39 +4533,60 @@ int sequencer_add_exec_commands(struct repository *r,\n \t * are considered part of the pick, so we insert the commands *after*\n \t * those chains if there are any.\n \t */\n-\tinsert = -1;\n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tenum todo_command command = todo_list.items[i].command;\n-\n-\t\tif (insert >= 0) {\n-\t\t\t/* skip fixup/squash chains */\n-\t\t\tif (command == TODO_COMMENT)\n-\t\t\t\tcontinue;\n-\t\t\telse if (is_fixup(command)) {\n-\t\t\t\tinsert = i + 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tstrbuf_insert(buf,\n-\t\t\t\t      todo_list.items[insert].offset_in_buf +\n-\t\t\t\t      offset, commands, commands_len);\n-\t\t\toffset += commands_len;\n-\t\t\tinsert = -1;\n+\tinsert = 0;\n+\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\tenum todo_command command = todo_list->items[i].command;\n+\t\tif (insert && !is_fixup(command)) {\n+\t\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n+\t\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n+\t\t\tnr += commands->nr;\n+\n+\t\t\tinsert = 0;\n \t\t}\n \n-\t\tif (command == TODO_PICK || command == TODO_MERGE)\n-\t\t\tinsert = i + 1;\n+\t\tALLOC_GROW(items, nr + 1, alloc);\n+\t\titems[nr++] = todo_list->items[i];\n+\n+\t\tif (command == TODO_PICK || command == TODO_MERGE || is_fixup(command))\n+\t\t\tinsert = 1;\n \t}\n \n \t/* insert or append final <commands> */\n-\tif (insert >= 0 && insert < todo_list.nr)\n-\t\tstrbuf_insert(buf, todo_list.items[insert].offset_in_buf +\n-\t\t\t      offset, commands, commands_len);\n-\telse if (insert >= 0 || !offset)\n-\t\tstrbuf_add(buf, commands, commands_len);\n+\tif (insert || nr == todo_list->nr) {\n+\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n+\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n+\t\tnr += commands->nr;\n+\t}\n+\n+\tfree(base_items);\n+\tFREE_AND_NULL(todo_list->items);\n+\ttodo_list->items = items;\n+\ttodo_list->nr = nr;\n+\ttodo_list->alloc = alloc;\n+}\n \n-\ti = write_message(buf->buf, buf->len, todo_file, 0);\n+int sequencer_add_exec_commands(struct repository *r,\n+\t\t\t\tstruct string_list *commands)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_add_exec_commands(&todo_list, commands);\n+\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n \ttodo_list_release(&todo_list);\n-\treturn i;\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n }\n \n static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n@@ -4790,7 +4817,7 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-\t\t    const char *onto, const char *orig_head, const char *cmd,\n+\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash)\n {\n \tconst char *shortonto, *todo_file = rebase_path_todo();\n@@ -4809,8 +4836,8 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tif (autosquash && rearrange_squash(r))\n \t\treturn -1;\n \n-\tif (cmd && *cmd)\n-\t\tsequencer_add_exec_commands(r, cmd);\n+\tif (commands->nr)\n+\t\tsequencer_add_exec_commands(r, commands);\n \n \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\ndiff --git a/sequencer.h b/sequencer.h\nindex 217353e9f0..87d04a3b9b 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -146,12 +146,13 @@ int sequencer_make_script(struct repository *r, FILE *out, int argc,\n \t\t\t  const char **argv,\n \t\t\t  unsigned flags);\n \n-int sequencer_add_exec_commands(struct repository *r, const char *command);\n+int sequencer_add_exec_commands(struct repository *r,\n+\t\t\t\tstruct string_list *commands);\n int transform_todo_file(struct repository *r, unsigned flags);\n int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-\t\t    const char *onto, const char *orig_head, const char *cmd,\n+\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash);\n int rearrange_squash(struct repository *r);\n \n-- \n2.20.1\n\n"},{"id":"368969","messageId":"20190210132648.12821-13-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190210132648.12821-1-alban.gruin@gmail.com","subject":"[PATCH v7 12/16] rebase-interactive: use todo_list_write_to_file() in edit_todo_list()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-10T13:26:44Z","receivedAt":"2019-02-10T13:27:25Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Just like complete_action(), edit_todo_list() used a\nfunction (transform_todo_file()) that read the todo list from the disk\nand wrote it back, resulting in useless disk accesses.\n\nThis changes edit_todo_list() to call directly todo_list_write_to_file()\ninstead.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v6.\n\n rebase-interactive.c | 38 ++++++++++++++++----------------------\n sequencer.c          |  4 ++--\n sequencer.h          |  3 ---\n 3 files changed, 18 insertions(+), 27 deletions(-)\n\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex dfa6dd530f..d396ecc599 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -79,39 +79,33 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \n int edit_todo_list(struct repository *r, unsigned flags)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n \tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n \n-\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n-\tstrbuf_stripspace(&buf, 1);\n-\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n-\t\tstrbuf_release(&buf);\n+\tstrbuf_stripspace(&todo_list.buf, 1);\n+\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n+\tif (todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n+\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP)) {\n+\t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n \n-\tstrbuf_release(&buf);\n-\n-\ttransform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS);\n-\n-\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tappend_todo_help(1, 0, &buf);\n-\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n-\t\tstrbuf_release(&buf);\n+\tstrbuf_reset(&todo_list.buf);\n+\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n+\t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n \n-\tstrbuf_release(&buf);\n+\tif (!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list))\n+\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n+\t\t\t\t\t      flags & ~(TODO_LIST_SHORTEN_IDS));\n \n-\tif (launch_sequence_editor(todo_file, NULL, NULL))\n-\t\treturn -1;\n-\n-\ttransform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS));\n-\n-\treturn 0;\n+\ttodo_list_release(&todo_list);\n+\treturn res;\n }\n \n define_commit_slab(commit_seen, unsigned char);\ndiff --git a/sequencer.c b/sequencer.c\nindex eacaf52250..74fbb14153 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -383,8 +383,8 @@ static void print_advice(struct repository *r, int show_hint,\n \t}\n }\n \n-int write_message(const void *buf, size_t len, const char *filename,\n-\t\t  int append_eol)\n+static int write_message(const void *buf, size_t len, const char *filename,\n+\t\t\t int append_eol)\n {\n \tstruct lock_file msg_file = LOCK_INIT;\n \ndiff --git a/sequencer.h b/sequencer.h\nindex 7029b39cd6..c80990659c 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -64,9 +64,6 @@ struct replay_opts {\n };\n #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }\n \n-int write_message(const void *buf, size_t len, const char *filename,\n-\t\t  int append_eol);\n-\n /*\n  * Note that ordering matters in this enum. Not only must it match the mapping\n  * of todo_command_info (in sequencer.c), it is also divided into several\n-- \n2.20.1\n\n"},{"id":"368970","messageId":"20190210132648.12821-11-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190210132648.12821-1-alban.gruin@gmail.com","subject":"[PATCH v7 10/16] sequencer: change complete_action() to use the refactored functions","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-10T13:26:42Z","receivedAt":"2019-02-10T13:27:26Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"complete_action() used functions that read the todo-list file, made some\nchanges to it, and wrote it back to the disk.\n\nThe previous commits were dedicated to separate the part that deals with\nthe file from the actual logic of these functions.  Now that this is\ndone, we can call directly the \"logic\" functions to avoid useless file\naccess.\n\nThe parsing of the list has to be done by the caller.  If the buffer of\nthe todo list provided by the caller is empty, a `noop' command is\ndirectly added to the todo list, without touching the buffer.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 20 +++------\n sequencer.c                   | 79 +++++++++++++++--------------------\n sequencer.h                   |  2 +-\n 3 files changed, 41 insertions(+), 60 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 506154b5e2..4f2949922f 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -71,7 +71,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tconst char *head_hash = NULL;\n \tchar *revisions = NULL, *shortrevisions = NULL;\n \tstruct argv_array make_script_args = ARGV_ARRAY_INIT;\n-\tFILE *todo_list_file;\n \tstruct todo_list todo_list = TODO_LIST_INIT;\n \n \tif (prepare_branch_to_be_rebased(opts, switch_to))\n@@ -94,14 +93,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (!upstream && squash_onto)\n \t\twrite_file(path_squash_onto(), \"%s\\n\", squash_onto);\n \n-\ttodo_list_file = fopen(rebase_path_todo(), \"w\");\n-\tif (!todo_list_file) {\n-\t\tfree(revisions);\n-\t\tfree(shortrevisions);\n-\n-\t\treturn error_errno(_(\"could not open %s\"), rebase_path_todo());\n-\t}\n-\n \targv_array_pushl(&make_script_args, \"\", revisions, NULL);\n \tif (restrict_revision)\n \t\targv_array_push(&make_script_args, restrict_revision);\n@@ -109,16 +100,17 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tret = sequencer_make_script(the_repository, &todo_list.buf,\n \t\t\t\t    make_script_args.argc, make_script_args.argv,\n \t\t\t\t    flags);\n-\tfputs(todo_list.buf.buf, todo_list_file);\n-\tfclose(todo_list_file);\n \n \tif (ret)\n \t\terror(_(\"could not generate todo list\"));\n \telse {\n \t\tdiscard_cache();\n-\t\tret = complete_action(the_repository, opts, flags,\n-\t\t\t\t      shortrevisions, onto_name, onto,\n-\t\t\t\t      head_hash, commands, autosquash);\n+\t\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n+\t\t\t\t\t\t&todo_list))\n+\t\t\tBUG(\"unusable todo list\");\n+\n+\t\tret = complete_action(the_repository, opts, flags, shortrevisions, onto_name,\n+\t\t\t\t      onto, head_hash, commands, autosquash, &todo_list);\n \t}\n \n \tfree(revisions);\ndiff --git a/sequencer.c b/sequencer.c\nindex dbef2547f7..eb8a622af0 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4810,93 +4810,82 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \treturn 0;\n }\n \n+static int todo_list_rearrange_squash(struct todo_list *todo_list);\n+\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n-\t\t    unsigned autosquash)\n+\t\t    unsigned autosquash, struct todo_list *todo_list)\n {\n \tconst char *shortonto, *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf *buf = &(todo_list.buf);\n+\tstruct todo_list new_todo = TODO_LIST_INIT;\n+\tstruct strbuf *buf = &todo_list->buf;\n \tstruct object_id oid;\n-\tstruct stat st;\n \n \tget_oid(onto, &oid);\n \tshortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);\n \n-\tif (!lstat(todo_file, &st) && st.st_size == 0 &&\n-\t    write_message(\"noop\\n\", 5, todo_file, 0))\n-\t\treturn -1;\n+\tif (buf->len == 0) {\n+\t\tstruct todo_item *item = append_new_todo(todo_list);\n+\t\titem->command = TODO_NOOP;\n+\t\titem->commit = NULL;\n+\t\titem->arg_len = item->arg_offset = item->flags = item->offset_in_buf = 0;\n+\t}\n \n-\tif (autosquash && rearrange_squash_in_todo_file(r))\n+\tif (autosquash && todo_list_rearrange_squash(todo_list))\n \t\treturn -1;\n \n \tif (commands->nr)\n-\t\tsequencer_add_exec_commands(r, commands);\n+\t\ttodo_list_add_exec_commands(todo_list, commands);\n \n-\tif (strbuf_read_file(buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(r, buf->buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tif (count_commands(&todo_list) == 0) {\n+\tif (count_commands(todo_list) == 0) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n \n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\tstrbuf_addch(buf, '\\n');\n-\tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n-\t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n-\t\t\t\t      count_commands(&todo_list)),\n-\t\t\t      shortrevisions, shortonto, count_commands(&todo_list));\n-\tappend_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);\n-\n-\tif (write_message(buf->buf, buf->len, todo_file, 0)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tif (todo_list_write_to_file(r, todo_list, todo_file,\n+\t\t\t\t    shortrevisions, shortonto, -1,\n+\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n \n \tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n \t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n \t\t\t     rebase_path_todo_backup());\n \n-\tif (transform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS))\n-\t\treturn error(_(\"could not transform the todo list\"));\n-\n-\tstrbuf_reset(buf);\n-\n-\tif (launch_sequence_editor(todo_file, buf, NULL)) {\n+\tif (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n \n \t\treturn -1;\n \t}\n \n-\tstrbuf_stripspace(buf, 1);\n-\tif (buf->len == 0) {\n+\tstrbuf_stripspace(&new_todo.buf, 1);\n+\tif (new_todo.buf.len == 0) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n+\t\ttodo_list_release(&new_todo);\n \n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\n-\tif (check_todo_list_from_file(r)) {\n+\tif (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) ||\n+\t    todo_list_check(todo_list, &new_todo)) {\n+\t\tfprintf(stderr, _(edit_todo_list_advice));\n \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n+\t\ttodo_list_release(&new_todo);\n+\n \t\treturn -1;\n \t}\n \n-\tif (transform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS)))\n-\t\treturn error(_(\"could not transform the todo list\"));\n+\tif (todo_list_write_to_file(r, &new_todo, todo_file, NULL, NULL, -1,\n+\t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS))) {\n+\t\ttodo_list_release(&new_todo);\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_release(&new_todo);\n \n \tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\n \t\treturn error(_(\"could not skip unnecessary pick commands\"));\ndiff --git a/sequencer.h b/sequencer.h\nindex e25f5151d3..7029b39cd6 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -152,7 +152,7 @@ int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n-\t\t    unsigned autosquash);\n+\t\t    unsigned autosquash, struct todo_list *todo_list);\n int rearrange_squash_in_todo_file(struct repository *r);\n \n extern const char sign_off_header[];\n-- \n2.20.1\n\n"},{"id":"368971","messageId":"20190210132648.12821-9-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190210132648.12821-1-alban.gruin@gmail.com","subject":"[PATCH v7 08/16] sequencer: refactor rearrange_squash() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-10T13:26:40Z","receivedAt":"2019-02-10T13:27:26Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors rearrange_squash() to work on a todo_list to avoid\nredundant reads and writes.  The function is renamed\ntodo_list_rearrange_squash().\n\nThe old version created a new buffer, which was directly written to the\ndisk.  This new version creates a new item list by just copying items\nfrom the old item list, without creating a new buffer.  This eliminates\nthe need to reparse the todo list, but this also means its buffer cannot\nbe directly written to the disk.\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, rearrange_squash_in_todo_file().\n\ncomplete_action() still uses rearrange_squash_in_todo_file() for now.\nThis will be changed in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v6.\n\n builtin/rebase--interactive.c |  2 +-\n sequencer.c                   | 92 ++++++++++++++++++-----------------\n sequencer.h                   |  2 +-\n 3 files changed, 49 insertions(+), 47 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex c131fd4a27..74ebe7a10e 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -265,7 +265,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = check_todo_list_from_file(the_repository);\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n-\t\tret = rearrange_squash(the_repository);\n+\t\tret = rearrange_squash_in_todo_file(the_repository);\n \t\tbreak;\n \tcase ADD_EXEC:\n \t\tret = sequencer_add_exec_commands(the_repository, &commands);\ndiff --git a/sequencer.c b/sequencer.c\nindex 82ca3432cd..ffa4c4da1a 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4833,7 +4833,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t    write_message(\"noop\\n\", 5, todo_file, 0))\n \t\treturn -1;\n \n-\tif (autosquash && rearrange_squash(r))\n+\tif (autosquash && rearrange_squash_in_todo_file(r))\n \t\treturn -1;\n \n \tif (commands->nr)\n@@ -4939,21 +4939,13 @@ define_commit_slab(commit_todo_item, struct todo_item *);\n  * message will have to be retrieved from the commit (as the oneline in the\n  * script cannot be trusted) in order to normalize the autosquash arrangement.\n  */\n-int rearrange_squash(struct repository *r)\n+static int todo_list_rearrange_squash(struct todo_list *todo_list)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct hashmap subject2item;\n-\tint res = 0, rearranged = 0, *next, *tail, i;\n+\tint rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;\n \tchar **subjects;\n \tstruct commit_todo_item commit_todo;\n-\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tstruct todo_item *items = NULL;\n \n \tinit_commit_todo_item(&commit_todo);\n \t/*\n@@ -4966,13 +4958,13 @@ int rearrange_squash(struct repository *r)\n \t * be moved to appear after the i'th.\n \t */\n \thashmap_init(&subject2item, (hashmap_cmp_fn) subject2item_cmp,\n-\t\t     NULL, todo_list.nr);\n-\tALLOC_ARRAY(next, todo_list.nr);\n-\tALLOC_ARRAY(tail, todo_list.nr);\n-\tALLOC_ARRAY(subjects, todo_list.nr);\n-\tfor (i = 0; i < todo_list.nr; i++) {\n+\t\t     NULL, todo_list->nr);\n+\tALLOC_ARRAY(next, todo_list->nr);\n+\tALLOC_ARRAY(tail, todo_list->nr);\n+\tALLOC_ARRAY(subjects, todo_list->nr);\n+\tfor (i = 0; i < todo_list->nr; i++) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n-\t\tstruct todo_item *item = todo_list.items + i;\n+\t\tstruct todo_item *item = todo_list->items + i;\n \t\tconst char *commit_buffer, *subject, *p;\n \t\tsize_t subject_len;\n \t\tint i2 = -1;\n@@ -4985,7 +4977,6 @@ int rearrange_squash(struct repository *r)\n \t\t}\n \n \t\tif (is_fixup(item->command)) {\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\tclear_commit_todo_item(&commit_todo);\n \t\t\treturn error(_(\"the script was already rearranged.\"));\n \t\t}\n@@ -5020,7 +5011,7 @@ int rearrange_squash(struct repository *r)\n \t\t\t\t *commit_todo_item_at(&commit_todo, commit2))\n \t\t\t\t/* found by commit name */\n \t\t\t\ti2 = *commit_todo_item_at(&commit_todo, commit2)\n-\t\t\t\t\t- todo_list.items;\n+\t\t\t\t\t- todo_list->items;\n \t\t\telse {\n \t\t\t\t/* copy can be a prefix of the commit subject */\n \t\t\t\tfor (i2 = 0; i2 < i; i2++)\n@@ -5033,7 +5024,7 @@ int rearrange_squash(struct repository *r)\n \t\t}\n \t\tif (i2 >= 0) {\n \t\t\trearranged = 1;\n-\t\t\ttodo_list.items[i].command =\n+\t\t\ttodo_list->items[i].command =\n \t\t\t\tstarts_with(subject, \"fixup!\") ?\n \t\t\t\tTODO_FIXUP : TODO_SQUASH;\n \t\t\tif (next[i2] < 0)\n@@ -5051,10 +5042,8 @@ int rearrange_squash(struct repository *r)\n \t}\n \n \tif (rearranged) {\n-\t\tstruct strbuf buf = STRBUF_INIT;\n-\n-\t\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\t\tenum todo_command command = todo_list.items[i].command;\n+\t\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\t\tenum todo_command command = todo_list->items[i].command;\n \t\t\tint cur = i;\n \n \t\t\t/*\n@@ -5065,37 +5054,50 @@ int rearrange_squash(struct repository *r)\n \t\t\t\tcontinue;\n \n \t\t\twhile (cur >= 0) {\n-\t\t\t\tconst char *bol =\n-\t\t\t\t\tget_item_line(&todo_list, cur);\n-\t\t\t\tconst char *eol =\n-\t\t\t\t\tget_item_line(&todo_list, cur + 1);\n-\n-\t\t\t\t/* replace 'pick', by 'fixup' or 'squash' */\n-\t\t\t\tcommand = todo_list.items[cur].command;\n-\t\t\t\tif (is_fixup(command)) {\n-\t\t\t\t\tstrbuf_addstr(&buf,\n-\t\t\t\t\t\ttodo_command_info[command].str);\n-\t\t\t\t\tbol += strcspn(bol, \" \\t\");\n-\t\t\t\t}\n-\n-\t\t\t\tstrbuf_add(&buf, bol, eol - bol);\n-\n+\t\t\t\tALLOC_GROW(items, nr + 1, alloc);\n+\t\t\t\titems[nr++] = todo_list->items[cur];\n \t\t\t\tcur = next[cur];\n \t\t\t}\n \t\t}\n \n-\t\tres = rewrite_file(todo_file, buf.buf, buf.len);\n-\t\tstrbuf_release(&buf);\n+\t\tFREE_AND_NULL(todo_list->items);\n+\t\ttodo_list->items = items;\n+\t\ttodo_list->nr = nr;\n+\t\ttodo_list->alloc = alloc;\n \t}\n \n \tfree(next);\n \tfree(tail);\n-\tfor (i = 0; i < todo_list.nr; i++)\n+\tfor (i = 0; i < todo_list->nr; i++)\n \t\tfree(subjects[i]);\n \tfree(subjects);\n \thashmap_free(&subject2item, 1);\n-\ttodo_list_release(&todo_list);\n \n \tclear_commit_todo_item(&commit_todo);\n-\treturn res;\n+\n+\treturn 0;\n+}\n+\n+int rearrange_squash_in_todo_file(struct repository *r)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n+\n+\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n+\t\treturn -1;\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn -1;\n+\t}\n+\n+\tres = todo_list_rearrange_squash(&todo_list);\n+\tif (!res)\n+\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n+\n+\ttodo_list_release(&todo_list);\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n }\ndiff --git a/sequencer.h b/sequencer.h\nindex 87d04a3b9b..add50f04f1 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -154,7 +154,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash);\n-int rearrange_squash(struct repository *r);\n+int rearrange_squash_in_todo_file(struct repository *r);\n \n extern const char sign_off_header[];\n \n-- \n2.20.1\n\n"},{"id":"368972","messageId":"20190210132648.12821-14-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190210132648.12821-1-alban.gruin@gmail.com","subject":"[PATCH v7 13/16] rebase-interactive: append_todo_help() changes","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-10T13:26:45Z","receivedAt":"2019-02-10T13:27:28Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This moves the writing of the comment \"Rebase $shortrevisions onto\n$shortonto ($command_count commands)\" from todo_list_write_to_file() to\nappend_todo_help().\n\nshortrevisions, shortonto, and command_count are passed as parameters to\nappend_todo_help().\n\nDuring the initial edit of the todo list, shortrevisions and shortonto\nare not NULL.  Therefore, if shortrevisions or shortonto is NULL, then\nedit_todo would be true, otherwise it would be false.  Thus, edit_todo\nis removed from the parameters of append_todo_help().\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v6.\n\n rebase-interactive.c | 12 +++++++++++-\n rebase-interactive.h |  3 ++-\n sequencer.c          | 17 ++++-------------\n 3 files changed, 17 insertions(+), 15 deletions(-)\n\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex d396ecc599..807f8370db 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -28,7 +28,8 @@ static enum missing_commit_check_level get_missing_commit_check_level(void)\n \treturn MISSING_COMMIT_CHECK_IGNORE;\n }\n \n-void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n+void append_todo_help(unsigned keep_empty, int command_count,\n+\t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf)\n {\n \tconst char *msg = _(\"\\nCommands:\\n\"\n@@ -48,6 +49,15 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \".       specified). Use -c <commit> to reword the commit message.\\n\"\n \"\\n\"\n \"These lines can be re-ordered; they are executed from top to bottom.\\n\");\n+\tunsigned edit_todo = !(shortrevisions && shortonto);\n+\n+\tif (!edit_todo) {\n+\t\tstrbuf_addch(buf, '\\n');\n+\t\tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n+\t\t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n+\t\t\t\t\t      command_count),\n+\t\t\t\t      shortrevisions, shortonto, command_count);\n+\t}\n \n \tstrbuf_add_commented_lines(buf, msg, strlen(msg));\n \ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 187b5032d6..0e5925e3aa 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -5,7 +5,8 @@ struct strbuf;\n struct repository;\n struct todo_list;\n \n-void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n+void append_todo_help(unsigned keep_empty, int command_count,\n+\t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf);\n int edit_todo_list(struct repository *r, unsigned flags);\n int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\ndiff --git a/sequencer.c b/sequencer.c\nindex 74fbb14153..64d698032c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4636,22 +4636,13 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n \t\t\t    const char *file, const char *shortrevisions,\n \t\t\t    const char *shortonto, int num, unsigned flags)\n {\n-\tint edit_todo = !(shortrevisions && shortonto), res;\n+\tint res;\n \tstruct strbuf buf = STRBUF_INIT;\n \n \ttodo_list_to_strbuf(r, todo_list, &buf, num, flags);\n-\n-\tif (flags & TODO_LIST_APPEND_TODO_HELP) {\n-\t\tint command_count = count_commands(todo_list);\n-\t\tif (!edit_todo) {\n-\t\t\tstrbuf_addch(&buf, '\\n');\n-\t\t\tstrbuf_commented_addf(&buf, Q_(\"Rebase %s onto %s (%d command)\",\n-\t\t\t\t\t\t       \"Rebase %s onto %s (%d commands)\",\n-\t\t\t\t\t\t       command_count),\n-\t\t\t\t\t      shortrevisions, shortonto, command_count);\n-\t\t}\n-\t\tappend_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, &buf);\n-\t}\n+\tif (flags & TODO_LIST_APPEND_TODO_HELP)\n+\t\tappend_todo_help(flags & TODO_LIST_KEEP_EMPTY, count_commands(todo_list),\n+\t\t\t\t shortrevisions, shortonto, &buf);\n \n \tres = write_message(buf.buf, buf.len, file, 0);\n \tstrbuf_release(&buf);\n-- \n2.20.1\n\n"},{"id":"368973","messageId":"20190210132648.12821-12-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190210132648.12821-1-alban.gruin@gmail.com","subject":"[PATCH v7 11/16] sequencer: refactor skip_unnecessary_picks() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-10T13:26:43Z","receivedAt":"2019-02-10T13:27:28Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors skip_unnecessary_picks() to work on a todo_list.  As this\nfunction is only called by complete_action() (and thus is not used by\nrebase -p), the file-handling logic is completely dropped here.\n\nInstead of truncating the todo list’s buffer, the items are moved to\nthe beginning of the list, eliminating the need to reparse the list.\nThis also means its buffer cannot be directly written to the disk.\n\nrewrite_file() is then removed, as it is now unused.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n sequencer.c | 82 +++++++++++++----------------------------------------\n 1 file changed, 19 insertions(+), 63 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex eb8a622af0..eacaf52250 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4717,52 +4717,22 @@ int check_todo_list_from_file(struct repository *r)\n \treturn res;\n }\n \n-static int rewrite_file(const char *path, const char *buf, size_t len)\n-{\n-\tint rc = 0;\n-\tint fd = open(path, O_WRONLY | O_TRUNC);\n-\tif (fd < 0)\n-\t\treturn error_errno(_(\"could not open '%s' for writing\"), path);\n-\tif (write_in_full(fd, buf, len) < 0)\n-\t\trc = error_errno(_(\"could not write to '%s'\"), path);\n-\tif (close(fd) && !rc)\n-\t\trc = error_errno(_(\"could not close '%s'\"), path);\n-\treturn rc;\n-}\n-\n /* skip picking commits whose parents are unchanged */\n-static int skip_unnecessary_picks(struct repository *r, struct object_id *output_oid)\n+static int skip_unnecessary_picks(struct repository *r,\n+\t\t\t\t  struct todo_list *todo_list,\n+\t\t\t\t  struct object_id *base_oid)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct object_id *parent_oid;\n-\tint fd, i;\n-\n-\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n-\t\treturn error(_(\"could not read 'onto'\"));\n-\tif (get_oid(buf.buf, output_oid)) {\n-\t\tstrbuf_release(&buf);\n-\t\treturn error(_(\"need a HEAD to fixup\"));\n-\t}\n-\tstrbuf_release(&buf);\n-\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tint i;\n \n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tstruct todo_item *item = todo_list.items + i;\n+\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\tstruct todo_item *item = todo_list->items + i;\n \n \t\tif (item->command >= TODO_NOOP)\n \t\t\tcontinue;\n \t\tif (item->command != TODO_PICK)\n \t\t\tbreak;\n \t\tif (parse_commit(item->commit)) {\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\treturn error(_(\"could not parse commit '%s'\"),\n \t\t\t\toid_to_hex(&item->commit->object.oid));\n \t\t}\n@@ -4771,42 +4741,26 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \t\tif (item->commit->parents->next)\n \t\t\tbreak; /* merge commit */\n \t\tparent_oid = &item->commit->parents->item->object.oid;\n-\t\tif (!oideq(parent_oid, output_oid))\n+\t\tif (!oideq(parent_oid, base_oid))\n \t\t\tbreak;\n-\t\toidcpy(output_oid, &item->commit->object.oid);\n+\t\toidcpy(base_oid, &item->commit->object.oid);\n \t}\n \tif (i > 0) {\n-\t\tint offset = get_item_line_offset(&todo_list, i);\n \t\tconst char *done_path = rebase_path_done();\n \n-\t\tfd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);\n-\t\tif (fd < 0) {\n-\t\t\terror_errno(_(\"could not open '%s' for writing\"),\n-\t\t\t\t    done_path);\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\treturn -1;\n-\t\t}\n-\t\tif (write_in_full(fd, todo_list.buf.buf, offset) < 0) {\n+\t\tif (todo_list_write_to_file(r, todo_list, done_path, NULL, NULL, i, 0)) {\n \t\t\terror_errno(_(\"could not write to '%s'\"), done_path);\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\tclose(fd);\n \t\t\treturn -1;\n \t\t}\n-\t\tclose(fd);\n \n-\t\tif (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,\n-\t\t\t\t todo_list.buf.len - offset) < 0) {\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\treturn -1;\n-\t\t}\n+\t\tMOVE_ARRAY(todo_list->items, todo_list->items + i, todo_list->nr - i);\n+\t\ttodo_list->nr -= i;\n+\t\ttodo_list->current = 0;\n \n-\t\ttodo_list.current = i;\n-\t\tif (is_fixup(peek_command(&todo_list, 0)))\n-\t\t\trecord_in_rewritten(output_oid, peek_command(&todo_list, 0));\n+\t\tif (is_fixup(peek_command(todo_list, 0)))\n+\t\t\trecord_in_rewritten(base_oid, peek_command(todo_list, 0));\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\n \treturn 0;\n }\n \n@@ -4879,6 +4833,11 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn -1;\n \t}\n \n+\tif (opts->allow_ff && skip_unnecessary_picks(r, &new_todo, &oid)) {\n+\t\ttodo_list_release(&new_todo);\n+\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n+\t}\n+\n \tif (todo_list_write_to_file(r, &new_todo, todo_file, NULL, NULL, -1,\n \t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS))) {\n \t\ttodo_list_release(&new_todo);\n@@ -4887,9 +4846,6 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \n \ttodo_list_release(&new_todo);\n \n-\tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\n-\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n-\n \tif (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))\n \t\treturn -1;\n \n-- \n2.20.1\n\n"},{"id":"368974","messageId":"20190210132648.12821-15-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190210132648.12821-1-alban.gruin@gmail.com","subject":"[PATCH v7 14/16] rebase-interactive: rewrite edit_todo_list() to handle the initial edit","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-10T13:26:46Z","receivedAt":"2019-02-10T13:27:29Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"edit_todo_list() is changed to work on a todo_list, and to handle the\ninitial edition of the todo list (ie. making a backup of the todo\nlist).\n\nIt does not check for dropped commits yet, as todo_list_check() does not\ntake the commits that have already been processed by the rebase (ie. the\ntodo list is edited in the middle of a rebase session).\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 24 +++++++++++++++++-\n rebase-interactive.c          | 48 +++++++++++++++++------------------\n rebase-interactive.h          |  4 ++-\n sequencer.c                   |  3 +--\n sequencer.h                   |  1 +\n 5 files changed, 52 insertions(+), 28 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 4f2949922f..370d584683 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -13,6 +13,28 @@ static GIT_PATH_FUNC(path_state_dir, \"rebase-merge/\")\n static GIT_PATH_FUNC(path_squash_onto, \"rebase-merge/squash-onto\")\n static GIT_PATH_FUNC(path_interactive, \"rebase-merge/interactive\")\n \n+static int edit_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT,\n+\t\tnew_todo = TODO_LIST_INIT;\n+\tint res = 0;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tstrbuf_stripspace(&todo_list.buf, 1);\n+\tres = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);\n+\tif (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,\n+\t\t\t\t\t    NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))\n+\t\tres = error_errno(_(\"could not write '%s'\"), todo_file);\n+\n+\ttodo_list_release(&todo_list);\n+\ttodo_list_release(&new_todo);\n+\n+\treturn res;\n+}\n+\n static int get_revision_ranges(const char *upstream, const char *onto,\n \t\t\t       const char **head_hash,\n \t\t\t       char **revisions, char **shortrevisions)\n@@ -241,7 +263,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tbreak;\n \t}\n \tcase EDIT_TODO:\n-\t\tret = edit_todo_list(the_repository, flags);\n+\t\tret = edit_todo_file(flags);\n \t\tbreak;\n \tcase SHOW_CURRENT_PATCH: {\n \t\tstruct child_process cmd = CHILD_PROCESS_INIT;\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 807f8370db..96c70d1959 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -87,35 +87,35 @@ void append_todo_help(unsigned keep_empty, int command_count,\n \t}\n }\n \n-int edit_todo_list(struct repository *r, unsigned flags)\n+int edit_todo_list(struct repository *r, struct todo_list *todo_list,\n+\t\t   struct todo_list *new_todo, const char *shortrevisions,\n+\t\t   const char *shortonto, unsigned flags)\n {\n \tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res = 0;\n-\n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tstrbuf_stripspace(&todo_list.buf, 1);\n-\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n-\tif (todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n-\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tunsigned initial = shortrevisions && shortonto;\n \n-\tstrbuf_reset(&todo_list.buf);\n-\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tif (!initial)\n+\t\ttodo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n \n-\tif (!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list))\n-\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n-\t\t\t\t\t      flags & ~(TODO_LIST_SHORTEN_IDS));\n+\tif (todo_list_write_to_file(r, todo_list, todo_file, shortrevisions, shortonto,\n+\t\t\t\t    -1, flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n \n-\ttodo_list_release(&todo_list);\n-\treturn res;\n+\tif (initial && copy_file(rebase_path_todo_backup(), todo_file, 0666))\n+\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n+\t\t\t     rebase_path_todo_backup());\n+\n+\tif (launch_sequence_editor(todo_file, &new_todo->buf, NULL))\n+\t\treturn -2;\n+\n+\tstrbuf_stripspace(&new_todo->buf, 1);\n+\tif (initial && new_todo->buf.len == 0)\n+\t\treturn -3;\n+\n+\tif (!initial)\n+\t\treturn todo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo);\n+\n+\treturn 0;\n }\n \n define_commit_slab(commit_seen, unsigned char);\ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 0e5925e3aa..44dbb06311 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -8,7 +8,9 @@ struct todo_list;\n void append_todo_help(unsigned keep_empty, int command_count,\n \t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf);\n-int edit_todo_list(struct repository *r, unsigned flags);\n+int edit_todo_list(struct repository *r, struct todo_list *todo_list,\n+\t\t   struct todo_list *new_todo, const char *shortrevisions,\n+\t\t   const char *shortonto, unsigned flags);\n int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n \n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex 64d698032c..f6ed4e21e9 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -55,8 +55,7 @@ static GIT_PATH_FUNC(rebase_path, \"rebase-merge\")\n  * file and written to the tail of 'done'.\n  */\n GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n-static GIT_PATH_FUNC(rebase_path_todo_backup,\n-\t\t     \"rebase-merge/git-rebase-todo.backup\")\n+GIT_PATH_FUNC(rebase_path_todo_backup, \"rebase-merge/git-rebase-todo.backup\")\n \n /*\n  * The rebase command lines that have already been processed. A line\ndiff --git a/sequencer.h b/sequencer.h\nindex c80990659c..b0688ba2a1 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -10,6 +10,7 @@ struct repository;\n const char *git_path_commit_editmsg(void);\n const char *git_path_seq_dir(void);\n const char *rebase_path_todo(void);\n+const char *rebase_path_todo_backup(void);\n \n #define APPEND_SIGNOFF_DEDUP (1u << 0)\n \n-- \n2.20.1\n\n"},{"id":"368975","messageId":"20190210132648.12821-10-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190210132648.12821-1-alban.gruin@gmail.com","subject":"[PATCH v7 09/16] sequencer: make sequencer_make_script() write its script to a strbuf","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-10T13:26:41Z","receivedAt":"2019-02-10T13:27:30Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This makes sequencer_make_script() write its script to a strbuf (ie. the\nbuffer of a todo_list) instead of a FILE.  This reduce the amount of\nread/write made by rebase interactive.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v6.\n\n builtin/rebase--interactive.c | 13 ++++++-----\n sequencer.c                   | 41 +++++++++++++++--------------------\n sequencer.h                   |  5 ++---\n 3 files changed, 28 insertions(+), 31 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 74ebe7a10e..506154b5e2 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -71,7 +71,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tconst char *head_hash = NULL;\n \tchar *revisions = NULL, *shortrevisions = NULL;\n \tstruct argv_array make_script_args = ARGV_ARRAY_INIT;\n-\tFILE *todo_list;\n+\tFILE *todo_list_file;\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n \n \tif (prepare_branch_to_be_rebased(opts, switch_to))\n \t\treturn -1;\n@@ -93,8 +94,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (!upstream && squash_onto)\n \t\twrite_file(path_squash_onto(), \"%s\\n\", squash_onto);\n \n-\ttodo_list = fopen(rebase_path_todo(), \"w\");\n-\tif (!todo_list) {\n+\ttodo_list_file = fopen(rebase_path_todo(), \"w\");\n+\tif (!todo_list_file) {\n \t\tfree(revisions);\n \t\tfree(shortrevisions);\n \n@@ -105,10 +106,11 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (restrict_revision)\n \t\targv_array_push(&make_script_args, restrict_revision);\n \n-\tret = sequencer_make_script(the_repository, todo_list,\n+\tret = sequencer_make_script(the_repository, &todo_list.buf,\n \t\t\t\t    make_script_args.argc, make_script_args.argv,\n \t\t\t\t    flags);\n-\tfclose(todo_list);\n+\tfputs(todo_list.buf.buf, todo_list_file);\n+\tfclose(todo_list_file);\n \n \tif (ret)\n \t\terror(_(\"could not generate todo list\"));\n@@ -121,6 +123,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \n \tfree(revisions);\n \tfree(shortrevisions);\n+\ttodo_list_release(&todo_list);\n \targv_array_clear(&make_script_args);\n \n \treturn ret;\ndiff --git a/sequencer.c b/sequencer.c\nindex ffa4c4da1a..dbef2547f7 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4215,7 +4215,7 @@ static const char *label_oid(struct object_id *oid, const char *label,\n }\n \n static int make_script_with_merges(struct pretty_print_context *pp,\n-\t\t\t\t   struct rev_info *revs, FILE *out,\n+\t\t\t\t   struct rev_info *revs, struct strbuf *out,\n \t\t\t\t   unsigned flags)\n {\n \tint keep_empty = flags & TODO_LIST_KEEP_EMPTY;\n@@ -4360,7 +4360,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t * gathering commits not yet shown, reversing the list on the fly,\n \t * then outputting that list (labeling revisions as needed).\n \t */\n-\tfprintf(out, \"%s onto\\n\", cmd_label);\n+\tstrbuf_addf(out, \"%s onto\\n\", cmd_label);\n \tfor (iter = tips; iter; iter = iter->next) {\n \t\tstruct commit_list *list = NULL, *iter2;\n \n@@ -4370,9 +4370,9 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\tentry = oidmap_get(&state.commit2label, &commit->object.oid);\n \n \t\tif (entry)\n-\t\t\tfprintf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n+\t\t\tstrbuf_addf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n \t\telse\n-\t\t\tfprintf(out, \"\\n\");\n+\t\t\tstrbuf_addch(out, '\\n');\n \n \t\twhile (oidset_contains(&interesting, &commit->object.oid) &&\n \t\t       !oidset_contains(&shown, &commit->object.oid)) {\n@@ -4385,8 +4385,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t}\n \n \t\tif (!commit)\n-\t\t\tfprintf(out, \"%s %s\\n\", cmd_reset,\n-\t\t\t\trebase_cousins ? \"onto\" : \"[new root]\");\n+\t\t\tstrbuf_addf(out, \"%s %s\\n\", cmd_reset,\n+\t\t\t\t    rebase_cousins ? \"onto\" : \"[new root]\");\n \t\telse {\n \t\t\tconst char *to = NULL;\n \n@@ -4399,12 +4399,12 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t\t\t\t       &state);\n \n \t\t\tif (!to || !strcmp(to, \"onto\"))\n-\t\t\t\tfprintf(out, \"%s onto\\n\", cmd_reset);\n+\t\t\t\tstrbuf_addf(out, \"%s onto\\n\", cmd_reset);\n \t\t\telse {\n \t\t\t\tstrbuf_reset(&oneline);\n \t\t\t\tpretty_print_commit(pp, commit, &oneline);\n-\t\t\t\tfprintf(out, \"%s %s # %s\\n\",\n-\t\t\t\t\tcmd_reset, to, oneline.buf);\n+\t\t\t\tstrbuf_addf(out, \"%s %s # %s\\n\",\n+\t\t\t\t\t    cmd_reset, to, oneline.buf);\n \t\t\t}\n \t\t}\n \n@@ -4413,11 +4413,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t\tentry = oidmap_get(&commit2todo, oid);\n \t\t\t/* only show if not already upstream */\n \t\t\tif (entry)\n-\t\t\t\tfprintf(out, \"%s\\n\", entry->string);\n+\t\t\t\tstrbuf_addf(out, \"%s\\n\", entry->string);\n \t\t\tentry = oidmap_get(&state.commit2label, oid);\n \t\t\tif (entry)\n-\t\t\t\tfprintf(out, \"%s %s\\n\",\n-\t\t\t\t\tcmd_label, entry->string);\n+\t\t\t\tstrbuf_addf(out, \"%s %s\\n\",\n+\t\t\t\t\t    cmd_label, entry->string);\n \t\t\toidset_insert(&shown, oid);\n \t\t}\n \n@@ -4439,13 +4439,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \treturn 0;\n }\n \n-int sequencer_make_script(struct repository *r, FILE *out,\n-\t\t\t  int argc, const char **argv,\n-\t\t\t  unsigned flags)\n+int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n+\t\t\t  const char **argv, unsigned flags)\n {\n \tchar *format = NULL;\n \tstruct pretty_print_context pp = {0};\n-\tstruct strbuf buf = STRBUF_INIT;\n \tstruct rev_info revs;\n \tstruct commit *commit;\n \tint keep_empty = flags & TODO_LIST_KEEP_EMPTY;\n@@ -4488,16 +4486,13 @@ int sequencer_make_script(struct repository *r, FILE *out,\n \n \t\tif (!is_empty && (commit->object.flags & PATCHSAME))\n \t\t\tcontinue;\n-\t\tstrbuf_reset(&buf);\n \t\tif (!keep_empty && is_empty)\n-\t\t\tstrbuf_addf(&buf, \"%c \", comment_line_char);\n-\t\tstrbuf_addf(&buf, \"%s %s \", insn,\n+\t\t\tstrbuf_addf(out, \"%c \", comment_line_char);\n+\t\tstrbuf_addf(out, \"%s %s \", insn,\n \t\t\t    oid_to_hex(&commit->object.oid));\n-\t\tpretty_print_commit(&pp, commit, &buf);\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t\tfputs(buf.buf, out);\n+\t\tpretty_print_commit(&pp, commit, out);\n+\t\tstrbuf_addch(out, '\\n');\n \t}\n-\tstrbuf_release(&buf);\n \treturn 0;\n }\n \ndiff --git a/sequencer.h b/sequencer.h\nindex add50f04f1..e25f5151d3 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -142,9 +142,8 @@ int sequencer_remove_state(struct replay_opts *opts);\n #define TODO_LIST_REBASE_COUSINS (1U << 4)\n #define TODO_LIST_APPEND_TODO_HELP (1U << 5)\n \n-int sequencer_make_script(struct repository *r, FILE *out, int argc,\n-\t\t\t  const char **argv,\n-\t\t\t  unsigned flags);\n+int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n+\t\t\t  const char **argv, unsigned flags);\n \n int sequencer_add_exec_commands(struct repository *r,\n \t\t\t\tstruct string_list *commands);\n-- \n2.20.1\n\n"},{"id":"368976","messageId":"20190210132648.12821-17-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190210132648.12821-1-alban.gruin@gmail.com","subject":"[PATCH v7 16/16] rebase--interactive: move several functions to rebase--interactive.c","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-10T13:26:48Z","receivedAt":"2019-02-10T13:27:34Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"As sequencer_add_exec_commands(), rearrange_squash_in_todo_file(), and\ntransform_todo_file() are only needed inside of rebase--interactive.c\nfor rebase -p, they are moved there from sequencer.c.\n\nThe parameter r (repository) is dropped from them, and the error\nhandling of rearrange_squash_in_todo_file() is slightly improved.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 81 +++++++++++++++++++++++++++++++++--\n sequencer.c                   | 79 ++--------------------------------\n sequencer.h                   |  7 ++-\n 3 files changed, 84 insertions(+), 83 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 370d584683..fb11f23692 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -13,6 +13,81 @@ static GIT_PATH_FUNC(path_state_dir, \"rebase-merge/\")\n static GIT_PATH_FUNC(path_squash_onto, \"rebase-merge/squash-onto\")\n static GIT_PATH_FUNC(path_interactive, \"rebase-merge/interactive\")\n \n+static int add_exec_commands(struct string_list *commands)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n+\t\t\t\t\t&todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_add_exec_commands(&todo_list, commands);\n+\tres = todo_list_write_to_file(the_repository, &todo_list,\n+\t\t\t\t      todo_file, NULL, NULL, -1, 0);\n+\ttodo_list_release(&todo_list);\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n+}\n+\n+static int rearrange_squash_in_todo_file(void)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n+\t\t\t\t\t&todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\tres = todo_list_rearrange_squash(&todo_list);\n+\tif (!res)\n+\t\tres = todo_list_write_to_file(the_repository, &todo_list,\n+\t\t\t\t\t      todo_file, NULL, NULL, -1, 0);\n+\n+\ttodo_list_release(&todo_list);\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n+}\n+\n+static int transform_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n+\t\t\t\t\t&todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\tres = todo_list_write_to_file(the_repository, &todo_list, todo_file,\n+\t\t\t\t      NULL, NULL, -1, flags);\n+\ttodo_list_release(&todo_list);\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n+}\n+\n static int edit_todo_file(unsigned flags)\n {\n \tconst char *todo_file = rebase_path_todo();\n@@ -276,16 +351,16 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t}\n \tcase SHORTEN_OIDS:\n \tcase EXPAND_OIDS:\n-\t\tret = transform_todo_file(the_repository, flags);\n+\t\tret = transform_todo_file(flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n \t\tret = check_todo_list_from_file(the_repository);\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n-\t\tret = rearrange_squash_in_todo_file(the_repository);\n+\t\tret = rearrange_squash_in_todo_file();\n \t\tbreak;\n \tcase ADD_EXEC:\n-\t\tret = sequencer_add_exec_commands(the_repository, &commands);\n+\t\tret = add_exec_commands(&commands);\n \t\tbreak;\n \tdefault:\n \t\tBUG(\"invalid command '%d'\", command);\ndiff --git a/sequencer.c b/sequencer.c\nindex df8b239fdc..7d35f83f4c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4499,8 +4499,8 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n  * Add commands after pick and (series of) squash/fixup commands\n  * in the todo list.\n  */\n-static void todo_list_add_exec_commands(struct todo_list *todo_list,\n-\t\t\t\t\tstruct string_list *commands)\n+void todo_list_add_exec_commands(struct todo_list *todo_list,\n+\t\t\t\t struct string_list *commands)\n {\n \tstruct strbuf *buf = &todo_list->buf;\n \tsize_t base_offset = buf->len;\n@@ -4559,30 +4559,6 @@ static void todo_list_add_exec_commands(struct todo_list *todo_list,\n \ttodo_list->alloc = alloc;\n }\n \n-int sequencer_add_exec_commands(struct repository *r,\n-\t\t\t\tstruct string_list *commands)\n-{\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res;\n-\n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\ttodo_list_add_exec_commands(&todo_list, commands);\n-\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n-\ttodo_list_release(&todo_list);\n-\n-\tif (res)\n-\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n-\treturn 0;\n-}\n-\n static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n \t\t\t\tstruct strbuf *buf, int num, unsigned flags)\n {\n@@ -4649,29 +4625,6 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n \treturn res;\n }\n \n-int transform_todo_file(struct repository *r, unsigned flags)\n-{\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res;\n-\n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tres = todo_list_write_to_file(r, &todo_list, todo_file,\n-\t\t\t\t      NULL, NULL, -1, flags);\n-\ttodo_list_release(&todo_list);\n-\n-\tif (res)\n-\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n-\treturn 0;\n-}\n-\n static const char edit_todo_list_advice[] =\n N_(\"You can fix this with 'git rebase --edit-todo' \"\n \"and then run 'git rebase --continue'.\\n\"\n@@ -4754,8 +4707,6 @@ static int skip_unnecessary_picks(struct repository *r,\n \treturn 0;\n }\n \n-static int todo_list_rearrange_squash(struct todo_list *todo_list);\n-\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n@@ -4862,7 +4813,7 @@ define_commit_slab(commit_todo_item, struct todo_item *);\n  * message will have to be retrieved from the commit (as the oneline in the\n  * script cannot be trusted) in order to normalize the autosquash arrangement.\n  */\n-static int todo_list_rearrange_squash(struct todo_list *todo_list)\n+int todo_list_rearrange_squash(struct todo_list *todo_list)\n {\n \tstruct hashmap subject2item;\n \tint rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;\n@@ -5000,27 +4951,3 @@ static int todo_list_rearrange_squash(struct todo_list *todo_list)\n \n \treturn 0;\n }\n-\n-int rearrange_squash_in_todo_file(struct repository *r)\n-{\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res = 0;\n-\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n-\n-\tres = todo_list_rearrange_squash(&todo_list);\n-\tif (!res)\n-\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n-\n-\ttodo_list_release(&todo_list);\n-\n-\tif (res)\n-\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n-\treturn 0;\n-}\ndiff --git a/sequencer.h b/sequencer.h\nindex b0688ba2a1..7cca49eff2 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -143,15 +143,14 @@ int sequencer_remove_state(struct replay_opts *opts);\n int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \t\t\t  const char **argv, unsigned flags);\n \n-int sequencer_add_exec_commands(struct repository *r,\n-\t\t\t\tstruct string_list *commands);\n-int transform_todo_file(struct repository *r, unsigned flags);\n+void todo_list_add_exec_commands(struct todo_list *todo_list,\n+\t\t\t\t struct string_list *commands);\n int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash, struct todo_list *todo_list);\n-int rearrange_squash_in_todo_file(struct repository *r);\n+int todo_list_rearrange_squash(struct todo_list *todo_list);\n \n extern const char sign_off_header[];\n \n-- \n2.20.1\n\n"},{"id":"368977","messageId":"20190210132648.12821-16-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190210132648.12821-1-alban.gruin@gmail.com","subject":"[PATCH v7 15/16] sequencer: use edit_todo_list() in complete_action()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-10T13:26:47Z","receivedAt":"2019-02-10T13:27:34Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This changes complete_action() to use edit_todo_list(), now that it can\nhandle the initial edit of the todo list.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v6.\n\n sequencer.c | 21 +++++++--------------\n 1 file changed, 7 insertions(+), 14 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex f6ed4e21e9..df8b239fdc 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4765,6 +4765,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tstruct todo_list new_todo = TODO_LIST_INIT;\n \tstruct strbuf *buf = &todo_list->buf;\n \tstruct object_id oid;\n+\tint res;\n \n \tget_oid(onto, &oid);\n \tshortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);\n@@ -4789,24 +4790,16 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\tif (todo_list_write_to_file(r, todo_list, todo_file,\n-\t\t\t\t    shortrevisions, shortonto, -1,\n-\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))\n-\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n-\n-\tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n-\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n-\t\t\t     rebase_path_todo_backup());\n-\n-\tif (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {\n+\tres = edit_todo_list(r, todo_list, &new_todo, shortrevisions,\n+\t\t\t     shortonto, flags);\n+\tif (res == -1)\n+\t\treturn -1;\n+\telse if (res == -2) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n \n \t\treturn -1;\n-\t}\n-\n-\tstrbuf_stripspace(&new_todo.buf, 1);\n-\tif (new_todo.buf.len == 0) {\n+\t} else if (res == -3) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n \t\ttodo_list_release(&new_todo);\n-- \n2.20.1\n\n"},{"id":"369142","messageId":"4c8f904a-329c-a42c-c2a4-06d5b348dce3@talktalk.net","threadId":"49504","inReplyTo":"20190210132648.12821-8-alban.gruin@gmail.com","subject":"Re: [PATCH v7 07/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-02-12T10:52:23Z","receivedAt":"2019-02-12T10:52:30Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nI think this is almost there, I've got a couple of small comments below.\n\nOn 10/02/2019 13:26, Alban Gruin wrote:\n> This refactors sequencer_add_exec_commands() to work on a todo_list to\n> avoid redundant reads and writes to the disk.\n> \n> Instead of inserting the `exec' commands between the other commands and\n> re-parsing the buffer at the end, they are appended to the buffer once,\n> and a new list of items is created.  Items from the old list are copied\n> across and new `exec' items are appended when necessary.  This\n> eliminates the need to reparse the buffer, but this also means we have\n> to use todo_list_write_to_disk() to write the file.\n> \n> todo_list_add_exec_commands() and sequencer_add_exec_commands() are\n> modified to take a string list instead of a string -- one item for each\n> command.  This makes it easier to insert a new command to the todo list\n> for each command to execute.\n> \n> sequencer_add_exec_commands() still reads the todo list from the disk,\n> as it is needed by rebase -p.\n> \n> complete_action() still uses sequencer_add_exec_commands() for now.\n> This will be changed in a future commit.\n> \n> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n> ---\n>  builtin/rebase--interactive.c |  15 +++--\n>  sequencer.c                   | 109 +++++++++++++++++++++-------------\n>  sequencer.h                   |   5 +-\n>  3 files changed, 82 insertions(+), 47 deletions(-)\n> \n> diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\n> index df19ccaeb9..c131fd4a27 100644\n> --- a/builtin/rebase--interactive.c\n> +++ b/builtin/rebase--interactive.c\n> @@ -65,7 +65,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n>  \t\t\t\t const char *onto, const char *onto_name,\n>  \t\t\t\t const char *squash_onto, const char *head_name,\n>  \t\t\t\t const char *restrict_revision, char *raw_strategies,\n> -\t\t\t\t const char *cmd, unsigned autosquash)\n> +\t\t\t\t struct string_list *commands, unsigned autosquash)\n>  {\n>  \tint ret;\n>  \tconst char *head_hash = NULL;\n> @@ -116,7 +116,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n>  \t\tdiscard_cache();\n>  \t\tret = complete_action(the_repository, opts, flags,\n>  \t\t\t\t      shortrevisions, onto_name, onto,\n> -\t\t\t\t      head_hash, cmd, autosquash);\n> +\t\t\t\t      head_hash, commands, autosquash);\n>  \t}\n>  \n>  \tfree(revisions);\n> @@ -139,6 +139,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \tconst char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,\n>  \t\t*squash_onto = NULL, *upstream = NULL, *head_name = NULL,\n>  \t\t*switch_to = NULL, *cmd = NULL;\n> +\tstruct string_list commands = STRING_LIST_INIT_DUP;\n>  \tchar *raw_strategies = NULL;\n>  \tenum {\n>  \t\tNONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,\n> @@ -221,6 +222,11 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \t\twarning(_(\"--[no-]rebase-cousins has no effect without \"\n>  \t\t\t  \"--rebase-merges\"));\n>  \n> +\tif (cmd && *cmd) {\n> +\t\tstring_list_split(&commands, cmd, '\\n', -1);\n\nI'd suggest a comment along the lines of\n/* As cmd always ends with a newline the last item is empty */\n\n> +\t\t--commands.nr;\n\nStyle: commands.nr--\n\n> +\t}\n> +\n>  \tswitch (command) {\n>  \tcase NONE:\n>  \t\tif (!onto && !upstream)\n> @@ -228,7 +234,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \n>  \t\tret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,\n>  \t\t\t\t\t    onto_name, squash_onto, head_name, restrict_revision,\n> -\t\t\t\t\t    raw_strategies, cmd, autosquash);\n> +\t\t\t\t\t    raw_strategies, &commands, autosquash);\n>  \t\tbreak;\n>  \tcase SKIP: {\n>  \t\tstruct string_list merge_rr = STRING_LIST_INIT_DUP;\n> @@ -262,11 +268,12 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \t\tret = rearrange_squash(the_repository);\n>  \t\tbreak;\n>  \tcase ADD_EXEC:\n> -\t\tret = sequencer_add_exec_commands(the_repository, cmd);\n> +\t\tret = sequencer_add_exec_commands(the_repository, &commands);\n>  \t\tbreak;\n>  \tdefault:\n>  \t\tBUG(\"invalid command '%d'\", command);\n>  \t}\n\nBefore freeing the string list it would be good to reset the number of\nitems with commands.nr++ (with a comment explaining why) so the NUL in\nthe last item gets freed.\n\n> +\tstring_list_clear(&commands, 1);\n\nAs we don't use item.util I think the second argument would be better as 0.\n\n>  \treturn !!ret;\n>  }\n> diff --git a/sequencer.c b/sequencer.c\n> index 99e12c751e..82ca3432cd 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -4505,21 +4505,27 @@ int sequencer_make_script(struct repository *r, FILE *out,\n>   * Add commands after pick and (series of) squash/fixup commands\n>   * in the todo list.\n>   */\n> -int sequencer_add_exec_commands(struct repository *r,\n> -\t\t\t\tconst char *commands)\n> +static void todo_list_add_exec_commands(struct todo_list *todo_list,\n> +\t\t\t\t\tstruct string_list *commands)\n>  {\n> -\tconst char *todo_file = rebase_path_todo();\n> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n> -\tstruct strbuf *buf = &todo_list.buf;\n> -\tsize_t offset = 0, commands_len = strlen(commands);\n> -\tint i, insert;\n> +\tstruct strbuf *buf = &todo_list->buf;\n> +\tsize_t base_offset = buf->len;\n> +\tint i, insert, nr = 0, alloc = 0;\n> +\tstruct todo_item *items = NULL, *base_items = NULL;\n>  \n> -\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> -\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n> +\tbase_items = xcalloc(commands->nr, sizeof(struct todo_item));\n> +\tfor (i = 0; i < commands->nr; i++) {\n> +\t\tsize_t command_len = strlen(commands->items[i].string);\n>  \n> -\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n> -\t\ttodo_list_release(&todo_list);\n> -\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> +\t\tstrbuf_addstr(buf, commands->items[i].string);\n> +\t\tstrbuf_addch(buf, '\\n');\n> +\n> +\t\tbase_items[i].command = TODO_EXEC;\n> +\t\tbase_items[i].offset_in_buf = base_offset;\n> +\t\tbase_items[i].arg_offset = base_offset + strlen(\"exec \");\n> +\t\tbase_items[i].arg_len = command_len - strlen(\"exec \");\n> +\n> +\t\tbase_offset += command_len + 1;\n>  \t}\n>  \n>  \t/*\n> @@ -4527,39 +4533,60 @@ int sequencer_add_exec_commands(struct repository *r,\n>  \t * are considered part of the pick, so we insert the commands *after*\n>  \t * those chains if there are any.\n>  \t */\n> -\tinsert = -1;\n> -\tfor (i = 0; i < todo_list.nr; i++) {\n> -\t\tenum todo_command command = todo_list.items[i].command;\n> -\n> -\t\tif (insert >= 0) {\n> -\t\t\t/* skip fixup/squash chains */\n> -\t\t\tif (command == TODO_COMMENT)\n> -\t\t\t\tcontinue;\n> -\t\t\telse if (is_fixup(command)) {\n> -\t\t\t\tinsert = i + 1;\n> -\t\t\t\tcontinue;\n> -\t\t\t}\n> -\t\t\tstrbuf_insert(buf,\n> -\t\t\t\t      todo_list.items[insert].offset_in_buf +\n> -\t\t\t\t      offset, commands, commands_len);\n> -\t\t\toffset += commands_len;\n> -\t\t\tinsert = -1;\n\nI like the simplification of using insert as a flag. Perhaps we should\ndocument the assumptions. Maybe something like\n\nWe insert the exec commands immediately after rearranging any fixups and\nbefore the user edits the list. This means that a fixup chain can never\ncontain comments (any comments are empty picks that have been commented\nout the the user did not specify --keep-empty) and so it is safe to\ninsert the exec command without looking at the command following the\ncomment.\n\n> +\tinsert = 0;\n> +\tfor (i = 0; i < todo_list->nr; i++) {\n> +\t\tenum todo_command command = todo_list->items[i].command;\n> +\t\tif (insert && !is_fixup(command)) {\n> +\t\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n> +\t\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n> +\t\t\tnr += commands->nr;\n> +\n> +\t\t\tinsert = 0;\n>  \t\t}\n>  \n> -\t\tif (command == TODO_PICK || command == TODO_MERGE)\n> -\t\t\tinsert = i + 1;\n> +\t\tALLOC_GROW(items, nr + 1, alloc);\n> +\t\titems[nr++] = todo_list->items[i];\n> +\n> +\t\tif (command == TODO_PICK || command == TODO_MERGE || is_fixup(command))\n\nI'm not sure what the is_fixup() is for, if the command is a fixup then\nit will have been preceded by a pick or merge so insert will already be 1\n\nI feel a bit mean suggesting a reroll when we're on v7 already but I\nthink these clean-ups would improve the maintainability of the code.\nI'll take a look at the rest of the changes to this series sometime this\nweek.\n\nBest Wishes\n\nPhillip\n\n> +\t\t\tinsert = 1;\n>  \t}\n>  \n>  \t/* insert or append final <commands> */\n> -\tif (insert >= 0 && insert < todo_list.nr)\n> -\t\tstrbuf_insert(buf, todo_list.items[insert].offset_in_buf +\n> -\t\t\t      offset, commands, commands_len);\n> -\telse if (insert >= 0 || !offset)\n> -\t\tstrbuf_add(buf, commands, commands_len);\n> +\tif (insert || nr == todo_list->nr) {\n> +\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n> +\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n> +\t\tnr += commands->nr;\n> +\t}\n> +\n> +\tfree(base_items);\n> +\tFREE_AND_NULL(todo_list->items);\n> +\ttodo_list->items = items;\n> +\ttodo_list->nr = nr;\n> +\ttodo_list->alloc = alloc;\n> +}\n>  \n> -\ti = write_message(buf->buf, buf->len, todo_file, 0);\n> +int sequencer_add_exec_commands(struct repository *r,\n> +\t\t\t\tstruct string_list *commands)\n> +{\n> +\tconst char *todo_file = rebase_path_todo();\n> +\tstruct todo_list todo_list = TODO_LIST_INIT;\n> +\tint res;\n> +\n> +\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> +\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> +\n> +\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n> +\t\ttodo_list_release(&todo_list);\n> +\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> +\t}\n> +\n> +\ttodo_list_add_exec_commands(&todo_list, commands);\n> +\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n>  \ttodo_list_release(&todo_list);\n> -\treturn i;\n> +\n> +\tif (res)\n> +\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n> +\treturn 0;\n>  }\n>  \n>  static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n> @@ -4790,7 +4817,7 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n>  \n>  int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n>  \t\t    const char *shortrevisions, const char *onto_name,\n> -\t\t    const char *onto, const char *orig_head, const char *cmd,\n> +\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n>  \t\t    unsigned autosquash)\n>  {\n>  \tconst char *shortonto, *todo_file = rebase_path_todo();\n> @@ -4809,8 +4836,8 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n>  \tif (autosquash && rearrange_squash(r))\n>  \t\treturn -1;\n>  \n> -\tif (cmd && *cmd)\n> -\t\tsequencer_add_exec_commands(r, cmd);\n> +\tif (commands->nr)\n> +\t\tsequencer_add_exec_commands(r, commands);\n>  \n>  \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n>  \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> diff --git a/sequencer.h b/sequencer.h\n> index 217353e9f0..87d04a3b9b 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -146,12 +146,13 @@ int sequencer_make_script(struct repository *r, FILE *out, int argc,\n>  \t\t\t  const char **argv,\n>  \t\t\t  unsigned flags);\n>  \n> -int sequencer_add_exec_commands(struct repository *r, const char *command);\n> +int sequencer_add_exec_commands(struct repository *r,\n> +\t\t\t\tstruct string_list *commands);\n>  int transform_todo_file(struct repository *r, unsigned flags);\n>  int check_todo_list_from_file(struct repository *r);\n>  int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n>  \t\t    const char *shortrevisions, const char *onto_name,\n> -\t\t    const char *onto, const char *orig_head, const char *cmd,\n> +\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n>  \t\t    unsigned autosquash);\n>  int rearrange_squash(struct repository *r);\n>  \n> \n\n"},{"id":"369164","messageId":"d334fd63-4053-7034-64f9-eee82bdd06c2@gmail.com","threadId":"49504","inReplyTo":"4c8f904a-329c-a42c-c2a4-06d5b348dce3@talktalk.net","subject":"Re: [PATCH v7 07/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-02-12T15:21:24Z","receivedAt":"2019-02-12T15:21:35Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Hi Phillip,\n\nLe 12/02/2019 à 11:52, Phillip Wood a écrit :\n> Hi Alban\n> \n> I think this is almost there, I've got a couple of small comments below.\n> \n> On 10/02/2019 13:26, Alban Gruin wrote:\n>> -%<-\n>> diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\n>> index df19ccaeb9..c131fd4a27 100644\n>> --- a/builtin/rebase--interactive.c\n>> +++ b/builtin/rebase--interactive.c\n>> -%<-\n>> @@ -221,6 +222,11 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>>  \t\twarning(_(\"--[no-]rebase-cousins has no effect without \"\n>>  \t\t\t  \"--rebase-merges\"));\n>>  \n>> +\tif (cmd && *cmd) {\n>> +\t\tstring_list_split(&commands, cmd, '\\n', -1);\n> \n> I'd suggest a comment along the lines of\n> /* As cmd always ends with a newline the last item is empty */\n> \n>> +\t\t--commands.nr;\n> \n> Style: commands.nr--\n> \n>> +\t}\n>> +\n>>  \tswitch (command) {\n>>  \tcase NONE:\n>>  \t\tif (!onto && !upstream)\n>> @@ -228,7 +234,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>>  \n>>  \t\tret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,\n>>  \t\t\t\t\t    onto_name, squash_onto, head_name, restrict_revision,\n>> -\t\t\t\t\t    raw_strategies, cmd, autosquash);\n>> +\t\t\t\t\t    raw_strategies, &commands, autosquash);\n>>  \t\tbreak;\n>>  \tcase SKIP: {\n>>  \t\tstruct string_list merge_rr = STRING_LIST_INIT_DUP;\n>> @@ -262,11 +268,12 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>>  \t\tret = rearrange_squash(the_repository);\n>>  \t\tbreak;\n>>  \tcase ADD_EXEC:\n>> -\t\tret = sequencer_add_exec_commands(the_repository, cmd);\n>> +\t\tret = sequencer_add_exec_commands(the_repository, &commands);\n>>  \t\tbreak;\n>>  \tdefault:\n>>  \t\tBUG(\"invalid command '%d'\", command);\n>>  \t}\n> \n> Before freeing the string list it would be good to reset the number of\n> items with commands.nr++ (with a comment explaining why) so the NUL in\n> the last item gets freed.\n> \n>> +\tstring_list_clear(&commands, 1);\n> \n> As we don't use item.util I think the second argument would be better as 0.\n> \n>>  \treturn !!ret;\n>>  }\n\nFWIW I just stumbled across string_list_remove_empty_items(), which\nseems to do exactly the same thing, but that way we don’t have to do\nthis kind of hacks.\n\n>> diff --git a/sequencer.c b/sequencer.c\n>> index 99e12c751e..82ca3432cd 100644\n>> --- a/sequencer.c\n>> +++ b/sequencer.c\n>> @@ -4505,21 +4505,27 @@ int sequencer_make_script(struct repository *r, FILE *out,\n>>   * Add commands after pick and (series of) squash/fixup commands\n>>   * in the todo list.\n>>   */\n>> -int sequencer_add_exec_commands(struct repository *r,\n>> -\t\t\t\tconst char *commands)\n>> +static void todo_list_add_exec_commands(struct todo_list *todo_list,\n>> +\t\t\t\t\tstruct string_list *commands)\n>>  {\n>> -\tconst char *todo_file = rebase_path_todo();\n>> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n>> -\tstruct strbuf *buf = &todo_list.buf;\n>> -\tsize_t offset = 0, commands_len = strlen(commands);\n>> -\tint i, insert;\n>> +\tstruct strbuf *buf = &todo_list->buf;\n>> +\tsize_t base_offset = buf->len;\n>> +\tint i, insert, nr = 0, alloc = 0;\n>> +\tstruct todo_item *items = NULL, *base_items = NULL;\n>>  \n>> -\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>> -\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n>> +\tbase_items = xcalloc(commands->nr, sizeof(struct todo_item));\n>> +\tfor (i = 0; i < commands->nr; i++) {\n>> +\t\tsize_t command_len = strlen(commands->items[i].string);\n>>  \n>> -\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n>> -\t\ttodo_list_release(&todo_list);\n>> -\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n>> +\t\tstrbuf_addstr(buf, commands->items[i].string);\n>> +\t\tstrbuf_addch(buf, '\\n');\n>> +\n>> +\t\tbase_items[i].command = TODO_EXEC;\n>> +\t\tbase_items[i].offset_in_buf = base_offset;\n>> +\t\tbase_items[i].arg_offset = base_offset + strlen(\"exec \");\n>> +\t\tbase_items[i].arg_len = command_len - strlen(\"exec \");\n>> +\n>> +\t\tbase_offset += command_len + 1;\n>>  \t}\n>>  \n>>  \t/*\n>> @@ -4527,39 +4533,60 @@ int sequencer_add_exec_commands(struct repository *r,\n>>  \t * are considered part of the pick, so we insert the commands *after*\n>>  \t * those chains if there are any.\n>>  \t */\n>> -\tinsert = -1;\n>> -\tfor (i = 0; i < todo_list.nr; i++) {\n>> -\t\tenum todo_command command = todo_list.items[i].command;\n>> -\n>> -\t\tif (insert >= 0) {\n>> -\t\t\t/* skip fixup/squash chains */\n>> -\t\t\tif (command == TODO_COMMENT)\n>> -\t\t\t\tcontinue;\n>> -\t\t\telse if (is_fixup(command)) {\n>> -\t\t\t\tinsert = i + 1;\n>> -\t\t\t\tcontinue;\n>> -\t\t\t}\n>> -\t\t\tstrbuf_insert(buf,\n>> -\t\t\t\t      todo_list.items[insert].offset_in_buf +\n>> -\t\t\t\t      offset, commands, commands_len);\n>> -\t\t\toffset += commands_len;\n>> -\t\t\tinsert = -1;\n> \n> I like the simplification of using insert as a flag. Perhaps we should\n> document the assumptions. Maybe something like\n> \n> We insert the exec commands immediately after rearranging any fixups and\n> before the user edits the list. This means that a fixup chain can never\n> contain comments (any comments are empty picks that have been commented\n> out the the user did not specify --keep-empty) and so it is safe to\n> insert the exec command without looking at the command following the\n> comment.\n> \n\nI slightly reworded this and added it to the existing comment just\nbefore the for loop:\n\n\t/*\n\t * Insert <commands> after every pick. Here, fixup/squash chains\n\t * are considered part of the pick, so we insert the commands *after*\n\t * those chains if there are any.\n\t *\n\t * As we insert the exec commands immediatly after rearranging\n\t * any fixups and before the user edits the list, a fixup chain\n\t * can never contain comments (any comments are empty picks that\n\t * have been commented out because the user did not specify\n\t * --keep-empty).  So, it is safe to insert an exec command\n\t * without looking at the command following a comment.\n\t */\n\n>> +\tinsert = 0;\n>> +\tfor (i = 0; i < todo_list->nr; i++) {\n>> +\t\tenum todo_command command = todo_list->items[i].command;\n>> +\t\tif (insert && !is_fixup(command)) {\n>> +\t\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n>> +\t\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n>> +\t\t\tnr += commands->nr;\n>> +\n>> +\t\t\tinsert = 0;\n>>  \t\t}\n>>  \n>> -\t\tif (command == TODO_PICK || command == TODO_MERGE)\n>> -\t\t\tinsert = i + 1;\n>> +\t\tALLOC_GROW(items, nr + 1, alloc);\n>> +\t\titems[nr++] = todo_list->items[i];\n>> +\n>> +\t\tif (command == TODO_PICK || command == TODO_MERGE || is_fixup(command))\n> \n> I'm not sure what the is_fixup() is for, if the command is a fixup then\n> it will have been preceded by a pick or merge so insert will already be 1\n> \n> I feel a bit mean suggesting a reroll when we're on v7 already but I\n> think these clean-ups would improve the maintainability of the code.\n> I'll take a look at the rest of the changes to this series sometime this\n> week.\n> \n> Best Wishes\n> \n> Phillip\n> \n\nCheers,\nAlban\n\n"},{"id":"369217","messageId":"3bc5ffea-f8ac-65f3-c9b8-61ddc2733742@talktalk.net","threadId":"49504","inReplyTo":"d334fd63-4053-7034-64f9-eee82bdd06c2@gmail.com","subject":"Re: [PATCH v7 07/16] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-02-13T10:03:49Z","receivedAt":"2019-02-13T10:03:53Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nOn 12/02/2019 15:21, Alban Gruin wrote:\n> Hi Phillip,\n> \n> Le 12/02/2019 à 11:52, Phillip Wood a écrit :\n>> Hi Alban\n>>\n>> I think this is almost there, I've got a couple of small comments below.\n>>\n>> On 10/02/2019 13:26, Alban Gruin wrote:\n>>> -%<-\n>>> diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\n>>> index df19ccaeb9..c131fd4a27 100644\n>>> --- a/builtin/rebase--interactive.c\n>>> +++ b/builtin/rebase--interactive.c\n>>> -%<-\n>>> @@ -221,6 +222,11 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>>>  \t\twarning(_(\"--[no-]rebase-cousins has no effect without \"\n>>>  \t\t\t  \"--rebase-merges\"));\n>>>  \n>>> +\tif (cmd && *cmd) {\n>>> +\t\tstring_list_split(&commands, cmd, '\\n', -1);\n>>\n>> I'd suggest a comment along the lines of\n>> /* As cmd always ends with a newline the last item is empty */\n>>\n>>> +\t\t--commands.nr;\n>>\n>> Style: commands.nr--\n>>\n>>> +\t}\n>>> +\n>>>  \tswitch (command) {\n>>>  \tcase NONE:\n>>>  \t\tif (!onto && !upstream)\n>>> @@ -228,7 +234,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>>>  \n>>>  \t\tret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,\n>>>  \t\t\t\t\t    onto_name, squash_onto, head_name, restrict_revision,\n>>> -\t\t\t\t\t    raw_strategies, cmd, autosquash);\n>>> +\t\t\t\t\t    raw_strategies, &commands, autosquash);\n>>>  \t\tbreak;\n>>>  \tcase SKIP: {\n>>>  \t\tstruct string_list merge_rr = STRING_LIST_INIT_DUP;\n>>> @@ -262,11 +268,12 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>>>  \t\tret = rearrange_squash(the_repository);\n>>>  \t\tbreak;\n>>>  \tcase ADD_EXEC:\n>>> -\t\tret = sequencer_add_exec_commands(the_repository, cmd);\n>>> +\t\tret = sequencer_add_exec_commands(the_repository, &commands);\n>>>  \t\tbreak;\n>>>  \tdefault:\n>>>  \t\tBUG(\"invalid command '%d'\", command);\n>>>  \t}\n>>\n>> Before freeing the string list it would be good to reset the number of\n>> items with commands.nr++ (with a comment explaining why) so the NUL in\n>> the last item gets freed.\n>>\n>>> +\tstring_list_clear(&commands, 1);\n>>\n>> As we don't use item.util I think the second argument would be better as 0.\n>>\n>>>  \treturn !!ret;\n>>>  }\n> \n> FWIW I just stumbled across string_list_remove_empty_items(), which\n> seems to do exactly the same thing, but that way we don’t have to do\n> this kind of hacks.\n\nGood find, that sounds like a much better solution.\n\n> \n>>> diff --git a/sequencer.c b/sequencer.c\n>>> index 99e12c751e..82ca3432cd 100644\n>>> --- a/sequencer.c\n>>> +++ b/sequencer.c\n>>> @@ -4505,21 +4505,27 @@ int sequencer_make_script(struct repository *r, FILE *out,\n>>>   * Add commands after pick and (series of) squash/fixup commands\n>>>   * in the todo list.\n>>>   */\n>>> -int sequencer_add_exec_commands(struct repository *r,\n>>> -\t\t\t\tconst char *commands)\n>>> +static void todo_list_add_exec_commands(struct todo_list *todo_list,\n>>> +\t\t\t\t\tstruct string_list *commands)\n>>>  {\n>>> -\tconst char *todo_file = rebase_path_todo();\n>>> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n>>> -\tstruct strbuf *buf = &todo_list.buf;\n>>> -\tsize_t offset = 0, commands_len = strlen(commands);\n>>> -\tint i, insert;\n>>> +\tstruct strbuf *buf = &todo_list->buf;\n>>> +\tsize_t base_offset = buf->len;\n>>> +\tint i, insert, nr = 0, alloc = 0;\n>>> +\tstruct todo_item *items = NULL, *base_items = NULL;\n>>>  \n>>> -\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>>> -\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n>>> +\tbase_items = xcalloc(commands->nr, sizeof(struct todo_item));\n>>> +\tfor (i = 0; i < commands->nr; i++) {\n>>> +\t\tsize_t command_len = strlen(commands->items[i].string);\n>>>  \n>>> -\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n>>> -\t\ttodo_list_release(&todo_list);\n>>> -\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n>>> +\t\tstrbuf_addstr(buf, commands->items[i].string);\n>>> +\t\tstrbuf_addch(buf, '\\n');\n>>> +\n>>> +\t\tbase_items[i].command = TODO_EXEC;\n>>> +\t\tbase_items[i].offset_in_buf = base_offset;\n>>> +\t\tbase_items[i].arg_offset = base_offset + strlen(\"exec \");\n>>> +\t\tbase_items[i].arg_len = command_len - strlen(\"exec \");\n>>> +\n>>> +\t\tbase_offset += command_len + 1;\n>>>  \t}\n>>>  \n>>>  \t/*\n>>> @@ -4527,39 +4533,60 @@ int sequencer_add_exec_commands(struct repository *r,\n>>>  \t * are considered part of the pick, so we insert the commands *after*\n>>>  \t * those chains if there are any.\n>>>  \t */\n>>> -\tinsert = -1;\n>>> -\tfor (i = 0; i < todo_list.nr; i++) {\n>>> -\t\tenum todo_command command = todo_list.items[i].command;\n>>> -\n>>> -\t\tif (insert >= 0) {\n>>> -\t\t\t/* skip fixup/squash chains */\n>>> -\t\t\tif (command == TODO_COMMENT)\n>>> -\t\t\t\tcontinue;\n>>> -\t\t\telse if (is_fixup(command)) {\n>>> -\t\t\t\tinsert = i + 1;\n>>> -\t\t\t\tcontinue;\n>>> -\t\t\t}\n>>> -\t\t\tstrbuf_insert(buf,\n>>> -\t\t\t\t      todo_list.items[insert].offset_in_buf +\n>>> -\t\t\t\t      offset, commands, commands_len);\n>>> -\t\t\toffset += commands_len;\n>>> -\t\t\tinsert = -1;\n>>\n>> I like the simplification of using insert as a flag. Perhaps we should\n>> document the assumptions. Maybe something like\n>>\n>> We insert the exec commands immediately after rearranging any fixups and\n>> before the user edits the list. This means that a fixup chain can never\n>> contain comments (any comments are empty picks that have been commented\n>> out the the user did not specify --keep-empty) and so it is safe to\n>> insert the exec command without looking at the command following the\n>> comment.\n>>\n> \n> I slightly reworded this and added it to the existing comment just\n> before the for loop:\n> \n> \t/*\n> \t * Insert <commands> after every pick. Here, fixup/squash chains\n> \t * are considered part of the pick, so we insert the commands *after*\n> \t * those chains if there are any.\n> \t *\n> \t * As we insert the exec commands immediatly after rearranging\n> \t * any fixups and before the user edits the list, a fixup chain\n> \t * can never contain comments (any comments are empty picks that\n> \t * have been commented out because the user did not specify\n> \t * --keep-empty).  So, it is safe to insert an exec command\n> \t * without looking at the command following a comment.\n> \t */\n> \n\nThat sounds good\n\nBest Wishes\n\nPhillip\n\n>>> +\tinsert = 0;\n>>> +\tfor (i = 0; i < todo_list->nr; i++) {\n>>> +\t\tenum todo_command command = todo_list->items[i].command;\n>>> +\t\tif (insert && !is_fixup(command)) {\n>>> +\t\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n>>> +\t\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n>>> +\t\t\tnr += commands->nr;\n>>> +\n>>> +\t\t\tinsert = 0;\n>>>  \t\t}\n>>>  \n>>> -\t\tif (command == TODO_PICK || command == TODO_MERGE)\n>>> -\t\t\tinsert = i + 1;\n>>> +\t\tALLOC_GROW(items, nr + 1, alloc);\n>>> +\t\titems[nr++] = todo_list->items[i];\n>>> +\n>>> +\t\tif (command == TODO_PICK || command == TODO_MERGE || is_fixup(command))\n>>\n>> I'm not sure what the is_fixup() is for, if the command is a fixup then\n>> it will have been preceded by a pick or merge so insert will already be 1\n>>\n>> I feel a bit mean suggesting a reroll when we're on v7 already but I\n>> think these clean-ups would improve the maintainability of the code.\n>> I'll take a look at the rest of the changes to this series sometime this\n>> week.\n>>\n>> Best Wishes\n>>\n>> Phillip\n>>\n> \n> Cheers,\n> Alban\n> \n\n"},{"id":"369218","messageId":"1cc273d3-6d49-20f0-780b-c2d6d2edda87@talktalk.net","threadId":"49504","inReplyTo":"20190210132648.12821-12-alban.gruin@gmail.com","subject":"Re: [PATCH v7 11/16] sequencer: refactor skip_unnecessary_picks() to work on a todo_list","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-02-13T10:05:42Z","receivedAt":"2019-02-13T10:05:44Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nOn 10/02/2019 13:26, Alban Gruin wrote:\n> This refactors skip_unnecessary_picks() to work on a todo_list.  As this\n> function is only called by complete_action() (and thus is not used by\n> rebase -p), the file-handling logic is completely dropped here.\n> \n> Instead of truncating the todo list’s buffer, the items are moved to\n> the beginning of the list, eliminating the need to reparse the list.\n> This also means its buffer cannot be directly written to the disk.\n> \n> rewrite_file() is then removed, as it is now unused.\n> \n> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n> ---\n>  sequencer.c | 82 +++++++++++++----------------------------------------\n>  1 file changed, 19 insertions(+), 63 deletions(-)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index eb8a622af0..eacaf52250 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -4717,52 +4717,22 @@ int check_todo_list_from_file(struct repository *r)\n>  \treturn res;\n>  }\n>  \n> -static int rewrite_file(const char *path, const char *buf, size_t len)\n> -{\n> -\tint rc = 0;\n> -\tint fd = open(path, O_WRONLY | O_TRUNC);\n> -\tif (fd < 0)\n> -\t\treturn error_errno(_(\"could not open '%s' for writing\"), path);\n> -\tif (write_in_full(fd, buf, len) < 0)\n> -\t\trc = error_errno(_(\"could not write to '%s'\"), path);\n> -\tif (close(fd) && !rc)\n> -\t\trc = error_errno(_(\"could not close '%s'\"), path);\n> -\treturn rc;\n> -}\n> -\n>  /* skip picking commits whose parents are unchanged */\n> -static int skip_unnecessary_picks(struct repository *r, struct object_id *output_oid)\n> +static int skip_unnecessary_picks(struct repository *r,\n> +\t\t\t\t  struct todo_list *todo_list,\n> +\t\t\t\t  struct object_id *base_oid)\n\nThanks for renaming that parameter, I think it's much clearer.\n\nBest Wishes\n\nPhillip\n>  {\n> -\tconst char *todo_file = rebase_path_todo();\n> -\tstruct strbuf buf = STRBUF_INIT;\n> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n>  \tstruct object_id *parent_oid;\n> -\tint fd, i;\n> -\n> -\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n> -\t\treturn error(_(\"could not read 'onto'\"));\n> -\tif (get_oid(buf.buf, output_oid)) {\n> -\t\tstrbuf_release(&buf);\n> -\t\treturn error(_(\"need a HEAD to fixup\"));\n> -\t}\n> -\tstrbuf_release(&buf);\n> -\n> -\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n> -\t\treturn -1;\n> -\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n> -\t\ttodo_list_release(&todo_list);\n> -\t\treturn -1;\n> -\t}\n> +\tint i;\n>  \n> -\tfor (i = 0; i < todo_list.nr; i++) {\n> -\t\tstruct todo_item *item = todo_list.items + i;\n> +\tfor (i = 0; i < todo_list->nr; i++) {\n> +\t\tstruct todo_item *item = todo_list->items + i;\n>  \n>  \t\tif (item->command >= TODO_NOOP)\n>  \t\t\tcontinue;\n>  \t\tif (item->command != TODO_PICK)\n>  \t\t\tbreak;\n>  \t\tif (parse_commit(item->commit)) {\n> -\t\t\ttodo_list_release(&todo_list);\n>  \t\t\treturn error(_(\"could not parse commit '%s'\"),\n>  \t\t\t\toid_to_hex(&item->commit->object.oid));\n>  \t\t}\n> @@ -4771,42 +4741,26 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n>  \t\tif (item->commit->parents->next)\n>  \t\t\tbreak; /* merge commit */\n>  \t\tparent_oid = &item->commit->parents->item->object.oid;\n> -\t\tif (!oideq(parent_oid, output_oid))\n> +\t\tif (!oideq(parent_oid, base_oid))\n>  \t\t\tbreak;\n> -\t\toidcpy(output_oid, &item->commit->object.oid);\n> +\t\toidcpy(base_oid, &item->commit->object.oid);\n>  \t}\n>  \tif (i > 0) {\n> -\t\tint offset = get_item_line_offset(&todo_list, i);\n>  \t\tconst char *done_path = rebase_path_done();\n>  \n> -\t\tfd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);\n> -\t\tif (fd < 0) {\n> -\t\t\terror_errno(_(\"could not open '%s' for writing\"),\n> -\t\t\t\t    done_path);\n> -\t\t\ttodo_list_release(&todo_list);\n> -\t\t\treturn -1;\n> -\t\t}\n> -\t\tif (write_in_full(fd, todo_list.buf.buf, offset) < 0) {\n> +\t\tif (todo_list_write_to_file(r, todo_list, done_path, NULL, NULL, i, 0)) {\n>  \t\t\terror_errno(_(\"could not write to '%s'\"), done_path);\n> -\t\t\ttodo_list_release(&todo_list);\n> -\t\t\tclose(fd);\n>  \t\t\treturn -1;\n>  \t\t}\n> -\t\tclose(fd);\n>  \n> -\t\tif (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,\n> -\t\t\t\t todo_list.buf.len - offset) < 0) {\n> -\t\t\ttodo_list_release(&todo_list);\n> -\t\t\treturn -1;\n> -\t\t}\n> +\t\tMOVE_ARRAY(todo_list->items, todo_list->items + i, todo_list->nr - i);\n> +\t\ttodo_list->nr -= i;\n> +\t\ttodo_list->current = 0;\n>  \n> -\t\ttodo_list.current = i;\n> -\t\tif (is_fixup(peek_command(&todo_list, 0)))\n> -\t\t\trecord_in_rewritten(output_oid, peek_command(&todo_list, 0));\n> +\t\tif (is_fixup(peek_command(todo_list, 0)))\n> +\t\t\trecord_in_rewritten(base_oid, peek_command(todo_list, 0));\n>  \t}\n>  \n> -\ttodo_list_release(&todo_list);\n> -\n>  \treturn 0;\n>  }\n>  \n> @@ -4879,6 +4833,11 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n>  \t\treturn -1;\n>  \t}\n>  \n> +\tif (opts->allow_ff && skip_unnecessary_picks(r, &new_todo, &oid)) {\n> +\t\ttodo_list_release(&new_todo);\n> +\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n> +\t}\n> +\n>  \tif (todo_list_write_to_file(r, &new_todo, todo_file, NULL, NULL, -1,\n>  \t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS))) {\n>  \t\ttodo_list_release(&new_todo);\n> @@ -4887,9 +4846,6 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n>  \n>  \ttodo_list_release(&new_todo);\n>  \n> -\tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\n> -\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n> -\n>  \tif (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))\n>  \t\treturn -1;\n>  \n> \n\n"},{"id":"369220","messageId":"2dfec48d-70fe-ca69-47ac-2a766db2e80c@talktalk.net","threadId":"49504","inReplyTo":"20190210132648.12821-15-alban.gruin@gmail.com","subject":"Re: [PATCH v7 14/16] rebase-interactive: rewrite edit_todo_list() to handle the initial edit","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-02-13T10:10:29Z","receivedAt":"2019-02-13T10:10:34Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nI think the error handling is fine now, I've got a couple of small\ncomments below.\n\nOn 10/02/2019 13:26, Alban Gruin wrote:\n> edit_todo_list() is changed to work on a todo_list, and to handle the\n> initial edition of the todo list (ie. making a backup of the todo\n> list).\n> \n> It does not check for dropped commits yet, as todo_list_check() does not\n> take the commits that have already been processed by the rebase (ie. the\n> todo list is edited in the middle of a rebase session).\n> \n> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n> ---\n>  builtin/rebase--interactive.c | 24 +++++++++++++++++-\n>  rebase-interactive.c          | 48 +++++++++++++++++------------------\n>  rebase-interactive.h          |  4 ++-\n>  sequencer.c                   |  3 +--\n>  sequencer.h                   |  1 +\n>  5 files changed, 52 insertions(+), 28 deletions(-)\n> \n> diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\n> index 4f2949922f..370d584683 100644\n> --- a/builtin/rebase--interactive.c\n> +++ b/builtin/rebase--interactive.c\n> @@ -13,6 +13,28 @@ static GIT_PATH_FUNC(path_state_dir, \"rebase-merge/\")\n>  static GIT_PATH_FUNC(path_squash_onto, \"rebase-merge/squash-onto\")\n>  static GIT_PATH_FUNC(path_interactive, \"rebase-merge/interactive\")\n>  \n> +static int edit_todo_file(unsigned flags)\n> +{\n> +\tconst char *todo_file = rebase_path_todo();\n> +\tstruct todo_list todo_list = TODO_LIST_INIT,\n> +\t\tnew_todo = TODO_LIST_INIT;\n> +\tint res = 0;\n> +\n> +\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> +\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> +\n> +\tstrbuf_stripspace(&todo_list.buf, 1);\n> +\tres = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);\n> +\tif (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,\n> +\t\t\t\t\t    NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))\n> +\t\tres = error_errno(_(\"could not write '%s'\"), todo_file);\n> +\n> +\ttodo_list_release(&todo_list);\n> +\ttodo_list_release(&new_todo);\n> +\n> +\treturn res;\n> +}\n> +\n>  static int get_revision_ranges(const char *upstream, const char *onto,\n>  \t\t\t       const char **head_hash,\n>  \t\t\t       char **revisions, char **shortrevisions)\n> @@ -241,7 +263,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \t\tbreak;\n>  \t}\n>  \tcase EDIT_TODO:\n> -\t\tret = edit_todo_list(the_repository, flags);\n> +\t\tret = edit_todo_file(flags);\n>  \t\tbreak;\n>  \tcase SHOW_CURRENT_PATCH: {\n>  \t\tstruct child_process cmd = CHILD_PROCESS_INIT;\n> diff --git a/rebase-interactive.c b/rebase-interactive.c\n> index 807f8370db..96c70d1959 100644\n> --- a/rebase-interactive.c\n> +++ b/rebase-interactive.c\n> @@ -87,35 +87,35 @@ void append_todo_help(unsigned keep_empty, int command_count,\n>  \t}\n>  }\n>  \n> -int edit_todo_list(struct repository *r, unsigned flags)\n> +int edit_todo_list(struct repository *r, struct todo_list *todo_list,\n> +\t\t   struct todo_list *new_todo, const char *shortrevisions,\n> +\t\t   const char *shortonto, unsigned flags)\n>  {\n>  \tconst char *todo_file = rebase_path_todo();\n> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n> -\tint res = 0;\n> -\n> -\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> -\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> -\n> -\tstrbuf_stripspace(&todo_list.buf, 1);\n> -\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n> -\tif (todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n> -\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP)) {\n> -\t\ttodo_list_release(&todo_list);\n> -\t\treturn -1;\n> -\t}\n> +\tunsigned initial = shortrevisions && shortonto;\n>  \n> -\tstrbuf_reset(&todo_list.buf);\n> -\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n> -\t\ttodo_list_release(&todo_list);\n> -\t\treturn -1;\n> -\t}\n> +\tif (!initial)\n> +\t\ttodo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n\nIt might be worth a comment to explain that we always want to edit the\nfile so we deliberately ignore any parsing errors (I'm not sure what we\nend writing to the disc if we can't parse the file though) and that the\ntodo list is already parsed in the initial case.\n\n>  \n> -\tif (!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list))\n> -\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n> -\t\t\t\t\t      flags & ~(TODO_LIST_SHORTEN_IDS));\n> +\tif (todo_list_write_to_file(r, todo_list, todo_file, shortrevisions, shortonto,\n> +\t\t\t\t    -1, flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))\n> +\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n>  \n> -\ttodo_list_release(&todo_list);\n> -\treturn res;\n> +\tif (initial && copy_file(rebase_path_todo_backup(), todo_file, 0666))\n> +\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n> +\t\t\t     rebase_path_todo_backup());\n> +\n> +\tif (launch_sequence_editor(todo_file, &new_todo->buf, NULL))\n> +\t\treturn -2;\n> +\n> +\tstrbuf_stripspace(&new_todo->buf, 1);\n> +\tif (initial && new_todo->buf.len == 0)\n> +\t\treturn -3;\n> +\n> +\tif (!initial)\n> +\t\treturn todo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo);\n\nIt might be worth a comment explaining where the list gets parsed for\nthe initial edit.\n\nBest Wishes\n\nPhillip\n\n> +\n> +\treturn 0;\n>  }\n>  \n>  define_commit_slab(commit_seen, unsigned char);\n> diff --git a/rebase-interactive.h b/rebase-interactive.h\n> index 0e5925e3aa..44dbb06311 100644\n> --- a/rebase-interactive.h\n> +++ b/rebase-interactive.h\n> @@ -8,7 +8,9 @@ struct todo_list;\n>  void append_todo_help(unsigned keep_empty, int command_count,\n>  \t\t      const char *shortrevisions, const char *shortonto,\n>  \t\t      struct strbuf *buf);\n> -int edit_todo_list(struct repository *r, unsigned flags);\n> +int edit_todo_list(struct repository *r, struct todo_list *todo_list,\n> +\t\t   struct todo_list *new_todo, const char *shortrevisions,\n> +\t\t   const char *shortonto, unsigned flags);\n>  int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n>  \n>  #endif\n> diff --git a/sequencer.c b/sequencer.c\n> index 64d698032c..f6ed4e21e9 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -55,8 +55,7 @@ static GIT_PATH_FUNC(rebase_path, \"rebase-merge\")\n>   * file and written to the tail of 'done'.\n>   */\n>  GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n> -static GIT_PATH_FUNC(rebase_path_todo_backup,\n> -\t\t     \"rebase-merge/git-rebase-todo.backup\")\n> +GIT_PATH_FUNC(rebase_path_todo_backup, \"rebase-merge/git-rebase-todo.backup\")\n>  \n>  /*\n>   * The rebase command lines that have already been processed. A line\n> diff --git a/sequencer.h b/sequencer.h\n> index c80990659c..b0688ba2a1 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -10,6 +10,7 @@ struct repository;\n>  const char *git_path_commit_editmsg(void);\n>  const char *git_path_seq_dir(void);\n>  const char *rebase_path_todo(void);\n> +const char *rebase_path_todo_backup(void);\n>  \n>  #define APPEND_SIGNOFF_DEDUP (1u << 0)\n>  \n> \n\n"},{"id":"369221","messageId":"e04475c2-6e31-428c-41a1-1805d0378014@talktalk.net","threadId":"49504","inReplyTo":"20190210132648.12821-17-alban.gruin@gmail.com","subject":"Re: [PATCH v7 16/16] rebase--interactive: move several functions to rebase--interactive.c","fromName":"Phillip Wood","fromEmail":"phillip.wood@talktalk.net","sentAt":"2019-02-13T10:17:15Z","receivedAt":"2019-02-13T10:17:21Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nI think it is a good idea to move these functions out of sequencer.c,\npreviously I suggested moving them to rebase-interactive.c rather than\nbuiltin/rebase--interactive.c but I don't think it matters that much. If\nthese can be moved when they're refactored (or in a separate commit\nimmediately after they're refactored if that makes the refactoring patch\nclearer) rather than at the end it might make the history cleaner.\n\nBest Wishes\n\nPhillip\n\nOn 10/02/2019 13:26, Alban Gruin wrote:\n> As sequencer_add_exec_commands(), rearrange_squash_in_todo_file(), and\n> transform_todo_file() are only needed inside of rebase--interactive.c\n> for rebase -p, they are moved there from sequencer.c.\n> \n> The parameter r (repository) is dropped from them, and the error\n> handling of rearrange_squash_in_todo_file() is slightly improved.\n> \n> Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n> ---\n>  builtin/rebase--interactive.c | 81 +++++++++++++++++++++++++++++++++--\n>  sequencer.c                   | 79 ++--------------------------------\n>  sequencer.h                   |  7 ++-\n>  3 files changed, 84 insertions(+), 83 deletions(-)\n> \n> diff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\n> index 370d584683..fb11f23692 100644\n> --- a/builtin/rebase--interactive.c\n> +++ b/builtin/rebase--interactive.c\n> @@ -13,6 +13,81 @@ static GIT_PATH_FUNC(path_state_dir, \"rebase-merge/\")\n>  static GIT_PATH_FUNC(path_squash_onto, \"rebase-merge/squash-onto\")\n>  static GIT_PATH_FUNC(path_interactive, \"rebase-merge/interactive\")\n>  \n> +static int add_exec_commands(struct string_list *commands)\n> +{\n> +\tconst char *todo_file = rebase_path_todo();\n> +\tstruct todo_list todo_list = TODO_LIST_INIT;\n> +\tint res;\n> +\n> +\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> +\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> +\n> +\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n> +\t\t\t\t\t&todo_list)) {\n> +\t\ttodo_list_release(&todo_list);\n> +\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> +\t}\n> +\n> +\ttodo_list_add_exec_commands(&todo_list, commands);\n> +\tres = todo_list_write_to_file(the_repository, &todo_list,\n> +\t\t\t\t      todo_file, NULL, NULL, -1, 0);\n> +\ttodo_list_release(&todo_list);\n> +\n> +\tif (res)\n> +\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n> +\treturn 0;\n> +}\n> +\n> +static int rearrange_squash_in_todo_file(void)\n> +{\n> +\tconst char *todo_file = rebase_path_todo();\n> +\tstruct todo_list todo_list = TODO_LIST_INIT;\n> +\tint res = 0;\n> +\n> +\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> +\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> +\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n> +\t\t\t\t\t&todo_list)) {\n> +\t\ttodo_list_release(&todo_list);\n> +\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> +\t}\n> +\n> +\tres = todo_list_rearrange_squash(&todo_list);\n> +\tif (!res)\n> +\t\tres = todo_list_write_to_file(the_repository, &todo_list,\n> +\t\t\t\t\t      todo_file, NULL, NULL, -1, 0);\n> +\n> +\ttodo_list_release(&todo_list);\n> +\n> +\tif (res)\n> +\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n> +\treturn 0;\n> +}\n> +\n> +static int transform_todo_file(unsigned flags)\n> +{\n> +\tconst char *todo_file = rebase_path_todo();\n> +\tstruct todo_list todo_list = TODO_LIST_INIT;\n> +\tint res;\n> +\n> +\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> +\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> +\n> +\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n> +\t\t\t\t\t&todo_list)) {\n> +\t\ttodo_list_release(&todo_list);\n> +\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> +\t}\n> +\n> +\tres = todo_list_write_to_file(the_repository, &todo_list, todo_file,\n> +\t\t\t\t      NULL, NULL, -1, flags);\n> +\ttodo_list_release(&todo_list);\n> +\n> +\tif (res)\n> +\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n> +\treturn 0;\n> +}\n> +\n>  static int edit_todo_file(unsigned flags)\n>  {\n>  \tconst char *todo_file = rebase_path_todo();\n> @@ -276,16 +351,16 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n>  \t}\n>  \tcase SHORTEN_OIDS:\n>  \tcase EXPAND_OIDS:\n> -\t\tret = transform_todo_file(the_repository, flags);\n> +\t\tret = transform_todo_file(flags);\n>  \t\tbreak;\n>  \tcase CHECK_TODO_LIST:\n>  \t\tret = check_todo_list_from_file(the_repository);\n>  \t\tbreak;\n>  \tcase REARRANGE_SQUASH:\n> -\t\tret = rearrange_squash_in_todo_file(the_repository);\n> +\t\tret = rearrange_squash_in_todo_file();\n>  \t\tbreak;\n>  \tcase ADD_EXEC:\n> -\t\tret = sequencer_add_exec_commands(the_repository, &commands);\n> +\t\tret = add_exec_commands(&commands);\n>  \t\tbreak;\n>  \tdefault:\n>  \t\tBUG(\"invalid command '%d'\", command);\n> diff --git a/sequencer.c b/sequencer.c\n> index df8b239fdc..7d35f83f4c 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -4499,8 +4499,8 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n>   * Add commands after pick and (series of) squash/fixup commands\n>   * in the todo list.\n>   */\n> -static void todo_list_add_exec_commands(struct todo_list *todo_list,\n> -\t\t\t\t\tstruct string_list *commands)\n> +void todo_list_add_exec_commands(struct todo_list *todo_list,\n> +\t\t\t\t struct string_list *commands)\n>  {\n>  \tstruct strbuf *buf = &todo_list->buf;\n>  \tsize_t base_offset = buf->len;\n> @@ -4559,30 +4559,6 @@ static void todo_list_add_exec_commands(struct todo_list *todo_list,\n>  \ttodo_list->alloc = alloc;\n>  }\n>  \n> -int sequencer_add_exec_commands(struct repository *r,\n> -\t\t\t\tstruct string_list *commands)\n> -{\n> -\tconst char *todo_file = rebase_path_todo();\n> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n> -\tint res;\n> -\n> -\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> -\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> -\n> -\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n> -\t\ttodo_list_release(&todo_list);\n> -\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> -\t}\n> -\n> -\ttodo_list_add_exec_commands(&todo_list, commands);\n> -\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n> -\ttodo_list_release(&todo_list);\n> -\n> -\tif (res)\n> -\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n> -\treturn 0;\n> -}\n> -\n>  static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n>  \t\t\t\tstruct strbuf *buf, int num, unsigned flags)\n>  {\n> @@ -4649,29 +4625,6 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n>  \treturn res;\n>  }\n>  \n> -int transform_todo_file(struct repository *r, unsigned flags)\n> -{\n> -\tconst char *todo_file = rebase_path_todo();\n> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n> -\tint res;\n> -\n> -\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n> -\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n> -\n> -\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n> -\t\ttodo_list_release(&todo_list);\n> -\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n> -\t}\n> -\n> -\tres = todo_list_write_to_file(r, &todo_list, todo_file,\n> -\t\t\t\t      NULL, NULL, -1, flags);\n> -\ttodo_list_release(&todo_list);\n> -\n> -\tif (res)\n> -\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n> -\treturn 0;\n> -}\n> -\n>  static const char edit_todo_list_advice[] =\n>  N_(\"You can fix this with 'git rebase --edit-todo' \"\n>  \"and then run 'git rebase --continue'.\\n\"\n> @@ -4754,8 +4707,6 @@ static int skip_unnecessary_picks(struct repository *r,\n>  \treturn 0;\n>  }\n>  \n> -static int todo_list_rearrange_squash(struct todo_list *todo_list);\n> -\n>  int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n>  \t\t    const char *shortrevisions, const char *onto_name,\n>  \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n> @@ -4862,7 +4813,7 @@ define_commit_slab(commit_todo_item, struct todo_item *);\n>   * message will have to be retrieved from the commit (as the oneline in the\n>   * script cannot be trusted) in order to normalize the autosquash arrangement.\n>   */\n> -static int todo_list_rearrange_squash(struct todo_list *todo_list)\n> +int todo_list_rearrange_squash(struct todo_list *todo_list)\n>  {\n>  \tstruct hashmap subject2item;\n>  \tint rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;\n> @@ -5000,27 +4951,3 @@ static int todo_list_rearrange_squash(struct todo_list *todo_list)\n>  \n>  \treturn 0;\n>  }\n> -\n> -int rearrange_squash_in_todo_file(struct repository *r)\n> -{\n> -\tconst char *todo_file = rebase_path_todo();\n> -\tstruct todo_list todo_list = TODO_LIST_INIT;\n> -\tint res = 0;\n> -\n> -\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n> -\t\treturn -1;\n> -\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n> -\t\ttodo_list_release(&todo_list);\n> -\t\treturn -1;\n> -\t}\n> -\n> -\tres = todo_list_rearrange_squash(&todo_list);\n> -\tif (!res)\n> -\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n> -\n> -\ttodo_list_release(&todo_list);\n> -\n> -\tif (res)\n> -\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n> -\treturn 0;\n> -}\n> diff --git a/sequencer.h b/sequencer.h\n> index b0688ba2a1..7cca49eff2 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -143,15 +143,14 @@ int sequencer_remove_state(struct replay_opts *opts);\n>  int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n>  \t\t\t  const char **argv, unsigned flags);\n>  \n> -int sequencer_add_exec_commands(struct repository *r,\n> -\t\t\t\tstruct string_list *commands);\n> -int transform_todo_file(struct repository *r, unsigned flags);\n> +void todo_list_add_exec_commands(struct todo_list *todo_list,\n> +\t\t\t\t struct string_list *commands);\n>  int check_todo_list_from_file(struct repository *r);\n>  int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n>  \t\t    const char *shortrevisions, const char *onto_name,\n>  \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n>  \t\t    unsigned autosquash, struct todo_list *todo_list);\n> -int rearrange_squash_in_todo_file(struct repository *r);\n> +int todo_list_rearrange_squash(struct todo_list *todo_list);\n>  \n>  extern const char sign_off_header[];\n>  \n> \n\n"},{"id":"370729","messageId":"20190305191805.13561-1-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190210132648.12821-1-alban.gruin@gmail.com","subject":"[PATCH v8 00/18] sequencer: refactor functions working on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:17:47Z","receivedAt":"2019-03-05T19:18:25Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"At the center of the \"interactive\" part of the interactive rebase lies\nthe todo list.  When the user starts an interactive rebase, a todo list\nis generated, presented to the user (who then edits it using a text\neditor), read back, and then is checked and processed before the actual\nrebase takes place.\n\nSome of this processing includes adding execs commands, reordering\nfixup! and squash! commits, and checking if no commits were accidentally\ndropped by the user.\n\nBefore I converted the interactive rebase in C, these functions were\ncalled by git-rebase--interactive.sh through git-rebase--helper.  Since\nthe only way to pass around a large amount of data between a shell\nscript and a C program is to use a file (or any declination of a file),\nthe functions that checked and processed the todo list were directly\nworking on a file, the same file that the user edited.\n\nDuring the conversion, I did not address this issue, which lead to a\ncomplete_action() that reads the todo list file, does some computation\nbased on its content, and writes it back to the disk, several times in\nthe same function.\n\nAs it is not an efficient way to handle a data structure, this patch\nseries refactor the functions that processes the todo list to work on a\ntodo_list structure instead of reading it from the disk.\n\nSome commits consists in modifying edit_todo_list() (initially used by\n--edit-todo) to handle the initial edition of the todo list, to increase\ncode sharing.\n\nThis is based on nd/the-index (cde555480b, \"Merge branch\n'nd/the-index'\").\n\nChanges since v7:\n\n - Add some comments to clarify some assumptions.\n\n - Use `string_list_remove_empty_items()` instead of `--commands.nr` to\n   remove empty commands.\n\n - Split the last commit (\"rebase--interactive: move several functions\n   to rebase--interactive.c\") into three, one for each function.\n\nThe tip of this series is tagged as \"refactor-todo-list-v8\" in\nhttps://github.com/agrn/git.\n\nThe range diff is included below.\n\nAlban Gruin (18):\n  sequencer: changes in parse_insn_buffer()\n  sequencer: make the todo_list structure public\n  sequencer: remove the 'arg' field from todo_item\n  sequencer: refactor transform_todos() to work on a todo_list\n  sequencer: introduce todo_list_write_to_file()\n  sequencer: refactor check_todo_list() to work on a todo_list\n  sequencer: refactor sequencer_add_exec_commands() to work on a\n    todo_list\n  sequencer: refactor rearrange_squash() to work on a todo_list\n  sequencer: make sequencer_make_script() write its script to a strbuf\n  sequencer: change complete_action() to use the refactored functions\n  rebase--interactive: move sequencer_add_exec_commands()\n  rebase--interactive: move rearrange_squash_in_todo_file()\n  sequencer: refactor skip_unnecessary_picks() to work on a todo_list\n  rebase-interactive: use todo_list_write_to_file() in edit_todo_list()\n  rebase-interactive: append_todo_help() changes\n  rebase-interactive: rewrite edit_todo_list() to handle the initial\n    edit\n  sequencer: use edit_todo_list() in complete_action()\n  rebase--interactive: move transform_todo_file()\n\n builtin/rebase--interactive.c | 144 ++++++--\n rebase-interactive.c          | 146 ++++++--\n rebase-interactive.h          |   9 +-\n sequencer.c                   | 655 ++++++++++++----------------------\n sequencer.h                   |  81 ++++-\n 5 files changed, 553 insertions(+), 482 deletions(-)\n\nRange-diff against v7:\n 1:  0bc5f714e5 =  1:  0bc5f714e5 sequencer: changes in parse_insn_buffer()\n 2:  34d149ff25 =  2:  34d149ff25 sequencer: make the todo_list structure public\n 3:  8200f7a6be =  3:  8200f7a6be sequencer: remove the 'arg' field from todo_item\n 4:  ce8ca23ee0 =  4:  ce8ca23ee0 sequencer: refactor transform_todos() to work on a todo_list\n 5:  67ebea475e =  5:  67ebea475e sequencer: introduce todo_list_write_to_file()\n 6:  370c153ebe =  6:  370c153ebe sequencer: refactor check_todo_list() to work on a todo_list\n 7:  66e7b65509 !  7:  95ae0f36d2 sequencer: refactor sequencer_add_exec_commands() to work on a todo_list\n    @@ -60,7 +60,10 @@\n      \n     +\tif (cmd && *cmd) {\n     +\t\tstring_list_split(&commands, cmd, '\\n', -1);\n    -+\t\t--commands.nr;\n    ++\n    ++\t\t/* rebase.c adds a new line to cmd after every command,\n    ++\t\t * so here the last command is always empty */\n    ++\t\tstring_list_remove_empty_items(&commands, 0);\n     +\t}\n     +\n      \tswitch (command) {\n    @@ -86,7 +89,7 @@\n      \t\tBUG(\"invalid command '%d'\", command);\n      \t}\n      \n    -+\tstring_list_clear(&commands, 1);\n    ++\tstring_list_clear(&commands, 0);\n      \treturn !!ret;\n      }\n     \n    @@ -133,9 +136,16 @@\n      \t}\n      \n      \t/*\n    -@@\n    + \t * Insert <commands> after every pick. Here, fixup/squash chains\n      \t * are considered part of the pick, so we insert the commands *after*\n      \t * those chains if there are any.\n    ++\t *\n    ++\t * As we insert the exec commands immediatly after rearranging\n    ++\t * any fixups and before the user edits the list, a fixup chain\n    ++\t * can never contain comments (any comments are empty picks that\n    ++\t * have been commented out because the user did not specify\n    ++\t * --keep-empty).  So, it is safe to insert an exec command\n    ++\t * without looking at the command following a comment.\n      \t */\n     -\tinsert = -1;\n     -\tfor (i = 0; i < todo_list.nr; i++) {\n    @@ -165,12 +175,11 @@\n     +\t\t\tinsert = 0;\n      \t\t}\n      \n    --\t\tif (command == TODO_PICK || command == TODO_MERGE)\n    --\t\t\tinsert = i + 1;\n     +\t\tALLOC_GROW(items, nr + 1, alloc);\n     +\t\titems[nr++] = todo_list->items[i];\n     +\n    -+\t\tif (command == TODO_PICK || command == TODO_MERGE || is_fixup(command))\n    + \t\tif (command == TODO_PICK || command == TODO_MERGE)\n    +-\t\t\tinsert = i + 1;\n     +\t\t\tinsert = 1;\n      \t}\n      \n    @@ -192,8 +201,7 @@\n     +\ttodo_list->nr = nr;\n     +\ttodo_list->alloc = alloc;\n     +}\n    - \n    --\ti = write_message(buf->buf, buf->len, todo_file, 0);\n    ++\n     +int sequencer_add_exec_commands(struct repository *r,\n     +\t\t\t\tstruct string_list *commands)\n     +{\n    @@ -203,7 +211,8 @@\n     +\n     +\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n     +\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n    -+\n    + \n    +-\ti = write_message(buf->buf, buf->len, todo_file, 0);\n     +\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n     +\t\ttodo_list_release(&todo_list);\n     +\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n 8:  640cb7aa54 =  8:  144eb32743 sequencer: refactor rearrange_squash() to work on a todo_list\n 9:  bef1970c88 =  9:  1501c26317 sequencer: make sequencer_make_script() write its script to a strbuf\n10:  48ee37a32a = 10:  121b270c4d sequencer: change complete_action() to use the refactored functions\n16:  f57a7405d0 ! 11:  a3763ecb0e rebase--interactive: move several functions to rebase--interactive.c\n    @@ -1,13 +1,12 @@\n     Author: Alban Gruin <alban.gruin@gmail.com>\n     \n    -    rebase--interactive: move several functions to rebase--interactive.c\n    +    rebase--interactive: move sequencer_add_exec_commands()\n     \n    -    As sequencer_add_exec_commands(), rearrange_squash_in_todo_file(), and\n    -    transform_todo_file() are only needed inside of rebase--interactive.c\n    -    for rebase -p, they are moved there from sequencer.c.\n    +    As sequencer_add_exec_commands() is only needed inside of\n    +    rebase--interactive.c for `rebase -p', it is moved there from\n    +    sequencer.c.\n     \n    -    The parameter r (repository) is dropped from them, and the error\n    -    handling of rearrange_squash_in_todo_file() is slightly improved.\n    +    The parameter r (repository) is dropped along the way.\n     \n         Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n     \n    @@ -43,72 +42,11 @@\n     +\treturn 0;\n     +}\n     +\n    -+static int rearrange_squash_in_todo_file(void)\n    -+{\n    -+\tconst char *todo_file = rebase_path_todo();\n    -+\tstruct todo_list todo_list = TODO_LIST_INIT;\n    -+\tint res = 0;\n    -+\n    -+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n    -+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n    -+\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n    -+\t\t\t\t\t&todo_list)) {\n    -+\t\ttodo_list_release(&todo_list);\n    -+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n    -+\t}\n    -+\n    -+\tres = todo_list_rearrange_squash(&todo_list);\n    -+\tif (!res)\n    -+\t\tres = todo_list_write_to_file(the_repository, &todo_list,\n    -+\t\t\t\t\t      todo_file, NULL, NULL, -1, 0);\n    -+\n    -+\ttodo_list_release(&todo_list);\n    -+\n    -+\tif (res)\n    -+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n    -+\treturn 0;\n    -+}\n    -+\n    -+static int transform_todo_file(unsigned flags)\n    -+{\n    -+\tconst char *todo_file = rebase_path_todo();\n    -+\tstruct todo_list todo_list = TODO_LIST_INIT;\n    -+\tint res;\n    -+\n    -+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n    -+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n    -+\n    -+\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n    -+\t\t\t\t\t&todo_list)) {\n    -+\t\ttodo_list_release(&todo_list);\n    -+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n    -+\t}\n    -+\n    -+\tres = todo_list_write_to_file(the_repository, &todo_list, todo_file,\n    -+\t\t\t\t      NULL, NULL, -1, flags);\n    -+\ttodo_list_release(&todo_list);\n    -+\n    -+\tif (res)\n    -+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n    -+\treturn 0;\n    -+}\n    -+\n    - static int edit_todo_file(unsigned flags)\n    - {\n    - \tconst char *todo_file = rebase_path_todo();\n    + static int get_revision_ranges(const char *upstream, const char *onto,\n    + \t\t\t       const char **head_hash,\n    + \t\t\t       char **revisions, char **shortrevisions)\n     @@\n    - \t}\n    - \tcase SHORTEN_OIDS:\n    - \tcase EXPAND_OIDS:\n    --\t\tret = transform_todo_file(the_repository, flags);\n    -+\t\tret = transform_todo_file(flags);\n    - \t\tbreak;\n    - \tcase CHECK_TODO_LIST:\n    - \t\tret = check_todo_list_from_file(the_repository);\n    - \t\tbreak;\n    - \tcase REARRANGE_SQUASH:\n    --\t\tret = rearrange_squash_in_todo_file(the_repository);\n    -+\t\tret = rearrange_squash_in_todo_file();\n    + \t\tret = rearrange_squash_in_todo_file(the_repository);\n      \t\tbreak;\n      \tcase ADD_EXEC:\n     -\t\tret = sequencer_add_exec_commands(the_repository, &commands);\n    @@ -162,82 +100,6 @@\n      static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n      \t\t\t\tstruct strbuf *buf, int num, unsigned flags)\n      {\n    -@@\n    - \treturn res;\n    - }\n    - \n    --int transform_todo_file(struct repository *r, unsigned flags)\n    --{\n    --\tconst char *todo_file = rebase_path_todo();\n    --\tstruct todo_list todo_list = TODO_LIST_INIT;\n    --\tint res;\n    --\n    --\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n    --\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n    --\n    --\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n    --\t\ttodo_list_release(&todo_list);\n    --\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n    --\t}\n    --\n    --\tres = todo_list_write_to_file(r, &todo_list, todo_file,\n    --\t\t\t\t      NULL, NULL, -1, flags);\n    --\ttodo_list_release(&todo_list);\n    --\n    --\tif (res)\n    --\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n    --\treturn 0;\n    --}\n    --\n    - static const char edit_todo_list_advice[] =\n    - N_(\"You can fix this with 'git rebase --edit-todo' \"\n    - \"and then run 'git rebase --continue'.\\n\"\n    -@@\n    - \treturn 0;\n    - }\n    - \n    --static int todo_list_rearrange_squash(struct todo_list *todo_list);\n    --\n    - int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n    - \t\t    const char *shortrevisions, const char *onto_name,\n    - \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n    -@@\n    -  * message will have to be retrieved from the commit (as the oneline in the\n    -  * script cannot be trusted) in order to normalize the autosquash arrangement.\n    -  */\n    --static int todo_list_rearrange_squash(struct todo_list *todo_list)\n    -+int todo_list_rearrange_squash(struct todo_list *todo_list)\n    - {\n    - \tstruct hashmap subject2item;\n    - \tint rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;\n    -@@\n    - \n    - \treturn 0;\n    - }\n    --\n    --int rearrange_squash_in_todo_file(struct repository *r)\n    --{\n    --\tconst char *todo_file = rebase_path_todo();\n    --\tstruct todo_list todo_list = TODO_LIST_INIT;\n    --\tint res = 0;\n    --\n    --\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n    --\t\treturn -1;\n    --\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n    --\t\ttodo_list_release(&todo_list);\n    --\t\treturn -1;\n    --\t}\n    --\n    --\tres = todo_list_rearrange_squash(&todo_list);\n    --\tif (!res)\n    --\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n    --\n    --\ttodo_list_release(&todo_list);\n    --\n    --\tif (res)\n    --\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n    --\treturn 0;\n    --}\n     \n      diff --git a/sequencer.h b/sequencer.h\n      --- a/sequencer.h\n    @@ -248,16 +110,9 @@\n      \n     -int sequencer_add_exec_commands(struct repository *r,\n     -\t\t\t\tstruct string_list *commands);\n    --int transform_todo_file(struct repository *r, unsigned flags);\n    + int transform_todo_file(struct repository *r, unsigned flags);\n     +void todo_list_add_exec_commands(struct todo_list *todo_list,\n     +\t\t\t\t struct string_list *commands);\n      int check_todo_list_from_file(struct repository *r);\n      int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n      \t\t    const char *shortrevisions, const char *onto_name,\n    - \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n    - \t\t    unsigned autosquash, struct todo_list *todo_list);\n    --int rearrange_squash_in_todo_file(struct repository *r);\n    -+int todo_list_rearrange_squash(struct todo_list *todo_list);\n    - \n    - extern const char sign_off_header[];\n    - \n -:  ---------- > 12:  e03663ed04 rebase--interactive: move rearrange_squash_in_todo_file()\n11:  bc89fbfea6 = 13:  d9cf3d7d2f sequencer: refactor skip_unnecessary_picks() to work on a todo_list\n12:  a754d0da96 = 14:  bf6cc5978c rebase-interactive: use todo_list_write_to_file() in edit_todo_list()\n13:  031e4de856 = 15:  1c9d1cb3c9 rebase-interactive: append_todo_help() changes\n14:  268998924b ! 16:  b98b07dae0 rebase-interactive: rewrite edit_todo_list() to handle the initial edit\n    @@ -16,8 +16,8 @@\n      --- a/builtin/rebase--interactive.c\n      +++ b/builtin/rebase--interactive.c\n     @@\n    - static GIT_PATH_FUNC(path_squash_onto, \"rebase-merge/squash-onto\")\n    - static GIT_PATH_FUNC(path_interactive, \"rebase-merge/interactive\")\n    + \treturn 0;\n    + }\n      \n     +static int edit_todo_file(unsigned flags)\n     +{\n    @@ -87,6 +87,9 @@\n     -\t\ttodo_list_release(&todo_list);\n     -\t\treturn -1;\n     -\t}\n    ++\t/* If the user is editing the todo list, we first try to parse\n    ++\t * it.  If there is an error, we do not return, because the user\n    ++\t * might want to fix it in the first place. */\n     +\tif (!initial)\n     +\t\ttodo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n      \n    @@ -110,6 +113,8 @@\n     +\tif (initial && new_todo->buf.len == 0)\n     +\t\treturn -3;\n     +\n    ++\t/* For the initial edit, the todo list gets parsed in\n    ++\t * complete_action(). */\n     +\tif (!initial)\n     +\t\treturn todo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo);\n     +\n15:  6f1274a33c = 17:  406e971e56 sequencer: use edit_todo_list() in complete_action()\n -:  ---------- > 18:  51d44a8200 rebase--interactive: move transform_todo_file()\n-- \n2.20.1\n\n"},{"id":"370730","messageId":"20190305191805.13561-2-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 01/18] sequencer: changes in parse_insn_buffer()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:17:48Z","receivedAt":"2019-03-05T19:18:30Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This clears the number of items of a todo_list before parsing it to\nallow to parse the same list multiple times without issues.  As its\nitems are not dynamically allocated, or don’t need to allocate memory,\nno additionnal memory management is required here.\n\nFurthermore, if a line is invalid, the type of the corresponding\ncommand is set to a garbage value, and its argument is defined properly.\nThis will allow to recreate the text of a todo list from its commands,\neven if one of them is incorrect.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v7.\n\n sequencer.c | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex b68bca0bef..d605199a54 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2141,6 +2141,8 @@ static int parse_insn_buffer(struct repository *r, char *buf,\n \tchar *p = buf, *next_p;\n \tint i, res = 0, fixup_okay = file_exists(rebase_path_done());\n \n+\ttodo_list->current = todo_list->nr = 0;\n+\n \tfor (i = 1; *p; i++, p = next_p) {\n \t\tchar *eol = strchrnul(p, '\\n');\n \n@@ -2154,7 +2156,10 @@ static int parse_insn_buffer(struct repository *r, char *buf,\n \t\tif (parse_insn_line(r, item, p, eol)) {\n \t\t\tres = error(_(\"invalid line %d: %.*s\"),\n \t\t\t\ti, (int)(eol - p), p);\n-\t\t\titem->command = TODO_NOOP;\n+\t\t\titem->command = TODO_COMMENT + 1;\n+\t\t\titem->arg = p;\n+\t\t\titem->arg_len = (int)(eol - p);\n+\t\t\titem->commit = NULL;\n \t\t}\n \n \t\tif (fixup_okay)\n-- \n2.20.1\n\n"},{"id":"370731","messageId":"20190305191805.13561-3-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 02/18] sequencer: make the todo_list structure public","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:17:49Z","receivedAt":"2019-03-05T19:18:30Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This makes the structures todo_list and todo_item, and the functions\ntodo_list_release() and parse_insn_buffer(), accessible outside of\nsequencer.c.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v7.\n\n sequencer.c | 69 ++++++++++-------------------------------------------\n sequencer.h | 50 ++++++++++++++++++++++++++++++++++++++\n 2 files changed, 62 insertions(+), 57 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex d605199a54..25cc7a9a91 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1510,32 +1510,6 @@ static int allow_empty(struct repository *r,\n \t\treturn 1;\n }\n \n-/*\n- * Note that ordering matters in this enum. Not only must it match the mapping\n- * below, it is also divided into several sections that matter.  When adding\n- * new commands, make sure you add it in the right section.\n- */\n-enum todo_command {\n-\t/* commands that handle commits */\n-\tTODO_PICK = 0,\n-\tTODO_REVERT,\n-\tTODO_EDIT,\n-\tTODO_REWORD,\n-\tTODO_FIXUP,\n-\tTODO_SQUASH,\n-\t/* commands that do something else than handling a single commit */\n-\tTODO_EXEC,\n-\tTODO_BREAK,\n-\tTODO_LABEL,\n-\tTODO_RESET,\n-\tTODO_MERGE,\n-\t/* commands that do nothing but are counted for reporting progress */\n-\tTODO_NOOP,\n-\tTODO_DROP,\n-\t/* comments (not counted for reporting progress) */\n-\tTODO_COMMENT\n-};\n-\n static struct {\n \tchar c;\n \tconst char *str;\n@@ -2012,26 +1986,7 @@ enum todo_item_flags {\n \tTODO_EDIT_MERGE_MSG = 1\n };\n \n-struct todo_item {\n-\tenum todo_command command;\n-\tstruct commit *commit;\n-\tunsigned int flags;\n-\tconst char *arg;\n-\tint arg_len;\n-\tsize_t offset_in_buf;\n-};\n-\n-struct todo_list {\n-\tstruct strbuf buf;\n-\tstruct todo_item *items;\n-\tint nr, alloc, current;\n-\tint done_nr, total_nr;\n-\tstruct stat_data stat;\n-};\n-\n-#define TODO_LIST_INIT { STRBUF_INIT }\n-\n-static void todo_list_release(struct todo_list *todo_list)\n+void todo_list_release(struct todo_list *todo_list)\n {\n \tstrbuf_release(&todo_list->buf);\n \tFREE_AND_NULL(todo_list->items);\n@@ -2134,8 +2089,8 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \treturn !item->commit;\n }\n \n-static int parse_insn_buffer(struct repository *r, char *buf,\n-\t\t\t     struct todo_list *todo_list)\n+int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n+\t\t\t\tstruct todo_list *todo_list)\n {\n \tstruct todo_item *item;\n \tchar *p = buf, *next_p;\n@@ -2234,7 +2189,7 @@ static int read_populate_todo(struct repository *r,\n \t\treturn error(_(\"could not stat '%s'\"), todo_file);\n \tfill_stat_data(&todo_list->stat, &st);\n \n-\tres = parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n+\tres = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n \tif (res) {\n \t\tif (is_rebase_i(opts))\n \t\t\treturn error(_(\"please fix this using \"\n@@ -2265,7 +2220,7 @@ static int read_populate_todo(struct repository *r,\n \t\tFILE *f = fopen_or_warn(rebase_path_msgtotal(), \"w\");\n \n \t\tif (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&\n-\t\t    !parse_insn_buffer(r, done.buf.buf, &done))\n+\t\t    !todo_list_parse_insn_buffer(r, done.buf.buf, &done))\n \t\t\ttodo_list->done_nr = count_commands(&done);\n \t\telse\n \t\t\ttodo_list->done_nr = 0;\n@@ -4556,7 +4511,7 @@ int sequencer_add_exec_commands(struct repository *r,\n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4612,7 +4567,7 @@ int transform_todos(struct repository *r, unsigned flags)\n \tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4698,7 +4653,7 @@ int check_todo_list(struct repository *r)\n \t\tgoto leave_check;\n \t}\n \tadvise_to_edit_todo = res =\n-\t\tparse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n+\t\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n \n \tif (res || check_level == MISSING_COMMIT_CHECK_IGNORE)\n \t\tgoto leave_check;\n@@ -4717,7 +4672,7 @@ int check_todo_list(struct repository *r)\n \t\tgoto leave_check;\n \t}\n \tstrbuf_release(&todo_file);\n-\tres = !!parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n+\tres = !!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n \n \t/* Find commits in git-rebase-todo.backup yet unseen */\n \tfor (i = todo_list.nr - 1; i >= 0; i--) {\n@@ -4799,7 +4754,7 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \n \tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n \t\treturn -1;\n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n@@ -4887,7 +4842,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n-\tif (parse_insn_buffer(r, buf->buf, &todo_list)) {\n+\tif (todo_list_parse_insn_buffer(r, buf->buf, &todo_list)) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n@@ -4995,7 +4950,7 @@ int rearrange_squash(struct repository *r)\n \n \tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n \t\treturn -1;\n-\tif (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n \t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex 9d83f0f3e9..c6360bac66 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -73,6 +73,56 @@ enum missing_commit_check_level {\n int write_message(const void *buf, size_t len, const char *filename,\n \t\t  int append_eol);\n \n+/*\n+ * Note that ordering matters in this enum. Not only must it match the mapping\n+ * of todo_command_info (in sequencer.c), it is also divided into several\n+ * sections that matter.  When adding new commands, make sure you add it in the\n+ * right section.\n+ */\n+enum todo_command {\n+\t/* commands that handle commits */\n+\tTODO_PICK = 0,\n+\tTODO_REVERT,\n+\tTODO_EDIT,\n+\tTODO_REWORD,\n+\tTODO_FIXUP,\n+\tTODO_SQUASH,\n+\t/* commands that do something else than handling a single commit */\n+\tTODO_EXEC,\n+\tTODO_BREAK,\n+\tTODO_LABEL,\n+\tTODO_RESET,\n+\tTODO_MERGE,\n+\t/* commands that do nothing but are counted for reporting progress */\n+\tTODO_NOOP,\n+\tTODO_DROP,\n+\t/* comments (not counted for reporting progress) */\n+\tTODO_COMMENT\n+};\n+\n+struct todo_item {\n+\tenum todo_command command;\n+\tstruct commit *commit;\n+\tunsigned int flags;\n+\tconst char *arg;\n+\tint arg_len;\n+\tsize_t offset_in_buf;\n+};\n+\n+struct todo_list {\n+\tstruct strbuf buf;\n+\tstruct todo_item *items;\n+\tint nr, alloc, current;\n+\tint done_nr, total_nr;\n+\tstruct stat_data stat;\n+};\n+\n+#define TODO_LIST_INIT { STRBUF_INIT }\n+\n+int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n+\t\t\t\tstruct todo_list *todo_list);\n+void todo_list_release(struct todo_list *todo_list);\n+\n /* Call this to setup defaults before parsing command line options */\n void sequencer_init_config(struct replay_opts *opts);\n int sequencer_pick_revisions(struct repository *repo,\n-- \n2.20.1\n\n"},{"id":"370732","messageId":"20190305191805.13561-4-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 03/18] sequencer: remove the 'arg' field from todo_item","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:17:50Z","receivedAt":"2019-03-05T19:18:33Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"The 'arg' field of todo_item used to store the address of the first byte\nof the parameter of a command in a todo list.  It was associated with\nthe length of the parameter (the 'arg_len' field).\n\nThis replaces the 'arg' field by 'arg_offset'.  This new field does not\nstore the address of the parameter, but the position of the first\ncharacter of the parameter in the buffer.  todo_item_get_arg() is added\nto return the address of the parameter of an item.\n\nThis will prevent todo_list_add_exec_commands() from having to do awful\npointer arithmetics when growing the todo list buffer.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v7.\n\n sequencer.c | 67 ++++++++++++++++++++++++++++++-----------------------\n sequencer.h |  6 +++--\n 2 files changed, 42 insertions(+), 31 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 25cc7a9a91..c844a9b7f3 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1999,8 +1999,14 @@ static struct todo_item *append_new_todo(struct todo_list *todo_list)\n \treturn todo_list->items + todo_list->nr++;\n }\n \n+const char *todo_item_get_arg(struct todo_list *todo_list,\n+\t\t\t      struct todo_item *item)\n+{\n+\treturn todo_list->buf.buf + item->arg_offset;\n+}\n+\n static int parse_insn_line(struct repository *r, struct todo_item *item,\n-\t\t\t   const char *bol, char *eol)\n+\t\t\t   const char *buf, const char *bol, char *eol)\n {\n \tstruct object_id commit_oid;\n \tchar *end_of_object_name;\n@@ -2014,7 +2020,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \tif (bol == eol || *bol == '\\r' || *bol == comment_line_char) {\n \t\titem->command = TODO_COMMENT;\n \t\titem->commit = NULL;\n-\t\titem->arg = bol;\n+\t\titem->arg_offset = bol - buf;\n \t\titem->arg_len = eol - bol;\n \t\treturn 0;\n \t}\n@@ -2041,7 +2047,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \t\t\treturn error(_(\"%s does not accept arguments: '%s'\"),\n \t\t\t\t     command_to_string(item->command), bol);\n \t\titem->commit = NULL;\n-\t\titem->arg = bol;\n+\t\titem->arg_offset = bol - buf;\n \t\titem->arg_len = eol - bol;\n \t\treturn 0;\n \t}\n@@ -2053,7 +2059,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \tif (item->command == TODO_EXEC || item->command == TODO_LABEL ||\n \t    item->command == TODO_RESET) {\n \t\titem->commit = NULL;\n-\t\titem->arg = bol;\n+\t\titem->arg_offset = bol - buf;\n \t\titem->arg_len = (int)(eol - bol);\n \t\treturn 0;\n \t}\n@@ -2067,7 +2073,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \t\t} else {\n \t\t\titem->flags |= TODO_EDIT_MERGE_MSG;\n \t\t\titem->commit = NULL;\n-\t\t\titem->arg = bol;\n+\t\t\titem->arg_offset = bol - buf;\n \t\t\titem->arg_len = (int)(eol - bol);\n \t\t\treturn 0;\n \t\t}\n@@ -2079,8 +2085,9 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,\n \tstatus = get_oid(bol, &commit_oid);\n \t*end_of_object_name = saved;\n \n-\titem->arg = end_of_object_name + strspn(end_of_object_name, \" \\t\");\n-\titem->arg_len = (int)(eol - item->arg);\n+\tbol = end_of_object_name + strspn(end_of_object_name, \" \\t\");\n+\titem->arg_offset = bol - buf;\n+\titem->arg_len = (int)(eol - bol);\n \n \tif (status < 0)\n \t\treturn -1;\n@@ -2108,11 +2115,11 @@ int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \n \t\titem = append_new_todo(todo_list);\n \t\titem->offset_in_buf = p - todo_list->buf.buf;\n-\t\tif (parse_insn_line(r, item, p, eol)) {\n+\t\tif (parse_insn_line(r, item, buf, p, eol)) {\n \t\t\tres = error(_(\"invalid line %d: %.*s\"),\n \t\t\t\ti, (int)(eol - p), p);\n \t\t\titem->command = TODO_COMMENT + 1;\n-\t\t\titem->arg = p;\n+\t\t\titem->arg_offset = p - buf;\n \t\t\titem->arg_len = (int)(eol - p);\n \t\t\titem->commit = NULL;\n \t\t}\n@@ -2452,7 +2459,7 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,\n \n \t\titem->command = command;\n \t\titem->commit = commit;\n-\t\titem->arg = NULL;\n+\t\titem->arg_offset = 0;\n \t\titem->arg_len = 0;\n \t\titem->offset_in_buf = todo_list->buf.len;\n \t\tsubject_len = find_commit_subject(commit_buffer, &subject);\n@@ -3491,6 +3498,8 @@ static int pick_commits(struct repository *r,\n \n \twhile (todo_list->current < todo_list->nr) {\n \t\tstruct todo_item *item = todo_list->items + todo_list->current;\n+\t\tconst char *arg = todo_item_get_arg(todo_list, item);\n+\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n \t\tif (is_rebase_i(opts)) {\n@@ -3542,10 +3551,9 @@ static int pick_commits(struct repository *r,\n \t\t\t\t\tfprintf(stderr,\n \t\t\t\t\t\t_(\"Stopped at %s...  %.*s\\n\"),\n \t\t\t\t\t\tshort_commit_name(commit),\n-\t\t\t\t\t\titem->arg_len, item->arg);\n+\t\t\t\t\t\titem->arg_len, arg);\n \t\t\t\treturn error_with_patch(r, commit,\n-\t\t\t\t\titem->arg, item->arg_len, opts, res,\n-\t\t\t\t\t!res);\n+\t\t\t\t\targ, item->arg_len, opts, res, !res);\n \t\t\t}\n \t\t\tif (is_rebase_i(opts) && !res)\n \t\t\t\trecord_in_rewritten(&item->commit->object.oid,\n@@ -3554,7 +3562,7 @@ static int pick_commits(struct repository *r,\n \t\t\t\tif (res == 1)\n \t\t\t\t\tintend_to_amend();\n \t\t\t\treturn error_failed_squash(r, item->commit, opts,\n-\t\t\t\t\titem->arg_len, item->arg);\n+\t\t\t\t\titem->arg_len, arg);\n \t\t\t} else if (res && is_rebase_i(opts) && item->commit) {\n \t\t\t\tint to_amend = 0;\n \t\t\t\tstruct object_id oid;\n@@ -3573,16 +3581,16 @@ static int pick_commits(struct repository *r,\n \t\t\t\t\tto_amend = 1;\n \n \t\t\t\treturn res | error_with_patch(r, item->commit,\n-\t\t\t\t\t\titem->arg, item->arg_len, opts,\n+\t\t\t\t\t\targ, item->arg_len, opts,\n \t\t\t\t\t\tres, to_amend);\n \t\t\t}\n \t\t} else if (item->command == TODO_EXEC) {\n-\t\t\tchar *end_of_arg = (char *)(item->arg + item->arg_len);\n+\t\t\tchar *end_of_arg = (char *)(arg + item->arg_len);\n \t\t\tint saved = *end_of_arg;\n \t\t\tstruct stat st;\n \n \t\t\t*end_of_arg = '\\0';\n-\t\t\tres = do_exec(r, item->arg);\n+\t\t\tres = do_exec(r, arg);\n \t\t\t*end_of_arg = saved;\n \n \t\t\t/* Reread the todo file if it has changed. */\n@@ -3599,14 +3607,14 @@ static int pick_commits(struct repository *r,\n \t\t\t\ttodo_list->current = -1;\n \t\t\t}\n \t\t} else if (item->command == TODO_LABEL) {\n-\t\t\tif ((res = do_label(r, item->arg, item->arg_len)))\n+\t\t\tif ((res = do_label(r, arg, item->arg_len)))\n \t\t\t\treschedule = 1;\n \t\t} else if (item->command == TODO_RESET) {\n-\t\t\tif ((res = do_reset(r, item->arg, item->arg_len, opts)))\n+\t\t\tif ((res = do_reset(r, arg, item->arg_len, opts)))\n \t\t\t\treschedule = 1;\n \t\t} else if (item->command == TODO_MERGE) {\n \t\t\tif ((res = do_merge(r, item->commit,\n-\t\t\t\t\t    item->arg, item->arg_len,\n+\t\t\t\t\t    arg, item->arg_len,\n \t\t\t\t\t    item->flags, opts)) < 0)\n \t\t\t\treschedule = 1;\n \t\t\telse if (item->commit)\n@@ -3615,9 +3623,8 @@ static int pick_commits(struct repository *r,\n \t\t\tif (res > 0)\n \t\t\t\t/* failed with merge conflicts */\n \t\t\t\treturn error_with_patch(r, item->commit,\n-\t\t\t\t\t\t\titem->arg,\n-\t\t\t\t\t\t\titem->arg_len, opts,\n-\t\t\t\t\t\t\tres, 0);\n+\t\t\t\t\t\t\targ, item->arg_len,\n+\t\t\t\t\t\t\topts, res, 0);\n \t\t} else if (!is_noop(item->command))\n \t\t\treturn error(_(\"unknown command %d\"), item->command);\n \n@@ -3632,9 +3639,8 @@ static int pick_commits(struct repository *r,\n \t\t\tif (item->commit)\n \t\t\t\treturn error_with_patch(r,\n \t\t\t\t\t\t\titem->commit,\n-\t\t\t\t\t\t\titem->arg,\n-\t\t\t\t\t\t\titem->arg_len, opts,\n-\t\t\t\t\t\t\tres, 0);\n+\t\t\t\t\t\t\targ, item->arg_len,\n+\t\t\t\t\t\t\topts, res, 0);\n \t\t}\n \n \t\ttodo_list->current++;\n@@ -4575,7 +4581,8 @@ int transform_todos(struct repository *r, unsigned flags)\n \tfor (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n-\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len, item->arg);\n+\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len,\n+\t\t\t\t    todo_item_get_arg(&todo_list, item));\n \t\t\tcontinue;\n \t\t}\n \n@@ -4605,7 +4612,8 @@ int transform_todos(struct repository *r, unsigned flags)\n \t\tif (!item->arg_len)\n \t\t\tstrbuf_addch(&buf, '\\n');\n \t\telse\n-\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len, item->arg);\n+\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len,\n+\t\t\t\t    todo_item_get_arg(&todo_list, item));\n \t}\n \n \ti = write_message(buf.buf, buf.len, todo_file, 0);\n@@ -4681,7 +4689,8 @@ int check_todo_list(struct repository *r)\n \t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n \t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n \t\t\t\t    short_commit_name(commit),\n-\t\t\t\t    item->arg_len, item->arg);\n+\t\t\t\t    item->arg_len,\n+\t\t\t\t    todo_item_get_arg(&todo_list, item));\n \t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n \t\t}\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex c6360bac66..50d552429c 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -104,9 +104,9 @@ struct todo_item {\n \tenum todo_command command;\n \tstruct commit *commit;\n \tunsigned int flags;\n-\tconst char *arg;\n \tint arg_len;\n-\tsize_t offset_in_buf;\n+\t/* The offset of the command and its argument in the strbuf */\n+\tsize_t offset_in_buf, arg_offset;\n };\n \n struct todo_list {\n@@ -122,6 +122,8 @@ struct todo_list {\n int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \t\t\t\tstruct todo_list *todo_list);\n void todo_list_release(struct todo_list *todo_list);\n+const char *todo_item_get_arg(struct todo_list *todo_list,\n+\t\t\t      struct todo_item *item);\n \n /* Call this to setup defaults before parsing command line options */\n void sequencer_init_config(struct replay_opts *opts);\n-- \n2.20.1\n\n"},{"id":"370733","messageId":"20190305191805.13561-5-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 04/18] sequencer: refactor transform_todos() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:17:51Z","receivedAt":"2019-03-05T19:18:34Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors transform_todos() to work on a todo_list.  The function\nis renamed todo_list_transform().\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, transform_todo_file().  It is still used by\ncomplete_action() and edit_todo_list() for now, but they will be\nreplaced in a future commit.\n\ntodo_list_transform() is not a static function, because it will be used\nby edit_todo_list() from rebase-interactive.c in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v7.\n\n builtin/rebase--interactive.c |  2 +-\n rebase-interactive.c          |  4 +--\n sequencer.c                   | 54 +++++++++++++++++++++++------------\n sequencer.h                   |  4 ++-\n 4 files changed, 42 insertions(+), 22 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex dd2a55ab1d..0898eb4c59 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -253,7 +253,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t}\n \tcase SHORTEN_OIDS:\n \tcase EXPAND_OIDS:\n-\t\tret = transform_todos(the_repository, flags);\n+\t\tret = transform_todo_file(the_repository, flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n \t\tret = check_todo_list(the_repository);\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 68aff1dac2..842fa07e7e 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -69,7 +69,7 @@ int edit_todo_list(struct repository *r, unsigned flags)\n \n \tstrbuf_release(&buf);\n \n-\ttransform_todos(r, flags | TODO_LIST_SHORTEN_IDS);\n+\ttransform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS);\n \n \tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n@@ -85,7 +85,7 @@ int edit_todo_list(struct repository *r, unsigned flags)\n \tif (launch_sequence_editor(todo_file, NULL, NULL))\n \t\treturn -1;\n \n-\ttransform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS));\n+\ttransform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS));\n \n \treturn 0;\n }\ndiff --git a/sequencer.c b/sequencer.c\nindex c844a9b7f3..346706029c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4562,27 +4562,18 @@ int sequencer_add_exec_commands(struct repository *r,\n \treturn i;\n }\n \n-int transform_todos(struct repository *r, unsigned flags)\n+void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n+\t\t\t unsigned flags)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct todo_item *item;\n \tint i;\n \n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tfor (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {\n+\tfor (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n \t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len,\n-\t\t\t\t    todo_item_get_arg(&todo_list, item));\n+\t\t\t\t    todo_item_get_arg(todo_list, item));\n \t\t\tcontinue;\n \t\t}\n \n@@ -4613,12 +4604,39 @@ int transform_todos(struct repository *r, unsigned flags)\n \t\t\tstrbuf_addch(&buf, '\\n');\n \t\telse\n \t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len,\n-\t\t\t\t    todo_item_get_arg(&todo_list, item));\n+\t\t\t\t    todo_item_get_arg(todo_list, item));\n \t}\n \n-\ti = write_message(buf.buf, buf.len, todo_file, 0);\n+\tstrbuf_reset(&todo_list->buf);\n+\tstrbuf_add(&todo_list->buf, buf.buf, buf.len);\n+\tstrbuf_release(&buf);\n+\n+\tif (todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list))\n+\t\tBUG(\"unusable todo list\");\n+}\n+\n+int transform_todo_file(struct repository *r, unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_transform(r, &todo_list, flags);\n+\n+\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n \ttodo_list_release(&todo_list);\n-\treturn i;\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n }\n \n enum missing_commit_check_level get_missing_commit_check_level(void)\n@@ -4880,7 +4898,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n \t\t\t     rebase_path_todo_backup());\n \n-\tif (transform_todos(r, flags | TODO_LIST_SHORTEN_IDS))\n+\tif (transform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS))\n \t\treturn error(_(\"could not transform the todo list\"));\n \n \tstrbuf_reset(buf);\n@@ -4909,7 +4927,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn -1;\n \t}\n \n-\tif (transform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS)))\n+\tif (transform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS)))\n \t\treturn error(_(\"could not transform the todo list\"));\n \n \tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\ndiff --git a/sequencer.h b/sequencer.h\nindex 50d552429c..2ddb20cbc1 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -121,6 +121,8 @@ struct todo_list {\n \n int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \t\t\t\tstruct todo_list *todo_list);\n+void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n+\t\t\t unsigned flags);\n void todo_list_release(struct todo_list *todo_list);\n const char *todo_item_get_arg(struct todo_list *todo_list,\n \t\t\t      struct todo_item *item);\n@@ -148,7 +150,7 @@ int sequencer_make_script(struct repository *repo, FILE *out,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(struct repository *r, const char *command);\n-int transform_todos(struct repository *r, unsigned flags);\n+int transform_todo_file(struct repository *r, unsigned flags);\n enum missing_commit_check_level get_missing_commit_check_level(void);\n int check_todo_list(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n-- \n2.20.1\n\n"},{"id":"370734","messageId":"20190305191805.13561-6-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 05/18] sequencer: introduce todo_list_write_to_file()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:17:52Z","receivedAt":"2019-03-05T19:18:35Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This introduces a new function to recreate the text of a todo list from\nits commands and write it to a file.  This will be useful as the next\nfew commits will change the use of the buffer in struct todo_list so it\nwill no longer be a mirror of the file on disk.\n\nThis functionality already exists in todo_list_transform(), but this\nfunction was made to replace the buffer of a todo list, which is not\nwhat we want here.  Thus, the part of todo_list_transform() that\nreplaces the buffer is dropped, and the function is renamed\ntodo_list_to_strbuf().  It is called by todo_list_write_to_file() to\nfill the buffer to write to the disk.\n\ntodo_list_write_to_file() can also take care of appending the help text\nto the buffer before writing it to the disk, or to write only the first\nn items of the list.  This feature will be used by\nskip_unnecessary_picks(), which has to write done commands in a file.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v7.\n\n sequencer.c | 61 +++++++++++++++++++++++++++++++++++------------------\n sequencer.h | 11 ++++++----\n 2 files changed, 48 insertions(+), 24 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 346706029c..4809b22ce4 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4562,26 +4562,28 @@ int sequencer_add_exec_commands(struct repository *r,\n \treturn i;\n }\n \n-void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n-\t\t\t unsigned flags)\n+static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n+\t\t\t\tstruct strbuf *buf, int num, unsigned flags)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n \tstruct todo_item *item;\n-\tint i;\n+\tint i, max = todo_list->nr;\n \n-\tfor (item = todo_list->items, i = 0; i < todo_list->nr; i++, item++) {\n+\tif (num > 0 && num < max)\n+\t\tmax = num;\n+\n+\tfor (item = todo_list->items, i = 0; i < max; i++, item++) {\n \t\t/* if the item is not a command write it and continue */\n \t\tif (item->command >= TODO_COMMENT) {\n-\t\t\tstrbuf_addf(&buf, \"%.*s\\n\", item->arg_len,\n+\t\t\tstrbuf_addf(buf, \"%.*s\\n\", item->arg_len,\n \t\t\t\t    todo_item_get_arg(todo_list, item));\n \t\t\tcontinue;\n \t\t}\n \n \t\t/* add command to the buffer */\n \t\tif (flags & TODO_LIST_ABBREVIATE_CMDS)\n-\t\t\tstrbuf_addch(&buf, command_to_char(item->command));\n+\t\t\tstrbuf_addch(buf, command_to_char(item->command));\n \t\telse\n-\t\t\tstrbuf_addstr(&buf, command_to_string(item->command));\n+\t\t\tstrbuf_addstr(buf, command_to_string(item->command));\n \n \t\t/* add commit id */\n \t\tif (item->commit) {\n@@ -4591,28 +4593,48 @@ void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n \n \t\t\tif (item->command == TODO_MERGE) {\n \t\t\t\tif (item->flags & TODO_EDIT_MERGE_MSG)\n-\t\t\t\t\tstrbuf_addstr(&buf, \" -c\");\n+\t\t\t\t\tstrbuf_addstr(buf, \" -c\");\n \t\t\t\telse\n-\t\t\t\t\tstrbuf_addstr(&buf, \" -C\");\n+\t\t\t\t\tstrbuf_addstr(buf, \" -C\");\n \t\t\t}\n \n-\t\t\tstrbuf_addf(&buf, \" %s\", oid);\n+\t\t\tstrbuf_addf(buf, \" %s\", oid);\n \t\t}\n \n \t\t/* add all the rest */\n \t\tif (!item->arg_len)\n-\t\t\tstrbuf_addch(&buf, '\\n');\n+\t\t\tstrbuf_addch(buf, '\\n');\n \t\telse\n-\t\t\tstrbuf_addf(&buf, \" %.*s\\n\", item->arg_len,\n+\t\t\tstrbuf_addf(buf, \" %.*s\\n\", item->arg_len,\n \t\t\t\t    todo_item_get_arg(todo_list, item));\n \t}\n+}\n \n-\tstrbuf_reset(&todo_list->buf);\n-\tstrbuf_add(&todo_list->buf, buf.buf, buf.len);\n+int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n+\t\t\t    const char *file, const char *shortrevisions,\n+\t\t\t    const char *shortonto, int num, unsigned flags)\n+{\n+\tint edit_todo = !(shortrevisions && shortonto), res;\n+\tstruct strbuf buf = STRBUF_INIT;\n+\n+\ttodo_list_to_strbuf(r, todo_list, &buf, num, flags);\n+\n+\tif (flags & TODO_LIST_APPEND_TODO_HELP) {\n+\t\tint command_count = count_commands(todo_list);\n+\t\tif (!edit_todo) {\n+\t\t\tstrbuf_addch(&buf, '\\n');\n+\t\t\tstrbuf_commented_addf(&buf, Q_(\"Rebase %s onto %s (%d command)\",\n+\t\t\t\t\t\t       \"Rebase %s onto %s (%d commands)\",\n+\t\t\t\t\t\t       command_count),\n+\t\t\t\t\t      shortrevisions, shortonto, command_count);\n+\t\t}\n+\t\tappend_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, &buf);\n+\t}\n+\n+\tres = write_message(buf.buf, buf.len, file, 0);\n \tstrbuf_release(&buf);\n \n-\tif (todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list))\n-\t\tBUG(\"unusable todo list\");\n+\treturn res;\n }\n \n int transform_todo_file(struct repository *r, unsigned flags)\n@@ -4629,9 +4651,8 @@ int transform_todo_file(struct repository *r, unsigned flags)\n \t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n \t}\n \n-\ttodo_list_transform(r, &todo_list, flags);\n-\n-\tres = write_message(todo_list.buf.buf, todo_list.buf.len, todo_file, 0);\n+\tres = todo_list_write_to_file(r, &todo_list, todo_file,\n+\t\t\t\t      NULL, NULL, -1, flags);\n \ttodo_list_release(&todo_list);\n \n \tif (res)\ndiff --git a/sequencer.h b/sequencer.h\nindex 2ddb20cbc1..7278f9675b 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -121,8 +121,9 @@ struct todo_list {\n \n int todo_list_parse_insn_buffer(struct repository *r, char *buf,\n \t\t\t\tstruct todo_list *todo_list);\n-void todo_list_transform(struct repository *r, struct todo_list *todo_list,\n-\t\t\t unsigned flags);\n+int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n+\t\t\t    const char *file, const char *shortrevisions,\n+\t\t\t    const char *shortonto, int num, unsigned flags);\n void todo_list_release(struct todo_list *todo_list);\n const char *todo_item_get_arg(struct todo_list *todo_list,\n \t\t\t      struct todo_item *item);\n@@ -145,8 +146,10 @@ int sequencer_remove_state(struct replay_opts *opts);\n  * commits should be rebased onto the new base, this flag needs to be passed.\n  */\n #define TODO_LIST_REBASE_COUSINS (1U << 4)\n-int sequencer_make_script(struct repository *repo, FILE *out,\n-\t\t\t  int argc, const char **argv,\n+#define TODO_LIST_APPEND_TODO_HELP (1U << 5)\n+\n+int sequencer_make_script(struct repository *r, FILE *out, int argc,\n+\t\t\t  const char **argv,\n \t\t\t  unsigned flags);\n \n int sequencer_add_exec_commands(struct repository *r, const char *command);\n-- \n2.20.1\n\n"},{"id":"370735","messageId":"20190305191805.13561-7-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 06/18] sequencer: refactor check_todo_list() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:17:53Z","receivedAt":"2019-03-05T19:18:38Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors check_todo_list() to work on a todo_list to avoid\nredundant reads and writes to the disk.  The function is renamed\ntodo_list_check().  The parsing of the two todo lists is left to the\ncaller.\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, check_todo_list_from_file().  It reads the file\nfrom the disk, parses it, pass the todo_list to todo_list_check(), and\nwrites it back to the disk.\n\nAs get_missing_commit_check_level() and the enum\nmissing_commit_check_level are no longer needed inside of sequencer.c,\nthey are moved to rebase-interactive.c, and made static again.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v7.\n\n builtin/rebase--interactive.c |   2 +-\n rebase-interactive.c          |  91 ++++++++++++++++++++++++-\n rebase-interactive.h          |   2 +\n sequencer.c                   | 121 +++++++---------------------------\n sequencer.h                   |   9 +--\n 5 files changed, 117 insertions(+), 108 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 0898eb4c59..df19ccaeb9 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -256,7 +256,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = transform_todo_file(the_repository, flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n-\t\tret = check_todo_list(the_repository);\n+\t\tret = check_todo_list_from_file(the_repository);\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n \t\tret = rearrange_squash(the_repository);\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 842fa07e7e..dfa6dd530f 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -1,8 +1,32 @@\n #include \"cache.h\"\n #include \"commit.h\"\n-#include \"rebase-interactive.h\"\n #include \"sequencer.h\"\n+#include \"rebase-interactive.h\"\n #include \"strbuf.h\"\n+#include \"commit-slab.h\"\n+#include \"config.h\"\n+\n+enum missing_commit_check_level {\n+\tMISSING_COMMIT_CHECK_IGNORE = 0,\n+\tMISSING_COMMIT_CHECK_WARN,\n+\tMISSING_COMMIT_CHECK_ERROR\n+};\n+\n+static enum missing_commit_check_level get_missing_commit_check_level(void)\n+{\n+\tconst char *value;\n+\n+\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n+\t\t\t!strcasecmp(\"ignore\", value))\n+\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n+\tif (!strcasecmp(\"warn\", value))\n+\t\treturn MISSING_COMMIT_CHECK_WARN;\n+\tif (!strcasecmp(\"error\", value))\n+\t\treturn MISSING_COMMIT_CHECK_ERROR;\n+\twarning(_(\"unrecognized setting %s for option \"\n+\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n+\treturn MISSING_COMMIT_CHECK_IGNORE;\n+}\n \n void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \t\t      struct strbuf *buf)\n@@ -89,3 +113,68 @@ int edit_todo_list(struct repository *r, unsigned flags)\n \n \treturn 0;\n }\n+\n+define_commit_slab(commit_seen, unsigned char);\n+/*\n+ * Check if the user dropped some commits by mistake\n+ * Behaviour determined by rebase.missingCommitsCheck.\n+ * Check if there is an unrecognized command or a\n+ * bad SHA-1 in a command.\n+ */\n+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo)\n+{\n+\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n+\tstruct strbuf missing = STRBUF_INIT;\n+\tint res = 0, i;\n+\tstruct commit_seen commit_seen;\n+\n+\tinit_commit_seen(&commit_seen);\n+\n+\tif (check_level == MISSING_COMMIT_CHECK_IGNORE)\n+\t\tgoto leave_check;\n+\n+\t/* Mark the commits in git-rebase-todo as seen */\n+\tfor (i = 0; i < new_todo->nr; i++) {\n+\t\tstruct commit *commit = new_todo->items[i].commit;\n+\t\tif (commit)\n+\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t}\n+\n+\t/* Find commits in git-rebase-todo.backup yet unseen */\n+\tfor (i = old_todo->nr - 1; i >= 0; i--) {\n+\t\tstruct todo_item *item = old_todo->items + i;\n+\t\tstruct commit *commit = item->commit;\n+\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n+\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n+\t\t\t\t    find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV),\n+\t\t\t\t    item->arg_len,\n+\t\t\t\t    todo_item_get_arg(old_todo, item));\n+\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t\t}\n+\t}\n+\n+\t/* Warn about missing commits */\n+\tif (!missing.len)\n+\t\tgoto leave_check;\n+\n+\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n+\t\tres = 1;\n+\n+\tfprintf(stderr,\n+\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n+\t\t\"Dropped commits (newer to older):\\n\"));\n+\n+\t/* Make the list user-friendly and display */\n+\tfputs(missing.buf, stderr);\n+\tstrbuf_release(&missing);\n+\n+\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n+\t\t\"explicitly remove a commit.\\n\\n\"\n+\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n+\t\t\"the level of warnings.\\n\"\n+\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n+\n+leave_check:\n+\tclear_commit_seen(&commit_seen);\n+\treturn res;\n+}\ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 17b6c9f6d0..187b5032d6 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -3,9 +3,11 @@\n \n struct strbuf;\n struct repository;\n+struct todo_list;\n \n void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \t\t      struct strbuf *buf);\n int edit_todo_list(struct repository *r, unsigned flags);\n+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n \n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex 4809b22ce4..99e12c751e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4660,112 +4660,37 @@ int transform_todo_file(struct repository *r, unsigned flags)\n \treturn 0;\n }\n \n-enum missing_commit_check_level get_missing_commit_check_level(void)\n-{\n-\tconst char *value;\n-\n-\tif (git_config_get_value(\"rebase.missingcommitscheck\", &value) ||\n-\t\t\t!strcasecmp(\"ignore\", value))\n-\t\treturn MISSING_COMMIT_CHECK_IGNORE;\n-\tif (!strcasecmp(\"warn\", value))\n-\t\treturn MISSING_COMMIT_CHECK_WARN;\n-\tif (!strcasecmp(\"error\", value))\n-\t\treturn MISSING_COMMIT_CHECK_ERROR;\n-\twarning(_(\"unrecognized setting %s for option \"\n-\t\t  \"rebase.missingCommitsCheck. Ignoring.\"), value);\n-\treturn MISSING_COMMIT_CHECK_IGNORE;\n-}\n+static const char edit_todo_list_advice[] =\n+N_(\"You can fix this with 'git rebase --edit-todo' \"\n+\"and then run 'git rebase --continue'.\\n\"\n+\"Or you can abort the rebase with 'git rebase\"\n+\" --abort'.\\n\");\n \n-define_commit_slab(commit_seen, unsigned char);\n-/*\n- * Check if the user dropped some commits by mistake\n- * Behaviour determined by rebase.missingCommitsCheck.\n- * Check if there is an unrecognized command or a\n- * bad SHA-1 in a command.\n- */\n-int check_todo_list(struct repository *r)\n+int check_todo_list_from_file(struct repository *r)\n {\n-\tenum missing_commit_check_level check_level = get_missing_commit_check_level();\n-\tstruct strbuf todo_file = STRBUF_INIT;\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf missing = STRBUF_INIT;\n-\tint advise_to_edit_todo = 0, res = 0, i;\n-\tstruct commit_seen commit_seen;\n-\n-\tinit_commit_seen(&commit_seen);\n+\tstruct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;\n+\tint res = 0;\n \n-\tstrbuf_addstr(&todo_file, rebase_path_todo());\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n+\tif (strbuf_read_file_or_whine(&new_todo.buf, rebase_path_todo()) < 0) {\n \t\tres = -1;\n-\t\tgoto leave_check;\n-\t}\n-\tadvise_to_edit_todo = res =\n-\t\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n-\n-\tif (res || check_level == MISSING_COMMIT_CHECK_IGNORE)\n-\t\tgoto leave_check;\n-\n-\t/* Mark the commits in git-rebase-todo as seen */\n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tstruct commit *commit = todo_list.items[i].commit;\n-\t\tif (commit)\n-\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n+\t\tgoto out;\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\tstrbuf_addstr(&todo_file, \".backup\");\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {\n+\tif (strbuf_read_file_or_whine(&old_todo.buf, rebase_path_todo_backup()) < 0) {\n \t\tres = -1;\n-\t\tgoto leave_check;\n-\t}\n-\tstrbuf_release(&todo_file);\n-\tres = !!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n-\n-\t/* Find commits in git-rebase-todo.backup yet unseen */\n-\tfor (i = todo_list.nr - 1; i >= 0; i--) {\n-\t\tstruct todo_item *item = todo_list.items + i;\n-\t\tstruct commit *commit = item->commit;\n-\t\tif (commit && !*commit_seen_at(&commit_seen, commit)) {\n-\t\t\tstrbuf_addf(&missing, \" - %s %.*s\\n\",\n-\t\t\t\t    short_commit_name(commit),\n-\t\t\t\t    item->arg_len,\n-\t\t\t\t    todo_item_get_arg(&todo_list, item));\n-\t\t\t*commit_seen_at(&commit_seen, commit) = 1;\n-\t\t}\n+\t\tgoto out;\n \t}\n \n-\t/* Warn about missing commits */\n-\tif (!missing.len)\n-\t\tgoto leave_check;\n-\n-\tif (check_level == MISSING_COMMIT_CHECK_ERROR)\n-\t\tadvise_to_edit_todo = res = 1;\n-\n-\tfprintf(stderr,\n-\t\t_(\"Warning: some commits may have been dropped accidentally.\\n\"\n-\t\t\"Dropped commits (newer to older):\\n\"));\n-\n-\t/* Make the list user-friendly and display */\n-\tfputs(missing.buf, stderr);\n-\tstrbuf_release(&missing);\n-\n-\tfprintf(stderr, _(\"To avoid this message, use \\\"drop\\\" to \"\n-\t\t\"explicitly remove a commit.\\n\\n\"\n-\t\t\"Use 'git config rebase.missingCommitsCheck' to change \"\n-\t\t\"the level of warnings.\\n\"\n-\t\t\"The possible behaviours are: ignore, warn, error.\\n\\n\"));\n-\n-leave_check:\n-\tclear_commit_seen(&commit_seen);\n-\tstrbuf_release(&todo_file);\n-\ttodo_list_release(&todo_list);\n-\n-\tif (advise_to_edit_todo)\n-\t\tfprintf(stderr,\n-\t\t\t_(\"You can fix this with 'git rebase --edit-todo' \"\n-\t\t\t  \"and then run 'git rebase --continue'.\\n\"\n-\t\t\t  \"Or you can abort the rebase with 'git rebase\"\n-\t\t\t  \" --abort'.\\n\"));\n+\tres = todo_list_parse_insn_buffer(r, old_todo.buf.buf, &old_todo);\n+\tif (!res)\n+\t\tres = todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo);\n+\tif (!res)\n+\t\tres = todo_list_check(&old_todo, &new_todo);\n+\tif (res)\n+\t\tfprintf(stderr, _(edit_todo_list_advice));\n+out:\n+\ttodo_list_release(&old_todo);\n+\ttodo_list_release(&new_todo);\n \n \treturn res;\n }\n@@ -4943,7 +4868,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \n \ttodo_list_release(&todo_list);\n \n-\tif (check_todo_list(r)) {\n+\tif (check_todo_list_from_file(r)) {\n \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n \t\treturn -1;\n \t}\ndiff --git a/sequencer.h b/sequencer.h\nindex 7278f9675b..217353e9f0 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -64,12 +64,6 @@ struct replay_opts {\n };\n #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }\n \n-enum missing_commit_check_level {\n-\tMISSING_COMMIT_CHECK_IGNORE = 0,\n-\tMISSING_COMMIT_CHECK_WARN,\n-\tMISSING_COMMIT_CHECK_ERROR\n-};\n-\n int write_message(const void *buf, size_t len, const char *filename,\n \t\t  int append_eol);\n \n@@ -154,8 +148,7 @@ int sequencer_make_script(struct repository *r, FILE *out, int argc,\n \n int sequencer_add_exec_commands(struct repository *r, const char *command);\n int transform_todo_file(struct repository *r, unsigned flags);\n-enum missing_commit_check_level get_missing_commit_check_level(void);\n-int check_todo_list(struct repository *r);\n+int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, const char *cmd,\n-- \n2.20.1\n\n"},{"id":"370736","messageId":"20190305191805.13561-8-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 07/18] sequencer: refactor sequencer_add_exec_commands() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:17:54Z","receivedAt":"2019-03-05T19:18:40Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors sequencer_add_exec_commands() to work on a todo_list to\navoid redundant reads and writes to the disk.\n\nInstead of inserting the `exec' commands between the other commands and\nre-parsing the buffer at the end, they are appended to the buffer once,\nand a new list of items is created.  Items from the old list are copied\nacross and new `exec' items are appended when necessary.  This\neliminates the need to reparse the buffer, but this also means we have\nto use todo_list_write_to_disk() to write the file.\n\ntodo_list_add_exec_commands() and sequencer_add_exec_commands() are\nmodified to take a string list instead of a string -- one item for each\ncommand.  This makes it easier to insert a new command to the todo list\nfor each command to execute.\n\nsequencer_add_exec_commands() still reads the todo list from the disk,\nas it is needed by rebase -p.\n\ncomplete_action() still uses sequencer_add_exec_commands() for now.\nThis will be changed in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c |  18 ++++--\n sequencer.c                   | 114 ++++++++++++++++++++++------------\n sequencer.h                   |   5 +-\n 3 files changed, 91 insertions(+), 46 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex df19ccaeb9..813bc34140 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -65,7 +65,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \t\t\t\t const char *onto, const char *onto_name,\n \t\t\t\t const char *squash_onto, const char *head_name,\n \t\t\t\t const char *restrict_revision, char *raw_strategies,\n-\t\t\t\t const char *cmd, unsigned autosquash)\n+\t\t\t\t struct string_list *commands, unsigned autosquash)\n {\n \tint ret;\n \tconst char *head_hash = NULL;\n@@ -116,7 +116,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \t\tdiscard_cache();\n \t\tret = complete_action(the_repository, opts, flags,\n \t\t\t\t      shortrevisions, onto_name, onto,\n-\t\t\t\t      head_hash, cmd, autosquash);\n+\t\t\t\t      head_hash, commands, autosquash);\n \t}\n \n \tfree(revisions);\n@@ -139,6 +139,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \tconst char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,\n \t\t*squash_onto = NULL, *upstream = NULL, *head_name = NULL,\n \t\t*switch_to = NULL, *cmd = NULL;\n+\tstruct string_list commands = STRING_LIST_INIT_DUP;\n \tchar *raw_strategies = NULL;\n \tenum {\n \t\tNONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,\n@@ -221,6 +222,14 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\twarning(_(\"--[no-]rebase-cousins has no effect without \"\n \t\t\t  \"--rebase-merges\"));\n \n+\tif (cmd && *cmd) {\n+\t\tstring_list_split(&commands, cmd, '\\n', -1);\n+\n+\t\t/* rebase.c adds a new line to cmd after every command,\n+\t\t * so here the last command is always empty */\n+\t\tstring_list_remove_empty_items(&commands, 0);\n+\t}\n+\n \tswitch (command) {\n \tcase NONE:\n \t\tif (!onto && !upstream)\n@@ -228,7 +237,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \n \t\tret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,\n \t\t\t\t\t    onto_name, squash_onto, head_name, restrict_revision,\n-\t\t\t\t\t    raw_strategies, cmd, autosquash);\n+\t\t\t\t\t    raw_strategies, &commands, autosquash);\n \t\tbreak;\n \tcase SKIP: {\n \t\tstruct string_list merge_rr = STRING_LIST_INIT_DUP;\n@@ -262,11 +271,12 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = rearrange_squash(the_repository);\n \t\tbreak;\n \tcase ADD_EXEC:\n-\t\tret = sequencer_add_exec_commands(the_repository, cmd);\n+\t\tret = sequencer_add_exec_commands(the_repository, &commands);\n \t\tbreak;\n \tdefault:\n \t\tBUG(\"invalid command '%d'\", command);\n \t}\n \n+\tstring_list_clear(&commands, 0);\n \treturn !!ret;\n }\ndiff --git a/sequencer.c b/sequencer.c\nindex 99e12c751e..4c076bd183 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4505,61 +4505,95 @@ int sequencer_make_script(struct repository *r, FILE *out,\n  * Add commands after pick and (series of) squash/fixup commands\n  * in the todo list.\n  */\n-int sequencer_add_exec_commands(struct repository *r,\n-\t\t\t\tconst char *commands)\n+static void todo_list_add_exec_commands(struct todo_list *todo_list,\n+\t\t\t\t\tstruct string_list *commands)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf *buf = &todo_list.buf;\n-\tsize_t offset = 0, commands_len = strlen(commands);\n-\tint i, insert;\n+\tstruct strbuf *buf = &todo_list->buf;\n+\tsize_t base_offset = buf->len;\n+\tint i, insert, nr = 0, alloc = 0;\n+\tstruct todo_item *items = NULL, *base_items = NULL;\n \n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error(_(\"could not read '%s'.\"), todo_file);\n+\tbase_items = xcalloc(commands->nr, sizeof(struct todo_item));\n+\tfor (i = 0; i < commands->nr; i++) {\n+\t\tsize_t command_len = strlen(commands->items[i].string);\n \n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t\tstrbuf_addstr(buf, commands->items[i].string);\n+\t\tstrbuf_addch(buf, '\\n');\n+\n+\t\tbase_items[i].command = TODO_EXEC;\n+\t\tbase_items[i].offset_in_buf = base_offset;\n+\t\tbase_items[i].arg_offset = base_offset + strlen(\"exec \");\n+\t\tbase_items[i].arg_len = command_len - strlen(\"exec \");\n+\n+\t\tbase_offset += command_len + 1;\n \t}\n \n \t/*\n \t * Insert <commands> after every pick. Here, fixup/squash chains\n \t * are considered part of the pick, so we insert the commands *after*\n \t * those chains if there are any.\n+\t *\n+\t * As we insert the exec commands immediatly after rearranging\n+\t * any fixups and before the user edits the list, a fixup chain\n+\t * can never contain comments (any comments are empty picks that\n+\t * have been commented out because the user did not specify\n+\t * --keep-empty).  So, it is safe to insert an exec command\n+\t * without looking at the command following a comment.\n \t */\n-\tinsert = -1;\n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tenum todo_command command = todo_list.items[i].command;\n-\n-\t\tif (insert >= 0) {\n-\t\t\t/* skip fixup/squash chains */\n-\t\t\tif (command == TODO_COMMENT)\n-\t\t\t\tcontinue;\n-\t\t\telse if (is_fixup(command)) {\n-\t\t\t\tinsert = i + 1;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tstrbuf_insert(buf,\n-\t\t\t\t      todo_list.items[insert].offset_in_buf +\n-\t\t\t\t      offset, commands, commands_len);\n-\t\t\toffset += commands_len;\n-\t\t\tinsert = -1;\n+\tinsert = 0;\n+\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\tenum todo_command command = todo_list->items[i].command;\n+\t\tif (insert && !is_fixup(command)) {\n+\t\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n+\t\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n+\t\t\tnr += commands->nr;\n+\n+\t\t\tinsert = 0;\n \t\t}\n \n+\t\tALLOC_GROW(items, nr + 1, alloc);\n+\t\titems[nr++] = todo_list->items[i];\n+\n \t\tif (command == TODO_PICK || command == TODO_MERGE)\n-\t\t\tinsert = i + 1;\n+\t\t\tinsert = 1;\n \t}\n \n \t/* insert or append final <commands> */\n-\tif (insert >= 0 && insert < todo_list.nr)\n-\t\tstrbuf_insert(buf, todo_list.items[insert].offset_in_buf +\n-\t\t\t      offset, commands, commands_len);\n-\telse if (insert >= 0 || !offset)\n-\t\tstrbuf_add(buf, commands, commands_len);\n+\tif (insert || nr == todo_list->nr) {\n+\t\tALLOC_GROW(items, nr + commands->nr, alloc);\n+\t\tCOPY_ARRAY(items + nr, base_items, commands->nr);\n+\t\tnr += commands->nr;\n+\t}\n+\n+\tfree(base_items);\n+\tFREE_AND_NULL(todo_list->items);\n+\ttodo_list->items = items;\n+\ttodo_list->nr = nr;\n+\ttodo_list->alloc = alloc;\n+}\n+\n+int sequencer_add_exec_commands(struct repository *r,\n+\t\t\t\tstruct string_list *commands)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n-\ti = write_message(buf->buf, buf->len, todo_file, 0);\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_add_exec_commands(&todo_list, commands);\n+\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n \ttodo_list_release(&todo_list);\n-\treturn i;\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n }\n \n static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n@@ -4790,7 +4824,7 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-\t\t    const char *onto, const char *orig_head, const char *cmd,\n+\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash)\n {\n \tconst char *shortonto, *todo_file = rebase_path_todo();\n@@ -4809,8 +4843,8 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tif (autosquash && rearrange_squash(r))\n \t\treturn -1;\n \n-\tif (cmd && *cmd)\n-\t\tsequencer_add_exec_commands(r, cmd);\n+\tif (commands->nr)\n+\t\tsequencer_add_exec_commands(r, commands);\n \n \tif (strbuf_read_file(buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\ndiff --git a/sequencer.h b/sequencer.h\nindex 217353e9f0..87d04a3b9b 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -146,12 +146,13 @@ int sequencer_make_script(struct repository *r, FILE *out, int argc,\n \t\t\t  const char **argv,\n \t\t\t  unsigned flags);\n \n-int sequencer_add_exec_commands(struct repository *r, const char *command);\n+int sequencer_add_exec_commands(struct repository *r,\n+\t\t\t\tstruct string_list *commands);\n int transform_todo_file(struct repository *r, unsigned flags);\n int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-\t\t    const char *onto, const char *orig_head, const char *cmd,\n+\t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash);\n int rearrange_squash(struct repository *r);\n \n-- \n2.20.1\n\n"},{"id":"370737","messageId":"20190305191805.13561-9-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 08/18] sequencer: refactor rearrange_squash() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:17:55Z","receivedAt":"2019-03-05T19:18:42Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors rearrange_squash() to work on a todo_list to avoid\nredundant reads and writes.  The function is renamed\ntodo_list_rearrange_squash().\n\nThe old version created a new buffer, which was directly written to the\ndisk.  This new version creates a new item list by just copying items\nfrom the old item list, without creating a new buffer.  This eliminates\nthe need to reparse the todo list, but this also means its buffer cannot\nbe directly written to the disk.\n\nAs rebase -p still need to check the todo list from the disk, a new\nfunction is introduced, rearrange_squash_in_todo_file().\n\ncomplete_action() still uses rearrange_squash_in_todo_file() for now.\nThis will be changed in a future commit.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v7.\n\n builtin/rebase--interactive.c |  2 +-\n sequencer.c                   | 92 ++++++++++++++++++-----------------\n sequencer.h                   |  2 +-\n 3 files changed, 49 insertions(+), 47 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 813bc34140..60b15f9693 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -268,7 +268,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = check_todo_list_from_file(the_repository);\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n-\t\tret = rearrange_squash(the_repository);\n+\t\tret = rearrange_squash_in_todo_file(the_repository);\n \t\tbreak;\n \tcase ADD_EXEC:\n \t\tret = sequencer_add_exec_commands(the_repository, &commands);\ndiff --git a/sequencer.c b/sequencer.c\nindex 4c076bd183..7798f93b23 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4840,7 +4840,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t    write_message(\"noop\\n\", 5, todo_file, 0))\n \t\treturn -1;\n \n-\tif (autosquash && rearrange_squash(r))\n+\tif (autosquash && rearrange_squash_in_todo_file(r))\n \t\treturn -1;\n \n \tif (commands->nr)\n@@ -4946,21 +4946,13 @@ define_commit_slab(commit_todo_item, struct todo_item *);\n  * message will have to be retrieved from the commit (as the oneline in the\n  * script cannot be trusted) in order to normalize the autosquash arrangement.\n  */\n-int rearrange_squash(struct repository *r)\n+static int todo_list_rearrange_squash(struct todo_list *todo_list)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct hashmap subject2item;\n-\tint res = 0, rearranged = 0, *next, *tail, i;\n+\tint rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;\n \tchar **subjects;\n \tstruct commit_todo_item commit_todo;\n-\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tstruct todo_item *items = NULL;\n \n \tinit_commit_todo_item(&commit_todo);\n \t/*\n@@ -4973,13 +4965,13 @@ int rearrange_squash(struct repository *r)\n \t * be moved to appear after the i'th.\n \t */\n \thashmap_init(&subject2item, (hashmap_cmp_fn) subject2item_cmp,\n-\t\t     NULL, todo_list.nr);\n-\tALLOC_ARRAY(next, todo_list.nr);\n-\tALLOC_ARRAY(tail, todo_list.nr);\n-\tALLOC_ARRAY(subjects, todo_list.nr);\n-\tfor (i = 0; i < todo_list.nr; i++) {\n+\t\t     NULL, todo_list->nr);\n+\tALLOC_ARRAY(next, todo_list->nr);\n+\tALLOC_ARRAY(tail, todo_list->nr);\n+\tALLOC_ARRAY(subjects, todo_list->nr);\n+\tfor (i = 0; i < todo_list->nr; i++) {\n \t\tstruct strbuf buf = STRBUF_INIT;\n-\t\tstruct todo_item *item = todo_list.items + i;\n+\t\tstruct todo_item *item = todo_list->items + i;\n \t\tconst char *commit_buffer, *subject, *p;\n \t\tsize_t subject_len;\n \t\tint i2 = -1;\n@@ -4992,7 +4984,6 @@ int rearrange_squash(struct repository *r)\n \t\t}\n \n \t\tif (is_fixup(item->command)) {\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\tclear_commit_todo_item(&commit_todo);\n \t\t\treturn error(_(\"the script was already rearranged.\"));\n \t\t}\n@@ -5027,7 +5018,7 @@ int rearrange_squash(struct repository *r)\n \t\t\t\t *commit_todo_item_at(&commit_todo, commit2))\n \t\t\t\t/* found by commit name */\n \t\t\t\ti2 = *commit_todo_item_at(&commit_todo, commit2)\n-\t\t\t\t\t- todo_list.items;\n+\t\t\t\t\t- todo_list->items;\n \t\t\telse {\n \t\t\t\t/* copy can be a prefix of the commit subject */\n \t\t\t\tfor (i2 = 0; i2 < i; i2++)\n@@ -5040,7 +5031,7 @@ int rearrange_squash(struct repository *r)\n \t\t}\n \t\tif (i2 >= 0) {\n \t\t\trearranged = 1;\n-\t\t\ttodo_list.items[i].command =\n+\t\t\ttodo_list->items[i].command =\n \t\t\t\tstarts_with(subject, \"fixup!\") ?\n \t\t\t\tTODO_FIXUP : TODO_SQUASH;\n \t\t\tif (next[i2] < 0)\n@@ -5058,10 +5049,8 @@ int rearrange_squash(struct repository *r)\n \t}\n \n \tif (rearranged) {\n-\t\tstruct strbuf buf = STRBUF_INIT;\n-\n-\t\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\t\tenum todo_command command = todo_list.items[i].command;\n+\t\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\t\tenum todo_command command = todo_list->items[i].command;\n \t\t\tint cur = i;\n \n \t\t\t/*\n@@ -5072,37 +5061,50 @@ int rearrange_squash(struct repository *r)\n \t\t\t\tcontinue;\n \n \t\t\twhile (cur >= 0) {\n-\t\t\t\tconst char *bol =\n-\t\t\t\t\tget_item_line(&todo_list, cur);\n-\t\t\t\tconst char *eol =\n-\t\t\t\t\tget_item_line(&todo_list, cur + 1);\n-\n-\t\t\t\t/* replace 'pick', by 'fixup' or 'squash' */\n-\t\t\t\tcommand = todo_list.items[cur].command;\n-\t\t\t\tif (is_fixup(command)) {\n-\t\t\t\t\tstrbuf_addstr(&buf,\n-\t\t\t\t\t\ttodo_command_info[command].str);\n-\t\t\t\t\tbol += strcspn(bol, \" \\t\");\n-\t\t\t\t}\n-\n-\t\t\t\tstrbuf_add(&buf, bol, eol - bol);\n-\n+\t\t\t\tALLOC_GROW(items, nr + 1, alloc);\n+\t\t\t\titems[nr++] = todo_list->items[cur];\n \t\t\t\tcur = next[cur];\n \t\t\t}\n \t\t}\n \n-\t\tres = rewrite_file(todo_file, buf.buf, buf.len);\n-\t\tstrbuf_release(&buf);\n+\t\tFREE_AND_NULL(todo_list->items);\n+\t\ttodo_list->items = items;\n+\t\ttodo_list->nr = nr;\n+\t\ttodo_list->alloc = alloc;\n \t}\n \n \tfree(next);\n \tfree(tail);\n-\tfor (i = 0; i < todo_list.nr; i++)\n+\tfor (i = 0; i < todo_list->nr; i++)\n \t\tfree(subjects[i]);\n \tfree(subjects);\n \thashmap_free(&subject2item, 1);\n-\ttodo_list_release(&todo_list);\n \n \tclear_commit_todo_item(&commit_todo);\n-\treturn res;\n+\n+\treturn 0;\n+}\n+\n+int rearrange_squash_in_todo_file(struct repository *r)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n+\n+\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n+\t\treturn -1;\n+\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn -1;\n+\t}\n+\n+\tres = todo_list_rearrange_squash(&todo_list);\n+\tif (!res)\n+\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n+\n+\ttodo_list_release(&todo_list);\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n }\ndiff --git a/sequencer.h b/sequencer.h\nindex 87d04a3b9b..add50f04f1 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -154,7 +154,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash);\n-int rearrange_squash(struct repository *r);\n+int rearrange_squash_in_todo_file(struct repository *r);\n \n extern const char sign_off_header[];\n \n-- \n2.20.1\n\n"},{"id":"370738","messageId":"20190305191805.13561-10-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 09/18] sequencer: make sequencer_make_script() write its script to a strbuf","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:17:56Z","receivedAt":"2019-03-05T19:18:45Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This makes sequencer_make_script() write its script to a strbuf (ie. the\nbuffer of a todo_list) instead of a FILE.  This reduce the amount of\nread/write made by rebase interactive.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v7.\n\n builtin/rebase--interactive.c | 13 ++++++-----\n sequencer.c                   | 41 +++++++++++++++--------------------\n sequencer.h                   |  5 ++---\n 3 files changed, 28 insertions(+), 31 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 60b15f9693..b4190e58e1 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -71,7 +71,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tconst char *head_hash = NULL;\n \tchar *revisions = NULL, *shortrevisions = NULL;\n \tstruct argv_array make_script_args = ARGV_ARRAY_INIT;\n-\tFILE *todo_list;\n+\tFILE *todo_list_file;\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n \n \tif (prepare_branch_to_be_rebased(opts, switch_to))\n \t\treturn -1;\n@@ -93,8 +94,8 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (!upstream && squash_onto)\n \t\twrite_file(path_squash_onto(), \"%s\\n\", squash_onto);\n \n-\ttodo_list = fopen(rebase_path_todo(), \"w\");\n-\tif (!todo_list) {\n+\ttodo_list_file = fopen(rebase_path_todo(), \"w\");\n+\tif (!todo_list_file) {\n \t\tfree(revisions);\n \t\tfree(shortrevisions);\n \n@@ -105,10 +106,11 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (restrict_revision)\n \t\targv_array_push(&make_script_args, restrict_revision);\n \n-\tret = sequencer_make_script(the_repository, todo_list,\n+\tret = sequencer_make_script(the_repository, &todo_list.buf,\n \t\t\t\t    make_script_args.argc, make_script_args.argv,\n \t\t\t\t    flags);\n-\tfclose(todo_list);\n+\tfputs(todo_list.buf.buf, todo_list_file);\n+\tfclose(todo_list_file);\n \n \tif (ret)\n \t\terror(_(\"could not generate todo list\"));\n@@ -121,6 +123,7 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \n \tfree(revisions);\n \tfree(shortrevisions);\n+\ttodo_list_release(&todo_list);\n \targv_array_clear(&make_script_args);\n \n \treturn ret;\ndiff --git a/sequencer.c b/sequencer.c\nindex 7798f93b23..cf716fa5b8 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4215,7 +4215,7 @@ static const char *label_oid(struct object_id *oid, const char *label,\n }\n \n static int make_script_with_merges(struct pretty_print_context *pp,\n-\t\t\t\t   struct rev_info *revs, FILE *out,\n+\t\t\t\t   struct rev_info *revs, struct strbuf *out,\n \t\t\t\t   unsigned flags)\n {\n \tint keep_empty = flags & TODO_LIST_KEEP_EMPTY;\n@@ -4360,7 +4360,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t * gathering commits not yet shown, reversing the list on the fly,\n \t * then outputting that list (labeling revisions as needed).\n \t */\n-\tfprintf(out, \"%s onto\\n\", cmd_label);\n+\tstrbuf_addf(out, \"%s onto\\n\", cmd_label);\n \tfor (iter = tips; iter; iter = iter->next) {\n \t\tstruct commit_list *list = NULL, *iter2;\n \n@@ -4370,9 +4370,9 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\tentry = oidmap_get(&state.commit2label, &commit->object.oid);\n \n \t\tif (entry)\n-\t\t\tfprintf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n+\t\t\tstrbuf_addf(out, \"\\n%c Branch %s\\n\", comment_line_char, entry->string);\n \t\telse\n-\t\t\tfprintf(out, \"\\n\");\n+\t\t\tstrbuf_addch(out, '\\n');\n \n \t\twhile (oidset_contains(&interesting, &commit->object.oid) &&\n \t\t       !oidset_contains(&shown, &commit->object.oid)) {\n@@ -4385,8 +4385,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t}\n \n \t\tif (!commit)\n-\t\t\tfprintf(out, \"%s %s\\n\", cmd_reset,\n-\t\t\t\trebase_cousins ? \"onto\" : \"[new root]\");\n+\t\t\tstrbuf_addf(out, \"%s %s\\n\", cmd_reset,\n+\t\t\t\t    rebase_cousins ? \"onto\" : \"[new root]\");\n \t\telse {\n \t\t\tconst char *to = NULL;\n \n@@ -4399,12 +4399,12 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t\t\t\t       &state);\n \n \t\t\tif (!to || !strcmp(to, \"onto\"))\n-\t\t\t\tfprintf(out, \"%s onto\\n\", cmd_reset);\n+\t\t\t\tstrbuf_addf(out, \"%s onto\\n\", cmd_reset);\n \t\t\telse {\n \t\t\t\tstrbuf_reset(&oneline);\n \t\t\t\tpretty_print_commit(pp, commit, &oneline);\n-\t\t\t\tfprintf(out, \"%s %s # %s\\n\",\n-\t\t\t\t\tcmd_reset, to, oneline.buf);\n+\t\t\t\tstrbuf_addf(out, \"%s %s # %s\\n\",\n+\t\t\t\t\t    cmd_reset, to, oneline.buf);\n \t\t\t}\n \t\t}\n \n@@ -4413,11 +4413,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \t\t\tentry = oidmap_get(&commit2todo, oid);\n \t\t\t/* only show if not already upstream */\n \t\t\tif (entry)\n-\t\t\t\tfprintf(out, \"%s\\n\", entry->string);\n+\t\t\t\tstrbuf_addf(out, \"%s\\n\", entry->string);\n \t\t\tentry = oidmap_get(&state.commit2label, oid);\n \t\t\tif (entry)\n-\t\t\t\tfprintf(out, \"%s %s\\n\",\n-\t\t\t\t\tcmd_label, entry->string);\n+\t\t\t\tstrbuf_addf(out, \"%s %s\\n\",\n+\t\t\t\t\t    cmd_label, entry->string);\n \t\t\toidset_insert(&shown, oid);\n \t\t}\n \n@@ -4439,13 +4439,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,\n \treturn 0;\n }\n \n-int sequencer_make_script(struct repository *r, FILE *out,\n-\t\t\t  int argc, const char **argv,\n-\t\t\t  unsigned flags)\n+int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n+\t\t\t  const char **argv, unsigned flags)\n {\n \tchar *format = NULL;\n \tstruct pretty_print_context pp = {0};\n-\tstruct strbuf buf = STRBUF_INIT;\n \tstruct rev_info revs;\n \tstruct commit *commit;\n \tint keep_empty = flags & TODO_LIST_KEEP_EMPTY;\n@@ -4488,16 +4486,13 @@ int sequencer_make_script(struct repository *r, FILE *out,\n \n \t\tif (!is_empty && (commit->object.flags & PATCHSAME))\n \t\t\tcontinue;\n-\t\tstrbuf_reset(&buf);\n \t\tif (!keep_empty && is_empty)\n-\t\t\tstrbuf_addf(&buf, \"%c \", comment_line_char);\n-\t\tstrbuf_addf(&buf, \"%s %s \", insn,\n+\t\t\tstrbuf_addf(out, \"%c \", comment_line_char);\n+\t\tstrbuf_addf(out, \"%s %s \", insn,\n \t\t\t    oid_to_hex(&commit->object.oid));\n-\t\tpretty_print_commit(&pp, commit, &buf);\n-\t\tstrbuf_addch(&buf, '\\n');\n-\t\tfputs(buf.buf, out);\n+\t\tpretty_print_commit(&pp, commit, out);\n+\t\tstrbuf_addch(out, '\\n');\n \t}\n-\tstrbuf_release(&buf);\n \treturn 0;\n }\n \ndiff --git a/sequencer.h b/sequencer.h\nindex add50f04f1..e25f5151d3 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -142,9 +142,8 @@ int sequencer_remove_state(struct replay_opts *opts);\n #define TODO_LIST_REBASE_COUSINS (1U << 4)\n #define TODO_LIST_APPEND_TODO_HELP (1U << 5)\n \n-int sequencer_make_script(struct repository *r, FILE *out, int argc,\n-\t\t\t  const char **argv,\n-\t\t\t  unsigned flags);\n+int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n+\t\t\t  const char **argv, unsigned flags);\n \n int sequencer_add_exec_commands(struct repository *r,\n \t\t\t\tstruct string_list *commands);\n-- \n2.20.1\n\n"},{"id":"370739","messageId":"20190305191805.13561-11-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 10/18] sequencer: change complete_action() to use the refactored functions","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:17:57Z","receivedAt":"2019-03-05T19:18:47Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"complete_action() used functions that read the todo-list file, made some\nchanges to it, and wrote it back to the disk.\n\nThe previous commits were dedicated to separate the part that deals with\nthe file from the actual logic of these functions.  Now that this is\ndone, we can call directly the \"logic\" functions to avoid useless file\naccess.\n\nThe parsing of the list has to be done by the caller.  If the buffer of\nthe todo list provided by the caller is empty, a `noop' command is\ndirectly added to the todo list, without touching the buffer.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v7.\n\n builtin/rebase--interactive.c | 20 +++------\n sequencer.c                   | 79 +++++++++++++++--------------------\n sequencer.h                   |  2 +-\n 3 files changed, 41 insertions(+), 60 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex b4190e58e1..ffbe14cef5 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -71,7 +71,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tconst char *head_hash = NULL;\n \tchar *revisions = NULL, *shortrevisions = NULL;\n \tstruct argv_array make_script_args = ARGV_ARRAY_INIT;\n-\tFILE *todo_list_file;\n \tstruct todo_list todo_list = TODO_LIST_INIT;\n \n \tif (prepare_branch_to_be_rebased(opts, switch_to))\n@@ -94,14 +93,6 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tif (!upstream && squash_onto)\n \t\twrite_file(path_squash_onto(), \"%s\\n\", squash_onto);\n \n-\ttodo_list_file = fopen(rebase_path_todo(), \"w\");\n-\tif (!todo_list_file) {\n-\t\tfree(revisions);\n-\t\tfree(shortrevisions);\n-\n-\t\treturn error_errno(_(\"could not open %s\"), rebase_path_todo());\n-\t}\n-\n \targv_array_pushl(&make_script_args, \"\", revisions, NULL);\n \tif (restrict_revision)\n \t\targv_array_push(&make_script_args, restrict_revision);\n@@ -109,16 +100,17 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,\n \tret = sequencer_make_script(the_repository, &todo_list.buf,\n \t\t\t\t    make_script_args.argc, make_script_args.argv,\n \t\t\t\t    flags);\n-\tfputs(todo_list.buf.buf, todo_list_file);\n-\tfclose(todo_list_file);\n \n \tif (ret)\n \t\terror(_(\"could not generate todo list\"));\n \telse {\n \t\tdiscard_cache();\n-\t\tret = complete_action(the_repository, opts, flags,\n-\t\t\t\t      shortrevisions, onto_name, onto,\n-\t\t\t\t      head_hash, commands, autosquash);\n+\t\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n+\t\t\t\t\t\t&todo_list))\n+\t\t\tBUG(\"unusable todo list\");\n+\n+\t\tret = complete_action(the_repository, opts, flags, shortrevisions, onto_name,\n+\t\t\t\t      onto, head_hash, commands, autosquash, &todo_list);\n \t}\n \n \tfree(revisions);\ndiff --git a/sequencer.c b/sequencer.c\nindex cf716fa5b8..1d5cd2fc27 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4817,93 +4817,82 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \treturn 0;\n }\n \n+static int todo_list_rearrange_squash(struct todo_list *todo_list);\n+\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n-\t\t    unsigned autosquash)\n+\t\t    unsigned autosquash, struct todo_list *todo_list)\n {\n \tconst char *shortonto, *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tstruct strbuf *buf = &(todo_list.buf);\n+\tstruct todo_list new_todo = TODO_LIST_INIT;\n+\tstruct strbuf *buf = &todo_list->buf;\n \tstruct object_id oid;\n-\tstruct stat st;\n \n \tget_oid(onto, &oid);\n \tshortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);\n \n-\tif (!lstat(todo_file, &st) && st.st_size == 0 &&\n-\t    write_message(\"noop\\n\", 5, todo_file, 0))\n-\t\treturn -1;\n+\tif (buf->len == 0) {\n+\t\tstruct todo_item *item = append_new_todo(todo_list);\n+\t\titem->command = TODO_NOOP;\n+\t\titem->commit = NULL;\n+\t\titem->arg_len = item->arg_offset = item->flags = item->offset_in_buf = 0;\n+\t}\n \n-\tif (autosquash && rearrange_squash_in_todo_file(r))\n+\tif (autosquash && todo_list_rearrange_squash(todo_list))\n \t\treturn -1;\n \n \tif (commands->nr)\n-\t\tsequencer_add_exec_commands(r, commands);\n+\t\ttodo_list_add_exec_commands(todo_list, commands);\n \n-\tif (strbuf_read_file(buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(r, buf->buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tif (count_commands(&todo_list) == 0) {\n+\tif (count_commands(todo_list) == 0) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n \n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\tstrbuf_addch(buf, '\\n');\n-\tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n-\t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n-\t\t\t\t      count_commands(&todo_list)),\n-\t\t\t      shortrevisions, shortonto, count_commands(&todo_list));\n-\tappend_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);\n-\n-\tif (write_message(buf->buf, buf->len, todo_file, 0)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tif (todo_list_write_to_file(r, todo_list, todo_file,\n+\t\t\t\t    shortrevisions, shortonto, -1,\n+\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n \n \tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n \t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n \t\t\t     rebase_path_todo_backup());\n \n-\tif (transform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS))\n-\t\treturn error(_(\"could not transform the todo list\"));\n-\n-\tstrbuf_reset(buf);\n-\n-\tif (launch_sequence_editor(todo_file, buf, NULL)) {\n+\tif (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n \n \t\treturn -1;\n \t}\n \n-\tstrbuf_stripspace(buf, 1);\n-\tif (buf->len == 0) {\n+\tstrbuf_stripspace(&new_todo.buf, 1);\n+\tif (new_todo.buf.len == 0) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n-\t\ttodo_list_release(&todo_list);\n+\t\ttodo_list_release(&new_todo);\n \n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\n-\tif (check_todo_list_from_file(r)) {\n+\tif (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) ||\n+\t    todo_list_check(todo_list, &new_todo)) {\n+\t\tfprintf(stderr, _(edit_todo_list_advice));\n \t\tcheckout_onto(opts, onto_name, onto, orig_head);\n+\t\ttodo_list_release(&new_todo);\n+\n \t\treturn -1;\n \t}\n \n-\tif (transform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS)))\n-\t\treturn error(_(\"could not transform the todo list\"));\n+\tif (todo_list_write_to_file(r, &new_todo, todo_file, NULL, NULL, -1,\n+\t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS))) {\n+\t\ttodo_list_release(&new_todo);\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_release(&new_todo);\n \n \tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\n \t\treturn error(_(\"could not skip unnecessary pick commands\"));\ndiff --git a/sequencer.h b/sequencer.h\nindex e25f5151d3..7029b39cd6 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -152,7 +152,7 @@ int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n-\t\t    unsigned autosquash);\n+\t\t    unsigned autosquash, struct todo_list *todo_list);\n int rearrange_squash_in_todo_file(struct repository *r);\n \n extern const char sign_off_header[];\n-- \n2.20.1\n\n"},{"id":"370740","messageId":"20190305191805.13561-12-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 11/18] rebase--interactive: move sequencer_add_exec_commands()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:17:58Z","receivedAt":"2019-03-05T19:18:49Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"As sequencer_add_exec_commands() is only needed inside of\nrebase--interactive.c for `rebase -p', it is moved there from\nsequencer.c.\n\nThe parameter r (repository) is dropped along the way.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNew commit, but was a part of \"rebase--interactive: move several\nfunctions to rebase--interactive.c\" from the v7.\n\n builtin/rebase--interactive.c | 27 ++++++++++++++++++++++++++-\n sequencer.c                   | 28 ++--------------------------\n sequencer.h                   |  4 ++--\n 3 files changed, 30 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex ffbe14cef5..3bf1da6940 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -13,6 +13,31 @@ static GIT_PATH_FUNC(path_state_dir, \"rebase-merge/\")\n static GIT_PATH_FUNC(path_squash_onto, \"rebase-merge/squash-onto\")\n static GIT_PATH_FUNC(path_interactive, \"rebase-merge/interactive\")\n \n+static int add_exec_commands(struct string_list *commands)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n+\t\t\t\t\t&todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\ttodo_list_add_exec_commands(&todo_list, commands);\n+\tres = todo_list_write_to_file(the_repository, &todo_list,\n+\t\t\t\t      todo_file, NULL, NULL, -1, 0);\n+\ttodo_list_release(&todo_list);\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n+}\n+\n static int get_revision_ranges(const char *upstream, const char *onto,\n \t\t\t       const char **head_hash,\n \t\t\t       char **revisions, char **shortrevisions)\n@@ -266,7 +291,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = rearrange_squash_in_todo_file(the_repository);\n \t\tbreak;\n \tcase ADD_EXEC:\n-\t\tret = sequencer_add_exec_commands(the_repository, &commands);\n+\t\tret = add_exec_commands(&commands);\n \t\tbreak;\n \tdefault:\n \t\tBUG(\"invalid command '%d'\", command);\ndiff --git a/sequencer.c b/sequencer.c\nindex 1d5cd2fc27..280d9bcce7 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4500,8 +4500,8 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n  * Add commands after pick and (series of) squash/fixup commands\n  * in the todo list.\n  */\n-static void todo_list_add_exec_commands(struct todo_list *todo_list,\n-\t\t\t\t\tstruct string_list *commands)\n+void todo_list_add_exec_commands(struct todo_list *todo_list,\n+\t\t\t\t struct string_list *commands)\n {\n \tstruct strbuf *buf = &todo_list->buf;\n \tsize_t base_offset = buf->len;\n@@ -4567,30 +4567,6 @@ static void todo_list_add_exec_commands(struct todo_list *todo_list,\n \ttodo_list->alloc = alloc;\n }\n \n-int sequencer_add_exec_commands(struct repository *r,\n-\t\t\t\tstruct string_list *commands)\n-{\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res;\n-\n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\ttodo_list_add_exec_commands(&todo_list, commands);\n-\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n-\ttodo_list_release(&todo_list);\n-\n-\tif (res)\n-\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n-\treturn 0;\n-}\n-\n static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n \t\t\t\tstruct strbuf *buf, int num, unsigned flags)\n {\ndiff --git a/sequencer.h b/sequencer.h\nindex 7029b39cd6..0c4b7c80f8 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -145,9 +145,9 @@ int sequencer_remove_state(struct replay_opts *opts);\n int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \t\t\t  const char **argv, unsigned flags);\n \n-int sequencer_add_exec_commands(struct repository *r,\n-\t\t\t\tstruct string_list *commands);\n int transform_todo_file(struct repository *r, unsigned flags);\n+void todo_list_add_exec_commands(struct todo_list *todo_list,\n+\t\t\t\t struct string_list *commands);\n int check_todo_list_from_file(struct repository *r);\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n-- \n2.20.1\n\n"},{"id":"370741","messageId":"20190305191805.13561-13-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 12/18] rebase--interactive: move rearrange_squash_in_todo_file()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:17:59Z","receivedAt":"2019-03-05T19:18:51Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"As rearrange_squash_in_todo_file() is only needed inside of\nrebase--interactive.c for `rebase -p', it is moved there from\nsequencer.c.\n\nThe parameter r (repository) is dropped along the way, and the error\nhandling is slightly improved.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNew commit, but was a part of \"rebase--interactive: move several\nfunctions to rebase--interactive.c\" from the v7.\n\n builtin/rebase--interactive.c | 28 +++++++++++++++++++++++++++-\n sequencer.c                   | 28 +---------------------------\n sequencer.h                   |  2 +-\n 3 files changed, 29 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex 3bf1da6940..ab2c6fcd99 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -38,6 +38,32 @@ static int add_exec_commands(struct string_list *commands)\n \treturn 0;\n }\n \n+static int rearrange_squash_in_todo_file(void)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n+\t\t\t\t\t&todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\tres = todo_list_rearrange_squash(&todo_list);\n+\tif (!res)\n+\t\tres = todo_list_write_to_file(the_repository, &todo_list,\n+\t\t\t\t\t      todo_file, NULL, NULL, -1, 0);\n+\n+\ttodo_list_release(&todo_list);\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n+}\n+\n static int get_revision_ranges(const char *upstream, const char *onto,\n \t\t\t       const char **head_hash,\n \t\t\t       char **revisions, char **shortrevisions)\n@@ -288,7 +314,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tret = check_todo_list_from_file(the_repository);\n \t\tbreak;\n \tcase REARRANGE_SQUASH:\n-\t\tret = rearrange_squash_in_todo_file(the_repository);\n+\t\tret = rearrange_squash_in_todo_file();\n \t\tbreak;\n \tcase ADD_EXEC:\n \t\tret = add_exec_commands(&commands);\ndiff --git a/sequencer.c b/sequencer.c\nindex 280d9bcce7..ffa4334982 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4793,8 +4793,6 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \treturn 0;\n }\n \n-static int todo_list_rearrange_squash(struct todo_list *todo_list);\n-\n int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n@@ -4906,7 +4904,7 @@ define_commit_slab(commit_todo_item, struct todo_item *);\n  * message will have to be retrieved from the commit (as the oneline in the\n  * script cannot be trusted) in order to normalize the autosquash arrangement.\n  */\n-static int todo_list_rearrange_squash(struct todo_list *todo_list)\n+int todo_list_rearrange_squash(struct todo_list *todo_list)\n {\n \tstruct hashmap subject2item;\n \tint rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;\n@@ -5044,27 +5042,3 @@ static int todo_list_rearrange_squash(struct todo_list *todo_list)\n \n \treturn 0;\n }\n-\n-int rearrange_squash_in_todo_file(struct repository *r)\n-{\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res = 0;\n-\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n-\n-\tres = todo_list_rearrange_squash(&todo_list);\n-\tif (!res)\n-\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n-\n-\ttodo_list_release(&todo_list);\n-\n-\tif (res)\n-\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n-\treturn 0;\n-}\ndiff --git a/sequencer.h b/sequencer.h\nindex 0c4b7c80f8..13c5676c24 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -153,7 +153,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\t    const char *shortrevisions, const char *onto_name,\n \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n \t\t    unsigned autosquash, struct todo_list *todo_list);\n-int rearrange_squash_in_todo_file(struct repository *r);\n+int todo_list_rearrange_squash(struct todo_list *todo_list);\n \n extern const char sign_off_header[];\n \n-- \n2.20.1\n\n"},{"id":"370742","messageId":"20190305191805.13561-14-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 13/18] sequencer: refactor skip_unnecessary_picks() to work on a todo_list","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:18:00Z","receivedAt":"2019-03-05T19:18:53Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This refactors skip_unnecessary_picks() to work on a todo_list.  As this\nfunction is only called by complete_action() (and thus is not used by\nrebase -p), the file-handling logic is completely dropped here.\n\nInstead of truncating the todo list’s buffer, the items are moved to\nthe beginning of the list, eliminating the need to reparse the list.\nThis also means its buffer cannot be directly written to the disk.\n\nrewrite_file() is then removed, as it is now unused.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v7.\n\n sequencer.c | 82 +++++++++++++----------------------------------------\n 1 file changed, 19 insertions(+), 63 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex ffa4334982..894c7538d5 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4700,52 +4700,22 @@ int check_todo_list_from_file(struct repository *r)\n \treturn res;\n }\n \n-static int rewrite_file(const char *path, const char *buf, size_t len)\n-{\n-\tint rc = 0;\n-\tint fd = open(path, O_WRONLY | O_TRUNC);\n-\tif (fd < 0)\n-\t\treturn error_errno(_(\"could not open '%s' for writing\"), path);\n-\tif (write_in_full(fd, buf, len) < 0)\n-\t\trc = error_errno(_(\"could not write to '%s'\"), path);\n-\tif (close(fd) && !rc)\n-\t\trc = error_errno(_(\"could not close '%s'\"), path);\n-\treturn rc;\n-}\n-\n /* skip picking commits whose parents are unchanged */\n-static int skip_unnecessary_picks(struct repository *r, struct object_id *output_oid)\n+static int skip_unnecessary_picks(struct repository *r,\n+\t\t\t\t  struct todo_list *todo_list,\n+\t\t\t\t  struct object_id *base_oid)\n {\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct strbuf buf = STRBUF_INIT;\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n \tstruct object_id *parent_oid;\n-\tint fd, i;\n-\n-\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n-\t\treturn error(_(\"could not read 'onto'\"));\n-\tif (get_oid(buf.buf, output_oid)) {\n-\t\tstrbuf_release(&buf);\n-\t\treturn error(_(\"need a HEAD to fixup\"));\n-\t}\n-\tstrbuf_release(&buf);\n-\n-\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n-\t\treturn -1;\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tint i;\n \n-\tfor (i = 0; i < todo_list.nr; i++) {\n-\t\tstruct todo_item *item = todo_list.items + i;\n+\tfor (i = 0; i < todo_list->nr; i++) {\n+\t\tstruct todo_item *item = todo_list->items + i;\n \n \t\tif (item->command >= TODO_NOOP)\n \t\t\tcontinue;\n \t\tif (item->command != TODO_PICK)\n \t\t\tbreak;\n \t\tif (parse_commit(item->commit)) {\n-\t\t\ttodo_list_release(&todo_list);\n \t\t\treturn error(_(\"could not parse commit '%s'\"),\n \t\t\t\toid_to_hex(&item->commit->object.oid));\n \t\t}\n@@ -4754,42 +4724,26 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output\n \t\tif (item->commit->parents->next)\n \t\t\tbreak; /* merge commit */\n \t\tparent_oid = &item->commit->parents->item->object.oid;\n-\t\tif (!oideq(parent_oid, output_oid))\n+\t\tif (!oideq(parent_oid, base_oid))\n \t\t\tbreak;\n-\t\toidcpy(output_oid, &item->commit->object.oid);\n+\t\toidcpy(base_oid, &item->commit->object.oid);\n \t}\n \tif (i > 0) {\n-\t\tint offset = get_item_line_offset(&todo_list, i);\n \t\tconst char *done_path = rebase_path_done();\n \n-\t\tfd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);\n-\t\tif (fd < 0) {\n-\t\t\terror_errno(_(\"could not open '%s' for writing\"),\n-\t\t\t\t    done_path);\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\treturn -1;\n-\t\t}\n-\t\tif (write_in_full(fd, todo_list.buf.buf, offset) < 0) {\n+\t\tif (todo_list_write_to_file(r, todo_list, done_path, NULL, NULL, i, 0)) {\n \t\t\terror_errno(_(\"could not write to '%s'\"), done_path);\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\tclose(fd);\n \t\t\treturn -1;\n \t\t}\n-\t\tclose(fd);\n \n-\t\tif (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,\n-\t\t\t\t todo_list.buf.len - offset) < 0) {\n-\t\t\ttodo_list_release(&todo_list);\n-\t\t\treturn -1;\n-\t\t}\n+\t\tMOVE_ARRAY(todo_list->items, todo_list->items + i, todo_list->nr - i);\n+\t\ttodo_list->nr -= i;\n+\t\ttodo_list->current = 0;\n \n-\t\ttodo_list.current = i;\n-\t\tif (is_fixup(peek_command(&todo_list, 0)))\n-\t\t\trecord_in_rewritten(output_oid, peek_command(&todo_list, 0));\n+\t\tif (is_fixup(peek_command(todo_list, 0)))\n+\t\t\trecord_in_rewritten(base_oid, peek_command(todo_list, 0));\n \t}\n \n-\ttodo_list_release(&todo_list);\n-\n \treturn 0;\n }\n \n@@ -4860,6 +4814,11 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn -1;\n \t}\n \n+\tif (opts->allow_ff && skip_unnecessary_picks(r, &new_todo, &oid)) {\n+\t\ttodo_list_release(&new_todo);\n+\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n+\t}\n+\n \tif (todo_list_write_to_file(r, &new_todo, todo_file, NULL, NULL, -1,\n \t\t\t\t    flags & ~(TODO_LIST_SHORTEN_IDS))) {\n \t\ttodo_list_release(&new_todo);\n@@ -4868,9 +4827,6 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \n \ttodo_list_release(&new_todo);\n \n-\tif (opts->allow_ff && skip_unnecessary_picks(r, &oid))\n-\t\treturn error(_(\"could not skip unnecessary pick commands\"));\n-\n \tif (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))\n \t\treturn -1;\n \n-- \n2.20.1\n\n"},{"id":"370743","messageId":"20190305191805.13561-15-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 14/18] rebase-interactive: use todo_list_write_to_file() in edit_todo_list()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:18:01Z","receivedAt":"2019-03-05T19:18:55Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"Just like complete_action(), edit_todo_list() used a\nfunction (transform_todo_file()) that read the todo list from the disk\nand wrote it back, resulting in useless disk accesses.\n\nThis changes edit_todo_list() to call directly todo_list_write_to_file()\ninstead.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v7.\n\n rebase-interactive.c | 38 ++++++++++++++++----------------------\n sequencer.c          |  4 ++--\n sequencer.h          |  3 ---\n 3 files changed, 18 insertions(+), 27 deletions(-)\n\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex dfa6dd530f..d396ecc599 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -79,39 +79,33 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \n int edit_todo_list(struct repository *r, unsigned flags)\n {\n-\tstruct strbuf buf = STRBUF_INIT;\n \tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res = 0;\n \n-\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n \t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n \n-\tstrbuf_stripspace(&buf, 1);\n-\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n-\t\tstrbuf_release(&buf);\n+\tstrbuf_stripspace(&todo_list.buf, 1);\n+\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n+\tif (todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n+\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP)) {\n+\t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n \n-\tstrbuf_release(&buf);\n-\n-\ttransform_todo_file(r, flags | TODO_LIST_SHORTEN_IDS);\n-\n-\tif (strbuf_read_file(&buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tappend_todo_help(1, 0, &buf);\n-\tif (write_message(buf.buf, buf.len, todo_file, 0)) {\n-\t\tstrbuf_release(&buf);\n+\tstrbuf_reset(&todo_list.buf);\n+\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n+\t\ttodo_list_release(&todo_list);\n \t\treturn -1;\n \t}\n \n-\tstrbuf_release(&buf);\n+\tif (!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list))\n+\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n+\t\t\t\t\t      flags & ~(TODO_LIST_SHORTEN_IDS));\n \n-\tif (launch_sequence_editor(todo_file, NULL, NULL))\n-\t\treturn -1;\n-\n-\ttransform_todo_file(r, flags & ~(TODO_LIST_SHORTEN_IDS));\n-\n-\treturn 0;\n+\ttodo_list_release(&todo_list);\n+\treturn res;\n }\n \n define_commit_slab(commit_seen, unsigned char);\ndiff --git a/sequencer.c b/sequencer.c\nindex 894c7538d5..b7289c93d4 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -383,8 +383,8 @@ static void print_advice(struct repository *r, int show_hint,\n \t}\n }\n \n-int write_message(const void *buf, size_t len, const char *filename,\n-\t\t  int append_eol)\n+static int write_message(const void *buf, size_t len, const char *filename,\n+\t\t\t int append_eol)\n {\n \tstruct lock_file msg_file = LOCK_INIT;\n \ndiff --git a/sequencer.h b/sequencer.h\nindex 13c5676c24..fb31a30d15 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -64,9 +64,6 @@ struct replay_opts {\n };\n #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }\n \n-int write_message(const void *buf, size_t len, const char *filename,\n-\t\t  int append_eol);\n-\n /*\n  * Note that ordering matters in this enum. Not only must it match the mapping\n  * of todo_command_info (in sequencer.c), it is also divided into several\n-- \n2.20.1\n\n"},{"id":"370744","messageId":"20190305191805.13561-16-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 15/18] rebase-interactive: append_todo_help() changes","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:18:02Z","receivedAt":"2019-03-05T19:18:57Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This moves the writing of the comment \"Rebase $shortrevisions onto\n$shortonto ($command_count commands)\" from todo_list_write_to_file() to\nappend_todo_help().\n\nshortrevisions, shortonto, and command_count are passed as parameters to\nappend_todo_help().\n\nDuring the initial edit of the todo list, shortrevisions and shortonto\nare not NULL.  Therefore, if shortrevisions or shortonto is NULL, then\nedit_todo would be true, otherwise it would be false.  Thus, edit_todo\nis removed from the parameters of append_todo_help().\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v7.\n\n rebase-interactive.c | 12 +++++++++++-\n rebase-interactive.h |  3 ++-\n sequencer.c          | 17 ++++-------------\n 3 files changed, 17 insertions(+), 15 deletions(-)\n\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex d396ecc599..807f8370db 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -28,7 +28,8 @@ static enum missing_commit_check_level get_missing_commit_check_level(void)\n \treturn MISSING_COMMIT_CHECK_IGNORE;\n }\n \n-void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n+void append_todo_help(unsigned keep_empty, int command_count,\n+\t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf)\n {\n \tconst char *msg = _(\"\\nCommands:\\n\"\n@@ -48,6 +49,15 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n \".       specified). Use -c <commit> to reword the commit message.\\n\"\n \"\\n\"\n \"These lines can be re-ordered; they are executed from top to bottom.\\n\");\n+\tunsigned edit_todo = !(shortrevisions && shortonto);\n+\n+\tif (!edit_todo) {\n+\t\tstrbuf_addch(buf, '\\n');\n+\t\tstrbuf_commented_addf(buf, Q_(\"Rebase %s onto %s (%d command)\",\n+\t\t\t\t\t      \"Rebase %s onto %s (%d commands)\",\n+\t\t\t\t\t      command_count),\n+\t\t\t\t      shortrevisions, shortonto, command_count);\n+\t}\n \n \tstrbuf_add_commented_lines(buf, msg, strlen(msg));\n \ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 187b5032d6..0e5925e3aa 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -5,7 +5,8 @@ struct strbuf;\n struct repository;\n struct todo_list;\n \n-void append_todo_help(unsigned edit_todo, unsigned keep_empty,\n+void append_todo_help(unsigned keep_empty, int command_count,\n+\t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf);\n int edit_todo_list(struct repository *r, unsigned flags);\n int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\ndiff --git a/sequencer.c b/sequencer.c\nindex b7289c93d4..8f3836c479 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4619,22 +4619,13 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n \t\t\t    const char *file, const char *shortrevisions,\n \t\t\t    const char *shortonto, int num, unsigned flags)\n {\n-\tint edit_todo = !(shortrevisions && shortonto), res;\n+\tint res;\n \tstruct strbuf buf = STRBUF_INIT;\n \n \ttodo_list_to_strbuf(r, todo_list, &buf, num, flags);\n-\n-\tif (flags & TODO_LIST_APPEND_TODO_HELP) {\n-\t\tint command_count = count_commands(todo_list);\n-\t\tif (!edit_todo) {\n-\t\t\tstrbuf_addch(&buf, '\\n');\n-\t\t\tstrbuf_commented_addf(&buf, Q_(\"Rebase %s onto %s (%d command)\",\n-\t\t\t\t\t\t       \"Rebase %s onto %s (%d commands)\",\n-\t\t\t\t\t\t       command_count),\n-\t\t\t\t\t      shortrevisions, shortonto, command_count);\n-\t\t}\n-\t\tappend_todo_help(edit_todo, flags & TODO_LIST_KEEP_EMPTY, &buf);\n-\t}\n+\tif (flags & TODO_LIST_APPEND_TODO_HELP)\n+\t\tappend_todo_help(flags & TODO_LIST_KEEP_EMPTY, count_commands(todo_list),\n+\t\t\t\t shortrevisions, shortonto, &buf);\n \n \tres = write_message(buf.buf, buf.len, file, 0);\n \tstrbuf_release(&buf);\n-- \n2.20.1\n\n"},{"id":"370745","messageId":"20190305191805.13561-17-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 16/18] rebase-interactive: rewrite edit_todo_list() to handle the initial edit","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:18:03Z","receivedAt":"2019-03-05T19:18:58Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"edit_todo_list() is changed to work on a todo_list, and to handle the\ninitial edition of the todo list (ie. making a backup of the todo\nlist).\n\nIt does not check for dropped commits yet, as todo_list_check() does not\ntake the commits that have already been processed by the rebase (ie. the\ntodo list is edited in the middle of a rebase session).\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\n builtin/rebase--interactive.c | 24 +++++++++++++++-\n rebase-interactive.c          | 53 +++++++++++++++++++----------------\n rebase-interactive.h          |  4 ++-\n sequencer.c                   |  3 +-\n sequencer.h                   |  1 +\n 5 files changed, 57 insertions(+), 28 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex ab2c6fcd99..b277239f21 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -64,6 +64,28 @@ static int rearrange_squash_in_todo_file(void)\n \treturn 0;\n }\n \n+static int edit_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT,\n+\t\tnew_todo = TODO_LIST_INIT;\n+\tint res = 0;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tstrbuf_stripspace(&todo_list.buf, 1);\n+\tres = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);\n+\tif (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,\n+\t\t\t\t\t    NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))\n+\t\tres = error_errno(_(\"could not write '%s'\"), todo_file);\n+\n+\ttodo_list_release(&todo_list);\n+\ttodo_list_release(&new_todo);\n+\n+\treturn res;\n+}\n+\n static int get_revision_ranges(const char *upstream, const char *onto,\n \t\t\t       const char **head_hash,\n \t\t\t       char **revisions, char **shortrevisions)\n@@ -295,7 +317,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t\tbreak;\n \t}\n \tcase EDIT_TODO:\n-\t\tret = edit_todo_list(the_repository, flags);\n+\t\tret = edit_todo_file(flags);\n \t\tbreak;\n \tcase SHOW_CURRENT_PATCH: {\n \t\tstruct child_process cmd = CHILD_PROCESS_INIT;\ndiff --git a/rebase-interactive.c b/rebase-interactive.c\nindex 807f8370db..aa18ae82b7 100644\n--- a/rebase-interactive.c\n+++ b/rebase-interactive.c\n@@ -87,35 +87,40 @@ void append_todo_help(unsigned keep_empty, int command_count,\n \t}\n }\n \n-int edit_todo_list(struct repository *r, unsigned flags)\n+int edit_todo_list(struct repository *r, struct todo_list *todo_list,\n+\t\t   struct todo_list *new_todo, const char *shortrevisions,\n+\t\t   const char *shortonto, unsigned flags)\n {\n \tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res = 0;\n-\n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tstrbuf_stripspace(&todo_list.buf, 1);\n-\ttodo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list);\n-\tif (todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n-\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\tunsigned initial = shortrevisions && shortonto;\n \n-\tstrbuf_reset(&todo_list.buf);\n-\tif (launch_sequence_editor(todo_file, &todo_list.buf, NULL)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn -1;\n-\t}\n+\t/* If the user is editing the todo list, we first try to parse\n+\t * it.  If there is an error, we do not return, because the user\n+\t * might want to fix it in the first place. */\n+\tif (!initial)\n+\t\ttodo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n \n-\tif (!todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list))\n-\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1,\n-\t\t\t\t\t      flags & ~(TODO_LIST_SHORTEN_IDS));\n+\tif (todo_list_write_to_file(r, todo_list, todo_file, shortrevisions, shortonto,\n+\t\t\t\t    -1, flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))\n+\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n \n-\ttodo_list_release(&todo_list);\n-\treturn res;\n+\tif (initial && copy_file(rebase_path_todo_backup(), todo_file, 0666))\n+\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n+\t\t\t     rebase_path_todo_backup());\n+\n+\tif (launch_sequence_editor(todo_file, &new_todo->buf, NULL))\n+\t\treturn -2;\n+\n+\tstrbuf_stripspace(&new_todo->buf, 1);\n+\tif (initial && new_todo->buf.len == 0)\n+\t\treturn -3;\n+\n+\t/* For the initial edit, the todo list gets parsed in\n+\t * complete_action(). */\n+\tif (!initial)\n+\t\treturn todo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo);\n+\n+\treturn 0;\n }\n \n define_commit_slab(commit_seen, unsigned char);\ndiff --git a/rebase-interactive.h b/rebase-interactive.h\nindex 0e5925e3aa..44dbb06311 100644\n--- a/rebase-interactive.h\n+++ b/rebase-interactive.h\n@@ -8,7 +8,9 @@ struct todo_list;\n void append_todo_help(unsigned keep_empty, int command_count,\n \t\t      const char *shortrevisions, const char *shortonto,\n \t\t      struct strbuf *buf);\n-int edit_todo_list(struct repository *r, unsigned flags);\n+int edit_todo_list(struct repository *r, struct todo_list *todo_list,\n+\t\t   struct todo_list *new_todo, const char *shortrevisions,\n+\t\t   const char *shortonto, unsigned flags);\n int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);\n \n #endif\ndiff --git a/sequencer.c b/sequencer.c\nindex 8f3836c479..7d46f76a8b 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -55,8 +55,7 @@ static GIT_PATH_FUNC(rebase_path, \"rebase-merge\")\n  * file and written to the tail of 'done'.\n  */\n GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n-static GIT_PATH_FUNC(rebase_path_todo_backup,\n-\t\t     \"rebase-merge/git-rebase-todo.backup\")\n+GIT_PATH_FUNC(rebase_path_todo_backup, \"rebase-merge/git-rebase-todo.backup\")\n \n /*\n  * The rebase command lines that have already been processed. A line\ndiff --git a/sequencer.h b/sequencer.h\nindex fb31a30d15..195891a267 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -10,6 +10,7 @@ struct repository;\n const char *git_path_commit_editmsg(void);\n const char *git_path_seq_dir(void);\n const char *rebase_path_todo(void);\n+const char *rebase_path_todo_backup(void);\n \n #define APPEND_SIGNOFF_DEDUP (1u << 0)\n \n-- \n2.20.1\n\n"},{"id":"370746","messageId":"20190305191805.13561-18-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 17/18] sequencer: use edit_todo_list() in complete_action()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:18:04Z","receivedAt":"2019-03-05T19:19:00Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"This changes complete_action() to use edit_todo_list(), now that it can\nhandle the initial edit of the todo list.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNo changes since v7.\n\n sequencer.c | 21 +++++++--------------\n 1 file changed, 7 insertions(+), 14 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 7d46f76a8b..c56c3add1a 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4746,6 +4746,7 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \tstruct todo_list new_todo = TODO_LIST_INIT;\n \tstruct strbuf *buf = &todo_list->buf;\n \tstruct object_id oid;\n+\tint res;\n \n \tget_oid(onto, &oid);\n \tshortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);\n@@ -4770,24 +4771,16 @@ int complete_action(struct repository *r, struct replay_opts *opts, unsigned fla\n \t\treturn error(_(\"nothing to do\"));\n \t}\n \n-\tif (todo_list_write_to_file(r, todo_list, todo_file,\n-\t\t\t\t    shortrevisions, shortonto, -1,\n-\t\t\t\t    flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))\n-\t\treturn error_errno(_(\"could not write '%s'\"), todo_file);\n-\n-\tif (copy_file(rebase_path_todo_backup(), todo_file, 0666))\n-\t\treturn error(_(\"could not copy '%s' to '%s'.\"), todo_file,\n-\t\t\t     rebase_path_todo_backup());\n-\n-\tif (launch_sequence_editor(todo_file, &new_todo.buf, NULL)) {\n+\tres = edit_todo_list(r, todo_list, &new_todo, shortrevisions,\n+\t\t\t     shortonto, flags);\n+\tif (res == -1)\n+\t\treturn -1;\n+\telse if (res == -2) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n \n \t\treturn -1;\n-\t}\n-\n-\tstrbuf_stripspace(&new_todo.buf, 1);\n-\tif (new_todo.buf.len == 0) {\n+\t} else if (res == -3) {\n \t\tapply_autostash(opts);\n \t\tsequencer_remove_state(opts);\n \t\ttodo_list_release(&new_todo);\n-- \n2.20.1\n\n"},{"id":"370747","messageId":"20190305191805.13561-19-alban.gruin@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"[PATCH v8 18/18] rebase--interactive: move transform_todo_file()","fromName":"Alban Gruin","fromEmail":"alban.gruin@gmail.com","sentAt":"2019-03-05T19:18:05Z","receivedAt":"2019-03-05T19:19:02Z","isPatch":true,"sender":{"key":"alban.gruin@gmail.com","avatar":"https://avatars.githubusercontent.com/u/6310153?v=4"},"body":"As transform_todo_file() is only needed inside of\nrebase--interactive.c for `rebase -p', it is moved there from\nsequencer.c.\n\nThe parameter r (repository) is dropped along the way.\n\nSigned-off-by: Alban Gruin <alban.gruin@gmail.com>\n---\nNew commit, but was a part of \"rebase--interactive: move several\nfunctions to rebase--interactive.c\" from the v7.\n\n builtin/rebase--interactive.c | 26 +++++++++++++++++++++++++-\n sequencer.c                   | 23 -----------------------\n sequencer.h                   |  1 -\n 3 files changed, 25 insertions(+), 25 deletions(-)\n\ndiff --git a/builtin/rebase--interactive.c b/builtin/rebase--interactive.c\nindex b277239f21..4d9c1e62bb 100644\n--- a/builtin/rebase--interactive.c\n+++ b/builtin/rebase--interactive.c\n@@ -64,6 +64,30 @@ static int rearrange_squash_in_todo_file(void)\n \treturn 0;\n }\n \n+static int transform_todo_file(unsigned flags)\n+{\n+\tconst char *todo_file = rebase_path_todo();\n+\tstruct todo_list todo_list = TODO_LIST_INIT;\n+\tint res;\n+\n+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n+\n+\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n+\t\t\t\t\t&todo_list)) {\n+\t\ttodo_list_release(&todo_list);\n+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n+\t}\n+\n+\tres = todo_list_write_to_file(the_repository, &todo_list, todo_file,\n+\t\t\t\t      NULL, NULL, -1, flags);\n+\ttodo_list_release(&todo_list);\n+\n+\tif (res)\n+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n+\treturn 0;\n+}\n+\n static int edit_todo_file(unsigned flags)\n {\n \tconst char *todo_file = rebase_path_todo();\n@@ -330,7 +354,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n \t}\n \tcase SHORTEN_OIDS:\n \tcase EXPAND_OIDS:\n-\t\tret = transform_todo_file(the_repository, flags);\n+\t\tret = transform_todo_file(flags);\n \t\tbreak;\n \tcase CHECK_TODO_LIST:\n \t\tret = check_todo_list_from_file(the_repository);\ndiff --git a/sequencer.c b/sequencer.c\nindex c56c3add1a..2a0fcb1cce 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -4632,29 +4632,6 @@ int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,\n \treturn res;\n }\n \n-int transform_todo_file(struct repository *r, unsigned flags)\n-{\n-\tconst char *todo_file = rebase_path_todo();\n-\tstruct todo_list todo_list = TODO_LIST_INIT;\n-\tint res;\n-\n-\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n-\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n-\n-\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n-\t\ttodo_list_release(&todo_list);\n-\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n-\t}\n-\n-\tres = todo_list_write_to_file(r, &todo_list, todo_file,\n-\t\t\t\t      NULL, NULL, -1, flags);\n-\ttodo_list_release(&todo_list);\n-\n-\tif (res)\n-\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n-\treturn 0;\n-}\n-\n static const char edit_todo_list_advice[] =\n N_(\"You can fix this with 'git rebase --edit-todo' \"\n \"and then run 'git rebase --continue'.\\n\"\ndiff --git a/sequencer.h b/sequencer.h\nindex 195891a267..7cca49eff2 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -143,7 +143,6 @@ int sequencer_remove_state(struct replay_opts *opts);\n int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \t\t\t  const char **argv, unsigned flags);\n \n-int transform_todo_file(struct repository *r, unsigned flags);\n void todo_list_add_exec_commands(struct todo_list *todo_list,\n \t\t\t\t struct string_list *commands);\n int check_todo_list_from_file(struct repository *r);\n-- \n2.20.1\n\n"},{"id":"371347","messageId":"133c90ed-b170-a65c-67cd-1e8d53d2fbe9@gmail.com","threadId":"49504","inReplyTo":"20190305191805.13561-1-alban.gruin@gmail.com","subject":"Re: [PATCH v8 00/18] sequencer: refactor functions working on a todo_list","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2019-03-13T10:45:38Z","receivedAt":"2019-03-13T10:45:45Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Alban\n\nThanks for working on this. The range and inter diffs look fine, I think \nthis is ready for next now.\n\nBest Wishes\n\nPhillip\n\nOn 05/03/2019 19:17, Alban Gruin wrote:\n> At the center of the \"interactive\" part of the interactive rebase lies\n> the todo list.  When the user starts an interactive rebase, a todo list\n> is generated, presented to the user (who then edits it using a text\n> editor), read back, and then is checked and processed before the actual\n> rebase takes place.\n> \n> Some of this processing includes adding execs commands, reordering\n> fixup! and squash! commits, and checking if no commits were accidentally\n> dropped by the user.\n> \n> Before I converted the interactive rebase in C, these functions were\n> called by git-rebase--interactive.sh through git-rebase--helper.  Since\n> the only way to pass around a large amount of data between a shell\n> script and a C program is to use a file (or any declination of a file),\n> the functions that checked and processed the todo list were directly\n> working on a file, the same file that the user edited.\n> \n> During the conversion, I did not address this issue, which lead to a\n> complete_action() that reads the todo list file, does some computation\n> based on its content, and writes it back to the disk, several times in\n> the same function.\n> \n> As it is not an efficient way to handle a data structure, this patch\n> series refactor the functions that processes the todo list to work on a\n> todo_list structure instead of reading it from the disk.\n> \n> Some commits consists in modifying edit_todo_list() (initially used by\n> --edit-todo) to handle the initial edition of the todo list, to increase\n> code sharing.\n> \n> This is based on nd/the-index (cde555480b, \"Merge branch\n> 'nd/the-index'\").\n> \n> Changes since v7:\n> \n>   - Add some comments to clarify some assumptions.\n> \n>   - Use `string_list_remove_empty_items()` instead of `--commands.nr` to\n>     remove empty commands.\n> \n>   - Split the last commit (\"rebase--interactive: move several functions\n>     to rebase--interactive.c\") into three, one for each function.\n> \n> The tip of this series is tagged as \"refactor-todo-list-v8\" in\n> https://github.com/agrn/git.\n> \n> The range diff is included below.\n> \n> Alban Gruin (18):\n>    sequencer: changes in parse_insn_buffer()\n>    sequencer: make the todo_list structure public\n>    sequencer: remove the 'arg' field from todo_item\n>    sequencer: refactor transform_todos() to work on a todo_list\n>    sequencer: introduce todo_list_write_to_file()\n>    sequencer: refactor check_todo_list() to work on a todo_list\n>    sequencer: refactor sequencer_add_exec_commands() to work on a\n>      todo_list\n>    sequencer: refactor rearrange_squash() to work on a todo_list\n>    sequencer: make sequencer_make_script() write its script to a strbuf\n>    sequencer: change complete_action() to use the refactored functions\n>    rebase--interactive: move sequencer_add_exec_commands()\n>    rebase--interactive: move rearrange_squash_in_todo_file()\n>    sequencer: refactor skip_unnecessary_picks() to work on a todo_list\n>    rebase-interactive: use todo_list_write_to_file() in edit_todo_list()\n>    rebase-interactive: append_todo_help() changes\n>    rebase-interactive: rewrite edit_todo_list() to handle the initial\n>      edit\n>    sequencer: use edit_todo_list() in complete_action()\n>    rebase--interactive: move transform_todo_file()\n> \n>   builtin/rebase--interactive.c | 144 ++++++--\n>   rebase-interactive.c          | 146 ++++++--\n>   rebase-interactive.h          |   9 +-\n>   sequencer.c                   | 655 ++++++++++++----------------------\n>   sequencer.h                   |  81 ++++-\n>   5 files changed, 553 insertions(+), 482 deletions(-)\n> \n> Range-diff against v7:\n>   1:  0bc5f714e5 =  1:  0bc5f714e5 sequencer: changes in parse_insn_buffer()\n>   2:  34d149ff25 =  2:  34d149ff25 sequencer: make the todo_list structure public\n>   3:  8200f7a6be =  3:  8200f7a6be sequencer: remove the 'arg' field from todo_item\n>   4:  ce8ca23ee0 =  4:  ce8ca23ee0 sequencer: refactor transform_todos() to work on a todo_list\n>   5:  67ebea475e =  5:  67ebea475e sequencer: introduce todo_list_write_to_file()\n>   6:  370c153ebe =  6:  370c153ebe sequencer: refactor check_todo_list() to work on a todo_list\n>   7:  66e7b65509 !  7:  95ae0f36d2 sequencer: refactor sequencer_add_exec_commands() to work on a todo_list\n>      @@ -60,7 +60,10 @@\n>        \n>       +\tif (cmd && *cmd) {\n>       +\t\tstring_list_split(&commands, cmd, '\\n', -1);\n>      -+\t\t--commands.nr;\n>      ++\n>      ++\t\t/* rebase.c adds a new line to cmd after every command,\n>      ++\t\t * so here the last command is always empty */\n>      ++\t\tstring_list_remove_empty_items(&commands, 0);\n>       +\t}\n>       +\n>        \tswitch (command) {\n>      @@ -86,7 +89,7 @@\n>        \t\tBUG(\"invalid command '%d'\", command);\n>        \t}\n>        \n>      -+\tstring_list_clear(&commands, 1);\n>      ++\tstring_list_clear(&commands, 0);\n>        \treturn !!ret;\n>        }\n>       \n>      @@ -133,9 +136,16 @@\n>        \t}\n>        \n>        \t/*\n>      -@@\n>      + \t * Insert <commands> after every pick. Here, fixup/squash chains\n>        \t * are considered part of the pick, so we insert the commands *after*\n>        \t * those chains if there are any.\n>      ++\t *\n>      ++\t * As we insert the exec commands immediatly after rearranging\n>      ++\t * any fixups and before the user edits the list, a fixup chain\n>      ++\t * can never contain comments (any comments are empty picks that\n>      ++\t * have been commented out because the user did not specify\n>      ++\t * --keep-empty).  So, it is safe to insert an exec command\n>      ++\t * without looking at the command following a comment.\n>        \t */\n>       -\tinsert = -1;\n>       -\tfor (i = 0; i < todo_list.nr; i++) {\n>      @@ -165,12 +175,11 @@\n>       +\t\t\tinsert = 0;\n>        \t\t}\n>        \n>      --\t\tif (command == TODO_PICK || command == TODO_MERGE)\n>      --\t\t\tinsert = i + 1;\n>       +\t\tALLOC_GROW(items, nr + 1, alloc);\n>       +\t\titems[nr++] = todo_list->items[i];\n>       +\n>      -+\t\tif (command == TODO_PICK || command == TODO_MERGE || is_fixup(command))\n>      + \t\tif (command == TODO_PICK || command == TODO_MERGE)\n>      +-\t\t\tinsert = i + 1;\n>       +\t\t\tinsert = 1;\n>        \t}\n>        \n>      @@ -192,8 +201,7 @@\n>       +\ttodo_list->nr = nr;\n>       +\ttodo_list->alloc = alloc;\n>       +}\n>      -\n>      --\ti = write_message(buf->buf, buf->len, todo_file, 0);\n>      ++\n>       +int sequencer_add_exec_commands(struct repository *r,\n>       +\t\t\t\tstruct string_list *commands)\n>       +{\n>      @@ -203,7 +211,8 @@\n>       +\n>       +\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>       +\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n>      -+\n>      +\n>      +-\ti = write_message(buf->buf, buf->len, todo_file, 0);\n>       +\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n>       +\t\ttodo_list_release(&todo_list);\n>       +\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n>   8:  640cb7aa54 =  8:  144eb32743 sequencer: refactor rearrange_squash() to work on a todo_list\n>   9:  bef1970c88 =  9:  1501c26317 sequencer: make sequencer_make_script() write its script to a strbuf\n> 10:  48ee37a32a = 10:  121b270c4d sequencer: change complete_action() to use the refactored functions\n> 16:  f57a7405d0 ! 11:  a3763ecb0e rebase--interactive: move several functions to rebase--interactive.c\n>      @@ -1,13 +1,12 @@\n>       Author: Alban Gruin <alban.gruin@gmail.com>\n>       \n>      -    rebase--interactive: move several functions to rebase--interactive.c\n>      +    rebase--interactive: move sequencer_add_exec_commands()\n>       \n>      -    As sequencer_add_exec_commands(), rearrange_squash_in_todo_file(), and\n>      -    transform_todo_file() are only needed inside of rebase--interactive.c\n>      -    for rebase -p, they are moved there from sequencer.c.\n>      +    As sequencer_add_exec_commands() is only needed inside of\n>      +    rebase--interactive.c for `rebase -p', it is moved there from\n>      +    sequencer.c.\n>       \n>      -    The parameter r (repository) is dropped from them, and the error\n>      -    handling of rearrange_squash_in_todo_file() is slightly improved.\n>      +    The parameter r (repository) is dropped along the way.\n>       \n>           Signed-off-by: Alban Gruin <alban.gruin@gmail.com>\n>       \n>      @@ -43,72 +42,11 @@\n>       +\treturn 0;\n>       +}\n>       +\n>      -+static int rearrange_squash_in_todo_file(void)\n>      -+{\n>      -+\tconst char *todo_file = rebase_path_todo();\n>      -+\tstruct todo_list todo_list = TODO_LIST_INIT;\n>      -+\tint res = 0;\n>      -+\n>      -+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>      -+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n>      -+\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n>      -+\t\t\t\t\t&todo_list)) {\n>      -+\t\ttodo_list_release(&todo_list);\n>      -+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n>      -+\t}\n>      -+\n>      -+\tres = todo_list_rearrange_squash(&todo_list);\n>      -+\tif (!res)\n>      -+\t\tres = todo_list_write_to_file(the_repository, &todo_list,\n>      -+\t\t\t\t\t      todo_file, NULL, NULL, -1, 0);\n>      -+\n>      -+\ttodo_list_release(&todo_list);\n>      -+\n>      -+\tif (res)\n>      -+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n>      -+\treturn 0;\n>      -+}\n>      -+\n>      -+static int transform_todo_file(unsigned flags)\n>      -+{\n>      -+\tconst char *todo_file = rebase_path_todo();\n>      -+\tstruct todo_list todo_list = TODO_LIST_INIT;\n>      -+\tint res;\n>      -+\n>      -+\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>      -+\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n>      -+\n>      -+\tif (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,\n>      -+\t\t\t\t\t&todo_list)) {\n>      -+\t\ttodo_list_release(&todo_list);\n>      -+\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n>      -+\t}\n>      -+\n>      -+\tres = todo_list_write_to_file(the_repository, &todo_list, todo_file,\n>      -+\t\t\t\t      NULL, NULL, -1, flags);\n>      -+\ttodo_list_release(&todo_list);\n>      -+\n>      -+\tif (res)\n>      -+\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n>      -+\treturn 0;\n>      -+}\n>      -+\n>      - static int edit_todo_file(unsigned flags)\n>      - {\n>      - \tconst char *todo_file = rebase_path_todo();\n>      + static int get_revision_ranges(const char *upstream, const char *onto,\n>      + \t\t\t       const char **head_hash,\n>      + \t\t\t       char **revisions, char **shortrevisions)\n>       @@\n>      - \t}\n>      - \tcase SHORTEN_OIDS:\n>      - \tcase EXPAND_OIDS:\n>      --\t\tret = transform_todo_file(the_repository, flags);\n>      -+\t\tret = transform_todo_file(flags);\n>      - \t\tbreak;\n>      - \tcase CHECK_TODO_LIST:\n>      - \t\tret = check_todo_list_from_file(the_repository);\n>      - \t\tbreak;\n>      - \tcase REARRANGE_SQUASH:\n>      --\t\tret = rearrange_squash_in_todo_file(the_repository);\n>      -+\t\tret = rearrange_squash_in_todo_file();\n>      + \t\tret = rearrange_squash_in_todo_file(the_repository);\n>        \t\tbreak;\n>        \tcase ADD_EXEC:\n>       -\t\tret = sequencer_add_exec_commands(the_repository, &commands);\n>      @@ -162,82 +100,6 @@\n>        static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,\n>        \t\t\t\tstruct strbuf *buf, int num, unsigned flags)\n>        {\n>      -@@\n>      - \treturn res;\n>      - }\n>      -\n>      --int transform_todo_file(struct repository *r, unsigned flags)\n>      --{\n>      --\tconst char *todo_file = rebase_path_todo();\n>      --\tstruct todo_list todo_list = TODO_LIST_INIT;\n>      --\tint res;\n>      --\n>      --\tif (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)\n>      --\t\treturn error_errno(_(\"could not read '%s'.\"), todo_file);\n>      --\n>      --\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {\n>      --\t\ttodo_list_release(&todo_list);\n>      --\t\treturn error(_(\"unusable todo list: '%s'\"), todo_file);\n>      --\t}\n>      --\n>      --\tres = todo_list_write_to_file(r, &todo_list, todo_file,\n>      --\t\t\t\t      NULL, NULL, -1, flags);\n>      --\ttodo_list_release(&todo_list);\n>      --\n>      --\tif (res)\n>      --\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n>      --\treturn 0;\n>      --}\n>      --\n>      - static const char edit_todo_list_advice[] =\n>      - N_(\"You can fix this with 'git rebase --edit-todo' \"\n>      - \"and then run 'git rebase --continue'.\\n\"\n>      -@@\n>      - \treturn 0;\n>      - }\n>      -\n>      --static int todo_list_rearrange_squash(struct todo_list *todo_list);\n>      --\n>      - int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n>      - \t\t    const char *shortrevisions, const char *onto_name,\n>      - \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n>      -@@\n>      -  * message will have to be retrieved from the commit (as the oneline in the\n>      -  * script cannot be trusted) in order to normalize the autosquash arrangement.\n>      -  */\n>      --static int todo_list_rearrange_squash(struct todo_list *todo_list)\n>      -+int todo_list_rearrange_squash(struct todo_list *todo_list)\n>      - {\n>      - \tstruct hashmap subject2item;\n>      - \tint rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;\n>      -@@\n>      -\n>      - \treturn 0;\n>      - }\n>      --\n>      --int rearrange_squash_in_todo_file(struct repository *r)\n>      --{\n>      --\tconst char *todo_file = rebase_path_todo();\n>      --\tstruct todo_list todo_list = TODO_LIST_INIT;\n>      --\tint res = 0;\n>      --\n>      --\tif (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)\n>      --\t\treturn -1;\n>      --\tif (todo_list_parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {\n>      --\t\ttodo_list_release(&todo_list);\n>      --\t\treturn -1;\n>      --\t}\n>      --\n>      --\tres = todo_list_rearrange_squash(&todo_list);\n>      --\tif (!res)\n>      --\t\tres = todo_list_write_to_file(r, &todo_list, todo_file, NULL, NULL, -1, 0);\n>      --\n>      --\ttodo_list_release(&todo_list);\n>      --\n>      --\tif (res)\n>      --\t\treturn error_errno(_(\"could not write '%s'.\"), todo_file);\n>      --\treturn 0;\n>      --}\n>       \n>        diff --git a/sequencer.h b/sequencer.h\n>        --- a/sequencer.h\n>      @@ -248,16 +110,9 @@\n>        \n>       -int sequencer_add_exec_commands(struct repository *r,\n>       -\t\t\t\tstruct string_list *commands);\n>      --int transform_todo_file(struct repository *r, unsigned flags);\n>      + int transform_todo_file(struct repository *r, unsigned flags);\n>       +void todo_list_add_exec_commands(struct todo_list *todo_list,\n>       +\t\t\t\t struct string_list *commands);\n>        int check_todo_list_from_file(struct repository *r);\n>        int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,\n>        \t\t    const char *shortrevisions, const char *onto_name,\n>      - \t\t    const char *onto, const char *orig_head, struct string_list *commands,\n>      - \t\t    unsigned autosquash, struct todo_list *todo_list);\n>      --int rearrange_squash_in_todo_file(struct repository *r);\n>      -+int todo_list_rearrange_squash(struct todo_list *todo_list);\n>      -\n>      - extern const char sign_off_header[];\n>      -\n>   -:  ---------- > 12:  e03663ed04 rebase--interactive: move rearrange_squash_in_todo_file()\n> 11:  bc89fbfea6 = 13:  d9cf3d7d2f sequencer: refactor skip_unnecessary_picks() to work on a todo_list\n> 12:  a754d0da96 = 14:  bf6cc5978c rebase-interactive: use todo_list_write_to_file() in edit_todo_list()\n> 13:  031e4de856 = 15:  1c9d1cb3c9 rebase-interactive: append_todo_help() changes\n> 14:  268998924b ! 16:  b98b07dae0 rebase-interactive: rewrite edit_todo_list() to handle the initial edit\n>      @@ -16,8 +16,8 @@\n>        --- a/builtin/rebase--interactive.c\n>        +++ b/builtin/rebase--interactive.c\n>       @@\n>      - static GIT_PATH_FUNC(path_squash_onto, \"rebase-merge/squash-onto\")\n>      - static GIT_PATH_FUNC(path_interactive, \"rebase-merge/interactive\")\n>      + \treturn 0;\n>      + }\n>        \n>       +static int edit_todo_file(unsigned flags)\n>       +{\n>      @@ -87,6 +87,9 @@\n>       -\t\ttodo_list_release(&todo_list);\n>       -\t\treturn -1;\n>       -\t}\n>      ++\t/* If the user is editing the todo list, we first try to parse\n>      ++\t * it.  If there is an error, we do not return, because the user\n>      ++\t * might want to fix it in the first place. */\n>       +\tif (!initial)\n>       +\t\ttodo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);\n>        \n>      @@ -110,6 +113,8 @@\n>       +\tif (initial && new_todo->buf.len == 0)\n>       +\t\treturn -3;\n>       +\n>      ++\t/* For the initial edit, the todo list gets parsed in\n>      ++\t * complete_action(). */\n>       +\tif (!initial)\n>       +\t\treturn todo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo);\n>       +\n> 15:  6f1274a33c = 17:  406e971e56 sequencer: use edit_todo_list() in complete_action()\n>   -:  ---------- > 18:  51d44a8200 rebase--interactive: move transform_todo_file()\n> \n"}]}