{"thread":{"id":"43966","subject":"[PATCH 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","startedAt":"2016-08-31T08:54:55Z","lastAt":"2017-01-17T19:50:27Z","messageCount":212,"participants":["Johannes Schindelin","Dennis Kaarsemaker","Junio C Hamano","Kevin Daudt","Linus Torvalds","Johannes Sixt","Jeff King","Stefan Beller","Stephan Beyer","Jacob Keller"],"isPatch":true,"patchVersion":1,"patchTotal":34},"messages":[{"id":"300635","messageId":"5488a031ffe14373b7434d497b7fd2f2e5fe55bd.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:54:42Z","receivedAt":"2016-08-31T08:54:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is a huge patch, and at the same time a huge step forward to\nexecute the performance-critical parts of the interactive rebase in a\nbuiltin command.\n\nSince 'fixup' and 'squash' are not only similar, but also need to know\nabout each other (we want to reduce a series of fixups/squashes into a\nsingle, final commit message edit, from the user's point of view), we\nreally have to implement them both at the same time.\n\nMost of the actual work is done by the existing code path that already\nhandles the \"pick\" and the \"edit\" commands; We added support for other\nfeatures (e.g. to amend the commit message) in the patches leading up to\nthis one, yet there are still quite a few bits in this patch that simply\nwould not make sense as individual patches (such as: determining whether\nthere was anything to \"fix up\" in the \"todo\" script, etc).\n\nIn theory, it would be possible to reuse the fast-forward code path also\nfor the fixup and the squash code paths, but in practice this would make\nthe code less readable. The end result cannot be fast-forwarded anyway,\ntherefore let's just extend the cherry-picking code path for now.\n\nSince the sequencer parses the entire `git-rebase-todo` script in one go,\nfixup or squash commands without a preceding pick can be reported early\n(in git-rebase--interactive, we could only report such errors just before\nexecuting the fixup/squash).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 242 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---\n 1 file changed, 231 insertions(+), 11 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 3bff3d9..3ae5978 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -45,6 +45,35 @@ static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n  */\n static GIT_PATH_FUNC(rebase_path_done, \"rebase-merge/done\")\n /*\n+ * The commit message that is planned to be used for any changes that\n+ * need to be committed following a user interaction.\n+ */\n+static GIT_PATH_FUNC(rebase_path_message, \"rebase-merge/message\")\n+/*\n+ * The file into which is accumulated the suggested commit message for\n+ * squash/fixup commands. When the first of a series of squash/fixups\n+ * is seen, the file is created and the commit message from the\n+ * previous commit and from the first squash/fixup commit are written\n+ * to it. The commit message for each subsequent squash/fixup commit\n+ * is appended to the file as it is processed.\n+ *\n+ * The first line of the file is of the form\n+ *     # This is a combination of $count commits.\n+ * where $count is the number of commits whose messages have been\n+ * written to the file so far (including the initial \"pick\" commit).\n+ * Each time that a commit message is processed, this line is read and\n+ * updated. It is deleted just before the combined commit is made.\n+ */\n+static GIT_PATH_FUNC(rebase_path_squash_msg, \"rebase-merge/message-squash\")\n+/*\n+ * If the current series of squash/fixups has not yet included a squash\n+ * command, then this file exists and holds the commit message of the\n+ * original \"pick\" commit.  (If the series ends without a \"squash\"\n+ * command, then this can be used as the commit message of the combined\n+ * commit without opening the editor.)\n+ */\n+static GIT_PATH_FUNC(rebase_path_fixup_msg, \"rebase-merge/message-fixup\")\n+/*\n  * A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and\n  * GIT_AUTHOR_DATE that will be used for the commit that is currently\n  * being rebased.\n@@ -670,6 +699,8 @@ enum todo_command {\n \tTODO_PICK,\n \tTODO_REVERT,\n \tTODO_EDIT,\n+\tTODO_FIXUP,\n+\tTODO_SQUASH,\n \tTODO_EXEC,\n \tTODO_NOOP\n };\n@@ -678,6 +709,8 @@ static const char *todo_command_strings[] = {\n \t\"pick\",\n \t\"revert\",\n \t\"edit\",\n+\t\"fixup\",\n+\t\"squash\",\n \t\"exec\",\n \t\"noop\"\n };\n@@ -689,16 +722,129 @@ static const char *command_to_string(const enum todo_command command)\n \tdie(\"Unknown command: %d\", command);\n }\n \n+static int is_fixup(enum todo_command command)\n+{\n+\treturn command == TODO_FIXUP || command == TODO_SQUASH;\n+}\n+\n+static const char *nth_for_number(int n)\n+{\n+\tint n1 = n % 10, n10 = n % 100;\n+\n+\tif (n1 == 1 && n10 != 11)\n+\t\treturn \"st\";\n+\tif (n1 == 2 && n10 != 12)\n+\t\treturn \"nd\";\n+\tif (n1 == 3 && n10 != 13)\n+\t\treturn \"rd\";\n+\treturn \"th\";\n+}\n+\n+static int update_squash_messages(enum todo_command command,\n+\t\tstruct commit *commit, struct replay_opts *opts)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint count;\n+\tconst char *message, *body;\n+\n+\tif (file_exists(rebase_path_squash_msg())) {\n+\t\tchar *p, *p2;\n+\n+\t\tif (strbuf_read_file(&buf, rebase_path_squash_msg(), 2048) <= 0)\n+\t\t\treturn error(\"Could not read %s\",\n+\t\t\t\trebase_path_squash_msg());\n+\n+\t\tif (buf.buf[0] == '\\n' || !skip_prefix(buf.buf + 1,\n+\t\t\t\t\" This is a combination of \",\n+\t\t\t\t(const char **)&p))\n+\t\t\treturn error(\"Unexpected 1st line of squash message:\\n\"\n+\t\t\t\t\"\\n\\t%.*s\",\n+\t\t\t\t(int)(strchrnul(buf.buf, '\\n') - buf.buf),\n+\t\t\t\tbuf.buf);\n+\t\tcount = strtol(p, &p2, 10);\n+\n+\t\tif (count < 1 || *p2 != ' ')\n+\t\t\treturn error(\"Invalid 1st line of squash message:\\n\"\n+\t\t\t\t\"\\n\\t%.*s\",\n+\t\t\t\t(int)(strchrnul(buf.buf, '\\n') - buf.buf),\n+\t\t\t\tbuf.buf);\n+\n+\t\tsprintf((char *)p, \"%d\", ++count);\n+\t\tif (!*p2)\n+\t\t\t*p2 = ' ';\n+\t\telse {\n+\t\t\t*(++p2) = 'c';\n+\t\t\tstrbuf_insert(&buf, p2 - buf.buf, \" \", 1);\n+\t\t}\n+\t}\n+\telse {\n+\t\tunsigned char head[20];\n+\t\tstruct commit *head_commit;\n+\t\tconst char *head_message, *body;\n+\n+\t\tif (get_sha1(\"HEAD\", head))\n+\t\t\treturn error(\"Need a HEAD to fixup\");\n+\t\tif (!(head_commit = lookup_commit_reference(head)))\n+\t\t\treturn error(\"Could not read HEAD\");\n+\t\tif (!(head_message = get_commit_buffer(head_commit, NULL)))\n+\t\t\treturn error(\"Could not read HEAD's commit message\");\n+\n+\t\tbody = strstr(head_message, \"\\n\\n\");\n+\t\tif (!body)\n+\t\t\tbody = \"\";\n+\t\telse\n+\t\t\tbody = skip_blank_lines(body + 2);\n+\t\tif (write_file_gently(rebase_path_fixup_msg(), body, 0))\n+\t\t\treturn error(\"Cannot write %s\",\n+\t\t\t\trebase_path_fixup_msg());\n+\n+\t\tcount = 2;\n+\t\tstrbuf_addf(&buf, \"%c This is a combination of 2 commits.\\n\"\n+\t\t\t\"%c The first commit's message is:\\n\\n%s\",\n+\t\t\tcomment_line_char, comment_line_char, body);\n+\n+\t\tunuse_commit_buffer(head_commit, head_message);\n+\t}\n+\n+\tif (!(message = get_commit_buffer(commit, NULL)))\n+\t\treturn error(\"Could not read commit message of %s\",\n+\t\t\toid_to_hex(&commit->object.oid));\n+\tbody = strstr(message, \"\\n\\n\");\n+\tif (!body)\n+\t\tbody = \"\";\n+\telse\n+\t\tbody = skip_blank_lines(body + 2);\n+\n+\tif (command == TODO_SQUASH) {\n+\t\tunlink(rebase_path_fixup_msg());\n+\t\tstrbuf_addf(&buf, \"\\n%c This is the %d%s commit message:\\n\\n%s\",\n+\t\t\tcomment_line_char,\n+\t\t\tcount, nth_for_number(count), body);\n+\t}\n+\telse if (command == TODO_FIXUP) {\n+\t\tstrbuf_addf(&buf,\n+\t\t\t\"\\n%c The %d%s commit message will be skipped:\\n\\n\",\n+\t\t\tcomment_line_char, count, nth_for_number(count));\n+\t\tstrbuf_add_commented_lines(&buf, body, strlen(body));\n+\t}\n+\telse\n+\t\treturn error(\"Unknown command: %d\", command);\n+\tunuse_commit_buffer(commit, message);\n+\n+\treturn write_message(&buf, rebase_path_squash_msg());\n+}\n \n static int do_pick_commit(enum todo_command command, struct commit *commit,\n-\t\tstruct replay_opts *opts)\n+\t\tstruct replay_opts *opts, int final_fixup)\n {\n+\tint edit = opts->edit, cleanup_commit_message = 0;\n+\tconst char *msg_file = edit ? NULL : git_path_merge_msg();\n \tunsigned char head[20];\n \tstruct commit *base, *next, *parent;\n \tconst char *base_label, *next_label;\n \tstruct commit_message msg = { NULL, NULL, NULL, NULL };\n \tstruct strbuf msgbuf = STRBUF_INIT;\n-\tint res = 0, unborn = 0, allow;\n+\tint res = 0, unborn = 0, amend = 0, allow;\n \n \tif (opts->no_commit) {\n \t\t/*\n@@ -744,7 +890,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \telse\n \t\tparent = commit->parents->item;\n \n-\tif (opts->allow_ff &&\n+\tif (opts->allow_ff && !is_fixup(command) &&\n \t    ((parent && !hashcmp(parent->object.oid.hash, head)) ||\n \t     (!parent && unborn)))\n \t\treturn fast_forward_to(commit->object.oid.hash, head, unborn, opts);\n@@ -808,6 +954,28 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t}\n \t}\n \n+\tif (is_fixup(command)) {\n+\t\tif (update_squash_messages(command, commit, opts))\n+\t\t\treturn -1;\n+\t\tamend = 1;\n+\t\tif (!final_fixup)\n+\t\t\tmsg_file = rebase_path_squash_msg();\n+\t\telse if (file_exists(rebase_path_fixup_msg())) {\n+\t\t\tcleanup_commit_message = 1;\n+\t\t\tmsg_file = rebase_path_fixup_msg();\n+\t\t}\n+\t\telse {\n+\t\t\tconst char *dest = git_path(\"SQUASH_MSG\");\n+\t\t\tunlink(dest);\n+\t\t\tif (copy_file(dest, rebase_path_squash_msg(), 0666))\n+\t\t\t\treturn error(\"Could not rename %s to \"\n+\t\t\t\t\t\"%s\", rebase_path_squash_msg(), dest);\n+\t\t\tunlink(git_path(\"MERGE_MSG\"));\n+\t\t\tmsg_file = dest;\n+\t\t\tedit = 1;\n+\t\t}\n+\t}\n+\n \tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n \t\tres |= do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n@@ -859,9 +1027,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tgoto leave;\n \t}\n \tif (!opts->no_commit)\n-\t\tres |= sequencer_commit(opts->edit ?\n-\t\t\t\tNULL : git_path_merge_msg(),\n-\t\t\topts, allow, opts->edit, 0, 0);\n+\t\tres |= sequencer_commit(msg_file, opts, allow, edit, amend,\n+\t\t\tcleanup_commit_message);\n+\n+\tif (!res && final_fixup) {\n+\t\tunlink(rebase_path_fixup_msg());\n+\t\tunlink(rebase_path_squash_msg());\n+\t}\n \n leave:\n \tfree_message(commit, &msg);\n@@ -1001,7 +1173,7 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n {\n \tstruct todo_item *item;\n \tchar *p = buf;\n-\tint i, res = 0;\n+\tint i, res = 0, fixup_okay = file_exists(rebase_path_done());\n \n \tfor (i = 1; *p; i++) {\n \t\tchar *eol = strchrnul(p, '\\n');\n@@ -1011,8 +1183,15 @@ 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 = -1;\n+\t\t\titem->command = TODO_NOOP;\n \t\t}\n+\t\tif (fixup_okay)\n+\t\t\t; /* do nothing */\n+\t\telse if (is_fixup(item->command))\n+\t\t\treturn error(\"Cannot '%s' without a previous commit\",\n+\t\t\t\tcommand_to_string(item->command));\n+\t\telse if (item->command != TODO_NOOP)\n+\t\t\tfixup_okay = 1;\n \t\tp = *eol ? eol + 1 : eol;\n \t}\n \tif (!todo_list->nr)\n@@ -1406,6 +1585,20 @@ static int error_with_patch(struct commit *commit,\n \treturn exit_code;\n }\n \n+static int error_failed_squash(struct commit *commit,\n+\tstruct replay_opts *opts, int subject_len, const char *subject)\n+{\n+\tif (rename(rebase_path_squash_msg(), rebase_path_message()))\n+\t\treturn error(\"Could not rename %s to %s\",\n+\t\t\trebase_path_squash_msg(), rebase_path_message());\n+\tunlink(rebase_path_fixup_msg());\n+\tunlink(git_path(\"MERGE_MSG\"));\n+\tif (copy_file(git_path(\"MERGE_MSG\"), rebase_path_message(), 0666))\n+\t\treturn error(\"Could not copy %s to %s\", rebase_path_message(),\n+\t\t\tgit_path(\"MERGE_MSG\"));\n+\treturn error_with_patch(commit, subject, subject_len, opts, 1, 0);\n+}\n+\n static int do_exec(const char *command_line)\n {\n \tconst char *child_argv[] = { NULL, NULL };\n@@ -1447,6 +1640,21 @@ static int do_exec(const char *command_line)\n \treturn status;\n }\n \n+static int is_final_fixup(struct todo_list *todo_list)\n+{\n+\tint i = todo_list->current;\n+\n+\tif (!is_fixup(todo_list->items[i].command))\n+\t\treturn 0;\n+\n+\twhile (++i < todo_list->nr)\n+\t\tif (is_fixup(todo_list->items[i].command))\n+\t\t\treturn 0;\n+\t\telse if (todo_list->items[i].command < TODO_NOOP)\n+\t\t\tbreak;\n+\treturn 1;\n+}\n+\n static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n {\n \tint res = 0;\n@@ -1462,9 +1670,15 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tstruct todo_item *item = todo_list->items + todo_list->current;\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n-\t\tif (item->command <= TODO_EDIT) {\n+\t\tif (is_rebase_i(opts)) {\n+\t\t\tunlink(rebase_path_message());\n+\t\t\tunlink(rebase_path_author_script());\n+\t\t\tunlink(rebase_path_stopped_sha());\n+\t\t\tunlink(rebase_path_amend());\n+\t\t}\n+\t\tif (item->command <= TODO_SQUASH) {\n \t\t\tres = do_pick_commit(item->command, item->commit,\n-\t\t\t\t\topts);\n+\t\t\t\t\topts, is_final_fixup(todo_list));\n \t\t\tif (item->command == TODO_EDIT) {\n \t\t\t\tstruct commit *commit = item->commit;\n \t\t\t\tif (!res)\n@@ -1475,6 +1689,12 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\t\titem->arg, item->arg_len, opts, res,\n \t\t\t\t\t!res);\n \t\t\t}\n+\t\t\tif (res && is_fixup(item->command)) {\n+\t\t\t\tif (res == 1)\n+\t\t\t\t\tintend_to_amend();\n+\t\t\t\treturn error_failed_squash(item->commit, opts,\n+\t\t\t\t\titem->arg_len, item->arg);\n+\t\t\t}\n \t\t}\n \t\telse if (item->command == TODO_EXEC) {\n \t\t\tchar *end_of_arg = (char *)(item->arg + item->arg_len);\n@@ -1565,7 +1785,7 @@ static int single_pick(struct commit *cmit, struct replay_opts *opts)\n {\n \tsetenv(GIT_REFLOG_ACTION, action_name(opts), 0);\n \treturn do_pick_commit(opts->action == REPLAY_PICK ?\n-\t\tTODO_PICK : TODO_REVERT, cmit, opts);\n+\t\tTODO_PICK : TODO_REVERT, cmit, opts, 0);\n }\n \n int sequencer_pick_revisions(struct replay_opts *opts)\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300636","messageId":"68118621a481d786dd57d5c7fd4c3ea0a8e61b07.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 05/34] sequencer (rebase -i): learn about the 'verbose' mode","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:54:20Z","receivedAt":"2016-08-31T08:55:01Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When calling `git rebase -i -v`, the user wants to see some statistics\nafter the commits were rebased. Let's show some.\n\nThe strbuf we use to perform that task will be used for other things\nin subsequent commits, hence it is declared and initialized in a wider\nscope than strictly needed here.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 22 ++++++++++++++++++++++\n sequencer.h |  1 +\n 2 files changed, 23 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a58bb91..95c31bb 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -63,6 +63,8 @@ static GIT_PATH_FUNC(rebase_path_stopped_sha, \"rebase-merge/stopped-sha\")\n  * command-line (and are only consumed, not modified, by the sequencer).\n  */\n static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, \"rebase-merge/gpg_sign_opt\")\n+static GIT_PATH_FUNC(rebase_path_orig_head, \"rebase-merge/orig-head\")\n+static GIT_PATH_FUNC(rebase_path_verbose, \"rebase-merge/verbose\")\n \n /* We will introduce the 'interactive rebase' mode later */\n static inline int is_rebase_i(const struct replay_opts *opts)\n@@ -1096,6 +1098,9 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\t\t}\n \t\t}\n \n+\t\tif (file_exists(rebase_path_verbose()))\n+\t\t\topts->verbose = 1;\n+\n \t\treturn 0;\n \t}\n \n@@ -1468,9 +1473,26 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t}\n \n \tif (is_rebase_i(opts)) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\n \t\t/* Stopped in the middle, as planned? */\n \t\tif (todo_list->current < todo_list->nr)\n \t\t\treturn 0;\n+\n+\t\tif (opts->verbose) {\n+\t\t\tconst char *argv[] = {\n+\t\t\t\t\"diff-tree\", \"--stat\", NULL, NULL\n+\t\t\t};\n+\n+\t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0))\n+\t\t\t\treturn error(\"Could not read %s\",\n+\t\t\t\t\trebase_path_orig_head());\n+\t\t\tstrbuf_addstr(&buf, \"..HEAD\");\n+\t\t\targv[2] = buf.buf;\n+\t\t\trun_command_v_opt(argv, RUN_GIT_CMD);\n+\t\t\tstrbuf_reset(&buf);\n+\t\t}\n+\t\tstrbuf_release(&buf);\n \t}\n \n \t/*\ndiff --git a/sequencer.h b/sequencer.h\nindex edd7d4a..fd2a719 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -24,6 +24,7 @@ struct replay_opts {\n \tint allow_empty;\n \tint allow_empty_message;\n \tint keep_redundant_commits;\n+\tint verbose;\n \n \tint mainline;\n \n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300637","messageId":"9191749ed726a88903c06e55478db64a32b632ec.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 04/34] sequencer (rebase -i): implement the 'exec' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:54:16Z","receivedAt":"2016-08-31T08:55:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The 'exec' command is a little special among rebase -i's commands, as it\ndoes *not* have a SHA-1 as first parameter. Instead, everything after the\n`exec` command is treated as command-line to execute.\n\nLet's reuse the arg/arg_len fields of the todo_item structure (which hold\nthe oneline for pick/edit commands) to point to the command-line.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 58 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex fe2f6e7..a58bb91 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -17,6 +17,7 @@\n #include \"argv-array.h\"\n #include \"quote.h\"\n #include \"log-tree.h\"\n+#include \"wt-status.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n@@ -661,6 +662,7 @@ enum todo_command {\n \tTODO_PICK,\n \tTODO_REVERT,\n \tTODO_EDIT,\n+\tTODO_EXEC,\n \tTODO_NOOP\n };\n \n@@ -668,6 +670,7 @@ static const char *todo_command_strings[] = {\n \t\"pick\",\n \t\"revert\",\n \t\"edit\",\n+\t\"exec\",\n \t\"noop\"\n };\n \n@@ -964,6 +967,12 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \t\treturn -1;\n \tbol += padding;\n \n+\tif (item->command == TODO_EXEC) {\n+\t\titem->arg = bol;\n+\t\titem->arg_len = (int)(eol - bol);\n+\t\treturn 0;\n+\t}\n+\n \tend_of_object_name = (char *) bol + strcspn(bol, \" \\t\\n\");\n \tsaved = *end_of_object_name;\n \t*end_of_object_name = '\\0';\n@@ -1372,6 +1381,47 @@ static int error_with_patch(struct commit *commit,\n \treturn exit_code;\n }\n \n+static int do_exec(const char *command_line)\n+{\n+\tconst char *child_argv[] = { NULL, NULL };\n+\tint dirty, status;\n+\n+\tfprintf(stderr, \"Executing: %s\\n\", command_line);\n+\tchild_argv[0] = command_line;\n+\tstatus = run_command_v_opt(child_argv, RUN_USING_SHELL);\n+\n+\t/* force re-reading of the cache */\n+\tif (discard_cache() < 0 || read_cache() < 0)\n+\t\treturn error(_(\"Could not read index\"));\n+\n+\tdirty = require_clean_work_tree(\"rebase\", NULL, 1, 1);\n+\n+\tif (status) {\n+\t\twarning(\"Execution failed: %s\\n%s\"\n+\t\t\t\"You can fix the problem, and then run\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"  git rebase --continue\\n\"\n+\t\t\t\"\\n\",\n+\t\t\tcommand_line,\n+\t\t\tdirty ? \"and made changes to the index and/or the \"\n+\t\t\t\t\"working tree\\n\" : \"\");\n+\t\tif (status == 127)\n+\t\t\t/* command not found */\n+\t\t\tstatus = 1;\n+\t}\n+\telse if (dirty) {\n+\t\twarning(\"Execution succeeded: %s\\nbut \"\n+\t\t\t\"left changes to the index and/or the working tree\\n\"\n+\t\t\t\"Commit or stash your changes, and then run\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"  git rebase --continue\\n\"\n+\t\t\t\"\\n\", command_line);\n+\t\tstatus = 1;\n+\t}\n+\n+\treturn status;\n+}\n+\n static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n {\n \tint res = 0;\n@@ -1401,6 +1451,14 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\t\t!res);\n \t\t\t}\n \t\t}\n+\t\telse if (item->command == TODO_EXEC) {\n+\t\t\tchar *end_of_arg = (char *)(item->arg + item->arg_len);\n+\t\t\tint saved = *end_of_arg;\n+\n+\t\t\t*end_of_arg = '\\0';\n+\t\t\tres = do_exec(item->arg);\n+\t\t\t*end_of_arg = saved;\n+\t\t}\n \t\telse if (item->command != TODO_NOOP)\n \t\t\treturn error(\"Unknown command %d\", item->command);\n \n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300638","messageId":"755be5201e9ab12a0cce2bebcdf3ab37dfc368c7.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 03/34] sequencer (rebase -i): implement the 'edit' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:54:12Z","receivedAt":"2016-08-31T08:55:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This patch is a straight-forward reimplementation of the `edit`\noperation of the interactive rebase command.\n\nWell, not *quite* straight-forward: when stopping, the `edit`\ncommand wants to write the `patch` file (which is not only the\npatch, but includes the commit message and author information). To\nthat end, this patch requires the earlier work that taught the\nlog-tree machinery to respect the `file` setting of\nrev_info->diffopt to write to a file stream different than stdout.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--\n 1 file changed, 112 insertions(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex f85dc9c..fe2f6e7 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -16,6 +16,7 @@\n #include \"refs.h\"\n #include \"argv-array.h\"\n #include \"quote.h\"\n+#include \"log-tree.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n@@ -43,6 +44,20 @@ static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n  */\n static GIT_PATH_FUNC(rebase_path_author_script, \"rebase-merge/author-script\")\n /*\n+ * When an \"edit\" rebase command is being processed, the SHA1 of the\n+ * commit to be edited is recorded in this file.  When \"git rebase\n+ * --continue\" is executed, if there are any staged changes then they\n+ * will be amended to the HEAD commit, but only provided the HEAD\n+ * commit is still the commit to be edited.  When any other rebase\n+ * command is processed, this file is deleted.\n+ */\n+static GIT_PATH_FUNC(rebase_path_amend, \"rebase-merge/amend\")\n+/*\n+ * When we stop at a given patch via the \"edit\" command, this file contains\n+ * the long commit name of the corresponding patch.\n+ */\n+static GIT_PATH_FUNC(rebase_path_stopped_sha, \"rebase-merge/stopped-sha\")\n+/*\n  * The following files are written by git-rebase just after parsing the\n  * command-line (and are only consumed, not modified, by the sequencer).\n  */\n@@ -645,12 +660,14 @@ static int allow_empty(struct replay_opts *opts, struct commit *commit)\n enum todo_command {\n \tTODO_PICK,\n \tTODO_REVERT,\n+\tTODO_EDIT,\n \tTODO_NOOP\n };\n \n static const char *todo_command_strings[] = {\n \t\"pick\",\n \t\"revert\",\n+\t\"edit\",\n \t\"noop\"\n };\n \n@@ -1279,9 +1296,85 @@ static int save_opts(struct replay_opts *opts)\n \treturn res;\n }\n \n+static int make_patch(struct commit *commit, struct replay_opts *opts)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct rev_info log_tree_opt;\n+\tconst char *commit_buffer = get_commit_buffer(commit, NULL), *subject;\n+\tint res = 0;\n+\n+\tif (write_file_gently(rebase_path_stopped_sha(),\n+\t\t\t      short_commit_name(commit), 1) < 0)\n+\t\treturn -1;\n+\n+\tstrbuf_addf(&buf, \"%s/patch\", get_dir(opts));\n+\tmemset(&log_tree_opt, 0, sizeof(log_tree_opt));\n+\tinit_revisions(&log_tree_opt, NULL);\n+\tlog_tree_opt.abbrev = 0;\n+\tlog_tree_opt.diff = 1;\n+\tlog_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;\n+\tlog_tree_opt.disable_stdin = 1;\n+\tlog_tree_opt.no_commit_id = 1;\n+\tlog_tree_opt.diffopt.file = fopen(buf.buf, \"w\");\n+\tlog_tree_opt.diffopt.use_color = GIT_COLOR_NEVER;\n+\tif (!log_tree_opt.diffopt.file)\n+\t\tres |= error_errno(\"could not open '%s'\", buf.buf);\n+\telse {\n+\t\tres |= log_tree_commit(&log_tree_opt, commit);\n+\t\tfclose(log_tree_opt.diffopt.file);\n+\t}\n+\tstrbuf_reset(&buf);\n+\n+\tstrbuf_addf(&buf, \"%s/message\", get_dir(opts));\n+\tif (!file_exists(buf.buf)) {\n+\t\tfind_commit_subject(commit_buffer, &subject);\n+\t\tres |= write_file_gently(buf.buf, subject, 1);\n+\t\tunuse_commit_buffer(commit, commit_buffer);\n+\t}\n+\tstrbuf_release(&buf);\n+\n+\treturn res;\n+}\n+\n+static int intend_to_amend(void)\n+{\n+\tunsigned char head[20];\n+\n+\tif (get_sha1(\"HEAD\", head))\n+\t\treturn error(\"Cannot read HEAD\");\n+\n+\treturn write_file_gently(rebase_path_amend(), sha1_to_hex(head), 1);\n+}\n+\n+static int error_with_patch(struct commit *commit,\n+\tconst char *subject, int subject_len,\n+\tstruct replay_opts *opts, int exit_code, int to_amend)\n+{\n+\tif (make_patch(commit, opts))\n+\t\treturn -1;\n+\n+\tif (to_amend) {\n+\t\tif (intend_to_amend())\n+\t\t\treturn -1;\n+\n+\t\tfprintf(stderr, \"You can amend the commit now, with\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"  git commit --amend %s\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"Once you are satisfied with your changes, run\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"  git rebase --continue\\n\", gpg_sign_opt_quoted(opts));\n+\t}\n+\telse if (exit_code)\n+\t\tfprintf(stderr, \"Could not apply %s... %.*s\\n\",\n+\t\t\tshort_commit_name(commit), subject_len, subject);\n+\n+\treturn exit_code;\n+}\n+\n static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n {\n-\tint res;\n+\tint res = 0;\n \n \tsetenv(GIT_REFLOG_ACTION, action_name(opts), 0);\n \tif (opts->allow_ff)\n@@ -1294,9 +1387,20 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tstruct todo_item *item = todo_list->items + todo_list->current;\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n-\t\tif (item->command <= TODO_REVERT)\n+\t\tif (item->command <= TODO_EDIT) {\n \t\t\tres = do_pick_commit(item->command, item->commit,\n \t\t\t\t\topts);\n+\t\t\tif (item->command == TODO_EDIT) {\n+\t\t\t\tstruct commit *commit = item->commit;\n+\t\t\t\tif (!res)\n+\t\t\t\t\twarning(\"Stopped at %s... %.*s\",\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\treturn error_with_patch(commit,\n+\t\t\t\t\titem->arg, item->arg_len, opts, res,\n+\t\t\t\t\t!res);\n+\t\t\t}\n+\t\t}\n \t\telse if (item->command != TODO_NOOP)\n \t\t\treturn error(\"Unknown command %d\", item->command);\n \n@@ -1305,6 +1409,12 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\treturn res;\n \t}\n \n+\tif (is_rebase_i(opts)) {\n+\t\t/* Stopped in the middle, as planned? */\n+\t\tif (todo_list->current < todo_list->nr)\n+\t\t\treturn 0;\n+\t}\n+\n \t/*\n \t * Sequence of picks finished successfully; cleanup by\n \t * removing the .git/sequencer directory\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300639","messageId":"3a08b4173a95f1321d5d17dfec278ae7f7dfa070.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 02/34] sequencer (rebase -i): implement the 'noop' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:54:06Z","receivedAt":"2016-08-31T08:55:09Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The 'noop' command is probably the most boring of all rebase -i commands\nto support in the sequencer.\n\nWhich makes it an excellent candidate for this first stab to add support\nfor rebase -i's commands to the sequencer.\n\nFor the moment, let's also treat empty lines and commented-out lines as\n'noop'; We will refine that handling later in this patch series.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 28 +++++++++++++++++++++++++---\n 1 file changed, 25 insertions(+), 3 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 5598534..f85dc9c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -644,12 +644,14 @@ static int allow_empty(struct replay_opts *opts, struct commit *commit)\n \n enum todo_command {\n \tTODO_PICK,\n-\tTODO_REVERT\n+\tTODO_REVERT,\n+\tTODO_NOOP\n };\n \n static const char *todo_command_strings[] = {\n \t\"pick\",\n-\t\"revert\"\n+\t\"revert\",\n+\t\"noop\"\n };\n \n static const char *command_to_string(const enum todo_command command)\n@@ -916,6 +918,14 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \t/* left-trim */\n \tbol += strspn(bol, \" \\t\");\n \n+\tif (bol == eol || *bol == '\\r' || *bol == comment_line_char) {\n+\t\titem->command = TODO_NOOP;\n+\t\titem->commit = NULL;\n+\t\titem->arg = bol;\n+\t\titem->arg_len = eol - bol;\n+\t\treturn 0;\n+\t}\n+\n \tfor (i = 0; i < ARRAY_SIZE(todo_command_strings); i++)\n \t\tif (skip_prefix(bol, todo_command_strings[i], &bol)) {\n \t\t\titem->command = i;\n@@ -924,6 +934,13 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \tif (i >= ARRAY_SIZE(todo_command_strings))\n \t\treturn -1;\n \n+\tif (item->command == TODO_NOOP) {\n+\t\titem->commit = NULL;\n+\t\titem->arg = bol;\n+\t\titem->arg_len = eol - bol;\n+\t\treturn 0;\n+\t}\n+\n \t/* Eat up extra spaces/ tabs before object name */\n \tpadding = strspn(bol, \" \\t\");\n \tif (!padding)\n@@ -1277,7 +1294,12 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tstruct todo_item *item = todo_list->items + todo_list->current;\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n-\t\tres = do_pick_commit(item->command, item->commit, opts);\n+\t\tif (item->command <= TODO_REVERT)\n+\t\t\tres = do_pick_commit(item->command, item->commit,\n+\t\t\t\t\topts);\n+\t\telse if (item->command != TODO_NOOP)\n+\t\t\treturn error(\"Unknown command %d\", item->command);\n+\n \t\ttodo_list->current++;\n \t\tif (res)\n \t\t\treturn res;\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300640","messageId":"5ffec2e588a4edc4902e1ab3a2ec3a73a7c3625b.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 01/34] sequencer: support a new action: 'interactive rebase'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:54:02Z","receivedAt":"2016-08-31T08:55:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This patch introduces a new action for the sequencer. It really does not\ndo a whole lot of its own right now, but lays the ground work for\npatches to come. The intention, of course, is to finally make the\nsequencer the work horse of the interactive rebase (the original idea\nbehind the \"sequencer\" concept).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 32 +++++++++++++++++++++++++++++---\n sequencer.h |  3 ++-\n 2 files changed, 31 insertions(+), 4 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a89ef27..5598534 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -28,6 +28,14 @@ static GIT_PATH_FUNC(git_path_todo_file, \"sequencer/todo\")\n static GIT_PATH_FUNC(git_path_opts_file, \"sequencer/opts\")\n static GIT_PATH_FUNC(git_path_head_file, \"sequencer/head\")\n \n+static GIT_PATH_FUNC(rebase_path, \"rebase-merge\")\n+/*\n+ * The file containing rebase commands, comments, and empty lines.\n+ * This file is created by \"git rebase -i\" then edited by the user. As\n+ * the lines are processed, they are removed from the front of this\n+ * file and written to the tail of 'done'.\n+ */\n+static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n /*\n  * A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and\n  * GIT_AUTHOR_DATE that will be used for the commit that is currently\n@@ -43,16 +51,20 @@ static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, \"rebase-merge/gpg_sign_opt\")\n /* We will introduce the 'interactive rebase' mode later */\n static inline int is_rebase_i(const struct replay_opts *opts)\n {\n-\treturn 0;\n+\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n }\n \n static const char *get_dir(const struct replay_opts *opts)\n {\n+\tif (is_rebase_i(opts))\n+\t\treturn rebase_path();\n \treturn git_path_seq_dir();\n }\n \n static const char *get_todo_path(const struct replay_opts *opts)\n {\n+\tif (is_rebase_i(opts))\n+\t\treturn rebase_path_todo();\n \treturn git_path_todo_file();\n }\n \n@@ -176,7 +188,15 @@ int sequencer_remove_state(struct replay_opts *opts)\n \n static const char *action_name(const struct replay_opts *opts)\n {\n-\treturn opts->action == REPLAY_REVERT ? \"revert\" : \"cherry-pick\";\n+\tswitch (opts->action) {\n+\tcase REPLAY_REVERT:\n+\t\treturn \"revert\";\n+\tcase REPLAY_PICK:\n+\t\treturn \"cherry-pick\";\n+\tcase REPLAY_INTERACTIVE_REBASE:\n+\t\treturn \"rebase -i\";\n+\t}\n+\tdie(\"Unknown action: %d\", opts->action);\n }\n \n struct commit_message {\n@@ -407,7 +427,10 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, &index_lock, COMMIT_LOCK))\n-\t\t/* TRANSLATORS: %s will be \"revert\" or \"cherry-pick\" */\n+\t\t/*\n+\t\t * TRANSLATORS: %s will be \"revert\", \"cherry-pick\" or\n+\t\t * \"rebase -i\".\n+\t\t */\n \t\treturn error(_(\"%s: Unable to write new index file\"),\n \t\t\taction_name(opts));\n \trollback_lock_file(&index_lock);\n@@ -1186,6 +1209,9 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)\n \tconst char *todo_path = get_todo_path(opts);\n \tint next = todo_list->current, offset, fd;\n \n+\tif (is_rebase_i(opts))\n+\t\tnext++;\n+\n \tfd = hold_lock_file_for_update(&todo_lock, todo_path, 0);\n \tif (fd < 0)\n \t\treturn error_errno(_(\"Could not lock '%s'\"),\ndiff --git a/sequencer.h b/sequencer.h\nindex 688fff1..edd7d4a 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -7,7 +7,8 @@ const char *git_path_seq_dir(void);\n \n enum replay_action {\n \tREPLAY_REVERT,\n-\tREPLAY_PICK\n+\tREPLAY_PICK,\n+\tREPLAY_INTERACTIVE_REBASE\n };\n \n struct replay_opts {\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300641","messageId":"cover.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":null,"subject":"[PATCH 00/34] Teach the sequencer to act as rebase -i's backend","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:53:41Z","receivedAt":"2016-08-31T08:55:18Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This marks the count down to '3': two more patch series after this\n(really tiny ones) and we have a faster rebase -i.\n\nThe idea of this patch series is to teach the sequencer to understand\nall of the commands in `git-rebase-todo` scripts, to execute them and to\nbehave pretty much very the same as `git rebase -i --continue` when\ncalled with the newly-introduced REPLAY_INTERACTIVE_REBASE setting.\n\nMost of these patches should be pretty much straight-forward. When not,\nI tried to make a point of describing enough background in the commit\nmessage. Please feel free to point out where my explanations fall short.\n\nNote that even after this patch series is applied, rebase -i is still\nunaffected. It will require the next patch series which introduces the\nrebase--helper that essentially implements `git rebase -i --continue` by\ncalling the sequencer with the appropriate options.\n\nThe final patch series will move a couple of pre- and post-processing\nsteps into the rebase--helper/sequencer (such as expanding/shrinking the\nSHA-1s, reordering the fixup!/squash! lines, etc). This might sound like\na mere add-on, but it is essential for the speed improvements: those\nstupid little processing steps really dominated the execution time in my\ntests.\n\nApart from mostly cosmetic patches (and the occasional odd bug that I\nfixed promptly), I used these patches since mid May to perform all of my\ninteractive rebases. In mid June, I had the idea to teach rebase -i to\nrun *both* scripted rebase and rebase--helper and to cross-validate the\nresults. This slowed down all my interactive rebases since, but helped\nme catch three rather obscure bugs (e.g. that git commit --fixup unfolds\nlong onelines and rebase -i still finds the correct original commit).\n\nThis is all only to say that I am rather confident that the current code\ndoes the job.\n\nAs stated earlier, my plan is to integrate all of these patch series\ninto Git for Windows v2.10.0. And then be mostly offline during the\nsecond half of September ;-) There are multiple reasons why I want to\npull through with this: I have been working on these patches since mid\nFebruary and it is high time that they benefit users other than me.\nAlso: I got a bit tired of postponing the integration for a couple of\nGit for Windows versions already. Originally, I had planned it for\nv2.9.0, but then I wanted to give the patches some review, regretting\nthat idea when it took one of the 14 patch series over a month to\nstabilize.\n\nThis means that I will not perform any major refactoring at this stage,\nbut postpone any such work, if necessary at all.\n\nJust to reiterate why I do all this: it speeds up the interactive rebase\nsubstantially. Even with a not yet fully builtin rebase -i, but just the\npart after the user edited the `git-rebase-todo` script.\n\nThe performance test I introduced to demonstrate this (p3404) shows a\nspeed-up of +380% here (i.e. roughly 5x), from ~8.8 seconds to ~1.8\nseconds. This is on Windows, where the performance impact of avoiding\nshell scripting is most noticable.\n\nOn MacOSX and on Linux, the speed-up is less pronounced, but still\nnoticable, at least if you trust Travis CI, which I abused to perform\nthat test for me. Check for yourself (searching for \"3404.2\") here:\nhttps://travis-ci.org/git/git/builds/156295227. According to those logs,\np3404 is speeded up from ~0.45 seconds to ~0.12 seconds on Linux (read:\nabout 3.5x) and from ~1.7 seconds to ~0.5 seconds on MacOSX (read:\nalmost 4x).\n\n\nJohannes Schindelin (34):\n  sequencer: support a new action: 'interactive rebase'\n  sequencer (rebase -i): implement the 'noop' command\n  sequencer (rebase -i): implement the 'edit' command\n  sequencer (rebase -i): implement the 'exec' command\n  sequencer (rebase -i): learn about the 'verbose' mode\n  sequencer (rebase -i): write the 'done' file\n  sequencer (rebase -i): add support for the 'fixup' and 'squash'\n    commands\n  sequencer (rebase -i): implement the short commands\n  sequencer (rebase -i): write an author-script file\n  sequencer (rebase -i): allow continuing with staged changes\n  sequencer (rebase -i): remove CHERRY_PICK_HEAD when no longer needed\n  sequencer (rebase -i): skip some revert/cherry-pick specific code path\n  sequencer (rebase -i): the todo can be empty when continuing\n  sequencer (rebase -i): update refs after a successful rebase\n  sequencer (rebase -i): leave a patch upon error\n  sequencer (rebase -i): implement the 'reword' command\n  sequencer (rebase -i): allow fast-forwarding for edit/reword\n  sequencer (rebase -i): refactor setting the reflog message\n  sequencer (rebase -i): set the reflog message consistently\n  sequencer (rebase -i): copy commit notes at end\n  sequencer (rebase -i): record interrupted commits in rewritten, too\n  sequencer (rebase -i): run the post-rewrite hook, if needed\n  sequencer (rebase -i): respect the rebase.autostash setting\n  sequencer (rebase -i): respect strategy/strategy_opts settings\n  sequencer (rebase -i): allow rescheduling commands\n  sequencer (rebase -i): implement the 'drop' command\n  sequencer (rebase -i): differentiate between comments and 'noop'\n  run_command_opt(): optionally hide stderr when the command succeeds\n  sequencer (rebase -i): show only failed `git commit`'s output\n  sequencer (rebase -i): show only failed cherry-picks' output\n  sequencer (rebase -i): suggest --edit-todo upon unknown command\n  sequencer (rebase -i): show the progress\n  sequencer (rebase -i): write the progress into files\n  sequencer (rebase -i): write out the final message\n\n run-command.c |  23 ++\n run-command.h |   1 +\n sequencer.c   | 990 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---\n sequencer.h   |   4 +-\n 4 files changed, 971 insertions(+), 47 deletions(-)\n\nBased-On: prepare-sequencer-and-wt_status at https://github.com/dscho/git\nFetch-Base-Via: git fetch https://github.com/dscho/git prepare-sequencer-and-wt_status\nPublished-As: https://github.com/dscho/git/releases/tag/sequencer-i-v1\nFetch-It-Via: git fetch https://github.com/dscho/git sequencer-i-v1\n\n-- \n2.10.0.rc2.102.g5c102ec\n\nbase-commit: 45bfeb0c4044dfcf090ad97e839d250d62308fac\n"},{"id":"300642","messageId":"41ccb12044cd26fa8aa919b537cd396b983f4e31.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 08/34] sequencer (rebase -i): implement the short commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:54:46Z","receivedAt":"2016-08-31T08:55:20Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"For users' convenience, most rebase commands can be abbreviated, e.g.\n'p' instead of 'pick' and 'x' instead of 'exec'. Let's teach the\nsequencer to handle those abbreviated commands just fine.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 34 +++++++++++++++++++++-------------\n 1 file changed, 21 insertions(+), 13 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 3ae5978..919698b 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -705,20 +705,23 @@ enum todo_command {\n \tTODO_NOOP\n };\n \n-static const char *todo_command_strings[] = {\n-\t\"pick\",\n-\t\"revert\",\n-\t\"edit\",\n-\t\"fixup\",\n-\t\"squash\",\n-\t\"exec\",\n-\t\"noop\"\n+static struct {\n+\tchar c;\n+\tconst char *str;\n+} todo_command_info[] = {\n+\t{ 'p', \"pick\" },\n+\t{ 0,   \"revert\" },\n+\t{ 'e', \"edit\" },\n+\t{ 'f', \"fixup\" },\n+\t{ 's', \"squash\" },\n+\t{ 'x', \"exec\" },\n+\t{ 0,   \"noop\" }\n };\n \n static const char *command_to_string(const enum todo_command command)\n {\n-\tif (command < ARRAY_SIZE(todo_command_strings))\n-\t\treturn todo_command_strings[command];\n+\tif (command < ARRAY_SIZE(todo_command_info))\n+\t\treturn todo_command_info[command].str;\n \tdie(\"Unknown command: %d\", command);\n }\n \n@@ -1126,12 +1129,17 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \t\treturn 0;\n \t}\n \n-\tfor (i = 0; i < ARRAY_SIZE(todo_command_strings); i++)\n-\t\tif (skip_prefix(bol, todo_command_strings[i], &bol)) {\n+\tfor (i = 0; i < ARRAY_SIZE(todo_command_info); i++)\n+\t\tif (skip_prefix(bol, todo_command_info[i].str, &bol)) {\n \t\t\titem->command = i;\n \t\t\tbreak;\n \t\t}\n-\tif (i >= ARRAY_SIZE(todo_command_strings))\n+\t\telse if (bol[1] == ' ' && *bol == todo_command_info[i].c) {\n+\t\t\tbol++;\n+\t\t\titem->command = i;\n+\t\t\tbreak;\n+\t\t}\n+\tif (i >= ARRAY_SIZE(todo_command_info))\n \t\treturn -1;\n \n \tif (item->command == TODO_NOOP) {\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300643","messageId":"36d2a101cc8eccbeba57da812e2f4b241b151238.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 09/34] sequencer (rebase -i): write an author-script file","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:54:50Z","receivedAt":"2016-08-31T08:55:21Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When the interactive rebase aborts, it writes out an author-script file\nto record the author information for the current commit. As we are about\nto teach the sequencer how to perform the actions behind an interactive\nrebase, it needs to write those author-script files, too.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 48 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 919698b..ef818a2 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -528,6 +528,51 @@ static int is_index_unchanged(void)\n \treturn !hashcmp(active_cache_tree->sha1, head_commit->tree->object.oid.hash);\n }\n \n+static int write_author_script(const char *message)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *eol;\n+\n+\tfor (;;)\n+\t\tif (!*message || starts_with(message, \"\\n\")) {\n+missing_author:\n+\t\t\t/* Missing 'author' line? */\n+\t\t\tunlink(rebase_path_author_script());\n+\t\t\treturn 0;\n+\t\t}\n+\t\telse if (skip_prefix(message, \"author \", &message))\n+\t\t\tbreak;\n+\t\telse if ((eol = strchr(message, '\\n')))\n+\t\t\tmessage = eol + 1;\n+\t\telse\n+\t\t\tgoto missing_author;\n+\n+\tstrbuf_addstr(&buf, \"GIT_AUTHOR_NAME='\");\n+\twhile (*message && *message != '\\n' && *message != '\\r')\n+\t\tif (skip_prefix(message, \" <\", &message))\n+\t\t\tbreak;\n+\t\telse if (*message != '\\'')\n+\t\t\tstrbuf_addch(&buf, *(message++));\n+\t\telse\n+\t\t\tstrbuf_addf(&buf, \"'\\\\\\\\%c'\", *(message++));\n+\tstrbuf_addstr(&buf, \"'\\nGIT_AUTHOR_EMAIL='\");\n+\twhile (*message && *message != '\\n' && *message != '\\r')\n+\t\tif (skip_prefix(message, \"> \", &message))\n+\t\t\tbreak;\n+\t\telse if (*message != '\\'')\n+\t\t\tstrbuf_addch(&buf, *(message++));\n+\t\telse\n+\t\t\tstrbuf_addf(&buf, \"'\\\\\\\\%c'\", *(message++));\n+\tstrbuf_addstr(&buf, \"'\\nGIT_AUTHOR_DATE='@\");\n+\twhile (*message && *message != '\\n' && *message != '\\r')\n+\t\tif (*message != '\\'')\n+\t\t\tstrbuf_addch(&buf, *(message++));\n+\t\telse\n+\t\t\tstrbuf_addf(&buf, \"'\\\\\\\\%c'\", *(message++));\n+\tstrbuf_addstr(&buf, \"'\\n\");\n+\treturn write_message(&buf, rebase_path_author_script());\n+}\n+\n static char **read_author_script(void)\n {\n \tstruct strbuf script = STRBUF_INIT;\n@@ -979,7 +1024,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t}\n \t}\n \n-\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n+\tif (is_rebase_i(opts) && write_author_script(msg.message) < 0)\n+\t\tres |= -1;\n+\telse if (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n \t\tres |= do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n \t\tif (res < 0)\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300644","messageId":"3a1417a67b17c057fe0c90b30c34e3d14fbf549f.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 12/34] sequencer (rebase -i): skip some revert/cherry-pick specific code path","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:55:01Z","receivedAt":"2016-08-31T08:55:22Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When a cherry-pick continues without a \"todo script\", the intention is\nsimply to pick a single commit.\n\nHowever, when an interactive rebase is continued without a \"todo\nscript\", it means that the last command has been completed and that we\nnow need to clean up.\n\nThis commit guards the revert/cherry-pick specific steps so that they\nare not executed in rebase -i mode.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 23 +++++++++++++----------\n 1 file changed, 13 insertions(+), 10 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 683b89f..7f017a8 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1859,22 +1859,25 @@ int sequencer_continue(struct replay_opts *opts)\n \t\tif (commit_staged_changes(opts))\n \t\t\treturn -1;\n \t}\n-\tif (!file_exists(get_todo_path(opts)))\n+\telse if (!file_exists(get_todo_path(opts)))\n \t\treturn continue_single_pick();\n \tif (read_populate_opts(opts) ||\n \t\t\tread_populate_todo(&todo_list, opts))\n \t\treturn -1;\n \n-\t/* Verify that the conflict has been resolved */\n-\tif (file_exists(git_path_cherry_pick_head()) ||\n-\t    file_exists(git_path_revert_head())) {\n-\t\tint ret = continue_single_pick();\n-\t\tif (ret)\n-\t\t\treturn ret;\n+\tif (!is_rebase_i(opts)) {\n+\t\t/* Verify that the conflict has been resolved */\n+\t\tif (file_exists(git_path_cherry_pick_head()) ||\n+\t\t    file_exists(git_path_revert_head())) {\n+\t\t\tint ret = continue_single_pick();\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t\tif (index_differs_from(\"HEAD\", 0))\n+\t\t\treturn error_dirty_index(opts);\n+\t\ttodo_list.current++;\n \t}\n-\tif (index_differs_from(\"HEAD\", 0))\n-\t\treturn error_dirty_index(opts);\n-\ttodo_list.current++;\n+\n \tres = pick_commits(&todo_list, opts);\n \ttodo_list_release(&todo_list);\n \treturn res;\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300645","messageId":"df4d48f33ff31ef2ae445be18674ec3f58540d08.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 11/34] sequencer (rebase -i): remove CHERRY_PICK_HEAD when no longer needed","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:54:57Z","receivedAt":"2016-08-31T08:55:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The scripted version of the interactive rebase already does that.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 4ddb12e..683b89f 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1813,8 +1813,13 @@ static int commit_staged_changes(struct replay_opts *opts)\n \n \tif (has_unstaged_changes(1))\n \t\treturn error(_(\"Cannot rebase: You have unstaged changes.\"));\n-\tif (!has_uncommitted_changes(0))\n+\tif (!has_uncommitted_changes(0)) {\n+\t\tconst char *cherry_pick_head = git_path(\"CHERRY_PICK_HEAD\");\n+\n+\t\tif (file_exists(cherry_pick_head) && unlink(cherry_pick_head))\n+\t\t\treturn error(\"Could not remove CHERRY_PICK_HEAD\");\n \t\treturn 0;\n+\t}\n \n \tif (file_exists(rebase_path_amend())) {\n \t\tstruct strbuf rev = STRBUF_INIT;\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300646","messageId":"1ed867cdbca0351265f2b0fcc8d10366a6b94411.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 13/34] sequencer (rebase -i): the todo can be empty when continuing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:55:05Z","receivedAt":"2016-08-31T08:55:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When the last command of an interactive rebase fails, the user needs to\nresolve the problem and then continue the interactive rebase. Naturally,\nthe todo script is empty by then. So let's not complain about that!\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 5 +++--\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 7f017a8..d4437f5 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1249,8 +1249,6 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n \t\t\tfixup_okay = 1;\n \t\tp = *eol ? eol + 1 : eol;\n \t}\n-\tif (!todo_list->nr)\n-\t\treturn error(_(\"No commits parsed.\"));\n \treturn res;\n }\n \n@@ -1273,6 +1271,9 @@ static int read_populate_todo(struct todo_list *todo_list,\n \tres = parse_insn_buffer(todo_list->buf.buf, todo_list);\n \tif (res)\n \t\treturn error(_(\"Unusable instruction sheet: %s\"), todo_file);\n+\tif (!todo_list->nr &&\n+\t    (!is_rebase_i(opts) || !file_exists(rebase_path_done())))\n+\t\treturn error(_(\"No commits parsed.\"));\n \treturn 0;\n }\n \n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300647","messageId":"31b30111bc29fd7362824034857df87b551c725a.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 10/34] sequencer (rebase -i): allow continuing with staged changes","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:54:53Z","receivedAt":"2016-08-31T08:55:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When an interactive rebase is interrupted, the user may stage changes\nbefore continuing, and we need to commit those changes in that case.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 39 +++++++++++++++++++++++++++++++++++++++\n 1 file changed, 39 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex ef818a2..4ddb12e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1807,6 +1807,41 @@ static int continue_single_pick(void)\n \treturn run_command_v_opt(argv, RUN_GIT_CMD);\n }\n \n+static int commit_staged_changes(struct replay_opts *opts)\n+{\n+\tint amend = 0;\n+\n+\tif (has_unstaged_changes(1))\n+\t\treturn error(_(\"Cannot rebase: You have unstaged changes.\"));\n+\tif (!has_uncommitted_changes(0))\n+\t\treturn 0;\n+\n+\tif (file_exists(rebase_path_amend())) {\n+\t\tstruct strbuf rev = STRBUF_INIT;\n+\t\tunsigned char head[20], to_amend[20];\n+\n+\t\tif (get_sha1(\"HEAD\", head))\n+\t\t\treturn error(\"Cannot amend non-existing commit\");\n+\t\tif (!read_oneliner(&rev, rebase_path_amend(), 0))\n+\t\t\treturn error(\"Invalid file: %s\", rebase_path_amend());\n+\t\tif (get_sha1_hex(rev.buf, to_amend))\n+\t\t\treturn error(\"Invalid contents: %s\",\n+\t\t\t\trebase_path_amend());\n+\t\tif (hashcmp(head, to_amend))\n+\t\t\treturn error(\"\\nYou have uncommitted changes in your \"\n+\t\t\t\t\"working tree. Please, commit them\\nfirst and \"\n+\t\t\t\t\"then run 'git rebase --continue' again.\");\n+\n+\t\tstrbuf_release(&rev);\n+\t\tamend = 1;\n+\t}\n+\n+\tif (sequencer_commit(rebase_path_message(), opts, 1, 1, amend, 0))\n+\t\treturn error(\"Could not commit staged changes.\");\n+\tunlink(rebase_path_amend());\n+\treturn 0;\n+}\n+\n int sequencer_continue(struct replay_opts *opts)\n {\n \tstruct todo_list todo_list = TODO_LIST_INIT;\n@@ -1815,6 +1850,10 @@ int sequencer_continue(struct replay_opts *opts)\n \tif (read_and_refresh_cache(opts))\n \t\treturn -1;\n \n+\tif (is_rebase_i(opts)) {\n+\t\tif (commit_staged_changes(opts))\n+\t\t\treturn -1;\n+\t}\n \tif (!file_exists(get_todo_path(opts)))\n \t\treturn continue_single_pick();\n \tif (read_populate_opts(opts) ||\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300648","messageId":"76ab1103be1a3414e200cb3766a00be049bbf03f.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 16/34] sequencer (rebase -i): implement the 'reword' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:55:30Z","receivedAt":"2016-08-31T08:56:01Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is now trivial, as all the building blocks are in place: all we need\nto do is to flip the \"edit\" switch when committing.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 9 +++++++--\n 1 file changed, 7 insertions(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 9a06b40..64fec47 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -746,6 +746,7 @@ enum todo_command {\n \tTODO_PICK,\n \tTODO_REVERT,\n \tTODO_EDIT,\n+\tTODO_REWORD,\n \tTODO_FIXUP,\n \tTODO_SQUASH,\n \tTODO_EXEC,\n@@ -759,6 +760,7 @@ static struct {\n \t{ 'p', \"pick\" },\n \t{ 0,   \"revert\" },\n \t{ 'e', \"edit\" },\n+\t{ 'r', \"reword\" },\n \t{ 'f', \"fixup\" },\n \t{ 's', \"squash\" },\n \t{ 'x', \"exec\" },\n@@ -1004,7 +1006,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t}\n \t}\n \n-\tif (is_fixup(command)) {\n+\tif (command == TODO_REWORD)\n+\t\tedit = 1;\n+\telse if (is_fixup(command)) {\n \t\tif (update_squash_messages(command, commit, opts))\n \t\t\treturn -1;\n \t\tamend = 1;\n@@ -1755,7 +1759,8 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t}\n \t\t\telse if (res && is_rebase_i(opts))\n \t\t\t\treturn res | error_with_patch(item->commit,\n-\t\t\t\t\titem->arg, item->arg_len, opts, res, 0);\n+\t\t\t\t\titem->arg, item->arg_len, opts, res,\n+\t\t\t\t\titem->command == TODO_REWORD);\n \t\t}\n \t\telse if (item->command == TODO_EXEC) {\n \t\t\tchar *end_of_arg = (char *)(item->arg + item->arg_len);\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300649","messageId":"4c90d25df32e296dbace2b47e6f985c283ee23b4.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 19/34] sequencer (rebase -i): set the reflog message consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:55:41Z","receivedAt":"2016-08-31T08:56:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We already used the same reflog message as the scripted version of rebase\n-i when finishing. With this commit, we do that also for all the commands\nbefore that.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 4 ++++\n 1 file changed, 4 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 71730e6..54f654c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1768,6 +1768,10 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\tunlink(rebase_path_amend());\n \t\t}\n \t\tif (item->command <= TODO_SQUASH) {\n+\t\t\tif (is_rebase_i(opts))\n+\t\t\t\tsetenv(\"GIT_REFLOG_ACTION\", reflog_message(opts,\n+\t\t\t\t\tcommand_to_string(item->command), NULL),\n+\t\t\t\t\t1);\n \t\t\tres = do_pick_commit(item->command, item->commit,\n \t\t\t\t\topts, is_final_fixup(todo_list));\n \t\t\tif (item->command == TODO_EDIT) {\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300650","messageId":"375b045469a7c240033eb27d14553e28c81ea9a3.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 18/34] sequencer (rebase -i): refactor setting the reflog message","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:55:37Z","receivedAt":"2016-08-31T08:56:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This makes the code DRYer, with the obvious benefit that we can enhance\nthe code further in a single place.\n\nWe can also reuse the functionality elsewhere by calling this new\nfunction.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 33 ++++++++++++++++++++++++++-------\n 1 file changed, 26 insertions(+), 7 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 399fc8d..71730e6 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1726,6 +1726,26 @@ static int is_final_fixup(struct todo_list *todo_list)\n \treturn 1;\n }\n \n+static const char *reflog_message(struct replay_opts *opts,\n+\tconst char *sub_action, const char *fmt, ...)\n+{\n+\tva_list ap;\n+\tstatic struct strbuf buf = STRBUF_INIT;\n+\n+\tva_start(ap, fmt);\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addstr(&buf, action_name(opts));\n+\tif (sub_action)\n+\t\tstrbuf_addf(&buf, \" (%s)\", sub_action);\n+\tif (fmt) {\n+\t\tstrbuf_addstr(&buf, \": \");\n+\t\tstrbuf_vaddf(&buf, fmt, ap);\n+\t}\n+\tva_end(ap);\n+\n+\treturn buf.buf;\n+}\n+\n static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n {\n \tint res = 0;\n@@ -1796,6 +1816,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \n \t\tif (read_oneliner(&head_ref, rebase_path_head_name(), 0) &&\n \t\t\t\tstarts_with(head_ref.buf, \"refs/\")) {\n+\t\t\tconst char *msg;\n \t\t\tunsigned char head[20], orig[20];\n \n \t\t\tif (get_sha1(\"HEAD\", head))\n@@ -1803,19 +1824,17 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0) ||\n \t\t\t\t\tget_sha1_hex(buf.buf, orig))\n \t\t\t\treturn error(\"Could not read orig-head\");\n-\t\t\tstrbuf_addf(&buf, \"rebase -i (finish): %s onto \",\n-\t\t\t\thead_ref.buf);\n \t\t\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n \t\t\t\treturn error(\"Could not read 'onto'\");\n-\t\t\tif (update_ref(buf.buf, head_ref.buf, head, orig,\n+\t\t\tmsg = reflog_message(opts, \"finish\", \"%s onto %s\",\n+\t\t\t\thead_ref.buf, buf.buf);\n+\t\t\tif (update_ref(msg, head_ref.buf, head, orig,\n \t\t\t\t\tREF_NODEREF, UPDATE_REFS_MSG_ON_ERR))\n \t\t\t\treturn error(\"Could not update %s\",\n \t\t\t\t\thead_ref.buf);\n-\t\t\tstrbuf_reset(&buf);\n-\t\t\tstrbuf_addf(&buf,\n-\t\t\t\t\"rebase -i (finish): returning to %s\",\n+\t\t\tmsg = reflog_message(opts, \"finish\", \"returning to %s\",\n \t\t\t\thead_ref.buf);\n-\t\t\tif (create_symref(\"HEAD\", head_ref.buf, buf.buf))\n+\t\t\tif (create_symref(\"HEAD\", head_ref.buf, msg))\n \t\t\t\treturn error(\"Could not update HEAD to %s\",\n \t\t\t\t\thead_ref.buf);\n \t\t\tstrbuf_reset(&buf);\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300651","messageId":"556caa4188ba74731c84947b26b91939eb0a98a7.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 21/34] sequencer (rebase -i): record interrupted commits in rewritten, too","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:55:48Z","receivedAt":"2016-08-31T08:56:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When continuing after a `pick` command failed, we want that commit\nto show up in the rewritten-list (and its notes to be rewritten), too.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 11 +++++++++++\n 1 file changed, 11 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a5e18f3..a00d349 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2023,6 +2023,17 @@ int sequencer_continue(struct replay_opts *opts)\n \t\t\treturn error_dirty_index(opts);\n \t\ttodo_list.current++;\n \t}\n+\telse if (file_exists(rebase_path_stopped_sha())) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tstruct object_id oid;\n+\n+\t\tif (read_oneliner(&buf, rebase_path_stopped_sha(), 1)) {\n+\t\t\tif (!get_sha1_committish(buf.buf, oid.hash))\n+\t\t\t\trecord_in_rewritten(&oid,\n+\t\t\t\t\t\tpeek_command(&todo_list, 0));\n+\t\t}\n+\t\tstrbuf_release(&buf);\n+\t}\n \n \tres = pick_commits(&todo_list, opts);\n \ttodo_list_release(&todo_list);\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300652","messageId":"7427d5a9e76264252540c72c5a70e71c3d470667.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 20/34] sequencer (rebase -i): copy commit notes at end","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:55:44Z","receivedAt":"2016-08-31T08:56:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When rebasing commits that have commit notes attached, the interactive\nrebase rewrites those notes faithfully at the end. The sequencer must\ndo this, too, if it wishes to do interactive rebase's job.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 75 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 54f654c..a5e18f3 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -94,6 +94,15 @@ static GIT_PATH_FUNC(rebase_path_amend, \"rebase-merge/amend\")\n  */\n static GIT_PATH_FUNC(rebase_path_stopped_sha, \"rebase-merge/stopped-sha\")\n /*\n+ * For the post-rewrite hook, we make a list of rewritten commits and\n+ * their new sha1s.  The rewritten-pending list keeps the sha1s of\n+ * commits that have been processed, but not committed yet,\n+ * e.g. because they are waiting for a 'squash' command.\n+ */\n+static GIT_PATH_FUNC(rebase_path_rewritten_list, \"rebase-merge/rewritten-list\")\n+static GIT_PATH_FUNC(rebase_path_rewritten_pending,\n+\t\"rebase-merge/rewritten-pending\")\n+/*\n  * The following files are written by git-rebase just after parsing the\n  * command-line (and are only consumed, not modified, by the sequencer).\n  */\n@@ -886,6 +895,43 @@ static int update_squash_messages(enum todo_command command,\n \treturn write_message(&buf, rebase_path_squash_msg());\n }\n \n+static void flush_rewritten_pending(void) {\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tunsigned char newsha1[20];\n+\tFILE *out;\n+\n+\tif (strbuf_read_file(&buf, rebase_path_rewritten_pending(), 82) > 0 &&\n+\t\t\t!get_sha1(\"HEAD\", newsha1) &&\n+\t\t\t(out = fopen(rebase_path_rewritten_list(), \"a\"))) {\n+\t\tchar *bol = buf.buf, *eol;\n+\n+\t\twhile (*bol) {\n+\t\t\teol = strchrnul(bol, '\\n');\n+\t\t\tfprintf(out, \"%.*s %s\\n\", (int)(eol - bol),\n+\t\t\t\t\tbol, sha1_to_hex(newsha1));\n+\t\t\tif (!*eol)\n+\t\t\t\tbreak;\n+\t\t\tbol = eol + 1;\n+\t\t}\n+\t\tfclose(out);\n+\t\tunlink(rebase_path_rewritten_pending());\n+\t}\n+}\n+\n+static void record_in_rewritten(struct object_id *oid,\n+\t\tenum todo_command next_command) {\n+\tFILE *out = fopen(rebase_path_rewritten_pending(), \"a\");\n+\n+\tif (!out)\n+\t\treturn;\n+\n+\tfprintf(out, \"%s\\n\", oid_to_hex(oid));\n+\tfclose(out);\n+\n+\tif (!is_fixup(next_command))\n+\t\tflush_rewritten_pending();\n+}\n+\n static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tstruct replay_opts *opts, int final_fixup)\n {\n@@ -1726,6 +1772,17 @@ static int is_final_fixup(struct todo_list *todo_list)\n \treturn 1;\n }\n \n+static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n+{\n+\tint i;\n+\n+\tfor (i = todo_list->current + offset; i < todo_list->nr; i++)\n+\t\tif (todo_list->items[i].command != TODO_NOOP)\n+\t\t\treturn todo_list->items[i].command;\n+\n+\treturn -1;\n+}\n+\n static const char *reflog_message(struct replay_opts *opts,\n \tconst char *sub_action, const char *fmt, ...)\n {\n@@ -1784,6 +1841,9 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\t\titem->arg, item->arg_len, opts, res,\n \t\t\t\t\t!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+\t\t\t\t\tpeek_command(todo_list, 1));\n \t\t\tif (res && is_fixup(item->command)) {\n \t\t\t\tif (res == 1)\n \t\t\t\t\tintend_to_amend();\n@@ -1813,6 +1873,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \n \tif (is_rebase_i(opts)) {\n \t\tstruct strbuf head_ref = STRBUF_INIT, buf = STRBUF_INIT;\n+\t\tstruct stat st;\n \n \t\t/* Stopped in the middle, as planned? */\n \t\tif (todo_list->current < todo_list->nr)\n@@ -1857,6 +1918,20 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\trun_command_v_opt(argv, RUN_GIT_CMD);\n \t\t\tstrbuf_reset(&buf);\n \t\t}\n+\t\tflush_rewritten_pending();\n+\t\tif (!stat(rebase_path_rewritten_list(), &st) &&\n+\t\t\t\tst.st_size > 0) {\n+\t\t\tstruct child_process child = CHILD_PROCESS_INIT;\n+\n+\t\t\tchild.in = open(rebase_path_rewritten_list(), O_RDONLY);\n+\t\t\tchild.git_cmd = 1;\n+\t\t\targv_array_push(&child.args, \"notes\");\n+\t\t\targv_array_push(&child.args, \"copy\");\n+\t\t\targv_array_push(&child.args, \"--for-rewrite=rebase\");\n+\t\t\t/* we don't care if this copying failed */\n+\t\t\trun_command(&child);\n+\t\t}\n+\n \t\tstrbuf_release(&buf);\n \t\tstrbuf_release(&head_ref);\n \t}\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300653","messageId":"22df98870d37c140296439115f32706cc8e436c2.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 22/34] sequencer (rebase -i): run the post-rewrite hook, if needed","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:55:52Z","receivedAt":"2016-08-31T08:56:21Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 13 +++++++++++++\n 1 file changed, 13 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a00d349..153116e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1922,6 +1922,8 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tif (!stat(rebase_path_rewritten_list(), &st) &&\n \t\t\t\tst.st_size > 0) {\n \t\t\tstruct child_process child = CHILD_PROCESS_INIT;\n+\t\t\tconst char *post_rewrite_hook =\n+\t\t\t\tfind_hook(\"post-rewrite\");\n \n \t\t\tchild.in = open(rebase_path_rewritten_list(), O_RDONLY);\n \t\t\tchild.git_cmd = 1;\n@@ -1930,6 +1932,17 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\targv_array_push(&child.args, \"--for-rewrite=rebase\");\n \t\t\t/* we don't care if this copying failed */\n \t\t\trun_command(&child);\n+\n+\t\t\tif (post_rewrite_hook) {\n+\t\t\t\tstruct child_process hook = CHILD_PROCESS_INIT;\n+\n+\t\t\t\thook.in = open(rebase_path_rewritten_list(),\n+\t\t\t\t\tO_RDONLY);\n+\t\t\t\targv_array_push(&hook.args, post_rewrite_hook);\n+\t\t\t\targv_array_push(&hook.args, \"rebase\");\n+\t\t\t\t/* we don't care if this hook failed */\n+\t\t\t\trun_command(&hook);\n+\t\t\t}\n \t\t}\n \n \t\tstrbuf_release(&buf);\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300654","messageId":"34bb14c610acc230d55d41c440c5763768c51814.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 26/34] sequencer (rebase -i): implement the 'drop' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:56:05Z","receivedAt":"2016-08-31T08:56:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The parsing part of a 'drop' command is almost identical to parsing a\n'pick', while the operation is the same as that of a 'noop'.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 12 +++++++-----\n 1 file changed, 7 insertions(+), 5 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 366ee78..51c2f76 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -762,7 +762,8 @@ enum todo_command {\n \tTODO_FIXUP,\n \tTODO_SQUASH,\n \tTODO_EXEC,\n-\tTODO_NOOP\n+\tTODO_NOOP,\n+\tTODO_DROP\n };\n \n static struct {\n@@ -776,7 +777,8 @@ static struct {\n \t{ 'f', \"fixup\" },\n \t{ 's', \"squash\" },\n \t{ 'x', \"exec\" },\n-\t{ 0,   \"noop\" }\n+\t{ 0,   \"noop\" },\n+\t{ 'd', \"drop\" }\n };\n \n static const char *command_to_string(const enum todo_command command)\n@@ -1309,7 +1311,7 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n \t\telse if (is_fixup(item->command))\n \t\t\treturn error(\"Cannot '%s' without a previous commit\",\n \t\t\t\tcommand_to_string(item->command));\n-\t\telse if (item->command != TODO_NOOP)\n+\t\telse if (item->command < TODO_NOOP)\n \t\t\tfixup_okay = 1;\n \t\tp = *eol ? eol + 1 : eol;\n \t}\n@@ -1800,7 +1802,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \tint i;\n \n \tfor (i = todo_list->current + offset; i < todo_list->nr; i++)\n-\t\tif (todo_list->items[i].command != TODO_NOOP)\n+\t\tif (todo_list->items[i].command < TODO_NOOP)\n \t\t\treturn todo_list->items[i].command;\n \n \treturn -1;\n@@ -1933,7 +1935,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\tres = do_exec(item->arg);\n \t\t\t*end_of_arg = saved;\n \t\t}\n-\t\telse if (item->command != TODO_NOOP)\n+\t\telse if (item->command < TODO_NOOP)\n \t\t\treturn error(\"Unknown command %d\", item->command);\n \n \t\ttodo_list->current++;\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300655","messageId":"7329458d8dafa92e7d7a6275ff03b90b1035a72f.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 23/34] sequencer (rebase -i): respect the rebase.autostash setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:55:55Z","receivedAt":"2016-08-31T08:56:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Git's `rebase` command inspects the `rebase.autostash` config setting\nto determine whether it should stash any uncommitted changes before\nrebasing and re-apply them afterwards.\n\nAs we introduce more bits and pieces to let the sequencer act as\ninteractive rebase's backend, here is the part that adds support for\nthe autostash feature.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 43 +++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 43 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 153116e..2922837 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -111,6 +111,7 @@ static GIT_PATH_FUNC(rebase_path_orig_head, \"rebase-merge/orig-head\")\n static GIT_PATH_FUNC(rebase_path_verbose, \"rebase-merge/verbose\")\n static GIT_PATH_FUNC(rebase_path_head_name, \"rebase-merge/head-name\")\n static GIT_PATH_FUNC(rebase_path_onto, \"rebase-merge/onto\")\n+static GIT_PATH_FUNC(rebase_path_autostash, \"rebase-merge/autostash\")\n \n /* We will introduce the 'interactive rebase' mode later */\n static inline int is_rebase_i(const struct replay_opts *opts)\n@@ -1783,6 +1784,47 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \treturn -1;\n }\n \n+static int apply_autostash(struct replay_opts *opts)\n+{\n+\tstruct strbuf stash_sha1 = STRBUF_INIT;\n+\tstruct child_process child = CHILD_PROCESS_INIT;\n+\tint ret = 0;\n+\n+\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1)) {\n+\t\tstrbuf_release(&stash_sha1);\n+\t\treturn 0;\n+\t}\n+\tstrbuf_trim(&stash_sha1);\n+\n+\tchild.git_cmd = 1;\n+\targv_array_push(&child.args, \"stash\");\n+\targv_array_push(&child.args, \"apply\");\n+\targv_array_push(&child.args, stash_sha1.buf);\n+\tif (!run_command(&child))\n+\t\tprintf(_(\"Applied autostash.\"));\n+\telse {\n+\t\tstruct child_process store = CHILD_PROCESS_INIT;\n+\n+\t\tstore.git_cmd = 1;\n+\t\targv_array_push(&store.args, \"stash\");\n+\t\targv_array_push(&store.args, \"store\");\n+\t\targv_array_push(&store.args, \"-m\");\n+\t\targv_array_push(&store.args, \"autostash\");\n+\t\targv_array_push(&store.args, \"-q\");\n+\t\targv_array_push(&store.args, stash_sha1.buf);\n+\t\tif (run_command(&store))\n+\t\t\tret = error(_(\"Cannot store %s\"), stash_sha1.buf);\n+\t\telse\n+\t\t\tprintf(_(\"Applying autostash resulted in conflicts.\\n\"\n+\t\t\t\t\"Your changes are safe in the stash.\\n\"\n+\t\t\t\t\"You can run \\\"git stash pop\\\" or\"\n+\t\t\t\t\" \\\"git stash drop\\\" at any time.\\n\"));\n+\t}\n+\n+\tstrbuf_release(&stash_sha1);\n+\treturn ret;\n+}\n+\n static const char *reflog_message(struct replay_opts *opts,\n \tconst char *sub_action, const char *fmt, ...)\n {\n@@ -1944,6 +1986,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\trun_command(&hook);\n \t\t\t}\n \t\t}\n+\t\tapply_autostash(opts);\n \n \t\tstrbuf_release(&buf);\n \t\tstrbuf_release(&head_ref);\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300656","messageId":"736bcb8e860c876e32e8f89f68b0b901abedc187.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 27/34] sequencer (rebase -i): differentiate between comments and 'noop'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:56:07Z","receivedAt":"2016-08-31T08:56:26Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"In the upcoming patch, we will support rebase -i's progress\nreporting. The progress skips comments but counts 'noop's.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 14 ++++++++------\n 1 file changed, 8 insertions(+), 6 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 51c2f76..4c902e5 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -763,7 +763,8 @@ enum todo_command {\n \tTODO_SQUASH,\n \tTODO_EXEC,\n \tTODO_NOOP,\n-\tTODO_DROP\n+\tTODO_DROP,\n+\tTODO_COMMENT\n };\n \n static struct {\n@@ -778,12 +779,13 @@ static struct {\n \t{ 's', \"squash\" },\n \t{ 'x', \"exec\" },\n \t{ 0,   \"noop\" },\n-\t{ 'd', \"drop\" }\n+\t{ 'd', \"drop\" },\n+\t{ 0,   NULL }\n };\n \n static const char *command_to_string(const enum todo_command command)\n {\n-\tif (command < ARRAY_SIZE(todo_command_info))\n+\tif (command < TODO_COMMENT)\n \t\treturn todo_command_info[command].str;\n \tdie(\"Unknown command: %d\", command);\n }\n@@ -1235,14 +1237,14 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \tbol += strspn(bol, \" \\t\");\n \n \tif (bol == eol || *bol == '\\r' || *bol == comment_line_char) {\n-\t\titem->command = TODO_NOOP;\n+\t\titem->command = TODO_COMMENT;\n \t\titem->commit = NULL;\n \t\titem->arg = bol;\n \t\titem->arg_len = eol - bol;\n \t\treturn 0;\n \t}\n \n-\tfor (i = 0; i < ARRAY_SIZE(todo_command_info); i++)\n+\tfor (i = 0; i < TODO_COMMENT; i++)\n \t\tif (skip_prefix(bol, todo_command_info[i].str, &bol)) {\n \t\t\titem->command = i;\n \t\t\tbreak;\n@@ -1252,7 +1254,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \t\t\titem->command = i;\n \t\t\tbreak;\n \t\t}\n-\tif (i >= ARRAY_SIZE(todo_command_info))\n+\tif (i >= TODO_COMMENT)\n \t\treturn -1;\n \n \tif (item->command == TODO_NOOP) {\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300657","messageId":"4148c74b28d6e62f507a88a588ea90dc9924a672.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 24/34] sequencer (rebase -i): respect strategy/strategy_opts settings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:55:58Z","receivedAt":"2016-08-31T08:56:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The sequencer already has an idea about using different merge\nstrategies. We just piggy-back on top of that, using rebase -i's\nown settings, when running the sequencer in interactive rebase mode.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 22 ++++++++++++++++++++++\n 1 file changed, 22 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 2922837..5f5fb9f 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -112,6 +112,8 @@ static GIT_PATH_FUNC(rebase_path_verbose, \"rebase-merge/verbose\")\n static GIT_PATH_FUNC(rebase_path_head_name, \"rebase-merge/head-name\")\n static GIT_PATH_FUNC(rebase_path_onto, \"rebase-merge/onto\")\n static GIT_PATH_FUNC(rebase_path_autostash, \"rebase-merge/autostash\")\n+static GIT_PATH_FUNC(rebase_path_strategy, \"rebase-merge/strategy\")\n+static GIT_PATH_FUNC(rebase_path_strategy_opts, \"rebase-merge/strategy_opts\")\n \n /* We will introduce the 'interactive rebase' mode later */\n static inline int is_rebase_i(const struct replay_opts *opts)\n@@ -1404,6 +1406,26 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\tif (file_exists(rebase_path_verbose()))\n \t\t\topts->verbose = 1;\n \n+\t\tif (read_oneliner(&buf, rebase_path_strategy(), 0)) {\n+\t\t\topts->strategy =\n+\t\t\t\tsequencer_entrust(opts,\n+\t\t\t\t\t\t  strbuf_detach(&buf, NULL));\n+\t\t\tif (read_oneliner(&buf,\n+\t\t\t\t\t  rebase_path_strategy_opts(), 0)) {\n+\t\t\t\tint i;\n+\t\t\t\topts->xopts_nr = split_cmdline(buf.buf,\n+\t\t\t\t\t&opts->xopts);\n+\t\t\t\tfor (i = 0; i < opts->xopts_nr; i++)\n+\t\t\t\t\tskip_prefix(opts->xopts[i], \"--\",\n+\t\t\t\t\t\t    &opts->xopts[i]);\n+\t\t\t\tif (opts->xopts_nr)\n+\t\t\t\t\tsequencer_entrust(opts,\n+\t\t\t\t\t\tstrbuf_detach(&buf, NULL));\n+\t\t\t\telse\n+\t\t\t\t\tstrbuf_release(&buf);\n+\t\t\t}\n+\t\t}\n+\n \t\treturn 0;\n \t}\n \n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300658","messageId":"7e3a0120e1da6ccad814d3ab3826d9ef6faa68e7.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 25/34] sequencer (rebase -i): allow rescheduling commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:56:01Z","receivedAt":"2016-08-31T08:56:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The interactive rebase has the very special magic that a cherry-pick\nthat exits with a status different from 0 and 1 signifies a failure to\neven record that a cherry-pick was started.\n\nThis can happen e.g. when a fast-forward fails because it would\noverwrite untracked files.\n\nIn that case, we must reschedule the command that we thought we already\nhad at least started successfully.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 6 ++++++\n 1 file changed, 6 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 5f5fb9f..366ee78 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1895,6 +1895,12 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\t\t1);\n \t\t\tres = do_pick_commit(item->command, item->commit,\n \t\t\t\t\topts, is_final_fixup(todo_list));\n+\t\t\tif (is_rebase_i(opts) && res < 0) {\n+\t\t\t\t/* Reschedule */\n+\t\t\t\ttodo_list->current--;\n+\t\t\t\tif (save_todo(todo_list, opts))\n+\t\t\t\t\treturn -1;\n+\t\t\t}\n \t\t\tif (item->command == TODO_EDIT) {\n \t\t\t\tstruct commit *commit = item->commit;\n \t\t\t\tif (!res)\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300659","messageId":"84b2be2078f4fb4853cb5725df9ae8e387dff42c.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 17/34] sequencer (rebase -i): allow fast-forwarding for edit/reword","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:55:34Z","receivedAt":"2016-08-31T08:56:33Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The sequencer already knew how to fast-forward instead of\ncherry-picking, if possible.\n\nWe want to continue to do this, of course, but in case of the 'reword'\ncommand, we will need to call `git commit` after fast-forwarding.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 25 +++++++++++++++++--------\n 1 file changed, 17 insertions(+), 8 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 64fec47..399fc8d 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -896,7 +896,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \tconst char *base_label, *next_label;\n \tstruct commit_message msg = { NULL, NULL, NULL, NULL };\n \tstruct strbuf msgbuf = STRBUF_INIT;\n-\tint res = 0, unborn = 0, amend = 0, allow;\n+\tint res = 0, unborn = 0, amend = 0, allow = 0;\n \n \tif (opts->no_commit) {\n \t\t/*\n@@ -942,11 +942,23 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \telse\n \t\tparent = commit->parents->item;\n \n+\tif (get_message(commit, &msg) != 0)\n+\t\treturn error(_(\"Cannot get commit message for %s\"),\n+\t\t\toid_to_hex(&commit->object.oid));\n+\n \tif (opts->allow_ff && !is_fixup(command) &&\n \t    ((parent && !hashcmp(parent->object.oid.hash, head)) ||\n-\t     (!parent && unborn)))\n-\t\treturn fast_forward_to(commit->object.oid.hash, head, unborn, opts);\n-\n+\t     (!parent && unborn))) {\n+\t\tif (is_rebase_i(opts))\n+\t\t\twrite_author_script(msg.message);\n+\t\tres |= fast_forward_to(commit->object.oid.hash, head, unborn,\n+\t\t\topts);\n+\t\tif (res || command != TODO_REWORD)\n+\t\t\tgoto leave;\n+\t\tedit = amend = 1;\n+\t\tmsg_file = NULL;\n+\t\tgoto fast_forward_edit;\n+\t}\n \tif (parent && parse_commit(parent) < 0)\n \t\t/* TRANSLATORS: The first %s will be \"revert\" or\n \t\t   \"cherry-pick\", the second %s a SHA1 */\n@@ -954,10 +966,6 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t\tcommand_to_string(command),\n \t\t\toid_to_hex(&parent->object.oid));\n \n-\tif (get_message(commit, &msg) != 0)\n-\t\treturn error(_(\"Cannot get commit message for %s\"),\n-\t\t\toid_to_hex(&commit->object.oid));\n-\n \t/*\n \t * \"commit\" is an existing commit.  We would want to apply\n \t * the difference it introduces since its first parent \"prev\"\n@@ -1083,6 +1091,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tgoto leave;\n \t}\n \tif (!opts->no_commit)\n+fast_forward_edit:\n \t\tres |= sequencer_commit(msg_file, opts, allow, edit, amend,\n \t\t\tcleanup_commit_message);\n \n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300660","messageId":"6383b7afcdeb6c999862aa32ba437997f2dd3d4e.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 28/34] run_command_opt(): optionally hide stderr when the command succeeds","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:56:10Z","receivedAt":"2016-08-31T08:56:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This will be needed to hide the output of `git commit` when the\nsequencer handles an interactive rebase's script.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n run-command.c | 23 +++++++++++++++++++++++\n run-command.h |  1 +\n 2 files changed, 24 insertions(+)\n\ndiff --git a/run-command.c b/run-command.c\nindex 5a4dbb6..921e43c 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -575,6 +575,29 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const\n \tcmd.clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0;\n \tcmd.dir = dir;\n \tcmd.env = env;\n+\n+\tif (opt & RUN_HIDE_STDERR_ON_SUCCESS) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tint res;\n+\n+\t\tcmd.err = -1;\n+\t\tif (start_command(&cmd) < 0)\n+\t\t\treturn -1;\n+\n+\t\tif (strbuf_read(&buf, cmd.err, 0) < 0) {\n+\t\t\tclose(cmd.err);\n+\t\t\tfinish_command(&cmd); /* throw away exit code */\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tclose(cmd.err);\n+\t\tres = finish_command(&cmd);\n+\t\tif (res)\n+\t\t\tfputs(buf.buf, stderr);\n+\t\tstrbuf_release(&buf);\n+\t\treturn res;\n+\t}\n+\n \treturn run_command(&cmd);\n }\n \ndiff --git a/run-command.h b/run-command.h\nindex 5066649..f87d01a 100644\n--- a/run-command.h\n+++ b/run-command.h\n@@ -70,6 +70,7 @@ extern int run_hook_ve(const char *const *env, const char *name, va_list args);\n #define RUN_SILENT_EXEC_FAILURE 8\n #define RUN_USING_SHELL 16\n #define RUN_CLEAN_ON_EXIT 32\n+#define RUN_HIDE_STDERR_ON_SUCCESS 64\n int run_command_v_opt(const char **argv, int opt);\n \n /*\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300661","messageId":"2b8220c24c1c9094783f7626f5f40dc2db8855c1.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 29/34] sequencer (rebase -i): show only failed `git commit`'s output","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:56:29Z","receivedAt":"2016-08-31T08:56:53Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is the behavior of the shell script version of the interactive\nrebase, by using the `output` function defined in `git-rebase.sh`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 9 +++++++--\n 1 file changed, 7 insertions(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 4c902e5..22568d2 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -640,10 +640,15 @@ int sequencer_commit(const char *defmsg, struct replay_opts *opts,\n {\n \tchar **env = NULL;\n \tstruct argv_array array;\n-\tint rc;\n+\tint opt = RUN_GIT_CMD, rc;\n \tconst char *value;\n \n \tif (is_rebase_i(opts)) {\n+\t\tif (!edit) {\n+\t\t\topt |= RUN_COMMAND_STDOUT_TO_STDERR;\n+\t\t\topt |= RUN_HIDE_STDERR_ON_SUCCESS;\n+\t\t}\n+\n \t\tenv = read_author_script();\n \t\tif (!env) {\n \t\t\tconst char *gpg_opt = gpg_sign_opt_quoted(opts);\n@@ -688,7 +693,7 @@ int sequencer_commit(const char *defmsg, struct replay_opts *opts,\n \tif (opts->allow_empty_message)\n \t\targv_array_push(&array, \"--allow-empty-message\");\n \n-\trc = run_command_v_opt_cd_env(array.argv, RUN_GIT_CMD, NULL,\n+\trc = run_command_v_opt_cd_env(array.argv, opt, NULL,\n \t\t\t(const char *const *)env);\n \targv_array_clear(&array);\n \tfree(env);\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300662","messageId":"fa3efbbadd7eeb9b7e357c5e6fa23e737fc5e048.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 31/34] sequencer (rebase -i): suggest --edit-todo upon unknown command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:56:36Z","receivedAt":"2016-08-31T08:56:54Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is the same behavior as known from `git rebase -i`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex b34c381..89fd625 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1346,8 +1346,12 @@ static int read_populate_todo(struct todo_list *todo_list,\n \tclose(fd);\n \n \tres = parse_insn_buffer(todo_list->buf.buf, todo_list);\n-\tif (res)\n+\tif (res) {\n+\t\tif (is_rebase_i(opts))\n+\t\t\treturn error(\"Please fix this using \"\n+\t\t\t\t\"'git rebase --edit-todo'.\");\n \t\treturn error(_(\"Unusable instruction sheet: %s\"), todo_file);\n+\t}\n \tif (!todo_list->nr &&\n \t    (!is_rebase_i(opts) || !file_exists(rebase_path_done())))\n \t\treturn error(_(\"No commits parsed.\"));\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300663","messageId":"3207457a57d056c3054faf718128fb5c806cb032.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 32/34] sequencer (rebase -i): show the progress","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:56:40Z","receivedAt":"2016-08-31T08:57:03Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The interactive rebase keeps the user informed about its progress.\nIf the sequencer wants to do the grunt work of the interactive\nrebase, it also needs to show that progress.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 33 +++++++++++++++++++++++++++++++++\n 1 file changed, 33 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 89fd625..e8c6daf 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1218,6 +1218,7 @@ struct todo_list {\n \tstruct strbuf buf;\n \tstruct todo_item *items;\n \tint nr, alloc, current;\n+\tint done_nr, total_nr;\n };\n \n #define TODO_LIST_INIT { STRBUF_INIT }\n@@ -1329,6 +1330,17 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n \treturn res;\n }\n \n+static int count_commands(struct todo_list *todo_list)\n+{\n+\tint count = 0, i;\n+\n+\tfor (i = 0; i < todo_list->nr; i++)\n+\t\tif (todo_list->items[i].command != TODO_COMMENT)\n+\t\t\tcount++;\n+\n+\treturn count;\n+}\n+\n static int read_populate_todo(struct todo_list *todo_list,\n \t\t\tstruct replay_opts *opts)\n {\n@@ -1355,6 +1367,22 @@ static int read_populate_todo(struct todo_list *todo_list,\n \tif (!todo_list->nr &&\n \t    (!is_rebase_i(opts) || !file_exists(rebase_path_done())))\n \t\treturn error(_(\"No commits parsed.\"));\n+\n+\tif (is_rebase_i(opts)) {\n+\t\tstruct todo_list done = TODO_LIST_INIT;\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\ttodo_list->done_nr = count_commands(&done);\n+\t\telse\n+\t\t\ttodo_list->done_nr = 0;\n+\n+\t\ttodo_list->total_nr = todo_list->done_nr\n+\t\t\t+ count_commands(todo_list);\n+\n+\t\ttodo_list_release(&done);\n+\t}\n+\n \treturn 0;\n }\n \n@@ -1900,6 +1928,11 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n \t\tif (is_rebase_i(opts)) {\n+\t\t\tif (item->command != TODO_COMMENT)\n+\t\t\t\tfprintf(stderr, \"Rebasing (%d/%d)%s\",\n+\t\t\t\t\t++(todo_list->done_nr),\n+\t\t\t\t\ttodo_list->total_nr,\n+\t\t\t\t\topts->verbose ? \"\\n\" : \"\\r\");\n \t\t\tunlink(rebase_path_message());\n \t\t\tunlink(rebase_path_author_script());\n \t\t\tunlink(rebase_path_stopped_sha());\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300664","messageId":"479efdc7e7c4f7bf92cce7a7033c5d27df9606ca.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 33/34] sequencer (rebase -i): write the progress into files","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:56:44Z","receivedAt":"2016-08-31T08:57:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"For the benefit of e.g. the shell prompt, the interactive rebase not\nonly displays the progress for the user to see, but also writes it into\nthe msgnum/end files in the state directory.\n\nTeach the sequencer this new trick.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 30 +++++++++++++++++++++++++++---\n 1 file changed, 27 insertions(+), 3 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex e8c6daf..9a8069e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -45,6 +45,16 @@ static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n  */\n static GIT_PATH_FUNC(rebase_path_done, \"rebase-merge/done\")\n /*\n+ * The file to keep track of how many commands were already processed (e.g.\n+ * for the prompt).\n+ */\n+static GIT_PATH_FUNC(rebase_path_msgnum, \"rebase-merge/msgnum\");\n+/*\n+ * The file to keep track of how many commands are to be processed in total\n+ * (e.g. for the prompt).\n+ */\n+static GIT_PATH_FUNC(rebase_path_msgtotal, \"rebase-merge/end\");\n+/*\n  * The commit message that is planned to be used for any changes that\n  * need to be committed following a user interaction.\n  */\n@@ -1370,17 +1380,22 @@ static int read_populate_todo(struct todo_list *todo_list,\n \n \tif (is_rebase_i(opts)) {\n \t\tstruct todo_list done = TODO_LIST_INIT;\n+\t\tFILE *f = fopen(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\ttodo_list->done_nr = count_commands(&done);\n \t\telse\n \t\t\ttodo_list->done_nr = 0;\n+\t\ttodo_list_release(&done);\n \n \t\ttodo_list->total_nr = todo_list->done_nr\n \t\t\t+ count_commands(todo_list);\n \n-\t\ttodo_list_release(&done);\n+\t\tif (f) {\n+\t\t\tfprintf(f, \"%d\\n\", todo_list->total_nr);\n+\t\t\tfclose(f);\n+\t\t}\n \t}\n \n \treturn 0;\n@@ -1928,11 +1943,20 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n \t\tif (is_rebase_i(opts)) {\n-\t\t\tif (item->command != TODO_COMMENT)\n+\t\t\tif (item->command != TODO_COMMENT) {\n+\t\t\t\tFILE *f = fopen(rebase_path_msgnum(), \"w\");\n+\n+\t\t\t\ttodo_list->done_nr++;\n+\n+\t\t\t\tif (f) {\n+\t\t\t\t\tfprintf(f, \"%d\\n\", todo_list->done_nr);\n+\t\t\t\t\tfclose(f);\n+\t\t\t\t}\n \t\t\t\tfprintf(stderr, \"Rebasing (%d/%d)%s\",\n-\t\t\t\t\t++(todo_list->done_nr),\n+\t\t\t\t\ttodo_list->done_nr,\n \t\t\t\t\ttodo_list->total_nr,\n \t\t\t\t\topts->verbose ? \"\\n\" : \"\\r\");\n+\t\t\t}\n \t\t\tunlink(rebase_path_message());\n \t\t\tunlink(rebase_path_author_script());\n \t\t\tunlink(rebase_path_stopped_sha());\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300665","messageId":"660acea3122b6379c7545b04fbecd94243c9fd02.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 30/34] sequencer (rebase -i): show only failed cherry-picks' output","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:56:32Z","receivedAt":"2016-08-31T08:57:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is the behavior of the shell script version of the interactive\nrebase, by using the `output` function defined in `git-rebase.sh`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 4 ++++\n 1 file changed, 4 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 22568d2..b34c381 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -477,6 +477,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \to.ancestor = base ? base_label : \"(empty tree)\";\n \to.branch1 = \"HEAD\";\n \to.branch2 = next ? next_label : \"(empty tree)\";\n+\tif (is_rebase_i(opts))\n+\t\to.buffer_output = 2;\n \n \thead_tree = parse_tree_indirect(head);\n \tnext_tree = next ? next->tree : empty_tree();\n@@ -488,6 +490,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \tclean = merge_trees(&o,\n \t\t\t    head_tree,\n \t\t\t    next_tree, base_tree, &result);\n+\tif (is_rebase_i(opts) && clean <= 0)\n+\t\tfputs(o.obuf.buf, stdout);\n \tstrbuf_release(&o.obuf);\n \tif (clean < 0)\n \t\treturn clean;\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300666","messageId":"02db26b4320bd9f8296358acd2a493555acae9aa.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 34/34] sequencer (rebase -i): write out the final message","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:56:48Z","receivedAt":"2016-08-31T08:57:08Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The shell script version of the interactive rebase has a very specific\nfinal message. Teach the sequencer to print the same.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 3 +++\n 1 file changed, 3 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 9a8069e..6064018 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2090,6 +2090,9 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t}\n \t\tapply_autostash(opts);\n \n+\t\tfprintf(stderr, \"Successfully rebased and updated %s.\\n\",\n+\t\t\thead_ref.buf);\n+\n \t\tstrbuf_release(&buf);\n \t\tstrbuf_release(&head_ref);\n \t}\n-- \n2.10.0.rc2.102.g5c102ec\n"},{"id":"300667","messageId":"cade51f893ed381ed03718439c7dba3f5dcebba3.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 15/34] sequencer (rebase -i): leave a patch upon error","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:55:26Z","receivedAt":"2016-08-31T08:57:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Just like the interactive rebase, we want to leave a 'patch' file for\nfurther inspection by the user (even if we never tried to actually apply\nthat patch, since we're cherry-picking instead).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 3 +++\n 1 file changed, 3 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 7662222..9a06b40 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1753,6 +1753,9 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\treturn error_failed_squash(item->commit, opts,\n \t\t\t\t\titem->arg_len, item->arg);\n \t\t\t}\n+\t\t\telse if (res && is_rebase_i(opts))\n+\t\t\t\treturn res | error_with_patch(item->commit,\n+\t\t\t\t\titem->arg, item->arg_len, opts, res, 0);\n \t\t}\n \t\telse if (item->command == TODO_EXEC) {\n \t\t\tchar *end_of_arg = (char *)(item->arg + item->arg_len);\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300668","messageId":"516d2aaf8837c8675141cf9c330c18c44765b69d.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 06/34] sequencer (rebase -i): write the 'done' file","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:54:38Z","receivedAt":"2016-08-31T08:57:53Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"In the interactive rebase, commands that were successfully processed are\nnot simply discarded, but appended to the 'done' file instead. This is\nused e.g. to display the current state to the user in the output of\n`git status` or the progress.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 20 ++++++++++++++++++++\n 1 file changed, 20 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 95c31bb..3bff3d9 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -39,6 +39,12 @@ static GIT_PATH_FUNC(rebase_path, \"rebase-merge\")\n  */\n static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n /*\n+ * The rebase command lines that have already been processed. A line\n+ * is moved here when it is first handled, before any associated user\n+ * actions.\n+ */\n+static GIT_PATH_FUNC(rebase_path_done, \"rebase-merge/done\")\n+/*\n  * A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and\n  * GIT_AUTHOR_DATE that will be used for the commit that is currently\n  * being rebased.\n@@ -1272,6 +1278,20 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\ttodo_path, strerror(errno));\n \tif (commit_lock_file(&todo_lock) < 0)\n \t\treturn error(_(\"Error wrapping up %s.\"), todo_path);\n+\n+\tif (is_rebase_i(opts)) {\n+\t\tconst char *done_path = rebase_path_done();\n+\t\tint fd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);\n+\t\tint prev_offset = !next ? 0 :\n+\t\t\ttodo_list->items[next - 1].offset_in_buf;\n+\n+\t\tif (offset > prev_offset && write_in_full(fd,\n+\t\t\t\ttodo_list->buf.buf + prev_offset,\n+\t\t\t\toffset - prev_offset) < 0)\n+\t\t\treturn error(_(\"Could not write to %s (%s)\"),\n+\t\t\t\tdone_path, strerror(errno));\n+\t\tclose(fd);\n+\t}\n \treturn 0;\n }\n \n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300669","messageId":"8dc2674ad2f2a0e0d751e876e1ace743d04eba0f.1472633606.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH 14/34] sequencer (rebase -i): update refs after a successful rebase","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T08:55:08Z","receivedAt":"2016-08-31T08:57:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"An interactive rebase operates on a detached HEAD (to keep the reflog\nof the original branch relatively clean), and updates the branch only\nat the end.\n\nNow that the sequencer learns to perform interactive rebases, it also\nneeds to learn the trick to update the branch before removing the\ndirectory containing the state of the interactive rebase.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 32 +++++++++++++++++++++++++++++++-\n 1 file changed, 31 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex d4437f5..7662222 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -100,6 +100,8 @@ static GIT_PATH_FUNC(rebase_path_stopped_sha, \"rebase-merge/stopped-sha\")\n static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, \"rebase-merge/gpg_sign_opt\")\n static GIT_PATH_FUNC(rebase_path_orig_head, \"rebase-merge/orig-head\")\n static GIT_PATH_FUNC(rebase_path_verbose, \"rebase-merge/verbose\")\n+static GIT_PATH_FUNC(rebase_path_head_name, \"rebase-merge/head-name\")\n+static GIT_PATH_FUNC(rebase_path_onto, \"rebase-merge/onto\")\n \n /* We will introduce the 'interactive rebase' mode later */\n static inline int is_rebase_i(const struct replay_opts *opts)\n@@ -1769,12 +1771,39 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t}\n \n \tif (is_rebase_i(opts)) {\n-\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tstruct strbuf head_ref = STRBUF_INIT, buf = STRBUF_INIT;\n \n \t\t/* Stopped in the middle, as planned? */\n \t\tif (todo_list->current < todo_list->nr)\n \t\t\treturn 0;\n \n+\t\tif (read_oneliner(&head_ref, rebase_path_head_name(), 0) &&\n+\t\t\t\tstarts_with(head_ref.buf, \"refs/\")) {\n+\t\t\tunsigned char head[20], orig[20];\n+\n+\t\t\tif (get_sha1(\"HEAD\", head))\n+\t\t\t\treturn error(\"Cannot read HEAD\");\n+\t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0) ||\n+\t\t\t\t\tget_sha1_hex(buf.buf, orig))\n+\t\t\t\treturn error(\"Could not read orig-head\");\n+\t\t\tstrbuf_addf(&buf, \"rebase -i (finish): %s onto \",\n+\t\t\t\thead_ref.buf);\n+\t\t\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n+\t\t\t\treturn error(\"Could not read 'onto'\");\n+\t\t\tif (update_ref(buf.buf, head_ref.buf, head, orig,\n+\t\t\t\t\tREF_NODEREF, UPDATE_REFS_MSG_ON_ERR))\n+\t\t\t\treturn error(\"Could not update %s\",\n+\t\t\t\t\thead_ref.buf);\n+\t\t\tstrbuf_reset(&buf);\n+\t\t\tstrbuf_addf(&buf,\n+\t\t\t\t\"rebase -i (finish): returning to %s\",\n+\t\t\t\thead_ref.buf);\n+\t\t\tif (create_symref(\"HEAD\", head_ref.buf, buf.buf))\n+\t\t\t\treturn error(\"Could not update HEAD to %s\",\n+\t\t\t\t\thead_ref.buf);\n+\t\t\tstrbuf_reset(&buf);\n+\t\t}\n+\n \t\tif (opts->verbose) {\n \t\t\tconst char *argv[] = {\n \t\t\t\t\"diff-tree\", \"--stat\", NULL, NULL\n@@ -1789,6 +1818,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\tstrbuf_reset(&buf);\n \t\t}\n \t\tstrbuf_release(&buf);\n+\t\tstrbuf_release(&head_ref);\n \t}\n \n \t/*\n-- \n2.10.0.rc2.102.g5c102ec\n\n\n"},{"id":"300710","messageId":"1472668767.4265.73.camel@kaarsemaker.net","threadId":"43966","inReplyTo":"68118621a481d786dd57d5c7fd4c3ea0a8e61b07.1472633606.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 05/34] sequencer (rebase -i): learn about the 'verbose' mode","fromName":"Dennis Kaarsemaker","fromEmail":"dennis@kaarsemaker.net","sentAt":"2016-08-31T18:39:27Z","receivedAt":"2016-08-31T18:39:35Z","isPatch":true,"sender":{"key":"dennis@kaarsemaker.net","avatar":"https://avatars.githubusercontent.com/u/200649?v=4"},"body":"On wo, 2016-08-31 at 10:54 +0200, Johannes Schindelin wrote:\n\n> +               if (file_exists(rebase_path_verbose()))\n> +                       opts->verbose = 1;\n\nI don't see anything in this series that creates this file, will that\nbe part of a later series?\n\nD.\n"},{"id":"300711","messageId":"1472668771.4265.74.camel@kaarsemaker.net","threadId":"43966","inReplyTo":"516d2aaf8837c8675141cf9c330c18c44765b69d.1472633606.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 06/34] sequencer (rebase -i): write the 'done' file","fromName":"Dennis Kaarsemaker","fromEmail":"dennis@kaarsemaker.net","sentAt":"2016-08-31T18:39:31Z","receivedAt":"2016-08-31T18:39:39Z","isPatch":true,"sender":{"key":"dennis@kaarsemaker.net","avatar":"https://avatars.githubusercontent.com/u/200649?v=4"},"body":"On wo, 2016-08-31 at 10:54 +0200, Johannes Schindelin wrote:\n> In the interactive rebase, commands that were successfully processed are\n> not simply discarded, but appended to the 'done' file instead. This is\n> used e.g. to display the current state to the user in the output of\n> `git status` or the progress.\n\nWouldn't it make more sense to have this patch before the ones that\nimplement the actual rebase commands?\n\nHmm, and after reading more of this series, I think the same applies to\nsome other patches too, e.g. 08/34 and 14/34, so I'm probably missing\nsomething. So before I make a fool of myself and suggest that the\nimplementation of the actual commands should come at the end, maybe you\ncould tell me what I'm missing :) \n\nD.\n"},{"id":"300712","messageId":"1472668773.4265.75.camel@kaarsemaker.net","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 00/34] Teach the sequencer to act as rebase -i's backend","fromName":"Dennis Kaarsemaker","fromEmail":"dennis@kaarsemaker.net","sentAt":"2016-08-31T18:39:33Z","receivedAt":"2016-08-31T18:39:43Z","isPatch":true,"sender":{"key":"dennis@kaarsemaker.net","avatar":"https://avatars.githubusercontent.com/u/200649?v=4"},"body":"On wo, 2016-08-31 at 10:53 +0200, Johannes Schindelin wrote:\n> This marks the count down to '3': two more patch series after this\n> (really tiny ones) and we have a faster rebase -i.\n\nI got to 16/34 (and skipped 07/34), will continue tomorrow. I hope the\ncomments are useful.\n\nD.\n"},{"id":"300713","messageId":"1472668778.4265.76.camel@kaarsemaker.net","threadId":"43966","inReplyTo":"cade51f893ed381ed03718439c7dba3f5dcebba3.1472633606.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 15/34] sequencer (rebase -i): leave a patch upon error","fromName":"Dennis Kaarsemaker","fromEmail":"dennis@kaarsemaker.net","sentAt":"2016-08-31T18:39:38Z","receivedAt":"2016-08-31T18:39:47Z","isPatch":true,"sender":{"key":"dennis@kaarsemaker.net","avatar":"https://avatars.githubusercontent.com/u/200649?v=4"},"body":"On wo, 2016-08-31 at 10:55 +0200, Johannes Schindelin wrote:\n> Just like the interactive rebase, we want to leave a 'patch' file for\n> further inspection by the user (even if we never tried to actually apply\n> that patch, since we're cherry-picking instead).\n> \n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n\nThis commit message confuses me. Did you mean s/Just like the/When\ndoing an/? \n\nD.\n"},{"id":"300735","messageId":"alpine.DEB.2.20.1608312236210.129229@virtualbox","threadId":"43966","inReplyTo":"1472668767.4265.73.camel@kaarsemaker.net","subject":"Re: [PATCH 05/34] sequencer (rebase -i): learn about the 'verbose' mode","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-08-31T20:36:49Z","receivedAt":"2016-08-31T20:37:16Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Dennis,\n\nOn Wed, 31 Aug 2016, Dennis Kaarsemaker wrote:\n\n> On wo, 2016-08-31 at 10:54 +0200, Johannes Schindelin wrote:\n> \n> > +               if (file_exists(rebase_path_verbose()))\n> > +                       opts->verbose = 1;\n> \n> I don't see anything in this series that creates this file, will that\n> be part of a later series?\n\nNo. The sequencer does not write that file, but just consumes it. `git\nrebase` writes it.\n\nCiao,\nDscho"},{"id":"300770","messageId":"1472714431.4680.1.camel@kaarsemaker.net","threadId":"43966","inReplyTo":"7427d5a9e76264252540c72c5a70e71c3d470667.1472633606.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 20/34] sequencer (rebase -i): copy commit notes at end","fromName":"Dennis Kaarsemaker","fromEmail":"dennis@kaarsemaker.net","sentAt":"2016-09-01T07:20:31Z","receivedAt":"2016-09-01T07:25:10Z","isPatch":true,"sender":{"key":"dennis@kaarsemaker.net","avatar":"https://avatars.githubusercontent.com/u/200649?v=4"},"body":"On wo, 2016-08-31 at 10:55 +0200, Johannes Schindelin wrote:\n> +               if (!stat(rebase_path_rewritten_list(), &st) &&\n> +                               st.st_size > 0) {\n> +                       struct child_process child = CHILD_PROCESS_INIT;\n> +\n> +                       child.in = open(rebase_path_rewritten_list(), O_RDONLY);\n> +                       child.git_cmd = 1;\n> +                       argv_array_push(&child.args, \"notes\");\n> +                       argv_array_push(&child.args, \"copy\");\n> +                       argv_array_push(&child.args, \"--for-rewrite=rebase\");\n> +                       /* we don't care if this copying failed */\n> +                       run_command(&child);\n> +               }\n\nI know this is a strict port of git-rebase--interactive.sh, but\nshouldn't we at least warn the user that the copy failed?\n\nD.\n"},{"id":"300783","messageId":"1472718808.4680.19.camel@kaarsemaker.net","threadId":"43966","inReplyTo":"5488a031ffe14373b7434d497b7fd2f2e5fe55bd.1472633606.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Dennis Kaarsemaker","fromEmail":"dennis@kaarsemaker.net","sentAt":"2016-09-01T08:33:28Z","receivedAt":"2016-09-01T08:33:50Z","isPatch":true,"sender":{"key":"dennis@kaarsemaker.net","avatar":"https://avatars.githubusercontent.com/u/200649?v=4"},"body":"On wo, 2016-08-31 at 10:54 +0200, Johannes Schindelin wrote:\n\n> +static int is_fixup(enum todo_command command)\n> +{\n> +\treturn command == TODO_FIXUP || command == TODO_SQUASH;\n> +}\n\nIt sounds wrong to have a function named is_fixup return true when the\ncommand isn't a fixup but a squash. Maybe name it\nchanges_previous_commit or something?\n\n> +static const char *nth_for_number(int n)\n> +{\n> +\tint n1 = n % 10, n10 = n % 100;\n> +\n> +\tif (n1 == 1 && n10 != 11)\n> +\t\treturn \"st\";\n> +\tif (n1 == 2 && n10 != 12)\n> +\t\treturn \"nd\";\n> +\tif (n1 == 3 && n10 != 13)\n> +\t\treturn \"rd\";\n> +\treturn \"th\";\n> +}\n\n>8---\n\n> +\tif (command == TODO_SQUASH) {\n> +\t\tunlink(rebase_path_fixup_msg());\n> +\t\tstrbuf_addf(&buf, \"\\n%c This is the %d%s commit message:\\n\\n%s\",\n> +\t\t\tcomment_line_char,\n> +\t\t\tcount, nth_for_number(count), body);\n> +\t}\n> +\telse if (command == TODO_FIXUP) {\n> +\t\tstrbuf_addf(&buf,\n> +\t\t\t\"\\n%c The %d%s commit message will be skipped:\\n\\n\",\n> +\t\t\tcomment_line_char, count, nth_for_number(count));\n> +\t\tstrbuf_add_commented_lines(&buf, body, strlen(body));\n> +\t}\n\nThis way of handling numbers is not translatable, and I really think we\nshould mark these strings for translation, like they are in the .sh\nversion.\n\nD.\n"},{"id":"300784","messageId":"1472718812.4680.20.camel@kaarsemaker.net","threadId":"43966","inReplyTo":"3207457a57d056c3054faf718128fb5c806cb032.1472633606.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 32/34] sequencer (rebase -i): show the progress","fromName":"Dennis Kaarsemaker","fromEmail":"dennis@kaarsemaker.net","sentAt":"2016-09-01T08:33:32Z","receivedAt":"2016-09-01T08:33:53Z","isPatch":true,"sender":{"key":"dennis@kaarsemaker.net","avatar":"https://avatars.githubusercontent.com/u/200649?v=4"},"body":"On wo, 2016-08-31 at 10:56 +0200, Johannes Schindelin wrote:\n> The interactive rebase keeps the user informed about its progress.\n> If the sequencer wants to do the grunt work of the interactive\n> rebase, it also needs to show that progress.\n> \n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  sequencer.c | 33 +++++++++++++++++++++++++++++++++\n>  1 file changed, 33 insertions(+)\n> \n> diff --git a/sequencer.c b/sequencer.c\n> index 89fd625..e8c6daf 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -1218,6 +1218,7 @@ struct todo_list {\n>  \tstruct strbuf buf;\n>  \tstruct todo_item *items;\n>  \tint nr, alloc, current;\n> +\tint done_nr, total_nr;\n>  };\n>  \n>  #define TODO_LIST_INIT { STRBUF_INIT }\n> @@ -1329,6 +1330,17 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n>  \treturn res;\n>  }\n>  \n> +static int count_commands(struct todo_list *todo_list)\n> +{\n> +\tint count = 0, i;\n> +\n> +\tfor (i = 0; i < todo_list->nr; i++)\n> +\t\tif (todo_list->items[i].command != TODO_COMMENT)\n> +\t\t\tcount++;\n> +\n> +\treturn count;\n> +}\n> +\n>  static int read_populate_todo(struct todo_list *todo_list,\n>  \t\t\tstruct replay_opts *opts)\n>  {\n> @@ -1355,6 +1367,22 @@ static int read_populate_todo(struct todo_list *todo_list,\n>  \tif (!todo_list->nr &&\n>  \t    (!is_rebase_i(opts) || !file_exists(rebase_path_done())))\n>  \t\treturn error(_(\"No commits parsed.\"));\n> +\n> +\tif (is_rebase_i(opts)) {\n> +\t\tstruct todo_list done = TODO_LIST_INIT;\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\ttodo_list->done_nr = count_commands(&done);\n> +\t\telse\n> +\t\t\ttodo_list->done_nr = 0;\n> +\n> +\t\ttodo_list->total_nr = todo_list->done_nr\n> +\t\t\t+ count_commands(todo_list);\n> +\n> +\t\ttodo_list_release(&done);\n> +\t}\n> +\n>  \treturn 0;\n>  }\n>  \n> @@ -1900,6 +1928,11 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n>  \t\tif (save_todo(todo_list, opts))\n>  \t\t\treturn -1;\n>  \t\tif (is_rebase_i(opts)) {\n> +\t\t\tif (item->command != TODO_COMMENT)\n> +\t\t\t\tfprintf(stderr, \"Rebasing (%d/%d)%s\",\n> +\t\t\t\t\t++(todo_list->done_nr),\n> +\t\t\t\t\ttodo_list->total_nr,\n> +\t\t\t\t\topts->verbose ? \"\\n\" : \"\\r\");\n>  \t\t\tunlink(rebase_path_message());\n>  \t\t\tunlink(rebase_path_author_script());\n>  \t\t\tunlink(rebase_path_stopped_sha());\n\n(picking a random commit that shows this 'symptom')\n\nYou're sprinking a lot of is_rebase_i's around sequencer.c to make sure\nthere are no changes in behaviour. I wonder if the right balance has\nbeen struck between 'no changes in behaviour' and 'common behaviour'.\nFor instance, in this case, maybe it would be a better idea for non-\nrebase uses of the sequencer to also show progress.\n\nD.\n"},{"id":"300785","messageId":"1472718815.4680.21.camel@kaarsemaker.net","threadId":"43966","inReplyTo":"736bcb8e860c876e32e8f89f68b0b901abedc187.1472633606.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 27/34] sequencer (rebase -i): differentiate between comments and 'noop'","fromName":"Dennis Kaarsemaker","fromEmail":"dennis@kaarsemaker.net","sentAt":"2016-09-01T08:33:35Z","receivedAt":"2016-09-01T08:33:54Z","isPatch":true,"sender":{"key":"dennis@kaarsemaker.net","avatar":"https://avatars.githubusercontent.com/u/200649?v=4"},"body":"On wo, 2016-08-31 at 10:56 +0200, Johannes Schindelin wrote:\n> diff --git a/sequencer.c b/sequencer.c\n> index 51c2f76..4c902e5 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -763,7 +763,8 @@ enum todo_command {\n>         TODO_SQUASH,\n>         TODO_EXEC,\n>         TODO_NOOP,\n> -       TODO_DROP\n> +       TODO_DROP,\n> +       TODO_COMMENT\n>  };\n\n(picking a random commit that touches this enum)\n\nIn a few places you now make comparisons like \"< TODO_NOOP\", so I think\nit would be good to have a comment near the definition of this enum\nthat says that ordering matters and why, so people don't attempt to add\na new TODO_FOOBAR at the end.\n\nD. \n"},{"id":"300787","messageId":"alpine.DEB.2.20.1609011046420.129229@virtualbox","threadId":"43966","inReplyTo":"1472668771.4265.74.camel@kaarsemaker.net","subject":"Re: [PATCH 06/34] sequencer (rebase -i): write the 'done' file","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-09-01T08:51:40Z","receivedAt":"2016-09-01T08:53:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Dennis,\n\nOn Wed, 31 Aug 2016, Dennis Kaarsemaker wrote:\n\n> On wo, 2016-08-31 at 10:54 +0200, Johannes Schindelin wrote:\n> > In the interactive rebase, commands that were successfully processed are\n> > not simply discarded, but appended to the 'done' file instead. This is\n> > used e.g. to display the current state to the user in the output of\n> > `git status` or the progress.\n> \n> Wouldn't it make more sense to have this patch before the ones that\n> implement the actual rebase commands?\n\nI waffled about the order so many times that I don't know anymore. The\nthing is, while the sequencer is taught incrementally to understand all of\nthe rebase -i functionality, rebase -i itself is not touched, on purpose.\n\nIn the case of the \"done\" file, my thoughts were: the commands do not need\nthis file *at all*. In fact, if we did not write the \"done\" file at all,\nthe only two types of test failures in the test suite would be 1) git\nstatus' output and 2) the prompt testing for the progress.\n\nSo you see, functionally, the \"done\" file is only relevant to the progress\npart of the patch series.\n\nAs such, I'd rather keep this patch in the current place, just before\nintroducing the progress.\n\n> Hmm, and after reading more of this series, I think the same applies to\n> some other patches too, e.g. 08/34 and 14/34, so I'm probably missing\n> something. So before I make a fool of myself and suggest that the\n> implementation of the actual commands should come at the end, maybe you\n> could tell me what I'm missing :) \n\nNo, no, don't hesitate to suggest reorderings. I am really thankful for\nthe discussion we are having, so that the outcome is better than what I\nhave right now. If the outcome would be the very same patches, but with\nmore confidence, it would still be better than what I have right now ;-)\n\nCiao,\nDscho"},{"id":"300793","messageId":"alpine.DEB.2.20.1609011500340.129229@virtualbox","threadId":"43966","inReplyTo":"1472668778.4265.76.camel@kaarsemaker.net","subject":"Re: [PATCH 15/34] sequencer (rebase -i): leave a patch upon error","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-09-01T13:03:39Z","receivedAt":"2016-09-01T13:04:01Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Dennis,\n\nOn Wed, 31 Aug 2016, Dennis Kaarsemaker wrote:\n\n> On wo, 2016-08-31 at 10:55 +0200, Johannes Schindelin wrote:\n> > Just like the interactive rebase, we want to leave a 'patch' file for\n> > further inspection by the user (even if we never tried to actually apply\n> > that patch, since we're cherry-picking instead).\n> > \n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> \n> This commit message confuses me. Did you mean s/Just like the/When\n> doing an/? \n\nWhat I meant is this: when calling `git rebase -i` right now, i.e. before\nany of my rebase--helper work, a failing rebase will leave a `patch` file\nin the `.git/rebase-merge/` directory. Since the sequencer is in the\nprocess of learning how to do an interactive rebase, it needs to learn the\nsame trick.\n\nBut I guess that your suggested edit makes things much clearer.\n\nThanks,\nDscho"},{"id":"300794","messageId":"alpine.DEB.2.20.1609011504550.129229@virtualbox","threadId":"43966","inReplyTo":"1472668773.4265.75.camel@kaarsemaker.net","subject":"Re: [PATCH 00/34] Teach the sequencer to act as rebase -i's backend","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-09-01T13:05:04Z","receivedAt":"2016-09-01T13:05:20Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Dennis,\n\nOn Wed, 31 Aug 2016, Dennis Kaarsemaker wrote:\n\n> On wo, 2016-08-31 at 10:53 +0200, Johannes Schindelin wrote:\n> > This marks the count down to '3': two more patch series after this\n> > (really tiny ones) and we have a faster rebase -i.\n> \n> I got to 16/34 (and skipped 07/34), will continue tomorrow. I hope the\n> comments are useful.\n\nMuch appreciated!\nDscho\n"},{"id":"300806","messageId":"alpine.DEB.2.20.1609011656340.129229@virtualbox","threadId":"43966","inReplyTo":"1472714431.4680.1.camel@kaarsemaker.net","subject":"Re: [PATCH 20/34] sequencer (rebase -i): copy commit notes at end","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-09-01T14:57:46Z","receivedAt":"2016-09-01T14:58:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Dennis,\n\nOn Thu, 1 Sep 2016, Dennis Kaarsemaker wrote:\n\n> On wo, 2016-08-31 at 10:55 +0200, Johannes Schindelin wrote:\n> > +               if (!stat(rebase_path_rewritten_list(), &st) &&\n> > +                               st.st_size > 0) {\n> > +                       struct child_process child = CHILD_PROCESS_INIT;\n> > +\n> > +                       child.in = open(rebase_path_rewritten_list(), O_RDONLY);\n> > +                       child.git_cmd = 1;\n> > +                       argv_array_push(&child.args, \"notes\");\n> > +                       argv_array_push(&child.args, \"copy\");\n> > +                       argv_array_push(&child.args, \"--for-rewrite=rebase\");\n> > +                       /* we don't care if this copying failed */\n> > +                       run_command(&child);\n> > +               }\n> \n> I know this is a strict port of git-rebase--interactive.sh, but\n> shouldn't we at least warn the user that the copy failed?\n\nAt this point, I want to have as faithful a conversion as possible (except\nthat I do not care for the speed of git-rebase--interactive.sh, of\ncourse).\n\nBesides, I am fairly certain that a failure will result in an error\nmessage. We just do not act on the exit value.\n\nCiao,\nDscho"},{"id":"300807","messageId":"alpine.DEB.2.20.1609011658300.129229@virtualbox","threadId":"43966","inReplyTo":"1472718808.4680.19.camel@kaarsemaker.net","subject":"Re: [PATCH 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-09-01T15:17:05Z","receivedAt":"2016-09-01T15:17:22Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Dennis,\n\nOn Thu, 1 Sep 2016, Dennis Kaarsemaker wrote:\n\n> On wo, 2016-08-31 at 10:54 +0200, Johannes Schindelin wrote:\n> \n> > +static int is_fixup(enum todo_command command)\n> > +{\n> > +\treturn command == TODO_FIXUP || command == TODO_SQUASH;\n> > +}\n> \n> It sounds wrong to have a function named is_fixup return true when the\n> command isn't a fixup but a squash. Maybe name it\n> changes_previous_commit or something?\n\nI can see how that may sound confusing, unless you understand that a\nsquash is a fixup that lets the user edit the commit message, too. So\nessentially squash = fixup + edit, if you will.\n\nMaybe the name is more appropriate in that light?\n\n> > +static const char *nth_for_number(int n)\n> > +{\n> > +\tint n1 = n % 10, n10 = n % 100;\n> > +\n> > +\tif (n1 == 1 && n10 != 11)\n> > +\t\treturn \"st\";\n> > +\tif (n1 == 2 && n10 != 12)\n> > +\t\treturn \"nd\";\n> > +\tif (n1 == 3 && n10 != 13)\n> > +\t\treturn \"rd\";\n> > +\treturn \"th\";\n> > +}\n> \n> >8---\n> \n> > +\tif (command == TODO_SQUASH) {\n> > +\t\tunlink(rebase_path_fixup_msg());\n> > +\t\tstrbuf_addf(&buf, \"\\n%c This is the %d%s commit message:\\n\\n%s\",\n> > +\t\t\tcomment_line_char,\n> > +\t\t\tcount, nth_for_number(count), body);\n> > +\t}\n> > +\telse if (command == TODO_FIXUP) {\n> > +\t\tstrbuf_addf(&buf,\n> > +\t\t\t\"\\n%c The %d%s commit message will be skipped:\\n\\n\",\n> > +\t\t\tcomment_line_char, count, nth_for_number(count));\n> > +\t\tstrbuf_add_commented_lines(&buf, body, strlen(body));\n> > +\t}\n> \n> This way of handling numbers is not translatable, and I really think we\n> should mark these strings for translation, like they are in the .sh\n> version.\n\nAh, this is the risk of working on something as big as rebase--helper.\nBack when I started with it, the relevant code in git-rebase--interactive\nread like this:\n\n\tnth_string () {\n\t\tcase \"$1\" in\n\t\t*1[0-9]|*[04-9]) echo \"$1\"th;;\n\t\t*1) echo \"$1\"st;;\n\t\t*2) echo \"$1\"nd;;\n\t\t*3) echo \"$1\"rd;;\n\t\tesac\n\t}\n\nI merely did a faithful translation of that...\n\nNow, I see that git-rebase--interactive was switched to use eval_gettext,\nwhich in turn is handled in git-sh-i18n whose code is quite convoluted. In\nthe absence of gettext, it uses git-sh-i18n--envsubst, which has no C API\nwhatsoever.\n\nAnd I see that the beautiful ordinal computation was given up in favor of\na lousy \"#1\", \"#2\", \"#3\", etc (it used to be \"1st\", \"2nd\", \"3rd\" etc).\n\nIn any case, translation is not my main concern until v2.10.0, so I'll\ntake care of this after that release.\n\nCiao,\nDscho\n"},{"id":"300808","messageId":"alpine.DEB.2.20.1609011717370.129229@virtualbox","threadId":"43966","inReplyTo":"1472718812.4680.20.camel@kaarsemaker.net","subject":"Re: [PATCH 32/34] sequencer (rebase -i): show the progress","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-09-01T15:19:22Z","receivedAt":"2016-09-01T15:19:47Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Dennis,\n\nOn Thu, 1 Sep 2016, Dennis Kaarsemaker wrote:\n\n> On wo, 2016-08-31 at 10:56 +0200, Johannes Schindelin wrote:\n> > The interactive rebase keeps the user informed about its progress.\n> > If the sequencer wants to do the grunt work of the interactive\n> > rebase, it also needs to show that progress.\n> > \n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> >  sequencer.c | 33 +++++++++++++++++++++++++++++++++\n> >  1 file changed, 33 insertions(+)\n> > \n> > diff --git a/sequencer.c b/sequencer.c\n> > index 89fd625..e8c6daf 100644\n> > --- a/sequencer.c\n> > +++ b/sequencer.c\n> > @@ -1218,6 +1218,7 @@ struct todo_list {\n> >  \tstruct strbuf buf;\n> >  \tstruct todo_item *items;\n> >  \tint nr, alloc, current;\n> > +\tint done_nr, total_nr;\n> >  };\n> >  \n> >  #define TODO_LIST_INIT { STRBUF_INIT }\n> > @@ -1329,6 +1330,17 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n> >  \treturn res;\n> >  }\n> >  \n> > +static int count_commands(struct todo_list *todo_list)\n> > +{\n> > +\tint count = 0, i;\n> > +\n> > +\tfor (i = 0; i < todo_list->nr; i++)\n> > +\t\tif (todo_list->items[i].command != TODO_COMMENT)\n> > +\t\t\tcount++;\n> > +\n> > +\treturn count;\n> > +}\n> > +\n> >  static int read_populate_todo(struct todo_list *todo_list,\n> >  \t\t\tstruct replay_opts *opts)\n> >  {\n> > @@ -1355,6 +1367,22 @@ static int read_populate_todo(struct todo_list *todo_list,\n> >  \tif (!todo_list->nr &&\n> >  \t    (!is_rebase_i(opts) || !file_exists(rebase_path_done())))\n> >  \t\treturn error(_(\"No commits parsed.\"));\n> > +\n> > +\tif (is_rebase_i(opts)) {\n> > +\t\tstruct todo_list done = TODO_LIST_INIT;\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\ttodo_list->done_nr = count_commands(&done);\n> > +\t\telse\n> > +\t\t\ttodo_list->done_nr = 0;\n> > +\n> > +\t\ttodo_list->total_nr = todo_list->done_nr\n> > +\t\t\t+ count_commands(todo_list);\n> > +\n> > +\t\ttodo_list_release(&done);\n> > +\t}\n> > +\n> >  \treturn 0;\n> >  }\n> >  \n> > @@ -1900,6 +1928,11 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n> >  \t\tif (save_todo(todo_list, opts))\n> >  \t\t\treturn -1;\n> >  \t\tif (is_rebase_i(opts)) {\n> > +\t\t\tif (item->command != TODO_COMMENT)\n> > +\t\t\t\tfprintf(stderr, \"Rebasing (%d/%d)%s\",\n> > +\t\t\t\t\t++(todo_list->done_nr),\n> > +\t\t\t\t\ttodo_list->total_nr,\n> > +\t\t\t\t\topts->verbose ? \"\\n\" : \"\\r\");\n> >  \t\t\tunlink(rebase_path_message());\n> >  \t\t\tunlink(rebase_path_author_script());\n> >  \t\t\tunlink(rebase_path_stopped_sha());\n> \n> (picking a random commit that shows this 'symptom')\n> \n> You're sprinking a lot of is_rebase_i's around sequencer.c to make sure\n> there are no changes in behaviour. I wonder if the right balance has\n> been struck between 'no changes in behaviour' and 'common behaviour'.\n> For instance, in this case, maybe it would be a better idea for non-\n> rebase uses of the sequencer to also show progress.\n\nActually, adding progress would make for a fine add-on patch series. I\nstill would like to have as faithful a conversion as possible, and\neverything else can come after that.\n\nThis strategy has a couple of advantages:\n\n- we can concentrate on correctness for now,\n\n- I get something to show for my time with Git for Windows v2.10.0, and\n\n- the add-on patches do not have to be done by *me* ;-)\n\nCiao,\nDscho"},{"id":"300810","messageId":"alpine.DEB.2.20.1609011720160.129229@virtualbox","threadId":"43966","inReplyTo":"1472718815.4680.21.camel@kaarsemaker.net","subject":"Re: [PATCH 27/34] sequencer (rebase -i): differentiate between comments and 'noop'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-09-01T15:32:01Z","receivedAt":"2016-09-01T15:32:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Dennis,\n\nOn Thu, 1 Sep 2016, Dennis Kaarsemaker wrote:\n\n> On wo, 2016-08-31 at 10:56 +0200, Johannes Schindelin wrote:\n> > diff --git a/sequencer.c b/sequencer.c\n> > index 51c2f76..4c902e5 100644\n> > --- a/sequencer.c\n> > +++ b/sequencer.c\n> > @@ -763,7 +763,8 @@ enum todo_command {\n> >         TODO_SQUASH,\n> >         TODO_EXEC,\n> >         TODO_NOOP,\n> > -       TODO_DROP\n> > +       TODO_DROP,\n> > +       TODO_COMMENT\n> >  };\n> \n> (picking a random commit that touches this enum)\n> \n> In a few places you now make comparisons like \"< TODO_NOOP\", so I think\n> it would be good to have a comment near the definition of this enum\n> that says that ordering matters and why, so people don't attempt to add\n> a new TODO_FOOBAR at the end.\n\nTrue.\n\nIt does not seem that we have a precedent for that. The closest is what I\nhad in an early iteration of the fsck message IDs, and subsequently things\nwere refactored so that it is not the order, but a flag, that determines\nwhat the command does.\n\nNot sure how to do this elegantly. Maybe like this?\n\n\tenum todo_command {\n\t\tTODO_PICK_COMMANDS = 0,\n\t\tTODO_PICK = TODO_PICK_COMMANDS,\n\t\tTODO_SQUASH,\n\n\t\tTODO_NON_PICK_COMMANDS,\n\t\tTODO_EXEC = TODO_NON_PICK_COMMANDS,\n\n\t\tTODO_NOOP_COMMANDS,\n\t\tTODO_NOOP = TODO_NOOP_COMMANDS,\n\t\tTODO_DROP\n\t\tTODO_DROP,\n\n\t\tTODO_LAST_COMMAND,\n\t\tTODO_COMMENT = TODO_LAST_COMMAND\n\t};\n\nBut that is so god-awful to read.\n\nStill unsure,\nDscho"},{"id":"300814","messageId":"1472746523.4680.30.camel@kaarsemaker.net","threadId":"43966","inReplyTo":"alpine.DEB.2.20.1609011720160.129229@virtualbox","subject":"Re: [PATCH 27/34] sequencer (rebase -i): differentiate between comments and 'noop'","fromName":"Dennis Kaarsemaker","fromEmail":"dennis@kaarsemaker.net","sentAt":"2016-09-01T16:15:23Z","receivedAt":"2016-09-01T16:15:32Z","isPatch":true,"sender":{"key":"dennis@kaarsemaker.net","avatar":"https://avatars.githubusercontent.com/u/200649?v=4"},"body":"On do, 2016-09-01 at 17:32 +0200, Johannes Schindelin wrote:\n> Hi Dennis,\n> \n> On Thu, 1 Sep 2016, Dennis Kaarsemaker wrote:\n> \n> > \n> > On wo, 2016-08-31 at 10:56 +0200, Johannes Schindelin wrote:\n> > > \n> > > diff --git a/sequencer.c b/sequencer.c\n> > > index 51c2f76..4c902e5 100644\n> > > --- a/sequencer.c\n> > > +++ b/sequencer.c\n> > > @@ -763,7 +763,8 @@ enum todo_command {\n> > >         TODO_SQUASH,\n> > >         TODO_EXEC,\n> > >         TODO_NOOP,\n> > > -       TODO_DROP\n> > > +       TODO_DROP,\n> > > +       TODO_COMMENT\n> > >  };\n> > (picking a random commit that touches this enum)\n> > \n> > In a few places you now make comparisons like \"< TODO_NOOP\", so I\n> > think\n> > it would be good to have a comment near the definition of this enum\n> > that says that ordering matters and why, so people don't attempt to\n> > add\n> > a new TODO_FOOBAR at the end.\n> True.\n> \n> It does not seem that we have a precedent for that. The closest is\n> what I\n> had in an early iteration of the fsck message IDs, and subsequently\n> things\n> were refactored so that it is not the order, but a flag, that\n> determines\n> what the command does.\n> \n> Not sure how to do this elegantly. Maybe like this?\n> \n> \tenum todo_command {\n> \t\tTODO_PICK_COMMANDS = 0,\n> \t\tTODO_PICK = TODO_PICK_COMMANDS,\n> \t\tTODO_SQUASH,\n> \n> \t\tTODO_NON_PICK_COMMANDS,\n> \t\tTODO_EXEC = TODO_NON_PICK_COMMANDS,\n> \n> \t\tTODO_NOOP_COMMANDS,\n> \t\tTODO_NOOP = TODO_NOOP_COMMANDS,\n> \t\tTODO_DROP\n> \t\tTODO_DROP,\n> \n> \t\tTODO_LAST_COMMAND,\n> \t\tTODO_COMMENT = TODO_LAST_COMMAND\n> \t};\n> \n> But that is so god-awful to read.\n\nAgreed, that sure is awful.\n\nHow about something like\n\n/*\n * Note that ordering matters in this enum. Not only must it match the\n * mapping below, it is also divided into several sections that matter.\n * When adding new commands, make sure you add it in the right section.\n */\nenum todo_command {\n\t/* All commands that handle commits */\n\tTODO_PICK,\n\t...\n\t/* All commands that do something else than pick */\n\tTODO_EXEC,\n\t...\n\t/* All commands that do nothing but are counted for reporting progress */\n\tTODO_NOOP,\n\t...\n\t/* Comments, which are not counted\n\tTODO_COMMENT\n}\n"},{"id":"300815","messageId":"1472746749.4680.33.camel@kaarsemaker.net","threadId":"43966","inReplyTo":"alpine.DEB.2.20.1609011658300.129229@virtualbox","subject":"Re: [PATCH 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Dennis Kaarsemaker","fromEmail":"dennis@kaarsemaker.net","sentAt":"2016-09-01T16:19:09Z","receivedAt":"2016-09-01T16:19:17Z","isPatch":true,"sender":{"key":"dennis@kaarsemaker.net","avatar":"https://avatars.githubusercontent.com/u/200649?v=4"},"body":"On do, 2016-09-01 at 17:17 +0200, Johannes Schindelin wrote:\n> Hi Dennis,\n> \n> On Thu, 1 Sep 2016, Dennis Kaarsemaker wrote:\n> \n> > \n> > On wo, 2016-08-31 at 10:54 +0200, Johannes Schindelin wrote:\n> > \n> > > \n> > > +static int is_fixup(enum todo_command command)\n> > > +{\n> > > +\treturn command == TODO_FIXUP || command == TODO_SQUASH;\n> > > +}\n> > It sounds wrong to have a function named is_fixup return true when\n> > the\n> > command isn't a fixup but a squash. Maybe name it\n> > changes_previous_commit or something?\n> I can see how that may sound confusing, unless you understand that a\n> squash is a fixup that lets the user edit the commit message, too. So\n> essentially squash = fixup + edit, if you will.\n> \n> Maybe the name is more appropriate in that light?\n\nKinda makes sense. It's not how I use fixup/squash as a user of rebase\n-i though. But we can't go there, that's bikeshed country :)\n\n> > > +static const char *nth_for_number(int n)\n> > > +{\n> > > +\tint n1 = n % 10, n10 = n % 100;\n> > > +\n> > > +\tif (n1 == 1 && n10 != 11)\n> > > +\t\treturn \"st\";\n> > > +\tif (n1 == 2 && n10 != 12)\n> > > +\t\treturn \"nd\";\n> > > +\tif (n1 == 3 && n10 != 13)\n> > > +\t\treturn \"rd\";\n> > > +\treturn \"th\";\n> > > +}\n> > > \n> > > 8---\n> > > \n> > > +\tif (command == TODO_SQUASH) {\n> > > +\t\tunlink(rebase_path_fixup_msg());\n> > > +\t\tstrbuf_addf(&buf, \"\\n%c This is the %d%s commit\n> > > message:\\n\\n%s\",\n> > > +\t\t\tcomment_line_char,\n> > > +\t\t\tcount, nth_for_number(count), body);\n> > > +\t}\n> > > +\telse if (command == TODO_FIXUP) {\n> > > +\t\tstrbuf_addf(&buf,\n> > > +\t\t\t\"\\n%c The %d%s commit message will be\n> > > skipped:\\n\\n\",\n> > > +\t\t\tcomment_line_char, count,\n> > > nth_for_number(count));\n> > > +\t\tstrbuf_add_commented_lines(&buf, body,\n> > > strlen(body));\n> > > +\t}\n> > This way of handling numbers is not translatable, and I really\n> > think we\n> > should mark these strings for translation, like they are in the .sh\n> > version.\n> Ah, this is the risk of working on something as big as rebase\n> --helper.\n> Back when I started with it, the relevant code in git-rebase\n> --interactive\n> read like this:\n> \n> \tnth_string () {\n> \t\tcase \"$1\" in\n> \t\t*1[0-9]|*[04-9]) echo \"$1\"th;;\n> \t\t*1) echo \"$1\"st;;\n> \t\t*2) echo \"$1\"nd;;\n> \t\t*3) echo \"$1\"rd;;\n> \t\tesac\n> \t}\n> \n> I merely did a faithful translation of that...\n> \n> Now, I see that git-rebase--interactive was switched to use\n> eval_gettext,\n> which in turn is handled in git-sh-i18n whose code is quite\n> convoluted. In\n> the absence of gettext, it uses git-sh-i18n--envsubst, which has no C\n> API\n> whatsoever.\n> \n> And I see that the beautiful ordinal computation was given up in\n> favor of\n> a lousy \"#1\", \"#2\", \"#3\", etc (it used to be \"1st\", \"2nd\", \"3rd\"\n> etc).\n> \n> In any case, translation is not my main concern until v2.10.0, so\n> I'll\n> take care of this after that release.\n\nHmm, not sure if I agree with that. I'd see it as a regression to lose\nthe i18n there.\n\nD.\n"},{"id":"300821","messageId":"xmqqr393wkof.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"1472718808.4680.19.camel@kaarsemaker.net","subject":"Re: [PATCH 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-09-01T18:15:44Z","receivedAt":"2016-09-01T20:56:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Dennis Kaarsemaker <dennis@kaarsemaker.net> writes:\n\n>> +static const char *nth_for_number(int n)\n>> +{\n>> +\tint n1 = n % 10, n10 = n % 100;\n>> +\n>> +\tif (n1 == 1 && n10 != 11)\n>> +\t\treturn \"st\";\n>> +\tif (n1 == 2 && n10 != 12)\n>> +\t\treturn \"nd\";\n>> +\tif (n1 == 3 && n10 != 13)\n>> +\t\treturn \"rd\";\n>> +\treturn \"th\";\n>> +}\n>\n>>8---\n>\n>> +\tif (command == TODO_SQUASH) {\n>> +\t\tunlink(rebase_path_fixup_msg());\n>> +\t\tstrbuf_addf(&buf, \"\\n%c This is the %d%s commit message:\\n\\n%s\",\n>> +\t\t\tcomment_line_char,\n>> +\t\t\tcount, nth_for_number(count), body);\n>> +\t}\n>> +\telse if (command == TODO_FIXUP) {\n>> +\t\tstrbuf_addf(&buf,\n>> +\t\t\t\"\\n%c The %d%s commit message will be skipped:\\n\\n\",\n>> +\t\t\tcomment_line_char, count, nth_for_number(count));\n>> +\t\tstrbuf_add_commented_lines(&buf, body, strlen(body));\n>> +\t}\n>\n> This way of handling numbers is not translatable, and I really think we\n> should mark these strings for translation, like they are in the .sh\n> version.\n\nCorrect.\n\nFor those who were not paying attention on the 'master' front during\nthis pre-release period [*1*], I have to point out that the scripted\nPorcelain has been updated to lose the Anglo-centric st/nd/rd/th and\nthis series would want to get updated to match.\n\n\n[Footnote]\n\n*1* Why weren't you?  Repent! ;-)\n"},{"id":"300865","messageId":"alpine.DEB.2.20.1609012009070.129229@virtualbox","threadId":"43966","inReplyTo":"1472746749.4680.33.camel@kaarsemaker.net","subject":"Re: [PATCH 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-09-02T07:13:50Z","receivedAt":"2016-09-02T07:14:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Dennis,\n\nOn Thu, 1 Sep 2016, Dennis Kaarsemaker wrote:\n\n> On do, 2016-09-01 at 17:17 +0200, Johannes Schindelin wrote:\n>\n> > And I see that the beautiful ordinal computation was given up in favor\n> > of a lousy \"#1\", \"#2\", \"#3\", etc (it used to be \"1st\", \"2nd\", \"3rd\"\n> > etc).\n> > \n> > In any case, translation is not my main concern until v2.10.0, so I'll\n> > take care of this after that release.\n> \n> Hmm, not sure if I agree with that. I'd see it as a regression to lose\n> the i18n there.\n\nAs Git for Windows does not ship with translations (for multiple reasons),\nit would not be a regression.\n\nHaving said that, I see that having a different text than the current\nrebase -i can be seen as a regression, so I changed that.\n\nThanks for the review!\nDscho\n"},{"id":"300866","messageId":"alpine.DEB.2.20.1609020914320.129229@virtualbox","threadId":"43966","inReplyTo":"xmqqr393wkof.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-09-02T07:15:46Z","receivedAt":"2016-09-02T07:16:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Thu, 1 Sep 2016, Junio C Hamano wrote:\n\n> For those who were not paying attention on the 'master' front during\n> this pre-release period [*1*], I have to point out that the scripted\n> Porcelain has been updated to lose the Anglo-centric st/nd/rd/th and\n> this series would want to get updated to match.\n> \n> \n> [Footnote]\n> \n> *1* Why weren't you?  Repent! ;-)\n\nI tried to. But, you know, I was kinda busy with a couple of patch series.\n\nIn any case, I changed the code this morning. Can't say that I like those\nforced last-minute changes.\n\nCiao,\nJohannes\n"},{"id":"300867","messageId":"alpine.DEB.2.20.1609020931450.129229@virtualbox","threadId":"43966","inReplyTo":"1472746523.4680.30.camel@kaarsemaker.net","subject":"Re: [PATCH 27/34] sequencer (rebase -i): differentiate between comments and 'noop'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-09-02T07:32:07Z","receivedAt":"2016-09-02T07:32:20Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Dennis,\n\nOn Thu, 1 Sep 2016, Dennis Kaarsemaker wrote:\n\n> /*\n>  * Note that ordering matters in this enum. Not only must it match the\n>  * mapping below, it is also divided into several sections that matter.\n>  * When adding new commands, make sure you add it in the right section.\n>  */\n> enum todo_command {\n> \t/* All commands that handle commits */\n> \tTODO_PICK,\n> \t...\n> \t/* All commands that do something else than pick */\n> \tTODO_EXEC,\n> \t...\n> \t/* All commands that do nothing but are counted for reporting progress */\n> \tTODO_NOOP,\n> \t...\n> \t/* Comments, which are not counted\n> \tTODO_COMMENT\n> }\n\nI like it! Changed accordingly.\n\nThanks!\nDscho"},{"id":"300881","messageId":"1472818007.4680.53.camel@kaarsemaker.net","threadId":"43966","inReplyTo":"alpine.DEB.2.20.1609012009070.129229@virtualbox","subject":"Re: [PATCH 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Dennis Kaarsemaker","fromEmail":"dennis@kaarsemaker.net","sentAt":"2016-09-02T12:06:47Z","receivedAt":"2016-09-02T12:07:08Z","isPatch":true,"sender":{"key":"dennis@kaarsemaker.net","avatar":"https://avatars.githubusercontent.com/u/200649?v=4"},"body":"On vr, 2016-09-02 at 09:13 +0200, Johannes Schindelin wrote:\n\n> As Git for Windows does not ship with translations (for multiple\n> reasons), it would not be a regression.\n\nI'm confused, how does \"git for windows does not ship with\ntranslations\" translate to \"this is not a regression\"? Is this patch\nseries only meant to be for git for windows and not go into git.git\nitself?\n\nD.\n"},{"id":"300885","messageId":"alpine.DEB.2.20.1609021557060.129229@virtualbox","threadId":"43966","inReplyTo":"1472818007.4680.53.camel@kaarsemaker.net","subject":"Re: [PATCH 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-09-02T14:22:21Z","receivedAt":"2016-09-02T14:22:41Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Dennis,\n\nOn Fri, 2 Sep 2016, Dennis Kaarsemaker wrote:\n\n> On vr, 2016-09-02 at 09:13 +0200, Johannes Schindelin wrote:\n> \n> > As Git for Windows does not ship with translations (for multiple\n> > reasons), it would not be a regression.\n> \n> I'm confused, how does \"git for windows does not ship with\n> translations\" translate to \"this is not a regression\"? Is this patch\n> series only meant to be for git for windows and not go into git.git\n> itself?\n\nOh, I thought I had clarified my plan... The timeline is:\n\n- I submit the remaining rebase--helper patch series for review (last week\n  and this one),\n\n- I publish a preview of Git for Windows v2.10.0 that already uses these\n  patches (done: https://github.com/git-for-windows/git/releases/tag/v2.9.3.windows.3)\n\n- once upstream Git v2.10.0 is released (possibly today, after my work\n  hours), I perform a final \"Git garden shears\" run (read: rebase Git for\n  Windows' patches, retaining the branch structure) on top of v2.10.0 and\n  release Git for Windows v2.10.0, tagged as v2.10.0.windows.1 in\n  https://github.com/git-for-windows/git (due to time zone differences\n  relative to Junio, the most likely time for this release would be\n  some time around noon tomorrow, given that the release engineering takes\n  roughly 2-4 hours, running unsupervised for the most part).\n\n- as far as Git for Windows is concerned, l10n is not really an issue yet:\n  the installer is released without any localizations.\n\n- After releasing Git for Windows v2.10.0, I will pay a lot of attention\n  to feedback. Not only to hear a lot of praise, but also to catch any\n  possible regressions. Not that I expect anything dramatic to happen\n  because I really tested this as thoroughly as I can: not a single one of\n  my interactive rebases since mid May has been performed without\n  involving the rebase--helper. In the three cases where I *did* find a\n  regression, I solved it immediately, of course.\n\n- After releasing Git for Windows v2.10.0, I will have a nice beer. Or\n  three.\n\n- Then I will leisurely try to address the l10n issues.\n\n- Then, I will send out the current iterations of the patch series that\n  are in flight.\n\n- I have the entire week to address concerns with Git for Windows as well\n  as with the patch series (where the former takes precedence, of course).\n\n- The second half of September, I will relax from this marathon that\n  started in early February. Meaning: I will be mostly offline.\n\nI hope this clarifies why I am not so concerned about some issues such as\ntranslation, or commit messages, or grammar, and more so about others,\nsuch as incorrect code.\n\nCiao,\nDscho\n"},{"id":"300899","messageId":"1472835875.4680.58.camel@kaarsemaker.net","threadId":"43966","inReplyTo":"alpine.DEB.2.20.1609021557060.129229@virtualbox","subject":"Re: [PATCH 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Dennis Kaarsemaker","fromEmail":"dennis@kaarsemaker.net","sentAt":"2016-09-02T17:04:35Z","receivedAt":"2016-09-02T17:04:43Z","isPatch":true,"sender":{"key":"dennis@kaarsemaker.net","avatar":"https://avatars.githubusercontent.com/u/200649?v=4"},"body":"On vr, 2016-09-02 at 16:22 +0200, Johannes Schindelin wrote:\n> I hope this clarifies why I am not so concerned about some issues\n> such as translation, or commit messages, or grammar, and more so\n> about others, such as incorrect code.\n\nIt does, thanks!\n\nD.\n"},{"id":"300915","messageId":"20160902211332.GA28305@ikke.info","threadId":"43966","inReplyTo":"5ffec2e588a4edc4902e1ab3a2ec3a73a7c3625b.1472633606.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH 01/34] sequencer: support a new action: 'interactive rebase'","fromName":"Kevin Daudt","fromEmail":"me@ikke.info","sentAt":"2016-09-02T21:13:32Z","receivedAt":"2016-09-02T21:13:38Z","isPatch":true,"sender":{"key":"me@ikke.info","avatar":"https://avatars.githubusercontent.com/u/135698?v=4"},"body":"On Wed, Aug 31, 2016 at 10:54:02AM +0200, Johannes Schindelin wrote:\n> @@ -43,16 +51,20 @@ static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, \"rebase-merge/gpg_sign_opt\")\n>  /* We will introduce the 'interactive rebase' mode later */\n>  static inline int is_rebase_i(const struct replay_opts *opts)\n>  {\n> -\treturn 0;\n> +\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n>  }\n>  \n>  static const char *get_dir(const struct replay_opts *opts)\n>  {\n> +\tif (is_rebase_i(opts))\n> +\t\treturn rebase_path();\n>  \treturn git_path_seq_dir();\n>  }\n>  \n>  static const char *get_todo_path(const struct replay_opts *opts)\n>  {\n> +\tif (is_rebase_i(opts))\n> +\t\treturn rebase_path_todo();\n>  \treturn git_path_todo_file();\n>  }\n\nThis patch fails to apply for me because function is_rebase_i has never\nbeen introduced before (no record of it anywhere). Currently, only\nIS_REBASE_I macro is present.\n"},{"id":"300942","messageId":"alpine.DEB.2.20.1609030845490.129229@virtualbox","threadId":"43966","inReplyTo":"20160902211332.GA28305@ikke.info","subject":"Re: [PATCH 01/34] sequencer: support a new action: 'interactive rebase'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-09-03T06:57:29Z","receivedAt":"2016-09-03T06:57:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Kevin,\n\nOn Fri, 2 Sep 2016, Kevin Daudt wrote:\n\n> On Wed, Aug 31, 2016 at 10:54:02AM +0200, Johannes Schindelin wrote:\n> > @@ -43,16 +51,20 @@ static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, \"rebase-merge/gpg_sign_opt\")\n> >  /* We will introduce the 'interactive rebase' mode later */\n> >  static inline int is_rebase_i(const struct replay_opts *opts)\n> >  {\n> > -\treturn 0;\n> > +\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n> >  }\n> >  \n> >  static const char *get_dir(const struct replay_opts *opts)\n> >  {\n> > +\tif (is_rebase_i(opts))\n> > +\t\treturn rebase_path();\n> >  \treturn git_path_seq_dir();\n> >  }\n> >  \n> >  static const char *get_todo_path(const struct replay_opts *opts)\n> >  {\n> > +\tif (is_rebase_i(opts))\n> > +\t\treturn rebase_path_todo();\n> >  \treturn git_path_todo_file();\n> >  }\n> \n> This patch fails to apply for me because function is_rebase_i has never\n> been introduced before (no record of it anywhere). Currently, only\n> IS_REBASE_I macro is present.\n\nI did not send out a new iteration of the prepare-sequencer patch series\n(mostly because I wanted reviewers to look at the later patch series,\nrather than re-review a 2nd iteration of something they already saw).\n\nBut I did address the concerns mentioned in the review already, of course,\nbecause part of the reason to show those patch series was to get valuable\nfeedback before including the work in Git for Windows v2.10.0.\n\nYou can find the current iteration of the prepare-sequencer here:\nhttps://github.com/dscho/git/compare/libify-sequencer...prepare-sequencer\n\nPlease note that I will most likely try to address some l10n concerns\nbefore sending out the next iteration of the patch series.\n\nThe patch introducing is_rebase_i() (and no longer IS_REBASE_I()) is:\nhttps://github.com/dscho/git/commit/76d272020bb72618957308f06083c807efe59aca\n\nIf you want to have the latest iteration of the entire patch thicket, just\n`git fetch https://github.com/dscho/git interactive-rebase`. I update that\nmore frequently than I send out updates via mail.\n\nCiao,\nJohannes\n"},{"id":"307598","messageId":"54d4e8d3673662d1ec806f3f4a779a17effbdaf2.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 04/34] sequencer (rebase -i): implement the 'exec' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:29:49Z","receivedAt":"2016-12-13T15:31:57Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The 'exec' command is a little special among rebase -i's commands, as it\ndoes *not* have a SHA-1 as first parameter. Instead, everything after the\n`exec` command is treated as command-line to execute.\n\nLet's reuse the arg/arg_len fields of the todo_item structure (which hold\nthe oneline for pick/edit commands) to point to the command-line.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 59 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 68e2c84803..700b7575ed 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -17,6 +17,7 @@\n #include \"argv-array.h\"\n #include \"quote.h\"\n #include \"log-tree.h\"\n+#include \"wt-status.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n@@ -664,6 +665,8 @@ enum todo_command {\n \tTODO_PICK = 0,\n \tTODO_REVERT,\n \tTODO_EDIT,\n+\t/* commands that do something else than handling a single commit */\n+\tTODO_EXEC,\n \t/* commands that do nothing but are counted for reporting progress */\n \tTODO_NOOP\n };\n@@ -672,6 +675,7 @@ static const char *todo_command_strings[] = {\n \t\"pick\",\n \t\"revert\",\n \t\"edit\",\n+\t\"exec\",\n \t\"noop\"\n };\n \n@@ -971,6 +975,12 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \t\treturn -1;\n \tbol += padding;\n \n+\tif (item->command == TODO_EXEC) {\n+\t\titem->arg = bol;\n+\t\titem->arg_len = (int)(eol - bol);\n+\t\treturn 0;\n+\t}\n+\n \tend_of_object_name = (char *) bol + strcspn(bol, \" \\t\\n\");\n \tsaved = *end_of_object_name;\n \t*end_of_object_name = '\\0';\n@@ -1396,6 +1406,47 @@ static int error_with_patch(struct commit *commit,\n \treturn exit_code;\n }\n \n+static int do_exec(const char *command_line)\n+{\n+\tconst char *child_argv[] = { NULL, NULL };\n+\tint dirty, status;\n+\n+\tfprintf(stderr, \"Executing: %s\\n\", command_line);\n+\tchild_argv[0] = command_line;\n+\tstatus = run_command_v_opt(child_argv, RUN_USING_SHELL);\n+\n+\t/* force re-reading of the cache */\n+\tif (discard_cache() < 0 || read_cache() < 0)\n+\t\treturn error(_(\"could not read index\"));\n+\n+\tdirty = require_clean_work_tree(\"rebase\", NULL, 1, 1);\n+\n+\tif (status) {\n+\t\twarning(_(\"execution failed: %s\\n%s\"\n+\t\t\t  \"You can fix the problem, and then run\\n\"\n+\t\t\t  \"\\n\"\n+\t\t\t  \"  git rebase --continue\\n\"\n+\t\t\t  \"\\n\"),\n+\t\t\tcommand_line,\n+\t\t\tdirty ? N_(\"and made changes to the index and/or the \"\n+\t\t\t\t\"working tree\\n\") : \"\");\n+\t\tif (status == 127)\n+\t\t\t/* command not found */\n+\t\t\tstatus = 1;\n+\t}\n+\telse if (dirty) {\n+\t\twarning(_(\"execution succeeded: %s\\nbut \"\n+\t\t\t  \"left changes to the index and/or the working tree\\n\"\n+\t\t\t  \"Commit or stash your changes, and then run\\n\"\n+\t\t\t  \"\\n\"\n+\t\t\t  \"  git rebase --continue\\n\"\n+\t\t\t  \"\\n\"), command_line);\n+\t\tstatus = 1;\n+\t}\n+\n+\treturn status;\n+}\n+\n static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n {\n \tint res = 0;\n@@ -1425,6 +1476,14 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\t\t!res);\n \t\t\t}\n \t\t}\n+\t\telse if (item->command == TODO_EXEC) {\n+\t\t\tchar *end_of_arg = (char *)(item->arg + item->arg_len);\n+\t\t\tint saved = *end_of_arg;\n+\n+\t\t\t*end_of_arg = '\\0';\n+\t\t\tres = do_exec(item->arg);\n+\t\t\t*end_of_arg = saved;\n+\t\t}\n \t\telse if (item->command != TODO_NOOP)\n \t\t\treturn error(_(\"unknown command %d\"), item->command);\n \n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307599","messageId":"a1361151ad1dad8f4dc3c412c7ed30f625d67ba0.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 03/34] sequencer (rebase -i): implement the 'edit' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:29:40Z","receivedAt":"2016-12-13T15:32:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This patch is a straight-forward reimplementation of the `edit`\noperation of the interactive rebase command.\n\nWell, not *quite* straight-forward: when stopping, the `edit`\ncommand wants to write the `patch` file (which is not only the\npatch, but includes the commit message and author information). To\nthat end, this patch requires the earlier work that taught the\nlog-tree machinery to respect the `file` setting of\nrev_info->diffopt to write to a file stream different than stdout.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--\n 1 file changed, 114 insertions(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 1224799286..68e2c84803 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -16,6 +16,7 @@\n #include \"refs.h\"\n #include \"argv-array.h\"\n #include \"quote.h\"\n+#include \"log-tree.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n@@ -43,6 +44,20 @@ static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n  */\n static GIT_PATH_FUNC(rebase_path_author_script, \"rebase-merge/author-script\")\n /*\n+ * When an \"edit\" rebase command is being processed, the SHA1 of the\n+ * commit to be edited is recorded in this file.  When \"git rebase\n+ * --continue\" is executed, if there are any staged changes then they\n+ * will be amended to the HEAD commit, but only provided the HEAD\n+ * commit is still the commit to be edited.  When any other rebase\n+ * command is processed, this file is deleted.\n+ */\n+static GIT_PATH_FUNC(rebase_path_amend, \"rebase-merge/amend\")\n+/*\n+ * When we stop at a given patch via the \"edit\" command, this file contains\n+ * the long commit name of the corresponding patch.\n+ */\n+static GIT_PATH_FUNC(rebase_path_stopped_sha, \"rebase-merge/stopped-sha\")\n+/*\n  * The following files are written by git-rebase just after parsing the\n  * command-line (and are only consumed, not modified, by the sequencer).\n  */\n@@ -648,6 +663,7 @@ enum todo_command {\n \t/* commands that handle commits */\n \tTODO_PICK = 0,\n \tTODO_REVERT,\n+\tTODO_EDIT,\n \t/* commands that do nothing but are counted for reporting progress */\n \tTODO_NOOP\n };\n@@ -655,6 +671,7 @@ enum todo_command {\n static const char *todo_command_strings[] = {\n \t\"pick\",\n \t\"revert\",\n+\t\"edit\",\n \t\"noop\"\n };\n \n@@ -1301,9 +1318,87 @@ static int save_opts(struct replay_opts *opts)\n \treturn res;\n }\n \n+static int make_patch(struct commit *commit, struct replay_opts *opts)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct rev_info log_tree_opt;\n+\tconst char *commit_buffer = get_commit_buffer(commit, NULL), *subject, *p;\n+\tint res = 0;\n+\n+\tp = short_commit_name(commit);\n+\tif (write_message(p, strlen(p), rebase_path_stopped_sha(), 1) < 0)\n+\t\treturn -1;\n+\n+\tstrbuf_addf(&buf, \"%s/patch\", get_dir(opts));\n+\tmemset(&log_tree_opt, 0, sizeof(log_tree_opt));\n+\tinit_revisions(&log_tree_opt, NULL);\n+\tlog_tree_opt.abbrev = 0;\n+\tlog_tree_opt.diff = 1;\n+\tlog_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;\n+\tlog_tree_opt.disable_stdin = 1;\n+\tlog_tree_opt.no_commit_id = 1;\n+\tlog_tree_opt.diffopt.file = fopen(buf.buf, \"w\");\n+\tlog_tree_opt.diffopt.use_color = GIT_COLOR_NEVER;\n+\tif (!log_tree_opt.diffopt.file)\n+\t\tres |= error_errno(_(\"could not open '%s'\"), buf.buf);\n+\telse {\n+\t\tres |= log_tree_commit(&log_tree_opt, commit);\n+\t\tfclose(log_tree_opt.diffopt.file);\n+\t}\n+\tstrbuf_reset(&buf);\n+\n+\tstrbuf_addf(&buf, \"%s/message\", get_dir(opts));\n+\tif (!file_exists(buf.buf)) {\n+\t\tfind_commit_subject(commit_buffer, &subject);\n+\t\tres |= write_message(subject, strlen(subject), buf.buf, 1);\n+\t\tunuse_commit_buffer(commit, commit_buffer);\n+\t}\n+\tstrbuf_release(&buf);\n+\n+\treturn res;\n+}\n+\n+static int intend_to_amend(void)\n+{\n+\tunsigned char head[20];\n+\tchar *p;\n+\n+\tif (get_sha1(\"HEAD\", head))\n+\t\treturn error(_(\"cannot read HEAD\"));\n+\n+\tp = sha1_to_hex(head);\n+\treturn write_message(p, strlen(p), rebase_path_amend(), 1);\n+}\n+\n+static int error_with_patch(struct commit *commit,\n+\tconst char *subject, int subject_len,\n+\tstruct replay_opts *opts, int exit_code, int to_amend)\n+{\n+\tif (make_patch(commit, opts))\n+\t\treturn -1;\n+\n+\tif (to_amend) {\n+\t\tif (intend_to_amend())\n+\t\t\treturn -1;\n+\n+\t\tfprintf(stderr, \"You can amend the commit now, with\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"  git commit --amend %s\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"Once you are satisfied with your changes, run\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"  git rebase --continue\\n\", gpg_sign_opt_quoted(opts));\n+\t}\n+\telse if (exit_code)\n+\t\tfprintf(stderr, \"Could not apply %s... %.*s\\n\",\n+\t\t\tshort_commit_name(commit), subject_len, subject);\n+\n+\treturn exit_code;\n+}\n+\n static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n {\n-\tint res;\n+\tint res = 0;\n \n \tsetenv(GIT_REFLOG_ACTION, action_name(opts), 0);\n \tif (opts->allow_ff)\n@@ -1316,9 +1411,20 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tstruct todo_item *item = todo_list->items + todo_list->current;\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n-\t\tif (item->command <= TODO_REVERT)\n+\t\tif (item->command <= TODO_EDIT) {\n \t\t\tres = do_pick_commit(item->command, item->commit,\n \t\t\t\t\topts);\n+\t\t\tif (item->command == TODO_EDIT) {\n+\t\t\t\tstruct commit *commit = item->commit;\n+\t\t\t\tif (!res)\n+\t\t\t\t\twarning(_(\"stopped at %s... %.*s\"),\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\treturn error_with_patch(commit,\n+\t\t\t\t\titem->arg, item->arg_len, opts, res,\n+\t\t\t\t\t!res);\n+\t\t\t}\n+\t\t}\n \t\telse if (item->command != TODO_NOOP)\n \t\t\treturn error(_(\"unknown command %d\"), item->command);\n \n@@ -1327,6 +1433,12 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\treturn res;\n \t}\n \n+\tif (is_rebase_i(opts)) {\n+\t\t/* Stopped in the middle, as planned? */\n+\t\tif (todo_list->current < todo_list->nr)\n+\t\t\treturn 0;\n+\t}\n+\n \t/*\n \t * Sequence of picks finished successfully; cleanup by\n \t * removing the .git/sequencer directory\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307600","messageId":"09d0f53cf310c6a81ac6ffabe90f45aeeec39d9b.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 12/34] sequencer (rebase -i): skip some revert/cherry-pick specific code path","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:30:42Z","receivedAt":"2016-12-13T15:32:03Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When a cherry-pick continues without a \"todo script\", the intention is\nsimply to pick a single commit.\n\nHowever, when an interactive rebase is continued without a \"todo\nscript\", it means that the last command has been completed and that we\nnow need to clean up.\n\nThis commit guards the revert/cherry-pick specific steps so that they\nare not executed in rebase -i mode.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 25 ++++++++++++++-----------\n 1 file changed, 14 insertions(+), 11 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex abffaf3b40..4ceb6f3ac5 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1882,25 +1882,28 @@ int sequencer_continue(struct replay_opts *opts)\n \t\tif (commit_staged_changes(opts))\n \t\t\treturn -1;\n \t}\n-\tif (!file_exists(get_todo_path(opts)))\n+\telse if (!file_exists(get_todo_path(opts)))\n \t\treturn continue_single_pick();\n \tif (read_populate_opts(opts))\n \t\treturn -1;\n \tif ((res = read_populate_todo(&todo_list, opts)))\n \t\tgoto release_todo_list;\n \n-\t/* Verify that the conflict has been resolved */\n-\tif (file_exists(git_path_cherry_pick_head()) ||\n-\t    file_exists(git_path_revert_head())) {\n-\t\tres = continue_single_pick();\n-\t\tif (res)\n+\tif (!is_rebase_i(opts)) {\n+\t\t/* Verify that the conflict has been resolved */\n+\t\tif (file_exists(git_path_cherry_pick_head()) ||\n+\t\t    file_exists(git_path_revert_head())) {\n+\t\t\tres = continue_single_pick();\n+\t\t\tif (res)\n+\t\t\t\tgoto release_todo_list;\n+\t\t}\n+\t\tif (index_differs_from(\"HEAD\", 0, 0)) {\n+\t\t\tres = error_dirty_index(opts);\n \t\t\tgoto release_todo_list;\n+\t\t}\n+\t\ttodo_list.current++;\n \t}\n-\tif (index_differs_from(\"HEAD\", 0, 0)) {\n-\t\tres = error_dirty_index(opts);\n-\t\tgoto release_todo_list;\n-\t}\n-\ttodo_list.current++;\n+\n \tres = pick_commits(&todo_list, opts);\n release_todo_list:\n \ttodo_list_release(&todo_list);\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307601","messageId":"596e3cf410a339c3212eea76394fe49be1c05ef8.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 14/34] sequencer (rebase -i): update refs after a successful rebase","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:30:53Z","receivedAt":"2016-12-13T15:32:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"An interactive rebase operates on a detached HEAD (to keep the reflog\nof the original branch relatively clean), and updates the branch only\nat the end.\n\nNow that the sequencer learns to perform interactive rebases, it also\nneeds to learn the trick to update the branch before removing the\ndirectory containing the state of the interactive rebase.\n\nWe introduce a new head_ref variable in a wider scope than necessary at\nthe moment, to allow for a later patch that prints out \"Successfully\nrebased and updated <ref>\".\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 32 +++++++++++++++++++++++++++++++-\n 1 file changed, 31 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a6625e765d..a4e9b326ba 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -100,6 +100,8 @@ static GIT_PATH_FUNC(rebase_path_stopped_sha, \"rebase-merge/stopped-sha\")\n static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, \"rebase-merge/gpg_sign_opt\")\n static GIT_PATH_FUNC(rebase_path_orig_head, \"rebase-merge/orig-head\")\n static GIT_PATH_FUNC(rebase_path_verbose, \"rebase-merge/verbose\")\n+static GIT_PATH_FUNC(rebase_path_head_name, \"rebase-merge/head-name\")\n+static GIT_PATH_FUNC(rebase_path_onto, \"rebase-merge/onto\")\n \n static inline int is_rebase_i(const struct replay_opts *opts)\n {\n@@ -1793,12 +1795,39 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t}\n \n \tif (is_rebase_i(opts)) {\n-\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tstruct strbuf head_ref = STRBUF_INIT, buf = STRBUF_INIT;\n \n \t\t/* Stopped in the middle, as planned? */\n \t\tif (todo_list->current < todo_list->nr)\n \t\t\treturn 0;\n \n+\t\tif (read_oneliner(&head_ref, rebase_path_head_name(), 0) &&\n+\t\t\t\tstarts_with(head_ref.buf, \"refs/\")) {\n+\t\t\tunsigned char head[20], orig[20];\n+\n+\t\t\tif (get_sha1(\"HEAD\", head))\n+\t\t\t\treturn error(_(\"cannot read HEAD\"));\n+\t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0) ||\n+\t\t\t\t\tget_sha1_hex(buf.buf, orig))\n+\t\t\t\treturn error(_(\"could not read orig-head\"));\n+\t\t\tstrbuf_addf(&buf, \"rebase -i (finish): %s onto \",\n+\t\t\t\thead_ref.buf);\n+\t\t\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n+\t\t\t\treturn error(_(\"could not read 'onto'\"));\n+\t\t\tif (update_ref(buf.buf, head_ref.buf, head, orig,\n+\t\t\t\t\tREF_NODEREF, UPDATE_REFS_MSG_ON_ERR))\n+\t\t\t\treturn error(_(\"could not update %s\"),\n+\t\t\t\t\thead_ref.buf);\n+\t\t\tstrbuf_reset(&buf);\n+\t\t\tstrbuf_addf(&buf,\n+\t\t\t\t\"rebase -i (finish): returning to %s\",\n+\t\t\t\thead_ref.buf);\n+\t\t\tif (create_symref(\"HEAD\", head_ref.buf, buf.buf))\n+\t\t\t\treturn error(_(\"could not update HEAD to %s\"),\n+\t\t\t\t\thead_ref.buf);\n+\t\t\tstrbuf_reset(&buf);\n+\t\t}\n+\n \t\tif (opts->verbose) {\n \t\t\tconst char *argv[] = {\n \t\t\t\t\"diff-tree\", \"--stat\", NULL, NULL\n@@ -1813,6 +1842,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\tstrbuf_reset(&buf);\n \t\t}\n \t\tstrbuf_release(&buf);\n+\t\tstrbuf_release(&head_ref);\n \t}\n \n \t/*\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307602","messageId":"b82347c627fd3b8a74827bc773a5df2d16c6dded.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 27/34] sequencer (rebase -i): differentiate between comments and 'noop'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:31:57Z","receivedAt":"2016-12-13T15:32:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"In the upcoming patch, we will support rebase -i's progress\nreporting. The progress skips comments but counts 'noop's.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 15 +++++++++------\n 1 file changed, 9 insertions(+), 6 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 1f314b2743..63f6f25ced 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -770,7 +770,9 @@ enum todo_command {\n \tTODO_EXEC,\n \t/* commands that do nothing but are counted for reporting progress */\n \tTODO_NOOP,\n-\tTODO_DROP\n+\tTODO_DROP,\n+\t/* comments (not counted for reporting progress) */\n+\tTODO_COMMENT\n };\n \n static struct {\n@@ -785,12 +787,13 @@ static struct {\n \t{ 's', \"squash\" },\n \t{ 'x', \"exec\" },\n \t{ 0,   \"noop\" },\n-\t{ 'd', \"drop\" }\n+\t{ 'd', \"drop\" },\n+\t{ 0,   NULL }\n };\n \n static const char *command_to_string(const enum todo_command command)\n {\n-\tif ((size_t)command < ARRAY_SIZE(todo_command_info))\n+\tif (command < TODO_COMMENT)\n \t\treturn todo_command_info[command].str;\n \tdie(\"Unknown command: %d\", command);\n }\n@@ -1237,14 +1240,14 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \tbol += strspn(bol, \" \\t\");\n \n \tif (bol == eol || *bol == '\\r' || *bol == comment_line_char) {\n-\t\titem->command = TODO_NOOP;\n+\t\titem->command = TODO_COMMENT;\n \t\titem->commit = NULL;\n \t\titem->arg = bol;\n \t\titem->arg_len = eol - bol;\n \t\treturn 0;\n \t}\n \n-\tfor (i = 0; i < ARRAY_SIZE(todo_command_info); i++)\n+\tfor (i = 0; i < TODO_COMMENT; i++)\n \t\tif (skip_prefix(bol, todo_command_info[i].str, &bol)) {\n \t\t\titem->command = i;\n \t\t\tbreak;\n@@ -1254,7 +1257,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \t\t\titem->command = i;\n \t\t\tbreak;\n \t\t}\n-\tif (i >= ARRAY_SIZE(todo_command_info))\n+\tif (i >= TODO_COMMENT)\n \t\treturn -1;\n \n \tif (item->command == TODO_NOOP) {\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307603","messageId":"4a1229e9f2d3715607935519f359b5d7986c2290.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 06/34] sequencer (rebase -i): write the 'done' file","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:29:56Z","receivedAt":"2016-12-13T15:32:28Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"In the interactive rebase, commands that were successfully processed are\nnot simply discarded, but appended to the 'done' file instead. This is\nused e.g. to display the current state to the user in the output of\n`git status` or the progress.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 23 +++++++++++++++++++++++\n 1 file changed, 23 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 1ab50884bd..f6e20b142a 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -39,6 +39,12 @@ static GIT_PATH_FUNC(rebase_path, \"rebase-merge\")\n  */\n static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n /*\n+ * The rebase command lines that have already been processed. A line\n+ * is moved here when it is first handled, before any associated user\n+ * actions.\n+ */\n+static GIT_PATH_FUNC(rebase_path_done, \"rebase-merge/done\")\n+/*\n  * A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and\n  * GIT_AUTHOR_DATE that will be used for the commit that is currently\n  * being rebased.\n@@ -1295,6 +1301,23 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)\n \t\treturn error_errno(_(\"could not write to '%s'\"), todo_path);\n \tif (commit_lock_file(&todo_lock) < 0)\n \t\treturn error(_(\"failed to finalize '%s'.\"), todo_path);\n+\n+\tif (is_rebase_i(opts)) {\n+\t\tconst char *done_path = rebase_path_done();\n+\t\tint fd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);\n+\t\tint prev_offset = !next ? 0 :\n+\t\t\ttodo_list->items[next - 1].offset_in_buf;\n+\n+\t\tif (fd >= 0 && offset > prev_offset &&\n+\t\t    write_in_full(fd, todo_list->buf.buf + prev_offset,\n+\t\t\t\t  offset - prev_offset) < 0) {\n+\t\t\tclose(fd);\n+\t\t\treturn error_errno(_(\"could not write to '%s'\"),\n+\t\t\t\t\t   done_path);\n+\t\t}\n+\t\tif (fd >= 0)\n+\t\t\tclose(fd);\n+\t}\n \treturn 0;\n }\n \n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307604","messageId":"1d1f8d8b0696769bb85dd8a2269dc281aa91eede.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 05/34] sequencer (rebase -i): learn about the 'verbose' mode","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:29:53Z","receivedAt":"2016-12-13T15:32:29Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When calling `git rebase -i -v`, the user wants to see some statistics\nafter the commits were rebased. Let's show some.\n\nThe strbuf we use to perform that task will be used for other things\nin subsequent commits, hence it is declared and initialized in a wider\nscope than strictly needed here.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 22 ++++++++++++++++++++++\n sequencer.h |  1 +\n 2 files changed, 23 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 700b7575ed..1ab50884bd 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -63,6 +63,8 @@ static GIT_PATH_FUNC(rebase_path_stopped_sha, \"rebase-merge/stopped-sha\")\n  * command-line (and are only consumed, not modified, by the sequencer).\n  */\n static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, \"rebase-merge/gpg_sign_opt\")\n+static GIT_PATH_FUNC(rebase_path_orig_head, \"rebase-merge/orig-head\")\n+static GIT_PATH_FUNC(rebase_path_verbose, \"rebase-merge/verbose\")\n \n static inline int is_rebase_i(const struct replay_opts *opts)\n {\n@@ -1121,6 +1123,9 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\t}\n \t\tstrbuf_release(&buf);\n \n+\t\tif (file_exists(rebase_path_verbose()))\n+\t\t\topts->verbose = 1;\n+\n \t\treturn 0;\n \t}\n \n@@ -1493,9 +1498,26 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t}\n \n \tif (is_rebase_i(opts)) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\n \t\t/* Stopped in the middle, as planned? */\n \t\tif (todo_list->current < todo_list->nr)\n \t\t\treturn 0;\n+\n+\t\tif (opts->verbose) {\n+\t\t\tconst char *argv[] = {\n+\t\t\t\t\"diff-tree\", \"--stat\", NULL, NULL\n+\t\t\t};\n+\n+\t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0))\n+\t\t\t\treturn error(_(\"could not read '%s'\"),\n+\t\t\t\t\trebase_path_orig_head());\n+\t\t\tstrbuf_addstr(&buf, \"..HEAD\");\n+\t\t\targv[2] = buf.buf;\n+\t\t\trun_command_v_opt(argv, RUN_GIT_CMD);\n+\t\t\tstrbuf_reset(&buf);\n+\t\t}\n+\t\tstrbuf_release(&buf);\n \t}\n \n \t/*\ndiff --git a/sequencer.h b/sequencer.h\nindex cb21cfddee..f885b68395 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -24,6 +24,7 @@ struct replay_opts {\n \tint allow_empty;\n \tint allow_empty_message;\n \tint keep_redundant_commits;\n+\tint verbose;\n \n \tint mainline;\n \n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307605","messageId":"ae521f75a105c6b9e54595d68bda3c5b62f313b6.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:30:01Z","receivedAt":"2016-12-13T15:32:31Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is a huge patch, and at the same time a huge step forward to\nexecute the performance-critical parts of the interactive rebase in a\nbuiltin command.\n\nSince 'fixup' and 'squash' are not only similar, but also need to know\nabout each other (we want to reduce a series of fixups/squashes into a\nsingle, final commit message edit, from the user's point of view), we\nreally have to implement them both at the same time.\n\nMost of the actual work is done by the existing code path that already\nhandles the \"pick\" and the \"edit\" commands; We added support for other\nfeatures (e.g. to amend the commit message) in the patches leading up to\nthis one, yet there are still quite a few bits in this patch that simply\nwould not make sense as individual patches (such as: determining whether\nthere was anything to \"fix up\" in the \"todo\" script, etc).\n\nIn theory, it would be possible to reuse the fast-forward code path also\nfor the fixup and the squash code paths, but in practice this would make\nthe code less readable. The end result cannot be fast-forwarded anyway,\ntherefore let's just extend the cherry-picking code path for now.\n\nSince the sequencer parses the entire `git-rebase-todo` script in one go,\nfixup or squash commands without a preceding pick can be reported early\n(in git-rebase--interactive, we could only report such errors just before\nexecuting the fixup/squash).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 232 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---\n 1 file changed, 222 insertions(+), 10 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex f6e20b142a..271c21581d 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -45,6 +45,35 @@ static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n  */\n static GIT_PATH_FUNC(rebase_path_done, \"rebase-merge/done\")\n /*\n+ * The commit message that is planned to be used for any changes that\n+ * need to be committed following a user interaction.\n+ */\n+static GIT_PATH_FUNC(rebase_path_message, \"rebase-merge/message\")\n+/*\n+ * The file into which is accumulated the suggested commit message for\n+ * squash/fixup commands. When the first of a series of squash/fixups\n+ * is seen, the file is created and the commit message from the\n+ * previous commit and from the first squash/fixup commit are written\n+ * to it. The commit message for each subsequent squash/fixup commit\n+ * is appended to the file as it is processed.\n+ *\n+ * The first line of the file is of the form\n+ *     # This is a combination of $count commits.\n+ * where $count is the number of commits whose messages have been\n+ * written to the file so far (including the initial \"pick\" commit).\n+ * Each time that a commit message is processed, this line is read and\n+ * updated. It is deleted just before the combined commit is made.\n+ */\n+static GIT_PATH_FUNC(rebase_path_squash_msg, \"rebase-merge/message-squash\")\n+/*\n+ * If the current series of squash/fixups has not yet included a squash\n+ * command, then this file exists and holds the commit message of the\n+ * original \"pick\" commit.  (If the series ends without a \"squash\"\n+ * command, then this can be used as the commit message of the combined\n+ * commit without opening the editor.)\n+ */\n+static GIT_PATH_FUNC(rebase_path_fixup_msg, \"rebase-merge/message-fixup\")\n+/*\n  * A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and\n  * GIT_AUTHOR_DATE that will be used for the commit that is currently\n  * being rebased.\n@@ -673,6 +702,8 @@ enum todo_command {\n \tTODO_PICK = 0,\n \tTODO_REVERT,\n \tTODO_EDIT,\n+\tTODO_FIXUP,\n+\tTODO_SQUASH,\n \t/* commands that do something else than handling a single commit */\n \tTODO_EXEC,\n \t/* commands that do nothing but are counted for reporting progress */\n@@ -683,6 +714,8 @@ static const char *todo_command_strings[] = {\n \t\"pick\",\n \t\"revert\",\n \t\"edit\",\n+\t\"fixup\",\n+\t\"squash\",\n \t\"exec\",\n \t\"noop\"\n };\n@@ -694,16 +727,119 @@ static const char *command_to_string(const enum todo_command command)\n \tdie(\"Unknown command: %d\", command);\n }\n \n+static int is_fixup(enum todo_command command)\n+{\n+\treturn command == TODO_FIXUP || command == TODO_SQUASH;\n+}\n+\n+static int update_squash_messages(enum todo_command command,\n+\t\tstruct commit *commit, struct replay_opts *opts)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint count, res;\n+\tconst char *message, *body;\n+\n+\tif (file_exists(rebase_path_squash_msg())) {\n+\t\tchar *p, *p2;\n+\n+\t\tif (strbuf_read_file(&buf, rebase_path_squash_msg(), 2048) <= 0)\n+\t\t\treturn error(_(\"could not read '%s'\"),\n+\t\t\t\trebase_path_squash_msg());\n+\n+\t\tif (buf.buf[0] != comment_line_char ||\n+\t\t    !skip_prefix(buf.buf + 1, \" This is a combination of \",\n+\t\t\t\t (const char **)&p))\n+\t\t\treturn error(_(\"unexpected 1st line of squash message:\"\n+\t\t\t\t       \"\\n\\n\\t%.*s\"),\n+\t\t\t\t     (int)(strchrnul(buf.buf, '\\n') - buf.buf),\n+\t\t\t\t     buf.buf);\n+\t\tcount = strtol(p, &p2, 10);\n+\n+\t\tif (count < 1 || *p2 != ' ')\n+\t\t\treturn error(_(\"invalid 1st line of squash message:\\n\"\n+\t\t\t\t       \"\\n\\t%.*s\"),\n+\t\t\t\t     (int)(strchrnul(buf.buf, '\\n') - buf.buf),\n+\t\t\t\t     buf.buf);\n+\n+\t\tsprintf((char *)p, \"%d\", ++count);\n+\t\tif (!*p2)\n+\t\t\t*p2 = ' ';\n+\t\telse {\n+\t\t\t*(++p2) = 'c';\n+\t\t\tstrbuf_insert(&buf, p2 - buf.buf, \" \", 1);\n+\t\t}\n+\t}\n+\telse {\n+\t\tunsigned char head[20];\n+\t\tstruct commit *head_commit;\n+\t\tconst char *head_message, *body;\n+\n+\t\tif (get_sha1(\"HEAD\", head))\n+\t\t\treturn error(_(\"need a HEAD to fixup\"));\n+\t\tif (!(head_commit = lookup_commit_reference(head)))\n+\t\t\treturn error(_(\"could not read HEAD\"));\n+\t\tif (!(head_message = get_commit_buffer(head_commit, NULL)))\n+\t\t\treturn error(_(\"could not read HEAD's commit message\"));\n+\n+\t\tbody = strstr(head_message, \"\\n\\n\");\n+\t\tif (!body)\n+\t\t\tbody = \"\";\n+\t\telse\n+\t\t\tbody = skip_blank_lines(body + 2);\n+\t\tif (write_message(body, strlen(body),\n+\t\t\t\t  rebase_path_fixup_msg(), 0))\n+\t\t\treturn error(_(\"cannot write '%s'\"),\n+\t\t\t\t     rebase_path_fixup_msg());\n+\n+\t\tcount = 2;\n+\t\tstrbuf_addf(&buf, _(\"%c This is a combination of 2 commits.\\n\"\n+\t\t\t\t    \"%c The first commit's message is:\\n\\n%s\"),\n+\t\t\t    comment_line_char, comment_line_char, body);\n+\n+\t\tunuse_commit_buffer(head_commit, head_message);\n+\t}\n+\n+\tif (!(message = get_commit_buffer(commit, NULL)))\n+\t\treturn error(_(\"could not read commit message of %s\"),\n+\t\t\t     oid_to_hex(&commit->object.oid));\n+\tbody = strstr(message, \"\\n\\n\");\n+\tif (!body)\n+\t\tbody = \"\";\n+\telse\n+\t\tbody = skip_blank_lines(body + 2);\n+\n+\tif (command == TODO_SQUASH) {\n+\t\tunlink(rebase_path_fixup_msg());\n+\t\tstrbuf_addf(&buf, _(\"\\n%c This is the commit message #%d:\\n\"\n+\t\t\t\t    \"\\n%s\"),\n+\t\t\t    comment_line_char, count, body);\n+\t}\n+\telse if (command == TODO_FIXUP) {\n+\t\tstrbuf_addf(&buf, _(\"\\n%c The commit message #%d \"\n+\t\t\t\t    \"will be skipped:\\n\\n\"),\n+\t\t\t    comment_line_char, count);\n+\t\tstrbuf_add_commented_lines(&buf, body, strlen(body));\n+\t}\n+\telse\n+\t\treturn error(_(\"unknown command: %d\"), command);\n+\tunuse_commit_buffer(commit, message);\n+\n+\tres = write_message(buf.buf, buf.len, rebase_path_squash_msg(), 0);\n+\tstrbuf_release(&buf);\n+\treturn res;\n+}\n \n static int do_pick_commit(enum todo_command command, struct commit *commit,\n-\t\tstruct replay_opts *opts)\n+\t\tstruct replay_opts *opts, int final_fixup)\n {\n+\tint edit = opts->edit, cleanup_commit_message = 0;\n+\tconst char *msg_file = edit ? NULL : git_path_merge_msg();\n \tunsigned char head[20];\n \tstruct commit *base, *next, *parent;\n \tconst char *base_label, *next_label;\n \tstruct commit_message msg = { NULL, NULL, NULL, NULL };\n \tstruct strbuf msgbuf = STRBUF_INIT;\n-\tint res, unborn = 0, allow;\n+\tint res, unborn = 0, amend = 0, allow;\n \n \tif (opts->no_commit) {\n \t\t/*\n@@ -749,7 +885,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \telse\n \t\tparent = commit->parents->item;\n \n-\tif (opts->allow_ff &&\n+\tif (opts->allow_ff && !is_fixup(command) &&\n \t    ((parent && !hashcmp(parent->object.oid.hash, head)) ||\n \t     (!parent && unborn)))\n \t\treturn fast_forward_to(commit->object.oid.hash, head, unborn, opts);\n@@ -813,6 +949,28 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t}\n \t}\n \n+\tif (is_fixup(command)) {\n+\t\tif (update_squash_messages(command, commit, opts))\n+\t\t\treturn -1;\n+\t\tamend = 1;\n+\t\tif (!final_fixup)\n+\t\t\tmsg_file = rebase_path_squash_msg();\n+\t\telse if (file_exists(rebase_path_fixup_msg())) {\n+\t\t\tcleanup_commit_message = 1;\n+\t\t\tmsg_file = rebase_path_fixup_msg();\n+\t\t}\n+\t\telse {\n+\t\t\tconst char *dest = git_path(\"SQUASH_MSG\");\n+\t\t\tunlink(dest);\n+\t\t\tif (copy_file(dest, rebase_path_squash_msg(), 0666))\n+\t\t\t\treturn error(_(\"could not rename '%s' to '%s'\"),\n+\t\t\t\t\t     rebase_path_squash_msg(), dest);\n+\t\t\tunlink(git_path(\"MERGE_MSG\"));\n+\t\t\tmsg_file = dest;\n+\t\t\tedit = 1;\n+\t\t}\n+\t}\n+\n \tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n@@ -868,8 +1026,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tgoto leave;\n \t}\n \tif (!opts->no_commit)\n-\t\tres = run_git_commit(opts->edit ? NULL : git_path_merge_msg(),\n-\t\t\t\t     opts, allow, opts->edit, 0, 0);\n+\t\tres = run_git_commit(msg_file, opts, allow, edit, amend,\n+\t\t\t\t     cleanup_commit_message);\n+\n+\tif (!res && final_fixup) {\n+\t\tunlink(rebase_path_fixup_msg());\n+\t\tunlink(rebase_path_squash_msg());\n+\t}\n \n leave:\n \tfree_message(commit, &msg);\n@@ -1009,7 +1172,7 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n {\n \tstruct todo_item *item;\n \tchar *p = buf, *next_p;\n-\tint i, res = 0;\n+\tint i, res = 0, fixup_okay = file_exists(rebase_path_done());\n \n \tfor (i = 1; *p; i++, p = next_p) {\n \t\tchar *eol = strchrnul(p, '\\n');\n@@ -1024,8 +1187,16 @@ 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 = -1;\n+\t\t\titem->command = TODO_NOOP;\n \t\t}\n+\n+\t\tif (fixup_okay)\n+\t\t\t; /* do nothing */\n+\t\telse if (is_fixup(item->command))\n+\t\t\treturn error(_(\"cannot '%s' without a previous commit\"),\n+\t\t\t\tcommand_to_string(item->command));\n+\t\telse if (item->command != TODO_NOOP)\n+\t\t\tfixup_okay = 1;\n \t}\n \tif (!todo_list->nr)\n \t\treturn error(_(\"no commits parsed.\"));\n@@ -1434,6 +1605,20 @@ static int error_with_patch(struct commit *commit,\n \treturn exit_code;\n }\n \n+static int error_failed_squash(struct commit *commit,\n+\tstruct replay_opts *opts, int subject_len, const char *subject)\n+{\n+\tif (rename(rebase_path_squash_msg(), rebase_path_message()))\n+\t\treturn error(_(\"could not rename '%s' to '%s'\"),\n+\t\t\trebase_path_squash_msg(), rebase_path_message());\n+\tunlink(rebase_path_fixup_msg());\n+\tunlink(git_path(\"MERGE_MSG\"));\n+\tif (copy_file(git_path(\"MERGE_MSG\"), rebase_path_message(), 0666))\n+\t\treturn error(_(\"could not copy '%s' to '%s'\"),\n+\t\t\t     rebase_path_message(), git_path(\"MERGE_MSG\"));\n+\treturn error_with_patch(commit, subject, subject_len, opts, 1, 0);\n+}\n+\n static int do_exec(const char *command_line)\n {\n \tconst char *child_argv[] = { NULL, NULL };\n@@ -1475,6 +1660,21 @@ static int do_exec(const char *command_line)\n \treturn status;\n }\n \n+static int is_final_fixup(struct todo_list *todo_list)\n+{\n+\tint i = todo_list->current;\n+\n+\tif (!is_fixup(todo_list->items[i].command))\n+\t\treturn 0;\n+\n+\twhile (++i < todo_list->nr)\n+\t\tif (is_fixup(todo_list->items[i].command))\n+\t\t\treturn 0;\n+\t\telse if (todo_list->items[i].command < TODO_NOOP)\n+\t\t\tbreak;\n+\treturn 1;\n+}\n+\n static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n {\n \tint res = 0;\n@@ -1490,9 +1690,15 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tstruct todo_item *item = todo_list->items + todo_list->current;\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n-\t\tif (item->command <= TODO_EDIT) {\n+\t\tif (is_rebase_i(opts)) {\n+\t\t\tunlink(rebase_path_message());\n+\t\t\tunlink(rebase_path_author_script());\n+\t\t\tunlink(rebase_path_stopped_sha());\n+\t\t\tunlink(rebase_path_amend());\n+\t\t}\n+\t\tif (item->command <= TODO_SQUASH) {\n \t\t\tres = do_pick_commit(item->command, item->commit,\n-\t\t\t\t\topts);\n+\t\t\t\t\topts, is_final_fixup(todo_list));\n \t\t\tif (item->command == TODO_EDIT) {\n \t\t\t\tstruct commit *commit = item->commit;\n \t\t\t\tif (!res)\n@@ -1503,6 +1709,12 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\t\titem->arg, item->arg_len, opts, res,\n \t\t\t\t\t!res);\n \t\t\t}\n+\t\t\tif (res && is_fixup(item->command)) {\n+\t\t\t\tif (res == 1)\n+\t\t\t\t\tintend_to_amend();\n+\t\t\t\treturn error_failed_squash(item->commit, opts,\n+\t\t\t\t\titem->arg_len, item->arg);\n+\t\t\t}\n \t\t}\n \t\telse if (item->command == TODO_EXEC) {\n \t\t\tchar *end_of_arg = (char *)(item->arg + item->arg_len);\n@@ -1597,7 +1809,7 @@ static int single_pick(struct commit *cmit, struct replay_opts *opts)\n {\n \tsetenv(GIT_REFLOG_ACTION, action_name(opts), 0);\n \treturn do_pick_commit(opts->action == REPLAY_PICK ?\n-\t\tTODO_PICK : TODO_REVERT, cmit, opts);\n+\t\tTODO_PICK : TODO_REVERT, cmit, opts, 0);\n }\n \n int sequencer_pick_revisions(struct replay_opts *opts)\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307606","messageId":"3e44043dec45fe197098308e80992684688382f9.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 13/34] sequencer (rebase -i): the todo can be empty when continuing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:30:47Z","receivedAt":"2016-12-13T15:32:43Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When the last command of an interactive rebase fails, the user needs to\nresolve the problem and then continue the interactive rebase. Naturally,\nthe todo script is empty by then. So let's not complain about that!\n\nTo that end, let's move that test out of the function that parses the\ntodo script, and into the more high-level function read_populate_todo().\nThis is also necessary by now because the lower-level parse_insn_buffer()\nhas no idea whether we are performing an interactive rebase or not.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 7 +++++--\n 1 file changed, 5 insertions(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 4ceb6f3ac5..a6625e765d 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1255,8 +1255,7 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n \t\telse if (item->command != TODO_NOOP)\n \t\t\tfixup_okay = 1;\n \t}\n-\tif (!todo_list->nr)\n-\t\treturn error(_(\"no commits parsed.\"));\n+\n \treturn res;\n }\n \n@@ -1280,6 +1279,10 @@ static int read_populate_todo(struct todo_list *todo_list,\n \tif (res)\n \t\treturn error(_(\"unusable instruction sheet: '%s'\"), todo_file);\n \n+\tif (!todo_list->nr &&\n+\t    (!is_rebase_i(opts) || !file_exists(rebase_path_done())))\n+\t\treturn error(_(\"no commits parsed.\"));\n+\n \tif (!is_rebase_i(opts)) {\n \t\tenum todo_command valid =\n \t\t\topts->action == REPLAY_PICK ? TODO_PICK : TODO_REVERT;\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307607","messageId":"1e82aeabb906a35175362418b2b4957fae50c3b0.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 28/34] run_command_opt(): optionally hide stderr when the command succeeds","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:32:00Z","receivedAt":"2016-12-13T15:32:46Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This will be needed to hide the output of `git commit` when the\nsequencer handles an interactive rebase's script.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n run-command.c | 23 +++++++++++++++++++++++\n run-command.h |  1 +\n 2 files changed, 24 insertions(+)\n\ndiff --git a/run-command.c b/run-command.c\nindex ca905a9e80..5bb957afdd 100644\n--- a/run-command.c\n+++ b/run-command.c\n@@ -589,6 +589,29 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const\n \tcmd.clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0;\n \tcmd.dir = dir;\n \tcmd.env = env;\n+\n+\tif (opt & RUN_HIDE_STDERR_ON_SUCCESS) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tint res;\n+\n+\t\tcmd.err = -1;\n+\t\tif (start_command(&cmd) < 0)\n+\t\t\treturn -1;\n+\n+\t\tif (strbuf_read(&buf, cmd.err, 0) < 0) {\n+\t\t\tclose(cmd.err);\n+\t\t\tfinish_command(&cmd); /* throw away exit code */\n+\t\t\treturn -1;\n+\t\t}\n+\n+\t\tclose(cmd.err);\n+\t\tres = finish_command(&cmd);\n+\t\tif (res)\n+\t\t\tfputs(buf.buf, stderr);\n+\t\tstrbuf_release(&buf);\n+\t\treturn res;\n+\t}\n+\n \treturn run_command(&cmd);\n }\n \ndiff --git a/run-command.h b/run-command.h\nindex dd1c78c28d..65a21ddd4e 100644\n--- a/run-command.h\n+++ b/run-command.h\n@@ -72,6 +72,7 @@ extern int run_hook_ve(const char *const *env, const char *name, va_list args);\n #define RUN_SILENT_EXEC_FAILURE 8\n #define RUN_USING_SHELL 16\n #define RUN_CLEAN_ON_EXIT 32\n+#define RUN_HIDE_STDERR_ON_SUCCESS 64\n int run_command_v_opt(const char **argv, int opt);\n \n /*\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307608","messageId":"ed2df6cecd9a53681d2fead4ae5b4839d653afc3.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 26/34] sequencer (rebase -i): implement the 'drop' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:31:54Z","receivedAt":"2016-12-13T15:32:48Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The parsing part of a 'drop' command is almost identical to parsing a\n'pick', while the operation is the same as that of a 'noop'.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 12 +++++++-----\n 1 file changed, 7 insertions(+), 5 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 03256b5b1d..1f314b2743 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -769,7 +769,8 @@ enum todo_command {\n \t/* commands that do something else than handling a single commit */\n \tTODO_EXEC,\n \t/* commands that do nothing but are counted for reporting progress */\n-\tTODO_NOOP\n+\tTODO_NOOP,\n+\tTODO_DROP\n };\n \n static struct {\n@@ -783,7 +784,8 @@ static struct {\n \t{ 'f', \"fixup\" },\n \t{ 's', \"squash\" },\n \t{ 'x', \"exec\" },\n-\t{ 0,   \"noop\" }\n+\t{ 0,   \"noop\" },\n+\t{ 'd', \"drop\" }\n };\n \n static const char *command_to_string(const enum todo_command command)\n@@ -1317,7 +1319,7 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n \t\telse if (is_fixup(item->command))\n \t\t\treturn error(_(\"cannot '%s' without a previous commit\"),\n \t\t\t\tcommand_to_string(item->command));\n-\t\telse if (item->command != TODO_NOOP)\n+\t\telse if (item->command < TODO_NOOP)\n \t\t\tfixup_okay = 1;\n \t}\n \n@@ -1827,7 +1829,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \tint i;\n \n \tfor (i = todo_list->current + offset; i < todo_list->nr; i++)\n-\t\tif (todo_list->items[i].command != TODO_NOOP)\n+\t\tif (todo_list->items[i].command < TODO_NOOP)\n \t\t\treturn todo_list->items[i].command;\n \n \treturn -1;\n@@ -1960,7 +1962,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\tres = do_exec(item->arg);\n \t\t\t*end_of_arg = saved;\n \t\t}\n-\t\telse if (item->command != TODO_NOOP)\n+\t\telse if (item->command < TODO_NOOP)\n \t\t\treturn error(_(\"unknown command %d\"), item->command);\n \n \t\ttodo_list->current++;\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307609","messageId":"091217525a7ff71794b3544680571ce9814a297f.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 15/34] sequencer (rebase -i): leave a patch upon error","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:31:11Z","receivedAt":"2016-12-13T15:32:49Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When doing an interactive rebase, we want to leave a 'patch' file for\nfurther inspection by the user (even if we never tried to actually apply\nthat patch, since we're cherry-picking instead).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 3 +++\n 1 file changed, 3 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a4e9b326ba..4361fe0e94 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1777,6 +1777,9 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\treturn error_failed_squash(item->commit, opts,\n \t\t\t\t\titem->arg_len, item->arg);\n \t\t\t}\n+\t\t\telse if (res && is_rebase_i(opts))\n+\t\t\t\treturn res | error_with_patch(item->commit,\n+\t\t\t\t\titem->arg, item->arg_len, opts, res, 0);\n \t\t}\n \t\telse if (item->command == TODO_EXEC) {\n \t\t\tchar *end_of_arg = (char *)(item->arg + item->arg_len);\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307610","messageId":"afe09db26dd4d7a9adbff25f051cdf3402570a69.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 25/34] sequencer (rebase -i): allow rescheduling commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:31:51Z","receivedAt":"2016-12-13T15:32:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The interactive rebase has the very special magic that a cherry-pick\nthat exits with a status different from 0 and 1 signifies a failure to\neven record that a cherry-pick was started.\n\nThis can happen e.g. when a fast-forward fails because it would\noverwrite untracked files.\n\nIn that case, we must reschedule the command that we thought we already\nhad at least started successfully.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 6 ++++++\n 1 file changed, 6 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a67ecec961..03256b5b1d 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1922,6 +1922,12 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\t\t1);\n \t\t\tres = do_pick_commit(item->command, item->commit,\n \t\t\t\t\topts, is_final_fixup(todo_list));\n+\t\t\tif (is_rebase_i(opts) && res < 0) {\n+\t\t\t\t/* Reschedule */\n+\t\t\t\ttodo_list->current--;\n+\t\t\t\tif (save_todo(todo_list, opts))\n+\t\t\t\t\treturn -1;\n+\t\t\t}\n \t\t\tif (item->command == TODO_EDIT) {\n \t\t\t\tstruct commit *commit = item->commit;\n \t\t\t\tif (!res)\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307611","messageId":"a21233f368f066408051e6bdc9a2b6ec513e9e11.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 24/34] sequencer (rebase -i): respect strategy/strategy_opts settings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:31:47Z","receivedAt":"2016-12-13T15:32:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The sequencer already has an idea about using different merge\nstrategies. We just piggy-back on top of that, using rebase -i's\nown settings, when running the sequencer in interactive rebase mode.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 26 +++++++++++++++++++++++++-\n 1 file changed, 25 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex c11eceb70b..a67ecec961 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -112,6 +112,8 @@ static GIT_PATH_FUNC(rebase_path_verbose, \"rebase-merge/verbose\")\n static GIT_PATH_FUNC(rebase_path_head_name, \"rebase-merge/head-name\")\n static GIT_PATH_FUNC(rebase_path_onto, \"rebase-merge/onto\")\n static GIT_PATH_FUNC(rebase_path_autostash, \"rebase-merge/autostash\")\n+static GIT_PATH_FUNC(rebase_path_strategy, \"rebase-merge/strategy\")\n+static GIT_PATH_FUNC(rebase_path_strategy_opts, \"rebase-merge/strategy_opts\")\n \n static inline int is_rebase_i(const struct replay_opts *opts)\n {\n@@ -1408,6 +1410,26 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n \treturn 0;\n }\n \n+static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)\n+{\n+\tint i;\n+\n+\tstrbuf_reset(buf);\n+\tif (!read_oneliner(buf, rebase_path_strategy(), 0))\n+\t\treturn;\n+\topts->strategy = strbuf_detach(buf, NULL);\n+\tif (!read_oneliner(buf, rebase_path_strategy_opts(), 0))\n+\t\treturn;\n+\n+\topts->xopts_nr = split_cmdline(buf->buf, (const char ***)&opts->xopts);\n+\tfor (i = 0; i < opts->xopts_nr; i++) {\n+\t\tconst char *arg = opts->xopts[i];\n+\n+\t\tskip_prefix(arg, \"--\", &arg);\n+\t\topts->xopts[i] = xstrdup(arg);\n+\t}\n+}\n+\n static int read_populate_opts(struct replay_opts *opts)\n {\n \tif (is_rebase_i(opts)) {\n@@ -1421,11 +1443,13 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\t\t\topts->gpg_sign = xstrdup(buf.buf + 2);\n \t\t\t}\n \t\t}\n-\t\tstrbuf_release(&buf);\n \n \t\tif (file_exists(rebase_path_verbose()))\n \t\t\topts->verbose = 1;\n \n+\t\tread_strategy_opts(opts, &buf);\n+\t\tstrbuf_release(&buf);\n+\n \t\treturn 0;\n \t}\n \n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307612","messageId":"a2ea8bd57461fe0969f3df09a0374005f12ac6c7.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 20/34] sequencer (rebase -i): copy commit notes at end","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:31:29Z","receivedAt":"2016-12-13T15:33:01Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When rebasing commits that have commit notes attached, the interactive\nrebase rewrites those notes faithfully at the end. The sequencer must\ndo this, too, if it wishes to do interactive rebase's job.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 76 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 118696f6d3..7ab533abd9 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -94,6 +94,15 @@ static GIT_PATH_FUNC(rebase_path_amend, \"rebase-merge/amend\")\n  */\n static GIT_PATH_FUNC(rebase_path_stopped_sha, \"rebase-merge/stopped-sha\")\n /*\n+ * For the post-rewrite hook, we make a list of rewritten commits and\n+ * their new sha1s.  The rewritten-pending list keeps the sha1s of\n+ * commits that have been processed, but not committed yet,\n+ * e.g. because they are waiting for a 'squash' command.\n+ */\n+static GIT_PATH_FUNC(rebase_path_rewritten_list, \"rebase-merge/rewritten-list\")\n+static GIT_PATH_FUNC(rebase_path_rewritten_pending,\n+\t\"rebase-merge/rewritten-pending\")\n+/*\n  * The following files are written by git-rebase just after parsing the\n  * command-line (and are only consumed, not modified, by the sequencer).\n  */\n@@ -883,6 +892,44 @@ static int update_squash_messages(enum todo_command command,\n \treturn res;\n }\n \n+static void flush_rewritten_pending(void) {\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tunsigned char newsha1[20];\n+\tFILE *out;\n+\n+\tif (strbuf_read_file(&buf, rebase_path_rewritten_pending(), 82) > 0 &&\n+\t\t\t!get_sha1(\"HEAD\", newsha1) &&\n+\t\t\t(out = fopen(rebase_path_rewritten_list(), \"a\"))) {\n+\t\tchar *bol = buf.buf, *eol;\n+\n+\t\twhile (*bol) {\n+\t\t\teol = strchrnul(bol, '\\n');\n+\t\t\tfprintf(out, \"%.*s %s\\n\", (int)(eol - bol),\n+\t\t\t\t\tbol, sha1_to_hex(newsha1));\n+\t\t\tif (!*eol)\n+\t\t\t\tbreak;\n+\t\t\tbol = eol + 1;\n+\t\t}\n+\t\tfclose(out);\n+\t\tunlink(rebase_path_rewritten_pending());\n+\t}\n+\tstrbuf_release(&buf);\n+}\n+\n+static void record_in_rewritten(struct object_id *oid,\n+\t\tenum todo_command next_command) {\n+\tFILE *out = fopen(rebase_path_rewritten_pending(), \"a\");\n+\n+\tif (!out)\n+\t\treturn;\n+\n+\tfprintf(out, \"%s\\n\", oid_to_hex(oid));\n+\tfclose(out);\n+\n+\tif (!is_fixup(next_command))\n+\t\tflush_rewritten_pending();\n+}\n+\n static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tstruct replay_opts *opts, int final_fixup)\n {\n@@ -1750,6 +1797,17 @@ static int is_final_fixup(struct todo_list *todo_list)\n \treturn 1;\n }\n \n+static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n+{\n+\tint i;\n+\n+\tfor (i = todo_list->current + offset; i < todo_list->nr; i++)\n+\t\tif (todo_list->items[i].command != TODO_NOOP)\n+\t\t\treturn todo_list->items[i].command;\n+\n+\treturn -1;\n+}\n+\n static const char *reflog_message(struct replay_opts *opts,\n \tconst char *sub_action, const char *fmt, ...)\n {\n@@ -1808,6 +1866,9 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\t\titem->arg, item->arg_len, opts, res,\n \t\t\t\t\t!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+\t\t\t\t\tpeek_command(todo_list, 1));\n \t\t\tif (res && is_fixup(item->command)) {\n \t\t\t\tif (res == 1)\n \t\t\t\t\tintend_to_amend();\n@@ -1837,6 +1898,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \n \tif (is_rebase_i(opts)) {\n \t\tstruct strbuf head_ref = STRBUF_INIT, buf = STRBUF_INIT;\n+\t\tstruct stat st;\n \n \t\t/* Stopped in the middle, as planned? */\n \t\tif (todo_list->current < todo_list->nr)\n@@ -1881,6 +1943,20 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\trun_command_v_opt(argv, RUN_GIT_CMD);\n \t\t\tstrbuf_reset(&buf);\n \t\t}\n+\t\tflush_rewritten_pending();\n+\t\tif (!stat(rebase_path_rewritten_list(), &st) &&\n+\t\t\t\tst.st_size > 0) {\n+\t\t\tstruct child_process child = CHILD_PROCESS_INIT;\n+\n+\t\t\tchild.in = open(rebase_path_rewritten_list(), O_RDONLY);\n+\t\t\tchild.git_cmd = 1;\n+\t\t\targv_array_push(&child.args, \"notes\");\n+\t\t\targv_array_push(&child.args, \"copy\");\n+\t\t\targv_array_push(&child.args, \"--for-rewrite=rebase\");\n+\t\t\t/* we don't care if this copying failed */\n+\t\t\trun_command(&child);\n+\t\t}\n+\n \t\tstrbuf_release(&buf);\n \t\tstrbuf_release(&head_ref);\n \t}\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307613","messageId":"e8ca098f5da19b540367b1d070169fc74d2e7863.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 23/34] sequencer (rebase -i): respect the rebase.autostash setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:31:43Z","receivedAt":"2016-12-13T15:33:02Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Git's `rebase` command inspects the `rebase.autostash` config setting\nto determine whether it should stash any uncommitted changes before\nrebasing and re-apply them afterwards.\n\nAs we introduce more bits and pieces to let the sequencer act as\ninteractive rebase's backend, here is the part that adds support for\nthe autostash feature.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 43 +++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 43 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex cafd945e83..c11eceb70b 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -111,6 +111,7 @@ static GIT_PATH_FUNC(rebase_path_orig_head, \"rebase-merge/orig-head\")\n static GIT_PATH_FUNC(rebase_path_verbose, \"rebase-merge/verbose\")\n static GIT_PATH_FUNC(rebase_path_head_name, \"rebase-merge/head-name\")\n static GIT_PATH_FUNC(rebase_path_onto, \"rebase-merge/onto\")\n+static GIT_PATH_FUNC(rebase_path_autostash, \"rebase-merge/autostash\")\n \n static inline int is_rebase_i(const struct replay_opts *opts)\n {\n@@ -1808,6 +1809,47 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \treturn -1;\n }\n \n+static int apply_autostash(struct replay_opts *opts)\n+{\n+\tstruct strbuf stash_sha1 = STRBUF_INIT;\n+\tstruct child_process child = CHILD_PROCESS_INIT;\n+\tint ret = 0;\n+\n+\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1)) {\n+\t\tstrbuf_release(&stash_sha1);\n+\t\treturn 0;\n+\t}\n+\tstrbuf_trim(&stash_sha1);\n+\n+\tchild.git_cmd = 1;\n+\targv_array_push(&child.args, \"stash\");\n+\targv_array_push(&child.args, \"apply\");\n+\targv_array_push(&child.args, stash_sha1.buf);\n+\tif (!run_command(&child))\n+\t\tprintf(_(\"Applied autostash.\"));\n+\telse {\n+\t\tstruct child_process store = CHILD_PROCESS_INIT;\n+\n+\t\tstore.git_cmd = 1;\n+\t\targv_array_push(&store.args, \"stash\");\n+\t\targv_array_push(&store.args, \"store\");\n+\t\targv_array_push(&store.args, \"-m\");\n+\t\targv_array_push(&store.args, \"autostash\");\n+\t\targv_array_push(&store.args, \"-q\");\n+\t\targv_array_push(&store.args, stash_sha1.buf);\n+\t\tif (run_command(&store))\n+\t\t\tret = error(_(\"cannot store %s\"), stash_sha1.buf);\n+\t\telse\n+\t\t\tprintf(_(\"Applying autostash resulted in conflicts.\\n\"\n+\t\t\t\t\"Your changes are safe in the stash.\\n\"\n+\t\t\t\t\"You can run \\\"git stash pop\\\" or\"\n+\t\t\t\t\" \\\"git stash drop\\\" at any time.\\n\"));\n+\t}\n+\n+\tstrbuf_release(&stash_sha1);\n+\treturn ret;\n+}\n+\n static const char *reflog_message(struct replay_opts *opts,\n \tconst char *sub_action, const char *fmt, ...)\n {\n@@ -1970,6 +2012,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\trun_command(&hook);\n \t\t\t}\n \t\t}\n+\t\tapply_autostash(opts);\n \n \t\tstrbuf_release(&buf);\n \t\tstrbuf_release(&head_ref);\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307614","messageId":"431b268b4828c6c1c5fc9bbadb38ad5f45aa9304.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 22/34] sequencer (rebase -i): run the post-rewrite hook, if needed","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:31:39Z","receivedAt":"2016-12-13T15:33:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 14 ++++++++++++++\n 1 file changed, 14 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 0233999389..cafd945e83 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1947,6 +1947,8 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tif (!stat(rebase_path_rewritten_list(), &st) &&\n \t\t\t\tst.st_size > 0) {\n \t\t\tstruct child_process child = CHILD_PROCESS_INIT;\n+\t\t\tconst char *post_rewrite_hook =\n+\t\t\t\tfind_hook(\"post-rewrite\");\n \n \t\t\tchild.in = open(rebase_path_rewritten_list(), O_RDONLY);\n \t\t\tchild.git_cmd = 1;\n@@ -1955,6 +1957,18 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\targv_array_push(&child.args, \"--for-rewrite=rebase\");\n \t\t\t/* we don't care if this copying failed */\n \t\t\trun_command(&child);\n+\n+\t\t\tif (post_rewrite_hook) {\n+\t\t\t\tstruct child_process hook = CHILD_PROCESS_INIT;\n+\n+\t\t\t\thook.in = open(rebase_path_rewritten_list(),\n+\t\t\t\t\tO_RDONLY);\n+\t\t\t\thook.stdout_to_stderr = 1;\n+\t\t\t\targv_array_push(&hook.args, post_rewrite_hook);\n+\t\t\t\targv_array_push(&hook.args, \"rebase\");\n+\t\t\t\t/* we don't care if this hook failed */\n+\t\t\t\trun_command(&hook);\n+\t\t\t}\n \t\t}\n \n \t\tstrbuf_release(&buf);\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307615","messageId":"13e2907524d24f5ab393d0203b50dbcca19e7728.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 19/34] sequencer (rebase -i): set the reflog message consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:31:25Z","receivedAt":"2016-12-13T15:33:05Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We already used the same reflog message as the scripted version of rebase\n-i when finishing. With this commit, we do that also for all the commands\nbefore that.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 4 ++++\n 1 file changed, 4 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex d20efad562..118696f6d3 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1792,6 +1792,10 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\tunlink(rebase_path_amend());\n \t\t}\n \t\tif (item->command <= TODO_SQUASH) {\n+\t\t\tif (is_rebase_i(opts))\n+\t\t\t\tsetenv(\"GIT_REFLOG_ACTION\", reflog_message(opts,\n+\t\t\t\t\tcommand_to_string(item->command), NULL),\n+\t\t\t\t\t1);\n \t\t\tres = do_pick_commit(item->command, item->commit,\n \t\t\t\t\topts, is_final_fixup(todo_list));\n \t\t\tif (item->command == TODO_EDIT) {\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307616","messageId":"db529051453b818c36de71bf35f6dfa6ffba8c3d.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 21/34] sequencer (rebase -i): record interrupted commits in rewritten, too","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:31:35Z","receivedAt":"2016-12-13T15:33:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When continuing after a `pick` command failed, we want that commit\nto show up in the rewritten-list (and its notes to be rewritten), too.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 9 +++++++++\n 1 file changed, 9 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 7ab533abd9..0233999389 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2052,6 +2052,15 @@ int sequencer_continue(struct replay_opts *opts)\n \t\t}\n \t\ttodo_list.current++;\n \t}\n+\telse if (file_exists(rebase_path_stopped_sha())) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tstruct object_id oid;\n+\n+\t\tif (read_oneliner(&buf, rebase_path_stopped_sha(), 1) &&\n+\t\t    !get_sha1_committish(buf.buf, oid.hash))\n+\t\t\trecord_in_rewritten(&oid, peek_command(&todo_list, 0));\n+\t\tstrbuf_release(&buf);\n+\t}\n \n \tres = pick_commits(&todo_list, opts);\n release_todo_list:\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307617","messageId":"6f7ec89ac91ba1aa860a28c27ab93f60099f1ddb.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 18/34] sequencer (rebase -i): refactor setting the reflog message","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:31:22Z","receivedAt":"2016-12-13T15:33:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This makes the code DRYer, with the obvious benefit that we can enhance\nthe code further in a single place.\n\nWe can also reuse the functionality elsewhere by calling this new\nfunction.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 33 ++++++++++++++++++++++++++-------\n 1 file changed, 26 insertions(+), 7 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 33fb36dcbe..d20efad562 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1750,6 +1750,26 @@ static int is_final_fixup(struct todo_list *todo_list)\n \treturn 1;\n }\n \n+static const char *reflog_message(struct replay_opts *opts,\n+\tconst char *sub_action, const char *fmt, ...)\n+{\n+\tva_list ap;\n+\tstatic struct strbuf buf = STRBUF_INIT;\n+\n+\tva_start(ap, fmt);\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addstr(&buf, action_name(opts));\n+\tif (sub_action)\n+\t\tstrbuf_addf(&buf, \" (%s)\", sub_action);\n+\tif (fmt) {\n+\t\tstrbuf_addstr(&buf, \": \");\n+\t\tstrbuf_vaddf(&buf, fmt, ap);\n+\t}\n+\tva_end(ap);\n+\n+\treturn buf.buf;\n+}\n+\n static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n {\n \tint res = 0;\n@@ -1820,6 +1840,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \n \t\tif (read_oneliner(&head_ref, rebase_path_head_name(), 0) &&\n \t\t\t\tstarts_with(head_ref.buf, \"refs/\")) {\n+\t\t\tconst char *msg;\n \t\t\tunsigned char head[20], orig[20];\n \n \t\t\tif (get_sha1(\"HEAD\", head))\n@@ -1827,19 +1848,17 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0) ||\n \t\t\t\t\tget_sha1_hex(buf.buf, orig))\n \t\t\t\treturn error(_(\"could not read orig-head\"));\n-\t\t\tstrbuf_addf(&buf, \"rebase -i (finish): %s onto \",\n-\t\t\t\thead_ref.buf);\n \t\t\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n \t\t\t\treturn error(_(\"could not read 'onto'\"));\n-\t\t\tif (update_ref(buf.buf, head_ref.buf, head, orig,\n+\t\t\tmsg = reflog_message(opts, \"finish\", \"%s onto %s\",\n+\t\t\t\thead_ref.buf, buf.buf);\n+\t\t\tif (update_ref(msg, head_ref.buf, head, orig,\n \t\t\t\t\tREF_NODEREF, UPDATE_REFS_MSG_ON_ERR))\n \t\t\t\treturn error(_(\"could not update %s\"),\n \t\t\t\t\thead_ref.buf);\n-\t\t\tstrbuf_reset(&buf);\n-\t\t\tstrbuf_addf(&buf,\n-\t\t\t\t\"rebase -i (finish): returning to %s\",\n+\t\t\tmsg = reflog_message(opts, \"finish\", \"returning to %s\",\n \t\t\t\thead_ref.buf);\n-\t\t\tif (create_symref(\"HEAD\", head_ref.buf, buf.buf))\n+\t\t\tif (create_symref(\"HEAD\", head_ref.buf, msg))\n \t\t\t\treturn error(_(\"could not update HEAD to %s\"),\n \t\t\t\t\thead_ref.buf);\n \t\t\tstrbuf_reset(&buf);\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307618","messageId":"c629351aababf2188672ae036ecbde4d9631e908.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 17/34] sequencer (rebase -i): allow fast-forwarding for edit/reword","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:31:19Z","receivedAt":"2016-12-13T15:33:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The sequencer already knew how to fast-forward instead of\ncherry-picking, if possible.\n\nWe want to continue to do this, of course, but in case of the 'reword'\ncommand, we will need to call `git commit` after fast-forwarding.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 25 +++++++++++++++++--------\n 1 file changed, 17 insertions(+), 8 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 5a9972fec3..33fb36dcbe 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -893,7 +893,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \tconst char *base_label, *next_label;\n \tstruct commit_message msg = { NULL, NULL, NULL, NULL };\n \tstruct strbuf msgbuf = STRBUF_INIT;\n-\tint res, unborn = 0, amend = 0, allow;\n+\tint res, unborn = 0, amend = 0, allow = 0;\n \n \tif (opts->no_commit) {\n \t\t/*\n@@ -939,11 +939,23 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \telse\n \t\tparent = commit->parents->item;\n \n+\tif (get_message(commit, &msg) != 0)\n+\t\treturn error(_(\"cannot get commit message for %s\"),\n+\t\t\toid_to_hex(&commit->object.oid));\n+\n \tif (opts->allow_ff && !is_fixup(command) &&\n \t    ((parent && !hashcmp(parent->object.oid.hash, head)) ||\n-\t     (!parent && unborn)))\n-\t\treturn fast_forward_to(commit->object.oid.hash, head, unborn, opts);\n-\n+\t     (!parent && unborn))) {\n+\t\tif (is_rebase_i(opts))\n+\t\t\twrite_author_script(msg.message);\n+\t\tres = fast_forward_to(commit->object.oid.hash, head, unborn,\n+\t\t\topts);\n+\t\tif (res || command != TODO_REWORD)\n+\t\t\tgoto leave;\n+\t\tedit = amend = 1;\n+\t\tmsg_file = NULL;\n+\t\tgoto fast_forward_edit;\n+\t}\n \tif (parent && parse_commit(parent) < 0)\n \t\t/* TRANSLATORS: The first %s will be a \"todo\" command like\n \t\t   \"revert\" or \"pick\", the second %s a SHA1. */\n@@ -951,10 +963,6 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t\tcommand_to_string(command),\n \t\t\toid_to_hex(&parent->object.oid));\n \n-\tif (get_message(commit, &msg) != 0)\n-\t\treturn error(_(\"cannot get commit message for %s\"),\n-\t\t\toid_to_hex(&commit->object.oid));\n-\n \t/*\n \t * \"commit\" is an existing commit.  We would want to apply\n \t * the difference it introduces since its first parent \"prev\"\n@@ -1084,6 +1092,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tgoto leave;\n \t}\n \tif (!opts->no_commit)\n+fast_forward_edit:\n \t\tres = run_git_commit(msg_file, opts, allow, edit, amend,\n \t\t\t\t     cleanup_commit_message);\n \n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307619","messageId":"61b34aad6bd063957afc974f3f484cfaf24a3213.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 16/34] sequencer (rebase -i): implement the 'reword' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:31:15Z","receivedAt":"2016-12-13T15:33:14Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is now trivial, as all the building blocks are in place: all we need\nto do is to flip the \"edit\" switch when committing.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 9 +++++++--\n 1 file changed, 7 insertions(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 4361fe0e94..5a9972fec3 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -751,6 +751,7 @@ enum todo_command {\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@@ -766,6 +767,7 @@ static struct {\n \t{ 'p', \"pick\" },\n \t{ 0,   \"revert\" },\n \t{ 'e', \"edit\" },\n+\t{ 'r', \"reword\" },\n \t{ 'f', \"fixup\" },\n \t{ 's', \"squash\" },\n \t{ 'x', \"exec\" },\n@@ -1001,7 +1003,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t}\n \t}\n \n-\tif (is_fixup(command)) {\n+\tif (command == TODO_REWORD)\n+\t\tedit = 1;\n+\telse if (is_fixup(command)) {\n \t\tif (update_squash_messages(command, commit, opts))\n \t\t\treturn -1;\n \t\tamend = 1;\n@@ -1779,7 +1783,8 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t}\n \t\t\telse if (res && is_rebase_i(opts))\n \t\t\t\treturn res | error_with_patch(item->commit,\n-\t\t\t\t\titem->arg, item->arg_len, opts, res, 0);\n+\t\t\t\t\titem->arg, item->arg_len, opts, res,\n+\t\t\t\t\titem->command == TODO_REWORD);\n \t\t}\n \t\telse if (item->command == TODO_EXEC) {\n \t\t\tchar *end_of_arg = (char *)(item->arg + item->arg_len);\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307620","messageId":"9e48dffa8e58183debb79f29413afa81af174475.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 32/34] sequencer (rebase -i): show the progress","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:32:44Z","receivedAt":"2016-12-13T15:33:54Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The interactive rebase keeps the user informed about its progress.\nIf the sequencer wants to do the grunt work of the interactive\nrebase, it also needs to show that progress.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 32 ++++++++++++++++++++++++++++++++\n 1 file changed, 32 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 498dd028d1..35d5ef4ef6 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1221,6 +1221,7 @@ struct todo_list {\n \tstruct strbuf buf;\n \tstruct todo_item *items;\n \tint nr, alloc, current;\n+\tint done_nr, total_nr;\n };\n \n #define TODO_LIST_INIT { STRBUF_INIT }\n@@ -1338,6 +1339,17 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n \treturn res;\n }\n \n+static int count_commands(struct todo_list *todo_list)\n+{\n+\tint count = 0, i;\n+\n+\tfor (i = 0; i < todo_list->nr; i++)\n+\t\tif (todo_list->items[i].command != TODO_COMMENT)\n+\t\t\tcount++;\n+\n+\treturn count;\n+}\n+\n static int read_populate_todo(struct todo_list *todo_list,\n \t\t\tstruct replay_opts *opts)\n {\n@@ -1380,6 +1392,21 @@ static int read_populate_todo(struct todo_list *todo_list,\n \t\t\t\treturn error(_(\"cannot revert during a cherry-pick.\"));\n \t}\n \n+\tif (is_rebase_i(opts)) {\n+\t\tstruct todo_list done = TODO_LIST_INIT;\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\ttodo_list->done_nr = count_commands(&done);\n+\t\telse\n+\t\t\ttodo_list->done_nr = 0;\n+\n+\t\ttodo_list->total_nr = todo_list->done_nr\n+\t\t\t+ count_commands(todo_list);\n+\n+\t\ttodo_list_release(&done);\n+\t}\n+\n \treturn 0;\n }\n \n@@ -1928,6 +1955,11 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n \t\tif (is_rebase_i(opts)) {\n+\t\t\tif (item->command != TODO_COMMENT)\n+\t\t\t\tfprintf(stderr, \"Rebasing (%d/%d)%s\",\n+\t\t\t\t\t++(todo_list->done_nr),\n+\t\t\t\t\ttodo_list->total_nr,\n+\t\t\t\t\topts->verbose ? \"\\n\" : \"\\r\");\n \t\t\tunlink(rebase_path_message());\n \t\t\tunlink(rebase_path_author_script());\n \t\t\tunlink(rebase_path_stopped_sha());\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307621","messageId":"546a6947e7a864a9a950ca4d5e4a87a4b0e8ea76.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 33/34] sequencer (rebase -i): write the progress into files","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:32:49Z","receivedAt":"2016-12-13T15:33:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"For the benefit of e.g. the shell prompt, the interactive rebase not\nonly displays the progress for the user to see, but also writes it into\nthe msgnum/end files in the state directory.\n\nTeach the sequencer this new trick.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 30 +++++++++++++++++++++++++++---\n 1 file changed, 27 insertions(+), 3 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 35d5ef4ef6..cb5e7f35fc 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -45,6 +45,16 @@ static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n  */\n static GIT_PATH_FUNC(rebase_path_done, \"rebase-merge/done\")\n /*\n+ * The file to keep track of how many commands were already processed (e.g.\n+ * for the prompt).\n+ */\n+static GIT_PATH_FUNC(rebase_path_msgnum, \"rebase-merge/msgnum\");\n+/*\n+ * The file to keep track of how many commands are to be processed in total\n+ * (e.g. for the prompt).\n+ */\n+static GIT_PATH_FUNC(rebase_path_msgtotal, \"rebase-merge/end\");\n+/*\n  * The commit message that is planned to be used for any changes that\n  * need to be committed following a user interaction.\n  */\n@@ -1394,6 +1404,7 @@ static int read_populate_todo(struct todo_list *todo_list,\n \n \tif (is_rebase_i(opts)) {\n \t\tstruct todo_list done = TODO_LIST_INIT;\n+\t\tFILE *f = fopen(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@@ -1403,8 +1414,12 @@ static int read_populate_todo(struct todo_list *todo_list,\n \n \t\ttodo_list->total_nr = todo_list->done_nr\n \t\t\t+ count_commands(todo_list);\n-\n \t\ttodo_list_release(&done);\n+\n+\t\tif (f) {\n+\t\t\tfprintf(f, \"%d\\n\", todo_list->total_nr);\n+\t\t\tfclose(f);\n+\t\t}\n \t}\n \n \treturn 0;\n@@ -1955,11 +1970,20 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n \t\tif (is_rebase_i(opts)) {\n-\t\t\tif (item->command != TODO_COMMENT)\n+\t\t\tif (item->command != TODO_COMMENT) {\n+\t\t\t\tFILE *f = fopen(rebase_path_msgnum(), \"w\");\n+\n+\t\t\t\ttodo_list->done_nr++;\n+\n+\t\t\t\tif (f) {\n+\t\t\t\t\tfprintf(f, \"%d\\n\", todo_list->done_nr);\n+\t\t\t\t\tfclose(f);\n+\t\t\t\t}\n \t\t\t\tfprintf(stderr, \"Rebasing (%d/%d)%s\",\n-\t\t\t\t\t++(todo_list->done_nr),\n+\t\t\t\t\ttodo_list->done_nr,\n \t\t\t\t\ttodo_list->total_nr,\n \t\t\t\t\topts->verbose ? \"\\n\" : \"\\r\");\n+\t\t\t}\n \t\t\tunlink(rebase_path_message());\n \t\t\tunlink(rebase_path_author_script());\n \t\t\tunlink(rebase_path_stopped_sha());\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307622","messageId":"28fc8e98c9a3cd6ee7afc117925ae483d89a90ec.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 34/34] sequencer (rebase -i): write out the final message","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:32:59Z","receivedAt":"2016-12-13T15:34:01Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The shell script version of the interactive rebase has a very specific\nfinal message. Teach the sequencer to print the same.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 3 +++\n 1 file changed, 3 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex cb5e7f35fc..41be4cde16 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2118,6 +2118,9 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t}\n \t\tapply_autostash(opts);\n \n+\t\tfprintf(stderr, \"Successfully rebased and updated %s.\\n\",\n+\t\t\thead_ref.buf);\n+\n \t\tstrbuf_release(&buf);\n \t\tstrbuf_release(&head_ref);\n \t}\n-- \n2.11.0.rc3.windows.1\n"},{"id":"307623","messageId":"eb4cfa652ec89944a7dfe3e871f28926dd4f606e.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 31/34] sequencer (rebase -i): suggest --edit-todo upon unknown command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:32:40Z","receivedAt":"2016-12-13T15:34:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is the same behavior as known from `git rebase -i`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex edc213a2c8..498dd028d1 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1355,8 +1355,12 @@ static int read_populate_todo(struct todo_list *todo_list,\n \tclose(fd);\n \n \tres = parse_insn_buffer(todo_list->buf.buf, todo_list);\n-\tif (res)\n+\tif (res) {\n+\t\tif (is_rebase_i(opts))\n+\t\t\treturn error(_(\"please fix this using \"\n+\t\t\t\t       \"'git rebase --edit-todo'.\"));\n \t\treturn error(_(\"unusable instruction sheet: '%s'\"), todo_file);\n+\t}\n \n \tif (!todo_list->nr &&\n \t    (!is_rebase_i(opts) || !file_exists(rebase_path_done())))\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307624","messageId":"cover.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1472633606.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 00/34] Teach the sequencer to act as rebase -i's backend","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:29:06Z","receivedAt":"2016-12-13T15:36:17Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This marks the count down to '3': two more patch series after this\n(really tiny ones) and we have a faster rebase -i.\n\nThe idea of this patch series is to teach the sequencer to understand\nall of the commands in `git-rebase-todo` scripts, to execute them and to\nbehave pretty much very the same as `git rebase -i --continue` when\ncalled with the newly-introduced REPLAY_INTERACTIVE_REBASE mode.\n\nMost of these patches should be pretty much straight-forward. When not,\nI tried to make a point of describing enough background in the commit\nmessage. Please feel free to point out where my explanations fall short.\n\nNote that even after this patch series is applied, rebase -i is still\nunaffected. It will require the next patch series which introduces the\nrebase--helper that essentially implements `git rebase -i --continue` by\ncalling the sequencer with the appropriate options.\n\nThe final patch series will move a couple of pre- and post-processing\nsteps into the rebase--helper/sequencer (such as expanding/shrinking the\nSHA-1s, reordering the fixup!/squash! lines, etc). This might sound like\na mere add-on, but it is essential for the speed improvements: those\nstupid little processing steps really dominated the execution time in my\ntests.\n\nApart from mostly cosmetic patches (and the occasional odd bug that I\nfixed promptly), I used these patches since mid May to perform all of my\ninteractive rebases. In mid June, I had the idea to teach rebase -i to\nrun *both* scripted rebase and rebase--helper and to cross-validate the\nresults. This slowed down all my interactive rebases since, but helped\nme catch three rather obscure bugs (e.g. that git commit --fixup unfolds\nlong onelines and rebase -i still finds the correct original commit).\n\nThis is all only to say that I am rather confident that the current code\ndoes the job.\n\nSince sending out v1, I integrated all of these patch series\ninto Git for Windows v2.10.0, where they have been live ever since, and\nused by myself (also in a Linux VM, as Git for Windows' master branch\nalways builds also on Linux and passes the test suite, too).\n\nJust to reiterate why I do all this: it speeds up the interactive rebase\nsubstantially. Even with a not yet fully builtin rebase -i, but just the\npart after the user edited the `git-rebase-todo` script.\n\nThe performance test I introduced to demonstrate this (p3404) shows a\nspeed-up of +380% here (i.e. roughly 5x), from ~8.8 seconds to ~1.8\nseconds. This is on Windows, where the performance impact of avoiding\nshell scripting is most noticable.\n\nOn MacOSX and on Linux, the speed-up is less pronounced, but still\nnoticable, at least if you trust Travis CI, which I abused to perform\nthat test for me. Check for yourself (searching for \"3404.2\") here:\nhttps://travis-ci.org/git/git/builds/156295227. According to those logs,\np3404 is speeded up from ~0.45 seconds to ~0.12 seconds on Linux (read:\nabout 3.5x) and from ~1.7 seconds to ~0.5 seconds on MacOSX (read:\nalmost 4x).\n\nPlease note that the interdiff vs v1 is only of limited use: too many\nthings changed in the meantime, in particular the prepare-sequencer\nbranch that went through a couple of iterations before it found its way\ninto git.git's master branch. So please take the interdiff with a\nmountain range of salt.\n\nChanges since v1:\n\n- some grammar touch-ups.\n\n- simplified determining the command string in\n  walk_revs_populate_todo().\n\n- removed the beautiful ordinal logic (to print out \"1st\", \"2nd\", \"3rd\"\n  etc) and made things consistent with the current `rebase -i`.\n\n- while at it, marked more messages for translation.\n\n- added code-comments to clarify the order, and the sections, of the\n  todo_command enum.\n\n- replaced one error(..., strerror(...)) to an error_errno(...).\n\n- downcased error messages\n\n- marked error messages for translation\n\n- adjusted the patches to account for sequencer_entrust() having been\n  removed from the prepare-sequencer patch series by request of Junio.\n\n- moved the introduction of write_message_gently() into the patch\n  introducing its first usage, i.e. the support for the 'edit' command.\n\n- adjusted some indentations\n\n- prevented an write_in_full() from being called after a failed open()\n\n- inserted a few forgotten strbuf_release() calls\n\n\nJohannes Schindelin (34):\n  sequencer: support a new action: 'interactive rebase'\n  sequencer (rebase -i): implement the 'noop' command\n  sequencer (rebase -i): implement the 'edit' command\n  sequencer (rebase -i): implement the 'exec' command\n  sequencer (rebase -i): learn about the 'verbose' mode\n  sequencer (rebase -i): write the 'done' file\n  sequencer (rebase -i): add support for the 'fixup' and 'squash'\n    commands\n  sequencer (rebase -i): implement the short commands\n  sequencer (rebase -i): write an author-script file\n  sequencer (rebase -i): allow continuing with staged changes\n  sequencer (rebase -i): remove CHERRY_PICK_HEAD when no longer needed\n  sequencer (rebase -i): skip some revert/cherry-pick specific code path\n  sequencer (rebase -i): the todo can be empty when continuing\n  sequencer (rebase -i): update refs after a successful rebase\n  sequencer (rebase -i): leave a patch upon error\n  sequencer (rebase -i): implement the 'reword' command\n  sequencer (rebase -i): allow fast-forwarding for edit/reword\n  sequencer (rebase -i): refactor setting the reflog message\n  sequencer (rebase -i): set the reflog message consistently\n  sequencer (rebase -i): copy commit notes at end\n  sequencer (rebase -i): record interrupted commits in rewritten, too\n  sequencer (rebase -i): run the post-rewrite hook, if needed\n  sequencer (rebase -i): respect the rebase.autostash setting\n  sequencer (rebase -i): respect strategy/strategy_opts settings\n  sequencer (rebase -i): allow rescheduling commands\n  sequencer (rebase -i): implement the 'drop' command\n  sequencer (rebase -i): differentiate between comments and 'noop'\n  run_command_opt(): optionally hide stderr when the command succeeds\n  sequencer (rebase -i): show only failed `git commit`'s output\n  sequencer (rebase -i): show only failed cherry-picks' output\n  sequencer (rebase -i): suggest --edit-todo upon unknown command\n  sequencer (rebase -i): show the progress\n  sequencer (rebase -i): write the progress into files\n  sequencer (rebase -i): write out the final message\n\n run-command.c |   23 ++\n run-command.h |    1 +\n sequencer.c   | 1003 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---\n sequencer.h   |    4 +-\n 4 files changed, 983 insertions(+), 48 deletions(-)\n\n\nbase-commit: 8d7a455ed52e2a96debc080dfc011b6bb00db5d2\nPublished-As: https://github.com/dscho/git/releases/tag/sequencer-i-v2\nFetch-It-Via: git fetch https://github.com/dscho/git sequencer-i-v2\n\nInterdiff vs v1:\n\n diff --git a/sequencer.c b/sequencer.c\n index 6ca9d1e09d..41be4cde16 100644\n --- a/sequencer.c\n +++ b/sequencer.c\n @@ -125,7 +125,6 @@ static GIT_PATH_FUNC(rebase_path_autostash, \"rebase-merge/autostash\")\n  static GIT_PATH_FUNC(rebase_path_strategy, \"rebase-merge/strategy\")\n  static GIT_PATH_FUNC(rebase_path_strategy_opts, \"rebase-merge/strategy_opts\")\n  \n -/* We will introduce the 'interactive rebase' mode later */\n  static inline int is_rebase_i(const struct replay_opts *opts)\n  {\n  \treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n @@ -259,13 +258,13 @@ static const char *action_name(const struct replay_opts *opts)\n  {\n  \tswitch (opts->action) {\n  \tcase REPLAY_REVERT:\n -\t\treturn \"revert\";\n +\t\treturn N_(\"revert\");\n  \tcase REPLAY_PICK:\n -\t\treturn \"cherry-pick\";\n +\t\treturn N_(\"cherry-pick\");\n  \tcase REPLAY_INTERACTIVE_REBASE:\n -\t\treturn \"rebase -i\";\n +\t\treturn N_(\"rebase -i\");\n  \t}\n -\tdie(\"Unknown action: %d\", opts->action);\n +\tdie(_(\"Unknown action: %d\"), opts->action);\n  }\n  \n  struct commit_message {\n @@ -548,6 +547,7 @@ static int write_author_script(const char *message)\n  {\n  \tstruct strbuf buf = STRBUF_INIT;\n  \tconst char *eol;\n +\tint res;\n  \n  \tfor (;;)\n  \t\tif (!*message || starts_with(message, \"\\n\")) {\n @@ -585,8 +585,9 @@ static int write_author_script(const char *message)\n  \t\t\tstrbuf_addch(&buf, *(message++));\n  \t\telse\n  \t\t\tstrbuf_addf(&buf, \"'\\\\\\\\%c'\", *(message++));\n -\tstrbuf_addstr(&buf, \"'\\n\");\n -\treturn write_message(&buf, rebase_path_author_script());\n +\tres = write_message(buf.buf, buf.len, rebase_path_author_script(), 1);\n +\tstrbuf_release(&buf);\n +\treturn res;\n  }\n  \n  /*\n @@ -771,16 +772,25 @@ 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 +\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 @@ -812,47 +822,34 @@ static int is_fixup(enum todo_command command)\n  \treturn command == TODO_FIXUP || command == TODO_SQUASH;\n  }\n  \n -static const char *nth_for_number(int n)\n -{\n -\tint n1 = n % 10, n10 = n % 100;\n -\n -\tif (n1 == 1 && n10 != 11)\n -\t\treturn \"st\";\n -\tif (n1 == 2 && n10 != 12)\n -\t\treturn \"nd\";\n -\tif (n1 == 3 && n10 != 13)\n -\t\treturn \"rd\";\n -\treturn \"th\";\n -}\n -\n  static int update_squash_messages(enum todo_command command,\n  \t\tstruct commit *commit, struct replay_opts *opts)\n  {\n  \tstruct strbuf buf = STRBUF_INIT;\n -\tint count;\n +\tint count, res;\n  \tconst char *message, *body;\n  \n  \tif (file_exists(rebase_path_squash_msg())) {\n  \t\tchar *p, *p2;\n  \n  \t\tif (strbuf_read_file(&buf, rebase_path_squash_msg(), 2048) <= 0)\n -\t\t\treturn error(\"Could not read %s\",\n +\t\t\treturn error(_(\"could not read '%s'\"),\n  \t\t\t\trebase_path_squash_msg());\n  \n -\t\tif (buf.buf[0] == '\\n' || !skip_prefix(buf.buf + 1,\n -\t\t\t\t\" This is a combination of \",\n -\t\t\t\t(const char **)&p))\n -\t\t\treturn error(\"Unexpected 1st line of squash message:\\n\"\n -\t\t\t\t\"\\n\\t%.*s\",\n -\t\t\t\t(int)(strchrnul(buf.buf, '\\n') - buf.buf),\n -\t\t\t\tbuf.buf);\n +\t\tif (buf.buf[0] != comment_line_char ||\n +\t\t    !skip_prefix(buf.buf + 1, \" This is a combination of \",\n +\t\t\t\t (const char **)&p))\n +\t\t\treturn error(_(\"unexpected 1st line of squash message:\"\n +\t\t\t\t       \"\\n\\n\\t%.*s\"),\n +\t\t\t\t     (int)(strchrnul(buf.buf, '\\n') - buf.buf),\n +\t\t\t\t     buf.buf);\n  \t\tcount = strtol(p, &p2, 10);\n  \n  \t\tif (count < 1 || *p2 != ' ')\n -\t\t\treturn error(\"Invalid 1st line of squash message:\\n\"\n -\t\t\t\t\"\\n\\t%.*s\",\n -\t\t\t\t(int)(strchrnul(buf.buf, '\\n') - buf.buf),\n -\t\t\t\tbuf.buf);\n +\t\t\treturn error(_(\"invalid 1st line of squash message:\\n\"\n +\t\t\t\t       \"\\n\\t%.*s\"),\n +\t\t\t\t     (int)(strchrnul(buf.buf, '\\n') - buf.buf),\n +\t\t\t\t     buf.buf);\n  \n  \t\tsprintf((char *)p, \"%d\", ++count);\n  \t\tif (!*p2)\n @@ -868,32 +865,33 @@ static int update_squash_messages(enum todo_command command,\n  \t\tconst char *head_message, *body;\n  \n  \t\tif (get_sha1(\"HEAD\", head))\n -\t\t\treturn error(\"Need a HEAD to fixup\");\n +\t\t\treturn error(_(\"need a HEAD to fixup\"));\n  \t\tif (!(head_commit = lookup_commit_reference(head)))\n -\t\t\treturn error(\"Could not read HEAD\");\n +\t\t\treturn error(_(\"could not read HEAD\"));\n  \t\tif (!(head_message = get_commit_buffer(head_commit, NULL)))\n -\t\t\treturn error(\"Could not read HEAD's commit message\");\n +\t\t\treturn error(_(\"could not read HEAD's commit message\"));\n  \n  \t\tbody = strstr(head_message, \"\\n\\n\");\n  \t\tif (!body)\n  \t\t\tbody = \"\";\n  \t\telse\n  \t\t\tbody = skip_blank_lines(body + 2);\n -\t\tif (write_file_gently(rebase_path_fixup_msg(), body, 0))\n -\t\t\treturn error(\"Cannot write %s\",\n -\t\t\t\trebase_path_fixup_msg());\n +\t\tif (write_message(body, strlen(body),\n +\t\t\t\t  rebase_path_fixup_msg(), 0))\n +\t\t\treturn error(_(\"cannot write '%s'\"),\n +\t\t\t\t     rebase_path_fixup_msg());\n  \n  \t\tcount = 2;\n -\t\tstrbuf_addf(&buf, \"%c This is a combination of 2 commits.\\n\"\n -\t\t\t\"%c The first commit's message is:\\n\\n%s\",\n -\t\t\tcomment_line_char, comment_line_char, body);\n +\t\tstrbuf_addf(&buf, _(\"%c This is a combination of 2 commits.\\n\"\n +\t\t\t\t    \"%c The first commit's message is:\\n\\n%s\"),\n +\t\t\t    comment_line_char, comment_line_char, body);\n  \n  \t\tunuse_commit_buffer(head_commit, head_message);\n  \t}\n  \n  \tif (!(message = get_commit_buffer(commit, NULL)))\n -\t\treturn error(\"Could not read commit message of %s\",\n -\t\t\toid_to_hex(&commit->object.oid));\n +\t\treturn error(_(\"could not read commit message of %s\"),\n +\t\t\t     oid_to_hex(&commit->object.oid));\n  \tbody = strstr(message, \"\\n\\n\");\n  \tif (!body)\n  \t\tbody = \"\";\n @@ -902,21 +900,23 @@ static int update_squash_messages(enum todo_command command,\n  \n  \tif (command == TODO_SQUASH) {\n  \t\tunlink(rebase_path_fixup_msg());\n -\t\tstrbuf_addf(&buf, \"\\n%c This is the %d%s commit message:\\n\\n%s\",\n -\t\t\tcomment_line_char,\n -\t\t\tcount, nth_for_number(count), body);\n +\t\tstrbuf_addf(&buf, _(\"\\n%c This is the commit message #%d:\\n\"\n +\t\t\t\t    \"\\n%s\"),\n +\t\t\t    comment_line_char, count, body);\n  \t}\n  \telse if (command == TODO_FIXUP) {\n -\t\tstrbuf_addf(&buf,\n -\t\t\t\"\\n%c The %d%s commit message will be skipped:\\n\\n\",\n -\t\t\tcomment_line_char, count, nth_for_number(count));\n +\t\tstrbuf_addf(&buf, _(\"\\n%c The commit message #%d \"\n +\t\t\t\t    \"will be skipped:\\n\\n\"),\n +\t\t\t    comment_line_char, count);\n  \t\tstrbuf_add_commented_lines(&buf, body, strlen(body));\n  \t}\n  \telse\n -\t\treturn error(\"Unknown command: %d\", command);\n +\t\treturn error(_(\"unknown command: %d\"), command);\n  \tunuse_commit_buffer(commit, message);\n  \n -\treturn write_message(&buf, rebase_path_squash_msg());\n +\tres = write_message(buf.buf, buf.len, rebase_path_squash_msg(), 0);\n +\tstrbuf_release(&buf);\n +\treturn res;\n  }\n  \n  static void flush_rewritten_pending(void) {\n @@ -940,6 +940,7 @@ static void flush_rewritten_pending(void) {\n  \t\tfclose(out);\n  \t\tunlink(rebase_path_rewritten_pending());\n  \t}\n +\tstrbuf_release(&buf);\n  }\n  \n  static void record_in_rewritten(struct object_id *oid,\n @@ -1013,7 +1014,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n  \t\tparent = commit->parents->item;\n  \n  \tif (get_message(commit, &msg) != 0)\n -\t\treturn error(_(\"Cannot get commit message for %s\"),\n +\t\treturn error(_(\"cannot get commit message for %s\"),\n  \t\t\toid_to_hex(&commit->object.oid));\n  \n  \tif (opts->allow_ff && !is_fixup(command) &&\n @@ -1021,7 +1022,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n  \t     (!parent && unborn))) {\n  \t\tif (is_rebase_i(opts))\n  \t\t\twrite_author_script(msg.message);\n -\t\tres |= fast_forward_to(commit->object.oid.hash, head, unborn,\n +\t\tres = fast_forward_to(commit->object.oid.hash, head, unborn,\n  \t\t\topts);\n  \t\tif (res || command != TODO_REWORD)\n  \t\t\tgoto leave;\n @@ -1100,8 +1101,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n  \t\t\tconst char *dest = git_path(\"SQUASH_MSG\");\n  \t\t\tunlink(dest);\n  \t\t\tif (copy_file(dest, rebase_path_squash_msg(), 0666))\n -\t\t\t\treturn error(\"Could not rename %s to \"\n -\t\t\t\t\t\"%s\", rebase_path_squash_msg(), dest);\n +\t\t\t\treturn error(_(\"could not rename '%s' to '%s'\"),\n +\t\t\t\t\t     rebase_path_squash_msg(), dest);\n  \t\t\tunlink(git_path(\"MERGE_MSG\"));\n  \t\t\tmsg_file = dest;\n  \t\t\tedit = 1;\n @@ -1109,7 +1110,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n  \t}\n  \n  \tif (is_rebase_i(opts) && write_author_script(msg.message) < 0)\n -\t\tres |= -1;\n +\t\tres = -1;\n  \telse if (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n  \t\tres = do_recursive_merge(base, next, base_label, next_label,\n  \t\t\t\t\t head, &msgbuf, opts);\n @@ -1166,8 +1167,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n  \t}\n  \tif (!opts->no_commit)\n  fast_forward_edit:\n -\t\tres |= sequencer_commit(msg_file, opts, allow, edit, amend,\n -\t\t\tcleanup_commit_message);\n +\t\tres = run_git_commit(msg_file, opts, allow, edit, amend,\n +\t\t\t\t     cleanup_commit_message);\n  \n  \tif (!res && final_fixup) {\n  \t\tunlink(rebase_path_fixup_msg());\n @@ -1317,7 +1318,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n  static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n  {\n  \tstruct todo_item *item;\n -\tchar *p = buf;\n +\tchar *p = buf, *next_p;\n  \tint i, res = 0, fixup_okay = file_exists(rebase_path_done());\n  \n  \tfor (i = 1; *p; i++, p = next_p) {\n @@ -1335,15 +1336,16 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n  \t\t\t\ti, (int)(eol - p), p);\n  \t\t\titem->command = TODO_NOOP;\n  \t\t}\n +\n  \t\tif (fixup_okay)\n  \t\t\t; /* do nothing */\n  \t\telse if (is_fixup(item->command))\n -\t\t\treturn error(\"Cannot '%s' without a previous commit\",\n +\t\t\treturn error(_(\"cannot '%s' without a previous commit\"),\n  \t\t\t\tcommand_to_string(item->command));\n  \t\telse if (item->command < TODO_NOOP)\n  \t\t\tfixup_okay = 1;\n -\t\tp = *eol ? eol + 1 : eol;\n  \t}\n +\n  \treturn res;\n  }\n  \n @@ -1377,13 +1379,14 @@ static int read_populate_todo(struct todo_list *todo_list,\n  \tres = 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 -\t\t\t\t\"'git rebase --edit-todo'.\");\n -\t\treturn error(_(\"Unusable instruction sheet: %s\"), todo_file);\n +\t\t\treturn error(_(\"please fix this using \"\n +\t\t\t\t       \"'git rebase --edit-todo'.\"));\n +\t\treturn error(_(\"unusable instruction sheet: '%s'\"), todo_file);\n  \t}\n +\n  \tif (!todo_list->nr &&\n  \t    (!is_rebase_i(opts) || !file_exists(rebase_path_done())))\n -\t\treturn error(_(\"No commits parsed.\"));\n +\t\treturn error(_(\"no commits parsed.\"));\n  \n  \tif (!is_rebase_i(opts)) {\n  \t\tenum todo_command valid =\n @@ -1408,10 +1411,10 @@ static int read_populate_todo(struct todo_list *todo_list,\n  \t\t\ttodo_list->done_nr = count_commands(&done);\n  \t\telse\n  \t\t\ttodo_list->done_nr = 0;\n -\t\ttodo_list_release(&done);\n  \n  \t\ttodo_list->total_nr = todo_list->done_nr\n  \t\t\t+ count_commands(todo_list);\n +\t\ttodo_list_release(&done);\n  \n  \t\tif (f) {\n  \t\t\tfprintf(f, \"%d\\n\", todo_list->total_nr);\n @@ -1467,6 +1470,26 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n  \treturn 0;\n  }\n  \n +static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)\n +{\n +\tint i;\n +\n +\tstrbuf_reset(buf);\n +\tif (!read_oneliner(buf, rebase_path_strategy(), 0))\n +\t\treturn;\n +\topts->strategy = strbuf_detach(buf, NULL);\n +\tif (!read_oneliner(buf, rebase_path_strategy_opts(), 0))\n +\t\treturn;\n +\n +\topts->xopts_nr = split_cmdline(buf->buf, (const char ***)&opts->xopts);\n +\tfor (i = 0; i < opts->xopts_nr; i++) {\n +\t\tconst char *arg = opts->xopts[i];\n +\n +\t\tskip_prefix(arg, \"--\", &arg);\n +\t\topts->xopts[i] = xstrdup(arg);\n +\t}\n +}\n +\n  static int read_populate_opts(struct replay_opts *opts)\n  {\n  \tif (is_rebase_i(opts)) {\n @@ -1480,30 +1503,12 @@ static int read_populate_opts(struct replay_opts *opts)\n  \t\t\t\topts->gpg_sign = xstrdup(buf.buf + 2);\n  \t\t\t}\n  \t\t}\n -\t\tstrbuf_release(&buf);\n  \n  \t\tif (file_exists(rebase_path_verbose()))\n  \t\t\topts->verbose = 1;\n  \n -\t\tif (read_oneliner(&buf, rebase_path_strategy(), 0)) {\n -\t\t\topts->strategy =\n -\t\t\t\tsequencer_entrust(opts,\n -\t\t\t\t\t\t  strbuf_detach(&buf, NULL));\n -\t\t\tif (read_oneliner(&buf,\n -\t\t\t\t\t  rebase_path_strategy_opts(), 0)) {\n -\t\t\t\tint i;\n -\t\t\t\topts->xopts_nr = split_cmdline(buf.buf,\n -\t\t\t\t\t&opts->xopts);\n -\t\t\t\tfor (i = 0; i < opts->xopts_nr; i++)\n -\t\t\t\t\tskip_prefix(opts->xopts[i], \"--\",\n -\t\t\t\t\t\t    &opts->xopts[i]);\n -\t\t\t\tif (opts->xopts_nr)\n -\t\t\t\t\tsequencer_entrust(opts,\n -\t\t\t\t\t\tstrbuf_detach(&buf, NULL));\n -\t\t\t\telse\n -\t\t\t\t\tstrbuf_release(&buf);\n -\t\t\t}\n -\t\t}\n +\t\tread_strategy_opts(opts, &buf);\n +\t\tstrbuf_release(&buf);\n  \n  \t\treturn 0;\n  \t}\n @@ -1527,7 +1532,7 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,\n  {\n  \tenum todo_command command = opts->action == REPLAY_PICK ?\n  \t\tTODO_PICK : TODO_REVERT;\n -\tconst char *command_string = todo_command_strings[command];\n +\tconst char *command_string = todo_command_info[command].str;\n  \tstruct commit *commit;\n  \n  \tif (prepare_revs(opts))\n @@ -1681,12 +1686,15 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)\n  \t\tint prev_offset = !next ? 0 :\n  \t\t\ttodo_list->items[next - 1].offset_in_buf;\n  \n -\t\tif (offset > prev_offset && write_in_full(fd,\n -\t\t\t\ttodo_list->buf.buf + prev_offset,\n -\t\t\t\toffset - prev_offset) < 0)\n -\t\t\treturn error(_(\"Could not write to %s (%s)\"),\n -\t\t\t\tdone_path, strerror(errno));\n -\t\tclose(fd);\n +\t\tif (fd >= 0 && offset > prev_offset &&\n +\t\t    write_in_full(fd, todo_list->buf.buf + prev_offset,\n +\t\t\t\t  offset - prev_offset) < 0) {\n +\t\t\tclose(fd);\n +\t\t\treturn error_errno(_(\"could not write to '%s'\"),\n +\t\t\t\t\t   done_path);\n +\t\t}\n +\t\tif (fd >= 0)\n +\t\t\tclose(fd);\n  \t}\n  \treturn 0;\n  }\n @@ -1730,11 +1738,11 @@ static int make_patch(struct commit *commit, struct replay_opts *opts)\n  {\n  \tstruct strbuf buf = STRBUF_INIT;\n  \tstruct rev_info log_tree_opt;\n -\tconst char *commit_buffer = get_commit_buffer(commit, NULL), *subject;\n +\tconst char *commit_buffer = get_commit_buffer(commit, NULL), *subject, *p;\n  \tint res = 0;\n  \n -\tif (write_file_gently(rebase_path_stopped_sha(),\n -\t\t\t      short_commit_name(commit), 1) < 0)\n +\tp = short_commit_name(commit);\n +\tif (write_message(p, strlen(p), rebase_path_stopped_sha(), 1) < 0)\n  \t\treturn -1;\n  \n  \tstrbuf_addf(&buf, \"%s/patch\", get_dir(opts));\n @@ -1748,7 +1756,7 @@ static int make_patch(struct commit *commit, struct replay_opts *opts)\n  \tlog_tree_opt.diffopt.file = fopen(buf.buf, \"w\");\n  \tlog_tree_opt.diffopt.use_color = GIT_COLOR_NEVER;\n  \tif (!log_tree_opt.diffopt.file)\n -\t\tres |= error_errno(\"could not open '%s'\", buf.buf);\n +\t\tres |= error_errno(_(\"could not open '%s'\"), buf.buf);\n  \telse {\n  \t\tres |= log_tree_commit(&log_tree_opt, commit);\n  \t\tfclose(log_tree_opt.diffopt.file);\n @@ -1758,7 +1766,7 @@ static int make_patch(struct commit *commit, struct replay_opts *opts)\n  \tstrbuf_addf(&buf, \"%s/message\", get_dir(opts));\n  \tif (!file_exists(buf.buf)) {\n  \t\tfind_commit_subject(commit_buffer, &subject);\n -\t\tres |= write_file_gently(buf.buf, subject, 1);\n +\t\tres |= write_message(subject, strlen(subject), buf.buf, 1);\n  \t\tunuse_commit_buffer(commit, commit_buffer);\n  \t}\n  \tstrbuf_release(&buf);\n @@ -1769,11 +1777,13 @@ static int make_patch(struct commit *commit, struct replay_opts *opts)\n  static int intend_to_amend(void)\n  {\n  \tunsigned char head[20];\n +\tchar *p;\n  \n  \tif (get_sha1(\"HEAD\", head))\n -\t\treturn error(\"Cannot read HEAD\");\n +\t\treturn error(_(\"cannot read HEAD\"));\n  \n -\treturn write_file_gently(rebase_path_amend(), sha1_to_hex(head), 1);\n +\tp = sha1_to_hex(head);\n +\treturn write_message(p, strlen(p), rebase_path_amend(), 1);\n  }\n  \n  static int error_with_patch(struct commit *commit,\n @@ -1806,13 +1816,13 @@ static int error_failed_squash(struct commit *commit,\n  \tstruct replay_opts *opts, int subject_len, const char *subject)\n  {\n  \tif (rename(rebase_path_squash_msg(), rebase_path_message()))\n -\t\treturn error(\"Could not rename %s to %s\",\n +\t\treturn error(_(\"could not rename '%s' to '%s'\"),\n  \t\t\trebase_path_squash_msg(), rebase_path_message());\n  \tunlink(rebase_path_fixup_msg());\n  \tunlink(git_path(\"MERGE_MSG\"));\n  \tif (copy_file(git_path(\"MERGE_MSG\"), rebase_path_message(), 0666))\n -\t\treturn error(\"Could not copy %s to %s\", rebase_path_message(),\n -\t\t\tgit_path(\"MERGE_MSG\"));\n +\t\treturn error(_(\"could not copy '%s' to '%s'\"),\n +\t\t\t     rebase_path_message(), git_path(\"MERGE_MSG\"));\n  \treturn error_with_patch(commit, subject, subject_len, opts, 1, 0);\n  }\n  \n @@ -1827,30 +1837,30 @@ static int do_exec(const char *command_line)\n  \n  \t/* force re-reading of the cache */\n  \tif (discard_cache() < 0 || read_cache() < 0)\n -\t\treturn error(_(\"Could not read index\"));\n +\t\treturn error(_(\"could not read index\"));\n  \n  \tdirty = require_clean_work_tree(\"rebase\", NULL, 1, 1);\n  \n  \tif (status) {\n -\t\twarning(\"Execution failed: %s\\n%s\"\n -\t\t\t\"You can fix the problem, and then run\\n\"\n -\t\t\t\"\\n\"\n -\t\t\t\"  git rebase --continue\\n\"\n -\t\t\t\"\\n\",\n +\t\twarning(_(\"execution failed: %s\\n%s\"\n +\t\t\t  \"You can fix the problem, and then run\\n\"\n +\t\t\t  \"\\n\"\n +\t\t\t  \"  git rebase --continue\\n\"\n +\t\t\t  \"\\n\"),\n  \t\t\tcommand_line,\n -\t\t\tdirty ? \"and made changes to the index and/or the \"\n -\t\t\t\t\"working tree\\n\" : \"\");\n +\t\t\tdirty ? N_(\"and made changes to the index and/or the \"\n +\t\t\t\t\"working tree\\n\") : \"\");\n  \t\tif (status == 127)\n  \t\t\t/* command not found */\n  \t\t\tstatus = 1;\n  \t}\n  \telse if (dirty) {\n -\t\twarning(\"Execution succeeded: %s\\nbut \"\n -\t\t\t\"left changes to the index and/or the working tree\\n\"\n -\t\t\t\"Commit or stash your changes, and then run\\n\"\n -\t\t\t\"\\n\"\n -\t\t\t\"  git rebase --continue\\n\"\n -\t\t\t\"\\n\", command_line);\n +\t\twarning(_(\"execution succeeded: %s\\nbut \"\n +\t\t\t  \"left changes to the index and/or the working tree\\n\"\n +\t\t\t  \"Commit or stash your changes, and then run\\n\"\n +\t\t\t  \"\\n\"\n +\t\t\t  \"  git rebase --continue\\n\"\n +\t\t\t  \"\\n\"), command_line);\n  \t\tstatus = 1;\n  \t}\n  \n @@ -1912,7 +1922,7 @@ static int apply_autostash(struct replay_opts *opts)\n  \t\targv_array_push(&store.args, \"-q\");\n  \t\targv_array_push(&store.args, stash_sha1.buf);\n  \t\tif (run_command(&store))\n -\t\t\tret = error(_(\"Cannot store %s\"), stash_sha1.buf);\n +\t\t\tret = error(_(\"cannot store %s\"), stash_sha1.buf);\n  \t\telse\n  \t\t\tprintf(_(\"Applying autostash resulted in conflicts.\\n\"\n  \t\t\t\t\"Your changes are safe in the stash.\\n\"\n @@ -1995,7 +2005,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n  \t\t\tif (item->command == TODO_EDIT) {\n  \t\t\t\tstruct commit *commit = item->commit;\n  \t\t\t\tif (!res)\n -\t\t\t\t\twarning(\"Stopped at %s... %.*s\",\n +\t\t\t\t\twarning(_(\"stopped at %s... %.*s\"),\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\treturn error_with_patch(commit,\n @@ -2025,7 +2035,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n  \t\t\t*end_of_arg = saved;\n  \t\t}\n  \t\telse if (item->command < TODO_NOOP)\n -\t\t\treturn error(\"Unknown command %d\", item->command);\n +\t\t\treturn error(_(\"unknown command %d\"), item->command);\n  \n  \t\ttodo_list->current++;\n  \t\tif (res)\n @@ -2046,22 +2056,22 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n  \t\t\tunsigned char head[20], orig[20];\n  \n  \t\t\tif (get_sha1(\"HEAD\", head))\n -\t\t\t\treturn error(\"Cannot read HEAD\");\n +\t\t\t\treturn error(_(\"cannot read HEAD\"));\n  \t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0) ||\n  \t\t\t\t\tget_sha1_hex(buf.buf, orig))\n -\t\t\t\treturn error(\"Could not read orig-head\");\n +\t\t\t\treturn error(_(\"could not read orig-head\"));\n  \t\t\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n -\t\t\t\treturn error(\"Could not read 'onto'\");\n +\t\t\t\treturn error(_(\"could not read 'onto'\"));\n  \t\t\tmsg = reflog_message(opts, \"finish\", \"%s onto %s\",\n  \t\t\t\thead_ref.buf, buf.buf);\n  \t\t\tif (update_ref(msg, head_ref.buf, head, orig,\n  \t\t\t\t\tREF_NODEREF, UPDATE_REFS_MSG_ON_ERR))\n -\t\t\t\treturn error(\"Could not update %s\",\n +\t\t\t\treturn error(_(\"could not update %s\"),\n  \t\t\t\t\thead_ref.buf);\n  \t\t\tmsg = reflog_message(opts, \"finish\", \"returning to %s\",\n  \t\t\t\thead_ref.buf);\n  \t\t\tif (create_symref(\"HEAD\", head_ref.buf, msg))\n -\t\t\t\treturn error(\"Could not update HEAD to %s\",\n +\t\t\t\treturn error(_(\"could not update HEAD to %s\"),\n  \t\t\t\t\thead_ref.buf);\n  \t\t\tstrbuf_reset(&buf);\n  \t\t}\n @@ -2072,7 +2082,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n  \t\t\t};\n  \n  \t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0))\n -\t\t\t\treturn error(\"Could not read %s\",\n +\t\t\t\treturn error(_(\"could not read '%s'\"),\n  \t\t\t\t\trebase_path_orig_head());\n  \t\t\tstrbuf_addstr(&buf, \"..HEAD\");\n  \t\t\targv[2] = buf.buf;\n @@ -2099,6 +2109,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n  \n  \t\t\t\thook.in = open(rebase_path_rewritten_list(),\n  \t\t\t\t\tO_RDONLY);\n +\t\t\t\thook.stdout_to_stderr = 1;\n  \t\t\t\targv_array_push(&hook.args, post_rewrite_hook);\n  \t\t\t\targv_array_push(&hook.args, \"rebase\");\n  \t\t\t\t/* we don't care if this hook failed */\n @@ -2136,12 +2147,12 @@ static int commit_staged_changes(struct replay_opts *opts)\n  \tint amend = 0;\n  \n  \tif (has_unstaged_changes(1))\n -\t\treturn error(_(\"Cannot rebase: You have unstaged changes.\"));\n +\t\treturn error(_(\"cannot rebase: You have unstaged changes.\"));\n  \tif (!has_uncommitted_changes(0)) {\n  \t\tconst char *cherry_pick_head = git_path(\"CHERRY_PICK_HEAD\");\n  \n  \t\tif (file_exists(cherry_pick_head) && unlink(cherry_pick_head))\n -\t\t\treturn error(\"Could not remove CHERRY_PICK_HEAD\");\n +\t\t\treturn error(_(\"could not remove CHERRY_PICK_HEAD\"));\n  \t\treturn 0;\n  \t}\n  \n @@ -2150,23 +2161,24 @@ static int commit_staged_changes(struct replay_opts *opts)\n  \t\tunsigned char head[20], to_amend[20];\n  \n  \t\tif (get_sha1(\"HEAD\", head))\n -\t\t\treturn error(\"Cannot amend non-existing commit\");\n +\t\t\treturn error(_(\"cannot amend non-existing commit\"));\n  \t\tif (!read_oneliner(&rev, rebase_path_amend(), 0))\n -\t\t\treturn error(\"Invalid file: %s\", rebase_path_amend());\n +\t\t\treturn error(_(\"invalid file: '%s'\"), rebase_path_amend());\n  \t\tif (get_sha1_hex(rev.buf, to_amend))\n -\t\t\treturn error(\"Invalid contents: %s\",\n +\t\t\treturn error(_(\"invalid contents: '%s'\"),\n  \t\t\t\trebase_path_amend());\n  \t\tif (hashcmp(head, to_amend))\n -\t\t\treturn error(\"\\nYou have uncommitted changes in your \"\n -\t\t\t\t\"working tree. Please, commit them\\nfirst and \"\n -\t\t\t\t\"then run 'git rebase --continue' again.\");\n +\t\t\treturn error(_(\"\\nYou have uncommitted changes in your \"\n +\t\t\t\t       \"working tree. Please, commit them\\n\"\n +\t\t\t\t       \"first and then run 'git rebase \"\n +\t\t\t\t       \"--continue' again.\"));\n  \n  \t\tstrbuf_release(&rev);\n  \t\tamend = 1;\n  \t}\n  \n -\tif (sequencer_commit(rebase_path_message(), opts, 1, 1, amend, 0))\n -\t\treturn error(\"Could not commit staged changes.\");\n +\tif (run_git_commit(rebase_path_message(), opts, 1, 1, amend, 0))\n +\t\treturn error(_(\"could not commit staged changes.\"));\n  \tunlink(rebase_path_amend());\n  \treturn 0;\n  }\n @@ -2194,23 +2206,23 @@ int sequencer_continue(struct replay_opts *opts)\n  \t\t/* Verify that the conflict has been resolved */\n  \t\tif (file_exists(git_path_cherry_pick_head()) ||\n  \t\t    file_exists(git_path_revert_head())) {\n -\t\t\tint ret = continue_single_pick();\n -\t\t\tif (ret)\n -\t\t\t\treturn ret;\n +\t\t\tres = continue_single_pick();\n +\t\t\tif (res)\n +\t\t\t\tgoto release_todo_list;\n +\t\t}\n +\t\tif (index_differs_from(\"HEAD\", 0, 0)) {\n +\t\t\tres = error_dirty_index(opts);\n +\t\t\tgoto release_todo_list;\n  \t\t}\n -\t\tif (index_differs_from(\"HEAD\", 0))\n -\t\t\treturn error_dirty_index(opts);\n  \t\ttodo_list.current++;\n  \t}\n  \telse if (file_exists(rebase_path_stopped_sha())) {\n  \t\tstruct strbuf buf = STRBUF_INIT;\n  \t\tstruct object_id oid;\n  \n -\t\tif (read_oneliner(&buf, rebase_path_stopped_sha(), 1)) {\n -\t\t\tif (!get_sha1_committish(buf.buf, oid.hash))\n -\t\t\t\trecord_in_rewritten(&oid,\n -\t\t\t\t\t\tpeek_command(&todo_list, 0));\n -\t\t}\n +\t\tif (read_oneliner(&buf, rebase_path_stopped_sha(), 1) &&\n +\t\t    !get_sha1_committish(buf.buf, oid.hash))\n +\t\t\trecord_in_rewritten(&oid, peek_command(&todo_list, 0));\n  \t\tstrbuf_release(&buf);\n  \t}\n  \n\n-- \n2.11.0.rc3.windows.1\n\n"},{"id":"307625","messageId":"297140020a7312af03136848dcdd0353ee3abdfe.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 01/34] sequencer: support a new action: 'interactive rebase'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:29:14Z","receivedAt":"2016-12-13T15:36:25Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This patch introduces a new action for the sequencer. It really does not\ndo a whole lot of its own right now, but lays the ground work for\npatches to come. The intention, of course, is to finally make the\nsequencer the work horse of the interactive rebase (the original idea\nbehind the \"sequencer\" concept).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 33 +++++++++++++++++++++++++++++----\n sequencer.h |  3 ++-\n 2 files changed, 31 insertions(+), 5 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 30b10ba143..21cfdacd06 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -28,6 +28,14 @@ static GIT_PATH_FUNC(git_path_todo_file, \"sequencer/todo\")\n static GIT_PATH_FUNC(git_path_opts_file, \"sequencer/opts\")\n static GIT_PATH_FUNC(git_path_head_file, \"sequencer/head\")\n \n+static GIT_PATH_FUNC(rebase_path, \"rebase-merge\")\n+/*\n+ * The file containing rebase commands, comments, and empty lines.\n+ * This file is created by \"git rebase -i\" then edited by the user. As\n+ * the lines are processed, they are removed from the front of this\n+ * file and written to the tail of 'done'.\n+ */\n+static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n /*\n  * A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and\n  * GIT_AUTHOR_DATE that will be used for the commit that is currently\n@@ -40,19 +48,22 @@ static GIT_PATH_FUNC(rebase_path_author_script, \"rebase-merge/author-script\")\n  */\n static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, \"rebase-merge/gpg_sign_opt\")\n \n-/* We will introduce the 'interactive rebase' mode later */\n static inline int is_rebase_i(const struct replay_opts *opts)\n {\n-\treturn 0;\n+\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n }\n \n static const char *get_dir(const struct replay_opts *opts)\n {\n+\tif (is_rebase_i(opts))\n+\t\treturn rebase_path();\n \treturn git_path_seq_dir();\n }\n \n static const char *get_todo_path(const struct replay_opts *opts)\n {\n+\tif (is_rebase_i(opts))\n+\t\treturn rebase_path_todo();\n \treturn git_path_todo_file();\n }\n \n@@ -168,7 +179,15 @@ int sequencer_remove_state(struct replay_opts *opts)\n \n static const char *action_name(const struct replay_opts *opts)\n {\n-\treturn opts->action == REPLAY_REVERT ? N_(\"revert\") : N_(\"cherry-pick\");\n+\tswitch (opts->action) {\n+\tcase REPLAY_REVERT:\n+\t\treturn N_(\"revert\");\n+\tcase REPLAY_PICK:\n+\t\treturn N_(\"cherry-pick\");\n+\tcase REPLAY_INTERACTIVE_REBASE:\n+\t\treturn N_(\"rebase -i\");\n+\t}\n+\tdie(_(\"Unknown action: %d\"), opts->action);\n }\n \n struct commit_message {\n@@ -395,7 +414,10 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, &index_lock, COMMIT_LOCK))\n-\t\t/* TRANSLATORS: %s will be \"revert\" or \"cherry-pick\" */\n+\t\t/*\n+\t\t * TRANSLATORS: %s will be \"revert\", \"cherry-pick\" or\n+\t\t * \"rebase -i\".\n+\t\t */\n \t\treturn error(_(\"%s: Unable to write new index file\"),\n \t\t\t_(action_name(opts)));\n \trollback_lock_file(&index_lock);\n@@ -1204,6 +1226,9 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)\n \tconst char *todo_path = get_todo_path(opts);\n \tint next = todo_list->current, offset, fd;\n \n+\tif (is_rebase_i(opts))\n+\t\tnext++;\n+\n \tfd = hold_lock_file_for_update(&todo_lock, todo_path, 0);\n \tif (fd < 0)\n \t\treturn error_errno(_(\"could not lock '%s'\"), todo_path);\ndiff --git a/sequencer.h b/sequencer.h\nindex 7a513c576b..cb21cfddee 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -7,7 +7,8 @@ const char *git_path_seq_dir(void);\n \n enum replay_action {\n \tREPLAY_REVERT,\n-\tREPLAY_PICK\n+\tREPLAY_PICK,\n+\tREPLAY_INTERACTIVE_REBASE\n };\n \n struct replay_opts {\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307626","messageId":"934c8e420cc4a75b1f3e4489fa4a4135c48f78ae.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 02/34] sequencer (rebase -i): implement the 'noop' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:29:34Z","receivedAt":"2016-12-13T15:36:32Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The 'noop' command is probably the most boring of all rebase -i commands\nto support in the sequencer.\n\nWhich makes it an excellent candidate for this first stab to add support\nfor rebase -i's commands to the sequencer.\n\nFor the moment, let's also treat empty lines and commented-out lines as\n'noop'; We will refine that handling later in this patch series.\n\nTo make it easier to identify \"classes\" of todo_commands (such as:\ndetermine whether a command is pick-like, i.e. handles a single commit),\nlet's enforce a certain order of said commands.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 35 ++++++++++++++++++++++++++++++++---\n 1 file changed, 32 insertions(+), 3 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 21cfdacd06..1224799286 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -639,14 +639,23 @@ 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_REVERT,\n+\t/* commands that do nothing but are counted for reporting progress */\n+\tTODO_NOOP\n };\n \n static const char *todo_command_strings[] = {\n \t\"pick\",\n-\t\"revert\"\n+\t\"revert\",\n+\t\"noop\"\n };\n \n static const char *command_to_string(const enum todo_command command)\n@@ -916,6 +925,14 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \t/* left-trim */\n \tbol += strspn(bol, \" \\t\");\n \n+\tif (bol == eol || *bol == '\\r' || *bol == comment_line_char) {\n+\t\titem->command = TODO_NOOP;\n+\t\titem->commit = NULL;\n+\t\titem->arg = bol;\n+\t\titem->arg_len = eol - bol;\n+\t\treturn 0;\n+\t}\n+\n \tfor (i = 0; i < ARRAY_SIZE(todo_command_strings); i++)\n \t\tif (skip_prefix(bol, todo_command_strings[i], &bol)) {\n \t\t\titem->command = i;\n@@ -924,6 +941,13 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \tif (i >= ARRAY_SIZE(todo_command_strings))\n \t\treturn -1;\n \n+\tif (item->command == TODO_NOOP) {\n+\t\titem->commit = NULL;\n+\t\titem->arg = bol;\n+\t\titem->arg_len = eol - bol;\n+\t\treturn 0;\n+\t}\n+\n \t/* Eat up extra spaces/ tabs before object name */\n \tpadding = strspn(bol, \" \\t\");\n \tif (!padding)\n@@ -1292,7 +1316,12 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tstruct todo_item *item = todo_list->items + todo_list->current;\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n-\t\tres = do_pick_commit(item->command, item->commit, opts);\n+\t\tif (item->command <= TODO_REVERT)\n+\t\t\tres = do_pick_commit(item->command, item->commit,\n+\t\t\t\t\topts);\n+\t\telse if (item->command != TODO_NOOP)\n+\t\t\treturn error(_(\"unknown command %d\"), item->command);\n+\n \t\ttodo_list->current++;\n \t\tif (res)\n \t\t\treturn res;\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307627","messageId":"42d43763075f3f2daa2cc06a8afc9bcfd8b20ac2.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 08/34] sequencer (rebase -i): implement the short commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:30:06Z","receivedAt":"2016-12-13T15:37:00Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"For users' convenience, most rebase commands can be abbreviated, e.g.\n'p' instead of 'pick' and 'x' instead of 'exec'. Let's teach the\nsequencer to handle those abbreviated commands just fine.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 36 ++++++++++++++++++++++--------------\n 1 file changed, 22 insertions(+), 14 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 271c21581d..e443f4765d 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -710,20 +710,23 @@ enum todo_command {\n \tTODO_NOOP\n };\n \n-static const char *todo_command_strings[] = {\n-\t\"pick\",\n-\t\"revert\",\n-\t\"edit\",\n-\t\"fixup\",\n-\t\"squash\",\n-\t\"exec\",\n-\t\"noop\"\n+static struct {\n+\tchar c;\n+\tconst char *str;\n+} todo_command_info[] = {\n+\t{ 'p', \"pick\" },\n+\t{ 0,   \"revert\" },\n+\t{ 'e', \"edit\" },\n+\t{ 'f', \"fixup\" },\n+\t{ 's', \"squash\" },\n+\t{ 'x', \"exec\" },\n+\t{ 0,   \"noop\" }\n };\n \n static const char *command_to_string(const enum todo_command command)\n {\n-\tif ((size_t)command < ARRAY_SIZE(todo_command_strings))\n-\t\treturn todo_command_strings[command];\n+\tif ((size_t)command < ARRAY_SIZE(todo_command_info))\n+\t\treturn todo_command_info[command].str;\n \tdie(\"Unknown command: %d\", command);\n }\n \n@@ -1125,12 +1128,17 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \t\treturn 0;\n \t}\n \n-\tfor (i = 0; i < ARRAY_SIZE(todo_command_strings); i++)\n-\t\tif (skip_prefix(bol, todo_command_strings[i], &bol)) {\n+\tfor (i = 0; i < ARRAY_SIZE(todo_command_info); i++)\n+\t\tif (skip_prefix(bol, todo_command_info[i].str, &bol)) {\n \t\t\titem->command = i;\n \t\t\tbreak;\n \t\t}\n-\tif (i >= ARRAY_SIZE(todo_command_strings))\n+\t\telse if (bol[1] == ' ' && *bol == todo_command_info[i].c) {\n+\t\t\tbol++;\n+\t\t\titem->command = i;\n+\t\t\tbreak;\n+\t\t}\n+\tif (i >= ARRAY_SIZE(todo_command_info))\n \t\treturn -1;\n \n \tif (item->command == TODO_NOOP) {\n@@ -1325,7 +1333,7 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,\n {\n \tenum todo_command command = opts->action == REPLAY_PICK ?\n \t\tTODO_PICK : TODO_REVERT;\n-\tconst char *command_string = todo_command_strings[command];\n+\tconst char *command_string = todo_command_info[command].str;\n \tstruct commit *commit;\n \n \tif (prepare_revs(opts))\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307628","messageId":"09c2718e119f809093794410ae1a738c1cd122d1.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 09/34] sequencer (rebase -i): write an author-script file","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:30:09Z","receivedAt":"2016-12-13T15:37:07Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When the interactive rebase aborts, it writes out an author-script file\nto record the author information for the current commit. As we are about\nto teach the sequencer how to perform the actions behind an interactive\nrebase, it needs to write those author-script files, too.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 50 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex e443f4765d..80469b6954 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -515,6 +515,53 @@ static int is_index_unchanged(void)\n \treturn !hashcmp(active_cache_tree->sha1, head_commit->tree->object.oid.hash);\n }\n \n+static int write_author_script(const char *message)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *eol;\n+\tint res;\n+\n+\tfor (;;)\n+\t\tif (!*message || starts_with(message, \"\\n\")) {\n+missing_author:\n+\t\t\t/* Missing 'author' line? */\n+\t\t\tunlink(rebase_path_author_script());\n+\t\t\treturn 0;\n+\t\t}\n+\t\telse if (skip_prefix(message, \"author \", &message))\n+\t\t\tbreak;\n+\t\telse if ((eol = strchr(message, '\\n')))\n+\t\t\tmessage = eol + 1;\n+\t\telse\n+\t\t\tgoto missing_author;\n+\n+\tstrbuf_addstr(&buf, \"GIT_AUTHOR_NAME='\");\n+\twhile (*message && *message != '\\n' && *message != '\\r')\n+\t\tif (skip_prefix(message, \" <\", &message))\n+\t\t\tbreak;\n+\t\telse if (*message != '\\'')\n+\t\t\tstrbuf_addch(&buf, *(message++));\n+\t\telse\n+\t\t\tstrbuf_addf(&buf, \"'\\\\\\\\%c'\", *(message++));\n+\tstrbuf_addstr(&buf, \"'\\nGIT_AUTHOR_EMAIL='\");\n+\twhile (*message && *message != '\\n' && *message != '\\r')\n+\t\tif (skip_prefix(message, \"> \", &message))\n+\t\t\tbreak;\n+\t\telse if (*message != '\\'')\n+\t\t\tstrbuf_addch(&buf, *(message++));\n+\t\telse\n+\t\t\tstrbuf_addf(&buf, \"'\\\\\\\\%c'\", *(message++));\n+\tstrbuf_addstr(&buf, \"'\\nGIT_AUTHOR_DATE='@\");\n+\twhile (*message && *message != '\\n' && *message != '\\r')\n+\t\tif (*message != '\\'')\n+\t\t\tstrbuf_addch(&buf, *(message++));\n+\t\telse\n+\t\t\tstrbuf_addf(&buf, \"'\\\\\\\\%c'\", *(message++));\n+\tres = write_message(buf.buf, buf.len, rebase_path_author_script(), 1);\n+\tstrbuf_release(&buf);\n+\treturn res;\n+}\n+\n /*\n  * Read the author-script file into an environment block, ready for use in\n  * run_command(), that can be free()d afterwards.\n@@ -974,7 +1021,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t}\n \t}\n \n-\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n+\tif (is_rebase_i(opts) && write_author_script(msg.message) < 0)\n+\t\tres = -1;\n+\telse if (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n \t\tif (res < 0)\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307629","messageId":"9978c32139c522c08d5a8f685011829cc830bfc0.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 10/34] sequencer (rebase -i): allow continuing with staged changes","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:30:30Z","receivedAt":"2016-12-13T15:37:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When an interactive rebase is interrupted, the user may stage changes\nbefore continuing, and we need to commit those changes in that case.\n\nPlease note that the nested \"if\" added to the sequencer_continue() is\nnot combined into a single \"if\" because it will be extended with an\n\"else\" clause in a later patch in this patch series.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 40 ++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 40 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 80469b6954..855d3ba503 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1829,6 +1829,42 @@ static int continue_single_pick(void)\n \treturn run_command_v_opt(argv, RUN_GIT_CMD);\n }\n \n+static int commit_staged_changes(struct replay_opts *opts)\n+{\n+\tint amend = 0;\n+\n+\tif (has_unstaged_changes(1))\n+\t\treturn error(_(\"cannot rebase: You have unstaged changes.\"));\n+\tif (!has_uncommitted_changes(0))\n+\t\treturn 0;\n+\n+\tif (file_exists(rebase_path_amend())) {\n+\t\tstruct strbuf rev = STRBUF_INIT;\n+\t\tunsigned char head[20], to_amend[20];\n+\n+\t\tif (get_sha1(\"HEAD\", head))\n+\t\t\treturn error(_(\"cannot amend non-existing commit\"));\n+\t\tif (!read_oneliner(&rev, rebase_path_amend(), 0))\n+\t\t\treturn error(_(\"invalid file: '%s'\"), rebase_path_amend());\n+\t\tif (get_sha1_hex(rev.buf, to_amend))\n+\t\t\treturn error(_(\"invalid contents: '%s'\"),\n+\t\t\t\trebase_path_amend());\n+\t\tif (hashcmp(head, to_amend))\n+\t\t\treturn error(_(\"\\nYou have uncommitted changes in your \"\n+\t\t\t\t       \"working tree. Please, commit them\\n\"\n+\t\t\t\t       \"first and then run 'git rebase \"\n+\t\t\t\t       \"--continue' again.\"));\n+\n+\t\tstrbuf_release(&rev);\n+\t\tamend = 1;\n+\t}\n+\n+\tif (run_git_commit(rebase_path_message(), opts, 1, 1, amend, 0))\n+\t\treturn error(_(\"could not commit staged changes.\"));\n+\tunlink(rebase_path_amend());\n+\treturn 0;\n+}\n+\n int sequencer_continue(struct replay_opts *opts)\n {\n \tstruct todo_list todo_list = TODO_LIST_INIT;\n@@ -1837,6 +1873,10 @@ int sequencer_continue(struct replay_opts *opts)\n \tif (read_and_refresh_cache(opts))\n \t\treturn -1;\n \n+\tif (is_rebase_i(opts)) {\n+\t\tif (commit_staged_changes(opts))\n+\t\t\treturn -1;\n+\t}\n \tif (!file_exists(get_todo_path(opts)))\n \t\treturn continue_single_pick();\n \tif (read_populate_opts(opts))\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307630","messageId":"81ba5f7ddb3a1a66e878b955094b7ae00f2cd781.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 11/34] sequencer (rebase -i): remove CHERRY_PICK_HEAD when no longer needed","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:30:38Z","receivedAt":"2016-12-13T15:37:21Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The scripted version of the interactive rebase already does that.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 855d3ba503..abffaf3b40 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1835,8 +1835,13 @@ static int commit_staged_changes(struct replay_opts *opts)\n \n \tif (has_unstaged_changes(1))\n \t\treturn error(_(\"cannot rebase: You have unstaged changes.\"));\n-\tif (!has_uncommitted_changes(0))\n+\tif (!has_uncommitted_changes(0)) {\n+\t\tconst char *cherry_pick_head = git_path(\"CHERRY_PICK_HEAD\");\n+\n+\t\tif (file_exists(cherry_pick_head) && unlink(cherry_pick_head))\n+\t\t\treturn error(_(\"could not remove CHERRY_PICK_HEAD\"));\n \t\treturn 0;\n+\t}\n \n \tif (file_exists(rebase_path_amend())) {\n \t\tstruct strbuf rev = STRBUF_INIT;\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307631","messageId":"45ba32ba1bb70ffe2f5034b638e3aa1936b5546d.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 30/34] sequencer (rebase -i): show only failed cherry-picks' output","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:32:37Z","receivedAt":"2016-12-13T15:39:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is the behavior of the shell script version of the interactive\nrebase, by using the `output` function defined in `git-rebase.sh`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 4 ++++\n 1 file changed, 4 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex dfa4fab98b..edc213a2c8 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -464,6 +464,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \to.ancestor = base ? base_label : \"(empty tree)\";\n \to.branch1 = \"HEAD\";\n \to.branch2 = next ? next_label : \"(empty tree)\";\n+\tif (is_rebase_i(opts))\n+\t\to.buffer_output = 2;\n \n \thead_tree = parse_tree_indirect(head);\n \tnext_tree = next ? next->tree : empty_tree();\n@@ -475,6 +477,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \tclean = merge_trees(&o,\n \t\t\t    head_tree,\n \t\t\t    next_tree, base_tree, &result);\n+\tif (is_rebase_i(opts) && clean <= 0)\n+\t\tfputs(o.obuf.buf, stdout);\n \tstrbuf_release(&o.obuf);\n \tif (clean < 0)\n \t\treturn clean;\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307632","messageId":"4cce3c1462b290494b3195c9ff4918ad99c24a14.1481642927.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v2 29/34] sequencer (rebase -i): show only failed `git commit`'s output","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-13T15:32:34Z","receivedAt":"2016-12-13T15:39:31Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is the behavior of the shell script version of the interactive\nrebase, by using the `output` function defined in `git-rebase.sh`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 9 +++++++--\n 1 file changed, 7 insertions(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 63f6f25ced..dfa4fab98b 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -647,10 +647,15 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n {\n \tchar **env = NULL;\n \tstruct argv_array array;\n-\tint rc;\n+\tint opt = RUN_GIT_CMD, rc;\n \tconst char *value;\n \n \tif (is_rebase_i(opts)) {\n+\t\tif (!edit) {\n+\t\t\topt |= RUN_COMMAND_STDOUT_TO_STDERR;\n+\t\t\topt |= RUN_HIDE_STDERR_ON_SUCCESS;\n+\t\t}\n+\n \t\tenv = read_author_script();\n \t\tif (!env) {\n \t\t\tconst char *gpg_opt = gpg_sign_opt_quoted(opts);\n@@ -687,7 +692,7 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \tif (opts->allow_empty_message)\n \t\targv_array_push(&array, \"--allow-empty-message\");\n \n-\trc = run_command_v_opt_cd_env(array.argv, RUN_GIT_CMD, NULL,\n+\trc = run_command_v_opt_cd_env(array.argv, opt, NULL,\n \t\t\t(const char *const *)env);\n \targv_array_clear(&array);\n \tfree(env);\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"307668","messageId":"xmqqa8bz39aw.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 00/34] Teach the sequencer to act as rebase -i's backend","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-13T19:42:31Z","receivedAt":"2016-12-13T19:44:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> This marks the count down to '3': two more patch series after this\n> (really tiny ones) and we have a faster rebase -i.\n\nNice.\n\n> Apart from mostly cosmetic patches (and the occasional odd bug that I\n> fixed promptly), I used these patches since mid May to perform all of my\n> interactive rebases. In mid June, I had the idea to teach rebase -i to\n> run *both* scripted rebase and rebase--helper and to cross-validate the\n> results. This slowed down all my interactive rebases since, but helped\n> me catch three rather obscure bugs (e.g. that git commit --fixup unfolds\n> long onelines and rebase -i still finds the correct original commit).\n> ...\n> Please note that the interdiff vs v1 is only of limited use: too many\n> things changed in the meantime, in particular the prepare-sequencer\n> branch that went through a couple of iterations before it found its way\n> into git.git's master branch. So please take the interdiff with a\n> mountain range of salt.\n> ...\n> Changes since v1:\n> ...\n> - removed the beautiful ordinal logic (to print out \"1st\", \"2nd\", \"3rd\"\n>   etc) and made things consistent with the current `rebase -i`.\n\nIt was removed because it was too Anglo-centric and unusable in i18n\ncontext, no?\n\nJudging from the list above, interdiff are pretty much all cosmetic\nand that is why you say it is only of limited use, I guess.\n\n    ... goes and reads the remainder and finds that these were\n    ... all minor changes, mostly cosmetic, with a helper function\n    ... refactored out or two and things of that nature.\n\nIt is actually a good thing.  We do not want to see it deviate too\ndrastically from what you have been testing for some months.\n\nThanks.\n"},{"id":"307678","messageId":"xmqqfulr1s5z.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"934c8e420cc4a75b1f3e4489fa4a4135c48f78ae.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 02/34] sequencer (rebase -i): implement the 'noop' command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-13T20:38:00Z","receivedAt":"2016-12-13T20:43:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\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\nGood thinking.  Makes me wish C were a better language, though ;-)\n\n>  enum todo_command {\n> +\t/* commands that handle commits */\n>  \tTODO_PICK = 0,\n> -\tTODO_REVERT\n> +\tTODO_REVERT,\n> +\t/* commands that do nothing but are counted for reporting progress */\n> +\tTODO_NOOP\n>  };\n>  \n>  static const char *todo_command_strings[] = {\n>  \t\"pick\",\n> -\t\"revert\"\n> +\t\"revert\",\n> +\t\"noop\"\n>  };\n\n> @@ -1292,7 +1316,12 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n>  \t\tstruct todo_item *item = todo_list->items + todo_list->current;\n>  \t\tif (save_todo(todo_list, opts))\n>  \t\t\treturn -1;\n> -\t\tres = do_pick_commit(item->command, item->commit, opts);\n> +\t\tif (item->command <= TODO_REVERT)\n> +\t\t\tres = do_pick_commit(item->command, item->commit,\n> +\t\t\t\t\topts);\n> +\t\telse if (item->command != TODO_NOOP)\n> +\t\t\treturn error(_(\"unknown command %d\"), item->command);\n\nI wonder if making this a switch() statement is easier to read in\nthe longer run.  The only thing at this point we are gaining by \"not\nonly mapping and enum must match, the orders matter\" is so that this\ncodepath can do the same thing for PICK and REVERT, but these two\nwould become more and more minority as we learn more words.\n\n>  \t\ttodo_list->current++;\n>  \t\tif (res)\n>  \t\t\treturn res;\n"},{"id":"307679","messageId":"xmqqk2b31sfs.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"297140020a7312af03136848dcdd0353ee3abdfe.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 01/34] sequencer: support a new action: 'interactive rebase'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-13T20:32:07Z","receivedAt":"2016-12-13T20:46:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n>  static inline int is_rebase_i(const struct replay_opts *opts)\n>  {\n> -\treturn 0;\n> +\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n>  }\n>  \n>  static const char *get_dir(const struct replay_opts *opts)\n>  {\n> +\tif (is_rebase_i(opts))\n> +\t\treturn rebase_path();\n>  \treturn git_path_seq_dir();\n>  }\n>  \n>  static const char *get_todo_path(const struct replay_opts *opts)\n>  {\n> +\tif (is_rebase_i(opts))\n> +\t\treturn rebase_path_todo();\n>  \treturn git_path_todo_file();\n>  }\n>  \n> @@ -168,7 +179,15 @@ int sequencer_remove_state(struct replay_opts *opts)\n>  \n>  static const char *action_name(const struct replay_opts *opts)\n>  {\n> -\treturn opts->action == REPLAY_REVERT ? N_(\"revert\") : N_(\"cherry-pick\");\n> +\tswitch (opts->action) {\n> +\tcase REPLAY_REVERT:\n> +\t\treturn N_(\"revert\");\n> +\tcase REPLAY_PICK:\n> +\t\treturn N_(\"cherry-pick\");\n> +\tcase REPLAY_INTERACTIVE_REBASE:\n> +\t\treturn N_(\"rebase -i\");\n> +\t}\n> +\tdie(_(\"Unknown action: %d\"), opts->action);\n>  }\n\nThis case statement which looks perfectly sensible---it says that\nthere are three equal modes the subsystem operates in.  \n\nThis is just a mental note and not a suggestion to change anything\nimmediately, but it makes me wonder if git_dir/get_todo_path would\nalso want to do so, moving towards retiring is_rebase_i() which is\n\"everything else vs one oddball which is rebase-i\" mindset.\n\n> @@ -395,7 +414,10 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n>  \n>  \tif (active_cache_changed &&\n>  \t    write_locked_index(&the_index, &index_lock, COMMIT_LOCK))\n> -\t\t/* TRANSLATORS: %s will be \"revert\" or \"cherry-pick\" */\n> +\t\t/*\n> +\t\t * TRANSLATORS: %s will be \"revert\", \"cherry-pick\" or\n> +\t\t * \"rebase -i\".\n> +\t\t */\n\nIIRC, the \"TRANSLATORS:\" comment has to deviate from our coding\nstyle due to tool limitation and has to be done like this:\n\n> +\t\t/* TRANSLATORS: %s will be \"revert\", \"cherry-pick\" or\n> +\t\t * \"rebase -i\".\n> +\t\t */\n\n> @@ -1204,6 +1226,9 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)\n>  \tconst char *todo_path = get_todo_path(opts);\n>  \tint next = todo_list->current, offset, fd;\n>  \n> +\tif (is_rebase_i(opts))\n> +\t\tnext++;\n> +\n\nThis is because...?  Everybody else counts 0-based while rebase-i\ncounts from 1 or something?\n\n>  \tfd = hold_lock_file_for_update(&todo_lock, todo_path, 0);\n>  \tif (fd < 0)\n>  \t\treturn error_errno(_(\"could not lock '%s'\"), todo_path);\n\nEverything else in the patch is understandable.  This bit isn't\nwithout explanation, at least to me.\n"},{"id":"307686","messageId":"CA+55aFzxFFNY+dL6s7dLZeVXBsBKD0aeof5Bj2wcD1CpefVSAA@mail.gmail.com","threadId":"43966","inReplyTo":"xmqqfulr1s5z.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 02/34] sequencer (rebase -i): implement the 'noop' command","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2016-12-13T20:48:31Z","receivedAt":"2016-12-13T21:09:03Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"On Tue, Dec 13, 2016 at 12:38 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\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>\n> Good thinking.  Makes me wish C were a better language, though ;-)\n\nDo this:\n\n  static const char *todo_command_strings[] = {\n      [TODO_PICK] = \"pick\",\n      [TODO_REVERT] = \"revert\",\n      [TODO_NOOP] = \"noop:,\n  };\n\nwhich makes the array be order-independent. You still need to make\nsure you fill in all the entries, of course, but it tends to avoid at\nleast one gotcha, and it makes it more obvious how the two are tied\ntogether.\n\n              Linus\n"},{"id":"307687","messageId":"xmqqbmwf1pqd.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"a1361151ad1dad8f4dc3c412c7ed30f625d67ba0.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 03/34] sequencer (rebase -i): implement the 'edit' command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-13T21:30:34Z","receivedAt":"2016-12-13T21:30:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> @@ -43,6 +44,20 @@ static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n>   */\n>  static GIT_PATH_FUNC(rebase_path_author_script, \"rebase-merge/author-script\")\n>  /*\n\nIt is minor, but please have a blank line to separate the logical\nblocks.  If you have \"comment for thing A\" before \"thing A\", then \nhaving a blank after that before \"comment for thing B\" and \"thing B\"\nthat follow would help.  Otherwise \"thing A\" immediately followed by\n\"comment for thing B\" are (mis)read together, leading to nonsense.\n\n> + * When an \"edit\" rebase command is being processed, the SHA1 of the\n> + * commit to be edited is recorded in this file.  When \"git rebase\n> + * --continue\" is executed, if there are any staged changes then they\n> + * will be amended to the HEAD commit, but only provided the HEAD\n> + * commit is still the commit to be edited.  When any other rebase\n> + * command is processed, this file is deleted.\n> + */\n> +static GIT_PATH_FUNC(rebase_path_amend, \"rebase-merge/amend\")\n> +/*\n> + * When we stop at a given patch via the \"edit\" command, this file contains\n> + * the long commit name of the corresponding patch.\n\nIf you abbreviate an object name to 38-hex that is still long but\nthat is not full; if you meant full 40-hex, better spell it out as\n\"full\"---that conveys useful information to programmers (e.g. they\ncan just use get_sha1_hex()).\n\nBut I think you are writing short_commit_name() to it?  So perhaps\n\"an abbreviated commit object name\"?\n\n> @@ -1301,9 +1318,87 @@ static int save_opts(struct replay_opts *opts)\n>  \treturn res;\n>  }\n>  \n> +static int make_patch(struct commit *commit, struct replay_opts *opts)\n> +{\n> +\tstruct strbuf buf = STRBUF_INIT;\n> +\tstruct rev_info log_tree_opt;\n> +\tconst char *commit_buffer = get_commit_buffer(commit, NULL), *subject, *p;\n> +\tint res = 0;\n> +\n> +\tp = short_commit_name(commit);\n> +\tif (write_message(p, strlen(p), rebase_path_stopped_sha(), 1) < 0)\n> +\t\treturn -1;\n> +\n> +\tstrbuf_addf(&buf, \"%s/patch\", get_dir(opts));\n> +\tmemset(&log_tree_opt, 0, sizeof(log_tree_opt));\n> +\tinit_revisions(&log_tree_opt, NULL);\n> +\tlog_tree_opt.abbrev = 0;\n> +\tlog_tree_opt.diff = 1;\n> +\tlog_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;\n> +\tlog_tree_opt.disable_stdin = 1;\n> +\tlog_tree_opt.no_commit_id = 1;\n> +\tlog_tree_opt.diffopt.file = fopen(buf.buf, \"w\");\n> +\tlog_tree_opt.diffopt.use_color = GIT_COLOR_NEVER;\n> +\tif (!log_tree_opt.diffopt.file)\n> +\t\tres |= error_errno(_(\"could not open '%s'\"), buf.buf);\n> +\telse {\n> +\t\tres |= log_tree_commit(&log_tree_opt, commit);\n> +\t\tfclose(log_tree_opt.diffopt.file);\n> +\t}\n> +\tstrbuf_reset(&buf);\n> +\tstrbuf_addf(&buf, \"%s/message\", get_dir(opts));\n> +\tif (!file_exists(buf.buf)) {\n> +\t\tfind_commit_subject(commit_buffer, &subject);\n> +\t\tres |= write_message(subject, strlen(subject), buf.buf, 1);\n> +\t\tunuse_commit_buffer(commit, commit_buffer);\n> +\t}\n> +\tstrbuf_release(&buf);\n> +\n> +\treturn res;\n> +}\n\nOK.  This seems to match what scripted make_patch does in a handful\nof lines.  We probably should have given you a helper to reduce\nboilerplate that sets up log_tree_opt so that this function does not\nhave to be this long, but that is a separate topic.\n\nDoes it matter output_format is set to FORMAT_PATCH here, though?\nWith --no-commit-id set, I suspect there is no log message or\nauthorship information given to the output.\n\nAs you are only interested in seeing the patch/diff in this file and\nthe log is stored in a separate \"message\" file, as long as \"patch\"\nfile gets the patch correctly, it is not a problem, but it just\nlooked strange to see FORMAT_PATCH there.\n\n"},{"id":"307690","messageId":"xmqq7f731pis.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"54d4e8d3673662d1ec806f3f4a779a17effbdaf2.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 04/34] sequencer (rebase -i): implement the 'exec' command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-13T21:35:07Z","receivedAt":"2016-12-13T21:42:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> +static int do_exec(const char *command_line)\n> +{\n> +\tconst char *child_argv[] = { NULL, NULL };\n> +\tint dirty, status;\n> +\n> +\tfprintf(stderr, \"Executing: %s\\n\", command_line);\n> +\tchild_argv[0] = command_line;\n> +\tstatus = run_command_v_opt(child_argv, RUN_USING_SHELL);\n> +\n> +\t/* force re-reading of the cache */\n> +\tif (discard_cache() < 0 || read_cache() < 0)\n> +\t\treturn error(_(\"could not read index\"));\n> +\n> +\tdirty = require_clean_work_tree(\"rebase\", NULL, 1, 1);\n> +\n> +\tif (status) {\n> +\t\twarning(_(\"execution failed: %s\\n%s\"\n> +\t\t\t  \"You can fix the problem, and then run\\n\"\n> +\t\t\t  \"\\n\"\n> +\t\t\t  \"  git rebase --continue\\n\"\n> +\t\t\t  \"\\n\"),\n> +\t\t\tcommand_line,\n> +\t\t\tdirty ? N_(\"and made changes to the index and/or the \"\n> +\t\t\t\t\"working tree\\n\") : \"\");\n> +\t\tif (status == 127)\n> +\t\t\t/* command not found */\n> +\t\t\tstatus = 1;\n> +\t}\n> +\telse if (dirty) {\n> +\t\twarning(_(\"execution succeeded: %s\\nbut \"\n> +\t\t\t  \"left changes to the index and/or the working tree\\n\"\n> +\t\t\t  \"Commit or stash your changes, and then run\\n\"\n> +\t\t\t  \"\\n\"\n> +\t\t\t  \"  git rebase --continue\\n\"\n> +\t\t\t  \"\\n\"), command_line);\n> +\t\tstatus = 1;\n> +\t}\n> +\n> +\treturn status;\n> +}\n\nOK, this looks like a faithful reproduction of what the scripted\nversion does inside do_next() helper function.\n\nPlease have \"else if\" on the same line as \"}\" that closes the\n\"if (...) {\" in the same if/else if/else cascade.\n"},{"id":"307692","messageId":"xmqqy3zjzebq.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"4a1229e9f2d3715607935519f359b5d7986c2290.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 06/34] sequencer (rebase -i): write the 'done' file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-13T21:52:57Z","receivedAt":"2016-12-13T21:53:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> In the interactive rebase, commands that were successfully processed are\n> not simply discarded, but appended to the 'done' file instead. This is\n> used e.g. to display the current state to the user in the output of\n> `git status` or the progress.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n\nLooks good.\n\nI'd stop at this point for now, and start working on other things\nfor the rest of the day.  I might find time to come back to it later\ntonight.\n\nSo far, looking mostly good.\n"},{"id":"307693","messageId":"xmqqtwa7ze9i.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"CA+55aFzxFFNY+dL6s7dLZeVXBsBKD0aeof5Bj2wcD1CpefVSAA@mail.gmail.com","subject":"Re: [PATCH v2 02/34] sequencer (rebase -i): implement the 'noop' command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-13T21:54:17Z","receivedAt":"2016-12-13T21:54:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> On Tue, Dec 13, 2016 at 12:38 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\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>>\n>> Good thinking.  Makes me wish C were a better language, though ;-)\n>\n> Do this:\n>\n>   static const char *todo_command_strings[] = {\n>       [TODO_PICK] = \"pick\",\n>       [TODO_REVERT] = \"revert\",\n>       [TODO_NOOP] = \"noop:,\n>   };\n>\n> which makes the array be order-independent. You still need to make\n> sure you fill in all the entries, of course, but it tends to avoid at\n> least one gotcha, and it makes it more obvious how the two are tied\n> together.\n>\n>               Linus\n\nYes, I know.  But I do not think the variant of C we stick to is not\nnew enough to have that.\n"},{"id":"307694","messageId":"xmqq37hr1orb.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"1d1f8d8b0696769bb85dd8a2269dc281aa91eede.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 05/34] sequencer (rebase -i): learn about the 'verbose' mode","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-13T21:51:36Z","receivedAt":"2016-12-13T21:57:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> @@ -1493,9 +1498,26 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n>  \t}\n>  \n>  \tif (is_rebase_i(opts)) {\n> +\t\tstruct strbuf buf = STRBUF_INIT;\n> +\n>  \t\t/* Stopped in the middle, as planned? */\n>  \t\tif (todo_list->current < todo_list->nr)\n>  \t\t\treturn 0;\n> +\n> +\t\tif (opts->verbose) {\n> +\t\t\tconst char *argv[] = {\n> +\t\t\t\t\"diff-tree\", \"--stat\", NULL, NULL\n> +\t\t\t};\n> +\n> +\t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0))\n> +\t\t\t\treturn error(_(\"could not read '%s'\"),\n> +\t\t\t\t\trebase_path_orig_head());\n> +\t\t\tstrbuf_addstr(&buf, \"..HEAD\");\n> +\t\t\targv[2] = buf.buf;\n> +\t\t\trun_command_v_opt(argv, RUN_GIT_CMD);\n> +\t\t\tstrbuf_reset(&buf);\n> +\t\t}\n> +\t\tstrbuf_release(&buf);\n>  \t}\n\nIt's a bit curious that the previous step avoided running a separate\nprocess and instead did \"diff-tree -p\" all in C, but this one does not.\n\nI think it is because this one is outside the loop?  The original,\nbeing a scripted Porcelain, formulates a lazy and loose command\nline, but you may want to tighten it up a bit if you spawn a\nprocess.  If your user happens to have a file whose name is\n$orig_head..HEAD, the command line you are creating (which is\nidentical to the scripted version) will barf with \"ambiguous\nargument\".\n\nOne good thing about a complete C rewrite is that it won't have an\nissue like this one because you'd be working with in-core objects.\n\n> diff --git a/sequencer.h b/sequencer.h\n> index cb21cfddee..f885b68395 100644\n> --- a/sequencer.h\n> +++ b/sequencer.h\n> @@ -24,6 +24,7 @@ struct replay_opts {\n>  \tint allow_empty;\n>  \tint allow_empty_message;\n>  \tint keep_redundant_commits;\n> +\tint verbose;\n>  \n>  \tint mainline;\n"},{"id":"307737","messageId":"xmqq8trjyp13.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"xmqq37hr1orb.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 05/34] sequencer (rebase -i): learn about the 'verbose' mode","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-14T06:59:20Z","receivedAt":"2016-12-14T07:02:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> ...\n>> +\t\tif (opts->verbose) {\n>> +\t\t\tconst char *argv[] = {\n>> +\t\t\t\t\"diff-tree\", \"--stat\", NULL, NULL\n>> +\t\t\t};\n>> + ...\n>> +\t\t\trun_command_v_opt(argv, RUN_GIT_CMD);\n>> +\t\t\tstrbuf_reset(&buf);\n>> +\t\t}\n>> +\t\tstrbuf_release(&buf);\n>>  \t}\n>\n> It's a bit curious that the previous step avoided running a separate\n> process and instead did \"diff-tree -p\" all in C, but this one does not.\n>\n> I think it is because this one is outside the loop?\n\nNah, this guess of mine \"The patch file generation done in 03/34\navoids spawn because it is in a loop\" is off the mark.  It is done\nbefore \"edit\" gives control back to the end user and it is not like\nwe write one patch file per iteration of the loop we want to get\nmaximum speed out of.\n\n"},{"id":"307738","messageId":"9d399b83-92c6-44e9-8415-7695e690e8be@kdbg.org","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 00/34] Teach the sequencer to act as rebase -i's backend","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2016-12-14T07:08:33Z","receivedAt":"2016-12-14T07:08:45Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 13.12.2016 um 16:29 schrieb Johannes Schindelin:\n> base-commit: 8d7a455ed52e2a96debc080dfc011b6bb00db5d2\n> Published-As: https://github.com/dscho/git/releases/tag/sequencer-i-v2\n> Fetch-It-Via: git fetch https://github.com/dscho/git sequencer-i-v2\n\nThank you so much!\n\nI would appreciate if you could publish a branch that contains the end \ngame so that I can test it, too. Currently I am still using\n\n  git://github.com/dscho/git interactive-rebase (fca871a3cf4d)\n\n-- Hannes\n\n"},{"id":"307744","messageId":"2637bed1-c36f-32f6-b255-ea32da76d792@kdbg.org","threadId":"43966","inReplyTo":"1e82aeabb906a35175362418b2b4957fae50c3b0.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 28/34] run_command_opt(): optionally hide stderr when the command succeeds","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2016-12-14T08:34:20Z","receivedAt":"2016-12-14T08:34:29Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 13.12.2016 um 16:32 schrieb Johannes Schindelin:\n> This will be needed to hide the output of `git commit` when the\n> sequencer handles an interactive rebase's script.\n> \n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  run-command.c | 23 +++++++++++++++++++++++\n>  run-command.h |  1 +\n>  2 files changed, 24 insertions(+)\n> \n> diff --git a/run-command.c b/run-command.c\n> index ca905a9e80..5bb957afdd 100644\n> --- a/run-command.c\n> +++ b/run-command.c\n> @@ -589,6 +589,29 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const\n>  \tcmd.clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0;\n>  \tcmd.dir = dir;\n>  \tcmd.env = env;\n> +\n> +\tif (opt & RUN_HIDE_STDERR_ON_SUCCESS) {\n> +\t\tstruct strbuf buf = STRBUF_INIT;\n> +\t\tint res;\n> +\n> +\t\tcmd.err = -1;\n> +\t\tif (start_command(&cmd) < 0)\n> +\t\t\treturn -1;\n> +\n> +\t\tif (strbuf_read(&buf, cmd.err, 0) < 0) {\n> +\t\t\tclose(cmd.err);\n> +\t\t\tfinish_command(&cmd); /* throw away exit code */\n> +\t\t\treturn -1;\n> +\t\t}\n> +\n> +\t\tclose(cmd.err);\n> +\t\tres = finish_command(&cmd);\n> +\t\tif (res)\n> +\t\t\tfputs(buf.buf, stderr);\n> +\t\tstrbuf_release(&buf);\n> +\t\treturn res;\n> +\t}\n> +\n>  \treturn run_command(&cmd);\n>  }\n\nClearly, this is a bolted-on feature. It's not the worst move that you\ndid not advertise the flag in Documentation/technical/api-run-command.txt...\n\nI wanted to see what it would look like if we make it the caller's\nresponsibility to throw away stderr. The patch is below, as fixup\nof patch 29/34. The change is gross, but the end result is not that\nbad, though not really a delightful read, either, mostly due to the\nstrange cleanup semantics of the start_command/finish_command combo,\nso... I dunno.\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 41be4cde16..f375880bd7 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -660,15 +660,16 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \t\t\t  int cleanup_commit_message)\n {\n \tchar **env = NULL;\n-\tstruct argv_array array;\n-\tint opt = RUN_GIT_CMD, rc;\n+\tint rc;\n \tconst char *value;\n+\tstruct strbuf errout = STRBUF_INIT;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n+\n+\tcmd.git_cmd = 1;\n \n \tif (is_rebase_i(opts)) {\n-\t\tif (!edit) {\n-\t\t\topt |= RUN_COMMAND_STDOUT_TO_STDERR;\n-\t\t\topt |= RUN_HIDE_STDERR_ON_SUCCESS;\n-\t\t}\n+\t\tif (!edit)\n+\t\t\tcmd.stdout_to_stderr = 1;\n \n \t\tenv = read_author_script();\n \t\tif (!env) {\n@@ -679,36 +680,58 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \t\t}\n \t}\n \n-\targv_array_init(&array);\n-\targv_array_push(&array, \"commit\");\n-\targv_array_push(&array, \"-n\");\n+\targv_array_push(&cmd.args, \"commit\");\n+\targv_array_push(&cmd.args, \"-n\");\n \n \tif (amend)\n-\t\targv_array_push(&array, \"--amend\");\n+\t\targv_array_push(&cmd.args, \"--amend\");\n \tif (opts->gpg_sign)\n-\t\targv_array_pushf(&array, \"-S%s\", opts->gpg_sign);\n+\t\targv_array_pushf(&cmd.args, \"-S%s\", opts->gpg_sign);\n \tif (opts->signoff)\n-\t\targv_array_push(&array, \"-s\");\n+\t\targv_array_push(&cmd.args, \"-s\");\n \tif (defmsg)\n-\t\targv_array_pushl(&array, \"-F\", defmsg, NULL);\n+\t\targv_array_pushl(&cmd.args, \"-F\", defmsg, NULL);\n \tif (cleanup_commit_message)\n-\t\targv_array_push(&array, \"--cleanup=strip\");\n+\t\targv_array_push(&cmd.args, \"--cleanup=strip\");\n \tif (edit)\n-\t\targv_array_push(&array, \"-e\");\n+\t\targv_array_push(&cmd.args, \"-e\");\n \telse if (!cleanup_commit_message &&\n \t\t !opts->signoff && !opts->record_origin &&\n \t\t git_config_get_value(\"commit.cleanup\", &value))\n-\t\targv_array_push(&array, \"--cleanup=verbatim\");\n+\t\targv_array_push(&cmd.args, \"--cleanup=verbatim\");\n \n \tif (allow_empty)\n-\t\targv_array_push(&array, \"--allow-empty\");\n+\t\targv_array_push(&cmd.args, \"--allow-empty\");\n \n \tif (opts->allow_empty_message)\n-\t\targv_array_push(&array, \"--allow-empty-message\");\n+\t\targv_array_push(&cmd.args, \"--allow-empty-message\");\n+\n+\tcmd.env = (const char *const *)env;\n+\n+\tif (cmd.stdout_to_stderr) {\n+\t\t/* hide stderr on success */\n+\t\tcmd.err = -1;\n+\t\trc = -1;\n+\t\tif (start_command(&cmd) < 0)\n+\t\t\tgoto cleanup;\n+\n+\t\tif (strbuf_read(&errout, cmd.err, 0) < 0) {\n+\t\t\tclose(cmd.err);\n+\t\t\tfinish_command(&cmd); /* throw away exit code */\n+\t\t\tgoto cleanup;\n+\t\t}\n+\n+\t\tclose(cmd.err);\n+\t\trc = finish_command(&cmd);\n+\t\tif (rc)\n+\t\t\tfputs(errout.buf, stderr);\n+\t} else {\n+\t\trc = run_command(&cmd);\n+\t}\n \n-\trc = run_command_v_opt_cd_env(array.argv, opt, NULL,\n-\t\t\t(const char *const *)env);\n-\targv_array_clear(&array);\n+cleanup:\n+\tchild_process_clear(&cmd);\n+\tstrbuf_release(&errout);\n \tfree(env);\n \n \treturn rc;\n-- \n2.11.0.79.g263f27a\n\n"},{"id":"307752","messageId":"20161214125322.o3naglvyuzgk2pri@sigill.intra.peff.net","threadId":"43966","inReplyTo":"2637bed1-c36f-32f6-b255-ea32da76d792@kdbg.org","subject":"Re: [PATCH v2 28/34] run_command_opt(): optionally hide stderr when the command succeeds","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-12-14T12:53:23Z","receivedAt":"2016-12-14T12:54:04Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Dec 14, 2016 at 09:34:20AM +0100, Johannes Sixt wrote:\n\n> I wanted to see what it would look like if we make it the caller's\n> responsibility to throw away stderr. The patch is below, as fixup\n> of patch 29/34. The change is gross, but the end result is not that\n> bad, though not really a delightful read, either, mostly due to the\n> strange cleanup semantics of the start_command/finish_command combo,\n> so... I dunno.\n\nI don't have a strong opinion on the patches under discussion, but here\nare a few pointers on the run-command interface:\n\n> +\tstruct child_process cmd = CHILD_PROCESS_INIT;\n> [...]\n>  \t\tenv = read_author_script();\n>  \t\tif (!env) {\n\nThe child_process struct comes with its own internal env array. So you\ncan do:\n\n  read_author_script(&cmd.env_array);\n  if (!cmd.env_array.argc)\n\t...\n\nand then you don't have to worry about free-ing env, as it happens\nautomatically as part of the child cleanup (I suspect the refactoring\nmay also reduce some of the confusing memory handling in\nread_author_script()).\n\n> +\tif (cmd.stdout_to_stderr) {\n> +\t\t/* hide stderr on success */\n> +\t\tcmd.err = -1;\n> +\t\trc = -1;\n> +\t\tif (start_command(&cmd) < 0)\n> +\t\t\tgoto cleanup;\n> +\n> +\t\tif (strbuf_read(&errout, cmd.err, 0) < 0) {\n> +\t\t\tclose(cmd.err);\n> +\t\t\tfinish_command(&cmd); /* throw away exit code */\n> +\t\t\tgoto cleanup;\n> +\t\t}\n> +\n> +\t\tclose(cmd.err);\n> +\t\trc = finish_command(&cmd);\n> +\t\tif (rc)\n> +\t\t\tfputs(errout.buf, stderr);\n> +\t} else {\n> +\t\trc = run_command(&cmd);\n> +\t}\n\nWe have a helper function for capturing output, so I think you can write\nthis as:\n\n  if (cmd.err == -1) {\n        struct strbuf errout = STRBUF_INIT;\n        int rc = pipe_command(&cmd,\n\t\t              NULL, 0, /* stdin */\n                              NULL, 0, /* stdout */\n                              &errout, 0);\n        if (rc)\n                fputs(errout.buf, stderr);\n        strbuf_release(&errout);\n  } else\n        rc = run_command(&cmd);\n\nand drop the cleanup goto entirely (if you do the \"env\" thing above, you\ncould even drop \"rc\" and just return directly from each branch of the\nconditional).\n\n> -\trc = run_command_v_opt_cd_env(array.argv, opt, NULL,\n> -\t\t\t(const char *const *)env);\n> -\targv_array_clear(&array);\n> +cleanup:\n> +\tchild_process_clear(&cmd);\n> +\tstrbuf_release(&errout);\n>  \tfree(env);\n\nEven if you do keep the goto here, I think this child_process_clear() is\nunnecessary. It should be done automatically either by finish_command(),\nor by start_command() when it returns an error.\n\n-Peff\n"},{"id":"307771","messageId":"20161214130640.ginadvry7wor3tkc@sigill.intra.peff.net","threadId":"43966","inReplyTo":"20161214125322.o3naglvyuzgk2pri@sigill.intra.peff.net","subject":"Re: [PATCH v2 28/34] run_command_opt(): optionally hide stderr when the command succeeds","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-12-14T13:06:40Z","receivedAt":"2016-12-14T13:06:49Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Dec 14, 2016 at 07:53:23AM -0500, Jeff King wrote:\n\n> On Wed, Dec 14, 2016 at 09:34:20AM +0100, Johannes Sixt wrote:\n> \n> > I wanted to see what it would look like if we make it the caller's\n> > responsibility to throw away stderr. The patch is below, as fixup\n> > of patch 29/34. The change is gross, but the end result is not that\n> > bad, though not really a delightful read, either, mostly due to the\n> > strange cleanup semantics of the start_command/finish_command combo,\n> > so... I dunno.\n> \n> I don't have a strong opinion on the patches under discussion, but here\n> are a few pointers on the run-command interface:\n> [...]\n\nAnd here is a patch representing my suggestions, on top of yours. Not\ntested beyond \"make test\".\n\nI think read_author_script() could be simplified even more by appending\nto the env array in the first loop, but I didn't want to refactor the\nquote handling.\n\n---\n sequencer.c | 65 +++++++++++------------------------\n 1 file changed, 20 insertions(+), 45 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex f375880bd..27a9eaf15 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -591,18 +591,17 @@ static int write_author_script(const char *message)\n }\n \n /*\n- * Read the author-script file into an environment block, ready for use in\n- * run_command(), that can be free()d afterwards.\n+ * Read the author-script file into an environment block. Returns -1\n+ * on error, 0 otherwise.\n  */\n-static char **read_author_script(void)\n+static int read_author_script(struct argv_array *env)\n {\n \tstruct strbuf script = STRBUF_INIT;\n \tint i, count = 0;\n-\tchar *p, *p2, **env;\n-\tsize_t env_size;\n+\tchar *p, *p2;\n \n \tif (strbuf_read_file(&script, rebase_path_author_script(), 256) <= 0)\n-\t\treturn NULL;\n+\t\treturn -1;\n \n \tfor (p = script.buf; *p; p++)\n \t\tif (skip_prefix(p, \"'\\\\\\\\''\", (const char **)&p2))\n@@ -614,19 +613,12 @@ static char **read_author_script(void)\n \t\t\tcount++;\n \t\t}\n \n-\tenv_size = (count + 1) * sizeof(*env);\n-\tstrbuf_grow(&script, env_size);\n-\tmemmove(script.buf + env_size, script.buf, script.len);\n-\tp = script.buf + env_size;\n-\tenv = (char **)strbuf_detach(&script, NULL);\n-\n \tfor (i = 0; i < count; i++) {\n-\t\tenv[i] = p;\n+\t\targv_array_push(env, p);\n \t\tp += strlen(p) + 1;\n \t}\n-\tenv[count] = NULL;\n \n-\treturn env;\n+\treturn 0;\n }\n \n static const char staged_changes_advice[] =\n@@ -659,20 +651,18 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \t\t\t  int allow_empty, int edit, int amend,\n \t\t\t  int cleanup_commit_message)\n {\n-\tchar **env = NULL;\n-\tint rc;\n \tconst char *value;\n-\tstruct strbuf errout = STRBUF_INIT;\n \tstruct child_process cmd = CHILD_PROCESS_INIT;\n \n \tcmd.git_cmd = 1;\n \n \tif (is_rebase_i(opts)) {\n-\t\tif (!edit)\n+\t\tif (!edit) {\n \t\t\tcmd.stdout_to_stderr = 1;\n+\t\t\tcmd.err = -1;\n+\t\t}\n \n-\t\tenv = read_author_script();\n-\t\tif (!env) {\n+\t\tif (read_author_script(&cmd.env_array)) {\n \t\t\tconst char *gpg_opt = gpg_sign_opt_quoted(opts);\n \n \t\t\treturn error(_(staged_changes_advice),\n@@ -706,35 +696,20 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \tif (opts->allow_empty_message)\n \t\targv_array_push(&cmd.args, \"--allow-empty-message\");\n \n-\tcmd.env = (const char *const *)env;\n-\n-\tif (cmd.stdout_to_stderr) {\n+\tif (cmd.err == -1) {\n \t\t/* hide stderr on success */\n-\t\tcmd.err = -1;\n-\t\trc = -1;\n-\t\tif (start_command(&cmd) < 0)\n-\t\t\tgoto cleanup;\n-\n-\t\tif (strbuf_read(&errout, cmd.err, 0) < 0) {\n-\t\t\tclose(cmd.err);\n-\t\t\tfinish_command(&cmd); /* throw away exit code */\n-\t\t\tgoto cleanup;\n-\t\t}\n-\n-\t\tclose(cmd.err);\n-\t\trc = finish_command(&cmd);\n+\t\tstruct strbuf errout = STRBUF_INIT;\n+\t\tint rc = pipe_command(&cmd,\n+\t\t\t\t      NULL, 0, /* stdin */\n+\t\t\t\t      NULL, 0, /* stdout */\n+\t\t\t\t      &errout, 0);\n \t\tif (rc)\n \t\t\tfputs(errout.buf, stderr);\n-\t} else {\n-\t\trc = run_command(&cmd);\n+\t\tstrbuf_release(&errout);\n+\t\treturn rc;\n \t}\n \n-cleanup:\n-\tchild_process_clear(&cmd);\n-\tstrbuf_release(&errout);\n-\tfree(env);\n-\n-\treturn rc;\n+\treturn run_command(&cmd);\n }\n \n static int is_original_commit_empty(struct commit *commit)\n"},{"id":"307801","messageId":"f4a72743-3488-3466-5b9f-0dacec102a54@kdbg.org","threadId":"43966","inReplyTo":"20161214130640.ginadvry7wor3tkc@sigill.intra.peff.net","subject":"Re: [PATCH v2 28/34] run_command_opt(): optionally hide stderr when the command succeeds","fromName":"Johannes Sixt","fromEmail":"j6t@kdbg.org","sentAt":"2016-12-14T19:17:00Z","receivedAt":"2016-12-14T19:19:09Z","isPatch":true,"sender":{"key":"j6t@kdbg.org","avatar":"https://avatars.githubusercontent.com/u/14810926?v=4"},"body":"Am 14.12.2016 um 14:06 schrieb Jeff King:\n> On Wed, Dec 14, 2016 at 07:53:23AM -0500, Jeff King wrote:\n>\n>> On Wed, Dec 14, 2016 at 09:34:20AM +0100, Johannes Sixt wrote:\n>>\n>>> I wanted to see what it would look like if we make it the caller's\n>>> responsibility to throw away stderr. The patch is below, as fixup\n>>> of patch 29/34. The change is gross, but the end result is not that\n>>> bad, though not really a delightful read, either, mostly due to the\n>>> strange cleanup semantics of the start_command/finish_command combo,\n>>> so... I dunno.\n\nThe cleanup semantics of start_command and finish_command are not that \nstrange as I thought first. I just hadn't looked well enough.\n\n>>\n>> I don't have a strong opinion on the patches under discussion, but here\n>> are a few pointers on the run-command interface:\n>> [...]\n >\n> And here is a patch representing my suggestions, on top of yours. Not\n> tested beyond \"make test\".\n\nThank you, that looks way better.\n\nIf there is agreement that this approach is preferable, I think we can \nhave patches on top of the series; they would be orthogonal and do not \nhave to take hostage of it. (And it looks like I won't be able to follow \nup until later this week[end].)\n\n-- Hannes\n\n"},{"id":"307802","messageId":"xmqqy3ziwbpk.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"297140020a7312af03136848dcdd0353ee3abdfe.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 01/34] sequencer: support a new action: 'interactive rebase'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-14T19:29:59Z","receivedAt":"2016-12-14T19:30:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> -/* We will introduce the 'interactive rebase' mode later */\n>  static inline int is_rebase_i(const struct replay_opts *opts)\n>  {\n> -\treturn 0;\n> +\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n>  }\n>  \n>  static const char *get_dir(const struct replay_opts *opts)\n>  {\n> +\tif (is_rebase_i(opts))\n> +\t\treturn rebase_path();\n>  \treturn git_path_seq_dir();\n>  }\n\nThis obviously makes the assumption made by 39784cd362 (\"sequencer:\nremove useless get_dir() function\", 2016-12-07) invalid, where the\n\"remove useless\" thought that the callers of this function wants a\nsingle answer that does not depend on opts.\n\nI'll revert that commit from the sb/sequencer-abort-safety topic (as\nthe topic is in 'next' already) to keep this one.  Please holler if\nthat is a mistaken decision.\n\nThanks.\n\n\n\n"},{"id":"307854","messageId":"xmqqlgvhuj82.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"ae521f75a105c6b9e54595d68bda3c5b62f313b6.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-15T18:42:53Z","receivedAt":"2016-12-15T18:43:41Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> diff --git a/sequencer.c b/sequencer.c\n> index f6e20b142a..271c21581d 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -45,6 +45,35 @@ static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n>   */\n>  static GIT_PATH_FUNC(rebase_path_done, \"rebase-merge/done\")\n>  /*\n> + * The commit message that is planned to be used for any changes that\n> + * need to be committed following a user interaction.\n> + */\n> +static GIT_PATH_FUNC(rebase_path_message, \"rebase-merge/message\")\n> +/*\n> + * The file into which is accumulated the suggested commit message for\n> + * squash/fixup commands. When the first of a series of squash/fixups\n\nThe same comment as 03/34 applies here, regarding blank line to\nseparate logical unit.\n\n> +static int update_squash_messages(enum todo_command command,\n> +\t\tstruct commit *commit, struct replay_opts *opts)\n> +{\n> +\tstruct strbuf buf = STRBUF_INIT;\n> +\tint count, res;\n> +\tconst char *message, *body;\n> +\n> +\tif (file_exists(rebase_path_squash_msg())) {\n> +\t\tchar *p, *p2;\n> +\n> +\t\tif (strbuf_read_file(&buf, rebase_path_squash_msg(), 2048) <= 0)\n> +\t\t\treturn error(_(\"could not read '%s'\"),\n> +\t\t\t\trebase_path_squash_msg());\n> +\n> +\t\tif (buf.buf[0] != comment_line_char ||\n> +\t\t    !skip_prefix(buf.buf + 1, \" This is a combination of \",\n> +\t\t\t\t (const char **)&p))\n> +\t\t\treturn error(_(\"unexpected 1st line of squash message:\"\n> +\t\t\t\t       \"\\n\\n\\t%.*s\"),\n> +\t\t\t\t     (int)(strchrnul(buf.buf, '\\n') - buf.buf),\n> +\t\t\t\t     buf.buf);\n> +\t\tcount = strtol(p, &p2, 10);\n> +\n> +\t\tif (count < 1 || *p2 != ' ')\n> +\t\t\treturn error(_(\"invalid 1st line of squash message:\\n\"\n> +\t\t\t\t       \"\\n\\t%.*s\"),\n> +\t\t\t\t     (int)(strchrnul(buf.buf, '\\n') - buf.buf),\n> +\t\t\t\t     buf.buf);\n> +\n> +\t\tsprintf((char *)p, \"%d\", ++count);\n\nDo we know the area pointed at p (which is inside buf) long enough\nnot to overflow?  If the original were 9 and you incremented to get\n10, you would need one extra byte.\n\n> +\t\tif (!*p2)\n> +\t\t\t*p2 = ' ';\n> +\t\telse {\n> +\t\t\t*(++p2) = 'c';\n\np2 points into buf; do we know this increment does not step beyond\nits end?  What is the meaning of a letter 'c' here (I do not see a\ncorresponding one in the scripted update_squash_messages)?\n\n> +\t\t\tstrbuf_insert(&buf, p2 - buf.buf, \" \", 1);\n> +\t\t}\n> +\t}\n> +\telse {\n\nStyle: \"} else {\" (I won't repeat this, as it will become too noisy).\n\n> +\t\tunsigned char head[20];\n> +\t\tstruct commit *head_commit;\n> +\t\tconst char *head_message, *body;\n> +\n> +\t\tif (get_sha1(\"HEAD\", head))\n> +\t\t\treturn error(_(\"need a HEAD to fixup\"));\n> +\t\tif (!(head_commit = lookup_commit_reference(head)))\n> +\t\t\treturn error(_(\"could not read HEAD\"));\n> +\t\tif (!(head_message = get_commit_buffer(head_commit, NULL)))\n> +\t\t\treturn error(_(\"could not read HEAD's commit message\"));\n> +\n> +\t\tbody = strstr(head_message, \"\\n\\n\");\n> +\t\tif (!body)\n> +\t\t\tbody = \"\";\n> +\t\telse\n> +\t\t\tbody = skip_blank_lines(body + 2);\n\nI think I saw you used a helper function find_commit_subject() to do\nthe above in an earlier patch for \"edit\" in this series.  Would it\nmake this part (and another one for \"commit\" we have after this\nif/else) shorter?\n\n>  static int do_pick_commit(enum todo_command command, struct commit *commit,\n> -\t\tstruct replay_opts *opts)\n> +\t\tstruct replay_opts *opts, int final_fixup)\n>  {\n> +\tint edit = opts->edit, cleanup_commit_message = 0;\n> +\tconst char *msg_file = edit ? NULL : git_path_merge_msg();\n>  \tunsigned char head[20];\n>  \tstruct commit *base, *next, *parent;\n>  \tconst char *base_label, *next_label;\n>  \tstruct commit_message msg = { NULL, NULL, NULL, NULL };\n>  \tstruct strbuf msgbuf = STRBUF_INIT;\n> -\tint res, unborn = 0, allow;\n> +\tint res, unborn = 0, amend = 0, allow;\n>  \n>  \tif (opts->no_commit) {\n>  \t\t/*\n> @@ -749,7 +885,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n>  \telse\n>  \t\tparent = commit->parents->item;\n>  \n> -\tif (opts->allow_ff &&\n> +\tif (opts->allow_ff && !is_fixup(command) &&\n>  \t    ((parent && !hashcmp(parent->object.oid.hash, head)) ||\n>  \t     (!parent && unborn)))\n>  \t\treturn fast_forward_to(commit->object.oid.hash, head, unborn, opts);\n> @@ -813,6 +949,28 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n>  \t\t}\n>  \t}\n>  \n> +\tif (is_fixup(command)) {\n> +\t\tif (update_squash_messages(command, commit, opts))\n> +\t\t\treturn -1;\n> +\t\tamend = 1;\n> +\t\tif (!final_fixup)\n> +\t\t\tmsg_file = rebase_path_squash_msg();\n> +\t\telse if (file_exists(rebase_path_fixup_msg())) {\n> +\t\t\tcleanup_commit_message = 1;\n> +\t\t\tmsg_file = rebase_path_fixup_msg();\n> +\t\t}\n> +\t\telse {\n> +\t\t\tconst char *dest = git_path(\"SQUASH_MSG\");\n> +\t\t\tunlink(dest);\n> +\t\t\tif (copy_file(dest, rebase_path_squash_msg(), 0666))\n> +\t\t\t\treturn error(_(\"could not rename '%s' to '%s'\"),\n> +\t\t\t\t\t     rebase_path_squash_msg(), dest);\n\nPerhaps an error from unlink(dest) before copy_file() should also\nresult in an error return?  After all, that unlink() was added to\nmimick the \"cp\" in the scripted one and copy_file() does not want\nto overwrite an existing destination.\n\n> +\t\t\tunlink(git_path(\"MERGE_MSG\"));\n\nErrors from this and other unlink() that emulates \"rm -f\" were\nunchecked in the scripted original, so not checking for errors is\nnot a regression.  I would check for an error if I were writing\nthis, however, because I know I would forget updating these after I\nam done with the series.\n\n> +\t\t\tmsg_file = dest;\n> +\t\t\tedit = 1;\n> +\t\t}\n> +\t}\n> +\n>  \tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n>  \t\tres = do_recursive_merge(base, next, base_label, next_label,\n>  \t\t\t\t\t head, &msgbuf, opts);\n> @@ -868,8 +1026,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n>  \t\tgoto leave;\n>  \t}\n>  \tif (!opts->no_commit)\n> -\t\tres = run_git_commit(opts->edit ? NULL : git_path_merge_msg(),\n> -\t\t\t\t     opts, allow, opts->edit, 0, 0);\n> +\t\tres = run_git_commit(msg_file, opts, allow, edit, amend,\n> +\t\t\t\t     cleanup_commit_message);\n\nThe introduction of msg_file variable made this call (actually, the\nlogic to decide which file is affected) much nicer to understand.\n\n> +\tif (!res && final_fixup) {\n> +\t\tunlink(rebase_path_fixup_msg());\n> +\t\tunlink(rebase_path_squash_msg());\n> +\t}\n>  \n> @@ -1475,6 +1660,21 @@ static int do_exec(const char *command_line)\n>  \treturn status;\n>  }\n>  \n> +static int is_final_fixup(struct todo_list *todo_list)\n> +{\n> +\tint i = todo_list->current;\n> +\n> +\tif (!is_fixup(todo_list->items[i].command))\n> +\t\treturn 0;\n> +\n> +\twhile (++i < todo_list->nr)\n> +\t\tif (is_fixup(todo_list->items[i].command))\n> +\t\t\treturn 0;\n> +\t\telse if (todo_list->items[i].command < TODO_NOOP)\n> +\t\t\tbreak;\n\nWhat follows NOOP are comment and \"drop\" which is another comment in\ndisguise, so this one is excluding all the no-op commands in various\nshapes, which makes sense but is clear only to a reader who bothered\nto go back to \"enum todo_command\" and checked that fact.  If a check\nfor \"is it one of the no-op commands?\" appears only here, a single\nliner comment may be sufficient (but necessary) to help readers.\nOtherwise a single-liner helper function (similar to is_fixup() you\nhave) with a descriptive name would be better than a single liner\ncomment.\n\n\n\n\n"},{"id":"307855","messageId":"xmqqh965uj6e.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"42d43763075f3f2daa2cc06a8afc9bcfd8b20ac2.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 08/34] sequencer (rebase -i): implement the short commands","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-15T18:43:53Z","receivedAt":"2016-12-15T18:44:00Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> For users' convenience, most rebase commands can be abbreviated, e.g.\n> 'p' instead of 'pick' and 'x' instead of 'exec'. Let's teach the\n> sequencer to handle those abbreviated commands just fine.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n\nSensible.\n"},{"id":"307856","messageId":"xmqqd1gtuivc.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"09c2718e119f809093794410ae1a738c1cd122d1.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 09/34] sequencer (rebase -i): write an author-script file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-15T18:50:31Z","receivedAt":"2016-12-15T18:52:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> +\tstrbuf_addstr(&buf, \"GIT_AUTHOR_NAME='\");\n> +\twhile (*message && *message != '\\n' && *message != '\\r')\n> +\t\tif (skip_prefix(message, \" <\", &message))\n> +\t\t\tbreak;\n> +\t\telse if (*message != '\\'')\n> +\t\t\tstrbuf_addch(&buf, *(message++));\n> +\t\telse\n> +\t\t\tstrbuf_addf(&buf, \"'\\\\\\\\%c'\", *(message++));\n> +\tstrbuf_addstr(&buf, \"'\\nGIT_AUTHOR_EMAIL='\");\n> +\twhile (*message && *message != '\\n' && *message != '\\r')\n> +\t\tif (skip_prefix(message, \"> \", &message))\n> +\t\t\tbreak;\n> +\t\telse if (*message != '\\'')\n> +\t\t\tstrbuf_addch(&buf, *(message++));\n> +\t\telse\n> +\t\t\tstrbuf_addf(&buf, \"'\\\\\\\\%c'\", *(message++));\n\nAren't these reading from an in-core commit object?  \n\nIf so, it should use split_ident_line() for consistency with other\nparts of the system to do this parsing.  We should also already have\na helper for simple shell-quoting in quote.c and you would want to\nuse that instead of open coding like this.\n"},{"id":"307857","messageId":"20161215185630.xxeimack63wqwv2e@sigill.intra.peff.net","threadId":"43966","inReplyTo":"xmqqlgvhuj82.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-12-15T18:56:30Z","receivedAt":"2016-12-15T18:56:43Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Dec 15, 2016 at 10:42:53AM -0800, Junio C Hamano wrote:\n\n> > +\t\tsprintf((char *)p, \"%d\", ++count);\n> \n> Do we know the area pointed at p (which is inside buf) long enough\n> not to overflow?  If the original were 9 and you incremented to get\n> 10, you would need one extra byte.\n\nEven if it is enough, I'd ask to please use xsnprintf(). In the off\nchance that there's a programming error, we'd get a nice die(\"BUG\")\ninstead of a buffer overflow (and it makes the code base easier to audit\nfor other overflows).\n\n-Peff\n"},{"id":"307858","messageId":"20161215190351.as76panrcz5rgibj@sigill.intra.peff.net","threadId":"43966","inReplyTo":"ae521f75a105c6b9e54595d68bda3c5b62f313b6.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-12-15T19:03:51Z","receivedAt":"2016-12-15T19:05:21Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Dec 13, 2016 at 04:30:01PM +0100, Johannes Schindelin wrote:\n\n> +\telse {\n> +\t\tunsigned char head[20];\n> +\t\tstruct commit *head_commit;\n> +\t\tconst char *head_message, *body;\n> +\n> +\t\tif (get_sha1(\"HEAD\", head))\n> +\t\t\treturn error(_(\"need a HEAD to fixup\"));\n> +\t\tif (!(head_commit = lookup_commit_reference(head)))\n> +\t\t\treturn error(_(\"could not read HEAD\"));\n> +\t\tif (!(head_message = get_commit_buffer(head_commit, NULL)))\n> +\t\t\treturn error(_(\"could not read HEAD's commit message\"));\n\nThis get_commit_buffer() may allocate a fresh buffer...\n\n> +\t\tbody = strstr(head_message, \"\\n\\n\");\n> +\t\tif (!body)\n> +\t\t\tbody = \"\";\n> +\t\telse\n> +\t\t\tbody = skip_blank_lines(body + 2);\n> +\t\tif (write_message(body, strlen(body),\n> +\t\t\t\t  rebase_path_fixup_msg(), 0))\n> +\t\t\treturn error(_(\"cannot write '%s'\"),\n> +\t\t\t\t     rebase_path_fixup_msg());\n\n...and then this return leaks the result (the other code path hits\nunuse_commit_buffer(), and is fine).\n\nThis leak was noticed by Coverity. It has a _ton_ of false positives\nacross the whole project, but it sends out a mail with new ones every\nfew days, which is usually short enough that I can process it in 30\nseconds or so.\n\nI _think_ that email just goes to me and Stefan right now. You can add\nyourself at:\n\n  https://scan.coverity.com/projects/git?tab=project_settings\n\nif you already have admin access to the project (which I think you\n(Dscho) do).  I wonder if it would be helpful to send that output to the\nlist.\n\n-Peff\n"},{"id":"307859","messageId":"CAGZ79kZ3i-eoxMsVMsb+VBtEVQf2-Fovh_YM5NBN2pSOBHajBg@mail.gmail.com","threadId":"43966","inReplyTo":"20161215190351.as76panrcz5rgibj@sigill.intra.peff.net","subject":"Re: [PATCH v2 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-12-15T19:07:34Z","receivedAt":"2016-12-15T19:07:47Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Thu, Dec 15, 2016 at 11:03 AM, Jeff King <peff@peff.net> wrote:\n> wonder if it would be helpful to send that output to the list.\n\nSure we can try.\n\nAnother project I used to run through coverity (Gerrit), shows\nsimilar characteristics w.r.t. false positives, so people complained\nwhen I was force feeding them the niceties of static analysis.\n\nI'll just try to set it up and see how the mailing list reacts.\n(Not sure if you can just add emails there or if the email has\nto be verified or such.)\n\nThanks,\nStefan\n"},{"id":"307860","messageId":"xmqq1sx9ui19.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"9978c32139c522c08d5a8f685011829cc830bfc0.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 10/34] sequencer (rebase -i): allow continuing with staged changes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-15T19:08:34Z","receivedAt":"2016-12-15T19:08:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> When an interactive rebase is interrupted, the user may stage changes\n> before continuing, and we need to commit those changes in that case.\n>\n> Please note that the nested \"if\" added to the sequencer_continue() is\n> not combined into a single \"if\" because it will be extended with an\n> \"else\" clause in a later patch in this patch series.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  sequencer.c | 40 ++++++++++++++++++++++++++++++++++++++++\n>  1 file changed, 40 insertions(+)\n>\n> diff --git a/sequencer.c b/sequencer.c\n> index 80469b6954..855d3ba503 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -1829,6 +1829,42 @@ static int continue_single_pick(void)\n>  \treturn run_command_v_opt(argv, RUN_GIT_CMD);\n>  }\n>  \n> +static int commit_staged_changes(struct replay_opts *opts)\n> +{\n> +\tint amend = 0;\n> +\n> +\tif (has_unstaged_changes(1))\n> +\t\treturn error(_(\"cannot rebase: You have unstaged changes.\"));\n\nThe scripted one from 'master' seems to say\n\n\t$path_to_the_file: needs update\n\tYou must edit all merge conflicts and then\n\tmark them as resolved using git add\n\nwhen editing an existing commit in this case.  The updated message\nlooks more sensible for the situation, but I wonder if the control\nshould even reach at this point.  \n\nOne bad thing about reviewing this series is that all the comments\nare about codepaths that are not exercised, so they cannot be more\nthan \"they look good\".  A comment \"If the caller does X, this will\nbe better than the original\" (or this will regress, for that matter)\ncannot be validated for its relevance because we won't know the what\nthe caller does in the endgame while reviewing these earlier steps.\n\n\n\n"},{"id":"307861","messageId":"20161215192016.qhbcyo7vb7petuwp@sigill.intra.peff.net","threadId":"43966","inReplyTo":"CAGZ79kZ3i-eoxMsVMsb+VBtEVQf2-Fovh_YM5NBN2pSOBHajBg@mail.gmail.com","subject":"Re: [PATCH v2 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-12-15T19:20:17Z","receivedAt":"2016-12-15T19:27:06Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Thu, Dec 15, 2016 at 11:07:34AM -0800, Stefan Beller wrote:\n\n> On Thu, Dec 15, 2016 at 11:03 AM, Jeff King <peff@peff.net> wrote:\n> > wonder if it would be helpful to send that output to the list.\n> \n> Sure we can try.\n> \n> Another project I used to run through coverity (Gerrit), shows\n> similar characteristics w.r.t. false positives, so people complained\n> when I was force feeding them the niceties of static analysis.\n> \n> I'll just try to set it up and see how the mailing list reacts.\n> (Not sure if you can just add emails there or if the email has\n> to be verified or such.)\n\nI see you added it, but I don't see the confirmation email on the list.\nI wonder if it was HTML mail and vger ate it.\n\n-Peff\n"},{"id":"307862","messageId":"CAGZ79kbebNRJq11sv9O9+dmRsFKF=70skd=zdgL5jkqD_1b3Zg@mail.gmail.com","threadId":"43966","inReplyTo":"20161215192016.qhbcyo7vb7petuwp@sigill.intra.peff.net","subject":"Re: [PATCH v2 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Stefan Beller","fromEmail":"sbeller@google.com","sentAt":"2016-12-15T19:27:01Z","receivedAt":"2016-12-15T19:27:10Z","isPatch":true,"sender":{"key":"stefanbeller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/455868?v=4"},"body":"On Thu, Dec 15, 2016 at 11:20 AM, Jeff King <peff@peff.net> wrote:\n> On Thu, Dec 15, 2016 at 11:07:34AM -0800, Stefan Beller wrote:\n>\n>> On Thu, Dec 15, 2016 at 11:03 AM, Jeff King <peff@peff.net> wrote:\n>> > wonder if it would be helpful to send that output to the list.\n>>\n>> Sure we can try.\n>>\n>> Another project I used to run through coverity (Gerrit), shows\n>> similar characteristics w.r.t. false positives, so people complained\n>> when I was force feeding them the niceties of static analysis.\n>>\n>> I'll just try to set it up and see how the mailing list reacts.\n>> (Not sure if you can just add emails there or if the email has\n>> to be verified or such.)\n>\n> I see you added it, but I don't see the confirmation email on the list.\n> I wonder if it was HTML mail and vger ate it.\n>\n> -Peff\n\nI think I'll setup a dummy gmail account to use for subscription\nand then I'll configure that to forward all email from coverity to the list.\n(the actual complaints about memleaks etc are plain text, so that\nforwarding then should work)\n"},{"id":"307911","messageId":"xmqq8trfsmvr.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"596e3cf410a339c3212eea76394fe49be1c05ef8.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 14/34] sequencer (rebase -i): update refs after a successful rebase","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-16T19:19:04Z","receivedAt":"2016-12-16T19:19:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> An interactive rebase operates on a detached HEAD (to keep the reflog\n> of the original branch relatively clean), and updates the branch only\n> at the end.\n>\n> Now that the sequencer learns to perform interactive rebases, it also\n> needs to learn the trick to update the branch before removing the\n> directory containing the state of the interactive rebase.\n>\n> We introduce a new head_ref variable in a wider scope than necessary at\n> the moment, to allow for a later patch that prints out \"Successfully\n> rebased and updated <ref>\".\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  sequencer.c | 32 +++++++++++++++++++++++++++++++-\n>  1 file changed, 31 insertions(+), 1 deletion(-)\n>\n> diff --git a/sequencer.c b/sequencer.c\n> index a6625e765d..a4e9b326ba 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -100,6 +100,8 @@ static GIT_PATH_FUNC(rebase_path_stopped_sha, \"rebase-merge/stopped-sha\")\n>  static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, \"rebase-merge/gpg_sign_opt\")\n>  static GIT_PATH_FUNC(rebase_path_orig_head, \"rebase-merge/orig-head\")\n>  static GIT_PATH_FUNC(rebase_path_verbose, \"rebase-merge/verbose\")\n> +static GIT_PATH_FUNC(rebase_path_head_name, \"rebase-merge/head-name\")\n> +static GIT_PATH_FUNC(rebase_path_onto, \"rebase-merge/onto\")\n>  \n>  static inline int is_rebase_i(const struct replay_opts *opts)\n>  {\n> @@ -1793,12 +1795,39 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n>  \t}\n>  \n>  \tif (is_rebase_i(opts)) {\n> -\t\tstruct strbuf buf = STRBUF_INIT;\n> +\t\tstruct strbuf head_ref = STRBUF_INIT, buf = STRBUF_INIT;\n>  \n>  \t\t/* Stopped in the middle, as planned? */\n>  \t\tif (todo_list->current < todo_list->nr)\n>  \t\t\treturn 0;\n>  \n> +\t\tif (read_oneliner(&head_ref, rebase_path_head_name(), 0) &&\n> +\t\t\t\tstarts_with(head_ref.buf, \"refs/\")) {\n> +\t\t\tunsigned char head[20], orig[20];\n> +\n> +\t\t\tif (get_sha1(\"HEAD\", head))\n> +\t\t\t\treturn error(_(\"cannot read HEAD\"));\n> +\t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0) ||\n> +\t\t\t\t\tget_sha1_hex(buf.buf, orig))\n> +\t\t\t\treturn error(_(\"could not read orig-head\"));\n> +\t\t\tstrbuf_addf(&buf, \"rebase -i (finish): %s onto \",\n> +\t\t\t\thead_ref.buf);\n> +\t\t\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n> +\t\t\t\treturn error(_(\"could not read 'onto'\"));\n> +\t\t\tif (update_ref(buf.buf, head_ref.buf, head, orig,\n> +\t\t\t\t\tREF_NODEREF, UPDATE_REFS_MSG_ON_ERR))\n> +\t\t\t\treturn error(_(\"could not update %s\"),\n> +\t\t\t\t\thead_ref.buf);\n> +\t\t\tstrbuf_reset(&buf);\n> +\t\t\tstrbuf_addf(&buf,\n> +\t\t\t\t\"rebase -i (finish): returning to %s\",\n> +\t\t\t\thead_ref.buf);\n> +\t\t\tif (create_symref(\"HEAD\", head_ref.buf, buf.buf))\n> +\t\t\t\treturn error(_(\"could not update HEAD to %s\"),\n> +\t\t\t\t\thead_ref.buf);\n\nAll of the above return error() calls leak head_ref.buf; in addition\nsome leak buf.buf, too.\n\n> +\t\t\tstrbuf_reset(&buf);\n> +\t\t}\n> +\n>  \t\tif (opts->verbose) {\n>  \t\t\tconst char *argv[] = {\n>  \t\t\t\t\"diff-tree\", \"--stat\", NULL, NULL\n> @@ -1813,6 +1842,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n>  \t\t\tstrbuf_reset(&buf);\n>  \t\t}\n>  \t\tstrbuf_release(&buf);\n> +\t\tstrbuf_release(&head_ref);\n>  \t}\n>  \n>  \t/*\n"},{"id":"307912","messageId":"xmqq4m23smok.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"091217525a7ff71794b3544680571ce9814a297f.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 15/34] sequencer (rebase -i): leave a patch upon error","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-16T19:23:23Z","receivedAt":"2016-12-16T19:23:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> When doing an interactive rebase, we want to leave a 'patch' file for\n> further inspection by the user (even if we never tried to actually apply\n> that patch, since we're cherry-picking instead).\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n\nYup.  \n\nThe other day, I was kind of surprised to see the \"patch\" file\nproduced when I tried to do \"git rebase -i HEAD^^ HEAD\" with the one\nin current Git (not yours), marked the first one \"edit\", and then it\ngave me control back.  Obviously there was no conflict and I could\njust do \"git show\" if I wanted to see what the original change was,\nbut the \"patch\" file was there.  I personally never have looked at\nthe \"patch\" file in such a situation, and I kind of feel it is\nwasteful, but I can see people expect to find one there whenever\n\"rebase -i\" stops and gives control back.  It is good that you are\npreserving the behaviour.\n\n>  sequencer.c | 3 +++\n>  1 file changed, 3 insertions(+)\n>\n> diff --git a/sequencer.c b/sequencer.c\n> index a4e9b326ba..4361fe0e94 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -1777,6 +1777,9 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n>  \t\t\t\treturn error_failed_squash(item->commit, opts,\n>  \t\t\t\t\titem->arg_len, item->arg);\n>  \t\t\t}\n> +\t\t\telse if (res && is_rebase_i(opts))\n> +\t\t\t\treturn res | error_with_patch(item->commit,\n> +\t\t\t\t\titem->arg, item->arg_len, opts, res, 0);\n>  \t\t}\n>  \t\telse if (item->command == TODO_EXEC) {\n>  \t\t\tchar *end_of_arg = (char *)(item->arg + item->arg_len);\n"},{"id":"307913","messageId":"xmqqd1grsn53.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"81ba5f7ddb3a1a66e878b955094b7ae00f2cd781.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 11/34] sequencer (rebase -i): remove CHERRY_PICK_HEAD when no longer needed","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-16T19:13:28Z","receivedAt":"2016-12-16T19:26:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> The scripted version of the interactive rebase already does that.\n\nSensible.  I was wondering why this wasn't there while reviewing\n10/34, comparing the two (this is not a suggestion to squash this\ninto the previous step).\n\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  sequencer.c | 7 ++++++-\n>  1 file changed, 6 insertions(+), 1 deletion(-)\n>\n> diff --git a/sequencer.c b/sequencer.c\n> index 855d3ba503..abffaf3b40 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -1835,8 +1835,13 @@ static int commit_staged_changes(struct replay_opts *opts)\n>  \n>  \tif (has_unstaged_changes(1))\n>  \t\treturn error(_(\"cannot rebase: You have unstaged changes.\"));\n> -\tif (!has_uncommitted_changes(0))\n> +\tif (!has_uncommitted_changes(0)) {\n> +\t\tconst char *cherry_pick_head = git_path(\"CHERRY_PICK_HEAD\");\n> +\n> +\t\tif (file_exists(cherry_pick_head) && unlink(cherry_pick_head))\n> +\t\t\treturn error(_(\"could not remove CHERRY_PICK_HEAD\"));\n>  \t\treturn 0;\n> +\t}\n>  \n>  \tif (file_exists(rebase_path_amend())) {\n>  \t\tstruct strbuf rev = STRBUF_INIT;\n"},{"id":"307914","messageId":"xmqqzijvr7v0.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"6f7ec89ac91ba1aa860a28c27ab93f60099f1ddb.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 18/34] sequencer (rebase -i): refactor setting the reflog message","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-16T19:28:51Z","receivedAt":"2016-12-16T19:29:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> This makes the code DRYer, with the obvious benefit that we can enhance\n> the code further in a single place.\n>\n> We can also reuse the functionality elsewhere by calling this new\n> function.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  sequencer.c | 33 ++++++++++++++++++++++++++-------\n>  1 file changed, 26 insertions(+), 7 deletions(-)\n>\n> diff --git a/sequencer.c b/sequencer.c\n> index 33fb36dcbe..d20efad562 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -1750,6 +1750,26 @@ static int is_final_fixup(struct todo_list *todo_list)\n>  \treturn 1;\n>  }\n>  \n> +static const char *reflog_message(struct replay_opts *opts,\n> +\tconst char *sub_action, const char *fmt, ...)\n> +{\n> +\tva_list ap;\n> +\tstatic struct strbuf buf = STRBUF_INIT;\n> +\n> +\tva_start(ap, fmt);\n> +\tstrbuf_reset(&buf);\n> +\tstrbuf_addstr(&buf, action_name(opts));\n> +\tif (sub_action)\n> +\t\tstrbuf_addf(&buf, \" (%s)\", sub_action);\n> +\tif (fmt) {\n> +\t\tstrbuf_addstr(&buf, \": \");\n> +\t\tstrbuf_vaddf(&buf, fmt, ap);\n> +\t}\n> +\tva_end(ap);\n> +\n> +\treturn buf.buf;\n> +}\n\nIt is unlikely for a single caller to want to format another reflog\nentry after formating one but before consuming it [*1*], so this\n\"call this, and you can use the return value until you call it the\nnext time without worrying about leakage\" is quite a reasonable\npattern to employ.\n\n[Footnote]\n\n*1* And it is unlikely that this will run in a multi-threaded\n    environment, either ;-)\n\n"},{"id":"307916","messageId":"xmqqvaujr7ed.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"a2ea8bd57461fe0969f3df09a0374005f12ac6c7.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 20/34] sequencer (rebase -i): copy commit notes at end","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-16T19:38:50Z","receivedAt":"2016-12-16T19:38:58Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> +static void flush_rewritten_pending(void) {\n> +\tstruct strbuf buf = STRBUF_INIT;\n> +\tunsigned char newsha1[20];\n> +\tFILE *out;\n> +\n> +\tif (strbuf_read_file(&buf, rebase_path_rewritten_pending(), 82) > 0 &&\n> +\t\t\t!get_sha1(\"HEAD\", newsha1) &&\n> +\t\t\t(out = fopen(rebase_path_rewritten_list(), \"a\"))) {\n\nAn error in fopen() here ...\n\n> + ...\n> +\t}\n> +\tstrbuf_release(&buf);\n> +}\n> +\n> +static void record_in_rewritten(struct object_id *oid,\n> +\t\tenum todo_command next_command) {\n> +\tFILE *out = fopen(rebase_path_rewritten_pending(), \"a\");\n> +\n> +\tif (!out)\n> +\t\treturn;\n\n... and here are ignored as an insignificant error in the scripted\nversion, and this one does the same.  \n\n> +\n> +\tfprintf(out, \"%s\\n\", oid_to_hex(oid));\n> +\tfclose(out);\n> +\n> +\tif (!is_fixup(next_command))\n> +\t\tflush_rewritten_pending();\n> +}\n> +\n>  static int do_pick_commit(enum todo_command command, struct commit *commit,\n>  \t\tstruct replay_opts *opts, int final_fixup)\n>  {\n> @@ -1750,6 +1797,17 @@ static int is_final_fixup(struct todo_list *todo_list)\n>  \treturn 1;\n>  }\n>  \n> +static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n> +{\n> +\tint i;\n> +\n> +\tfor (i = todo_list->current + offset; i < todo_list->nr; i++)\n> +\t\tif (todo_list->items[i].command != TODO_NOOP)\n> +\t\t\treturn todo_list->items[i].command;\n\nMakes me wonder, after having commented on 07/34 regarding the fact\nthat in the end you would end up having three variants of no-op\n(i.e. NOOP, DROP and COMMENT), what definition of a \"command\" this\nfunction uses to return its result, when asked to \"peek\".  I suspect\nthat this will be updated in a later patch to do \"< TODO_NOOP\"\ninstead?  If so, then that answers one question in my comment on\n07/34, i.e.\n\n    If a check for \"is it one of the no-op commands?\" appears only\n    here, a single liner comment may be sufficient (but necessary)\n    to help readers.  Otherwise a single-liner helper function\n    (similar to is_fixup() you have) with a descriptive name would\n    be better than a single liner comment.\n\nThe answer is \"no, it is not just there\" hence the conclusion is \"we\nwant a helper with a descriptive name\".\n"},{"id":"307986","messageId":"f0762491-63ca-0814-0005-b2cbdd4dc505@gmx.net","threadId":"43966","inReplyTo":"xmqqy3ziwbpk.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 01/34] sequencer: support a new action: 'interactive rebase'","fromName":"Stephan Beyer","fromEmail":"s-beyer@gmx.net","sentAt":"2016-12-17T20:23:52Z","receivedAt":"2016-12-17T20:24:20Z","isPatch":true,"sender":{"key":"s-beyer@gmx.net","avatar":"https://avatars.githubusercontent.com/u/143889?v=4"},"body":"Hi,\n\nOn 12/14/2016 08:29 PM, Junio C Hamano wrote:\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n>> -/* We will introduce the 'interactive rebase' mode later */\n>>  static inline int is_rebase_i(const struct replay_opts *opts)\n>>  {\n>> -\treturn 0;\n>> +\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n>>  }\n>>  \n>>  static const char *get_dir(const struct replay_opts *opts)\n>>  {\n>> +\tif (is_rebase_i(opts))\n>> +\t\treturn rebase_path();\n>>  \treturn git_path_seq_dir();\n>>  }\n> \n> This obviously makes the assumption made by 39784cd362 (\"sequencer:\n> remove useless get_dir() function\", 2016-12-07) invalid, where the\n> \"remove useless\" thought that the callers of this function wants a\n> single answer that does not depend on opts.\n> \n> I'll revert that commit from the sb/sequencer-abort-safety topic (as\n> the topic is in 'next' already) to keep this one.  Please holler if\n> that is a mistaken decision.\n\nIt seems to be the right decision if one wants to keep the internal-path\nbackwards-compatibility of \"git rebase -i\" (and it seems that Dscho\nwants to keep it).\n\nHowever, maintaining more than one directory (like \"sequencer\" for\nsequencer state and \"rebase-merge\" for rebase todo and log) can cause\nthe necessity to be even more careful when hacking on sequencer... For\nexample, the cleanup code must delete both of them, not only one of them.\n\nHence, I think that it's a wiser choice to keep one directory for\nsequencer state *and* additional files.\nIf we (a) save everything in \"sequencer\", we break the internal-path\nbackwards-compatbility but we can get rid of get_dir()...\nIf we (b) save everything in \"rebase-merge\" when using rebase -i and\nsave everything in \"sequencer\" for pick/revert, we are 100%\nbackwards-compatible, but we have to change every occurrence of\ngit_path_seq_dir() to get_dir() and the GIT_PATH_FUNC definitions of\ngit_path_{todo,opts,head}_file must be replaced by definitions using\nget_dir().\n\nBest\n  Stephan\n"},{"id":"308021","messageId":"alpine.DEB.2.20.1612191237400.54750@virtualbox","threadId":"43966","inReplyTo":"xmqqtwa7ze9i.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 02/34] sequencer (rebase -i): implement the 'noop' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-19T12:51:14Z","receivedAt":"2016-12-19T12:52:28Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 13 Dec 2016, Junio C Hamano wrote:\n\n> Linus Torvalds <torvalds@linux-foundation.org> writes:\n> \n> > On Tue, Dec 13, 2016 at 12:38 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> >> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\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> >>\n> >> Good thinking.\n\nNot my thinking... This was in direct response to a suggestion by Dennis\nKaarsemaker, I cannot take credit for the idea.\n\n> Makes me wish C were a better language, though ;-)\n> >\n> > Do this:\n> >\n> >   static const char *todo_command_strings[] = {\n> >       [TODO_PICK] = \"pick\",\n> >       [TODO_REVERT] = \"revert\",\n> >       [TODO_NOOP] = \"noop:,\n> >   };\n> >\n> > which makes the array be order-independent. You still need to make\n> > sure you fill in all the entries, of course, but it tends to avoid at\n> > least one gotcha, and it makes it more obvious how the two are tied\n> > together.\n> \n> Yes, I know.  But I do not think the variant of C we stick to is not\n> new enough to have that.\n\nLet me try to express this without double negation: our coding guidelines\nstate very explicitly that we do not use C99 initializers, and it also\nexplains why: we want to support a broader range of compilers. For\ndetails, see:\n\nhttps://github.com/git/git/blob/v2.11.0/Documentation/CodingGuidelines#L179-L181\n\nTBH I briefly considered going the same route as I did for the fsck\nwarnings (taking a lot of heat for the ugliness):\n\nhttps://github.com/git/git/blob/v2.11.0/fsck.c#L17-L85\n\nIt would have looked somewhat like this:\n\n\t#define FOREACH_TODO(FUNC) \\\n\t\tFUNC(PICK, 'p', \"pick\") \\\n\t\tFUNC(REVERT, 0, \"revert\") \\\n\t\tFUNC(EDIT, 'e', \"edit\") \\\n\t\t...\n\t\tFUNC(COMMENT, 0, NULL)\n\n\t#define TODO(id, short, long) TODO_##id,\n\tenum todo_command {\n\t\tFOREACH_TODO(TODO)\n\t\tTODO_MAX\n\t};\n\t#undef TODO\n\n\t#define TODO(id, short, long) { short, long },\n\tstatic struct {\n\t\tchar c;\n\t\tconst char *str;\n\t} todo_command_info[] = {\n\t\tFOREACH_TODO(TODO)\n\t\t{ 0, NULL }\n\t};\n\t#undef TODO\n\nI have to admit that even I prefer the version I provided in my\ncontribution over the \"fsck method\" outlined above.\n\nCiao,\nDscho\n"},{"id":"308022","messageId":"alpine.DEB.2.20.1612191352310.54750@virtualbox","threadId":"43966","inReplyTo":"xmqqa8bz39aw.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 00/34] Teach the sequencer to act as rebase -i's backend","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-19T13:05:30Z","receivedAt":"2016-12-19T13:06:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 13 Dec 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > Apart from mostly cosmetic patches (and the occasional odd bug that I\n> > fixed promptly), I used these patches since mid May to perform all of my\n> > interactive rebases. In mid June, I had the idea to teach rebase -i to\n> > run *both* scripted rebase and rebase--helper and to cross-validate the\n> > results. This slowed down all my interactive rebases since, but helped\n> > me catch three rather obscure bugs (e.g. that git commit --fixup unfolds\n> > long onelines and rebase -i still finds the correct original commit).\n> > ...\n> > Please note that the interdiff vs v1 is only of limited use: too many\n> > things changed in the meantime, in particular the prepare-sequencer\n> > branch that went through a couple of iterations before it found its way\n> > into git.git's master branch. So please take the interdiff with a\n> > mountain range of salt.\n> > ...\n> > Changes since v1:\n> > ...\n> > - removed the beautiful ordinal logic (to print out \"1st\", \"2nd\", \"3rd\"\n> >   etc) and made things consistent with the current `rebase -i`.\n> \n> It was removed because it was too Anglo-centric and unusable in i18n\n> context, no?\n\nYes, but I remember putting in a lot of time to try to come up with the\nmost elegant way to convert a number into an English ordinal in a shell\nfunction. That's where all that wistfulness came from.\n\n> Judging from the list above, interdiff are pretty much all cosmetic\n> and that is why you say it is only of limited use, I guess.\n\nNo, I said that it is only of limited use because I had to fudge things to\ncome up with an interdiff. I had to fudge things because there is no\ninterdiff: it would require the previous iteration of the patch series to\napply cleanly to the current `master`, which it does not. So I rebased the\npatches and left as much intact as possible, which means that the rebased\nchanges do not even compile because they were based on a previous\niteration of the prepare-sequencer patch series that did not make it into\n`master` without substantial changes.\n\n>     ... goes and reads the remainder and finds that these were\n>     ... all minor changes, mostly cosmetic, with a helper function\n>     ... refactored out or two and things of that nature.\n> \n> It is actually a good thing.  We do not want to see it deviate too\n> drastically from what you have been testing for some months.\n\nWell, that ship has sailed. Neither of the patch series\n\"require-clean-work-tree\", \"libify-sequencer\" and \"prepare-sequencer\" made\nit into `master` without substantial deviations from the code I had been\ntesting and improving for half a year. I did not expect the code to be\naccepted unchanged, anyways.\n\nCiao,\nDscho\n"},{"id":"308024","messageId":"alpine.DEB.2.20.1612191408250.54750@virtualbox","threadId":"43966","inReplyTo":"xmqqk2b31sfs.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 01/34] sequencer: support a new action: 'interactive rebase'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-19T13:33:56Z","receivedAt":"2016-12-19T13:35:13Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 13 Dec 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > @@ -395,7 +414,10 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n> >  \n> >  \tif (active_cache_changed &&\n> >  \t    write_locked_index(&the_index, &index_lock, COMMIT_LOCK))\n> > -\t\t/* TRANSLATORS: %s will be \"revert\" or \"cherry-pick\" */\n> > +\t\t/*\n> > +\t\t * TRANSLATORS: %s will be \"revert\", \"cherry-pick\" or\n> > +\t\t * \"rebase -i\".\n> > +\t\t */\n> \n> IIRC, the \"TRANSLATORS:\" comment has to deviate from our coding\n> style due to tool limitation and has to be done like this:\n> \n> > +\t\t/* TRANSLATORS: %s will be \"revert\", \"cherry-pick\" or\n> > +\t\t * \"rebase -i\".\n> > +\t\t */\n\nAargh. I had fixed that in another patch series already, but failed to do\nso in this here patch series. Sorry.\n\nFixed.\n\n> > @@ -1204,6 +1226,9 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)\n> >  \tconst char *todo_path = get_todo_path(opts);\n> >  \tint next = todo_list->current, offset, fd;\n> >  \n> > +\tif (is_rebase_i(opts))\n> > +\t\tnext++;\n> > +\n> \n> This is because...?  Everybody else counts 0-based while rebase-i\n> counts from 1 or something?\n\nNo. Since its conception, edit-patch-series.sh (the artist now known as\nrebase -i) processed each line of the \"todo script\" by writing a new\n\"todo\" file, skipping the first line, and appending it to the \"done\" file.\n\nThe sequencer chooses to write the new \"insn sheet\" after a successful\noperation.\n\nThis is not an option for rebase -i, as it has many error paths and it was\nsimply impossible to express \"save the todo script in case of failure\" in\nshell script.\n\nI added a comment to clarify why the current command is not written to\ngit-rebase-todo.\n\nCiao,\nDscho\n"},{"id":"308025","messageId":"alpine.DEB.2.20.1612191436100.54750@virtualbox","threadId":"43966","inReplyTo":"xmqqfulr1s5z.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 02/34] sequencer (rebase -i): implement the 'noop' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-19T13:37:14Z","receivedAt":"2016-12-19T13:38:39Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 13 Dec 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > @@ -1292,7 +1316,12 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n> >  \t\tstruct todo_item *item = todo_list->items + todo_list->current;\n> >  \t\tif (save_todo(todo_list, opts))\n> >  \t\t\treturn -1;\n> > -\t\tres = do_pick_commit(item->command, item->commit, opts);\n> > +\t\tif (item->command <= TODO_REVERT)\n> > +\t\t\tres = do_pick_commit(item->command, item->commit,\n> > +\t\t\t\t\topts);\n> > +\t\telse if (item->command != TODO_NOOP)\n> > +\t\t\treturn error(_(\"unknown command %d\"), item->command);\n> \n> I wonder if making this a switch() statement is easier to read in\n> the longer run.  The only thing at this point we are gaining by \"not\n> only mapping and enum must match, the orders matter\" is so that this\n> codepath can do the same thing for PICK and REVERT, but these two\n> would become more and more minority as we learn more words.\n\nI doubt that this is easier to read. There are essentially three\ncategories we are handling: exec, comments, and everything else. IMO the\ncurrent code is the easiest to understand.\n\nCiao,\nDscho\n"},{"id":"308028","messageId":"alpine.DEB.2.20.1612191438200.54750@virtualbox","threadId":"43966","inReplyTo":"xmqqbmwf1pqd.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 03/34] sequencer (rebase -i): implement the 'edit' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-19T13:46:08Z","receivedAt":"2016-12-19T13:47:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 13 Dec 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > @@ -43,6 +44,20 @@ static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n> >   */\n> >  static GIT_PATH_FUNC(rebase_path_author_script, \"rebase-merge/author-script\")\n> >  /*\n> \n> It is minor, but please have a blank line to separate the logical\n> blocks.\n\nAh, but where to draw the line? These comments in front of some of the\nrebase_path_* functions clarify the particular role of the corresponding\npaths, but all of those functions belong to the same block of\nrebase_path_* functions. That latter circumstance is what made me decide\nto *not* insert blank lines here, but only a blank line before that block\n(to separate from the sequencer path functions) and after that block (to\nseparate from the rest of the code).\n\n> If you have \"comment for thing A\" before \"thing A\", then having a blank\n> after that before \"comment for thing B\" and \"thing B\" that follow would\n> help.  Otherwise \"thing A\" immediately followed by \"comment for thing B\"\n> are (mis)read together, leading to nonsense.\n\nIn this case, I think it is quite obvious that the comments belong to the\nimmediately following line, and that all of the path functions are part of\na bigger block.\n\n> > + * When an \"edit\" rebase command is being processed, the SHA1 of the\n> > + * commit to be edited is recorded in this file.  When \"git rebase\n> > + * --continue\" is executed, if there are any staged changes then they\n> > + * will be amended to the HEAD commit, but only provided the HEAD\n> > + * commit is still the commit to be edited.  When any other rebase\n> > + * command is processed, this file is deleted.\n> > + */\n> > +static GIT_PATH_FUNC(rebase_path_amend, \"rebase-merge/amend\")\n> > +/*\n> > + * When we stop at a given patch via the \"edit\" command, this file contains\n> > + * the long commit name of the corresponding patch.\n> \n> If you abbreviate an object name to 38-hex that is still long but\n> that is not full; if you meant full 40-hex, better spell it out as\n> \"full\"---that conveys useful information to programmers (e.g. they\n> can just use get_sha1_hex()).\n> \n> But I think you are writing short_commit_name() to it?  So perhaps\n> \"an abbreviated commit object name\"?\n\nFixed.\n\n> > @@ -1301,9 +1318,87 @@ static int save_opts(struct replay_opts *opts)\n> >  \treturn res;\n> >  }\n> >  \n> > +static int make_patch(struct commit *commit, struct replay_opts *opts)\n> > +{\n> > +\tstruct strbuf buf = STRBUF_INIT;\n> > +\tstruct rev_info log_tree_opt;\n> > +\tconst char *commit_buffer = get_commit_buffer(commit, NULL), *subject, *p;\n> > +\tint res = 0;\n> > +\n> > +\tp = short_commit_name(commit);\n> > +\tif (write_message(p, strlen(p), rebase_path_stopped_sha(), 1) < 0)\n> > +\t\treturn -1;\n> > +\n> > +\tstrbuf_addf(&buf, \"%s/patch\", get_dir(opts));\n> > +\tmemset(&log_tree_opt, 0, sizeof(log_tree_opt));\n> > +\tinit_revisions(&log_tree_opt, NULL);\n> > +\tlog_tree_opt.abbrev = 0;\n> > +\tlog_tree_opt.diff = 1;\n> > +\tlog_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;\n> > +\tlog_tree_opt.disable_stdin = 1;\n> > +\tlog_tree_opt.no_commit_id = 1;\n> > +\tlog_tree_opt.diffopt.file = fopen(buf.buf, \"w\");\n> > +\tlog_tree_opt.diffopt.use_color = GIT_COLOR_NEVER;\n> > +\tif (!log_tree_opt.diffopt.file)\n> > +\t\tres |= error_errno(_(\"could not open '%s'\"), buf.buf);\n> > +\telse {\n> > +\t\tres |= log_tree_commit(&log_tree_opt, commit);\n> > +\t\tfclose(log_tree_opt.diffopt.file);\n> > +\t}\n> > +\tstrbuf_reset(&buf);\n> > +\tstrbuf_addf(&buf, \"%s/message\", get_dir(opts));\n> > +\tif (!file_exists(buf.buf)) {\n> > +\t\tfind_commit_subject(commit_buffer, &subject);\n> > +\t\tres |= write_message(subject, strlen(subject), buf.buf, 1);\n> > +\t\tunuse_commit_buffer(commit, commit_buffer);\n> > +\t}\n> > +\tstrbuf_release(&buf);\n> > +\n> > +\treturn res;\n> > +}\n> \n> OK.  This seems to match what scripted make_patch does in a handful\n> of lines.  We probably should have given you a helper to reduce\n> boilerplate that sets up log_tree_opt so that this function does not\n> have to be this long, but that is a separate topic.\n> \n> Does it matter output_format is set to FORMAT_PATCH here, though?\n> With --no-commit-id set, I suspect there is no log message or\n> authorship information given to the output.\n> \n> As you are only interested in seeing the patch/diff in this file and\n> the log is stored in a separate \"message\" file, as long as \"patch\"\n> file gets the patch correctly, it is not a problem, but it just\n> looked strange to see FORMAT_PATCH there.\n\nI am indifferent as to FORMAT_PATCH. It is there simply as a direct\ntranslation of the `diff-tree -p` command in\nhttps://github.com/git/git/blob/v2.11.0/git-rebase--interactive.sh#L188\n\nCiao,\nDscho\n"},{"id":"308031","messageId":"alpine.DEB.2.20.1612191505390.54750@virtualbox","threadId":"43966","inReplyTo":"xmqq7f731pis.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 04/34] sequencer (rebase -i): implement the 'exec' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-19T14:06:22Z","receivedAt":"2016-12-19T14:07:33Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 13 Dec 2016, Junio C Hamano wrote:\n\n> Please have \"else if\" on the same line as \"}\" that closes the\n> \"if (...) {\" in the same if/else if/else cascade.\n\nFixed. This affected quite a few more patches than just this one, and\nadded two more to this already large patch series.\n\nCiao,\nDscho\n"},{"id":"308032","messageId":"alpine.DEB.2.20.1612191508510.54750@virtualbox","threadId":"43966","inReplyTo":"9d399b83-92c6-44e9-8415-7695e690e8be@kdbg.org","subject":"Re: [PATCH v2 00/34] Teach the sequencer to act as rebase -i's backend","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-19T14:09:35Z","receivedAt":"2016-12-19T14:11:13Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Hannes,\n\nOn Wed, 14 Dec 2016, Johannes Sixt wrote:\n\n> Am 13.12.2016 um 16:29 schrieb Johannes Schindelin:\n> > base-commit: 8d7a455ed52e2a96debc080dfc011b6bb00db5d2\n> > Published-As: https://github.com/dscho/git/releases/tag/sequencer-i-v2\n> > Fetch-It-Via: git fetch https://github.com/dscho/git sequencer-i-v2\n> \n> Thank you so much!\n> \n> I would appreciate if you could publish a branch that contains the end game so\n> that I can test it, too. Currently I am still using\n> \n>  git://github.com/dscho/git interactive-rebase (fca871a3cf4d)\n\nThat's the one. Unless I forget, I use the Git garden shears to update\n'interactive-rebase' whenever I update 'sequencer-i', too.\n\nCiao,\nDscho\n"},{"id":"308036","messageId":"alpine.DEB.2.20.1612191515000.54750@virtualbox","threadId":"43966","inReplyTo":"f0762491-63ca-0814-0005-b2cbdd4dc505@gmx.net","subject":"Re: [PATCH v2 01/34] sequencer: support a new action: 'interactive rebase'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-19T14:25:33Z","receivedAt":"2016-12-19T15:21:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Stephan,\n\nOn Sat, 17 Dec 2016, Stephan Beyer wrote:\n\n> On 12/14/2016 08:29 PM, Junio C Hamano wrote:\n> > Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> >> -/* We will introduce the 'interactive rebase' mode later */\n> >>  static inline int is_rebase_i(const struct replay_opts *opts)\n> >>  {\n> >> -\treturn 0;\n> >> +\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n> >>  }\n> >>  \n> >>  static const char *get_dir(const struct replay_opts *opts)\n> >>  {\n> >> +\tif (is_rebase_i(opts))\n> >> +\t\treturn rebase_path();\n> >>  \treturn git_path_seq_dir();\n> >>  }\n> > \n> > This obviously makes the assumption made by 39784cd362 (\"sequencer:\n> > remove useless get_dir() function\", 2016-12-07) invalid, where the\n> > \"remove useless\" thought that the callers of this function wants a\n> > single answer that does not depend on opts.\n> > \n> > I'll revert that commit from the sb/sequencer-abort-safety topic (as\n> > the topic is in 'next' already) to keep this one.  Please holler if\n> > that is a mistaken decision.\n> \n> It seems to be the right decision if one wants to keep the internal-path\n> backwards-compatibility of \"git rebase -i\" (and it seems that Dscho\n> wants to keep it).\n\nYou make it sound as if I had any choice there. But you probably know as\nwell as I do that scripted usage of rebase -i relies on the \"internal-path\nbackwards-compatibility\", and as one of my stated goals was not to break\nanybody's existing rebase -i usage, well, you know.\n\n> However, maintaining more than one directory (like \"sequencer\" for\n> sequencer state and \"rebase-merge\" for rebase todo and log) can cause\n> the necessity to be even more careful when hacking on sequencer... For\n> example, the cleanup code must delete both of them, not only one of them.\n\nThat is incorrect. It depends on the options which directory is used. And\nit is that directory (and not both) that should be cleaned up in the end.\n\nOtherwise you run into a ton of pain e.g. when running a rebase -i with an\n`exec git cherry-pick ...` line: all of a sudden, that little innocuous\nline would simply destroy the state directory of the current rebase -i.\n\nThat's a rather big no-no.\n\n> Hence, I think that it's a wiser choice to keep one directory for\n> sequencer state *and* additional files.\n\nSee above for a good reason not to choose that.\n\n> If we (a) save everything in \"sequencer\", we break the internal-path\n> backwards-compatbility but we can get rid of get_dir()...\n\n... and we break power users' scripts that use rebase -i. Making those\npower users very angry.\n\nIncluding me.\n\n> If we (b) save everything in \"rebase-merge\" when using rebase -i and\n> save everything in \"sequencer\" for pick/revert, we are 100%\n> backwards-compatible, but we have to change every occurrence of\n> git_path_seq_dir() to get_dir()\n\nYes, that is exactly what we have to do. And that is exactly what I did,\ntoo, in the patch series I labeled \"prepare-sequencer\".\n\nThe only three uses of git_path_seq_dir() I left intact are:\n\n- in builtin/commit.c, where it is used to determine whether a cherry-pick\n  is really still in progress, after testing that there is a\n  CHERRY_PICK_HEAD file\n\n- in get_dir() (obviously)\n\n- in create_seq_dir(), where we could technically avoid calling the\n  function a whopping three times and use a parameter instead (not my\n  construction site, though, so I leave this alone)\n\n> and the GIT_PATH_FUNC definitions of git_path_{todo,opts,head}_file must\n> be replaced by definitions using get_dir().\n\nThat is incorrect. The git_path_todo(), git_path_opts() and\ngit_path_head() functions need to be used in the cherry-pick/revert code\npath. So they need to be left as-are.\n\nCiao,\nDscho\n"},{"id":"308054","messageId":"alpine.DEB.2.20.1612191548090.54750@virtualbox","threadId":"43966","inReplyTo":"xmqqlgvhuj82.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-19T16:58:10Z","receivedAt":"2016-12-19T16:59:20Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Thu, 15 Dec 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > diff --git a/sequencer.c b/sequencer.c\n> > index f6e20b142a..271c21581d 100644\n> > --- a/sequencer.c\n> > +++ b/sequencer.c\n> > @@ -45,6 +45,35 @@ static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n> >   */\n> >  static GIT_PATH_FUNC(rebase_path_done, \"rebase-merge/done\")\n> >  /*\n> > + * The commit message that is planned to be used for any changes that\n> > + * need to be committed following a user interaction.\n> > + */\n> > +static GIT_PATH_FUNC(rebase_path_message, \"rebase-merge/message\")\n> > +/*\n> > + * The file into which is accumulated the suggested commit message for\n> > + * squash/fixup commands. When the first of a series of squash/fixups\n> \n> The same comment as 03/34 applies here, regarding blank line to\n> separate logical unit.\n\nSame rationale here: the path functions are one big continuous block, with\ncomments obviously applying to the immediately following line only.\n\n> > +static int update_squash_messages(enum todo_command command,\n> > +\t\tstruct commit *commit, struct replay_opts *opts)\n> > +{\n> > +\tstruct strbuf buf = STRBUF_INIT;\n> > +\tint count, res;\n> > +\tconst char *message, *body;\n> > +\n> > +\tif (file_exists(rebase_path_squash_msg())) {\n> > +\t\tchar *p, *p2;\n> > +\n> > +\t\tif (strbuf_read_file(&buf, rebase_path_squash_msg(), 2048) <= 0)\n> > +\t\t\treturn error(_(\"could not read '%s'\"),\n> > +\t\t\t\trebase_path_squash_msg());\n> > +\n> > +\t\tif (buf.buf[0] != comment_line_char ||\n> > +\t\t    !skip_prefix(buf.buf + 1, \" This is a combination of \",\n> > +\t\t\t\t (const char **)&p))\n> > +\t\t\treturn error(_(\"unexpected 1st line of squash message:\"\n> > +\t\t\t\t       \"\\n\\n\\t%.*s\"),\n> > +\t\t\t\t     (int)(strchrnul(buf.buf, '\\n') - buf.buf),\n> > +\t\t\t\t     buf.buf);\n> > +\t\tcount = strtol(p, &p2, 10);\n> > +\n> > +\t\tif (count < 1 || *p2 != ' ')\n> > +\t\t\treturn error(_(\"invalid 1st line of squash message:\\n\"\n> > +\t\t\t\t       \"\\n\\t%.*s\"),\n> > +\t\t\t\t     (int)(strchrnul(buf.buf, '\\n') - buf.buf),\n> > +\t\t\t\t     buf.buf);\n> > +\n> > +\t\tsprintf((char *)p, \"%d\", ++count);\n> \n> Do we know the area pointed at p (which is inside buf) long enough\n> not to overflow?\n\nYes, we know that: p2 points to the byte after the parsed number, and said\nbyte is a space (ASCII 0x20), as verified by the if() above.\n\n> If the original were 9 and you incremented to get 10, you would need one\n> extra byte.\n\nExactly. That extra byte (if needed) is 0x20, as verified above, and can\nbe overwritten.\n\nIf that extra byte (to which p2 points) is *not* overwritten, i.e. if the\nnew count requires the same amount of space in decimal representation as\nthe previous count, it is now NUL, as tested here:\n\n> > +\t\tif (!*p2)\n> > +\t\t\t*p2 = ' ';\n> > +\t\telse {\n> > +\t\t\t*(++p2) = 'c';\n> \n> p2 points into buf; do we know this increment does not step beyond\n> its end?  What is the meaning of a letter 'c' here (I do not see a\n> corresponding one in the scripted update_squash_messages)?\n\nThis clause is entered only when the space needed by the previous count\nwas not sufficient to hold the new count, at which point we know that not\nonly the space after the old count was overwritten, but also the 'c' of\nthe \"commits\" string.\n\nTherefore, this clause reinstates the 'c' and inserts the space, so that\neverything is groovy again:\n\n> > +\t\t\tstrbuf_insert(&buf, p2 - buf.buf, \" \", 1);\n> > +\t\t}\n\nHaving said that, I just realized that this code was only safe as long as\nthe squash messages were not localized.\n\nI changed the code to imitate more closely what the shell script does. It\nmade the code a little more verbose, but it should work better as a\nconsequence, and I am pretty certain you will find it easier to verify\nthat it is correct.\n\n> > +\t}\n> > +\telse {\n> \n> Style: \"} else {\" (I won't repeat this, as it will become too noisy).\n\nIt is not necessary to repeat this, either, as I took the first such\ncomment as a strong hint to look at the entire patch series and fix it\nappropriately.\n\n> \n> > +\t\tunsigned char head[20];\n> > +\t\tstruct commit *head_commit;\n> > +\t\tconst char *head_message, *body;\n> > +\n> > +\t\tif (get_sha1(\"HEAD\", head))\n> > +\t\t\treturn error(_(\"need a HEAD to fixup\"));\n> > +\t\tif (!(head_commit = lookup_commit_reference(head)))\n> > +\t\t\treturn error(_(\"could not read HEAD\"));\n> > +\t\tif (!(head_message = get_commit_buffer(head_commit, NULL)))\n> > +\t\t\treturn error(_(\"could not read HEAD's commit message\"));\n> > +\n> > +\t\tbody = strstr(head_message, \"\\n\\n\");\n> > +\t\tif (!body)\n> > +\t\t\tbody = \"\";\n> > +\t\telse\n> > +\t\t\tbody = skip_blank_lines(body + 2);\n> \n> I think I saw you used a helper function find_commit_subject() to do\n> the above in an earlier patch for \"edit\" in this series.  Would it\n> make this part (and another one for \"commit\" we have after this\n> if/else) shorter?\n\nRight, and by using the helper function, I also fixed a bug handling funny\ncommit objects that had more than one empty line separating header from\nthe message.\n\nOf course, there is a third location in sequencer.c (predating my patches)\nthat uses the very same idiom. Yet another patch added to this growing\npatch series...\n\n> >  static int do_pick_commit(enum todo_command command, struct commit *commit,\n> > -\t\tstruct replay_opts *opts)\n> > +\t\tstruct replay_opts *opts, int final_fixup)\n> >  {\n> > +\tint edit = opts->edit, cleanup_commit_message = 0;\n> > +\tconst char *msg_file = edit ? NULL : git_path_merge_msg();\n> >  \tunsigned char head[20];\n> >  \tstruct commit *base, *next, *parent;\n> >  \tconst char *base_label, *next_label;\n> >  \tstruct commit_message msg = { NULL, NULL, NULL, NULL };\n> >  \tstruct strbuf msgbuf = STRBUF_INIT;\n> > -\tint res, unborn = 0, allow;\n> > +\tint res, unborn = 0, amend = 0, allow;\n> >  \n> >  \tif (opts->no_commit) {\n> >  \t\t/*\n> > @@ -749,7 +885,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n> >  \telse\n> >  \t\tparent = commit->parents->item;\n> >  \n> > -\tif (opts->allow_ff &&\n> > +\tif (opts->allow_ff && !is_fixup(command) &&\n> >  \t    ((parent && !hashcmp(parent->object.oid.hash, head)) ||\n> >  \t     (!parent && unborn)))\n> >  \t\treturn fast_forward_to(commit->object.oid.hash, head, unborn, opts);\n> > @@ -813,6 +949,28 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n> >  \t\t}\n> >  \t}\n> >  \n> > +\tif (is_fixup(command)) {\n> > +\t\tif (update_squash_messages(command, commit, opts))\n> > +\t\t\treturn -1;\n> > +\t\tamend = 1;\n> > +\t\tif (!final_fixup)\n> > +\t\t\tmsg_file = rebase_path_squash_msg();\n> > +\t\telse if (file_exists(rebase_path_fixup_msg())) {\n> > +\t\t\tcleanup_commit_message = 1;\n> > +\t\t\tmsg_file = rebase_path_fixup_msg();\n> > +\t\t}\n> > +\t\telse {\n> > +\t\t\tconst char *dest = git_path(\"SQUASH_MSG\");\n> > +\t\t\tunlink(dest);\n> > +\t\t\tif (copy_file(dest, rebase_path_squash_msg(), 0666))\n> > +\t\t\t\treturn error(_(\"could not rename '%s' to '%s'\"),\n> > +\t\t\t\t\t     rebase_path_squash_msg(), dest);\n> \n> Perhaps an error from unlink(dest) before copy_file() should also\n> result in an error return?\n\nNo, because the most likely cause for that `unlink()` to fail is that the\ndestination does not exist, and it is fine if it does not exist yet.\n\nNo worries, copy_file() will fail if we could not remove any existing file\nand we still error out in that case.\n\n> > +\t\t\tunlink(git_path(\"MERGE_MSG\"));\n> \n> Errors from this and other unlink() that emulates \"rm -f\" were\n> unchecked in the scripted original, so not checking for errors is\n> not a regression.  I would check for an error if I were writing\n> this, however, because I know I would forget updating these after I\n> am done with the series.\n\nThe problem is that these files do not necessarily exist. We only unlink()\nthem to make sure that they do not exist afterwards.\n\nIn any case, I am still more interested in a faithful translation than\nalready starting to improve the code at this stage.\n\n> > @@ -1475,6 +1660,21 @@ static int do_exec(const char *command_line)\n> >  \treturn status;\n> >  }\n> >  \n> > +static int is_final_fixup(struct todo_list *todo_list)\n> > +{\n> > +\tint i = todo_list->current;\n> > +\n> > +\tif (!is_fixup(todo_list->items[i].command))\n> > +\t\treturn 0;\n> > +\n> > +\twhile (++i < todo_list->nr)\n> > +\t\tif (is_fixup(todo_list->items[i].command))\n> > +\t\t\treturn 0;\n> > +\t\telse if (todo_list->items[i].command < TODO_NOOP)\n> > +\t\t\tbreak;\n> \n> What follows NOOP are comment and \"drop\" which is another comment in\n> disguise, so this one is excluding all the no-op commands in various\n> shapes, which makes sense but is clear only to a reader who bothered\n> to go back to \"enum todo_command\" and checked that fact.  If a check\n> for \"is it one of the no-op commands?\" appears only here, a single\n> liner comment may be sufficient (but necessary) to help readers.\n> Otherwise a single-liner helper function (similar to is_fixup() you\n> have) with a descriptive name would be better than a single liner\n> comment.\n\nTrue. I introduced is_noop().\n\nCiao,\nDscho\n"},{"id":"308055","messageId":"alpine.DEB.2.20.1612191758290.54750@virtualbox","threadId":"43966","inReplyTo":"20161215185630.xxeimack63wqwv2e@sigill.intra.peff.net","subject":"Re: [PATCH v2 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-19T16:59:06Z","receivedAt":"2016-12-19T17:00:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Peff,\n\nOn Thu, 15 Dec 2016, Jeff King wrote:\n\n> On Thu, Dec 15, 2016 at 10:42:53AM -0800, Junio C Hamano wrote:\n> \n> > > +\t\tsprintf((char *)p, \"%d\", ++count);\n> > \n> > Do we know the area pointed at p (which is inside buf) long enough\n> > not to overflow?  If the original were 9 and you incremented to get\n> > 10, you would need one extra byte.\n> \n> Even if it is enough, I'd ask to please use xsnprintf(). In the off\n> chance that there's a programming error, we'd get a nice die(\"BUG\")\n> instead of a buffer overflow (and it makes the code base easier to audit\n> for other overflows).\n\nI ended up with more verbose, easier-to-read code that does not try to do\nthings in-place, in favor of being slightly more wasteful with strbufs.\n\nCiao,\nDscho\n"},{"id":"308057","messageId":"alpine.DEB.2.20.1612191800530.54750@virtualbox","threadId":"43966","inReplyTo":"xmqqd1gtuivc.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 09/34] sequencer (rebase -i): write an author-script file","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-19T17:06:37Z","receivedAt":"2016-12-19T17:07:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Thu, 15 Dec 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > +\tstrbuf_addstr(&buf, \"GIT_AUTHOR_NAME='\");\n> > +\twhile (*message && *message != '\\n' && *message != '\\r')\n> > +\t\tif (skip_prefix(message, \" <\", &message))\n> > +\t\t\tbreak;\n> > +\t\telse if (*message != '\\'')\n> > +\t\t\tstrbuf_addch(&buf, *(message++));\n> > +\t\telse\n> > +\t\t\tstrbuf_addf(&buf, \"'\\\\\\\\%c'\", *(message++));\n> > +\tstrbuf_addstr(&buf, \"'\\nGIT_AUTHOR_EMAIL='\");\n> > +\twhile (*message && *message != '\\n' && *message != '\\r')\n> > +\t\tif (skip_prefix(message, \"> \", &message))\n> > +\t\t\tbreak;\n> > +\t\telse if (*message != '\\'')\n> > +\t\t\tstrbuf_addch(&buf, *(message++));\n> > +\t\telse\n> > +\t\t\tstrbuf_addf(&buf, \"'\\\\\\\\%c'\", *(message++));\n> \n> Aren't these reading from an in-core commit object?  \n> \n> If so, it should use split_ident_line() for consistency with other\n> parts of the system to do this parsing.  We should also already have\n> a helper for simple shell-quoting in quote.c and you would want to\n> use that instead of open coding like this.\n\nWe keep coming back to the same argument. You want this quoting/dequoting\nto be turned into a full-fledged parser. And I keep pointing out that the\ncode here does not *need* to parse but only construct an environment\nblock.\n\nHopefully the next iteration that integrates Peff's suggestions will find\nmore of your approval.\n\nCiao,\nDscho\n"},{"id":"308059","messageId":"alpine.DEB.2.20.1612191812520.54750@virtualbox","threadId":"43966","inReplyTo":"20161215190351.as76panrcz5rgibj@sigill.intra.peff.net","subject":"Re: [PATCH v2 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-19T17:14:10Z","receivedAt":"2016-12-19T17:15:30Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Peff,\n\nOn Thu, 15 Dec 2016, Jeff King wrote:\n\n> On Tue, Dec 13, 2016 at 04:30:01PM +0100, Johannes Schindelin wrote:\n> \n> > +\telse {\n> > +\t\tunsigned char head[20];\n> > +\t\tstruct commit *head_commit;\n> > +\t\tconst char *head_message, *body;\n> > +\n> > +\t\tif (get_sha1(\"HEAD\", head))\n> > +\t\t\treturn error(_(\"need a HEAD to fixup\"));\n> > +\t\tif (!(head_commit = lookup_commit_reference(head)))\n> > +\t\t\treturn error(_(\"could not read HEAD\"));\n> > +\t\tif (!(head_message = get_commit_buffer(head_commit, NULL)))\n> > +\t\t\treturn error(_(\"could not read HEAD's commit message\"));\n> \n> This get_commit_buffer() may allocate a fresh buffer...\n> \n> > +\t\tbody = strstr(head_message, \"\\n\\n\");\n> > +\t\tif (!body)\n> > +\t\t\tbody = \"\";\n> > +\t\telse\n> > +\t\t\tbody = skip_blank_lines(body + 2);\n> > +\t\tif (write_message(body, strlen(body),\n> > +\t\t\t\t  rebase_path_fixup_msg(), 0))\n> > +\t\t\treturn error(_(\"cannot write '%s'\"),\n> > +\t\t\t\t     rebase_path_fixup_msg());\n> \n> ...and then this return leaks the result (the other code path hits\n> unuse_commit_buffer(), and is fine).\n\nGood point.\n\nI found another leaked commit buffer in make_patch() and fixed it, too.\n\n> This leak was noticed by Coverity. It has a _ton_ of false positives\n> across the whole project, but it sends out a mail with new ones every\n> few days, which is usually short enough that I can process it in 30\n> seconds or so.\n\nYeah, I get these mails now, thanks to Stephan adding me in response to\nsome issues I introduced with the builtin difftool (and hence I did not\nget the warnings when I introduced the problems with sequencer-i).\n\nCiao,\nDscho\n"},{"id":"308061","messageId":"20161219171941.z3yvelyekc5ys32p@sigill.intra.peff.net","threadId":"43966","inReplyTo":"alpine.DEB.2.20.1612191758290.54750@virtualbox","subject":"Re: [PATCH v2 07/34] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2016-12-19T17:19:42Z","receivedAt":"2016-12-19T17:20:43Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Dec 19, 2016 at 05:59:06PM +0100, Johannes Schindelin wrote:\n\n> > > > +\t\tsprintf((char *)p, \"%d\", ++count);\n> > > \n> > > Do we know the area pointed at p (which is inside buf) long enough\n> > > not to overflow?  If the original were 9 and you incremented to get\n> > > 10, you would need one extra byte.\n> > \n> > Even if it is enough, I'd ask to please use xsnprintf(). In the off\n> > chance that there's a programming error, we'd get a nice die(\"BUG\")\n> > instead of a buffer overflow (and it makes the code base easier to audit\n> > for other overflows).\n> \n> I ended up with more verbose, easier-to-read code that does not try to do\n> things in-place, in favor of being slightly more wasteful with strbufs.\n\nGreat. I agree that should make the whole thing way more readable.\n\n-Peff\n"},{"id":"308062","messageId":"alpine.DEB.2.20.1612191819470.54750@virtualbox","threadId":"43966","inReplyTo":"xmqqd1grsn53.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 11/34] sequencer (rebase -i): remove CHERRY_PICK_HEAD when no longer needed","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-19T17:22:16Z","receivedAt":"2016-12-19T17:23:45Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Fri, 16 Dec 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > The scripted version of the interactive rebase already does that.\n> \n> Sensible.  I was wondering why this wasn't there while reviewing\n> 10/34, comparing the two (this is not a suggestion to squash this\n> into the previous step).\n\nIt's a bit of a historical wart, as I discovered test breakages only long\nafter implementing the fixup/squash commands (as you may have guessed, I\nimplemented the commands one after another, fixing things as discovered by\nthe test suite; it took something like a month until I got the\nrebase--helper based rebase -i to pass t3404).\n\nIn the end, I decided not to squash this into 10/34 because it seemed to\nbe a significant enough change to merit its own commit.\n\nCiao,\nDscho\n"},{"id":"308065","messageId":"alpine.DEB.2.20.1612191832450.54750@virtualbox","threadId":"43966","inReplyTo":"xmqq8trfsmvr.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 14/34] sequencer (rebase -i): update refs after a successful rebase","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2016-12-19T17:33:05Z","receivedAt":"2016-12-19T17:34:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Fri, 16 Dec 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > An interactive rebase operates on a detached HEAD (to keep the reflog\n> > of the original branch relatively clean), and updates the branch only\n> > at the end.\n> >\n> > Now that the sequencer learns to perform interactive rebases, it also\n> > needs to learn the trick to update the branch before removing the\n> > directory containing the state of the interactive rebase.\n> >\n> > We introduce a new head_ref variable in a wider scope than necessary at\n> > the moment, to allow for a later patch that prints out \"Successfully\n> > rebased and updated <ref>\".\n> >\n> > Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> > ---\n> >  sequencer.c | 32 +++++++++++++++++++++++++++++++-\n> >  1 file changed, 31 insertions(+), 1 deletion(-)\n> >\n> > diff --git a/sequencer.c b/sequencer.c\n> > index a6625e765d..a4e9b326ba 100644\n> > --- a/sequencer.c\n> > +++ b/sequencer.c\n> > @@ -100,6 +100,8 @@ static GIT_PATH_FUNC(rebase_path_stopped_sha, \"rebase-merge/stopped-sha\")\n> >  static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, \"rebase-merge/gpg_sign_opt\")\n> >  static GIT_PATH_FUNC(rebase_path_orig_head, \"rebase-merge/orig-head\")\n> >  static GIT_PATH_FUNC(rebase_path_verbose, \"rebase-merge/verbose\")\n> > +static GIT_PATH_FUNC(rebase_path_head_name, \"rebase-merge/head-name\")\n> > +static GIT_PATH_FUNC(rebase_path_onto, \"rebase-merge/onto\")\n> >  \n> >  static inline int is_rebase_i(const struct replay_opts *opts)\n> >  {\n> > @@ -1793,12 +1795,39 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n> >  \t}\n> >  \n> >  \tif (is_rebase_i(opts)) {\n> > -\t\tstruct strbuf buf = STRBUF_INIT;\n> > +\t\tstruct strbuf head_ref = STRBUF_INIT, buf = STRBUF_INIT;\n> >  \n> >  \t\t/* Stopped in the middle, as planned? */\n> >  \t\tif (todo_list->current < todo_list->nr)\n> >  \t\t\treturn 0;\n> >  \n> > +\t\tif (read_oneliner(&head_ref, rebase_path_head_name(), 0) &&\n> > +\t\t\t\tstarts_with(head_ref.buf, \"refs/\")) {\n> > +\t\t\tunsigned char head[20], orig[20];\n> > +\n> > +\t\t\tif (get_sha1(\"HEAD\", head))\n> > +\t\t\t\treturn error(_(\"cannot read HEAD\"));\n> > +\t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0) ||\n> > +\t\t\t\t\tget_sha1_hex(buf.buf, orig))\n> > +\t\t\t\treturn error(_(\"could not read orig-head\"));\n> > +\t\t\tstrbuf_addf(&buf, \"rebase -i (finish): %s onto \",\n> > +\t\t\t\thead_ref.buf);\n> > +\t\t\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n> > +\t\t\t\treturn error(_(\"could not read 'onto'\"));\n> > +\t\t\tif (update_ref(buf.buf, head_ref.buf, head, orig,\n> > +\t\t\t\t\tREF_NODEREF, UPDATE_REFS_MSG_ON_ERR))\n> > +\t\t\t\treturn error(_(\"could not update %s\"),\n> > +\t\t\t\t\thead_ref.buf);\n> > +\t\t\tstrbuf_reset(&buf);\n> > +\t\t\tstrbuf_addf(&buf,\n> > +\t\t\t\t\"rebase -i (finish): returning to %s\",\n> > +\t\t\t\thead_ref.buf);\n> > +\t\t\tif (create_symref(\"HEAD\", head_ref.buf, buf.buf))\n> > +\t\t\t\treturn error(_(\"could not update HEAD to %s\"),\n> > +\t\t\t\t\thead_ref.buf);\n> \n> All of the above return error() calls leak head_ref.buf; in addition\n> some leak buf.buf, too.\n\nThanks, fixed.\n\nCiao,\nDscho\n"},{"id":"308077","messageId":"xmqqh95zpy7v.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"alpine.DEB.2.20.1612191237400.54750@virtualbox","subject":"Re: [PATCH v2 02/34] sequencer (rebase -i): implement the 'noop' command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-19T18:31:32Z","receivedAt":"2016-12-19T18:32:37Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> On Tue, 13 Dec 2016, Junio C Hamano wrote:\n>\n>> Linus Torvalds <torvalds@linux-foundation.org> writes:\n>> \n>> > On Tue, Dec 13, 2016 at 12:38 PM, Junio C Hamano <gitster@pobox.com> wrote:\n>> >> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\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>> >>\n>> >> Good thinking.\n>\n> Not my thinking... This was in direct response to a suggestion by Dennis\n> Kaarsemaker, I cannot take credit for the idea.\n\nI now realize that I was unclear about what \"thinking\" I found good\nin my comment.  I do not particularly like defining two parallel\nthings and having to maintain them in sync.  The \"Good thinking\"\npraise goes to whoever thought that this burdensome fact deserves a\nclear comment in front of these two things.\n\nAnd ...\n\n>> Makes me wish C were a better language, though ;-)\n>> >\n>> > Do this:\n>> >\n>> >   static const char *todo_command_strings[] = {\n>> >       [TODO_PICK] = \"pick\",\n>> >       [TODO_REVERT] = \"revert\",\n>> >       [TODO_NOOP] = \"noop:,\n>> >   };\n>> >\n>> > which makes the array be order-independent.\n\n... solves only one-half of the problem with the language I had.\nThe order of the entries in this array[] may become more flexible\nin the source, but you still have to define enum separately.\n\nI guess if we really want to, we need to resort to something \"ugly\nbut workable\" like what you did in fsck.c with FOREACH_MSG_ID(X).\nThat approach may be the least ugly way if we have to maintain two\nor more parallel things in sync.\n\n    ... and then realizes you wrote pretty much the same thing\n    ... after writing all of the above ;-)\n\nBut it is way overkill for sequencer commands that are only handful.\n"},{"id":"308079","messageId":"xmqq8trbpxhx.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"alpine.DEB.2.20.1612191438200.54750@virtualbox","subject":"Re: [PATCH v2 03/34] sequencer (rebase -i): implement the 'edit' command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-19T18:47:06Z","receivedAt":"2016-12-19T18:48:16Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":""},{"id":"308080","messageId":"xmqq4m1zpxff.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"alpine.DEB.2.20.1612191438200.54750@virtualbox","subject":"Re: [PATCH v2 03/34] sequencer (rebase -i): implement the 'edit' command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-19T18:48:36Z","receivedAt":"2016-12-19T18:49:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> > +\tstrbuf_addf(&buf, \"%s/patch\", get_dir(opts));\n>> > +\tmemset(&log_tree_opt, 0, sizeof(log_tree_opt));\n>> > +\tinit_revisions(&log_tree_opt, NULL);\n>> > +\tlog_tree_opt.abbrev = 0;\n>> > +\tlog_tree_opt.diff = 1;\n>> > +\tlog_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;\n>> > +\tlog_tree_opt.disable_stdin = 1;\n>> > +\tlog_tree_opt.no_commit_id = 1;\n>> > +\tlog_tree_opt.diffopt.file = fopen(buf.buf, \"w\");\n>> > +\tlog_tree_opt.diffopt.use_color = GIT_COLOR_NEVER;\n>> > +\tif (!log_tree_opt.diffopt.file)\n>> > +\t\tres |= error_errno(_(\"could not open '%s'\"), buf.buf);\n>> > +\telse {\n>> > +\t\tres |= log_tree_commit(&log_tree_opt, commit);\n>> > +\t\tfclose(log_tree_opt.diffopt.file);\n>> > +\t}\n>> > +\tstrbuf_reset(&buf);\n>> > +\tstrbuf_addf(&buf, \"%s/message\", get_dir(opts));\n>> > +\tif (!file_exists(buf.buf)) {\n>> > +\t\tfind_commit_subject(commit_buffer, &subject);\n>> > +\t\tres |= write_message(subject, strlen(subject), buf.buf, 1);\n>> > +\t\tunuse_commit_buffer(commit, commit_buffer);\n>> > +\t}\n>> > +\tstrbuf_release(&buf);\n>> > +\n>> > +\treturn res;\n>> > +}\n>> \n>> OK.  This seems to match what scripted make_patch does in a handful\n>> of lines.  We probably should have given you a helper to reduce\n>> boilerplate that sets up log_tree_opt so that this function does not\n>> have to be this long, but that is a separate topic.\n>> \n>> Does it matter output_format is set to FORMAT_PATCH here, though?\n>> With --no-commit-id set, I suspect there is no log message or\n>> authorship information given to the output.\n\nSorry, this was me being stupid.  \n\nFORMAT_PATCH here does not have anythning to do with \"git\nformat-patch\" (and \"git log --pretty=email\").  The PATCH there is as\nopposed to things like --stat and --raw.  We want patch text that\ncan be fed to \"git apply\" and it is absolutely the right thing to\nuse here.\n"},{"id":"308082","messageId":"xmqqzijroieg.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"a21233f368f066408051e6bdc9a2b6ec513e9e11.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 24/34] sequencer (rebase -i): respect strategy/strategy_opts settings","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-19T18:58:31Z","receivedAt":"2016-12-19T18:59:28Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> The sequencer already has an idea about using different merge\n> strategies. We just piggy-back on top of that, using rebase -i's\n> own settings, when running the sequencer in interactive rebase mode.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  sequencer.c | 26 +++++++++++++++++++++++++-\n>  1 file changed, 25 insertions(+), 1 deletion(-)\n\nA handful of steps before and including this one look quite faithful\nport to C from the scripted one.  \n\nLooking good.\n"},{"id":"308083","messageId":"xmqqvaufoi53.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"b82347c627fd3b8a74827bc773a5df2d16c6dded.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 27/34] sequencer (rebase -i): differentiate between comments and 'noop'","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-19T19:04:08Z","receivedAt":"2016-12-19T19:05:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> In the upcoming patch, we will support rebase -i's progress\n> reporting. The progress skips comments but counts 'noop's.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  sequencer.c | 15 +++++++++------\n>  1 file changed, 9 insertions(+), 6 deletions(-)\n\n> diff --git a/sequencer.c b/sequencer.c\n> index 1f314b2743..63f6f25ced 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -770,7 +770,9 @@ enum todo_command {\n>  \tTODO_EXEC,\n>  \t/* commands that do nothing but are counted for reporting progress */\n>  \tTODO_NOOP,\n> -\tTODO_DROP\n> +\tTODO_DROP,\n> +\t/* comments (not counted for reporting progress) */\n> +\tTODO_COMMENT\n>  };\n>  \n>  static struct {\n\nMakes sense.  I would have done this immediately after introducing\nNOOP if I were doing this series, if only because by having the\nunchanging last element early in enum {} definition, we can avoid\nhaving to deal with the \"last element cannot have comma\", but that\nis not a big issue.\n\n> @@ -785,12 +787,13 @@ static struct {\n>  \t{ 's', \"squash\" },\n>  \t{ 'x', \"exec\" },\n>  \t{ 0,   \"noop\" },\n> -\t{ 'd', \"drop\" }\n> +\t{ 'd', \"drop\" },\n> +\t{ 0,   NULL }\n>  };\n>  \n>  static const char *command_to_string(const enum todo_command command)\n>  {\n> -\tif ((size_t)command < ARRAY_SIZE(todo_command_info))\n> +\tif (command < TODO_COMMENT)\n>  \t\treturn todo_command_info[command].str;\n>  \tdie(\"Unknown command: %d\", command);\n>  }\n\nThe same comment as \"instead of comparing with TODO_NOOP, you would\nwant is_noop()\" applies to three instances of comparing with\nTODO_COMMENT we can see in this patch, I think.\n\n\"is_counted()\" perhaps?\n"},{"id":"308084","messageId":"xmqqr353ohhy.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"9e48dffa8e58183debb79f29413afa81af174475.1481642927.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v2 32/34] sequencer (rebase -i): show the progress","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-19T19:18:01Z","receivedAt":"2016-12-19T19:19:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> The interactive rebase keeps the user informed about its progress.\n> If the sequencer wants to do the grunt work of the interactive\n> rebase, it also needs to show that progress.\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  sequencer.c | 32 ++++++++++++++++++++++++++++++++\n>  1 file changed, 32 insertions(+)\n\nSensible.  This further adds two comparisons with TODO_COMMENT and\nmakes need for is_counted() or something like that felt more.\n\n    $ git grep TODO_COMMENT pu sequencer.c\n\nshows that some places \"x < TODO_COMMENT\" is used while some other\nplaces \"y != TODO_COMMENT\" is used, both for the same purpose, and\n\"z >= TODO_COMMENT\" is also seen for the negation of the same.\n\nI think all of them except for one can become is_counted() or\n!is_counted() to convey what they want to check better while the one\nused in the loop control:\n\n\tfor (i = 0; i < TODO_COMMENT; i++)\n\nshould probably become:\n\n\tfor (i = 0; i < ARRAY_SIZE(todo_command_info); i++)\n\nas the parsing loop wants to check the input against all known\ncommands and there is no strong reason for that layer to know how\nthe insns are numbered.  is_xxx() implementation can take advantage\nof the way the insns are numbered, but there is no point spreading\nthe knowledge to higher layer in the callchain.\n\nOther than that, all 34 patches looked sensible steps explained as a\ncoherent story that unfolds bit by bit, which was mostly a pleasant\nread.\n\nThanks.\n"},{"id":"308096","messageId":"940cbdcc-e289-6be2-29fd-97c5e2549642@gmx.net","threadId":"43966","inReplyTo":"alpine.DEB.2.20.1612191515000.54750@virtualbox","subject":"Re: [PATCH v2 01/34] sequencer: support a new action: 'interactive rebase'","fromName":"Stephan Beyer","fromEmail":"s-beyer@gmx.net","sentAt":"2016-12-19T21:27:26Z","receivedAt":"2016-12-19T21:28:54Z","isPatch":true,"sender":{"key":"s-beyer@gmx.net","avatar":"https://avatars.githubusercontent.com/u/143889?v=4"},"body":"Hi Dscho,\n\n>> However, maintaining more than one directory (like \"sequencer\" for\n>> sequencer state and \"rebase-merge\" for rebase todo and log) can cause\n>> the necessity to be even more careful when hacking on sequencer... For\n>> example, the cleanup code must delete both of them, not only one of them.\n> \n> That is incorrect. It depends on the options which directory is used. And\n> it is that directory (and not both) that should be cleaned up in the end.\n> \n> Otherwise you run into a ton of pain e.g. when running a rebase -i with an\n> `exec git cherry-pick ...` line: all of a sudden, that little innocuous\n> line would simply destroy the state directory of the current rebase -i.\n> \n> That's a rather big no-no.\n\nAhh, I see, there seems to be a misunderstanding on my side about how\nyou did it (I did not look into the other patches (obviously)).\n\nThanks for clarifying!\n\nBest\n  Stephan\n"},{"id":"308126","messageId":"xmqqy3zbl718.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"alpine.DEB.2.20.1612191800530.54750@virtualbox","subject":"Re: [PATCH v2 09/34] sequencer (rebase -i): write an author-script file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-20T01:32:19Z","receivedAt":"2016-12-20T01:33:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Hi Junio,\n>\n> On Thu, 15 Dec 2016, Junio C Hamano wrote:\n>\n>> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n>> \n>> > +\tstrbuf_addstr(&buf, \"GIT_AUTHOR_NAME='\");\n>> > +\twhile (*message && *message != '\\n' && *message != '\\r')\n>> > +\t\tif (skip_prefix(message, \" <\", &message))\n>> > +\t\t\tbreak;\n>> > +\t\telse if (*message != '\\'')\n>> > +\t\t\tstrbuf_addch(&buf, *(message++));\n>> > +\t\telse\n>> > +\t\t\tstrbuf_addf(&buf, \"'\\\\\\\\%c'\", *(message++));\n>> > +\tstrbuf_addstr(&buf, \"'\\nGIT_AUTHOR_EMAIL='\");\n>> > +\twhile (*message && *message != '\\n' && *message != '\\r')\n>> > +\t\tif (skip_prefix(message, \"> \", &message))\n>> > +\t\t\tbreak;\n>> > +\t\telse if (*message != '\\'')\n>> > +\t\t\tstrbuf_addch(&buf, *(message++));\n>> > +\t\telse\n>> > +\t\t\tstrbuf_addf(&buf, \"'\\\\\\\\%c'\", *(message++));\n>> \n>> Aren't these reading from an in-core commit object?  \n>> \n>> If so, it should use split_ident_line() for consistency with other\n>> parts of the system to do this parsing.  We should also already have\n>> a helper for simple shell-quoting in quote.c and you would want to\n>> use that instead of open coding like this.\n>\n> We keep coming back to the same argument. You want this quoting/dequoting\n> to be turned into a full-fledged parser. And I keep pointing out that the\n> code here does not *need* to parse but only construct an environment\n> block.\n\nI am afraid you are mis-reading me.  I see a code that _READS_ some\ndata format, for which we already have a parser, and then write out\nthings based on what it read.  I do not want you to make anything\ninto a full-fledged parser---I just do not want to see an ad-hoc\nreader and instead the code to USE existing parser.\n\n"},{"id":"308188","messageId":"xmqq37hijh9l.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"xmqqy3zbl718.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 09/34] sequencer (rebase -i): write an author-script file","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2016-12-20T23:46:30Z","receivedAt":"2016-12-20T23:47:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n>> We keep coming back to the same argument. You want this quoting/dequoting\n>> to be turned into a full-fledged parser. And I keep pointing out that the\n>> code here does not *need* to parse but only construct an environment\n>> block.\n>\n> I am afraid you are mis-reading me.  I see a code that _READS_ some\n> data format, for which we already have a parser, and then write out\n> things based on what it read.  I do not want you to make anything\n> into a full-fledged parser---I just do not want to see an ad-hoc\n> reader and instead the code to USE existing parser.\n\nTo extend this a bit.\n\nI care rather deeply about not spreading the re-invention of parsers\nand formatters throughout the code, because that would introduce\nunnecesary maintenance burden.\n\nQuoting a string so that it is acceptable inside a pair of single\nquotes, occasionally stepping outside of the sq context and using\nbackquote to excape individual byte, for example, might feel simple\nenough that anybody can just write inline instead of learning how to\ncall and actually calling sq_quote().  You open ', show each byte\nunless it is a ', in which case you close ' and give \\' and\nimmediately open '.  That was what sq_quote() did originally and for\na long time.  If however we allowed everybody to reinvent the code\nto quote all over the place, with a lame \"This is different and does\nnot *need* to call shared code\" excuse, it would have required a lot\nmore effort to do a change like the one in 77d604c309 (\"Enhanced\nsq_quote()\", 2005-10-10) that changed the quoting rules slightly to\nmake the output safer to accomodate different variants of shells.\n\nThe same thing can be said for split_ident() code.  The \"author\"\nline has name, '<', email address, '>', timestring, '+' or '-', and\ntimezone.  Splitting them into NAME and TIME may be very simple, and\n\"does not *need* to parse\", right?  Not really.  If you look at how\nsplit_ident() evolved to accomodate the real world malformed lines\nlike duplicated closing '>' and realize that what we have there may\nprobably *NOT* the perfect one (i.e. there may be other malformed\ninput we may have to accomodate by tweaking the implementation\nfurther), we really do not want a hand-rolled ad-hoc \"splitter\" that\n\"does not *need* to parse\".\n\nThe same thing can be said for the code that reads the\nauthor-script, too.\n\nSo please do not waste any more time arguing.  I think you spent\narguing more time than you would otherwise have had to spend if you\njust used existing helper functions, both in this thread and the\nolder one about reading the author-script.\n"},{"id":"308625","messageId":"alpine.DEB.2.20.1701021533220.3469@virtualbox","threadId":"43966","inReplyTo":"f4a72743-3488-3466-5b9f-0dacec102a54@kdbg.org","subject":"Re: [PATCH v2 28/34] run_command_opt(): optionally hide stderr when the command succeeds","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T14:38:31Z","receivedAt":"2017-01-02T14:38:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Hannes,\n\nOn Wed, 14 Dec 2016, Johannes Sixt wrote:\n\n> Am 14.12.2016 um 14:06 schrieb Jeff King:\n> > On Wed, Dec 14, 2016 at 07:53:23AM -0500, Jeff King wrote:\n> >\n> > > I don't have a strong opinion on the patches under discussion, but\n> > > here are a few pointers on the run-command interface:\n> > > [...]\n> >\n> > And here is a patch representing my suggestions, on top of yours. Not\n> > tested beyond \"make test\".\n> \n> Thank you, that looks way better.\n> \n> If there is agreement that this approach is preferable, I think we can\n> have patches on top of the series; they would be orthogonal and do not\n> have to take hostage of it. (And it looks like I won't be able to follow\n> up until later this week[end].)\n\nSeeing as the original intention was to do away with the\nRUN_HIDE_STDERR_ON_SUCCESS flag, and that the sequencer-i branch *must*\ninclude that functionality somehow, it is unfortunately not really\npossible to do this on top of the patch series.\n\nI say \"unfortunately\" because I feel pretty uncomfortable with replacing\nsomething that has been tried and tested by something that still awaits\nthe test of time.\n\nSo the only possible course of action I see is to go the really long\nroute: incorporate the patches to use pipe_command() instead of\nintroducing a new RUN_* flag (which means basically munch up your patch\nand Peff's and move it somewhere into the middle of the sequencer-i patch\nseries, which is exactly what I already did locally), cook the patches\nbeyond recognition in `next`, i.e. cook it really long to give it a really\ngood testing before moving the patches to `master`.\n\nCiao,\nJohannes\n"},{"id":"308626","messageId":"alpine.DEB.2.20.1701021545450.3469@virtualbox","threadId":"43966","inReplyTo":"xmqqvaujr7ed.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 20/34] sequencer (rebase -i): copy commit notes at end","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T14:51:04Z","receivedAt":"2017-01-02T14:51:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Fri, 16 Dec 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > @@ -1750,6 +1797,17 @@ static int is_final_fixup(struct todo_list *todo_list)\n> >  \treturn 1;\n> >  }\n> >  \n> > +static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n> > +{\n> > +\tint i;\n> > +\n> > +\tfor (i = todo_list->current + offset; i < todo_list->nr; i++)\n> > +\t\tif (todo_list->items[i].command != TODO_NOOP)\n> > +\t\t\treturn todo_list->items[i].command;\n> \n> Makes me wonder, after having commented on 07/34 regarding the fact\n> that in the end you would end up having three variants of no-op\n> (i.e. NOOP, DROP and COMMENT), what definition of a \"command\" this\n> function uses to return its result, when asked to \"peek\".\n\nWell, it uses the todo_command idea of a \"command\"... ;-)\n\nThe only thing we do with this for now is to look whether the next command\nis a fixup/squash (so that the user gets to edit the commit message just\nonce, for example, and also to record rewritten commits properly).\n\n> I suspect that this will be updated in a later patch to do \"< TODO_NOOP\"\n> instead?\n\nActually, no. I introduced a new function is_noop() and that is used now.\n\nCiao,\nDscho\n"},{"id":"308628","messageId":"alpine.DEB.2.20.1701021610590.3469@virtualbox","threadId":"43966","inReplyTo":"xmqq37hr1orb.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 05/34] sequencer (rebase -i): learn about the 'verbose' mode","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:11:36Z","receivedAt":"2017-01-02T15:11:54Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Tue, 13 Dec 2016, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > @@ -1493,9 +1498,26 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n> >  \t}\n> >  \n> >  \tif (is_rebase_i(opts)) {\n> > +\t\tstruct strbuf buf = STRBUF_INIT;\n> > +\n> >  \t\t/* Stopped in the middle, as planned? */\n> >  \t\tif (todo_list->current < todo_list->nr)\n> >  \t\t\treturn 0;\n> > +\n> > +\t\tif (opts->verbose) {\n> > +\t\t\tconst char *argv[] = {\n> > +\t\t\t\t\"diff-tree\", \"--stat\", NULL, NULL\n> > +\t\t\t};\n> > +\n> > +\t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0))\n> > +\t\t\t\treturn error(_(\"could not read '%s'\"),\n> > +\t\t\t\t\trebase_path_orig_head());\n> > +\t\t\tstrbuf_addstr(&buf, \"..HEAD\");\n> > +\t\t\targv[2] = buf.buf;\n> > +\t\t\trun_command_v_opt(argv, RUN_GIT_CMD);\n> > +\t\t\tstrbuf_reset(&buf);\n> > +\t\t}\n> > +\t\tstrbuf_release(&buf);\n> >  \t}\n> \n> It's a bit curious that the previous step avoided running a separate\n> process and instead did \"diff-tree -p\" all in C, but this one does not.\n\nI guess my only defence is that I tried to be a little lazy.\n\nFixed.\nDscho\n"},{"id":"308629","messageId":"cover.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1481642927.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 00/38] Teach the sequencer to act as rebase -i's backend","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:25:59Z","receivedAt":"2017-01-02T15:26:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This marks the count down to '3': two more patch series after this\n(really tiny ones) and we have a faster rebase -i.\n\nThe idea of this patch series is to teach the sequencer to understand\nall of the commands in `git-rebase-todo` scripts, to execute them and to\nbehave pretty much very the same as `git rebase -i --continue` when\ncalled with the newly-introduced REPLAY_INTERACTIVE_REBASE mode.\n\nMost of these patches should be pretty much straight-forward. When not,\nI tried to make a point of describing enough background in the commit\nmessage. Please feel free to point out where my explanations fall short.\n\nNote that even after this patch series is applied, rebase -i is still\nunaffected. It will require the next patch series which introduces the\nrebase--helper that essentially implements `git rebase -i --continue` by\ncalling the sequencer with the appropriate options.\n\nThe final patch series will move a couple of pre- and post-processing\nsteps into the rebase--helper/sequencer (such as expanding/shrinking the\nSHA-1s, reordering the fixup!/squash! lines, etc). This might sound like\na mere add-on, but it is essential for the speed improvements: those\nstupid little processing steps really dominated the execution time in my\ntests.\n\nApart from mostly cosmetic patches (and the occasional odd bug that I\nfixed promptly), I used these patches since mid May to perform all of my\ninteractive rebases. In mid June, I had the idea to teach rebase -i to\nrun *both* scripted rebase and rebase--helper and to cross-validate the\nresults. This slowed down all my interactive rebases since, but helped\nme catch three rather obscure bugs (e.g. that git commit --fixup unfolds\nlong onelines and rebase -i still finds the correct original commit).\n\nThis is all only to say that I am rather confident that the current code\ndoes the job.\n\nSince sending out v1, I integrated all of these patch series\ninto Git for Windows v2.10.0, where they have been live ever since, and\nused by myself (also in a Linux VM, as Git for Windows' master branch\nalways builds also on Linux and passes the test suite, too).\n\nJust to reiterate why I do all this: it speeds up the interactive rebase\nsubstantially. Even with a not yet fully builtin rebase -i, but just the\npart after the user edited the `git-rebase-todo` script.\n\nThe performance test I introduced to demonstrate this (p3404) shows a\nspeed-up of +380% here (i.e. roughly 5x), from ~8.8 seconds to ~1.8\nseconds. This is on Windows, where the performance impact of avoiding\nshell scripting is most noticable.\n\nOn MacOSX and on Linux, the speed-up is less pronounced, but still\nnoticable, at least if you trust Travis CI, which I abused to perform\nthat test for me. Check for yourself (searching for \"3404.2\") here:\nhttps://travis-ci.org/git/git/builds/156295227. According to those logs,\np3404 is speeded up from ~0.45 seconds to ~0.12 seconds on Linux (read:\nabout 3.5x) and from ~1.7 seconds to ~0.5 seconds on MacOSX (read:\nalmost 4x).\n\nChanges since v2:\n\n- fixed a TRANSLATORS: comment\n\n- added a comment to clarify why save_todo() does not write the current\n  command to git-rebase-todo\n\n- fixed the comment for \"stopped-sha\" that said that a long commit name\n  is stored in that file, when it is in fact an abbreviated one\n\n- consistently put the \"else\" keyword on the same line as the preceding\n  closing brace, if any (this adds two patches to the already large\n  patch series, sorry)\n\n- completely revamped the update_squash_messages() function to make it\n  more obvious why it does not overflow its buffer, and to fix it when\n  using it with localised messages\n\n- now the sequencer code uses find_commit_subject() consistently to find\n  the commit message (it does not use the subject's length returned by\n  the find_commit_subject() function, of course); this adds yet another\n  patch to this already large patch series, sorry.\n\n- introduced an is_noop() function (as opposed to testing <= TODO_NOOP\n  manually) to make the code less magic.\n\n- fixed two potential leaks of get_commit_buffer()'s returned buffer\n\n- fixed leaks in the error code paths when trying to update the ref at\n  the end\n\n- renamed read_author_script() into read_env_script() to reflect that\n  it would accept any environment setting, and made it much more\n  readable\n\n- completely reworked the strategy how to suppress the output of\n  successful cherry-picks: instead of introducing\n  RUN_HIDE_STDERR_ON_SUCCESS, the code now uses pipe_command() to catch\n  the output itself, so that it can be shown in case of error\n\n- replaced a spawned `diff-tree` command by code using the diff functions\n  directly\n\n\nJohannes Schindelin (38):\n  sequencer: avoid unnecessary curly braces\n  sequencer: move \"else\" keyword onto the same line as preceding brace\n  sequencer: use a helper to find the commit message\n  sequencer: support a new action: 'interactive rebase'\n  sequencer (rebase -i): implement the 'noop' command\n  sequencer (rebase -i): implement the 'edit' command\n  sequencer (rebase -i): implement the 'exec' command\n  sequencer (rebase -i): learn about the 'verbose' mode\n  sequencer (rebase -i): write the 'done' file\n  sequencer (rebase -i): add support for the 'fixup' and 'squash'\n    commands\n  sequencer (rebase -i): implement the short commands\n  sequencer (rebase -i): write an author-script file\n  sequencer (rebase -i): allow continuing with staged changes\n  sequencer (rebase -i): remove CHERRY_PICK_HEAD when no longer needed\n  sequencer (rebase -i): skip some revert/cherry-pick specific code path\n  sequencer (rebase -i): the todo can be empty when continuing\n  sequencer (rebase -i): update refs after a successful rebase\n  sequencer (rebase -i): leave a patch upon error\n  sequencer (rebase -i): implement the 'reword' command\n  sequencer (rebase -i): allow fast-forwarding for edit/reword\n  sequencer (rebase -i): refactor setting the reflog message\n  sequencer (rebase -i): set the reflog message consistently\n  sequencer (rebase -i): copy commit notes at end\n  sequencer (rebase -i): record interrupted commits in rewritten, too\n  sequencer (rebase -i): run the post-rewrite hook, if needed\n  sequencer (rebase -i): respect the rebase.autostash setting\n  sequencer (rebase -i): respect strategy/strategy_opts settings\n  sequencer (rebase -i): allow rescheduling commands\n  sequencer (rebase -i): implement the 'drop' command\n  sequencer (rebase -i): differentiate between comments and 'noop'\n  sequencer: make reading author-script more elegant\n  sequencer: use run_command() directly\n  sequencer (rebase -i): show only failed `git commit`'s output\n  sequencer (rebase -i): show only failed cherry-picks' output\n  sequencer (rebase -i): suggest --edit-todo upon unknown command\n  sequencer (rebase -i): show the progress\n  sequencer (rebase -i): write the progress into files\n  sequencer (rebase -i): write out the final message\n\n sequencer.c | 1105 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----\n sequencer.h |    4 +-\n 2 files changed, 1013 insertions(+), 96 deletions(-)\n\n\nbase-commit: e05806da9ec4aff8adfed142ab2a2b3b02e33c8c\nPublished-As: https://github.com/dscho/git/releases/tag/sequencer-i-v3\nFetch-It-Via: git fetch https://github.com/dscho/git sequencer-i-v3\n\nInterdiff vs v2:\n\n diff --git a/run-command.c b/run-command.c\n index 5bb957afdd..ca905a9e80 100644\n --- a/run-command.c\n +++ b/run-command.c\n @@ -589,29 +589,6 @@ int run_command_v_opt_cd_env(const char **argv, int opt, const char *dir, const\n  \tcmd.clean_on_exit = opt & RUN_CLEAN_ON_EXIT ? 1 : 0;\n  \tcmd.dir = dir;\n  \tcmd.env = env;\n -\n -\tif (opt & RUN_HIDE_STDERR_ON_SUCCESS) {\n -\t\tstruct strbuf buf = STRBUF_INIT;\n -\t\tint res;\n -\n -\t\tcmd.err = -1;\n -\t\tif (start_command(&cmd) < 0)\n -\t\t\treturn -1;\n -\n -\t\tif (strbuf_read(&buf, cmd.err, 0) < 0) {\n -\t\t\tclose(cmd.err);\n -\t\t\tfinish_command(&cmd); /* throw away exit code */\n -\t\t\treturn -1;\n -\t\t}\n -\n -\t\tclose(cmd.err);\n -\t\tres = finish_command(&cmd);\n -\t\tif (res)\n -\t\t\tfputs(buf.buf, stderr);\n -\t\tstrbuf_release(&buf);\n -\t\treturn res;\n -\t}\n -\n  \treturn run_command(&cmd);\n  }\n  \n diff --git a/run-command.h b/run-command.h\n index 65a21ddd4e..dd1c78c28d 100644\n --- a/run-command.h\n +++ b/run-command.h\n @@ -72,7 +72,6 @@ extern int run_hook_ve(const char *const *env, const char *name, va_list args);\n  #define RUN_SILENT_EXEC_FAILURE 8\n  #define RUN_USING_SHELL 16\n  #define RUN_CLEAN_ON_EXIT 32\n -#define RUN_HIDE_STDERR_ON_SUCCESS 64\n  int run_command_v_opt(const char **argv, int opt);\n  \n  /*\n diff --git a/sequencer.c b/sequencer.c\n index c0aeb4f5f7..0e7d2ca5c8 100644\n --- a/sequencer.c\n +++ b/sequencer.c\n @@ -102,7 +102,7 @@ static GIT_PATH_FUNC(rebase_path_author_script, \"rebase-merge/author-script\")\n  static GIT_PATH_FUNC(rebase_path_amend, \"rebase-merge/amend\")\n  /*\n   * When we stop at a given patch via the \"edit\" command, this file contains\n - * the long commit name of the corresponding patch.\n + * the abbreviated commit name of the corresponding patch.\n   */\n  static GIT_PATH_FUNC(rebase_path_stopped_sha, \"rebase-merge/stopped-sha\")\n  /*\n @@ -464,8 +464,7 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n  \n  \tif (active_cache_changed &&\n  \t    write_locked_index(&the_index, &index_lock, COMMIT_LOCK))\n -\t\t/*\n -\t\t * TRANSLATORS: %s will be \"revert\", \"cherry-pick\" or\n +\t\t/* TRANSLATORS: %s will be \"revert\", \"cherry-pick\" or\n  \t\t * \"rebase -i\".\n  \t\t */\n  \t\treturn error(_(\"%s: Unable to write new index file\"),\n @@ -524,8 +523,7 @@ static int write_author_script(const char *message)\n  \t\t\t/* Missing 'author' line? */\n  \t\t\tunlink(rebase_path_author_script());\n  \t\t\treturn 0;\n -\t\t}\n -\t\telse if (skip_prefix(message, \"author \", &message))\n +\t\t} else if (skip_prefix(message, \"author \", &message))\n  \t\t\tbreak;\n  \t\telse if ((eol = strchr(message, '\\n')))\n  \t\t\tmessage = eol + 1;\n @@ -560,18 +558,17 @@ static int write_author_script(const char *message)\n  }\n  \n  /*\n - * Read the author-script file into an environment block, ready for use in\n - * run_command(), that can be free()d afterwards.\n + * Read a list of environment variable assignments (such as the author-script\n + * file) into an environment block. Returns -1 on error, 0 otherwise.\n   */\n -static char **read_author_script(void)\n +static int read_env_script(struct argv_array *env)\n  {\n  \tstruct strbuf script = STRBUF_INIT;\n  \tint i, count = 0;\n -\tchar *p, *p2, **env;\n -\tsize_t env_size;\n +\tchar *p, *p2;\n  \n  \tif (strbuf_read_file(&script, rebase_path_author_script(), 256) <= 0)\n -\t\treturn NULL;\n +\t\treturn -1;\n  \n  \tfor (p = script.buf; *p; p++)\n  \t\tif (skip_prefix(p, \"'\\\\\\\\''\", (const char **)&p2))\n @@ -583,19 +580,12 @@ static char **read_author_script(void)\n  \t\t\tcount++;\n  \t\t}\n  \n -\tenv_size = (count + 1) * sizeof(*env);\n -\tstrbuf_grow(&script, env_size);\n -\tmemmove(script.buf + env_size, script.buf, script.len);\n -\tp = script.buf + env_size;\n -\tenv = (char **)strbuf_detach(&script, NULL);\n -\n  \tfor (i = 0; i < count; i++) {\n -\t\tenv[i] = p;\n +\t\targv_array_push(env, p);\n  \t\tp += strlen(p) + 1;\n  \t}\n -\tenv[count] = NULL;\n  \n -\treturn env;\n +\treturn 0;\n  }\n  \n  static const char staged_changes_advice[] =\n @@ -628,19 +618,18 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n  \t\t\t  int allow_empty, int edit, int amend,\n  \t\t\t  int cleanup_commit_message)\n  {\n -\tchar **env = NULL;\n -\tstruct argv_array array;\n -\tint opt = RUN_GIT_CMD, rc;\n +\tstruct child_process cmd = CHILD_PROCESS_INIT;\n  \tconst char *value;\n  \n +\tcmd.git_cmd = 1;\n +\n  \tif (is_rebase_i(opts)) {\n  \t\tif (!edit) {\n -\t\t\topt |= RUN_COMMAND_STDOUT_TO_STDERR;\n -\t\t\topt |= RUN_HIDE_STDERR_ON_SUCCESS;\n +\t\t\tcmd.stdout_to_stderr = 1;\n +\t\t\tcmd.err = -1;\n  \t\t}\n  \n -\t\tenv = read_author_script();\n -\t\tif (!env) {\n +\t\tif (read_env_script(&cmd.env_array)) {\n  \t\t\tconst char *gpg_opt = gpg_sign_opt_quoted(opts);\n  \n  \t\t\treturn error(_(staged_changes_advice),\n @@ -648,39 +637,47 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n  \t\t}\n  \t}\n  \n -\targv_array_init(&array);\n -\targv_array_push(&array, \"commit\");\n -\targv_array_push(&array, \"-n\");\n +\targv_array_push(&cmd.args, \"commit\");\n +\targv_array_push(&cmd.args, \"-n\");\n  \n  \tif (amend)\n -\t\targv_array_push(&array, \"--amend\");\n +\t\targv_array_push(&cmd.args, \"--amend\");\n  \tif (opts->gpg_sign)\n -\t\targv_array_pushf(&array, \"-S%s\", opts->gpg_sign);\n +\t\targv_array_pushf(&cmd.args, \"-S%s\", opts->gpg_sign);\n  \tif (opts->signoff)\n -\t\targv_array_push(&array, \"-s\");\n +\t\targv_array_push(&cmd.args, \"-s\");\n  \tif (defmsg)\n -\t\targv_array_pushl(&array, \"-F\", defmsg, NULL);\n +\t\targv_array_pushl(&cmd.args, \"-F\", defmsg, NULL);\n  \tif (cleanup_commit_message)\n -\t\targv_array_push(&array, \"--cleanup=strip\");\n +\t\targv_array_push(&cmd.args, \"--cleanup=strip\");\n  \tif (edit)\n -\t\targv_array_push(&array, \"-e\");\n +\t\targv_array_push(&cmd.args, \"-e\");\n  \telse if (!cleanup_commit_message &&\n  \t\t !opts->signoff && !opts->record_origin &&\n  \t\t git_config_get_value(\"commit.cleanup\", &value))\n -\t\targv_array_push(&array, \"--cleanup=verbatim\");\n +\t\targv_array_push(&cmd.args, \"--cleanup=verbatim\");\n  \n  \tif (allow_empty)\n -\t\targv_array_push(&array, \"--allow-empty\");\n +\t\targv_array_push(&cmd.args, \"--allow-empty\");\n  \n  \tif (opts->allow_empty_message)\n -\t\targv_array_push(&array, \"--allow-empty-message\");\n +\t\targv_array_push(&cmd.args, \"--allow-empty-message\");\n  \n -\trc = run_command_v_opt_cd_env(array.argv, opt, NULL,\n -\t\t\t(const char *const *)env);\n -\targv_array_clear(&array);\n -\tfree(env);\n +\tif (cmd.err == -1) {\n +\t\t/* hide stderr on success */\n +\t\tstruct strbuf buf = STRBUF_INIT;\n +\t\tint rc = pipe_command(&cmd,\n +\t\t\t\t      NULL, 0,\n +\t\t\t\t      /* stdout is already redirected */\n +\t\t\t\t      NULL, 0,\n +\t\t\t\t      &buf, 0);\n +\t\tif (rc)\n +\t\t\tfputs(buf.buf, stderr);\n +\t\tstrbuf_release(&buf);\n +\t\treturn rc;\n +\t}\n  \n -\treturn rc;\n +\treturn run_command(&cmd);\n  }\n  \n  static int is_original_commit_empty(struct commit *commit)\n @@ -786,6 +783,11 @@ static const char *command_to_string(const enum todo_command command)\n  \tdie(\"Unknown command: %d\", command);\n  }\n  \n +static int is_noop(const enum todo_command command)\n +{\n +\treturn TODO_NOOP <= command;\n +}\n +\n  static int is_fixup(enum todo_command command)\n  {\n  \treturn command == TODO_FIXUP || command == TODO_SQUASH;\n @@ -799,36 +801,33 @@ static int update_squash_messages(enum todo_command command,\n  \tconst char *message, *body;\n  \n  \tif (file_exists(rebase_path_squash_msg())) {\n -\t\tchar *p, *p2;\n +\t\tstruct strbuf header = STRBUF_INIT;\n +\t\tchar *eol, *p;\n  \n  \t\tif (strbuf_read_file(&buf, rebase_path_squash_msg(), 2048) <= 0)\n  \t\t\treturn error(_(\"could not read '%s'\"),\n  \t\t\t\trebase_path_squash_msg());\n  \n +\t\tp = buf.buf + 1;\n +\t\teol = strchrnul(buf.buf, '\\n');\n  \t\tif (buf.buf[0] != comment_line_char ||\n -\t\t    !skip_prefix(buf.buf + 1, \" This is a combination of \",\n -\t\t\t\t (const char **)&p))\n +\t\t    (p += strcspn(p, \"0123456789\\n\")) == eol)\n  \t\t\treturn error(_(\"unexpected 1st line of squash message:\"\n  \t\t\t\t       \"\\n\\n\\t%.*s\"),\n -\t\t\t\t     (int)(strchrnul(buf.buf, '\\n') - buf.buf),\n -\t\t\t\t     buf.buf);\n -\t\tcount = strtol(p, &p2, 10);\n +\t\t\t\t     (int)(eol - buf.buf), buf.buf);\n +\t\tcount = strtol(p, NULL, 10);\n  \n -\t\tif (count < 1 || *p2 != ' ')\n +\t\tif (count < 1)\n  \t\t\treturn error(_(\"invalid 1st line of squash message:\\n\"\n  \t\t\t\t       \"\\n\\t%.*s\"),\n -\t\t\t\t     (int)(strchrnul(buf.buf, '\\n') - buf.buf),\n -\t\t\t\t     buf.buf);\n -\n -\t\tsprintf((char *)p, \"%d\", ++count);\n -\t\tif (!*p2)\n -\t\t\t*p2 = ' ';\n -\t\telse {\n -\t\t\t*(++p2) = 'c';\n -\t\t\tstrbuf_insert(&buf, p2 - buf.buf, \" \", 1);\n -\t\t}\n -\t}\n -\telse {\n +\t\t\t\t     (int)(eol - buf.buf), buf.buf);\n +\n +\t\tstrbuf_addf(&header, \"%c \", comment_line_char);\n +\t\tstrbuf_addf(&header,\n +\t\t\t    _(\"This is a combination of %d commits.\"), ++count);\n +\t\tstrbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len);\n +\t\tstrbuf_release(&header);\n +\t} else {\n  \t\tunsigned char head[20];\n  \t\tstruct commit *head_commit;\n  \t\tconst char *head_message, *body;\n @@ -840,20 +839,22 @@ static int update_squash_messages(enum todo_command command,\n  \t\tif (!(head_message = get_commit_buffer(head_commit, NULL)))\n  \t\t\treturn error(_(\"could not read HEAD's commit message\"));\n  \n -\t\tbody = strstr(head_message, \"\\n\\n\");\n -\t\tif (!body)\n -\t\t\tbody = \"\";\n -\t\telse\n -\t\t\tbody = skip_blank_lines(body + 2);\n +\t\tfind_commit_subject(head_message, &body);\n  \t\tif (write_message(body, strlen(body),\n -\t\t\t\t  rebase_path_fixup_msg(), 0))\n +\t\t\t\t  rebase_path_fixup_msg(), 0)) {\n +\t\t\tunuse_commit_buffer(head_commit, head_message);\n  \t\t\treturn error(_(\"cannot write '%s'\"),\n  \t\t\t\t     rebase_path_fixup_msg());\n +\t\t}\n  \n  \t\tcount = 2;\n -\t\tstrbuf_addf(&buf, _(\"%c This is a combination of 2 commits.\\n\"\n -\t\t\t\t    \"%c The first commit's message is:\\n\\n%s\"),\n -\t\t\t    comment_line_char, comment_line_char, body);\n +\t\tstrbuf_addf(&buf, \"%c \", comment_line_char);\n +\t\tstrbuf_addf(&buf, _(\"This is a combination of %d commits.\"),\n +\t\t\t    count);\n +\t\tstrbuf_addf(&buf, \"\\n%c \", comment_line_char);\n +\t\tstrbuf_addstr(&buf, _(\"This is the 1st commit message:\"));\n +\t\tstrbuf_addstr(&buf, \"\\n\\n\");\n +\t\tstrbuf_addstr(&buf, body);\n  \n  \t\tunuse_commit_buffer(head_commit, head_message);\n  \t}\n @@ -861,25 +862,21 @@ static int update_squash_messages(enum todo_command command,\n  \tif (!(message = get_commit_buffer(commit, NULL)))\n  \t\treturn error(_(\"could not read commit message of %s\"),\n  \t\t\t     oid_to_hex(&commit->object.oid));\n -\tbody = strstr(message, \"\\n\\n\");\n -\tif (!body)\n -\t\tbody = \"\";\n -\telse\n -\t\tbody = skip_blank_lines(body + 2);\n +\tfind_commit_subject(message, &body);\n  \n  \tif (command == TODO_SQUASH) {\n  \t\tunlink(rebase_path_fixup_msg());\n -\t\tstrbuf_addf(&buf, _(\"\\n%c This is the commit message #%d:\\n\"\n -\t\t\t\t    \"\\n%s\"),\n -\t\t\t    comment_line_char, count, body);\n -\t}\n -\telse if (command == TODO_FIXUP) {\n -\t\tstrbuf_addf(&buf, _(\"\\n%c The commit message #%d \"\n -\t\t\t\t    \"will be skipped:\\n\\n\"),\n -\t\t\t    comment_line_char, count);\n +\t\tstrbuf_addf(&buf, \"\\n%c \", comment_line_char);\n +\t\tstrbuf_addf(&buf, _(\"This is the commit message #%d:\"), count);\n +\t\tstrbuf_addstr(&buf, \"\\n\\n\");\n +\t\tstrbuf_addstr(&buf, body);\n +\t} else if (command == TODO_FIXUP) {\n +\t\tstrbuf_addf(&buf, \"\\n%c \", comment_line_char);\n +\t\tstrbuf_addf(&buf, _(\"The commit message #%d will be skipped:\"),\n +\t\t\t    count);\n +\t\tstrbuf_addstr(&buf, \"\\n\\n\");\n  \t\tstrbuf_add_commented_lines(&buf, body, strlen(body));\n -\t}\n -\telse\n +\t} else\n  \t\treturn error(_(\"unknown command: %d\"), command);\n  \tunuse_commit_buffer(commit, message);\n  \n @@ -956,9 +953,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n  \t}\n  \tdiscard_cache();\n  \n -\tif (!commit->parents) {\n +\tif (!commit->parents)\n  \t\tparent = NULL;\n -\t}\n  \telse if (commit->parents->next) {\n  \t\t/* Reverting or cherry-picking a merge commit */\n  \t\tint cnt;\n @@ -1036,14 +1032,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n  \t\tnext = commit;\n  \t\tnext_label = msg.label;\n  \n -\t\t/*\n -\t\t * Append the commit log message to msgbuf; it starts\n -\t\t * after the tree, parent, author, committer\n -\t\t * information followed by \"\\n\\n\".\n -\t\t */\n -\t\tp = strstr(msg.message, \"\\n\\n\");\n -\t\tif (p)\n -\t\t\tstrbuf_addstr(&msgbuf, skip_blank_lines(p + 2));\n +\t\t/* Append the commit log message to msgbuf. */\n +\t\tif (find_commit_subject(msg.message, &p))\n +\t\t\tstrbuf_addstr(&msgbuf, p);\n  \n  \t\tif (opts->record_origin) {\n  \t\t\tif (!has_conforming_footer(&msgbuf, NULL, 0))\n @@ -1065,8 +1056,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n  \t\telse if (file_exists(rebase_path_fixup_msg())) {\n  \t\t\tcleanup_commit_message = 1;\n  \t\t\tmsg_file = rebase_path_fixup_msg();\n -\t\t}\n -\t\telse {\n +\t\t} else {\n  \t\t\tconst char *dest = git_path(\"SQUASH_MSG\");\n  \t\t\tunlink(dest);\n  \t\t\tif (copy_file(dest, rebase_path_squash_msg(), 0666))\n @@ -1241,8 +1231,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n  \t\tif (skip_prefix(bol, todo_command_info[i].str, &bol)) {\n  \t\t\titem->command = i;\n  \t\t\tbreak;\n -\t\t}\n -\t\telse if (bol[1] == ' ' && *bol == todo_command_info[i].c) {\n +\t\t} else if (bol[1] == ' ' && *bol == todo_command_info[i].c) {\n  \t\t\tbol++;\n  \t\t\titem->command = i;\n  \t\t\tbreak;\n @@ -1312,7 +1301,7 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n  \t\telse if (is_fixup(item->command))\n  \t\t\treturn error(_(\"cannot '%s' without a previous commit\"),\n  \t\t\t\tcommand_to_string(item->command));\n -\t\telse if (item->command < TODO_NOOP)\n +\t\telse if (!is_noop(item->command))\n  \t\t\tfixup_okay = 1;\n  \t}\n  \n @@ -1533,8 +1522,7 @@ static int create_seq_dir(void)\n  \t\terror(_(\"a cherry-pick or revert is already in progress\"));\n  \t\tadvise(_(\"try \\\"git cherry-pick (--continue | --quit | --abort)\\\"\"));\n  \t\treturn -1;\n -\t}\n -\telse if (mkdir(git_path_seq_dir(), 0777) < 0)\n +\t} else if (mkdir(git_path_seq_dir(), 0777) < 0)\n  \t\treturn error_errno(_(\"could not create sequencer directory '%s'\"),\n  \t\t\t\t   git_path_seq_dir());\n  \treturn 0;\n @@ -1667,6 +1655,10 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)\n  \tconst char *todo_path = get_todo_path(opts);\n  \tint next = todo_list->current, offset, fd;\n  \n +\t/*\n +\t * rebase -i writes \"git-rebase-todo\" without the currently executing\n +\t * command, appending it to \"done\" instead.\n +\t */\n  \tif (is_rebase_i(opts))\n  \t\tnext++;\n  \n @@ -1739,7 +1731,7 @@ static int make_patch(struct commit *commit, struct replay_opts *opts)\n  {\n  \tstruct strbuf buf = STRBUF_INIT;\n  \tstruct rev_info log_tree_opt;\n -\tconst char *commit_buffer = get_commit_buffer(commit, NULL), *subject, *p;\n +\tconst char *subject, *p;\n  \tint res = 0;\n  \n  \tp = short_commit_name(commit);\n @@ -1766,6 +1758,7 @@ static int make_patch(struct commit *commit, struct replay_opts *opts)\n  \n  \tstrbuf_addf(&buf, \"%s/message\", get_dir(opts));\n  \tif (!file_exists(buf.buf)) {\n +\t\tconst char *commit_buffer = get_commit_buffer(commit, NULL);\n  \t\tfind_commit_subject(commit_buffer, &subject);\n  \t\tres |= write_message(subject, strlen(subject), buf.buf, 1);\n  \t\tunuse_commit_buffer(commit, commit_buffer);\n @@ -1805,8 +1798,7 @@ static int error_with_patch(struct commit *commit,\n  \t\t\t\"Once you are satisfied with your changes, run\\n\"\n  \t\t\t\"\\n\"\n  \t\t\t\"  git rebase --continue\\n\", gpg_sign_opt_quoted(opts));\n -\t}\n -\telse if (exit_code)\n +\t} else if (exit_code)\n  \t\tfprintf(stderr, \"Could not apply %s... %.*s\\n\",\n  \t\t\tshort_commit_name(commit), subject_len, subject);\n  \n @@ -1854,8 +1846,7 @@ static int do_exec(const char *command_line)\n  \t\tif (status == 127)\n  \t\t\t/* command not found */\n  \t\t\tstatus = 1;\n -\t}\n -\telse if (dirty) {\n +\t} else if (dirty) {\n  \t\twarning(_(\"execution succeeded: %s\\nbut \"\n  \t\t\t  \"left changes to the index and/or the working tree\\n\"\n  \t\t\t  \"Commit or stash your changes, and then run\\n\"\n @@ -1878,7 +1869,7 @@ static int is_final_fixup(struct todo_list *todo_list)\n  \twhile (++i < todo_list->nr)\n  \t\tif (is_fixup(todo_list->items[i].command))\n  \t\t\treturn 0;\n -\t\telse if (todo_list->items[i].command < TODO_NOOP)\n +\t\telse if (!is_noop(todo_list->items[i].command))\n  \t\t\tbreak;\n  \treturn 1;\n  }\n @@ -1888,7 +1879,7 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n  \tint i;\n  \n  \tfor (i = todo_list->current + offset; i < todo_list->nr; i++)\n -\t\tif (todo_list->items[i].command < TODO_NOOP)\n +\t\tif (!is_noop(todo_list->items[i].command))\n  \t\t\treturn todo_list->items[i].command;\n  \n  \treturn -1;\n @@ -2021,21 +2012,18 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n  \t\t\t\t\tintend_to_amend();\n  \t\t\t\treturn error_failed_squash(item->commit, opts,\n  \t\t\t\t\titem->arg_len, item->arg);\n -\t\t\t}\n -\t\t\telse if (res && is_rebase_i(opts))\n +\t\t\t} else if (res && is_rebase_i(opts))\n  \t\t\t\treturn res | error_with_patch(item->commit,\n  \t\t\t\t\titem->arg, item->arg_len, opts, res,\n  \t\t\t\t\titem->command == TODO_REWORD);\n -\t\t}\n -\t\telse if (item->command == TODO_EXEC) {\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\tint saved = *end_of_arg;\n  \n  \t\t\t*end_of_arg = '\\0';\n  \t\t\tres = do_exec(item->arg);\n  \t\t\t*end_of_arg = saved;\n -\t\t}\n -\t\telse if (item->command < TODO_NOOP)\n +\t\t} else if (!is_noop(item->command))\n  \t\t\treturn error(_(\"unknown command %d\"), item->command);\n  \n  \t\ttodo_list->current++;\n @@ -2055,40 +2043,60 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n  \t\t\t\tstarts_with(head_ref.buf, \"refs/\")) {\n  \t\t\tconst char *msg;\n  \t\t\tunsigned char head[20], orig[20];\n -\n -\t\t\tif (get_sha1(\"HEAD\", head))\n -\t\t\t\treturn error(_(\"cannot read HEAD\"));\n +\t\t\tint res;\n +\n +\t\t\tif (get_sha1(\"HEAD\", head)) {\n +\t\t\t\tres = error(_(\"cannot read HEAD\"));\n +cleanup_head_ref:\n +\t\t\t\tstrbuf_release(&head_ref);\n +\t\t\t\tstrbuf_release(&buf);\n +\t\t\t\treturn res;\n +\t\t\t}\n  \t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0) ||\n -\t\t\t\t\tget_sha1_hex(buf.buf, orig))\n -\t\t\t\treturn error(_(\"could not read orig-head\"));\n -\t\t\tif (!read_oneliner(&buf, rebase_path_onto(), 0))\n -\t\t\t\treturn error(_(\"could not read 'onto'\"));\n +\t\t\t\t\tget_sha1_hex(buf.buf, orig)) {\n +\t\t\t\tres = error(_(\"could not read orig-head\"));\n +\t\t\t\tgoto cleanup_head_ref;\n +\t\t\t}\n +\t\t\tif (!read_oneliner(&buf, rebase_path_onto(), 0)) {\n +\t\t\t\tres = error(_(\"could not read 'onto'\"));\n +\t\t\t\tgoto cleanup_head_ref;\n +\t\t\t}\n  \t\t\tmsg = reflog_message(opts, \"finish\", \"%s onto %s\",\n  \t\t\t\thead_ref.buf, buf.buf);\n  \t\t\tif (update_ref(msg, head_ref.buf, head, orig,\n -\t\t\t\t\tREF_NODEREF, UPDATE_REFS_MSG_ON_ERR))\n -\t\t\t\treturn error(_(\"could not update %s\"),\n +\t\t\t\t\tREF_NODEREF, UPDATE_REFS_MSG_ON_ERR)) {\n +\t\t\t\tres = error(_(\"could not update %s\"),\n  \t\t\t\t\thead_ref.buf);\n +\t\t\t\tgoto cleanup_head_ref;\n +\t\t\t}\n  \t\t\tmsg = reflog_message(opts, \"finish\", \"returning to %s\",\n  \t\t\t\thead_ref.buf);\n -\t\t\tif (create_symref(\"HEAD\", head_ref.buf, msg))\n -\t\t\t\treturn error(_(\"could not update HEAD to %s\"),\n +\t\t\tif (create_symref(\"HEAD\", head_ref.buf, msg)) {\n +\t\t\t\tres = error(_(\"could not update HEAD to %s\"),\n  \t\t\t\t\thead_ref.buf);\n +\t\t\t\tgoto cleanup_head_ref;\n +\t\t\t}\n  \t\t\tstrbuf_reset(&buf);\n  \t\t}\n  \n  \t\tif (opts->verbose) {\n -\t\t\tconst char *argv[] = {\n -\t\t\t\t\"diff-tree\", \"--stat\", NULL, NULL\n -\t\t\t};\n -\n -\t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0))\n -\t\t\t\treturn error(_(\"could not read '%s'\"),\n -\t\t\t\t\trebase_path_orig_head());\n -\t\t\tstrbuf_addstr(&buf, \"..HEAD\");\n -\t\t\targv[2] = buf.buf;\n -\t\t\trun_command_v_opt(argv, RUN_GIT_CMD);\n -\t\t\tstrbuf_reset(&buf);\n +\t\t\tstruct rev_info log_tree_opt;\n +\t\t\tstruct object_id orig, head;\n +\n +\t\t\tmemset(&log_tree_opt, 0, sizeof(log_tree_opt));\n +\t\t\tinit_revisions(&log_tree_opt, NULL);\n +\t\t\tlog_tree_opt.diff = 1;\n +\t\t\tlog_tree_opt.diffopt.output_format =\n +\t\t\t\tDIFF_FORMAT_DIFFSTAT;\n +\t\t\tlog_tree_opt.disable_stdin = 1;\n +\n +\t\t\tif (read_oneliner(&buf, rebase_path_orig_head(), 0) &&\n +\t\t\t    !get_sha1(buf.buf, orig.hash) &&\n +\t\t\t    !get_sha1(\"HEAD\", head.hash)) {\n +\t\t\t\tdiff_tree_sha1(orig.hash, head.hash,\n +\t\t\t\t\t       \"\", &log_tree_opt.diffopt);\n +\t\t\t\tlog_tree_diff_flush(&log_tree_opt);\n +\t\t\t}\n  \t\t}\n  \t\tflush_rewritten_pending();\n  \t\tif (!stat(rebase_path_rewritten_list(), &st) &&\n @@ -2195,8 +2203,7 @@ int sequencer_continue(struct replay_opts *opts)\n  \tif (is_rebase_i(opts)) {\n  \t\tif (commit_staged_changes(opts))\n  \t\t\treturn -1;\n -\t}\n -\telse if (!file_exists(get_todo_path(opts)))\n +\t} else if (!file_exists(get_todo_path(opts)))\n  \t\treturn continue_single_pick();\n  \tif (read_populate_opts(opts))\n  \t\treturn -1;\n @@ -2216,8 +2223,7 @@ int sequencer_continue(struct replay_opts *opts)\n  \t\t\tgoto release_todo_list;\n  \t\t}\n  \t\ttodo_list.current++;\n -\t}\n -\telse if (file_exists(rebase_path_stopped_sha())) {\n +\t} else if (file_exists(rebase_path_stopped_sha())) {\n  \t\tstruct strbuf buf = STRBUF_INIT;\n  \t\tstruct object_id oid;\n  \n\n-- \n2.11.0.rc3.windows.1\n\n"},{"id":"308630","messageId":"65e0dac0115713b6ae955acdbc5b7655aeb18e7c.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 02/38] sequencer: move \"else\" keyword onto the same line as preceding brace","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:26:14Z","receivedAt":"2017-01-02T15:26:27Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It is the current coding style of the Git project to write\n\n\tif (...) {\n\t\t...\n\t} else {\n\t\t...\n\t}\n\ninstead of putting the closing brace and the \"else\" keyword on separate\nlines.\n\nPointed out by Junio Hamano.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 3 +--\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 23793db08b..3eededcb98 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1070,8 +1070,7 @@ static int create_seq_dir(void)\n \t\terror(_(\"a cherry-pick or revert is already in progress\"));\n \t\tadvise(_(\"try \\\"git cherry-pick (--continue | --quit | --abort)\\\"\"));\n \t\treturn -1;\n-\t}\n-\telse if (mkdir(git_path_seq_dir(), 0777) < 0)\n+\t} else if (mkdir(git_path_seq_dir(), 0777) < 0)\n \t\treturn error_errno(_(\"could not create sequencer directory '%s'\"),\n \t\t\t\t   git_path_seq_dir());\n \treturn 0;\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308631","messageId":"bc1a6c21c9ab2f55882c363e802bfcf37e49d79f.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 01/38] sequencer: avoid unnecessary curly braces","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:26:08Z","receivedAt":"2017-01-02T15:26:32Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This was noticed while addressing Junio Hamano's concern that some\n\"else\" operators were on separate lines than the preceding closing\nbrace.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 3 +--\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 9adb7bbf1d..23793db08b 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -632,9 +632,8 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t}\n \tdiscard_cache();\n \n-\tif (!commit->parents) {\n+\tif (!commit->parents)\n \t\tparent = NULL;\n-\t}\n \telse if (commit->parents->next) {\n \t\t/* Reverting or cherry-picking a merge commit */\n \t\tint cnt;\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308632","messageId":"780d71b5844b526a850ac9b76d36b764b2580efd.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 03/38] sequencer: use a helper to find the commit message","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:26:20Z","receivedAt":"2017-01-02T15:26:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"It is actually not safe to look for a commit message by looking for the\nfirst empty line and skipping it.\n\nThe find_commit_subject() function looks more carefully, so let's use\nit. Since we are interested in the entire commit message, we re-compute\nthe string length after verifying that the commit subject is not empty\n(in which case the entire commit message would be empty, something that\nshould not happen but that we want to handle gracefully).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 11 +++--------\n 1 file changed, 3 insertions(+), 8 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 3eededcb98..720857beda 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -703,14 +703,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tnext = commit;\n \t\tnext_label = msg.label;\n \n-\t\t/*\n-\t\t * Append the commit log message to msgbuf; it starts\n-\t\t * after the tree, parent, author, committer\n-\t\t * information followed by \"\\n\\n\".\n-\t\t */\n-\t\tp = strstr(msg.message, \"\\n\\n\");\n-\t\tif (p)\n-\t\t\tstrbuf_addstr(&msgbuf, skip_blank_lines(p + 2));\n+\t\t/* Append the commit log message to msgbuf. */\n+\t\tif (find_commit_subject(msg.message, &p))\n+\t\t\tstrbuf_addstr(&msgbuf, p);\n \n \t\tif (opts->record_origin) {\n \t\t\tif (!has_conforming_footer(&msgbuf, NULL, 0))\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308633","messageId":"a5d77fc7ffd430b437d3b0dfab4cc0e29f31a2b6.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 04/38] sequencer: support a new action: 'interactive rebase'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:26:28Z","receivedAt":"2017-01-02T15:26:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This patch introduces a new action for the sequencer. It really does not\ndo a whole lot of its own right now, but lays the ground work for\npatches to come. The intention, of course, is to finally make the\nsequencer the work horse of the interactive rebase (the original idea\nbehind the \"sequencer\" concept).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 36 ++++++++++++++++++++++++++++++++----\n sequencer.h |  3 ++-\n 2 files changed, 34 insertions(+), 5 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 720857beda..690460bc67 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -30,6 +30,14 @@ static GIT_PATH_FUNC(git_path_opts_file, \"sequencer/opts\")\n static GIT_PATH_FUNC(git_path_head_file, \"sequencer/head\")\n static GIT_PATH_FUNC(git_path_abort_safety_file, \"sequencer/abort-safety\")\n \n+static GIT_PATH_FUNC(rebase_path, \"rebase-merge\")\n+/*\n+ * The file containing rebase commands, comments, and empty lines.\n+ * This file is created by \"git rebase -i\" then edited by the user. As\n+ * the lines are processed, they are removed from the front of this\n+ * file and written to the tail of 'done'.\n+ */\n+static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n /*\n  * A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and\n  * GIT_AUTHOR_DATE that will be used for the commit that is currently\n@@ -42,19 +50,22 @@ static GIT_PATH_FUNC(rebase_path_author_script, \"rebase-merge/author-script\")\n  */\n static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, \"rebase-merge/gpg_sign_opt\")\n \n-/* We will introduce the 'interactive rebase' mode later */\n static inline int is_rebase_i(const struct replay_opts *opts)\n {\n-\treturn 0;\n+\treturn opts->action == REPLAY_INTERACTIVE_REBASE;\n }\n \n static const char *get_dir(const struct replay_opts *opts)\n {\n+\tif (is_rebase_i(opts))\n+\t\treturn rebase_path();\n \treturn git_path_seq_dir();\n }\n \n static const char *get_todo_path(const struct replay_opts *opts)\n {\n+\tif (is_rebase_i(opts))\n+\t\treturn rebase_path_todo();\n \treturn git_path_todo_file();\n }\n \n@@ -122,7 +133,15 @@ int sequencer_remove_state(struct replay_opts *opts)\n \n static const char *action_name(const struct replay_opts *opts)\n {\n-\treturn opts->action == REPLAY_REVERT ? N_(\"revert\") : N_(\"cherry-pick\");\n+\tswitch (opts->action) {\n+\tcase REPLAY_REVERT:\n+\t\treturn N_(\"revert\");\n+\tcase REPLAY_PICK:\n+\t\treturn N_(\"cherry-pick\");\n+\tcase REPLAY_INTERACTIVE_REBASE:\n+\t\treturn N_(\"rebase -i\");\n+\t}\n+\tdie(_(\"Unknown action: %d\"), opts->action);\n }\n \n struct commit_message {\n@@ -364,7 +383,9 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \n \tif (active_cache_changed &&\n \t    write_locked_index(&the_index, &index_lock, COMMIT_LOCK))\n-\t\t/* TRANSLATORS: %s will be \"revert\" or \"cherry-pick\" */\n+\t\t/* TRANSLATORS: %s will be \"revert\", \"cherry-pick\" or\n+\t\t * \"rebase -i\".\n+\t\t */\n \t\treturn error(_(\"%s: Unable to write new index file\"),\n \t\t\t_(action_name(opts)));\n \trollback_lock_file(&index_lock);\n@@ -1198,6 +1219,13 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)\n \tconst char *todo_path = get_todo_path(opts);\n \tint next = todo_list->current, offset, fd;\n \n+\t/*\n+\t * rebase -i writes \"git-rebase-todo\" without the currently executing\n+\t * command, appending it to \"done\" instead.\n+\t */\n+\tif (is_rebase_i(opts))\n+\t\tnext++;\n+\n \tfd = hold_lock_file_for_update(&todo_lock, todo_path, 0);\n \tif (fd < 0)\n \t\treturn error_errno(_(\"could not lock '%s'\"), todo_path);\ndiff --git a/sequencer.h b/sequencer.h\nindex 7a513c576b..cb21cfddee 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -7,7 +7,8 @@ const char *git_path_seq_dir(void);\n \n enum replay_action {\n \tREPLAY_REVERT,\n-\tREPLAY_PICK\n+\tREPLAY_PICK,\n+\tREPLAY_INTERACTIVE_REBASE\n };\n \n struct replay_opts {\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308634","messageId":"39bd6d62017990c1f99ca8438b298ea67197205b.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 05/38] sequencer (rebase -i): implement the 'noop' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:26:38Z","receivedAt":"2017-01-02T15:26:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The 'noop' command is probably the most boring of all rebase -i commands\nto support in the sequencer.\n\nWhich makes it an excellent candidate for this first stab to add support\nfor rebase -i's commands to the sequencer.\n\nFor the moment, let's also treat empty lines and commented-out lines as\n'noop'; We will refine that handling later in this patch series.\n\nTo make it easier to identify \"classes\" of todo_commands (such as:\ndetermine whether a command is pick-like, i.e. handles a single commit),\nlet's enforce a certain order of said commands.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 39 ++++++++++++++++++++++++++++++++++++---\n 1 file changed, 36 insertions(+), 3 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 690460bc67..84f18e64e9 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -607,14 +607,23 @@ 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_REVERT,\n+\t/* commands that do nothing but are counted for reporting progress */\n+\tTODO_NOOP\n };\n \n static const char *todo_command_strings[] = {\n \t\"pick\",\n-\t\"revert\"\n+\t\"revert\",\n+\t\"noop\"\n };\n \n static const char *command_to_string(const enum todo_command command)\n@@ -624,6 +633,10 @@ static const char *command_to_string(const enum todo_command command)\n \tdie(\"Unknown command: %d\", command);\n }\n \n+static int is_noop(const enum todo_command command)\n+{\n+\treturn TODO_NOOP <= (size_t)command;\n+}\n \n static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tstruct replay_opts *opts)\n@@ -879,6 +892,14 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \t/* left-trim */\n \tbol += strspn(bol, \" \\t\");\n \n+\tif (bol == eol || *bol == '\\r' || *bol == comment_line_char) {\n+\t\titem->command = TODO_NOOP;\n+\t\titem->commit = NULL;\n+\t\titem->arg = bol;\n+\t\titem->arg_len = eol - bol;\n+\t\treturn 0;\n+\t}\n+\n \tfor (i = 0; i < ARRAY_SIZE(todo_command_strings); i++)\n \t\tif (skip_prefix(bol, todo_command_strings[i], &bol)) {\n \t\t\titem->command = i;\n@@ -887,6 +908,13 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \tif (i >= ARRAY_SIZE(todo_command_strings))\n \t\treturn -1;\n \n+\tif (item->command == TODO_NOOP) {\n+\t\titem->commit = NULL;\n+\t\titem->arg = bol;\n+\t\titem->arg_len = eol - bol;\n+\t\treturn 0;\n+\t}\n+\n \t/* Eat up extra spaces/ tabs before object name */\n \tpadding = strspn(bol, \" \\t\");\n \tif (!padding)\n@@ -1289,7 +1317,12 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tstruct todo_item *item = todo_list->items + todo_list->current;\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n-\t\tres = do_pick_commit(item->command, item->commit, opts);\n+\t\tif (item->command <= TODO_REVERT)\n+\t\t\tres = do_pick_commit(item->command, item->commit,\n+\t\t\t\t\topts);\n+\t\telse if (!is_noop(item->command))\n+\t\t\treturn error(_(\"unknown command %d\"), item->command);\n+\n \t\ttodo_list->current++;\n \t\tif (res)\n \t\t\treturn res;\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308635","messageId":"736f100f4c219ee5c81e1e7b664128785df80521.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 06/38] sequencer (rebase -i): implement the 'edit' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:26:43Z","receivedAt":"2017-01-02T15:26:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This patch is a straight-forward reimplementation of the `edit`\noperation of the interactive rebase command.\n\nWell, not *quite* straight-forward: when stopping, the `edit`\ncommand wants to write the `patch` file (which is not only the\npatch, but includes the commit message and author information). To\nthat end, this patch requires the earlier work that taught the\nlog-tree machinery to respect the `file` setting of\nrev_info->diffopt to write to a file stream different than stdout.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--\n 1 file changed, 114 insertions(+), 3 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 84f18e64e9..b138a3906c 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -17,6 +17,7 @@\n #include \"argv-array.h\"\n #include \"quote.h\"\n #include \"trailer.h\"\n+#include \"log-tree.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n@@ -45,6 +46,20 @@ static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n  */\n static GIT_PATH_FUNC(rebase_path_author_script, \"rebase-merge/author-script\")\n /*\n+ * When an \"edit\" rebase command is being processed, the SHA1 of the\n+ * commit to be edited is recorded in this file.  When \"git rebase\n+ * --continue\" is executed, if there are any staged changes then they\n+ * will be amended to the HEAD commit, but only provided the HEAD\n+ * commit is still the commit to be edited.  When any other rebase\n+ * command is processed, this file is deleted.\n+ */\n+static GIT_PATH_FUNC(rebase_path_amend, \"rebase-merge/amend\")\n+/*\n+ * When we stop at a given patch via the \"edit\" command, this file contains\n+ * the abbreviated commit name of the corresponding patch.\n+ */\n+static GIT_PATH_FUNC(rebase_path_stopped_sha, \"rebase-merge/stopped-sha\")\n+/*\n  * The following files are written by git-rebase just after parsing the\n  * command-line (and are only consumed, not modified, by the sequencer).\n  */\n@@ -616,6 +631,7 @@ enum todo_command {\n \t/* commands that handle commits */\n \tTODO_PICK = 0,\n \tTODO_REVERT,\n+\tTODO_EDIT,\n \t/* commands that do nothing but are counted for reporting progress */\n \tTODO_NOOP\n };\n@@ -623,6 +639,7 @@ enum todo_command {\n static const char *todo_command_strings[] = {\n \t\"pick\",\n \t\"revert\",\n+\t\"edit\",\n \t\"noop\"\n };\n \n@@ -1302,9 +1319,87 @@ static int save_opts(struct replay_opts *opts)\n \treturn res;\n }\n \n+static int make_patch(struct commit *commit, struct replay_opts *opts)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tstruct rev_info log_tree_opt;\n+\tconst char *subject, *p;\n+\tint res = 0;\n+\n+\tp = short_commit_name(commit);\n+\tif (write_message(p, strlen(p), rebase_path_stopped_sha(), 1) < 0)\n+\t\treturn -1;\n+\n+\tstrbuf_addf(&buf, \"%s/patch\", get_dir(opts));\n+\tmemset(&log_tree_opt, 0, sizeof(log_tree_opt));\n+\tinit_revisions(&log_tree_opt, NULL);\n+\tlog_tree_opt.abbrev = 0;\n+\tlog_tree_opt.diff = 1;\n+\tlog_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;\n+\tlog_tree_opt.disable_stdin = 1;\n+\tlog_tree_opt.no_commit_id = 1;\n+\tlog_tree_opt.diffopt.file = fopen(buf.buf, \"w\");\n+\tlog_tree_opt.diffopt.use_color = GIT_COLOR_NEVER;\n+\tif (!log_tree_opt.diffopt.file)\n+\t\tres |= error_errno(_(\"could not open '%s'\"), buf.buf);\n+\telse {\n+\t\tres |= log_tree_commit(&log_tree_opt, commit);\n+\t\tfclose(log_tree_opt.diffopt.file);\n+\t}\n+\tstrbuf_reset(&buf);\n+\n+\tstrbuf_addf(&buf, \"%s/message\", get_dir(opts));\n+\tif (!file_exists(buf.buf)) {\n+\t\tconst char *commit_buffer = get_commit_buffer(commit, NULL);\n+\t\tfind_commit_subject(commit_buffer, &subject);\n+\t\tres |= write_message(subject, strlen(subject), buf.buf, 1);\n+\t\tunuse_commit_buffer(commit, commit_buffer);\n+\t}\n+\tstrbuf_release(&buf);\n+\n+\treturn res;\n+}\n+\n+static int intend_to_amend(void)\n+{\n+\tunsigned char head[20];\n+\tchar *p;\n+\n+\tif (get_sha1(\"HEAD\", head))\n+\t\treturn error(_(\"cannot read HEAD\"));\n+\n+\tp = sha1_to_hex(head);\n+\treturn write_message(p, strlen(p), rebase_path_amend(), 1);\n+}\n+\n+static int error_with_patch(struct commit *commit,\n+\tconst char *subject, int subject_len,\n+\tstruct replay_opts *opts, int exit_code, int to_amend)\n+{\n+\tif (make_patch(commit, opts))\n+\t\treturn -1;\n+\n+\tif (to_amend) {\n+\t\tif (intend_to_amend())\n+\t\t\treturn -1;\n+\n+\t\tfprintf(stderr, \"You can amend the commit now, with\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"  git commit --amend %s\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"Once you are satisfied with your changes, run\\n\"\n+\t\t\t\"\\n\"\n+\t\t\t\"  git rebase --continue\\n\", gpg_sign_opt_quoted(opts));\n+\t} else if (exit_code)\n+\t\tfprintf(stderr, \"Could not apply %s... %.*s\\n\",\n+\t\t\tshort_commit_name(commit), subject_len, subject);\n+\n+\treturn exit_code;\n+}\n+\n static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n {\n-\tint res;\n+\tint res = 0;\n \n \tsetenv(GIT_REFLOG_ACTION, action_name(opts), 0);\n \tif (opts->allow_ff)\n@@ -1317,10 +1412,20 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tstruct todo_item *item = todo_list->items + todo_list->current;\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n-\t\tif (item->command <= TODO_REVERT)\n+\t\tif (item->command <= TODO_EDIT) {\n \t\t\tres = do_pick_commit(item->command, item->commit,\n \t\t\t\t\topts);\n-\t\telse if (!is_noop(item->command))\n+\t\t\tif (item->command == TODO_EDIT) {\n+\t\t\t\tstruct commit *commit = item->commit;\n+\t\t\t\tif (!res)\n+\t\t\t\t\twarning(_(\"stopped at %s... %.*s\"),\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\treturn error_with_patch(commit,\n+\t\t\t\t\titem->arg, item->arg_len, opts, res,\n+\t\t\t\t\t!res);\n+\t\t\t}\n+\t\t} else if (!is_noop(item->command))\n \t\t\treturn error(_(\"unknown command %d\"), item->command);\n \n \t\ttodo_list->current++;\n@@ -1328,6 +1433,12 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\treturn res;\n \t}\n \n+\tif (is_rebase_i(opts)) {\n+\t\t/* Stopped in the middle, as planned? */\n+\t\tif (todo_list->current < todo_list->nr)\n+\t\t\treturn 0;\n+\t}\n+\n \t/*\n \t * Sequence of picks finished successfully; cleanup by\n \t * removing the .git/sequencer directory\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308636","messageId":"8f0c02dd91d69365554b3c410d95afb9d4665c87.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 07/38] sequencer (rebase -i): implement the 'exec' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:26:47Z","receivedAt":"2017-01-02T15:27:04Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The 'exec' command is a little special among rebase -i's commands, as it\ndoes *not* have a SHA-1 as first parameter. Instead, everything after the\n`exec` command is treated as command-line to execute.\n\nLet's reuse the arg/arg_len fields of the todo_item structure (which hold\nthe oneline for pick/edit commands) to point to the command-line.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 57 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex b138a3906c..e9c10d7fe5 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -18,6 +18,7 @@\n #include \"quote.h\"\n #include \"trailer.h\"\n #include \"log-tree.h\"\n+#include \"wt-status.h\"\n \n #define GIT_REFLOG_ACTION \"GIT_REFLOG_ACTION\"\n \n@@ -632,6 +633,8 @@ enum todo_command {\n \tTODO_PICK = 0,\n \tTODO_REVERT,\n \tTODO_EDIT,\n+\t/* commands that do something else than handling a single commit */\n+\tTODO_EXEC,\n \t/* commands that do nothing but are counted for reporting progress */\n \tTODO_NOOP\n };\n@@ -640,6 +643,7 @@ static const char *todo_command_strings[] = {\n \t\"pick\",\n \t\"revert\",\n \t\"edit\",\n+\t\"exec\",\n \t\"noop\"\n };\n \n@@ -938,6 +942,12 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \t\treturn -1;\n \tbol += padding;\n \n+\tif (item->command == TODO_EXEC) {\n+\t\titem->arg = bol;\n+\t\titem->arg_len = (int)(eol - bol);\n+\t\treturn 0;\n+\t}\n+\n \tend_of_object_name = (char *) bol + strcspn(bol, \" \\t\\n\");\n \tsaved = *end_of_object_name;\n \t*end_of_object_name = '\\0';\n@@ -1397,6 +1407,46 @@ static int error_with_patch(struct commit *commit,\n \treturn exit_code;\n }\n \n+static int do_exec(const char *command_line)\n+{\n+\tconst char *child_argv[] = { NULL, NULL };\n+\tint dirty, status;\n+\n+\tfprintf(stderr, \"Executing: %s\\n\", command_line);\n+\tchild_argv[0] = command_line;\n+\tstatus = run_command_v_opt(child_argv, RUN_USING_SHELL);\n+\n+\t/* force re-reading of the cache */\n+\tif (discard_cache() < 0 || read_cache() < 0)\n+\t\treturn error(_(\"could not read index\"));\n+\n+\tdirty = require_clean_work_tree(\"rebase\", NULL, 1, 1);\n+\n+\tif (status) {\n+\t\twarning(_(\"execution failed: %s\\n%s\"\n+\t\t\t  \"You can fix the problem, and then run\\n\"\n+\t\t\t  \"\\n\"\n+\t\t\t  \"  git rebase --continue\\n\"\n+\t\t\t  \"\\n\"),\n+\t\t\tcommand_line,\n+\t\t\tdirty ? N_(\"and made changes to the index and/or the \"\n+\t\t\t\t\"working tree\\n\") : \"\");\n+\t\tif (status == 127)\n+\t\t\t/* command not found */\n+\t\t\tstatus = 1;\n+\t} else if (dirty) {\n+\t\twarning(_(\"execution succeeded: %s\\nbut \"\n+\t\t\t  \"left changes to the index and/or the working tree\\n\"\n+\t\t\t  \"Commit or stash your changes, and then run\\n\"\n+\t\t\t  \"\\n\"\n+\t\t\t  \"  git rebase --continue\\n\"\n+\t\t\t  \"\\n\"), command_line);\n+\t\tstatus = 1;\n+\t}\n+\n+\treturn status;\n+}\n+\n static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n {\n \tint res = 0;\n@@ -1425,6 +1475,13 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\t\titem->arg, item->arg_len, opts, res,\n \t\t\t\t\t!res);\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\tint saved = *end_of_arg;\n+\n+\t\t\t*end_of_arg = '\\0';\n+\t\t\tres = do_exec(item->arg);\n+\t\t\t*end_of_arg = saved;\n \t\t} else if (!is_noop(item->command))\n \t\t\treturn error(_(\"unknown command %d\"), item->command);\n \n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308637","messageId":"6111bb6e05b82f4692f894c75edc8985aaf8d437.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 08/38] sequencer (rebase -i): learn about the 'verbose' mode","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:26:53Z","receivedAt":"2017-01-02T15:27:12Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When calling `git rebase -i -v`, the user wants to see some statistics\nafter the commits were rebased. Let's show some.\n\nThe strbuf we use to perform that task will be used for other things\nin subsequent commits, hence it is declared and initialized in a wider\nscope than strictly needed here.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 28 ++++++++++++++++++++++++++++\n sequencer.h |  1 +\n 2 files changed, 29 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex e9c10d7fe5..ddc4d144d7 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -65,6 +65,8 @@ static GIT_PATH_FUNC(rebase_path_stopped_sha, \"rebase-merge/stopped-sha\")\n  * command-line (and are only consumed, not modified, by the sequencer).\n  */\n static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, \"rebase-merge/gpg_sign_opt\")\n+static GIT_PATH_FUNC(rebase_path_orig_head, \"rebase-merge/orig-head\")\n+static GIT_PATH_FUNC(rebase_path_verbose, \"rebase-merge/verbose\")\n \n static inline int is_rebase_i(const struct replay_opts *opts)\n {\n@@ -1088,6 +1090,9 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\t}\n \t\tstrbuf_release(&buf);\n \n+\t\tif (file_exists(rebase_path_verbose()))\n+\t\t\topts->verbose = 1;\n+\n \t\treturn 0;\n \t}\n \n@@ -1491,9 +1496,32 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t}\n \n \tif (is_rebase_i(opts)) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\n \t\t/* Stopped in the middle, as planned? */\n \t\tif (todo_list->current < todo_list->nr)\n \t\t\treturn 0;\n+\n+\t\tif (opts->verbose) {\n+\t\t\tstruct rev_info log_tree_opt;\n+\t\t\tstruct object_id orig, head;\n+\n+\t\t\tmemset(&log_tree_opt, 0, sizeof(log_tree_opt));\n+\t\t\tinit_revisions(&log_tree_opt, NULL);\n+\t\t\tlog_tree_opt.diff = 1;\n+\t\t\tlog_tree_opt.diffopt.output_format =\n+\t\t\t\tDIFF_FORMAT_DIFFSTAT;\n+\t\t\tlog_tree_opt.disable_stdin = 1;\n+\n+\t\t\tif (read_oneliner(&buf, rebase_path_orig_head(), 0) &&\n+\t\t\t    !get_sha1(buf.buf, orig.hash) &&\n+\t\t\t    !get_sha1(\"HEAD\", head.hash)) {\n+\t\t\t\tdiff_tree_sha1(orig.hash, head.hash,\n+\t\t\t\t\t       \"\", &log_tree_opt.diffopt);\n+\t\t\t\tlog_tree_diff_flush(&log_tree_opt);\n+\t\t\t}\n+\t\t}\n+\t\tstrbuf_release(&buf);\n \t}\n \n \t/*\ndiff --git a/sequencer.h b/sequencer.h\nindex cb21cfddee..f885b68395 100644\n--- a/sequencer.h\n+++ b/sequencer.h\n@@ -24,6 +24,7 @@ struct replay_opts {\n \tint allow_empty;\n \tint allow_empty_message;\n \tint keep_redundant_commits;\n+\tint verbose;\n \n \tint mainline;\n \n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308638","messageId":"4f5b2b38d352c0abdc70fda55e6b47a8780a155a.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 09/38] sequencer (rebase -i): write the 'done' file","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:27:00Z","receivedAt":"2017-01-02T15:27:15Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"In the interactive rebase, commands that were successfully processed are\nnot simply discarded, but appended to the 'done' file instead. This is\nused e.g. to display the current state to the user in the output of\n`git status` or the progress.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 23 +++++++++++++++++++++++\n 1 file changed, 23 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex ddc4d144d7..8ea3d6aa94 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -41,6 +41,12 @@ static GIT_PATH_FUNC(rebase_path, \"rebase-merge\")\n  */\n static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n /*\n+ * The rebase command lines that have already been processed. A line\n+ * is moved here when it is first handled, before any associated user\n+ * actions.\n+ */\n+static GIT_PATH_FUNC(rebase_path_done, \"rebase-merge/done\")\n+/*\n  * A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and\n  * GIT_AUTHOR_DATE that will be used for the commit that is currently\n  * being rebased.\n@@ -1296,6 +1302,23 @@ static int save_todo(struct todo_list *todo_list, struct replay_opts *opts)\n \t\treturn error_errno(_(\"could not write to '%s'\"), todo_path);\n \tif (commit_lock_file(&todo_lock) < 0)\n \t\treturn error(_(\"failed to finalize '%s'.\"), todo_path);\n+\n+\tif (is_rebase_i(opts)) {\n+\t\tconst char *done_path = rebase_path_done();\n+\t\tint fd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);\n+\t\tint prev_offset = !next ? 0 :\n+\t\t\ttodo_list->items[next - 1].offset_in_buf;\n+\n+\t\tif (fd >= 0 && offset > prev_offset &&\n+\t\t    write_in_full(fd, todo_list->buf.buf + prev_offset,\n+\t\t\t\t  offset - prev_offset) < 0) {\n+\t\t\tclose(fd);\n+\t\t\treturn error_errno(_(\"could not write to '%s'\"),\n+\t\t\t\t\t   done_path);\n+\t\t}\n+\t\tif (fd >= 0)\n+\t\t\tclose(fd);\n+\t}\n \treturn 0;\n }\n \n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308639","messageId":"58cf9377b8df966d05feab3113e83ee07113a891.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 11/38] sequencer (rebase -i): implement the short commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:27:15Z","receivedAt":"2017-01-02T15:27:28Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"For users' convenience, most rebase commands can be abbreviated, e.g.\n'p' instead of 'pick' and 'x' instead of 'exec'. Let's teach the\nsequencer to handle those abbreviated commands just fine.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 35 +++++++++++++++++++++--------------\n 1 file changed, 21 insertions(+), 14 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 6a939a10bd..29b944d724 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -678,20 +678,23 @@ enum todo_command {\n \tTODO_NOOP\n };\n \n-static const char *todo_command_strings[] = {\n-\t\"pick\",\n-\t\"revert\",\n-\t\"edit\",\n-\t\"fixup\",\n-\t\"squash\",\n-\t\"exec\",\n-\t\"noop\"\n+static struct {\n+\tchar c;\n+\tconst char *str;\n+} todo_command_info[] = {\n+\t{ 'p', \"pick\" },\n+\t{ 0,   \"revert\" },\n+\t{ 'e', \"edit\" },\n+\t{ 'f', \"fixup\" },\n+\t{ 's', \"squash\" },\n+\t{ 'x', \"exec\" },\n+\t{ 0,   \"noop\" }\n };\n \n static const char *command_to_string(const enum todo_command command)\n {\n-\tif ((size_t)command < ARRAY_SIZE(todo_command_strings))\n-\t\treturn todo_command_strings[command];\n+\tif ((size_t)command < ARRAY_SIZE(todo_command_info))\n+\t\treturn todo_command_info[command].str;\n \tdie(\"Unknown command: %d\", command);\n }\n \n@@ -1087,12 +1090,16 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \t\treturn 0;\n \t}\n \n-\tfor (i = 0; i < ARRAY_SIZE(todo_command_strings); i++)\n-\t\tif (skip_prefix(bol, todo_command_strings[i], &bol)) {\n+\tfor (i = 0; i < ARRAY_SIZE(todo_command_info); i++)\n+\t\tif (skip_prefix(bol, todo_command_info[i].str, &bol)) {\n+\t\t\titem->command = i;\n+\t\t\tbreak;\n+\t\t} else if (bol[1] == ' ' && *bol == todo_command_info[i].c) {\n+\t\t\tbol++;\n \t\t\titem->command = i;\n \t\t\tbreak;\n \t\t}\n-\tif (i >= ARRAY_SIZE(todo_command_strings))\n+\tif (i >= ARRAY_SIZE(todo_command_info))\n \t\treturn -1;\n \n \tif (item->command == TODO_NOOP) {\n@@ -1287,7 +1294,7 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,\n {\n \tenum todo_command command = opts->action == REPLAY_PICK ?\n \t\tTODO_PICK : TODO_REVERT;\n-\tconst char *command_string = todo_command_strings[command];\n+\tconst char *command_string = todo_command_info[command].str;\n \tstruct commit *commit;\n \n \tif (prepare_revs(opts))\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308640","messageId":"3ee0dde0e1e694fcab10c62420f738ae3c67859e.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 10/38] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:27:07Z","receivedAt":"2017-01-02T15:27:36Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is a huge patch, and at the same time a huge step forward to\nexecute the performance-critical parts of the interactive rebase in a\nbuiltin command.\n\nSince 'fixup' and 'squash' are not only similar, but also need to know\nabout each other (we want to reduce a series of fixups/squashes into a\nsingle, final commit message edit, from the user's point of view), we\nreally have to implement them both at the same time.\n\nMost of the actual work is done by the existing code path that already\nhandles the \"pick\" and the \"edit\" commands; We added support for other\nfeatures (e.g. to amend the commit message) in the patches leading up to\nthis one, yet there are still quite a few bits in this patch that simply\nwould not make sense as individual patches (such as: determining whether\nthere was anything to \"fix up\" in the \"todo\" script, etc).\n\nIn theory, it would be possible to reuse the fast-forward code path also\nfor the fixup and the squash code paths, but in practice this would make\nthe code less readable. The end result cannot be fast-forwarded anyway,\ntherefore let's just extend the cherry-picking code path for now.\n\nSince the sequencer parses the entire `git-rebase-todo` script in one go,\nfixup or squash commands without a preceding pick can be reported early\n(in git-rebase--interactive, we could only report such errors just before\nexecuting the fixup/squash).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 227 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---\n 1 file changed, 217 insertions(+), 10 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 8ea3d6aa94..6a939a10bd 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -47,6 +47,35 @@ static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n  */\n static GIT_PATH_FUNC(rebase_path_done, \"rebase-merge/done\")\n /*\n+ * The commit message that is planned to be used for any changes that\n+ * need to be committed following a user interaction.\n+ */\n+static GIT_PATH_FUNC(rebase_path_message, \"rebase-merge/message\")\n+/*\n+ * The file into which is accumulated the suggested commit message for\n+ * squash/fixup commands. When the first of a series of squash/fixups\n+ * is seen, the file is created and the commit message from the\n+ * previous commit and from the first squash/fixup commit are written\n+ * to it. The commit message for each subsequent squash/fixup commit\n+ * is appended to the file as it is processed.\n+ *\n+ * The first line of the file is of the form\n+ *     # This is a combination of $count commits.\n+ * where $count is the number of commits whose messages have been\n+ * written to the file so far (including the initial \"pick\" commit).\n+ * Each time that a commit message is processed, this line is read and\n+ * updated. It is deleted just before the combined commit is made.\n+ */\n+static GIT_PATH_FUNC(rebase_path_squash_msg, \"rebase-merge/message-squash\")\n+/*\n+ * If the current series of squash/fixups has not yet included a squash\n+ * command, then this file exists and holds the commit message of the\n+ * original \"pick\" commit.  (If the series ends without a \"squash\"\n+ * command, then this can be used as the commit message of the combined\n+ * commit without opening the editor.)\n+ */\n+static GIT_PATH_FUNC(rebase_path_fixup_msg, \"rebase-merge/message-fixup\")\n+/*\n  * A script to set the GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, and\n  * GIT_AUTHOR_DATE that will be used for the commit that is currently\n  * being rebased.\n@@ -641,6 +670,8 @@ enum todo_command {\n \tTODO_PICK = 0,\n \tTODO_REVERT,\n \tTODO_EDIT,\n+\tTODO_FIXUP,\n+\tTODO_SQUASH,\n \t/* commands that do something else than handling a single commit */\n \tTODO_EXEC,\n \t/* commands that do nothing but are counted for reporting progress */\n@@ -651,6 +682,8 @@ static const char *todo_command_strings[] = {\n \t\"pick\",\n \t\"revert\",\n \t\"edit\",\n+\t\"fixup\",\n+\t\"squash\",\n \t\"exec\",\n \t\"noop\"\n };\n@@ -667,15 +700,114 @@ static int is_noop(const enum todo_command command)\n \treturn TODO_NOOP <= (size_t)command;\n }\n \n+static int is_fixup(enum todo_command command)\n+{\n+\treturn command == TODO_FIXUP || command == TODO_SQUASH;\n+}\n+\n+static int update_squash_messages(enum todo_command command,\n+\t\tstruct commit *commit, struct replay_opts *opts)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tint count, res;\n+\tconst char *message, *body;\n+\n+\tif (file_exists(rebase_path_squash_msg())) {\n+\t\tstruct strbuf header = STRBUF_INIT;\n+\t\tchar *eol, *p;\n+\n+\t\tif (strbuf_read_file(&buf, rebase_path_squash_msg(), 2048) <= 0)\n+\t\t\treturn error(_(\"could not read '%s'\"),\n+\t\t\t\trebase_path_squash_msg());\n+\n+\t\tp = buf.buf + 1;\n+\t\teol = strchrnul(buf.buf, '\\n');\n+\t\tif (buf.buf[0] != comment_line_char ||\n+\t\t    (p += strcspn(p, \"0123456789\\n\")) == eol)\n+\t\t\treturn error(_(\"unexpected 1st line of squash message:\"\n+\t\t\t\t       \"\\n\\n\\t%.*s\"),\n+\t\t\t\t     (int)(eol - buf.buf), buf.buf);\n+\t\tcount = strtol(p, NULL, 10);\n+\n+\t\tif (count < 1)\n+\t\t\treturn error(_(\"invalid 1st line of squash message:\\n\"\n+\t\t\t\t       \"\\n\\t%.*s\"),\n+\t\t\t\t     (int)(eol - buf.buf), buf.buf);\n+\n+\t\tstrbuf_addf(&header, \"%c \", comment_line_char);\n+\t\tstrbuf_addf(&header,\n+\t\t\t    _(\"This is a combination of %d commits.\"), ++count);\n+\t\tstrbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len);\n+\t\tstrbuf_release(&header);\n+\t} else {\n+\t\tunsigned char head[20];\n+\t\tstruct commit *head_commit;\n+\t\tconst char *head_message, *body;\n+\n+\t\tif (get_sha1(\"HEAD\", head))\n+\t\t\treturn error(_(\"need a HEAD to fixup\"));\n+\t\tif (!(head_commit = lookup_commit_reference(head)))\n+\t\t\treturn error(_(\"could not read HEAD\"));\n+\t\tif (!(head_message = get_commit_buffer(head_commit, NULL)))\n+\t\t\treturn error(_(\"could not read HEAD's commit message\"));\n+\n+\t\tfind_commit_subject(head_message, &body);\n+\t\tif (write_message(body, strlen(body),\n+\t\t\t\t  rebase_path_fixup_msg(), 0)) {\n+\t\t\tunuse_commit_buffer(head_commit, head_message);\n+\t\t\treturn error(_(\"cannot write '%s'\"),\n+\t\t\t\t     rebase_path_fixup_msg());\n+\t\t}\n+\n+\t\tcount = 2;\n+\t\tstrbuf_addf(&buf, \"%c \", comment_line_char);\n+\t\tstrbuf_addf(&buf, _(\"This is a combination of %d commits.\"),\n+\t\t\t    count);\n+\t\tstrbuf_addf(&buf, \"\\n%c \", comment_line_char);\n+\t\tstrbuf_addstr(&buf, _(\"This is the 1st commit message:\"));\n+\t\tstrbuf_addstr(&buf, \"\\n\\n\");\n+\t\tstrbuf_addstr(&buf, body);\n+\n+\t\tunuse_commit_buffer(head_commit, head_message);\n+\t}\n+\n+\tif (!(message = get_commit_buffer(commit, NULL)))\n+\t\treturn error(_(\"could not read commit message of %s\"),\n+\t\t\t     oid_to_hex(&commit->object.oid));\n+\tfind_commit_subject(message, &body);\n+\n+\tif (command == TODO_SQUASH) {\n+\t\tunlink(rebase_path_fixup_msg());\n+\t\tstrbuf_addf(&buf, \"\\n%c \", comment_line_char);\n+\t\tstrbuf_addf(&buf, _(\"This is the commit message #%d:\"), count);\n+\t\tstrbuf_addstr(&buf, \"\\n\\n\");\n+\t\tstrbuf_addstr(&buf, body);\n+\t} else if (command == TODO_FIXUP) {\n+\t\tstrbuf_addf(&buf, \"\\n%c \", comment_line_char);\n+\t\tstrbuf_addf(&buf, _(\"The commit message #%d will be skipped:\"),\n+\t\t\t    count);\n+\t\tstrbuf_addstr(&buf, \"\\n\\n\");\n+\t\tstrbuf_add_commented_lines(&buf, body, strlen(body));\n+\t} else\n+\t\treturn error(_(\"unknown command: %d\"), command);\n+\tunuse_commit_buffer(commit, message);\n+\n+\tres = write_message(buf.buf, buf.len, rebase_path_squash_msg(), 0);\n+\tstrbuf_release(&buf);\n+\treturn res;\n+}\n+\n static int do_pick_commit(enum todo_command command, struct commit *commit,\n-\t\tstruct replay_opts *opts)\n+\t\tstruct replay_opts *opts, int final_fixup)\n {\n+\tint edit = opts->edit, cleanup_commit_message = 0;\n+\tconst char *msg_file = edit ? NULL : git_path_merge_msg();\n \tunsigned char head[20];\n \tstruct commit *base, *next, *parent;\n \tconst char *base_label, *next_label;\n \tstruct commit_message msg = { NULL, NULL, NULL, NULL };\n \tstruct strbuf msgbuf = STRBUF_INIT;\n-\tint res, unborn = 0, allow;\n+\tint res, unborn = 0, amend = 0, allow;\n \n \tif (opts->no_commit) {\n \t\t/*\n@@ -720,7 +852,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \telse\n \t\tparent = commit->parents->item;\n \n-\tif (opts->allow_ff &&\n+\tif (opts->allow_ff && !is_fixup(command) &&\n \t    ((parent && !hashcmp(parent->object.oid.hash, head)) ||\n \t     (!parent && unborn)))\n \t\treturn fast_forward_to(commit->object.oid.hash, head, unborn, opts);\n@@ -779,6 +911,27 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t}\n \t}\n \n+\tif (is_fixup(command)) {\n+\t\tif (update_squash_messages(command, commit, opts))\n+\t\t\treturn -1;\n+\t\tamend = 1;\n+\t\tif (!final_fixup)\n+\t\t\tmsg_file = rebase_path_squash_msg();\n+\t\telse if (file_exists(rebase_path_fixup_msg())) {\n+\t\t\tcleanup_commit_message = 1;\n+\t\t\tmsg_file = rebase_path_fixup_msg();\n+\t\t} else {\n+\t\t\tconst char *dest = git_path(\"SQUASH_MSG\");\n+\t\t\tunlink(dest);\n+\t\t\tif (copy_file(dest, rebase_path_squash_msg(), 0666))\n+\t\t\t\treturn error(_(\"could not rename '%s' to '%s'\"),\n+\t\t\t\t\t     rebase_path_squash_msg(), dest);\n+\t\t\tunlink(git_path(\"MERGE_MSG\"));\n+\t\t\tmsg_file = dest;\n+\t\t\tedit = 1;\n+\t\t}\n+\t}\n+\n \tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n@@ -834,8 +987,13 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tgoto leave;\n \t}\n \tif (!opts->no_commit)\n-\t\tres = run_git_commit(opts->edit ? NULL : git_path_merge_msg(),\n-\t\t\t\t     opts, allow, opts->edit, 0, 0);\n+\t\tres = run_git_commit(msg_file, opts, allow, edit, amend,\n+\t\t\t\t     cleanup_commit_message);\n+\n+\tif (!res && final_fixup) {\n+\t\tunlink(rebase_path_fixup_msg());\n+\t\tunlink(rebase_path_squash_msg());\n+\t}\n \n leave:\n \tfree_message(commit, &msg);\n@@ -976,7 +1134,7 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n {\n \tstruct todo_item *item;\n \tchar *p = buf, *next_p;\n-\tint i, res = 0;\n+\tint i, res = 0, fixup_okay = file_exists(rebase_path_done());\n \n \tfor (i = 1; *p; i++, p = next_p) {\n \t\tchar *eol = strchrnul(p, '\\n');\n@@ -991,8 +1149,16 @@ 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 = -1;\n+\t\t\titem->command = TODO_NOOP;\n \t\t}\n+\n+\t\tif (fixup_okay)\n+\t\t\t; /* do nothing */\n+\t\telse if (is_fixup(item->command))\n+\t\t\treturn error(_(\"cannot '%s' without a previous commit\"),\n+\t\t\t\tcommand_to_string(item->command));\n+\t\telse if (!is_noop(item->command))\n+\t\t\tfixup_okay = 1;\n \t}\n \tif (!todo_list->nr)\n \t\treturn error(_(\"no commits parsed.\"));\n@@ -1435,6 +1601,20 @@ static int error_with_patch(struct commit *commit,\n \treturn exit_code;\n }\n \n+static int error_failed_squash(struct commit *commit,\n+\tstruct replay_opts *opts, int subject_len, const char *subject)\n+{\n+\tif (rename(rebase_path_squash_msg(), rebase_path_message()))\n+\t\treturn error(_(\"could not rename '%s' to '%s'\"),\n+\t\t\trebase_path_squash_msg(), rebase_path_message());\n+\tunlink(rebase_path_fixup_msg());\n+\tunlink(git_path(\"MERGE_MSG\"));\n+\tif (copy_file(git_path(\"MERGE_MSG\"), rebase_path_message(), 0666))\n+\t\treturn error(_(\"could not copy '%s' to '%s'\"),\n+\t\t\t     rebase_path_message(), git_path(\"MERGE_MSG\"));\n+\treturn error_with_patch(commit, subject, subject_len, opts, 1, 0);\n+}\n+\n static int do_exec(const char *command_line)\n {\n \tconst char *child_argv[] = { NULL, NULL };\n@@ -1475,6 +1655,21 @@ static int do_exec(const char *command_line)\n \treturn status;\n }\n \n+static int is_final_fixup(struct todo_list *todo_list)\n+{\n+\tint i = todo_list->current;\n+\n+\tif (!is_fixup(todo_list->items[i].command))\n+\t\treturn 0;\n+\n+\twhile (++i < todo_list->nr)\n+\t\tif (is_fixup(todo_list->items[i].command))\n+\t\t\treturn 0;\n+\t\telse if (!is_noop(todo_list->items[i].command))\n+\t\t\tbreak;\n+\treturn 1;\n+}\n+\n static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n {\n \tint res = 0;\n@@ -1490,9 +1685,15 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tstruct todo_item *item = todo_list->items + todo_list->current;\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n-\t\tif (item->command <= TODO_EDIT) {\n+\t\tif (is_rebase_i(opts)) {\n+\t\t\tunlink(rebase_path_message());\n+\t\t\tunlink(rebase_path_author_script());\n+\t\t\tunlink(rebase_path_stopped_sha());\n+\t\t\tunlink(rebase_path_amend());\n+\t\t}\n+\t\tif (item->command <= TODO_SQUASH) {\n \t\t\tres = do_pick_commit(item->command, item->commit,\n-\t\t\t\t\topts);\n+\t\t\t\t\topts, is_final_fixup(todo_list));\n \t\t\tif (item->command == TODO_EDIT) {\n \t\t\t\tstruct commit *commit = item->commit;\n \t\t\t\tif (!res)\n@@ -1503,6 +1704,12 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\t\titem->arg, item->arg_len, opts, res,\n \t\t\t\t\t!res);\n \t\t\t}\n+\t\t\tif (res && is_fixup(item->command)) {\n+\t\t\t\tif (res == 1)\n+\t\t\t\t\tintend_to_amend();\n+\t\t\t\treturn error_failed_squash(item->commit, opts,\n+\t\t\t\t\titem->arg_len, item->arg);\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\tint saved = *end_of_arg;\n@@ -1601,7 +1808,7 @@ static int single_pick(struct commit *cmit, struct replay_opts *opts)\n {\n \tsetenv(GIT_REFLOG_ACTION, action_name(opts), 0);\n \treturn do_pick_commit(opts->action == REPLAY_PICK ?\n-\t\tTODO_PICK : TODO_REVERT, cmit, opts);\n+\t\tTODO_PICK : TODO_REVERT, cmit, opts, 0);\n }\n \n int sequencer_pick_revisions(struct replay_opts *opts)\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308641","messageId":"c1318a1663643adea672e4af6d0f068b36e7cb02.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 12/38] sequencer (rebase -i): write an author-script file","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:27:18Z","receivedAt":"2017-01-02T15:27:39Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When the interactive rebase aborts, it writes out an author-script file\nto record the author information for the current commit. As we are about\nto teach the sequencer how to perform the actions behind an interactive\nrebase, it needs to write those author-script files, too.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 49 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 29b944d724..9913882603 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -483,6 +483,52 @@ static int is_index_unchanged(void)\n \treturn !hashcmp(active_cache_tree->sha1, head_commit->tree->object.oid.hash);\n }\n \n+static int write_author_script(const char *message)\n+{\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tconst char *eol;\n+\tint res;\n+\n+\tfor (;;)\n+\t\tif (!*message || starts_with(message, \"\\n\")) {\n+missing_author:\n+\t\t\t/* Missing 'author' line? */\n+\t\t\tunlink(rebase_path_author_script());\n+\t\t\treturn 0;\n+\t\t} else if (skip_prefix(message, \"author \", &message))\n+\t\t\tbreak;\n+\t\telse if ((eol = strchr(message, '\\n')))\n+\t\t\tmessage = eol + 1;\n+\t\telse\n+\t\t\tgoto missing_author;\n+\n+\tstrbuf_addstr(&buf, \"GIT_AUTHOR_NAME='\");\n+\twhile (*message && *message != '\\n' && *message != '\\r')\n+\t\tif (skip_prefix(message, \" <\", &message))\n+\t\t\tbreak;\n+\t\telse if (*message != '\\'')\n+\t\t\tstrbuf_addch(&buf, *(message++));\n+\t\telse\n+\t\t\tstrbuf_addf(&buf, \"'\\\\\\\\%c'\", *(message++));\n+\tstrbuf_addstr(&buf, \"'\\nGIT_AUTHOR_EMAIL='\");\n+\twhile (*message && *message != '\\n' && *message != '\\r')\n+\t\tif (skip_prefix(message, \"> \", &message))\n+\t\t\tbreak;\n+\t\telse if (*message != '\\'')\n+\t\t\tstrbuf_addch(&buf, *(message++));\n+\t\telse\n+\t\t\tstrbuf_addf(&buf, \"'\\\\\\\\%c'\", *(message++));\n+\tstrbuf_addstr(&buf, \"'\\nGIT_AUTHOR_DATE='@\");\n+\twhile (*message && *message != '\\n' && *message != '\\r')\n+\t\tif (*message != '\\'')\n+\t\t\tstrbuf_addch(&buf, *(message++));\n+\t\telse\n+\t\t\tstrbuf_addf(&buf, \"'\\\\\\\\%c'\", *(message++));\n+\tres = write_message(buf.buf, buf.len, rebase_path_author_script(), 1);\n+\tstrbuf_release(&buf);\n+\treturn res;\n+}\n+\n /*\n  * Read the author-script file into an environment block, ready for use in\n  * run_command(), that can be free()d afterwards.\n@@ -935,7 +981,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t}\n \t}\n \n-\tif (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n+\tif (is_rebase_i(opts) && write_author_script(msg.message) < 0)\n+\t\tres = -1;\n+\telse if (!opts->strategy || !strcmp(opts->strategy, \"recursive\") || command == TODO_REVERT) {\n \t\tres = do_recursive_merge(base, next, base_label, next_label,\n \t\t\t\t\t head, &msgbuf, opts);\n \t\tif (res < 0)\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308642","messageId":"8bd1f486c810836b0d7622163d3be0907607af92.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 15/38] sequencer (rebase -i): skip some revert/cherry-pick specific code path","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:27:30Z","receivedAt":"2017-01-02T15:27:43Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When a cherry-pick continues without a \"todo script\", the intention is\nsimply to pick a single commit.\n\nHowever, when an interactive rebase is continued without a \"todo\nscript\", it means that the last command has been completed and that we\nnow need to clean up.\n\nThis commit guards the revert/cherry-pick specific steps so that they\nare not executed in rebase -i mode.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 26 ++++++++++++++------------\n 1 file changed, 14 insertions(+), 12 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 52e17c8887..a7b9ee0d04 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1878,26 +1878,28 @@ int sequencer_continue(struct replay_opts *opts)\n \tif (is_rebase_i(opts)) {\n \t\tif (commit_staged_changes(opts))\n \t\t\treturn -1;\n-\t}\n-\tif (!file_exists(get_todo_path(opts)))\n+\t} else if (!file_exists(get_todo_path(opts)))\n \t\treturn continue_single_pick();\n \tif (read_populate_opts(opts))\n \t\treturn -1;\n \tif ((res = read_populate_todo(&todo_list, opts)))\n \t\tgoto release_todo_list;\n \n-\t/* Verify that the conflict has been resolved */\n-\tif (file_exists(git_path_cherry_pick_head()) ||\n-\t    file_exists(git_path_revert_head())) {\n-\t\tres = continue_single_pick();\n-\t\tif (res)\n+\tif (!is_rebase_i(opts)) {\n+\t\t/* Verify that the conflict has been resolved */\n+\t\tif (file_exists(git_path_cherry_pick_head()) ||\n+\t\t    file_exists(git_path_revert_head())) {\n+\t\t\tres = continue_single_pick();\n+\t\t\tif (res)\n+\t\t\t\tgoto release_todo_list;\n+\t\t}\n+\t\tif (index_differs_from(\"HEAD\", 0, 0)) {\n+\t\t\tres = error_dirty_index(opts);\n \t\t\tgoto release_todo_list;\n+\t\t}\n+\t\ttodo_list.current++;\n \t}\n-\tif (index_differs_from(\"HEAD\", 0, 0)) {\n-\t\tres = error_dirty_index(opts);\n-\t\tgoto release_todo_list;\n-\t}\n-\ttodo_list.current++;\n+\n \tres = pick_commits(&todo_list, opts);\n release_todo_list:\n \ttodo_list_release(&todo_list);\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308643","messageId":"0712fcd8a1489ec2cd67855d7940c1a167a04aa4.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 14/38] sequencer (rebase -i): remove CHERRY_PICK_HEAD when no longer needed","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:27:25Z","receivedAt":"2017-01-02T15:27:48Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The scripted version of the interactive rebase already does that.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 69301fecc6..52e17c8887 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1832,8 +1832,13 @@ static int commit_staged_changes(struct replay_opts *opts)\n \n \tif (has_unstaged_changes(1))\n \t\treturn error(_(\"cannot rebase: You have unstaged changes.\"));\n-\tif (!has_uncommitted_changes(0))\n+\tif (!has_uncommitted_changes(0)) {\n+\t\tconst char *cherry_pick_head = git_path(\"CHERRY_PICK_HEAD\");\n+\n+\t\tif (file_exists(cherry_pick_head) && unlink(cherry_pick_head))\n+\t\t\treturn error(_(\"could not remove CHERRY_PICK_HEAD\"));\n \t\treturn 0;\n+\t}\n \n \tif (file_exists(rebase_path_amend())) {\n \t\tstruct strbuf rev = STRBUF_INIT;\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308644","messageId":"7d3fe6850ffbf9252199c3d190e76efb18afeaca.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 13/38] sequencer (rebase -i): allow continuing with staged changes","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:27:21Z","receivedAt":"2017-01-02T15:27:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When an interactive rebase is interrupted, the user may stage changes\nbefore continuing, and we need to commit those changes in that case.\n\nPlease note that the nested \"if\" added to the sequencer_continue() is\nnot combined into a single \"if\" because it will be extended with an\n\"else\" clause in a later patch in this patch series.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 40 ++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 40 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 9913882603..69301fecc6 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1826,6 +1826,42 @@ static int continue_single_pick(void)\n \treturn run_command_v_opt(argv, RUN_GIT_CMD);\n }\n \n+static int commit_staged_changes(struct replay_opts *opts)\n+{\n+\tint amend = 0;\n+\n+\tif (has_unstaged_changes(1))\n+\t\treturn error(_(\"cannot rebase: You have unstaged changes.\"));\n+\tif (!has_uncommitted_changes(0))\n+\t\treturn 0;\n+\n+\tif (file_exists(rebase_path_amend())) {\n+\t\tstruct strbuf rev = STRBUF_INIT;\n+\t\tunsigned char head[20], to_amend[20];\n+\n+\t\tif (get_sha1(\"HEAD\", head))\n+\t\t\treturn error(_(\"cannot amend non-existing commit\"));\n+\t\tif (!read_oneliner(&rev, rebase_path_amend(), 0))\n+\t\t\treturn error(_(\"invalid file: '%s'\"), rebase_path_amend());\n+\t\tif (get_sha1_hex(rev.buf, to_amend))\n+\t\t\treturn error(_(\"invalid contents: '%s'\"),\n+\t\t\t\trebase_path_amend());\n+\t\tif (hashcmp(head, to_amend))\n+\t\t\treturn error(_(\"\\nYou have uncommitted changes in your \"\n+\t\t\t\t       \"working tree. Please, commit them\\n\"\n+\t\t\t\t       \"first and then run 'git rebase \"\n+\t\t\t\t       \"--continue' again.\"));\n+\n+\t\tstrbuf_release(&rev);\n+\t\tamend = 1;\n+\t}\n+\n+\tif (run_git_commit(rebase_path_message(), opts, 1, 1, amend, 0))\n+\t\treturn error(_(\"could not commit staged changes.\"));\n+\tunlink(rebase_path_amend());\n+\treturn 0;\n+}\n+\n int sequencer_continue(struct replay_opts *opts)\n {\n \tstruct todo_list todo_list = TODO_LIST_INIT;\n@@ -1834,6 +1870,10 @@ int sequencer_continue(struct replay_opts *opts)\n \tif (read_and_refresh_cache(opts))\n \t\treturn -1;\n \n+\tif (is_rebase_i(opts)) {\n+\t\tif (commit_staged_changes(opts))\n+\t\t\treturn -1;\n+\t}\n \tif (!file_exists(get_todo_path(opts)))\n \t\treturn continue_single_pick();\n \tif (read_populate_opts(opts))\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308645","messageId":"f7a123fa9fed3d157011ac6a42123742eec67298.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 16/38] sequencer (rebase -i): the todo can be empty when continuing","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:27:34Z","receivedAt":"2017-01-02T15:28:20Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When the last command of an interactive rebase fails, the user needs to\nresolve the problem and then continue the interactive rebase. Naturally,\nthe todo script is empty by then. So let's not complain about that!\n\nTo that end, let's move that test out of the function that parses the\ntodo script, and into the more high-level function read_populate_todo().\nThis is also necessary by now because the lower-level parse_insn_buffer()\nhas no idea whether we are performing an interactive rebase or not.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 7 +++++--\n 1 file changed, 5 insertions(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a7b9ee0d04..6a840216b1 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1215,8 +1215,7 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n \t\telse if (!is_noop(item->command))\n \t\t\tfixup_okay = 1;\n \t}\n-\tif (!todo_list->nr)\n-\t\treturn error(_(\"no commits parsed.\"));\n+\n \treturn res;\n }\n \n@@ -1240,6 +1239,10 @@ static int read_populate_todo(struct todo_list *todo_list,\n \tif (res)\n \t\treturn error(_(\"unusable instruction sheet: '%s'\"), todo_file);\n \n+\tif (!todo_list->nr &&\n+\t    (!is_rebase_i(opts) || !file_exists(rebase_path_done())))\n+\t\treturn error(_(\"no commits parsed.\"));\n+\n \tif (!is_rebase_i(opts)) {\n \t\tenum todo_command valid =\n \t\t\topts->action == REPLAY_PICK ? TODO_PICK : TODO_REVERT;\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308646","messageId":"59d8be697e7fc27caf255c1bc5c089752694e29c.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 18/38] sequencer (rebase -i): leave a patch upon error","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:27:57Z","receivedAt":"2017-01-02T15:28:23Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When doing an interactive rebase, we want to leave a 'patch' file for\nfurther inspection by the user (even if we never tried to actually apply\nthat patch, since we're cherry-picking instead).\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 80b2b2a975..a2002f1c12 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1769,7 +1769,9 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\t\tintend_to_amend();\n \t\t\t\treturn error_failed_squash(item->commit, opts,\n \t\t\t\t\titem->arg_len, item->arg);\n-\t\t\t}\n+\t\t\t} else if (res && is_rebase_i(opts))\n+\t\t\t\treturn res | error_with_patch(item->commit,\n+\t\t\t\t\titem->arg, item->arg_len, opts, res, 0);\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\tint saved = *end_of_arg;\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308647","messageId":"ff57bff73a0d9a6cc961fdf00a608f3d554bb719.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 17/38] sequencer (rebase -i): update refs after a successful rebase","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:27:53Z","receivedAt":"2017-01-02T15:28:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"An interactive rebase operates on a detached HEAD (to keep the reflog\nof the original branch relatively clean), and updates the branch only\nat the end.\n\nNow that the sequencer learns to perform interactive rebases, it also\nneeds to learn the trick to update the branch before removing the\ndirectory containing the state of the interactive rebase.\n\nWe introduce a new head_ref variable in a wider scope than necessary at\nthe moment, to allow for a later patch that prints out \"Successfully\nrebased and updated <ref>\".\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 46 +++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 45 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 6a840216b1..80b2b2a975 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -102,6 +102,8 @@ static GIT_PATH_FUNC(rebase_path_stopped_sha, \"rebase-merge/stopped-sha\")\n static GIT_PATH_FUNC(rebase_path_gpg_sign_opt, \"rebase-merge/gpg_sign_opt\")\n static GIT_PATH_FUNC(rebase_path_orig_head, \"rebase-merge/orig-head\")\n static GIT_PATH_FUNC(rebase_path_verbose, \"rebase-merge/verbose\")\n+static GIT_PATH_FUNC(rebase_path_head_name, \"rebase-merge/head-name\")\n+static GIT_PATH_FUNC(rebase_path_onto, \"rebase-merge/onto\")\n \n static inline int is_rebase_i(const struct replay_opts *opts)\n {\n@@ -1784,12 +1786,53 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t}\n \n \tif (is_rebase_i(opts)) {\n-\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tstruct strbuf head_ref = STRBUF_INIT, buf = STRBUF_INIT;\n \n \t\t/* Stopped in the middle, as planned? */\n \t\tif (todo_list->current < todo_list->nr)\n \t\t\treturn 0;\n \n+\t\tif (read_oneliner(&head_ref, rebase_path_head_name(), 0) &&\n+\t\t\t\tstarts_with(head_ref.buf, \"refs/\")) {\n+\t\t\tunsigned char head[20], orig[20];\n+\t\t\tint res;\n+\n+\t\t\tif (get_sha1(\"HEAD\", head)) {\n+\t\t\t\tres = error(_(\"cannot read HEAD\"));\n+cleanup_head_ref:\n+\t\t\t\tstrbuf_release(&head_ref);\n+\t\t\t\tstrbuf_release(&buf);\n+\t\t\t\treturn res;\n+\t\t\t}\n+\t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0) ||\n+\t\t\t\t\tget_sha1_hex(buf.buf, orig)) {\n+\t\t\t\tres = error(_(\"could not read orig-head\"));\n+\t\t\t\tgoto cleanup_head_ref;\n+\t\t\t}\n+\t\t\tstrbuf_addf(&buf, \"rebase -i (finish): %s onto \",\n+\t\t\t\thead_ref.buf);\n+\t\t\tif (!read_oneliner(&buf, rebase_path_onto(), 0)) {\n+\t\t\t\tres = error(_(\"could not read 'onto'\"));\n+\t\t\t\tgoto cleanup_head_ref;\n+\t\t\t}\n+\t\t\tif (update_ref(buf.buf, head_ref.buf, head, orig,\n+\t\t\t\t\tREF_NODEREF, UPDATE_REFS_MSG_ON_ERR)) {\n+\t\t\t\tres = error(_(\"could not update %s\"),\n+\t\t\t\t\thead_ref.buf);\n+\t\t\t\tgoto cleanup_head_ref;\n+\t\t\t}\n+\t\t\tstrbuf_reset(&buf);\n+\t\t\tstrbuf_addf(&buf,\n+\t\t\t\t\"rebase -i (finish): returning to %s\",\n+\t\t\t\thead_ref.buf);\n+\t\t\tif (create_symref(\"HEAD\", head_ref.buf, buf.buf)) {\n+\t\t\t\tres = error(_(\"could not update HEAD to %s\"),\n+\t\t\t\t\thead_ref.buf);\n+\t\t\t\tgoto cleanup_head_ref;\n+\t\t\t}\n+\t\t\tstrbuf_reset(&buf);\n+\t\t}\n+\n \t\tif (opts->verbose) {\n \t\t\tstruct rev_info log_tree_opt;\n \t\t\tstruct object_id orig, head;\n@@ -1810,6 +1853,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t}\n \t\t}\n \t\tstrbuf_release(&buf);\n+\t\tstrbuf_release(&head_ref);\n \t}\n \n \t/*\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308648","messageId":"ee15745b9c587de8816e3bdfebcf89e7962ee113.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 19/38] sequencer (rebase -i): implement the 'reword' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:28:00Z","receivedAt":"2017-01-02T15:28:35Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is now trivial, as all the building blocks are in place: all we need\nto do is to flip the \"edit\" switch when committing.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 9 +++++++--\n 1 file changed, 7 insertions(+), 2 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a2002f1c12..50e998acc4 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -718,6 +718,7 @@ enum todo_command {\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@@ -733,6 +734,7 @@ static struct {\n \t{ 'p', \"pick\" },\n \t{ 0,   \"revert\" },\n \t{ 'e', \"edit\" },\n+\t{ 'r', \"reword\" },\n \t{ 'f', \"fixup\" },\n \t{ 's', \"squash\" },\n \t{ 'x', \"exec\" },\n@@ -962,7 +964,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t}\n \t}\n \n-\tif (is_fixup(command)) {\n+\tif (command == TODO_REWORD)\n+\t\tedit = 1;\n+\telse if (is_fixup(command)) {\n \t\tif (update_squash_messages(command, commit, opts))\n \t\t\treturn -1;\n \t\tamend = 1;\n@@ -1771,7 +1775,8 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\t\titem->arg_len, item->arg);\n \t\t\t} else if (res && is_rebase_i(opts))\n \t\t\t\treturn res | error_with_patch(item->commit,\n-\t\t\t\t\titem->arg, item->arg_len, opts, res, 0);\n+\t\t\t\t\titem->arg, item->arg_len, opts, res,\n+\t\t\t\t\titem->command == TODO_REWORD);\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\tint saved = *end_of_arg;\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308649","messageId":"937c6f725ada6514554be13bd00eb036ada2bae2.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 21/38] sequencer (rebase -i): refactor setting the reflog message","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:28:09Z","receivedAt":"2017-01-02T15:28:38Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This makes the code DRYer, with the obvious benefit that we can enhance\nthe code further in a single place.\n\nWe can also reuse the functionality elsewhere by calling this new\nfunction.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 33 ++++++++++++++++++++++++++-------\n 1 file changed, 26 insertions(+), 7 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 23161f593e..0d8e11f580 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1743,6 +1743,26 @@ static int is_final_fixup(struct todo_list *todo_list)\n \treturn 1;\n }\n \n+static const char *reflog_message(struct replay_opts *opts,\n+\tconst char *sub_action, const char *fmt, ...)\n+{\n+\tva_list ap;\n+\tstatic struct strbuf buf = STRBUF_INIT;\n+\n+\tva_start(ap, fmt);\n+\tstrbuf_reset(&buf);\n+\tstrbuf_addstr(&buf, action_name(opts));\n+\tif (sub_action)\n+\t\tstrbuf_addf(&buf, \" (%s)\", sub_action);\n+\tif (fmt) {\n+\t\tstrbuf_addstr(&buf, \": \");\n+\t\tstrbuf_vaddf(&buf, fmt, ap);\n+\t}\n+\tva_end(ap);\n+\n+\treturn buf.buf;\n+}\n+\n static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n {\n \tint res = 0;\n@@ -1810,6 +1830,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \n \t\tif (read_oneliner(&head_ref, rebase_path_head_name(), 0) &&\n \t\t\t\tstarts_with(head_ref.buf, \"refs/\")) {\n+\t\t\tconst char *msg;\n \t\t\tunsigned char head[20], orig[20];\n \t\t\tint res;\n \n@@ -1825,23 +1846,21 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\tres = error(_(\"could not read orig-head\"));\n \t\t\t\tgoto cleanup_head_ref;\n \t\t\t}\n-\t\t\tstrbuf_addf(&buf, \"rebase -i (finish): %s onto \",\n-\t\t\t\thead_ref.buf);\n \t\t\tif (!read_oneliner(&buf, rebase_path_onto(), 0)) {\n \t\t\t\tres = error(_(\"could not read 'onto'\"));\n \t\t\t\tgoto cleanup_head_ref;\n \t\t\t}\n-\t\t\tif (update_ref(buf.buf, head_ref.buf, head, orig,\n+\t\t\tmsg = reflog_message(opts, \"finish\", \"%s onto %s\",\n+\t\t\t\thead_ref.buf, buf.buf);\n+\t\t\tif (update_ref(msg, head_ref.buf, head, orig,\n \t\t\t\t\tREF_NODEREF, UPDATE_REFS_MSG_ON_ERR)) {\n \t\t\t\tres = error(_(\"could not update %s\"),\n \t\t\t\t\thead_ref.buf);\n \t\t\t\tgoto cleanup_head_ref;\n \t\t\t}\n-\t\t\tstrbuf_reset(&buf);\n-\t\t\tstrbuf_addf(&buf,\n-\t\t\t\t\"rebase -i (finish): returning to %s\",\n+\t\t\tmsg = reflog_message(opts, \"finish\", \"returning to %s\",\n \t\t\t\thead_ref.buf);\n-\t\t\tif (create_symref(\"HEAD\", head_ref.buf, buf.buf)) {\n+\t\t\tif (create_symref(\"HEAD\", head_ref.buf, msg)) {\n \t\t\t\tres = error(_(\"could not update HEAD to %s\"),\n \t\t\t\t\thead_ref.buf);\n \t\t\t\tgoto cleanup_head_ref;\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308650","messageId":"9c687b46a78c8eb4e9b5a6c4b979bbf1ba8269b9.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 23/38] sequencer (rebase -i): copy commit notes at end","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:28:16Z","receivedAt":"2017-01-02T15:28:40Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When rebasing commits that have commit notes attached, the interactive\nrebase rewrites those notes faithfully at the end. The sequencer must\ndo this, too, if it wishes to do interactive rebase's job.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 76 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 95ae4bcd1e..50380a15b8 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -96,6 +96,15 @@ static GIT_PATH_FUNC(rebase_path_amend, \"rebase-merge/amend\")\n  */\n static GIT_PATH_FUNC(rebase_path_stopped_sha, \"rebase-merge/stopped-sha\")\n /*\n+ * For the post-rewrite hook, we make a list of rewritten commits and\n+ * their new sha1s.  The rewritten-pending list keeps the sha1s of\n+ * commits that have been processed, but not committed yet,\n+ * e.g. because they are waiting for a 'squash' command.\n+ */\n+static GIT_PATH_FUNC(rebase_path_rewritten_list, \"rebase-merge/rewritten-list\")\n+static GIT_PATH_FUNC(rebase_path_rewritten_pending,\n+\t\"rebase-merge/rewritten-pending\")\n+/*\n  * The following files are written by git-rebase just after parsing the\n  * command-line (and are only consumed, not modified, by the sequencer).\n  */\n@@ -850,6 +859,44 @@ static int update_squash_messages(enum todo_command command,\n \treturn res;\n }\n \n+static void flush_rewritten_pending(void) {\n+\tstruct strbuf buf = STRBUF_INIT;\n+\tunsigned char newsha1[20];\n+\tFILE *out;\n+\n+\tif (strbuf_read_file(&buf, rebase_path_rewritten_pending(), 82) > 0 &&\n+\t\t\t!get_sha1(\"HEAD\", newsha1) &&\n+\t\t\t(out = fopen(rebase_path_rewritten_list(), \"a\"))) {\n+\t\tchar *bol = buf.buf, *eol;\n+\n+\t\twhile (*bol) {\n+\t\t\teol = strchrnul(bol, '\\n');\n+\t\t\tfprintf(out, \"%.*s %s\\n\", (int)(eol - bol),\n+\t\t\t\t\tbol, sha1_to_hex(newsha1));\n+\t\t\tif (!*eol)\n+\t\t\t\tbreak;\n+\t\t\tbol = eol + 1;\n+\t\t}\n+\t\tfclose(out);\n+\t\tunlink(rebase_path_rewritten_pending());\n+\t}\n+\tstrbuf_release(&buf);\n+}\n+\n+static void record_in_rewritten(struct object_id *oid,\n+\t\tenum todo_command next_command) {\n+\tFILE *out = fopen(rebase_path_rewritten_pending(), \"a\");\n+\n+\tif (!out)\n+\t\treturn;\n+\n+\tfprintf(out, \"%s\\n\", oid_to_hex(oid));\n+\tfclose(out);\n+\n+\tif (!is_fixup(next_command))\n+\t\tflush_rewritten_pending();\n+}\n+\n static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tstruct replay_opts *opts, int final_fixup)\n {\n@@ -1743,6 +1790,17 @@ static int is_final_fixup(struct todo_list *todo_list)\n \treturn 1;\n }\n \n+static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n+{\n+\tint i;\n+\n+\tfor (i = todo_list->current + offset; i < todo_list->nr; i++)\n+\t\tif (!is_noop(todo_list->items[i].command))\n+\t\t\treturn todo_list->items[i].command;\n+\n+\treturn -1;\n+}\n+\n static const char *reflog_message(struct replay_opts *opts,\n \tconst char *sub_action, const char *fmt, ...)\n {\n@@ -1801,6 +1859,9 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\t\titem->arg, item->arg_len, opts, res,\n \t\t\t\t\t!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+\t\t\t\t\tpeek_command(todo_list, 1));\n \t\t\tif (res && is_fixup(item->command)) {\n \t\t\t\tif (res == 1)\n \t\t\t\t\tintend_to_amend();\n@@ -1827,6 +1888,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \n \tif (is_rebase_i(opts)) {\n \t\tstruct strbuf head_ref = STRBUF_INIT, buf = STRBUF_INIT;\n+\t\tstruct stat st;\n \n \t\t/* Stopped in the middle, as planned? */\n \t\tif (todo_list->current < todo_list->nr)\n@@ -1891,6 +1953,20 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\tlog_tree_diff_flush(&log_tree_opt);\n \t\t\t}\n \t\t}\n+\t\tflush_rewritten_pending();\n+\t\tif (!stat(rebase_path_rewritten_list(), &st) &&\n+\t\t\t\tst.st_size > 0) {\n+\t\t\tstruct child_process child = CHILD_PROCESS_INIT;\n+\n+\t\t\tchild.in = open(rebase_path_rewritten_list(), O_RDONLY);\n+\t\t\tchild.git_cmd = 1;\n+\t\t\targv_array_push(&child.args, \"notes\");\n+\t\t\targv_array_push(&child.args, \"copy\");\n+\t\t\targv_array_push(&child.args, \"--for-rewrite=rebase\");\n+\t\t\t/* we don't care if this copying failed */\n+\t\t\trun_command(&child);\n+\t\t}\n+\n \t\tstrbuf_release(&buf);\n \t\tstrbuf_release(&head_ref);\n \t}\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308651","messageId":"06952cd808c4d4344ff887d77684142752d59a76.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 20/38] sequencer (rebase -i): allow fast-forwarding for edit/reword","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:28:05Z","receivedAt":"2017-01-02T15:28:41Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The sequencer already knew how to fast-forward instead of\ncherry-picking, if possible.\n\nWe want to continue to do this, of course, but in case of the 'reword'\ncommand, we will need to call `git commit` after fast-forwarding.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 25 +++++++++++++++++--------\n 1 file changed, 17 insertions(+), 8 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 50e998acc4..23161f593e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -860,7 +860,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \tconst char *base_label, *next_label;\n \tstruct commit_message msg = { NULL, NULL, NULL, NULL };\n \tstruct strbuf msgbuf = STRBUF_INIT;\n-\tint res, unborn = 0, amend = 0, allow;\n+\tint res, unborn = 0, amend = 0, allow = 0;\n \n \tif (opts->no_commit) {\n \t\t/*\n@@ -905,11 +905,23 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \telse\n \t\tparent = commit->parents->item;\n \n+\tif (get_message(commit, &msg) != 0)\n+\t\treturn error(_(\"cannot get commit message for %s\"),\n+\t\t\toid_to_hex(&commit->object.oid));\n+\n \tif (opts->allow_ff && !is_fixup(command) &&\n \t    ((parent && !hashcmp(parent->object.oid.hash, head)) ||\n-\t     (!parent && unborn)))\n-\t\treturn fast_forward_to(commit->object.oid.hash, head, unborn, opts);\n-\n+\t     (!parent && unborn))) {\n+\t\tif (is_rebase_i(opts))\n+\t\t\twrite_author_script(msg.message);\n+\t\tres = fast_forward_to(commit->object.oid.hash, head, unborn,\n+\t\t\topts);\n+\t\tif (res || command != TODO_REWORD)\n+\t\t\tgoto leave;\n+\t\tedit = amend = 1;\n+\t\tmsg_file = NULL;\n+\t\tgoto fast_forward_edit;\n+\t}\n \tif (parent && parse_commit(parent) < 0)\n \t\t/* TRANSLATORS: The first %s will be a \"todo\" command like\n \t\t   \"revert\" or \"pick\", the second %s a SHA1. */\n@@ -917,10 +929,6 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\t\tcommand_to_string(command),\n \t\t\toid_to_hex(&parent->object.oid));\n \n-\tif (get_message(commit, &msg) != 0)\n-\t\treturn error(_(\"cannot get commit message for %s\"),\n-\t\t\toid_to_hex(&commit->object.oid));\n-\n \t/*\n \t * \"commit\" is an existing commit.  We would want to apply\n \t * the difference it introduces since its first parent \"prev\"\n@@ -1044,6 +1052,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n \t\tgoto leave;\n \t}\n \tif (!opts->no_commit)\n+fast_forward_edit:\n \t\tres = run_git_commit(msg_file, opts, allow, edit, amend,\n \t\t\t\t     cleanup_commit_message);\n \n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308652","messageId":"90682e922832844663a761eaf96885f255a405d9.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 24/38] sequencer (rebase -i): record interrupted commits in rewritten, too","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:28:20Z","receivedAt":"2017-01-02T15:28:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"When continuing after a `pick` command failed, we want that commit\nto show up in the rewritten-list (and its notes to be rewritten), too.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 8 ++++++++\n 1 file changed, 8 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 50380a15b8..d7273fd1b3 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2060,6 +2060,14 @@ int sequencer_continue(struct replay_opts *opts)\n \t\t\tgoto release_todo_list;\n \t\t}\n \t\ttodo_list.current++;\n+\t} else if (file_exists(rebase_path_stopped_sha())) {\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tstruct object_id oid;\n+\n+\t\tif (read_oneliner(&buf, rebase_path_stopped_sha(), 1) &&\n+\t\t    !get_sha1_committish(buf.buf, oid.hash))\n+\t\t\trecord_in_rewritten(&oid, peek_command(&todo_list, 0));\n+\t\tstrbuf_release(&buf);\n \t}\n \n \tres = pick_commits(&todo_list, opts);\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308653","messageId":"60bfbf24fe89bad4bb8295baa216f98932d4ee8f.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 25/38] sequencer (rebase -i): run the post-rewrite hook, if needed","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:28:23Z","receivedAt":"2017-01-02T15:28:43Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 14 ++++++++++++++\n 1 file changed, 14 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex d7273fd1b3..43ced8db31 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1957,6 +1957,8 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tif (!stat(rebase_path_rewritten_list(), &st) &&\n \t\t\t\tst.st_size > 0) {\n \t\t\tstruct child_process child = CHILD_PROCESS_INIT;\n+\t\t\tconst char *post_rewrite_hook =\n+\t\t\t\tfind_hook(\"post-rewrite\");\n \n \t\t\tchild.in = open(rebase_path_rewritten_list(), O_RDONLY);\n \t\t\tchild.git_cmd = 1;\n@@ -1965,6 +1967,18 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\targv_array_push(&child.args, \"--for-rewrite=rebase\");\n \t\t\t/* we don't care if this copying failed */\n \t\t\trun_command(&child);\n+\n+\t\t\tif (post_rewrite_hook) {\n+\t\t\t\tstruct child_process hook = CHILD_PROCESS_INIT;\n+\n+\t\t\t\thook.in = open(rebase_path_rewritten_list(),\n+\t\t\t\t\tO_RDONLY);\n+\t\t\t\thook.stdout_to_stderr = 1;\n+\t\t\t\targv_array_push(&hook.args, post_rewrite_hook);\n+\t\t\t\targv_array_push(&hook.args, \"rebase\");\n+\t\t\t\t/* we don't care if this hook failed */\n+\t\t\t\trun_command(&hook);\n+\t\t\t}\n \t\t}\n \n \t\tstrbuf_release(&buf);\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308654","messageId":"0080cdee6b77664264961a4f073103cc4b482b00.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 22/38] sequencer (rebase -i): set the reflog message consistently","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:28:13Z","receivedAt":"2017-01-02T15:28:48Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"We already used the same reflog message as the scripted version of rebase\n-i when finishing. With this commit, we do that also for all the commands\nbefore that.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 4 ++++\n 1 file changed, 4 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 0d8e11f580..95ae4bcd1e 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1785,6 +1785,10 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\tunlink(rebase_path_amend());\n \t\t}\n \t\tif (item->command <= TODO_SQUASH) {\n+\t\t\tif (is_rebase_i(opts))\n+\t\t\t\tsetenv(\"GIT_REFLOG_ACTION\", reflog_message(opts,\n+\t\t\t\t\tcommand_to_string(item->command), NULL),\n+\t\t\t\t\t1);\n \t\t\tres = do_pick_commit(item->command, item->commit,\n \t\t\t\t\topts, is_final_fixup(todo_list));\n \t\t\tif (item->command == TODO_EDIT) {\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308655","messageId":"af4cb86a6db311a4f18ce476ac27a5a982ff7482.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 26/38] sequencer (rebase -i): respect the rebase.autostash setting","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:28:27Z","receivedAt":"2017-01-02T15:28:51Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Git's `rebase` command inspects the `rebase.autostash` config setting\nto determine whether it should stash any uncommitted changes before\nrebasing and re-apply them afterwards.\n\nAs we introduce more bits and pieces to let the sequencer act as\ninteractive rebase's backend, here is the part that adds support for\nthe autostash feature.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 43 +++++++++++++++++++++++++++++++++++++++++++\n 1 file changed, 43 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 43ced8db31..06f7cebe48 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -113,6 +113,7 @@ static GIT_PATH_FUNC(rebase_path_orig_head, \"rebase-merge/orig-head\")\n static GIT_PATH_FUNC(rebase_path_verbose, \"rebase-merge/verbose\")\n static GIT_PATH_FUNC(rebase_path_head_name, \"rebase-merge/head-name\")\n static GIT_PATH_FUNC(rebase_path_onto, \"rebase-merge/onto\")\n+static GIT_PATH_FUNC(rebase_path_autostash, \"rebase-merge/autostash\")\n \n static inline int is_rebase_i(const struct replay_opts *opts)\n {\n@@ -1801,6 +1802,47 @@ static enum todo_command peek_command(struct todo_list *todo_list, int offset)\n \treturn -1;\n }\n \n+static int apply_autostash(struct replay_opts *opts)\n+{\n+\tstruct strbuf stash_sha1 = STRBUF_INIT;\n+\tstruct child_process child = CHILD_PROCESS_INIT;\n+\tint ret = 0;\n+\n+\tif (!read_oneliner(&stash_sha1, rebase_path_autostash(), 1)) {\n+\t\tstrbuf_release(&stash_sha1);\n+\t\treturn 0;\n+\t}\n+\tstrbuf_trim(&stash_sha1);\n+\n+\tchild.git_cmd = 1;\n+\targv_array_push(&child.args, \"stash\");\n+\targv_array_push(&child.args, \"apply\");\n+\targv_array_push(&child.args, stash_sha1.buf);\n+\tif (!run_command(&child))\n+\t\tprintf(_(\"Applied autostash.\"));\n+\telse {\n+\t\tstruct child_process store = CHILD_PROCESS_INIT;\n+\n+\t\tstore.git_cmd = 1;\n+\t\targv_array_push(&store.args, \"stash\");\n+\t\targv_array_push(&store.args, \"store\");\n+\t\targv_array_push(&store.args, \"-m\");\n+\t\targv_array_push(&store.args, \"autostash\");\n+\t\targv_array_push(&store.args, \"-q\");\n+\t\targv_array_push(&store.args, stash_sha1.buf);\n+\t\tif (run_command(&store))\n+\t\t\tret = error(_(\"cannot store %s\"), stash_sha1.buf);\n+\t\telse\n+\t\t\tprintf(_(\"Applying autostash resulted in conflicts.\\n\"\n+\t\t\t\t\"Your changes are safe in the stash.\\n\"\n+\t\t\t\t\"You can run \\\"git stash pop\\\" or\"\n+\t\t\t\t\" \\\"git stash drop\\\" at any time.\\n\"));\n+\t}\n+\n+\tstrbuf_release(&stash_sha1);\n+\treturn ret;\n+}\n+\n static const char *reflog_message(struct replay_opts *opts,\n \tconst char *sub_action, const char *fmt, ...)\n {\n@@ -1980,6 +2022,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\trun_command(&hook);\n \t\t\t}\n \t\t}\n+\t\tapply_autostash(opts);\n \n \t\tstrbuf_release(&buf);\n \t\tstrbuf_release(&head_ref);\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308656","messageId":"71b0cfa8b619cebce4ff85f4fcd243a5725cb694.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 27/38] sequencer (rebase -i): respect strategy/strategy_opts settings","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:28:30Z","receivedAt":"2017-01-02T15:28:52Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The sequencer already has an idea about using different merge\nstrategies. We just piggy-back on top of that, using rebase -i's\nown settings, when running the sequencer in interactive rebase mode.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 26 +++++++++++++++++++++++++-\n 1 file changed, 25 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 06f7cebe48..04a64cf0dc 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -114,6 +114,8 @@ static GIT_PATH_FUNC(rebase_path_verbose, \"rebase-merge/verbose\")\n static GIT_PATH_FUNC(rebase_path_head_name, \"rebase-merge/head-name\")\n static GIT_PATH_FUNC(rebase_path_onto, \"rebase-merge/onto\")\n static GIT_PATH_FUNC(rebase_path_autostash, \"rebase-merge/autostash\")\n+static GIT_PATH_FUNC(rebase_path_strategy, \"rebase-merge/strategy\")\n+static GIT_PATH_FUNC(rebase_path_strategy_opts, \"rebase-merge/strategy_opts\")\n \n static inline int is_rebase_i(const struct replay_opts *opts)\n {\n@@ -1368,6 +1370,26 @@ static int populate_opts_cb(const char *key, const char *value, void *data)\n \treturn 0;\n }\n \n+static void read_strategy_opts(struct replay_opts *opts, struct strbuf *buf)\n+{\n+\tint i;\n+\n+\tstrbuf_reset(buf);\n+\tif (!read_oneliner(buf, rebase_path_strategy(), 0))\n+\t\treturn;\n+\topts->strategy = strbuf_detach(buf, NULL);\n+\tif (!read_oneliner(buf, rebase_path_strategy_opts(), 0))\n+\t\treturn;\n+\n+\topts->xopts_nr = split_cmdline(buf->buf, (const char ***)&opts->xopts);\n+\tfor (i = 0; i < opts->xopts_nr; i++) {\n+\t\tconst char *arg = opts->xopts[i];\n+\n+\t\tskip_prefix(arg, \"--\", &arg);\n+\t\topts->xopts[i] = xstrdup(arg);\n+\t}\n+}\n+\n static int read_populate_opts(struct replay_opts *opts)\n {\n \tif (is_rebase_i(opts)) {\n@@ -1381,11 +1403,13 @@ static int read_populate_opts(struct replay_opts *opts)\n \t\t\t\topts->gpg_sign = xstrdup(buf.buf + 2);\n \t\t\t}\n \t\t}\n-\t\tstrbuf_release(&buf);\n \n \t\tif (file_exists(rebase_path_verbose()))\n \t\t\topts->verbose = 1;\n \n+\t\tread_strategy_opts(opts, &buf);\n+\t\tstrbuf_release(&buf);\n+\n \t\treturn 0;\n \t}\n \n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308657","messageId":"6fadd5543520c06f2329bd3051922dbb01534fc5.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 28/38] sequencer (rebase -i): allow rescheduling commands","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:28:34Z","receivedAt":"2017-01-02T15:28:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The interactive rebase has the very special magic that a cherry-pick\nthat exits with a status different from 0 and 1 signifies a failure to\neven record that a cherry-pick was started.\n\nThis can happen e.g. when a fast-forward fails because it would\noverwrite untracked files.\n\nIn that case, we must reschedule the command that we thought we already\nhad at least started successfully.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 6 ++++++\n 1 file changed, 6 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 04a64cf0dc..dd5b843a84 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1915,6 +1915,12 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t\t\t\t1);\n \t\t\tres = do_pick_commit(item->command, item->commit,\n \t\t\t\t\topts, is_final_fixup(todo_list));\n+\t\t\tif (is_rebase_i(opts) && res < 0) {\n+\t\t\t\t/* Reschedule */\n+\t\t\t\ttodo_list->current--;\n+\t\t\t\tif (save_todo(todo_list, opts))\n+\t\t\t\t\treturn -1;\n+\t\t\t}\n \t\t\tif (item->command == TODO_EDIT) {\n \t\t\t\tstruct commit *commit = item->commit;\n \t\t\t\tif (!res)\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308659","messageId":"d9cf36422193fc9bd97fe7a0cccb7bf5eda93002.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 29/38] sequencer (rebase -i): implement the 'drop' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:34:34Z","receivedAt":"2017-01-02T15:34:58Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The parsing part of a 'drop' command is almost identical to parsing a\n'pick', while the operation is the same as that of a 'noop'.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 8 +++++---\n 1 file changed, 5 insertions(+), 3 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex dd5b843a84..6e92f186ae 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -736,7 +736,8 @@ enum todo_command {\n \t/* commands that do something else than handling a single commit */\n \tTODO_EXEC,\n \t/* commands that do nothing but are counted for reporting progress */\n-\tTODO_NOOP\n+\tTODO_NOOP,\n+\tTODO_DROP\n };\n \n static struct {\n@@ -750,7 +751,8 @@ static struct {\n \t{ 'f', \"fixup\" },\n \t{ 's', \"squash\" },\n \t{ 'x', \"exec\" },\n-\t{ 0,   \"noop\" }\n+\t{ 0,   \"noop\" },\n+\t{ 'd', \"drop\" }\n };\n \n static const char *command_to_string(const enum todo_command command)\n@@ -762,7 +764,7 @@ static const char *command_to_string(const enum todo_command command)\n \n static int is_noop(const enum todo_command command)\n {\n-\treturn TODO_NOOP <= (size_t)command;\n+\treturn TODO_NOOP <= command;\n }\n \n static int is_fixup(enum todo_command command)\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308660","messageId":"fa764016c4fa4cbb78436eabe1e1bd0ee1766890.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 30/38] sequencer (rebase -i): differentiate between comments and 'noop'","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:34:39Z","receivedAt":"2017-01-02T15:35:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"In the upcoming patch, we will support rebase -i's progress\nreporting. The progress skips comments but counts 'noop's.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 15 +++++++++------\n 1 file changed, 9 insertions(+), 6 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 6e92f186ae..41f80ea2c4 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -737,7 +737,9 @@ enum todo_command {\n \tTODO_EXEC,\n \t/* commands that do nothing but are counted for reporting progress */\n \tTODO_NOOP,\n-\tTODO_DROP\n+\tTODO_DROP,\n+\t/* comments (not counted for reporting progress) */\n+\tTODO_COMMENT\n };\n \n static struct {\n@@ -752,12 +754,13 @@ static struct {\n \t{ 's', \"squash\" },\n \t{ 'x', \"exec\" },\n \t{ 0,   \"noop\" },\n-\t{ 'd', \"drop\" }\n+\t{ 'd', \"drop\" },\n+\t{ 0,   NULL }\n };\n \n static const char *command_to_string(const enum todo_command command)\n {\n-\tif ((size_t)command < ARRAY_SIZE(todo_command_info))\n+\tif (command < TODO_COMMENT)\n \t\treturn todo_command_info[command].str;\n \tdie(\"Unknown command: %d\", command);\n }\n@@ -1198,14 +1201,14 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \tbol += strspn(bol, \" \\t\");\n \n \tif (bol == eol || *bol == '\\r' || *bol == comment_line_char) {\n-\t\titem->command = TODO_NOOP;\n+\t\titem->command = TODO_COMMENT;\n \t\titem->commit = NULL;\n \t\titem->arg = bol;\n \t\titem->arg_len = eol - bol;\n \t\treturn 0;\n \t}\n \n-\tfor (i = 0; i < ARRAY_SIZE(todo_command_info); i++)\n+\tfor (i = 0; i < TODO_COMMENT; i++)\n \t\tif (skip_prefix(bol, todo_command_info[i].str, &bol)) {\n \t\t\titem->command = i;\n \t\t\tbreak;\n@@ -1214,7 +1217,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)\n \t\t\titem->command = i;\n \t\t\tbreak;\n \t\t}\n-\tif (i >= ARRAY_SIZE(todo_command_info))\n+\tif (i >= TODO_COMMENT)\n \t\treturn -1;\n \n \tif (item->command == TODO_NOOP) {\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308661","messageId":"fe7fcd26ae2a37e189a6e34e325d308c8fa11ca2.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 32/38] sequencer: use run_command() directly","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:35:29Z","receivedAt":"2017-01-02T15:35:44Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Instead of using the convenience function run_command_v_opt_cd_env(), we\nnow use the run_command() function. The former function is simply a\nwrapper of the latter, trying to make it more convenient to use.\n\nHowever, we already have to construct the argv and the env parameters,\nand we will need even finer control e.g. over the output of the command,\nso let's just stop using the convenience function.\n\nBased on patches and suggestions by Johannes Sixt and Jeff King.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 36 ++++++++++++++++--------------------\n 1 file changed, 16 insertions(+), 20 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a0d0aaeaf8..c7dc5a2ad4 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -604,12 +604,13 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \t\t\t  int allow_empty, int edit, int amend,\n \t\t\t  int cleanup_commit_message)\n {\n-\tstruct argv_array env = ARGV_ARRAY_INIT, array;\n-\tint rc;\n+\tstruct child_process cmd = CHILD_PROCESS_INIT;\n \tconst char *value;\n \n+\tcmd.git_cmd = 1;\n+\n \tif (is_rebase_i(opts)) {\n-\t\tif (!read_env_script(&env)) {\n+\t\tif (read_env_script(&cmd.env_array)) {\n \t\t\tconst char *gpg_opt = gpg_sign_opt_quoted(opts);\n \n \t\t\treturn error(_(staged_changes_advice),\n@@ -617,39 +618,34 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \t\t}\n \t}\n \n-\targv_array_init(&array);\n-\targv_array_push(&array, \"commit\");\n-\targv_array_push(&array, \"-n\");\n+\targv_array_push(&cmd.args, \"commit\");\n+\targv_array_push(&cmd.args, \"-n\");\n \n \tif (amend)\n-\t\targv_array_push(&array, \"--amend\");\n+\t\targv_array_push(&cmd.args, \"--amend\");\n \tif (opts->gpg_sign)\n-\t\targv_array_pushf(&array, \"-S%s\", opts->gpg_sign);\n+\t\targv_array_pushf(&cmd.args, \"-S%s\", opts->gpg_sign);\n \tif (opts->signoff)\n-\t\targv_array_push(&array, \"-s\");\n+\t\targv_array_push(&cmd.args, \"-s\");\n \tif (defmsg)\n-\t\targv_array_pushl(&array, \"-F\", defmsg, NULL);\n+\t\targv_array_pushl(&cmd.args, \"-F\", defmsg, NULL);\n \tif (cleanup_commit_message)\n-\t\targv_array_push(&array, \"--cleanup=strip\");\n+\t\targv_array_push(&cmd.args, \"--cleanup=strip\");\n \tif (edit)\n-\t\targv_array_push(&array, \"-e\");\n+\t\targv_array_push(&cmd.args, \"-e\");\n \telse if (!cleanup_commit_message &&\n \t\t !opts->signoff && !opts->record_origin &&\n \t\t git_config_get_value(\"commit.cleanup\", &value))\n-\t\targv_array_push(&array, \"--cleanup=verbatim\");\n+\t\targv_array_push(&cmd.args, \"--cleanup=verbatim\");\n \n \tif (allow_empty)\n-\t\targv_array_push(&array, \"--allow-empty\");\n+\t\targv_array_push(&cmd.args, \"--allow-empty\");\n \n \tif (opts->allow_empty_message)\n-\t\targv_array_push(&array, \"--allow-empty-message\");\n+\t\targv_array_push(&cmd.args, \"--allow-empty-message\");\n \n-\trc = run_command_v_opt_cd_env(array.argv, RUN_GIT_CMD, NULL,\n-\t\t\t(const char *const *)env.argv);\n-\targv_array_clear(&array);\n-\targv_array_clear(&env);\n \n-\treturn rc;\n+\treturn run_command(&cmd);\n }\n \n static int is_original_commit_empty(struct commit *commit)\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308662","messageId":"f1ebd5e2de84d197326593a0c8018fd7e0446d6e.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 33/38] sequencer (rebase -i): show only failed `git commit`'s output","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:35:34Z","receivedAt":"2017-01-02T15:35:48Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is the behavior of the shell script version of the interactive\nrebase, by using the `output` function defined in `git-rebase.sh`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 18 ++++++++++++++++++\n 1 file changed, 18 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex c7dc5a2ad4..a501dfce38 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -610,6 +610,11 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \tcmd.git_cmd = 1;\n \n \tif (is_rebase_i(opts)) {\n+\t\tif (!edit) {\n+\t\t\tcmd.stdout_to_stderr = 1;\n+\t\t\tcmd.err = -1;\n+\t\t}\n+\n \t\tif (read_env_script(&cmd.env_array)) {\n \t\t\tconst char *gpg_opt = gpg_sign_opt_quoted(opts);\n \n@@ -644,6 +649,19 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \tif (opts->allow_empty_message)\n \t\targv_array_push(&cmd.args, \"--allow-empty-message\");\n \n+\tif (cmd.err == -1) {\n+\t\t/* hide stderr on success */\n+\t\tstruct strbuf buf = STRBUF_INIT;\n+\t\tint rc = pipe_command(&cmd,\n+\t\t\t\t      NULL, 0,\n+\t\t\t\t      /* stdout is already redirected */\n+\t\t\t\t      NULL, 0,\n+\t\t\t\t      &buf, 0);\n+\t\tif (rc)\n+\t\t\tfputs(buf.buf, stderr);\n+\t\tstrbuf_release(&buf);\n+\t\treturn rc;\n+\t}\n \n \treturn run_command(&cmd);\n }\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308663","messageId":"f688d6b0aba5cc761a15c0305af8c8250e953352.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 31/38] sequencer: make reading author-script more elegant","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:35:25Z","receivedAt":"2017-01-02T15:35:50Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Rather than abusing a strbuf to come up with an environment block, let's\njust use the argv_array structure which serves the same purpose much\nbetter.\n\nWhile at it, rename the function to reflect the fact that it does not\nreally care exactly what environment variables are defined in said file.\n\nSuggested-by: Jeff King <peff@peff.net>\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 32 +++++++++++---------------------\n 1 file changed, 11 insertions(+), 21 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 41f80ea2c4..a0d0aaeaf8 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -544,18 +544,17 @@ static int write_author_script(const char *message)\n }\n \n /*\n- * Read the author-script file into an environment block, ready for use in\n- * run_command(), that can be free()d afterwards.\n+ * Read a list of environment variable assignments (such as the author-script\n+ * file) into an environment block. Returns -1 on error, 0 otherwise.\n  */\n-static char **read_author_script(void)\n+static int read_env_script(struct argv_array *env)\n {\n \tstruct strbuf script = STRBUF_INIT;\n \tint i, count = 0;\n-\tchar *p, *p2, **env;\n-\tsize_t env_size;\n+\tchar *p, *p2;\n \n \tif (strbuf_read_file(&script, rebase_path_author_script(), 256) <= 0)\n-\t\treturn NULL;\n+\t\treturn -1;\n \n \tfor (p = script.buf; *p; p++)\n \t\tif (skip_prefix(p, \"'\\\\\\\\''\", (const char **)&p2))\n@@ -567,19 +566,12 @@ static char **read_author_script(void)\n \t\t\tcount++;\n \t\t}\n \n-\tenv_size = (count + 1) * sizeof(*env);\n-\tstrbuf_grow(&script, env_size);\n-\tmemmove(script.buf + env_size, script.buf, script.len);\n-\tp = script.buf + env_size;\n-\tenv = (char **)strbuf_detach(&script, NULL);\n-\n \tfor (i = 0; i < count; i++) {\n-\t\tenv[i] = p;\n+\t\targv_array_push(env, p);\n \t\tp += strlen(p) + 1;\n \t}\n-\tenv[count] = NULL;\n \n-\treturn env;\n+\treturn 0;\n }\n \n static const char staged_changes_advice[] =\n@@ -612,14 +604,12 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \t\t\t  int allow_empty, int edit, int amend,\n \t\t\t  int cleanup_commit_message)\n {\n-\tchar **env = NULL;\n-\tstruct argv_array array;\n+\tstruct argv_array env = ARGV_ARRAY_INIT, array;\n \tint rc;\n \tconst char *value;\n \n \tif (is_rebase_i(opts)) {\n-\t\tenv = read_author_script();\n-\t\tif (!env) {\n+\t\tif (!read_env_script(&env)) {\n \t\t\tconst char *gpg_opt = gpg_sign_opt_quoted(opts);\n \n \t\t\treturn error(_(staged_changes_advice),\n@@ -655,9 +645,9 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,\n \t\targv_array_push(&array, \"--allow-empty-message\");\n \n \trc = run_command_v_opt_cd_env(array.argv, RUN_GIT_CMD, NULL,\n-\t\t\t(const char *const *)env);\n+\t\t\t(const char *const *)env.argv);\n \targv_array_clear(&array);\n-\tfree(env);\n+\targv_array_clear(&env);\n \n \treturn rc;\n }\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308664","messageId":"4209508dfa80b1097a1b25b07f1a723c27d7c07d.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 34/38] sequencer (rebase -i): show only failed cherry-picks' output","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:35:39Z","receivedAt":"2017-01-02T15:35:55Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is the behavior of the shell script version of the interactive\nrebase, by using the `output` function defined in `git-rebase.sh`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 4 ++++\n 1 file changed, 4 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex a501dfce38..4f37ba8d33 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -433,6 +433,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \to.ancestor = base ? base_label : \"(empty tree)\";\n \to.branch1 = \"HEAD\";\n \to.branch2 = next ? next_label : \"(empty tree)\";\n+\tif (is_rebase_i(opts))\n+\t\to.buffer_output = 2;\n \n \thead_tree = parse_tree_indirect(head);\n \tnext_tree = next ? next->tree : empty_tree();\n@@ -444,6 +446,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,\n \tclean = merge_trees(&o,\n \t\t\t    head_tree,\n \t\t\t    next_tree, base_tree, &result);\n+\tif (is_rebase_i(opts) && clean <= 0)\n+\t\tfputs(o.obuf.buf, stdout);\n \tstrbuf_release(&o.obuf);\n \tif (clean < 0)\n \t\treturn clean;\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308665","messageId":"42517ba8f64410dd81fac7b7b31f855c1a3e9c7a.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 35/38] sequencer (rebase -i): suggest --edit-todo upon unknown command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:35:42Z","receivedAt":"2017-01-02T15:35:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"This is the same behavior as known from `git rebase -i`.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 6 +++++-\n 1 file changed, 5 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 4f37ba8d33..4792a3de3b 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1314,8 +1314,12 @@ static int read_populate_todo(struct todo_list *todo_list,\n \tclose(fd);\n \n \tres = parse_insn_buffer(todo_list->buf.buf, todo_list);\n-\tif (res)\n+\tif (res) {\n+\t\tif (is_rebase_i(opts))\n+\t\t\treturn error(_(\"please fix this using \"\n+\t\t\t\t       \"'git rebase --edit-todo'.\"));\n \t\treturn error(_(\"unusable instruction sheet: '%s'\"), todo_file);\n+\t}\n \n \tif (!todo_list->nr &&\n \t    (!is_rebase_i(opts) || !file_exists(rebase_path_done())))\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308666","messageId":"56d3adf6a954be4ee52b20cbb1c8957ed66b88c0.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 36/38] sequencer (rebase -i): show the progress","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:35:46Z","receivedAt":"2017-01-02T15:36:11Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The interactive rebase keeps the user informed about its progress.\nIf the sequencer wants to do the grunt work of the interactive\nrebase, it also needs to show that progress.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 32 ++++++++++++++++++++++++++++++++\n 1 file changed, 32 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 4792a3de3b..2c9c555ab6 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1181,6 +1181,7 @@ struct todo_list {\n \tstruct strbuf buf;\n \tstruct todo_item *items;\n \tint nr, alloc, current;\n+\tint done_nr, total_nr;\n };\n \n #define TODO_LIST_INIT { STRBUF_INIT }\n@@ -1297,6 +1298,17 @@ static int parse_insn_buffer(char *buf, struct todo_list *todo_list)\n \treturn res;\n }\n \n+static int count_commands(struct todo_list *todo_list)\n+{\n+\tint count = 0, i;\n+\n+\tfor (i = 0; i < todo_list->nr; i++)\n+\t\tif (todo_list->items[i].command != TODO_COMMENT)\n+\t\t\tcount++;\n+\n+\treturn count;\n+}\n+\n static int read_populate_todo(struct todo_list *todo_list,\n \t\t\tstruct replay_opts *opts)\n {\n@@ -1339,6 +1351,21 @@ static int read_populate_todo(struct todo_list *todo_list,\n \t\t\t\treturn error(_(\"cannot revert during a cherry-pick.\"));\n \t}\n \n+\tif (is_rebase_i(opts)) {\n+\t\tstruct todo_list done = TODO_LIST_INIT;\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\ttodo_list->done_nr = count_commands(&done);\n+\t\telse\n+\t\t\ttodo_list->done_nr = 0;\n+\n+\t\ttodo_list->total_nr = todo_list->done_nr\n+\t\t\t+ count_commands(todo_list);\n+\n+\t\ttodo_list_release(&done);\n+\t}\n+\n \treturn 0;\n }\n \n@@ -1920,6 +1947,11 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n \t\tif (is_rebase_i(opts)) {\n+\t\t\tif (item->command != TODO_COMMENT)\n+\t\t\t\tfprintf(stderr, \"Rebasing (%d/%d)%s\",\n+\t\t\t\t\t++(todo_list->done_nr),\n+\t\t\t\t\ttodo_list->total_nr,\n+\t\t\t\t\topts->verbose ? \"\\n\" : \"\\r\");\n \t\t\tunlink(rebase_path_message());\n \t\t\tunlink(rebase_path_author_script());\n \t\t\tunlink(rebase_path_stopped_sha());\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308667","messageId":"355835c0a42580b708683d1dc777d33026afc804.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 37/38] sequencer (rebase -i): write the progress into files","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:36:20Z","receivedAt":"2017-01-02T15:36:33Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"For the benefit of e.g. the shell prompt, the interactive rebase not\nonly displays the progress for the user to see, but also writes it into\nthe msgnum/end files in the state directory.\n\nTeach the sequencer this new trick.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 30 +++++++++++++++++++++++++++---\n 1 file changed, 27 insertions(+), 3 deletions(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 2c9c555ab6..b39cd21e03 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -47,6 +47,16 @@ static GIT_PATH_FUNC(rebase_path_todo, \"rebase-merge/git-rebase-todo\")\n  */\n static GIT_PATH_FUNC(rebase_path_done, \"rebase-merge/done\")\n /*\n+ * The file to keep track of how many commands were already processed (e.g.\n+ * for the prompt).\n+ */\n+static GIT_PATH_FUNC(rebase_path_msgnum, \"rebase-merge/msgnum\");\n+/*\n+ * The file to keep track of how many commands are to be processed in total\n+ * (e.g. for the prompt).\n+ */\n+static GIT_PATH_FUNC(rebase_path_msgtotal, \"rebase-merge/end\");\n+/*\n  * The commit message that is planned to be used for any changes that\n  * need to be committed following a user interaction.\n  */\n@@ -1353,6 +1363,7 @@ static int read_populate_todo(struct todo_list *todo_list,\n \n \tif (is_rebase_i(opts)) {\n \t\tstruct todo_list done = TODO_LIST_INIT;\n+\t\tFILE *f = fopen(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@@ -1362,8 +1373,12 @@ static int read_populate_todo(struct todo_list *todo_list,\n \n \t\ttodo_list->total_nr = todo_list->done_nr\n \t\t\t+ count_commands(todo_list);\n-\n \t\ttodo_list_release(&done);\n+\n+\t\tif (f) {\n+\t\t\tfprintf(f, \"%d\\n\", todo_list->total_nr);\n+\t\t\tfclose(f);\n+\t\t}\n \t}\n \n \treturn 0;\n@@ -1947,11 +1962,20 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\tif (save_todo(todo_list, opts))\n \t\t\treturn -1;\n \t\tif (is_rebase_i(opts)) {\n-\t\t\tif (item->command != TODO_COMMENT)\n+\t\t\tif (item->command != TODO_COMMENT) {\n+\t\t\t\tFILE *f = fopen(rebase_path_msgnum(), \"w\");\n+\n+\t\t\t\ttodo_list->done_nr++;\n+\n+\t\t\t\tif (f) {\n+\t\t\t\t\tfprintf(f, \"%d\\n\", todo_list->done_nr);\n+\t\t\t\t\tfclose(f);\n+\t\t\t\t}\n \t\t\t\tfprintf(stderr, \"Rebasing (%d/%d)%s\",\n-\t\t\t\t\t++(todo_list->done_nr),\n+\t\t\t\t\ttodo_list->done_nr,\n \t\t\t\t\ttodo_list->total_nr,\n \t\t\t\t\topts->verbose ? \"\\n\" : \"\\r\");\n+\t\t\t}\n \t\t\tunlink(rebase_path_message());\n \t\t\tunlink(rebase_path_author_script());\n \t\t\tunlink(rebase_path_stopped_sha());\n-- \n2.11.0.rc3.windows.1\n\n\n"},{"id":"308668","messageId":"a033bf2ea3ab4d0b77d720d73564a052ed0f2b8b.1483370556.git.johannes.schindelin@gmx.de","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"[PATCH v3 38/38] sequencer (rebase -i): write out the final message","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-02T15:36:25Z","receivedAt":"2017-01-02T15:36:48Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"The shell script version of the interactive rebase has a very specific\nfinal message. Teach the sequencer to print the same.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c | 3 +++\n 1 file changed, 3 insertions(+)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex b39cd21e03..0e7d2ca5c8 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -2127,6 +2127,9 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n \t\t}\n \t\tapply_autostash(opts);\n \n+\t\tfprintf(stderr, \"Successfully rebased and updated %s.\\n\",\n+\t\t\thead_ref.buf);\n+\n \t\tstrbuf_release(&buf);\n \t\tstrbuf_release(&head_ref);\n \t}\n-- \n2.11.0.rc3.windows.1\n"},{"id":"308936","messageId":"xmqqk2a635it.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"alpine.DEB.2.20.1701021610590.3469@virtualbox","subject":"Re: [PATCH v2 05/34] sequencer (rebase -i): learn about the 'verbose' mode","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-07T21:48:58Z","receivedAt":"2017-01-07T21:49:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> Hi Junio,\n>\n> On Tue, 13 Dec 2016, Junio C Hamano wrote:\n>\n>> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n>> \n>> > @@ -1493,9 +1498,26 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)\n>> >  \t}\n>> >  \n>> >  \tif (is_rebase_i(opts)) {\n>> > +\t\tstruct strbuf buf = STRBUF_INIT;\n>> > +\n>> >  \t\t/* Stopped in the middle, as planned? */\n>> >  \t\tif (todo_list->current < todo_list->nr)\n>> >  \t\t\treturn 0;\n>> > +\n>> > +\t\tif (opts->verbose) {\n>> > +\t\t\tconst char *argv[] = {\n>> > +\t\t\t\t\"diff-tree\", \"--stat\", NULL, NULL\n>> > +\t\t\t};\n>> > +\n>> > +\t\t\tif (!read_oneliner(&buf, rebase_path_orig_head(), 0))\n>> > +\t\t\t\treturn error(_(\"could not read '%s'\"),\n>> > +\t\t\t\t\trebase_path_orig_head());\n>> > +\t\t\tstrbuf_addstr(&buf, \"..HEAD\");\n>> > +\t\t\targv[2] = buf.buf;\n>> > +\t\t\trun_command_v_opt(argv, RUN_GIT_CMD);\n>> > +\t\t\tstrbuf_reset(&buf);\n>> > +\t\t}\n>> > +\t\tstrbuf_release(&buf);\n>> >  \t}\n>> \n>> It's a bit curious that the previous step avoided running a separate\n>> process and instead did \"diff-tree -p\" all in C, but this one does not.\n>\n> I guess my only defence is that I tried to be a little lazy.\n\nI actually was alluding to going the other way around, spawning\n\"diff-tree -p\" in the other codepath like this one does.\n"},{"id":"308943","messageId":"xmqq37gu2tzu.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"xmqqk2a635it.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v2 05/34] sequencer (rebase -i): learn about the 'verbose' mode","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-08T01:57:57Z","receivedAt":"2017-01-08T01:58:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n>\n> ...\n>>\n>> I guess my only defence is that I tried to be a little lazy.\n>\n> I actually was alluding to going the other way around, spawning\n> \"diff-tree -p\" in the other codepath like this one does.\n\nPre-emptively, because I expect it will take a while for me to clear\nthe backlog to get to your reroll already on the mailing list, I do\nnot mean to say that you must rewrite the other one to spawn to\nmatch this one.  I meant that I wouldn't have minded if the series\nwere done that way, as this kind of being \"little lazy\" would reduce\nthe chance of regression by reducing the number of exchanged parts\nand it is not necessarily a bad thing.\n"},{"id":"309072","messageId":"xmqqinpnuaxl.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"cover.1483370556.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v3 00/38] Teach the sequencer to act as rebase -i's backend","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-09T22:26:46Z","receivedAt":"2017-01-09T22:26:53Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> Changes since v2:\n>\n> - fixed a TRANSLATORS: comment\n> ...\n> - replaced a spawned `diff-tree` command by code using the diff functions\n>   directly\n\nI just finished skimming the interdiff (the difference between the\nresult of merging the v2 into 'master' and the result of applying\nthis series on 'master').  I didn't cross reference it with all the\nreview comments that I saw on the list for the previous round, I\nwouldn't have noticed even if there are issues that still haven't\nbeen addressed.  But what I saw in the interdiff were mostly good\nchanges.\n\nI however think that the renaming of read_author_script() is totally\nbackwards from maintainability's point of view.  \n\nBoth \"am\" and \"rebase\" use an external file whose name is the same\n\"author-script\" (because they are meant to serve the same purpose),\nand introduction of new code that is different from what \"am\" uses\nto read it in v2 was bad enough.  If the rename of the function in\nthis round really means \"author-script of 'am' can hold only author\ninformation, but that of 'rebase' can hold anything the end user\ndesires\", it makes things even worse.  If 'rebase' side extends the\nfile to hold other environment variables (in its own code), users\nwould wonder why the same extension will not be available in 'am'\nside (and vice versa).\n\nI ran out of time for the day and haven't looked at individual\npatches yet.  I may find other issues, but from the interdiff, that\nwas the only thing that I found even worse than the previous round,\nand everything else was either the same or has improved.\n\nThanks.\n"},{"id":"309076","messageId":"xmqqa8azu99k.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"xmqqinpnuaxl.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 00/38] Teach the sequencer to act as rebase -i's backend","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-09T23:02:47Z","receivedAt":"2017-01-09T23:02:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I ran out of time for the day and haven't looked at individual\n> patches yet.  I may find other issues, but from the interdiff, that\n> was the only thing that I found even worse than the previous round,\n> and everything else was either the same or has improved.\n\n... I forgot to say a few things:\n\n * In the meantime, I think this is overall better than what we had\n   for the past few weeks; I'll try to replace the copy in my tree\n   with this round, even though this may not be the final one.\n\n * There were a few style issues [*1*] pointed out by the\n   checkpatch.pl script borrowed from the kernel land.\n\n * And finally, you wrote \"This marks the count down to '3': two\n   more patch series after this (really tiny ones) and we have a\n   faster rebase -i.\"  This is seriously an exciting news.  Thanks.\n\nI probably am still forgetting to say a few more things, but I'd\nsend this message out in this shape for now.  Thanks.\n\n\n[Footnote]\n\n*1*\n\nSubject: [PATCH v3 10/38] sequencer (rebase -i): add support for the 'fixup' and 'squash' commands\nERROR: do not use assignment in if condition\n#118: FILE: sequencer.c:725:\n+\t\tif (buf.buf[0] != comment_line_char ||\n\nERROR: do not use assignment in if condition\n#142: FILE: sequencer.c:749:\n+\t\tif (!(head_commit = lookup_commit_reference(head)))\n\nERROR: do not use assignment in if condition\n#144: FILE: sequencer.c:751:\n+\t\tif (!(head_message = get_commit_buffer(head_commit, NULL)))\n\nERROR: do not use assignment in if condition\n#167: FILE: sequencer.c:774:\n+\tif (!(message = get_commit_buffer(commit, NULL)))\n\ntotal: 4 errors, 1 warnings, 320 lines checked\n------------------------------------------------\nSubject: [PATCH v3 12/38] sequencer (rebase -i): write an author-script file\nERROR: do not use assignment in if condition\n#35: FILE: sequencer.c:500:\n+\t\telse if ((eol = strchr(message, '\\n')))\n\ntotal: 1 errors, 0 warnings, 62 lines checked\n------------------------------------------------\nSubject: [PATCH v3 23/38] sequencer (rebase -i): copy commit notes at end\nERROR: open brace '{' following function declarations go on the next line\n#36: FILE: sequencer.c:862:\n+static void flush_rewritten_pending(void) {\n\nERROR: do not use assignment in if condition\n#41: FILE: sequencer.c:867:\n+\tif (strbuf_read_file(&buf, rebase_path_rewritten_pending(), 82) > 0 &&\n\ntotal: 2 errors, 0 warnings, 112 lines checked\n"},{"id":"309279","messageId":"xmqqk2a0ktxr.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"bc1a6c21c9ab2f55882c363e802bfcf37e49d79f.1483370556.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v3 01/38] sequencer: avoid unnecessary curly braces","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-12T18:35:28Z","receivedAt":"2017-01-12T18:35:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n>  \n> -\tif (!commit->parents) {\n> +\tif (!commit->parents)\n>  \t\tparent = NULL;\n> -\t}\n>  \telse if (commit->parents->next) {\n>  \t\t/* Reverting or cherry-picking a merge commit */\n>  \t\tint cnt;\n\nThe result becomes\n\n\tif (...)\n\t\tsingle statement;\n\telse if (...) {\n\t\tmultiple;\n                statements;\n        }\n\nwhich is not quite an improvement.  \n\nThe preferred style is for all arms in if/elseif/else cascade to\neither use or not use brace pairs, so I think a fix toward that goal\nwould be more like:\n\n\tif (!commit->parents) {\n\t\tparent = NULL;\n-\t}                \n-\telse if (commit->parents->next) {\n+\t} else if (commit->parents->next) {\n\t\t/* Reverting ...\n\n"},{"id":"309280","messageId":"xmqqfukoktwo.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"780d71b5844b526a850ac9b76d36b764b2580efd.1483370556.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v3 03/38] sequencer: use a helper to find the commit message","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-12T18:36:07Z","receivedAt":"2017-01-12T18:37:54Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> It is actually not safe to look for a commit message by looking for the\n> first empty line and skipping it.\n>\n> The find_commit_subject() function looks more carefully, so let's use\n> it. Since we are interested in the entire commit message, we re-compute\n> the string length after verifying that the commit subject is not empty\n> (in which case the entire commit message would be empty, something that\n> should not happen but that we want to handle gracefully).\n>\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n\nVery sensible.\n\n>  sequencer.c | 11 +++--------\n>  1 file changed, 3 insertions(+), 8 deletions(-)\n>\n> diff --git a/sequencer.c b/sequencer.c\n> index 3eededcb98..720857beda 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -703,14 +703,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,\n>  \t\tnext = commit;\n>  \t\tnext_label = msg.label;\n>  \n> -\t\t/*\n> -\t\t * Append the commit log message to msgbuf; it starts\n> -\t\t * after the tree, parent, author, committer\n> -\t\t * information followed by \"\\n\\n\".\n> -\t\t */\n> -\t\tp = strstr(msg.message, \"\\n\\n\");\n> -\t\tif (p)\n> -\t\t\tstrbuf_addstr(&msgbuf, skip_blank_lines(p + 2));\n> +\t\t/* Append the commit log message to msgbuf. */\n> +\t\tif (find_commit_subject(msg.message, &p))\n> +\t\t\tstrbuf_addstr(&msgbuf, p);\n>  \n>  \t\tif (opts->record_origin) {\n>  \t\t\tif (!has_conforming_footer(&msgbuf, NULL, 0))\n"},{"id":"309281","messageId":"xmqqbmvckta7.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"65e0dac0115713b6ae955acdbc5b7655aeb18e7c.1483370556.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v3 02/38] sequencer: move \"else\" keyword onto the same line as preceding brace","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-12T18:49:36Z","receivedAt":"2017-01-12T18:49:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> It is the current coding style of the Git project to write\n>\n> \tif (...) {\n> \t\t...\n> \t} else {\n> \t\t...\n> \t}\n>\n> instead of putting the closing brace and the \"else\" keyword on separate\n> lines.\n>\n> Pointed out by Junio Hamano.\n\nFor a small thing like this, the attribution is mostly irrelevant,\nbut for the record, the credit should go to checkpatch.pl from the\nkernel project ;-).\n\nI'll try to squash the part that was touched by 01/38 into this\npatch locally; even though I haven't finished going through the\nindividual patches, I expect that my conclusion would be what I\nsaid in <xmqqinpnuaxl.fsf@gitster.mtv.corp.google.com>, i.e. the\nchanges in the series are mostly good and improved relative to the\nprevious round except for the parts reading and writing of\nauthor-script (from the maintainability's point of view).  \n\nAs I do not think we want to see another reroll of three-dozen\npatches, I am leaning towards merging v3 (with minimum fixes like\nsquashing 01/ and 02/ into one) to 'next' and cook it there and\nfix remaining issues incrementally while the series is in 'next'.\n\nI may have to change my mind after I read through the remaining\npatches (I've done with the first dozen or so at this moment), but I\ndo not expect that, judging from what I saw in the interdiff.\n\nThanks.\n\n> Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n> ---\n>  sequencer.c | 3 +--\n>  1 file changed, 1 insertion(+), 2 deletions(-)\n>\n> diff --git a/sequencer.c b/sequencer.c\n> index 23793db08b..3eededcb98 100644\n> --- a/sequencer.c\n> +++ b/sequencer.c\n> @@ -1070,8 +1070,7 @@ static int create_seq_dir(void)\n>  \t\terror(_(\"a cherry-pick or revert is already in progress\"));\n>  \t\tadvise(_(\"try \\\"git cherry-pick (--continue | --quit | --abort)\\\"\"));\n>  \t\treturn -1;\n> -\t}\n> -\telse if (mkdir(git_path_seq_dir(), 0777) < 0)\n> +\t} else if (mkdir(git_path_seq_dir(), 0777) < 0)\n>  \t\treturn error_errno(_(\"could not create sequencer directory '%s'\"),\n>  \t\t\t\t   git_path_seq_dir());\n>  \treturn 0;\n"},{"id":"309282","messageId":"xmqq7f60kssh.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"736f100f4c219ee5c81e1e7b664128785df80521.1483370556.git.johannes.schindelin@gmx.de","subject":"Re: [PATCH v3 06/38] sequencer (rebase -i): implement the 'edit' command","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-12T19:00:14Z","receivedAt":"2017-01-12T19:00:23Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n\n> +static int make_patch(struct commit *commit, struct replay_opts *opts)\n> +{\n> +\tstruct strbuf buf = STRBUF_INIT;\n> +\tstruct rev_info log_tree_opt;\n> +\tconst char *subject, *p;\n> +\tint res = 0;\n> +\n> +\tp = short_commit_name(commit);\n> +\tif (write_message(p, strlen(p), rebase_path_stopped_sha(), 1) < 0)\n> +\t\treturn -1;\n> +\n> +\tstrbuf_addf(&buf, \"%s/patch\", get_dir(opts));\n> +\tmemset(&log_tree_opt, 0, sizeof(log_tree_opt));\n> +\tinit_revisions(&log_tree_opt, NULL);\n> +\tlog_tree_opt.abbrev = 0;\n> +\tlog_tree_opt.diff = 1;\n> +\tlog_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;\n> +\tlog_tree_opt.disable_stdin = 1;\n> +\tlog_tree_opt.no_commit_id = 1;\n> +\tlog_tree_opt.diffopt.file = fopen(buf.buf, \"w\");\n> +\tlog_tree_opt.diffopt.use_color = GIT_COLOR_NEVER;\n> +\tif (!log_tree_opt.diffopt.file)\n> +\t\tres |= error_errno(_(\"could not open '%s'\"), buf.buf);\n> +\telse {\n> +\t\tres |= log_tree_commit(&log_tree_opt, commit);\n> +\t\tfclose(log_tree_opt.diffopt.file);\n> +\t}\n> +\tstrbuf_reset(&buf);\n> +\n> +\tstrbuf_addf(&buf, \"%s/message\", get_dir(opts));\n> +\tif (!file_exists(buf.buf)) {\n> +\t\tconst char *commit_buffer = get_commit_buffer(commit, NULL);\n> +\t\tfind_commit_subject(commit_buffer, &subject);\n> +\t\tres |= write_message(subject, strlen(subject), buf.buf, 1);\n> +\t\tunuse_commit_buffer(commit, commit_buffer);\n> +\t}\n> +\tstrbuf_release(&buf);\n> +\n> +\treturn res;\n> +}\n\nUnlike the scripted version, where a merge is shown with \"diff --cc\"\nand a root commit is shown as \"Root commit\", this only deals with a\nsingle-parent commit.  Is this because this helper, at least in its\ncurrent form, will not be used by \"rebase -m\" and not with \"--root\"?\n\nIf that is the case, that is perfectly fine, perhaps that deserves a\nmention in the log message and in-code comment before the function.\n\n"},{"id":"309420","messageId":"alpine.DEB.2.20.1701141855180.3469@virtualbox","threadId":"43966","inReplyTo":"xmqq7f60kssh.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 06/38] sequencer (rebase -i): implement the 'edit' command","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-14T17:56:10Z","receivedAt":"2017-01-14T18:03:24Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Thu, 12 Jan 2017, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > +static int make_patch(struct commit *commit, struct replay_opts *opts)\n> > +{\n> > +\tstruct strbuf buf = STRBUF_INIT;\n> > +\tstruct rev_info log_tree_opt;\n> > +\tconst char *subject, *p;\n> > +\tint res = 0;\n> > +\n> > +\tp = short_commit_name(commit);\n> > +\tif (write_message(p, strlen(p), rebase_path_stopped_sha(), 1) < 0)\n> > +\t\treturn -1;\n> > +\n> > +\tstrbuf_addf(&buf, \"%s/patch\", get_dir(opts));\n> > +\tmemset(&log_tree_opt, 0, sizeof(log_tree_opt));\n> > +\tinit_revisions(&log_tree_opt, NULL);\n> > +\tlog_tree_opt.abbrev = 0;\n> > +\tlog_tree_opt.diff = 1;\n> > +\tlog_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;\n> > +\tlog_tree_opt.disable_stdin = 1;\n> > +\tlog_tree_opt.no_commit_id = 1;\n> > +\tlog_tree_opt.diffopt.file = fopen(buf.buf, \"w\");\n> > +\tlog_tree_opt.diffopt.use_color = GIT_COLOR_NEVER;\n> > +\tif (!log_tree_opt.diffopt.file)\n> > +\t\tres |= error_errno(_(\"could not open '%s'\"), buf.buf);\n> > +\telse {\n> > +\t\tres |= log_tree_commit(&log_tree_opt, commit);\n> > +\t\tfclose(log_tree_opt.diffopt.file);\n> > +\t}\n> > +\tstrbuf_reset(&buf);\n> > +\n> > +\tstrbuf_addf(&buf, \"%s/message\", get_dir(opts));\n> > +\tif (!file_exists(buf.buf)) {\n> > +\t\tconst char *commit_buffer = get_commit_buffer(commit, NULL);\n> > +\t\tfind_commit_subject(commit_buffer, &subject);\n> > +\t\tres |= write_message(subject, strlen(subject), buf.buf, 1);\n> > +\t\tunuse_commit_buffer(commit, commit_buffer);\n> > +\t}\n> > +\tstrbuf_release(&buf);\n> > +\n> > +\treturn res;\n> > +}\n> \n> Unlike the scripted version, where a merge is shown with \"diff --cc\"\n> and a root commit is shown as \"Root commit\", this only deals with a\n> single-parent commit.\n\nIndeed. The reason is that we never encounter a merge commit (as we\nexplicitly do not handle --preserve-merges) nor root commits (as we\nexplicitly do not handle --root)\n\nCiao,\nJohannes\n"},{"id":"309421","messageId":"alpine.DEB.2.20.1701141856240.3469@virtualbox","threadId":"43966","inReplyTo":"xmqqk2a0ktxr.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 01/38] sequencer: avoid unnecessary curly braces","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-14T17:57:13Z","receivedAt":"2017-01-14T18:04:06Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Thu, 12 Jan 2017, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> >  \n> > -\tif (!commit->parents) {\n> > +\tif (!commit->parents)\n> >  \t\tparent = NULL;\n> > -\t}\n> >  \telse if (commit->parents->next) {\n> >  \t\t/* Reverting or cherry-picking a merge commit */\n> >  \t\tint cnt;\n> \n> The result becomes\n> \n> \tif (...)\n> \t\tsingle statement;\n> \telse if (...) {\n> \t\tmultiple;\n>                 statements;\n>         }\n> \n> which is not quite an improvement.  \n\nYet, this used to be the coding style of Git, and your statement comes\nquite as a surprise to me.\n\nCiao,\nJohannes\n"},{"id":"309422","messageId":"alpine.DEB.2.20.1701141857270.3469@virtualbox","threadId":"43966","inReplyTo":"xmqqinpnuaxl.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 00/38] Teach the sequencer to act as rebase -i's backend","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-14T18:04:28Z","receivedAt":"2017-01-14T18:04:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 9 Jan 2017, Junio C Hamano wrote:\n\n> Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> \n> > Changes since v2:\n> >\n> > - fixed a TRANSLATORS: comment\n> > ...\n> > - replaced a spawned `diff-tree` command by code using the diff functions\n> >   directly\n> \n> I just finished skimming the interdiff (the difference between the\n> result of merging the v2 into 'master' and the result of applying\n> this series on 'master').\n\nI wish you would not have skimmed it, but provided a thorough review.\nThere was a rather serious bug in this (not the first problem introduced\ninto one of my patch series *because of* code review, unhidden-git and\nmmap-regexec are also very recent examples, I really should learn to\nresist the prodding to replace well-tested code with code of unknown\ncorrectness).\n\nThe problem in this instance was that the authorship is no longer retained\nwhen continuing after resolving a conflict. Let me stress again that this\nhas not been a problem with v1 of sequencer-i, nor with v2. The regression\nwas caused by changes required by the code review.\n\nIn case you wonder: Yes, I am upset by this.\n\nThe required fixup patch is:\n\n-- snipsnap --\nSubject: [PATCH] fixup! sequencer: make reading author-script more elegant\n\nAn unfortunate regression of formerly battle-tested code sadly crept\ninto Git for Windows v2.11.0(2): authorship was not retained in case of\nconflicts during picks.\n\nSigned-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>\n---\n sequencer.c                   |  2 +-\n t/t3404-rebase-interactive.sh | 16 ++++++++++++++++\n 2 files changed, 17 insertions(+), 1 deletion(-)\n\ndiff --git a/sequencer.c b/sequencer.c\nindex 73b2ec6894..8ecab02291 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -612,7 +612,7 @@ static int read_env_script(struct argv_array *env)\n \t\t\tcount++;\n \t\t}\n \n-\tfor (i = 0; i < count; i++) {\n+\tfor (i = 0, p = script.buf; i < count; i++) {\n \t\targv_array_push(env, p);\n \t\tp += strlen(p) + 1;\n \t}\ndiff --git a/t/t3404-rebase-interactive.sh b/t/t3404-rebase-interactive.sh\nindex 71b9c8ef8b..61113be08a 100755\n--- a/t/t3404-rebase-interactive.sh\n+++ b/t/t3404-rebase-interactive.sh\n@@ -237,6 +237,22 @@ test_expect_success 'retain authorship' '\n \tgit show HEAD | grep \"^Author: Twerp Snog\"\n '\n \n+test_expect_success 'retain authorship w/ conflicts' '\n+\tgit reset --hard twerp &&\n+\ttest_commit a conflict a conflict-a &&\n+\tgit reset --hard twerp &&\n+\tGIT_AUTHOR_NAME=AttributeMe \\\n+\ttest_commit b conflict b conflict-b &&\n+\tset_fake_editor &&\n+\ttest_must_fail git rebase -i conflict-a &&\n+\techo resolved >conflict &&\n+\tgit add conflict &&\n+\tgit rebase --continue &&\n+\ttest $(git rev-parse conflict-a^0) = $(git rev-parse HEAD^) &&\n+\tgit show >out &&\n+\tgrep AttributeMe out\n+'\n+\n test_expect_success 'squash' '\n \tgit reset --hard twerp &&\n \techo B > file7 &&\n-- \n2.11.0.windows.3\n\n"},{"id":"309423","messageId":"20170114180550.ebra5qexewetuoyk@sigill.intra.peff.net","threadId":"43966","inReplyTo":"alpine.DEB.2.20.1701141856240.3469@virtualbox","subject":"Re: [PATCH v3 01/38] sequencer: avoid unnecessary curly braces","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2017-01-14T18:05:50Z","receivedAt":"2017-01-14T18:05:58Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Jan 14, 2017 at 06:57:13PM +0100, Johannes Schindelin wrote:\n\n> On Thu, 12 Jan 2017, Junio C Hamano wrote:\n> \n> > Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n> > \n> > >  \n> > > -\tif (!commit->parents) {\n> > > +\tif (!commit->parents)\n> > >  \t\tparent = NULL;\n> > > -\t}\n> > >  \telse if (commit->parents->next) {\n> > >  \t\t/* Reverting or cherry-picking a merge commit */\n> > >  \t\tint cnt;\n> > \n> > The result becomes\n> > \n> > \tif (...)\n> > \t\tsingle statement;\n> > \telse if (...) {\n> > \t\tmultiple;\n> >                 statements;\n> >         }\n> > \n> > which is not quite an improvement.  \n> \n> Yet, this used to be the coding style of Git, and your statement comes\n> quite as a surprise to me.\n\nYeah, I thought we were OK with:\n\n  if (cond)\n\tsingle statement;\n  else {\n\tmultiple;\n\tstatements;\n  }\n\nbut not the other way around:\n\n  if (cond) {\n\tmultiple;\n\tstatements;\n  } else\n\tsingle statement;\n\nI don't know if the \"else if\" changes that or not, but I certainly have\nwritten things like your patch does.\n\n-Peff\n"},{"id":"309425","messageId":"alpine.DEB.2.20.1701141904390.3469@virtualbox","threadId":"43966","inReplyTo":"xmqqinpnuaxl.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 00/38] Teach the sequencer to act as rebase -i's backend","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-14T18:08:41Z","receivedAt":"2017-01-14T18:08:59Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Mon, 9 Jan 2017, Junio C Hamano wrote:\n\n> I however think that the renaming of read_author_script() is totally\n> backwards from maintainability's point of view.  \n\nYou stated elsewhere that converting a script into a builtin should focus\non a faithful conversion.\n\nThe original code is:\n\n\t. \"$author_script\"\n\nGranted, this *cannot* be converted faithfully without reimplementing a\nshell interpreter. So I did the next best thing: I converted it into code\nthat reads a block of environment variable settings.\n\nWhat you asked for is totally unreasonable: you ask me to make this\nconversion *even less faithful*.\n\nWhat is worse: you argue from the \"maintainability point of view\", when it\nis pretty obvious that *adding validation that was not there before* can\nin no way make the code more maintainable, as it *adds new logic*.\n\nAnd what is the worst: over all these discussions about a nothingburger\n(you simply cannot convince me that I should introduce validating code\nthat has not been there before in the same patch series that simply tries\nto recreate existing functionality), the most important part of a code\nreview was forgotten: to make sure that the changes are correct.\n\nThe worst direction a code review can take is to introduce regressions,\nand that is exactly what happened.\n\nCiao,\nJohannes\n"},{"id":"309431","messageId":"xmqqmvetc9zf.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"alpine.DEB.2.20.1701141857270.3469@virtualbox","subject":"Re: [PATCH v3 00/38] Teach the sequencer to act as rebase -i's backend","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-15T02:50:28Z","receivedAt":"2017-01-15T02:50:36Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> The problem in this instance was that the authorship is no longer retained\n> when continuing after resolving a conflict. Let me stress again that this\n> has not been a problem with v1 of sequencer-i, nor with v2. The regression\n> was caused by changes required by the code review.\n>\n> In case you wonder: Yes, I am upset by this.\n>\n> -- snipsnap --\n> Subject: [PATCH] fixup! sequencer: make reading author-script more elegant\n\nI do not think anybody asked to make the code \"more elegant\".  Quite\nfrankly, I do not expect elegance in your code (or any of the code\nin our codebase, for that matter).  What we want is readable code\nthat does not make the overall codebase less maintainable that is\ncorrect.  Not reinveting a new codepath when there is already code\nthat does the thing is one of the things that we may need to do, but\nthat was not done between these rerolls.\n\nOf course, when trying to share code, the existing code we have that\nthe new codepath needs to borrow would have to be refactored and\nextended, and a new bug can sneak in during the process.  If that\nwere what happened, I would be a bit more sympathetic, but I suspect\nthat this \"more elegant\" thing that needed fix-up is not that.\n\nYou may be upset, but I cannot quite bring myself to feel sympathy\nin this particular case.\n\n\n\n\n\n"},{"id":"309434","messageId":"xmqqinpgdass.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"alpine.DEB.2.20.1701141904390.3469@virtualbox","subject":"Re: [PATCH v3 00/38] Teach the sequencer to act as rebase -i's backend","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-15T07:47:31Z","receivedAt":"2017-01-15T07:47:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n> You stated elsewhere that converting a script into a builtin should focus\n> on a faithful conversion.\n>\n> The original code is:\n>\n> \t. \"$author_script\"\n>\n> Granted, this *cannot* be converted faithfully without reimplementing a\n> shell interpreter. So I did the next best thing: I converted it into code\n> that reads a block of environment variable settings.\n\nIt is unfortunate that you took \"faithful\" too literally.  While I\ndo appreciate it if your conversion aims to faithfully replicate the\noriginal, even to be bug-to-bug compatible, but obviously we cannot\nreplicate everything the above original _could_ do.  \n\nBy \"a faithful conversion\", I meant that the behaviour of the\nreimplementation should be as faithful to the original's intent as\npossible.  Nothing more.\n\nThe intent of the above original is to read back what we wrote to\npreserve the author identity we learned earlier when we wrote the\nfile.  We read the \"author\" line from the commit object header, and\nwrite assignments to GIT_AUTHOR_{NAME,EMAIL,DATE} variables.  Nothing\nmore is intended.\n\nThe end-users COULD abuse the original code to cause it to do a lot\nmore than that, e.g. by adding \"export FOO=BAR\" at the end and have\nthe value of the new environment variable propagate throughout the\ncode and even down to subprocesses.  They can even assign to some\nvariables we use internally for bookkeeping and break \"rebase -i\"\nmachinery.  But that is outside the intent of the original code--we\ndo not need to or want to replicate that faithfully.\n\nI also need to react to the \"environment variable settings\" at the\nend of the quoted paragraph.\n\nIf the code in the sequencer.c reads things other than the three\nvariables we ourselves set, and make them into environment variables\nand propagate to subprocesses (hooks and editors), it would be a\nbug.  The original did not intend to do that (the dot-sourcing is\noverly loose than reading three known variables and nothing else,\nbut is OK because we do not support the case where end users muck\nwith the file).  Also, writing FOO=BAR alone (not \"export FOO=BAR\"\nor \"FOO=BAR; export FOO\") to the file wouldn't have exported FOO to\nsubprocesses anyway.\n"},{"id":"309468","messageId":"CA+P7+xrujKPCxCH9NSvhkxsrs1JojwdgsRvvedZhUVXucsG0QQ@mail.gmail.com","threadId":"43966","inReplyTo":"20170114180550.ebra5qexewetuoyk@sigill.intra.peff.net","subject":"Re: [PATCH v3 01/38] sequencer: avoid unnecessary curly braces","fromName":"Jacob Keller","fromEmail":"jacob.keller@gmail.com","sentAt":"2017-01-16T04:10:41Z","receivedAt":"2017-01-16T04:11:08Z","isPatch":true,"sender":{"key":"jacob.keller@gmail.com","avatar":"https://avatars.githubusercontent.com/u/874719?v=4"},"body":"On Sat, Jan 14, 2017 at 10:05 AM, Jeff King <peff@peff.net> wrote:\n> On Sat, Jan 14, 2017 at 06:57:13PM +0100, Johannes Schindelin wrote:\n>\n>> On Thu, 12 Jan 2017, Junio C Hamano wrote:\n>>\n>> > Johannes Schindelin <johannes.schindelin@gmx.de> writes:\n>> >\n>> > >\n>> > > - if (!commit->parents) {\n>> > > + if (!commit->parents)\n>> > >           parent = NULL;\n>> > > - }\n>> > >   else if (commit->parents->next) {\n>> > >           /* Reverting or cherry-picking a merge commit */\n>> > >           int cnt;\n>> >\n>> > The result becomes\n>> >\n>> >     if (...)\n>> >             single statement;\n>> >     else if (...) {\n>> >             multiple;\n>> >                 statements;\n>> >         }\n>> >\n>> > which is not quite an improvement.\n>>\n>> Yet, this used to be the coding style of Git, and your statement comes\n>> quite as a surprise to me.\n>\n> Yeah, I thought we were OK with:\n>\n>   if (cond)\n>         single statement;\n>   else {\n>         multiple;\n>         statements;\n>   }\n>\n> but not the other way around:\n>\n>   if (cond) {\n>         multiple;\n>         statements;\n>   } else\n>         single statement;\n>\n> I don't know if the \"else if\" changes that or not, but I certainly have\n> written things like your patch does.\n>\n> -Peff\n\nPersonally, I am of the faith that if any branch of the\nif-then-else-if-then-else blocks needs braces, then all branches\nshould use braces. However, I think that\n\nif (condition)\n  <line>\nelse {\n  <block>\n}\n\nis reasonably close to this, as the main part can still clearly see\nthe if condition pretty well.\n\nThanks,\nJake\n"},{"id":"309478","messageId":"alpine.DEB.2.20.1701161129240.3469@virtualbox","threadId":"43966","inReplyTo":"xmqqinpgdass.fsf@gitster.mtv.corp.google.com","subject":"Re: [PATCH v3 00/38] Teach the sequencer to act as rebase -i's backend","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2017-01-16T10:45:06Z","receivedAt":"2017-01-16T10:45:54Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi Junio,\n\nOn Sat, 14 Jan 2017, Junio C Hamano wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n> \n> > You stated elsewhere that converting a script into a builtin should focus\n> > on a faithful conversion.\n> >\n> > The original code is:\n> >\n> > \t. \"$author_script\"\n> \n> [...]\n>\n> If the code in the sequencer.c reads things other than the three\n> variables we ourselves set, and make them into environment variables\n> and propagate to subprocesses (hooks and editors), it would be a\n> bug.  The original did not intend to do that (the dot-sourcing is\n> overly loose than reading three known variables and nothing else,\n> but is OK because we do not support the case where end users muck\n> with the file).  Also, writing FOO=BAR alone (not \"export FOO=BAR\"\n> or \"FOO=BAR; export FOO\") to the file wouldn't have exported FOO to\n> subprocesses anyway.\n\nThat analysis cannot be completely correct, as the GIT_AUTHOR_* variables\n*are* used by the `git commit` subprocess.\n\nIn any case, it is clear that we (I include all reviewers here) messed\nthis patch series up quite a bit. Hence I will be more careful from now on\nto only act on suggestions that do, in fact, improve the patch series from\nmy point of view.\n\nCiao,\nJohannes\n"},{"id":"309541","messageId":"xmqq7f5tfouc.fsf@gitster.mtv.corp.google.com","threadId":"43966","inReplyTo":"alpine.DEB.2.20.1701161129240.3469@virtualbox","subject":"Re: [PATCH v3 00/38] Teach the sequencer to act as rebase -i's backend","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2017-01-17T19:50:19Z","receivedAt":"2017-01-17T19:50:27Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> writes:\n\n>> > The original code is:\n>> >\n>> > \t. \"$author_script\"\n>> \n>> [...]\n>>\n>> If the code in the sequencer.c reads things other than the three\n>> variables we ourselves set, and make them into environment variables\n>> and propagate to subprocesses (hooks and editors), it would be a\n>> bug.  The original did not intend to do that (the dot-sourcing is\n>> overly loose than reading three known variables and nothing else,\n>> but is OK because we do not support the case where end users muck\n>> with the file).  Also, writing FOO=BAR alone (not \"export FOO=BAR\"\n>> or \"FOO=BAR; export FOO\") to the file wouldn't have exported FOO to\n>> subprocesses anyway.\n>\n> That analysis cannot be completely correct, as the GIT_AUTHOR_* variables\n> *are* used by the `git commit` subprocess.\n\nIn the scripted version, do_with_author shell function explicitly\nexports GIT_AUTHOR_* variables because they are the only ones we\ncare about that are read from existing commit and carried forward\nvia the author-script mechanism.  We do not care about, and in fact\nwe do not intend to support, any other variables or shell commands\nthat appear in the author-script.\n"}]}